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...@@ -2574,23 +2574,6 @@ pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) C
2574 wr.* = write;2574 wr.* = write;
2575}2575}
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
2594/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.2577/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
2595/// It implements similar behavior to `DeviceIoControl` and is meant to serve2578/// It implements similar behavior to `DeviceIoControl` and is meant to serve
2596/// as a direct substitute for that call.2579/// as a direct substitute for that call.
...@@ -2606,9 +2589,9 @@ pub fn DeviceIoControl(...@@ -2606,9 +2589,9 @@ pub fn DeviceIoControl(
2606 in: []const u8 = &.{},2589 in: []const u8 = &.{},
2607 out: []u8 = &.{},2590 out: []u8 = &.{},
2608 },2591 },
2609) DeviceIoControlError!void {2592) NTSTATUS {
2610 var io_status_block: IO_STATUS_BLOCK = undefined;2593 var io_status_block: IO_STATUS_BLOCK = undefined;
2611 const rc = switch (io_control_code.DeviceType) {2594 return switch (io_control_code.DeviceType) {
2612 .FILE_SYSTEM, .NAMED_PIPE => ntdll.NtFsControlFile(2595 .FILE_SYSTEM, .NAMED_PIPE => ntdll.NtFsControlFile(
2613 device,2596 device,
2614 opts.event,2597 opts.event,
...@@ -2634,19 +2617,6 @@ pub fn DeviceIoControl(...@@ -2634,19 +2617,6 @@ pub fn DeviceIoControl(
2634 @intCast(opts.out.len),2617 @intCast(opts.out.len),
2635 ),2618 ),
2636 };2619 };
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 }
2650}2620}
26512621
2652pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {2622pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
...@@ -3037,9 +3007,6 @@ pub const CreateSymbolicLinkError = error{...@@ -3037,9 +3007,6 @@ pub const CreateSymbolicLinkError = error{
3037 NoDevice,3007 NoDevice,
3038 NetworkNotFound,3008 NetworkNotFound,
3039 BadPathName,3009 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,
3043 Unexpected,3010 Unexpected,
3044};3011};
30453012
...@@ -3139,13 +3106,14 @@ pub fn CreateSymbolicLink(...@@ -3139,13 +3106,14 @@ pub fn CreateSymbolicLink(
3139 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));3106 @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
3140 const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;3107 const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
3141 @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));3108 @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) {3109 const rc = DeviceIoControl(symlink_handle, FSCTL.SET_REPARSE_POINT, .{ .in = buffer[0..buf_len] });
3143 error.PipeClosing => unreachable,3110 switch (rc) {
3144 error.PipeAlreadyConnected => unreachable,3111 .SUCCESS => {},
3145 error.PipeAlreadyListening => unreachable,3112 .PRIVILEGE_NOT_HELD => return error.AccessDenied,
3146 error.Pending => unreachable,3113 .ACCESS_DENIED => return error.AccessDenied,
3147 else => |e| return e,3114 .INVALID_DEVICE_REQUEST => return error.AccessDenied, // Not supported by the underlying filesystem
3148 };3115 else => return unexpectedStatus(rc),
3116 }
3149}3117}
31503118
3151pub const ReadLinkError = error{3119pub const ReadLinkError = error{
...@@ -3184,15 +3152,11 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLi...@@ -3184,15 +3152,11 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u16) ReadLi
3184 defer CloseHandle(result_handle);3152 defer CloseHandle(result_handle);
31853153
3186 var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(REPARSE_DATA_BUFFER)) = undefined;3154 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) {3155 const rc = DeviceIoControl(result_handle, FSCTL.GET_REPARSE_POINT, .{ .out = reparse_buf[0..] });
3188 error.PipeClosing => unreachable,3156 switch (rc) {
3189 error.PipeAlreadyConnected => unreachable,3157 .SUCCESS => {},
3190 error.PipeAlreadyListening => unreachable,3158 else => return unexpectedStatus(rc),
3191 error.AccessDenied => return error.Unexpected,3159 }
3192 error.UnrecognizedVolume => return error.Unexpected,
3193 error.Pending => unreachable,
3194 else => |e| return e,
3195 };
31963160
3197 const reparse_struct: *const REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));3161 const reparse_struct: *const REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));
3198 const IoReparseTagInt = @typeInfo(IO_REPARSE_TAG).@"struct".backing_integer.?;3162 const IoReparseTagInt = @typeInfo(IO_REPARSE_TAG).@"struct".backing_integer.?;
...@@ -3744,14 +3708,14 @@ pub fn GetFinalPathNameByHandle(...@@ -3744,14 +3708,14 @@ pub fn GetFinalPathNameByHandle(
3744 input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);3708 input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);
3745 @memcpy(input_buf[@sizeOf(MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));3709 @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) {3711 {
3748 error.PipeClosing => unreachable,3712 const rc = DeviceIoControl(mgmt_handle, IOCTL.MOUNTMGR.QUERY_POINTS, .{ .in = &input_buf, .out = &output_buf });
3749 error.PipeAlreadyConnected => unreachable,3713 switch (rc) {
3750 error.PipeAlreadyListening => unreachable,3714 .SUCCESS => {},
3751 error.AccessDenied => return error.Unexpected,3715 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
3752 error.Pending => unreachable,3716 else => return unexpectedStatus(rc),
3753 else => |e| return e,3717 }
3754 };3718 }
3755 const mount_points_struct: *const MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);3719 const mount_points_struct: *const MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);
37563720
3757 const mount_points = @as(3721 const mount_points = @as(
...@@ -3803,14 +3767,12 @@ pub fn GetFinalPathNameByHandle(...@@ -3803,14 +3767,12 @@ pub fn GetFinalPathNameByHandle(
3803 vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);3767 vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);
3804 @memcpy(@as([*]WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink);3768 @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) {3770 const rc = DeviceIoControl(mgmt_handle, IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH, .{ .in = &vol_input_buf, .out = &vol_output_buf });
3807 error.PipeClosing => unreachable,3771 switch (rc) {
3808 error.PipeAlreadyConnected => unreachable,3772 .SUCCESS => {},
3809 error.PipeAlreadyListening => unreachable,3773 .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
3810 error.AccessDenied => return error.Unexpected,3774 else => return unexpectedStatus(rc),
3811 error.Pending => unreachable,3775 }
3812 else => |e| return e,
3813 };
3814 const volume_paths_struct: *const MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);3776 const volume_paths_struct: *const MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);
3815 const volume_path = std.mem.sliceTo(@as(3777 const volume_path = std.mem.sliceTo(@as(
3816 [*]const u16,3778 [*]const u16,