factor out verify_resume_data to storage_utils
This commit is contained in:
parent
8cc88a8921
commit
202386dd9d
|
@ -49,6 +49,8 @@ namespace libtorrent
|
||||||
class file_storage;
|
class file_storage;
|
||||||
struct part_file;
|
struct part_file;
|
||||||
struct storage_error;
|
struct storage_error;
|
||||||
|
struct stat_cache;
|
||||||
|
struct add_torrent_params;
|
||||||
|
|
||||||
#ifdef TORRENT_WINDOWS
|
#ifdef TORRENT_WINDOWS
|
||||||
struct iovec_t
|
struct iovec_t
|
||||||
|
@ -60,6 +62,8 @@ namespace libtorrent
|
||||||
using iovec_t = ::iovec;
|
using iovec_t = ::iovec;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace aux {
|
||||||
|
|
||||||
TORRENT_EXTRA_EXPORT int copy_bufs(span<iovec_t const> bufs, int bytes, span<iovec_t> target);
|
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);
|
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
|
delete_files(file_storage const& fs, std::string const& save_path
|
||||||
, std::string const& part_file_name, int const options, storage_error& ec);
|
, 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
|
#endif
|
||||||
|
|
||||||
|
|
108
src/storage.cpp
108
src/storage.cpp
|
@ -118,7 +118,7 @@ namespace libtorrent
|
||||||
std::memset(buf.iov_base, 0, buf.iov_len);
|
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)
|
write_fileop(default_storage& st, int flags)
|
||||||
: m_storage(st)
|
: m_storage(st)
|
||||||
|
@ -198,7 +198,7 @@ namespace libtorrent
|
||||||
int m_flags;
|
int m_flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct read_fileop final : fileop
|
struct read_fileop final : aux::fileop
|
||||||
{
|
{
|
||||||
read_fileop(default_storage& st, int const flags)
|
read_fileop(default_storage& st, int const flags)
|
||||||
: m_storage(st)
|
: m_storage(st)
|
||||||
|
@ -620,7 +620,7 @@ namespace libtorrent
|
||||||
// delete it
|
// delete it
|
||||||
if (m_part_file) m_part_file.reset();
|
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)
|
DFLOG(stderr, "[%p] delete_files result: %s\n", static_cast<void*>(this)
|
||||||
, ec.ec.message().c_str());
|
, ec.ec.message().c_str());
|
||||||
|
@ -630,104 +630,8 @@ namespace libtorrent
|
||||||
, aux::vector<std::string, file_index_t> const& links
|
, aux::vector<std::string, file_index_t> const& links
|
||||||
, storage_error& ec)
|
, storage_error& ec)
|
||||||
{
|
{
|
||||||
file_storage const& fs = files();
|
return aux::verify_resume_data(rd, links, files()
|
||||||
#ifdef TORRENT_DISABLE_MUTABLE_TORRENTS
|
, m_file_priority, m_stat_cache, m_save_path, ec);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status_t default_storage::move_storage(std::string const& sp, int const flags
|
status_t default_storage::move_storage(std::string const& sp, int const flags
|
||||||
|
@ -736,7 +640,7 @@ namespace libtorrent
|
||||||
m_pool.release(storage_index());
|
m_pool.release(storage_index());
|
||||||
|
|
||||||
status_t ret;
|
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);
|
, m_part_file.get(), flags, ec);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,13 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
#include "libtorrent/file.hpp" // for count_bufs
|
#include "libtorrent/file.hpp" // for count_bufs
|
||||||
#include "libtorrent/part_file.hpp"
|
#include "libtorrent/part_file.hpp"
|
||||||
#include "libtorrent/session.hpp" // for session::delete_files
|
#include "libtorrent/session.hpp" // for session::delete_files
|
||||||
|
#include "libtorrent/stat_cache.hpp"
|
||||||
|
#include "libtorrent/add_torrent_params.hpp"
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace libtorrent
|
namespace libtorrent { namespace aux
|
||||||
{
|
{
|
||||||
int copy_bufs(span<iovec_t const> bufs, int bytes, span<iovec_t> target)
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
|
@ -990,7 +990,7 @@ TORRENT_TEST(iovec_copy_bufs)
|
||||||
TEST_CHECK(bufs_size({iov1, 10}) >= 106);
|
TEST_CHECK(bufs_size({iov1, 10}) >= 106);
|
||||||
|
|
||||||
// copy exactly 106 bytes from iov1 to iov2
|
// 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
|
// verify that the first 100 bytes is pattern 1
|
||||||
// and that the remaining bytes are pattern 2
|
// 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
|
// advance iov 13 bytes. Make sure what's left fits pattern 1 shifted
|
||||||
// 13 bytes
|
// 13 bytes
|
||||||
iov = advance_bufs(iov, 13);
|
iov = aux::advance_bufs(iov, 13);
|
||||||
|
|
||||||
// make sure what's in
|
// make sure what's in
|
||||||
int counter = 13;
|
int counter = 13;
|
||||||
|
@ -1089,7 +1089,7 @@ file_storage make_fs()
|
||||||
return fs;
|
return fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct test_fileop : libtorrent::fileop
|
struct test_fileop : aux::fileop
|
||||||
{
|
{
|
||||||
explicit test_fileop(int stripe_size) : m_stripe_size(stripe_size) {}
|
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;
|
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
|
// EOF after size bytes read
|
||||||
explicit test_read_fileop(int size) : m_size(size), m_counter(0) {}
|
explicit test_read_fileop(int size) : m_size(size), m_counter(0) {}
|
||||||
|
@ -1157,7 +1157,7 @@ struct test_read_fileop : fileop
|
||||||
int m_counter;
|
int m_counter;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct test_error_fileop : fileop
|
struct test_error_fileop : aux::fileop
|
||||||
{
|
{
|
||||||
// EOF after size bytes read
|
// EOF after size bytes read
|
||||||
explicit test_error_fileop(file_index_t error_file)
|
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());
|
TEST_CHECK(bufs_size({iov, size_t(num_bufs)}) >= fs.total_size());
|
||||||
|
|
||||||
iovec_t iov2[num_bufs];
|
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()));
|
int num_bufs2 = count_bufs(iov2, int(fs.total_size()));
|
||||||
TEST_CHECK(num_bufs2 <= num_bufs);
|
TEST_CHECK(num_bufs2 <= num_bufs);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue