diff --git a/lib/std/fs.zig b/lib/std/fs.zig index c03b36d718..d14feedb59 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -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); } diff --git a/lib/std/os.zig b/lib/std/os.zig index 56e8374bdb..664b6c35b9 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -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, diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 7310562d64..cfa358ca72 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -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"); } diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 3b810951e0..64998bfbb0 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -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;