| 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 | 1834 | const create_options_flags = if (want_rmdir_behavior) |
| 1835 | 1835 | @as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_DIRECTORY_FILE) |
| 1836 | 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 | 1839 | const path_len_bytes = @intCast(u16, mem.lenZ(sub_path_w) * 2); |
| 1840 | 1840 | var nt_name = w.UNICODE_STRING{ |
| ... | ... | @@ -2371,7 +2371,7 @@ pub const ReadLinkError = error{ |
| 2371 | 2371 | InvalidUtf8, |
| 2372 | 2372 | BadPathName, |
| 2373 | 2373 | /// Windows-only. |
| 2374 | UnsupportedSymlinkType, | |
| 2374 | UnsupportedReparsePointType, | |
| 2375 | 2375 | } || UnexpectedError; |
| 2376 | 2376 | |
| 2377 | 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 | 2412 | const reparse_struct = @ptrCast(*const w.REPARSE_DATA_BUFFER, @alignCast(@alignOf(w.REPARSE_DATA_BUFFER), &reparse_buf[0])); |
| 2413 | 2413 | switch (reparse_struct.ReparseTag) { |
| 2414 | 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 | 2416 | const offset = buf.SubstituteNameOffset >> 1; |
| 2417 | 2417 | const len = buf.SubstituteNameLength >> 1; |
| 2418 | 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 | 2420 | return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer); |
| 2421 | 2421 | }, |
| 2422 | 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 | 2424 | const offset = buf.SubstituteNameOffset >> 1; |
| 2425 | 2425 | const len = buf.SubstituteNameLength >> 1; |
| 2426 | 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 | 2428 | }, |
| 2429 | 2429 | else => |value| { |
| 2430 | 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 | 43 | |
| 44 | 44 | test "readlink" { |
| 45 | 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; | |
| 52 | const given = try os.readlink("symlinked", buffer[0..]); | |
| 53 | expect(mem.eql(u8, "file.txt", given)); | |
| 54 | ||
| 55 | // var tmp = tmpDir(.{}); | |
| 56 | // defer tmp.cleanup(); | |
| 57 | ||
| 58 | // // create file | |
| 59 | // try tmp.dir.writeFile("file.txt", "nonsense"); | |
| 60 | ||
| 61 | // // get paths | |
| 62 | // // TODO: use Dir's realpath function once that exists | |
| 63 | // var arena = ArenaAllocator.init(testing.allocator); | |
| 64 | // defer arena.deinit(); | |
| 65 | ||
| 66 | // const base_path = blk: { | |
| 67 | // const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..]}); | |
| 68 | // break :blk try fs.realpathAlloc(&arena.allocator, relative_path); | |
| 69 | // }; | |
| 70 | // const target_path = try fs.path.join(&arena.allocator, &[_][]const u8{base_path, "file.txt"}); | |
| 71 | // const symlink_path = try fs.path.join(&arena.allocator, &[_][]const u8{base_path, "symlinked"}); | |
| 72 | // std.debug.warn("\ntarget_path={}\n", .{target_path}); | |
| 73 | // std.debug.warn("symlink_path={}\n", .{symlink_path}); | |
| 74 | ||
| 75 | // // create symbolic link by path | |
| 76 | // try os.symlink(target_path, symlink_path); | |
| 77 | ||
| 78 | // // now, read the link and verify | |
| 79 | // var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | |
| 80 | // const given = try os.readlink(symlink_path, buffer[0..]); | |
| 81 | // expect(mem.eql(u8, symlink_path, given)); | |
| 47 | // First, try relative paths | |
| 48 | { | |
| 49 | var cwd = fs.cwd(); | |
| 50 | try cwd.writeFile("file.txt", "nonsense"); | |
| 51 | try os.symlink("file.txt", "symlinked"); | |
| 52 | ||
| 53 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | |
| 54 | const given = try os.readlink("symlinked", buffer[0..]); | |
| 55 | expect(mem.eql(u8, "file.txt", given)); | |
| 56 | ||
| 57 | try cwd.deleteFile("file.txt"); | |
| 58 | try cwd.deleteFile("symlinked"); | |
| 59 | } | |
| 60 | ||
| 61 | // Next, let's try fully-qualified paths | |
| 62 | { | |
| 63 | var tmp = tmpDir(.{}); | |
| 64 | // defer tmp.cleanup(); | |
| 65 | ||
| 66 | // create file | |
| 67 | try tmp.dir.writeFile("file.txt", "nonsense"); | |
| 68 | ||
| 69 | // get paths | |
| 70 | // TODO: use Dir's realpath function once that exists | |
| 71 | var arena = ArenaAllocator.init(testing.allocator); | |
| 72 | defer arena.deinit(); | |
| 73 | ||
| 74 | const base_path = blk: { | |
| 75 | const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] }); | |
| 76 | break :blk try fs.realpathAlloc(&arena.allocator, relative_path); | |
| 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 | 93 | test "readlinkat" { |
lib/std/os/windows/bits.zig+2-2| ... | ... | @@ -1549,7 +1549,7 @@ pub const REPARSE_DATA_BUFFER = extern struct { |
| 1549 | 1549 | Reserved: USHORT, |
| 1550 | 1550 | DataBuffer: [1]UCHAR, |
| 1551 | 1551 | }; |
| 1552 | pub const SymbolicLinkReparseBuffer = extern struct { | |
| 1552 | pub const SYMBOLIC_LINK_REPARSE_BUFFER = extern struct { | |
| 1553 | 1553 | SubstituteNameOffset: USHORT, |
| 1554 | 1554 | SubstituteNameLength: USHORT, |
| 1555 | 1555 | PrintNameOffset: USHORT, |
| ... | ... | @@ -1557,7 +1557,7 @@ pub const SymbolicLinkReparseBuffer = extern struct { |
| 1557 | 1557 | Flags: ULONG, |
| 1558 | 1558 | PathBuffer: [1]WCHAR, |
| 1559 | 1559 | }; |
| 1560 | pub const MountPointReparseBuffer = extern struct { | |
| 1560 | pub const MOUNT_POINT_REPARSE_BUFFER = extern struct { | |
| 1561 | 1561 | SubstituteNameOffset: USHORT, |
| 1562 | 1562 | SubstituteNameLength: USHORT, |
| 1563 | 1563 | PrintNameOffset: USHORT, |