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.
This commit is contained in:
123
src/initrd_fs.c
Normal file
123
src/initrd_fs.c
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
Reference in New Issue
Block a user