Refactor internal Win routines to reuse OpenFile

This covers mainly `ReadLink` and `CreateSymolicLink` functions.
master
Jakub Konka 2020-07-30 17:50:49 +02:00
parent a89d5cfc3e
commit 66bbe4ec4c
4 changed files with 83 additions and 186 deletions

View File

@ -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);
}

View File

@ -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,

View File

@ -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");
}

View File

@ -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;