diff options
author | Himbeer <himbeer@disroot.org> | 2024-07-10 15:55:45 +0200 |
---|---|---|
committer | Himbeer <himbeer@disroot.org> | 2024-07-10 15:55:45 +0200 |
commit | 6b1eef5e7e4c2ee0f51ee1d5a258e86b08b22298 (patch) | |
tree | 96e97aa08ce87751d6cb9d9654441ea022c5d6d3 | |
parent | 538fb19cd3394cbcd89d0dc9af60540d8b3cb19e (diff) |
vfs: Implement walking directory hooks
Directory hooks are now treated transparently like regular directories when it comes to looking up subresources by name.
-rw-r--r-- | src/kernel.zig | 2 | ||||
-rw-r--r-- | src/lib/syscall.zig | 44 | ||||
-rw-r--r-- | src/lib/vfs.zig | 138 |
3 files changed, 119 insertions, 65 deletions
diff --git a/src/kernel.zig b/src/kernel.zig index 1ab300b..b66bef1 100644 --- a/src/kernel.zig +++ b/src/kernel.zig @@ -197,7 +197,7 @@ fn pagedRun() !noreturn { } } - vfs.init(allocator); + try vfs.init(allocator); try w.print("Initialize VFS\r\n", .{}); try w.print("Start init process\r\n", .{}); diff --git a/src/lib/syscall.zig b/src/lib/syscall.zig index 3b7ed71..51f6ff5 100644 --- a/src/lib/syscall.zig +++ b/src/lib/syscall.zig @@ -86,10 +86,11 @@ fn provideStream(proc: *const process.Info, trap_frame: *trap.Frame) void { const writeFn: ?vfs.Stream.WriteFn = @ptrFromInt(trap_frame.general_purpose_registers[12]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ - .stream = .{ + .tag = .stream, + .data = .{ .stream = .{ .readFn = readFn, .writeFn = writeFn, - }, + } }, }, proc.id)); } @@ -111,12 +112,13 @@ fn provideFile(proc: *const process.Info, trap_frame: *trap.Frame) void { const closeFn: ?vfs.File.CloseFn = @ptrFromInt(trap_frame.general_purpose_registers[14]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ - .file = .{ + .tag = .file, + .data = .{ .file = .{ .openFn = openFn, .readFn = readFn, .writeFn = writeFn, .closeFn = closeFn, - }, + } }, }, proc.id)); } @@ -132,9 +134,10 @@ fn provideHook(proc: *const process.Info, trap_frame: *trap.Frame) void { const callback: vfs.Hook.Callback = @ptrFromInt(trap_frame.general_purpose_registers[11]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ - .hook = .{ + .tag = .hook, + .data = .{ .hook = .{ .callback = callback, - }, + } }, }, proc.id)); } @@ -143,8 +146,17 @@ fn provideHook(proc: *const process.Info, trap_frame: *trap.Frame) void { // ) Result(void) fn mkdir(proc: *const process.Info, trap_frame: *trap.Frame) void { const path_c: [*:0]const u8 = @ptrFromInt(trap_frame.general_purpose_registers[10]); + + const allocator = vfs.treeRoot().allocator; + const tree = allocator.create(vfs.Tree) catch |err| { + sysexchange.frameReturn(void, trap_frame, err); + return; + }; + tree.* = vfs.Tree.init(allocator); + sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ - .dir = vfs.Tree.init(vfs.treeRoot().allocator), + .tag = .dir, + .data = .{ .dir = tree }, }, proc.id)); } @@ -164,11 +176,12 @@ fn provideDirHook(proc: *const process.Info, trap_frame: *trap.Frame) void { const removeFn: vfs.DirHook.RemoveFn = @ptrFromInt(trap_frame.general_purpose_registers[13]); sysexchange.frameReturn(null, trap_frame, vfs.provideResourceZ(path_c, .{ - .dir_hook = .{ + .tag = .dir_hook, + .data = .{ .dir_hook = .{ .provideFn = provideFn, .findFn = findFn, .removeFn = removeFn, - }, + } }, }, proc.id)); } @@ -178,11 +191,16 @@ fn remove(trap_frame: *trap.Frame) void { const path = std.mem.sliceTo(path_c, 0); const dirname = std.fs.path.dirnamePosix(path) orelse "/"; - if (vfs.find(dirname)) |node| { + if (vfs.find(dirname)) |inode| { const basename = std.fs.path.basenamePosix(path); - switch (node.data.resource) { - .dir => |*dir| sysexchange.frameReturn(null, trap_frame, dir.remove(basename)), - .dir_hook => |dir_hook| { + switch (inode.resource.tag) { + .dir => { + const dir = inode.resource.data.dir; + sysexchange.frameReturn(null, trap_frame, dir.remove(basename)); + }, + .dir_hook => { + const dir_hook = inode.resource.data.dir_hook; + const result = dir_hook.removeFn(basename.ptr, basename.len); sysexchange.frameReturnResult(void, trap_frame, result); }, diff --git a/src/lib/vfs.zig b/src/lib/vfs.zig index 49a086a..4285b79 100644 --- a/src/lib/vfs.zig +++ b/src/lib/vfs.zig @@ -10,10 +10,10 @@ const sysexchange = @import("sysexchange.zig"); const mem = std.mem; -var root: Node = .{ .data = .{ .name = "", .resource = .{ .dir = undefined }, .pid = 0 } }; +var root: Node = .{ .data = .{ .name = "", .inode = .{ .resource = .{ .tag = .dir, .data = .{ .dir = undefined } }, .pid = 0 } } }; pub fn treeRoot() *const Tree { - return &root.data.resource.dir; + return root.data.inode.resource.data.dir; } pub const Error = error{ @@ -28,7 +28,7 @@ pub const Error = error{ }; // A stream is a resource that provides a shared data stream with a driver. -pub const Stream = struct { +pub const Stream = extern struct { readFn: ?ReadFn, writeFn: ?WriteFn, @@ -37,7 +37,7 @@ pub const Stream = struct { }; // A file is a resource that creates a unique data stream with a driver. -pub const File = struct { +pub const File = extern struct { openFn: OpenFn, readFn: ?ReadFn, writeFn: ?WriteFn, @@ -50,48 +50,63 @@ pub const File = struct { }; // A hook is a resource that invokes raw driver callbacks when interacted with. -pub const Hook = struct { +pub const Hook = extern struct { callback: Callback, - pub const Callback = *allowzero const fn (pid: u16, data: usize) sysexchange.Result(usize); + pub const Callback = *allowzero const fn (pid: u16, data: usize) callconv(.C) sysexchange.Result(usize); }; // A directory hook is a resource that provides other resources via driver callbacks. -pub const DirHook = struct { +pub const DirHook = extern struct { provideFn: ProvideFn, findFn: FindFn, removeFn: RemoveFn, - pub const ProvideFn = *allowzero const fn (inode: Inode) sysexchange.Result(void); - pub const FindFn = *allowzero const fn (name: [*:0]const u8) ?Inode; - pub const RemoveFn = *allowzero const fn (name: [*]const u8, name_len: usize) callconv(.C) sysexchange.Result(void); + pub const ProvideFn = *allowzero const fn (inode: Inode) callconv(.C) sysexchange.Result(void); + pub const FindFn = *allowzero const fn (name_ptr: [*]const u8, name_len: usize) callconv(.C) ?*Inode; + pub const RemoveFn = *allowzero const fn (name_ptr: [*]const u8, name_len: usize) callconv(.C) sysexchange.Result(void); }; -pub const Resource = union(enum) { - stream: Stream, - file: File, - hook: Hook, - dir: Tree, - dir_hook: DirHook, +pub const ResourceKind = enum(u32) { + stream, + file, + hook, + dir, + dir_hook, }; -pub const Inode = struct { - name: []const u8, +pub const Resource = extern struct { + tag: ResourceKind, + data: extern union { + stream: Stream, + file: File, + hook: Hook, + dir: *Tree, + dir_hook: DirHook, + }, +}; + +pub const Inode = extern struct { resource: Resource, refs: usize = 0, pid: u16, }; -pub const Node = std.DoublyLinkedList(Inode).Node; +pub const DirEntry = struct { + name: []const u8, + inode: Inode, +}; + +pub const Node = std.DoublyLinkedList(DirEntry).Node; pub const Tree = struct { allocator: mem.Allocator, - nodes: std.DoublyLinkedList(Inode), + nodes: std.DoublyLinkedList(DirEntry), pub fn init(allocator: mem.Allocator) Tree { return .{ .allocator = allocator, - .nodes = std.DoublyLinkedList(Inode){}, + .nodes = std.DoublyLinkedList(DirEntry){}, }; } @@ -106,15 +121,15 @@ pub const Tree = struct { return null; } - pub fn provideResource(self: *Tree, inode: Inode) !void { + pub fn provideResource(self: *Tree, entry: DirEntry) !void { var node = try self.allocator.create(Node); - node.data = inode; + node.data = entry; self.nodes.append(node); } pub fn remove(self: *Tree, name: []const u8) !void { if (self.find(name)) |node| { - if (node.data.refs > 0) return Error.InUse; + if (node.data.inode.refs > 0) return Error.InUse; self.nodes.remove(node); } else return Error.NotFound; } @@ -135,8 +150,10 @@ pub const ResourceDescriptor = struct { } pub fn read(self: ResourceDescriptor, proc: *process.Info, buffer: []u8) !noreturn { - return switch (self.inode.resource) { - .stream => |stream| { + return switch (self.inode.resource.tag) { + .stream => { + const stream = self.inode.resource.data.stream; + const driver = process.latestThread(self.inode.pid).?; const copy = try driver.copyBuffer(buffer); @@ -157,8 +174,10 @@ pub const ResourceDescriptor = struct { } pub fn write(self: ResourceDescriptor, proc: *process.Info, bytes: []const u8) !noreturn { - return switch (self.inode.resource) { - .stream => |stream| { + return switch (self.inode.resource.tag) { + .stream => { + const stream = self.inode.resource.data.stream; + const driver = process.latestThread(self.inode.pid).?; const copy = try driver.copyBytes(bytes); @@ -193,43 +212,58 @@ pub const UserInfo = union(enum) { value: sysexchange.Result(usize), }; -pub fn init(allocator: mem.Allocator) void { - root.data.resource.dir = Tree.init(allocator); +pub fn init(allocator: mem.Allocator) !void { + const tree = try allocator.create(Tree); + tree.* = Tree.init(allocator); + root.data.inode.resource.data.dir = tree; } -pub fn find(path: []const u8) ?*Node { +pub fn find(path: []const u8) ?*Inode { if (!std.fs.path.isAbsolutePosix(path)) return null; // Error set is empty. var it = std.fs.path.ComponentIterator(.posix, u8).init(path) catch unreachable; - var tree = root.data.resource.dir; + var resource = root.data.inode.resource; while (it.next()) |component| { - const node = tree.find(component.name) orelse return null; + const inode = switch (resource.tag) { + .dir => blk: { + const dir = resource.data.dir; + break :blk if (dir.find(component.name)) |entry| &entry.data.inode else null; + }, + .dir_hook => blk: { + const dir_hook = resource.data.dir_hook; + break :blk dir_hook.findFn(component.name.ptr, component.name.len); + }, + else => null, // Not a directory (or directory hook). + } orelse return null; if (mem.eql(u8, component.path, (it.last() orelse return null).path)) { - return node; - } - switch (node.data.resource) { - .dir => |dir| tree = dir, - else => {}, + return inode; } + resource = inode.resource; } // No components, this is the root directory (/). - return &root; + return &root.data.inode; } pub fn provideResource(path: []const u8, resource: Resource, pid: u16) !void { if (!std.fs.path.isAbsolutePosix(path)) return Error.RelativePathNotAllowed; const dirname = std.fs.path.dirnamePosix(path) orelse return Error.NoAbsoluteContainingDirectory; - if (find(dirname)) |node| { - return switch (node.data.resource) { - .dir => |*dir| dir.provideResource(.{ - .name = std.fs.path.basenamePosix(path), - .resource = resource, - .pid = pid, - }), + if (find(dirname)) |inode| { + return switch (inode.resource.tag) { + .dir => blk: { + const dir = inode.resource.data.dir; + + break :blk dir.provideResource(.{ + .name = std.fs.path.basenamePosix(path), + .inode = .{ + .resource = resource, + .pid = pid, + }, + }); + }, else => Error.NotADirectory, }; } else return Error.NotFound; @@ -240,10 +274,12 @@ pub fn provideResourceZ(path_c: [*:0]const u8, resource: Resource, pid: u16) !vo } pub fn open(proc: *process.Info, path: []const u8, pid: u16, data: usize) !ResourceDescriptor { - const node = find(path) orelse return Error.NotFound; - return switch (node.data.resource) { - .hook => |hook| { - const driver = process.latestThread(node.data.pid).?; + const inode = find(path) orelse return Error.NotFound; + return switch (inode.resource.tag) { + .hook => { + const hook = inode.resource.data.hook; + + const driver = process.latestThread(inode.pid).?; proc.state = .suspended; try call(driver, hook.callback, .{ pid, data }, null, .{ @@ -251,7 +287,7 @@ pub fn open(proc: *process.Info, path: []const u8, pid: u16, data: usize) !Resou .context = proc, }); }, - else => ResourceDescriptor.init(&node.data), + else => ResourceDescriptor.init(inode), }; } |