diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index cd6546bc5a..f82eb8cd47 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -1705,6 +1705,15 @@ pub fn symLink( var target_path_w: std.os.windows.PathSpace = undefined; target_path_w.len = try std.unicode.wtf8ToWtf16Le(&target_path_w.data, target_path); target_path_w.data[target_path_w.len] = 0; + // However, we need to canonicalize any path separators to `\`, since if + // the target path is relative, then it must use `\` as the path separator. + mem.replaceScalar( + u16, + target_path_w.data[0..target_path_w.len], + mem.nativeToLittle(u16, '/'), + mem.nativeToLittle(u16, '\\'), + ); + const sym_link_path_w = try std.os.windows.sliceToPrefixedFileW(self.fd, sym_link_path); return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags); } @@ -1744,6 +1753,7 @@ pub fn symLinkW( self: Dir, /// WTF-16, does not need to be NT-prefixed. The NT-prefixing /// of this path is handled by CreateSymbolicLink. + /// Any path separators must be `\`, not `/`. target_path_w: [:0]const u16, /// WTF-16, must be NT-prefixed or relative sym_link_path_w: []const u16, diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig index 33bea3c322..9703c8e07c 100644 --- a/lib/std/fs/test.zig +++ b/lib/std/fs/test.zig @@ -178,6 +178,19 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void try testing.expectEqualStrings(target_path, given); } +test "Dir.symLink with relative target that has a / path separator" { + var tmp = testing.tmpDir(.{}); + defer tmp.cleanup(); + + try tmp.dir.makePath("a"); + try tmp.dir.writeFile("a/file", ""); + try tmp.dir.symLink("a/file", "symlink", .{}); + + const stat = try tmp.dir.statFile("symlink"); + // statFile follows symlinks + try testing.expectEqual(File.Kind.file, stat.kind); +} + test "File.stat on a File that is a symlink returns Kind.sym_link" { // This test requires getting a file descriptor of a symlink which // is not possible on all targets