确定文件是否在任何macOS废纸folder文件夹中 [英] Determine if a file is inside any macOS Trash folder

查看:189
本文介绍了确定文件是否在任何macOS废纸folder文件夹中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于iOS有一个类似的问题,但是我发现推荐的解决方案在所有情况下都不适用于macOS.

>

在Mac上,可能有很多垃圾箱"文件夹:

  • /.Trashes
  • ~/.Trash
  • ~/Library/Mobile Documents/com~apple~CloudDocs/.Trash –这是来自iCloud
  • /Users/xxx/.Trash –任何其他用户的垃圾桶
  • /Volumes/xxx/.Trashes

此代码应该有效,但不适用于iCloud垃圾箱:

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);

如果URL指向任何iCloud文件夹(包括上面显示的垃圾箱"文件夹),则会出现此错误:

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

奇怪的是,即使是"NSFileManager"的头文件也可以被使用. 10.15 SDK中的建议使用相同的代码:

/* 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.
 */

在iCloud同步的文件夹上似乎还有一个问题,其中带有trashItemAtURL:.

那么,我该如何解决呢?如果Finder可以检测到iCloud垃圾,我也应该可以.

(注意:我用于测试此应用的应用程序甚至都没有沙盒化处理)

更多发现:符号链接也失效

如果URL指向目标不存在的符号链接,则正式建议的使用getRelationship:的方法也会失败并显示错误.

因此,基本上,此功能已被破坏(已在10.13.6、10.15.7和11.0.1中进行验证).

这里是演示该bug的代码,我已将其提交给FB8890518,并已提交给Apple:

#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;
}

解决方案

认识到[NSFileManager getRelationship:]甚至会因符号链接损坏而失败,因此我得出结论,这是macOS中的错误,多年来一直未发现.

我想出了以下解决方法:

使用getRelationship:操作,然后首先检查返回的错误:

  • 如果没有错误,请检查是否为relationship == NSURLRelationshipContains,并将其用作我的结果.
  • 否则,如果有任何错误,请检查路径是否包含"/.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)
                   || error && (
                                    [theURL.path containsString:@"/.Trash/"]
                                 || [theURL.path containsString:@"/.Trashes/"]
                                )
                  );

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;
}

解决方案

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/"]
                                )
                  );

这篇关于确定文件是否在任何macOS废纸folder文件夹中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆