沙盒化 Mac 应用程序耗尽安全范围的 URL 资源

Posted

技术标签:

【中文标题】沙盒化 Mac 应用程序耗尽安全范围的 URL 资源【英文标题】:Sandboxed Mac app exhausting security scoped URL resources 【发布时间】:2014-09-02 15:59:04 【问题描述】:

我正在开发一个使用 NSOpenPanel 提示用户输入文件的 Mac 应用程序。该应用程序是沙盒的(在 OSX 10.9.4 上测试)。我注意到如果我打开大量文件(~3000),打开的面板开始向日志发出错误。如果我多次尝试在卡盘中打开较少数量的文件,也会发生这种情况。

在第一次开始出现错误后,每次再次使用 NSOpenPanel 打开文件,无论文件多少,都会再次产生这些错误(直到应用程序关闭)。

错误信息如下所示:

TestPanel[98508:303] __41+[NSSavePanel _consumeSandboxExtensions:]_block_invoke: sandbox_consume_fs_extension failed

我尝试打开的每个文件对应一行。

我设法用一个简单的应用程序重现了这种行为:一个带有单个按钮的沙盒应用程序,调用以下代码:

NSOpenPanel* panel = [NSOpenPanel openPanel];
[panel setAllowsMultipleSelection:YES];
[panel setCanChooseDirectories:NO];
[panel setCanChooseFiles:YES];
[panel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) 
    NSLog(@"%lu", [panel.URLs count]);
];

错误出现在代码到达完成处理程序之前。

似乎我仍然可以从完成处理程序的面板中获取 URL,但它确实污染了系统日志。

编辑:

似乎这个问题与 NSOpenPanel/NSSavePanel 面板没有直接关系。对文件使用拖放/拖放时会发生非常相似的事情。像这样的:

- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender 
    ...
    NSPasteboard *pboard = [sender draggingPasteboard];
    if ([[pboard types] containsObject:NSURLPboardType]) 
        NSArray *urls = [pboard readObjectsForClasses:@[[NSURL class]] options:nil];
    
    ...

这将在拖动大量文件时生成以下日志消息(“神奇”数字似乎在 2900 左右):

Consume sandbox extension for itemIdentifier (2937) from pasteboard failed!

与 NSOpenPanel 一样,在第一次发生这种情况后,删除的每个文件都会在日志中生成相同的错误。

编辑 2:

@mahal tertin 的回复为我指明了正确的方向。问题确实在于文件的数量以及安全范围的 URL 资源是有限的。

但是,似乎没有找到合理的解决方案。问题在于,当用户在 NSOpenPanel 上单击“确定”(或将文件放在拖放感知控件上)时,操作系统已经在后台尝试创建这些安全范围的 URL 并为您隐式调用 startAccessingSecurityScopedResource。因此,如果用户尝试打开的文件超过限制,资源就会耗尽,唯一的选择就是关闭并重新启动应用程序。

在返回的 URL 上调用 stopAccessingSecurityScopedResource 似乎可以释放资源,但 Apple 代表在 official developers forums 上不鼓励这种解决方案(链接在登录后面)。

似乎该应用程序受制于用户不要打开太多文件。这甚至不是一次,因为没有批准的方式来释放这些资源。您可以在文档中甚至使用应用内警报来警告用户,但没有办法阻止他们弄乱应用并强制重启。

因此,如果应用运行时间足够长并且用户不断打开文件,应用最终将无法使用。

仍在为此寻找合理的解决方案。

【问题讨论】:

【参考方案1】:

在高低搜索并在各个地方询问之后,我将关闭这个问题并得出结论,这个问题没有答案或解决方案。我在此发布已知信息以供将来参考。

建议的所有解决方案都只是解决方法,可以最大限度地减少问题并尝试引导用户不要尝试打开太多文件。但是没有什么可以真正解决这个问题。

以下是有关此问题的已知事实:

无论您做什么,用户都可能尝试在 NSOpenPanel 对话框中打开太多文件并耗尽安全范围的 URL 资源 一旦这些资源耗尽,就无法再打开任何文件进行读/写。应用程序需要关闭并重新打开 即使用户不尝试一次打开太多文件,如果应用程序运行的时间足够长并且用户随着时间的推移打开足够多的文件,应用程序仍然可能会耗尽这些资源,因为自动调用 startAccessingSecurityScopedResource NSOpenPanel(或拖放机制),没有任何东西可以关闭这些资源 在打开面板检索到的所有 URL 上调用 stopAccessingSecurityScopedResource 将释放这些资源,但 Apple 不鼓励这种做法,称它可能与未来的解决方案不兼容 当您从 NSOpenPanel(或拖放)收到 URL 列表时,无法判断是否所有 URL 都已成功访问,或者是否有 URL 超出限制并因此无效。 Apple 已意识到这一点,并可能在未来修复它。它在 10.10 中仍未修复,当然,这对在当前/以前的 OSX 版本上运行的当前应用程序没有帮助。

看来苹果真的在这件事上丢了球,沙盒的实施似乎非常草率和短视。

【讨论】:

我也看到了这个——你有没有提交过雷达?我很想欺骗它。如果没有,我会尝试编写自己的测试用例并归档。 不,我没有提交 Radar。我停止提交雷达,因为我开始觉得这是在浪费我的时间。这些雷达大多被忽略,没有对它们的反馈,没有确认,没有搜索其他雷达的能力。如果 Apple 希望我为他们做他们的肮脏工作,查找、调试、复制和报告错误,他们最好给我适当的工具和反馈。苹果的“你提交雷达,也许是第 1000 次,然后也许我们会调查它,也许不会”的态度开始让我感到不安。对不起。 同意。 Apple 对整个沙盒和安全范围的书签机制的实施极其(而且一反常态)短视。【参考方案2】:

您遇到的行为是因为安全范围内的资源有限:

NSURL - (BOOL)startAccessingSecurityScopedResource 告诉

如果泄漏了足够的内核资源,您的应用将失去其能力 将文件系统位置添加到其沙箱...

目前的限制大致是您所经历的。看: What are the current kernel resource limits on security-scoped bookmarks?

为了防止它:

仅在给定时间开始访问您需要的 SSB,然后停止访问它们 开始访问不是文件而是封闭文件夹:要求用户不要选择文件而是选择整个文件夹。这将授予您访问该目录下的整个树的权限 在 draggingEntered 上:显示一个 NSOpenPanel,其中包含要授予访问权限的封闭目录

【讨论】:

感谢您的回复。这为我指明了正确的方向。但是,我没有明确调用 startAccessingSecurityScopedResource。我假设 NSSavePanel/drag 在打开/删除文件时隐式调用它们。所以当我开始使用这些资源并不重要,当我得到 URL 列表(从面板或拖动)时,已经太晚了,资源已经耗尽。使用文件夹而不是文件在我的应用程序中不适用,因为我需要用户选择单个文件的能力。我也看不出您回复中的第三点如何解决问题。

以上是关于沙盒化 Mac 应用程序耗尽安全范围的 URL 资源的主要内容,如果未能解决你的问题,请参考以下文章

沙盒化后辅助功能 API 停止工作

10.7.3 之前的 Mac App Store 沙盒和处理安全范围的书签

沙盒 - 不允许 killall 操作

Mac App Store:规避沙盒要求

JavaScript Ajaxian»使用iframe沙盒化JavaScript

Chrome 控制台中的沙盒权限错误,但我没有使用任何 iframe