sylveos

Toy Operating System
Log | Files | Refs

fs.zig (8759B)


      1 const std = @import("std");
      2 const pi = @import("pi");
      3 
      4 const vfs = pi.fs.vfs;
      5 const sd = pi.devices.sd;
      6 const uart = pi.devices.mini_uart;
      7 
      8 const Explorer = struct {
      9     alloc: std.mem.Allocator,
     10     io_w: *std.Io.Writer,
     11 
     12     fs: vfs.FileSystem,
     13     dir: vfs.Directory,
     14     path: std.ArrayList([]const u8),
     15 
     16     const Self = @This();
     17 
     18     pub fn init(alloc: std.mem.Allocator, fs: vfs.FileSystem, io_w: *std.Io.Writer) !Self {
     19         var path: std.ArrayList([]const u8) = try .initCapacity(alloc, 1);
     20         try path.append(alloc, "/");
     21 
     22         return .{
     23             .alloc = alloc,
     24             .fs = fs,
     25             .dir = try fs.open_root(),
     26             .path = path,
     27             .io_w = io_w,
     28         };
     29     }
     30 
     31     pub fn read_line(self: *Self) !void {
     32         const path = try std.mem.concat(self.alloc, u8, self.path.items);
     33         defer self.alloc.free(path);
     34         try self.io_w.print("[{s}] $ ", .{path});
     35 
     36         var line: std.ArrayList(u8) = try .initCapacity(self.alloc, 16);
     37         defer line.deinit(self.alloc);
     38 
     39         // Wait until \n
     40         while (true) {
     41             const byte = uart.read_byte_sync();
     42             if (byte == '\n') break;
     43 
     44             try line.append(self.alloc, byte);
     45         }
     46 
     47         try self.parse_line(line.items);
     48     }
     49 
     50     // I would like to apologize for this truly bad parser
     51     fn parse_line(self: *Self, line: []const u8) !void {
     52         var it = std.mem.splitAny(u8, line, " ");
     53 
     54         const cmd = it.next() orelse return;
     55         const entry = it.next() orelse ".";
     56 
     57         if (std.mem.eql(u8, cmd, "ls")) {
     58             try self.ls(entry);
     59         } else if (std.mem.eql(u8, cmd, "tree")) {
     60             try self.tree(entry);
     61         } else if (std.mem.eql(u8, cmd, "cd")) {
     62             try self.cd(entry);
     63         } else if (std.mem.eql(u8, cmd, "stat")) {
     64             try self.stat(entry);
     65         } else if (std.mem.eql(u8, cmd, "cat")) {
     66             try self.cat(entry);
     67         } else if (std.mem.eql(u8, cmd, "help")) {
     68             try self.io_w.print("Commands: ls, tree, cd, stat, cat, help, exit\n", .{});
     69         } else if (std.mem.eql(u8, cmd, "exit")) {
     70             pi.reboot();
     71         } else {
     72             try self.io_w.print("\"{s}\": Command not found\n", .{cmd});
     73         }
     74     }
     75 
     76     fn open_directory(self: *Self, directory: []const u8) !?vfs.Directory {
     77         var entry = try self.dir.open_entry(directory) orelse {
     78             try self.io_w.print("\"{s}\": No such file or directory\n", .{directory});
     79             return null;
     80         };
     81         defer entry.close();
     82 
     83         if (!entry.stat().directory) {
     84             try self.io_w.print("\"{s}\": Not a directory\n", .{directory});
     85             return null;
     86         }
     87 
     88         return try self.fs.open_directory(&entry);
     89     }
     90 
     91     fn ls(self: *Self, directory: []const u8) !void {
     92         const dir = if (std.mem.eql(u8, directory, ".")) self.dir else try self.open_directory(directory) orelse return;
     93 
     94         try self.io_w.print("Name:\n", .{});
     95 
     96         var it = try dir.iterate();
     97         while (try it.next()) |entry| {
     98             defer entry.close();
     99 
    100             try self.io_w.print("- {s}\n", .{entry.stat().name});
    101         }
    102 
    103         if (!std.mem.eql(u8, directory, ".")) {
    104             dir.close();
    105         }
    106     }
    107     fn tree(self: *Self, directory: []const u8) !void {
    108         const dir = if (std.mem.eql(u8, directory, ".")) self.dir else try self.open_directory(directory) orelse return;
    109 
    110         try self.io_w.print("{s}\n", .{directory});
    111 
    112         var visited: std.ArrayList(Visited) = try .initCapacity(self.alloc, 0);
    113         defer visited.deinit(self.alloc);
    114 
    115         try walk(
    116             self.alloc,
    117             &self.fs,
    118             dir,
    119             &visited,
    120             1,
    121         );
    122 
    123         for (visited.items) |entry| {
    124             const prefix = try make_prefix(self.alloc, entry.level);
    125             defer self.alloc.free(prefix);
    126             defer self.alloc.free(entry.name);
    127 
    128             try self.io_w.print("{s}{s}\n", .{ prefix, entry.name });
    129         }
    130 
    131         if (!std.mem.eql(u8, directory, ".")) {
    132             dir.close();
    133         }
    134     }
    135     fn cd(self: *Self, directory: []const u8) !void {
    136         if (std.mem.eql(u8, directory, ".")) return;
    137 
    138         const dir = try self.open_directory(directory) orelse return;
    139 
    140         self.dir.close();
    141         self.dir = dir;
    142 
    143         if (std.mem.eql(u8, directory, "..")) {
    144             self.alloc.free(self.path.pop() orelse unreachable);
    145             self.alloc.free(self.path.pop() orelse unreachable);
    146 
    147             if (self.path.items.len == 1) {
    148                 self.dir.close();
    149                 self.dir = try self.fs.open_root();
    150             }
    151 
    152             return;
    153         }
    154 
    155         try self.path.append(self.alloc, try self.alloc.dupe(u8, directory));
    156         try self.path.append(self.alloc, try self.alloc.dupe(u8, "/"));
    157     }
    158     fn stat(self: *Self, file: []const u8) !void {
    159         const entry = (try self.dir.open_entry(file)) orelse {
    160             try self.io_w.print("\"{s}\": No such file or directory\n", .{file});
    161             return;
    162         };
    163         defer entry.close();
    164 
    165         const info = entry.stat();
    166 
    167         try self.io_w.print(
    168             \\     File: {s}
    169             \\     Size: {b}
    170             \\     Kind: {s}
    171             \\   Hidden: {any}
    172             \\Read Only: {any}
    173             \\ Creation: {d}
    174             \\   Access: {d}
    175             \\   Modify: {d}
    176             \\
    177         , .{
    178             info.name,
    179             info.size,
    180             if (info.directory) "directory" else "file",
    181             info.read_only,
    182             info.hidden,
    183             info.creation_time,
    184             info.access_time,
    185             info.modify_time,
    186         });
    187     }
    188     fn cat(self: *Self, file: []const u8) !void {
    189         var file_entry = (try self.dir.open_entry(file)) orelse {
    190             try self.io_w.print("\"{s}\": No such file or directory\n", .{file});
    191             return;
    192         };
    193         defer file_entry.close();
    194 
    195         const file_handle = try self.fs.open_file(&file_entry);
    196         defer file_entry.close();
    197 
    198         var contents: std.ArrayList(u8) = try .initCapacity(self.alloc, file_entry.stat().size);
    199         defer contents.deinit(self.alloc);
    200 
    201         while (try file_handle.read()) |bytes| {
    202             defer self.alloc.free(bytes);
    203             try contents.appendSlice(self.alloc, bytes);
    204         }
    205 
    206         try self.io_w.print("{s}\n", .{contents.items});
    207     }
    208 
    209     // Tree
    210     const Visited = struct {
    211         name: []const u8,
    212         level: usize,
    213     };
    214 
    215     fn make_prefix(alloc: std.mem.Allocator, level: usize) ![]const u8 {
    216         const prefix = "|  ";
    217         const base = "|- ";
    218 
    219         return switch (level) {
    220             0 => return try alloc.dupe(u8, ""),
    221             1 => return try alloc.dupe(u8, base),
    222             else => {
    223                 var out = try alloc.alloc(u8, (level - 1) * prefix.len + base.len);
    224                 for (0..(level - 1)) |i| {
    225                     @memcpy(out[i * prefix.len .. (i + 1) * prefix.len], prefix);
    226                 }
    227                 @memcpy(out[(level - 1) * prefix.len .. level * prefix.len], base);
    228 
    229                 return out;
    230             },
    231         };
    232     }
    233 
    234     fn walk(alloc: std.mem.Allocator, fs: *vfs.FileSystem, directory: vfs.Directory, visited: *std.ArrayList(Visited), level: usize) !void {
    235         defer directory.close();
    236 
    237         var it = try directory.iterate();
    238         defer it.close();
    239 
    240         while (try it.next()) |entry| {
    241             defer entry.close();
    242 
    243             const info = entry.stat();
    244             if (std.mem.eql(u8, info.name, ".") or std.mem.eql(u8, info.name, "..")) continue;
    245 
    246             const dupe_name = try alloc.dupe(u8, info.name);
    247             try visited.append(alloc, .{ .level = level, .name = dupe_name });
    248 
    249             if (info.directory) {
    250                 try walk(
    251                     alloc,
    252                     fs,
    253                     try fs.open_directory(@constCast(&entry)),
    254                     visited,
    255                     level + 1,
    256                 );
    257             }
    258         }
    259     }
    260 };
    261 
    262 pub fn main() !void {
    263     var buffer: [1024 * 1024]u8 = undefined;
    264     var fba: std.heap.FixedBufferAllocator = .init(&buffer);
    265     const alloc = fba.allocator();
    266 
    267     var sd_card = try sd.init(alloc);
    268     var fat_vfs: pi.fs.FatVFS = .init();
    269 
    270     var vfs_disk = vfs.Disk.impl_by(&sd_card);
    271     const vfs_fat = vfs.FileSystem.impl_by(&fat_vfs);
    272 
    273     const mbr: pi.fs.mbr.MasterBootRecord = @bitCast(try vfs_disk.read_sector(0));
    274 
    275     try mbr.debug_print(&uart.writer);
    276 
    277     try vfs_fat.open(alloc, &vfs_disk, mbr.pte1.first_lba);
    278     defer vfs_fat.close();
    279 
    280     var explorer = try Explorer.init(alloc, vfs_fat, &uart.writer);
    281 
    282     while (true) {
    283         try explorer.read_line();
    284     }
    285 }