motiejus/zig

fork of https://codeberg.org/ziglang/zig
git clone https://git.jakstys.lt/motiejus/zig.git
Log | Tree | Refs | README | LICENSE

lib/std/Io/Threaded.zig (433260B) - Raw


      1 const Threaded = @This();
      2 
      3 const builtin = @import("builtin");
      4 const native_os = builtin.os.tag;
      5 const is_windows = native_os == .windows;
      6 const is_darwin = native_os.isDarwin();
      7 const is_debug = builtin.mode == .Debug;
      8 
      9 const std = @import("../std.zig");
     10 const Io = std.Io;
     11 const net = std.Io.net;
     12 const File = std.Io.File;
     13 const Dir = std.Io.Dir;
     14 const HostName = std.Io.net.HostName;
     15 const IpAddress = std.Io.net.IpAddress;
     16 const Allocator = std.mem.Allocator;
     17 const Alignment = std.mem.Alignment;
     18 const assert = std.debug.assert;
     19 const posix = std.posix;
     20 const windows = std.os.windows;
     21 const ws2_32 = std.os.windows.ws2_32;
     22 
     23 /// Thread-safe.
     24 allocator: Allocator,
     25 mutex: std.Thread.Mutex = .{},
     26 cond: std.Thread.Condition = .{},
     27 run_queue: std.SinglyLinkedList = .{},
     28 join_requested: bool = false,
     29 stack_size: usize,
     30 /// All threads are spawned detached; this is how we wait until they all exit.
     31 wait_group: std.Thread.WaitGroup = .{},
     32 /// Maximum thread pool size (excluding main thread) when dispatching async
     33 /// tasks. Until this limit, calls to `Io.async` when all threads are busy will
     34 /// cause a new thread to be spawned and permanently added to the pool. After
     35 /// this limit, calls to `Io.async` when all threads are busy run the task
     36 /// immediately.
     37 ///
     38 /// Defaults to a number equal to logical CPU cores.
     39 ///
     40 /// Protected by `mutex` once the I/O instance is already in use. See
     41 /// `setAsyncLimit`.
     42 async_limit: Io.Limit,
     43 /// Maximum thread pool size (excluding main thread) for dispatching concurrent
     44 /// tasks. Until this limit, calls to `Io.concurrent` will increase the thread
     45 /// pool size.
     46 ///
     47 /// concurrent tasks. After this number, calls to `Io.concurrent` return
     48 /// `error.ConcurrencyUnavailable`.
     49 concurrent_limit: Io.Limit = .unlimited,
     50 /// Error from calling `std.Thread.getCpuCount` in `init`.
     51 cpu_count_error: ?std.Thread.CpuCountError,
     52 /// Number of threads that are unavailable to take tasks. To calculate
     53 /// available count, subtract this from either `async_limit` or
     54 /// `concurrent_limit`.
     55 busy_count: usize = 0,
     56 main_thread: Thread,
     57 pid: Pid = .unknown,
     58 /// When a cancel request is made, blocking syscalls can be unblocked by
     59 /// issuing a signal. However, if the signal arrives after the check and before
     60 /// the syscall instruction, it is missed.
     61 ///
     62 /// This option solves the race condition by retrying the signal delivery
     63 /// until it is acknowledged, with an exponential backoff.
     64 ///
     65 /// Unfortunately, trying again until the cancellation request is acknowledged
     66 /// has been observed to be relatively slow, and usually strong cancellation
     67 /// guarantees are not needed, so this defaults to off.
     68 robust_cancel: RobustCancel = .disabled,
     69 
     70 wsa: if (is_windows) Wsa else struct {} = .{},
     71 
     72 have_signal_handler: bool,
     73 old_sig_io: if (have_sig_io) posix.Sigaction else void,
     74 old_sig_pipe: if (have_sig_pipe) posix.Sigaction else void,
     75 
     76 use_sendfile: UseSendfile = .default,
     77 use_copy_file_range: UseCopyFileRange = .default,
     78 use_fcopyfile: UseFcopyfile = .default,
     79 use_fchmodat2: UseFchmodat2 = .default,
     80 
     81 stderr_writer: File.Writer = .{
     82     .io = undefined,
     83     .interface = Io.File.Writer.initInterface(&.{}),
     84     .file = if (is_windows) undefined else .stderr(),
     85     .mode = undefined,
     86 },
     87 stderr_writer_initialized: bool = false,
     88 
     89 pub const RobustCancel = if (std.Thread.use_pthreads or native_os == .linux) enum {
     90     enabled,
     91     disabled,
     92 } else enum {
     93     disabled,
     94 };
     95 
     96 pub const Pid = if (native_os == .linux) enum(posix.pid_t) {
     97     unknown = 0,
     98     _,
     99 } else enum(u0) { unknown = 0 };
    100 
    101 pub const UseSendfile = if (have_sendfile) enum {
    102     enabled,
    103     disabled,
    104     pub const default: UseSendfile = .enabled;
    105 } else enum {
    106     disabled,
    107     pub const default: UseSendfile = .disabled;
    108 };
    109 
    110 pub const UseCopyFileRange = if (have_copy_file_range) enum {
    111     enabled,
    112     disabled,
    113     pub const default: UseCopyFileRange = .enabled;
    114 } else enum {
    115     disabled,
    116     pub const default: UseCopyFileRange = .disabled;
    117 };
    118 
    119 pub const UseFcopyfile = if (have_fcopyfile) enum {
    120     enabled,
    121     disabled,
    122     pub const default: UseFcopyfile = .enabled;
    123 } else enum {
    124     disabled,
    125     pub const default: UseFcopyfile = .disabled;
    126 };
    127 
    128 pub const UseFchmodat2 = if (have_fchmodat2 and !have_fchmodat_flags) enum {
    129     enabled,
    130     disabled,
    131     pub const default: UseFchmodat2 = .enabled;
    132 } else enum {
    133     disabled,
    134     pub const default: UseFchmodat2 = .disabled;
    135 };
    136 
    137 const Thread = struct {
    138     /// The value that needs to be passed to pthread_kill or tgkill in order to
    139     /// send a signal.
    140     signal_id: SignaleeId,
    141     current_closure: ?*Closure,
    142     /// Only populated if `current_closure != null`. Indicates the current cancel protection mode.
    143     cancel_protection: Io.CancelProtection,
    144 
    145     const SignaleeId = if (std.Thread.use_pthreads) std.c.pthread_t else std.Thread.Id;
    146 
    147     threadlocal var current: ?*Thread = null;
    148 
    149     fn getCurrent(t: *Threaded) *Thread {
    150         return current orelse return &t.main_thread;
    151     }
    152 
    153     fn checkCancel(thread: *Thread) error{Canceled}!void {
    154         const closure = thread.current_closure orelse return;
    155 
    156         switch (thread.cancel_protection) {
    157             .unblocked => {},
    158             .blocked => return,
    159         }
    160 
    161         switch (@cmpxchgStrong(
    162             CancelStatus,
    163             &closure.cancel_status,
    164             .requested,
    165             .acknowledged,
    166             .acq_rel,
    167             .acquire,
    168         ) orelse return error.Canceled) {
    169             .requested => unreachable,
    170             .acknowledged => unreachable,
    171             .none, _ => {},
    172         }
    173     }
    174 
    175     fn beginSyscall(thread: *Thread) error{Canceled}!void {
    176         const closure = thread.current_closure orelse return;
    177 
    178         switch (thread.cancel_protection) {
    179             .unblocked => {},
    180             .blocked => return,
    181         }
    182 
    183         switch (@cmpxchgStrong(
    184             CancelStatus,
    185             &closure.cancel_status,
    186             .none,
    187             .fromSignaleeId(thread.signal_id),
    188             .acq_rel,
    189             .acquire,
    190         ) orelse return) {
    191             .none => unreachable,
    192             .requested => {
    193                 @atomicStore(CancelStatus, &closure.cancel_status, .acknowledged, .release);
    194                 return error.Canceled;
    195             },
    196             .acknowledged => return,
    197             _ => unreachable,
    198         }
    199     }
    200 
    201     fn endSyscall(thread: *Thread) void {
    202         const closure = thread.current_closure orelse return;
    203 
    204         switch (thread.cancel_protection) {
    205             .unblocked => {},
    206             .blocked => return,
    207         }
    208 
    209         _ = @cmpxchgStrong(
    210             CancelStatus,
    211             &closure.cancel_status,
    212             .fromSignaleeId(thread.signal_id),
    213             .none,
    214             .acq_rel,
    215             .acquire,
    216         ) orelse return;
    217     }
    218 
    219     fn endSyscallCanceled(thread: *Thread) Io.Cancelable {
    220         if (thread.current_closure) |closure| {
    221             @atomicStore(CancelStatus, &closure.cancel_status, .acknowledged, .release);
    222         }
    223         return error.Canceled;
    224     }
    225 
    226     fn currentSignalId() SignaleeId {
    227         return if (std.Thread.use_pthreads) std.c.pthread_self() else std.Thread.getCurrentId();
    228     }
    229 
    230     fn futexWaitUncancelable(ptr: *const u32, expect: u32) void {
    231         return Thread.futexWaitTimed(null, ptr, expect, null) catch unreachable;
    232     }
    233 
    234     fn futexWait(thread: *Thread, ptr: *const u32, expect: u32) Io.Cancelable!void {
    235         return Thread.futexWaitTimed(thread, ptr, expect, null) catch |err| switch (err) {
    236             error.Canceled => return error.Canceled,
    237             error.Timeout => unreachable,
    238         };
    239     }
    240 
    241     fn futexWaitTimed(thread: ?*Thread, ptr: *const u32, expect: u32, timeout_ns: ?u64) Io.Cancelable!void {
    242         @branchHint(.cold);
    243 
    244         if (builtin.single_threaded) unreachable; // nobody would ever wake us
    245 
    246         if (builtin.cpu.arch.isWasm()) {
    247             comptime assert(builtin.cpu.has(.wasm, .atomics));
    248             if (thread) |t| try t.checkCancel();
    249             const to: i64 = if (timeout_ns) |ns| ns else -1;
    250             const signed_expect: i32 = @bitCast(expect);
    251             const result = asm volatile (
    252                 \\local.get %[ptr]
    253                 \\local.get %[expected]
    254                 \\local.get %[timeout]
    255                 \\memory.atomic.wait32 0
    256                 \\local.set %[ret]
    257                 : [ret] "=r" (-> u32),
    258                 : [ptr] "r" (ptr),
    259                   [expected] "r" (signed_expect),
    260                   [timeout] "r" (to),
    261             );
    262             switch (result) {
    263                 0 => {}, // ok
    264                 1 => {}, // expected != loaded
    265                 2 => {}, // timeout
    266                 else => assert(!is_debug),
    267             }
    268         } else switch (native_os) {
    269             .linux => {
    270                 const linux = std.os.linux;
    271                 var ts_buffer: linux.timespec = undefined;
    272                 const ts: ?*linux.timespec = if (timeout_ns) |ns| ts: {
    273                     ts_buffer = timestampToPosix(ns);
    274                     break :ts &ts_buffer;
    275                 } else null;
    276                 if (thread) |t| try t.beginSyscall();
    277                 const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, ts);
    278                 if (thread) |t| t.endSyscall();
    279                 switch (linux.errno(rc)) {
    280                     .SUCCESS => {}, // notified by `wake()`
    281                     .INTR => {}, // caller's responsibility to retry
    282                     .AGAIN => {}, // ptr.* != expect
    283                     .INVAL => {}, // possibly timeout overflow
    284                     .TIMEDOUT => {}, // timeout
    285                     .FAULT => recoverableOsBugDetected(), // ptr was invalid
    286                     else => recoverableOsBugDetected(),
    287                 }
    288             },
    289             .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
    290                 const c = std.c;
    291                 const flags: c.UL = .{
    292                     .op = .COMPARE_AND_WAIT,
    293                     .NO_ERRNO = true,
    294                 };
    295                 if (thread) |t| try t.beginSyscall();
    296                 const status = switch (darwin_supports_ulock_wait2) {
    297                     true => c.__ulock_wait2(flags, ptr, expect, ns: {
    298                         const ns = timeout_ns orelse break :ns 0;
    299                         if (ns == 0) break :ns 1;
    300                         break :ns ns;
    301                     }, 0),
    302                     false => c.__ulock_wait(flags, ptr, expect, us: {
    303                         const ns = timeout_ns orelse break :us 0;
    304                         const us = std.math.lossyCast(u32, ns / std.time.ns_per_us);
    305                         if (us == 0) break :us 1;
    306                         break :us us;
    307                     }),
    308                 };
    309                 if (thread) |t| t.endSyscall();
    310                 if (status >= 0) return;
    311                 switch (@as(c.E, @enumFromInt(-status))) {
    312                     .INTR => {}, // spurious wake
    313                     // Address of the futex was paged out. This is unlikely, but possible in theory, and
    314                     // pthread/libdispatch on darwin bother to handle it. In this case we'll return
    315                     // without waiting, but the caller should retry anyway.
    316                     .FAULT => {},
    317                     .TIMEDOUT => {}, // timeout
    318                     else => recoverableOsBugDetected(),
    319                 }
    320             },
    321             .windows => {
    322                 var timeout_value: windows.LARGE_INTEGER = undefined;
    323                 var timeout_ptr: ?*const windows.LARGE_INTEGER = null;
    324                 // NTDLL functions work with time in units of 100 nanoseconds.
    325                 // Positive values are absolute deadlines while negative values are relative durations.
    326                 if (timeout_ns) |delay| {
    327                     timeout_value = @as(windows.LARGE_INTEGER, @intCast(delay / 100));
    328                     timeout_value = -timeout_value;
    329                     timeout_ptr = &timeout_value;
    330                 }
    331                 if (thread) |t| try t.checkCancel();
    332                 switch (windows.ntdll.RtlWaitOnAddress(ptr, &expect, @sizeOf(@TypeOf(expect)), timeout_ptr)) {
    333                     .SUCCESS => {},
    334                     .CANCELLED => {},
    335                     .TIMEOUT => {}, // timeout
    336                     else => recoverableOsBugDetected(),
    337                 }
    338             },
    339             .freebsd => {
    340                 const flags = @intFromEnum(std.c.UMTX_OP.WAIT_UINT_PRIVATE);
    341                 var tm_size: usize = 0;
    342                 var tm: std.c._umtx_time = undefined;
    343                 var tm_ptr: ?*const std.c._umtx_time = null;
    344                 if (timeout_ns) |ns| {
    345                     tm_ptr = &tm;
    346                     tm_size = @sizeOf(@TypeOf(tm));
    347                     tm.flags = 0; // use relative time not UMTX_ABSTIME
    348                     tm.clockid = .MONOTONIC;
    349                     tm.timeout = timestampToPosix(ns);
    350                 }
    351                 if (thread) |t| try t.beginSyscall();
    352                 const rc = std.c._umtx_op(@intFromPtr(ptr), flags, @as(c_ulong, expect), tm_size, @intFromPtr(tm_ptr));
    353                 if (thread) |t| t.endSyscall();
    354                 if (is_debug) switch (posix.errno(rc)) {
    355                     .SUCCESS => {},
    356                     .FAULT => unreachable, // one of the args points to invalid memory
    357                     .INVAL => unreachable, // arguments should be correct
    358                     .TIMEDOUT => {}, // timeout
    359                     .INTR => {}, // spurious wake
    360                     else => unreachable,
    361                 };
    362             },
    363             else => @compileError("unimplemented: futexWait"),
    364         }
    365     }
    366 
    367     fn futexWake(ptr: *const u32, max_waiters: u32) void {
    368         @branchHint(.cold);
    369 
    370         if (builtin.single_threaded) return; // nothing to wake up
    371 
    372         if (builtin.cpu.arch.isWasm()) {
    373             comptime assert(builtin.cpu.has(.wasm, .atomics));
    374             assert(max_waiters != 0);
    375             const woken_count = asm volatile (
    376                 \\local.get %[ptr]
    377                 \\local.get %[waiters]
    378                 \\memory.atomic.notify 0
    379                 \\local.set %[ret]
    380                 : [ret] "=r" (-> u32),
    381                 : [ptr] "r" (ptr),
    382                   [waiters] "r" (max_waiters),
    383             );
    384             _ = woken_count; // can be 0 when linker flag 'shared-memory' is not enabled
    385         } else switch (native_os) {
    386             .linux => {
    387                 const linux = std.os.linux;
    388                 switch (linux.errno(linux.futex_3arg(
    389                     ptr,
    390                     .{ .cmd = .WAKE, .private = true },
    391                     @min(max_waiters, std.math.maxInt(i32)),
    392                 ))) {
    393                     .SUCCESS => return, // successful wake up
    394                     .INVAL => return, // invalid futex_wait() on ptr done elsewhere
    395                     .FAULT => return, // pointer became invalid while doing the wake
    396                     else => return recoverableOsBugDetected(), // deadlock due to operating system bug
    397                 }
    398             },
    399             .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
    400                 const c = std.c;
    401                 const flags: c.UL = .{
    402                     .op = .COMPARE_AND_WAIT,
    403                     .NO_ERRNO = true,
    404                     .WAKE_ALL = max_waiters > 1,
    405                 };
    406                 while (true) {
    407                     const status = c.__ulock_wake(flags, ptr, 0);
    408                     if (status >= 0) return;
    409                     switch (@as(c.E, @enumFromInt(-status))) {
    410                         .INTR, .CANCELED => continue, // spurious wake()
    411                         .FAULT => unreachable, // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t
    412                         .NOENT => return, // nothing was woken up
    413                         .ALREADY => unreachable, // only for UL.Op.WAKE_THREAD
    414                         else => unreachable, // deadlock due to operating system bug
    415                     }
    416                 }
    417             },
    418             .windows => {
    419                 assert(max_waiters != 0);
    420                 switch (max_waiters) {
    421                     1 => windows.ntdll.RtlWakeAddressSingle(ptr),
    422                     else => windows.ntdll.RtlWakeAddressAll(ptr),
    423                 }
    424             },
    425             .freebsd => {
    426                 const rc = std.c._umtx_op(
    427                     @intFromPtr(ptr),
    428                     @intFromEnum(std.c.UMTX_OP.WAKE_PRIVATE),
    429                     @as(c_ulong, max_waiters),
    430                     0, // there is no timeout struct
    431                     0, // there is no timeout struct pointer
    432                 );
    433                 switch (posix.errno(rc)) {
    434                     .SUCCESS => {},
    435                     .FAULT => {}, // it's ok if the ptr doesn't point to valid memory
    436                     .INVAL => unreachable, // arguments should be correct
    437                     else => unreachable, // deadlock due to operating system bug
    438                 }
    439             },
    440             else => @compileError("unimplemented: futexWake"),
    441         }
    442     }
    443 };
    444 
    445 const max_iovecs_len = 8;
    446 const splat_buffer_size = 64;
    447 
    448 comptime {
    449     if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX);
    450 }
    451 
    452 const CancelStatus = enum(usize) {
    453     /// Cancellation has neither been requested, nor checked. The async
    454     /// operation will check status before entering a blocking syscall.
    455     /// This is also the status used for uninteruptible tasks.
    456     none = 0,
    457     /// Cancellation has been requested and the status will be checked before
    458     /// entering a blocking syscall.
    459     requested = std.math.maxInt(usize) - 1,
    460     /// Cancellation has been acknowledged and is in progress. Signals should
    461     /// not be sent.
    462     acknowledged = std.math.maxInt(usize),
    463     /// Stores a `Thread.SignaleeId` and indicates that sending a signal to this thread
    464     /// is needed in order to cancel. This state is set before going into
    465     /// a blocking operation that needs to get unblocked via signal.
    466     _,
    467 
    468     const Unpacked = union(enum) {
    469         none,
    470         requested,
    471         acknowledged,
    472         signal_id: Thread.SignaleeId,
    473     };
    474 
    475     fn unpack(cs: CancelStatus) Unpacked {
    476         return switch (cs) {
    477             .none => .none,
    478             .requested => .requested,
    479             .acknowledged => .acknowledged,
    480             _ => |signal_id| .{
    481                 .signal_id = if (std.Thread.use_pthreads)
    482                     @ptrFromInt(@intFromEnum(signal_id))
    483                 else
    484                     @truncate(@intFromEnum(signal_id)),
    485             },
    486         };
    487     }
    488 
    489     fn fromSignaleeId(signal_id: Thread.SignaleeId) CancelStatus {
    490         return if (std.Thread.use_pthreads)
    491             @enumFromInt(@intFromPtr(signal_id))
    492         else
    493             @enumFromInt(signal_id);
    494     }
    495 };
    496 
    497 const Closure = struct {
    498     start: Start,
    499     node: std.SinglyLinkedList.Node = .{},
    500     cancel_status: CancelStatus,
    501 
    502     const Start = *const fn (*Closure, *Threaded) void;
    503 
    504     fn requestCancel(closure: *Closure, t: *Threaded) void {
    505         var signal_id = switch (@atomicRmw(CancelStatus, &closure.cancel_status, .Xchg, .requested, .monotonic).unpack()) {
    506             .none, .acknowledged, .requested => return,
    507             .signal_id => |signal_id| signal_id,
    508         };
    509         // The task will enter a blocking syscall before checking for cancellation again.
    510         // We can send a signal to interrupt the syscall, but if it arrives before
    511         // the syscall instruction, it will be missed. Therefore, this code tries
    512         // again until the cancellation request is acknowledged.
    513 
    514         // 1 << 10 ns is about 1 microsecond, approximately syscall overhead.
    515         // 1 << 20 ns is about 1 millisecond.
    516         // 1 << 30 ns is about 1 second.
    517         //
    518         // On a heavily loaded Linux 6.17.5, I observed a maximum of 20
    519         // attempts not acknowledged before the timeout (including exponential
    520         // backoff) was sufficient, despite the heavy load.
    521         const max_attempts = 22;
    522 
    523         for (0..max_attempts) |attempt_index| {
    524             if (std.Thread.use_pthreads) {
    525                 if (std.c.pthread_kill(signal_id, .IO) != 0) return;
    526             } else if (native_os == .linux) {
    527                 const pid: posix.pid_t = p: {
    528                     const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic);
    529                     if (cached_pid != .unknown) break :p @intFromEnum(cached_pid);
    530                     const pid = std.os.linux.getpid();
    531                     @atomicStore(Pid, &t.pid, @enumFromInt(pid), .monotonic);
    532                     break :p pid;
    533                 };
    534                 if (std.os.linux.tgkill(pid, @bitCast(signal_id), .IO) != 0) return;
    535             } else {
    536                 return;
    537             }
    538 
    539             if (t.robust_cancel != .enabled) return;
    540 
    541             var timespec: posix.timespec = .{
    542                 .sec = 0,
    543                 .nsec = @as(isize, 1) << @intCast(attempt_index),
    544             };
    545             if (native_os == .linux) {
    546                 _ = std.os.linux.clock_nanosleep(posix.CLOCK.MONOTONIC, .{ .ABSTIME = false }, &timespec, &timespec);
    547             } else {
    548                 _ = posix.system.nanosleep(&timespec, &timespec);
    549             }
    550 
    551             switch (@atomicRmw(CancelStatus, &closure.cancel_status, .Xchg, .requested, .monotonic).unpack()) {
    552                 .requested => continue, // Retry needed in case other thread hasn't yet entered the syscall.
    553                 .none, .acknowledged => return,
    554                 .signal_id => |new_signal_id| signal_id = new_signal_id,
    555             }
    556         }
    557     }
    558 };
    559 
    560 /// Related:
    561 /// * `init_single_threaded`
    562 pub fn init(
    563     /// Must be threadsafe. Only used for the following functions:
    564     /// * `Io.VTable.async`
    565     /// * `Io.VTable.concurrent`
    566     /// * `Io.VTable.groupAsync`
    567     /// * `Io.VTable.groupConcurrent`
    568     /// If these functions are avoided, then `Allocator.failing` may be passed
    569     /// here.
    570     gpa: Allocator,
    571 ) Threaded {
    572     if (builtin.single_threaded) return .init_single_threaded;
    573 
    574     const cpu_count = std.Thread.getCpuCount();
    575 
    576     var t: Threaded = .{
    577         .allocator = gpa,
    578         .stack_size = std.Thread.SpawnConfig.default_stack_size,
    579         .async_limit = if (cpu_count) |n| .limited(n - 1) else |_| .nothing,
    580         .cpu_count_error = if (cpu_count) |_| null else |e| e,
    581         .old_sig_io = undefined,
    582         .old_sig_pipe = undefined,
    583         .have_signal_handler = false,
    584         .main_thread = .{
    585             .signal_id = Thread.currentSignalId(),
    586             .current_closure = null,
    587             .cancel_protection = undefined,
    588         },
    589     };
    590 
    591     if (posix.Sigaction != void) {
    592         // This causes sending `posix.SIG.IO` to thread to interrupt blocking
    593         // syscalls, returning `posix.E.INTR`.
    594         const act: posix.Sigaction = .{
    595             .handler = .{ .handler = doNothingSignalHandler },
    596             .mask = posix.sigemptyset(),
    597             .flags = 0,
    598         };
    599         if (have_sig_io) posix.sigaction(.IO, &act, &t.old_sig_io);
    600         if (have_sig_pipe) posix.sigaction(.PIPE, &act, &t.old_sig_pipe);
    601         t.have_signal_handler = true;
    602     }
    603 
    604     return t;
    605 }
    606 
    607 /// Statically initialize such that calls to `Io.VTable.concurrent` will fail
    608 /// with `error.ConcurrencyUnavailable`.
    609 ///
    610 /// When initialized this way:
    611 /// * cancel requests have no effect.
    612 /// * `deinit` is safe, but unnecessary to call.
    613 pub const init_single_threaded: Threaded = .{
    614     .allocator = .failing,
    615     .stack_size = std.Thread.SpawnConfig.default_stack_size,
    616     .async_limit = .nothing,
    617     .cpu_count_error = null,
    618     .concurrent_limit = .nothing,
    619     .old_sig_io = undefined,
    620     .old_sig_pipe = undefined,
    621     .have_signal_handler = false,
    622     .main_thread = .{
    623         .signal_id = undefined,
    624         .current_closure = null,
    625         .cancel_protection = undefined,
    626     },
    627 };
    628 
    629 pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void {
    630     t.mutex.lock();
    631     defer t.mutex.unlock();
    632     t.async_limit = new_limit;
    633 }
    634 
    635 pub fn deinit(t: *Threaded) void {
    636     t.join();
    637     if (is_windows and t.wsa.status == .initialized) {
    638         if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected();
    639     }
    640     if (posix.Sigaction != void and t.have_signal_handler) {
    641         if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null);
    642         if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null);
    643     }
    644     t.* = undefined;
    645 }
    646 
    647 fn join(t: *Threaded) void {
    648     if (builtin.single_threaded) return;
    649     {
    650         t.mutex.lock();
    651         defer t.mutex.unlock();
    652         t.join_requested = true;
    653     }
    654     t.cond.broadcast();
    655     t.wait_group.wait();
    656 }
    657 
    658 fn worker(t: *Threaded) void {
    659     var thread: Thread = .{
    660         .signal_id = Thread.currentSignalId(),
    661         .current_closure = null,
    662         .cancel_protection = undefined,
    663     };
    664     Thread.current = &thread;
    665 
    666     defer t.wait_group.finish();
    667 
    668     t.mutex.lock();
    669     defer t.mutex.unlock();
    670 
    671     while (true) {
    672         while (t.run_queue.popFirst()) |closure_node| {
    673             t.mutex.unlock();
    674             const closure: *Closure = @fieldParentPtr("node", closure_node);
    675             closure.start(closure, t);
    676             t.mutex.lock();
    677             t.busy_count -= 1;
    678         }
    679         if (t.join_requested) break;
    680         t.cond.wait(&t.mutex);
    681     }
    682 }
    683 
    684 pub fn io(t: *Threaded) Io {
    685     return .{
    686         .userdata = t,
    687         .vtable = &.{
    688             .async = async,
    689             .concurrent = concurrent,
    690             .await = await,
    691             .cancel = cancel,
    692             .select = select,
    693 
    694             .groupAsync = groupAsync,
    695             .groupConcurrent = groupConcurrent,
    696             .groupWait = groupWait,
    697             .groupCancel = groupCancel,
    698 
    699             .recancel = recancel,
    700             .swapCancelProtection = swapCancelProtection,
    701             .checkCancel = checkCancel,
    702 
    703             .futexWait = futexWait,
    704             .futexWaitUncancelable = futexWaitUncancelable,
    705             .futexWake = futexWake,
    706 
    707             .dirMake = dirMake,
    708             .dirMakePath = dirMakePath,
    709             .dirMakeOpenPath = dirMakeOpenPath,
    710             .dirStat = dirStat,
    711             .dirStatFile = dirStatFile,
    712             .dirAccess = dirAccess,
    713             .dirCreateFile = dirCreateFile,
    714             .dirOpenFile = dirOpenFile,
    715             .dirOpenDir = dirOpenDir,
    716             .dirClose = dirClose,
    717             .dirRead = dirRead,
    718             .dirRealPath = dirRealPath,
    719             .dirDeleteFile = dirDeleteFile,
    720             .dirDeleteDir = dirDeleteDir,
    721             .dirRename = dirRename,
    722             .dirSymLink = dirSymLink,
    723             .dirReadLink = dirReadLink,
    724             .dirSetOwner = dirSetOwner,
    725             .dirSetFileOwner = dirSetFileOwner,
    726             .dirSetPermissions = dirSetPermissions,
    727             .dirSetFilePermissions = dirSetFilePermissions,
    728             .dirSetTimestamps = dirSetTimestamps,
    729             .dirSetTimestampsNow = dirSetTimestampsNow,
    730 
    731             .fileStat = fileStat,
    732             .fileLength = fileLength,
    733             .fileClose = fileClose,
    734             .fileWriteStreaming = fileWriteStreaming,
    735             .fileWritePositional = fileWritePositional,
    736             .fileWriteFileStreaming = fileWriteFileStreaming,
    737             .fileWriteFilePositional = fileWriteFilePositional,
    738             .fileReadStreaming = fileReadStreaming,
    739             .fileReadPositional = fileReadPositional,
    740             .fileSeekBy = fileSeekBy,
    741             .fileSeekTo = fileSeekTo,
    742             .fileSync = fileSync,
    743             .fileIsTty = fileIsTty,
    744             .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes,
    745             .fileSupportsAnsiEscapeCodes = fileSupportsAnsiEscapeCodes,
    746             .fileSetLength = fileSetLength,
    747             .fileSetOwner = fileSetOwner,
    748             .fileSetPermissions = fileSetPermissions,
    749             .fileSetTimestamps = fileSetTimestamps,
    750             .fileSetTimestampsNow = fileSetTimestampsNow,
    751             .fileLock = fileLock,
    752             .fileTryLock = fileTryLock,
    753             .fileUnlock = fileUnlock,
    754             .fileDowngradeLock = fileDowngradeLock,
    755 
    756             .processExecutableOpen = processExecutableOpen,
    757             .processExecutablePath = processExecutablePath,
    758             .lockStderrWriter = lockStderrWriter,
    759             .tryLockStderrWriter = tryLockStderrWriter,
    760             .unlockStderrWriter = unlockStderrWriter,
    761 
    762             .now = now,
    763             .sleep = sleep,
    764 
    765             .netListenIp = switch (native_os) {
    766                 .windows => netListenIpWindows,
    767                 else => netListenIpPosix,
    768             },
    769             .netListenUnix = switch (native_os) {
    770                 .windows => netListenUnixWindows,
    771                 else => netListenUnixPosix,
    772             },
    773             .netAccept = switch (native_os) {
    774                 .windows => netAcceptWindows,
    775                 else => netAcceptPosix,
    776             },
    777             .netBindIp = switch (native_os) {
    778                 .windows => netBindIpWindows,
    779                 else => netBindIpPosix,
    780             },
    781             .netConnectIp = switch (native_os) {
    782                 .windows => netConnectIpWindows,
    783                 else => netConnectIpPosix,
    784             },
    785             .netConnectUnix = switch (native_os) {
    786                 .windows => netConnectUnixWindows,
    787                 else => netConnectUnixPosix,
    788             },
    789             .netClose = netClose,
    790             .netRead = switch (native_os) {
    791                 .windows => netReadWindows,
    792                 else => netReadPosix,
    793             },
    794             .netWrite = switch (native_os) {
    795                 .windows => netWriteWindows,
    796                 else => netWritePosix,
    797             },
    798             .netWriteFile = netWriteFile,
    799             .netSend = switch (native_os) {
    800                 .windows => netSendWindows,
    801                 else => netSendPosix,
    802             },
    803             .netReceive = switch (native_os) {
    804                 .windows => netReceiveWindows,
    805                 else => netReceivePosix,
    806             },
    807             .netInterfaceNameResolve = netInterfaceNameResolve,
    808             .netInterfaceName = netInterfaceName,
    809             .netLookup = netLookup,
    810         },
    811     };
    812 }
    813 
    814 /// Same as `io` but disables all networking functionality, which has
    815 /// an additional dependency on Windows (ws2_32).
    816 pub fn ioBasic(t: *Threaded) Io {
    817     return .{
    818         .userdata = t,
    819         .vtable = &.{
    820             .async = async,
    821             .concurrent = concurrent,
    822             .await = await,
    823             .cancel = cancel,
    824             .select = select,
    825 
    826             .groupAsync = groupAsync,
    827             .groupConcurrent = groupConcurrent,
    828             .groupWait = groupWait,
    829             .groupCancel = groupCancel,
    830 
    831             .recancel = recancel,
    832             .swapCancelProtection = swapCancelProtection,
    833             .checkCancel = checkCancel,
    834 
    835             .futexWait = futexWait,
    836             .futexWaitUncancelable = futexWaitUncancelable,
    837             .futexWake = futexWake,
    838 
    839             .dirMake = dirMake,
    840             .dirMakePath = dirMakePath,
    841             .dirMakeOpenPath = dirMakeOpenPath,
    842             .dirStat = dirStat,
    843             .dirStatFile = dirStatFile,
    844             .dirAccess = dirAccess,
    845             .dirCreateFile = dirCreateFile,
    846             .dirOpenFile = dirOpenFile,
    847             .dirOpenDir = dirOpenDir,
    848             .dirClose = dirClose,
    849             .dirRead = dirRead,
    850             .dirRealPath = dirRealPath,
    851             .dirDeleteFile = dirDeleteFile,
    852             .dirDeleteDir = dirDeleteDir,
    853             .dirRename = dirRename,
    854             .dirSymLink = dirSymLink,
    855             .dirReadLink = dirReadLink,
    856             .dirSetOwner = dirSetOwner,
    857             .dirSetFileOwner = dirSetFileOwner,
    858             .dirSetPermissions = dirSetPermissions,
    859             .dirSetFilePermissions = dirSetFilePermissions,
    860             .dirSetTimestamps = dirSetTimestamps,
    861             .dirSetTimestampsNow = dirSetTimestampsNow,
    862 
    863             .fileStat = fileStat,
    864             .fileLength = fileLength,
    865             .fileClose = fileClose,
    866             .fileWriteStreaming = fileWriteStreaming,
    867             .fileWritePositional = fileWritePositional,
    868             .fileWriteFileStreaming = fileWriteFileStreaming,
    869             .fileWriteFilePositional = fileWriteFilePositional,
    870             .fileReadStreaming = fileReadStreaming,
    871             .fileReadPositional = fileReadPositional,
    872             .fileSeekBy = fileSeekBy,
    873             .fileSeekTo = fileSeekTo,
    874             .fileSync = fileSync,
    875             .fileIsTty = fileIsTty,
    876             .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes,
    877             .fileSupportsAnsiEscapeCodes = fileSupportsAnsiEscapeCodes,
    878             .fileSetLength = fileSetLength,
    879             .fileSetOwner = fileSetOwner,
    880             .fileSetPermissions = fileSetPermissions,
    881             .fileSetTimestamps = fileSetTimestamps,
    882             .fileSetTimestampsNow = fileSetTimestampsNow,
    883             .fileLock = fileLock,
    884             .fileTryLock = fileTryLock,
    885             .fileUnlock = fileUnlock,
    886             .fileDowngradeLock = fileDowngradeLock,
    887 
    888             .processExecutableOpen = processExecutableOpen,
    889             .processExecutablePath = processExecutablePath,
    890             .lockStderrWriter = lockStderrWriter,
    891             .tryLockStderrWriter = tryLockStderrWriter,
    892             .unlockStderrWriter = unlockStderrWriter,
    893 
    894             .now = now,
    895             .sleep = sleep,
    896 
    897             .netListenIp = netListenIpUnavailable,
    898             .netListenUnix = netListenUnixUnavailable,
    899             .netAccept = netAcceptUnavailable,
    900             .netBindIp = netBindIpUnavailable,
    901             .netConnectIp = netConnectIpUnavailable,
    902             .netConnectUnix = netConnectUnixUnavailable,
    903             .netClose = netCloseUnavailable,
    904             .netRead = netReadUnavailable,
    905             .netWrite = netWriteUnavailable,
    906             .netWriteFile = netWriteFileUnavailable,
    907             .netSend = netSendUnavailable,
    908             .netReceive = netReceiveUnavailable,
    909             .netInterfaceNameResolve = netInterfaceNameResolveUnavailable,
    910             .netInterfaceName = netInterfaceNameUnavailable,
    911             .netLookup = netLookupUnavailable,
    912         },
    913     };
    914 }
    915 
    916 pub const socket_flags_unsupported = is_darwin or native_os == .haiku;
    917 const have_accept4 = !socket_flags_unsupported;
    918 const have_flock_open_flags = @hasField(posix.O, "EXLOCK");
    919 const have_networking = native_os != .wasi;
    920 const have_flock = @TypeOf(posix.system.flock) != void;
    921 const have_sendmmsg = native_os == .linux;
    922 const have_futex = switch (builtin.cpu.arch) {
    923     .wasm32, .wasm64 => builtin.cpu.has(.wasm, .atomics),
    924     else => true,
    925 };
    926 const have_preadv = switch (native_os) {
    927     .windows, .haiku => false,
    928     else => true,
    929 };
    930 const have_sig_io = posix.SIG != void and @hasField(posix.SIG, "IO");
    931 const have_sig_pipe = posix.SIG != void and @hasField(posix.SIG, "PIPE");
    932 const have_sendfile = if (builtin.link_libc) @TypeOf(std.c.sendfile) != void else native_os == .linux;
    933 const have_copy_file_range = switch (native_os) {
    934     .linux, .freebsd => true,
    935     else => false,
    936 };
    937 const have_fcopyfile = is_darwin;
    938 const have_fchmodat2 = native_os == .linux and
    939     (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse true) and
    940     (builtin.abi.isAndroid() or !std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 }));
    941 const have_fchmodat_flags = native_os != .linux or
    942     (!builtin.abi.isAndroid() and std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 }));
    943 
    944 const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat;
    945 const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat;
    946 const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat;
    947 const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek;
    948 const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;
    949 const ftruncate_sym = if (posix.lfs64_abi) posix.system.ftruncate64 else posix.system.ftruncate;
    950 const pwritev_sym = if (posix.lfs64_abi) posix.system.pwritev64 else posix.system.pwritev;
    951 const sendfile_sym = if (posix.lfs64_abi) posix.system.sendfile64 else posix.system.sendfile;
    952 const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{
    953     .major = 34,
    954     .minor = 0,
    955     .patch = 0,
    956 } else .{
    957     .major = 2,
    958     .minor = 27,
    959     .patch = 0,
    960 });
    961 const linux_copy_file_range_sys = if (linux_copy_file_range_use_c) std.c else std.os.linux;
    962 
    963 /// Trailing data:
    964 /// 1. context
    965 /// 2. result
    966 const AsyncClosure = struct {
    967     closure: Closure,
    968     func: *const fn (context: *anyopaque, result: *anyopaque) void,
    969     event: Io.Event,
    970     select_condition: ?*Io.Event,
    971     context_alignment: Alignment,
    972     result_offset: usize,
    973     alloc_len: usize,
    974 
    975     const done_event: *Io.Event = @ptrFromInt(@alignOf(Io.Event));
    976 
    977     fn start(closure: *Closure, t: *Threaded) void {
    978         const ac: *AsyncClosure = @alignCast(@fieldParentPtr("closure", closure));
    979         const current_thread = Thread.getCurrent(t);
    980 
    981         current_thread.current_closure = closure;
    982         current_thread.cancel_protection = .unblocked;
    983 
    984         ac.func(ac.contextPointer(), ac.resultPointer());
    985 
    986         current_thread.current_closure = null;
    987         current_thread.cancel_protection = undefined;
    988 
    989         if (@atomicRmw(?*Io.Event, &ac.select_condition, .Xchg, done_event, .release)) |select_event| {
    990             assert(select_event != done_event);
    991             select_event.set(ioBasic(t));
    992         }
    993         ac.event.set(ioBasic(t));
    994     }
    995 
    996     fn resultPointer(ac: *AsyncClosure) [*]u8 {
    997         const base: [*]u8 = @ptrCast(ac);
    998         return base + ac.result_offset;
    999     }
   1000 
   1001     fn contextPointer(ac: *AsyncClosure) [*]u8 {
   1002         const base: [*]u8 = @ptrCast(ac);
   1003         const context_offset = ac.context_alignment.forward(@intFromPtr(ac) + @sizeOf(AsyncClosure)) - @intFromPtr(ac);
   1004         return base + context_offset;
   1005     }
   1006 
   1007     fn init(
   1008         gpa: Allocator,
   1009         result_len: usize,
   1010         result_alignment: Alignment,
   1011         context: []const u8,
   1012         context_alignment: Alignment,
   1013         func: *const fn (context: *const anyopaque, result: *anyopaque) void,
   1014     ) Allocator.Error!*AsyncClosure {
   1015         const max_context_misalignment = context_alignment.toByteUnits() -| @alignOf(AsyncClosure);
   1016         const worst_case_context_offset = context_alignment.forward(@sizeOf(AsyncClosure) + max_context_misalignment);
   1017         const worst_case_result_offset = result_alignment.forward(worst_case_context_offset + context.len);
   1018         const alloc_len = worst_case_result_offset + result_len;
   1019 
   1020         const ac: *AsyncClosure = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(AsyncClosure), alloc_len)));
   1021         errdefer comptime unreachable;
   1022 
   1023         const actual_context_addr = context_alignment.forward(@intFromPtr(ac) + @sizeOf(AsyncClosure));
   1024         const actual_result_addr = result_alignment.forward(actual_context_addr + context.len);
   1025         const actual_result_offset = actual_result_addr - @intFromPtr(ac);
   1026         ac.* = .{
   1027             .closure = .{
   1028                 .cancel_status = .none,
   1029                 .start = start,
   1030             },
   1031             .func = func,
   1032             .context_alignment = context_alignment,
   1033             .result_offset = actual_result_offset,
   1034             .alloc_len = alloc_len,
   1035             .event = .unset,
   1036             .select_condition = null,
   1037         };
   1038         @memcpy(ac.contextPointer()[0..context.len], context);
   1039         return ac;
   1040     }
   1041 
   1042     fn waitAndDeinit(ac: *AsyncClosure, t: *Threaded, result: []u8) void {
   1043         ac.event.wait(ioBasic(t)) catch |err| switch (err) {
   1044             error.Canceled => {
   1045                 ac.closure.requestCancel(t);
   1046                 ac.event.waitUncancelable(ioBasic(t));
   1047             },
   1048         };
   1049         @memcpy(result, ac.resultPointer()[0..result.len]);
   1050         ac.deinit(t.allocator);
   1051     }
   1052 
   1053     fn deinit(ac: *AsyncClosure, gpa: Allocator) void {
   1054         const base: [*]align(@alignOf(AsyncClosure)) u8 = @ptrCast(ac);
   1055         gpa.free(base[0..ac.alloc_len]);
   1056     }
   1057 };
   1058 
   1059 fn async(
   1060     userdata: ?*anyopaque,
   1061     result: []u8,
   1062     result_alignment: Alignment,
   1063     context: []const u8,
   1064     context_alignment: Alignment,
   1065     start: *const fn (context: *const anyopaque, result: *anyopaque) void,
   1066 ) ?*Io.AnyFuture {
   1067     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1068     if (builtin.single_threaded) {
   1069         start(context.ptr, result.ptr);
   1070         return null;
   1071     }
   1072     const gpa = t.allocator;
   1073     const ac = AsyncClosure.init(gpa, result.len, result_alignment, context, context_alignment, start) catch {
   1074         start(context.ptr, result.ptr);
   1075         return null;
   1076     };
   1077 
   1078     t.mutex.lock();
   1079 
   1080     const busy_count = t.busy_count;
   1081 
   1082     if (busy_count >= @intFromEnum(t.async_limit)) {
   1083         t.mutex.unlock();
   1084         ac.deinit(gpa);
   1085         start(context.ptr, result.ptr);
   1086         return null;
   1087     }
   1088 
   1089     t.busy_count = busy_count + 1;
   1090 
   1091     const pool_size = t.wait_group.value();
   1092     if (pool_size - busy_count == 0) {
   1093         t.wait_group.start();
   1094         const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {
   1095             t.wait_group.finish();
   1096             t.busy_count = busy_count;
   1097             t.mutex.unlock();
   1098             ac.deinit(gpa);
   1099             start(context.ptr, result.ptr);
   1100             return null;
   1101         };
   1102         thread.detach();
   1103     }
   1104 
   1105     t.run_queue.prepend(&ac.closure.node);
   1106     t.mutex.unlock();
   1107     t.cond.signal();
   1108     return @ptrCast(ac);
   1109 }
   1110 
   1111 fn concurrent(
   1112     userdata: ?*anyopaque,
   1113     result_len: usize,
   1114     result_alignment: Alignment,
   1115     context: []const u8,
   1116     context_alignment: Alignment,
   1117     start: *const fn (context: *const anyopaque, result: *anyopaque) void,
   1118 ) Io.ConcurrentError!*Io.AnyFuture {
   1119     if (builtin.single_threaded) return error.ConcurrencyUnavailable;
   1120 
   1121     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1122 
   1123     const gpa = t.allocator;
   1124     const ac = AsyncClosure.init(gpa, result_len, result_alignment, context, context_alignment, start) catch
   1125         return error.ConcurrencyUnavailable;
   1126     errdefer ac.deinit(gpa);
   1127 
   1128     t.mutex.lock();
   1129     defer t.mutex.unlock();
   1130 
   1131     const busy_count = t.busy_count;
   1132 
   1133     if (busy_count >= @intFromEnum(t.concurrent_limit))
   1134         return error.ConcurrencyUnavailable;
   1135 
   1136     t.busy_count = busy_count + 1;
   1137     errdefer t.busy_count = busy_count;
   1138 
   1139     const pool_size = t.wait_group.value();
   1140     if (pool_size - busy_count == 0) {
   1141         t.wait_group.start();
   1142         errdefer t.wait_group.finish();
   1143 
   1144         const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch
   1145             return error.ConcurrencyUnavailable;
   1146         thread.detach();
   1147     }
   1148 
   1149     t.run_queue.prepend(&ac.closure.node);
   1150     t.cond.signal();
   1151     return @ptrCast(ac);
   1152 }
   1153 
   1154 const GroupClosure = struct {
   1155     closure: Closure,
   1156     group: *Io.Group,
   1157     /// Points to sibling `GroupClosure`. Used for walking the group to cancel all.
   1158     node: std.SinglyLinkedList.Node,
   1159     func: *const fn (*Io.Group, context: *anyopaque) void,
   1160     context_alignment: Alignment,
   1161     alloc_len: usize,
   1162 
   1163     fn start(closure: *Closure, t: *Threaded) void {
   1164         const gc: *GroupClosure = @alignCast(@fieldParentPtr("closure", closure));
   1165         const current_thread = Thread.getCurrent(t);
   1166         const group = gc.group;
   1167         const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
   1168         const event: *Io.Event = @ptrCast(&group.context);
   1169         current_thread.current_closure = closure;
   1170         current_thread.cancel_protection = .unblocked;
   1171 
   1172         gc.func(group, gc.contextPointer());
   1173 
   1174         current_thread.current_closure = null;
   1175         current_thread.cancel_protection = undefined;
   1176 
   1177         const prev_state = group_state.fetchSub(sync_one_pending, .acq_rel);
   1178         assert((prev_state / sync_one_pending) > 0);
   1179         if (prev_state == (sync_one_pending | sync_is_waiting)) event.set(ioBasic(t));
   1180     }
   1181 
   1182     fn contextPointer(gc: *GroupClosure) [*]u8 {
   1183         const base: [*]u8 = @ptrCast(gc);
   1184         const context_offset = gc.context_alignment.forward(@intFromPtr(gc) + @sizeOf(GroupClosure)) - @intFromPtr(gc);
   1185         return base + context_offset;
   1186     }
   1187 
   1188     /// Does not initialize the `node` field.
   1189     fn init(
   1190         gpa: Allocator,
   1191         group: *Io.Group,
   1192         context: []const u8,
   1193         context_alignment: Alignment,
   1194         func: *const fn (*Io.Group, context: *const anyopaque) void,
   1195     ) Allocator.Error!*GroupClosure {
   1196         const max_context_misalignment = context_alignment.toByteUnits() -| @alignOf(GroupClosure);
   1197         const worst_case_context_offset = context_alignment.forward(@sizeOf(GroupClosure) + max_context_misalignment);
   1198         const alloc_len = worst_case_context_offset + context.len;
   1199 
   1200         const gc: *GroupClosure = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(GroupClosure), alloc_len)));
   1201         errdefer comptime unreachable;
   1202 
   1203         gc.* = .{
   1204             .closure = .{
   1205                 .cancel_status = .none,
   1206                 .start = start,
   1207             },
   1208             .group = group,
   1209             .node = undefined,
   1210             .func = func,
   1211             .context_alignment = context_alignment,
   1212             .alloc_len = alloc_len,
   1213         };
   1214         @memcpy(gc.contextPointer()[0..context.len], context);
   1215         return gc;
   1216     }
   1217 
   1218     fn deinit(gc: *GroupClosure, gpa: Allocator) void {
   1219         const base: [*]align(@alignOf(GroupClosure)) u8 = @ptrCast(gc);
   1220         gpa.free(base[0..gc.alloc_len]);
   1221     }
   1222 
   1223     const sync_is_waiting: usize = 1 << 0;
   1224     const sync_one_pending: usize = 1 << 1;
   1225 };
   1226 
   1227 fn groupAsync(
   1228     userdata: ?*anyopaque,
   1229     group: *Io.Group,
   1230     context: []const u8,
   1231     context_alignment: Alignment,
   1232     start: *const fn (*Io.Group, context: *const anyopaque) void,
   1233 ) void {
   1234     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1235     if (builtin.single_threaded) return start(group, context.ptr);
   1236 
   1237     const gpa = t.allocator;
   1238     const gc = GroupClosure.init(gpa, group, context, context_alignment, start) catch
   1239         return start(group, context.ptr);
   1240 
   1241     t.mutex.lock();
   1242 
   1243     const busy_count = t.busy_count;
   1244 
   1245     if (busy_count >= @intFromEnum(t.async_limit)) {
   1246         t.mutex.unlock();
   1247         gc.deinit(gpa);
   1248         return start(group, context.ptr);
   1249     }
   1250 
   1251     t.busy_count = busy_count + 1;
   1252 
   1253     const pool_size = t.wait_group.value();
   1254     if (pool_size - busy_count == 0) {
   1255         t.wait_group.start();
   1256         const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {
   1257             t.wait_group.finish();
   1258             t.busy_count = busy_count;
   1259             t.mutex.unlock();
   1260             gc.deinit(gpa);
   1261             return start(group, context.ptr);
   1262         };
   1263         thread.detach();
   1264     }
   1265 
   1266     // Append to the group linked list inside the mutex to make `Io.Group.async` thread-safe.
   1267     gc.node = .{ .next = @ptrCast(@alignCast(group.token.load(.monotonic))) };
   1268     group.token.store(&gc.node, .monotonic);
   1269 
   1270     t.run_queue.prepend(&gc.closure.node);
   1271 
   1272     // This needs to be done before unlocking the mutex to avoid a race with
   1273     // the associated task finishing.
   1274     const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
   1275     const prev_state = group_state.fetchAdd(GroupClosure.sync_one_pending, .monotonic);
   1276     assert((prev_state / GroupClosure.sync_one_pending) < (std.math.maxInt(usize) / GroupClosure.sync_one_pending));
   1277 
   1278     t.mutex.unlock();
   1279     t.cond.signal();
   1280 }
   1281 
   1282 fn groupConcurrent(
   1283     userdata: ?*anyopaque,
   1284     group: *Io.Group,
   1285     context: []const u8,
   1286     context_alignment: Alignment,
   1287     start: *const fn (*Io.Group, context: *const anyopaque) void,
   1288 ) Io.ConcurrentError!void {
   1289     if (builtin.single_threaded) return error.ConcurrencyUnavailable;
   1290 
   1291     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1292 
   1293     const gpa = t.allocator;
   1294     const gc = GroupClosure.init(gpa, group, context, context_alignment, start) catch
   1295         return error.ConcurrencyUnavailable;
   1296 
   1297     t.mutex.lock();
   1298     defer t.mutex.unlock();
   1299 
   1300     const busy_count = t.busy_count;
   1301 
   1302     if (busy_count >= @intFromEnum(t.concurrent_limit))
   1303         return error.ConcurrencyUnavailable;
   1304 
   1305     t.busy_count = busy_count + 1;
   1306     errdefer t.busy_count = busy_count;
   1307 
   1308     const pool_size = t.wait_group.value();
   1309     if (pool_size - busy_count == 0) {
   1310         t.wait_group.start();
   1311         errdefer t.wait_group.finish();
   1312 
   1313         const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch
   1314             return error.ConcurrencyUnavailable;
   1315         thread.detach();
   1316     }
   1317 
   1318     // Append to the group linked list inside the mutex to make `Io.Group.concurrent` thread-safe.
   1319     gc.node = .{ .next = @ptrCast(@alignCast(group.token.load(.monotonic))) };
   1320     group.token.store(&gc.node, .monotonic);
   1321 
   1322     t.run_queue.prepend(&gc.closure.node);
   1323 
   1324     // This needs to be done before unlocking the mutex to avoid a race with
   1325     // the associated task finishing.
   1326     const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
   1327     const prev_state = group_state.fetchAdd(GroupClosure.sync_one_pending, .monotonic);
   1328     assert((prev_state / GroupClosure.sync_one_pending) < (std.math.maxInt(usize) / GroupClosure.sync_one_pending));
   1329 
   1330     t.cond.signal();
   1331 }
   1332 
   1333 fn groupWait(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) void {
   1334     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1335     const gpa = t.allocator;
   1336 
   1337     _ = initial_token; // we need to load `token` *after* the group finishes
   1338 
   1339     if (builtin.single_threaded) unreachable; // we never set `group.token` to non-`null`
   1340 
   1341     const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
   1342     const event: *Io.Event = @ptrCast(&group.context);
   1343     const prev_state = group_state.fetchAdd(GroupClosure.sync_is_waiting, .acquire);
   1344     assert(prev_state & GroupClosure.sync_is_waiting == 0);
   1345     if ((prev_state / GroupClosure.sync_one_pending) > 0) event.wait(ioBasic(t)) catch |err| switch (err) {
   1346         error.Canceled => {
   1347             var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.load(.monotonic)));
   1348             while (it) |node| : (it = node.next) {
   1349                 const gc: *GroupClosure = @fieldParentPtr("node", node);
   1350                 gc.closure.requestCancel(t);
   1351             }
   1352             event.waitUncancelable(ioBasic(t));
   1353         },
   1354     };
   1355 
   1356     // Since the group has now finished, it's illegal to add more tasks to it until we return. It's
   1357     // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only
   1358     // thread who can access `group` right now.
   1359     var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.raw));
   1360     group.token.raw = null;
   1361     while (it) |node| {
   1362         it = node.next; // update `it` now, because `deinit` will invalidate `node`
   1363         const gc: *GroupClosure = @fieldParentPtr("node", node);
   1364         gc.deinit(gpa);
   1365     }
   1366 }
   1367 
   1368 fn groupCancel(userdata: ?*anyopaque, group: *Io.Group, initial_token: *anyopaque) void {
   1369     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1370     const gpa = t.allocator;
   1371 
   1372     _ = initial_token; // we need to load `token` *after* the group finishes
   1373 
   1374     if (builtin.single_threaded) unreachable; // we never set `group.token` to non-`null`
   1375 
   1376     {
   1377         var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.load(.monotonic)));
   1378         while (it) |node| : (it = node.next) {
   1379             const gc: *GroupClosure = @fieldParentPtr("node", node);
   1380             gc.closure.requestCancel(t);
   1381         }
   1382     }
   1383 
   1384     const group_state: *std.atomic.Value(usize) = @ptrCast(&group.state);
   1385     const event: *Io.Event = @ptrCast(&group.context);
   1386     const prev_state = group_state.fetchAdd(GroupClosure.sync_is_waiting, .acquire);
   1387     assert(prev_state & GroupClosure.sync_is_waiting == 0);
   1388     if ((prev_state / GroupClosure.sync_one_pending) > 0) event.waitUncancelable(ioBasic(t));
   1389 
   1390     // Since the group has now finished, it's illegal to add more tasks to it until we return. It's
   1391     // also illegal for us to race with another `await` or `cancel`. Therefore, we must be the only
   1392     // thread who can access `group` right now.
   1393     var it: ?*std.SinglyLinkedList.Node = @ptrCast(@alignCast(group.token.raw));
   1394     group.token.raw = null;
   1395     while (it) |node| {
   1396         it = node.next; // update `it` now, because `deinit` will invalidate `node`
   1397         const gc: *GroupClosure = @fieldParentPtr("node", node);
   1398         gc.deinit(gpa);
   1399     }
   1400 }
   1401 
   1402 fn recancel(userdata: ?*anyopaque) void {
   1403     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1404     const current_thread: *Thread = .getCurrent(t);
   1405     const cancel_status = &current_thread.current_closure.?.cancel_status;
   1406     switch (@atomicLoad(CancelStatus, cancel_status, .monotonic)) {
   1407         .none => unreachable, // called `recancel` when not canceled
   1408         .requested => unreachable, // called `recancel` when cancelation was already outstanding
   1409         .acknowledged => {},
   1410         _ => unreachable, // invalid state: not in a syscall
   1411     }
   1412     @atomicStore(CancelStatus, cancel_status, .requested, .monotonic);
   1413 }
   1414 
   1415 fn swapCancelProtection(userdata: ?*anyopaque, new: Io.CancelProtection) Io.CancelProtection {
   1416     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1417     const current_thread: *Thread = .getCurrent(t);
   1418     const old = current_thread.cancel_protection;
   1419     current_thread.cancel_protection = new;
   1420     return old;
   1421 }
   1422 
   1423 fn checkCancel(userdata: ?*anyopaque) Io.Cancelable!void {
   1424     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1425     return Thread.getCurrent(t).checkCancel();
   1426 }
   1427 
   1428 fn await(
   1429     userdata: ?*anyopaque,
   1430     any_future: *Io.AnyFuture,
   1431     result: []u8,
   1432     result_alignment: Alignment,
   1433 ) void {
   1434     _ = result_alignment;
   1435     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1436     const closure: *AsyncClosure = @ptrCast(@alignCast(any_future));
   1437     closure.waitAndDeinit(t, result);
   1438 }
   1439 
   1440 fn cancel(
   1441     userdata: ?*anyopaque,
   1442     any_future: *Io.AnyFuture,
   1443     result: []u8,
   1444     result_alignment: Alignment,
   1445 ) void {
   1446     _ = result_alignment;
   1447     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1448     const ac: *AsyncClosure = @ptrCast(@alignCast(any_future));
   1449     ac.closure.requestCancel(t);
   1450     ac.waitAndDeinit(t, result);
   1451 }
   1452 
   1453 fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Io.Timeout) Io.Cancelable!void {
   1454     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1455     const current_thread = Thread.getCurrent(t);
   1456     const t_io = ioBasic(t);
   1457     const timeout_ns: ?u64 = ns: {
   1458         const d = (timeout.toDurationFromNow(t_io) catch break :ns 10) orelse break :ns null;
   1459         break :ns std.math.lossyCast(u64, d.raw.toNanoseconds());
   1460     };
   1461     switch (native_os) {
   1462         .illumos, .netbsd, .openbsd => @panic("TODO"),
   1463         else => try current_thread.futexWaitTimed(ptr, expected, timeout_ns),
   1464     }
   1465 }
   1466 
   1467 fn futexWaitUncancelable(userdata: ?*anyopaque, ptr: *const u32, expected: u32) void {
   1468     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1469     _ = t;
   1470     switch (native_os) {
   1471         .illumos, .netbsd, .openbsd => @panic("TODO"),
   1472         else => Thread.futexWaitUncancelable(ptr, expected),
   1473     }
   1474 }
   1475 
   1476 fn futexWake(userdata: ?*anyopaque, ptr: *const u32, max_waiters: u32) void {
   1477     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1478     _ = t;
   1479     switch (native_os) {
   1480         .illumos, .netbsd, .openbsd => @panic("TODO"),
   1481         else => Thread.futexWake(ptr, max_waiters),
   1482     }
   1483 }
   1484 
   1485 const dirMake = switch (native_os) {
   1486     .windows => dirMakeWindows,
   1487     .wasi => dirMakeWasi,
   1488     else => dirMakePosix,
   1489 };
   1490 
   1491 fn dirMakePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void {
   1492     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1493     const current_thread = Thread.getCurrent(t);
   1494 
   1495     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   1496     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   1497 
   1498     try current_thread.beginSyscall();
   1499     while (true) {
   1500         switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, permissions.toMode()))) {
   1501             .SUCCESS => {
   1502                 current_thread.endSyscall();
   1503                 return;
   1504             },
   1505             .INTR => {
   1506                 try current_thread.checkCancel();
   1507                 continue;
   1508             },
   1509             .CANCELED => return current_thread.endSyscallCanceled(),
   1510             else => |e| {
   1511                 current_thread.endSyscall();
   1512                 switch (e) {
   1513                     .ACCES => return error.AccessDenied,
   1514                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   1515                     .PERM => return error.PermissionDenied,
   1516                     .DQUOT => return error.DiskQuota,
   1517                     .EXIST => return error.PathAlreadyExists,
   1518                     .FAULT => |err| return errnoBug(err),
   1519                     .LOOP => return error.SymLinkLoop,
   1520                     .MLINK => return error.LinkQuotaExceeded,
   1521                     .NAMETOOLONG => return error.NameTooLong,
   1522                     .NOENT => return error.FileNotFound,
   1523                     .NOMEM => return error.SystemResources,
   1524                     .NOSPC => return error.NoSpaceLeft,
   1525                     .NOTDIR => return error.NotDir,
   1526                     .ROFS => return error.ReadOnlyFileSystem,
   1527                     // dragonfly: when dir_fd is unlinked from filesystem
   1528                     .NOTCONN => return error.FileNotFound,
   1529                     .ILSEQ => return error.BadPathName,
   1530                     else => |err| return posix.unexpectedErrno(err),
   1531                 }
   1532             },
   1533         }
   1534     }
   1535 }
   1536 
   1537 fn dirMakeWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void {
   1538     if (builtin.link_libc) return dirMakePosix(userdata, dir, sub_path, permissions);
   1539     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1540     const current_thread = Thread.getCurrent(t);
   1541     try current_thread.beginSyscall();
   1542     while (true) {
   1543         switch (std.os.wasi.path_create_directory(dir.handle, sub_path.ptr, sub_path.len)) {
   1544             .SUCCESS => {
   1545                 current_thread.endSyscall();
   1546                 return;
   1547             },
   1548             .INTR => {
   1549                 try current_thread.checkCancel();
   1550                 continue;
   1551             },
   1552             .CANCELED => return current_thread.endSyscallCanceled(),
   1553             else => |e| {
   1554                 current_thread.endSyscall();
   1555                 switch (e) {
   1556                     .ACCES => return error.AccessDenied,
   1557                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   1558                     .PERM => return error.PermissionDenied,
   1559                     .DQUOT => return error.DiskQuota,
   1560                     .EXIST => return error.PathAlreadyExists,
   1561                     .FAULT => |err| return errnoBug(err),
   1562                     .LOOP => return error.SymLinkLoop,
   1563                     .MLINK => return error.LinkQuotaExceeded,
   1564                     .NAMETOOLONG => return error.NameTooLong,
   1565                     .NOENT => return error.FileNotFound,
   1566                     .NOMEM => return error.SystemResources,
   1567                     .NOSPC => return error.NoSpaceLeft,
   1568                     .NOTDIR => return error.NotDir,
   1569                     .ROFS => return error.ReadOnlyFileSystem,
   1570                     .NOTCAPABLE => return error.AccessDenied,
   1571                     .ILSEQ => return error.BadPathName,
   1572                     else => |err| return posix.unexpectedErrno(err),
   1573                 }
   1574             },
   1575         }
   1576     }
   1577 }
   1578 
   1579 fn dirMakeWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.MakeError!void {
   1580     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1581     const current_thread = Thread.getCurrent(t);
   1582     try current_thread.checkCancel();
   1583 
   1584     const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
   1585     _ = permissions; // TODO use this value
   1586     const sub_dir_handle = windows.OpenFile(sub_path_w.span(), .{
   1587         .dir = dir.handle,
   1588         .access_mask = .{
   1589             .GENERIC = .{ .READ = true },
   1590             .STANDARD = .{ .SYNCHRONIZE = true },
   1591         },
   1592         .creation = .CREATE,
   1593         .filter = .dir_only,
   1594     }) catch |err| switch (err) {
   1595         error.IsDir => return error.Unexpected,
   1596         error.PipeBusy => return error.Unexpected,
   1597         error.NoDevice => return error.Unexpected,
   1598         error.WouldBlock => return error.Unexpected,
   1599         error.AntivirusInterference => return error.Unexpected,
   1600         else => |e| return e,
   1601     };
   1602     windows.CloseHandle(sub_dir_handle);
   1603 }
   1604 
   1605 fn dirMakePath(
   1606     userdata: ?*anyopaque,
   1607     dir: Dir,
   1608     sub_path: []const u8,
   1609     permissions: Dir.Permissions,
   1610 ) Dir.MakePathError!Dir.MakePathStatus {
   1611     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1612 
   1613     var it = std.fs.path.componentIterator(sub_path);
   1614     var status: Dir.MakePathStatus = .existed;
   1615     var component = it.last() orelse return error.BadPathName;
   1616     while (true) {
   1617         if (dirMake(t, dir, component.path, permissions)) |_| {
   1618             status = .created;
   1619         } else |err| switch (err) {
   1620             error.PathAlreadyExists => {
   1621                 // stat the file and return an error if it's not a directory
   1622                 // this is important because otherwise a dangling symlink
   1623                 // could cause an infinite loop
   1624                 check_dir: {
   1625                     // workaround for windows, see https://github.com/ziglang/zig/issues/16738
   1626                     const fstat = dirStatFile(t, dir, component.path, .{}) catch |stat_err| switch (stat_err) {
   1627                         error.IsDir => break :check_dir,
   1628                         else => |e| return e,
   1629                     };
   1630                     if (fstat.kind != .directory) return error.NotDir;
   1631                 }
   1632             },
   1633             error.FileNotFound => |e| {
   1634                 component = it.previous() orelse return e;
   1635                 continue;
   1636             },
   1637             else => |e| return e,
   1638         }
   1639         component = it.next() orelse return status;
   1640     }
   1641 }
   1642 
   1643 const dirMakeOpenPath = switch (native_os) {
   1644     .windows => dirMakeOpenPathWindows,
   1645     .wasi => dirMakeOpenPathWasi,
   1646     else => dirMakeOpenPathPosix,
   1647 };
   1648 
   1649 fn dirMakeOpenPathPosix(
   1650     userdata: ?*anyopaque,
   1651     dir: Dir,
   1652     sub_path: []const u8,
   1653     permissions: Dir.Permissions,
   1654     options: Dir.OpenOptions,
   1655 ) Dir.MakeOpenPathError!Dir {
   1656     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1657     const t_io = ioBasic(t);
   1658     return dirOpenDirPosix(t, dir, sub_path, options) catch |err| switch (err) {
   1659         error.FileNotFound => {
   1660             _ = try dir.makePathStatus(t_io, sub_path, permissions);
   1661             return dirOpenDirPosix(t, dir, sub_path, options);
   1662         },
   1663         else => |e| return e,
   1664     };
   1665 }
   1666 
   1667 fn dirMakeOpenPathWindows(
   1668     userdata: ?*anyopaque,
   1669     dir: Dir,
   1670     sub_path: []const u8,
   1671     permissions: Dir.Permissions,
   1672     options: Dir.OpenOptions,
   1673 ) Dir.MakeOpenPathError!Dir {
   1674     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1675     const current_thread = Thread.getCurrent(t);
   1676     const w = windows;
   1677 
   1678     _ = permissions; // TODO apply these permissions
   1679 
   1680     var it = std.fs.path.componentIterator(sub_path);
   1681     // If there are no components in the path, then create a dummy component with the full path.
   1682     var component: std.fs.path.NativeComponentIterator.Component = it.last() orelse .{
   1683         .name = "",
   1684         .path = sub_path,
   1685     };
   1686 
   1687     while (true) {
   1688         try current_thread.checkCancel();
   1689 
   1690         const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, component.path);
   1691         const sub_path_w = sub_path_w_array.span();
   1692         const is_last = it.peekNext() == null;
   1693         const create_disposition: w.FILE.CREATE_DISPOSITION = if (is_last) .OPEN_IF else .CREATE;
   1694 
   1695         var result: Dir = .{ .handle = undefined };
   1696 
   1697         const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);
   1698         var nt_name: w.UNICODE_STRING = .{
   1699             .Length = path_len_bytes,
   1700             .MaximumLength = path_len_bytes,
   1701             .Buffer = @constCast(sub_path_w.ptr),
   1702         };
   1703         var io_status_block: w.IO_STATUS_BLOCK = undefined;
   1704         const rc = w.ntdll.NtCreateFile(
   1705             &result.handle,
   1706             .{
   1707                 .SPECIFIC = .{ .FILE_DIRECTORY = .{
   1708                     .LIST = options.iterate,
   1709                     .READ_EA = true,
   1710                     .READ_ATTRIBUTES = true,
   1711                     .TRAVERSE = true,
   1712                 } },
   1713                 .STANDARD = .{
   1714                     .RIGHTS = .READ,
   1715                     .SYNCHRONIZE = true,
   1716                 },
   1717             },
   1718             &.{
   1719                 .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
   1720                 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
   1721                 .Attributes = .{},
   1722                 .ObjectName = &nt_name,
   1723                 .SecurityDescriptor = null,
   1724                 .SecurityQualityOfService = null,
   1725             },
   1726             &io_status_block,
   1727             null,
   1728             .{ .NORMAL = true },
   1729             .VALID_FLAGS,
   1730             create_disposition,
   1731             .{
   1732                 .DIRECTORY_FILE = true,
   1733                 .IO = .SYNCHRONOUS_NONALERT,
   1734                 .OPEN_FOR_BACKUP_INTENT = true,
   1735                 .OPEN_REPARSE_POINT = !options.follow_symlinks,
   1736             },
   1737             null,
   1738             0,
   1739         );
   1740 
   1741         switch (rc) {
   1742             .SUCCESS => {
   1743                 component = it.next() orelse return result;
   1744                 w.CloseHandle(result.handle);
   1745                 continue;
   1746             },
   1747             .OBJECT_NAME_INVALID => return error.BadPathName,
   1748             .OBJECT_NAME_COLLISION => {
   1749                 assert(!is_last);
   1750                 // stat the file and return an error if it's not a directory
   1751                 // this is important because otherwise a dangling symlink
   1752                 // could cause an infinite loop
   1753                 check_dir: {
   1754                     // workaround for windows, see https://github.com/ziglang/zig/issues/16738
   1755                     const fstat = dirStatFileWindows(t, dir, component.path, .{
   1756                         .follow_symlinks = options.follow_symlinks,
   1757                     }) catch |stat_err| switch (stat_err) {
   1758                         error.IsDir => break :check_dir,
   1759                         else => |e| return e,
   1760                     };
   1761                     if (fstat.kind != .directory) return error.NotDir;
   1762                 }
   1763 
   1764                 component = it.next().?;
   1765                 continue;
   1766             },
   1767 
   1768             .OBJECT_NAME_NOT_FOUND,
   1769             .OBJECT_PATH_NOT_FOUND,
   1770             => {
   1771                 component = it.previous() orelse return error.FileNotFound;
   1772                 continue;
   1773             },
   1774 
   1775             .NOT_A_DIRECTORY => return error.NotDir,
   1776             // This can happen if the directory has 'List folder contents' permission set to 'Deny'
   1777             // and the directory is trying to be opened for iteration.
   1778             .ACCESS_DENIED => return error.AccessDenied,
   1779             .INVALID_PARAMETER => |err| return w.statusBug(err),
   1780             else => return w.unexpectedStatus(rc),
   1781         }
   1782     }
   1783 }
   1784 
   1785 fn dirMakeOpenPathWasi(
   1786     userdata: ?*anyopaque,
   1787     dir: Dir,
   1788     sub_path: []const u8,
   1789     permissions: Dir.Permissions,
   1790     options: Dir.OpenOptions,
   1791 ) Dir.MakeOpenPathError!Dir {
   1792     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1793     const t_io = ioBasic(t);
   1794     return dirOpenDirWasi(t, dir, sub_path, options) catch |err| switch (err) {
   1795         error.FileNotFound => {
   1796             _ = try dir.makePathStatus(t_io, sub_path, permissions);
   1797             return dirOpenDirWasi(t, dir, sub_path, options);
   1798         },
   1799         else => |e| return e,
   1800     };
   1801 }
   1802 
   1803 fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {
   1804     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1805     const file: File = .{ .handle = dir.handle };
   1806     return fileStat(t, file);
   1807 }
   1808 
   1809 const dirStatFile = switch (native_os) {
   1810     .linux => dirStatFileLinux,
   1811     .windows => dirStatFileWindows,
   1812     .wasi => dirStatFileWasi,
   1813     else => dirStatFilePosix,
   1814 };
   1815 
   1816 fn dirStatFileLinux(
   1817     userdata: ?*anyopaque,
   1818     dir: Dir,
   1819     sub_path: []const u8,
   1820     options: Dir.StatFileOptions,
   1821 ) Dir.StatFileError!File.Stat {
   1822     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1823     const current_thread = Thread.getCurrent(t);
   1824     const linux = std.os.linux;
   1825     const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())
   1826         .{ .major = 30, .minor = 0, .patch = 0 }
   1827     else
   1828         .{ .major = 2, .minor = 28, .patch = 0 });
   1829     const sys = if (use_c) std.c else std.os.linux;
   1830 
   1831     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   1832     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   1833 
   1834     const flags: u32 = linux.AT.NO_AUTOMOUNT |
   1835         @as(u32, if (!options.follow_symlinks) linux.AT.SYMLINK_NOFOLLOW else 0);
   1836 
   1837     try current_thread.beginSyscall();
   1838     while (true) {
   1839         var statx = std.mem.zeroes(linux.Statx);
   1840         const rc = sys.statx(
   1841             dir.handle,
   1842             sub_path_posix,
   1843             flags,
   1844             .{ .TYPE = true, .MODE = true, .ATIME = true, .MTIME = true, .CTIME = true, .INO = true, .SIZE = true },
   1845             &statx,
   1846         );
   1847         switch (sys.errno(rc)) {
   1848             .SUCCESS => {
   1849                 current_thread.endSyscall();
   1850                 assert(statx.mask.TYPE);
   1851                 assert(statx.mask.MODE);
   1852                 assert(statx.mask.ATIME);
   1853                 assert(statx.mask.MTIME);
   1854                 assert(statx.mask.CTIME);
   1855                 assert(statx.mask.INO);
   1856                 assert(statx.mask.SIZE);
   1857                 return statFromLinux(&statx);
   1858             },
   1859             .INTR => {
   1860                 try current_thread.checkCancel();
   1861                 continue;
   1862             },
   1863             .CANCELED => return current_thread.endSyscallCanceled(),
   1864             else => |e| {
   1865                 current_thread.endSyscall();
   1866                 switch (e) {
   1867                     .ACCES => return error.AccessDenied,
   1868                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   1869                     .FAULT => |err| return errnoBug(err),
   1870                     .INVAL => |err| return errnoBug(err),
   1871                     .LOOP => return error.SymLinkLoop,
   1872                     .NAMETOOLONG => |err| return errnoBug(err), // Handled by pathToPosix() above.
   1873                     .NOENT => return error.FileNotFound,
   1874                     .NOTDIR => return error.NotDir,
   1875                     .NOMEM => return error.SystemResources,
   1876                     else => |err| return posix.unexpectedErrno(err),
   1877                 }
   1878             },
   1879         }
   1880     }
   1881 }
   1882 
   1883 fn dirStatFilePosix(
   1884     userdata: ?*anyopaque,
   1885     dir: Dir,
   1886     sub_path: []const u8,
   1887     options: Dir.StatFileOptions,
   1888 ) Dir.StatFileError!File.Stat {
   1889     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1890     const current_thread = Thread.getCurrent(t);
   1891 
   1892     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   1893     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   1894 
   1895     const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
   1896 
   1897     return posixStatFile(current_thread, dir.handle, sub_path_posix, flags);
   1898 }
   1899 
   1900 fn posixStatFile(current_thread: *Thread, dir_fd: posix.fd_t, sub_path: [:0]const u8, flags: u32) Dir.StatFileError!File.Stat {
   1901     try current_thread.beginSyscall();
   1902     while (true) {
   1903         var stat = std.mem.zeroes(posix.Stat);
   1904         switch (posix.errno(fstatat_sym(dir_fd, sub_path, &stat, flags))) {
   1905             .SUCCESS => {
   1906                 current_thread.endSyscall();
   1907                 return statFromPosix(&stat);
   1908             },
   1909             .INTR => {
   1910                 try current_thread.checkCancel();
   1911                 continue;
   1912             },
   1913             .CANCELED => return current_thread.endSyscallCanceled(),
   1914             else => |e| {
   1915                 current_thread.endSyscall();
   1916                 switch (e) {
   1917                     .INVAL => |err| return errnoBug(err),
   1918                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   1919                     .NOMEM => return error.SystemResources,
   1920                     .ACCES => return error.AccessDenied,
   1921                     .PERM => return error.PermissionDenied,
   1922                     .FAULT => |err| return errnoBug(err),
   1923                     .NAMETOOLONG => return error.NameTooLong,
   1924                     .LOOP => return error.SymLinkLoop,
   1925                     .NOENT => return error.FileNotFound,
   1926                     .NOTDIR => return error.FileNotFound,
   1927                     .ILSEQ => return error.BadPathName,
   1928                     else => |err| return posix.unexpectedErrno(err),
   1929                 }
   1930             },
   1931         }
   1932     }
   1933 }
   1934 
   1935 fn dirStatFileWindows(
   1936     userdata: ?*anyopaque,
   1937     dir: Dir,
   1938     sub_path: []const u8,
   1939     options: Dir.StatFileOptions,
   1940 ) Dir.StatFileError!File.Stat {
   1941     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1942     const file = try dirOpenFileWindows(t, dir, sub_path, .{
   1943         .follow_symlinks = options.follow_symlinks,
   1944     });
   1945     defer windows.CloseHandle(file.handle);
   1946     return fileStatWindows(t, file);
   1947 }
   1948 
   1949 fn dirStatFileWasi(
   1950     userdata: ?*anyopaque,
   1951     dir: Dir,
   1952     sub_path: []const u8,
   1953     options: Dir.StatFileOptions,
   1954 ) Dir.StatFileError!File.Stat {
   1955     if (builtin.link_libc) return dirStatFilePosix(userdata, dir, sub_path, options);
   1956     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1957     const current_thread = Thread.getCurrent(t);
   1958     const wasi = std.os.wasi;
   1959     const flags: wasi.lookupflags_t = .{
   1960         .SYMLINK_FOLLOW = options.follow_symlinks,
   1961     };
   1962     var stat: wasi.filestat_t = undefined;
   1963     try current_thread.beginSyscall();
   1964     while (true) {
   1965         switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
   1966             .SUCCESS => {
   1967                 current_thread.endSyscall();
   1968                 return statFromWasi(&stat);
   1969             },
   1970             .INTR => {
   1971                 try current_thread.checkCancel();
   1972                 continue;
   1973             },
   1974             .CANCELED => return current_thread.endSyscallCanceled(),
   1975             else => |e| {
   1976                 current_thread.endSyscall();
   1977                 switch (e) {
   1978                     .INVAL => |err| return errnoBug(err),
   1979                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   1980                     .NOMEM => return error.SystemResources,
   1981                     .ACCES => return error.AccessDenied,
   1982                     .FAULT => |err| return errnoBug(err),
   1983                     .NAMETOOLONG => return error.NameTooLong,
   1984                     .NOENT => return error.FileNotFound,
   1985                     .NOTDIR => return error.FileNotFound,
   1986                     .NOTCAPABLE => return error.AccessDenied,
   1987                     .ILSEQ => return error.BadPathName,
   1988                     else => |err| return posix.unexpectedErrno(err),
   1989                 }
   1990             },
   1991         }
   1992     }
   1993 }
   1994 
   1995 fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {
   1996     const t: *Threaded = @ptrCast(@alignCast(userdata));
   1997 
   1998     if (native_os == .linux) {
   1999         const current_thread = Thread.getCurrent(t);
   2000         const linux = std.os.linux;
   2001 
   2002         try current_thread.beginSyscall();
   2003         while (true) {
   2004             var statx = std.mem.zeroes(linux.Statx);
   2005             switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .{ .SIZE = true }, &statx))) {
   2006                 .SUCCESS => {
   2007                     current_thread.endSyscall();
   2008                     return statx.size;
   2009                 },
   2010                 .INTR => {
   2011                     try current_thread.checkCancel();
   2012                     continue;
   2013                 },
   2014                 .CANCELED => return current_thread.endSyscallCanceled(),
   2015                 else => |e| {
   2016                     current_thread.endSyscall();
   2017                     switch (e) {
   2018                         .ACCES => |err| return errnoBug(err),
   2019                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2020                         .FAULT => |err| return errnoBug(err),
   2021                         .INVAL => |err| return errnoBug(err),
   2022                         .LOOP => |err| return errnoBug(err),
   2023                         .NAMETOOLONG => |err| return errnoBug(err),
   2024                         .NOENT => |err| return errnoBug(err),
   2025                         .NOMEM => return error.SystemResources,
   2026                         .NOTDIR => |err| return errnoBug(err),
   2027                         else => |err| return posix.unexpectedErrno(err),
   2028                     }
   2029                 },
   2030             }
   2031         }
   2032     } else if (is_windows) {
   2033         // TODO call NtQueryInformationFile and ask for only the size instead of "all"
   2034     }
   2035 
   2036     const stat = try fileStat(t, file);
   2037     return stat.size;
   2038 }
   2039 
   2040 const fileStat = switch (native_os) {
   2041     .linux => fileStatLinux,
   2042     .windows => fileStatWindows,
   2043     .wasi => fileStatWasi,
   2044     else => fileStatPosix,
   2045 };
   2046 
   2047 fn fileStatPosix(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
   2048     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2049     const current_thread = Thread.getCurrent(t);
   2050 
   2051     if (posix.Stat == void) return error.Streaming;
   2052 
   2053     try current_thread.beginSyscall();
   2054     while (true) {
   2055         var stat = std.mem.zeroes(posix.Stat);
   2056         switch (posix.errno(fstat_sym(file.handle, &stat))) {
   2057             .SUCCESS => {
   2058                 current_thread.endSyscall();
   2059                 return statFromPosix(&stat);
   2060             },
   2061             .INTR => {
   2062                 try current_thread.checkCancel();
   2063                 continue;
   2064             },
   2065             .CANCELED => return current_thread.endSyscallCanceled(),
   2066             else => |e| {
   2067                 current_thread.endSyscall();
   2068                 switch (e) {
   2069                     .INVAL => |err| return errnoBug(err),
   2070                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2071                     .NOMEM => return error.SystemResources,
   2072                     .ACCES => return error.AccessDenied,
   2073                     else => |err| return posix.unexpectedErrno(err),
   2074                 }
   2075             },
   2076         }
   2077     }
   2078 }
   2079 
   2080 fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
   2081     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2082     const current_thread = Thread.getCurrent(t);
   2083     const linux = std.os.linux;
   2084     const use_c = std.c.versionCheck(if (builtin.abi.isAndroid())
   2085         .{ .major = 30, .minor = 0, .patch = 0 }
   2086     else
   2087         .{ .major = 2, .minor = 28, .patch = 0 });
   2088     const sys = if (use_c) std.c else std.os.linux;
   2089 
   2090     try current_thread.beginSyscall();
   2091     while (true) {
   2092         var statx = std.mem.zeroes(linux.Statx);
   2093         const rc = sys.statx(
   2094             file.handle,
   2095             "",
   2096             linux.AT.EMPTY_PATH,
   2097             .{ .TYPE = true, .MODE = true, .ATIME = true, .MTIME = true, .CTIME = true, .INO = true, .SIZE = true },
   2098             &statx,
   2099         );
   2100         switch (sys.errno(rc)) {
   2101             .SUCCESS => {
   2102                 current_thread.endSyscall();
   2103                 assert(statx.mask.TYPE);
   2104                 assert(statx.mask.MODE);
   2105                 assert(statx.mask.ATIME);
   2106                 assert(statx.mask.MTIME);
   2107                 assert(statx.mask.CTIME);
   2108                 assert(statx.mask.INO);
   2109                 assert(statx.mask.SIZE);
   2110                 return statFromLinux(&statx);
   2111             },
   2112             .INTR => {
   2113                 try current_thread.checkCancel();
   2114                 continue;
   2115             },
   2116             .CANCELED => return current_thread.endSyscallCanceled(),
   2117             else => |e| {
   2118                 current_thread.endSyscall();
   2119                 switch (e) {
   2120                     .ACCES => |err| return errnoBug(err),
   2121                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2122                     .FAULT => |err| return errnoBug(err),
   2123                     .INVAL => |err| return errnoBug(err),
   2124                     .LOOP => |err| return errnoBug(err),
   2125                     .NAMETOOLONG => |err| return errnoBug(err),
   2126                     .NOENT => |err| return errnoBug(err),
   2127                     .NOMEM => return error.SystemResources,
   2128                     .NOTDIR => |err| return errnoBug(err),
   2129                     else => |err| return posix.unexpectedErrno(err),
   2130                 }
   2131             },
   2132         }
   2133     }
   2134 }
   2135 
   2136 fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
   2137     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2138     const current_thread = Thread.getCurrent(t);
   2139     try current_thread.checkCancel();
   2140 
   2141     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   2142     var info: windows.FILE.ALL_INFORMATION = undefined;
   2143     const rc = windows.ntdll.NtQueryInformationFile(file.handle, &io_status_block, &info, @sizeOf(windows.FILE.ALL_INFORMATION), .All);
   2144     switch (rc) {
   2145         .SUCCESS => {},
   2146         // Buffer overflow here indicates that there is more information available than was able to be stored in the buffer
   2147         // size provided. This is treated as success because the type of variable-length information that this would be relevant for
   2148         // (name, volume name, etc) we don't care about.
   2149         .BUFFER_OVERFLOW => {},
   2150         .INVALID_PARAMETER => |err| return windows.statusBug(err),
   2151         .ACCESS_DENIED => return error.AccessDenied,
   2152         else => return windows.unexpectedStatus(rc),
   2153     }
   2154     return .{
   2155         .inode = info.InternalInformation.IndexNumber,
   2156         .size = @as(u64, @bitCast(info.StandardInformation.EndOfFile)),
   2157         .mode = 0,
   2158         .kind = if (info.BasicInformation.FileAttributes.REPARSE_POINT) reparse_point: {
   2159             var tag_info: windows.FILE.ATTRIBUTE_TAG_INFO = undefined;
   2160             const tag_rc = windows.ntdll.NtQueryInformationFile(file.handle, &io_status_block, &tag_info, @sizeOf(windows.FILE.ATTRIBUTE_TAG_INFO), .AttributeTag);
   2161             switch (tag_rc) {
   2162                 .SUCCESS => {},
   2163                 // INFO_LENGTH_MISMATCH and ACCESS_DENIED are the only documented possible errors
   2164                 // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d295752f-ce89-4b98-8553-266d37c84f0e
   2165                 .INFO_LENGTH_MISMATCH => |err| return windows.statusBug(err),
   2166                 .ACCESS_DENIED => return error.AccessDenied,
   2167                 else => return windows.unexpectedStatus(rc),
   2168             }
   2169             if (tag_info.ReparseTag.IsSurrogate) break :reparse_point .sym_link;
   2170             // Unknown reparse point
   2171             break :reparse_point .unknown;
   2172         } else if (info.BasicInformation.FileAttributes.DIRECTORY)
   2173             .directory
   2174         else
   2175             .file,
   2176         .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
   2177         .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
   2178         .ctime = windows.fromSysTime(info.BasicInformation.ChangeTime),
   2179     };
   2180 }
   2181 
   2182 fn fileStatWasi(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
   2183     if (builtin.link_libc) return fileStatPosix(userdata, file);
   2184 
   2185     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2186     const current_thread = Thread.getCurrent(t);
   2187 
   2188     try current_thread.beginSyscall();
   2189     while (true) {
   2190         var stat: std.os.wasi.filestat_t = undefined;
   2191         switch (std.os.wasi.fd_filestat_get(file.handle, &stat)) {
   2192             .SUCCESS => {
   2193                 current_thread.endSyscall();
   2194                 return statFromWasi(&stat);
   2195             },
   2196             .INTR => {
   2197                 try current_thread.checkCancel();
   2198                 continue;
   2199             },
   2200             .CANCELED => return current_thread.endSyscallCanceled(),
   2201             else => |e| {
   2202                 current_thread.endSyscall();
   2203                 switch (e) {
   2204                     .INVAL => |err| return errnoBug(err),
   2205                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2206                     .NOMEM => return error.SystemResources,
   2207                     .ACCES => return error.AccessDenied,
   2208                     .NOTCAPABLE => return error.AccessDenied,
   2209                     else => |err| return posix.unexpectedErrno(err),
   2210                 }
   2211             },
   2212         }
   2213     }
   2214 }
   2215 
   2216 const dirAccess = switch (native_os) {
   2217     .windows => dirAccessWindows,
   2218     .wasi => dirAccessWasi,
   2219     else => dirAccessPosix,
   2220 };
   2221 
   2222 fn dirAccessPosix(
   2223     userdata: ?*anyopaque,
   2224     dir: Dir,
   2225     sub_path: []const u8,
   2226     options: Dir.AccessOptions,
   2227 ) Dir.AccessError!void {
   2228     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2229     const current_thread = Thread.getCurrent(t);
   2230 
   2231     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   2232     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   2233 
   2234     const flags: u32 = @as(u32, if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0);
   2235 
   2236     const mode: u32 =
   2237         @as(u32, if (options.read) posix.R_OK else 0) |
   2238         @as(u32, if (options.write) posix.W_OK else 0) |
   2239         @as(u32, if (options.execute) posix.X_OK else 0);
   2240 
   2241     try current_thread.beginSyscall();
   2242     while (true) {
   2243         switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) {
   2244             .SUCCESS => {
   2245                 current_thread.endSyscall();
   2246                 return;
   2247             },
   2248             .INTR => {
   2249                 try current_thread.checkCancel();
   2250                 continue;
   2251             },
   2252             .CANCELED => return current_thread.endSyscallCanceled(),
   2253             else => |e| {
   2254                 current_thread.endSyscall();
   2255                 switch (e) {
   2256                     .ACCES => return error.AccessDenied,
   2257                     .PERM => return error.PermissionDenied,
   2258                     .ROFS => return error.ReadOnlyFileSystem,
   2259                     .LOOP => return error.SymLinkLoop,
   2260                     .TXTBSY => return error.FileBusy,
   2261                     .NOTDIR => return error.FileNotFound,
   2262                     .NOENT => return error.FileNotFound,
   2263                     .NAMETOOLONG => return error.NameTooLong,
   2264                     .INVAL => |err| return errnoBug(err),
   2265                     .FAULT => |err| return errnoBug(err),
   2266                     .IO => return error.InputOutput,
   2267                     .NOMEM => return error.SystemResources,
   2268                     .ILSEQ => return error.BadPathName,
   2269                     else => |err| return posix.unexpectedErrno(err),
   2270                 }
   2271             },
   2272         }
   2273     }
   2274 }
   2275 
   2276 fn dirAccessWasi(
   2277     userdata: ?*anyopaque,
   2278     dir: Dir,
   2279     sub_path: []const u8,
   2280     options: Dir.AccessOptions,
   2281 ) Dir.AccessError!void {
   2282     if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options);
   2283     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2284     const current_thread = Thread.getCurrent(t);
   2285     const wasi = std.os.wasi;
   2286     const flags: wasi.lookupflags_t = .{
   2287         .SYMLINK_FOLLOW = options.follow_symlinks,
   2288     };
   2289     var stat: wasi.filestat_t = undefined;
   2290 
   2291     try current_thread.beginSyscall();
   2292     while (true) {
   2293         switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
   2294             .SUCCESS => {
   2295                 current_thread.endSyscall();
   2296                 break;
   2297             },
   2298             .INTR => {
   2299                 try current_thread.checkCancel();
   2300                 continue;
   2301             },
   2302             .CANCELED => return current_thread.endSyscallCanceled(),
   2303             else => |e| {
   2304                 current_thread.endSyscall();
   2305                 switch (e) {
   2306                     .INVAL => |err| return errnoBug(err),
   2307                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2308                     .NOMEM => return error.SystemResources,
   2309                     .ACCES => return error.AccessDenied,
   2310                     .FAULT => |err| return errnoBug(err),
   2311                     .NAMETOOLONG => return error.NameTooLong,
   2312                     .NOENT => return error.FileNotFound,
   2313                     .NOTDIR => return error.FileNotFound,
   2314                     .NOTCAPABLE => return error.AccessDenied,
   2315                     .ILSEQ => return error.BadPathName,
   2316                     else => |err| return posix.unexpectedErrno(err),
   2317                 }
   2318             },
   2319         }
   2320     }
   2321 
   2322     if (!options.read and !options.write and !options.execute)
   2323         return;
   2324 
   2325     var directory: wasi.fdstat_t = undefined;
   2326     if (wasi.fd_fdstat_get(dir.handle, &directory) != .SUCCESS)
   2327         return error.AccessDenied;
   2328 
   2329     var rights: wasi.rights_t = .{};
   2330     if (options.read) {
   2331         if (stat.filetype == .DIRECTORY) {
   2332             rights.FD_READDIR = true;
   2333         } else {
   2334             rights.FD_READ = true;
   2335         }
   2336     }
   2337     if (options.write)
   2338         rights.FD_WRITE = true;
   2339 
   2340     // No validation for execution.
   2341 
   2342     // https://github.com/ziglang/zig/issues/18882
   2343     const rights_int: u64 = @bitCast(rights);
   2344     const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting);
   2345     if ((rights_int & inheriting_int) != rights_int)
   2346         return error.AccessDenied;
   2347 }
   2348 
   2349 fn dirAccessWindows(
   2350     userdata: ?*anyopaque,
   2351     dir: Dir,
   2352     sub_path: []const u8,
   2353     options: Dir.AccessOptions,
   2354 ) Dir.AccessError!void {
   2355     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2356     const current_thread = Thread.getCurrent(t);
   2357     try current_thread.checkCancel();
   2358 
   2359     _ = options; // TODO
   2360 
   2361     const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
   2362     const sub_path_w = sub_path_w_array.span();
   2363 
   2364     if (sub_path_w[0] == '.' and sub_path_w[1] == 0) return;
   2365     if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) return;
   2366 
   2367     const path_len_bytes = std.math.cast(u16, std.mem.sliceTo(sub_path_w, 0).len * 2) orelse
   2368         return error.NameTooLong;
   2369     var nt_name: windows.UNICODE_STRING = .{
   2370         .Length = path_len_bytes,
   2371         .MaximumLength = path_len_bytes,
   2372         .Buffer = @constCast(sub_path_w.ptr),
   2373     };
   2374     var attr: windows.OBJECT_ATTRIBUTES = .{
   2375         .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
   2376         .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
   2377         .Attributes = .{},
   2378         .ObjectName = &nt_name,
   2379         .SecurityDescriptor = null,
   2380         .SecurityQualityOfService = null,
   2381     };
   2382     var basic_info: windows.FILE.BASIC_INFORMATION = undefined;
   2383     switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
   2384         .SUCCESS => return,
   2385         .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   2386         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   2387         .OBJECT_NAME_INVALID => |err| return windows.statusBug(err),
   2388         .INVALID_PARAMETER => |err| return windows.statusBug(err),
   2389         .ACCESS_DENIED => return error.AccessDenied,
   2390         .OBJECT_PATH_SYNTAX_BAD => |err| return windows.statusBug(err),
   2391         else => |rc| return windows.unexpectedStatus(rc),
   2392     }
   2393 }
   2394 
   2395 const dirCreateFile = switch (native_os) {
   2396     .windows => dirCreateFileWindows,
   2397     .wasi => dirCreateFileWasi,
   2398     else => dirCreateFilePosix,
   2399 };
   2400 
   2401 fn dirCreateFilePosix(
   2402     userdata: ?*anyopaque,
   2403     dir: Dir,
   2404     sub_path: []const u8,
   2405     flags: File.CreateFlags,
   2406 ) File.OpenError!File {
   2407     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2408     const current_thread = Thread.getCurrent(t);
   2409 
   2410     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   2411     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   2412 
   2413     var os_flags: posix.O = .{
   2414         .ACCMODE = if (flags.read) .RDWR else .WRONLY,
   2415         .CREAT = true,
   2416         .TRUNC = flags.truncate,
   2417         .EXCL = flags.exclusive,
   2418     };
   2419     if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true;
   2420     if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true;
   2421 
   2422     // Use the O locking flags if the os supports them to acquire the lock
   2423     // atomically. Note that the NONBLOCK flag is removed after the openat()
   2424     // call is successful.
   2425     if (have_flock_open_flags) switch (flags.lock) {
   2426         .none => {},
   2427         .shared => {
   2428             os_flags.SHLOCK = true;
   2429             os_flags.NONBLOCK = flags.lock_nonblocking;
   2430         },
   2431         .exclusive => {
   2432             os_flags.EXLOCK = true;
   2433             os_flags.NONBLOCK = flags.lock_nonblocking;
   2434         },
   2435     };
   2436 
   2437     try current_thread.beginSyscall();
   2438     const fd: posix.fd_t = while (true) {
   2439         const rc = openat_sym(dir.handle, sub_path_posix, os_flags, flags.permissions.toMode());
   2440         switch (posix.errno(rc)) {
   2441             .SUCCESS => {
   2442                 current_thread.endSyscall();
   2443                 break @intCast(rc);
   2444             },
   2445             .INTR => {
   2446                 try current_thread.checkCancel();
   2447                 continue;
   2448             },
   2449             .CANCELED => return current_thread.endSyscallCanceled(),
   2450             else => |e| {
   2451                 current_thread.endSyscall();
   2452                 switch (e) {
   2453                     .FAULT => |err| return errnoBug(err),
   2454                     .INVAL => return error.BadPathName,
   2455                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2456                     .ACCES => return error.AccessDenied,
   2457                     .FBIG => return error.FileTooBig,
   2458                     .OVERFLOW => return error.FileTooBig,
   2459                     .ISDIR => return error.IsDir,
   2460                     .LOOP => return error.SymLinkLoop,
   2461                     .MFILE => return error.ProcessFdQuotaExceeded,
   2462                     .NAMETOOLONG => return error.NameTooLong,
   2463                     .NFILE => return error.SystemFdQuotaExceeded,
   2464                     .NODEV => return error.NoDevice,
   2465                     .NOENT => return error.FileNotFound,
   2466                     .SRCH => return error.FileNotFound, // Linux when accessing procfs.
   2467                     .NOMEM => return error.SystemResources,
   2468                     .NOSPC => return error.NoSpaceLeft,
   2469                     .NOTDIR => return error.NotDir,
   2470                     .PERM => return error.PermissionDenied,
   2471                     .EXIST => return error.PathAlreadyExists,
   2472                     .BUSY => return error.DeviceBusy,
   2473                     .OPNOTSUPP => return error.FileLocksUnsupported,
   2474                     .AGAIN => return error.WouldBlock,
   2475                     .TXTBSY => return error.FileBusy,
   2476                     .NXIO => return error.NoDevice,
   2477                     .ILSEQ => return error.BadPathName,
   2478                     else => |err| return posix.unexpectedErrno(err),
   2479                 }
   2480             },
   2481         }
   2482     };
   2483     errdefer posix.close(fd);
   2484 
   2485     if (have_flock and !have_flock_open_flags and flags.lock != .none) {
   2486         const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0;
   2487         const lock_flags = switch (flags.lock) {
   2488             .none => unreachable,
   2489             .shared => posix.LOCK.SH | lock_nonblocking,
   2490             .exclusive => posix.LOCK.EX | lock_nonblocking,
   2491         };
   2492 
   2493         try current_thread.beginSyscall();
   2494         while (true) {
   2495             switch (posix.errno(posix.system.flock(fd, lock_flags))) {
   2496                 .SUCCESS => {
   2497                     current_thread.endSyscall();
   2498                     break;
   2499                 },
   2500                 .INTR => {
   2501                     try current_thread.checkCancel();
   2502                     continue;
   2503                 },
   2504                 .CANCELED => return current_thread.endSyscallCanceled(),
   2505                 else => |e| {
   2506                     current_thread.endSyscall();
   2507                     switch (e) {
   2508                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2509                         .INVAL => |err| return errnoBug(err), // invalid parameters
   2510                         .NOLCK => return error.SystemResources,
   2511                         .AGAIN => return error.WouldBlock,
   2512                         .OPNOTSUPP => return error.FileLocksUnsupported,
   2513                         else => |err| return posix.unexpectedErrno(err),
   2514                     }
   2515                 },
   2516             }
   2517         }
   2518     }
   2519 
   2520     if (have_flock_open_flags and flags.lock_nonblocking) {
   2521         try current_thread.beginSyscall();
   2522         var fl_flags: usize = while (true) {
   2523             const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));
   2524             switch (posix.errno(rc)) {
   2525                 .SUCCESS => {
   2526                     current_thread.endSyscall();
   2527                     break @intCast(rc);
   2528                 },
   2529                 .INTR => {
   2530                     try current_thread.checkCancel();
   2531                     continue;
   2532                 },
   2533                 else => |err| {
   2534                     current_thread.endSyscall();
   2535                     return posix.unexpectedErrno(err);
   2536                 },
   2537             }
   2538         };
   2539 
   2540         fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK"));
   2541 
   2542         try current_thread.beginSyscall();
   2543         while (true) {
   2544             switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) {
   2545                 .SUCCESS => {
   2546                     current_thread.endSyscall();
   2547                     break;
   2548                 },
   2549                 .INTR => {
   2550                     try current_thread.checkCancel();
   2551                     continue;
   2552                 },
   2553                 else => |err| {
   2554                     current_thread.endSyscall();
   2555                     return posix.unexpectedErrno(err);
   2556                 },
   2557             }
   2558         }
   2559     }
   2560 
   2561     return .{ .handle = fd };
   2562 }
   2563 
   2564 fn dirCreateFileWindows(
   2565     userdata: ?*anyopaque,
   2566     dir: Dir,
   2567     sub_path: []const u8,
   2568     flags: File.CreateFlags,
   2569 ) File.OpenError!File {
   2570     const w = windows;
   2571     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2572     const current_thread = Thread.getCurrent(t);
   2573     try current_thread.checkCancel();
   2574 
   2575     const sub_path_w_array = try w.sliceToPrefixedFileW(dir.handle, sub_path);
   2576     const sub_path_w = sub_path_w_array.span();
   2577 
   2578     const handle = try w.OpenFile(sub_path_w, .{
   2579         .dir = dir.handle,
   2580         .access_mask = .{
   2581             .STANDARD = .{ .SYNCHRONIZE = true },
   2582             .GENERIC = .{
   2583                 .WRITE = true,
   2584                 .READ = flags.read,
   2585             },
   2586         },
   2587         .creation = if (flags.exclusive)
   2588             .CREATE
   2589         else if (flags.truncate)
   2590             .OVERWRITE_IF
   2591         else
   2592             .OPEN_IF,
   2593     });
   2594     errdefer w.CloseHandle(handle);
   2595     var io_status_block: w.IO_STATUS_BLOCK = undefined;
   2596     const range_off: w.LARGE_INTEGER = 0;
   2597     const range_len: w.LARGE_INTEGER = 1;
   2598     const exclusive = switch (flags.lock) {
   2599         .none => return .{ .handle = handle },
   2600         .shared => false,
   2601         .exclusive => true,
   2602     };
   2603     try w.LockFile(
   2604         handle,
   2605         null,
   2606         null,
   2607         null,
   2608         &io_status_block,
   2609         &range_off,
   2610         &range_len,
   2611         null,
   2612         @intFromBool(flags.lock_nonblocking),
   2613         @intFromBool(exclusive),
   2614     );
   2615     return .{ .handle = handle };
   2616 }
   2617 
   2618 fn dirCreateFileWasi(
   2619     userdata: ?*anyopaque,
   2620     dir: Dir,
   2621     sub_path: []const u8,
   2622     flags: File.CreateFlags,
   2623 ) File.OpenError!File {
   2624     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2625     const current_thread = Thread.getCurrent(t);
   2626     const wasi = std.os.wasi;
   2627     const lookup_flags: wasi.lookupflags_t = .{};
   2628     const oflags: wasi.oflags_t = .{
   2629         .CREAT = true,
   2630         .TRUNC = flags.truncate,
   2631         .EXCL = flags.exclusive,
   2632     };
   2633     const fdflags: wasi.fdflags_t = .{};
   2634     const base: wasi.rights_t = .{
   2635         .FD_READ = flags.read,
   2636         .FD_WRITE = true,
   2637         .FD_DATASYNC = true,
   2638         .FD_SEEK = true,
   2639         .FD_TELL = true,
   2640         .FD_FDSTAT_SET_FLAGS = true,
   2641         .FD_SYNC = true,
   2642         .FD_ALLOCATE = true,
   2643         .FD_ADVISE = true,
   2644         .FD_FILESTAT_SET_TIMES = true,
   2645         .FD_FILESTAT_SET_SIZE = true,
   2646         .FD_FILESTAT_GET = true,
   2647         // POLL_FD_READWRITE only grants extra rights if the corresponding FD_READ and/or
   2648         // FD_WRITE is also set.
   2649         .POLL_FD_READWRITE = true,
   2650     };
   2651     const inheriting: wasi.rights_t = .{};
   2652     var fd: posix.fd_t = undefined;
   2653     try current_thread.beginSyscall();
   2654     while (true) {
   2655         switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) {
   2656             .SUCCESS => {
   2657                 current_thread.endSyscall();
   2658                 return .{ .handle = fd };
   2659             },
   2660             .INTR => {
   2661                 try current_thread.checkCancel();
   2662                 continue;
   2663             },
   2664             .CANCELED => return current_thread.endSyscallCanceled(),
   2665             else => |e| {
   2666                 current_thread.endSyscall();
   2667                 switch (e) {
   2668                     .FAULT => |err| return errnoBug(err),
   2669                     .INVAL => return error.BadPathName,
   2670                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2671                     .ACCES => return error.AccessDenied,
   2672                     .FBIG => return error.FileTooBig,
   2673                     .OVERFLOW => return error.FileTooBig,
   2674                     .ISDIR => return error.IsDir,
   2675                     .LOOP => return error.SymLinkLoop,
   2676                     .MFILE => return error.ProcessFdQuotaExceeded,
   2677                     .NAMETOOLONG => return error.NameTooLong,
   2678                     .NFILE => return error.SystemFdQuotaExceeded,
   2679                     .NODEV => return error.NoDevice,
   2680                     .NOENT => return error.FileNotFound,
   2681                     .NOMEM => return error.SystemResources,
   2682                     .NOSPC => return error.NoSpaceLeft,
   2683                     .NOTDIR => return error.NotDir,
   2684                     .PERM => return error.PermissionDenied,
   2685                     .EXIST => return error.PathAlreadyExists,
   2686                     .BUSY => return error.DeviceBusy,
   2687                     .NOTCAPABLE => return error.AccessDenied,
   2688                     .ILSEQ => return error.BadPathName,
   2689                     else => |err| return posix.unexpectedErrno(err),
   2690                 }
   2691             },
   2692         }
   2693     }
   2694 }
   2695 
   2696 const dirOpenFile = switch (native_os) {
   2697     .windows => dirOpenFileWindows,
   2698     .wasi => dirOpenFileWasi,
   2699     else => dirOpenFilePosix,
   2700 };
   2701 
   2702 fn dirOpenFilePosix(
   2703     userdata: ?*anyopaque,
   2704     dir: Dir,
   2705     sub_path: []const u8,
   2706     flags: File.OpenFlags,
   2707 ) File.OpenError!File {
   2708     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2709     const current_thread = Thread.getCurrent(t);
   2710 
   2711     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   2712     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   2713 
   2714     var os_flags: posix.O = switch (native_os) {
   2715         .wasi => .{
   2716             .read = flags.mode != .write_only,
   2717             .write = flags.mode != .read_only,
   2718         },
   2719         else => .{
   2720             .ACCMODE = switch (flags.mode) {
   2721                 .read_only => .RDONLY,
   2722                 .write_only => .WRONLY,
   2723                 .read_write => .RDWR,
   2724             },
   2725         },
   2726     };
   2727     if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true;
   2728     if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true;
   2729     if (@hasField(posix.O, "NOCTTY")) os_flags.NOCTTY = !flags.allow_ctty;
   2730 
   2731     // Use the O locking flags if the os supports them to acquire the lock
   2732     // atomically. Note that the NONBLOCK flag is removed after the openat()
   2733     // call is successful.
   2734     if (have_flock_open_flags) switch (flags.lock) {
   2735         .none => {},
   2736         .shared => {
   2737             os_flags.SHLOCK = true;
   2738             os_flags.NONBLOCK = flags.lock_nonblocking;
   2739         },
   2740         .exclusive => {
   2741             os_flags.EXLOCK = true;
   2742             os_flags.NONBLOCK = flags.lock_nonblocking;
   2743         },
   2744     };
   2745 
   2746     try current_thread.beginSyscall();
   2747     const fd: posix.fd_t = while (true) {
   2748         const rc = openat_sym(dir.handle, sub_path_posix, os_flags, @as(posix.mode_t, 0));
   2749         switch (posix.errno(rc)) {
   2750             .SUCCESS => {
   2751                 current_thread.endSyscall();
   2752                 break @intCast(rc);
   2753             },
   2754             .INTR => {
   2755                 try current_thread.checkCancel();
   2756                 continue;
   2757             },
   2758             .CANCELED => return current_thread.endSyscallCanceled(),
   2759             else => |e| {
   2760                 current_thread.endSyscall();
   2761                 switch (e) {
   2762                     .FAULT => |err| return errnoBug(err),
   2763                     .INVAL => return error.BadPathName,
   2764                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2765                     .ACCES => return error.AccessDenied,
   2766                     .FBIG => return error.FileTooBig,
   2767                     .OVERFLOW => return error.FileTooBig,
   2768                     .ISDIR => return error.IsDir,
   2769                     .LOOP => return error.SymLinkLoop,
   2770                     .MFILE => return error.ProcessFdQuotaExceeded,
   2771                     .NAMETOOLONG => return error.NameTooLong,
   2772                     .NFILE => return error.SystemFdQuotaExceeded,
   2773                     .NODEV => return error.NoDevice,
   2774                     .NOENT => return error.FileNotFound,
   2775                     .SRCH => return error.FileNotFound, // Linux when opening procfs files.
   2776                     .NOMEM => return error.SystemResources,
   2777                     .NOSPC => return error.NoSpaceLeft,
   2778                     .NOTDIR => return error.NotDir,
   2779                     .PERM => return error.PermissionDenied,
   2780                     .EXIST => return error.PathAlreadyExists,
   2781                     .BUSY => return error.DeviceBusy,
   2782                     .OPNOTSUPP => return error.FileLocksUnsupported,
   2783                     .AGAIN => return error.WouldBlock,
   2784                     .TXTBSY => return error.FileBusy,
   2785                     .NXIO => return error.NoDevice,
   2786                     .ILSEQ => return error.BadPathName,
   2787                     else => |err| return posix.unexpectedErrno(err),
   2788                 }
   2789             },
   2790         }
   2791     };
   2792     errdefer posix.close(fd);
   2793 
   2794     if (have_flock and !have_flock_open_flags and flags.lock != .none) {
   2795         const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0;
   2796         const lock_flags = switch (flags.lock) {
   2797             .none => unreachable,
   2798             .shared => posix.LOCK.SH | lock_nonblocking,
   2799             .exclusive => posix.LOCK.EX | lock_nonblocking,
   2800         };
   2801         try current_thread.beginSyscall();
   2802         while (true) {
   2803             switch (posix.errno(posix.system.flock(fd, lock_flags))) {
   2804                 .SUCCESS => {
   2805                     current_thread.endSyscall();
   2806                     break;
   2807                 },
   2808                 .INTR => {
   2809                     try current_thread.checkCancel();
   2810                     continue;
   2811                 },
   2812                 .CANCELED => return current_thread.endSyscallCanceled(),
   2813                 else => |e| {
   2814                     current_thread.endSyscall();
   2815                     switch (e) {
   2816                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   2817                         .INVAL => |err| return errnoBug(err), // invalid parameters
   2818                         .NOLCK => return error.SystemResources,
   2819                         .AGAIN => return error.WouldBlock,
   2820                         .OPNOTSUPP => return error.FileLocksUnsupported,
   2821                         else => |err| return posix.unexpectedErrno(err),
   2822                     }
   2823                 },
   2824             }
   2825         }
   2826     }
   2827 
   2828     if (have_flock_open_flags and flags.lock_nonblocking) {
   2829         try current_thread.beginSyscall();
   2830         var fl_flags: usize = while (true) {
   2831             const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));
   2832             switch (posix.errno(rc)) {
   2833                 .SUCCESS => {
   2834                     current_thread.endSyscall();
   2835                     break @intCast(rc);
   2836                 },
   2837                 .INTR => {
   2838                     try current_thread.checkCancel();
   2839                     continue;
   2840                 },
   2841                 .CANCELED => return current_thread.endSyscallCanceled(),
   2842                 else => |err| {
   2843                     current_thread.endSyscall();
   2844                     return posix.unexpectedErrno(err);
   2845                 },
   2846             }
   2847         };
   2848 
   2849         fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK"));
   2850 
   2851         try current_thread.beginSyscall();
   2852         while (true) {
   2853             switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) {
   2854                 .SUCCESS => {
   2855                     current_thread.endSyscall();
   2856                     break;
   2857                 },
   2858                 .INTR => {
   2859                     try current_thread.checkCancel();
   2860                     continue;
   2861                 },
   2862                 .CANCELED => return current_thread.endSyscallCanceled(),
   2863                 else => |err| {
   2864                     current_thread.endSyscall();
   2865                     return posix.unexpectedErrno(err);
   2866                 },
   2867             }
   2868         }
   2869     }
   2870 
   2871     return .{ .handle = fd };
   2872 }
   2873 
   2874 fn dirOpenFileWindows(
   2875     userdata: ?*anyopaque,
   2876     dir: Dir,
   2877     sub_path: []const u8,
   2878     flags: File.OpenFlags,
   2879 ) File.OpenError!File {
   2880     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2881     const sub_path_w_array = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
   2882     const sub_path_w = sub_path_w_array.span();
   2883     const dir_handle = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle;
   2884     return dirOpenFileWtf16(t, dir_handle, sub_path_w, flags);
   2885 }
   2886 
   2887 pub fn dirOpenFileWtf16(
   2888     t: *Threaded,
   2889     dir_handle: ?windows.HANDLE,
   2890     sub_path_w: [:0]const u16,
   2891     flags: File.OpenFlags,
   2892 ) File.OpenError!File {
   2893     if (std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;
   2894     if (std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir;
   2895     const path_len_bytes = std.math.cast(u16, sub_path_w.len * 2) orelse return error.NameTooLong;
   2896     const current_thread = Thread.getCurrent(t);
   2897     const w = windows;
   2898 
   2899     var nt_name: w.UNICODE_STRING = .{
   2900         .Length = path_len_bytes,
   2901         .MaximumLength = path_len_bytes,
   2902         .Buffer = @constCast(sub_path_w.ptr),
   2903     };
   2904     var attr: w.OBJECT_ATTRIBUTES = .{
   2905         .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
   2906         .RootDirectory = dir_handle,
   2907         .Attributes = .{},
   2908         .ObjectName = &nt_name,
   2909         .SecurityDescriptor = null,
   2910         .SecurityQualityOfService = null,
   2911     };
   2912     var io_status_block: w.IO_STATUS_BLOCK = undefined;
   2913 
   2914     // There are multiple kernel bugs being worked around with retries.
   2915     const max_attempts = 13;
   2916     var attempt: u5 = 0;
   2917 
   2918     const handle = while (true) {
   2919         try current_thread.checkCancel();
   2920 
   2921         var result: w.HANDLE = undefined;
   2922         const rc = w.ntdll.NtCreateFile(
   2923             &result,
   2924             .{
   2925                 .STANDARD = .{ .SYNCHRONIZE = true },
   2926                 .GENERIC = .{
   2927                     .READ = flags.isRead(),
   2928                     .WRITE = flags.isWrite(),
   2929                 },
   2930             },
   2931             &attr,
   2932             &io_status_block,
   2933             null,
   2934             .{ .NORMAL = true },
   2935             .VALID_FLAGS,
   2936             .OPEN,
   2937             .{
   2938                 .IO = if (flags.follow_symlinks) .SYNCHRONOUS_NONALERT else .ASYNCHRONOUS,
   2939                 .NON_DIRECTORY_FILE = true,
   2940                 .OPEN_REPARSE_POINT = !flags.follow_symlinks,
   2941             },
   2942             null,
   2943             0,
   2944         );
   2945         switch (rc) {
   2946             .SUCCESS => break result,
   2947             .OBJECT_NAME_INVALID => return error.BadPathName,
   2948             .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   2949             .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   2950             .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
   2951             .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
   2952             .NO_MEDIA_IN_DEVICE => return error.NoDevice,
   2953             .INVALID_PARAMETER => |err| return w.statusBug(err),
   2954             .SHARING_VIOLATION => {
   2955                 // This occurs if the file attempting to be opened is a running
   2956                 // executable. However, there's a kernel bug: the error may be
   2957                 // incorrectly returned for an indeterminate amount of time
   2958                 // after an executable file is closed. Here we work around the
   2959                 // kernel bug with retry attempts.
   2960                 if (max_attempts - attempt == 0) return error.SharingViolation;
   2961                 _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
   2962                 attempt += 1;
   2963                 continue;
   2964             },
   2965             .ACCESS_DENIED => return error.AccessDenied,
   2966             .PIPE_BUSY => return error.PipeBusy,
   2967             .PIPE_NOT_AVAILABLE => return error.NoDevice,
   2968             .OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err),
   2969             .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
   2970             .FILE_IS_A_DIRECTORY => return error.IsDir,
   2971             .NOT_A_DIRECTORY => return error.NotDir,
   2972             .USER_MAPPED_FILE => return error.AccessDenied,
   2973             .INVALID_HANDLE => |err| return w.statusBug(err),
   2974             .DELETE_PENDING => {
   2975                 // This error means that there *was* a file in this location on
   2976                 // the file system, but it was deleted. However, the OS is not
   2977                 // finished with the deletion operation, and so this CreateFile
   2978                 // call has failed. Here, we simulate the kernel bug being
   2979                 // fixed by sleeping and retrying until the error goes away.
   2980                 if (max_attempts - attempt == 0) return error.SharingViolation;
   2981                 _ = w.kernel32.SleepEx((@as(u32, 1) << attempt) >> 1, w.TRUE);
   2982                 attempt += 1;
   2983                 continue;
   2984             },
   2985             .VIRUS_INFECTED, .VIRUS_DELETED => return error.AntivirusInterference,
   2986             else => return w.unexpectedStatus(rc),
   2987         }
   2988     };
   2989     errdefer w.CloseHandle(handle);
   2990 
   2991     const range_off: w.LARGE_INTEGER = 0;
   2992     const range_len: w.LARGE_INTEGER = 1;
   2993     const exclusive = switch (flags.lock) {
   2994         .none => return .{ .handle = handle },
   2995         .shared => false,
   2996         .exclusive => true,
   2997     };
   2998     try w.LockFile(
   2999         handle,
   3000         null,
   3001         null,
   3002         null,
   3003         &io_status_block,
   3004         &range_off,
   3005         &range_len,
   3006         null,
   3007         @intFromBool(flags.lock_nonblocking),
   3008         @intFromBool(exclusive),
   3009     );
   3010     return .{ .handle = handle };
   3011 }
   3012 
   3013 fn dirOpenFileWasi(
   3014     userdata: ?*anyopaque,
   3015     dir: Dir,
   3016     sub_path: []const u8,
   3017     flags: File.OpenFlags,
   3018 ) File.OpenError!File {
   3019     if (builtin.link_libc) return dirOpenFilePosix(userdata, dir, sub_path, flags);
   3020     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3021     const current_thread = Thread.getCurrent(t);
   3022     const wasi = std.os.wasi;
   3023     var base: std.os.wasi.rights_t = .{};
   3024     // POLL_FD_READWRITE only grants extra rights if the corresponding FD_READ and/or FD_WRITE
   3025     // is also set.
   3026     if (flags.isRead()) {
   3027         base.FD_READ = true;
   3028         base.FD_TELL = true;
   3029         base.FD_SEEK = true;
   3030         base.FD_FILESTAT_GET = true;
   3031         base.POLL_FD_READWRITE = true;
   3032     }
   3033     if (flags.isWrite()) {
   3034         base.FD_WRITE = true;
   3035         base.FD_TELL = true;
   3036         base.FD_SEEK = true;
   3037         base.FD_DATASYNC = true;
   3038         base.FD_FDSTAT_SET_FLAGS = true;
   3039         base.FD_SYNC = true;
   3040         base.FD_ALLOCATE = true;
   3041         base.FD_ADVISE = true;
   3042         base.FD_FILESTAT_SET_TIMES = true;
   3043         base.FD_FILESTAT_SET_SIZE = true;
   3044         base.POLL_FD_READWRITE = true;
   3045     }
   3046     const lookup_flags: wasi.lookupflags_t = .{};
   3047     const oflags: wasi.oflags_t = .{};
   3048     const inheriting: wasi.rights_t = .{};
   3049     const fdflags: wasi.fdflags_t = .{};
   3050     var fd: posix.fd_t = undefined;
   3051     try current_thread.beginSyscall();
   3052     while (true) {
   3053         switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) {
   3054             .SUCCESS => {
   3055                 errdefer posix.close(fd);
   3056                 current_thread.endSyscall();
   3057                 return .{ .handle = fd };
   3058             },
   3059             .INTR => {
   3060                 try current_thread.checkCancel();
   3061                 continue;
   3062             },
   3063             .CANCELED => return current_thread.endSyscallCanceled(),
   3064             else => |e| {
   3065                 current_thread.endSyscall();
   3066                 switch (e) {
   3067                     .FAULT => |err| return errnoBug(err),
   3068                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3069                     .ACCES => return error.AccessDenied,
   3070                     .FBIG => return error.FileTooBig,
   3071                     .OVERFLOW => return error.FileTooBig,
   3072                     .ISDIR => return error.IsDir,
   3073                     .LOOP => return error.SymLinkLoop,
   3074                     .MFILE => return error.ProcessFdQuotaExceeded,
   3075                     .NFILE => return error.SystemFdQuotaExceeded,
   3076                     .NODEV => return error.NoDevice,
   3077                     .NOENT => return error.FileNotFound,
   3078                     .NOMEM => return error.SystemResources,
   3079                     .NOTDIR => return error.NotDir,
   3080                     .PERM => return error.PermissionDenied,
   3081                     .BUSY => return error.DeviceBusy,
   3082                     .NOTCAPABLE => return error.AccessDenied,
   3083                     .NAMETOOLONG => return error.NameTooLong,
   3084                     .INVAL => return error.BadPathName,
   3085                     .ILSEQ => return error.BadPathName,
   3086                     else => |err| return posix.unexpectedErrno(err),
   3087                 }
   3088             },
   3089         }
   3090     }
   3091 }
   3092 
   3093 const dirOpenDir = switch (native_os) {
   3094     .wasi => dirOpenDirWasi,
   3095     .haiku => dirOpenDirHaiku,
   3096     else => dirOpenDirPosix,
   3097 };
   3098 
   3099 /// This function is also used for WASI when libc is linked.
   3100 fn dirOpenDirPosix(
   3101     userdata: ?*anyopaque,
   3102     dir: Dir,
   3103     sub_path: []const u8,
   3104     options: Dir.OpenOptions,
   3105 ) Dir.OpenError!Dir {
   3106     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3107 
   3108     if (is_windows) {
   3109         const sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
   3110         return dirOpenDirWindows(t, dir, sub_path_w.span(), options);
   3111     }
   3112 
   3113     const current_thread = Thread.getCurrent(t);
   3114 
   3115     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   3116     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   3117 
   3118     var flags: posix.O = switch (native_os) {
   3119         .wasi => .{
   3120             .read = true,
   3121             .NOFOLLOW = !options.follow_symlinks,
   3122             .DIRECTORY = true,
   3123         },
   3124         else => .{
   3125             .ACCMODE = .RDONLY,
   3126             .NOFOLLOW = !options.follow_symlinks,
   3127             .DIRECTORY = true,
   3128             .CLOEXEC = true,
   3129         },
   3130     };
   3131 
   3132     if (@hasField(posix.O, "PATH") and !options.iterate)
   3133         flags.PATH = true;
   3134 
   3135     try current_thread.beginSyscall();
   3136     while (true) {
   3137         const rc = openat_sym(dir.handle, sub_path_posix, flags, @as(usize, 0));
   3138         switch (posix.errno(rc)) {
   3139             .SUCCESS => {
   3140                 current_thread.endSyscall();
   3141                 return .{ .handle = @intCast(rc) };
   3142             },
   3143             .INTR => {
   3144                 try current_thread.checkCancel();
   3145                 continue;
   3146             },
   3147             .CANCELED => return current_thread.endSyscallCanceled(),
   3148             else => |e| {
   3149                 current_thread.endSyscall();
   3150                 switch (e) {
   3151                     .FAULT => |err| return errnoBug(err),
   3152                     .INVAL => return error.BadPathName,
   3153                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3154                     .ACCES => return error.AccessDenied,
   3155                     .LOOP => return error.SymLinkLoop,
   3156                     .MFILE => return error.ProcessFdQuotaExceeded,
   3157                     .NAMETOOLONG => return error.NameTooLong,
   3158                     .NFILE => return error.SystemFdQuotaExceeded,
   3159                     .NODEV => return error.NoDevice,
   3160                     .NOENT => return error.FileNotFound,
   3161                     .NOMEM => return error.SystemResources,
   3162                     .NOTDIR => return error.NotDir,
   3163                     .PERM => return error.PermissionDenied,
   3164                     .BUSY => return error.DeviceBusy,
   3165                     .NXIO => return error.NoDevice,
   3166                     .ILSEQ => return error.BadPathName,
   3167                     else => |err| return posix.unexpectedErrno(err),
   3168                 }
   3169             },
   3170         }
   3171     }
   3172 }
   3173 
   3174 fn dirOpenDirHaiku(
   3175     userdata: ?*anyopaque,
   3176     dir: Dir,
   3177     sub_path: []const u8,
   3178     options: Dir.OpenOptions,
   3179 ) Dir.OpenError!Dir {
   3180     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3181     const current_thread = Thread.getCurrent(t);
   3182 
   3183     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   3184     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   3185 
   3186     _ = options;
   3187 
   3188     try current_thread.beginSyscall();
   3189     while (true) {
   3190         const rc = posix.system._kern_open_dir(dir.handle, sub_path_posix);
   3191         if (rc >= 0) {
   3192             current_thread.endSyscall();
   3193             return .{ .handle = rc };
   3194         }
   3195         switch (@as(posix.E, @enumFromInt(rc))) {
   3196             .INTR => {
   3197                 try current_thread.checkCancel();
   3198                 continue;
   3199             },
   3200             .CANCELED => return current_thread.endSyscallCanceled(),
   3201             else => |e| {
   3202                 current_thread.endSyscall();
   3203                 switch (e) {
   3204                     .FAULT => |err| return errnoBug(err),
   3205                     .INVAL => |err| return errnoBug(err),
   3206                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3207                     .ACCES => return error.AccessDenied,
   3208                     .LOOP => return error.SymLinkLoop,
   3209                     .MFILE => return error.ProcessFdQuotaExceeded,
   3210                     .NAMETOOLONG => return error.NameTooLong,
   3211                     .NFILE => return error.SystemFdQuotaExceeded,
   3212                     .NODEV => return error.NoDevice,
   3213                     .NOENT => return error.FileNotFound,
   3214                     .NOMEM => return error.SystemResources,
   3215                     .NOTDIR => return error.NotDir,
   3216                     .PERM => return error.PermissionDenied,
   3217                     .BUSY => return error.DeviceBusy,
   3218                     else => |err| return posix.unexpectedErrno(err),
   3219                 }
   3220             },
   3221         }
   3222     }
   3223 }
   3224 
   3225 pub fn dirOpenDirWindows(
   3226     t: *Io.Threaded,
   3227     dir: Dir,
   3228     sub_path_w: [:0]const u16,
   3229     options: Dir.OpenOptions,
   3230 ) Dir.OpenError!Dir {
   3231     const current_thread = Thread.getCurrent(t);
   3232     const w = windows;
   3233 
   3234     const path_len_bytes: u16 = @intCast(sub_path_w.len * 2);
   3235     var nt_name: w.UNICODE_STRING = .{
   3236         .Length = path_len_bytes,
   3237         .MaximumLength = path_len_bytes,
   3238         .Buffer = @constCast(sub_path_w.ptr),
   3239     };
   3240     var io_status_block: w.IO_STATUS_BLOCK = undefined;
   3241     var result: Dir = .{ .handle = undefined };
   3242     try current_thread.checkCancel();
   3243     const rc = w.ntdll.NtCreateFile(
   3244         &result.handle,
   3245         // TODO remove some of these flags if options.access_sub_paths is false
   3246         .{
   3247             .SPECIFIC = .{ .FILE_DIRECTORY = .{
   3248                 .LIST = options.iterate,
   3249                 .READ_EA = true,
   3250                 .TRAVERSE = true,
   3251                 .READ_ATTRIBUTES = true,
   3252             } },
   3253             .STANDARD = .{
   3254                 .RIGHTS = .READ,
   3255                 .SYNCHRONIZE = true,
   3256             },
   3257         },
   3258         &.{
   3259             .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
   3260             .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
   3261             .Attributes = .{},
   3262             .ObjectName = &nt_name,
   3263             .SecurityDescriptor = null,
   3264             .SecurityQualityOfService = null,
   3265         },
   3266         &io_status_block,
   3267         null,
   3268         .{ .NORMAL = true },
   3269         .VALID_FLAGS,
   3270         .OPEN,
   3271         .{
   3272             .DIRECTORY_FILE = true,
   3273             .IO = .SYNCHRONOUS_NONALERT,
   3274             .OPEN_FOR_BACKUP_INTENT = true,
   3275             .OPEN_REPARSE_POINT = !options.follow_symlinks,
   3276         },
   3277         null,
   3278         0,
   3279     );
   3280 
   3281     switch (rc) {
   3282         .SUCCESS => return result,
   3283         .OBJECT_NAME_INVALID => return error.BadPathName,
   3284         .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   3285         .OBJECT_NAME_COLLISION => |err| return w.statusBug(err),
   3286         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   3287         .NOT_A_DIRECTORY => return error.NotDir,
   3288         // This can happen if the directory has 'List folder contents' permission set to 'Deny'
   3289         // and the directory is trying to be opened for iteration.
   3290         .ACCESS_DENIED => return error.AccessDenied,
   3291         .INVALID_PARAMETER => |err| return w.statusBug(err),
   3292         else => return w.unexpectedStatus(rc),
   3293     }
   3294 }
   3295 
   3296 const MakeOpenDirAccessMaskWOptions = struct {
   3297     no_follow: bool,
   3298     create_disposition: u32,
   3299 };
   3300 
   3301 fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void {
   3302     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3303     _ = t;
   3304     for (dirs) |dir| posix.close(dir.handle);
   3305 }
   3306 
   3307 const dirRead = switch (native_os) {
   3308     .linux => dirReadLinux,
   3309     .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => dirReadDarwin,
   3310     .freebsd, .netbsd, .dragonfly, .openbsd => dirReadBsd,
   3311     .illumos => dirReadIllumos,
   3312     .haiku => dirReadHaiku,
   3313     .windows => dirReadWindows,
   3314     .wasi => dirReadWasi,
   3315     else => dirReadUnimplemented,
   3316 };
   3317 
   3318 fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   3319     const linux = std.os.linux;
   3320     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3321     const current_thread = Thread.getCurrent(t);
   3322     var buffer_index: usize = 0;
   3323     while (buffer.len - buffer_index != 0) {
   3324         if (dr.end - dr.index == 0) {
   3325             // Refill the buffer, unless we've already created references to
   3326             // buffered data.
   3327             if (buffer_index != 0) break;
   3328             if (dr.state == .reset) {
   3329                 posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) {
   3330                     error.Unseekable => return error.Unexpected,
   3331                     else => |e| return e,
   3332                 };
   3333                 dr.state = .reading;
   3334             }
   3335             try current_thread.beginSyscall();
   3336             const n = while (true) {
   3337                 const rc = linux.getdents64(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
   3338                 switch (linux.errno(rc)) {
   3339                     .SUCCESS => {
   3340                         current_thread.endSyscall();
   3341                         break rc;
   3342                     },
   3343                     .INTR => {
   3344                         try current_thread.checkCancel();
   3345                         continue;
   3346                     },
   3347                     .CANCELED => return current_thread.endSyscallCanceled(),
   3348                     else => |e| {
   3349                         current_thread.endSyscall();
   3350                         switch (e) {
   3351                             .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.
   3352                             .FAULT => |err| return errnoBug(err),
   3353                             .NOTDIR => |err| return errnoBug(err),
   3354                             // To be consistent across platforms, iteration
   3355                             // ends if the directory being iterated is deleted
   3356                             // during iteration. This matches the behavior of
   3357                             // non-Linux UNIX platforms.
   3358                             .NOENT => {
   3359                                 dr.state = .finished;
   3360                                 return 0;
   3361                             },
   3362                             .INVAL => return error.Unexpected, // Linux may in some cases return EINVAL when reading /proc/$PID/net.
   3363                             .ACCES => return error.AccessDenied, // Lacking permission to iterate this directory.
   3364                             else => |err| return posix.unexpectedErrno(err),
   3365                         }
   3366                     },
   3367                 }
   3368             };
   3369             if (n == 0) {
   3370                 dr.state = .finished;
   3371                 return 0;
   3372             }
   3373             dr.index = 0;
   3374             dr.end = n;
   3375         }
   3376         const linux_entry: *align(1) linux.dirent64 = @ptrCast(&dr.buffer[dr.index]);
   3377         const next_index = dr.index + linux_entry.reclen;
   3378         dr.index = next_index;
   3379 
   3380         const name = std.mem.sliceTo(@as([*:0]u8, @ptrCast(&linux_entry.name)), 0);
   3381         if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;
   3382 
   3383         const entry_kind: File.Kind = switch (linux_entry.type) {
   3384             linux.DT.BLK => .block_device,
   3385             linux.DT.CHR => .character_device,
   3386             linux.DT.DIR => .directory,
   3387             linux.DT.FIFO => .named_pipe,
   3388             linux.DT.LNK => .sym_link,
   3389             linux.DT.REG => .file,
   3390             linux.DT.SOCK => .unix_domain_socket,
   3391             else => .unknown,
   3392         };
   3393         buffer[buffer_index] = .{
   3394             .name = name,
   3395             .kind = entry_kind,
   3396             .inode = linux_entry.ino,
   3397         };
   3398         buffer_index += 1;
   3399     }
   3400     return buffer_index;
   3401 }
   3402 
   3403 fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   3404     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3405     const current_thread = Thread.getCurrent(t);
   3406     const Header = extern struct {
   3407         seek: i64,
   3408     };
   3409     const header: *Header = @ptrCast(dr.buffer.ptr);
   3410     const header_end: usize = @sizeOf(Header);
   3411     if (dr.index < header_end) {
   3412         // Initialize header.
   3413         dr.index = header_end;
   3414         dr.end = header_end;
   3415         header.* = .{ .seek = 0 };
   3416     }
   3417     var buffer_index: usize = 0;
   3418     while (buffer.len - buffer_index != 0) {
   3419         if (dr.end - dr.index == 0) {
   3420             // Refill the buffer, unless we've already created references to
   3421             // buffered data.
   3422             if (buffer_index != 0) break;
   3423             if (dr.state == .reset) {
   3424                 posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) {
   3425                     error.Unseekable => return error.Unexpected,
   3426                     else => |e| return e,
   3427                 };
   3428                 dr.state = .reading;
   3429             }
   3430             const dents_buffer = dr.buffer[header_end..];
   3431             try current_thread.beginSyscall();
   3432             const n: usize = while (true) {
   3433                 const rc = posix.system.getdirentries(dr.dir.handle, dents_buffer.ptr, dents_buffer.len, &header.seek);
   3434                 switch (posix.errno(rc)) {
   3435                     .SUCCESS => {
   3436                         current_thread.endSyscall();
   3437                         break @intCast(rc);
   3438                     },
   3439                     .INTR => {
   3440                         try current_thread.checkCancel();
   3441                         continue;
   3442                     },
   3443                     .CANCELED => return current_thread.endSyscallCanceled(),
   3444                     else => |e| {
   3445                         current_thread.endSyscall();
   3446                         switch (e) {
   3447                             .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability
   3448                             .FAULT => |err| return errnoBug(err),
   3449                             .NOTDIR => |err| return errnoBug(err),
   3450                             .INVAL => |err| return errnoBug(err),
   3451                             else => |err| return posix.unexpectedErrno(err),
   3452                         }
   3453                     },
   3454                 }
   3455             };
   3456             if (n == 0) {
   3457                 dr.state = .finished;
   3458                 return 0;
   3459             }
   3460             dr.index = header_end;
   3461             dr.end = header_end + n;
   3462         }
   3463         const darwin_entry = @as(*align(1) posix.system.dirent, @ptrCast(&dr.buffer[dr.index]));
   3464         const next_index = dr.index + darwin_entry.reclen;
   3465         dr.index = next_index;
   3466 
   3467         const name = @as([*]u8, @ptrCast(&darwin_entry.name))[0..darwin_entry.namlen];
   3468         if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..") or (darwin_entry.ino == 0))
   3469             continue;
   3470 
   3471         const entry_kind: File.Kind = switch (darwin_entry.type) {
   3472             posix.DT.BLK => .block_device,
   3473             posix.DT.CHR => .character_device,
   3474             posix.DT.DIR => .directory,
   3475             posix.DT.FIFO => .named_pipe,
   3476             posix.DT.LNK => .sym_link,
   3477             posix.DT.REG => .file,
   3478             posix.DT.SOCK => .unix_domain_socket,
   3479             posix.DT.WHT => .whiteout,
   3480             else => .unknown,
   3481         };
   3482         buffer[buffer_index] = .{
   3483             .name = name,
   3484             .kind = entry_kind,
   3485             .inode = darwin_entry.ino,
   3486         };
   3487         buffer_index += 1;
   3488     }
   3489     return buffer_index;
   3490 }
   3491 
   3492 fn dirReadBsd(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   3493     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3494     const current_thread = Thread.getCurrent(t);
   3495     var buffer_index: usize = 0;
   3496     while (buffer.len - buffer_index != 0) {
   3497         if (dr.end - dr.index == 0) {
   3498             // Refill the buffer, unless we've already created references to
   3499             // buffered data.
   3500             if (buffer_index != 0) break;
   3501             if (dr.state == .reset) {
   3502                 posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) {
   3503                     error.Unseekable => return error.Unexpected,
   3504                     else => |e| return e,
   3505                 };
   3506                 dr.state = .reading;
   3507             }
   3508             try current_thread.beginSyscall();
   3509             const n: usize = while (true) {
   3510                 const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
   3511                 switch (posix.errno(rc)) {
   3512                     .SUCCESS => {
   3513                         current_thread.endSyscall();
   3514                         break @intCast(rc);
   3515                     },
   3516                     .INTR => {
   3517                         try current_thread.checkCancel();
   3518                         continue;
   3519                     },
   3520                     .CANCELED => return current_thread.endSyscallCanceled(),
   3521                     else => |e| {
   3522                         current_thread.endSyscall();
   3523                         switch (e) {
   3524                             .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability
   3525                             .FAULT => |err| return errnoBug(err),
   3526                             .NOTDIR => |err| return errnoBug(err),
   3527                             .INVAL => |err| return errnoBug(err),
   3528                             // Introduced in freebsd 13.2: directory unlinked
   3529                             // but still open. To be consistent, iteration ends
   3530                             // if the directory being iterated is deleted
   3531                             // during iteration.
   3532                             .NOENT => {
   3533                                 dr.state = .finished;
   3534                                 return 0;
   3535                             },
   3536                             else => |err| return posix.unexpectedErrno(err),
   3537                         }
   3538                     },
   3539                 }
   3540             };
   3541             if (n == 0) {
   3542                 dr.state = .finished;
   3543                 return 0;
   3544             }
   3545             dr.index = 0;
   3546             dr.end = n;
   3547         }
   3548         const bsd_entry = @as(*align(1) posix.system.dirent, @ptrCast(&dr.buffer[dr.index]));
   3549         const next_index = dr.index +
   3550             if (@hasField(posix.system.dirent, "reclen")) bsd_entry.reclen else bsd_entry.reclen();
   3551         dr.index = next_index;
   3552 
   3553         const name = @as([*]u8, @ptrCast(&bsd_entry.name))[0..bsd_entry.namlen];
   3554 
   3555         const skip_zero_fileno = switch (native_os) {
   3556             // fileno=0 is used to mark invalid entries or deleted files.
   3557             .openbsd, .netbsd => true,
   3558             else => false,
   3559         };
   3560         if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..") or
   3561             (skip_zero_fileno and bsd_entry.fileno == 0))
   3562         {
   3563             continue;
   3564         }
   3565 
   3566         const entry_kind: File.Kind = switch (bsd_entry.type) {
   3567             posix.DT.BLK => .block_device,
   3568             posix.DT.CHR => .character_device,
   3569             posix.DT.DIR => .directory,
   3570             posix.DT.FIFO => .named_pipe,
   3571             posix.DT.LNK => .sym_link,
   3572             posix.DT.REG => .file,
   3573             posix.DT.SOCK => .unix_domain_socket,
   3574             posix.DT.WHT => .whiteout,
   3575             else => .unknown,
   3576         };
   3577         buffer[buffer_index] = .{
   3578             .name = name,
   3579             .kind = entry_kind,
   3580             .inode = bsd_entry.fileno,
   3581         };
   3582         buffer_index += 1;
   3583     }
   3584     return buffer_index;
   3585 }
   3586 
   3587 fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   3588     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3589     const current_thread = Thread.getCurrent(t);
   3590     var buffer_index: usize = 0;
   3591     while (buffer.len - buffer_index != 0) {
   3592         if (dr.end - dr.index == 0) {
   3593             // Refill the buffer, unless we've already created references to
   3594             // buffered data.
   3595             if (buffer_index != 0) break;
   3596             if (dr.state == .reset) {
   3597                 posixSeekTo(current_thread, dr.dir.handle, 0) catch |err| switch (err) {
   3598                     error.Unseekable => return error.Unexpected,
   3599                     else => |e| return e,
   3600                 };
   3601                 dr.state = .reading;
   3602             }
   3603             try current_thread.beginSyscall();
   3604             const n: usize = while (true) {
   3605                 const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
   3606                 switch (posix.errno(rc)) {
   3607                     .SUCCESS => {
   3608                         current_thread.endSyscall();
   3609                         break rc;
   3610                     },
   3611                     .INTR => {
   3612                         try current_thread.checkCancel();
   3613                         continue;
   3614                     },
   3615                     .CANCELED => return current_thread.endSyscallCanceled(),
   3616                     else => |e| {
   3617                         current_thread.endSyscall();
   3618                         switch (e) {
   3619                             .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability
   3620                             .FAULT => |err| return errnoBug(err),
   3621                             .NOTDIR => |err| return errnoBug(err),
   3622                             .INVAL => |err| return errnoBug(err),
   3623                             else => |err| return posix.unexpectedErrno(err),
   3624                         }
   3625                     },
   3626                 }
   3627             };
   3628             if (n == 0) {
   3629                 dr.state = .finished;
   3630                 return 0;
   3631             }
   3632             dr.index = 0;
   3633             dr.end = n;
   3634         }
   3635         const entry = @as(*align(1) posix.system.dirent, @ptrCast(&dr.buffer[dr.index]));
   3636         const next_index = dr.index + entry.reclen;
   3637         dr.index = next_index;
   3638 
   3639         const name = std.mem.sliceTo(@as([*:0]u8, @ptrCast(&entry.name)), 0);
   3640         if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;
   3641 
   3642         // illumos dirent doesn't expose type, so we have to call stat to get it.
   3643         const stat = try posixStatFile(current_thread, dr.dir.handle, name, posix.AT.SYMLINK_NOFOLLOW);
   3644 
   3645         buffer[buffer_index] = .{
   3646             .name = name,
   3647             .kind = stat.kind,
   3648             .inode = entry.ino,
   3649         };
   3650         buffer_index += 1;
   3651     }
   3652     return buffer_index;
   3653 }
   3654 
   3655 fn dirReadHaiku(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   3656     _ = userdata;
   3657     _ = dr;
   3658     _ = buffer;
   3659     @panic("TODO");
   3660 }
   3661 
   3662 fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   3663     _ = userdata;
   3664     _ = dr;
   3665     _ = buffer;
   3666     @panic("TODO");
   3667 }
   3668 
   3669 fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   3670     _ = userdata;
   3671     _ = dr;
   3672     _ = buffer;
   3673     @panic("TODO");
   3674 }
   3675 
   3676 fn dirReadUnimplemented(userdata: ?*anyopaque, dir_reader: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   3677     _ = userdata;
   3678     _ = dir_reader;
   3679     _ = buffer;
   3680     return error.Unimplemented;
   3681 }
   3682 
   3683 const dirRealPath = switch (native_os) {
   3684     .windows => dirRealPathWindows,
   3685     else => dirRealPathPosix,
   3686 };
   3687 
   3688 fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathError!usize {
   3689     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3690     const w = windows;
   3691     const current_thread = Thread.getCurrent(t);
   3692 
   3693     try current_thread.checkCancel();
   3694 
   3695     var path_name_w = try w.sliceToPrefixedFileW(dir.handle, sub_path);
   3696 
   3697     const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
   3698     const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE;
   3699     const creation = w.FILE_OPEN;
   3700     const h_file = blk: {
   3701         const res = w.OpenFile(path_name_w.span(), .{
   3702             .dir = dir.handle,
   3703             .access_mask = access_mask,
   3704             .share_access = share_access,
   3705             .creation = creation,
   3706             .filter = .any,
   3707         }) catch |err| switch (err) {
   3708             error.WouldBlock => unreachable,
   3709             else => |e| return e,
   3710         };
   3711         break :blk res;
   3712     };
   3713     defer w.CloseHandle(h_file);
   3714 
   3715     const wide_slice = w.GetFinalPathNameByHandle(h_file, .{}, out_buffer);
   3716 
   3717     const len = std.unicode.calcWtf8Len(wide_slice);
   3718     if (len > out_buffer.len)
   3719         return error.NameTooLong;
   3720 
   3721     return std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
   3722 }
   3723 
   3724 fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathError!usize {
   3725     if (native_os == .wasi) @compileError("unsupported operating system");
   3726     const max_path_bytes = std.fs.max_path_bytes;
   3727 
   3728     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3729     const current_thread = Thread.getCurrent(t);
   3730 
   3731     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   3732     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   3733 
   3734     var flags: posix.O = .{};
   3735     if (@hasField(posix.O, "NONBLOCK")) flags.NONBLOCK = true;
   3736     if (@hasField(posix.O, "CLOEXEC")) flags.CLOEXEC = true;
   3737     if (@hasField(posix.O, "PATH")) flags.PATH = true;
   3738 
   3739     const mode: posix.mode_t = 0;
   3740 
   3741     try current_thread.beginSyscall();
   3742     const fd: posix.fd_t = while (true) {
   3743         const rc = openat_sym(dir.handle, sub_path_posix, flags, mode);
   3744         switch (posix.errno(rc)) {
   3745             .SUCCESS => {
   3746                 current_thread.endSyscall();
   3747                 break @intCast(rc);
   3748             },
   3749             .INTR => {
   3750                 try current_thread.checkCancel();
   3751                 continue;
   3752             },
   3753             .CANCELED => return current_thread.endSyscallCanceled(),
   3754             else => |e| {
   3755                 current_thread.endSyscall();
   3756                 switch (e) {
   3757                     .FAULT => |err| return errnoBug(err),
   3758                     .INVAL => return error.BadPathName,
   3759                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3760                     .ACCES => return error.AccessDenied,
   3761                     .FBIG => return error.FileTooBig,
   3762                     .OVERFLOW => return error.FileTooBig,
   3763                     .ISDIR => return error.IsDir,
   3764                     .LOOP => return error.SymLinkLoop,
   3765                     .MFILE => return error.ProcessFdQuotaExceeded,
   3766                     .NAMETOOLONG => return error.NameTooLong,
   3767                     .NFILE => return error.SystemFdQuotaExceeded,
   3768                     .NODEV => return error.NoDevice,
   3769                     .NOENT => return error.FileNotFound,
   3770                     .SRCH => return error.FileNotFound, // Linux when accessing procfs.
   3771                     .NOMEM => return error.SystemResources,
   3772                     .NOSPC => return error.NoSpaceLeft,
   3773                     .NOTDIR => return error.NotDir,
   3774                     .PERM => return error.PermissionDenied,
   3775                     .EXIST => return error.PathAlreadyExists,
   3776                     .BUSY => return error.DeviceBusy,
   3777                     .NXIO => return error.NoDevice,
   3778                     .ILSEQ => return error.BadPathName,
   3779                     else => |err| return posix.unexpectedErrno(err),
   3780                 }
   3781             },
   3782         }
   3783     };
   3784     errdefer posix.close(fd);
   3785 
   3786     switch (native_os) {
   3787         .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
   3788             // On macOS, we can use F.GETPATH fcntl command to query the OS for
   3789             // the path to the file descriptor.
   3790             var sufficient_buffer: [posix.PATH_MAX]u8 = undefined;
   3791             @memset(&sufficient_buffer, 0);
   3792             try current_thread.beginSyscall();
   3793             while (true) {
   3794                 switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, &sufficient_buffer))) {
   3795                     .SUCCESS => {
   3796                         current_thread.endSyscall();
   3797                         break;
   3798                     },
   3799                     .INTR => {
   3800                         try current_thread.checkCancel();
   3801                         continue;
   3802                     },
   3803                     .CANCELED => return current_thread.endSyscallCanceled(),
   3804                     else => |e| {
   3805                         current_thread.endSyscall();
   3806                         switch (e) {
   3807                             .BADF => return error.FileNotFound,
   3808                             .NOSPC => return error.NameTooLong,
   3809                             .NOENT => return error.FileNotFound,
   3810                             else => |err| return posix.unexpectedErrno(err),
   3811                         }
   3812                     },
   3813                 }
   3814             }
   3815             const n = std.mem.indexOfScalar(u8, &sufficient_buffer, 0) orelse sufficient_buffer.len;
   3816             if (n > out_buffer.len) return error.NameTooLong;
   3817             @memcpy(out_buffer[0..n], sufficient_buffer[0..n]);
   3818             return n;
   3819         },
   3820         .linux, .serenity, .illumos => {
   3821             var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
   3822             const template = if (native_os == .illumos) "/proc/self/path/{d}" else "/proc/self/fd/{d}";
   3823             const proc_path = std.fmt.bufPrintSentinel(&procfs_buf, template, .{fd}, 0) catch unreachable;
   3824             try current_thread.beginSyscall();
   3825             while (true) {
   3826                 const rc = posix.system.readlink(proc_path, out_buffer.ptr, out_buffer.len);
   3827                 switch (posix.errno(rc)) {
   3828                     .SUCCESS => {
   3829                         current_thread.endSyscall();
   3830                         const len: usize = @bitCast(rc);
   3831                         return len;
   3832                     },
   3833                     .INTR => {
   3834                         try current_thread.checkCancel();
   3835                         continue;
   3836                     },
   3837                     .CANCELED => return current_thread.endSyscallCanceled(),
   3838                     else => |e| {
   3839                         current_thread.endSyscall();
   3840                         switch (e) {
   3841                             .ACCES => return error.AccessDenied,
   3842                             .FAULT => |err| return errnoBug(err),
   3843                             .IO => return error.FileSystem,
   3844                             .LOOP => return error.SymLinkLoop,
   3845                             .NAMETOOLONG => return error.NameTooLong,
   3846                             .NOENT => return error.FileNotFound,
   3847                             .NOMEM => return error.SystemResources,
   3848                             .NOTDIR => return error.NotDir,
   3849                             .ILSEQ => |err| return errnoBug(err),
   3850                             else => |err| return posix.unexpectedErrno(err),
   3851                         }
   3852                     },
   3853                 }
   3854             }
   3855         },
   3856         .freebsd => {
   3857             var kfile: std.c.kinfo_file = undefined;
   3858             kfile.structsize = std.c.KINFO_FILE_SIZE;
   3859             try current_thread.beginSyscall();
   3860             while (true) {
   3861                 switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&kfile)))) {
   3862                     .SUCCESS => {
   3863                         current_thread.endSyscall();
   3864                         break;
   3865                     },
   3866                     .INTR => {
   3867                         try current_thread.checkCancel();
   3868                         continue;
   3869                     },
   3870                     .CANCELED => return current_thread.endSyscallCanceled(),
   3871                     else => |e| {
   3872                         current_thread.endSyscall();
   3873                         switch (e) {
   3874                             .BADF => return error.FileNotFound,
   3875                             else => |err| return posix.unexpectedErrno(err),
   3876                         }
   3877                     },
   3878                 }
   3879             }
   3880             const len = std.mem.indexOfScalar(u8, &kfile.path, 0) orelse kfile.path.len;
   3881             if (len == 0) return error.NameTooLong;
   3882             return len;
   3883         },
   3884         .netbsd, .dragonfly => {
   3885             @memset(out_buffer[0..max_path_bytes], 0);
   3886             try current_thread.beginSyscall();
   3887             while (true) {
   3888                 switch (posix.errno(std.c.fcntl(fd, posix.F.GETPATH, out_buffer))) {
   3889                     .SUCCESS => {
   3890                         current_thread.endSyscall();
   3891                         break;
   3892                     },
   3893                     .INTR => {
   3894                         try current_thread.checkCancel();
   3895                         continue;
   3896                     },
   3897                     .CANCELED => return current_thread.endSyscallCanceled(),
   3898                     else => |e| {
   3899                         current_thread.endSyscall();
   3900                         switch (e) {
   3901                             .ACCES => return error.AccessDenied,
   3902                             .BADF => return error.FileNotFound,
   3903                             .NOENT => return error.FileNotFound,
   3904                             .NOMEM => return error.SystemResources,
   3905                             .RANGE => return error.NameTooLong,
   3906                             else => |err| return posix.unexpectedErrno(err),
   3907                         }
   3908                     },
   3909                 }
   3910             }
   3911             return std.mem.indexOfScalar(u8, &out_buffer, 0) orelse out_buffer.len;
   3912         },
   3913         else => @compileError("unsupported OS"),
   3914     }
   3915     comptime unreachable;
   3916 }
   3917 
   3918 const dirDeleteFile = switch (native_os) {
   3919     .windows => dirDeleteFileWindows,
   3920     .wasi => dirDeleteFileWasi,
   3921     else => dirDeleteFilePosix,
   3922 };
   3923 
   3924 fn dirDeleteFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
   3925     return dirDeleteWindows(userdata, dir, sub_path, false);
   3926 }
   3927 
   3928 fn dirDeleteFileWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
   3929     if (builtin.link_libc) return dirDeleteFilePosix(userdata, dir, sub_path);
   3930     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3931     const current_thread = Thread.getCurrent(t);
   3932     try current_thread.beginSyscall();
   3933     while (true) {
   3934         const res = std.os.wasi.path_unlink_file(dir.handle, sub_path.ptr, sub_path.len);
   3935         switch (res) {
   3936             .SUCCESS => {
   3937                 current_thread.endSyscall();
   3938                 return;
   3939             },
   3940             .INTR => {
   3941                 try current_thread.checkCancel();
   3942                 continue;
   3943             },
   3944             .CANCELED => return current_thread.endSyscallCanceled(),
   3945             else => |e| {
   3946                 current_thread.endSyscall();
   3947                 switch (e) {
   3948                     .ACCES => return error.AccessDenied,
   3949                     .PERM => return error.PermissionDenied,
   3950                     .BUSY => return error.FileBusy,
   3951                     .FAULT => |err| return errnoBug(err),
   3952                     .IO => return error.FileSystem,
   3953                     .ISDIR => return error.IsDir,
   3954                     .LOOP => return error.SymLinkLoop,
   3955                     .NAMETOOLONG => return error.NameTooLong,
   3956                     .NOENT => return error.FileNotFound,
   3957                     .NOTDIR => return error.NotDir,
   3958                     .NOMEM => return error.SystemResources,
   3959                     .ROFS => return error.ReadOnlyFileSystem,
   3960                     .NOTEMPTY => return error.DirNotEmpty,
   3961                     .NOTCAPABLE => return error.AccessDenied,
   3962                     .ILSEQ => return error.BadPathName,
   3963                     .INVAL => |err| return errnoBug(err), // invalid flags, or pathname has . as last component
   3964                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3965                     else => |err| return posix.unexpectedErrno(err),
   3966                 }
   3967             },
   3968         }
   3969     }
   3970 }
   3971 
   3972 fn dirDeleteFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
   3973     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3974     const current_thread = Thread.getCurrent(t);
   3975 
   3976     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   3977     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   3978 
   3979     try current_thread.beginSyscall();
   3980     while (true) {
   3981         switch (posix.errno(posix.system.unlinkat(dir.handle, sub_path_posix, 0))) {
   3982             .SUCCESS => {
   3983                 current_thread.endSyscall();
   3984                 return;
   3985             },
   3986             .INTR => {
   3987                 try current_thread.checkCancel();
   3988                 continue;
   3989             },
   3990             .CANCELED => return current_thread.endSyscallCanceled(),
   3991             // Some systems return permission errors when trying to delete a
   3992             // directory, so we need to handle that case specifically and
   3993             // translate the error.
   3994             .PERM => switch (native_os) {
   3995                 .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .netbsd, .dragonfly, .openbsd, .illumos => {
   3996 
   3997                     // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them).
   3998                     var st = std.mem.zeroes(posix.Stat);
   3999                     while (true) {
   4000                         try current_thread.checkCancel();
   4001                         switch (posix.errno(fstatat_sym(dir.handle, sub_path_posix, &st, posix.AT.SYMLINK_NOFOLLOW))) {
   4002                             .SUCCESS => {
   4003                                 current_thread.endSyscall();
   4004                                 break;
   4005                             },
   4006                             .INTR => continue,
   4007                             .CANCELED => return current_thread.endSyscallCanceled(),
   4008                             else => {
   4009                                 current_thread.endSyscall();
   4010                                 return error.PermissionDenied;
   4011                             },
   4012                         }
   4013                     }
   4014                     const is_dir = st.mode & posix.S.IFMT == posix.S.IFDIR;
   4015                     if (is_dir)
   4016                         return error.IsDir
   4017                     else
   4018                         return error.PermissionDenied;
   4019                 },
   4020                 else => {
   4021                     current_thread.endSyscall();
   4022                     return error.PermissionDenied;
   4023                 },
   4024             },
   4025             else => |e| {
   4026                 current_thread.endSyscall();
   4027                 switch (e) {
   4028                     .ACCES => return error.AccessDenied,
   4029                     .BUSY => return error.FileBusy,
   4030                     .FAULT => |err| return errnoBug(err),
   4031                     .IO => return error.FileSystem,
   4032                     .ISDIR => return error.IsDir,
   4033                     .LOOP => return error.SymLinkLoop,
   4034                     .NAMETOOLONG => return error.NameTooLong,
   4035                     .NOENT => return error.FileNotFound,
   4036                     .NOTDIR => return error.NotDir,
   4037                     .NOMEM => return error.SystemResources,
   4038                     .ROFS => return error.ReadOnlyFileSystem,
   4039                     .EXIST => |err| return errnoBug(err),
   4040                     .NOTEMPTY => |err| return errnoBug(err), // Not passing AT.REMOVEDIR
   4041                     .ILSEQ => return error.BadPathName,
   4042                     .INVAL => |err| return errnoBug(err), // invalid flags, or pathname has . as last component
   4043                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   4044                     else => |err| return posix.unexpectedErrno(err),
   4045                 }
   4046             },
   4047         }
   4048     }
   4049 }
   4050 
   4051 const dirDeleteDir = switch (native_os) {
   4052     .windows => dirDeleteDirWindows,
   4053     .wasi => dirDeleteDirWasi,
   4054     else => dirDeleteDirPosix,
   4055 };
   4056 
   4057 fn dirDeleteDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
   4058     return dirDeleteWindows(userdata, dir, sub_path, true);
   4059 }
   4060 
   4061 fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) Dir.DeleteFileError!void {
   4062     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4063     const current_thread = Thread.getCurrent(t);
   4064     const w = windows;
   4065 
   4066     try current_thread.checkCancel();
   4067 
   4068     const sub_path_w = try w.sliceToPrefixedFileW(dir.handle, sub_path);
   4069 
   4070     const path_len_bytes = @as(u16, @intCast(sub_path_w.len * 2));
   4071     var nt_name: w.UNICODE_STRING = .{
   4072         .Length = path_len_bytes,
   4073         .MaximumLength = path_len_bytes,
   4074         // The Windows API makes this mutable, but it will not mutate here.
   4075         .Buffer = @constCast(sub_path_w.ptr),
   4076     };
   4077 
   4078     if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
   4079         // Windows does not recognize this, but it does work with empty string.
   4080         nt_name.Length = 0;
   4081     }
   4082     if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
   4083         // Can't remove the parent directory with an open handle.
   4084         return error.FileBusy;
   4085     }
   4086 
   4087     const create_options_flags: w.ULONG = if (remove_dir)
   4088         w.FILE_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT
   4089     else
   4090         w.FILE_NON_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT;
   4091 
   4092     var attr: w.OBJECT_ATTRIBUTES = .{
   4093         .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
   4094         .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
   4095         .Attributes = w.OBJ_CASE_INSENSITIVE,
   4096         .ObjectName = &nt_name,
   4097         .SecurityDescriptor = null,
   4098         .SecurityQualityOfService = null,
   4099     };
   4100     var io_status_block: w.IO_STATUS_BLOCK = undefined;
   4101     var tmp_handle: w.HANDLE = undefined;
   4102     var rc = w.ntdll.NtCreateFile(
   4103         &tmp_handle,
   4104         w.SYNCHRONIZE | w.DELETE,
   4105         &attr,
   4106         &io_status_block,
   4107         null,
   4108         0,
   4109         w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE,
   4110         w.FILE_OPEN,
   4111         create_options_flags,
   4112         null,
   4113         0,
   4114     );
   4115     switch (rc) {
   4116         .SUCCESS => {},
   4117         .OBJECT_NAME_INVALID => |err| return w.statusBug(err),
   4118         .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   4119         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   4120         .BAD_NETWORK_PATH => return error.NetworkNotFound, // \\server was not found
   4121         .BAD_NETWORK_NAME => return error.NetworkNotFound, // \\server was found but \\server\share wasn't
   4122         .INVALID_PARAMETER => |err| return w.statusBug(err),
   4123         .FILE_IS_A_DIRECTORY => return error.IsDir,
   4124         .NOT_A_DIRECTORY => return error.NotDir,
   4125         .SHARING_VIOLATION => return error.FileBusy,
   4126         .ACCESS_DENIED => return error.AccessDenied,
   4127         .DELETE_PENDING => return,
   4128         else => return w.unexpectedStatus(rc),
   4129     }
   4130     defer w.CloseHandle(tmp_handle);
   4131 
   4132     // FileDispositionInformationEx has varying levels of support:
   4133     // - FILE_DISPOSITION_INFORMATION_EX requires >= win10_rs1
   4134     //   (INVALID_INFO_CLASS is returned if not supported)
   4135     // - Requires the NTFS filesystem
   4136     //   (on filesystems like FAT32, INVALID_PARAMETER is returned)
   4137     // - FILE_DISPOSITION_POSIX_SEMANTICS requires >= win10_rs1
   4138     // - FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
   4139     //   (NOT_SUPPORTED is returned if a flag is unsupported)
   4140     //
   4141     // The strategy here is just to try using FileDispositionInformationEx and fall back to
   4142     // FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
   4143     const need_fallback = need_fallback: {
   4144         try current_thread.checkCancel();
   4145 
   4146         // Deletion with posix semantics if the filesystem supports it.
   4147         var info: w.FILE_DISPOSITION_INFORMATION_EX = .{
   4148             .Flags = w.FILE_DISPOSITION_DELETE |
   4149                 w.FILE_DISPOSITION_POSIX_SEMANTICS |
   4150                 w.FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE,
   4151         };
   4152 
   4153         rc = w.ntdll.NtSetInformationFile(
   4154             tmp_handle,
   4155             &io_status_block,
   4156             &info,
   4157             @sizeOf(w.FILE_DISPOSITION_INFORMATION_EX),
   4158             .FileDispositionInformationEx,
   4159         );
   4160         switch (rc) {
   4161             .SUCCESS => return,
   4162             // The filesystem does not support FileDispositionInformationEx
   4163             .INVALID_PARAMETER,
   4164             // The operating system does not support FileDispositionInformationEx
   4165             .INVALID_INFO_CLASS,
   4166             // The operating system does not support one of the flags
   4167             .NOT_SUPPORTED,
   4168             => break :need_fallback true,
   4169             // For all other statuses, fall down to the switch below to handle them.
   4170             else => break :need_fallback false,
   4171         }
   4172     };
   4173 
   4174     if (need_fallback) {
   4175         try current_thread.checkCancel();
   4176 
   4177         // Deletion with file pending semantics, which requires waiting or moving
   4178         // files to get them removed (from here).
   4179         var file_dispo: w.FILE_DISPOSITION_INFORMATION = .{
   4180             .DeleteFile = w.TRUE,
   4181         };
   4182 
   4183         rc = w.ntdll.NtSetInformationFile(
   4184             tmp_handle,
   4185             &io_status_block,
   4186             &file_dispo,
   4187             @sizeOf(w.FILE_DISPOSITION_INFORMATION),
   4188             .FileDispositionInformation,
   4189         );
   4190     }
   4191     switch (rc) {
   4192         .SUCCESS => {},
   4193         .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
   4194         .INVALID_PARAMETER => |err| return w.statusBug(err),
   4195         .CANNOT_DELETE => return error.AccessDenied,
   4196         .MEDIA_WRITE_PROTECTED => return error.AccessDenied,
   4197         .ACCESS_DENIED => return error.AccessDenied,
   4198         else => return w.unexpectedStatus(rc),
   4199     }
   4200 }
   4201 
   4202 fn dirDeleteDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
   4203     if (builtin.link_libc) return dirDeleteDirPosix(userdata, dir, sub_path);
   4204 
   4205     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4206     const current_thread = Thread.getCurrent(t);
   4207 
   4208     try current_thread.beginSyscall();
   4209     while (true) {
   4210         const res = std.os.wasi.path_remove_directory(dir.handle, sub_path.ptr, sub_path.len);
   4211         switch (res) {
   4212             .SUCCESS => {
   4213                 current_thread.endSyscall();
   4214                 return;
   4215             },
   4216             .INTR => {
   4217                 try current_thread.checkCancel();
   4218                 continue;
   4219             },
   4220             .CANCELED => return current_thread.endSyscallCanceled(),
   4221             else => |e| {
   4222                 current_thread.endSyscall();
   4223                 switch (e) {
   4224                     .ACCES => return error.AccessDenied,
   4225                     .PERM => return error.PermissionDenied,
   4226                     .BUSY => return error.FileBusy,
   4227                     .FAULT => |err| return errnoBug(err),
   4228                     .IO => return error.FileSystem,
   4229                     .ISDIR => return error.IsDir,
   4230                     .LOOP => return error.SymLinkLoop,
   4231                     .NAMETOOLONG => return error.NameTooLong,
   4232                     .NOENT => return error.FileNotFound,
   4233                     .NOTDIR => return error.NotDir,
   4234                     .NOMEM => return error.SystemResources,
   4235                     .ROFS => return error.ReadOnlyFileSystem,
   4236                     .NOTEMPTY => return error.DirNotEmpty,
   4237                     .NOTCAPABLE => return error.AccessDenied,
   4238                     .ILSEQ => return error.BadPathName,
   4239                     .INVAL => |err| return errnoBug(err), // invalid flags, or pathname has . as last component
   4240                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   4241                     else => |err| return posix.unexpectedErrno(err),
   4242                 }
   4243             },
   4244         }
   4245     }
   4246 }
   4247 
   4248 fn dirDeleteDirPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
   4249     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4250     const current_thread = Thread.getCurrent(t);
   4251 
   4252     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   4253     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   4254 
   4255     try current_thread.beginSyscall();
   4256     while (true) {
   4257         switch (posix.errno(posix.system.unlinkat(dir.handle, sub_path_posix, posix.AT.REMOVEDIR))) {
   4258             .SUCCESS => {
   4259                 current_thread.endSyscall();
   4260                 return;
   4261             },
   4262             .INTR => {
   4263                 try current_thread.checkCancel();
   4264                 continue;
   4265             },
   4266             .CANCELED => return current_thread.endSyscallCanceled(),
   4267             else => |e| {
   4268                 current_thread.endSyscall();
   4269                 switch (e) {
   4270                     .ACCES => return error.AccessDenied,
   4271                     .PERM => return error.PermissionDenied,
   4272                     .BUSY => return error.FileBusy,
   4273                     .FAULT => |err| return errnoBug(err),
   4274                     .IO => return error.FileSystem,
   4275                     .ISDIR => |err| return errnoBug(err),
   4276                     .LOOP => return error.SymLinkLoop,
   4277                     .NAMETOOLONG => return error.NameTooLong,
   4278                     .NOENT => return error.FileNotFound,
   4279                     .NOTDIR => return error.NotDir,
   4280                     .NOMEM => return error.SystemResources,
   4281                     .ROFS => return error.ReadOnlyFileSystem,
   4282                     .EXIST => |err| return errnoBug(err),
   4283                     .NOTEMPTY => |err| return errnoBug(err), // Not passing AT.REMOVEDIR
   4284                     .ILSEQ => return error.BadPathName,
   4285                     .INVAL => |err| return errnoBug(err), // invalid flags, or pathname has . as last component
   4286                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   4287                     else => |err| return posix.unexpectedErrno(err),
   4288                 }
   4289             },
   4290         }
   4291     }
   4292 }
   4293 
   4294 const dirRename = switch (native_os) {
   4295     .windows => dirRenameWindows,
   4296     .wasi => dirRenameWasi,
   4297     else => dirRenamePosix,
   4298 };
   4299 
   4300 fn dirRenameWindows(
   4301     userdata: ?*anyopaque,
   4302     old_dir: Dir,
   4303     old_sub_path: []const u8,
   4304     new_dir: Dir,
   4305     new_sub_path: []const u8,
   4306 ) Dir.RenameError!void {
   4307     const w = windows;
   4308     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4309     const current_thread = Thread.getCurrent(t);
   4310 
   4311     const old_path_w = try windows.sliceToPrefixedFileW(old_dir.handle, old_sub_path);
   4312     const new_path_w = try windows.sliceToPrefixedFileW(new_dir.handle, new_sub_path);
   4313     const replace_if_exists = true;
   4314 
   4315     try current_thread.checkCancel();
   4316 
   4317     const src_fd = w.OpenFile(old_path_w, .{
   4318         .dir = old_dir.handle,
   4319         .access_mask = w.SYNCHRONIZE | w.GENERIC_WRITE | w.DELETE,
   4320         .creation = w.FILE_OPEN,
   4321         .filter = .any, // This function is supposed to rename both files and directories.
   4322         .follow_symlinks = false,
   4323     }) catch |err| switch (err) {
   4324         error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
   4325         else => |e| return e,
   4326     };
   4327     defer w.CloseHandle(src_fd);
   4328 
   4329     var rc: w.NTSTATUS = undefined;
   4330     // FileRenameInformationEx has varying levels of support:
   4331     // - FILE_RENAME_INFORMATION_EX requires >= win10_rs1
   4332     //   (INVALID_INFO_CLASS is returned if not supported)
   4333     // - Requires the NTFS filesystem
   4334     //   (on filesystems like FAT32, INVALID_PARAMETER is returned)
   4335     // - FILE_RENAME_POSIX_SEMANTICS requires >= win10_rs1
   4336     // - FILE_RENAME_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
   4337     //   (NOT_SUPPORTED is returned if a flag is unsupported)
   4338     //
   4339     // The strategy here is just to try using FileRenameInformationEx and fall back to
   4340     // FileRenameInformation if the return value lets us know that some aspect of it is not supported.
   4341     const need_fallback = need_fallback: {
   4342         const struct_buf_len = @sizeOf(w.FILE_RENAME_INFORMATION_EX) + (w.PATH_MAX_WIDE * 2);
   4343         var rename_info_buf: [struct_buf_len]u8 align(@alignOf(w.FILE_RENAME_INFORMATION_EX)) = undefined;
   4344         const struct_len = @sizeOf(w.FILE_RENAME_INFORMATION_EX) + new_path_w.len * 2;
   4345         if (struct_len > struct_buf_len) return error.NameTooLong;
   4346 
   4347         const rename_info: *w.FILE_RENAME_INFORMATION_EX = @ptrCast(&rename_info_buf);
   4348         var io_status_block: w.IO_STATUS_BLOCK = undefined;
   4349 
   4350         var flags: w.ULONG = w.FILE_RENAME_POSIX_SEMANTICS | w.FILE_RENAME_IGNORE_READONLY_ATTRIBUTE;
   4351         if (replace_if_exists) flags |= w.FILE_RENAME_REPLACE_IF_EXISTS;
   4352         rename_info.* = .{
   4353             .Flags = flags,
   4354             .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
   4355             .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
   4356             .FileName = undefined,
   4357         };
   4358         @memcpy((&rename_info.FileName).ptr, new_path_w);
   4359         rc = w.ntdll.NtSetInformationFile(
   4360             src_fd,
   4361             &io_status_block,
   4362             rename_info,
   4363             @intCast(struct_len), // already checked for error.NameTooLong
   4364             .FileRenameInformationEx,
   4365         );
   4366         switch (rc) {
   4367             .SUCCESS => return,
   4368             // The filesystem does not support FileDispositionInformationEx
   4369             .INVALID_PARAMETER,
   4370             // The operating system does not support FileDispositionInformationEx
   4371             .INVALID_INFO_CLASS,
   4372             // The operating system does not support one of the flags
   4373             .NOT_SUPPORTED,
   4374             => break :need_fallback true,
   4375             // For all other statuses, fall down to the switch below to handle them.
   4376             else => break :need_fallback false,
   4377         }
   4378     };
   4379 
   4380     if (need_fallback) {
   4381         const struct_buf_len = @sizeOf(w.FILE_RENAME_INFORMATION) + (w.PATH_MAX_WIDE * 2);
   4382         var rename_info_buf: [struct_buf_len]u8 align(@alignOf(w.FILE_RENAME_INFORMATION)) = undefined;
   4383         const struct_len = @sizeOf(w.FILE_RENAME_INFORMATION) + new_path_w.len * 2;
   4384         if (struct_len > struct_buf_len) return error.NameTooLong;
   4385 
   4386         const rename_info: *w.FILE_RENAME_INFORMATION = @ptrCast(&rename_info_buf);
   4387         var io_status_block: w.IO_STATUS_BLOCK = undefined;
   4388 
   4389         rename_info.* = .{
   4390             .Flags = @intFromBool(replace_if_exists),
   4391             .RootDirectory = if (std.fs.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
   4392             .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
   4393             .FileName = undefined,
   4394         };
   4395         @memcpy((&rename_info.FileName).ptr, new_path_w);
   4396 
   4397         rc = w.ntdll.NtSetInformationFile(
   4398             src_fd,
   4399             &io_status_block,
   4400             rename_info,
   4401             @intCast(struct_len), // already checked for error.NameTooLong
   4402             .FileRenameInformation,
   4403         );
   4404     }
   4405 
   4406     switch (rc) {
   4407         .SUCCESS => {},
   4408         .INVALID_HANDLE => |err| return w.statusBug(err),
   4409         .INVALID_PARAMETER => |err| return w.statusBug(err),
   4410         .OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err),
   4411         .ACCESS_DENIED => return error.AccessDenied,
   4412         .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   4413         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   4414         .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,
   4415         .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
   4416         .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,
   4417         .FILE_IS_A_DIRECTORY => return error.IsDir,
   4418         .NOT_A_DIRECTORY => return error.NotDir,
   4419         else => return w.unexpectedStatus(rc),
   4420     }
   4421 }
   4422 
   4423 fn dirRenameWasi(
   4424     userdata: ?*anyopaque,
   4425     old_dir: Dir,
   4426     old_sub_path: []const u8,
   4427     new_dir: Dir,
   4428     new_sub_path: []const u8,
   4429 ) Dir.RenameError!void {
   4430     if (builtin.link_libc) return dirRenamePosix(userdata, old_dir, old_sub_path, new_dir, new_sub_path);
   4431 
   4432     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4433     const current_thread = Thread.getCurrent(t);
   4434 
   4435     try current_thread.beginSyscall();
   4436     while (true) {
   4437         switch (std.os.wasi.path_rename(old_dir.handle, old_sub_path.ptr, old_sub_path.len, new_dir.handle, new_sub_path.ptr, new_sub_path.len)) {
   4438             .SUCCESS => return current_thread.endSyscall(),
   4439             .CANCELED => return current_thread.endSyscallCanceled(),
   4440             .INTR => {
   4441                 try current_thread.checkCancel();
   4442                 continue;
   4443             },
   4444             else => |e| {
   4445                 current_thread.endSyscall();
   4446                 switch (e) {
   4447                     .ACCES => return error.AccessDenied,
   4448                     .PERM => return error.PermissionDenied,
   4449                     .BUSY => return error.FileBusy,
   4450                     .DQUOT => return error.DiskQuota,
   4451                     .FAULT => |err| return errnoBug(err),
   4452                     .INVAL => |err| return errnoBug(err),
   4453                     .ISDIR => return error.IsDir,
   4454                     .LOOP => return error.SymLinkLoop,
   4455                     .MLINK => return error.LinkQuotaExceeded,
   4456                     .NAMETOOLONG => return error.NameTooLong,
   4457                     .NOENT => return error.FileNotFound,
   4458                     .NOTDIR => return error.NotDir,
   4459                     .NOMEM => return error.SystemResources,
   4460                     .NOSPC => return error.NoSpaceLeft,
   4461                     .EXIST => return error.PathAlreadyExists,
   4462                     .NOTEMPTY => return error.PathAlreadyExists,
   4463                     .ROFS => return error.ReadOnlyFileSystem,
   4464                     .XDEV => return error.RenameAcrossMountPoints,
   4465                     .NOTCAPABLE => return error.AccessDenied,
   4466                     .ILSEQ => return error.BadPathName,
   4467                     else => |err| return posix.unexpectedErrno(err),
   4468                 }
   4469             },
   4470         }
   4471     }
   4472 }
   4473 
   4474 fn dirRenamePosix(
   4475     userdata: ?*anyopaque,
   4476     old_dir: Dir,
   4477     old_sub_path: []const u8,
   4478     new_dir: Dir,
   4479     new_sub_path: []const u8,
   4480 ) Dir.RenameError!void {
   4481     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4482     const current_thread = Thread.getCurrent(t);
   4483 
   4484     var old_path_buffer: [posix.PATH_MAX]u8 = undefined;
   4485     var new_path_buffer: [posix.PATH_MAX]u8 = undefined;
   4486 
   4487     const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);
   4488     const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
   4489 
   4490     try current_thread.beginSyscall();
   4491     while (true) {
   4492         switch (posix.errno(posix.system.renameat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix))) {
   4493             .SUCCESS => return current_thread.endSyscall(),
   4494             .CANCELED => return current_thread.endSyscallCanceled(),
   4495             .INTR => {
   4496                 try current_thread.checkCancel();
   4497                 continue;
   4498             },
   4499             else => |e| {
   4500                 current_thread.endSyscall();
   4501                 switch (e) {
   4502                     .ACCES => return error.AccessDenied,
   4503                     .PERM => return error.PermissionDenied,
   4504                     .BUSY => return error.FileBusy,
   4505                     .DQUOT => return error.DiskQuota,
   4506                     .FAULT => |err| return errnoBug(err),
   4507                     .INVAL => |err| return errnoBug(err),
   4508                     .ISDIR => return error.IsDir,
   4509                     .LOOP => return error.SymLinkLoop,
   4510                     .MLINK => return error.LinkQuotaExceeded,
   4511                     .NAMETOOLONG => return error.NameTooLong,
   4512                     .NOENT => return error.FileNotFound,
   4513                     .NOTDIR => return error.NotDir,
   4514                     .NOMEM => return error.SystemResources,
   4515                     .NOSPC => return error.NoSpaceLeft,
   4516                     .EXIST => return error.PathAlreadyExists,
   4517                     .NOTEMPTY => return error.PathAlreadyExists,
   4518                     .ROFS => return error.ReadOnlyFileSystem,
   4519                     .XDEV => return error.RenameAcrossMountPoints,
   4520                     .ILSEQ => return error.BadPathName,
   4521                     else => |err| return posix.unexpectedErrno(err),
   4522                 }
   4523             },
   4524         }
   4525     }
   4526 }
   4527 
   4528 const dirSymLink = switch (native_os) {
   4529     .windows => dirSymLinkWindows,
   4530     .wasi => dirSymLinkWasi,
   4531     else => dirSymLinkPosix,
   4532 };
   4533 
   4534 fn dirSymLinkWindows(
   4535     userdata: ?*anyopaque,
   4536     dir: Dir,
   4537     target_path: []const u8,
   4538     sym_link_path: []const u8,
   4539     flags: Dir.SymLinkFlags,
   4540 ) Dir.SymLinkError!void {
   4541     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4542     const current_thread = Thread.getCurrent(t);
   4543     const w = windows;
   4544 
   4545     try current_thread.checkCancel();
   4546 
   4547     // Target path does not use sliceToPrefixedFileW because certain paths
   4548     // are handled differently when creating a symlink than they would be
   4549     // when converting to an NT namespaced path. CreateSymbolicLink in
   4550     // symLinkW will handle the necessary conversion.
   4551     var target_path_w: w.PathSpace = undefined;
   4552     target_path_w.len = try w.wtf8ToWtf16Le(&target_path_w.data, target_path);
   4553     target_path_w.data[target_path_w.len] = 0;
   4554     // However, we need to canonicalize any path separators to `\`, since if
   4555     // the target path is relative, then it must use `\` as the path separator.
   4556     std.mem.replaceScalar(
   4557         u16,
   4558         target_path_w.data[0..target_path_w.len],
   4559         std.mem.nativeToLittle(u16, '/'),
   4560         std.mem.nativeToLittle(u16, '\\'),
   4561     );
   4562 
   4563     const sym_link_path_w = try w.sliceToPrefixedFileW(dir.handle, sym_link_path);
   4564 
   4565     const SYMLINK_DATA = extern struct {
   4566         ReparseTag: w.ULONG,
   4567         ReparseDataLength: w.USHORT,
   4568         Reserved: w.USHORT,
   4569         SubstituteNameOffset: w.USHORT,
   4570         SubstituteNameLength: w.USHORT,
   4571         PrintNameOffset: w.USHORT,
   4572         PrintNameLength: w.USHORT,
   4573         Flags: w.ULONG,
   4574     };
   4575 
   4576     const symlink_handle = w.OpenFile(sym_link_path_w.span(), .{
   4577         .access_mask = w.SYNCHRONIZE | w.GENERIC_READ | w.GENERIC_WRITE,
   4578         .dir = dir,
   4579         .creation = w.FILE_CREATE,
   4580         .filter = if (flags.is_directory) .dir_only else .file_only,
   4581     }) catch |err| switch (err) {
   4582         error.IsDir => return error.PathAlreadyExists,
   4583         error.NotDir => return error.Unexpected,
   4584         error.WouldBlock => return error.Unexpected,
   4585         error.PipeBusy => return error.Unexpected,
   4586         error.NoDevice => return error.Unexpected,
   4587         error.AntivirusInterference => return error.Unexpected,
   4588         else => |e| return e,
   4589     };
   4590     defer w.CloseHandle(symlink_handle);
   4591 
   4592     // Relevant portions of the documentation:
   4593     // > Relative links are specified using the following conventions:
   4594     // > - Root relative—for example, "\Windows\System32" resolves to "current drive:\Windows\System32".
   4595     // > - Current working directory–relative—for example, if the current working directory is
   4596     // >   C:\Windows\System32, "C:File.txt" resolves to "C:\Windows\System32\File.txt".
   4597     // > Note: If you specify a current working directory–relative link, it is created as an absolute
   4598     // > link, due to the way the current working directory is processed based on the user and the thread.
   4599     // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
   4600     var is_target_absolute = false;
   4601     const final_target_path = target_path: {
   4602         if (w.hasCommonNtPrefix(u16, target_path)) {
   4603             // Already an NT path, no need to do anything to it
   4604             break :target_path target_path;
   4605         } else {
   4606             switch (w.getWin32PathType(u16, target_path)) {
   4607                 // Rooted paths need to avoid getting put through wToPrefixedFileW
   4608                 // (and they are treated as relative in this context)
   4609                 // Note: It seems that rooted paths in symbolic links are relative to
   4610                 //       the drive that the symbolic exists on, not to the CWD's drive.
   4611                 //       So, if the symlink is on C:\ and the CWD is on D:\,
   4612                 //       it will still resolve the path relative to the root of
   4613                 //       the C:\ drive.
   4614                 .rooted => break :target_path target_path,
   4615                 // Keep relative paths relative, but anything else needs to get NT-prefixed.
   4616                 else => if (!std.fs.path.isAbsoluteWindowsWtf16(target_path))
   4617                     break :target_path target_path,
   4618             }
   4619         }
   4620         var prefixed_target_path = try w.wToPrefixedFileW(dir, target_path);
   4621         // We do this after prefixing to ensure that drive-relative paths are treated as absolute
   4622         is_target_absolute = std.fs.path.isAbsoluteWindowsWtf16(prefixed_target_path.span());
   4623         break :target_path prefixed_target_path.span();
   4624     };
   4625 
   4626     // prepare reparse data buffer
   4627     var buffer: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
   4628     const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4;
   4629     const header_len = @sizeOf(w.ULONG) + @sizeOf(w.USHORT) * 2;
   4630     const target_is_absolute = std.fs.path.isAbsoluteWindowsWtf16(final_target_path);
   4631     const symlink_data = SYMLINK_DATA{
   4632         .ReparseTag = w.IO_REPARSE_TAG_SYMLINK,
   4633         .ReparseDataLength = @intCast(buf_len - header_len),
   4634         .Reserved = 0,
   4635         .SubstituteNameOffset = @intCast(final_target_path.len * 2),
   4636         .SubstituteNameLength = @intCast(final_target_path.len * 2),
   4637         .PrintNameOffset = 0,
   4638         .PrintNameLength = @intCast(final_target_path.len * 2),
   4639         .Flags = if (!target_is_absolute) w.SYMLINK_FLAG_RELATIVE else 0,
   4640     };
   4641 
   4642     @memcpy(buffer[0..@sizeOf(SYMLINK_DATA)], std.mem.asBytes(&symlink_data));
   4643     @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
   4644     const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
   4645     @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
   4646     _ = try w.DeviceIoControl(symlink_handle, w.FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
   4647 }
   4648 
   4649 fn dirSymLinkWasi(
   4650     userdata: ?*anyopaque,
   4651     dir: Dir,
   4652     target_path: []const u8,
   4653     sym_link_path: []const u8,
   4654     flags: Dir.SymLinkFlags,
   4655 ) Dir.SymLinkError!void {
   4656     if (builtin.link_libc) return dirSymLinkPosix(dir, target_path, sym_link_path, flags);
   4657 
   4658     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4659     const current_thread = Thread.getCurrent(t);
   4660 
   4661     try current_thread.beginSyscall();
   4662     while (true) {
   4663         switch (std.os.wasi.path_symlink(target_path.ptr, target_path.len, dir.handle, sym_link_path.ptr, sym_link_path.len)) {
   4664             .SUCCESS => return current_thread.endSyscall(),
   4665             .CANCELED => return current_thread.endSyscallCanceled(),
   4666             .INTR => {
   4667                 try current_thread.checkCancel();
   4668                 continue;
   4669             },
   4670             else => |e| {
   4671                 current_thread.endSyscall();
   4672                 switch (e) {
   4673                     .FAULT => |err| return errnoBug(err),
   4674                     .INVAL => |err| return errnoBug(err),
   4675                     .BADF => |err| return errnoBug(err),
   4676                     .ACCES => return error.AccessDenied,
   4677                     .PERM => return error.PermissionDenied,
   4678                     .DQUOT => return error.DiskQuota,
   4679                     .EXIST => return error.PathAlreadyExists,
   4680                     .IO => return error.FileSystem,
   4681                     .LOOP => return error.SymLinkLoop,
   4682                     .NAMETOOLONG => return error.NameTooLong,
   4683                     .NOENT => return error.FileNotFound,
   4684                     .NOTDIR => return error.NotDir,
   4685                     .NOMEM => return error.SystemResources,
   4686                     .NOSPC => return error.NoSpaceLeft,
   4687                     .ROFS => return error.ReadOnlyFileSystem,
   4688                     .NOTCAPABLE => return error.AccessDenied,
   4689                     .ILSEQ => return error.BadPathName,
   4690                     else => |err| return posix.unexpectedErrno(err),
   4691                 }
   4692             },
   4693         }
   4694     }
   4695 }
   4696 
   4697 fn dirSymLinkPosix(
   4698     userdata: ?*anyopaque,
   4699     dir: Dir,
   4700     target_path: []const u8,
   4701     sym_link_path: []const u8,
   4702     flags: Dir.SymLinkFlags,
   4703 ) Dir.SymLinkError!void {
   4704     _ = flags;
   4705     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4706     const current_thread = Thread.getCurrent(t);
   4707 
   4708     var target_path_buffer: [posix.PATH_MAX]u8 = undefined;
   4709     var sym_link_path_buffer: [posix.PATH_MAX]u8 = undefined;
   4710 
   4711     const target_path_posix = try pathToPosix(target_path, &target_path_buffer);
   4712     const sym_link_path_posix = try pathToPosix(sym_link_path, &sym_link_path_buffer);
   4713 
   4714     try current_thread.beginSyscall();
   4715     while (true) {
   4716         switch (posix.errno(posix.system.symlinkat(target_path_posix, dir.handle, sym_link_path_posix))) {
   4717             .SUCCESS => return current_thread.endSyscall(),
   4718             .CANCELED => return current_thread.endSyscallCanceled(),
   4719             .INTR => {
   4720                 try current_thread.checkCancel();
   4721                 continue;
   4722             },
   4723             else => |e| {
   4724                 current_thread.endSyscall();
   4725                 switch (e) {
   4726                     .FAULT => |err| return errnoBug(err),
   4727                     .INVAL => |err| return errnoBug(err),
   4728                     .ACCES => return error.AccessDenied,
   4729                     .PERM => return error.PermissionDenied,
   4730                     .DQUOT => return error.DiskQuota,
   4731                     .EXIST => return error.PathAlreadyExists,
   4732                     .IO => return error.FileSystem,
   4733                     .LOOP => return error.SymLinkLoop,
   4734                     .NAMETOOLONG => return error.NameTooLong,
   4735                     .NOENT => return error.FileNotFound,
   4736                     .NOTDIR => return error.NotDir,
   4737                     .NOMEM => return error.SystemResources,
   4738                     .NOSPC => return error.NoSpaceLeft,
   4739                     .ROFS => return error.ReadOnlyFileSystem,
   4740                     .ILSEQ => return error.BadPathName,
   4741                     else => |err| return posix.unexpectedErrno(err),
   4742                 }
   4743             },
   4744         }
   4745     }
   4746 }
   4747 
   4748 const dirReadLink = switch (native_os) {
   4749     .windows => dirReadLinkWindows,
   4750     .wasi => dirReadLinkWasi,
   4751     else => dirReadLinkPosix,
   4752 };
   4753 
   4754 fn dirReadLinkWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
   4755     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4756     const current_thread = Thread.getCurrent(t);
   4757     const w = windows;
   4758 
   4759     try current_thread.checkCancel();
   4760 
   4761     var sub_path_w = try windows.sliceToPrefixedFileW(dir.handle, sub_path);
   4762 
   4763     const result_handle = w.OpenFile(sub_path_w.span(), .{
   4764         .access_mask = w.FILE_READ_ATTRIBUTES | w.SYNCHRONIZE,
   4765         .dir = dir,
   4766         .creation = w.FILE_OPEN,
   4767         .follow_symlinks = false,
   4768         .filter = .any,
   4769     }) catch |err| switch (err) {
   4770         error.IsDir, error.NotDir => return error.Unexpected, // filter = .any
   4771         error.PathAlreadyExists => return error.Unexpected, // FILE_OPEN
   4772         error.WouldBlock => return error.Unexpected,
   4773         error.NoDevice => return error.FileNotFound,
   4774         error.PipeBusy => return error.AccessDenied,
   4775         else => |e| return e,
   4776     };
   4777     defer w.CloseHandle(result_handle);
   4778 
   4779     var reparse_buf: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(w.REPARSE_DATA_BUFFER)) = undefined;
   4780     _ = w.DeviceIoControl(result_handle, w.FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]) catch |err| switch (err) {
   4781         error.AccessDenied => return error.Unexpected,
   4782         error.UnrecognizedVolume => return error.Unexpected,
   4783         else => |e| return e,
   4784     };
   4785 
   4786     const reparse_struct: *const w.REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf[0]));
   4787     const wide_result = switch (reparse_struct.ReparseTag) {
   4788         w.IO_REPARSE_TAG_SYMLINK => r: {
   4789             const buf: *const w.SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
   4790             const offset = buf.SubstituteNameOffset >> 1;
   4791             const len = buf.SubstituteNameLength >> 1;
   4792             const path_buf: [*]const u16 = &buf.PathBuffer;
   4793             const is_relative = buf.Flags & w.SYMLINK_FLAG_RELATIVE != 0;
   4794             break :r try w.parseReadLinkPath(path_buf[offset..][0..len], is_relative, buffer);
   4795         },
   4796         w.IO_REPARSE_TAG_MOUNT_POINT => r: {
   4797             const buf: *const w.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
   4798             const offset = buf.SubstituteNameOffset >> 1;
   4799             const len = buf.SubstituteNameLength >> 1;
   4800             const path_buf: [*]const u16 = &buf.PathBuffer;
   4801             break :r try w.parseReadLinkPath(path_buf[offset..][0..len], false, buffer);
   4802         },
   4803         else => return error.UnsupportedReparsePointType,
   4804     };
   4805 
   4806     const len = std.unicode.calcWtf8Len(wide_result);
   4807     if (len > buffer.len) return error.NameTooLong;
   4808 
   4809     return std.unicode.wtf16LeToWtf8(buffer, wide_result);
   4810 }
   4811 
   4812 fn dirReadLinkWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
   4813     if (builtin.link_libc) return dirReadLinkPosix(userdata, dir, sub_path, buffer);
   4814 
   4815     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4816     const current_thread = Thread.getCurrent(t);
   4817 
   4818     var n: usize = undefined;
   4819     try current_thread.beginSyscall();
   4820     while (true) {
   4821         switch (std.os.wasi.path_readlink(dir.handle, sub_path.ptr, sub_path.len, buffer.ptr, buffer.len, &n)) {
   4822             .SUCCESS => {
   4823                 current_thread.endSyscall();
   4824                 return buffer[0..n];
   4825             },
   4826             .CANCELED => return current_thread.endSyscallCanceled(),
   4827             .INTR => {
   4828                 try current_thread.checkCancel();
   4829                 continue;
   4830             },
   4831             else => |e| {
   4832                 current_thread.endSyscall();
   4833                 switch (e) {
   4834                     .ACCES => return error.AccessDenied,
   4835                     .FAULT => |err| return errnoBug(err),
   4836                     .INVAL => return error.NotLink,
   4837                     .IO => return error.FileSystem,
   4838                     .LOOP => return error.SymLinkLoop,
   4839                     .NAMETOOLONG => return error.NameTooLong,
   4840                     .NOENT => return error.FileNotFound,
   4841                     .NOMEM => return error.SystemResources,
   4842                     .NOTDIR => return error.NotDir,
   4843                     .NOTCAPABLE => return error.AccessDenied,
   4844                     .ILSEQ => return error.BadPathName,
   4845                     else => |err| return posix.unexpectedErrno(err),
   4846                 }
   4847             },
   4848         }
   4849     }
   4850 }
   4851 
   4852 fn dirReadLinkPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
   4853     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4854     const current_thread = Thread.getCurrent(t);
   4855 
   4856     var sub_path_buffer: [posix.PATH_MAX]u8 = undefined;
   4857     const sub_path_posix = try pathToPosix(sub_path, &sub_path_buffer);
   4858 
   4859     try current_thread.beginSyscall();
   4860     while (true) {
   4861         const rc = posix.system.readlinkat(dir.handle, sub_path_posix, buffer.ptr, buffer.len);
   4862         switch (posix.errno(rc)) {
   4863             .SUCCESS => {
   4864                 current_thread.endSyscall();
   4865                 const len: usize = @bitCast(rc);
   4866                 return len;
   4867             },
   4868             .CANCELED => return current_thread.endSyscallCanceled(),
   4869             .INTR => {
   4870                 try current_thread.checkCancel();
   4871                 continue;
   4872             },
   4873             else => |e| {
   4874                 current_thread.endSyscall();
   4875                 switch (e) {
   4876                     .ACCES => return error.AccessDenied,
   4877                     .FAULT => |err| return errnoBug(err),
   4878                     .INVAL => return error.NotLink,
   4879                     .IO => return error.FileSystem,
   4880                     .LOOP => return error.SymLinkLoop,
   4881                     .NAMETOOLONG => return error.NameTooLong,
   4882                     .NOENT => return error.FileNotFound,
   4883                     .NOMEM => return error.SystemResources,
   4884                     .NOTDIR => return error.NotDir,
   4885                     .ILSEQ => return error.BadPathName,
   4886                     else => |err| return posix.unexpectedErrno(err),
   4887                 }
   4888             },
   4889         }
   4890     }
   4891 }
   4892 
   4893 const dirSetPermissions = switch (native_os) {
   4894     .windows => dirSetPermissionsWindows,
   4895     else => dirSetPermissionsPosix,
   4896 };
   4897 
   4898 fn dirSetPermissionsWindows(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void {
   4899     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4900     _ = t;
   4901     _ = dir;
   4902     _ = permissions;
   4903     @panic("TODO");
   4904 }
   4905 
   4906 fn dirSetPermissionsPosix(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void {
   4907     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4908     const current_thread = Thread.getCurrent(t);
   4909     return setPermissionsPosix(current_thread, dir.handle, permissions.toMode());
   4910 }
   4911 
   4912 fn dirSetFilePermissions(
   4913     userdata: ?*anyopaque,
   4914     dir: Dir,
   4915     sub_path: []const u8,
   4916     permissions: Dir.Permissions,
   4917     options: Dir.SetFilePermissionsOptions,
   4918 ) Dir.SetFilePermissionsError!void {
   4919     if (!Dir.Permissions.has_executable_bit) return error.Unexpected;
   4920     if (is_windows) @panic("TODO");
   4921     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4922     const current_thread = Thread.getCurrent(t);
   4923 
   4924     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   4925     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   4926 
   4927     const mode = permissions.toMode();
   4928     const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
   4929 
   4930     return posixFchmodat(t, current_thread, dir.handle, sub_path_posix, mode, flags);
   4931 }
   4932 
   4933 fn posixFchmodat(
   4934     t: *Threaded,
   4935     current_thread: *Thread,
   4936     dir_fd: posix.fd_t,
   4937     path: [*:0]const u8,
   4938     mode: posix.mode_t,
   4939     flags: u32,
   4940 ) Dir.SetFilePermissionsError!void {
   4941     // No special handling for linux is needed if we can use the libc fallback
   4942     // or `flags` is empty. Glibc only added the fallback in 2.32.
   4943     if (have_fchmodat_flags or flags == 0) {
   4944         try current_thread.beginSyscall();
   4945         while (true) {
   4946             const rc = if (have_fchmodat_flags)
   4947                 posix.system.fchmodat(dir_fd, path, mode, flags)
   4948             else
   4949                 posix.system.fchmodat(dir_fd, path, mode);
   4950             switch (posix.errno(rc)) {
   4951                 .SUCCESS => return current_thread.endSyscall(),
   4952                 .CANCELED => return current_thread.endSyscallCanceled(),
   4953                 .INTR => {
   4954                     try current_thread.checkCancel();
   4955                     continue;
   4956                 },
   4957                 else => |e| {
   4958                     current_thread.endSyscall();
   4959                     switch (e) {
   4960                         .BADF => |err| return errnoBug(err),
   4961                         .FAULT => |err| return errnoBug(err),
   4962                         .INVAL => |err| return errnoBug(err),
   4963                         .ACCES => return error.AccessDenied,
   4964                         .IO => return error.InputOutput,
   4965                         .LOOP => return error.SymLinkLoop,
   4966                         .MFILE => return error.ProcessFdQuotaExceeded,
   4967                         .NAMETOOLONG => return error.NameTooLong,
   4968                         .NFILE => return error.SystemFdQuotaExceeded,
   4969                         .NOENT => return error.FileNotFound,
   4970                         .NOTDIR => return error.FileNotFound,
   4971                         .NOMEM => return error.SystemResources,
   4972                         .OPNOTSUPP => return error.OperationNotSupported,
   4973                         .PERM => return error.PermissionDenied,
   4974                         .ROFS => return error.ReadOnlyFileSystem,
   4975                         else => |err| return posix.unexpectedErrno(err),
   4976                     }
   4977                 },
   4978             }
   4979         }
   4980     }
   4981 
   4982     if (@atomicLoad(UseFchmodat2, &t.use_fchmodat2, .monotonic) == .disabled)
   4983         return fchmodatFallback(current_thread, dir_fd, path, mode, flags);
   4984 
   4985     comptime assert(native_os == .linux);
   4986 
   4987     try current_thread.beginSyscall();
   4988     while (true) {
   4989         switch (std.os.linux.errno(std.os.linux.fchmodat2(dir_fd, path, mode, flags))) {
   4990             .SUCCESS => return current_thread.endSyscall(),
   4991             .CANCELED => return current_thread.endSyscallCanceled(),
   4992             .INTR => {
   4993                 try current_thread.checkCancel();
   4994                 continue;
   4995             },
   4996             else => |e| {
   4997                 current_thread.endSyscall();
   4998                 switch (e) {
   4999                     .BADF => |err| return errnoBug(err),
   5000                     .FAULT => |err| return errnoBug(err),
   5001                     .INVAL => |err| return errnoBug(err),
   5002                     .ACCES => return error.AccessDenied,
   5003                     .IO => return error.InputOutput,
   5004                     .LOOP => return error.SymLinkLoop,
   5005                     .NOENT => return error.FileNotFound,
   5006                     .NOMEM => return error.SystemResources,
   5007                     .NOTDIR => return error.FileNotFound,
   5008                     .OPNOTSUPP => return error.OperationNotSupported,
   5009                     .PERM => return error.PermissionDenied,
   5010                     .ROFS => return error.ReadOnlyFileSystem,
   5011                     .NOSYS => {
   5012                         @atomicStore(UseFchmodat2, &t.use_fchmodat2, .disabled, .monotonic);
   5013                         return fchmodatFallback(current_thread, dir_fd, path, mode, flags);
   5014                     },
   5015                     else => |err| return posix.unexpectedErrno(err),
   5016                 }
   5017             },
   5018         }
   5019     }
   5020 }
   5021 
   5022 fn fchmodatFallback(
   5023     current_thread: *Thread,
   5024     dir_fd: posix.fd_t,
   5025     path: [*:0]const u8,
   5026     mode: posix.mode_t,
   5027     flags: u32,
   5028 ) Dir.SetFilePermissionsError!void {
   5029     _ = current_thread;
   5030     _ = dir_fd;
   5031     _ = path;
   5032     _ = mode;
   5033     _ = flags;
   5034     // I deleted the previous fallback implementation because it looked wrong to me. Please cross-reference
   5035     // fhmodat.c in musl libc before blindly restoring the implementation.
   5036     @panic("TODO");
   5037 }
   5038 
   5039 const dirSetOwner = switch (native_os) {
   5040     .windows => dirSetOwnerUnsupported,
   5041     else => dirSetOwnerPosix,
   5042 };
   5043 
   5044 fn dirSetOwnerUnsupported(userdata: ?*anyopaque, dir: Dir, owner: ?File.Uid, group: ?File.Gid) Dir.SetOwnerError!void {
   5045     _ = userdata;
   5046     _ = dir;
   5047     _ = owner;
   5048     _ = group;
   5049     return error.Unexpected;
   5050 }
   5051 
   5052 fn dirSetOwnerPosix(userdata: ?*anyopaque, dir: Dir, owner: ?File.Uid, group: ?File.Gid) Dir.SetOwnerError!void {
   5053     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5054     const current_thread = Thread.getCurrent(t);
   5055     const uid = owner orelse ~@as(posix.uid_t, 0);
   5056     const gid = group orelse ~@as(posix.gid_t, 0);
   5057     return setOwnerPosix(current_thread, dir.handle, uid, gid);
   5058 }
   5059 
   5060 fn setOwnerPosix(current_thread: *Thread, fd: posix.fd_t, uid: posix.uid_t, gid: posix.gid_t) File.SetOwnerError!void {
   5061     try current_thread.beginSyscall();
   5062     while (true) {
   5063         switch (posix.errno(posix.system.fchown(fd, uid, gid))) {
   5064             .SUCCESS => return current_thread.endSyscall(),
   5065             .CANCELED => return current_thread.endSyscallCanceled(),
   5066             .INTR => {
   5067                 try current_thread.checkCancel();
   5068                 continue;
   5069             },
   5070             else => |e| {
   5071                 current_thread.endSyscall();
   5072                 switch (e) {
   5073                     .BADF => |err| return errnoBug(err), // likely fd refers to directory opened without `Dir.OpenOptions.iterate`
   5074                     .FAULT => |err| return errnoBug(err),
   5075                     .INVAL => |err| return errnoBug(err),
   5076                     .ACCES => return error.AccessDenied,
   5077                     .IO => return error.InputOutput,
   5078                     .LOOP => return error.SymLinkLoop,
   5079                     .NOENT => return error.FileNotFound,
   5080                     .NOMEM => return error.SystemResources,
   5081                     .NOTDIR => return error.FileNotFound,
   5082                     .PERM => return error.PermissionDenied,
   5083                     .ROFS => return error.ReadOnlyFileSystem,
   5084                     else => |err| return posix.unexpectedErrno(err),
   5085                 }
   5086             },
   5087         }
   5088     }
   5089 }
   5090 
   5091 fn dirSetFileOwner(
   5092     userdata: ?*anyopaque,
   5093     dir: Dir,
   5094     sub_path: []const u8,
   5095     owner: ?File.Uid,
   5096     group: ?File.Gid,
   5097     options: Dir.SetFileOwnerOptions,
   5098 ) Dir.SetFileOwnerError!void {
   5099     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5100     const current_thread = Thread.getCurrent(t);
   5101 
   5102     if (is_windows) {
   5103         @panic("TODO");
   5104     }
   5105 
   5106     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   5107     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   5108 
   5109     _ = current_thread;
   5110     _ = dir;
   5111     _ = sub_path_posix;
   5112     _ = owner;
   5113     _ = group;
   5114     _ = options;
   5115     @panic("TODO");
   5116 }
   5117 
   5118 const fileSync = switch (native_os) {
   5119     .windows => fileSyncWindows,
   5120     else => fileSyncPosix,
   5121 };
   5122 
   5123 fn fileSyncWindows(userdata: ?*anyopaque, file: File) File.SyncError!void {
   5124     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5125     const current_thread = Thread.getCurrent(t);
   5126 
   5127     try current_thread.checkCancel();
   5128 
   5129     if (windows.kernel32.FlushFileBuffers(file.handle) != 0)
   5130         return;
   5131 
   5132     switch (windows.GetLastError()) {
   5133         .SUCCESS => return,
   5134         .INVALID_HANDLE => unreachable,
   5135         .ACCESS_DENIED => return error.AccessDenied, // a sync was performed but the system couldn't update the access time
   5136         .UNEXP_NET_ERR => return error.InputOutput,
   5137         else => |err| return windows.unexpectedError(err),
   5138     }
   5139 }
   5140 
   5141 fn fileSyncPosix(userdata: ?*anyopaque, file: File) File.SyncError!void {
   5142     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5143     const current_thread = Thread.getCurrent(t);
   5144     try current_thread.beginSyscall();
   5145     while (true) {
   5146         switch (posix.errno(posix.system.fsync(file.handle))) {
   5147             .SUCCESS => return current_thread.endSyscall(),
   5148             .CANCELED => return current_thread.endSyscallCanceled(),
   5149             .INTR => {
   5150                 try current_thread.checkCancel();
   5151                 continue;
   5152             },
   5153             else => |e| {
   5154                 current_thread.endSyscall();
   5155                 switch (e) {
   5156                     .BADF => |err| return errnoBug(err),
   5157                     .INVAL => |err| return errnoBug(err),
   5158                     .ROFS => |err| return errnoBug(err),
   5159                     .IO => return error.InputOutput,
   5160                     .NOSPC => return error.NoSpaceLeft,
   5161                     .DQUOT => return error.DiskQuota,
   5162                     else => |err| return posix.unexpectedErrno(err),
   5163                 }
   5164             },
   5165         }
   5166     }
   5167 }
   5168 
   5169 fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
   5170     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5171     const current_thread = Thread.getCurrent(t);
   5172     return isTty(current_thread, file);
   5173 }
   5174 
   5175 fn isTty(current_thread: *Thread, file: File) Io.Cancelable!bool {
   5176     if (is_windows) {
   5177         if (try isCygwinPty(current_thread, file)) return true;
   5178         try current_thread.checkCancel();
   5179         var out: windows.DWORD = undefined;
   5180         return windows.kernel32.GetConsoleMode(file.handle, &out) != 0;
   5181     }
   5182 
   5183     if (builtin.link_libc) {
   5184         try current_thread.beginSyscall();
   5185         while (true) {
   5186             const rc = posix.system.isatty(file.handle);
   5187             switch (posix.errno(rc - 1)) {
   5188                 .SUCCESS => {
   5189                     current_thread.endSyscall();
   5190                     return true;
   5191                 },
   5192                 .CANCELED => return current_thread.endSyscallCanceled(),
   5193                 .INTR => {
   5194                     try current_thread.checkCancel();
   5195                     continue;
   5196                 },
   5197                 else => {
   5198                     current_thread.endSyscall();
   5199                     return false;
   5200                 },
   5201             }
   5202         }
   5203     }
   5204 
   5205     if (native_os == .wasi) {
   5206         var statbuf: std.os.wasi.fdstat_t = undefined;
   5207         const err = std.os.wasi.fd_fdstat_get(file.handle, &statbuf);
   5208         if (err != .SUCCESS)
   5209             return false;
   5210 
   5211         // A tty is a character device that we can't seek or tell on.
   5212         if (statbuf.fs_filetype != .CHARACTER_DEVICE)
   5213             return false;
   5214         if (statbuf.fs_rights_base.FD_SEEK or statbuf.fs_rights_base.FD_TELL)
   5215             return false;
   5216 
   5217         return true;
   5218     }
   5219 
   5220     if (native_os == .linux) {
   5221         const linux = std.os.linux;
   5222         try current_thread.beginSyscall();
   5223         while (true) {
   5224             var wsz: posix.winsize = undefined;
   5225             const fd: usize = @bitCast(@as(isize, file.handle));
   5226             const rc = linux.syscall3(.ioctl, fd, linux.T.IOCGWINSZ, @intFromPtr(&wsz));
   5227             switch (linux.errno(rc)) {
   5228                 .SUCCESS => {
   5229                     current_thread.endSyscall();
   5230                     return true;
   5231                 },
   5232                 .CANCELED => return current_thread.endSyscallCanceled(),
   5233                 .INTR => {
   5234                     try current_thread.checkCancel();
   5235                     continue;
   5236                 },
   5237                 else => {
   5238                     current_thread.endSyscall();
   5239                     return false;
   5240                 },
   5241             }
   5242         }
   5243     }
   5244 
   5245     @compileError("unimplemented");
   5246 }
   5247 
   5248 fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
   5249     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5250     const current_thread = Thread.getCurrent(t);
   5251 
   5252     if (is_windows) {
   5253         try current_thread.checkCancel();
   5254 
   5255         // For Windows Terminal, VT Sequences processing is enabled by default.
   5256         var original_console_mode: windows.DWORD = 0;
   5257         if (windows.kernel32.GetConsoleMode(file.handle, &original_console_mode) != 0) {
   5258             if (original_console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return;
   5259 
   5260             // For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.
   5261             // https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/
   5262             //
   5263             // Note: In Microsoft's example for enabling virtual terminal processing, it
   5264             // shows attempting to enable `DISABLE_NEWLINE_AUTO_RETURN` as well:
   5265             // https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
   5266             // This is avoided because in the old Windows Console, that flag causes \n (as opposed to \r\n)
   5267             // to behave unexpectedly (the cursor moves down 1 row but remains on the same column).
   5268             // Additionally, the default console mode in Windows Terminal does not have
   5269             // `DISABLE_NEWLINE_AUTO_RETURN` set, so by only enabling `ENABLE_VIRTUAL_TERMINAL_PROCESSING`
   5270             // we end up matching the mode of Windows Terminal.
   5271             const requested_console_modes = windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
   5272             const console_mode = original_console_mode | requested_console_modes;
   5273             try current_thread.checkCancel();
   5274             if (windows.kernel32.SetConsoleMode(file.handle, console_mode) != 0) return;
   5275         }
   5276         if (try isCygwinPty(current_thread, file)) return;
   5277     } else {
   5278         if (try supportsAnsiEscapeCodes(current_thread, file)) return;
   5279     }
   5280     return error.NotTerminalDevice;
   5281 }
   5282 
   5283 fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
   5284     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5285     const current_thread = Thread.getCurrent(t);
   5286     return supportsAnsiEscapeCodes(current_thread, file);
   5287 }
   5288 
   5289 fn supportsAnsiEscapeCodes(current_thread: *Thread, file: File) Io.Cancelable!bool {
   5290     if (is_windows) {
   5291         try current_thread.checkCancel();
   5292         var console_mode: windows.DWORD = 0;
   5293         if (windows.kernel32.GetConsoleMode(file.handle, &console_mode) != 0) {
   5294             if (console_mode & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return true;
   5295         }
   5296         return isCygwinPty(current_thread, file);
   5297     }
   5298 
   5299     if (native_os == .wasi) {
   5300         // WASI sanitizes stdout when fd is a tty so ANSI escape codes will not
   5301         // be interpreted as actual cursor commands, and stderr is always
   5302         // sanitized.
   5303         return false;
   5304     }
   5305 
   5306     if (try isTty(current_thread, file)) return true;
   5307 
   5308     return false;
   5309 }
   5310 
   5311 fn isCygwinPty(current_thread: *Thread, file: File) Io.Cancelable!bool {
   5312     if (!is_windows) return false;
   5313 
   5314     const handle = file.handle;
   5315 
   5316     // If this is a MSYS2/cygwin pty, then it will be a named pipe with a name in one of these formats:
   5317     //   msys-[...]-ptyN-[...]
   5318     //   cygwin-[...]-ptyN-[...]
   5319     //
   5320     // Example: msys-1888ae32e00d56aa-pty0-to-master
   5321 
   5322     // First, just check that the handle is a named pipe.
   5323     // This allows us to avoid the more costly NtQueryInformationFile call
   5324     // for handles that aren't named pipes.
   5325     {
   5326         try current_thread.checkCancel();
   5327         var io_status: windows.IO_STATUS_BLOCK = undefined;
   5328         var device_info: windows.FILE_FS_DEVICE_INFORMATION = undefined;
   5329         const rc = windows.ntdll.NtQueryVolumeInformationFile(handle, &io_status, &device_info, @sizeOf(windows.FILE_FS_DEVICE_INFORMATION), .FileFsDeviceInformation);
   5330         switch (rc) {
   5331             .SUCCESS => {},
   5332             else => return false,
   5333         }
   5334         if (device_info.DeviceType != windows.FILE_DEVICE_NAMED_PIPE) return false;
   5335     }
   5336 
   5337     const name_bytes_offset = @offsetOf(windows.FILE_NAME_INFO, "FileName");
   5338     // `NAME_MAX` UTF-16 code units (2 bytes each)
   5339     // This buffer may not be long enough to handle *all* possible paths
   5340     // (PATH_MAX_WIDE would be necessary for that), but because we only care
   5341     // about certain paths and we know they must be within a reasonable length,
   5342     // we can use this smaller buffer and just return false on any error from
   5343     // NtQueryInformationFile.
   5344     const num_name_bytes = windows.MAX_PATH * 2;
   5345     var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);
   5346 
   5347     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   5348     try current_thread.checkCancel();
   5349     const rc = windows.ntdll.NtQueryInformationFile(handle, &io_status_block, &name_info_bytes, @intCast(name_info_bytes.len), .FileNameInformation);
   5350     switch (rc) {
   5351         .SUCCESS => {},
   5352         .INVALID_PARAMETER => unreachable,
   5353         else => return false,
   5354     }
   5355 
   5356     const name_info: *const windows.FILE_NAME_INFO = @ptrCast(&name_info_bytes);
   5357     const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength];
   5358     const name_wide = std.mem.bytesAsSlice(u16, name_bytes);
   5359     // The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master
   5360     return (std.mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'm', 's', 'y', 's', '-' }) or
   5361         std.mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'c', 'y', 'g', 'w', 'i', 'n', '-' })) and
   5362         std.mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null;
   5363 }
   5364 
   5365 fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthError!void {
   5366     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5367     const current_thread = Thread.getCurrent(t);
   5368 
   5369     const signed_len: i64 = @bitCast(length);
   5370     if (signed_len < 0) return error.FileTooBig; // Avoid ambiguous EINVAL errors.
   5371 
   5372     if (is_windows) {
   5373         try current_thread.checkCancel();
   5374 
   5375         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   5376         var eof_info: windows.FILE_END_OF_FILE_INFORMATION = .{
   5377             .EndOfFile = signed_len,
   5378         };
   5379 
   5380         const status = windows.ntdll.NtSetInformationFile(
   5381             file.handle,
   5382             &io_status_block,
   5383             &eof_info,
   5384             @sizeOf(windows.FILE_END_OF_FILE_INFORMATION),
   5385             .FileEndOfFileInformation,
   5386         );
   5387         switch (status) {
   5388             .SUCCESS => return,
   5389             .INVALID_HANDLE => |err| return windows.statusBug(err), // Handle not open for writing.
   5390             .ACCESS_DENIED => return error.AccessDenied,
   5391             .USER_MAPPED_FILE => return error.AccessDenied,
   5392             .INVALID_PARAMETER => return error.FileTooBig,
   5393             else => return windows.unexpectedStatus(status),
   5394         }
   5395     }
   5396 
   5397     if (native_os == .wasi and !builtin.link_libc) {
   5398         try current_thread.beginSyscall();
   5399         while (true) {
   5400             switch (std.os.wasi.fd_filestat_set_size(file.handle, length)) {
   5401                 .SUCCESS => return current_thread.endSyscall(),
   5402                 .CANCELED => return current_thread.endSyscallCanceled(),
   5403                 .INTR => {
   5404                     try current_thread.checkCancel();
   5405                     continue;
   5406                 },
   5407                 else => |e| {
   5408                     current_thread.endSyscall();
   5409                     switch (e) {
   5410                         .FBIG => return error.FileTooBig,
   5411                         .IO => return error.InputOutput,
   5412                         .PERM => return error.PermissionDenied,
   5413                         .TXTBSY => return error.FileBusy,
   5414                         .BADF => |err| return errnoBug(err), // Handle not open for writing
   5415                         .INVAL => return error.NonResizable,
   5416                         .NOTCAPABLE => return error.AccessDenied,
   5417                         else => |err| return posix.unexpectedErrno(err),
   5418                     }
   5419                 },
   5420             }
   5421         }
   5422     }
   5423 
   5424     try current_thread.beginSyscall();
   5425     while (true) {
   5426         switch (posix.errno(ftruncate_sym(file.handle, signed_len))) {
   5427             .SUCCESS => return current_thread.endSyscall(),
   5428             .CANCELED => return current_thread.endSyscallCanceled(),
   5429             .INTR => {
   5430                 try current_thread.checkCancel();
   5431                 continue;
   5432             },
   5433             else => |e| {
   5434                 current_thread.endSyscall();
   5435                 switch (e) {
   5436                     .FBIG => return error.FileTooBig,
   5437                     .IO => return error.InputOutput,
   5438                     .PERM => return error.PermissionDenied,
   5439                     .TXTBSY => return error.FileBusy,
   5440                     .BADF => |err| return errnoBug(err), // Handle not open for writing.
   5441                     .INVAL => return error.NonResizable, // This is returned for /dev/null for example.
   5442                     else => |err| return posix.unexpectedErrno(err),
   5443                 }
   5444             },
   5445         }
   5446     }
   5447 }
   5448 
   5449 fn fileSetOwner(userdata: ?*anyopaque, file: File, owner: ?File.Uid, group: ?File.Gid) File.SetOwnerError!void {
   5450     switch (native_os) {
   5451         .windows, .wasi => return error.Unexpected,
   5452         else => {},
   5453     }
   5454     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5455     const current_thread = Thread.getCurrent(t);
   5456     const uid = owner orelse ~@as(posix.uid_t, 0);
   5457     const gid = group orelse ~@as(posix.gid_t, 0);
   5458     return setOwnerPosix(current_thread, file.handle, uid, gid);
   5459 }
   5460 
   5461 fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permissions) File.SetPermissionsError!void {
   5462     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5463     const current_thread = Thread.getCurrent(t);
   5464     switch (native_os) {
   5465         .windows => {
   5466             try current_thread.checkCancel();
   5467             var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   5468             var info: windows.FILE_BASIC_INFORMATION = .{
   5469                 .CreationTime = 0,
   5470                 .LastAccessTime = 0,
   5471                 .LastWriteTime = 0,
   5472                 .ChangeTime = 0,
   5473                 .FileAttributes = permissions.inner.attributes,
   5474             };
   5475             const status = windows.ntdll.NtSetInformationFile(
   5476                 file.handle,
   5477                 &io_status_block,
   5478                 &info,
   5479                 @sizeOf(windows.FILE_BASIC_INFORMATION),
   5480                 .FileBasicInformation,
   5481             );
   5482             switch (status) {
   5483                 .SUCCESS => return,
   5484                 .INVALID_HANDLE => |err| return windows.statusBug(err),
   5485                 .ACCESS_DENIED => return error.AccessDenied,
   5486                 else => return windows.unexpectedStatus(status),
   5487             }
   5488         },
   5489         .wasi => return error.Unexpected, // Unsupported OS.
   5490         else => return setPermissionsPosix(current_thread, file.handle, permissions.toMode()),
   5491     }
   5492 }
   5493 
   5494 fn setPermissionsPosix(current_thread: *Thread, fd: posix.fd_t, mode: posix.mode_t) File.SetPermissionsError!void {
   5495     try current_thread.beginSyscall();
   5496     while (true) {
   5497         switch (posix.errno(posix.system.fchmod(fd, mode))) {
   5498             .SUCCESS => return current_thread.endSyscall(),
   5499             .CANCELED => return current_thread.endSyscallCanceled(),
   5500             .INTR => {
   5501                 try current_thread.checkCancel();
   5502                 continue;
   5503             },
   5504             else => |e| {
   5505                 current_thread.endSyscall();
   5506                 switch (e) {
   5507                     .BADF => |err| return errnoBug(err),
   5508                     .FAULT => |err| return errnoBug(err),
   5509                     .INVAL => |err| return errnoBug(err),
   5510                     .ACCES => return error.AccessDenied,
   5511                     .IO => return error.InputOutput,
   5512                     .LOOP => return error.SymLinkLoop,
   5513                     .NOENT => return error.FileNotFound,
   5514                     .NOMEM => return error.SystemResources,
   5515                     .NOTDIR => return error.FileNotFound,
   5516                     .PERM => return error.PermissionDenied,
   5517                     .ROFS => return error.ReadOnlyFileSystem,
   5518                     else => |err| return posix.unexpectedErrno(err),
   5519                 }
   5520             },
   5521         }
   5522     }
   5523 }
   5524 
   5525 fn dirSetTimestamps(
   5526     userdata: ?*anyopaque,
   5527     dir: Dir,
   5528     sub_path: []const u8,
   5529     last_accessed: Io.Timestamp,
   5530     last_modified: Io.Timestamp,
   5531     options: Dir.SetTimestampsOptions,
   5532 ) Dir.SetTimestampsError!void {
   5533     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5534     const current_thread = Thread.getCurrent(t);
   5535 
   5536     if (is_windows) {
   5537         @panic("TODO");
   5538     }
   5539 
   5540     if (native_os == .wasi and !builtin.link_libc) {
   5541         @panic("TODO");
   5542     }
   5543 
   5544     const times: [2]posix.timespec = .{
   5545         timestampToPosix(last_accessed.nanoseconds),
   5546         timestampToPosix(last_modified.nanoseconds),
   5547     };
   5548 
   5549     const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
   5550 
   5551     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   5552     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   5553 
   5554     try current_thread.beginSyscall();
   5555     while (true) {
   5556         switch (posix.errno(posix.system.utimensat(dir.handle, sub_path_posix, &times, flags))) {
   5557             .SUCCESS => return current_thread.endSyscall(),
   5558             .CANCELED => return current_thread.endSyscallCanceled(),
   5559             .INTR => {
   5560                 try current_thread.checkCancel();
   5561                 continue;
   5562             },
   5563             else => |e| {
   5564                 current_thread.endSyscall();
   5565                 switch (e) {
   5566                     .ACCES => return error.AccessDenied,
   5567                     .PERM => return error.PermissionDenied,
   5568                     .BADF => |err| return errnoBug(err), // always a race condition
   5569                     .FAULT => |err| return errnoBug(err),
   5570                     .INVAL => |err| return errnoBug(err),
   5571                     .ROFS => return error.ReadOnlyFileSystem,
   5572                     else => |err| return posix.unexpectedErrno(err),
   5573                 }
   5574             },
   5575         }
   5576     }
   5577 }
   5578 
   5579 fn dirSetTimestampsNow(
   5580     userdata: ?*anyopaque,
   5581     dir: Dir,
   5582     sub_path: []const u8,
   5583     options: Dir.SetTimestampsOptions,
   5584 ) Dir.SetTimestampsError!void {
   5585     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5586     const current_thread = Thread.getCurrent(t);
   5587 
   5588     if (is_windows) {
   5589         @panic("TODO");
   5590     }
   5591 
   5592     if (native_os == .wasi and !builtin.link_libc) {
   5593         @panic("TODO");
   5594     }
   5595 
   5596     const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
   5597 
   5598     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   5599     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   5600 
   5601     try current_thread.beginSyscall();
   5602     while (true) {
   5603         switch (posix.errno(posix.system.utimensat(dir.handle, sub_path_posix, null, flags))) {
   5604             .SUCCESS => return current_thread.endSyscall(),
   5605             .CANCELED => return current_thread.endSyscallCanceled(),
   5606             .INTR => {
   5607                 try current_thread.checkCancel();
   5608                 continue;
   5609             },
   5610             else => |e| {
   5611                 current_thread.endSyscall();
   5612                 switch (e) {
   5613                     .ACCES => return error.AccessDenied,
   5614                     .PERM => return error.PermissionDenied,
   5615                     .BADF => |err| return errnoBug(err), // always a race condition
   5616                     .FAULT => |err| return errnoBug(err),
   5617                     .INVAL => |err| return errnoBug(err),
   5618                     .ROFS => return error.ReadOnlyFileSystem,
   5619                     else => |err| return posix.unexpectedErrno(err),
   5620                 }
   5621             },
   5622         }
   5623     }
   5624 }
   5625 
   5626 fn fileSetTimestamps(
   5627     userdata: ?*anyopaque,
   5628     file: File,
   5629     last_accessed: Io.Timestamp,
   5630     last_modified: Io.Timestamp,
   5631 ) File.SetTimestampsError!void {
   5632     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5633     const current_thread = Thread.getCurrent(t);
   5634 
   5635     if (is_windows) {
   5636         try current_thread.checkCancel();
   5637 
   5638         const atime_ft = windows.nanoSecondsToFileTime(last_accessed.toNanoseconds());
   5639         const mtime_ft = windows.nanoSecondsToFileTime(last_modified.toNanoseconds());
   5640 
   5641         // https://github.com/ziglang/zig/issues/1840
   5642         const rc = windows.kernel32.SetFileTime(file.handle, null, &atime_ft, &mtime_ft);
   5643         if (rc == 0) {
   5644             switch (windows.GetLastError()) {
   5645                 else => |err| return windows.unexpectedError(err),
   5646             }
   5647         }
   5648         return;
   5649     }
   5650 
   5651     const times: [2]posix.timespec = .{
   5652         timestampToPosix(last_accessed.nanoseconds),
   5653         timestampToPosix(last_modified.nanoseconds),
   5654     };
   5655 
   5656     if (native_os == .wasi and !builtin.link_libc) {
   5657         const atim = times[0].toTimestamp();
   5658         const mtim = times[1].toTimestamp();
   5659         try current_thread.beginSyscall();
   5660         while (true) {
   5661             switch (std.os.wasi.fd_filestat_set_times(file.handle, atim, mtim, .{
   5662                 .ATIM = true,
   5663                 .MTIM = true,
   5664             })) {
   5665                 .SUCCESS => return current_thread.endSyscall(),
   5666                 .CANCELED => return current_thread.endSyscallCanceled(),
   5667                 .INTR => {
   5668                     try current_thread.checkCancel();
   5669                     continue;
   5670                 },
   5671                 else => |e| {
   5672                     current_thread.endSyscall();
   5673                     switch (e) {
   5674                         .ACCES => return error.AccessDenied,
   5675                         .PERM => return error.PermissionDenied,
   5676                         .BADF => |err| return errnoBug(err), // File descriptor use-after-free.
   5677                         .FAULT => |err| return errnoBug(err),
   5678                         .INVAL => |err| return errnoBug(err),
   5679                         .ROFS => return error.ReadOnlyFileSystem,
   5680                         else => |err| return posix.unexpectedErrno(err),
   5681                     }
   5682                 },
   5683             }
   5684         }
   5685     }
   5686 
   5687     try current_thread.beginSyscall();
   5688     while (true) {
   5689         switch (posix.errno(posix.system.futimens(file.handle, &times))) {
   5690             .SUCCESS => return current_thread.endSyscall(),
   5691             .CANCELED => return current_thread.endSyscallCanceled(),
   5692             .INTR => {
   5693                 try current_thread.checkCancel();
   5694                 continue;
   5695             },
   5696             else => |e| {
   5697                 current_thread.endSyscall();
   5698                 switch (e) {
   5699                     .ACCES => return error.AccessDenied,
   5700                     .PERM => return error.PermissionDenied,
   5701                     .BADF => |err| return errnoBug(err), // always a race condition
   5702                     .FAULT => |err| return errnoBug(err),
   5703                     .INVAL => |err| return errnoBug(err),
   5704                     .ROFS => return error.ReadOnlyFileSystem,
   5705                     else => |err| return posix.unexpectedErrno(err),
   5706                 }
   5707             },
   5708         }
   5709     }
   5710 }
   5711 
   5712 fn fileSetTimestampsNow(userdata: ?*anyopaque, file: File) File.SetTimestampsError!void {
   5713     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5714     const current_thread = Thread.getCurrent(t);
   5715 
   5716     if (is_windows) {
   5717         @panic("TODO");
   5718     }
   5719 
   5720     if (native_os == .wasi and !builtin.link_libc) {
   5721         try current_thread.beginSyscall();
   5722         while (true) {
   5723             switch (std.os.wasi.fd_filestat_set_times(file.handle, 0, 0, .{
   5724                 .ATIM_NOW = true,
   5725                 .MTIM_NOW = true,
   5726             })) {
   5727                 .SUCCESS => return current_thread.endSyscall(),
   5728                 .CANCELED => return current_thread.endSyscallCanceled(),
   5729                 .INTR => {
   5730                     try current_thread.checkCancel();
   5731                     continue;
   5732                 },
   5733                 else => |e| {
   5734                     current_thread.endSyscall();
   5735                     switch (e) {
   5736                         .ACCES => return error.AccessDenied,
   5737                         .PERM => return error.PermissionDenied,
   5738                         .BADF => |err| return errnoBug(err), // always a race condition
   5739                         .FAULT => |err| return errnoBug(err),
   5740                         .INVAL => |err| return errnoBug(err),
   5741                         .ROFS => return error.ReadOnlyFileSystem,
   5742                         else => |err| return posix.unexpectedErrno(err),
   5743                     }
   5744                 },
   5745             }
   5746         }
   5747     }
   5748 
   5749     try current_thread.beginSyscall();
   5750     while (true) {
   5751         switch (posix.errno(posix.system.futimens(file.handle, null))) {
   5752             .SUCCESS => return current_thread.endSyscall(),
   5753             .CANCELED => return current_thread.endSyscallCanceled(),
   5754             .INTR => {
   5755                 try current_thread.checkCancel();
   5756                 continue;
   5757             },
   5758             else => |e| {
   5759                 current_thread.endSyscall();
   5760                 switch (e) {
   5761                     .ACCES => return error.AccessDenied,
   5762                     .PERM => return error.PermissionDenied,
   5763                     .BADF => |err| return errnoBug(err), // always a race condition
   5764                     .FAULT => |err| return errnoBug(err),
   5765                     .INVAL => |err| return errnoBug(err),
   5766                     .ROFS => return error.ReadOnlyFileSystem,
   5767                     else => |err| return posix.unexpectedErrno(err),
   5768                 }
   5769             },
   5770         }
   5771     }
   5772 }
   5773 
   5774 const windows_lock_range_off: windows.LARGE_INTEGER = 0;
   5775 const windows_lock_range_len: windows.LARGE_INTEGER = 1;
   5776 
   5777 fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!void {
   5778     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5779     const current_thread = Thread.getCurrent(t);
   5780 
   5781     if (is_windows) {
   5782         const exclusive = switch (lock) {
   5783             .none => return,
   5784             .shared => false,
   5785             .exclusive => true,
   5786         };
   5787         try current_thread.checkCancel();
   5788         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   5789         const status = windows.ntdll.NtLockFile(
   5790             file.handle,
   5791             null,
   5792             null,
   5793             null,
   5794             &io_status_block,
   5795             &windows_lock_range_off,
   5796             &windows_lock_range_len,
   5797             null,
   5798             windows.FALSE,
   5799             @intFromBool(exclusive),
   5800         );
   5801         switch (status) {
   5802             .SUCCESS => return,
   5803             .INSUFFICIENT_RESOURCES => return error.SystemResources,
   5804             .LOCK_NOT_GRANTED => |err| return windows.statusBug(err), // passed FailImmediately=false
   5805             .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
   5806             else => return windows.unexpectedStatus(status),
   5807         }
   5808     }
   5809 
   5810     const operation: i32 = switch (lock) {
   5811         .none => posix.LOCK.UN,
   5812         .shared => posix.LOCK.SH,
   5813         .exclusive => posix.LOCK.EX,
   5814     };
   5815     try current_thread.beginSyscall();
   5816     while (true) {
   5817         switch (posix.errno(posix.system.flock(file.handle, operation))) {
   5818             .SUCCESS => return current_thread.endSyscall(),
   5819             .CANCELED => return current_thread.endSyscallCanceled(),
   5820             .INTR => {
   5821                 try current_thread.checkCancel();
   5822                 continue;
   5823             },
   5824             else => |e| {
   5825                 current_thread.endSyscall();
   5826                 switch (e) {
   5827                     .BADF => |err| return errnoBug(err),
   5828                     .INVAL => |err| return errnoBug(err), // invalid parameters
   5829                     .NOLCK => return error.SystemResources,
   5830                     .AGAIN => |err| return errnoBug(err),
   5831                     .OPNOTSUPP => return error.FileLocksUnsupported,
   5832                     else => |err| return posix.unexpectedErrno(err),
   5833                 }
   5834             },
   5835         }
   5836     }
   5837 }
   5838 
   5839 fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!bool {
   5840     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5841     const current_thread = Thread.getCurrent(t);
   5842 
   5843     if (is_windows) {
   5844         const exclusive = switch (lock) {
   5845             .none => return,
   5846             .shared => false,
   5847             .exclusive => true,
   5848         };
   5849         try current_thread.checkCancel();
   5850         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   5851         const status = windows.ntdll.NtLockFile(
   5852             file.handle,
   5853             null,
   5854             null,
   5855             null,
   5856             &io_status_block,
   5857             &windows_lock_range_off,
   5858             &windows_lock_range_len,
   5859             null,
   5860             windows.TRUE,
   5861             @intFromBool(exclusive),
   5862         );
   5863         switch (status) {
   5864             .SUCCESS => return true,
   5865             .INSUFFICIENT_RESOURCES => return error.SystemResources,
   5866             .LOCK_NOT_GRANTED => return false,
   5867             .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
   5868             else => return windows.unexpectedStatus(status),
   5869         }
   5870     }
   5871 
   5872     const operation: i32 = switch (lock) {
   5873         .none => posix.LOCK.UN,
   5874         .shared => posix.LOCK.SH | posix.LOCK.NB,
   5875         .exclusive => posix.LOCK.EX | posix.LOCK.NB,
   5876     };
   5877     try current_thread.beginSyscall();
   5878     while (true) {
   5879         switch (posix.errno(posix.system.flock(file.handle, operation))) {
   5880             .SUCCESS => {
   5881                 current_thread.endSyscall();
   5882                 return true;
   5883             },
   5884             .CANCELED => return current_thread.endSyscallCanceled(),
   5885             .INTR => {
   5886                 try current_thread.checkCancel();
   5887                 continue;
   5888             },
   5889             .AGAIN => {
   5890                 current_thread.endSyscall();
   5891                 return false;
   5892             },
   5893             else => |e| {
   5894                 current_thread.endSyscall();
   5895                 switch (e) {
   5896                     .BADF => |err| return errnoBug(err),
   5897                     .INVAL => |err| return errnoBug(err), // invalid parameters
   5898                     .NOLCK => return error.SystemResources,
   5899                     .OPNOTSUPP => return error.FileLocksUnsupported,
   5900                     else => |err| return posix.unexpectedErrno(err),
   5901                 }
   5902             },
   5903         }
   5904     }
   5905 }
   5906 
   5907 fn fileUnlock(userdata: ?*anyopaque, file: File) void {
   5908     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5909     const current_thread = Thread.getCurrent(t);
   5910 
   5911     if (is_windows) {
   5912         try current_thread.checkCancel();
   5913         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   5914         const status = windows.ntdll.NtUnlockFile(
   5915             file.handle,
   5916             &io_status_block,
   5917             &windows_lock_range_off,
   5918             &windows_lock_range_len,
   5919             null,
   5920         );
   5921         if (is_debug) switch (status) {
   5922             .SUCCESS => {},
   5923             .RANGE_NOT_LOCKED => unreachable, // Function asserts unlocked.
   5924             .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
   5925             else => unreachable, // Resource deallocation must succeed.
   5926         };
   5927         return;
   5928     }
   5929 
   5930     while (true) {
   5931         switch (posix.errno(posix.system.flock(file.handle, posix.LOCK.UN))) {
   5932             .SUCCESS => return,
   5933             .CANCELED, .INTR => continue,
   5934             .AGAIN => return assert(!is_debug), // unlocking can't block
   5935             .BADF => return assert(!is_debug), // File descriptor used after closed.
   5936             .INVAL => return assert(!is_debug), // invalid parameters
   5937             .NOLCK => return assert(!is_debug), // Resource deallocation.
   5938             .OPNOTSUPP => return assert(!is_debug), // We already got the lock.
   5939             else => return assert(!is_debug), // Resource deallocation must succeed.
   5940         }
   5941     }
   5942 }
   5943 
   5944 fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError!void {
   5945     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5946     const current_thread = Thread.getCurrent(t);
   5947 
   5948     if (is_windows) {
   5949         try current_thread.checkCancel();
   5950         // On Windows it works like a semaphore + exclusivity flag. To
   5951         // implement this function, we first obtain another lock in shared
   5952         // mode. This changes the exclusivity flag, but increments the
   5953         // semaphore to 2. So we follow up with an NtUnlockFile which
   5954         // decrements the semaphore but does not modify the exclusivity flag.
   5955         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   5956         switch (windows.ntdll.NtLockFile(
   5957             file.handle,
   5958             null,
   5959             null,
   5960             null,
   5961             &io_status_block,
   5962             &windows_lock_range_off,
   5963             &windows_lock_range_len,
   5964             null,
   5965             windows.TRUE,
   5966             windows.FALSE,
   5967         )) {
   5968             .SUCCESS => {},
   5969             .INSUFFICIENT_RESOURCES => |err| return windows.statusBug(err),
   5970             .LOCK_NOT_GRANTED => |err| return windows.statusBug(err), // File was not locked in exclusive mode.
   5971             .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
   5972             else => |status| return windows.unexpectedStatus(status),
   5973         }
   5974         const status = windows.ntdll.NtUnlockFile(
   5975             file.handle,
   5976             &io_status_block,
   5977             &windows_lock_range_off,
   5978             &windows_lock_range_len,
   5979             null,
   5980         );
   5981         if (is_debug) switch (status) {
   5982             .SUCCESS => {},
   5983             .RANGE_NOT_LOCKED => unreachable, // File was not locked.
   5984             .ACCESS_VIOLATION => unreachable, // bad io_status_block pointer
   5985             else => unreachable, // Resource deallocation must succeed.
   5986         };
   5987         return;
   5988     }
   5989 
   5990     const operation = posix.LOCK.SH | posix.LOCK.NB;
   5991 
   5992     try current_thread.beginSyscall();
   5993     while (true) {
   5994         switch (posix.errno(posix.system.flock(file.handle, operation))) {
   5995             .SUCCESS => {
   5996                 current_thread.endSyscall();
   5997                 return;
   5998             },
   5999             .CANCELED => return current_thread.endSyscallCanceled(),
   6000             .INTR => {
   6001                 try current_thread.checkCancel();
   6002                 continue;
   6003             },
   6004             else => |e| {
   6005                 current_thread.endSyscall();
   6006                 switch (e) {
   6007                     .AGAIN => |err| return errnoBug(err), // File was not locked in exclusive mode.
   6008                     .BADF => |err| return errnoBug(err),
   6009                     .INVAL => |err| return errnoBug(err), // invalid parameters
   6010                     .NOLCK => |err| return errnoBug(err), // Lock already obtained.
   6011                     .OPNOTSUPP => |err| return errnoBug(err), // Lock already obtained.
   6012                     else => |err| return posix.unexpectedErrno(err),
   6013                 }
   6014             },
   6015         }
   6016     }
   6017 }
   6018 
   6019 fn dirOpenDirWasi(
   6020     userdata: ?*anyopaque,
   6021     dir: Dir,
   6022     sub_path: []const u8,
   6023     options: Dir.OpenOptions,
   6024 ) Dir.OpenError!Dir {
   6025     if (builtin.link_libc) return dirOpenDirPosix(userdata, dir, sub_path, options);
   6026     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6027     const current_thread = Thread.getCurrent(t);
   6028     const wasi = std.os.wasi;
   6029 
   6030     var base: std.os.wasi.rights_t = .{
   6031         .FD_FILESTAT_GET = true,
   6032         .FD_FDSTAT_SET_FLAGS = true,
   6033         .FD_FILESTAT_SET_TIMES = true,
   6034     };
   6035     if (options.access_sub_paths) {
   6036         base.FD_READDIR = true;
   6037         base.PATH_CREATE_DIRECTORY = true;
   6038         base.PATH_CREATE_FILE = true;
   6039         base.PATH_LINK_SOURCE = true;
   6040         base.PATH_LINK_TARGET = true;
   6041         base.PATH_OPEN = true;
   6042         base.PATH_READLINK = true;
   6043         base.PATH_RENAME_SOURCE = true;
   6044         base.PATH_RENAME_TARGET = true;
   6045         base.PATH_FILESTAT_GET = true;
   6046         base.PATH_FILESTAT_SET_SIZE = true;
   6047         base.PATH_FILESTAT_SET_TIMES = true;
   6048         base.PATH_SYMLINK = true;
   6049         base.PATH_REMOVE_DIRECTORY = true;
   6050         base.PATH_UNLINK_FILE = true;
   6051     }
   6052 
   6053     const lookup_flags: wasi.lookupflags_t = .{ .SYMLINK_FOLLOW = options.follow_symlinks };
   6054     const oflags: wasi.oflags_t = .{ .DIRECTORY = true };
   6055     const fdflags: wasi.fdflags_t = .{};
   6056     var fd: posix.fd_t = undefined;
   6057     try current_thread.beginSyscall();
   6058     while (true) {
   6059         switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, base, fdflags, &fd)) {
   6060             .SUCCESS => {
   6061                 current_thread.endSyscall();
   6062                 return .{ .handle = fd };
   6063             },
   6064             .INTR => {
   6065                 try current_thread.checkCancel();
   6066                 continue;
   6067             },
   6068             .CANCELED => return current_thread.endSyscallCanceled(),
   6069             else => |e| {
   6070                 current_thread.endSyscall();
   6071                 switch (e) {
   6072                     .FAULT => |err| return errnoBug(err),
   6073                     .INVAL => return error.BadPathName,
   6074                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   6075                     .ACCES => return error.AccessDenied,
   6076                     .LOOP => return error.SymLinkLoop,
   6077                     .MFILE => return error.ProcessFdQuotaExceeded,
   6078                     .NAMETOOLONG => return error.NameTooLong,
   6079                     .NFILE => return error.SystemFdQuotaExceeded,
   6080                     .NODEV => return error.NoDevice,
   6081                     .NOENT => return error.FileNotFound,
   6082                     .NOMEM => return error.SystemResources,
   6083                     .NOTDIR => return error.NotDir,
   6084                     .PERM => return error.PermissionDenied,
   6085                     .BUSY => return error.DeviceBusy,
   6086                     .NOTCAPABLE => return error.AccessDenied,
   6087                     .ILSEQ => return error.BadPathName,
   6088                     else => |err| return posix.unexpectedErrno(err),
   6089                 }
   6090             },
   6091         }
   6092     }
   6093 }
   6094 
   6095 fn fileClose(userdata: ?*anyopaque, files: []const File) void {
   6096     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6097     _ = t;
   6098     for (files) |file| posix.close(file.handle);
   6099 }
   6100 
   6101 const fileReadStreaming = switch (native_os) {
   6102     .windows => fileReadStreamingWindows,
   6103     else => fileReadStreamingPosix,
   6104 };
   6105 
   6106 fn fileReadStreamingPosix(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
   6107     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6108     const current_thread = Thread.getCurrent(t);
   6109 
   6110     var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
   6111     var i: usize = 0;
   6112     for (data) |buf| {
   6113         if (iovecs_buffer.len - i == 0) break;
   6114         if (buf.len != 0) {
   6115             iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len };
   6116             i += 1;
   6117         }
   6118     }
   6119     const dest = iovecs_buffer[0..i];
   6120     assert(dest[0].len > 0);
   6121 
   6122     if (native_os == .wasi and !builtin.link_libc) {
   6123         try current_thread.beginSyscall();
   6124         while (true) {
   6125             var nread: usize = undefined;
   6126             switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) {
   6127                 .SUCCESS => {
   6128                     current_thread.endSyscall();
   6129                     return nread;
   6130                 },
   6131                 .INTR => {
   6132                     try current_thread.checkCancel();
   6133                     continue;
   6134                 },
   6135                 .CANCELED => return current_thread.endSyscallCanceled(),
   6136                 else => |e| {
   6137                     current_thread.endSyscall();
   6138                     switch (e) {
   6139                         .INVAL => |err| return errnoBug(err),
   6140                         .FAULT => |err| return errnoBug(err),
   6141                         .BADF => return error.NotOpenForReading, // File operation on directory.
   6142                         .IO => return error.InputOutput,
   6143                         .ISDIR => return error.IsDir,
   6144                         .NOBUFS => return error.SystemResources,
   6145                         .NOMEM => return error.SystemResources,
   6146                         .NOTCONN => return error.SocketUnconnected,
   6147                         .CONNRESET => return error.ConnectionResetByPeer,
   6148                         .TIMEDOUT => return error.Timeout,
   6149                         .NOTCAPABLE => return error.AccessDenied,
   6150                         else => |err| return posix.unexpectedErrno(err),
   6151                     }
   6152                 },
   6153             }
   6154         }
   6155     }
   6156 
   6157     try current_thread.beginSyscall();
   6158     while (true) {
   6159         const rc = posix.system.readv(file.handle, dest.ptr, @intCast(dest.len));
   6160         switch (posix.errno(rc)) {
   6161             .SUCCESS => {
   6162                 current_thread.endSyscall();
   6163                 return @intCast(rc);
   6164             },
   6165             .INTR => {
   6166                 try current_thread.checkCancel();
   6167                 continue;
   6168             },
   6169             .CANCELED => return current_thread.endSyscallCanceled(),
   6170             else => |e| {
   6171                 current_thread.endSyscall();
   6172                 switch (e) {
   6173                     .INVAL => |err| return errnoBug(err),
   6174                     .FAULT => |err| return errnoBug(err),
   6175                     .AGAIN => return error.WouldBlock,
   6176                     .BADF => |err| {
   6177                         if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory.
   6178                         return errnoBug(err); // File descriptor used after closed.
   6179                     },
   6180                     .IO => return error.InputOutput,
   6181                     .ISDIR => return error.IsDir,
   6182                     .NOBUFS => return error.SystemResources,
   6183                     .NOMEM => return error.SystemResources,
   6184                     .NOTCONN => return error.SocketUnconnected,
   6185                     .CONNRESET => return error.ConnectionResetByPeer,
   6186                     .TIMEDOUT => return error.Timeout,
   6187                     else => |err| return posix.unexpectedErrno(err),
   6188                 }
   6189             },
   6190         }
   6191     }
   6192 }
   6193 
   6194 fn fileReadStreamingWindows(userdata: ?*anyopaque, file: File, data: []const []u8) File.Reader.Error!usize {
   6195     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6196     const current_thread = Thread.getCurrent(t);
   6197 
   6198     const DWORD = windows.DWORD;
   6199     var index: usize = 0;
   6200     while (data[index].len == 0) index += 1;
   6201     const buffer = data[index];
   6202     const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
   6203 
   6204     while (true) {
   6205         try current_thread.checkCancel();
   6206         var n: DWORD = undefined;
   6207         if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, null) != 0)
   6208             return n;
   6209         switch (windows.GetLastError()) {
   6210             .IO_PENDING => |err| return windows.errorBug(err),
   6211             .OPERATION_ABORTED => continue,
   6212             .BROKEN_PIPE => return 0,
   6213             .HANDLE_EOF => return 0,
   6214             .NETNAME_DELETED => return error.ConnectionResetByPeer,
   6215             .LOCK_VIOLATION => return error.LockViolation,
   6216             .ACCESS_DENIED => return error.AccessDenied,
   6217             .INVALID_HANDLE => return error.NotOpenForReading,
   6218             else => |err| return windows.unexpectedError(err),
   6219         }
   6220     }
   6221 }
   6222 
   6223 fn fileReadPositionalPosix(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
   6224     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6225     const current_thread = Thread.getCurrent(t);
   6226 
   6227     if (!have_preadv) @compileError("TODO");
   6228 
   6229     var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
   6230     var i: usize = 0;
   6231     for (data) |buf| {
   6232         if (iovecs_buffer.len - i == 0) break;
   6233         if (buf.len != 0) {
   6234             iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len };
   6235             i += 1;
   6236         }
   6237     }
   6238     const dest = iovecs_buffer[0..i];
   6239     assert(dest[0].len > 0);
   6240 
   6241     if (native_os == .wasi and !builtin.link_libc) {
   6242         try current_thread.beginSyscall();
   6243         while (true) {
   6244             var nread: usize = undefined;
   6245             switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) {
   6246                 .SUCCESS => {
   6247                     current_thread.endSyscall();
   6248                     return nread;
   6249                 },
   6250                 .INTR => {
   6251                     try current_thread.checkCancel();
   6252                     continue;
   6253                 },
   6254                 .CANCELED => return current_thread.endSyscallCanceled(),
   6255                 else => |e| {
   6256                     current_thread.endSyscall();
   6257                     switch (e) {
   6258                         .INVAL => |err| return errnoBug(err),
   6259                         .FAULT => |err| return errnoBug(err),
   6260                         .AGAIN => |err| return errnoBug(err),
   6261                         .BADF => return error.NotOpenForReading, // File operation on directory.
   6262                         .IO => return error.InputOutput,
   6263                         .ISDIR => return error.IsDir,
   6264                         .NOBUFS => return error.SystemResources,
   6265                         .NOMEM => return error.SystemResources,
   6266                         .NOTCONN => return error.SocketUnconnected,
   6267                         .CONNRESET => return error.ConnectionResetByPeer,
   6268                         .TIMEDOUT => return error.Timeout,
   6269                         .NXIO => return error.Unseekable,
   6270                         .SPIPE => return error.Unseekable,
   6271                         .OVERFLOW => return error.Unseekable,
   6272                         .NOTCAPABLE => return error.AccessDenied,
   6273                         else => |err| return posix.unexpectedErrno(err),
   6274                     }
   6275                 },
   6276             }
   6277         }
   6278     }
   6279 
   6280     try current_thread.beginSyscall();
   6281     while (true) {
   6282         const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
   6283         switch (posix.errno(rc)) {
   6284             .SUCCESS => {
   6285                 current_thread.endSyscall();
   6286                 return @bitCast(rc);
   6287             },
   6288             .INTR => {
   6289                 try current_thread.checkCancel();
   6290                 continue;
   6291             },
   6292             .CANCELED => return current_thread.endSyscallCanceled(),
   6293             else => |e| {
   6294                 current_thread.endSyscall();
   6295                 switch (e) {
   6296                     .INVAL => |err| return errnoBug(err),
   6297                     .FAULT => |err| return errnoBug(err),
   6298                     .AGAIN => return error.WouldBlock,
   6299                     .BADF => |err| {
   6300                         if (native_os == .wasi) return error.NotOpenForReading; // File operation on directory.
   6301                         return errnoBug(err); // File descriptor used after closed.
   6302                     },
   6303                     .IO => return error.InputOutput,
   6304                     .ISDIR => return error.IsDir,
   6305                     .NOBUFS => return error.SystemResources,
   6306                     .NOMEM => return error.SystemResources,
   6307                     .NOTCONN => return error.SocketUnconnected,
   6308                     .CONNRESET => return error.ConnectionResetByPeer,
   6309                     .TIMEDOUT => return error.Timeout,
   6310                     .NXIO => return error.Unseekable,
   6311                     .SPIPE => return error.Unseekable,
   6312                     .OVERFLOW => return error.Unseekable,
   6313                     else => |err| return posix.unexpectedErrno(err),
   6314                 }
   6315             },
   6316         }
   6317     }
   6318 }
   6319 
   6320 const fileReadPositional = switch (native_os) {
   6321     .windows => fileReadPositionalWindows,
   6322     else => fileReadPositionalPosix,
   6323 };
   6324 
   6325 fn fileReadPositionalWindows(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
   6326     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6327     const current_thread = Thread.getCurrent(t);
   6328 
   6329     const DWORD = windows.DWORD;
   6330 
   6331     var index: usize = 0;
   6332     while (data[index].len == 0) index += 1;
   6333     const buffer = data[index];
   6334     const want_read_count: DWORD = @min(std.math.maxInt(DWORD), buffer.len);
   6335 
   6336     var overlapped: windows.OVERLAPPED = .{
   6337         .Internal = 0,
   6338         .InternalHigh = 0,
   6339         .DUMMYUNIONNAME = .{
   6340             .DUMMYSTRUCTNAME = .{
   6341                 .Offset = @truncate(offset),
   6342                 .OffsetHigh = @truncate(offset >> 32),
   6343             },
   6344         },
   6345         .hEvent = null,
   6346     };
   6347 
   6348     while (true) {
   6349         try current_thread.checkCancel();
   6350         var n: DWORD = undefined;
   6351         if (windows.kernel32.ReadFile(file.handle, buffer.ptr, want_read_count, &n, &overlapped) != 0)
   6352             return n;
   6353         switch (windows.GetLastError()) {
   6354             .IO_PENDING => |err| return windows.errorBug(err),
   6355             .OPERATION_ABORTED => continue,
   6356             .BROKEN_PIPE => return 0,
   6357             .HANDLE_EOF => return 0,
   6358             .NETNAME_DELETED => return error.ConnectionResetByPeer,
   6359             .LOCK_VIOLATION => return error.LockViolation,
   6360             .ACCESS_DENIED => return error.AccessDenied,
   6361             .INVALID_HANDLE => return error.NotOpenForReading,
   6362             else => |err| return windows.unexpectedError(err),
   6363         }
   6364     }
   6365 }
   6366 
   6367 fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!void {
   6368     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6369     const current_thread = Thread.getCurrent(t);
   6370     const fd = file.handle;
   6371 
   6372     if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   6373         var result: u64 = undefined;
   6374         try current_thread.beginSyscall();
   6375         while (true) {
   6376             switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.CUR))) {
   6377                 .SUCCESS => {
   6378                     current_thread.endSyscall();
   6379                     return;
   6380                 },
   6381                 .INTR => {
   6382                     try current_thread.checkCancel();
   6383                     continue;
   6384                 },
   6385                 .CANCELED => return current_thread.endSyscallCanceled(),
   6386                 else => |e| {
   6387                     current_thread.endSyscall();
   6388                     switch (e) {
   6389                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   6390                         .INVAL => return error.Unseekable,
   6391                         .OVERFLOW => return error.Unseekable,
   6392                         .SPIPE => return error.Unseekable,
   6393                         .NXIO => return error.Unseekable,
   6394                         else => |err| return posix.unexpectedErrno(err),
   6395                     }
   6396                 },
   6397             }
   6398         }
   6399     }
   6400 
   6401     if (native_os == .windows) {
   6402         try current_thread.checkCancel();
   6403         return windows.SetFilePointerEx_CURRENT(fd, offset);
   6404     }
   6405 
   6406     if (native_os == .wasi and !builtin.link_libc) {
   6407         var new_offset: std.os.wasi.filesize_t = undefined;
   6408         try current_thread.beginSyscall();
   6409         while (true) {
   6410             switch (std.os.wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
   6411                 .SUCCESS => {
   6412                     current_thread.endSyscall();
   6413                     return;
   6414                 },
   6415                 .INTR => {
   6416                     try current_thread.checkCancel();
   6417                     continue;
   6418                 },
   6419                 .CANCELED => return current_thread.endSyscallCanceled(),
   6420                 else => |e| {
   6421                     current_thread.endSyscall();
   6422                     switch (e) {
   6423                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   6424                         .INVAL => return error.Unseekable,
   6425                         .OVERFLOW => return error.Unseekable,
   6426                         .SPIPE => return error.Unseekable,
   6427                         .NXIO => return error.Unseekable,
   6428                         .NOTCAPABLE => return error.AccessDenied,
   6429                         else => |err| return posix.unexpectedErrno(err),
   6430                     }
   6431                 },
   6432             }
   6433         }
   6434     }
   6435 
   6436     if (posix.SEEK == void) return error.Unseekable;
   6437 
   6438     try current_thread.beginSyscall();
   6439     while (true) {
   6440         switch (posix.errno(lseek_sym(fd, offset, posix.SEEK.CUR))) {
   6441             .SUCCESS => {
   6442                 current_thread.endSyscall();
   6443                 return;
   6444             },
   6445             .INTR => {
   6446                 try current_thread.checkCancel();
   6447                 continue;
   6448             },
   6449             .CANCELED => return current_thread.endSyscallCanceled(),
   6450             else => |e| {
   6451                 current_thread.endSyscall();
   6452                 switch (e) {
   6453                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   6454                     .INVAL => return error.Unseekable,
   6455                     .OVERFLOW => return error.Unseekable,
   6456                     .SPIPE => return error.Unseekable,
   6457                     .NXIO => return error.Unseekable,
   6458                     else => |err| return posix.unexpectedErrno(err),
   6459                 }
   6460             },
   6461         }
   6462     }
   6463 }
   6464 
   6465 fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!void {
   6466     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6467     const current_thread = Thread.getCurrent(t);
   6468     const fd = file.handle;
   6469 
   6470     if (native_os == .windows) {
   6471         try current_thread.checkCancel();
   6472         return windows.SetFilePointerEx_BEGIN(fd, offset);
   6473     }
   6474 
   6475     if (native_os == .wasi and !builtin.link_libc) {
   6476         try current_thread.beginSyscall();
   6477         while (true) {
   6478             var new_offset: std.os.wasi.filesize_t = undefined;
   6479             switch (std.os.wasi.fd_seek(fd, @bitCast(offset), .SET, &new_offset)) {
   6480                 .SUCCESS => {
   6481                     current_thread.endSyscall();
   6482                     return;
   6483                 },
   6484                 .INTR => {
   6485                     try current_thread.checkCancel();
   6486                     continue;
   6487                 },
   6488                 .CANCELED => return current_thread.endSyscallCanceled(),
   6489                 else => |e| {
   6490                     current_thread.endSyscall();
   6491                     switch (e) {
   6492                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   6493                         .INVAL => return error.Unseekable,
   6494                         .OVERFLOW => return error.Unseekable,
   6495                         .SPIPE => return error.Unseekable,
   6496                         .NXIO => return error.Unseekable,
   6497                         .NOTCAPABLE => return error.AccessDenied,
   6498                         else => |err| return posix.unexpectedErrno(err),
   6499                     }
   6500                 },
   6501             }
   6502         }
   6503     }
   6504 
   6505     if (posix.SEEK == void) return error.Unseekable;
   6506 
   6507     return posixSeekTo(current_thread, fd, offset);
   6508 }
   6509 
   6510 fn posixSeekTo(current_thread: *Thread, fd: posix.fd_t, offset: u64) File.SeekError!void {
   6511     if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   6512         try current_thread.beginSyscall();
   6513         while (true) {
   6514             var result: u64 = undefined;
   6515             switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) {
   6516                 .SUCCESS => {
   6517                     current_thread.endSyscall();
   6518                     return;
   6519                 },
   6520                 .INTR => {
   6521                     try current_thread.checkCancel();
   6522                     continue;
   6523                 },
   6524                 .CANCELED => return current_thread.endSyscallCanceled(),
   6525                 else => |e| {
   6526                     current_thread.endSyscall();
   6527                     switch (e) {
   6528                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   6529                         .INVAL => return error.Unseekable,
   6530                         .OVERFLOW => return error.Unseekable,
   6531                         .SPIPE => return error.Unseekable,
   6532                         .NXIO => return error.Unseekable,
   6533                         else => |err| return posix.unexpectedErrno(err),
   6534                     }
   6535                 },
   6536             }
   6537         }
   6538     }
   6539 
   6540     try current_thread.beginSyscall();
   6541     while (true) {
   6542         switch (posix.errno(lseek_sym(fd, @bitCast(offset), posix.SEEK.SET))) {
   6543             .SUCCESS => {
   6544                 current_thread.endSyscall();
   6545                 return;
   6546             },
   6547             .INTR => {
   6548                 try current_thread.checkCancel();
   6549                 continue;
   6550             },
   6551             .CANCELED => return current_thread.endSyscallCanceled(),
   6552             else => |e| {
   6553                 current_thread.endSyscall();
   6554                 switch (e) {
   6555                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   6556                     .INVAL => return error.Unseekable,
   6557                     .OVERFLOW => return error.Unseekable,
   6558                     .SPIPE => return error.Unseekable,
   6559                     .NXIO => return error.Unseekable,
   6560                     else => |err| return posix.unexpectedErrno(err),
   6561                 }
   6562             },
   6563         }
   6564     }
   6565 }
   6566 
   6567 fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) std.process.OpenExecutableError!File {
   6568     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6569     switch (native_os) {
   6570         .linux, .serenity => return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags),
   6571         .windows => {
   6572             // If ImagePathName is a symlink, then it will contain the path of the symlink,
   6573             // not the path that the symlink points to. However, because we are opening
   6574             // the file, we can let the openFileW call follow the symlink for us.
   6575             const image_path_unicode_string = &windows.peb().ProcessParameters.ImagePathName;
   6576             const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
   6577             const prefixed_path_w = try windows.wToPrefixedFileW(null, image_path_name);
   6578             return dirOpenFileWtf16(t, null, prefixed_path_w.span(), flags);
   6579         },
   6580         else => @panic("TODO implement processExecutableOpen"),
   6581     }
   6582 }
   6583 
   6584 fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) std.process.ExecutablePathError!usize {
   6585     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6586 
   6587     switch (native_os) {
   6588         .driverkit,
   6589         .ios,
   6590         .maccatalyst,
   6591         .macos,
   6592         .tvos,
   6593         .visionos,
   6594         .watchos,
   6595         => {
   6596             // Note that _NSGetExecutablePath() will return "a path" to
   6597             // the executable not a "real path" to the executable.
   6598             var symlink_path_buf: [posix.PATH_MAX:0]u8 = undefined;
   6599             var u32_len: u32 = posix.PATH_MAX + 1; // include the sentinel
   6600             const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &u32_len);
   6601             if (rc != 0) return error.NameTooLong;
   6602 
   6603             var real_path_buf: [posix.PATH_MAX]u8 = undefined;
   6604             const n = Io.Dir.realPathAbsolute(ioBasic(t), &symlink_path_buf, &real_path_buf) catch |err| switch (err) {
   6605                 error.NetworkNotFound => unreachable, // Windows-only
   6606                 else => |e| return e,
   6607             };
   6608             if (n > out_buffer.len) return error.NameTooLong;
   6609             @memcpy(out_buffer[0..n], real_path_buf[0..n]);
   6610             return n;
   6611         },
   6612         .linux, .serenity => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/exe", out_buffer) catch |err| switch (err) {
   6613             error.UnsupportedReparsePointType => unreachable, // Windows-only
   6614             error.NetworkNotFound => unreachable, // Windows-only
   6615             else => |e| return e,
   6616         },
   6617         .illumos => return Io.Dir.readLinkAbsolute(ioBasic(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
   6618             error.UnsupportedReparsePointType => unreachable, // Windows-only
   6619             error.NetworkNotFound => unreachable, // Windows-only
   6620             else => |e| return e,
   6621         },
   6622         .freebsd, .dragonfly => {
   6623             const current_thread = Thread.getCurrent(t);
   6624             try current_thread.checkCancel();
   6625             var mib: [4]c_int = .{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
   6626             var out_len: usize = out_buffer.len;
   6627             try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
   6628             return out_len;
   6629         },
   6630         .netbsd => {
   6631             const current_thread = Thread.getCurrent(t);
   6632             try current_thread.checkCancel();
   6633             var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
   6634             var out_len: usize = out_buffer.len;
   6635             try posix.sysctl(&mib, out_buffer.ptr, &out_len, null, 0);
   6636             return out_len;
   6637         },
   6638         .openbsd, .haiku => {
   6639             // OpenBSD doesn't support getting the path of a running process, so try to guess it
   6640             if (std.os.argv.len == 0)
   6641                 return error.FileNotFound;
   6642 
   6643             const argv0 = std.mem.span(std.os.argv[0]);
   6644             if (std.mem.indexOf(u8, argv0, "/") != null) {
   6645                 // argv[0] is a path (relative or absolute): use realpath(3) directly
   6646                 var real_path_buf: [posix.PATH_MAX]u8 = undefined;
   6647                 const real_path = Io.Dir.realPathAbsolute(ioBasic(t), std.os.argv[0], &real_path_buf) catch |err| switch (err) {
   6648                     error.NetworkNotFound => unreachable, // Windows-only
   6649                     else => |e| return e,
   6650                 };
   6651                 if (real_path.len > out_buffer.len)
   6652                     return error.NameTooLong;
   6653                 const result = out_buffer[0..real_path.len];
   6654                 @memcpy(result, real_path);
   6655                 return result.len;
   6656             } else if (argv0.len != 0) {
   6657                 // argv[0] is not empty (and not a path): search it inside PATH
   6658                 const PATH = posix.getenvZ("PATH") orelse return error.FileNotFound;
   6659                 var path_it = std.mem.tokenizeScalar(u8, PATH, std.fs.path.delimiter);
   6660                 while (path_it.next()) |a_path| {
   6661                     var resolved_path_buf: [posix.PATH_MAX - 1:0]u8 = undefined;
   6662                     const resolved_path = std.fmt.bufPrintSentinel(&resolved_path_buf, "{s}/{s}", .{
   6663                         a_path, std.os.argv[0],
   6664                     }, 0) catch continue;
   6665 
   6666                     var real_path_buf: [posix.PATH_MAX]u8 = undefined;
   6667                     if (Io.Dir.realPathAbsolute(ioBasic(t), resolved_path, &real_path_buf)) |real_path| {
   6668                         // found a file, and hope it is the right file
   6669                         if (real_path.len > out_buffer.len)
   6670                             return error.NameTooLong;
   6671                         const result = out_buffer[0..real_path.len];
   6672                         @memcpy(result, real_path);
   6673                         return result.len;
   6674                     } else |_| continue;
   6675                 }
   6676             }
   6677             return error.FileNotFound;
   6678         },
   6679         .windows => {
   6680             const current_thread = Thread.getCurrent(t);
   6681             try current_thread.checkCancel();
   6682             const w = windows;
   6683             const image_path_unicode_string = &w.peb().ProcessParameters.ImagePathName;
   6684             const image_path_name = image_path_unicode_string.Buffer.?[0 .. image_path_unicode_string.Length / 2 :0];
   6685 
   6686             // If ImagePathName is a symlink, then it will contain the path of the
   6687             // symlink, not the path that the symlink points to. We want the path
   6688             // that the symlink points to, though, so we need to get the realpath.
   6689             var path_name_w = try w.wToPrefixedFileW(null, image_path_name);
   6690 
   6691             const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
   6692             const share_access = w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE;
   6693             const creation = w.FILE_OPEN;
   6694             const h_file = blk: {
   6695                 const res = w.OpenFile(path_name_w.span(), .{
   6696                     .dir = null,
   6697                     .access_mask = access_mask,
   6698                     .share_access = share_access,
   6699                     .creation = creation,
   6700                     .filter = .any,
   6701                 }) catch |err| switch (err) {
   6702                     error.WouldBlock => unreachable,
   6703                     else => |e| return e,
   6704                 };
   6705                 break :blk res;
   6706             };
   6707             defer w.CloseHandle(h_file);
   6708 
   6709             const wide_slice = w.GetFinalPathNameByHandle(h_file, .{}, out_buffer);
   6710 
   6711             const len = std.unicode.calcWtf8Len(wide_slice);
   6712             if (len > out_buffer.len)
   6713                 return error.NameTooLong;
   6714 
   6715             const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
   6716             return end_index;
   6717         },
   6718         else => @compileError("unsupported OS"),
   6719     }
   6720 }
   6721 
   6722 fn fileWritePositional(
   6723     userdata: ?*anyopaque,
   6724     file: File,
   6725     header: []const u8,
   6726     data: []const []const u8,
   6727     splat: usize,
   6728     offset: u64,
   6729 ) File.WritePositionalError!usize {
   6730     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6731     const current_thread = Thread.getCurrent(t);
   6732 
   6733     if (is_windows) @panic("TODO");
   6734 
   6735     var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
   6736     var iovlen: iovlen_t = 0;
   6737     addBuf(&iovecs, &iovlen, header);
   6738     for (data[0 .. data.len - 1]) |bytes| addBuf(&iovecs, &iovlen, bytes);
   6739     const pattern = data[data.len - 1];
   6740     if (iovecs.len - iovlen != 0) switch (splat) {
   6741         0 => {},
   6742         1 => addBuf(&iovecs, &iovlen, pattern),
   6743         else => switch (pattern.len) {
   6744             0 => {},
   6745             1 => {
   6746                 var backup_buffer: [splat_buffer_size]u8 = undefined;
   6747                 const splat_buffer = &backup_buffer;
   6748                 const memset_len = @min(splat_buffer.len, splat);
   6749                 const buf = splat_buffer[0..memset_len];
   6750                 @memset(buf, pattern[0]);
   6751                 addBuf(&iovecs, &iovlen, buf);
   6752                 var remaining_splat = splat - buf.len;
   6753                 while (remaining_splat > splat_buffer.len and iovecs.len - iovlen != 0) {
   6754                     assert(buf.len == splat_buffer.len);
   6755                     addBuf(&iovecs, &iovlen, splat_buffer);
   6756                     remaining_splat -= splat_buffer.len;
   6757                 }
   6758                 addBuf(&iovecs, &iovlen, splat_buffer[0..remaining_splat]);
   6759             },
   6760             else => for (0..@min(splat, iovecs.len - iovlen)) |_| {
   6761                 addBuf(&iovecs, &iovlen, pattern);
   6762             },
   6763         },
   6764     };
   6765 
   6766     if (native_os == .wasi and !builtin.link_libc) {
   6767         var n_written: usize = undefined;
   6768         try current_thread.beginSyscall();
   6769         while (true) {
   6770             switch (std.os.wasi.fd_pwrite(file.handle, &iovecs, iovlen, offset, &n_written)) {
   6771                 .SUCCESS => {
   6772                     current_thread.endSyscall();
   6773                     return n_written;
   6774                 },
   6775                 .INTR => {
   6776                     try current_thread.checkCancel();
   6777                     continue;
   6778                 },
   6779                 .CANCELED => return current_thread.endSyscallCanceled(),
   6780                 else => |e| {
   6781                     current_thread.endSyscall();
   6782                     switch (e) {
   6783                         .INVAL => |err| return errnoBug(err),
   6784                         .FAULT => |err| return errnoBug(err),
   6785                         .AGAIN => |err| return errnoBug(err),
   6786                         .BADF => return error.NotOpenForWriting, // can be a race condition.
   6787                         .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called.
   6788                         .DQUOT => return error.DiskQuota,
   6789                         .FBIG => return error.FileTooBig,
   6790                         .IO => return error.InputOutput,
   6791                         .NOSPC => return error.NoSpaceLeft,
   6792                         .PERM => return error.PermissionDenied,
   6793                         .PIPE => return error.BrokenPipe,
   6794                         .NOTCAPABLE => return error.AccessDenied,
   6795                         .NXIO => return error.Unseekable,
   6796                         .SPIPE => return error.Unseekable,
   6797                         .OVERFLOW => return error.Unseekable,
   6798                         else => |err| return posix.unexpectedErrno(err),
   6799                     }
   6800                 },
   6801             }
   6802         }
   6803     }
   6804 
   6805     try current_thread.beginSyscall();
   6806     while (true) {
   6807         const rc = pwritev_sym(file.handle, &iovecs, iovlen, @bitCast(offset));
   6808         switch (posix.errno(rc)) {
   6809             .SUCCESS => {
   6810                 current_thread.endSyscall();
   6811                 return @intCast(rc);
   6812             },
   6813             .INTR => {
   6814                 try current_thread.checkCancel();
   6815                 continue;
   6816             },
   6817             .CANCELED => return current_thread.endSyscallCanceled(),
   6818             else => |e| {
   6819                 current_thread.endSyscall();
   6820                 switch (e) {
   6821                     .INVAL => return error.InvalidArgument,
   6822                     .FAULT => |err| return errnoBug(err),
   6823                     .AGAIN => return error.WouldBlock,
   6824                     .BADF => return error.NotOpenForWriting, // Usually a race condition.
   6825                     .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called.
   6826                     .DQUOT => return error.DiskQuota,
   6827                     .FBIG => return error.FileTooBig,
   6828                     .IO => return error.InputOutput,
   6829                     .NOSPC => return error.NoSpaceLeft,
   6830                     .PERM => return error.PermissionDenied,
   6831                     .PIPE => return error.BrokenPipe,
   6832                     .CONNRESET => |err| return errnoBug(err), // Not a socket handle.
   6833                     .BUSY => return error.DeviceBusy,
   6834                     .TXTBSY => return error.FileBusy,
   6835                     .NXIO => return error.Unseekable,
   6836                     .SPIPE => return error.Unseekable,
   6837                     .OVERFLOW => return error.Unseekable,
   6838                     else => |err| return posix.unexpectedErrno(err),
   6839                 }
   6840             },
   6841         }
   6842     }
   6843 }
   6844 
   6845 fn fileWriteStreaming(
   6846     userdata: ?*anyopaque,
   6847     file: File,
   6848     header: []const u8,
   6849     data: []const []const u8,
   6850     splat: usize,
   6851 ) File.Writer.Error!usize {
   6852     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6853     const current_thread = Thread.getCurrent(t);
   6854 
   6855     if (is_windows) @panic("TODO");
   6856 
   6857     var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
   6858     var iovlen: iovlen_t = 0;
   6859     addBuf(&iovecs, &iovlen, header);
   6860     for (data[0 .. data.len - 1]) |bytes| addBuf(&iovecs, &iovlen, bytes);
   6861     const pattern = data[data.len - 1];
   6862     if (iovecs.len - iovlen != 0) switch (splat) {
   6863         0 => {},
   6864         1 => addBuf(&iovecs, &iovlen, pattern),
   6865         else => switch (pattern.len) {
   6866             0 => {},
   6867             1 => {
   6868                 var backup_buffer: [splat_buffer_size]u8 = undefined;
   6869                 const splat_buffer = &backup_buffer;
   6870                 const memset_len = @min(splat_buffer.len, splat);
   6871                 const buf = splat_buffer[0..memset_len];
   6872                 @memset(buf, pattern[0]);
   6873                 addBuf(&iovecs, &iovlen, buf);
   6874                 var remaining_splat = splat - buf.len;
   6875                 while (remaining_splat > splat_buffer.len and iovecs.len - iovlen != 0) {
   6876                     assert(buf.len == splat_buffer.len);
   6877                     addBuf(&iovecs, &iovlen, splat_buffer);
   6878                     remaining_splat -= splat_buffer.len;
   6879                 }
   6880                 addBuf(&iovecs, &iovlen, splat_buffer[0..remaining_splat]);
   6881             },
   6882             else => for (0..@min(splat, iovecs.len - iovlen)) |_| {
   6883                 addBuf(&iovecs, &iovlen, pattern);
   6884             },
   6885         },
   6886     };
   6887 
   6888     if (native_os == .wasi and !builtin.link_libc) {
   6889         var n_written: usize = undefined;
   6890         try current_thread.beginSyscall();
   6891         while (true) {
   6892             switch (std.os.wasi.fd_write(file.handle, &iovecs, iovlen, &n_written)) {
   6893                 .SUCCESS => {
   6894                     current_thread.endSyscall();
   6895                     return n_written;
   6896                 },
   6897                 .INTR => {
   6898                     try current_thread.checkCancel();
   6899                     continue;
   6900                 },
   6901                 .CANCELED => return current_thread.endSyscallCanceled(),
   6902                 else => |e| {
   6903                     current_thread.endSyscall();
   6904                     switch (e) {
   6905                         .INVAL => |err| return errnoBug(err),
   6906                         .FAULT => |err| return errnoBug(err),
   6907                         .AGAIN => |err| return errnoBug(err),
   6908                         .BADF => return error.NotOpenForWriting, // can be a race condition.
   6909                         .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called.
   6910                         .DQUOT => return error.DiskQuota,
   6911                         .FBIG => return error.FileTooBig,
   6912                         .IO => return error.InputOutput,
   6913                         .NOSPC => return error.NoSpaceLeft,
   6914                         .PERM => return error.PermissionDenied,
   6915                         .PIPE => return error.BrokenPipe,
   6916                         .NOTCAPABLE => return error.AccessDenied,
   6917                         else => |err| return posix.unexpectedErrno(err),
   6918                     }
   6919                 },
   6920             }
   6921         }
   6922     }
   6923 
   6924     try current_thread.beginSyscall();
   6925     while (true) {
   6926         const rc = posix.system.writev(file.handle, &iovecs, iovlen);
   6927         switch (posix.errno(rc)) {
   6928             .SUCCESS => {
   6929                 current_thread.endSyscall();
   6930                 return @intCast(rc);
   6931             },
   6932             .INTR => {
   6933                 try current_thread.checkCancel();
   6934                 continue;
   6935             },
   6936             .CANCELED => return current_thread.endSyscallCanceled(),
   6937             else => |e| {
   6938                 current_thread.endSyscall();
   6939                 switch (e) {
   6940                     .INVAL => return error.InvalidArgument,
   6941                     .FAULT => |err| return errnoBug(err),
   6942                     .AGAIN => return error.WouldBlock,
   6943                     .BADF => return error.NotOpenForWriting, // Can be a race condition.
   6944                     .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called.
   6945                     .DQUOT => return error.DiskQuota,
   6946                     .FBIG => return error.FileTooBig,
   6947                     .IO => return error.InputOutput,
   6948                     .NOSPC => return error.NoSpaceLeft,
   6949                     .PERM => return error.PermissionDenied,
   6950                     .PIPE => return error.BrokenPipe,
   6951                     .CONNRESET => |err| return errnoBug(err), // Not a socket handle.
   6952                     .BUSY => return error.DeviceBusy,
   6953                     else => |err| return posix.unexpectedErrno(err),
   6954                 }
   6955             },
   6956         }
   6957     }
   6958 }
   6959 
   6960 fn fileWriteFileStreaming(
   6961     userdata: ?*anyopaque,
   6962     file: File,
   6963     header: []const u8,
   6964     file_reader: *File.Reader,
   6965     limit: Io.Limit,
   6966 ) File.Writer.WriteFileError!usize {
   6967     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6968     const reader_buffered = file_reader.interface.buffered();
   6969     if (reader_buffered.len >= @intFromEnum(limit)) {
   6970         const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
   6971         file_reader.interface.toss(n -| header.len);
   6972         return n;
   6973     }
   6974     const file_limit = @intFromEnum(limit) - reader_buffered.len;
   6975     const out_fd = file.handle;
   6976     const in_fd = file_reader.file.handle;
   6977 
   6978     if (file_reader.size) |size| {
   6979         if (size - file_reader.pos == 0) {
   6980             if (reader_buffered.len != 0) {
   6981                 const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
   6982                 file_reader.interface.toss(n -| header.len);
   6983                 return n;
   6984             } else {
   6985                 return error.EndOfStream;
   6986             }
   6987         }
   6988     }
   6989 
   6990     if (native_os == .freebsd) sf: {
   6991         // Try using sendfile on FreeBSD.
   6992         if (@atomicLoad(UseSendfile, &t.use_sendfile, .monotonic) == .disabled) break :sf;
   6993         const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf;
   6994         var hdtr_data: std.c.sf_hdtr = undefined;
   6995         var headers: [2]posix.iovec_const = undefined;
   6996         var headers_i: u8 = 0;
   6997         if (header.len != 0) {
   6998             headers[headers_i] = .{ .base = header.ptr, .len = header.len };
   6999             headers_i += 1;
   7000         }
   7001         if (reader_buffered.len != 0) {
   7002             headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len };
   7003             headers_i += 1;
   7004         }
   7005         const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: {
   7006             hdtr_data = .{
   7007                 .headers = &headers,
   7008                 .hdr_cnt = headers_i,
   7009                 .trailers = null,
   7010                 .trl_cnt = 0,
   7011             };
   7012             break :b &hdtr_data;
   7013         };
   7014         var sbytes: std.c.off_t = 0;
   7015         const nbytes: usize = @min(file_limit, std.math.maxInt(usize));
   7016         const flags = 0;
   7017 
   7018         const current_thread = Thread.getCurrent(t);
   7019         try current_thread.beginSyscall();
   7020         while (true) {
   7021             switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, nbytes, hdtr, &sbytes, flags))) {
   7022                 .SUCCESS => {
   7023                     current_thread.endSyscall();
   7024                     break;
   7025                 },
   7026                 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
   7027                     // Give calling code chance to observe before trying
   7028                     // something else.
   7029                     current_thread.endSyscall();
   7030                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
   7031                     return 0;
   7032                 },
   7033                 .INTR, .BUSY => {
   7034                     if (sbytes == 0) {
   7035                         try current_thread.checkCancel();
   7036                         continue;
   7037                     } else {
   7038                         // Even if we are being canceled, there have been side
   7039                         // effects, so it is better to report those side
   7040                         // effects to the caller.
   7041                         current_thread.endSyscall();
   7042                         break;
   7043                     }
   7044                 },
   7045                 .AGAIN => {
   7046                     current_thread.endSyscall();
   7047                     if (sbytes == 0) return error.WouldBlock;
   7048                     break;
   7049                 },
   7050                 else => |e| {
   7051                     current_thread.endSyscall();
   7052                     assert(error.Unexpected == switch (e) {
   7053                         .NOTCONN => return error.BrokenPipe,
   7054                         .IO => return error.InputOutput,
   7055                         .PIPE => return error.BrokenPipe,
   7056                         .NOBUFS => return error.SystemResources,
   7057                         .BADF => |err| errnoBug(err),
   7058                         .FAULT => |err| errnoBug(err),
   7059                         else => |err| posix.unexpectedErrno(err),
   7060                     });
   7061                     // Give calling code chance to observe the error before trying
   7062                     // something else.
   7063                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
   7064                     return 0;
   7065                 },
   7066             }
   7067         }
   7068         if (sbytes == 0) {
   7069             file_reader.size = file_reader.pos;
   7070             return error.EndOfStream;
   7071         }
   7072         const ubytes: usize = @intCast(sbytes);
   7073         file_reader.interface.toss(ubytes -| header.len);
   7074         return ubytes;
   7075     }
   7076 
   7077     if (is_darwin) sf: {
   7078         // Try using sendfile on macOS.
   7079         if (@atomicLoad(UseSendfile, &t.use_sendfile, .monotonic) == .disabled) break :sf;
   7080         const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf;
   7081         var hdtr_data: std.c.sf_hdtr = undefined;
   7082         var headers: [2]posix.iovec_const = undefined;
   7083         var headers_i: u8 = 0;
   7084         if (header.len != 0) {
   7085             headers[headers_i] = .{ .base = header.ptr, .len = header.len };
   7086             headers_i += 1;
   7087         }
   7088         if (reader_buffered.len != 0) {
   7089             headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len };
   7090             headers_i += 1;
   7091         }
   7092         const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: {
   7093             hdtr_data = .{
   7094                 .headers = &headers,
   7095                 .hdr_cnt = headers_i,
   7096                 .trailers = null,
   7097                 .trl_cnt = 0,
   7098             };
   7099             break :b &hdtr_data;
   7100         };
   7101         const max_count = std.math.maxInt(i32); // Avoid EINVAL.
   7102         var len: std.c.off_t = @min(file_limit, max_count);
   7103         const flags = 0;
   7104         const current_thread = Thread.getCurrent(t);
   7105         try current_thread.beginSyscall();
   7106         while (true) {
   7107             switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, &len, hdtr, flags))) {
   7108                 .SUCCESS => {
   7109                     current_thread.endSyscall();
   7110                     break;
   7111                 },
   7112                 .OPNOTSUPP, .NOTSOCK, .NOSYS => {
   7113                     // Give calling code chance to observe before trying
   7114                     // something else.
   7115                     current_thread.endSyscall();
   7116                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
   7117                     return 0;
   7118                 },
   7119                 .INTR => {
   7120                     if (len == 0) {
   7121                         try current_thread.checkCancel();
   7122                         continue;
   7123                     } else {
   7124                         // Even if we are being canceled, there have been side
   7125                         // effects, so it is better to report those side
   7126                         // effects to the caller.
   7127                         current_thread.endSyscall();
   7128                         break;
   7129                     }
   7130                 },
   7131                 .AGAIN => {
   7132                     current_thread.endSyscall();
   7133                     if (len == 0) return error.WouldBlock;
   7134                     break;
   7135                 },
   7136                 else => |e| {
   7137                     current_thread.endSyscall();
   7138                     assert(error.Unexpected == switch (e) {
   7139                         .NOTCONN => return error.BrokenPipe,
   7140                         .IO => return error.InputOutput,
   7141                         .PIPE => return error.BrokenPipe,
   7142                         .BADF => |err| errnoBug(err),
   7143                         .FAULT => |err| errnoBug(err),
   7144                         .INVAL => |err| errnoBug(err),
   7145                         else => |err| posix.unexpectedErrno(err),
   7146                     });
   7147                     // Give calling code chance to observe the error before trying
   7148                     // something else.
   7149                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
   7150                     return 0;
   7151                 },
   7152             }
   7153         }
   7154         if (len == 0) {
   7155             file_reader.size = file_reader.pos;
   7156             return error.EndOfStream;
   7157         }
   7158         const u_len: usize = @bitCast(len);
   7159         file_reader.interface.toss(u_len -| header.len);
   7160         return u_len;
   7161     }
   7162 
   7163     if (native_os == .linux) sf: {
   7164         // Try using sendfile on Linux.
   7165         if (@atomicLoad(UseSendfile, &t.use_sendfile, .monotonic) == .disabled) break :sf;
   7166         // Linux sendfile does not support headers.
   7167         if (header.len != 0 or reader_buffered.len != 0) {
   7168             const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
   7169             file_reader.interface.toss(n -| header.len);
   7170             return n;
   7171         }
   7172         const max_count = 0x7ffff000; // Avoid EINVAL.
   7173         var off: std.os.linux.off_t = undefined;
   7174         const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
   7175             .positional => o: {
   7176                 const size = file_reader.getSize() catch return 0;
   7177                 off = std.math.cast(std.os.linux.off_t, file_reader.pos) orelse return error.ReadFailed;
   7178                 break :o .{ &off, @min(@intFromEnum(limit), size - file_reader.pos, max_count) };
   7179             },
   7180             .streaming => .{ null, limit.minInt(max_count) },
   7181             .streaming_reading, .positional_reading => break :sf,
   7182             .failure => return error.ReadFailed,
   7183         };
   7184         const current_thread = Thread.getCurrent(t);
   7185         try current_thread.beginSyscall();
   7186         const n: usize = while (true) {
   7187             const rc = sendfile_sym(out_fd, in_fd, off_ptr, count);
   7188             switch (posix.errno(rc)) {
   7189                 .SUCCESS => {
   7190                     current_thread.endSyscall();
   7191                     break @intCast(rc);
   7192                 },
   7193                 .NOSYS, .INVAL => {
   7194                     // Give calling code chance to observe before trying
   7195                     // something else.
   7196                     current_thread.endSyscall();
   7197                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
   7198                     return 0;
   7199                 },
   7200                 .INTR => {
   7201                     try current_thread.checkCancel();
   7202                     continue;
   7203                 },
   7204                 .CANCELED => return current_thread.endSyscallCanceled(),
   7205                 else => |e| {
   7206                     current_thread.endSyscall();
   7207                     assert(error.Unexpected == switch (e) {
   7208                         .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
   7209                         .AGAIN => return error.WouldBlock,
   7210                         .IO => return error.InputOutput,
   7211                         .PIPE => return error.BrokenPipe,
   7212                         .NOMEM => return error.SystemResources,
   7213                         .NXIO, .SPIPE => {
   7214                             file_reader.mode = file_reader.mode.toStreaming();
   7215                             const pos = file_reader.pos;
   7216                             if (pos != 0) {
   7217                                 file_reader.pos = 0;
   7218                                 file_reader.seekBy(@intCast(pos)) catch {
   7219                                     file_reader.mode = .failure;
   7220                                     return error.ReadFailed;
   7221                                 };
   7222                             }
   7223                             return 0;
   7224                         },
   7225                         .BADF => |err| errnoBug(err), // Always a race condition.
   7226                         .FAULT => |err| errnoBug(err), // Segmentation fault.
   7227                         .OVERFLOW => |err| errnoBug(err), // We avoid passing too large of a `count`.
   7228                         else => |err| posix.unexpectedErrno(err),
   7229                     });
   7230                     // Give calling code chance to observe the error before trying
   7231                     // something else.
   7232                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
   7233                     return 0;
   7234                 },
   7235             }
   7236         };
   7237         if (n == 0) {
   7238             file_reader.size = file_reader.pos;
   7239             return error.EndOfStream;
   7240         }
   7241         file_reader.pos += n;
   7242         return n;
   7243     }
   7244 
   7245     if (have_copy_file_range) cfr: {
   7246         if (@atomicLoad(UseCopyFileRange, &t.use_copy_file_range, .monotonic) == .disabled) break :cfr;
   7247         if (header.len != 0 or reader_buffered.len != 0) {
   7248             const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
   7249             file_reader.interface.toss(n -| header.len);
   7250             return n;
   7251         }
   7252         var off_in: i64 = undefined;
   7253         const off_in_ptr: ?*i64 = switch (file_reader.mode) {
   7254             .positional_reading, .streaming_reading => return error.Unimplemented,
   7255             .positional => p: {
   7256                 off_in = @intCast(file_reader.pos);
   7257                 break :p &off_in;
   7258             },
   7259             .streaming => null,
   7260             .failure => return error.ReadFailed,
   7261         };
   7262         const current_thread = Thread.getCurrent(t);
   7263         const n: usize = switch (native_os) {
   7264             .linux => n: {
   7265                 try current_thread.beginSyscall();
   7266                 while (true) {
   7267                     const rc = linux_copy_file_range_sys.copy_file_range(in_fd, off_in_ptr, out_fd, null, @intFromEnum(limit), 0);
   7268                     switch (linux_copy_file_range_sys.errno(rc)) {
   7269                         .SUCCESS => {
   7270                             current_thread.endSyscall();
   7271                             break :n @intCast(rc);
   7272                         },
   7273                         .INTR => {
   7274                             try current_thread.checkCancel();
   7275                             continue;
   7276                         },
   7277                         .CANCELED => return current_thread.endSyscallCanceled(),
   7278                         .OPNOTSUPP, .INVAL, .NOSYS => {
   7279                             // Give calling code chance to observe before trying
   7280                             // something else.
   7281                             current_thread.endSyscall();
   7282                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
   7283                             return 0;
   7284                         },
   7285                         else => |e| {
   7286                             current_thread.endSyscall();
   7287                             assert(error.Unexpected == switch (e) {
   7288                                 .FBIG => return error.FileTooBig,
   7289                                 .IO => return error.InputOutput,
   7290                                 .NOMEM => return error.SystemResources,
   7291                                 .NOSPC => return error.NoSpaceLeft,
   7292                                 .OVERFLOW => |err| errnoBug(err), // We avoid passing too large a count.
   7293                                 .PERM => return error.PermissionDenied,
   7294                                 .BUSY => return error.DeviceBusy,
   7295                                 .TXTBSY => return error.FileBusy,
   7296                                 // copy_file_range can still work but not on
   7297                                 // this pair of file descriptors.
   7298                                 .XDEV => return error.Unimplemented,
   7299                                 .ISDIR => |err| errnoBug(err),
   7300                                 .BADF => |err| errnoBug(err),
   7301                                 else => |err| posix.unexpectedErrno(err),
   7302                             });
   7303                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
   7304                             return 0;
   7305                         },
   7306                     }
   7307                 }
   7308             },
   7309             .freebsd => n: {
   7310                 try current_thread.beginSyscall();
   7311                 while (true) {
   7312                     const rc = std.c.copy_file_range(in_fd, off_in_ptr, out_fd, null, @intFromEnum(limit), 0);
   7313                     switch (std.c.errno(rc)) {
   7314                         .SUCCESS => {
   7315                             current_thread.endSyscall();
   7316                             break :n @intCast(rc);
   7317                         },
   7318                         .INTR => {
   7319                             try current_thread.checkCancel();
   7320                             continue;
   7321                         },
   7322                         .OPNOTSUPP, .INVAL, .NOSYS => {
   7323                             // Give calling code chance to observe before trying
   7324                             // something else.
   7325                             current_thread.endSyscall();
   7326                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
   7327                             return 0;
   7328                         },
   7329                         else => |e| {
   7330                             current_thread.endSyscall();
   7331                             assert(error.Unexpected == switch (e) {
   7332                                 .FBIG => return error.FileTooBig,
   7333                                 .IO => return error.InputOutput,
   7334                                 .INTEGRITY => return error.CorruptedData,
   7335                                 .ISDIR => return error.IsDir,
   7336                                 .NOSPC => return error.NoSpaceLeft,
   7337                                 .BADF => |err| errnoBug(err),
   7338                                 else => |err| posix.unexpectedErrno(err),
   7339                             });
   7340                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
   7341                             return 0;
   7342                         },
   7343                     }
   7344                 }
   7345             },
   7346             else => comptime unreachable,
   7347         };
   7348         if (n == 0) {
   7349             file_reader.size = file_reader.pos;
   7350             return error.EndOfStream;
   7351         }
   7352         file_reader.pos += n;
   7353         return n;
   7354     }
   7355 
   7356     return error.Unimplemented;
   7357 }
   7358 
   7359 fn netWriteFile(
   7360     userdata: ?*anyopaque,
   7361     socket_handle: net.Socket.Handle,
   7362     header: []const u8,
   7363     file_reader: *File.Reader,
   7364     limit: Io.Limit,
   7365 ) net.Stream.Writer.WriteFileError!usize {
   7366     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7367     _ = t;
   7368     _ = socket_handle;
   7369     _ = header;
   7370     _ = file_reader;
   7371     _ = limit;
   7372     @panic("TODO");
   7373 }
   7374 
   7375 fn netWriteFileUnavailable(
   7376     userdata: ?*anyopaque,
   7377     socket_handle: net.Socket.Handle,
   7378     header: []const u8,
   7379     file_reader: *File.Reader,
   7380     limit: Io.Limit,
   7381 ) net.Stream.Writer.WriteFileError!usize {
   7382     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7383     _ = t;
   7384     _ = socket_handle;
   7385     _ = header;
   7386     _ = file_reader;
   7387     _ = limit;
   7388     return error.NetworkDown;
   7389 }
   7390 
   7391 fn fileWriteFilePositional(
   7392     userdata: ?*anyopaque,
   7393     file: File,
   7394     header: []const u8,
   7395     file_reader: *File.Reader,
   7396     limit: Io.Limit,
   7397     offset: u64,
   7398 ) File.WriteFilePositionalError!usize {
   7399     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7400     const reader_buffered = file_reader.interface.buffered();
   7401     if (reader_buffered.len >= @intFromEnum(limit)) {
   7402         const n = try fileWritePositional(t, file, header, &.{limit.slice(reader_buffered)}, 1, offset);
   7403         file_reader.interface.toss(n -| header.len);
   7404         return n;
   7405     }
   7406     const out_fd = file.handle;
   7407     const in_fd = file_reader.file.handle;
   7408 
   7409     if (file_reader.size) |size| {
   7410         if (size - file_reader.pos == 0) {
   7411             if (reader_buffered.len != 0) {
   7412                 const n = try fileWritePositional(t, file, header, &.{limit.slice(reader_buffered)}, 1, offset);
   7413                 file_reader.interface.toss(n -| header.len);
   7414                 return n;
   7415             } else {
   7416                 return error.EndOfStream;
   7417             }
   7418         }
   7419     }
   7420 
   7421     if (have_copy_file_range) cfr: {
   7422         if (@atomicLoad(UseCopyFileRange, &t.use_copy_file_range, .monotonic) == .disabled) break :cfr;
   7423         if (header.len != 0 or reader_buffered.len != 0) {
   7424             const n = try fileWritePositional(t, file, header, &.{limit.slice(reader_buffered)}, 1, offset);
   7425             file_reader.interface.toss(n -| header.len);
   7426             return n;
   7427         }
   7428         var off_in: i64 = undefined;
   7429         const off_in_ptr: ?*i64 = switch (file_reader.mode) {
   7430             .positional_reading, .streaming_reading => return error.Unimplemented,
   7431             .positional => p: {
   7432                 off_in = @intCast(file_reader.pos);
   7433                 break :p &off_in;
   7434             },
   7435             .streaming => null,
   7436             .failure => return error.ReadFailed,
   7437         };
   7438         var off_out: i64 = @intCast(offset);
   7439         const current_thread = Thread.getCurrent(t);
   7440         const n: usize = switch (native_os) {
   7441             .linux => n: {
   7442                 try current_thread.beginSyscall();
   7443                 while (true) {
   7444                     const rc = linux_copy_file_range_sys.copy_file_range(in_fd, off_in_ptr, out_fd, &off_out, @intFromEnum(limit), 0);
   7445                     switch (linux_copy_file_range_sys.errno(rc)) {
   7446                         .SUCCESS => {
   7447                             current_thread.endSyscall();
   7448                             break :n @intCast(rc);
   7449                         },
   7450                         .INTR => {
   7451                             try current_thread.checkCancel();
   7452                             continue;
   7453                         },
   7454                         .CANCELED => return current_thread.endSyscallCanceled(),
   7455                         .OPNOTSUPP, .INVAL, .NOSYS => {
   7456                             // Give calling code chance to observe before trying
   7457                             // something else.
   7458                             current_thread.endSyscall();
   7459                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
   7460                             return 0;
   7461                         },
   7462                         else => |e| {
   7463                             current_thread.endSyscall();
   7464                             assert(error.Unexpected == switch (e) {
   7465                                 .FBIG => return error.FileTooBig,
   7466                                 .IO => return error.InputOutput,
   7467                                 .NOMEM => return error.SystemResources,
   7468                                 .NOSPC => return error.NoSpaceLeft,
   7469                                 .OVERFLOW => return error.Unseekable,
   7470                                 .NXIO => return error.Unseekable,
   7471                                 .SPIPE => return error.Unseekable,
   7472                                 .PERM => return error.PermissionDenied,
   7473                                 .TXTBSY => return error.FileBusy,
   7474                                 // copy_file_range can still work but not on
   7475                                 // this pair of file descriptors.
   7476                                 .XDEV => return error.Unimplemented,
   7477                                 .ISDIR => |err| errnoBug(err),
   7478                                 .BADF => |err| errnoBug(err),
   7479                                 else => |err| posix.unexpectedErrno(err),
   7480                             });
   7481                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
   7482                             return 0;
   7483                         },
   7484                     }
   7485                 }
   7486             },
   7487             .freebsd => n: {
   7488                 try current_thread.beginSyscall();
   7489                 while (true) {
   7490                     const rc = std.c.copy_file_range(in_fd, off_in_ptr, out_fd, &off_out, @intFromEnum(limit), 0);
   7491                     switch (std.c.errno(rc)) {
   7492                         .SUCCESS => {
   7493                             current_thread.endSyscall();
   7494                             break :n @intCast(rc);
   7495                         },
   7496                         .INTR => {
   7497                             try current_thread.checkCancel();
   7498                             continue;
   7499                         },
   7500                         .OPNOTSUPP, .INVAL, .NOSYS => {
   7501                             // Give calling code chance to observe before trying
   7502                             // something else.
   7503                             current_thread.endSyscall();
   7504                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
   7505                             return 0;
   7506                         },
   7507                         else => |e| {
   7508                             current_thread.endSyscall();
   7509                             assert(error.Unexpected == switch (e) {
   7510                                 .FBIG => return error.FileTooBig,
   7511                                 .IO => return error.InputOutput,
   7512                                 .INTEGRITY => return error.CorruptedData,
   7513                                 .ISDIR => return error.IsDir,
   7514                                 .NOSPC => return error.NoSpaceLeft,
   7515                                 .OVERFLOW => return error.Unseekable,
   7516                                 .NXIO => return error.Unseekable,
   7517                                 .SPIPE => return error.Unseekable,
   7518                                 .BADF => |err| errnoBug(err),
   7519                                 else => |err| posix.unexpectedErrno(err),
   7520                             });
   7521                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
   7522                             return 0;
   7523                         },
   7524                     }
   7525                 }
   7526             },
   7527             else => comptime unreachable,
   7528         };
   7529         if (n == 0) {
   7530             file_reader.size = file_reader.pos;
   7531             return error.EndOfStream;
   7532         }
   7533         file_reader.pos += n;
   7534         return n;
   7535     }
   7536 
   7537     if (is_darwin) fcf: {
   7538         if (@atomicLoad(UseFcopyfile, &t.use_fcopyfile, .monotonic) == .disabled) break :fcf;
   7539         if (file_reader.pos != 0) break :fcf;
   7540         if (offset != 0) break :fcf;
   7541         if (limit != .unlimited) break :fcf;
   7542         const size = file_reader.getSize() catch break :fcf;
   7543         if (header.len != 0 or reader_buffered.len != 0) {
   7544             const n = try fileWritePositional(t, file, header, &.{limit.slice(reader_buffered)}, 1, offset);
   7545             file_reader.interface.toss(n -| header.len);
   7546             return n;
   7547         }
   7548         const current_thread = Thread.getCurrent(t);
   7549         try current_thread.beginSyscall();
   7550         while (true) {
   7551             const rc = std.c.fcopyfile(in_fd, out_fd, null, .{ .DATA = true });
   7552             switch (posix.errno(rc)) {
   7553                 .SUCCESS => {
   7554                     current_thread.endSyscall();
   7555                     break;
   7556                 },
   7557                 .INTR => {
   7558                     try current_thread.checkCancel();
   7559                     continue;
   7560                 },
   7561                 .OPNOTSUPP => {
   7562                     // Give calling code chance to observe before trying
   7563                     // something else.
   7564                     current_thread.endSyscall();
   7565                     @atomicStore(UseFcopyfile, &t.use_fcopyfile, .disabled, .monotonic);
   7566                     return 0;
   7567                 },
   7568                 else => |e| {
   7569                     current_thread.endSyscall();
   7570                     assert(error.Unexpected == switch (e) {
   7571                         .NOMEM => return error.SystemResources,
   7572                         .INVAL => |err| errnoBug(err),
   7573                         else => |err| posix.unexpectedErrno(err),
   7574                     });
   7575                     return 0;
   7576                 },
   7577             }
   7578         }
   7579         file_reader.pos = size;
   7580         return size;
   7581     }
   7582 
   7583     return error.Unimplemented;
   7584 }
   7585 
   7586 fn nowPosix(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
   7587     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7588     _ = t;
   7589     const clock_id: posix.clockid_t = clockToPosix(clock);
   7590     var tp: posix.timespec = undefined;
   7591     switch (posix.errno(posix.system.clock_gettime(clock_id, &tp))) {
   7592         .SUCCESS => return timestampFromPosix(&tp),
   7593         .INVAL => return error.UnsupportedClock,
   7594         else => |err| return posix.unexpectedErrno(err),
   7595     }
   7596 }
   7597 
   7598 const now = switch (native_os) {
   7599     .windows => nowWindows,
   7600     .wasi => nowWasi,
   7601     else => nowPosix,
   7602 };
   7603 
   7604 fn nowWindows(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
   7605     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7606     _ = t;
   7607     switch (clock) {
   7608         .real => {
   7609             // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds
   7610             // and uses the NTFS/Windows epoch, which is 1601-01-01.
   7611             const epoch_ns = std.time.epoch.windows * std.time.ns_per_s;
   7612             return .{ .nanoseconds = @as(i96, windows.ntdll.RtlGetSystemTimePrecise()) * 100 + epoch_ns };
   7613         },
   7614         .awake, .boot => {
   7615             // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
   7616             const qpc = windows.QueryPerformanceCounter();
   7617             // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA
   7618             // (a read-only page of info updated and mapped by the kernel to all processes):
   7619             // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
   7620             // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
   7621             const qpf = windows.QueryPerformanceFrequency();
   7622 
   7623             // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
   7624             // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
   7625             const common_qpf = 10_000_000;
   7626             if (qpf == common_qpf) return .{ .nanoseconds = qpc * (std.time.ns_per_s / common_qpf) };
   7627 
   7628             // Convert to ns using fixed point.
   7629             const scale = @as(u64, std.time.ns_per_s << 32) / @as(u32, @intCast(qpf));
   7630             const result = (@as(u96, qpc) * scale) >> 32;
   7631             return .{ .nanoseconds = @intCast(result) };
   7632         },
   7633         .cpu_process,
   7634         .cpu_thread,
   7635         => return error.UnsupportedClock,
   7636     }
   7637 }
   7638 
   7639 fn nowWasi(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.Error!Io.Timestamp {
   7640     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7641     _ = t;
   7642     var ns: std.os.wasi.timestamp_t = undefined;
   7643     const err = std.os.wasi.clock_time_get(clockToWasi(clock), 1, &ns);
   7644     if (err != .SUCCESS) return error.Unexpected;
   7645     return .fromNanoseconds(ns);
   7646 }
   7647 
   7648 const sleep = switch (native_os) {
   7649     .windows => sleepWindows,
   7650     .wasi => sleepWasi,
   7651     .linux => sleepLinux,
   7652     else => sleepPosix,
   7653 };
   7654 
   7655 fn sleepLinux(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
   7656     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7657     const current_thread = Thread.getCurrent(t);
   7658     const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
   7659         .none => .awake,
   7660         .duration => |d| d.clock,
   7661         .deadline => |d| d.clock,
   7662     });
   7663     const deadline_nanoseconds: i96 = switch (timeout) {
   7664         .none => std.math.maxInt(i96),
   7665         .duration => |duration| duration.raw.nanoseconds,
   7666         .deadline => |deadline| deadline.raw.nanoseconds,
   7667     };
   7668     var timespec: posix.timespec = timestampToPosix(deadline_nanoseconds);
   7669     try current_thread.beginSyscall();
   7670     while (true) {
   7671         switch (std.os.linux.errno(std.os.linux.clock_nanosleep(clock_id, .{ .ABSTIME = switch (timeout) {
   7672             .none, .duration => false,
   7673             .deadline => true,
   7674         } }, &timespec, &timespec))) {
   7675             .SUCCESS => {
   7676                 current_thread.endSyscall();
   7677                 return;
   7678             },
   7679             .INTR => {
   7680                 try current_thread.checkCancel();
   7681                 continue;
   7682             },
   7683             .CANCELED => return current_thread.endSyscallCanceled(),
   7684             else => |e| {
   7685                 current_thread.endSyscall();
   7686                 switch (e) {
   7687                     .INVAL => return error.UnsupportedClock,
   7688                     else => |err| return posix.unexpectedErrno(err),
   7689                 }
   7690             },
   7691         }
   7692     }
   7693 }
   7694 
   7695 fn sleepWindows(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
   7696     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7697     const current_thread = Thread.getCurrent(t);
   7698     const t_io = ioBasic(t);
   7699     try current_thread.checkCancel();
   7700     const ms = ms: {
   7701         const d = (try timeout.toDurationFromNow(t_io)) orelse
   7702             break :ms std.math.maxInt(windows.DWORD);
   7703         break :ms std.math.lossyCast(windows.DWORD, d.raw.toMilliseconds());
   7704     };
   7705     // TODO: alertable true with checkCancel in a loop plus deadline
   7706     _ = windows.kernel32.SleepEx(ms, windows.FALSE);
   7707 }
   7708 
   7709 fn sleepWasi(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
   7710     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7711     const current_thread = Thread.getCurrent(t);
   7712     const t_io = ioBasic(t);
   7713     const w = std.os.wasi;
   7714 
   7715     const clock: w.subscription_clock_t = if (try timeout.toDurationFromNow(t_io)) |d| .{
   7716         .id = clockToWasi(d.clock),
   7717         .timeout = std.math.lossyCast(u64, d.raw.nanoseconds),
   7718         .precision = 0,
   7719         .flags = 0,
   7720     } else .{
   7721         .id = .MONOTONIC,
   7722         .timeout = std.math.maxInt(u64),
   7723         .precision = 0,
   7724         .flags = 0,
   7725     };
   7726     const in: w.subscription_t = .{
   7727         .userdata = 0,
   7728         .u = .{
   7729             .tag = .CLOCK,
   7730             .u = .{ .clock = clock },
   7731         },
   7732     };
   7733     var event: w.event_t = undefined;
   7734     var nevents: usize = undefined;
   7735     try current_thread.beginSyscall();
   7736     _ = w.poll_oneoff(&in, &event, 1, &nevents);
   7737     current_thread.endSyscall();
   7738 }
   7739 
   7740 fn sleepPosix(userdata: ?*anyopaque, timeout: Io.Timeout) Io.SleepError!void {
   7741     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7742     const current_thread = Thread.getCurrent(t);
   7743     const t_io = ioBasic(t);
   7744     const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type;
   7745     const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type;
   7746 
   7747     var timespec: posix.timespec = t: {
   7748         const d = (try timeout.toDurationFromNow(t_io)) orelse break :t .{
   7749             .sec = std.math.maxInt(sec_type),
   7750             .nsec = std.math.maxInt(nsec_type),
   7751         };
   7752         break :t timestampToPosix(d.raw.toNanoseconds());
   7753     };
   7754     try current_thread.beginSyscall();
   7755     while (true) {
   7756         switch (posix.errno(posix.system.nanosleep(&timespec, &timespec))) {
   7757             .INTR => {
   7758                 try current_thread.checkCancel();
   7759                 continue;
   7760             },
   7761             .CANCELED => return current_thread.endSyscallCanceled(),
   7762             // This prong handles success as well as unexpected errors.
   7763             else => return current_thread.endSyscall(),
   7764         }
   7765     }
   7766 }
   7767 
   7768 fn select(userdata: ?*anyopaque, futures: []const *Io.AnyFuture) Io.Cancelable!usize {
   7769     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7770 
   7771     var event: Io.Event = .unset;
   7772 
   7773     for (futures, 0..) |future, i| {
   7774         const closure: *AsyncClosure = @ptrCast(@alignCast(future));
   7775         if (@atomicRmw(?*Io.Event, &closure.select_condition, .Xchg, &event, .seq_cst) == AsyncClosure.done_event) {
   7776             for (futures[0..i]) |cleanup_future| {
   7777                 const cleanup_closure: *AsyncClosure = @ptrCast(@alignCast(cleanup_future));
   7778                 if (@atomicRmw(?*Io.Event, &cleanup_closure.select_condition, .Xchg, null, .seq_cst) == AsyncClosure.done_event) {
   7779                     cleanup_closure.event.waitUncancelable(ioBasic(t)); // Ensure no reference to our stack-allocated event.
   7780                 }
   7781             }
   7782             return i;
   7783         }
   7784     }
   7785 
   7786     try event.wait(ioBasic(t));
   7787 
   7788     var result: ?usize = null;
   7789     for (futures, 0..) |future, i| {
   7790         const closure: *AsyncClosure = @ptrCast(@alignCast(future));
   7791         if (@atomicRmw(?*Io.Event, &closure.select_condition, .Xchg, null, .seq_cst) == AsyncClosure.done_event) {
   7792             closure.event.waitUncancelable(ioBasic(t)); // Ensure no reference to our stack-allocated event.
   7793             if (result == null) result = i; // In case multiple are ready, return first.
   7794         }
   7795     }
   7796     return result.?;
   7797 }
   7798 
   7799 fn netListenIpPosix(
   7800     userdata: ?*anyopaque,
   7801     address: IpAddress,
   7802     options: IpAddress.ListenOptions,
   7803 ) IpAddress.ListenError!net.Server {
   7804     if (!have_networking) return error.NetworkDown;
   7805     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7806     const current_thread = Thread.getCurrent(t);
   7807     const family = posixAddressFamily(&address);
   7808     const socket_fd = try openSocketPosix(current_thread, family, .{
   7809         .mode = options.mode,
   7810         .protocol = options.protocol,
   7811     });
   7812     errdefer posix.close(socket_fd);
   7813 
   7814     if (options.reuse_address) {
   7815         try setSocketOption(current_thread, socket_fd, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1);
   7816         if (@hasDecl(posix.SO, "REUSEPORT"))
   7817             try setSocketOption(current_thread, socket_fd, posix.SOL.SOCKET, posix.SO.REUSEPORT, 1);
   7818     }
   7819 
   7820     var storage: PosixAddress = undefined;
   7821     var addr_len = addressToPosix(&address, &storage);
   7822     try posixBind(current_thread, socket_fd, &storage.any, addr_len);
   7823 
   7824     try current_thread.beginSyscall();
   7825     while (true) {
   7826         switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) {
   7827             .SUCCESS => {
   7828                 current_thread.endSyscall();
   7829                 break;
   7830             },
   7831             .INTR => {
   7832                 try current_thread.checkCancel();
   7833                 continue;
   7834             },
   7835             .CANCELED => return current_thread.endSyscallCanceled(),
   7836             else => |e| {
   7837                 current_thread.endSyscall();
   7838                 switch (e) {
   7839                     .ADDRINUSE => return error.AddressInUse,
   7840                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   7841                     else => |err| return posix.unexpectedErrno(err),
   7842                 }
   7843             },
   7844         }
   7845     }
   7846 
   7847     try posixGetSockName(current_thread, socket_fd, &storage.any, &addr_len);
   7848     return .{
   7849         .socket = .{
   7850             .handle = socket_fd,
   7851             .address = addressFromPosix(&storage),
   7852         },
   7853     };
   7854 }
   7855 
   7856 fn netListenIpWindows(
   7857     userdata: ?*anyopaque,
   7858     address: IpAddress,
   7859     options: IpAddress.ListenOptions,
   7860 ) IpAddress.ListenError!net.Server {
   7861     if (!have_networking) return error.NetworkDown;
   7862     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7863     const current_thread = Thread.getCurrent(t);
   7864     const family = posixAddressFamily(&address);
   7865     const socket_handle = try openSocketWsa(t, current_thread, family, .{
   7866         .mode = options.mode,
   7867         .protocol = options.protocol,
   7868     });
   7869     errdefer closeSocketWindows(socket_handle);
   7870 
   7871     if (options.reuse_address)
   7872         try setSocketOptionWsa(t, socket_handle, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1);
   7873 
   7874     var storage: WsaAddress = undefined;
   7875     var addr_len = addressToWsa(&address, &storage);
   7876 
   7877     try current_thread.beginSyscall();
   7878     while (true) {
   7879         const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
   7880         if (rc != ws2_32.SOCKET_ERROR) {
   7881             current_thread.endSyscall();
   7882             break;
   7883         }
   7884         switch (ws2_32.WSAGetLastError()) {
   7885             .EINTR => {
   7886                 try current_thread.checkCancel();
   7887                 continue;
   7888             },
   7889             .NOTINITIALISED => {
   7890                 try initializeWsa(t);
   7891                 try current_thread.checkCancel();
   7892                 continue;
   7893             },
   7894             else => |e| {
   7895                 current_thread.endSyscall();
   7896                 switch (e) {
   7897                     .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   7898                     .EADDRINUSE => return error.AddressInUse,
   7899                     .EADDRNOTAVAIL => return error.AddressUnavailable,
   7900                     .ENOTSOCK => |err| return wsaErrorBug(err),
   7901                     .EFAULT => |err| return wsaErrorBug(err),
   7902                     .EINVAL => |err| return wsaErrorBug(err),
   7903                     .ENOBUFS => return error.SystemResources,
   7904                     .ENETDOWN => return error.NetworkDown,
   7905                     else => |err| return windows.unexpectedWSAError(err),
   7906                 }
   7907             },
   7908         }
   7909     }
   7910 
   7911     try current_thread.beginSyscall();
   7912     while (true) {
   7913         const rc = ws2_32.listen(socket_handle, options.kernel_backlog);
   7914         if (rc != ws2_32.SOCKET_ERROR) {
   7915             current_thread.endSyscall();
   7916             break;
   7917         }
   7918         switch (ws2_32.WSAGetLastError()) {
   7919             .EINTR => {
   7920                 try current_thread.checkCancel();
   7921                 continue;
   7922             },
   7923             .NOTINITIALISED => {
   7924                 try initializeWsa(t);
   7925                 try current_thread.checkCancel();
   7926                 continue;
   7927             },
   7928             else => |e| {
   7929                 current_thread.endSyscall();
   7930                 switch (e) {
   7931                     .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   7932                     .ENETDOWN => return error.NetworkDown,
   7933                     .EADDRINUSE => return error.AddressInUse,
   7934                     .EISCONN => |err| return wsaErrorBug(err),
   7935                     .EINVAL => |err| return wsaErrorBug(err),
   7936                     .EMFILE, .ENOBUFS => return error.SystemResources,
   7937                     .ENOTSOCK => |err| return wsaErrorBug(err),
   7938                     .EOPNOTSUPP => |err| return wsaErrorBug(err),
   7939                     .EINPROGRESS => |err| return wsaErrorBug(err),
   7940                     else => |err| return windows.unexpectedWSAError(err),
   7941                 }
   7942             },
   7943         }
   7944     }
   7945 
   7946     try wsaGetSockName(t, current_thread, socket_handle, &storage.any, &addr_len);
   7947 
   7948     return .{
   7949         .socket = .{
   7950             .handle = socket_handle,
   7951             .address = addressFromWsa(&storage),
   7952         },
   7953     };
   7954 }
   7955 
   7956 fn netListenIpUnavailable(
   7957     userdata: ?*anyopaque,
   7958     address: IpAddress,
   7959     options: IpAddress.ListenOptions,
   7960 ) IpAddress.ListenError!net.Server {
   7961     _ = userdata;
   7962     _ = address;
   7963     _ = options;
   7964     return error.NetworkDown;
   7965 }
   7966 
   7967 fn netListenUnixPosix(
   7968     userdata: ?*anyopaque,
   7969     address: *const net.UnixAddress,
   7970     options: net.UnixAddress.ListenOptions,
   7971 ) net.UnixAddress.ListenError!net.Socket.Handle {
   7972     if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
   7973     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7974     const current_thread = Thread.getCurrent(t);
   7975     const socket_fd = openSocketPosix(current_thread, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
   7976         error.ProtocolUnsupportedBySystem => return error.AddressFamilyUnsupported,
   7977         error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported,
   7978         error.SocketModeUnsupported => return error.AddressFamilyUnsupported,
   7979         error.OptionUnsupported => return error.Unexpected,
   7980         else => |e| return e,
   7981     };
   7982     errdefer posix.close(socket_fd);
   7983 
   7984     var storage: UnixAddress = undefined;
   7985     const addr_len = addressUnixToPosix(address, &storage);
   7986     try posixBindUnix(current_thread, socket_fd, &storage.any, addr_len);
   7987 
   7988     try current_thread.beginSyscall();
   7989     while (true) {
   7990         switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) {
   7991             .SUCCESS => {
   7992                 current_thread.endSyscall();
   7993                 break;
   7994             },
   7995             .INTR => {
   7996                 try current_thread.checkCancel();
   7997                 continue;
   7998             },
   7999             .CANCELED => return current_thread.endSyscallCanceled(),
   8000             else => |e| {
   8001                 current_thread.endSyscall();
   8002                 switch (e) {
   8003                     .ADDRINUSE => return error.AddressInUse,
   8004                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8005                     else => |err| return posix.unexpectedErrno(err),
   8006                 }
   8007             },
   8008         }
   8009     }
   8010 
   8011     return socket_fd;
   8012 }
   8013 
   8014 fn netListenUnixWindows(
   8015     userdata: ?*anyopaque,
   8016     address: *const net.UnixAddress,
   8017     options: net.UnixAddress.ListenOptions,
   8018 ) net.UnixAddress.ListenError!net.Socket.Handle {
   8019     if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
   8020     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8021     const current_thread = Thread.getCurrent(t);
   8022 
   8023     const socket_handle = openSocketWsa(t, current_thread, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
   8024         error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported,
   8025         else => |e| return e,
   8026     };
   8027     errdefer closeSocketWindows(socket_handle);
   8028 
   8029     var storage: WsaAddress = undefined;
   8030     const addr_len = addressUnixToWsa(address, &storage);
   8031 
   8032     try current_thread.beginSyscall();
   8033     while (true) {
   8034         const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
   8035         if (rc != ws2_32.SOCKET_ERROR) break;
   8036         switch (ws2_32.WSAGetLastError()) {
   8037             .EINTR => {
   8038                 try current_thread.checkCancel();
   8039                 continue;
   8040             },
   8041             .NOTINITIALISED => {
   8042                 try initializeWsa(t);
   8043                 try current_thread.checkCancel();
   8044                 continue;
   8045             },
   8046             else => |e| {
   8047                 current_thread.endSyscall();
   8048                 switch (e) {
   8049                     .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   8050                     .EADDRINUSE => return error.AddressInUse,
   8051                     .EADDRNOTAVAIL => return error.AddressUnavailable,
   8052                     .ENOTSOCK => |err| return wsaErrorBug(err),
   8053                     .EFAULT => |err| return wsaErrorBug(err),
   8054                     .EINVAL => |err| return wsaErrorBug(err),
   8055                     .ENOBUFS => return error.SystemResources,
   8056                     .ENETDOWN => return error.NetworkDown,
   8057                     else => |err| return windows.unexpectedWSAError(err),
   8058                 }
   8059             },
   8060         }
   8061     }
   8062 
   8063     while (true) {
   8064         try current_thread.checkCancel();
   8065         const rc = ws2_32.listen(socket_handle, options.kernel_backlog);
   8066         if (rc != ws2_32.SOCKET_ERROR) {
   8067             current_thread.endSyscall();
   8068             return socket_handle;
   8069         }
   8070         switch (ws2_32.WSAGetLastError()) {
   8071             .EINTR => continue,
   8072             .NOTINITIALISED => {
   8073                 try initializeWsa(t);
   8074                 continue;
   8075             },
   8076             else => |e| {
   8077                 current_thread.endSyscall();
   8078                 switch (e) {
   8079                     .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   8080                     .ENETDOWN => return error.NetworkDown,
   8081                     .EADDRINUSE => return error.AddressInUse,
   8082                     .EISCONN => |err| return wsaErrorBug(err),
   8083                     .EINVAL => |err| return wsaErrorBug(err),
   8084                     .EMFILE, .ENOBUFS => return error.SystemResources,
   8085                     .ENOTSOCK => |err| return wsaErrorBug(err),
   8086                     .EOPNOTSUPP => |err| return wsaErrorBug(err),
   8087                     .EINPROGRESS => |err| return wsaErrorBug(err),
   8088                     else => |err| return windows.unexpectedWSAError(err),
   8089                 }
   8090             },
   8091         }
   8092     }
   8093 }
   8094 
   8095 fn netListenUnixUnavailable(
   8096     userdata: ?*anyopaque,
   8097     address: *const net.UnixAddress,
   8098     options: net.UnixAddress.ListenOptions,
   8099 ) net.UnixAddress.ListenError!net.Socket.Handle {
   8100     _ = userdata;
   8101     _ = address;
   8102     _ = options;
   8103     return error.AddressFamilyUnsupported;
   8104 }
   8105 
   8106 fn posixBindUnix(
   8107     current_thread: *Thread,
   8108     fd: posix.socket_t,
   8109     addr: *const posix.sockaddr,
   8110     addr_len: posix.socklen_t,
   8111 ) !void {
   8112     try current_thread.beginSyscall();
   8113     while (true) {
   8114         switch (posix.errno(posix.system.bind(fd, addr, addr_len))) {
   8115             .SUCCESS => {
   8116                 current_thread.endSyscall();
   8117                 break;
   8118             },
   8119             .INTR => {
   8120                 try current_thread.checkCancel();
   8121                 continue;
   8122             },
   8123             .CANCELED => return current_thread.endSyscallCanceled(),
   8124             else => |e| {
   8125                 current_thread.endSyscall();
   8126                 switch (e) {
   8127                     .ACCES => return error.AccessDenied,
   8128                     .ADDRINUSE => return error.AddressInUse,
   8129                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
   8130                     .ADDRNOTAVAIL => return error.AddressUnavailable,
   8131                     .NOMEM => return error.SystemResources,
   8132 
   8133                     .LOOP => return error.SymLinkLoop,
   8134                     .NOENT => return error.FileNotFound,
   8135                     .NOTDIR => return error.NotDir,
   8136                     .ROFS => return error.ReadOnlyFileSystem,
   8137                     .PERM => return error.PermissionDenied,
   8138 
   8139                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8140                     .INVAL => |err| return errnoBug(err), // invalid parameters
   8141                     .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd`
   8142                     .FAULT => |err| return errnoBug(err), // invalid `addr` pointer
   8143                     .NAMETOOLONG => |err| return errnoBug(err),
   8144                     else => |err| return posix.unexpectedErrno(err),
   8145                 }
   8146             },
   8147         }
   8148     }
   8149 }
   8150 
   8151 fn posixBind(
   8152     current_thread: *Thread,
   8153     socket_fd: posix.socket_t,
   8154     addr: *const posix.sockaddr,
   8155     addr_len: posix.socklen_t,
   8156 ) !void {
   8157     try current_thread.beginSyscall();
   8158     while (true) {
   8159         switch (posix.errno(posix.system.bind(socket_fd, addr, addr_len))) {
   8160             .SUCCESS => {
   8161                 current_thread.endSyscall();
   8162                 break;
   8163             },
   8164             .INTR => {
   8165                 try current_thread.checkCancel();
   8166                 continue;
   8167             },
   8168             .CANCELED => return current_thread.endSyscallCanceled(),
   8169             else => |e| {
   8170                 current_thread.endSyscall();
   8171                 switch (e) {
   8172                     .ADDRINUSE => return error.AddressInUse,
   8173                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8174                     .INVAL => |err| return errnoBug(err), // invalid parameters
   8175                     .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd`
   8176                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
   8177                     .ADDRNOTAVAIL => return error.AddressUnavailable,
   8178                     .FAULT => |err| return errnoBug(err), // invalid `addr` pointer
   8179                     .NOMEM => return error.SystemResources,
   8180                     else => |err| return posix.unexpectedErrno(err),
   8181                 }
   8182             },
   8183         }
   8184     }
   8185 }
   8186 
   8187 fn posixConnect(
   8188     current_thread: *Thread,
   8189     socket_fd: posix.socket_t,
   8190     addr: *const posix.sockaddr,
   8191     addr_len: posix.socklen_t,
   8192 ) !void {
   8193     try current_thread.beginSyscall();
   8194     while (true) {
   8195         switch (posix.errno(posix.system.connect(socket_fd, addr, addr_len))) {
   8196             .SUCCESS => {
   8197                 current_thread.endSyscall();
   8198                 return;
   8199             },
   8200             .INTR => {
   8201                 try current_thread.checkCancel();
   8202                 continue;
   8203             },
   8204             .CANCELED => return current_thread.endSyscallCanceled(),
   8205             else => |e| {
   8206                 current_thread.endSyscall();
   8207                 switch (e) {
   8208                     .ADDRNOTAVAIL => return error.AddressUnavailable,
   8209                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
   8210                     .AGAIN, .INPROGRESS => return error.WouldBlock,
   8211                     .ALREADY => return error.ConnectionPending,
   8212                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8213                     .CONNREFUSED => return error.ConnectionRefused,
   8214                     .CONNRESET => return error.ConnectionResetByPeer,
   8215                     .FAULT => |err| return errnoBug(err),
   8216                     .ISCONN => |err| return errnoBug(err),
   8217                     .HOSTUNREACH => return error.HostUnreachable,
   8218                     .NETUNREACH => return error.NetworkUnreachable,
   8219                     .NOTSOCK => |err| return errnoBug(err),
   8220                     .PROTOTYPE => |err| return errnoBug(err),
   8221                     .TIMEDOUT => return error.Timeout,
   8222                     .CONNABORTED => |err| return errnoBug(err),
   8223                     .ACCES => return error.AccessDenied,
   8224                     .PERM => |err| return errnoBug(err),
   8225                     .NOENT => |err| return errnoBug(err),
   8226                     .NETDOWN => return error.NetworkDown,
   8227                     else => |err| return posix.unexpectedErrno(err),
   8228                 }
   8229             },
   8230         }
   8231     }
   8232 }
   8233 
   8234 fn posixConnectUnix(
   8235     current_thread: *Thread,
   8236     fd: posix.socket_t,
   8237     addr: *const posix.sockaddr,
   8238     addr_len: posix.socklen_t,
   8239 ) !void {
   8240     try current_thread.beginSyscall();
   8241     while (true) {
   8242         switch (posix.errno(posix.system.connect(fd, addr, addr_len))) {
   8243             .SUCCESS => {
   8244                 current_thread.endSyscall();
   8245                 return;
   8246             },
   8247             .INTR => {
   8248                 try current_thread.checkCancel();
   8249                 continue;
   8250             },
   8251             .CANCELED => return current_thread.endSyscallCanceled(),
   8252             else => |e| {
   8253                 current_thread.endSyscall();
   8254                 switch (e) {
   8255                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
   8256                     .AGAIN => return error.WouldBlock,
   8257                     .INPROGRESS => return error.WouldBlock,
   8258                     .ACCES => return error.AccessDenied,
   8259 
   8260                     .LOOP => return error.SymLinkLoop,
   8261                     .NOENT => return error.FileNotFound,
   8262                     .NOTDIR => return error.NotDir,
   8263                     .ROFS => return error.ReadOnlyFileSystem,
   8264                     .PERM => return error.PermissionDenied,
   8265 
   8266                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8267                     .CONNABORTED => |err| return errnoBug(err),
   8268                     .FAULT => |err| return errnoBug(err),
   8269                     .ISCONN => |err| return errnoBug(err),
   8270                     .NOTSOCK => |err| return errnoBug(err),
   8271                     .PROTOTYPE => |err| return errnoBug(err),
   8272                     else => |err| return posix.unexpectedErrno(err),
   8273                 }
   8274             },
   8275         }
   8276     }
   8277 }
   8278 
   8279 fn posixGetSockName(
   8280     current_thread: *Thread,
   8281     socket_fd: posix.fd_t,
   8282     addr: *posix.sockaddr,
   8283     addr_len: *posix.socklen_t,
   8284 ) !void {
   8285     try current_thread.beginSyscall();
   8286     while (true) {
   8287         switch (posix.errno(posix.system.getsockname(socket_fd, addr, addr_len))) {
   8288             .SUCCESS => {
   8289                 current_thread.endSyscall();
   8290                 break;
   8291             },
   8292             .INTR => {
   8293                 try current_thread.checkCancel();
   8294                 continue;
   8295             },
   8296             .CANCELED => return current_thread.endSyscallCanceled(),
   8297             else => |e| {
   8298                 current_thread.endSyscall();
   8299                 switch (e) {
   8300                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8301                     .FAULT => |err| return errnoBug(err),
   8302                     .INVAL => |err| return errnoBug(err), // invalid parameters
   8303                     .NOTSOCK => |err| return errnoBug(err), // always a race condition
   8304                     .NOBUFS => return error.SystemResources,
   8305                     else => |err| return posix.unexpectedErrno(err),
   8306                 }
   8307             },
   8308         }
   8309     }
   8310 }
   8311 
   8312 fn wsaGetSockName(
   8313     t: *Threaded,
   8314     current_thread: *Thread,
   8315     handle: ws2_32.SOCKET,
   8316     addr: *ws2_32.sockaddr,
   8317     addr_len: *i32,
   8318 ) !void {
   8319     try current_thread.beginSyscall();
   8320     while (true) {
   8321         const rc = ws2_32.getsockname(handle, addr, addr_len);
   8322         if (rc != ws2_32.SOCKET_ERROR) {
   8323             current_thread.endSyscall();
   8324             return;
   8325         }
   8326         switch (ws2_32.WSAGetLastError()) {
   8327             .EINTR => {
   8328                 try current_thread.checkCancel();
   8329                 continue;
   8330             },
   8331             .NOTINITIALISED => {
   8332                 try initializeWsa(t);
   8333                 try current_thread.checkCancel();
   8334                 continue;
   8335             },
   8336             else => |e| {
   8337                 current_thread.endSyscall();
   8338                 switch (e) {
   8339                     .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   8340                     .ENETDOWN => return error.NetworkDown,
   8341                     .EFAULT => |err| return wsaErrorBug(err),
   8342                     .ENOTSOCK => |err| return wsaErrorBug(err),
   8343                     .EINVAL => |err| return wsaErrorBug(err),
   8344                     else => |err| return windows.unexpectedWSAError(err),
   8345                 }
   8346             },
   8347         }
   8348     }
   8349 }
   8350 
   8351 fn setSocketOption(current_thread: *Thread, fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void {
   8352     const o: []const u8 = @ptrCast(&option);
   8353     try current_thread.beginSyscall();
   8354     while (true) {
   8355         switch (posix.errno(posix.system.setsockopt(fd, level, opt_name, o.ptr, @intCast(o.len)))) {
   8356             .SUCCESS => {
   8357                 current_thread.endSyscall();
   8358                 return;
   8359             },
   8360             .INTR => {
   8361                 try current_thread.checkCancel();
   8362                 continue;
   8363             },
   8364             .CANCELED => return current_thread.endSyscallCanceled(),
   8365             else => |e| {
   8366                 current_thread.endSyscall();
   8367                 switch (e) {
   8368                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8369                     .NOTSOCK => |err| return errnoBug(err),
   8370                     .INVAL => |err| return errnoBug(err),
   8371                     .FAULT => |err| return errnoBug(err),
   8372                     else => |err| return posix.unexpectedErrno(err),
   8373                 }
   8374             },
   8375         }
   8376     }
   8377 }
   8378 
   8379 fn setSocketOptionWsa(t: *Threaded, socket: Io.net.Socket.Handle, level: i32, opt_name: u32, option: u32) !void {
   8380     const o: []const u8 = @ptrCast(&option);
   8381     const rc = ws2_32.setsockopt(socket, level, @bitCast(opt_name), o.ptr, @intCast(o.len));
   8382     while (true) {
   8383         if (rc != ws2_32.SOCKET_ERROR) return;
   8384         switch (ws2_32.WSAGetLastError()) {
   8385             .EINTR => continue,
   8386             .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   8387             .NOTINITIALISED => {
   8388                 try initializeWsa(t);
   8389                 continue;
   8390             },
   8391             .ENETDOWN => return error.NetworkDown,
   8392             .EFAULT => |err| return wsaErrorBug(err),
   8393             .ENOTSOCK => |err| return wsaErrorBug(err),
   8394             .EINVAL => |err| return wsaErrorBug(err),
   8395             else => |err| return windows.unexpectedWSAError(err),
   8396         }
   8397     }
   8398 }
   8399 
   8400 fn netConnectIpPosix(
   8401     userdata: ?*anyopaque,
   8402     address: *const IpAddress,
   8403     options: IpAddress.ConnectOptions,
   8404 ) IpAddress.ConnectError!net.Stream {
   8405     if (!have_networking) return error.NetworkDown;
   8406     if (options.timeout != .none) @panic("TODO implement netConnectIpPosix with timeout");
   8407     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8408     const current_thread = Thread.getCurrent(t);
   8409     const family = posixAddressFamily(address);
   8410     const socket_fd = try openSocketPosix(current_thread, family, .{
   8411         .mode = options.mode,
   8412         .protocol = options.protocol,
   8413     });
   8414     errdefer posix.close(socket_fd);
   8415     var storage: PosixAddress = undefined;
   8416     var addr_len = addressToPosix(address, &storage);
   8417     try posixConnect(current_thread, socket_fd, &storage.any, addr_len);
   8418     try posixGetSockName(current_thread, socket_fd, &storage.any, &addr_len);
   8419     return .{ .socket = .{
   8420         .handle = socket_fd,
   8421         .address = addressFromPosix(&storage),
   8422     } };
   8423 }
   8424 
   8425 fn netConnectIpWindows(
   8426     userdata: ?*anyopaque,
   8427     address: *const IpAddress,
   8428     options: IpAddress.ConnectOptions,
   8429 ) IpAddress.ConnectError!net.Stream {
   8430     if (!have_networking) return error.NetworkDown;
   8431     if (options.timeout != .none) @panic("TODO implement netConnectIpWindows with timeout");
   8432     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8433     const current_thread = Thread.getCurrent(t);
   8434     const family = posixAddressFamily(address);
   8435     const socket_handle = try openSocketWsa(t, current_thread, family, .{
   8436         .mode = options.mode,
   8437         .protocol = options.protocol,
   8438     });
   8439     errdefer closeSocketWindows(socket_handle);
   8440 
   8441     var storage: WsaAddress = undefined;
   8442     var addr_len = addressToWsa(address, &storage);
   8443 
   8444     try current_thread.beginSyscall();
   8445     while (true) {
   8446         const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);
   8447         if (rc != ws2_32.SOCKET_ERROR) {
   8448             current_thread.endSyscall();
   8449             break;
   8450         }
   8451         switch (ws2_32.WSAGetLastError()) {
   8452             .EINTR => {
   8453                 try current_thread.checkCancel();
   8454                 continue;
   8455             },
   8456             .NOTINITIALISED => {
   8457                 try initializeWsa(t);
   8458                 try current_thread.checkCancel();
   8459                 continue;
   8460             },
   8461             else => |e| {
   8462                 current_thread.endSyscall();
   8463                 switch (e) {
   8464                     .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   8465                     .EADDRNOTAVAIL => return error.AddressUnavailable,
   8466                     .ECONNREFUSED => return error.ConnectionRefused,
   8467                     .ECONNRESET => return error.ConnectionResetByPeer,
   8468                     .ETIMEDOUT => return error.Timeout,
   8469                     .EHOSTUNREACH => return error.HostUnreachable,
   8470                     .ENETUNREACH => return error.NetworkUnreachable,
   8471                     .EFAULT => |err| return wsaErrorBug(err),
   8472                     .EINVAL => |err| return wsaErrorBug(err),
   8473                     .EISCONN => |err| return wsaErrorBug(err),
   8474                     .ENOTSOCK => |err| return wsaErrorBug(err),
   8475                     .EWOULDBLOCK => return error.WouldBlock,
   8476                     .EACCES => return error.AccessDenied,
   8477                     .ENOBUFS => return error.SystemResources,
   8478                     .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
   8479                     else => |err| return windows.unexpectedWSAError(err),
   8480                 }
   8481             },
   8482         }
   8483     }
   8484 
   8485     try wsaGetSockName(t, current_thread, socket_handle, &storage.any, &addr_len);
   8486 
   8487     return .{ .socket = .{
   8488         .handle = socket_handle,
   8489         .address = addressFromWsa(&storage),
   8490     } };
   8491 }
   8492 
   8493 fn netConnectIpUnavailable(
   8494     userdata: ?*anyopaque,
   8495     address: *const IpAddress,
   8496     options: IpAddress.ConnectOptions,
   8497 ) IpAddress.ConnectError!net.Stream {
   8498     _ = userdata;
   8499     _ = address;
   8500     _ = options;
   8501     return error.NetworkDown;
   8502 }
   8503 
   8504 fn netConnectUnixPosix(
   8505     userdata: ?*anyopaque,
   8506     address: *const net.UnixAddress,
   8507 ) net.UnixAddress.ConnectError!net.Socket.Handle {
   8508     if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
   8509     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8510     const current_thread = Thread.getCurrent(t);
   8511     const socket_fd = openSocketPosix(current_thread, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
   8512         error.OptionUnsupported => return error.Unexpected,
   8513         else => |e| return e,
   8514     };
   8515     errdefer posix.close(socket_fd);
   8516     var storage: UnixAddress = undefined;
   8517     const addr_len = addressUnixToPosix(address, &storage);
   8518     try posixConnectUnix(current_thread, socket_fd, &storage.any, addr_len);
   8519     return socket_fd;
   8520 }
   8521 
   8522 fn netConnectUnixWindows(
   8523     userdata: ?*anyopaque,
   8524     address: *const net.UnixAddress,
   8525 ) net.UnixAddress.ConnectError!net.Socket.Handle {
   8526     if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
   8527     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8528     const current_thread = Thread.getCurrent(t);
   8529 
   8530     const socket_handle = try openSocketWsa(t, current_thread, posix.AF.UNIX, .{ .mode = .stream });
   8531     errdefer closeSocketWindows(socket_handle);
   8532     var storage: WsaAddress = undefined;
   8533     const addr_len = addressUnixToWsa(address, &storage);
   8534 
   8535     while (true) {
   8536         const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);
   8537         if (rc != ws2_32.SOCKET_ERROR) break;
   8538         switch (ws2_32.WSAGetLastError()) {
   8539             .EINTR => continue,
   8540             .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   8541             .NOTINITIALISED => {
   8542                 try initializeWsa(t);
   8543                 continue;
   8544             },
   8545 
   8546             .ECONNREFUSED => return error.FileNotFound,
   8547             .EFAULT => |err| return wsaErrorBug(err),
   8548             .EINVAL => |err| return wsaErrorBug(err),
   8549             .EISCONN => |err| return wsaErrorBug(err),
   8550             .ENOTSOCK => |err| return wsaErrorBug(err),
   8551             .EWOULDBLOCK => return error.WouldBlock,
   8552             .EACCES => return error.AccessDenied,
   8553             .ENOBUFS => return error.SystemResources,
   8554             .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
   8555             else => |err| return windows.unexpectedWSAError(err),
   8556         }
   8557     }
   8558 
   8559     return socket_handle;
   8560 }
   8561 
   8562 fn netConnectUnixUnavailable(
   8563     userdata: ?*anyopaque,
   8564     address: *const net.UnixAddress,
   8565 ) net.UnixAddress.ConnectError!net.Socket.Handle {
   8566     _ = userdata;
   8567     _ = address;
   8568     return error.AddressFamilyUnsupported;
   8569 }
   8570 
   8571 fn netBindIpPosix(
   8572     userdata: ?*anyopaque,
   8573     address: *const IpAddress,
   8574     options: IpAddress.BindOptions,
   8575 ) IpAddress.BindError!net.Socket {
   8576     if (!have_networking) return error.NetworkDown;
   8577     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8578     const current_thread = Thread.getCurrent(t);
   8579     const family = posixAddressFamily(address);
   8580     const socket_fd = try openSocketPosix(current_thread, family, options);
   8581     errdefer posix.close(socket_fd);
   8582     var storage: PosixAddress = undefined;
   8583     var addr_len = addressToPosix(address, &storage);
   8584     try posixBind(current_thread, socket_fd, &storage.any, addr_len);
   8585     try posixGetSockName(current_thread, socket_fd, &storage.any, &addr_len);
   8586     return .{
   8587         .handle = socket_fd,
   8588         .address = addressFromPosix(&storage),
   8589     };
   8590 }
   8591 
   8592 fn netBindIpWindows(
   8593     userdata: ?*anyopaque,
   8594     address: *const IpAddress,
   8595     options: IpAddress.BindOptions,
   8596 ) IpAddress.BindError!net.Socket {
   8597     if (!have_networking) return error.NetworkDown;
   8598     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8599     const current_thread = Thread.getCurrent(t);
   8600     const family = posixAddressFamily(address);
   8601     const socket_handle = try openSocketWsa(t, current_thread, family, .{
   8602         .mode = options.mode,
   8603         .protocol = options.protocol,
   8604     });
   8605     errdefer closeSocketWindows(socket_handle);
   8606 
   8607     var storage: WsaAddress = undefined;
   8608     var addr_len = addressToWsa(address, &storage);
   8609 
   8610     try current_thread.beginSyscall();
   8611     while (true) {
   8612         const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
   8613         if (rc != ws2_32.SOCKET_ERROR) {
   8614             current_thread.endSyscall();
   8615             break;
   8616         }
   8617         switch (ws2_32.WSAGetLastError()) {
   8618             .EINTR => {
   8619                 try current_thread.checkCancel();
   8620                 continue;
   8621             },
   8622             .NOTINITIALISED => {
   8623                 try initializeWsa(t);
   8624                 try current_thread.checkCancel();
   8625                 continue;
   8626             },
   8627             else => |e| {
   8628                 current_thread.endSyscall();
   8629                 switch (e) {
   8630                     .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   8631                     .EADDRINUSE => return error.AddressInUse,
   8632                     .EADDRNOTAVAIL => return error.AddressUnavailable,
   8633                     .ENOTSOCK => |err| return wsaErrorBug(err),
   8634                     .EFAULT => |err| return wsaErrorBug(err),
   8635                     .EINVAL => |err| return wsaErrorBug(err),
   8636                     .ENOBUFS => return error.SystemResources,
   8637                     .ENETDOWN => return error.NetworkDown,
   8638                     else => |err| return windows.unexpectedWSAError(err),
   8639                 }
   8640             },
   8641         }
   8642     }
   8643 
   8644     try wsaGetSockName(t, current_thread, socket_handle, &storage.any, &addr_len);
   8645 
   8646     return .{
   8647         .handle = socket_handle,
   8648         .address = addressFromWsa(&storage),
   8649     };
   8650 }
   8651 
   8652 fn netBindIpUnavailable(
   8653     userdata: ?*anyopaque,
   8654     address: *const IpAddress,
   8655     options: IpAddress.BindOptions,
   8656 ) IpAddress.BindError!net.Socket {
   8657     _ = userdata;
   8658     _ = address;
   8659     _ = options;
   8660     return error.NetworkDown;
   8661 }
   8662 
   8663 fn openSocketPosix(
   8664     current_thread: *Thread,
   8665     family: posix.sa_family_t,
   8666     options: IpAddress.BindOptions,
   8667 ) error{
   8668     AddressFamilyUnsupported,
   8669     ProtocolUnsupportedBySystem,
   8670     ProcessFdQuotaExceeded,
   8671     SystemFdQuotaExceeded,
   8672     SystemResources,
   8673     ProtocolUnsupportedByAddressFamily,
   8674     SocketModeUnsupported,
   8675     OptionUnsupported,
   8676     Unexpected,
   8677     Canceled,
   8678 }!posix.socket_t {
   8679     const mode = posixSocketMode(options.mode);
   8680     const protocol = posixProtocol(options.protocol);
   8681     try current_thread.beginSyscall();
   8682     const socket_fd = while (true) {
   8683         const flags: u32 = mode | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC;
   8684         const socket_rc = posix.system.socket(family, flags, protocol);
   8685         switch (posix.errno(socket_rc)) {
   8686             .SUCCESS => {
   8687                 const fd: posix.fd_t = @intCast(socket_rc);
   8688                 errdefer posix.close(fd);
   8689                 if (socket_flags_unsupported) while (true) {
   8690                     try current_thread.checkCancel();
   8691                     switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
   8692                         .SUCCESS => break,
   8693                         .INTR => continue,
   8694                         .CANCELED => return current_thread.endSyscallCanceled(),
   8695                         else => |err| {
   8696                             current_thread.endSyscall();
   8697                             return posix.unexpectedErrno(err);
   8698                         },
   8699                     }
   8700                 };
   8701                 current_thread.endSyscall();
   8702                 break fd;
   8703             },
   8704             .INTR => {
   8705                 try current_thread.checkCancel();
   8706                 continue;
   8707             },
   8708             .CANCELED => return current_thread.endSyscallCanceled(),
   8709             else => |e| {
   8710                 current_thread.endSyscall();
   8711                 switch (e) {
   8712                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
   8713                     .INVAL => return error.ProtocolUnsupportedBySystem,
   8714                     .MFILE => return error.ProcessFdQuotaExceeded,
   8715                     .NFILE => return error.SystemFdQuotaExceeded,
   8716                     .NOBUFS => return error.SystemResources,
   8717                     .NOMEM => return error.SystemResources,
   8718                     .PROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily,
   8719                     .PROTOTYPE => return error.SocketModeUnsupported,
   8720                     else => |err| return posix.unexpectedErrno(err),
   8721                 }
   8722             },
   8723         }
   8724     };
   8725     errdefer posix.close(socket_fd);
   8726 
   8727     if (options.ip6_only) {
   8728         if (posix.IPV6 == void) return error.OptionUnsupported;
   8729         try setSocketOption(current_thread, socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0);
   8730     }
   8731 
   8732     return socket_fd;
   8733 }
   8734 
   8735 fn openSocketWsa(
   8736     t: *Threaded,
   8737     current_thread: *Thread,
   8738     family: posix.sa_family_t,
   8739     options: IpAddress.BindOptions,
   8740 ) !ws2_32.SOCKET {
   8741     const mode = posixSocketMode(options.mode);
   8742     const protocol = posixProtocol(options.protocol);
   8743     const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
   8744     try current_thread.beginSyscall();
   8745     while (true) {
   8746         const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags);
   8747         if (rc != ws2_32.INVALID_SOCKET) {
   8748             current_thread.endSyscall();
   8749             return rc;
   8750         }
   8751         switch (ws2_32.WSAGetLastError()) {
   8752             .EINTR => {
   8753                 try current_thread.checkCancel();
   8754                 continue;
   8755             },
   8756             .NOTINITIALISED => {
   8757                 try initializeWsa(t);
   8758                 try current_thread.checkCancel();
   8759                 continue;
   8760             },
   8761             else => |e| {
   8762                 current_thread.endSyscall();
   8763                 switch (e) {
   8764                     .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   8765                     .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
   8766                     .EMFILE => return error.ProcessFdQuotaExceeded,
   8767                     .ENOBUFS => return error.SystemResources,
   8768                     .EPROTONOSUPPORT => return error.ProtocolUnsupportedByAddressFamily,
   8769                     else => |err| return windows.unexpectedWSAError(err),
   8770                 }
   8771             },
   8772         }
   8773     }
   8774 }
   8775 
   8776 fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Server.AcceptError!net.Stream {
   8777     if (!have_networking) return error.NetworkDown;
   8778     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8779     const current_thread = Thread.getCurrent(t);
   8780     var storage: PosixAddress = undefined;
   8781     var addr_len: posix.socklen_t = @sizeOf(PosixAddress);
   8782     try current_thread.beginSyscall();
   8783     const fd = while (true) {
   8784         const rc = if (have_accept4)
   8785             posix.system.accept4(listen_fd, &storage.any, &addr_len, posix.SOCK.CLOEXEC)
   8786         else
   8787             posix.system.accept(listen_fd, &storage.any, &addr_len);
   8788         switch (posix.errno(rc)) {
   8789             .SUCCESS => {
   8790                 const fd: posix.fd_t = @intCast(rc);
   8791                 errdefer posix.close(fd);
   8792                 if (!have_accept4) while (true) {
   8793                     try current_thread.checkCancel();
   8794                     switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
   8795                         .SUCCESS => break,
   8796                         .INTR => continue,
   8797                         else => |err| {
   8798                             current_thread.endSyscall();
   8799                             return posix.unexpectedErrno(err);
   8800                         },
   8801                     }
   8802                 };
   8803                 current_thread.endSyscall();
   8804                 break fd;
   8805             },
   8806             .INTR => {
   8807                 try current_thread.checkCancel();
   8808                 continue;
   8809             },
   8810             .CANCELED => return current_thread.endSyscallCanceled(),
   8811             else => |e| {
   8812                 current_thread.endSyscall();
   8813                 switch (e) {
   8814                     .AGAIN => |err| return errnoBug(err),
   8815                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8816                     .CONNABORTED => return error.ConnectionAborted,
   8817                     .FAULT => |err| return errnoBug(err),
   8818                     .INVAL => return error.SocketNotListening,
   8819                     .NOTSOCK => |err| return errnoBug(err),
   8820                     .MFILE => return error.ProcessFdQuotaExceeded,
   8821                     .NFILE => return error.SystemFdQuotaExceeded,
   8822                     .NOBUFS => return error.SystemResources,
   8823                     .NOMEM => return error.SystemResources,
   8824                     .OPNOTSUPP => |err| return errnoBug(err),
   8825                     .PROTO => return error.ProtocolFailure,
   8826                     .PERM => return error.BlockedByFirewall,
   8827                     else => |err| return posix.unexpectedErrno(err),
   8828                 }
   8829             },
   8830         }
   8831     };
   8832     return .{ .socket = .{
   8833         .handle = fd,
   8834         .address = addressFromPosix(&storage),
   8835     } };
   8836 }
   8837 
   8838 fn netAcceptWindows(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net.Server.AcceptError!net.Stream {
   8839     if (!have_networking) return error.NetworkDown;
   8840     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8841     const current_thread = Thread.getCurrent(t);
   8842     var storage: WsaAddress = undefined;
   8843     var addr_len: i32 = @sizeOf(WsaAddress);
   8844     try current_thread.beginSyscall();
   8845     while (true) {
   8846         const rc = ws2_32.accept(listen_handle, &storage.any, &addr_len);
   8847         if (rc != ws2_32.INVALID_SOCKET) {
   8848             current_thread.endSyscall();
   8849             return .{ .socket = .{
   8850                 .handle = rc,
   8851                 .address = addressFromWsa(&storage),
   8852             } };
   8853         }
   8854         switch (ws2_32.WSAGetLastError()) {
   8855             .EINTR => {
   8856                 try current_thread.checkCancel();
   8857                 continue;
   8858             },
   8859             .NOTINITIALISED => {
   8860                 try initializeWsa(t);
   8861                 try current_thread.checkCancel();
   8862                 continue;
   8863             },
   8864             else => |e| {
   8865                 current_thread.endSyscall();
   8866                 switch (e) {
   8867                     .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   8868                     .ECONNRESET => return error.ConnectionAborted,
   8869                     .EFAULT => |err| return wsaErrorBug(err),
   8870                     .ENOTSOCK => |err| return wsaErrorBug(err),
   8871                     .EINVAL => |err| return wsaErrorBug(err),
   8872                     .EMFILE => return error.ProcessFdQuotaExceeded,
   8873                     .ENETDOWN => return error.NetworkDown,
   8874                     .ENOBUFS => return error.SystemResources,
   8875                     .EOPNOTSUPP => |err| return wsaErrorBug(err),
   8876                     else => |err| return windows.unexpectedWSAError(err),
   8877                 }
   8878             },
   8879         }
   8880     }
   8881 }
   8882 
   8883 fn netAcceptUnavailable(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net.Server.AcceptError!net.Stream {
   8884     _ = userdata;
   8885     _ = listen_handle;
   8886     return error.NetworkDown;
   8887 }
   8888 
   8889 fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
   8890     if (!have_networking) return error.NetworkDown;
   8891     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8892     const current_thread = Thread.getCurrent(t);
   8893 
   8894     var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
   8895     var i: usize = 0;
   8896     for (data) |buf| {
   8897         if (iovecs_buffer.len - i == 0) break;
   8898         if (buf.len != 0) {
   8899             iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len };
   8900             i += 1;
   8901         }
   8902     }
   8903     const dest = iovecs_buffer[0..i];
   8904     assert(dest[0].len > 0);
   8905 
   8906     if (native_os == .wasi and !builtin.link_libc) {
   8907         try current_thread.beginSyscall();
   8908         while (true) {
   8909             var n: usize = undefined;
   8910             switch (std.os.wasi.fd_read(fd, dest.ptr, dest.len, &n)) {
   8911                 .SUCCESS => {
   8912                     current_thread.endSyscall();
   8913                     return n;
   8914                 },
   8915                 .INTR => {
   8916                     try current_thread.checkCancel();
   8917                     continue;
   8918                 },
   8919                 .CANCELED => return current_thread.endSyscallCanceled(),
   8920                 else => |e| {
   8921                     current_thread.endSyscall();
   8922                     switch (e) {
   8923                         .INVAL => |err| return errnoBug(err),
   8924                         .FAULT => |err| return errnoBug(err),
   8925                         .AGAIN => |err| return errnoBug(err),
   8926                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8927                         .NOBUFS => return error.SystemResources,
   8928                         .NOMEM => return error.SystemResources,
   8929                         .NOTCONN => return error.SocketUnconnected,
   8930                         .CONNRESET => return error.ConnectionResetByPeer,
   8931                         .TIMEDOUT => return error.Timeout,
   8932                         .NOTCAPABLE => return error.AccessDenied,
   8933                         else => |err| return posix.unexpectedErrno(err),
   8934                     }
   8935                 },
   8936             }
   8937         }
   8938     }
   8939 
   8940     try current_thread.beginSyscall();
   8941     while (true) {
   8942         const rc = posix.system.readv(fd, dest.ptr, @intCast(dest.len));
   8943         switch (posix.errno(rc)) {
   8944             .SUCCESS => {
   8945                 current_thread.endSyscall();
   8946                 return @intCast(rc);
   8947             },
   8948             .INTR => {
   8949                 try current_thread.checkCancel();
   8950                 continue;
   8951             },
   8952             .CANCELED => return current_thread.endSyscallCanceled(),
   8953             else => |e| {
   8954                 current_thread.endSyscall();
   8955                 switch (e) {
   8956                     .INVAL => |err| return errnoBug(err),
   8957                     .FAULT => |err| return errnoBug(err),
   8958                     .AGAIN => |err| return errnoBug(err),
   8959                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   8960                     .NOBUFS => return error.SystemResources,
   8961                     .NOMEM => return error.SystemResources,
   8962                     .NOTCONN => return error.SocketUnconnected,
   8963                     .CONNRESET => return error.ConnectionResetByPeer,
   8964                     .TIMEDOUT => return error.Timeout,
   8965                     .PIPE => return error.SocketUnconnected,
   8966                     .NETDOWN => return error.NetworkDown,
   8967                     else => |err| return posix.unexpectedErrno(err),
   8968                 }
   8969             },
   8970         }
   8971     }
   8972 }
   8973 
   8974 fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
   8975     if (!have_networking) return error.NetworkDown;
   8976     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8977     const current_thread = Thread.getCurrent(t);
   8978 
   8979     const bufs = b: {
   8980         var iovec_buffer: [max_iovecs_len]ws2_32.WSABUF = undefined;
   8981         var i: usize = 0;
   8982         var n: usize = 0;
   8983         for (data) |buf| {
   8984             if (iovec_buffer.len - i == 0) break;
   8985             if (buf.len == 0) continue;
   8986             if (std.math.cast(u32, buf.len)) |len| {
   8987                 iovec_buffer[i] = .{ .buf = buf.ptr, .len = len };
   8988                 i += 1;
   8989                 n += len;
   8990                 continue;
   8991             }
   8992             iovec_buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
   8993             i += 1;
   8994             n += std.math.maxInt(u32);
   8995             break;
   8996         }
   8997 
   8998         const bufs = iovec_buffer[0..i];
   8999         assert(bufs[0].len != 0);
   9000 
   9001         break :b bufs;
   9002     };
   9003 
   9004     while (true) {
   9005         try current_thread.checkCancel();
   9006 
   9007         var flags: u32 = 0;
   9008         var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED);
   9009         var n: u32 = undefined;
   9010         const rc = ws2_32.WSARecv(handle, bufs.ptr, @intCast(bufs.len), &n, &flags, &overlapped, null);
   9011         if (rc != ws2_32.SOCKET_ERROR) return n;
   9012         const wsa_error: ws2_32.WinsockError = switch (ws2_32.WSAGetLastError()) {
   9013             .IO_PENDING => e: {
   9014                 var result_flags: u32 = undefined;
   9015                 const overlapped_rc = ws2_32.WSAGetOverlappedResult(
   9016                     handle,
   9017                     &overlapped,
   9018                     &n,
   9019                     windows.TRUE,
   9020                     &result_flags,
   9021                 );
   9022                 if (overlapped_rc == windows.FALSE) {
   9023                     break :e ws2_32.WSAGetLastError();
   9024                 } else {
   9025                     return n;
   9026                 }
   9027             },
   9028             else => |err| err,
   9029         };
   9030         switch (wsa_error) {
   9031             .EINTR => continue,
   9032             .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   9033             .NOTINITIALISED => {
   9034                 try initializeWsa(t);
   9035                 continue;
   9036             },
   9037 
   9038             .ECONNRESET => return error.ConnectionResetByPeer,
   9039             .EFAULT => unreachable, // a pointer is not completely contained in user address space.
   9040             .EINVAL => |err| return wsaErrorBug(err),
   9041             .EMSGSIZE => |err| return wsaErrorBug(err),
   9042             .ENETDOWN => return error.NetworkDown,
   9043             .ENETRESET => return error.ConnectionResetByPeer,
   9044             .ENOTCONN => return error.SocketUnconnected,
   9045             else => |err| return windows.unexpectedWSAError(err),
   9046         }
   9047     }
   9048 }
   9049 
   9050 fn netReadUnavailable(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
   9051     _ = userdata;
   9052     _ = fd;
   9053     _ = data;
   9054     return error.NetworkDown;
   9055 }
   9056 
   9057 fn netSendPosix(
   9058     userdata: ?*anyopaque,
   9059     handle: net.Socket.Handle,
   9060     messages: []net.OutgoingMessage,
   9061     flags: net.SendFlags,
   9062 ) struct { ?net.Socket.SendError, usize } {
   9063     if (!have_networking) return .{ error.NetworkDown, 0 };
   9064     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9065     const current_thread = Thread.getCurrent(t);
   9066 
   9067     const posix_flags: u32 =
   9068         @as(u32, if (@hasDecl(posix.MSG, "CONFIRM") and flags.confirm) posix.MSG.CONFIRM else 0) |
   9069         @as(u32, if (@hasDecl(posix.MSG, "DONTROUTE") and flags.dont_route) posix.MSG.DONTROUTE else 0) |
   9070         @as(u32, if (@hasDecl(posix.MSG, "EOR") and flags.eor) posix.MSG.EOR else 0) |
   9071         @as(u32, if (@hasDecl(posix.MSG, "OOB") and flags.oob) posix.MSG.OOB else 0) |
   9072         @as(u32, if (@hasDecl(posix.MSG, "FASTOPEN") and flags.fastopen) posix.MSG.FASTOPEN else 0) |
   9073         posix.MSG.NOSIGNAL;
   9074 
   9075     var i: usize = 0;
   9076     while (messages.len - i != 0) {
   9077         if (have_sendmmsg) {
   9078             i += netSendMany(current_thread, handle, messages[i..], posix_flags) catch |err| return .{ err, i };
   9079             continue;
   9080         }
   9081         netSendOne(t, current_thread, handle, &messages[i], posix_flags) catch |err| return .{ err, i };
   9082         i += 1;
   9083     }
   9084     return .{ null, i };
   9085 }
   9086 
   9087 fn netSendWindows(
   9088     userdata: ?*anyopaque,
   9089     handle: net.Socket.Handle,
   9090     messages: []net.OutgoingMessage,
   9091     flags: net.SendFlags,
   9092 ) struct { ?net.Socket.SendError, usize } {
   9093     if (!have_networking) return .{ error.NetworkDown, 0 };
   9094     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9095     _ = t;
   9096     _ = handle;
   9097     _ = messages;
   9098     _ = flags;
   9099     @panic("TODO netSendWindows");
   9100 }
   9101 
   9102 fn netSendUnavailable(
   9103     userdata: ?*anyopaque,
   9104     handle: net.Socket.Handle,
   9105     messages: []net.OutgoingMessage,
   9106     flags: net.SendFlags,
   9107 ) struct { ?net.Socket.SendError, usize } {
   9108     _ = userdata;
   9109     _ = handle;
   9110     _ = messages;
   9111     _ = flags;
   9112     return .{ error.NetworkDown, 0 };
   9113 }
   9114 
   9115 fn netSendOne(
   9116     t: *Threaded,
   9117     current_thread: *Thread,
   9118     handle: net.Socket.Handle,
   9119     message: *net.OutgoingMessage,
   9120     flags: u32,
   9121 ) net.Socket.SendError!void {
   9122     var addr: PosixAddress = undefined;
   9123     var iovec: posix.iovec_const = .{ .base = @constCast(message.data_ptr), .len = message.data_len };
   9124     const msg: posix.msghdr_const = .{
   9125         .name = &addr.any,
   9126         .namelen = addressToPosix(message.address, &addr),
   9127         .iov = (&iovec)[0..1],
   9128         .iovlen = 1,
   9129         // OS returns EINVAL if this pointer is invalid even if controllen is zero.
   9130         .control = if (message.control.len == 0) null else @constCast(message.control.ptr),
   9131         .controllen = @intCast(message.control.len),
   9132         .flags = 0,
   9133     };
   9134     try current_thread.beginSyscall();
   9135     while (true) {
   9136         const rc = posix.system.sendmsg(handle, &msg, flags);
   9137         if (is_windows) {
   9138             if (rc != ws2_32.SOCKET_ERROR) {
   9139                 current_thread.endSyscall();
   9140                 message.data_len = @intCast(rc);
   9141                 return;
   9142             }
   9143             switch (ws2_32.WSAGetLastError()) {
   9144                 .EINTR => {
   9145                     try current_thread.checkCancel();
   9146                     continue;
   9147                 },
   9148                 .NOTINITIALISED => {
   9149                     try initializeWsa(t);
   9150                     try current_thread.checkCancel();
   9151                     continue;
   9152                 },
   9153                 else => |e| {
   9154                     current_thread.endSyscall();
   9155                     switch (e) {
   9156                         .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   9157                         .EACCES => return error.AccessDenied,
   9158                         .EADDRNOTAVAIL => return error.AddressUnavailable,
   9159                         .ECONNRESET => return error.ConnectionResetByPeer,
   9160                         .EMSGSIZE => return error.MessageOversize,
   9161                         .ENOBUFS => return error.SystemResources,
   9162                         .ENOTSOCK => return error.FileDescriptorNotASocket,
   9163                         .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
   9164                         .EDESTADDRREQ => unreachable, // A destination address is required.
   9165                         .EFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
   9166                         .EHOSTUNREACH => return error.NetworkUnreachable,
   9167                         .EINVAL => unreachable,
   9168                         .ENETDOWN => return error.NetworkDown,
   9169                         .ENETRESET => return error.ConnectionResetByPeer,
   9170                         .ENETUNREACH => return error.NetworkUnreachable,
   9171                         .ENOTCONN => return error.SocketUnconnected,
   9172                         .ESHUTDOWN => |err| return wsaErrorBug(err),
   9173                         else => |err| return windows.unexpectedWSAError(err),
   9174                     }
   9175                 },
   9176             }
   9177         }
   9178         switch (posix.errno(rc)) {
   9179             .SUCCESS => {
   9180                 current_thread.endSyscall();
   9181                 message.data_len = @intCast(rc);
   9182                 return;
   9183             },
   9184             .INTR => {
   9185                 try current_thread.checkCancel();
   9186                 continue;
   9187             },
   9188             .CANCELED => return current_thread.endSyscallCanceled(),
   9189             else => |e| {
   9190                 current_thread.endSyscall();
   9191                 switch (e) {
   9192                     .ACCES => return error.AccessDenied,
   9193                     .ALREADY => return error.FastOpenAlreadyInProgress,
   9194                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   9195                     .CONNRESET => return error.ConnectionResetByPeer,
   9196                     .DESTADDRREQ => |err| return errnoBug(err),
   9197                     .FAULT => |err| return errnoBug(err),
   9198                     .INVAL => |err| return errnoBug(err),
   9199                     .ISCONN => |err| return errnoBug(err),
   9200                     .MSGSIZE => return error.MessageOversize,
   9201                     .NOBUFS => return error.SystemResources,
   9202                     .NOMEM => return error.SystemResources,
   9203                     .NOTSOCK => |err| return errnoBug(err),
   9204                     .OPNOTSUPP => |err| return errnoBug(err),
   9205                     .PIPE => return error.SocketUnconnected,
   9206                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
   9207                     .HOSTUNREACH => return error.HostUnreachable,
   9208                     .NETUNREACH => return error.NetworkUnreachable,
   9209                     .NOTCONN => return error.SocketUnconnected,
   9210                     .NETDOWN => return error.NetworkDown,
   9211                     else => |err| return posix.unexpectedErrno(err),
   9212                 }
   9213             },
   9214         }
   9215     }
   9216 }
   9217 
   9218 fn netSendMany(
   9219     current_thread: *Thread,
   9220     handle: net.Socket.Handle,
   9221     messages: []net.OutgoingMessage,
   9222     flags: u32,
   9223 ) net.Socket.SendError!usize {
   9224     var msg_buffer: [64]posix.system.mmsghdr = undefined;
   9225     var addr_buffer: [msg_buffer.len]PosixAddress = undefined;
   9226     var iovecs_buffer: [msg_buffer.len]posix.iovec = undefined;
   9227     const min_len: usize = @min(messages.len, msg_buffer.len);
   9228     const clamped_messages = messages[0..min_len];
   9229     const clamped_msgs = (&msg_buffer)[0..min_len];
   9230     const clamped_addrs = (&addr_buffer)[0..min_len];
   9231     const clamped_iovecs = (&iovecs_buffer)[0..min_len];
   9232 
   9233     for (clamped_messages, clamped_msgs, clamped_addrs, clamped_iovecs) |*message, *msg, *addr, *iovec| {
   9234         iovec.* = .{ .base = @constCast(message.data_ptr), .len = message.data_len };
   9235         msg.* = .{
   9236             .hdr = .{
   9237                 .name = &addr.any,
   9238                 .namelen = addressToPosix(message.address, addr),
   9239                 .iov = iovec[0..1],
   9240                 .iovlen = 1,
   9241                 .control = @constCast(message.control.ptr),
   9242                 .controllen = message.control.len,
   9243                 .flags = 0,
   9244             },
   9245             .len = undefined, // Populated by calling sendmmsg below.
   9246         };
   9247     }
   9248 
   9249     try current_thread.beginSyscall();
   9250     while (true) {
   9251         const rc = posix.system.sendmmsg(handle, clamped_msgs.ptr, @intCast(clamped_msgs.len), flags);
   9252         switch (posix.errno(rc)) {
   9253             .SUCCESS => {
   9254                 current_thread.endSyscall();
   9255                 const n: usize = @intCast(rc);
   9256                 for (clamped_messages[0..n], clamped_msgs[0..n]) |*message, *msg| {
   9257                     message.data_len = msg.len;
   9258                 }
   9259                 return n;
   9260             },
   9261             .INTR => {
   9262                 try current_thread.checkCancel();
   9263                 continue;
   9264             },
   9265             .CANCELED => return current_thread.endSyscallCanceled(),
   9266             else => |e| {
   9267                 current_thread.endSyscall();
   9268                 switch (e) {
   9269                     .AGAIN => |err| return errnoBug(err),
   9270                     .ALREADY => return error.FastOpenAlreadyInProgress,
   9271                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   9272                     .CONNRESET => return error.ConnectionResetByPeer,
   9273                     .DESTADDRREQ => |err| return errnoBug(err), // The socket is not connection-mode, and no peer address is set.
   9274                     .FAULT => |err| return errnoBug(err), // An invalid user space address was specified for an argument.
   9275                     .INVAL => |err| return errnoBug(err), // Invalid argument passed.
   9276                     .ISCONN => |err| return errnoBug(err), // connection-mode socket was connected already but a recipient was specified
   9277                     .MSGSIZE => return error.MessageOversize,
   9278                     .NOBUFS => return error.SystemResources,
   9279                     .NOMEM => return error.SystemResources,
   9280                     .NOTSOCK => |err| return errnoBug(err), // The file descriptor sockfd does not refer to a socket.
   9281                     .OPNOTSUPP => |err| return errnoBug(err), // Some bit in the flags argument is inappropriate for the socket type.
   9282                     .PIPE => return error.SocketUnconnected,
   9283                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
   9284                     .HOSTUNREACH => return error.HostUnreachable,
   9285                     .NETUNREACH => return error.NetworkUnreachable,
   9286                     .NOTCONN => return error.SocketUnconnected,
   9287                     .NETDOWN => return error.NetworkDown,
   9288                     else => |err| return posix.unexpectedErrno(err),
   9289                 }
   9290             },
   9291         }
   9292     }
   9293 }
   9294 
   9295 fn netReceivePosix(
   9296     userdata: ?*anyopaque,
   9297     handle: net.Socket.Handle,
   9298     message_buffer: []net.IncomingMessage,
   9299     data_buffer: []u8,
   9300     flags: net.ReceiveFlags,
   9301     timeout: Io.Timeout,
   9302 ) struct { ?net.Socket.ReceiveTimeoutError, usize } {
   9303     if (!have_networking) return .{ error.NetworkDown, 0 };
   9304     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9305     const current_thread = Thread.getCurrent(t);
   9306     const t_io = io(t);
   9307 
   9308     // recvmmsg is useless, here's why:
   9309     // * [timeout bug](https://bugzilla.kernel.org/show_bug.cgi?id=75371)
   9310     // * it wants iovecs for each message but we have a better API: one data
   9311     //   buffer to handle all the messages. The better API cannot be lowered to
   9312     //   the split vectors though because reducing the buffer size might make
   9313     //   some messages unreceivable.
   9314 
   9315     // So the strategy instead is to use non-blocking recvmsg calls, calling
   9316     // poll() with timeout if the first one returns EAGAIN.
   9317     const posix_flags: u32 =
   9318         @as(u32, if (flags.oob) posix.MSG.OOB else 0) |
   9319         @as(u32, if (flags.peek) posix.MSG.PEEK else 0) |
   9320         @as(u32, if (flags.trunc) posix.MSG.TRUNC else 0) |
   9321         posix.MSG.DONTWAIT | posix.MSG.NOSIGNAL;
   9322 
   9323     var poll_fds: [1]posix.pollfd = .{
   9324         .{
   9325             .fd = handle,
   9326             .events = posix.POLL.IN,
   9327             .revents = undefined,
   9328         },
   9329     };
   9330     var message_i: usize = 0;
   9331     var data_i: usize = 0;
   9332 
   9333     const deadline = timeout.toDeadline(t_io) catch |err| return .{ err, message_i };
   9334 
   9335     recv: while (true) {
   9336         if (message_buffer.len - message_i == 0) return .{ null, message_i };
   9337         const message = &message_buffer[message_i];
   9338         const remaining_data_buffer = data_buffer[data_i..];
   9339         var storage: PosixAddress = undefined;
   9340         var iov: posix.iovec = .{ .base = remaining_data_buffer.ptr, .len = remaining_data_buffer.len };
   9341         var msg: posix.msghdr = .{
   9342             .name = &storage.any,
   9343             .namelen = @sizeOf(PosixAddress),
   9344             .iov = (&iov)[0..1],
   9345             .iovlen = 1,
   9346             .control = message.control.ptr,
   9347             .controllen = @intCast(message.control.len),
   9348             .flags = undefined,
   9349         };
   9350 
   9351         current_thread.beginSyscall() catch |err| return .{ err, message_i };
   9352         const recv_rc = posix.system.recvmsg(handle, &msg, posix_flags);
   9353         current_thread.endSyscall();
   9354         switch (posix.errno(recv_rc)) {
   9355             .SUCCESS => {
   9356                 const data = remaining_data_buffer[0..@intCast(recv_rc)];
   9357                 data_i += data.len;
   9358                 message.* = .{
   9359                     .from = addressFromPosix(&storage),
   9360                     .data = data,
   9361                     .control = if (msg.control) |ptr| @as([*]u8, @ptrCast(ptr))[0..msg.controllen] else message.control,
   9362                     .flags = .{
   9363                         .eor = (msg.flags & posix.MSG.EOR) != 0,
   9364                         .trunc = (msg.flags & posix.MSG.TRUNC) != 0,
   9365                         .ctrunc = (msg.flags & posix.MSG.CTRUNC) != 0,
   9366                         .oob = (msg.flags & posix.MSG.OOB) != 0,
   9367                         .errqueue = if (@hasDecl(posix.MSG, "ERRQUEUE")) (msg.flags & posix.MSG.ERRQUEUE) != 0 else false,
   9368                     },
   9369                 };
   9370                 message_i += 1;
   9371                 continue;
   9372             },
   9373             .AGAIN => while (true) {
   9374                 if (message_i != 0) return .{ null, message_i };
   9375 
   9376                 const max_poll_ms = std.math.maxInt(u31);
   9377                 const timeout_ms: u31 = if (deadline) |d| t: {
   9378                     const duration = d.durationFromNow(t_io) catch |err| return .{ err, message_i };
   9379                     if (duration.raw.nanoseconds <= 0) return .{ error.Timeout, message_i };
   9380                     break :t @intCast(@min(max_poll_ms, duration.raw.toMilliseconds()));
   9381                 } else max_poll_ms;
   9382 
   9383                 current_thread.beginSyscall() catch |err| return .{ err, message_i };
   9384                 const poll_rc = posix.system.poll(&poll_fds, poll_fds.len, timeout_ms);
   9385                 current_thread.endSyscall();
   9386 
   9387                 switch (posix.errno(poll_rc)) {
   9388                     .SUCCESS => {
   9389                         if (poll_rc == 0) {
   9390                             // Although spurious timeouts are OK, when no deadline
   9391                             // is passed we must not return `error.Timeout`.
   9392                             if (deadline == null) continue;
   9393                             return .{ error.Timeout, message_i };
   9394                         }
   9395                         continue :recv;
   9396                     },
   9397                     .INTR => continue,
   9398                     .CANCELED => return .{ current_thread.endSyscallCanceled(), message_i },
   9399 
   9400                     .FAULT => |err| return .{ errnoBug(err), message_i },
   9401                     .INVAL => |err| return .{ errnoBug(err), message_i },
   9402                     .NOMEM => return .{ error.SystemResources, message_i },
   9403                     else => |err| return .{ posix.unexpectedErrno(err), message_i },
   9404                 }
   9405             },
   9406             .INTR => continue,
   9407             .CANCELED => return .{ current_thread.endSyscallCanceled(), message_i },
   9408 
   9409             .BADF => |err| return .{ errnoBug(err), message_i },
   9410             .NFILE => return .{ error.SystemFdQuotaExceeded, message_i },
   9411             .MFILE => return .{ error.ProcessFdQuotaExceeded, message_i },
   9412             .FAULT => |err| return .{ errnoBug(err), message_i },
   9413             .INVAL => |err| return .{ errnoBug(err), message_i },
   9414             .NOBUFS => return .{ error.SystemResources, message_i },
   9415             .NOMEM => return .{ error.SystemResources, message_i },
   9416             .NOTCONN => return .{ error.SocketUnconnected, message_i },
   9417             .NOTSOCK => |err| return .{ errnoBug(err), message_i },
   9418             .MSGSIZE => return .{ error.MessageOversize, message_i },
   9419             .PIPE => return .{ error.SocketUnconnected, message_i },
   9420             .OPNOTSUPP => |err| return .{ errnoBug(err), message_i },
   9421             .CONNRESET => return .{ error.ConnectionResetByPeer, message_i },
   9422             .NETDOWN => return .{ error.NetworkDown, message_i },
   9423             else => |err| return .{ posix.unexpectedErrno(err), message_i },
   9424         }
   9425     }
   9426 }
   9427 
   9428 fn netReceiveWindows(
   9429     userdata: ?*anyopaque,
   9430     handle: net.Socket.Handle,
   9431     message_buffer: []net.IncomingMessage,
   9432     data_buffer: []u8,
   9433     flags: net.ReceiveFlags,
   9434     timeout: Io.Timeout,
   9435 ) struct { ?net.Socket.ReceiveTimeoutError, usize } {
   9436     if (!have_networking) return .{ error.NetworkDown, 0 };
   9437     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9438     _ = t;
   9439     _ = handle;
   9440     _ = message_buffer;
   9441     _ = data_buffer;
   9442     _ = flags;
   9443     _ = timeout;
   9444     @panic("TODO implement netReceiveWindows");
   9445 }
   9446 
   9447 fn netReceiveUnavailable(
   9448     userdata: ?*anyopaque,
   9449     handle: net.Socket.Handle,
   9450     message_buffer: []net.IncomingMessage,
   9451     data_buffer: []u8,
   9452     flags: net.ReceiveFlags,
   9453     timeout: Io.Timeout,
   9454 ) struct { ?net.Socket.ReceiveTimeoutError, usize } {
   9455     _ = userdata;
   9456     _ = handle;
   9457     _ = message_buffer;
   9458     _ = data_buffer;
   9459     _ = flags;
   9460     _ = timeout;
   9461     return .{ error.NetworkDown, 0 };
   9462 }
   9463 
   9464 fn netWritePosix(
   9465     userdata: ?*anyopaque,
   9466     fd: net.Socket.Handle,
   9467     header: []const u8,
   9468     data: []const []const u8,
   9469     splat: usize,
   9470 ) net.Stream.Writer.Error!usize {
   9471     if (!have_networking) return error.NetworkDown;
   9472     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9473     const current_thread = Thread.getCurrent(t);
   9474 
   9475     var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
   9476     var msg: posix.msghdr_const = .{
   9477         .name = null,
   9478         .namelen = 0,
   9479         .iov = &iovecs,
   9480         .iovlen = 0,
   9481         .control = null,
   9482         .controllen = 0,
   9483         .flags = 0,
   9484     };
   9485     addBuf(&iovecs, &msg.iovlen, header);
   9486     for (data[0 .. data.len - 1]) |bytes| addBuf(&iovecs, &msg.iovlen, bytes);
   9487     const pattern = data[data.len - 1];
   9488     if (iovecs.len - msg.iovlen != 0) switch (splat) {
   9489         0 => {},
   9490         1 => addBuf(&iovecs, &msg.iovlen, pattern),
   9491         else => switch (pattern.len) {
   9492             0 => {},
   9493             1 => {
   9494                 var backup_buffer: [splat_buffer_size]u8 = undefined;
   9495                 const splat_buffer = &backup_buffer;
   9496                 const memset_len = @min(splat_buffer.len, splat);
   9497                 const buf = splat_buffer[0..memset_len];
   9498                 @memset(buf, pattern[0]);
   9499                 addBuf(&iovecs, &msg.iovlen, buf);
   9500                 var remaining_splat = splat - buf.len;
   9501                 while (remaining_splat > splat_buffer.len and iovecs.len - msg.iovlen != 0) {
   9502                     assert(buf.len == splat_buffer.len);
   9503                     addBuf(&iovecs, &msg.iovlen, splat_buffer);
   9504                     remaining_splat -= splat_buffer.len;
   9505                 }
   9506                 addBuf(&iovecs, &msg.iovlen, splat_buffer[0..remaining_splat]);
   9507             },
   9508             else => for (0..@min(splat, iovecs.len - msg.iovlen)) |_| {
   9509                 addBuf(&iovecs, &msg.iovlen, pattern);
   9510             },
   9511         },
   9512     };
   9513     const flags = posix.MSG.NOSIGNAL;
   9514 
   9515     try current_thread.beginSyscall();
   9516     while (true) {
   9517         const rc = posix.system.sendmsg(fd, &msg, flags);
   9518         switch (posix.errno(rc)) {
   9519             .SUCCESS => {
   9520                 current_thread.endSyscall();
   9521                 return @intCast(rc);
   9522             },
   9523             .INTR => {
   9524                 try current_thread.checkCancel();
   9525                 continue;
   9526             },
   9527             .CANCELED => return current_thread.endSyscallCanceled(),
   9528             else => |e| {
   9529                 current_thread.endSyscall();
   9530                 switch (e) {
   9531                     .ACCES => |err| return errnoBug(err),
   9532                     .AGAIN => |err| return errnoBug(err),
   9533                     .ALREADY => return error.FastOpenAlreadyInProgress,
   9534                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   9535                     .CONNRESET => return error.ConnectionResetByPeer,
   9536                     .DESTADDRREQ => |err| return errnoBug(err), // The socket is not connection-mode, and no peer address is set.
   9537                     .FAULT => |err| return errnoBug(err), // An invalid user space address was specified for an argument.
   9538                     .INVAL => |err| return errnoBug(err), // Invalid argument passed.
   9539                     .ISCONN => |err| return errnoBug(err), // connection-mode socket was connected already but a recipient was specified
   9540                     .MSGSIZE => |err| return errnoBug(err),
   9541                     .NOBUFS => return error.SystemResources,
   9542                     .NOMEM => return error.SystemResources,
   9543                     .NOTSOCK => |err| return errnoBug(err), // The file descriptor sockfd does not refer to a socket.
   9544                     .OPNOTSUPP => |err| return errnoBug(err), // Some bit in the flags argument is inappropriate for the socket type.
   9545                     .PIPE => return error.SocketUnconnected,
   9546                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
   9547                     .HOSTUNREACH => return error.HostUnreachable,
   9548                     .NETUNREACH => return error.NetworkUnreachable,
   9549                     .NOTCONN => return error.SocketUnconnected,
   9550                     .NETDOWN => return error.NetworkDown,
   9551                     else => |err| return posix.unexpectedErrno(err),
   9552                 }
   9553             },
   9554         }
   9555     }
   9556 }
   9557 
   9558 fn netWriteWindows(
   9559     userdata: ?*anyopaque,
   9560     handle: net.Socket.Handle,
   9561     header: []const u8,
   9562     data: []const []const u8,
   9563     splat: usize,
   9564 ) net.Stream.Writer.Error!usize {
   9565     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9566     const current_thread = Thread.getCurrent(t);
   9567     comptime assert(native_os == .windows);
   9568 
   9569     var iovecs: [max_iovecs_len]ws2_32.WSABUF = undefined;
   9570     var len: u32 = 0;
   9571     addWsaBuf(&iovecs, &len, header);
   9572     for (data[0 .. data.len - 1]) |bytes| addWsaBuf(&iovecs, &len, bytes);
   9573     const pattern = data[data.len - 1];
   9574     if (iovecs.len - len != 0) switch (splat) {
   9575         0 => {},
   9576         1 => addWsaBuf(&iovecs, &len, pattern),
   9577         else => switch (pattern.len) {
   9578             0 => {},
   9579             1 => {
   9580                 var backup_buffer: [64]u8 = undefined;
   9581                 const splat_buffer = &backup_buffer;
   9582                 const memset_len = @min(splat_buffer.len, splat);
   9583                 const buf = splat_buffer[0..memset_len];
   9584                 @memset(buf, pattern[0]);
   9585                 addWsaBuf(&iovecs, &len, buf);
   9586                 var remaining_splat = splat - buf.len;
   9587                 while (remaining_splat > splat_buffer.len and len < iovecs.len) {
   9588                     addWsaBuf(&iovecs, &len, splat_buffer);
   9589                     remaining_splat -= splat_buffer.len;
   9590                 }
   9591                 addWsaBuf(&iovecs, &len, splat_buffer[0..remaining_splat]);
   9592             },
   9593             else => for (0..@min(splat, iovecs.len - len)) |_| {
   9594                 addWsaBuf(&iovecs, &len, pattern);
   9595             },
   9596         },
   9597     };
   9598 
   9599     while (true) {
   9600         try current_thread.checkCancel();
   9601 
   9602         var n: u32 = undefined;
   9603         var overlapped: windows.OVERLAPPED = std.mem.zeroes(windows.OVERLAPPED);
   9604         const rc = ws2_32.WSASend(handle, &iovecs, len, &n, 0, &overlapped, null);
   9605         if (rc != ws2_32.SOCKET_ERROR) return n;
   9606         const wsa_error: ws2_32.WinsockError = switch (ws2_32.WSAGetLastError()) {
   9607             .IO_PENDING => e: {
   9608                 var result_flags: u32 = undefined;
   9609                 const overlapped_rc = ws2_32.WSAGetOverlappedResult(
   9610                     handle,
   9611                     &overlapped,
   9612                     &n,
   9613                     windows.TRUE,
   9614                     &result_flags,
   9615                 );
   9616                 if (overlapped_rc == windows.FALSE) {
   9617                     break :e ws2_32.WSAGetLastError();
   9618                 } else {
   9619                     return n;
   9620                 }
   9621             },
   9622             else => |err| err,
   9623         };
   9624         switch (wsa_error) {
   9625             .EINTR => continue,
   9626             .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return current_thread.endSyscallCanceled(),
   9627             .NOTINITIALISED => {
   9628                 try initializeWsa(t);
   9629                 continue;
   9630             },
   9631 
   9632             .ECONNABORTED => return error.ConnectionResetByPeer,
   9633             .ECONNRESET => return error.ConnectionResetByPeer,
   9634             .EINVAL => return error.SocketUnconnected,
   9635             .ENETDOWN => return error.NetworkDown,
   9636             .ENETRESET => return error.ConnectionResetByPeer,
   9637             .ENOBUFS => return error.SystemResources,
   9638             .ENOTCONN => return error.SocketUnconnected,
   9639             .ENOTSOCK => |err| return wsaErrorBug(err),
   9640             .EOPNOTSUPP => |err| return wsaErrorBug(err),
   9641             .ESHUTDOWN => |err| return wsaErrorBug(err),
   9642             else => |err| return windows.unexpectedWSAError(err),
   9643         }
   9644     }
   9645 }
   9646 
   9647 fn addWsaBuf(v: []ws2_32.WSABUF, i: *u32, bytes: []const u8) void {
   9648     const cap = std.math.maxInt(u32);
   9649     var remaining = bytes;
   9650     while (remaining.len > cap) {
   9651         if (v.len - i.* == 0) return;
   9652         v[i.*] = .{ .buf = @constCast(remaining.ptr), .len = cap };
   9653         i.* += 1;
   9654         remaining = remaining[cap..];
   9655     } else {
   9656         @branchHint(.likely);
   9657         if (v.len - i.* == 0) return;
   9658         v[i.*] = .{ .buf = @constCast(remaining.ptr), .len = @intCast(remaining.len) };
   9659         i.* += 1;
   9660     }
   9661 }
   9662 
   9663 fn netWriteUnavailable(
   9664     userdata: ?*anyopaque,
   9665     handle: net.Socket.Handle,
   9666     header: []const u8,
   9667     data: []const []const u8,
   9668     splat: usize,
   9669 ) net.Stream.Writer.Error!usize {
   9670     _ = userdata;
   9671     _ = handle;
   9672     _ = header;
   9673     _ = data;
   9674     _ = splat;
   9675     return error.NetworkDown;
   9676 }
   9677 
   9678 /// This is either usize or u32. Since, either is fine, let's use the same
   9679 /// `addBuf` function for both writing to a file and sending network messages.
   9680 const iovlen_t = @FieldType(posix.msghdr_const, "iovlen");
   9681 
   9682 fn addBuf(v: []posix.iovec_const, i: *iovlen_t, bytes: []const u8) void {
   9683     // OS checks ptr addr before length so zero length vectors must be omitted.
   9684     if (bytes.len == 0) return;
   9685     if (v.len - i.* == 0) return;
   9686     v[i.*] = .{ .base = bytes.ptr, .len = bytes.len };
   9687     i.* += 1;
   9688 }
   9689 
   9690 fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
   9691     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9692     _ = t;
   9693     switch (native_os) {
   9694         .windows => for (handles) |handle| closeSocketWindows(handle),
   9695         else => for (handles) |handle| posix.close(handle),
   9696     }
   9697 }
   9698 
   9699 fn netCloseUnavailable(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
   9700     _ = userdata;
   9701     _ = handles;
   9702     unreachable; // How you gonna close something that was impossible to open?
   9703 }
   9704 
   9705 fn netInterfaceNameResolve(
   9706     userdata: ?*anyopaque,
   9707     name: *const net.Interface.Name,
   9708 ) net.Interface.Name.ResolveError!net.Interface {
   9709     if (!have_networking) return error.InterfaceNotFound;
   9710     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9711     const current_thread = Thread.getCurrent(t);
   9712 
   9713     if (native_os == .linux) {
   9714         const sock_fd = openSocketPosix(current_thread, posix.AF.UNIX, .{ .mode = .dgram }) catch |err| switch (err) {
   9715             error.ProcessFdQuotaExceeded => return error.SystemResources,
   9716             error.SystemFdQuotaExceeded => return error.SystemResources,
   9717             error.AddressFamilyUnsupported => return error.Unexpected,
   9718             error.ProtocolUnsupportedBySystem => return error.Unexpected,
   9719             error.ProtocolUnsupportedByAddressFamily => return error.Unexpected,
   9720             error.SocketModeUnsupported => return error.Unexpected,
   9721             error.OptionUnsupported => return error.Unexpected,
   9722             else => |e| return e,
   9723         };
   9724         defer posix.close(sock_fd);
   9725 
   9726         var ifr: posix.ifreq = .{
   9727             .ifrn = .{ .name = @bitCast(name.bytes) },
   9728             .ifru = undefined,
   9729         };
   9730 
   9731         try current_thread.beginSyscall();
   9732         while (true) {
   9733             switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) {
   9734                 .SUCCESS => {
   9735                     current_thread.endSyscall();
   9736                     return .{ .index = @bitCast(ifr.ifru.ivalue) };
   9737                 },
   9738                 .INTR => {
   9739                     try current_thread.checkCancel();
   9740                     continue;
   9741                 },
   9742                 .CANCELED => return current_thread.endSyscallCanceled(),
   9743                 else => |e| {
   9744                     current_thread.endSyscall();
   9745                     switch (e) {
   9746                         .INVAL => |err| return errnoBug(err), // Bad parameters.
   9747                         .NOTTY => |err| return errnoBug(err),
   9748                         .NXIO => |err| return errnoBug(err),
   9749                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   9750                         .FAULT => |err| return errnoBug(err), // Bad pointer parameter.
   9751                         .IO => |err| return errnoBug(err), // sock_fd is not a file descriptor
   9752                         .NODEV => return error.InterfaceNotFound,
   9753                         else => |err| return posix.unexpectedErrno(err),
   9754                     }
   9755                 },
   9756             }
   9757         }
   9758     }
   9759 
   9760     if (native_os == .windows) {
   9761         try current_thread.checkCancel();
   9762         @panic("TODO implement netInterfaceNameResolve for Windows");
   9763     }
   9764 
   9765     if (builtin.link_libc) {
   9766         try current_thread.checkCancel();
   9767         const index = std.c.if_nametoindex(&name.bytes);
   9768         if (index == 0) return error.InterfaceNotFound;
   9769         return .{ .index = @bitCast(index) };
   9770     }
   9771 
   9772     @panic("unimplemented");
   9773 }
   9774 
   9775 fn netInterfaceNameResolveUnavailable(
   9776     userdata: ?*anyopaque,
   9777     name: *const net.Interface.Name,
   9778 ) net.Interface.Name.ResolveError!net.Interface {
   9779     _ = userdata;
   9780     _ = name;
   9781     return error.InterfaceNotFound;
   9782 }
   9783 
   9784 fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
   9785     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9786     const current_thread = Thread.getCurrent(t);
   9787     try current_thread.checkCancel();
   9788 
   9789     if (native_os == .linux) {
   9790         _ = interface;
   9791         @panic("TODO implement netInterfaceName for linux");
   9792     }
   9793 
   9794     if (native_os == .windows) {
   9795         @panic("TODO implement netInterfaceName for windows");
   9796     }
   9797 
   9798     if (builtin.link_libc) {
   9799         @panic("TODO implement netInterfaceName for libc");
   9800     }
   9801 
   9802     @panic("unimplemented");
   9803 }
   9804 
   9805 fn netInterfaceNameUnavailable(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
   9806     _ = userdata;
   9807     _ = interface;
   9808     return error.Unexpected;
   9809 }
   9810 
   9811 fn netLookup(
   9812     userdata: ?*anyopaque,
   9813     host_name: HostName,
   9814     resolved: *Io.Queue(HostName.LookupResult),
   9815     options: HostName.LookupOptions,
   9816 ) net.HostName.LookupError!void {
   9817     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9818     defer resolved.close(io(t));
   9819     netLookupFallible(t, host_name, resolved, options) catch |err| switch (err) {
   9820         error.Closed => unreachable, // `resolved` must not be closed until `netLookup` returns
   9821         else => |e| return e,
   9822     };
   9823 }
   9824 
   9825 fn netLookupUnavailable(
   9826     userdata: ?*anyopaque,
   9827     host_name: HostName,
   9828     resolved: *Io.Queue(HostName.LookupResult),
   9829     options: HostName.LookupOptions,
   9830 ) net.HostName.LookupError!void {
   9831     _ = host_name;
   9832     _ = options;
   9833     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9834     resolved.close(ioBasic(t));
   9835     return error.NetworkDown;
   9836 }
   9837 
   9838 fn netLookupFallible(
   9839     t: *Threaded,
   9840     host_name: HostName,
   9841     resolved: *Io.Queue(HostName.LookupResult),
   9842     options: HostName.LookupOptions,
   9843 ) (net.HostName.LookupError || Io.QueueClosedError)!void {
   9844     if (!have_networking) return error.NetworkDown;
   9845 
   9846     const current_thread: *Thread = .getCurrent(t);
   9847     const t_io = io(t);
   9848     const name = host_name.bytes;
   9849     assert(name.len <= HostName.max_len);
   9850 
   9851     if (is_windows) {
   9852         var name_buffer: [HostName.max_len + 1]u16 = undefined;
   9853         const name_len = std.unicode.wtf8ToWtf16Le(&name_buffer, host_name.bytes) catch
   9854             unreachable; // HostName is prevalidated.
   9855         name_buffer[name_len] = 0;
   9856         const name_w = name_buffer[0..name_len :0];
   9857 
   9858         var port_buffer: [8]u8 = undefined;
   9859         var port_buffer_wide: [8]u16 = undefined;
   9860         const port = std.fmt.bufPrint(&port_buffer, "{d}", .{options.port}) catch
   9861             unreachable; // `port_buffer` is big enough for decimal u16.
   9862         for (port, port_buffer_wide[0..port.len]) |byte, *wide|
   9863             wide.* = std.mem.nativeToLittle(u16, byte);
   9864         port_buffer_wide[port.len] = 0;
   9865         const port_w = port_buffer_wide[0..port.len :0];
   9866 
   9867         const hints: ws2_32.ADDRINFOEXW = .{
   9868             .flags = .{ .NUMERICSERV = true },
   9869             .family = if (options.family) |f| switch (f) {
   9870                 .ip4 => posix.AF.INET,
   9871                 .ip6 => posix.AF.INET6,
   9872             } else posix.AF.UNSPEC,
   9873             .socktype = posix.SOCK.STREAM,
   9874             .protocol = posix.IPPROTO.TCP,
   9875             .canonname = null,
   9876             .addr = null,
   9877             .addrlen = 0,
   9878             .blob = null,
   9879             .bloblen = 0,
   9880             .provider = null,
   9881             .next = null,
   9882         };
   9883         const cancel_handle: ?*windows.HANDLE = null;
   9884         var res: *ws2_32.ADDRINFOEXW = undefined;
   9885         const timeout: ?*ws2_32.timeval = null;
   9886         while (true) {
   9887             try current_thread.checkCancel(); // TODO make requestCancel call GetAddrInfoExCancel
   9888             // TODO make this append to the queue eagerly rather than blocking until
   9889             // the whole thing finishes
   9890             const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, cancel_handle));
   9891             switch (rc) {
   9892                 @as(ws2_32.WinsockError, @enumFromInt(0)) => break,
   9893                 .EINTR => continue,
   9894                 .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => return error.Canceled,
   9895                 .NOTINITIALISED => {
   9896                     try initializeWsa(t);
   9897                     continue;
   9898                 },
   9899                 .TRY_AGAIN => return error.NameServerFailure,
   9900                 .EINVAL => |err| return wsaErrorBug(err),
   9901                 .NO_RECOVERY => return error.NameServerFailure,
   9902                 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
   9903                 .NOT_ENOUGH_MEMORY => return error.SystemResources,
   9904                 .HOST_NOT_FOUND => return error.UnknownHostName,
   9905                 .TYPE_NOT_FOUND => return error.ProtocolUnsupportedByAddressFamily,
   9906                 .ESOCKTNOSUPPORT => return error.ProtocolUnsupportedBySystem,
   9907                 else => |err| return windows.unexpectedWSAError(err),
   9908             }
   9909         }
   9910         defer ws2_32.FreeAddrInfoExW(res);
   9911 
   9912         var it: ?*ws2_32.ADDRINFOEXW = res;
   9913         var canon_name: ?[*:0]const u16 = null;
   9914         while (it) |info| : (it = info.next) {
   9915             const addr = info.addr orelse continue;
   9916             const storage: WsaAddress = .{ .any = addr.* };
   9917             try resolved.putOne(t_io, .{ .address = addressFromWsa(&storage) });
   9918 
   9919             if (info.canonname) |n| {
   9920                 if (canon_name == null) {
   9921                     canon_name = n;
   9922                 }
   9923             }
   9924         }
   9925         if (canon_name) |n| {
   9926             const len = std.unicode.wtf16LeToWtf8(options.canonical_name_buffer, std.mem.sliceTo(n, 0));
   9927             try resolved.putOne(t_io, .{ .canonical_name = .{
   9928                 .bytes = options.canonical_name_buffer[0..len],
   9929             } });
   9930         }
   9931         return;
   9932     }
   9933 
   9934     // On Linux, glibc provides getaddrinfo_a which is capable of supporting our semantics.
   9935     // However, musl's POSIX-compliant getaddrinfo is not, so we bypass it.
   9936 
   9937     if (builtin.target.isGnuLibC()) {
   9938         // TODO use getaddrinfo_a / gai_cancel
   9939     }
   9940 
   9941     if (native_os == .linux) {
   9942         if (options.family != .ip4) {
   9943             if (IpAddress.parseIp6(name, options.port)) |addr| {
   9944                 try resolved.putAll(t_io, &.{
   9945                     .{ .address = addr },
   9946                     .{ .canonical_name = copyCanon(options.canonical_name_buffer, name) },
   9947                 });
   9948                 return;
   9949             } else |_| {}
   9950         }
   9951 
   9952         if (options.family != .ip6) {
   9953             if (IpAddress.parseIp4(name, options.port)) |addr| {
   9954                 try resolved.putAll(t_io, &.{
   9955                     .{ .address = addr },
   9956                     .{ .canonical_name = copyCanon(options.canonical_name_buffer, name) },
   9957                 });
   9958                 return;
   9959             } else |_| {}
   9960         }
   9961 
   9962         lookupHosts(t, host_name, resolved, options) catch |err| switch (err) {
   9963             error.UnknownHostName => {},
   9964             else => |e| return e,
   9965         };
   9966 
   9967         // RFC 6761 Section 6.3.3
   9968         // Name resolution APIs and libraries SHOULD recognize
   9969         // localhost names as special and SHOULD always return the IP
   9970         // loopback address for address queries and negative responses
   9971         // for all other query types.
   9972 
   9973         // Check for equal to "localhost(.)" or ends in ".localhost(.)"
   9974         const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost";
   9975         if (std.mem.endsWith(u8, name, localhost) and
   9976             (name.len == localhost.len or name[name.len - localhost.len] == '.'))
   9977         {
   9978             var results_buffer: [3]HostName.LookupResult = undefined;
   9979             var results_index: usize = 0;
   9980             if (options.family != .ip4) {
   9981                 results_buffer[results_index] = .{ .address = .{ .ip6 = .loopback(options.port) } };
   9982                 results_index += 1;
   9983             }
   9984             if (options.family != .ip6) {
   9985                 results_buffer[results_index] = .{ .address = .{ .ip4 = .loopback(options.port) } };
   9986                 results_index += 1;
   9987             }
   9988             const canon_name = "localhost";
   9989             const canon_name_dest = options.canonical_name_buffer[0..canon_name.len];
   9990             canon_name_dest.* = canon_name.*;
   9991             results_buffer[results_index] = .{ .canonical_name = .{ .bytes = canon_name_dest } };
   9992             results_index += 1;
   9993             try resolved.putAll(t_io, results_buffer[0..results_index]);
   9994             return;
   9995         }
   9996 
   9997         return lookupDnsSearch(t, host_name, resolved, options);
   9998     }
   9999 
  10000     if (native_os == .openbsd) {
  10001         // TODO use getaddrinfo_async / asr_abort
  10002     }
  10003 
  10004     if (native_os == .freebsd) {
  10005         // TODO use dnsres_getaddrinfo
  10006     }
  10007 
  10008     if (is_darwin) {
  10009         // TODO use CFHostStartInfoResolution / CFHostCancelInfoResolution
  10010     }
  10011 
  10012     if (builtin.link_libc) {
  10013         // This operating system lacks a way to resolve asynchronously. We are
  10014         // stuck with getaddrinfo.
  10015         var name_buffer: [HostName.max_len + 1]u8 = undefined;
  10016         @memcpy(name_buffer[0..host_name.bytes.len], host_name.bytes);
  10017         name_buffer[host_name.bytes.len] = 0;
  10018         const name_c = name_buffer[0..host_name.bytes.len :0];
  10019 
  10020         var port_buffer: [8]u8 = undefined;
  10021         const port_c = std.fmt.bufPrintZ(&port_buffer, "{d}", .{options.port}) catch unreachable;
  10022 
  10023         const hints: posix.addrinfo = .{
  10024             .flags = .{ .NUMERICSERV = true },
  10025             .family = posix.AF.UNSPEC,
  10026             .socktype = posix.SOCK.STREAM,
  10027             .protocol = posix.IPPROTO.TCP,
  10028             .canonname = null,
  10029             .addr = null,
  10030             .addrlen = 0,
  10031             .next = null,
  10032         };
  10033         var res: ?*posix.addrinfo = null;
  10034         try current_thread.beginSyscall();
  10035         while (true) {
  10036             switch (posix.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {
  10037                 @as(posix.system.EAI, @enumFromInt(0)) => {
  10038                     current_thread.endSyscall();
  10039                     break;
  10040                 },
  10041                 .SYSTEM => switch (posix.errno(-1)) {
  10042                     .INTR => {
  10043                         try current_thread.checkCancel();
  10044                         continue;
  10045                     },
  10046                     .CANCELED => return current_thread.endSyscallCanceled(),
  10047                     else => |e| {
  10048                         current_thread.endSyscall();
  10049                         return posix.unexpectedErrno(e);
  10050                     },
  10051                 },
  10052                 else => |e| {
  10053                     current_thread.endSyscall();
  10054                     switch (e) {
  10055                         .ADDRFAMILY => return error.AddressFamilyUnsupported,
  10056                         .AGAIN => return error.NameServerFailure,
  10057                         .FAIL => return error.NameServerFailure,
  10058                         .FAMILY => return error.AddressFamilyUnsupported,
  10059                         .MEMORY => return error.SystemResources,
  10060                         .NODATA => return error.UnknownHostName,
  10061                         .NONAME => return error.UnknownHostName,
  10062                         else => return error.Unexpected,
  10063                     }
  10064                 },
  10065             }
  10066         }
  10067         defer if (res) |some| posix.system.freeaddrinfo(some);
  10068 
  10069         var it = res;
  10070         var canon_name: ?[*:0]const u8 = null;
  10071         while (it) |info| : (it = info.next) {
  10072             const addr = info.addr orelse continue;
  10073             const storage: PosixAddress = .{ .any = addr.* };
  10074             try resolved.putOne(t_io, .{ .address = addressFromPosix(&storage) });
  10075 
  10076             if (info.canonname) |n| {
  10077                 if (canon_name == null) {
  10078                     canon_name = n;
  10079                 }
  10080             }
  10081         }
  10082         if (canon_name) |n| {
  10083             try resolved.putOne(t_io, .{
  10084                 .canonical_name = copyCanon(options.canonical_name_buffer, std.mem.sliceTo(n, 0)),
  10085             });
  10086         }
  10087         return;
  10088     }
  10089 
  10090     return error.OptionUnsupported;
  10091 }
  10092 
  10093 fn lockStderrWriter(userdata: ?*anyopaque, buffer: []u8) Io.Cancelable!*File.Writer {
  10094     const t: *Threaded = @ptrCast(@alignCast(userdata));
  10095     // Only global mutex since this is Threaded.
  10096     Io.stderr_thread_mutex.lock();
  10097     if (!t.stderr_writer_initialized) {
  10098         const io_t = ioBasic(t);
  10099         if (is_windows) t.stderr_writer.file = .stderr();
  10100         t.stderr_writer.io = io_t;
  10101         t.stderr_writer.mode = try .detect(io_t, t.stderr_writer.file, true, .streaming_simple);
  10102         t.stderr_writer_initialized = true;
  10103     }
  10104     std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch {};
  10105     t.stderr_writer.interface.flush() catch {};
  10106     t.stderr_writer.interface.buffer = buffer;
  10107     return &t.stderr_writer;
  10108 }
  10109 
  10110 fn tryLockStderrWriter(userdata: ?*anyopaque, buffer: []u8) ?*File.Writer {
  10111     const t: *Threaded = @ptrCast(@alignCast(userdata));
  10112     // Only global mutex since this is Threaded.
  10113     if (!Io.stderr_thread_mutex.tryLock()) return null;
  10114     if (!t.stderr_writer_initialized) {
  10115         const io_t = ioBasic(t);
  10116         if (is_windows) t.stderr_writer.file = .stderr();
  10117         t.stderr_writer.io = io_t;
  10118         t.stderr_writer.mode = File.Writer.Mode.detect(io_t, t.stderr_writer.file, true, .streaming_simple) catch
  10119             return null;
  10120         t.stderr_writer_initialized = true;
  10121     }
  10122     std.Progress.clearWrittenWithEscapeCodes(&t.stderr_writer) catch {};
  10123     t.stderr_writer.interface.flush() catch {};
  10124     t.stderr_writer.interface.buffer = buffer;
  10125     return &t.stderr_writer;
  10126 }
  10127 
  10128 fn unlockStderrWriter(userdata: ?*anyopaque) void {
  10129     const t: *Threaded = @ptrCast(@alignCast(userdata));
  10130     t.stderr_writer.interface.flush() catch {};
  10131     t.stderr_writer.interface.end = 0;
  10132     t.stderr_writer.interface.buffer = &.{};
  10133     Io.stderr_thread_mutex.unlock();
  10134 }
  10135 
  10136 pub const PosixAddress = extern union {
  10137     any: posix.sockaddr,
  10138     in: posix.sockaddr.in,
  10139     in6: posix.sockaddr.in6,
  10140 };
  10141 
  10142 const UnixAddress = extern union {
  10143     any: posix.sockaddr,
  10144     un: posix.sockaddr.un,
  10145 };
  10146 
  10147 const WsaAddress = extern union {
  10148     any: ws2_32.sockaddr,
  10149     in: ws2_32.sockaddr.in,
  10150     in6: ws2_32.sockaddr.in6,
  10151     un: ws2_32.sockaddr.un,
  10152 };
  10153 
  10154 pub fn posixAddressFamily(a: *const IpAddress) posix.sa_family_t {
  10155     return switch (a.*) {
  10156         .ip4 => posix.AF.INET,
  10157         .ip6 => posix.AF.INET6,
  10158     };
  10159 }
  10160 
  10161 pub fn addressFromPosix(posix_address: *const PosixAddress) IpAddress {
  10162     return switch (posix_address.any.family) {
  10163         posix.AF.INET => .{ .ip4 = address4FromPosix(&posix_address.in) },
  10164         posix.AF.INET6 => .{ .ip6 = address6FromPosix(&posix_address.in6) },
  10165         else => .{ .ip4 = .loopback(0) },
  10166     };
  10167 }
  10168 
  10169 fn addressFromWsa(wsa_address: *const WsaAddress) IpAddress {
  10170     return switch (wsa_address.any.family) {
  10171         posix.AF.INET => .{ .ip4 = address4FromWsa(&wsa_address.in) },
  10172         posix.AF.INET6 => .{ .ip6 = address6FromWsa(&wsa_address.in6) },
  10173         else => .{ .ip4 = .loopback(0) },
  10174     };
  10175 }
  10176 
  10177 pub fn addressToPosix(a: *const IpAddress, storage: *PosixAddress) posix.socklen_t {
  10178     return switch (a.*) {
  10179         .ip4 => |ip4| {
  10180             storage.in = address4ToPosix(ip4);
  10181             return @sizeOf(posix.sockaddr.in);
  10182         },
  10183         .ip6 => |*ip6| {
  10184             storage.in6 = address6ToPosix(ip6);
  10185             return @sizeOf(posix.sockaddr.in6);
  10186         },
  10187     };
  10188 }
  10189 
  10190 fn addressToWsa(a: *const IpAddress, storage: *WsaAddress) i32 {
  10191     return switch (a.*) {
  10192         .ip4 => |ip4| {
  10193             storage.in = address4ToPosix(ip4);
  10194             return @sizeOf(posix.sockaddr.in);
  10195         },
  10196         .ip6 => |*ip6| {
  10197             storage.in6 = address6ToPosix(ip6);
  10198             return @sizeOf(posix.sockaddr.in6);
  10199         },
  10200     };
  10201 }
  10202 
  10203 fn addressUnixToPosix(a: *const net.UnixAddress, storage: *UnixAddress) posix.socklen_t {
  10204     @memcpy(storage.un.path[0..a.path.len], a.path);
  10205     storage.un.family = posix.AF.UNIX;
  10206     storage.un.path[a.path.len] = 0;
  10207     return @sizeOf(posix.sockaddr.un);
  10208 }
  10209 
  10210 fn addressUnixToWsa(a: *const net.UnixAddress, storage: *WsaAddress) i32 {
  10211     @memcpy(storage.un.path[0..a.path.len], a.path);
  10212     storage.un.family = posix.AF.UNIX;
  10213     storage.un.path[a.path.len] = 0;
  10214     return @sizeOf(posix.sockaddr.un);
  10215 }
  10216 
  10217 fn address4FromPosix(in: *const posix.sockaddr.in) net.Ip4Address {
  10218     return .{
  10219         .port = std.mem.bigToNative(u16, in.port),
  10220         .bytes = @bitCast(in.addr),
  10221     };
  10222 }
  10223 
  10224 fn address6FromPosix(in6: *const posix.sockaddr.in6) net.Ip6Address {
  10225     return .{
  10226         .port = std.mem.bigToNative(u16, in6.port),
  10227         .bytes = in6.addr,
  10228         .flow = in6.flowinfo,
  10229         .interface = .{ .index = in6.scope_id },
  10230     };
  10231 }
  10232 
  10233 fn address4FromWsa(in: *const ws2_32.sockaddr.in) net.Ip4Address {
  10234     return .{
  10235         .port = std.mem.bigToNative(u16, in.port),
  10236         .bytes = @bitCast(in.addr),
  10237     };
  10238 }
  10239 
  10240 fn address6FromWsa(in6: *const ws2_32.sockaddr.in6) net.Ip6Address {
  10241     return .{
  10242         .port = std.mem.bigToNative(u16, in6.port),
  10243         .bytes = in6.addr,
  10244         .flow = in6.flowinfo,
  10245         .interface = .{ .index = in6.scope_id },
  10246     };
  10247 }
  10248 
  10249 fn address4ToPosix(a: net.Ip4Address) posix.sockaddr.in {
  10250     return .{
  10251         .port = std.mem.nativeToBig(u16, a.port),
  10252         .addr = @bitCast(a.bytes),
  10253     };
  10254 }
  10255 
  10256 fn address6ToPosix(a: *const net.Ip6Address) posix.sockaddr.in6 {
  10257     return .{
  10258         .port = std.mem.nativeToBig(u16, a.port),
  10259         .flowinfo = a.flow,
  10260         .addr = a.bytes,
  10261         .scope_id = a.interface.index,
  10262     };
  10263 }
  10264 
  10265 pub fn errnoBug(err: posix.E) Io.UnexpectedError {
  10266     if (is_debug) std.debug.panic("programmer bug caused syscall error: {t}", .{err});
  10267     return error.Unexpected;
  10268 }
  10269 
  10270 fn wsaErrorBug(err: ws2_32.WinsockError) Io.UnexpectedError {
  10271     if (is_debug) std.debug.panic("programmer bug caused syscall error: {t}", .{err});
  10272     return error.Unexpected;
  10273 }
  10274 
  10275 pub fn posixSocketMode(mode: net.Socket.Mode) u32 {
  10276     return switch (mode) {
  10277         .stream => posix.SOCK.STREAM,
  10278         .dgram => posix.SOCK.DGRAM,
  10279         .seqpacket => posix.SOCK.SEQPACKET,
  10280         .raw => posix.SOCK.RAW,
  10281         .rdm => posix.SOCK.RDM,
  10282     };
  10283 }
  10284 
  10285 pub fn posixProtocol(protocol: ?net.Protocol) u32 {
  10286     return @intFromEnum(protocol orelse return 0);
  10287 }
  10288 
  10289 fn recoverableOsBugDetected() void {
  10290     if (is_debug) unreachable;
  10291 }
  10292 
  10293 fn clockToPosix(clock: Io.Clock) posix.clockid_t {
  10294     return switch (clock) {
  10295         .real => posix.CLOCK.REALTIME,
  10296         .awake => switch (native_os) {
  10297             .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => posix.CLOCK.UPTIME_RAW,
  10298             else => posix.CLOCK.MONOTONIC,
  10299         },
  10300         .boot => switch (native_os) {
  10301             .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => posix.CLOCK.MONOTONIC_RAW,
  10302             // On freebsd derivatives, use MONOTONIC_FAST as currently there's
  10303             // no precision tradeoff.
  10304             .freebsd, .dragonfly => posix.CLOCK.MONOTONIC_FAST,
  10305             // On linux, use BOOTTIME instead of MONOTONIC as it ticks while
  10306             // suspended.
  10307             .linux => posix.CLOCK.BOOTTIME,
  10308             // On other posix systems, MONOTONIC is generally the fastest and
  10309             // ticks while suspended.
  10310             else => posix.CLOCK.MONOTONIC,
  10311         },
  10312         .cpu_process => posix.CLOCK.PROCESS_CPUTIME_ID,
  10313         .cpu_thread => posix.CLOCK.THREAD_CPUTIME_ID,
  10314     };
  10315 }
  10316 
  10317 fn clockToWasi(clock: Io.Clock) std.os.wasi.clockid_t {
  10318     return switch (clock) {
  10319         .real => .REALTIME,
  10320         .awake => .MONOTONIC,
  10321         .boot => .MONOTONIC,
  10322         .cpu_process => .PROCESS_CPUTIME_ID,
  10323         .cpu_thread => .THREAD_CPUTIME_ID,
  10324     };
  10325 }
  10326 
  10327 fn statFromLinux(stx: *const std.os.linux.Statx) File.Stat {
  10328     const atime = stx.atime;
  10329     const mtime = stx.mtime;
  10330     const ctime = stx.ctime;
  10331     return .{
  10332         .inode = stx.ino,
  10333         .size = stx.size,
  10334         .permissions = .fromMode(stx.mode),
  10335         .kind = switch (stx.mode & std.os.linux.S.IFMT) {
  10336             std.os.linux.S.IFDIR => .directory,
  10337             std.os.linux.S.IFCHR => .character_device,
  10338             std.os.linux.S.IFBLK => .block_device,
  10339             std.os.linux.S.IFREG => .file,
  10340             std.os.linux.S.IFIFO => .named_pipe,
  10341             std.os.linux.S.IFLNK => .sym_link,
  10342             std.os.linux.S.IFSOCK => .unix_domain_socket,
  10343             else => .unknown,
  10344         },
  10345         .atime = .{ .nanoseconds = @intCast(@as(i128, atime.sec) * std.time.ns_per_s + atime.nsec) },
  10346         .mtime = .{ .nanoseconds = @intCast(@as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec) },
  10347         .ctime = .{ .nanoseconds = @intCast(@as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec) },
  10348     };
  10349 }
  10350 
  10351 fn statFromPosix(st: *const posix.Stat) File.Stat {
  10352     const atime = st.atime();
  10353     const mtime = st.mtime();
  10354     const ctime = st.ctime();
  10355     return .{
  10356         .inode = st.ino,
  10357         .size = @bitCast(st.size),
  10358         .permissions = .fromMode(st.mode),
  10359         .kind = k: {
  10360             const m = st.mode & posix.S.IFMT;
  10361             switch (m) {
  10362                 posix.S.IFBLK => break :k .block_device,
  10363                 posix.S.IFCHR => break :k .character_device,
  10364                 posix.S.IFDIR => break :k .directory,
  10365                 posix.S.IFIFO => break :k .named_pipe,
  10366                 posix.S.IFLNK => break :k .sym_link,
  10367                 posix.S.IFREG => break :k .file,
  10368                 posix.S.IFSOCK => break :k .unix_domain_socket,
  10369                 else => {},
  10370             }
  10371             if (native_os == .illumos) switch (m) {
  10372                 posix.S.IFDOOR => break :k .door,
  10373                 posix.S.IFPORT => break :k .event_port,
  10374                 else => {},
  10375             };
  10376 
  10377             break :k .unknown;
  10378         },
  10379         .atime = timestampFromPosix(&atime),
  10380         .mtime = timestampFromPosix(&mtime),
  10381         .ctime = timestampFromPosix(&ctime),
  10382     };
  10383 }
  10384 
  10385 fn statFromWasi(st: *const std.os.wasi.filestat_t) File.Stat {
  10386     return .{
  10387         .inode = st.ino,
  10388         .size = @bitCast(st.size),
  10389         .mode = 0,
  10390         .kind = switch (st.filetype) {
  10391             .BLOCK_DEVICE => .block_device,
  10392             .CHARACTER_DEVICE => .character_device,
  10393             .DIRECTORY => .directory,
  10394             .SYMBOLIC_LINK => .sym_link,
  10395             .REGULAR_FILE => .file,
  10396             .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket,
  10397             else => .unknown,
  10398         },
  10399         .atime = .fromNanoseconds(st.atim),
  10400         .mtime = .fromNanoseconds(st.mtim),
  10401         .ctime = .fromNanoseconds(st.ctim),
  10402     };
  10403 }
  10404 
  10405 fn timestampFromPosix(timespec: *const posix.timespec) Io.Timestamp {
  10406     return .{ .nanoseconds = @intCast(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec) };
  10407 }
  10408 
  10409 fn timestampToPosix(nanoseconds: i96) posix.timespec {
  10410     return .{
  10411         .sec = @intCast(@divFloor(nanoseconds, std.time.ns_per_s)),
  10412         .nsec = @intCast(@mod(nanoseconds, std.time.ns_per_s)),
  10413     };
  10414 }
  10415 
  10416 fn pathToPosix(file_path: []const u8, buffer: *[posix.PATH_MAX]u8) Dir.PathNameError![:0]u8 {
  10417     if (std.mem.containsAtLeastScalar2(u8, file_path, 0, 1)) return error.BadPathName;
  10418     // >= rather than > to make room for the null byte
  10419     if (file_path.len >= buffer.len) return error.NameTooLong;
  10420     @memcpy(buffer[0..file_path.len], file_path);
  10421     buffer[file_path.len] = 0;
  10422     return buffer[0..file_path.len :0];
  10423 }
  10424 
  10425 fn lookupDnsSearch(
  10426     t: *Threaded,
  10427     host_name: HostName,
  10428     resolved: *Io.Queue(HostName.LookupResult),
  10429     options: HostName.LookupOptions,
  10430 ) (HostName.LookupError || Io.QueueClosedError)!void {
  10431     const t_io = io(t);
  10432     const rc = HostName.ResolvConf.init(t_io) catch return error.ResolvConfParseFailed;
  10433 
  10434     // Count dots, suppress search when >=ndots or name ends in
  10435     // a dot, which is an explicit request for global scope.
  10436     const dots = std.mem.countScalar(u8, host_name.bytes, '.');
  10437     const search_len = if (dots >= rc.ndots or std.mem.endsWith(u8, host_name.bytes, ".")) 0 else rc.search_len;
  10438     const search = rc.search_buffer[0..search_len];
  10439 
  10440     var canon_name = host_name.bytes;
  10441 
  10442     // Strip final dot for canon, fail if multiple trailing dots.
  10443     if (std.mem.endsWith(u8, canon_name, ".")) canon_name.len -= 1;
  10444     if (std.mem.endsWith(u8, canon_name, ".")) return error.UnknownHostName;
  10445 
  10446     // Name with search domain appended is set up in `canon_name`. This
  10447     // both provides the desired default canonical name (if the requested
  10448     // name is not a CNAME record) and serves as a buffer for passing the
  10449     // full requested name to `lookupDns`.
  10450     @memcpy(options.canonical_name_buffer[0..canon_name.len], canon_name);
  10451     options.canonical_name_buffer[canon_name.len] = '.';
  10452     var it = std.mem.tokenizeAny(u8, search, " \t");
  10453     while (it.next()) |token| {
  10454         @memcpy(options.canonical_name_buffer[canon_name.len + 1 ..][0..token.len], token);
  10455         const lookup_canon_name = options.canonical_name_buffer[0 .. canon_name.len + 1 + token.len];
  10456         if (lookupDns(t, lookup_canon_name, &rc, resolved, options)) |result| {
  10457             return result;
  10458         } else |err| switch (err) {
  10459             error.UnknownHostName => continue,
  10460             else => |e| return e,
  10461         }
  10462     }
  10463 
  10464     const lookup_canon_name = options.canonical_name_buffer[0..canon_name.len];
  10465     return lookupDns(t, lookup_canon_name, &rc, resolved, options);
  10466 }
  10467 
  10468 fn lookupDns(
  10469     t: *Threaded,
  10470     lookup_canon_name: []const u8,
  10471     rc: *const HostName.ResolvConf,
  10472     resolved: *Io.Queue(HostName.LookupResult),
  10473     options: HostName.LookupOptions,
  10474 ) (HostName.LookupError || Io.QueueClosedError)!void {
  10475     const t_io = io(t);
  10476     const family_records: [2]struct { af: IpAddress.Family, rr: HostName.DnsRecord } = .{
  10477         .{ .af = .ip6, .rr = .A },
  10478         .{ .af = .ip4, .rr = .AAAA },
  10479     };
  10480     var query_buffers: [2][280]u8 = undefined;
  10481     var answer_buffer: [2 * 512]u8 = undefined;
  10482     var queries_buffer: [2][]const u8 = undefined;
  10483     var answers_buffer: [2][]const u8 = undefined;
  10484     var nq: usize = 0;
  10485     var answer_buffer_i: usize = 0;
  10486 
  10487     for (family_records) |fr| {
  10488         if (options.family != fr.af) {
  10489             const entropy = std.crypto.random.array(u8, 2);
  10490             const len = writeResolutionQuery(&query_buffers[nq], 0, lookup_canon_name, 1, fr.rr, entropy);
  10491             queries_buffer[nq] = query_buffers[nq][0..len];
  10492             nq += 1;
  10493         }
  10494     }
  10495 
  10496     var ip4_mapped_buffer: [HostName.ResolvConf.max_nameservers]IpAddress = undefined;
  10497     const ip4_mapped = ip4_mapped_buffer[0..rc.nameservers_len];
  10498     var any_ip6 = false;
  10499     for (rc.nameservers(), ip4_mapped) |*ns, *m| {
  10500         m.* = .{ .ip6 = .fromAny(ns.*) };
  10501         any_ip6 = any_ip6 or ns.* == .ip6;
  10502     }
  10503     var socket = s: {
  10504         if (any_ip6) ip6: {
  10505             const ip6_addr: IpAddress = .{ .ip6 = .unspecified(0) };
  10506             const socket = ip6_addr.bind(t_io, .{ .ip6_only = true, .mode = .dgram }) catch |err| switch (err) {
  10507                 error.AddressFamilyUnsupported => break :ip6,
  10508                 else => |e| return e,
  10509             };
  10510             break :s socket;
  10511         }
  10512         any_ip6 = false;
  10513         const ip4_addr: IpAddress = .{ .ip4 = .unspecified(0) };
  10514         const socket = try ip4_addr.bind(t_io, .{ .mode = .dgram });
  10515         break :s socket;
  10516     };
  10517     defer socket.close(t_io);
  10518 
  10519     const mapped_nameservers = if (any_ip6) ip4_mapped else rc.nameservers();
  10520     const queries = queries_buffer[0..nq];
  10521     const answers = answers_buffer[0..queries.len];
  10522     var answers_remaining = answers.len;
  10523     for (answers) |*answer| answer.len = 0;
  10524 
  10525     // boot clock is chosen because time the computer is suspended should count
  10526     // against time spent waiting for external messages to arrive.
  10527     const clock: Io.Clock = .boot;
  10528     var now_ts = try clock.now(t_io);
  10529     const final_ts = now_ts.addDuration(.fromSeconds(rc.timeout_seconds));
  10530     const attempt_duration: Io.Duration = .{
  10531         .nanoseconds = (std.time.ns_per_s / rc.attempts) * @as(i96, rc.timeout_seconds),
  10532     };
  10533 
  10534     send: while (now_ts.nanoseconds < final_ts.nanoseconds) : (now_ts = try clock.now(t_io)) {
  10535         const max_messages = queries_buffer.len * HostName.ResolvConf.max_nameservers;
  10536         {
  10537             var message_buffer: [max_messages]Io.net.OutgoingMessage = undefined;
  10538             var message_i: usize = 0;
  10539             for (queries, answers) |query, *answer| {
  10540                 if (answer.len != 0) continue;
  10541                 for (mapped_nameservers) |*ns| {
  10542                     message_buffer[message_i] = .{
  10543                         .address = ns,
  10544                         .data_ptr = query.ptr,
  10545                         .data_len = query.len,
  10546                     };
  10547                     message_i += 1;
  10548                 }
  10549             }
  10550             _ = netSendPosix(t, socket.handle, message_buffer[0..message_i], .{});
  10551         }
  10552 
  10553         const timeout: Io.Timeout = .{ .deadline = .{
  10554             .raw = now_ts.addDuration(attempt_duration),
  10555             .clock = clock,
  10556         } };
  10557 
  10558         while (true) {
  10559             var message_buffer: [max_messages]Io.net.IncomingMessage = @splat(.init);
  10560             const buf = answer_buffer[answer_buffer_i..];
  10561             const recv_err, const recv_n = socket.receiveManyTimeout(t_io, &message_buffer, buf, .{}, timeout);
  10562             for (message_buffer[0..recv_n]) |*received_message| {
  10563                 const reply = received_message.data;
  10564                 // Ignore non-identifiable packets.
  10565                 if (reply.len < 4) continue;
  10566 
  10567                 // Ignore replies from addresses we didn't send to.
  10568                 const ns = for (mapped_nameservers) |*ns| {
  10569                     if (received_message.from.eql(ns)) break ns;
  10570                 } else {
  10571                     continue;
  10572                 };
  10573 
  10574                 // Find which query this answer goes with, if any.
  10575                 const query, const answer = for (queries, answers) |query, *answer| {
  10576                     if (reply[0] == query[0] and reply[1] == query[1]) break .{ query, answer };
  10577                 } else {
  10578                     continue;
  10579                 };
  10580                 if (answer.len != 0) continue;
  10581 
  10582                 // Only accept positive or negative responses; retry immediately on
  10583                 // server failure, and ignore all other codes such as refusal.
  10584                 switch (reply[3] & 15) {
  10585                     0, 3 => {
  10586                         answer.* = reply;
  10587                         answer_buffer_i += reply.len;
  10588                         answers_remaining -= 1;
  10589                         if (answer_buffer.len - answer_buffer_i == 0) break :send;
  10590                         if (answers_remaining == 0) break :send;
  10591                     },
  10592                     2 => {
  10593                         var retry_message: Io.net.OutgoingMessage = .{
  10594                             .address = ns,
  10595                             .data_ptr = query.ptr,
  10596                             .data_len = query.len,
  10597                         };
  10598                         _ = netSendPosix(t, socket.handle, (&retry_message)[0..1], .{});
  10599                         continue;
  10600                     },
  10601                     else => continue,
  10602                 }
  10603             }
  10604             if (recv_err) |err| switch (err) {
  10605                 error.Canceled => return error.Canceled,
  10606                 error.Timeout => continue :send,
  10607                 else => continue,
  10608             };
  10609         }
  10610     } else {
  10611         return error.NameServerFailure;
  10612     }
  10613 
  10614     var addresses_len: usize = 0;
  10615     var canonical_name: ?HostName = null;
  10616 
  10617     for (answers) |answer| {
  10618         var it = HostName.DnsResponse.init(answer) catch {
  10619             // Here we could potentially add diagnostics to the results queue.
  10620             continue;
  10621         };
  10622         while (it.next() catch {
  10623             // Here we could potentially add diagnostics to the results queue.
  10624             continue;
  10625         }) |record| switch (record.rr) {
  10626             .A => {
  10627                 const data = record.packet[record.data_off..][0..record.data_len];
  10628                 if (data.len != 4) return error.InvalidDnsARecord;
  10629                 try resolved.putOne(t_io, .{ .address = .{ .ip4 = .{
  10630                     .bytes = data[0..4].*,
  10631                     .port = options.port,
  10632                 } } });
  10633                 addresses_len += 1;
  10634             },
  10635             .AAAA => {
  10636                 const data = record.packet[record.data_off..][0..record.data_len];
  10637                 if (data.len != 16) return error.InvalidDnsAAAARecord;
  10638                 try resolved.putOne(t_io, .{ .address = .{ .ip6 = .{
  10639                     .bytes = data[0..16].*,
  10640                     .port = options.port,
  10641                 } } });
  10642                 addresses_len += 1;
  10643             },
  10644             .CNAME => {
  10645                 _, canonical_name = HostName.expand(record.packet, record.data_off, options.canonical_name_buffer) catch
  10646                     return error.InvalidDnsCnameRecord;
  10647             },
  10648             _ => continue,
  10649         };
  10650     }
  10651 
  10652     try resolved.putOne(t_io, .{ .canonical_name = canonical_name orelse .{ .bytes = lookup_canon_name } });
  10653     if (addresses_len == 0) return error.NameServerFailure;
  10654 }
  10655 
  10656 fn lookupHosts(
  10657     t: *Threaded,
  10658     host_name: HostName,
  10659     resolved: *Io.Queue(HostName.LookupResult),
  10660     options: HostName.LookupOptions,
  10661 ) !void {
  10662     const t_io = io(t);
  10663     const file = Dir.openFileAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) {
  10664         error.FileNotFound,
  10665         error.NotDir,
  10666         error.AccessDenied,
  10667         => return error.UnknownHostName,
  10668 
  10669         error.Canceled => |e| return e,
  10670 
  10671         else => {
  10672             // Here we could add more detailed diagnostics to the results queue.
  10673             return error.DetectingNetworkConfigurationFailed;
  10674         },
  10675     };
  10676     defer file.close(t_io);
  10677 
  10678     var line_buf: [512]u8 = undefined;
  10679     var file_reader = file.reader(t_io, &line_buf);
  10680     return lookupHostsReader(t, host_name, resolved, options, &file_reader.interface) catch |err| switch (err) {
  10681         error.ReadFailed => switch (file_reader.err.?) {
  10682             error.Canceled => |e| return e,
  10683             else => {
  10684                 // Here we could add more detailed diagnostics to the results queue.
  10685                 return error.DetectingNetworkConfigurationFailed;
  10686             },
  10687         },
  10688         error.Canceled,
  10689         error.Closed,
  10690         error.UnknownHostName,
  10691         => |e| return e,
  10692     };
  10693 }
  10694 
  10695 fn lookupHostsReader(
  10696     t: *Threaded,
  10697     host_name: HostName,
  10698     resolved: *Io.Queue(HostName.LookupResult),
  10699     options: HostName.LookupOptions,
  10700     reader: *Io.Reader,
  10701 ) error{ ReadFailed, Canceled, UnknownHostName, Closed }!void {
  10702     const t_io = io(t);
  10703     var addresses_len: usize = 0;
  10704     var canonical_name: ?HostName = null;
  10705     while (true) {
  10706         const line = reader.takeDelimiterExclusive('\n') catch |err| switch (err) {
  10707             error.StreamTooLong => {
  10708                 // Skip lines that are too long.
  10709                 _ = reader.discardDelimiterInclusive('\n') catch |e| switch (e) {
  10710                     error.EndOfStream => break,
  10711                     error.ReadFailed => return error.ReadFailed,
  10712                 };
  10713                 continue;
  10714             },
  10715             error.ReadFailed => return error.ReadFailed,
  10716             error.EndOfStream => break,
  10717         };
  10718         reader.toss(1);
  10719         var split_it = std.mem.splitScalar(u8, line, '#');
  10720         const no_comment_line = split_it.first();
  10721 
  10722         var line_it = std.mem.tokenizeAny(u8, no_comment_line, " \t");
  10723         const ip_text = line_it.next() orelse continue;
  10724         var first_name_text: ?[]const u8 = null;
  10725         while (line_it.next()) |name_text| {
  10726             if (std.mem.eql(u8, name_text, host_name.bytes)) {
  10727                 if (first_name_text == null) first_name_text = name_text;
  10728                 break;
  10729             }
  10730         } else continue;
  10731 
  10732         if (canonical_name == null) {
  10733             if (HostName.init(first_name_text.?)) |name_text| {
  10734                 if (name_text.bytes.len <= options.canonical_name_buffer.len) {
  10735                     const canonical_name_dest = options.canonical_name_buffer[0..name_text.bytes.len];
  10736                     @memcpy(canonical_name_dest, name_text.bytes);
  10737                     canonical_name = .{ .bytes = canonical_name_dest };
  10738                 }
  10739             } else |_| {}
  10740         }
  10741 
  10742         if (options.family != .ip6) {
  10743             if (IpAddress.parseIp4(ip_text, options.port)) |addr| {
  10744                 try resolved.putOne(t_io, .{ .address = addr });
  10745                 addresses_len += 1;
  10746             } else |_| {}
  10747         }
  10748         if (options.family != .ip4) {
  10749             if (IpAddress.parseIp6(ip_text, options.port)) |addr| {
  10750                 try resolved.putOne(t_io, .{ .address = addr });
  10751                 addresses_len += 1;
  10752             } else |_| {}
  10753         }
  10754     }
  10755 
  10756     if (canonical_name) |canon_name| try resolved.putOne(t_io, .{ .canonical_name = canon_name });
  10757     if (addresses_len == 0) return error.UnknownHostName;
  10758 }
  10759 
  10760 /// Writes DNS resolution query packet data to `w`; at most 280 bytes.
  10761 fn writeResolutionQuery(q: *[280]u8, op: u4, dname: []const u8, class: u8, ty: HostName.DnsRecord, entropy: [2]u8) usize {
  10762     // This implementation is ported from musl libc.
  10763     // A more idiomatic "ziggy" implementation would be welcome.
  10764     var name = dname;
  10765     if (std.mem.endsWith(u8, name, ".")) name.len -= 1;
  10766     assert(name.len <= 253);
  10767     const n = 17 + name.len + @intFromBool(name.len != 0);
  10768 
  10769     // Construct query template - ID will be filled later
  10770     q[0..2].* = entropy;
  10771     @memset(q[2..n], 0);
  10772     q[2] = @as(u8, op) * 8 + 1;
  10773     q[5] = 1;
  10774     @memcpy(q[13..][0..name.len], name);
  10775     var i: usize = 13;
  10776     var j: usize = undefined;
  10777     while (q[i] != 0) : (i = j + 1) {
  10778         j = i;
  10779         while (q[j] != 0 and q[j] != '.') : (j += 1) {}
  10780         // TODO determine the circumstances for this and whether or
  10781         // not this should be an error.
  10782         if (j - i - 1 > 62) unreachable;
  10783         q[i - 1] = @intCast(j - i);
  10784     }
  10785     q[i + 1] = @intFromEnum(ty);
  10786     q[i + 3] = class;
  10787     return n;
  10788 }
  10789 
  10790 fn copyCanon(canonical_name_buffer: *[HostName.max_len]u8, name: []const u8) HostName {
  10791     const dest = canonical_name_buffer[0..name.len];
  10792     @memcpy(dest, name);
  10793     return .{ .bytes = dest };
  10794 }
  10795 
  10796 /// Darwin XNU 7195.50.7.100.1 introduced __ulock_wait2 and migrated code paths (notably pthread_cond_t) towards it:
  10797 /// https://github.com/apple/darwin-xnu/commit/d4061fb0260b3ed486147341b72468f836ed6c8f#diff-08f993cc40af475663274687b7c326cc6c3031e0db3ac8de7b24624610616be6
  10798 ///
  10799 /// This XNU version appears to correspond to 11.0.1:
  10800 /// https://kernelshaman.blogspot.com/2021/01/building-xnu-for-macos-big-sur-1101.html
  10801 ///
  10802 /// ulock_wait() uses 32-bit micro-second timeouts where 0 = INFINITE or no-timeout
  10803 /// ulock_wait2() uses 64-bit nano-second timeouts (with the same convention)
  10804 const darwin_supports_ulock_wait2 = builtin.os.version_range.semver.min.major >= 11;
  10805 
  10806 fn closeSocketWindows(s: ws2_32.SOCKET) void {
  10807     const rc = ws2_32.closesocket(s);
  10808     if (is_debug) switch (rc) {
  10809         0 => {},
  10810         ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
  10811             else => recoverableOsBugDetected(),
  10812         },
  10813         else => recoverableOsBugDetected(),
  10814     };
  10815 }
  10816 
  10817 const Wsa = struct {
  10818     status: Status = .uninitialized,
  10819     mutex: Io.Mutex = .init,
  10820     init_error: ?Wsa.InitError = null,
  10821 
  10822     const Status = enum { uninitialized, initialized, failure };
  10823 
  10824     const InitError = error{
  10825         ProcessFdQuotaExceeded,
  10826         NetworkDown,
  10827         VersionUnsupported,
  10828         BlockingOperationInProgress,
  10829     } || Io.UnexpectedError;
  10830 };
  10831 
  10832 fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void {
  10833     const t_io = io(t);
  10834     const wsa = &t.wsa;
  10835     try wsa.mutex.lock(t_io);
  10836     defer wsa.mutex.unlock(t_io);
  10837     switch (wsa.status) {
  10838         .uninitialized => {
  10839             var wsa_data: ws2_32.WSADATA = undefined;
  10840             const minor_version = 2;
  10841             const major_version = 2;
  10842             switch (ws2_32.WSAStartup((@as(windows.WORD, minor_version) << 8) | major_version, &wsa_data)) {
  10843                 0 => {
  10844                     wsa.status = .initialized;
  10845                     return;
  10846                 },
  10847                 else => |err_int| {
  10848                     wsa.status = .failure;
  10849                     wsa.init_error = switch (@as(ws2_32.WinsockError, @enumFromInt(@as(u16, @intCast(err_int))))) {
  10850                         .SYSNOTREADY => error.NetworkDown,
  10851                         .VERNOTSUPPORTED => error.VersionUnsupported,
  10852                         .EINPROGRESS => error.BlockingOperationInProgress,
  10853                         .EPROCLIM => error.ProcessFdQuotaExceeded,
  10854                         else => |err| windows.unexpectedWSAError(err),
  10855                     };
  10856                 },
  10857             }
  10858         },
  10859         .initialized => return,
  10860         .failure => {},
  10861     }
  10862     return error.NetworkDown;
  10863 }
  10864 
  10865 fn doNothingSignalHandler(_: posix.SIG) callconv(.c) void {}
  10866 
  10867 test {
  10868     _ = @import("Threaded/test.zig");
  10869 }