diff --git a/docs/manual.rst b/docs/manual.rst index 6a1246d22..3dbf59b21 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -3557,6 +3557,7 @@ session_settings int write_cache_line_size; int optimistic_disk_retry; + bool disable_hash_check; }; ``user_agent`` this is the client identification to the tracker. @@ -3975,6 +3976,12 @@ libtorrent will only do this automatically for auto managed torrents. You can explicitly take a torrent out of upload only mode using `set_upload_mode()`_. +``disable_hash_check`` controls if downloaded pieces are verified against +the piece hashes in the torrent file or not. The default is false, i.e. +to verify all downloaded data. It may be useful to turn this off for performance +profiling and simulation scenarios. Do not disable the hash check for regular +bittorrent clients. + pe_settings =========== diff --git a/include/libtorrent/session_settings.hpp b/include/libtorrent/session_settings.hpp index f5839b43d..3bc5fa360 100644 --- a/include/libtorrent/session_settings.hpp +++ b/include/libtorrent/session_settings.hpp @@ -175,6 +175,7 @@ namespace libtorrent , read_cache_line_size(16) , write_cache_line_size(32) , optimistic_disk_retry(10 * 60) + , disable_hash_checks(false) {} // this is the user agent that will be sent to the tracker @@ -612,6 +613,14 @@ namespace libtorrent // this is the number of seconds a disk failure // occurs until libtorrent will re-try. int optimistic_disk_retry; + + // when set to true, all data downloaded from + // peers will be assumed to be correct, and not + // tested to match the hashes in the torrent + // this is only useful for simulation and + // testing purposes (typically combined with + // disabled_storage) + bool disable_hash_checks; }; #ifndef TORRENT_DISABLE_DHT diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index 586072c04..b55d4167d 100644 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -195,6 +195,9 @@ namespace libtorrent TORRENT_EXPORT storage_interface* default_storage_constructor( file_storage const&, file_storage const* mapped, fs::path const&, file_pool&); + TORRENT_EXPORT storage_interface* disabled_storage_constructor( + file_storage const&, file_storage const* mapped, fs::path const&, file_pool&); + struct disk_io_thread; class TORRENT_EXPORT piece_manager diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index 446f06069..318a55bbb 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -1488,7 +1488,8 @@ namespace libtorrent test_error(j); break; } - ret = (j.storage->info()->hash_for_piece(j.piece) == h)?ret:-3; + if (!m_settings.disable_hash_checks) + ret = (j.storage->info()->hash_for_piece(j.piece) == h)?ret:-3; if (ret == -3) { j.storage->mark_failed(j.piece); @@ -1656,6 +1657,11 @@ namespace libtorrent } } l.unlock(); + if (m_settings.disable_hash_checks) + { + ret = 0; + break; + } sha1_hash h = j.storage->hash_for_piece_impl(j.piece); if (test_error(j)) { @@ -1663,6 +1669,7 @@ namespace libtorrent j.storage->mark_failed(j.piece); break; } + ret = (j.storage->info()->hash_for_piece(j.piece) == h)?0:-2; if (ret == -2) j.storage->mark_failed(j.piece); break; diff --git a/src/session_impl.cpp b/src/session_impl.cpp index a7aee3af0..7ee62ca5b 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -648,6 +648,7 @@ namespace aux { || m_settings.coalesce_writes != s.coalesce_writes || m_settings.coalesce_reads != s.coalesce_reads || m_settings.max_queued_disk_bytes != s.max_queued_disk_bytes + || m_settings.disable_hash_checks != s.disable_hash_checks #ifndef TORRENT_DISABLE_MLOCK || m_settings.lock_disk_cache != s.lock_disk_cache #endif diff --git a/src/storage.cpp b/src/storage.cpp index 06f205162..f1ffb69c2 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -1448,6 +1448,85 @@ ret: return new storage(fs, mapped, path, fp); } + // this storage implementation does not write anything to disk + // and it pretends to read, and just leaves garbage in the buffers + // this is useful when simulating many clients on the same machine + // or when running stress tests and want to take the cost of the + // disk I/O out of the picture. This cannot be used for any kind + // of normal bittorrent operation, since it will just send garbage + // to peers and throw away all the data it downloads. It would end + // up being banned immediately + class disabled_storage : public storage_interface, boost::noncopyable + { + public: + disabled_storage(int piece_size) : m_piece_size(piece_size) {} + bool has_any_file() { return false; } + bool rename_file(int index, std::string const& new_filename) { return false; } + bool release_files() { return false; } + bool delete_files() { return false; } + bool initialize(bool allocate_files) { return false; } + bool move_storage(fs::path save_path) { return false; } + int read(char* buf, int slot, int offset, int size) { return size; } + int write(char const* buf, int slot, int offset, int size) { return size; } + int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " read " + << (size_type(slot) * m_piece_size + offset) << std::endl; + } +#endif + int ret = 0; + for (int i = 0; i < num_bufs; ++i) + ret += bufs[i].iov_len; +#ifdef TORRENT_DISK_STATS + if (pool) + { + pool->m_disk_access_log << log_time() << " read_end " + << (size_type(slot) * m_piece_size + offset + ret) << std::endl; + } +#endif + return ret; + } + int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs) + { +#ifdef TORRENT_DISK_STATS + disk_buffer_pool* pool = disk_pool(); + if (pool) + { + pool->m_disk_access_log << log_time() << " write " + << (size_type(slot) * m_piece_size + offset) << std::endl; + } +#endif + int ret = 0; + for (int i = 0; i < num_bufs; ++i) + ret += bufs[i].iov_len; +#ifdef TORRENT_DISK_STATS + if (pool) + { + pool->m_disk_access_log << log_time() << " write_end " + << (size_type(slot) * m_piece_size + offset + ret) << std::endl; + } +#endif + return ret; + } + bool move_slot(int src_slot, int dst_slot) { return false; } + bool swap_slots(int slot1, int slot2) { return false; } + bool swap_slots3(int slot1, int slot2, int slot3) { return false; } + bool verify_resume_data(lazy_entry const& rd, error_code& error) { return false; } + bool write_resume_data(entry& rd) const { return false; } + + int m_piece_size; + }; + + storage_interface* disabled_storage_constructor(file_storage const& fs + , file_storage const* mapped, fs::path const& path, file_pool& fp) + { + return new disabled_storage(fs.piece_length()); + } + // -- piece_manager ----------------------------------------------------- piece_manager::piece_manager(