1
0
Fork 0
mirror of https://github.com/transmission/transmission synced 2024-12-26 09:37:56 +00:00

(trunk libT) #4377 "Incomplete Folder removed when sub-folder of Default Location" -- possible fix.

This commit is contained in:
Jordan Lee 2011-08-11 14:16:29 +00:00
parent 154f2f96af
commit d9541de52d

View file

@ -2682,199 +2682,174 @@ tr_torrentGetBytesLeftToAllocate( const tr_torrent * tor )
***** Removing the torrent's local data ***** 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 bool static bool
isSystemFile( const char * base ) isJunkFile( const char * base )
{ {
int i; int i;
static const char * stockFiles[] = { ".DS_Store", "desktop.ini", "Thumbs.db" }; static const char * files[] = { ".DS_Store", "desktop.ini", "Thumbs.db" };
static const int stockFileCount = sizeof(stockFiles) / sizeof(stockFiles[0]); static const int file_count = sizeof(files) / sizeof(files[0]);
for( i=0; i<stockFileCount; ++i ) for( i=0; i<file_count; ++i )
if( !strcmp( base, stockFiles[i] ) ) if( !strcmp( base, files[i] ) )
return true; return true;
#ifdef SYS_DARWIN
/* check for resource forks. <http://support.apple.com/kb/TA20578> */ /* check for resource forks. <http://support.apple.com/kb/TA20578> */
if( !memcmp( base, "._", 2 ) ) if( !memcmp( base, "._", 2 ) )
return true; return true;
#endif
return false; return false;
} }
static void static void
deleteLocalFile( const tr_torrent * tor, const char * filename, tr_fileFunc fileFunc ) removeEmptyFoldersAndJunkFiles( const char * folder )
{ {
struct stat sb; DIR * odir;
if(( odir = opendir( folder ))) {
/* add safeguards for (1) making sure the file exists and struct dirent * d;
(2) that we haven't somehow walked up past the torrent's top directory... */ while(( d = readdir( odir ))) {
if( !stat( filename, &sb ) ) if( strcmp( d->d_name, "." ) && strcmp( d->d_name, ".." ) ) {
if( !tr_is_same_file( tor->currentDir, filename ) ) struct stat sb;
fileFunc( filename ); char * filename = tr_buildPath( folder, d->d_name, NULL );
} if( !stat( filename, &sb ) ) {
if( S_ISDIR( sb.st_mode ) )
static void removeEmptyFoldersAndJunkFiles( filename );
walkLocalData( const tr_torrent * tor, else if( isJunkFile( d->d_name ) )
const char * root, remove( filename );
const char * dir, }
const char * base, tr_free( filename );
tr_ptrArray * torrentFiles,
tr_ptrArray * folders,
tr_ptrArray * dirtyFolders,
tr_fileFunc fileFunc )
{
struct stat sb;
char * buf = tr_buildPath( dir, base, NULL );
int 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 && strcmp( d->d_name, "." ) && strcmp( d->d_name, ".." ) )
walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders, fileFunc );
closedir( odir );
}
else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
{
if( isSystemFile( base ) )
deleteLocalFile( tor, buf, fileFunc );
else {
const char * sub = buf + strlen( tor->currentDir ) + strlen( TR_PATH_DELIMITER_STR );
const bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
if( !isTorrentFile )
addDirtyFile( root, buf, dirtyFolders );
} }
} }
remove( folder );
closedir( odir );
} }
tr_free( buf );
} }
static bool fileExists( const char * filename, time_t * optional_mtime );
/**
* This convoluted code does something (seemingly) simple:
* remove the torrent's local files.
*
* Fun complications:
* 1. Try to preserve the directory hierarchy in the recycle bin.
* 2. If there are nontorrent files, don't delete them...
* 3. ...unless the other files are "junk", such as .DS_Store
*/
static void static void
deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc ) deleteLocalData( tr_torrent * tor, tr_fileFunc func )
{ {
int i, n; int i, n;
char * tmp;
char * root;
char ** s;
tr_file_index_t f; tr_file_index_t f;
const char * cpch; char * base;
const char * firstFile; DIR * odir;
tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT; char * tmpdir = NULL;
tr_ptrArray folders = TR_PTR_ARRAY_INIT; tr_ptrArray files = TR_PTR_ARRAY_INIT;
tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */ tr_ptrArray folders = TR_PTR_ARRAY_INIT;
const void * const vstrcmp = strcmp;
const char * const top = tor->currentDir;
if( !tr_torrentHasMetadata( tor ) ) /***
return; **** Move the local data to a new tmpdir
***/
firstFile = tor->info.files[0].name; base = tr_strdup_printf( "%s__XXXXXX", tr_torrentName( tor ) );
cpch = strchr( firstFile, TR_PATH_DELIMITER ); tmpdir = tr_buildPath( top, base, NULL );
tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL; mkdtemp( tmpdir );
root = tr_buildPath( tor->currentDir, tmp, NULL ); tr_free( base );
for( f=0; f<tor->info.fileCount; ++f ) { for( f=0; f<tor->info.fileCount; ++f )
tr_ptrArrayInsertSorted( &torrentFiles, tr_strdup( tor->info.files[f].name ), vstrcmp );
tr_ptrArrayInsertSorted( &torrentFiles, tr_torrentBuildPartial( tor, f ), vstrcmp );
}
/* build the set of folders and dirtyFolders */
walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders, fileFunc );
/* 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 )
deleteLocalFile( tor, s[i], fileFunc );
/* now blow away any remaining torrent files, such as torrent files in dirty folders */
for( i=0, n=tr_ptrArraySize( &torrentFiles ); i<n; ++i ) {
char * path = tr_buildPath( tor->currentDir, tr_ptrArrayNth( &torrentFiles, i ), NULL );
deleteLocalFile( tor, path, fileFunc );
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_PTR_ARRAY_INIT; char * subpath;
s = (char**) tr_ptrArrayPeek( &folders, &n ); const char * base;
for( i=0; i<n; ++i )
if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL ) if( tr_torrentFindFile2( tor, f, &base, &subpath, NULL ) )
tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst ); {
s = (char**) tr_ptrArrayPeek( &cleanFolders, &n ); char * source = tr_buildPath( base, subpath, NULL );
for( i=0; i<n; ++i ) char * target = tr_buildPath( tmpdir, subpath, NULL );
deleteLocalFile( tor, s[i], fileFunc ); char * target_dir = tr_dirname( target );
tr_ptrArrayDestruct( &cleanFolders, NULL ); tr_mkdirp( target_dir, 0777 );
rename( source, target );
tr_free( target_dir );
tr_free( target );
tr_free( source );
}
}
/***
**** Remove tmpdir.
****
**** Try deleting the top-level files & folders to preserve
**** the directory hierarchy in the recycle bin.
**** If case that fails -- for example, rmdir() doesn't
**** delete nonempty folders -- go from the bottom up too.
***/
/* try deleting the local data's top-level files & folders */
if(( odir = opendir( tmpdir )))
{
struct dirent * d;
while(( d = readdir( odir )))
{
if( strcmp( d->d_name, "." ) && strcmp( d->d_name, ".." ) )
{
char * file = tr_buildPath( tmpdir, d->d_name, NULL );
tr_ptrArrayInsertSorted( &folders, tr_strdup( d->d_name ), vstrcmp );
func( file );
tr_free( file );
}
}
closedir( odir );
}
/* go from the bottom up */
for( i=0, n=tr_ptrArraySize(&files); i<n; ++i )
{
char * walk = tr_strdup( tr_ptrArrayNth( &files, i ) );
while( fileExists( walk, NULL ) && !tr_is_same_file( tmpdir, walk ) )
{
char * tmp = tr_dirname( walk );
func( walk );
tr_free( walk );
walk = tmp;
}
tr_free( walk );
}
/***
**** The local data has been removed.
**** What's left in top are empty folders, junk, and user-generated files.
**** Remove the first two categories and leave the third.
***/
for( i=0, n=tr_ptrArraySize(&folders); i<n; ++i )
{
char * folder = tr_buildPath( top, tr_ptrArrayNth(&folders,i), NULL );
removeEmptyFoldersAndJunkFiles( folder );
tr_free( folder );
} }
/* cleanup */ /* cleanup */
tr_ptrArrayDestruct( &dirtyFolders, tr_free ); rmdir( tmpdir );
tr_free( tmpdir );
tr_ptrArrayDestruct( &files, tr_free );
tr_ptrArrayDestruct( &folders, tr_free ); tr_ptrArrayDestruct( &folders, tr_free );
tr_ptrArrayDestruct( &torrentFiles, tr_free );
tr_free( root );
tr_free( tmp );
} }
static void static void
tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc ) tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc func )
{ {
assert( tr_isTorrent( tor ) ); assert( tr_isTorrent( tor ) );
if( fileFunc == NULL ) if( func == NULL )
fileFunc = remove; func = remove;
/* close all the files because we're about to delete them */ /* close all the files because we're about to delete them */
tr_cacheFlushTorrent( tor->session->cache, tor ); tr_cacheFlushTorrent( tor->session->cache, tor );
tr_fdTorrentClose( tor->session, tor->uniqueId ); tr_fdTorrentClose( tor->session, tor->uniqueId );
deleteLocalData( tor, fileFunc ); deleteLocalData( tor, func );
} }
/*** /***