factor out verify_resume_data to storage_utils

This commit is contained in:
arvidn 2017-01-20 00:01:00 -05:00 committed by Arvid Norberg
parent 8cc88a8921
commit 202386dd9d
4 changed files with 137 additions and 111 deletions

View File

@ -49,6 +49,8 @@ namespace libtorrent
class file_storage;
struct part_file;
struct storage_error;
struct stat_cache;
struct add_torrent_params;
#ifdef TORRENT_WINDOWS
struct iovec_t
@ -60,6 +62,8 @@ namespace libtorrent
using iovec_t = ::iovec;
#endif
namespace aux {
TORRENT_EXTRA_EXPORT int copy_bufs(span<iovec_t const> bufs, int bytes, span<iovec_t> target);
TORRENT_EXTRA_EXPORT span<iovec_t> advance_bufs(span<iovec_t> bufs, int bytes);
@ -97,7 +101,15 @@ namespace libtorrent
delete_files(file_storage const& fs, std::string const& save_path
, std::string const& part_file_name, int const options, storage_error& ec);
}
TORRENT_EXTRA_EXPORT bool
verify_resume_data(add_torrent_params const& rd
, aux::vector<std::string, file_index_t> const& links
, file_storage const& fs
, aux::vector<std::uint8_t, file_index_t> const& file_priority
, stat_cache& stat
, std::string const& save_path
, storage_error& ec);
}}
#endif

View File

@ -118,7 +118,7 @@ namespace libtorrent
std::memset(buf.iov_base, 0, buf.iov_len);
}
struct write_fileop final : fileop
struct write_fileop final : aux::fileop
{
write_fileop(default_storage& st, int flags)
: m_storage(st)
@ -198,7 +198,7 @@ namespace libtorrent
int m_flags;
};
struct read_fileop final : fileop
struct read_fileop final : aux::fileop
{
read_fileop(default_storage& st, int const flags)
: m_storage(st)
@ -620,7 +620,7 @@ namespace libtorrent
// delete it
if (m_part_file) m_part_file.reset();
libtorrent::delete_files(files(), m_save_path, m_part_file_name, options, ec);
aux::delete_files(files(), m_save_path, m_part_file_name, options, ec);
DFLOG(stderr, "[%p] delete_files result: %s\n", static_cast<void*>(this)
, ec.ec.message().c_str());
@ -630,104 +630,8 @@ namespace libtorrent
, aux::vector<std::string, file_index_t> const& links
, storage_error& ec)
{
file_storage const& fs = files();
#ifdef TORRENT_DISABLE_MUTABLE_TORRENTS
TORRENT_UNUSED(links);
#else
if (!links.empty())
{
TORRENT_ASSERT(int(links.size()) == fs.num_files());
// if this is a mutable torrent, and we need to pick up some files
// from other torrents, do that now. Note that there is an inherent
// race condition here. We checked if the files existed on a different
// thread a while ago. These files may no longer exist or may have been
// moved. If so, we just fail. The user is responsible to not touch
// other torrents until a new mutable torrent has been completely
// added.
for (file_index_t idx(0); idx < fs.end_file(); ++idx)
{
std::string const& s = links[idx];
if (s.empty()) continue;
error_code err;
std::string file_path = fs.file_path(idx, m_save_path);
hard_link(s, file_path, err);
// if the file already exists, that's not an error
// TODO: 2 is this risky? The upper layer will assume we have the
// whole file. Perhaps we should verify that at least the size
// of the file is correct
if (!err || err == boost::system::errc::file_exists)
continue;
ec.ec = err;
ec.file(idx);
ec.operation = storage_error::hard_link;
return false;
}
}
#endif // TORRENT_DISABLE_MUTABLE_TORRENTS
bool const seed = rd.have_pieces.all_set();
// parse have bitmask. Verify that the files we expect to have
// actually do exist
for (piece_index_t i(0); i < piece_index_t(rd.have_pieces.size()); ++i)
{
if (rd.have_pieces.get_bit(i) == false) continue;
std::vector<file_slice> f = fs.map_block(i, 0, 1);
TORRENT_ASSERT(!f.empty());
file_index_t const file_index = f[0].file_index;
// files with priority zero may not have been saved to disk at their
// expected location, but is likely to be in a partfile. Just exempt it
// from checking
if (file_index < m_file_priority.end_index()
&& m_file_priority[file_index] == 0)
continue;
error_code error;
std::int64_t const size = m_stat_cache.get_filesize(f[0].file_index
, fs, m_save_path, error);
if (size < 0)
{
if (error != boost::system::errc::no_such_file_or_directory)
{
ec.ec = error;
ec.file(file_index);
ec.operation = storage_error::stat;
return false;
}
else
{
ec.ec = errors::mismatching_file_size;
ec.file(file_index);
ec.operation = storage_error::stat;
return false;
}
}
if (seed && size != fs.file_size(file_index))
{
// the resume data indicates we're a seed, but this file has
// the wrong size. Reject the resume data
ec.ec = errors::mismatching_file_size;
ec.file(file_index);
ec.operation = storage_error::check_resume;
return false;
}
// OK, this file existed, good. Now, skip all remaining pieces in
// this file. We're just sanity-checking whether the files exist
// or not.
peer_request const pr = fs.map_file(file_index
, fs.file_size(file_index) + 1, 0);
i = std::max(next(i), pr.piece);
}
return true;
return aux::verify_resume_data(rd, links, files()
, m_file_priority, m_stat_cache, m_save_path, ec);
}
status_t default_storage::move_storage(std::string const& sp, int const flags
@ -736,7 +640,7 @@ namespace libtorrent
m_pool.release(storage_index());
status_t ret;
std::tie(ret, m_save_path) = libtorrent::move_storage(files(), m_save_path, sp
std::tie(ret, m_save_path) = aux::move_storage(files(), m_save_path, sp
, m_part_file.get(), flags, ec);
return ret;
}

View File

@ -36,11 +36,13 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/file.hpp" // for count_bufs
#include "libtorrent/part_file.hpp"
#include "libtorrent/session.hpp" // for session::delete_files
#include "libtorrent/stat_cache.hpp"
#include "libtorrent/add_torrent_params.hpp"
#include <set>
#include <string>
namespace libtorrent
namespace libtorrent { namespace aux
{
int copy_bufs(span<iovec_t const> bufs, int bytes, span<iovec_t> target)
{
@ -445,5 +447,113 @@ namespace libtorrent
}
}
}
}
bool verify_resume_data(add_torrent_params const& rd
, aux::vector<std::string, file_index_t> const& links
, file_storage const& fs
, aux::vector<std::uint8_t, file_index_t> const& file_priority
, stat_cache& stat
, std::string const& save_path
, storage_error& ec)
{
#ifdef TORRENT_DISABLE_MUTABLE_TORRENTS
TORRENT_UNUSED(links);
#else
if (!links.empty())
{
TORRENT_ASSERT(int(links.size()) == fs.num_files());
// if this is a mutable torrent, and we need to pick up some files
// from other torrents, do that now. Note that there is an inherent
// race condition here. We checked if the files existed on a different
// thread a while ago. These files may no longer exist or may have been
// moved. If so, we just fail. The user is responsible to not touch
// other torrents until a new mutable torrent has been completely
// added.
for (file_index_t idx(0); idx < fs.end_file(); ++idx)
{
std::string const& s = links[idx];
if (s.empty()) continue;
error_code err;
std::string file_path = fs.file_path(idx, save_path);
hard_link(s, file_path, err);
// if the file already exists, that's not an error
// TODO: 2 is this risky? The upper layer will assume we have the
// whole file. Perhaps we should verify that at least the size
// of the file is correct
if (!err || err == boost::system::errc::file_exists)
continue;
ec.ec = err;
ec.file(idx);
ec.operation = storage_error::hard_link;
return false;
}
}
#endif // TORRENT_DISABLE_MUTABLE_TORRENTS
bool const seed = rd.have_pieces.all_set();
// parse have bitmask. Verify that the files we expect to have
// actually do exist
for (piece_index_t i(0); i < piece_index_t(rd.have_pieces.size()); ++i)
{
if (rd.have_pieces.get_bit(i) == false) continue;
std::vector<file_slice> f = fs.map_block(i, 0, 1);
TORRENT_ASSERT(!f.empty());
file_index_t const file_index = f[0].file_index;
// files with priority zero may not have been saved to disk at their
// expected location, but is likely to be in a partfile. Just exempt it
// from checking
if (file_index < file_priority.end_index()
&& file_priority[file_index] == 0)
continue;
error_code error;
std::int64_t const size = stat.get_filesize(f[0].file_index
, fs, save_path, error);
if (size < 0)
{
if (error != boost::system::errc::no_such_file_or_directory)
{
ec.ec = error;
ec.file(file_index);
ec.operation = storage_error::stat;
return false;
}
else
{
ec.ec = errors::mismatching_file_size;
ec.file(file_index);
ec.operation = storage_error::stat;
return false;
}
}
if (seed && size != fs.file_size(file_index))
{
// the resume data indicates we're a seed, but this file has
// the wrong size. Reject the resume data
ec.ec = errors::mismatching_file_size;
ec.file(file_index);
ec.operation = storage_error::check_resume;
return false;
}
// OK, this file existed, good. Now, skip all remaining pieces in
// this file. We're just sanity-checking whether the files exist
// or not.
peer_request const pr = fs.map_file(file_index
, fs.file_size(file_index) + 1, 0);
i = std::max(next(i), pr.piece);
}
return true;
}
}}

View File

@ -990,7 +990,7 @@ TORRENT_TEST(iovec_copy_bufs)
TEST_CHECK(bufs_size({iov1, 10}) >= 106);
// copy exactly 106 bytes from iov1 to iov2
int num_bufs = copy_bufs(iov1, 106, iov2);
int num_bufs = aux::copy_bufs(iov1, 106, iov2);
// verify that the first 100 bytes is pattern 1
// and that the remaining bytes are pattern 2
@ -1057,7 +1057,7 @@ TORRENT_TEST(iovec_advance_bufs)
// advance iov 13 bytes. Make sure what's left fits pattern 1 shifted
// 13 bytes
iov = advance_bufs(iov, 13);
iov = aux::advance_bufs(iov, 13);
// make sure what's in
int counter = 13;
@ -1089,7 +1089,7 @@ file_storage make_fs()
return fs;
}
struct test_fileop : libtorrent::fileop
struct test_fileop : aux::fileop
{
explicit test_fileop(int stripe_size) : m_stripe_size(stripe_size) {}
@ -1127,7 +1127,7 @@ struct test_fileop : libtorrent::fileop
aux::vector<std::vector<char>, file_index_t> m_file_data;
};
struct test_read_fileop : fileop
struct test_read_fileop : aux::fileop
{
// EOF after size bytes read
explicit test_read_fileop(int size) : m_size(size), m_counter(0) {}
@ -1157,7 +1157,7 @@ struct test_read_fileop : fileop
int m_counter;
};
struct test_error_fileop : fileop
struct test_error_fileop : aux::fileop
{
// EOF after size bytes read
explicit test_error_fileop(file_index_t error_file)
@ -1207,7 +1207,7 @@ TORRENT_TEST(readwritev_stripe_1)
TEST_CHECK(bufs_size({iov, size_t(num_bufs)}) >= fs.total_size());
iovec_t iov2[num_bufs];
copy_bufs(iov, int(fs.total_size()), iov2);
aux::copy_bufs(iov, int(fs.total_size()), iov2);
int num_bufs2 = count_bufs(iov2, int(fs.total_size()));
TEST_CHECK(num_bufs2 <= num_bufs);