Changeset 12665


Ignore:
Timestamp:
Aug 11, 2011, 2:16:29 PM (10 years ago)
Author:
jordan
Message:

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/libtransmission/torrent.c

    r12640 r12665  
    26832683****/
    26842684
    2685 static int
    2686 vstrcmp( const void * a, const void * b )
    2687 {
    2688     return strcmp( a, b );
    2689 }
    2690 
    2691 static int
    2692 compareLongestFirst( const void * a, const void * b )
    2693 {
    2694     const size_t alen = strlen( a );
    2695     const size_t blen = strlen( b );
    2696 
    2697     if( alen != blen )
    2698         return alen > blen ? -1 : 1;
    2699 
    2700     return vstrcmp( a, b );
    2701 }
    2702 
    2703 static void
    2704 addDirtyFile( const char  * root,
    2705               const char  * filename,
    2706               tr_ptrArray * dirtyFolders )
    2707 {
    2708     char * dir = tr_dirname( filename );
    2709 
    2710     /* add the parent folders to dirtyFolders until we reach the root or a known-dirty */
    2711     while (     ( dir != NULL )
    2712              && ( strlen( root ) <= strlen( dir ) )
    2713              && ( tr_ptrArrayFindSorted( dirtyFolders, dir, vstrcmp ) == NULL ) )
    2714     {
    2715         char * tmp;
    2716         tr_ptrArrayInsertSorted( dirtyFolders, tr_strdup( dir ), vstrcmp );
    2717 
    2718         tmp = tr_dirname( dir );
    2719         tr_free( dir );
    2720         dir = tmp;
    2721     }
    2722 
    2723     tr_free( dir );
    2724 }
    2725 
    27262685static bool
    2727 isSystemFile( const char * base )
     2686isJunkFile( const char * base )
    27282687{
    27292688    int i;
    2730     static const char * stockFiles[] = { ".DS_Store", "desktop.ini", "Thumbs.db" };
    2731     static const int stockFileCount = sizeof(stockFiles) / sizeof(stockFiles[0]);
    2732 
    2733     for( i=0; i<stockFileCount; ++i )
    2734         if( !strcmp( base, stockFiles[i] ) )
     2689    static const char * files[] = { ".DS_Store", "desktop.ini", "Thumbs.db" };
     2690    static const int file_count = sizeof(files) / sizeof(files[0]);
     2691
     2692    for( i=0; i<file_count; ++i )
     2693        if( !strcmp( base, files[i] ) )
    27352694            return true;
    27362695
     2696#ifdef SYS_DARWIN
    27372697    /* check for resource forks. <http://support.apple.com/kb/TA20578> */
    27382698    if( !memcmp( base, "._", 2 ) )
    27392699        return true;
     2700#endif
    27402701
    27412702    return false;
     
    27432704
    27442705static void
    2745 deleteLocalFile( const tr_torrent * tor, const char * filename, tr_fileFunc fileFunc )
    2746 {
    2747     struct stat sb;
    2748 
    2749     /* add safeguards for (1) making sure the file exists and
    2750        (2) that we haven't somehow walked up past the torrent's top directory... */
    2751     if( !stat( filename, &sb ) )
    2752         if( !tr_is_same_file( tor->currentDir, filename ) )
    2753             fileFunc( filename );
    2754 }
    2755 
    2756 static void
    2757 walkLocalData( const tr_torrent * tor,
    2758                const char       * root,
    2759                const char       * dir,
    2760                const char       * base,
    2761                tr_ptrArray      * torrentFiles,
    2762                tr_ptrArray      * folders,
    2763                tr_ptrArray      * dirtyFolders,
    2764                tr_fileFunc        fileFunc )
    2765 {
    2766     struct stat sb;
    2767     char * buf = tr_buildPath( dir, base, NULL );
    2768     int i = stat( buf, &sb );
    2769 
    2770     if( !i )
    2771     {
    2772         DIR * odir = NULL;
    2773 
    2774         if( S_ISDIR( sb.st_mode ) && ( ( odir = opendir ( buf ) ) ) )
    2775         {
    2776             struct dirent *d;
    2777             tr_ptrArrayInsertSorted( folders, tr_strdup( buf ), vstrcmp );
    2778             for( d = readdir( odir ); d != NULL; d = readdir( odir ) )
    2779                 if( d->d_name && strcmp( d->d_name, "." ) && strcmp( d->d_name, ".." ) )
    2780                     walkLocalData( tor, root, buf, d->d_name, torrentFiles, folders, dirtyFolders, fileFunc );
    2781             closedir( odir );
    2782         }
    2783         else if( S_ISREG( sb.st_mode ) && ( sb.st_size > 0 ) )
    2784         {
    2785             if( isSystemFile( base ) )
    2786                 deleteLocalFile( tor, buf, fileFunc );
    2787             else {
    2788                  const char * sub = buf + strlen( tor->currentDir ) + strlen( TR_PATH_DELIMITER_STR );
    2789                 const bool isTorrentFile = tr_ptrArrayFindSorted( torrentFiles, sub, vstrcmp ) != NULL;
    2790                 if( !isTorrentFile )
    2791                     addDirtyFile( root, buf, dirtyFolders );
     2706removeEmptyFoldersAndJunkFiles( const char * folder )
     2707{
     2708    DIR * odir;
     2709    if(( odir = opendir( folder ))) {
     2710        struct dirent * d;
     2711        while(( d = readdir( odir ))) {
     2712            if( strcmp( d->d_name, "." ) && strcmp( d->d_name, ".." ) ) {
     2713                struct stat sb;
     2714                char * filename = tr_buildPath( folder, d->d_name, NULL );
     2715                if( !stat( filename, &sb ) ) {
     2716                    if( S_ISDIR( sb.st_mode ) )
     2717                        removeEmptyFoldersAndJunkFiles( filename );
     2718                    else if( isJunkFile( d->d_name ) )
     2719                        remove( filename );
     2720                }
     2721                tr_free( filename );
    27922722            }
    27932723        }
    2794     }
    2795 
    2796     tr_free( buf );
    2797 }
    2798 
     2724        remove( folder );
     2725        closedir( odir );
     2726    }
     2727}
     2728
     2729static bool fileExists( const char * filename, time_t * optional_mtime );
     2730
     2731/**
     2732 * This convoluted code does something (seemingly) simple:
     2733 * remove the torrent's local files.
     2734 *
     2735 * Fun complications:
     2736 * 1. Try to preserve the directory hierarchy in the recycle bin.
     2737 * 2. If there are nontorrent files, don't delete them...
     2738 * 3. ...unless the other files are "junk", such as .DS_Store
     2739 */
    27992740static void
    2800 deleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
     2741deleteLocalData( tr_torrent * tor, tr_fileFunc func )
    28012742{
    28022743    int i, n;
    2803     char * tmp;
    2804     char * root;
    2805     char ** s;
    28062744    tr_file_index_t f;
    2807     const char * cpch;
    2808     const char * firstFile;
    2809     tr_ptrArray torrentFiles = TR_PTR_ARRAY_INIT;
    2810     tr_ptrArray folders      = TR_PTR_ARRAY_INIT;
    2811     tr_ptrArray dirtyFolders = TR_PTR_ARRAY_INIT; /* dirty == contains non-torrent files */
    2812 
    2813     if( !tr_torrentHasMetadata( tor ) )
    2814         return;
    2815 
    2816     firstFile = tor->info.files[0].name;
    2817     cpch = strchr( firstFile, TR_PATH_DELIMITER );
    2818     tmp = cpch ? tr_strndup( firstFile, cpch - firstFile ) : NULL;
    2819     root = tr_buildPath( tor->currentDir, tmp, NULL );
    2820 
    2821     for( f=0; f<tor->info.fileCount; ++f ) {
    2822         tr_ptrArrayInsertSorted( &torrentFiles, tr_strdup( tor->info.files[f].name ), vstrcmp );
    2823         tr_ptrArrayInsertSorted( &torrentFiles, tr_torrentBuildPartial( tor, f ), vstrcmp );
    2824     }
    2825 
    2826     /* build the set of folders and dirtyFolders */
    2827     walkLocalData( tor, root, root, NULL, &torrentFiles, &folders, &dirtyFolders, fileFunc );
    2828 
    2829     /* try to remove entire folders first, so that the recycle bin will be tidy */
    2830     s = (char**) tr_ptrArrayPeek( &folders, &n );
    2831     for( i=0; i<n; ++i )
    2832         if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
    2833             deleteLocalFile( tor, s[i], fileFunc );
    2834 
    2835     /* now blow away any remaining torrent files, such as torrent files in dirty folders */
    2836     for( i=0, n=tr_ptrArraySize( &torrentFiles ); i<n; ++i ) {
    2837         char * path = tr_buildPath( tor->currentDir, tr_ptrArrayNth( &torrentFiles, i ), NULL );
    2838         deleteLocalFile( tor, path, fileFunc );
    2839         tr_free( path );
    2840     }
    2841 
    2842     /* Now clean out the directories left empty from the previous step.
    2843      * Work from deepest to shallowest s.t. lower folders
    2844      * won't prevent the upper folders from being deleted */
    2845     {
    2846         tr_ptrArray cleanFolders = TR_PTR_ARRAY_INIT;
    2847         s = (char**) tr_ptrArrayPeek( &folders, &n );
    2848         for( i=0; i<n; ++i )
    2849             if( tr_ptrArrayFindSorted( &dirtyFolders, s[i], vstrcmp ) == NULL )
    2850                 tr_ptrArrayInsertSorted( &cleanFolders, s[i], compareLongestFirst );
    2851         s = (char**) tr_ptrArrayPeek( &cleanFolders, &n );
    2852         for( i=0; i<n; ++i )
    2853             deleteLocalFile( tor, s[i], fileFunc );
    2854         tr_ptrArrayDestruct( &cleanFolders, NULL );
     2745    char * base;
     2746    DIR * odir;
     2747    char * tmpdir = NULL;
     2748    tr_ptrArray files = TR_PTR_ARRAY_INIT;
     2749    tr_ptrArray folders = TR_PTR_ARRAY_INIT;
     2750    const void * const vstrcmp = strcmp;
     2751    const char * const top = tor->currentDir;
     2752
     2753    /***
     2754    ****  Move the local data to a new tmpdir
     2755    ***/
     2756
     2757    base = tr_strdup_printf( "%s__XXXXXX", tr_torrentName( tor ) );
     2758    tmpdir = tr_buildPath( top, base, NULL );
     2759    mkdtemp( tmpdir );
     2760    tr_free( base );
     2761
     2762    for( f=0; f<tor->info.fileCount; ++f )
     2763    {
     2764        char * subpath;
     2765        const char * base;
     2766
     2767        if( tr_torrentFindFile2( tor, f, &base, &subpath, NULL ) )
     2768        {
     2769            char * source = tr_buildPath( base, subpath, NULL );
     2770            char * target = tr_buildPath( tmpdir, subpath, NULL );
     2771            char * target_dir = tr_dirname( target );
     2772            tr_mkdirp( target_dir, 0777 );
     2773            rename( source, target );
     2774            tr_free( target_dir );
     2775            tr_free( target );
     2776            tr_free( source );
     2777        }
     2778    }
     2779
     2780    /***
     2781    ****  Remove tmpdir.
     2782    ****
     2783    ****  Try deleting the top-level files & folders to preserve
     2784    ****  the directory hierarchy in the recycle bin.
     2785    ****  If case that fails -- for example, rmdir() doesn't
     2786    ****  delete nonempty folders -- go from the bottom up too.
     2787    ***/
     2788
     2789    /* try deleting the local data's top-level files & folders */
     2790    if(( odir = opendir( tmpdir )))
     2791    {
     2792        struct dirent * d;
     2793        while(( d = readdir( odir )))
     2794        {
     2795            if( strcmp( d->d_name, "." ) && strcmp( d->d_name, ".." ) )
     2796            {
     2797                char * file = tr_buildPath( tmpdir, d->d_name, NULL );
     2798                tr_ptrArrayInsertSorted( &folders, tr_strdup( d->d_name ), vstrcmp );
     2799                func( file );
     2800                tr_free( file );
     2801            }
     2802        }
     2803        closedir( odir );
     2804    }
     2805
     2806    /* go from the bottom up */
     2807    for( i=0, n=tr_ptrArraySize(&files); i<n; ++i )
     2808    {
     2809        char * walk = tr_strdup( tr_ptrArrayNth( &files, i ) );
     2810        while( fileExists( walk, NULL ) && !tr_is_same_file( tmpdir, walk ) )
     2811        {
     2812            char * tmp = tr_dirname( walk );
     2813            func( walk );
     2814            tr_free( walk );
     2815            walk = tmp;
     2816        }
     2817        tr_free( walk );
     2818    }
     2819
     2820    /***
     2821    ****  The local data has been removed.
     2822    ****  What's left in top are empty folders, junk, and user-generated files.
     2823    ****  Remove the first two categories and leave the third.
     2824    ***/
     2825
     2826    for( i=0, n=tr_ptrArraySize(&folders); i<n; ++i )
     2827    {
     2828        char * folder = tr_buildPath( top, tr_ptrArrayNth(&folders,i), NULL );
     2829        removeEmptyFoldersAndJunkFiles( folder );
     2830        tr_free( folder );
    28552831    }
    28562832
    28572833    /* cleanup */
    2858     tr_ptrArrayDestruct( &dirtyFolders, tr_free );
     2834    rmdir( tmpdir );
     2835    tr_free( tmpdir );
     2836    tr_ptrArrayDestruct( &files, tr_free );
    28592837    tr_ptrArrayDestruct( &folders, tr_free );
    2860     tr_ptrArrayDestruct( &torrentFiles, tr_free );
    2861     tr_free( root );
    2862     tr_free( tmp );
    28632838}
    28642839
    28652840static void
    2866 tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc fileFunc )
    2867 {
    2868     assert( tr_isTorrent( tor ) );
    2869 
    2870     if( fileFunc == NULL )
    2871         fileFunc = remove;
     2841tr_torrentDeleteLocalData( tr_torrent * tor, tr_fileFunc func )
     2842{
     2843    assert( tr_isTorrent( tor ) );
     2844
     2845    if( func == NULL )
     2846        func = remove;
    28722847
    28732848    /* close all the files because we're about to delete them */
     
    28752850    tr_fdTorrentClose( tor->session, tor->uniqueId );
    28762851
    2877     deleteLocalData( tor, fileFunc );
     2852    deleteLocalData( tor, func );
    28782853}
    28792854
Note: See TracChangeset for help on using the changeset viewer.