diff --git a/libtorrent/include/libtorrent/alert.hpp b/libtorrent/include/libtorrent/alert.hpp index afa8e978f..49b1129b4 100644 --- a/libtorrent/include/libtorrent/alert.hpp +++ b/libtorrent/include/libtorrent/alert.hpp @@ -115,7 +115,7 @@ namespace libtorrent { std::auto_ptr get(); template - bool should_post() const { return m_alert_mask & T::static_category; } + bool should_post() const { return (m_alert_mask & T::static_category) != 0; } alert const* wait_for_alert(time_duration max_wait); diff --git a/libtorrent/include/libtorrent/config.hpp b/libtorrent/include/libtorrent/config.hpp index 1ee97bd57..a1db4b412 100644 --- a/libtorrent/include/libtorrent/config.hpp +++ b/libtorrent/include/libtorrent/config.hpp @@ -83,7 +83,8 @@ POSSIBILITY OF SUCH DAMAGE. #endif // should wpath or path be used? -#if defined UNICODE && !defined BOOST_FILESYSTEM_NARROW_ONLY && BOOST_VERSION >= 103400 +#if defined UNICODE && !defined BOOST_FILESYSTEM_NARROW_ONLY \ + && BOOST_VERSION >= 103400 && defined WIN32 #define TORRENT_USE_WPATH 1 #else #define TORRENT_USE_WPATH 0 diff --git a/libtorrent/include/libtorrent/disk_io_thread.hpp b/libtorrent/include/libtorrent/disk_io_thread.hpp index d0d574411..922896741 100644 --- a/libtorrent/include/libtorrent/disk_io_thread.hpp +++ b/libtorrent/include/libtorrent/disk_io_thread.hpp @@ -86,6 +86,7 @@ namespace libtorrent , save_resume_data , rename_file , abort_thread + , clear_read_cache }; action_t action; @@ -111,6 +112,9 @@ namespace libtorrent boost::shared_ptr resume_data; + // the error code from the file operation + error_code error; + // this is called when operation completes boost::function callback; }; @@ -214,6 +218,8 @@ namespace libtorrent typedef boost::recursive_mutex mutex_t; typedef std::list cache_t; + bool test_error(disk_io_job& j); + // cache operations cache_t::iterator find_cached_piece( cache_t& cache, disk_io_job const& j diff --git a/libtorrent/include/libtorrent/error_code.hpp b/libtorrent/include/libtorrent/error_code.hpp new file mode 100644 index 000000000..ffc7d662e --- /dev/null +++ b/libtorrent/include/libtorrent/error_code.hpp @@ -0,0 +1,81 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_ERROR_CODE_HPP_INCLUDED +#define TORRENT_ERROR_CODE_HPP_INCLUDED + +#include + +#if BOOST_VERSION < 103500 +#include +#else +#include +#endif + +namespace libtorrent +{ + + namespace errors + { + enum error_code_enum + { + no_error = 0, + file_collision + }; + } + +#if BOOST_VERSION < 103500 + typedef asio::error_code error_code; + inline asio::error::error_category get_posix_category() { return asio::error::system_category; } + inline asio::error::error_category get_system_category() { return asio::error::system_category; } +#else + + struct libtorrent_error_category : boost::system::error_category + { + virtual const char* name() const; + virtual std::string message(int ev) const; + virtual boost::system::error_condition default_error_condition(int ev) const + { return boost::system::error_condition(ev, *this); } + }; + + extern libtorrent_error_category libtorrent_category; + + using boost::system::error_code; + inline boost::system::error_category const& get_system_category() + { return boost::system::get_system_category(); } + inline boost::system::error_category const& get_posix_category() + { return boost::system::get_posix_category(); } +#endif +} + +#endif + diff --git a/libtorrent/include/libtorrent/file.hpp b/libtorrent/include/libtorrent/file.hpp index a0d3ef1c0..c1f70b50c 100644 --- a/libtorrent/include/libtorrent/file.hpp +++ b/libtorrent/include/libtorrent/file.hpp @@ -48,6 +48,7 @@ POSSIBILITY OF SUCH DAMAGE. #pragma warning(pop) #endif +#include "libtorrent/error_code.hpp" #include "libtorrent/size_type.hpp" #include "libtorrent/config.hpp" @@ -76,7 +77,6 @@ namespace libtorrent public: open_mode(): m_mask(0) {} - open_mode operator|(open_mode m) const { return open_mode(m.m_mask | m_mask); } @@ -91,6 +91,7 @@ namespace libtorrent bool operator==(open_mode m) const { return m_mask == m.m_mask; } bool operator!=(open_mode m) const { return m_mask != m.m_mask; } + operator bool() const { return m_mask != 0; } private: @@ -102,25 +103,30 @@ namespace libtorrent static const open_mode out; file(); - file(fs::path const& p, open_mode m); + file(fs::path const& p, open_mode m, error_code& ec); ~file(); - bool open(fs::path const& p, open_mode m); + bool open(fs::path const& p, open_mode m, error_code& ec); + bool is_open() const; void close(); - bool set_size(size_type size); + bool set_size(size_type size, error_code& ec); - size_type write(const char*, size_type num_bytes); - size_type read(char*, size_type num_bytes); + size_type write(const char*, size_type num_bytes, error_code& ec); + size_type read(char*, size_type num_bytes, error_code& ec); - size_type seek(size_type pos, seek_mode m = begin); - size_type tell(); - - std::string const& error() const; + size_type seek(size_type pos, seek_mode m, error_code& ec); + size_type tell(error_code& ec); private: - struct impl; - const std::auto_ptr m_impl; +#ifdef TORRENT_WINDOWS + HANDLE m_file_handle; +#else + int m_fd; +#endif +#ifndef NDEBUG + open_mode m_open_mode; +#endif }; diff --git a/libtorrent/include/libtorrent/file_pool.hpp b/libtorrent/include/libtorrent/file_pool.hpp index 69116d675..cbabfe807 100644 --- a/libtorrent/include/libtorrent/file_pool.hpp +++ b/libtorrent/include/libtorrent/file_pool.hpp @@ -66,7 +66,7 @@ namespace libtorrent file_pool(int size = 40): m_size(size) {} boost::shared_ptr open_file(void* st, fs::path const& p - , file::open_mode m, std::string& error); + , file::open_mode m, error_code& ec); void release(void* st); void release(fs::path const& p); void resize(int size); diff --git a/libtorrent/include/libtorrent/lazy_entry.hpp b/libtorrent/include/libtorrent/lazy_entry.hpp index ffd4337e4..ffb5b67ee 100644 --- a/libtorrent/include/libtorrent/lazy_entry.hpp +++ b/libtorrent/include/libtorrent/lazy_entry.hpp @@ -124,6 +124,7 @@ namespace libtorrent size_type dict_find_int_value(char const* name, size_type default_val = 0) const; lazy_entry const* dict_find_dict(char const* name) const; lazy_entry const* dict_find_list(char const* name) const; + lazy_entry const* dict_find_string(char const* name) const; std::pair dict_at(int i) const { diff --git a/libtorrent/include/libtorrent/peer_connection.hpp b/libtorrent/include/libtorrent/peer_connection.hpp index 5a24e433d..c97ca66b9 100644 --- a/libtorrent/include/libtorrent/peer_connection.hpp +++ b/libtorrent/include/libtorrent/peer_connection.hpp @@ -323,6 +323,10 @@ namespace libtorrent // the number of bytes transferred within unchoke cycles void reset_choke_counters(); + // if this peer connection is useless (neither party is + // interested in the other), disconnect it + void disconnect_if_redundant(); + #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING boost::shared_ptr m_logger; #endif @@ -854,9 +858,13 @@ namespace libtorrent // is set to 1 bool m_snubbed:1; + // this is set to true once the bitfield is received + bool m_bitfield_received:1; + #ifndef NDEBUG public: bool m_in_constructor:1; + bool m_disconnect_started:1; #endif }; } diff --git a/libtorrent/include/libtorrent/peer_info.hpp b/libtorrent/include/libtorrent/peer_info.hpp index 83aa217cf..c85069bff 100644 --- a/libtorrent/include/libtorrent/peer_info.hpp +++ b/libtorrent/include/libtorrent/peer_info.hpp @@ -57,7 +57,8 @@ namespace libtorrent on_parole = 0x200, seed = 0x400, optimistic_unchoke = 0x800, - snubbed = 0x1000 + snubbed = 0x1000, + upload_only = 0x2000 #ifndef TORRENT_DISABLE_ENCRYPTION , rc4_encrypted = 0x100000, plaintext_encrypted = 0x200000 diff --git a/libtorrent/include/libtorrent/socket.hpp b/libtorrent/include/libtorrent/socket.hpp index d041024bb..d4cc9bdb0 100644 --- a/libtorrent/include/libtorrent/socket.hpp +++ b/libtorrent/include/libtorrent/socket.hpp @@ -73,6 +73,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/io.hpp" #include "libtorrent/time.hpp" +#include "libtorrent/error_code.hpp" #ifdef _MSC_VER #pragma warning(pop) @@ -94,12 +95,10 @@ namespace libtorrent typedef asio::ip::udp::socket datagram_socket; typedef asio::ip::tcp::acceptor socket_acceptor; typedef asio::io_service io_service; - typedef asio::error_code error_code; namespace asio = ::asio; typedef asio::basic_deadline_timer deadline_timer; #else - using boost::system::error_code; using boost::asio::ip::tcp; using boost::asio::ip::udp; using boost::asio::async_write; diff --git a/libtorrent/include/libtorrent/storage.hpp b/libtorrent/include/libtorrent/storage.hpp index 1733abe8a..d2847cb8e 100644 --- a/libtorrent/include/libtorrent/storage.hpp +++ b/libtorrent/include/libtorrent/storage.hpp @@ -164,17 +164,17 @@ namespace libtorrent // non-zero return value indicates an error virtual bool delete_files() = 0; - void set_error(std::string const& file, std::string const& msg) const + void set_error(boost::filesystem::path const& file, error_code const& ec) const { - m_error_file = file; - m_error = msg; + m_error_file = file.string(); + m_error = ec; } - std::string const& error() const { return m_error; } + error_code const& error() const { return m_error; } std::string const& error_file() const { return m_error_file; } - void clear_error() { m_error.clear(); m_error_file.clear(); } + void clear_error() { m_error = error_code(); m_error_file.clear(); } - mutable std::string m_error; + mutable error_code m_error; mutable std::string m_error_file; virtual ~storage_interface() {} @@ -236,6 +236,10 @@ namespace libtorrent boost::function const& handler = boost::function()); + void async_clear_read_cache( + boost::function const& handler + = boost::function()); + void async_delete_files( boost::function const& handler = boost::function()); @@ -267,7 +271,7 @@ namespace libtorrent void mark_failed(int index); - std::string const& error() const { return m_storage->error(); } + error_code const& error() const { return m_storage->error(); } std::string const& error_file() const { return m_storage->error_file(); } void clear_error() { m_storage->clear_error(); } @@ -308,7 +312,8 @@ namespace libtorrent , int offset , int size); - bool check_one_piece(int& have_piece); + // -1=error 0=ok 1=skip + int check_one_piece(int& have_piece); int identify_data( const std::vector& piece_data , int current_slot); diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index 46c438208..d6a0f9dd3 100644 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/include/libtorrent/torrent.hpp @@ -235,7 +235,13 @@ namespace libtorrent void prioritize_pieces(std::vector const& pieces); void piece_priorities(std::vector&) const; + void set_file_priority(int index, int priority); + int file_priority(int index) const; + void prioritize_files(std::vector const& files); + void file_priorities(std::vector&) const; + + void update_piece_priorities(); torrent_status status() const; @@ -610,6 +616,8 @@ namespace libtorrent { return m_connections_initialized; } bool valid_metadata() const { return m_torrent_file->is_valid(); } + bool are_files_checked() const + { return m_files_checked; } // parses the info section from the given // bencoded tree and moves the torrent @@ -768,6 +776,8 @@ namespace libtorrent // this torrent belongs to. aux::session_impl& m_ses; + std::vector m_file_priority; + boost::scoped_ptr m_picker; // the queue of peer_connections that want more bandwidth diff --git a/libtorrent/include/libtorrent/torrent_handle.hpp b/libtorrent/include/libtorrent/torrent_handle.hpp index a062580c2..b19b2a2a9 100644 --- a/libtorrent/include/libtorrent/torrent_handle.hpp +++ b/libtorrent/include/libtorrent/torrent_handle.hpp @@ -392,8 +392,12 @@ namespace libtorrent void prioritize_pieces(std::vector const& pieces) const; std::vector piece_priorities() const; - void prioritize_files(std::vector const& files) const; + // priority must be within the range [0, 7] + void file_priority(int index, int priority) const; + int file_priority(int index) const; + void prioritize_files(std::vector const& files) const; + std::vector file_priorities() const; // set the interface to bind outgoing connections // to. diff --git a/libtorrent/src/bt_peer_connection.cpp b/libtorrent/src/bt_peer_connection.cpp index 72515ff19..1d7b5830f 100755 --- a/libtorrent/src/bt_peer_connection.cpp +++ b/libtorrent/src/bt_peer_connection.cpp @@ -2647,9 +2647,6 @@ namespace libtorrent TORRENT_ASSERT(m_sent_handshake); } - if (!m_in_constructor) - peer_connection::check_invariant(); - if (!m_payloads.empty()) { for (std::deque::const_iterator i = m_payloads.begin(); diff --git a/libtorrent/src/disk_io_thread.cpp b/libtorrent/src/disk_io_thread.cpp index b1b49880f..f091c3db8 100644 --- a/libtorrent/src/disk_io_thread.cpp +++ b/libtorrent/src/disk_io_thread.cpp @@ -420,7 +420,7 @@ namespace libtorrent l.unlock(); ret += p.storage->read_impl(buf.get(), p.piece, start_block * m_block_size, buffer_size); l.lock(); - if (!p.storage->error().empty()) { return -1; } + if (p.storage->error()) { return -1; } ++m_cache_stats.reads; } @@ -440,7 +440,7 @@ namespace libtorrent { l.unlock(); ret += p.storage->read_impl(p.blocks[i], p.piece, piece_offset, block_size); - if (!p.storage->error().empty()) { return -1; } + if (!p.storage->error()) { return -1; } l.lock(); ++m_cache_stats.reads; } @@ -735,6 +735,23 @@ namespace libtorrent #endif } + bool disk_io_thread::test_error(disk_io_job& j) + { + error_code const& ec = j.storage->error(); + if (ec) + { + j.str = ec.message(); + j.error = ec; + j.error_file = j.storage->error_file(); + j.storage->clear_error(); +#ifndef NDEBUG + std::cout << "ERROR: '" << j.str << "' " << j.error_file << std::endl; +#endif + return true; + } + return false; + } + void disk_io_thread::operator()() { for (;;) @@ -819,17 +836,10 @@ namespace libtorrent } case disk_io_job::read: { - std::string const& error_string = j.storage->error(); - if (!error_string.empty()) + if (test_error(j)) { -#ifndef NDEBUG - std::cout << "ERROR: '" << error_string << "' " << j.error_file << std::endl; -#endif - j.str = error_string; - j.error_file = j.storage->error_file(); - j.storage->clear_error(); ret = -1; - break; + return; } #ifdef TORRENT_DISK_STATS m_log << log_time() << " read " << j.buffer_size << std::endl; @@ -841,7 +851,8 @@ namespace libtorrent if (j.buffer == 0) { ret = -1; - j.str = "out of memory"; + j.error = error_code(ENOMEM, get_posix_category()); + j.str = j.error.message(); break; } @@ -853,9 +864,7 @@ namespace libtorrent if (ret == -1) { j.buffer = 0; - j.str = j.storage->error(); - j.error_file = j.storage->error_file(); - j.storage->clear_error(); + test_error(j); break; } else if (ret == -2) @@ -864,9 +873,7 @@ namespace libtorrent , j.buffer_size); if (ret < 0) { - j.str = j.storage->error(); - j.error_file = j.storage->error_file(); - j.storage->clear_error(); + test_error(j); break; } ++m_cache_stats.blocks_read; @@ -876,15 +883,8 @@ namespace libtorrent } case disk_io_job::write: { - std::string const& error_string = j.storage->error(); - if (!error_string.empty()) + if (test_error(j)) { -#ifndef NDEBUG - std::cout << "ERROR: '" << error_string << "' " << j.error_file << std::endl; -#endif - j.str = error_string; - j.error_file = j.storage->error_file(); - j.storage->clear_error(); ret = -1; break; } @@ -936,26 +936,18 @@ namespace libtorrent if (i != m_pieces.end()) { flush_and_remove(i, l); - std::string const& e = j.storage->error(); - if (!e.empty()) + if (test_error(j)) { - j.str = e; - j.error_file = j.storage->error_file(); ret = -1; - j.storage->clear_error(); j.storage->mark_failed(j.piece); break; } } l.unlock(); sha1_hash h = j.storage->hash_for_piece_impl(j.piece); - std::string const& e = j.storage->error(); - if (!e.empty()) + if (test_error(j)) { - j.str = e; - j.error_file = j.storage->error_file(); ret = -1; - j.storage->clear_error(); j.storage->mark_failed(j.piece); break; } @@ -972,9 +964,7 @@ namespace libtorrent ret = j.storage->move_storage_impl(j.str) ? 1 : 0; if (ret != 0) { - j.str = j.storage->error(); - j.error_file = j.storage->error_file(); - j.storage->clear_error(); + test_error(j); break; } j.str = j.storage->save_path().string(); @@ -1010,12 +1000,40 @@ namespace libtorrent } #endif ret = j.storage->release_files_impl(); - if (ret != 0) + if (ret != 0) test_error(j); + break; + } + case disk_io_job::clear_read_cache: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " clear-cache" << std::endl; +#endif + TORRENT_ASSERT(j.buffer == 0); + + mutex_t::scoped_lock l(m_piece_mutex); + INVARIANT_CHECK; + + for (cache_t::iterator i = m_read_pieces.begin(); + i != m_read_pieces.end();) { - j.str = j.storage->error(); - j.error_file = j.storage->error_file(); - j.storage->clear_error(); + if (i->storage == j.storage) + { + free_piece(*i, l); + i = m_read_pieces.erase(i); + } + else + { + ++i; + } } + l.unlock(); +#ifndef TORRENT_DISABLE_POOL_ALLOCATOR + { + mutex_t::scoped_lock l(m_pool_mutex); + m_pool.release_memory(); + } +#endif + ret = 0; break; } case disk_io_job::delete_files: @@ -1051,12 +1069,7 @@ namespace libtorrent } #endif ret = j.storage->delete_files_impl(); - if (ret != 0) - { - j.str = j.storage->error(); - j.error_file = j.storage->error_file(); - j.storage->clear_error(); - } + if (ret != 0) test_error(j); break; } case disk_io_job::check_fastresume: @@ -1090,6 +1103,13 @@ namespace libtorrent #endif if (ret != piece_manager::need_full_check) break; } + if (test_error(j)) + { + ret = piece_manager::fatal_disk_error; + break; + } + TORRENT_ASSERT(ret != -2 || !j.str.empty()); + // if the check is not done, add it at the end of the job queue if (ret == piece_manager::need_full_check) { @@ -1133,6 +1153,7 @@ namespace libtorrent #ifndef BOOST_NO_EXCEPTIONS try { #endif + TORRENT_ASSERT(ret != -2 || !j.str.empty()); if (handler) m_ios.post(bind(handler, ret, j)); #ifndef BOOST_NO_EXCEPTIONS } catch (std::exception&) diff --git a/libtorrent/src/error_code.cpp b/libtorrent/src/error_code.cpp new file mode 100644 index 000000000..cdb33eeec --- /dev/null +++ b/libtorrent/src/error_code.cpp @@ -0,0 +1,60 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include + +#if BOOST_VERSION >= 103500 + +#include "libtorrent/error_code.hpp" + +namespace libtorrent +{ + const char* libtorrent_error_category::name() const + { + return "libtorrent error"; + } + + std::string libtorrent_error_category::message(int ev) const + { + static char const* msgs[] = + { "no error", "torrent file collides with file from another torrent" }; + if (ev < 0 || ev >= sizeof(msgs)/sizeof(msgs[0])) + return "Unknown error"; + return msgs[ev]; + } + + libtorrent_error_category libtorrent_category; + +} + +#endif + diff --git a/libtorrent/src/file.cpp b/libtorrent/src/file.cpp index 451d892be..936e13ce4 100755 --- a/libtorrent/src/file.cpp +++ b/libtorrent/src/file.cpp @@ -31,27 +31,22 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "libtorrent/pch.hpp" +#include "libtorrent/config.hpp" #include -#ifdef _WIN32 +#ifdef TORRENT_WINDOWS // windows part #include "libtorrent/utf8.hpp" -#include -#include -#include -#include - -#ifndef _MODE_T_ -typedef int mode_t; -#endif +#include +#include #ifdef UNICODE #include "libtorrent/storage.hpp" #endif #else -// unix part +// posix part #define _FILE_OFFSET_BITS 64 #include #include @@ -88,18 +83,7 @@ BOOST_STATIC_ASSERT(sizeof(lseek(0, 0, 0)) >= 8); namespace { - enum { mode_in = 1, mode_out = 2 }; - - mode_t map_open_mode(int m) - { - if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM; - if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM; - if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM; - TORRENT_ASSERT(false); - return 0; - } - -#ifdef WIN32 +#ifdef TORRENT_WINDOWS std::string utf8_native(std::string const& s) { try @@ -121,9 +105,16 @@ namespace } } #else - std::string utf8_native(std::string const& s) + + enum { mode_in = 1, mode_out = 2 }; + + mode_t map_open_mode(int m) { - return s; + if (m == (mode_in | mode_out)) return O_RDWR | O_CREAT | O_BINARY | O_RANDOM; + if (m == mode_out) return O_WRONLY | O_CREAT | O_BINARY | O_RANDOM; + if (m == mode_in) return O_RDONLY | O_BINARY | O_RANDOM; + TORRENT_ASSERT(false); + return 0; } #endif @@ -133,239 +124,243 @@ namespace libtorrent { namespace fs = boost::filesystem; +#ifdef TORRENT_WINDOWS + const file::open_mode file::in(GENERIC_READ); + const file::open_mode file::out(GENERIC_WRITE); + const file::seek_mode file::begin(FILE_BEGIN); + const file::seek_mode file::end(FILE_END); +#else const file::open_mode file::in(mode_in); const file::open_mode file::out(mode_out); - - const file::seek_mode file::begin(1); - const file::seek_mode file::end(2); - - struct file::impl - { - impl() - : m_fd(-1) - , m_open_mode(0) - {} - - impl(fs::path const& path, int mode) - : m_fd(-1) - , m_open_mode(0) - { - open(path, mode); - } - - ~impl() - { - close(); - } - - bool open(fs::path const& path, int mode) - { - close(); -#if defined _WIN32 && defined UNICODE - std::wstring wpath(safe_convert(path.native_file_string())); - m_fd = ::_wopen( - wpath.c_str() - , map_open_mode(mode) - , S_IREAD | S_IWRITE); -#elif defined _WIN32 - m_fd = ::_open( - utf8_native(path.native_file_string()).c_str() - , map_open_mode(mode) - , S_IREAD | S_IWRITE); -#else - m_fd = ::open( - utf8_native(path.native_file_string()).c_str() - , map_open_mode(mode) - , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); -#endif - if (m_fd == -1) - { - std::stringstream msg; - msg << "open failed: '" << path.native_file_string() << "'. " - << std::strerror(errno); - if (!m_error) m_error.reset(new std::string); - *m_error = msg.str(); - return false; - } - m_open_mode = mode; - return true; - } - - void close() - { - if (m_fd == -1) return; - -#ifdef _WIN32 - ::_close(m_fd); -#else - ::close(m_fd); -#endif - m_fd = -1; - m_open_mode = 0; - } - - size_type read(char* buf, size_type num_bytes) - { - TORRENT_ASSERT(m_open_mode & mode_in); - TORRENT_ASSERT(m_fd != -1); - -#ifdef _WIN32 - size_type ret = ::_read(m_fd, buf, num_bytes); -#else - size_type ret = ::read(m_fd, buf, num_bytes); -#endif - if (ret == -1) - { - std::stringstream msg; - msg << "read failed: " << std::strerror(errno); - if (!m_error) m_error.reset(new std::string); - *m_error = msg.str(); - } - return ret; - } - - size_type write(const char* buf, size_type num_bytes) - { - TORRENT_ASSERT(m_open_mode & mode_out); - TORRENT_ASSERT(m_fd != -1); - - // TODO: Test this a bit more, what happens with random failures in - // the files? -// if ((rand() % 100) > 80) -// throw file_error("debug"); - -#ifdef _WIN32 - size_type ret = ::_write(m_fd, buf, num_bytes); -#else - size_type ret = ::write(m_fd, buf, num_bytes); -#endif - if (ret == -1) - { - std::stringstream msg; - msg << "write failed: " << std::strerror(errno); - if (!m_error) m_error.reset(new std::string); - *m_error = msg.str(); - } - return ret; - } - - bool set_size(size_type s) - { -#ifdef _WIN32 -#error file.cpp is for posix systems only. use file_win.cpp on windows -#else - if (ftruncate(m_fd, s) < 0) - { - std::stringstream msg; - msg << "ftruncate failed: '" << std::strerror(errno); - if (!m_error) m_error.reset(new std::string); - *m_error = msg.str(); - return false; - } - return true; -#endif - } - - size_type seek(size_type offset, int m = 1) - { - TORRENT_ASSERT(m_open_mode); - TORRENT_ASSERT(m_fd != -1); - - int seekdir = (m == 1)?SEEK_SET:SEEK_END; -#ifdef _WIN32 - size_type ret = _lseeki64(m_fd, offset, seekdir); -#else - size_type ret = lseek(m_fd, offset, seekdir); + const file::seek_mode file::begin(SEEK_SET); + const file::seek_mode file::end(SEEK_END); #endif - // For some strange reason this fails - // on win32. Use windows specific file - // wrapper instead. - if (ret == -1) - { - std::stringstream msg; - msg << "seek failed: '" << std::strerror(errno) - << "' fd: " << m_fd - << " offset: " << offset - << " seekdir: " << seekdir; - if (!m_error) m_error.reset(new std::string); - *m_error = msg.str(); - return -1; - } - return ret; - } - - size_type tell() - { - TORRENT_ASSERT(m_open_mode); - TORRENT_ASSERT(m_fd != -1); - -#ifdef _WIN32 - return _telli64(m_fd); + file::file() +#ifdef TORRENT_WINDOWS + : m_file_handle(INVALID_HANDLE_VALUE) #else - return lseek(m_fd, 0, SEEK_CUR); + : m_fd(-1) +#endif +#ifndef NDEBUG + , m_open_mode(0) #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 - - file::file() : m_impl(new impl()) {} - - file::file(fs::path const& p, file::open_mode m) - : m_impl(new impl(p, m.m_mask)) {} - file::~file() {} - - bool file::open(fs::path const& p, file::open_mode m) + file::file(fs::path const& path, open_mode mode, error_code& ec) +#ifdef TORRENT_WINDOWS + : m_file_handle(INVALID_HANDLE_VALUE) +#else + : m_fd(-1) +#endif +#ifndef NDEBUG + , m_open_mode(0) +#endif { - return m_impl->open(p, m.m_mask); + open(path, mode, ec); + } + + file::~file() + { + close(); + } + + bool file::open(fs::path const& path, open_mode mode, error_code& ec) + { + close(); +#ifdef TORRENT_WINDOWS + +#ifdef UNICODE + std::wstring file_path(safe_convert(path.native_file_string())); +#else + std::string file_path = utf8_native(path.native_file_string()); +#endif + + m_file_handle = CreateFile( + file_path.c_str() + , mode.m_mask + , FILE_SHARE_READ + , 0 + , (mode & out)?OPEN_ALWAYS:OPEN_EXISTING + , FILE_ATTRIBUTE_NORMAL + , 0); + + if (m_file_handle == INVALID_HANDLE_VALUE) + { + ec = error_code(GetLastError(), get_system_category()); + return false; + } + + // try to make the file sparse if supported + if (mode & out) + { + DWORD temp; + ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, 0, 0 + , 0, 0, &temp, 0); + } +#else + // rely on default umask to filter x and w permissions + // for group and others + m_fd = ::open(path.native_file_string().c_str() + , map_open_mode(mode.m_mask), S_IRWXU | S_IRWXG | S_IRWXO); + + if (m_fd == -1) + { + ec = error_code(errno, get_posix_category()); + return false; + } +#endif +#ifndef NDEBUG + m_open_mode = mode; +#endif + TORRENT_ASSERT(is_open()); + return true; + } + + bool file::is_open() const + { +#ifdef TORRENT_WINDOWS + return m_file_handle != INVALID_HANDLE_VALUE; +#else + return m_fd != -1; +#endif } void file::close() { - m_impl->close(); +#ifdef TORRENT_WINDOWS + if (m_file_handle == INVALID_HANDLE_VALUE) return; + CloseHandle(m_file_handle); + m_file_handle = INVALID_HANDLE_VALUE; +#else + if (m_fd == -1) return; + ::close(m_fd); + m_fd = -1; +#endif +#ifndef NDEBUG + m_open_mode = 0; +#endif } - size_type file::write(const char* buf, size_type num_bytes) + size_type file::read(char* buf, size_type num_bytes, error_code& ec) { - return m_impl->write(buf, num_bytes); + TORRENT_ASSERT((m_open_mode & in) == in); + TORRENT_ASSERT(buf); + TORRENT_ASSERT(num_bytes >= 0); + TORRENT_ASSERT(is_open()); + +#ifdef TORRENT_WINDOWS + + TORRENT_ASSERT(DWORD(num_bytes) == num_bytes); + DWORD ret = 0; + if (num_bytes != 0) + { + if (ReadFile(m_file_handle, buf, (DWORD)num_bytes, &ret, 0) == FALSE) + { + ec = error_code(GetLastError(), get_system_category()); + return -1; + } + } +#else + size_type ret = ::read(m_fd, buf, num_bytes); + if (ret == -1) ec = error_code(errno, get_posix_category()); +#endif + return ret; } - size_type file::read(char* buf, size_type num_bytes) + size_type file::write(const char* buf, size_type num_bytes, error_code& ec) { - return m_impl->read(buf, num_bytes); + TORRENT_ASSERT((m_open_mode & out) == out); + TORRENT_ASSERT(buf); + TORRENT_ASSERT(num_bytes >= 0); + TORRENT_ASSERT(is_open()); + +#ifdef TORRENT_WINDOWS + + DWORD ret = 0; + if (num_bytes != 0) + { + if (WriteFile(m_file_handle, buf, (DWORD)num_bytes, &ret, 0) == FALSE) + { + ec = error_code(GetLastError(), get_system_category()); + return -1; + } + } +#else + size_type ret = ::write(m_fd, buf, num_bytes); + if (ret == -1) ec = error_code(errno, get_posix_category()); +#endif + return ret; } - bool file::set_size(size_type s) + bool file::set_size(size_type s, error_code& ec) + { + TORRENT_ASSERT(is_open()); + TORRENT_ASSERT(s >= 0); + +#ifdef TORRENT_WINDOWS + size_type pos = tell(ec); + if (ec) return false; + seek(s, begin, ec); + if (ec) return false; + if (::SetEndOfFile(m_file_handle) == FALSE) + { + ec = error_code(GetLastError(), get_system_category()); + return false; + } +#else + if (ftruncate(m_fd, s) < 0) + { + ec = error_code(errno, get_posix_category()); + return false; + } +#endif + return true; + } + + size_type file::seek(size_type offset, seek_mode m, error_code& ec) { - return m_impl->set_size(s); + TORRENT_ASSERT(is_open()); + +#ifdef TORRENT_WINDOWS + LARGE_INTEGER offs; + offs.QuadPart = offset; + if (SetFilePointerEx(m_file_handle, offs, &offs, m.m_val) == FALSE) + { + ec = error_code(GetLastError(), get_system_category()); + return -1; + } + return offs.QuadPart; +#else + size_type ret = lseek(m_fd, offset, m.m_val); + if (ret < 0) ec = error_code(errno, get_posix_category()); + return ret; +#endif } - size_type file::seek(size_type pos, file::seek_mode m) + size_type file::tell(error_code& ec) { - return m_impl->seek(pos, m.m_val); - } + TORRENT_ASSERT(is_open()); - size_type file::tell() - { - return m_impl->tell(); - } +#ifdef TORRENT_WINDOWS + LARGE_INTEGER offs; + offs.QuadPart = 0; - std::string const& file::error() const - { - return m_impl->error(); - } + // is there any other way to get offset? + if (SetFilePointerEx(m_file_handle, offs, &offs + , FILE_CURRENT) == FALSE) + { + ec = error_code(GetLastError(), get_system_category()); + return -1; + } + return offs.QuadPart; +#else + size_type ret; + ret = lseek(m_fd, 0, SEEK_CUR); + if (ret < 0) ec = error_code(errno, get_posix_category()); + return ret; +#endif + } } + diff --git a/libtorrent/src/file_pool.cpp b/libtorrent/src/file_pool.cpp index 19bdb4162..865c071ff 100644 --- a/libtorrent/src/file_pool.cpp +++ b/libtorrent/src/file_pool.cpp @@ -30,9 +30,10 @@ POSSIBILITY OF SUCH DAMAGE. */ +#include #include "libtorrent/pch.hpp" - #include "libtorrent/file_pool.hpp" +#include "libtorrent/error_code.hpp" #include @@ -42,7 +43,7 @@ namespace libtorrent using boost::multi_index::get; boost::shared_ptr file_pool::open_file(void* st, fs::path const& p - , file::open_mode m, std::string& error) + , file::open_mode m, error_code& ec) { TORRENT_ASSERT(st != 0); TORRENT_ASSERT(p.is_complete()); @@ -60,8 +61,9 @@ namespace libtorrent { // this means that another instance of the storage // is using the exact same file. - error = "torrent uses the same file as another torrent " - "(" + p.string() + ")"; +#if BOOST_VERSION >= 103500 + ec = error_code(errors::file_collision, libtorrent_category); +#endif return boost::shared_ptr(); } @@ -73,12 +75,12 @@ namespace libtorrent i->file_ptr.reset(); TORRENT_ASSERT(e.file_ptr.unique()); e.file_ptr->close(); - if (!e.file_ptr->open(p, m)) + if (!e.file_ptr->open(p, m, ec)) { - error = e.file_ptr->error(); m_files.erase(i); return boost::shared_ptr(); } + TORRENT_ASSERT(e.file_ptr->is_open()); e.mode = m; } pt.replace(i, e); @@ -97,21 +99,19 @@ namespace libtorrent lt.erase(i); } lru_file_entry e; - e.file_ptr.reset(new file); + e.file_ptr.reset(new (std::nothrow)file); if (!e.file_ptr) { - error = "no memory"; + ec = error_code(ENOMEM, get_posix_category()); return e.file_ptr; } - if (!e.file_ptr->open(p, m)) - { - error = e.file_ptr->error(); + if (!e.file_ptr->open(p, m, ec)) return boost::shared_ptr(); - } e.mode = m; e.key = st; e.file_path = p; pt.insert(e); + TORRENT_ASSERT(e.file_ptr->is_open()); return e.file_ptr; } diff --git a/libtorrent/src/file_win.cpp b/libtorrent/src/file_win.cpp deleted file mode 100644 index fb4f0ce9a..000000000 --- a/libtorrent/src/file_win.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* - -Copyright (c) 2003, Magnus Jonsson & Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "libtorrent/file.hpp" -#include "libtorrent/utf8.hpp" -#include "libtorrent/assert.hpp" - -#ifdef UNICODE -#include "libtorrent/storage.hpp" -#endif - -#include -#include -#include -#include - -namespace -{ - // must be used to not leak memory in case something would throw - class auto_localfree - { - public: - auto_localfree(HLOCAL memory) - : m_memory(memory) - { - } - ~auto_localfree() - { - if (m_memory) - LocalFree(m_memory); - } - private: - HLOCAL m_memory; - }; - - std::string utf8_native(std::string const& s) - { - try - { - std::wstring ws; - libtorrent::utf8_wchar(s, ws); - std::size_t size = wcstombs(0, ws.c_str(), 0); - if (size == std::size_t(-1)) return s; - std::string ret; - ret.resize(size); - size = wcstombs(&ret[0], ws.c_str(), size + 1); - if (size == wchar_t(-1)) return s; - ret.resize(size); - return ret; - } - catch(std::exception) - { - return s; - } - } - } - -namespace libtorrent -{ - - struct file::impl : boost::noncopyable - { - enum open_flags - { - read_flag = 1, - write_flag = 2 - }; - - enum seek_mode - { - seek_begin = FILE_BEGIN, - seek_from_here = FILE_CURRENT, - seek_end = FILE_END - }; - - void set_error(const char* thrower) - { - DWORD err = GetLastError(); - -#ifdef UNICODE - wchar_t *wbuffer = 0; - FormatMessage( - FORMAT_MESSAGE_FROM_SYSTEM - |FORMAT_MESSAGE_ALLOCATE_BUFFER - , 0, err, 0, (LPWSTR)&wbuffer, 0, 0); - auto_localfree auto_free(wbuffer); - std::string tmp_utf8; - libtorrent::wchar_utf8(wbuffer, tmp_utf8); - char const* buffer = tmp_utf8.c_str(); -#else - char* buffer = 0; - FormatMessage( - FORMAT_MESSAGE_FROM_SYSTEM - |FORMAT_MESSAGE_ALLOCATE_BUFFER - , 0, err, 0, (LPSTR)&buffer, 0, 0); - auto_localfree auto_free(buffer); -#endif - - std::stringstream s; - s << (thrower ? thrower : "NULL") << ": " << (buffer ? buffer : "NULL"); - - if (!m_error) m_error.reset(new std::string); - *m_error = s.str(); - } - - impl() - { - m_file_handle = INVALID_HANDLE_VALUE; - } - - bool open(const char *file_name, open_flags flags) - { - TORRENT_ASSERT(file_name); - TORRENT_ASSERT(flags & (read_flag | write_flag)); - - DWORD access_mask = 0; - if (flags & read_flag) - access_mask |= GENERIC_READ; - if (flags & write_flag) - access_mask |= GENERIC_WRITE; - - TORRENT_ASSERT(access_mask & (GENERIC_READ | GENERIC_WRITE)); - - #ifdef UNICODE - std::wstring wfile_name(safe_convert(file_name)); - HANDLE new_handle = CreateFile( - wfile_name.c_str() - , access_mask - , FILE_SHARE_READ - , 0 - , (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING - , FILE_ATTRIBUTE_NORMAL - , 0); - #else - HANDLE new_handle = CreateFile( - utf8_native(file_name).c_str() - , access_mask - , FILE_SHARE_READ - , 0 - , (flags & write_flag)?OPEN_ALWAYS:OPEN_EXISTING - , FILE_ATTRIBUTE_NORMAL - , 0); - #endif - - if (new_handle == INVALID_HANDLE_VALUE) - { - set_error(file_name); - return false; - } - // try to make the file sparse if supported - if (access_mask & GENERIC_WRITE) - { - DWORD temp; - ::DeviceIoControl(new_handle, FSCTL_SET_SPARSE, 0, 0 - , 0, 0, &temp, 0); - } - // will only close old file if the open succeeded - close(); - m_file_handle = new_handle; - return true; - } - - void close() - { - if (m_file_handle != INVALID_HANDLE_VALUE) - { - CloseHandle(m_file_handle); - m_file_handle = INVALID_HANDLE_VALUE; - } - } - - ~impl() - { - close(); - } - - size_type write(const char* buffer, size_type num_bytes) - { - TORRENT_ASSERT(buffer); - TORRENT_ASSERT((DWORD)num_bytes == num_bytes); - DWORD bytes_written = 0; - if (num_bytes != 0) - { - if (FALSE == WriteFile( - m_file_handle - , buffer - , (DWORD)num_bytes - , &bytes_written - , 0)) - { - set_error("file::write"); - return -1; - } - } - return bytes_written; - } - - size_type read(char* buffer, size_type num_bytes) - { - TORRENT_ASSERT(buffer); - TORRENT_ASSERT(num_bytes >= 0); - TORRENT_ASSERT((DWORD)num_bytes == num_bytes); - - DWORD bytes_read = 0; - if (num_bytes != 0) - { - if (FALSE == ReadFile( - m_file_handle - , buffer - , (DWORD)num_bytes - , &bytes_read - , 0)) - { - set_error("file::set_size"); - return -1; - } - } - return bytes_read; - } - - bool set_size(size_type s) - { - size_type pos = tell(); - seek(s, seek_begin); - if (FALSE == ::SetEndOfFile(m_file_handle)) - { - set_error("file::set_size"); - return false; - } - return true; - } - - size_type seek(size_type pos, seek_mode from_where) - { - TORRENT_ASSERT(pos >= 0 || from_where != seek_begin); - TORRENT_ASSERT(pos <= 0 || from_where != seek_end); - LARGE_INTEGER offs; - offs.QuadPart = pos; - if (FALSE == SetFilePointerEx( - m_file_handle - , offs - , &offs - , from_where)) - { - set_error("file::seek"); - return -1; - } - return offs.QuadPart; - } - - size_type tell() - { - LARGE_INTEGER offs; - offs.QuadPart = 0; - - // is there any other way to get offset? - if (FALSE == SetFilePointerEx( - m_file_handle - , offs - , &offs - , FILE_CURRENT)) - { - set_error("file::tell"); - return -1; - } - - size_type pos = offs.QuadPart; - TORRENT_ASSERT(pos >= 0); - return pos; - } -/* - size_type size() - { - LARGE_INTEGER s; - if (FALSE == GetFileSizeEx(m_file_handle, &s)) - { - throw_exception("file::size"); - } - - size_type size = s.QuadPart; - TORRENT_ASSERT(size >= 0); - return size; - } -*/ - - std::string const& error() const - { - if (!m_error) m_error.reset(new std::string); - return *m_error; - } - - private: - - HANDLE m_file_handle; - mutable boost::scoped_ptr m_error; - - }; -} - -namespace libtorrent -{ - - const file::seek_mode file::begin(file::impl::seek_begin); - const file::seek_mode file::end(file::impl::seek_end); - - const file::open_mode file::in(file::impl::read_flag); - const file::open_mode file::out(file::impl::write_flag); - - file::file() - : m_impl(new libtorrent::file::impl()) - { - } - file::file(boost::filesystem::path const& p, open_mode m) - : m_impl(new libtorrent::file::impl()) - { - open(p,m); - } - - file::~file() - { - } - - bool file::open(boost::filesystem::path const& p, open_mode m) - { - TORRENT_ASSERT(p.is_complete()); - return m_impl->open(p.native_file_string().c_str(), impl::open_flags(m.m_mask)); - } - - void file::close() - { - m_impl->close(); - } - - size_type file::write(const char* buffer, size_type num_bytes) - { - return m_impl->write(buffer, num_bytes); - } - - size_type file::read(char* buffer, size_type num_bytes) - { - return m_impl->read(buffer, num_bytes); - } - - bool file::set_size(size_type s) - { - return m_impl->set_size(s); - } - - size_type file::seek(size_type pos, seek_mode m) - { - return m_impl->seek(pos,impl::seek_mode(m.m_val)); - } - - size_type file::tell() - { - return m_impl->tell(); - } - - std::string const& file::error() const - { - return m_impl->error(); - } -} diff --git a/libtorrent/src/lazy_bdecode.cpp b/libtorrent/src/lazy_bdecode.cpp index 5c1e37419..a6a6ad6d0 100644 --- a/libtorrent/src/lazy_bdecode.cpp +++ b/libtorrent/src/lazy_bdecode.cpp @@ -260,6 +260,13 @@ namespace libtorrent return e->string_value(); } + lazy_entry const* lazy_entry::dict_find_string(char const* name) const + { + lazy_entry const* e = dict_find(name); + if (e == 0 || e->type() != lazy_entry::string_t) return 0; + return e; + } + size_type lazy_entry::dict_find_int_value(char const* name, size_type default_val) const { lazy_entry const* e = dict_find(name); @@ -359,7 +366,7 @@ namespace libtorrent switch (e.type()) { case lazy_entry::none_t: return os << "none"; - case lazy_entry::int_t: return os << e.int_value(); + case lazy_entry::int_t: return os << std::dec << std::setw(0) << e.int_value(); case lazy_entry::string_t: { bool printable = true; @@ -374,7 +381,8 @@ namespace libtorrent os << "'"; if (printable) return os << e.string_value() << "'"; for (int i = 0; i < e.string_length(); ++i) - os << std::hex << int((unsigned char)(str[i])); + os << std::hex << std::setfill('0') << std::setw(2) + << int((unsigned char)(str[i])); return os << "'"; } case lazy_entry::list_t: diff --git a/libtorrent/src/peer_connection.cpp b/libtorrent/src/peer_connection.cpp index 8a387105e..95dd990b5 100755 --- a/libtorrent/src/peer_connection.cpp +++ b/libtorrent/src/peer_connection.cpp @@ -129,8 +129,10 @@ namespace libtorrent , m_request_large_blocks(false) , m_upload_only(false) , m_snubbed(false) + , m_bitfield_received(false) #ifndef NDEBUG , m_in_constructor(true) + , m_disconnect_started(false) #endif { m_channel_state[upload_channel] = peer_info::bw_idle; @@ -232,8 +234,10 @@ namespace libtorrent , m_request_large_blocks(false) , m_upload_only(false) , m_snubbed(false) + , m_bitfield_received(false) #ifndef NDEBUG , m_in_constructor(true) + , m_disconnect_started(false) #endif { m_channel_state[upload_channel] = peer_info::bw_idle; @@ -328,8 +332,6 @@ namespace libtorrent void peer_connection::update_interest() { - INVARIANT_CHECK; - boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); @@ -351,10 +353,8 @@ namespace libtorrent } try { - if (!interested) - send_not_interested(); - else - t->get_policy().peer_is_interesting(*this); + if (!interested) send_not_interested(); + else t->get_policy().peer_is_interesting(*this); } // may throw an asio error if socket has disconnected catch (std::exception&) {} @@ -455,15 +455,10 @@ namespace libtorrent #endif // if this is a web seed. we don't have a peer_info struct if (m_peer_info) m_peer_info->seed = true; - // if we're a seed too, disconnect - if (t->is_finished() && m_ses.settings().close_redundant_connections) - { - disconnect("seed to seed connection redundant"); - return; - } + t->peer_has_all(); - if (!t->is_finished()) - t->get_policy().peer_is_interesting(*this); + if (t->is_finished()) send_not_interested(); + else t->get_policy().peer_is_interesting(*this); return; } @@ -482,8 +477,12 @@ namespace libtorrent interesting = true; } } - if (interesting) - t->get_policy().peer_is_interesting(*this); + if (interesting) t->get_policy().peer_is_interesting(*this); + else send_not_interested(); + } + else + { + update_interest(); } } @@ -492,6 +491,7 @@ namespace libtorrent // INVARIANT_CHECK; TORRENT_ASSERT(!m_in_constructor); TORRENT_ASSERT(m_disconnecting); + TORRENT_ASSERT(m_disconnect_started); m_disk_recv_buffer_size = 0; @@ -535,16 +535,25 @@ namespace libtorrent m_suggested_pieces.begin(), m_suggested_pieces.end(), index); if (i != m_suggested_pieces.end()) m_suggested_pieces.erase(i); - // optimization, don't send have messages - // to peers that already have the piece - if (!m_ses.settings().send_redundant_have - && has_piece(index)) + if (has_piece(index)) { + // if we got a piece that this peer has + // it might have been the last interesting + // piece this peer had. We might not be + // interested anymore + update_interest(); + if (is_disconnecting()) return; + + // optimization, don't send have messages + // to peers that already have the piece + if (!m_ses.settings().send_redundant_have) + { #ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << time_now_string() - << " ==> HAVE [ piece: " << index << " ] SUPRESSED\n"; + (*m_logger) << time_now_string() + << " ==> HAVE [ piece: " << index << " ] SUPRESSED\n"; #endif - return; + return; + } } #ifdef TORRENT_VERBOSE_LOGGING @@ -561,8 +570,6 @@ namespace libtorrent bool peer_connection::has_piece(int i) const { - INVARIANT_CHECK; - boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); TORRENT_ASSERT(t->valid_metadata()); @@ -1055,6 +1062,10 @@ namespace libtorrent if (is_disconnecting()) return; + // if we haven't received a bitfield, it was + // probably omitted, which is the same as 'have_none' + if (!m_bitfield_received) incoming_have_none(); + #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << time_now_string() << " <== HAVE [ piece: " << index << "]\n"; @@ -1121,15 +1132,12 @@ namespace libtorrent } } - if (upload_only()) + if (is_seed()) { - TORRENT_ASSERT(m_peer_info); - if (is_seed()) m_peer_info->seed = true; - if (t->is_finished() && m_ses.settings().close_redundant_connections) - { - disconnect("seed to seed connection redundant"); - return; - } + m_peer_info->seed = true; + m_upload_only = true; + disconnect_if_redundant(); + if (is_disconnecting()) return; } } } @@ -1179,6 +1187,8 @@ namespace libtorrent return; } + m_bitfield_received = true; + // if we don't have metadata yet // just remember the bitmask // don't update the piecepicker @@ -1201,12 +1211,9 @@ namespace libtorrent #endif // if this is a web seed. we don't have a peer_info struct if (m_peer_info) m_peer_info->seed = true; - // if we're a seed too, disconnect - if (t->is_finished() && m_ses.settings().close_redundant_connections) - { - disconnect("seed to seed connection redundant, disconnecting"); - return; - } + m_upload_only = true; + disconnect_if_redundant(); + if (is_disconnecting()) return; m_have_piece.set_all(); m_num_pieces = num_pieces; @@ -1247,6 +1254,22 @@ namespace libtorrent else if (upload_only()) disconnect("upload to upload connections"); } + void peer_connection::disconnect_if_redundant() + { + if (!m_ses.settings().close_redundant_connections) return; + + boost::shared_ptr t = m_torrent.lock(); + TORRENT_ASSERT(t); + if (m_upload_only && t->is_finished()) + disconnect("seed to seed"); + + if (m_upload_only + && !m_interesting + && m_bitfield_received + && t->are_files_checked()) + disconnect("uninteresting upload-only peer"); + } + // ----------------------------- // ---------- REQUEST ---------- // ----------------------------- @@ -1258,6 +1281,10 @@ namespace libtorrent boost::shared_ptr t = m_torrent.lock(); TORRENT_ASSERT(t); + // if we haven't received a bitfield, it was + // probably omitted, which is the same as 'have_none' + if (!m_bitfield_received) incoming_have_none(); + #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) @@ -1460,6 +1487,10 @@ namespace libtorrent } #endif + // if we haven't received a bitfield, it was + // probably omitted, which is the same as 'have_none' + if (!m_bitfield_received) incoming_have_none(); + #ifndef TORRENT_DISABLE_EXTENSIONS for (extension_list_t::iterator i = m_extensions.begin() , end(m_extensions.end()); i != end; ++i) @@ -1797,6 +1828,12 @@ namespace libtorrent m_have_all = true; if (m_peer_info) m_peer_info->seed = true; + m_upload_only = true; + m_bitfield_received = true; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << " *** THIS IS A SEED ***\n"; +#endif // if we don't have metadata yet // just remember the bitmask @@ -1804,30 +1841,24 @@ namespace libtorrent // (since it doesn't exist yet) if (!t->ready_for_connections()) { + disconnect_if_redundant(); // TODO: this might need something more // so that once we have the metadata // we can construct a full bitfield return; } -#ifdef TORRENT_VERBOSE_LOGGING - (*m_logger) << " *** THIS IS A SEED ***\n"; -#endif - - // if we're a seed too, disconnect - if (t->is_finished() && m_ses.settings().close_redundant_connections) - { - disconnect("seed to seed connection redundant, disconnecting"); - return; - } - TORRENT_ASSERT(!m_have_piece.empty()); m_have_piece.set_all(); m_num_pieces = m_have_piece.size(); t->peer_has_all(); - if (!t->is_finished()) - t->get_policy().peer_is_interesting(*this); + + // if we're finished, we're not interested + if (t->is_finished()) send_not_interested(); + else t->get_policy().peer_is_interesting(*this); + + disconnect_if_redundant(); } // ----------------------------- @@ -1854,8 +1885,13 @@ namespace libtorrent #endif if (is_disconnecting()) return; if (m_peer_info) m_peer_info->seed = false; + m_bitfield_received = true; + + // we're never interested in a peer that doesn't have anything + send_not_interested(); TORRENT_ASSERT(!m_have_piece.empty() || !t->ready_for_connections()); + disconnect_if_redundant(); } // ----------------------------- @@ -2090,11 +2126,9 @@ namespace libtorrent void peer_connection::send_interested() { - INVARIANT_CHECK; - if (m_interesting) return; - write_interested(); m_interesting = true; + write_interested(); #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << time_now_string() << " ==> INTERESTED\n"; @@ -2103,17 +2137,16 @@ namespace libtorrent void peer_connection::send_not_interested() { - INVARIANT_CHECK; - if (!m_interesting) return; - write_not_interested(); m_interesting = false; + write_not_interested(); m_became_uninteresting = time_now(); #ifdef TORRENT_VERBOSE_LOGGING (*m_logger) << time_now_string() << " ==> NOT_INTERESTED\n"; #endif + disconnect_if_redundant(); } void peer_connection::send_block_requests() @@ -2238,7 +2271,6 @@ namespace libtorrent void peer_connection::timed_out() { TORRENT_ASSERT(m_connecting); - TORRENT_ASSERT(m_connection_ticket >= 0); #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING (*m_ses.m_logger) << time_now_string() << " CONNECTION TIMED OUT: " << m_remote.address().to_string() << "\n"; @@ -2253,6 +2285,10 @@ namespace libtorrent { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); +#ifndef NDEBUG + m_disconnect_started = true; +#endif + #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_ERROR_LOGGING switch (error) { @@ -2461,6 +2497,7 @@ namespace libtorrent p.flags |= is_seed() ? peer_info::seed : 0; p.flags |= m_snubbed ? peer_info::snubbed : 0; + p.flags |= m_upload_only ? peer_info::upload_only : 0; if (peer_info_struct()) { policy::peer* pi = peer_info_struct(); @@ -2979,8 +3016,6 @@ namespace libtorrent { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); - INVARIANT_CHECK; - if (m_channel_state[upload_channel] != peer_info::bw_idle) return; shared_ptr t = m_torrent.lock(); @@ -3388,8 +3423,6 @@ namespace libtorrent bool peer_connection::can_write() const { - INVARIANT_CHECK; - // if we have requests or pending data to be sent or announcements to be made // we want to send data return !m_send_buffer.empty() @@ -3400,8 +3433,6 @@ namespace libtorrent bool peer_connection::can_read() const { - INVARIANT_CHECK; - bool ret = (m_bandwidth_limit[download_channel].quota_left() > 0 || m_ignore_bandwidth_limits) && !m_connecting @@ -3585,6 +3616,7 @@ namespace libtorrent if (m_disconnecting) { TORRENT_ASSERT(!t); + TORRENT_ASSERT(m_disconnect_started); } else if (!m_in_constructor) { @@ -3634,6 +3666,23 @@ namespace libtorrent return; } + if (m_ses.settings().close_redundant_connections) + { + // make sure upload only peers are disconnected + if (t->is_finished() && m_upload_only) + TORRENT_ASSERT(m_disconnect_started); + if (m_upload_only + && !m_interesting + && m_bitfield_received + && t->are_files_checked()) + TORRENT_ASSERT(m_disconnect_started); + } + + if (t->is_finished()) + TORRENT_ASSERT(!m_interesting); + if (is_seed()) + TORRENT_ASSERT(m_upload_only); + if (t->has_picker()) { std::map num_requests; diff --git a/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index a16bdc5c1..cca630d01 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/src/session_impl.cpp @@ -1088,17 +1088,20 @@ namespace aux { // -------------------------------------------------------------- // scrape paused torrents that are auto managed // -------------------------------------------------------------- - --m_auto_scrape_time_scaler; - if (m_auto_scrape_time_scaler <= 0) + if (!is_paused()) { - m_auto_scrape_time_scaler = m_settings.auto_scrape_interval - / (std::max)(1, num_paused_auto_managed); - if (m_auto_scrape_time_scaler < m_settings.auto_scrape_min_interval) - m_auto_scrape_time_scaler = m_settings.auto_scrape_min_interval; - - if (least_recently_scraped != m_torrents.end()) + --m_auto_scrape_time_scaler; + if (m_auto_scrape_time_scaler <= 0) { - least_recently_scraped->second->scrape_tracker(); + m_auto_scrape_time_scaler = m_settings.auto_scrape_interval + / (std::max)(1, num_paused_auto_managed); + if (m_auto_scrape_time_scaler < m_settings.auto_scrape_min_interval) + m_auto_scrape_time_scaler = m_settings.auto_scrape_min_interval; + + if (least_recently_scraped != m_torrents.end()) + { + least_recently_scraped->second->scrape_tracker(); + } } } diff --git a/libtorrent/src/storage.cpp b/libtorrent/src/storage.cpp index d6ecd4e71..dffea8267 100755 --- a/libtorrent/src/storage.cpp +++ b/libtorrent/src/storage.cpp @@ -253,20 +253,20 @@ namespace namespace libtorrent { template - void recursive_copy(Path const& old_path, Path const& new_path, std::string& error) + void recursive_copy(Path const& old_path, Path const& new_path, error_code& ec) { using boost::filesystem::basic_directory_iterator; #ifndef BOOST_NO_EXCEPTIONS try { #endif - TORRENT_ASSERT(error.empty()); + TORRENT_ASSERT(!ec); if (is_directory(old_path)) { create_directory(new_path); for (basic_directory_iterator i(old_path), end; i != end; ++i) { - recursive_copy(i->path(), new_path / i->leaf(), error); - if (!error.empty()) return; + recursive_copy(i->path(), new_path / i->leaf(), ec); + if (ec) return; } } else @@ -274,7 +274,7 @@ namespace libtorrent copy_file(old_path, new_path); } #ifndef BOOST_NO_EXCEPTIONS - } catch (std::exception& e) { error = e.what(); } + } catch (std::exception& e) { ec = error_code(errno, get_posix_category()); } #endif } @@ -436,6 +436,7 @@ namespace libtorrent boost::scoped_ptr m_mapped_files; file_storage const& m_files; + std::vector m_file_priority; fs::path m_save_path; // the file pool is typically stored in // the session, to make all storage @@ -478,6 +479,7 @@ namespace libtorrent bool storage::initialize(bool allocate_files) { + error_code ec; // first, create all missing directories fs::path last_path; for (file_storage::iterator file_iter = files().begin(), @@ -508,42 +510,44 @@ namespace libtorrent // the directory exists. if (file_iter->size == 0) { -#ifndef BOOST_NO_EXCEPTIONS - try { -#endif - file(m_save_path / file_iter->path, file::out); -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) + file(m_save_path / file_iter->path, file::out, ec); + if (ec) { - set_error((m_save_path / file_iter->path).string(), e.what()); + set_error(m_save_path / file_iter->path, ec); return true; } -#endif continue; } #ifndef BOOST_NO_EXCEPTIONS try { #endif - if (allocate_files) + // don't allocate files with priority 0 + int file_index = file_iter - files().begin(); + if (allocate_files && (m_file_priority.size() <= file_index + || m_file_priority[file_index] > 0)) { - std::string error; + error_code ec; boost::shared_ptr f = m_pool.open_file(this - , m_save_path / file_iter->path, file::in | file::out - , error); - if (f && f->error().empty()) - f->set_size(file_iter->size); + , m_save_path / file_iter->path, file::in | file::out, ec); + if (ec) set_error(m_save_path / file_iter->path, ec); + else if (f) + { + f->set_size(file_iter->size, ec); + if (ec) set_error(m_save_path / file_iter->path, ec); + } } #ifndef BOOST_NO_EXCEPTIONS } catch (std::exception& e) { - set_error((m_save_path / file_iter->path).string(), e.what()); + set_error(m_save_path / file_iter->path + , error_code(errno, get_posix_category())); return true; } #endif } + std::vector().swap(m_file_priority); // close files that were opened in write mode m_pool.release(this); return false; @@ -568,6 +572,15 @@ namespace libtorrent { #endif rename(old_path, new_path); +/* + error_code ec; + rename(old_path, new_path, ec); + if (ec) + { + set_error(old_path, ec); + return; + } +*/ if (!m_mapped_files) { m_mapped_files.reset(new file_storage(m_files)); } m_mapped_files->rename_file(index, new_filename); @@ -575,7 +588,7 @@ namespace libtorrent } catch (std::exception& e) { - set_error(old_name.string(), e.what()); + set_error(old_name, error_code(errno, get_posix_category())); return true; } #endif @@ -595,8 +608,7 @@ namespace libtorrent m_pool.release(this); buffer().swap(m_scratch_buffer); - int result = 0; - std::string error; + int error = 0; std::string error_file; // delete the files from disk @@ -619,16 +631,14 @@ namespace libtorrent { fs::remove(safe_convert(p)); } catch (std::exception& e) { - error = e.what(); + error = errno; error_file = p; - result = 1; } #else if (std::remove(p.c_str()) != 0 && errno != ENOENT) { - error = std::strerror(errno); + error = errno; error_file = p; - result = errno; } #endif } @@ -644,26 +654,25 @@ namespace libtorrent { fs::remove(safe_convert(*i)); } catch (std::exception& e) { - error = e.what(); + error = errno; error_file = *i; - result = 1; } #else if (std::remove(i->c_str()) != 0 && errno != ENOENT) { - error = std::strerror(errno); + error = errno; error_file = *i; - result = errno; } #endif } - if (!error.empty()) + if (error) { - m_error.swap(error); + m_error = error_code(error, get_posix_category()); m_error_file.swap(error_file); + return true; } - return result != 0; + return false; } bool storage::write_resume_data(entry& rd) const @@ -682,15 +691,38 @@ namespace libtorrent p.push_back(entry(i->second)); fl.push_back(entry(p)); } + + if (m_mapped_files) + { + entry::list_type& fl = rd["mapped_files"].list(); + for (file_storage::iterator i = m_mapped_files->begin() + , end(m_mapped_files->end()); i != end; ++i) + { + fl.push_back(i->path.string()); + } + } + return false; } bool storage::verify_resume_data(lazy_entry const& rd, std::string& error) { - if (rd.type() != lazy_entry::dict_t) + lazy_entry const* file_priority = rd.dict_find_list("file_priority"); + if (file_priority && file_priority->list_size() + == files().num_files()) { - error = "invalid fastresume file (not a dictionary)"; - return true; + m_file_priority.resize(file_priority->list_size()); + for (int i = 0; i < file_priority->list_size(); ++i) + m_file_priority[i] = file_priority->list_int_value_at(i, 1); + } + + lazy_entry const* mapped_files = rd.dict_find_list("mapped_files"); + if (mapped_files && mapped_files->list_size() == m_files.num_files()) + { + if (!m_mapped_files) + { m_mapped_files.reset(new file_storage(m_files)); } + for (int i = 0; i < m_files.num_files(); ++i) + m_mapped_files->rename_file(i, mapped_files->list_string_value_at(i)); } std::vector > file_sizes; @@ -718,30 +750,46 @@ namespace libtorrent error = "the number of files in resume data is 0"; return false; } - - lazy_entry const* slots = rd.dict_find_list("slots"); - if (slots == 0) - { - error = "missing or invalid 'slots' entry in resume data"; - return false; - } bool seed = false; - if (int(slots->list_size()) == m_files.num_pieces()) + lazy_entry const* slots = rd.dict_find_list("slots"); + if (slots) { - bool seed = true; - for (int i = 0; i < slots->list_size(); ++i) + if (int(slots->list_size()) == m_files.num_pieces()) { - lazy_entry const* e = slots->list_at(i); - if (e->list_int_value_at(i, -1) >= 0) continue; - seed = false; - break; + seed = true; + for (int i = 0; i < slots->list_size(); ++i) + { + lazy_entry const* e = slots->list_at(i); + if (e->list_int_value_at(i, -1) >= 0) continue; + seed = false; + break; + } } } + else if (lazy_entry const* pieces = rd.dict_find_string("pieces")) + { + if (int(pieces->string_length()) == m_files.num_pieces()) + { + seed = true; + char const* p = pieces->string_ptr(); + for (int i = 0; i < pieces->string_length(); ++i) + { + if ((p[i] & 1) == 1) continue; + seed = false; + break; + } + } + } + else + { + error = "missing 'slots' and 'pieces' entry in resume data"; + return false; + } bool full_allocation_mode = false; - if (rd.dict_find_string_value("allocation") == "full") + if (rd.dict_find_string_value("allocation") != "compact") full_allocation_mode = true; if (seed) @@ -832,11 +880,11 @@ namespace libtorrent } catch (std::exception& e) { - std::string err; - recursive_copy(old_path, new_path, err); - if (!err.empty()) + error_code ec; + recursive_copy(old_path, new_path, ec); + if (ec) { - set_error((m_save_path / files().name()).string(), e.what()); + set_error(m_save_path / files().name(), ec); return true; } m_save_path = save_path; @@ -964,30 +1012,24 @@ namespace libtorrent } int buf_pos = 0; - std::string error; + error_code ec; boost::shared_ptr in(m_pool.open_file( - this, m_save_path / file_iter->path, file::in - , error)); - if (!in) + this, m_save_path / file_iter->path, file::in, ec)); + if (!in || ec) { - set_error((m_save_path / file_iter->path).string(), error); - return -1; - } - if (!in->error().empty()) - { - set_error((m_save_path / file_iter->path).string(), in->error()); + set_error(m_save_path / file_iter->path, ec); return -1; } TORRENT_ASSERT(file_offset < file_iter->size); TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base); - size_type new_pos = in->seek(file_offset + file_iter->file_base); - if (new_pos != file_offset + file_iter->file_base) + size_type new_pos = in->seek(file_offset + file_iter->file_base, file::begin, ec); + if (new_pos != file_offset + file_iter->file_base || ec) { // the file was not big enough if (!fill_zero) { - set_error((m_save_path / file_iter->path).string(), "seek failed"); + set_error(m_save_path / file_iter->path, ec); return -1; } std::memset(buf + buf_pos, 0, size - buf_pos); @@ -995,8 +1037,8 @@ namespace libtorrent } #ifndef NDEBUG - size_type in_tell = in->tell(); - TORRENT_ASSERT(in_tell == file_offset + file_iter->file_base); + size_type in_tell = in->tell(ec); + TORRENT_ASSERT(in_tell == file_offset + file_iter->file_base && !ec); #endif int left_to_read = size; @@ -1029,15 +1071,15 @@ namespace libtorrent == file_iter->path); #endif - int actual_read = int(in->read(buf + buf_pos, read_bytes)); + int actual_read = int(in->read(buf + buf_pos, read_bytes, ec)); - if (read_bytes != actual_read) + if (read_bytes != actual_read || ec) { // the file was not big enough if (actual_read > 0) buf_pos += actual_read; if (!fill_zero) { - set_error((m_save_path / file_iter->path).string(), "read failed"); + set_error(m_save_path / file_iter->path, ec); return -1; } std::memset(buf + buf_pos, 0, size - buf_pos); @@ -1061,25 +1103,19 @@ namespace libtorrent fs::path path = m_save_path / file_iter->path; file_offset = 0; - std::string error; - in = m_pool.open_file( - this, path, file::in, error); - if (!in) + error_code ec; + in = m_pool.open_file( this, path, file::in, ec); + if (!in || ec) { - set_error(path.string(), error); + set_error(path, ec); return -1; } - if (!in->error().empty()) - { - set_error((m_save_path / file_iter->path).string(), in->error()); - return -1; - } - size_type pos = in->seek(file_iter->file_base); - if (pos != file_iter->file_base) + size_type pos = in->seek(file_iter->file_base, file::begin, ec); + if (pos != file_iter->file_base || ec) { if (!fill_zero) { - set_error((m_save_path / file_iter->path).string(), "seek failed"); + set_error(m_save_path / file_iter->path, ec); return -1; } std::memset(buf + buf_pos, 0, size - buf_pos); @@ -1125,28 +1161,23 @@ namespace libtorrent } fs::path p(m_save_path / file_iter->path); - std::string error; + error_code ec; boost::shared_ptr out = m_pool.open_file( - this, p, file::out | file::in, error); + this, p, file::out | file::in, ec); - if (!out) + if (!out || ec) { - set_error(p.string(), error); - return -1; - } - if (!out->error().empty()) - { - set_error(p.string(), out->error()); + set_error(p, ec); return -1; } TORRENT_ASSERT(file_offset < file_iter->size); TORRENT_ASSERT(slices[0].offset == file_offset + file_iter->file_base); - size_type pos = out->seek(file_offset + file_iter->file_base); + size_type pos = out->seek(file_offset + file_iter->file_base, file::begin, ec); - if (pos != file_offset + file_iter->file_base) + if (pos != file_offset + file_iter->file_base || ec) { - set_error((m_save_path / file_iter->path).string(), "seek failed"); + set_error(p, ec); return -1; } @@ -1180,11 +1211,12 @@ namespace libtorrent TORRENT_ASSERT(buf_pos >= 0); TORRENT_ASSERT(write_bytes >= 0); - size_type written = out->write(buf + buf_pos, write_bytes); + error_code ec; + size_type written = out->write(buf + buf_pos, write_bytes, ec); - if (written != write_bytes) + if (written != write_bytes || ec) { - set_error((m_save_path / file_iter->path).string(), "write failed"); + set_error(m_save_path / file_iter->path, ec); return -1; } @@ -1205,26 +1237,21 @@ namespace libtorrent TORRENT_ASSERT(file_iter != files().end()); fs::path p = m_save_path / file_iter->path; file_offset = 0; - std::string error; + error_code ec; out = m_pool.open_file( - this, p, file::out | file::in, error); + this, p, file::out | file::in, ec); - if (!out) + if (!out || ec) { - set_error(p.string(), error); - return -1; - } - if (!out->error().empty()) - { - set_error(p.string(), out->error()); + set_error(p, ec); return -1; } - size_type pos = out->seek(file_iter->file_base); + size_type pos = out->seek(file_iter->file_base, file::begin, ec); - if (pos != file_iter->file_base) + if (pos != file_iter->file_base || ec) { - set_error((m_save_path / file_iter->path).string(), "seek failed"); + set_error(p, ec); return -1; } } @@ -1276,6 +1303,15 @@ namespace libtorrent m_io_thread.add_job(j, handler); } + void piece_manager::async_clear_read_cache( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::clear_read_cache; + m_io_thread.add_job(j, handler); + } + void piece_manager::async_release_files( boost::function const& handler) { @@ -1723,6 +1759,7 @@ namespace libtorrent error = f.string(); error += ": "; error += e.what(); + TORRENT_ASSERT(!error.empty()); return fatal_disk_error; } #endif @@ -1738,6 +1775,7 @@ namespace libtorrent m_unallocated_slots.clear(); m_free_slots.clear(); } + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); return need_full_check; } } @@ -1762,8 +1800,8 @@ namespace libtorrent { if (m_storage->initialize(m_storage_mode == storage_mode_allocate)) { - error = m_storage->error(); - m_storage->clear_error(); + error = m_storage->error().message(); + TORRENT_ASSERT(!error.empty()); return fatal_disk_error; } m_state = state_finished; @@ -1817,6 +1855,9 @@ namespace libtorrent if (rd.dict_find_string_value("allocation") != "compact") storage_mode = storage_mode_sparse; + if (!m_storage->verify_resume_data(rd, error)) + return check_no_fastresume(error); + // assume no piece is out of place (i.e. in a slot // other than the one it should be in) bool out_of_place = false; @@ -1902,9 +1943,6 @@ namespace libtorrent } } - if (!m_storage->verify_resume_data(rd, error)) - return check_no_fastresume(error); - // This will corrupt the storage // use while debugging to find // states that cannot be scanned @@ -1927,6 +1965,7 @@ namespace libtorrent m_state = state_expand_pieces; m_current_slot = 0; error = "pieces needs to be reordered"; + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); return need_full_check; } } @@ -2021,8 +2060,8 @@ namespace libtorrent if (m_storage->read(&m_scratch_buffer2[0], piece, 0, piece_size) != piece_size) { - error = m_storage->error(); - m_storage->clear_error(); + error = m_storage->error().message(); + TORRENT_ASSERT(!error.empty()); return fatal_disk_error; } m_scratch_piece = other_piece; @@ -2034,8 +2073,8 @@ namespace libtorrent int piece_size = m_files.piece_size(piece); if (m_storage->write(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size) { - error = m_storage->error(); - m_storage->clear_error(); + error = m_storage->error().message(); + TORRENT_ASSERT(!error.empty()); return fatal_disk_error; } m_piece_to_slot[piece] = piece; @@ -2044,6 +2083,7 @@ namespace libtorrent if (other_piece >= 0) m_scratch_buffer.swap(m_scratch_buffer2); + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); return need_full_check; } @@ -2075,8 +2115,8 @@ namespace libtorrent int piece_size = m_files.piece_size(other_piece); if (m_storage->read(&m_scratch_buffer[0], piece, 0, piece_size) != piece_size) { - error = m_storage->error(); - m_storage->clear_error(); + error = m_storage->error().message(); + TORRENT_ASSERT(!error.empty()); return fatal_disk_error; } m_scratch_piece = other_piece; @@ -2090,14 +2130,22 @@ namespace libtorrent m_slot_to_piece[m_current_slot] = unassigned; m_slot_to_piece[piece] = piece; + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); return need_full_check; } TORRENT_ASSERT(m_state == state_full_check); - bool skip = check_one_piece(have_piece); + int skip = check_one_piece(have_piece); TORRENT_ASSERT(m_current_slot <= m_files.num_pieces()); + if (skip == -1) + { + error = m_storage->error().message(); + TORRENT_ASSERT(!error.empty()); + return fatal_disk_error; + } + if (skip) { clear_error(); @@ -2162,6 +2210,7 @@ namespace libtorrent m_state = state_expand_pieces; m_current_slot = 0; current_slot = m_current_slot; + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); return need_full_check; } } @@ -2171,10 +2220,13 @@ namespace libtorrent } return check_init_storage(error); } + + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); return need_full_check; } - bool piece_manager::check_one_piece(int& have_piece) + // -1=error 0=ok 1=skip + int piece_manager::check_one_piece(int& have_piece) { // ------------------------ // DO THE FULL CHECK @@ -2196,9 +2248,20 @@ namespace libtorrent int num_read = m_storage->read(&m_piece_data[0] , m_current_slot, 0, piece_size); + if (num_read < 0) + { + if (m_storage->error() + && m_storage->error() != error_code(ENOENT, get_posix_category())) + { + std::cerr << m_storage->error().message() << std::endl; + return -1; + } + return 1; + } + // if the file is incomplete, skip the rest of it if (num_read != piece_size) - return true; + return 1; int piece_index = identify_data(m_piece_data, m_current_slot); @@ -2271,7 +2334,7 @@ namespace libtorrent else ret |= m_storage->move_slot(m_current_slot, other_slot); - if (ret) return true; + if (ret) return 1; TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); @@ -2304,7 +2367,7 @@ namespace libtorrent ret |= m_storage->move_slot(other_slot, m_current_slot); } - if (ret) return true; + if (ret) return 1; TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); @@ -2388,7 +2451,7 @@ namespace libtorrent ret |= m_storage->move_slot(slot2, m_current_slot); } - if (ret) return true; + if (ret) return 1; TORRENT_ASSERT(m_slot_to_piece[m_current_slot] == unassigned || m_piece_to_slot[m_slot_to_piece[m_current_slot]] == m_current_slot); @@ -2411,7 +2474,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); } - return false; + return 0; } void piece_manager::switch_to_full_mode() diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index d062f1720..d2e1b8064 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/src/torrent.cpp @@ -424,6 +424,9 @@ namespace libtorrent TORRENT_ASSERT(m_torrent_file->num_files() > 0); TORRENT_ASSERT(m_torrent_file->total_size() >= 0); + m_file_priority.clear(); + m_file_priority.resize(m_torrent_file->num_files(), 1); + m_block_size = (std::min)(m_block_size, m_torrent_file->piece_length()); if (m_torrent_file->num_pieces() @@ -1344,8 +1347,12 @@ namespace libtorrent std::copy(downloaders.begin(), downloaders.end(), std::inserter(peers, peers.begin())); m_picker->we_have(index); - for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) - (*i)->announce_piece(index); + for (peer_iterator i = m_connections.begin(); i != m_connections.end();) + { + peer_connection* p = *i; + ++i; + p->announce_piece(index); + } for (std::set::iterator i = peers.begin() , end(peers.end()); i != end; ++i) @@ -1767,17 +1774,49 @@ namespace libtorrent // in the torrent TORRENT_ASSERT(int(files.size()) == m_torrent_file->num_files()); - size_type position = 0; - if (m_torrent_file->num_pieces() == 0) return; + std::copy(files.begin(), files.end(), m_file_priority.begin()); + update_piece_priorities(); + } + + void torrent::set_file_priority(int index, int prio) + { + INVARIANT_CHECK; + TORRENT_ASSERT(index < m_torrent_file->num_files()); + TORRENT_ASSERT(index >= 0); + if (m_file_priority[index] == prio) return; + m_file_priority[index] = prio; + update_piece_priorities(); + } + + int torrent::file_priority(int index) const + { + TORRENT_ASSERT(index < m_torrent_file->num_files()); + TORRENT_ASSERT(index >= 0); + return m_file_priority[index]; + } + + void torrent::file_priorities(std::vector& files) const + { + INVARIANT_CHECK; + files.resize(m_file_priority.size()); + std::copy(m_file_priority.begin(), m_file_priority.end(), files.begin()); + } + + void torrent::update_piece_priorities() + { + INVARIANT_CHECK; + + if (m_torrent_file->num_pieces() == 0) return; bool was_finished = is_finished(); + size_type position = 0; int piece_length = m_torrent_file->piece_length(); // initialize the piece priorities to 0, then only allow // setting higher priorities std::vector pieces(m_torrent_file->num_pieces(), 0); - for (int i = 0; i < int(files.size()); ++i) + for (int i = 0; i < int(m_file_priority.size()); ++i) { size_type start = position; size_type size = m_torrent_file->files().at(i).size; @@ -1788,12 +1827,12 @@ namespace libtorrent // already set (to avoid problems with overlapping pieces) int start_piece = int(start / piece_length); int last_piece = int((position - 1) / piece_length); - TORRENT_ASSERT(last_piece <= int(pieces.size())); + TORRENT_ASSERT(last_piece < int(pieces.size())); // if one piece spans several files, we might // come here several times with the same start_piece, end_piece std::for_each(pieces.begin() + start_piece , pieces.begin() + last_piece + 1 - , bind(&set_if_greater, _1, files[i])); + , bind(&set_if_greater, _1, m_file_priority[i])); } prioritize_pieces(pieces); update_peer_interest(was_finished); @@ -1989,7 +2028,6 @@ namespace libtorrent { if (m_picker.get()) { - TORRENT_ASSERT(!is_seed()); m_picker->dec_refcount_all(); } } @@ -2434,6 +2472,30 @@ namespace libtorrent m_seeding_time = seconds(rd.dict_find_int_value("seeding_time")); m_complete = rd.dict_find_int_value("num_seeds", -1); m_incomplete = rd.dict_find_int_value("num_downloaders", -1); + set_upload_limit(rd.dict_find_int_value("upload_rate_limit", -1)); + set_download_limit(rd.dict_find_int_value("download_rate_limit", -1)); + set_max_connections(rd.dict_find_int_value("max_connections", -1)); + set_max_uploads(rd.dict_find_int_value("max_uploads", -1)); + + lazy_entry const* file_priority = rd.dict_find_list("file_priority"); + if (file_priority && file_priority->list_size() + == m_torrent_file->num_files()) + { + for (int i = 0; i < file_priority->list_size(); ++i) + m_file_priority[i] = file_priority->list_int_value_at(i, 1); + update_piece_priorities(); + } + lazy_entry const* piece_priority = rd.dict_find_string("piece_priority"); + if (piece_priority && piece_priority->string_length() + == m_torrent_file->num_pieces()) + { + char const* p = piece_priority->string_ptr(); + for (int i = 0; i < piece_priority->string_length(); ++i) + m_picker->set_piece_priority(i, p[i]); + } + + if (rd.dict_find_int_value("auto_managed")) auto_managed(true); + if (rd.dict_find_int_value("paused")) pause(); } void torrent::write_resume_data(entry& ret) const @@ -2512,8 +2574,7 @@ namespace libtorrent pieces.resize(m_torrent_file->num_pieces()); if (is_seed()) { - for (int i = 0, end(pieces.size()); i < end; ++i) - pieces[i] = 1; + std::memset(&pieces[0], 1, pieces.size()); } else { @@ -2559,6 +2620,33 @@ namespace libtorrent peer["port"] = i->second.port; peer_list.push_back(peer); } + + ret["upload_rate_limit"] = upload_limit(); + ret["download_rate_limit"] = download_limit(); + ret["max_connections"] = max_connections(); + ret["max_uploads"] = max_uploads(); + ret["paused"] = m_paused; + ret["auto_managed"] = m_auto_managed; + + // write piece priorities + entry::string_type& piece_priority = ret["piece_priority"].string(); + piece_priority.resize(m_torrent_file->num_pieces()); + if (is_seed()) + { + std::memset(&piece_priority[0], 1, pieces.size()); + } + else + { + for (int i = 0, end(piece_priority.size()); i < end; ++i) + piece_priority[i] = m_picker->piece_priority(i); + } + + // write file priorities + entry::list_type& file_priority = ret["file_priority"].list(); + file_priority.clear(); + for (int i = 0, end(m_file_priority.size()); i < end; ++i) + file_priority.push_back(m_file_priority[i]); + } void torrent::get_full_peer_list(std::vector& v) const @@ -3108,8 +3196,6 @@ namespace libtorrent // called when torrent is complete (all pieces downloaded) void torrent::completed() { - INVARIANT_CHECK; - m_picker.reset(); set_state(torrent_status::seeding); @@ -3224,24 +3310,13 @@ namespace libtorrent m_connections_initialized = true; // all peer connections have to initialize themselves now that the metadata // is available - for (torrent::peer_iterator i = m_connections.begin() - , end(m_connections.end()); i != end;) + for (torrent::peer_iterator i = m_connections.begin(); + i != m_connections.end();) { - boost::intrusive_ptr pc = *i; + peer_connection* pc = *i; ++i; -#ifndef BOOST_NO_EXCEPTIONS - try - { -#endif - pc->on_metadata(); - pc->init(); -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - pc->disconnect(e.what()); - } -#endif + pc->on_metadata(); + pc->init(); } } @@ -3763,6 +3838,7 @@ namespace libtorrent TORRENT_ASSERT(m_storage); m_storage->async_release_files( bind(&torrent::on_torrent_paused, shared_from_this(), _1, _2)); + m_storage->async_clear_read_cache(); } else { @@ -4270,7 +4346,7 @@ namespace libtorrent st.next_announce = boost::posix_time::seconds( total_seconds(next_announce() - now)); - if (st.next_announce.is_negative()) + if (st.next_announce.is_negative() || is_paused()) st.next_announce = boost::posix_time::seconds(0); st.announce_interval = boost::posix_time::seconds(m_duration); diff --git a/libtorrent/src/torrent_handle.cpp b/libtorrent/src/torrent_handle.cpp index 4b8d8a452..e9f179e32 100755 --- a/libtorrent/src/torrent_handle.cpp +++ b/libtorrent/src/torrent_handle.cpp @@ -400,12 +400,32 @@ namespace libtorrent return ret; } + void torrent_handle::file_priority(int index, int priority) const + { + INVARIANT_CHECK; + TORRENT_FORWARD(set_file_priority(index, priority)); + } + + int torrent_handle::file_priority(int index) const + { + INVARIANT_CHECK; + TORRENT_FORWARD_RETURN(file_priority(index), 0); + } + void torrent_handle::prioritize_files(std::vector const& files) const { INVARIANT_CHECK; TORRENT_FORWARD(prioritize_files(files)); } + std::vector torrent_handle::file_priorities() const + { + INVARIANT_CHECK; + std::vector ret; + TORRENT_FORWARD_RETURN2(file_priorities(ret), ret); + return ret; + } + // ============ start deprecation =============== void torrent_handle::filter_piece(int index, bool filter) const diff --git a/libtorrent/src/torrent_info.cpp b/libtorrent/src/torrent_info.cpp index c6c222fa0..03dc235d6 100755 --- a/libtorrent/src/torrent_info.cpp +++ b/libtorrent/src/torrent_info.cpp @@ -211,14 +211,19 @@ namespace libtorrent int load_file(fs::path const& filename, std::vector& v) { file f; - if (!f.open(filename, file::in)) return -1; - f.seek(0, file::end); - size_type s = f.tell(); + error_code ec; + if (!f.open(filename, file::in, ec)) return -1; + f.seek(0, file::end, ec); + if (ec) return -1; + size_type s = f.tell(ec); + if (ec) return -1; if (s > 5000000) return -2; v.resize(s); - f.seek(0); - size_type read = f.read(&v[0], s); + f.seek(0, file::begin, ec); + if (ec) return -1; + size_type read = f.read(&v[0], s, ec); if (read != s) return -3; + if (ec) return -3; return 0; } diff --git a/setup.py b/setup.py index 9d01eb750..585722a88 100644 --- a/setup.py +++ b/setup.py @@ -175,11 +175,6 @@ _sources = glob.glob("./libtorrent/src/*.cpp") + \ # Remove some files from the source that aren't needed _source_removals = ["mapped_storage.cpp"] -if windows_check(): - _source_removals.append("file.cpp") -else: - _source_removals.append("file_win.cpp") - for source in _sources: for rem in _source_removals: if rem in source: @@ -327,7 +322,7 @@ setup( ]}, packages = find_packages(exclude=["plugins"]), url = "http://deluge-torrent.org", - version = "0.9.02", + version = "0.9.03", ) try: