Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions src/kernel/filesystem/vfs.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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 {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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());
}