Files
claude-os/src/initrd_fs.c
AI 0c5aa72fd3 Add VFS subsystem and initrd filesystem driver (AI)
VFS subsystem (vfs.c/h):
- Mount table with up to 16 mount points, longest-prefix path matching.
- File descriptor table (256 entries, fds 0-2 reserved for std streams).
- Path resolution walks mount table then delegates to filesystem's
  finddir() for each path component.
- Operations: open, close, read, write, seek, readdir, stat.
- Each filesystem driver provides a vfs_fs_ops_t with callbacks.

Initrd filesystem driver (initrd_fs.c/h):
- Read-only VFS driver backed by the CPIO ramdisk.
- Mounted at '/initrd' during boot.
- Zero-copy reads: file data points directly into the CPIO archive
  memory, no allocation or copying needed.
- Supports readdir (flat iteration) and finddir (name lookup).

Bug fix: resolve_path was overwriting file-specific fs_data (set by
finddir, e.g. pointer to CPIO file data) with the mount's fs_data
(NULL). Fixed to preserve fs_data from finddir.

Verified in QEMU: kernel reads /initrd/README via VFS and prints its
contents. Ring 3 user process continues to work.
2026-02-23 12:23:32 +00:00

124 lines
3.3 KiB
C

/**
* @file initrd_fs.c
* @brief CPIO initial ramdisk VFS driver implementation.
*
* Provides a read-only VFS interface to the CPIO archive loaded at boot.
* Files are accessed directly from the archive memory (zero-copy reads).
*/
#include "initrd_fs.h"
#include "vfs.h"
#include "cpio.h"
#include <string.h>
/* Debug print helpers defined in kernel.c */
extern void offset_print(const char *str);
extern void print_hex(uint32_t val);
/**
* Read from a file in the initrd.
* Data is read directly from the CPIO archive (memory-mapped).
*/
static int32_t initrd_read(vfs_node_t *node, uint32_t offset,
uint32_t size, void *buf) {
if (!node || !node->fs_data || !buf) return -1;
/* fs_data points to the file's data within the CPIO archive */
const uint8_t *data = (const uint8_t *)node->fs_data;
uint32_t file_size = node->size;
if (offset >= file_size) return 0;
uint32_t remaining = file_size - offset;
if (size > remaining) size = remaining;
memcpy(buf, data + offset, size);
return (int32_t)size;
}
/**
* Read a directory entry from the initrd root.
* The initrd is a flat archive — all files are at the root level.
*/
static int initrd_readdir(vfs_node_t *dir, uint32_t idx, vfs_dirent_t *out) {
(void)dir;
uint32_t off = 0;
uint32_t current = 0;
cpio_entry_t entry;
while (cpio_next(&off, &entry) == 0) {
/* Skip the "." directory entry if present */
if (entry.name[0] == '.' && entry.name[1] == '\0') continue;
/* Strip "./" prefix */
const char *name = entry.name;
if (name[0] == '.' && name[1] == '/') name += 2;
/* Skip empty names */
if (*name == '\0') continue;
if (current == idx) {
memset(out, 0, sizeof(vfs_dirent_t));
strncpy(out->name, name, VFS_MAX_NAME - 1);
out->inode = current;
out->type = VFS_FILE;
return 0;
}
current++;
}
return -1; /* No more entries */
}
/**
* Find a file by name within the initrd.
*/
static int initrd_finddir(vfs_node_t *dir, const char *name, vfs_node_t *out) {
(void)dir;
cpio_entry_t entry;
if (cpio_find(name, &entry) != 0) {
return -1;
}
memset(out, 0, sizeof(vfs_node_t));
/* Strip "./" prefix for the node name */
const char *display_name = entry.name;
if (display_name[0] == '.' && display_name[1] == '/') {
display_name += 2;
}
strncpy(out->name, display_name, VFS_MAX_NAME - 1);
out->type = VFS_FILE;
out->size = entry.datasize;
out->mode = entry.mode;
out->fs_data = (void *)entry.data; /* Direct pointer into CPIO archive */
return 0;
}
/** Filesystem operations for the initrd. */
static vfs_fs_ops_t initrd_ops = {
.open = NULL, /* No special open needed */
.close = NULL, /* No special close needed */
.read = initrd_read,
.write = NULL, /* Read-only */
.readdir = initrd_readdir,
.finddir = initrd_finddir,
};
int init_initrd_fs(void) {
if (cpio_count() == 0) {
offset_print(" INITRD_FS: no files in ramdisk\n");
}
int ret = vfs_mount("/initrd", &initrd_ops, NULL);
if (ret != 0) {
offset_print(" INITRD_FS: failed to mount\n");
return -1;
}
offset_print(" INITRD_FS: mounted at /initrd\n");
return 0;
}