fix backwards compatibility issue when loading the torrent info dict from resume data

This commit is contained in:
arvidn 2017-06-29 17:24:24 +03:00 committed by Arvid Norberg
parent b13ac50f4e
commit 3c8450d47c
4 changed files with 198 additions and 127 deletions

View File

@ -49,6 +49,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <map> #include <map>
#include <cstring> #include <cstring>
#include <deque> #include <deque>
#include <mutex>
#ifdef __MACH__ #ifdef __MACH__
#include <mach/task_info.h> #include <mach/task_info.h>

View File

@ -45,22 +45,23 @@ namespace libtorrent {
namespace { namespace {
void apply_flag(std::uint64_t& current_flags void apply_flag(std::uint64_t& current_flags
, bdecode_node const& n , bdecode_node const& n
, char const* name , char const* name
, std::uint64_t const flag) , std::uint64_t const flag)
{
if (n.dict_find_int_value(name, 0) == 0)
{ {
if (n.dict_find_int_value(name, 0) == 0) current_flags &= ~flag;
{ }
current_flags &= ~flag; else
} {
else current_flags |= flag;
{
current_flags |= flag;
}
} }
} }
} // anonyous namespace
add_torrent_params read_resume_data(bdecode_node const& rd, error_code& ec) add_torrent_params read_resume_data(bdecode_node const& rd, error_code& ec)
{ {
add_torrent_params ret; add_torrent_params ret;
@ -89,7 +90,6 @@ namespace {
ret.info_hash.assign(info_hash.data()); ret.info_hash.assign(info_hash.data());
// TODO: 4 add unit test for this, and all other fields of the resume data
bdecode_node const info = rd.dict_find_dict("info"); bdecode_node const info = rd.dict_find_dict("info");
if (info) if (info)
{ {

View File

@ -201,121 +201,128 @@ namespace libtorrent {
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
namespace { namespace {
void handle_backwards_compatible_resume_data(add_torrent_params& atp) void handle_backwards_compatible_resume_data(add_torrent_params& atp)
{
// if there's no resume data set, there's nothing to do. It's either
// using the previous API without resume data, or the resume data has
// already been parsed out into the add_torrent_params struct.
if (atp.resume_data.empty()) return;
error_code ec;
add_torrent_params resume_data
= read_resume_data(atp.resume_data, ec);
resume_data.internal_resume_data_error = ec;
if (ec) return;
// now, merge resume_data into atp according to the merge flags
if (atp.flags & add_torrent_params::flag_use_resume_save_path
&& !resume_data.save_path.empty())
{ {
// if there's no resume data set, there's nothing to do. It's either atp.save_path = std::move(resume_data.save_path);
// using the previous API without resume data, or the resume data has }
// already been parsed out into the add_torrent_params struct.
if (atp.resume_data.empty()) return;
error_code ec; if (!atp.ti)
add_torrent_params resume_data {
= read_resume_data(atp.resume_data, ec); atp.ti = std::move(resume_data.ti);
}
resume_data.internal_resume_data_error = ec; if (!resume_data.trackers.empty())
if (ec) return; {
atp.tracker_tiers.resize(atp.trackers.size(), 0);
atp.trackers.insert(atp.trackers.end()
, resume_data.trackers.begin()
, resume_data.trackers.end());
atp.tracker_tiers.insert(atp.tracker_tiers.end()
, resume_data.tracker_tiers.begin()
, resume_data.tracker_tiers.end());
if ((resume_data.flags & add_torrent_params::flag_merge_resume_trackers) == 0)
atp.flags |= add_torrent_params::flag_override_trackers;
}
// now, merge resume_data into atp according to the merge flags if (!resume_data.url_seeds.empty())
if (atp.flags & add_torrent_params::flag_use_resume_save_path {
&& !resume_data.save_path.empty()) if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
{ atp.url_seeds.clear();
atp.save_path = resume_data.save_path;
}
if (!resume_data.trackers.empty()) atp.url_seeds.insert(atp.url_seeds.end()
{ , resume_data.url_seeds.begin()
atp.tracker_tiers.resize(atp.trackers.size(), 0); , resume_data.url_seeds.end());
atp.trackers.insert(atp.trackers.end() if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
, resume_data.trackers.begin() atp.flags |= add_torrent_params::flag_override_web_seeds;
, resume_data.trackers.end()); }
atp.tracker_tiers.insert(atp.tracker_tiers.end()
, resume_data.tracker_tiers.begin()
, resume_data.tracker_tiers.end());
if ((resume_data.flags & add_torrent_params::flag_merge_resume_trackers) == 0)
atp.flags |= add_torrent_params::flag_override_trackers;
}
if (!resume_data.url_seeds.empty()) if (!resume_data.http_seeds.empty())
{ {
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0) if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.url_seeds.clear(); atp.http_seeds.clear();
atp.url_seeds.insert(atp.url_seeds.end() atp.http_seeds.insert(atp.http_seeds.end()
, resume_data.url_seeds.begin() , resume_data.http_seeds.begin()
, resume_data.url_seeds.end()); , resume_data.http_seeds.end());
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0) if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.flags |= add_torrent_params::flag_override_web_seeds; atp.flags |= add_torrent_params::flag_override_web_seeds;
} }
if (!resume_data.http_seeds.empty()) atp.total_uploaded = resume_data.total_uploaded;
{ atp.total_downloaded = resume_data.total_downloaded;
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0) atp.num_complete = resume_data.num_complete;
atp.http_seeds.clear(); atp.num_incomplete = resume_data.num_incomplete;
atp.num_downloaded = resume_data.num_downloaded;
atp.total_uploaded = resume_data.total_uploaded;
atp.total_downloaded = resume_data.total_downloaded;
atp.active_time = resume_data.active_time;
atp.finished_time = resume_data.finished_time;
atp.seeding_time = resume_data.seeding_time;
atp.http_seeds.insert(atp.http_seeds.end() atp.last_seen_complete = resume_data.last_seen_complete;
, resume_data.http_seeds.begin() atp.url = resume_data.url;
, resume_data.http_seeds.end()); atp.uuid = resume_data.uuid;
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.flags |= add_torrent_params::flag_override_web_seeds;
}
atp.total_uploaded = resume_data.total_uploaded; atp.added_time = resume_data.added_time;
atp.total_downloaded = resume_data.total_downloaded; atp.completed_time = resume_data.completed_time;
atp.num_complete = resume_data.num_complete;
atp.num_incomplete = resume_data.num_incomplete;
atp.num_downloaded = resume_data.num_downloaded;
atp.total_uploaded = resume_data.total_uploaded;
atp.total_downloaded = resume_data.total_downloaded;
atp.active_time = resume_data.active_time;
atp.finished_time = resume_data.finished_time;
atp.seeding_time = resume_data.seeding_time;
atp.last_seen_complete = resume_data.last_seen_complete; atp.peers.swap(resume_data.peers);
atp.url = resume_data.url; atp.banned_peers.swap(resume_data.banned_peers);
atp.uuid = resume_data.uuid;
atp.added_time = resume_data.added_time; atp.unfinished_pieces.swap(resume_data.unfinished_pieces);
atp.completed_time = resume_data.completed_time; atp.have_pieces.swap(resume_data.have_pieces);
atp.verified_pieces.swap(resume_data.verified_pieces);
atp.piece_priorities.swap(resume_data.piece_priorities);
atp.peers.swap(resume_data.peers); atp.merkle_tree = std::move(resume_data.merkle_tree);
atp.banned_peers.swap(resume_data.banned_peers);
atp.unfinished_pieces.swap(resume_data.unfinished_pieces); atp.renamed_files = std::move(resume_data.renamed_files);
atp.have_pieces.swap(resume_data.have_pieces);
atp.verified_pieces.swap(resume_data.verified_pieces);
atp.piece_priorities.swap(resume_data.piece_priorities);
atp.merkle_tree.swap(resume_data.merkle_tree); if ((atp.flags & add_torrent_params::flag_override_resume_data) == 0)
{
atp.download_limit = resume_data.download_limit;
atp.upload_limit = resume_data.upload_limit;
atp.max_connections = resume_data.max_connections;
atp.max_uploads = resume_data.max_uploads;
atp.trackerid = resume_data.trackerid;
if (!resume_data.file_priorities.empty())
atp.file_priorities = resume_data.file_priorities;
atp.renamed_files.swap(resume_data.renamed_files); std::uint64_t const mask =
add_torrent_params::flag_seed_mode
| add_torrent_params::flag_super_seeding
| add_torrent_params::flag_auto_managed
| add_torrent_params::flag_sequential_download
| add_torrent_params::flag_paused;
if ((atp.flags & add_torrent_params::flag_override_resume_data) == 0) atp.flags &= ~mask;
{ atp.flags |= resume_data.flags & mask;
atp.download_limit = resume_data.download_limit; }
atp.upload_limit = resume_data.upload_limit; else
atp.max_connections = resume_data.max_connections; {
atp.max_uploads = resume_data.max_uploads; if (atp.file_priorities.empty())
atp.trackerid = resume_data.trackerid; atp.file_priorities = resume_data.file_priorities;
if (!resume_data.file_priorities.empty())
atp.file_priorities = resume_data.file_priorities;
std::uint64_t const mask =
add_torrent_params::flag_seed_mode
| add_torrent_params::flag_super_seeding
| add_torrent_params::flag_auto_managed
| add_torrent_params::flag_sequential_download
| add_torrent_params::flag_paused;
atp.flags &= ~mask;
atp.flags |= resume_data.flags & mask;
}
else
{
if (atp.file_priorities.empty())
atp.file_priorities = resume_data.file_priorities;
}
} }
} }
} // anonymous namespace
#endif #endif
#ifndef BOOST_NO_EXCEPTIONS #ifndef BOOST_NO_EXCEPTIONS

View File

@ -751,19 +751,80 @@ TORRENT_TEST(zero_file_prio_deprecated)
{ {
test_zero_file_prio(true); test_zero_file_prio(true);
} }
TORRENT_TEST(backwards_compatible_resume_info_dict)
{
// make sure the "info" dictionary is picked up correctly from the
// resume data in backwards compatible mode
std::shared_ptr<torrent_info> ti = generate_torrent();
entry rd;
rd["file-format"] = "libtorrent resume file";
rd["name"] = ti->name();
rd["info-hash"] = ti->info_hash();
auto metainfo = ti->metadata();
rd["info"] = bdecode(metainfo.get(), metainfo.get() + ti->metadata_size());
std::vector<char> resume_data;
bencode(back_inserter(resume_data), rd);
add_torrent_params atp;
atp.resume_data = std::move(resume_data);
atp.save_path = ".";
session ses;
torrent_handle h = ses.add_torrent(atp);
auto torrent = h.torrent_file();
TEST_CHECK(torrent->info_hash() == ti->info_hash());
torrent_status s = h.status();
}
#endif #endif
TORRENT_TEST(resume_info_dict)
{
// make sure the "info" dictionary is picked up correctly from the
// resume data
std::shared_ptr<torrent_info> ti = generate_torrent();
entry rd;
rd["file-format"] = "libtorrent resume file";
rd["name"] = ti->name();
rd["info-hash"] = ti->info_hash();
auto metainfo = ti->metadata();
rd["info"] = bdecode(metainfo.get(), metainfo.get() + ti->metadata_size());
std::vector<char> resume_data;
bencode(back_inserter(resume_data), rd);
error_code ec;
add_torrent_params atp = read_resume_data(resume_data, ec);
TEST_CHECK(atp.ti->info_hash() == ti->info_hash());
}
TORRENT_TEST(zero_file_prio) TORRENT_TEST(zero_file_prio)
{ {
test_zero_file_prio(); test_zero_file_prio();
} }
void test_seed_mode(bool const file_prio, bool const pieces_have, bool const piece_prio enum class test_mode_t
, bool const all_files_zero = false, bool const test_deprecated = false)
{ {
std::printf("test_seed_mode file_prio: %d pieces_have: %d piece_prio: %d\n" none = 0,
, file_prio, pieces_have, piece_prio); file_prio = 1,
pieces_have = 2,
piece_prio = 4,
all_files_zero = 8,
deprecated = 16
};
namespace libtorrent {
namespace flags {
template <>
struct enable_flag_operators<test_mode_t> : std::true_type {};
}
}
void test_seed_mode(test_mode_t const flags)
{
lt::session ses(settings()); lt::session ses(settings());
std::shared_ptr<torrent_info> ti = generate_torrent(); std::shared_ptr<torrent_info> ti = generate_torrent();
add_torrent_params p; add_torrent_params p;
@ -777,12 +838,12 @@ void test_seed_mode(bool const file_prio, bool const pieces_have, bool const pie
rd["info-hash"] = ti->info_hash().to_string(); rd["info-hash"] = ti->info_hash().to_string();
rd["blocks per piece"] = (std::max)(1, ti->piece_length() / 0x4000); rd["blocks per piece"] = (std::max)(1, ti->piece_length() / 0x4000);
if (file_prio) if (test(flags & test_mode_t::file_prio))
{ {
// this should take it out of seed_mode // this should take it out of seed_mode
entry::list_type& file_prio = rd["file_priority"].list(); entry::list_type& file_prio = rd["file_priority"].list();
file_prio.push_back(entry(0)); file_prio.push_back(entry(0));
if (all_files_zero) if (test(flags & test_mode_t::all_files_zero))
{ {
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{ {
@ -792,14 +853,14 @@ void test_seed_mode(bool const file_prio, bool const pieces_have, bool const pie
} }
std::string pieces(ti->num_pieces(), '\x01'); std::string pieces(ti->num_pieces(), '\x01');
if (pieces_have) if (test(flags & test_mode_t::pieces_have))
{ {
pieces[0] = '\0'; pieces[0] = '\0';
} }
rd["pieces"] = pieces; rd["pieces"] = pieces;
std::string pieces_prio(ti->num_pieces(), '\x01'); std::string pieces_prio(ti->num_pieces(), '\x01');
if (piece_prio) if (test(flags & test_mode_t::piece_prio))
{ {
pieces_prio[0] = '\0'; pieces_prio[0] = '\0';
} }
@ -811,7 +872,7 @@ void test_seed_mode(bool const file_prio, bool const pieces_have, bool const pie
bencode(back_inserter(resume_data), rd); bencode(back_inserter(resume_data), rd);
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
if (test_deprecated) if (test(flags & test_mode_t::deprecated))
{ {
p.resume_data = resume_data; p.resume_data = resume_data;
} }
@ -828,7 +889,9 @@ void test_seed_mode(bool const file_prio, bool const pieces_have, bool const pie
torrent_handle h = ses.add_torrent(p); torrent_handle h = ses.add_torrent(p);
torrent_status s = h.status(); torrent_status s = h.status();
if (file_prio || piece_prio || pieces_have) if (test(flags & (test_mode_t::file_prio
| test_mode_t::piece_prio
| test_mode_t::pieces_have)))
{ {
TEST_EQUAL(s.seed_mode, false); TEST_EQUAL(s.seed_mode, false);
} }
@ -840,43 +903,43 @@ void test_seed_mode(bool const file_prio, bool const pieces_have, bool const pie
#ifndef TORRENT_NO_DEPRECATE #ifndef TORRENT_NO_DEPRECATE
TORRENT_TEST(seed_mode_file_prio_deprecated) TORRENT_TEST(seed_mode_file_prio_deprecated)
{ {
test_seed_mode(true, false, false, true); test_seed_mode(test_mode_t::file_prio | test_mode_t::deprecated);
} }
TORRENT_TEST(seed_mode_piece_prio_deprecated) TORRENT_TEST(seed_mode_piece_prio_deprecated)
{ {
test_seed_mode(false, true, false, true); test_seed_mode(test_mode_t::pieces_have | test_mode_t::deprecated);
} }
TORRENT_TEST(seed_mode_piece_have_deprecated) TORRENT_TEST(seed_mode_piece_have_deprecated)
{ {
test_seed_mode(false, false, true, true); test_seed_mode(test_mode_t::piece_prio | test_mode_t::deprecated);
} }
TORRENT_TEST(seed_mode_preserve_deprecated) TORRENT_TEST(seed_mode_preserve_deprecated)
{ {
test_seed_mode(false, false, false, true); test_seed_mode(test_mode_t::deprecated);
} }
#endif #endif
TORRENT_TEST(seed_mode_file_prio) TORRENT_TEST(seed_mode_file_prio)
{ {
test_seed_mode(true, false, false); test_seed_mode(test_mode_t::file_prio);
} }
TORRENT_TEST(seed_mode_piece_prio) TORRENT_TEST(seed_mode_piece_prio)
{ {
test_seed_mode(false, true, false); test_seed_mode(test_mode_t::pieces_have);
} }
TORRENT_TEST(seed_mode_piece_have) TORRENT_TEST(seed_mode_piece_have)
{ {
test_seed_mode(false, false, true); test_seed_mode(test_mode_t::piece_prio);
} }
TORRENT_TEST(seed_mode_preserve) TORRENT_TEST(seed_mode_preserve)
{ {
test_seed_mode(false, false, false); test_seed_mode(test_mode_t::none);
} }
TORRENT_TEST(resume_save_load) TORRENT_TEST(resume_save_load)