authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-28 16:18:43-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-01-28 17:02:17-08:00
log18c6abc0ba9a58a3d25908c47df3bb9374d51c35
tree65cb6c2fbfabd6be770c3574f01efd53bb6cc5df
parent57742480414b2060411b0b07f258e9187b4e8ca0

std: finish moving os.windows.ReadLink logic to Io.Threaded

- remove error.SharingViolation from all error sets since it has the same meaning as FileBusy - add error.FileBusy to CreateFileAtomicError and ReadLinkError - update dirReadLinkWindows to use NtCreateFile and NtFsControlFile and integrate with cancelation properly. - move windows CTL_CODE constants to the proper namespace - delete os.windows.ReadLink

7 files changed, 218 insertions(+), 173 deletions(-)

lib/std/Io/Dir.zig+5-1
......@@ -940,6 +940,7 @@ pub const RenameError = error{
940940 /// Attempted to replace a nonempty directory.
941941 DirNotEmpty,
942942 PermissionDenied,
943 /// The file attempted to be moved or replaced is a running executable.
943944 FileBusy,
944945 DiskQuota,
945946 IsDir,
......@@ -952,7 +953,6 @@ pub const RenameError = error{
952953 ReadOnlyFileSystem,
953954 CrossDevice,
954955 NoDevice,
955 SharingViolation,
956956 PipeBusy,
957957 /// On Windows, `\\server` or `\\server\share` was not found.
958958 NetworkNotFound,
......@@ -1167,6 +1167,8 @@ pub const ReadLinkError = error{
11671167 /// intercepts file system operations and makes them significantly slower
11681168 /// in addition to possibly failing with this error code.
11691169 AntivirusInterference,
1170 /// File attempted to be opened is a running executable.
1171 FileBusy,
11701172} || PathNameError || Io.Cancelable || Io.UnexpectedError;
11711173
11721174/// Obtain target of a symbolic link.
......@@ -1791,6 +1793,8 @@ pub const CreateFileAtomicError = error{
17911793 NotDir,
17921794 WouldBlock,
17931795 ReadOnlyFileSystem,
1796 /// The file attempted to be created is a running executable.
1797 FileBusy,
17941798} || Io.Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
17951799
17961800/// Create an unnamed ephemeral file that can eventually be atomically
lib/std/Io/File.zig+1-2
......@@ -249,7 +249,6 @@ pub const CreateFlags = struct {
249249};
250250
251251pub const OpenError = error{
252 SharingViolation,
253252 PipeBusy,
254253 NoDevice,
255254 /// On Windows, `\\server` or `\\server\share` was not found.
......@@ -757,7 +756,7 @@ pub const RealPathError = error{
757756 NoSpaceLeft,
758757 FileSystem,
759758 DeviceBusy,
760 SharingViolation,
759 FileBusy,
761760 PipeBusy,
762761 /// On Windows, `\\server` or `\\server\share` was not found.
763762 NetworkNotFound,
lib/std/Io/Threaded.zig+201-72
......@@ -3635,7 +3635,7 @@ fn dirCreateFileWindows(
36353635 // after an executable file is closed. Here we work around the
36363636 // kernel bug with retry attempts.
36373637 syscall.finish();
3638 if (max_attempts - attempt == 0) return error.SharingViolation;
3638 if (max_attempts - attempt == 0) return error.FileBusy;
36393639 try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
36403640 attempt += 1;
36413641 syscall = try .start();
......@@ -3648,7 +3648,7 @@ fn dirCreateFileWindows(
36483648 // call has failed. Here, we simulate the kernel bug being
36493649 // fixed by sleeping and retrying until the error goes away.
36503650 syscall.finish();
3651 if (max_attempts - attempt == 0) return error.SharingViolation;
3651 if (max_attempts - attempt == 0) return error.FileBusy;
36523652 try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
36533653 attempt += 1;
36543654 syscall = try .start();
......@@ -3668,10 +3668,10 @@ fn dirCreateFileWindows(
36683668 .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
36693669 .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
36703670 .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
3671 .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
3672 .OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
3673 .INVALID_HANDLE => |err| return syscall.ntstatusBug(err),
3674 else => |err| return syscall.unexpectedNtstatus(err),
3671 .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
3672 .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
3673 .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
3674 else => |status| return syscall.unexpectedNtstatus(status),
36753675 };
36763676 errdefer windows.CloseHandle(handle);
36773677
......@@ -3819,7 +3819,6 @@ fn dirCreateFileAtomic(
38193819 error.DiskQuota,
38203820 error.PathAlreadyExists,
38213821 error.LinkQuotaExceeded,
3822 error.SharingViolation,
38233822 error.PipeBusy,
38243823 error.FileTooBig,
38253824 error.DeviceBusy,
......@@ -3889,11 +3888,9 @@ fn dirCreateFileAtomic(
38893888 error.DiskQuota,
38903889 error.PathAlreadyExists,
38913890 error.LinkQuotaExceeded,
3892 error.SharingViolation,
38933891 error.PipeBusy,
38943892 error.FileTooBig,
38953893 error.FileLocksUnsupported,
3896 error.FileBusy,
38973894 error.DeviceBusy,
38983895 => return error.Unexpected,
38993896
......@@ -3926,7 +3923,6 @@ fn atomicFileInit(
39263923 error.PathAlreadyExists => continue,
39273924 error.DeviceBusy => continue,
39283925 error.FileBusy => continue,
3929 error.SharingViolation => continue,
39303926
39313927 error.IsDir => return error.Unexpected, // No path components.
39323928 error.FileTooBig => return error.Unexpected, // Creating, not opening.
......@@ -4236,7 +4232,7 @@ pub fn dirOpenFileWtf16(
42364232 // after an executable file is closed. Here we work around the
42374233 // kernel bug with retry attempts.
42384234 syscall.finish();
4239 if (max_attempts - attempt == 0) return error.SharingViolation;
4235 if (max_attempts - attempt == 0) return error.FileBusy;
42404236 try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
42414237 attempt += 1;
42424238 syscall = try .start();
......@@ -4258,7 +4254,7 @@ pub fn dirOpenFileWtf16(
42584254 // call has failed. Here, we simulate the kernel bug being
42594255 // fixed by sleeping and retrying until the error goes away.
42604256 syscall.finish();
4261 if (max_attempts - attempt == 0) return error.SharingViolation;
4257 if (max_attempts - attempt == 0) return error.FileBusy;
42624258 try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
42634259 attempt += 1;
42644260 syscall = try .start();
......@@ -6464,7 +6460,7 @@ fn dirSymLinkWindows(
64646460 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
64656461 const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
64666462 @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
6467 const rc = w.DeviceIoControl(symlink_handle, w.FSCTL.SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
6463 const rc = w.DeviceIoControl(symlink_handle, .SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
64686464 switch (rc) {
64696465 .SUCCESS => {},
64706466 .PRIVILEGE_NOT_HELD => return error.PermissionDenied,
......@@ -6571,44 +6567,189 @@ fn dirSymLinkPosix(
65716567 }
65726568}
65736569
6574const dirReadLink = switch (native_os) {
6575 .windows => dirReadLinkWindows,
6576 .wasi => dirReadLinkWasi,
6577 else => dirReadLinkPosix,
6578};
6579
6580fn dirReadLinkWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
6570fn dirReadLink(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
65816571 const t: *Threaded = @ptrCast(@alignCast(userdata));
65826572 _ = t;
6583 const w = windows;
6573 switch (native_os) {
6574 .windows => return dirReadLinkWindows(dir, sub_path, buffer),
6575 .wasi => return dirReadLinkWasi(dir, sub_path, buffer),
6576 else => return dirReadLinkPosix(dir, sub_path, buffer),
6577 }
6578}
65846579
6580fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
6581 // This gets used once for `sub_path` and then reused again temporarily
6582 // before converting back to `buffer`.
65856583 var sub_path_w_buf = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
6584 const sub_path_w = sub_path_w_buf.span();
6585 const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
6586 var nt_name: windows.UNICODE_STRING = .{
6587 .Length = path_len_bytes,
6588 .MaximumLength = path_len_bytes,
6589 .Buffer = @constCast(sub_path_w.ptr),
6590 };
6591 const attr: windows.OBJECT_ATTRIBUTES = .{
6592 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
6593 .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
6594 .Attributes = .{
6595 .INHERIT = false,
6596 },
6597 .ObjectName = &nt_name,
6598 .SecurityDescriptor = null,
6599 .SecurityQualityOfService = null,
6600 };
6601 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
6602 var result_handle: windows.HANDLE = undefined;
65866603
6587 const syscall: Syscall = try .start();
6588 const result_w = while (true) {
6589 if (w.ReadLink(dir.handle, sub_path_w_buf.span(), &sub_path_w_buf.data)) |res| {
6604 // There are multiple kernel bugs being worked around with retries.
6605 const max_attempts = 13;
6606 var attempt: u5 = 0;
6607
6608 var syscall: Syscall = try .start();
6609 while (true) switch (windows.ntdll.NtCreateFile(
6610 &result_handle,
6611 .{
6612 .SPECIFIC = .{ .FILE = .{
6613 .READ_ATTRIBUTES = true,
6614 } },
6615 .STANDARD = .{ .SYNCHRONIZE = true },
6616 },
6617 &attr,
6618 &io_status_block,
6619 null,
6620 .{ .NORMAL = true },
6621 .VALID_FLAGS,
6622 .OPEN,
6623 .{
6624 .DIRECTORY_FILE = false,
6625 .NON_DIRECTORY_FILE = false,
6626 .IO = .ASYNCHRONOUS,
6627 .OPEN_REPARSE_POINT = true,
6628 },
6629 null,
6630 0,
6631 )) {
6632 .SUCCESS => {
65906633 syscall.finish();
6591 break res;
6592 } else |err| switch (err) {
6593 error.OperationCanceled => {
6594 try syscall.checkCancel();
6595 continue;
6596 },
6597 else => |e| return syscall.fail(e),
6598 }
6634 break;
6635 },
6636 .CANCELLED => {
6637 try syscall.checkCancel();
6638 continue;
6639 },
6640 .SHARING_VIOLATION => {
6641 // This occurs if the file attempting to be opened is a running
6642 // executable. However, there's a kernel bug: the error may be
6643 // incorrectly returned for an indeterminate amount of time
6644 // after an executable file is closed. Here we work around the
6645 // kernel bug with retry attempts.
6646 syscall.finish();
6647 if (max_attempts - attempt == 0) return error.FileBusy;
6648 try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
6649 attempt += 1;
6650 syscall = try .start();
6651 continue;
6652 },
6653 .DELETE_PENDING => {
6654 // This error means that there *was* a file in this location on
6655 // the file system, but it was deleted. However, the OS is not
6656 // finished with the deletion operation, and so this CreateFile
6657 // call has failed. Here, we simulate the kernel bug being
6658 // fixed by sleeping and retrying until the error goes away.
6659 syscall.finish();
6660 if (max_attempts - attempt == 0) return error.FileBusy;
6661 try parking_sleep.windowsRetrySleep((@as(u32, 1) << attempt) >> 1);
6662 attempt += 1;
6663 syscall = try .start();
6664 continue;
6665 },
6666 .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
6667 .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
6668 .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
6669 .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
6670 .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
6671 .NO_MEDIA_IN_DEVICE => return syscall.fail(error.FileNotFound),
6672 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
6673 .PIPE_BUSY => return syscall.fail(error.AccessDenied),
6674 .PIPE_NOT_AVAILABLE => return syscall.fail(error.FileNotFound),
6675 .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
6676 .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
6677 .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
6678 .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
6679 .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
6680 else => |status| return syscall.unexpectedNtstatus(status),
65996681 };
6682 defer windows.CloseHandle(result_handle);
6683
6684 var reparse_buf: [windows.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(windows.REPARSE_DATA_BUFFER)) = undefined;
66006685
6686 syscall = try .start();
6687 while (true) switch (windows.ntdll.NtFsControlFile(
6688 result_handle,
6689 null, // event
6690 null, // APC routine
6691 null, // APC context
6692 &io_status_block,
6693 .GET_REPARSE_POINT,
6694 null, // input buffer
6695 0, // input buffer length
6696 &reparse_buf,
6697 reparse_buf.len,
6698 )) {
6699 .SUCCESS => {
6700 syscall.finish();
6701 break;
6702 },
6703 .CANCELLED => {
6704 try syscall.checkCancel();
6705 continue;
6706 },
6707 .NOT_A_REPARSE_POINT => return syscall.fail(error.NotLink),
6708 else => |status| return syscall.unexpectedNtstatus(status),
6709 };
6710
6711 const reparse_struct: *const windows.REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf));
6712 const IoReparseTagInt = @typeInfo(windows.IO_REPARSE_TAG).@"struct".backing_integer.?;
6713 const result_w = switch (@as(IoReparseTagInt, @bitCast(reparse_struct.ReparseTag))) {
6714 @as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.SYMLINK)) => r: {
6715 const buf: *const windows.SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
6716 const offset = buf.SubstituteNameOffset >> 1;
6717 const len = buf.SubstituteNameLength >> 1;
6718 const path_buf = @as([*]const u16, &buf.PathBuffer);
6719 const is_relative = buf.Flags & windows.SYMLINK_FLAG_RELATIVE != 0;
6720 break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w_buf.data);
6721 },
6722 @as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.MOUNT_POINT)) => r: {
6723 const buf: *const windows.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
6724 const offset = buf.SubstituteNameOffset >> 1;
6725 const len = buf.SubstituteNameLength >> 1;
6726 const path_buf = @as([*]const u16, &buf.PathBuffer);
6727 break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w_buf.data);
6728 },
6729 else => return error.UnsupportedReparsePointType,
6730 };
66016731 const len = std.unicode.calcWtf8Len(result_w);
66026732 if (len > buffer.len) return error.NameTooLong;
66036733
66046734 return std.unicode.wtf16LeToWtf8(buffer, result_w);
66056735}
66066736
6607fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
6608 if (builtin.link_libc) return dirReadLinkPosix(userdata, dir, sub_path, buffer);
6737fn parseReadLinkPath(path: []const u16, is_relative: bool, out_buffer: []u16) error{NameTooLong}![]u16 {
6738 path: {
6739 if (is_relative) break :path;
6740 return windows.ntToWin32Namespace(path, out_buffer) catch |err| switch (err) {
6741 error.NameTooLong => |e| return e,
6742 error.NotNtPath => break :path,
6743 };
6744 }
6745 if (out_buffer.len < path.len) return error.NameTooLong;
6746 const dest = out_buffer[0..path.len];
6747 @memcpy(dest, path);
6748 return dest;
6749}
66096750
6610 const t: *Threaded = @ptrCast(@alignCast(userdata));
6611 _ = t;
6751fn dirReadLinkWasi(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
6752 if (builtin.link_libc) return dirReadLinkPosix(dir, sub_path, buffer);
66126753
66136754 var n: usize = undefined;
66146755 const syscall: Syscall = try .start();
......@@ -6643,10 +6784,7 @@ fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer
66436784 }
66446785}
66456786
6646fn dirReadLinkPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
6647 const t: *Threaded = @ptrCast(@alignCast(userdata));
6648 _ = t;
6649
6787fn dirReadLinkPosix(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
66506788 var sub_path_buffer: [posix.PATH_MAX]u8 = undefined;
66516789 const sub_path_posix = try pathToPosix(sub_path, &sub_path_buffer);
66526790
......@@ -8708,45 +8846,41 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
87088846 const symlink_path = std.mem.sliceTo(&symlink_path_buf, 0);
87098847 return Io.Dir.realPathFileAbsolute(ioBasic(t), symlink_path, out_buffer) catch |err| switch (err) {
87108848 error.NetworkNotFound => unreachable, // Windows-only
8849 error.FileBusy => unreachable, // Windows-only
87118850 else => |e| return e,
87128851 };
87138852 },
87148853 .linux, .serenity => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/exe", out_buffer) catch |err| switch (err) {
87158854 error.UnsupportedReparsePointType => unreachable, // Windows-only
87168855 error.NetworkNotFound => unreachable, // Windows-only
8856 error.FileBusy => unreachable, // Windows-only
87178857 else => |e| return e,
87188858 },
87198859 .illumos => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
87208860 error.UnsupportedReparsePointType => unreachable, // Windows-only
87218861 error.NetworkNotFound => unreachable, // Windows-only
8862 error.FileBusy => unreachable, // Windows-only
87228863 else => |e| return e,
87238864 },
87248865 .freebsd, .dragonfly => {
87258866 var mib: [4]c_int = .{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
87268867 var out_len: usize = out_buffer.len;
87278868 const syscall: Syscall = try .start();
8728 while (true) {
8729 switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
8730 .SUCCESS => {
8731 syscall.finish();
8732 return out_len - 1; // discard terminating NUL
8733 },
8734 .INTR => {
8735 try syscall.checkCancel();
8736 continue;
8737 },
8738 else => |e| {
8739 syscall.finish();
8740 switch (e) {
8741 .FAULT => |err| return errnoBug(err),
8742 .PERM => return error.PermissionDenied,
8743 .NOMEM => return error.SystemResources,
8744 .NOENT => |err| return errnoBug(err),
8745 else => |err| return posix.unexpectedErrno(err),
8746 }
8747 },
8748 }
8749 }
8869 while (true) switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
8870 .SUCCESS => {
8871 syscall.finish();
8872 return out_len - 1; // discard terminating NUL
8873 },
8874 .INTR => {
8875 try syscall.checkCancel();
8876 continue;
8877 },
8878 .PERM => return syscall.fail(error.PermissionDenied),
8879 .NOMEM => return syscall.fail(error.SystemResources),
8880 .FAULT => |err| return syscall.errnoBug(err),
8881 .NOENT => |err| return syscall.errnoBug(err),
8882 else => |err| return syscall.unexpectedErrno(err),
8883 };
87508884 },
87518885 .netbsd => {
87528886 var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
......@@ -8762,16 +8896,11 @@ fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.Execut
87628896 try syscall.checkCancel();
87638897 continue;
87648898 },
8765 else => |e| {
8766 syscall.finish();
8767 switch (e) {
8768 .FAULT => |err| return errnoBug(err),
8769 .PERM => return error.PermissionDenied,
8770 .NOMEM => return error.SystemResources,
8771 .NOENT => |err| return errnoBug(err),
8772 else => |err| return posix.unexpectedErrno(err),
8773 }
8774 },
8899 .PERM => return syscall.fail(error.PermissionDenied),
8900 .NOMEM => return syscall.fail(error.SystemResources),
8901 .FAULT => |err| return syscall.errnoBug(err),
8902 .NOENT => |err| return syscall.errnoBug(err),
8903 else => |err| return syscall.unexpectedErrno(err),
87758904 }
87768905 }
87778906 },
lib/std/debug/SelfInfo/Windows.zig-1
......@@ -335,7 +335,6 @@ const Module = struct {
335335 error.NoSpaceLeft,
336336 error.DeviceBusy,
337337 error.NoDevice,
338 error.SharingViolation,
339338 error.PathAlreadyExists,
340339 error.PipeBusy,
341340 error.NetworkNotFound,
lib/std/os/windows.zig+10-94
......@@ -1135,19 +1135,7 @@ pub const CTL_CODE = packed struct(ULONG) {
11351135
11361136 _,
11371137 };
1138};
1139
1140pub const IOCTL = struct {
1141 pub const KSEC = struct {
1142 pub const GEN_RANDOM: CTL_CODE = .{ .DeviceType = .KSEC, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
1143 };
1144 pub const MOUNTMGR = struct {
1145 pub const QUERY_POINTS: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
1146 pub const QUERY_DOS_VOLUME_PATH: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 12, .Method = .BUFFERED, .Access = .ANY };
1147 };
1148};
11491138
1150pub const FSCTL = struct {
11511139 pub const SET_REPARSE_POINT: CTL_CODE = .{ .DeviceType = .FILE_SYSTEM, .Function = 41, .Method = .BUFFERED, .Access = .SPECIAL };
11521140 pub const GET_REPARSE_POINT: CTL_CODE = .{ .DeviceType = .FILE_SYSTEM, .Function = 42, .Method = .BUFFERED, .Access = .ANY };
11531141
......@@ -1177,6 +1165,16 @@ pub const FSCTL = struct {
11771165 };
11781166};
11791167
1168pub const IOCTL = struct {
1169 pub const KSEC = struct {
1170 pub const GEN_RANDOM: CTL_CODE = .{ .DeviceType = .KSEC, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
1171 };
1172 pub const MOUNTMGR = struct {
1173 pub const QUERY_POINTS: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 2, .Method = .BUFFERED, .Access = .ANY };
1174 pub const QUERY_DOS_VOLUME_PATH: CTL_CODE = .{ .DeviceType = .MOUNTMGRCONTROLTYPE, .Function = 12, .Method = .BUFFERED, .Access = .ANY };
1175 };
1176};
1177
11801178pub const MAXIMUM_REPARSE_DATA_BUFFER_SIZE: ULONG = 16 * 1024;
11811179
11821180pub const IO_REPARSE_TAG = packed struct(ULONG) {
......@@ -2908,88 +2906,6 @@ pub fn GetCurrentDirectory(buffer: []u8) GetCurrentDirectoryError![]u8 {
29082906 return buffer[0..end_index];
29092907}
29102908
2911pub const ReadLinkError = error{
2912 FileNotFound,
2913 NetworkNotFound,
2914 AccessDenied,
2915 Unexpected,
2916 NameTooLong,
2917 BadPathName,
2918 AntivirusInterference,
2919 UnsupportedReparsePointType,
2920 NotLink,
2921 OperationCanceled,
2922};
2923
2924/// `sub_path_w` will never be accessed after `out_buffer` has been written to, so it
2925/// is safe to reuse a single buffer for both.
2926pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLinkError![]u16 {
2927 const result_handle = OpenFile(sub_path_w, .{
2928 .access_mask = .{
2929 .SPECIFIC = .{ .FILE = .{
2930 .READ_ATTRIBUTES = true,
2931 } },
2932 .STANDARD = .{ .SYNCHRONIZE = true },
2933 },
2934 .dir = dir,
2935 .creation = .OPEN,
2936 .follow_symlinks = false,
2937 .filter = .any,
2938 }) catch |err| switch (err) {
2939 error.IsDir, error.NotDir => return error.Unexpected, // filter = .any
2940 error.PathAlreadyExists => return error.Unexpected, // FILE_OPEN
2941 error.WouldBlock => return error.Unexpected,
2942 error.NoDevice => return error.FileNotFound,
2943 error.PipeBusy => return error.AccessDenied,
2944 else => |e| return e,
2945 };
2946 defer CloseHandle(result_handle);
2947
2948 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
2949 const rc = DeviceIoControl(result_handle, FSCTL.GET_REPARSE_POINT, .{ .out = reparse_buf[0..] });
2950 switch (rc) {
2951 .SUCCESS => {},
2952 .CANCELLED => return error.OperationCanceled,
2953 .NOT_A_REPARSE_POINT => return error.NotLink,
2954 else => return unexpectedStatus(rc),
2955 }
2956
2957 const reparse_struct: *const REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));
2958 const IoReparseTagInt = @typeInfo(IO_REPARSE_TAG).@"struct".backing_integer.?;
2959 switch (@as(IoReparseTagInt, @bitCast(reparse_struct.ReparseTag))) {
2960 @as(IoReparseTagInt, @bitCast(IO_REPARSE_TAG.SYMLINK)) => {
2961 const buf: *const SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
2962 const offset = buf.SubstituteNameOffset >> 1;
2963 const len = buf.SubstituteNameLength >> 1;
2964 const path_buf = @as([*]const u16, &buf.PathBuffer);
2965 const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
2966 return parseReadLinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
2967 },
2968 @as(IoReparseTagInt, @bitCast(IO_REPARSE_TAG.MOUNT_POINT)) => {
2969 const buf: *const MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
2970 const offset = buf.SubstituteNameOffset >> 1;
2971 const len = buf.SubstituteNameLength >> 1;
2972 const path_buf = @as([*]const u16, &buf.PathBuffer);
2973 return parseReadLinkPath(path_buf[offset..][0..len], false, out_buffer);
2974 },
2975 else => return error.UnsupportedReparsePointType,
2976 }
2977}
2978
2979fn parseReadLinkPath(path: []const u16, is_relative: bool, out_buffer: []u16) error{NameTooLong}![]u16 {
2980 path: {
2981 if (is_relative) break :path;
2982 return ntToWin32Namespace(path, out_buffer) catch |err| switch (err) {
2983 error.NameTooLong => |e| return e,
2984 error.NotNtPath => break :path,
2985 };
2986 }
2987 if (out_buffer.len < path.len) return error.NameTooLong;
2988 const dest = out_buffer[0..path.len];
2989 @memcpy(dest, path);
2990 return dest;
2991}
2992
29932909pub const DeleteFileError = error{
29942910 FileNotFound,
29952911 AccessDenied,
lib/std/process.zig-1
......@@ -704,7 +704,6 @@ pub const ExecutablePathBaseError = error{
704704 FileSystem,
705705 BadPathName,
706706 DeviceBusy,
707 SharingViolation,
708707 PipeBusy,
709708 NotLink,
710709 PathAlreadyExists,
lib/std/zig/system.zig+1-2
......@@ -723,6 +723,7 @@ fn abiAndDynamicLinkerFromFile(
723723 error.UnsupportedReparsePointType => unreachable, // Windows only
724724 error.NetworkNotFound => unreachable, // Windows only
725725 error.AntivirusInterference => unreachable, // Windows only
726 error.FileBusy => unreachable, // Windows only
726727
727728 error.AccessDenied,
728729 error.PermissionDenied,
......@@ -844,7 +845,6 @@ fn glibcVerFromRPath(io: Io, rpath: []const u8) !std.SemanticVersion {
844845 error.NameTooLong => return error.Unexpected,
845846 error.BadPathName => return error.Unexpected,
846847 error.PipeBusy => return error.Unexpected, // Windows-only
847 error.SharingViolation => return error.Unexpected, // Windows-only
848848 error.NetworkNotFound => return error.Unexpected, // Windows-only
849849 error.AntivirusInterference => return error.Unexpected, // Windows-only
850850 error.FileLocksUnsupported => return error.Unexpected, // No lock requested.
......@@ -1052,7 +1052,6 @@ fn detectAbiAndDynamicLinker(io: Io, cpu: Target.Cpu, os: Target.Os, query: Targ
10521052 error.NoSpaceLeft => return error.Unexpected,
10531053 error.NameTooLong => return error.Unexpected,
10541054 error.PathAlreadyExists => return error.Unexpected,
1055 error.SharingViolation => return error.Unexpected,
10561055 error.BadPathName => return error.Unexpected,
10571056 error.PipeBusy => return error.Unexpected,
10581057 error.FileLocksUnsupported => return error.Unexpected,