6

I am creating a application which is reading files outside the application sandbox. I need the user's permission to read these files and I have got the security-scoped bookmarks to work, by making the user pick the path in a NSOpenPanel.

My problem is that I don't want the user to give me permission every time the application accesses the same path. I am saving the bookmarks in the NSApplicationSupportDirectory folder.

This works fine until the user restarts the computer, then it seems like the saved bookmarks become invalid. The bookmark data looks completely fine when loaded from the disk, but the application requires new security-scoped bookmarks to read the files even though the application is using a bookmark for the path which worked before the restart.

I am creating the bookmarks like this:

NSData * bookmark  = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope includingResourceValuesForKeys:nil relativeToURL:nil error:&err];
NSURL* bookmarkUrl = [NSURL URLByResolvingBookmarkData:bookmark options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:&isStale error:&err]

I am checking if the bookmark is accessible with the following:

[url startAccessingSecurityScopedResource];
[[NSFileManager defaultManager] contentsOfDirectoryAtURL:url includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles error:&err]

if (err && err.code == NSFileReadNoPermissionError)
{
    NSLog(@"No URL access");
    res = NO;
}
else if(err)
{
    NSLog(@"URL validate unexpected error:%@", err);
    res = NO;
}

if (res)
{
    //Bookmark works!
}
[url stopAccessingSecurityScopedResource];

This works as long as I don't restart the Mac. I can quit the application and start it again and my bookmarks still work. But as soon as the Mac is restarted I get the NSFileReadNoPermissionError error even though I am using the saved bookmark that worked before I restarted.

Is there a way to create a bookmark that works when the Mac has been restarted?

ThomasCle
  • 6,792
  • 7
  • 41
  • 81
  • I don't understand the point of the first code fragment; `NSURL` -> `NSData` -> `NSURL`? What does that achieve? – trojanfoe Jun 17 '14 at 08:32
  • It is creating a new `NSURL` containing the security-scope. – ThomasCle Jun 17 '14 at 08:38
  • There is no need for that. When I've done this in the past I've created a class to handle creating the URL from data (saved to a plist file) and for saving the URL to a plist file and the class simply remembered if it was created from an `NSURL` object or from an `NSData` object (via the plist). – trojanfoe Jun 17 '14 at 08:45
  • Why do you save a security-scoped bookmark in the Documents folder? I'm sorry to say, but what you do is fundamentally wrong right from the beginning. – El Tomato Jun 17 '14 at 09:30
  • Oh, that was a "typo". I am saving them in `NSApplicationSupportDirectory`. I would love to see how it's done the right way... – ThomasCle Jun 17 '14 at 09:33
  • You don't show how you save and then recover your bookmarks. So I don't know what is the exact problem you are having. All you have to do is save your paths and bookmarks with NSMutableArray. – El Tomato Jun 17 '14 at 09:46
  • You are calling stopAccessingSecurityScopedResource. That's why it stops working. – El Tomato Jun 17 '14 at 09:56
  • My bookmarks work, but stops working if the Mac has been restarted. The `NSFileReadNoPermissionError` only occurs if ***the Mac has been restarted***. – ThomasCle Jun 17 '14 at 11:54

1 Answers1

6

It turned out I had failed to have balance between start and stop invokations. As the documentation of startAccessingSecurityScopedResource states, this can make the booksmarks invalid:

Warning: You must balance every call to the startAccessingSecurityScopedResource method with a corresponding call to the stopAccessingSecurityScopedResource method. If you fail to relinquish your access when you no longer need a file-system resource, your app leaks kernel resources. If sufficient kernel resources are leaked, your app loses its ability to add file-system locations to its sandbox, such as via Powerbox or security-scoped bookmarks, until relaunched.

I also ended up storing the bookmarks in NSUserDefaults instead of a file in the NSApplicationSupportDirectory folder.

ThomasCle
  • 6,792
  • 7
  • 41
  • 81