// SPDX-FileCopyrightText: 2024 Himbeer // // SPDX-License-Identifier: AGPL-3.0-or-later const std = @import("std"); const Console = @import("Console.zig"); const process = @import("process.zig"); const syscall = @import("syscall.zig"); const sysexchange = @import("sysexchange.zig"); const userinit = @import("userinit.zig"); const vfs = @import("vfs.zig"); const Result = sysexchange.Result; const io = std.io; const tar = std.tar; const File = tar.Iterator(io.FixedBufferStream([]const u8).Reader).File; const iofs = struct { const debug = struct { fn write(ptr: [*]const u8, len: usize) callconv(.C) Result(usize) { const bytes = ptr[0..len]; const console = Console.autoChoose() orelse { return Result(usize).fromAnyTypeOrError(Error.NoConsole); }; const w = console.writer(); w.print("{s}", .{bytes}) catch |err| { return Result(usize).fromAnyTypeOrError(err); }; return Result(usize).fromAnyTypeOrError(bytes.len); } }; }; pub const Error = error{ NoTarFileInitializer, NoConsole, }; pub fn provideBuiltin() !void { try addDir("/io"); try addDir("/userinit"); try provideConsole(); try provideUserinit(); } fn provideConsole() !void { try vfs.provideResource("/io/debug", .{ .tag = .stream, .data = .{ .stream = .{ .readFn = null, .writeFn = iofs.debug.write, }, }, }, 0); } fn provideUserinit() !void { var userinit_stream = io.fixedBufferStream(userinit.tarball); const reader = userinit_stream.reader(); var file_name_buffer: [4086]u8 = undefined; var link_name_buffer: [4086]u8 = undefined; var path_buffer: [4096]u8 = undefined; var it = tar.iterator(reader, .{ .file_name_buffer = file_name_buffer[0..], .link_name_buffer = link_name_buffer[0..], }); while (try it.next()) |file| { const path = try std.fmt.bufPrint(path_buffer[0..], "/userinit/{s}", .{file.name}); switch (file.kind) { .file => try addFile(path, file), .directory => try addDir(path), .sym_link => {}, } } } fn addFile(path: []const u8, file: File) !void { const allocator = vfs.treeRoot().allocator; const initializer = try allocator.create(File); initializer.* = file; try vfs.provideResource(path, .{ .tag = .file, .data = .{ .file = .{ .openFn = open, .readFn = read, .writeFn = null, .closeFn = close, .initializer = initializer, }, }, }, 0); } fn addDir(path: []const u8) !void { const allocator = vfs.treeRoot().allocator; const tree = try allocator.create(vfs.Tree); tree.* = vfs.Tree.init(allocator); try vfs.provideResource(path, .{ .tag = .dir, .data = .{ .dir = tree }, }, 0); } fn open(initializer: ?*anyopaque, _: u16) callconv(.C) Result(*anyopaque) { const file_template: *File = @alignCast(@ptrCast(initializer orelse { return Result(*anyopaque).fromAnyTypeOrError(Error.NoTarFileInitializer); })); const allocator = vfs.treeRoot().allocator; const context = allocator.create(File) catch |err| { return Result(*anyopaque).fromAnyTypeOrError(err); }; context.* = file_template.*; return Result(*anyopaque).fromAnyTypeOrError(context); } fn read(context: *anyopaque, ptr: [*]u8, len: usize) callconv(.C) Result(usize) { const ctx: *File = @alignCast(@ptrCast(context)); const buffer = ptr[0..len]; return Result(usize).fromAnyTypeOrError(ctx.read(buffer)); } fn close(context: *anyopaque) callconv(.C) void { const ctx: *File = @alignCast(@ptrCast(context)); const allocator = vfs.treeRoot().allocator; allocator.destroy(ctx); }