iOS Security Scoped Folder Bookmark

I'm trying to persist a bookmark to an external device (mass storage controller connected via camera adapter) across disconnection / reconnection, but it is failing at startAccessingSecurityScopedResource.

The URL is initially retrieved using

    UIDocumentPickerViewController *documentProvider;
    documentProvider = [[UIDocumentPickerViewController alloc] initForOpeningContentTypes:[NSArray arrayWithObjects:UTTypeFolder, nil]];
    documentProvider.delegate = self;
    documentProvider.modalPresentationStyle = UIModalPresentationOverFullScreen;
    [self presentViewController:documentProvider animated:YES completion:nil];

and then persisted to a bookmark using

DeviceBookmark = [url bookmarkDataWithOptions:NSURLBookmarkCreationMinimalBookmark includingResourceValuesForKeys:nil relativeToURL:nil error:nil]

When accessing the resource I use

NSURL *url = [NSURL URLByResolvingBookmarkData:DeviceBookmark options:NSURLBookmarkResolutionWithoutUI relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]

to retrieve the new URL.

If I don't remove the MSC then the retrieved URL remains the same and functions as expected. If I remove and reconnect the MSC then the URL changes, I get true for isStale and nil for error but startAccessingSecurityScopedResource fails.

I've been banging my head against this for about a day now, but can't see what the issue can be. I've tried adding some related permissions to the entitlements, but this seems to be macOS related as far as I can tell.

What am I missing?!

Answered by RHQ in 823056022

I found the issue, and it was suitably obscure. The MSC in question is provided by a microcontroller. It just happened in the boot sector (FAT12) the volume serial number was set to 0. While I would think 0 is still a valid number, in this case it doesn't seem to be. Setting it to non-zero allows the bookmark to resolve correctly.

I'm sure no-one else will ever come across this, but there we go, a solution.

Actually, it's more subtle than I thought. I'd changed the code a bit and hadn't realised the difference.

If I'm retrieving the bookmark via:

NSURL *url = [NSURL URLByResolvingBookmarkData:DeviceBookmark options:NSURLBookmarkResolutionWithoutUI relativeToURL:nil bookmarkDataIsStale:&isStale error:&error]

Then this will work exactly once and only if the MSC device has not been disconnected and reconnected. If the above call is made a second time to get the NSURL, while the returned path does not change, startAccessingSecurityScopedResource fails. If the MSC device is changed before the first call is made then a different path is returned (along with isStale true) but never works.

If I use the NSURL initially returned by UIDocumentPickerViewController then I can access the MSC device as many times as I like while it's connected. If I change the device then I can still access that NSURL and get no error from startAccessingSecurityScopedResource but there are no files present.

I thought of another thing to try. If I create the bookmark after calling startAccessingSecurityScopedResource then when the MSC device is reconnected when I try and get an NSURL from the bookmark instead of getting a new path and isStale: true, I get an error of:

domain: @"NSFileProviderErrorDomain" - code: 18446744073709549615 (or -2001 signed, "The application cannot be used right now." )

I'm hoping all of this might point to some kind of solution.

Accepted Answer

I found the issue, and it was suitably obscure. The MSC in question is provided by a microcontroller. It just happened in the boot sector (FAT12) the volume serial number was set to 0. While I would think 0 is still a valid number, in this case it doesn't seem to be. Setting it to non-zero allows the bookmark to resolve correctly.

I'm sure no-one else will ever come across this, but there we go, a solution.

Since you asked...

I found the issue, and it was suitably obscure. The MSC in question is provided by a microcontroller. It just happened in the boot sector (FAT12) the volume serial number was set to 0. While I would think 0 is still a valid number, in this case it doesn't seem to be.

The problem here is that it turns out that there are lots of volumes "in the wild", typically created by things like cameras, that lay down a very simple FAT volumes and leave the VSN 0. Those devices also tend to be automatically naming files and, in some case, that means they end up arbitrarily replicating logical hierarchies that are basically indistinguishable from each other.

This can make bookmark resolution look absolutely BONKERS, as you can end up with multiple volumes with totally different contents (because the pictures themselves are different) but any one of which is FULLY capable of "legitimately" resolving the same bookmark data. The bookmark API always resolves to a single file, so which file you'd endup getting ends up depending on invisible factors (primarily mount order), which ends up looking random and broken to the user.

SO, we short circuit that issue by:

  1. (I think) If something scopes the bookmark to the volume, we try and resolve it within the volume. This allows bookmark files to work within the volume.

  2. Otherwise, we don't resolve it.

The idea here is that bookmarks should prefer failing resolution over returning the wrong file.

And, on this point:

While I would think 0 is still a valid number

No, it's not a valid number. It certainly happens, but that's not the same as being a "good idea".

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

iOS Security Scoped Folder Bookmark
 
 
Q