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 four different file system permission mechanisms:
Traditional BSD permissions
Access control lists (ACLs)
App Sandbox
Mandatory access control (MAC)
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. This post is my attempt to clear that up.
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 typically 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 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: Fight!
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.
Revision History
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
Sort by:
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
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"
Hi Apple team,
I'm experiencing a persistent issue with writing to UserDefaults from a widget extension on iOS. Here's the situation:
I've set up an App Group: group.test.blah
The main app has the correct entitlement and can read/write from UserDefaults(suiteName:) using this group successfully.
I can read the value written by the app from the widget (e.g., "testFromApp": "hiFromApp").
The widget extension has the same App Group enabled under Signing & Capabilities.
The provisioning profile reflects the App Group and the build installs successfully on a real device.
The suite name is correct and matches across both targets.
I’ve confirmed via FileManager.default.containerURL(...) that the app group container resolves properly.
When I try to write from the widget extension like this
let sharedDefaults = UserDefaults(suiteName: "group.test.blah")
sharedDefaults?.set("hiFromWidget", forKey: "testFromWidget")
...I get this error in the console:
Couldn't write values for keys (
testFromWidget
) in CFPrefsPlistSource<0x1140d2880> (Domain: group.test.blah, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null), Contents Need Refresh: No): setting preferences outside an application's container requires user-preference-write or file-write-data sandbox access
Questions:
What could still cause the widget extension to lack write access to the app group container, even though it reads just fine?
Are there any internal sandboxing nuances or timing-related issues specific to Live Activity widgets that could explain this?
Is this a known limitation or platform issue?
Topic:
App & System Services
SubTopic:
Widgets & Live Activities
Tags:
Extensions
Entitlements
ActivityKit
Files and Storage
working on app like dropbox and making a feature like dropbox offline download, so when a file is added from a system it will download in other system but as a placeholder file once user double click or open that file or after copy pasting to another location on demand need to download the file and after that do the action open or paste
so here I need file open event and paste event to be block untill it downloads.
how to achieve this uing obj c, c , c++ or swift
I have an app which uses ubiquitous containers and files in them to share data between devices. It's a bit unusual in that it indexes files in directories the user grants access to, which may or may not exist on a second device - those files are identified by SHA-1 hash. So a second device scanning before iCloud data has fully sync'd can create duplicate references which lead to an unpleasant user experience.
To solve this, I store a small binary index in the root of the ubiquitous file container of the shared data, containing all of the known hashes, and as the user proceeds through the onboarding process, a background thread is attempting to "prime" the ubiquitous container by calling FileManager.default.startDownloadingUbiquitousItemAt() for each expected folder and file in a sane order.
This likely creates a situation not anticipated by the iOS/iCloud integration's design, as it means my app has a sort of precognition of files it should not yet know about.
In the common case, it works, but there is a corner case where iCloud sync has just begun, and very, very little metadata is available (the common case, however, in an emulator), in which two issues come up:
I/O may hang indefinitely, trying to read a file as it is arriving. This one I can work around by running the I/O in a thread created with the POSIX pthread_create and using pthread_cancel to kill it after a timeout.
Attempts to call FileManager.default.startDownloadingUbiquitousItemAt() fails with an error Error Domain=NSCocoaErrorDomain Code=257 "The file couldn’t be opened because you don’t have permission to view it.". The permissions aspect of it is nonsense, but I can believe there's no applicable "sort of exists, sort of doesn't" error code to use and someone punted. The problem is that this same error will be thrown on any attempt to access that file for the life of the application - a restart is required to make it usable.
Clearly, the error or the hallucinated permission failure is cached somewhere in the bowels of iOS's FileManager. I was hoping startAccessingSecurityScopedResource() would allow me to bypass such a cache, as it does with URL.resourceValues() returning stale file sizes and last modified times. But it does not.
Is there some way to clear this state without popping up a UI with an Exit button (not exactly the desired iOS user experience)?
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
Foundation
Files and Storage
iOS
iCloud Drive
Hi,
I'm having trouble implementing iCloud Drive in my app. I've already taken the obvious steps, including enabling iCloud Documents in Xcode and selecting a container. This container is correctly specified in my code, and in theory, everything should work.
The data generated by my app should be saved to iCloud Drive in addition to local storage. The data does get stored in the Files app, but the automatic syncing to iCloud Drive doesn’t work as expected.
I’ve also considered updating my .entitlements file.
Since I’m at a loss, I’m reaching out for help maybe I’ve overlooked something important that's causing it not to work. If anyone has an idea, please let me know.
Thanks in advance!
Hello everyone,
I am new to swift development and I am currently facing a "bug".
I am building, an app on my Mac mini. It's working fine on my machine but when exporting the executable on a MacBook Air, one of my feature does not work anymore.
I should be able to drag and drop a PDF which should be copy to my App document folder. But for some reason it won't work.
I should add that :
The app is sandboxed
I tried to build the app on the MacBook Air and it does not work either.
I gave all the permission to the app in the MacBook Air parameter menu.
I have another drag and drop functionality with read a csv file, and it works.
With Xcode, the error message was about : file not found (after being read and recognized on my log)
I hope someone would have some ideas
Thank you in advance
PS: I'm French, sorry for my English
content = try String(contentsOfFile: filepath) is the following warning.
'init(contentsOfFile:)' was deprecated in iOS 18: Use init(contentsOfFile:encoding:) instead
After internet searching I could find an answer.
I would appreciate a rewrite of my code line to get rid of 31 warnings.
Thanks a LOT
Charlie
Dear Apple:
We are developing a file management-related app, I want to configure com.apple.security.temporary-exception.files.absolute-path.read-write to /Users/*** in the entitlements. Will this affect the approval process of our app on the App Store?
Dear Apple:
We are developing a file management-related app, and I would like to retrieve the list of files from the "Recents" section under "Favorites" in the Mac sidebar, then display this information in the app's interface for users. Is there an API available to obtain this information?
After creating a hardlink on a distributed filesystem of my own via:
% ln f.txt hlf.txt
Neither the original file, f.txt, nor the hardlink, hlf.txt, are immediately accessible, e.g. via cat(1) with ENOENT returned. A short time later though, both the original file and the hardlink are accessible. Both files can be stat(1)ed though, which confirms that vnop_getattr returns success for both files.
Dtruss(1) indicates it's the open(2) syscall that fails:
% sudo dtruss -f cat hlf.txt
2038/0x4f68: open("hlf.txt\0", 0x0, 0x0) = -1 Err#2 ;ENOENT
2038/0x4f68: write_nocancel(0x2, "cat: \0", 0x5) = 5 0
2038/0x4f68: write_nocancel(0x2, "hlf.txt\0", 0x7) = 7 0
2038/0x4f68: write_nocancel(0x2, ": \0", 0x2) = 2 0
2038/0x4f68: write_nocancel(0x2, "No such file or directory\n\0", 0x1A) = 26 0
Dtrace(1)ing my KEXT no longer works on macOS Sequoia, so based on the diagnostics print statements I inserted into my KEXT, the following sequence of calls is observed:
vnop_lookup(hlf.txt) -> EJUSTRETURN ;ln(1)
vnop_link(hlf.txt) -> KERN_SUCCESS ;ln(1)
vnop_lookup(hlf.txt) -> KERN_SUCCESS ;cat(1)
vnop_open(/) ; I expected to see vnop_open(hlf.txt) here instead of the parent directory.
Internally, hardlinks are created in vnop_link via a call to vnode_setmultipath with cache_purge_negatives called on the destination directory.
On macOS Monterey for example, where the same code does result in hardlinks being accessible, the following calls are made:
vnop_lookup(hlf.txt) -> EJUSTRETURN ;ln(1)
vnop_link(hlf.txt) -> KERN_SUCCESS ;ln(1)
vnop_lookup(hlf.txt) -> KERN_SUCCESS ;cat(1)
vnop_open(hlf.txt) -> KERN_SUCCESS ;cat(1)
Not sure how else to debug this.
Perusing the kernel sources for uses of VISHARDLINK, VNOP_LINK and vnode_setmultipath call sites did not clear things up for me.
Any pointers would be greatly appreciated.
Currently, I use NSFileProviderItemCapabilitiesAllowsAddingSubitems on a folder to control the creation of sub-items (either folders or files) within a parent folder. However, this capability doesn't allow me to meet a requirement where I need to permit file creation but restrict folder creation.
I am seeking input on different options to achieve this requirement.
Note: One reactive approach would be to intercept folder creation within the createItem() event handler and reject it with an ExcludedFromSync error (without uploading to cloud). This would prevent createItem() from being reattempted on that folder, but the folder would still remain on the mount. Is there any way to delete it?
We create plug-ins for Adobe Creative Cloud and have run into an issue with respect to file/folder permissions. First, all of our libraries, code is code-signed and notarized as per Apple requirements but distribute outside of the Mac App store.
We install a Photoshop plug-in and its mainly a UI which then executes a background app containing the business logic to read/write files. The background app runs as a separate process and is not in the Photoshop sandbox space so it doesn't inherit Photoshop permissions/scoping rules. Our plug-in communicates with the background process via ports etc.
When a user chooses a file to process from lets say the Desktop, generally macOS first pops up a message that says ABCD background app is trying to access files from the Desktop do you grant it permission etc...This is also true for network mounted volumes or downloads folder. This message generally appears properly when everything is under an account with admin rights.
However, when our tool is installed from a Standard Account, the macOS messages asking for confirmation to access the Desktop or Documents or Downloads folder doesn't appear and access to the file/folders is denied. Thus our background only process errors out. Looking at the Security and Privacy->Files and Folders the button to enable access is in the Off position. If we turn these on Manually, everything works.
But this is a really poor user experience and sometimes our users think our software is not working.
Does anybody have any idea how to allow for the file/folder permissions to be registered/granted in such a case? Should we try to register these as Full Disk Access? Any ideas and/or solutions are welcome.
I reported this as a bug (FB18614667), but also wanted to ask here in case this is actually just me doing something wrong, or maybe I'm misunderstanding the entire use case of metadataRead. (My understanding is that metadataRead is basically read but it checks a cache that the kernel manages before trying to read the physical resource, and in the case of a cache miss it would just go to the physical resource and then add the bytes to the cache. Is that right?)
I’m encountering an issue in an FSKit file system extension where (for example)
read(into: buf, startingAt: 0, length: Int(physicalBlockSize))
works, but
metadataRead(into: buf, startingAt: 0, length: Int(physicalBlockSize))
throws an EIO error (Input/output error) no matter what I do.
(Note: physicalBlockSize is 512 in this example.)
The documentation (https://developer.apple.com/documentation/fskit/fsblockdeviceresource/metadataread(into:startingat:length:)) indicates that the restrictions on metadataRead are that the operations must be sector-addressed (which is the case here, especially as regular read has the same restriction and succeeds) and that partial reading of metadata is not supported. (I don’t think that applies here?)
In a sample project I was able to replicate this behavior where the module only ever reads the block device in its enumerateDirectory implementation, and so trying to list the contents of a directory leads to an "Input/output error" when e.g. running ls on the volume.
The enumerateDirectory sample implementation is like so:
func enumerateDirectory(_ directory: FSItem, startingAt cookie: FSDirectoryCookie, verifier: FSDirectoryVerifier, attributes: FSItem.GetAttributesRequest?, packer: FSDirectoryEntryPacker) async throws -> FSDirectoryVerifier {
let buf = UnsafeMutableRawBufferPointer.allocate(byteCount: Int(blockDevice.physicalBlockSize), alignment: 1)
defer {
buf.deallocate()
}
// metadataRead will throw...
try blockDevice.metadataRead(into: buf, startingAt: 0, length: Int(blockDevice.physicalBlockSize))
// but read will work.
// try await blockDevice.read(into: buf, startingAt: 0, length: Int(blockDevice.physicalBlockSize))
// ... return dummy file here (won't reach this point because metadataRead throws)
}
I'm observing this behavior on both macOS 15.5 (24F74) and macOS 15.6 beta 3 (24G5074c).
Has anyone been able to get metadataRead to work? I see it used in Apple's msdos FSKit implementation so it seems like it has to work at some level.
I have discovered a gap in my understanding of user selected URLs in iOS, and I would be grateful if someone can put me right please.
My understanding is that a URL selected by a user can be accessed by calling url.startAccessingSecurityScopedResource() call. Subsequently a call to stopAccessingSecurityScopedResource() is made to avoid sandbox memory leaks.
Furthermore, the URL can be saved as a bookmark and reconstituted when the app is run again to avoid re-asking permission from the user.
So far so good.
However, I have discovered that a URL retrieved from a bookmark can be accessed without the call to url.startAccessingSecurityScopedResource(). This seems contrary to what the documentation says here
So my question is (assuming this is not a bug) why not save and retrieve the URL immediately in order to avoid having to make any additional calls to url.startAccessingSecurityScopedResource?
Bill Aylward
You can copy and paste the code below into a new iOS project to illustrate this. Having chosen a folder, the 'Summarise folder without permission' button fails as expected, but once the 'Retrieve URL from bookmark' has been pressed, it works fine.
import SwiftUI
import UniformTypeIdentifiers
struct ContentView: View {
@AppStorage("bookmarkData") private var bookmarkData: Data?
@State private var showFolderPicker = false
@State private var folderUrl: URL?
@State private var folderReport: String?
var body: some View {
VStack(spacing: 20) {
Text("Selected folder: \(folderUrl?.lastPathComponent ?? "None")")
Text("Contents: \(folderReport ?? "Unknown")")
Button("Select folder") {
showFolderPicker.toggle()
}
Button("Deselect folder") {
folderUrl = nil
folderReport = nil
bookmarkData = nil
}
.disabled(folderUrl == nil)
Button("Retrieve URL from bookmark") {
retrieveFolderURL()
}
.disabled(bookmarkData == nil)
Button("Summarise folder with permission") {
summariseFolderWithPermission(true)
}
.disabled(folderUrl == nil)
Button("Summarise folder without permission") {
summariseFolderWithPermission(false)
}
.disabled(folderUrl == nil)
}
.padding()
.fileImporter(
isPresented: $showFolderPicker,
allowedContentTypes: [UTType.init("public.folder")!],
allowsMultipleSelection: false
) { result in
switch result {
case .success(let urls):
if let selectedUrl = urls.first {
print("Processing folder: \(selectedUrl)")
processFolderURL(selectedUrl)
}
case .failure(let error):
print("\(error.localizedDescription)")
}
}
.onAppear() {
guard folderUrl == nil else { return }
retrieveFolderURL()
}
}
func processFolderURL(_ selectedUrl: URL?) {
guard selectedUrl != nil else { return }
// Create and save a security scoped bookmark in AppStorage
do {
guard selectedUrl!.startAccessingSecurityScopedResource() else { print("Unable to access \(selectedUrl!)"); return }
// Save bookmark
bookmarkData = try selectedUrl!.bookmarkData(options: .minimalBookmark, includingResourceValuesForKeys: nil, relativeTo: nil)
selectedUrl!.stopAccessingSecurityScopedResource()
} catch {
print("Unable to save security scoped bookmark")
}
folderUrl = selectedUrl!
}
func retrieveFolderURL() {
guard let bookmarkData = bookmarkData else {
print("No bookmark data available")
return
}
do {
var isStale = false
let url = try URL(
resolvingBookmarkData: bookmarkData,
options: .withoutUI,
relativeTo: nil,
bookmarkDataIsStale: &isStale
)
folderUrl = url
} catch {
print("Error accessing URL: \(error.localizedDescription)")
}
}
func summariseFolderWithPermission(_ permission: Bool) {
folderReport = nil
print(String(describing: folderUrl))
guard folderUrl != nil else { return }
if permission { print("Result of access requrest is \(folderUrl!.startAccessingSecurityScopedResource())") }
do {
let contents = try FileManager.default.contentsOfDirectory(atPath: folderUrl!.path)
folderReport = "\(contents.count) files, the first is: \(contents.first!)"
} catch {
print(error.localizedDescription)
}
if permission { folderUrl!.stopAccessingSecurityScopedResource() }
}
}
Getting "Error Domain=NSCocoaErrorDomain Code=513 "You don’t have permission to save the file" unexpectedly while attempting to create a small log file. Here's some background.
This is a Swift app I wrote for my own use six years ago.
A week ago, I made a small update to the app, which has not been changed in over two years. First time using Xcode 16.4 on this app, which required some code updates of course. The code creating this file has not been changed.
Now for the first time, I'm getting this permissions error on a folder with wide-open permissions.
This is the code. Worked for years under previous versions of Xcode.
*
if let outputURL = URL(string: "file://" + logPath + "/output_" + outputFormatter.string(from:Date()) + ".txt"){
do{
try outputString.write(to: outputURL, atomically:false, encoding: .utf8)
}catch let error as NSError{
print ("log write error (error) (nl) (outputString)")
}
}
I have a simple impossible task, to restore
/var/root/Library/Application Support/multipassd/qemu/vault/instances/gcc-cobol/ubuntu-22.04-server-cloudimg-arm64.img
as of 8:02 Saturday morning. Because /var/root is owned by, well, root, the usual techniques don't work.
This is a VM image hosted by qemu via Canonical's Multipass.
ISTM the strategy would be to first mount the NAS filesystem and then use tmutil(8) to list the backups and recover the file. But
$ sudo mount -v -o rdonly -t smb //nasa.local/TimeMachine /usr/local/mnt/
mount: exec /Library/Filesystems/smb.fs/Contents/Resources/mount_smb for /usr/local/mnt: No such file or directory
mount: /usr/local/mnt failed with 72
Must I defeat SIP to do this?
I've faced with some performance issues developing my readonly filesystem using fskit.
For below screenshot:
enumerateDirectory returns two hardcoded items, compiled with release config
3000 readdirsync are done from nodejs.
macos 15.5 (24F74)
I see that getdirentries syscall takes avg 121us.
Because all other variables are minimised, it seems like it's fskit<->kernel overhead.
This itself seems like a big number. I need to compare it with fuse though to be sure.
But what fuse has and fskit seams don't (I checked every page in fskit docs) is kernel caching.
Fuse supports:
caching lookups (entry_timeout)
negative lookups (entry_timeout)
attributes (attr_timeout)
readdir (via opendir cache_readdir and keep_cache)
read and write ops but thats another topic.
And afaik it works for both readonly and read-write file systems, because kernel can assume (if client is providing this) that cache is valid until kernel do write operations on corresponding inodes (create, setattr, write, etc).
Questions are:
is 100+us reasonable overhead for fskit?
is there any way to do caching by kernel. If not currently, any plans to implement?
Also, additional performance optimisation could be done by providing lower level api when we can operate with raw inodes (Uint64), this will eliminate overhead from storing, removing and retrieving FSItems in hashmap.
Hi there,
I have discovered that the behavior of file copying has changed starting from iOS 18.4.
When using FileManager.copyItem(atPath:toPath:) to copy a directory specified as an argument, whether or not there is a trailing slash ('/') affects whether the copy process works correctly.
The same process operates as expected in the iOS 18.3.1 Simulator.
Is this the correct behavior, or could it be a bug?
The application's build environment is Xcode 16.2.
Below is an example of the code. In practice, the file copying is performed within the application's folder.
// Both iOS 18.3.1 and iOS 18.4 successfully complete the copy process.
FileManager.default.copyItem(atPath: "/path/from/dirA", toPath: "/path/to/dirB")
FileManager.default.copyItem(atPath: "/path/from/dirA/", toPath: "/path/to/dirB/")
// iOS 18.3.1 successfully complete the copy process, but iOS 18.4 fails.
FileManager.default.copyItem(atPath: "/path/from/dirA/", toPath: "/path/to/dirB")
I hope this helps Apple engineers and other developers experiencing the same issue. Feedback or additional insights would be appreciated.
Question:
What is the standard, most reliable way to manage temporary files associated with a URLSessionDownloadTask that has been terminated abnormally due to a network error or other issues?
Details
Hello,
I'm currently developing a feature to download multiple files concurrently on iOS using URLSessionDownloadTask, and I have a question regarding the lifecycle of the temporary files created during this process.
As I understand it, URLSessionDownloadTask stores incoming data in a temporary file within the tmp directory, typically with a name like CFNetworkDownload_*.tmp.
In my testing, temporary files are managed correctly in the normal scenario. For instance, when I call the cancel() method on an active downloadTask and then release all references to it, the corresponding temporary file is automatically cleaned up from the tmp directory shortly after.
However, the problem occurs when a download is interrupted abnormally due to external factors, such as a lost network connection. In this situation, the urlSession(_:task:didCompleteWithError:) delegate method is called, but the associated temporary file is not deleted and remains in the tmp directory.
I've observed a particularly interesting behavior related to this. Immediately after the error occurs, if I check my app's storage usage in the iOS Settings app, the data size appears to have decreased momentarily. However, the tmp file has not actually been deleted, and after a short while, the storage usage is recalculated to include the size of this orphaned temporary file.
Since my app does not support resuming interrupted downloads, these leftover files become orphaned and unnecessarily consume storage. Therefore, I want to ensure they are all reliably deleted.
With this context, I'd like to ask the community:
What is the standard, most reliable way to manage temporary files associated with a URLSessionDownloadTask that has been terminated abnormally due to a network error or other issues?
I am wondering if there is an official guide or a framework-level API to handle these orphaned files.
I would appreciate any advice from those with experience in this area. Thank you.
A user of my app reported that when trying to remove a file it always fails with the error "file couldn't be removed because you don't have permission to access it (Cocoa Error Domain 513)". After some testing, we found out that it's caused by trying to delete non-empty directories.
I'm using FileManager.removeItem(atPath:) which has worked fine for many years, but it seems that with their particular NAS, it doesn't work.
I could work around this by checking if the file is a directory, and if it is, enumerating the directory and remove each contained file before removing the directory itself. But shouldn't this already be taken care of? In the source code of FileManager I see that for Darwin platforms it calls
removefile(pathPtr, state, removefile_flags_t(REMOVEFILE_RECURSIVE))
so it seems that it should already work. Is the REMOVEFILE_RECURSIVE flag perhaps ignored by the device? But then, is the misleading "you don't have permission to access the file" error thrown by the device or by macOS?
For the FileManager source code, see https://github.com/swiftlang/swift-foundation/blob/1d5d70997410fc8b7700c8648b10d6fc28194202/Sources/FoundationEssentials/FileManager/FileOperations.swift#L444
I attempted to mount a WebDAV server on macOS using Finder. The mount was successful when using the server's IPv4 address or hostname, but failed when using its IPv6 address.
I also tested the mount using the NetFS framework API via NetFSMountURLSync. The results were consistent: the mount succeeded with the IPv4 address and hostname but failed when using the IPv6 address.
While observing live logs via Console.app, I saw that the process NetAuthSysAgent produced the following error during the IPv6 mount attempt: GetServerInfo failed with error 5