Network filesystems
Lazy remote trees, materialized local files, leases and cache policy.
A network backend implements Filesystem like any other, then adds NetworkFilesystem to
choose between lazy reads through read and reads of a file you have already downloaded.
The trait
NetworkFilesystem extends Filesystem and adds three defaulted methods:
Mount it with MountSession::mount_network or MountSession::mount_network_with_options.
mount takes a plain Filesystem, so a network backend passed to mount loses the open/close
hooks and every read goes through read.
network_configuration() is read once when the volume starts. Changing it later has no effect
until you remount.
Configuration
KernelCachePolicy has four values:
The default is conservative: metadata may be cached, but file bytes still come from your
backend. Use Read or ReadWrite only when you can invalidate correctly.
Remote or local
open_file returns one of two choices:
NetworkFile::Remote keeps the file lazy; reads call Filesystem::read. NetworkFile::Local
hands the adapter a fully materialized file, and reads of that file bypass your read method.
LocalFile::new(path, generation) creates a lease:
- The path must remain readable and unchanged until
close_filereturns. - The path must be valid UTF-8, because the macOS extension opens it through Foundation.
generationis a backend-defined value that changes when the local contents change. The adapter forwards it to the kernel so cached data can be invalidated. Bump it whenever you rewrite the file, including after re-downloading the same path.
The adapter never deletes or evicts a leased file. It only tells you when the kernel is done with it.
Where local files may live
On macOS the backend and the FSKit extension are different processes, and the extension is
sandboxed to the group.dev.specta.fsk App Group. A file in your process temporary directory
cannot be opened by the extension. The mount then falls back to remote reads, and older
extensions fail the open outright.
Use local_file_directory() and create a backend-specific subdirectory:
On macOS the function returns
~/Library/Group Containers/group.dev.specta.fsk/Caches. On other platforms it returns
std::env::temp_dir(). It creates the directory if needed, but the returned root is shared, so
the subdirectory is what keeps independent mounts from colliding.
Open and close
A lease starts in open_file and ends in close_file. Keep the cache entry alive between them:
open_file errors are returned to the kernel as positive errnos. On Linux, if a local open fails,
the adapter calls close_file with the failed lease before replying, so your bookkeeping stays
balanced.
On macOS a duplicate open of an already-open file shares the existing lease; the extra lease is
closed immediately. Do not assume one open_file call per close_file call in that case.
Eviction and activity
The crate reports releases; you decide the policy. A practical split:
close_filerecords last access and marks the entry idle.- A worker evicts entries by age or total size, skipping anything with an active lease.
- A later
open_filereturnsRemotefor a missing entry, which makes the next read download it again.
On Windows there are no local-file leases. ProjFS keeps hydrated content in its own
virtualization root and never evicts it automatically. The Windows MountSession adds two
methods for that cache:
MountSession::evict(path) is the same operation without a handle.
EvictionOutcome::Evicted means the cached copy was removed (or was already virtual);
Retained means the item has local changes, is a tombstone, is open, or is a non-empty
directory. Both outcomes are retryable, and both leave user data intact. Call eviction outside a
Filesystem callback, and do not hold a lock that callbacks need.
Platform differences
Writes
Return NetworkFile::Remote for a writable open unless you make the write durable yourself. A
local file that the kernel writes to is invisible to your backend, so the next reader sees local
edits that were never uploaded.
On macOS and Linux, writes routed through Remote reach Filesystem::write. On Windows they do
not reach the backend: ProjFS keeps them in its cache and reports them through raw notification
events if you asked for them. See Reading and writing.
The network example
Examples has a complete small backend that serves one remote file,
materializes a second into local_file_directory(), and logs close callbacks.
Next
- Platform setup for the App Group and FUSE prerequisites.
- Raw events for observing Windows notifications and local writes.
- Examples for a backend that materializes files end to end.
