authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-21 19:22:22+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2020-07-22 08:51:23+02:00
log3d41d3fb6e875192ab5f859ca4ef95c894df7fe4
tree7df319939d7cce48ff36cb1d8f41928cc3bcd30d
parent4887350bf420db3193569e8adb30937e6379db0c

Draft out ReadLinkW using NT primitives


2 files changed, 144 insertions(+), 195 deletions(-)

lib/std/os.zig+4-67
...@@ -2364,8 +2364,7 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {...@@ -2364,8 +2364,7 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
2364 if (builtin.os.tag == .wasi) {2364 if (builtin.os.tag == .wasi) {
2365 @compileError("readlink is not supported in WASI; use readlinkat instead");2365 @compileError("readlink is not supported in WASI; use readlinkat instead");
2366 } else if (builtin.os.tag == .windows) {2366 } else if (builtin.os.tag == .windows) {
2367 const file_path_w = try windows.sliceToWin32PrefixedFileW(file_path);2367 return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
2368 return readlinkW(file_path_w.span().ptr, out_buffer);
2369 } else {2368 } else {
2370 const file_path_c = try toPosixPath(file_path);2369 const file_path_c = try toPosixPath(file_path);
2371 return readlinkZ(&file_path_c, out_buffer);2370 return readlinkZ(&file_path_c, out_buffer);
...@@ -2377,56 +2376,7 @@ pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");...@@ -2377,56 +2376,7 @@ pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
2377/// Windows-only. Same as `readlink` except `file_path` is null-terminated, WTF16 encoded.2376/// Windows-only. Same as `readlink` except `file_path` is null-terminated, WTF16 encoded.
2378/// See also `readlinkZ`.2377/// See also `readlinkZ`.
2379pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {2378pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2380 const w = windows;2379 return windows.ReadLinkW(std.fs.cwd().fd, file_path, out_buffer);
2381
2382 const sharing = w.FILE_SHARE_DELETE | w.FILE_SHARE_READ | w.FILE_SHARE_WRITE;
2383 const disposition = w.OPEN_EXISTING;
2384 const flags = w.FILE_FLAG_BACKUP_SEMANTICS | w.FILE_FLAG_OPEN_REPARSE_POINT;
2385 const handle = w.CreateFileW(file_path, 0, sharing, null, disposition, flags, null) catch |err| {
2386 switch (err) {
2387 error.SharingViolation => return error.AccessDenied,
2388 error.PipeBusy => unreachable,
2389 error.PathAlreadyExists => unreachable,
2390 else => |e| return e,
2391 }
2392 };
2393 defer w.CloseHandle(handle);
2394
2395 var reparse_buf: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
2396 _ = try w.DeviceIoControl(handle, w.FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..], null);
2397
2398 const reparse_struct = @ptrCast(*const w.REPARSE_DATA_BUFFER, @alignCast(@alignOf(w.REPARSE_DATA_BUFFER), &reparse_buf[0]));
2399 switch (reparse_struct.ReparseTag) {
2400 w.IO_REPARSE_TAG_SYMLINK => {
2401 const buf = @ptrCast(*const w.SYMBOLIC_LINK_REPARSE_BUFFER, @alignCast(@alignOf(w.SYMBOLIC_LINK_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
2402 const offset = buf.SubstituteNameOffset >> 1;
2403 const len = buf.SubstituteNameLength >> 1;
2404 const path_buf = @as([*]const u16, &buf.PathBuffer);
2405 const is_relative = buf.Flags & w.SYMLINK_FLAG_RELATIVE != 0;
2406 return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer);
2407 },
2408 w.IO_REPARSE_TAG_MOUNT_POINT => {
2409 const buf = @ptrCast(*const w.MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(w.MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
2410 const offset = buf.SubstituteNameOffset >> 1;
2411 const len = buf.SubstituteNameLength >> 1;
2412 const path_buf = @as([*]const u16, &buf.PathBuffer);
2413 return parseReadlinkPath(path_buf[offset .. offset + len], false, out_buffer);
2414 },
2415 else => |value| {
2416 std.debug.warn("unsupported symlink type: {}", .{value});
2417 return error.UnsupportedReparsePointType;
2418 },
2419 }
2420}
2421
2422fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u8 {
2423 const prefix = [_]u16{ '\\', '?', '?', '\\' };
2424 var start_index: usize = 0;
2425 if (!is_relative and mem.startsWith(u16, path, &prefix)) {
2426 start_index = prefix.len;
2427 }
2428 const out_len = std.unicode.utf16leToUtf8(out_buffer, path[start_index..]) catch unreachable;
2429 return out_buffer[0..out_len];
2430}2380}
24312381
2432/// Same as `readlink` except `file_path` is null-terminated.2382/// Same as `readlink` except `file_path` is null-terminated.
...@@ -2459,8 +2409,7 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink...@@ -2459,8 +2409,7 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink
2459 return readlinkatWasi(dirfd, file_path, out_buffer);2409 return readlinkatWasi(dirfd, file_path, out_buffer);
2460 }2410 }
2461 if (builtin.os.tag == .windows) {2411 if (builtin.os.tag == .windows) {
2462 const file_path_w = try windows.cStrToPrefixedFileW(file_path);2412 return windows.ReadLink(dirfd, file_path, out_buffer);
2463 return readlinkatW(dirfd, file_path.span().ptr, out_buffer);
2464 }2413 }
2465 const file_path_c = try toPosixPath(file_path);2414 const file_path_c = try toPosixPath(file_path);
2466 return readlinkatZ(dirfd, &file_path_c, out_buffer);2415 return readlinkatZ(dirfd, &file_path_c, out_buffer);
...@@ -2491,19 +2440,7 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read...@@ -2491,19 +2440,7 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read
2491/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.2440/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
2492/// See also `readlinkat`.2441/// See also `readlinkat`.
2493pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {2442pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
2494 const w = windows;2443 return windows.ReadLinkW(dirfd, file_path, out_buffer);
2495
2496 const handle = w.OpenReparsePoint(dir, file_path) catch |err| {
2497 switch (err) {
2498 error.SharingViolation => return error.AccessDenied,
2499 error.PathAlreadyExists => unreachable,
2500 error.PipeBusy => unreachable,
2501 error.PathAlreadyExists => unreachable,
2502 error.NoDevice => return error.FileNotFound,
2503 else => |e| return e,
2504 }
2505 };
2506 @compileError("TODO implement on Windows");
2507}2444}
25082445
2509/// Same as `readlinkat` except `file_path` is null-terminated.2446/// Same as `readlinkat` except `file_path` is null-terminated.
lib/std/os/windows.zig+140-128
...@@ -731,6 +731,117 @@ pub fn CreateSymbolicLinkW(...@@ -731,6 +731,117 @@ pub fn CreateSymbolicLinkW(
731 _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null, null);731 _ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null, null);
732}732}
733733
734pub const ReadLinkError = error{
735 FileNotFound,
736 AccessDenied,
737 Unexpected,
738 NameTooLong,
739 UnsupportedReparsePointType,
740 InvalidUtf8,
741 BadPathName,
742};
743
744pub fn ReadLink(
745 dir: ?HANDLE,
746 sub_path: []const u8,
747 out_buffer: []u8,
748) ReadLinkError![]u8 {
749 const sub_path_w = try sliceToPrefixedFileW(sub_path);
750 return ReadLinkW(dir, sub_path_w.span().ptr, out_buffer);
751}
752
753pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
754 const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
755 error.Overflow => return error.NameTooLong,
756 };
757 var nt_name = UNICODE_STRING{
758 .Length = path_len_bytes,
759 .MaximumLength = path_len_bytes,
760 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
761 };
762
763 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
764 // Windows does not recognize this, but it does work with empty string.
765 nt_name.Length = 0;
766 }
767
768 var attr = OBJECT_ATTRIBUTES{
769 .Length = @sizeOf(OBJECT_ATTRIBUTES),
770 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
771 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
772 .ObjectName = &nt_name,
773 .SecurityDescriptor = null,
774 .SecurityQualityOfService = null,
775 };
776 var io: IO_STATUS_BLOCK = undefined;
777 var result_handle: HANDLE = undefined;
778 const rc = ntdll.NtCreateFile(
779 &result_handle,
780 FILE_READ_ATTRIBUTES,
781 &attr,
782 &io,
783 null,
784 FILE_ATTRIBUTE_NORMAL,
785 FILE_SHARE_READ,
786 FILE_OPEN,
787 FILE_OPEN_REPARSE_POINT,
788 null,
789 0,
790 );
791 switch (rc) {
792 .SUCCESS => {},
793 .OBJECT_NAME_INVALID => unreachable,
794 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
795 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
796 .NO_MEDIA_IN_DEVICE => return error.FileNotFound,
797 .INVALID_PARAMETER => unreachable,
798 .SHARING_VIOLATION => return error.AccessDenied,
799 .ACCESS_DENIED => return error.AccessDenied,
800 .PIPE_BUSY => return error.AccessDenied,
801 .OBJECT_PATH_SYNTAX_BAD => unreachable,
802 .OBJECT_NAME_COLLISION => unreachable,
803 .FILE_IS_A_DIRECTORY => unreachable,
804 else => return unexpectedStatus(rc),
805 }
806 defer CloseHandle(result_handle);
807
808 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
809 _ = try DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..], null);
810
811 const reparse_struct = @ptrCast(*const REPARSE_DATA_BUFFER, @alignCast(@alignOf(REPARSE_DATA_BUFFER), &reparse_buf[0]));
812 switch (reparse_struct.ReparseTag) {
813 IO_REPARSE_TAG_SYMLINK => {
814 const buf = @ptrCast(*const SYMBOLIC_LINK_REPARSE_BUFFER, @alignCast(@alignOf(SYMBOLIC_LINK_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
815 const offset = buf.SubstituteNameOffset >> 1;
816 const len = buf.SubstituteNameLength >> 1;
817 const path_buf = @as([*]const u16, &buf.PathBuffer);
818 const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
819 return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer);
820 },
821 IO_REPARSE_TAG_MOUNT_POINT => {
822 const buf = @ptrCast(*const MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
823 const offset = buf.SubstituteNameOffset >> 1;
824 const len = buf.SubstituteNameLength >> 1;
825 const path_buf = @as([*]const u16, &buf.PathBuffer);
826 return parseReadlinkPath(path_buf[offset .. offset + len], false, out_buffer);
827 },
828 else => |value| {
829 std.debug.warn("unsupported symlink type: {}", .{value});
830 return error.UnsupportedReparsePointType;
831 },
832 }
833}
834
835fn parseReadlinkPath(path: []const u16, is_relative: bool, out_buffer: []u8) []u8 {
836 const prefix = [_]u16{ '\\', '?', '?', '\\' };
837 var start_index: usize = 0;
838 if (!is_relative and std.mem.startsWith(u16, path, &prefix)) {
839 start_index = prefix.len;
840 }
841 const out_len = std.unicode.utf16leToUtf8(out_buffer, path[start_index..]) catch unreachable;
842 return out_buffer[0..out_len];
843}
844
734pub const DeleteFileError = error{845pub const DeleteFileError = error{
735 FileNotFound,846 FileNotFound,
736 AccessDenied,847 AccessDenied,
...@@ -1343,21 +1454,6 @@ pub const PathSpace = struct {...@@ -1343,21 +1454,6 @@ pub const PathSpace = struct {
1343 pub fn span(self: PathSpace) [:0]const u16 {1454 pub fn span(self: PathSpace) [:0]const u16 {
1344 return self.data[0..self.len :0];1455 return self.data[0..self.len :0];
1345 }1456 }
1346
1347 fn ensureNtStyle(self: *PathSpace) void {
1348 // > File I/O functions in the Windows API convert "/" to "\" as part of
1349 // > converting the name to an NT-style name, except when using the "\\?\"
1350 // > prefix as detailed in the following sections.
1351 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
1352 // Because we want the larger maximum path length for absolute paths, we
1353 // convert forward slashes to backward slashes here.
1354 for (self.data[0..self.len]) |*elem| {
1355 if (elem.* == '/') {
1356 elem.* = '\\';
1357 }
1358 }
1359 self.data[self.len] = 0;
1360 }
1361};1457};
13621458
1363/// Same as `sliceToPrefixedFileW` but accepts a pointer1459/// Same as `sliceToPrefixedFileW` but accepts a pointer
...@@ -1366,50 +1462,14 @@ pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {...@@ -1366,50 +1462,14 @@ pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {
1366 return sliceToPrefixedFileW(mem.spanZ(s));1462 return sliceToPrefixedFileW(mem.spanZ(s));
1367}1463}
13681464
1369/// Same as `sliceToWin32PrefixedFileW` but accepts a pointer
1370/// to a null-terminated path.
1371pub fn cStrToWin32PrefixedFileW(s: [*:0]const u8) !PathSpace {
1372 return sliceToWin32PrefixedFileW(mem.spanZ(s));
1373}
1374
1375/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,1465/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
1376/// it will get NT-style prefix `\??\` prepended automatically. For prepending1466/// it will get NT-style prefix `\??\` prepended automatically. For prepending
1377/// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead.1467/// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead.
1378pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {1468pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {
1379 return sliceToPrefixedFileWInternal(s, PathPrefix.Nt);
1380}
1381
1382/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
1383/// it will get Win32-style extended prefix `\\?\` prepended automatically. For prepending
1384/// NT-style prefix, see `sliceToPrefixedFileW` instead.
1385pub fn sliceToWin32PrefixedFileW(s: []const u8) !PathSpace {
1386 return sliceToPrefixedFileWInternal(s, PathPrefix.Win32);
1387}
1388
1389const PathPrefix = enum {
1390 Win32,
1391 Nt,
1392
1393 fn toUtf8(self: PathPrefix) []const u8 {
1394 return switch (self) {
1395 .Win32 => "\\\\?\\",
1396 .Nt => "\\??\\",
1397 };
1398 }
1399
1400 fn toUtf16(self: PathPrefix) []const u16 {
1401 return switch (self) {
1402 .Win32 => &[_]u16{ '\\', '\\', '?', '\\' },
1403 .Nt => &[_]u16{ '\\', '?', '?', '\\' },
1404 };
1405 }
1406};
1407
1408fn sliceToPrefixedFileWInternal(s: []const u8, prefix: PathPrefix) !PathSpace {
1409 // TODO https://github.com/ziglang/zig/issues/27651469 // TODO https://github.com/ziglang/zig/issues/2765
1410 var path_space: PathSpace = undefined;1470 var path_space: PathSpace = undefined;
1411 const prefix_utf8 = prefix.toUtf8();1471 const prefix = "\\??\\";
1412 const prefix_index: usize = if (mem.startsWith(u8, s, prefix_utf8)) prefix_utf8.len else 0;1472 const prefix_index: usize = if (mem.startsWith(u8, s, prefix)) prefix.len else 0;
1413 for (s[prefix_index..]) |byte| {1473 for (s[prefix_index..]) |byte| {
1414 switch (byte) {1474 switch (byte) {
1415 '*', '?', '"', '<', '>', '|' => return error.BadPathName,1475 '*', '?', '"', '<', '>', '|' => return error.BadPathName,
...@@ -1417,13 +1477,24 @@ fn sliceToPrefixedFileWInternal(s: []const u8, prefix: PathPrefix) !PathSpace {...@@ -1417,13 +1477,24 @@ fn sliceToPrefixedFileWInternal(s: []const u8, prefix: PathPrefix) !PathSpace {
1417 }1477 }
1418 }1478 }
1419 const start_index = if (prefix_index > 0 or !std.fs.path.isAbsolute(s)) 0 else blk: {1479 const start_index = if (prefix_index > 0 or !std.fs.path.isAbsolute(s)) 0 else blk: {
1420 const prefix_utf16 = prefix.toUtf16();1480 const prefix_u16 = [_]u16{ '\\', '?', '?', '\\' };
1421 mem.copy(u16, path_space.data[0..], prefix_utf16);1481 mem.copy(u16, path_space.data[0..], prefix_u16[0..]);
1422 break :blk prefix_utf16.len;1482 break :blk prefix_u16.len;
1423 };1483 };
1424 path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s);1484 path_space.len = start_index + try std.unicode.utf8ToUtf16Le(path_space.data[start_index..], s);
1425 if (path_space.len > path_space.data.len) return error.NameTooLong;1485 if (path_space.len > path_space.data.len) return error.NameTooLong;
1426 path_space.ensureNtStyle();1486 // > File I/O functions in the Windows API convert "/" to "\" as part of
1487 // > converting the name to an NT-style name, except when using the "\\?\"
1488 // > prefix as detailed in the following sections.
1489 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
1490 // Because we want the larger maximum path length for absolute paths, we
1491 // convert forward slashes to backward slashes here.
1492 for (path_space.data[0..path_space.len]) |*elem| {
1493 if (elem.* == '/') {
1494 elem.* = '\\';
1495 }
1496 }
1497 path_space.data[path_space.len] = 0;
1427 return path_space;1498 return path_space;
1428}1499}
14291500
...@@ -1440,7 +1511,18 @@ pub fn wToPrefixedFileW(s: []const u16) !PathSpace {...@@ -1440,7 +1511,18 @@ pub fn wToPrefixedFileW(s: []const u16) !PathSpace {
1440 path_space.len = start_index + s.len;1511 path_space.len = start_index + s.len;
1441 if (path_space.len > path_space.data.len) return error.NameTooLong;1512 if (path_space.len > path_space.data.len) return error.NameTooLong;
1442 mem.copy(u16, path_space.data[start_index..], s);1513 mem.copy(u16, path_space.data[start_index..], s);
1443 path_space.ensureNtStyle();1514 // > File I/O functions in the Windows API convert "/" to "\" as part of
1515 // > converting the name to an NT-style name, except when using the "\\?\"
1516 // > prefix as detailed in the following sections.
1517 // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation
1518 // Because we want the larger maximum path length for absolute paths, we
1519 // convert forward slashes to backward slashes here.
1520 for (path_space.data[0..path_space.len]) |*elem| {
1521 if (elem.* == '/') {
1522 elem.* = '\\';
1523 }
1524 }
1525 path_space.data[path_space.len] = 0;
1444 return path_space;1526 return path_space;
1445}1527}
14461528
...@@ -1484,73 +1566,3 @@ pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {...@@ -1484,73 +1566,3 @@ pub fn unexpectedStatus(status: NTSTATUS) std.os.UnexpectedError {
1484 }1566 }
1485 return error.Unexpected;1567 return error.Unexpected;
1486}1568}
1487
1488pub const OpenReparsePointError = error{
1489 FileNotFound,
1490 NoDevice,
1491 SharingViolation,
1492 AccessDenied,
1493 PipeBusy,
1494 PathAlreadyExists,
1495 Unexpected,
1496 NameTooLong,
1497};
1498
1499/// Open file as a reparse point
1500pub fn OpenReparsePoint(
1501 dir: ?HANDLE,
1502 sub_path_w: [*:0]const u16,
1503) OpenReparsePointError!HANDLE {
1504 const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
1505 error.Overflow => return error.NameTooLong,
1506 };
1507 var nt_name = UNICODE_STRING{
1508 .Length = path_len_bytes,
1509 .MaximumLength = path_len_bytes,
1510 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
1511 };
1512
1513 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
1514 // Windows does not recognize this, but it does work with empty string.
1515 nt_name.Length = 0;
1516 }
1517
1518 var attr = OBJECT_ATTRIBUTES{
1519 .Length = @sizeOf(OBJECT_ATTRIBUTES),
1520 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
1521 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
1522 .ObjectName = &nt_name,
1523 .SecurityDescriptor = null,
1524 .SecurityQualityOfService = null,
1525 };
1526 var io: IO_STATUS_BLOCK = undefined;
1527 var result_handle: HANDLE = undefined;
1528 const rc = ntdll.NtCreateFile(
1529 &result_handle,
1530 FILE_READ_ATTRIBUTES,
1531 &attr,
1532 &io,
1533 null,
1534 FILE_ATTRIBUTE_NORMAL,
1535 FILE_SHARE_READ,
1536 FILE_OPEN,
1537 FILE_OPEN_REPARSE_POINT,
1538 null,
1539 0,
1540 );
1541 switch (rc) {
1542 .SUCCESS => return result_handle,
1543 .OBJECT_NAME_INVALID => unreachable,
1544 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
1545 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
1546 .NO_MEDIA_IN_DEVICE => return error.NoDevice,
1547 .INVALID_PARAMETER => unreachable,
1548 .SHARING_VIOLATION => return error.SharingViolation,
1549 .ACCESS_DENIED => return error.AccessDenied,
1550 .PIPE_BUSY => return error.PipeBusy,
1551 .OBJECT_PATH_SYNTAX_BAD => unreachable,
1552 .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
1553 .FILE_IS_A_DIRECTORY => unreachable,
1554 else => return unexpectedStatus(rc),
1555 }
1556}