How can I get the system to use my FSModule for probing?

I've gotten to the point where I can use the mount(8) command line tool and the -t option to mount a file system using my FSKit file system extension, in which case I can see a process for my extension launch, probe, and perform the other necessary actions.

However, when plugging in my USB flash drive or trying to mount with diskutil mount, the file system does not mount:

$ diskutil mount disk20s3
Volume on disk20s3 failed to mount
If you think the volume is supported but damaged, try the "readOnly" option
$ diskutil mount readOnly disk20s3
Volume on disk20s3 failed to mount
If you think the volume is supported but damaged, try the "readOnly" option

Initially I thought it would be enough to just implement probeExtension(resource:replyHandler:) and the system would handle the rest, but this doesn't seem to be the case. Even a trivial implementation that always returns .usable doesn't cause the system to use my FSModule, even though I've enabled my extension in System Settings > General > Login Items & Extensions > File System Extensions.

From looking at some of the open source msdos and Disk Arb code, it seems like my app extension needs to list FSMediaTypes to probe. I eventually tried putting this in my Info.plist of the app extension:

<key>FSMediaTypes</key>
<dict>
	<key>EBD0A0A2-B9E5-4433-87C0-68B6B72699C7</key>
	<dict>
		<key>FSMediaProperties</key>
		<dict>
			<key>Content Hint</key>
			<string>EBD0A0A2-B9E5-4433-87C0-68B6B72699C7</string>
			<key>Leaf</key>
			<true/>
		</dict>
	</dict>
	<key>0FC63DAF-8483-4772-8E79-3D69D8477DE4</key>
	<dict>
		<key>FSMediaProperties</key>
		<dict>
			<key>Content Hint</key>
			<string>0FC63DAF-8483-4772-8E79-3D69D8477DE4</string>
			<key>Leaf</key>
			<true/>
		</dict>
	</dict>
	<key>Whole</key>
	<dict>
		<key>FSMediaProperties</key>
		<dict>
			<key>Leaf</key>
			<true/>
			<key>Whole</key>
			<true/>
		</dict>
	</dict>
	<key>ext4</key>
	<dict>
		<key>FSMediaProperties</key>
		<dict>
			<key>Content Hint</key>
			<string>ext4</string>
			<key>Leaf</key>
			<true/>
		</dict>
	</dict>
</dict>
</plist>

(For reference, the partition represented by disk20s3 has a Content Hint of 0FC63DAF-8483-4772-8E79-3D69D8477DE4 and Leaf is True which I verified using IORegistryExplorer.app from the Xcode additional tools.)

Looking in Console it does appear now that the system is trying to use my module (ExtendFS_fskit) to probe when I plug in my USB drive, but I never see a process for my extension actually launch when trying to attach to it from Xcode by name (unlike when I use mount(8), where I can do this). However I do see a Can't find the extension for <private> error which I'm not sure is related but does sound like the system can't find the extension for some reason.

The below messages are when filtering for "FSKit":

default	19:14:53.455826-0400	diskarbitrationd	probed disk, id = /dev/disk20s3, with ExtendFS_fskit, ongoing.
default	19:14:53.456038-0400	fskitd	Incomming connection, entitled 1
default	19:14:53.456064-0400	fskitd	[0x7d4172e40] activating connection: mach=false listener=false peer=true name=com.apple.filesystems.fskitd.peer[350].0x7d4172e40
default	19:14:53.456123-0400	fskitd	Hello FSClient! entitlement yes
default	19:14:53.455902-0400	diskarbitrationd	[0x7461d8dc0] activating connection: mach=true listener=false peer=false name=com.apple.filesystems.fskitd
default	19:14:53.456151-0400	diskarbitrationd	Setting remote protocol to all XPC
default	19:14:53.456398-0400	fskitd	About to get current agent for 501
default	19:14:53.457185-0400	diskarbitrationd	probed disk, id = /dev/disk20s3, with ExtendFS_fskit, failure.
error	19:14:53.456963-0400	fskitd	-[fskitdXPCServer applyResource:targetBundle:instanceID:initiatorAuditToken:authorizingAuditToken:isProbe:usingBlock:]: Can't find the extension for <private>

(I only see these messages after plugging my USB drive in. When running diskutil mount, I see no messages in the console when filtering by FSKit, diskarbitrationd, or ExtendFS afterward. It just fails.)

Is there a step I'm missing to get this to work, or would this be an FSKit bug/current limitation?

Answered by DTS Engineer in 846292022

I looked over the data you sent and, yes, I think that's what's going on

Correcting myself, I think the issue might actually be on your side. After digging through the log more, it looks like mounting had actually started and what actually failed was this:

 2025-06-26 03:05:07.387063-0700 796 ProbingBugSampleExtension: (FSKit) [com.apple.FSKit:default] -[FSModuleVolume(Project) getMaxFileSizeInBits]: Volume does not implement both maxFileSizeInBits and maxFileSize, while one of them must be implemented.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Is there a step I'm missing to get this to work, or would this be an FSKit bug/current limitation?

I haven't had a chance to look into this yet and I'm not sure when I will (given WWDC prep) but in the meantime please file a bug on this then post the bug number once it's filed. As you're seeing, there are significant gaps in the FSKit documentation and we need bugs filed to get those addressed.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

@DTS Engineer No worries, I'm sure this is a busy time for all of you. Filed FB17772372, sample project included.

As a side note, you only need "-F" with mount when you have BOTH an FSKit implementation and a KEXT implementation with the same short name. By default, any existing KEXT implementations are preferred over FSKit implementations.

When a short name matches only an FSKit implementation, it will be selected by the mount(8) command.

@DTS Engineer No worries, I'm sure this is a busy time for all of you. Filed FB17772372, sample project included.

I was able to get the engineering team to look at your bug and here's where things stand:

  1. There is a bug on our side (specifically in diskarbitrationd) which is causing probe to incorrectly fail. I obviously can't comment on release schedules or plans, but the bug is considered a high priority.

  2. The project you included does not implement FSManageableResourceMaintenanceOperations, but DiskArb requires a success from startCheck(task:options:) in order to automount. Note that you can test/run this particular task using /sbin/fsck_fskit.

Per the engineering team, once both of those are resolved (to be clear, #1 being entirely our bug), DiskArb should automount that volume/driver. To be clear, #2 may have been a choice on your part to minimize the test project (which is perfectly reasonable), but I want to pass that back to you just to make sure you were aware of the issue.

Also, as a minor note on FSManageableResourceMaintenanceOperations, while diskutil does not currently have full integration with FSKit, you can test/use startFormat(task:options:) by running /sbin/newfs_fskit.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Note also that diskarbitrationd requires that a file system implement check (fsck) in order to be mounted via DA.

For FSKit, a module should conform to FSManageableResourceMaintenanceOperations and implement startCheckWithTask:options:error:.

This method accepts a task, and attempts to start a check on the container. This method is called after a loadResource:, which supplied the resource(s). It should parse the command options options and assess. If there are no errors in the command options, the method should start the check. Otherwise it should return an error (throw in Swift, *error = in ObjC).

startCheckWithTask: (and startFormatWithTask:) either returns an NSProgress * when the task starts, or it returns an error.

The check (or format) must be performed on another thread – asynchronously.

Once a task successfully starts, the module reports task completion by calling the didCompleteWithError: method on the supplied task. Pass in nil for successful completion and an error for failure.

The two command line options required by DiskArbitration are -y and -q. -q performs a "quick" check. This check should quickly assess the file system to see if it needs repair. If this check fails, DiskArbitration will perform a full check with -y before minting the file system. If that check passes, DiskArbitration will mount the file system. Otherwise it will not.

A common flow for -q is to assess that the superblock looks ok (right signature?) and assess if the file system is flagged as dirty.

The fsck_fskit -t shortname command can start a check operation.

The same flow applies for formatting. A module conforms to FSManageableResourceMaintenanceOperations, implements the required method startCheckWithTask:options:error:, and then implements the optional method startFormatWithTask:options:error:. The newfs_fskit -t shortname tool can perform a format.

For anyone following this who can't see FB17772372, I received a reply on my bug report stating that the issue should be fixed in macOS 15.6 beta (24G5054d).

I tested this and probing now seems to work, although I'm still encountering issues at the mount stage when Disk Arb tries to automount. Based on my observations in Console it seems like it can't find the module, although mount(8) still works. I replied to the feedback with additional details.

For anyone following this who can't see FB17772372, I received a reply on my bug report stating that the issue should be fixed in macOS 15.6 beta (24G5054d).

I tested this and probing now seems to work, although I'm still encountering issues at the mount stage when Disk Arb tries to automount. Based on my observations in Console it seems like it can't find the module, although mount(8) still works. I replied to the feedback with additional details

I looked over the data you sent and, yes, I think that's what's going on. If you look closely at the log data, fskit_agent is finding 4 modules (which tracks with what DiskArb logs and is why probe is working) but fskitd is only finding 2 modules (which leads to the failure you're seeing). As far as I can tell, both processes are actually running the same code, so I think the difference is actually come from what's been registered with LaunchServices, not FSKit itself.

Note that you referenced another bug about FSClient.installedExtensions only returning the system's extensions. FSClient.installedExtensions is not applying an artificial filter, it is returning what fskitd "sees", which explains why your extensions are missing.

Ruling out something easy/obvious, is your extension running out Xcode's directory or some other user owned hieararchy? What happens if you move the app/extension into /Applications and run it from there?

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

I looked over the data you sent and, yes, I think that's what's going on

Correcting myself, I think the issue might actually be on your side. After digging through the log more, it looks like mounting had actually started and what actually failed was this:

 2025-06-26 03:05:07.387063-0700 796 ProbingBugSampleExtension: (FSKit) [com.apple.FSKit:default] -[FSModuleVolume(Project) getMaxFileSizeInBits]: Volume does not implement both maxFileSizeInBits and maxFileSize, while one of them must be implemented.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Regarding the mount issue:

Thanks for pointing me to that error. Testing this myself, it seems like that error is only appearing in Console when I use mount(8) (and oddly enough, the mount succeeds despite the error). When the disk just appears and DA tries to automount I don't see that error, which makes me think this is not the (only) issue. (The reason it appeared in the logs was that I was trying out different things before reproducing - you'll see that the timestamp there is a few seconds before the time I gave, which is probably when I tried using mount(8) manually instead.)

In any case, when adding

var maximumFileSize: UInt64 = UInt64.max

to my sample FSVolume, the situation doesn't change and the automount still fails. It does silence the error when manually using mount(8), though.


Regarding the issue of fskitd not "seeing" the extensions:

Ruling out something easy/obvious, is your extension running out Xcode's directory or some other user owned hieararchy? What happens if you move the app/extension into /Applications and run it from there?

I've tested the following configurations, and the issue occurred in all cases:

  • Running from Xcode build directory, macOS 15.5 (24F74) (not VM)
  • Running from a user-owned directory on a non-boot volume (which is actually a VM shared folder), macOS 15.6 (24G5054d) (in a VM)
  • Moving the sample app from the VM shared folder to the /Applications directory (also removing it from original location), then opening it, then trying again
  • After moving the sample app, restarting the (VM) Mac, then trying again

Also don't think this matters, but in the VM test cases I used a notarized copy of the sample app archived from Xcode since I don't have the development provisioning profile setup for my VM's device identifier (and thus it was in release mode), and in the first case I simply used the debug build directly from Xcode.

I also posted this as a comment on FB17772372, but reposting this here:

As a workaround, it does seem that if I login to a GUI session as the root user (https://support.apple.com/en-us/102367) and enable my FSKit module as the root user in System Settings, then automount works (even from a regular user). Seems like some kind of “modules enabled as the logged in user” vs “modules enabled as the root user” discrepancy.


As an extra note that's somewhat unrelated, it's a little odd how that despite this now working, FSClient.installedExtensions still can't see the enabled module (FB18398975) even when enabled as the root user, despite fskitd now seemingly being able to see it during an automount operation. It still just shows the 2 system modules.

As a workaround, it does seem that if I log in to a GUI session as the root user (https://support.apple.com/en-us/102367) and enable my FSKit module as the root user in System Settings, then automount works (even from a regular user). Seems like some kind of “modules enabled as the logged in user” vs “modules enabled as the root user” discrepancy.

Yeah, there are a bunch of places in the system where logging in as root will change the "normal" system behavior. There's a common Unix pattern macOS inherits, which is that the root is both a:

  1. Standard user account (which is why you can log in).

  2. The user ID the system uses as the owner ID for "stuff" it doesn't want some other specific user to own.

Going back to here:

enable my FSKit module as the root user in System Settings, then automount works (even from a regular user).

What's going here is that there's a split between the "system's extensions" (which work) and the "user's extensions" (which should work, but don't). Strictly speaking, logging in as root shouldn't change that; however, parts of the system (particularly lower level components like this) don't really differentiate between "root's data" (meaning, the data the root user directly "owns") and "system data" (meaning, the system-wide session data). Enabling it as root the (basically) "tricked" the system into thinking your extension was part of the global session, even though it probably shouldn't have.

Theoretically, these issues could be considered a security bug; however, any security exploit that starts with "First, log in as root..."

...isn't really an exploit. Similarly, correcting these issues isn't really a priority since they're sometimes useful for testing/debugging, and running as the root user creates MUCH bigger problems than any single issue we might "fix".

__
Kevin Elliott
DTS Engineering, CoreOS/Hardware

How can I get the system to use my FSModule for probing?
 
 
Q