Let’s start by plugging in a USB stick (using the FAT, ExFAT or NTFS file systems) on macOS Ventura… and see what happens. The NTFS kext isn’t even present anymore!
It turns out:
% mount | grep disk4
fat://disk4s1/NO NAME on /Volumes/NO NAME (lifs, local, nodev, nosuid, noowners, noatime)
macOS Ventura does use user-mode file systems for external disk drives.
The file system extensions
/System/Library/Filesystems/msdos.fs/Contents/Resources/livefiles_msdos.dylib
/System/Library/Filesystems/exfat.fs/Contents/Resources/livefiles_exfat.dylib
/System/Library/Filesystems/ntfs.fs/Contents/Resources/livefiles_ntfs.dylib
We see that the livefiles extensions are shipping today for 3 file systems: FAT, ExFAT and NTFS.
Those are loaded by the UVFSService process, which is itself loaded by userfsd.
The bad news, the set of entitlements used by UVFSService:
entitlements = {
"com.apple.private.LiveFS.connection" = true;
"com.apple.security.iokit-user-client-class" = "AppleLIFSUserClient";
"com.apple.private.allow-external-storage" = true;
};
This means that we cannot load our own LiveFS extension modules within a stock operating system configuration.
What does that interface between the LiveFS provider and UVFSService look like anyway?
https://github.com/apple-oss-distributions/msdosfs/blob/main/LiveFilesTester/UserVFS.h gives the answer. It’s a fairly conventional interface that matches the semantics required quite well.
What if we try to mount directly?
% sudo diskutil unmount "/Volumes/NO NAME"
Volume NO NAME on disk4s1 unmounted
% sudo mount -o local,nodev,nosuid,noowners,noatime -t lifs "fat://disk4s1/NO NAME" ~/a
mount error:: Input/output error
mount: /Users/sunrise/a failed with 71
% sudo dmesg | grep lifs
arm64e_plugin_host: running binary "sudo" in keys-off mode due to entitlement: com.apple.private.security.clear-library-validation
lifs_mount:uid:501:gid:20
lifs_req_callback_thread: thread <ptr> starting for mount <ptr>
lifs_io_strategy_thread: thread <ptr> starting for mount <ptr>
Invalid lifs host port or port dying
Got error during mount request: 5
lifs_io_strategy_thread: thread <ptr> exiting for mount <ptr>
lifs_req_callback_thread: thread <ptr> exiting for mount <ptr>
Ok, it wants the lifs host port to be set properly before calling mount
. What if we try to mount the disk using the regular Unix mount
command sequence?
% sudo mount -t msdos /dev/disk4s1 ~/a
Executing: /usr/bin/kmutil load -p /System/Library/Extensions/msdosfs.kext
% mount | grep disk4
/dev/disk4s1 on /Users/sunrise/a (msdos, local, noowners)
Ok, in that case it doesn’t use UserFS, but the kext instead. Now what if we use diskutil mount
?
% sudo umount ~/a
% sudo diskutil mount /dev/disk4s1
Volume NO NAME on /dev/disk4s1 mounted
% mount | grep disk4
fat://disk4s1/NO NAME on /Volumes/NO NAME (lifs, local, nodev, nosuid, noowners, noatime)
OK, that makes sense.
More questions…
I didn’t track down yet how to mount manually for this scenario. How should that be done?
And, I really want this to be extensible to FUSE too. It turns out that there’s a sign of that at /System/Library/PrivateFrameworks/UVFSXPCService.framework/XPCServices/UVFSService.xpc/Contents/Resources/knownPlugins.internal.plist
which does have a fuse
entry. I wonder when that will happen to the public?
This makes sense! Been trying to figure why mount_ntfs is not working after installing ntfs-3g