| author | |
| committer | |
| log | 3ab5e6b1a97160ddadb90d627ae127a62c3cbd96 |
| tree | befd4252e555fbca1a09079a7ad598d3fe69d989 |
| parent | cc9c5c5b0e7c37f96d7ea4c6bd22118ea72a0265 |
3 files changed, 59 insertions(+), 29 deletions(-)
lib/std/os.zig+6-6| ... | @@ -1556,8 +1556,8 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8, flags: Symlin | ... | @@ -1556,8 +1556,8 @@ pub fn symlink(target_path: []const u8, sym_link_path: []const u8, flags: Symlin |
| 1556 | @compileError("symlink is not supported in WASI; use symlinkat instead"); | 1556 | @compileError("symlink is not supported in WASI; use symlinkat instead"); |
| 1557 | } | 1557 | } |
| 1558 | if (builtin.os.tag == .windows) { | 1558 | if (builtin.os.tag == .windows) { |
| 1559 | const target_path_w = try windows.sliceToPrefixedFileW(target_path); | 1559 | const target_path_w = try windows.sliceToWin32PrefixedFileW(target_path); |
| 1560 | const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path); | 1560 | const sym_link_path_w = try windows.sliceToWin32PrefixedFileW(sym_link_path); |
| 1561 | return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr, flags); | 1561 | return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr, flags); |
| 1562 | } | 1562 | } |
| 1563 | const target_path_c = try toPosixPath(target_path); | 1563 | 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 | ... | @@ -1578,8 +1578,8 @@ pub fn symlinkW(target_path: [*:0]const u16, sym_link_path: [*:0]const u16, flag |
| 1578 | /// See also `symlink`. | 1578 | /// See also `symlink`. |
| 1579 | pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8, flags: SymlinkFlags) SymLinkError!void { | 1579 | pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8, flags: SymlinkFlags) SymLinkError!void { |
| 1580 | if (builtin.os.tag == .windows) { | 1580 | if (builtin.os.tag == .windows) { |
| 1581 | const target_path_w = try windows.cStrToPrefixedFileW(target_path); | 1581 | const target_path_w = try windows.cStrToWin32PrefixedFileW(target_path); |
| 1582 | const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path); | 1582 | const sym_link_path_w = try windows.cStrToWin32PrefixedFileW(sym_link_path); |
| 1583 | return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr); | 1583 | return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr); |
| 1584 | } | 1584 | } |
| 1585 | switch (errno(system.symlink(target_path, sym_link_path))) { | 1585 | 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 { | ... | @@ -2382,7 +2382,7 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { |
| 2382 | if (builtin.os.tag == .wasi) { | 2382 | if (builtin.os.tag == .wasi) { |
| 2383 | @compileError("readlink is not supported in WASI; use readlinkat instead"); | 2383 | @compileError("readlink is not supported in WASI; use readlinkat instead"); |
| 2384 | } else if (builtin.os.tag == .windows) { | 2384 | } else if (builtin.os.tag == .windows) { |
| 2385 | const file_path_w = try windows.sliceToPrefixedFileW(file_path); | 2385 | const file_path_w = try windows.sliceToWin32PrefixedFileW(file_path); |
| 2386 | return readlinkW(file_path_w.span().ptr, out_buffer); | 2386 | return readlinkW(file_path_w.span().ptr, out_buffer); |
| 2387 | } else { | 2387 | } else { |
| 2388 | const file_path_c = try toPosixPath(file_path); | 2388 | const file_path_c = try toPosixPath(file_path); |
| ... | @@ -2448,7 +2448,7 @@ fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u | ... | @@ -2448,7 +2448,7 @@ fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u |
| 2448 | /// Same as `readlink` except `file_path` is null-terminated. | 2448 | /// Same as `readlink` except `file_path` is null-terminated. |
| 2449 | pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { | 2449 | pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { |
| 2450 | if (builtin.os.tag == .windows) { | 2450 | if (builtin.os.tag == .windows) { |
| 2451 | const file_path_w = try windows.cStrToPrefixedFileW(file_path); | 2451 | const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path); |
| 2452 | return readlinkW(file_path_w.span().ptr, out_buffer); | 2452 | return readlinkW(file_path_w.span().ptr, out_buffer); |
| 2453 | } | 2453 | } |
| 2454 | const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len); | 2454 | const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len); |
lib/std/os/test.zig-5| ... | @@ -80,8 +80,6 @@ test "readlink" { | ... | @@ -80,8 +80,6 @@ test "readlink" { |
| 80 | { | 80 | { |
| 81 | const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "file.txt" }); | 81 | const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "file.txt" }); |
| 82 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink1" }); | 82 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink1" }); |
| 83 | std.debug.warn("\ntarget_path={}\n", .{target_path}); | ||
| 84 | std.debug.warn("symlink_path={}\n", .{symlink_path}); | ||
| 85 | 83 | ||
| 86 | // Create symbolic link by path | 84 | // Create symbolic link by path |
| 87 | try os.symlink(target_path, symlink_path, .{ .is_directory = false }); | 85 | try os.symlink(target_path, symlink_path, .{ .is_directory = false }); |
| ... | @@ -90,8 +88,6 @@ test "readlink" { | ... | @@ -90,8 +88,6 @@ test "readlink" { |
| 90 | { | 88 | { |
| 91 | const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "subdir" }); | 89 | const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "subdir" }); |
| 92 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" }); | 90 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" }); |
| 93 | std.debug.warn("\ntarget_path={}\n", .{target_path}); | ||
| 94 | std.debug.warn("symlink_path={}\n", .{symlink_path}); | ||
| 95 | 91 | ||
| 96 | // Create symbolic link by path | 92 | // Create symbolic link by path |
| 97 | try os.symlink(target_path, symlink_path, .{ .is_directory = true }); | 93 | try os.symlink(target_path, symlink_path, .{ .is_directory = true }); |
| ... | @@ -108,7 +104,6 @@ test "readlink" { | ... | @@ -108,7 +104,6 @@ test "readlink" { |
| 108 | fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { | 104 | fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { |
| 109 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | 105 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; |
| 110 | const given = try os.readlink(symlink_path, buffer[0..]); | 106 | const given = try os.readlink(symlink_path, buffer[0..]); |
| 111 | std.debug.warn("given={}\n", .{given}); | ||
| 112 | expect(mem.eql(u8, target_path, given)); | 107 | expect(mem.eql(u8, target_path, given)); |
| 113 | } | 108 | } |
| 114 | 109 |
lib/std/os/windows.zig+53-18| ... | @@ -1263,40 +1263,80 @@ pub const PathSpace = struct { | ... | @@ -1263,40 +1263,80 @@ pub const PathSpace = struct { |
| 1263 | pub fn span(self: PathSpace) [:0]const u16 { | 1263 | pub fn span(self: PathSpace) [:0]const u16 { |
| 1264 | return self.data[0..self.len :0]; | 1264 | return self.data[0..self.len :0]; |
| 1265 | } | 1265 | } |
| 1266 | |||
| 1267 | fn ensureNtStyle(self: *PathSpace) void { | ||
| 1268 | // > File I/O functions in the Windows API convert "/" to "\" as part of | ||
| 1269 | // > converting the name to an NT-style name, except when using the "\\?\" | ||
| 1270 | // > prefix as detailed in the following sections. | ||
| 1271 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | ||
| 1272 | // Because we want the larger maximum path length for absolute paths, we | ||
| 1273 | // convert forward slashes to backward slashes here. | ||
| 1274 | for (self.data[0..self.len]) |*elem| { | ||
| 1275 | if (elem.* == '/') { | ||
| 1276 | elem.* = '\\'; | ||
| 1277 | } | ||
| 1278 | } | ||
| 1279 | self.data[self.len] = 0; | ||
| 1280 | } | ||
| 1266 | }; | 1281 | }; |
| 1267 | 1282 | ||
| 1283 | /// Same as `sliceToPrefixedFileW` but accepts a pointer | ||
| 1284 | /// to a null-terminated path. | ||
| 1268 | pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace { | 1285 | pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace { |
| 1269 | return sliceToPrefixedFileW(mem.spanZ(s)); | 1286 | return sliceToPrefixedFileW(mem.spanZ(s)); |
| 1270 | } | 1287 | } |
| 1271 | 1288 | ||
| 1289 | /// Same as `sliceToWin32PrefixedFileW` but accepts a pointer | ||
| 1290 | /// to a null-terminated path. | ||
| 1291 | pub fn cStrToWin32PrefixedFileW(s: [*:0]const u8) !PathSpace { | ||
| 1292 | return sliceToWin32PrefixedFileW(mem.spanZ(s)); | ||
| 1293 | } | ||
| 1294 | |||
| 1295 | /// Converts the path `s` to WTF16, null-terminated. If the path is absolute, | ||
| 1296 | /// it will get NT-style prefix `\??\` prepended automatically. For prepending | ||
| 1297 | /// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead. | ||
| 1272 | pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace { | 1298 | pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace { |
| 1273 | // TODO https://github.com/ziglang/zig/issues/2765 | 1299 | // TODO https://github.com/ziglang/zig/issues/2765 |
| 1274 | var path_space: PathSpace = undefined; | 1300 | var path_space: PathSpace = undefined; |
| 1275 | for (s) |byte| { | 1301 | const prefix_index: usize = if (mem.startsWith(u8, s, "\\??\\")) 4 else 0; |
| 1302 | for (s[prefix_index..]) |byte| { | ||
| 1276 | switch (byte) { | 1303 | switch (byte) { |
| 1277 | '*', '?', '"', '<', '>', '|' => return error.BadPathName, | 1304 | '*', '?', '"', '<', '>', '|' => return error.BadPathName, |
| 1278 | else => {}, | 1305 | else => {}, |
| 1279 | } | 1306 | } |
| 1280 | } | 1307 | } |
| 1281 | const start_index = if (mem.startsWith(u8, s, "\\?") or !std.fs.path.isAbsolute(s)) 0 else blk: { | 1308 | const start_index = if (prefix_index > 0 or !std.fs.path.isAbsolute(s)) 0 else blk: { |
| 1282 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; | 1309 | const prefix = [_]u16{ '\\', '?', '?', '\\' }; |
| 1283 | mem.copy(u16, path_space.data[0..], &prefix); | 1310 | mem.copy(u16, path_space.data[0..], &prefix); |
| 1284 | break :blk prefix.len; | 1311 | break :blk prefix.len; |
| 1285 | }; | 1312 | }; |
| 1286 | path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s); | 1313 | path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s); |
| 1287 | if (path_space.len > path_space.data.len) return error.NameTooLong; | 1314 | if (path_space.len > path_space.data.len) return error.NameTooLong; |
| 1288 | // > File I/O functions in the Windows API convert "/" to "\" as part of | 1315 | path_space.ensureNtStyle(); |
| 1289 | // > converting the name to an NT-style name, except when using the "\\?\" | 1316 | return path_space; |
| 1290 | // > prefix as detailed in the following sections. | 1317 | } |
| 1291 | // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation | 1318 | |
| 1292 | // Because we want the larger maximum path length for absolute paths, we | 1319 | /// Converts the path `s` to WTF16, null-terminated. If the path is absolute, |
| 1293 | // convert forward slashes to backward slashes here. | 1320 | /// it will get Win32-style extended prefix `\\?\` prepended automatically. For prepending |
| 1294 | for (path_space.data[0..path_space.len]) |*elem| { | 1321 | /// NT-style prefix, see `sliceToPrefixedFileW` instead. |
| 1295 | if (elem.* == '/') { | 1322 | pub fn sliceToWin32PrefixedFileW(s: []const u8) !PathSpace { |
| 1296 | elem.* = '\\'; | 1323 | // TODO https://github.com/ziglang/zig/issues/2765 |
| 1324 | var path_space: PathSpace = undefined; | ||
| 1325 | const prefix_index: usize = if (mem.startsWith(u8, s, "\\\\?\\")) 4 else 0; | ||
| 1326 | for (s[prefix_index..]) |byte| { | ||
| 1327 | switch (byte) { | ||
| 1328 | '*', '?', '"', '<', '>', '|' => return error.BadPathName, | ||
| 1329 | else => {}, | ||
| 1297 | } | 1330 | } |
| 1298 | } | 1331 | } |
| 1299 | path_space.data[path_space.len] = 0; | 1332 | const start_index = if (prefix_index > 0 or !std.fs.path.isAbsolute(s)) 0 else blk: { |
| 1333 | const prefix = [_]u16{ '\\', '\\', '?', '\\' }; | ||
| 1334 | mem.copy(u16, path_space.data[0..], &prefix); | ||
| 1335 | break :blk prefix.len; | ||
| 1336 | }; | ||
| 1337 | path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s); | ||
| 1338 | if (path_space.len > path_space.data.len) return error.NameTooLong; | ||
| 1339 | path_space.ensureNtStyle(); | ||
| 1300 | return path_space; | 1340 | return path_space; |
| 1301 | } | 1341 | } |
| 1302 | 1342 | ||
| ... | @@ -1313,12 +1353,7 @@ pub fn wToPrefixedFileW(s: []const u16) !PathSpace { | ... | @@ -1313,12 +1353,7 @@ pub fn wToPrefixedFileW(s: []const u16) !PathSpace { |
| 1313 | path_space.len = start_index + s.len; | 1353 | path_space.len = start_index + s.len; |
| 1314 | if (path_space.len > path_space.data.len) return error.NameTooLong; | 1354 | if (path_space.len > path_space.data.len) return error.NameTooLong; |
| 1315 | mem.copy(u16, path_space.data[start_index..], s); | 1355 | mem.copy(u16, path_space.data[start_index..], s); |
| 1316 | for (path_space.data[0..path_space.len]) |*elem| { | 1356 | path_space.ensureNtStyle(); |
| 1317 | if (elem.* == '/') { | ||
| 1318 | elem.* = '\\'; | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | path_space.data[path_space.len] = 0; | ||
| 1322 | return path_space; | 1357 | return path_space; |
| 1323 | } | 1358 | } |
| 1324 | 1359 |