Refactor internal Win routines to reuse OpenFile
This covers mainly `ReadLink` and `CreateSymolicLink` functions.master
parent
a89d5cfc3e
commit
66bbe4ec4c
|
@ -1261,11 +1261,11 @@ pub const Dir = struct {
|
|||
/// are null-terminated, WTF16 encoded.
|
||||
pub fn symLinkW(
|
||||
self: Dir,
|
||||
target_path_w: [:0]const u16,
|
||||
sym_link_path_w: [:0]const u16,
|
||||
target_path_w: []const u16,
|
||||
sym_link_path_w: []const u16,
|
||||
flags: SymLinkFlags,
|
||||
) !void {
|
||||
return os.windows.CreateSymbolicLinkW(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
|
||||
return os.windows.CreateSymbolicLink(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
|
||||
}
|
||||
|
||||
/// Read value of a symbolic link.
|
||||
|
@ -1276,7 +1276,8 @@ pub const Dir = struct {
|
|||
return self.readLinkWasi(sub_path, buffer);
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
return os.windows.ReadLink(self.fd, sub_path, buffer);
|
||||
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.readLinkW(sub_path_w.span(), buffer);
|
||||
}
|
||||
const sub_path_c = try os.toPosixPath(sub_path);
|
||||
return self.readLinkZ(&sub_path_c, buffer);
|
||||
|
@ -1293,15 +1294,15 @@ pub const Dir = struct {
|
|||
pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
|
||||
return self.readLinkW(sub_path_w, buffer);
|
||||
return self.readLinkW(sub_path_w.span(), buffer);
|
||||
}
|
||||
return os.readlinkatZ(self.fd, sub_path_c, buffer);
|
||||
}
|
||||
|
||||
/// Windows-only. Same as `readLink` except the pathname parameter
|
||||
/// is null-terminated, WTF16 encoded.
|
||||
pub fn readLinkW(self: Dir, sub_path_w: [*:0]const u16, buffer: []u8) ![]u8 {
|
||||
return os.windows.ReadLinkW(self.fd, sub_path_w, buffer);
|
||||
pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
|
||||
return os.windows.ReadLink(self.fd, sub_path_w, buffer);
|
||||
}
|
||||
|
||||
/// On success, caller owns returned buffer.
|
||||
|
@ -1811,7 +1812,9 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
|
|||
assert(path.isAbsolute(target_path));
|
||||
assert(path.isAbsolute(sym_link_path));
|
||||
if (builtin.os.tag == .windows) {
|
||||
return os.windows.CreateSymbolicLink(null, sym_link_path, target_path, flags.is_directory);
|
||||
const target_path_w = try os.windows.sliceToPrefixedFileW(target_path);
|
||||
const sym_link_path_w = try os.windows.sliceToPrefixedFileW(sym_link_path);
|
||||
return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
|
||||
}
|
||||
return os.symlink(target_path, sym_link_path);
|
||||
}
|
||||
|
@ -1820,10 +1823,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
|
||||
/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.
|
||||
/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.
|
||||
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(sym_link_path_w));
|
||||
return os.windows.CreateSymbolicLinkW(null, sym_link_path_w, target_path_w, flags.is_directory);
|
||||
pub fn symLinkAbsoluteW(target_path_w: []const u16, sym_link_path_w: []const u16, flags: SymLinkFlags) !void {
|
||||
assert(path.isAbsoluteWindowsWTF16(target_path_w));
|
||||
assert(path.isAbsoluteWindowsWTF16(sym_link_path_w));
|
||||
return os.windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);
|
||||
}
|
||||
|
||||
/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.
|
||||
|
@ -1834,7 +1837,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons
|
|||
if (builtin.os.tag == .windows) {
|
||||
const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c);
|
||||
const sym_link_path_w = try os.windows.cStrToWin32PrefixedFileW(sym_link_path_c);
|
||||
return os.windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, flags.is_directory);
|
||||
return os.windows.CreateSymbolicLink(sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
|
||||
}
|
||||
return os.symlinkZ(target_path_c, sym_link_path_c);
|
||||
}
|
||||
|
|
|
@ -2087,7 +2087,7 @@ pub fn renameatW(
|
|||
pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
|
||||
return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);
|
||||
return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
|
||||
} else if (builtin.os.tag == .wasi) {
|
||||
return mkdiratWasi(dir_fd, sub_dir_path, mode);
|
||||
} else {
|
||||
|
@ -2145,13 +2145,13 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
|
||||
const sub_dir_handle = windows.OpenFile(std.mem.spanZ(sub_path_w), .{
|
||||
pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
|
||||
const sub_dir_handle = windows.OpenFile(sub_path_w, .{
|
||||
.dir = dir_fd,
|
||||
.access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
|
||||
.creation = windows.FILE_CREATE,
|
||||
.io_mode = .blocking,
|
||||
.expect_dir = true,
|
||||
.open_dir = true,
|
||||
}) catch |err| switch (err) {
|
||||
error.IsDir => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
|
@ -2187,7 +2187,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
|
|||
@compileError("mkdir is not supported in WASI; use mkdirat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
|
||||
return mkdirW(dir_path_w.span().ptr, mode);
|
||||
return mkdirW(dir_path_w.span(), mode);
|
||||
} else {
|
||||
const dir_path_c = try toPosixPath(dir_path);
|
||||
return mkdirZ(&dir_path_c, mode);
|
||||
|
@ -2198,7 +2198,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
|
|||
pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
|
||||
return mkdirW(dir_path_w.span().ptr, mode);
|
||||
return mkdirW(dir_path_w.span(), mode);
|
||||
}
|
||||
switch (errno(system.mkdir(dir_path, mode))) {
|
||||
0 => return,
|
||||
|
@ -2220,13 +2220,13 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
|
|||
}
|
||||
|
||||
/// Windows-only. Same as `mkdir` but the parameters is null-terminated, WTF16 encoded.
|
||||
pub fn mkdirW(dir_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
|
||||
const sub_dir_handle = windows.OpenFile(std.mem.spanZ(dir_path_w), .{
|
||||
pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
|
||||
const sub_dir_handle = windows.OpenFile(dir_path_w, .{
|
||||
.dir = std.fs.cwd().fd,
|
||||
.access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
|
||||
.creation = windows.FILE_CREATE,
|
||||
.io_mode = .blocking,
|
||||
.expect_dir = true,
|
||||
.open_dir = true,
|
||||
}) catch |err| switch (err) {
|
||||
error.IsDir => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
|
@ -2379,7 +2379,8 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
|||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("readlink is not supported in WASI; use readlinkat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return readlinkW(file_path_w.span(), out_buffer);
|
||||
} else {
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return readlinkZ(&file_path_c, out_buffer);
|
||||
|
@ -2390,15 +2391,15 @@ pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
|
|||
|
||||
/// Windows-only. Same as `readlink` except `file_path` is null-terminated, WTF16 encoded.
|
||||
/// See also `readlinkZ`.
|
||||
pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
return windows.ReadLinkW(std.fs.cwd().fd, file_path, out_buffer);
|
||||
pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
|
||||
}
|
||||
|
||||
/// Same as `readlink` except `file_path` is null-terminated.
|
||||
pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);
|
||||
return readlinkW(file_path_w.span().ptr, out_buffer);
|
||||
return readlinkW(file_path_w.span(), out_buffer);
|
||||
}
|
||||
const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
|
||||
switch (errno(rc)) {
|
||||
|
@ -2424,7 +2425,8 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink
|
|||
return readlinkatWasi(dirfd, file_path, out_buffer);
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
return windows.ReadLink(dirfd, file_path, out_buffer);
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return readlinkatW(dirfd, file_path_w.span(), out_buffer);
|
||||
}
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return readlinkatZ(dirfd, &file_path_c, out_buffer);
|
||||
|
@ -2454,8 +2456,8 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read
|
|||
|
||||
/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
|
||||
/// See also `readlinkat`.
|
||||
pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
return windows.ReadLinkW(dirfd, file_path, out_buffer);
|
||||
pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
return windows.ReadLink(dirfd, file_path, out_buffer);
|
||||
}
|
||||
|
||||
/// Same as `readlinkat` except `file_path` is null-terminated.
|
||||
|
@ -2463,7 +2465,7 @@ pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) Rea
|
|||
pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);
|
||||
return readlinkatW(dirfd, file_path_w.span(), out_buffer);
|
||||
}
|
||||
const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
|
||||
switch (errno(rc)) {
|
||||
|
@ -3984,7 +3986,7 @@ pub const RealPathError = error{
|
|||
pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const pathname_w = try windows.sliceToPrefixedFileW(pathname);
|
||||
return realpathW(pathname_w.span().ptr, out_buffer);
|
||||
return realpathW(pathname_w.span(), out_buffer);
|
||||
}
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths");
|
||||
|
@ -3999,7 +4001,7 @@ pub const realpathC = @compileError("deprecated: renamed realpathZ");
|
|||
pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const pathname_w = try windows.cStrToPrefixedFileW(pathname);
|
||||
return realpathW(pathname_w.span().ptr, out_buffer);
|
||||
return realpathW(pathname_w.span(), out_buffer);
|
||||
}
|
||||
if (builtin.os.tag == .linux and !builtin.link_libc) {
|
||||
const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {
|
||||
|
@ -4037,7 +4039,7 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
|
|||
|
||||
/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
|
||||
/// TODO use ntdll for better semantics
|
||||
pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
|
||||
pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
|
||||
const w = windows;
|
||||
|
||||
const dir = std.fs.cwd().fd;
|
||||
|
@ -4045,20 +4047,20 @@ pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) Real
|
|||
const share_access = w.FILE_SHARE_READ;
|
||||
const creation = w.FILE_OPEN;
|
||||
const h_file = blk: {
|
||||
const res = w.OpenFile(std.mem.spanZ(pathname), .{
|
||||
const res = w.OpenFile(pathname, .{
|
||||
.dir = dir,
|
||||
.access_mask = access_mask,
|
||||
.share_access = share_access,
|
||||
.creation = creation,
|
||||
.io_mode = .blocking,
|
||||
}) catch |err| switch (err) {
|
||||
error.IsDir => break :blk w.OpenFile(std.mem.spanZ(pathname), .{
|
||||
error.IsDir => break :blk w.OpenFile(pathname, .{
|
||||
.dir = dir,
|
||||
.access_mask = access_mask,
|
||||
.share_access = share_access,
|
||||
.creation = creation,
|
||||
.io_mode = .blocking,
|
||||
.expect_dir = true,
|
||||
.open_dir = true,
|
||||
}) catch |er| switch (er) {
|
||||
error.WouldBlock => unreachable,
|
||||
else => |e2| return e2,
|
||||
|
|
|
@ -27,7 +27,7 @@ test "symlink with relative paths" {
|
|||
try cwd.writeFile("file.txt", "nonsense");
|
||||
|
||||
if (builtin.os.tag == .windows) {
|
||||
try os.windows.CreateSymbolicLink(cwd.fd, "symlinked", "file.txt", false);
|
||||
try os.windows.CreateSymbolicLink(cwd.fd, &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' }, &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false);
|
||||
} else {
|
||||
try os.symlink("file.txt", "symlinked");
|
||||
}
|
||||
|
@ -85,7 +85,7 @@ test "readlinkat" {
|
|||
|
||||
// create a symbolic link
|
||||
if (builtin.os.tag == .windows) {
|
||||
try os.windows.CreateSymbolicLink(tmp.dir.fd, "link", "file.txt", false);
|
||||
try os.windows.CreateSymbolicLink(tmp.dir.fd, &[_]u16{ 'l', 'i', 'n', 'k' }, &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false);
|
||||
} else {
|
||||
try os.symlinkat("file.txt", tmp.dir.fd, "link");
|
||||
}
|
||||
|
|
|
@ -69,16 +69,21 @@ pub const OpenFileOptions = struct {
|
|||
share_access_nonblocking: bool = false,
|
||||
creation: ULONG,
|
||||
io_mode: std.io.ModeOverride,
|
||||
expect_dir: bool = false,
|
||||
/// If true, tries to open path as a directory.
|
||||
/// Defaults to false.
|
||||
open_dir: bool = false,
|
||||
/// If false, tries to open path as a reparse point without dereferencing it.
|
||||
/// Defaults to true.
|
||||
follow_symlinks: bool = true,
|
||||
};
|
||||
|
||||
/// TODO when share_access_nonblocking is false, this implementation uses
|
||||
/// untinterruptible sleep() to block. This is not the final iteration of the API.
|
||||
pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
|
||||
if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.expect_dir) {
|
||||
if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir and options.follow_symlinks) {
|
||||
return error.IsDir;
|
||||
}
|
||||
if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.expect_dir) {
|
||||
if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir and options.follow_symlinks) {
|
||||
return error.IsDir;
|
||||
}
|
||||
|
||||
|
@ -105,8 +110,8 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
|
|||
var delay: usize = 1;
|
||||
while (true) {
|
||||
const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
|
||||
const file_or_dir_flag: ULONG = if (options.expect_dir) FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT else FILE_NON_DIRECTORY_FILE;
|
||||
const flags: ULONG = file_or_dir_flag | blocking_flag;
|
||||
const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE | FILE_OPEN_FOR_BACKUP_INTENT else FILE_NON_DIRECTORY_FILE;
|
||||
const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else FILE_OPEN_REPARSE_POINT;
|
||||
const rc = ntdll.NtCreateFile(
|
||||
&result,
|
||||
options.access_mask,
|
||||
|
@ -143,7 +148,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
|
|||
.PIPE_BUSY => return error.PipeBusy,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
.FILE_IS_A_DIRECTORY => if (options.expect_dir) unreachable else return error.IsDir,
|
||||
.FILE_IS_A_DIRECTORY => if (options.open_dir) unreachable else return error.IsDir,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
@ -598,27 +603,14 @@ pub const CreateSymbolicLinkError = error{
|
|||
PathAlreadyExists,
|
||||
FileNotFound,
|
||||
NameTooLong,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
NoDevice,
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
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,
|
||||
sym_link_path: []const u16,
|
||||
target_path: []const u16,
|
||||
is_directory: bool,
|
||||
) CreateSymbolicLinkError!void {
|
||||
const SYMLINK_DATA = extern struct {
|
||||
|
@ -632,70 +624,18 @@ pub fn CreateSymbolicLinkW(
|
|||
Flags: ULONG,
|
||||
};
|
||||
|
||||
var symlink_handle: HANDLE = undefined;
|
||||
if (is_directory) {
|
||||
const sym_link_len_bytes = math.cast(u16, sym_link_path.len * 2) catch |err| switch (err) {
|
||||
error.Overflow => return error.NameTooLong,
|
||||
};
|
||||
var nt_name = UNICODE_STRING{
|
||||
.Length = sym_link_len_bytes,
|
||||
.MaximumLength = sym_link_len_bytes,
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sym_link_path.ptr)),
|
||||
};
|
||||
|
||||
if (sym_link_path[0] == '.' and sym_link_path[1] == 0) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
nt_name.Length = 0;
|
||||
}
|
||||
|
||||
var attr = OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sym_link_path)) null else dir,
|
||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
||||
.ObjectName = &nt_name,
|
||||
.SecurityDescriptor = null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
const rc = ntdll.NtCreateFile(
|
||||
&symlink_handle,
|
||||
GENERIC_READ | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_SHARE_READ,
|
||||
FILE_CREATE,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
switch (rc) {
|
||||
.SUCCESS => {},
|
||||
.OBJECT_NAME_INVALID => unreachable,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
} else {
|
||||
symlink_handle = OpenFile(sym_link_path, .{
|
||||
.access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
|
||||
.dir = dir,
|
||||
.creation = FILE_CREATE,
|
||||
.io_mode = .blocking,
|
||||
}) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable,
|
||||
error.IsDir => return error.PathAlreadyExists,
|
||||
error.PipeBusy => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
const symlink_handle = OpenFile(sym_link_path, .{
|
||||
.access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
|
||||
.dir = dir,
|
||||
.creation = FILE_CREATE,
|
||||
.io_mode = .blocking,
|
||||
.open_dir = is_directory,
|
||||
}) catch |err| switch (err) {
|
||||
error.IsDir => return error.PathAlreadyExists,
|
||||
error.WouldBlock => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer CloseHandle(symlink_handle);
|
||||
|
||||
// prepare reparse data buffer
|
||||
|
@ -726,72 +666,24 @@ pub const ReadLinkError = error{
|
|||
Unexpected,
|
||||
NameTooLong,
|
||||
UnsupportedReparsePointType,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
};
|
||||
|
||||
pub fn ReadLink(
|
||||
dir: ?HANDLE,
|
||||
sub_path: []const u8,
|
||||
out_buffer: []u8,
|
||||
) ReadLinkError![]u8 {
|
||||
const sub_path_w = try sliceToPrefixedFileW(sub_path);
|
||||
return ReadLinkW(dir, sub_path_w.span().ptr, out_buffer);
|
||||
}
|
||||
|
||||
pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
|
||||
error.Overflow => return error.NameTooLong,
|
||||
pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
const result_handle = OpenFile(sub_path_w, .{
|
||||
.dir = dir,
|
||||
.access_mask = FILE_READ_ATTRIBUTES,
|
||||
.share_access = FILE_SHARE_READ,
|
||||
.creation = FILE_OPEN,
|
||||
.io_mode = .blocking,
|
||||
.follow_symlinks = false,
|
||||
}) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
error.IsDir => unreachable,
|
||||
error.NoDevice => unreachable,
|
||||
error.PathAlreadyExists => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
var nt_name = UNICODE_STRING{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
|
||||
};
|
||||
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
nt_name.Length = 0;
|
||||
}
|
||||
|
||||
var attr = OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
|
||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
||||
.ObjectName = &nt_name,
|
||||
.SecurityDescriptor = null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
var result_handle: HANDLE = undefined;
|
||||
const rc = ntdll.NtCreateFile(
|
||||
&result_handle,
|
||||
FILE_READ_ATTRIBUTES,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_SHARE_READ,
|
||||
FILE_OPEN,
|
||||
FILE_OPEN_REPARSE_POINT,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
switch (rc) {
|
||||
.SUCCESS => {},
|
||||
.OBJECT_NAME_INVALID => unreachable,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.NO_MEDIA_IN_DEVICE => return error.FileNotFound,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.SHARING_VIOLATION => return error.AccessDenied,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.PIPE_BUSY => return error.AccessDenied,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => unreachable,
|
||||
.FILE_IS_A_DIRECTORY => unreachable,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
defer CloseHandle(result_handle);
|
||||
|
||||
var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
|
||||
|
|
Loading…
Reference in New Issue