(trunk) #1029: When removing local data only remove data from the torrent
This commit is contained in:
parent
fe7751c9d0
commit
303356d110
|
@ -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
|
||||
|
|
|
@ -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*
|
||||
|
|
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue