* Better handling of resume after crash/reboot. Sponsored by anonymous source.

git-svn-id: svn://rakshasa.no/libtorrent/trunk/rtorrent@1126 e378c898-3ddf-0310-93e7-cc216c733640
This commit is contained in:
rakshasa
2010-02-08 19:41:46 +00:00
parent 2d3e12387d
commit 222bf7ae8f
7 changed files with 98 additions and 42 deletions
+2 -1
View File
@@ -64,8 +64,9 @@ class timer {
static timer current(); static timer current();
static int64_t current_seconds() { return current().seconds(); } static int64_t current_seconds() { return current().seconds(); }
static int64_t current_usec() { return current().usec(); } static int64_t current_usec() { return current().usec(); }
static timer from_minutes(uint32_t minutes) { return rak::timer((uint64_t)minutes * 60 * 1000000); }
static timer from_seconds(uint32_t seconds) { return rak::timer((uint64_t)seconds * 1000000); } static timer from_seconds(uint32_t seconds) { return rak::timer((uint64_t)seconds * 1000000); }
static timer from_milliseconds(uint32_t seconds) { return rak::timer((uint64_t)seconds * 1000); } static timer from_milliseconds(uint32_t msec) { return rak::timer((uint64_t)msec * 1000); }
static timer max() { return std::numeric_limits<int64_t>::max(); } static timer max() { return std::numeric_limits<int64_t>::max(); }
+2 -1
View File
@@ -593,7 +593,8 @@ initialize_command_download() {
ADD_CD_F_VOID("erase", rak::make_mem_fun(control->core()->download_list(), &core::DownloadList::erase_ptr)); ADD_CD_F_VOID("erase", rak::make_mem_fun(control->core()->download_list(), &core::DownloadList::erase_ptr));
ADD_CD_F_VOID("check_hash", rak::make_mem_fun(control->core()->download_list(), &core::DownloadList::check_hash)); ADD_CD_F_VOID("check_hash", rak::make_mem_fun(control->core()->download_list(), &core::DownloadList::check_hash));
ADD_CD_F_VOID("save_session", rak::make_mem_fun(control->core()->download_store(), &core::DownloadStore::save)); ADD_CD_F_VOID("save_resume", rak::make_mem_fun(control->core()->download_store(), &core::DownloadStore::save_resume));
ADD_CD_F_VOID("save_full_session", rak::make_mem_fun(control->core()->download_store(), &core::DownloadStore::save_full));
ADD_CD_F_VOID("update_priorities", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::update_priorities))); ADD_CD_F_VOID("update_priorities", rak::on(std::mem_fun(&core::Download::download), std::mem_fun(&torrent::Download::update_priorities)));
+24 -3
View File
@@ -42,6 +42,7 @@
#include <stdexcept> #include <stdexcept>
#include <rak/path.h> #include <rak/path.h>
#include <torrent/object.h> #include <torrent/object.h>
#include <torrent/object_stream.h>
#include <torrent/exceptions.h> #include <torrent/exceptions.h>
#include <torrent/rate.h> #include <torrent/rate.h>
#include <torrent/resume.h> #include <torrent/resume.h>
@@ -161,6 +162,23 @@ DownloadFactory::receive_commit() {
receive_success(); receive_success();
} }
static bool
download_factory_add_stream(torrent::Object* root, const char* key, const char* filename) {
std::fstream stream(filename, std::ios::in | std::ios::binary);
if (!stream.is_open())
return false;
torrent::Object obj;
stream >> obj;
if (!stream.good())
return false;
root->insert_key_swap(key, obj);
return true;
}
void void
DownloadFactory::receive_success() { DownloadFactory::receive_success() {
if (m_stream == NULL) if (m_stream == NULL)
@@ -177,15 +195,19 @@ DownloadFactory::receive_success() {
torrent::Object* root = download->bencode(); torrent::Object* root = download->bencode();
if (!m_session) { if (m_session) {
download_factory_add_stream(root, "rtorrent", (rak::path_expand(m_uri) + ".rtorrent").c_str());
download_factory_add_stream(root, "libtorrent_resume", (rak::path_expand(m_uri) + ".libtorrent_resume").c_str());
} else {
// We only allow session torrents to keep their // We only allow session torrents to keep their
// 'rtorrent/libtorrent' sections. The "fast_resume" section // 'rtorrent/libtorrent' sections. The "fast_resume" section
// should be safe to keep. // should be safe to keep.
root->erase_key("rtorrent"); root->erase_key("rtorrent");
root->erase_key("libtorrent");
} }
torrent::Object* rtorrent = &root->insert_preserve_copy("rtorrent", torrent::Object::create_map()).first->second; torrent::Object* rtorrent = &root->insert_preserve_copy("rtorrent", torrent::Object::create_map()).first->second;
torrent::Object& resumeObject = root->insert_preserve_copy("libtorrent_resume", torrent::Object::create_map()).first->second;
initialize_rtorrent(download, rtorrent); initialize_rtorrent(download, rtorrent);
@@ -233,7 +255,6 @@ DownloadFactory::receive_success() {
rpc::call_command("d.set_peer_exchange", rpc::call_command_value("get_peer_exchange"), rpc::make_target(download)); rpc::call_command("d.set_peer_exchange", rpc::call_command_value("get_peer_exchange"), rpc::make_target(download));
torrent::Object& resumeObject = root->insert_preserve_copy("libtorrent_resume", torrent::Object::create_map()).first->second;
torrent::resume_load_addresses(*download->download(), resumeObject); torrent::resume_load_addresses(*download->download(), resumeObject);
torrent::resume_load_file_priorities(*download->download(), resumeObject); torrent::resume_load_file_priorities(*download->download(), resumeObject);
torrent::resume_load_tracker_settings(*download->download(), resumeObject); torrent::resume_load_tracker_settings(*download->download(), resumeObject);
+6 -4
View File
@@ -82,7 +82,7 @@ DownloadList::clear() {
void void
DownloadList::session_save() { DownloadList::session_save() {
unsigned int c = std::count_if(begin(), end(), std::bind1st(std::mem_fun(&DownloadStore::save), control->core()->download_store())); unsigned int c = std::count_if(begin(), end(), std::bind1st(std::mem_fun(&DownloadStore::save_resume), control->core()->download_store()));
if (c != size()) if (c != size())
control->core()->push_log("Failed to save session torrents."); control->core()->push_log("Failed to save session torrents.");
@@ -342,7 +342,9 @@ DownloadList::resume(Download* download, int flags) {
// on non-complete downloads after a crash. This shouldn't be // on non-complete downloads after a crash. This shouldn't be
// needed, but for some reason linux 2.6 is very lazy about // needed, but for some reason linux 2.6 is very lazy about
// updating mtime. // updating mtime.
torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"), true); //
// Disabling this due to the new resume code.
// torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"), true);
} }
// If the DHT server is set to auto, start it now. // If the DHT server is set to auto, start it now.
@@ -559,8 +561,8 @@ DownloadList::confirm_finished(Download* download) {
// //
// Obsolete. // Obsolete.
if (!download->is_active() && rpc::call_command_value("get_session_on_completion") != 0) { if (!download->is_active() && rpc::call_command_value("get_session_on_completion") != 0) {
torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume")); // torrent::resume_save_progress(*download->download(), download->download()->bencode()->get_key("libtorrent_resume"));
control->core()->download_store()->save(download); control->core()->download_store()->save_resume(download);
} }
// Send the completed request before resuming so we don't reset the // Send the completed request before resuming so we don't reset the
+56 -30
View File
@@ -99,56 +99,82 @@ DownloadStore::set_path(const std::string& path) {
} }
bool bool
DownloadStore::save(Download* d) { DownloadStore::write_bencode(const std::string& filename, const torrent::Object& obj, uint32_t skip_mask) {
if (!is_enabled())
return true;
std::fstream f((create_filename(d) + ".new").c_str(), std::ios::out | std::ios::trunc);
if (!f.is_open())
return false;
// Move this somewhere else?
d->bencode()->get_key("rtorrent").insert_key("total_uploaded", d->download()->up_rate()->total());
d->bencode()->get_key("rtorrent").insert_key("chunks_done", d->download()->file_list()->completed_chunks());
torrent::Object tmp; torrent::Object tmp;
torrent::Object& resumeObject = d->download()->bencode()->get_key("libtorrent_resume"); std::fstream output(filename.c_str(), std::ios::out | std::ios::trunc);
torrent::resume_save_addresses(*d->download(), resumeObject); if (!output.is_open())
torrent::resume_save_file_priorities(*d->download(), resumeObject);
torrent::resume_save_tracker_settings(*d->download(), resumeObject);
f << *d->bencode();
if (!f.good())
goto download_store_save_error; goto download_store_save_error;
f.close(); torrent::object_write_bencode(&output, &obj, skip_mask);
if (!output.good())
goto download_store_save_error;
output.close();
// Test the new file, to ensure it is a valid bencode string. // Test the new file, to ensure it is a valid bencode string.
f.open((create_filename(d) + ".new").c_str(), std::ios::in); output.open(filename.c_str(), std::ios::in);
output >> tmp;
f >> tmp; if (!output.good())
if (!f.good())
goto download_store_save_error; goto download_store_save_error;
f.close(); output.close();
::rename((create_filename(d) + ".new").c_str(), create_filename(d).c_str());
return true; return true;
download_store_save_error: download_store_save_error:
f.close(); output.close();
return false; return false;
} }
bool
DownloadStore::save(Download* d, int flags) {
if (!is_enabled())
return true;
torrent::Object* resume_base = &d->download()->bencode()->get_key("libtorrent_resume");
torrent::Object* rtorrent_base = &d->download()->bencode()->get_key("rtorrent");
// Move this somewhere else?
rtorrent_base->insert_key("chunks_done", d->download()->file_list()->completed_chunks());
rtorrent_base->insert_key("total_uploaded", d->download()->up_rate()->total());
// Don't save for completed torrents when we've cleared the uncertain_pieces.
torrent::resume_save_progress(*d->download(), *resume_base);
torrent::resume_save_uncertain_pieces(*d->download(), *resume_base);
torrent::resume_save_addresses(*d->download(), *resume_base);
torrent::resume_save_file_priorities(*d->download(), *resume_base);
torrent::resume_save_tracker_settings(*d->download(), *resume_base);
// Temp fixing of all flags, move to a better place:
resume_base->set_flags(torrent::Object::flag_session_data);
rtorrent_base->set_flags(torrent::Object::flag_session_data);
std::string base_filename = create_filename(d);
if (!write_bencode(base_filename + ".libtorrent_resume.new", *resume_base, 0) ||
!write_bencode(base_filename + ".rtorrent.new", *rtorrent_base, 0))
return false;
::rename((base_filename + ".libtorrent_resume.new").c_str(), (base_filename + ".libtorrent_resume").c_str());
::rename((base_filename + ".rtorrent.new").c_str(), (base_filename + ".rtorrent").c_str());
if (!(flags & flag_skip_static) &&
write_bencode(base_filename + ".new", *d->bencode(), torrent::Object::flag_session_data))
::rename((base_filename + ".new").c_str(), base_filename.c_str());
return true;
}
void void
DownloadStore::remove(Download* d) { DownloadStore::remove(Download* d) {
if (!is_enabled()) if (!is_enabled())
return; return;
::unlink((create_filename(d) + ".libtorrent_resume").c_str());
::unlink((create_filename(d) + ".rtorrent").c_str());
::unlink(create_filename(d).c_str()); ::unlink(create_filename(d).c_str());
} }
+6 -1
View File
@@ -51,6 +51,7 @@ class Download;
class DownloadStore { class DownloadStore {
public: public:
static const int flag_skip_static = 0x1;
bool is_enabled() { return m_lockfile.is_locked(); } bool is_enabled() { return m_lockfile.is_locked(); }
@@ -60,7 +61,9 @@ public:
const std::string& path() const { return m_path; } const std::string& path() const { return m_path; }
void set_path(const std::string& path); void set_path(const std::string& path);
bool save(Download* d); bool save(Download* d, int flags);
bool save_full(Download* d) { return save(d, 0); }
bool save_resume(Download* d) { return save(d, flag_skip_static); }
void remove(Download* d); void remove(Download* d);
// Currently shows all entries in the correct format. // Currently shows all entries in the correct format.
@@ -71,6 +74,8 @@ public:
private: private:
std::string create_filename(Download* d); std::string create_filename(Download* d);
bool write_bencode(const std::string& filename, const torrent::Object& obj, uint32_t skip_mask);
std::string m_path; std::string m_path;
utils::Lockfile m_lockfile; utils::Lockfile m_lockfile;
}; };
+2 -2
View File
@@ -211,7 +211,7 @@ main(int argc, char** argv) {
"system.method.insert = event.download.hash_queued,multi\n" "system.method.insert = event.download.hash_queued,multi\n"
"system.method.set_key = event.download.inserted, 1_connect_logs, d.initialize_logs=\n" "system.method.set_key = event.download.inserted, 1_connect_logs, d.initialize_logs=\n"
"system.method.set_key = event.download.inserted_new, 1_prepare, \"branch=d.get_state=,view.set_visible=started,view.set_visible=stopped ;d.save_session=\"\n" "system.method.set_key = event.download.inserted_new, 1_prepare, \"branch=d.get_state=,view.set_visible=started,view.set_visible=stopped ;d.save_full_session=\"\n"
"system.method.set_key = event.download.inserted_session, 1_prepare, \"branch=d.get_state=,view.set_visible=started,view.set_visible=stopped\"\n" "system.method.set_key = event.download.inserted_session, 1_prepare, \"branch=d.get_state=,view.set_visible=started,view.set_visible=stopped\"\n"
"system.method.set_key = event.download.erased, !_download_list, ui.unfocus_download=\n" "system.method.set_key = event.download.erased, !_download_list, ui.unfocus_download=\n"
@@ -290,7 +290,7 @@ main(int argc, char** argv) {
"schedule = view_main,10,10,\"view_sort=main,20\"\n" "schedule = view_main,10,10,\"view_sort=main,20\"\n"
"schedule = view_name,10,10,\"view_sort=name,20\"\n" "schedule = view_name,10,10,\"view_sort=name,20\"\n"
"schedule = session_save,1800,1800,session_save=\n" "schedule = session_save,1200,1200,session_save=\n"
"schedule = low_diskspace,5,60,close_low_diskspace=500M\n" "schedule = low_diskspace,5,60,close_low_diskspace=500M\n"
"schedule = prune_file_status,3600,86400,system.file_status_cache.prune=\n" "schedule = prune_file_status,3600,86400,system.file_status_cache.prune=\n"