Overview
Filesystem Stack
Userland (syscalls 0x20–0x2E)
│
▼
fs/vfs — mount table, path dispatch
├── fs/fat12 — floppy FAT12 (read/write)
└── fs/iso9660 — CD-ROM ISO9660 (read-only)
│ │
fat12/block.rs iso9660/block.rs
(Floppy/ISA DMA) (Atapi/PIO)
│ │
BlockDevice trait (fs/block.rs)
Both filesystems implement the same BlockDevice trait. All higher-level logic (directory walks, FAT chains, ISO records) is built on top of that single abstraction.
BlockDevice Trait (fs/block.rs)
pub trait BlockDevice {
fn read_sector(&self, lba: u64, buffer: &mut [u8]);
fn write_sector(&self, lba: u64, buffer: &[u8; 512]);
}
One sector = 512 bytes for FAT12/floppy. ISO9660 uses 2048-byte logical blocks but maps them to the same interface internally. Implementors: Floppy, Atapi (write is a no-op), MemDisk.
VFS (fs/vfs/mod.rs)
The VFS is a simple mount table — it does not provide a unified file descriptor layer or inode abstraction. All it does is map path prefixes to filesystem types, and dispatch resolves which filesystem to use for a given absolute path.
Mount Table
pub static VFS: Mutex<VfsTable>
pub struct VfsTable {
mounts: [VfsMount; MAX_MOUNTS], // MAX_MOUNTS = 8
count: usize,
}
pub struct VfsMount {
path: [u8; 32],
path_len: usize,
fs_type: FsType,
}
FsType enum:
| Variant | Meaning |
|---|---|
None |
Empty / unused slot |
Root |
Root mountpoint (/) |
Fat12 |
FAT12 floppy at /mnt/fat |
Iso9660 |
ISO9660 CD-ROM at /mnt/iso |
Mounts at Boot
Set up by init::fs::vfs_init():
| Path | FsType | Condition |
|---|---|---|
/ |
Root |
Always |
/mnt/fat |
Fat12 |
Always |
/mnt/iso |
Iso9660 |
Only if Iso9660::probe() succeeds |
Path Resolution
VfsTable::resolve(path) returns (FsType, relative_sub_path) using longest-prefix matching:
- Iterate all mounts; check if
pathstarts with the mount path. - Require exact match or that the next character after the prefix is
/. - The mount with the longest matching prefix wins.
- Returns the sub-path after stripping the mount prefix (and a leading
/).
Example: path b"/mnt/fat/SUBDIR/FILE.TXT" → (Fat12, b"SUBDIR/FILE.TXT").
Helpers
| Function | Description |
|---|---|
try_fat12_absolute(path) |
Returns Some(rel) if path resolves under the Fat12 mount |
try_iso9660_absolute(path) |
Returns Some(rel) if path resolves under the Iso9660 mount |
mount(path, fs_type) |
Add a mount entry |
umount(path) |
Remove a mount entry by path |
These are the primary VFS entry points used by syscall handlers in abi/syscall.rs.
Syscall Dispatch Pattern
Every filesystem syscall uses the same two-step dispatch:
path → try_iso9660_absolute(path)
Some(rel) → Iso9660::probe()?.resolve(rel) [read-only]
None → vfs_resolve_fat12(path) → Filesystem::new(&floppy)
vfs_resolve_fat12(path) strips the /mnt/fat/ prefix if present, or falls back to the current working directory cluster from SYSTEM_CONFIG.
Working Directory
The current working directory is stored in SYSTEM_CONFIG (init/config.rs) as two fields:
| Field | Type | Description |
|---|---|---|
path |
[u8; 32] |
String representation (e.g. /mnt/fat/SUBDIR) |
path_cluster |
u16 |
FAT12 cluster for the directory (0 = root, 0 for ISO9660) |
Changed by syscall 0x2E (chdir), which validates that the target exists as a directory before updating.
MemDisk (fs/memdisk/block.rs)
MemDisk wraps a &'static mut [u8] as a BlockDevice. read_sector copies 512 bytes from the slice. write_sector is a no-op. Intended for in-memory disk images but not currently wired into any live code path.
Limits
| Resource | Value |
|---|---|
| Max VFS mounts | 8 |
| Max mount path length | 31 bytes |
| FAT12 sector size | 512 bytes |
| ISO9660 block size | 2048 bytes |
| Max directory entries returned (syscall 0x28) | 32 |
| Max directory entries returned (syscall 0x2D) | 64 |
| Max VFS mounts listed (syscall 0x2C) | 8 × 34-byte entries |