authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-12 10:38:31-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-12 10:38:31-07:00
log35b85d3ba53fa750abe4b7ca33c9b059354618ab
treea7ddb4497ac58357ba4d25c52466b0712a1ccd38
parent6b06a696dfc77b17886030273314dedab54211c8
parent7e542e788c335b33a2c19f3ec1b453777cad901a
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16783 from squeek502/fs-too-many-parent-dirs

Windows: Fix `TooManyParentDirs` handling for paths that shouldn't be cwd-relative

7 files changed, 133 insertions(+), 72 deletions(-)

lib/std/child_process.zig+1-1
......@@ -961,7 +961,7 @@ fn windowsCreateProcessPathExt(
961961 try dir_buf.append(allocator, 0);
962962 defer dir_buf.shrinkRetainingCapacity(dir_path_len);
963963 const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
964 const prefixed_path = try windows.wToPrefixedFileW(dir_path_z);
964 const prefixed_path = try windows.wToPrefixedFileW(null, dir_path_z);
965965 break :dir fs.cwd().openDirW(prefixed_path.span().ptr, .{}, true) catch return error.FileNotFound;
966966 };
967967 defer dir.close();
lib/std/dynamic_library.zig+2-2
......@@ -317,12 +317,12 @@ pub const WindowsDynLib = struct {
317317 dll: windows.HMODULE,
318318
319319 pub fn open(path: []const u8) !WindowsDynLib {
320 const path_w = try windows.sliceToPrefixedFileW(path);
320 const path_w = try windows.sliceToPrefixedFileW(null, path);
321321 return openW(path_w.span().ptr);
322322 }
323323
324324 pub fn openZ(path_c: [*:0]const u8) !WindowsDynLib {
325 const path_w = try windows.cStrToPrefixedFileW(path_c);
325 const path_w = try windows.cStrToPrefixedFileW(null, path_c);
326326 return openW(path_w.span().ptr);
327327 }
328328
lib/std/fs.zig+28-22
......@@ -1118,7 +1118,7 @@ pub const Dir = struct {
11181118 /// Asserts that the path parameter has no null bytes.
11191119 pub fn openFile(self: Dir, sub_path: []const u8, flags: File.OpenFlags) File.OpenError!File {
11201120 if (builtin.os.tag == .windows) {
1121 const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1121 const path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);
11221122 return self.openFileW(path_w.span(), flags);
11231123 }
11241124 if (builtin.os.tag == .wasi and !builtin.link_libc) {
......@@ -1156,7 +1156,7 @@ pub const Dir = struct {
11561156 /// Same as `openFile` but the path parameter is null-terminated.
11571157 pub fn openFileZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) File.OpenError!File {
11581158 if (builtin.os.tag == .windows) {
1159 const path_w = try os.windows.cStrToPrefixedFileW(sub_path);
1159 const path_w = try os.windows.cStrToPrefixedFileW(self.fd, sub_path);
11601160 return self.openFileW(path_w.span(), flags);
11611161 }
11621162
......@@ -1282,7 +1282,7 @@ pub const Dir = struct {
12821282 /// Asserts that the path parameter has no null bytes.
12831283 pub fn createFile(self: Dir, sub_path: []const u8, flags: File.CreateFlags) File.OpenError!File {
12841284 if (builtin.os.tag == .windows) {
1285 const path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1285 const path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);
12861286 return self.createFileW(path_w.span(), flags);
12871287 }
12881288 if (builtin.os.tag == .wasi and !builtin.link_libc) {
......@@ -1323,7 +1323,7 @@ pub const Dir = struct {
13231323 /// Same as `createFile` but the path parameter is null-terminated.
13241324 pub fn createFileZ(self: Dir, sub_path_c: [*:0]const u8, flags: File.CreateFlags) File.OpenError!File {
13251325 if (builtin.os.tag == .windows) {
1326 const path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
1326 const path_w = try os.windows.cStrToPrefixedFileW(self.fd, sub_path_c);
13271327 return self.createFileW(path_w.span(), flags);
13281328 }
13291329
......@@ -1513,7 +1513,7 @@ pub const Dir = struct {
15131513 @compileError("realpath is not available on WASI");
15141514 }
15151515 if (builtin.os.tag == .windows) {
1516 const pathname_w = try os.windows.sliceToPrefixedFileW(pathname);
1516 const pathname_w = try os.windows.sliceToPrefixedFileW(self.fd, pathname);
15171517 return self.realpathW(pathname_w.span(), out_buffer);
15181518 }
15191519 const pathname_c = try os.toPosixPath(pathname);
......@@ -1524,7 +1524,7 @@ pub const Dir = struct {
15241524 /// See also `Dir.realpath`, `realpathZ`.
15251525 pub fn realpathZ(self: Dir, pathname: [*:0]const u8, out_buffer: []u8) ![]u8 {
15261526 if (builtin.os.tag == .windows) {
1527 const pathname_w = try os.windows.cStrToPrefixedFileW(pathname);
1527 const pathname_w = try os.windows.cStrToPrefixedFileW(self.fd, pathname);
15281528 return self.realpathW(pathname_w.span(), out_buffer);
15291529 }
15301530
......@@ -1646,7 +1646,7 @@ pub const Dir = struct {
16461646 /// Asserts that the path parameter has no null bytes.
16471647 pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
16481648 if (builtin.os.tag == .windows) {
1649 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1649 const sub_path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);
16501650 return self.openDirW(sub_path_w.span().ptr, args, false);
16511651 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
16521652 return self.openDirWasi(sub_path, args);
......@@ -1662,7 +1662,7 @@ pub const Dir = struct {
16621662 /// Asserts that the path parameter has no null bytes.
16631663 pub fn openIterableDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!IterableDir {
16641664 if (builtin.os.tag == .windows) {
1665 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1665 const sub_path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);
16661666 return IterableDir{ .dir = try self.openDirW(sub_path_w.span().ptr, args, true) };
16671667 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
16681668 return IterableDir{ .dir = try self.openDirWasi(sub_path, args) };
......@@ -1722,7 +1722,7 @@ pub const Dir = struct {
17221722 /// Same as `openDir` except the parameter is null-terminated.
17231723 pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions, iterable: bool) OpenError!Dir {
17241724 if (builtin.os.tag == .windows) {
1725 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
1725 const sub_path_w = try os.windows.cStrToPrefixedFileW(self.fd, sub_path_c);
17261726 return self.openDirW(sub_path_w.span().ptr, args, iterable);
17271727 }
17281728 const symlink_flags: u32 = if (args.no_follow) os.O.NOFOLLOW else 0x0;
......@@ -1821,7 +1821,7 @@ pub const Dir = struct {
18211821 /// Asserts that the path parameter has no null bytes.
18221822 pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
18231823 if (builtin.os.tag == .windows) {
1824 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1824 const sub_path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);
18251825 return self.deleteFileW(sub_path_w.span());
18261826 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
18271827 os.unlinkat(self.fd, sub_path, 0) catch |err| switch (err) {
......@@ -1884,7 +1884,7 @@ pub const Dir = struct {
18841884 /// Asserts that the path parameter has no null bytes.
18851885 pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
18861886 if (builtin.os.tag == .windows) {
1887 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1887 const sub_path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);
18881888 return self.deleteDirW(sub_path_w.span());
18891889 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
18901890 os.unlinkat(self.fd, sub_path, os.AT.REMOVEDIR) catch |err| switch (err) {
......@@ -1949,8 +1949,8 @@ pub const Dir = struct {
19491949 return self.symLinkWasi(target_path, sym_link_path, flags);
19501950 }
19511951 if (builtin.os.tag == .windows) {
1952 const target_path_w = try os.windows.sliceToPrefixedFileW(target_path);
1953 const sym_link_path_w = try os.windows.sliceToPrefixedFileW(sym_link_path);
1952 const target_path_w = try os.windows.sliceToPrefixedFileW(self.fd, target_path);
1953 const sym_link_path_w = try os.windows.sliceToPrefixedFileW(self.fd, sym_link_path);
19541954 return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);
19551955 }
19561956 const target_path_c = try os.toPosixPath(target_path);
......@@ -1976,8 +1976,8 @@ pub const Dir = struct {
19761976 flags: SymLinkFlags,
19771977 ) !void {
19781978 if (builtin.os.tag == .windows) {
1979 const target_path_w = try os.windows.cStrToPrefixedFileW(target_path_c);
1980 const sym_link_path_w = try os.windows.cStrToPrefixedFileW(sym_link_path_c);
1979 const target_path_w = try os.windows.cStrToPrefixedFileW(self.fd, target_path_c);
1980 const sym_link_path_w = try os.windows.cStrToPrefixedFileW(self.fd, sym_link_path_c);
19811981 return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);
19821982 }
19831983 return os.symlinkatZ(target_path_c, self.fd, sym_link_path_c);
......@@ -2002,7 +2002,7 @@ pub const Dir = struct {
20022002 return self.readLinkWasi(sub_path, buffer);
20032003 }
20042004 if (builtin.os.tag == .windows) {
2005 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
2005 const sub_path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);
20062006 return self.readLinkW(sub_path_w.span(), buffer);
20072007 }
20082008 const sub_path_c = try os.toPosixPath(sub_path);
......@@ -2017,7 +2017,7 @@ pub const Dir = struct {
20172017 /// Same as `readLink`, except the `pathname` parameter is null-terminated.
20182018 pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
20192019 if (builtin.os.tag == .windows) {
2020 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
2020 const sub_path_w = try os.windows.cStrToPrefixedFileW(self.fd, sub_path_c);
20212021 return self.readLinkW(sub_path_w.span(), buffer);
20222022 }
20232023 return os.readlinkatZ(self.fd, sub_path_c, buffer);
......@@ -2491,7 +2491,10 @@ pub const Dir = struct {
24912491 /// open it and handle the error for file not found.
24922492 pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
24932493 if (builtin.os.tag == .windows) {
2494 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
2494 const sub_path_w = os.windows.sliceToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {
2495 error.AccessDenied => return error.PermissionDenied,
2496 else => |e| return e,
2497 };
24952498 return self.accessW(sub_path_w.span().ptr, flags);
24962499 }
24972500 const path_c = try os.toPosixPath(sub_path);
......@@ -2501,7 +2504,10 @@ pub const Dir = struct {
25012504 /// Same as `access` except the path parameter is null-terminated.
25022505 pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
25032506 if (builtin.os.tag == .windows) {
2504 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path);
2507 const sub_path_w = os.windows.cStrToPrefixedFileW(self.fd, sub_path) catch |err| switch (err) {
2508 error.AccessDenied => return error.PermissionDenied,
2509 else => |e| return e,
2510 };
25052511 return self.accessW(sub_path_w.span().ptr, flags);
25062512 }
25072513 const os_mode = switch (flags.mode) {
......@@ -2884,8 +2890,8 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
28842890 assert(path.isAbsolute(target_path));
28852891 assert(path.isAbsolute(sym_link_path));
28862892 if (builtin.os.tag == .windows) {
2887 const target_path_w = try os.windows.sliceToPrefixedFileW(target_path);
2888 const sym_link_path_w = try os.windows.sliceToPrefixedFileW(sym_link_path);
2893 const target_path_w = try os.windows.sliceToPrefixedFileW(null, target_path);
2894 const sym_link_path_w = try os.windows.sliceToPrefixedFileW(null, sym_link_path);
28892895 return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
28902896 }
28912897 return os.symlink(target_path, sym_link_path);
......@@ -2935,7 +2941,7 @@ pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
29352941 }
29362942 if (builtin.os.tag == .windows) {
29372943 const wide_slice = selfExePathW();
2938 const prefixed_path_w = try os.windows.wToPrefixedFileW(wide_slice);
2944 const prefixed_path_w = try os.windows.wToPrefixedFileW(null, wide_slice);
29392945 return cwd().openFileW(prefixed_path_w.span(), flags);
29402946 }
29412947 // Use of MAX_PATH_BYTES here is valid as the resulting path is immediately
lib/std/fs/test.zig+21
......@@ -101,6 +101,27 @@ test "openDir cwd parent .." {
101101 defer dir.close();
102102}
103103
104test "openDir non-cwd parent .." {
105 if (builtin.os.tag == .wasi) return error.SkipZigTest;
106
107 var tmp = tmpDir(.{});
108 defer tmp.cleanup();
109
110 var subdir = try tmp.dir.makeOpenPath("subdir", .{});
111 defer subdir.close();
112
113 var dir = try subdir.openDir("..", .{});
114 defer dir.close();
115
116 const expected_path = try tmp.dir.realpathAlloc(testing.allocator, ".");
117 defer testing.allocator.free(expected_path);
118
119 const actual_path = try dir.realpathAlloc(testing.allocator, ".");
120 defer testing.allocator.free(actual_path);
121
122 try testing.expectEqualStrings(expected_path, actual_path);
123}
124
104125test "readLinkAbsolute" {
105126 if (builtin.os.tag == .wasi) return error.SkipZigTest;
106127
lib/std/os.zig+37-31
......@@ -1464,7 +1464,7 @@ pub const OpenError = error{
14641464/// See also `openZ`.
14651465pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t {
14661466 if (builtin.os.tag == .windows) {
1467 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1467 const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
14681468 return openW(file_path_w.span(), flags, perm);
14691469 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
14701470 return openat(wasi.AT.FDCWD, file_path, flags, perm);
......@@ -1477,7 +1477,7 @@ pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t {
14771477/// See also `open`.
14781478pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t {
14791479 if (builtin.os.tag == .windows) {
1480 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
1480 const file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
14811481 return openW(file_path_w.span(), flags, perm);
14821482 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
14831483 return open(mem.sliceTo(file_path, 0), flags, perm);
......@@ -1568,7 +1568,7 @@ pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t
15681568/// See also `openatZ`.
15691569pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
15701570 if (builtin.os.tag == .windows) {
1571 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
1571 const file_path_w = try windows.sliceToPrefixedFileW(dir_fd, file_path);
15721572 return openatW(dir_fd, file_path_w.span(), flags, mode);
15731573 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
15741574 // `mode` is ignored on WASI, which does not support unix-style file permissions
......@@ -1690,7 +1690,7 @@ pub fn openatWasi(
16901690/// See also `openat`.
16911691pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
16921692 if (builtin.os.tag == .windows) {
1693 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
1693 const file_path_w = try windows.cStrToPrefixedFileW(dir_fd, file_path);
16941694 return openatW(dir_fd, file_path_w.span(), flags, mode);
16951695 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
16961696 return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode);
......@@ -2305,7 +2305,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {
23052305 else => |e| return e,
23062306 };
23072307 } else if (builtin.os.tag == .windows) {
2308 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
2308 const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
23092309 return unlinkW(file_path_w.span());
23102310 } else {
23112311 const file_path_c = try toPosixPath(file_path);
......@@ -2316,7 +2316,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {
23162316/// Same as `unlink` except the parameter is a null terminated UTF8-encoded string.
23172317pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
23182318 if (builtin.os.tag == .windows) {
2319 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
2319 const file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
23202320 return unlinkW(file_path_w.span());
23212321 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
23222322 return unlink(mem.sliceTo(file_path, 0));
......@@ -2354,7 +2354,7 @@ pub const UnlinkatError = UnlinkError || error{
23542354/// Asserts that the path parameter has no null bytes.
23552355pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
23562356 if (builtin.os.tag == .windows) {
2357 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
2357 const file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
23582358 return unlinkatW(dirfd, file_path_w.span(), flags);
23592359 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
23602360 return unlinkatWasi(dirfd, file_path, flags);
......@@ -2399,7 +2399,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro
23992399/// Same as `unlinkat` but `file_path` is a null-terminated string.
24002400pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
24012401 if (builtin.os.tag == .windows) {
2402 const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
2402 const file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path_c);
24032403 return unlinkatW(dirfd, file_path_w.span(), flags);
24042404 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
24052405 return unlinkat(dirfd, mem.sliceTo(file_path_c, 0), flags);
......@@ -2468,8 +2468,8 @@ pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
24682468 if (builtin.os.tag == .wasi and !builtin.link_libc) {
24692469 return renameat(wasi.AT.FDCWD, old_path, wasi.AT.FDCWD, new_path);
24702470 } else if (builtin.os.tag == .windows) {
2471 const old_path_w = try windows.sliceToPrefixedFileW(old_path);
2472 const new_path_w = try windows.sliceToPrefixedFileW(new_path);
2471 const old_path_w = try windows.sliceToPrefixedFileW(null, old_path);
2472 const new_path_w = try windows.sliceToPrefixedFileW(null, new_path);
24732473 return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
24742474 } else {
24752475 const old_path_c = try toPosixPath(old_path);
......@@ -2481,8 +2481,8 @@ pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
24812481/// Same as `rename` except the parameters are null-terminated byte arrays.
24822482pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void {
24832483 if (builtin.os.tag == .windows) {
2484 const old_path_w = try windows.cStrToPrefixedFileW(old_path);
2485 const new_path_w = try windows.cStrToPrefixedFileW(new_path);
2484 const old_path_w = try windows.cStrToPrefixedFileW(null, old_path);
2485 const new_path_w = try windows.cStrToPrefixedFileW(null, new_path);
24862486 return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
24872487 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
24882488 return rename(mem.sliceTo(old_path, 0), mem.sliceTo(new_path, 0));
......@@ -2526,8 +2526,8 @@ pub fn renameat(
25262526 new_path: []const u8,
25272527) RenameError!void {
25282528 if (builtin.os.tag == .windows) {
2529 const old_path_w = try windows.sliceToPrefixedFileW(old_path);
2530 const new_path_w = try windows.sliceToPrefixedFileW(new_path);
2529 const old_path_w = try windows.sliceToPrefixedFileW(old_dir_fd, old_path);
2530 const new_path_w = try windows.sliceToPrefixedFileW(new_dir_fd, new_path);
25312531 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
25322532 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
25332533 const old: RelativePathWasi = .{ .dir_fd = old_dir_fd, .relative_path = old_path };
......@@ -2576,8 +2576,8 @@ pub fn renameatZ(
25762576 new_path: [*:0]const u8,
25772577) RenameError!void {
25782578 if (builtin.os.tag == .windows) {
2579 const old_path_w = try windows.cStrToPrefixedFileW(old_path);
2580 const new_path_w = try windows.cStrToPrefixedFileW(new_path);
2579 const old_path_w = try windows.cStrToPrefixedFileW(old_dir_fd, old_path);
2580 const new_path_w = try windows.cStrToPrefixedFileW(new_dir_fd, new_path);
25812581 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
25822582 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
25832583 return renameat(old_dir_fd, mem.sliceTo(old_path, 0), new_dir_fd, mem.sliceTo(new_path, 0));
......@@ -2670,7 +2670,7 @@ pub fn renameatW(
26702670
26712671pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
26722672 if (builtin.os.tag == .windows) {
2673 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
2673 const sub_dir_path_w = try windows.sliceToPrefixedFileW(dir_fd, sub_dir_path);
26742674 return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
26752675 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
26762676 return mkdiratWasi(dir_fd, sub_dir_path, mode);
......@@ -2705,7 +2705,7 @@ pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirErr
27052705
27062706pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
27072707 if (builtin.os.tag == .windows) {
2708 const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path);
2708 const sub_dir_path_w = try windows.cStrToPrefixedFileW(dir_fd, sub_dir_path);
27092709 return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);
27102710 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
27112711 return mkdirat(dir_fd, mem.sliceTo(sub_dir_path, 0), mode);
......@@ -2776,7 +2776,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
27762776 if (builtin.os.tag == .wasi and !builtin.link_libc) {
27772777 return mkdirat(wasi.AT.FDCWD, dir_path, mode);
27782778 } else if (builtin.os.tag == .windows) {
2779 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
2779 const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
27802780 return mkdirW(dir_path_w.span(), mode);
27812781 } else {
27822782 const dir_path_c = try toPosixPath(dir_path);
......@@ -2787,7 +2787,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
27872787/// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
27882788pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
27892789 if (builtin.os.tag == .windows) {
2790 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
2790 const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path);
27912791 return mkdirW(dir_path_w.span(), mode);
27922792 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
27932793 return mkdir(mem.sliceTo(dir_path, 0), mode);
......@@ -2854,7 +2854,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
28542854 else => |e| return e,
28552855 };
28562856 } else if (builtin.os.tag == .windows) {
2857 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
2857 const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
28582858 return rmdirW(dir_path_w.span());
28592859 } else {
28602860 const dir_path_c = try toPosixPath(dir_path);
......@@ -2865,7 +2865,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
28652865/// Same as `rmdir` except the parameter is null-terminated.
28662866pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
28672867 if (builtin.os.tag == .windows) {
2868 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
2868 const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path);
28692869 return rmdirW(dir_path_w.span());
28702870 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
28712871 return rmdir(mem.sliceTo(dir_path, 0));
......@@ -3003,7 +3003,7 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
30033003 if (builtin.os.tag == .wasi and !builtin.link_libc) {
30043004 return readlinkat(wasi.AT.FDCWD, file_path, out_buffer);
30053005 } else if (builtin.os.tag == .windows) {
3006 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
3006 const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
30073007 return readlinkW(file_path_w.span(), out_buffer);
30083008 } else {
30093009 const file_path_c = try toPosixPath(file_path);
......@@ -3049,7 +3049,7 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink
30493049 return readlinkatWasi(dirfd, file_path, out_buffer);
30503050 }
30513051 if (builtin.os.tag == .windows) {
3052 const file_path_w = try windows.sliceToPrefixedFileW(file_path);
3052 const file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
30533053 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
30543054 }
30553055 const file_path_c = try toPosixPath(file_path);
......@@ -3086,7 +3086,7 @@ pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLi
30863086/// See also `readlinkat`.
30873087pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
30883088 if (builtin.os.tag == .windows) {
3089 const file_path_w = try windows.cStrToPrefixedFileW(file_path);
3089 const file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path);
30903090 return readlinkatW(dirfd, file_path_w.span(), out_buffer);
30913091 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
30923092 return readlinkat(dirfd, mem.sliceTo(file_path, 0), out_buffer);
......@@ -4443,7 +4443,10 @@ pub const AccessError = error{
44434443/// TODO currently this assumes `mode` is `F.OK` on Windows.
44444444pub fn access(path: []const u8, mode: u32) AccessError!void {
44454445 if (builtin.os.tag == .windows) {
4446 const path_w = try windows.sliceToPrefixedFileW(path);
4446 const path_w = windows.sliceToPrefixedFileW(null, path) catch |err| switch (err) {
4447 error.AccessDenied => return error.PermissionDenied,
4448 else => |e| return e,
4449 };
44474450 _ = try windows.GetFileAttributesW(path_w.span().ptr);
44484451 return;
44494452 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
......@@ -4456,7 +4459,10 @@ pub fn access(path: []const u8, mode: u32) AccessError!void {
44564459/// Same as `access` except `path` is null-terminated.
44574460pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
44584461 if (builtin.os.tag == .windows) {
4459 const path_w = try windows.cStrToPrefixedFileW(path);
4462 const path_w = windows.cStrToPrefixedFileW(null, path) catch |err| switch (err) {
4463 error.AccessDenied => return error.PermissionDenied,
4464 else => |e| return e,
4465 };
44604466 _ = try windows.GetFileAttributesW(path_w.span().ptr);
44614467 return;
44624468 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
......@@ -4500,7 +4506,7 @@ pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!v
45004506/// TODO currently this ignores `mode` and `flags` on Windows.
45014507pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
45024508 if (builtin.os.tag == .windows) {
4503 const path_w = try windows.sliceToPrefixedFileW(path);
4509 const path_w = try windows.sliceToPrefixedFileW(dirfd, path);
45044510 return faccessatW(dirfd, path_w.span().ptr, mode, flags);
45054511 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
45064512 var resolved = RelativePathWasi{ .dir_fd = dirfd, .relative_path = path };
......@@ -4543,7 +4549,7 @@ pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessErr
45434549/// Same as `faccessat` except the path parameter is null-terminated.
45444550pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
45454551 if (builtin.os.tag == .windows) {
4546 const path_w = try windows.cStrToPrefixedFileW(path);
4552 const path_w = try windows.cStrToPrefixedFileW(dirfd, path);
45474553 return faccessatW(dirfd, path_w.span().ptr, mode, flags);
45484554 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
45494555 return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
......@@ -5079,7 +5085,7 @@ pub const RealPathError = error{
50795085/// See also `realpathZ` and `realpathW`.
50805086pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
50815087 if (builtin.os.tag == .windows) {
5082 const pathname_w = try windows.sliceToPrefixedFileW(pathname);
5088 const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
50835089 return realpathW(pathname_w.span(), out_buffer);
50845090 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
50855091 @compileError("WASI does not support os.realpath");
......@@ -5091,7 +5097,7 @@ pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathE
50915097/// Same as `realpath` except `pathname` is null-terminated.
50925098pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
50935099 if (builtin.os.tag == .windows) {
5094 const pathname_w = try windows.cStrToPrefixedFileW(pathname);
5100 const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
50955101 return realpathW(pathname_w.span(), out_buffer);
50965102 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
50975103 return realpath(mem.sliceTo(pathname, 0), out_buffer);
lib/std/os/windows.zig+42-14
......@@ -170,7 +170,7 @@ pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) C
170170}
171171
172172pub fn CreateEventEx(attributes: ?*SECURITY_ATTRIBUTES, name: []const u8, flags: DWORD, desired_access: DWORD) !HANDLE {
173 const nameW = try sliceToPrefixedFileW(name);
173 const nameW = try sliceToPrefixedFileW(null, name);
174174 return CreateEventExW(attributes, nameW.span().ptr, flags, desired_access);
175175}
176176
......@@ -1007,8 +1007,8 @@ pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFil
10071007pub const MoveFileError = error{ FileNotFound, AccessDenied, Unexpected };
10081008
10091009pub fn MoveFileEx(old_path: []const u8, new_path: []const u8, flags: DWORD) MoveFileError!void {
1010 const old_path_w = try sliceToPrefixedFileW(old_path);
1011 const new_path_w = try sliceToPrefixedFileW(new_path);
1010 const old_path_w = try sliceToPrefixedFileW(null, old_path);
1011 const new_path_w = try sliceToPrefixedFileW(null, new_path);
10121012 return MoveFileExW(old_path_w.span().ptr, new_path_w.span().ptr, flags);
10131013}
10141014
......@@ -1317,7 +1317,7 @@ pub const GetFileAttributesError = error{
13171317};
13181318
13191319pub fn GetFileAttributes(filename: []const u8) GetFileAttributesError!DWORD {
1320 const filename_w = try sliceToPrefixedFileW(filename);
1320 const filename_w = try sliceToPrefixedFileW(null, filename);
13211321 return GetFileAttributesW(filename_w.span().ptr);
13221322}
13231323
......@@ -2120,16 +2120,16 @@ pub fn normalizePath(comptime T: type, path: []T) RemoveDotDirsError!usize {
21202120
21212121/// Same as `sliceToPrefixedFileW` but accepts a pointer
21222122/// to a null-terminated path.
2123pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {
2124 return sliceToPrefixedFileW(mem.sliceTo(s, 0));
2123pub fn cStrToPrefixedFileW(dir: ?HANDLE, s: [*:0]const u8) !PathSpace {
2124 return sliceToPrefixedFileW(dir, mem.sliceTo(s, 0));
21252125}
21262126
21272127/// Same as `wToPrefixedFileW` but accepts a UTF-8 encoded path.
2128pub fn sliceToPrefixedFileW(path: []const u8) !PathSpace {
2128pub fn sliceToPrefixedFileW(dir: ?HANDLE, path: []const u8) !PathSpace {
21292129 var temp_path: PathSpace = undefined;
21302130 temp_path.len = try std.unicode.utf8ToUtf16Le(&temp_path.data, path);
21312131 temp_path.data[temp_path.len] = 0;
2132 return wToPrefixedFileW(temp_path.span());
2132 return wToPrefixedFileW(dir, temp_path.span());
21332133}
21342134
21352135/// Converts the `path` to WTF16, null-terminated. If the path contains any
......@@ -2139,11 +2139,11 @@ pub fn sliceToPrefixedFileW(path: []const u8) !PathSpace {
21392139/// Similar to RtlDosPathNameToNtPathName_U with a few differences:
21402140/// - Does not allocate on the heap.
21412141/// - Relative paths are kept as relative unless they contain too many ..
2142/// components, in which case they are treated as drive-relative and resolved
2143/// against the CWD.
2142/// components, in which case they are resolved against the `dir` if it
2143/// is non-null, or the CWD if it is null.
21442144/// - Special case device names like COM1, NUL, etc are not handled specially (TODO)
21452145/// - . and space are not stripped from the end of relative paths (potential TODO)
2146pub fn wToPrefixedFileW(path: [:0]const u16) !PathSpace {
2146pub fn wToPrefixedFileW(dir: ?HANDLE, path: [:0]const u16) !PathSpace {
21472147 const nt_prefix = [_]u16{ '\\', '?', '?', '\\' };
21482148 switch (getNamespacePrefix(u16, path)) {
21492149 // TODO: Figure out a way to design an API that can avoid the copy for .nt,
......@@ -2194,8 +2194,7 @@ pub fn wToPrefixedFileW(path: [:0]const u16) !PathSpace {
21942194
21952195 @memcpy(path_space.data[0..path.len], path);
21962196 // Try to normalize, but if we get too many parent directories,
2197 // then this is effectively a 'drive relative' path, so we need to
2198 // start over and use RtlGetFullPathName_U instead.
2197 // then we need to start over and use RtlGetFullPathName_U instead.
21992198 path_space.len = normalizePath(u16, path_space.data[0..path.len]) catch |err| switch (err) {
22002199 error.TooManyParentDirs => break :relative,
22012200 };
......@@ -2224,8 +2223,37 @@ pub fn wToPrefixedFileW(path: [:0]const u16) !PathSpace {
22242223 else => nt_prefix.len,
22252224 };
22262225 const buf_len = @as(u32, @intCast(path_space.data.len - path_buf_offset));
2226 const path_to_get: [:0]const u16 = path_to_get: {
2227 // If dir is null, then we don't need to bother with GetFinalPathNameByHandle because
2228 // RtlGetFullPathName_U will resolve relative paths against the CWD for us.
2229 if (path_type != .relative or dir == null) {
2230 break :path_to_get path;
2231 }
2232 // We can also skip GetFinalPathNameByHandle if the handle matches
2233 // the handle returned by fs.cwd()
2234 if (dir.? == std.fs.cwd().fd) {
2235 break :path_to_get path;
2236 }
2237 // At this point, we know we have a relative path that had too many
2238 // `..` components to be resolved by normalizePath, so we need to
2239 // convert it into an absolute path and let RtlGetFullPathName_U
2240 // canonicalize it. We do this by getting the path of the `dir`
2241 // and appending the relative path to it.
2242 var dir_path_buf: [PATH_MAX_WIDE:0]u16 = undefined;
2243 const dir_path = try GetFinalPathNameByHandle(dir.?, .{}, &dir_path_buf);
2244 if (dir_path.len + 1 + path.len > PATH_MAX_WIDE) {
2245 return error.NameTooLong;
2246 }
2247 // We don't have to worry about potentially doubling up path separators
2248 // here since RtlGetFullPathName_U will handle canonicalizing it.
2249 dir_path_buf[dir_path.len] = '\\';
2250 @memcpy(dir_path_buf[dir_path.len + 1 ..][0..path.len], path);
2251 const full_len = dir_path.len + 1 + path.len;
2252 dir_path_buf[full_len] = 0;
2253 break :path_to_get dir_path_buf[0..full_len :0];
2254 };
22272255 const path_byte_len = ntdll.RtlGetFullPathName_U(
2228 path.ptr,
2256 path_to_get.ptr,
22292257 buf_len * 2,
22302258 path_space.data[path_buf_offset..].ptr,
22312259 null,
lib/std/os/windows/test.zig+2-2
......@@ -28,7 +28,7 @@ fn RtlDosPathNameToNtPathName_U(path: [:0]const u16) !windows.PathSpace {
2828fn testToPrefixedFileNoOracle(comptime path: []const u8, comptime expected_path: []const u8) !void {
2929 const path_utf16 = std.unicode.utf8ToUtf16LeStringLiteral(path);
3030 const expected_path_utf16 = std.unicode.utf8ToUtf16LeStringLiteral(expected_path);
31 const actual_path = try windows.wToPrefixedFileW(path_utf16);
31 const actual_path = try windows.wToPrefixedFileW(null, path_utf16);
3232 std.testing.expectEqualSlices(u16, expected_path_utf16, actual_path.span()) catch |e| {
3333 std.debug.print("got '{s}', expected '{s}'\n", .{ std.unicode.fmtUtf16le(actual_path.span()), std.unicode.fmtUtf16le(expected_path_utf16) });
3434 return e;
......@@ -45,7 +45,7 @@ fn testToPrefixedFileWithOracle(comptime path: []const u8, comptime expected_pat
4545/// Test that the Zig conversion matches the conversion that RtlDosPathNameToNtPathName_U does.
4646fn testToPrefixedFileOnlyOracle(comptime path: []const u8) !void {
4747 const path_utf16 = std.unicode.utf8ToUtf16LeStringLiteral(path);
48 const zig_result = try windows.wToPrefixedFileW(path_utf16);
48 const zig_result = try windows.wToPrefixedFileW(null, path_utf16);
4949 const win32_api_result = try RtlDosPathNameToNtPathName_U(path_utf16);
5050 std.testing.expectEqualSlices(u16, win32_api_result.span(), zig_result.span()) catch |e| {
5151 std.debug.print("got '{s}', expected '{s}'\n", .{ std.unicode.fmtUtf16le(zig_result.span()), std.unicode.fmtUtf16le(win32_api_result.span()) });