| author | |
| committer | |
| log | fc7d87fef19964273eb65e8cee05eaffa7a9e72f |
| tree | fcba1d78951806d07f3cd02bd5fbdb35d36148a6 |
| parent | dd366d316d0b3555864145494b28fdb6e090a3f2 |
This way `std.fs.symlinkAbsolute` becomes cross-platform and we can
legally include `SymlinkFlags` as an argument that's only used on
Windows. Also, now `std.os.symlink` generates a compile error on
Windows with a message to instead use `std.os.windows.CreateSymbolicLink`.
Finally, this PR also reshuffles the tests between `std.os.test` and
`std.fs.test`.5 files changed, 148 insertions(+), 99 deletions(-)
lib/std/fs.zig+61-6| ... | @@ -16,9 +16,6 @@ pub const wasi = @import("fs/wasi.zig"); | ... | @@ -16,9 +16,6 @@ pub const wasi = @import("fs/wasi.zig"); |
| 16 | 16 | ||
| 17 | // TODO audit these APIs with respect to Dir and absolute paths | 17 | // TODO audit these APIs with respect to Dir and absolute paths |
| 18 | 18 | ||
| 19 | pub const symLink = os.symlink; | ||
| 20 | pub const symLinkZ = os.symlinkZ; | ||
| 21 | pub const symLinkC = @compileError("deprecated: renamed to symlinkZ"); | ||
| 22 | pub const rename = os.rename; | 19 | pub const rename = os.rename; |
| 23 | pub const renameZ = os.renameZ; | 20 | pub const renameZ = os.renameZ; |
| 24 | pub const renameC = @compileError("deprecated: renamed to renameZ"); | 21 | pub const renameC = @compileError("deprecated: renamed to renameZ"); |
| ... | @@ -69,7 +66,7 @@ pub const need_async_thread = std.io.is_async and switch (builtin.os.tag) { | ... | @@ -69,7 +66,7 @@ pub const need_async_thread = std.io.is_async and switch (builtin.os.tag) { |
| 69 | 66 | ||
| 70 | /// TODO remove the allocator requirement from this API | 67 | /// TODO remove the allocator requirement from this API |
| 71 | pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { | 68 | pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { |
| 72 | if (symLink(existing_path, new_path, .{})) { | 69 | if (symLinkAbsolute(existing_path, new_path, .{})) { |
| 73 | return; | 70 | return; |
| 74 | } else |err| switch (err) { | 71 | } else |err| switch (err) { |
| 75 | error.PathAlreadyExists => {}, | 72 | error.PathAlreadyExists => {}, |
| ... | @@ -87,7 +84,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: | ... | @@ -87,7 +84,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: |
| 87 | try crypto.randomBytes(rand_buf[0..]); | 84 | try crypto.randomBytes(rand_buf[0..]); |
| 88 | base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf); | 85 | base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf); |
| 89 | 86 | ||
| 90 | if (symLink(existing_path, tmp_path, .{})) { | 87 | if (symLinkAbsolute(existing_path, tmp_path, .{})) { |
| 91 | return rename(tmp_path, new_path); | 88 | return rename(tmp_path, new_path); |
| 92 | } else |err| switch (err) { | 89 | } else |err| switch (err) { |
| 93 | error.PathAlreadyExists => continue, | 90 | error.PathAlreadyExists => continue, |
| ... | @@ -1692,8 +1689,15 @@ pub fn readLinkAbsolute(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 | ... | @@ -1692,8 +1689,15 @@ pub fn readLinkAbsolute(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 |
| 1692 | return os.readlink(pathname, buffer); | 1689 | return os.readlink(pathname, buffer); |
| 1693 | } | 1690 | } |
| 1694 | 1691 | ||
| 1692 | /// Windows-only. Same as `readlinkW`, except the path parameter is null-terminated, WTF16 | ||
| 1693 | /// encoded. | ||
| 1694 | pub fn readlinkAbsoluteW(pathname_w: [*:0]const u16, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | ||
| 1695 | assert(path.isAbsoluteWindowsW(pathname_w)); | ||
| 1696 | return os.readlinkW(pathname_w, buffer); | ||
| 1697 | } | ||
| 1698 | |||
| 1695 | /// Same as `readLink`, except the path parameter is null-terminated. | 1699 | /// Same as `readLink`, except the path parameter is null-terminated. |
| 1696 | pub fn readLinkAbsoluteZ(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | 1700 | pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 { |
| 1697 | assert(path.isAbsoluteZ(pathname_c)); | 1701 | assert(path.isAbsoluteZ(pathname_c)); |
| 1698 | return os.readlinkZ(pathname_c, buffer); | 1702 | return os.readlinkZ(pathname_c, buffer); |
| 1699 | } | 1703 | } |
| ... | @@ -1701,6 +1705,57 @@ pub fn readLinkAbsoluteZ(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ! | ... | @@ -1701,6 +1705,57 @@ pub fn readLinkAbsoluteZ(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ! |
| 1701 | pub const readLink = @compileError("deprecated; use Dir.readLink or readLinkAbsolute"); | 1705 | pub const readLink = @compileError("deprecated; use Dir.readLink or readLinkAbsolute"); |
| 1702 | pub const readLinkC = @compileError("deprecated; use Dir.readLinkZ or readLinkAbsoluteZ"); | 1706 | pub const readLinkC = @compileError("deprecated; use Dir.readLinkZ or readLinkAbsoluteZ"); |
| 1703 | 1707 | ||
| 1708 | /// Use with `symLinkAbsolute` to specify whether the symlink will point to a file | ||
| 1709 | /// or a directory. This value is ignored on all hosts except Windows where | ||
| 1710 | /// creating symlinks to different resource types, requires different flags. | ||
| 1711 | /// By default, `symLinkAbsolute` is assumed to point to a file. | ||
| 1712 | pub const SymlinkFlags = struct { | ||
| 1713 | is_directory: bool = false, | ||
| 1714 | }; | ||
| 1715 | |||
| 1716 | /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`. | ||
| 1717 | /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent | ||
| 1718 | /// one; the latter case is known as a dangling link. | ||
| 1719 | /// If `sym_link_path` exists, it will not be overwritten. | ||
| 1720 | /// See also `symLinkAbsoluteZ` and `symLinkAbsoluteW`. | ||
| 1721 | pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags: SymlinkFlags) !void { | ||
| 1722 | if (builtin.os.tag == .wasi) { | ||
| 1723 | @compileError("symLink is not supported in WASI"); | ||
| 1724 | } | ||
| 1725 | assert(path.isAbsolute(target_path)); | ||
| 1726 | assert(path.isAbsolute(sym_link_path)); | ||
| 1727 | if (builtin.os.tag == .windows) { | ||
| 1728 | return os.windows.CreateSymbolicLink(target_path, sym_link_path, flags.is_directory); | ||
| 1729 | } | ||
| 1730 | return os.symlink(target_path, sym_link_path); | ||
| 1731 | } | ||
| 1732 | |||
| 1733 | /// Windows-only. Same as `symLinkAbsolute` except the parameters are null-terminated, WTF16 encoded. | ||
| 1734 | /// Note that this function will by default try creating a symbolic link to a file. If you would | ||
| 1735 | /// like to create a symbolic link to a directory, specify this with `SymlinkFlags{ .is_directory = true }`. | ||
| 1736 | /// See also `symLinkAbsolute`, `symLinkAbsoluteZ`. | ||
| 1737 | pub fn symLinkAbsoluteW(target_path_w: [*:0]const u16, sym_link_path_w: [*:0]const u16, flags: SymlinkFlags) !void { | ||
| 1738 | assert(path.isAbsoluteWindowsW(target_path_w)); | ||
| 1739 | assert(path.isAbsoluteWindowsW(sym_link_path_w)); | ||
| 1740 | return os.windows.CreateSymbolicLinkW(target_path_w, sym_link_path_w, flags.is_directory); | ||
| 1741 | } | ||
| 1742 | |||
| 1743 | /// Same as `symLinkAbsolute` except the parameters are null-terminated pointers. | ||
| 1744 | /// See also `symLinkAbsolute`. | ||
| 1745 | pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]const u8, flags: SymlinkFlags) !void { | ||
| 1746 | assert(path.isAbsoluteZ(target_path_c)); | ||
| 1747 | assert(path.isAbsoluteZ(sym_link_path_c)); | ||
| 1748 | if (builtin.os.tag == .windows) { | ||
| 1749 | const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c); | ||
| 1750 | const sym_link_path_w = try os.windows.cStrToWin32PrefixedFileW(sym_link_path_c); | ||
| 1751 | return os.windows.CreateSymbolicLinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr, flags.is_directory); | ||
| 1752 | } | ||
| 1753 | return os.symlinkZ(target_path_c, sym_link_path_c); | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | pub const symLink = @compileError("deprecated: use symLinkAbsolute"); | ||
| 1757 | pub const symLinkC = @compileError("deprecated: use symLinkAbsoluteC"); | ||
| 1758 | |||
| 1704 | pub const Walker = struct { | 1759 | pub const Walker = struct { |
| 1705 | stack: std.ArrayList(StackItem), | 1760 | stack: std.ArrayList(StackItem), |
| 1706 | name_buffer: std.ArrayList(u8), | 1761 | name_buffer: std.ArrayList(u8), |
lib/std/fs/test.zig+44| ... | @@ -10,6 +10,50 @@ const Dir = std.fs.Dir; | ... | @@ -10,6 +10,50 @@ const Dir = std.fs.Dir; |
| 10 | const File = std.fs.File; | 10 | const File = std.fs.File; |
| 11 | const tmpDir = testing.tmpDir; | 11 | const tmpDir = testing.tmpDir; |
| 12 | 12 | ||
| 13 | test "readLinkAbsolute" { | ||
| 14 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 15 | |||
| 16 | var tmp = tmpDir(.{}); | ||
| 17 | defer tmp.cleanup(); | ||
| 18 | |||
| 19 | // Create some targets | ||
| 20 | try tmp.dir.writeFile("file.txt", "nonsense"); | ||
| 21 | try tmp.dir.makeDir("subdir"); | ||
| 22 | |||
| 23 | // Get base abs path | ||
| 24 | var arena = ArenaAllocator.init(testing.allocator); | ||
| 25 | defer arena.deinit(); | ||
| 26 | |||
| 27 | const base_path = blk: { | ||
| 28 | const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] }); | ||
| 29 | break :blk try fs.realpathAlloc(&arena.allocator, relative_path); | ||
| 30 | }; | ||
| 31 | const allocator = &arena.allocator; | ||
| 32 | |||
| 33 | { | ||
| 34 | const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "file.txt" }); | ||
| 35 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink1" }); | ||
| 36 | |||
| 37 | // Create symbolic link by path | ||
| 38 | try fs.symLinkAbsolute(target_path, symlink_path, .{}); | ||
| 39 | try testReadlinkAbsolute(target_path, symlink_path); | ||
| 40 | } | ||
| 41 | { | ||
| 42 | const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "subdir" }); | ||
| 43 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" }); | ||
| 44 | |||
| 45 | // Create symbolic link by path | ||
| 46 | try fs.symLinkAbsolute(target_path, symlink_path, .{ .is_directory = true }); | ||
| 47 | try testReadlinkAbsolute(target_path, symlink_path); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | fn testReadlinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void { | ||
| 52 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | ||
| 53 | const given = try fs.readLinkAbsolute(symlink_path, buffer[0..]); | ||
| 54 | testing.expect(mem.eql(u8, target_path, given)); | ||
| 55 | } | ||
| 56 | |||
| 13 | test "Dir.Iterator" { | 57 | test "Dir.Iterator" { |
| 14 | var tmp_dir = tmpDir(.{ .iterate = true }); | 58 | var tmp_dir = tmpDir(.{ .iterate = true }); |
| 15 | defer tmp_dir.cleanup(); | 59 | defer tmp_dir.cleanup(); |
lib/std/os.zig+6-25| ... | @@ -1520,14 +1520,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { | ... | @@ -1520,14 +1520,6 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { |
| 1520 | } | 1520 | } |
| 1521 | } | 1521 | } |
| 1522 | 1522 | ||
| 1523 | /// Use with `symlink` to specify whether the symlink will point to a file | ||
| 1524 | /// or a directory. This value is ignored on all hosts except Windows where | ||
| 1525 | /// creating symlinks to different resource types, requires different flags. | ||
| 1526 | /// By default, symlink is assumed to point to a file. | ||
| 1527 | pub const SymlinkFlags = struct { | ||
| 1528 | is_directory: bool = false, | ||
| 1529 | }; | ||
| 1530 | |||
| 1531 | pub const SymLinkError = error{ | 1523 | pub const SymLinkError = error{ |
| 1532 | /// In WASI, this error may occur when the file descriptor does | 1524 | /// In WASI, this error may occur when the file descriptor does |
| 1533 | /// not hold the required rights to create a new symbolic link relative to it. | 1525 | /// not hold the required rights to create a new symbolic link relative to it. |
| ... | @@ -1550,37 +1542,26 @@ pub const SymLinkError = error{ | ... | @@ -1550,37 +1542,26 @@ pub const SymLinkError = error{ |
| 1550 | /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent | 1542 | /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent |
| 1551 | /// one; the latter case is known as a dangling link. | 1543 | /// one; the latter case is known as a dangling link. |
| 1552 | /// If `sym_link_path` exists, it will not be overwritten. | 1544 | /// If `sym_link_path` exists, it will not be overwritten. |
| 1553 | /// See also `symlinkC` and `symlinkW`. | 1545 | /// See also `symlinkZ. |
| 1554 | pub fn symlink(target_path: []const u8, sym_link_path: []const u8, flags: SymlinkFlags) SymLinkError!void { | 1546 | pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { |
| 1555 | if (builtin.os.tag == .wasi) { | 1547 | if (builtin.os.tag == .wasi) { |
| 1556 | @compileError("symlink is not supported in WASI; use symlinkat instead"); | 1548 | @compileError("symlink is not supported in WASI; use symlinkat instead"); |
| 1557 | } | 1549 | } |
| 1558 | if (builtin.os.tag == .windows) { | 1550 | if (builtin.os.tag == .windows) { |
| 1559 | const target_path_w = try windows.sliceToWin32PrefixedFileW(target_path); | 1551 | @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); |
| 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); | ||
| 1562 | } | 1552 | } |
| 1563 | const target_path_c = try toPosixPath(target_path); | 1553 | const target_path_c = try toPosixPath(target_path); |
| 1564 | const sym_link_path_c = try toPosixPath(sym_link_path); | 1554 | const sym_link_path_c = try toPosixPath(sym_link_path); |
| 1565 | return symlinkZ(&target_path_c, &sym_link_path_c, flags); | 1555 | return symlinkZ(&target_path_c, &sym_link_path_c); |
| 1566 | } | 1556 | } |
| 1567 | 1557 | ||
| 1568 | pub const symlinkC = @compileError("deprecated: renamed to symlinkZ"); | 1558 | pub const symlinkC = @compileError("deprecated: renamed to symlinkZ"); |
| 1569 | 1559 | ||
| 1570 | /// Windows-only. Same as `symlink` except the parameters are null-terminated, WTF16 encoded. | ||
| 1571 | /// Note that this function will by default try creating a symbolic link to a file. If you would | ||
| 1572 | /// like to create a symbolic link to a directory, specify this with `SymlinkFlags{ .is_directory = true }`. | ||
| 1573 | pub fn symlinkW(target_path: [*:0]const u16, sym_link_path: [*:0]const u16, flags: SymlinkFlags) SymLinkError!void { | ||
| 1574 | return windows.CreateSymbolicLinkW(sym_link_path, target_path, flags.is_directory); | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | /// This is the same as `symlink` except the parameters are null-terminated pointers. | 1560 | /// This is the same as `symlink` except the parameters are null-terminated pointers. |
| 1578 | /// See also `symlink`. | 1561 | /// See also `symlink`. |
| 1579 | pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8, flags: SymlinkFlags) SymLinkError!void { | 1562 | pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void { |
| 1580 | if (builtin.os.tag == .windows) { | 1563 | if (builtin.os.tag == .windows) { |
| 1581 | const target_path_w = try windows.cStrToWin32PrefixedFileW(target_path); | 1564 | @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); |
| 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); | ||
| 1584 | } | 1565 | } |
| 1585 | switch (errno(system.symlink(target_path, sym_link_path))) { | 1566 | switch (errno(system.symlink(target_path, sym_link_path))) { |
| 1586 | 0 => return, | 1567 | 0 => return, |
lib/std/os/test.zig+35-66| ... | @@ -19,6 +19,41 @@ const tmpDir = std.testing.tmpDir; | ... | @@ -19,6 +19,41 @@ const tmpDir = std.testing.tmpDir; |
| 19 | const Dir = std.fs.Dir; | 19 | const Dir = std.fs.Dir; |
| 20 | const ArenaAllocator = std.heap.ArenaAllocator; | 20 | const ArenaAllocator = std.heap.ArenaAllocator; |
| 21 | 21 | ||
| 22 | test "symlink with relative paths" { | ||
| 23 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 24 | |||
| 25 | // First, try relative paths in cwd | ||
| 26 | var cwd = fs.cwd(); | ||
| 27 | try cwd.writeFile("file.txt", "nonsense"); | ||
| 28 | |||
| 29 | if (builtin.os.tag == .windows) { | ||
| 30 | try os.windows.CreateSymbolicLink("file.txt", "symlinked", false); | ||
| 31 | } else { | ||
| 32 | try os.symlink("file.txt", "symlinked"); | ||
| 33 | } | ||
| 34 | |||
| 35 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | ||
| 36 | const given = try os.readlink("symlinked", buffer[0..]); | ||
| 37 | expect(mem.eql(u8, "file.txt", given)); | ||
| 38 | |||
| 39 | try cwd.deleteFile("file.txt"); | ||
| 40 | try cwd.deleteFile("symlinked"); | ||
| 41 | } | ||
| 42 | |||
| 43 | test "readlink on Windows" { | ||
| 44 | if (builtin.os.tag != .windows) return error.SkipZigTest; | ||
| 45 | |||
| 46 | try testReadlink("C:\\ProgramData", "C:\\Users\\All Users"); | ||
| 47 | try testReadlink("C:\\Users\\Default", "C:\\Users\\Default User"); | ||
| 48 | try testReadlink("C:\\Users", "C:\\Documents and Settings"); | ||
| 49 | } | ||
| 50 | |||
| 51 | fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { | ||
| 52 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | ||
| 53 | const given = try os.readlink(symlink_path, buffer[0..]); | ||
| 54 | expect(mem.eql(u8, target_path, given)); | ||
| 55 | } | ||
| 56 | |||
| 22 | test "fstatat" { | 57 | test "fstatat" { |
| 23 | // enable when `fstat` and `fstatat` are implemented on Windows | 58 | // enable when `fstat` and `fstatat` are implemented on Windows |
| 24 | if (builtin.os.tag == .windows) return error.SkipZigTest; | 59 | if (builtin.os.tag == .windows) return error.SkipZigTest; |
| ... | @@ -41,72 +76,6 @@ test "fstatat" { | ... | @@ -41,72 +76,6 @@ test "fstatat" { |
| 41 | expectEqual(stat, statat); | 76 | expectEqual(stat, statat); |
| 42 | } | 77 | } |
| 43 | 78 | ||
| 44 | test "readlink" { | ||
| 45 | if (builtin.os.tag == .wasi) return error.SkipZigTest; | ||
| 46 | |||
| 47 | // First, try relative paths in cwd | ||
| 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 | // Now, let's use tempdir | ||
| 62 | var tmp = tmpDir(.{}); | ||
| 63 | // defer tmp.cleanup(); | ||
| 64 | |||
| 65 | // Create some targets | ||
| 66 | try tmp.dir.writeFile("file.txt", "nonsense"); | ||
| 67 | try tmp.dir.makeDir("subdir"); | ||
| 68 | |||
| 69 | // Get base abs path | ||
| 70 | var arena = ArenaAllocator.init(testing.allocator); | ||
| 71 | defer arena.deinit(); | ||
| 72 | |||
| 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 allocator = &arena.allocator; | ||
| 79 | |||
| 80 | { | ||
| 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" }); | ||
| 83 | |||
| 84 | // Create symbolic link by path | ||
| 85 | try os.symlink(target_path, symlink_path, .{ .is_directory = false }); | ||
| 86 | try testReadlink(target_path, symlink_path); | ||
| 87 | } | ||
| 88 | { | ||
| 89 | const target_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "subdir" }); | ||
| 90 | const symlink_path = try fs.path.join(allocator, &[_][]const u8{ base_path, "symlink2" }); | ||
| 91 | |||
| 92 | // Create symbolic link by path | ||
| 93 | try os.symlink(target_path, symlink_path, .{ .is_directory = true }); | ||
| 94 | try testReadlink(target_path, symlink_path); | ||
| 95 | } | ||
| 96 | |||
| 97 | if (builtin.os.tag == .windows) { | ||
| 98 | try testReadlink("C:\\ProgramData", "C:\\Users\\All Users"); | ||
| 99 | try testReadlink("C:\\Users\\Default", "C:\\Users\\Default User"); | ||
| 100 | try testReadlink("C:\\Users", "C:\\Documents and Settings"); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { | ||
| 105 | var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; | ||
| 106 | const given = try os.readlink(symlink_path, buffer[0..]); | ||
| 107 | expect(mem.eql(u8, target_path, given)); | ||
| 108 | } | ||
| 109 | |||
| 110 | test "readlinkat" { | 79 | test "readlinkat" { |
| 111 | // enable when `readlinkat` and `symlinkat` are implemented on Windows | 80 | // enable when `readlinkat` and `symlinkat` are implemented on Windows |
| 112 | if (builtin.os.tag == .windows) return error.SkipZigTest; | 81 | if (builtin.os.tag == .windows) return error.SkipZigTest; |
lib/std/os/windows.zig+2-2| ... | @@ -609,8 +609,8 @@ pub fn CreateSymbolicLink( | ... | @@ -609,8 +609,8 @@ pub fn CreateSymbolicLink( |
| 609 | target_path: []const u8, | 609 | target_path: []const u8, |
| 610 | is_directory: bool, | 610 | is_directory: bool, |
| 611 | ) CreateSymbolicLinkError!void { | 611 | ) CreateSymbolicLinkError!void { |
| 612 | const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path); | 612 | const sym_link_path_w = try sliceToWin32PrefixedFileW(sym_link_path); |
| 613 | const target_path_w = try sliceToPrefixedFileW(target_path); | 613 | const target_path_w = try sliceToWin32PrefixedFileW(target_path); |
| 614 | return CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, is_directory); | 614 | return CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, is_directory); |
| 615 | } | 615 | } |
| 616 | 616 |