Phase 38
Filesystem Enhancements
The filesystem supports symlinks, hard links, `umask`-backed creation defaults, the remaining Unix permission semantics needed for symlink-aware metadata and directory listing, a `/proc` filesystem for process introspection, and basic device nodes (`/dev/null`, `/dev/zero`, `/dev/urandom`, `/dev/full`). These are the filesystem primitives that Unix tools and build systems assume exist.
Learning goal
Understand the difference between hard links and symbolic links at the inode level.
Category
infrastructure
Components touched
Builds on
Unlocked by this phase
Why this phase matters
The filesystem supports symlinks, hard links, umask-backed creation defaults, the remaining Unix permission semantics needed for symlink-aware metadata and directory listing, a /proc filesystem for process introspection, and basic device nodes (/dev/null, /dev/zero, /dev/urandom, /dev/full). These are the filesystem primitives that Unix tools and build systems assume exist.
How it works in m3OS
The roadmap entry defines the learning scope for this phase; a deeper implementation doc has not been matched yet.
How it builds on earlier phases
This phase builds on I/O Multiplexing and turns that foundation into Filesystem Enhancements.
Implementation outline
- Implement symlinks in tmpfs (create, readlink, path resolution with follow).
- Implement symlinks in ext2.
- Add symlink loop detection (max 40 hops, return
ELOOP). - Implement hard links in ext2 (link count management).
- Complete the permission model for directory listing and symlink-aware metadata lookups.
- Implement
umask()syscall with per-process mask. - Implement
/procfilesystem (start with/proc/<pid>/statusand/proc/meminfo). - Add
/dev/null,/dev/zero,/dev/urandom,/dev/fulldevice nodes. - Implement
statfs/fstatfs. - Write
lnutility for creating links. - Test:
ln -s /bin/sh0 /tmp/mysh && /tmp/myshworks via symlink. - Test: permission enforcement blocks unauthorized access.
Task progress snapshot
208 of 208 tracked tasks are complete.
Documentation targets
- Documentation deliverables have not been listed yet.
Deferred follow-on work
- Extended attributes (xattr)
- Access Control Lists (ACLs)
- inotify / fanotify (filesystem events)
- sysfs
- Full /proc (cgroups, net, sys subdirectories)
- File locking (flock, lockf)
- Overlay filesystems
- Page cache for file data
- mknod syscall for arbitrary device creation
Source materials
Important code
Code spotlight cards
Code spotlight
Add `Symlink` variant to `TmpfsNode`
require a third variant that stores the target path string instead of file data. Why it matters: tmpfs currently has File and Dir variants only. Symlinks. Focus on `Symlink` and `TmpfsNode` in `kernel-core/src/fs/tmpfs.rs` while you trace the snippet.
enum TmpfsNode {
File(FileData),
Dir(DirData),
Symlink(SymlinkData),
} Code spotlight
Implement ext2 symlink creation and reading
pointers (up to 60 bytes) and longer targets in a data block. Both paths must work. Why it matters: ext2 stores short symlink targets inline in the inode's block. Focus on `Ext2Volume::create_symlink` and `Ext2Volume::read_symlink` in `kernel/src/fs/ext2.rs` while you trace the snippet.
EXT2_DIND_BLOCK, EXT2_FT_DIR, EXT2_FT_REG_FILE, EXT2_FT_SYMLINK, EXT2_IND_BLOCK,
EXT2_NDIR_BLOCKS, EXT2_ROOT_INO, Ext2BlockGroupDescriptor, Ext2DirEntry, Ext2Error, Ext2Inode,
Ext2Superblock, S_IFDIR, S_IFLNK, S_IFREG,
};
use spin::Mutex;
// ---------------------------------------------------------------------------
// Ext2Volume (P28-T019)
// ---------------------------------------------------------------------------
/// A mounted ext2 volume backed by virtio-blk sectors.
pub struct Ext2Volume { Code spotlight
Implement `sys_symlink()` and `sys_symlinkat()` syscalls
They must route through resolve_fs_target() to the correct filesystem backend. Why it matters: These are the userspace entry points for symlink creation. Focus on `sys_symlink` and `sys_symlinkat` in `kernel/src/arch/x86_64/syscall.rs` while you trace the snippet.
fn sys_symlink(target_ptr: u64, linkpath_ptr: u64) -> u64 {
sys_symlinkat(target_ptr, AT_FDCWD, linkpath_ptr)
} Key files
- kernel/src/fs/procfs.rs — A new kernel/src/fs/procfs.rs module synthesizes file content on read from kernel
How real OSes differ
- **Dentry cache** for fast path lookup with negative caching.
- **Inode cache** for recently accessed inodes.
- **Page cache** for file data (unified with VM).
- **Overlay filesystems** (overlayfs, unionfs).
- **Extended attributes** (xattr) for SELinux labels, ACLs, capabilities.
- **Full procfs** with hundreds of entries per process.
- **sysfs** for device/driver introspection.
- **devtmpfs** with udev rules for dynamic device creation.
- **inotify/fanotify** for filesystem event monitoring.
Success criteria
- `symlink("/bin/sh0", "/tmp/mysh")` creates a working symlink.
- `readlink("/tmp/mysh")` returns `"/bin/sh0"`.
- Symlink chain resolution works (A→B→C) with loop detection.
- Hard links share an inode: modify via one path, see changes via the other.
- `unlink()` of a hard-linked file only removes it when link count hits 0.
- `getdents64()` on a directory without read permission returns `EACCES`.
- `umask(022)` causes `mkdir()` to create directories with mode 0755.
- `cat /proc/self/status` shows the process's own info.
- `readlink /proc/self/exe` returns the current process executable path.
- `cat /proc/meminfo` shows memory statistics.
- `cat /dev/urandom | hexdump | head` produces random output.
- `echo test > /dev/null` succeeds silently.
- `dd if=/dev/zero bs=4096 count=1` produces 4096 zero bytes.
- All existing tests pass without regression.
Related phases
Keep exploring the roadmap
Phase 37
CompleteI/O Multiplexing
Programs can wait on multiple file descriptors simultaneously without busy-waiting. `select()`, an improved `poll()`, and `epoll` provide scalable I/O readiness notification. Non-blocking I/O (`O_NONBLOCK`) works for sockets, pipes, and PTYs. Event-driven servers become possible.
Phase 39
CompleteUnix Domain Sockets
The OS supports `AF_UNIX` (Unix domain) sockets for high-performance local inter-process communication. Programs can create stream and datagram sockets bound to filesystem paths. `socketpair()` creates connected pairs for parent-child IPC after `fork()`. This is the primary IPC mechanism on Linux, used by D-Bus, X11, systemd, PostgreSQL, Docker, and many other systems.