changed storage interface to not require exceptions
This commit is contained in:
parent
c6de20173f
commit
7e83c3fc51
|
@ -470,7 +470,8 @@ void add_torrent(libtorrent::session& ses
|
|||
catch (boost::filesystem::filesystem_error&) {}
|
||||
|
||||
torrent_handle h = ses.add_torrent(t, save_path, resume_data
|
||||
, compact_mode ? storage_mode_compact : storage_mode_sparse, false);
|
||||
, compact_mode ? storage_mode_compact : storage_mode_sparse, false
|
||||
, mapped_storage_constructor);
|
||||
handles.insert(std::make_pair(
|
||||
monitored_dir?std::string(torrent):std::string(), h));
|
||||
|
||||
|
@ -871,7 +872,7 @@ int main(int ac, char* av[])
|
|||
|
||||
torrent_handle h = ses.add_torrent(std::string(what[2]).c_str()
|
||||
, info_hash, 0, save_path, entry(), compact_allocation_mode ? storage_mode_compact
|
||||
: storage_mode_sparse);
|
||||
: storage_mode_sparse, false, mapped_storage_constructor);
|
||||
handles.insert(std::make_pair(std::string(), h));
|
||||
|
||||
h.set_max_connections(50);
|
||||
|
|
|
@ -111,7 +111,7 @@ namespace libtorrent
|
|||
|
||||
void open(fs::path const& p, open_mode m);
|
||||
void close();
|
||||
void set_size(size_type size);
|
||||
bool set_size(size_type size);
|
||||
|
||||
size_type write(const char*, size_type num_bytes);
|
||||
size_type read(char*, size_type num_bytes);
|
||||
|
@ -119,6 +119,8 @@ namespace libtorrent
|
|||
size_type seek(size_type pos, seek_mode m = begin);
|
||||
size_type tell();
|
||||
|
||||
std::string const& error() const;
|
||||
|
||||
private:
|
||||
|
||||
struct impl;
|
||||
|
|
|
@ -119,31 +119,33 @@ namespace libtorrent
|
|||
// if allocate_files is true.
|
||||
// allocate_files is true if allocation mode
|
||||
// is set to full and sparse files are supported
|
||||
virtual void initialize(bool allocate_files) = 0;
|
||||
virtual bool initialize(bool allocate_files) = 0;
|
||||
|
||||
// may throw file_error if storage for slot does not exist
|
||||
// negative return value indicates an error
|
||||
virtual size_type read(char* buf, int slot, int offset, int size) = 0;
|
||||
|
||||
// may throw file_error if storage for slot hasn't been allocated
|
||||
virtual void write(const char* buf, int slot, int offset, int size) = 0;
|
||||
// negative return value indicates an error
|
||||
virtual size_type write(const char* buf, int slot, int offset, int size) = 0;
|
||||
|
||||
// non-zero return value indicates an error
|
||||
virtual bool move_storage(fs::path save_path) = 0;
|
||||
|
||||
// verify storage dependent fast resume entries
|
||||
virtual bool verify_resume_data(entry& rd, std::string& error) = 0;
|
||||
virtual bool verify_resume_data(entry const& rd, std::string& error) = 0;
|
||||
|
||||
// write storage dependent fast resume entries
|
||||
virtual void write_resume_data(entry& rd) const = 0;
|
||||
virtual bool write_resume_data(entry& rd) const = 0;
|
||||
|
||||
// moves (or copies) the content in src_slot to dst_slot
|
||||
virtual void move_slot(int src_slot, int dst_slot) = 0;
|
||||
virtual bool move_slot(int src_slot, int dst_slot) = 0;
|
||||
|
||||
// swaps the data in slot1 and slot2
|
||||
virtual void swap_slots(int slot1, int slot2) = 0;
|
||||
virtual bool swap_slots(int slot1, int slot2) = 0;
|
||||
|
||||
// swaps the puts the data in slot1 in slot2, the data in slot2
|
||||
// in slot3 and the data in slot3 in slot1
|
||||
virtual void swap_slots3(int slot1, int slot2, int slot3) = 0;
|
||||
virtual bool swap_slots3(int slot1, int slot2, int slot3) = 0;
|
||||
|
||||
// returns the sha1-hash for the data at the given slot
|
||||
virtual sha1_hash hash_for_slot(int slot, partial_hash& h, int piece_size) = 0;
|
||||
|
@ -151,10 +153,15 @@ namespace libtorrent
|
|||
// this will close all open files that are opened for
|
||||
// writing. This is called when a torrent has finished
|
||||
// downloading.
|
||||
virtual void release_files() = 0;
|
||||
// non-zero return value indicates an error
|
||||
virtual bool release_files() = 0;
|
||||
|
||||
// this will close all open files and delete them
|
||||
virtual void delete_files() = 0;
|
||||
// non-zero return value indicates an error
|
||||
virtual bool delete_files() = 0;
|
||||
|
||||
virtual std::string const& error() const = 0;
|
||||
virtual void clear_error() = 0;
|
||||
|
||||
virtual ~storage_interface() {}
|
||||
};
|
||||
|
@ -197,19 +204,25 @@ namespace libtorrent
|
|||
, std::vector<bool>& pieces, int& num_pieces, storage_mode_t storage_mode
|
||||
, std::string& error_msg);
|
||||
std::pair<bool, float> check_files(std::vector<bool>& pieces
|
||||
, int& num_pieces, boost::recursive_mutex& mutex);
|
||||
, int& num_pieces, boost::recursive_mutex& mutex, bool& error);
|
||||
|
||||
// frees a buffer that was returned from a read operation
|
||||
void free_buffer(char* buf);
|
||||
|
||||
void write_resume_data(entry& rd) const;
|
||||
bool verify_resume_data(entry& rd, std::string& error);
|
||||
void write_resume_data(entry& rd) const
|
||||
{ m_storage->write_resume_data(rd); }
|
||||
|
||||
bool verify_resume_data(entry const& rd, std::string& error)
|
||||
{ return m_storage->verify_resume_data(rd, error); }
|
||||
|
||||
bool is_allocating() const
|
||||
{ return m_state == state_expand_pieces; }
|
||||
|
||||
void mark_failed(int index);
|
||||
|
||||
std::string const& error() const { return m_storage->error(); }
|
||||
void clear_error() { m_storage->clear_error(); }
|
||||
|
||||
unsigned long piece_crc(
|
||||
int slot_index
|
||||
, int block_size
|
||||
|
@ -276,17 +289,20 @@ namespace libtorrent
|
|||
, int offset
|
||||
, int size);
|
||||
|
||||
void write_impl(
|
||||
size_type write_impl(
|
||||
const char* buf
|
||||
, int piece_index
|
||||
, int offset
|
||||
, int size);
|
||||
|
||||
bool check_one_piece(std::vector<bool>& pieces, int& num_pieces
|
||||
, boost::recursive_mutex& mutex);
|
||||
|
||||
void switch_to_full_mode();
|
||||
sha1_hash hash_for_piece_impl(int piece);
|
||||
|
||||
void release_files_impl() { m_storage->release_files(); }
|
||||
void delete_files_impl() { m_storage->delete_files(); }
|
||||
int release_files_impl() { return m_storage->release_files(); }
|
||||
int delete_files_impl() { return m_storage->delete_files(); }
|
||||
|
||||
bool move_storage_impl(fs::path const& save_path);
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ namespace libtorrent
|
|||
|
||||
void set_sequential_download(bool sd);
|
||||
|
||||
bool verify_resume_data(entry& rd, std::string& error)
|
||||
bool verify_resume_data(entry const& rd, std::string& error)
|
||||
{ TORRENT_ASSERT(m_storage); return m_storage->verify_resume_data(rd, error); }
|
||||
|
||||
void second_tick(stat& accumulator, float tick_interval);
|
||||
|
@ -170,7 +170,7 @@ namespace libtorrent
|
|||
std::string name() const;
|
||||
|
||||
bool check_fastresume(aux::piece_checker_data&);
|
||||
std::pair<bool, float> check_files();
|
||||
std::pair<bool, float> check_files(bool& error);
|
||||
void files_checked(std::vector<piece_picker::downloading_piece> const&
|
||||
unfinished_pieces);
|
||||
|
||||
|
|
|
@ -60,6 +60,8 @@ for k in keys:
|
|||
except:
|
||||
print l
|
||||
|
||||
out.close()
|
||||
peak_out.close()
|
||||
|
||||
out = open('send_buffer.gnuplot', 'wb')
|
||||
print >>out, "set term png size 1200,700"
|
||||
|
@ -79,5 +81,5 @@ for k in keys:
|
|||
print >>out, 'x=0'
|
||||
out.close()
|
||||
|
||||
os.system('gnuplot send_buffer.gnuplot');
|
||||
os.system('gnuplot send_buffer.gnuplot')
|
||||
|
||||
|
|
|
@ -461,16 +461,27 @@ namespace libtorrent
|
|||
int ret = 0;
|
||||
|
||||
bool free_current_buffer = true;
|
||||
try
|
||||
{
|
||||
TORRENT_ASSERT(j.storage);
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
ptime start = time_now();
|
||||
#endif
|
||||
// std::cerr << "DISK THREAD: executing job: " << j.action << std::endl;
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
std::string const& error_string = j.storage->error();
|
||||
if (!error_string.empty())
|
||||
{
|
||||
std::cout << "ERROR: " << error_string << std::endl;
|
||||
j.str = error_string;
|
||||
j.storage->clear_error();
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (j.action)
|
||||
{
|
||||
case disk_io_job::read:
|
||||
{
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
m_log << log_time() << " read " << j.buffer_size << std::endl;
|
||||
#endif
|
||||
|
@ -488,7 +499,13 @@ namespace libtorrent
|
|||
}
|
||||
ret = j.storage->read_impl(j.buffer, j.piece, j.offset
|
||||
, j.buffer_size);
|
||||
if (ret < 0)
|
||||
{
|
||||
j.str = j.storage->error();
|
||||
j.storage->clear_error();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case disk_io_job::write:
|
||||
{
|
||||
mutex_t::scoped_lock l(m_mutex);
|
||||
|
@ -527,17 +544,33 @@ namespace libtorrent
|
|||
if (i != m_pieces.end()) flush_and_remove(i, l);
|
||||
l.unlock();
|
||||
sha1_hash h = j.storage->hash_for_piece_impl(j.piece);
|
||||
std::string const& e = j.storage->error();
|
||||
if (!e.empty())
|
||||
{
|
||||
j.str = e;
|
||||
ret = -1;
|
||||
j.storage->clear_error();
|
||||
break;
|
||||
}
|
||||
j.str.resize(20);
|
||||
std::memcpy(&j.str[0], &h[0], 20);
|
||||
break;
|
||||
}
|
||||
case disk_io_job::move_storage:
|
||||
{
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
m_log << log_time() << " move" << std::endl;
|
||||
#endif
|
||||
ret = j.storage->move_storage_impl(j.str) ? 1 : 0;
|
||||
if (ret != 0)
|
||||
{
|
||||
j.str = j.storage->error();
|
||||
j.storage->clear_error();
|
||||
break;
|
||||
}
|
||||
j.str = j.storage->save_path().string();
|
||||
break;
|
||||
}
|
||||
case disk_io_job::release_files:
|
||||
{
|
||||
#ifdef TORRENT_DISK_STATS
|
||||
|
@ -553,7 +586,12 @@ namespace libtorrent
|
|||
m_pieces.erase(i, m_pieces.end());
|
||||
m_pool.release_memory();
|
||||
l.unlock();
|
||||
j.storage->release_files_impl();
|
||||
ret = j.storage->release_files_impl();
|
||||
if (ret != 0)
|
||||
{
|
||||
j.str = j.storage->error();
|
||||
j.storage->clear_error();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case disk_io_job::delete_files:
|
||||
|
@ -580,26 +618,38 @@ namespace libtorrent
|
|||
m_pieces.erase(i, m_pieces.end());
|
||||
m_pool.release_memory();
|
||||
l.unlock();
|
||||
j.storage->delete_files_impl();
|
||||
ret = j.storage->delete_files_impl();
|
||||
if (ret != 0)
|
||||
{
|
||||
j.str = j.storage->error();
|
||||
j.storage->clear_error();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
} catch (std::exception& e)
|
||||
{
|
||||
// std::cerr << "DISK THREAD: exception: " << e.what() << std::endl;
|
||||
ret = -1;
|
||||
try
|
||||
{
|
||||
j.str = e.what();
|
||||
}
|
||||
catch (std::exception&) {}
|
||||
ret = -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
// if (!handler) std::cerr << "DISK THREAD: no callback specified" << std::endl;
|
||||
// else std::cerr << "DISK THREAD: invoking callback" << std::endl;
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
if (handler) m_ios.post(bind(handler, ret, j));
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
} catch (std::exception&) {}
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
m_current.storage = 0;
|
||||
|
@ -608,6 +658,7 @@ namespace libtorrent
|
|||
|
||||
if (j.buffer && free_current_buffer) free_buffer(j.buffer);
|
||||
}
|
||||
TORRENT_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
37
src/file.cpp
37
src/file.cpp
|
@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||
|
||||
#include "libtorrent/pch.hpp"
|
||||
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#ifdef _WIN32
|
||||
// windows part
|
||||
#include "libtorrent/utf8.hpp"
|
||||
|
@ -186,7 +187,8 @@ namespace libtorrent
|
|||
std::stringstream msg;
|
||||
msg << "open failed: '" << path.native_file_string() << "'. "
|
||||
<< std::strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
}
|
||||
m_open_mode = mode;
|
||||
}
|
||||
|
@ -218,7 +220,8 @@ namespace libtorrent
|
|||
{
|
||||
std::stringstream msg;
|
||||
msg << "read failed: " << std::strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -242,12 +245,13 @@ namespace libtorrent
|
|||
{
|
||||
std::stringstream msg;
|
||||
msg << "write failed: " << std::strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void set_size(size_type s)
|
||||
bool set_size(size_type s)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#error file.cpp is for posix systems only. use file_win.cpp on windows
|
||||
|
@ -256,8 +260,11 @@ namespace libtorrent
|
|||
{
|
||||
std::stringstream msg;
|
||||
msg << "ftruncate failed: '" << std::strerror(errno);
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -283,7 +290,9 @@ namespace libtorrent
|
|||
<< "' fd: " << m_fd
|
||||
<< " offset: " << offset
|
||||
<< " seekdir: " << seekdir;
|
||||
throw file_error(msg.str());
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
*m_error = msg.str();
|
||||
return -1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -300,8 +309,15 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
std::string const& error() const
|
||||
{
|
||||
if (!m_error) m_error.reset(new std::string);
|
||||
return *m_error;
|
||||
}
|
||||
|
||||
int m_fd;
|
||||
int m_open_mode;
|
||||
mutable boost::scoped_ptr<std::string> m_error;
|
||||
};
|
||||
|
||||
// pimpl forwardings
|
||||
|
@ -334,9 +350,9 @@ namespace libtorrent
|
|||
return m_impl->read(buf, num_bytes);
|
||||
}
|
||||
|
||||
void file::set_size(size_type s)
|
||||
bool file::set_size(size_type s)
|
||||
{
|
||||
m_impl->set_size(s);
|
||||
return m_impl->set_size(s);
|
||||
}
|
||||
|
||||
size_type file::seek(size_type pos, file::seek_mode m)
|
||||
|
@ -349,4 +365,9 @@ namespace libtorrent
|
|||
return m_impl->tell();
|
||||
}
|
||||
|
||||
std::string const& file::error() const
|
||||
{
|
||||
return m_impl->error();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -57,10 +57,14 @@ namespace libtorrent
|
|||
|
||||
if (e.key != st)
|
||||
{
|
||||
#ifdef BOOST_NO_EXCEPTIONS
|
||||
return boost::shared_ptr<file>();
|
||||
#else
|
||||
// this means that another instance of the storage
|
||||
// is using the exact same file.
|
||||
throw file_error("torrent uses the same file as another torrent "
|
||||
"(" + p.string() + ")");
|
||||
#endif
|
||||
}
|
||||
|
||||
e.key = st;
|
||||
|
|
|
@ -254,7 +254,7 @@ namespace libtorrent
|
|||
, m_save_path(save_path)
|
||||
{}
|
||||
|
||||
void initialize(bool allocate_files) {}
|
||||
bool initialize(bool allocate_files) { return false; }
|
||||
|
||||
size_type read(char* buf, int slot, int offset, int size)
|
||||
{
|
||||
|
@ -292,7 +292,12 @@ namespace libtorrent
|
|||
, file_iter->size + file_iter->file_base);
|
||||
|
||||
if (!view.valid())
|
||||
throw file_error("failed to open file for reading");
|
||||
{
|
||||
m_error = "failed to open file '";
|
||||
m_error += (m_save_path / file_iter->path).string();
|
||||
m_error += "'for reading";
|
||||
return -1;
|
||||
}
|
||||
|
||||
int left_to_read = size;
|
||||
int slot_size = static_cast<int>(m_info->piece_size(slot));
|
||||
|
@ -347,14 +352,20 @@ namespace libtorrent
|
|||
view = m_pool.open_file(path, std::ios::in, file_offset + file_iter->file_base
|
||||
, left_to_read, this
|
||||
, file_iter->size + file_iter->file_base);
|
||||
|
||||
if (!view.valid())
|
||||
throw file_error("failed to open file for reading");
|
||||
{
|
||||
m_error = "failed to open file '";
|
||||
m_error += (m_save_path / file_iter->path).string();
|
||||
m_error += "'for reading";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void write(const char* buf, int slot, int offset, int size)
|
||||
size_type write(const char* buf, int slot, int offset, int size)
|
||||
{
|
||||
TORRENT_ASSERT(buf != 0);
|
||||
TORRENT_ASSERT(slot >= 0 && slot < m_info->num_pieces());
|
||||
|
@ -390,7 +401,12 @@ namespace libtorrent
|
|||
, file_iter->size + file_iter->file_base);
|
||||
|
||||
if (!view.valid())
|
||||
throw file_error("failed to open file for writing");
|
||||
{
|
||||
m_error = "failed to open file '";
|
||||
m_error += (m_save_path / file_iter->path).string();
|
||||
m_error += "'for writing";
|
||||
return -1;
|
||||
}
|
||||
|
||||
int left_to_write = size;
|
||||
int slot_size = static_cast<int>(m_info->piece_size(slot));
|
||||
|
@ -442,11 +458,18 @@ namespace libtorrent
|
|||
view = m_pool.open_file(path, std::ios::in | std::ios::out
|
||||
, file_offset + file_iter->file_base, left_to_write, this
|
||||
, file_iter->size + file_iter->file_base);
|
||||
|
||||
if (!view.valid())
|
||||
throw file_error("failed to open file for reading");
|
||||
{
|
||||
m_error = "failed to open file '";
|
||||
m_error += (m_save_path / file_iter->path).string();
|
||||
m_error += "'for reading";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool move_storage(fs::path save_path)
|
||||
{
|
||||
|
@ -509,17 +532,34 @@ namespace libtorrent
|
|||
return false;
|
||||
}
|
||||
|
||||
bool verify_resume_data(entry& rd, std::string& error)
|
||||
bool verify_resume_data(entry const& rd, std::string& error)
|
||||
{
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes;
|
||||
entry::list_type& l = rd["file sizes"].list();
|
||||
if (rd.type() != entry::dictionary_t)
|
||||
{
|
||||
error = "invalid fastresume file";
|
||||
return true;
|
||||
}
|
||||
|
||||
for (entry::list_type::iterator i = l.begin();
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes;
|
||||
entry const* file_sizes_ent = rd.find_key("file sizes");
|
||||
if (file_sizes_ent == 0 || file_sizes_ent->type() != entry::list_t)
|
||||
{
|
||||
error = "missing or invalid 'file sizes' entry in resume data";
|
||||
return false;
|
||||
}
|
||||
|
||||
entry::list_type const& l = file_sizes_ent->list();
|
||||
|
||||
for (entry::list_type::const_iterator i = l.begin();
|
||||
i != l.end(); ++i)
|
||||
{
|
||||
if (i->type() != entry::list_t) break;
|
||||
entry::list_type const& pair = i->list();
|
||||
if (pair.size() != 2 || pair.front().type() != entry::int_t
|
||||
|| pair.back().type() != entry::int_t)
|
||||
break;
|
||||
file_sizes.push_back(std::pair<size_type, std::time_t>(
|
||||
i->list().front().integer()
|
||||
, i->list().back().integer()));
|
||||
pair.front().integer(), pair.back().integer()));
|
||||
}
|
||||
|
||||
if (file_sizes.empty())
|
||||
|
@ -528,7 +568,14 @@ namespace libtorrent
|
|||
return false;
|
||||
}
|
||||
|
||||
entry::list_type& slots = rd["slots"].list();
|
||||
entry const* slots_ent = rd.find_key("slots");
|
||||
if (slots_ent == 0 || slots_ent->type() != entry::list_t)
|
||||
{
|
||||
error = "missing or invalid 'slots' entry in resume data";
|
||||
return false;
|
||||
}
|
||||
|
||||
entry::list_type const& slots = slots_ent->list();
|
||||
bool seed = int(slots.size()) == m_info->num_pieces()
|
||||
&& std::find_if(slots.begin(), slots.end()
|
||||
, boost::bind<bool>(std::less<int>()
|
||||
|
@ -536,11 +583,9 @@ namespace libtorrent
|
|||
&entry::integer, _1), 0)) == slots.end();
|
||||
|
||||
bool full_allocation_mode = false;
|
||||
try
|
||||
{
|
||||
full_allocation_mode = rd["allocation"].string() == "full";
|
||||
}
|
||||
catch (std::exception&) {}
|
||||
entry const* allocation_mode = rd.find_key("allocation");
|
||||
if (allocation_mode && allocation_mode->type() == entry::string_t)
|
||||
full_allocation_mode = allocation_mode->string() == "full";
|
||||
|
||||
if (seed)
|
||||
{
|
||||
|
@ -574,12 +619,16 @@ namespace libtorrent
|
|||
, !full_allocation_mode, &error);
|
||||
}
|
||||
|
||||
void write_resume_data(entry& rd) const
|
||||
bool write_resume_data(entry& rd) const
|
||||
{
|
||||
if (rd.type() != entry::dictionary_t)
|
||||
{
|
||||
m_error = "invalid fastresume file";
|
||||
return true;
|
||||
}
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes
|
||||
= get_filesizes(*m_info, m_save_path);
|
||||
|
||||
rd["file sizes"] = entry::list_type();
|
||||
entry::list_type& fl = rd["file sizes"].list();
|
||||
for (std::vector<std::pair<size_type, std::time_t> >::iterator i
|
||||
= file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
|
||||
|
@ -589,18 +638,20 @@ namespace libtorrent
|
|||
p.push_back(entry(i->second));
|
||||
fl.push_back(entry(p));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void move_slot(int src_slot, int dst_slot)
|
||||
bool move_slot(int src_slot, int dst_slot)
|
||||
{
|
||||
// TODO: this can be optimized by mapping both slots and do a straight memcpy
|
||||
int piece_size = m_info->piece_size(dst_slot);
|
||||
m_scratch_buffer.resize(piece_size);
|
||||
read(&m_scratch_buffer[0], src_slot, 0, piece_size);
|
||||
write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
|
||||
size_type ret1 = read(&m_scratch_buffer[0], src_slot, 0, piece_size);
|
||||
size_type ret2 = write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
|
||||
return ret1 != piece_size || ret2 != piece_size;
|
||||
}
|
||||
|
||||
void swap_slots(int slot1, int slot2)
|
||||
bool swap_slots(int slot1, int slot2)
|
||||
{
|
||||
// TODO: this can be optimized by mapping both slots and do a straight memcpy
|
||||
// the size of the target slot is the size of the piece
|
||||
|
@ -608,13 +659,15 @@ namespace libtorrent
|
|||
int piece1_size = m_info->piece_size(slot2);
|
||||
int piece2_size = m_info->piece_size(slot1);
|
||||
m_scratch_buffer.resize(piece_size * 2);
|
||||
read(&m_scratch_buffer[0], slot1, 0, piece1_size);
|
||||
read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size);
|
||||
write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size);
|
||||
size_type ret1 = read(&m_scratch_buffer[0], slot1, 0, piece1_size);
|
||||
size_type ret2 = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size);
|
||||
size_type ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
size_type ret4 = write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size);
|
||||
return ret1 != piece1_size || ret2 != piece2_size
|
||||
|| ret3 != piece1_size || ret4 != piece2_size;
|
||||
}
|
||||
|
||||
void swap_slots3(int slot1, int slot2, int slot3)
|
||||
bool swap_slots3(int slot1, int slot2, int slot3)
|
||||
{
|
||||
// TODO: this can be optimized by mapping both slots and do a straight memcpy
|
||||
// the size of the target slot is the size of the piece
|
||||
|
@ -623,12 +676,15 @@ namespace libtorrent
|
|||
int piece2_size = m_info->piece_size(slot3);
|
||||
int piece3_size = m_info->piece_size(slot1);
|
||||
m_scratch_buffer.resize(piece_size * 2);
|
||||
read(&m_scratch_buffer[0], slot1, 0, piece1_size);
|
||||
read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size);
|
||||
write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
read(&m_scratch_buffer[0], slot3, 0, piece3_size);
|
||||
write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size);
|
||||
write(&m_scratch_buffer[0], slot1, 0, piece3_size);
|
||||
size_type ret1 = read(&m_scratch_buffer[0], slot1, 0, piece1_size);
|
||||
size_type ret2 = read(&m_scratch_buffer[piece_size], slot2, 0, piece2_size);
|
||||
size_type ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
size_type ret4 = read(&m_scratch_buffer[0], slot3, 0, piece3_size);
|
||||
size_type ret5 = write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size);
|
||||
size_type ret6 = write(&m_scratch_buffer[0], slot1, 0, piece3_size);
|
||||
return ret1 != piece1_size || ret2 != piece2_size
|
||||
|| ret3 != piece1_size || ret4 != piece3_size
|
||||
|| ret5 != piece2_size || ret6 != piece3_size;
|
||||
}
|
||||
|
||||
sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size)
|
||||
|
@ -661,17 +717,19 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
void release_files()
|
||||
bool release_files()
|
||||
{
|
||||
m_pool.release(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
void delete_files()
|
||||
bool delete_files()
|
||||
{
|
||||
// make sure we don't have the files open
|
||||
m_pool.release(this);
|
||||
buffer().swap(m_scratch_buffer);
|
||||
|
||||
int result = 0;
|
||||
std::string error;
|
||||
|
||||
// delete the files from disk
|
||||
|
@ -690,7 +748,10 @@ namespace libtorrent
|
|||
bp = bp.branch_path();
|
||||
}
|
||||
if (std::remove(p.c_str()) != 0 && errno != ENOENT)
|
||||
{
|
||||
error = std::strerror(errno);
|
||||
result = errno;
|
||||
}
|
||||
}
|
||||
|
||||
// remove the directories. Reverse order to delete
|
||||
|
@ -700,12 +761,19 @@ namespace libtorrent
|
|||
, end(directories.rend()); i != end; ++i)
|
||||
{
|
||||
if (std::remove(i->c_str()) != 0 && errno != ENOENT)
|
||||
{
|
||||
error = std::strerror(errno);
|
||||
result = errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error.empty()) throw std::runtime_error(error);
|
||||
if (!error.empty()) m_error.swap(error);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
std::string const& error() const { return m_error; }
|
||||
void clear_error() { m_error.clear(); }
|
||||
|
||||
private:
|
||||
|
||||
boost::intrusive_ptr<torrent_info const> m_info;
|
||||
|
@ -715,6 +783,8 @@ namespace libtorrent
|
|||
buffer m_scratch_buffer;
|
||||
|
||||
static mapped_file_pool m_pool;
|
||||
|
||||
mutable std::string m_error;
|
||||
};
|
||||
|
||||
storage_interface* mapped_storage_constructor(boost::intrusive_ptr<torrent_info const> ti
|
||||
|
|
|
@ -134,8 +134,6 @@ namespace detail
|
|||
for (;;)
|
||||
{
|
||||
// temporary torrent used while checking fastresume data
|
||||
try
|
||||
{
|
||||
t.reset();
|
||||
{
|
||||
boost::mutex::scoped_lock l(m_mutex);
|
||||
|
@ -290,46 +288,32 @@ namespace detail
|
|||
t.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
if (!processing) continue;
|
||||
|
||||
TORRENT_ASSERT(processing);
|
||||
|
||||
bool finished = false;
|
||||
bool error = false;
|
||||
float progress = 0.f;
|
||||
boost::tie(finished, progress) = processing->torrent_ptr->check_files(error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
// This will happen if the storage fails to initialize
|
||||
// for example if one of the files has an invalid filename.
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
mutex::scoped_lock l2(m_mutex);
|
||||
|
||||
if (m_ses.m_alerts.should_post(alert::fatal))
|
||||
if (!m_processing.empty()
|
||||
&& m_processing.front() == processing)
|
||||
m_processing.pop_front();
|
||||
processing.reset();
|
||||
if (!m_processing.empty())
|
||||
{
|
||||
m_ses.m_alerts.post_alert(
|
||||
file_error_alert(
|
||||
t->torrent_ptr->get_handle()
|
||||
, e.what()));
|
||||
processing = m_processing.front();
|
||||
processing->processing = true;
|
||||
}
|
||||
t->torrent_ptr->abort();
|
||||
|
||||
TORRENT_ASSERT(!m_torrents.empty());
|
||||
m_torrents.pop_front();
|
||||
continue;
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::cerr << "error while checking resume data\n";
|
||||
#endif
|
||||
mutex::scoped_lock l(m_mutex);
|
||||
TORRENT_ASSERT(!m_torrents.empty());
|
||||
m_torrents.pop_front();
|
||||
TORRENT_ASSERT(false);
|
||||
}
|
||||
|
||||
if (!processing) continue;
|
||||
|
||||
try
|
||||
{
|
||||
TORRENT_ASSERT(processing);
|
||||
|
||||
float finished = false;
|
||||
float progress = 0.f;
|
||||
boost::tie(finished, progress) = processing->torrent_ptr->check_files();
|
||||
|
||||
{
|
||||
mutex::scoped_lock l2(m_mutex);
|
||||
|
@ -421,51 +405,6 @@ namespace detail
|
|||
}
|
||||
}
|
||||
}
|
||||
catch(std::exception const& e)
|
||||
{
|
||||
// This will happen if the storage fails to initialize
|
||||
session_impl::mutex_t::scoped_lock l(m_ses.m_mutex);
|
||||
mutex::scoped_lock l2(m_mutex);
|
||||
|
||||
if (m_ses.m_alerts.should_post(alert::fatal))
|
||||
{
|
||||
m_ses.m_alerts.post_alert(
|
||||
file_error_alert(
|
||||
processing->torrent_ptr->get_handle()
|
||||
, e.what()));
|
||||
}
|
||||
|
||||
processing->torrent_ptr->abort();
|
||||
|
||||
if (!m_processing.empty()
|
||||
&& m_processing.front() == processing)
|
||||
m_processing.pop_front();
|
||||
processing.reset();
|
||||
if (!m_processing.empty())
|
||||
{
|
||||
processing = m_processing.front();
|
||||
processing->processing = true;
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
std::cerr << "error while checking files\n";
|
||||
#endif
|
||||
mutex::scoped_lock l(m_mutex);
|
||||
TORRENT_ASSERT(!m_processing.empty());
|
||||
|
||||
processing.reset();
|
||||
m_processing.pop_front();
|
||||
if (!m_processing.empty())
|
||||
{
|
||||
processing = m_processing.front();
|
||||
processing->processing = true;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aux::piece_checker_data* checker_impl::find_torrent(sha1_hash const& info_hash)
|
||||
|
@ -2455,6 +2394,7 @@ namespace detail
|
|||
|
||||
boost::mutex::scoped_lock l(m_send_buffer_mutex);
|
||||
#ifdef TORRENT_STATS
|
||||
TORRENT_ASSERT(m_buffer_allocations >= 0);
|
||||
m_buffer_allocations += num_buffers;
|
||||
m_buffer_usage_logger << log_time() << " protocol_buffer: "
|
||||
<< (m_buffer_allocations * send_buffer_size) << std::endl;
|
||||
|
|
460
src/storage.cpp
460
src/storage.cpp
|
@ -361,19 +361,22 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(m_save_path.is_complete());
|
||||
}
|
||||
|
||||
void release_files();
|
||||
void delete_files();
|
||||
void initialize(bool allocate_files);
|
||||
bool release_files();
|
||||
bool delete_files();
|
||||
bool initialize(bool allocate_files);
|
||||
bool move_storage(fs::path save_path);
|
||||
size_type read(char* buf, int slot, int offset, int size);
|
||||
void write(const char* buf, int slot, int offset, int size);
|
||||
void move_slot(int src_slot, int dst_slot);
|
||||
void swap_slots(int slot1, int slot2);
|
||||
void swap_slots3(int slot1, int slot2, int slot3);
|
||||
bool verify_resume_data(entry& rd, std::string& error);
|
||||
void write_resume_data(entry& rd) const;
|
||||
size_type write(const char* buf, int slot, int offset, int size);
|
||||
bool move_slot(int src_slot, int dst_slot);
|
||||
bool swap_slots(int slot1, int slot2);
|
||||
bool swap_slots3(int slot1, int slot2, int slot3);
|
||||
bool verify_resume_data(entry const& rd, std::string& error);
|
||||
bool write_resume_data(entry& rd) const;
|
||||
sha1_hash hash_for_slot(int slot, partial_hash& ph, int piece_size);
|
||||
|
||||
std::string const& error() const { return m_error; }
|
||||
void clear_error() { m_error.clear(); }
|
||||
|
||||
size_type read_impl(char* buf, int slot, int offset, int size, bool fill_zero);
|
||||
|
||||
~storage()
|
||||
|
@ -388,6 +391,8 @@ namespace libtorrent
|
|||
|
||||
// temporary storage for moving pieces
|
||||
buffer m_scratch_buffer;
|
||||
|
||||
mutable std::string m_error;
|
||||
};
|
||||
|
||||
sha1_hash storage::hash_for_slot(int slot, partial_hash& ph, int piece_size)
|
||||
|
@ -420,7 +425,7 @@ namespace libtorrent
|
|||
#endif
|
||||
}
|
||||
|
||||
void storage::initialize(bool allocate_files)
|
||||
bool storage::initialize(bool allocate_files)
|
||||
{
|
||||
// first, create all missing directories
|
||||
fs::path last_path;
|
||||
|
@ -467,8 +472,10 @@ namespace libtorrent
|
|||
#endif
|
||||
if (allocate_files)
|
||||
{
|
||||
m_files.open_file(this, m_save_path / file_iter->path, file::in | file::out)
|
||||
->set_size(file_iter->size);
|
||||
boost::shared_ptr<file> f = m_files.open_file(this
|
||||
, m_save_path / file_iter->path, file::in | file::out);
|
||||
if (f && f->error().empty())
|
||||
f->set_size(file_iter->size);
|
||||
}
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
} catch (std::exception&) {}
|
||||
|
@ -476,20 +483,23 @@ namespace libtorrent
|
|||
}
|
||||
// close files that were opened in write mode
|
||||
m_files.release(this);
|
||||
return false;
|
||||
}
|
||||
|
||||
void storage::release_files()
|
||||
bool storage::release_files()
|
||||
{
|
||||
m_files.release(this);
|
||||
buffer().swap(m_scratch_buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
void storage::delete_files()
|
||||
bool storage::delete_files()
|
||||
{
|
||||
// make sure we don't have the files open
|
||||
m_files.release(this);
|
||||
buffer().swap(m_scratch_buffer);
|
||||
|
||||
int result = 0;
|
||||
std::string error;
|
||||
|
||||
// delete the files from disk
|
||||
|
@ -508,7 +518,10 @@ namespace libtorrent
|
|||
bp = bp.branch_path();
|
||||
}
|
||||
if (std::remove(p.c_str()) != 0 && errno != ENOENT)
|
||||
{
|
||||
error = std::strerror(errno);
|
||||
result = errno;
|
||||
}
|
||||
}
|
||||
|
||||
// remove the directories. Reverse order to delete
|
||||
|
@ -518,18 +531,26 @@ namespace libtorrent
|
|||
, end(directories.rend()); i != end; ++i)
|
||||
{
|
||||
if (std::remove(i->c_str()) != 0 && errno != ENOENT)
|
||||
error = std::strerror(errno);
|
||||
}
|
||||
|
||||
if (!error.empty()) throw std::runtime_error(error);
|
||||
}
|
||||
|
||||
void storage::write_resume_data(entry& rd) const
|
||||
{
|
||||
error = std::strerror(errno);
|
||||
result = errno;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error.empty()) m_error.swap(error);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
bool storage::write_resume_data(entry& rd) const
|
||||
{
|
||||
if (rd.type() != entry::dictionary_t)
|
||||
{
|
||||
m_error = "invalid fastresume file";
|
||||
return true;
|
||||
}
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes
|
||||
= get_filesizes(*m_info, m_save_path);
|
||||
|
||||
rd["file sizes"] = entry::list_type();
|
||||
entry::list_type& fl = rd["file sizes"].list();
|
||||
for (std::vector<std::pair<size_type, std::time_t> >::iterator i
|
||||
= file_sizes.begin(), end(file_sizes.end()); i != end; ++i)
|
||||
|
@ -539,19 +560,37 @@ namespace libtorrent
|
|||
p.push_back(entry(i->second));
|
||||
fl.push_back(entry(p));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool storage::verify_resume_data(entry& rd, std::string& error)
|
||||
bool storage::verify_resume_data(entry const& rd, std::string& error)
|
||||
{
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes;
|
||||
entry::list_type& l = rd["file sizes"].list();
|
||||
if (rd.type() != entry::dictionary_t)
|
||||
{
|
||||
error = "invalid fastresume file";
|
||||
return true;
|
||||
}
|
||||
|
||||
for (entry::list_type::iterator i = l.begin();
|
||||
std::vector<std::pair<size_type, std::time_t> > file_sizes;
|
||||
entry const* file_sizes_ent = rd.find_key("file sizes");
|
||||
if (file_sizes_ent == 0 || file_sizes_ent->type() != entry::list_t)
|
||||
{
|
||||
error = "missing or invalid 'file sizes' entry in resume data";
|
||||
return false;
|
||||
}
|
||||
|
||||
entry::list_type const& l = file_sizes_ent->list();
|
||||
|
||||
for (entry::list_type::const_iterator i = l.begin();
|
||||
i != l.end(); ++i)
|
||||
{
|
||||
if (i->type() != entry::list_t) break;
|
||||
entry::list_type const& pair = i->list();
|
||||
if (pair.size() != 2 || pair.front().type() != entry::int_t
|
||||
|| pair.back().type() != entry::int_t)
|
||||
break;
|
||||
file_sizes.push_back(std::pair<size_type, std::time_t>(
|
||||
i->list().front().integer()
|
||||
, i->list().back().integer()));
|
||||
pair.front().integer(), pair.back().integer()));
|
||||
}
|
||||
|
||||
if (file_sizes.empty())
|
||||
|
@ -560,7 +599,14 @@ namespace libtorrent
|
|||
return false;
|
||||
}
|
||||
|
||||
entry::list_type& slots = rd["slots"].list();
|
||||
entry const* slots_ent = rd.find_key("slots");
|
||||
if (slots_ent == 0 || slots_ent->type() != entry::list_t)
|
||||
{
|
||||
error = "missing or invalid 'slots' entry in resume data";
|
||||
return false;
|
||||
}
|
||||
|
||||
entry::list_type const& slots = slots_ent->list();
|
||||
bool seed = int(slots.size()) == m_info->num_pieces()
|
||||
&& std::find_if(slots.begin(), slots.end()
|
||||
, boost::bind<bool>(std::less<int>()
|
||||
|
@ -568,11 +614,9 @@ namespace libtorrent
|
|||
&entry::integer, _1), 0)) == slots.end();
|
||||
|
||||
bool full_allocation_mode = false;
|
||||
try
|
||||
{
|
||||
full_allocation_mode = rd["allocation"].string() == "full";
|
||||
}
|
||||
catch (std::exception&) {}
|
||||
entry const* allocation_mode = rd.find_key("allocation");
|
||||
if (allocation_mode && allocation_mode->type() == entry::string_t)
|
||||
full_allocation_mode = allocation_mode->string() == "full";
|
||||
|
||||
if (seed)
|
||||
{
|
||||
|
@ -648,8 +692,10 @@ namespace libtorrent
|
|||
new_path = save_path / m_info->name();
|
||||
#endif
|
||||
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try
|
||||
{
|
||||
#endif
|
||||
#if defined(_WIN32) && defined(UNICODE) && BOOST_VERSION < 103400
|
||||
rename_win(old_path, new_path);
|
||||
rename(old_path, new_path);
|
||||
|
@ -658,9 +704,11 @@ namespace libtorrent
|
|||
#endif
|
||||
m_save_path = save_path;
|
||||
return true;
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
}
|
||||
catch (std::exception&) {}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
@ -693,28 +741,31 @@ namespace libtorrent
|
|||
*/
|
||||
#endif
|
||||
|
||||
void storage::move_slot(int src_slot, int dst_slot)
|
||||
bool storage::move_slot(int src_slot, int dst_slot)
|
||||
{
|
||||
int piece_size = m_info->piece_size(dst_slot);
|
||||
m_scratch_buffer.resize(piece_size);
|
||||
read_impl(&m_scratch_buffer[0], src_slot, 0, piece_size, true);
|
||||
write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
|
||||
size_type ret1 = read_impl(&m_scratch_buffer[0], src_slot, 0, piece_size, true);
|
||||
size_type ret2 = write(&m_scratch_buffer[0], dst_slot, 0, piece_size);
|
||||
return ret1 != piece_size || ret2 != piece_size;
|
||||
}
|
||||
|
||||
void storage::swap_slots(int slot1, int slot2)
|
||||
bool storage::swap_slots(int slot1, int slot2)
|
||||
{
|
||||
// the size of the target slot is the size of the piece
|
||||
int piece_size = m_info->piece_length();
|
||||
int piece1_size = m_info->piece_size(slot2);
|
||||
int piece2_size = m_info->piece_size(slot1);
|
||||
m_scratch_buffer.resize(piece_size * 2);
|
||||
read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
|
||||
read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
|
||||
write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size);
|
||||
size_type ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
|
||||
size_type ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
|
||||
size_type ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
size_type ret4 = write(&m_scratch_buffer[piece_size], slot1, 0, piece2_size);
|
||||
return ret1 != piece1_size || ret2 != piece2_size
|
||||
|| ret3 != piece1_size || ret4 != piece2_size;
|
||||
}
|
||||
|
||||
void storage::swap_slots3(int slot1, int slot2, int slot3)
|
||||
bool storage::swap_slots3(int slot1, int slot2, int slot3)
|
||||
{
|
||||
// the size of the target slot is the size of the piece
|
||||
int piece_size = m_info->piece_length();
|
||||
|
@ -722,12 +773,15 @@ namespace libtorrent
|
|||
int piece2_size = m_info->piece_size(slot3);
|
||||
int piece3_size = m_info->piece_size(slot1);
|
||||
m_scratch_buffer.resize(piece_size * 2);
|
||||
read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
|
||||
read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
|
||||
write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
read_impl(&m_scratch_buffer[0], slot3, 0, piece3_size, true);
|
||||
write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size);
|
||||
write(&m_scratch_buffer[0], slot1, 0, piece3_size);
|
||||
size_type ret1 = read_impl(&m_scratch_buffer[0], slot1, 0, piece1_size, true);
|
||||
size_type ret2 = read_impl(&m_scratch_buffer[piece_size], slot2, 0, piece2_size, true);
|
||||
size_type ret3 = write(&m_scratch_buffer[0], slot2, 0, piece1_size);
|
||||
size_type ret4 = read_impl(&m_scratch_buffer[0], slot3, 0, piece3_size, true);
|
||||
size_type ret5 = write(&m_scratch_buffer[piece_size], slot3, 0, piece2_size);
|
||||
size_type ret6 = write(&m_scratch_buffer[0], slot1, 0, piece3_size);
|
||||
return ret1 != piece1_size || ret2 != piece2_size
|
||||
|| ret3 != piece1_size || ret4 != piece3_size
|
||||
|| ret5 != piece2_size || ret6 != piece3_size;
|
||||
}
|
||||
|
||||
size_type storage::read(
|
||||
|
@ -777,6 +831,16 @@ namespace libtorrent
|
|||
int buf_pos = 0;
|
||||
boost::shared_ptr<file> in(m_files.open_file(
|
||||
this, m_save_path / file_iter->path, file::in));
|
||||
if (!in)
|
||||
{
|
||||
m_error = "failed to open file " + (m_save_path / file_iter->path).string();
|
||||
return -1;
|
||||
}
|
||||
if (!in->error().empty())
|
||||
{
|
||||
m_error = in->error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(file_offset < file_iter->size);
|
||||
|
||||
|
@ -787,7 +851,10 @@ namespace libtorrent
|
|||
{
|
||||
// the file was not big enough
|
||||
if (!fill_zero)
|
||||
throw file_error("slot has no storage");
|
||||
{
|
||||
m_error = "slot has no storage";
|
||||
return -1;
|
||||
}
|
||||
std::memset(buf + buf_pos, 0, size - buf_pos);
|
||||
return size;
|
||||
}
|
||||
|
@ -834,7 +901,11 @@ namespace libtorrent
|
|||
// the file was not big enough
|
||||
if (actual_read > 0) buf_pos += actual_read;
|
||||
if (!fill_zero)
|
||||
throw file_error("slot has no storage");
|
||||
{
|
||||
m_error = "failed to read file: "
|
||||
+ (m_save_path / file_iter->path).string();
|
||||
return -1;
|
||||
}
|
||||
std::memset(buf + buf_pos, 0, size - buf_pos);
|
||||
return size;
|
||||
}
|
||||
|
@ -858,6 +929,16 @@ namespace libtorrent
|
|||
file_offset = 0;
|
||||
in = m_files.open_file(
|
||||
this, path, file::in);
|
||||
if (!in)
|
||||
{
|
||||
m_error = "failed to open file " + (m_save_path / file_iter->path).string();
|
||||
return -1;
|
||||
}
|
||||
if (!in->error().empty())
|
||||
{
|
||||
m_error = in->error();
|
||||
return -1;
|
||||
}
|
||||
in->seek(file_iter->file_base);
|
||||
}
|
||||
}
|
||||
|
@ -865,7 +946,7 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
// throws file_error if it fails to write
|
||||
void storage::write(
|
||||
size_type storage::write(
|
||||
const char* buf
|
||||
, int slot
|
||||
, int offset
|
||||
|
@ -902,6 +983,16 @@ namespace libtorrent
|
|||
fs::path p(m_save_path / file_iter->path);
|
||||
boost::shared_ptr<file> out = m_files.open_file(
|
||||
this, p, file::out | file::in);
|
||||
if (!out)
|
||||
{
|
||||
m_error = "failed to open file " + (m_save_path / file_iter->path).string();
|
||||
return -1;
|
||||
}
|
||||
if (!out->error().empty())
|
||||
{
|
||||
m_error = out->error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(file_offset < file_iter->size);
|
||||
TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base);
|
||||
|
@ -910,9 +1001,8 @@ namespace libtorrent
|
|||
|
||||
if (pos != file_offset + file_iter->file_base)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "no storage for slot " << slot;
|
||||
throw file_error(s.str());
|
||||
m_error = "failed to seek " + (m_save_path / file_iter->path).string();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int left_to_write = size;
|
||||
|
@ -949,9 +1039,8 @@ namespace libtorrent
|
|||
|
||||
if (written != write_bytes)
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "no storage for slot " << slot;
|
||||
throw file_error(s.str());
|
||||
m_error = "failed to write " + (m_save_path / file_iter->path).string();
|
||||
return -1;
|
||||
}
|
||||
|
||||
left_to_write -= write_bytes;
|
||||
|
@ -973,10 +1062,21 @@ namespace libtorrent
|
|||
file_offset = 0;
|
||||
out = m_files.open_file(
|
||||
this, p, file::out | file::in);
|
||||
if (!out)
|
||||
{
|
||||
m_error = "failed to open file " + (m_save_path / file_iter->path).string();
|
||||
return -1;
|
||||
}
|
||||
if (!out->error().empty())
|
||||
{
|
||||
m_error = out->error();
|
||||
return -1;
|
||||
}
|
||||
|
||||
out->seek(file_iter->file_base);
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
storage_interface* default_storage_constructor(boost::intrusive_ptr<torrent_info const> ti
|
||||
|
@ -1012,16 +1112,6 @@ namespace libtorrent
|
|||
{
|
||||
}
|
||||
|
||||
void piece_manager::write_resume_data(entry& rd) const
|
||||
{
|
||||
m_storage->write_resume_data(rd);
|
||||
}
|
||||
|
||||
bool piece_manager::verify_resume_data(entry& rd, std::string& error)
|
||||
{
|
||||
return m_storage->verify_resume_data(rd, error);
|
||||
}
|
||||
|
||||
void piece_manager::free_buffer(char* buf)
|
||||
{
|
||||
m_io_thread.free_buffer(buf);
|
||||
|
@ -1093,7 +1183,10 @@ namespace libtorrent
|
|||
j.offset = r.start;
|
||||
j.buffer_size = r.length;
|
||||
j.buffer = m_io_thread.allocate_buffer();
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
if (j.buffer == 0) throw file_error("out of memory");
|
||||
// TODO: return error code instead of throwing
|
||||
#endif
|
||||
std::memcpy(j.buffer, buffer, j.buffer_size);
|
||||
m_io_thread.add_job(j, handler);
|
||||
}
|
||||
|
@ -1197,7 +1290,6 @@ namespace libtorrent
|
|||
int slot_index
|
||||
, int block_size
|
||||
, piece_picker::block_info const* bi)
|
||||
try
|
||||
{
|
||||
TORRENT_ASSERT(slot_index >= 0);
|
||||
TORRENT_ASSERT(slot_index < m_info->num_pieces());
|
||||
|
@ -1230,10 +1322,6 @@ namespace libtorrent
|
|||
}
|
||||
return crc.final();
|
||||
}
|
||||
catch (std::exception&)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_type piece_manager::read_impl(
|
||||
char* buf
|
||||
|
@ -1248,7 +1336,7 @@ namespace libtorrent
|
|||
return m_storage->read(buf, slot, offset, size);
|
||||
}
|
||||
|
||||
void piece_manager::write_impl(
|
||||
size_type piece_manager::write_impl(
|
||||
const char* buf
|
||||
, int piece_index
|
||||
, int offset
|
||||
|
@ -1282,7 +1370,7 @@ namespace libtorrent
|
|||
}
|
||||
|
||||
int slot = allocate_slot_for_piece(piece_index);
|
||||
m_storage->write(buf, slot, offset, size);
|
||||
return m_storage->write(buf, slot, offset, size);
|
||||
}
|
||||
|
||||
int piece_manager::identify_data(
|
||||
|
@ -1593,7 +1681,8 @@ namespace libtorrent
|
|||
// file check is at. 0 is nothing done, and 1
|
||||
// is finished
|
||||
std::pair<bool, float> piece_manager::check_files(
|
||||
std::vector<bool>& pieces, int& num_pieces, boost::recursive_mutex& mutex)
|
||||
std::vector<bool>& pieces, int& num_pieces, boost::recursive_mutex& mutex
|
||||
, bool& error)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
boost::recursive_mutex::scoped_lock l_(mutex);
|
||||
|
@ -1623,14 +1712,25 @@ namespace libtorrent
|
|||
if (m_scratch_buffer2.empty())
|
||||
m_scratch_buffer2.resize(m_info->piece_length());
|
||||
|
||||
m_storage->read(&m_scratch_buffer2[0], piece, 0, m_info->piece_size(other_piece));
|
||||
int piece_size = m_info->piece_size(other_piece);
|
||||
if (m_storage->read(&m_scratch_buffer2[0], piece, 0, piece_size)
|
||||
!= piece_size)
|
||||
{
|
||||
error = true;
|
||||
return std::make_pair(true, (float)m_current_slot / m_info->num_pieces());
|
||||
}
|
||||
m_scratch_piece = other_piece;
|
||||
m_piece_to_slot[other_piece] = unassigned;
|
||||
}
|
||||
|
||||
// the slot where this piece belongs is
|
||||
// free. Just move the piece there.
|
||||
m_storage->write(&m_scratch_buffer[0], piece, 0, m_info->piece_size(piece));
|
||||
int piece_size = m_info->piece_size(piece);
|
||||
if (m_storage->write(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
|
||||
{
|
||||
error = true;
|
||||
return std::make_pair(true, (float)m_current_slot / m_info->num_pieces());
|
||||
}
|
||||
m_piece_to_slot[piece] = piece;
|
||||
m_slot_to_piece[piece] = piece;
|
||||
|
||||
|
@ -1671,7 +1771,12 @@ namespace libtorrent
|
|||
if (m_scratch_buffer.empty())
|
||||
m_scratch_buffer.resize(m_info->piece_length());
|
||||
|
||||
m_storage->read(&m_scratch_buffer[0], piece, 0, m_info->piece_size(other_piece));
|
||||
int piece_size = m_info->piece_size(other_piece);
|
||||
if (m_storage->read(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size)
|
||||
{
|
||||
error = true;
|
||||
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces());
|
||||
}
|
||||
m_scratch_piece = other_piece;
|
||||
m_piece_to_slot[other_piece] = unassigned;
|
||||
}
|
||||
|
@ -1688,12 +1793,98 @@ namespace libtorrent
|
|||
|
||||
TORRENT_ASSERT(m_state == state_full_check);
|
||||
|
||||
bool skip = check_one_piece(pieces, num_pieces, mutex);
|
||||
|
||||
if (skip)
|
||||
{
|
||||
clear_error();
|
||||
// skip means that the piece we checked failed to be read from disk
|
||||
// completely. We should skip all pieces belonging to that file.
|
||||
// find the file that failed, and skip all the pieces in that file
|
||||
size_type file_offset = 0;
|
||||
size_type current_offset = m_current_slot * m_info->piece_length();
|
||||
for (torrent_info::file_iterator i = m_info->begin_files(true);
|
||||
i != m_info->end_files(true); ++i)
|
||||
{
|
||||
file_offset += i->size;
|
||||
if (file_offset > current_offset) break;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(file_offset > current_offset);
|
||||
int skip_blocks = static_cast<int>(
|
||||
(file_offset - current_offset + m_info->piece_length() - 1)
|
||||
/ m_info->piece_length());
|
||||
|
||||
if (m_storage_mode == storage_mode_compact)
|
||||
{
|
||||
for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i)
|
||||
{
|
||||
TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
|
||||
m_unallocated_slots.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// current slot will increase by one at the end of the for-loop too
|
||||
m_current_slot += skip_blocks - 1;
|
||||
}
|
||||
|
||||
++m_current_slot;
|
||||
|
||||
if (m_current_slot >= m_info->num_pieces())
|
||||
{
|
||||
TORRENT_ASSERT(m_current_slot == m_info->num_pieces());
|
||||
|
||||
// clear the memory we've been using
|
||||
std::vector<char>().swap(m_piece_data);
|
||||
std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
|
||||
|
||||
if (m_storage_mode != storage_mode_compact)
|
||||
{
|
||||
if (!m_out_of_place)
|
||||
{
|
||||
// if no piece is out of place
|
||||
// since we're in full allocation mode, we can
|
||||
// forget the piece allocation tables
|
||||
|
||||
std::vector<int>().swap(m_piece_to_slot);
|
||||
std::vector<int>().swap(m_slot_to_piece);
|
||||
m_state = state_create_files;
|
||||
return std::make_pair(false, 1.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// in this case we're in full allocation mode, but
|
||||
// we're resuming a compact allocated storage
|
||||
m_state = state_expand_pieces;
|
||||
m_current_slot = 0;
|
||||
return std::make_pair(false, 0.f);
|
||||
}
|
||||
}
|
||||
else if (m_unallocated_slots.empty())
|
||||
{
|
||||
switch_to_full_mode();
|
||||
}
|
||||
m_state = state_create_files;
|
||||
|
||||
#ifndef NDEBUG
|
||||
boost::recursive_mutex::scoped_lock l(mutex);
|
||||
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
|
||||
#endif
|
||||
return std::make_pair(false, 1.f);
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
|
||||
|
||||
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces());
|
||||
}
|
||||
|
||||
bool piece_manager::check_one_piece(std::vector<bool>& pieces, int& num_pieces
|
||||
, boost::recursive_mutex& mutex)
|
||||
{
|
||||
// ------------------------
|
||||
// DO THE FULL CHECK
|
||||
// ------------------------
|
||||
|
||||
try
|
||||
{
|
||||
// initialization for the full check
|
||||
if (m_hash_to_piece.empty())
|
||||
{
|
||||
|
@ -1713,7 +1904,7 @@ namespace libtorrent
|
|||
|
||||
// if the file is incomplete, skip the rest of it
|
||||
if (num_read != piece_size)
|
||||
throw file_error("");
|
||||
return true;
|
||||
|
||||
int piece_index = identify_data(m_piece_data, m_current_slot
|
||||
, pieces, num_pieces, m_hash_to_piece, mutex);
|
||||
|
@ -1780,10 +1971,13 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
if (other_piece >= 0)
|
||||
m_storage->swap_slots(other_slot, m_current_slot);
|
||||
ret |= m_storage->swap_slots(other_slot, m_current_slot);
|
||||
else
|
||||
m_storage->move_slot(m_current_slot, other_slot);
|
||||
ret |= m_storage->move_slot(m_current_slot, other_slot);
|
||||
|
||||
if (ret) return true;
|
||||
|
||||
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
|
||||
|| m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
|
||||
|
@ -1805,15 +1999,19 @@ namespace libtorrent
|
|||
&& m_storage_mode == storage_mode_compact)
|
||||
m_free_slots.push_back(other_slot);
|
||||
|
||||
bool ret = false;
|
||||
if (piece_index >= 0)
|
||||
{
|
||||
m_piece_to_slot[piece_index] = other_slot;
|
||||
m_storage->swap_slots(other_slot, m_current_slot);
|
||||
ret |= m_piece_to_slot[piece_index] = other_slot;
|
||||
ret |= m_storage->swap_slots(other_slot, m_current_slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_storage->move_slot(other_slot, m_current_slot);
|
||||
ret |= m_storage->move_slot(other_slot, m_current_slot);
|
||||
|
||||
}
|
||||
if (ret) return true;
|
||||
|
||||
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
|
||||
|| m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
|
||||
}
|
||||
|
@ -1884,17 +2082,20 @@ namespace libtorrent
|
|||
}
|
||||
}
|
||||
|
||||
bool ret = false;
|
||||
if (piece1 >= 0)
|
||||
{
|
||||
m_piece_to_slot[piece1] = slot2;
|
||||
m_storage->swap_slots3(m_current_slot, slot1, slot2);
|
||||
ret |= m_storage->swap_slots3(m_current_slot, slot1, slot2);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_storage->move_slot(m_current_slot, slot1);
|
||||
m_storage->move_slot(slot2, m_current_slot);
|
||||
ret |= m_storage->move_slot(m_current_slot, slot1);
|
||||
ret |= m_storage->move_slot(slot2, m_current_slot);
|
||||
}
|
||||
|
||||
if (ret) return true;
|
||||
|
||||
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
|
||||
|| m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
|
||||
}
|
||||
|
@ -1916,84 +2117,7 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned
|
||||
|| m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot);
|
||||
}
|
||||
}
|
||||
catch (file_error&)
|
||||
{
|
||||
// find the file that failed, and skip all the blocks in that file
|
||||
size_type file_offset = 0;
|
||||
size_type current_offset = m_current_slot * m_info->piece_length();
|
||||
for (torrent_info::file_iterator i = m_info->begin_files(true);
|
||||
i != m_info->end_files(true); ++i)
|
||||
{
|
||||
file_offset += i->size;
|
||||
if (file_offset > current_offset) break;
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(file_offset > current_offset);
|
||||
int skip_blocks = static_cast<int>(
|
||||
(file_offset - current_offset + m_info->piece_length() - 1)
|
||||
/ m_info->piece_length());
|
||||
|
||||
if (m_storage_mode == storage_mode_compact)
|
||||
{
|
||||
for (int i = m_current_slot; i < m_current_slot + skip_blocks; ++i)
|
||||
{
|
||||
TORRENT_ASSERT(m_slot_to_piece[i] == unallocated);
|
||||
m_unallocated_slots.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// current slot will increase by one at the end of the for-loop too
|
||||
m_current_slot += skip_blocks - 1;
|
||||
}
|
||||
++m_current_slot;
|
||||
|
||||
if (m_current_slot >= m_info->num_pieces())
|
||||
{
|
||||
TORRENT_ASSERT(m_current_slot == m_info->num_pieces());
|
||||
|
||||
// clear the memory we've been using
|
||||
std::vector<char>().swap(m_piece_data);
|
||||
std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
|
||||
|
||||
if (m_storage_mode != storage_mode_compact)
|
||||
{
|
||||
if (!m_out_of_place)
|
||||
{
|
||||
// if no piece is out of place
|
||||
// since we're in full allocation mode, we can
|
||||
// forget the piece allocation tables
|
||||
|
||||
std::vector<int>().swap(m_piece_to_slot);
|
||||
std::vector<int>().swap(m_slot_to_piece);
|
||||
m_state = state_create_files;
|
||||
return std::make_pair(false, 1.f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// in this case we're in full allocation mode, but
|
||||
// we're resuming a compact allocated storage
|
||||
m_state = state_expand_pieces;
|
||||
m_current_slot = 0;
|
||||
return std::make_pair(false, 0.f);
|
||||
}
|
||||
}
|
||||
else if (m_unallocated_slots.empty())
|
||||
{
|
||||
switch_to_full_mode();
|
||||
}
|
||||
m_state = state_create_files;
|
||||
|
||||
#ifndef NDEBUG
|
||||
boost::recursive_mutex::scoped_lock l(mutex);
|
||||
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
|
||||
#endif
|
||||
return std::make_pair(false, 1.f);
|
||||
}
|
||||
|
||||
TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true));
|
||||
|
||||
return std::make_pair(false, (float)m_current_slot / m_info->num_pieces());
|
||||
return false;
|
||||
}
|
||||
|
||||
void piece_manager::switch_to_full_mode()
|
||||
|
|
|
@ -2511,7 +2511,7 @@ namespace libtorrent
|
|||
return done;
|
||||
}
|
||||
|
||||
std::pair<bool, float> torrent::check_files()
|
||||
std::pair<bool, float> torrent::check_files(bool& error)
|
||||
{
|
||||
TORRENT_ASSERT(m_torrent_file->is_valid());
|
||||
INVARIANT_CHECK;
|
||||
|
@ -2519,13 +2519,11 @@ namespace libtorrent
|
|||
TORRENT_ASSERT(m_owning_storage.get());
|
||||
|
||||
std::pair<bool, float> progress(true, 1.f);
|
||||
try
|
||||
{
|
||||
TORRENT_ASSERT(m_storage);
|
||||
progress = m_storage->check_files(m_have_pieces, m_num_pieces
|
||||
, m_ses.m_mutex);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
, m_ses.m_mutex, error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
// probably means file permission failure or invalid filename
|
||||
std::fill(m_have_pieces.begin(), m_have_pieces.end(), false);
|
||||
|
@ -2536,7 +2534,7 @@ namespace libtorrent
|
|||
m_ses.m_alerts.post_alert(
|
||||
file_error_alert(
|
||||
get_handle()
|
||||
, e.what()));
|
||||
, m_storage->error()));
|
||||
}
|
||||
pause();
|
||||
}
|
||||
|
@ -2573,7 +2571,13 @@ namespace libtorrent
|
|||
for (extension_list_t::iterator i = m_extensions.begin()
|
||||
, end(m_extensions.end()); i != end; ++i)
|
||||
{
|
||||
try { (*i)->on_files_checked(); } catch (std::exception&) {}
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
try {
|
||||
#endif
|
||||
(*i)->on_files_checked();
|
||||
#ifndef BOOST_NO_EXCEPTIONS
|
||||
} catch (std::exception&) {}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -102,8 +102,9 @@ void run_storage_tests(boost::intrusive_ptr<torrent_info> info
|
|||
float progress;
|
||||
num_pieces = 0;
|
||||
boost::recursive_mutex mutex;
|
||||
bool error;
|
||||
while (!finished)
|
||||
boost::tie(finished, progress) = pm->check_files(pieces, num_pieces, mutex);
|
||||
boost::tie(finished, progress) = pm->check_files(pieces, num_pieces, mutex, error);
|
||||
|
||||
TEST_CHECK(num_pieces == std::count(pieces.begin(), pieces.end()
|
||||
, true));
|
||||
|
|
Loading…
Reference in New Issue