Skip to content

Commit f81a20a

Browse files
committed
fix loading resume data when in seed mode
1 parent 0fe2e85 commit f81a20a

9 files changed

+138
-75
lines changed

ChangeLog

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
* fix loading resume data when in seed mode
23
* fix part-file creation race condition
34
* fix issue with initializing settings on session construction
45
* fix issue with receiving interested before metadata

docs/hunspell/libtorrent.dic

+2
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ pread
8484
preadv
8585
pwrite
8686
pwritev
87+
readv
88+
writev
8789
ftruncate
8890
iovec
8991
uint8

include/libtorrent/storage.hpp

+18-4
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,24 @@ namespace libtorrent
209209
storage_interface(): m_settings(0) {}
210210

211211

212-
// This function is called when the storage is to be initialized. The
213-
// default storage will create directories and empty files at this point.
214-
// If ``allocate_files`` is true, it will also ``ftruncate`` all files to
215-
// their target size.
212+
// This function is called when the *storage* on disk is to be
213+
// initialized. The default storage will create directories and empty
214+
// files at this point. If ``allocate_files`` is true, it will also
215+
// ``ftruncate`` all files to their target size.
216+
//
217+
// This function may be called multiple time on a single instance. When a
218+
// torrent is force-rechecked, the storage is re-initialized to trigger
219+
// the re-check from scratch.
220+
//
221+
// The function is not necessarily called before other member functions.
222+
// For instance has_any_files() and verify_resume_data() are
223+
// called early to determine whether we may have to check all files or
224+
// not. If we're doing a full check of the files every piece will be
225+
// hashed, causing readv() to be called as well.
226+
//
227+
// Any required internals that need initialization should be done in the
228+
// constructor. This function is called before the torrent starts to
229+
// download.
216230
//
217231
// If an error occurs, ``storage_error`` should be set to reflect it.
218232
virtual void initialize(storage_error& ec) = 0;

src/torrent.cpp

+67-61
Original file line numberDiff line numberDiff line change
@@ -1998,84 +1998,83 @@ namespace libtorrent
19981998
if (m_seed_mode)
19991999
{
20002000
m_have_all = true;
2001-
m_ses.get_io_service().post(boost::bind(&torrent::files_checked, shared_from_this()));
2002-
m_resume_data.reset();
20032001
update_gauge();
20042002
update_state_list();
2005-
return;
20062003
}
2007-
2008-
set_state(torrent_status::checking_resume_data);
2009-
2010-
int num_pad_files = 0;
2011-
TORRENT_ASSERT(block_size() > 0);
2012-
file_storage const& fs = m_torrent_file->files();
2013-
for (int i = 0; i < fs.num_files(); ++i)
2004+
else
20142005
{
2015-
if (!fs.pad_file_at(i) || fs.file_size(i) == 0) continue;
2006+
int num_pad_files = 0;
2007+
TORRENT_ASSERT(block_size() > 0);
2008+
file_storage const& fs = m_torrent_file->files();
2009+
for (int i = 0; i < fs.num_files(); ++i)
2010+
{
2011+
if (!fs.pad_file_at(i) || fs.file_size(i) == 0) continue;
20162012

2017-
if (fs.pad_file_at(i)) ++num_pad_files;
2013+
if (fs.pad_file_at(i)) ++num_pad_files;
20182014

2019-
m_padding += boost::uint32_t(fs.file_size(i));
2015+
m_padding += boost::uint32_t(fs.file_size(i));
20202016

2021-
// TODO: instead of creating the picker up front here,
2022-
// maybe this whole section should move to need_picker()
2023-
need_picker();
2017+
// TODO: instead of creating the picker up front here,
2018+
// maybe this whole section should move to need_picker()
2019+
need_picker();
20242020

2025-
peer_request pr = m_torrent_file->map_file(i, 0, fs.file_size(i));
2026-
int off = pr.start & (block_size()-1);
2027-
if (off != 0) { pr.length -= block_size() - off; pr.start += block_size() - off; }
2028-
TORRENT_ASSERT((pr.start & (block_size()-1)) == 0);
2021+
peer_request pr = m_torrent_file->map_file(i, 0, fs.file_size(i));
2022+
int off = pr.start & (block_size()-1);
2023+
if (off != 0) { pr.length -= block_size() - off; pr.start += block_size() - off; }
2024+
TORRENT_ASSERT((pr.start & (block_size()-1)) == 0);
20292025

2030-
int block = block_size();
2031-
int blocks_per_piece = m_torrent_file->piece_length() / block;
2032-
piece_block pb(pr.piece, pr.start / block);
2033-
for (; pr.length >= block; pr.length -= block, ++pb.block_index)
2034-
{
2035-
if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
2036-
m_picker->mark_as_finished(pb, 0);
2026+
int block = block_size();
2027+
int blocks_per_piece = m_torrent_file->piece_length() / block;
2028+
piece_block pb(pr.piece, pr.start / block);
2029+
for (; pr.length >= block; pr.length -= block, ++pb.block_index)
2030+
{
2031+
if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
2032+
m_picker->mark_as_finished(pb, 0);
2033+
}
2034+
// ugly edge case where padfiles are not used they way they're
2035+
// supposed to be. i.e. added back-to back or at the end
2036+
if (pb.block_index == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
2037+
if (pr.length > 0 && ((i+1 != fs.num_files() && fs.pad_file_at(i+1))
2038+
|| i + 1 == fs.num_files()))
2039+
{
2040+
m_picker->mark_as_finished(pb, 0);
2041+
}
20372042
}
2038-
// ugly edge case where padfiles are not used they way they're
2039-
// supposed to be. i.e. added back-to back or at the end
2040-
if (pb.block_index == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
2041-
if (pr.length > 0 && ((i+1 != fs.num_files() && fs.pad_file_at(i+1))
2042-
|| i + 1 == fs.num_files()))
2043+
2044+
if (m_padding > 0)
20432045
{
2044-
m_picker->mark_as_finished(pb, 0);
2045-
}
2046-
}
2046+
// if we marked an entire piece as finished, we actually
2047+
// need to consider it finished
20472048

2048-
if (m_padding > 0)
2049-
{
2050-
// if we marked an entire piece as finished, we actually
2051-
// need to consider it finished
2049+
std::vector<piece_picker::downloading_piece> dq
2050+
= m_picker->get_download_queue();
20522051

2053-
std::vector<piece_picker::downloading_piece> dq
2054-
= m_picker->get_download_queue();
2052+
std::vector<int> have_pieces;
20552053

2056-
std::vector<int> have_pieces;
2054+
for (std::vector<piece_picker::downloading_piece>::const_iterator i
2055+
= dq.begin(); i != dq.end(); ++i)
2056+
{
2057+
int num_blocks = m_picker->blocks_in_piece(i->index);
2058+
if (i->finished < num_blocks) continue;
2059+
have_pieces.push_back(i->index);
2060+
}
20572061

2058-
for (std::vector<piece_picker::downloading_piece>::const_iterator i
2059-
= dq.begin(); i != dq.end(); ++i)
2060-
{
2061-
int num_blocks = m_picker->blocks_in_piece(i->index);
2062-
if (i->finished < num_blocks) continue;
2063-
have_pieces.push_back(i->index);
2062+
for (std::vector<int>::iterator i = have_pieces.begin();
2063+
i != have_pieces.end(); ++i)
2064+
{
2065+
picker().piece_passed(*i);
2066+
TORRENT_ASSERT(picker().have_piece(*i));
2067+
we_have(*i);
2068+
}
20642069
}
20652070

2066-
for (std::vector<int>::iterator i = have_pieces.begin();
2067-
i != have_pieces.end(); ++i)
2068-
{
2069-
picker().piece_passed(*i);
2070-
TORRENT_ASSERT(picker().have_piece(*i));
2071-
we_have(*i);
2072-
}
2073-
}
2071+
if (!need_loaded()) return;
20742072

2075-
if (!need_loaded()) return;
2073+
if (num_pad_files > 0)
2074+
m_picker->set_num_pad_files(num_pad_files);
2075+
}
20762076

2077-
if (num_pad_files > 0)
2078-
m_picker->set_num_pad_files(num_pad_files);
2077+
set_state(torrent_status::checking_resume_data);
20792078

20802079
std::vector<std::string> links;
20812080
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
@@ -2350,7 +2349,7 @@ namespace libtorrent
23502349
// want anything in this function to affect the state of
23512350
// m_need_save_resume_data, so we save it in a local variable and reset
23522351
// it at the end of the function.
2353-
bool need_save_resume_data = m_need_save_resume_data;
2352+
bool const need_save_resume_data = m_need_save_resume_data;
23542353

23552354
dec_refcount("check_fastresume");
23562355
TORRENT_ASSERT(is_single_thread());
@@ -2497,7 +2496,14 @@ namespace libtorrent
24972496
// that when the resume data check fails. For instance, if the resume data
24982497
// is incorrect, but we don't have any files, we skip the check and initialize
24992498
// the storage to not have anything.
2500-
if (j->ret == 0)
2499+
if (m_seed_mode)
2500+
{
2501+
m_have_all = true;
2502+
files_checked();
2503+
update_gauge();
2504+
update_state_list();
2505+
}
2506+
else if (j->ret == 0)
25012507
{
25022508
// there are either no files for this torrent
25032509
// or the resume_data was accepted

test/test_fast_extension.cpp

+1-4
Original file line numberDiff line numberDiff line change
@@ -441,10 +441,7 @@ boost::shared_ptr<torrent_info> setup_peer(tcp::socket& s, sha1_hash& ih
441441
if (th) *th = ret;
442442

443443
// wait for the torrent to be ready
444-
if ((flags & add_torrent_params::flag_seed_mode) == 0)
445-
{
446-
wait_for_downloading(*ses, "ses");
447-
}
444+
wait_for_downloading(*ses, "ses");
448445

449446
if (incoming)
450447
{

test/test_priority.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ void test_transfer(settings_pack const& sett)
165165
}
166166

167167
TEST_CHECK(st1.state == torrent_status::seeding
168+
|| st1.state == torrent_status::checking_resume_data
168169
|| st1.state == torrent_status::checking_files);
169170
TEST_CHECK(st2.state == torrent_status::downloading
170171
|| st2.state == torrent_status::checking_resume_data);

test/test_remap_files.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ void test_remap_files_gather(storage_mode_t storage_mode = storage_mode_sparse)
173173
}
174174

175175
TEST_CHECK(st1.state == torrent_status::seeding
176+
|| st1.state == torrent_status::checking_resume_data
176177
|| st1.state == torrent_status::checking_files);
177178
TEST_CHECK(st2.state == torrent_status::downloading
178179
|| st2.state == torrent_status::checking_resume_data);
@@ -317,6 +318,7 @@ void test_remap_files_scatter(storage_mode_t storage_mode = storage_mode_sparse)
317318
}
318319

319320
TEST_CHECK(st1.state == torrent_status::seeding
321+
|| st1.state == torrent_status::checking_resume_data
320322
|| st1.state == torrent_status::checking_files);
321323
TEST_CHECK(st2.state == torrent_status::downloading
322324
|| st2.state == torrent_status::checking_resume_data);

test/test_remove_torrent.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,13 @@ void test_remove_torrent(int const remove_options
118118
if (st2.is_finished) break;
119119

120120
TEST_CHECK(st1.state == torrent_status::seeding
121+
|| st1.state == torrent_status::checking_resume_data
121122
|| st1.state == torrent_status::checking_files);
122123
TEST_CHECK(st2.state == torrent_status::downloading
123124
|| st2.state == torrent_status::checking_resume_data);
124125

125-
// if nothing is being transferred after 2 seconds, we're failing the test
126-
if (st1.upload_payload_rate == 0 && i > 20)
126+
// if nothing is being transferred after 3 seconds, we're failing the test
127+
if (st1.upload_payload_rate == 0 && i > 30)
127128
{
128129
TEST_ERROR("no transfer");
129130
return;

test/test_resume.cpp

+43-4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE.
3838
#include "libtorrent/alert_types.hpp"
3939
#include "libtorrent/entry.hpp"
4040
#include "libtorrent/bencode.hpp"
41+
#include "libtorrent/peer_info.hpp"
4142

4243
#include <boost/make_shared.hpp>
4344

@@ -186,10 +187,15 @@ void default_tests(torrent_status const& s)
186187
TEST_CHECK(s.time_since_upload < 1351 + 10);
187188
TEST_CHECK(s.active_time < 1339 + 10);
188189

189-
TEST_EQUAL(s.finished_time, 1352);
190-
TEST_EQUAL(s.seeding_time, 1340);
191-
TEST_EQUAL(s.added_time, 1347);
192-
TEST_EQUAL(s.completed_time, 1348);
190+
TEST_CHECK(s.finished_time >= 1352);
191+
TEST_CHECK(s.seeding_time >= 1340);
192+
TEST_CHECK(s.added_time >= 1347);
193+
TEST_CHECK(s.completed_time >= 1348);
194+
195+
TEST_CHECK(s.finished_time < 1352 + 5);
196+
TEST_CHECK(s.seeding_time < 1340 + 5);
197+
TEST_CHECK(s.added_time < 1347 + 5);
198+
TEST_CHECK(s.completed_time < 1348 + 5);
193199
}
194200

195201
void test_file_sizes(bool allocate)
@@ -494,6 +500,39 @@ TORRENT_TEST(seed_mode_preserve)
494500
test_seed_mode(false, false, false);
495501
}
496502

503+
TORRENT_TEST(seed_mode_load_peers)
504+
{
505+
lt::session ses(settings());
506+
boost::shared_ptr<torrent_info> ti = generate_torrent();
507+
add_torrent_params p;
508+
p.ti = ti;
509+
p.save_path = ".";
510+
511+
entry rd;
512+
513+
rd["file-format"] = "libtorrent resume file";
514+
rd["file-version"] = 1;
515+
rd["info-hash"] = ti->info_hash().to_string();
516+
rd["blocks per piece"] = std::max(1, ti->piece_length() / 0x4000);
517+
518+
rd["pieces"] = std::string(ti->num_pieces(), '\x01');
519+
rd["piece_priority"] = std::string(ti->num_pieces(), '\x01');
520+
rd["seed_mode"] = 1;
521+
rd["peers"] = "\x01\x02\x03\x04\x30\x39";
522+
523+
bencode(back_inserter(p.resume_data), rd);
524+
525+
torrent_handle h = ses.add_torrent(p);
526+
527+
wait_for_alert(ses, torrent_checked_alert::alert_type, "seed_mode_load_peers");
528+
529+
std::vector<peer_list_entry> peers;
530+
h.get_full_peer_list(peers);
531+
532+
TEST_EQUAL(peers.size(), 1);
533+
TEST_CHECK(peers[0].ip == tcp::endpoint(address::from_string("1.2.3.4"), 12345));
534+
}
535+
497536
TORRENT_TEST(resume_save_load)
498537
{
499538
lt::session ses(settings());

0 commit comments

Comments
 (0)