Commit 6b4f45f782

rpkak <rpkak@users.noreply.github.com>
2025-11-14 23:16:55
system specific errno
1 parent 4b5351b
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,
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,
         } }, &timespec, &timespec))) {
@@ -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)),
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;
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(&reg)), 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),
     }
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;
 }
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);
 
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);
-        }
-    }
 };
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;
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
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;
     }
 };
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,
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
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),
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)}),
                 }
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;