mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 11:13:08 +00:00
Finish drafting CreateSymolicLink using NT calls
This commit is contained in:
parent
99f0e64fa0
commit
4887350bf4
@ -66,15 +66,7 @@ pub const need_async_thread = std.io.is_async and switch (builtin.os.tag) {
|
|||||||
|
|
||||||
/// TODO remove the allocator requirement from this API
|
/// TODO remove the allocator requirement from this API
|
||||||
pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
|
pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {
|
||||||
const res = blk: {
|
if (cwd().symLink(existing_path, new_path, .{})) {
|
||||||
// TODO this is just a temporary until Dir.symLink is implemented on Windows
|
|
||||||
if (builtin.os.tag == .windows) {
|
|
||||||
break :blk os.windows.CreateSymbolicLink(new_path, existing_path, false);
|
|
||||||
} else {
|
|
||||||
break :blk cwd().symLink(existing_path, new_path, .{});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (res) {
|
|
||||||
return;
|
return;
|
||||||
} else |err| switch (err) {
|
} else |err| switch (err) {
|
||||||
error.PathAlreadyExists => {},
|
error.PathAlreadyExists => {},
|
||||||
@ -92,15 +84,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
|
|||||||
try crypto.randomBytes(rand_buf[0..]);
|
try crypto.randomBytes(rand_buf[0..]);
|
||||||
base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
|
base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
|
||||||
|
|
||||||
const res2 = blk: {
|
if (cwd().symLink(existing_path, new_path, .{})) {
|
||||||
// TODO this is just a temporary until Dir.symLink is implemented on Windows
|
|
||||||
if (builtin.os.tag == .windows) {
|
|
||||||
break :blk os.windows.CreateSymbolicLink(tmp_path, existing_path, false);
|
|
||||||
} else {
|
|
||||||
break :blk cwd().symLink(existing_path, new_path, .{});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if (res2) {
|
|
||||||
return rename(tmp_path, new_path);
|
return rename(tmp_path, new_path);
|
||||||
} else |err| switch (err) {
|
} else |err| switch (err) {
|
||||||
error.PathAlreadyExists => continue,
|
error.PathAlreadyExists => continue,
|
||||||
@ -1239,10 +1223,12 @@ pub const Dir = struct {
|
|||||||
flags: SymLinkFlags,
|
flags: SymLinkFlags,
|
||||||
) !void {
|
) !void {
|
||||||
if (builtin.os.tag == .wasi) {
|
if (builtin.os.tag == .wasi) {
|
||||||
return self.symLinkWasi(target_path, sym_link_path);
|
return self.symLinkWasi(target_path, sym_link_path, flags);
|
||||||
}
|
}
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
@compileError("TODO implement Dir.symLink on Windows");
|
const target_path_w = try os.windows.sliceToPrefixedFileW(target_path);
|
||||||
|
const sym_link_path_w = try os.windows.sliceToPrefixedFileW(sym_link_path);
|
||||||
|
return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);
|
||||||
}
|
}
|
||||||
const target_path_c = try os.toPosixPath(target_path);
|
const target_path_c = try os.toPosixPath(target_path);
|
||||||
const sym_link_path_c = try os.toPosixPath(sym_link_path);
|
const sym_link_path_c = try os.toPosixPath(sym_link_path);
|
||||||
@ -1250,15 +1236,41 @@ pub const Dir = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// WASI-only. Same as `symLink` except targeting WASI.
|
/// WASI-only. Same as `symLink` except targeting WASI.
|
||||||
pub fn symLinkWasi(self: Dir, target_path: []const u8, sym_link_path: []const u8, flags: SymLinkFlags) !void {
|
pub fn symLinkWasi(
|
||||||
|
self: Dir,
|
||||||
|
target_path: []const u8,
|
||||||
|
sym_link_path: []const u8,
|
||||||
|
flags: SymLinkFlags,
|
||||||
|
) !void {
|
||||||
return os.symlinkatWasi(target_path, self.fd, sym_link_path);
|
return os.symlinkatWasi(target_path, self.fd, sym_link_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `symLink`, except the pathname parameters are null-terminated.
|
/// Same as `symLink`, except the pathname parameters are null-terminated.
|
||||||
pub fn symLinkZ(self: Dir, target_path_c: [*:0]const u8, sym_link_path_c: [*:0]const u8, flags: SymLinkFlags) !void {
|
pub fn symLinkZ(
|
||||||
|
self: Dir,
|
||||||
|
target_path_c: [*:0]const u8,
|
||||||
|
sym_link_path_c: [*:0]const u8,
|
||||||
|
flags: SymLinkFlags,
|
||||||
|
) !void {
|
||||||
|
if (builtin.os.tag == .windows) {
|
||||||
|
const target_path_w = try os.windows.cStrToPrefixedFileW(target_path_c);
|
||||||
|
const sym_link_path_w = try os.windows.cStrToPrefixedFileW(sym_link_path_c);
|
||||||
|
return self.symLinkW(target_path_w.span(), sym_link_path_w.span(), flags);
|
||||||
|
}
|
||||||
return os.symlinkatZ(target_path_c, self.fd, sym_link_path_c);
|
return os.symlinkatZ(target_path_c, self.fd, sym_link_path_c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Windows-only. Same as `symLink` except the pathname parameters
|
||||||
|
/// are null-terminated, WTF16 encoded.
|
||||||
|
pub fn symLinkW(
|
||||||
|
self: Dir,
|
||||||
|
target_path_w: [:0]const u16,
|
||||||
|
sym_link_path_w: [:0]const u16,
|
||||||
|
flags: SymLinkFlags,
|
||||||
|
) !void {
|
||||||
|
return os.windows.CreateSymbolicLinkW(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
|
||||||
|
}
|
||||||
|
|
||||||
/// Read value of a symbolic link.
|
/// Read value of a symbolic link.
|
||||||
/// The return value is a slice of `buffer`, from index `0`.
|
/// The return value is a slice of `buffer`, from index `0`.
|
||||||
/// Asserts that the path parameter has no null bytes.
|
/// Asserts that the path parameter has no null bytes.
|
||||||
@ -1761,10 +1773,10 @@ pub fn readLinkAbsoluteZ(pathname_c: [*:0]const u8, buffer: *[MAX_PATH_BYTES]u8)
|
|||||||
pub const readLink = @compileError("deprecated; use Dir.readLink or readLinkAbsolute");
|
pub const readLink = @compileError("deprecated; use Dir.readLink or readLinkAbsolute");
|
||||||
pub const readLinkC = @compileError("deprecated; use Dir.readLinkZ or readLinkAbsoluteZ");
|
pub const readLinkC = @compileError("deprecated; use Dir.readLinkZ or readLinkAbsoluteZ");
|
||||||
|
|
||||||
/// Use with `symLinkAbsolute` to specify whether the symlink will point to a file
|
/// Use with `Dir.symLink` and `symLinkAbsolute` to specify whether the symlink
|
||||||
/// or a directory. This value is ignored on all hosts except Windows where
|
/// will point to a file or a directory. This value is ignored on all hosts
|
||||||
/// creating symlinks to different resource types, requires different flags.
|
/// except Windows where creating symlinks to different resource types, requires
|
||||||
/// By default, `symLinkAbsolute` is assumed to point to a file.
|
/// different flags. By default, `symLinkAbsolute` is assumed to point to a file.
|
||||||
pub const SymLinkFlags = struct {
|
pub const SymLinkFlags = struct {
|
||||||
is_directory: bool = false,
|
is_directory: bool = false,
|
||||||
};
|
};
|
||||||
@ -1781,7 +1793,7 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
|
|||||||
assert(path.isAbsolute(target_path));
|
assert(path.isAbsolute(target_path));
|
||||||
assert(path.isAbsolute(sym_link_path));
|
assert(path.isAbsolute(sym_link_path));
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
return os.windows.CreateSymbolicLink(sym_link_path, target_path, flags.is_directory);
|
return os.windows.CreateSymbolicLink(null, sym_link_path, target_path, flags.is_directory);
|
||||||
}
|
}
|
||||||
return os.symlink(target_path, sym_link_path);
|
return os.symlink(target_path, sym_link_path);
|
||||||
}
|
}
|
||||||
@ -1790,10 +1802,10 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
|
|||||||
/// Note that this function will by default try creating a symbolic link to a file. If you would
|
/// 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, specify this with `SymLinkFlags{ .is_directory = true }`.
|
/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.
|
||||||
/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.
|
/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.
|
||||||
pub fn symLinkAbsoluteW(target_path_w: [*:0]const u16, sym_link_path_w: [*:0]const u16, flags: SymLinkFlags) !void {
|
pub fn symLinkAbsoluteW(target_path_w: [:0]const u16, sym_link_path_w: [:0]const u16, flags: SymLinkFlags) !void {
|
||||||
assert(path.isAbsoluteWindowsW(target_path_w));
|
assert(path.isAbsoluteWindowsW(target_path_w));
|
||||||
assert(path.isAbsoluteWindowsW(sym_link_path_w));
|
assert(path.isAbsoluteWindowsW(sym_link_path_w));
|
||||||
return os.windows.CreateSymbolicLinkW(sym_link_path_w, target_path_w, flags.is_directory);
|
return os.windows.CreateSymbolicLinkW(null, sym_link_path_w, target_path_w, flags.is_directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.
|
/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.
|
||||||
|
|||||||
@ -27,7 +27,7 @@ test "symlink with relative paths" {
|
|||||||
try cwd.writeFile("file.txt", "nonsense");
|
try cwd.writeFile("file.txt", "nonsense");
|
||||||
|
|
||||||
if (builtin.os.tag == .windows) {
|
if (builtin.os.tag == .windows) {
|
||||||
try os.windows.CreateSymbolicLink("symlinked", "file.txt", false);
|
try os.windows.CreateSymbolicLink(cwd.fd, "symlinked", "file.txt", false);
|
||||||
} else {
|
} else {
|
||||||
try os.symlink("file.txt", "symlinked");
|
try os.symlink("file.txt", "symlinked");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -602,9 +602,34 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
|
|||||||
return buffer[0..end_index];
|
return buffer[0..end_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const CreateSymbolicLinkError = error{ AccessDenied, PathAlreadyExists, FileNotFound, NameTooLong, InvalidUtf8, BadPathName, Unexpected };
|
pub const CreateSymbolicLinkError = error{
|
||||||
|
AccessDenied,
|
||||||
|
PathAlreadyExists,
|
||||||
|
FileNotFound,
|
||||||
|
NameTooLong,
|
||||||
|
InvalidUtf8,
|
||||||
|
BadPathName,
|
||||||
|
NoDevice,
|
||||||
|
Unexpected,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: [:0]const u16, target_path: [:0]const u16, is_directory: bool) CreateSymbolicLinkError!void {
|
pub fn CreateSymbolicLink(
|
||||||
|
dir: ?HANDLE,
|
||||||
|
sym_link_path: []const u8,
|
||||||
|
target_path: []const u8,
|
||||||
|
is_directory: bool,
|
||||||
|
) CreateSymbolicLinkError!void {
|
||||||
|
const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path);
|
||||||
|
const target_path_w = try sliceToPrefixedFileW(target_path);
|
||||||
|
return CreateSymbolicLinkW(dir, sym_link_path_w.span(), target_path_w.span(), is_directory);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn CreateSymbolicLinkW(
|
||||||
|
dir: ?HANDLE,
|
||||||
|
sym_link_path: [:0]const u16,
|
||||||
|
target_path: [:0]const u16,
|
||||||
|
is_directory: bool,
|
||||||
|
) CreateSymbolicLinkError!void {
|
||||||
const SYMLINK_DATA = extern struct {
|
const SYMLINK_DATA = extern struct {
|
||||||
ReparseTag: ULONG,
|
ReparseTag: ULONG,
|
||||||
ReparseDataLength: USHORT,
|
ReparseDataLength: USHORT,
|
||||||
@ -660,7 +685,7 @@ pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: [:0]const u16, target_
|
|||||||
.OBJECT_NAME_INVALID => unreachable,
|
.OBJECT_NAME_INVALID => unreachable,
|
||||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
||||||
// .NO_MEDIA_IN_DEVICE => return error.NoDevice,
|
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
|
||||||
.INVALID_PARAMETER => unreachable,
|
.INVALID_PARAMETER => unreachable,
|
||||||
.ACCESS_DENIED => return error.AccessDenied,
|
.ACCESS_DENIED => return error.AccessDenied,
|
||||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||||
@ -674,7 +699,11 @@ pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: [:0]const u16, target_
|
|||||||
.creation = FILE_CREATE,
|
.creation = FILE_CREATE,
|
||||||
.io_mode = .blocking,
|
.io_mode = .blocking,
|
||||||
}) catch |err| switch (err) {
|
}) catch |err| switch (err) {
|
||||||
else => |e| unreachable,
|
error.WouldBlock => unreachable,
|
||||||
|
error.IsDir => return error.PathAlreadyExists,
|
||||||
|
error.PipeBusy => unreachable,
|
||||||
|
error.SharingViolation => return error.AccessDenied,
|
||||||
|
else => |e| return e,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
defer CloseHandle(symlink_handle);
|
defer CloseHandle(symlink_handle);
|
||||||
@ -702,53 +731,6 @@ pub fn NtCreateSymbolicLinkW(dir: ?HANDLE, sym_link_path: [:0]const u16, target_
|
|||||||
_ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null, null);
|
_ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn CreateSymbolicLink(
|
|
||||||
sym_link_path: []const u8,
|
|
||||||
target_path: []const u8,
|
|
||||||
is_directory: bool,
|
|
||||||
) CreateSymbolicLinkError!void {
|
|
||||||
const sym_link_path_w = try sliceToWin32PrefixedFileW(sym_link_path);
|
|
||||||
const target_path_w = try sliceToWin32PrefixedFileW(target_path);
|
|
||||||
return CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, is_directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn CreateSymbolicLinkW(
|
|
||||||
sym_link_path: [*:0]const u16,
|
|
||||||
target_path: [*:0]const u16,
|
|
||||||
is_directory: bool,
|
|
||||||
) CreateSymbolicLinkError!void {
|
|
||||||
// Previously, until Win 10 Creators Update, creating symbolic links required
|
|
||||||
// SeCreateSymbolicLink privilege. Currently, this is no longer required if the
|
|
||||||
// OS is in Developer Mode; however, SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
|
|
||||||
// must be added to the input flags.
|
|
||||||
const flags = if (is_directory) SYMBOLIC_LINK_FLAG_DIRECTORY else 0;
|
|
||||||
if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) == 0) {
|
|
||||||
switch (kernel32.GetLastError()) {
|
|
||||||
.INVALID_PARAMETER => {
|
|
||||||
// If we're on Windows pre Creators Update, SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
|
|
||||||
// flag is an invalid parameter, in which case repeat without the flag.
|
|
||||||
if (kernel32.CreateSymbolicLinkW(sym_link_path, target_path, flags) == 0) {
|
|
||||||
switch (kernel32.GetLastError()) {
|
|
||||||
.PRIVILEGE_NOT_HELD => return error.AccessDenied,
|
|
||||||
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
||||||
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
||||||
.ACCESS_DENIED => return error.AccessDenied,
|
|
||||||
.ALREADY_EXISTS => return error.PathAlreadyExists,
|
|
||||||
else => |err| return unexpectedError(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
.PRIVILEGE_NOT_HELD => return error.AccessDenied,
|
|
||||||
.FILE_NOT_FOUND => return error.FileNotFound,
|
|
||||||
.PATH_NOT_FOUND => return error.FileNotFound,
|
|
||||||
.ACCESS_DENIED => return error.AccessDenied,
|
|
||||||
.ALREADY_EXISTS => return error.PathAlreadyExists,
|
|
||||||
else => |err| return unexpectedError(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const DeleteFileError = error{
|
pub const DeleteFileError = error{
|
||||||
FileNotFound,
|
FileNotFound,
|
||||||
AccessDenied,
|
AccessDenied,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user