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/rel/msdosfs-466/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?
Edit on June 6th, 2024: Update URL to the Apple repository. The lifs header wasn’t intended to be public and is removed from later source code releases.
This makes sense! Been trying to figure why mount_ntfs is not working after installing ntfs-3g