authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-12-10 18:56:14-08:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2025-12-14 07:12:38-08:00
logea7512084bc6f0f18b9dc3d1ecd23599640b3363
tree87257ccd9ea9505b21dd954fe910537ee9c0a6d6
parentc13857e504f5893cabf182dde1e826131f2acf24

Make windows.DeviceIoControl return NTSTATUS instead of a Zig error

The number of possible errors in the theoretical error set of this function is very large, while each individual call to the function is only concerned with a (potentially small) subset of those errors that are specific to the control code being used. This commit makes the callers determine which statuses they are interested in to avoid an ever-ballooning error set and ever-growing switch cases at each call site that throw away most of those errors.

1 files changed, 29 insertions(+), 67 deletions(-)

lib/std/os/windows.zig+29-67
......@@ -2574,23 +2574,6 @@ pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) C
25742574 wr.* = write;
25752575}
25762576
2577pub const DeviceIoControlError = error{
2578 AccessDenied,
2579 /// The volume does not contain a recognized file system. File system
2580 /// drivers might not be loaded, or the volume may be corrupt.
2581 UnrecognizedVolume,
2582 Pending,
2583 /// Attempted to connect a named pipe in the "closing" state, meaning a previous client has
2584 /// has closed their handle but we have not yet disconnected the pipe.
2585 PipeClosing,
2586 /// Attempted to connect a named pipe in the "connected" state, meaning a client has already
2587 /// opened the pipe; there is a good connection between client and server.
2588 PipeAlreadyConnected,
2589 /// Attempted to connect a non-blocking named pipe which is already listening for connections.
2590 PipeAlreadyListening,
2591 Unexpected,
2592};
2593
25942577/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
25952578/// It implements similar behavior to `DeviceIoControl` and is meant to serve
25962579/// as a direct substitute for that call.
......@@ -2606,9 +2589,9 @@ pub fn DeviceIoControl(
26062589 in: []const u8 = &.{},
26072590 out: []u8 = &.{},
26082591 },
2609) DeviceIoControlError!void {
2592) NTSTATUS {
26102593 var io_status_block: IO_STATUS_BLOCK = undefined;
2611 const rc = switch (io_control_code.DeviceType) {
2594 return switch (io_control_code.DeviceType) {
26122595 .FILE_SYSTEM, .NAMED_PIPE => ntdll.NtFsControlFile(
26132596 device,
26142597 opts.event,
......@@ -2634,19 +2617,6 @@ pub fn DeviceIoControl(
26342617 @intCast(opts.out.len),
26352618 ),
26362619 };
2637 switch (rc) {
2638 .SUCCESS => {},
2639 .PIPE_CLOSING => return error.PipeClosing,
2640 .PIPE_CONNECTED => return error.PipeAlreadyConnected,
2641 .PIPE_LISTENING => return error.PipeAlreadyListening,
2642 .PRIVILEGE_NOT_HELD => return error.AccessDenied,
2643 .ACCESS_DENIED => return error.AccessDenied,
2644 .INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem
2645 .INVALID_PARAMETER => unreachable,
2646 .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
2647 .PENDING => return error.Pending,
2648 else => return unexpectedStatus(rc),
2649 }
26502620}
26512621
26522622pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
......@@ -3037,9 +3007,6 @@ pub const CreateSymbolicLinkError = error{
30373007 NoDevice,
30383008 NetworkNotFound,
30393009 BadPathName,
3040 /// The volume does not contain a recognized file system. File system
3041 /// drivers might not be loaded, or the volume may be corrupt.
3042 UnrecognizedVolume,
30433010 Unexpected,
30443011};
30453012
......@@ -3139,13 +3106,14 @@ pub fn CreateSymbolicLink(
31393106 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
31403107 const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
31413108 @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
3142 _ = DeviceIoControl(symlink_handle, FSCTL.SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] }) catch |err| switch (err) {
3143 error.PipeClosing => unreachable,
3144 error.PipeAlreadyConnected => unreachable,
3145 error.PipeAlreadyListening => unreachable,
3146 error.Pending => unreachable,
3147 else => |e| return e,
3148 };
3109 const rc = DeviceIoControl(symlink_handle, FSCTL.SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
3110 switch (rc) {
3111 .SUCCESS => {},
3112 .PRIVILEGE_NOT_HELD => return error.AccessDenied,
3113 .ACCESS_DENIED => return error.AccessDenied,
3114 .INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem
3115 else => return unexpectedStatus(rc),
3116 }
31493117}
31503118
31513119pub const ReadLinkError = error{
......@@ -3184,15 +3152,11 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLi
31843152 defer CloseHandle(result_handle);
31853153
31863154 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;
3187 _ = DeviceIoControl(result_handle, FSCTL.GET_REPARSE_POINT, .{ .out = reparse_buf[0..] }) catch |err| switch (err) {
3188 error.PipeClosing => unreachable,
3189 error.PipeAlreadyConnected => unreachable,
3190 error.PipeAlreadyListening => unreachable,
3191 error.AccessDenied => return error.Unexpected,
3192 error.UnrecognizedVolume => return error.Unexpected,
3193 error.Pending => unreachable,
3194 else => |e| return e,
3195 };
3155 const rc = DeviceIoControl(result_handle, FSCTL.GET_REPARSE_POINT, .{ .out = reparse_buf[0..] });
3156 switch (rc) {
3157 .SUCCESS => {},
3158 else => return unexpectedStatus(rc),
3159 }
31963160
31973161 const reparse_struct: *const REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));
31983162 const IoReparseTagInt = @typeInfo(IO_REPARSE_TAG).@"struct".backing_integer.?;
......@@ -3744,14 +3708,14 @@ pub fn GetFinalPathNameByHandle(
37443708 input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);
37453709 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
37463710
3747 DeviceIoControl(mgmt_handle, IOCTL.MOUNTMGR.QUERY_POINTS, .{ .in = &input_buf, .out = &output_buf }) catch |err| switch (err) {
3748 error.PipeClosing => unreachable,
3749 error.PipeAlreadyConnected => unreachable,
3750 error.PipeAlreadyListening => unreachable,
3751 error.AccessDenied => return error.Unexpected,
3752 error.Pending => unreachable,
3753 else => |e| return e,
3754 };
3711 {
3712 const rc = DeviceIoControl(mgmt_handle, IOCTL.MOUNTMGR.QUERY_POINTS, .{ .in = &input_buf, .out = &output_buf });
3713 switch (rc) {
3714 .SUCCESS => {},
3715 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
3716 else => return unexpectedStatus(rc),
3717 }
3718 }
37553719 const mount_points_struct: *const MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);
37563720
37573721 const mount_points = @as(
......@@ -3803,14 +3767,12 @@ pub fn GetFinalPathNameByHandle(
38033767 vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);
38043768 @memcpy(@as([*]WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink);
38053769
3806 DeviceIoControl(mgmt_handle, IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH, .{ .in = &vol_input_buf, .out = &vol_output_buf }) catch |err| switch (err) {
3807 error.PipeClosing => unreachable,
3808 error.PipeAlreadyConnected => unreachable,
3809 error.PipeAlreadyListening => unreachable,
3810 error.AccessDenied => return error.Unexpected,
3811 error.Pending => unreachable,
3812 else => |e| return e,
3813 };
3770 const rc = DeviceIoControl(mgmt_handle, IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH, .{ .in = &vol_input_buf, .out = &vol_output_buf });
3771 switch (rc) {
3772 .SUCCESS => {},
3773 .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
3774 else => return unexpectedStatus(rc),
3775 }
38143776 const volume_paths_struct: *const MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);
38153777 const volume_path = std.mem.sliceTo(@as(
38163778 [*]const u16,