| author | |
| committer | |
| log | c47cb8d09f7cf1c02d3af59ebbca665ce78c85ca |
| tree | febc011d21f115dfbd95fa0e8250e2c0327fc3ce |
| parent | ae8abedbeda33ff5cd93ca2fb21ef2f5453dfb37 |
3 files changed, 51 insertions(+), 42 deletions(-)
lib/std/os.zig+5-5| ... | @@ -1834,7 +1834,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr | ... | @@ -1834,7 +1834,7 @@ pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatEr |
| 1834 | const create_options_flags = if (want_rmdir_behavior) | 1834 | const create_options_flags = if (want_rmdir_behavior) |
| 1835 | @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_DIRECTORY_FILE) | 1835 | @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_DIRECTORY_FILE) |
| 1836 | else | 1836 | else |
| 1837 | @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE); | 1837 | @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT); // would we ever want to delete the target instead? |
| 1838 | 1838 | ||
| 1839 | const path_len_bytes = @intCast(u16, mem.lenZ(sub_path_w) * 2); | 1839 | const path_len_bytes = @intCast(u16, mem.lenZ(sub_path_w) * 2); |
| 1840 | var nt_name = w.UNICODE_STRING{ | 1840 | var nt_name = w.UNICODE_STRING{ |
| ... | @@ -2371,7 +2371,7 @@ pub const ReadLinkError = error{ | ... | @@ -2371,7 +2371,7 @@ pub const ReadLinkError = error{ |
| 2371 | InvalidUtf8, | 2371 | InvalidUtf8, |
| 2372 | BadPathName, | 2372 | BadPathName, |
| 2373 | /// Windows-only. | 2373 | /// Windows-only. |
| 2374 | UnsupportedSymlinkType, | 2374 | UnsupportedReparsePointType, |
| 2375 | } || UnexpectedError; | 2375 | } || UnexpectedError; |
| 2376 | 2376 | ||
| 2377 | /// Read value of a symbolic link. | 2377 | /// Read value of a symbolic link. |
| ... | @@ -2412,7 +2412,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 | ... | @@ -2412,7 +2412,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 |
| 2412 | const reparse_struct = @ptrCast(*const w.REPARSE_DATA_BUFFER, @alignCast(@alignOf(w.REPARSE_DATA_BUFFER), &reparse_buf[0])); | 2412 | const reparse_struct = @ptrCast(*const w.REPARSE_DATA_BUFFER, @alignCast(@alignOf(w.REPARSE_DATA_BUFFER), &reparse_buf[0])); |
| 2413 | switch (reparse_struct.ReparseTag) { | 2413 | switch (reparse_struct.ReparseTag) { |
| 2414 | w.IO_REPARSE_TAG_SYMLINK => { | 2414 | w.IO_REPARSE_TAG_SYMLINK => { |
| 2415 | const buf = @ptrCast(*const w.SymbolicLinkReparseBuffer, @alignCast(@alignOf(w.SymbolicLinkReparseBuffer), &reparse_struct.DataBuffer[0])); | 2415 | const buf = @ptrCast(*const w.SYMBOLIC_LINK_REPARSE_BUFFER, @alignCast(@alignOf(w.SYMBOLIC_LINK_REPARSE_BUFFER), &reparse_struct.DataBuffer[0])); |
| 2416 | const offset = buf.SubstituteNameOffset >> 1; | 2416 | const offset = buf.SubstituteNameOffset >> 1; |
| 2417 | const len = buf.SubstituteNameLength >> 1; | 2417 | const len = buf.SubstituteNameLength >> 1; |
| 2418 | const path_buf = @as([*]const u16, &buf.PathBuffer); | 2418 | const path_buf = @as([*]const u16, &buf.PathBuffer); |
| ... | @@ -2420,7 +2420,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 | ... | @@ -2420,7 +2420,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 |
| 2420 | return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer); | 2420 | return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer); |
| 2421 | }, | 2421 | }, |
| 2422 | w.IO_REPARSE_TAG_MOUNT_POINT => { | 2422 | w.IO_REPARSE_TAG_MOUNT_POINT => { |
| 2423 | const buf = @ptrCast(*const w.MountPointReparseBuffer, @alignCast(@alignOf(w.MountPointReparseBuffer), &reparse_struct.DataBuffer[0])); | 2423 | const buf = @ptrCast(*const w.MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(w.MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0])); |
| 2424 | const offset = buf.SubstituteNameOffset >> 1; | 2424 | const offset = buf.SubstituteNameOffset >> 1; |
| 2425 | const len = buf.SubstituteNameLength >> 1; | 2425 | const len = buf.SubstituteNameLength >> 1; |
| 2426 | const path_buf = @as([*]const u16, &buf.PathBuffer); | 2426 | const path_buf = @as([*]const u16, &buf.PathBuffer); |
| ... | @@ -2428,7 +2428,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 | ... | @@ -2428,7 +2428,7 @@ pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 |
| 2428 | }, | 2428 | }, |
| 2429 | else => |value| { | 2429 | else => |value| { |
| 2430 | std.debug.warn("unsupported symlink type: {}", .{value}); | 2430 | std.debug.warn("unsupported symlink type: {}", .{value}); |
| 2431 | return error.UnsupportedSymlinkType; | 2431 | return error.UnsupportedReparsePointType; |
| 2432 | }, | 2432 | }, |
| 2433 | } | 2433 | } |
| 2434 | } | 2434 | } |
lib/std/os/test.zig+44-35| ... | @@ -43,42 +43,51 @@ test "fstatat" { | ... | @@ -43,42 +43,51 @@ test "fstatat" { |
| 43 | 43 | ||
| 44 | test "readlink" { | 44 | test "readlink" { |
| 45 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | 45 | if (builtin.os.tag == .wasi) return error.SkipZigTest; |
| 46 | |||
| 47 | var cwd = fs.cwd(); | ||
| 48 | try cwd.writeFile("file.txt", "nonsense"); | ||
| 49 | try os.symlink("file.txt", "symlinked"); | ||
| 50 | 46 | ||
| 51 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | 47 | // First, try relative paths |
| 52 | const given = try os.readlink("symlinked", buffer[0..]); | 48 | { |
| 53 | expect(mem.eql(u8, "file.txt", given)); | 49 | var cwd = fs.cwd(); |
| 54 | 50 | try cwd.writeFile("file.txt", "nonsense"); | |
| 55 | // var tmp = tmpDir(.{}); | 51 | try os.symlink("file.txt", "symlinked"); |
| 56 | // defer tmp.cleanup(); | 52 | |
| 57 | 53 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | |
| 58 | // // create file | 54 | const given = try os.readlink("symlinked", buffer[0..]); |
| 59 | // try tmp.dir.writeFile("file.txt", "nonsense"); | 55 | expect(mem.eql(u8, "file.txt", given)); |
| 60 | 56 | ||
| 61 | // // get paths | 57 | try cwd.deleteFile("file.txt"); |
| 62 | // // TODO: use Dir's realpath function once that exists | 58 | try cwd.deleteFile("symlinked"); |
| 63 | // var arena = ArenaAllocator.init(testing.allocator); | 59 | } |
| 64 | // defer arena.deinit(); | 60 | |
| 65 | 61 | // Next, let's try fully-qualified paths | |
| 66 | // const base_path = blk: { | 62 | { |
| 67 | // const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..]}); | 63 | var tmp = tmpDir(.{}); |
| 68 | // break :blk try fs.realpathAlloc(&arena.allocator, relative_path); | 64 | // defer tmp.cleanup(); |
| 69 | // }; | 65 | |
| 70 | // const target_path = try fs.path.join(&arena.allocator, &[_][]const u8{base_path, "file.txt"}); | 66 | // create file |
| 71 | // const symlink_path = try fs.path.join(&arena.allocator, &[_][]const u8{base_path, "symlinked"}); | 67 | try tmp.dir.writeFile("file.txt", "nonsense"); |
| 72 | // std.debug.warn("\ntarget_path={}\n", .{target_path}); | 68 | |
| 73 | // std.debug.warn("symlink_path={}\n", .{symlink_path}); | 69 | // get paths |
| 74 | 70 | // TODO: use Dir's realpath function once that exists | |
| 75 | // // create symbolic link by path | 71 | var arena = ArenaAllocator.init(testing.allocator); |
| 76 | // try os.symlink(target_path, symlink_path); | 72 | defer arena.deinit(); |
| 77 | 73 | ||
| 78 | // // now, read the link and verify | 74 | const base_path = blk: { |
| 79 | // var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | 75 | const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] }); |
| 80 | // const given = try os.readlink(symlink_path, buffer[0..]); | 76 | break :blk try fs.realpathAlloc(&arena.allocator, relative_path); |
| 81 | // expect(mem.eql(u8, symlink_path, given)); | 77 | }; |
| 78 | const target_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "file.txt" }); | ||
| 79 | const symlink_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "symlinked" }); | ||
| 80 | std.debug.warn("\ntarget_path={}\n", .{target_path}); | ||
| 81 | std.debug.warn("symlink_path={}\n", .{symlink_path}); | ||
| 82 | |||
| 83 | // create symbolic link by path | ||
| 84 | try os.symlink(target_path, symlink_path); | ||
| 85 | |||
| 86 | // now, read the link and verify | ||
| 87 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | ||
| 88 | const given = try os.readlink(symlink_path, buffer[0..]); | ||
| 89 | expect(mem.eql(u8, symlink_path, given)); | ||
| 90 | } | ||
| 82 | } | 91 | } |
| 83 | 92 | ||
| 84 | test "readlinkat" { | 93 | test "readlinkat" { |
lib/std/os/windows/bits.zig+2-2| ... | @@ -1549,7 +1549,7 @@ pub const REPARSE_DATA_BUFFER = extern struct { | ... | @@ -1549,7 +1549,7 @@ pub const REPARSE_DATA_BUFFER = extern struct { |
| 1549 | Reserved: USHORT, | 1549 | Reserved: USHORT, |
| 1550 | DataBuffer: [1]UCHAR, | 1550 | DataBuffer: [1]UCHAR, |
| 1551 | }; | 1551 | }; |
| 1552 | pub const SymbolicLinkReparseBuffer = extern struct { | 1552 | pub const SYMBOLIC_LINK_REPARSE_BUFFER = extern struct { |
| 1553 | SubstituteNameOffset: USHORT, | 1553 | SubstituteNameOffset: USHORT, |
| 1554 | SubstituteNameLength: USHORT, | 1554 | SubstituteNameLength: USHORT, |
| 1555 | PrintNameOffset: USHORT, | 1555 | PrintNameOffset: USHORT, |
| ... | @@ -1557,7 +1557,7 @@ pub const SymbolicLinkReparseBuffer = extern struct { | ... | @@ -1557,7 +1557,7 @@ pub const SymbolicLinkReparseBuffer = extern struct { |
| 1557 | Flags: ULONG, | 1557 | Flags: ULONG, |
| 1558 | PathBuffer: [1]WCHAR, | 1558 | PathBuffer: [1]WCHAR, |
| 1559 | }; | 1559 | }; |
| 1560 | pub const MountPointReparseBuffer = extern struct { | 1560 | pub const MOUNT_POINT_REPARSE_BUFFER = extern struct { |
| 1561 | SubstituteNameOffset: USHORT, | 1561 | SubstituteNameOffset: USHORT, |
| 1562 | SubstituteNameLength: USHORT, | 1562 | SubstituteNameLength: USHORT, |
| 1563 | PrintNameOffset: USHORT, | 1563 | PrintNameOffset: USHORT, |