diff --git a/src/kernel/filesystem/vfs.zig b/src/kernel/filesystem/vfs.zig index 8a906b04..a46c019f 100644 --- a/src/kernel/filesystem/vfs.zig +++ b/src/kernel/filesystem/vfs.zig @@ -174,6 +174,12 @@ pub const FileSystem = struct { /// The function for retrieving the root node getRootNode: GetRootNode, + initDirIterator: fn (dir: *const DirNode) std.mem.Allocator.Error!void, + + deinitDirIterator: fn (dir: *const DirNode) void, + + listDirIterator: fn (dir: *const DirNode, i: usize) ?[]const u8, + /// Points to a usize field within the underlying filesystem so that the close, read, write and open functions can access its low-level implementation using @fieldParentPtr. For example, this could point to a usize field within a FAT32 filesystem data structure, which stores all the data and state that is needed in order to interact with a physical disk /// The value of instance is reserved for future use and so should be left as 0 instance: *usize, @@ -209,6 +215,27 @@ pub const DirNode = struct { /// The directory that this directory is mounted to, else null mount: ?*const DirNode, + const DirIterator = struct { + dir: *const DirNode, + i: usize, + + const Self = @This(); + + pub fn init(node: *const DirNode) std.mem.Allocator.Error!DirIterator { + try node.fs.initDirIterator(node); + return DirIterator{ .dir = node, .i = 0 }; + } + + pub fn next(self: *Self) ?[]const u8 { + self.i += 1; + return self.dir.fs.listDirIterator(dir, self.i - 1); + } + + pub fn deinit(self: *Self) void { + return self.dir.fs.deinitDirIterator(self.dir); + } + }; + /// See the documentation for FileSystem.Open pub fn open(self: *const DirNode, name: []const u8, flags: OpenFlags, args: OpenArgs) (Allocator.Error || Error)!*Node { var fs = self.fs; @@ -233,6 +260,10 @@ pub const DirNode = struct { } return fs.close(fs, cast_node); } + + pub fn list(self: *const DirNode) DirIterator { + return DirIterator.init(self.mount orelse self); + } }; pub const SymlinkNode = struct { @@ -588,6 +619,7 @@ const TestFS = struct { fs: *FileSystem, allocator: Allocator, open_count: usize, + dir_iter_dummy_memory: std.AutoHashMap(*const DirNode, u8[1]), instance: usize, const Self = @This(); @@ -716,6 +748,25 @@ const TestFS = struct { } return Error.NoSuchFileOrDir; } + + pub fn initDirIterator(node: *const DirNode) std.mem.Allocator.Error!void { + // Allocate some dummy memory to test initialisation and deinitialisation + var test_fs = @fieldParentPtr(TestFS, "instance", node.fs.instance); + if (test_fs.dir_iter_dummy_memory.get(node) == null) try test_fs.dir_iter_dummy_memory.put(node, test_fs.allocator.alloc(u8, 1)); + } + + pub fn deinitDirIterator(node: *const DirNode) void { + // De-allocate the dummy memory allocated to test initialisation and deinitialisation + var test_fs = @fieldParentPtr(TestFS, "instance", node.fs.instance); + test_fs.allocator.free(test_fs.dir_iter_dummy_memory.get(node) orelse unreachable); + } + + pub fn listDirIterator(node: *const DirNode, i: usize) ?[]const u8 { + var test_fs = @fieldParentPtr(TestFS, "instance", node.fs.instance); + const tree_node = (try getTreeNode(test_fs, node)) orelse unreachable; + if (tree_node.children.items.len <= i) return null; + return tree_node.children.items[i].name; + } }; fn testInitFs(allocator: Allocator) !*TestFS { @@ -745,6 +796,9 @@ fn testInitFs(allocator: Allocator) !*TestFS { .write = TestFS.write, .instance = &testfs.instance, .getRootNode = TestFS.getRootNode, + .initDirIterator = TestFS.initDirIterator, + .deinitDirIterator = TestFS.deinitDirIterator, + .listDirIterator = TestFS.listDirIterator, }; return testfs; } @@ -1009,3 +1063,40 @@ test "write" { _ = try test_file.write(str2); try testing.expect(std.mem.eql(u8, str2, f_data.* orelse unreachable)); } + +test "list" { + var testfs = try testInitFs(testing.allocator); + defer testing.allocator.destroy(testfs); + defer testfs.deinit(); + root = testfs.tree.val; + + const child_1_name = "child1.txt"; + const child_2_name = "child2.txt"; + const child_3_name = "child3"; + const child_3_1_name = "child3_1.txt"; + const child_3_2_name = "child3_2.txt"; + + const child_1 = try openFile("/" ++ child_1_name, .CREATE_FILE); + const child_2 = try openFile("/" ++ child_2_name, .CREATE_FILE); + const child_3 = try openDir("/" ++ child_3_name, .CREATE_DIR); + + var iterator = root.Dir.list(); + defer iterator.deinit(); + testing.expectEqualSlices(child_1_name, iterator.next()); + testing.expectEqualSlices(child_2_name, iterator.next()); + testing.expectEqualSlices(child_3_name, iterator.next()); + testing.expectEqualSlices(null, iterator.next()); + + var iterator2 = child_3.list(); + defer iterator2.deinit(); + testing.expectEqualSlices(null, iterator2.next()); + + const child_3_1 = try openDir("/" ++ child_3_name ++ "/" ++ child_3_1_name, .CREATE_DIR); + testing.expectEqualSlices(child_3_1_name, iterator2.next()); + testing.expectEqualSlices(null, iterator2.next()); + + const child_3_2 = try openDir("/" ++ child_3_name ++ "/" ++ child_3_2_name, .CREATE_DIR); + testing.expectEqualSlices(child_3_1_name, iterator2.next()); + testing.expectEqualSlices(child_3_2_name, iterator2.next()); + testing.expectEqualSlices(null, iterator2.next()); +}