Filesystem: new vs extend
Today: thin VFS with two backends and a prefix mount table. In-RAM memfs (A6 / ADR-027) and FAT16 on virtio-blk (A7 / ADR-028). Same open / read / write / close. Path prefixes route to a backend (ADR-058): /mem (+ A6 probe names) → memfs; / → FAT16 (/probe, plus freestanding samples /hello / /fsdemo / /fatdemo / /yldemo). FAT16 write is a depth mile (ADR-050). Multi-cluster grow is a depth mile (ADR-064). FAT16 root listing is a depth mile (ADR-056). FAT16 root delete is a depth mile (ADR-057). Not POSIX open / mount / getdents / unlink, not Linux VFS, not FAT32, not xv6. Track B B5 compares the concepts: ADR-034. Not claiming a Linux filesystem.
Do not say “supports FAT” as a product. Say the guest read a known FAT16 file when the ledger has fat: ok, wrote FAT16 bytes when it has fat: write / fat: create, grew a file across a cluster boundary when it has fat: grow, listed root entries when it has fat: readdir / fat: entries, deleted a root file when it has fat: delete, created a root directory when it has fat: mkdir, created a one-level nested directory when it has fat: nested, and removed an empty directory when it has fat: rmdir. Hub: honesty ledger, What can run today. Extra stance: filesystem.md.
flowchart TD CALL["EL0 / EL1 callers"] --> VFS["Thin VFS<br/>open · read · write · close · readdir · unlink"] VFS --> MT["Prefix mount table<br/>ADR-058"] MT -->|"/mem · A6 names"| MEM["memfs<br/>in-RAM buffers"] MT -->|"/"| FAT["FAT16<br/>read · write · root readdir · unlink"] FAT --> BLK["virtio-blk<br/>host fat16.img"]
Same thin API, two backends, prefix mounts. Not POSIX open / mount / getdents / unlink. Do not say “supports FAT” as a product.
New vs extend
Prefer implementing a known small filesystem behind this thin VFS over inventing a novel on-disk format.
| Fit | What | Why |
|---|---|---|
| Landed | Ramdisk / memfs | Named heap buffers. Create / lookup / read / write without DMA. |
| Landed | virtio-blk + FAT16 read | Host-visible raw image (scripts/mkfat16.py). xv6-like was rejected so the host can inspect the volume. |
| Landed | FAT16 write | Guest vfs::write on an open FAT handle + backend create of a small file (ADR-050). |
| Landed | FAT16 multi-cluster grow | Guest write allocates a FAT chain and crosses a cluster boundary (ADR-064). |
| Landed | FAT16 readdir | Guest vfs::readdir lists FAT16 root paths (ADR-056). Not POSIX getdents. |
| Landed | FAT16 delete | Guest vfs::unlink deletes a FAT16 root file (ADR-057). Not POSIX unlink. |
| Landed | FAT16 mkdir | Guest vfs::mkdir creates a FAT16 root directory with . / .. (ADR-073). Not POSIX mkdir. |
| Landed | EL0 fs_mkdir | SVC 27 + freestanding /mkdemo (ADR-075). Exists/BadPath fail-closed. Root create from EL0. |
| Landed | FAT16 nested mkdir + empty rmdir | One-level /parent/child + empty vfs::rmdir (ADR-077). Not POSIX. Not arbitrary depth. |
| Landed | ctos-specific virtual mounts | Prefix mounts behind the thin VFS (ADR-058). Not a new magic format. Not mount(2). Tree mounts (extra /) stay later. |
Avoid early
Do not start with ext4, btrfs, ZFS, or NTFS. Those are large, journaled or feature-heavy, and hide the block mile. They are not a first cut.
Host QEMU -drive without guest virtio + a VFS read is not a filesystem.
Roadmap order
One milestone → one branch → one PR.
flowchart LR V["1. VFS ADR<br/>ADR-027"] --> M["2. memfs<br/>A6"] M --> B["3. virtio-blk<br/>A7"] B --> F["4. FAT16 read<br/>A7"] F --> H["5. host image + guest read<br/>A7/A9"] H --> W["6. FAT16 write<br/>ADR-050"] W --> P["7. FAT vs memfs write CNTPCT<br/>ADR-051"] P --> PR["7b. FAT vs memfs read CNTPCT<br/>ADR-065"] PR --> R["8. FAT16 readdir<br/>ADR-056"] R --> D["9. FAT16 delete<br/>ADR-057"] D --> V2["10. Prefix mounts<br/>ADR-058"]
A7 is virtio-blk + FAT16 read. ADR-050 is FAT16 write depth. ADR-051 / ADR-065 are same-boot measurement pairs (not benches). ADR-056 is FAT16 root listing. ADR-057 is FAT16 root delete. ADR-058 is prefix mounts. A8 is documented recipes. A9 / ADR-059 / ADR-061 / ADR-062 use the same volume for /hello / /fsdemo / /fatdemo / /yldemo. Do not say “supports FAT” as a product.
- VFS ADR — thin interface (create / open / read / write / close of a path).
- memfs — in-RAM named buffers; serial
fs: ok. - virtio-blk — virtqueues + sector I/O on QEMU
virt. Serialblk: ok. - On-disk FS — FAT16 (ADR-028). Serial
fat: ok. - Host-checkable image —
scripts/mkfat16.pywritestarget/fat16.img; smoke attaches-drive if=none,file=…,id=hd0 -device virtio-blk-device,drive=hd0. Guestvfs::open("/probe")must readfat-hi. A9 adds--appso/hellois the published app ELF; ADR-059/061/062 add--app2/--app3/--app4for/fsdemo//fatdemo//yldemo. - FAT16 write — guest
vfs::writeon an open FAT handle + backend create of/fwr(ADR-050). Serialfat: write/fat: create; host--check-write. - FAT vs memfs write CNTPCT — same-boot pair
perf: fat-write/perf: memfs-write/perf: fs-write-delta(ADR-051). QEMU TCG lab — not a percent or latency SLA. 7b. FAT vs memfs read CNTPCT — same-boot pairperf: fat-read/perf: memfs-read/perf: fs-read-delta(ADR-065). QEMU TCG lab — not a percent or latency SLA. - FAT16 readdir — guest
vfs::readdirlists root thin-VFS paths (ADR-056). Serialfat: readdir/fat: entries. Not POSIXgetdents. - FAT16 delete — guest
vfs::unlinkdeletes a root file (ADR-057). Serialfat: delete. Not POSIXunlink. - Prefix mounts — mount table routes path prefixes to memfs or FAT16 (ADR-058). Serial
vfs: mount/vfs: mounts. Not POSIXmount(2).
Honesty
| Claim | Probe | Status |
|---|---|---|
| Thin VFS + memfs create/write/read/close | Serial fs: ok + #[test_case] | Verified (A6; honesty ledger) |
| virtio-blk sector R/W | Serial blk: ok + #[test_case] | Verified on this tip when the ledger has the probe |
FAT16 /probe via the same open | Serial fat: ok + #[test_case] | Verified on this tip when the ledger has the probe |
| FAT16 write + small create | Serial fat: write / fat: rewrite / fat: create + host --check-write | Verified on this tip when the ledger has the probe (ADR-050) |
| FAT vs memfs write CNTPCT pair | Serial perf: fat-write / perf: memfs-write / perf: fs-write-delta | Verified on this tip when the ledger has the probe (ADR-051). Not a bench. |
| FAT vs memfs read CNTPCT pair | Serial perf: fat-read / perf: memfs-read / perf: fs-read-delta | Verified on this tip when the ledger has the probe (ADR-065). Not a bench. |
| FAT16 root readdir | Serial fat: readdir / fat: entries + #[test_case] | Verified on this tip when the ledger has the probe (ADR-056). Not POSIX. |
| FAT16 root delete | Serial fat: delete + #[test_case] | Verified on this tip when the ledger has the probe (ADR-057). Not POSIX. |
| FAT16 root mkdir | Serial fat: mkdir + #[test_case] | Verified on this tip when the ledger has the probe (ADR-073). Not POSIX. |
EL0 fs_mkdir sample | Serial libctos: mkdir-ok / mkdemo: ok | Verified on this tip when the ledger has the probe (ADR-075). Not POSIX. |
| FAT16 nested mkdir + empty rmdir | Serial fat: nested / fat: rmdir + #[test_case] | Verified on this tip when the ledger has the probe (ADR-077). Not POSIX. One nest level only. |
| Thin VFS prefix mounts | Serial vfs: mount / vfs: mounts + #[test_case] | Verified on this tip when the ledger has the probe (ADR-058). Not mount(2). |
FAT16 /hello app slot (A9) | Serial slot: ok + #[test_case] | First cut when the ledger has the probe |
Do not claim compatibility with anyone’s existing disk.