diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 6f6489e80f7fc3a63c25ea971f633c1f1186545b..13438412ce4a01ab5d97848dd2f884126001a269 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -218,6 +218,32 @@ test "Dir.readLink" { }.impl); } +test "Dir.readLink on non-symlinks" { + try testWithAllSupportedPathTypes(struct { + fn impl(ctx: *TestContext) !void { + const file_path = try ctx.transformPath("file.txt"); + try ctx.dir.writeFile(.{ .sub_path = file_path, .data = "nonsense" }); + const dir_path = try ctx.transformPath("subdir"); + try ctx.dir.makeDir(dir_path); + + // file + var buffer: [fs.max_path_bytes]u8 = undefined; + try std.testing.expectError(error.NotLink, ctx.dir.readLink(file_path, &buffer)); + if (builtin.os.tag == .windows) { + var file_path_w = try std.os.windows.sliceToPrefixedFileW(ctx.dir.fd, file_path); + try std.testing.expectError(error.NotLink, ctx.dir.readLinkW(file_path_w.span(), &file_path_w.data)); + } + + // dir + try std.testing.expectError(error.NotLink, ctx.dir.readLink(dir_path, &buffer)); + if (builtin.os.tag == .windows) { + var dir_path_w = try std.os.windows.sliceToPrefixedFileW(ctx.dir.fd, dir_path); + try std.testing.expectError(error.NotLink, ctx.dir.readLinkW(dir_path_w.span(), &dir_path_w.data)); + } + } + }.impl); +} + fn testReadLink(dir: Dir, target_path: []const u8, symlink_path: []const u8) !void { var buffer: [fs.max_path_bytes]u8 = undefined; const actual = try dir.readLink(symlink_path, buffer[0..]); diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index bb908143c10b1bb2fa03e1d6c764fac8b540e93e..5f41a6a1fdcdcfea2cd32b4f8df3840b53ca4b05 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -3125,6 +3125,7 @@ pub const ReadLinkError = error{ BadPathName, AntivirusInterference, UnsupportedReparsePointType, + NotLink, }; /// `sub_path_w` will never be accessed after `out_buffer` has been written to, so it @@ -3155,6 +3156,7 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLi const rc = DeviceIoControl(result_handle, FSCTL.GET_REPARSE_POINT, .{ .out = reparse_buf[0..] }); switch (rc) { .SUCCESS => {}, + .NOT_A_REPARSE_POINT => return error.NotLink, else => return unexpectedStatus(rc), }