Commit 3ab5e6b1a9

Jakub Konka <kubkon@jakubkonka.com>
2020-07-15 18:15:20
Ensure we use Win32 prefix in Win32 calls
1 parent cc9c5c5
Changed files (3)
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));
 }
 
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;
 }
 
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);