add option to delete just the partfile when removing a torrent

This commit is contained in:
arvidn 2016-03-13 03:50:37 -04:00 committed by arvidn
parent 035f8e98d1
commit 921cbeebed
13 changed files with 157 additions and 71 deletions

View File

@ -84,7 +84,7 @@ namespace libtorrent
, boost::function<void(disk_io_job const*)> const& handler)= 0;
virtual void async_rename_file(piece_manager* storage, int index, std::string const& name
, boost::function<void(disk_io_job const*)> const& handler) = 0;
virtual void async_delete_files(piece_manager* storage
virtual void async_delete_files(piece_manager* storage, int options
, boost::function<void(disk_io_job const*)> const& handler) = 0;
virtual void async_save_resume_data(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler) = 0;

View File

@ -167,6 +167,7 @@ namespace libtorrent
bdecode_node const* check_resume_data;
std::vector<boost::uint8_t>* priorities;
torrent_info* torrent_file;
int delete_options;
} buffer;
// the disk storage this job applies to (if applicable)

View File

@ -311,7 +311,7 @@ namespace libtorrent
void async_release_files(piece_manager* storage
, boost::function<void(disk_io_job const*)> const& handler
= boost::function<void(disk_io_job const*)>());
void async_delete_files(piece_manager* storage
void async_delete_files(piece_manager* storage, int options
, boost::function<void(disk_io_job const*)> const& handler);
void async_check_fastresume(piece_manager* storage
, bdecode_node const* resume_data

View File

@ -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

View File

@ -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;

View File

@ -505,7 +505,7 @@ namespace libtorrent
bool should_check_files() const;
bool delete_files();
bool delete_files(int options);
void peers_erased(std::vector<torrent_peer*> const& peers);
// ============ start deprecation =============

View File

@ -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

View File

@ -1817,6 +1817,7 @@ namespace libtorrent
}
void disk_io_thread::async_delete_files(piece_manager* storage
, int const options
, boost::function<void(disk_io_job const*)> 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;
}

View File

@ -5116,7 +5116,8 @@ retry:
boost::shared_ptr<torrent> tptr = h.m_torrent.lock();
if (!tptr) return;
m_alerts.emplace_alert<torrent_removed_alert>(tptr->get_handle(), tptr->info_hash());
m_alerts.emplace_alert<torrent_removed_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<torrent> tptr, int options)
void session_impl::remove_torrent_impl(boost::shared_ptr<torrent> 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<torrent_delete_failed_alert>())
m_alerts.emplace_alert<torrent_delete_failed_alert>(t.get_handle()

View File

@ -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<void*>(this));
DFLOG(stderr, "[%p] delete_files [%x]\n", static_cast<void*>(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<std::string> directories;
typedef std::set<std::string>::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<iter_t, bool> 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<std::string>::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<void*>(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<std::string> directories;
typedef std::set<std::string>::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<iter_t, bool> 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<std::string>::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<void*>(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<void*>(this)
, ec.ec.message().c_str());

View File

@ -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;

View File

@ -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 \

View File

@ -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;