From e82cb2188ea44fb217102c5487ff620460467fd3 Mon Sep 17 00:00:00 2001 From: Christophe Dumez Date: Thu, 23 Aug 2007 14:04:53 +0000 Subject: [PATCH] - Made torrent deletion from hard-drive safer --- Changelog | 1 + TODO | 25 +------ src/bittorrent.cpp | 6 +- src/deleteThread.h | 58 ++++++--------- src/misc.h | 157 +++++++++++++++++++++-------------------- src/qtorrenthandle.cpp | 15 ++++ src/qtorrenthandle.h | 3 + 7 files changed, 125 insertions(+), 140 deletions(-) diff --git a/Changelog b/Changelog index ca67fb395..f0c51f7c2 100644 --- a/Changelog +++ b/Changelog @@ -49,6 +49,7 @@ - BUGFIX: Improved the way menu icons are installed to avoid problems on some systems - BUGFIX: Improved incremental download - BUGFIX: Improved unicode support + - BUGFIX: Made torrent deletion from hard-drive safer - COSMETIC: Redesigned torrent properties a little - COSMETIC: Redesigned options a little - COSMETIC: Display more logs messages concerning features diff --git a/TODO b/TODO index 6caee9250..22bdbf0cf 100644 --- a/TODO +++ b/TODO @@ -71,26 +71,5 @@ LANGUAGES UPDATED: - Russian *BETA5* - Korean *BETA5* -beta4->beta5 changelog: -- FEATURE: Supports Bittorrent FAST extension -- FEATURE: Improved code handling torrents that have just finished checking -- FEATURE: Improved progress column sorting code -- FEATURE: Allow to remove url seeds, even hard-coded ones -- FEATURE: Improved code for handling of finished torrents -- FEATURE: Optimized download list refreshing a little -- FEATURE: Big code cleanup -- BUGFIX: Wait for torrent_paused_alert before saving fast resume data on exit -- BUGFIX: Wait for torrent_paused_alert before reloading a torrent for full allocation mode -- BUFFIG: Fixed overflow causing ratio data to be negative -- BUGFIX: Fixed progress column delayed sorting (after torrent finished checking) -- BUGFIX: Finished torrents were still displayed as checking when paused by libtorrent on full disk (hitted an assert) -- BUGFIX: Fixed the way icons are installed to avoid problems on some systems -- BUGFIX: Fixed qBittorrent version in .desktop file -- BUGFIX: Fixed session ratio value (was either 10. or 1.) -- BUGFIX: Improved incremental download -- BUGFIX: Fixed preview from seeding list -- BUGFIX: Fixed Alt+3 & Ctrl+F keyboard shortcuts for third tab -- BUGFIX: Improved unicode support -- BUGFIX: Add torrents in pause before applying settings to avoid race conditions -- I18N: Updated Italian, Polish, Portuguese, Brazilian, German, Russian, Korean and Spanish translations -- COSMETIC: Improved the way progress bars are rendered +beta5->beta6 changelog: +- BUGFIX: Made torrent deletion from hard-drive safer diff --git a/src/bittorrent.cpp b/src/bittorrent.cpp index 954c465ad..ad27dbeb1 100644 --- a/src/bittorrent.cpp +++ b/src/bittorrent.cpp @@ -166,6 +166,10 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) { } QString savePath = h.save_path(); QString fileName = h.name(); + QStringList files_path; + if(permanent){ + files_path = h.files_path(); + } // Remove it from session s->remove_torrent(h.get_torrent_handle()); // Remove it from torrent backup directory @@ -208,7 +212,7 @@ void bittorrent::deleteTorrent(QString hash, bool permanent) { // Remove from Hard drive qDebug("Removing this on hard drive: %s", qPrintable(savePath+QDir::separator()+fileName)); // Deleting in a thread to avoid GUI freeze - deleter->deletePath(savePath+QDir::separator()+fileName); + deleter->deleteTorrent(savePath, files_path); } } diff --git a/src/deleteThread.h b/src/deleteThread.h index f9387303d..2e0a9d56a 100644 --- a/src/deleteThread.h +++ b/src/deleteThread.h @@ -26,17 +26,19 @@ #include #include #include +#include #include "misc.h" class subDeleteThread : public QThread { Q_OBJECT private: - QString path; + QString save_path; + QStringList files_path; bool abort; public: - subDeleteThread(QObject *parent, QString path) : QThread(parent), path(path){ + subDeleteThread(QObject *parent, QString save_path, QStringList files_path) : QThread(parent), save_path(save_path), files_path(files_path){ abort = false; } @@ -47,16 +49,15 @@ class subDeleteThread : public QThread { signals: // For subthreads - void deletionSuccessST(subDeleteThread* st, QString path); - void deletionFailureST(subDeleteThread* st, QString path); + void deletionSuccessST(subDeleteThread* st); + void deletionFailureST(subDeleteThread* st); protected: void run(){ - if(misc::removePath(path)) - emit deletionSuccessST(this, path); + if(misc::removeTorrentSavePath(save_path, files_path)) + emit deletionSuccessST(this); else - emit deletionFailureST(this, path); - qDebug("deletion completed for %s", (const char*)path.toUtf8()); + emit deletionFailureST(this); } }; @@ -64,16 +65,12 @@ class deleteThread : public QThread { Q_OBJECT private: - QStringList path_list; + QList > torrents_list; QMutex mutex; QWaitCondition condition; bool abort; QList subThreads; - signals: - void deletionSuccess(QString path); - void deletionFailure(QString path); - public: deleteThread(QObject* parent) : QThread(parent){ abort = false; @@ -88,9 +85,9 @@ class deleteThread : public QThread { wait(); } - void deletePath(QString path){ + void deleteTorrent(QString save_path, QStringList files_path){ QMutexLocker locker(&mutex); - path_list << path; + torrents_list << QPair(save_path, files_path); if(!isRunning()){ start(); }else{ @@ -104,18 +101,14 @@ class deleteThread : public QThread { if(abort) return; mutex.lock(); - if(path_list.size() != 0){ - QString path = path_list.takeFirst(); + if(torrents_list.size() != 0){ + QPair torrent = torrents_list.takeFirst(); mutex.unlock(); - if(QFile::exists(path)){ - subDeleteThread *st = new subDeleteThread(0, path); - subThreads << st; - connect(st, SIGNAL(deletionSuccessST(subDeleteThread*, QString)), this, SLOT(propagateDeletionSuccess(subDeleteThread*, QString))); - connect(st, SIGNAL(deletionFailureST(subDeleteThread*, QString)), this, SLOT(propagateDeletionFailure(subDeleteThread*, QString))); - st->start(); - }else{ - qDebug("%s does not exist, nothing to delete", (const char*)path.toUtf8()); - } + subDeleteThread *st = new subDeleteThread(0, torrent.first, torrent.second); + subThreads << st; + connect(st, SIGNAL(deletionSuccessST(subDeleteThread*)), this, SLOT(deleteSubThread(subDeleteThread*))); + connect(st, SIGNAL(deletionFailureST(subDeleteThread*)), this, SLOT(deleteSubThread(subDeleteThread*))); + st->start(); }else{ condition.wait(&mutex); mutex.unlock(); @@ -123,22 +116,11 @@ class deleteThread : public QThread { } } protected slots: - void propagateDeletionSuccess(subDeleteThread* st, QString path){ + void deleteSubThread(subDeleteThread* st){ int index = subThreads.indexOf(st); Q_ASSERT(index != -1); subThreads.removeAt(index); delete st; - emit deletionSuccess(path); - qDebug("%s was successfully deleted", (const char*)path.toUtf8()); - } - - void propagateDeletionFailure(subDeleteThread* st, QString path){ - int index = subThreads.indexOf(st); - Q_ASSERT(index != -1); - subThreads.removeAt(index); - delete st; - emit deletionFailure(path); - std::cerr << "Could not delete path: " << (const char*)path.toUtf8() << ". Check if qBittorrent has the required rights.\n"; } }; diff --git a/src/misc.h b/src/misc.h index ff71a0bde..0b03ac25b 100644 --- a/src/misc.h +++ b/src/misc.h @@ -27,13 +27,14 @@ #include #include #include +#include #include #include #include #include #include -// #include "qtorrenthandle.h" +#include "qtorrenthandle.h" using namespace libtorrent; #define MAX_CHAR_TMP 128 @@ -143,89 +144,89 @@ class misc : public QObject{ return qBtPath; } - static bool removePath(QString path) { - qDebug((QString::fromUtf8("file to delete:") + path).toUtf8()); - if(!QFile::remove(path)) { - // Probably a folder - QDir current_dir(path); - if(current_dir.exists()) { - //Remove sub items - QStringList subItems = current_dir.entryList(); - QString item; - foreach(item, subItems) { - if(item != QString::fromUtf8(".") && item != QString::fromUtf8("..")) { - qDebug("-> Removing "+(path+QDir::separator()+item).toUtf8()); - removePath(path+QDir::separator()+item); - } - } - // Remove empty folder - if(current_dir.rmdir(path)) { - return true; - }else{ - return false; +// Not used anymore because it is not safe +// static bool removePath(QString path) { +// qDebug((QString::fromUtf8("file to delete:") + path).toUtf8()); +// if(!QFile::remove(path)) { +// // Probably a folder +// QDir current_dir(path); +// if(current_dir.exists()) { +// //Remove sub items +// QStringList subItems = current_dir.entryList(); +// QString item; +// foreach(item, subItems) { +// if(item != QString::fromUtf8(".") && item != QString::fromUtf8("..")) { +// qDebug("-> Removing "+(path+QDir::separator()+item).toUtf8()); +// removePath(path+QDir::separator()+item); +// } +// } +// // Remove empty folder +// if(current_dir.rmdir(path)) { +// return true; +// }else{ +// return false; +// } +// }else{ +// return false; +// } +// } +// return true; +// } + + // safe function to remove a torrent from hard-drive + static bool removeTorrentSavePath(QString savePath, QStringList filesPath) { + bool success = true; + QDir saveDir(savePath); + QString path; + // Check how many file there are + if(filesPath.size() == 1){ + // Only one file, not in a folder + path = filesPath.first(); + if(QFile::exists(path)) { + if(QFile::remove(path)){ + qDebug("Deleted only file in torrent at %s", path.toUtf8().data()); + } else { + std::cerr << "Could not delete only file in torrent at " << path.toUtf8().data() << '\n'; + success = false; } }else{ - return false; + // File didn't exist, nothing to do + qDebug("Only file %s did not exist, nothing to delete", path.toUtf8().data()); + } + // Try to remove parent folder if empty (and not save_dir) + QFileInfo fi(path); + QDir parentFolder = fi.absoluteDir(); + while(parentFolder != saveDir) { + qDebug("trying to remove parent folder: %s", parentFolder.absolutePath().toUtf8().data()); + if(!saveDir.rmdir(parentFolder.absolutePath())) break; + parentFolder.cdUp(); + } + return success; + } + // Torrent has several files in a subFolder + foreach(path, filesPath) { + if(QFile::exists(path)) { + if(QFile::remove(path)){ + qDebug("Deleted file in torrent at %s", path.toUtf8().data()); + } else { + std::cerr << "Could not delete file in torrent at " << path.toUtf8().data() << '\n'; + success = false; + } + } else { + qDebug("File %s did not exist, nothing to delete", path.toUtf8().data()); + } + // Try to remove parent folder if empty (and not save_dir) + QFileInfo fi(path); + QDir parentFolder = fi.absoluteDir(); + while(parentFolder != saveDir) { + qDebug("trying to remove parent folder: %s", parentFolder.absolutePath().toUtf8().data()); + if(!saveDir.rmdir(parentFolder.absolutePath())) break; + parentFolder.cdUp(); } } - return true; + return success; } - // FIXME: Not used yet because it is buggy -// static bool removeTorrentSavePath(QTorrentHandle h) { -// bool success = true; -// QString savePath = QDir::cleanPath(h.save_path() + QDir::separator()); -// unsigned int nbFiles = h.num_files(); -// QDir saveDir(savePath); -// // Check how many file there are -// if(nbFiles == 1){ -// // Only one file, not in a folder -// QStringList filters; -// filters << h.file_at(0); -// QFileInfoList files = saveDir.entryInfoList(filters, QDir::Files); -// QFileInfo file; -// foreach(file, files){ -// if(file.fileName() == h.file_at(0) && !file.isSymLink()){ -// if(saveDir.remove(h.file_at(0))){ -// qDebug("Deleted only file in torrent at %s", (savePath + h.file_at(0)).toUtf8().data()); -// return true; -// }else{ -// return false; -// } -// } -// } -// std::cerr << "Could not delete only file in torrent at " << (savePath + h.file_at(0)).toUtf8().data() << '\n'; -// return false; -// } -// QDir subDir(savePath + h.name()); -// // Torrent has several files in a subFolder -// for(unsigned int i=0; i +#include #include +#include #include #include "misc.h" #include "qtorrenthandle.h" @@ -202,6 +204,19 @@ size_type QTorrentHandle::total_payload_upload() { return s.total_payload_upload; } +// Return a list of absolute paths corresponding +// to all files in a torrent +QStringList QTorrentHandle::files_path() const { + QString saveDir = misc::toQString(h.save_path().string()) + QDir::separator(); + QStringList res; + torrent_info::file_iterator fi = t.begin_files(); + while(fi != t.end_files()) { + res << QDir::cleanPath(saveDir + misc::toQString(fi->path.string())); + fi++; + } + return res; +} + // // Setters // diff --git a/src/qtorrenthandle.h b/src/qtorrenthandle.h index c16ba34ba..26ccfad11 100644 --- a/src/qtorrenthandle.h +++ b/src/qtorrenthandle.h @@ -23,9 +23,11 @@ #define QTORRENTHANDLE_H #include +#include using namespace libtorrent; class QString; +class QStringList; // A wrapper for torrent_handle in libtorrent // to interact well with Qt types @@ -81,6 +83,7 @@ class QTorrentHandle { void file_progress(std::vector& fp); size_type total_payload_download(); size_type total_payload_upload(); + QStringList files_path() const; // // Setters