Forest OS Kernel: Filesystems
Forest OS supports a wide range of filesystem types, from ubiquitous consumer formats to niche retro-computing and embedded systems. The filesystem layer is built on a VFS (Virtual File System) abstraction that decouples individual drivers from path resolution, mount management, and the syscall interface.
Supported Filesystems
| Filesystem | Source File | Read | Write | Status |
|---|---|---|---|---|
| FAT12/16/32 | fat.c |
Yes | Yes | Production |
| exFAT | exfat.c |
Yes | No | Read-only |
| ISO 9660 | iso9660.c |
Yes | No | Read-only |
| UDF | udf.c |
Yes | No | Read-only |
| JFFS2 | jffs2.c |
Yes | No | Read-only |
| YAFFS | yaffs.c |
Yes | No | Read-only |
| LEAN | lean.c |
Yes | No | Read-only |
| Amiga FFS | ffs_amiga.c |
Yes | No | Read-only |
| z/OS Datasets | zdsfs.c |
Yes | No | Read-only |
| initrd (ustar) | vfs.c / ustar.c |
Yes | No | Production |
| tmpfs | fs.c |
Yes | Yes | Production |
| procfs | procfs.c |
Yes | Partial | Partial |
| sysfs | sysfs.c |
Yes | Partial | Partial |
| devfs | devfs.c |
Yes | N/A | Production |
| ext2 | ext2.c |
Yes | No | Read-only |
| symlinks | symlink.c |
Yes | N/A | Production |
All drivers are gated by ENABLE_VFS=yes in fern/build/features/filesystems.mk. Individual drivers have independent toggles (ENABLE_FAT32, ENABLE_EXFAT, ENABLE_ISO9660, ENABLE_UDF, ENABLE_EXT2, etc.).
FAT12 / FAT16 / FAT32
Source: fern/src/fat.c (844 lines) and fern/src/include/fat.h
The FAT driver is the most mature filesystem in Forest OS, handling all three classic FAT variants through a single code path.
Boot Sector Parsing
The driver reads the BIOS Parameter Block (BPB) from sector 0 and determines FAT type by cluster count:
- < 4085 clusters → FAT12
- < 65525 clusters → FAT16
- ≥ 65525 clusters → FAT32
It validates the boot signature (0xAA55) and extracts bytes_per_sector, sectors_per_cluster, reserved_sectors, num_fats, root_entries, total_sectors, and sectors_per_fat.
Cluster Chain Traversal
fat_get_next_cluster() follows cluster chains via the standard linear offset calculation:
- FAT12:
cluster + (cluster / 2)bytes offset, 12-bit entries packed in pairs - FAT16:
cluster * 2bytes offset, 16-bit entries - FAT32:
cluster * 4bytes offset, 28-bit entries (upper 4 bits reserved)
End-of-chain markers: 0xFF8+ (FAT12), 0xFFF8+ (FAT16), 0x0FFFFFF8+ (FAT32).
Directory and Write Support
Short filenames use classic 8.3 encoding. LFN entries are defined in the header (fat_lfn_entry_t) but not yet resolved in lookup.
Full write support includes:
fat_alloc_cluster()— scans for free FAT entriesfat_free_cluster_chain()— zeroes cluster chainsfat_node_mkdir()— allocates cluster, writes.and..entriesfat_node_unlink()— marks directory entry deleted (0xE5) and frees clusters
VFS Integration
Each FAT node stores a fat_node_t in vfs_node_t->internal_data. The VFS operation table is set at fat.c:496-501:
node->read = fat_node_read;
node->open = fat_node_open;
node->close = fat_node_close;
node->readdir = fat_node_readdir;
node->finddir = fat_node_finddir;
node->unlink = fat_node_unlink;
node->mkdir = fat_node_mkdir;
Registration: fat_register() → vfs_register_filesystem().
exFAT
Source: fern/src/exfat.c (390 lines) and fern/src/include/exfat.h
Functional read-only driver. Validates the "EXFAT " identifier and parses the boot sector, including shift-based sector/cluster sizing (1 << shift), FAT offset/length, cluster heap offset, cluster count, and root_dir_cluster.
Directory traversal decodes entry sets (0x85 file, 0xC0 stream, 0xC1 name) over a FAT-like cluster walk, with contiguous (NoFatChain) fast-path and FAT-table chaining otherwise. UTF-16 names are compared with ASCII case-insensitive fold. Provides get_root (from root_dir_cluster), readdir, finddir, and file read via byte-granular cluster-chain reads. No write support. Probe confidence: 90.
exfat_register();
vfs_mount("hd0p1", "/mnt/exfat", "exfat",
dev_data, read_sector, NULL, total_sectors);
ISO 9660 (CD-ROM)
Source: fern/src/iso9660.c (438 lines) and fern/src/include/iso9660.h
Functional read-only driver. Validates the Primary Volume Descriptor (PVD) at sector 16 — type 1, identifier "CD001" — and mounts with the root extent (LBA/size), path-table LBA, and logical block size. The header fully defines iso_primary_vd_t, iso_dir_record_t, and iso_path_table_entry_t structures.
Implements get_root, readdir, finddir, and file read by walking directory records and following multi-extent files extent-by-extent. Names use plain ISO form with the ";1" version suffix stripped and case-insensitive compare; . (0x00) and .. (0x01) handled. Rock Ridge/Joliet extensions ignored. No write support. Probe confidence: 100.
mount -t iso9660 /dev/cd0 /cdrom
UDF (Universal Disk Format)
Source: fern/src/udf.c (861 lines) and fern/src/include/udf.h
Functional read-only driver. Locates the anchor Volume Descriptor Pointer at sectors 256 / 512 (with trailing-anchor fallback), then recognizes UDF via "NSR02" or "NSR03" descriptors at sector 16 and parses File Entry / Extended File Entry metadata, including extended-attribute and allocation-descriptor areas. Defines udf_tag_t, udf_descriptor_t, and udf_anchor_vdp_t structures.
Resolves files through ICB extents, supporting short, long, and extended allocation descriptors with run collection (including Allocation Extent Descriptor chaining) for file data extents. Provides get_root, readdir, finddir, and file read. Fixed 2048-byte blocks. No write support. Probe confidence: 85.
mount -t udf /dev/cd0 /dvd
JFFS2 (Journaling Flash File System v2)
Source: fern/src/jffs2.c (1195 lines) and fern/src/include/jffs2.h
Functional read-only driver for NOR flash. Scans linear flash for nodes keyed by magic 0x1985 or 0x2001, with header-CRC gating, name/data-CRC checks, and length sanity (64 KiB cap, erased 0xFFFFFFFF rejected). Uses page-level I/O over 256-byte pages (OOB/spare ignored) via a read_page callback rather than block-sector I/O.
Scan builds an in-RAM versioned dirent-to-inode map (tombstone-aware, latest version wins) and fragment list for file data from jffs2_inode_node_t and jffs2_dirent_node_t records, backing get_root, readdir, finddir, and file read. No decompression or write support. Probe confidence: 75.
mount -t jffs2 /dev/mtd0 /mnt/flash
YAFFS (Yet Another Flash File System)
Source: fern/src/yaffs.c (669 lines) and fern/src/include/yaffs.h
Functional read-only driver for NAND flash. Parses OOB tags (object ID, chunk ID, byte count) over 512-byte pages + 16-byte OOB via separate read_page and read_oob callbacks, with erased-page (0xFF) detection and header validation (type, name length, parent ID, file-size bounds). Supports file, directory, symlink, hardlink, and special object types.
Scan builds an in-RAM object index (validated headers plus chunk-ID-sequenced data pages) backing get_root, readdir, finddir, and chunk-chain file read. No write support. Probe confidence: 70.
mount -t yaffs /dev/mtd1 /mnt/nand
LEAN
Source: fern/src/lean.c (605 lines) and fern/src/include/lean.h
Functional read-only driver for this lightweight filesystem. Locates the superblock by scanning blocks 1–32 for magic 0x4E41454C, resolves block size from log_block_size (1 << n), and loads inodes by block number. The lean_inode_t supports up to 6 direct extents plus indirect-block chains, fork support, and full POSIX timestamps.
Direct-extent plus indirect traversal backs get_root, readdir, finddir, and file read. No write support. Probe confidence: 80.
mount -t lean /dev/sda1 /mnt/lean
Amiga FFS (Fast File System)
Source: fern/src/ffs_amiga.c (605 lines) and fern/src/include/ffs_amiga.h
Functional read-only driver for Amiga OS. Validates root block type 0x444F with Amiga-style checksum (16-bit ones-complement sum over 256 words folding to 0xFFFF). Defines ffs_root_block_t with hash table for directory entries and hash-based name lookup (ffs_hash_name()).
Traverses file-header and data-block chains for size and contents, backing get_root, readdir, finddir, and file read. Registered as ffs_amiga with affs alias. No write support. Probe confidence: 65.
mount -t ffs_amiga /dev/hd0 /amiga
z/OS Dataset Filesystem (ZDSFS)
Source: fern/src/zdsfs.c (768 lines) and fern/src/include/zdsfs.h
Functional read-only driver for mainframe DASD dataset structures using CCHHR (Cylinder/Head/Record) addressing. Scans the VTOC for DSCB1 records validating format == 0xF1. The dscb1_t structure mirrors IBM's DSCB format, with EBCDIC-037 to ASCII translation for dataset and member names.
Exposes a root → dataset → PDS-member hierarchy (cached dataset table) backing get_root, readdir, finddir, and member read. No write support. Probe confidence: 60.
mount -t zdsfs /dev/dasd0 /zds
ext2
Source: fern/src/ext2.c (552 lines) and fern/src/include/ext2.h
Functional read-only driver (covers ext3/4 compat, no journal). Validates superblock magic 0xEF53 at offset 1024, block size (1024 << log), inode size, and group counts, then resolves group-descriptor inode tables.
Traverses direct plus single/double indirect blocks (triple indirect recognised but unsupported) for file data, with directory-entry parsing backing get_root, readdir, finddir, and file read. Registered as ext2 with ext3/ext4 aliases (journal ignored, read-only). Gated by ENABLE_EXT2. No write or journal replay support. Probe confidence: 95.
mount -t ext2 /dev/hd0 /mnt
initrd (Initial RAM Disk)
Source: fern/src/vfs.c and fern/src/ustar.c (585 lines) plus fern/src/include/ustar.h
Forest OS's root filesystem at boot. A read-only ramdisk loaded from a multi-file ustar archive (512-byte blocks, "ustar" magic, prefix + name path building, octal size fields). Scans the archive into an in-RAM table of up to USTAR_MAX_FILES (128) entries backing get_root, readdir, finddir, and file read. Registered as ustar with initrd alias. No write support. Probe confidence: 100.
Features path normalization with ../. support, case-insensitive lookup, automatic PATH prefix resolution (bin/, usr/bin/, sbin/, usr/sbin/), and ELF extension auto-resolution.
mount -t ustar /dev/ram0 /initrd
tmpfs
Source: fern/src/fs.c (inline in VFS core)
In-memory filesystem with full read/write support. The only filesystem currently supporting hard links (vfs_link()). Gated by ENABLE_TMPFS. No persistence across reboots.
Virtual Filesystems
- procfs (
procfs.c) — Exposes kernel/process info. Integration withvfs_open()is commented out. - sysfs (
sysfs.c) — Device/driver information. Also commented out in VFS layer. - devfs (
devfs.c) — Device nodes at/dev/. Fully integrated;vfs_open("/dev/...")routes directly todevfs_open().
Filesystem Driver Registration
Forest OS uses two registration mechanisms:
VFS-Level Registration
Each driver calls vfs_register_filesystem() with a vfs_filesystem_t struct:
int fat_register(void) {
vfs_filesystem_t* vfs_fs = enhanced_heap_alloc(sizeof(vfs_filesystem_t), ...);
vfs_fs->name = "fat";
vfs_fs->probe = fat_probe;
vfs_fs->mount = fat_mount;
vfs_fs->umount = fat_umount;
vfs_fs->get_root = fat_get_root;
return vfs_register_filesystem(vfs_fs);
}
The function (vfs.c:830-838) prepends to a singly-linked list. Lookup by name via vfs_get_filesystem().
Architecture-Level Registration
arch/fs.c provides a second registry via arch_fs_register_type() for arch_fs_type_t structs. This handles block-device-based filesystems needing architecture-specific probing. arch_fs_try_mount() bridges the two registries.
Mount Flow
vfs_mount(device, mountpoint, fstype, ...)looks up the filesystem by name- Calls
fs->mount()to initialize the superblock - Calls
fs->get_root()for the root VFS node - Creates a
vfs_mount_tentry in the mount table
Driver Architecture
┌─────────────────────────────────────────────────┐
│ Syscall Layer │
│ vfs_open / vfs_read / vfs_write │
├─────────────────────────────────────────────────┤
│ VFS Core │
│ vfs.c │ Mount table │ Path resolution │
│ │ Deferred ops │ Symlink resolution │
├─────────┴───────────────┴────────────────────────┤
│ Filesystem Drivers │
│ fat.c │ exfat.c │ iso9660.c │ udf.c │ lean.c │
├─────────────────────────────────────────────────┤
│ Block Device Layer │
│ arch/fs.c │ AHCI │ virtio-blk │ ATA │
├─────────────────────────────────────────────────┤
│ Hardware │
│ PCI │ DTB │ MMIO │ Flash NAND/NOR │
└─────────────────────────────────────────────────┘
Callback Interface
Each filesystem driver provides:
| Callback | Purpose |
|---|---|
probe |
Detect filesystem, return confidence (0–100) |
mount |
Parse superblock, initialize state |
umount |
Free state, flush dirty data |
get_root |
Return VFS node for root directory |
VFS nodes add: read, write, open/close, readdir, finddir, unlink, mkdir.
I/O Abstraction
Block-based drivers use sector callbacks:
typedef uint32_t (*fat_read_sector_fn)(void* dev_data, uint64_t lba, uint8_t* buffer);
Flash drivers use page-level I/O:
typedef uint64_t (*jffs2_read_page_fn)(void* dev_data, uint64_t offset, uint8_t* buffer);
Deferred Operations
Forest OS supports deferred filesystem operations for open files. When a file is deleted while open, the VFS marks it VFS_DELETED and queues a VFS_DEFERRED_DELETE. Actual deletion occurs when the last handle closes (fat_node_close() checks open_count == 0 && flags & VFS_DELETED). The queue is processed by vfs_process_deferred_ops().
Performance Characteristics
| Filesystem | Sequential Read | Random Access | Write | Memory | Notes |
|---|---|---|---|---|---|
| FAT12/16/32 | Good | Poor | Moderate | Low | FAT table not cached; cluster chains traversed per-read |
| ISO 9660 / UDF | Excellent | Moderate | N/A | Very Low | Designed for sequential optical media |
| JFFS2 / YAFFS | Moderate | Moderate | Good (append) | Moderate | Optimized for flash endurance |
| tmpfs | Fast | Fast | Fast | Variable | In-memory only; no persistence |
| initrd | Fast | Fast | N/A | Fixed | In-memory after boot; read-only |
FAT random access is poor because each seek must re-traverse the cluster chain from the FAT. Flash filesystems trade raw throughput for wear leveling and power-fail safety.
Build Configuration
fern/build/features/filesystems.mk provides granular control:
ENABLE_VFS=yes # Master switch for entire FS layer
ENABLE_EXT2=yes # ext2/3/4 driver (read-only, ext2.c)
ENABLE_FAT32=yes # FAT12/16/32 driver
ENABLE_EXFAT=yes # exFAT driver
ENABLE_ISO9660=yes # ISO 9660 driver
ENABLE_UDF=yes # UDF driver
ENABLE_LEAN=yes # LEAN driver
ENABLE_YAFFS=yes # YAFFS driver
ENABLE_JFFS2=yes # JFFS2 driver
ENABLE_FFS_AMIGA=yes # Amiga FFS driver
ENABLE_ZDSFS=yes # z/OS dataset driver
ENABLE_TMPFS=yes # tmpfs (in VFS core)
ENABLE_PROCFS=yes # procfs
ENABLE_SYSFS=yes # sysfs
ENABLE_DEVFS=yes # devfs
ENABLE_RAMDISK=yes # ramdisk support
ENABLE_SYMLINKS=yes # symbolic link support
Setting ENABLE_VFS=no excludes all filesystem sources. Individual flags only matter when VFS is enabled.
Future Directions
- ext2 write support —
ext2.c(552 lines) implements read-only mount/probe (ENABLE_EXT2, confidence 95), direct + single/double-indirect reads, andread/readdir/finddir;ext2_node_write()is a stub returning 0. - Journal support — ext3/ext4 mount read-only with the journal ignored; no journal replay yet.
- FAT LFN support —
fat_lfn_entry_tis defined but not wired into directory lookup. - procfs/sysfs integration — Implementations exist but are commented out in VFS.