2
votes

I'm developing a sandboxed app for Mac OS X 10.7 and I'm trying to implement file saving in a way similar to NSDocument:

  1. Rewrite the file's new contents to a temporary file
  2. Overwrite the original file with the temporary file

The issue I'm having is that the sandbox is denying step 2. I see the following line in Console:

sandboxd: XXXX deny file-write-create /Volumes/Home/sbooth/Test Files/Test

I already have this file open for reading and writing, and I have the File System Read/Write Access entitlement enabled. I know NSDocument does this with no special entitlements so I'm trying to figure out what I've missed.

Here is how I'm doing things now (this portion of the app is in C++, not Objective-C/C++):

FSRef tempFileFSRef;
if(noErr != FSPathMakeRef((const UInt8 *)tempFileName, &tempFileFSRef, NULL))
  ; // Handle it
CFURLRef destinationDirURL = CFURLCreateCopyDeletingLastPathComponent(kCFAllocatorDefault, mURL);
FSRef destinationDirFSRef;
if(!CFURLGetFSRef(destinationDirURL, &destinationDirFSRef))
  ; // Handle it
CFRelease(destinationDirURL), destinationDirURL = NULL;
CFStringRef destinationName = CFURLCopyLastPathComponent(mURL);
FSRef target;
OSStatus result = FSCopyObjectSync(&tempFileFSRef, &destinationDirFSRef, destinationName, &target, kFSFileOperationOverwrite | kFSFileOperationSkipSourcePermissionErrors);
if(noErr != result)
  ; // Handle it

The code works correctly if I disable sandboxing.

Edit: Additional information was requested by Femi. I open the file using C stdio:

FILE *f = fopen(reinterpret_cast<const char *>(buf), "r");

and it closed using fclose before the temp file is created.

My entitlements are:

<dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.assets.music.read-write</key>
    <true/>
    <key>com.apple.security.files.downloads.read-write</key>
    <true/>
    <key>com.apple.security.files.user-selected.read-write</key>
    <true/>
</dict>

It's also worth noting that Apple says in their App Sandbox Design Guide that:

If you are managing documents using any technology other than the NSDocument class, you must convert to using this class. The NSDocument class automatically works with Powerbox. NSDocument also provides support for keeping documents within your sandbox if the user moves them using Finder.

1
Besides the sandboxd line in the Console, is FSCopyObjectSync returning an error? - Michael Dautermann
It does return an error: -54 | permErr - sbooth
It's also not a surprise, but FSMoveObjectSync exhibits the same behavior - sbooth
Curious: can you include the code where you open the original file? And the entitlements you've asked for? - Femi
IIRC the sandbox keeps track of what files the user selected in a sandbox... try to select the file via a dialog again after you closed the file and before you open for writing... - Yahia

1 Answers

0
votes

The sandbox only allows you to read and write from your application's container. If you have the read/write entitlement enabled, then you only have access to the files that are opened via the OpenSavePanel (or some method like dragging an icon to the dock where the user has specifically opened the file). This will severely limit how the application works with files on the file system.

So, as long as your application has permission to write over the document in question, you can write your temporary file to your applications container and then retrieve the temp file contents to then save to the original location. However, this assumes that you app maintains permissions to the original file throughout the process.

So, my suggestion is to make sure any writing you do is done in the application container and then verify that you app still has permission to write to the real file location before calling the save function.

I recently wrote a similar question where my app lost access to files that were open because another (non-sandboxed) app saved to the same file. The sandbox then gave that app access to my doc and removed permissions from the app.