|
miniOS
x86_64 hobby kernel with SMP, VFS, and POSIX process model
|
miniOS uses a two-layer registration model: a filesystem type registry maps names like "tmpfs" to mount callbacks, and a mount table maps path prefixes like "/tmp" to vfs_ops_t operation tables. Adding a new filesystem means implementing vfs_ops_t, writing a mount callback, and registering with register_filesystem().
register_filesystem(name, mount_cb) — called once at kernel init — records a name→callback mapping. When mount("tmpfs", ...) arrives from userspace (or from main.c), the VFS calls vfs_mount_fstype("tmpfs", source, target, data), which looks up the name and invokes the callback. Up to VFS_MAX_FS_TYPES = 16 types can be registered.
vfs_register_mount(point, ops, root_ino) — called inside the mount callback — writes a vfs_mount_t entry that binds a path prefix to a vfs_ops_t *. The VFS router uses longest-prefix matching: opening /tmp/foo finds the /tmp entry and passes "foo" (relative) to that filesystem's lookup(). Up to VFS_MAX_MOUNTS = 16 mounts are supported.
NULL pointers are safe for operations the filesystem does not support. The VFS checks for NULL before calling and returns -ENOSYS / -EROFS to the caller.
The readdir callback type vfs_dirent_cb_t is defined in <miniOS/fs/fs_types.h> (included transitively via <miniOS/fs/vfs.h>):
Operation contract:
| Op | Receives | Returns |
|---|---|---|
| lookup | path relative to mount point ("" = root) | 0 / −1 |
| read | inode number, byte offset, buffer, len | bytes read / −1 |
| readdir | dir inode, cursor *offset, callback | 0 or callback's non-zero |
| write | inode number, byte offset, buffer, len | bytes written / −1 |
| flush | inode number | 0 / −1 |
| create | parent inode, filename (no /) | 0 / −errno |
| unlink | parent inode, filename | 0 / −errno |
| rename | old parent inode + name, new parent inode + name | 0 / −errno |
| mkdir | parent inode, name | 0 / −errno |
| rmdir | parent inode, name | 0 / −errno |
| set_root | root inode for this mount | void |
| truncate | inode number, new size in bytes (0 = truncate-to-zero) | 0 / −errno |
| readlink | symlink inode, buf, len | bytes / −1 |
| symlink | parent inode, name, target string | 0 / −1 |
| mknod | parent inode, name, mode, dev | 0 / −errno |
| chmod | inode, mode bits | 0 / −1 |
set_root is required for filesystems that support multiple independent mounts from the same inode pool (e.g. tmpfs). It is called by the VFS router before every lookup() to set the per-mount root inode. Single-root filesystems (ext2, sysfs) leave it NULL.
Include <miniOS/fs/vfs.h> in both. The header needs at minimum:
In myfs.c, define a static ops table and implement only the operations your filesystem supports. Read-only example:
The mount callback is invoked by vfs_mount_fstype(). It must call vfs_register_mount() before returning. For a single-instance filesystem:
For a filesystem that can be mounted multiple times (like tmpfs), allocate a fresh root inode per mount and pass it as the third argument to vfs_register_mount(). The VFS will call set_root(root_ino) before each lookup() on that mount:
Call myfs_init() before the first mount attempt. Add the include and the call:
If you want the kernel to auto-mount it at boot (before init runs), add:
The mount point directory must already exist in the root filesystem (create it in the ext2 image) or be a prefix that the VFS router can match.
user/init/main.c runs as PID 1. It already mounts /sys via a raw SYS_mount syscall (nr 165). Add further mounts the same way:
The filesystem type ("myfs") must already be registered by the kernel via myfs_init(). Userspace cannot register new filesystem types — only the kernel can call register_filesystem().
Rule of thumb: Mount in main.c (kernel) if the filesystem must exist before init starts (e.g. /dev, /tmp). Mount in init if it requires the root filesystem to be up first (e.g. /sys needs /sys directory in ext2).
| Type name | Source | Mount points | Notes |
|---|---|---|---|
| ext2 | src/kernel/fs/ext2.c | / | Persistent; requires vfs_mount_data_t |
| tmpfs | src/kernel/fs/tmpfs.c | /tmp, /dev | Multi-root; each mount gets an isolated inode pool |
| sysfs | src/kernel/fs/sysfs.c | /sys | Read-only; tree built by drivers via sysfs_create_*() |
| fat32 | src/kernel/fs/fat32.c | /data (or any) | Read-only; requires vfs_mount_data_t; LFN + 8.3 names; inode = starting cluster |
Both drivers accept a vfs_mount_data_t (defined in include/miniOS/fs/fs_types.h) that identifies the block device by major/minor number. The driver calls blkdev_find() internally to resolve the LBA bounds — the caller never handles raw sector numbers.
Each call allocates a fresh, empty root directory. All tmpfs mounts share the same global inode pool (TMPFS_MAX_INODES = 256 across all mounts).
Populate the tree before or after mounting using sysfs_create_dir() / sysfs_create_file_show() with g_sysfs_root as the root anchor.