Userspace file systems on Mac

I recently had the need for sshfs on Mac, so I did the usual:

$ brew install sshfs

To my surprise it was no longer available:

$ brew install sshfs
sshfs: Linux is required for this software.
libfuse: Linux is required for this software.
Error: sshfs: Unsatisfied requirements failed this build.

The reason for its disappearance became obvious when I started digging:

Learning that, I put on my spelunking gear and went headfirst down the rabbit hole looking for a solution.

Userspace file systems without kernel extensions

The current version of MacOS (Sonoma) does contain a remote file system API, known as file providers, but this is more designed for cloud storage synchronization (such as DropBox and iCloud) than to be a Virtual file system (VFS). While it can enumerate a directory structure without downloading everything, it’s quite limited as a VFS, and does not support things such as partially reading a file (head, tail, streaming) or loading/creating directory structures on demand.

All hope isn’t lost though, there are ways to do it. One way, a less than ideal solution, is to piggy back off of existing file systems. Just by tab completing on mount_ we’re given a few ideas:

$ mount_
mount_9p        mount_apfs      mount_devfs     mount_ftp       mount_msdos     mount_tmpfs
mount_webdav    mount_acfs      mount_cd9660    mount_exfat     mount_hfs       mount_nfs
mount_udf       mount_afp       mount_cddafs    mount_fdesc     mount_lifs      mount_smbfs
mount_virtiofs

Out of these there are a few extra interesting ones: 9p, virtiofs, ftp, smbfs, nfs, and webdav. What these have in common is that they are file system abstractions used to expose file systems over a network, or to share a file system with a virtual machine. So by creating a bridge from, say sftp, to one of the aforementioned mountable file system abstractions we can implement a file system without touching kernel extensions.

NFS + SSH

While digging I came across an interesting project known as nfsserve. It, as its name implies, implements a NFS server, but instead of directly mounting a network file system it exposes a file system trait which you implement as you see fit.

Using an example from the above project, and combining it with a sftp implementation in Rust, I was able to create an experimental, but working sshfs implementation over the course of a weekend. And since I live in 🌨️ Sunny Bergen, Norway, I found some time to improve its performance:

Rain rain rain

Currently it’s a read-only file system, and should be considered experimental. Adding write support shouldn’t be too difficult, I might do so if I find the time. As it’s built on the NFSv3 protocol features such as file locking are not supported. Credit really belongs to both nfsserve and russh though, with them a Rust novice like myself was able to quickly put together something that actually works:

There are obviously downsides to implementing file systems like this. There are feature limitations, it adds overhead, and in the case of file systems such as NFS or FTP it also exposes the remote file system via a port to other users on the same machine (it’s bound to 127.0.0.1).

fuse-t

It’s worth mentioning that there does exist a a proprietary, closed-source, userspace file system implementation for MacOS, known as fuse-t. I haven’t tried it myself, but I believe it employs the same trick as nfsserve, but uses nfsv4/samba and provides a proper fuse implementation, allowing existing fuse based file systems to work on MacOS.

FSKit - Coming soon!

Before investing a lot of effort in implementing something similar to the above, it might be worth waiting a bit. The next version of MacOS, Sequoia, will include a new API known as FSKit, which looks like it will offer a proper VFS API. Currently, the FSKit API is sadly nearly undocumented, but I did find two examples, Apples’s msdosfs and a much simpler unofficial FSKit sample.

Code