Maintaining access to a folder across renames

I have a sandboxed Mac app which I can grant access to a folder using an NSOpenPanel. Once it’s been granted access it can enumerate the contents of the folder just fine. If I rename the folder while the app is open and then make the app enumerate the folder’s contents again, though, it seems to have lost access.

What’s the recommended way to have an app’s sandbox “track” files as they’re moved around the filesystem? (NSDocument handles this for you, from what I can tell.) I’ve managed to hack something together with a combination of Dispatch sources and security-scoped bookmarks, but it feels like there must be an easier solution …

Answered by DTS Engineer in 832310022
Written by lunarazzaghipour in 778846021
What’s the recommended way to have an app’s sandbox “track” files as they’re moved around the filesystem?

There are two standard approaches:

  • Open the item and leave it open while you need it.

  • Use a security-scoped bookmark.

Written by lunarazzaghipour in 778846021
I’ve managed to hack something together with a combination of Dispatch sources and security-scoped bookmarks

I’m not sure where Dispatch sources come into this, but I wouldn’t consider security-scoped bookmarks to be a hack. Bookmarks were specifically designed to track items across renames, and security-scoped bookmarks are just an extension on that for sandboxed app.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer
Written by lunarazzaghipour in 778846021
What’s the recommended way to have an app’s sandbox “track” files as they’re moved around the filesystem?

There are two standard approaches:

  • Open the item and leave it open while you need it.

  • Use a security-scoped bookmark.

Written by lunarazzaghipour in 778846021
I’ve managed to hack something together with a combination of Dispatch sources and security-scoped bookmarks

I’m not sure where Dispatch sources come into this, but I wouldn’t consider security-scoped bookmarks to be a hack. Bookmarks were specifically designed to track items across renames, and security-scoped bookmarks are just an extension on that for sandboxed app.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

My app monitors a folder selected by the user for internal changes with FSEvents, which sadly only takes a path, not a file descriptor. As a result, the “leave it open” approach doesn’t work for my use case. Instead, the app needs to respond immediately when the folder is renamed so it can destroy and recreate the necessary FSEvents event stream – hence the Dispatch source.

The reason I feel security-scoped bookmarks are a bit of a hack here is that AFAIK they’re intended for persisting sandbox extensions across app launches, rather than within a single run of an app. Shouldn’t non-security-scoped bookmarks work here?

The FSEvent create flag kFSEventStreamCreateFlagWatchRoot was specifically designed to handle this event.

I haven't used this event yet. It doesn't sound like you need to recreate the event stream. The documentation for kFSEventStreamCreateFlagWatchRoot describes how to handle it. It specifically says to open the directory before creating the stream so you have the file descriptor handy.

From my testing FSEvents stops delivering events once the root has been changed, so creating a new event stream is necessary. AFAICT the documentation’s reason for recommending you keep around a file descriptor is only so that you can determine the root’s path after the rename, which in turn lets you create a new event stream.

Written by lunarazzaghipour in 832319022
AFAIK [security-scoped bookmarks are] intended for persisting sandbox extensions across app launches, rather than within a single run of an app.

That’s certainly the standard use case, but I don’t see your use case as particularly weird. Lemme explain some background.

When you get a URL from a user action, like the open panel, that URL contains a security scope [1]. When you call startAccessingSecurityScopedResource() on it, that gets the security scope from the URL and uses it to create a dynamic sandbox extension for that path [2].

The URL’s security scope is tied to the URL’s path, and that’s why things are failing after the rename. The security scope is also tied to the originating process, so you can’t persist it.

When you create a security-scoped bookmark for the URL, the bookmark records information about the file’s identity. This allows the bookmark to track the file across a rename. It also includes a security scope, but a different type of security scope, one that supports persistence.

When you resolve a security-scoped bookmark, you get back a new URL with a new security scope that’s appropriate for the new path.

So, while security-scoped bookmarks do support persistence, they also act like little security-scoped URL factories. You want the latter and not the former. And that’s fine.

And your case is relatively unusual because you’re dealing with an API, FSEvents, that only works in terms of paths. Most folks in your situation just open the file and leave it open, but that’s not an option for you.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Historically that was stored in the fragment part of the URL — so file:///some/path#SSS — which was cool because you could actually distinguish between those URLs with and without security scopes. These days it’s store in a private resource value.

[2] The actual rules here are complex because there’s a bunch of implicit security scoping happening, partly as a compatibility measure and partly to support MAC, that is, which relies up reusing a bunch of App Sandox infrastructure in non-sandboxed app. See On File System Permissions for more about MAC.

That makes perfect sense, thank you for clarifying!

Maintaining access to a folder across renames
 
 
Q