diff --git a/libtorrent/include/libtorrent/alert_types.hpp b/libtorrent/include/libtorrent/alert_types.hpp index a46eb7817..6f41758fa 100755 --- a/libtorrent/include/libtorrent/alert_types.hpp +++ b/libtorrent/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/libtorrent/include/libtorrent/aux_/session_impl.hpp b/libtorrent/include/libtorrent/aux_/session_impl.hpp index a936974f5..bff8e3387 100644 --- a/libtorrent/include/libtorrent/aux_/session_impl.hpp +++ b/libtorrent/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/libtorrent/include/libtorrent/disk_io_thread.hpp b/libtorrent/include/libtorrent/disk_io_thread.hpp index b893aaf60..bd6d5e1ba 100644 --- a/libtorrent/include/libtorrent/disk_io_thread.hpp +++ b/libtorrent/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/libtorrent/include/libtorrent/session.hpp b/libtorrent/include/libtorrent/session.hpp index 446fe74c8..5093e2336 100755 --- a/libtorrent/include/libtorrent/session.hpp +++ b/libtorrent/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/libtorrent/include/libtorrent/storage.hpp b/libtorrent/include/libtorrent/storage.hpp index bad4daca6..68a81c75b 100755 --- a/libtorrent/include/libtorrent/storage.hpp +++ b/libtorrent/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() {} }; @@ -162,10 +166,6 @@ namespace libtorrent boost::intrusive_ptr ti , fs::path const& path, file_pool& fp); - // returns true if the filesystem the path relies on supports - // sparse files or automatic zero filling of files. - TORRENT_EXPORT bool supports_sparse_files(fs::path const& p); - struct disk_io_thread; class TORRENT_EXPORT piece_manager @@ -230,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); @@ -274,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); @@ -289,9 +294,6 @@ namespace libtorrent storage_mode_t m_storage_mode; - // a bitmask representing the pieces we have - std::vector m_have_piece; - boost::intrusive_ptr m_info; // slots that haven't had any file storage allocated diff --git a/libtorrent/include/libtorrent/torrent.hpp b/libtorrent/include/libtorrent/torrent.hpp index 7d5520b18..60140f2c2 100755 --- a/libtorrent/include/libtorrent/torrent.hpp +++ b/libtorrent/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/libtorrent/src/disk_io_thread.cpp b/libtorrent/src/disk_io_thread.cpp index b6b2e20a7..22ee12179 100644 --- a/libtorrent/src/disk_io_thread.cpp +++ b/libtorrent/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/libtorrent/src/session.cpp b/libtorrent/src/session.cpp index e0c3ec57a..5bdb6b07e 100755 --- a/libtorrent/src/session.cpp +++ b/libtorrent/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/libtorrent/src/session_impl.cpp b/libtorrent/src/session_impl.cpp index ae86cdaeb..182f935bb 100755 --- a/libtorrent/src/session_impl.cpp +++ b/libtorrent/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/libtorrent/src/storage.cpp b/libtorrent/src/storage.cpp index 15a869a03..6671e38e9 100755 --- a/libtorrent/src/storage.cpp +++ b/libtorrent/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 { @@ -1495,7 +1533,6 @@ namespace libtorrent return false; } - error_msg = "empty piece map"; m_state = state_full_check; return false; } diff --git a/libtorrent/src/torrent.cpp b/libtorrent/src/torrent.cpp index a81f6ba73..742c1c6de 100755 --- a/libtorrent/src/torrent.cpp +++ b/libtorrent/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/src/core.py b/src/core.py index a21655855..b9d0d0c78 100644 --- a/src/core.py +++ b/src/core.py @@ -169,7 +169,7 @@ class torrent_info: self.trackers = "" self.delete_me = False # set this to true, to delete it on next sync - + self.del_data = False # set this to true, to delete data on next sync # The persistent state of the torrent system. Everything in this will be pickled @@ -360,31 +360,12 @@ class Manager: temp = self.unique_IDs[unique_ID] temp_fileinfo = deluge_core.get_file_info(unique_ID) - self.remove_torrent_ns(unique_ID) + self.remove_torrent_ns(unique_ID, data_also) self.sync() # Remove .torrent file if asked to do so if torrent_also: os.remove(temp.filename) - - # Remove data, if asked to do so - if data_also: - # Must be done AFTER the torrent is removed - # Note: can this be to the trash? - for filedata in temp_fileinfo: - filename = filedata['path'] - if filename.find(os.sep) != -1: - # This is a file inside a directory inside the torrent. We can delete the - # directory itself, save time - try: - shutil.rmtree(os.path.dirname(os.path.join(temp.save_dir, filename))) - except OSError: # Perhaps it wasn't downloaded - pass - # Perhaps this is just a file, try to remove it - try: - os.remove(os.path.join(temp.save_dir, filename)) - except OSError: - pass # No file just means it wasn't downloaded, we can continue # A function to try and reload a torrent from a previous session. This is # used in the event that Deluge crashes and a blank state is loaded. @@ -839,9 +820,9 @@ class Manager: new_torrent = torrent_info(full_new_name, save_dir, compact) self.state.torrents[new_torrent] = None - def remove_torrent_ns(self, unique_ID): + def remove_torrent_ns(self, unique_ID, data_also): self.unique_IDs[unique_ID].delete_me = True - + self.unique_IDs[unique_ID].del_data = data_also # Sync the state.torrents and unique_IDs lists with the core # ___ALL syncing code with the core is here, and ONLY here___ @@ -886,7 +867,8 @@ class Manager: for unique_ID in self.unique_IDs.keys(): # print torrent if self.unique_IDs[unique_ID].delete_me: - deluge_core.remove_torrent(unique_ID) + deluge_core.remove_torrent(unique_ID, \ + self.unique_IDs[unique_ID].del_data) to_delete.append(unique_ID) for unique_ID in to_delete: diff --git a/src/deluge_core.cpp b/src/deluge_core.cpp index 8133c7e1c..c85838489 100644 --- a/src/deluge_core.cpp +++ b/src/deluge_core.cpp @@ -265,13 +265,12 @@ boost::filesystem::path const& save_path) } -void internal_remove_torrent(long index) +void internal_remove_torrent(long index, int delete_files) { assert(index < M_torrents->size()); torrent_handle& h = M_torrents->at(index).handle; - - M_ses->remove_torrent(h); + M_ses->remove_torrent(h, delete_files); torrents_t_iterator it = M_torrents->begin() + index; M_torrents->erase(it); @@ -728,15 +727,15 @@ static PyObject *torrent_move_storage(PyObject *self, PyObject *args) static PyObject *torrent_remove_torrent(PyObject *self, PyObject *args) { - python_long unique_ID; - if (!PyArg_ParseTuple(args, "i", &unique_ID)) + python_long unique_ID, delete_files; + if (!PyArg_ParseTuple(args, "ii", &unique_ID, &delete_files)) return NULL; long index = get_index_from_unique_ID(unique_ID); if (PyErr_Occurred()) return NULL; - internal_remove_torrent(index); + internal_remove_torrent(index, delete_files); Py_INCREF(Py_None); return Py_None; }