SymlinkNode + umount + closeing dir path
Added a symlink node so can close a open symlink. Added umount to can unmount a directory. Closes open directories while traversing the path. Closes #277
This commit is contained in:
parent
d8cb4a5a77
commit
d32c52b13b
2 changed files with 84 additions and 40 deletions
|
@ -1182,10 +1182,7 @@ pub fn Fat32FS(comptime StreamType: type) type {
|
||||||
node.* = switch (flags) {
|
node.* = switch (flags) {
|
||||||
.CREATE_DIR => .{ .Dir = .{ .fs = self.fs, .mount = null } },
|
.CREATE_DIR => .{ .Dir = .{ .fs = self.fs, .mount = null } },
|
||||||
.CREATE_FILE => .{ .File = .{ .fs = self.fs } },
|
.CREATE_FILE => .{ .File = .{ .fs = self.fs } },
|
||||||
.CREATE_SYMLINK => if (open_args.symlink_target) |target|
|
.CREATE_SYMLINK => return vfs.Error.InvalidFlags,
|
||||||
.{ .Symlink = target }
|
|
||||||
else
|
|
||||||
return vfs.Error.NoSymlinkTarget,
|
|
||||||
.NO_CREATION => unreachable,
|
.NO_CREATION => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub const Node = union(enum) {
|
||||||
/// The dir node if this represents a directory
|
/// The dir node if this represents a directory
|
||||||
Dir: DirNode,
|
Dir: DirNode,
|
||||||
/// The absolute path that this symlink is linked to
|
/// The absolute path that this symlink is linked to
|
||||||
Symlink: []const u8,
|
Symlink: SymlinkNode,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
|
@ -224,12 +224,28 @@ pub const DirNode = struct {
|
||||||
pub fn close(self: *const DirNode) void {
|
pub fn close(self: *const DirNode) void {
|
||||||
var fs = self.fs;
|
var fs = self.fs;
|
||||||
var node = self;
|
var node = self;
|
||||||
if (self.mount) |mnt| {
|
|
||||||
fs = mnt.fs;
|
|
||||||
node = mnt;
|
|
||||||
}
|
|
||||||
// TODO: Use @fieldParentPtr() once implemented for unions
|
// TODO: Use @fieldParentPtr() once implemented for unions
|
||||||
return fs.close(fs, @ptrCast(*const Node, node));
|
const cast_node = @ptrCast(*const Node, node);
|
||||||
|
// Can't close the root node
|
||||||
|
if (cast_node == root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return fs.close(fs, cast_node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const SymlinkNode = struct {
|
||||||
|
/// The filesystem that handles operations on this directory
|
||||||
|
fs: *const FileSystem,
|
||||||
|
|
||||||
|
/// The absolute path that this symlink is linked to
|
||||||
|
path: []const u8,
|
||||||
|
|
||||||
|
/// See the documentation for FileSystem.Close
|
||||||
|
pub fn close(self: *const SymlinkNode) void {
|
||||||
|
// TODO: Use @fieldParentPtr() once implemented for unions
|
||||||
|
return self.fs.close(self.fs, @ptrCast(*const Node, self));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -316,13 +332,16 @@ fn traversePath(path: []const u8, follow_symlinks: bool, flags: OpenFlags, args:
|
||||||
.File => Error.NotADirectory,
|
.File => Error.NotADirectory,
|
||||||
.Dir => |*dir| blk: {
|
.Dir => |*dir| blk: {
|
||||||
var child = try dir.open(seg, rec_flags, .{});
|
var child = try dir.open(seg, rec_flags, .{});
|
||||||
|
defer dir.close();
|
||||||
// If the segment refers to a symlink, redirect to the node it represents instead
|
// If the segment refers to a symlink, redirect to the node it represents instead
|
||||||
if (child.isSymlink() and follow_links) {
|
if (child.isSymlink() and follow_links) {
|
||||||
child = try traversePath(child.Symlink, follow_links, rec_flags, .{});
|
const new_child = try traversePath(child.Symlink.path, follow_links, rec_flags, .{});
|
||||||
|
child.Symlink.close();
|
||||||
|
child = new_child;
|
||||||
}
|
}
|
||||||
break :blk try func(split, child, follow_links, rec_flags);
|
break :blk try func(split, child, follow_links, rec_flags);
|
||||||
},
|
},
|
||||||
.Symlink => |target| if (follow_links) try func(split, try traversePath(target, follow_links, .NO_CREATION, .{}), follow_links, rec_flags) else Error.NotADirectory,
|
.Symlink => |target| if (follow_links) try func(split, try traversePath(target.path, follow_links, .NO_CREATION, .{}), follow_links, rec_flags) else Error.NotADirectory,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -343,12 +362,15 @@ fn traversePath(path: []const u8, follow_symlinks: bool, flags: OpenFlags, args:
|
||||||
file.close();
|
file.close();
|
||||||
break :blk Error.NotADirectory;
|
break :blk Error.NotADirectory;
|
||||||
},
|
},
|
||||||
.Symlink => |target| if (follow_symlinks) try traversePath(target, follow_symlinks, .NO_CREATION, .{}) else result.parent,
|
.Symlink => |target| if (follow_symlinks) try traversePath(target.path, follow_symlinks, .NO_CREATION, .{}) else result.parent,
|
||||||
.Dir => |*dir| blk: {
|
.Dir => |*dir| blk: {
|
||||||
var n = try dir.open(result.child, flags, args);
|
var n = try dir.open(result.child, flags, args);
|
||||||
|
defer dir.close();
|
||||||
if (n.isSymlink() and follow_symlinks) {
|
if (n.isSymlink() and follow_symlinks) {
|
||||||
// If the child is a symnlink and we're following them, find the node it refers to
|
// If the child is a symlink and we're following them, find the node it refers to
|
||||||
n = try traversePath(n.Symlink, follow_symlinks, flags, args);
|
const new_n = try traversePath(n.Symlink.path, follow_symlinks, flags, args);
|
||||||
|
n.Symlink.close();
|
||||||
|
n = new_n;
|
||||||
}
|
}
|
||||||
break :blk n;
|
break :blk n;
|
||||||
},
|
},
|
||||||
|
@ -372,6 +394,16 @@ pub fn mount(dir: *DirNode, fs: *const FileSystem) MountError!void {
|
||||||
dir.mount = fs.getRootNode(fs);
|
dir.mount = fs.getRootNode(fs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Unmount the filesystem attached to a directory.
|
||||||
|
///
|
||||||
|
/// Arguments:
|
||||||
|
/// IN dir: *DirNode - The directory to unmount from.
|
||||||
|
///
|
||||||
|
pub fn umount(dir: *DirNode) void {
|
||||||
|
dir.mount = null;
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Open a node at a path.
|
/// Open a node at a path.
|
||||||
///
|
///
|
||||||
|
@ -419,8 +451,11 @@ pub fn openFile(path: []const u8, flags: OpenFlags) (Allocator.Error || Error)!*
|
||||||
var node = try open(path, true, flags, .{});
|
var node = try open(path, true, flags, .{});
|
||||||
return switch (node.*) {
|
return switch (node.*) {
|
||||||
.File => &node.File,
|
.File => &node.File,
|
||||||
.Dir => Error.IsADirectory,
|
.Dir => |*dir| blk: {
|
||||||
// We instructed open to folow symlinks above, so this is impossible
|
dir.close();
|
||||||
|
break :blk Error.IsADirectory;
|
||||||
|
},
|
||||||
|
// We instructed open to follow symlinks above, so this is impossible
|
||||||
.Symlink => unreachable,
|
.Symlink => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -440,7 +475,7 @@ pub fn openFile(path: []const u8, flags: OpenFlags) (Allocator.Error || Error)!*
|
||||||
/// Error.InvalidFlags - The flags were a value invalid when opening files
|
/// Error.InvalidFlags - The flags were a value invalid when opening files
|
||||||
/// Error.NotADirectory - A segment within the path which is not at the end does not correspond to a directory
|
/// Error.NotADirectory - A segment within the path which is not at the end does not correspond to a directory
|
||||||
/// Error.NoSuchFileOrDir - The file/dir at the end of the path doesn't exist and the flags didn't specify to create it
|
/// Error.NoSuchFileOrDir - The file/dir at the end of the path doesn't exist and the flags didn't specify to create it
|
||||||
/// Error.NotADirectory - The path corresponds to a file rather than a directory
|
/// Error.IsAFile - The path corresponds to a file rather than a directory
|
||||||
///
|
///
|
||||||
pub fn openDir(path: []const u8, flags: OpenFlags) (Allocator.Error || Error)!*DirNode {
|
pub fn openDir(path: []const u8, flags: OpenFlags) (Allocator.Error || Error)!*DirNode {
|
||||||
switch (flags) {
|
switch (flags) {
|
||||||
|
@ -451,7 +486,7 @@ pub fn openDir(path: []const u8, flags: OpenFlags) (Allocator.Error || Error)!*D
|
||||||
return switch (node.*) {
|
return switch (node.*) {
|
||||||
.File => |*file| blk: {
|
.File => |*file| blk: {
|
||||||
file.close();
|
file.close();
|
||||||
break :blk Error.NotADirectory;
|
break :blk Error.IsAFile;
|
||||||
},
|
},
|
||||||
// We instructed open to folow symlinks above, so this is impossible
|
// We instructed open to folow symlinks above, so this is impossible
|
||||||
.Symlink => unreachable,
|
.Symlink => unreachable,
|
||||||
|
@ -485,9 +520,15 @@ pub fn openSymlink(path: []const u8, target: ?[]const u8, flags: OpenFlags) (All
|
||||||
}
|
}
|
||||||
var node = try open(path, false, flags, .{ .symlink_target = target });
|
var node = try open(path, false, flags, .{ .symlink_target = target });
|
||||||
return switch (node.*) {
|
return switch (node.*) {
|
||||||
.Symlink => |t| t,
|
.Symlink => |t| t.path,
|
||||||
.File => Error.IsAFile,
|
.File => |*file| blk: {
|
||||||
.Dir => Error.IsADirectory,
|
file.close();
|
||||||
|
break :blk Error.IsAFile;
|
||||||
|
},
|
||||||
|
.Dir => |*dir| blk: {
|
||||||
|
dir.close();
|
||||||
|
break :blk Error.IsADirectory;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -540,7 +581,7 @@ const TestFS = struct {
|
||||||
tree: TreeNode,
|
tree: TreeNode,
|
||||||
fs: *FileSystem,
|
fs: *FileSystem,
|
||||||
allocator: *Allocator,
|
allocator: *Allocator,
|
||||||
open_files_count: usize,
|
open_count: usize,
|
||||||
instance: usize,
|
instance: usize,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
@ -589,7 +630,7 @@ const TestFS = struct {
|
||||||
|
|
||||||
fn close(fs: *const FileSystem, node: *const Node) void {
|
fn close(fs: *const FileSystem, node: *const Node) void {
|
||||||
var test_fs = @fieldParentPtr(TestFS, "instance", fs.instance);
|
var test_fs = @fieldParentPtr(TestFS, "instance", fs.instance);
|
||||||
test_fs.open_files_count -= 1;
|
test_fs.open_count -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read(fs: *const FileSystem, node: *const FileNode, bytes: []u8) (Allocator.Error || Error)!usize {
|
fn read(fs: *const FileSystem, node: *const FileNode, bytes: []u8) (Allocator.Error || Error)!usize {
|
||||||
|
@ -619,10 +660,8 @@ const TestFS = struct {
|
||||||
// Check if the children match the file wanted
|
// Check if the children match the file wanted
|
||||||
for (parent.children.items) |child| {
|
for (parent.children.items) |child| {
|
||||||
if (std.mem.eql(u8, child.name, name)) {
|
if (std.mem.eql(u8, child.name, name)) {
|
||||||
// Increment the open files count
|
// Increment the open count
|
||||||
if (child.val.isFile()) {
|
test_fs.open_count += 1;
|
||||||
test_fs.open_files_count += 1;
|
|
||||||
}
|
|
||||||
return child.val;
|
return child.val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -643,7 +682,7 @@ const TestFS = struct {
|
||||||
.CREATE_SYMLINK => {
|
.CREATE_SYMLINK => {
|
||||||
if (args.symlink_target) |target| {
|
if (args.symlink_target) |target| {
|
||||||
child = try test_fs.allocator.create(Node);
|
child = try test_fs.allocator.create(Node);
|
||||||
child.* = .{ .Symlink = target };
|
child.* = .{ .Symlink = .{ .fs = test_fs.fs, .path = target } };
|
||||||
} else {
|
} else {
|
||||||
return Error.NoSymlinkTarget;
|
return Error.NoSymlinkTarget;
|
||||||
}
|
}
|
||||||
|
@ -663,10 +702,8 @@ const TestFS = struct {
|
||||||
child_tree.children.* = ArrayList(*TreeNode).init(test_fs.allocator);
|
child_tree.children.* = ArrayList(*TreeNode).init(test_fs.allocator);
|
||||||
// Add it to the tree
|
// Add it to the tree
|
||||||
try parent.children.append(child_tree);
|
try parent.children.append(child_tree);
|
||||||
// Increment the open files count
|
// Increment the open count
|
||||||
if (child.isFile()) {
|
test_fs.open_count += 1;
|
||||||
test_fs.open_files_count += 1;
|
|
||||||
}
|
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
return Error.NoSuchFileOrDir;
|
return Error.NoSuchFileOrDir;
|
||||||
|
@ -689,7 +726,7 @@ fn testInitFs(allocator: *Allocator) !*TestFS {
|
||||||
},
|
},
|
||||||
.fs = fs,
|
.fs = fs,
|
||||||
.instance = 123,
|
.instance = 123,
|
||||||
.open_files_count = 0,
|
.open_count = 0,
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
};
|
};
|
||||||
testfs.tree.children.* = ArrayList(*TestFS.TreeNode).init(allocator);
|
testfs.tree.children.* = ArrayList(*TestFS.TreeNode).init(allocator);
|
||||||
|
@ -711,6 +748,7 @@ test "mount" {
|
||||||
var testfs = try testInitFs(allocator);
|
var testfs = try testInitFs(allocator);
|
||||||
defer allocator.destroy(testfs);
|
defer allocator.destroy(testfs);
|
||||||
defer testfs.deinit();
|
defer testfs.deinit();
|
||||||
|
defer testing.expectEqual(testfs.open_count, 0);
|
||||||
|
|
||||||
testfs.instance = 1;
|
testfs.instance = 1;
|
||||||
root = testfs.tree.val;
|
root = testfs.tree.val;
|
||||||
|
@ -719,11 +757,14 @@ test "mount" {
|
||||||
var testfs2 = try testInitFs(allocator);
|
var testfs2 = try testInitFs(allocator);
|
||||||
defer allocator.destroy(testfs2);
|
defer allocator.destroy(testfs2);
|
||||||
defer testfs2.deinit();
|
defer testfs2.deinit();
|
||||||
|
defer testing.expectEqual(testfs2.open_count, 0);
|
||||||
|
|
||||||
testfs2.instance = 2;
|
testfs2.instance = 2;
|
||||||
// Create the dir to mount to
|
// Create the dir to mount to
|
||||||
var dir = try openDir("/mnt", .CREATE_DIR);
|
var dir = try openDir("/mnt", .CREATE_DIR);
|
||||||
|
defer dir.close();
|
||||||
try mount(dir, testfs2.fs);
|
try mount(dir, testfs2.fs);
|
||||||
|
defer umount(dir);
|
||||||
testing.expectError(MountError.DirAlreadyMounted, mount(dir, testfs2.fs));
|
testing.expectError(MountError.DirAlreadyMounted, mount(dir, testfs2.fs));
|
||||||
|
|
||||||
// Ensure the mount worked
|
// Ensure the mount worked
|
||||||
|
@ -731,6 +772,7 @@ test "mount" {
|
||||||
testing.expectEqual((dir.mount orelse unreachable).fs, testfs2.fs);
|
testing.expectEqual((dir.mount orelse unreachable).fs, testfs2.fs);
|
||||||
// Create a file within the mounted directory
|
// Create a file within the mounted directory
|
||||||
var test_file = try openFile("/mnt/123.txt", .CREATE_FILE);
|
var test_file = try openFile("/mnt/123.txt", .CREATE_FILE);
|
||||||
|
defer test_file.close();
|
||||||
testing.expectEqual(@ptrCast(*const FileSystem, testfs2.fs), test_file.fs);
|
testing.expectEqual(@ptrCast(*const FileSystem, testfs2.fs), test_file.fs);
|
||||||
// This shouldn't be in the root fs
|
// This shouldn't be in the root fs
|
||||||
testing.expectEqual(@as(usize, 1), testfs.tree.children.items.len);
|
testing.expectEqual(@as(usize, 1), testfs.tree.children.items.len);
|
||||||
|
@ -760,14 +802,16 @@ test "traversePath" {
|
||||||
|
|
||||||
// Same but with a directory
|
// Same but with a directory
|
||||||
var child2 = try test_root.Dir.open("child2", .CREATE_DIR, .{});
|
var child2 = try test_root.Dir.open("child2", .CREATE_DIR, .{});
|
||||||
testing.expectEqual(child2, try traversePath("/child2", false, .NO_CREATION, .{}));
|
const child2_traversed = try traversePath("/child2", false, .NO_CREATION, .{});
|
||||||
|
testing.expectEqual(child2, child2_traversed);
|
||||||
|
|
||||||
// Again but with a file within that directory
|
// Again but with a file within that directory
|
||||||
var child3 = try child2.Dir.open("child3.txt", .CREATE_FILE, .{});
|
var child3 = try child2.Dir.open("child3.txt", .CREATE_FILE, .{});
|
||||||
var child3_traversed = try traversePath("/child2/child3.txt", false, .NO_CREATION, .{});
|
var child3_traversed = try traversePath("/child2/child3.txt", false, .NO_CREATION, .{});
|
||||||
testing.expectEqual(child3, child3_traversed);
|
testing.expectEqual(child3, child3_traversed);
|
||||||
// Close the open files
|
// Close the open files
|
||||||
child3.File.close();
|
child2.Dir.close();
|
||||||
|
child2_traversed.Dir.close();
|
||||||
child3_traversed.File.close();
|
child3_traversed.File.close();
|
||||||
|
|
||||||
// Create and open a symlink
|
// Create and open a symlink
|
||||||
|
@ -777,6 +821,9 @@ test "traversePath" {
|
||||||
var child5 = try traversePath("/child4", false, .CREATE_SYMLINK, .{ .symlink_target = "/child2" });
|
var child5 = try traversePath("/child4", false, .CREATE_SYMLINK, .{ .symlink_target = "/child2" });
|
||||||
var child5_linked = try traversePath("/child4/child3.txt", true, .NO_CREATION, .{});
|
var child5_linked = try traversePath("/child4/child3.txt", true, .NO_CREATION, .{});
|
||||||
testing.expectEqual(child5_linked, child4_linked);
|
testing.expectEqual(child5_linked, child4_linked);
|
||||||
|
child3.File.close();
|
||||||
|
child4.Symlink.close();
|
||||||
|
child5.Symlink.close();
|
||||||
child4_linked.File.close();
|
child4_linked.File.close();
|
||||||
child5_linked.File.close();
|
child5_linked.File.close();
|
||||||
|
|
||||||
|
@ -789,7 +836,7 @@ test "traversePath" {
|
||||||
testing.expectError(Error.NoSymlinkTarget, traversePath("/childX.txt", false, .CREATE_SYMLINK, .{}));
|
testing.expectError(Error.NoSymlinkTarget, traversePath("/childX.txt", false, .CREATE_SYMLINK, .{}));
|
||||||
|
|
||||||
// Since we've closed all the files, the open files count should be zero
|
// Since we've closed all the files, the open files count should be zero
|
||||||
testing.expectEqual(testfs.open_files_count, 0);
|
testing.expectEqual(testfs.open_count, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "isAbsolute" {
|
test "isAbsolute" {
|
||||||
|
@ -808,7 +855,7 @@ test "isDir" {
|
||||||
const fs: FileSystem = undefined;
|
const fs: FileSystem = undefined;
|
||||||
const dir = Node{ .Dir = .{ .fs = &fs, .mount = null } };
|
const dir = Node{ .Dir = .{ .fs = &fs, .mount = null } };
|
||||||
const file = Node{ .File = .{ .fs = &fs } };
|
const file = Node{ .File = .{ .fs = &fs } };
|
||||||
const symlink = Node{ .Symlink = "" };
|
const symlink = Node{ .Symlink = .{ .fs = &fs, .path = "" } };
|
||||||
testing.expect(!symlink.isDir());
|
testing.expect(!symlink.isDir());
|
||||||
testing.expect(!file.isDir());
|
testing.expect(!file.isDir());
|
||||||
testing.expect(dir.isDir());
|
testing.expect(dir.isDir());
|
||||||
|
@ -818,7 +865,7 @@ test "isFile" {
|
||||||
const fs: FileSystem = undefined;
|
const fs: FileSystem = undefined;
|
||||||
const dir = Node{ .Dir = .{ .fs = &fs, .mount = null } };
|
const dir = Node{ .Dir = .{ .fs = &fs, .mount = null } };
|
||||||
const file = Node{ .File = .{ .fs = &fs } };
|
const file = Node{ .File = .{ .fs = &fs } };
|
||||||
const symlink = Node{ .Symlink = "" };
|
const symlink = Node{ .Symlink = .{ .fs = &fs, .path = "" } };
|
||||||
testing.expect(!dir.isFile());
|
testing.expect(!dir.isFile());
|
||||||
testing.expect(!symlink.isFile());
|
testing.expect(!symlink.isFile());
|
||||||
testing.expect(file.isFile());
|
testing.expect(file.isFile());
|
||||||
|
@ -828,7 +875,7 @@ test "isSymlink" {
|
||||||
const fs: FileSystem = undefined;
|
const fs: FileSystem = undefined;
|
||||||
const dir = Node{ .Dir = .{ .fs = &fs, .mount = null } };
|
const dir = Node{ .Dir = .{ .fs = &fs, .mount = null } };
|
||||||
const file = Node{ .File = .{ .fs = &fs } };
|
const file = Node{ .File = .{ .fs = &fs } };
|
||||||
const symlink = Node{ .Symlink = "" };
|
const symlink = Node{ .Symlink = .{ .fs = &fs, .path = "" } };
|
||||||
testing.expect(!dir.isSymlink());
|
testing.expect(!dir.isSymlink());
|
||||||
testing.expect(!file.isSymlink());
|
testing.expect(!file.isSymlink());
|
||||||
testing.expect(symlink.isSymlink());
|
testing.expect(symlink.isSymlink());
|
||||||
|
|
Loading…
Reference in a new issue