kernel: VFS root — mount table, /system from the initrd, fs syscalls
system/kernel/vfs.zig is the resolve+redirect router: fs_resolve (#46) walks the kernel mount table; a path under the kernel-backed /system mount (the initrd, seeded by setInitialRamdisk with a derived directory table) yields a permanent stateless node token served by fs_node (#47) — read/status/readdir with copy-out, initrd reads lock-free — while a path under a userspace mount yields the backend's endpoint (installed in the caller's table, DEDUPLICATED so 16 slots can't be exhausted by repeated resolves) plus the rewritten mount-relative path; the caller then speaks the unchanged vfs-protocol rendezvous directly. The kernel never blocks on a userspace filesystem, holds no open-file state, and refuses create-intent on the immutable /system. fs_mount (#48) is the syscall form of the old router's op-6 cap-pass (possession of the backend handle is the capability; an optional rewrite prefix maps the mount into the backend's namespace — how /var will reach the flash volume); fs_unmount (#49) removes one. A dead backend's mount clears lazily on resolve. Dormant this milestone: the userspace vfs still serves runtime.fs unchanged; the kvfs QEMU case covers the kernel side (resolution, ELF magic read-through, /system listing, read-only + unknown refusals) until the M-G cutover exercises the syscalls end-to-end.
This commit is contained in:
@@ -27,6 +27,7 @@ const sync = @import("sync.zig");
|
||||
const process = @import("process.zig");
|
||||
const initial_ramdisk = @import("initial-ramdisk");
|
||||
const kernel_log = @import("log.zig");
|
||||
const kernel_vfs = @import("vfs.zig");
|
||||
|
||||
/// Formatted test-marker write. Goes through the kernel log (not straight to
|
||||
/// serial): the log lock is what keeps marker lines from interleaving with
|
||||
@@ -209,6 +210,8 @@ pub fn run(case: []const u8, boot_information: *const BootInformation) void {
|
||||
initialRamdiskTest(boot_information);
|
||||
} else if (eql(case, "vfs")) {
|
||||
vfsTest(boot_information);
|
||||
} else if (eql(case, "kvfs")) {
|
||||
kernelVfsTest(boot_information);
|
||||
} else if (eql(case, "input")) {
|
||||
inputTest(boot_information);
|
||||
} else if (eql(case, "iopass")) {
|
||||
@@ -3073,6 +3076,64 @@ fn bundledInit(boot_information: *const BootInformation) ?[]const u8 {
|
||||
return item.blob;
|
||||
}
|
||||
|
||||
/// The kernel VFS root (M-F): resolve initrd paths to node tokens, read an ELF
|
||||
/// header through nodeRead, enumerate /system's derived directory table, and
|
||||
/// verify the read-only + unknown-path refusals. Pure kernel-side — the
|
||||
/// syscall surface gets its end-to-end coverage when runtime.fs cuts over.
|
||||
fn kernelVfsTest(boot_information: *const BootInformation) void {
|
||||
log("DANOS-TEST-BEGIN: kvfs\n", .{});
|
||||
if (boot_information.initial_ramdisk_len == 0) {
|
||||
check("bootloader handed over the initial_ramdisk", false);
|
||||
result();
|
||||
return;
|
||||
}
|
||||
const image = @as([*]const u8, @ptrFromInt(boot_handoff.physicalToVirtual(boot_information.initial_ramdisk_base)))[0..boot_information.initial_ramdisk_len];
|
||||
process.setInitialRamdisk(image); // also seeds the kernel VFS /system mount
|
||||
|
||||
// A file resolves to a kernel node token; its status and bytes are served.
|
||||
const resolved = kernel_vfs.resolvePath("/system/services/init", false);
|
||||
const is_file = resolved == .kernel_node;
|
||||
check("/system/services/init resolves to a kernel node", is_file);
|
||||
if (is_file) {
|
||||
const status = kernel_vfs.nodeStatus(resolved.kernel_node);
|
||||
check("its status is a non-empty regular file", status != null and status.?.kind == abi.file_kind_regular and status.?.size > 0);
|
||||
var header: [4]u8 = undefined;
|
||||
const n = kernel_vfs.nodeRead(resolved.kernel_node, 0, &header) orelse 0;
|
||||
check("its first bytes are an ELF magic", n == 4 and header[0] == 0x7f and header[1] == 'E' and header[2] == 'L' and header[3] == 'F');
|
||||
}
|
||||
|
||||
// Directories resolve and enumerate: /system lists services/drivers/tests.
|
||||
const root_directory = kernel_vfs.resolvePath("/system", false);
|
||||
check("/system resolves to a directory node", root_directory == .kernel_node);
|
||||
var saw_services = false;
|
||||
var saw_drivers = false;
|
||||
var saw_files_in_services = false;
|
||||
if (root_directory == .kernel_node) {
|
||||
var cursor: u64 = 0;
|
||||
var name: [64]u8 = undefined;
|
||||
while (kernel_vfs.nodeReaddir(root_directory.kernel_node, cursor, &name)) |entry| : (cursor += 1) {
|
||||
if (eql(name[0..entry.name_len], "services")) saw_services = true;
|
||||
if (eql(name[0..entry.name_len], "drivers")) saw_drivers = true;
|
||||
}
|
||||
}
|
||||
check("readdir /system yields services and drivers", saw_services and saw_drivers);
|
||||
const services = kernel_vfs.resolvePath("/system/services", false);
|
||||
if (services == .kernel_node) {
|
||||
var cursor: u64 = 0;
|
||||
var name: [64]u8 = undefined;
|
||||
while (kernel_vfs.nodeReaddir(services.kernel_node, cursor, &name)) |entry| : (cursor += 1) {
|
||||
if (eql(name[0..entry.name_len], "init")) saw_files_in_services = true;
|
||||
}
|
||||
}
|
||||
check("readdir /system/services yields init", saw_files_in_services);
|
||||
|
||||
// Refusals: unknown paths, and create-intent on the immutable initrd.
|
||||
check("an unknown path does not resolve", kernel_vfs.resolvePath("/system/services/no-such", false) == .not_found);
|
||||
check("an unmounted absolute path does not resolve", kernel_vfs.resolvePath("/elsewhere", false) == .not_found);
|
||||
check("create on /system is refused (read-only)", kernel_vfs.resolvePath("/system/services/new-file", true) == .not_found);
|
||||
result();
|
||||
}
|
||||
|
||||
fn spawnNamed(rd: initial_ramdisk.Reader, name: []const u8) bool {
|
||||
var i: u32 = 0;
|
||||
while (i < rd.count) : (i += 1) {
|
||||
|
||||
Reference in New Issue
Block a user