mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-03-08 02:24:33 +01:00
std.Io: add ioctl / DeviceIoControlFile API
This commit is contained in:
parent
e5454ff780
commit
59073484ba
5 changed files with 211 additions and 58 deletions
|
|
@ -257,6 +257,9 @@ pub const VTable = struct {
|
|||
pub const Operation = union(enum) {
|
||||
file_read_streaming: FileReadStreaming,
|
||||
file_write_streaming: FileWriteStreaming,
|
||||
/// On Windows this is NtDeviceIoControlFile. On POSIX this is ioctl. On
|
||||
/// other systems this tag is unreachable.
|
||||
device_io_control: DeviceIoControl,
|
||||
|
||||
pub const Tag = @typeInfo(Operation).@"union".tag_type.?;
|
||||
|
||||
|
|
@ -324,13 +327,37 @@ pub const Operation = union(enum) {
|
|||
pub const Result = Error!usize;
|
||||
};
|
||||
|
||||
pub const DeviceIoControl = switch (builtin.os.tag) {
|
||||
.wasi => noreturn,
|
||||
.windows => struct {
|
||||
file: File,
|
||||
IoControlCode: std.os.windows.CTL_CODE,
|
||||
InputBuffer: ?*const anyopaque,
|
||||
InputBufferLength: u32,
|
||||
OutputBuffer: ?*anyopaque,
|
||||
OutputBufferLength: u32,
|
||||
|
||||
pub const Result = std.os.windows.IO_STATUS_BLOCK;
|
||||
},
|
||||
else => struct {
|
||||
file: File,
|
||||
/// Device-dependent operation code.
|
||||
code: u32,
|
||||
arg: ?*anyopaque,
|
||||
|
||||
/// Device and operation dependent result. Negative values are
|
||||
/// negative errno.
|
||||
pub const Result = i32;
|
||||
},
|
||||
};
|
||||
|
||||
pub const Result = Result: {
|
||||
const operation_fields = @typeInfo(Operation).@"union".fields;
|
||||
var field_names: [operation_fields.len][]const u8 = undefined;
|
||||
var field_types: [operation_fields.len]type = undefined;
|
||||
for (operation_fields, &field_names, &field_types) |field, *field_name, *field_type| {
|
||||
field_name.* = field.name;
|
||||
field_type.* = field.type.Result;
|
||||
field_type.* = if (field.type == noreturn) noreturn else field.type.Result;
|
||||
}
|
||||
break :Result @Union(.auto, Tag, &field_names, &field_types, &@splat(.{}));
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2500,6 +2500,9 @@ fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Oper
|
|||
else => |e| e,
|
||||
},
|
||||
},
|
||||
.device_io_control => |*o| return .{
|
||||
.device_io_control = try deviceIoControl(t, o),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2531,6 +2534,14 @@ fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {
|
|||
poll_buffer[poll_len] = .{ .fd = o.file.handle, .events = posix.POLL.OUT, .revents = 0 };
|
||||
poll_len += 1;
|
||||
},
|
||||
.device_io_control => |o| {
|
||||
poll_buffer[poll_len] = .{
|
||||
.fd = o.file.handle,
|
||||
.events = posix.POLL.OUT | posix.POLL.IN | posix.POLL.ERR,
|
||||
.revents = 0,
|
||||
};
|
||||
poll_len += 1;
|
||||
},
|
||||
}
|
||||
index = submission.node.next;
|
||||
}
|
||||
|
|
@ -2696,6 +2707,7 @@ fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout
|
|||
switch (submission.operation) {
|
||||
.file_read_streaming => |o| try poll_storage.add(o.file, posix.POLL.IN),
|
||||
.file_write_streaming => |o| try poll_storage.add(o.file, posix.POLL.OUT),
|
||||
.device_io_control => |o| try poll_storage.add(o.file, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR),
|
||||
}
|
||||
index = submission.node.next;
|
||||
}
|
||||
|
|
@ -2874,6 +2886,7 @@ fn batchApc(apc_context: ?*anyopaque, iosb: *windows.IO_STATUS_BLOCK, _: windows
|
|||
const result: Io.Operation.Result = switch (pending.tag) {
|
||||
.file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) },
|
||||
.file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) },
|
||||
.device_io_control => .{ .device_io_control = iosb.* },
|
||||
};
|
||||
storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
|
||||
},
|
||||
|
|
@ -3020,6 +3033,59 @@ fn batchAwaitWindows(b: *Io.Batch, concurrency: bool) error{ Canceled, Concurren
|
|||
else => |status| {
|
||||
syscall.finish();
|
||||
|
||||
context.iosb.u.Status = status;
|
||||
batchApc(b, &context.iosb, 0);
|
||||
break;
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
.device_io_control => |o| {
|
||||
if (o.file.flags.nonblocking) {
|
||||
context.file = o.file.handle;
|
||||
switch (windows.ntdll.NtDeviceIoControlFile(
|
||||
o.file.handle,
|
||||
null, // event
|
||||
&batchApc,
|
||||
b,
|
||||
&context.iosb,
|
||||
o.IoControlCode,
|
||||
o.InputBuffer,
|
||||
o.InputBufferLength,
|
||||
o.OutputBuffer,
|
||||
o.OutputBufferLength,
|
||||
)) {
|
||||
.PENDING, .SUCCESS => {},
|
||||
.CANCELLED => unreachable,
|
||||
else => |status| {
|
||||
context.iosb.u.Status = status;
|
||||
batchApc(b, &context.iosb, 0);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
if (concurrency) return error.ConcurrencyUnavailable;
|
||||
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.NtDeviceIoControlFile(
|
||||
o.file.handle,
|
||||
null, // event
|
||||
null, // APC routine
|
||||
null, // APC context
|
||||
&context.iosb,
|
||||
o.IoControlCode,
|
||||
o.InputBuffer,
|
||||
o.InputBufferLength,
|
||||
o.OutputBuffer,
|
||||
o.OutputBufferLength,
|
||||
)) {
|
||||
.PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
|
||||
.CANCELLED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |status| {
|
||||
syscall.finish();
|
||||
|
||||
context.iosb.u.Status = status;
|
||||
batchApc(b, &context.iosb, 0);
|
||||
break;
|
||||
|
|
@ -12986,31 +13052,18 @@ fn netInterfaceNameResolve(
|
|||
};
|
||||
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return .{ .index = @bitCast(ifr.ifru.ivalue) };
|
||||
},
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |e| {
|
||||
syscall.finish();
|
||||
switch (e) {
|
||||
.INVAL => |err| return errnoBug(err), // Bad parameters.
|
||||
.NOTTY => |err| return errnoBug(err),
|
||||
.NXIO => |err| return errnoBug(err),
|
||||
.BADF => |err| return errnoBug(err), // File descriptor used after closed.
|
||||
.FAULT => |err| return errnoBug(err), // Bad pointer parameter.
|
||||
.IO => |err| return errnoBug(err), // sock_fd is not a file descriptor
|
||||
.NODEV => return error.InterfaceNotFound,
|
||||
else => |err| return posix.unexpectedErrno(err),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
while (true) switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
return .{ .index = @bitCast(ifr.ifru.ivalue) };
|
||||
},
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
.NODEV => return syscall.fail(error.InterfaceNotFound),
|
||||
else => |err| return syscall.unexpectedErrno(err),
|
||||
};
|
||||
}
|
||||
|
||||
if (is_windows) {
|
||||
|
|
@ -17849,3 +17902,88 @@ fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deviceIoControl(t: *Threaded, o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {
|
||||
_ = t;
|
||||
if (is_windows) {
|
||||
var iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
if (o.file.flags.nonblocking) {
|
||||
var done: bool = false;
|
||||
switch (windows.ntdll.NtDeviceIoControlFile(
|
||||
o.file.handle,
|
||||
null, // event
|
||||
flagApc,
|
||||
&done, // APC context
|
||||
&iosb,
|
||||
o.IoControlCode,
|
||||
o.InputBuffer,
|
||||
o.InputBufferLength,
|
||||
o.OutputBuffer,
|
||||
o.OutputBufferLength,
|
||||
)) {
|
||||
// We must wait for the APC routine.
|
||||
.PENDING, .SUCCESS => while (!done) {
|
||||
// Once we get here we must not return from the function until the
|
||||
// operation completes, thereby releasing reference to io_status_block.
|
||||
const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
|
||||
error.Canceled => |e| {
|
||||
var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
|
||||
_ = windows.ntdll.NtCancelIoFileEx(o.file.handle, &iosb, &cancel_iosb);
|
||||
while (!done) waitForApcOrAlert();
|
||||
return e;
|
||||
},
|
||||
};
|
||||
waitForApcOrAlert();
|
||||
alertable_syscall.finish();
|
||||
},
|
||||
else => |status| iosb.u.Status = status,
|
||||
}
|
||||
} else {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) switch (windows.ntdll.NtDeviceIoControlFile(
|
||||
o.file.handle,
|
||||
null, // event
|
||||
null, // APC routine
|
||||
null, // APC context
|
||||
&iosb,
|
||||
o.IoControlCode,
|
||||
o.InputBuffer,
|
||||
o.InputBufferLength,
|
||||
o.OutputBuffer,
|
||||
o.OutputBufferLength,
|
||||
)) {
|
||||
.PENDING => unreachable, // unrecoverable: wrong asynchronous flag
|
||||
.CANCELLED => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |status| {
|
||||
syscall.finish();
|
||||
iosb.u.Status = status;
|
||||
break;
|
||||
},
|
||||
};
|
||||
}
|
||||
return iosb;
|
||||
} else {
|
||||
const syscall: Syscall = try .start();
|
||||
while (true) {
|
||||
const rc = posix.system.ioctl(o.file.handle, @bitCast(o.code), @intFromPtr(o.arg));
|
||||
switch (posix.errno(rc)) {
|
||||
.SUCCESS => {
|
||||
syscall.finish();
|
||||
if (@TypeOf(rc) == usize) return @bitCast(@as(u32, @truncate(rc)));
|
||||
return rc;
|
||||
},
|
||||
.INTR => {
|
||||
try syscall.checkCancel();
|
||||
continue;
|
||||
},
|
||||
else => |err| {
|
||||
syscall.finish();
|
||||
return -@as(i32, @intFromEnum(err));
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -573,7 +573,7 @@ fn updateTask(io: Io) void {
|
|||
{
|
||||
const resize_flag = wait(io, global_progress.initial_delay_ns);
|
||||
if (@atomicLoad(bool, &global_progress.done, .monotonic)) return;
|
||||
maybeUpdateSize(resize_flag);
|
||||
maybeUpdateSize(io, resize_flag) catch return;
|
||||
|
||||
const buffer, _ = computeRedraw(&serialized_buffer);
|
||||
if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| {
|
||||
|
|
@ -592,7 +592,7 @@ fn updateTask(io: Io) void {
|
|||
return clearWrittenWithEscapeCodes(stderr.file_writer) catch {};
|
||||
}
|
||||
|
||||
maybeUpdateSize(resize_flag);
|
||||
maybeUpdateSize(io, resize_flag) catch return;
|
||||
|
||||
const buffer, _ = computeRedraw(&serialized_buffer);
|
||||
if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| {
|
||||
|
|
@ -622,7 +622,7 @@ fn windowsApiUpdateTask(io: Io) void {
|
|||
{
|
||||
const resize_flag = wait(io, global_progress.initial_delay_ns);
|
||||
if (@atomicLoad(bool, &global_progress.done, .monotonic)) return;
|
||||
maybeUpdateSize(resize_flag);
|
||||
maybeUpdateSize(io, resize_flag) catch return;
|
||||
|
||||
const buffer, const nl_n = computeRedraw(&serialized_buffer);
|
||||
if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| {
|
||||
|
|
@ -643,7 +643,7 @@ fn windowsApiUpdateTask(io: Io) void {
|
|||
return clearWrittenWindowsApi() catch {};
|
||||
}
|
||||
|
||||
maybeUpdateSize(resize_flag);
|
||||
maybeUpdateSize(io, resize_flag) catch return;
|
||||
|
||||
const buffer, const nl_n = computeRedraw(&serialized_buffer);
|
||||
if (io.vtable.tryLockStderr(io.userdata, null) catch return) |locked_stderr| {
|
||||
|
|
@ -1484,15 +1484,15 @@ fn writevNonblock(io: Io, file: Io.File, iov: [][]const u8) Io.File.Writer.Error
|
|||
}
|
||||
}
|
||||
|
||||
fn maybeUpdateSize(resize_flag: bool) void {
|
||||
fn maybeUpdateSize(io: Io, resize_flag: bool) !void {
|
||||
if (!resize_flag) return;
|
||||
|
||||
const fd = global_progress.terminal.handle;
|
||||
const file = global_progress.terminal;
|
||||
|
||||
if (is_windows) {
|
||||
var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
|
||||
|
||||
if (windows.kernel32.GetConsoleScreenBufferInfo(fd, &info) != windows.FALSE) {
|
||||
if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.FALSE) {
|
||||
// In the old Windows console, dwSize.Y is the line count of the
|
||||
// entire scrollback buffer, so we use this instead so that we
|
||||
// always get the size of the screen.
|
||||
|
|
@ -1512,8 +1512,13 @@ fn maybeUpdateSize(resize_flag: bool) void {
|
|||
.ypixel = 0,
|
||||
};
|
||||
|
||||
const err = posix.system.ioctl(fd, posix.T.IOCGWINSZ, @intFromPtr(&winsize));
|
||||
if (posix.errno(err) == .SUCCESS) {
|
||||
const err = (try io.operate(.{ .device_io_control = .{
|
||||
.file = file,
|
||||
.code = posix.T.IOCGWINSZ,
|
||||
.arg = &winsize,
|
||||
} })).device_io_control;
|
||||
|
||||
if (err >= 0) {
|
||||
global_progress.rows = winsize.row;
|
||||
global_progress.cols = winsize.col;
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -10731,7 +10731,6 @@ pub extern "c" fn sysctlnametomib(name: [*:0]const u8, mibp: ?*c_int, sizep: ?*u
|
|||
pub extern "c" fn tcgetattr(fd: fd_t, termios_p: *termios) c_int;
|
||||
pub extern "c" fn tcsetattr(fd: fd_t, optional_action: TCSA, termios_p: *const termios) c_int;
|
||||
pub extern "c" fn fcntl(fd: fd_t, cmd: c_int, ...) c_int;
|
||||
pub extern "c" fn ioctl(fd: fd_t, request: c_int, ...) c_int;
|
||||
pub extern "c" fn uname(buf: *utsname) c_int;
|
||||
|
||||
pub extern "c" fn gethostname(name: [*]u8, len: usize) c_int;
|
||||
|
|
@ -11108,6 +11107,11 @@ pub const clock_nanosleep = switch (native_os) {
|
|||
else => {},
|
||||
};
|
||||
|
||||
pub const ioctl = switch (native_os) {
|
||||
.windows, .wasi => {},
|
||||
else => private.ioctl,
|
||||
};
|
||||
|
||||
// OS-specific bits. These are protected from being used on the wrong OS by
|
||||
// comptime assertions inside each OS-specific file.
|
||||
|
||||
|
|
@ -11495,6 +11499,7 @@ const private = struct {
|
|||
};
|
||||
extern "c" fn getrusage(who: c_int, usage: *rusage) c_int;
|
||||
extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int;
|
||||
extern "c" fn ioctl(fd: fd_t, request: c_int, ...) c_int;
|
||||
extern "c" fn msync(addr: *align(page_size) const anyopaque, len: usize, flags: c_int) c_int;
|
||||
extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int;
|
||||
extern "c" fn clock_nanosleep(clockid: clockid_t, flags: TIMER, t: *const timespec, remain: ?*timespec) c_int;
|
||||
|
|
|
|||
|
|
@ -1800,28 +1800,6 @@ pub fn name_to_handle_atZ(
|
|||
}
|
||||
}
|
||||
|
||||
pub const IoCtl_SIOCGIFINDEX_Error = error{
|
||||
FileSystem,
|
||||
InterfaceNotFound,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
|
||||
while (true) {
|
||||
switch (errno(system.ioctl(fd, SIOCGIFINDEX, @intFromPtr(ifr)))) {
|
||||
.SUCCESS => return,
|
||||
.INVAL => unreachable, // Bad parameters.
|
||||
.NOTTY => unreachable,
|
||||
.NXIO => unreachable,
|
||||
.BADF => unreachable, // Always a race condition.
|
||||
.FAULT => unreachable, // Bad pointer parameter.
|
||||
.INTR => continue,
|
||||
.IO => return error.FileSystem,
|
||||
.NODEV => return error.InterfaceNotFound,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const lfs64_abi = native_os == .linux and builtin.link_libc and (builtin.abi.isGnu() or builtin.abi.isAndroid());
|
||||
|
||||
/// Whether or not `error.Unexpected` will print its value and a stack trace.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue