From 7e83c3fc51d20f94e2ae3dcae313349fa2e3cee6 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Thu, 14 Feb 2008 03:48:20 +0000 Subject: [PATCH] changed storage interface to not require exceptions --- examples/client_test.cpp | 5 +- include/libtorrent/file.hpp | 4 +- include/libtorrent/storage.hpp | 48 ++- include/libtorrent/torrent.hpp | 4 +- parse_buffer_log.py | 4 +- src/disk_io_thread.cpp | 73 +++- src/file.cpp | 37 +- src/file_pool.cpp | 4 + src/mapped_storage.cpp | 146 +++++-- src/session_impl.cpp | 456 +++++++++----------- src/storage.cpp | 738 +++++++++++++++++++-------------- src/torrent.cpp | 24 +- test/test_storage.cpp | 3 +- 13 files changed, 891 insertions(+), 655 deletions(-) diff --git a/examples/client_test.cpp b/examples/client_test.cpp index d1c0136d0..f962f823d 100644 --- a/examples/client_test.cpp +++ b/examples/client_test.cpp @@ -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); diff --git a/include/libtorrent/file.hpp b/include/libtorrent/file.hpp index bd0d03539..bf8e4d84b 100755 --- a/include/libtorrent/file.hpp +++ b/include/libtorrent/file.hpp @@ -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; diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index ff206ad7b..a8763ee10 100755 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -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& pieces, int& num_pieces, storage_mode_t storage_mode , std::string& error_msg); std::pair check_files(std::vector& 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& 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); diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 35f6e18d5..dfeef53e6 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -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 check_files(); + std::pair check_files(bool& error); void files_checked(std::vector const& unfinished_pieces); diff --git a/parse_buffer_log.py b/parse_buffer_log.py index b424ea899..803aac4bc 100644 --- a/parse_buffer_log.py +++ b/parse_buffer_log.py @@ -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') diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index 2d5acfffd..4b8e424dc 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -461,16 +461,27 @@ namespace libtorrent int ret = 0; bool free_current_buffer = true; - try - { - TORRENT_ASSERT(j.storage); + TORRENT_ASSERT(j.storage); #ifdef TORRENT_DISK_STATS - ptime start = time_now(); + 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 - if (handler) m_ios.post(bind(handler, ret, j)); #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); } } diff --git a/src/file.cpp b/src/file.cpp index 5e01e1ae5..efd20acc4 100755 --- a/src/file.cpp +++ b/src/file.cpp @@ -32,6 +32,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/pch.hpp" +#include #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 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(); + } + } diff --git a/src/file_pool.cpp b/src/file_pool.cpp index 7bdf24085..3d70c9a29 100644 --- a/src/file_pool.cpp +++ b/src/file_pool.cpp @@ -57,10 +57,14 @@ namespace libtorrent if (e.key != st) { +#ifdef BOOST_NO_EXCEPTIONS + return boost::shared_ptr(); +#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; diff --git a/src/mapped_storage.cpp b/src/mapped_storage.cpp index 2635b9f82..9e1721eb2 100644 --- a/src/mapped_storage.cpp +++ b/src/mapped_storage.cpp @@ -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(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(m_info->piece_size(slot)); @@ -442,10 +458,17 @@ 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 > 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 > 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( - 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(std::less() @@ -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 > 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 >::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 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 ti diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 63e5465c6..4257bc440 100755 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -134,309 +134,175 @@ namespace detail for (;;) { // temporary torrent used while checking fastresume data - try + t.reset(); { - t.reset(); + boost::mutex::scoped_lock l(m_mutex); + + INVARIANT_CHECK; + + // if the job queue is empty and + // we shouldn't abort + // wait for a signal + while (m_torrents.empty() && !m_abort && !processing) + m_cond.wait(l); + + if (m_abort) { - boost::mutex::scoped_lock l(m_mutex); - - INVARIANT_CHECK; - - // if the job queue is empty and - // we shouldn't abort - // wait for a signal - while (m_torrents.empty() && !m_abort && !processing) - m_cond.wait(l); - - if (m_abort) - { - // no lock is needed here, because the main thread - // has already been shut down by now - processing.reset(); - t.reset(); - std::for_each(m_torrents.begin(), m_torrents.end() - , boost::bind(&torrent::abort + // no lock is needed here, because the main thread + // has already been shut down by now + processing.reset(); + t.reset(); + std::for_each(m_torrents.begin(), m_torrents.end() + , boost::bind(&torrent::abort , boost::bind(&shared_ptr::get - , boost::bind(&piece_checker_data::torrent_ptr, _1)))); - m_torrents.clear(); - std::for_each(m_processing.begin(), m_processing.end() - , boost::bind(&torrent::abort + , boost::bind(&piece_checker_data::torrent_ptr, _1)))); + m_torrents.clear(); + std::for_each(m_processing.begin(), m_processing.end() + , boost::bind(&torrent::abort , boost::bind(&shared_ptr::get - , boost::bind(&piece_checker_data::torrent_ptr, _1)))); - m_processing.clear(); - return; - } - - if (!m_torrents.empty()) - { - t = m_torrents.front(); - if (t->abort) - { - // make sure the locking order is - // consistent to avoid dead locks - // we need to lock the session because closing - // torrents assume to have access to it - l.unlock(); - session_impl::mutex_t::scoped_lock l2(m_ses.m_mutex); - l.lock(); - - t->torrent_ptr->abort(); - m_torrents.pop_front(); - continue; - } - } + , boost::bind(&piece_checker_data::torrent_ptr, _1)))); + m_processing.clear(); + return; } - if (t) + if (!m_torrents.empty()) { - std::string error_msg; - t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file() - , error_msg); - - // lock the session to add the new torrent - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - - if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning)) + t = m_torrents.front(); + if (t->abort) { - m_ses.m_alerts.post_alert(fastresume_rejected_alert( + // make sure the locking order is + // consistent to avoid dead locks + // we need to lock the session because closing + // torrents assume to have access to it + l.unlock(); + session_impl::mutex_t::scoped_lock l2(m_ses.m_mutex); + l.lock(); + + t->torrent_ptr->abort(); + m_torrents.pop_front(); + continue; + } + } + } + + if (t) + { + std::string error_msg; + t->parse_resume_data(t->resume_data, t->torrent_ptr->torrent_file() + , error_msg); + + // lock the session to add the new torrent + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + if (!error_msg.empty() && m_ses.m_alerts.should_post(alert::warning)) + { + m_ses.m_alerts.post_alert(fastresume_rejected_alert( t->torrent_ptr->get_handle() , error_msg)); #if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING) - (*m_ses.m_logger) << "fastresume data for " - << t->torrent_ptr->torrent_file().name() << " rejected: " - << error_msg << "\n"; + (*m_ses.m_logger) << "fastresume data for " + << t->torrent_ptr->torrent_file().name() << " rejected: " + << error_msg << "\n"; #endif - } - - mutex::scoped_lock l2(m_mutex); - - if (m_torrents.empty() || m_torrents.front() != t) - { - // this means the torrent was removed right after it was - // added. Abort the checking. - t.reset(); - continue; - } - - // clear the resume data now that it has been used - // (the fast resume data is now parsed and stored in t) - t->resume_data = entry(); - bool up_to_date = t->torrent_ptr->check_fastresume(*t); - - if (up_to_date) - { - INVARIANT_CHECK; - - TORRENT_ASSERT(!m_torrents.empty()); - TORRENT_ASSERT(m_torrents.front() == t); - - t->torrent_ptr->files_checked(t->unfinished_pieces); - m_torrents.pop_front(); - - // we cannot add the torrent if the session is aborted. - if (!m_ses.is_aborted()) - { - m_ses.m_torrents.insert(std::make_pair(t->info_hash, t->torrent_ptr)); - if (m_ses.m_alerts.should_post(alert::info)) - { - m_ses.m_alerts.post_alert(torrent_checked_alert( - processing->torrent_ptr->get_handle() - , "torrent finished checking")); - } - if (t->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info)) - { - m_ses.m_alerts.post_alert(torrent_finished_alert( - t->torrent_ptr->get_handle() - , "torrent is complete")); - } - - peer_id id; - std::fill(id.begin(), id.end(), 0); - for (std::vector::const_iterator i = t->peers.begin(); - i != t->peers.end(); ++i) - { - t->torrent_ptr->get_policy().peer_from_tracker(*i, id - , peer_info::resume_data, 0); - } - - for (std::vector::const_iterator i = t->banned_peers.begin(); - i != t->banned_peers.end(); ++i) - { - policy::peer* p = t->torrent_ptr->get_policy().peer_from_tracker(*i, id - , peer_info::resume_data, 0); - if (p) p->banned = true; - } - } - else - { - t->torrent_ptr->abort(); - } - t.reset(); - continue; - } - - l.unlock(); - - // move the torrent from - // m_torrents to m_processing - TORRENT_ASSERT(m_torrents.front() == t); - - m_torrents.pop_front(); - m_processing.push_back(t); - if (!processing) - { - processing = t; - processing->processing = true; - t.reset(); - } } - } - catch (const std::exception& e) - { - // 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_torrents.empty() || m_torrents.front() != t) { - m_ses.m_alerts.post_alert( - file_error_alert( - t->torrent_ptr->get_handle() - , e.what())); + // this means the torrent was removed right after it was + // added. Abort the checking. + t.reset(); + continue; } - t->torrent_ptr->abort(); - TORRENT_ASSERT(!m_torrents.empty()); - m_torrents.pop_front(); - } - 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(); + // clear the resume data now that it has been used + // (the fast resume data is now parsed and stored in t) + t->resume_data = entry(); + bool up_to_date = t->torrent_ptr->check_fastresume(*t); + if (up_to_date) { - mutex::scoped_lock l2(m_mutex); - INVARIANT_CHECK; - processing->progress = progress; - if (processing->abort) - { - TORRENT_ASSERT(!m_processing.empty()); - TORRENT_ASSERT(m_processing.front() == processing); - m_processing.pop_front(); + TORRENT_ASSERT(!m_torrents.empty()); + TORRENT_ASSERT(m_torrents.front() == t); - // make sure the lock order is correct - l2.unlock(); - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - l2.lock(); - processing->torrent_ptr->abort(); + t->torrent_ptr->files_checked(t->unfinished_pieces); + m_torrents.pop_front(); - processing.reset(); - if (!m_processing.empty()) - { - processing = m_processing.front(); - processing->processing = true; - } - continue; - } - } - if (finished) - { - // lock the session to add the new torrent - session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - mutex::scoped_lock l2(m_mutex); - - INVARIANT_CHECK; - - TORRENT_ASSERT(!m_processing.empty()); - TORRENT_ASSERT(m_processing.front() == processing); - - // TODO: factor out the adding of torrents to the session - // and to the checker thread to avoid duplicating the - // check for abortion. + // we cannot add the torrent if the session is aborted. if (!m_ses.is_aborted()) { - processing->torrent_ptr->files_checked(processing->unfinished_pieces); - m_ses.m_torrents.insert(std::make_pair( - processing->info_hash, processing->torrent_ptr)); + m_ses.m_torrents.insert(std::make_pair(t->info_hash, t->torrent_ptr)); if (m_ses.m_alerts.should_post(alert::info)) { m_ses.m_alerts.post_alert(torrent_checked_alert( - processing->torrent_ptr->get_handle() - , "torrent finished checking")); - } - if (processing->torrent_ptr->is_seed() - && m_ses.m_alerts.should_post(alert::info)) + processing->torrent_ptr->get_handle() + , "torrent finished checking")); + } + if (t->torrent_ptr->is_seed() && m_ses.m_alerts.should_post(alert::info)) { m_ses.m_alerts.post_alert(torrent_finished_alert( - processing->torrent_ptr->get_handle() - , "torrent is complete")); + t->torrent_ptr->get_handle() + , "torrent is complete")); } peer_id id; std::fill(id.begin(), id.end(), 0); - for (std::vector::const_iterator i = processing->peers.begin(); - i != processing->peers.end(); ++i) + for (std::vector::const_iterator i = t->peers.begin(); + i != t->peers.end(); ++i) { - processing->torrent_ptr->get_policy().peer_from_tracker(*i, id + t->torrent_ptr->get_policy().peer_from_tracker(*i, id , peer_info::resume_data, 0); } - for (std::vector::const_iterator i = processing->banned_peers.begin(); - i != processing->banned_peers.end(); ++i) + for (std::vector::const_iterator i = t->banned_peers.begin(); + i != t->banned_peers.end(); ++i) { - policy::peer* p = processing->torrent_ptr->get_policy().peer_from_tracker(*i, id + policy::peer* p = t->torrent_ptr->get_policy().peer_from_tracker(*i, id , peer_info::resume_data, 0); if (p) p->banned = true; } } else { - processing->torrent_ptr->abort(); - } - processing.reset(); - m_processing.pop_front(); - if (!m_processing.empty()) - { - processing = m_processing.front(); - processing->processing = true; + t->torrent_ptr->abort(); } + t.reset(); + continue; + } + + l.unlock(); + + // move the torrent from + // m_torrents to m_processing + TORRENT_ASSERT(m_torrents.front() == t); + + m_torrents.pop_front(); + m_processing.push_back(t); + if (!processing) + { + processing = t; + processing->processing = true; + t.reset(); } } - catch(std::exception const& 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 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(); @@ -446,15 +312,90 @@ namespace detail processing = m_processing.front(); processing->processing = true; } + continue; } - catch(...) - { -#ifndef NDEBUG - std::cerr << "error while checking files\n"; -#endif - mutex::scoped_lock l(m_mutex); - TORRENT_ASSERT(!m_processing.empty()); + { + mutex::scoped_lock l2(m_mutex); + + INVARIANT_CHECK; + + processing->progress = progress; + if (processing->abort) + { + TORRENT_ASSERT(!m_processing.empty()); + TORRENT_ASSERT(m_processing.front() == processing); + m_processing.pop_front(); + + // make sure the lock order is correct + l2.unlock(); + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + l2.lock(); + processing->torrent_ptr->abort(); + + processing.reset(); + if (!m_processing.empty()) + { + processing = m_processing.front(); + processing->processing = true; + } + continue; + } + } + if (finished) + { + // lock the session to add the new torrent + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + mutex::scoped_lock l2(m_mutex); + + INVARIANT_CHECK; + + TORRENT_ASSERT(!m_processing.empty()); + TORRENT_ASSERT(m_processing.front() == processing); + + // TODO: factor out the adding of torrents to the session + // and to the checker thread to avoid duplicating the + // check for abortion. + if (!m_ses.is_aborted()) + { + processing->torrent_ptr->files_checked(processing->unfinished_pieces); + m_ses.m_torrents.insert(std::make_pair( + processing->info_hash, processing->torrent_ptr)); + if (m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(torrent_checked_alert( + processing->torrent_ptr->get_handle() + , "torrent finished checking")); + } + if (processing->torrent_ptr->is_seed() + && m_ses.m_alerts.should_post(alert::info)) + { + m_ses.m_alerts.post_alert(torrent_finished_alert( + processing->torrent_ptr->get_handle() + , "torrent is complete")); + } + + peer_id id; + std::fill(id.begin(), id.end(), 0); + for (std::vector::const_iterator i = processing->peers.begin(); + i != processing->peers.end(); ++i) + { + processing->torrent_ptr->get_policy().peer_from_tracker(*i, id + , peer_info::resume_data, 0); + } + + for (std::vector::const_iterator i = processing->banned_peers.begin(); + i != processing->banned_peers.end(); ++i) + { + policy::peer* p = processing->torrent_ptr->get_policy().peer_from_tracker(*i, id + , peer_info::resume_data, 0); + if (p) p->banned = true; + } + } + else + { + processing->torrent_ptr->abort(); + } processing.reset(); m_processing.pop_front(); if (!m_processing.empty()) @@ -462,8 +403,6 @@ namespace detail processing = m_processing.front(); processing->processing = true; } - - TORRENT_ASSERT(false); } } } @@ -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; diff --git a/src/storage.cpp b/src/storage.cpp index ba88575a0..108c125bb 100755 --- a/src/storage.cpp +++ b/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 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); + result = errno; + } } - if (!error.empty()) throw std::runtime_error(error); + if (!error.empty()) m_error.swap(error); + return result != 0; } - void storage::write_resume_data(entry& rd) const + bool storage::write_resume_data(entry& rd) const { + if (rd.type() != entry::dictionary_t) + { + m_error = "invalid fastresume file"; + return true; + } std::vector > 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 >::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 > 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 > 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( - 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(std::less() @@ -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( @@ -776,7 +830,17 @@ namespace libtorrent int buf_pos = 0; boost::shared_ptr in(m_files.open_file( - this, m_save_path / file_iter->path, file::in)); + 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 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 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 piece_manager::check_files( - std::vector& pieces, int& num_pieces, boost::recursive_mutex& mutex) + std::vector& 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,238 +1793,14 @@ namespace libtorrent TORRENT_ASSERT(m_state == state_full_check); - // ------------------------ - // DO THE FULL CHECK - // ------------------------ + bool skip = check_one_piece(pieces, num_pieces, mutex); - try + if (skip) { - // initialization for the full check - if (m_hash_to_piece.empty()) - { - for (int i = 0; i < m_info->num_pieces(); ++i) - { - m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i)); - } - boost::recursive_mutex::scoped_lock l(mutex); - std::fill(pieces.begin(), pieces.end(), false); - num_pieces = 0; - } - - m_piece_data.resize(int(m_info->piece_length())); - int piece_size = int(m_info->piece_size(m_current_slot)); - int num_read = m_storage->read(&m_piece_data[0] - , m_current_slot, 0, piece_size); - - // if the file is incomplete, skip the rest of it - if (num_read != piece_size) - throw file_error(""); - - int piece_index = identify_data(m_piece_data, m_current_slot - , pieces, num_pieces, m_hash_to_piece, mutex); - - if (piece_index != m_current_slot - && piece_index >= 0) - m_out_of_place = true; - - TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true)); - TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0); - - const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated; - const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot; - - // check if this piece should be swapped with any other slot - // this section will ensure that the storage is correctly sorted - // libtorrent will never leave the storage in a state that - // requires this sorting, but other clients may. - - // example of worst case: - // | m_current_slot = 5 - // V - // +---+- - - +---+- - - +---+- - - // | x | | 5 | | 3 | <- piece data in slots - // +---+- - - +---+- - - +---+- - - // 3 y 5 <- slot index - - // in this example, the data in the m_current_slot (5) - // is piece 3. It has to be moved into slot 3. The data - // in slot y (piece 5) should be moved into the m_current_slot. - // and the data in slot 3 (piece x) should be moved to slot y. - - // there are three possible cases. - // 1. There's another piece that should be placed into this slot - // 2. This piece should be placed into another slot. - // 3. There's another piece that should be placed into this slot - // and this piece should be placed into another slot - - // swap piece_index with this slot - - // case 1 - if (this_should_move && !other_should_move) - { - TORRENT_ASSERT(piece_index != m_current_slot); - - const int other_slot = piece_index; - TORRENT_ASSERT(other_slot >= 0); - int other_piece = m_slot_to_piece[other_slot]; - - m_slot_to_piece[other_slot] = piece_index; - m_slot_to_piece[m_current_slot] = other_piece; - m_piece_to_slot[piece_index] = piece_index; - if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot; - - if (other_piece == unassigned) - { - std::vector::iterator i = - std::find(m_free_slots.begin(), m_free_slots.end(), other_slot); - TORRENT_ASSERT(i != m_free_slots.end()); - if (m_storage_mode == storage_mode_compact) - { - m_free_slots.erase(i); - m_free_slots.push_back(m_current_slot); - } - } - - if (other_piece >= 0) - m_storage->swap_slots(other_slot, m_current_slot); - else - m_storage->move_slot(m_current_slot, other_slot); - - TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned - || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); - } - // case 2 - else if (!this_should_move && other_should_move) - { - TORRENT_ASSERT(piece_index != m_current_slot); - - const int other_piece = m_current_slot; - const int other_slot = m_piece_to_slot[other_piece]; - TORRENT_ASSERT(other_slot >= 0); - - m_slot_to_piece[m_current_slot] = other_piece; - m_slot_to_piece[other_slot] = piece_index; - m_piece_to_slot[other_piece] = m_current_slot; - - if (piece_index == unassigned - && m_storage_mode == storage_mode_compact) - m_free_slots.push_back(other_slot); - - if (piece_index >= 0) - { - m_piece_to_slot[piece_index] = other_slot; - m_storage->swap_slots(other_slot, m_current_slot); - } - else - { - m_storage->move_slot(other_slot, m_current_slot); - } - TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned - || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); - } - else if (this_should_move && other_should_move) - { - TORRENT_ASSERT(piece_index != m_current_slot); - TORRENT_ASSERT(piece_index >= 0); - - const int piece1 = m_slot_to_piece[piece_index]; - const int piece2 = m_current_slot; - const int slot1 = piece_index; - const int slot2 = m_piece_to_slot[piece2]; - - TORRENT_ASSERT(slot1 >= 0); - TORRENT_ASSERT(slot2 >= 0); - TORRENT_ASSERT(piece2 >= 0); - - if (slot1 == slot2) - { - // this means there are only two pieces involved in the swap - TORRENT_ASSERT(piece1 >= 0); - - // movement diagram: - // +-------------------------------+ - // | | - // +--> slot1 --> m_current_slot --+ - - m_slot_to_piece[slot1] = piece_index; - m_slot_to_piece[m_current_slot] = piece1; - - m_piece_to_slot[piece_index] = slot1; - m_piece_to_slot[piece1] = m_current_slot; - - TORRENT_ASSERT(piece1 == m_current_slot); - TORRENT_ASSERT(piece_index == slot1); - - m_storage->swap_slots(m_current_slot, slot1); - - TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned - || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); - } - else - { - TORRENT_ASSERT(slot1 != slot2); - TORRENT_ASSERT(piece1 != piece2); - - // movement diagram: - // +-----------------------------------------+ - // | | - // +--> slot1 --> slot2 --> m_current_slot --+ - - m_slot_to_piece[slot1] = piece_index; - m_slot_to_piece[slot2] = piece1; - m_slot_to_piece[m_current_slot] = piece2; - - m_piece_to_slot[piece_index] = slot1; - m_piece_to_slot[m_current_slot] = piece2; - - if (piece1 == unassigned) - { - std::vector::iterator i = - std::find(m_free_slots.begin(), m_free_slots.end(), slot1); - TORRENT_ASSERT(i != m_free_slots.end()); - if (m_storage_mode == storage_mode_compact) - { - m_free_slots.erase(i); - m_free_slots.push_back(slot2); - } - } - - if (piece1 >= 0) - { - m_piece_to_slot[piece1] = slot2; - 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); - } - - TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned - || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); - } - } - else - { - TORRENT_ASSERT(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot); - TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unallocated); - TORRENT_ASSERT(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot); - - // the slot was identified as piece 'piece_index' - if (piece_index != unassigned) - m_piece_to_slot[piece_index] = m_current_slot; - else if (m_storage_mode == storage_mode_compact) - m_free_slots.push_back(m_current_slot); - - m_slot_to_piece[m_current_slot] = piece_index; - - 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 + 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); @@ -1931,8 +1812,8 @@ namespace libtorrent TORRENT_ASSERT(file_offset > current_offset); int skip_blocks = static_cast( - (file_offset - current_offset + m_info->piece_length() - 1) - / m_info->piece_length()); + (file_offset - current_offset + m_info->piece_length() - 1) + / m_info->piece_length()); if (m_storage_mode == storage_mode_compact) { @@ -1946,6 +1827,7 @@ namespace libtorrent // 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()) @@ -1996,6 +1878,248 @@ namespace libtorrent return std::make_pair(false, (float)m_current_slot / m_info->num_pieces()); } + bool piece_manager::check_one_piece(std::vector& pieces, int& num_pieces + , boost::recursive_mutex& mutex) + { + // ------------------------ + // DO THE FULL CHECK + // ------------------------ + + // initialization for the full check + if (m_hash_to_piece.empty()) + { + for (int i = 0; i < m_info->num_pieces(); ++i) + { + m_hash_to_piece.insert(std::make_pair(m_info->hash_for_piece(i), i)); + } + boost::recursive_mutex::scoped_lock l(mutex); + std::fill(pieces.begin(), pieces.end(), false); + num_pieces = 0; + } + + m_piece_data.resize(int(m_info->piece_length())); + int piece_size = int(m_info->piece_size(m_current_slot)); + int num_read = m_storage->read(&m_piece_data[0] + , m_current_slot, 0, piece_size); + + // if the file is incomplete, skip the rest of it + if (num_read != piece_size) + return true; + + int piece_index = identify_data(m_piece_data, m_current_slot + , pieces, num_pieces, m_hash_to_piece, mutex); + + if (piece_index != m_current_slot + && piece_index >= 0) + m_out_of_place = true; + + TORRENT_ASSERT(num_pieces == std::count(pieces.begin(), pieces.end(), true)); + TORRENT_ASSERT(piece_index == unassigned || piece_index >= 0); + + const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated; + const bool other_should_move = m_piece_to_slot[m_current_slot] != has_no_slot; + + // check if this piece should be swapped with any other slot + // this section will ensure that the storage is correctly sorted + // libtorrent will never leave the storage in a state that + // requires this sorting, but other clients may. + + // example of worst case: + // | m_current_slot = 5 + // V + // +---+- - - +---+- - - +---+- - + // | x | | 5 | | 3 | <- piece data in slots + // +---+- - - +---+- - - +---+- - + // 3 y 5 <- slot index + + // in this example, the data in the m_current_slot (5) + // is piece 3. It has to be moved into slot 3. The data + // in slot y (piece 5) should be moved into the m_current_slot. + // and the data in slot 3 (piece x) should be moved to slot y. + + // there are three possible cases. + // 1. There's another piece that should be placed into this slot + // 2. This piece should be placed into another slot. + // 3. There's another piece that should be placed into this slot + // and this piece should be placed into another slot + + // swap piece_index with this slot + + // case 1 + if (this_should_move && !other_should_move) + { + TORRENT_ASSERT(piece_index != m_current_slot); + + const int other_slot = piece_index; + TORRENT_ASSERT(other_slot >= 0); + int other_piece = m_slot_to_piece[other_slot]; + + m_slot_to_piece[other_slot] = piece_index; + m_slot_to_piece[m_current_slot] = other_piece; + m_piece_to_slot[piece_index] = piece_index; + if (other_piece >= 0) m_piece_to_slot[other_piece] = m_current_slot; + + if (other_piece == unassigned) + { + std::vector::iterator i = + std::find(m_free_slots.begin(), m_free_slots.end(), other_slot); + TORRENT_ASSERT(i != m_free_slots.end()); + if (m_storage_mode == storage_mode_compact) + { + m_free_slots.erase(i); + m_free_slots.push_back(m_current_slot); + } + } + + bool ret = false; + if (other_piece >= 0) + ret |= m_storage->swap_slots(other_slot, m_current_slot); + else + 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); + } + // case 2 + else if (!this_should_move && other_should_move) + { + TORRENT_ASSERT(piece_index != m_current_slot); + + const int other_piece = m_current_slot; + const int other_slot = m_piece_to_slot[other_piece]; + TORRENT_ASSERT(other_slot >= 0); + + m_slot_to_piece[m_current_slot] = other_piece; + m_slot_to_piece[other_slot] = piece_index; + m_piece_to_slot[other_piece] = m_current_slot; + + if (piece_index == unassigned + && m_storage_mode == storage_mode_compact) + m_free_slots.push_back(other_slot); + + bool ret = false; + if (piece_index >= 0) + { + ret |= m_piece_to_slot[piece_index] = other_slot; + ret |= m_storage->swap_slots(other_slot, m_current_slot); + } + else + { + 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); + } + else if (this_should_move && other_should_move) + { + TORRENT_ASSERT(piece_index != m_current_slot); + TORRENT_ASSERT(piece_index >= 0); + + const int piece1 = m_slot_to_piece[piece_index]; + const int piece2 = m_current_slot; + const int slot1 = piece_index; + const int slot2 = m_piece_to_slot[piece2]; + + TORRENT_ASSERT(slot1 >= 0); + TORRENT_ASSERT(slot2 >= 0); + TORRENT_ASSERT(piece2 >= 0); + + if (slot1 == slot2) + { + // this means there are only two pieces involved in the swap + TORRENT_ASSERT(piece1 >= 0); + + // movement diagram: + // +-------------------------------+ + // | | + // +--> slot1 --> m_current_slot --+ + + m_slot_to_piece[slot1] = piece_index; + m_slot_to_piece[m_current_slot] = piece1; + + m_piece_to_slot[piece_index] = slot1; + m_piece_to_slot[piece1] = m_current_slot; + + TORRENT_ASSERT(piece1 == m_current_slot); + TORRENT_ASSERT(piece_index == slot1); + + m_storage->swap_slots(m_current_slot, slot1); + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + else + { + TORRENT_ASSERT(slot1 != slot2); + TORRENT_ASSERT(piece1 != piece2); + + // movement diagram: + // +-----------------------------------------+ + // | | + // +--> slot1 --> slot2 --> m_current_slot --+ + + m_slot_to_piece[slot1] = piece_index; + m_slot_to_piece[slot2] = piece1; + m_slot_to_piece[m_current_slot] = piece2; + + m_piece_to_slot[piece_index] = slot1; + m_piece_to_slot[m_current_slot] = piece2; + + if (piece1 == unassigned) + { + std::vector::iterator i = + std::find(m_free_slots.begin(), m_free_slots.end(), slot1); + TORRENT_ASSERT(i != m_free_slots.end()); + if (m_storage_mode == storage_mode_compact) + { + m_free_slots.erase(i); + m_free_slots.push_back(slot2); + } + } + + bool ret = false; + if (piece1 >= 0) + { + m_piece_to_slot[piece1] = slot2; + ret |= m_storage->swap_slots3(m_current_slot, slot1, slot2); + } + else + { + 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); + } + } + else + { + TORRENT_ASSERT(m_piece_to_slot[m_current_slot] == has_no_slot || piece_index != m_current_slot); + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unallocated); + TORRENT_ASSERT(piece_index == unassigned || m_piece_to_slot[piece_index] == has_no_slot); + + // the slot was identified as piece 'piece_index' + if (piece_index != unassigned) + m_piece_to_slot[piece_index] = m_current_slot; + else if (m_storage_mode == storage_mode_compact) + m_free_slots.push_back(m_current_slot); + + m_slot_to_piece[m_current_slot] = piece_index; + + TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned + || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); + } + return false; + } + void piece_manager::switch_to_full_mode() { TORRENT_ASSERT(m_storage_mode == storage_mode_compact); diff --git a/src/torrent.cpp b/src/torrent.cpp index a5c81d313..727602c84 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -2511,7 +2511,7 @@ namespace libtorrent return done; } - std::pair torrent::check_files() + std::pair 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 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) + TORRENT_ASSERT(m_storage); + progress = m_storage->check_files(m_have_pieces, m_num_pieces + , 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 diff --git a/test/test_storage.cpp b/test/test_storage.cpp index 93a99bdda..9901d1be1 100644 --- a/test/test_storage.cpp +++ b/test/test_storage.cpp @@ -102,8 +102,9 @@ void run_storage_tests(boost::intrusive_ptr 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));