My Mac app copies files the user drags in. The app is sandboxed. I've got a bug now, which I can reliably reproduce in Xcode, but which I can't track down the source of.
When I add a certain number of files, the app suddenly stops being able to access the later ones. All the source files come one level down from the same parent directory, and all of the folders in between have the same permissions, verified with ls -l
. The app retains an app-scoped bookmark to Destination Directory
, which is accessed before creating .directoryName
and beginning the file copy.
I get the error:
Error Domain=NSCocoaErrorDomain Code=513 "“Filename.ext” couldn’t be copied because you don’t have permission to access “.directoryName”." UserInfo=0x6080000eaf80 {NSSourceFilePathErrorKey=/Users/Username/Parent Directory/Subdir/Filename.ext, NSUserStringVariant=( Copy ), NSDestinationFilePath=/Users/Username/Desktop/Destination Directory/.directoryName/Filename.ext, NSFilePath=/Users/Username/Parent Directory/Subdir/Filename.ext, NSUnderlyingError=0x6080004567a0 "The operation couldn’t be completed. Operation not permitted"}
I have done the following while debugging:
- Tried adding
Subdir/Filename.ext
on its own after quitting and relaunching, which worked - Verified (with
lsof
and Activity Monitor) that I'm not leaking file handles (which I know has caused similar problems for me in the past) - Verified with symbolic breakpoints that every call to
-[NSURL startAccessingSecurityScopedResource]
to is balanced with a call to-[NSURL stopAccessingSecurityScopedResource]
(and theirCF
equivalents) - Determined that
Filename.ext
, and the other files that fail, succeed when added without the others - The same behavior occurs for Debug and Release builds, in and out of the debugger
- I tried running as root, but my app won't run that way. When running in the debugger, I get an
EXC_BAD_INSTRUCTION
exception, and running it withsudo
on the command line produces a crash (probably the same one)
The behavior seems to indicate some sort of resource leak, but it's not anything I've yet been able to detect. Any ideas what else might be causing this?
Update
I'm not ready to declare an answer, but I started to get these permission errors in a different part of my code, after a user has dropped files into the app. I noticed that a little ways up the logs, I'd see messages like the following:
Consume sandbox extension for itemIdentifier (1) from pasteboard failed!
Tracking down this error led me to a question someone else asked: Sandboxed Mac app exhausting security scoped URL resources
Unfortunately, the accepted answer says (paraphrasing) "tough noogies". It seems that Cocoa and the sandboxing mechanism themselves are leaking the security-scope tokens (though I can't verify with symbolic breakpoints and know of no other way to check). After a certain (unknown) number of file operations on sandboxed files, you'll start getting this error, and the only solution is to quit and relaunch.
I wish there was at least some way to prompt the user to relaunch when it gets close, but I'm not sure of any way to measure how many of these handles are in use. Or, even better, if I could manually clean up after I finish handling the dropped files, but I'm not sure how that would work, since I need to use the NSFilenamesPboardType
pasteboard type to get multiple files' paths. I tried creating NSURL
s from these and stopping security scope access, but that had no effect.
Update 2
I submitted a DTS ticket for this, as it's affecting users and there is no clear workaround. I'll update the question (and maybe give an answer?) as I find out more.
Response from Apple's DTS team
Apparently, this is a known issue, with no available workaround. I submitted a Radar: rdar://20652066, if you'd like to dupe it.