Determine if a file is inside any macOS Trash folder

本秂侑毒 提交于 2020-12-26 06:43:45

问题


There's a similar question for iOS, but I found that the proprosed solutions do not work on macOS in all cases.

On a Mac, there are many possible Trash folders:

  • /.Trashes
  • ~/.Trash
  • ~/Library/Mobile Documents/com~apple~CloudDocs/.Trash – this one is from iCloud
  • /Users/xxx/.Trash – any other user's trash
  • /Volumes/xxx/.Trashes

This code should work but doesn't for the case of the iCloud trash:

NSURL *theURL = ...;
NSURLRelationship relationship = NSURLRelationshipOther;
NSError *error = nil;
[NSFileManager.defaultManager
         getRelationship: &relationship
             ofDirectory: NSTrashDirectory
                inDomain: 0
             toItemAtURL: theURL
                   error: &error];
BOOL insideTrash = !error && (relationship == NSURLRelationshipContains);

If the URL points to any iCloud folder (including the Trash folder shown above), I get this error:

Error Domain=NSCocoaErrorDomain Code=3328
"The requested operation couldn’t be completed because the feature is not supported."

Curiously, even the header file of "NSFileManager" in the 10.15 SDK suggests to use this same code:

/* trashItemAtURL:resultingItemURL:error: [...]

    To easily discover if an item is in the Trash, you may use
    [fileManager getRelationship:&result ofDirectory:NSTrashDirectory
       inDomain:0 toItemAtURL:url error:&error]
    && result == NSURLRelationshipContains.
 */

There also seems to be an issue with trashItemAtURL: on iCloud-synched folders.

So, how do I solve this? If the Finder can detect the iCloud trash, I should be, too.

(Note: The app I use for testing this is not even sandboxed)

More findings: Fails with dead symlinks, too

The officially suggested method of using getRelationship: also fails with an error if the url points to a symlink whose target doesn't exist.

So, basically, this function is quite broken (verified in 10.13.6, 10.15.7 and 11.0.1).

Here's code to demonstrate the bug, which I've filed with Apple under FB8890518:

#import <Foundation/Foundation.h>

static void testSymlink (NSString* symlinkName, NSString* symlinkTarget)
{
    NSString *path = [[NSString stringWithFormat:@"~/.Trash/%@", symlinkName] stringByExpandingTildeInPath];
    NSURL *url = [NSURL fileURLWithPath:path];
    symlink (symlinkTarget.UTF8String, path.UTF8String);
    NSLog(@"created symlink at <%@> pointing to <%@>", url.path, symlinkTarget);

    NSURLRelationship relationship = -1;
    NSError *error = nil;
    [NSFileManager.defaultManager getRelationship:&relationship ofDirectory:NSTrashDirectory inDomain:0 toItemAtURL:url error:&error];
    NSString *rel = @"undetermined";
    if (relationship == 0) rel = @"NSURLRelationshipContains";
    if (relationship == 1) rel = @"NSURLRelationshipSame";
    if (relationship == 2) rel = @"NSURLRelationshipOther";
    NSLog(@"result:\n relationship: %@\n error: %@", rel, error);
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        testSymlink (@"validSymlink", @"/System");
        testSymlink (@"brokenSymlink", @"/nonexisting_file");
    }
    return 0;
}

回答1:


With the realization that [NSFileManager getRelationship:] even fails for broken symlinks, I conclude that this is a bug in macOS that's been existing undetected for years.

I came up with the following work-around:

Use the getRelationship: operation, then check the returned error first:

  • If there's no error, then check if relationship == NSURLRelationshipContains, and use that as my result.
  • Else, in case of any error, check whether the path contains "/.Trash/" or "/.Trashes/" - if so, assume the item is inside the Trash folder.
NSURL *theURL = ...;
NSURLRelationship relationship = NSURLRelationshipOther;
NSError *error = nil;
[NSFileManager.defaultManager
         getRelationship: &relationship
             ofDirectory: NSTrashDirectory
                inDomain: 0
             toItemAtURL: theURL
                   error: &error];
BOOL insideTrash =   !error && (relationship == NSURLRelationshipContains)
                   || error && (
                                    [theURL.path containsString:@"/.Trash/"]
                                 || [theURL.path containsString:@"/.Trashes/"]
                                )
                  );


来源:https://stackoverflow.com/questions/64663834/determine-if-a-file-is-inside-any-macos-trash-folder

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!