diff --git a/docs/manual.rst b/docs/manual.rst index f5053aa59..64972f913 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -5112,12 +5112,30 @@ storage_interface ================= The storage interface is a pure virtual class that can be implemented to -change the behavior of the actual file storage. The interface looks like -this:: +customize how and where data for a torrent is stored. The default storage +implementation uses regular files in the filesystem, mapping the files in the +torrent in the way one would assume a torrent is saved to disk. Implementing +your own storage interface makes it possible to store all data in RAM, or in +some optimized order on disk (the order the pieces are received for instance), +or saving multifile torrents in a single file in order to be able to take +advantage of optimized disk-I/O. + +It is also possible to write a thin class that uses the default storage but +modifies some particular behavior, for instance encrypting the data before +it's written to disk, and decrypting it when it's read again. + +The storage interface is based on slots, each slot is 'piece_size' number +of bytes. All access is done by writing and reading whole or partial +slots. One slot is one piece in the torrent, but the data in the slot +does not necessarily correspond to the piece with the same index (in +compact allocation mode it won't). + +The interface looks like this:: struct storage_interface { virtual bool initialize(bool allocate_files) = 0; + virtual bool has_any_file() = 0; virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs) = 0; virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs) = 0; virtual int sparse_end(int start) const; @@ -5156,6 +5174,16 @@ it will also ``ftruncate`` all files to their target size. Returning ``true`` indicates an error occurred. +has_any_file() +-------------- + + :: + + virtual bool has_any_file() = 0; + +This function is called when first checking (or re-checking) the storage for a torrent. +It should return true if any of the files that is used in this storage exists on disk. +If so, the storage will be checked for existing pieces before starting the download. readv() writev() ---------------- diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index 85701fba7..4964f3811 100644 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -119,6 +119,8 @@ namespace libtorrent // false return value indicates an error virtual bool initialize(bool allocate_files) = 0; + virtual bool has_any_file() = 0; + virtual int readv(file::iovec_t const* bufs, int slot, int offset, int num_bufs); virtual int writev(file::iovec_t const* bufs, int slot, int offset, int num_bufs); diff --git a/src/storage.cpp b/src/storage.cpp index 7ed9a9dfa..344064185 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -407,6 +407,7 @@ namespace libtorrent #endif } + bool has_any_file(); bool rename_file(int index, std::string const& new_filename); bool release_files(); bool delete_files(); @@ -581,6 +582,43 @@ namespace libtorrent return false; } + bool storage::has_any_file() + { + file_storage::iterator i = files().begin(); + file_storage::iterator end = files().end(); + + for (; i != end; ++i) + { + bool file_exists = false; + fs::path f = m_save_path / i->path; +#ifndef BOOST_NO_EXCEPTIONS + try + { +#endif +#if TORRENT_USE_WPATH + fs::wpath wf = convert_to_wstring(f.string()); + file_exists = exists(wf); +#elif TORRENT_USE_LOCALE_FILENAMES + file_exists = exists(convert_to_native(f.string())); +#else + file_exists = exists(f); +#endif + std::cerr << "has_any_file(): " << f << " " << (file_exists?"exists": "don't exist") + << std::endl; +#ifndef BOOST_NO_EXCEPTIONS + } + catch (boost::system::system_error& e) + { + set_error(f, e.code()); + return false; + } +#endif + if (file_exists && i->size > 0) + return true; + } + return false; + } + bool storage::rename_file(int index, std::string const& new_filename) { if (index < 0 || index >= m_files.num_files()) return true; @@ -1857,53 +1895,27 @@ ret: int piece_manager::check_no_fastresume(std::string& error) { - file_storage::iterator i = m_files.begin(); - file_storage::iterator end = m_files.end(); + bool has_files = m_storage->has_any_file(); - for (; i != end; ++i) + if (m_storage->error()) + return fatal_disk_error; + + if (has_files) { - bool file_exists = false; - fs::path f = m_save_path / i->path; -#ifndef BOOST_NO_EXCEPTIONS - try + m_state = state_full_check; + m_piece_to_slot.clear(); + m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot); + m_slot_to_piece.clear(); + m_slot_to_piece.resize(m_files.num_pieces(), unallocated); + if (m_storage_mode == storage_mode_compact) { -#endif -#if TORRENT_USE_WPATH - fs::wpath wf = convert_to_wstring(f.string()); - file_exists = exists(wf); -#elif TORRENT_USE_LOCALE_FILENAMES - file_exists = exists(convert_to_native(f.string())); -#else - file_exists = exists(f); -#endif -#ifndef BOOST_NO_EXCEPTIONS - } - catch (std::exception& e) - { - error = f.string(); - error += ": "; - error += e.what(); - TORRENT_ASSERT(!error.empty()); - return fatal_disk_error; - } -#endif - if (file_exists && i->size > 0) - { - m_state = state_full_check; - m_piece_to_slot.clear(); - m_piece_to_slot.resize(m_files.num_pieces(), has_no_slot); - m_slot_to_piece.clear(); - m_slot_to_piece.resize(m_files.num_pieces(), unallocated); - if (m_storage_mode == storage_mode_compact) - { - m_unallocated_slots.clear(); - m_free_slots.clear(); - } - TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); - return need_full_check; + m_unallocated_slots.clear(); + m_free_slots.clear(); } + TORRENT_ASSERT(int(m_piece_to_slot.size()) == m_files.num_pieces()); + return need_full_check; } - + if (m_storage_mode == storage_mode_compact) { // in compact mode without checking, we need to diff --git a/src/torrent.cpp b/src/torrent.cpp index aa8c12aab..97c385d72 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -804,10 +804,6 @@ namespace libtorrent disconnect_all(); m_owning_storage->async_release_files(); - m_owning_storage = new piece_manager(shared_from_this(), m_torrent_file - , m_save_path, m_ses.m_files, m_ses.m_disk_thread, m_storage_constructor - , m_storage_mode); - m_storage = m_owning_storage.get(); if (!m_picker) m_picker.reset(new piece_picker()); m_picker->init(m_torrent_file->piece_length() / m_block_size , int((m_torrent_file->total_size()+m_block_size-1)/m_block_size)); @@ -845,9 +841,17 @@ namespace libtorrent pause(); return; } - set_state(torrent_status::queued_for_checking); - if (should_check_files()) - queue_torrent_check(); + if (ret == 0) + { + // if there are no files, just start + files_checked(); + } + else + { + set_state(torrent_status::queued_for_checking); + if (should_check_files()) + queue_torrent_check(); + } } void torrent::start_checking()