Skip to content

Filesystem

kazah-png edited this page Aug 25, 2026 · 10 revisions

Filesystem

A POSIX-like VFS over four backends: a ramdisk loaded from the initramfs, a read/write EXT2 disk mounted at /mnt, the generated /proc pseudo-filesystem, and /dev special files. All four are reached through the same open/read/write/getdents calls — there are no side-channel syscalls.

See also: Architecture, Drivers, Syscalls, Shell, GUI-Subsystem

VFS (vfs.c)

One unified namespace over a pool of 512 nodes (MAX_INODES, raised from 256 at v6.4.197), plus a free-list for the transient nodes that back mounted files. Paths resolve relative to the calling process's working directory, with . and .. normalised. The vfsstat command prints a census of the pool (used/free nodes), added to profile the node-pool exhaustion (#66) that the N-Language example suite uncovered as an OS stress test.

Function Purpose
vfs_open / read / write / close File I/O
vfs_pread / vfs_pwrite Offset-aware I/O — the per-fd offset tracker and mmap use these
vfs_readdir Directory listing across ramdisk, EXT2 and /proc
vfs_mkdir / vfs_unlink / vfs_rename Namespace mutation
vfs_chdir / vfs_getcwd Navigation; returns a full absolute path
vfs_isdir Directory test
vfs_symlink / vfs_readlink Symbolic links (ln -s / readlink) — the target is a path string in the node's data; resolution is depth-bounded (SYMLINK_MAX = 8 hops) against loops (v6.4.351)
vfs_chmod File permissions — mode bits, with read-only enforcement on writes (v6.4.352)
vfs_mount / vfs_find_mount The 16-entry mount table

Working directories are per-process. Each process carries its own cwd, inherited across fork and kept across execve. Each GUI terminal window tracks its own; the kernel shell keeps a single global one.

Concurrency

The node pool, the free-list and the mount table each sit behind a spinlock, and the EXT2 driver has one of its own. This is not theoretical: the EXT2 driver uses a single global scratch buffer for the duration of an operation, so two cores inside it at once would corrupt each other's block reads.

One subtlety if you work on this code: a VFS fd is a node pointer, so the pool must track who holds one — a node cannot be recycled while an fd still names it.

Note

rename atomicity (v5.9.109). vfs_rename wrote the new name into the node before attempting a cross-directory move, so when the destination directory was full (vfs_append_child hits MAX_CHILDREN = 128 and aborts) the source was left renamed but not moved — rename("/a/x","/b/y") turned /a/x into /a/y. A failed rename must be a no-op on the source; the name is now written only after the move commits. Same-directory renames and successful moves are unchanged.

Ramdisk

The default filesystem, built at boot from the initramfs:

/
├── bin/  etc/  mnt/  proc/  root/  tmp/  usr/  var/
├── dev/          null  zero  random  urandom
├── home/
│   └── <user>/   created at first login, seeded with starter content
└── *.elf         the userspace programs

Initramfs (initramfs.c)

A CPIO newc archive (magic 070701) embedded in the kernel as a C byte array in initramfs_data.h, generated by tools/mkinitramfs.py. At boot it is unpacked into the ramdisk in one pass. See Building for how to regenerate it.

/dev special files

Real VFS nodes carrying a dev_type field. vfs_pread/vfs_pwrite intercept on that field, so no new syscalls were needed and ordinary tools work unchanged — ls /dev, cat, and shell redirection all behave.

Node Read Write
/dev/null EOF (0 bytes) Discarded
/dev/zero Endless null bytes Discarded
/dev/random Pseudo-random bytes from a lazily-seeded xorshift64 PRNG Discarded
/dev/urandom Cryptographic bytes from the CSPRNG (csprng_bytes) since v6.4.341 — no longer a tick-seeded xorshift (#79) Discarded

/dev/random is a plain xorshift PRNG (fine for scripts, not for keys); /dev/urandom is the real thing — the same csprng_bytes() (HMAC_DRBG-SHA256 seeded from RDSEED/RDRAND) that TLS and ephemeral key generation use, since v6.4.341. See Cryptography-and-TLS.

/proc pseudo-filesystem

Generated nodes whose contents are synthesized on read by proc_generate() — nothing is stored. proc_sync() reconciles the per-pid directories with the live process table and is called from vfs_open and vfs_isdir, so cat /proc/<pid>/status always sees a current view.

System-wide

Path Contents
/proc/version Kernel banner
/proc/meminfo MemTotal / MemUsed / MemFree
/proc/uptime tick_count converted to seconds
/proc/cpuinfo CPU identification
/proc/mounts Mounted filesystems (v6.4.176)

Per-process

Path Contents
/proc/<pid>/status Name, Pid, PPid, State
/proc/<pid>/cmdline Full command line including argv
/proc/<pid>/maps Mapped memory regions with their address ranges

Process names are real, set by proc_set_comm() — a process shows as init, not elf.

Two ring-3 tools read nothing but these files: free (from /proc/meminfo) and pmap [pid] (from /proc/<pid>/maps).

EXT2 (ext2.c)

A read/write EXT2 driver over ATA PIO, auto-mounted at /mnt when a disk is present.

  • Superblock, block groups, inode tables, directory entries
  • Inode and block allocation, directory creation, file writes, unlink
  • Fd-based file I/O flushed to disk on close — persistent across reboots
  • Fd-based readdir, so the GUI file manager can browse and edit the disk
  • A write-through sector cache — it accelerates reads and cannot corrupt the disk, because every write still reaches the platter

The write path is verified against e2fsck, not against itself. That distinction matters: a driver that reads back what it wrote can be self-consistently wrong.

df reports usage of the mounted filesystem.

How a mount is reached

vfs_open detects a path under a mount point via vfs_find_mount and returns a transient mount-backed node preloaded from the filesystem. vfs_write/vfs_pwrite flush the whole node back; vfs_close frees it. Opening a directory probes readdir — the EXT2 driver returns −1 for non-directories — and loads every entry once, which vfs_readdir then serves by index. The node free-list exists precisely so these per-open nodes do not exhaust the pool.

Note

vfs_isdir on a mount (v6.0.7). vfs_isdir treated every mounted (EXT2) path as a directory, so the File Manager listed files as folders and showed them as 0 B. It now probes the real type, so files on the disk list correctly with their size.

Note

Large mount writes (fixed v6.4.10–v6.4.13). vfs_pwrite once reallocated the whole in-memory node and re-flushed the entire file on every 4 KB write(), so a growing kmalloc failed around 274 KB and large objects truncated — which is why the v6.4.9 self-compiled tcc.c object (~496 KB) could not persist. The write path was reworked so the full object now reaches the disk, and the in-OS EXT2 reader gained sparse-file (hole) support (v6.4.13) so a program persisted on /mnt runs across reboots. See Toolchain.

Note

VFS path resolution (fixed v6.4.85). A path with a missing intermediate directory used to silently operate on an earlier parent; it now fails, as it should.

Note

Fd handles are pool indices (v6.4.91). A VFS fd is now a small pool index, not a truncated 64-bit node pointer — closing a class of handle-aliasing bug. (This is the userspace-facing fd; the kernel still tracks node ownership internally.)

Note

File-name validation (v6.4.148). touch and mkdir now reject a name that is not valid UTF-8 or that contains control characters, rather than writing it to the directory.

Persistent home directory

From v6.0.6 the logged-in user's home lives on the EXT2 disk at /mnt/home/<user>, not on the volatile ramdisk — so anything created there survives a reboot. Login creates it on first sign-in (kernel/auth/login.c); when no disk is attached, the home stays on the ramdisk as before.

Release Change
v6.0.6 Home moves to /mnt/home/<user> on the persistent disk
v6.0.8 The Terminal opens in, and can navigate, the persistent home
v6.0.9 Relative file ops from a mount-backed cwd hit the mounted FS — touch/mkdir/rm/mv/cp persist
v6.0.10 The Text Editor's Save works, and a new note defaults into the persistent home

What lives on the disk

The EXT2 image (ext2-test.img under QEMU) holds:

Path (as seen from NyxOS) Purpose
/etc/passwd User accounts — see Security
/mnt/home/<user> The logged-in user's persistent home (v6.0.6)
/mnt/doom1.wad The DOOM shareware WAD
anything you write under /mnt Persists across reboots

Tools

Kernel shell: ls cd pwd cat touch mkdir rm cp mv write head tail grep sort wc find tree diff df mount open

Ring 3: ls cat touch mkdir rm cp mv head tail grep sort find wc stat less more edit

See Shell for the full reference.

See also

External resources

Clone this wiki locally