From 59c7f5bfbb9afa9f978e7b0e43d78c30b173541a Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Sat, 13 Oct 2007 03:33:33 +0000 Subject: [PATCH] added option to delete files from disk as a torrent is removed --- include/libtorrent/alert_types.hpp | 10 +++++ include/libtorrent/aux_/session_impl.hpp | 4 +- include/libtorrent/disk_io_thread.hpp | 1 + include/libtorrent/session.hpp | 8 +++- include/libtorrent/storage.hpp | 11 +++++- include/libtorrent/torrent.hpp | 3 ++ src/disk_io_thread.cpp | 8 ++++ src/session.cpp | 4 +- src/session_impl.cpp | 12 ++++-- src/storage.cpp | 48 +++++++++++++++++++++--- src/torrent.cpp | 33 ++++++++++++++++ test/test_swarm.cpp | 11 ++++++ 12 files changed, 139 insertions(+), 14 deletions(-) diff --git a/include/libtorrent/alert_types.hpp b/include/libtorrent/alert_types.hpp index a46eb7817..6f41758fa 100755 --- a/include/libtorrent/alert_types.hpp +++ b/include/libtorrent/alert_types.hpp @@ -252,6 +252,16 @@ namespace libtorrent { return std::auto_ptr(new storage_moved_alert(*this)); } }; + struct TORRENT_EXPORT torrent_deleted_alert: torrent_alert + { + torrent_deleted_alert(torrent_handle const& h, std::string const& msg) + : torrent_alert(h, alert::warning, msg) + {} + + virtual std::auto_ptr clone() const + { return std::auto_ptr(new torrent_deleted_alert(*this)); } + }; + struct TORRENT_EXPORT torrent_paused_alert: torrent_alert { torrent_paused_alert(torrent_handle const& h, std::string const& msg) diff --git a/include/libtorrent/aux_/session_impl.hpp b/include/libtorrent/aux_/session_impl.hpp index a936974f5..bff8e3387 100644 --- a/include/libtorrent/aux_/session_impl.hpp +++ b/include/libtorrent/aux_/session_impl.hpp @@ -140,7 +140,7 @@ namespace libtorrent checker_impl(session_impl& s): m_ses(s), m_abort(false) {} void operator()(); piece_checker_data* find_torrent(const sha1_hash& info_hash); - void remove_torrent(sha1_hash const& info_hash); + void remove_torrent(sha1_hash const& info_hash, int options); #ifndef NDEBUG void check_invariant() const; @@ -270,7 +270,7 @@ namespace libtorrent , bool paused , void* userdata); - void remove_torrent(torrent_handle const& h); + void remove_torrent(torrent_handle const& h, int options); std::vector get_torrents(); diff --git a/include/libtorrent/disk_io_thread.hpp b/include/libtorrent/disk_io_thread.hpp index b893aaf60..bd6d5e1ba 100644 --- a/include/libtorrent/disk_io_thread.hpp +++ b/include/libtorrent/disk_io_thread.hpp @@ -64,6 +64,7 @@ namespace libtorrent , hash , move_storage , release_files + , delete_files }; action_t action; diff --git a/include/libtorrent/session.hpp b/include/libtorrent/session.hpp index 446fe74c8..5093e2336 100755 --- a/include/libtorrent/session.hpp +++ b/include/libtorrent/session.hpp @@ -219,7 +219,13 @@ namespace libtorrent // number of half open connections. int num_connections() const; - void remove_torrent(const torrent_handle& h); + enum options_t + { + none = 0, + delete_files = 1 + }; + + void remove_torrent(const torrent_handle& h, int options = none); void set_settings(session_settings const& s); session_settings const& settings(); diff --git a/include/libtorrent/storage.hpp b/include/libtorrent/storage.hpp index a36d1bac4..68a81c75b 100755 --- a/include/libtorrent/storage.hpp +++ b/include/libtorrent/storage.hpp @@ -151,6 +151,10 @@ namespace libtorrent // writing. This is called when a torrent has finished // downloading. virtual void release_files() = 0; + + // this will close all open files and delete them + virtual void delete_files() = 0; + virtual ~storage_interface() {} }; @@ -226,6 +230,10 @@ namespace libtorrent boost::function const& handler = boost::function()); + void async_delete_files( + boost::function const& handler + = boost::function()); + void async_move_storage(fs::path const& p , boost::function const& handler); @@ -270,7 +278,8 @@ namespace libtorrent void switch_to_full_mode(); sha1_hash hash_for_piece_impl(int piece); - void release_files_impl(); + void release_files_impl() { m_storage->release_files(); } + void delete_files_impl() { m_storage->delete_files(); } bool move_storage_impl(fs::path const& save_path); diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 7d5520b18..60140f2c2 100755 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -177,6 +177,8 @@ namespace libtorrent void resume(); bool is_paused() const { return m_paused; } + void delete_files(); + // ============ start deprecation ============= void filter_piece(int index, bool filter); void filter_pieces(std::vector const& bitmask); @@ -550,6 +552,7 @@ namespace libtorrent private: + void on_files_deleted(int ret, disk_io_job const& j); void on_files_released(int ret, disk_io_job const& j); void on_torrent_paused(int ret, disk_io_job const& j); void on_storage_moved(int ret, disk_io_job const& j); diff --git a/src/disk_io_thread.cpp b/src/disk_io_thread.cpp index b6b2e20a7..22ee12179 100644 --- a/src/disk_io_thread.cpp +++ b/src/disk_io_thread.cpp @@ -125,6 +125,7 @@ namespace libtorrent , boost::function const& f) { TORRENT_ASSERT(!j.callback); + TORRENT_ASSERT(j.storage); boost::mutex::scoped_lock l(m_mutex); std::deque::reverse_iterator i = m_jobs.rbegin(); @@ -220,6 +221,7 @@ namespace libtorrent bool free_buffer = true; try { + TORRENT_ASSERT(j.storage); #ifdef TORRENT_DISK_STATS ptime start = time_now(); #endif @@ -288,6 +290,12 @@ namespace libtorrent #endif j.storage->release_files_impl(); break; + case disk_io_job::delete_files: +#ifdef TORRENT_DISK_STATS + m_log << log_time() << " delete" << std::endl; +#endif + j.storage->delete_files_impl(); + break; } } catch (std::exception& e) diff --git a/src/session.cpp b/src/session.cpp index e0c3ec57a..5bdb6b07e 100755 --- a/src/session.cpp +++ b/src/session.cpp @@ -225,9 +225,9 @@ namespace libtorrent , storage_mode, sc, paused, userdata); } - void session::remove_torrent(const torrent_handle& h) + void session::remove_torrent(const torrent_handle& h, int options) { - m_impl->remove_torrent(h); + m_impl->remove_torrent(h, options); } bool session::listen_on( diff --git a/src/session_impl.cpp b/src/session_impl.cpp index ae86cdaeb..182f935bb 100755 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -481,7 +481,7 @@ namespace detail return 0; } - void checker_impl::remove_torrent(sha1_hash const& info_hash) + void checker_impl::remove_torrent(sha1_hash const& info_hash, int options) { INVARIANT_CHECK; for (std::deque >::iterator i @@ -490,6 +490,8 @@ namespace detail if ((*i)->info_hash == info_hash) { TORRENT_ASSERT((*i)->processing == false); + if (options & session::delete_files) + (*i)->torrent_ptr->delete_files(); m_torrents.erase(i); return; } @@ -500,6 +502,8 @@ namespace detail if ((*i)->info_hash == info_hash) { TORRENT_ASSERT((*i)->processing == false); + if (options & session::delete_files) + (*i)->torrent_ptr->delete_files(); m_processing.erase(i); return; } @@ -1754,7 +1758,7 @@ namespace detail return torrent_handle(this, &m_checker_impl, info_hash); } - void session_impl::remove_torrent(const torrent_handle& h) + void session_impl::remove_torrent(const torrent_handle& h, int options) { if (h.m_ses != this) return; TORRENT_ASSERT(h.m_chk == &m_checker_impl || h.m_chk == 0); @@ -1769,6 +1773,8 @@ namespace detail if (i != m_torrents.end()) { torrent& t = *i->second; + if (options & session::delete_files) + t.delete_files(); t.abort(); if ((!t.is_paused() || t.should_request()) @@ -1815,7 +1821,7 @@ namespace detail if (d != 0) { if (d->processing) d->abort = true; - else m_checker_impl.remove_torrent(h.m_info_hash); + else m_checker_impl.remove_torrent(h.m_info_hash, options); return; } } diff --git a/src/storage.cpp b/src/storage.cpp index 15a869a03..8ec2ee5ae 100755 --- a/src/storage.cpp +++ b/src/storage.cpp @@ -361,6 +361,7 @@ namespace libtorrent } void release_files(); + void delete_files(); void initialize(bool allocate_files); bool move_storage(fs::path save_path); size_type read(char* buf, int slot, int offset, int size); @@ -470,6 +471,38 @@ namespace libtorrent std::vector().swap(m_scratch_buffer); } + void storage::delete_files() + { + // make sure we don't have the files open + m_files.release(this); + std::vector().swap(m_scratch_buffer); + + // delete the files from disk + std::set directories; + typedef std::set::iterator iter_t; + for (torrent_info::file_iterator i = m_info->begin_files(true) + , end(m_info->end_files(true)); i != end; ++i) + { + std::string p = (m_save_path / i->path).string(); + fs::path bp = i->path.branch_path(); + std::pair ret = directories.insert(bp.string()); + while (ret.second && !bp.empty()) + { + bp = bp.branch_path(); + std::pair ret = directories.insert(bp.string()); + } + std::remove(p.c_str()); + } + + // remove the directories. Reverse order to delete + // subdirectories first + std::for_each(directories.rbegin(), directories.rend() + , bind((int(*)(char const*))&std::remove, bind(&std::string::c_str, _1))); + + std::string p = (m_save_path / m_info->name()).string(); + std::remove(p.c_str()); + } + void storage::write_resume_data(entry& rd) const { std::vector > file_sizes @@ -981,6 +1014,15 @@ namespace libtorrent m_io_thread.add_job(j, handler); } + void piece_manager::async_delete_files( + boost::function const& handler) + { + disk_io_job j; + j.storage = this; + j.action = disk_io_job::delete_files; + m_io_thread.add_job(j, handler); + } + void piece_manager::async_move_storage(fs::path const& p , boost::function const& handler) { @@ -1063,11 +1105,6 @@ namespace libtorrent return m_storage->hash_for_slot(slot, ph, m_info->piece_size(piece)); } - void piece_manager::release_files_impl() - { - m_storage->release_files(); - } - bool piece_manager::move_storage_impl(fs::path const& save_path) { if (m_storage->move_storage(save_path)) @@ -1077,6 +1114,7 @@ namespace libtorrent } return false; } + void piece_manager::export_piece_map( std::vector& p, std::vector const& have) const { diff --git a/src/torrent.cpp b/src/torrent.cpp index a81f6ba73..742c1c6de 100755 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -1032,6 +1032,16 @@ namespace libtorrent m_announce_timer.cancel(); } + void torrent::on_files_deleted(int ret, disk_io_job const& j) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + + if (alerts().should_post(alert::warning)) + { + alerts().post_alert(torrent_deleted_alert(get_handle(), "files deleted")); + } + } + void torrent::on_files_released(int ret, disk_io_job const& j) { /* @@ -2549,6 +2559,29 @@ namespace libtorrent return limit; } + void torrent::delete_files() + { +#if defined(TORRENT_VERBOSE_LOGGING) + for (peer_iterator i = m_connections.begin(); + i != m_connections.end(); ++i) + { + (*i->second->m_logger) << "*** DELETING FILES IN TORRENT\n"; + } +#endif + + disconnect_all(); + m_paused = true; + // tell the tracker that we stopped + m_event = tracker_request::stopped; + + if (m_owning_storage.get()) + { + TORRENT_ASSERT(m_storage); + m_storage->async_delete_files( + bind(&torrent::on_files_deleted, shared_from_this(), _1, _2)); + } + } + void torrent::pause() { INVARIANT_CHECK; diff --git a/test/test_swarm.cpp b/test/test_swarm.cpp index 22b171b34..077ab2e96 100644 --- a/test/test_swarm.cpp +++ b/test/test_swarm.cpp @@ -9,6 +9,7 @@ #include "setup_transfer.hpp" using boost::filesystem::remove_all; +using boost::filesystem::exists; void test_swarm() { @@ -113,6 +114,11 @@ void test_swarm() TEST_CHECK(std::fabs(average2 - float(rate_limit)) < 3000.f); TEST_CHECK(std::fabs(average3 - float(rate_limit)) < 3000.f); if (tor2.is_seed() && tor3.is_seed()) std::cerr << "done\n"; + + // make sure the files are deleted + ses1.remove_torrent(tor1, session::delete_files); + ses2.remove_torrent(tor2, session::delete_files); + ses3.remove_torrent(tor3, session::delete_files); } int test_main() @@ -127,6 +133,11 @@ int test_main() test_swarm(); + test_sleep(2000); + TEST_CHECK(!exists("./tmp1/temporary")); + TEST_CHECK(!exists("./tmp2/temporary")); + TEST_CHECK(!exists("./tmp3/temporary")); + remove_all("./tmp1"); remove_all("./tmp2"); remove_all("./tmp3");