From 70b248497ab87471997c62a515bf4bb4109899b9 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 21 Dec 2023 23:15:12 -0800 Subject: [PATCH] fs: Add tests for deleteTree not following symlinks In theory this is part of https://github.com/ziglang/zig/issues/18335, but these tests already pass since deleteTree does not depend on `OpenDirOptions.no_follow` behavior for these test cases: - `deleteTree` always tries to delete the initial path as a file first, which will succeed on symlinks because `deleteFile` doesn't follow symlinks - `deleteTree` when iterating a directory will get the type of symlinks as .sym_link, not as .directory (even if the symlink points to a directory), meaning it will never try to open a symlink as a directory. --- lib/std/fs/test.zig | 51 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 23eaf06b65..95cacd25d1 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -999,6 +999,57 @@ test "openSelfExe" { self_exe_file.close(); } +test "deleteTree does not follow symlinks" { + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + try tmp.dir.makePath("b"); + { + var a = try tmp.dir.makeOpenPath("a", .{}); + defer a.close(); + + a.symLink("../b", "b", .{ .is_directory = true }) catch |err| switch (err) { + // Symlink requires admin privileges on windows, so this test can legitimately fail. + error.AccessDenied => return error.SkipZigTest, + else => return err, + }; + } + + try tmp.dir.deleteTree("a"); + + try testing.expectError(error.FileNotFound, tmp.dir.access("a", .{})); + try tmp.dir.access("b", .{}); +} + +test "deleteTree on a symlink" { + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + // Symlink to a file + try tmp.dir.writeFile("file", ""); + tmp.dir.symLink("file", "filelink", .{}) catch |err| switch (err) { + // Symlink requires admin privileges on windows, so this test can legitimately fail. + error.AccessDenied => return error.SkipZigTest, + else => return err, + }; + + try tmp.dir.deleteTree("filelink"); + try testing.expectError(error.FileNotFound, tmp.dir.access("filelink", .{})); + try tmp.dir.access("file", .{}); + + // Symlink to a directory + try tmp.dir.makePath("dir"); + tmp.dir.symLink("dir", "dirlink", .{ .is_directory = true }) catch |err| switch (err) { + // Symlink requires admin privileges on windows, so this test can legitimately fail. + error.AccessDenied => return error.SkipZigTest, + else => return err, + }; + + try tmp.dir.deleteTree("dirlink"); + try testing.expectError(error.FileNotFound, tmp.dir.access("dirlink", .{})); + try tmp.dir.access("dir", .{}); +} + test "makePath, put some files in it, deleteTree" { try testWithAllSupportedPathTypes(struct { fn impl(ctx: *TestContext) !void {