]> granicus.if.org Git - transmission/commitdiff
(trunk) #1029: When removing local data only remove data from the torrent
authorCharles Kerr <charles@transmissionbt.com>
Tue, 23 Dec 2008 16:04:11 +0000 (16:04 +0000)
committerCharles Kerr <charles@transmissionbt.com>
Tue, 23 Dec 2008 16:04:11 +0000 (16:04 +0000)
gtk/tr-torrent.c
gtk/util.c
gtk/util.h
libtransmission/rpcimpl.c
libtransmission/torrent.c
libtransmission/transmission.h

index ac4311712d789c532ac62724144500480e6468ad..b633e0c1c88373b4aa73b6e97257df9c566c775f 100644 (file)
@@ -318,24 +318,7 @@ tr_torrent_set_remove_flag( TrTorrent * gtor,
 void
 tr_torrent_delete_files( TrTorrent * gtor )
 {
-    tr_file_index_t i;
-    const tr_info * info = tr_torrent_info( gtor );
-    const char *    stop =
-        tr_torrentGetDownloadDir( tr_torrent_handle( gtor ) );
-
-    for( i = 0; info && i < info->fileCount; ++i )
-    {
-        char * file = g_build_filename( stop, info->files[i].name, NULL );
-        while( strcmp( stop, file ) && strlen( stop ) < strlen( file ) )
-        {
-            char * swap = g_path_get_dirname( file );
-            tr_file_trash_or_unlink( file );
-            g_free( file );
-            file = swap;
-        }
-
-        g_free( file );
-    }
+    tr_torrentDeleteLocalData( tr_torrent_handle( gtor ), tr_file_trash_or_unlink );
 }
 
 void
index 60ad690f55948abf52681a225e4ae7c705eadd7b..2ce9fcd7e39ade872d6ce21f2a1cd5e8d9feb76c 100644 (file)
@@ -514,21 +514,22 @@ tr_object_ref_sink( gpointer object )
     return object;
 }
 
-void
+int
 tr_file_trash_or_unlink( const char * filename )
 {
     if( filename && *filename )
     {
         gboolean trashed = FALSE;
 #ifdef HAVE_GIO
-        GError * err = NULL;
         GFile *  file = g_file_new_for_path( filename );
-        trashed = g_file_trash( file, NULL, &err );
+        trashed = g_file_trash( file, NULL, NULL );
         g_object_unref( G_OBJECT( file ) );
 #endif
         if( !trashed )
             g_unlink( filename );
     }
+
+    return 0;
 }
 
 char*
index 864980efd8b2ad280d48d230d195c408bfdb2cbd..abff0d14128c63ee35b0111eae94ae78eac3fec6 100644 (file)
@@ -132,7 +132,7 @@ gboolean    on_tree_view_button_released( GtkWidget *      view,
 
 gpointer    tr_object_ref_sink( gpointer object );
 
-void        tr_file_trash_or_unlink( const char * filename );
+int         tr_file_trash_or_unlink( const char * filename );
 
 #endif /* GTK_MAJOR_VERSION */
 
index eecdef1dee09b790223691cc315afd82d7160ffe..13fefbee48ce7db8e29206fda38f3b850f31a1c3 100644 (file)
@@ -150,7 +150,7 @@ torrentRemove( tr_session  * session,
         const tr_rpc_callback_status status = notify( session, TR_RPC_TORRENT_REMOVING, tor );
         int64_t deleteFlag;
         if( tr_bencDictFindInt( args_in, "delete-local-data", &deleteFlag ) && deleteFlag )
-            tr_torrentDeleteLocalData( tor );
+            tr_torrentDeleteLocalData( tor, NULL );
         if( !( status & TR_RPC_NOREMOVE ) )
             tr_torrentRemove( tor );
     }
index 9fc15fe65c3f1262320f65a152aa66eb56ff24b1..95962440bb57850a6147d2364c11eb96eb82034b 100644 (file)
@@ -25,6 +25,7 @@
 #include <sys/types.h> /* stat */
 #include <sys/stat.h> /* stat */
 #include <unistd.h> /* stat */
+#include <dirent.h>
 
 #include <assert.h>
 #include <limits.h> /* INT_MAX */
@@ -40,6 +41,8 @@
 #include "fdlimit.h" /* tr_fdFileClose */
 #include "metainfo.h"
 #include "peer-mgr.h"
+#include "platform.h" /* TR_PATH_DELIMITER_STR */
+#include "ptrarray.h"
 #include "ratecontrol.h"
 #include "torrent.h"
 #include "tracker.h"
@@ -1187,21 +1190,6 @@ stopTorrent( void * vtor )
     }
 }
 
-void
-tr_torrentDeleteLocalData( tr_torrent * tor )
-{
-    tr_file_index_t i;
-
-    for( i=0; i<tor->info.fileCount; ++i )
-    {
-        const tr_file * file = &tor->info.files[i];
-        char * path = tr_buildPath( tor->downloadDir, file->name, NULL );
-        tr_fdFileClose( path );
-        unlink( path );
-        tr_free( path );
-    }
-}
-
 void
 tr_torrentStop( tr_torrent * tor )
 {
@@ -1815,3 +1803,166 @@ tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
 
     return bytesLeft;
 }
+
+/****
+*****  Removing the torrent's local data
+****/
+
+static int
+vstrcmp( const void * a, const void * b )
+{
+    return strcmp( a, b );
+}
+
+static int
+compareLongestFirst( const void * a, const void * b )
+{
+    const size_t alen = strlen( a );
+    const size_t blen = strlen( b );
+
+    if( alen != blen )
+        return alen > blen ? -1 : 1;
+
+    return vstrcmp( a, b );
+}
+
+static void
+addDirtyFile( const char  * root,
+              const char  * filename,
+              tr_ptrArray * dirtyFolders )
+{
+    char * dir = tr_dirname( filename );
+
+    /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
+    while (     ( dir != NULL )
+             && ( strlen( root ) <= strlen( dir ) )
+             && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
+    {
+        char * tmp;
+        tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
+        tmp = tr_dirname( dir );
+        tr_free( dir );
+        dir = tmp;
+    }
+
+    tr_free( dir );
+}
+
+static void
+walkLocalData( const tr_torrent * tor,
+               const char       * root,
+               const char       * dir,
+               const char       * base,
+               tr_ptrArray      * torrentFiles,
+               tr_ptrArray      * folders,
+               tr_ptrArray      * dirtyFolders )
+{
+    int i;
+    struct stat sb;
+    char * buf;
+
+    buf = tr_buildPath( dir, base, NULL );
+    i = stat( buf, &sb );
+    if( !i )
+    {
+        DIR * odir = NULL;
+
+        if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
+        {
+            struct dirent *d;
+            tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
+            for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
+                if( d->d_name && d->d_name[0] != '.' ) /* skip dotfiles */
+                    walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders );
+            closedir( odir );
+        }
+        else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
+        {
+            const char * sub = buf + strlen( tor->downloadDir ) + strlen( TR_PATH_DELIMITER_STR );
+            const tr_bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
+            if( !isTorrentFile )
+                addDirtyFile( root, buf, dirtyFolders );
+        }
+    }
+
+    tr_free( buf );
+}
+
+static void
+deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
+{
+    int i, n;
+    char ** s;
+    tr_file_index_t f;
+    tr_ptrArray * torrentFiles = tr_ptrArrayNew( );
+    tr_ptrArray * folders = tr_ptrArrayNew( );
+    tr_ptrArray * dirtyFolders = tr_ptrArrayNew( ); /* dirty == contains non-torrent files */
+
+    const char * firstFile = tor->info.files[0].name;
+    const char * cpch = strchr( firstFile, TR_PATH_DELIMITER );
+    char * tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
+    char * root = tr_buildPath( tor->downloadDir, tmp, NULL );
+
+    for( f=0; f<tor->info.fileCount; ++f )
+        tr_ptrArrayInsertSorted( torrentFiles, tor->info.files[f].name, vstrcmp );
+
+    /* build the set of folders and dirtyFolders */
+    walkLocalData( tor, root, root, NULL, torrentFiles, folders, dirtyFolders );
+
+    /* close all the files because we're about to delete them */
+    for( f=0; f<tor->info.fileCount; ++f ) {
+        char * path = tr_buildPath( tor->downloadDir, tor->info.files[f].name, NULL );
+        tr_fdFileClose( path );
+        tr_free( path );
+    }
+
+    /* try to remove entire folders first, so that the recycle bin will be tidy */
+    s = (char**) tr_ptrArrayPeek( folders, &n );
+    for( i=0; i<n; ++i )
+        if( tr_ptrArrayFindSorted( dirtyFolders, s[i], vstrcmp ) == NULL )
+            fileFunc( s[i] );
+
+    /* now blow away any remaining torrent files, such torrent files in dirty folders */
+    for( f=0; f<tor->info.fileCount; ++f ) {
+        char * path = tr_buildPath( tor->downloadDir, tor->info.files[f].name, NULL );
+        fileFunc( path );
+        tr_free( path );
+    }
+
+    /* Now clean out the directories left empty from the previous step.
+     * Work from deepest to shallowest s.t. lower folders
+     * won't prevent the upper folders from being deleted */
+    {
+        tr_ptrArray * cleanFolders = tr_ptrArrayNew( );
+        s = (char**) tr_ptrArrayPeek( folders, &n );
+        for( i=0; i<n; ++i )
+            if( tr_ptrArrayFindSorted( dirtyFolders, s[i], vstrcmp ) == NULL )
+                tr_ptrArrayInsertSorted( cleanFolders, s[i], compareLongestFirst );
+        s = (char**) tr_ptrArrayPeek( cleanFolders, &n );
+        for( i=0; i<n; ++i )
+            fileFunc( s[i] );
+        tr_ptrArrayFree( cleanFolders, NULL );
+    }
+
+    /* cleanup */
+    tr_ptrArrayFree( dirtyFolders, tr_free );
+    tr_ptrArrayFree( folders, tr_free );
+    tr_ptrArrayFree( torrentFiles, NULL );
+    tr_free( root );
+    tr_free( tmp );
+}
+
+void
+tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
+{
+    if( fileFunc == NULL )
+        fileFunc = unlink;
+
+    if( tor->info.fileCount > 1 )
+        deleteLocalData( tor, fileFunc );
+    else {
+        char * path = tr_buildPath( tor->downloadDir, tor->info.files[0].name, NULL );
+        fileFunc( path );
+        tr_free( path );
+    }
+}
index b085cf56c7e7a850eb8b7ec492d7a75196aa3b69..a1948c6e38ede05ceea3abe28b3bcf92ad7c00c8 100644 (file)
@@ -795,8 +795,16 @@ void tr_torrentStart( tr_torrent * torrent );
 /** @brief Stop (pause) a torrent */
 void tr_torrentStop( tr_torrent * torrent );
 
-/** @brief Deletes the torrent data stored on disk. */
-void tr_torrentDeleteLocalData( tr_torrent * torrent );
+typedef int tr_fileFunc( const char * filename );
+
+/**
+ * @brief Deletes the torrent's local data.
+ * @param torrent
+ * @param fileFunc Pass in "unlink" to destroy the files or, on platforms with
+ *                 recycle bins, pass in a function that uses it instead.
+ *                 tr_torrentDeleteLocalData() ignores fileFunc's return value.
+ */
+void tr_torrentDeleteLocalData( tr_torrent * torrent,  tr_fileFunc fileFunc );
 
 /**
  * @brief Iterate through the torrents.