Modern versions of macOS use a file system permission model that’s far more complex than the traditional BSD rwx model, and this post is my attempt at explaining that model. If you have a question about this, post it here on DevForums. Put your thread in the App & System Services > Core OS topic area and tag it with Files and Storage.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
On File System Permissions
Modern versions of macOS have five different file system permission mechanisms:
Traditional BSD permissions
Access control lists (ACLs)
App Sandbox
Mandatory access control (MAC)
Endpoint Security (ES)
The first two were introduced a long time ago and rarely trip folks up. The second two are newer, more complex, and specific to macOS, and thus are the source of some confusion. Finally, Endpoint Security allows third-party developers to deny file system operations based on their own criteria. This post offers explanations and advice about all of these mechanisms.
Error Codes
App Sandbox and the mandatory access control system are both implemented using macOS’s sandboxing infrastructure. When a file system operation fails, check the error to see whether it was blocked by this sandboxing infrastructure. If an operation was blocked by BSD permissions or ACLs, it fails with EACCES (Permission denied, 13). If it was blocked by something else, it’ll fail with EPERM (Operation not permitted, 1).
If you’re using Foundation’s FileManager, these error are both reported as Foundation errors, for example, the NSFileReadNoPermissionError error. To recover the underlying error, get the NSUnderlyingErrorKey property from the info dictionary.
App Sandbox
File system access within the App Sandbox is controlled by two factors. The first is the entitlements on the main executable. There are three relevant groups of entitlements:
The com.apple.security.app-sandbox entitlement enables the App Sandbox. This denies access to all file system locations except those on a built-in allowlist (things like /System) or within the app’s containers.
The various “standard location” entitlements extend the sandbox to include their corresponding locations.
The various “file access temporary exceptions” entitlements extend the sandbox to include the items listed in the entitlement.
Collectively this is known as your static sandbox.
The second factor is dynamic sandbox extensions. The system issues these extensions to your sandbox based on user behaviour. For example, if the user selects a file in the open panel, the system issues a sandbox extension to your process so that it can access that file. The type of extension is determined by the main executable’s entitlements:
com.apple.security.files.user-selected.read-only results in an extension that grants read-only access.
com.apple.security.files.user-selected.read-write results in an extension that grants read/write access.
Note There’s currently no way to get a dynamic sandbox extension that grants executable access. For all the gory details, see this post.
These dynamic sandbox extensions are tied to your process; they go away when your process terminates. To maintain persistent access to an item, use a security-scoped bookmark. See Accessing files from the macOS App Sandbox. To pass access between processes, use an implicit security scoped bookmark, that is, a bookmark that was created without an explicit security scope (no .withSecurityScope flag) and without disabling the implicit security scope (no .withoutImplicitSecurityScope flag)).
If you have access to a directory — regardless of whether that’s via an entitlement or a dynamic sandbox extension — then, in general, you have access to all items in the hierarchy rooted at that directory. This does not overrule the MAC protection discussed below. For example, if the user grants you access to ~/Library, that does not give you access to ~/Library/Mail because the latter is protected by MAC.
Finally, the discussion above is focused on a new sandbox, the thing you get when you launch a sandboxed app from the Finder. If a sandboxed process starts a child process, that child process inherits its sandbox from its parent. For information on what happens in that case, see the Note box in Enabling App Sandbox Inheritance.
IMPORTANT The child process inherits its parent process’s sandbox regardless of whether it has the com.apple.security.inherit entitlement. That entitlement exists primarily to act as a marker for App Review. App Review requires that all main executables have the com.apple.security.app-sandbox entitlement, and that entitlements starts a new sandbox by default. Thus, any helper tool inside your app needs the com.apple.security.inherit entitlement to trigger inheritance. However, if you’re not shipping on the Mac App Store you can leave off both of these entitlement and the helper process will inherit its parent’s sandbox just fine. The same applies if you run a built-in executable, like /bin/sh, as a child process.
When the App Sandbox blocks something, it might generates a sandbox violation report. For information on how to view these reports, see Discovering and diagnosing App Sandbox violations.
To learn more about the App Sandbox, see the various links in App Sandbox Resources. For information about how to embed a helper tool in a sandboxed app, see Embedding a Command-Line Tool in a Sandboxed App.
Mandatory Access Control
Mandatory access control (MAC) has been a feature of macOS for many releases, but it’s become a lot more prominent since macOS 10.14. There are many flavours of MAC but the ones you’re most likely to encounter are:
Full Disk Access (macOS 10.14 and later)
Files and Folders (macOS 10.15 and later)
App bundle protection (macOS 13 and later)
App container protection (macOS 14 and later)
App group container protection (macOS 15 and later)
Data Vaults (see below) and other internal techniques used by various macOS subsystems
Mandatory access control, as the name suggests, is mandatory; it’s not an opt-in like the App Sandbox. Rather, all processes on the system, including those running as root, as subject to MAC.
Data Vaults are not a third-party developer opportunity. See this post if you’re curious.
In the Full Disk Access and Files and Folders cases, users grant a program a MAC privilege using System Settings > Privacy & Security. Some MAC privileges are per user (Files and Folders) and some are system wide (Full Disk Access). If you’re not sure, run this simple test:
On a Mac with two users, log in as user A and enable the MAC privilege for a program.
Now log in as user B. Does the program have the privilege?
If a process tries to access an item restricted by MAC, the system may prompt the user to grant it access there and then. For example, if an app tries to access the desktop, you’ll see an alert like this:
“AAA” would like to access files in your Desktop folder.
[Don’t Allow] [OK]
To customise this message, set Files and Folders properties in your Info.plist.
This system only displays this alert once. It remembers the user’s initial choice and returns the same result thereafter. This relies on your code having a stable code signing identity. If your code is unsigned, or signed ad hoc (Signed to Run Locally in Xcode parlance), the system can’t tell that version N+1 of your code is the same as version N, and thus you’ll encounter excessive prompts.
Note For information about how that works, see TN3127 Inside Code Signing: Requirements.
The Files and Folders prompts only show up if the process is running in a GUI login session. If not, the operation is allowed or denied based on existing information. If there’s no existing information, the operation is denied by default.
For more information about app and app group container protection, see the links in Trusted Execution Resources. For more information about app groups in general, see App Groups: macOS vs iOS: Working Towards Harmony
On managed systems the site admin can use the com.apple.TCC.configuration-profile-policy payload to assign MAC privileges.
For testing purposes you can reset parts of TCC using the tccutil command-line tool. For general information about that tool, see its man page. For a list of TCC service names, see the posts on this thread.
Note TCC stands for transparency, consent, and control. It’s the subsystem within macOS that manages most of the privileges visible in System Settings > Privacy & Security. TCC has no API surface, but you see its name in various places, including the above-mentioned configuration profile payload and command-line tool, and the name of its accompanying daemon, tccd.
While tccutil is an easy way to do basic TCC testing, the most reliable way to test TCC is in a VM, restoring to a fresh snapshot between each test. If you want to try this out, crib ideas from Testing a Notarised Product.
The MAC privilege mechanism is heavily dependent on the concept of responsible code. For example, if an app contains a helper tool and the helper tool triggers a MAC prompt, we want:
The app’s name and usage description to appear in the alert.
The user’s decision to be recorded for the whole app, not that specific helper tool.
That decision to show up in System Settings under the app’s name.
For this to work the system must be able to tell that the app is the responsible code for the helper tool. The system has various heuristics to determine this and it works reasonably well in most cases. However, it’s possible to break this link. I haven’t fully research this but my experience is that this most often breaks when the child process does something ‘odd’ to break the link, such as trying to daemonise itself.
If you’re building a launchd daemon or agent and you find that it’s not correctly attributed to your app, add the AssociatedBundleIdentifiers property to your launchd property list. See the launchd.plist man page for the details.
Scripting
MAC presents some serious challenges for scripting because scripts are run by interpreters and the system can’t distinguish file system operations done by the interpreter from those done by the script. For example, if you have a script that needs to manipulate files on your desktop, you wouldn’t want to give the interpreter that privilege because then any script could do that.
The easiest solution to this problem is to package your script as a standalone program that MAC can use for its tracking. This may be easy or hard depending on the specific scripting environment. For example, AppleScript makes it easy to export a script as a signed app, but that’s not true for shell scripts.
TCC and Main Executables
TCC expects its bundled clients — apps, app extensions, and so on — to use a native main executable. That is, it expects the CFBundleExecutable property to be the name of a Mach-O executable. If your product uses a script as its main executable, you’re likely to encounter TCC problems. To resolve these, switch to using a Mach-O executable. For an example of how you might do that, see this post.
Endpoint Security
Endpoint Security (ES) is a general mechanism for third-party products to enforce custom security policies on the Mac. An ES client asks ES to send it events when specific security-relevant operations occur. These events can be notifications or authorisations. In the case of authorisation events, the ES client must either allow or deny the operation.
As you might imagine, the set of security-relevant operations includes file system operations. For example, when you open a file using the open system call, ES delivers the ES_EVENT_TYPE_AUTH_OPEN event to any interested ES clients. If one of those ES client denies the operation, the open system call fails with EPERM.
For more information about ES, see the Endpoint Security framework documentation.
Revision History
2025-11-04 Added a discussion of Endpoint Security. Made numerous minor editorial changes.
2024-11-08 Added info about app group container protection. Clarified that Data Vaults are just one example of the techniques used internally by macOS. Made other editorial changes.
2023-06-13 Replaced two obsolete links with links to shiny new official documentation: Accessing files from the macOS App Sandbox and Discovering and diagnosing App Sandbox violations. Added a short discussion of app container protection and a link to WWDC 2023 Session 10053 What’s new in privacy.
2023-04-07 Added a link to my post about executable permissions. Fixed a broken link.
2023-02-10 In TCC and Main Executables, added a link to my native trampoline code. Introduced the concept of an implicit security scoped bookmark. Introduced AssociatedBundleIdentifiers. Made other minor editorial changes.
2022-04-26 Added an explanation of the TCC initialism. Added a link to Viewing Sandbox Violation Reports. Added the TCC and Main Executables section. Made significant editorial changes.
2022-01-10 Added a discussion of the file system hierarchy.
2021-04-26 First posted.
Files and Storage
RSS for tagAsk questions about file systems and block storage.
Posts under Files and Storage tag
200 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
General:
Forums subtopic: App & System Services > Core OS
Forums tags: Files and Storage, Foundation, FSKit, File Provider, Finder Sync, Disk Arbitration, APFS
Foundation > Files and Data Persistence documentation
Low-level file system APIs are documented in UNIX manual pages
File System Programming Guide archived documentation
About Apple File System documentation
Apple File System Guide archived documentation
File system changes introduced in iOS 17 forums post
On File System Permissions forums post
Extended Attributes and Zip Archives forums post
Unpacking Apple Archives forums post
Creating new file systems:
FSKit framework documentation
Building a passthrough file system sample code
File Provider framework documentation
Finder Sync framework documentation
App Extension Programming Guide > App Extension Types > Finder Sync archived documentation
Managing storage:
Disk Arbitration framework documentation
Disk Arbitration Programming Guide archived documentation
Mass Storage Device Driver Programming Guide archived documentation
Device File Access Guide for Storage Devices archived documentation
BlockStorageDeviceDriverKit framework documentation
Volume format references:
Apple File System Reference
TN1150 HFS Plus Volume Format
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
Testing copyfile on a folder on an external volume (which takes a bit a of time) I'm running into an issue where copyfile gets to the end of the operation and then just fails.
In the callback I can see that the failure occurs on a .DS_Store file inside the folder. So for a .DS_Store it is simple enough for me to just ignore the error and return COPYFILE_SKIP but the somewhat more concerning issue here is that the true error reason is seemingly not reported? In the callback if I read errno it is 0. When copyfile returns it returns -1 after I return COPYFILE_QUIT (and errno is 0) so I don't know what the error is or the appropriate way to handle it.
For .DS_Store just skipping seems reasonable but when copying a folder it may be appropriate to get the true failure reason.
But checking the last path component of source path seems like a hack way to handle errors. If a file in the copying folder with important user data I can't just silently skip it - it isn't clear to me how I should properly proceed in a situation where I can't get the actual reason for the failure.
I'm writing a read-only filesystem extension.
I see that the documentation for loadResource(resource:options:replyHandler:) claims that the --rdonly option is supported, which suggests that this should be possible. However, I have never seen this option provided to my filesystem extension, even if I return usableButLimited as a probe result (where it doesn't mount at all - FB19241327) or pass the -r or -o rdonly options to the mount(8) command. Instead I see those options on the volume's activate call.
But other than saving that "readonly" state (which, in my case, is always the case) and then throwing on all write-related calls I'm not sure how to actually mark the filesystem as "read-only." Without such an indicator, the user is still offered the option to do things like trash items in Finder (although of course those operations do not succeed since I throw an EROFS error in the relevant calls).
It also seems like the FSKit extensions that come with the system handle read-only strangely as well. For example, for a FAT32 filesystem, if I mount it like
mount -r -F -t msdos /dev/disk15s1 /tmp/mnt
Then it acts... weirdly. For example, Finder doesn't know that the volume is read-only, and lets me do some operations like making new folders, although they never actually get written to disk. Writing may or may not lead to errors and/or the change just disappearing immediately (or later), which is pretty much what I'm seeing in my own filesystem extension. If I remove the -F option (thus using the kernel extension version of msdos), this doesn't happen.
Are read-only filesystems currently supported by FSKit? The fact that extensions like Apple's own msdos also seem to act weirdly makes me think this is just a current FSKit limitation, although maybe I'm missing something. It's not necessarily a hard blocker given that I can prevent writes from happening in my FSKit module code (or, in my case, just not implement such features at all), but it does make for a strange experience.
(I reported this as FB21068845, although I'm mostly asking here because I'm not 100% sure this is not just me missing something.)
A user of my app reported that when my app copies files from a QNAP NAS to a folder on their Mac, they get the error "Result too large". When copying the same files from the Desktop, it works.
I asked them to reproduce the issue with the sample code below and they confirmed that it reproduces. They contacted QNAP for support who in turn contacted me saying that they are not sure they can do anything about it, and asking if Apple can help.
Both the app user and QNAP are willing to help, but at this point I'm also unsure how to proceed. Can someone at Apple say anything about this? Is this something QNAP should solve, or is this a bug in macOS?
P.S.: I've had users in the past who reported the same issue with other brands, mostly Synology.
import Cocoa
@main
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidFinishLaunching(_ aNotification: Notification) {
let openPanel = NSOpenPanel()
openPanel.canChooseDirectories = true
openPanel.runModal()
let source = openPanel.urls[0]
openPanel.canChooseFiles = false
openPanel.runModal()
let destination = openPanel.urls[0]
do {
try copyFile(from: source, to: destination.appendingPathComponent(source.lastPathComponent, isDirectory: false))
} catch {
NSAlert(error: error).runModal()
}
NSApp.terminate(nil)
}
private func copyFile(from source: URL, to destination: URL) throws {
if try source.resourceValues(forKeys: [.isDirectoryKey]).isDirectory == true {
try FileManager.default.createDirectory(at: destination, withIntermediateDirectories: false)
for source in try FileManager.default.contentsOfDirectory(at: source, includingPropertiesForKeys: nil) {
try copyFile(from: source, to: destination.appendingPathComponent(source.lastPathComponent, isDirectory: false))
}
} else {
try copyRegularFile(from: source, to: destination)
}
}
private func copyRegularFile(from source: URL, to destination: URL) throws {
let state = copyfile_state_alloc()
defer {
copyfile_state_free(state)
}
var bsize = UInt32(16_777_216)
if copyfile_state_set(state, UInt32(COPYFILE_STATE_BSIZE), &bsize) != 0 {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
} else if copyfile_state_set(state, UInt32(COPYFILE_STATE_STATUS_CB), unsafeBitCast(copyfileCallback, to: UnsafeRawPointer.self)) != 0 {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
} else if copyfile(source.path, destination.path, state, copyfile_flags_t(COPYFILE_DATA | COPYFILE_SECURITY | COPYFILE_NOFOLLOW | COPYFILE_EXCL | COPYFILE_XATTR)) != 0 {
throw NSError(domain: NSPOSIXErrorDomain, code: Int(errno))
}
}
private let copyfileCallback: copyfile_callback_t = { what, stage, state, src, dst, ctx in
if what == COPYFILE_COPY_DATA {
if stage == COPYFILE_ERR {
return COPYFILE_QUIT
}
}
return COPYFILE_CONTINUE
}
}
So I'm in the middle of an asynchronous file operation. I publish an NSProgress and it displays wonderfully in Finder.
But it is a folder and while the operation is in progress the user should not be allowed to enter it, modify it, etc, while the work is being done. I want to do this to protect the user from doing something silly.
But Finder does not prevent the selection with the published progress. And while it would be kind of dumb to do - the user can just go about adding/removing contents to the folder while it has progress.
If I remember correctly publishing an NSProgress did use to prevent the file from being selectable in Finder until either the progress finished or my app is quit (or maybe not)? But now the user is free to select, edit, modify during progress which could cause problems if the user does something unexpectedly silly.
Is there a way to mark the file 'unselectable' with the published progress?
Thanks in advance.
Mounting NFS to the application's own container directory using NetFSMountURLSync failed.
Mounted to /Users/li/Library/Containers/com.xxxxx.navm.MyNavm/Data/Documents/NFSMount
Do sandbox applications not allow mounting NFS cloud storage?
code:
// 1. NFS 服务器 URL(指定 NFSv3)
let urlString = "nfs://192.168.64.4/seaweed?vers=3&resvport&nolocks&locallocks&soft&intr&timeo=600"
guard let nfsURL = URL(string: urlString) else {
os_log("❌ 无效的 URL: %@", log: netfsLog, type: .error, urlString)
return
}
// 2. 挂载点(必须在沙盒容器内)
let fileManager = FileManager.default
guard let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
os_log("❌ 无法获取 Documents 目录", log: netfsLog, type: .error)
return
}
let mountPointURL = documentsURL.appendingPathComponent("NFSMount", isDirectory: true)
// 创建挂载点目录
do {
try fileManager.createDirectory(at: mountPointURL, withIntermediateDirectories: true, attributes: nil)
os_log("✅ 挂载点目录已准备: %@", log: netfsLog, type: .info, mountPointURL.path)
} catch {
os_log("❌ 创建挂载点目录失败: %@", log: netfsLog, type: .error, error.localizedDescription)
return
}
// 3. 挂载选项(使用 NSMutableDictionary 以匹配 CFMutableDictionary)
let mountOptions = NSMutableDictionary()
// 如果需要,可以添加选项,例如:
// mountOptions[kNetFSNoUserAuthenticationKey as String] = true
// 4. 调用 NetFSMountURLSync
var mountPoints: Unmanaged<CFArray>? = nil
let status = NetFSMountURLSync(
nfsURL as CFURL,
mountPointURL as CFURL,
nil, // user
nil, // password
nil, // open_options
mountOptions, // 直接传递 NSMutableDictionary,自动桥接为 CFMutableDictionary
&mountPoints
)
log:
0 sandboxd: (TCC) [com.apple.TCC:cache] REMOVE: (kTCCServiceSystemPolicyAppData, <Credential (0x7ed0b4230) | Audit Token, 42834.109774/501>)
2026-03-03 21:38:27.656702+0800 0x2de8d8 Info 0x867e9d 408 0 sandboxd: (TCC) [com.apple.TCC:cache] SET: (kTCCServiceSystemPolicyAppData, <Credential (0x7ed0b4230) | Audit Token, 42834.109774/501>) -> <Authorization Record (0x7ecca8180) | Service: kTCCServiceSystemPolicyAppData, AuthRight: Unknown, Reason: None, Version: 1, Session pid: 42832, Session pid version: 109769, Boot UUID: 7DDB03FC-132C-4E56-BA65-5C858D2CC8DD, >
2026-03-03 21:38:27.656753+0800 0x2de8d8 Default 0x867e9d 408 0 sandboxd: (libxpc.dylib) [com.apple.xpc:connection] [0x7ecc88640] invalidated after the last release of the connection object
2026-03-03 21:38:27.656772+0800 0x2de8d8 Debug 0x867e9b 408 0 sandboxd: (TCC) [com.apple.TCC:access] disposing: 0x7ecc3aa80(OS_tcc_message_options)
2026-03-03 21:38:27.656779+0800 0x2de8d8 Debug 0x867e9b 408 0 sandboxd: (TCC) [com.apple.TCC:access] disposing: 0x7ecc44820(OS_tcc_server)
2026-03-03 21:38:27.656788+0800 0x2de8d8 Info 0x867e9b 408 0 sandboxd: [com.apple.sandbox:sandcastle] kTCCServiceSystemPolicyAppData would require prompt by TCC for mount_nfs
I’m encountering a strange, sporadic error in FileManager.replaceItemAt(_:withItemAt:) when trying to update files that happen to be stored in cloud containers such as iCloud Drive or Dropbox. Here’s my setup:
I have an NSDocument-based app which uses a zip file format (although the error can be reproduced using any kind of file).
In my NSDocument.writeToURL: implementation, I do the following:
Create a temp folder using FileManager.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: fileURL, create: true).
Copy the original zip file into the temp directory.
Update the zip file in the temp directory.
Move the updated zip file into place by moving it from the temp directory to the original location using FileManager.replaceItemAt(_:withItemAt:).
This all works perfectly - most of the time. However, very occasionally I receive a save error caused by replaceItemAt(_withItemAt:) failing. Saving can work fine for hundreds of times, but then, once in a while, I’ll receive an “operation not permitted” error in replaceItemAt.
I have narrowed the issue down and found that it only occurs when the original file is in a cloud container - when FileManager.isUbiquitousItem(at:) returns true for the original fileURL I am trying to replace. (e.g. Because the user has placed the file in iCloud Drive.) Although strangely, the permissions issue seems to be with the temp file rather than with the original (if I try copying or deleting the temp file after this error occurs, I’m not allowed; I am allowed to delete the original though - not that I’d want to of course).
Here’s an example of the error thrown by replaceItemAt:
Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “test-file.txt” in the folder “Dropbox”." UserInfo={NSFileBackupItemLeftBehindLocationKey=file:///var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSFileOriginalItemLocationKey=file:///var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSURL=file:///Users/username/Library/CloudStorage/Dropbox/test-file.txt, NSFileNewItemLocationKey=file:///Users/username/Library/CloudStorage/Dropbox/test-file.txt, NSUnderlyingError=0xb1e22ff90 {Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file “test-file.txt” in the folder “NSIRD_TempFolderBug_y3UvzP”." UserInfo={NSURL=file:///var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSFilePath=/var/folders/mt/0snrr8fx7270rm0b14ll5k500000gn/T/TemporaryItems/NSIRD_TempFolderBug_y3UvzP/test-file.txt, NSUnderlyingError=0xb1e22ffc0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}}}
And here’s some very simple sample code that reproduces the issue in a test app:
// Ask user to choose this via a save panel.
var savingURL: URL? {
didSet {
setUpSpamSave()
}
}
var spamSaveTimer: Timer?
// Set up a timer to save the file every 0.2 seconds so that we can see the sporadic save problem quickly.
func setUpSpamSave() {
spamSaveTimer?.invalidate()
let timer = Timer(fire: Date(), interval: 0.2, repeats: true) { [weak self] _ in
self?.spamSave()
}
spamSaveTimer = timer
RunLoop.main.add(timer, forMode: .default)
}
func spamSave() {
guard let savingURL else { return }
let fileManager = FileManager.default
// Create a new file in a temp folder.
guard let replacementDirURL = try? fileManager.url(for: .itemReplacementDirectory, in: .userDomainMask, appropriateFor: savingURL, create: true) else {
return
}
let tempURL = replacementDirURL.appendingPathComponent(savingURL.lastPathComponent)
guard (try? "Dummy text".write(to: tempURL, atomically: false, encoding: .utf8)) != nil else {
return
}
do {
// Use replaceItemAt to safely move the new file into place.
_ = try fileManager.replaceItemAt(savingURL, withItemAt: tempURL)
print("save succeeded!")
try? fileManager.removeItem(at: replacementDirURL) // Clean up.
} catch {
print("save failed with error: \(error)")
// Note: if we try to remove replaceDirURL here or do anything with tempURL we will be refused permission.
NSAlert(error: error).runModal()
}
}
If you run this code and set savingURL to a location in a non-cloud container such as your ~/Documents directory, it will run forever, resaving the file over and over again without any problems.
But if you run the code and set savingURL to a location in a cloud container, such as in an iCloud Drive folder, it will work fine for a while, but after a few minutes - after maybe 100 saves, maybe 500 - it will throw a permissions error in replaceItemAt.
(Note that my real app has all the save code wrapped in file coordination via NSDocument methods, so I don’t believe file coordination to be the problem.)
What am I doing wrong here? How do I avoid this error? Thanks in advance for any suggestions.
Hi everyone,
I’m running out of space on my Mac and I need to use an external SSD to free up room, especially because Xcode is taking a lot of storage. I want to know if it’s safe to move some Xcode files—projects, derived data, or system-related files—to an external SSD. Which files can I move safely, and which should stay on the internal drive? I want to avoid breaking anything or causing issues with Xcode or macOS.
Also, is it the same situation if I use an external HDD instead of an SSD? Are there additional risks or performance issues to consider?
The man page of copyfile sates the following:
COPYFILE_CLONE [..] Note also that there is no support for cloning directories"
COPYFILE_CLONE_FORCE [...] Note also that there is no support for cloning directories: if a directory is provided as the source, an error will be returned.
Now the man page for clonefile:
> Cloning directories with these functions is strongly discouraged. Use copyfile(3) to clone directories instead.
--
So am I to enumerate the content of a directory build subfolders along the way in the target destination and clone each file inside individually? If I recall NSFileManager seems to clone a large directory instantly (edit actually I remembered wrong NSFileManager does not do this. Finder seems to copy instead of clone as well).
On further inspection, clonefile states that it can do this, but it is discouraged. Interesting. I wonder why.
If src names a directory, the directory hierarchy is cloned as if each item was cloned individually. However, the use of clonefile(2) to clone directory hierarchies is strongly discouraged. Use copyfile(3) instead for copying directories.
P.S. - Forgive me if I posting this in the wrong category, I couldn't find a "category" in the list of available categories on these forums that seems appropriate for this question.
The Numbers.app reopens the last edited document when the app launches.
If the document was moved to another folder in the Files.app while the app was not running, Numbers.app correctly tracks the file and reopens it.
However, if the document was deleted in the Files.app and moved to Recently Deleted, Numbers.app does not reopen the document when the app launches.
Question :
How does Numbers.app detect that a document has been moved to Recently Deleted?
Can third-party apps implement the same behavior?
What I tested :
If a file is moved while the app is not running, resolving a bookmark successfully tracks the moved file.
Files that are deleted via the Files.app appear in Recently Deleted, but those files are actually moved to the following directories:
iCloud Drive
/var/mobile/Library/Mobile Documents/.Trash/
On My iPad
/var/mobile/Containers/Shared/AppGroup/{UUID}/File Provider Storage/.Trash/
App sandbox Documents directory ([On My iPad]/[Any App])
/var/mobile/Containers/Data/Application/{UUID}/Documents/.Trash/
When resolving the bookmark after deletion, the bookmark still resolves successfully and returns the new file URL inside the .Trash directory.
I tried the following checks on the resolved URL:
Checking file existence
Checking read/write accessibility
Inspecting bookmark resolution results
Using APIs related to NSTrashDirectory
See https://developer.apple.com/forums/thread/813329#813329021
All of these behaved the same as when the file was moved to a normal directory. None of these checks allowed me to detect that the file had been deleted.
Additional experiment:
I suspected that the app might simply check whether the path contains ".Trash", so I performed the following experiment.
If a .numbers file is moved to
/var/mobile/Containers/Data/Application/{UUID}/Documents/.Trash/
then
The file appears in Recently Deleted in Files.app
Numbers.app does not reopen the document when the app launches
However, if the same file is moved to
Documents/Trash
Documents/.Trashed
Documents/Any Folder/.Trash
then
The file does not appear in Recently Deleted in Files
Numbers does reopen the document when launched
This suggests that Numbers.app is not simply checking whether the path contains ".Trash".
I am developing an iOS/iPadOS application and have encountered some behavior regarding Files App and security-scoped bookmarks that I would like to clarify.
Additionally, I would like to report some behavior which might include a potential issue.
Question1: Accessing deleted files via bookmark (Specification clarification)
Our app saves file URLs as bookmarks, which file that user has selected on Files App or app-created so to open a file which user has modified previously in the next launch.
When a user deletes a file in Files App (moves a file to Recently Deleted), the app can still resolve the bookmark and access the file for read/write operations.
Is this behavior intended?
In other words, is it correct that a bookmark can access a file that has been deleted in Files App but not permanently removed?
Question2: Overwriting a file in Recently Deleted (Potential bug)
We noticed that overwriting a file in Recently Deleted behaves differently depending on the method used.
Current implementation
1.Create a temporary file in the same directory
2.Write content to the temporary file
3.Delete the original file ([NSFileManager removeItemAtURL:error:])
4.Move the temporary file to the original file path ([NSFileManager moveItemAtURL:toURL:error:])
Result: The file disappears from Files App Recently Deleted.
In contrast, using
[NSFileManager replaceItemAtURL:withItemAtURL:]
keeps the file visible in Recently Deleted.
Is this difference designed behavior?
If not, this may be a bug.
Question3: Detecting files in Recently Deleted
We want to detect whether a file resides in Recently Deleted, but we cannot find a reliable and officially supported method.
Recently Deleted files appear under .Trash, but using the path alone is not a reliable method.
We have tried the following APIs without success:
[NSURL getResourceValue:forKey:NSURLIsHiddenKey error:]
[NSURL checkResourceIsReachableAndReturnError:]
[NSFileManager fileExistsAtPath:]
[NSFileManager isReadableFileAtPath:]
[NSFileManager getRelationship:ofDirectory:NSTrashDirectory inDomain:NSUserDomainMask toItemAtURL:error:]
We could not obtain the Recently Deleted folder URL using standard APIs.
[NSFileManager URLsForDirectory:NSTrashDirectory inDomains:NSUserDomainMask]
[NSFileManager URLForDirectory:NSTrashDirectory inDomain:NSUserDomainMask appropriateForURL:url create:error:]
Could you advise a safe and supported way to detect Recently Deleted files properly by the app?
A user of my app sent me a crash report. I have never seen one like this before. All of my app's symbols are replaced with three question marks (???)
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 ??? 0x10844eb40 ???
1 CoreFoundation 0x7ff80f155518 __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 137
and the binary image as
0x0 - 0xffffffffffffffff ??? (*) <00000000-0000-0000-0000-000000000000> ???
so I cannot find out where exactly the crash happened.
What can cause this kind of crash report and can I do anything with it?
crash.ips
I'll try to ask a question that makes sense this time :) . I'm using the following method on NSFileManager:
(BOOL) getRelationship:(NSURLRelationship *) outRelationship
ofDirectoryAtURL:(NSURL *) directoryURL
toItemAtURL:(NSURL *) otherURL
error:(NSError * *) error;
Sets 'outRelationship' to NSURLRelationshipContains if the directory at 'directoryURL' directly or indirectly contains the item at 'otherURL', meaning 'directoryURL' is found while enumerating parent URLs starting from 'otherURL'. Sets 'outRelationship' to NSURLRelationshipSame if 'directoryURL' and 'otherURL' locate the same item, meaning they have the same NSURLFileResourceIdentifierKey value. If 'directoryURL' is not a directory, or does not contain 'otherURL' and they do not locate the same file, then sets 'outRelationship' to NSURLRelationshipOther. If an error occurs, returns NO and sets 'error'.
So this method falsely returns NSURLRelationshipSame for different directories. One is empty, one is not. Really weird behavior. Two file path urls pointing to two different file paths have the same NSURLFileResourceIdentifierKey? Could it be related to https://developer.apple.com/forums/thread/813641 ?
One url in the check lived at the same file path as the other url at one time (but no longer does). No symlinks or anything going on. Just plain directory urls.
And YES calling -removeCachedResourceValueForKey: with NSURLFileResourceIdentifierKey causes proper result of NSURLRelationshipOther to be returned. And I'm doing the check on a background queue.
So if I create an Alias of a folder in Finder and hand the alias to my app (I also moved the alias file to a new folder, but I did not move the original folder)...so then my app resolves the alias using:
NSURL +URLByResolvingAliasFileAtURL:
What happens?
The resolved URL points to a completely different folder. Well not completely different. It resolves to a folder that happens to share same last path component as the original folder...but this folder is inside the same parent folder the alias file is in. It does not resolve to the original folder I created the alias of.
So then once my app touches the alias with +URLByResolvingAliasFileAtURL: the alias now resolves to this new (wrong) location (even in Finder).
Couple details:
My app is not sandboxed.
I have permission to access the original folder in my app (but even if I didn't the alias shouldn't be mutated just by merely resolving it).
Only seems to happen if the folder I move the alias to happens to contain a sibling folder that has the same title as the original folder. Like it's just deleting the last path component of the alias and then appending the last path component of the original filename and just going with that. I hope that makes sense.
I tried creating the alias myself using -bookmarkDataWithOptions: and going the other way (to Finder) but Finder must be resolving the alias using a different API because it resolves to the original location as expected.
I developed a cloud drive using fskit, but after mounting it, it did not appear in the Finder sidebar and the disk tool could not list it. How should I adapt?
The mounting looks successful, and you can also open and see the fixed files I wrote in the code.
I have also turned on the Finder sidebar settings function
Topic:
App & System Services
SubTopic:
Core OS
Tags:
Files and Storage
Extensions
Disk Arbitration
FSKit
Hi,
I'm encountering a weird issue on iOS that happens:
for files with diacritics in their name, like "Gòmez.pdf" or "Télé.mp4",
when the iPhone or iPad main language is not set to English,
if the file has been created with a relatively low-level Unix function like fopen() or copyfile().
Then, the file cannot be previsualized using QuickLook or opened using other apps. Most of the time it fails silently, but on some occasions I get the following error message: "You do not have permission to save the file "filename.pdf" in the folder "myFolder"".
The issue is present in, at least, iOS 16 and 26. It seems worse in iOS 26. It seems that all three conditions are required, I don't see the issue when the iPhone or iPad is set to use English as the main language. I also don't see the issue if I rename the files in the Files app.
I'm probably doing something wrong, but what can it be?
(it's kind of weird that my recommendation for users becomes: if you want to use international characters in your file names, you need to set the iPad language to English...)
Topic:
App & System Services
SubTopic:
Core OS
Tags:
APFS
Internationalization
QuickLook
Files and Storage
I set the value of NSURLHasHiddenExtensionKey. I provide a textfield to rename a file and I set this flag based on whether the user has deleted or left on the path extension.
So -setResourceValue:forKey:error: returns YES, does not populate the error, but does not honor the value I just set it to.
I'm always setting it off the main thread on the same serial queue. Works the first time I rename the file then it just starts failing (silently).
For example:
NSError *setError = nil;
if ([theURL setResourceValue:@(NO) forKey:NSURLHasHiddenExtensionKey error:&setError])
{
[theURL removeAllCachedResourceValues];
NSNumber *freshRead = nil;
NSError *getError = nil;
if ([theURL getResourceValue:&freshRead forKey:NSURLHasHiddenExtensionKey error:&getError])
{
if (freshRead.boolValue)
{
NSLog(@"it is yes when it should be NO.");
}
}
if (getError != nil)
{
NSLog(@"Get error: %@",getError);
}
}
if (setError != nil)
{
NSLog(@"Set error: %@",setError);
}
While I get that it is possible for there to be other apps setting this value at the same time as my app, doesn't really seem possible in my local environment right now.
No errors log out but "it is yes when it should be NO." does log out.
Apple provides sample code for compressing a single file here, but the "aa" command and Finder cannot decompress these files. I needed to write a custom decompresser using Apple's sample code here.
Apple provides sample code for creating an "aar" file for directories here and a single string here, and the aa command and Finder can deal with these.
However, I have struggled creating an ".aar" file for a single file.
The file could be quite large, so reading it into memory and writing it as a blob is not an option.
Does anyone have suggestions or can point me to Apple documentation that can create a ".aar" file for a single file?
So I'm reworking couple things in my app. And I noticed I had this old code that does the following:
Creates a temporary directory.
Writes a file in the temporary directory.
After the file is written moves the file out of the temporary location and places it in its final destination.
Okay so I was not creating the temporary directory using the recommended API. I was simply doing something like this:
NSURL *tempDirectory = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:[NSProcessInfo processInfo].globallyUniqueString]];
// Create tempDirectory and write files inside it.
Now I just changed the code to use the recommended API which takes the the volume of the target destination into account: -URLForDirectory:inDomain:appropriateForURL:create:error:) and I pass in NSItemReplacementDirectory and a url to appropriateForURL so the destination volume is taken into account.
Now I have external storage mounted and I use the recommended approach. I discovered NSFileManager simply writes a directory right in a user facing location titled: **(A Document Being Saved By App Name) ** and the folder is not hidden.
Is this intended behavior for this API? Are temporary files supposed to be user facing? I know it is good practice to clean up temporary stuff when you are done but in crashes or just forgetting to clean up will leave these behind which isn't the behavior I expect for "temporary files." Also if the user is viewing the folder in Finder they'll see these A Document Being Saved By App Name folders appear and disappear in the window as my app does this work.
Since the introduction of the siblings / and /System/Volumes/Data architecture, some very basic, critical commands seems to have a broken behaviour ( cp, rsync, tar, cpio…).
As an example, ditto which was introduced more than 10 years ago to integrate correctly all the peculiarity of HFS Apple filesystem as compared to the UFS Unix filesystem is not behaving correctly.
For example, from man ditto:
--rsrc Preserve resource forks and HFS meta-data. ditto will
store this data in Carbon-compatible ._ AppleDouble files
on filesystems that do not natively support resource forks.
As of Mac OS X 10.4, --rsrc is default behavior.
[...]
--extattr Preserve extended attributes (requires --rsrc). As of Mac
OS X 10.5, --extattr is the default.
and nonetheless:
# ls -@delO /private/var/db/ConfigurationProfiles/Store
drwx------@ 5 root wheel datavault 160 Jan 20 2024 /private/var/db/ConfigurationProfiles/Store
*********
com.apple.rootless 28
***************************
# mkdir tmp
# ditto /private/var/db/ConfigurationProfiles tmp
ditto: /Users/alice/Security/Admin/Apple/APFS/tmp/Settings: Operation not permitted
ditto: /Users/alice/Security/Admin/Apple/APFS/tmp/Store: Operation not permitted
# ls -@delO tmp/Store
drwx------ 5 root wheel - 160 Aug 8 13:55 tmp/Store
*
#
The extended attribute on copied directory Store is empty, the file flags are missing, not preserved as documented and as usual behaviour of ditto was since a long time ( macOS 10.5 ).
cp, rsync, tar, cpio exhibit the same misbehaviour. But I was using ditto to be sure to avoid any incompatibility with the Apple FS propriaitary modifications.
As a consequence, all backup scripts and applications are failing more or less silently, and provide corrupted copies of files or directories. ( I was here investigating why one of my security backup shell script was making corrupted backups, and only on macOS ).
How to recover the standard behaviour --extattr working on modern macOS?
Topic:
App & System Services
SubTopic:
Core OS
Tags:
Files and Storage
macOS
Security
Security Foundation
following the update of the system of my MBP 16" M1 to beta 26.4 none of my external drives work anymore