Phase 28
Ext2 Filesystem
Replace FAT32 as the primary persistent filesystem with ext2, which natively stores Unix ownership (uid/gid), permission modes, and timestamps in every inode. After this phase, file permissions set via `chmod`/`chown` survive reboots and the FAT32 permissions index file workaround from Phase 27 is no longer needed.
Learning goal
Understand on-disk filesystem layout: superblock, block group descriptors, inode tables, block/inode bitmaps.
Category
infrastructure
Components touched
Builds on
Unlocked by this phase
Why this phase matters
Replace FAT32 as the primary persistent filesystem with ext2, which natively stores Unix ownership (uid/gid), permission modes, and timestamps in every inode. After this phase, file permissions set via chmod/chown survive reboots and the FAT32 permissions index file workaround from Phase 27 is no longer needed.
How it works in m3OS
Phase 28 replaces the FAT32 permissions overlay with a native ext2 filesystem implementation. ext2 stores Unix ownership (uid/gid), permission modes, and timestamps directly in inodes, eliminating the .m3os_permissions index file workaround from Phase 27.
The implementation covers ext2 revision 0 with 4K blocks, direct + single-indirect + double-indirect block pointers, linked-list directory entries, and block/inode bitmap management. The ext2 partition is mounted at / and serves as the primary persistent filesystem.
How it builds on earlier phases
This phase builds on User Accounts and turns that foundation into Ext2 Filesystem.
Implementation outline
- Add ext2 on-disk structure definitions to
kernel-corewith unit tests. - Implement superblock and block group descriptor parsing.
- Implement inode reading and block pointer traversal (direct + indirect).
- Implement directory entry parsing and path resolution.
- Implement read-only file access through the VFS layer.
- Implement block/inode bitmap management (allocation and freeing).
- Implement file creation, writing, truncation, and deletion.
- Implement directory creation and deletion.
- Implement
VfsMetadatatrait: chmod/chown modify inode fields directly. - Update xtask to create ext2 images with
mkfs.ext2. - Mount ext2 as
/datainstead of FAT32. - Migrate
/etc/passwd,/etc/shadow,/etc/groupto ext2. - Remove FAT32 permissions index file code.
Task progress snapshot
84 of 84 tracked tasks are complete.
Documentation targets
- Documentation deliverables have not been listed yet.
Deferred follow-on work
- ext3/ext4 journaling
- Triple-indirect block pointers
- Extended attributes (xattr)
- Directory hash tree indexing
- Sparse superblock backups
- Block group locality heuristics
- Online filesystem resize
fsck/ filesystem repair tool- Symbolic links (stored in inode for short links, in data blocks for long)
- Hard link count management beyond basic tracking
- File timestamps beyond mtime (atime, ctime updates)
Source materials
Important code
Code spotlight cards
Code spotlight
Define `Ext2Superblock` struct
Define `Ext2Superblock` struct. Why it matters: The superblock contains all filesystem geometry needed to locate every other structure on disk. Focus on `Ext2Superblock` and `inodes_count` in `kernel-core/src/fs/ext2.rs` while you trace the snippet.
/// ext2 superblock — 1024 bytes at byte offset 1024 on disk.
#[derive(Debug, Clone, Copy)]
pub struct Ext2Superblock {
pub inodes_count: u32,
pub blocks_count: u32,
pub r_blocks_count: u32,
pub free_blocks_count: u32,
pub free_inodes_count: u32,
pub first_data_block: u32,
pub log_block_size: u32,
pub log_frag_size: u32,
pub blocks_per_group: u32,
pub frags_per_group: u32,
pub inodes_per_group: u32,
pub mtime: u32,
pub wtime: u32,
pub mnt_count: u16,
pub max_mnt_count: u16,
pub magic: u16,
pub state: u16,
pub errors: u16,
pub minor_rev_level: u16,
pub lastcheck: u32,
pub checkinterval: u32, Code spotlight
Implement `read_inode()`
Implement `read_inode()`. Why it matters: Every file and directory access begins with reading the target inode from disk. Focus on `read_inode` in `kernel/src/fs/ext2.rs` while you trace the snippet.
/// Read an inode by number (1-based).
pub fn read_inode(&self, inode_num: u32) -> Result<Ext2Inode, Ext2Error> {
let group =
kernel_core::fs::ext2::inode_block_group(inode_num, self.superblock.inodes_per_group);
let index = kernel_core::fs::ext2::inode_index_in_group(
inode_num,
self.superblock.inodes_per_group,
);
let bgd = self
.bgd_table
.get(group as usize)
.ok_or(Ext2Error::CorruptedEntry)?;
let inode_size = self.superblock.inode_size as u32;
let byte_offset = (index as u64) * (inode_size as u64);
let block_offset = byte_offset / (self.block_size as u64);
let offset_in_block = (byte_offset % (self.block_size as u64)) as usize;
let block_num = bgd.inode_table + block_offset as u32;
let block_data = self.read_block(block_num)?;
Ext2Inode::parse(&block_data[offset_in_block..])
} Code spotlight
Replace `mkfs.fat` with `mkfs.ext2`
Replace `mkfs.fat` with `mkfs.ext2`. Why it matters: The data partition must be ext2 format for native Unix metadata support. Focus on `mkfs.fat` and `mkfs.ext2` in `xtask/src/main.rs` while you trace the snippet.
Key files
- kernel-core/src/fs/ — Add ext2 data structure definitions to kernel-core/src/fs/: superblock,
- kernel-core/src/fs/ext2.rs — All on-disk structures are defined in kernel-core/src/fs/ext2.rs for
- kernel/src/fs/ext2.rs — The kernel-side driver (kernel/src/fs/ext2.rs) wraps all I/O in the
- kernel/src/process/mod.rs — The FdBackend enum in kernel/src/process/mod.rs has an Ext2Disk
- xtask/src/main.rs — xtask/src/main.rs creates the ext2 disk image at build time:
How real OSes differ
- **ext3/ext4 journaling**: crash recovery via a write-ahead log. ext2 has no journal — an unclean shutdown requires `e2fsck` to repair.
- **Extent trees** (ext4): replace block pointer lists for better large-file performance.
- **Triple-indirect blocks**: ext2 supports triple-indirect for files >64 MB. We defer this — double-indirect handles up to ~64 MB with 4K blocks.
- **Sparse superblock**: backup superblocks only in certain block groups.
- **Directory indexing** (ext3+ htree): hash-based directory lookup for large directories. We use linear scan.
- **Extended attributes** (xattr): ACLs, SELinux labels, etc.
- **Block group locality**: real implementations try to allocate files near their directory's block group for locality. We use simple first-fit.
- **Delayed allocation** (ext4): allocate blocks at writeback time, not at write time, for better layout.
- **Online resize and defragmentation**.
- Our implementation covers the core ext2 specification (revision 0) which is sufficient for persistent Unix-style file storage with native permissions.
Success criteria
- Boot mounts an ext2 partition at `/data` (or `/`).
- Files created with `touch`/`edit` are stored on ext2 and survive reboot.
- `ls -l` (or fstat) shows correct uid, gid, and permission mode from ext2 inodes.
- `chmod 600 /etc/shadow` persists across reboot.
- `chown user:user /home/user/file` persists across reboot.
- Permission enforcement from Phase 27 works identically with ext2 backend.
- `cargo xtask check` passes.
- The ext2 image can be mounted and inspected on the host with standard Linux tools (`mount -o loop`, `debugfs`, `ls -la`).
- No regressions in existing functionality.
Related phases
Keep exploring the roadmap
Phase 27
CompleteUser Accounts
The OS supports multiple user accounts with login authentication, file ownership, and permission enforcement. A `login` program prompts for username and password before granting shell access. This is the foundation for multi-user operation and secure remote access.
Phase 29
CompletePTY Subsystem
Implement real pseudo-terminal (PTY) pairs so that multiple independent terminal sessions can run simultaneously. A PTY master/slave pair connects a terminal emulator (or network server) to a shell process, enabling remote access (telnet, SSH) and local terminal multiplexing.