From 921cbeebed3270eb062afddf7a1661a1197394ca Mon Sep 17 00:00:00 2001 From: arvidn Date: Sun, 13 Mar 2016 03:50:37 -0400 Subject: [PATCH] add option to delete just the partfile when removing a torrent --- include/libtorrent/disk_interface.hpp | 2 +- include/libtorrent/disk_io_job.hpp | 1 + include/libtorrent/disk_io_thread.hpp | 2 +- include/libtorrent/session_handle.hpp | 6 +- include/libtorrent/storage.hpp | 14 ++-- include/libtorrent/torrent.hpp | 2 +- simulation/test_swarm.cpp | 63 +++++++++++++++ src/disk_io_thread.cpp | 6 +- src/session_impl.cpp | 10 ++- src/storage.cpp | 110 ++++++++++++++------------ src/torrent.cpp | 4 +- test/test_block_cache.cpp | 4 +- test/test_storage.cpp | 4 +- 13 files changed, 157 insertions(+), 71 deletions(-) diff --git a/include/libtorrent/disk_interface.hpp b/include/libtorrent/disk_interface.hpp index eb6cd8f51..523f0f66a 100644 --- a/include/libtorrent/disk_interface.hpp +++ b/include/libtorrent/disk_interface.hpp @@ -84,7 +84,7 @@ namespace libtorrent , boost::function const& handler)= 0; virtual void async_rename_file(piece_manager* storage, int index, std::string const& name , boost::function const& handler) = 0; - virtual void async_delete_files(piece_manager* storage + virtual void async_delete_files(piece_manager* storage, int options , boost::function const& handler) = 0; virtual void async_save_resume_data(piece_manager* storage , boost::function const& handler) = 0; diff --git a/include/libtorrent/disk_io_job.hpp b/include/libtorrent/disk_io_job.hpp index ded630b6f..e0a6c28e5 100644 --- a/include/libtorrent/disk_io_job.hpp +++ b/include/libtorrent/disk_io_job.hpp @@ -167,6 +167,7 @@ namespace libtorrent bdecode_node const* check_resume_data; std::vector* priorities; torrent_info* torrent_file; + int delete_options; } buffer; // the disk storage this job applies to (if applicable) diff --git a/include/libtorrent/disk_io_thread.hpp b/include/libtorrent/disk_io_thread.hpp index 2bd64d2d8..10d3b6ac7 100644 --- a/include/libtorrent/disk_io_thread.hpp +++ b/include/libtorrent/disk_io_thread.hpp @@ -311,7 +311,7 @@ namespace libtorrent void async_release_files(piece_manager* storage , boost::function const& handler = boost::function()); - void async_delete_files(piece_manager* storage + void async_delete_files(piece_manager* storage, int options , boost::function const& handler); void async_check_fastresume(piece_manager* storage , bdecode_node const* resume_data diff --git a/include/libtorrent/session_handle.hpp b/include/libtorrent/session_handle.hpp index d387d4c9f..0fb9a4f85 100644 --- a/include/libtorrent/session_handle.hpp +++ b/include/libtorrent/session_handle.hpp @@ -767,7 +767,11 @@ namespace libtorrent enum options_t { // delete the files belonging to the torrent from disk. - delete_files = 1 + // including the part-file, if there is one + delete_files = 1, + + // delete just the part-file associated with this torrent + delete_partfile = 2 }; // flags to be passed in to the session constructor diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index ccd8e94aa..3474b229c 100644 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -339,8 +339,10 @@ namespace libtorrent virtual void rename_file(int index, std::string const& new_filename , storage_error& ec) = 0; - // This function should delete all files and directories belonging to - // this storage. + // This function should delete some or all of the storage for this torrent. + // The ``options`` parameter specifies whether to delete all files or just + // the partfile. ``options`` are set to the same value as the options + // passed to session::remove_torrent(). // // If an error occurs, ``storage_error`` should be set to reflect it. // @@ -360,7 +362,7 @@ namespace libtorrent // void release_memory(); // }; // - virtual void delete_files(storage_error& ec) = 0; + virtual void delete_files(int options, storage_error& ec) = 0; #ifndef TORRENT_NO_DEPRECATE // This function is called each time a file is completely downloaded. The @@ -426,7 +428,7 @@ namespace libtorrent virtual void rename_file(int index, std::string const& new_filename , storage_error& ec) TORRENT_OVERRIDE; virtual void release_files(storage_error& ec) TORRENT_OVERRIDE; - virtual void delete_files(storage_error& ec) TORRENT_OVERRIDE; + virtual void delete_files(int options, storage_error& ec) TORRENT_OVERRIDE; virtual void initialize(storage_error& ec) TORRENT_OVERRIDE; virtual int move_storage(std::string const& save_path, int flags , storage_error& ec) TORRENT_OVERRIDE; @@ -506,7 +508,7 @@ namespace libtorrent , storage_error&) TORRENT_OVERRIDE {} virtual void rename_file(int, std::string const&, storage_error&) TORRENT_OVERRIDE {} virtual void release_files(storage_error&) TORRENT_OVERRIDE {} - virtual void delete_files(storage_error&) TORRENT_OVERRIDE {} + virtual void delete_files(int, storage_error&) TORRENT_OVERRIDE {} virtual void initialize(storage_error&) TORRENT_OVERRIDE {} virtual int move_storage(std::string const&, int, storage_error&) TORRENT_OVERRIDE { return 0; } @@ -545,7 +547,7 @@ namespace libtorrent virtual void release_files(storage_error&) TORRENT_OVERRIDE {} virtual void rename_file(int /* index */ , std::string const& /* new_filenamem */, storage_error&) TORRENT_OVERRIDE {} - virtual void delete_files(storage_error&) TORRENT_OVERRIDE {} + virtual void delete_files(int, storage_error&) TORRENT_OVERRIDE {} }; struct disk_io_thread; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 48c5e6c08..2339e4805 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -505,7 +505,7 @@ namespace libtorrent bool should_check_files() const; - bool delete_files(); + bool delete_files(int options); void peers_erased(std::vector const& peers); // ============ start deprecation ============= diff --git a/simulation/test_swarm.cpp b/simulation/test_swarm.cpp index 8cc3e3be5..449fde8d1 100644 --- a/simulation/test_swarm.cpp +++ b/simulation/test_swarm.cpp @@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/alert_types.hpp" #include "libtorrent/session.hpp" #include "libtorrent/session_stats.hpp" +#include "libtorrent/file.hpp" using namespace libtorrent; @@ -350,6 +351,68 @@ TORRENT_TEST(shutdown) }); } +TORRENT_TEST(delete_files) +{ + std::string save_path; + + setup_swarm(2, swarm_test::download + // add session + , [](lt::settings_pack& pack) {} + // add torrent + , [](lt::add_torrent_params& params) {} + // on alert + , [](lt::alert const* a, lt::session& ses) {} + // terminate + , [&save_path](int ticks, lt::session& ses) -> bool + { + if (completed_pieces(ses) == 0) return false; + + auto h = ses.get_torrents()[0]; + save_path = h.status().save_path; + ses.remove_torrent(h, session::delete_files); + return true; + }); + + // assert the file is no longer there + file_status st; + error_code ec; + stat_file(combine_path(save_path, "temporary"), &st, ec); + printf("expecting \"%s/temporary\" to NOT exist [%s | %s]\n" + , save_path.c_str() + , ec.category().name() + , ec.message().c_str()); + TEST_EQUAL(ec, error_code(boost::system::errc::no_such_file_or_directory, system_category())); +} + +TORRENT_TEST(delete_partfile) +{ + std::string save_path; + setup_swarm(2, swarm_test::download + // add session + , [](lt::settings_pack& pack) {} + // add torrent + , [](lt::add_torrent_params& params) {} + // on alert + , [](lt::alert const* a, lt::session& ses) {} + // terminate + , [&save_path](int ticks, lt::session& ses) -> bool + { + if (completed_pieces(ses) == 0) return false; + + auto h = ses.get_torrents()[0]; + save_path = h.status().save_path; + ses.remove_torrent(h, session::delete_partfile); + return true; + }); + // assert the file *is* still there + file_status st; + error_code ec; + stat_file(combine_path(save_path, "temporary"), &st, ec); + printf("expecting \"%s/temporary\" to exist [%s]\n", save_path.c_str() + , ec.message().c_str()); + TEST_CHECK(!ec); +} + // TODO: add test that makes sure a torrent in graceful pause mode won't make // outgoing connections // TODO: add test that makes sure a torrent in graceful pause mode won't accept diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index 22f403764..f864f5b0f 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -1817,6 +1817,7 @@ namespace libtorrent } void disk_io_thread::async_delete_files(piece_manager* storage + , int const options , boost::function const& handler) { #ifdef TORRENT_DEBUG @@ -1857,6 +1858,7 @@ namespace libtorrent disk_io_job* j = allocate_job(disk_io_job::delete_files); j->storage = storage->shared_from_this(); j->callback = handler; + j->buffer.delete_options = options; add_fence_job(storage, j); fail_jobs_impl(storage_error(boost::asio::error::operation_aborted) @@ -2548,7 +2550,7 @@ namespace libtorrent int disk_io_thread::do_delete_files(disk_io_job* j, jobqueue_t& completed_jobs) { - TORRENT_ASSERT(j->buffer.string == 0); + TORRENT_ASSERT(j->buffer.delete_options != 0); INVARIANT_CHECK; // if this assert fails, something's wrong with the fence logic @@ -2563,7 +2565,7 @@ namespace libtorrent , completed_jobs, l); l.unlock(); - j->storage->get_storage_impl()->delete_files(j->error); + j->storage->get_storage_impl()->delete_files(j->buffer.delete_options, j->error); return j->error ? -1 : 0; } diff --git a/src/session_impl.cpp b/src/session_impl.cpp index b38567f83..7031386fd 100644 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -5116,7 +5116,8 @@ retry: boost::shared_ptr tptr = h.m_torrent.lock(); if (!tptr) return; - m_alerts.emplace_alert(tptr->get_handle(), tptr->info_hash()); + m_alerts.emplace_alert(tptr->get_handle() + , tptr->info_hash()); remove_torrent_impl(tptr, options); @@ -5124,7 +5125,8 @@ retry: tptr->set_queue_position(-1); } - void session_impl::remove_torrent_impl(boost::shared_ptr tptr, int options) + void session_impl::remove_torrent_impl(boost::shared_ptr tptr + , int options) { // remove from uuid list if (!tptr->uuid().empty()) @@ -5148,9 +5150,9 @@ retry: if (i == m_torrents.end()) return; torrent& t = *i->second; - if (options & session::delete_files) + if (options) { - if (!t.delete_files()) + if (!t.delete_files(options)) { if (m_alerts.should_post()) m_alerts.emplace_alert(t.get_handle() diff --git a/src/storage.cpp b/src/storage.cpp index 38b5e3da1..db8613870 100644 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -754,9 +754,10 @@ namespace libtorrent ec.clear(); } - void default_storage::delete_files(storage_error& ec) + void default_storage::delete_files(int const options, storage_error& ec) { - DFLOG(stderr, "[%p] delete_files\n", static_cast(this)); + DFLOG(stderr, "[%p] delete_files [%x]\n", static_cast(this) + , options); #if TORRENT_USE_ASSERTS // this is a fence job, we expect no other @@ -775,58 +776,69 @@ namespace libtorrent // make sure we don't have the files open m_pool.release(this); -#if defined TORRENT_DEBUG_FILE_LEAKS - print_open_files("release files", m_files.name().c_str()); -#endif - -#if TORRENT_USE_ASSERTS - m_pool.mark_deleted(m_files); -#endif - // delete the files from disk - std::set directories; - typedef std::set::iterator iter_t; - for (int i = 0; i < files().num_files(); ++i) - { - std::string fp = files().file_path(i); - bool complete = is_complete(fp); - std::string p = complete ? fp : combine_path(m_save_path, fp); - if (!complete) - { - std::string bp = parent_path(fp); - std::pair ret; - ret.second = true; - while (ret.second && !bp.empty()) - { - ret = directories.insert(combine_path(m_save_path, bp)); - bp = parent_path(bp); - } - } - delete_one_file(p, ec.ec); - if (ec) { ec.file = i; ec.operation = storage_error::remove; } - } - - // remove the directories. Reverse order to delete - // subdirectories first - - for (std::set::reverse_iterator i = directories.rbegin() - , end(directories.rend()); i != end; ++i) - { - error_code error; - delete_one_file(*i, error); - if (error && !ec) { ec.file = -1; ec.ec = error; ec.operation = storage_error::remove; } - } - // if there's a part file open, make sure to destruct it to have it // release the underlying part file. Otherwise we may not be able to // delete it if (m_part_file) m_part_file.reset(); - error_code error; - remove(combine_path(m_save_path, m_part_file_name), error); - DFLOG(stderr, "[%p] delete partfile %s/%s [%s]\n", static_cast(this) - , m_save_path.c_str(), m_part_file_name.c_str(), error.message().c_str()); - if (error != boost::system::errc::no_such_file_or_directory && !error) - { ec.file = -1; ec.ec = error; ec.operation = storage_error::remove; } +#if defined TORRENT_DEBUG_FILE_LEAKS + print_open_files("release files", m_files.name().c_str()); +#endif + + if (options == session::delete_files) + { +#if TORRENT_USE_ASSERTS + m_pool.mark_deleted(m_files); +#endif + // delete the files from disk + std::set directories; + typedef std::set::iterator iter_t; + for (int i = 0; i < files().num_files(); ++i) + { + std::string fp = files().file_path(i); + bool complete = is_complete(fp); + std::string p = complete ? fp : combine_path(m_save_path, fp); + if (!complete) + { + std::string bp = parent_path(fp); + std::pair ret; + ret.second = true; + while (ret.second && !bp.empty()) + { + ret = directories.insert(combine_path(m_save_path, bp)); + bp = parent_path(bp); + } + } + delete_one_file(p, ec.ec); + if (ec) { ec.file = i; ec.operation = storage_error::remove; } + } + + // remove the directories. Reverse order to delete + // subdirectories first + + for (std::set::reverse_iterator i = directories.rbegin() + , end(directories.rend()); i != end; ++i) + { + error_code error; + delete_one_file(*i, error); + if (error && !ec) { ec.file = -1; ec.ec = error; ec.operation = storage_error::remove; } + } + } + + if (options == session::delete_files + || options == session::delete_partfile) + { + error_code error; + remove(combine_path(m_save_path, m_part_file_name), error); + DFLOG(stderr, "[%p] delete partfile %s/%s [%s]\n", static_cast(this) + , m_save_path.c_str(), m_part_file_name.c_str(), error.message().c_str()); + if (error && error != boost::system::errc::no_such_file_or_directory) + { + ec.file = -1; + ec.ec = error; + ec.operation = storage_error::remove; + } + } DFLOG(stderr, "[%p] delete_files result: %s\n", static_cast(this) , ec.ec.message().c_str()); diff --git a/src/torrent.cpp b/src/torrent.cpp index ea9a1638c..da5a53297 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -9317,7 +9317,7 @@ namespace libtorrent return limit_impl(peer_connection::download_channel); } - bool torrent::delete_files() + bool torrent::delete_files(int const options) { TORRENT_ASSERT(is_single_thread()); @@ -9333,7 +9333,7 @@ namespace libtorrent { TORRENT_ASSERT(m_storage); inc_refcount("delete_files"); - m_ses.disk_thread().async_delete_files(m_storage.get() + m_ses.disk_thread().async_delete_files(m_storage.get(), options , boost::bind(&torrent::on_files_deleted, shared_from_this(), _1)); m_deleted = true; return true; diff --git a/test/test_block_cache.cpp b/test/test_block_cache.cpp index b28816b68..c64cda2a7 100644 --- a/test/test_block_cache.cpp +++ b/test/test_block_cache.cpp @@ -72,7 +72,7 @@ struct test_storage_impl : storage_interface virtual void release_files(storage_error& ec) {} virtual void rename_file(int index, std::string const& new_filenamem , storage_error& ec) {} - virtual void delete_files(storage_error& ec) {} + virtual void delete_files(int, storage_error& ec) {} virtual void finalize_file(int, storage_error&) {} }; @@ -81,7 +81,7 @@ static void nop() {} #if defined TORRENT_DEBUG || TORRENT_RELEASE_ASSERTS #define INITIALIZE_JOB(j) j.in_use = true; #else -#define INITIALIZE_JOB(j) +#define INITIALIZE_JOB(j) #endif #define TEST_SETUP \ diff --git a/test/test_storage.cpp b/test/test_storage.cpp index 06e3699d7..a032f0b54 100644 --- a/test/test_storage.cpp +++ b/test/test_storage.cpp @@ -354,7 +354,7 @@ void test_remove(std::string const& test_path, bool unbuffered) , combine_path("_folder3", "test5.tmp"))), &st, ec); TEST_EQUAL(st.file_size, 8); - s->delete_files(se); + s->delete_files(session::delete_files, se); if (se) print_error("delete_files", 0, se.ec); if (se) @@ -664,7 +664,7 @@ TORRENT_TEST(fastresume) { print_alerts(ses, "ses"); s = h.status(); - if (s.progress == 1.0f) + if (s.progress == 1.0f) { std::cout << "progress: 1.0f" << std::endl; break;