Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if a file is inside any macOS Trash folder

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;
}
like image 866
Thomas Tempelmann Avatar asked Nov 03 '20 13:11

Thomas Tempelmann


People also ask

How do I find the original location of a file?

To view the location of the original file that a shortcut points to, right-click the shortcut and select "Open file location." Windows will open the folder and highlight the original file. You can see the folder path where the file is located in the top of the Windows Explorer window.

Why is there recovered files in my Trash Mac?

One or more Recovered Files folders may appear in your Trash after you restart your Mac. The recovered files are temporary files used by macOS apps. Usually temporary files are deleted by an app when it no longer needs them. If an app quits unexpectedly, it may not be able to delete the temporary files before it quits.


1 Answers

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/"]
                                )
                  );
like image 96
Thomas Tempelmann Avatar answered Sep 30 '22 08:09

Thomas Tempelmann