From: Charles Kerr Date: Tue, 23 Dec 2008 16:04:11 +0000 (+0000) Subject: (trunk) #1029: When removing local data only remove data from the torrent X-Git-Tag: 1.60~693 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=303356d110dabf92cfe6816e4a555dd8648d551a;p=transmission (trunk) #1029: When removing local data only remove data from the torrent --- diff --git a/gtk/tr-torrent.c b/gtk/tr-torrent.c index ac4311712..b633e0c1c 100644 --- a/gtk/tr-torrent.c +++ b/gtk/tr-torrent.c @@ -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 diff --git a/gtk/util.c b/gtk/util.c index 60ad690f5..2ce9fcd7e 100644 --- a/gtk/util.c +++ b/gtk/util.c @@ -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* diff --git a/gtk/util.h b/gtk/util.h index 864980efd..abff0d141 100644 --- a/gtk/util.h +++ b/gtk/util.h @@ -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 */ diff --git a/libtransmission/rpcimpl.c b/libtransmission/rpcimpl.c index eecdef1de..13fefbee4 100644 --- a/libtransmission/rpcimpl.c +++ b/libtransmission/rpcimpl.c @@ -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 ); } diff --git a/libtransmission/torrent.c b/libtransmission/torrent.c index 9fc15fe65..95962440b 100644 --- a/libtransmission/torrent.c +++ b/libtransmission/torrent.c @@ -25,6 +25,7 @@ #include /* stat */ #include /* stat */ #include /* stat */ +#include #include #include /* 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; iinfo.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; finfo.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; finfo.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; iinfo.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; iinfo.fileCount > 1 ) + deleteLocalData( tor, fileFunc ); + else { + char * path = tr_buildPath( tor->downloadDir, tor->info.files[0].name, NULL ); + fileFunc( path ); + tr_free( path ); + } +} diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h index b085cf56c..a1948c6e3 100644 --- a/libtransmission/transmission.h +++ b/libtransmission/transmission.h @@ -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.