The VFS routes operations through each mount’s delegate. The live system uses a
ramdisk; FAT volumes appear under /mount/fat<ID>, and an installed system uses
its boot volume as /.
FAT volume control
libstorage requests mount, flush and unmount through the storage manager.
Its administrative API separates partition-table changes from filesystem
formatting. FAT32 formatting initializes an unmounted volume while preserving
its partition table and neighboring volumes. Unmount rejects open files, active
transactions and the boot root.
See Installation for the disk layout, copy/verification sequence and the handoff from Limine bootstrap files to the FAT root filesystem.
Task delegates
Driver threads serve filesystems through ghost/filesystem/delegate.h and the
functions in ghost/filesystem.h:
-
g_fs_delegate_registermounts/mount/NAMEand returns the mount ID and a sharedg_fs_delegate_transaction_storagecontrol page. -
g_fs_delegate_node_createstages child nodes during lookup, creation or directory refresh. Successful completion publishes them; failure discards them. -
g_fs_delegate_transaction_completeacknowledges a request. -
g_fs_delegate_unregisterdetaches an owned mount between transactions.
Physical node IDs are opaque 64-bit values scoped to the delegate. An existing
name retains its type and ID; replacing a deleted file requires a new ID.
Lookup returns the child’s virtual ID in data.lookup.result_node_id.
Requests and shared memory
One transaction is active per control page. The serving thread receives
g_fs_delegate_request messages and must check G_FS_DELEGATE_MESSAGE_SENDER
and the storage version, size, mount, operation and 64-bit transaction ID against
the shared header. Filesystem transaction IDs are separate from IPC IDs.
Read/write data uses a separate 4 KiB mapped_buffer. Transfers can be short;
clients must loop as needed. The kernel owns both shared pages, and g_unmap
cannot release them. They are not DMA buffers.
The driver writes result fields before completing the transaction. The
completion syscall’s status is separate from the operation’s status.
SUCCESSFUL or STALE_TRANSACTION acknowledges that the driver has stopped
using the mapping; other completion errors leave it outstanding.
Cancellation and lifetime
Requests have a 15-second deadline, including queue time. Timeout or caller
termination fails the operation and isolates the caller’s buffer. Shared pages
remain reserved until the serving thread acknowledges cancellation or exits.
Unregister returns BUSY while a transaction remains unacknowledged.
Serving-task exit detaches the mount and wakes clients. Old descriptors retain the retired backend identity and cannot follow a replacement mount. The serving thread must not access its own mount while handling a request.
Retired VFS nodes remain tombstones until reboot. Limits are 64 active mounts, 1,024 registrations per boot and 16,384 staged or retained delegate nodes.
Mutations
Protocol version 2 supports CREATE, RESIZE, REMOVE and FLUSH with
g_fs_mutation_status results. Applications use g_open_f with CREATE,
g_create_directory, g_unlink, g_unlink_directory, g_truncate and g_sync.
CREATE|EXCLUSIVE rejects an existing name; writes and truncation require a
writable descriptor. Append serializes EOF selection with writes/truncation on
the same node. Extending past EOF zero-fills the gap.
FAT file data can remain in the disk cache after a successful write. Use fsync()
or g_sync for durability; volume flush and unmount also flush pending writes.
Cancellation does not undo writes already accepted by a backend. Drivers must
not retry uncertain mutations. A cancelled successful OPEN releases its private
open reference after STALE_TRANSACTION; completed mutations retain their effects.