From 6b4f45f782fb9c8906d3fcbfca9e21fc3771c2dd Mon Sep 17 00:00:00 2001 From: rpkak Date: Fri, 14 Nov 2025 23:16:55 +0100 Subject: [PATCH] system specific errno --- lib/std/Io/Threaded.zig | 14 ++++----- lib/std/Thread.zig | 4 +-- lib/std/Thread/Futex.zig | 4 +-- lib/std/c.zig | 7 ++++- lib/std/fs/Dir.zig | 2 +- lib/std/os/linux.zig | 27 ++++------------ lib/std/os/linux/IoUring.zig | 16 +++++----- lib/std/os/linux/bpf.zig | 2 +- lib/std/os/linux/test.zig | 60 ++++++++++++++++++++---------------- lib/std/os/linux/tls.zig | 2 +- lib/std/os/plan9.zig | 14 +++++---- lib/std/posix.zig | 25 ++++++--------- lib/std/process.zig | 2 +- src/link/Elf/ZigObject.zig | 4 +-- src/link/MappedFile.zig | 6 ++-- 15 files changed, 91 insertions(+), 98 deletions(-) diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 665e006e8a..8dd9ae13fc 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -1302,7 +1302,7 @@ fn dirStatPathLinux( linux.STATX_INO | linux.STATX_SIZE | linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, &statx, ); - switch (linux.E.init(rc)) { + switch (linux.errno(rc)) { .SUCCESS => return statFromLinux(&statx), .INTR => continue, .CANCELED => return error.Canceled, @@ -1449,7 +1449,7 @@ fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File linux.STATX_INO | linux.STATX_SIZE | linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, &statx, ); - switch (linux.E.init(rc)) { + switch (linux.errno(rc)) { .SUCCESS => return statFromLinux(&statx), .INTR => continue, .CANCELED => return error.Canceled, @@ -2931,7 +2931,7 @@ fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void { var timespec: posix.timespec = timestampToPosix(deadline_nanoseconds); while (true) { try t.checkCancel(); - switch (std.os.linux.E.init(std.os.linux.clock_nanosleep(clock_id, .{ .ABSTIME = switch (timeout) { + switch (std.os.linux.errno(std.os.linux.clock_nanosleep(clock_id, .{ .ABSTIME = switch (timeout) { .none, .duration => false, .deadline => true, } }, ×pec, ×pec))) { @@ -5677,7 +5677,7 @@ fn futexWait(t: *Threaded, ptr: *const std.atomic.Value(u32), expect: u32) Io.Ca const linux = std.os.linux; try t.checkCancel(); const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, null); - if (is_debug) switch (linux.E.init(rc)) { + if (is_debug) switch (linux.errno(rc)) { .SUCCESS => {}, // notified by `wake()` .INTR => {}, // gives caller a chance to check cancellation .AGAIN => {}, // ptr.* != expect @@ -5764,7 +5764,7 @@ pub fn futexWaitUncancelable(ptr: *const std.atomic.Value(u32), expect: u32) voi .linux => { const linux = std.os.linux; const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, null); - switch (linux.E.init(rc)) { + switch (linux.errno(rc)) { .SUCCESS => {}, // notified by `wake()` .INTR => {}, // gives caller a chance to check cancellation .AGAIN => {}, // ptr.* != expect @@ -5827,7 +5827,7 @@ pub fn futexWaitDurationUncancelable(ptr: *const std.atomic.Value(u32), expect: const linux = std.os.linux; var ts = timestampToPosix(timeout.toNanoseconds()); const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, &ts); - if (is_debug) switch (linux.E.init(rc)) { + if (is_debug) switch (linux.errno(rc)) { .SUCCESS => {}, // notified by `wake()` .INTR => {}, // gives caller a chance to check cancellation .AGAIN => {}, // ptr.* != expect @@ -5861,7 +5861,7 @@ pub fn futexWake(ptr: *const std.atomic.Value(u32), max_waiters: u32) void { } else switch (native_os) { .linux => { const linux = std.os.linux; - switch (linux.E.init(linux.futex_3arg( + switch (linux.errno(linux.futex_3arg( &ptr.raw, .{ .cmd = .WAKE, .private = true }, @min(max_waiters, std.math.maxInt(i32)), diff --git a/lib/std/Thread.zig b/lib/std/Thread.zig index c984038f46..07b3c9076b 100644 --- a/lib/std/Thread.zig +++ b/lib/std/Thread.zig @@ -1622,7 +1622,7 @@ const LinuxThreadImpl = struct { linux.CLONE.PARENT_SETTID | linux.CLONE.CHILD_CLEARTID | linux.CLONE.SIGHAND | linux.CLONE.SYSVSEM | linux.CLONE.SETTLS; - switch (linux.E.init(linux.clone( + switch (linux.errno(linux.clone( Instance.entryFn, @intFromPtr(&mapped[stack_offset]), flags, @@ -1661,7 +1661,7 @@ const LinuxThreadImpl = struct { const tid = self.thread.child_tid.load(.seq_cst); if (tid == 0) break; - switch (linux.E.init(linux.futex_4arg( + switch (linux.errno(linux.futex_4arg( &self.thread.child_tid.raw, .{ .cmd = .WAIT, .private = false }, @bitCast(tid), diff --git a/lib/std/Thread/Futex.zig b/lib/std/Thread/Futex.zig index 6c7b58a540..b61062c361 100644 --- a/lib/std/Thread/Futex.zig +++ b/lib/std/Thread/Futex.zig @@ -269,7 +269,7 @@ const LinuxImpl = struct { if (timeout != null) &ts else null, ); - switch (linux.E.init(rc)) { + switch (linux.errno(rc)) { .SUCCESS => {}, // notified by `wake()` .INTR => {}, // spurious wakeup .AGAIN => {}, // ptr.* != expect @@ -290,7 +290,7 @@ const LinuxImpl = struct { @min(max_waiters, std.math.maxInt(i32)), ); - switch (linux.E.init(rc)) { + switch (linux.errno(rc)) { .SUCCESS => {}, // successful wake up .INVAL => {}, // invalid futex_wait() on ptr done elsewhere .FAULT => {}, // pointer became invalid while doing the wake diff --git a/lib/std/c.zig b/lib/std/c.zig index 2d95c77414..f22112a086 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -72,6 +72,11 @@ pub inline fn versionCheck(comptime version: std.SemanticVersion) bool { }; } +/// Get the errno if rc is -1 and SUCCESS if rc is not -1. +pub fn errno(rc: anytype) E { + return if (rc == -1) @enumFromInt(_errno().*) else .SUCCESS; +} + pub const ino_t = switch (native_os) { .linux => linux.ino_t, .emscripten => emscripten.ino_t, @@ -11580,6 +11585,6 @@ const private = struct { extern threadlocal var errno: c_int; fn errnoFromThreadLocal() *c_int { - return &errno; + return &private.errno; } }; diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 1eea1e6a9d..23bd903fe8 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -378,7 +378,7 @@ pub const Iterator = switch (native_os) { self.first_iter = false; } const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len); - switch (linux.E.init(rc)) { + switch (linux.errno(rc)) { .SUCCESS => {}, .BADF => unreachable, // Dir is invalid or was opened without iteration ability .FAULT => unreachable, diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index acb588bcae..2029356a66 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -568,9 +568,8 @@ fn splitValue64(val: i64) [2]u32 { } } -/// Get the errno from a syscall return value, or 0 for no error. -/// The public API is exposed via the `E` namespace. -fn errnoFromSyscall(r: usize) E { +/// Get the errno from a syscall return value. SUCCESS means no error. +pub fn errno(r: usize) E { const signed_r: isize = @bitCast(r); const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0; return @enumFromInt(int); @@ -1961,7 +1960,7 @@ pub fn sigaction(sig: SIG, noalias act: ?*const Sigaction, noalias oact: ?*Sigac .sparc, .sparc64 => syscall5(.rt_sigaction, @intFromEnum(sig), ksa_arg, oldksa_arg, @intFromPtr(ksa.restorer), mask_size), else => syscall4(.rt_sigaction, @intFromEnum(sig), ksa_arg, oldksa_arg, mask_size), }; - if (E.init(result) != .SUCCESS) return result; + if (errno(result) != .SUCCESS) return result; if (oact) |old| { old.handler.handler = oldksa.handler; @@ -2402,7 +2401,7 @@ pub fn sched_setaffinity(pid: pid_t, set: *const cpu_set_t) !void { const size = @sizeOf(cpu_set_t); const rc = syscall3(.sched_setaffinity, @as(usize, @bitCast(@as(isize, pid))), size, @intFromPtr(set)); - switch (E.init(rc)) { + switch (errno(rc)) { .SUCCESS => return, else => |err| return std.posix.unexpectedErrno(err), } @@ -3003,8 +3002,6 @@ pub const E = switch (native_arch) { HWPOISON = 168, DQUOT = 1133, _, - - pub const init = errnoFromSyscall; }, .sparc, .sparc64 => enum(u16) { /// No error occurred. @@ -3148,8 +3145,6 @@ pub const E = switch (native_arch) { RFKILL = 134, HWPOISON = 135, _, - - pub const init = errnoFromSyscall; }, else => enum(u16) { /// No error occurred. @@ -3459,8 +3454,6 @@ pub const E = switch (native_arch) { NSRCNAMELOOP = 177, _, - - pub const init = errnoFromSyscall; }, }; @@ -9892,7 +9885,7 @@ pub const wrapped = struct { const adjusted_len = @min(in_len, 0x7ffff000); // Prevents EOVERFLOW. const sendfileSymbol = if (lfs64_abi) system.sendfile64 else system.sendfile; const rc = sendfileSymbol(out_fd, in_fd, in_offset, adjusted_len); - switch (errno(rc)) { + switch (system.errno(rc)) { .SUCCESS => return @intCast(rc), .BADF => return invalidApiUsage(), // Always a race condition. .FAULT => return invalidApiUsage(), // Segmentation fault. @@ -9958,7 +9951,7 @@ pub const wrapped = struct { const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ .major = 34, .minor = 0, .patch = 0 } else .{ .major = 2, .minor = 27, .patch = 0 }); const sys = if (use_c) std.c else std.os.linux; const rc = sys.copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); - switch (errno(rc)) { + switch (sys.errno(rc)) { .SUCCESS => return @intCast(rc), .BADF => return error.BadFileFlags, .FBIG => return error.FileTooBig, @@ -9982,12 +9975,4 @@ pub const wrapped = struct { if (builtin.mode == .Debug) @panic("invalid API usage"); return error.Unexpected; } - - fn errno(rc: anytype) E { - if (builtin.link_libc) { - return if (rc == -1) @enumFromInt(std.c._errno().*) else .SUCCESS; - } else { - return errnoFromSyscall(rc); - } - } }; diff --git a/lib/std/os/linux/IoUring.zig b/lib/std/os/linux/IoUring.zig index f5f68a0ebe..4992a352d2 100644 --- a/lib/std/os/linux/IoUring.zig +++ b/lib/std/os/linux/IoUring.zig @@ -46,7 +46,7 @@ pub fn init_params(entries: u16, p: *linux.io_uring_params) !IoUring { assert(p.resv[2] == 0); const res = linux.io_uring_setup(entries, p); - switch (linux.E.init(res)) { + switch (linux.errno(res)) { .SUCCESS => {}, .FAULT => return error.ParamsOutsideAccessibleAddressSpace, // The resv array contains non-zero data, p.flags contains an unsupported flag, @@ -175,7 +175,7 @@ pub fn submit_and_wait(self: *IoUring, wait_nr: u32) !u32 { pub fn enter(self: *IoUring, to_submit: u32, min_complete: u32, flags: u32) !u32 { assert(self.fd >= 0); const res = linux.io_uring_enter(self.fd, to_submit, min_complete, flags, null); - switch (linux.E.init(res)) { + switch (linux.errno(res)) { .SUCCESS => {}, // The kernel was unable to allocate memory or ran out of resources for the request. // The application should wait for some completions and try again: @@ -1298,7 +1298,7 @@ pub fn register_buffers(self: *IoUring, buffers: []const posix.iovec) !void { pub fn unregister_buffers(self: *IoUring) !void { assert(self.fd >= 0); const res = linux.io_uring_register(self.fd, .UNREGISTER_BUFFERS, null, 0); - switch (linux.E.init(res)) { + switch (linux.errno(res)) { .SUCCESS => {}, .NXIO => return error.BuffersNotRegistered, else => |errno| return posix.unexpectedErrno(errno), @@ -1316,7 +1316,7 @@ pub fn get_probe(self: *IoUring) !linux.io_uring_probe { } fn handle_registration_result(res: usize) !void { - switch (linux.E.init(res)) { + switch (linux.errno(res)) { .SUCCESS => {}, // One or more fds in the array are invalid, or the kernel does not support sparse sets: .BADF => return error.FileDescriptorInvalid, @@ -1341,7 +1341,7 @@ fn handle_registration_result(res: usize) !void { pub fn unregister_files(self: *IoUring) !void { assert(self.fd >= 0); const res = linux.io_uring_register(self.fd, .UNREGISTER_FILES, null, 0); - switch (linux.E.init(res)) { + switch (linux.errno(res)) { .SUCCESS => {}, .NXIO => return error.FilesNotRegistered, else => |errno| return posix.unexpectedErrno(errno), @@ -1771,7 +1771,7 @@ fn register_buf_ring( .flags = flags, }); var res = linux.io_uring_register(fd, .REGISTER_PBUF_RING, @as(*const anyopaque, @ptrCast(®)), 1); - if (linux.E.init(res) == .INVAL and reg.flags.inc) { + if (linux.errno(res) == .INVAL and reg.flags.inc) { // Retry without incremental buffer consumption. // It is available since kernel 6.12. returns INVAL on older. reg.flags.inc = false; @@ -1794,7 +1794,7 @@ fn unregister_buf_ring(fd: linux.fd_t, group_id: u16) !void { } fn handle_register_buf_ring_result(res: usize) !void { - switch (linux.E.init(res)) { + switch (linux.errno(res)) { .SUCCESS => {}, .INVAL => return error.ArgumentsInvalid, else => |errno| return posix.unexpectedErrno(errno), @@ -4085,7 +4085,7 @@ inline fn skipKernelLessThan(required: std.SemanticVersion) !void { var uts: linux.utsname = undefined; const res = linux.uname(&uts); - switch (linux.E.init(res)) { + switch (linux.errno(res)) { .SUCCESS => {}, else => |errno| return posix.unexpectedErrno(errno), } diff --git a/lib/std/os/linux/bpf.zig b/lib/std/os/linux/bpf.zig index a0d5956af8..b7c5c7d4e9 100644 --- a/lib/std/os/linux/bpf.zig +++ b/lib/std/os/linux/bpf.zig @@ -1,5 +1,5 @@ const std = @import("../../std.zig"); -const errno = linux.E.init; +const errno = linux.errno; const unexpectedErrno = std.posix.unexpectedErrno; const expectEqual = std.testing.expectEqual; const expectError = std.testing.expectError; diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index 6b658b87c0..f5533a54bd 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -22,7 +22,7 @@ test "fallocate" { try expect((try file.stat()).size == 0); const len: i64 = 65536; - switch (linux.E.init(linux.fallocate(file.handle, 0, 0, len))) { + switch (linux.errno(linux.fallocate(file.handle, 0, 0, len))) { .SUCCESS => {}, .NOSYS => return error.SkipZigTest, .OPNOTSUPP => return error.SkipZigTest, @@ -42,11 +42,11 @@ test "getppid" { test "timer" { const epoll_fd = linux.epoll_create(); - var err: linux.E = linux.E.init(epoll_fd); + var err: linux.E = linux.errno(epoll_fd); try expect(err == .SUCCESS); const timer_fd = linux.timerfd_create(linux.TIMERFD_CLOCK.MONOTONIC, .{}); - try expect(linux.E.init(timer_fd) == .SUCCESS); + try expect(linux.errno(timer_fd) == .SUCCESS); const time_interval = linux.timespec{ .sec = 0, @@ -58,7 +58,7 @@ test "timer" { .it_value = time_interval, }; - err = linux.E.init(linux.timerfd_settime(@as(i32, @intCast(timer_fd)), .{}, &new_time, null)); + err = linux.errno(linux.timerfd_settime(@as(i32, @intCast(timer_fd)), .{}, &new_time, null)); try expect(err == .SUCCESS); var event = linux.epoll_event{ @@ -66,13 +66,13 @@ test "timer" { .data = linux.epoll_data{ .ptr = 0 }, }; - err = linux.E.init(linux.epoll_ctl(@as(i32, @intCast(epoll_fd)), linux.EPOLL.CTL_ADD, @as(i32, @intCast(timer_fd)), &event)); + err = linux.errno(linux.epoll_ctl(@as(i32, @intCast(epoll_fd)), linux.EPOLL.CTL_ADD, @as(i32, @intCast(timer_fd)), &event)); try expect(err == .SUCCESS); const events_one: linux.epoll_event = undefined; var events = [_]linux.epoll_event{events_one} ** 8; - err = linux.E.init(linux.epoll_wait(@as(i32, @intCast(epoll_fd)), &events, 8, -1)); + err = linux.errno(linux.epoll_wait(@as(i32, @intCast(epoll_fd)), &events, 8, -1)); try expect(err == .SUCCESS); } @@ -85,7 +85,7 @@ test "statx" { defer file.close(); var statx_buf: linux.Statx = undefined; - switch (linux.E.init(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { + switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { .SUCCESS => {}, else => unreachable, } @@ -93,7 +93,7 @@ test "statx" { if (builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) return error.SkipZigTest; // No fstatat, so the rest of the test is meaningless. var stat_buf: linux.Stat = undefined; - switch (linux.E.init(linux.fstatat(file.handle, "", &stat_buf, linux.AT.EMPTY_PATH))) { + switch (linux.errno(linux.fstatat(file.handle, "", &stat_buf, linux.AT.EMPTY_PATH))) { .SUCCESS => {}, else => unreachable, } @@ -179,7 +179,7 @@ test "sigemptyset" { test "sysinfo" { var info: linux.Sysinfo = undefined; const result: usize = linux.sysinfo(&info); - try expect(std.os.linux.E.init(result) == .SUCCESS); + try expect(std.os.linux.errno(result) == .SUCCESS); try expect(info.mem_unit > 0); try expect(info.mem_unit <= std.heap.page_size_max); @@ -202,17 +202,17 @@ test "futex v1" { // No-op wait, lock value is not expected value rc = linux.futex(&lock.raw, .{ .cmd = .WAIT, .private = true }, 2, .{ .timeout = null }, null, 0); - try expectEqual(.AGAIN, linux.E.init(rc)); + try expectEqual(.AGAIN, linux.errno(rc)); rc = linux.futex_4arg(&lock.raw, .{ .cmd = .WAIT, .private = true }, 2, null); - try expectEqual(.AGAIN, linux.E.init(rc)); + try expectEqual(.AGAIN, linux.errno(rc)); // Short-fuse wait, timeout kicks in rc = linux.futex(&lock.raw, .{ .cmd = .WAIT, .private = true }, 1, .{ .timeout = &.{ .sec = 0, .nsec = 2 } }, null, 0); - try expectEqual(.TIMEDOUT, linux.E.init(rc)); + try expectEqual(.TIMEDOUT, linux.errno(rc)); rc = linux.futex_4arg(&lock.raw, .{ .cmd = .WAIT, .private = true }, 1, &.{ .sec = 0, .nsec = 2 }); - try expectEqual(.TIMEDOUT, linux.E.init(rc)); + try expectEqual(.TIMEDOUT, linux.errno(rc)); // Wakeup (no waiters) rc = linux.futex(&lock.raw, .{ .cmd = .WAKE, .private = true }, 2, .{ .timeout = null }, null, 0); @@ -223,7 +223,7 @@ test "futex v1" { // CMP_REQUEUE - val3 mismatch rc = linux.futex(&lock.raw, .{ .cmd = .CMP_REQUEUE, .private = true }, 2, .{ .val2 = 0 }, null, 99); - try expectEqual(.AGAIN, linux.E.init(rc)); + try expectEqual(.AGAIN, linux.errno(rc)); // CMP_REQUEUE - requeue (but no waiters, so ... not much) { @@ -256,12 +256,12 @@ test "futex v1" { { // val1 return early rc = linux.futex(&lock.raw, .{ .cmd = .WAIT_BITSET, .private = true }, 2, .{ .timeout = null }, null, 0xfff); - try expectEqual(.AGAIN, linux.E.init(rc)); + try expectEqual(.AGAIN, linux.errno(rc)); // timeout wait const timeout: linux.timespec = .{ .sec = 0, .nsec = 2 }; rc = linux.futex(&lock.raw, .{ .cmd = .WAIT_BITSET, .private = true }, 1, .{ .timeout = &timeout }, null, 0xfff); - try expectEqual(.TIMEDOUT, linux.E.init(rc)); + try expectEqual(.TIMEDOUT, linux.errno(rc)); } // WAKE_BITSET @@ -271,7 +271,7 @@ test "futex v1" { // bitmask must have at least 1 bit set: rc = linux.futex(&lock.raw, .{ .cmd = .WAKE_BITSET, .private = true }, 2, .{ .timeout = null }, null, 0); - try expectEqual(.INVAL, linux.E.init(rc)); + try expectEqual(.INVAL, linux.errno(rc)); } } @@ -307,7 +307,7 @@ test "futex2_waitv" { const timeout = linux.kernel_timespec{ .sec = 0, .nsec = 2 }; // absolute timeout, so this is 1970... const rc = linux.futex2_waitv(&futexes, futexes.len, .{}, &timeout, .MONOTONIC); - switch (linux.E.init(rc)) { + switch (linux.errno(rc)) { .NOSYS => return error.SkipZigTest, // futex2_waitv added in kernel v5.16 else => |err| try expectEqual(.TIMEDOUT, err), } @@ -318,7 +318,7 @@ test "futex2_waitv" { fn futex2_skip_if_unsupported() !void { const lock: u32 = 0; const rc = linux.futex2_wake(&lock, 0, 1, .{ .size = .U32, .private = true }); - if (linux.E.init(rc) == .NOSYS) { + if (linux.errno(rc) == .NOSYS) { return error.SkipZigTest; } } @@ -334,23 +334,23 @@ test "futex2_wait" { // (at least) they're not implemented. if (false) { rc = linux.futex2_wait(&lock.raw, 1, mask, .{ .size = .U8, .private = true }, null, .MONOTONIC); - try expectEqual(.INVAL, linux.E.init(rc)); + try expectEqual(.INVAL, linux.errno(rc)); rc = linux.futex2_wait(&lock.raw, 1, mask, .{ .size = .U16, .private = true }, null, .MONOTONIC); - try expectEqual(.INVAL, linux.E.init(rc)); + try expectEqual(.INVAL, linux.errno(rc)); rc = linux.futex2_wait(&lock.raw, 1, mask, .{ .size = .U64, .private = true }, null, .MONOTONIC); - try expectEqual(.INVAL, linux.E.init(rc)); + try expectEqual(.INVAL, linux.errno(rc)); } const flags = linux.FUTEX2_FLAGS{ .size = .U32, .private = true }; // no-wait, lock state mismatch rc = linux.futex2_wait(&lock.raw, 2, mask, flags, null, .MONOTONIC); - try expectEqual(.AGAIN, linux.E.init(rc)); + try expectEqual(.AGAIN, linux.errno(rc)); // hit timeout on wait rc = linux.futex2_wait(&lock.raw, 1, mask, flags, &.{ .sec = 0, .nsec = 2 }, .MONOTONIC); - try expectEqual(.TIMEDOUT, linux.E.init(rc)); + try expectEqual(.TIMEDOUT, linux.errno(rc)); // timeout is absolute { @@ -364,11 +364,11 @@ test "futex2_wait" { .nsec = curr.nsec + 2, }; rc = linux.futex2_wait(&lock.raw, 1, mask, flags, &timeout, .MONOTONIC); - try expectEqual(.TIMEDOUT, linux.E.init(rc)); + try expectEqual(.TIMEDOUT, linux.errno(rc)); } rc = linux.futex2_wait(&lock.raw, 1, mask, flags, &.{ .sec = 0, .nsec = 2 }, .REALTIME); - try expectEqual(.TIMEDOUT, linux.E.init(rc)); + try expectEqual(.TIMEDOUT, linux.errno(rc)); } test "futex2_wake" { @@ -405,6 +405,14 @@ test "futex2_requeue" { try expectEqual(0, rc); } +test "copy_file_range error" { + const fds = try std.posix.pipe(); + defer std.posix.close(fds[0]); + defer std.posix.close(fds[1]); + + try std.testing.expectError(error.InvalidArguments, linux.wrapped.copy_file_range(fds[0], null, fds[1], null, 1, 0)); +} + test { _ = linux.IoUring; } diff --git a/lib/std/os/linux/tls.zig b/lib/std/os/linux/tls.zig index 3f3e6f987d..cbec3cc00a 100644 --- a/lib/std/os/linux/tls.zig +++ b/lib/std/os/linux/tls.zig @@ -555,7 +555,7 @@ pub fn initStatic(phdrs: []elf.Phdr) void { } const begin_addr = mmap_tls(area_desc.size + area_desc.alignment - 1); - if (@call(.always_inline, linux.E.init, .{begin_addr}) != .SUCCESS) @trap(); + if (@call(.always_inline, linux.errno, .{begin_addr}) != .SUCCESS) @trap(); const area_ptr: [*]align(page_size_min) u8 = @ptrFromInt(begin_addr); diff --git a/lib/std/os/plan9.zig b/lib/std/os/plan9.zig index b910e26c71..36bf83b21c 100644 --- a/lib/std/os/plan9.zig +++ b/lib/std/os/plan9.zig @@ -94,13 +94,15 @@ pub const E = enum(u16) { OVERFLOW, LOOP, TXTBSY, - - pub fn init(r: usize) E { - const signed_r: isize = @bitCast(r); - const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0; - return @enumFromInt(int); - } }; + +/// Get the errno from a syscall return value. SUCCESS means no error. +pub fn errno(r: usize) E { + const signed_r: isize = @bitCast(r); + const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0; + return @enumFromInt(int); +} + // The max bytes that can be in the errstr buff pub const ERRMAX = 128; var errstr_buf: [ERRMAX]u8 = undefined; diff --git a/lib/std/posix.zig b/lib/std/posix.zig index dc402cc077..13d16ea324 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -272,14 +272,7 @@ pub const socket_t = if (native_os == .windows) windows.ws2_32.SOCKET else fd_t; /// for others it will use a thread-local errno variable. Therefore, this /// function only returns a well-defined value when it is called directly after /// the system function call whose errno value is intended to be observed. -pub fn errno(rc: anytype) E { - if (use_libc) { - return if (rc == -1) @enumFromInt(std.c._errno().*) else .SUCCESS; - } - const signed: isize = @bitCast(rc); - const int = if (signed > -4096 and signed < 0) -signed else 0; - return @enumFromInt(int); -} +pub const errno = system.errno; /// Closes the file descriptor. /// @@ -433,7 +426,7 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr // Later on this should be changed to `system.fchmodat2` // when the musl/glibc add a wrapper. const res = linux.fchmodat2(dirfd, &path_c, mode, flags); - switch (E.init(res)) { + switch (linux.errno(res)) { .SUCCESS => return, .INTR => continue, .BADF => unreachable, @@ -588,7 +581,7 @@ pub const RebootCommand = switch (native_os) { pub fn reboot(cmd: RebootCommand) RebootError!void { switch (native_os) { .linux => { - switch (linux.E.init(linux.reboot( + switch (linux.errno(linux.reboot( .MAGIC1, .MAGIC2, cmd, @@ -647,7 +640,7 @@ pub fn getrandom(buffer: []u8) GetRandomError!void { break :res .{ @bitCast(rc), errno(rc) }; } else res: { const rc = linux.getrandom(buf.ptr, buf.len, 0); - break :res .{ rc, linux.E.init(rc) }; + break :res .{ rc, linux.errno(rc) }; }; switch (err) { @@ -3298,7 +3291,7 @@ pub fn isatty(handle: fd_t) bool { var wsz: winsize = undefined; const fd: usize = @bitCast(@as(isize, handle)); const rc = linux.syscall3(.ioctl, fd, linux.T.IOCGWINSZ, @intFromPtr(&wsz)); - switch (linux.E.init(rc)) { + switch (linux.errno(rc)) { .SUCCESS => return true, .INTR => continue, else => return false, @@ -5762,7 +5755,7 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len while (true) { const rc = sys.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags); if (native_os == .freebsd) { - switch (errno(rc)) { + switch (sys.errno(rc)) { .SUCCESS => return @intCast(rc), .BADF => return error.FilesOpenedWithWrongFlags, .FBIG => return error.FileTooBig, @@ -5775,7 +5768,7 @@ pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len else => |err| return unexpectedErrno(err), } } else { // assume linux - switch (errno(rc)) { + switch (sys.errno(rc)) { .SUCCESS => return @intCast(rc), .BADF => return error.FilesOpenedWithWrongFlags, .FBIG => return error.FileTooBig, @@ -6070,7 +6063,7 @@ pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t { const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{ .major = 30, .minor = 0, .patch = 0 } else .{ .major = 2, .minor = 27, .patch = 0 }); const sys = if (use_c) std.c else linux; const rc = sys.memfd_create(name, flags); - switch (errno(rc)) { + switch (sys.errno(rc)) { .SUCCESS => return @intCast(rc), .FAULT => unreachable, // name has invalid memory .INVAL => return error.NameTooLong, // or, program has a bug and flags are faulty @@ -6494,7 +6487,7 @@ pub fn perf_event_open( if (native_os == .linux) { // There is no syscall wrapper for this function exposed by libcs const rc = linux.perf_event_open(attr, pid, cpu, group_fd, flags); - switch (errno(rc)) { + switch (linux.errno(rc)) { .SUCCESS => return @intCast(rc), .@"2BIG" => return error.TooBig, .ACCES => return error.PermissionDenied, diff --git a/lib/std/process.zig b/lib/std/process.zig index 1f605c87ce..7bb5e4f505 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -1772,7 +1772,7 @@ pub fn totalSystemMemory() TotalSystemMemoryError!u64 { .linux => { var info: std.os.linux.Sysinfo = undefined; const result: usize = std.os.linux.sysinfo(&info); - if (std.os.linux.E.init(result) != .SUCCESS) { + if (std.os.linux.errno(result) != .SUCCESS) { return error.UnknownTotalSystemMemory; } // Promote to u64 to avoid overflow on systems where info.totalram is a 32-bit usize diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index a77858f06c..1450e3ab92 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -1437,7 +1437,7 @@ fn updateNavCode( .len = code.len, }}; const rc = std.os.linux.process_vm_writev(pid, &code_vec, &remote_vec, 0); - switch (std.os.linux.E.init(rc)) { + switch (std.os.linux.errno(rc)) { .SUCCESS => assert(rc == code.len), else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), } @@ -2026,7 +2026,7 @@ fn writeTrampoline(tr_sym: Symbol, target: Symbol, elf_file: *Elf) !void { .len = out.len, }}; const rc = std.os.linux.process_vm_writev(pid, &local_vec, &remote_vec, 0); - switch (std.os.linux.E.init(rc)) { + switch (std.os.linux.errno(rc)) { .SUCCESS => assert(rc == out.len), else => |errno| log.warn("process_vm_writev failure: {s}", .{@tagName(errno)}), } diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig index f0294df350..09d940e85f 100644 --- a/src/link/MappedFile.zig +++ b/src/link/MappedFile.zig @@ -645,7 +645,7 @@ fn resizeNode(mf: *MappedFile, gpa: std.mem.Allocator, ni: Node.Index, requested @intCast(requested_size +| requested_size / growth_factor), ) - old_size; _, const file_size = Node.Index.root.location(mf).resolve(mf); - while (true) switch (linux.E.init(switch (std.math.order(range_file_offset, file_size)) { + while (true) switch (linux.errno(switch (std.math.order(range_file_offset, file_size)) { .lt => linux.fallocate( mf.file.handle, linux.FALLOC.FL_INSERT_RANGE, @@ -858,7 +858,7 @@ fn moveRange(mf: *MappedFile, old_file_offset: u64, new_file_offset: u64, size: // delete the copy of this node at the old location if (is_linux and !mf.flags.fallocate_punch_hole_unsupported and size >= mf.flags.block_size.toByteUnits() * 2 - 1) while (true) - switch (linux.E.init(linux.fallocate( + switch (linux.errno(linux.fallocate( mf.file.handle, linux.FALLOC.FL_PUNCH_HOLE | linux.FALLOC.FL_KEEP_SIZE, @intCast(old_file_offset), @@ -910,7 +910,7 @@ fn copyFileRange( @intCast(remaining_size), 0, ); - switch (linux.E.init(copy_len)) { + switch (linux.errno(copy_len)) { .SUCCESS => { if (copy_len == 0) break; remaining_size -= copy_len;