fix loading resume data when in seed mode
This commit is contained in:
parent
0fe2e85fee
commit
f81a20a035
|
@ -1,4 +1,5 @@
|
||||||
|
|
||||||
|
* fix loading resume data when in seed mode
|
||||||
* fix part-file creation race condition
|
* fix part-file creation race condition
|
||||||
* fix issue with initializing settings on session construction
|
* fix issue with initializing settings on session construction
|
||||||
* fix issue with receiving interested before metadata
|
* fix issue with receiving interested before metadata
|
||||||
|
|
|
@ -84,6 +84,8 @@ pread
|
||||||
preadv
|
preadv
|
||||||
pwrite
|
pwrite
|
||||||
pwritev
|
pwritev
|
||||||
|
readv
|
||||||
|
writev
|
||||||
ftruncate
|
ftruncate
|
||||||
iovec
|
iovec
|
||||||
uint8
|
uint8
|
||||||
|
|
|
@ -209,10 +209,24 @@ namespace libtorrent
|
||||||
storage_interface(): m_settings(0) {}
|
storage_interface(): m_settings(0) {}
|
||||||
|
|
||||||
|
|
||||||
// This function is called when the storage is to be initialized. The
|
// This function is called when the *storage* on disk is to be
|
||||||
// default storage will create directories and empty files at this point.
|
// initialized. The default storage will create directories and empty
|
||||||
// If ``allocate_files`` is true, it will also ``ftruncate`` all files to
|
// files at this point. If ``allocate_files`` is true, it will also
|
||||||
// their target size.
|
// ``ftruncate`` all files to their target size.
|
||||||
|
//
|
||||||
|
// This function may be called multiple time on a single instance. When a
|
||||||
|
// torrent is force-rechecked, the storage is re-initialized to trigger
|
||||||
|
// the re-check from scratch.
|
||||||
|
//
|
||||||
|
// The function is not necessarily called before other member functions.
|
||||||
|
// For instance has_any_files() and verify_resume_data() are
|
||||||
|
// called early to determine whether we may have to check all files or
|
||||||
|
// not. If we're doing a full check of the files every piece will be
|
||||||
|
// hashed, causing readv() to be called as well.
|
||||||
|
//
|
||||||
|
// Any required internals that need initialization should be done in the
|
||||||
|
// constructor. This function is called before the torrent starts to
|
||||||
|
// download.
|
||||||
//
|
//
|
||||||
// If an error occurs, ``storage_error`` should be set to reflect it.
|
// If an error occurs, ``storage_error`` should be set to reflect it.
|
||||||
virtual void initialize(storage_error& ec) = 0;
|
virtual void initialize(storage_error& ec) = 0;
|
||||||
|
|
156
src/torrent.cpp
156
src/torrent.cpp
|
@ -1998,85 +1998,84 @@ namespace libtorrent
|
||||||
if (m_seed_mode)
|
if (m_seed_mode)
|
||||||
{
|
{
|
||||||
m_have_all = true;
|
m_have_all = true;
|
||||||
m_ses.get_io_service().post(boost::bind(&torrent::files_checked, shared_from_this()));
|
|
||||||
m_resume_data.reset();
|
|
||||||
update_gauge();
|
update_gauge();
|
||||||
update_state_list();
|
update_state_list();
|
||||||
return;
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int num_pad_files = 0;
|
||||||
|
TORRENT_ASSERT(block_size() > 0);
|
||||||
|
file_storage const& fs = m_torrent_file->files();
|
||||||
|
for (int i = 0; i < fs.num_files(); ++i)
|
||||||
|
{
|
||||||
|
if (!fs.pad_file_at(i) || fs.file_size(i) == 0) continue;
|
||||||
|
|
||||||
|
if (fs.pad_file_at(i)) ++num_pad_files;
|
||||||
|
|
||||||
|
m_padding += boost::uint32_t(fs.file_size(i));
|
||||||
|
|
||||||
|
// TODO: instead of creating the picker up front here,
|
||||||
|
// maybe this whole section should move to need_picker()
|
||||||
|
need_picker();
|
||||||
|
|
||||||
|
peer_request pr = m_torrent_file->map_file(i, 0, fs.file_size(i));
|
||||||
|
int off = pr.start & (block_size()-1);
|
||||||
|
if (off != 0) { pr.length -= block_size() - off; pr.start += block_size() - off; }
|
||||||
|
TORRENT_ASSERT((pr.start & (block_size()-1)) == 0);
|
||||||
|
|
||||||
|
int block = block_size();
|
||||||
|
int blocks_per_piece = m_torrent_file->piece_length() / block;
|
||||||
|
piece_block pb(pr.piece, pr.start / block);
|
||||||
|
for (; pr.length >= block; pr.length -= block, ++pb.block_index)
|
||||||
|
{
|
||||||
|
if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
|
||||||
|
m_picker->mark_as_finished(pb, 0);
|
||||||
|
}
|
||||||
|
// ugly edge case where padfiles are not used they way they're
|
||||||
|
// supposed to be. i.e. added back-to back or at the end
|
||||||
|
if (pb.block_index == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
|
||||||
|
if (pr.length > 0 && ((i+1 != fs.num_files() && fs.pad_file_at(i+1))
|
||||||
|
|| i + 1 == fs.num_files()))
|
||||||
|
{
|
||||||
|
m_picker->mark_as_finished(pb, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_padding > 0)
|
||||||
|
{
|
||||||
|
// if we marked an entire piece as finished, we actually
|
||||||
|
// need to consider it finished
|
||||||
|
|
||||||
|
std::vector<piece_picker::downloading_piece> dq
|
||||||
|
= m_picker->get_download_queue();
|
||||||
|
|
||||||
|
std::vector<int> have_pieces;
|
||||||
|
|
||||||
|
for (std::vector<piece_picker::downloading_piece>::const_iterator i
|
||||||
|
= dq.begin(); i != dq.end(); ++i)
|
||||||
|
{
|
||||||
|
int num_blocks = m_picker->blocks_in_piece(i->index);
|
||||||
|
if (i->finished < num_blocks) continue;
|
||||||
|
have_pieces.push_back(i->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<int>::iterator i = have_pieces.begin();
|
||||||
|
i != have_pieces.end(); ++i)
|
||||||
|
{
|
||||||
|
picker().piece_passed(*i);
|
||||||
|
TORRENT_ASSERT(picker().have_piece(*i));
|
||||||
|
we_have(*i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!need_loaded()) return;
|
||||||
|
|
||||||
|
if (num_pad_files > 0)
|
||||||
|
m_picker->set_num_pad_files(num_pad_files);
|
||||||
}
|
}
|
||||||
|
|
||||||
set_state(torrent_status::checking_resume_data);
|
set_state(torrent_status::checking_resume_data);
|
||||||
|
|
||||||
int num_pad_files = 0;
|
|
||||||
TORRENT_ASSERT(block_size() > 0);
|
|
||||||
file_storage const& fs = m_torrent_file->files();
|
|
||||||
for (int i = 0; i < fs.num_files(); ++i)
|
|
||||||
{
|
|
||||||
if (!fs.pad_file_at(i) || fs.file_size(i) == 0) continue;
|
|
||||||
|
|
||||||
if (fs.pad_file_at(i)) ++num_pad_files;
|
|
||||||
|
|
||||||
m_padding += boost::uint32_t(fs.file_size(i));
|
|
||||||
|
|
||||||
// TODO: instead of creating the picker up front here,
|
|
||||||
// maybe this whole section should move to need_picker()
|
|
||||||
need_picker();
|
|
||||||
|
|
||||||
peer_request pr = m_torrent_file->map_file(i, 0, fs.file_size(i));
|
|
||||||
int off = pr.start & (block_size()-1);
|
|
||||||
if (off != 0) { pr.length -= block_size() - off; pr.start += block_size() - off; }
|
|
||||||
TORRENT_ASSERT((pr.start & (block_size()-1)) == 0);
|
|
||||||
|
|
||||||
int block = block_size();
|
|
||||||
int blocks_per_piece = m_torrent_file->piece_length() / block;
|
|
||||||
piece_block pb(pr.piece, pr.start / block);
|
|
||||||
for (; pr.length >= block; pr.length -= block, ++pb.block_index)
|
|
||||||
{
|
|
||||||
if (int(pb.block_index) == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
|
|
||||||
m_picker->mark_as_finished(pb, 0);
|
|
||||||
}
|
|
||||||
// ugly edge case where padfiles are not used they way they're
|
|
||||||
// supposed to be. i.e. added back-to back or at the end
|
|
||||||
if (pb.block_index == blocks_per_piece) { pb.block_index = 0; ++pb.piece_index; }
|
|
||||||
if (pr.length > 0 && ((i+1 != fs.num_files() && fs.pad_file_at(i+1))
|
|
||||||
|| i + 1 == fs.num_files()))
|
|
||||||
{
|
|
||||||
m_picker->mark_as_finished(pb, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_padding > 0)
|
|
||||||
{
|
|
||||||
// if we marked an entire piece as finished, we actually
|
|
||||||
// need to consider it finished
|
|
||||||
|
|
||||||
std::vector<piece_picker::downloading_piece> dq
|
|
||||||
= m_picker->get_download_queue();
|
|
||||||
|
|
||||||
std::vector<int> have_pieces;
|
|
||||||
|
|
||||||
for (std::vector<piece_picker::downloading_piece>::const_iterator i
|
|
||||||
= dq.begin(); i != dq.end(); ++i)
|
|
||||||
{
|
|
||||||
int num_blocks = m_picker->blocks_in_piece(i->index);
|
|
||||||
if (i->finished < num_blocks) continue;
|
|
||||||
have_pieces.push_back(i->index);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (std::vector<int>::iterator i = have_pieces.begin();
|
|
||||||
i != have_pieces.end(); ++i)
|
|
||||||
{
|
|
||||||
picker().piece_passed(*i);
|
|
||||||
TORRENT_ASSERT(picker().have_piece(*i));
|
|
||||||
we_have(*i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!need_loaded()) return;
|
|
||||||
|
|
||||||
if (num_pad_files > 0)
|
|
||||||
m_picker->set_num_pad_files(num_pad_files);
|
|
||||||
|
|
||||||
std::vector<std::string> links;
|
std::vector<std::string> links;
|
||||||
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
|
#ifndef TORRENT_DISABLE_MUTABLE_TORRENTS
|
||||||
if (!m_torrent_file->similar_torrents().empty()
|
if (!m_torrent_file->similar_torrents().empty()
|
||||||
|
@ -2350,7 +2349,7 @@ namespace libtorrent
|
||||||
// want anything in this function to affect the state of
|
// want anything in this function to affect the state of
|
||||||
// m_need_save_resume_data, so we save it in a local variable and reset
|
// m_need_save_resume_data, so we save it in a local variable and reset
|
||||||
// it at the end of the function.
|
// it at the end of the function.
|
||||||
bool need_save_resume_data = m_need_save_resume_data;
|
bool const need_save_resume_data = m_need_save_resume_data;
|
||||||
|
|
||||||
dec_refcount("check_fastresume");
|
dec_refcount("check_fastresume");
|
||||||
TORRENT_ASSERT(is_single_thread());
|
TORRENT_ASSERT(is_single_thread());
|
||||||
|
@ -2497,7 +2496,14 @@ namespace libtorrent
|
||||||
// that when the resume data check fails. For instance, if the resume data
|
// that when the resume data check fails. For instance, if the resume data
|
||||||
// is incorrect, but we don't have any files, we skip the check and initialize
|
// is incorrect, but we don't have any files, we skip the check and initialize
|
||||||
// the storage to not have anything.
|
// the storage to not have anything.
|
||||||
if (j->ret == 0)
|
if (m_seed_mode)
|
||||||
|
{
|
||||||
|
m_have_all = true;
|
||||||
|
files_checked();
|
||||||
|
update_gauge();
|
||||||
|
update_state_list();
|
||||||
|
}
|
||||||
|
else if (j->ret == 0)
|
||||||
{
|
{
|
||||||
// there are either no files for this torrent
|
// there are either no files for this torrent
|
||||||
// or the resume_data was accepted
|
// or the resume_data was accepted
|
||||||
|
|
|
@ -441,10 +441,7 @@ boost::shared_ptr<torrent_info> setup_peer(tcp::socket& s, sha1_hash& ih
|
||||||
if (th) *th = ret;
|
if (th) *th = ret;
|
||||||
|
|
||||||
// wait for the torrent to be ready
|
// wait for the torrent to be ready
|
||||||
if ((flags & add_torrent_params::flag_seed_mode) == 0)
|
wait_for_downloading(*ses, "ses");
|
||||||
{
|
|
||||||
wait_for_downloading(*ses, "ses");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (incoming)
|
if (incoming)
|
||||||
{
|
{
|
||||||
|
|
|
@ -165,6 +165,7 @@ void test_transfer(settings_pack const& sett)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CHECK(st1.state == torrent_status::seeding
|
TEST_CHECK(st1.state == torrent_status::seeding
|
||||||
|
|| st1.state == torrent_status::checking_resume_data
|
||||||
|| st1.state == torrent_status::checking_files);
|
|| st1.state == torrent_status::checking_files);
|
||||||
TEST_CHECK(st2.state == torrent_status::downloading
|
TEST_CHECK(st2.state == torrent_status::downloading
|
||||||
|| st2.state == torrent_status::checking_resume_data);
|
|| st2.state == torrent_status::checking_resume_data);
|
||||||
|
|
|
@ -173,6 +173,7 @@ void test_remap_files_gather(storage_mode_t storage_mode = storage_mode_sparse)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CHECK(st1.state == torrent_status::seeding
|
TEST_CHECK(st1.state == torrent_status::seeding
|
||||||
|
|| st1.state == torrent_status::checking_resume_data
|
||||||
|| st1.state == torrent_status::checking_files);
|
|| st1.state == torrent_status::checking_files);
|
||||||
TEST_CHECK(st2.state == torrent_status::downloading
|
TEST_CHECK(st2.state == torrent_status::downloading
|
||||||
|| st2.state == torrent_status::checking_resume_data);
|
|| st2.state == torrent_status::checking_resume_data);
|
||||||
|
@ -317,6 +318,7 @@ void test_remap_files_scatter(storage_mode_t storage_mode = storage_mode_sparse)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CHECK(st1.state == torrent_status::seeding
|
TEST_CHECK(st1.state == torrent_status::seeding
|
||||||
|
|| st1.state == torrent_status::checking_resume_data
|
||||||
|| st1.state == torrent_status::checking_files);
|
|| st1.state == torrent_status::checking_files);
|
||||||
TEST_CHECK(st2.state == torrent_status::downloading
|
TEST_CHECK(st2.state == torrent_status::downloading
|
||||||
|| st2.state == torrent_status::checking_resume_data);
|
|| st2.state == torrent_status::checking_resume_data);
|
||||||
|
|
|
@ -118,12 +118,13 @@ void test_remove_torrent(int const remove_options
|
||||||
if (st2.is_finished) break;
|
if (st2.is_finished) break;
|
||||||
|
|
||||||
TEST_CHECK(st1.state == torrent_status::seeding
|
TEST_CHECK(st1.state == torrent_status::seeding
|
||||||
|
|| st1.state == torrent_status::checking_resume_data
|
||||||
|| st1.state == torrent_status::checking_files);
|
|| st1.state == torrent_status::checking_files);
|
||||||
TEST_CHECK(st2.state == torrent_status::downloading
|
TEST_CHECK(st2.state == torrent_status::downloading
|
||||||
|| st2.state == torrent_status::checking_resume_data);
|
|| st2.state == torrent_status::checking_resume_data);
|
||||||
|
|
||||||
// if nothing is being transferred after 2 seconds, we're failing the test
|
// if nothing is being transferred after 3 seconds, we're failing the test
|
||||||
if (st1.upload_payload_rate == 0 && i > 20)
|
if (st1.upload_payload_rate == 0 && i > 30)
|
||||||
{
|
{
|
||||||
TEST_ERROR("no transfer");
|
TEST_ERROR("no transfer");
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -38,6 +38,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "libtorrent/alert_types.hpp"
|
#include "libtorrent/alert_types.hpp"
|
||||||
#include "libtorrent/entry.hpp"
|
#include "libtorrent/entry.hpp"
|
||||||
#include "libtorrent/bencode.hpp"
|
#include "libtorrent/bencode.hpp"
|
||||||
|
#include "libtorrent/peer_info.hpp"
|
||||||
|
|
||||||
#include <boost/make_shared.hpp>
|
#include <boost/make_shared.hpp>
|
||||||
|
|
||||||
|
@ -186,10 +187,15 @@ void default_tests(torrent_status const& s)
|
||||||
TEST_CHECK(s.time_since_upload < 1351 + 10);
|
TEST_CHECK(s.time_since_upload < 1351 + 10);
|
||||||
TEST_CHECK(s.active_time < 1339 + 10);
|
TEST_CHECK(s.active_time < 1339 + 10);
|
||||||
|
|
||||||
TEST_EQUAL(s.finished_time, 1352);
|
TEST_CHECK(s.finished_time >= 1352);
|
||||||
TEST_EQUAL(s.seeding_time, 1340);
|
TEST_CHECK(s.seeding_time >= 1340);
|
||||||
TEST_EQUAL(s.added_time, 1347);
|
TEST_CHECK(s.added_time >= 1347);
|
||||||
TEST_EQUAL(s.completed_time, 1348);
|
TEST_CHECK(s.completed_time >= 1348);
|
||||||
|
|
||||||
|
TEST_CHECK(s.finished_time < 1352 + 5);
|
||||||
|
TEST_CHECK(s.seeding_time < 1340 + 5);
|
||||||
|
TEST_CHECK(s.added_time < 1347 + 5);
|
||||||
|
TEST_CHECK(s.completed_time < 1348 + 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_file_sizes(bool allocate)
|
void test_file_sizes(bool allocate)
|
||||||
|
@ -494,6 +500,39 @@ TORRENT_TEST(seed_mode_preserve)
|
||||||
test_seed_mode(false, false, false);
|
test_seed_mode(false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TORRENT_TEST(seed_mode_load_peers)
|
||||||
|
{
|
||||||
|
lt::session ses(settings());
|
||||||
|
boost::shared_ptr<torrent_info> ti = generate_torrent();
|
||||||
|
add_torrent_params p;
|
||||||
|
p.ti = ti;
|
||||||
|
p.save_path = ".";
|
||||||
|
|
||||||
|
entry rd;
|
||||||
|
|
||||||
|
rd["file-format"] = "libtorrent resume file";
|
||||||
|
rd["file-version"] = 1;
|
||||||
|
rd["info-hash"] = ti->info_hash().to_string();
|
||||||
|
rd["blocks per piece"] = std::max(1, ti->piece_length() / 0x4000);
|
||||||
|
|
||||||
|
rd["pieces"] = std::string(ti->num_pieces(), '\x01');
|
||||||
|
rd["piece_priority"] = std::string(ti->num_pieces(), '\x01');
|
||||||
|
rd["seed_mode"] = 1;
|
||||||
|
rd["peers"] = "\x01\x02\x03\x04\x30\x39";
|
||||||
|
|
||||||
|
bencode(back_inserter(p.resume_data), rd);
|
||||||
|
|
||||||
|
torrent_handle h = ses.add_torrent(p);
|
||||||
|
|
||||||
|
wait_for_alert(ses, torrent_checked_alert::alert_type, "seed_mode_load_peers");
|
||||||
|
|
||||||
|
std::vector<peer_list_entry> peers;
|
||||||
|
h.get_full_peer_list(peers);
|
||||||
|
|
||||||
|
TEST_EQUAL(peers.size(), 1);
|
||||||
|
TEST_CHECK(peers[0].ip == tcp::endpoint(address::from_string("1.2.3.4"), 12345));
|
||||||
|
}
|
||||||
|
|
||||||
TORRENT_TEST(resume_save_load)
|
TORRENT_TEST(resume_save_load)
|
||||||
{
|
{
|
||||||
lt::session ses(settings());
|
lt::session ses(settings());
|
||||||
|
|
Loading…
Reference in New Issue