{"id":450,"date":"2022-06-09T09:09:23","date_gmt":"2022-06-09T07:09:23","guid":{"rendered":"https:\/\/threedots.ovh\/blog\/?p=450"},"modified":"2024-06-06T22:38:22","modified_gmt":"2024-06-06T20:38:22","slug":"quick-look-at-user-mode-file-systems-on-macos-ventura","status":"publish","type":"post","link":"https:\/\/threedots.ovh\/blog\/2022\/06\/quick-look-at-user-mode-file-systems-on-macos-ventura\/","title":{"rendered":"Quick look at user-mode file systems on macOS Ventura"},"content":{"rendered":"\n<p>Let&#8217;s start by plugging in a USB stick (using the FAT, ExFAT or NTFS file systems) on macOS Ventura&#8230; and see what happens. The NTFS kext isn&#8217;t even present anymore!<\/p>\n\n\n\n<p>It turns out:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>% mount | grep disk4\nfat:\/\/disk4s1\/NO NAME on \/Volumes\/NO NAME (lifs, local, nodev, nosuid, noowners, noatime)<\/code><\/pre>\n\n\n\n<p>macOS Ventura does use user-mode file systems for external disk drives.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The file system extensions<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>\/System\/Library\/Filesystems\/msdos.fs\/Contents\/Resources\/livefiles_msdos.dylib\n\/System\/Library\/Filesystems\/exfat.fs\/Contents\/Resources\/livefiles_exfat.dylib\n\/System\/Library\/Filesystems\/ntfs.fs\/Contents\/Resources\/livefiles_ntfs.dylib<\/code><\/pre>\n\n\n\n<p>We see that the livefiles extensions are shipping today for 3 file systems: FAT, ExFAT and NTFS.<\/p>\n\n\n\n<p>Those are loaded by the UVFSService process, which is itself loaded by userfsd.<\/p>\n\n\n\n<p>The bad news, the set of entitlements used by UVFSService:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>entitlements = {\n\t\"com.apple.private.LiveFS.connection\" = true;\n\t\"com.apple.security.iokit-user-client-class\" = \"AppleLIFSUserClient\";\n\t\"com.apple.private.allow-external-storage\" = true;\n};<\/code><\/pre>\n\n\n\n<p>This means that we cannot load our own LiveFS extension modules within a stock operating system configuration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What does that interface between the LiveFS provider and UVFSService look like anyway?<\/h2>\n\n\n\n<p><a href=\"https:\/\/github.com\/apple-oss-distributions\/msdosfs\/blob\/rel\/msdosfs-466\/LiveFilesTester\/UserVFS.h\">https:\/\/github.com\/apple-oss-distributions\/msdosfs\/blob\/rel\/msdosfs-466\/LiveFilesTester\/UserVFS.h<\/a> gives the answer. It&#8217;s a fairly conventional interface that matches the semantics required quite well.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What if we try to mount directly?<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>% sudo diskutil unmount \"\/Volumes\/NO NAME\" \nVolume NO NAME on disk4s1 unmounted\n% sudo mount -o local,nodev,nosuid,noowners,noatime -t lifs \"fat:\/\/disk4s1\/NO NAME\" ~\/a   \nmount error:: Input\/output error\nmount: \/Users\/sunrise\/a failed with 71\n% sudo dmesg | grep lifs\narm64e_plugin_host: running binary \"sudo\" in keys-off mode due to entitlement: com.apple.private.security.clear-library-validation\nlifs_mount:uid:501:gid:20\nlifs_req_callback_thread: thread &lt;ptr&gt; starting for mount &lt;ptr&gt;\nlifs_io_strategy_thread: thread &lt;ptr&gt; starting for mount &lt;ptr&gt;\nInvalid lifs host port or port dying\nGot error during mount request: 5\nlifs_io_strategy_thread: thread &lt;ptr&gt; exiting for mount &lt;ptr&gt;\nlifs_req_callback_thread: thread &lt;ptr&gt; exiting for mount &lt;ptr&gt;<\/code><\/pre>\n\n\n\n<p>Ok, it wants the lifs <em>host port<\/em> to be set properly <em>before <\/em>calling <code>mount<\/code>. What if we try to mount the disk using the regular Unix <code>mount<\/code> command sequence?<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>% sudo mount -t msdos \/dev\/disk4s1 ~\/a\nExecuting: \/usr\/bin\/kmutil load -p \/System\/Library\/Extensions\/msdosfs.kext\n% mount | grep disk4\n\/dev\/disk4s1 on \/Users\/sunrise\/a (msdos, local, noowners)<\/code><\/pre>\n\n\n\n<p>Ok, in that case it doesn&#8217;t use UserFS, but the kext instead. Now what if we use <code>diskutil mount<\/code>?<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>% sudo umount ~\/a\n% sudo diskutil mount \/dev\/disk4s1\nVolume NO NAME on \/dev\/disk4s1 mounted\n% mount | grep disk4       \nfat:\/\/disk4s1\/NO NAME on \/Volumes\/NO NAME (lifs, local, nodev, nosuid, noowners, noatime)<\/code><\/pre>\n\n\n\n<p>OK, that makes sense. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">More questions&#8230;<\/h2>\n\n\n\n<p>I didn&#8217;t track down yet how to mount manually for this scenario. How should that be done?<\/p>\n\n\n\n<p>And, I really want this to be extensible to FUSE too. It turns out that there&#8217;s a sign of that at <code>\/System\/Library\/PrivateFrameworks\/UVFSXPCService.framework\/XPCServices\/UVFSService.xpc\/Contents\/Resources\/knownPlugins.internal.plist<\/code> which <em>does<\/em> have a <code>fuse<\/code> entry. I wonder when that will happen to the public?<\/p>\n\n\n\n<p><em>Edit on June 6th, 2024: Update URL to the Apple repository. The lifs header wasn&#8217;t intended to be public and is removed from later source code releases. <\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Let&#8217;s start by plugging in a USB stick (using the FAT, ExFAT or NTFS file systems) on macOS Ventura&#8230; and see what happens. The NTFS kext isn&#8217;t even present anymore! It turns out: macOS Ventura does use user-mode file systems for external disk drives. The file system extensions We see that the livefiles extensions are&hellip;&nbsp;<a href=\"https:\/\/threedots.ovh\/blog\/2022\/06\/quick-look-at-user-mode-file-systems-on-macos-ventura\/\" rel=\"bookmark\">Read More &raquo;<span class=\"screen-reader-text\">Quick look at user-mode file systems on macOS Ventura<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"","neve_meta_content_width":0,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-450","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/posts\/450","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/comments?post=450"}],"version-history":[{"count":2,"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/posts\/450\/revisions"}],"predecessor-version":[{"id":629,"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/posts\/450\/revisions\/629"}],"wp:attachment":[{"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/media?parent=450"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/categories?post=450"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/threedots.ovh\/blog\/wp-json\/wp\/v2\/tags?post=450"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}