diff --git a/lib/std/fs.zig b/lib/std/fs.zig index 0feaf69d67..6b4c09845c 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -69,7 +69,7 @@ pub const need_async_thread = std.io.is_async and switch (builtin.os.tag) { /// TODO remove the allocator requirement from this API pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { - if (symLink(existing_path, new_path)) { + if (symLink(existing_path, new_path, .{})) { return; } else |err| switch (err) { error.PathAlreadyExists => {}, @@ -87,7 +87,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: try crypto.randomBytes(rand_buf[0..]); base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf); - if (symLink(existing_path, tmp_path)) { + if (symLink(existing_path, tmp_path, .{})) { return rename(tmp_path, new_path); } else |err| switch (err) { error.PathAlreadyExists => continue, diff --git a/lib/std/os.zig b/lib/std/os.zig index cfcab92bd4..6e614b8006 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -1520,6 +1520,14 @@ pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { } } +/// Use with `symlink` to specify whether the symlink will point to a file +/// or a directory. This value is ignored on all hosts except Windows where +/// creating symlinks to different resource types, requires different flags. +/// By default, symlink is assumed to point to a file. +pub const SymlinkFlags = struct{ + is_directory: bool = false, +}; + pub const SymLinkError = error{ /// In WASI, this error may occur when the file descriptor does /// not hold the required rights to create a new symbolic link relative to it. @@ -1541,39 +1549,34 @@ pub const SymLinkError = error{ /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`. /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent /// one; the latter case is known as a dangling link. -/// On Windows, it is only legal to create a symbolic link to an existing resource. Furthermore, -/// this function will by default try creating a symbolic link to a file. If you would like to -/// create a symbolic link to a directory instead, see `symlinkW` for more information how to -/// do that. /// If `sym_link_path` exists, it will not be overwritten. /// See also `symlinkC` and `symlinkW`. -pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { +pub fn symlink(target_path: []const u8, sym_link_path: []const u8, flags: SymlinkFlags) SymLinkError!void { if (builtin.os.tag == .wasi) { @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); - return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr); + return symlinkW(target_path_w.span().ptr, sym_link_path_w.span().ptr, flags); } const target_path_c = try toPosixPath(target_path); const sym_link_path_c = try toPosixPath(sym_link_path); - return symlinkZ(&target_path_c, &sym_link_path_c); + return symlinkZ(&target_path_c, &sym_link_path_c, flags); } pub const symlinkC = @compileError("deprecated: renamed to symlinkZ"); /// Windows-only. Same as `symlink` except the parameters are null-terminated, WTF16 encoded. /// Note that this function will by default try creating a symbolic link to a file. If you would -/// like to create a symbolic link to a directory, use `std.os.windows.CreateSymbolicLinkW` directly -/// specifying as flags `std.os.windows.CreateSymbolicLinkFlags.Directory`. -pub fn symlinkW(target_path: [*:0]const u16, sym_link_path: [*:0]const u16) SymLinkError!void { - return windows.CreateSymbolicLinkW(sym_link_path, target_path, false); +/// like to create a symbolic link to a directory, specify this with `SymlinkFlags{ .is_directory = true }`. +pub fn symlinkW(target_path: [*:0]const u16, sym_link_path: [*:0]const u16, flags: SymlinkFlags) SymLinkError!void { + return windows.CreateSymbolicLinkW(sym_link_path, target_path, flags.is_directory); } /// This is the same as `symlink` except the parameters are null-terminated pointers. /// See also `symlink`. -pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void { +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); diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 597ec88951..5bb1ef38db 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -48,7 +48,7 @@ test "readlink" { { var cwd = fs.cwd(); try cwd.writeFile("file.txt", "nonsense"); - try os.symlink("file.txt", "symlinked"); + try os.symlink("file.txt", "symlinked", .{}); var buffer: [fs.MAX_PATH_BYTES]u8 = undefined; const given = try os.readlink("symlinked", buffer[0..]); @@ -81,7 +81,7 @@ test "readlink" { std.debug.warn("symlink_path={}\n", .{symlink_path}); // create symbolic link by path - try os.symlink(target_path, symlink_path); + try os.symlink(target_path, symlink_path, .{}); // now, read the link and verify var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;