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 <cstring>
#include <deque>
#include <mutex>
#ifdef __MACH__
#include <mach/task_info.h>

View File

@ -45,22 +45,23 @@ namespace libtorrent {
namespace {
void apply_flag(std::uint64_t& current_flags
, bdecode_node const& n
, char const* name
, std::uint64_t const flag)
void apply_flag(std::uint64_t& current_flags
, bdecode_node const& n
, char const* name
, 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;
}
else
{
current_flags |= flag;
}
current_flags &= ~flag;
}
else
{
current_flags |= flag;
}
}
} // anonyous namespace
add_torrent_params read_resume_data(bdecode_node const& rd, error_code& ec)
{
add_torrent_params ret;
@ -89,7 +90,6 @@ namespace {
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");
if (info)
{

View File

@ -201,121 +201,128 @@ namespace libtorrent {
#ifndef TORRENT_NO_DEPRECATE
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
// 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;
atp.save_path = std::move(resume_data.save_path);
}
error_code ec;
add_torrent_params resume_data
= read_resume_data(atp.resume_data, ec);
if (!atp.ti)
{
atp.ti = std::move(resume_data.ti);
}
resume_data.internal_resume_data_error = ec;
if (ec) return;
if (!resume_data.trackers.empty())
{
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 (atp.flags & add_torrent_params::flag_use_resume_save_path
&& !resume_data.save_path.empty())
{
atp.save_path = resume_data.save_path;
}
if (!resume_data.url_seeds.empty())
{
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.url_seeds.clear();
if (!resume_data.trackers.empty())
{
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;
}
atp.url_seeds.insert(atp.url_seeds.end()
, resume_data.url_seeds.begin()
, resume_data.url_seeds.end());
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.flags |= add_torrent_params::flag_override_web_seeds;
}
if (!resume_data.url_seeds.empty())
{
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.url_seeds.clear();
if (!resume_data.http_seeds.empty())
{
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.http_seeds.clear();
atp.url_seeds.insert(atp.url_seeds.end()
, resume_data.url_seeds.begin()
, resume_data.url_seeds.end());
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.flags |= add_torrent_params::flag_override_web_seeds;
}
atp.http_seeds.insert(atp.http_seeds.end()
, resume_data.http_seeds.begin()
, resume_data.http_seeds.end());
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.flags |= add_torrent_params::flag_override_web_seeds;
}
if (!resume_data.http_seeds.empty())
{
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.http_seeds.clear();
atp.total_uploaded = resume_data.total_uploaded;
atp.total_downloaded = resume_data.total_downloaded;
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.http_seeds.insert(atp.http_seeds.end()
, resume_data.http_seeds.begin()
, resume_data.http_seeds.end());
if ((atp.flags & add_torrent_params::flag_merge_resume_http_seeds) == 0)
atp.flags |= add_torrent_params::flag_override_web_seeds;
}
atp.last_seen_complete = resume_data.last_seen_complete;
atp.url = resume_data.url;
atp.uuid = resume_data.uuid;
atp.total_uploaded = resume_data.total_uploaded;
atp.total_downloaded = resume_data.total_downloaded;
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.added_time = resume_data.added_time;
atp.completed_time = resume_data.completed_time;
atp.last_seen_complete = resume_data.last_seen_complete;
atp.url = resume_data.url;
atp.uuid = resume_data.uuid;
atp.peers.swap(resume_data.peers);
atp.banned_peers.swap(resume_data.banned_peers);
atp.added_time = resume_data.added_time;
atp.completed_time = resume_data.completed_time;
atp.unfinished_pieces.swap(resume_data.unfinished_pieces);
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.banned_peers.swap(resume_data.banned_peers);
atp.merkle_tree = std::move(resume_data.merkle_tree);
atp.unfinished_pieces.swap(resume_data.unfinished_pieces);
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.renamed_files = std::move(resume_data.renamed_files);
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.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;
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;
}
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
#ifndef BOOST_NO_EXCEPTIONS

View File

@ -751,19 +751,80 @@ TORRENT_TEST(zero_file_prio_deprecated)
{
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
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)
{
test_zero_file_prio();
}
void test_seed_mode(bool const file_prio, bool const pieces_have, bool const piece_prio
, bool const all_files_zero = false, bool const test_deprecated = false)
enum class test_mode_t
{
std::printf("test_seed_mode file_prio: %d pieces_have: %d piece_prio: %d\n"
, file_prio, pieces_have, piece_prio);
none = 0,
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());
std::shared_ptr<torrent_info> ti = generate_torrent();
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["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
entry::list_type& file_prio = rd["file_priority"].list();
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)
{
@ -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');
if (pieces_have)
if (test(flags & test_mode_t::pieces_have))
{
pieces[0] = '\0';
}
rd["pieces"] = pieces;
std::string pieces_prio(ti->num_pieces(), '\x01');
if (piece_prio)
if (test(flags & test_mode_t::piece_prio))
{
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);
#ifndef TORRENT_NO_DEPRECATE
if (test_deprecated)
if (test(flags & test_mode_t::deprecated))
{
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_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);
}
@ -840,43 +903,43 @@ void test_seed_mode(bool const file_prio, bool const pieces_have, bool const pie
#ifndef TORRENT_NO_DEPRECATE
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)
{
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)
{
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)
{
test_seed_mode(false, false, false, true);
test_seed_mode(test_mode_t::deprecated);
}
#endif
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)
{
test_seed_mode(false, true, false);
test_seed_mode(test_mode_t::pieces_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)
{
test_seed_mode(false, false, false);
test_seed_mode(test_mode_t::none);
}
TORRENT_TEST(resume_save_load)