-
-
Notifications
You must be signed in to change notification settings - Fork 5
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
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.
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.
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
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.
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/randomis a plain xorshift PRNG (fine for scripts, not for keys);/dev/urandomis the real thing — the samecsprng_bytes()(HMAC_DRBG-SHA256 seeded from RDSEED/RDRAND) that TLS and ephemeral key generation use, sincev6.4.341. See Cryptography-and-TLS.
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.
| 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) |
| 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).
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.
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.
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 |
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 |
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.
- Format-Reference - EXT2 and CPIO on-disk layout
-
Kernel-Data-Structures -
vfs_node_tfield by field - Drivers - the ATA driver EXT2 sits on
- Syscalls - the file I/O interface
- Userspace - the coreutils that use it
- The Second Extended File System - Dave Poirier
- cpio newc format - Linux kernel documentation
NyxOS v6.4.363 · GPL v2 · GitHub · uselessalter on Discord · nyxos@inbox.lv
NyxOS Wiki
Getting started
Kernel
Storage & network
Graphics & apps
Userspace
HOWTO
- HOWTO-Add-a-system-call
- HOWTO-Write-a-userspace-program
- HOWTO-Add-a-shell-command
- HOWTO-Add-a-GUI-application
Reference
- Syscall-Reference
- Command-Reference
- Hardware-Reference
- Format-Reference
- Kernel-Data-Structures
- Source-Tree-Reference
Project