From 3ab5e6b1a97160ddadb90d627ae127a62c3cbd96 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Wed, 15 Jul 2020 18:15:20 +0200 Subject: [PATCH] Ensure we use Win32 prefix in Win32 calls --- lib/std/os.zig | 12 +++---- lib/std/os/test.zig | 5 --- lib/std/os/windows.zig | 71 +++++++++++++++++++++++++++++++----------- 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/lib/std/os.zig b/lib/std/os.zig index 5243c1155a..e638e9f2e1 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1556,8 +1556,8 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8, flags: Symlin @compileError("symlink is not supported in WASI; use symlinkat instead"); } if (builtin.os.tag == .windows) { - const target_path_w = try windows.sliceToPrefixedFileW(target_path); - const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); + const target_path_w = try windows.sliceToWin32PrefixedFileW(target_path); + const sym_link_path_w = try windows.sliceToWin32PrefixedFileW(sym_link_path); return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr, flags); } const target_path_c = try toPosixPath(target_path); @@ -1578,8 +1578,8 @@ pub fn symlinkW(target_path: [*:0]const u16, sym_link_path: [*:0]const u16, flag /// See also `symlink`. pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8, flags: SymlinkFlags) SymLinkError!void { if (builtin.os.tag == .windows) { - const target_path_w = try windows.cStrToPrefixedFileW(target_path); - const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); + const target_path_w = try windows.cStrToWin32PrefixedFileW(target_path); + const sym_link_path_w = try windows.cStrToWin32PrefixedFileW(sym_link_path); return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr); } switch (errno(system.symlink(target_path, sym_link_path))) { @@ -2382,7 +2382,7 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { if (builtin.os.tag == .wasi) { @compileError("readlink is not supported in WASI; use readlinkat instead"); } else if (builtin.os.tag == .windows) { - const file_path_w = try windows.sliceToPrefixedFileW(file_path); + const file_path_w = try windows.sliceToWin32PrefixedFileW(file_path); return readlinkW(file_path_w.span().ptr, out_buffer); } else { const file_path_c = try toPosixPath(file_path); @@ -2448,7 +2448,7 @@ fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u /// Same as `readlink` except `file_path` is null-terminated. pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { if (builtin.os.tag == .windows) { - const file_path_w = try windows.cStrToPrefixedFileW(file_path); + const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path); return readlinkW(file_path_w.span().ptr, out_buffer); } const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len); diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 056d06d975..65179e579f 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -80,8 +80,6 @@ test "readlink" { { const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "file.txt" }); const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink1" }); - std.debug.warn("\ntarget_path={}\n", .{target_path}); - std.debug.warn("symlink_path={}\n", .{symlink_path}); // Create symbolic link by path try os.symlink(target_path, symlink_path, .{ .is_directory = false }); @@ -90,8 +88,6 @@ test "readlink" { { const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "subdir" }); const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" }); - std.debug.warn("\ntarget_path={}\n", .{target_path}); - std.debug.warn("symlink_path={}\n", .{symlink_path}); // Create symbolic link by path try os.symlink(target_path, symlink_path, .{ .is_directory = true }); @@ -108,7 +104,6 @@ test "readlink" { fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; const given = try os.readlink(symlink_path, buffer[0..]); - std.debug.warn("given={}\n", .{given}); expect(mem.eql(u8, target_path, given)); } diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 67f200d339..ab8705048a 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -1263,40 +1263,80 @@ pub const PathSpace = struct { pub fn span(self: PathSpace) [:0]const u16 { return self.data[0..self.len :0]; } + + fn ensureNtStyle(self: *PathSpace) void { + // > File I/O functions in the Windows API convert "/" to "\" as part of + // > converting the name to an NT-style name, except when using the "\\?\" + // > prefix as detailed in the following sections. + // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation + // Because we want the larger maximum path length for absolute paths, we + // convert forward slashes to backward slashes here. + for (self.data[0..self.len]) |*elem| { + if (elem.* == '/') { + elem.* = '\\'; + } + } + self.data[self.len] = 0; + } }; +/// Same as `sliceToPrefixedFileW` but accepts a pointer +/// to a null-terminated path. pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace { return sliceToPrefixedFileW(mem.spanZ(s)); } +/// Same as `sliceToWin32PrefixedFileW` but accepts a pointer +/// to a null-terminated path. +pub fn cStrToWin32PrefixedFileW(s: [*:0]const u8) !PathSpace { + return sliceToWin32PrefixedFileW(mem.spanZ(s)); +} + +/// Converts the path `s` to WTF16, null-terminated. If the path is absolute, +/// it will get NT-style prefix `\??\` prepended automatically. For prepending +/// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead. pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace { // TODO https://github.com/ziglang/zig/issues/2765 var path_space: PathSpace = undefined; - for (s) |byte| { + const prefix_index: usize = if (mem.startsWith(u8, s, "\\??\\")) 4 else 0; + for (s[prefix_index..]) |byte| { switch (byte) { '*', '?', '"', '<', '>', '|' => return error.BadPathName, else => {}, } } - const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: { + const start_index = if (prefix_index > 0 or !std.fs.path.isAbsolute(s)) 0 else blk: { const prefix = [_]u16{ '\\', '?', '?', '\\' }; mem.copy(u16, path_space.data[0..], &prefix); break :blk prefix.len; }; path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s); if (path_space.len > path_space.data.len) return error.NameTooLong; - // > File I/O functions in the Windows API convert "/" to "\" as part of - // > converting the name to an NT-style name, except when using the "\\?\" - // > prefix as detailed in the following sections. - // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation - // Because we want the larger maximum path length for absolute paths, we - // convert forward slashes to backward slashes here. - for (path_space.data[0..path_space.len]) |*elem| { - if (elem.* == '/') { - elem.* = '\\'; + path_space.ensureNtStyle(); + return path_space; +} + +/// Converts the path `s` to WTF16, null-terminated. If the path is absolute, +/// it will get Win32-style extended prefix `\\?\` prepended automatically. For prepending +/// NT-style prefix, see `sliceToPrefixedFileW` instead. +pub fn sliceToWin32PrefixedFileW(s: []const u8) !PathSpace { + // TODO https://github.com/ziglang/zig/issues/2765 + var path_space: PathSpace = undefined; + const prefix_index: usize = if (mem.startsWith(u8, s, "\\\\?\\")) 4 else 0; + for (s[prefix_index..]) |byte| { + switch (byte) { + '*', '?', '"', '<', '>', '|' => return error.BadPathName, + else => {}, } } - path_space.data[path_space.len] = 0; + const start_index = if (prefix_index > 0 or !std.fs.path.isAbsolute(s)) 0 else blk: { + const prefix = [_]u16{ '\\', '\\', '?', '\\' }; + mem.copy(u16, path_space.data[0..], &prefix); + break :blk prefix.len; + }; + path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s); + if (path_space.len > path_space.data.len) return error.NameTooLong; + path_space.ensureNtStyle(); return path_space; } @@ -1313,12 +1353,7 @@ pub fn wToPrefixedFileW(s: []const u16) !PathSpace { path_space.len = start_index + s.len; if (path_space.len > path_space.data.len) return error.NameTooLong; mem.copy(u16, path_space.data[start_index..], s); - for (path_space.data[0..path_space.len]) |*elem| { - if (elem.* == '/') { - elem.* = '\\'; - } - } - path_space.data[path_space.len] = 0; + path_space.ensureNtStyle(); return path_space; }