diff --git a/ChangeLog b/ChangeLog index 4bab24e27..d64dd7137 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * clear sparse flag on files that complete on windows * support retry-after header for web seeds * replaced boost.filesystem with custom functions * replaced dependency on boost.thread by asio's internal thread primitives diff --git a/docs/manual.rst b/docs/manual.rst index 03dcd0023..f5abb3b28 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -6257,6 +6257,7 @@ The interface looks like this:: virtual bool rename_file(int file, std::string const& new_name) = 0; virtual bool release_files() = 0; virtual bool delete_files() = 0; + virtual void finalize_file(int index) {} virtual ~storage_interface() {} // non virtual functions @@ -6482,6 +6483,21 @@ following members:: void release_memory(); }; +finalize_file() +--------------- + + :: + + virtual void finalize_file(int index); + +This function is called each time a file is completely downloaded. The +storage implementation can perform last operations on a file. The file will +not be opened for writing after this. + +``index`` is the index of the file that completed. + +On windows the default storage implementation clears the sparse file flag +on the specified file. magnet links ============ diff --git a/include/libtorrent/disk_io_thread.hpp b/include/libtorrent/disk_io_thread.hpp index c08cf83be..c4fa0926e 100644 --- a/include/libtorrent/disk_io_thread.hpp +++ b/include/libtorrent/disk_io_thread.hpp @@ -93,6 +93,7 @@ namespace libtorrent , abort_torrent , update_settings , read_and_hash + , finalize_file }; action_t action; diff --git a/include/libtorrent/file.hpp b/include/libtorrent/file.hpp index 6cd997ca4..44d7bf80a 100644 --- a/include/libtorrent/file.hpp +++ b/include/libtorrent/file.hpp @@ -210,6 +210,10 @@ namespace libtorrent void close(); bool set_size(size_type size, error_code& ec); + // called when we're done writing to the file. + // On windows this will clear the sparse bit + void finalize(); + int open_mode() const { return m_open_mode; } // when opened in unbuffered mode, this is the diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index fde723deb..e1a1463c9 100644 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -158,6 +158,8 @@ namespace libtorrent // non-zero return value indicates an error virtual bool delete_files() = 0; + virtual void finalize_file(int file) {} + disk_buffer_pool* disk_pool() { return m_disk_pool; } session_settings const& settings() const { return *m_settings; } @@ -204,6 +206,8 @@ namespace libtorrent boost::intrusive_ptr info() const { return m_info; } void write_resume_data(entry& rd) const; + void async_finalize_file(int file); + void async_check_fastresume(lazy_entry const* resume_data , boost::function const& handler); @@ -325,6 +329,8 @@ namespace libtorrent size_type physical_offset(int piece_index, int offset); + void finalize_file(int index); + // returns the number of pieces left in the // file currently being checked int skip_file() const; diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index 5c3612438..e8d01a26a 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -1341,6 +1341,7 @@ namespace libtorrent , fence_operation // abort_torrent , 0 // update_settings , read_operation // read_and_hash + , 0 // finalize_file }; bool is_fence_operation(disk_io_job const& j) @@ -1672,6 +1673,14 @@ namespace libtorrent #endif break; } + case disk_io_job::finalize_file: + { +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " finalize_file " << j.piece << std::endl; +#endif + j.storage->finalize_file(j.piece); + break; + } case disk_io_job::read: { if (test_error(j)) diff --git a/src/file.cpp b/src/file.cpp index abb397c82..ec01be3fa 100644 --- a/src/file.cpp +++ b/src/file.cpp @@ -732,7 +732,8 @@ namespace libtorrent } // try to make the file sparse if supported - if (mode & file::sparse) + // only set this flag if the file is opened for writing + if ((mode & file::sparse) && (mode & rw_mask) != read_only) { DWORD temp; ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, 0, 0 @@ -1451,6 +1452,19 @@ namespace libtorrent return true; } + void file::finalize() + { +#ifdef TORRENT_WINDOWS + // according to MSDN, clearing the sparse flag of a file only + // works on windows vista and later + DWORD temp; + FILE_SET_SPARSE_BUFFER b; + b.SetSparse = FALSE; + ::DeviceIoControl(m_file_handle, FSCTL_SET_SPARSE, &b, sizeof(b) + , 0, 0, &temp, 0); +#endif + } + size_type file::get_size(error_code& ec) const { #ifdef TORRENT_WINDOWS diff --git a/src/storage.cpp b/src/storage.cpp index 9c25c847e..ef70378b6 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -349,6 +349,7 @@ namespace libtorrent m_save_path = complete(path); } + void finalize_file(int file); bool has_any_file(); bool rename_file(int index, std::string const& new_filename); bool release_files(); @@ -398,6 +399,9 @@ namespace libtorrent boost::scoped_ptr m_mapped_files; file_storage const& m_files; + // helper function to open a file in the file pool with the right mode + boost::shared_ptr open_file(file_entry const& fe, int mode, error_code& ec) const; + std::vector m_file_priority; std::string m_save_path; // the file pool is typically stored in @@ -540,20 +544,13 @@ namespace libtorrent if (ec || s.file_size > file_iter->size || file_iter->size == 0) { ec.clear(); - int mode = file::read_write; - if (m_settings - && (settings().disk_io_read_mode == session_settings::disable_os_cache - || (settings().disk_io_read_mode == session_settings::disable_os_cache_for_aligned_files - && ((file_iter->offset + file_iter->file_base) & (m_page_size-1)) == 0))) - mode |= file::no_buffer; - if (!m_allocate_files) mode |= file::sparse; - boost::shared_ptr f = m_pool.open_file(this - , combine_path(m_save_path, file_iter->path), mode, ec); - if (ec) set_error(combine_path(m_save_path, file_iter->path), ec); + boost::shared_ptr f = open_file(*file_iter, file::read_write, ec); + std::string path = combine_path(m_save_path, file_iter->path); + if (ec) set_error(path, ec); else if (f) { f->set_size(file_iter->size, ec); - if (ec) set_error(combine_path(m_save_path, file_iter->path), ec); + if (ec) set_error(path, ec); } } @@ -566,6 +563,18 @@ namespace libtorrent return false; } + void storage::finalize_file(int index) + { + TORRENT_ASSERT(index >= 0 && index < m_files.num_files()); + if (index < 0 || index >= m_files.num_files()) return; + + error_code ec; + boost::shared_ptr f = open_file(files().at(index), file::read_write, ec); + if (ec || !f) return; + + f->finalize(); + } + bool storage::has_any_file() { file_storage::iterator i = files().begin(); @@ -696,19 +705,8 @@ namespace libtorrent TORRENT_ASSERT(file_iter != files().end()); } - std::string path = combine_path(m_save_path, file_iter->path); error_code ec; - int mode = file::read_only; - - boost::shared_ptr file_handle; - int cache_setting = m_settings ? settings().disk_io_write_mode : 0; - if (cache_setting == session_settings::disable_os_cache - || (cache_setting == session_settings::disable_os_cache_for_aligned_files - && ((file_iter->offset + file_iter->file_base) & (m_page_size-1)) == 0)) - mode |= file::no_buffer; - if (!m_allocate_files) mode |= file::sparse; - - file_handle = m_pool.open_file(const_cast(this), path, mode, ec); + boost::shared_ptr file_handle = open_file(*file_iter, file::read_only, ec); if (!file_handle || ec) return slot; size_type data_start = file_handle->sparse_end(file_offset); @@ -1044,13 +1042,10 @@ ret: size_type file_offset = tor_off - file_iter->offset; TORRENT_ASSERT(file_offset >= 0); - std::string p = combine_path(m_save_path, file_iter->path); - error_code ec; - // open the file read only to avoid re-opening // it in case it's already opened in read-only mode - boost::shared_ptr f = m_pool.open_file( - this, p, file::read_only, ec); + error_code ec; + boost::shared_ptr f = open_file(*file_iter, file::read_only, ec); size_type ret = 0; if (f && !ec) ret = f->phys_offset(file_offset); @@ -1191,20 +1186,11 @@ ret: continue; } - std::string path = combine_path(m_save_path, file_iter->path); - error_code ec; - int mode = op.mode; - - if (op.cache_setting == session_settings::disable_os_cache - || (op.cache_setting == session_settings::disable_os_cache_for_aligned_files - && ((file_iter->offset + file_iter->file_base) & (m_page_size-1)) == 0)) - mode |= file::no_buffer; - if (!m_allocate_files) mode |= file::sparse; - - file_handle = m_pool.open_file(this, path, mode, ec); + file_handle = open_file(*file_iter, op.mode, ec); if (!file_handle || ec) { + std::string path = combine_path(m_save_path, file_iter->path); TORRENT_ASSERT(ec); set_error(path, ec); return -1; @@ -1310,6 +1296,18 @@ ret: return readv(&b, slot, offset, 1); } + boost::shared_ptr storage::open_file(file_entry const& fe, int mode, error_code& ec) const + { + int cache_setting = m_settings ? settings().disk_io_write_mode : 0; + if (cache_setting == session_settings::disable_os_cache + || (cache_setting == session_settings::disable_os_cache_for_aligned_files + && ((fe.offset + fe.file_base) & (m_page_size-1)) == 0)) + mode |= file::no_buffer; + if (!m_allocate_files) mode |= file::sparse; + + return m_pool.open_file(const_cast(this), combine_path(m_save_path, fe.path), mode, ec); + } + storage_interface* default_storage_constructor(file_storage const& fs , file_storage const* mapped, std::string const& path, file_pool& fp) { @@ -1426,10 +1424,23 @@ ret: m_storage->m_disk_pool = &m_io_thread; } + void piece_manager::finalize_file(int index) + { m_storage->finalize_file(index); } + piece_manager::~piece_manager() { } + void piece_manager::async_finalize_file(int file) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::finalize_file; + j.piece = file; + boost::function empty; + m_io_thread.add_job(j, empty); + } + void piece_manager::async_save_resume_data( boost::function const& handler) { diff --git a/src/torrent.cpp b/src/torrent.cpp index 7150de9ad..7fa55c8af 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -2003,6 +2003,7 @@ namespace libtorrent if (m_file_progress[file_index] >= m_torrent_file->files().at(file_index).size) { + filesystem().async_finalize_file(file_index); if (m_ses.m_alerts.should_post()) { // this file just completed, post alert