The payoff milestone: files are served by a user-space process, reached over
IPC — the kernel never sees a path or an fd.
- lib/vfs_proto.zig: the VFS wire protocol (Op, Request/Reply fixed header +
inline payload, Stat), shared by client and server; one message <= MSG_MAX.
- lib/ipc.zig: replyWait() — the server-side dual-return stub (length in rax,
badge in rdx via a "+{rdx}" read-write operand), deferred from M7.
- sbin/vfs.zig: the real VFS server — an in-heap ramfs (open creates a node)
with an IPC_ReplyWait dispatch loop serving open/read/write/stat/close.
Registers its endpoint under the well-known vfs id at startup.
- lib/unistd.zig: POSIX-style client API in rt — a per-process fd table +
open/close/read/write/lseek/stat, each an IPC_Call to the VFS. The kernel
knows nothing of fds; the table lives here.
- lib/stdio.zig: C stdio over unistd — FILE + fopen/fclose/fread/fwrite/
fseek/ftell/rewind/feof/ferror/fputs/fputc/fgetc (unbuffered for now).
- sbin/vfstest.zig: a client that opens/writes/seeks/reads a file and only
heartbeats "vfstest: ok" if the round trip matched. Packed in the initrd.
- New `vfs` test drives it end to end. initrd test relaxed to generic
liveness. Suite 31/31.
54 lines
1.8 KiB
Zig
54 lines
1.8 KiB
Zig
//! The VFS wire protocol — the message format spoken between a client (via the
|
|
//! `rt` file API) and the user-space VFS server over IPC. A request is a fixed
|
|
//! `Request` header followed by an inline payload (a path, or write bytes); a
|
|
//! reply is a fixed `Reply` header followed by an inline payload (read bytes, or
|
|
//! a Stat). Everything fits in one IPC message (<= ipc MSG_MAX = 256 bytes).
|
|
//!
|
|
//! This is user-space only — the kernel knows nothing of files or paths; it only
|
|
//! moves the bytes. Shared by lib/unistd.zig (client) and sbin/vfs.zig (server).
|
|
|
|
pub const Op = enum(u32) {
|
|
open, // open(path) -> node id
|
|
close, // close(node)
|
|
read, // read(node, offset, len) -> bytes
|
|
write, // write(node, offset, bytes) -> count
|
|
stat, // stat(node) -> Stat
|
|
};
|
|
|
|
/// Request header. `node` is the server-side open-file id (from a prior open);
|
|
/// for `open` the path is the payload and `len` is its length. `offset`/`len`
|
|
/// carry the read/write position and count.
|
|
pub const Request = extern struct {
|
|
op: Op,
|
|
node: u64,
|
|
offset: u64,
|
|
len: u32,
|
|
flags: u32,
|
|
};
|
|
|
|
/// Reply header. `status` is 0 on success or a negative errno; `node` is the new
|
|
/// open-file id (for `open`); `len` is the payload length (bytes read, or the
|
|
/// Stat size).
|
|
pub const Reply = extern struct {
|
|
status: i32,
|
|
_pad: u32 = 0,
|
|
node: u64 = 0,
|
|
len: u32 = 0,
|
|
_pad2: u32 = 0,
|
|
};
|
|
|
|
pub const Stat = extern struct {
|
|
size: u64,
|
|
kind: u32,
|
|
_pad: u32 = 0,
|
|
};
|
|
|
|
pub const msg_max: usize = 256;
|
|
pub const req_size: usize = @sizeOf(Request);
|
|
pub const reply_size: usize = @sizeOf(Reply);
|
|
/// Largest inline payload that still fits one IPC message alongside a header.
|
|
pub const max_payload: usize = msg_max - req_size;
|
|
|
|
/// Open flags.
|
|
pub const O_CREAT: u32 = 1;
|