zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob bd40bdf4 (775880B) - 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 process = std.process;
     17 const Allocator = std.mem.Allocator;
     18 const Alignment = std.mem.Alignment;
     19 const assert = std.debug.assert;
     20 const posix = std.posix;
     21 const windows = std.os.windows;
     22 const ws2_32 = windows.ws2_32;
     23 
     24 /// Thread-safe.
     25 ///
     26 /// Used for:
     27 /// * allocating `Io.Future` and `Io.Group` closures.
     28 /// * formatting spawning child processes
     29 /// * scanning environment variables on some targets
     30 /// * memory-mapping when mmap or equivalent is not available
     31 allocator: Allocator,
     32 mutex: Io.Mutex = .init,
     33 cond: Io.Condition = .init,
     34 run_queue: std.SinglyLinkedList = .{},
     35 join_requested: bool = false,
     36 stack_size: usize,
     37 /// All threads are spawned detached; this is how we wait until they all exit.
     38 wait_group: WaitGroup = .init,
     39 async_limit: Io.Limit,
     40 concurrent_limit: Io.Limit = .unlimited,
     41 /// Error from calling `std.Thread.getCpuCount` in `init`.
     42 cpu_count_error: ?std.Thread.CpuCountError,
     43 /// Number of threads that are unavailable to take tasks. To calculate
     44 /// available count, subtract this from either `async_limit` or
     45 /// `concurrent_limit`.
     46 busy_count: usize = 0,
     47 worker_threads: std.atomic.Value(?*Thread),
     48 pid: Pid = .unknown,
     49 
     50 wsa: if (is_windows) Wsa else struct {} = .{},
     51 
     52 have_signal_handler: bool,
     53 old_sig_io: if (have_sig_io) posix.Sigaction else void,
     54 old_sig_pipe: if (have_sig_pipe) posix.Sigaction else void,
     55 
     56 use_sendfile: UseSendfile = .default,
     57 use_copy_file_range: UseCopyFileRange = .default,
     58 use_fcopyfile: UseFcopyfile = .default,
     59 use_fchmodat2: UseFchmodat2 = .default,
     60 disable_memory_mapping: bool,
     61 
     62 stderr_writer: File.Writer = .{
     63     .io = undefined,
     64     .interface = File.Writer.initInterface(&.{}),
     65     .file = if (is_windows) undefined else .stderr(),
     66     .mode = .streaming,
     67 },
     68 stderr_mode: Io.Terminal.Mode = .no_color,
     69 stderr_writer_initialized: bool = false,
     70 stderr_mutex: Io.Mutex = .init,
     71 stderr_mutex_locker: std.Thread.Id = Thread.invalid_id,
     72 stderr_mutex_lock_count: usize = 0,
     73 
     74 argv0: Argv0,
     75 /// Protected by `mutex`. Determines whether `environ` has been
     76 /// memoized based on `process_environ`.
     77 environ_initialized: bool,
     78 environ: Environ,
     79 
     80 null_file: NullFile = .{},
     81 random_file: RandomFile = .{},
     82 pipe_file: PipeFile = .{},
     83 
     84 csprng: Csprng = .uninitialized,
     85 
     86 system_basic_information: SystemBasicInformation = .{},
     87 
     88 const SystemBasicInformation = if (!is_windows) struct {} else struct {
     89     buffer: windows.SYSTEM.BASIC_INFORMATION = undefined,
     90     initialized: std.atomic.Value(bool) = .{ .raw = false },
     91 };
     92 
     93 pub const Csprng = struct {
     94     rng: std.Random.DefaultCsprng,
     95 
     96     pub const uninitialized: Csprng = .{ .rng = .{
     97         .state = undefined,
     98         .offset = std.math.maxInt(usize),
     99     } };
    100 
    101     pub const seed_len = std.Random.DefaultCsprng.secret_seed_length;
    102 
    103     pub fn isInitialized(c: *const Csprng) bool {
    104         return c.rng.offset != std.math.maxInt(usize);
    105     }
    106 };
    107 
    108 pub const Argv0 = switch (native_os) {
    109     .openbsd, .haiku => struct {
    110         value: ?[*:0]const u8,
    111 
    112         pub const empty: Argv0 = .{ .value = null };
    113 
    114         pub fn init(args: process.Args) Argv0 {
    115             return .{ .value = args.vector[0] };
    116         }
    117     },
    118     else => struct {
    119         pub const empty: Argv0 = .{};
    120 
    121         pub fn init(args: process.Args) Argv0 {
    122             _ = args;
    123             return .{};
    124         }
    125     },
    126 };
    127 
    128 pub const Environ = struct {
    129     /// Unmodified data directly from the OS.
    130     process_environ: process.Environ,
    131     /// Protected by `mutex`. Memoized based on `process_environ`. Tracks whether the
    132     /// environment variables are present, ignoring their value.
    133     exist: Exist = .{},
    134     /// Protected by `mutex`. Memoized based on `process_environ`.
    135     string: String = .{},
    136     /// ZIG_PROGRESS
    137     zig_progress_file: std.Progress.ParentFileError!File = error.EnvironmentVariableMissing,
    138     /// Protected by `mutex`. Tracks the problem, if any, that occurred when
    139     /// trying to scan environment variables.
    140     ///
    141     /// Errors are only possible on WASI.
    142     err: ?Error = null,
    143 
    144     pub const empty: Environ = .{ .process_environ = .empty };
    145 
    146     pub const Error = Allocator.Error || Io.UnexpectedError;
    147 
    148     pub const Exist = struct {
    149         NO_COLOR: bool = false,
    150         CLICOLOR_FORCE: bool = false,
    151     };
    152 
    153     pub const String = switch (native_os) {
    154         .windows, .wasi => struct {},
    155         else => struct {
    156             PATH: ?[:0]const u8 = null,
    157             DEBUGINFOD_CACHE_PATH: ?[:0]const u8 = null,
    158             XDG_CACHE_HOME: ?[:0]const u8 = null,
    159             HOME: ?[:0]const u8 = null,
    160         },
    161     };
    162 
    163     pub fn scan(environ: *Environ, allocator: Allocator) void {
    164         if (is_windows) {
    165             // This value expires with any call that modifies the environment,
    166             // which is outside of this Io implementation's control, so references
    167             // must be short-lived.
    168             const peb = windows.peb();
    169             assert(windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS);
    170             defer assert(windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS);
    171             const ptr = peb.ProcessParameters.Environment;
    172 
    173             var i: usize = 0;
    174             while (ptr[i] != 0) {
    175                 // There are some special environment variables that start with =,
    176                 // so we need a special case to not treat = as a key/value separator
    177                 // if it's the first character.
    178                 // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
    179                 const key_start = i;
    180                 if (ptr[i] == '=') i += 1;
    181                 while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
    182                 const key_w = ptr[key_start..i];
    183 
    184                 const value_start = i + 1;
    185                 while (ptr[i] != 0) : (i += 1) {} // skip over '=' and value
    186                 const value_w = ptr[value_start..i];
    187                 i += 1; // skip over null byte
    188 
    189                 if (windows.eqlIgnoreCaseWtf16(key_w, &.{ 'N', 'O', '_', 'C', 'O', 'L', 'O', 'R' })) {
    190                     environ.exist.NO_COLOR = true;
    191                 } else if (windows.eqlIgnoreCaseWtf16(key_w, &.{ 'C', 'L', 'I', 'C', 'O', 'L', 'O', 'R', '_', 'F', 'O', 'R', 'C', 'E' })) {
    192                     environ.exist.CLICOLOR_FORCE = true;
    193                 } else if (windows.eqlIgnoreCaseWtf16(key_w, &.{ 'Z', 'I', 'G', '_', 'P', 'R', 'O', 'G', 'R', 'E', 'S', 'S' })) {
    194                     environ.zig_progress_file = file: {
    195                         var value_buf: [std.fmt.count("{d}", .{std.math.maxInt(usize)})]u8 = undefined;
    196                         const len = std.unicode.calcWtf8Len(value_w);
    197                         if (len > value_buf.len) break :file error.UnrecognizedFormat;
    198                         assert(std.unicode.wtf16LeToWtf8(&value_buf, value_w) == len);
    199                         break :file .{
    200                             .handle = @ptrFromInt(std.fmt.parseInt(usize, value_buf[0..len], 10) catch
    201                                 break :file error.UnrecognizedFormat),
    202                             .flags = .{ .nonblocking = true },
    203                         };
    204                     };
    205                 }
    206                 comptime assert(@sizeOf(String) == 0);
    207             }
    208         } else if (native_os == .wasi and !builtin.link_libc) {
    209             var environ_size: usize = undefined;
    210             var environ_buf_size: usize = undefined;
    211 
    212             switch (std.os.wasi.environ_sizes_get(&environ_size, &environ_buf_size)) {
    213                 .SUCCESS => {},
    214                 else => |err| {
    215                     environ.err = posix.unexpectedErrno(err);
    216                     return;
    217                 },
    218             }
    219             if (environ_size == 0) return;
    220 
    221             const wasi_environ = allocator.alloc([*:0]u8, environ_size) catch |err| {
    222                 environ.err = err;
    223                 return;
    224             };
    225             defer allocator.free(wasi_environ);
    226             const wasi_environ_buf = allocator.alloc(u8, environ_buf_size) catch |err| {
    227                 environ.err = err;
    228                 return;
    229             };
    230             defer allocator.free(wasi_environ_buf);
    231 
    232             switch (std.os.wasi.environ_get(wasi_environ.ptr, wasi_environ_buf.ptr)) {
    233                 .SUCCESS => {},
    234                 else => |err| {
    235                     environ.err = posix.unexpectedErrno(err);
    236                     return;
    237                 },
    238             }
    239 
    240             for (wasi_environ) |env| {
    241                 const pair = std.mem.sliceTo(env, 0);
    242                 var parts = std.mem.splitScalar(u8, pair, '=');
    243                 const key = parts.first();
    244                 if (std.mem.eql(u8, key, "NO_COLOR")) {
    245                     environ.exist.NO_COLOR = true;
    246                 } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) {
    247                     environ.exist.CLICOLOR_FORCE = true;
    248                 }
    249                 comptime assert(@sizeOf(String) == 0);
    250             }
    251         } else {
    252             for (environ.process_environ.block.slice) |opt_entry| {
    253                 const entry = opt_entry.?;
    254                 var entry_i: usize = 0;
    255                 while (entry[entry_i] != 0 and entry[entry_i] != '=') : (entry_i += 1) {}
    256                 const key = entry[0..entry_i];
    257 
    258                 var end_i: usize = entry_i;
    259                 while (entry[end_i] != 0) : (end_i += 1) {}
    260                 const value = entry[entry_i + 1 .. end_i :0];
    261 
    262                 if (std.mem.eql(u8, key, "NO_COLOR")) {
    263                     environ.exist.NO_COLOR = true;
    264                 } else if (std.mem.eql(u8, key, "CLICOLOR_FORCE")) {
    265                     environ.exist.CLICOLOR_FORCE = true;
    266                 } else if (std.mem.eql(u8, key, "ZIG_PROGRESS")) {
    267                     environ.zig_progress_file = file: {
    268                         break :file .{
    269                             .handle = std.fmt.parseInt(u31, value, 10) catch
    270                                 break :file error.UnrecognizedFormat,
    271                             .flags = .{ .nonblocking = true },
    272                         };
    273                     };
    274                 } else inline for (@typeInfo(String).@"struct".fields) |field| {
    275                     if (std.mem.eql(u8, key, field.name)) @field(environ.string, field.name) = value;
    276                 }
    277             }
    278         }
    279     }
    280 };
    281 
    282 pub const NullFile = switch (native_os) {
    283     .windows => struct {
    284         handle: ?windows.HANDLE = null,
    285 
    286         fn deinit(this: *@This()) void {
    287             if (this.handle) |handle| {
    288                 windows.CloseHandle(handle);
    289                 this.handle = null;
    290             }
    291         }
    292     },
    293     .wasi, .ios, .tvos, .visionos, .watchos => struct {
    294         fn deinit(this: @This()) void {
    295             _ = this;
    296         }
    297     },
    298     else => struct {
    299         fd: posix.fd_t = -1,
    300 
    301         fn deinit(this: *@This()) void {
    302             if (this.fd >= 0) {
    303                 closeFd(this.fd);
    304                 this.fd = -1;
    305             }
    306         }
    307     },
    308 };
    309 
    310 pub const RandomFile = switch (native_os) {
    311     .windows => NullFile,
    312     else => if (use_dev_urandom) NullFile else struct {
    313         fn deinit(this: @This()) void {
    314             _ = this;
    315         }
    316     },
    317 };
    318 
    319 pub const PipeFile = switch (native_os) {
    320     .windows => struct {
    321         handle: ?windows.HANDLE = null,
    322 
    323         fn deinit(this: *@This()) void {
    324             if (this.handle) |handle| {
    325                 windows.CloseHandle(handle);
    326                 this.handle = null;
    327             }
    328         }
    329     },
    330     else => struct {
    331         fn deinit(this: @This()) void {
    332             _ = this;
    333         }
    334     },
    335 };
    336 
    337 pub const Pid = if (native_os == .linux) enum(posix.pid_t) {
    338     unknown = 0,
    339     _,
    340 } else enum(u0) { unknown = 0 };
    341 
    342 pub const UseSendfile = if (have_sendfile) enum {
    343     enabled,
    344     disabled,
    345     pub const default: UseSendfile = .enabled;
    346 } else enum {
    347     disabled,
    348     pub const default: UseSendfile = .disabled;
    349 };
    350 
    351 pub const UseCopyFileRange = if (have_copy_file_range) enum {
    352     enabled,
    353     disabled,
    354     pub const default: UseCopyFileRange = .enabled;
    355 } else enum {
    356     disabled,
    357     pub const default: UseCopyFileRange = .disabled;
    358 };
    359 
    360 pub const UseFcopyfile = if (have_fcopyfile) enum {
    361     enabled,
    362     disabled,
    363     pub const default: UseFcopyfile = .enabled;
    364 } else enum {
    365     disabled,
    366     pub const default: UseFcopyfile = .disabled;
    367 };
    368 
    369 pub const UseFchmodat2 = if (have_fchmodat2 and !have_fchmodat_flags) enum {
    370     enabled,
    371     disabled,
    372     pub const default: UseFchmodat2 = .enabled;
    373 } else enum {
    374     disabled,
    375     pub const default: UseFchmodat2 = .disabled;
    376 };
    377 
    378 const Runnable = struct {
    379     node: std.SinglyLinkedList.Node,
    380     startFn: *const fn (*Runnable, *Thread, *Threaded) void,
    381 };
    382 
    383 const Group = struct {
    384     ptr: *Io.Group,
    385 
    386     /// Returns a correctly-typed pointer to the `Io.Group.token` field.
    387     ///
    388     /// The status indicates how many pending tasks are in the group, whether the group has been
    389     /// canceled, and whether the group has been awaited.
    390     ///
    391     /// Note that the zero value of `Status` intentionally represents the initial group state (empty
    392     /// with no awaiters). This is a requirement of `Io.Group`.
    393     fn status(g: Group) *std.atomic.Value(Status) {
    394         return @ptrCast(&g.ptr.token);
    395     }
    396     /// Returns a correctly-typed pointer to the `Io.Group.state` field. The double-pointer here is
    397     /// intentional, because the `state` field itself stores a pointer, and this function returns a
    398     /// pointer to that field.
    399     ///
    400     /// On completion of the whole group, if `status` indicates that there is an awaiter, the last
    401     /// task must increment this `u32` and do a futex wake on it to signal that awaiter.
    402     fn awaiter(g: Group) **std.atomic.Value(u32) {
    403         return @ptrCast(&g.ptr.state);
    404     }
    405 
    406     const Status = packed struct(usize) {
    407         num_running: @Int(.unsigned, @bitSizeOf(usize) - 2),
    408         have_awaiter: bool,
    409         canceled: bool,
    410     };
    411 
    412     const Task = struct {
    413         runnable: Runnable,
    414         group: *Io.Group,
    415         func: *const fn (context: *const anyopaque) void,
    416         context_alignment: Alignment,
    417         alloc_len: usize,
    418 
    419         /// `Task.runnable.node` is `undefined` in the created `Task`.
    420         fn create(
    421             gpa: Allocator,
    422             group: Group,
    423             context: []const u8,
    424             context_alignment: Alignment,
    425             func: *const fn (context: *const anyopaque) void,
    426         ) Allocator.Error!*Task {
    427             const max_context_misalignment = context_alignment.toByteUnits() -| @alignOf(Task);
    428             const worst_case_context_offset = context_alignment.forward(@sizeOf(Task) + max_context_misalignment);
    429             const alloc_len = worst_case_context_offset + context.len;
    430 
    431             const task: *Task = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(Task), alloc_len)));
    432             errdefer comptime unreachable;
    433 
    434             task.* = .{
    435                 .runnable = .{
    436                     .node = undefined,
    437                     .startFn = &start,
    438                 },
    439                 .group = group.ptr,
    440                 .func = func,
    441                 .context_alignment = context_alignment,
    442                 .alloc_len = alloc_len,
    443             };
    444             @memcpy(task.contextPointer()[0..context.len], context);
    445             return task;
    446         }
    447 
    448         fn destroy(task: *Task, gpa: Allocator) void {
    449             const base: [*]align(@alignOf(Task)) u8 = @ptrCast(task);
    450             gpa.free(base[0..task.alloc_len]);
    451         }
    452 
    453         fn contextPointer(task: *Task) [*]u8 {
    454             const base: [*]u8 = @ptrCast(task);
    455             const offset = task.context_alignment.forward(@intFromPtr(base) + @sizeOf(Task)) - @intFromPtr(base);
    456             return base + offset;
    457         }
    458 
    459         fn start(r: *Runnable, thread: *Thread, t: *Threaded) void {
    460             const task: *Task = @fieldParentPtr("runnable", r);
    461             const group: Group = .{ .ptr = task.group };
    462 
    463             // This would be a simple store, but it's upgraded to an RMW so we can use `.acquire` to
    464             // enforce the ordering between this and the `group.status().load` below. Paired with
    465             // the `.release` rmw on `Thread.status` in `cancelThreads`, this creates a StoreLoad
    466             // barrier which guarantees that when a group is canceled, either we see the cancelation
    467             // in the group status, or the canceler sees our thread status so can directly notify us
    468             // of the cancelation.
    469             _ = thread.status.swap(.{
    470                 .cancelation = .none,
    471                 .awaitable = .fromGroup(group.ptr),
    472             }, .acquire);
    473             if (group.status().load(.monotonic).canceled) {
    474                 thread.status.store(.{
    475                     .cancelation = .canceling,
    476                     .awaitable = .fromGroup(group.ptr),
    477                 }, .monotonic);
    478             }
    479 
    480             task.func(task.contextPointer());
    481 
    482             thread.status.store(.{ .cancelation = .none, .awaitable = .null }, .monotonic);
    483             const old_status = group.status().fetchSub(.{
    484                 .num_running = 1,
    485                 .have_awaiter = false,
    486                 .canceled = false,
    487             }, .acq_rel); // acquire `group.awaiter()`, release task results
    488             assert(old_status.num_running > 0);
    489             if (old_status.have_awaiter and old_status.num_running == 1) {
    490                 const to_signal = group.awaiter().*;
    491                 // `awaiter` should only be modified by us. For another thread to see `num_running`
    492                 // drop to 0 after this point would indicate that another task started up, meaning
    493                 // `async`/`cancel` was racing with awaited group completion.
    494                 group.awaiter().* = undefined;
    495                 _ = to_signal.fetchAdd(1, .release); // release results
    496                 Thread.futexWake(&to_signal.raw, 1);
    497             }
    498 
    499             // Task completed. Self-destruct sequence initiated.
    500             task.destroy(t.allocator);
    501         }
    502     };
    503 
    504     /// Assumes the caller has already atomically updated the group status to indicate cancelation,
    505     /// and notifies any already-running threads of this cancelation.
    506     fn cancelThreads(g: Group, t: *Threaded) bool {
    507         var any_blocked = false;
    508         var it = t.worker_threads.load(.acquire); // acquire `Thread` values
    509         while (it) |thread| : (it = thread.next) {
    510             // This non-mutating RMW exists for ordering reasons: see comment in `Group.Task.start` for reasons.
    511             _ = thread.status.fetchOr(.{ .cancelation = @enumFromInt(0), .awaitable = .null }, .release);
    512             if (thread.cancelAwaitable(.fromGroup(g.ptr))) any_blocked = true;
    513         }
    514         return any_blocked;
    515     }
    516 
    517     /// Uses `Thread.signalCanceledSyscall` to signal any threads which are still blocked in a
    518     /// syscall for this group and have not observed a cancelation request yet. Returns `true` if
    519     /// more signals may be necessary, in which case the caller must call this again after a delay.
    520     fn signalAllCanceledSyscalls(g: Group, t: *Threaded) bool {
    521         var any_signaled = false;
    522         var it = t.worker_threads.load(.acquire); // acquire `Thread` values
    523         while (it) |thread| : (it = thread.next) {
    524             if (thread.signalCanceledSyscall(t, .fromGroup(g.ptr))) any_signaled = true;
    525         }
    526         return any_signaled;
    527     }
    528 
    529     /// The caller has canceled `g`. Inform any threads working on that group of the cancelation if
    530     /// necessary, and wait for `g` to finish (indicated by `num_completed` being incremented from 0
    531     /// to 1), while sending regular signals to threads if necessary for them to unblock from any
    532     /// cancelable syscalls.
    533     ///
    534     /// `skip_signals` means it is already known that no threads are currently working on the group
    535     /// so no notifications or signals are necessary.
    536     fn waitForCancelWithSignaling(
    537         g: Group,
    538         t: *Threaded,
    539         num_completed: *std.atomic.Value(u32),
    540         skip_signals: bool,
    541     ) void {
    542         var need_signal: bool = !skip_signals and g.cancelThreads(t);
    543         var timeout_ns: u64 = 1 << 10;
    544         while (true) {
    545             need_signal = need_signal and g.signalAllCanceledSyscalls(t);
    546             Thread.futexWaitUncancelable(&num_completed.raw, 0, if (need_signal) timeout_ns else null);
    547             switch (num_completed.load(.acquire)) { // acquire task results
    548                 0 => {},
    549                 1 => break,
    550                 else => unreachable,
    551             }
    552             timeout_ns <<|= 1;
    553         }
    554     }
    555 };
    556 
    557 /// Trailing data:
    558 /// 1. context
    559 /// 2. result
    560 const Future = struct {
    561     runnable: Runnable,
    562     func: *const fn (context: *const anyopaque, result: *anyopaque) void,
    563     status: std.atomic.Value(Status),
    564     /// On completion, increment this `u32` and do a futex wake on it.
    565     awaiter: *std.atomic.Value(u32),
    566     context_alignment: Alignment,
    567     result_offset: usize,
    568     alloc_len: usize,
    569 
    570     const Status = packed struct(usize) {
    571         /// The values of this enum are chosen so that await/cancel can just OR with 0b01 and 0b11
    572         /// respectively. That *does* clobber `.done`, but that's actually fine, because if the tag
    573         /// is `.done` then only the awaiter is referencing this `Future` anyway.
    574         tag: enum(u2) {
    575             /// The future is queued or running (depending on whether `thread` is set).
    576             pending = 0b00,
    577             /// Like `pending`, but the future is being awaited. `Future.awaiter` is populated.
    578             pending_awaited = 0b01,
    579             /// Like `pending`, but the future is being canceled. `Future.awaiter` is populated.
    580             pending_canceled = 0b11,
    581             /// The future has already completed. `thread` is `.null`, unless the future terminated
    582             /// with an acknowledged cancel request, in which case `thread` is `.all_ones`.
    583             done = 0b10,
    584         },
    585         /// When the future begins execution, this is atomically updated from `null` to the thread running the
    586         /// `Future`, so that cancelation knows which thread to cancel.
    587         thread: Thread.PackedPtr,
    588     };
    589 
    590     /// `Future.runnable.node` is `undefined` in the created `Future`.
    591     fn create(
    592         gpa: Allocator,
    593         result_len: usize,
    594         result_alignment: Alignment,
    595         context: []const u8,
    596         context_alignment: Alignment,
    597         func: *const fn (context: *const anyopaque, result: *anyopaque) void,
    598     ) Allocator.Error!*Future {
    599         const max_context_misalignment = context_alignment.toByteUnits() -| @alignOf(Future);
    600         const worst_case_context_offset = context_alignment.forward(@sizeOf(Future) + max_context_misalignment);
    601         const worst_case_result_offset = result_alignment.forward(worst_case_context_offset + context.len);
    602         const alloc_len = worst_case_result_offset + result_len;
    603 
    604         const future: *Future = @ptrCast(@alignCast(try gpa.alignedAlloc(u8, .of(Future), alloc_len)));
    605         errdefer comptime unreachable;
    606 
    607         const actual_context_addr = context_alignment.forward(@intFromPtr(future) + @sizeOf(Future));
    608         const actual_result_addr = result_alignment.forward(actual_context_addr + context.len);
    609         const actual_result_offset = actual_result_addr - @intFromPtr(future);
    610         future.* = .{
    611             .runnable = .{
    612                 .node = undefined,
    613                 .startFn = &start,
    614             },
    615             .func = func,
    616             .status = .init(.{
    617                 .tag = .pending,
    618                 .thread = .null,
    619             }),
    620             .awaiter = undefined,
    621             .context_alignment = context_alignment,
    622             .result_offset = actual_result_offset,
    623             .alloc_len = alloc_len,
    624         };
    625         @memcpy(future.contextPointer()[0..context.len], context);
    626         return future;
    627     }
    628 
    629     fn destroy(future: *Future, gpa: Allocator) void {
    630         const base: [*]align(@alignOf(Future)) u8 = @ptrCast(future);
    631         gpa.free(base[0..future.alloc_len]);
    632     }
    633 
    634     fn resultPointer(future: *Future) [*]u8 {
    635         const base: [*]u8 = @ptrCast(future);
    636         return base + future.result_offset;
    637     }
    638 
    639     fn contextPointer(future: *Future) [*]u8 {
    640         const base: [*]u8 = @ptrCast(future);
    641         const context_offset = future.context_alignment.forward(@intFromPtr(future) + @sizeOf(Future)) - @intFromPtr(future);
    642         return base + context_offset;
    643     }
    644 
    645     fn start(r: *Runnable, thread: *Thread, t: *Threaded) void {
    646         _ = t;
    647         const future: *Future = @fieldParentPtr("runnable", r);
    648 
    649         thread.status.store(.{
    650             .cancelation = .none,
    651             .awaitable = .fromFuture(future),
    652         }, .monotonic);
    653         {
    654             const old_status = future.status.fetchOr(.{
    655                 .tag = .pending,
    656                 .thread = .pack(thread),
    657             }, .release);
    658             assert(old_status.thread == .null);
    659             switch (old_status.tag) {
    660                 .pending, .pending_awaited => {},
    661                 .pending_canceled => thread.status.store(.{
    662                     .cancelation = .canceling,
    663                     .awaitable = .fromFuture(future),
    664                 }, .monotonic),
    665                 .done => unreachable,
    666             }
    667         }
    668 
    669         future.func(future.contextPointer(), future.resultPointer());
    670 
    671         const had_acknowledged_cancel = switch (thread.status.load(.monotonic).cancelation) {
    672             .none, .canceling => false,
    673             .canceled => true,
    674             .parked => unreachable,
    675             .blocked => unreachable,
    676             .blocked_alertable => unreachable,
    677             .blocked_alertable_canceling => unreachable,
    678             .blocked_canceling => unreachable,
    679         };
    680         thread.status.store(.{ .cancelation = .none, .awaitable = .null }, .monotonic);
    681         const old_status = future.status.swap(.{
    682             .tag = .done,
    683             .thread = if (had_acknowledged_cancel) .all_ones else .null,
    684         }, .acq_rel); // acquire `future.awaiter`, release results
    685         switch (old_status.tag) {
    686             .pending => {},
    687             .pending_awaited, .pending_canceled => {
    688                 const to_signal = future.awaiter;
    689                 _ = to_signal.fetchAdd(1, .release); // release results
    690                 Thread.futexWake(&to_signal.raw, 1);
    691             },
    692             .done => unreachable,
    693         }
    694     }
    695 
    696     /// The caller has canceled `future`. `thread` is the thread currently running that future.
    697     /// Inform `thread` of the cancelation if necessary, and wait for `future` to finish (indicated
    698     /// by `num_completed` being incremented from 0 to 1), while sending regular signals to `thread`
    699     /// if necessary for it to unblock from a cancelable syscall.
    700     fn waitForCancelWithSignaling(
    701         future: *Future,
    702         t: *Threaded,
    703         num_completed: *std.atomic.Value(u32),
    704         thread: ?*Thread,
    705     ) void {
    706         var need_signal: bool = if (thread) |th| th.cancelAwaitable(.fromFuture(future)) else false;
    707         var timeout_ns: u64 = 1 << 10;
    708         while (true) {
    709             need_signal = need_signal and thread.?.signalCanceledSyscall(t, .fromFuture(future));
    710             Thread.futexWaitUncancelable(&num_completed.raw, 0, if (need_signal) timeout_ns else null);
    711             switch (num_completed.load(.acquire)) { // acquire task results
    712                 0 => {},
    713                 1 => break,
    714                 else => unreachable,
    715             }
    716             timeout_ns <<|= 1;
    717         }
    718     }
    719 };
    720 
    721 /// A sequence of (ptr_bit_width - 3) bits which uniquely identifies a group or future. The bits are
    722 /// the MSBs of the `*Io.Group` or `*Future`. These things do not necessarily have 3 zero bits at
    723 /// the end (they are pointer-aligned, so on 32-bit targets only have 2), but because they both have
    724 /// a *size* of at least 8 bytes, no two groups/futures in memory at the same time will have the
    725 /// same value for all of these bits. In other words, given a group/future pointer, the next group
    726 /// or future must be at least 8 bytes later, so its address will have a different value for one of
    727 /// the top (ptr_bit_width - 3) bits.
    728 const AwaitableId = enum(@Int(.unsigned, @bitSizeOf(usize) - 3)) {
    729     comptime {
    730         assert(@sizeOf(Future) >= 8);
    731         assert(@sizeOf(Io.Group) >= 8);
    732     }
    733     null = 0,
    734     all_ones = std.math.maxInt(@Int(.unsigned, @bitSizeOf(usize) - 3)),
    735     _,
    736     const Split = packed struct(usize) { low: u3, high: AwaitableId };
    737     fn fromGroup(g: *Io.Group) AwaitableId {
    738         const split: Split = @bitCast(@intFromPtr(g));
    739         return split.high;
    740     }
    741     fn fromFuture(f: *Future) AwaitableId {
    742         const split: Split = @bitCast(@intFromPtr(f));
    743         return split.high;
    744     }
    745 };
    746 
    747 const Thread = struct {
    748     next: ?*Thread,
    749 
    750     id: std.Thread.Id,
    751     handle: Handle,
    752 
    753     status: std.atomic.Value(Status),
    754 
    755     cancel_protection: Io.CancelProtection,
    756     /// Always released when `Status.cancelation` is set to `.parked`.
    757     futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,
    758     unpark_flag: UnparkFlag,
    759 
    760     csprng: Csprng,
    761 
    762     const Handle = Handle: {
    763         if (std.Thread.use_pthreads) break :Handle std.c.pthread_t;
    764         if (is_windows) break :Handle windows.HANDLE;
    765         break :Handle void;
    766     };
    767 
    768     const Status = packed struct(usize) {
    769         /// The specific values of these enum fields are chosen to simplify the implementation of
    770         /// the transformations we need to apply to this state.
    771         cancelation: enum(u3) {
    772             /// The thread has not yet been canceled, and is not in a cancelable operation.
    773             /// To request cancelation, just set the status to `.canceling`.
    774             none = 0b000,
    775 
    776             /// The thread is parked in a cancelable futex wait or sleep.
    777             /// Only applicable if `use_parking_futex` or `use_parking_sleep`.
    778             /// To request cancelation, set the status to `.canceling` and unpark the thread.
    779             /// To unpark for another reason (futex wake), set the status to `.none` and unpark the thread.
    780             parked = 0b001,
    781 
    782             /// The thread is blocked in a cancelable system call.
    783             /// To request cancelation, set the status to `.blocked_canceling` and repeatedly interrupt the system call until the status changes.
    784             blocked = 0b011,
    785 
    786             /// Windows-only: the thread is blocked in an alertable wait via
    787             /// `NtDelayExecution`. To request cancelation, set the status to
    788             /// `blocked_alertable_canceling` and repeatedly alert the thread
    789             /// until the status changes.
    790             blocked_alertable = 0b010,
    791 
    792             /// The thread has an outstanding cancelation request but is not in a cancelable operation.
    793             /// When it acknowledges the cancelation, it will set the status to `.canceled`.
    794             canceling = 0b110,
    795 
    796             /// The thread has received and acknowledged a cancelation request.
    797             /// If `recancel` is called, the status will revert to `.canceling`, but otherwise, the status
    798             /// will not change for the remainder of this task's execution.
    799             canceled = 0b111,
    800 
    801             /// The thread is blocked in a cancelable system call, and is being
    802             /// canceled. The thread which triggered the cancelation will send
    803             /// signals to this thread until its status changes.
    804             blocked_canceling = 0b101,
    805 
    806             /// Windows-only: the thread is blocked in an alertable wait via
    807             /// `NtDelayExecution`, and is being canceled. The thread which
    808             /// triggered the cancelation will send signals to this thread
    809             /// until its status changes.
    810             blocked_alertable_canceling = 0b100,
    811         },
    812 
    813         /// We cannot turn this value back into a pointer. Instead, it exists so that a task can be
    814         /// canceled by a cmpxchg on thread status: if it is running the task we want to cancel,
    815         /// then update the `cancelation` field.
    816         awaitable: AwaitableId,
    817     };
    818 
    819     const SignaleeId = if (std.Thread.use_pthreads) std.c.pthread_t else std.Thread.Id;
    820 
    821     threadlocal var current: ?*Thread = null;
    822 
    823     /// A value that does not alias any other thread id.
    824     const invalid_id: std.Thread.Id = std.math.maxInt(std.Thread.Id);
    825 
    826     fn currentId() std.Thread.Id {
    827         return if (current) |t| t.id else std.Thread.getCurrentId();
    828     }
    829 
    830     /// The thread is neither in a syscall nor entering one, but we want to check for cancelation
    831     /// anyway. If there is a pending cancel request, acknowledge it and return `error.Canceled`.
    832     fn checkCancel() Io.Cancelable!void {
    833         const thread = Thread.current orelse return;
    834         switch (thread.cancel_protection) {
    835             .blocked => return,
    836             .unblocked => {},
    837         }
    838         // Here, unlike `Syscall.checkCancel`, it's not particularly likely that we're canceled, so
    839         // it seems preferable to do a cheap atomic load and, in the unlikely case, a separate store
    840         // to acknowledge. Besides, the state transitions we need here can't be done with one atomic
    841         // OR/AND/XOR on `Status.cancelation`, so we don't actually have any other option.
    842         const status = thread.status.load(.monotonic);
    843         switch (status.cancelation) {
    844             .parked => unreachable,
    845             .blocked => unreachable,
    846             .blocked_alertable => unreachable,
    847             .blocked_alertable_canceling => unreachable,
    848             .blocked_canceling => unreachable,
    849             .none, .canceled => {},
    850             .canceling => {
    851                 thread.status.store(.{
    852                     .cancelation = .canceled,
    853                     .awaitable = status.awaitable,
    854                 }, .monotonic);
    855                 return error.Canceled;
    856             },
    857         }
    858     }
    859 
    860     fn futexWaitUncancelable(ptr: *const u32, expect: u32, timeout_ns: ?u64) void {
    861         return Thread.futexWaitInner(ptr, expect, true, timeout_ns) catch unreachable;
    862     }
    863 
    864     fn futexWait(ptr: *const u32, expect: u32, timeout_ns: ?u64) Io.Cancelable!void {
    865         return Thread.futexWaitInner(ptr, expect, false, timeout_ns);
    866     }
    867 
    868     fn futexWaitInner(ptr: *const u32, expect: u32, uncancelable: bool, timeout_ns: ?u64) Io.Cancelable!void {
    869         @branchHint(.cold);
    870 
    871         if (builtin.single_threaded) unreachable; // nobody would ever wake us
    872 
    873         if (use_parking_futex) {
    874             return parking_futex.wait(
    875                 ptr,
    876                 expect,
    877                 uncancelable,
    878                 if (timeout_ns) |ns| .{ .duration = .{
    879                     .raw = .fromNanoseconds(ns),
    880                     .clock = .boot,
    881                 } } else .none,
    882             );
    883         } else if (builtin.cpu.arch.isWasm()) {
    884             comptime assert(builtin.cpu.has(.wasm, .atomics));
    885             // TODO implement cancelation for WASM futex waits by signaling the futex
    886             if (!uncancelable) try Thread.checkCancel();
    887             const to: i64 = if (timeout_ns) |ns| ns else -1;
    888             const signed_expect: i32 = @bitCast(expect);
    889             const result = asm volatile (
    890                 \\local.get %[ptr]
    891                 \\local.get %[expected]
    892                 \\local.get %[timeout]
    893                 \\memory.atomic.wait32 0
    894                 \\local.set %[ret]
    895                 : [ret] "=r" (-> u32),
    896                 : [ptr] "r" (ptr),
    897                   [expected] "r" (signed_expect),
    898                   [timeout] "r" (to),
    899             );
    900             switch (result) {
    901                 0 => {}, // ok
    902                 1 => {}, // expected != loaded
    903                 2 => {}, // timeout
    904                 else => assert(!is_debug),
    905             }
    906         } else switch (native_os) {
    907             .linux => {
    908                 const linux = std.os.linux;
    909                 var ts_buffer: linux.timespec = undefined;
    910                 const ts: ?*linux.timespec = if (timeout_ns) |ns| ts: {
    911                     ts_buffer = timestampToPosix(ns);
    912                     break :ts &ts_buffer;
    913                 } else null;
    914                 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
    915                 const rc = linux.futex_4arg(ptr, .{ .cmd = .WAIT, .private = true }, expect, ts);
    916                 syscall.finish();
    917                 switch (linux.errno(rc)) {
    918                     .SUCCESS => {}, // notified by `wake()`
    919                     .INTR => {}, // caller's responsibility to retry
    920                     .AGAIN => {}, // ptr.* != expect
    921                     .INVAL => {}, // possibly timeout overflow
    922                     .TIMEDOUT => {},
    923                     .FAULT => recoverableOsBugDetected(), // ptr was invalid
    924                     else => recoverableOsBugDetected(),
    925                 }
    926             },
    927             .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
    928                 const c = std.c;
    929                 const flags: c.UL = .{
    930                     .op = .COMPARE_AND_WAIT,
    931                     .NO_ERRNO = true,
    932                 };
    933                 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
    934                 const status = switch (darwin_supports_ulock_wait2) {
    935                     true => c.__ulock_wait2(flags, ptr, expect, ns: {
    936                         const ns = timeout_ns orelse break :ns 0;
    937                         if (ns == 0) break :ns 1;
    938                         break :ns ns;
    939                     }, 0),
    940                     false => c.__ulock_wait(flags, ptr, expect, us: {
    941                         const ns = timeout_ns orelse break :us 0;
    942                         const us = std.math.lossyCast(u32, ns / std.time.ns_per_us);
    943                         if (us == 0) break :us 1;
    944                         break :us us;
    945                     }),
    946                 };
    947                 syscall.finish();
    948                 if (status >= 0) return;
    949                 switch (@as(c.E, @enumFromInt(-status))) {
    950                     .INTR => {}, // spurious wake
    951                     // Address of the futex was paged out. This is unlikely, but possible in theory, and
    952                     // pthread/libdispatch on darwin bother to handle it. In this case we'll return
    953                     // without waiting, but the caller should retry anyway.
    954                     .FAULT => {},
    955                     .TIMEDOUT => {}, // timeout
    956                     else => recoverableOsBugDetected(),
    957                 }
    958             },
    959             .freebsd => {
    960                 const flags = @intFromEnum(std.c.UMTX_OP.WAIT_UINT_PRIVATE);
    961                 var tm_size: usize = 0;
    962                 var tm: std.c._umtx_time = undefined;
    963                 var tm_ptr: ?*const std.c._umtx_time = null;
    964                 if (timeout_ns) |ns| {
    965                     tm_ptr = &tm;
    966                     tm_size = @sizeOf(@TypeOf(tm));
    967                     tm.flags = 0; // use relative time not UMTX_ABSTIME
    968                     tm.clockid = .MONOTONIC;
    969                     tm.timeout = timestampToPosix(ns);
    970                 }
    971                 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
    972                 const rc = std.c._umtx_op(@intFromPtr(ptr), flags, @as(c_ulong, expect), tm_size, @intFromPtr(tm_ptr));
    973                 syscall.finish();
    974                 if (is_debug) switch (posix.errno(rc)) {
    975                     .SUCCESS => {},
    976                     .FAULT => unreachable, // one of the args points to invalid memory
    977                     .INVAL => unreachable, // arguments should be correct
    978                     .TIMEDOUT => {}, // timeout
    979                     .INTR => {}, // spurious wake
    980                     else => unreachable,
    981                 };
    982             },
    983             .openbsd => {
    984                 var tm: std.c.timespec = undefined;
    985                 var tm_ptr: ?*const std.c.timespec = null;
    986                 if (timeout_ns) |ns| {
    987                     tm_ptr = &tm;
    988                     tm = timestampToPosix(ns);
    989                 }
    990                 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
    991                 const rc = std.c.futex(
    992                     ptr,
    993                     std.c.FUTEX.WAIT | std.c.FUTEX.PRIVATE_FLAG,
    994                     @as(c_int, @bitCast(expect)),
    995                     tm_ptr,
    996                     null, // uaddr2 is ignored
    997                 );
    998                 syscall.finish();
    999                 if (is_debug) switch (posix.errno(rc)) {
   1000                     .SUCCESS => {},
   1001                     .NOSYS => unreachable, // constant op known good value
   1002                     .AGAIN => {}, // contents of uaddr != val
   1003                     .INVAL => unreachable, // invalid timeout
   1004                     .TIMEDOUT => {}, // timeout
   1005                     .INTR => {}, // a signal arrived
   1006                     .CANCELED => {}, // a signal arrived and SA_RESTART was set
   1007                     else => unreachable,
   1008                 };
   1009             },
   1010             .dragonfly => {
   1011                 var timeout_us: c_int = undefined;
   1012                 if (timeout_ns) |ns| {
   1013                     timeout_us = std.math.cast(c_int, ns / std.time.ns_per_us) orelse std.math.maxInt(c_int);
   1014                 } else {
   1015                     timeout_us = 0;
   1016                 }
   1017                 const syscall: Syscall = if (uncancelable) .{ .thread = null } else try .start();
   1018                 const rc = std.c.umtx_sleep(@ptrCast(ptr), @bitCast(expect), timeout_us);
   1019                 syscall.finish();
   1020                 if (is_debug) switch (std.posix.errno(rc)) {
   1021                     .SUCCESS => {},
   1022                     .BUSY => {}, // ptr != expect
   1023                     .AGAIN => {}, // maybe timed out, or paged out, or hit 2s kernel refresh
   1024                     .INTR => {}, // spurious wake
   1025                     .INVAL => unreachable, // invalid timeout
   1026                     else => unreachable,
   1027                 };
   1028             },
   1029             else => @compileError("unimplemented: futexWait"),
   1030         }
   1031     }
   1032 
   1033     fn futexWake(ptr: *const u32, max_waiters: u32) void {
   1034         @branchHint(.cold);
   1035         assert(max_waiters != 0);
   1036 
   1037         if (builtin.single_threaded) return; // nothing to wake up
   1038 
   1039         if (use_parking_futex) {
   1040             return parking_futex.wake(ptr, max_waiters);
   1041         } else if (builtin.cpu.arch.isWasm()) {
   1042             comptime assert(builtin.cpu.has(.wasm, .atomics));
   1043             const woken_count = asm volatile (
   1044                 \\local.get %[ptr]
   1045                 \\local.get %[waiters]
   1046                 \\memory.atomic.notify 0
   1047                 \\local.set %[ret]
   1048                 : [ret] "=r" (-> u32),
   1049                 : [ptr] "r" (ptr),
   1050                   [waiters] "r" (max_waiters),
   1051             );
   1052             _ = woken_count; // can be 0 when linker flag 'shared-memory' is not enabled
   1053         } else switch (native_os) {
   1054             .linux => {
   1055                 const linux = std.os.linux;
   1056                 switch (linux.errno(linux.futex_3arg(
   1057                     ptr,
   1058                     .{ .cmd = .WAKE, .private = true },
   1059                     @min(max_waiters, std.math.maxInt(i32)),
   1060                 ))) {
   1061                     .SUCCESS => return, // successful wake up
   1062                     .INVAL => return, // invalid futex_wait() on ptr done elsewhere
   1063                     .FAULT => return, // pointer became invalid while doing the wake
   1064                     else => return recoverableOsBugDetected(), // deadlock due to operating system bug
   1065                 }
   1066             },
   1067             .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
   1068                 const c = std.c;
   1069                 const flags: c.UL = .{
   1070                     .op = .COMPARE_AND_WAIT,
   1071                     .NO_ERRNO = true,
   1072                     .WAKE_ALL = max_waiters > 1,
   1073                 };
   1074                 while (true) {
   1075                     const status = c.__ulock_wake(flags, ptr, 0);
   1076                     if (status >= 0) return;
   1077                     switch (@as(c.E, @enumFromInt(-status))) {
   1078                         .INTR, .CANCELED => continue, // spurious wake()
   1079                         .FAULT => unreachable, // __ulock_wake doesn't generate EFAULT according to darwin pthread_cond_t
   1080                         .NOENT => return, // nothing was woken up
   1081                         .ALREADY => unreachable, // only for UL.Op.WAKE_THREAD
   1082                         else => unreachable, // deadlock due to operating system bug
   1083                     }
   1084                 }
   1085             },
   1086             .freebsd => {
   1087                 const rc = std.c._umtx_op(
   1088                     @intFromPtr(ptr),
   1089                     @intFromEnum(std.c.UMTX_OP.WAKE_PRIVATE),
   1090                     @as(c_ulong, @min(max_waiters, std.math.maxInt(c_int))),
   1091                     0, // there is no timeout struct
   1092                     0, // there is no timeout struct pointer
   1093                 );
   1094                 switch (posix.errno(rc)) {
   1095                     .SUCCESS => {},
   1096                     .FAULT => {}, // it's ok if the ptr doesn't point to valid memory
   1097                     .INVAL => unreachable, // arguments should be correct
   1098                     else => unreachable, // deadlock due to operating system bug
   1099                 }
   1100             },
   1101             .openbsd => {
   1102                 const rc = std.c.futex(
   1103                     ptr,
   1104                     std.c.FUTEX.WAKE | std.c.FUTEX.PRIVATE_FLAG,
   1105                     @min(max_waiters, std.math.maxInt(c_int)),
   1106                     null, // timeout is ignored
   1107                     null, // uaddr2 is ignored
   1108                 );
   1109                 assert(rc >= 0);
   1110             },
   1111             .dragonfly => {
   1112                 // will generally return 0 unless the address is bad
   1113                 _ = std.c.umtx_wakeup(
   1114                     @ptrCast(ptr),
   1115                     @min(max_waiters, std.math.maxInt(c_int)),
   1116                 );
   1117             },
   1118             else => @compileError("unimplemented: futexWake"),
   1119         }
   1120     }
   1121 
   1122     /// Cancels `thread` if it is working on `awaitable`.
   1123     ///
   1124     /// It is possible that `thread` gets canceled by this function, but is blocked in a syscall. In
   1125     /// that case, the thread may need to be sent a signal to interrupt the call. This function will
   1126     /// return `true` to indicate this, in which case the caller must call `signalCanceledSyscall`.
   1127     fn cancelAwaitable(thread: *Thread, awaitable: AwaitableId) bool {
   1128         var status = thread.status.load(.monotonic);
   1129         while (true) {
   1130             if (status.awaitable != awaitable) return false; // thread is working on something else
   1131             status = switch (status.cancelation) {
   1132                 .none => thread.status.cmpxchgWeak(
   1133                     .{ .cancelation = .none, .awaitable = awaitable },
   1134                     .{ .cancelation = .canceling, .awaitable = awaitable },
   1135                     .monotonic,
   1136                     .monotonic,
   1137                 ) orelse return false,
   1138 
   1139                 .parked => thread.status.cmpxchgWeak(
   1140                     .{ .cancelation = .parked, .awaitable = awaitable },
   1141                     .{ .cancelation = .canceling, .awaitable = awaitable },
   1142                     .acquire, // acquire `thread.futex_waiter`
   1143                     .monotonic,
   1144                 ) orelse {
   1145                     if (!use_parking_futex and !use_parking_sleep) unreachable;
   1146                     if (thread.futex_waiter) |futex_waiter| {
   1147                         parking_futex.removeCanceledWaiter(futex_waiter);
   1148                     }
   1149                     if (need_unpark_flag) setUnparkFlag(&thread.unpark_flag);
   1150                     unpark(&.{thread.id}, null);
   1151                     return false;
   1152                 },
   1153 
   1154                 .blocked => thread.status.cmpxchgWeak(
   1155                     .{ .cancelation = .blocked, .awaitable = awaitable },
   1156                     .{ .cancelation = .blocked_canceling, .awaitable = awaitable },
   1157                     .monotonic,
   1158                     .monotonic,
   1159                 ) orelse return true,
   1160 
   1161                 .blocked_alertable => thread.status.cmpxchgWeak(
   1162                     .{ .cancelation = .blocked_alertable, .awaitable = awaitable },
   1163                     .{ .cancelation = .blocked_alertable_canceling, .awaitable = awaitable },
   1164                     .monotonic,
   1165                     .monotonic,
   1166                 ) orelse {
   1167                     if (!is_windows) unreachable;
   1168                     return true;
   1169                 },
   1170 
   1171                 .canceling, .canceled => {
   1172                     // This can happen when the task start raced with the cancelation, so the thread
   1173                     // saw the cancelation on the future/group *and* we are trying to signal the
   1174                     // thread here.
   1175                     return false;
   1176                 },
   1177 
   1178                 .blocked_canceling => unreachable, // `awaitable` has not been canceled before now
   1179                 .blocked_alertable_canceling => unreachable, // `awaitable` has not been canceled before now
   1180             };
   1181         }
   1182     }
   1183 
   1184     /// Sends a signal to `thread` if it is still blocked in a syscall (i.e. has not yet observed
   1185     /// the cancelation request from `cancelAwaitable`).
   1186     ///
   1187     /// Unfortunately, the signal could arrive before the syscall actually starts, so the interrupt
   1188     /// is missed. To handle this, we may need to send multiple signals. As such, if this function
   1189     /// returns `true`, then it should be called again after a short delay to send another signal if
   1190     /// the thread is still blocked. For the implementation, `Future.waitForCancelWithSignaling` and
   1191     /// `Group.waitForCancelWithSignaling`: they use exponential backoff starting at a 1us delay and
   1192     /// doubling each call. In practice, it is rare to send more than one signal.
   1193     fn signalCanceledSyscall(thread: *Thread, t: *Threaded, awaitable: AwaitableId) bool {
   1194         const status = thread.status.load(.monotonic);
   1195         if (status.awaitable != awaitable) {
   1196             // The thread has moved on and is working on something totally different.
   1197             return false;
   1198         }
   1199 
   1200         // The thread ID and/or handle can be read non-atomically because they never change and were
   1201         // released by the store that made `thread` available to us.
   1202 
   1203         switch (status.cancelation) {
   1204             .blocked_canceling => if (std.Thread.use_pthreads) {
   1205                 return switch (std.c.pthread_kill(thread.handle, .IO)) {
   1206                     0 => true,
   1207                     else => false,
   1208                 };
   1209             } else switch (native_os) {
   1210                 .linux => {
   1211                     const pid: posix.pid_t = pid: {
   1212                         const cached_pid = @atomicLoad(Pid, &t.pid, .monotonic);
   1213                         if (cached_pid != .unknown) break :pid @intFromEnum(cached_pid);
   1214                         const pid = std.os.linux.getpid();
   1215                         @atomicStore(Pid, &t.pid, @enumFromInt(pid), .monotonic);
   1216                         break :pid pid;
   1217                     };
   1218                     return switch (std.os.linux.tgkill(pid, @bitCast(thread.id), .IO)) {
   1219                         0 => true,
   1220                         else => false,
   1221                     };
   1222                 },
   1223                 .windows => {
   1224                     var iosb: windows.IO_STATUS_BLOCK = undefined;
   1225                     return switch (windows.ntdll.NtCancelSynchronousIoFile(thread.handle, null, &iosb)) {
   1226                         .NOT_FOUND => true, // this might mean the operation hasn't started yet
   1227                         .SUCCESS => false, // the OS confirmed that our cancelation worked
   1228                         else => false,
   1229                     };
   1230                 },
   1231                 else => return false,
   1232             },
   1233 
   1234             .blocked_alertable_canceling => {
   1235                 if (!is_windows) unreachable;
   1236                 return switch (windows.ntdll.NtAlertThread(thread.handle)) {
   1237                     .SUCCESS => true,
   1238                     else => false,
   1239                 };
   1240             },
   1241 
   1242             else => {
   1243                 // The thread is working on `awaitable`, but no longer needs signaling (they already
   1244                 // woke up and saw the cancelation).
   1245                 return false;
   1246             },
   1247         }
   1248     }
   1249 
   1250     /// Like a `*Thread`, but 2 bits smaller than a pointer (because the LSBs are always 0 due to
   1251     /// alignment) so that those two bits can be used in a `packed struct`.
   1252     const PackedPtr = enum(@Int(.unsigned, @bitSizeOf(usize) - 2)) {
   1253         null = 0,
   1254         all_ones = std.math.maxInt(@Int(.unsigned, @bitSizeOf(usize) - 2)),
   1255         _,
   1256 
   1257         const Split = packed struct(usize) { low: u2, high: PackedPtr };
   1258         fn pack(ptr: *Thread) PackedPtr {
   1259             const split: Split = @bitCast(@intFromPtr(ptr));
   1260             assert(split.low == 0);
   1261             return split.high;
   1262         }
   1263         fn unpack(ptr: PackedPtr) ?*Thread {
   1264             const split: Split = .{ .low = 0, .high = ptr };
   1265             return @ptrFromInt(@as(usize, @bitCast(split)));
   1266         }
   1267     };
   1268 
   1269     /// Same as `Io.Mutex.lock` but avoids the VTable.
   1270     fn mutexLock(m: *Io.Mutex) Io.Cancelable!void {
   1271         const initial_state = m.state.cmpxchgWeak(
   1272             .unlocked,
   1273             .locked_once,
   1274             .acquire,
   1275             .monotonic,
   1276         ) orelse {
   1277             @branchHint(.likely);
   1278             return;
   1279         };
   1280         if (initial_state == .contended) {
   1281             try Thread.futexWait(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null);
   1282         }
   1283         while (m.state.swap(.contended, .acquire) != .unlocked) {
   1284             try Thread.futexWait(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null);
   1285         }
   1286     }
   1287 };
   1288 
   1289 const Syscall = struct {
   1290     thread: ?*Thread,
   1291     /// Marks entry to a syscall region. This should be tightly scoped around the actual syscall
   1292     /// to minimize races. The syscall must be marked as "finished" by `checkCancel`, `finish`,
   1293     /// or one of the wrappers of `finish`.
   1294     fn start() Io.Cancelable!Syscall {
   1295         const thread = Thread.current orelse return .{ .thread = null };
   1296         switch (thread.cancel_protection) {
   1297             .blocked => return .{ .thread = null },
   1298             .unblocked => {},
   1299         }
   1300         switch (thread.status.fetchOr(.{
   1301             .cancelation = @enumFromInt(0b011),
   1302             .awaitable = .null,
   1303         }, .monotonic).cancelation) {
   1304             .parked => unreachable,
   1305             .blocked => unreachable,
   1306             .blocked_alertable => unreachable,
   1307             .blocked_alertable_canceling => unreachable,
   1308             .blocked_canceling => unreachable,
   1309             .none => return .{ .thread = thread }, // new status is `.blocked`
   1310             .canceling => return error.Canceled, // new status is `.canceled`
   1311             .canceled => return .{ .thread = null }, // new status is `.canceled` (unchanged)
   1312         }
   1313     }
   1314     /// Checks whether this syscall has been canceled. This should be called when a syscall is
   1315     /// interrupted through a mechanism which may indicate cancelation, or may be spurious. If
   1316     /// the syscall was canceled, it is finished and `error.Canceled` is returned. Otherwise,
   1317     /// the syscall is not marked finished, and the caller should retry.
   1318     fn checkCancel(s: Syscall) Io.Cancelable!void {
   1319         const thread = s.thread orelse return;
   1320         switch (thread.status.fetchOr(.{
   1321             .cancelation = @enumFromInt(0b010),
   1322             .awaitable = .null,
   1323         }, .monotonic).cancelation) {
   1324             .none => unreachable,
   1325             .parked => unreachable,
   1326             .blocked_alertable => unreachable,
   1327             .blocked_alertable_canceling => unreachable,
   1328             .canceling => unreachable,
   1329             .canceled => unreachable,
   1330             .blocked => {}, // new status is `.blocked` (unchanged)
   1331             .blocked_canceling => return error.Canceled, // new status is `.canceled`
   1332         }
   1333     }
   1334     /// Marks this syscall as finished.
   1335     fn finish(s: Syscall) void {
   1336         const thread = s.thread orelse return;
   1337         switch (thread.status.fetchXor(.{
   1338             .cancelation = @enumFromInt(0b011),
   1339             .awaitable = .null,
   1340         }, .monotonic).cancelation) {
   1341             .none => unreachable,
   1342             .parked => unreachable,
   1343             .blocked_alertable => unreachable,
   1344             .blocked_alertable_canceling => unreachable,
   1345             .canceling => unreachable,
   1346             .canceled => unreachable,
   1347             .blocked => {}, // new status is `.none`
   1348             .blocked_canceling => {}, // new status is `.canceling`
   1349         }
   1350     }
   1351     /// Indicates instead of `NtCancelSynchronousIoFile` we need to use
   1352     /// `NtAlertThread` to interrupt the wait.
   1353     ///
   1354     /// Windows only, called from blocked state only.
   1355     fn toAlertable(s: Syscall) Io.Cancelable!AlertableSyscall {
   1356         comptime assert(is_windows);
   1357         const thread = s.thread orelse return .{ .thread = null };
   1358         var prev = thread.status.load(.monotonic);
   1359         while (true) prev = switch (prev.cancelation) {
   1360             .none => unreachable,
   1361             .parked => unreachable,
   1362             .blocked_alertable => unreachable,
   1363             .blocked_alertable_canceling => unreachable,
   1364             .canceling => unreachable,
   1365             .canceled => unreachable,
   1366 
   1367             .blocked => thread.status.cmpxchgWeak(prev, .{
   1368                 .cancelation = .blocked_alertable,
   1369                 .awaitable = prev.awaitable,
   1370             }, .monotonic, .monotonic) orelse return .{ .thread = thread },
   1371 
   1372             .blocked_canceling => thread.status.cmpxchgWeak(prev, .{
   1373                 .cancelation = .canceled,
   1374                 .awaitable = prev.awaitable,
   1375             }, .monotonic, .monotonic) orelse return error.Canceled,
   1376         };
   1377     }
   1378     /// Convenience wrapper which calls `finish`, then returns `err`.
   1379     fn fail(s: Syscall, err: anytype) @TypeOf(err) {
   1380         s.finish();
   1381         return err;
   1382     }
   1383     /// Convenience wrapper which calls `finish`, then calls `Threaded.errnoBug`.
   1384     fn errnoBug(s: Syscall, err: posix.E) Io.UnexpectedError {
   1385         @branchHint(.cold);
   1386         s.finish();
   1387         return Threaded.errnoBug(err);
   1388     }
   1389     /// Convenience wrapper which calls `finish`, then calls `posix.unexpectedErrno`.
   1390     fn unexpectedErrno(s: Syscall, err: posix.E) Io.UnexpectedError {
   1391         @branchHint(.cold);
   1392         s.finish();
   1393         return posix.unexpectedErrno(err);
   1394     }
   1395     /// Convenience wrapper which calls `finish`, then calls `windows.statusBug`.
   1396     fn ntstatusBug(s: Syscall, status: windows.NTSTATUS) Io.UnexpectedError {
   1397         @branchHint(.cold);
   1398         s.finish();
   1399         return windows.statusBug(status);
   1400     }
   1401     /// Convenience wrapper which calls `finish`, then calls `windows.unexpectedStatus`.
   1402     fn unexpectedNtstatus(s: Syscall, status: windows.NTSTATUS) Io.UnexpectedError {
   1403         @branchHint(.cold);
   1404         s.finish();
   1405         return windows.unexpectedStatus(status);
   1406     }
   1407 };
   1408 
   1409 const AlertableSyscall = struct {
   1410     thread: ?*Thread,
   1411 
   1412     comptime {
   1413         assert(is_windows);
   1414     }
   1415 
   1416     fn start() Io.Cancelable!AlertableSyscall {
   1417         const thread = Thread.current orelse return .{ .thread = null };
   1418         switch (thread.cancel_protection) {
   1419             .blocked => return .{ .thread = null },
   1420             .unblocked => {},
   1421         }
   1422         const old_status = thread.status.fetchOr(.{
   1423             .cancelation = @enumFromInt(0b010),
   1424             .awaitable = .null,
   1425         }, .monotonic);
   1426         switch (old_status.cancelation) {
   1427             .parked => unreachable,
   1428             .blocked => unreachable,
   1429             .blocked_alertable => unreachable,
   1430             .blocked_canceling => unreachable,
   1431             .blocked_alertable_canceling => unreachable,
   1432             .none => return .{ .thread = thread }, // new status is `.blocked_alertable`
   1433             .canceling => {
   1434                 // Status is unchanged (still `.canceling`)---change to `.canceled` before return.
   1435                 thread.status.store(.{ .cancelation = .canceled, .awaitable = old_status.awaitable }, .monotonic);
   1436                 return error.Canceled;
   1437             },
   1438             .canceled => return .{ .thread = null }, // new status is `.canceled` (unchanged)
   1439         }
   1440     }
   1441 
   1442     fn checkCancel(s: AlertableSyscall) Io.Cancelable!void {
   1443         comptime assert(is_windows);
   1444         const thread = s.thread orelse return;
   1445         const old_status = thread.status.fetchOr(.{
   1446             .cancelation = @enumFromInt(0b010),
   1447             .awaitable = .null,
   1448         }, .monotonic);
   1449         switch (old_status.cancelation) {
   1450             .none => unreachable,
   1451             .parked => unreachable,
   1452             .blocked => unreachable,
   1453             .blocked_canceling => unreachable,
   1454             .canceling => unreachable,
   1455             .canceled => unreachable,
   1456             .blocked_alertable => {}, // new status is `.blocked_alertable` (unchanged)
   1457             .blocked_alertable_canceling => {
   1458                 // New status is `.canceling`---change to `.canceled` before return.
   1459                 thread.status.store(.{ .cancelation = .canceled, .awaitable = old_status.awaitable }, .monotonic);
   1460                 return error.Canceled;
   1461             },
   1462         }
   1463     }
   1464 
   1465     fn finish(s: AlertableSyscall) void {
   1466         comptime assert(is_windows);
   1467         const thread = s.thread orelse return;
   1468         switch (thread.status.fetchXor(.{
   1469             .cancelation = @enumFromInt(0b010),
   1470             .awaitable = .null,
   1471         }, .monotonic).cancelation) {
   1472             .none => unreachable,
   1473             .parked => unreachable,
   1474             .blocked => unreachable,
   1475             .blocked_canceling => unreachable,
   1476             .canceling => unreachable,
   1477             .canceled => unreachable,
   1478             .blocked_alertable => {}, // new status is `.none`
   1479             .blocked_alertable_canceling => {}, // new status is `.canceling`
   1480         }
   1481     }
   1482 
   1483     fn fail(s: AlertableSyscall, err: anytype) @TypeOf(err) {
   1484         s.finish();
   1485         return err;
   1486     }
   1487 
   1488     fn ntstatusBug(s: AlertableSyscall, status: windows.NTSTATUS) Io.UnexpectedError {
   1489         @branchHint(.cold);
   1490         s.finish();
   1491         return windows.statusBug(status);
   1492     }
   1493 
   1494     fn unexpectedNtstatus(s: AlertableSyscall, status: windows.NTSTATUS) Io.UnexpectedError {
   1495         @branchHint(.cold);
   1496         s.finish();
   1497         return windows.unexpectedStatus(status);
   1498     }
   1499 
   1500     fn unexpectedWsaError(s: AlertableSyscall, err: ws2_32.WinsockError) Io.UnexpectedError {
   1501         @branchHint(.cold);
   1502         s.finish();
   1503         return windows.unexpectedWsaError(err);
   1504     }
   1505 
   1506     fn wsaErrorBug(s: AlertableSyscall, err: ws2_32.WinsockError) Io.UnexpectedError {
   1507         @branchHint(.cold);
   1508         s.finish();
   1509         return windows.wsaErrorBug(err);
   1510     }
   1511 };
   1512 
   1513 pub fn waitForApcOrAlert() void {
   1514     const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
   1515     _ = windows.ntdll.NtDelayExecution(windows.TRUE, &infinite_timeout);
   1516 }
   1517 
   1518 pub const max_iovecs_len = 8;
   1519 pub const splat_buffer_size = 64;
   1520 /// Happens to be the same number that matches maximum number of handles that
   1521 /// NtWaitForMultipleObjects accepts. We use this value also for poll() on
   1522 /// posix systems.
   1523 const poll_buffer_len = 64;
   1524 pub const default_PATH = "/usr/local/bin:/bin/:/usr/bin";
   1525 /// There are multiple kernel bugs being worked around with retries.
   1526 const max_windows_kernel_bug_retries = 13;
   1527 
   1528 comptime {
   1529     if (@TypeOf(posix.IOV_MAX) != void) assert(max_iovecs_len <= posix.IOV_MAX);
   1530 }
   1531 
   1532 pub const InitOptions = struct {
   1533     /// Affects how many bytes are memory-mapped for threads.
   1534     stack_size: usize = std.Thread.SpawnConfig.default_stack_size,
   1535     /// Maximum thread pool size (excluding main thread) when dispatching async
   1536     /// tasks. Until this limit, calls to `Io.async` when all threads are busy will
   1537     /// cause a new thread to be spawned and permanently added to the pool. After
   1538     /// this limit, calls to `Io.async` when all threads are busy run the task
   1539     /// immediately.
   1540     ///
   1541     /// Defaults to one less than the number of logical CPU cores.
   1542     ///
   1543     /// Protected by `Threaded.mutex` once the I/O instance is already in use. See
   1544     /// `setAsyncLimit`.
   1545     async_limit: ?Io.Limit = null,
   1546     /// Maximum thread pool size (excluding main thread) for dispatching concurrent
   1547     /// tasks. Until this limit, calls to `Io.concurrent` will increase the thread
   1548     /// pool size.
   1549     ///
   1550     /// After this number, calls to `Io.concurrent` return `error.ConcurrencyUnavailable`.
   1551     concurrent_limit: Io.Limit = .unlimited,
   1552     /// Affects the following operations:
   1553     /// * `processExecutablePath` on OpenBSD and Haiku.
   1554     argv0: Argv0 = .empty,
   1555     /// Affects the following operations:
   1556     /// * `fileIsTty`
   1557     /// * `processExecutablePath` on OpenBSD and Haiku (observes "PATH").
   1558     /// * `processSpawn`, `processSpawnPath`, `processReplace`, `processReplacePath`
   1559     environ: process.Environ = .empty,
   1560     /// If set to `true`, `File.MemoryMap` APIs will always take the fallback path.
   1561     disable_memory_mapping: bool = false,
   1562 };
   1563 
   1564 /// Related:
   1565 /// * `init_single_threaded`
   1566 pub fn init(
   1567     /// Must be threadsafe. Only used for the following functions:
   1568     /// * `Io.VTable.async`
   1569     /// * `Io.VTable.concurrent`
   1570     /// * `Io.VTable.groupAsync`
   1571     /// * `Io.VTable.groupConcurrent`
   1572     /// If these functions are avoided, then `Allocator.failing` may be passed
   1573     /// here.
   1574     gpa: Allocator,
   1575     options: InitOptions,
   1576 ) Threaded {
   1577     if (builtin.single_threaded) return .{
   1578         .allocator = gpa,
   1579         .stack_size = options.stack_size,
   1580         .async_limit = options.async_limit orelse init_single_threaded.async_limit,
   1581         .cpu_count_error = init_single_threaded.cpu_count_error,
   1582         .concurrent_limit = options.concurrent_limit,
   1583         .old_sig_io = undefined,
   1584         .old_sig_pipe = undefined,
   1585         .have_signal_handler = init_single_threaded.have_signal_handler,
   1586         .argv0 = options.argv0,
   1587         .environ_initialized = options.environ.block.isEmpty(),
   1588         .environ = .{ .process_environ = options.environ },
   1589         .worker_threads = init_single_threaded.worker_threads,
   1590         .disable_memory_mapping = options.disable_memory_mapping,
   1591     };
   1592 
   1593     const cpu_count = std.Thread.getCpuCount();
   1594 
   1595     var t: Threaded = .{
   1596         .allocator = gpa,
   1597         .stack_size = options.stack_size,
   1598         .async_limit = options.async_limit orelse if (cpu_count) |n| .limited(n - 1) else |_| .nothing,
   1599         .concurrent_limit = options.concurrent_limit,
   1600         .cpu_count_error = if (cpu_count) |_| null else |e| e,
   1601         .old_sig_io = undefined,
   1602         .old_sig_pipe = undefined,
   1603         .have_signal_handler = false,
   1604         .argv0 = options.argv0,
   1605         .environ_initialized = options.environ.block.isEmpty(),
   1606         .environ = .{ .process_environ = options.environ },
   1607         .worker_threads = .init(null),
   1608         .disable_memory_mapping = options.disable_memory_mapping,
   1609     };
   1610 
   1611     if (posix.Sigaction != void) {
   1612         // This causes sending `posix.SIG.IO` to thread to interrupt blocking
   1613         // syscalls, returning `posix.E.INTR`.
   1614         const act: posix.Sigaction = .{
   1615             .handler = .{ .handler = doNothingSignalHandler },
   1616             .mask = posix.sigemptyset(),
   1617             .flags = 0,
   1618         };
   1619         if (have_sig_io) posix.sigaction(.IO, &act, &t.old_sig_io);
   1620         if (have_sig_pipe) posix.sigaction(.PIPE, &act, &t.old_sig_pipe);
   1621         t.have_signal_handler = true;
   1622     }
   1623 
   1624     return t;
   1625 }
   1626 
   1627 /// Statically initialize such that calls to `Io.VTable.concurrent` will fail
   1628 /// with `error.ConcurrencyUnavailable`.
   1629 ///
   1630 /// When initialized this way:
   1631 /// * cancel requests have no effect.
   1632 /// * `deinit` is safe, but unnecessary to call.
   1633 pub const init_single_threaded: Threaded = .{
   1634     .allocator = .failing,
   1635     .stack_size = std.Thread.SpawnConfig.default_stack_size,
   1636     .async_limit = .nothing,
   1637     .cpu_count_error = null,
   1638     .concurrent_limit = .nothing,
   1639     .old_sig_io = undefined,
   1640     .old_sig_pipe = undefined,
   1641     .have_signal_handler = false,
   1642     .argv0 = .empty,
   1643     .environ_initialized = true,
   1644     .environ = .empty,
   1645     .worker_threads = .init(null),
   1646     .disable_memory_mapping = false,
   1647 };
   1648 
   1649 var global_single_threaded_instance: Threaded = .init_single_threaded;
   1650 
   1651 /// In general, the application is responsible for choosing the `Io`
   1652 /// implementation and library code should accept an `Io` parameter rather than
   1653 /// accessing this declaration. Most code should avoid referencing this
   1654 /// declaration entirely.
   1655 ///
   1656 /// However, in some cases such as debugging, it is desirable to hardcode a
   1657 /// reference to this `Io` implementation.
   1658 ///
   1659 /// This instance does not support concurrency or cancelation.
   1660 pub const global_single_threaded: *Threaded = &global_single_threaded_instance;
   1661 
   1662 pub fn setAsyncLimit(t: *Threaded, new_limit: Io.Limit) void {
   1663     mutexLock(&t.mutex);
   1664     defer mutexUnlock(&t.mutex);
   1665     t.async_limit = new_limit;
   1666 }
   1667 
   1668 pub fn deinit(t: *Threaded) void {
   1669     t.join();
   1670     if (is_windows and t.wsa.status == .initialized) {
   1671         if (ws2_32.WSACleanup() != 0) recoverableOsBugDetected();
   1672     }
   1673     if (posix.Sigaction != void and t.have_signal_handler) {
   1674         if (have_sig_io) posix.sigaction(.IO, &t.old_sig_io, null);
   1675         if (have_sig_pipe) posix.sigaction(.PIPE, &t.old_sig_pipe, null);
   1676     }
   1677     t.null_file.deinit();
   1678     t.random_file.deinit();
   1679     t.pipe_file.deinit();
   1680     t.* = undefined;
   1681 }
   1682 
   1683 fn join(t: *Threaded) void {
   1684     if (builtin.single_threaded) return;
   1685     {
   1686         mutexLock(&t.mutex);
   1687         defer mutexUnlock(&t.mutex);
   1688         t.join_requested = true;
   1689     }
   1690     condBroadcast(&t.cond);
   1691     t.wait_group.wait();
   1692 }
   1693 
   1694 fn worker(t: *Threaded) void {
   1695     var thread: Thread = .{
   1696         .next = undefined,
   1697         .id = std.Thread.getCurrentId(),
   1698         .handle = handle: {
   1699             if (std.Thread.use_pthreads) break :handle std.c.pthread_self();
   1700             if (is_windows) break :handle undefined; // populated below
   1701         },
   1702         .status = .init(.{
   1703             .cancelation = .none,
   1704             .awaitable = .null,
   1705         }),
   1706         .cancel_protection = .unblocked,
   1707         .futex_waiter = undefined,
   1708         .unpark_flag = unpark_flag_init,
   1709         .csprng = .uninitialized,
   1710     };
   1711     Thread.current = &thread;
   1712 
   1713     if (is_windows) {
   1714         assert(windows.ntdll.NtOpenThread(
   1715             &thread.handle,
   1716             .{
   1717                 .SPECIFIC = .{
   1718                     .THREAD = .{
   1719                         .TERMINATE = true, // for `NtCancelSynchronousIoFile`
   1720                     },
   1721                 },
   1722             },
   1723             &.{ .ObjectName = null },
   1724             &windows.teb().ClientId,
   1725         ) == .SUCCESS);
   1726     }
   1727     defer if (is_windows) {
   1728         windows.CloseHandle(thread.handle);
   1729     };
   1730 
   1731     {
   1732         var head = t.worker_threads.load(.monotonic);
   1733         while (true) {
   1734             thread.next = head;
   1735             head = t.worker_threads.cmpxchgWeak(
   1736                 head,
   1737                 &thread,
   1738                 .release,
   1739                 .monotonic,
   1740             ) orelse break;
   1741         }
   1742     }
   1743 
   1744     defer t.wait_group.finish();
   1745 
   1746     mutexLock(&t.mutex);
   1747     defer mutexUnlock(&t.mutex);
   1748 
   1749     while (true) {
   1750         while (t.run_queue.popFirst()) |runnable_node| {
   1751             mutexUnlock(&t.mutex);
   1752             thread.cancel_protection = .unblocked;
   1753             const runnable: *Runnable = @fieldParentPtr("node", runnable_node);
   1754             runnable.startFn(runnable, &thread, t);
   1755             mutexLock(&t.mutex);
   1756             t.busy_count -= 1;
   1757         }
   1758         if (t.join_requested) break;
   1759         condWait(&t.cond, &t.mutex);
   1760     }
   1761 }
   1762 
   1763 pub fn io(t: *Threaded) Io {
   1764     return .{
   1765         .userdata = t,
   1766         .vtable = &.{
   1767             .crashHandler = crashHandler,
   1768 
   1769             .async = async,
   1770             .concurrent = concurrent,
   1771             .await = await,
   1772             .cancel = cancel,
   1773 
   1774             .groupAsync = groupAsync,
   1775             .groupConcurrent = groupConcurrent,
   1776             .groupAwait = groupAwait,
   1777             .groupCancel = groupCancel,
   1778 
   1779             .recancel = recancel,
   1780             .swapCancelProtection = swapCancelProtection,
   1781             .checkCancel = checkCancel,
   1782 
   1783             .futexWait = futexWait,
   1784             .futexWaitUncancelable = futexWaitUncancelable,
   1785             .futexWake = futexWake,
   1786 
   1787             .operate = operate,
   1788             .batchAwaitAsync = batchAwaitAsync,
   1789             .batchAwaitConcurrent = batchAwaitConcurrent,
   1790             .batchCancel = batchCancel,
   1791 
   1792             .dirCreateDir = dirCreateDir,
   1793             .dirCreateDirPath = dirCreateDirPath,
   1794             .dirCreateDirPathOpen = dirCreateDirPathOpen,
   1795             .dirStat = dirStat,
   1796             .dirStatFile = dirStatFile,
   1797             .dirAccess = dirAccess,
   1798             .dirCreateFile = dirCreateFile,
   1799             .dirCreateFileAtomic = dirCreateFileAtomic,
   1800             .dirOpenFile = dirOpenFile,
   1801             .dirOpenDir = dirOpenDir,
   1802             .dirClose = dirClose,
   1803             .dirRead = dirRead,
   1804             .dirRealPath = dirRealPath,
   1805             .dirRealPathFile = dirRealPathFile,
   1806             .dirDeleteFile = dirDeleteFile,
   1807             .dirDeleteDir = dirDeleteDir,
   1808             .dirRename = dirRename,
   1809             .dirRenamePreserve = dirRenamePreserve,
   1810             .dirSymLink = dirSymLink,
   1811             .dirReadLink = dirReadLink,
   1812             .dirSetOwner = dirSetOwner,
   1813             .dirSetFileOwner = dirSetFileOwner,
   1814             .dirSetPermissions = dirSetPermissions,
   1815             .dirSetFilePermissions = dirSetFilePermissions,
   1816             .dirSetTimestamps = dirSetTimestamps,
   1817             .dirHardLink = dirHardLink,
   1818 
   1819             .fileStat = fileStat,
   1820             .fileLength = fileLength,
   1821             .fileClose = fileClose,
   1822             .fileWritePositional = fileWritePositional,
   1823             .fileWriteFileStreaming = fileWriteFileStreaming,
   1824             .fileWriteFilePositional = fileWriteFilePositional,
   1825             .fileReadPositional = fileReadPositional,
   1826             .fileSeekBy = fileSeekBy,
   1827             .fileSeekTo = fileSeekTo,
   1828             .fileSync = fileSync,
   1829             .fileIsTty = fileIsTty,
   1830             .fileEnableAnsiEscapeCodes = fileEnableAnsiEscapeCodes,
   1831             .fileSupportsAnsiEscapeCodes = fileSupportsAnsiEscapeCodes,
   1832             .fileSetLength = fileSetLength,
   1833             .fileSetOwner = fileSetOwner,
   1834             .fileSetPermissions = fileSetPermissions,
   1835             .fileSetTimestamps = fileSetTimestamps,
   1836             .fileLock = fileLock,
   1837             .fileTryLock = fileTryLock,
   1838             .fileUnlock = fileUnlock,
   1839             .fileDowngradeLock = fileDowngradeLock,
   1840             .fileRealPath = fileRealPath,
   1841             .fileHardLink = fileHardLink,
   1842 
   1843             .fileMemoryMapCreate = fileMemoryMapCreate,
   1844             .fileMemoryMapDestroy = fileMemoryMapDestroy,
   1845             .fileMemoryMapSetLength = fileMemoryMapSetLength,
   1846             .fileMemoryMapRead = fileMemoryMapRead,
   1847             .fileMemoryMapWrite = fileMemoryMapWrite,
   1848 
   1849             .processExecutableOpen = processExecutableOpen,
   1850             .processExecutablePath = processExecutablePath,
   1851             .lockStderr = lockStderr,
   1852             .tryLockStderr = tryLockStderr,
   1853             .unlockStderr = unlockStderr,
   1854             .processCurrentPath = processCurrentPath,
   1855             .processSetCurrentDir = processSetCurrentDir,
   1856             .processSetCurrentPath = processSetCurrentPath,
   1857             .processReplace = processReplace,
   1858             .processReplacePath = processReplacePath,
   1859             .processSpawn = processSpawn,
   1860             .processSpawnPath = processSpawnPath,
   1861             .childWait = childWait,
   1862             .childKill = childKill,
   1863 
   1864             .progressParentFile = progressParentFile,
   1865 
   1866             .now = now,
   1867             .clockResolution = clockResolution,
   1868             .sleep = sleep,
   1869 
   1870             .random = random,
   1871             .randomSecure = randomSecure,
   1872 
   1873             .netListenIp = switch (native_os) {
   1874                 .windows => netListenIpWindows,
   1875                 else => netListenIpPosix,
   1876             },
   1877             .netListenUnix = switch (native_os) {
   1878                 .windows => netListenUnixWindows,
   1879                 else => netListenUnixPosix,
   1880             },
   1881             .netAccept = switch (native_os) {
   1882                 .windows => netAcceptWindows,
   1883                 else => netAcceptPosix,
   1884             },
   1885             .netBindIp = switch (native_os) {
   1886                 .windows => netBindIpWindows,
   1887                 else => netBindIpPosix,
   1888             },
   1889             .netConnectIp = switch (native_os) {
   1890                 .windows => netConnectIpWindows,
   1891                 else => netConnectIpPosix,
   1892             },
   1893             .netConnectUnix = switch (native_os) {
   1894                 .windows => netConnectUnixWindows,
   1895                 else => netConnectUnixPosix,
   1896             },
   1897             .netSocketCreatePair = netSocketCreatePair,
   1898             .netClose = netClose,
   1899             .netShutdown = switch (native_os) {
   1900                 .windows => netShutdownWindows,
   1901                 else => netShutdownPosix,
   1902             },
   1903             .netRead = switch (native_os) {
   1904                 .windows => netReadWindows,
   1905                 else => netReadPosix,
   1906             },
   1907             .netWrite = switch (native_os) {
   1908                 .windows => netWriteWindows,
   1909                 else => netWritePosix,
   1910             },
   1911             .netWriteFile = netWriteFile,
   1912             .netSend = switch (native_os) {
   1913                 .windows => netSendWindows,
   1914                 else => netSendPosix,
   1915             },
   1916             .netInterfaceNameResolve = netInterfaceNameResolve,
   1917             .netInterfaceName = netInterfaceName,
   1918             .netLookup = netLookup,
   1919         },
   1920     };
   1921 }
   1922 
   1923 pub const socket_flags_unsupported = is_darwin or native_os == .haiku;
   1924 const have_accept4 = !socket_flags_unsupported;
   1925 const have_flock_open_flags = @hasField(posix.O, "EXLOCK");
   1926 const have_networking = std.options.networking and native_os != .wasi;
   1927 const have_flock = @TypeOf(posix.system.flock) != void;
   1928 const have_sendmmsg = native_os == .linux;
   1929 const have_futex = switch (builtin.cpu.arch) {
   1930     .wasm32, .wasm64 => builtin.cpu.has(.wasm, .atomics),
   1931     else => true,
   1932 };
   1933 const have_preadv = switch (native_os) {
   1934     .windows, .haiku => false,
   1935     else => true,
   1936 };
   1937 const have_sig_io = posix.SIG != void and @hasField(posix.SIG, "IO");
   1938 const have_sig_pipe = posix.SIG != void and @hasField(posix.SIG, "PIPE");
   1939 const have_sendfile = if (builtin.link_libc) @TypeOf(std.c.sendfile) != void else native_os == .linux;
   1940 const have_copy_file_range = switch (native_os) {
   1941     .linux, .freebsd => true,
   1942     else => false,
   1943 };
   1944 const have_fcopyfile = is_darwin;
   1945 const have_fchmodat2 = native_os == .linux and
   1946     (builtin.os.isAtLeast(.linux, .{ .major = 6, .minor = 6, .patch = 0 }) orelse true) and
   1947     (builtin.abi.isAndroid() or !std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 }));
   1948 const have_fchmodat_flags = native_os != .linux or
   1949     (!builtin.abi.isAndroid() and std.c.versionCheck(.{ .major = 2, .minor = 32, .patch = 0 }));
   1950 
   1951 const have_fchown = switch (native_os) {
   1952     .wasi, .windows => false,
   1953     else => true,
   1954 };
   1955 
   1956 const have_fchmod = switch (native_os) {
   1957     .windows => false,
   1958     .wasi => builtin.link_libc,
   1959     else => true,
   1960 };
   1961 
   1962 const have_waitid = switch (native_os) {
   1963     .linux => @hasField(std.os.linux.SYS, "waitid"),
   1964     else => false,
   1965 };
   1966 
   1967 const have_wait4 = switch (native_os) {
   1968     .linux => @hasField(std.os.linux.SYS, "wait4"),
   1969     .dragonfly, .freebsd, .netbsd, .openbsd, .illumos, .serenity, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => true,
   1970     else => false,
   1971 };
   1972 
   1973 const have_mmap = switch (native_os) {
   1974     .wasi, .windows => false,
   1975     else => true,
   1976 };
   1977 const have_poll = switch (native_os) {
   1978     .wasi, .windows => false,
   1979     else => true,
   1980 };
   1981 
   1982 const open_sym = if (posix.lfs64_abi) posix.system.open64 else posix.system.open;
   1983 const openat_sym = if (posix.lfs64_abi) posix.system.openat64 else posix.system.openat;
   1984 const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat;
   1985 const fstatat_sym = if (posix.lfs64_abi) posix.system.fstatat64 else posix.system.fstatat;
   1986 const lseek_sym = if (posix.lfs64_abi) posix.system.lseek64 else posix.system.lseek;
   1987 const preadv_sym = if (posix.lfs64_abi) posix.system.preadv64 else posix.system.preadv;
   1988 const pread_sym = if (posix.lfs64_abi) posix.system.pread64 else posix.system.pread;
   1989 const ftruncate_sym = if (posix.lfs64_abi) posix.system.ftruncate64 else posix.system.ftruncate;
   1990 const pwritev_sym = if (posix.lfs64_abi) posix.system.pwritev64 else posix.system.pwritev;
   1991 const pwrite_sym = if (posix.lfs64_abi) posix.system.pwrite64 else posix.system.pwrite;
   1992 const sendfile_sym = if (posix.lfs64_abi) posix.system.sendfile64 else posix.system.sendfile;
   1993 const mmap_sym = if (posix.lfs64_abi) posix.system.mmap64 else posix.system.mmap;
   1994 
   1995 const linux_copy_file_range_use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) .{
   1996     .major = 34,
   1997     .minor = 0,
   1998     .patch = 0,
   1999 } else .{
   2000     .major = 2,
   2001     .minor = 27,
   2002     .patch = 0,
   2003 });
   2004 const linux_copy_file_range_sys = if (linux_copy_file_range_use_c) std.c else std.os.linux;
   2005 
   2006 const statx_use_c = std.c.versionCheck(if (builtin.abi.isAndroid())
   2007     .{ .major = 30, .minor = 0, .patch = 0 }
   2008 else
   2009     .{ .major = 2, .minor = 28, .patch = 0 });
   2010 
   2011 const use_libc_getrandom = std.c.versionCheck(if (builtin.abi.isAndroid()) .{
   2012     .major = 28,
   2013     .minor = 0,
   2014     .patch = 0,
   2015 } else .{
   2016     .major = 2,
   2017     .minor = 25,
   2018     .patch = 0,
   2019 });
   2020 
   2021 const use_dev_urandom = @TypeOf(posix.system.getrandom) == void and native_os == .linux;
   2022 
   2023 fn crashHandler(userdata: ?*anyopaque) void {
   2024     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2025     _ = t;
   2026     const thread = Thread.current orelse return;
   2027     thread.status.store(.{ .cancelation = .canceled, .awaitable = .null }, .monotonic);
   2028     thread.cancel_protection = .blocked;
   2029 }
   2030 
   2031 fn async(
   2032     userdata: ?*anyopaque,
   2033     result: []u8,
   2034     result_alignment: Alignment,
   2035     context: []const u8,
   2036     context_alignment: Alignment,
   2037     start: *const fn (context: *const anyopaque, result: *anyopaque) void,
   2038 ) ?*Io.AnyFuture {
   2039     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2040     if (builtin.single_threaded) {
   2041         start(context.ptr, result.ptr);
   2042         return null;
   2043     }
   2044 
   2045     const gpa = t.allocator;
   2046     const future = Future.create(gpa, result.len, result_alignment, context, context_alignment, start) catch |err| switch (err) {
   2047         error.OutOfMemory => {
   2048             start(context.ptr, result.ptr);
   2049             return null;
   2050         },
   2051     };
   2052 
   2053     mutexLock(&t.mutex);
   2054 
   2055     const busy_count = t.busy_count;
   2056 
   2057     if (busy_count >= @intFromEnum(t.async_limit)) {
   2058         mutexUnlock(&t.mutex);
   2059         future.destroy(gpa);
   2060         start(context.ptr, result.ptr);
   2061         return null;
   2062     }
   2063 
   2064     t.busy_count = busy_count + 1;
   2065 
   2066     const pool_size = t.wait_group.value();
   2067     if (pool_size - busy_count == 0) {
   2068         t.wait_group.start();
   2069         const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {
   2070             t.wait_group.finish();
   2071             t.busy_count = busy_count;
   2072             mutexUnlock(&t.mutex);
   2073             future.destroy(gpa);
   2074             start(context.ptr, result.ptr);
   2075             return null;
   2076         };
   2077         thread.detach();
   2078     }
   2079 
   2080     t.run_queue.prepend(&future.runnable.node);
   2081 
   2082     mutexUnlock(&t.mutex);
   2083     condSignal(&t.cond);
   2084     return @ptrCast(future);
   2085 }
   2086 
   2087 fn concurrent(
   2088     userdata: ?*anyopaque,
   2089     result_len: usize,
   2090     result_alignment: Alignment,
   2091     context: []const u8,
   2092     context_alignment: Alignment,
   2093     start: *const fn (context: *const anyopaque, result: *anyopaque) void,
   2094 ) Io.ConcurrentError!*Io.AnyFuture {
   2095     if (builtin.single_threaded) return error.ConcurrencyUnavailable;
   2096 
   2097     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2098 
   2099     const gpa = t.allocator;
   2100     const future = Future.create(gpa, result_len, result_alignment, context, context_alignment, start) catch |err| switch (err) {
   2101         error.OutOfMemory => return error.ConcurrencyUnavailable,
   2102     };
   2103     errdefer future.destroy(gpa);
   2104 
   2105     mutexLock(&t.mutex);
   2106     defer mutexUnlock(&t.mutex);
   2107 
   2108     const busy_count = t.busy_count;
   2109 
   2110     if (busy_count >= @intFromEnum(t.concurrent_limit))
   2111         return error.ConcurrencyUnavailable;
   2112 
   2113     t.busy_count = busy_count + 1;
   2114     errdefer t.busy_count = busy_count;
   2115 
   2116     const pool_size = t.wait_group.value();
   2117     if (pool_size - busy_count == 0) {
   2118         t.wait_group.start();
   2119         errdefer t.wait_group.finish();
   2120 
   2121         const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch
   2122             return error.ConcurrencyUnavailable;
   2123 
   2124         thread.detach();
   2125     }
   2126 
   2127     t.run_queue.prepend(&future.runnable.node);
   2128 
   2129     condSignal(&t.cond);
   2130     return @ptrCast(future);
   2131 }
   2132 
   2133 fn groupAsync(
   2134     userdata: ?*anyopaque,
   2135     type_erased: *Io.Group,
   2136     context: []const u8,
   2137     context_alignment: Alignment,
   2138     start: *const fn (context: *const anyopaque) void,
   2139 ) void {
   2140     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2141     const g: Group = .{ .ptr = type_erased };
   2142 
   2143     if (builtin.single_threaded) return groupAsyncEager(start, context.ptr);
   2144 
   2145     const gpa = t.allocator;
   2146     const task = Group.Task.create(gpa, g, context, context_alignment, start) catch |err| switch (err) {
   2147         error.OutOfMemory => return groupAsyncEager(start, context.ptr),
   2148     };
   2149 
   2150     mutexLock(&t.mutex);
   2151 
   2152     const busy_count = t.busy_count;
   2153 
   2154     if (busy_count >= @intFromEnum(t.async_limit)) {
   2155         mutexUnlock(&t.mutex);
   2156         task.destroy(gpa);
   2157         return groupAsyncEager(start, context.ptr);
   2158     }
   2159 
   2160     t.busy_count = busy_count + 1;
   2161 
   2162     const pool_size = t.wait_group.value();
   2163     if (pool_size - busy_count == 0) {
   2164         t.wait_group.start();
   2165         const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch {
   2166             t.wait_group.finish();
   2167             t.busy_count = busy_count;
   2168             mutexUnlock(&t.mutex);
   2169             task.destroy(gpa);
   2170             return groupAsyncEager(start, context.ptr);
   2171         };
   2172         thread.detach();
   2173     }
   2174 
   2175     // TODO: if this logic is changed to be lock-free, this `fetchAdd` must be released by the queue
   2176     // prepend so that the task doesn't finish without observing this and try to decrement the count
   2177     // below zero.
   2178     _ = g.status().fetchAdd(.{
   2179         .num_running = 1,
   2180         .have_awaiter = false,
   2181         .canceled = false,
   2182     }, .monotonic);
   2183     t.run_queue.prepend(&task.runnable.node);
   2184 
   2185     mutexUnlock(&t.mutex);
   2186     condSignal(&t.cond);
   2187 }
   2188 fn groupAsyncEager(
   2189     start: *const fn (context: *const anyopaque) void,
   2190     context: *const anyopaque,
   2191 ) void {
   2192     start(context);
   2193 }
   2194 
   2195 fn groupConcurrent(
   2196     userdata: ?*anyopaque,
   2197     type_erased: *Io.Group,
   2198     context: []const u8,
   2199     context_alignment: Alignment,
   2200     start: *const fn (context: *const anyopaque) void,
   2201 ) Io.ConcurrentError!void {
   2202     if (builtin.single_threaded) return error.ConcurrencyUnavailable;
   2203 
   2204     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2205     const g: Group = .{ .ptr = type_erased };
   2206 
   2207     const gpa = t.allocator;
   2208     const task = Group.Task.create(gpa, g, context, context_alignment, start) catch |err| switch (err) {
   2209         error.OutOfMemory => return error.ConcurrencyUnavailable,
   2210     };
   2211     errdefer task.destroy(gpa);
   2212 
   2213     mutexLock(&t.mutex);
   2214     defer mutexUnlock(&t.mutex);
   2215 
   2216     const busy_count = t.busy_count;
   2217 
   2218     if (busy_count >= @intFromEnum(t.concurrent_limit))
   2219         return error.ConcurrencyUnavailable;
   2220 
   2221     t.busy_count = busy_count + 1;
   2222     errdefer t.busy_count = busy_count;
   2223 
   2224     const pool_size = t.wait_group.value();
   2225     if (pool_size - busy_count == 0) {
   2226         t.wait_group.start();
   2227         errdefer t.wait_group.finish();
   2228 
   2229         const thread = std.Thread.spawn(.{ .stack_size = t.stack_size }, worker, .{t}) catch
   2230             return error.ConcurrencyUnavailable;
   2231 
   2232         thread.detach();
   2233     }
   2234 
   2235     // TODO: if this logic is changed to be lock-free, this `fetchAdd` must be released by the queue
   2236     // prepend so that the task doesn't finish without observing this and try to decrement the count
   2237     // below zero.
   2238     _ = g.status().fetchAdd(.{
   2239         .num_running = 1,
   2240         .have_awaiter = false,
   2241         .canceled = false,
   2242     }, .monotonic);
   2243     t.run_queue.prepend(&task.runnable.node);
   2244 
   2245     condSignal(&t.cond);
   2246 }
   2247 
   2248 fn groupAwait(userdata: ?*anyopaque, type_erased: *Io.Group, initial_token: *anyopaque) Io.Cancelable!void {
   2249     _ = initial_token; // we need to load `token` *after* the group finishes
   2250     if (builtin.single_threaded) unreachable; // nothing to await
   2251     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2252     const g: Group = .{ .ptr = type_erased };
   2253 
   2254     var num_completed: std.atomic.Value(u32) = .init(0);
   2255     g.awaiter().* = &num_completed;
   2256 
   2257     const pre_await_status = g.status().fetchOr(.{
   2258         .num_running = 0,
   2259         .have_awaiter = true,
   2260         .canceled = false,
   2261     }, .acq_rel); // acquire results if complete; release `g.awaiter()`
   2262 
   2263     assert(!pre_await_status.have_awaiter);
   2264     assert(!pre_await_status.canceled);
   2265     if (pre_await_status.num_running == 0) {
   2266         // Already done. Since the group is finished, it's illegal to spawn more tasks in it
   2267         // until we return, so we can access `g.status()` non-atomically.
   2268         g.status().raw.have_awaiter = false;
   2269         return;
   2270     }
   2271 
   2272     while (Thread.futexWait(&num_completed.raw, 0, null)) {
   2273         switch (num_completed.load(.acquire)) { // acquire task results
   2274             0 => continue,
   2275             1 => break,
   2276             else => unreachable, // group was reused before `await` returned
   2277         }
   2278     } else |err| switch (err) {
   2279         error.Canceled => {
   2280             const pre_cancel_status = g.status().fetchOr(.{
   2281                 .num_running = 0,
   2282                 .have_awaiter = false,
   2283                 .canceled = true,
   2284             }, .acq_rel); // acquire results if complete; release `g.awaiter()`
   2285             assert(pre_cancel_status.have_awaiter);
   2286             assert(!pre_cancel_status.canceled);
   2287 
   2288             // Even if `pre_cancel_status.num_running == 0`, we still need to wait for the signal,
   2289             // because in that case the last member of the group is already trying to modify it.
   2290             // However, if we know everything is done, we *can* skip signaling blocked threads.
   2291             const skip_signals = pre_cancel_status.num_running == 0;
   2292             g.waitForCancelWithSignaling(t, &num_completed, skip_signals);
   2293 
   2294             // The group is finished, so it's illegal to spawn more tasks in it until we return, so
   2295             // we can access `g.status()` non-atomically.
   2296             g.status().raw.canceled = false;
   2297             g.status().raw.have_awaiter = false;
   2298             return error.Canceled;
   2299         },
   2300     }
   2301 
   2302     // The group is finished, so it's illegal to spawn more tasks in it until we return, so
   2303     // we can access `g.status()` non-atomically.
   2304     g.status().raw.have_awaiter = false;
   2305 }
   2306 
   2307 fn groupCancel(userdata: ?*anyopaque, type_erased: *Io.Group, initial_token: *anyopaque) void {
   2308     _ = initial_token;
   2309     if (builtin.single_threaded) unreachable; // nothing to cancel
   2310     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2311     const g: Group = .{ .ptr = type_erased };
   2312 
   2313     var num_completed: std.atomic.Value(u32) = .init(0);
   2314     g.awaiter().* = &num_completed;
   2315 
   2316     const pre_cancel_status = g.status().fetchOr(.{
   2317         .num_running = 0,
   2318         .have_awaiter = true,
   2319         .canceled = true,
   2320     }, .acq_rel); // acquire results if complete; release `g.awaiter()`
   2321 
   2322     assert(!pre_cancel_status.have_awaiter);
   2323     assert(!pre_cancel_status.canceled);
   2324     if (pre_cancel_status.num_running == 0) {
   2325         // Already done. Since the group is finished, it's illegal to spawn more tasks in it
   2326         // until we return, so we can access `g.status()` non-atomically.
   2327         g.status().raw.have_awaiter = false;
   2328         g.status().raw.canceled = false;
   2329         return;
   2330     }
   2331 
   2332     g.waitForCancelWithSignaling(t, &num_completed, false);
   2333 
   2334     g.status().raw = .{ .num_running = 0, .have_awaiter = false, .canceled = false };
   2335 }
   2336 
   2337 fn recancel(userdata: ?*anyopaque) void {
   2338     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2339     _ = t;
   2340     recancelInner();
   2341 }
   2342 fn recancelInner() void {
   2343     const thread = Thread.current.?; // called `recancel` but was not canceled
   2344     switch (thread.status.fetchXor(.{
   2345         .cancelation = @enumFromInt(0b001),
   2346         .awaitable = .null,
   2347     }, .monotonic).cancelation) {
   2348         .canceled => {},
   2349         .none => unreachable, // called `recancel` but was not canceled
   2350         .canceling => unreachable, // called `recancel` but cancelation was already pending
   2351         .parked => unreachable,
   2352         .blocked => unreachable,
   2353         .blocked_alertable => unreachable,
   2354         .blocked_alertable_canceling => unreachable,
   2355         .blocked_canceling => unreachable,
   2356     }
   2357 }
   2358 
   2359 fn swapCancelProtection(userdata: ?*anyopaque, new: Io.CancelProtection) Io.CancelProtection {
   2360     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2361     _ = t;
   2362     const thread = Thread.current orelse return .unblocked;
   2363     const old = thread.cancel_protection;
   2364     thread.cancel_protection = new;
   2365     return old;
   2366 }
   2367 
   2368 fn checkCancel(userdata: ?*anyopaque) Io.Cancelable!void {
   2369     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2370     _ = t;
   2371     return Thread.checkCancel();
   2372 }
   2373 
   2374 fn await(
   2375     userdata: ?*anyopaque,
   2376     any_future: *Io.AnyFuture,
   2377     result: []u8,
   2378     result_alignment: Alignment,
   2379 ) void {
   2380     _ = result_alignment;
   2381     if (builtin.single_threaded) unreachable; // nothing to await
   2382     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2383     const future: *Future = @ptrCast(@alignCast(any_future));
   2384 
   2385     var num_completed: std.atomic.Value(u32) = .init(0);
   2386     future.awaiter = &num_completed;
   2387 
   2388     const pre_await_status = future.status.fetchOr(.{
   2389         .tag = .pending_awaited,
   2390         .thread = .null,
   2391     }, .acq_rel); // acquire results if complete; release `future.awaiter`
   2392     switch (pre_await_status.tag) {
   2393         .pending => while (Thread.futexWait(&num_completed.raw, 0, null)) {
   2394             switch (num_completed.load(.acquire)) { // acquire task results
   2395                 0 => continue,
   2396                 1 => break,
   2397                 else => unreachable, // group was reused before `await` returned
   2398             }
   2399         } else |err| switch (err) {
   2400             error.Canceled => {
   2401                 const pre_cancel_status = future.status.fetchOr(.{
   2402                     .tag = .pending_canceled,
   2403                     .thread = .null,
   2404                 }, .acq_rel); // acquire results if complete; release `future.awaiter`
   2405                 const done_status = switch (pre_cancel_status.tag) {
   2406                     .pending => unreachable, // invalid state: we already awaited
   2407                     .pending_awaited => done_status: {
   2408                         const working_thread = pre_cancel_status.thread.unpack();
   2409                         future.waitForCancelWithSignaling(t, &num_completed, @alignCast(working_thread));
   2410                         break :done_status future.status.load(.monotonic);
   2411                     },
   2412                     .pending_canceled => unreachable, // `await` raced with `cancel`
   2413                     .done => done_status: {
   2414                         // The task just finished, but we still need to wait for the signal, because the
   2415                         // task thread already figured out that they need to update `future.awaiter`.
   2416                         future.waitForCancelWithSignaling(t, &num_completed, null);
   2417                         // Also, we have clobbered `future.status.tag` to `.pending_canceled`, but that's
   2418                         // not actually a problem for the logic below.
   2419                         break :done_status pre_cancel_status;
   2420                     },
   2421                 };
   2422                 // If the future did not acknowledge the cancelation, we need to mark it outstanding
   2423                 // for us. Because `done_status.tag == .done`, the information about whether there
   2424                 // was an acknowledged cancelation is encoded in `done_status.thread`.
   2425                 assert(done_status.tag == .done);
   2426                 switch (done_status.thread) {
   2427                     .null => recancelInner(), // cancelation was not acknowledged, so it's ours
   2428                     .all_ones => {}, // cancelation was acknowledged, so it was this task's job to propagate it
   2429                     _ => unreachable,
   2430                 }
   2431             },
   2432         },
   2433         .pending_awaited => unreachable, // `await` raced with `await`
   2434         .pending_canceled => unreachable, // `await` raced with `cancel`
   2435         .done => {},
   2436     }
   2437     @memcpy(result, future.resultPointer());
   2438     future.destroy(t.allocator);
   2439 }
   2440 
   2441 fn cancel(
   2442     userdata: ?*anyopaque,
   2443     any_future: *Io.AnyFuture,
   2444     result: []u8,
   2445     result_alignment: Alignment,
   2446 ) void {
   2447     _ = result_alignment;
   2448     if (builtin.single_threaded) unreachable; // nothing to cancel
   2449     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2450     const future: *Future = @ptrCast(@alignCast(any_future));
   2451 
   2452     var num_completed: std.atomic.Value(u32) = .init(0);
   2453     future.awaiter = &num_completed;
   2454 
   2455     const pre_cancel_status = future.status.fetchOr(.{
   2456         .tag = .pending_canceled,
   2457         .thread = .null,
   2458     }, .acq_rel); // acquire results if complete; release `future.awaiter`
   2459     switch (pre_cancel_status.tag) {
   2460         .pending => {
   2461             const working_thread = pre_cancel_status.thread.unpack();
   2462             future.waitForCancelWithSignaling(t, &num_completed, @alignCast(working_thread));
   2463         },
   2464         .pending_awaited => unreachable, // `await` raced with `await`
   2465         .pending_canceled => unreachable, // `await` raced with `cancel`
   2466         .done => {},
   2467     }
   2468     @memcpy(result, future.resultPointer());
   2469     future.destroy(t.allocator);
   2470 }
   2471 
   2472 fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: Io.Timeout) Io.Cancelable!void {
   2473     if (builtin.single_threaded) {
   2474         assert(timeout != .none); // Deadlock.
   2475         return;
   2476     }
   2477     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2478     const t_io = io(t);
   2479     const timeout_ns: ?u64 = ns: {
   2480         const d = timeout.toDurationFromNow(t_io) orelse break :ns null;
   2481         break :ns std.math.lossyCast(u64, d.raw.toNanoseconds());
   2482     };
   2483     return Thread.futexWait(ptr, expected, timeout_ns);
   2484 }
   2485 
   2486 fn futexWaitUncancelable(userdata: ?*anyopaque, ptr: *const u32, expected: u32) void {
   2487     if (builtin.single_threaded) unreachable; // Deadlock.
   2488     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2489     _ = t;
   2490     Thread.futexWaitUncancelable(ptr, expected, null);
   2491 }
   2492 
   2493 fn futexWake(userdata: ?*anyopaque, ptr: *const u32, max_waiters: u32) void {
   2494     if (builtin.single_threaded) return; // Nothing to wake up.
   2495     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2496     _ = t;
   2497     Thread.futexWake(ptr, max_waiters);
   2498 }
   2499 
   2500 fn operate(userdata: ?*anyopaque, operation: Io.Operation) Io.Cancelable!Io.Operation.Result {
   2501     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2502     switch (operation) {
   2503         .file_read_streaming => |o| return .{
   2504             .file_read_streaming = fileReadStreaming(t, o.file, o.data) catch |err| switch (err) {
   2505                 error.Canceled => |e| return e,
   2506                 else => |e| e,
   2507             },
   2508         },
   2509         .file_write_streaming => |o| return .{
   2510             .file_write_streaming = fileWriteStreaming(t, o.file, o.header, o.data, o.splat) catch |err| switch (err) {
   2511                 error.Canceled => |e| return e,
   2512                 else => |e| e,
   2513             },
   2514         },
   2515         .device_io_control => |*o| return .{ .device_io_control = try deviceIoControl(o) },
   2516         .net_receive => |*o| return .{ .net_receive = o: {
   2517             if (!have_networking) break :o .{ error.NetworkDown, 0 };
   2518             if (is_windows) break :o netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags);
   2519             netReceivePosix(o.socket_handle, &o.message_buffer[0], o.data_buffer, o.flags, false) catch |err| switch (err) {
   2520                 error.Canceled => |e| return e,
   2521                 error.WouldBlock => unreachable,
   2522                 else => |e| break :o .{ e, 0 },
   2523             };
   2524             break :o .{ null, 1 };
   2525         } },
   2526     }
   2527 }
   2528 
   2529 fn batchAwaitAsync(userdata: ?*anyopaque, b: *Io.Batch) Io.Cancelable!void {
   2530     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2531     if (is_windows) {
   2532         batchDrainSubmittedWindows(t, b, false) catch |err| switch (err) {
   2533             error.ConcurrencyUnavailable => unreachable, // passed concurrency=false
   2534             else => |e| return e,
   2535         };
   2536         const alertable_syscall = try AlertableSyscall.start();
   2537         while (b.pending.head != .none and b.completed.head == .none) waitForApcOrAlert();
   2538         alertable_syscall.finish();
   2539         return;
   2540     }
   2541     if (have_poll) {
   2542         var poll_buffer: [poll_buffer_len]posix.pollfd = undefined;
   2543         var poll_len: u32 = 0;
   2544         {
   2545             var index = b.submitted.head;
   2546             while (index != .none and poll_len < poll_buffer_len) {
   2547                 const submission = &b.storage[index.toIndex()].submission;
   2548                 switch (submission.operation) {
   2549                     .file_read_streaming => |o| {
   2550                         poll_buffer[poll_len] = .{
   2551                             .fd = o.file.handle,
   2552                             .events = posix.POLL.IN | posix.POLL.ERR,
   2553                             .revents = 0,
   2554                         };
   2555                         poll_len += 1;
   2556                     },
   2557                     .file_write_streaming => |o| {
   2558                         poll_buffer[poll_len] = .{
   2559                             .fd = o.file.handle,
   2560                             .events = posix.POLL.OUT | posix.POLL.ERR,
   2561                             .revents = 0,
   2562                         };
   2563                         poll_len += 1;
   2564                     },
   2565                     .device_io_control => |o| {
   2566                         poll_buffer[poll_len] = .{
   2567                             .fd = o.file.handle,
   2568                             .events = posix.POLL.OUT | posix.POLL.IN | posix.POLL.ERR,
   2569                             .revents = 0,
   2570                         };
   2571                         poll_len += 1;
   2572                     },
   2573                     .net_receive => |*o| {
   2574                         poll_buffer[poll_len] = .{
   2575                             .fd = o.socket_handle,
   2576                             .events = posix.POLL.IN | posix.POLL.ERR,
   2577                             .revents = 0,
   2578                         };
   2579                         poll_len += 1;
   2580                     },
   2581                 }
   2582                 index = submission.node.next;
   2583             }
   2584         }
   2585         switch (poll_len) {
   2586             0 => return,
   2587             1 => {},
   2588             else => while (true) {
   2589                 const timeout_ms: i32 = t: {
   2590                     if (b.completed.head != .none) {
   2591                         // It is legal to call batchWait with already completed
   2592                         // operations in the ring. In such case, we need to avoid
   2593                         // blocking in the poll syscall, but we can still take this
   2594                         // opportunity to find additional ready operations.
   2595                         break :t 0;
   2596                     }
   2597                     break :t std.math.maxInt(i32);
   2598                 };
   2599                 const syscall = try Syscall.start();
   2600                 const rc = posix.system.poll(&poll_buffer, poll_len, timeout_ms);
   2601                 syscall.finish();
   2602                 switch (posix.errno(rc)) {
   2603                     .SUCCESS => {
   2604                         if (rc == 0) {
   2605                             if (b.completed.head != .none) {
   2606                                 // Since there are already completions available in the
   2607                                 // queue, this is neither a timeout nor a case for
   2608                                 // retrying.
   2609                                 return;
   2610                             }
   2611                             continue;
   2612                         }
   2613                         var prev_index: Io.Operation.OptionalIndex = .none;
   2614                         var index = b.submitted.head;
   2615                         for (poll_buffer[0..poll_len]) |poll_entry| {
   2616                             const storage = &b.storage[index.toIndex()];
   2617                             const submission = &storage.submission;
   2618                             const next_index = submission.node.next;
   2619                             if (poll_entry.revents != 0) {
   2620                                 const result = try operate(t, submission.operation);
   2621 
   2622                                 switch (prev_index) {
   2623                                     .none => b.submitted.head = next_index,
   2624                                     else => b.storage[prev_index.toIndex()].submission.node.next = next_index,
   2625                                 }
   2626                                 if (next_index == .none) b.submitted.tail = prev_index;
   2627 
   2628                                 switch (b.completed.tail) {
   2629                                     .none => b.completed.head = index,
   2630                                     else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
   2631                                 }
   2632                                 storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
   2633                                 b.completed.tail = index;
   2634                             } else prev_index = index;
   2635                             index = next_index;
   2636                         }
   2637                         assert(index == .none);
   2638                         return;
   2639                     },
   2640                     .INTR => continue,
   2641                     else => break,
   2642                 }
   2643             },
   2644         }
   2645     }
   2646 
   2647     var tail_index = b.completed.tail;
   2648     defer b.completed.tail = tail_index;
   2649     var index = b.submitted.head;
   2650     errdefer b.submitted.head = index;
   2651     while (index != .none) {
   2652         const storage = &b.storage[index.toIndex()];
   2653         const submission = &storage.submission;
   2654         const next_index = submission.node.next;
   2655         const result = try operate(t, submission.operation);
   2656 
   2657         switch (tail_index) {
   2658             .none => b.completed.head = index,
   2659             else => b.storage[tail_index.toIndex()].completion.node.next = index,
   2660         }
   2661         storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
   2662         tail_index = index;
   2663         index = next_index;
   2664     }
   2665     b.submitted = .{ .head = .none, .tail = .none };
   2666 }
   2667 
   2668 fn batchAwaitConcurrent(userdata: ?*anyopaque, b: *Io.Batch, timeout: Io.Timeout) Io.Batch.AwaitConcurrentError!void {
   2669     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2670     if (is_windows) {
   2671         const deadline: ?Io.Clock.Timestamp = timeout.toTimestamp(io(t));
   2672         try batchDrainSubmittedWindows(t, b, true);
   2673         while (b.pending.head != .none and b.completed.head == .none) {
   2674             var delay_interval: windows.LARGE_INTEGER = interval: {
   2675                 const d = deadline orelse break :interval std.math.minInt(windows.LARGE_INTEGER);
   2676                 break :interval timeoutToWindowsInterval(.{ .deadline = d }).?;
   2677             };
   2678             const alertable_syscall = try AlertableSyscall.start();
   2679             const delay_rc = windows.ntdll.NtDelayExecution(windows.TRUE, &delay_interval);
   2680             alertable_syscall.finish();
   2681             switch (delay_rc) {
   2682                 .SUCCESS, .TIMEOUT => {
   2683                     // The thread woke due to the timeout. Although spurious
   2684                     // timeouts are OK, when no deadline is passed we must not
   2685                     // return `error.Timeout`.
   2686                     if (timeout != .none and b.completed.head == .none) return error.Timeout;
   2687                 },
   2688                 else => {},
   2689             }
   2690         }
   2691         return;
   2692     }
   2693     if (native_os == .wasi) {
   2694         // TODO call poll_oneoff
   2695         return error.ConcurrencyUnavailable;
   2696     }
   2697     if (!have_poll) return error.ConcurrencyUnavailable;
   2698     var poll_buffer: [poll_buffer_len]posix.pollfd = undefined;
   2699     var poll_storage: struct {
   2700         gpa: Allocator,
   2701         batch: *Io.Batch,
   2702         slice: []posix.pollfd,
   2703         len: u32,
   2704 
   2705         fn add(storage: *@This(), fd: File.Handle, events: @FieldType(posix.pollfd, "events")) Io.ConcurrentError!void {
   2706             const len = storage.len;
   2707             if (len == poll_buffer_len) {
   2708                 const slice: []posix.pollfd = if (storage.batch.userdata) |batch_userdata|
   2709                     @as([*]posix.pollfd, @ptrCast(@alignCast(batch_userdata)))[0..storage.batch.storage.len]
   2710                 else allocation: {
   2711                     const allocation = storage.gpa.alloc(posix.pollfd, storage.batch.storage.len) catch
   2712                         return error.ConcurrencyUnavailable;
   2713                     storage.batch.userdata = allocation.ptr;
   2714                     break :allocation allocation;
   2715                 };
   2716                 @memcpy(slice[0..poll_buffer_len], storage.slice);
   2717                 storage.slice = slice;
   2718             }
   2719             storage.slice[len] = .{
   2720                 .fd = fd,
   2721                 .events = events,
   2722                 .revents = 0,
   2723             };
   2724             storage.len = len + 1;
   2725         }
   2726     } = .{ .gpa = t.allocator, .batch = b, .slice = &poll_buffer, .len = 0 };
   2727     {
   2728         var index = b.submitted.head;
   2729         while (index != .none) {
   2730             const storage = &b.storage[index.toIndex()];
   2731             const submission = storage.submission;
   2732             switch (submission.operation) {
   2733                 .file_read_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.ERR),
   2734                 .file_write_streaming => |o| try poll_storage.add(o.file.handle, posix.POLL.OUT | posix.POLL.ERR),
   2735                 .device_io_control => |o| try poll_storage.add(o.file.handle, posix.POLL.IN | posix.POLL.OUT | posix.POLL.ERR),
   2736                 .net_receive => |*o| nb: {
   2737                     var data_i: usize = 0;
   2738                     const result: Io.Operation.Result = .{ .net_receive = for (o.message_buffer, 0..) |*msg, msg_i| {
   2739                         const remaining_data_buffer = o.data_buffer[data_i..];
   2740                         netReceivePosix(o.socket_handle, msg, remaining_data_buffer, o.flags, true) catch |err| switch (err) {
   2741                             error.Canceled => |e| return e,
   2742                             error.WouldBlock => {
   2743                                 if (msg_i != 0) break .{ null, msg_i };
   2744                                 try poll_storage.add(o.socket_handle, posix.POLL.IN | posix.POLL.ERR);
   2745                                 break :nb;
   2746                             },
   2747                             else => |e| break .{ e, 0 },
   2748                         };
   2749                         data_i += msg.data.len;
   2750                     } else .{ null, o.message_buffer.len } };
   2751                     switch (b.completed.tail) {
   2752                         .none => b.completed.head = index,
   2753                         else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
   2754                     }
   2755                     storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
   2756                     b.completed.tail = index;
   2757                 },
   2758             }
   2759             index = submission.node.next;
   2760         }
   2761     }
   2762     switch (poll_storage.len) {
   2763         0 => return,
   2764         1 => if (timeout == .none and b.completed.head == .none) {
   2765             const index = b.submitted.head;
   2766             const storage = &b.storage[index.toIndex()];
   2767             const result = try operate(t, storage.submission.operation);
   2768 
   2769             b.submitted = .{ .head = .none, .tail = .none };
   2770 
   2771             switch (b.completed.tail) {
   2772                 .none => b.completed.head = index,
   2773                 else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
   2774             }
   2775             storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
   2776             b.completed.tail = index;
   2777             return;
   2778         },
   2779         else => {},
   2780     }
   2781     const t_io = io(t);
   2782     const deadline = timeout.toTimestamp(t_io);
   2783     while (true) {
   2784         const timeout_ms: i32 = t: {
   2785             if (b.completed.head != .none) {
   2786                 // It is legal to call batchWait with already completed
   2787                 // operations in the ring. In such case, we need to avoid
   2788                 // blocking in the poll syscall, but we can still take this
   2789                 // opportunity to find additional ready operations.
   2790                 break :t 0;
   2791             }
   2792             const d = deadline orelse break :t -1;
   2793             const duration = d.durationFromNow(t_io);
   2794             break :t @min(@max(0, duration.raw.toMilliseconds()), std.math.maxInt(i32));
   2795         };
   2796         const syscall = try Syscall.start();
   2797         const rc = posix.system.poll(poll_storage.slice.ptr, poll_storage.len, timeout_ms);
   2798         syscall.finish();
   2799         switch (posix.errno(rc)) {
   2800             .SUCCESS => {
   2801                 if (rc == 0) {
   2802                     if (b.completed.head != .none) {
   2803                         // Since there are already completions available in the
   2804                         // queue, this is neither a timeout nor a case for
   2805                         // retrying.
   2806                         return;
   2807                     }
   2808                     // Although spurious timeouts are OK, when no deadline is
   2809                     // passed we must not return `error.Timeout`.
   2810                     if (deadline == null) continue;
   2811                     return error.Timeout;
   2812                 }
   2813                 var prev_index: Io.Operation.OptionalIndex = .none;
   2814                 var index = b.submitted.head;
   2815                 for (poll_storage.slice[0..poll_storage.len]) |poll_entry| {
   2816                     const submission = &b.storage[index.toIndex()].submission;
   2817                     const next_index = submission.node.next;
   2818                     if (poll_entry.revents != 0) {
   2819                         const result = try operate(t, submission.operation);
   2820 
   2821                         switch (prev_index) {
   2822                             .none => b.submitted.head = next_index,
   2823                             else => b.storage[prev_index.toIndex()].submission.node.next = next_index,
   2824                         }
   2825                         if (next_index == .none) b.submitted.tail = prev_index;
   2826 
   2827                         switch (b.completed.tail) {
   2828                             .none => b.completed.head = index,
   2829                             else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
   2830                         }
   2831                         b.completed.tail = index;
   2832                         b.storage[index.toIndex()] = .{ .completion = .{
   2833                             .node = .{ .next = .none },
   2834                             .result = result,
   2835                         } };
   2836                     } else prev_index = index;
   2837                     index = next_index;
   2838                 }
   2839                 assert(index == .none);
   2840                 return;
   2841             },
   2842             .INTR => continue,
   2843             else => return error.ConcurrencyUnavailable,
   2844         }
   2845     }
   2846 }
   2847 
   2848 const WindowsBatchOperationUserdata = extern struct {
   2849     file: windows.HANDLE,
   2850     iosb: windows.IO_STATUS_BLOCK,
   2851 
   2852     const Erased = Io.Operation.Storage.Pending.Userdata;
   2853 
   2854     comptime {
   2855         assert(@sizeOf(WindowsBatchOperationUserdata) <= @sizeOf(Erased));
   2856     }
   2857 
   2858     fn toErased(userdata: *WindowsBatchOperationUserdata) *Erased {
   2859         return @ptrCast(userdata);
   2860     }
   2861 
   2862     fn fromErased(erased: *Erased) *WindowsBatchOperationUserdata {
   2863         return @ptrCast(erased);
   2864     }
   2865 };
   2866 
   2867 fn batchCancel(userdata: ?*anyopaque, b: *Io.Batch) void {
   2868     const t: *Threaded = @ptrCast(@alignCast(userdata));
   2869     if (is_windows) {
   2870         if (b.pending.head == .none) return;
   2871         waitForApcOrAlert();
   2872         var index = b.pending.head;
   2873         while (index != .none) {
   2874             const pending = &b.storage[index.toIndex()].pending;
   2875             const operation_userdata: *WindowsBatchOperationUserdata = .fromErased(&pending.userdata);
   2876             var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
   2877             _ = windows.ntdll.NtCancelIoFileEx(operation_userdata.file, &operation_userdata.iosb, &cancel_iosb);
   2878             index = pending.node.next;
   2879         }
   2880         while (b.pending.head != .none) waitForApcOrAlert();
   2881     } else if (b.userdata) |batch_userdata| {
   2882         const poll_storage: [*]posix.pollfd = @ptrCast(@alignCast(batch_userdata));
   2883         t.allocator.free(poll_storage[0..b.storage.len]);
   2884         b.userdata = null;
   2885     }
   2886 }
   2887 
   2888 fn batchCompleteBlockingWindows(
   2889     b: *Io.Batch,
   2890     operation_userdata: *WindowsBatchOperationUserdata,
   2891     result: Io.Operation.Result,
   2892 ) void {
   2893     const erased_userdata = operation_userdata.toErased();
   2894     const pending: *Io.Operation.Storage.Pending = @fieldParentPtr("userdata", erased_userdata);
   2895     switch (pending.node.prev) {
   2896         .none => b.pending.head = pending.node.next,
   2897         else => |prev_index| b.storage[prev_index.toIndex()].pending.node.next = pending.node.next,
   2898     }
   2899     switch (pending.node.next) {
   2900         .none => b.pending.tail = pending.node.prev,
   2901         else => |next_index| b.storage[next_index.toIndex()].pending.node.prev = pending.node.prev,
   2902     }
   2903     const storage: *Io.Operation.Storage = @fieldParentPtr("pending", pending);
   2904     const index: Io.Operation.OptionalIndex = .fromIndex(storage - b.storage.ptr);
   2905     switch (b.completed.tail) {
   2906         .none => b.completed.head = index,
   2907         else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
   2908     }
   2909     b.completed.tail = index;
   2910     storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
   2911 }
   2912 
   2913 fn batchApc(
   2914     apc_context: ?*anyopaque,
   2915     iosb: *windows.IO_STATUS_BLOCK,
   2916     _: windows.ULONG,
   2917 ) callconv(.winapi) void {
   2918     const b: *Io.Batch = @ptrCast(@alignCast(apc_context));
   2919     const operation_userdata: *WindowsBatchOperationUserdata = @fieldParentPtr("iosb", iosb);
   2920     const erased_userdata = operation_userdata.toErased();
   2921     const pending: *Io.Operation.Storage.Pending = @fieldParentPtr("userdata", erased_userdata);
   2922     switch (pending.node.prev) {
   2923         .none => b.pending.head = pending.node.next,
   2924         else => |prev_index| b.storage[prev_index.toIndex()].pending.node.next = pending.node.next,
   2925     }
   2926     switch (pending.node.next) {
   2927         .none => b.pending.tail = pending.node.prev,
   2928         else => |next_index| b.storage[next_index.toIndex()].pending.node.prev = pending.node.prev,
   2929     }
   2930     const storage: *Io.Operation.Storage = @fieldParentPtr("pending", pending);
   2931     const index: Io.Operation.OptionalIndex = .fromIndex(storage - b.storage.ptr);
   2932     switch (iosb.u.Status) {
   2933         .CANCELLED => {
   2934             const tail_index = b.unused.tail;
   2935             switch (tail_index) {
   2936                 .none => b.unused.head = index,
   2937                 else => b.storage[tail_index.toIndex()].unused.next = index,
   2938             }
   2939             storage.* = .{ .unused = .{ .prev = tail_index, .next = .none } };
   2940             b.unused.tail = index;
   2941         },
   2942         else => {
   2943             switch (b.completed.tail) {
   2944                 .none => b.completed.head = index,
   2945                 else => |tail_index| b.storage[tail_index.toIndex()].completion.node.next = index,
   2946             }
   2947             b.completed.tail = index;
   2948             const result: Io.Operation.Result = switch (pending.tag) {
   2949                 .file_read_streaming => .{ .file_read_streaming = ntReadFileResult(iosb) },
   2950                 .file_write_streaming => .{ .file_write_streaming = ntWriteFileResult(iosb) },
   2951                 .device_io_control => .{ .device_io_control = iosb.* },
   2952                 .net_receive => unreachable,
   2953             };
   2954             storage.* = .{ .completion = .{ .node = .{ .next = .none }, .result = result } };
   2955         },
   2956     }
   2957 }
   2958 
   2959 /// If `concurrency` is false, `error.ConcurrencyUnavailable` is unreachable.
   2960 fn batchDrainSubmittedWindows(t: *Threaded, b: *Io.Batch, concurrency: bool) (Io.ConcurrentError || Io.Cancelable)!void {
   2961     var index = b.submitted.head;
   2962     errdefer b.submitted.head = index;
   2963     while (index != .none) {
   2964         const storage = &b.storage[index.toIndex()];
   2965         const submission = storage.submission;
   2966         storage.* = .{ .pending = .{
   2967             .node = .{ .prev = b.pending.tail, .next = .none },
   2968             .tag = submission.operation,
   2969             .userdata = undefined,
   2970         } };
   2971         switch (b.pending.tail) {
   2972             .none => b.pending.head = index,
   2973             else => |tail_index| b.storage[tail_index.toIndex()].pending.node.next = index,
   2974         }
   2975         b.pending.tail = index;
   2976         const operation_userdata: *WindowsBatchOperationUserdata = .fromErased(&storage.pending.userdata);
   2977         errdefer {
   2978             operation_userdata.iosb = .{ .u = .{ .Status = .CANCELLED }, .Information = undefined };
   2979             batchApc(b, &operation_userdata.iosb, 0);
   2980         }
   2981         switch (submission.operation) {
   2982             .file_read_streaming => |o| o: {
   2983                 var data_index: usize = 0;
   2984                 while (o.data.len - data_index != 0 and o.data[data_index].len == 0) data_index += 1;
   2985                 if (o.data.len - data_index == 0) {
   2986                     operation_userdata.iosb = .{ .u = .{ .Status = .SUCCESS }, .Information = 0 };
   2987                     batchApc(b, &operation_userdata.iosb, 0);
   2988                     break :o;
   2989                 }
   2990                 const buffer = o.data[data_index];
   2991                 const short_buffer_len = std.math.lossyCast(u32, buffer.len);
   2992 
   2993                 if (o.file.flags.nonblocking) {
   2994                     operation_userdata.file = o.file.handle;
   2995                     switch (windows.ntdll.NtReadFile(
   2996                         o.file.handle,
   2997                         null, // event
   2998                         &batchApc,
   2999                         b,
   3000                         &operation_userdata.iosb,
   3001                         buffer.ptr,
   3002                         short_buffer_len,
   3003                         null, // byte offset
   3004                         null, // key
   3005                     )) {
   3006                         .PENDING, .SUCCESS => {},
   3007                         .CANCELLED => unreachable,
   3008                         else => |status| {
   3009                             operation_userdata.iosb.u.Status = status;
   3010                             batchApc(b, &operation_userdata.iosb, 0);
   3011                         },
   3012                     }
   3013                 } else {
   3014                     if (concurrency) return error.ConcurrencyUnavailable;
   3015 
   3016                     const syscall: Syscall = try .start();
   3017                     while (true) switch (windows.ntdll.NtReadFile(
   3018                         o.file.handle,
   3019                         null, // event
   3020                         null, // APC routine
   3021                         null, // APC context
   3022                         &operation_userdata.iosb,
   3023                         buffer.ptr,
   3024                         short_buffer_len,
   3025                         null, // byte offset
   3026                         null, // key
   3027                     )) {
   3028                         .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
   3029                         .CANCELLED => {
   3030                             try syscall.checkCancel();
   3031                             continue;
   3032                         },
   3033                         else => |status| {
   3034                             syscall.finish();
   3035                             operation_userdata.iosb.u.Status = status;
   3036                             batchApc(b, &operation_userdata.iosb, 0);
   3037                             break;
   3038                         },
   3039                     };
   3040                 }
   3041             },
   3042             .file_write_streaming => |o| o: {
   3043                 const buffer = windowsWriteBuffer(o.header, o.data, o.splat);
   3044                 if (buffer.len == 0) {
   3045                     operation_userdata.iosb = .{ .u = .{ .Status = .SUCCESS }, .Information = 0 };
   3046                     batchApc(b, &operation_userdata.iosb, 0);
   3047                     break :o;
   3048                 }
   3049                 if (o.file.flags.nonblocking) {
   3050                     operation_userdata.file = o.file.handle;
   3051                     switch (windows.ntdll.NtWriteFile(
   3052                         o.file.handle,
   3053                         null, // event
   3054                         &batchApc,
   3055                         b,
   3056                         &operation_userdata.iosb,
   3057                         buffer.ptr,
   3058                         @intCast(buffer.len),
   3059                         null, // byte offset
   3060                         null, // key
   3061                     )) {
   3062                         .PENDING, .SUCCESS => {},
   3063                         .CANCELLED => unreachable,
   3064                         else => |status| {
   3065                             operation_userdata.iosb.u.Status = status;
   3066                             batchApc(b, &operation_userdata.iosb, 0);
   3067                         },
   3068                     }
   3069                 } else {
   3070                     if (concurrency) return error.ConcurrencyUnavailable;
   3071 
   3072                     const syscall: Syscall = try .start();
   3073                     while (true) switch (windows.ntdll.NtWriteFile(
   3074                         o.file.handle,
   3075                         null, // event
   3076                         null, // APC routine
   3077                         null, // APC context
   3078                         &operation_userdata.iosb,
   3079                         buffer.ptr,
   3080                         @intCast(buffer.len),
   3081                         null, // byte offset
   3082                         null, // key
   3083                     )) {
   3084                         .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
   3085                         .CANCELLED => {
   3086                             try syscall.checkCancel();
   3087                             continue;
   3088                         },
   3089                         else => |status| {
   3090                             syscall.finish();
   3091                             operation_userdata.iosb.u.Status = status;
   3092                             batchApc(b, &operation_userdata.iosb, 0);
   3093                             break;
   3094                         },
   3095                     };
   3096                 }
   3097             },
   3098             .device_io_control => |o| {
   3099                 const NtControlFile = switch (o.code.DeviceType) {
   3100                     .FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile,
   3101                     else => &windows.ntdll.NtDeviceIoControlFile,
   3102                 };
   3103                 if (o.file.flags.nonblocking) {
   3104                     operation_userdata.file = o.file.handle;
   3105                     switch (NtControlFile(
   3106                         o.file.handle,
   3107                         null, // event
   3108                         &batchApc,
   3109                         b,
   3110                         &operation_userdata.iosb,
   3111                         o.code,
   3112                         if (o.in.len > 0) o.in.ptr else null,
   3113                         @intCast(o.in.len),
   3114                         if (o.out.len > 0) o.out.ptr else null,
   3115                         @intCast(o.out.len),
   3116                     )) {
   3117                         .PENDING, .SUCCESS => {},
   3118                         .CANCELLED => unreachable,
   3119                         else => |status| {
   3120                             operation_userdata.iosb.u.Status = status;
   3121                             batchApc(b, &operation_userdata.iosb, 0);
   3122                         },
   3123                     }
   3124                 } else {
   3125                     if (concurrency) return error.ConcurrencyUnavailable;
   3126 
   3127                     const syscall: Syscall = try .start();
   3128                     while (true) switch (NtControlFile(
   3129                         o.file.handle,
   3130                         null, // event
   3131                         null, // APC routine
   3132                         null, // APC context
   3133                         &operation_userdata.iosb,
   3134                         o.code,
   3135                         if (o.in.len > 0) o.in.ptr else null,
   3136                         @intCast(o.in.len),
   3137                         if (o.out.len > 0) o.out.ptr else null,
   3138                         @intCast(o.out.len),
   3139                     )) {
   3140                         .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
   3141                         .CANCELLED => {
   3142                             try syscall.checkCancel();
   3143                             continue;
   3144                         },
   3145                         else => |status| {
   3146                             syscall.finish();
   3147                             operation_userdata.iosb.u.Status = status;
   3148                             batchApc(b, &operation_userdata.iosb, 0);
   3149                             break;
   3150                         },
   3151                     };
   3152                 }
   3153             },
   3154             .net_receive => |*o| {
   3155                 // TODO integrate with overlapped I/O or equivalent to avoid this error
   3156                 if (concurrency) return error.ConcurrencyUnavailable;
   3157                 batchCompleteBlockingWindows(b, operation_userdata, .{
   3158                     .net_receive = netReceiveWindows(t, o.socket_handle, o.message_buffer, o.data_buffer, o.flags),
   3159                 });
   3160             },
   3161         }
   3162         index = submission.node.next;
   3163     }
   3164     b.submitted = .{ .head = .none, .tail = .none };
   3165 }
   3166 
   3167 /// Since Windows only supports writing one contiguous buffer, returns the
   3168 /// first one, while also limiting it to a length representable by 32-bit
   3169 /// unsigned integer.
   3170 fn windowsWriteBuffer(header: []const u8, data: []const []const u8, splat: usize) []const u8 {
   3171     const buffer = b: {
   3172         if (header.len != 0) break :b header;
   3173         for (data[0 .. data.len - 1]) |buffer| {
   3174             if (buffer.len != 0) break :b buffer;
   3175         }
   3176         if (splat == 0) return &.{};
   3177         break :b data[data.len - 1];
   3178     };
   3179     return buffer[0..std.math.lossyCast(u32, buffer.len)];
   3180 }
   3181 
   3182 fn submitComplete(ring: []u32, complete_tail: *Io.Batch.RingIndex, op: u32) void {
   3183     const ct = complete_tail.*;
   3184     const len: u31 = @intCast(ring.len);
   3185     ring[ct.index(len)] = op;
   3186     complete_tail.* = ct.next(len);
   3187 }
   3188 
   3189 const dirCreateDir = switch (native_os) {
   3190     .windows => dirCreateDirWindows,
   3191     .wasi => dirCreateDirWasi,
   3192     else => dirCreateDirPosix,
   3193 };
   3194 
   3195 fn dirCreateDirPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
   3196     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3197     _ = t;
   3198 
   3199     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   3200     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   3201 
   3202     const syscall: Syscall = try .start();
   3203     while (true) {
   3204         switch (posix.errno(posix.system.mkdirat(dir.handle, sub_path_posix, permissions.toMode()))) {
   3205             .SUCCESS => {
   3206                 syscall.finish();
   3207                 return;
   3208             },
   3209             .INTR => {
   3210                 try syscall.checkCancel();
   3211                 continue;
   3212             },
   3213             .ACCES => return syscall.fail(error.AccessDenied),
   3214             .PERM => return syscall.fail(error.PermissionDenied),
   3215             .DQUOT => return syscall.fail(error.DiskQuota),
   3216             .EXIST => return syscall.fail(error.PathAlreadyExists),
   3217             .LOOP => return syscall.fail(error.SymLinkLoop),
   3218             .MLINK => return syscall.fail(error.LinkQuotaExceeded),
   3219             .NAMETOOLONG => return syscall.fail(error.NameTooLong),
   3220             .NOENT => return syscall.fail(error.FileNotFound),
   3221             .NOMEM => return syscall.fail(error.SystemResources),
   3222             .NOSPC => return syscall.fail(error.NoSpaceLeft),
   3223             .NOTDIR => return syscall.fail(error.NotDir),
   3224             .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
   3225             // dragonfly: when dir_fd is unlinked from filesystem
   3226             .NOTCONN => return syscall.fail(error.FileNotFound),
   3227             .ILSEQ => return syscall.fail(error.BadPathName),
   3228             .BADF => |err| return syscall.errnoBug(err), // File descriptor used after closed.
   3229             .FAULT => |err| return syscall.errnoBug(err),
   3230             else => |err| return syscall.unexpectedErrno(err),
   3231         }
   3232     }
   3233 }
   3234 
   3235 fn dirCreateDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
   3236     if (builtin.link_libc) return dirCreateDirPosix(userdata, dir, sub_path, permissions);
   3237     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3238     _ = t;
   3239     const syscall: Syscall = try .start();
   3240     while (true) {
   3241         switch (std.os.wasi.path_create_directory(dir.handle, sub_path.ptr, sub_path.len)) {
   3242             .SUCCESS => {
   3243                 syscall.finish();
   3244                 return;
   3245             },
   3246             .INTR => {
   3247                 try syscall.checkCancel();
   3248                 continue;
   3249             },
   3250             else => |e| {
   3251                 syscall.finish();
   3252                 switch (e) {
   3253                     .ACCES => return error.AccessDenied,
   3254                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3255                     .PERM => return error.PermissionDenied,
   3256                     .DQUOT => return error.DiskQuota,
   3257                     .EXIST => return error.PathAlreadyExists,
   3258                     .FAULT => |err| return errnoBug(err),
   3259                     .LOOP => return error.SymLinkLoop,
   3260                     .MLINK => return error.LinkQuotaExceeded,
   3261                     .NAMETOOLONG => return error.NameTooLong,
   3262                     .NOENT => return error.FileNotFound,
   3263                     .NOMEM => return error.SystemResources,
   3264                     .NOSPC => return error.NoSpaceLeft,
   3265                     .NOTDIR => return error.NotDir,
   3266                     .ROFS => return error.ReadOnlyFileSystem,
   3267                     .NOTCAPABLE => return error.AccessDenied,
   3268                     .ILSEQ => return error.BadPathName,
   3269                     else => |err| return posix.unexpectedErrno(err),
   3270                 }
   3271             },
   3272         }
   3273     }
   3274 }
   3275 
   3276 fn dirCreateDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
   3277     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3278     _ = t;
   3279     _ = permissions; // TODO use this value
   3280 
   3281     const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
   3282     const attr: windows.OBJECT.ATTRIBUTES = .{
   3283         .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
   3284         .Attributes = .{ .INHERIT = false },
   3285         .ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path_w.span())),
   3286         .SecurityDescriptor = null,
   3287         .SecurityQualityOfService = null,
   3288     };
   3289 
   3290     var sub_dir_handle: windows.HANDLE = undefined;
   3291     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   3292     var attempt: u5 = 0;
   3293     var syscall: Syscall = try .start();
   3294     while (true) switch (windows.ntdll.NtCreateFile(
   3295         &sub_dir_handle,
   3296         .{
   3297             .GENERIC = .{ .READ = true },
   3298             .STANDARD = .{ .SYNCHRONIZE = true },
   3299         },
   3300         &attr,
   3301         &io_status_block,
   3302         null,
   3303         .{ .NORMAL = true },
   3304         .VALID_FLAGS,
   3305         .CREATE,
   3306         .{
   3307             .DIRECTORY_FILE = true,
   3308             .NON_DIRECTORY_FILE = false,
   3309             .IO = .SYNCHRONOUS_NONALERT,
   3310             .OPEN_REPARSE_POINT = false,
   3311         },
   3312         null,
   3313         0,
   3314     )) {
   3315         .SUCCESS => {
   3316             syscall.finish();
   3317             windows.CloseHandle(sub_dir_handle);
   3318             return;
   3319         },
   3320         .CANCELLED => {
   3321             try syscall.checkCancel();
   3322             continue;
   3323         },
   3324         .SHARING_VIOLATION => {
   3325             // This occurs if the file attempting to be opened is a running
   3326             // executable. However, there's a kernel bug: the error may be
   3327             // incorrectly returned for an indeterminate amount of time
   3328             // after an executable file is closed. Here we work around the
   3329             // kernel bug with retry attempts.
   3330             syscall.finish();
   3331             if (max_windows_kernel_bug_retries - attempt == 0) return error.Unexpected;
   3332             try parking_sleep.sleep(.{ .duration = .{
   3333                 .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
   3334                 .clock = .awake,
   3335             } });
   3336             attempt += 1;
   3337             syscall = try .start();
   3338             continue;
   3339         },
   3340         .DELETE_PENDING => {
   3341             // This error means that there *was* a file in this location on
   3342             // the file system, but it was deleted. However, the OS is not
   3343             // finished with the deletion operation, and so this CreateFile
   3344             // call has failed. There is not really a sane way to handle
   3345             // this other than retrying the creation after the OS finishes
   3346             // the deletion.
   3347             syscall.finish();
   3348             if (max_windows_kernel_bug_retries - attempt == 0) return error.Unexpected;
   3349             try parking_sleep.sleep(.{ .duration = .{
   3350                 .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
   3351                 .clock = .awake,
   3352             } });
   3353             attempt += 1;
   3354             syscall = try .start();
   3355             continue;
   3356         },
   3357         .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
   3358         .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
   3359         .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
   3360         .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
   3361         .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
   3362         .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   3363         .OBJECT_NAME_COLLISION => return syscall.fail(error.PathAlreadyExists),
   3364         .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
   3365         .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
   3366         .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
   3367         .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
   3368         .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
   3369         else => |status| return syscall.unexpectedNtstatus(status),
   3370     };
   3371 }
   3372 
   3373 fn dirCreateDirPath(
   3374     userdata: ?*anyopaque,
   3375     dir: Dir,
   3376     sub_path: []const u8,
   3377     permissions: Dir.Permissions,
   3378 ) Dir.CreateDirPathError!Dir.CreatePathStatus {
   3379     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3380 
   3381     var it = Dir.path.componentIterator(sub_path);
   3382     var status: Dir.CreatePathStatus = .existed;
   3383     var component = it.last() orelse return error.BadPathName;
   3384     while (true) {
   3385         if (dirCreateDir(t, dir, component.path, permissions)) |_| {
   3386             status = .created;
   3387         } else |err| switch (err) {
   3388             error.PathAlreadyExists => {
   3389                 // It is important to return an error if it's not a directory
   3390                 // because otherwise a dangling symlink could cause an infinite
   3391                 // loop.
   3392                 const kind = try filePathKind(t, dir, component.path);
   3393                 if (kind != .directory) return error.NotDir;
   3394             },
   3395             error.FileNotFound => |e| {
   3396                 component = it.previous() orelse return e;
   3397                 continue;
   3398             },
   3399             else => |e| return e,
   3400         }
   3401         component = it.next() orelse return status;
   3402     }
   3403 }
   3404 
   3405 const dirCreateDirPathOpen = switch (native_os) {
   3406     .windows => dirCreateDirPathOpenWindows,
   3407     .wasi => dirCreateDirPathOpenWasi,
   3408     else => dirCreateDirPathOpenPosix,
   3409 };
   3410 
   3411 fn dirCreateDirPathOpenPosix(
   3412     userdata: ?*anyopaque,
   3413     dir: Dir,
   3414     sub_path: []const u8,
   3415     permissions: Dir.Permissions,
   3416     options: Dir.OpenOptions,
   3417 ) Dir.CreateDirPathOpenError!Dir {
   3418     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3419     const t_io = io(t);
   3420     return dirOpenDirPosix(t, dir, sub_path, options) catch |err| switch (err) {
   3421         error.FileNotFound => {
   3422             _ = try dir.createDirPathStatus(t_io, sub_path, permissions);
   3423             return dirOpenDirPosix(t, dir, sub_path, options);
   3424         },
   3425         else => |e| return e,
   3426     };
   3427 }
   3428 
   3429 fn dirCreateDirPathOpenWindows(
   3430     userdata: ?*anyopaque,
   3431     dir: Dir,
   3432     sub_path: []const u8,
   3433     permissions: Dir.Permissions,
   3434     options: Dir.OpenOptions,
   3435 ) Dir.CreateDirPathOpenError!Dir {
   3436     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3437     const w = windows;
   3438 
   3439     _ = permissions; // TODO apply these permissions
   3440 
   3441     var it = Dir.path.componentIterator(sub_path);
   3442     // If there are no components in the path, then create a dummy component with the full path.
   3443     var component: Dir.path.NativeComponentIterator.Component = it.last() orelse .{
   3444         .name = "",
   3445         .path = sub_path,
   3446     };
   3447 
   3448     components: while (true) {
   3449         const sub_path_w = try sliceToPrefixedFileW(dir.handle, component.path);
   3450         const attr: windows.OBJECT.ATTRIBUTES = .{
   3451             .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
   3452             .ObjectName = @constCast(&sub_path_w.string()),
   3453         };
   3454         const is_last = it.peekNext() == null;
   3455         var result: Dir = .{ .handle = undefined };
   3456         var iosb: w.IO_STATUS_BLOCK = undefined;
   3457         const syscall: Syscall = try .start();
   3458         while (true) switch (w.ntdll.NtCreateFile(
   3459             &result.handle,
   3460             .{
   3461                 .SPECIFIC = .{ .FILE_DIRECTORY = .{
   3462                     .LIST = options.iterate,
   3463                     .READ_EA = true,
   3464                     .READ_ATTRIBUTES = true,
   3465                     .TRAVERSE = true,
   3466                 } },
   3467                 .STANDARD = .{
   3468                     .RIGHTS = .READ,
   3469                     .SYNCHRONIZE = true,
   3470                 },
   3471             },
   3472             &attr,
   3473             &iosb,
   3474             null,
   3475             .{ .NORMAL = true },
   3476             .VALID_FLAGS,
   3477             if (is_last) .OPEN_IF else .CREATE,
   3478             .{
   3479                 .DIRECTORY_FILE = true,
   3480                 .IO = .SYNCHRONOUS_NONALERT,
   3481                 .OPEN_FOR_BACKUP_INTENT = true,
   3482                 .OPEN_REPARSE_POINT = !options.follow_symlinks,
   3483             },
   3484             null,
   3485             0,
   3486         )) {
   3487             .SUCCESS => {
   3488                 syscall.finish();
   3489                 component = it.next() orelse return result;
   3490                 w.CloseHandle(result.handle);
   3491                 continue :components;
   3492             },
   3493             .CANCELLED => {
   3494                 try syscall.checkCancel();
   3495                 continue;
   3496             },
   3497             .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
   3498             .OBJECT_NAME_COLLISION => {
   3499                 syscall.finish();
   3500                 assert(!is_last);
   3501                 // stat the file and return an error if it's not a directory
   3502                 // this is important because otherwise a dangling symlink
   3503                 // could cause an infinite loop
   3504                 const fstat = try dirStatFileWindows(t, dir, component.path, .{
   3505                     .follow_symlinks = options.follow_symlinks,
   3506                 });
   3507                 if (fstat.kind != .directory) return error.NotDir;
   3508 
   3509                 component = it.next().?;
   3510                 continue :components;
   3511             },
   3512 
   3513             .OBJECT_NAME_NOT_FOUND,
   3514             .OBJECT_PATH_NOT_FOUND,
   3515             => {
   3516                 syscall.finish();
   3517                 component = it.previous() orelse return error.FileNotFound;
   3518                 continue :components;
   3519             },
   3520 
   3521             .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
   3522             // This can happen if the directory has 'List folder contents' permission set to 'Deny'
   3523             // and the directory is trying to be opened for iteration.
   3524             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   3525             .DISK_FULL => return syscall.fail(error.NoSpaceLeft),
   3526             .INVALID_PARAMETER => |s| return syscall.ntstatusBug(s),
   3527             else => |s| return syscall.unexpectedNtstatus(s),
   3528         };
   3529     }
   3530 }
   3531 
   3532 fn dirCreateDirPathOpenWasi(
   3533     userdata: ?*anyopaque,
   3534     dir: Dir,
   3535     sub_path: []const u8,
   3536     permissions: Dir.Permissions,
   3537     options: Dir.OpenOptions,
   3538 ) Dir.CreateDirPathOpenError!Dir {
   3539     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3540     const t_io = io(t);
   3541     return dirOpenDirWasi(t, dir, sub_path, options) catch |err| switch (err) {
   3542         error.FileNotFound => {
   3543             _ = try dir.createDirPathStatus(t_io, sub_path, permissions);
   3544             return dirOpenDirWasi(t, dir, sub_path, options);
   3545         },
   3546         else => |e| return e,
   3547     };
   3548 }
   3549 
   3550 fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {
   3551     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3552     return fileStat(t, .{
   3553         .handle = dir.handle,
   3554         .flags = .{ .nonblocking = false },
   3555     });
   3556 }
   3557 
   3558 const dirStatFile = switch (native_os) {
   3559     .linux => dirStatFileLinux,
   3560     .windows => dirStatFileWindows,
   3561     .wasi => dirStatFileWasi,
   3562     else => dirStatFilePosix,
   3563 };
   3564 
   3565 fn dirStatFileLinux(
   3566     userdata: ?*anyopaque,
   3567     dir: Dir,
   3568     sub_path: []const u8,
   3569     options: Dir.StatFileOptions,
   3570 ) Dir.StatFileError!File.Stat {
   3571     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3572     _ = t;
   3573     const linux = std.os.linux;
   3574     const sys = if (statx_use_c) std.c else std.os.linux;
   3575 
   3576     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   3577     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   3578 
   3579     const flags: u32 = linux.AT.NO_AUTOMOUNT |
   3580         @as(u32, if (!options.follow_symlinks) linux.AT.SYMLINK_NOFOLLOW else 0);
   3581 
   3582     const syscall: Syscall = try .start();
   3583     while (true) {
   3584         var statx = std.mem.zeroes(linux.Statx);
   3585         switch (sys.errno(sys.statx(dir.handle, sub_path_posix, flags, linux_statx_request, &statx))) {
   3586             .SUCCESS => {
   3587                 syscall.finish();
   3588                 return statFromLinux(&statx);
   3589             },
   3590             .INTR => {
   3591                 try syscall.checkCancel();
   3592                 continue;
   3593             },
   3594             else => |e| {
   3595                 syscall.finish();
   3596                 switch (e) {
   3597                     .ACCES => return error.AccessDenied,
   3598                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3599                     .FAULT => |err| return errnoBug(err),
   3600                     .INVAL => |err| return errnoBug(err),
   3601                     .LOOP => return error.SymLinkLoop,
   3602                     .NAMETOOLONG => |err| return errnoBug(err), // Handled by pathToPosix() above.
   3603                     .NOENT => return error.FileNotFound,
   3604                     .NOTDIR => return error.NotDir,
   3605                     .NOMEM => return error.SystemResources,
   3606                     else => |err| return posix.unexpectedErrno(err),
   3607                 }
   3608             },
   3609         }
   3610     }
   3611 }
   3612 
   3613 fn dirStatFilePosix(
   3614     userdata: ?*anyopaque,
   3615     dir: Dir,
   3616     sub_path: []const u8,
   3617     options: Dir.StatFileOptions,
   3618 ) Dir.StatFileError!File.Stat {
   3619     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3620     _ = t;
   3621 
   3622     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   3623     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   3624 
   3625     const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
   3626 
   3627     return posixStatFile(dir.handle, sub_path_posix, flags);
   3628 }
   3629 
   3630 fn posixStatFile(dir_fd: posix.fd_t, sub_path: [:0]const u8, flags: u32) Dir.StatFileError!File.Stat {
   3631     const syscall: Syscall = try .start();
   3632     while (true) {
   3633         var stat = std.mem.zeroes(posix.Stat);
   3634         switch (posix.errno(fstatat_sym(dir_fd, sub_path, &stat, flags))) {
   3635             .SUCCESS => {
   3636                 syscall.finish();
   3637                 return statFromPosix(&stat);
   3638             },
   3639             .INTR => {
   3640                 try syscall.checkCancel();
   3641                 continue;
   3642             },
   3643             else => |e| {
   3644                 syscall.finish();
   3645                 switch (e) {
   3646                     .INVAL => |err| return errnoBug(err),
   3647                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3648                     .NOMEM => return error.SystemResources,
   3649                     .ACCES => return error.AccessDenied,
   3650                     .PERM => return error.PermissionDenied,
   3651                     .FAULT => |err| return errnoBug(err),
   3652                     .NAMETOOLONG => return error.NameTooLong,
   3653                     .LOOP => return error.SymLinkLoop,
   3654                     .NOENT => return error.FileNotFound,
   3655                     .NOTDIR => return error.FileNotFound,
   3656                     .ILSEQ => return error.BadPathName,
   3657                     else => |err| return posix.unexpectedErrno(err),
   3658                 }
   3659             },
   3660         }
   3661     }
   3662 }
   3663 
   3664 fn dirStatFileWindows(
   3665     userdata: ?*anyopaque,
   3666     dir: Dir,
   3667     sub_path: []const u8,
   3668     options: Dir.StatFileOptions,
   3669 ) Dir.StatFileError!File.Stat {
   3670     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3671     const file = try dirOpenFileWindows(t, dir, sub_path, .{
   3672         .follow_symlinks = options.follow_symlinks,
   3673     });
   3674     defer windows.CloseHandle(file.handle);
   3675     return fileStatWindows(t, file);
   3676 }
   3677 
   3678 fn dirStatFileWasi(
   3679     userdata: ?*anyopaque,
   3680     dir: Dir,
   3681     sub_path: []const u8,
   3682     options: Dir.StatFileOptions,
   3683 ) Dir.StatFileError!File.Stat {
   3684     if (builtin.link_libc) return dirStatFilePosix(userdata, dir, sub_path, options);
   3685     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3686     _ = t;
   3687     const wasi = std.os.wasi;
   3688     const flags: wasi.lookupflags_t = .{
   3689         .SYMLINK_FOLLOW = options.follow_symlinks,
   3690     };
   3691     var stat: wasi.filestat_t = undefined;
   3692     const syscall: Syscall = try .start();
   3693     while (true) {
   3694         switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
   3695             .SUCCESS => {
   3696                 syscall.finish();
   3697                 return statFromWasi(&stat);
   3698             },
   3699             .INTR => {
   3700                 try syscall.checkCancel();
   3701                 continue;
   3702             },
   3703             else => |e| {
   3704                 syscall.finish();
   3705                 switch (e) {
   3706                     .INVAL => |err| return errnoBug(err),
   3707                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3708                     .NOMEM => return error.SystemResources,
   3709                     .ACCES => return error.AccessDenied,
   3710                     .FAULT => |err| return errnoBug(err),
   3711                     .NAMETOOLONG => return error.NameTooLong,
   3712                     .NOENT => return error.FileNotFound,
   3713                     .NOTDIR => return error.FileNotFound,
   3714                     .NOTCAPABLE => return error.AccessDenied,
   3715                     .ILSEQ => return error.BadPathName,
   3716                     else => |err| return posix.unexpectedErrno(err),
   3717                 }
   3718             },
   3719         }
   3720     }
   3721 }
   3722 
   3723 fn filePathKind(t: *Threaded, dir: Dir, sub_path: []const u8) !File.Kind {
   3724     if (native_os == .linux) {
   3725         var path_buffer: [posix.PATH_MAX]u8 = undefined;
   3726         const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   3727 
   3728         const linux = std.os.linux;
   3729         const syscall: Syscall = try .start();
   3730         while (true) {
   3731             var statx = std.mem.zeroes(linux.Statx);
   3732             switch (linux.errno(linux.statx(
   3733                 dir.handle,
   3734                 sub_path_posix,
   3735                 linux.AT.NO_AUTOMOUNT | linux.AT.SYMLINK_NOFOLLOW,
   3736                 .{ .TYPE = true },
   3737                 &statx,
   3738             ))) {
   3739                 .SUCCESS => {
   3740                     syscall.finish();
   3741                     if (!statx.mask.TYPE) return error.Unexpected;
   3742                     return statxKind(statx.mode);
   3743                 },
   3744                 .INTR => {
   3745                     try syscall.checkCancel();
   3746                     continue;
   3747                 },
   3748                 .NOMEM => return syscall.fail(error.SystemResources),
   3749                 else => |err| return syscall.unexpectedErrno(err),
   3750             }
   3751         }
   3752     }
   3753 
   3754     const stat = try dirStatFile(t, dir, sub_path, .{ .follow_symlinks = false });
   3755     return stat.kind;
   3756 }
   3757 
   3758 fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {
   3759     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3760 
   3761     if (native_os == .linux) {
   3762         const linux = std.os.linux;
   3763 
   3764         const syscall: Syscall = try .start();
   3765         while (true) {
   3766             var statx = std.mem.zeroes(linux.Statx);
   3767             switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .{ .SIZE = true }, &statx))) {
   3768                 .SUCCESS => {
   3769                     syscall.finish();
   3770                     if (!statx.mask.SIZE) return error.Unexpected;
   3771                     return statx.size;
   3772                 },
   3773                 .INTR => {
   3774                     try syscall.checkCancel();
   3775                     continue;
   3776                 },
   3777                 else => |e| {
   3778                     syscall.finish();
   3779                     switch (e) {
   3780                         .ACCES => |err| return errnoBug(err),
   3781                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3782                         .FAULT => |err| return errnoBug(err),
   3783                         .INVAL => |err| return errnoBug(err),
   3784                         .LOOP => |err| return errnoBug(err),
   3785                         .NAMETOOLONG => |err| return errnoBug(err),
   3786                         .NOENT => |err| return errnoBug(err),
   3787                         .NOMEM => return error.SystemResources,
   3788                         .NOTDIR => |err| return errnoBug(err),
   3789                         else => |err| return posix.unexpectedErrno(err),
   3790                     }
   3791                 },
   3792             }
   3793         }
   3794     } else if (is_windows) {
   3795         // TODO call NtQueryInformationFile and ask for only the size instead of "all"
   3796     }
   3797 
   3798     const stat = try fileStat(t, file);
   3799     return stat.size;
   3800 }
   3801 
   3802 const fileStat = switch (native_os) {
   3803     .linux => fileStatLinux,
   3804     .windows => fileStatWindows,
   3805     .wasi => fileStatWasi,
   3806     else => fileStatPosix,
   3807 };
   3808 
   3809 fn fileStatPosix(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
   3810     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3811     _ = t;
   3812 
   3813     if (posix.Stat == void) return error.Streaming;
   3814 
   3815     const syscall: Syscall = try .start();
   3816     while (true) {
   3817         var stat = std.mem.zeroes(posix.Stat);
   3818         switch (posix.errno(fstat_sym(file.handle, &stat))) {
   3819             .SUCCESS => {
   3820                 syscall.finish();
   3821                 return statFromPosix(&stat);
   3822             },
   3823             .INTR => {
   3824                 try syscall.checkCancel();
   3825                 continue;
   3826             },
   3827             else => |e| {
   3828                 syscall.finish();
   3829                 switch (e) {
   3830                     .INVAL => |err| return errnoBug(err),
   3831                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3832                     .NOMEM => return error.SystemResources,
   3833                     .ACCES => return error.AccessDenied,
   3834                     else => |err| return posix.unexpectedErrno(err),
   3835                 }
   3836             },
   3837         }
   3838     }
   3839 }
   3840 
   3841 fn fileStatLinux(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
   3842     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3843     _ = t;
   3844     const linux = std.os.linux;
   3845     const sys = if (statx_use_c) std.c else std.os.linux;
   3846 
   3847     const syscall: Syscall = try .start();
   3848     while (true) {
   3849         var statx = std.mem.zeroes(linux.Statx);
   3850         switch (sys.errno(sys.statx(file.handle, "", linux.AT.EMPTY_PATH, linux_statx_request, &statx))) {
   3851             .SUCCESS => {
   3852                 syscall.finish();
   3853                 return statFromLinux(&statx);
   3854             },
   3855             .INTR => {
   3856                 try syscall.checkCancel();
   3857                 continue;
   3858             },
   3859             else => |e| {
   3860                 syscall.finish();
   3861                 switch (e) {
   3862                     .ACCES => |err| return errnoBug(err),
   3863                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3864                     .FAULT => |err| return errnoBug(err),
   3865                     .INVAL => |err| return errnoBug(err),
   3866                     .LOOP => |err| return errnoBug(err),
   3867                     .NAMETOOLONG => |err| return errnoBug(err),
   3868                     .NOENT => |err| return errnoBug(err),
   3869                     .NOMEM => return error.SystemResources,
   3870                     .NOTDIR => |err| return errnoBug(err),
   3871                     else => |err| return posix.unexpectedErrno(err),
   3872                 }
   3873             },
   3874         }
   3875     }
   3876 }
   3877 
   3878 fn fileStatWindows(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
   3879     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3880 
   3881     const block_size: u32 = if (t.systemBasicInformation()) |sbi|
   3882         @intCast(@max(sbi.PageSize, sbi.AllocationGranularity))
   3883     else
   3884         std.heap.page_size_max;
   3885 
   3886     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   3887     var info: windows.FILE.ALL_INFORMATION = undefined;
   3888     {
   3889         const syscall: Syscall = try .start();
   3890         while (true) switch (windows.ntdll.NtQueryInformationFile(
   3891             file.handle,
   3892             &io_status_block,
   3893             &info,
   3894             @sizeOf(windows.FILE.ALL_INFORMATION),
   3895             .All,
   3896         )) {
   3897             .SUCCESS => break syscall.finish(),
   3898             // Buffer overflow here indicates that there is more information available than was able to be stored in the buffer
   3899             // size provided. This is treated as success because the type of variable-length information that this would be relevant for
   3900             // (name, volume name, etc) we don't care about.
   3901             .BUFFER_OVERFLOW => break syscall.finish(),
   3902             .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
   3903             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   3904             .CANCELLED => {
   3905                 try syscall.checkCancel();
   3906                 continue;
   3907             },
   3908             else => |s| return syscall.unexpectedNtstatus(s),
   3909         };
   3910     }
   3911     return .{
   3912         .inode = info.InternalInformation.IndexNumber,
   3913         .size = @as(u64, @bitCast(info.StandardInformation.EndOfFile)),
   3914         .permissions = .default_file,
   3915         .kind = if (info.BasicInformation.FileAttributes.REPARSE_POINT) reparse_point: {
   3916             var tag_info: windows.FILE.ATTRIBUTE_TAG_INFO = undefined;
   3917             const syscall: Syscall = try .start();
   3918             while (true) switch (windows.ntdll.NtQueryInformationFile(
   3919                 file.handle,
   3920                 &io_status_block,
   3921                 &tag_info,
   3922                 @sizeOf(windows.FILE.ATTRIBUTE_TAG_INFO),
   3923                 .AttributeTag,
   3924             )) {
   3925                 .SUCCESS => break syscall.finish(),
   3926                 // INFO_LENGTH_MISMATCH and ACCESS_DENIED are the only documented possible errors
   3927                 // https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/d295752f-ce89-4b98-8553-266d37c84f0e
   3928                 .INFO_LENGTH_MISMATCH => |err| return syscall.ntstatusBug(err),
   3929                 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   3930                 .CANCELLED => {
   3931                     try syscall.checkCancel();
   3932                     continue;
   3933                 },
   3934                 else => |s| return syscall.unexpectedNtstatus(s),
   3935             };
   3936             if (tag_info.ReparseTag.IsSurrogate) break :reparse_point .sym_link;
   3937             // Unknown reparse point
   3938             break :reparse_point .unknown;
   3939         } else if (info.BasicInformation.FileAttributes.DIRECTORY)
   3940             .directory
   3941         else
   3942             .file,
   3943         .atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
   3944         .mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
   3945         .ctime = windows.fromSysTime(info.BasicInformation.ChangeTime),
   3946         .nlink = info.StandardInformation.NumberOfLinks,
   3947         .block_size = block_size,
   3948     };
   3949 }
   3950 
   3951 fn systemBasicInformation(t: *Threaded) ?*const windows.SYSTEM.BASIC_INFORMATION {
   3952     if (!t.system_basic_information.initialized.load(.acquire)) {
   3953         mutexLock(&t.mutex);
   3954         defer mutexUnlock(&t.mutex);
   3955 
   3956         switch (windows.ntdll.NtQuerySystemInformation(
   3957             .Basic,
   3958             &t.system_basic_information.buffer,
   3959             @sizeOf(windows.SYSTEM.BASIC_INFORMATION),
   3960             null,
   3961         )) {
   3962             .SUCCESS => {},
   3963             else => return null,
   3964         }
   3965 
   3966         t.system_basic_information.initialized.store(true, .release);
   3967     }
   3968     return &t.system_basic_information.buffer;
   3969 }
   3970 
   3971 fn fileStatWasi(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
   3972     if (builtin.link_libc) return fileStatPosix(userdata, file);
   3973 
   3974     const t: *Threaded = @ptrCast(@alignCast(userdata));
   3975     _ = t;
   3976 
   3977     const syscall: Syscall = try .start();
   3978     while (true) {
   3979         var stat: std.os.wasi.filestat_t = undefined;
   3980         switch (std.os.wasi.fd_filestat_get(file.handle, &stat)) {
   3981             .SUCCESS => {
   3982                 syscall.finish();
   3983                 return statFromWasi(&stat);
   3984             },
   3985             .INTR => {
   3986                 try syscall.checkCancel();
   3987                 continue;
   3988             },
   3989             else => |e| {
   3990                 syscall.finish();
   3991                 switch (e) {
   3992                     .INVAL => |err| return errnoBug(err),
   3993                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   3994                     .NOMEM => return error.SystemResources,
   3995                     .ACCES => return error.AccessDenied,
   3996                     .NOTCAPABLE => return error.AccessDenied,
   3997                     else => |err| return posix.unexpectedErrno(err),
   3998                 }
   3999             },
   4000         }
   4001     }
   4002 }
   4003 
   4004 const dirAccess = switch (native_os) {
   4005     .windows => dirAccessWindows,
   4006     .wasi => dirAccessWasi,
   4007     else => dirAccessPosix,
   4008 };
   4009 
   4010 fn dirAccessPosix(
   4011     userdata: ?*anyopaque,
   4012     dir: Dir,
   4013     sub_path: []const u8,
   4014     options: Dir.AccessOptions,
   4015 ) Dir.AccessError!void {
   4016     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4017     _ = t;
   4018 
   4019     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   4020     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   4021 
   4022     const flags: u32 = @as(u32, if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0);
   4023 
   4024     const mode: u32 =
   4025         @as(u32, if (options.read) posix.R_OK else 0) |
   4026         @as(u32, if (options.write) posix.W_OK else 0) |
   4027         @as(u32, if (options.execute) posix.X_OK else 0);
   4028 
   4029     const syscall: Syscall = try .start();
   4030     while (true) {
   4031         switch (posix.errno(posix.system.faccessat(dir.handle, sub_path_posix, mode, flags))) {
   4032             .SUCCESS => {
   4033                 syscall.finish();
   4034                 return;
   4035             },
   4036             .INTR => {
   4037                 try syscall.checkCancel();
   4038                 continue;
   4039             },
   4040             else => |e| {
   4041                 syscall.finish();
   4042                 switch (e) {
   4043                     .ACCES => return error.AccessDenied,
   4044                     .PERM => return error.PermissionDenied,
   4045                     .ROFS => return error.ReadOnlyFileSystem,
   4046                     .LOOP => return error.SymLinkLoop,
   4047                     .TXTBSY => return error.FileBusy,
   4048                     .NOTDIR => return error.FileNotFound,
   4049                     .NOENT => return error.FileNotFound,
   4050                     .NAMETOOLONG => return error.NameTooLong,
   4051                     .INVAL => |err| return errnoBug(err),
   4052                     .FAULT => |err| return errnoBug(err),
   4053                     .IO => return error.InputOutput,
   4054                     .NOMEM => return error.SystemResources,
   4055                     .ILSEQ => return error.BadPathName,
   4056                     else => |err| return posix.unexpectedErrno(err),
   4057                 }
   4058             },
   4059         }
   4060     }
   4061 }
   4062 
   4063 fn dirAccessWasi(
   4064     userdata: ?*anyopaque,
   4065     dir: Dir,
   4066     sub_path: []const u8,
   4067     options: Dir.AccessOptions,
   4068 ) Dir.AccessError!void {
   4069     if (builtin.link_libc) return dirAccessPosix(userdata, dir, sub_path, options);
   4070     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4071     _ = t;
   4072     const wasi = std.os.wasi;
   4073     const flags: wasi.lookupflags_t = .{
   4074         .SYMLINK_FOLLOW = options.follow_symlinks,
   4075     };
   4076     var stat: wasi.filestat_t = undefined;
   4077 
   4078     const syscall: Syscall = try .start();
   4079     while (true) {
   4080         switch (wasi.path_filestat_get(dir.handle, flags, sub_path.ptr, sub_path.len, &stat)) {
   4081             .SUCCESS => {
   4082                 syscall.finish();
   4083                 break;
   4084             },
   4085             .INTR => {
   4086                 try syscall.checkCancel();
   4087                 continue;
   4088             },
   4089             else => |e| {
   4090                 syscall.finish();
   4091                 switch (e) {
   4092                     .INVAL => |err| return errnoBug(err),
   4093                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   4094                     .NOMEM => return error.SystemResources,
   4095                     .ACCES => return error.AccessDenied,
   4096                     .FAULT => |err| return errnoBug(err),
   4097                     .NAMETOOLONG => return error.NameTooLong,
   4098                     .NOENT => return error.FileNotFound,
   4099                     .NOTDIR => return error.FileNotFound,
   4100                     .NOTCAPABLE => return error.AccessDenied,
   4101                     .ILSEQ => return error.BadPathName,
   4102                     else => |err| return posix.unexpectedErrno(err),
   4103                 }
   4104             },
   4105         }
   4106     }
   4107 
   4108     if (!options.read and !options.write and !options.execute)
   4109         return;
   4110 
   4111     var directory: wasi.fdstat_t = undefined;
   4112     if (wasi.fd_fdstat_get(dir.handle, &directory) != .SUCCESS)
   4113         return error.AccessDenied;
   4114 
   4115     var rights: wasi.rights_t = .{};
   4116     if (options.read) {
   4117         if (stat.filetype == .DIRECTORY) {
   4118             rights.FD_READDIR = true;
   4119         } else {
   4120             rights.FD_READ = true;
   4121         }
   4122     }
   4123     if (options.write)
   4124         rights.FD_WRITE = true;
   4125 
   4126     // No validation for execution.
   4127 
   4128     // https://github.com/ziglang/zig/issues/18882
   4129     const rights_int: u64 = @bitCast(rights);
   4130     const inheriting_int: u64 = @bitCast(directory.fs_rights_inheriting);
   4131     if ((rights_int & inheriting_int) != rights_int)
   4132         return error.AccessDenied;
   4133 }
   4134 
   4135 fn dirAccessWindows(
   4136     userdata: ?*anyopaque,
   4137     dir: Dir,
   4138     sub_path: []const u8,
   4139     options: Dir.AccessOptions,
   4140 ) Dir.AccessError!void {
   4141     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4142     _ = t;
   4143 
   4144     _ = options; // TODO
   4145 
   4146     if (std.mem.eql(u8, sub_path, ".") or std.mem.eql(u8, sub_path, "..")) return;
   4147     const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
   4148     const attr: windows.OBJECT.ATTRIBUTES = .{
   4149         .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
   4150         .ObjectName = @constCast(&sub_path_w.string()),
   4151     };
   4152     var basic_info: windows.FILE.BASIC_INFORMATION = undefined;
   4153     const syscall: Syscall = try .start();
   4154     while (true) switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
   4155         .SUCCESS => return syscall.finish(),
   4156         .CANCELLED => {
   4157             try syscall.checkCancel();
   4158             continue;
   4159         },
   4160         .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
   4161         .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
   4162         .OBJECT_NAME_INVALID => |err| return syscall.ntstatusBug(err),
   4163         .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
   4164         .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   4165         .OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
   4166         else => |rc| return syscall.unexpectedNtstatus(rc),
   4167     };
   4168 }
   4169 
   4170 const dirCreateFile = switch (native_os) {
   4171     .windows => dirCreateFileWindows,
   4172     .wasi => dirCreateFileWasi,
   4173     else => dirCreateFilePosix,
   4174 };
   4175 
   4176 fn dirCreateFilePosix(
   4177     userdata: ?*anyopaque,
   4178     dir: Dir,
   4179     sub_path: []const u8,
   4180     flags: File.CreateFlags,
   4181 ) File.OpenError!File {
   4182     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4183     _ = t;
   4184 
   4185     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   4186     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   4187 
   4188     var os_flags: posix.O = .{
   4189         .ACCMODE = if (flags.read) .RDWR else .WRONLY,
   4190         .CREAT = true,
   4191         .TRUNC = flags.truncate,
   4192         .EXCL = flags.exclusive,
   4193     };
   4194     if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true;
   4195     if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true;
   4196 
   4197     // Use the O locking flags if the os supports them to acquire the lock
   4198     // atomically. Note that the NONBLOCK flag is removed after the openat()
   4199     // call is successful.
   4200     if (have_flock_open_flags) switch (flags.lock) {
   4201         .none => {},
   4202         .shared => {
   4203             os_flags.SHLOCK = true;
   4204             os_flags.NONBLOCK = flags.lock_nonblocking;
   4205         },
   4206         .exclusive => {
   4207             os_flags.EXLOCK = true;
   4208             os_flags.NONBLOCK = flags.lock_nonblocking;
   4209         },
   4210     };
   4211 
   4212     const fd: posix.fd_t = fd: {
   4213         const syscall: Syscall = try .start();
   4214         while (true) {
   4215             const rc = openat_sym(dir.handle, sub_path_posix, os_flags, flags.permissions.toMode());
   4216             switch (posix.errno(rc)) {
   4217                 .SUCCESS => {
   4218                     syscall.finish();
   4219                     break :fd @intCast(rc);
   4220                 },
   4221                 .INTR => {
   4222                     try syscall.checkCancel();
   4223                     continue;
   4224                 },
   4225                 else => |e| {
   4226                     syscall.finish();
   4227                     switch (e) {
   4228                         .FAULT => |err| return errnoBug(err),
   4229                         .INVAL => return error.BadPathName,
   4230                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   4231                         .ACCES => return error.AccessDenied,
   4232                         .FBIG => return error.FileTooBig,
   4233                         .OVERFLOW => return error.FileTooBig,
   4234                         .ISDIR => return error.IsDir,
   4235                         .LOOP => return error.SymLinkLoop,
   4236                         .MFILE => return error.ProcessFdQuotaExceeded,
   4237                         .NAMETOOLONG => return error.NameTooLong,
   4238                         .NFILE => return error.SystemFdQuotaExceeded,
   4239                         .NODEV => return error.NoDevice,
   4240                         .NOENT => return error.FileNotFound,
   4241                         .SRCH => return error.FileNotFound, // Linux when accessing procfs.
   4242                         .NOMEM => return error.SystemResources,
   4243                         .NOSPC => return error.NoSpaceLeft,
   4244                         .NOTDIR => return error.NotDir,
   4245                         .PERM => return error.PermissionDenied,
   4246                         .EXIST => return error.PathAlreadyExists,
   4247                         .BUSY => return error.DeviceBusy,
   4248                         .OPNOTSUPP => return error.FileLocksUnsupported,
   4249                         .AGAIN => return error.WouldBlock,
   4250                         .TXTBSY => return error.FileBusy,
   4251                         .NXIO => return error.NoDevice,
   4252                         .ILSEQ => return error.BadPathName,
   4253                         else => |err| return posix.unexpectedErrno(err),
   4254                     }
   4255                 },
   4256             }
   4257         }
   4258     };
   4259     errdefer closeFd(fd);
   4260 
   4261     if (have_flock and !have_flock_open_flags and flags.lock != .none) {
   4262         const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0;
   4263         const lock_flags = switch (flags.lock) {
   4264             .none => unreachable,
   4265             .shared => posix.LOCK.SH | lock_nonblocking,
   4266             .exclusive => posix.LOCK.EX | lock_nonblocking,
   4267         };
   4268 
   4269         const syscall: Syscall = try .start();
   4270         while (true) {
   4271             switch (posix.errno(posix.system.flock(fd, lock_flags))) {
   4272                 .SUCCESS => {
   4273                     syscall.finish();
   4274                     break;
   4275                 },
   4276                 .INTR => {
   4277                     try syscall.checkCancel();
   4278                     continue;
   4279                 },
   4280                 else => |e| {
   4281                     syscall.finish();
   4282                     switch (e) {
   4283                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   4284                         .INVAL => |err| return errnoBug(err), // invalid parameters
   4285                         .NOLCK => return error.SystemResources,
   4286                         .AGAIN => return error.WouldBlock,
   4287                         .OPNOTSUPP => return error.FileLocksUnsupported,
   4288                         else => |err| return posix.unexpectedErrno(err),
   4289                     }
   4290                 },
   4291             }
   4292         }
   4293     }
   4294 
   4295     if (have_flock_open_flags and flags.lock_nonblocking) {
   4296         var fl_flags: usize = fl: {
   4297             const syscall: Syscall = try .start();
   4298             while (true) {
   4299                 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));
   4300                 switch (posix.errno(rc)) {
   4301                     .SUCCESS => {
   4302                         syscall.finish();
   4303                         break :fl @intCast(rc);
   4304                     },
   4305                     .INTR => {
   4306                         try syscall.checkCancel();
   4307                         continue;
   4308                     },
   4309                     else => |err| {
   4310                         syscall.finish();
   4311                         return posix.unexpectedErrno(err);
   4312                     },
   4313                 }
   4314             }
   4315         };
   4316 
   4317         fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK"));
   4318 
   4319         const syscall: Syscall = try .start();
   4320         while (true) {
   4321             switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) {
   4322                 .SUCCESS => {
   4323                     syscall.finish();
   4324                     break;
   4325                 },
   4326                 .INTR => {
   4327                     try syscall.checkCancel();
   4328                     continue;
   4329                 },
   4330                 else => |err| {
   4331                     syscall.finish();
   4332                     return posix.unexpectedErrno(err);
   4333                 },
   4334             }
   4335         }
   4336     }
   4337 
   4338     return .{
   4339         .handle = fd,
   4340         .flags = .{ .nonblocking = false },
   4341     };
   4342 }
   4343 
   4344 fn dirCreateFileWindows(
   4345     userdata: ?*anyopaque,
   4346     dir: Dir,
   4347     sub_path: []const u8,
   4348     flags: File.CreateFlags,
   4349 ) File.OpenError!File {
   4350     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4351     _ = t;
   4352 
   4353     if (std.mem.eql(u8, sub_path, ".")) return error.IsDir;
   4354     if (std.mem.eql(u8, sub_path, "..")) return error.IsDir;
   4355 
   4356     const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
   4357     const attr: windows.OBJECT.ATTRIBUTES = .{
   4358         .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
   4359         .ObjectName = @constCast(&sub_path_w.string()),
   4360     };
   4361     const create_disposition: windows.FILE.CREATE_DISPOSITION = if (flags.exclusive)
   4362         .CREATE
   4363     else if (flags.truncate)
   4364         .OVERWRITE_IF
   4365     else
   4366         .OPEN_IF;
   4367 
   4368     const access_mask: windows.ACCESS_MASK = .{
   4369         .STANDARD = .{ .SYNCHRONIZE = true },
   4370         .GENERIC = .{
   4371             .WRITE = true,
   4372             .READ = flags.read,
   4373         },
   4374     };
   4375 
   4376     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   4377     var attempt: u5 = 0;
   4378     var handle: windows.HANDLE = undefined;
   4379     var syscall: Syscall = try .start();
   4380     while (true) switch (windows.ntdll.NtCreateFile(
   4381         &handle,
   4382         access_mask,
   4383         &attr,
   4384         &io_status_block,
   4385         null,
   4386         .{ .NORMAL = true },
   4387         .VALID_FLAGS, // share access
   4388         create_disposition,
   4389         .{
   4390             .NON_DIRECTORY_FILE = true,
   4391             .IO = .SYNCHRONOUS_NONALERT,
   4392         },
   4393         null,
   4394         0,
   4395     )) {
   4396         .SUCCESS => {
   4397             syscall.finish();
   4398             break;
   4399         },
   4400         .CANCELLED => {
   4401             try syscall.checkCancel();
   4402             continue;
   4403         },
   4404         .SHARING_VIOLATION => {
   4405             // This occurs if the file attempting to be opened is a running
   4406             // executable. However, there's a kernel bug: the error may be
   4407             // incorrectly returned for an indeterminate amount of time
   4408             // after an executable file is closed. Here we work around the
   4409             // kernel bug with retry attempts.
   4410             syscall.finish();
   4411             if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
   4412             try parking_sleep.sleep(.{ .duration = .{
   4413                 .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
   4414                 .clock = .awake,
   4415             } });
   4416             attempt += 1;
   4417             syscall = try .start();
   4418             continue;
   4419         },
   4420         .DELETE_PENDING => {
   4421             // This error means that there *was* a file in this location on
   4422             // the file system, but it was deleted. However, the OS is not
   4423             // finished with the deletion operation, and so this CreateFile
   4424             // call has failed. Here, we simulate the kernel bug being
   4425             // fixed by sleeping and retrying until the error goes away.
   4426             syscall.finish();
   4427             if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
   4428             try parking_sleep.sleep(.{ .duration = .{
   4429                 .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
   4430                 .clock = .awake,
   4431             } });
   4432             attempt += 1;
   4433             syscall = try .start();
   4434             continue;
   4435         },
   4436         .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
   4437         .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
   4438         .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
   4439         .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
   4440         .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
   4441         .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
   4442         .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   4443         .PIPE_BUSY => return syscall.fail(error.PipeBusy),
   4444         .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice),
   4445         .OBJECT_NAME_COLLISION => return syscall.fail(error.PathAlreadyExists),
   4446         .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
   4447         .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
   4448         .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
   4449         .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
   4450         .DISK_FULL => return syscall.fail(error.NoSpaceLeft),
   4451         .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
   4452         .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
   4453         .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
   4454         else => |status| return syscall.unexpectedNtstatus(status),
   4455     };
   4456     errdefer windows.CloseHandle(handle);
   4457 
   4458     const exclusive = switch (flags.lock) {
   4459         .none => return .{
   4460             .handle = handle,
   4461             .flags = .{ .nonblocking = false },
   4462         },
   4463         .shared => false,
   4464         .exclusive => true,
   4465     };
   4466 
   4467     syscall = try .start();
   4468     while (true) switch (windows.ntdll.NtLockFile(
   4469         handle,
   4470         null,
   4471         null,
   4472         null,
   4473         &io_status_block,
   4474         &windows_lock_range_off,
   4475         &windows_lock_range_len,
   4476         null,
   4477         @intFromBool(flags.lock_nonblocking),
   4478         @intFromBool(exclusive),
   4479     )) {
   4480         .SUCCESS => {
   4481             syscall.finish();
   4482             return .{
   4483                 .handle = handle,
   4484                 .flags = .{ .nonblocking = false },
   4485             };
   4486         },
   4487         .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
   4488         .LOCK_NOT_GRANTED => return syscall.fail(error.WouldBlock),
   4489         .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
   4490         else => |status| return syscall.unexpectedNtstatus(status),
   4491     };
   4492 }
   4493 
   4494 fn dirCreateFileWasi(
   4495     userdata: ?*anyopaque,
   4496     dir: Dir,
   4497     sub_path: []const u8,
   4498     flags: File.CreateFlags,
   4499 ) File.OpenError!File {
   4500     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4501     _ = t;
   4502     const wasi = std.os.wasi;
   4503     const lookup_flags: wasi.lookupflags_t = .{};
   4504     const oflags: wasi.oflags_t = .{
   4505         .CREAT = true,
   4506         .TRUNC = flags.truncate,
   4507         .EXCL = flags.exclusive,
   4508     };
   4509     const fdflags: wasi.fdflags_t = .{};
   4510     const base: wasi.rights_t = .{
   4511         .FD_READ = flags.read,
   4512         .FD_WRITE = true,
   4513         .FD_DATASYNC = true,
   4514         .FD_SEEK = true,
   4515         .FD_TELL = true,
   4516         .FD_FDSTAT_SET_FLAGS = true,
   4517         .FD_SYNC = true,
   4518         .FD_ALLOCATE = true,
   4519         .FD_ADVISE = true,
   4520         .FD_FILESTAT_SET_TIMES = true,
   4521         .FD_FILESTAT_SET_SIZE = true,
   4522         .FD_FILESTAT_GET = true,
   4523         // POLL_FD_READWRITE only grants extra rights if the corresponding FD_READ and/or
   4524         // FD_WRITE is also set.
   4525         .POLL_FD_READWRITE = true,
   4526     };
   4527     const inheriting: wasi.rights_t = .{};
   4528     var fd: posix.fd_t = undefined;
   4529     const syscall: Syscall = try .start();
   4530     while (true) {
   4531         switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) {
   4532             .SUCCESS => {
   4533                 syscall.finish();
   4534                 return .{
   4535                     .handle = fd,
   4536                     .flags = .{ .nonblocking = false },
   4537                 };
   4538             },
   4539             .INTR => {
   4540                 try syscall.checkCancel();
   4541                 continue;
   4542             },
   4543             else => |e| {
   4544                 syscall.finish();
   4545                 switch (e) {
   4546                     .FAULT => |err| return errnoBug(err),
   4547                     .INVAL => return error.BadPathName,
   4548                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   4549                     .ACCES => return error.AccessDenied,
   4550                     .FBIG => return error.FileTooBig,
   4551                     .OVERFLOW => return error.FileTooBig,
   4552                     .ISDIR => return error.IsDir,
   4553                     .LOOP => return error.SymLinkLoop,
   4554                     .MFILE => return error.ProcessFdQuotaExceeded,
   4555                     .NAMETOOLONG => return error.NameTooLong,
   4556                     .NFILE => return error.SystemFdQuotaExceeded,
   4557                     .NODEV => return error.NoDevice,
   4558                     .NOENT => return error.FileNotFound,
   4559                     .NOMEM => return error.SystemResources,
   4560                     .NOSPC => return error.NoSpaceLeft,
   4561                     .NOTDIR => return error.NotDir,
   4562                     .PERM => return error.PermissionDenied,
   4563                     .EXIST => return error.PathAlreadyExists,
   4564                     .BUSY => return error.DeviceBusy,
   4565                     .NOTCAPABLE => return error.AccessDenied,
   4566                     .ILSEQ => return error.BadPathName,
   4567                     else => |err| return posix.unexpectedErrno(err),
   4568                 }
   4569             },
   4570         }
   4571     }
   4572 }
   4573 
   4574 fn dirCreateFileAtomic(
   4575     userdata: ?*anyopaque,
   4576     dir: Dir,
   4577     dest_path: []const u8,
   4578     options: Dir.CreateFileAtomicOptions,
   4579 ) Dir.CreateFileAtomicError!File.Atomic {
   4580     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4581     const t_io = io(t);
   4582 
   4583     // Linux has O_TMPFILE, but linkat() does not support AT_REPLACE, so it's
   4584     // useless when we have to make up a bogus path name to do the rename()
   4585     // anyway.
   4586     if (native_os == .linux and !options.replace) tmpfile: {
   4587         const flags: posix.O = if (@hasField(posix.O, "TMPFILE")) .{
   4588             .ACCMODE = .RDWR,
   4589             .TMPFILE = true,
   4590             .DIRECTORY = true,
   4591             .CLOEXEC = true,
   4592         } else if (@hasField(posix.O, "TMPFILE0") and !@hasField(posix.O, "TMPFILE2")) .{
   4593             .ACCMODE = .RDWR,
   4594             .TMPFILE0 = true,
   4595             .TMPFILE1 = true,
   4596             .DIRECTORY = true,
   4597             .CLOEXEC = true,
   4598         } else break :tmpfile;
   4599 
   4600         const dest_dirname = Dir.path.dirname(dest_path);
   4601         if (dest_dirname) |dirname| {
   4602             // This has a nice side effect of preemptively triggering EISDIR or
   4603             // ENOENT, avoiding the ambiguity below.
   4604             if (options.make_path) dir.createDirPath(t_io, dirname) catch |err| switch (err) {
   4605                 // None of these make sense in this context.
   4606                 error.IsDir,
   4607                 error.Streaming,
   4608                 error.DiskQuota,
   4609                 error.PathAlreadyExists,
   4610                 error.LinkQuotaExceeded,
   4611                 error.PipeBusy,
   4612                 error.FileTooBig,
   4613                 error.DeviceBusy,
   4614                 error.FileLocksUnsupported,
   4615                 error.FileBusy,
   4616                 => return error.Unexpected,
   4617 
   4618                 else => |e| return e,
   4619             };
   4620         }
   4621 
   4622         var path_buffer: [posix.PATH_MAX]u8 = undefined;
   4623         const sub_path_posix = try pathToPosix(dest_dirname orelse ".", &path_buffer);
   4624 
   4625         const syscall: Syscall = try .start();
   4626         while (true) {
   4627             const rc = openat_sym(dir.handle, sub_path_posix, flags, options.permissions.toMode());
   4628             switch (posix.errno(rc)) {
   4629                 .SUCCESS => {
   4630                     syscall.finish();
   4631                     return .{
   4632                         .file = .{
   4633                             .handle = @intCast(rc),
   4634                             .flags = .{ .nonblocking = false },
   4635                         },
   4636                         .file_basename_hex = 0,
   4637                         .dest_sub_path = dest_path,
   4638                         .file_open = true,
   4639                         .file_exists = false,
   4640                         .close_dir_on_deinit = false,
   4641                         .dir = dir,
   4642                     };
   4643                 },
   4644                 .INTR => {
   4645                     try syscall.checkCancel();
   4646                     continue;
   4647                 },
   4648                 .ISDIR, .NOENT => {
   4649                     // Ambiguous error code. It might mean the file system
   4650                     // does not support O_TMPFILE. Therefore, we must fall
   4651                     // back to not using O_TMPFILE.
   4652                     syscall.finish();
   4653                     break :tmpfile;
   4654                 },
   4655                 .INVAL => return syscall.fail(error.BadPathName),
   4656                 .ACCES => return syscall.fail(error.AccessDenied),
   4657                 .LOOP => return syscall.fail(error.SymLinkLoop),
   4658                 .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
   4659                 .NAMETOOLONG => return syscall.fail(error.NameTooLong),
   4660                 .NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
   4661                 .NODEV => return syscall.fail(error.NoDevice),
   4662                 .NOMEM => return syscall.fail(error.SystemResources),
   4663                 .NOSPC => return syscall.fail(error.NoSpaceLeft),
   4664                 .NOTDIR => return syscall.fail(error.NotDir),
   4665                 .PERM => return syscall.fail(error.PermissionDenied),
   4666                 .AGAIN => return syscall.fail(error.WouldBlock),
   4667                 .NXIO => return syscall.fail(error.NoDevice),
   4668                 .ILSEQ => return syscall.fail(error.BadPathName),
   4669                 else => |err| return syscall.unexpectedErrno(err),
   4670             }
   4671         }
   4672     }
   4673 
   4674     if (Dir.path.dirname(dest_path)) |dirname| {
   4675         const new_dir = if (options.make_path)
   4676             dir.createDirPathOpen(t_io, dirname, .{}) catch |err| switch (err) {
   4677                 // None of these make sense in this context.
   4678                 error.IsDir,
   4679                 error.Streaming,
   4680                 error.DiskQuota,
   4681                 error.PathAlreadyExists,
   4682                 error.LinkQuotaExceeded,
   4683                 error.PipeBusy,
   4684                 error.FileTooBig,
   4685                 error.FileLocksUnsupported,
   4686                 error.DeviceBusy,
   4687                 => return error.Unexpected,
   4688 
   4689                 else => |e| return e,
   4690             }
   4691         else
   4692             try dir.openDir(t_io, dirname, .{});
   4693 
   4694         return atomicFileInit(t_io, Dir.path.basename(dest_path), options.permissions, new_dir, true);
   4695     }
   4696 
   4697     return atomicFileInit(t_io, dest_path, options.permissions, dir, false);
   4698 }
   4699 
   4700 fn atomicFileInit(
   4701     t_io: Io,
   4702     dest_basename: []const u8,
   4703     permissions: File.Permissions,
   4704     dir: Dir,
   4705     close_dir_on_deinit: bool,
   4706 ) Dir.CreateFileAtomicError!File.Atomic {
   4707     while (true) {
   4708         var random_integer: u64 = undefined;
   4709         t_io.random(@ptrCast(&random_integer));
   4710         const tmp_sub_path = std.fmt.hex(random_integer);
   4711         const file = dir.createFile(t_io, &tmp_sub_path, .{
   4712             .permissions = permissions,
   4713             .exclusive = true,
   4714         }) catch |err| switch (err) {
   4715             error.PathAlreadyExists => continue,
   4716             error.DeviceBusy => continue,
   4717             error.FileBusy => continue,
   4718 
   4719             error.IsDir => return error.Unexpected, // No path components.
   4720             error.FileTooBig => return error.Unexpected, // Creating, not opening.
   4721             error.FileLocksUnsupported => return error.Unexpected, // Not asking for locks.
   4722             error.PipeBusy => return error.Unexpected, // Not opening a pipe.
   4723 
   4724             else => |e| return e,
   4725         };
   4726         return .{
   4727             .file = file,
   4728             .file_basename_hex = random_integer,
   4729             .dest_sub_path = dest_basename,
   4730             .file_open = true,
   4731             .file_exists = true,
   4732             .close_dir_on_deinit = close_dir_on_deinit,
   4733             .dir = dir,
   4734         };
   4735     }
   4736 }
   4737 
   4738 const dirOpenFile = switch (native_os) {
   4739     .windows => dirOpenFileWindows,
   4740     .wasi => dirOpenFileWasi,
   4741     else => dirOpenFilePosix,
   4742 };
   4743 
   4744 fn dirOpenFilePosix(
   4745     userdata: ?*anyopaque,
   4746     dir: Dir,
   4747     sub_path: []const u8,
   4748     flags: File.OpenFlags,
   4749 ) File.OpenError!File {
   4750     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4751 
   4752     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   4753     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   4754 
   4755     var os_flags: posix.O = switch (native_os) {
   4756         .wasi => .{
   4757             .read = flags.mode != .write_only,
   4758             .write = flags.mode != .read_only,
   4759             .NOFOLLOW = !flags.follow_symlinks,
   4760         },
   4761         else => .{
   4762             .ACCMODE = switch (flags.mode) {
   4763                 .read_only => .RDONLY,
   4764                 .write_only => .WRONLY,
   4765                 .read_write => .RDWR,
   4766             },
   4767             .NOFOLLOW = !flags.follow_symlinks,
   4768         },
   4769     };
   4770     if (@hasField(posix.O, "CLOEXEC")) os_flags.CLOEXEC = true;
   4771     if (@hasField(posix.O, "LARGEFILE")) os_flags.LARGEFILE = true;
   4772     if (@hasField(posix.O, "NOCTTY")) os_flags.NOCTTY = !flags.allow_ctty;
   4773     if (@hasField(posix.O, "PATH") and flags.path_only) os_flags.PATH = true;
   4774 
   4775     // Use the O locking flags if the os supports them to acquire the lock
   4776     // atomically. Note that the NONBLOCK flag is removed after the openat()
   4777     // call is successful.
   4778     if (have_flock_open_flags) switch (flags.lock) {
   4779         .none => {},
   4780         .shared => {
   4781             os_flags.SHLOCK = true;
   4782             os_flags.NONBLOCK = flags.lock_nonblocking;
   4783         },
   4784         .exclusive => {
   4785             os_flags.EXLOCK = true;
   4786             os_flags.NONBLOCK = flags.lock_nonblocking;
   4787         },
   4788     };
   4789 
   4790     const mode: posix.mode_t = 0;
   4791 
   4792     const fd: posix.fd_t = fd: {
   4793         const syscall: Syscall = try .start();
   4794         while (true) {
   4795             const rc = openat_sym(dir.handle, sub_path_posix, os_flags, mode);
   4796             switch (posix.errno(rc)) {
   4797                 .SUCCESS => {
   4798                     syscall.finish();
   4799                     break :fd @intCast(rc);
   4800                 },
   4801                 .INTR => {
   4802                     try syscall.checkCancel();
   4803                     continue;
   4804                 },
   4805                 else => |e| {
   4806                     syscall.finish();
   4807                     switch (e) {
   4808                         .FAULT => |err| return errnoBug(err),
   4809                         .INVAL => return error.BadPathName,
   4810                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   4811                         .ACCES => return error.AccessDenied,
   4812                         .FBIG => return error.FileTooBig,
   4813                         .OVERFLOW => return error.FileTooBig,
   4814                         .ISDIR => return error.IsDir,
   4815                         .LOOP => return error.SymLinkLoop,
   4816                         .MFILE => return error.ProcessFdQuotaExceeded,
   4817                         .NAMETOOLONG => return error.NameTooLong,
   4818                         .NFILE => return error.SystemFdQuotaExceeded,
   4819                         .NODEV => return error.NoDevice,
   4820                         .NOENT => return error.FileNotFound,
   4821                         .SRCH => return error.FileNotFound, // Linux when opening procfs files.
   4822                         .NOMEM => return error.SystemResources,
   4823                         .NOSPC => return error.NoSpaceLeft,
   4824                         .NOTDIR => return error.NotDir,
   4825                         .PERM => return error.PermissionDenied,
   4826                         .EXIST => return error.PathAlreadyExists,
   4827                         .BUSY => return error.DeviceBusy,
   4828                         .OPNOTSUPP => return error.FileLocksUnsupported,
   4829                         .AGAIN => return error.WouldBlock,
   4830                         .TXTBSY => return error.FileBusy,
   4831                         .NXIO => return error.NoDevice,
   4832                         .ILSEQ => return error.BadPathName,
   4833                         else => |err| return posix.unexpectedErrno(err),
   4834                     }
   4835                 },
   4836             }
   4837         }
   4838     };
   4839     errdefer closeFd(fd);
   4840 
   4841     if (!flags.allow_directory) {
   4842         const is_dir = is_dir: {
   4843             const stat = fileStat(t, .{
   4844                 .handle = fd,
   4845                 .flags = .{ .nonblocking = false },
   4846             }) catch |err| switch (err) {
   4847                 // The directory-ness is either unknown or unknowable
   4848                 error.Streaming => break :is_dir false,
   4849                 else => |e| return e,
   4850             };
   4851             break :is_dir stat.kind == .directory;
   4852         };
   4853         if (is_dir) return error.IsDir;
   4854     }
   4855 
   4856     if (have_flock and !have_flock_open_flags and flags.lock != .none) {
   4857         const lock_nonblocking: i32 = if (flags.lock_nonblocking) posix.LOCK.NB else 0;
   4858         const lock_flags = switch (flags.lock) {
   4859             .none => unreachable,
   4860             .shared => posix.LOCK.SH | lock_nonblocking,
   4861             .exclusive => posix.LOCK.EX | lock_nonblocking,
   4862         };
   4863         const syscall: Syscall = try .start();
   4864         while (true) {
   4865             switch (posix.errno(posix.system.flock(fd, lock_flags))) {
   4866                 .SUCCESS => {
   4867                     syscall.finish();
   4868                     break;
   4869                 },
   4870                 .INTR => {
   4871                     try syscall.checkCancel();
   4872                     continue;
   4873                 },
   4874                 else => |e| {
   4875                     syscall.finish();
   4876                     switch (e) {
   4877                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   4878                         .INVAL => |err| return errnoBug(err), // invalid parameters
   4879                         .NOLCK => return error.SystemResources,
   4880                         .AGAIN => return error.WouldBlock,
   4881                         .OPNOTSUPP => return error.FileLocksUnsupported,
   4882                         else => |err| return posix.unexpectedErrno(err),
   4883                     }
   4884                 },
   4885             }
   4886         }
   4887     }
   4888 
   4889     if (have_flock_open_flags and flags.lock_nonblocking) {
   4890         var fl_flags: usize = fl: {
   4891             const syscall: Syscall = try .start();
   4892             while (true) {
   4893                 const rc = posix.system.fcntl(fd, posix.F.GETFL, @as(usize, 0));
   4894                 switch (posix.errno(rc)) {
   4895                     .SUCCESS => {
   4896                         syscall.finish();
   4897                         break :fl @intCast(rc);
   4898                     },
   4899                     .INTR => {
   4900                         try syscall.checkCancel();
   4901                         continue;
   4902                     },
   4903                     else => |err| {
   4904                         syscall.finish();
   4905                         return posix.unexpectedErrno(err);
   4906                     },
   4907                 }
   4908             }
   4909         };
   4910 
   4911         fl_flags |= @as(usize, 1 << @bitOffsetOf(posix.O, "NONBLOCK"));
   4912 
   4913         const syscall: Syscall = try .start();
   4914         while (true) {
   4915             switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, fl_flags))) {
   4916                 .SUCCESS => {
   4917                     syscall.finish();
   4918                     break;
   4919                 },
   4920                 .INTR => {
   4921                     try syscall.checkCancel();
   4922                     continue;
   4923                 },
   4924                 else => |err| {
   4925                     syscall.finish();
   4926                     return posix.unexpectedErrno(err);
   4927                 },
   4928             }
   4929         }
   4930     }
   4931 
   4932     return .{
   4933         .handle = fd,
   4934         .flags = .{ .nonblocking = false },
   4935     };
   4936 }
   4937 
   4938 fn dirOpenFileWindows(
   4939     userdata: ?*anyopaque,
   4940     dir: Dir,
   4941     sub_path: []const u8,
   4942     flags: File.OpenFlags,
   4943 ) File.OpenError!File {
   4944     const t: *Threaded = @ptrCast(@alignCast(userdata));
   4945     _ = t;
   4946     const sub_path_w_array = try sliceToPrefixedFileW(dir.handle, sub_path);
   4947     const sub_path_w = sub_path_w_array.span();
   4948     const dir_handle = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle;
   4949     return dirOpenFileWtf16(dir_handle, sub_path_w, flags);
   4950 }
   4951 
   4952 pub fn dirOpenFileWtf16(
   4953     dir_handle: ?windows.HANDLE,
   4954     sub_path_w: []const u16,
   4955     flags: File.OpenFlags,
   4956 ) File.OpenError!File {
   4957     const allow_directory = flags.allow_directory and !flags.isWrite();
   4958     if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{'.'})) return error.IsDir;
   4959     if (!allow_directory and std.mem.eql(u16, sub_path_w, &.{ '.', '.' })) return error.IsDir;
   4960     const w = windows;
   4961 
   4962     var io_status_block: w.IO_STATUS_BLOCK = undefined;
   4963     var attempt: u5 = 0;
   4964     var syscall: Syscall = try .start();
   4965     const handle = while (true) {
   4966         var result: w.HANDLE = undefined;
   4967         switch (w.ntdll.NtCreateFile(
   4968             &result,
   4969             .{
   4970                 .STANDARD = .{ .SYNCHRONIZE = true },
   4971                 .GENERIC = .{
   4972                     .READ = flags.isRead(),
   4973                     .WRITE = flags.isWrite(),
   4974                 },
   4975             },
   4976             &.{
   4977                 .RootDirectory = dir_handle,
   4978                 .ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)),
   4979             },
   4980             &io_status_block,
   4981             null,
   4982             .{ .NORMAL = true },
   4983             .VALID_FLAGS,
   4984             .OPEN,
   4985             .{
   4986                 .IO = if (flags.follow_symlinks) .SYNCHRONOUS_NONALERT else .ASYNCHRONOUS,
   4987                 .NON_DIRECTORY_FILE = !allow_directory,
   4988                 .OPEN_REPARSE_POINT = !flags.follow_symlinks,
   4989             },
   4990             null,
   4991             0,
   4992         )) {
   4993             .SUCCESS => {
   4994                 syscall.finish();
   4995                 break result;
   4996             },
   4997             .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
   4998             .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
   4999             .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
   5000             .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
   5001             .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
   5002             .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
   5003             .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
   5004             .CANCELLED => {
   5005                 try syscall.checkCancel();
   5006                 continue;
   5007             },
   5008             .SHARING_VIOLATION => {
   5009                 // This occurs if the file attempting to be opened is a running
   5010                 // executable. However, there's a kernel bug: the error may be
   5011                 // incorrectly returned for an indeterminate amount of time
   5012                 // after an executable file is closed. Here we work around the
   5013                 // kernel bug with retry attempts.
   5014                 syscall.finish();
   5015                 if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
   5016                 try parking_sleep.sleep(.{ .duration = .{
   5017                     .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
   5018                     .clock = .awake,
   5019                 } });
   5020                 attempt += 1;
   5021                 syscall = try .start();
   5022                 continue;
   5023             },
   5024             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   5025             .PIPE_BUSY => return syscall.fail(error.PipeBusy),
   5026             .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice),
   5027             .OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
   5028             .OBJECT_NAME_COLLISION => return syscall.fail(error.PathAlreadyExists),
   5029             .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
   5030             .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
   5031             .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
   5032             .INVALID_HANDLE => |err| return syscall.ntstatusBug(err),
   5033             .DELETE_PENDING => {
   5034                 // This error means that there *was* a file in this location on
   5035                 // the file system, but it was deleted. However, the OS is not
   5036                 // finished with the deletion operation, and so this CreateFile
   5037                 // call has failed. Here, we simulate the kernel bug being
   5038                 // fixed by sleeping and retrying until the error goes away.
   5039                 syscall.finish();
   5040                 if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
   5041                 try parking_sleep.sleep(.{ .duration = .{
   5042                     .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
   5043                     .clock = .awake,
   5044                 } });
   5045                 attempt += 1;
   5046                 syscall = try .start();
   5047                 continue;
   5048             },
   5049             .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
   5050             else => |rc| return syscall.unexpectedNtstatus(rc),
   5051         }
   5052     };
   5053     errdefer w.CloseHandle(handle);
   5054 
   5055     const exclusive = switch (flags.lock) {
   5056         .none => return .{
   5057             .handle = handle,
   5058             .flags = .{ .nonblocking = false },
   5059         },
   5060         .shared => false,
   5061         .exclusive => true,
   5062     };
   5063     syscall = try .start();
   5064     while (true) switch (w.ntdll.NtLockFile(
   5065         handle,
   5066         null,
   5067         null,
   5068         null,
   5069         &io_status_block,
   5070         &windows_lock_range_off,
   5071         &windows_lock_range_len,
   5072         null,
   5073         @intFromBool(flags.lock_nonblocking),
   5074         @intFromBool(exclusive),
   5075     )) {
   5076         .SUCCESS => break syscall.finish(),
   5077         .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
   5078         .LOCK_NOT_GRANTED => return syscall.fail(error.WouldBlock),
   5079         .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
   5080         else => |status| return syscall.unexpectedNtstatus(status),
   5081     };
   5082     return .{
   5083         .handle = handle,
   5084         .flags = .{ .nonblocking = false },
   5085     };
   5086 }
   5087 
   5088 fn dirOpenFileWasi(
   5089     userdata: ?*anyopaque,
   5090     dir: Dir,
   5091     sub_path: []const u8,
   5092     flags: File.OpenFlags,
   5093 ) File.OpenError!File {
   5094     if (builtin.link_libc) return dirOpenFilePosix(userdata, dir, sub_path, flags);
   5095     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5096     const wasi = std.os.wasi;
   5097     var base: std.os.wasi.rights_t = .{};
   5098     // POLL_FD_READWRITE only grants extra rights if the corresponding FD_READ and/or FD_WRITE
   5099     // is also set.
   5100     if (flags.isRead()) {
   5101         base.FD_READ = true;
   5102         base.FD_TELL = true;
   5103         base.FD_SEEK = true;
   5104         base.FD_FILESTAT_GET = true;
   5105         base.POLL_FD_READWRITE = true;
   5106     }
   5107     if (flags.isWrite()) {
   5108         base.FD_WRITE = true;
   5109         base.FD_TELL = true;
   5110         base.FD_SEEK = true;
   5111         base.FD_DATASYNC = true;
   5112         base.FD_FDSTAT_SET_FLAGS = true;
   5113         base.FD_SYNC = true;
   5114         base.FD_ALLOCATE = true;
   5115         base.FD_ADVISE = true;
   5116         base.FD_FILESTAT_SET_TIMES = true;
   5117         base.FD_FILESTAT_SET_SIZE = true;
   5118         base.POLL_FD_READWRITE = true;
   5119     }
   5120     const lookup_flags: wasi.lookupflags_t = .{};
   5121     const oflags: wasi.oflags_t = .{};
   5122     const inheriting: wasi.rights_t = .{};
   5123     const fdflags: wasi.fdflags_t = .{};
   5124     var fd: posix.fd_t = undefined;
   5125     const syscall: Syscall = try .start();
   5126     while (true) {
   5127         switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, inheriting, fdflags, &fd)) {
   5128             .SUCCESS => {
   5129                 syscall.finish();
   5130                 break;
   5131             },
   5132             .INTR => {
   5133                 try syscall.checkCancel();
   5134                 continue;
   5135             },
   5136             else => |e| {
   5137                 syscall.finish();
   5138                 switch (e) {
   5139                     .FAULT => |err| return errnoBug(err),
   5140                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   5141                     .ACCES => return error.AccessDenied,
   5142                     .FBIG => return error.FileTooBig,
   5143                     .OVERFLOW => return error.FileTooBig,
   5144                     .ISDIR => return error.IsDir,
   5145                     .LOOP => return error.SymLinkLoop,
   5146                     .MFILE => return error.ProcessFdQuotaExceeded,
   5147                     .NFILE => return error.SystemFdQuotaExceeded,
   5148                     .NODEV => return error.NoDevice,
   5149                     .NOENT => return error.FileNotFound,
   5150                     .NOMEM => return error.SystemResources,
   5151                     .NOTDIR => return error.NotDir,
   5152                     .PERM => return error.PermissionDenied,
   5153                     .BUSY => return error.DeviceBusy,
   5154                     .NOTCAPABLE => return error.AccessDenied,
   5155                     .NAMETOOLONG => return error.NameTooLong,
   5156                     .INVAL => return error.BadPathName,
   5157                     .ILSEQ => return error.BadPathName,
   5158                     else => |err| return posix.unexpectedErrno(err),
   5159                 }
   5160             },
   5161         }
   5162     }
   5163     errdefer closeFd(fd);
   5164 
   5165     if (!flags.allow_directory) {
   5166         const is_dir = is_dir: {
   5167             const stat = fileStat(t, .{ .handle = fd, .flags = .{ .nonblocking = false } }) catch |err| switch (err) {
   5168                 // The directory-ness is either unknown or unknowable
   5169                 error.Streaming => break :is_dir false,
   5170                 else => |e| return e,
   5171             };
   5172             break :is_dir stat.kind == .directory;
   5173         };
   5174         if (is_dir) return error.IsDir;
   5175     }
   5176 
   5177     return .{
   5178         .handle = fd,
   5179         .flags = .{ .nonblocking = false },
   5180     };
   5181 }
   5182 
   5183 const dirOpenDir = switch (native_os) {
   5184     .wasi => dirOpenDirWasi,
   5185     .haiku => dirOpenDirHaiku,
   5186     else => dirOpenDirPosix,
   5187 };
   5188 
   5189 /// This function is also used for WASI when libc is linked.
   5190 fn dirOpenDirPosix(
   5191     userdata: ?*anyopaque,
   5192     dir: Dir,
   5193     sub_path: []const u8,
   5194     options: Dir.OpenOptions,
   5195 ) Dir.OpenError!Dir {
   5196     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5197     _ = t;
   5198 
   5199     if (is_windows) {
   5200         const sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
   5201         return dirOpenDirWindows(dir, sub_path_w.span(), options);
   5202     }
   5203 
   5204     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   5205     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   5206 
   5207     var flags: posix.O = switch (native_os) {
   5208         .wasi => .{
   5209             .read = true,
   5210             .NOFOLLOW = !options.follow_symlinks,
   5211             .DIRECTORY = true,
   5212         },
   5213         else => .{
   5214             .ACCMODE = .RDONLY,
   5215             .NOFOLLOW = !options.follow_symlinks,
   5216             .DIRECTORY = true,
   5217             .CLOEXEC = true,
   5218         },
   5219     };
   5220 
   5221     if (@hasField(posix.O, "PATH") and !options.iterate)
   5222         flags.PATH = true;
   5223 
   5224     const mode: posix.mode_t = 0;
   5225 
   5226     const syscall: Syscall = try .start();
   5227     while (true) {
   5228         const rc = openat_sym(dir.handle, sub_path_posix, flags, mode);
   5229         switch (posix.errno(rc)) {
   5230             .SUCCESS => {
   5231                 syscall.finish();
   5232                 return .{ .handle = @intCast(rc) };
   5233             },
   5234             .INTR => {
   5235                 try syscall.checkCancel();
   5236                 continue;
   5237             },
   5238             .INVAL => return syscall.fail(error.BadPathName),
   5239             .ACCES => return syscall.fail(error.AccessDenied),
   5240             .LOOP => return syscall.fail(error.SymLinkLoop),
   5241             .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
   5242             .NAMETOOLONG => return syscall.fail(error.NameTooLong),
   5243             .NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
   5244             .NODEV => return syscall.fail(error.NoDevice),
   5245             .NOENT => return syscall.fail(error.FileNotFound),
   5246             .NOMEM => return syscall.fail(error.SystemResources),
   5247             .NOTDIR => return syscall.fail(error.NotDir),
   5248             .PERM => return syscall.fail(error.PermissionDenied),
   5249             .NXIO => return syscall.fail(error.NoDevice),
   5250             .ILSEQ => return syscall.fail(error.BadPathName),
   5251             .FAULT => |err| return syscall.errnoBug(err),
   5252             .BADF => |err| return syscall.errnoBug(err), // File descriptor used after closed.
   5253             .BUSY => |err| return syscall.errnoBug(err), // O_EXCL not passed
   5254             else => |err| return syscall.unexpectedErrno(err),
   5255         }
   5256     }
   5257 }
   5258 
   5259 fn dirOpenDirHaiku(
   5260     userdata: ?*anyopaque,
   5261     dir: Dir,
   5262     sub_path: []const u8,
   5263     options: Dir.OpenOptions,
   5264 ) Dir.OpenError!Dir {
   5265     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5266     _ = t;
   5267 
   5268     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   5269     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   5270 
   5271     _ = options;
   5272 
   5273     const syscall: Syscall = try .start();
   5274     while (true) {
   5275         const rc = posix.system._kern_open_dir(dir.handle, sub_path_posix);
   5276         if (rc >= 0) {
   5277             syscall.finish();
   5278             return .{ .handle = rc };
   5279         }
   5280         switch (@as(posix.E, @enumFromInt(rc))) {
   5281             .INTR => {
   5282                 try syscall.checkCancel();
   5283                 continue;
   5284             },
   5285             else => |e| {
   5286                 syscall.finish();
   5287                 switch (e) {
   5288                     .FAULT => |err| return errnoBug(err),
   5289                     .INVAL => |err| return errnoBug(err),
   5290                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   5291                     .ACCES => return error.AccessDenied,
   5292                     .LOOP => return error.SymLinkLoop,
   5293                     .MFILE => return error.ProcessFdQuotaExceeded,
   5294                     .NAMETOOLONG => return error.NameTooLong,
   5295                     .NFILE => return error.SystemFdQuotaExceeded,
   5296                     .NODEV => return error.NoDevice,
   5297                     .NOENT => return error.FileNotFound,
   5298                     .NOMEM => return error.SystemResources,
   5299                     .NOTDIR => return error.NotDir,
   5300                     .PERM => return error.PermissionDenied,
   5301                     .BUSY => return error.DeviceBusy,
   5302                     else => |err| return posix.unexpectedErrno(err),
   5303                 }
   5304             },
   5305         }
   5306     }
   5307 }
   5308 
   5309 pub fn dirOpenDirWindows(
   5310     dir: Dir,
   5311     sub_path_w: []const u16,
   5312     options: Dir.OpenOptions,
   5313 ) Dir.OpenError!Dir {
   5314     const w = windows;
   5315 
   5316     var io_status_block: w.IO_STATUS_BLOCK = undefined;
   5317     var result: Dir = .{ .handle = undefined };
   5318 
   5319     const attr: w.OBJECT.ATTRIBUTES = .{
   5320         .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else dir.handle,
   5321         .ObjectName = @constCast(&w.UNICODE_STRING.init(sub_path_w)),
   5322     };
   5323 
   5324     const syscall: Syscall = try .start();
   5325     while (true) switch (w.ntdll.NtCreateFile(
   5326         &result.handle,
   5327         // TODO remove some of these flags if options.access_sub_paths is false
   5328         .{
   5329             .SPECIFIC = .{ .FILE_DIRECTORY = .{
   5330                 .LIST = options.iterate,
   5331                 .READ_EA = true,
   5332                 .TRAVERSE = true,
   5333                 .READ_ATTRIBUTES = true,
   5334             } },
   5335             .STANDARD = .{
   5336                 .RIGHTS = .READ,
   5337                 .SYNCHRONIZE = true,
   5338             },
   5339         },
   5340         &attr,
   5341         &io_status_block,
   5342         null,
   5343         .{ .NORMAL = true },
   5344         .VALID_FLAGS,
   5345         .OPEN,
   5346         .{
   5347             .DIRECTORY_FILE = true,
   5348             .IO = .SYNCHRONOUS_NONALERT,
   5349             .OPEN_FOR_BACKUP_INTENT = true,
   5350             .OPEN_REPARSE_POINT = !options.follow_symlinks,
   5351         },
   5352         null,
   5353         0,
   5354     )) {
   5355         .SUCCESS => {
   5356             syscall.finish();
   5357             return result;
   5358         },
   5359         .CANCELLED => {
   5360             try syscall.checkCancel();
   5361             continue;
   5362         },
   5363         .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
   5364         .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
   5365         .OBJECT_NAME_COLLISION => |err| return w.statusBug(err),
   5366         .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
   5367         .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
   5368         // This can happen if the directory has 'List folder contents' permission set to 'Deny'
   5369         // and the directory is trying to be opened for iteration.
   5370         .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   5371         .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
   5372         else => |rc| return syscall.unexpectedNtstatus(rc),
   5373     };
   5374 }
   5375 
   5376 fn dirClose(userdata: ?*anyopaque, dirs: []const Dir) void {
   5377     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5378     _ = t;
   5379     for (dirs) |dir| {
   5380         if (is_windows) {
   5381             windows.CloseHandle(dir.handle);
   5382         } else {
   5383             closeFd(dir.handle);
   5384         }
   5385     }
   5386 }
   5387 
   5388 const dirRead = switch (native_os) {
   5389     .linux => dirReadLinux,
   5390     .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => dirReadDarwin,
   5391     .freebsd, .netbsd, .dragonfly, .openbsd => dirReadBsd,
   5392     .illumos => dirReadIllumos,
   5393     .haiku => dirReadHaiku,
   5394     .windows => dirReadWindows,
   5395     .wasi => dirReadWasi,
   5396     else => dirReadUnimplemented,
   5397 };
   5398 
   5399 fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   5400     const linux = std.os.linux;
   5401     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5402     _ = t;
   5403     var buffer_index: usize = 0;
   5404     while (buffer.len - buffer_index != 0) {
   5405         if (dr.end - dr.index == 0) {
   5406             // Refill the buffer, unless we've already created references to
   5407             // buffered data.
   5408             if (buffer_index != 0) break;
   5409             if (dr.state == .reset) {
   5410                 posixSeekTo(dr.dir.handle, 0) catch |err| switch (err) {
   5411                     error.Unseekable => return error.Unexpected,
   5412                     else => |e| return e,
   5413                 };
   5414                 dr.state = .reading;
   5415             }
   5416             const syscall: Syscall = try .start();
   5417             const n = while (true) {
   5418                 const rc = linux.getdents64(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
   5419                 switch (linux.errno(rc)) {
   5420                     .SUCCESS => {
   5421                         syscall.finish();
   5422                         break rc;
   5423                     },
   5424                     .INTR => {
   5425                         try syscall.checkCancel();
   5426                         continue;
   5427                     },
   5428                     else => |e| {
   5429                         syscall.finish();
   5430                         switch (e) {
   5431                             .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.
   5432                             .FAULT => |err| return errnoBug(err),
   5433                             .NOTDIR => |err| return errnoBug(err),
   5434                             // To be consistent across platforms, iteration
   5435                             // ends if the directory being iterated is deleted
   5436                             // during iteration. This matches the behavior of
   5437                             // non-Linux, non-WASI UNIX platforms.
   5438                             .NOENT => {
   5439                                 dr.state = .finished;
   5440                                 return 0;
   5441                             },
   5442                             // This can occur when reading /proc/$PID/net, or
   5443                             // if the provided buffer is too small. Neither
   5444                             // scenario is intended to be handled by this API.
   5445                             .INVAL => return error.Unexpected,
   5446                             .ACCES => return error.AccessDenied, // Lacking permission to iterate this directory.
   5447                             else => |err| return posix.unexpectedErrno(err),
   5448                         }
   5449                     },
   5450                 }
   5451             };
   5452             if (n == 0) {
   5453                 dr.state = .finished;
   5454                 return 0;
   5455             }
   5456             dr.index = 0;
   5457             dr.end = n;
   5458         }
   5459         // Linux aligns the header by padding after the null byte of the name
   5460         // to align the next entry. This means we can find the end of the name
   5461         // by looking at only the 8 bytes before the next record. However since
   5462         // file names are usually short it's better to keep the machine code
   5463         // simpler.
   5464         //
   5465         // Furthermore, I observed qemu user mode to not align this struct, so
   5466         // this code makes the conservative choice to not assume alignment.
   5467         const linux_entry: *align(1) linux.dirent64 = @ptrCast(&dr.buffer[dr.index]);
   5468         const next_index = dr.index + linux_entry.reclen;
   5469         dr.index = next_index;
   5470         const name_ptr: [*]u8 = &linux_entry.name;
   5471         const padded_name = name_ptr[0 .. linux_entry.reclen - @offsetOf(linux.dirent64, "name")];
   5472         const name_len = std.mem.findScalar(u8, padded_name, 0).?;
   5473         const name = name_ptr[0..name_len :0];
   5474 
   5475         if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;
   5476 
   5477         const entry_kind: File.Kind = switch (linux_entry.type) {
   5478             linux.DT.BLK => .block_device,
   5479             linux.DT.CHR => .character_device,
   5480             linux.DT.DIR => .directory,
   5481             linux.DT.FIFO => .named_pipe,
   5482             linux.DT.LNK => .sym_link,
   5483             linux.DT.REG => .file,
   5484             linux.DT.SOCK => .unix_domain_socket,
   5485             else => .unknown,
   5486         };
   5487         buffer[buffer_index] = .{
   5488             .name = name,
   5489             .kind = entry_kind,
   5490             .inode = linux_entry.ino,
   5491         };
   5492         buffer_index += 1;
   5493     }
   5494     return buffer_index;
   5495 }
   5496 
   5497 fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   5498     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5499     _ = t;
   5500     const Header = extern struct {
   5501         seek: i64,
   5502     };
   5503     const header: *Header = @ptrCast(dr.buffer.ptr);
   5504     const header_end: usize = @sizeOf(Header);
   5505     if (dr.index < header_end) {
   5506         // Initialize header.
   5507         dr.index = header_end;
   5508         dr.end = header_end;
   5509         header.* = .{ .seek = 0 };
   5510     }
   5511     var buffer_index: usize = 0;
   5512     while (buffer.len - buffer_index != 0) {
   5513         if (dr.end - dr.index == 0) {
   5514             // Refill the buffer, unless we've already created references to
   5515             // buffered data.
   5516             if (buffer_index != 0) break;
   5517             if (dr.state == .reset) {
   5518                 posixSeekTo(dr.dir.handle, 0) catch |err| switch (err) {
   5519                     error.Unseekable => return error.Unexpected,
   5520                     else => |e| return e,
   5521                 };
   5522                 dr.state = .reading;
   5523             }
   5524             const dents_buffer = dr.buffer[header_end..];
   5525             const syscall: Syscall = try .start();
   5526             const n: usize = while (true) {
   5527                 const rc = posix.system.getdirentries(dr.dir.handle, dents_buffer.ptr, dents_buffer.len, &header.seek);
   5528                 switch (posix.errno(rc)) {
   5529                     .SUCCESS => {
   5530                         syscall.finish();
   5531                         break @intCast(rc);
   5532                     },
   5533                     .INTR => {
   5534                         try syscall.checkCancel();
   5535                         continue;
   5536                     },
   5537                     else => |e| {
   5538                         syscall.finish();
   5539                         switch (e) {
   5540                             .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.
   5541                             .FAULT => |err| return errnoBug(err),
   5542                             .NOTDIR => |err| return errnoBug(err),
   5543                             .INVAL => |err| return errnoBug(err),
   5544                             else => |err| return posix.unexpectedErrno(err),
   5545                         }
   5546                     },
   5547                 }
   5548             };
   5549             if (n == 0) {
   5550                 dr.state = .finished;
   5551                 return 0;
   5552             }
   5553             dr.index = header_end;
   5554             dr.end = header_end + n;
   5555         }
   5556         const darwin_entry = @as(*align(1) posix.system.dirent, @ptrCast(&dr.buffer[dr.index]));
   5557         const next_index = dr.index + darwin_entry.reclen;
   5558         dr.index = next_index;
   5559 
   5560         const name = @as([*]u8, @ptrCast(&darwin_entry.name))[0..darwin_entry.namlen];
   5561         if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..") or (darwin_entry.ino == 0))
   5562             continue;
   5563 
   5564         const entry_kind: File.Kind = switch (darwin_entry.type) {
   5565             posix.DT.BLK => .block_device,
   5566             posix.DT.CHR => .character_device,
   5567             posix.DT.DIR => .directory,
   5568             posix.DT.FIFO => .named_pipe,
   5569             posix.DT.LNK => .sym_link,
   5570             posix.DT.REG => .file,
   5571             posix.DT.SOCK => .unix_domain_socket,
   5572             posix.DT.WHT => .whiteout,
   5573             else => .unknown,
   5574         };
   5575         buffer[buffer_index] = .{
   5576             .name = name,
   5577             .kind = entry_kind,
   5578             .inode = darwin_entry.ino,
   5579         };
   5580         buffer_index += 1;
   5581     }
   5582     return buffer_index;
   5583 }
   5584 
   5585 fn dirReadBsd(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   5586     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5587     _ = t;
   5588     var buffer_index: usize = 0;
   5589     while (buffer.len - buffer_index != 0) {
   5590         if (dr.end - dr.index == 0) {
   5591             // Refill the buffer, unless we've already created references to
   5592             // buffered data.
   5593             if (buffer_index != 0) break;
   5594             if (dr.state == .reset) {
   5595                 posixSeekTo(dr.dir.handle, 0) catch |err| switch (err) {
   5596                     error.Unseekable => return error.Unexpected,
   5597                     else => |e| return e,
   5598                 };
   5599                 dr.state = .reading;
   5600             }
   5601             const syscall: Syscall = try .start();
   5602             const n: usize = while (true) {
   5603                 const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
   5604                 switch (posix.errno(rc)) {
   5605                     .SUCCESS => {
   5606                         syscall.finish();
   5607                         break @intCast(rc);
   5608                     },
   5609                     .INTR => {
   5610                         try syscall.checkCancel();
   5611                         continue;
   5612                     },
   5613                     else => |e| {
   5614                         syscall.finish();
   5615                         switch (e) {
   5616                             .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability
   5617                             .FAULT => |err| return errnoBug(err),
   5618                             .NOTDIR => |err| return errnoBug(err),
   5619                             .INVAL => |err| return errnoBug(err),
   5620                             // Introduced in freebsd 13.2: directory unlinked
   5621                             // but still open. To be consistent, iteration ends
   5622                             // if the directory being iterated is deleted
   5623                             // during iteration.
   5624                             .NOENT => {
   5625                                 dr.state = .finished;
   5626                                 return 0;
   5627                             },
   5628                             else => |err| return posix.unexpectedErrno(err),
   5629                         }
   5630                     },
   5631                 }
   5632             };
   5633             if (n == 0) {
   5634                 dr.state = .finished;
   5635                 return 0;
   5636             }
   5637             dr.index = 0;
   5638             dr.end = n;
   5639         }
   5640         const bsd_entry = @as(*align(1) posix.system.dirent, @ptrCast(&dr.buffer[dr.index]));
   5641         const next_index = dr.index +
   5642             if (@hasField(posix.system.dirent, "reclen")) bsd_entry.reclen else bsd_entry.reclen();
   5643         dr.index = next_index;
   5644 
   5645         const name = @as([*]u8, @ptrCast(&bsd_entry.name))[0..bsd_entry.namlen];
   5646 
   5647         const skip_zero_fileno = switch (native_os) {
   5648             // fileno=0 is used to mark invalid entries or deleted files.
   5649             .openbsd, .netbsd => true,
   5650             else => false,
   5651         };
   5652         if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..") or
   5653             (skip_zero_fileno and bsd_entry.fileno == 0))
   5654         {
   5655             continue;
   5656         }
   5657 
   5658         const entry_kind: File.Kind = switch (bsd_entry.type) {
   5659             posix.DT.BLK => .block_device,
   5660             posix.DT.CHR => .character_device,
   5661             posix.DT.DIR => .directory,
   5662             posix.DT.FIFO => .named_pipe,
   5663             posix.DT.LNK => .sym_link,
   5664             posix.DT.REG => .file,
   5665             posix.DT.SOCK => .unix_domain_socket,
   5666             posix.DT.WHT => .whiteout,
   5667             else => .unknown,
   5668         };
   5669         buffer[buffer_index] = .{
   5670             .name = name,
   5671             .kind = entry_kind,
   5672             .inode = bsd_entry.fileno,
   5673         };
   5674         buffer_index += 1;
   5675     }
   5676     return buffer_index;
   5677 }
   5678 
   5679 fn dirReadIllumos(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   5680     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5681     _ = t;
   5682     var buffer_index: usize = 0;
   5683     while (buffer.len - buffer_index != 0) {
   5684         if (dr.end - dr.index == 0) {
   5685             // Refill the buffer, unless we've already created references to
   5686             // buffered data.
   5687             if (buffer_index != 0) break;
   5688             if (dr.state == .reset) {
   5689                 posixSeekTo(dr.dir.handle, 0) catch |err| switch (err) {
   5690                     error.Unseekable => return error.Unexpected,
   5691                     else => |e| return e,
   5692                 };
   5693                 dr.state = .reading;
   5694             }
   5695             const syscall: Syscall = try .start();
   5696             const n: usize = while (true) {
   5697                 const rc = posix.system.getdents(dr.dir.handle, dr.buffer.ptr, dr.buffer.len);
   5698                 switch (posix.errno(rc)) {
   5699                     .SUCCESS => {
   5700                         syscall.finish();
   5701                         break rc;
   5702                     },
   5703                     .INTR => {
   5704                         try syscall.checkCancel();
   5705                         continue;
   5706                     },
   5707                     else => |e| {
   5708                         syscall.finish();
   5709                         switch (e) {
   5710                             .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability
   5711                             .FAULT => |err| return errnoBug(err),
   5712                             .NOTDIR => |err| return errnoBug(err),
   5713                             .INVAL => |err| return errnoBug(err),
   5714                             else => |err| return posix.unexpectedErrno(err),
   5715                         }
   5716                     },
   5717                 }
   5718             };
   5719             if (n == 0) {
   5720                 dr.state = .finished;
   5721                 return 0;
   5722             }
   5723             dr.index = 0;
   5724             dr.end = n;
   5725         }
   5726         const entry = @as(*align(1) posix.system.dirent, @ptrCast(&dr.buffer[dr.index]));
   5727         const next_index = dr.index + entry.reclen;
   5728         dr.index = next_index;
   5729 
   5730         const name = std.mem.sliceTo(@as([*:0]u8, @ptrCast(&entry.name)), 0);
   5731         if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) continue;
   5732 
   5733         // illumos dirent doesn't expose type, so we have to call stat to get it.
   5734         const stat = try posixStatFile(dr.dir.handle, name, posix.AT.SYMLINK_NOFOLLOW);
   5735 
   5736         buffer[buffer_index] = .{
   5737             .name = name,
   5738             .kind = stat.kind,
   5739             .inode = entry.ino,
   5740         };
   5741         buffer_index += 1;
   5742     }
   5743     return buffer_index;
   5744 }
   5745 
   5746 fn dirReadHaiku(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   5747     _ = userdata;
   5748     _ = dr;
   5749     _ = buffer;
   5750     @panic("TODO implement dirReadHaiku");
   5751 }
   5752 
   5753 fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   5754     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5755     _ = t;
   5756     const w = windows;
   5757 
   5758     // We want to be able to use the `dr.buffer` for both the NtQueryDirectoryFile call (which
   5759     // returns WTF-16 names) *and* as a buffer for storing those WTF-16 names as WTF-8 to be able
   5760     // to return them in `Dir.Entry.name`. However, the problem that needs to be overcome in order to do
   5761     // that is that each WTF-16 code unit can be encoded as a maximum of 3 WTF-8 bytes, which means
   5762     // that it's not guaranteed that the memory used for the WTF-16 name will be sufficient
   5763     // for the WTF-8 encoding of the same name (for example, € is encoded as one WTF-16 code unit,
   5764     // [2 bytes] but encoded in WTF-8 as 3 bytes).
   5765     //
   5766     // The approach taken here is to "reserve" enough space in the `dr.buffer` to ensure that
   5767     // at least one entry with the maximum possible WTF-8 name length can be stored without clobbering
   5768     // any entries that follow it. That is, we determine how much space is needed to allow that,
   5769     // and then only provide the remaining portion of `dr.buffer` to the NtQueryDirectoryFile
   5770     // call. The WTF-16 names can then be safely converted using the full `dr.buffer` slice, making
   5771     // sure that each name can only potentially overwrite the data of its own entry.
   5772     //
   5773     // The worst case, where an entry's name is both the maximum length of a component and
   5774     // made up entirely of code points that are encoded as one WTF-16 code unit/three WTF-8 bytes,
   5775     // would therefore look like the diagram below, and only one entry would be able to be returned:
   5776     //
   5777     //     |   reserved  | remaining unreserved buffer |
   5778     //                   | entry 1 | entry 2 |   ...   |
   5779     //     | wtf-8 name of entry 1 |
   5780     //
   5781     // However, in the average case we will be able to store more than one WTF-8 name at a time in the
   5782     // available buffer and therefore we will be able to populate more than one `Dir.Entry` at a time.
   5783     // That might look something like this (where name 1, name 2, etc are the converted WTF-8 names):
   5784     //
   5785     //     |   reserved  | remaining unreserved buffer |
   5786     //                   | entry 1 | entry 2 |   ...   |
   5787     //     | name 1 | name 2 | name 3 | name 4 |  ...  |
   5788     //
   5789     // Note: More than the minimum amount of space could be reserved to make the "worst case"
   5790     // less likely, but since the worst-case also requires a maximum length component to matter,
   5791     // it's unlikely for it to become a problem in normal scenarios even if all names on the filesystem
   5792     // are made up of non-ASCII characters that have the "one WTF-16 code unit <-> three WTF-8 bytes"
   5793     // property (e.g. code points >= U+0800 and <= U+FFFF), as it's unlikely for a significant
   5794     // number of components to be maximum length.
   5795 
   5796     // We need `3 * NAME_MAX` bytes to store a max-length component as WTF-8 safely.
   5797     // Because needing to store a max-length component depends on a `FileName` *with* the maximum
   5798     // component length, we know that the corresponding populated `FILE_BOTH_DIR_INFORMATION` will
   5799     // be of size `@sizeOf(w.FILE_BOTH_DIR_INFORMATION) + 2 * NAME_MAX` bytes, so we only need to
   5800     // reserve enough to get us to up to having `3 * NAME_MAX` bytes available when taking into account
   5801     // that we have the ability to write over top of the reserved memory + the full footprint of that
   5802     // particular `FILE_BOTH_DIR_INFORMATION`.
   5803     const max_info_len = @sizeOf(w.FILE_BOTH_DIR_INFORMATION) + w.NAME_MAX * 2;
   5804     const info_align = @alignOf(w.FILE_BOTH_DIR_INFORMATION);
   5805     const reserve_needed = std.mem.alignForward(usize, Dir.max_name_bytes, info_align) - max_info_len;
   5806     const unreserved_start = std.mem.alignForward(usize, reserve_needed, info_align);
   5807     const unreserved_buffer = dr.buffer[unreserved_start..];
   5808     // This is enforced by `Dir.Reader`
   5809     assert(unreserved_buffer.len >= max_info_len);
   5810 
   5811     var name_index: usize = 0;
   5812     var buffer_index: usize = 0;
   5813     while (buffer.len - buffer_index != 0) {
   5814         if (dr.end - dr.index == 0) {
   5815             // Refill the buffer, unless we've already created references to
   5816             // buffered data.
   5817             if (buffer_index != 0) break;
   5818 
   5819             var io_status_block: w.IO_STATUS_BLOCK = undefined;
   5820             const syscall: Syscall = try .start();
   5821             const rc = while (true) switch (w.ntdll.NtQueryDirectoryFile(
   5822                 dr.dir.handle,
   5823                 null,
   5824                 null,
   5825                 null,
   5826                 &io_status_block,
   5827                 unreserved_buffer.ptr,
   5828                 std.math.lossyCast(w.ULONG, unreserved_buffer.len),
   5829                 .BothDirectory,
   5830                 w.FALSE,
   5831                 null,
   5832                 @intFromBool(dr.state == .reset),
   5833             )) {
   5834                 .CANCELLED => {
   5835                     try syscall.checkCancel();
   5836                     continue;
   5837                 },
   5838                 else => |rc| {
   5839                     syscall.finish();
   5840                     break rc;
   5841                 },
   5842             };
   5843             dr.state = .reading;
   5844             if (io_status_block.Information == 0) {
   5845                 dr.state = .finished;
   5846                 return 0;
   5847             }
   5848             dr.index = 0;
   5849             dr.end = io_status_block.Information;
   5850             switch (rc) {
   5851                 .SUCCESS => {},
   5852                 .ACCESS_DENIED => return error.AccessDenied, // Double-check that the Dir was opened with iteration ability
   5853                 else => return w.unexpectedStatus(rc),
   5854             }
   5855         }
   5856 
   5857         // While the official API docs guarantee FILE_BOTH_DIR_INFORMATION to be aligned properly
   5858         // this may not always be the case (e.g. due to faulty VM/sandboxing tools)
   5859         const dir_info: *align(2) w.FILE_BOTH_DIR_INFORMATION = @ptrCast(@alignCast(&unreserved_buffer[dr.index]));
   5860         const backtrack_index = dr.index;
   5861         if (dir_info.NextEntryOffset != 0) {
   5862             dr.index += dir_info.NextEntryOffset;
   5863         } else {
   5864             dr.index = dr.end;
   5865         }
   5866 
   5867         const name_wtf16le = @as([*]u16, @ptrCast(&dir_info.FileName))[0 .. dir_info.FileNameLength / 2];
   5868 
   5869         if (std.mem.eql(u16, name_wtf16le, &[_]u16{'.'}) or std.mem.eql(u16, name_wtf16le, &[_]u16{ '.', '.' })) {
   5870             continue;
   5871         }
   5872 
   5873         // Read any relevant information from the `dir_info` now since it's possible the WTF-8
   5874         // name will overwrite it.
   5875         const kind: File.Kind = blk: {
   5876             const attrs = dir_info.FileAttributes;
   5877             if (attrs.REPARSE_POINT) break :blk .sym_link;
   5878             if (attrs.DIRECTORY) break :blk .directory;
   5879             break :blk .file;
   5880         };
   5881         const inode: File.INode = dir_info.FileIndex;
   5882 
   5883         // If there's no more space for WTF-8 names without bleeding over into
   5884         // the remaining unprocessed entries, then backtrack and return what we have so far.
   5885         if (name_index + std.unicode.calcWtf8Len(name_wtf16le) > unreserved_start + dr.index) {
   5886             // We should always be able to fit at least one entry into the buffer no matter what
   5887             assert(buffer_index != 0);
   5888             dr.index = backtrack_index;
   5889             break;
   5890         }
   5891 
   5892         const name_buf = dr.buffer[name_index..];
   5893         const name_wtf8_len = std.unicode.wtf16LeToWtf8(name_buf, name_wtf16le);
   5894         const name_wtf8 = name_buf[0..name_wtf8_len];
   5895         name_index += name_wtf8_len;
   5896 
   5897         buffer[buffer_index] = .{
   5898             .name = name_wtf8,
   5899             .kind = kind,
   5900             .inode = inode,
   5901         };
   5902         buffer_index += 1;
   5903     }
   5904 
   5905     return buffer_index;
   5906 }
   5907 
   5908 fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   5909     // We intentinally use fd_readdir even when linked with libc, since its
   5910     // implementation is exactly the same as below, and we avoid the code
   5911     // complexity here.
   5912     const wasi = std.os.wasi;
   5913     const t: *Threaded = @ptrCast(@alignCast(userdata));
   5914     _ = t;
   5915     const Header = extern struct {
   5916         cookie: u64,
   5917     };
   5918     const header: *align(@alignOf(usize)) Header = @ptrCast(dr.buffer.ptr);
   5919     const header_end: usize = @sizeOf(Header);
   5920     if (dr.index < header_end) {
   5921         // Initialize header.
   5922         dr.index = header_end;
   5923         dr.end = header_end;
   5924         header.* = .{ .cookie = wasi.DIRCOOKIE_START };
   5925     }
   5926     var buffer_index: usize = 0;
   5927     while (buffer.len - buffer_index != 0) {
   5928         // According to the WASI spec, the last entry might be truncated, so we
   5929         // need to check if the remaining buffer contains the whole dirent.
   5930         if (dr.end - dr.index < @sizeOf(wasi.dirent_t)) {
   5931             // Refill the buffer, unless we've already created references to
   5932             // buffered data.
   5933             if (buffer_index != 0) break;
   5934             if (dr.state == .reset) {
   5935                 header.* = .{ .cookie = wasi.DIRCOOKIE_START };
   5936                 dr.state = .reading;
   5937             }
   5938             const dents_buffer = dr.buffer[header_end..];
   5939             var n: usize = undefined;
   5940             const syscall: Syscall = try .start();
   5941             while (true) {
   5942                 switch (wasi.fd_readdir(dr.dir.handle, dents_buffer.ptr, dents_buffer.len, header.cookie, &n)) {
   5943                     .SUCCESS => {
   5944                         syscall.finish();
   5945                         break;
   5946                     },
   5947                     .INTR => {
   5948                         try syscall.checkCancel();
   5949                         continue;
   5950                     },
   5951                     else => |e| {
   5952                         syscall.finish();
   5953                         switch (e) {
   5954                             .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability.
   5955                             .FAULT => |err| return errnoBug(err),
   5956                             .NOTDIR => |err| return errnoBug(err),
   5957                             .INVAL => |err| return errnoBug(err),
   5958                             // To be consistent across platforms, iteration
   5959                             // ends if the directory being iterated is deleted
   5960                             // during iteration. This matches the behavior of
   5961                             // non-Linux, non-WASI UNIX platforms.
   5962                             .NOENT => {
   5963                                 dr.state = .finished;
   5964                                 return 0;
   5965                             },
   5966                             .NOTCAPABLE => return error.AccessDenied,
   5967                             else => |err| return posix.unexpectedErrno(err),
   5968                         }
   5969                     },
   5970                 }
   5971             }
   5972             if (n == 0) {
   5973                 dr.state = .finished;
   5974                 return 0;
   5975             }
   5976             dr.index = header_end;
   5977             dr.end = header_end + n;
   5978         }
   5979         const entry: *align(1) wasi.dirent_t = @ptrCast(&dr.buffer[dr.index]);
   5980         const entry_size = @sizeOf(wasi.dirent_t);
   5981         const name_index = dr.index + entry_size;
   5982         if (name_index + entry.namlen > dr.end) {
   5983             // This case, the name is truncated, so we need to call readdir to store the entire name.
   5984             dr.end = dr.index; // Force fd_readdir in the next loop.
   5985             continue;
   5986         }
   5987         const name = dr.buffer[name_index..][0..entry.namlen];
   5988         const next_index = name_index + entry.namlen;
   5989         dr.index = next_index;
   5990         header.cookie = entry.next;
   5991 
   5992         if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, ".."))
   5993             continue;
   5994 
   5995         const entry_kind: File.Kind = switch (entry.type) {
   5996             .BLOCK_DEVICE => .block_device,
   5997             .CHARACTER_DEVICE => .character_device,
   5998             .DIRECTORY => .directory,
   5999             .SYMBOLIC_LINK => .sym_link,
   6000             .REGULAR_FILE => .file,
   6001             .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket,
   6002             else => .unknown,
   6003         };
   6004         buffer[buffer_index] = .{
   6005             .name = name,
   6006             .kind = entry_kind,
   6007             .inode = entry.ino,
   6008         };
   6009         buffer_index += 1;
   6010     }
   6011     return buffer_index;
   6012 }
   6013 
   6014 fn dirReadUnimplemented(userdata: ?*anyopaque, dir_reader: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize {
   6015     _ = userdata;
   6016     _ = dir_reader;
   6017     _ = buffer;
   6018     return error.Unexpected;
   6019 }
   6020 
   6021 const dirRealPathFile = switch (native_os) {
   6022     .windows => dirRealPathFileWindows,
   6023     else => dirRealPathFilePosix,
   6024 };
   6025 
   6026 fn dirRealPathFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathFileError!usize {
   6027     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6028     _ = t;
   6029 
   6030     var path_name_w = try sliceToPrefixedFileW(dir.handle, sub_path);
   6031 
   6032     const h_file = handle: {
   6033         if (OpenFile(path_name_w.span(), .{
   6034             .dir = dir.handle,
   6035             .access_mask = .{
   6036                 .GENERIC = .{ .READ = true },
   6037                 .STANDARD = .{ .SYNCHRONIZE = true },
   6038             },
   6039             .creation = .OPEN,
   6040             .filter = .any,
   6041         })) |handle| {
   6042             break :handle handle;
   6043         } else |err| switch (err) {
   6044             error.WouldBlock => unreachable,
   6045             else => |e| return e,
   6046         }
   6047     };
   6048     defer windows.CloseHandle(h_file);
   6049     return realPathWindows(h_file, out_buffer);
   6050 }
   6051 
   6052 fn realPathWindows(h_file: windows.HANDLE, out_buffer: []u8) File.RealPathError!usize {
   6053     var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
   6054     const wide_slice = try GetFinalPathNameByHandle(h_file, .{}, &wide_buf);
   6055 
   6056     const len = std.unicode.calcWtf8Len(wide_slice);
   6057     if (len > out_buffer.len)
   6058         return error.NameTooLong;
   6059 
   6060     return std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
   6061 }
   6062 
   6063 /// Specifies how to format volume path in the result of `GetFinalPathNameByHandle`.
   6064 /// Defaults to DOS volume names.
   6065 pub const GetFinalPathNameByHandleFormat = struct {
   6066     volume_name: enum {
   6067         /// Format as DOS volume name
   6068         Dos,
   6069         /// Format as NT volume name
   6070         Nt,
   6071     } = .Dos,
   6072 };
   6073 
   6074 pub const GetFinalPathNameByHandleError = error{
   6075     AccessDenied,
   6076     FileNotFound,
   6077     NameTooLong,
   6078     /// The volume does not contain a recognized file system. File system
   6079     /// drivers might not be loaded, or the volume may be corrupt.
   6080     UnrecognizedVolume,
   6081 } || Io.Cancelable || Io.UnexpectedError;
   6082 
   6083 /// Returns canonical (normalized) path of handle.
   6084 /// Use `GetFinalPathNameByHandleFormat` to specify whether the path is meant to include
   6085 /// NT or DOS volume name (e.g., `\Device\HarddiskVolume0\foo.txt` versus `C:\foo.txt`).
   6086 /// If DOS volume name format is selected, note that this function does *not* prepend
   6087 /// `\\?\` prefix to the resultant path.
   6088 pub fn GetFinalPathNameByHandle(
   6089     hFile: windows.HANDLE,
   6090     fmt: GetFinalPathNameByHandleFormat,
   6091     out_buffer: []u16,
   6092 ) GetFinalPathNameByHandleError![]u16 {
   6093     const final_path = QueryObjectName(hFile, out_buffer) catch |err| switch (err) {
   6094         // we assume InvalidHandle is close enough to FileNotFound in semantics
   6095         // to not further complicate the error set
   6096         error.InvalidHandle => return error.FileNotFound,
   6097         else => |e| return e,
   6098     };
   6099 
   6100     switch (fmt.volume_name) {
   6101         .Nt => {
   6102             // the returned path is already in .Nt format
   6103             return final_path;
   6104         },
   6105         .Dos => {
   6106             // parse the string to separate volume path from file path
   6107             const device_prefix = std.unicode.utf8ToUtf16LeStringLiteral("\\Device\\");
   6108 
   6109             // We aren't entirely sure of the structure of the path returned by
   6110             // QueryObjectName in all contexts/environments.
   6111             // This code is written to cover the various cases that have
   6112             // been encountered and solved appropriately. But note that there's
   6113             // no easy way to verify that they have all been tackled!
   6114             // (Unless you, the reader knows of one then please do action that!)
   6115             if (!std.mem.startsWith(u16, final_path, device_prefix)) {
   6116                 // Wine seems to return NT namespaced paths starting with \??\ from QueryObjectName
   6117                 // (e.g. `\??\Z:\some\path\to\a\file.txt`), in which case we can just strip the
   6118                 // prefix to turn it into an absolute path.
   6119                 // https://github.com/ziglang/zig/issues/26029
   6120                 // https://bugs.winehq.org/show_bug.cgi?id=39569
   6121                 return windows.ntToWin32Namespace(final_path, out_buffer) catch |err| switch (err) {
   6122                     error.NotNtPath => return error.Unexpected,
   6123                     error.NameTooLong => |e| return e,
   6124                 };
   6125             }
   6126 
   6127             const file_path_begin_index = std.mem.findPos(u16, final_path, device_prefix.len, &[_]u16{'\\'}) orelse unreachable;
   6128             const volume_name_u16 = final_path[0..file_path_begin_index];
   6129             const device_name_u16 = volume_name_u16[device_prefix.len..];
   6130             const file_name_u16 = final_path[file_path_begin_index..];
   6131 
   6132             // MUP is Multiple UNC Provider, and indicates that the path is a UNC
   6133             // path. In this case, the canonical UNC path can be gotten by just
   6134             // dropping the \Device\Mup\ and making sure the path begins with \\
   6135             if (std.mem.eql(u16, device_name_u16, std.unicode.utf8ToUtf16LeStringLiteral("Mup"))) {
   6136                 out_buffer[0] = '\\';
   6137                 @memmove(out_buffer[1..][0..file_name_u16.len], file_name_u16);
   6138                 return out_buffer[0 .. 1 + file_name_u16.len];
   6139             }
   6140 
   6141             // Get DOS volume name. DOS volume names are actually symbolic link objects to the
   6142             // actual NT volume. For example:
   6143             // (NT) \Device\HarddiskVolume4 => (DOS) \DosDevices\C: == (DOS) C:
   6144             const MIN_SIZE = @sizeOf(windows.MOUNTMGR_MOUNT_POINT) + windows.MAX_PATH;
   6145             // We initialize the input buffer to all zeros for convenience since
   6146             // `DeviceIoControl` with `IOCTL_MOUNTMGR_QUERY_POINTS` expects this.
   6147             var input_buf: [MIN_SIZE]u8 align(@alignOf(windows.MOUNTMGR_MOUNT_POINT)) = [_]u8{0} ** MIN_SIZE;
   6148             var output_buf: [MIN_SIZE * 4]u8 align(@alignOf(windows.MOUNTMGR_MOUNT_POINTS)) = undefined;
   6149 
   6150             // This surprising path is a filesystem path to the mount manager on Windows.
   6151             // Source: https://stackoverflow.com/questions/3012828/using-ioctl-mountmgr-query-points
   6152             // This is the NT namespaced version of \\.\MountPointManager
   6153             const mgmt_path_u16 = std.unicode.utf8ToUtf16LeStringLiteral("\\??\\MountPointManager");
   6154             const mgmt_handle = OpenFile(mgmt_path_u16, .{
   6155                 .access_mask = .{ .STANDARD = .{ .SYNCHRONIZE = true } },
   6156                 .creation = .OPEN,
   6157             }) catch |err| switch (err) {
   6158                 error.IsDir => return error.Unexpected,
   6159                 error.NotDir => return error.Unexpected,
   6160                 error.NoDevice => return error.Unexpected,
   6161                 error.AccessDenied => return error.Unexpected,
   6162                 error.PipeBusy => return error.Unexpected,
   6163                 error.FileBusy => return error.Unexpected,
   6164                 error.PathAlreadyExists => return error.Unexpected,
   6165                 error.WouldBlock => return error.Unexpected,
   6166                 error.NetworkNotFound => return error.Unexpected,
   6167                 error.AntivirusInterference => return error.Unexpected,
   6168                 error.BadPathName => return error.Unexpected,
   6169                 else => |e| return e,
   6170             };
   6171             defer windows.CloseHandle(mgmt_handle);
   6172 
   6173             var input_struct: *windows.MOUNTMGR_MOUNT_POINT = @ptrCast(&input_buf[0]);
   6174             input_struct.DeviceNameOffset = @sizeOf(windows.MOUNTMGR_MOUNT_POINT);
   6175             input_struct.DeviceNameLength = @intCast(volume_name_u16.len * 2);
   6176             @memcpy(input_buf[@sizeOf(windows.MOUNTMGR_MOUNT_POINT)..][0 .. volume_name_u16.len * 2], @as([*]const u8, @ptrCast(volume_name_u16.ptr)));
   6177 
   6178             switch ((try deviceIoControl(&.{
   6179                 .file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = false } },
   6180                 .code = windows.IOCTL.MOUNTMGR.QUERY_POINTS,
   6181                 .in = &input_buf,
   6182                 .out = &output_buf,
   6183             })).u.Status) {
   6184                 .SUCCESS => {},
   6185                 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   6186                 else => |status| return windows.unexpectedStatus(status),
   6187             }
   6188             const mount_points_struct: *const windows.MOUNTMGR_MOUNT_POINTS = @ptrCast(&output_buf[0]);
   6189 
   6190             const mount_points = @as(
   6191                 [*]const windows.MOUNTMGR_MOUNT_POINT,
   6192                 @ptrCast(&mount_points_struct.MountPoints[0]),
   6193             )[0..mount_points_struct.NumberOfMountPoints];
   6194 
   6195             for (mount_points) |mount_point| {
   6196                 const symlink = @as(
   6197                     [*]const u16,
   6198                     @ptrCast(@alignCast(&output_buf[mount_point.SymbolicLinkNameOffset])),
   6199                 )[0 .. mount_point.SymbolicLinkNameLength / 2];
   6200 
   6201                 // Look for `\DosDevices\` prefix. We don't really care if there are more than one symlinks
   6202                 // with traditional DOS drive letters, so pick the first one available.
   6203                 var prefix_buf = std.unicode.utf8ToUtf16LeStringLiteral("\\DosDevices\\");
   6204                 const prefix = prefix_buf[0..prefix_buf.len];
   6205 
   6206                 if (std.mem.startsWith(u16, symlink, prefix)) {
   6207                     const drive_letter = symlink[prefix.len..];
   6208 
   6209                     if (out_buffer.len < drive_letter.len + file_name_u16.len) return error.NameTooLong;
   6210 
   6211                     @memcpy(out_buffer[0..drive_letter.len], drive_letter);
   6212                     @memmove(out_buffer[drive_letter.len..][0..file_name_u16.len], file_name_u16);
   6213                     const total_len = drive_letter.len + file_name_u16.len;
   6214 
   6215                     // Validate that DOS does not contain any spurious nul bytes.
   6216                     assert(std.mem.findScalar(u16, out_buffer[0..total_len], 0) == null);
   6217 
   6218                     return out_buffer[0..total_len];
   6219                 } else if (mountmgrIsVolumeName(symlink)) {
   6220                     // If the symlink is a volume GUID like \??\Volume{383da0b0-717f-41b6-8c36-00500992b58d},
   6221                     // then it is a volume mounted as a path rather than a drive letter. We need to
   6222                     // query the mount manager again to get the DOS path for the volume.
   6223 
   6224                     // 49 is the maximum length accepted by mountmgrIsVolumeName
   6225                     const vol_input_size = @sizeOf(windows.MOUNTMGR_TARGET_NAME) + (49 * 2);
   6226                     var vol_input_buf: [vol_input_size]u8 align(@alignOf(windows.MOUNTMGR_TARGET_NAME)) = [_]u8{0} ** vol_input_size;
   6227                     // Note: If the path exceeds MAX_PATH, the Disk Management GUI doesn't accept the full path,
   6228                     // and instead if must be specified using a shortened form (e.g. C:\FOO~1\BAR~1\<...>).
   6229                     // However, just to be sure we can handle any path length, we use PATH_MAX_WIDE here.
   6230                     const min_output_size = @sizeOf(windows.MOUNTMGR_VOLUME_PATHS) + (windows.PATH_MAX_WIDE * 2);
   6231                     var vol_output_buf: [min_output_size]u8 align(@alignOf(windows.MOUNTMGR_VOLUME_PATHS)) = undefined;
   6232 
   6233                     var vol_input_struct: *windows.MOUNTMGR_TARGET_NAME = @ptrCast(&vol_input_buf[0]);
   6234                     vol_input_struct.DeviceNameLength = @intCast(symlink.len * 2);
   6235                     @memcpy(@as([*]windows.WCHAR, &vol_input_struct.DeviceName)[0..symlink.len], symlink);
   6236 
   6237                     switch ((try deviceIoControl(&.{
   6238                         .file = .{ .handle = mgmt_handle, .flags = .{ .nonblocking = true } },
   6239                         .code = windows.IOCTL.MOUNTMGR.QUERY_DOS_VOLUME_PATH,
   6240                         .in = &vol_input_buf,
   6241                         .out = &vol_output_buf,
   6242                     })).u.Status) {
   6243                         .SUCCESS => {},
   6244                         .UNRECOGNIZED_VOLUME => return error.UnrecognizedVolume,
   6245                         else => |status| return windows.unexpectedStatus(status),
   6246                     }
   6247                     const volume_paths_struct: *const windows.MOUNTMGR_VOLUME_PATHS = @ptrCast(&vol_output_buf[0]);
   6248                     const volume_path = std.mem.sliceTo(@as(
   6249                         [*]const u16,
   6250                         &volume_paths_struct.MultiSz,
   6251                     )[0 .. volume_paths_struct.MultiSzLength / 2], 0);
   6252 
   6253                     if (out_buffer.len < volume_path.len + file_name_u16.len) return error.NameTooLong;
   6254 
   6255                     // `out_buffer` currently contains the memory of `file_name_u16`, so it can overlap with where
   6256                     // we want to place the filename before returning. Here are the possible overlapping cases:
   6257                     //
   6258                     // out_buffer:       [filename]
   6259                     //       dest: [___(a)___] [___(b)___]
   6260                     //
   6261                     // In the case of (a), we need to copy forwards, and in the case of (b) we need
   6262                     // to copy backwards. We also need to do this before copying the volume path because
   6263                     // it could overwrite the file_name_u16 memory.
   6264                     const file_name_dest = out_buffer[volume_path.len..][0..file_name_u16.len];
   6265                     @memmove(file_name_dest, file_name_u16);
   6266                     @memcpy(out_buffer[0..volume_path.len], volume_path);
   6267                     const total_len = volume_path.len + file_name_u16.len;
   6268 
   6269                     // Validate that DOS does not contain any spurious nul bytes.
   6270                     assert(std.mem.findScalar(u16, out_buffer[0..total_len], 0) == null);
   6271 
   6272                     return out_buffer[0..total_len];
   6273                 }
   6274             }
   6275 
   6276             // If we've ended up here, then something went wrong/is corrupted in the OS,
   6277             // so error out!
   6278             return error.FileNotFound;
   6279         },
   6280     }
   6281 }
   6282 
   6283 test GetFinalPathNameByHandle {
   6284     if (builtin.os.tag != .windows)
   6285         return;
   6286 
   6287     //any file will do
   6288     var tmp = std.testing.tmpDir(.{});
   6289     defer tmp.cleanup();
   6290     const handle = tmp.dir.handle;
   6291     var buffer: [windows.PATH_MAX_WIDE]u16 = undefined;
   6292 
   6293     //check with sufficient size
   6294     const nt_path = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, &buffer);
   6295     _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, &buffer);
   6296 
   6297     const required_len_in_u16 = nt_path.len + @divExact(@intFromPtr(nt_path.ptr) - @intFromPtr(&buffer), 2) + 1;
   6298     //check with insufficient size
   6299     try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0 .. required_len_in_u16 - 1]));
   6300     try std.testing.expectError(error.NameTooLong, GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0 .. required_len_in_u16 - 1]));
   6301 
   6302     //check with exactly-sufficient size
   6303     _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Nt }, buffer[0..required_len_in_u16]);
   6304     _ = try GetFinalPathNameByHandle(handle, .{ .volume_name = .Dos }, buffer[0..required_len_in_u16]);
   6305 }
   6306 
   6307 /// Equivalent to the MOUNTMGR_IS_VOLUME_NAME macro in mountmgr.h
   6308 fn mountmgrIsVolumeName(name: []const u16) bool {
   6309     return (name.len == 48 or (name.len == 49 and name[48] == std.mem.nativeToLittle(u16, '\\'))) and
   6310         name[0] == std.mem.nativeToLittle(u16, '\\') and
   6311         (name[1] == std.mem.nativeToLittle(u16, '?') or name[1] == std.mem.nativeToLittle(u16, '\\')) and
   6312         name[2] == std.mem.nativeToLittle(u16, '?') and
   6313         name[3] == std.mem.nativeToLittle(u16, '\\') and
   6314         std.mem.startsWith(u16, name[4..], std.unicode.utf8ToUtf16LeStringLiteral("Volume{")) and
   6315         name[19] == std.mem.nativeToLittle(u16, '-') and
   6316         name[24] == std.mem.nativeToLittle(u16, '-') and
   6317         name[29] == std.mem.nativeToLittle(u16, '-') and
   6318         name[34] == std.mem.nativeToLittle(u16, '-') and
   6319         name[47] == std.mem.nativeToLittle(u16, '}');
   6320 }
   6321 
   6322 test mountmgrIsVolumeName {
   6323     @setEvalBranchQuota(2000);
   6324     const L = std.unicode.utf8ToUtf16LeStringLiteral;
   6325     try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
   6326     try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
   6327     try std.testing.expect(mountmgrIsVolumeName(L("\\\\?\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\")));
   6328     try std.testing.expect(mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\")));
   6329     try std.testing.expect(!mountmgrIsVolumeName(L("\\\\.\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}")));
   6330     try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58d}\\foo")));
   6331     try std.testing.expect(!mountmgrIsVolumeName(L("\\??\\Volume{383da0b0-717f-41b6-8c36-00500992b58}")));
   6332 }
   6333 
   6334 pub const QueryObjectNameError = error{
   6335     AccessDenied,
   6336     InvalidHandle,
   6337     NameTooLong,
   6338     Unexpected,
   6339 };
   6340 
   6341 pub fn QueryObjectName(handle: windows.HANDLE, out_buffer: []u16) QueryObjectNameError![]u16 {
   6342     const out_buffer_aligned = std.mem.alignInSlice(out_buffer, @alignOf(windows.OBJECT.NAME_INFORMATION)) orelse return error.NameTooLong;
   6343 
   6344     const info: *windows.OBJECT.NAME_INFORMATION = @ptrCast(out_buffer_aligned);
   6345     // buffer size is specified in bytes
   6346     const out_buffer_len = std.math.cast(windows.ULONG, out_buffer_aligned.len * 2) orelse std.math.maxInt(windows.ULONG);
   6347     // last argument would return the length required for full_buffer, not exposed here
   6348     return switch (windows.ntdll.NtQueryObject(handle, .Name, info, out_buffer_len, null)) {
   6349         .SUCCESS => {
   6350             // info.Name from ObQueryNameString is documented to be empty if the object
   6351             // was "unnamed", not sure if this can happen for file handles
   6352             return if (info.Name.isEmpty()) error.Unexpected else info.Name.slice();
   6353         },
   6354         .ACCESS_DENIED => error.AccessDenied,
   6355         .INVALID_HANDLE => error.InvalidHandle,
   6356         // triggered when the buffer is too small for the OBJECT_NAME_INFORMATION object (.INFO_LENGTH_MISMATCH),
   6357         // or if the buffer is too small for the file path returned (.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL)
   6358         .INFO_LENGTH_MISMATCH, .BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => error.NameTooLong,
   6359         else => |e| windows.unexpectedStatus(e),
   6360     };
   6361 }
   6362 
   6363 test QueryObjectName {
   6364     if (builtin.os.tag != .windows)
   6365         return;
   6366 
   6367     //any file will do; canonicalization works on NTFS junctions and symlinks, hardlinks remain separate paths.
   6368     var tmp = std.testing.tmpDir(.{});
   6369     defer tmp.cleanup();
   6370     const handle = tmp.dir.handle;
   6371     var out_buffer: [windows.PATH_MAX_WIDE]u16 = undefined;
   6372 
   6373     const result_path = try QueryObjectName(handle, &out_buffer);
   6374     const required_len_in_u16 = result_path.len + @divExact(@intFromPtr(result_path.ptr) - @intFromPtr(&out_buffer), 2) + 1;
   6375     //insufficient size
   6376     try std.testing.expectError(error.NameTooLong, QueryObjectName(handle, out_buffer[0 .. required_len_in_u16 - 1]));
   6377     //exactly-sufficient size
   6378     _ = try QueryObjectName(handle, out_buffer[0..required_len_in_u16]);
   6379 }
   6380 
   6381 const Wtf16ToPrefixedFileWError = error{
   6382     AccessDenied,
   6383     FileNotFound,
   6384 } || Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
   6385 
   6386 /// Converts the `path` to WTF16, null-terminated. If the path contains any
   6387 /// namespace prefix, or is anything but a relative path (rooted, drive relative,
   6388 /// etc) the result will have the NT-style prefix `\??\`.
   6389 ///
   6390 /// Similar to RtlDosPathNameToNtPathName_U with a few differences:
   6391 /// - Does not allocate on the heap.
   6392 /// - Relative paths are kept as relative unless they contain too many ..
   6393 ///   components, in which case they are resolved against the `dir` if it
   6394 ///   is non-null, or the CWD if it is null.
   6395 /// - Special case device names like COM1, NUL, etc are not handled specially (TODO)
   6396 /// - . and space are not stripped from the end of relative paths (potential TODO)
   6397 pub fn wToPrefixedFileW(dir: ?windows.HANDLE, path: [:0]const u16) Wtf16ToPrefixedFileWError!WindowsPathSpace {
   6398     const nt_prefix = [_]u16{ '\\', '?', '?', '\\' };
   6399     if (windows.hasCommonNtPrefix(u16, path)) {
   6400         // TODO: Figure out a way to design an API that can avoid the copy for NT,
   6401         //       since it is always returned fully unmodified.
   6402         var path_space: WindowsPathSpace = undefined;
   6403         path_space.data[0..nt_prefix.len].* = nt_prefix;
   6404         const len_after_prefix = path.len - nt_prefix.len;
   6405         @memcpy(path_space.data[nt_prefix.len..][0..len_after_prefix], path[nt_prefix.len..]);
   6406         path_space.len = path.len;
   6407         path_space.data[path_space.len] = 0;
   6408         return path_space;
   6409     } else {
   6410         const path_type = Dir.path.getWin32PathType(u16, path);
   6411         var path_space: WindowsPathSpace = undefined;
   6412         if (path_type == .local_device) {
   6413             switch (getLocalDevicePathType(u16, path)) {
   6414                 .verbatim => {
   6415                     path_space.data[0..nt_prefix.len].* = nt_prefix;
   6416                     const len_after_prefix = path.len - nt_prefix.len;
   6417                     @memcpy(path_space.data[nt_prefix.len..][0..len_after_prefix], path[nt_prefix.len..]);
   6418                     path_space.len = path.len;
   6419                     path_space.data[path_space.len] = 0;
   6420                     return path_space;
   6421                 },
   6422                 .local_device, .fake_verbatim => {
   6423                     const path_byte_len = windows.ntdll.RtlGetFullPathName_U(
   6424                         path.ptr,
   6425                         path_space.data.len * 2,
   6426                         &path_space.data,
   6427                         null,
   6428                     );
   6429                     if (path_byte_len == 0) {
   6430                         // TODO: This may not be the right error
   6431                         return error.BadPathName;
   6432                     } else if (path_byte_len / 2 > path_space.data.len) {
   6433                         return error.NameTooLong;
   6434                     }
   6435                     path_space.len = path_byte_len / 2;
   6436                     // Both prefixes will be normalized but retained, so all
   6437                     // we need to do now is replace them with the NT prefix
   6438                     path_space.data[0..nt_prefix.len].* = nt_prefix;
   6439                     return path_space;
   6440                 },
   6441             }
   6442         }
   6443         relative: {
   6444             if (path_type == .relative) {
   6445                 // TODO: Handle special case device names like COM1, AUX, NUL, CONIN$, CONOUT$, etc.
   6446                 //       See https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
   6447 
   6448                 // TODO: Potentially strip all trailing . and space characters from the
   6449                 //       end of the path. This is something that both RtlDosPathNameToNtPathName_U
   6450                 //       and RtlGetFullPathName_U do. Technically, trailing . and spaces
   6451                 //       are allowed, but such paths may not interact well with Windows (i.e.
   6452                 //       files with these paths can't be deleted from explorer.exe, etc).
   6453                 //       This could be something that normalizePath may want to do.
   6454 
   6455                 @memcpy(path_space.data[0..path.len], path);
   6456                 // Try to normalize, but if we get too many parent directories,
   6457                 // then we need to start over and use RtlGetFullPathName_U instead.
   6458                 path_space.len = windows.normalizePath(u16, path_space.data[0..path.len]) catch |err| switch (err) {
   6459                     error.TooManyParentDirs => break :relative,
   6460                 };
   6461                 path_space.data[path_space.len] = 0;
   6462                 return path_space;
   6463             }
   6464         }
   6465         // We now know we are going to return an absolute NT path, so
   6466         // we can unconditionally prefix it with the NT prefix.
   6467         path_space.data[0..nt_prefix.len].* = nt_prefix;
   6468         if (path_type == .root_local_device) {
   6469             // `\\.` and `\\?` always get converted to `\??\` exactly, so
   6470             // we can just stop here
   6471             path_space.len = nt_prefix.len;
   6472             path_space.data[path_space.len] = 0;
   6473             return path_space;
   6474         }
   6475         const path_buf_offset = switch (path_type) {
   6476             // UNC paths will always start with `\\`. However, we want to
   6477             // end up with something like `\??\UNC\server\share`, so to get
   6478             // RtlGetFullPathName to write into the spot we want the `server`
   6479             // part to end up, we need to provide an offset such that
   6480             // the `\\` part gets written where the `C\` of `UNC\` will be
   6481             // in the final NT path.
   6482             .unc_absolute => nt_prefix.len + 2,
   6483             else => nt_prefix.len,
   6484         };
   6485         const buf_len: u32 = @intCast(path_space.data.len - path_buf_offset);
   6486         const path_to_get: [:0]const u16 = path_to_get: {
   6487             // If dir is null, then we don't need to bother with GetFinalPathNameByHandle because
   6488             // RtlGetFullPathName_U will resolve relative paths against the CWD for us.
   6489             if (path_type != .relative or dir == null) {
   6490                 break :path_to_get path;
   6491             }
   6492             // We can also skip GetFinalPathNameByHandle if the handle matches
   6493             // the handle returned by Io.Dir.cwd()
   6494             if (dir.? == Io.Dir.cwd().handle) {
   6495                 break :path_to_get path;
   6496             }
   6497             // At this point, we know we have a relative path that had too many
   6498             // `..` components to be resolved by normalizePath, so we need to
   6499             // convert it into an absolute path and let RtlGetFullPathName_U
   6500             // canonicalize it. We do this by getting the path of the `dir`
   6501             // and appending the relative path to it.
   6502             var dir_path_buf: [windows.PATH_MAX_WIDE:0]u16 = undefined;
   6503             const dir_path = GetFinalPathNameByHandle(dir.?, .{}, &dir_path_buf) catch |err| switch (err) {
   6504                 // This mapping is not correct; it is actually expected
   6505                 // that calling GetFinalPathNameByHandle might return
   6506                 // error.UnrecognizedVolume, and in fact has been observed
   6507                 // in the wild. The problem is that wToPrefixedFileW was
   6508                 // never intended to make *any* OS syscall APIs. It's only
   6509                 // supposed to convert a string to one that is eligible to
   6510                 // be used in the ntdll syscalls.
   6511                 //
   6512                 // To solve this, this function needs to no longer call
   6513                 // GetFinalPathNameByHandle under any conditions, or the
   6514                 // calling function needs to get reworked to not need to
   6515                 // call this function.
   6516                 //
   6517                 // This may involve making breaking API changes.
   6518                 error.UnrecognizedVolume => return error.Unexpected,
   6519                 else => |e| return e,
   6520             };
   6521             if (dir_path.len + 1 + path.len > windows.PATH_MAX_WIDE) {
   6522                 return error.NameTooLong;
   6523             }
   6524             // We don't have to worry about potentially doubling up path separators
   6525             // here since RtlGetFullPathName_U will handle canonicalizing it.
   6526             dir_path_buf[dir_path.len] = '\\';
   6527             @memcpy(dir_path_buf[dir_path.len + 1 ..][0..path.len], path);
   6528             const full_len = dir_path.len + 1 + path.len;
   6529             dir_path_buf[full_len] = 0;
   6530             break :path_to_get dir_path_buf[0..full_len :0];
   6531         };
   6532         const path_byte_len = windows.ntdll.RtlGetFullPathName_U(
   6533             path_to_get.ptr,
   6534             buf_len * 2,
   6535             path_space.data[path_buf_offset..].ptr,
   6536             null,
   6537         );
   6538         if (path_byte_len == 0) {
   6539             // TODO: This may not be the right error
   6540             return error.BadPathName;
   6541         } else if (path_byte_len / 2 > buf_len) {
   6542             return error.NameTooLong;
   6543         }
   6544         path_space.len = path_buf_offset + (path_byte_len / 2);
   6545         if (path_type == .unc_absolute) {
   6546             // Now add in the UNC, the `C` should overwrite the first `\` of the
   6547             // FullPathName, ultimately resulting in `\??\UNC\<the rest of the path>`
   6548             assert(path_space.data[path_buf_offset] == '\\');
   6549             assert(path_space.data[path_buf_offset + 1] == '\\');
   6550             const unc = [_]u16{ 'U', 'N', 'C' };
   6551             path_space.data[nt_prefix.len..][0..unc.len].* = unc;
   6552         }
   6553         return path_space;
   6554     }
   6555 }
   6556 
   6557 const LocalDevicePathType = enum {
   6558     /// `\\.\` (path separators can be `\` or `/`)
   6559     local_device,
   6560     /// `\\?\`
   6561     /// When converted to an NT path, everything past the prefix is left
   6562     /// untouched and `\\?\` is replaced by `\??\`.
   6563     verbatim,
   6564     /// `\\?\` without all path separators being `\`.
   6565     /// This seems to be recognized as a prefix, but the 'verbatim' aspect
   6566     /// is not respected (i.e. if `//?/C:/foo` is converted to an NT path,
   6567     /// it will become `\??\C:\foo` [it will be canonicalized and the //?/ won't
   6568     /// be treated as part of the final path])
   6569     fake_verbatim,
   6570 };
   6571 
   6572 /// Only relevant for Win32 -> NT path conversion.
   6573 /// Asserts `path` is of type `Dir.path.Win32PathType.local_device`.
   6574 fn getLocalDevicePathType(comptime T: type, path: []const T) LocalDevicePathType {
   6575     if (std.debug.runtime_safety) {
   6576         assert(Dir.path.getWin32PathType(T, path) == .local_device);
   6577     }
   6578 
   6579     const backslash = std.mem.nativeToLittle(T, '\\');
   6580     const all_backslash = path[0] == backslash and
   6581         path[1] == backslash and
   6582         path[3] == backslash;
   6583     return switch (path[2]) {
   6584         std.mem.nativeToLittle(T, '?') => if (all_backslash) .verbatim else .fake_verbatim,
   6585         std.mem.nativeToLittle(T, '.') => .local_device,
   6586         else => unreachable,
   6587     };
   6588 }
   6589 
   6590 pub const Wtf8ToPrefixedFileWError = Wtf16ToPrefixedFileWError;
   6591 
   6592 /// Same as `wToPrefixedFileW` but accepts a WTF-8 encoded path.
   6593 /// https://wtf-8.codeberg.page/
   6594 pub fn sliceToPrefixedFileW(dir: ?windows.HANDLE, path: []const u8) Wtf8ToPrefixedFileWError!WindowsPathSpace {
   6595     var temp_path: WindowsPathSpace = undefined;
   6596     temp_path.len = std.unicode.wtf8ToWtf16Le(&temp_path.data, path) catch |err| switch (err) {
   6597         error.InvalidWtf8 => return error.BadPathName,
   6598     };
   6599     temp_path.data[temp_path.len] = 0;
   6600     return wToPrefixedFileW(dir, temp_path.span());
   6601 }
   6602 
   6603 pub const WindowsPathSpace = struct {
   6604     data: [windows.PATH_MAX_WIDE:0]u16,
   6605     len: usize,
   6606 
   6607     pub fn span(wps: *const WindowsPathSpace) [:0]const u16 {
   6608         return wps.data[0..wps.len :0];
   6609     }
   6610 
   6611     pub fn string(wps: *const WindowsPathSpace) windows.UNICODE_STRING {
   6612         return .init(wps.span());
   6613     }
   6614 };
   6615 
   6616 fn dirRealPathFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, out_buffer: []u8) Dir.RealPathFileError!usize {
   6617     if (native_os == .wasi) return error.OperationUnsupported;
   6618 
   6619     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6620     _ = t;
   6621 
   6622     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   6623     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   6624 
   6625     if (builtin.link_libc and dir.handle == posix.AT.FDCWD) {
   6626         if (out_buffer.len < posix.PATH_MAX) return error.NameTooLong;
   6627         const syscall: Syscall = try .start();
   6628         while (true) {
   6629             if (std.c.realpath(sub_path_posix, out_buffer.ptr)) |redundant_pointer| {
   6630                 syscall.finish();
   6631                 assert(redundant_pointer == out_buffer.ptr);
   6632                 return std.mem.indexOfScalar(u8, out_buffer, 0) orelse out_buffer.len;
   6633             }
   6634             const err: posix.E = @enumFromInt(std.c._errno().*);
   6635             if (err == .INTR) {
   6636                 try syscall.checkCancel();
   6637                 continue;
   6638             }
   6639             syscall.finish();
   6640             switch (err) {
   6641                 .INVAL => return errnoBug(err),
   6642                 .BADF => return errnoBug(err),
   6643                 .FAULT => return errnoBug(err),
   6644                 .ACCES => return error.AccessDenied,
   6645                 .NOENT => return error.FileNotFound,
   6646                 .OPNOTSUPP => return error.OperationUnsupported,
   6647                 .NOTDIR => return error.NotDir,
   6648                 .NAMETOOLONG => return error.NameTooLong,
   6649                 .LOOP => return error.SymLinkLoop,
   6650                 .IO => return error.InputOutput,
   6651                 else => return posix.unexpectedErrno(err),
   6652             }
   6653         }
   6654     }
   6655 
   6656     var flags: posix.O = .{};
   6657     if (@hasField(posix.O, "NONBLOCK")) flags.NONBLOCK = true;
   6658     if (@hasField(posix.O, "CLOEXEC")) flags.CLOEXEC = true;
   6659     if (@hasField(posix.O, "PATH")) flags.PATH = true;
   6660 
   6661     const mode: posix.mode_t = 0;
   6662 
   6663     const syscall: Syscall = try .start();
   6664     const fd: posix.fd_t = while (true) {
   6665         const rc = openat_sym(dir.handle, sub_path_posix, flags, mode);
   6666         switch (posix.errno(rc)) {
   6667             .SUCCESS => {
   6668                 syscall.finish();
   6669                 break @intCast(rc);
   6670             },
   6671             .INTR => {
   6672                 try syscall.checkCancel();
   6673                 continue;
   6674             },
   6675             else => |e| {
   6676                 syscall.finish();
   6677                 switch (e) {
   6678                     .FAULT => |err| return errnoBug(err),
   6679                     .INVAL => return error.BadPathName,
   6680                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   6681                     .ACCES => return error.AccessDenied,
   6682                     .FBIG => return error.FileTooBig,
   6683                     .OVERFLOW => return error.FileTooBig,
   6684                     .ISDIR => return error.IsDir,
   6685                     .LOOP => return error.SymLinkLoop,
   6686                     .MFILE => return error.ProcessFdQuotaExceeded,
   6687                     .NAMETOOLONG => return error.NameTooLong,
   6688                     .NFILE => return error.SystemFdQuotaExceeded,
   6689                     .NODEV => return error.NoDevice,
   6690                     .NOENT => return error.FileNotFound,
   6691                     .SRCH => return error.FileNotFound, // Linux when accessing procfs.
   6692                     .NOMEM => return error.SystemResources,
   6693                     .NOSPC => return error.NoSpaceLeft,
   6694                     .NOTDIR => return error.NotDir,
   6695                     .PERM => return error.PermissionDenied,
   6696                     .EXIST => return error.PathAlreadyExists,
   6697                     .BUSY => return error.DeviceBusy,
   6698                     .NXIO => return error.NoDevice,
   6699                     .ILSEQ => return error.BadPathName,
   6700                     else => |err| return posix.unexpectedErrno(err),
   6701                 }
   6702             },
   6703         }
   6704     };
   6705     defer closeFd(fd);
   6706     return realPathPosix(fd, out_buffer);
   6707 }
   6708 
   6709 const dirRealPath = switch (native_os) {
   6710     .windows => dirRealPathWindows,
   6711     else => dirRealPathPosix,
   6712 };
   6713 
   6714 fn dirRealPathPosix(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize {
   6715     if (native_os == .wasi) return error.OperationUnsupported;
   6716     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6717     _ = t;
   6718     return realPathPosix(dir.handle, out_buffer);
   6719 }
   6720 
   6721 fn dirRealPathWindows(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize {
   6722     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6723     _ = t;
   6724     return realPathWindows(dir.handle, out_buffer);
   6725 }
   6726 
   6727 const fileRealPath = switch (native_os) {
   6728     .windows => fileRealPathWindows,
   6729     else => fileRealPathPosix,
   6730 };
   6731 
   6732 fn fileRealPathWindows(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {
   6733     if (native_os == .wasi) return error.OperationUnsupported;
   6734     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6735     _ = t;
   6736     return realPathWindows(file.handle, out_buffer);
   6737 }
   6738 
   6739 fn fileRealPathPosix(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {
   6740     if (native_os == .wasi) return error.OperationUnsupported;
   6741     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6742     _ = t;
   6743     return realPathPosix(file.handle, out_buffer);
   6744 }
   6745 
   6746 fn realPathPosix(fd: posix.fd_t, out_buffer: []u8) File.RealPathError!usize {
   6747     switch (native_os) {
   6748         .dragonfly, .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => {
   6749             var sufficient_buffer: [posix.PATH_MAX]u8 = undefined;
   6750             @memset(&sufficient_buffer, 0);
   6751             const syscall: Syscall = try .start();
   6752             while (true) {
   6753                 switch (posix.errno(posix.system.fcntl(fd, posix.F.GETPATH, &sufficient_buffer))) {
   6754                     .SUCCESS => {
   6755                         syscall.finish();
   6756                         break;
   6757                     },
   6758                     .INTR => {
   6759                         try syscall.checkCancel();
   6760                         continue;
   6761                     },
   6762                     else => |e| {
   6763                         syscall.finish();
   6764                         switch (e) {
   6765                             .ACCES => return error.AccessDenied,
   6766                             .BADF => return error.FileNotFound,
   6767                             .NOENT => return error.FileNotFound,
   6768                             .NOMEM => return error.SystemResources,
   6769                             .NOSPC => return error.NameTooLong,
   6770                             .RANGE => return error.NameTooLong,
   6771                             else => |err| return posix.unexpectedErrno(err),
   6772                         }
   6773                     },
   6774                 }
   6775             }
   6776             const n = std.mem.indexOfScalar(u8, &sufficient_buffer, 0) orelse sufficient_buffer.len;
   6777             if (n > out_buffer.len) return error.NameTooLong;
   6778             @memcpy(out_buffer[0..n], sufficient_buffer[0..n]);
   6779             return n;
   6780         },
   6781         .linux, .serenity, .illumos => {
   6782             var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
   6783             const template = if (native_os == .illumos) "/proc/self/path/{d}" else "/proc/self/fd/{d}";
   6784             const proc_path = std.fmt.bufPrintSentinel(&procfs_buf, template, .{fd}, 0) catch unreachable;
   6785             const syscall: Syscall = try .start();
   6786             while (true) {
   6787                 const rc = posix.system.readlink(proc_path, out_buffer.ptr, out_buffer.len);
   6788                 switch (posix.errno(rc)) {
   6789                     .SUCCESS => {
   6790                         syscall.finish();
   6791                         const len: usize = @bitCast(rc);
   6792                         return len;
   6793                     },
   6794                     .INTR => {
   6795                         try syscall.checkCancel();
   6796                         continue;
   6797                     },
   6798                     else => |e| {
   6799                         syscall.finish();
   6800                         switch (e) {
   6801                             .ACCES => return error.AccessDenied,
   6802                             .FAULT => |err| return errnoBug(err),
   6803                             .IO => return error.FileSystem,
   6804                             .LOOP => return error.SymLinkLoop,
   6805                             .NAMETOOLONG => return error.NameTooLong,
   6806                             .NOENT => return error.FileNotFound,
   6807                             .NOMEM => return error.SystemResources,
   6808                             .NOTDIR => return error.NotDir,
   6809                             .ILSEQ => |err| return errnoBug(err),
   6810                             else => |err| return posix.unexpectedErrno(err),
   6811                         }
   6812                     },
   6813                 }
   6814             }
   6815         },
   6816         .freebsd => {
   6817             var k_file: std.c.kinfo_file = undefined;
   6818             k_file.structsize = std.c.KINFO_FILE_SIZE;
   6819             const syscall: Syscall = try .start();
   6820             while (true) {
   6821                 switch (posix.errno(std.c.fcntl(fd, std.c.F.KINFO, @intFromPtr(&k_file)))) {
   6822                     .SUCCESS => {
   6823                         syscall.finish();
   6824                         break;
   6825                     },
   6826                     .INTR => {
   6827                         try syscall.checkCancel();
   6828                         continue;
   6829                     },
   6830                     .BADF => {
   6831                         syscall.finish();
   6832                         return error.FileNotFound;
   6833                     },
   6834                     else => |err| {
   6835                         syscall.finish();
   6836                         return posix.unexpectedErrno(err);
   6837                     },
   6838                 }
   6839             }
   6840             const len = std.mem.findScalar(u8, &k_file.path, 0) orelse k_file.path.len;
   6841             if (len == 0) return error.NameTooLong;
   6842             @memcpy(out_buffer[0..len], k_file.path[0..len]);
   6843             return len;
   6844         },
   6845         else => return error.OperationUnsupported,
   6846     }
   6847     comptime unreachable;
   6848 }
   6849 
   6850 fn fileHardLink(
   6851     userdata: ?*anyopaque,
   6852     file: File,
   6853     new_dir: Dir,
   6854     new_sub_path: []const u8,
   6855     options: File.HardLinkOptions,
   6856 ) File.HardLinkError!void {
   6857     _ = userdata;
   6858     if (native_os != .linux) return error.OperationUnsupported;
   6859 
   6860     var new_path_buffer: [posix.PATH_MAX]u8 = undefined;
   6861     const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
   6862 
   6863     const flags: u32 = if (options.follow_symlinks)
   6864         posix.AT.SYMLINK_FOLLOW | posix.AT.EMPTY_PATH
   6865     else
   6866         posix.AT.EMPTY_PATH;
   6867 
   6868     return linkat(file.handle, "", new_dir.handle, new_sub_path_posix, flags) catch |err| switch (err) {
   6869         error.FileNotFound => {
   6870             if (options.follow_symlinks) return error.FileNotFound;
   6871             var proc_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
   6872             const proc_path = std.fmt.bufPrintSentinel(&proc_buf, "/proc/self/fd/{d}", .{file.handle}, 0) catch
   6873                 unreachable;
   6874             return linkat(posix.AT.FDCWD, proc_path, new_dir.handle, new_sub_path_posix, posix.AT.SYMLINK_FOLLOW);
   6875         },
   6876         else => |e| return e,
   6877     };
   6878 }
   6879 
   6880 fn linkat(
   6881     old_dir: posix.fd_t,
   6882     old_path: [*:0]const u8,
   6883     new_dir: posix.fd_t,
   6884     new_path: [*:0]const u8,
   6885     flags: u32,
   6886 ) File.HardLinkError!void {
   6887     const syscall: Syscall = try .start();
   6888     while (true) {
   6889         switch (posix.errno(posix.system.linkat(old_dir, old_path, new_dir, new_path, flags))) {
   6890             .SUCCESS => return syscall.finish(),
   6891             .INTR => {
   6892                 try syscall.checkCancel();
   6893                 continue;
   6894             },
   6895             .ACCES => return syscall.fail(error.AccessDenied),
   6896             .DQUOT => return syscall.fail(error.DiskQuota),
   6897             .EXIST => return syscall.fail(error.PathAlreadyExists),
   6898             .IO => return syscall.fail(error.HardwareFailure),
   6899             .LOOP => return syscall.fail(error.SymLinkLoop),
   6900             .MLINK => return syscall.fail(error.LinkQuotaExceeded),
   6901             .NAMETOOLONG => return syscall.fail(error.NameTooLong),
   6902             .NOENT => return syscall.fail(error.FileNotFound),
   6903             .NOMEM => return syscall.fail(error.SystemResources),
   6904             .NOSPC => return syscall.fail(error.NoSpaceLeft),
   6905             .NOTDIR => return syscall.fail(error.NotDir),
   6906             .PERM => return syscall.fail(error.PermissionDenied),
   6907             .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
   6908             .XDEV => return syscall.fail(error.CrossDevice),
   6909             .ILSEQ => return syscall.fail(error.BadPathName),
   6910             .FAULT => |err| return syscall.errnoBug(err),
   6911             .INVAL => |err| return syscall.errnoBug(err),
   6912             else => |err| return syscall.unexpectedErrno(err),
   6913         }
   6914     }
   6915 }
   6916 
   6917 const dirDeleteFile = switch (native_os) {
   6918     .windows => dirDeleteFileWindows,
   6919     .wasi => dirDeleteFileWasi,
   6920     else => dirDeleteFilePosix,
   6921 };
   6922 
   6923 fn dirDeleteFileWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
   6924     return dirDeleteWindows(userdata, dir, sub_path, false) catch |err| switch (err) {
   6925         error.DirNotEmpty => unreachable,
   6926         else => |e| return e,
   6927     };
   6928 }
   6929 
   6930 fn dirDeleteFileWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
   6931     if (builtin.link_libc) return dirDeleteFilePosix(userdata, dir, sub_path);
   6932     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6933     _ = t;
   6934     const syscall: Syscall = try .start();
   6935     while (true) {
   6936         const res = std.os.wasi.path_unlink_file(dir.handle, sub_path.ptr, sub_path.len);
   6937         switch (res) {
   6938             .SUCCESS => {
   6939                 syscall.finish();
   6940                 return;
   6941             },
   6942             .INTR => {
   6943                 try syscall.checkCancel();
   6944                 continue;
   6945             },
   6946             else => |e| {
   6947                 syscall.finish();
   6948                 switch (e) {
   6949                     .ACCES => return error.AccessDenied,
   6950                     .PERM => return error.PermissionDenied,
   6951                     .BUSY => return error.FileBusy,
   6952                     .FAULT => |err| return errnoBug(err),
   6953                     .IO => return error.FileSystem,
   6954                     .ISDIR => return error.IsDir,
   6955                     .LOOP => return error.SymLinkLoop,
   6956                     .NAMETOOLONG => return error.NameTooLong,
   6957                     .NOENT => return error.FileNotFound,
   6958                     .NOTDIR => return error.NotDir,
   6959                     .NOMEM => return error.SystemResources,
   6960                     .ROFS => return error.ReadOnlyFileSystem,
   6961                     .NOTCAPABLE => return error.AccessDenied,
   6962                     .ILSEQ => return error.BadPathName,
   6963                     .INVAL => |err| return errnoBug(err), // invalid flags, or pathname has . as last component
   6964                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   6965                     else => |err| return posix.unexpectedErrno(err),
   6966                 }
   6967             },
   6968         }
   6969     }
   6970 }
   6971 
   6972 fn dirDeleteFilePosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
   6973     const t: *Threaded = @ptrCast(@alignCast(userdata));
   6974     _ = t;
   6975 
   6976     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   6977     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   6978 
   6979     const syscall: Syscall = try .start();
   6980     while (true) {
   6981         switch (posix.errno(posix.system.unlinkat(dir.handle, sub_path_posix, 0))) {
   6982             .SUCCESS => {
   6983                 syscall.finish();
   6984                 return;
   6985             },
   6986             .INTR => {
   6987                 try syscall.checkCancel();
   6988                 continue;
   6989             },
   6990             // Some systems return permission errors when trying to delete a
   6991             // directory, so we need to handle that case specifically and
   6992             // translate the error.
   6993             .PERM => switch (native_os) {
   6994                 .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos, .freebsd, .netbsd, .dragonfly, .openbsd, .illumos => {
   6995 
   6996                     // Don't follow symlinks to match unlinkat (which acts on symlinks rather than follows them).
   6997                     var st = std.mem.zeroes(posix.Stat);
   6998                     while (true) {
   6999                         try syscall.checkCancel();
   7000                         switch (posix.errno(fstatat_sym(dir.handle, sub_path_posix, &st, posix.AT.SYMLINK_NOFOLLOW))) {
   7001                             .SUCCESS => {
   7002                                 syscall.finish();
   7003                                 break;
   7004                             },
   7005                             .INTR => continue,
   7006                             else => {
   7007                                 syscall.finish();
   7008                                 return error.PermissionDenied;
   7009                             },
   7010                         }
   7011                     }
   7012                     const is_dir = st.mode & posix.S.IFMT == posix.S.IFDIR;
   7013                     if (is_dir)
   7014                         return error.IsDir
   7015                     else
   7016                         return error.PermissionDenied;
   7017                 },
   7018                 else => {
   7019                     syscall.finish();
   7020                     return error.PermissionDenied;
   7021                 },
   7022             },
   7023             else => |e| {
   7024                 syscall.finish();
   7025                 switch (e) {
   7026                     .ACCES => return error.AccessDenied,
   7027                     .BUSY => return error.FileBusy,
   7028                     .FAULT => |err| return errnoBug(err),
   7029                     .IO => return error.FileSystem,
   7030                     .ISDIR => return error.IsDir,
   7031                     .LOOP => return error.SymLinkLoop,
   7032                     .NAMETOOLONG => return error.NameTooLong,
   7033                     .NOENT => return error.FileNotFound,
   7034                     .NOTDIR => return error.NotDir,
   7035                     .NOMEM => return error.SystemResources,
   7036                     .ROFS => return error.ReadOnlyFileSystem,
   7037                     .EXIST => |err| return errnoBug(err),
   7038                     .NOTEMPTY => |err| return errnoBug(err), // Not passing AT.REMOVEDIR
   7039                     .ILSEQ => return error.BadPathName,
   7040                     .INVAL => |err| return errnoBug(err), // invalid flags, or pathname has . as last component
   7041                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   7042                     else => |err| return posix.unexpectedErrno(err),
   7043                 }
   7044             },
   7045         }
   7046     }
   7047 }
   7048 
   7049 const dirDeleteDir = switch (native_os) {
   7050     .windows => dirDeleteDirWindows,
   7051     .wasi => dirDeleteDirWasi,
   7052     else => dirDeleteDirPosix,
   7053 };
   7054 
   7055 fn dirDeleteDirWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
   7056     return dirDeleteWindows(userdata, dir, sub_path, true) catch |err| switch (err) {
   7057         error.IsDir => unreachable,
   7058         else => |e| return e,
   7059     };
   7060 }
   7061 
   7062 fn dirDeleteWindows(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, remove_dir: bool) (Dir.DeleteDirError || Dir.DeleteFileError)!void {
   7063     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7064     _ = t;
   7065     const w = windows;
   7066 
   7067     if (std.mem.eql(u8, sub_path, "..")) {
   7068         // Can't remove the parent directory with an open handle.
   7069         return error.FileBusy;
   7070     }
   7071     var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
   7072     if (std.mem.eql(u8, sub_path, ".")) {
   7073         // Windows does not recognize this, but it does work with empty string.
   7074         sub_path_w.len = 0;
   7075     }
   7076     const attr: w.OBJECT.ATTRIBUTES = .{
   7077         .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
   7078         .ObjectName = @constCast(&sub_path_w.string()),
   7079     };
   7080 
   7081     var io_status_block: w.IO_STATUS_BLOCK = undefined;
   7082     var tmp_handle: w.HANDLE = undefined;
   7083     {
   7084         const syscall: Syscall = try .start();
   7085         while (true) switch (w.ntdll.NtCreateFile(
   7086             &tmp_handle,
   7087             .{ .STANDARD = .{
   7088                 .RIGHTS = .{ .DELETE = true },
   7089                 .SYNCHRONIZE = true,
   7090             } },
   7091             &attr,
   7092             &io_status_block,
   7093             null,
   7094             .{},
   7095             .VALID_FLAGS,
   7096             .OPEN,
   7097             .{
   7098                 .DIRECTORY_FILE = remove_dir,
   7099                 .IO = .SYNCHRONOUS_NONALERT,
   7100                 .NON_DIRECTORY_FILE = !remove_dir,
   7101                 .OPEN_REPARSE_POINT = true, // would we ever want to delete the target instead?
   7102             },
   7103             null,
   7104             0,
   7105         )) {
   7106             .SUCCESS => break syscall.finish(),
   7107             .OBJECT_NAME_INVALID => |err| return syscall.ntstatusBug(err),
   7108             .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
   7109             .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
   7110             .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
   7111             .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
   7112             .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
   7113             .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
   7114             .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
   7115             .SHARING_VIOLATION => return syscall.fail(error.FileBusy),
   7116             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   7117             .DELETE_PENDING => return syscall.finish(),
   7118             else => |rc| return syscall.unexpectedNtstatus(rc),
   7119         };
   7120     }
   7121     defer w.CloseHandle(tmp_handle);
   7122 
   7123     // FileDispositionInformationEx has varying levels of support:
   7124     // - FILE_DISPOSITION_INFORMATION_EX requires >= win10_rs1
   7125     //   (INVALID_INFO_CLASS is returned if not supported)
   7126     // - Requires the NTFS filesystem
   7127     //   (on filesystems like FAT32, INVALID_PARAMETER is returned)
   7128     // - FILE_DISPOSITION_POSIX_SEMANTICS requires >= win10_rs1
   7129     // - FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
   7130     //   (NOT_SUPPORTED is returned if a flag is unsupported)
   7131     //
   7132     // The strategy here is just to try using FileDispositionInformationEx and fall back to
   7133     // FileDispositionInformation if the return value lets us know that some aspect of it is not supported.
   7134     const rc = rc: {
   7135         // Deletion with posix semantics if the filesystem supports it.
   7136         var info: w.FILE.DISPOSITION.INFORMATION.EX = .{ .Flags = .{
   7137             .DELETE = true,
   7138             .POSIX_SEMANTICS = true,
   7139             .IGNORE_READONLY_ATTRIBUTE = true,
   7140         } };
   7141 
   7142         const syscall: Syscall = try .start();
   7143         while (true) switch (w.ntdll.NtSetInformationFile(
   7144             tmp_handle,
   7145             &io_status_block,
   7146             &info,
   7147             @sizeOf(w.FILE.DISPOSITION.INFORMATION.EX),
   7148             .DispositionEx,
   7149         )) {
   7150             .CANCELLED => {
   7151                 try syscall.checkCancel();
   7152                 continue;
   7153             },
   7154             // The filesystem does not support FileDispositionInformationEx
   7155             .INVALID_PARAMETER,
   7156             // The operating system does not support FileDispositionInformationEx
   7157             .INVALID_INFO_CLASS,
   7158             // The operating system does not support one of the flags
   7159             .NOT_SUPPORTED,
   7160             => break, // use fallback path below; `syscall` still active
   7161 
   7162             // For all other statuses, fall down to the switch below to handle them.
   7163             else => |rc| {
   7164                 syscall.finish();
   7165                 break :rc rc;
   7166             },
   7167         };
   7168 
   7169         // Deletion with file pending semantics, which requires waiting or moving
   7170         // files to get them removed (from here).
   7171         var file_dispo: w.FILE.DISPOSITION.INFORMATION = .{
   7172             .DeleteFile = w.TRUE,
   7173         };
   7174 
   7175         while (true) switch (w.ntdll.NtSetInformationFile(
   7176             tmp_handle,
   7177             &io_status_block,
   7178             &file_dispo,
   7179             @sizeOf(w.FILE.DISPOSITION.INFORMATION),
   7180             .Disposition,
   7181         )) {
   7182             .CANCELLED => {
   7183                 try syscall.checkCancel();
   7184                 continue;
   7185             },
   7186             else => |rc| {
   7187                 syscall.finish();
   7188                 break :rc rc;
   7189             },
   7190         };
   7191     };
   7192     switch (rc) {
   7193         .SUCCESS => {},
   7194         .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
   7195         .INVALID_PARAMETER => |err| return w.statusBug(err),
   7196         .CANNOT_DELETE => return error.AccessDenied,
   7197         .MEDIA_WRITE_PROTECTED => return error.AccessDenied,
   7198         .ACCESS_DENIED => return error.AccessDenied,
   7199         else => return w.unexpectedStatus(rc),
   7200     }
   7201 }
   7202 
   7203 fn dirDeleteDirWasi(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
   7204     if (builtin.link_libc) return dirDeleteDirPosix(userdata, dir, sub_path);
   7205 
   7206     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7207     _ = t;
   7208 
   7209     const syscall: Syscall = try .start();
   7210     while (true) {
   7211         const res = std.os.wasi.path_remove_directory(dir.handle, sub_path.ptr, sub_path.len);
   7212         switch (res) {
   7213             .SUCCESS => {
   7214                 syscall.finish();
   7215                 return;
   7216             },
   7217             .INTR => {
   7218                 try syscall.checkCancel();
   7219                 continue;
   7220             },
   7221             else => |e| {
   7222                 syscall.finish();
   7223                 switch (e) {
   7224                     .ACCES => return error.AccessDenied,
   7225                     .PERM => return error.PermissionDenied,
   7226                     .BUSY => return error.FileBusy,
   7227                     .FAULT => |err| return errnoBug(err),
   7228                     .IO => return error.FileSystem,
   7229                     .LOOP => return error.SymLinkLoop,
   7230                     .NAMETOOLONG => return error.NameTooLong,
   7231                     .NOENT => return error.FileNotFound,
   7232                     .NOTDIR => return error.NotDir,
   7233                     .NOMEM => return error.SystemResources,
   7234                     .ROFS => return error.ReadOnlyFileSystem,
   7235                     .NOTEMPTY => return error.DirNotEmpty,
   7236                     .NOTCAPABLE => return error.AccessDenied,
   7237                     .ILSEQ => return error.BadPathName,
   7238                     .INVAL => |err| return errnoBug(err), // invalid flags, or pathname has . as last component
   7239                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   7240                     else => |err| return posix.unexpectedErrno(err),
   7241                 }
   7242             },
   7243         }
   7244     }
   7245 }
   7246 
   7247 fn dirDeleteDirPosix(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
   7248     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7249     _ = t;
   7250 
   7251     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   7252     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   7253 
   7254     const syscall: Syscall = try .start();
   7255     while (true) {
   7256         switch (posix.errno(posix.system.unlinkat(dir.handle, sub_path_posix, posix.AT.REMOVEDIR))) {
   7257             .SUCCESS => {
   7258                 syscall.finish();
   7259                 return;
   7260             },
   7261             .INTR => {
   7262                 try syscall.checkCancel();
   7263                 continue;
   7264             },
   7265             else => |e| {
   7266                 syscall.finish();
   7267                 switch (e) {
   7268                     .ACCES => return error.AccessDenied,
   7269                     .PERM => return error.PermissionDenied,
   7270                     .BUSY => return error.FileBusy,
   7271                     .FAULT => |err| return errnoBug(err),
   7272                     .IO => return error.FileSystem,
   7273                     .ISDIR => |err| return errnoBug(err),
   7274                     .LOOP => return error.SymLinkLoop,
   7275                     .NAMETOOLONG => return error.NameTooLong,
   7276                     .NOENT => return error.FileNotFound,
   7277                     .NOTDIR => return error.NotDir,
   7278                     .NOMEM => return error.SystemResources,
   7279                     .ROFS => return error.ReadOnlyFileSystem,
   7280                     .EXIST => |err| return errnoBug(err),
   7281                     .NOTEMPTY => return error.DirNotEmpty,
   7282                     .ILSEQ => return error.BadPathName,
   7283                     .INVAL => |err| return errnoBug(err), // invalid flags, or pathname has . as last component
   7284                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   7285                     else => |err| return posix.unexpectedErrno(err),
   7286                 }
   7287             },
   7288         }
   7289     }
   7290 }
   7291 
   7292 const dirRename = switch (native_os) {
   7293     .windows => dirRenameWindows,
   7294     .wasi => dirRenameWasi,
   7295     else => dirRenamePosix,
   7296 };
   7297 
   7298 fn dirRenameWindows(
   7299     userdata: ?*anyopaque,
   7300     old_dir: Dir,
   7301     old_sub_path: []const u8,
   7302     new_dir: Dir,
   7303     new_sub_path: []const u8,
   7304 ) Dir.RenameError!void {
   7305     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7306     _ = t;
   7307     return dirRenameWindowsInner(old_dir, old_sub_path, new_dir, new_sub_path, true) catch |err| switch (err) {
   7308         error.PathAlreadyExists => return error.Unexpected,
   7309         error.OperationUnsupported => return error.Unexpected,
   7310         else => |e| return e,
   7311     };
   7312 }
   7313 
   7314 fn dirRenamePreserve(
   7315     userdata: ?*anyopaque,
   7316     old_dir: Dir,
   7317     old_sub_path: []const u8,
   7318     new_dir: Dir,
   7319     new_sub_path: []const u8,
   7320 ) Dir.RenamePreserveError!void {
   7321     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7322     if (is_windows) return dirRenameWindowsInner(old_dir, old_sub_path, new_dir, new_sub_path, false);
   7323     if (native_os == .linux) return dirRenamePreserveLinux(old_dir, old_sub_path, new_dir, new_sub_path);
   7324     // Make a hard link then delete the original.
   7325     try dirHardLink(t, old_dir, old_sub_path, new_dir, new_sub_path, .{ .follow_symlinks = false });
   7326     const prev = swapCancelProtection(t, .blocked);
   7327     defer _ = swapCancelProtection(t, prev);
   7328     dirDeleteFile(t, old_dir, old_sub_path) catch {};
   7329 }
   7330 
   7331 fn dirRenameWindowsInner(
   7332     old_dir: Dir,
   7333     old_sub_path: []const u8,
   7334     new_dir: Dir,
   7335     new_sub_path: []const u8,
   7336     replace_if_exists: bool,
   7337 ) Dir.RenamePreserveError!void {
   7338     const w = windows;
   7339     const old_path_w_buf = try sliceToPrefixedFileW(old_dir.handle, old_sub_path);
   7340     const old_path_w = old_path_w_buf.span();
   7341     const new_path_w_buf = try sliceToPrefixedFileW(new_dir.handle, new_sub_path);
   7342     const new_path_w = new_path_w_buf.span();
   7343 
   7344     const src_fd = src_fd: {
   7345         if (OpenFile(old_path_w, .{
   7346             .dir = old_dir.handle,
   7347             .access_mask = .{
   7348                 .GENERIC = .{ .WRITE = true },
   7349                 .STANDARD = .{
   7350                     .RIGHTS = .{ .DELETE = true },
   7351                     .SYNCHRONIZE = true,
   7352                 },
   7353             },
   7354             .creation = .OPEN,
   7355             .filter = .any, // This function is supposed to rename both files and directories.
   7356             .follow_symlinks = false,
   7357         })) |handle| {
   7358             break :src_fd handle;
   7359         } else |err| switch (err) {
   7360             error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
   7361             else => |e| return e,
   7362         }
   7363     };
   7364     defer w.CloseHandle(src_fd);
   7365 
   7366     var rc: w.NTSTATUS = undefined;
   7367     // FileRenameInformationEx has varying levels of support:
   7368     // - FILE_RENAME_INFORMATION_EX requires >= win10_rs1
   7369     //   (INVALID_INFO_CLASS is returned if not supported)
   7370     // - Requires the NTFS filesystem
   7371     //   (on filesystems like FAT32, INVALID_PARAMETER is returned)
   7372     // - FILE_RENAME_POSIX_SEMANTICS requires >= win10_rs1
   7373     // - FILE_RENAME_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5
   7374     //   (NOT_SUPPORTED is returned if a flag is unsupported)
   7375     //
   7376     // The strategy here is just to try using FileRenameInformationEx and fall back to
   7377     // FileRenameInformation if the return value lets us know that some aspect of it is not supported.
   7378     const need_fallback = need_fallback: {
   7379         var rename_info: w.FILE.RENAME_INFORMATION = .init(.{
   7380             .Flags = .{
   7381                 .REPLACE_IF_EXISTS = replace_if_exists,
   7382                 .POSIX_SEMANTICS = true,
   7383                 .IGNORE_READONLY_ATTRIBUTE = true,
   7384             },
   7385             .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
   7386             .FileName = new_path_w,
   7387         });
   7388         var io_status_block: w.IO_STATUS_BLOCK = undefined;
   7389         const rename_info_buf = rename_info.toBuffer();
   7390         rc = w.ntdll.NtSetInformationFile(
   7391             src_fd,
   7392             &io_status_block,
   7393             rename_info_buf.ptr,
   7394             @intCast(rename_info_buf.len),
   7395             .RenameEx,
   7396         );
   7397         switch (rc) {
   7398             .SUCCESS => return,
   7399             // The filesystem does not support FileDispositionInformationEx
   7400             .INVALID_PARAMETER,
   7401             // The operating system does not support FileDispositionInformationEx
   7402             .INVALID_INFO_CLASS,
   7403             // The operating system does not support one of the flags
   7404             .NOT_SUPPORTED,
   7405             => break :need_fallback true,
   7406             // For all other statuses, fall down to the switch below to handle them.
   7407             else => break :need_fallback false,
   7408         }
   7409     };
   7410 
   7411     if (need_fallback) {
   7412         var rename_info: w.FILE.RENAME_INFORMATION = .init(.{
   7413             .Flags = .{ .REPLACE_IF_EXISTS = replace_if_exists },
   7414             .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(new_path_w)) null else new_dir.handle,
   7415             .FileName = new_path_w,
   7416         });
   7417         var io_status_block: w.IO_STATUS_BLOCK = undefined;
   7418         const rename_info_buf = rename_info.toBuffer();
   7419         rc = w.ntdll.NtSetInformationFile(
   7420             src_fd,
   7421             &io_status_block,
   7422             rename_info_buf.ptr,
   7423             @intCast(rename_info_buf.len),
   7424             .Rename,
   7425         );
   7426     }
   7427 
   7428     switch (rc) {
   7429         .SUCCESS => {},
   7430         .INVALID_HANDLE => |err| return w.statusBug(err),
   7431         .INVALID_PARAMETER => |err| return w.statusBug(err),
   7432         .OBJECT_PATH_SYNTAX_BAD => |err| return w.statusBug(err),
   7433         .ACCESS_DENIED => return error.AccessDenied,
   7434         .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   7435         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   7436         .NOT_SAME_DEVICE => return error.CrossDevice,
   7437         .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
   7438         .DIRECTORY_NOT_EMPTY => return error.DirNotEmpty,
   7439         .FILE_IS_A_DIRECTORY => return error.IsDir,
   7440         .NOT_A_DIRECTORY => return error.NotDir,
   7441         else => return w.unexpectedStatus(rc),
   7442     }
   7443 }
   7444 
   7445 fn dirRenameWasi(
   7446     userdata: ?*anyopaque,
   7447     old_dir: Dir,
   7448     old_sub_path: []const u8,
   7449     new_dir: Dir,
   7450     new_sub_path: []const u8,
   7451 ) Dir.RenameError!void {
   7452     if (builtin.link_libc) return dirRenamePosix(userdata, old_dir, old_sub_path, new_dir, new_sub_path);
   7453 
   7454     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7455     _ = t;
   7456 
   7457     const syscall: Syscall = try .start();
   7458     while (true) {
   7459         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)) {
   7460             .SUCCESS => return syscall.finish(),
   7461             .INTR => {
   7462                 try syscall.checkCancel();
   7463                 continue;
   7464             },
   7465             else => |e| {
   7466                 syscall.finish();
   7467                 switch (e) {
   7468                     .ACCES => return error.AccessDenied,
   7469                     .PERM => return error.PermissionDenied,
   7470                     .BUSY => return error.FileBusy,
   7471                     .DQUOT => return error.DiskQuota,
   7472                     .FAULT => |err| return errnoBug(err),
   7473                     .INVAL => |err| return errnoBug(err),
   7474                     .ISDIR => return error.IsDir,
   7475                     .LOOP => return error.SymLinkLoop,
   7476                     .MLINK => return error.LinkQuotaExceeded,
   7477                     .NAMETOOLONG => return error.NameTooLong,
   7478                     .NOENT => return error.FileNotFound,
   7479                     .NOTDIR => return error.NotDir,
   7480                     .NOMEM => return error.SystemResources,
   7481                     .NOSPC => return error.NoSpaceLeft,
   7482                     .EXIST => return error.DirNotEmpty,
   7483                     .NOTEMPTY => return error.DirNotEmpty,
   7484                     .ROFS => return error.ReadOnlyFileSystem,
   7485                     .XDEV => return error.CrossDevice,
   7486                     .NOTCAPABLE => return error.AccessDenied,
   7487                     .ILSEQ => return error.BadPathName,
   7488                     else => |err| return posix.unexpectedErrno(err),
   7489                 }
   7490             },
   7491         }
   7492     }
   7493 }
   7494 
   7495 fn dirRenamePosix(
   7496     userdata: ?*anyopaque,
   7497     old_dir: Dir,
   7498     old_sub_path: []const u8,
   7499     new_dir: Dir,
   7500     new_sub_path: []const u8,
   7501 ) Dir.RenameError!void {
   7502     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7503     _ = t;
   7504 
   7505     var old_path_buffer: [posix.PATH_MAX]u8 = undefined;
   7506     var new_path_buffer: [posix.PATH_MAX]u8 = undefined;
   7507 
   7508     const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);
   7509     const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
   7510 
   7511     return renameat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix);
   7512 }
   7513 
   7514 fn dirRenamePreserveLinux(
   7515     old_dir: Dir,
   7516     old_sub_path: []const u8,
   7517     new_dir: Dir,
   7518     new_sub_path: []const u8,
   7519 ) Dir.RenamePreserveError!void {
   7520     const linux = std.os.linux;
   7521 
   7522     var old_path_buffer: [linux.PATH_MAX]u8 = undefined;
   7523     var new_path_buffer: [linux.PATH_MAX]u8 = undefined;
   7524 
   7525     const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);
   7526     const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
   7527 
   7528     const syscall: Syscall = try .start();
   7529     while (true) switch (linux.errno(linux.renameat2(
   7530         old_dir.handle,
   7531         old_sub_path_posix,
   7532         new_dir.handle,
   7533         new_sub_path_posix,
   7534         .{ .NOREPLACE = true },
   7535     ))) {
   7536         .SUCCESS => return syscall.finish(),
   7537         .INTR => {
   7538             try syscall.checkCancel();
   7539             continue;
   7540         },
   7541         .ACCES => return syscall.fail(error.AccessDenied),
   7542         .PERM => return syscall.fail(error.PermissionDenied),
   7543         .BUSY => return syscall.fail(error.FileBusy),
   7544         .DQUOT => return syscall.fail(error.DiskQuota),
   7545         .ISDIR => return syscall.fail(error.IsDir),
   7546         .LOOP => return syscall.fail(error.SymLinkLoop),
   7547         .MLINK => return syscall.fail(error.LinkQuotaExceeded),
   7548         .NAMETOOLONG => return syscall.fail(error.NameTooLong),
   7549         .NOENT => return syscall.fail(error.FileNotFound),
   7550         .NOTDIR => return syscall.fail(error.NotDir),
   7551         .NOMEM => return syscall.fail(error.SystemResources),
   7552         .NOSPC => return syscall.fail(error.NoSpaceLeft),
   7553         .EXIST => return syscall.fail(error.PathAlreadyExists),
   7554         .NOTEMPTY => return syscall.fail(error.DirNotEmpty),
   7555         .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
   7556         .XDEV => return syscall.fail(error.CrossDevice),
   7557         .ILSEQ => return syscall.fail(error.BadPathName),
   7558         .FAULT => |err| return syscall.errnoBug(err),
   7559         .INVAL => |err| return syscall.errnoBug(err),
   7560         else => |err| return syscall.unexpectedErrno(err),
   7561     };
   7562 }
   7563 
   7564 fn renameat(
   7565     old_dir: posix.fd_t,
   7566     old_sub_path: [*:0]const u8,
   7567     new_dir: posix.fd_t,
   7568     new_sub_path: [*:0]const u8,
   7569 ) Dir.RenameError!void {
   7570     const syscall: Syscall = try .start();
   7571     while (true) switch (posix.errno(posix.system.renameat(old_dir, old_sub_path, new_dir, new_sub_path))) {
   7572         .SUCCESS => return syscall.finish(),
   7573         .INTR => {
   7574             try syscall.checkCancel();
   7575             continue;
   7576         },
   7577         .ACCES => return syscall.fail(error.AccessDenied),
   7578         .PERM => return syscall.fail(error.PermissionDenied),
   7579         .BUSY => return syscall.fail(error.FileBusy),
   7580         .DQUOT => return syscall.fail(error.DiskQuota),
   7581         .ISDIR => return syscall.fail(error.IsDir),
   7582         .IO => return syscall.fail(error.HardwareFailure),
   7583         .LOOP => return syscall.fail(error.SymLinkLoop),
   7584         .MLINK => return syscall.fail(error.LinkQuotaExceeded),
   7585         .NAMETOOLONG => return syscall.fail(error.NameTooLong),
   7586         .NOENT => return syscall.fail(error.FileNotFound),
   7587         .NOTDIR => return syscall.fail(error.NotDir),
   7588         .NOMEM => return syscall.fail(error.SystemResources),
   7589         .NOSPC => return syscall.fail(error.NoSpaceLeft),
   7590         .EXIST => return syscall.fail(error.DirNotEmpty),
   7591         .NOTEMPTY => return syscall.fail(error.DirNotEmpty),
   7592         .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
   7593         .XDEV => return syscall.fail(error.CrossDevice),
   7594         .ILSEQ => return syscall.fail(error.BadPathName),
   7595         .FAULT => |err| return syscall.errnoBug(err),
   7596         .INVAL => |err| return syscall.errnoBug(err),
   7597         else => |err| return syscall.unexpectedErrno(err),
   7598     };
   7599 }
   7600 
   7601 fn renameatPreserve(
   7602     old_dir: posix.fd_t,
   7603     old_sub_path: [*:0]const u8,
   7604     new_dir: posix.fd_t,
   7605     new_sub_path: [*:0]const u8,
   7606 ) Dir.RenameError!void {
   7607     const syscall: Syscall = try .start();
   7608     while (true) {
   7609         switch (posix.errno(posix.system.renameat(old_dir, old_sub_path, new_dir, new_sub_path))) {
   7610             .SUCCESS => return syscall.finish(),
   7611             .INTR => {
   7612                 try syscall.checkCancel();
   7613                 continue;
   7614             },
   7615             else => |e| {
   7616                 syscall.finish();
   7617                 switch (e) {
   7618                     .ACCES => return error.AccessDenied,
   7619                     .PERM => return error.PermissionDenied,
   7620                     .BUSY => return error.FileBusy,
   7621                     .DQUOT => return error.DiskQuota,
   7622                     .FAULT => |err| return errnoBug(err),
   7623                     .INVAL => |err| return errnoBug(err),
   7624                     .ISDIR => return error.IsDir,
   7625                     .LOOP => return error.SymLinkLoop,
   7626                     .MLINK => return error.LinkQuotaExceeded,
   7627                     .NAMETOOLONG => return error.NameTooLong,
   7628                     .NOENT => return error.FileNotFound,
   7629                     .NOTDIR => return error.NotDir,
   7630                     .NOMEM => return error.SystemResources,
   7631                     .NOSPC => return error.NoSpaceLeft,
   7632                     .EXIST => return error.PathAlreadyExists,
   7633                     .NOTEMPTY => return error.PathAlreadyExists,
   7634                     .ROFS => return error.ReadOnlyFileSystem,
   7635                     .XDEV => return error.CrossDevice,
   7636                     .ILSEQ => return error.BadPathName,
   7637                     else => |err| return posix.unexpectedErrno(err),
   7638                 }
   7639             },
   7640         }
   7641     }
   7642 }
   7643 
   7644 const dirSymLink = switch (native_os) {
   7645     .windows => dirSymLinkWindows,
   7646     .wasi => dirSymLinkWasi,
   7647     else => dirSymLinkPosix,
   7648 };
   7649 
   7650 fn dirSymLinkWindows(
   7651     userdata: ?*anyopaque,
   7652     dir: Dir,
   7653     target_path: []const u8,
   7654     sym_link_path: []const u8,
   7655     flags: Dir.SymLinkFlags,
   7656 ) Dir.SymLinkError!void {
   7657     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7658     _ = t;
   7659     const w = windows;
   7660 
   7661     // Target path does not use sliceToPrefixedFileW because certain paths
   7662     // are handled differently when creating a symlink than they would be
   7663     // when converting to an NT namespaced path.
   7664     var target_path_w: WindowsPathSpace = undefined;
   7665     target_path_w.len = try w.wtf8ToWtf16Le(&target_path_w.data, target_path);
   7666     target_path_w.data[target_path_w.len] = 0;
   7667     // However, we need to canonicalize any path separators to `\`, since if
   7668     // the target path is relative, then it must use `\` as the path separator.
   7669     std.mem.replaceScalar(
   7670         u16,
   7671         target_path_w.data[0..target_path_w.len],
   7672         std.mem.nativeToLittle(u16, '/'),
   7673         std.mem.nativeToLittle(u16, '\\'),
   7674     );
   7675 
   7676     const sym_link_path_w = try sliceToPrefixedFileW(dir.handle, sym_link_path);
   7677 
   7678     const SYMLINK_DATA = extern struct {
   7679         ReparseTag: w.IO_REPARSE_TAG,
   7680         ReparseDataLength: w.USHORT,
   7681         Reserved: w.USHORT,
   7682         SubstituteNameOffset: w.USHORT,
   7683         SubstituteNameLength: w.USHORT,
   7684         PrintNameOffset: w.USHORT,
   7685         PrintNameLength: w.USHORT,
   7686         Flags: w.ULONG,
   7687     };
   7688 
   7689     const symlink_handle = handle: {
   7690         if (OpenFile(sym_link_path_w.span(), .{
   7691             .access_mask = .{
   7692                 .GENERIC = .{ .READ = true, .WRITE = true },
   7693                 .STANDARD = .{ .SYNCHRONIZE = true },
   7694             },
   7695             .dir = dir.handle,
   7696             .creation = .CREATE,
   7697             .filter = if (flags.is_directory) .dir_only else .non_directory_only,
   7698         })) |handle| {
   7699             break :handle handle;
   7700         } else |err| switch (err) {
   7701             error.IsDir => return error.PathAlreadyExists,
   7702             error.NotDir => return error.Unexpected,
   7703             error.WouldBlock => return error.Unexpected,
   7704             error.PipeBusy => return error.Unexpected,
   7705             error.FileBusy => return error.Unexpected,
   7706             error.NoDevice => return error.Unexpected,
   7707             error.AntivirusInterference => return error.Unexpected,
   7708             else => |e| return e,
   7709         }
   7710     };
   7711     defer w.CloseHandle(symlink_handle);
   7712 
   7713     // Relevant portions of the documentation:
   7714     // > Relative links are specified using the following conventions:
   7715     // > - Root relative—for example, "\Windows\System32" resolves to "current drive:\Windows\System32".
   7716     // > - Current working directory–relative—for example, if the current working directory is
   7717     // >   C:\Windows\System32, "C:File.txt" resolves to "C:\Windows\System32\File.txt".
   7718     // > Note: If you specify a current working directory–relative link, it is created as an absolute
   7719     // > link, due to the way the current working directory is processed based on the user and the thread.
   7720     // https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
   7721     var is_target_absolute = false;
   7722     const final_target_path = target_path: {
   7723         if (w.hasCommonNtPrefix(u16, target_path_w.span())) {
   7724             // Already an NT path, no need to do anything to it
   7725             break :target_path target_path_w.span();
   7726         } else {
   7727             switch (Dir.path.getWin32PathType(u16, target_path_w.span())) {
   7728                 // Rooted paths need to avoid getting put through wToPrefixedFileW
   7729                 // (and they are treated as relative in this context)
   7730                 // Note: It seems that rooted paths in symbolic links are relative to
   7731                 //       the drive that the symbolic exists on, not to the CWD's drive.
   7732                 //       So, if the symlink is on C:\ and the CWD is on D:\,
   7733                 //       it will still resolve the path relative to the root of
   7734                 //       the C:\ drive.
   7735                 .rooted => break :target_path target_path_w.span(),
   7736                 // Keep relative paths relative, but anything else needs to get NT-prefixed.
   7737                 else => if (!Dir.path.isAbsoluteWindowsWtf16(target_path_w.span()))
   7738                     break :target_path target_path_w.span(),
   7739             }
   7740         }
   7741         var prefixed_target_path = try wToPrefixedFileW(dir.handle, target_path_w.span());
   7742         // We do this after prefixing to ensure that drive-relative paths are treated as absolute
   7743         is_target_absolute = Dir.path.isAbsoluteWindowsWtf16(prefixed_target_path.span());
   7744         break :target_path prefixed_target_path.span();
   7745     };
   7746 
   7747     // prepare reparse data buffer
   7748     var buffer: [w.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
   7749     const buf_len = @sizeOf(SYMLINK_DATA) + final_target_path.len * 4;
   7750     const header_len = @sizeOf(w.ULONG) + @sizeOf(w.USHORT) * 2;
   7751     const target_is_absolute = Dir.path.isAbsoluteWindowsWtf16(final_target_path);
   7752     const symlink_data: SYMLINK_DATA = .{
   7753         .ReparseTag = .SYMLINK,
   7754         .ReparseDataLength = @intCast(buf_len - header_len),
   7755         .Reserved = 0,
   7756         .SubstituteNameOffset = @intCast(final_target_path.len * 2),
   7757         .SubstituteNameLength = @intCast(final_target_path.len * 2),
   7758         .PrintNameOffset = 0,
   7759         .PrintNameLength = @intCast(final_target_path.len * 2),
   7760         .Flags = if (!target_is_absolute) w.SYMLINK_FLAG_RELATIVE else 0,
   7761     };
   7762 
   7763     @memcpy(buffer[0..@sizeOf(SYMLINK_DATA)], std.mem.asBytes(&symlink_data));
   7764     @memcpy(buffer[@sizeOf(SYMLINK_DATA)..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
   7765     const paths_start = @sizeOf(SYMLINK_DATA) + final_target_path.len * 2;
   7766     @memcpy(buffer[paths_start..][0 .. final_target_path.len * 2], @as([*]const u8, @ptrCast(final_target_path)));
   7767     switch ((try deviceIoControl(&.{
   7768         .file = .{ .handle = symlink_handle, .flags = .{ .nonblocking = false } },
   7769         .code = .SET_REPARSE_POINT,
   7770         .in = buffer[0..buf_len],
   7771     })).u.Status) {
   7772         .SUCCESS => {},
   7773         .PRIVILEGE_NOT_HELD => return error.PermissionDenied,
   7774         .ACCESS_DENIED => return error.AccessDenied,
   7775         .INVALID_DEVICE_REQUEST => return error.FileSystem,
   7776         else => |status| return w.unexpectedStatus(status),
   7777     }
   7778 }
   7779 
   7780 fn dirSymLinkWasi(
   7781     userdata: ?*anyopaque,
   7782     dir: Dir,
   7783     target_path: []const u8,
   7784     sym_link_path: []const u8,
   7785     flags: Dir.SymLinkFlags,
   7786 ) Dir.SymLinkError!void {
   7787     if (builtin.link_libc) return dirSymLinkPosix(userdata, dir, target_path, sym_link_path, flags);
   7788 
   7789     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7790     _ = t;
   7791 
   7792     const syscall: Syscall = try .start();
   7793     while (true) {
   7794         switch (std.os.wasi.path_symlink(target_path.ptr, target_path.len, dir.handle, sym_link_path.ptr, sym_link_path.len)) {
   7795             .SUCCESS => return syscall.finish(),
   7796             .INTR => {
   7797                 try syscall.checkCancel();
   7798                 continue;
   7799             },
   7800             else => |e| {
   7801                 syscall.finish();
   7802                 switch (e) {
   7803                     .FAULT => |err| return errnoBug(err),
   7804                     .INVAL => |err| return errnoBug(err),
   7805                     .BADF => |err| return errnoBug(err),
   7806                     .ACCES => return error.AccessDenied,
   7807                     .PERM => return error.PermissionDenied,
   7808                     .DQUOT => return error.DiskQuota,
   7809                     .EXIST => return error.PathAlreadyExists,
   7810                     .IO => return error.FileSystem,
   7811                     .LOOP => return error.SymLinkLoop,
   7812                     .NAMETOOLONG => return error.NameTooLong,
   7813                     .NOENT => return error.FileNotFound,
   7814                     .NOTDIR => return error.NotDir,
   7815                     .NOMEM => return error.SystemResources,
   7816                     .NOSPC => return error.NoSpaceLeft,
   7817                     .ROFS => return error.ReadOnlyFileSystem,
   7818                     .NOTCAPABLE => return error.AccessDenied,
   7819                     .ILSEQ => return error.BadPathName,
   7820                     else => |err| return posix.unexpectedErrno(err),
   7821                 }
   7822             },
   7823         }
   7824     }
   7825 }
   7826 
   7827 fn dirSymLinkPosix(
   7828     userdata: ?*anyopaque,
   7829     dir: Dir,
   7830     target_path: []const u8,
   7831     sym_link_path: []const u8,
   7832     flags: Dir.SymLinkFlags,
   7833 ) Dir.SymLinkError!void {
   7834     _ = flags;
   7835     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7836     _ = t;
   7837 
   7838     var target_path_buffer: [posix.PATH_MAX]u8 = undefined;
   7839     var sym_link_path_buffer: [posix.PATH_MAX]u8 = undefined;
   7840 
   7841     const target_path_posix = try pathToPosix(target_path, &target_path_buffer);
   7842     const sym_link_path_posix = try pathToPosix(sym_link_path, &sym_link_path_buffer);
   7843 
   7844     const syscall: Syscall = try .start();
   7845     while (true) {
   7846         switch (posix.errno(posix.system.symlinkat(target_path_posix, dir.handle, sym_link_path_posix))) {
   7847             .SUCCESS => return syscall.finish(),
   7848             .INTR => {
   7849                 try syscall.checkCancel();
   7850                 continue;
   7851             },
   7852             else => |e| {
   7853                 syscall.finish();
   7854                 switch (e) {
   7855                     .FAULT => |err| return errnoBug(err),
   7856                     .INVAL => |err| return errnoBug(err),
   7857                     .ACCES => return error.AccessDenied,
   7858                     .PERM => return error.PermissionDenied,
   7859                     .DQUOT => return error.DiskQuota,
   7860                     .EXIST => return error.PathAlreadyExists,
   7861                     .IO => return error.FileSystem,
   7862                     .LOOP => return error.SymLinkLoop,
   7863                     .NAMETOOLONG => return error.NameTooLong,
   7864                     .NOENT => return error.FileNotFound,
   7865                     .NOTDIR => return error.NotDir,
   7866                     .NOMEM => return error.SystemResources,
   7867                     .NOSPC => return error.NoSpaceLeft,
   7868                     .ROFS => return error.ReadOnlyFileSystem,
   7869                     .ILSEQ => return error.BadPathName,
   7870                     else => |err| return posix.unexpectedErrno(err),
   7871                 }
   7872             },
   7873         }
   7874     }
   7875 }
   7876 
   7877 fn dirReadLink(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
   7878     const t: *Threaded = @ptrCast(@alignCast(userdata));
   7879     _ = t;
   7880     switch (native_os) {
   7881         .windows => return dirReadLinkWindows(dir, sub_path, buffer),
   7882         .wasi => return dirReadLinkWasi(dir, sub_path, buffer),
   7883         else => return dirReadLinkPosix(dir, sub_path, buffer),
   7884     }
   7885 }
   7886 
   7887 fn dirReadLinkWindows(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
   7888     // This gets used once for `sub_path` and then reused again temporarily
   7889     // before converting back to `buffer`.
   7890     var sub_path_w = try sliceToPrefixedFileW(dir.handle, sub_path);
   7891     const attr: windows.OBJECT.ATTRIBUTES = .{
   7892         .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w.span())) null else dir.handle,
   7893         .ObjectName = @constCast(&sub_path_w.string()),
   7894     };
   7895     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   7896     var result_handle: windows.HANDLE = undefined;
   7897     var attempt: u5 = 0;
   7898     var syscall: Syscall = try .start();
   7899     while (true) switch (windows.ntdll.NtCreateFile(
   7900         &result_handle,
   7901         .{
   7902             .SPECIFIC = .{ .FILE = .{
   7903                 .READ_ATTRIBUTES = true,
   7904             } },
   7905             .STANDARD = .{ .SYNCHRONIZE = true },
   7906         },
   7907         &attr,
   7908         &io_status_block,
   7909         null,
   7910         .{ .NORMAL = true },
   7911         .VALID_FLAGS,
   7912         .OPEN,
   7913         .{
   7914             .DIRECTORY_FILE = false,
   7915             .NON_DIRECTORY_FILE = false,
   7916             .IO = .ASYNCHRONOUS,
   7917             .OPEN_REPARSE_POINT = true,
   7918         },
   7919         null,
   7920         0,
   7921     )) {
   7922         .SUCCESS => {
   7923             syscall.finish();
   7924             break;
   7925         },
   7926         .CANCELLED => {
   7927             try syscall.checkCancel();
   7928             continue;
   7929         },
   7930         .SHARING_VIOLATION => {
   7931             // This occurs if the file attempting to be opened is a running
   7932             // executable. However, there's a kernel bug: the error may be
   7933             // incorrectly returned for an indeterminate amount of time
   7934             // after an executable file is closed. Here we work around the
   7935             // kernel bug with retry attempts.
   7936             syscall.finish();
   7937             if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
   7938             try parking_sleep.sleep(.{ .duration = .{
   7939                 .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
   7940                 .clock = .awake,
   7941             } });
   7942             attempt += 1;
   7943             syscall = try .start();
   7944             continue;
   7945         },
   7946         .DELETE_PENDING => {
   7947             // This error means that there *was* a file in this location on
   7948             // the file system, but it was deleted. However, the OS is not
   7949             // finished with the deletion operation, and so this CreateFile
   7950             // call has failed. Here, we simulate the kernel bug being
   7951             // fixed by sleeping and retrying until the error goes away.
   7952             syscall.finish();
   7953             if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
   7954             try parking_sleep.sleep(.{ .duration = .{
   7955                 .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
   7956                 .clock = .awake,
   7957             } });
   7958             attempt += 1;
   7959             syscall = try .start();
   7960             continue;
   7961         },
   7962         .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
   7963         .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
   7964         .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
   7965         .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
   7966         .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
   7967         .NO_MEDIA_IN_DEVICE => return syscall.fail(error.FileNotFound),
   7968         .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   7969         .PIPE_BUSY => return syscall.fail(error.AccessDenied),
   7970         .PIPE_NOT_AVAILABLE => return syscall.fail(error.FileNotFound),
   7971         .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
   7972         .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
   7973         .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
   7974         .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
   7975         .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
   7976         else => |status| return syscall.unexpectedNtstatus(status),
   7977     };
   7978     defer windows.CloseHandle(result_handle);
   7979 
   7980     var reparse_buf: [windows.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 align(@alignOf(windows.REPARSE_DATA_BUFFER)) = undefined;
   7981 
   7982     syscall = try .start();
   7983     while (true) switch (windows.ntdll.NtFsControlFile(
   7984         result_handle,
   7985         null, // event
   7986         null, // APC routine
   7987         null, // APC context
   7988         &io_status_block,
   7989         .GET_REPARSE_POINT,
   7990         null, // input buffer
   7991         0, // input buffer length
   7992         &reparse_buf,
   7993         reparse_buf.len,
   7994     )) {
   7995         .SUCCESS => {
   7996             syscall.finish();
   7997             break;
   7998         },
   7999         .CANCELLED => {
   8000             try syscall.checkCancel();
   8001             continue;
   8002         },
   8003         .NOT_A_REPARSE_POINT => return syscall.fail(error.NotLink),
   8004         else => |status| return syscall.unexpectedNtstatus(status),
   8005     };
   8006 
   8007     const reparse_struct: *const windows.REPARSE_DATA_BUFFER = @ptrCast(@alignCast(&reparse_buf));
   8008     const IoReparseTagInt = @typeInfo(windows.IO_REPARSE_TAG).@"struct".backing_integer.?;
   8009     const result_w = switch (@as(IoReparseTagInt, @bitCast(reparse_struct.ReparseTag))) {
   8010         @as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.SYMLINK)) => r: {
   8011             const buf: *const windows.SYMBOLIC_LINK_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
   8012             const offset = buf.SubstituteNameOffset >> 1;
   8013             const len = buf.SubstituteNameLength >> 1;
   8014             const path_buf = @as([*]const u16, &buf.PathBuffer);
   8015             const is_relative = buf.Flags & windows.SYMLINK_FLAG_RELATIVE != 0;
   8016             break :r try parseReadLinkPath(path_buf[offset..][0..len], is_relative, &sub_path_w.data);
   8017         },
   8018         @as(IoReparseTagInt, @bitCast(windows.IO_REPARSE_TAG.MOUNT_POINT)) => r: {
   8019             const buf: *const windows.MOUNT_POINT_REPARSE_BUFFER = @ptrCast(@alignCast(&reparse_struct.DataBuffer[0]));
   8020             const offset = buf.SubstituteNameOffset >> 1;
   8021             const len = buf.SubstituteNameLength >> 1;
   8022             const path_buf = @as([*]const u16, &buf.PathBuffer);
   8023             break :r try parseReadLinkPath(path_buf[offset..][0..len], false, &sub_path_w.data);
   8024         },
   8025         else => return error.UnsupportedReparsePointType,
   8026     };
   8027     const len = std.unicode.calcWtf8Len(result_w);
   8028     if (len > buffer.len) return error.NameTooLong;
   8029 
   8030     return std.unicode.wtf16LeToWtf8(buffer, result_w);
   8031 }
   8032 
   8033 fn parseReadLinkPath(path: []const u16, is_relative: bool, out_buffer: []u16) error{NameTooLong}![]u16 {
   8034     path: {
   8035         if (is_relative) break :path;
   8036         return windows.ntToWin32Namespace(path, out_buffer) catch |err| switch (err) {
   8037             error.NameTooLong => |e| return e,
   8038             error.NotNtPath => break :path,
   8039         };
   8040     }
   8041     if (out_buffer.len < path.len) return error.NameTooLong;
   8042     const dest = out_buffer[0..path.len];
   8043     @memcpy(dest, path);
   8044     return dest;
   8045 }
   8046 
   8047 fn dirReadLinkWasi(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
   8048     if (builtin.link_libc) return dirReadLinkPosix(dir, sub_path, buffer);
   8049 
   8050     var n: usize = undefined;
   8051     const syscall: Syscall = try .start();
   8052     while (true) {
   8053         switch (std.os.wasi.path_readlink(dir.handle, sub_path.ptr, sub_path.len, buffer.ptr, buffer.len, &n)) {
   8054             .SUCCESS => {
   8055                 syscall.finish();
   8056                 return n;
   8057             },
   8058             .INTR => {
   8059                 try syscall.checkCancel();
   8060                 continue;
   8061             },
   8062             else => |e| {
   8063                 syscall.finish();
   8064                 switch (e) {
   8065                     .ACCES => return error.AccessDenied,
   8066                     .FAULT => |err| return errnoBug(err),
   8067                     .INVAL => return error.NotLink,
   8068                     .IO => return error.FileSystem,
   8069                     .LOOP => return error.SymLinkLoop,
   8070                     .NAMETOOLONG => return error.NameTooLong,
   8071                     .NOENT => return error.FileNotFound,
   8072                     .NOMEM => return error.SystemResources,
   8073                     .NOTDIR => return error.NotDir,
   8074                     .NOTCAPABLE => return error.AccessDenied,
   8075                     .ILSEQ => return error.BadPathName,
   8076                     else => |err| return posix.unexpectedErrno(err),
   8077                 }
   8078             },
   8079         }
   8080     }
   8081 }
   8082 
   8083 fn dirReadLinkPosix(dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
   8084     var sub_path_buffer: [posix.PATH_MAX]u8 = undefined;
   8085     const sub_path_posix = try pathToPosix(sub_path, &sub_path_buffer);
   8086 
   8087     const syscall: Syscall = try .start();
   8088     while (true) {
   8089         const rc = posix.system.readlinkat(dir.handle, sub_path_posix, buffer.ptr, buffer.len);
   8090         switch (posix.errno(rc)) {
   8091             .SUCCESS => {
   8092                 syscall.finish();
   8093                 const len: usize = @bitCast(rc);
   8094                 return len;
   8095             },
   8096             .INTR => {
   8097                 try syscall.checkCancel();
   8098                 continue;
   8099             },
   8100             else => |e| {
   8101                 syscall.finish();
   8102                 switch (e) {
   8103                     .ACCES => return error.AccessDenied,
   8104                     .FAULT => |err| return errnoBug(err),
   8105                     .INVAL => return error.NotLink,
   8106                     .IO => return error.FileSystem,
   8107                     .LOOP => return error.SymLinkLoop,
   8108                     .NAMETOOLONG => return error.NameTooLong,
   8109                     .NOENT => return error.FileNotFound,
   8110                     .NOMEM => return error.SystemResources,
   8111                     .NOTDIR => return error.NotDir,
   8112                     .ILSEQ => return error.BadPathName,
   8113                     else => |err| return posix.unexpectedErrno(err),
   8114                 }
   8115             },
   8116         }
   8117     }
   8118 }
   8119 
   8120 const dirSetPermissions = switch (native_os) {
   8121     .windows => dirSetPermissionsWindows,
   8122     else => dirSetPermissionsPosix,
   8123 };
   8124 
   8125 fn dirSetPermissionsWindows(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void {
   8126     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8127     _ = t;
   8128     _ = dir;
   8129     _ = permissions;
   8130     @panic("TODO implement dirSetPermissionsWindows");
   8131 }
   8132 
   8133 fn dirSetPermissionsPosix(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void {
   8134     if (@sizeOf(Dir.Permissions) == 0) return;
   8135     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8136     _ = t;
   8137     return setPermissionsPosix(dir.handle, permissions.toMode());
   8138 }
   8139 
   8140 fn dirSetFilePermissions(
   8141     userdata: ?*anyopaque,
   8142     dir: Dir,
   8143     sub_path: []const u8,
   8144     permissions: Dir.Permissions,
   8145     options: Dir.SetFilePermissionsOptions,
   8146 ) Dir.SetFilePermissionsError!void {
   8147     if (@sizeOf(Dir.Permissions) == 0) return;
   8148     if (is_windows) @panic("TODO implement dirSetFilePermissions windows");
   8149     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8150 
   8151     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   8152     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   8153 
   8154     const mode = permissions.toMode();
   8155     const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
   8156 
   8157     return posixFchmodat(t, dir.handle, sub_path_posix, mode, flags);
   8158 }
   8159 
   8160 fn posixFchmodat(
   8161     t: *Threaded,
   8162     dir_fd: posix.fd_t,
   8163     path: [*:0]const u8,
   8164     mode: posix.mode_t,
   8165     flags: u32,
   8166 ) Dir.SetFilePermissionsError!void {
   8167     // No special handling for linux is needed if we can use the libc fallback
   8168     // or `flags` is empty. Glibc only added the fallback in 2.32.
   8169     if (have_fchmodat_flags or flags == 0) {
   8170         const syscall: Syscall = try .start();
   8171         while (true) {
   8172             const rc = if (have_fchmodat_flags or builtin.link_libc)
   8173                 posix.system.fchmodat(dir_fd, path, mode, flags)
   8174             else
   8175                 posix.system.fchmodat(dir_fd, path, mode);
   8176             switch (posix.errno(rc)) {
   8177                 .SUCCESS => return syscall.finish(),
   8178                 .INTR => {
   8179                     try syscall.checkCancel();
   8180                     continue;
   8181                 },
   8182                 else => |e| {
   8183                     syscall.finish();
   8184                     switch (e) {
   8185                         .BADF => |err| return errnoBug(err),
   8186                         .FAULT => |err| return errnoBug(err),
   8187                         .INVAL => |err| return errnoBug(err),
   8188                         .ACCES => return error.AccessDenied,
   8189                         .IO => return error.InputOutput,
   8190                         .LOOP => return error.SymLinkLoop,
   8191                         .MFILE => return error.ProcessFdQuotaExceeded,
   8192                         .NAMETOOLONG => return error.NameTooLong,
   8193                         .NFILE => return error.SystemFdQuotaExceeded,
   8194                         .NOENT => return error.FileNotFound,
   8195                         .NOTDIR => return error.FileNotFound,
   8196                         .NOMEM => return error.SystemResources,
   8197                         .OPNOTSUPP => return error.OperationUnsupported,
   8198                         .PERM => return error.PermissionDenied,
   8199                         .ROFS => return error.ReadOnlyFileSystem,
   8200                         else => |err| return posix.unexpectedErrno(err),
   8201                     }
   8202                 },
   8203             }
   8204         }
   8205     }
   8206 
   8207     if (@atomicLoad(UseFchmodat2, &t.use_fchmodat2, .monotonic) == .disabled)
   8208         return fchmodatFallback(dir_fd, path, mode);
   8209 
   8210     comptime assert(native_os == .linux);
   8211 
   8212     const syscall: Syscall = try .start();
   8213     while (true) {
   8214         switch (std.os.linux.errno(std.os.linux.fchmodat2(dir_fd, path, mode, flags))) {
   8215             .SUCCESS => return syscall.finish(),
   8216             .INTR => {
   8217                 try syscall.checkCancel();
   8218                 continue;
   8219             },
   8220             else => |e| {
   8221                 syscall.finish();
   8222                 switch (e) {
   8223                     .BADF => |err| return errnoBug(err),
   8224                     .FAULT => |err| return errnoBug(err),
   8225                     .INVAL => |err| return errnoBug(err),
   8226                     .ACCES => return error.AccessDenied,
   8227                     .IO => return error.InputOutput,
   8228                     .LOOP => return error.SymLinkLoop,
   8229                     .NOENT => return error.FileNotFound,
   8230                     .NOMEM => return error.SystemResources,
   8231                     .NOTDIR => return error.FileNotFound,
   8232                     .OPNOTSUPP => return error.OperationUnsupported,
   8233                     .PERM => return error.PermissionDenied,
   8234                     .ROFS => return error.ReadOnlyFileSystem,
   8235                     .NOSYS => {
   8236                         @atomicStore(UseFchmodat2, &t.use_fchmodat2, .disabled, .monotonic);
   8237                         return fchmodatFallback(dir_fd, path, mode);
   8238                     },
   8239                     else => |err| return posix.unexpectedErrno(err),
   8240                 }
   8241             },
   8242         }
   8243     }
   8244 }
   8245 
   8246 fn fchmodatFallback(
   8247     dir_fd: posix.fd_t,
   8248     path: [*:0]const u8,
   8249     mode: posix.mode_t,
   8250 ) Dir.SetFilePermissionsError!void {
   8251     comptime assert(native_os == .linux);
   8252 
   8253     // Fallback to changing permissions using procfs:
   8254     //
   8255     // 1. Open `path` as a `PATH` descriptor.
   8256     // 2. Stat the fd and check if it isn't a symbolic link.
   8257     // 3. Generate the procfs reference to the fd via `/proc/self/fd/{fd}`.
   8258     // 4. Pass the procfs path to `chmod` with the `mode`.
   8259     const path_fd: posix.fd_t = fd: {
   8260         const syscall: Syscall = try .start();
   8261         while (true) {
   8262             const rc = posix.system.openat(dir_fd, path, .{
   8263                 .PATH = true,
   8264                 .NOFOLLOW = true,
   8265                 .CLOEXEC = true,
   8266             }, @as(posix.mode_t, 0));
   8267             switch (posix.errno(rc)) {
   8268                 .SUCCESS => {
   8269                     syscall.finish();
   8270                     break :fd @intCast(rc);
   8271                 },
   8272                 .INTR => {
   8273                     try syscall.checkCancel();
   8274                     continue;
   8275                 },
   8276                 else => |e| {
   8277                     syscall.finish();
   8278                     switch (e) {
   8279                         .FAULT => |err| return errnoBug(err),
   8280                         .INVAL => |err| return errnoBug(err),
   8281                         .ACCES => return error.AccessDenied,
   8282                         .PERM => return error.PermissionDenied,
   8283                         .LOOP => return error.SymLinkLoop,
   8284                         .MFILE => return error.ProcessFdQuotaExceeded,
   8285                         .NAMETOOLONG => return error.NameTooLong,
   8286                         .NFILE => return error.SystemFdQuotaExceeded,
   8287                         .NOENT => return error.FileNotFound,
   8288                         .NOMEM => return error.SystemResources,
   8289                         else => |err| return posix.unexpectedErrno(err),
   8290                     }
   8291                 },
   8292             }
   8293         }
   8294     };
   8295     defer closeFd(path_fd);
   8296 
   8297     const path_mode = mode: {
   8298         const sys = if (statx_use_c) std.c else std.os.linux;
   8299         const syscall: Syscall = try .start();
   8300         while (true) {
   8301             var statx = std.mem.zeroes(std.os.linux.Statx);
   8302             switch (sys.errno(sys.statx(path_fd, "", posix.AT.EMPTY_PATH, .{ .TYPE = true }, &statx))) {
   8303                 .SUCCESS => {
   8304                     syscall.finish();
   8305                     if (!statx.mask.TYPE) return error.Unexpected;
   8306                     break :mode statx.mode;
   8307                 },
   8308                 .INTR => {
   8309                     try syscall.checkCancel();
   8310                     continue;
   8311                 },
   8312                 else => |e| {
   8313                     syscall.finish();
   8314                     switch (e) {
   8315                         .ACCES => return error.AccessDenied,
   8316                         .LOOP => return error.SymLinkLoop,
   8317                         .NOMEM => return error.SystemResources,
   8318                         else => |err| return posix.unexpectedErrno(err),
   8319                     }
   8320                 },
   8321             }
   8322         }
   8323     };
   8324 
   8325     // Even though we only wanted TYPE, the kernel can still fill in the additional bits.
   8326     if ((path_mode & posix.S.IFMT) == posix.S.IFLNK)
   8327         return error.OperationUnsupported;
   8328 
   8329     var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
   8330     const proc_path = std.fmt.bufPrintSentinel(&procfs_buf, "/proc/self/fd/{d}", .{path_fd}, 0) catch unreachable;
   8331     const syscall: Syscall = try .start();
   8332     while (true) {
   8333         switch (posix.errno(posix.system.chmod(proc_path, mode))) {
   8334             .SUCCESS => return syscall.finish(),
   8335             .INTR => {
   8336                 try syscall.checkCancel();
   8337                 continue;
   8338             },
   8339             else => |e| {
   8340                 syscall.finish();
   8341                 switch (e) {
   8342                     .NOENT => return error.OperationUnsupported, // procfs not mounted.
   8343                     .BADF => |err| return errnoBug(err),
   8344                     .FAULT => |err| return errnoBug(err),
   8345                     .INVAL => |err| return errnoBug(err),
   8346                     .ACCES => return error.AccessDenied,
   8347                     .IO => return error.InputOutput,
   8348                     .LOOP => return error.SymLinkLoop,
   8349                     .NOMEM => return error.SystemResources,
   8350                     .NOTDIR => return error.FileNotFound,
   8351                     .PERM => return error.PermissionDenied,
   8352                     .ROFS => return error.ReadOnlyFileSystem,
   8353                     else => |err| return posix.unexpectedErrno(err),
   8354                 }
   8355             },
   8356         }
   8357     }
   8358 }
   8359 
   8360 const dirSetOwner = switch (native_os) {
   8361     .windows => dirSetOwnerUnsupported,
   8362     else => dirSetOwnerPosix,
   8363 };
   8364 
   8365 fn dirSetOwnerUnsupported(userdata: ?*anyopaque, dir: Dir, owner: ?File.Uid, group: ?File.Gid) Dir.SetOwnerError!void {
   8366     _ = userdata;
   8367     _ = dir;
   8368     _ = owner;
   8369     _ = group;
   8370     return error.Unexpected;
   8371 }
   8372 
   8373 fn dirSetOwnerPosix(userdata: ?*anyopaque, dir: Dir, owner: ?File.Uid, group: ?File.Gid) Dir.SetOwnerError!void {
   8374     if (!have_fchown) return error.Unexpected; // Unsupported OS, don't call this function.
   8375     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8376     _ = t;
   8377     const uid = owner orelse ~@as(posix.uid_t, 0);
   8378     const gid = group orelse ~@as(posix.gid_t, 0);
   8379     return posixFchown(dir.handle, uid, gid);
   8380 }
   8381 
   8382 fn posixFchown(fd: posix.fd_t, uid: posix.uid_t, gid: posix.gid_t) File.SetOwnerError!void {
   8383     comptime assert(have_fchown);
   8384     const syscall: Syscall = try .start();
   8385     while (true) {
   8386         switch (posix.errno(posix.system.fchown(fd, uid, gid))) {
   8387             .SUCCESS => return syscall.finish(),
   8388             .INTR => {
   8389                 try syscall.checkCancel();
   8390                 continue;
   8391             },
   8392             else => |e| {
   8393                 syscall.finish();
   8394                 switch (e) {
   8395                     .BADF => |err| return errnoBug(err), // likely fd refers to directory opened without `Dir.OpenOptions.iterate`
   8396                     .FAULT => |err| return errnoBug(err),
   8397                     .INVAL => |err| return errnoBug(err),
   8398                     .ACCES => return error.AccessDenied,
   8399                     .IO => return error.InputOutput,
   8400                     .LOOP => return error.SymLinkLoop,
   8401                     .NOENT => return error.FileNotFound,
   8402                     .NOMEM => return error.SystemResources,
   8403                     .NOTDIR => return error.FileNotFound,
   8404                     .PERM => return error.PermissionDenied,
   8405                     .ROFS => return error.ReadOnlyFileSystem,
   8406                     else => |err| return posix.unexpectedErrno(err),
   8407                 }
   8408             },
   8409         }
   8410     }
   8411 }
   8412 
   8413 fn dirSetFileOwner(
   8414     userdata: ?*anyopaque,
   8415     dir: Dir,
   8416     sub_path: []const u8,
   8417     owner: ?File.Uid,
   8418     group: ?File.Gid,
   8419     options: Dir.SetFileOwnerOptions,
   8420 ) Dir.SetFileOwnerError!void {
   8421     if (!have_fchown) return error.Unexpected; // Unsupported OS, don't call this function.
   8422     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8423     _ = t;
   8424 
   8425     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   8426     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   8427 
   8428     _ = dir;
   8429     _ = sub_path_posix;
   8430     _ = owner;
   8431     _ = group;
   8432     _ = options;
   8433     @panic("TODO implement dirSetFileOwner");
   8434 }
   8435 
   8436 const fileSync = switch (native_os) {
   8437     .windows => fileSyncWindows,
   8438     .wasi => fileSyncWasi,
   8439     else => fileSyncPosix,
   8440 };
   8441 
   8442 fn fileSyncWindows(userdata: ?*anyopaque, file: File) File.SyncError!void {
   8443     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8444     _ = t;
   8445 
   8446     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   8447     const syscall: Syscall = try .start();
   8448     while (true) {
   8449         switch (windows.ntdll.NtFlushBuffersFile(file.handle, &io_status_block)) {
   8450             .SUCCESS => break syscall.finish(),
   8451             .CANCELLED => {
   8452                 try syscall.checkCancel();
   8453                 continue;
   8454             },
   8455             .INVALID_HANDLE => unreachable,
   8456             .ACCESS_DENIED => return syscall.fail(error.AccessDenied), // a sync was performed but the system couldn't update the access time
   8457             .UNEXPECTED_NETWORK_ERROR => return syscall.fail(error.InputOutput),
   8458             else => |status| return syscall.unexpectedNtstatus(status),
   8459         }
   8460     }
   8461 }
   8462 
   8463 fn fileSyncPosix(userdata: ?*anyopaque, file: File) File.SyncError!void {
   8464     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8465     _ = t;
   8466     const syscall: Syscall = try .start();
   8467     while (true) {
   8468         switch (posix.errno(posix.system.fsync(file.handle))) {
   8469             .SUCCESS => return syscall.finish(),
   8470             .INTR => {
   8471                 try syscall.checkCancel();
   8472                 continue;
   8473             },
   8474             else => |e| {
   8475                 syscall.finish();
   8476                 switch (e) {
   8477                     .BADF => |err| return errnoBug(err),
   8478                     .INVAL => |err| return errnoBug(err),
   8479                     .ROFS => |err| return errnoBug(err),
   8480                     .IO => return error.InputOutput,
   8481                     .NOSPC => return error.NoSpaceLeft,
   8482                     .DQUOT => return error.DiskQuota,
   8483                     else => |err| return posix.unexpectedErrno(err),
   8484                 }
   8485             },
   8486         }
   8487     }
   8488 }
   8489 
   8490 fn fileSyncWasi(userdata: ?*anyopaque, file: File) File.SyncError!void {
   8491     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8492     _ = t;
   8493     const syscall: Syscall = try .start();
   8494     while (true) {
   8495         switch (std.os.wasi.fd_sync(file.handle)) {
   8496             .SUCCESS => return syscall.finish(),
   8497             .INTR => {
   8498                 try syscall.checkCancel();
   8499                 continue;
   8500             },
   8501             else => |e| {
   8502                 syscall.finish();
   8503                 switch (e) {
   8504                     .BADF => |err| return errnoBug(err),
   8505                     .INVAL => |err| return errnoBug(err),
   8506                     .ROFS => |err| return errnoBug(err),
   8507                     .IO => return error.InputOutput,
   8508                     .NOSPC => return error.NoSpaceLeft,
   8509                     .DQUOT => return error.DiskQuota,
   8510                     else => |err| return posix.unexpectedErrno(err),
   8511                 }
   8512             },
   8513         }
   8514     }
   8515 }
   8516 
   8517 fn fileIsTty(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
   8518     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8519     _ = t;
   8520     return isTty(file);
   8521 }
   8522 
   8523 fn isTty(file: File) Io.Cancelable!bool {
   8524     if (is_windows) {
   8525         var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
   8526         switch ((try deviceIoControl(&.{
   8527             .file = .{
   8528                 .handle = windows.peb().ProcessParameters.ConsoleHandle,
   8529                 .flags = .{ .nonblocking = false },
   8530             },
   8531             .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
   8532             .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
   8533         })).u.Status) {
   8534             .SUCCESS => return true,
   8535             .INVALID_HANDLE => return isCygwinPty(file),
   8536             else => return false,
   8537         }
   8538     }
   8539 
   8540     if (builtin.link_libc) {
   8541         const syscall: Syscall = try .start();
   8542         while (true) {
   8543             const rc = posix.system.isatty(file.handle);
   8544             switch (posix.errno(rc - 1)) {
   8545                 .SUCCESS => {
   8546                     syscall.finish();
   8547                     return true;
   8548                 },
   8549                 .INTR => {
   8550                     try syscall.checkCancel();
   8551                     continue;
   8552                 },
   8553                 else => {
   8554                     syscall.finish();
   8555                     return false;
   8556                 },
   8557             }
   8558         }
   8559     }
   8560 
   8561     if (native_os == .wasi) {
   8562         var statbuf: std.os.wasi.fdstat_t = undefined;
   8563         const err = std.os.wasi.fd_fdstat_get(file.handle, &statbuf);
   8564         if (err != .SUCCESS)
   8565             return false;
   8566 
   8567         // A tty is a character device that we can't seek or tell on.
   8568         if (statbuf.fs_filetype != .CHARACTER_DEVICE)
   8569             return false;
   8570         if (statbuf.fs_rights_base.FD_SEEK or statbuf.fs_rights_base.FD_TELL)
   8571             return false;
   8572 
   8573         return true;
   8574     }
   8575 
   8576     if (native_os == .linux) {
   8577         const linux = std.os.linux;
   8578         const syscall: Syscall = try .start();
   8579         while (true) {
   8580             var wsz: posix.winsize = undefined;
   8581             const fd: usize = @bitCast(@as(isize, file.handle));
   8582             const rc = linux.syscall3(.ioctl, fd, linux.T.IOCGWINSZ, @intFromPtr(&wsz));
   8583             switch (linux.errno(rc)) {
   8584                 .SUCCESS => {
   8585                     syscall.finish();
   8586                     return true;
   8587                 },
   8588                 .INTR => {
   8589                     try syscall.checkCancel();
   8590                     continue;
   8591                 },
   8592                 else => {
   8593                     syscall.finish();
   8594                     return false;
   8595                 },
   8596             }
   8597         }
   8598     }
   8599 
   8600     @compileError("unimplemented");
   8601 }
   8602 
   8603 fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
   8604     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8605     _ = t;
   8606 
   8607     if (!is_windows) return if (!try supportsAnsiEscapeCodes(file)) error.NotTerminalDevice;
   8608 
   8609     // For Windows Terminal, VT Sequences processing is enabled by default.
   8610     const console: File = .{
   8611         .handle = windows.peb().ProcessParameters.ConsoleHandle,
   8612         .flags = .{ .nonblocking = false },
   8613     };
   8614     var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
   8615     switch ((try deviceIoControl(&.{
   8616         .file = console,
   8617         .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
   8618         .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
   8619     })).u.Status) {
   8620         .SUCCESS => {},
   8621         .INVALID_HANDLE => return if (!try isCygwinPty(file)) error.NotTerminalDevice,
   8622         else => return error.NotTerminalDevice,
   8623     }
   8624 
   8625     if (get_console_mode.Data & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0) return;
   8626 
   8627     // For Windows Console, VT Sequences processing support was added in Windows 10 build 14361, but disabled by default.
   8628     // https://devblogs.microsoft.com/commandline/tmux-support-arrives-for-bash-on-ubuntu-on-windows/
   8629     //
   8630     // Note: In Microsoft's example for enabling virtual terminal processing, it
   8631     // shows attempting to enable `DISABLE_NEWLINE_AUTO_RETURN` as well:
   8632     // https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#example-of-enabling-virtual-terminal-processing
   8633     // This is avoided because in the old Windows Console, that flag causes \n (as opposed to \r\n)
   8634     // to behave unexpectedly (the cursor moves down 1 row but remains on the same column).
   8635     // Additionally, the default console mode in Windows Terminal does not have
   8636     // `DISABLE_NEWLINE_AUTO_RETURN` set, so by only enabling `ENABLE_VIRTUAL_TERMINAL_PROCESSING`
   8637     // we end up matching the mode of Windows Terminal.
   8638     var set_console_mode = windows.CONSOLE.USER_IO.SET_MODE(
   8639         get_console_mode.Data | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING,
   8640     );
   8641     switch ((try deviceIoControl(&.{
   8642         .file = console,
   8643         .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
   8644         .in = @ptrCast(&set_console_mode.request(file, 0, .{}, 0, .{})),
   8645     })).u.Status) {
   8646         .SUCCESS => {},
   8647         else => |status| return windows.unexpectedStatus(status),
   8648     }
   8649 }
   8650 
   8651 fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Io.Cancelable!bool {
   8652     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8653     _ = t;
   8654     return supportsAnsiEscapeCodes(file);
   8655 }
   8656 
   8657 fn supportsAnsiEscapeCodes(file: File) Io.Cancelable!bool {
   8658     if (is_windows) {
   8659         var get_console_mode = windows.CONSOLE.USER_IO.GET_MODE;
   8660         switch ((try deviceIoControl(&.{
   8661             .file = .{
   8662                 .handle = windows.peb().ProcessParameters.ConsoleHandle,
   8663                 .flags = .{ .nonblocking = false },
   8664             },
   8665             .code = windows.IOCTL.CONDRV.ISSUE_USER_IO,
   8666             .in = @ptrCast(&get_console_mode.request(file, 0, .{}, 0, .{})),
   8667         })).u.Status) {
   8668             .SUCCESS => if (get_console_mode.Data & windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0)
   8669                 return true,
   8670             .INVALID_HANDLE => return isCygwinPty(file),
   8671             else => return false,
   8672         }
   8673     }
   8674 
   8675     if (native_os == .wasi) {
   8676         // WASI sanitizes stdout when fd is a tty so ANSI escape codes will not
   8677         // be interpreted as actual cursor commands, and stderr is always
   8678         // sanitized.
   8679         return false;
   8680     }
   8681 
   8682     if (try isTty(file)) return true;
   8683 
   8684     return false;
   8685 }
   8686 
   8687 fn isCygwinPty(file: File) Io.Cancelable!bool {
   8688     if (!is_windows) return false;
   8689 
   8690     const handle = file.handle;
   8691 
   8692     // If this is a MSYS2/cygwin pty, then it will be a named pipe with a name in one of these formats:
   8693     //   msys-[...]-ptyN-[...]
   8694     //   cygwin-[...]-ptyN-[...]
   8695     //
   8696     // Example: msys-1888ae32e00d56aa-pty0-to-master
   8697 
   8698     // First, just check that the handle is a named pipe.
   8699     // This allows us to avoid the more costly NtQueryInformationFile call
   8700     // for handles that aren't named pipes.
   8701     {
   8702         var io_status: windows.IO_STATUS_BLOCK = undefined;
   8703         var device_info: windows.FILE.FS_DEVICE_INFORMATION = undefined;
   8704         const syscall: Syscall = try .start();
   8705         while (true) switch (windows.ntdll.NtQueryVolumeInformationFile(
   8706             handle,
   8707             &io_status,
   8708             &device_info,
   8709             @sizeOf(windows.FILE.FS_DEVICE_INFORMATION),
   8710             .Device,
   8711         )) {
   8712             .SUCCESS => break syscall.finish(),
   8713             .CANCELLED => {
   8714                 try syscall.checkCancel();
   8715                 continue;
   8716             },
   8717             else => {
   8718                 syscall.finish();
   8719                 return false;
   8720             },
   8721         };
   8722         if (device_info.DeviceType.FileDevice != .NAMED_PIPE) return false;
   8723     }
   8724 
   8725     const name_bytes_offset = @offsetOf(windows.FILE.NAME_INFORMATION, "FileName");
   8726     // `NAME_MAX` UTF-16 code units (2 bytes each)
   8727     // This buffer may not be long enough to handle *all* possible paths
   8728     // (PATH_MAX_WIDE would be necessary for that), but because we only care
   8729     // about certain paths and we know they must be within a reasonable length,
   8730     // we can use this smaller buffer and just return false on any error from
   8731     // NtQueryInformationFile.
   8732     const num_name_bytes = windows.MAX_PATH * 2;
   8733     var name_info_bytes align(@alignOf(windows.FILE.NAME_INFORMATION)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);
   8734 
   8735     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   8736     const syscall: Syscall = try .start();
   8737     while (true) switch (windows.ntdll.NtQueryInformationFile(
   8738         handle,
   8739         &io_status_block,
   8740         &name_info_bytes,
   8741         @intCast(name_info_bytes.len),
   8742         .Name,
   8743     )) {
   8744         .SUCCESS => break syscall.finish(),
   8745         .CANCELLED => {
   8746             try syscall.checkCancel();
   8747             continue;
   8748         },
   8749         .INVALID_PARAMETER => unreachable,
   8750         else => {
   8751             syscall.finish();
   8752             return false;
   8753         },
   8754     };
   8755 
   8756     const name_info: *const windows.FILE.NAME_INFORMATION = @ptrCast(&name_info_bytes);
   8757     const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + name_info.FileNameLength];
   8758     const name_wide = std.mem.bytesAsSlice(u16, name_bytes);
   8759     // The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master
   8760     return (std.mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'm', 's', 'y', 's', '-' }) or
   8761         std.mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'c', 'y', 'g', 'w', 'i', 'n', '-' })) and
   8762         std.mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null;
   8763 }
   8764 
   8765 fn fileSetLength(userdata: ?*anyopaque, file: File, length: u64) File.SetLengthError!void {
   8766     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8767     _ = t;
   8768 
   8769     const signed_len: i64 = @bitCast(length);
   8770     if (signed_len < 0) return error.FileTooBig; // Avoid ambiguous EINVAL errors.
   8771 
   8772     if (is_windows) {
   8773         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   8774         var eof_info: windows.FILE.END_OF_FILE_INFORMATION = .{
   8775             .EndOfFile = signed_len,
   8776         };
   8777 
   8778         const syscall: Syscall = try .start();
   8779         while (true) switch (windows.ntdll.NtSetInformationFile(
   8780             file.handle,
   8781             &io_status_block,
   8782             &eof_info,
   8783             @sizeOf(windows.FILE.END_OF_FILE_INFORMATION),
   8784             .EndOfFile,
   8785         )) {
   8786             .SUCCESS => return syscall.finish(),
   8787             .CANCELLED => {
   8788                 try syscall.checkCancel();
   8789                 continue;
   8790             },
   8791             .INVALID_HANDLE => |err| return syscall.ntstatusBug(err), // Handle not open for writing.
   8792             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   8793             .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
   8794             .INVALID_PARAMETER => return syscall.fail(error.FileTooBig),
   8795             else => |status| return syscall.unexpectedNtstatus(status),
   8796         };
   8797     }
   8798 
   8799     if (native_os == .wasi and !builtin.link_libc) {
   8800         const syscall: Syscall = try .start();
   8801         while (true) {
   8802             switch (std.os.wasi.fd_filestat_set_size(file.handle, length)) {
   8803                 .SUCCESS => return syscall.finish(),
   8804                 .INTR => {
   8805                     try syscall.checkCancel();
   8806                     continue;
   8807                 },
   8808                 else => |e| {
   8809                     syscall.finish();
   8810                     switch (e) {
   8811                         .FBIG => return error.FileTooBig,
   8812                         .IO => return error.InputOutput,
   8813                         .PERM => return error.PermissionDenied,
   8814                         .TXTBSY => return error.FileBusy,
   8815                         .BADF => |err| return errnoBug(err), // Handle not open for writing
   8816                         .INVAL => return error.NonResizable,
   8817                         .NOTCAPABLE => return error.AccessDenied,
   8818                         else => |err| return posix.unexpectedErrno(err),
   8819                     }
   8820                 },
   8821             }
   8822         }
   8823     }
   8824 
   8825     const syscall: Syscall = try .start();
   8826     while (true) {
   8827         switch (posix.errno(ftruncate_sym(file.handle, signed_len))) {
   8828             .SUCCESS => return syscall.finish(),
   8829             .INTR => {
   8830                 try syscall.checkCancel();
   8831                 continue;
   8832             },
   8833             else => |e| {
   8834                 syscall.finish();
   8835                 switch (e) {
   8836                     .FBIG => return error.FileTooBig,
   8837                     .IO => return error.InputOutput,
   8838                     .PERM => return error.PermissionDenied,
   8839                     .TXTBSY => return error.FileBusy,
   8840                     .BADF => |err| return errnoBug(err), // Handle not open for writing.
   8841                     .INVAL => return error.NonResizable, // This is returned for /dev/null for example.
   8842                     else => |err| return posix.unexpectedErrno(err),
   8843                 }
   8844             },
   8845         }
   8846     }
   8847 }
   8848 
   8849 fn fileSetOwner(userdata: ?*anyopaque, file: File, owner: ?File.Uid, group: ?File.Gid) File.SetOwnerError!void {
   8850     if (!have_fchown) return error.Unexpected; // Unsupported OS, don't call this function.
   8851     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8852     _ = t;
   8853     const uid = owner orelse ~@as(posix.uid_t, 0);
   8854     const gid = group orelse ~@as(posix.gid_t, 0);
   8855     return posixFchown(file.handle, uid, gid);
   8856 }
   8857 
   8858 fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permissions) File.SetPermissionsError!void {
   8859     if (@sizeOf(File.Permissions) == 0) return;
   8860     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8861     _ = t;
   8862     switch (native_os) {
   8863         .windows => {
   8864             var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   8865             var info: windows.FILE.BASIC_INFORMATION = .{
   8866                 .CreationTime = 0,
   8867                 .LastAccessTime = 0,
   8868                 .LastWriteTime = 0,
   8869                 .ChangeTime = 0,
   8870                 .FileAttributes = permissions.toAttributes(),
   8871             };
   8872             const syscall: Syscall = try .start();
   8873             while (true) switch (windows.ntdll.NtSetInformationFile(
   8874                 file.handle,
   8875                 &io_status_block,
   8876                 &info,
   8877                 @sizeOf(windows.FILE.BASIC_INFORMATION),
   8878                 .Basic,
   8879             )) {
   8880                 .SUCCESS => return syscall.finish(),
   8881                 .CANCELLED => {
   8882                     try syscall.checkCancel();
   8883                     continue;
   8884                 },
   8885                 .INVALID_HANDLE => |err| return syscall.ntstatusBug(err),
   8886                 .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   8887                 else => |status| return syscall.unexpectedNtstatus(status),
   8888             };
   8889         },
   8890         .wasi => return error.Unexpected, // Unsupported OS.
   8891         else => return setPermissionsPosix(file.handle, permissions.toMode()),
   8892     }
   8893 }
   8894 
   8895 fn setPermissionsPosix(fd: posix.fd_t, mode: posix.mode_t) File.SetPermissionsError!void {
   8896     comptime assert(have_fchmod);
   8897     const syscall: Syscall = try .start();
   8898     while (true) {
   8899         switch (posix.errno(posix.system.fchmod(fd, mode))) {
   8900             .SUCCESS => return syscall.finish(),
   8901             .INTR => {
   8902                 try syscall.checkCancel();
   8903                 continue;
   8904             },
   8905             else => |e| {
   8906                 syscall.finish();
   8907                 switch (e) {
   8908                     .BADF => |err| return errnoBug(err),
   8909                     .FAULT => |err| return errnoBug(err),
   8910                     .INVAL => |err| return errnoBug(err),
   8911                     .ACCES => return error.AccessDenied,
   8912                     .IO => return error.InputOutput,
   8913                     .LOOP => return error.SymLinkLoop,
   8914                     .NOENT => return error.FileNotFound,
   8915                     .NOMEM => return error.SystemResources,
   8916                     .NOTDIR => return error.FileNotFound,
   8917                     .PERM => return error.PermissionDenied,
   8918                     .ROFS => return error.ReadOnlyFileSystem,
   8919                     else => |err| return posix.unexpectedErrno(err),
   8920                 }
   8921             },
   8922         }
   8923     }
   8924 }
   8925 
   8926 fn dirSetTimestamps(
   8927     userdata: ?*anyopaque,
   8928     dir: Dir,
   8929     sub_path: []const u8,
   8930     options: Dir.SetTimestampsOptions,
   8931 ) Dir.SetTimestampsError!void {
   8932     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8933     _ = t;
   8934 
   8935     if (is_windows) {
   8936         @panic("TODO implement dirSetTimestamps windows");
   8937     }
   8938 
   8939     if (native_os == .wasi and !builtin.link_libc) {
   8940         @panic("TODO implement dirSetTimestamps wasi");
   8941     }
   8942 
   8943     var times_buffer: [2]posix.timespec = undefined;
   8944     const times = if (options.modify_timestamp == .now and options.access_timestamp == .now) null else p: {
   8945         times_buffer = .{
   8946             setTimestampToPosix(options.access_timestamp),
   8947             setTimestampToPosix(options.modify_timestamp),
   8948         };
   8949         break :p &times_buffer;
   8950     };
   8951 
   8952     const flags: u32 = if (!options.follow_symlinks) posix.AT.SYMLINK_NOFOLLOW else 0;
   8953 
   8954     var path_buffer: [posix.PATH_MAX]u8 = undefined;
   8955     const sub_path_posix = try pathToPosix(sub_path, &path_buffer);
   8956 
   8957     const syscall: Syscall = try .start();
   8958     while (true) switch (posix.errno(posix.system.utimensat(dir.handle, sub_path_posix, times, flags))) {
   8959         .SUCCESS => return syscall.finish(),
   8960         .INTR => {
   8961             try syscall.checkCancel();
   8962             continue;
   8963         },
   8964         .BADF => |err| return syscall.errnoBug(err), // always a race condition
   8965         .FAULT => |err| return syscall.errnoBug(err),
   8966         .INVAL => |err| return syscall.errnoBug(err),
   8967         .ACCES => return syscall.fail(error.AccessDenied),
   8968         .PERM => return syscall.fail(error.PermissionDenied),
   8969         .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
   8970         else => |err| return syscall.unexpectedErrno(err),
   8971     };
   8972 }
   8973 
   8974 fn fileSetTimestamps(
   8975     userdata: ?*anyopaque,
   8976     file: File,
   8977     options: File.SetTimestampsOptions,
   8978 ) File.SetTimestampsError!void {
   8979     const t: *Threaded = @ptrCast(@alignCast(userdata));
   8980     _ = t;
   8981 
   8982     if (is_windows) {
   8983         const now_sys = if (options.access_timestamp == .now or options.modify_timestamp == .now)
   8984             windows.ntdll.RtlGetSystemTimePrecise()
   8985         else
   8986             undefined;
   8987         var iosb: windows.IO_STATUS_BLOCK = undefined;
   8988         var info: windows.FILE.BASIC_INFORMATION = .{
   8989             .CreationTime = 0,
   8990             .LastAccessTime = switch (options.access_timestamp) {
   8991                 .unchanged => 0,
   8992                 .now => now_sys,
   8993                 .new => |ts| windows.toSysTime(ts),
   8994             },
   8995             .LastWriteTime = switch (options.modify_timestamp) {
   8996                 .unchanged => 0,
   8997                 .now => now_sys,
   8998                 .new => |ts| windows.toSysTime(ts),
   8999             },
   9000             .ChangeTime = 0,
   9001             .FileAttributes = .{},
   9002         };
   9003         var syscall: Syscall = try .start();
   9004         while (true) switch (windows.ntdll.NtSetInformationFile(
   9005             file.handle,
   9006             &iosb,
   9007             &info,
   9008             @sizeOf(windows.FILE.BASIC_INFORMATION),
   9009             .Basic,
   9010         )) {
   9011             .SUCCESS => return syscall.finish(),
   9012             .CANCELLED => try syscall.checkCancel(),
   9013             else => |status| return syscall.unexpectedNtstatus(status),
   9014         };
   9015     }
   9016 
   9017     if (native_os == .wasi and !builtin.link_libc) {
   9018         var atime: std.os.wasi.timestamp_t = 0;
   9019         var mtime: std.os.wasi.timestamp_t = 0;
   9020         var flags: std.os.wasi.fstflags_t = .{};
   9021 
   9022         switch (options.access_timestamp) {
   9023             .unchanged => {},
   9024             .now => flags.ATIM_NOW = true,
   9025             .new => |ts| {
   9026                 atime = timestampToPosix(ts.nanoseconds).toTimestamp();
   9027                 flags.ATIM = true;
   9028             },
   9029         }
   9030 
   9031         switch (options.modify_timestamp) {
   9032             .unchanged => {},
   9033             .now => flags.MTIM_NOW = true,
   9034             .new => |ts| {
   9035                 mtime = timestampToPosix(ts.nanoseconds).toTimestamp();
   9036                 flags.MTIM = true;
   9037             },
   9038         }
   9039 
   9040         const syscall: Syscall = try .start();
   9041         while (true) switch (std.os.wasi.fd_filestat_set_times(file.handle, atime, mtime, flags)) {
   9042             .SUCCESS => return syscall.finish(),
   9043             .INTR => {
   9044                 try syscall.checkCancel();
   9045                 continue;
   9046             },
   9047             .BADF => |err| return syscall.errnoBug(err), // File descriptor use-after-free.
   9048             .FAULT => |err| return syscall.errnoBug(err),
   9049             .INVAL => |err| return syscall.errnoBug(err),
   9050             .ACCES => return syscall.fail(error.AccessDenied),
   9051             .PERM => return syscall.fail(error.PermissionDenied),
   9052             .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
   9053             else => |err| return syscall.unexpectedErrno(err),
   9054         };
   9055     }
   9056 
   9057     var times_buffer: [2]posix.timespec = undefined;
   9058     const times = if (options.modify_timestamp == .now and options.access_timestamp == .now) null else p: {
   9059         times_buffer = .{
   9060             setTimestampToPosix(options.access_timestamp),
   9061             setTimestampToPosix(options.modify_timestamp),
   9062         };
   9063         break :p &times_buffer;
   9064     };
   9065 
   9066     const syscall: Syscall = try .start();
   9067     while (true) switch (posix.errno(posix.system.futimens(file.handle, times))) {
   9068         .SUCCESS => return syscall.finish(),
   9069         .INTR => {
   9070             try syscall.checkCancel();
   9071             continue;
   9072         },
   9073         .BADF => |err| return syscall.errnoBug(err), // always a race condition
   9074         .FAULT => |err| return syscall.errnoBug(err),
   9075         .INVAL => |err| return syscall.errnoBug(err),
   9076         .ACCES => return syscall.fail(error.AccessDenied),
   9077         .PERM => return syscall.fail(error.PermissionDenied),
   9078         .ROFS => return syscall.fail(error.ReadOnlyFileSystem),
   9079         else => |err| return syscall.unexpectedErrno(err),
   9080     };
   9081 }
   9082 
   9083 const windows_lock_range_off: windows.LARGE_INTEGER = 0;
   9084 const windows_lock_range_len: windows.LARGE_INTEGER = 1;
   9085 
   9086 fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!void {
   9087     if (native_os == .wasi) return error.FileLocksUnsupported;
   9088     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9089     _ = t;
   9090 
   9091     if (is_windows) {
   9092         const exclusive = switch (lock) {
   9093             .none => {
   9094                 // To match the non-Windows behavior, unlock
   9095                 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   9096                 while (true) switch (windows.ntdll.NtUnlockFile(
   9097                     file.handle,
   9098                     &io_status_block,
   9099                     &windows_lock_range_off,
   9100                     &windows_lock_range_len,
   9101                     0,
   9102                 )) {
   9103                     .SUCCESS => return,
   9104                     .CANCELLED => continue,
   9105                     .RANGE_NOT_LOCKED => return,
   9106                     .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
   9107                     else => |status| return windows.unexpectedStatus(status),
   9108                 };
   9109             },
   9110             .shared => false,
   9111             .exclusive => true,
   9112         };
   9113         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   9114         const syscall: Syscall = try .start();
   9115         while (true) switch (windows.ntdll.NtLockFile(
   9116             file.handle,
   9117             null,
   9118             null,
   9119             null,
   9120             &io_status_block,
   9121             &windows_lock_range_off,
   9122             &windows_lock_range_len,
   9123             null,
   9124             windows.FALSE,
   9125             @intFromBool(exclusive),
   9126         )) {
   9127             .SUCCESS => return syscall.finish(),
   9128             .CANCELLED => {
   9129                 try syscall.checkCancel();
   9130                 continue;
   9131             },
   9132             .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
   9133             .LOCK_NOT_GRANTED => |err| return syscall.ntstatusBug(err), // passed FailImmediately=false
   9134             .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
   9135             else => |status| return syscall.unexpectedNtstatus(status),
   9136         };
   9137     }
   9138 
   9139     const operation: i32 = switch (lock) {
   9140         .none => posix.LOCK.UN,
   9141         .shared => posix.LOCK.SH,
   9142         .exclusive => posix.LOCK.EX,
   9143     };
   9144     const syscall: Syscall = try .start();
   9145     while (true) {
   9146         switch (posix.errno(posix.system.flock(file.handle, operation))) {
   9147             .SUCCESS => return syscall.finish(),
   9148             .INTR => {
   9149                 try syscall.checkCancel();
   9150                 continue;
   9151             },
   9152             else => |e| {
   9153                 syscall.finish();
   9154                 switch (e) {
   9155                     .BADF => |err| return errnoBug(err),
   9156                     .INVAL => |err| return errnoBug(err), // invalid parameters
   9157                     .NOLCK => return error.SystemResources,
   9158                     .AGAIN => |err| return errnoBug(err),
   9159                     .OPNOTSUPP => return error.FileLocksUnsupported,
   9160                     else => |err| return posix.unexpectedErrno(err),
   9161                 }
   9162             },
   9163         }
   9164     }
   9165 }
   9166 
   9167 fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!bool {
   9168     if (native_os == .wasi) return error.FileLocksUnsupported;
   9169     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9170     _ = t;
   9171 
   9172     if (is_windows) {
   9173         const exclusive = switch (lock) {
   9174             .none => {
   9175                 // To match the non-Windows behavior, unlock
   9176                 var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   9177                 while (true) switch (windows.ntdll.NtUnlockFile(
   9178                     file.handle,
   9179                     &io_status_block,
   9180                     &windows_lock_range_off,
   9181                     &windows_lock_range_len,
   9182                     0,
   9183                 )) {
   9184                     .SUCCESS => return true,
   9185                     .CANCELLED => continue,
   9186                     .RANGE_NOT_LOCKED => return false,
   9187                     .ACCESS_VIOLATION => |err| return windows.statusBug(err), // bad io_status_block pointer
   9188                     else => |status| return windows.unexpectedStatus(status),
   9189                 };
   9190             },
   9191             .shared => false,
   9192             .exclusive => true,
   9193         };
   9194         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   9195         const syscall: Syscall = try .start();
   9196         while (true) switch (windows.ntdll.NtLockFile(
   9197             file.handle,
   9198             null,
   9199             null,
   9200             null,
   9201             &io_status_block,
   9202             &windows_lock_range_off,
   9203             &windows_lock_range_len,
   9204             null,
   9205             windows.TRUE,
   9206             @intFromBool(exclusive),
   9207         )) {
   9208             .SUCCESS => {
   9209                 syscall.finish();
   9210                 return true;
   9211             },
   9212             .LOCK_NOT_GRANTED => {
   9213                 syscall.finish();
   9214                 return false;
   9215             },
   9216             .CANCELLED => {
   9217                 try syscall.checkCancel();
   9218                 continue;
   9219             },
   9220             .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
   9221             .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
   9222             else => |status| return syscall.unexpectedNtstatus(status),
   9223         };
   9224     }
   9225 
   9226     const operation: i32 = switch (lock) {
   9227         .none => posix.LOCK.UN,
   9228         .shared => posix.LOCK.SH | posix.LOCK.NB,
   9229         .exclusive => posix.LOCK.EX | posix.LOCK.NB,
   9230     };
   9231     const syscall: Syscall = try .start();
   9232     while (true) {
   9233         switch (posix.errno(posix.system.flock(file.handle, operation))) {
   9234             .SUCCESS => {
   9235                 syscall.finish();
   9236                 return true;
   9237             },
   9238             .INTR => {
   9239                 try syscall.checkCancel();
   9240                 continue;
   9241             },
   9242             .AGAIN => {
   9243                 syscall.finish();
   9244                 return false;
   9245             },
   9246             else => |e| {
   9247                 syscall.finish();
   9248                 switch (e) {
   9249                     .BADF => |err| return errnoBug(err),
   9250                     .INVAL => |err| return errnoBug(err), // invalid parameters
   9251                     .NOLCK => return error.SystemResources,
   9252                     .OPNOTSUPP => return error.FileLocksUnsupported,
   9253                     else => |err| return posix.unexpectedErrno(err),
   9254                 }
   9255             },
   9256         }
   9257     }
   9258 }
   9259 
   9260 fn fileUnlock(userdata: ?*anyopaque, file: File) void {
   9261     if (native_os == .wasi) return;
   9262     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9263     _ = t;
   9264 
   9265     if (is_windows) {
   9266         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   9267         while (true) switch (windows.ntdll.NtUnlockFile(
   9268             file.handle,
   9269             &io_status_block,
   9270             &windows_lock_range_off,
   9271             &windows_lock_range_len,
   9272             0,
   9273         )) {
   9274             .SUCCESS => return,
   9275             .CANCELLED => continue,
   9276             .RANGE_NOT_LOCKED => if (is_debug) unreachable else return, // Function asserts unlocked.
   9277             .ACCESS_VIOLATION => if (is_debug) unreachable else return, // bad io_status_block pointer
   9278             else => if (is_debug) unreachable else return, // Resource deallocation must succeed.
   9279         };
   9280     }
   9281 
   9282     while (true) {
   9283         switch (posix.errno(posix.system.flock(file.handle, posix.LOCK.UN))) {
   9284             .SUCCESS => return,
   9285             .CANCELED, .INTR => continue,
   9286             .AGAIN => return assert(!is_debug), // unlocking can't block
   9287             .BADF => return assert(!is_debug), // File descriptor used after closed.
   9288             .INVAL => return assert(!is_debug), // invalid parameters
   9289             .NOLCK => return assert(!is_debug), // Resource deallocation.
   9290             .OPNOTSUPP => return assert(!is_debug), // We already got the lock.
   9291             else => return assert(!is_debug), // Resource deallocation must succeed.
   9292         }
   9293     }
   9294 }
   9295 
   9296 fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError!void {
   9297     if (native_os == .wasi) return;
   9298     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9299     _ = t;
   9300 
   9301     if (is_windows) {
   9302         // On Windows it works like a semaphore + exclusivity flag. To
   9303         // implement this function, we first obtain another lock in shared
   9304         // mode. This changes the exclusivity flag, but increments the
   9305         // semaphore to 2. So we follow up with an NtUnlockFile which
   9306         // decrements the semaphore but does not modify the exclusivity flag.
   9307         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   9308         const syscall: Syscall = try .start();
   9309         while (true) switch (windows.ntdll.NtLockFile(
   9310             file.handle,
   9311             null,
   9312             null,
   9313             null,
   9314             &io_status_block,
   9315             &windows_lock_range_off,
   9316             &windows_lock_range_len,
   9317             null,
   9318             windows.TRUE,
   9319             windows.FALSE,
   9320         )) {
   9321             .SUCCESS => break syscall.finish(),
   9322             .CANCELLED => {
   9323                 try syscall.checkCancel();
   9324                 continue;
   9325             },
   9326             .INSUFFICIENT_RESOURCES => |err| return syscall.ntstatusBug(err),
   9327             .LOCK_NOT_GRANTED => |err| return syscall.ntstatusBug(err), // File was not locked in exclusive mode.
   9328             .ACCESS_VIOLATION => |err| return syscall.ntstatusBug(err), // bad io_status_block pointer
   9329             else => |status| return syscall.unexpectedNtstatus(status),
   9330         };
   9331         while (true) switch (windows.ntdll.NtUnlockFile(
   9332             file.handle,
   9333             &io_status_block,
   9334             &windows_lock_range_off,
   9335             &windows_lock_range_len,
   9336             0,
   9337         )) {
   9338             .SUCCESS => return,
   9339             .CANCELLED => continue,
   9340             .RANGE_NOT_LOCKED => if (is_debug) unreachable else return, // File was not locked.
   9341             .ACCESS_VIOLATION => if (is_debug) unreachable else return, // bad io_status_block pointer
   9342             else => if (is_debug) unreachable else return, // Resource deallocation must succeed.
   9343         };
   9344     }
   9345 
   9346     const operation = posix.LOCK.SH | posix.LOCK.NB;
   9347 
   9348     const syscall: Syscall = try .start();
   9349     while (true) {
   9350         switch (posix.errno(posix.system.flock(file.handle, operation))) {
   9351             .SUCCESS => {
   9352                 syscall.finish();
   9353                 return;
   9354             },
   9355             .INTR => {
   9356                 try syscall.checkCancel();
   9357                 continue;
   9358             },
   9359             else => |e| {
   9360                 syscall.finish();
   9361                 switch (e) {
   9362                     .AGAIN => |err| return errnoBug(err), // File was not locked in exclusive mode.
   9363                     .BADF => |err| return errnoBug(err),
   9364                     .INVAL => |err| return errnoBug(err), // invalid parameters
   9365                     .NOLCK => |err| return errnoBug(err), // Lock already obtained.
   9366                     .OPNOTSUPP => |err| return errnoBug(err), // Lock already obtained.
   9367                     else => |err| return posix.unexpectedErrno(err),
   9368                 }
   9369             },
   9370         }
   9371     }
   9372 }
   9373 
   9374 fn dirOpenDirWasi(
   9375     userdata: ?*anyopaque,
   9376     dir: Dir,
   9377     sub_path: []const u8,
   9378     options: Dir.OpenOptions,
   9379 ) Dir.OpenError!Dir {
   9380     if (builtin.link_libc) return dirOpenDirPosix(userdata, dir, sub_path, options);
   9381     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9382     _ = t;
   9383     const wasi = std.os.wasi;
   9384 
   9385     var base: std.os.wasi.rights_t = .{
   9386         .FD_FILESTAT_GET = true,
   9387         .FD_FDSTAT_SET_FLAGS = true,
   9388         .FD_FILESTAT_SET_TIMES = true,
   9389     };
   9390     if (options.access_sub_paths) {
   9391         base.FD_READDIR = true;
   9392         base.PATH_CREATE_DIRECTORY = true;
   9393         base.PATH_CREATE_FILE = true;
   9394         base.PATH_LINK_SOURCE = true;
   9395         base.PATH_LINK_TARGET = true;
   9396         base.PATH_OPEN = true;
   9397         base.PATH_READLINK = true;
   9398         base.PATH_RENAME_SOURCE = true;
   9399         base.PATH_RENAME_TARGET = true;
   9400         base.PATH_FILESTAT_GET = true;
   9401         base.PATH_FILESTAT_SET_SIZE = true;
   9402         base.PATH_FILESTAT_SET_TIMES = true;
   9403         base.PATH_SYMLINK = true;
   9404         base.PATH_REMOVE_DIRECTORY = true;
   9405         base.PATH_UNLINK_FILE = true;
   9406     }
   9407 
   9408     const lookup_flags: wasi.lookupflags_t = .{ .SYMLINK_FOLLOW = options.follow_symlinks };
   9409     const oflags: wasi.oflags_t = .{ .DIRECTORY = true };
   9410     const fdflags: wasi.fdflags_t = .{};
   9411     var fd: posix.fd_t = undefined;
   9412     const syscall: Syscall = try .start();
   9413     while (true) {
   9414         switch (wasi.path_open(dir.handle, lookup_flags, sub_path.ptr, sub_path.len, oflags, base, base, fdflags, &fd)) {
   9415             .SUCCESS => {
   9416                 syscall.finish();
   9417                 return .{ .handle = fd };
   9418             },
   9419             .INTR => {
   9420                 try syscall.checkCancel();
   9421                 continue;
   9422             },
   9423             else => |e| {
   9424                 syscall.finish();
   9425                 switch (e) {
   9426                     .FAULT => |err| return errnoBug(err),
   9427                     .INVAL => return error.BadPathName,
   9428                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   9429                     .ACCES => return error.AccessDenied,
   9430                     .LOOP => return error.SymLinkLoop,
   9431                     .MFILE => return error.ProcessFdQuotaExceeded,
   9432                     .NAMETOOLONG => return error.NameTooLong,
   9433                     .NFILE => return error.SystemFdQuotaExceeded,
   9434                     .NODEV => return error.NoDevice,
   9435                     .NOENT => return error.FileNotFound,
   9436                     .NOMEM => return error.SystemResources,
   9437                     .NOTDIR => return error.NotDir,
   9438                     .PERM => return error.PermissionDenied,
   9439                     .NOTCAPABLE => return error.AccessDenied,
   9440                     .ILSEQ => return error.BadPathName,
   9441                     else => |err| return posix.unexpectedErrno(err),
   9442                 }
   9443             },
   9444         }
   9445     }
   9446 }
   9447 
   9448 fn dirHardLink(
   9449     userdata: ?*anyopaque,
   9450     old_dir: Dir,
   9451     old_sub_path: []const u8,
   9452     new_dir: Dir,
   9453     new_sub_path: []const u8,
   9454     options: Dir.HardLinkOptions,
   9455 ) Dir.HardLinkError!void {
   9456     if (is_windows) return error.OperationUnsupported;
   9457     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9458     _ = t;
   9459 
   9460     if (native_os == .wasi and !builtin.link_libc) {
   9461         const flags: std.os.wasi.lookupflags_t = .{
   9462             .SYMLINK_FOLLOW = options.follow_symlinks,
   9463         };
   9464         const syscall: Syscall = try .start();
   9465         while (true) {
   9466             switch (std.os.wasi.path_link(
   9467                 old_dir.handle,
   9468                 flags,
   9469                 old_sub_path.ptr,
   9470                 old_sub_path.len,
   9471                 new_dir.handle,
   9472                 new_sub_path.ptr,
   9473                 new_sub_path.len,
   9474             )) {
   9475                 .SUCCESS => return syscall.finish(),
   9476                 .INTR => {
   9477                     try syscall.checkCancel();
   9478                     continue;
   9479                 },
   9480                 else => |e| {
   9481                     syscall.finish();
   9482                     switch (e) {
   9483                         .ACCES => return error.AccessDenied,
   9484                         .DQUOT => return error.DiskQuota,
   9485                         .EXIST => return error.PathAlreadyExists,
   9486                         .FAULT => |err| return errnoBug(err),
   9487                         .IO => return error.HardwareFailure,
   9488                         .LOOP => return error.SymLinkLoop,
   9489                         .MLINK => return error.LinkQuotaExceeded,
   9490                         .NAMETOOLONG => return error.NameTooLong,
   9491                         .NOENT => return error.FileNotFound,
   9492                         .NOMEM => return error.SystemResources,
   9493                         .NOSPC => return error.NoSpaceLeft,
   9494                         .NOTDIR => return error.NotDir,
   9495                         .PERM => return error.PermissionDenied,
   9496                         .ROFS => return error.ReadOnlyFileSystem,
   9497                         .XDEV => return error.CrossDevice,
   9498                         .INVAL => |err| return errnoBug(err),
   9499                         .ILSEQ => return error.BadPathName,
   9500                         else => |err| return posix.unexpectedErrno(err),
   9501                     }
   9502                 },
   9503             }
   9504         }
   9505     }
   9506 
   9507     var old_path_buffer: [posix.PATH_MAX]u8 = undefined;
   9508     var new_path_buffer: [posix.PATH_MAX]u8 = undefined;
   9509 
   9510     const old_sub_path_posix = try pathToPosix(old_sub_path, &old_path_buffer);
   9511     const new_sub_path_posix = try pathToPosix(new_sub_path, &new_path_buffer);
   9512 
   9513     const flags: u32 = if (options.follow_symlinks) posix.AT.SYMLINK_FOLLOW else 0;
   9514     return linkat(old_dir.handle, old_sub_path_posix, new_dir.handle, new_sub_path_posix, flags);
   9515 }
   9516 
   9517 fn fileClose(userdata: ?*anyopaque, files: []const File) void {
   9518     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9519     _ = t;
   9520     for (files) |file| {
   9521         if (is_windows) {
   9522             windows.CloseHandle(file.handle);
   9523         } else {
   9524             closeFd(file.handle);
   9525         }
   9526     }
   9527 }
   9528 
   9529 fn fileReadStreaming(userdata: ?*anyopaque, file: File, data: []const []u8) File.ReadStreamingError!usize {
   9530     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9531     _ = t;
   9532     if (is_windows) return fileReadStreamingWindows(file, data);
   9533     return fileReadStreamingPosix(file, data);
   9534 }
   9535 
   9536 fn fileReadStreamingPosix(file: File, data: []const []u8) File.ReadStreamingError!usize {
   9537     var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
   9538     var i: usize = 0;
   9539     for (data) |buf| {
   9540         if (iovecs_buffer.len - i == 0) break;
   9541         if (buf.len != 0) {
   9542             iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len };
   9543             i += 1;
   9544         }
   9545     }
   9546     if (i == 0) return 0;
   9547     const dest = iovecs_buffer[0..i];
   9548     assert(dest[0].len > 0);
   9549 
   9550     if (native_os == .wasi and !builtin.link_libc) {
   9551         const syscall: Syscall = try .start();
   9552         while (true) {
   9553             var nread: usize = undefined;
   9554             switch (std.os.wasi.fd_read(file.handle, dest.ptr, dest.len, &nread)) {
   9555                 .SUCCESS => {
   9556                     syscall.finish();
   9557                     if (nread == 0) return error.EndOfStream;
   9558                     return nread;
   9559                 },
   9560                 .INTR, .TIMEDOUT => {
   9561                     try syscall.checkCancel();
   9562                     continue;
   9563                 },
   9564                 .BADF => return syscall.fail(error.IsDir), // File operation on directory.
   9565                 .IO => return syscall.fail(error.InputOutput),
   9566                 .ISDIR => return syscall.fail(error.IsDir),
   9567                 .NOBUFS => return syscall.fail(error.SystemResources),
   9568                 .NOMEM => return syscall.fail(error.SystemResources),
   9569                 .NOTCONN => return syscall.fail(error.SocketUnconnected),
   9570                 .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
   9571                 .NOTCAPABLE => return syscall.fail(error.AccessDenied),
   9572                 .INVAL => |err| return syscall.errnoBug(err),
   9573                 .FAULT => |err| return syscall.errnoBug(err),
   9574                 else => |err| return syscall.unexpectedErrno(err),
   9575             }
   9576         }
   9577     }
   9578 
   9579     const syscall: Syscall = try .start();
   9580     while (true) {
   9581         const rc = posix.system.readv(file.handle, dest.ptr, @intCast(dest.len));
   9582         switch (posix.errno(rc)) {
   9583             .SUCCESS => {
   9584                 syscall.finish();
   9585                 if (rc == 0) return error.EndOfStream;
   9586                 return @intCast(rc);
   9587             },
   9588             .INTR, .TIMEDOUT => {
   9589                 try syscall.checkCancel();
   9590                 continue;
   9591             },
   9592             .BADF => {
   9593                 syscall.finish();
   9594                 if (native_os == .wasi) return error.IsDir; // File operation on directory.
   9595                 return error.NotOpenForReading;
   9596             },
   9597             .AGAIN => return syscall.fail(error.WouldBlock),
   9598             .IO => return syscall.fail(error.InputOutput),
   9599             .ISDIR => return syscall.fail(error.IsDir),
   9600             .NOBUFS => return syscall.fail(error.SystemResources),
   9601             .NOMEM => return syscall.fail(error.SystemResources),
   9602             .NOTCONN => return syscall.fail(error.SocketUnconnected),
   9603             .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
   9604             .INVAL => |err| return syscall.errnoBug(err),
   9605             .FAULT => |err| return syscall.errnoBug(err),
   9606             else => |err| return syscall.unexpectedErrno(err),
   9607         }
   9608     }
   9609 }
   9610 
   9611 fn fileReadStreamingWindows(file: File, data: []const []u8) File.ReadStreamingError!usize {
   9612     var iosb: windows.IO_STATUS_BLOCK = undefined;
   9613     var index: usize = 0;
   9614     while (data.len - index != 0 and data[index].len == 0) index += 1;
   9615     if (data.len - index == 0) return 0;
   9616     const buffer = data[index];
   9617     const short_buffer_len = std.math.lossyCast(u32, buffer.len);
   9618     if (file.flags.nonblocking) {
   9619         var done: bool = false;
   9620         switch (windows.ntdll.NtReadFile(
   9621             file.handle,
   9622             null, // event
   9623             flagApc,
   9624             &done, // APC context
   9625             &iosb,
   9626             buffer.ptr,
   9627             short_buffer_len,
   9628             null, // byte offset
   9629             null, // key
   9630         )) {
   9631             // We must wait for the APC routine.
   9632             .PENDING, .SUCCESS => while (!done) {
   9633                 // Once we get here we must not return from the function until the
   9634                 // operation completes, thereby releasing reference to the iosb.
   9635                 const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
   9636                     error.Canceled => |e| {
   9637                         var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
   9638                         _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
   9639                         while (!done) waitForApcOrAlert();
   9640                         return e;
   9641                     },
   9642                 };
   9643                 waitForApcOrAlert();
   9644                 alertable_syscall.finish();
   9645             },
   9646             else => |status| iosb.u.Status = status,
   9647         }
   9648     } else {
   9649         const syscall: Syscall = try .start();
   9650         while (true) switch (windows.ntdll.NtReadFile(
   9651             file.handle,
   9652             null, // event
   9653             null, // APC routine
   9654             null, // APC context
   9655             &iosb,
   9656             buffer.ptr,
   9657             short_buffer_len,
   9658             null, // byte offset
   9659             null, // key
   9660         )) {
   9661             .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
   9662             .CANCELLED => {
   9663                 try syscall.checkCancel();
   9664                 continue;
   9665             },
   9666             else => |status| {
   9667                 syscall.finish();
   9668                 iosb.u.Status = status;
   9669                 break;
   9670             },
   9671         };
   9672     }
   9673     return ntReadFileResult(&iosb);
   9674 }
   9675 
   9676 fn flagApc(userdata: ?*anyopaque, _: *windows.IO_STATUS_BLOCK, _: windows.ULONG) callconv(.winapi) void {
   9677     const flag: *bool = @ptrCast(userdata);
   9678     flag.* = true;
   9679 }
   9680 
   9681 fn ntReadFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
   9682     switch (io_status_block.u.Status) {
   9683         .PENDING => unreachable,
   9684         .CANCELLED => unreachable,
   9685         .SUCCESS => return io_status_block.Information,
   9686         .END_OF_FILE, .PIPE_BROKEN => return error.EndOfStream,
   9687         .INVALID_HANDLE => return error.NotOpenForReading,
   9688         .INVALID_DEVICE_REQUEST => return error.IsDir,
   9689         .FILE_LOCK_CONFLICT => return error.LockViolation,
   9690         .ACCESS_DENIED => return error.AccessDenied,
   9691         else => |status| return windows.unexpectedStatus(status),
   9692     }
   9693 }
   9694 
   9695 fn ntWriteFileResult(io_status_block: *const windows.IO_STATUS_BLOCK) !usize {
   9696     switch (io_status_block.u.Status) {
   9697         .PENDING => unreachable,
   9698         .CANCELLED => unreachable,
   9699         .SUCCESS => return io_status_block.Information,
   9700         .INVALID_USER_BUFFER => return error.SystemResources,
   9701         .NO_MEMORY => return error.SystemResources,
   9702         .QUOTA_EXCEEDED => return error.SystemResources,
   9703         .PIPE_BROKEN => return error.BrokenPipe,
   9704         .INVALID_HANDLE => return error.NotOpenForWriting,
   9705         .FILE_LOCK_CONFLICT => return error.LockViolation,
   9706         .ACCESS_DENIED => return error.AccessDenied,
   9707         .WORKING_SET_QUOTA => return error.SystemResources,
   9708         .DISK_FULL => return error.NoSpaceLeft,
   9709         else => |status| return windows.unexpectedStatus(status),
   9710     }
   9711 }
   9712 
   9713 fn fileReadPositionalPosix(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
   9714     var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
   9715     var i: usize = 0;
   9716     for (data) |buf| {
   9717         if (iovecs_buffer.len - i == 0) break;
   9718         if (buf.len != 0) {
   9719             iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len };
   9720             i += 1;
   9721         }
   9722     }
   9723     if (i == 0) return 0;
   9724     const dest = iovecs_buffer[0..i];
   9725     assert(dest[0].len > 0);
   9726 
   9727     if (native_os == .wasi and !builtin.link_libc) {
   9728         const syscall: Syscall = try .start();
   9729         while (true) {
   9730             var nread: usize = undefined;
   9731             switch (std.os.wasi.fd_pread(file.handle, dest.ptr, dest.len, offset, &nread)) {
   9732                 .SUCCESS => {
   9733                     syscall.finish();
   9734                     return nread;
   9735                 },
   9736                 .INTR, .TIMEDOUT => {
   9737                     try syscall.checkCancel();
   9738                     continue;
   9739                 },
   9740                 .NOTCONN => |err| return syscall.errnoBug(err), // not a socket
   9741                 .CONNRESET => |err| return syscall.errnoBug(err), // not a socket
   9742                 .INVAL => |err| return syscall.errnoBug(err),
   9743                 .FAULT => |err| return syscall.errnoBug(err), // segmentation fault
   9744                 .AGAIN => |err| return syscall.errnoBug(err),
   9745                 .IO => return syscall.fail(error.InputOutput),
   9746                 .ISDIR => return syscall.fail(error.IsDir),
   9747                 .BADF => return syscall.fail(error.IsDir),
   9748                 .NOBUFS => return syscall.fail(error.SystemResources),
   9749                 .NOMEM => return syscall.fail(error.SystemResources),
   9750                 .NXIO => return syscall.fail(error.Unseekable),
   9751                 .SPIPE => return syscall.fail(error.Unseekable),
   9752                 .OVERFLOW => return syscall.fail(error.Unseekable),
   9753                 .NOTCAPABLE => return syscall.fail(error.AccessDenied),
   9754                 else => |err| return syscall.unexpectedErrno(err),
   9755             }
   9756         }
   9757     }
   9758 
   9759     if (have_preadv) {
   9760         const syscall: Syscall = try .start();
   9761         while (true) {
   9762             const rc = preadv_sym(file.handle, dest.ptr, @intCast(dest.len), @bitCast(offset));
   9763             switch (posix.errno(rc)) {
   9764                 .SUCCESS => {
   9765                     syscall.finish();
   9766                     return @bitCast(rc);
   9767                 },
   9768                 .INTR, .TIMEDOUT => {
   9769                     try syscall.checkCancel();
   9770                     continue;
   9771                 },
   9772                 .NXIO => return syscall.fail(error.Unseekable),
   9773                 .SPIPE => return syscall.fail(error.Unseekable),
   9774                 .OVERFLOW => return syscall.fail(error.Unseekable),
   9775                 .NOBUFS => return syscall.fail(error.SystemResources),
   9776                 .NOMEM => return syscall.fail(error.SystemResources),
   9777                 .AGAIN => return syscall.fail(error.WouldBlock),
   9778                 .IO => return syscall.fail(error.InputOutput),
   9779                 .ISDIR => return syscall.fail(error.IsDir),
   9780                 .NOTCONN => |err| return syscall.errnoBug(err), // not a socket
   9781                 .CONNRESET => |err| return syscall.errnoBug(err), // not a socket
   9782                 .INVAL => |err| return syscall.errnoBug(err),
   9783                 .FAULT => |err| return syscall.errnoBug(err),
   9784                 .BADF => {
   9785                     syscall.finish();
   9786                     if (native_os == .wasi) return error.IsDir; // File operation on directory.
   9787                     return error.NotOpenForReading;
   9788                 },
   9789                 else => |err| return syscall.unexpectedErrno(err),
   9790             }
   9791         }
   9792     }
   9793 
   9794     const syscall: Syscall = try .start();
   9795     while (true) {
   9796         const rc = posix.pread(file.handle, dest[0].ptr, @intCast(dest[0].len), @bitCast(offset));
   9797         switch (posix.errno(rc)) {
   9798             .SUCCESS => {
   9799                 syscall.finish();
   9800                 return @bitCast(rc);
   9801             },
   9802             .INTR, .TIMEDOUT => {
   9803                 try syscall.checkCancel();
   9804                 continue;
   9805             },
   9806             .NXIO => return syscall.fail(error.Unseekable),
   9807             .SPIPE => return syscall.fail(error.Unseekable),
   9808             .OVERFLOW => return syscall.fail(error.Unseekable),
   9809             .NOBUFS => return syscall.fail(error.SystemResources),
   9810             .NOMEM => return syscall.fail(error.SystemResources),
   9811             .AGAIN => return syscall.fail(error.WouldBlock),
   9812             .IO => return syscall.fail(error.InputOutput),
   9813             .ISDIR => return syscall.fail(error.IsDir),
   9814             .NOTCONN => |err| return syscall.errnoBug(err), // not a socket
   9815             .CONNRESET => |err| return syscall.errnoBug(err), // not a socket
   9816             .INVAL => |err| return syscall.errnoBug(err),
   9817             .FAULT => |err| return syscall.errnoBug(err),
   9818             .BADF => {
   9819                 syscall.finish();
   9820                 if (native_os == .wasi) return error.IsDir; // File operation on directory.
   9821                 return error.NotOpenForReading;
   9822             },
   9823             else => |err| return syscall.unexpectedErrno(err),
   9824         }
   9825     }
   9826 }
   9827 
   9828 fn fileReadPositional(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
   9829     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9830     _ = t;
   9831     if (is_windows) return fileReadPositionalWindows(file, data, offset);
   9832     return fileReadPositionalPosix(file, data, offset);
   9833 }
   9834 
   9835 fn fileReadPositionalWindows(file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
   9836     var index: usize = 0;
   9837     while (index < data.len and data[index].len == 0) index += 1;
   9838     if (index == data.len) return 0;
   9839     const buffer = data[index];
   9840 
   9841     return readFilePositionalWindows(file, buffer, offset);
   9842 }
   9843 
   9844 fn readFilePositionalWindows(file: File, buffer: []u8, offset: u64) File.ReadPositionalError!usize {
   9845     var iosb: windows.IO_STATUS_BLOCK = undefined;
   9846     const short_buffer_len = std.math.lossyCast(u32, buffer.len);
   9847     const signed_offset: windows.LARGE_INTEGER = @intCast(offset);
   9848     if (file.flags.nonblocking) {
   9849         var done: bool = false;
   9850         switch (windows.ntdll.NtReadFile(
   9851             file.handle,
   9852             null, // event
   9853             flagApc,
   9854             &done, // APC context
   9855             &iosb,
   9856             buffer.ptr,
   9857             short_buffer_len,
   9858             &signed_offset,
   9859             null, // key
   9860         )) {
   9861             // We must wait for the APC routine.
   9862             .PENDING, .SUCCESS => while (!done) {
   9863                 // Once we get here we must not return from the function until the
   9864                 // operation completes, thereby releasing reference to the iosb.
   9865                 const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
   9866                     error.Canceled => |e| {
   9867                         var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
   9868                         _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
   9869                         while (!done) waitForApcOrAlert();
   9870                         return e;
   9871                     },
   9872                 };
   9873                 waitForApcOrAlert();
   9874                 alertable_syscall.finish();
   9875             },
   9876             else => |status| iosb.u.Status = status,
   9877         }
   9878     } else {
   9879         const syscall: Syscall = try .start();
   9880         while (true) switch (windows.ntdll.NtReadFile(
   9881             file.handle,
   9882             null, // event
   9883             null, // APC routine
   9884             null, // APC context
   9885             &iosb,
   9886             buffer.ptr,
   9887             short_buffer_len,
   9888             &signed_offset,
   9889             null, // key
   9890         )) {
   9891             .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
   9892             .CANCELLED => try syscall.checkCancel(),
   9893             else => |status| {
   9894                 syscall.finish();
   9895                 iosb.u.Status = status;
   9896                 break;
   9897             },
   9898         };
   9899     }
   9900     return ntReadFileResult(&iosb) catch |err| switch (err) {
   9901         error.EndOfStream => 0,
   9902         else => |e| e,
   9903     };
   9904 }
   9905 
   9906 fn fileSeekBy(userdata: ?*anyopaque, file: File, offset: i64) File.SeekError!void {
   9907     const t: *Threaded = @ptrCast(@alignCast(userdata));
   9908     _ = t;
   9909 
   9910     if (is_windows) {
   9911         var iosb: windows.IO_STATUS_BLOCK = undefined;
   9912         var info: windows.FILE.POSITION_INFORMATION = undefined;
   9913         const syscall: Syscall = try .start();
   9914         while (true) switch (windows.ntdll.NtQueryInformationFile(
   9915             file.handle,
   9916             &iosb,
   9917             &info,
   9918             @sizeOf(windows.FILE.POSITION_INFORMATION),
   9919             .Position,
   9920         )) {
   9921             .SUCCESS => break,
   9922             .CANCELLED => try syscall.checkCancel(),
   9923             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   9924             .PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
   9925             else => |status| return syscall.unexpectedNtstatus(status),
   9926         };
   9927         info.CurrentByteOffset = @bitCast((if (offset >= 0) std.math.add(
   9928             u64,
   9929             @bitCast(info.CurrentByteOffset),
   9930             @intCast(offset),
   9931         ) else std.math.sub(
   9932             u64,
   9933             @bitCast(info.CurrentByteOffset),
   9934             @intCast(-offset),
   9935         )) catch |err| switch (err) {
   9936             error.Overflow => return syscall.fail(error.Unseekable),
   9937         });
   9938         while (true) switch (windows.ntdll.NtSetInformationFile(
   9939             file.handle,
   9940             &iosb,
   9941             &info,
   9942             @sizeOf(windows.FILE.POSITION_INFORMATION),
   9943             .Position,
   9944         )) {
   9945             .SUCCESS => return syscall.finish(),
   9946             .CANCELLED => try syscall.checkCancel(),
   9947             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
   9948             .PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
   9949             else => |status| return syscall.unexpectedNtstatus(status),
   9950         };
   9951     }
   9952 
   9953     if (native_os == .wasi and !builtin.link_libc) {
   9954         var new_offset: std.os.wasi.filesize_t = undefined;
   9955         const syscall: Syscall = try .start();
   9956         while (true) {
   9957             switch (std.os.wasi.fd_seek(file.handle, offset, .CUR, &new_offset)) {
   9958                 .SUCCESS => {
   9959                     syscall.finish();
   9960                     return;
   9961                 },
   9962                 .INTR => {
   9963                     try syscall.checkCancel();
   9964                     continue;
   9965                 },
   9966                 else => |e| {
   9967                     syscall.finish();
   9968                     switch (e) {
   9969                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
   9970                         .INVAL => return error.Unseekable,
   9971                         .OVERFLOW => return error.Unseekable,
   9972                         .SPIPE => return error.Unseekable,
   9973                         .NXIO => return error.Unseekable,
   9974                         .NOTCAPABLE => return error.AccessDenied,
   9975                         else => |err| return posix.unexpectedErrno(err),
   9976                     }
   9977                 },
   9978             }
   9979         }
   9980     }
   9981 
   9982     if (posix.SEEK == void) return error.Unseekable;
   9983 
   9984     if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   9985         var result: u64 = undefined;
   9986         const syscall: Syscall = try .start();
   9987         while (true) {
   9988             switch (posix.errno(posix.system.llseek(file.handle, @bitCast(offset), &result, posix.SEEK.CUR))) {
   9989                 .SUCCESS => {
   9990                     syscall.finish();
   9991                     return;
   9992                 },
   9993                 .INTR => {
   9994                     try syscall.checkCancel();
   9995                     continue;
   9996                 },
   9997                 else => |e| {
   9998                     syscall.finish();
   9999                     switch (e) {
  10000                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  10001                         .INVAL => return error.Unseekable,
  10002                         .OVERFLOW => return error.Unseekable,
  10003                         .SPIPE => return error.Unseekable,
  10004                         .NXIO => return error.Unseekable,
  10005                         else => |err| return posix.unexpectedErrno(err),
  10006                     }
  10007                 },
  10008             }
  10009         }
  10010     }
  10011 
  10012     const syscall: Syscall = try .start();
  10013     while (true) {
  10014         switch (posix.errno(lseek_sym(file.handle, offset, posix.SEEK.CUR))) {
  10015             .SUCCESS => {
  10016                 syscall.finish();
  10017                 return;
  10018             },
  10019             .INTR => {
  10020                 try syscall.checkCancel();
  10021                 continue;
  10022             },
  10023             else => |e| {
  10024                 syscall.finish();
  10025                 switch (e) {
  10026                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  10027                     .INVAL => return error.Unseekable,
  10028                     .OVERFLOW => return error.Unseekable,
  10029                     .SPIPE => return error.Unseekable,
  10030                     .NXIO => return error.Unseekable,
  10031                     else => |err| return posix.unexpectedErrno(err),
  10032                 }
  10033             },
  10034         }
  10035     }
  10036 }
  10037 
  10038 fn fileSeekTo(userdata: ?*anyopaque, file: File, offset: u64) File.SeekError!void {
  10039     const t: *Threaded = @ptrCast(@alignCast(userdata));
  10040     _ = t;
  10041 
  10042     if (is_windows) {
  10043         var iosb: windows.IO_STATUS_BLOCK = undefined;
  10044         var info: windows.FILE.POSITION_INFORMATION = .{ .CurrentByteOffset = @bitCast(offset) };
  10045         const syscall: Syscall = try .start();
  10046         while (true) switch (windows.ntdll.NtSetInformationFile(
  10047             file.handle,
  10048             &iosb,
  10049             &info,
  10050             @sizeOf(windows.FILE.POSITION_INFORMATION),
  10051             .Position,
  10052         )) {
  10053             .SUCCESS => return syscall.finish(),
  10054             .CANCELLED => try syscall.checkCancel(),
  10055             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
  10056             .PIPE_NOT_AVAILABLE => return syscall.fail(error.Unseekable),
  10057             else => |status| return syscall.unexpectedNtstatus(status),
  10058         };
  10059     }
  10060 
  10061     if (native_os == .wasi and !builtin.link_libc) {
  10062         const syscall: Syscall = try .start();
  10063         while (true) {
  10064             var new_offset: std.os.wasi.filesize_t = undefined;
  10065             switch (std.os.wasi.fd_seek(file.handle, @bitCast(offset), .SET, &new_offset)) {
  10066                 .SUCCESS => {
  10067                     syscall.finish();
  10068                     return;
  10069                 },
  10070                 .INTR => {
  10071                     try syscall.checkCancel();
  10072                     continue;
  10073                 },
  10074                 else => |e| {
  10075                     syscall.finish();
  10076                     switch (e) {
  10077                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  10078                         .INVAL => return error.Unseekable,
  10079                         .OVERFLOW => return error.Unseekable,
  10080                         .SPIPE => return error.Unseekable,
  10081                         .NXIO => return error.Unseekable,
  10082                         .NOTCAPABLE => return error.AccessDenied,
  10083                         else => |err| return posix.unexpectedErrno(err),
  10084                     }
  10085                 },
  10086             }
  10087         }
  10088     }
  10089 
  10090     if (posix.SEEK == void) return error.Unseekable;
  10091 
  10092     return posixSeekTo(file.handle, offset);
  10093 }
  10094 
  10095 fn posixSeekTo(fd: posix.fd_t, offset: u64) File.SeekError!void {
  10096     if (native_os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
  10097         const syscall: Syscall = try .start();
  10098         while (true) {
  10099             var result: u64 = undefined;
  10100             switch (posix.errno(posix.system.llseek(fd, offset, &result, posix.SEEK.SET))) {
  10101                 .SUCCESS => {
  10102                     syscall.finish();
  10103                     return;
  10104                 },
  10105                 .INTR => {
  10106                     try syscall.checkCancel();
  10107                     continue;
  10108                 },
  10109                 else => |e| {
  10110                     syscall.finish();
  10111                     switch (e) {
  10112                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  10113                         .INVAL => return error.Unseekable,
  10114                         .OVERFLOW => return error.Unseekable,
  10115                         .SPIPE => return error.Unseekable,
  10116                         .NXIO => return error.Unseekable,
  10117                         else => |err| return posix.unexpectedErrno(err),
  10118                     }
  10119                 },
  10120             }
  10121         }
  10122     }
  10123 
  10124     const syscall: Syscall = try .start();
  10125     while (true) {
  10126         switch (posix.errno(lseek_sym(fd, @bitCast(offset), posix.SEEK.SET))) {
  10127             .SUCCESS => {
  10128                 syscall.finish();
  10129                 return;
  10130             },
  10131             .INTR => {
  10132                 try syscall.checkCancel();
  10133                 continue;
  10134             },
  10135             else => |e| {
  10136                 syscall.finish();
  10137                 switch (e) {
  10138                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  10139                     .INVAL => return error.Unseekable,
  10140                     .OVERFLOW => return error.Unseekable,
  10141                     .SPIPE => return error.Unseekable,
  10142                     .NXIO => return error.Unseekable,
  10143                     else => |err| return posix.unexpectedErrno(err),
  10144                 }
  10145             },
  10146         }
  10147     }
  10148 }
  10149 
  10150 fn processExecutableOpen(userdata: ?*anyopaque, flags: File.OpenFlags) process.OpenExecutableError!File {
  10151     const t: *Threaded = @ptrCast(@alignCast(userdata));
  10152     switch (native_os) {
  10153         .wasi => return error.OperationUnsupported,
  10154         .linux, .serenity => return dirOpenFilePosix(t, .{ .handle = posix.AT.FDCWD }, "/proc/self/exe", flags),
  10155         .windows => {
  10156             // If ImagePathName is a symlink, then it will contain the path of the symlink,
  10157             // not the path that the symlink points to. However, because we are opening
  10158             // the file, we can let the openFileW call follow the symlink for us.
  10159             const image_path_name = windows.peb().ProcessParameters.ImagePathName.sliceZ();
  10160             const prefixed_path_w = try wToPrefixedFileW(null, image_path_name);
  10161             return dirOpenFileWtf16(null, prefixed_path_w.span(), flags);
  10162         },
  10163         .driverkit,
  10164         .ios,
  10165         .maccatalyst,
  10166         .macos,
  10167         .tvos,
  10168         .visionos,
  10169         .watchos,
  10170         => {
  10171             // _NSGetExecutablePath() returns a path that might be a symlink to
  10172             // the executable. Here it does not matter since we open it.
  10173             var symlink_path_buf: [posix.PATH_MAX + 1]u8 = undefined;
  10174             var n: u32 = symlink_path_buf.len;
  10175             const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &n);
  10176             if (rc != 0) return error.NameTooLong;
  10177             const symlink_path = std.mem.sliceTo(&symlink_path_buf, 0);
  10178             return dirOpenFilePosix(t, .cwd(), symlink_path, flags);
  10179         },
  10180         else => {
  10181             var buffer: [Dir.max_path_bytes]u8 = undefined;
  10182             const n = try processExecutablePath(t, &buffer);
  10183             buffer[n] = 0;
  10184             const executable_path = buffer[0..n :0];
  10185             return dirOpenFilePosix(t, .cwd(), executable_path, flags);
  10186         },
  10187     }
  10188 }
  10189 
  10190 fn processExecutablePath(userdata: ?*anyopaque, out_buffer: []u8) process.ExecutablePathError!usize {
  10191     const t: *Threaded = @ptrCast(@alignCast(userdata));
  10192 
  10193     switch (native_os) {
  10194         .driverkit,
  10195         .ios,
  10196         .maccatalyst,
  10197         .macos,
  10198         .tvos,
  10199         .visionos,
  10200         .watchos,
  10201         => {
  10202             // _NSGetExecutablePath() returns a path that might be a symlink to
  10203             // the executable.
  10204             var symlink_path_buf: [posix.PATH_MAX + 1]u8 = undefined;
  10205             var n: u32 = symlink_path_buf.len;
  10206             const rc = std.c._NSGetExecutablePath(&symlink_path_buf, &n);
  10207             if (rc != 0) return error.NameTooLong;
  10208             const symlink_path = std.mem.sliceTo(&symlink_path_buf, 0);
  10209             return Io.Dir.realPathFileAbsolute(io(t), symlink_path, out_buffer) catch |err| switch (err) {
  10210                 error.NetworkNotFound => unreachable, // Windows-only
  10211                 error.FileBusy => unreachable, // Windows-only
  10212                 else => |e| return e,
  10213             };
  10214         },
  10215         .linux, .serenity => return Io.Dir.readLinkAbsolute(io(t), "/proc/self/exe", out_buffer) catch |err| switch (err) {
  10216             error.UnsupportedReparsePointType => unreachable, // Windows-only
  10217             error.NetworkNotFound => unreachable, // Windows-only
  10218             error.FileBusy => unreachable, // Windows-only
  10219             else => |e| return e,
  10220         },
  10221         .illumos => return Io.Dir.readLinkAbsolute(io(t), "/proc/self/path/a.out", out_buffer) catch |err| switch (err) {
  10222             error.UnsupportedReparsePointType => unreachable, // Windows-only
  10223             error.NetworkNotFound => unreachable, // Windows-only
  10224             error.FileBusy => unreachable, // Windows-only
  10225             else => |e| return e,
  10226         },
  10227         .freebsd, .dragonfly => {
  10228             var mib: [4]c_int = .{ posix.CTL.KERN, posix.KERN.PROC, posix.KERN.PROC_PATHNAME, -1 };
  10229             var out_len: usize = out_buffer.len;
  10230             const syscall: Syscall = try .start();
  10231             while (true) switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
  10232                 .SUCCESS => {
  10233                     syscall.finish();
  10234                     return out_len - 1; // discard terminating NUL
  10235                 },
  10236                 .INTR => {
  10237                     try syscall.checkCancel();
  10238                     continue;
  10239                 },
  10240                 .PERM => return syscall.fail(error.PermissionDenied),
  10241                 .NOMEM => return syscall.fail(error.SystemResources),
  10242                 .FAULT => |err| return syscall.errnoBug(err),
  10243                 .NOENT => |err| return syscall.errnoBug(err),
  10244                 else => |err| return syscall.unexpectedErrno(err),
  10245             };
  10246         },
  10247         .netbsd => {
  10248             var mib = [4]c_int{ posix.CTL.KERN, posix.KERN.PROC_ARGS, -1, posix.KERN.PROC_PATHNAME };
  10249             var out_len: usize = out_buffer.len;
  10250             const syscall: Syscall = try .start();
  10251             while (true) {
  10252                 switch (posix.errno(posix.system.sysctl(&mib, mib.len, out_buffer.ptr, &out_len, null, 0))) {
  10253                     .SUCCESS => {
  10254                         syscall.finish();
  10255                         return out_len - 1; // discard terminating NUL
  10256                     },
  10257                     .INTR => {
  10258                         try syscall.checkCancel();
  10259                         continue;
  10260                     },
  10261                     .PERM => return syscall.fail(error.PermissionDenied),
  10262                     .NOMEM => return syscall.fail(error.SystemResources),
  10263                     .FAULT => |err| return syscall.errnoBug(err),
  10264                     .NOENT => |err| return syscall.errnoBug(err),
  10265                     else => |err| return syscall.unexpectedErrno(err),
  10266                 }
  10267             }
  10268         },
  10269         .openbsd, .haiku => {
  10270             // The best we can do on these operating systems is check based on
  10271             // the first process argument.
  10272             const argv0 = std.mem.span(t.argv0.value orelse return error.OperationUnsupported);
  10273             if (std.mem.findScalar(u8, argv0, '/') != null) {
  10274                 // argv[0] is a path (relative or absolute): use realpath(3) directly
  10275                 var resolved_buf: [std.c.PATH_MAX]u8 = undefined;
  10276                 const syscall: Syscall = try .start();
  10277                 while (true) {
  10278                     if (std.c.realpath(argv0, &resolved_buf)) |p| {
  10279                         assert(p == &resolved_buf);
  10280                         break syscall.finish();
  10281                     } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) {
  10282                         .INTR => {
  10283                             try syscall.checkCancel();
  10284                             continue;
  10285                         },
  10286                         else => |e| {
  10287                             syscall.finish();
  10288                             switch (e) {
  10289                                 .ACCES => return error.AccessDenied,
  10290                                 .INVAL => |err| return errnoBug(err), // the pathname argument is a null pointer
  10291                                 .IO => return error.InputOutput,
  10292                                 .LOOP => return error.SymLinkLoop,
  10293                                 .NAMETOOLONG => return error.NameTooLong,
  10294                                 .NOENT => return error.FileNotFound,
  10295                                 .NOTDIR => return error.NotDir,
  10296                                 .NOMEM => |err| return errnoBug(err), // sufficient storage space is unavailable for allocation
  10297                                 else => |err| return posix.unexpectedErrno(err),
  10298                             }
  10299                         },
  10300                     }
  10301                 }
  10302                 const resolved = std.mem.sliceTo(&resolved_buf, 0);
  10303                 if (resolved.len > out_buffer.len)
  10304                     return error.NameTooLong;
  10305                 @memcpy(out_buffer[0..resolved.len], resolved);
  10306                 return resolved.len;
  10307             } else if (argv0.len != 0) {
  10308                 // argv[0] is not empty (and not a path): search PATH
  10309                 t.scanEnviron();
  10310                 const PATH = t.environ.string.PATH orelse return error.FileNotFound;
  10311                 var it = std.mem.tokenizeScalar(u8, PATH, ':');
  10312                 it: while (it.next()) |dir| {
  10313                     var resolved_path_buf: [std.c.PATH_MAX]u8 = undefined;
  10314                     const resolved_path = std.fmt.bufPrintSentinel(&resolved_path_buf, "{s}/{s}", .{
  10315                         dir, argv0,
  10316                     }, 0) catch continue;
  10317 
  10318                     var resolved_buf: [std.c.PATH_MAX]u8 = undefined;
  10319                     const syscall: Syscall = try .start();
  10320                     while (true) {
  10321                         if (std.c.realpath(resolved_path, &resolved_buf)) |p| {
  10322                             assert(p == &resolved_buf);
  10323                             break syscall.finish();
  10324                         } else switch (@as(std.c.E, @enumFromInt(std.c._errno().*))) {
  10325                             .INTR => {
  10326                                 try syscall.checkCancel();
  10327                                 continue;
  10328                             },
  10329                             .NAMETOOLONG => {
  10330                                 syscall.finish();
  10331                                 return error.NameTooLong;
  10332                             },
  10333                             .NOMEM => {
  10334                                 syscall.finish();
  10335                                 return error.SystemResources;
  10336                             },
  10337                             .IO => {
  10338                                 syscall.finish();
  10339                                 return error.InputOutput;
  10340                             },
  10341                             .ACCES, .LOOP, .NOENT, .NOTDIR => {
  10342                                 syscall.finish();
  10343                                 continue :it;
  10344                             },
  10345                             else => |err| {
  10346                                 syscall.finish();
  10347                                 return posix.unexpectedErrno(err);
  10348                             },
  10349                         }
  10350                     }
  10351                     const resolved = std.mem.sliceTo(&resolved_buf, 0);
  10352                     if (resolved.len > out_buffer.len)
  10353                         return error.NameTooLong;
  10354                     @memcpy(out_buffer[0..resolved.len], resolved);
  10355                     return resolved.len;
  10356                 }
  10357             }
  10358             return error.FileNotFound;
  10359         },
  10360         .windows => {
  10361             // If ImagePathName is a symlink, then it will contain the path of the
  10362             // symlink, not the path that the symlink points to. We want the path
  10363             // that the symlink points to, though, so we need to get the realpath.
  10364             var path_name_w_buf = try wToPrefixedFileW(
  10365                 null,
  10366                 windows.peb().ProcessParameters.ImagePathName.sliceZ(),
  10367             );
  10368 
  10369             const h_file = handle: {
  10370                 if (OpenFile(path_name_w_buf.span(), .{
  10371                     .dir = null,
  10372                     .access_mask = .{
  10373                         .GENERIC = .{ .READ = true },
  10374                         .STANDARD = .{ .SYNCHRONIZE = true },
  10375                     },
  10376                     .creation = .OPEN,
  10377                     .filter = .any,
  10378                 })) |handle| {
  10379                     break :handle handle;
  10380                 } else |err| switch (err) {
  10381                     error.WouldBlock => unreachable,
  10382                     error.FileBusy => unreachable,
  10383                     else => |e| return e,
  10384                 }
  10385             };
  10386             defer windows.CloseHandle(h_file);
  10387 
  10388             const wide_slice = try GetFinalPathNameByHandle(h_file, .{}, &path_name_w_buf.data);
  10389 
  10390             const len = std.unicode.calcWtf8Len(wide_slice);
  10391             if (len > out_buffer.len)
  10392                 return error.NameTooLong;
  10393 
  10394             const end_index = std.unicode.wtf16LeToWtf8(out_buffer, wide_slice);
  10395             return end_index;
  10396         },
  10397         else => return error.OperationUnsupported,
  10398     }
  10399 }
  10400 
  10401 fn fileWritePositional(
  10402     userdata: ?*anyopaque,
  10403     file: File,
  10404     header: []const u8,
  10405     data: []const []const u8,
  10406     splat: usize,
  10407     offset: u64,
  10408 ) File.WritePositionalError!usize {
  10409     const t: *Threaded = @ptrCast(@alignCast(userdata));
  10410     _ = t;
  10411 
  10412     if (is_windows) {
  10413         if (header.len != 0) {
  10414             return writeFilePositionalWindows(file, header, offset);
  10415         }
  10416         for (data[0 .. data.len - 1]) |buf| {
  10417             if (buf.len == 0) continue;
  10418             return writeFilePositionalWindows(file, buf, offset);
  10419         }
  10420         const pattern = data[data.len - 1];
  10421         if (pattern.len == 0 or splat == 0) return 0;
  10422         return writeFilePositionalWindows(file, pattern, offset);
  10423     }
  10424 
  10425     var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
  10426     var iovlen: iovlen_t = 0;
  10427     addBuf(&iovecs, &iovlen, header);
  10428     for (data[0 .. data.len - 1]) |bytes| addBuf(&iovecs, &iovlen, bytes);
  10429     const pattern = data[data.len - 1];
  10430 
  10431     var splat_backup_buffer: [splat_buffer_size]u8 = undefined;
  10432     if (iovecs.len - iovlen != 0) switch (splat) {
  10433         0 => {},
  10434         1 => addBuf(&iovecs, &iovlen, pattern),
  10435         else => switch (pattern.len) {
  10436             0 => {},
  10437             1 => {
  10438                 const splat_buffer = &splat_backup_buffer;
  10439                 const memset_len = @min(splat_buffer.len, splat);
  10440                 const buf = splat_buffer[0..memset_len];
  10441                 @memset(buf, pattern[0]);
  10442                 addBuf(&iovecs, &iovlen, buf);
  10443                 var remaining_splat = splat - buf.len;
  10444                 while (remaining_splat > splat_buffer.len and iovecs.len - iovlen != 0) {
  10445                     assert(buf.len == splat_buffer.len);
  10446                     addBuf(&iovecs, &iovlen, splat_buffer);
  10447                     remaining_splat -= splat_buffer.len;
  10448                 }
  10449                 addBuf(&iovecs, &iovlen, splat_buffer[0..@min(remaining_splat, splat_buffer.len)]);
  10450             },
  10451             else => for (0..@min(splat, iovecs.len - iovlen)) |_| {
  10452                 addBuf(&iovecs, &iovlen, pattern);
  10453             },
  10454         },
  10455     };
  10456 
  10457     if (iovlen == 0) return 0;
  10458 
  10459     if (native_os == .wasi and !builtin.link_libc) {
  10460         var n_written: usize = undefined;
  10461         const syscall: Syscall = try .start();
  10462         while (true) {
  10463             switch (std.os.wasi.fd_pwrite(file.handle, &iovecs, iovlen, offset, &n_written)) {
  10464                 .SUCCESS => {
  10465                     syscall.finish();
  10466                     return n_written;
  10467                 },
  10468                 .INTR => {
  10469                     try syscall.checkCancel();
  10470                     continue;
  10471                 },
  10472                 else => |e| {
  10473                     syscall.finish();
  10474                     switch (e) {
  10475                         .INVAL => |err| return errnoBug(err),
  10476                         .FAULT => |err| return errnoBug(err),
  10477                         .AGAIN => |err| return errnoBug(err),
  10478                         .BADF => return error.NotOpenForWriting,
  10479                         .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called.
  10480                         .DQUOT => return error.DiskQuota,
  10481                         .FBIG => return error.FileTooBig,
  10482                         .IO => return error.InputOutput,
  10483                         .NOSPC => return error.NoSpaceLeft,
  10484                         .PERM => return error.PermissionDenied,
  10485                         .PIPE => return error.BrokenPipe,
  10486                         .NOTCAPABLE => return error.AccessDenied,
  10487                         .NXIO => return error.Unseekable,
  10488                         .SPIPE => return error.Unseekable,
  10489                         .OVERFLOW => return error.Unseekable,
  10490                         else => |err| return posix.unexpectedErrno(err),
  10491                     }
  10492                 },
  10493             }
  10494         }
  10495     }
  10496 
  10497     const syscall: Syscall = try .start();
  10498     while (true) {
  10499         const rc = pwritev_sym(file.handle, &iovecs, @intCast(iovlen), @bitCast(offset));
  10500         switch (posix.errno(rc)) {
  10501             .SUCCESS => {
  10502                 syscall.finish();
  10503                 return @intCast(rc);
  10504             },
  10505             .INTR => {
  10506                 try syscall.checkCancel();
  10507                 continue;
  10508             },
  10509             .INVAL => |err| return syscall.errnoBug(err),
  10510             .FAULT => |err| return syscall.errnoBug(err),
  10511             .DESTADDRREQ => |err| return syscall.errnoBug(err), // `connect` was never called.
  10512             .CONNRESET => |err| return syscall.errnoBug(err), // Not a socket handle.
  10513             .BADF => return syscall.fail(error.NotOpenForWriting),
  10514             .AGAIN => return syscall.fail(error.WouldBlock),
  10515             .DQUOT => return syscall.fail(error.DiskQuota),
  10516             .FBIG => return syscall.fail(error.FileTooBig),
  10517             .IO => return syscall.fail(error.InputOutput),
  10518             .NOSPC => return syscall.fail(error.NoSpaceLeft),
  10519             .PERM => return syscall.fail(error.PermissionDenied),
  10520             .PIPE => return syscall.fail(error.BrokenPipe),
  10521             .BUSY => return syscall.fail(error.DeviceBusy),
  10522             .TXTBSY => return syscall.fail(error.FileBusy),
  10523             .NXIO => return syscall.fail(error.Unseekable),
  10524             .SPIPE => return syscall.fail(error.Unseekable),
  10525             .OVERFLOW => return syscall.fail(error.Unseekable),
  10526             else => |err| return syscall.unexpectedErrno(err),
  10527         }
  10528     }
  10529 }
  10530 
  10531 fn writeFilePositionalWindows(file: File, buffer: []const u8, offset: u64) File.WritePositionalError!usize {
  10532     assert(buffer.len != 0);
  10533     var iosb: windows.IO_STATUS_BLOCK = undefined;
  10534     const short_buffer_len = std.math.lossyCast(u32, buffer.len);
  10535     const signed_offset: windows.LARGE_INTEGER = @intCast(offset);
  10536     if (file.flags.nonblocking) {
  10537         var done: bool = false;
  10538         switch (windows.ntdll.NtWriteFile(
  10539             file.handle,
  10540             null, // event
  10541             flagApc,
  10542             &done, // APC context
  10543             &iosb,
  10544             buffer.ptr,
  10545             short_buffer_len,
  10546             &signed_offset,
  10547             null, // key
  10548         )) {
  10549             // We must wait for the APC routine.
  10550             .PENDING, .SUCCESS => while (!done) {
  10551                 // Once we get here we must not return from the function until the
  10552                 // operation completes, thereby releasing reference to the iosb.
  10553                 const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
  10554                     error.Canceled => |e| {
  10555                         var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
  10556                         _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
  10557                         while (!done) waitForApcOrAlert();
  10558                         return e;
  10559                     },
  10560                 };
  10561                 waitForApcOrAlert();
  10562                 alertable_syscall.finish();
  10563             },
  10564             else => |status| iosb.u.Status = status,
  10565         }
  10566     } else {
  10567         const syscall: Syscall = try .start();
  10568         while (true) switch (windows.ntdll.NtWriteFile(
  10569             file.handle,
  10570             null, // event
  10571             null, // APC routine
  10572             null, // APC context
  10573             &iosb,
  10574             buffer.ptr,
  10575             short_buffer_len,
  10576             &signed_offset,
  10577             null, // key
  10578         )) {
  10579             .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
  10580             .CANCELLED => try syscall.checkCancel(),
  10581             else => |status| {
  10582                 syscall.finish();
  10583                 iosb.u.Status = status;
  10584                 return ntWriteFileResult(&iosb);
  10585             },
  10586         };
  10587     }
  10588     return ntWriteFileResult(&iosb);
  10589 }
  10590 
  10591 fn fileWriteStreaming(
  10592     userdata: ?*anyopaque,
  10593     file: File,
  10594     header: []const u8,
  10595     data: []const []const u8,
  10596     splat: usize,
  10597 ) File.Writer.Error!usize {
  10598     const t: *Threaded = @ptrCast(@alignCast(userdata));
  10599     _ = t;
  10600 
  10601     if (is_windows) {
  10602         const buffer = windowsWriteBuffer(header, data, splat);
  10603         if (buffer.len == 0) return 0;
  10604         return fileWriteStreamingWindows(file, buffer);
  10605     }
  10606 
  10607     var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
  10608     var iovlen: iovlen_t = 0;
  10609     addBuf(&iovecs, &iovlen, header);
  10610     for (data[0 .. data.len - 1]) |bytes| addBuf(&iovecs, &iovlen, bytes);
  10611     const pattern = data[data.len - 1];
  10612 
  10613     var splat_backup_buffer: [splat_buffer_size]u8 = undefined;
  10614     if (iovecs.len - iovlen != 0) switch (splat) {
  10615         0 => {},
  10616         1 => addBuf(&iovecs, &iovlen, pattern),
  10617         else => switch (pattern.len) {
  10618             0 => {},
  10619             1 => {
  10620                 const splat_buffer = &splat_backup_buffer;
  10621                 const memset_len = @min(splat_buffer.len, splat);
  10622                 const buf = splat_buffer[0..memset_len];
  10623                 @memset(buf, pattern[0]);
  10624                 addBuf(&iovecs, &iovlen, buf);
  10625                 var remaining_splat = splat - buf.len;
  10626                 while (remaining_splat > splat_buffer.len and iovecs.len - iovlen != 0) {
  10627                     assert(buf.len == splat_buffer.len);
  10628                     addBuf(&iovecs, &iovlen, splat_buffer);
  10629                     remaining_splat -= splat_buffer.len;
  10630                 }
  10631                 addBuf(&iovecs, &iovlen, splat_buffer[0..@min(remaining_splat, splat_buffer.len)]);
  10632             },
  10633             else => for (0..@min(splat, iovecs.len - iovlen)) |_| {
  10634                 addBuf(&iovecs, &iovlen, pattern);
  10635             },
  10636         },
  10637     };
  10638 
  10639     if (iovlen == 0) return 0;
  10640 
  10641     if (native_os == .wasi and !builtin.link_libc) {
  10642         var n_written: usize = undefined;
  10643         const syscall: Syscall = try .start();
  10644         while (true) {
  10645             switch (std.os.wasi.fd_write(file.handle, &iovecs, iovlen, &n_written)) {
  10646                 .SUCCESS => {
  10647                     syscall.finish();
  10648                     return n_written;
  10649                 },
  10650                 .INTR => {
  10651                     try syscall.checkCancel();
  10652                     continue;
  10653                 },
  10654                 else => |e| {
  10655                     syscall.finish();
  10656                     switch (e) {
  10657                         .INVAL => |err| return errnoBug(err),
  10658                         .FAULT => |err| return errnoBug(err),
  10659                         .AGAIN => |err| return errnoBug(err),
  10660                         .BADF => return error.NotOpenForWriting, // can be a race condition.
  10661                         .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called.
  10662                         .DQUOT => return error.DiskQuota,
  10663                         .FBIG => return error.FileTooBig,
  10664                         .IO => return error.InputOutput,
  10665                         .NOSPC => return error.NoSpaceLeft,
  10666                         .PERM => return error.PermissionDenied,
  10667                         .PIPE => return error.BrokenPipe,
  10668                         .NOTCAPABLE => return error.AccessDenied,
  10669                         else => |err| return posix.unexpectedErrno(err),
  10670                     }
  10671                 },
  10672             }
  10673         }
  10674     }
  10675 
  10676     const syscall: Syscall = try .start();
  10677     while (true) {
  10678         const rc = posix.system.writev(file.handle, &iovecs, @intCast(iovlen));
  10679         switch (posix.errno(rc)) {
  10680             .SUCCESS => {
  10681                 syscall.finish();
  10682                 return @intCast(rc);
  10683             },
  10684             .INTR => {
  10685                 try syscall.checkCancel();
  10686                 continue;
  10687             },
  10688             else => |e| {
  10689                 syscall.finish();
  10690                 switch (e) {
  10691                     .INVAL => |err| return errnoBug(err),
  10692                     .FAULT => |err| return errnoBug(err),
  10693                     .AGAIN => return error.WouldBlock,
  10694                     .BADF => return error.NotOpenForWriting, // Can be a race condition.
  10695                     .DESTADDRREQ => |err| return errnoBug(err), // `connect` was never called.
  10696                     .DQUOT => return error.DiskQuota,
  10697                     .FBIG => return error.FileTooBig,
  10698                     .IO => return error.InputOutput,
  10699                     .NOSPC => return error.NoSpaceLeft,
  10700                     .PERM => return error.PermissionDenied,
  10701                     .PIPE => return error.BrokenPipe,
  10702                     .CONNRESET => |err| return errnoBug(err), // Not a socket handle.
  10703                     .BUSY => return error.DeviceBusy,
  10704                     else => |err| return posix.unexpectedErrno(err),
  10705                 }
  10706             },
  10707         }
  10708     }
  10709 }
  10710 
  10711 fn fileWriteStreamingWindows(file: File, buffer: []const u8) File.Writer.Error!usize {
  10712     assert(buffer.len != 0);
  10713     var iosb: windows.IO_STATUS_BLOCK = undefined;
  10714     if (file.flags.nonblocking) {
  10715         var done: bool = false;
  10716         switch (windows.ntdll.NtWriteFile(
  10717             file.handle,
  10718             null, // event
  10719             flagApc,
  10720             &done, // APC context
  10721             &iosb,
  10722             buffer.ptr,
  10723             @intCast(buffer.len),
  10724             null, // byte offset
  10725             null, // key
  10726         )) {
  10727             // We must wait for the APC routine.
  10728             .PENDING, .SUCCESS => while (!done) {
  10729                 // Once we get here we must not return from the function until the
  10730                 // operation completes, thereby releasing reference to io_status_block.
  10731                 const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
  10732                     error.Canceled => |e| {
  10733                         var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
  10734                         _ = windows.ntdll.NtCancelIoFileEx(file.handle, &iosb, &cancel_iosb);
  10735                         while (!done) waitForApcOrAlert();
  10736                         return e;
  10737                     },
  10738                 };
  10739                 waitForApcOrAlert();
  10740                 alertable_syscall.finish();
  10741             },
  10742             else => |status| iosb.u.Status = status,
  10743         }
  10744     } else {
  10745         const syscall: Syscall = try .start();
  10746         while (true) switch (windows.ntdll.NtWriteFile(
  10747             file.handle,
  10748             null, // event
  10749             null, // APC routine
  10750             null, // APC context
  10751             &iosb,
  10752             buffer.ptr,
  10753             @intCast(buffer.len),
  10754             null, // byte offset
  10755             null, // key
  10756         )) {
  10757             .PENDING => unreachable, // unrecoverable: wrong File nonblocking flag
  10758             .CANCELLED => try syscall.checkCancel(),
  10759             else => |status| {
  10760                 syscall.finish();
  10761                 iosb.u.Status = status;
  10762                 break;
  10763             },
  10764         };
  10765     }
  10766     return ntWriteFileResult(&iosb);
  10767 }
  10768 
  10769 fn fileWriteFileStreaming(
  10770     userdata: ?*anyopaque,
  10771     file: File,
  10772     header: []const u8,
  10773     file_reader: *File.Reader,
  10774     limit: Io.Limit,
  10775 ) File.Writer.WriteFileError!usize {
  10776     const t: *Threaded = @ptrCast(@alignCast(userdata));
  10777     const reader_buffered = file_reader.interface.buffered();
  10778     if (reader_buffered.len >= @intFromEnum(limit)) {
  10779         const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
  10780         file_reader.interface.toss(n -| header.len);
  10781         return n;
  10782     }
  10783     const file_limit = @intFromEnum(limit) - reader_buffered.len;
  10784     const out_fd = file.handle;
  10785     const in_fd = file_reader.file.handle;
  10786 
  10787     if (file_reader.size) |size| {
  10788         if (size - file_reader.pos == 0) {
  10789             if (reader_buffered.len != 0) {
  10790                 const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
  10791                 file_reader.interface.toss(n -| header.len);
  10792                 return n;
  10793             } else {
  10794                 return error.EndOfStream;
  10795             }
  10796         }
  10797     }
  10798 
  10799     if (native_os == .freebsd) sf: {
  10800         // Try using sendfile on FreeBSD.
  10801         if (@atomicLoad(UseSendfile, &t.use_sendfile, .monotonic) == .disabled) break :sf;
  10802         const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf;
  10803         var hdtr_data: std.c.sf_hdtr = undefined;
  10804         var headers: [2]posix.iovec_const = undefined;
  10805         var headers_i: u8 = 0;
  10806         if (header.len != 0) {
  10807             headers[headers_i] = .{ .base = header.ptr, .len = header.len };
  10808             headers_i += 1;
  10809         }
  10810         if (reader_buffered.len != 0) {
  10811             headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len };
  10812             headers_i += 1;
  10813         }
  10814         const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: {
  10815             hdtr_data = .{
  10816                 .headers = &headers,
  10817                 .hdr_cnt = headers_i,
  10818                 .trailers = null,
  10819                 .trl_cnt = 0,
  10820             };
  10821             break :b &hdtr_data;
  10822         };
  10823         var sbytes: std.c.off_t = 0;
  10824         const nbytes: usize = @min(file_limit, std.math.maxInt(usize));
  10825         const flags = 0;
  10826 
  10827         const syscall: Syscall = try .start();
  10828         while (true) {
  10829             switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, nbytes, hdtr, &sbytes, flags))) {
  10830                 .SUCCESS => {
  10831                     syscall.finish();
  10832                     break;
  10833                 },
  10834                 .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
  10835                     // Give calling code chance to observe before trying
  10836                     // something else.
  10837                     syscall.finish();
  10838                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
  10839                     return 0;
  10840                 },
  10841                 .INTR, .BUSY => {
  10842                     if (sbytes == 0) {
  10843                         try syscall.checkCancel();
  10844                         continue;
  10845                     } else {
  10846                         // Even if we are being canceled, there have been side
  10847                         // effects, so it is better to report those side
  10848                         // effects to the caller.
  10849                         syscall.finish();
  10850                         break;
  10851                     }
  10852                 },
  10853                 .AGAIN => {
  10854                     syscall.finish();
  10855                     if (sbytes == 0) return error.WouldBlock;
  10856                     break;
  10857                 },
  10858                 else => |e| {
  10859                     syscall.finish();
  10860                     assert(error.Unexpected == switch (e) {
  10861                         .NOTCONN => return error.BrokenPipe,
  10862                         .IO => return error.InputOutput,
  10863                         .PIPE => return error.BrokenPipe,
  10864                         .NOBUFS => return error.SystemResources,
  10865                         .BADF => |err| errnoBug(err),
  10866                         .FAULT => |err| errnoBug(err),
  10867                         else => |err| posix.unexpectedErrno(err),
  10868                     });
  10869                     // Give calling code chance to observe the error before trying
  10870                     // something else.
  10871                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
  10872                     return 0;
  10873                 },
  10874             }
  10875         }
  10876         if (sbytes == 0) {
  10877             file_reader.size = file_reader.pos;
  10878             return error.EndOfStream;
  10879         }
  10880         const ubytes: usize = @intCast(sbytes);
  10881         file_reader.interface.toss(ubytes -| header.len);
  10882         return ubytes;
  10883     }
  10884 
  10885     if (is_darwin) sf: {
  10886         // Try using sendfile on macOS.
  10887         if (@atomicLoad(UseSendfile, &t.use_sendfile, .monotonic) == .disabled) break :sf;
  10888         const offset = std.math.cast(std.c.off_t, file_reader.pos) orelse break :sf;
  10889         var hdtr_data: std.c.sf_hdtr = undefined;
  10890         var headers: [2]posix.iovec_const = undefined;
  10891         var headers_i: u8 = 0;
  10892         if (header.len != 0) {
  10893             headers[headers_i] = .{ .base = header.ptr, .len = header.len };
  10894             headers_i += 1;
  10895         }
  10896         if (reader_buffered.len != 0) {
  10897             headers[headers_i] = .{ .base = reader_buffered.ptr, .len = reader_buffered.len };
  10898             headers_i += 1;
  10899         }
  10900         const hdtr: ?*std.c.sf_hdtr = if (headers_i == 0) null else b: {
  10901             hdtr_data = .{
  10902                 .headers = &headers,
  10903                 .hdr_cnt = headers_i,
  10904                 .trailers = null,
  10905                 .trl_cnt = 0,
  10906             };
  10907             break :b &hdtr_data;
  10908         };
  10909         const max_count = std.math.maxInt(i32); // Avoid EINVAL.
  10910         var len: std.c.off_t = @min(file_limit, max_count);
  10911         const flags = 0;
  10912         const syscall: Syscall = try .start();
  10913         while (true) {
  10914             switch (posix.errno(std.c.sendfile(in_fd, out_fd, offset, &len, hdtr, flags))) {
  10915                 .SUCCESS => {
  10916                     syscall.finish();
  10917                     break;
  10918                 },
  10919                 .OPNOTSUPP, .NOTSOCK, .NOSYS => {
  10920                     // Give calling code chance to observe before trying
  10921                     // something else.
  10922                     syscall.finish();
  10923                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
  10924                     return 0;
  10925                 },
  10926                 .INTR => {
  10927                     if (len == 0) {
  10928                         try syscall.checkCancel();
  10929                         continue;
  10930                     } else {
  10931                         // Even if we are being canceled, there have been side
  10932                         // effects, so it is better to report those side
  10933                         // effects to the caller.
  10934                         syscall.finish();
  10935                         break;
  10936                     }
  10937                 },
  10938                 .AGAIN => {
  10939                     syscall.finish();
  10940                     if (len == 0) return error.WouldBlock;
  10941                     break;
  10942                 },
  10943                 else => |e| {
  10944                     syscall.finish();
  10945                     assert(error.Unexpected == switch (e) {
  10946                         .NOTCONN => return error.BrokenPipe,
  10947                         .IO => return error.InputOutput,
  10948                         .PIPE => return error.BrokenPipe,
  10949                         .BADF => |err| errnoBug(err),
  10950                         .FAULT => |err| errnoBug(err),
  10951                         .INVAL => |err| errnoBug(err),
  10952                         else => |err| posix.unexpectedErrno(err),
  10953                     });
  10954                     // Give calling code chance to observe the error before trying
  10955                     // something else.
  10956                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
  10957                     return 0;
  10958                 },
  10959             }
  10960         }
  10961         if (len == 0) {
  10962             file_reader.size = file_reader.pos;
  10963             return error.EndOfStream;
  10964         }
  10965         const u_len: usize = @bitCast(len);
  10966         file_reader.interface.toss(u_len -| header.len);
  10967         return u_len;
  10968     }
  10969 
  10970     if (native_os == .linux) sf: {
  10971         // Try using sendfile on Linux.
  10972         if (@atomicLoad(UseSendfile, &t.use_sendfile, .monotonic) == .disabled) break :sf;
  10973         // Linux sendfile does not support headers.
  10974         if (header.len != 0 or reader_buffered.len != 0) {
  10975             const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
  10976             file_reader.interface.toss(n -| header.len);
  10977             return n;
  10978         }
  10979         const max_count = 0x7ffff000; // Avoid EINVAL.
  10980         var off: std.os.linux.off_t = undefined;
  10981         const off_ptr: ?*std.os.linux.off_t, const count: usize = switch (file_reader.mode) {
  10982             .positional => o: {
  10983                 const size = file_reader.getSize() catch return 0;
  10984                 off = std.math.cast(std.os.linux.off_t, file_reader.pos) orelse return error.ReadFailed;
  10985                 break :o .{ &off, @min(@intFromEnum(limit), size - file_reader.pos, max_count) };
  10986             },
  10987             .streaming => .{ null, limit.minInt(max_count) },
  10988             .streaming_simple, .positional_simple => break :sf,
  10989             .failure => return error.ReadFailed,
  10990         };
  10991         const syscall: Syscall = try .start();
  10992         const n: usize = while (true) {
  10993             const rc = sendfile_sym(out_fd, in_fd, off_ptr, count);
  10994             switch (posix.errno(rc)) {
  10995                 .SUCCESS => {
  10996                     syscall.finish();
  10997                     break @intCast(rc);
  10998                 },
  10999                 .NOSYS, .INVAL => {
  11000                     // Give calling code chance to observe before trying
  11001                     // something else.
  11002                     syscall.finish();
  11003                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
  11004                     return 0;
  11005                 },
  11006                 .INTR => {
  11007                     try syscall.checkCancel();
  11008                     continue;
  11009                 },
  11010                 else => |e| {
  11011                     syscall.finish();
  11012                     assert(error.Unexpected == switch (e) {
  11013                         .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
  11014                         .AGAIN => return error.WouldBlock,
  11015                         .IO => return error.InputOutput,
  11016                         .PIPE => return error.BrokenPipe,
  11017                         .NOMEM => return error.SystemResources,
  11018                         .NXIO, .SPIPE => {
  11019                             file_reader.mode = file_reader.mode.toStreaming();
  11020                             const pos = file_reader.pos;
  11021                             if (pos != 0) {
  11022                                 file_reader.pos = 0;
  11023                                 file_reader.seekBy(@intCast(pos)) catch {
  11024                                     file_reader.mode = .failure;
  11025                                     return error.ReadFailed;
  11026                                 };
  11027                             }
  11028                             return 0;
  11029                         },
  11030                         .BADF => |err| errnoBug(err), // Always a race condition.
  11031                         .FAULT => |err| errnoBug(err), // Segmentation fault.
  11032                         .OVERFLOW => |err| errnoBug(err), // We avoid passing too large of a `count`.
  11033                         else => |err| posix.unexpectedErrno(err),
  11034                     });
  11035                     // Give calling code chance to observe the error before trying
  11036                     // something else.
  11037                     @atomicStore(UseSendfile, &t.use_sendfile, .disabled, .monotonic);
  11038                     return 0;
  11039                 },
  11040             }
  11041         };
  11042         if (n == 0) {
  11043             file_reader.size = file_reader.pos;
  11044             return error.EndOfStream;
  11045         }
  11046         file_reader.pos += n;
  11047         return n;
  11048     }
  11049 
  11050     if (have_copy_file_range) cfr: {
  11051         if (@atomicLoad(UseCopyFileRange, &t.use_copy_file_range, .monotonic) == .disabled) break :cfr;
  11052         if (header.len != 0 or reader_buffered.len != 0) {
  11053             const n = try fileWriteStreaming(t, file, header, &.{limit.slice(reader_buffered)}, 1);
  11054             file_reader.interface.toss(n -| header.len);
  11055             return n;
  11056         }
  11057         var len: usize = @intFromEnum(limit);
  11058         var off_in: i64 = undefined;
  11059         const off_in_ptr: ?*i64 = switch (file_reader.mode) {
  11060             .positional_simple, .streaming_simple => return error.Unimplemented,
  11061             .positional => p: {
  11062                 len = @min(len, std.math.maxInt(usize) - file_reader.pos);
  11063                 off_in = @intCast(file_reader.pos);
  11064                 break :p &off_in;
  11065             },
  11066             .streaming => null,
  11067             .failure => return error.ReadFailed,
  11068         };
  11069         const n: usize = switch (native_os) {
  11070             .linux => n: {
  11071                 const syscall: Syscall = try .start();
  11072                 while (true) {
  11073                     const rc = linux_copy_file_range_sys.copy_file_range(in_fd, off_in_ptr, out_fd, null, len, 0);
  11074                     switch (linux_copy_file_range_sys.errno(rc)) {
  11075                         .SUCCESS => {
  11076                             syscall.finish();
  11077                             break :n @intCast(rc);
  11078                         },
  11079                         .INTR => {
  11080                             try syscall.checkCancel();
  11081                             continue;
  11082                         },
  11083                         .OPNOTSUPP, .INVAL, .NOSYS => {
  11084                             // Give calling code chance to observe before trying
  11085                             // something else.
  11086                             syscall.finish();
  11087                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
  11088                             return 0;
  11089                         },
  11090                         else => |e| {
  11091                             syscall.finish();
  11092                             assert(error.Unexpected == switch (e) {
  11093                                 .FBIG => return error.FileTooBig,
  11094                                 .IO => return error.InputOutput,
  11095                                 .NOMEM => return error.SystemResources,
  11096                                 .NOSPC => return error.NoSpaceLeft,
  11097                                 .OVERFLOW => |err| errnoBug(err), // We avoid passing too large a count.
  11098                                 .PERM => return error.PermissionDenied,
  11099                                 .BUSY => return error.DeviceBusy,
  11100                                 .TXTBSY => return error.FileBusy,
  11101                                 // copy_file_range can still work but not on
  11102                                 // this pair of file descriptors.
  11103                                 .XDEV => return error.Unimplemented,
  11104                                 .ISDIR => |err| errnoBug(err),
  11105                                 .BADF => |err| errnoBug(err),
  11106                                 else => |err| posix.unexpectedErrno(err),
  11107                             });
  11108                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
  11109                             return 0;
  11110                         },
  11111                     }
  11112                 }
  11113             },
  11114             .freebsd => n: {
  11115                 const syscall: Syscall = try .start();
  11116                 while (true) {
  11117                     const rc = std.c.copy_file_range(in_fd, off_in_ptr, out_fd, null, @intFromEnum(limit), 0);
  11118                     switch (std.c.errno(rc)) {
  11119                         .SUCCESS => {
  11120                             syscall.finish();
  11121                             break :n @intCast(rc);
  11122                         },
  11123                         .INTR => {
  11124                             try syscall.checkCancel();
  11125                             continue;
  11126                         },
  11127                         .OPNOTSUPP, .INVAL, .NOSYS => {
  11128                             // Give calling code chance to observe before trying
  11129                             // something else.
  11130                             syscall.finish();
  11131                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
  11132                             return 0;
  11133                         },
  11134                         else => |e| {
  11135                             syscall.finish();
  11136                             assert(error.Unexpected == switch (e) {
  11137                                 .FBIG => return error.FileTooBig,
  11138                                 .IO => return error.InputOutput,
  11139                                 .INTEGRITY => return error.CorruptedData,
  11140                                 .NOSPC => return error.NoSpaceLeft,
  11141                                 .ISDIR => |err| errnoBug(err),
  11142                                 .BADF => |err| errnoBug(err),
  11143                                 else => |err| posix.unexpectedErrno(err),
  11144                             });
  11145                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
  11146                             return 0;
  11147                         },
  11148                     }
  11149                 }
  11150             },
  11151             else => comptime unreachable,
  11152         };
  11153         if (n == 0) {
  11154             file_reader.size = file_reader.pos;
  11155             return error.EndOfStream;
  11156         }
  11157         file_reader.pos += n;
  11158         return n;
  11159     }
  11160 
  11161     return error.Unimplemented;
  11162 }
  11163 
  11164 fn netWriteFile(
  11165     userdata: ?*anyopaque,
  11166     socket_handle: net.Socket.Handle,
  11167     header: []const u8,
  11168     file_reader: *File.Reader,
  11169     limit: Io.Limit,
  11170 ) net.Stream.Writer.WriteFileError!usize {
  11171     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11172     _ = t;
  11173     _ = socket_handle;
  11174     _ = header;
  11175     _ = file_reader;
  11176     _ = limit;
  11177     @panic("TODO implement netWriteFile");
  11178 }
  11179 
  11180 fn netWriteFileUnavailable(
  11181     userdata: ?*anyopaque,
  11182     socket_handle: net.Socket.Handle,
  11183     header: []const u8,
  11184     file_reader: *File.Reader,
  11185     limit: Io.Limit,
  11186 ) net.Stream.Writer.WriteFileError!usize {
  11187     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11188     _ = t;
  11189     _ = socket_handle;
  11190     _ = header;
  11191     _ = file_reader;
  11192     _ = limit;
  11193     return error.NetworkDown;
  11194 }
  11195 
  11196 fn fileWriteFilePositional(
  11197     userdata: ?*anyopaque,
  11198     file: File,
  11199     header: []const u8,
  11200     file_reader: *File.Reader,
  11201     limit: Io.Limit,
  11202     offset: u64,
  11203 ) File.WriteFilePositionalError!usize {
  11204     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11205     const reader_buffered = file_reader.interface.buffered();
  11206     if (reader_buffered.len >= @intFromEnum(limit)) {
  11207         const n = try fileWritePositional(t, file, header, &.{limit.slice(reader_buffered)}, 1, offset);
  11208         file_reader.interface.toss(n -| header.len);
  11209         return n;
  11210     }
  11211     const out_fd = file.handle;
  11212     const in_fd = file_reader.file.handle;
  11213 
  11214     if (file_reader.size) |size| {
  11215         if (size - file_reader.pos == 0) {
  11216             if (reader_buffered.len != 0) {
  11217                 const n = try fileWritePositional(t, file, header, &.{limit.slice(reader_buffered)}, 1, offset);
  11218                 file_reader.interface.toss(n -| header.len);
  11219                 return n;
  11220             } else {
  11221                 return error.EndOfStream;
  11222             }
  11223         }
  11224     }
  11225 
  11226     if (have_copy_file_range) cfr: {
  11227         if (@atomicLoad(UseCopyFileRange, &t.use_copy_file_range, .monotonic) == .disabled) break :cfr;
  11228         if (header.len != 0 or reader_buffered.len != 0) {
  11229             const n = try fileWritePositional(t, file, header, &.{limit.slice(reader_buffered)}, 1, offset);
  11230             file_reader.interface.toss(n -| header.len);
  11231             return n;
  11232         }
  11233         var len: usize = @min(@intFromEnum(limit), std.math.maxInt(usize) - offset);
  11234         var off_in: i64 = undefined;
  11235         const off_in_ptr: ?*i64 = switch (file_reader.mode) {
  11236             .positional_simple, .streaming_simple => return error.Unimplemented,
  11237             .positional => p: {
  11238                 len = @min(len, std.math.maxInt(usize) - file_reader.pos);
  11239                 off_in = @intCast(file_reader.pos);
  11240                 break :p &off_in;
  11241             },
  11242             .streaming => null,
  11243             .failure => return error.ReadFailed,
  11244         };
  11245         var off_out: i64 = @intCast(offset);
  11246         const n: usize = switch (native_os) {
  11247             .linux => n: {
  11248                 const syscall: Syscall = try .start();
  11249                 while (true) {
  11250                     const rc = linux_copy_file_range_sys.copy_file_range(in_fd, off_in_ptr, out_fd, &off_out, len, 0);
  11251                     switch (linux_copy_file_range_sys.errno(rc)) {
  11252                         .SUCCESS => {
  11253                             syscall.finish();
  11254                             break :n @intCast(rc);
  11255                         },
  11256                         .INTR => {
  11257                             try syscall.checkCancel();
  11258                             continue;
  11259                         },
  11260                         .OPNOTSUPP, .INVAL, .NOSYS => {
  11261                             // Give calling code chance to observe before trying
  11262                             // something else.
  11263                             syscall.finish();
  11264                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
  11265                             return 0;
  11266                         },
  11267                         else => |e| {
  11268                             syscall.finish();
  11269                             assert(error.Unexpected == switch (e) {
  11270                                 .FBIG => return error.FileTooBig,
  11271                                 .IO => return error.InputOutput,
  11272                                 .NOMEM => return error.SystemResources,
  11273                                 .NOSPC => return error.NoSpaceLeft,
  11274                                 .OVERFLOW => |err| errnoBug(err), // We avoid passing too large a count.
  11275                                 .NXIO => return error.Unseekable,
  11276                                 .SPIPE => return error.Unseekable,
  11277                                 .PERM => return error.PermissionDenied,
  11278                                 .TXTBSY => return error.FileBusy,
  11279                                 // copy_file_range can still work but not on
  11280                                 // this pair of file descriptors.
  11281                                 .XDEV => return error.Unimplemented,
  11282                                 .ISDIR => |err| errnoBug(err),
  11283                                 .BADF => |err| errnoBug(err),
  11284                                 else => |err| posix.unexpectedErrno(err),
  11285                             });
  11286                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
  11287                             return 0;
  11288                         },
  11289                     }
  11290                 }
  11291             },
  11292             .freebsd => n: {
  11293                 const syscall: Syscall = try .start();
  11294                 while (true) {
  11295                     const rc = std.c.copy_file_range(in_fd, off_in_ptr, out_fd, &off_out, @intFromEnum(limit), 0);
  11296                     switch (std.c.errno(rc)) {
  11297                         .SUCCESS => {
  11298                             syscall.finish();
  11299                             break :n @intCast(rc);
  11300                         },
  11301                         .INTR => {
  11302                             try syscall.checkCancel();
  11303                             continue;
  11304                         },
  11305                         .OPNOTSUPP, .INVAL, .NOSYS => {
  11306                             // Give calling code chance to observe before trying
  11307                             // something else.
  11308                             syscall.finish();
  11309                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
  11310                             return 0;
  11311                         },
  11312                         else => |e| {
  11313                             syscall.finish();
  11314                             assert(error.Unexpected == switch (e) {
  11315                                 .FBIG => return error.FileTooBig,
  11316                                 .IO => return error.InputOutput,
  11317                                 .INTEGRITY => return error.CorruptedData,
  11318                                 .NOSPC => return error.NoSpaceLeft,
  11319                                 .OVERFLOW => return error.Unseekable,
  11320                                 .NXIO => return error.Unseekable,
  11321                                 .SPIPE => return error.Unseekable,
  11322                                 .ISDIR => |err| errnoBug(err),
  11323                                 .BADF => |err| errnoBug(err),
  11324                                 else => |err| posix.unexpectedErrno(err),
  11325                             });
  11326                             @atomicStore(UseCopyFileRange, &t.use_copy_file_range, .disabled, .monotonic);
  11327                             return 0;
  11328                         },
  11329                     }
  11330                 }
  11331             },
  11332             else => comptime unreachable,
  11333         };
  11334         if (n == 0) {
  11335             file_reader.size = file_reader.pos;
  11336             return error.EndOfStream;
  11337         }
  11338         file_reader.pos += n;
  11339         return n;
  11340     }
  11341 
  11342     if (is_darwin) fcf: {
  11343         if (@atomicLoad(UseFcopyfile, &t.use_fcopyfile, .monotonic) == .disabled) break :fcf;
  11344         if (file_reader.pos != 0) break :fcf;
  11345         if (offset != 0) break :fcf;
  11346         if (limit != .unlimited) break :fcf;
  11347         const size = file_reader.getSize() catch break :fcf;
  11348         if (header.len != 0 or reader_buffered.len != 0) {
  11349             const n = try fileWritePositional(t, file, header, &.{limit.slice(reader_buffered)}, 1, offset);
  11350             file_reader.interface.toss(n -| header.len);
  11351             return n;
  11352         }
  11353         const syscall: Syscall = try .start();
  11354         while (true) {
  11355             const rc = std.c.fcopyfile(in_fd, out_fd, null, .{ .DATA = true });
  11356             switch (posix.errno(rc)) {
  11357                 .SUCCESS => {
  11358                     syscall.finish();
  11359                     break;
  11360                 },
  11361                 .INTR => {
  11362                     try syscall.checkCancel();
  11363                     continue;
  11364                 },
  11365                 .OPNOTSUPP => {
  11366                     // Give calling code chance to observe before trying
  11367                     // something else.
  11368                     syscall.finish();
  11369                     @atomicStore(UseFcopyfile, &t.use_fcopyfile, .disabled, .monotonic);
  11370                     return 0;
  11371                 },
  11372                 else => |e| {
  11373                     syscall.finish();
  11374                     assert(error.Unexpected == switch (e) {
  11375                         .NOMEM => return error.SystemResources,
  11376                         .INVAL => |err| errnoBug(err),
  11377                         else => |err| posix.unexpectedErrno(err),
  11378                     });
  11379                     return 0;
  11380                 },
  11381             }
  11382         }
  11383         file_reader.pos = size;
  11384         return size;
  11385     }
  11386 
  11387     return error.Unimplemented;
  11388 }
  11389 
  11390 fn nowPosix(clock: Io.Clock) Io.Timestamp {
  11391     const clock_id: posix.clockid_t = clockToPosix(clock);
  11392     var timespec: posix.timespec = undefined;
  11393     switch (posix.errno(posix.system.clock_gettime(clock_id, &timespec))) {
  11394         .SUCCESS => return timestampFromPosix(&timespec),
  11395         else => return .zero,
  11396     }
  11397 }
  11398 
  11399 fn now(userdata: ?*anyopaque, clock: Io.Clock) Io.Timestamp {
  11400     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11401     _ = t;
  11402     return switch (native_os) {
  11403         .windows => nowWindows(clock),
  11404         .wasi => nowWasi(clock),
  11405         else => nowPosix(clock),
  11406     };
  11407 }
  11408 
  11409 fn clockResolution(userdata: ?*anyopaque, clock: Io.Clock) Io.Clock.ResolutionError!Io.Duration {
  11410     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11411     _ = t;
  11412     return switch (native_os) {
  11413         .windows => switch (clock) {
  11414             .awake, .boot, .real => {
  11415                 // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA
  11416                 // (a read-only page of info updated and mapped by the kernel to all processes):
  11417                 // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
  11418                 // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
  11419                 var qpf: windows.LARGE_INTEGER = undefined;
  11420                 if (windows.ntdll.RtlQueryPerformanceFrequency(&qpf) != 0) {
  11421                     recoverableOsBugDetected();
  11422                     return .zero;
  11423                 }
  11424                 // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
  11425                 // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
  11426                 const common_qpf = 10_000_000;
  11427                 if (qpf == common_qpf) return .fromNanoseconds(std.time.ns_per_s / common_qpf);
  11428 
  11429                 // Convert to ns using fixed point.
  11430                 const scale = @as(u64, std.time.ns_per_s << 32) / @as(u32, @intCast(qpf));
  11431                 const result = scale >> 32;
  11432                 return .fromNanoseconds(result);
  11433             },
  11434             .cpu_process, .cpu_thread => return error.ClockUnavailable,
  11435         },
  11436         .wasi => {
  11437             if (builtin.link_libc) return clockResolutionPosix(clock);
  11438             var ns: std.os.wasi.timestamp_t = undefined;
  11439             return switch (std.os.wasi.clock_res_get(clockToWasi(clock), &ns)) {
  11440                 .SUCCESS => .fromNanoseconds(ns),
  11441                 .INVAL => return error.ClockUnavailable,
  11442                 else => |err| return posix.unexpectedErrno(err),
  11443             };
  11444         },
  11445         else => return clockResolutionPosix(clock),
  11446     };
  11447 }
  11448 
  11449 fn clockResolutionPosix(clock: Io.Clock) Io.Clock.ResolutionError!Io.Duration {
  11450     const clock_id: posix.clockid_t = clockToPosix(clock);
  11451     var timespec: posix.timespec = undefined;
  11452     return switch (posix.errno(posix.system.clock_getres(clock_id, &timespec))) {
  11453         .SUCCESS => .fromNanoseconds(nanosecondsFromPosix(&timespec)),
  11454         .INVAL => return error.ClockUnavailable,
  11455         else => |err| return posix.unexpectedErrno(err),
  11456     };
  11457 }
  11458 
  11459 fn nowWindows(clock: Io.Clock) Io.Timestamp {
  11460     switch (clock) {
  11461         .real => {
  11462             // RtlGetSystemTimePrecise() has a granularity of 100 nanoseconds
  11463             // and uses the NTFS/Windows epoch, which is 1601-01-01.
  11464             const epoch_ns = std.time.epoch.windows * std.time.ns_per_s;
  11465             return .{ .nanoseconds = @as(i96, windows.ntdll.RtlGetSystemTimePrecise()) * 100 + epoch_ns };
  11466         },
  11467         .awake, .boot => {
  11468             // We don't need to cache QPF as it's internally just a memory read to KUSER_SHARED_DATA
  11469             // (a read-only page of info updated and mapped by the kernel to all processes):
  11470             // https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
  11471             // https://www.geoffchappell.com/studies/windows/km/ntoskrnl/inc/api/ntexapi_x/kuser_shared_data/index.htm
  11472             const qpf: u64 = qpf: {
  11473                 var qpf: windows.LARGE_INTEGER = undefined;
  11474                 assert(windows.ntdll.RtlQueryPerformanceFrequency(&qpf) != windows.FALSE);
  11475                 break :qpf @bitCast(qpf);
  11476             };
  11477 
  11478             // QPC on windows doesn't fail on >= XP/2000 and includes time suspended.
  11479             const qpc: u64 = qpc: {
  11480                 var qpc: windows.LARGE_INTEGER = undefined;
  11481                 assert(windows.ntdll.RtlQueryPerformanceCounter(&qpc) != windows.FALSE);
  11482                 break :qpc @bitCast(qpc);
  11483             };
  11484 
  11485             // 10Mhz (1 qpc tick every 100ns) is a common enough QPF value that we can optimize on it.
  11486             // https://github.com/microsoft/STL/blob/785143a0c73f030238ef618890fd4d6ae2b3a3a0/stl/inc/chrono#L694-L701
  11487             const common_qpf = 10_000_000;
  11488             if (qpf == common_qpf) return .{ .nanoseconds = qpc * (std.time.ns_per_s / common_qpf) };
  11489 
  11490             // Convert to ns using fixed point.
  11491             const scale = @as(u64, std.time.ns_per_s << 32) / @as(u32, @intCast(qpf));
  11492             const result = (@as(u96, qpc) * scale) >> 32;
  11493             return .{ .nanoseconds = @intCast(result) };
  11494         },
  11495         .cpu_process => {
  11496             const handle = windows.GetCurrentProcess();
  11497             var times: windows.KERNEL_USER_TIMES = undefined;
  11498 
  11499             // https://github.com/reactos/reactos/blob/master/ntoskrnl/ps/query.c#L442-L485
  11500             if (windows.ntdll.NtQueryInformationProcess(
  11501                 handle,
  11502                 .Times,
  11503                 &times,
  11504                 @sizeOf(windows.KERNEL_USER_TIMES),
  11505                 null,
  11506             ) != .SUCCESS) return .zero;
  11507 
  11508             const sum = @as(i96, times.UserTime) + @as(i96, times.KernelTime);
  11509             return .{ .nanoseconds = sum * 100 };
  11510         },
  11511         .cpu_thread => {
  11512             const handle = windows.GetCurrentThread();
  11513             var times: windows.KERNEL_USER_TIMES = undefined;
  11514 
  11515             // https://github.com/reactos/reactos/blob/master/ntoskrnl/ps/query.c#L2971-L3019
  11516             if (windows.ntdll.NtQueryInformationThread(
  11517                 handle,
  11518                 .Times,
  11519                 &times,
  11520                 @sizeOf(windows.KERNEL_USER_TIMES),
  11521                 null,
  11522             ) != .SUCCESS) return .zero;
  11523 
  11524             const sum = @as(i96, times.UserTime) + @as(i96, times.KernelTime);
  11525             return .{ .nanoseconds = sum * 100 };
  11526         },
  11527     }
  11528 }
  11529 
  11530 fn nowWasi(clock: Io.Clock) Io.Timestamp {
  11531     var ns: std.os.wasi.timestamp_t = undefined;
  11532     const err = std.os.wasi.clock_time_get(clockToWasi(clock), 1, &ns);
  11533     if (err != .SUCCESS) return .zero;
  11534     return .fromNanoseconds(ns);
  11535 }
  11536 
  11537 fn sleep(userdata: ?*anyopaque, timeout: Io.Timeout) Io.Cancelable!void {
  11538     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11539     if (timeout == .none) return;
  11540     if (use_parking_sleep) return parking_sleep.sleep(timeout);
  11541     if (native_os == .wasi) return sleepWasi(t, timeout);
  11542     if (@TypeOf(posix.system.clock_nanosleep) != void) return sleepPosix(timeout);
  11543     return sleepNanosleep(t, timeout);
  11544 }
  11545 
  11546 fn sleepPosix(timeout: Io.Timeout) Io.Cancelable!void {
  11547     const clock_id: posix.clockid_t = clockToPosix(switch (timeout) {
  11548         .none => .awake,
  11549         .duration => |d| d.clock,
  11550         .deadline => |d| d.clock,
  11551     });
  11552     const deadline_nanoseconds: i96 = switch (timeout) {
  11553         .none => std.math.maxInt(i96),
  11554         .duration => |duration| duration.raw.nanoseconds,
  11555         .deadline => |deadline| deadline.raw.nanoseconds,
  11556     };
  11557     var timespec: posix.timespec = timestampToPosix(deadline_nanoseconds);
  11558     const syscall: Syscall = try .start();
  11559     while (true) {
  11560         const rc = posix.system.clock_nanosleep(clock_id, .{ .ABSTIME = switch (timeout) {
  11561             .none, .duration => false,
  11562             .deadline => true,
  11563         } }, &timespec, &timespec);
  11564         // POSIX-standard libc clock_nanosleep() returns *positive* errno values directly
  11565         switch (if (builtin.link_libc) @as(posix.E, @enumFromInt(rc)) else posix.errno(rc)) {
  11566             .INTR => {
  11567                 try syscall.checkCancel();
  11568                 continue;
  11569             },
  11570             // Handles SUCCESS as well as clock not available and unexpected
  11571             // errors. The user had a chance to check clock resolution before
  11572             // getting here, which would have reported 0, making this a legal
  11573             // amount of time to sleep.
  11574             else => {
  11575                 syscall.finish();
  11576                 return;
  11577             },
  11578         }
  11579     }
  11580 }
  11581 
  11582 fn sleepWasi(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void {
  11583     const t_io = io(t);
  11584     const w = std.os.wasi;
  11585 
  11586     const clock: w.subscription_clock_t = if (timeout.toDurationFromNow(t_io)) |d| .{
  11587         .id = clockToWasi(d.clock),
  11588         .timeout = std.math.lossyCast(u64, d.raw.nanoseconds),
  11589         .precision = 0,
  11590         .flags = 0,
  11591     } else .{
  11592         .id = .MONOTONIC,
  11593         .timeout = std.math.maxInt(u64),
  11594         .precision = 0,
  11595         .flags = 0,
  11596     };
  11597     const in: w.subscription_t = .{
  11598         .userdata = 0,
  11599         .u = .{
  11600             .tag = .CLOCK,
  11601             .u = .{ .clock = clock },
  11602         },
  11603     };
  11604     var event: w.event_t = undefined;
  11605     var nevents: usize = undefined;
  11606     const syscall: Syscall = try .start();
  11607     _ = w.poll_oneoff(&in, &event, 1, &nevents);
  11608     syscall.finish();
  11609 }
  11610 
  11611 fn sleepNanosleep(t: *Threaded, timeout: Io.Timeout) Io.Cancelable!void {
  11612     const t_io = io(t);
  11613     const sec_type = @typeInfo(posix.timespec).@"struct".fields[0].type;
  11614     const nsec_type = @typeInfo(posix.timespec).@"struct".fields[1].type;
  11615 
  11616     var timespec: posix.timespec = t: {
  11617         const d = timeout.toDurationFromNow(t_io) orelse break :t .{
  11618             .sec = std.math.maxInt(sec_type),
  11619             .nsec = std.math.maxInt(nsec_type),
  11620         };
  11621         break :t timestampToPosix(d.raw.toNanoseconds());
  11622     };
  11623     const syscall: Syscall = try .start();
  11624     while (true) {
  11625         switch (posix.errno(posix.system.nanosleep(&timespec, &timespec))) {
  11626             .INTR => {
  11627                 try syscall.checkCancel();
  11628                 continue;
  11629             },
  11630             // This prong handles success as well as unexpected errors.
  11631             else => return syscall.finish(),
  11632         }
  11633     }
  11634 }
  11635 
  11636 fn netListenIpPosix(
  11637     userdata: ?*anyopaque,
  11638     address: IpAddress,
  11639     options: IpAddress.ListenOptions,
  11640 ) IpAddress.ListenError!net.Server {
  11641     if (!have_networking) return error.NetworkDown;
  11642     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11643     _ = t;
  11644     const family = posixAddressFamily(&address);
  11645     const socket_fd = try openSocketPosix(family, .{
  11646         .mode = options.mode,
  11647         .protocol = options.protocol,
  11648     });
  11649     errdefer closeFd(socket_fd);
  11650 
  11651     if (options.reuse_address) {
  11652         try setSocketOption(socket_fd, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1);
  11653         if (@hasDecl(posix.SO, "REUSEPORT"))
  11654             try setSocketOption(socket_fd, posix.SOL.SOCKET, posix.SO.REUSEPORT, 1);
  11655     }
  11656 
  11657     var storage: PosixAddress = undefined;
  11658     var addr_len = addressToPosix(&address, &storage);
  11659     try posixBind(socket_fd, &storage.any, addr_len);
  11660 
  11661     const syscall: Syscall = try .start();
  11662     while (true) {
  11663         switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) {
  11664             .SUCCESS => {
  11665                 syscall.finish();
  11666                 break;
  11667             },
  11668             .INTR => {
  11669                 try syscall.checkCancel();
  11670                 continue;
  11671             },
  11672             .ADDRINUSE => return syscall.fail(error.AddressInUse),
  11673             .BADF => |err| return syscall.errnoBug(err), // File descriptor used after closed.
  11674             else => |err| return syscall.unexpectedErrno(err),
  11675         }
  11676     }
  11677 
  11678     try posixGetSockName(socket_fd, &storage.any, &addr_len);
  11679     return .{
  11680         .socket = .{
  11681             .handle = socket_fd,
  11682             .address = addressFromPosix(&storage),
  11683         },
  11684     };
  11685 }
  11686 
  11687 fn netListenIpWindows(
  11688     userdata: ?*anyopaque,
  11689     address: IpAddress,
  11690     options: IpAddress.ListenOptions,
  11691 ) IpAddress.ListenError!net.Server {
  11692     if (!have_networking) return error.NetworkDown;
  11693     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11694     const family = posixAddressFamily(&address);
  11695     const socket_handle = try openSocketWsa(t, family, .{
  11696         .mode = options.mode,
  11697         .protocol = options.protocol,
  11698     });
  11699     errdefer closeSocketWindows(socket_handle);
  11700 
  11701     if (options.reuse_address)
  11702         try setSocketOptionWsa(t, socket_handle, posix.SOL.SOCKET, posix.SO.REUSEADDR, 1);
  11703 
  11704     var storage: WsaAddress = undefined;
  11705     var addr_len = addressToWsa(&address, &storage);
  11706 
  11707     var syscall: AlertableSyscall = try .start();
  11708     while (true) {
  11709         const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
  11710         if (rc != ws2_32.SOCKET_ERROR) {
  11711             syscall.finish();
  11712             break;
  11713         }
  11714         switch (ws2_32.WSAGetLastError()) {
  11715             .NOTINITIALISED => {
  11716                 syscall.finish();
  11717                 try initializeWsa(t);
  11718                 syscall = try .start();
  11719                 continue;
  11720             },
  11721             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  11722                 try syscall.checkCancel();
  11723                 continue;
  11724             },
  11725             .EADDRINUSE => return syscall.fail(error.AddressInUse),
  11726             .EADDRNOTAVAIL => return syscall.fail(error.AddressUnavailable),
  11727             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  11728             .EFAULT => |err| return syscall.wsaErrorBug(err),
  11729             .EINVAL => |err| return syscall.wsaErrorBug(err),
  11730             .ENOBUFS => return syscall.fail(error.SystemResources),
  11731             .ENETDOWN => return syscall.fail(error.NetworkDown),
  11732             else => |err| return syscall.unexpectedWsaError(err),
  11733         }
  11734     }
  11735 
  11736     syscall = try .start();
  11737     while (true) {
  11738         const rc = ws2_32.listen(socket_handle, options.kernel_backlog);
  11739         if (rc != ws2_32.SOCKET_ERROR) {
  11740             syscall.finish();
  11741             break;
  11742         }
  11743         switch (ws2_32.WSAGetLastError()) {
  11744             .NOTINITIALISED => {
  11745                 syscall.finish();
  11746                 try initializeWsa(t);
  11747                 syscall = try .start();
  11748                 continue;
  11749             },
  11750             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  11751                 try syscall.checkCancel();
  11752                 continue;
  11753             },
  11754             .ENETDOWN => return syscall.fail(error.NetworkDown),
  11755             .EADDRINUSE => return syscall.fail(error.AddressInUse),
  11756             .EMFILE, .ENOBUFS => return syscall.fail(error.SystemResources),
  11757             .EISCONN => |err| return syscall.wsaErrorBug(err),
  11758             .EINVAL => |err| return syscall.wsaErrorBug(err),
  11759             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  11760             .EOPNOTSUPP => |err| return syscall.wsaErrorBug(err),
  11761             .EINPROGRESS => |err| return syscall.wsaErrorBug(err),
  11762             else => |err| return syscall.unexpectedWsaError(err),
  11763         }
  11764     }
  11765 
  11766     try wsaGetSockName(t, socket_handle, &storage.any, &addr_len);
  11767 
  11768     return .{
  11769         .socket = .{
  11770             .handle = socket_handle,
  11771             .address = addressFromWsa(&storage),
  11772         },
  11773     };
  11774 }
  11775 
  11776 fn netListenIpUnavailable(
  11777     userdata: ?*anyopaque,
  11778     address: IpAddress,
  11779     options: IpAddress.ListenOptions,
  11780 ) IpAddress.ListenError!net.Server {
  11781     _ = userdata;
  11782     _ = address;
  11783     _ = options;
  11784     return error.NetworkDown;
  11785 }
  11786 
  11787 fn netListenUnixPosix(
  11788     userdata: ?*anyopaque,
  11789     address: *const net.UnixAddress,
  11790     options: net.UnixAddress.ListenOptions,
  11791 ) net.UnixAddress.ListenError!net.Socket.Handle {
  11792     if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
  11793     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11794     _ = t;
  11795     const socket_fd = openSocketPosix(posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
  11796         error.ProtocolUnsupportedBySystem => return error.AddressFamilyUnsupported,
  11797         error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported,
  11798         error.SocketModeUnsupported => return error.AddressFamilyUnsupported,
  11799         error.OptionUnsupported => return error.Unexpected,
  11800         else => |e| return e,
  11801     };
  11802     errdefer closeFd(socket_fd);
  11803 
  11804     var storage: UnixAddress = undefined;
  11805     const addr_len = addressUnixToPosix(address, &storage);
  11806     try posixBindUnix(socket_fd, &storage.any, addr_len);
  11807 
  11808     const syscall: Syscall = try .start();
  11809     while (true) {
  11810         switch (posix.errno(posix.system.listen(socket_fd, options.kernel_backlog))) {
  11811             .SUCCESS => {
  11812                 syscall.finish();
  11813                 break;
  11814             },
  11815             .INTR => {
  11816                 try syscall.checkCancel();
  11817                 continue;
  11818             },
  11819             .ADDRINUSE => return syscall.fail(error.AddressInUse),
  11820             .BADF => |err| return syscall.errnoBug(err), // File descriptor used after closed.
  11821             else => |err| return syscall.unexpectedErrno(err),
  11822         }
  11823     }
  11824 
  11825     return socket_fd;
  11826 }
  11827 
  11828 fn netListenUnixWindows(
  11829     userdata: ?*anyopaque,
  11830     address: *const net.UnixAddress,
  11831     options: net.UnixAddress.ListenOptions,
  11832 ) net.UnixAddress.ListenError!net.Socket.Handle {
  11833     if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
  11834     if (!have_networking) return error.NetworkDown;
  11835     const t: *Threaded = @ptrCast(@alignCast(userdata));
  11836 
  11837     const socket_handle = openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
  11838         error.ProtocolUnsupportedByAddressFamily => return error.AddressFamilyUnsupported,
  11839         else => |e| return e,
  11840     };
  11841     errdefer closeSocketWindows(socket_handle);
  11842 
  11843     var storage: WsaAddress = undefined;
  11844     const addr_len = addressUnixToWsa(address, &storage);
  11845 
  11846     var syscall: AlertableSyscall = try .start();
  11847     while (true) {
  11848         const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
  11849         if (rc != ws2_32.SOCKET_ERROR) {
  11850             syscall.finish();
  11851             break;
  11852         }
  11853         switch (ws2_32.WSAGetLastError()) {
  11854             .NOTINITIALISED => {
  11855                 syscall.finish();
  11856                 try initializeWsa(t);
  11857                 syscall = try .start();
  11858                 continue;
  11859             },
  11860             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  11861                 try syscall.checkCancel();
  11862                 continue;
  11863             },
  11864             .EADDRINUSE => return syscall.fail(error.AddressInUse),
  11865             .EADDRNOTAVAIL => return syscall.fail(error.AddressUnavailable),
  11866             .ENOBUFS => return syscall.fail(error.SystemResources),
  11867             .ENETDOWN => return syscall.fail(error.NetworkDown),
  11868             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  11869             .EFAULT => |err| return syscall.wsaErrorBug(err),
  11870             .EINVAL => |err| return syscall.wsaErrorBug(err),
  11871             else => |err| return syscall.unexpectedWsaError(err),
  11872         }
  11873     }
  11874 
  11875     syscall = try .start();
  11876     while (true) {
  11877         const rc = ws2_32.listen(socket_handle, options.kernel_backlog);
  11878         if (rc != ws2_32.SOCKET_ERROR) {
  11879             syscall.finish();
  11880             return socket_handle;
  11881         }
  11882         switch (ws2_32.WSAGetLastError()) {
  11883             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  11884                 try syscall.checkCancel();
  11885                 continue;
  11886             },
  11887             .NOTINITIALISED => {
  11888                 syscall.finish();
  11889                 try initializeWsa(t);
  11890                 syscall = try .start();
  11891                 continue;
  11892             },
  11893             .ENETDOWN => return syscall.fail(error.NetworkDown),
  11894             .EADDRINUSE => return syscall.fail(error.AddressInUse),
  11895             .EMFILE, .ENOBUFS => return syscall.fail(error.SystemResources),
  11896             .EISCONN => |err| return syscall.wsaErrorBug(err),
  11897             .EINVAL => |err| return syscall.wsaErrorBug(err),
  11898             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  11899             .EOPNOTSUPP => |err| return syscall.wsaErrorBug(err),
  11900             .EINPROGRESS => |err| return syscall.wsaErrorBug(err),
  11901             else => |err| return syscall.unexpectedWsaError(err),
  11902         }
  11903     }
  11904 }
  11905 
  11906 fn netListenUnixUnavailable(
  11907     userdata: ?*anyopaque,
  11908     address: *const net.UnixAddress,
  11909     options: net.UnixAddress.ListenOptions,
  11910 ) net.UnixAddress.ListenError!net.Socket.Handle {
  11911     _ = userdata;
  11912     _ = address;
  11913     _ = options;
  11914     return error.AddressFamilyUnsupported;
  11915 }
  11916 
  11917 fn posixBindUnix(
  11918     fd: posix.socket_t,
  11919     addr: *const posix.sockaddr,
  11920     addr_len: posix.socklen_t,
  11921 ) !void {
  11922     const syscall: Syscall = try .start();
  11923     while (true) {
  11924         switch (posix.errno(posix.system.bind(fd, addr, addr_len))) {
  11925             .SUCCESS => {
  11926                 syscall.finish();
  11927                 break;
  11928             },
  11929             .INTR => {
  11930                 try syscall.checkCancel();
  11931                 continue;
  11932             },
  11933             else => |e| {
  11934                 syscall.finish();
  11935                 switch (e) {
  11936                     .ACCES => return error.AccessDenied,
  11937                     .ADDRINUSE => return error.AddressInUse,
  11938                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
  11939                     .ADDRNOTAVAIL => return error.AddressUnavailable,
  11940                     .NOMEM => return error.SystemResources,
  11941 
  11942                     .LOOP => return error.SymLinkLoop,
  11943                     .NOENT => return error.FileNotFound,
  11944                     .NOTDIR => return error.NotDir,
  11945                     .ROFS => return error.ReadOnlyFileSystem,
  11946                     .PERM => return error.PermissionDenied,
  11947 
  11948                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  11949                     .INVAL => |err| return errnoBug(err), // invalid parameters
  11950                     .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd`
  11951                     .FAULT => |err| return errnoBug(err), // invalid `addr` pointer
  11952                     .NAMETOOLONG => |err| return errnoBug(err),
  11953                     else => |err| return posix.unexpectedErrno(err),
  11954                 }
  11955             },
  11956         }
  11957     }
  11958 }
  11959 
  11960 fn posixBind(
  11961     socket_fd: posix.socket_t,
  11962     addr: *const posix.sockaddr,
  11963     addr_len: posix.socklen_t,
  11964 ) !void {
  11965     const syscall: Syscall = try .start();
  11966     while (true) {
  11967         switch (posix.errno(posix.system.bind(socket_fd, addr, addr_len))) {
  11968             .SUCCESS => {
  11969                 syscall.finish();
  11970                 break;
  11971             },
  11972             .INTR => {
  11973                 try syscall.checkCancel();
  11974                 continue;
  11975             },
  11976             else => |e| {
  11977                 syscall.finish();
  11978                 switch (e) {
  11979                     .ADDRINUSE => return error.AddressInUse,
  11980                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  11981                     .INVAL => |err| return errnoBug(err), // invalid parameters
  11982                     .NOTSOCK => |err| return errnoBug(err), // invalid `sockfd`
  11983                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
  11984                     .ADDRNOTAVAIL => return error.AddressUnavailable,
  11985                     .FAULT => |err| return errnoBug(err), // invalid `addr` pointer
  11986                     .NOMEM => return error.SystemResources,
  11987                     else => |err| return posix.unexpectedErrno(err),
  11988                 }
  11989             },
  11990         }
  11991     }
  11992 }
  11993 
  11994 fn posixConnect(
  11995     socket_fd: posix.socket_t,
  11996     addr: *const posix.sockaddr,
  11997     addr_len: posix.socklen_t,
  11998 ) !void {
  11999     const syscall: Syscall = try .start();
  12000     while (true) switch (posix.errno(posix.system.connect(socket_fd, addr, addr_len))) {
  12001         .SUCCESS => {
  12002             syscall.finish();
  12003             return;
  12004         },
  12005         .INTR => {
  12006             try syscall.checkCancel();
  12007             continue;
  12008         },
  12009         .ADDRNOTAVAIL => return syscall.fail(error.AddressUnavailable),
  12010         .AFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
  12011         .AGAIN, .INPROGRESS => return syscall.fail(error.WouldBlock),
  12012         .ALREADY => return syscall.fail(error.ConnectionPending),
  12013         .CONNREFUSED => return syscall.fail(error.ConnectionRefused),
  12014         .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  12015         .HOSTUNREACH => return syscall.fail(error.HostUnreachable),
  12016         .NETUNREACH => return syscall.fail(error.NetworkUnreachable),
  12017         .TIMEDOUT => return syscall.fail(error.Timeout),
  12018         .ACCES => return syscall.fail(error.AccessDenied),
  12019         .NETDOWN => return syscall.fail(error.NetworkDown),
  12020         .BADF => |err| return syscall.errnoBug(err), // File descriptor used after closed.
  12021         .CONNABORTED => |err| return syscall.errnoBug(err),
  12022         .FAULT => |err| return syscall.errnoBug(err),
  12023         .ISCONN => |err| return syscall.errnoBug(err),
  12024         .NOENT => |err| return syscall.errnoBug(err),
  12025         .NOTSOCK => |err| return syscall.errnoBug(err),
  12026         .PERM => |err| return syscall.errnoBug(err),
  12027         .PROTOTYPE => |err| return syscall.errnoBug(err),
  12028         else => |err| return syscall.unexpectedErrno(err),
  12029     };
  12030 }
  12031 
  12032 fn posixConnectUnix(
  12033     fd: posix.socket_t,
  12034     addr: *const posix.sockaddr,
  12035     addr_len: posix.socklen_t,
  12036 ) !void {
  12037     const syscall: Syscall = try .start();
  12038     while (true) {
  12039         switch (posix.errno(posix.system.connect(fd, addr, addr_len))) {
  12040             .SUCCESS => {
  12041                 syscall.finish();
  12042                 return;
  12043             },
  12044             .INTR => {
  12045                 try syscall.checkCancel();
  12046                 continue;
  12047             },
  12048             else => |e| {
  12049                 syscall.finish();
  12050                 switch (e) {
  12051                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
  12052                     .AGAIN => return error.WouldBlock,
  12053                     .INPROGRESS => return error.WouldBlock,
  12054                     .ACCES => return error.AccessDenied,
  12055 
  12056                     .LOOP => return error.SymLinkLoop,
  12057                     .NOENT => return error.FileNotFound,
  12058                     .NOTDIR => return error.NotDir,
  12059                     .ROFS => return error.ReadOnlyFileSystem,
  12060                     .PERM => return error.PermissionDenied,
  12061 
  12062                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  12063                     .CONNABORTED => |err| return errnoBug(err),
  12064                     .FAULT => |err| return errnoBug(err),
  12065                     .ISCONN => |err| return errnoBug(err),
  12066                     .NOTSOCK => |err| return errnoBug(err),
  12067                     .PROTOTYPE => |err| return errnoBug(err),
  12068                     else => |err| return posix.unexpectedErrno(err),
  12069                 }
  12070             },
  12071         }
  12072     }
  12073 }
  12074 
  12075 fn posixGetSockName(
  12076     socket_fd: posix.fd_t,
  12077     addr: *posix.sockaddr,
  12078     addr_len: *posix.socklen_t,
  12079 ) !void {
  12080     const syscall: Syscall = try .start();
  12081     while (true) {
  12082         switch (posix.errno(posix.system.getsockname(socket_fd, addr, addr_len))) {
  12083             .SUCCESS => {
  12084                 syscall.finish();
  12085                 break;
  12086             },
  12087             .INTR => {
  12088                 try syscall.checkCancel();
  12089                 continue;
  12090             },
  12091             else => |e| {
  12092                 syscall.finish();
  12093                 switch (e) {
  12094                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  12095                     .FAULT => |err| return errnoBug(err),
  12096                     .INVAL => |err| return errnoBug(err), // invalid parameters
  12097                     .NOTSOCK => |err| return errnoBug(err), // always a race condition
  12098                     .NOBUFS => return error.SystemResources,
  12099                     else => |err| return posix.unexpectedErrno(err),
  12100                 }
  12101             },
  12102         }
  12103     }
  12104 }
  12105 
  12106 fn wsaGetSockName(
  12107     t: *Threaded,
  12108     handle: ws2_32.SOCKET,
  12109     addr: *ws2_32.sockaddr,
  12110     addr_len: *i32,
  12111 ) !void {
  12112     var syscall: AlertableSyscall = try .start();
  12113     while (true) {
  12114         const rc = ws2_32.getsockname(handle, addr, addr_len);
  12115         if (rc != ws2_32.SOCKET_ERROR) {
  12116             syscall.finish();
  12117             return;
  12118         }
  12119         switch (ws2_32.WSAGetLastError()) {
  12120             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  12121                 try syscall.checkCancel();
  12122                 continue;
  12123             },
  12124             .NOTINITIALISED => {
  12125                 syscall.finish();
  12126                 try initializeWsa(t);
  12127                 syscall = try .start();
  12128                 continue;
  12129             },
  12130             .ENETDOWN => return syscall.fail(error.NetworkDown),
  12131             .EFAULT => |err| return syscall.wsaErrorBug(err),
  12132             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  12133             .EINVAL => |err| return syscall.wsaErrorBug(err),
  12134             else => |err| return syscall.unexpectedWsaError(err),
  12135         }
  12136     }
  12137 }
  12138 
  12139 fn setSocketOption(fd: posix.fd_t, level: i32, opt_name: u32, option: u32) !void {
  12140     const o: []const u8 = @ptrCast(&option);
  12141     const syscall: Syscall = try .start();
  12142     while (true) {
  12143         switch (posix.errno(posix.system.setsockopt(fd, level, opt_name, o.ptr, @intCast(o.len)))) {
  12144             .SUCCESS => {
  12145                 syscall.finish();
  12146                 return;
  12147             },
  12148             .INTR => {
  12149                 try syscall.checkCancel();
  12150                 continue;
  12151             },
  12152             else => |e| {
  12153                 syscall.finish();
  12154                 switch (e) {
  12155                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  12156                     .NOTSOCK => |err| return errnoBug(err),
  12157                     .INVAL => |err| return errnoBug(err),
  12158                     .FAULT => |err| return errnoBug(err),
  12159                     else => |err| return posix.unexpectedErrno(err),
  12160                 }
  12161             },
  12162         }
  12163     }
  12164 }
  12165 
  12166 fn setSocketOptionWsa(t: *Threaded, socket: Io.net.Socket.Handle, level: i32, opt_name: u32, option: u32) !void {
  12167     const o: []const u8 = @ptrCast(&option);
  12168     var syscall: AlertableSyscall = try .start();
  12169     while (true) {
  12170         const rc = ws2_32.setsockopt(socket, level, @bitCast(opt_name), o.ptr, @intCast(o.len));
  12171         if (rc != ws2_32.SOCKET_ERROR) {
  12172             syscall.finish();
  12173             return;
  12174         }
  12175         switch (ws2_32.WSAGetLastError()) {
  12176             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  12177                 try syscall.checkCancel();
  12178                 continue;
  12179             },
  12180             .NOTINITIALISED => {
  12181                 syscall.finish();
  12182                 try initializeWsa(t);
  12183                 syscall = try .start();
  12184                 continue;
  12185             },
  12186             .ENETDOWN => return syscall.fail(error.NetworkDown),
  12187             .EFAULT => |err| return syscall.wsaErrorBug(err),
  12188             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  12189             .EINVAL => |err| return syscall.wsaErrorBug(err),
  12190             else => |err| return syscall.unexpectedWsaError(err),
  12191         }
  12192     }
  12193 }
  12194 
  12195 fn netConnectIpPosix(
  12196     userdata: ?*anyopaque,
  12197     address: *const IpAddress,
  12198     options: IpAddress.ConnectOptions,
  12199 ) IpAddress.ConnectError!net.Stream {
  12200     if (!have_networking) return error.NetworkDown;
  12201     if (options.timeout != .none) @panic("TODO implement netConnectIpPosix with timeout");
  12202     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12203     _ = t;
  12204     const family = posixAddressFamily(address);
  12205     const socket_fd = try openSocketPosix(family, .{
  12206         .mode = options.mode,
  12207         .protocol = options.protocol,
  12208     });
  12209     errdefer closeFd(socket_fd);
  12210     var storage: PosixAddress = undefined;
  12211     var addr_len = addressToPosix(address, &storage);
  12212     try posixConnect(socket_fd, &storage.any, addr_len);
  12213     try posixGetSockName(socket_fd, &storage.any, &addr_len);
  12214     return .{ .socket = .{
  12215         .handle = socket_fd,
  12216         .address = addressFromPosix(&storage),
  12217     } };
  12218 }
  12219 
  12220 fn netConnectIpWindows(
  12221     userdata: ?*anyopaque,
  12222     address: *const IpAddress,
  12223     options: IpAddress.ConnectOptions,
  12224 ) IpAddress.ConnectError!net.Stream {
  12225     if (!have_networking) return error.NetworkDown;
  12226     if (options.timeout != .none) @panic("TODO implement netConnectIpWindows with timeout");
  12227     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12228     const family = posixAddressFamily(address);
  12229     const socket_handle = try openSocketWsa(t, family, .{
  12230         .mode = options.mode,
  12231         .protocol = options.protocol,
  12232     });
  12233     errdefer closeSocketWindows(socket_handle);
  12234 
  12235     var storage: WsaAddress = undefined;
  12236     var addr_len = addressToWsa(address, &storage);
  12237 
  12238     var syscall: AlertableSyscall = try .start();
  12239     while (true) {
  12240         const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);
  12241         if (rc != ws2_32.SOCKET_ERROR) {
  12242             syscall.finish();
  12243             break;
  12244         }
  12245         switch (ws2_32.WSAGetLastError()) {
  12246             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  12247                 try syscall.checkCancel();
  12248                 continue;
  12249             },
  12250             .NOTINITIALISED => {
  12251                 syscall.finish();
  12252                 try initializeWsa(t);
  12253                 syscall = try .start();
  12254                 continue;
  12255             },
  12256             .EADDRNOTAVAIL => return syscall.fail(error.AddressUnavailable),
  12257             .ECONNREFUSED => return syscall.fail(error.ConnectionRefused),
  12258             .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  12259             .ETIMEDOUT => return syscall.fail(error.Timeout),
  12260             .EHOSTUNREACH => return syscall.fail(error.HostUnreachable),
  12261             .ENETUNREACH => return syscall.fail(error.NetworkUnreachable),
  12262             .EFAULT => |err| return syscall.wsaErrorBug(err),
  12263             .EINVAL => |err| return syscall.wsaErrorBug(err),
  12264             .EISCONN => |err| return syscall.wsaErrorBug(err),
  12265             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  12266             .EWOULDBLOCK => return syscall.fail(error.WouldBlock),
  12267             .EACCES => return syscall.fail(error.AccessDenied),
  12268             .ENOBUFS => return syscall.fail(error.SystemResources),
  12269             .EAFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
  12270             else => |err| return syscall.unexpectedWsaError(err),
  12271         }
  12272     }
  12273 
  12274     try wsaGetSockName(t, socket_handle, &storage.any, &addr_len);
  12275 
  12276     return .{ .socket = .{
  12277         .handle = socket_handle,
  12278         .address = addressFromWsa(&storage),
  12279     } };
  12280 }
  12281 
  12282 fn netConnectIpUnavailable(
  12283     userdata: ?*anyopaque,
  12284     address: *const IpAddress,
  12285     options: IpAddress.ConnectOptions,
  12286 ) IpAddress.ConnectError!net.Stream {
  12287     _ = userdata;
  12288     _ = address;
  12289     _ = options;
  12290     return error.NetworkDown;
  12291 }
  12292 
  12293 fn netConnectUnixPosix(
  12294     userdata: ?*anyopaque,
  12295     address: *const net.UnixAddress,
  12296 ) net.UnixAddress.ConnectError!net.Socket.Handle {
  12297     if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
  12298     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12299     _ = t;
  12300     const socket_fd = openSocketPosix(posix.AF.UNIX, .{ .mode = .stream }) catch |err| switch (err) {
  12301         error.OptionUnsupported => return error.Unexpected,
  12302         else => |e| return e,
  12303     };
  12304     errdefer closeFd(socket_fd);
  12305     var storage: UnixAddress = undefined;
  12306     const addr_len = addressUnixToPosix(address, &storage);
  12307     try posixConnectUnix(socket_fd, &storage.any, addr_len);
  12308     return socket_fd;
  12309 }
  12310 
  12311 fn netConnectUnixWindows(
  12312     userdata: ?*anyopaque,
  12313     address: *const net.UnixAddress,
  12314 ) net.UnixAddress.ConnectError!net.Socket.Handle {
  12315     if (!net.has_unix_sockets) return error.AddressFamilyUnsupported;
  12316     if (!have_networking) return error.NetworkDown;
  12317     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12318 
  12319     const socket_handle = try openSocketWsa(t, posix.AF.UNIX, .{ .mode = .stream });
  12320     errdefer closeSocketWindows(socket_handle);
  12321     var storage: WsaAddress = undefined;
  12322     const addr_len = addressUnixToWsa(address, &storage);
  12323 
  12324     var syscall: AlertableSyscall = try .start();
  12325     while (true) {
  12326         const rc = ws2_32.connect(socket_handle, &storage.any, addr_len);
  12327         if (rc != ws2_32.SOCKET_ERROR) {
  12328             syscall.finish();
  12329             break;
  12330         }
  12331         switch (ws2_32.WSAGetLastError()) {
  12332             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  12333                 try syscall.checkCancel();
  12334                 continue;
  12335             },
  12336             .NOTINITIALISED => {
  12337                 syscall.finish();
  12338                 try initializeWsa(t);
  12339                 syscall = try .start();
  12340                 continue;
  12341             },
  12342             .ECONNREFUSED => return syscall.fail(error.FileNotFound),
  12343             .EWOULDBLOCK => return syscall.fail(error.WouldBlock),
  12344             .EACCES => return syscall.fail(error.AccessDenied),
  12345             .ENOBUFS => return syscall.fail(error.SystemResources),
  12346             .EAFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
  12347             .EFAULT => |err| return syscall.wsaErrorBug(err),
  12348             .EINVAL => |err| return syscall.wsaErrorBug(err),
  12349             .EISCONN => |err| return syscall.wsaErrorBug(err),
  12350             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  12351             else => |err| return syscall.unexpectedWsaError(err),
  12352         }
  12353     }
  12354 
  12355     return socket_handle;
  12356 }
  12357 
  12358 fn netConnectUnixUnavailable(
  12359     userdata: ?*anyopaque,
  12360     address: *const net.UnixAddress,
  12361 ) net.UnixAddress.ConnectError!net.Socket.Handle {
  12362     _ = userdata;
  12363     _ = address;
  12364     return error.AddressFamilyUnsupported;
  12365 }
  12366 
  12367 fn netBindIpPosix(
  12368     userdata: ?*anyopaque,
  12369     address: *const IpAddress,
  12370     options: IpAddress.BindOptions,
  12371 ) IpAddress.BindError!net.Socket {
  12372     if (!have_networking) return error.NetworkDown;
  12373     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12374     _ = t;
  12375     const family = posixAddressFamily(address);
  12376     const socket_fd = try openSocketPosix(family, options);
  12377     errdefer closeFd(socket_fd);
  12378     var storage: PosixAddress = undefined;
  12379     var addr_len = addressToPosix(address, &storage);
  12380     try posixBind(socket_fd, &storage.any, addr_len);
  12381     try posixGetSockName(socket_fd, &storage.any, &addr_len);
  12382     return .{
  12383         .handle = socket_fd,
  12384         .address = addressFromPosix(&storage),
  12385     };
  12386 }
  12387 
  12388 fn netBindIpWindows(
  12389     userdata: ?*anyopaque,
  12390     address: *const IpAddress,
  12391     options: IpAddress.BindOptions,
  12392 ) IpAddress.BindError!net.Socket {
  12393     if (!have_networking) return error.NetworkDown;
  12394     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12395     const family = posixAddressFamily(address);
  12396     const socket_handle = try openSocketWsa(t, family, .{
  12397         .mode = options.mode,
  12398         .protocol = options.protocol,
  12399     });
  12400     errdefer closeSocketWindows(socket_handle);
  12401 
  12402     var storage: WsaAddress = undefined;
  12403     var addr_len = addressToWsa(address, &storage);
  12404 
  12405     var syscall: AlertableSyscall = try .start();
  12406     while (true) {
  12407         const rc = ws2_32.bind(socket_handle, &storage.any, addr_len);
  12408         if (rc != ws2_32.SOCKET_ERROR) {
  12409             syscall.finish();
  12410             break;
  12411         }
  12412         switch (ws2_32.WSAGetLastError()) {
  12413             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  12414                 try syscall.checkCancel();
  12415                 continue;
  12416             },
  12417             .NOTINITIALISED => {
  12418                 syscall.finish();
  12419                 try initializeWsa(t);
  12420                 syscall = try .start();
  12421                 continue;
  12422             },
  12423             .EADDRINUSE => return syscall.fail(error.AddressInUse),
  12424             .EADDRNOTAVAIL => return syscall.fail(error.AddressUnavailable),
  12425             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  12426             .EFAULT => |err| return syscall.wsaErrorBug(err),
  12427             .EINVAL => |err| return syscall.wsaErrorBug(err),
  12428             .ENOBUFS => return syscall.fail(error.SystemResources),
  12429             .ENETDOWN => return syscall.fail(error.NetworkDown),
  12430             else => |err| return syscall.unexpectedWsaError(err),
  12431         }
  12432     }
  12433 
  12434     try wsaGetSockName(t, socket_handle, &storage.any, &addr_len);
  12435 
  12436     return .{
  12437         .handle = socket_handle,
  12438         .address = addressFromWsa(&storage),
  12439     };
  12440 }
  12441 
  12442 fn netBindIpUnavailable(
  12443     userdata: ?*anyopaque,
  12444     address: *const IpAddress,
  12445     options: IpAddress.BindOptions,
  12446 ) IpAddress.BindError!net.Socket {
  12447     _ = userdata;
  12448     _ = address;
  12449     _ = options;
  12450     return error.NetworkDown;
  12451 }
  12452 
  12453 fn openSocketPosix(
  12454     family: posix.sa_family_t,
  12455     options: IpAddress.BindOptions,
  12456 ) error{
  12457     AddressFamilyUnsupported,
  12458     ProtocolUnsupportedBySystem,
  12459     ProcessFdQuotaExceeded,
  12460     SystemFdQuotaExceeded,
  12461     SystemResources,
  12462     ProtocolUnsupportedByAddressFamily,
  12463     SocketModeUnsupported,
  12464     OptionUnsupported,
  12465     Unexpected,
  12466     Canceled,
  12467 }!posix.socket_t {
  12468     const mode = posixSocketMode(options.mode);
  12469     const protocol = posixProtocol(options.protocol);
  12470     const flags: u32 = mode | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC;
  12471     const syscall: Syscall = try .start();
  12472     const socket_fd = while (true) {
  12473         const rc = posix.system.socket(family, flags, protocol);
  12474         switch (posix.errno(rc)) {
  12475             .SUCCESS => {
  12476                 syscall.finish();
  12477                 const fd: posix.fd_t = @intCast(rc);
  12478                 errdefer closeFd(fd);
  12479                 if (socket_flags_unsupported) try setCloexec(fd);
  12480                 break fd;
  12481             },
  12482             .INTR => {
  12483                 try syscall.checkCancel();
  12484                 continue;
  12485             },
  12486             .AFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
  12487             .INVAL => return syscall.fail(error.ProtocolUnsupportedBySystem),
  12488             .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
  12489             .NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
  12490             .NOBUFS => return syscall.fail(error.SystemResources),
  12491             .NOMEM => return syscall.fail(error.SystemResources),
  12492             .PROTONOSUPPORT => return syscall.fail(error.ProtocolUnsupportedByAddressFamily),
  12493             .PROTOTYPE => return syscall.fail(error.SocketModeUnsupported),
  12494             else => |err| return syscall.unexpectedErrno(err),
  12495         }
  12496     };
  12497     errdefer closeFd(socket_fd);
  12498 
  12499     if (options.ip6_only) {
  12500         if (posix.IPV6 == void) return error.OptionUnsupported;
  12501         try setSocketOption(socket_fd, posix.IPPROTO.IPV6, posix.IPV6.V6ONLY, 0);
  12502     }
  12503 
  12504     return socket_fd;
  12505 }
  12506 
  12507 fn setCloexec(fd: posix.fd_t) error{ Canceled, Unexpected }!void {
  12508     const syscall: Syscall = try .start();
  12509     while (true) switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(usize, posix.FD_CLOEXEC)))) {
  12510         .SUCCESS => return syscall.finish(),
  12511         .INTR => {
  12512             try syscall.checkCancel();
  12513             continue;
  12514         },
  12515         else => |err| return syscall.unexpectedErrno(err),
  12516     };
  12517 }
  12518 
  12519 fn netSocketCreatePair(
  12520     userdata: ?*anyopaque,
  12521     options: net.Socket.CreatePairOptions,
  12522 ) net.Socket.CreatePairError![2]net.Socket {
  12523     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12524     _ = t;
  12525     if (!have_networking) return error.OperationUnsupported;
  12526     if (@TypeOf(posix.system.socketpair) == void) return error.OperationUnsupported;
  12527     if (native_os == .haiku) @panic("TODO");
  12528 
  12529     const family: posix.sa_family_t = switch (options.family) {
  12530         .ip4 => posix.AF.INET,
  12531         .ip6 => posix.AF.INET6,
  12532     };
  12533     const mode = posixSocketMode(options.mode);
  12534     const protocol = posixProtocol(options.protocol);
  12535     const flags: u32 = mode | if (socket_flags_unsupported) 0 else posix.SOCK.CLOEXEC;
  12536 
  12537     var sockets: [2]posix.socket_t = undefined;
  12538     const syscall: Syscall = try .start();
  12539     while (true) switch (posix.errno(posix.system.socketpair(family, flags, protocol, &sockets))) {
  12540         .SUCCESS => {
  12541             syscall.finish();
  12542             errdefer {
  12543                 closeFd(sockets[0]);
  12544                 closeFd(sockets[1]);
  12545             }
  12546             if (socket_flags_unsupported) {
  12547                 try setCloexec(sockets[0]);
  12548                 try setCloexec(sockets[1]);
  12549             }
  12550             var storages: [2]PosixAddress = undefined;
  12551             var addr_lens: [2]posix.socklen_t = .{ @sizeOf(PosixAddress), @sizeOf(PosixAddress) };
  12552             try posixGetSockName(sockets[0], &storages[0].any, &addr_lens[0]);
  12553             try posixGetSockName(sockets[1], &storages[1].any, &addr_lens[1]);
  12554             return .{
  12555                 .{ .handle = sockets[0], .address = addressFromPosix(&storages[0]) },
  12556                 .{ .handle = sockets[1], .address = addressFromPosix(&storages[1]) },
  12557             };
  12558         },
  12559         .INTR => {
  12560             try syscall.checkCancel();
  12561             continue;
  12562         },
  12563         .ACCES => return syscall.fail(error.AccessDenied),
  12564         .AFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
  12565         .INVAL => return syscall.fail(error.ProtocolUnsupportedBySystem),
  12566         .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
  12567         .NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
  12568         .NOBUFS => return syscall.fail(error.SystemResources),
  12569         .NOMEM => return syscall.fail(error.SystemResources),
  12570         .PROTONOSUPPORT => return syscall.fail(error.ProtocolUnsupportedByAddressFamily),
  12571         .PROTOTYPE => return syscall.fail(error.SocketModeUnsupported),
  12572         else => |err| return syscall.unexpectedErrno(err),
  12573     };
  12574 }
  12575 
  12576 fn netSocketCreatePairUnavailable(
  12577     userdata: ?*anyopaque,
  12578     options: net.Socket.CreatePairOptions,
  12579 ) net.Socket.CreatePairError![2]net.Socket {
  12580     _ = userdata;
  12581     _ = options;
  12582     return error.OperationUnsupported;
  12583 }
  12584 
  12585 fn openSocketWsa(
  12586     t: *Threaded,
  12587     family: posix.sa_family_t,
  12588     options: IpAddress.BindOptions,
  12589 ) !ws2_32.SOCKET {
  12590     const mode = posixSocketMode(options.mode);
  12591     const protocol = posixProtocol(options.protocol);
  12592     // WSA_FLAG_OVERLAPPED is chosen here because without this different
  12593     // threads cannot use the same open socket handle.
  12594     const flags: u32 = ws2_32.WSA_FLAG_OVERLAPPED | ws2_32.WSA_FLAG_NO_HANDLE_INHERIT;
  12595     var syscall: AlertableSyscall = try .start();
  12596     while (true) {
  12597         const rc = ws2_32.WSASocketW(family, @bitCast(mode), @bitCast(protocol), null, 0, flags);
  12598         if (rc != ws2_32.INVALID_SOCKET) {
  12599             syscall.finish();
  12600             return rc;
  12601         }
  12602         switch (ws2_32.WSAGetLastError()) {
  12603             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  12604                 try syscall.checkCancel();
  12605                 continue;
  12606             },
  12607             .NOTINITIALISED => {
  12608                 syscall.finish();
  12609                 try initializeWsa(t);
  12610                 syscall = try .start();
  12611                 continue;
  12612             },
  12613             .EAFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
  12614             .EMFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
  12615             .ENOBUFS => return syscall.fail(error.SystemResources),
  12616             .EPROTONOSUPPORT => return syscall.fail(error.ProtocolUnsupportedByAddressFamily),
  12617             else => |err| return syscall.unexpectedWsaError(err),
  12618         }
  12619     }
  12620 }
  12621 
  12622 fn netAcceptPosix(userdata: ?*anyopaque, listen_fd: net.Socket.Handle) net.Server.AcceptError!net.Stream {
  12623     if (!have_networking) return error.NetworkDown;
  12624     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12625     _ = t;
  12626     var storage: PosixAddress = undefined;
  12627     var addr_len: posix.socklen_t = @sizeOf(PosixAddress);
  12628     const syscall: Syscall = try .start();
  12629     const fd = while (true) {
  12630         const rc = if (have_accept4)
  12631             posix.system.accept4(listen_fd, &storage.any, &addr_len, posix.SOCK.CLOEXEC)
  12632         else
  12633             posix.system.accept(listen_fd, &storage.any, &addr_len);
  12634         switch (posix.errno(rc)) {
  12635             .SUCCESS => {
  12636                 syscall.finish();
  12637                 const fd: posix.fd_t = @intCast(rc);
  12638                 errdefer closeFd(fd);
  12639                 if (!have_accept4) try setCloexec(fd);
  12640                 break fd;
  12641             },
  12642             .INTR => {
  12643                 try syscall.checkCancel();
  12644                 continue;
  12645             },
  12646             else => |e| {
  12647                 syscall.finish();
  12648                 switch (e) {
  12649                     .AGAIN => |err| return errnoBug(err),
  12650                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  12651                     .CONNABORTED => return error.ConnectionAborted,
  12652                     .FAULT => |err| return errnoBug(err),
  12653                     .INVAL => return error.SocketNotListening,
  12654                     .NOTSOCK => |err| return errnoBug(err),
  12655                     .MFILE => return error.ProcessFdQuotaExceeded,
  12656                     .NFILE => return error.SystemFdQuotaExceeded,
  12657                     .NOBUFS => return error.SystemResources,
  12658                     .NOMEM => return error.SystemResources,
  12659                     .OPNOTSUPP => |err| return errnoBug(err),
  12660                     .PROTO => return error.ProtocolFailure,
  12661                     .PERM => return error.BlockedByFirewall,
  12662                     else => |err| return posix.unexpectedErrno(err),
  12663                 }
  12664             },
  12665         }
  12666     };
  12667     return .{ .socket = .{
  12668         .handle = fd,
  12669         .address = addressFromPosix(&storage),
  12670     } };
  12671 }
  12672 
  12673 fn netAcceptWindows(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net.Server.AcceptError!net.Stream {
  12674     if (!have_networking) return error.NetworkDown;
  12675     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12676     var storage: WsaAddress = undefined;
  12677     var addr_len: i32 = @sizeOf(WsaAddress);
  12678     var syscall: AlertableSyscall = try .start();
  12679     while (true) {
  12680         const rc = ws2_32.accept(listen_handle, &storage.any, &addr_len);
  12681         if (rc != ws2_32.INVALID_SOCKET) {
  12682             syscall.finish();
  12683             return .{ .socket = .{
  12684                 .handle = rc,
  12685                 .address = addressFromWsa(&storage),
  12686             } };
  12687         }
  12688         switch (ws2_32.WSAGetLastError()) {
  12689             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  12690                 try syscall.checkCancel();
  12691                 continue;
  12692             },
  12693             .NOTINITIALISED => {
  12694                 syscall.finish();
  12695                 try initializeWsa(t);
  12696                 syscall = try .start();
  12697                 continue;
  12698             },
  12699             .ECONNRESET => return syscall.fail(error.ConnectionAborted),
  12700             .EMFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
  12701             .ENETDOWN => return syscall.fail(error.NetworkDown),
  12702             .ENOBUFS => return syscall.fail(error.SystemResources),
  12703             .EFAULT => |err| return syscall.wsaErrorBug(err),
  12704             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  12705             .EINVAL => |err| return syscall.wsaErrorBug(err),
  12706             .EOPNOTSUPP => |err| return syscall.wsaErrorBug(err),
  12707             else => |err| return syscall.unexpectedWsaError(err),
  12708         }
  12709     }
  12710 }
  12711 
  12712 fn netAcceptUnavailable(userdata: ?*anyopaque, listen_handle: net.Socket.Handle) net.Server.AcceptError!net.Stream {
  12713     _ = userdata;
  12714     _ = listen_handle;
  12715     return error.NetworkDown;
  12716 }
  12717 
  12718 fn netReadPosix(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
  12719     if (!have_networking) return error.NetworkDown;
  12720     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12721     _ = t;
  12722 
  12723     var iovecs_buffer: [max_iovecs_len]posix.iovec = undefined;
  12724     var i: usize = 0;
  12725     for (data) |buf| {
  12726         if (iovecs_buffer.len - i == 0) break;
  12727         if (buf.len != 0) {
  12728             iovecs_buffer[i] = .{ .base = buf.ptr, .len = buf.len };
  12729             i += 1;
  12730         }
  12731     }
  12732     const dest = iovecs_buffer[0..i];
  12733     assert(dest[0].len > 0);
  12734 
  12735     if (native_os == .wasi and !builtin.link_libc) {
  12736         const syscall: Syscall = try .start();
  12737         while (true) {
  12738             var n: usize = undefined;
  12739             switch (std.os.wasi.fd_read(fd, dest.ptr, dest.len, &n)) {
  12740                 .SUCCESS => {
  12741                     syscall.finish();
  12742                     return n;
  12743                 },
  12744                 .INTR => {
  12745                     try syscall.checkCancel();
  12746                     continue;
  12747                 },
  12748                 else => |e| {
  12749                     syscall.finish();
  12750                     switch (e) {
  12751                         .INVAL => |err| return errnoBug(err),
  12752                         .FAULT => |err| return errnoBug(err),
  12753                         .AGAIN => |err| return errnoBug(err),
  12754                         .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  12755                         .NOBUFS => return error.SystemResources,
  12756                         .NOMEM => return error.SystemResources,
  12757                         .NOTCONN => return error.SocketUnconnected,
  12758                         .CONNRESET => return error.ConnectionResetByPeer,
  12759                         .TIMEDOUT => return error.Timeout,
  12760                         .NOTCAPABLE => return error.AccessDenied,
  12761                         else => |err| return posix.unexpectedErrno(err),
  12762                     }
  12763                 },
  12764             }
  12765         }
  12766     }
  12767 
  12768     const syscall: Syscall = try .start();
  12769     while (true) {
  12770         const rc = posix.system.readv(fd, dest.ptr, @intCast(dest.len));
  12771         switch (posix.errno(rc)) {
  12772             .SUCCESS => {
  12773                 syscall.finish();
  12774                 return @intCast(rc);
  12775             },
  12776             .INTR => {
  12777                 try syscall.checkCancel();
  12778                 continue;
  12779             },
  12780             else => |e| {
  12781                 syscall.finish();
  12782                 switch (e) {
  12783                     .INVAL => |err| return errnoBug(err),
  12784                     .FAULT => |err| return errnoBug(err),
  12785                     .AGAIN => |err| return errnoBug(err),
  12786                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  12787                     .NOBUFS => return error.SystemResources,
  12788                     .NOMEM => return error.SystemResources,
  12789                     .NOTCONN => return error.SocketUnconnected,
  12790                     .CONNRESET => return error.ConnectionResetByPeer,
  12791                     .TIMEDOUT => return error.Timeout,
  12792                     .PIPE => return error.SocketUnconnected,
  12793                     .NETDOWN => return error.NetworkDown,
  12794                     else => |err| return posix.unexpectedErrno(err),
  12795                 }
  12796             },
  12797         }
  12798     }
  12799 }
  12800 
  12801 fn netReadWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
  12802     if (!have_networking) return error.NetworkDown;
  12803     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12804 
  12805     var iovec_buffer: [max_iovecs_len]ws2_32.WSABUF = undefined;
  12806     const bufs = b: {
  12807         var i: usize = 0;
  12808         var n: usize = 0;
  12809         for (data) |buf| {
  12810             if (iovec_buffer.len - i == 0) break;
  12811             if (buf.len == 0) continue;
  12812             if (std.math.cast(u32, buf.len)) |len| {
  12813                 iovec_buffer[i] = .{ .buf = buf.ptr, .len = len };
  12814                 i += 1;
  12815                 n += len;
  12816                 continue;
  12817             }
  12818             iovec_buffer[i] = .{ .buf = buf.ptr, .len = std.math.maxInt(u32) };
  12819             i += 1;
  12820             n += std.math.maxInt(u32);
  12821             break;
  12822         }
  12823 
  12824         const bufs = iovec_buffer[0..i];
  12825         assert(bufs[0].len != 0);
  12826 
  12827         break :b bufs;
  12828     };
  12829 
  12830     var syscall: AlertableSyscall = try .start();
  12831     while (true) {
  12832         var flags: u32 = 0;
  12833         var n: u32 = undefined;
  12834         const rc = ws2_32.WSARecv(handle, bufs.ptr, @intCast(bufs.len), &n, &flags, null, null);
  12835         if (rc != ws2_32.SOCKET_ERROR) {
  12836             syscall.finish();
  12837             return n;
  12838         }
  12839         switch (ws2_32.WSAGetLastError()) {
  12840             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  12841                 try syscall.checkCancel();
  12842                 continue;
  12843             },
  12844             .NOTINITIALISED => {
  12845                 syscall.finish();
  12846                 try initializeWsa(t);
  12847                 syscall = try .start();
  12848                 continue;
  12849             },
  12850 
  12851             .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  12852             .ENETDOWN => return syscall.fail(error.NetworkDown),
  12853             .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
  12854             .ENOTCONN => return syscall.fail(error.SocketUnconnected),
  12855             .EFAULT => unreachable, // a pointer is not completely contained in user address space.
  12856             .EINVAL => |err| return syscall.wsaErrorBug(err),
  12857             .EMSGSIZE => |err| return syscall.wsaErrorBug(err),
  12858             else => |err| return syscall.unexpectedWsaError(err),
  12859         }
  12860     }
  12861 }
  12862 
  12863 fn netReadUnavailable(userdata: ?*anyopaque, fd: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
  12864     _ = userdata;
  12865     _ = fd;
  12866     _ = data;
  12867     return error.NetworkDown;
  12868 }
  12869 
  12870 fn netSendPosix(
  12871     userdata: ?*anyopaque,
  12872     handle: net.Socket.Handle,
  12873     messages: []net.OutgoingMessage,
  12874     flags: net.SendFlags,
  12875 ) struct { ?net.Socket.SendError, usize } {
  12876     if (!have_networking) return .{ error.NetworkDown, 0 };
  12877     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12878 
  12879     const posix_flags: u32 =
  12880         @as(u32, if (@hasDecl(posix.MSG, "CONFIRM") and flags.confirm) posix.MSG.CONFIRM else 0) |
  12881         @as(u32, if (@hasDecl(posix.MSG, "DONTROUTE") and flags.dont_route) posix.MSG.DONTROUTE else 0) |
  12882         @as(u32, if (@hasDecl(posix.MSG, "EOR") and flags.eor) posix.MSG.EOR else 0) |
  12883         @as(u32, if (@hasDecl(posix.MSG, "OOB") and flags.oob) posix.MSG.OOB else 0) |
  12884         @as(u32, if (@hasDecl(posix.MSG, "FASTOPEN") and flags.fastopen) posix.MSG.FASTOPEN else 0) |
  12885         posix.MSG.NOSIGNAL;
  12886 
  12887     var i: usize = 0;
  12888     while (messages.len - i != 0) {
  12889         if (have_sendmmsg) {
  12890             i += netSendMany(handle, messages[i..], posix_flags) catch |err| return .{ err, i };
  12891             continue;
  12892         }
  12893         netSendOne(t, handle, &messages[i], posix_flags) catch |err| return .{ err, i };
  12894         i += 1;
  12895     }
  12896     return .{ null, i };
  12897 }
  12898 
  12899 fn netSendWindows(
  12900     userdata: ?*anyopaque,
  12901     handle: net.Socket.Handle,
  12902     messages: []net.OutgoingMessage,
  12903     flags: net.SendFlags,
  12904 ) struct { ?net.Socket.SendError, usize } {
  12905     if (!have_networking) return .{ error.NetworkDown, 0 };
  12906     const t: *Threaded = @ptrCast(@alignCast(userdata));
  12907 
  12908     // Ignored flags: confirm, eor, fastopen
  12909     const windows_flags: u32 =
  12910         @as(u32, if (flags.oob) ws2_32.MSG.OOB else 0) |
  12911         @as(u32, if (flags.dont_route) ws2_32.MSG.DONTROUTE else 0);
  12912 
  12913     for (messages, 0..) |*m, i| {
  12914         netSendWindowsOne(t, handle, m, windows_flags) catch |err| return .{ err, i };
  12915     }
  12916     return .{ null, messages.len };
  12917 }
  12918 
  12919 fn netSendWindowsOne(
  12920     t: *Threaded,
  12921     handle: net.Socket.Handle,
  12922     message: *net.OutgoingMessage,
  12923     flags: u32,
  12924 ) net.Socket.SendError!void {
  12925     var buf: ws2_32.WSABUF = .{
  12926         .buf = @constCast(message.data_ptr),
  12927         .len = std.math.cast(u32, message.data_len) orelse return error.MessageOversize,
  12928     };
  12929     var n: u32 = undefined;
  12930     var address: WsaAddress = undefined;
  12931     const address_size = addressToWsa(message.address, &address);
  12932     var syscall: AlertableSyscall = try .start();
  12933     while (true) {
  12934         const rc = ws2_32.WSASendTo(
  12935             handle,
  12936             (&buf)[0..1],
  12937             1,
  12938             &n,
  12939             flags,
  12940             &address.any,
  12941             address_size,
  12942             null,
  12943             null,
  12944         );
  12945         if (rc != ws2_32.SOCKET_ERROR) {
  12946             syscall.finish();
  12947             return;
  12948         }
  12949         switch (ws2_32.WSAGetLastError()) {
  12950             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  12951                 try syscall.checkCancel();
  12952                 continue;
  12953             },
  12954             .NOTINITIALISED => {
  12955                 syscall.finish();
  12956                 try initializeWsa(t);
  12957                 syscall = try .start();
  12958                 continue;
  12959             },
  12960 
  12961             .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  12962             .ENETDOWN => return syscall.fail(error.NetworkDown),
  12963             .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
  12964             .ENOTCONN => return syscall.fail(error.SocketUnconnected),
  12965             .EFAULT => unreachable, // a pointer is not completely contained in user address space.
  12966             .EINVAL => |err| return syscall.wsaErrorBug(err),
  12967             .EMSGSIZE => |err| return syscall.wsaErrorBug(err),
  12968             else => |err| return syscall.unexpectedWsaError(err),
  12969         }
  12970     }
  12971 }
  12972 
  12973 fn netSendUnavailable(
  12974     userdata: ?*anyopaque,
  12975     handle: net.Socket.Handle,
  12976     messages: []net.OutgoingMessage,
  12977     flags: net.SendFlags,
  12978 ) struct { ?net.Socket.SendError, usize } {
  12979     _ = userdata;
  12980     _ = handle;
  12981     _ = messages;
  12982     _ = flags;
  12983     return .{ error.NetworkDown, 0 };
  12984 }
  12985 
  12986 fn netSendOne(
  12987     t: *Threaded,
  12988     handle: net.Socket.Handle,
  12989     message: *net.OutgoingMessage,
  12990     flags: u32,
  12991 ) net.Socket.SendError!void {
  12992     var addr: PosixAddress = undefined;
  12993     var iovec: posix.iovec_const = .{ .base = @constCast(message.data_ptr), .len = message.data_len };
  12994     const msg: posix.msghdr_const = .{
  12995         .name = &addr.any,
  12996         .namelen = addressToPosix(message.address, &addr),
  12997         .iov = (&iovec)[0..1],
  12998         .iovlen = 1,
  12999         // OS returns EINVAL if this pointer is invalid even if controllen is zero.
  13000         .control = if (message.control.len == 0) null else @constCast(message.control.ptr),
  13001         .controllen = @intCast(message.control.len),
  13002         .flags = 0,
  13003     };
  13004     var syscall: if (is_windows) AlertableSyscall else Syscall = try .start();
  13005     while (true) {
  13006         const rc = posix.system.sendmsg(handle, &msg, flags);
  13007         if (is_windows) {
  13008             if (rc != ws2_32.SOCKET_ERROR) {
  13009                 syscall.finish();
  13010                 message.data_len = @intCast(rc);
  13011                 return;
  13012             }
  13013             switch (ws2_32.WSAGetLastError()) {
  13014                 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  13015                     try syscall.checkCancel();
  13016                     continue;
  13017                 },
  13018                 .NOTINITIALISED => {
  13019                     syscall.finish();
  13020                     try initializeWsa(t);
  13021                     syscall = try .start();
  13022                     continue;
  13023                 },
  13024                 .EACCES => return syscall.fail(error.AccessDenied),
  13025                 .EADDRNOTAVAIL => return syscall.fail(error.AddressUnavailable),
  13026                 .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  13027                 .EMSGSIZE => return syscall.fail(error.MessageOversize),
  13028                 .ENOBUFS => return syscall.fail(error.SystemResources),
  13029                 .ENOTSOCK => return syscall.fail(error.FileDescriptorNotASocket),
  13030                 .EAFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
  13031                 .EHOSTUNREACH => return syscall.fail(error.NetworkUnreachable),
  13032                 .ENETDOWN => return syscall.fail(error.NetworkDown),
  13033                 .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
  13034                 .ENETUNREACH => return syscall.fail(error.NetworkUnreachable),
  13035                 .ENOTCONN => return syscall.fail(error.SocketUnconnected),
  13036                 .EDESTADDRREQ => unreachable, // A destination address is required.
  13037                 .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.
  13038                 .EINVAL => unreachable,
  13039                 .ESHUTDOWN => |err| return syscall.wsaErrorBug(err),
  13040                 else => |err| return syscall.unexpectedWsaError(err),
  13041             }
  13042         }
  13043         switch (posix.errno(rc)) {
  13044             .SUCCESS => {
  13045                 syscall.finish();
  13046                 message.data_len = @intCast(rc);
  13047                 return;
  13048             },
  13049             .INTR => {
  13050                 try syscall.checkCancel();
  13051                 continue;
  13052             },
  13053             .ACCES => return syscall.fail(error.AccessDenied),
  13054             .ALREADY => return syscall.fail(error.FastOpenAlreadyInProgress),
  13055             .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  13056             .MSGSIZE => return syscall.fail(error.MessageOversize),
  13057             .NOBUFS => return syscall.fail(error.SystemResources),
  13058             .NOMEM => return syscall.fail(error.SystemResources),
  13059             .PIPE => return syscall.fail(error.SocketUnconnected),
  13060             .AFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
  13061             .HOSTUNREACH => return syscall.fail(error.HostUnreachable),
  13062             .NETUNREACH => return syscall.fail(error.NetworkUnreachable),
  13063             .NOTCONN => return syscall.fail(error.SocketUnconnected),
  13064             .NETDOWN => return syscall.fail(error.NetworkDown),
  13065             .BADF => |err| return syscall.errnoBug(err), // File descriptor used after closed.
  13066             .DESTADDRREQ => |err| return syscall.errnoBug(err),
  13067             .FAULT => |err| return syscall.errnoBug(err),
  13068             .INVAL => |err| return syscall.errnoBug(err),
  13069             .ISCONN => |err| return syscall.errnoBug(err),
  13070             .NOTSOCK => |err| return syscall.errnoBug(err),
  13071             .OPNOTSUPP => |err| return syscall.errnoBug(err),
  13072             else => |err| return syscall.unexpectedErrno(err),
  13073         }
  13074     }
  13075 }
  13076 
  13077 fn netSendMany(
  13078     handle: net.Socket.Handle,
  13079     messages: []net.OutgoingMessage,
  13080     flags: u32,
  13081 ) net.Socket.SendError!usize {
  13082     var msg_buffer: [64]posix.system.mmsghdr = undefined;
  13083     var addr_buffer: [msg_buffer.len]PosixAddress = undefined;
  13084     var iovecs_buffer: [msg_buffer.len]posix.iovec = undefined;
  13085     const min_len: usize = @min(messages.len, msg_buffer.len);
  13086     const clamped_messages = messages[0..min_len];
  13087     const clamped_msgs = (&msg_buffer)[0..min_len];
  13088     const clamped_addrs = (&addr_buffer)[0..min_len];
  13089     const clamped_iovecs = (&iovecs_buffer)[0..min_len];
  13090 
  13091     for (clamped_messages, clamped_msgs, clamped_addrs, clamped_iovecs) |*message, *msg, *addr, *iovec| {
  13092         iovec.* = .{ .base = @constCast(message.data_ptr), .len = message.data_len };
  13093         msg.* = .{
  13094             .hdr = .{
  13095                 .name = &addr.any,
  13096                 .namelen = addressToPosix(message.address, addr),
  13097                 .iov = iovec[0..1],
  13098                 .iovlen = 1,
  13099                 .control = @constCast(message.control.ptr),
  13100                 .controllen = message.control.len,
  13101                 .flags = 0,
  13102             },
  13103             .len = undefined, // Populated by calling sendmmsg below.
  13104         };
  13105     }
  13106 
  13107     const syscall: Syscall = try .start();
  13108     while (true) {
  13109         const rc = posix.system.sendmmsg(handle, clamped_msgs.ptr, @intCast(clamped_msgs.len), flags);
  13110         switch (posix.errno(rc)) {
  13111             .SUCCESS => {
  13112                 syscall.finish();
  13113                 const n: usize = @intCast(rc);
  13114                 for (clamped_messages[0..n], clamped_msgs[0..n]) |*message, *msg| {
  13115                     message.data_len = msg.len;
  13116                 }
  13117                 return n;
  13118             },
  13119             .INTR => {
  13120                 try syscall.checkCancel();
  13121                 continue;
  13122             },
  13123             .ACCES => return syscall.fail(error.AccessDenied),
  13124             .ALREADY => return syscall.fail(error.FastOpenAlreadyInProgress),
  13125             .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  13126             .MSGSIZE => return syscall.fail(error.MessageOversize),
  13127             .NOBUFS => return syscall.fail(error.SystemResources),
  13128             .NOMEM => return syscall.fail(error.SystemResources),
  13129             .PIPE => return syscall.fail(error.SocketUnconnected),
  13130             .AFNOSUPPORT => return syscall.fail(error.AddressFamilyUnsupported),
  13131             .HOSTUNREACH => return syscall.fail(error.HostUnreachable),
  13132             .NETUNREACH => return syscall.fail(error.NetworkUnreachable),
  13133             .NOTCONN => return syscall.fail(error.SocketUnconnected),
  13134             .NETDOWN => return syscall.fail(error.NetworkDown),
  13135 
  13136             .AGAIN => |err| return syscall.errnoBug(err),
  13137             .BADF => |err| return syscall.errnoBug(err), // File descriptor used after closed.
  13138             .DESTADDRREQ => |err| return syscall.errnoBug(err), // The socket is not connection-mode, and no peer address is set.
  13139             .FAULT => |err| return syscall.errnoBug(err), // An invalid user space address was specified for an argument.
  13140             .INVAL => |err| return syscall.errnoBug(err), // Invalid argument passed.
  13141             .ISCONN => |err| return syscall.errnoBug(err), // connection-mode socket was connected already but a recipient was specified
  13142             .NOTSOCK => |err| return syscall.errnoBug(err), // The file descriptor sockfd does not refer to a socket.
  13143             .OPNOTSUPP => |err| return syscall.errnoBug(err), // Some bit in the flags argument is inappropriate for the socket type.
  13144 
  13145             else => |err| return syscall.unexpectedErrno(err),
  13146         }
  13147     }
  13148 }
  13149 
  13150 fn netReceivePosix(
  13151     socket_handle: net.Socket.Handle,
  13152     message: *net.IncomingMessage,
  13153     data_buffer: []u8,
  13154     flags: net.ReceiveFlags,
  13155     nonblocking: bool,
  13156 ) (net.Socket.ReceiveError || error{WouldBlock})!void {
  13157     // recvmmsg is useless, here's why:
  13158     // * [timeout bug](https://bugzilla.kernel.org/show_bug.cgi?id=75371)
  13159     // * it wants iovecs for each message but we have a better API: one data
  13160     //   buffer to handle all the messages. The better API cannot be lowered to
  13161     //   the split vectors though because reducing the buffer size might make
  13162     //   some messages unreceivable.
  13163     const posix_flags: u32 =
  13164         @as(u32, if (flags.oob) posix.MSG.OOB else 0) |
  13165         @as(u32, if (flags.peek) posix.MSG.PEEK else 0) |
  13166         @as(u32, if (flags.trunc) posix.MSG.TRUNC else 0) |
  13167         posix.MSG.NOSIGNAL |
  13168         @as(u32, if (nonblocking) posix.MSG.DONTWAIT else 0);
  13169 
  13170     var storage: PosixAddress = undefined;
  13171     var iov: posix.iovec = .{ .base = data_buffer.ptr, .len = data_buffer.len };
  13172     var msg: posix.msghdr = .{
  13173         .name = &storage.any,
  13174         .namelen = @sizeOf(PosixAddress),
  13175         .iov = (&iov)[0..1],
  13176         .iovlen = 1,
  13177         .control = message.control.ptr,
  13178         .controllen = @intCast(message.control.len),
  13179         .flags = undefined,
  13180     };
  13181 
  13182     const syscall = try Syscall.start();
  13183     while (true) {
  13184         const rc = posix.system.recvmsg(socket_handle, &msg, posix_flags);
  13185         switch (posix.errno(rc)) {
  13186             .SUCCESS => {
  13187                 syscall.finish();
  13188                 const data = data_buffer[0..@intCast(rc)];
  13189                 message.* = .{
  13190                     .from = addressFromPosix(&storage),
  13191                     .data = data,
  13192                     .control = if (msg.control) |ptr| @as([*]u8, @ptrCast(ptr))[0..msg.controllen] else message.control,
  13193                     .flags = .{
  13194                         .eor = (msg.flags & posix.MSG.EOR) != 0,
  13195                         .trunc = (msg.flags & posix.MSG.TRUNC) != 0,
  13196                         .ctrunc = (msg.flags & posix.MSG.CTRUNC) != 0,
  13197                         .oob = (msg.flags & posix.MSG.OOB) != 0,
  13198                         .errqueue = if (@hasDecl(posix.MSG, "ERRQUEUE")) (msg.flags & posix.MSG.ERRQUEUE) != 0 else false,
  13199                     },
  13200                 };
  13201                 return;
  13202             },
  13203             .INTR => {
  13204                 try syscall.checkCancel();
  13205                 continue;
  13206             },
  13207             .NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
  13208             .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
  13209             .NOBUFS => return syscall.fail(error.SystemResources),
  13210             .NOMEM => return syscall.fail(error.SystemResources),
  13211             .NOTCONN => return syscall.fail(error.SocketUnconnected),
  13212             .MSGSIZE => return syscall.fail(error.MessageOversize),
  13213             .PIPE => return syscall.fail(error.SocketUnconnected),
  13214             .CONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  13215             .NETDOWN => return syscall.fail(error.NetworkDown),
  13216             .AGAIN => return syscall.fail(error.WouldBlock),
  13217             .BADF => |err| return syscall.errnoBug(err),
  13218             .FAULT => |err| return syscall.errnoBug(err),
  13219             .INVAL => |err| return syscall.errnoBug(err),
  13220             .NOTSOCK => |err| return syscall.errnoBug(err),
  13221             .OPNOTSUPP => |err| return syscall.errnoBug(err),
  13222             else => |err| return syscall.unexpectedErrno(err),
  13223         }
  13224     }
  13225 }
  13226 
  13227 fn netReceiveWindows(
  13228     t: *Threaded,
  13229     socket_handle: net.Socket.Handle,
  13230     message_buffer: []net.IncomingMessage,
  13231     data_buffer: []u8,
  13232     flags: net.ReceiveFlags,
  13233 ) struct { ?net.Socket.ReceiveError, usize } {
  13234     netReceiveWindowsOne(t, socket_handle, &message_buffer[0], data_buffer, flags) catch |err| return .{ err, 0 };
  13235     return .{ null, 1 };
  13236 }
  13237 
  13238 fn netReceiveWindowsOne(
  13239     t: *Threaded,
  13240     socket_handle: net.Socket.Handle,
  13241     message: *net.IncomingMessage,
  13242     data_buffer: []u8,
  13243     flags: net.ReceiveFlags,
  13244 ) net.Socket.ReceiveError!void {
  13245     if (!have_networking) return error.NetworkDown;
  13246 
  13247     var windows_flags: u32 =
  13248         @as(u32, if (flags.oob) ws2_32.MSG.OOB else 0) |
  13249         @as(u32, if (flags.peek) ws2_32.MSG.PEEK else 0) |
  13250         @as(u32, if (flags.trunc) ws2_32.MSG.TRUNC else 0);
  13251 
  13252     var buf: ws2_32.WSABUF = .{
  13253         .buf = data_buffer.ptr,
  13254         .len = std.math.cast(u32, data_buffer.len) orelse return error.MessageOversize,
  13255     };
  13256     var n: u32 = undefined;
  13257     var from_storage: WsaAddress = undefined;
  13258     var from_storage_len: i32 = @sizeOf(WsaAddress);
  13259     var syscall: AlertableSyscall = try .start();
  13260 
  13261     while (true) {
  13262         const rc = ws2_32.WSARecvFrom(
  13263             socket_handle,
  13264             (&buf)[0..1],
  13265             1,
  13266             &n,
  13267             &windows_flags,
  13268             &from_storage.any,
  13269             &from_storage_len,
  13270             null,
  13271             null,
  13272         );
  13273         if (rc != ws2_32.SOCKET_ERROR) {
  13274             syscall.finish();
  13275             message.* = .{
  13276                 .from = addressFromWsa(&from_storage),
  13277                 .data = data_buffer[0..n],
  13278                 .control = &.{},
  13279                 .flags = .{
  13280                     .eor = false,
  13281                     .trunc = (windows_flags & ws2_32.MSG.TRUNC) != 0,
  13282                     .ctrunc = (windows_flags & ws2_32.MSG.CTRUNC) != 0,
  13283                     .oob = false,
  13284                     .errqueue = false,
  13285                 },
  13286             };
  13287             return;
  13288         }
  13289         switch (ws2_32.WSAGetLastError()) {
  13290             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  13291                 try syscall.checkCancel();
  13292                 continue;
  13293             },
  13294             .NOTINITIALISED => {
  13295                 syscall.finish();
  13296                 try initializeWsa(t);
  13297                 syscall = try .start();
  13298                 continue;
  13299             },
  13300             .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  13301             .ENETDOWN => return syscall.fail(error.NetworkDown),
  13302             .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
  13303             .ENOTCONN => return syscall.fail(error.SocketUnconnected),
  13304             .EFAULT => unreachable, // a pointer is not completely contained in user address space.
  13305             .EINVAL => |err| return syscall.wsaErrorBug(err),
  13306             .EMSGSIZE => |err| return syscall.wsaErrorBug(err),
  13307             else => |err| return syscall.unexpectedWsaError(err),
  13308         }
  13309     }
  13310 }
  13311 
  13312 fn netWritePosix(
  13313     userdata: ?*anyopaque,
  13314     fd: net.Socket.Handle,
  13315     header: []const u8,
  13316     data: []const []const u8,
  13317     splat: usize,
  13318 ) net.Stream.Writer.Error!usize {
  13319     if (!have_networking) return error.NetworkDown;
  13320     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13321     _ = t;
  13322 
  13323     var iovecs: [max_iovecs_len]posix.iovec_const = undefined;
  13324     var msg: posix.msghdr_const = .{
  13325         .name = null,
  13326         .namelen = 0,
  13327         .iov = &iovecs,
  13328         .iovlen = 0,
  13329         .control = null,
  13330         .controllen = 0,
  13331         .flags = 0,
  13332     };
  13333     addBuf(&iovecs, &msg.iovlen, header);
  13334     for (data[0 .. data.len - 1]) |bytes| addBuf(&iovecs, &msg.iovlen, bytes);
  13335     const pattern = data[data.len - 1];
  13336 
  13337     var splat_backup_buffer: [splat_buffer_size]u8 = undefined;
  13338     if (iovecs.len - msg.iovlen != 0) switch (splat) {
  13339         0 => {},
  13340         1 => addBuf(&iovecs, &msg.iovlen, pattern),
  13341         else => switch (pattern.len) {
  13342             0 => {},
  13343             1 => {
  13344                 const splat_buffer = &splat_backup_buffer;
  13345                 const memset_len = @min(splat_buffer.len, splat);
  13346                 const buf = splat_buffer[0..memset_len];
  13347                 @memset(buf, pattern[0]);
  13348                 addBuf(&iovecs, &msg.iovlen, buf);
  13349                 var remaining_splat = splat - buf.len;
  13350                 while (remaining_splat > splat_buffer.len and iovecs.len - msg.iovlen != 0) {
  13351                     assert(buf.len == splat_buffer.len);
  13352                     addBuf(&iovecs, &msg.iovlen, splat_buffer);
  13353                     remaining_splat -= splat_buffer.len;
  13354                 }
  13355                 addBuf(&iovecs, &msg.iovlen, splat_buffer[0..@min(remaining_splat, splat_buffer.len)]);
  13356             },
  13357             else => for (0..@min(splat, iovecs.len - msg.iovlen)) |_| {
  13358                 addBuf(&iovecs, &msg.iovlen, pattern);
  13359             },
  13360         },
  13361     };
  13362     const flags = posix.MSG.NOSIGNAL;
  13363 
  13364     const syscall: Syscall = try .start();
  13365     while (true) {
  13366         const rc = posix.system.sendmsg(fd, &msg, flags);
  13367         switch (posix.errno(rc)) {
  13368             .SUCCESS => {
  13369                 syscall.finish();
  13370                 return @intCast(rc);
  13371             },
  13372             .INTR => {
  13373                 try syscall.checkCancel();
  13374                 continue;
  13375             },
  13376             else => |e| {
  13377                 syscall.finish();
  13378                 switch (e) {
  13379                     .ACCES => |err| return errnoBug(err),
  13380                     .AGAIN => |err| return errnoBug(err),
  13381                     .ALREADY => return error.FastOpenAlreadyInProgress,
  13382                     .BADF => |err| return errnoBug(err), // File descriptor used after closed.
  13383                     .CONNRESET => return error.ConnectionResetByPeer,
  13384                     .DESTADDRREQ => |err| return errnoBug(err), // The socket is not connection-mode, and no peer address is set.
  13385                     .FAULT => |err| return errnoBug(err), // An invalid user space address was specified for an argument.
  13386                     .INVAL => |err| return errnoBug(err), // Invalid argument passed.
  13387                     .ISCONN => |err| return errnoBug(err), // connection-mode socket was connected already but a recipient was specified
  13388                     .MSGSIZE => |err| return errnoBug(err),
  13389                     .NOBUFS => return error.SystemResources,
  13390                     .NOMEM => return error.SystemResources,
  13391                     .NOTSOCK => |err| return errnoBug(err), // The file descriptor sockfd does not refer to a socket.
  13392                     .OPNOTSUPP => |err| return errnoBug(err), // Some bit in the flags argument is inappropriate for the socket type.
  13393                     .PIPE => return error.SocketUnconnected,
  13394                     .AFNOSUPPORT => return error.AddressFamilyUnsupported,
  13395                     .HOSTUNREACH => return error.HostUnreachable,
  13396                     .NETUNREACH => return error.NetworkUnreachable,
  13397                     .NOTCONN => return error.SocketUnconnected,
  13398                     .NETDOWN => return error.NetworkDown,
  13399                     else => |err| return posix.unexpectedErrno(err),
  13400                 }
  13401             },
  13402         }
  13403     }
  13404 }
  13405 
  13406 fn netWriteWindows(
  13407     userdata: ?*anyopaque,
  13408     handle: net.Socket.Handle,
  13409     header: []const u8,
  13410     data: []const []const u8,
  13411     splat: usize,
  13412 ) net.Stream.Writer.Error!usize {
  13413     if (!have_networking) return error.NetworkDown;
  13414     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13415     comptime assert(is_windows);
  13416 
  13417     var iovecs: [max_iovecs_len]ws2_32.WSABUF = undefined;
  13418     var len: u32 = 0;
  13419     addWsaBuf(&iovecs, &len, header);
  13420     for (data[0 .. data.len - 1]) |bytes| addWsaBuf(&iovecs, &len, bytes);
  13421     const pattern = data[data.len - 1];
  13422     var backup_buffer: [64]u8 = undefined;
  13423     if (iovecs.len - len != 0) switch (splat) {
  13424         0 => {},
  13425         1 => addWsaBuf(&iovecs, &len, pattern),
  13426         else => switch (pattern.len) {
  13427             0 => {},
  13428             1 => {
  13429                 const splat_buffer = &backup_buffer;
  13430                 const memset_len = @min(splat_buffer.len, splat);
  13431                 const buf = splat_buffer[0..memset_len];
  13432                 @memset(buf, pattern[0]);
  13433                 addWsaBuf(&iovecs, &len, buf);
  13434                 var remaining_splat = splat - buf.len;
  13435                 while (remaining_splat > splat_buffer.len and len < iovecs.len) {
  13436                     addWsaBuf(&iovecs, &len, splat_buffer);
  13437                     remaining_splat -= splat_buffer.len;
  13438                 }
  13439                 addWsaBuf(&iovecs, &len, splat_buffer[0..@min(remaining_splat, splat_buffer.len)]);
  13440             },
  13441             else => for (0..@min(splat, iovecs.len - len)) |_| {
  13442                 addWsaBuf(&iovecs, &len, pattern);
  13443             },
  13444         },
  13445     };
  13446 
  13447     var syscall: AlertableSyscall = try .start();
  13448     while (true) {
  13449         var n: u32 = undefined;
  13450         const rc = ws2_32.WSASend(handle, &iovecs, len, &n, 0, null, null);
  13451         if (rc != ws2_32.SOCKET_ERROR) {
  13452             syscall.finish();
  13453             return n;
  13454         }
  13455         switch (ws2_32.WSAGetLastError()) {
  13456             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  13457                 try syscall.checkCancel();
  13458                 continue;
  13459             },
  13460             .NOTINITIALISED => {
  13461                 syscall.finish();
  13462                 try initializeWsa(t);
  13463                 syscall = try .start();
  13464                 continue;
  13465             },
  13466 
  13467             .ECONNABORTED => return syscall.fail(error.ConnectionResetByPeer),
  13468             .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  13469             .EINVAL => return syscall.fail(error.SocketUnconnected),
  13470             .ENETDOWN => return syscall.fail(error.NetworkDown),
  13471             .ENETRESET => return syscall.fail(error.ConnectionResetByPeer),
  13472             .ENOBUFS => return syscall.fail(error.SystemResources),
  13473             .ENOTCONN => return syscall.fail(error.SocketUnconnected),
  13474             .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  13475             .EOPNOTSUPP => |err| return syscall.wsaErrorBug(err),
  13476             .ESHUTDOWN => |err| return syscall.wsaErrorBug(err),
  13477             .IO_PENDING => |err| return syscall.wsaErrorBug(err),
  13478             else => |err| return syscall.unexpectedWsaError(err),
  13479         }
  13480     }
  13481 }
  13482 
  13483 fn addWsaBuf(v: []ws2_32.WSABUF, i: *u32, bytes: []const u8) void {
  13484     const cap = std.math.maxInt(u32);
  13485     var remaining = bytes;
  13486     while (remaining.len > cap) {
  13487         if (v.len - i.* == 0) return;
  13488         v[i.*] = .{ .buf = @constCast(remaining.ptr), .len = cap };
  13489         i.* += 1;
  13490         remaining = remaining[cap..];
  13491     } else {
  13492         @branchHint(.likely);
  13493         if (v.len - i.* == 0) return;
  13494         v[i.*] = .{ .buf = @constCast(remaining.ptr), .len = @intCast(remaining.len) };
  13495         i.* += 1;
  13496     }
  13497 }
  13498 
  13499 fn netWriteUnavailable(
  13500     userdata: ?*anyopaque,
  13501     handle: net.Socket.Handle,
  13502     header: []const u8,
  13503     data: []const []const u8,
  13504     splat: usize,
  13505 ) net.Stream.Writer.Error!usize {
  13506     _ = userdata;
  13507     _ = handle;
  13508     _ = header;
  13509     _ = data;
  13510     _ = splat;
  13511     return error.NetworkDown;
  13512 }
  13513 
  13514 /// This is either usize or u32. Since, either is fine, let's use the same
  13515 /// `addBuf` function for both writing to a file and sending network messages.
  13516 const iovlen_t = switch (native_os) {
  13517     .wasi => u32,
  13518     else => @FieldType(posix.msghdr_const, "iovlen"),
  13519 };
  13520 
  13521 fn addBuf(v: []posix.iovec_const, i: *iovlen_t, bytes: []const u8) void {
  13522     // OS checks ptr addr before length so zero length vectors must be omitted.
  13523     if (bytes.len == 0) return;
  13524     if (v.len - i.* == 0) return;
  13525     v[i.*] = .{ .base = bytes.ptr, .len = bytes.len };
  13526     i.* += 1;
  13527 }
  13528 
  13529 fn netClose(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
  13530     if (!have_networking) unreachable;
  13531     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13532     _ = t;
  13533     switch (native_os) {
  13534         .windows => for (handles) |handle| closeSocketWindows(handle),
  13535         else => for (handles) |handle| closeFd(handle),
  13536     }
  13537 }
  13538 
  13539 fn netCloseUnavailable(userdata: ?*anyopaque, handles: []const net.Socket.Handle) void {
  13540     _ = userdata;
  13541     _ = handles;
  13542     unreachable; // How you gonna close something that was impossible to open?
  13543 }
  13544 
  13545 fn netShutdownPosix(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
  13546     if (!have_networking) return error.NetworkDown;
  13547     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13548     _ = t;
  13549 
  13550     const posix_how: i32 = switch (how) {
  13551         .recv => posix.SHUT.RD,
  13552         .send => posix.SHUT.WR,
  13553         .both => posix.SHUT.RDWR,
  13554     };
  13555 
  13556     const syscall: Syscall = try .start();
  13557     while (true) {
  13558         switch (posix.errno(posix.system.shutdown(handle, posix_how))) {
  13559             .SUCCESS => return syscall.finish(),
  13560             .INTR => {
  13561                 try syscall.checkCancel();
  13562                 continue;
  13563             },
  13564             else => |e| {
  13565                 syscall.finish();
  13566                 switch (e) {
  13567                     .BADF, .NOTSOCK, .INVAL => |err| return errnoBug(err),
  13568                     .NOTCONN => return error.SocketUnconnected,
  13569                     .NOBUFS => return error.SystemResources,
  13570                     else => |err| return posix.unexpectedErrno(err),
  13571                 }
  13572             },
  13573         }
  13574     }
  13575 }
  13576 
  13577 fn netShutdownWindows(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
  13578     if (!have_networking) return error.NetworkDown;
  13579     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13580 
  13581     const wsa_how: i32 = switch (how) {
  13582         .recv => ws2_32.SD_RECEIVE,
  13583         .send => ws2_32.SD_SEND,
  13584         .both => ws2_32.SD_BOTH,
  13585     };
  13586 
  13587     var syscall: AlertableSyscall = try .start();
  13588     while (true) {
  13589         const rc = ws2_32.shutdown(handle, wsa_how);
  13590         if (rc != ws2_32.SOCKET_ERROR) {
  13591             syscall.finish();
  13592             return;
  13593         }
  13594         switch (ws2_32.WSAGetLastError()) {
  13595             .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => {
  13596                 try syscall.checkCancel();
  13597                 continue;
  13598             },
  13599             .NOTINITIALISED => {
  13600                 syscall.finish();
  13601                 try initializeWsa(t);
  13602                 syscall = try .start();
  13603                 continue;
  13604             },
  13605             .ECONNABORTED => return syscall.fail(error.ConnectionAborted),
  13606             .ECONNRESET => return syscall.fail(error.ConnectionResetByPeer),
  13607             .ENETDOWN => return syscall.fail(error.NetworkDown),
  13608             .ENOTCONN => return syscall.fail(error.SocketUnconnected),
  13609             .EINVAL, .ENOTSOCK => |err| return syscall.wsaErrorBug(err),
  13610             else => |err| return syscall.unexpectedWsaError(err),
  13611         }
  13612     }
  13613 }
  13614 
  13615 fn netShutdownUnavailable(_: ?*anyopaque, _: net.Socket.Handle, _: net.ShutdownHow) net.ShutdownError!void {
  13616     unreachable; // How you gonna shutdown something that was impossible to open?
  13617 }
  13618 
  13619 fn netInterfaceNameResolve(
  13620     userdata: ?*anyopaque,
  13621     name: *const net.Interface.Name,
  13622 ) net.Interface.Name.ResolveError!net.Interface {
  13623     if (!have_networking) return error.InterfaceNotFound;
  13624     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13625     _ = t;
  13626 
  13627     if (native_os == .linux) {
  13628         const sock_fd = openSocketPosix(posix.AF.UNIX, .{ .mode = .dgram }) catch |err| switch (err) {
  13629             error.ProcessFdQuotaExceeded => return error.SystemResources,
  13630             error.SystemFdQuotaExceeded => return error.SystemResources,
  13631             error.AddressFamilyUnsupported => return error.Unexpected,
  13632             error.ProtocolUnsupportedBySystem => return error.Unexpected,
  13633             error.ProtocolUnsupportedByAddressFamily => return error.Unexpected,
  13634             error.SocketModeUnsupported => return error.Unexpected,
  13635             error.OptionUnsupported => return error.Unexpected,
  13636             else => |e| return e,
  13637         };
  13638         defer closeFd(sock_fd);
  13639 
  13640         var ifr: posix.ifreq = .{
  13641             .ifrn = .{ .name = @bitCast(name.bytes) },
  13642             .ifru = undefined,
  13643         };
  13644 
  13645         const syscall: Syscall = try .start();
  13646         while (true) switch (posix.errno(posix.system.ioctl(sock_fd, posix.SIOCGIFINDEX, @intFromPtr(&ifr)))) {
  13647             .SUCCESS => {
  13648                 syscall.finish();
  13649                 return .{ .index = @bitCast(ifr.ifru.ivalue) };
  13650             },
  13651             .INTR => {
  13652                 try syscall.checkCancel();
  13653                 continue;
  13654             },
  13655             .NODEV => return syscall.fail(error.InterfaceNotFound),
  13656             else => |err| return syscall.unexpectedErrno(err),
  13657         };
  13658     }
  13659 
  13660     if (is_windows) {
  13661         try Thread.checkCancel();
  13662         @panic("TODO implement netInterfaceNameResolve for Windows");
  13663     }
  13664 
  13665     if (builtin.link_libc) {
  13666         try Thread.checkCancel();
  13667         const index = std.c.if_nametoindex(&name.bytes);
  13668         if (index == 0) return error.InterfaceNotFound;
  13669         return .{ .index = @bitCast(index) };
  13670     }
  13671 
  13672     @panic("unimplemented");
  13673 }
  13674 
  13675 fn netInterfaceNameResolveUnavailable(
  13676     userdata: ?*anyopaque,
  13677     name: *const net.Interface.Name,
  13678 ) net.Interface.Name.ResolveError!net.Interface {
  13679     _ = userdata;
  13680     _ = name;
  13681     return error.InterfaceNotFound;
  13682 }
  13683 
  13684 fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
  13685     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13686     _ = t;
  13687     try Thread.checkCancel();
  13688 
  13689     if (native_os == .linux) {
  13690         _ = interface;
  13691         @panic("TODO implement netInterfaceName for linux");
  13692     }
  13693 
  13694     if (is_windows) {
  13695         @panic("TODO implement netInterfaceName for windows");
  13696     }
  13697 
  13698     if (builtin.link_libc) {
  13699         @panic("TODO implement netInterfaceName for libc");
  13700     }
  13701 
  13702     @panic("unimplemented");
  13703 }
  13704 
  13705 fn netInterfaceNameUnavailable(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
  13706     _ = userdata;
  13707     _ = interface;
  13708     return error.Unexpected;
  13709 }
  13710 
  13711 fn netLookup(
  13712     userdata: ?*anyopaque,
  13713     host_name: HostName,
  13714     resolved: *Io.Queue(HostName.LookupResult),
  13715     options: HostName.LookupOptions,
  13716 ) net.HostName.LookupError!void {
  13717     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13718     defer resolved.close(io(t));
  13719     netLookupFallible(t, host_name, resolved, options) catch |err| switch (err) {
  13720         error.Closed => unreachable, // `resolved` must not be closed until `netLookup` returns
  13721         else => |e| return e,
  13722     };
  13723 }
  13724 
  13725 fn netLookupUnavailable(
  13726     userdata: ?*anyopaque,
  13727     host_name: HostName,
  13728     resolved: *Io.Queue(HostName.LookupResult),
  13729     options: HostName.LookupOptions,
  13730 ) net.HostName.LookupError!void {
  13731     _ = host_name;
  13732     _ = options;
  13733     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13734     resolved.close(io(t));
  13735     return error.NetworkDown;
  13736 }
  13737 
  13738 fn netLookupFallible(
  13739     t: *Threaded,
  13740     host_name: HostName,
  13741     resolved: *Io.Queue(HostName.LookupResult),
  13742     options: HostName.LookupOptions,
  13743 ) (net.HostName.LookupError || Io.QueueClosedError)!void {
  13744     if (!have_networking) return error.NetworkDown;
  13745 
  13746     const t_io = io(t);
  13747     const name = host_name.bytes;
  13748     assert(name.len <= HostName.max_len);
  13749 
  13750     if (is_windows) {
  13751         var name_buffer: [HostName.max_len + 1]u16 = undefined;
  13752         const name_len = std.unicode.wtf8ToWtf16Le(&name_buffer, host_name.bytes) catch
  13753             unreachable; // HostName is prevalidated.
  13754         name_buffer[name_len] = 0;
  13755         const name_w = name_buffer[0..name_len :0];
  13756 
  13757         var port_buffer: [8]u8 = undefined;
  13758         var port_buffer_wide: [8]u16 = undefined;
  13759         const port = std.fmt.bufPrint(&port_buffer, "{d}", .{options.port}) catch
  13760             unreachable; // `port_buffer` is big enough for decimal u16.
  13761         for (port, port_buffer_wide[0..port.len]) |byte, *wide|
  13762             wide.* = std.mem.nativeToLittle(u16, byte);
  13763         port_buffer_wide[port.len] = 0;
  13764         const port_w = port_buffer_wide[0..port.len :0];
  13765 
  13766         const hints: ws2_32.ADDRINFOEXW = .{
  13767             .flags = .{ .NUMERICSERV = true },
  13768             .family = if (options.family) |f| switch (f) {
  13769                 .ip4 => posix.AF.INET,
  13770                 .ip6 => posix.AF.INET6,
  13771             } else posix.AF.UNSPEC,
  13772             .socktype = posix.SOCK.STREAM,
  13773             .protocol = posix.IPPROTO.TCP,
  13774             .canonname = null,
  13775             .addr = null,
  13776             .addrlen = 0,
  13777             .blob = null,
  13778             .bloblen = 0,
  13779             .provider = null,
  13780             .next = null,
  13781         };
  13782         var res: *ws2_32.ADDRINFOEXW = undefined;
  13783         const timeout: ?*ws2_32.timeval = null;
  13784         while (true) {
  13785             // TODO: hook this up to cancelation with `NtDelayExecution` and APC callbacks.
  13786             try Thread.checkCancel();
  13787             // TODO make this append to the queue eagerly rather than blocking until the whole thing finishes
  13788             const rc: ws2_32.WinsockError = @enumFromInt(ws2_32.GetAddrInfoExW(name_w, port_w, .DNS, null, &hints, &res, timeout, null, null, null));
  13789             switch (rc) {
  13790                 @as(ws2_32.WinsockError, @enumFromInt(0)) => break,
  13791                 .EINTR, .ECANCELLED, .E_CANCELLED, .OPERATION_ABORTED => continue,
  13792                 .NOTINITIALISED => {
  13793                     try initializeWsa(t);
  13794                     continue;
  13795                 },
  13796                 .TRY_AGAIN => return error.NameServerFailure,
  13797                 .NO_RECOVERY => return error.NameServerFailure,
  13798                 .EAFNOSUPPORT => return error.AddressFamilyUnsupported,
  13799                 .NOT_ENOUGH_MEMORY => return error.SystemResources,
  13800                 .HOST_NOT_FOUND => return error.UnknownHostName,
  13801                 .TYPE_NOT_FOUND => return error.ProtocolUnsupportedByAddressFamily,
  13802                 .ESOCKTNOSUPPORT => return error.ProtocolUnsupportedBySystem,
  13803                 .EINVAL => |err| return windows.wsaErrorBug(err),
  13804                 else => |err| return windows.unexpectedWsaError(err),
  13805             }
  13806         }
  13807         defer ws2_32.FreeAddrInfoExW(res);
  13808 
  13809         var it: ?*ws2_32.ADDRINFOEXW = res;
  13810         var canon_name: ?[*:0]const u16 = null;
  13811         while (it) |info| : (it = info.next) {
  13812             const addr = info.addr orelse continue;
  13813             try resolved.putOne(t_io, .{ .address = addressFromWsa(@alignCast(@fieldParentPtr("any", addr))) });
  13814 
  13815             if (info.canonname) |n| {
  13816                 if (canon_name == null) {
  13817                     canon_name = n;
  13818                 }
  13819             }
  13820         }
  13821         if (canon_name) |n| {
  13822             const len = std.unicode.wtf16LeToWtf8(options.canonical_name_buffer, std.mem.sliceTo(n, 0));
  13823             try resolved.putOne(t_io, .{ .canonical_name = .{
  13824                 .bytes = options.canonical_name_buffer[0..len],
  13825             } });
  13826         }
  13827         return;
  13828     }
  13829 
  13830     // On Linux, glibc provides getaddrinfo_a which is capable of supporting our semantics.
  13831     // However, musl's POSIX-compliant getaddrinfo is not, so we bypass it.
  13832 
  13833     if (builtin.target.isGnuLibC()) {
  13834         // TODO use getaddrinfo_a / gai_cancel
  13835     }
  13836 
  13837     if (native_os == .linux) {
  13838         if (options.family != .ip4) {
  13839             if (IpAddress.parseIp6(name, options.port)) |addr| {
  13840                 try resolved.putAll(t_io, &.{
  13841                     .{ .address = addr },
  13842                     .{ .canonical_name = copyCanon(options.canonical_name_buffer, name) },
  13843                 });
  13844                 return;
  13845             } else |_| {}
  13846         }
  13847 
  13848         if (options.family != .ip6) {
  13849             if (IpAddress.parseIp4(name, options.port)) |addr| {
  13850                 try resolved.putAll(t_io, &.{
  13851                     .{ .address = addr },
  13852                     .{ .canonical_name = copyCanon(options.canonical_name_buffer, name) },
  13853                 });
  13854                 return;
  13855             } else |_| {}
  13856         }
  13857 
  13858         lookupHosts(t, host_name, resolved, options) catch |err| switch (err) {
  13859             error.UnknownHostName => {},
  13860             else => |e| return e,
  13861         };
  13862 
  13863         // RFC 6761 Section 6.3.3
  13864         // Name resolution APIs and libraries SHOULD recognize
  13865         // localhost names as special and SHOULD always return the IP
  13866         // loopback address for address queries and negative responses
  13867         // for all other query types.
  13868 
  13869         // Check for equal to "localhost(.)" or ends in ".localhost(.)"
  13870         const localhost = if (name[name.len - 1] == '.') "localhost." else "localhost";
  13871         if (std.mem.endsWith(u8, name, localhost) and
  13872             (name.len == localhost.len or name[name.len - localhost.len] == '.'))
  13873         {
  13874             var results_buffer: [3]HostName.LookupResult = undefined;
  13875             var results_index: usize = 0;
  13876             if (options.family != .ip4) {
  13877                 results_buffer[results_index] = .{ .address = .{ .ip6 = .loopback(options.port) } };
  13878                 results_index += 1;
  13879             }
  13880             if (options.family != .ip6) {
  13881                 results_buffer[results_index] = .{ .address = .{ .ip4 = .loopback(options.port) } };
  13882                 results_index += 1;
  13883             }
  13884             const canon_name = "localhost";
  13885             const canon_name_dest = options.canonical_name_buffer[0..canon_name.len];
  13886             canon_name_dest.* = canon_name.*;
  13887             results_buffer[results_index] = .{ .canonical_name = .{ .bytes = canon_name_dest } };
  13888             results_index += 1;
  13889             try resolved.putAll(t_io, results_buffer[0..results_index]);
  13890             return;
  13891         }
  13892 
  13893         return lookupDnsSearch(t, host_name, resolved, options);
  13894     }
  13895 
  13896     if (native_os == .openbsd) {
  13897         // TODO use getaddrinfo_async / asr_abort
  13898     }
  13899 
  13900     if (native_os == .freebsd) {
  13901         // TODO use dnsres_getaddrinfo
  13902     }
  13903 
  13904     if (is_darwin) {
  13905         // TODO use CFHostStartInfoResolution / CFHostCancelInfoResolution
  13906     }
  13907 
  13908     if (builtin.link_libc) {
  13909         // This operating system lacks a way to resolve asynchronously. We are
  13910         // stuck with getaddrinfo.
  13911         var name_buffer: [HostName.max_len + 1]u8 = undefined;
  13912         @memcpy(name_buffer[0..host_name.bytes.len], host_name.bytes);
  13913         name_buffer[host_name.bytes.len] = 0;
  13914         const name_c = name_buffer[0..host_name.bytes.len :0];
  13915 
  13916         var port_buffer: [8]u8 = undefined;
  13917         const port_c = std.fmt.bufPrintZ(&port_buffer, "{d}", .{options.port}) catch unreachable;
  13918 
  13919         const hints: posix.addrinfo = .{
  13920             .flags = .{ .NUMERICSERV = true },
  13921             .family = posix.AF.UNSPEC,
  13922             .socktype = posix.SOCK.STREAM,
  13923             .protocol = posix.IPPROTO.TCP,
  13924             .canonname = null,
  13925             .addr = null,
  13926             .addrlen = 0,
  13927             .next = null,
  13928         };
  13929         var res: ?*posix.addrinfo = null;
  13930         const syscall: Syscall = try .start();
  13931         while (true) {
  13932             switch (posix.system.getaddrinfo(name_c.ptr, port_c.ptr, &hints, &res)) {
  13933                 @as(posix.system.EAI, @enumFromInt(0)) => {
  13934                     syscall.finish();
  13935                     break;
  13936                 },
  13937                 .SYSTEM => switch (posix.errno(-1)) {
  13938                     .INTR => {
  13939                         try syscall.checkCancel();
  13940                         continue;
  13941                     },
  13942                     else => |e| {
  13943                         syscall.finish();
  13944                         return posix.unexpectedErrno(e);
  13945                     },
  13946                 },
  13947                 else => |e| {
  13948                     syscall.finish();
  13949                     switch (e) {
  13950                         .ADDRFAMILY => return error.AddressFamilyUnsupported,
  13951                         .AGAIN => return error.NameServerFailure,
  13952                         .FAIL => return error.NameServerFailure,
  13953                         .FAMILY => return error.AddressFamilyUnsupported,
  13954                         .MEMORY => return error.SystemResources,
  13955                         .NODATA => return error.UnknownHostName,
  13956                         .NONAME => return error.UnknownHostName,
  13957                         else => return error.Unexpected,
  13958                     }
  13959                 },
  13960             }
  13961         }
  13962         defer if (res) |some| posix.system.freeaddrinfo(some);
  13963 
  13964         var it = res;
  13965         var canon_name: ?[*:0]const u8 = null;
  13966         while (it) |info| : (it = info.next) {
  13967             const addr = info.addr orelse continue;
  13968             try resolved.putOne(t_io, .{ .address = addressFromPosix(@alignCast(@fieldParentPtr("any", addr))) });
  13969 
  13970             if (info.canonname) |n| {
  13971                 if (canon_name == null) {
  13972                     canon_name = n;
  13973                 }
  13974             }
  13975         }
  13976         if (canon_name) |n| {
  13977             try resolved.putOne(t_io, .{
  13978                 .canonical_name = copyCanon(options.canonical_name_buffer, std.mem.sliceTo(n, 0)),
  13979             });
  13980         }
  13981         return;
  13982     }
  13983 
  13984     return error.OptionUnsupported;
  13985 }
  13986 
  13987 fn lockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr {
  13988     const t: *Threaded = @ptrCast(@alignCast(userdata));
  13989     const current_thread_id = Thread.currentId();
  13990 
  13991     if (@atomicLoad(std.Thread.Id, &t.stderr_mutex_locker, .unordered) != current_thread_id) {
  13992         mutexLock(&t.stderr_mutex);
  13993         assert(t.stderr_mutex_lock_count == 0);
  13994         @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, current_thread_id, .unordered);
  13995     }
  13996     t.stderr_mutex_lock_count += 1;
  13997 
  13998     return initLockedStderr(t, terminal_mode);
  13999 }
  14000 
  14001 fn tryLockStderr(userdata: ?*anyopaque, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!?Io.LockedStderr {
  14002     const t: *Threaded = @ptrCast(@alignCast(userdata));
  14003     const current_thread_id = Thread.currentId();
  14004 
  14005     if (@atomicLoad(std.Thread.Id, &t.stderr_mutex_locker, .unordered) != current_thread_id) {
  14006         if (!t.stderr_mutex.tryLock()) return null;
  14007         assert(t.stderr_mutex_lock_count == 0);
  14008         @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, current_thread_id, .unordered);
  14009     }
  14010     t.stderr_mutex_lock_count += 1;
  14011 
  14012     return try initLockedStderr(t, terminal_mode);
  14013 }
  14014 
  14015 fn initLockedStderr(t: *Threaded, terminal_mode: ?Io.Terminal.Mode) Io.Cancelable!Io.LockedStderr {
  14016     if (!t.stderr_writer_initialized) {
  14017         const io_t = io(t);
  14018         if (is_windows) t.stderr_writer.file = .stderr();
  14019         t.stderr_writer.io = io_t;
  14020         t.stderr_writer_initialized = true;
  14021         t.scanEnviron();
  14022         const NO_COLOR = t.environ.exist.NO_COLOR;
  14023         const CLICOLOR_FORCE = t.environ.exist.CLICOLOR_FORCE;
  14024         t.stderr_mode = terminal_mode orelse try .detect(io_t, t.stderr_writer.file, NO_COLOR, CLICOLOR_FORCE);
  14025     }
  14026     return .{
  14027         .file_writer = &t.stderr_writer,
  14028         .terminal_mode = terminal_mode orelse t.stderr_mode,
  14029     };
  14030 }
  14031 
  14032 fn unlockStderr(userdata: ?*anyopaque) void {
  14033     const t: *Threaded = @ptrCast(@alignCast(userdata));
  14034     if (t.stderr_writer.err == null) t.stderr_writer.interface.flush() catch {};
  14035     if (t.stderr_writer.err) |err| {
  14036         switch (err) {
  14037             error.Canceled => recancelInner(),
  14038             else => {},
  14039         }
  14040         t.stderr_writer.err = null;
  14041     }
  14042     t.stderr_writer.interface.end = 0;
  14043     t.stderr_writer.interface.buffer = &.{};
  14044 
  14045     t.stderr_mutex_lock_count -= 1;
  14046     if (t.stderr_mutex_lock_count == 0) {
  14047         @atomicStore(std.Thread.Id, &t.stderr_mutex_locker, Thread.invalid_id, .unordered);
  14048         mutexUnlock(&t.stderr_mutex);
  14049     }
  14050 }
  14051 
  14052 fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) process.CurrentPathError!usize {
  14053     const t: *Threaded = @ptrCast(@alignCast(userdata));
  14054     _ = t;
  14055     if (is_windows) {
  14056         var wtf16le_buf: [windows.PATH_MAX_WIDE:0]u16 = undefined;
  14057         const n = windows.ntdll.RtlGetCurrentDirectory_U(wtf16le_buf.len * 2 + 2, &wtf16le_buf) / 2;
  14058         if (n == 0) return error.Unexpected;
  14059         assert(n <= wtf16le_buf.len);
  14060         const wtf16le_slice = wtf16le_buf[0..n];
  14061         var end_index: usize = 0;
  14062         var it = std.unicode.Wtf16LeIterator.init(wtf16le_slice);
  14063         while (it.nextCodepoint()) |codepoint| {
  14064             const seq_len = std.unicode.utf8CodepointSequenceLength(codepoint) catch unreachable;
  14065             if (end_index + seq_len >= buffer.len)
  14066                 return error.NameTooLong;
  14067             end_index += std.unicode.wtf8Encode(codepoint, buffer[end_index..]) catch unreachable;
  14068         }
  14069         return end_index;
  14070     } else if (native_os == .wasi and !builtin.link_libc) {
  14071         if (buffer.len == 0) return error.NameTooLong;
  14072         buffer[0] = '.';
  14073         return 1;
  14074     }
  14075 
  14076     const err: posix.E = if (builtin.link_libc) err: {
  14077         const c_err = if (std.c.getcwd(buffer.ptr, buffer.len)) |_| 0 else std.c._errno().*;
  14078         break :err @enumFromInt(c_err);
  14079     } else err: {
  14080         break :err posix.errno(posix.system.getcwd(buffer.ptr, buffer.len));
  14081     };
  14082     switch (err) {
  14083         .SUCCESS => return std.mem.findScalar(u8, buffer, 0).?,
  14084         .NOENT => return error.CurrentDirUnlinked,
  14085         .RANGE => return error.NameTooLong,
  14086         .FAULT => |e| return errnoBug(e),
  14087         .INVAL => |e| return errnoBug(e),
  14088         else => return posix.unexpectedErrno(err),
  14089     }
  14090 }
  14091 
  14092 fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) process.SetCurrentDirError!void {
  14093     const t: *Threaded = @ptrCast(@alignCast(userdata));
  14094     _ = t;
  14095 
  14096     if (native_os == .wasi) return error.OperationUnsupported;
  14097 
  14098     if (is_windows) {
  14099         var dir_path_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
  14100         const dir_path = try GetFinalPathNameByHandle(dir.handle, .{}, &dir_path_buf);
  14101         const syscall: Syscall = try .start();
  14102         while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&.init(dir_path))) {
  14103             .SUCCESS => return syscall.finish(),
  14104             .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
  14105             .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
  14106             .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
  14107             .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
  14108             .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
  14109             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
  14110             .OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
  14111             .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
  14112             .CANCELLED => {
  14113                 try syscall.checkCancel();
  14114                 continue;
  14115             },
  14116             else => |status| return syscall.unexpectedNtstatus(status),
  14117         };
  14118     }
  14119 
  14120     return fchdir(dir.handle);
  14121 }
  14122 
  14123 fn processSetCurrentPath(userdata: ?*anyopaque, path: []const u8) process.SetCurrentPathError!void {
  14124     const t: *Threaded = @ptrCast(@alignCast(userdata));
  14125     _ = t;
  14126 
  14127     if (native_os == .wasi) return error.OperationUnsupported;
  14128 
  14129     if (is_windows) {
  14130         var path_w_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
  14131         const len = std.unicode.calcWtf16LeLen(path) catch return error.InvalidWtf8;
  14132         if (len > path_w_buf.len) return error.NameTooLong;
  14133         const path_w_len = std.unicode.wtf8ToWtf16Le(&path_w_buf, path) catch |err| switch (err) {
  14134             error.InvalidWtf8 => unreachable, // already validated
  14135         };
  14136         const path_w = path_w_buf[0..path_w_len];
  14137 
  14138         const syscall: Syscall = try .start();
  14139         while (true) switch (windows.ntdll.RtlSetCurrentDirectory_U(&.init(path_w))) {
  14140             .SUCCESS => return syscall.finish(),
  14141             .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
  14142             .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
  14143             .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
  14144             .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
  14145             .INVALID_PARAMETER => |err| return syscall.ntstatusBug(err),
  14146             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
  14147             .OBJECT_PATH_SYNTAX_BAD => |err| return syscall.ntstatusBug(err),
  14148             .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
  14149             .CANCELLED => {
  14150                 try syscall.checkCancel();
  14151                 continue;
  14152             },
  14153             else => |status| return syscall.unexpectedNtstatus(status),
  14154         };
  14155     }
  14156 
  14157     return chdir(path);
  14158 }
  14159 
  14160 pub const PosixAddress = extern union {
  14161     any: posix.sockaddr,
  14162     in: posix.sockaddr.in,
  14163     in6: posix.sockaddr.in6,
  14164 };
  14165 
  14166 const UnixAddress = extern union {
  14167     any: posix.sockaddr,
  14168     un: posix.sockaddr.un,
  14169 };
  14170 
  14171 const WsaAddress = extern union {
  14172     any: ws2_32.sockaddr,
  14173     in: ws2_32.sockaddr.in,
  14174     in6: ws2_32.sockaddr.in6,
  14175     un: ws2_32.sockaddr.un,
  14176 };
  14177 
  14178 pub fn posixAddressFamily(a: *const IpAddress) posix.sa_family_t {
  14179     return switch (a.*) {
  14180         .ip4 => posix.AF.INET,
  14181         .ip6 => posix.AF.INET6,
  14182     };
  14183 }
  14184 
  14185 pub fn addressFromPosix(posix_address: *const PosixAddress) IpAddress {
  14186     return switch (posix_address.any.family) {
  14187         posix.AF.INET => .{ .ip4 = address4FromPosix(&posix_address.in) },
  14188         posix.AF.INET6 => .{ .ip6 = address6FromPosix(&posix_address.in6) },
  14189         else => .{ .ip4 = .loopback(0) },
  14190     };
  14191 }
  14192 
  14193 fn addressFromWsa(wsa_address: *const WsaAddress) IpAddress {
  14194     return switch (wsa_address.any.family) {
  14195         posix.AF.INET => .{ .ip4 = address4FromWsa(&wsa_address.in) },
  14196         posix.AF.INET6 => .{ .ip6 = address6FromWsa(&wsa_address.in6) },
  14197         else => .{ .ip4 = .loopback(0) },
  14198     };
  14199 }
  14200 
  14201 pub fn addressToPosix(a: *const IpAddress, storage: *PosixAddress) posix.socklen_t {
  14202     return switch (a.*) {
  14203         .ip4 => |ip4| {
  14204             storage.in = address4ToPosix(ip4);
  14205             return @sizeOf(posix.sockaddr.in);
  14206         },
  14207         .ip6 => |*ip6| {
  14208             storage.in6 = address6ToPosix(ip6);
  14209             return @sizeOf(posix.sockaddr.in6);
  14210         },
  14211     };
  14212 }
  14213 
  14214 fn addressToWsa(a: *const IpAddress, storage: *WsaAddress) i32 {
  14215     return switch (a.*) {
  14216         .ip4 => |ip4| {
  14217             storage.in = address4ToPosix(ip4);
  14218             return @sizeOf(posix.sockaddr.in);
  14219         },
  14220         .ip6 => |*ip6| {
  14221             storage.in6 = address6ToPosix(ip6);
  14222             return @sizeOf(posix.sockaddr.in6);
  14223         },
  14224     };
  14225 }
  14226 
  14227 fn addressUnixToPosix(a: *const net.UnixAddress, storage: *UnixAddress) posix.socklen_t {
  14228     @memcpy(storage.un.path[0..a.path.len], a.path);
  14229     storage.un.family = posix.AF.UNIX;
  14230     storage.un.path[a.path.len] = 0;
  14231     return @sizeOf(posix.sockaddr.un);
  14232 }
  14233 
  14234 fn addressUnixToWsa(a: *const net.UnixAddress, storage: *WsaAddress) i32 {
  14235     @memcpy(storage.un.path[0..a.path.len], a.path);
  14236     storage.un.family = posix.AF.UNIX;
  14237     storage.un.path[a.path.len] = 0;
  14238     return @sizeOf(posix.sockaddr.un);
  14239 }
  14240 
  14241 fn address4FromPosix(in: *const posix.sockaddr.in) net.Ip4Address {
  14242     return .{
  14243         .port = std.mem.bigToNative(u16, in.port),
  14244         .bytes = @bitCast(in.addr),
  14245     };
  14246 }
  14247 
  14248 fn address6FromPosix(in6: *const posix.sockaddr.in6) net.Ip6Address {
  14249     return .{
  14250         .port = std.mem.bigToNative(u16, in6.port),
  14251         .bytes = in6.addr,
  14252         .flow = in6.flowinfo,
  14253         .interface = .{ .index = in6.scope_id },
  14254     };
  14255 }
  14256 
  14257 fn address4FromWsa(in: *const ws2_32.sockaddr.in) net.Ip4Address {
  14258     return .{
  14259         .port = std.mem.bigToNative(u16, in.port),
  14260         .bytes = @bitCast(in.addr),
  14261     };
  14262 }
  14263 
  14264 fn address6FromWsa(in6: *const ws2_32.sockaddr.in6) net.Ip6Address {
  14265     return .{
  14266         .port = std.mem.bigToNative(u16, in6.port),
  14267         .bytes = in6.addr,
  14268         .flow = in6.flowinfo,
  14269         .interface = .{ .index = in6.scope_id },
  14270     };
  14271 }
  14272 
  14273 fn address4ToPosix(a: net.Ip4Address) posix.sockaddr.in {
  14274     return .{
  14275         .port = std.mem.nativeToBig(u16, a.port),
  14276         .addr = @bitCast(a.bytes),
  14277     };
  14278 }
  14279 
  14280 fn address6ToPosix(a: *const net.Ip6Address) posix.sockaddr.in6 {
  14281     return .{
  14282         .port = std.mem.nativeToBig(u16, a.port),
  14283         .flowinfo = a.flow,
  14284         .addr = a.bytes,
  14285         .scope_id = a.interface.index,
  14286     };
  14287 }
  14288 
  14289 pub fn errnoBug(err: posix.E) Io.UnexpectedError {
  14290     if (is_debug) std.debug.panic("programmer bug caused syscall error: {t}", .{err});
  14291     return error.Unexpected;
  14292 }
  14293 
  14294 pub fn posixSocketMode(mode: net.Socket.Mode) u32 {
  14295     return switch (mode) {
  14296         .stream => posix.SOCK.STREAM,
  14297         .dgram => posix.SOCK.DGRAM,
  14298         .seqpacket => posix.SOCK.SEQPACKET,
  14299         .raw => posix.SOCK.RAW,
  14300         .rdm => posix.SOCK.RDM,
  14301     };
  14302 }
  14303 
  14304 pub fn posixProtocol(protocol: ?net.Protocol) u32 {
  14305     return @intFromEnum(protocol orelse return 0);
  14306 }
  14307 
  14308 pub fn recoverableOsBugDetected() void {
  14309     if (is_debug) unreachable;
  14310 }
  14311 
  14312 pub fn clockToPosix(clock: Io.Clock) posix.clockid_t {
  14313     return switch (clock) {
  14314         .real => posix.CLOCK.REALTIME,
  14315         .awake => switch (native_os) {
  14316             .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => posix.CLOCK.UPTIME_RAW,
  14317             else => posix.CLOCK.MONOTONIC,
  14318         },
  14319         .boot => switch (native_os) {
  14320             .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => posix.CLOCK.MONOTONIC_RAW,
  14321             // On freebsd derivatives, use MONOTONIC_FAST as currently there's
  14322             // no precision tradeoff.
  14323             .freebsd, .dragonfly => posix.CLOCK.MONOTONIC_FAST,
  14324             // On linux, use BOOTTIME instead of MONOTONIC as it ticks while
  14325             // suspended.
  14326             .linux => posix.CLOCK.BOOTTIME,
  14327             // On other posix systems, MONOTONIC is generally the fastest and
  14328             // ticks while suspended.
  14329             else => posix.CLOCK.MONOTONIC,
  14330         },
  14331         .cpu_process => posix.CLOCK.PROCESS_CPUTIME_ID,
  14332         .cpu_thread => posix.CLOCK.THREAD_CPUTIME_ID,
  14333     };
  14334 }
  14335 
  14336 fn clockToWasi(clock: Io.Clock) std.os.wasi.clockid_t {
  14337     return switch (clock) {
  14338         .real => .REALTIME,
  14339         .awake => .MONOTONIC,
  14340         .boot => .MONOTONIC,
  14341         .cpu_process => .PROCESS_CPUTIME_ID,
  14342         .cpu_thread => .THREAD_CPUTIME_ID,
  14343     };
  14344 }
  14345 
  14346 pub const linux_statx_request: std.os.linux.STATX = .{
  14347     .TYPE = true,
  14348     .MODE = true,
  14349     .ATIME = true,
  14350     .MTIME = true,
  14351     .CTIME = true,
  14352     .INO = true,
  14353     .SIZE = true,
  14354     .NLINK = true,
  14355     .BLOCKS = true,
  14356 };
  14357 
  14358 pub const linux_statx_check: std.os.linux.STATX = .{
  14359     .TYPE = true,
  14360     .MODE = true,
  14361     .ATIME = false,
  14362     .MTIME = true,
  14363     .CTIME = true,
  14364     .INO = true,
  14365     .SIZE = true,
  14366     .NLINK = true,
  14367     .BLOCKS = false,
  14368 };
  14369 
  14370 pub fn statFromLinux(stx: *const std.os.linux.Statx) Io.UnexpectedError!File.Stat {
  14371     const actual_mask_int: u32 = @bitCast(stx.mask);
  14372     const wanted_mask_int: u32 = @bitCast(linux_statx_check);
  14373     if ((actual_mask_int | wanted_mask_int) != actual_mask_int) return error.Unexpected;
  14374 
  14375     return .{
  14376         .inode = stx.ino,
  14377         .nlink = stx.nlink,
  14378         .size = stx.size,
  14379         .permissions = .fromMode(stx.mode),
  14380         .kind = statxKind(stx.mode),
  14381         .atime = if (!stx.mask.ATIME) null else .{
  14382             .nanoseconds = @intCast(@as(i128, stx.atime.sec) * std.time.ns_per_s + stx.atime.nsec),
  14383         },
  14384         .mtime = .{ .nanoseconds = @intCast(@as(i128, stx.mtime.sec) * std.time.ns_per_s + stx.mtime.nsec) },
  14385         .ctime = .{ .nanoseconds = @intCast(@as(i128, stx.ctime.sec) * std.time.ns_per_s + stx.ctime.nsec) },
  14386         .block_size = if (stx.mask.BLOCKS) stx.blksize else 1,
  14387     };
  14388 }
  14389 
  14390 pub fn statxKind(stx_mode: u16) File.Kind {
  14391     return switch (stx_mode & std.os.linux.S.IFMT) {
  14392         std.os.linux.S.IFDIR => .directory,
  14393         std.os.linux.S.IFCHR => .character_device,
  14394         std.os.linux.S.IFBLK => .block_device,
  14395         std.os.linux.S.IFREG => .file,
  14396         std.os.linux.S.IFIFO => .named_pipe,
  14397         std.os.linux.S.IFLNK => .sym_link,
  14398         std.os.linux.S.IFSOCK => .unix_domain_socket,
  14399         else => .unknown,
  14400     };
  14401 }
  14402 
  14403 pub fn statFromPosix(st: *const posix.Stat) File.Stat {
  14404     const atime = st.atime();
  14405     const mtime = st.mtime();
  14406     const ctime = st.ctime();
  14407     return .{
  14408         .inode = st.ino,
  14409         .nlink = st.nlink,
  14410         .size = @bitCast(st.size),
  14411         .permissions = .fromMode(st.mode),
  14412         .kind = k: {
  14413             const m = st.mode & posix.S.IFMT;
  14414             switch (m) {
  14415                 posix.S.IFBLK => break :k .block_device,
  14416                 posix.S.IFCHR => break :k .character_device,
  14417                 posix.S.IFDIR => break :k .directory,
  14418                 posix.S.IFIFO => break :k .named_pipe,
  14419                 posix.S.IFLNK => break :k .sym_link,
  14420                 posix.S.IFREG => break :k .file,
  14421                 posix.S.IFSOCK => break :k .unix_domain_socket,
  14422                 else => {},
  14423             }
  14424             if (native_os == .illumos) switch (m) {
  14425                 posix.S.IFDOOR => break :k .door,
  14426                 posix.S.IFPORT => break :k .event_port,
  14427                 else => {},
  14428             };
  14429 
  14430             break :k .unknown;
  14431         },
  14432         .atime = timestampFromPosix(&atime),
  14433         .mtime = timestampFromPosix(&mtime),
  14434         .ctime = timestampFromPosix(&ctime),
  14435         .block_size = @intCast(st.blksize),
  14436     };
  14437 }
  14438 
  14439 fn statFromWasi(st: *const std.os.wasi.filestat_t) File.Stat {
  14440     return .{
  14441         .inode = st.ino,
  14442         .nlink = st.nlink,
  14443         .size = @bitCast(st.size),
  14444         .permissions = .default_file,
  14445         .kind = switch (st.filetype) {
  14446             .BLOCK_DEVICE => .block_device,
  14447             .CHARACTER_DEVICE => .character_device,
  14448             .DIRECTORY => .directory,
  14449             .SYMBOLIC_LINK => .sym_link,
  14450             .REGULAR_FILE => .file,
  14451             .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket,
  14452             else => .unknown,
  14453         },
  14454         .atime = .fromNanoseconds(st.atim),
  14455         .mtime = .fromNanoseconds(st.mtim),
  14456         .ctime = .fromNanoseconds(st.ctim),
  14457         .block_size = 1,
  14458     };
  14459 }
  14460 
  14461 pub fn timestampFromPosix(timespec: *const posix.timespec) Io.Timestamp {
  14462     return .{ .nanoseconds = nanosecondsFromPosix(timespec) };
  14463 }
  14464 
  14465 pub fn nanosecondsFromPosix(timespec: *const posix.timespec) i96 {
  14466     return @intCast(@as(i128, timespec.sec) * std.time.ns_per_s + timespec.nsec);
  14467 }
  14468 
  14469 fn timestampToPosix(nanoseconds: i96) posix.timespec {
  14470     if (builtin.zig_backend == .stage2_wasm) {
  14471         // Workaround for https://codeberg.org/ziglang/zig/issues/30575
  14472         return .{
  14473             .sec = @intCast(@divTrunc(nanoseconds, std.time.ns_per_s)),
  14474             .nsec = @intCast(@rem(nanoseconds, std.time.ns_per_s)),
  14475         };
  14476     }
  14477     return .{
  14478         .sec = @intCast(@divFloor(nanoseconds, std.time.ns_per_s)),
  14479         .nsec = @intCast(@mod(nanoseconds, std.time.ns_per_s)),
  14480     };
  14481 }
  14482 
  14483 pub fn setTimestampToPosix(set_ts: File.SetTimestamp) posix.timespec {
  14484     return switch (set_ts) {
  14485         .unchanged => .OMIT,
  14486         .now => .NOW,
  14487         .new => |t| timestampToPosix(t.nanoseconds),
  14488     };
  14489 }
  14490 
  14491 pub fn pathToPosix(file_path: []const u8, buffer: *[posix.PATH_MAX]u8) Dir.PathNameError![:0]u8 {
  14492     if (std.mem.containsAtLeastScalar2(u8, file_path, 0, 1)) return error.BadPathName;
  14493     // >= rather than > to make room for the null byte
  14494     if (file_path.len >= buffer.len) return error.NameTooLong;
  14495     @memcpy(buffer[0..file_path.len], file_path);
  14496     buffer[file_path.len] = 0;
  14497     return buffer[0..file_path.len :0];
  14498 }
  14499 
  14500 fn lookupDnsSearch(
  14501     t: *Threaded,
  14502     host_name: HostName,
  14503     resolved: *Io.Queue(HostName.LookupResult),
  14504     options: HostName.LookupOptions,
  14505 ) (HostName.LookupError || Io.QueueClosedError)!void {
  14506     const t_io = io(t);
  14507     const rc = HostName.ResolvConf.init(t_io) catch return error.ResolvConfParseFailed;
  14508 
  14509     // Count dots, suppress search when >=ndots or name ends in
  14510     // a dot, which is an explicit request for global scope.
  14511     const dots = std.mem.countScalar(u8, host_name.bytes, '.');
  14512     const search_len = if (dots >= rc.ndots or std.mem.endsWith(u8, host_name.bytes, ".")) 0 else rc.search_len;
  14513     const search = rc.search_buffer[0..search_len];
  14514 
  14515     var canon_name = host_name.bytes;
  14516 
  14517     // Strip final dot for canon, fail if multiple trailing dots.
  14518     if (std.mem.endsWith(u8, canon_name, ".")) canon_name.len -= 1;
  14519     if (std.mem.endsWith(u8, canon_name, ".")) return error.UnknownHostName;
  14520 
  14521     // Name with search domain appended is set up in `canon_name`. This
  14522     // both provides the desired default canonical name (if the requested
  14523     // name is not a CNAME record) and serves as a buffer for passing the
  14524     // full requested name to `lookupDns`.
  14525     @memcpy(options.canonical_name_buffer[0..canon_name.len], canon_name);
  14526     options.canonical_name_buffer[canon_name.len] = '.';
  14527     var it = std.mem.tokenizeAny(u8, search, " \t");
  14528     while (it.next()) |token| {
  14529         @memcpy(options.canonical_name_buffer[canon_name.len + 1 ..][0..token.len], token);
  14530         const lookup_canon_name = options.canonical_name_buffer[0 .. canon_name.len + 1 + token.len];
  14531         if (lookupDns(t, lookup_canon_name, &rc, resolved, options)) |result| {
  14532             return result;
  14533         } else |err| switch (err) {
  14534             error.UnknownHostName, error.NoAddressReturned => continue,
  14535             else => |e| return e,
  14536         }
  14537     }
  14538 
  14539     const lookup_canon_name = options.canonical_name_buffer[0..canon_name.len];
  14540     return lookupDns(t, lookup_canon_name, &rc, resolved, options);
  14541 }
  14542 
  14543 fn lookupDns(
  14544     t: *Threaded,
  14545     lookup_canon_name: []const u8,
  14546     rc: *const HostName.ResolvConf,
  14547     resolved: *Io.Queue(HostName.LookupResult),
  14548     options: HostName.LookupOptions,
  14549 ) (HostName.LookupError || Io.QueueClosedError)!void {
  14550     const t_io = io(t);
  14551     const family_records: [2]struct { af: IpAddress.Family, rr: HostName.DnsRecord } = .{
  14552         .{ .af = .ip6, .rr = .A },
  14553         .{ .af = .ip4, .rr = .AAAA },
  14554     };
  14555     var query_buffers: [2][280]u8 = undefined;
  14556     var answer_buffer: [2 * 512]u8 = undefined;
  14557     var queries_buffer: [2][]const u8 = undefined;
  14558     var answers_buffer: [2][]const u8 = undefined;
  14559     var nq: usize = 0;
  14560     var answer_buffer_i: usize = 0;
  14561 
  14562     for (family_records) |fr| {
  14563         if (options.family != fr.af) {
  14564             var entropy: [2]u8 = undefined;
  14565             random(t, &entropy);
  14566             const len = writeResolutionQuery(&query_buffers[nq], 0, lookup_canon_name, 1, fr.rr, entropy);
  14567             queries_buffer[nq] = query_buffers[nq][0..len];
  14568             nq += 1;
  14569         }
  14570     }
  14571 
  14572     var ip4_mapped_buffer: [HostName.ResolvConf.max_nameservers]IpAddress = undefined;
  14573     const ip4_mapped = ip4_mapped_buffer[0..rc.nameservers_len];
  14574     var any_ip6 = false;
  14575     for (rc.nameservers(), ip4_mapped) |*ns, *m| {
  14576         m.* = .{ .ip6 = .fromAny(ns.*) };
  14577         any_ip6 = any_ip6 or ns.* == .ip6;
  14578     }
  14579     var socket = s: {
  14580         if (any_ip6) ip6: {
  14581             const ip6_addr: IpAddress = .{ .ip6 = .unspecified(0) };
  14582             const socket = ip6_addr.bind(t_io, .{ .ip6_only = true, .mode = .dgram }) catch |err| switch (err) {
  14583                 error.AddressFamilyUnsupported => break :ip6,
  14584                 else => |e| return e,
  14585             };
  14586             break :s socket;
  14587         }
  14588         any_ip6 = false;
  14589         const ip4_addr: IpAddress = .{ .ip4 = .unspecified(0) };
  14590         const socket = try ip4_addr.bind(t_io, .{ .mode = .dgram });
  14591         break :s socket;
  14592     };
  14593     defer socket.close(t_io);
  14594 
  14595     const mapped_nameservers = if (any_ip6) ip4_mapped else rc.nameservers();
  14596     const queries = queries_buffer[0..nq];
  14597     const answers = answers_buffer[0..queries.len];
  14598     var answers_remaining = answers.len;
  14599     for (answers) |*answer| answer.len = 0;
  14600 
  14601     // boot clock is chosen because time the computer is suspended should count
  14602     // against time spent waiting for external messages to arrive.
  14603     const clock: Io.Clock = .boot;
  14604     var now_ts = clock.now(t_io);
  14605     const final_ts = now_ts.addDuration(.fromSeconds(rc.timeout_seconds));
  14606     const attempt_duration: Io.Duration = .{
  14607         .nanoseconds = (std.time.ns_per_s / rc.attempts) * @as(i96, rc.timeout_seconds),
  14608     };
  14609 
  14610     send: while (now_ts.nanoseconds < final_ts.nanoseconds) : (now_ts = clock.now(t_io)) {
  14611         const max_messages = queries_buffer.len * HostName.ResolvConf.max_nameservers;
  14612         {
  14613             var message_buffer: [max_messages]Io.net.OutgoingMessage = undefined;
  14614             var message_i: usize = 0;
  14615             for (queries, answers) |query, *answer| {
  14616                 if (answer.len != 0) continue;
  14617                 for (mapped_nameservers) |*ns| {
  14618                     message_buffer[message_i] = .{
  14619                         .address = ns,
  14620                         .data_ptr = query.ptr,
  14621                         .data_len = query.len,
  14622                     };
  14623                     message_i += 1;
  14624                 }
  14625             }
  14626             _ = netSendPosix(t, socket.handle, message_buffer[0..message_i], .{});
  14627         }
  14628 
  14629         const timeout: Io.Timeout = .{ .deadline = .{
  14630             .raw = now_ts.addDuration(attempt_duration),
  14631             .clock = clock,
  14632         } };
  14633 
  14634         while (true) {
  14635             var message_buffer: [max_messages]Io.net.IncomingMessage = @splat(.init);
  14636             const buf = answer_buffer[answer_buffer_i..];
  14637             const recv_err, const recv_n = socket.receiveManyTimeout(t_io, &message_buffer, buf, .{}, timeout);
  14638             for (message_buffer[0..recv_n]) |*received_message| {
  14639                 const reply = received_message.data;
  14640                 // Ignore non-identifiable packets.
  14641                 if (reply.len < 4) continue;
  14642 
  14643                 // Ignore replies from addresses we didn't send to.
  14644                 const ns = for (mapped_nameservers) |*ns| {
  14645                     if (received_message.from.eql(ns)) break ns;
  14646                 } else {
  14647                     continue;
  14648                 };
  14649 
  14650                 // Find which query this answer goes with, if any.
  14651                 const query, const answer = for (queries, answers) |query, *answer| {
  14652                     if (reply[0] == query[0] and reply[1] == query[1]) break .{ query, answer };
  14653                 } else {
  14654                     continue;
  14655                 };
  14656                 if (answer.len != 0) continue;
  14657 
  14658                 // Only accept positive or negative responses; retry immediately on
  14659                 // server failure, and ignore all other codes such as refusal.
  14660                 switch (reply[3] & 15) {
  14661                     0, 3 => {
  14662                         answer.* = reply;
  14663                         answer_buffer_i += reply.len;
  14664                         answers_remaining -= 1;
  14665                         if (answer_buffer.len - answer_buffer_i == 0) break :send;
  14666                         if (answers_remaining == 0) break :send;
  14667                     },
  14668                     2 => {
  14669                         var retry_message: Io.net.OutgoingMessage = .{
  14670                             .address = ns,
  14671                             .data_ptr = query.ptr,
  14672                             .data_len = query.len,
  14673                         };
  14674                         _ = netSendPosix(t, socket.handle, (&retry_message)[0..1], .{});
  14675                         continue;
  14676                     },
  14677                     else => continue,
  14678                 }
  14679             }
  14680             if (recv_err) |err| switch (err) {
  14681                 error.Canceled => return error.Canceled,
  14682                 error.Timeout => continue :send,
  14683                 else => continue,
  14684             };
  14685         }
  14686     } else {
  14687         return error.NameServerFailure;
  14688     }
  14689 
  14690     var addresses_len: usize = 0;
  14691     var canonical_name: ?HostName = null;
  14692 
  14693     for (answers) |answer| {
  14694         var it = HostName.DnsResponse.init(answer) catch {
  14695             // Here we could potentially add diagnostics to the results queue.
  14696             continue;
  14697         };
  14698         while (it.next() catch {
  14699             // Here we could potentially add diagnostics to the results queue.
  14700             continue;
  14701         }) |record| switch (record.rr) {
  14702             .A => {
  14703                 const data = record.packet[record.data_off..][0..record.data_len];
  14704                 if (data.len != 4) return error.InvalidDnsARecord;
  14705                 try resolved.putOne(t_io, .{ .address = .{ .ip4 = .{
  14706                     .bytes = data[0..4].*,
  14707                     .port = options.port,
  14708                 } } });
  14709                 addresses_len += 1;
  14710             },
  14711             .AAAA => {
  14712                 const data = record.packet[record.data_off..][0..record.data_len];
  14713                 if (data.len != 16) return error.InvalidDnsAAAARecord;
  14714                 try resolved.putOne(t_io, .{ .address = .{ .ip6 = .{
  14715                     .bytes = data[0..16].*,
  14716                     .port = options.port,
  14717                 } } });
  14718                 addresses_len += 1;
  14719             },
  14720             .CNAME => {
  14721                 _, canonical_name = HostName.expand(record.packet, record.data_off, options.canonical_name_buffer) catch
  14722                     return error.InvalidDnsCnameRecord;
  14723             },
  14724             _ => continue,
  14725         };
  14726     }
  14727 
  14728     try resolved.putOne(t_io, .{ .canonical_name = canonical_name orelse .{ .bytes = lookup_canon_name } });
  14729     if (addresses_len == 0) return error.NoAddressReturned;
  14730 }
  14731 
  14732 fn lookupHosts(
  14733     t: *Threaded,
  14734     host_name: HostName,
  14735     resolved: *Io.Queue(HostName.LookupResult),
  14736     options: HostName.LookupOptions,
  14737 ) !void {
  14738     const t_io = io(t);
  14739     const file = Dir.openFileAbsolute(t_io, "/etc/hosts", .{}) catch |err| switch (err) {
  14740         error.FileNotFound,
  14741         error.NotDir,
  14742         error.AccessDenied,
  14743         => return error.UnknownHostName,
  14744 
  14745         error.Canceled => |e| return e,
  14746 
  14747         else => {
  14748             // Here we could add more detailed diagnostics to the results queue.
  14749             return error.DetectingNetworkConfigurationFailed;
  14750         },
  14751     };
  14752     defer file.close(t_io);
  14753 
  14754     var line_buf: [512]u8 = undefined;
  14755     var file_reader = file.reader(t_io, &line_buf);
  14756     return lookupHostsReader(t, host_name, resolved, options, &file_reader.interface) catch |err| switch (err) {
  14757         error.ReadFailed => switch (file_reader.err.?) {
  14758             error.Canceled => |e| return e,
  14759             else => {
  14760                 // Here we could add more detailed diagnostics to the results queue.
  14761                 return error.DetectingNetworkConfigurationFailed;
  14762             },
  14763         },
  14764         error.Canceled,
  14765         error.Closed,
  14766         error.UnknownHostName,
  14767         => |e| return e,
  14768     };
  14769 }
  14770 
  14771 fn lookupHostsReader(
  14772     t: *Threaded,
  14773     host_name: HostName,
  14774     resolved: *Io.Queue(HostName.LookupResult),
  14775     options: HostName.LookupOptions,
  14776     reader: *Io.Reader,
  14777 ) error{ ReadFailed, Canceled, UnknownHostName, Closed }!void {
  14778     const t_io = io(t);
  14779     var addresses_len: usize = 0;
  14780     var canonical_name: ?HostName = null;
  14781     while (true) {
  14782         const line = reader.takeDelimiterExclusive('\n') catch |err| switch (err) {
  14783             error.StreamTooLong => {
  14784                 // Skip lines that are too long.
  14785                 _ = reader.discardDelimiterInclusive('\n') catch |e| switch (e) {
  14786                     error.EndOfStream => break,
  14787                     error.ReadFailed => return error.ReadFailed,
  14788                 };
  14789                 continue;
  14790             },
  14791             error.ReadFailed => return error.ReadFailed,
  14792             error.EndOfStream => break,
  14793         };
  14794         reader.toss(@min(1, reader.bufferedLen()));
  14795         var split_it = std.mem.splitScalar(u8, line, '#');
  14796         const no_comment_line = split_it.first();
  14797 
  14798         var line_it = std.mem.tokenizeAny(u8, no_comment_line, " \t");
  14799         const ip_text = line_it.next() orelse continue;
  14800         var first_name_text: ?[]const u8 = null;
  14801         while (line_it.next()) |name_text| {
  14802             if (std.mem.eql(u8, name_text, host_name.bytes)) {
  14803                 if (first_name_text == null) first_name_text = name_text;
  14804                 break;
  14805             }
  14806         } else continue;
  14807 
  14808         if (canonical_name == null) {
  14809             if (HostName.init(first_name_text.?)) |name_text| {
  14810                 if (name_text.bytes.len <= options.canonical_name_buffer.len) {
  14811                     const canonical_name_dest = options.canonical_name_buffer[0..name_text.bytes.len];
  14812                     @memcpy(canonical_name_dest, name_text.bytes);
  14813                     canonical_name = .{ .bytes = canonical_name_dest };
  14814                 }
  14815             } else |_| {}
  14816         }
  14817 
  14818         if (options.family != .ip6) {
  14819             if (IpAddress.parseIp4(ip_text, options.port)) |addr| {
  14820                 try resolved.putOne(t_io, .{ .address = addr });
  14821                 addresses_len += 1;
  14822             } else |_| {}
  14823         }
  14824         if (options.family != .ip4) {
  14825             if (IpAddress.parseIp6(ip_text, options.port)) |addr| {
  14826                 try resolved.putOne(t_io, .{ .address = addr });
  14827                 addresses_len += 1;
  14828             } else |_| {}
  14829         }
  14830     }
  14831 
  14832     if (canonical_name) |canon_name| try resolved.putOne(t_io, .{ .canonical_name = canon_name });
  14833     if (addresses_len == 0) return error.UnknownHostName;
  14834 }
  14835 
  14836 /// Writes DNS resolution query packet data to `w`; at most 280 bytes.
  14837 fn writeResolutionQuery(q: *[280]u8, op: u4, dname: []const u8, class: u8, ty: HostName.DnsRecord, entropy: [2]u8) usize {
  14838     // This implementation is ported from musl libc.
  14839     // A more idiomatic "ziggy" implementation would be welcome.
  14840     var name = dname;
  14841     if (std.mem.endsWith(u8, name, ".")) name.len -= 1;
  14842     assert(name.len <= 253);
  14843     const n = 17 + name.len + @intFromBool(name.len != 0);
  14844 
  14845     // Construct query template - ID will be filled later
  14846     q[0..2].* = entropy;
  14847     @memset(q[2..n], 0);
  14848     q[2] = @as(u8, op) * 8 + 1;
  14849     q[5] = 1;
  14850     @memcpy(q[13..][0..name.len], name);
  14851     var i: usize = 13;
  14852     var j: usize = undefined;
  14853     while (q[i] != 0) : (i = j + 1) {
  14854         j = i;
  14855         while (q[j] != 0 and q[j] != '.') : (j += 1) {}
  14856         // TODO determine the circumstances for this and whether or
  14857         // not this should be an error.
  14858         if (j - i - 1 > 62) unreachable;
  14859         q[i - 1] = @intCast(j - i);
  14860     }
  14861     q[i + 1] = @intFromEnum(ty);
  14862     q[i + 3] = class;
  14863     return n;
  14864 }
  14865 
  14866 fn copyCanon(canonical_name_buffer: *[HostName.max_len]u8, name: []const u8) HostName {
  14867     const dest = canonical_name_buffer[0..name.len];
  14868     @memcpy(dest, name);
  14869     return .{ .bytes = dest };
  14870 }
  14871 
  14872 /// Darwin XNU 7195.50.7.100.1 introduced __ulock_wait2 and migrated code paths (notably pthread_cond_t) towards it:
  14873 /// https://github.com/apple/darwin-xnu/commit/d4061fb0260b3ed486147341b72468f836ed6c8f#diff-08f993cc40af475663274687b7c326cc6c3031e0db3ac8de7b24624610616be6
  14874 ///
  14875 /// This XNU version appears to correspond to 11.0.1:
  14876 /// https://kernelshaman.blogspot.com/2021/01/building-xnu-for-macos-big-sur-1101.html
  14877 ///
  14878 /// ulock_wait() uses 32-bit micro-second timeouts where 0 = INFINITE or no-timeout
  14879 /// ulock_wait2() uses 64-bit nano-second timeouts (with the same convention)
  14880 const darwin_supports_ulock_wait2 = builtin.os.version_range.semver.min.major >= 11;
  14881 
  14882 fn closeSocketWindows(s: ws2_32.SOCKET) void {
  14883     const rc = ws2_32.closesocket(s);
  14884     if (is_debug) switch (rc) {
  14885         0 => {},
  14886         ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
  14887             else => recoverableOsBugDetected(),
  14888         },
  14889         else => recoverableOsBugDetected(),
  14890     };
  14891 }
  14892 
  14893 const Wsa = struct {
  14894     status: Status = .uninitialized,
  14895     mutex: Io.Mutex = .init,
  14896     init_error: ?Wsa.InitError = null,
  14897 
  14898     const Status = enum { uninitialized, initialized, failure };
  14899 
  14900     const InitError = error{
  14901         ProcessFdQuotaExceeded,
  14902         NetworkDown,
  14903         VersionUnsupported,
  14904         BlockingOperationInProgress,
  14905     } || Io.UnexpectedError;
  14906 };
  14907 
  14908 fn initializeWsa(t: *Threaded) error{ NetworkDown, Canceled }!void {
  14909     const wsa = &t.wsa;
  14910     mutexLock(&wsa.mutex);
  14911     defer mutexUnlock(&wsa.mutex);
  14912     switch (wsa.status) {
  14913         .uninitialized => {
  14914             var wsa_data: ws2_32.WSADATA = undefined;
  14915             const minor_version = 2;
  14916             const major_version = 2;
  14917             switch (ws2_32.WSAStartup((@as(windows.WORD, minor_version) << 8) | major_version, &wsa_data)) {
  14918                 0 => {
  14919                     wsa.status = .initialized;
  14920                     return;
  14921                 },
  14922                 else => |err_int| {
  14923                     wsa.status = .failure;
  14924                     wsa.init_error = switch (@as(ws2_32.WinsockError, @enumFromInt(@as(u16, @intCast(err_int))))) {
  14925                         .SYSNOTREADY => error.NetworkDown,
  14926                         .VERNOTSUPPORTED => error.VersionUnsupported,
  14927                         .EINPROGRESS => error.BlockingOperationInProgress,
  14928                         .EPROCLIM => error.ProcessFdQuotaExceeded,
  14929                         else => |err| windows.unexpectedWsaError(err),
  14930                     };
  14931                 },
  14932             }
  14933         },
  14934         .initialized => return,
  14935         .failure => {},
  14936     }
  14937     return error.NetworkDown;
  14938 }
  14939 
  14940 fn doNothingSignalHandler(_: posix.SIG) callconv(.c) void {}
  14941 
  14942 const WindowsEnvironStrings = struct {
  14943     PATH: ?[:0]const u16 = null,
  14944     PATHEXT: ?[:0]const u16 = null,
  14945 
  14946     fn scan() WindowsEnvironStrings {
  14947         const peb = windows.peb();
  14948         assert(windows.ntdll.RtlEnterCriticalSection(peb.FastPebLock) == .SUCCESS);
  14949         defer assert(windows.ntdll.RtlLeaveCriticalSection(peb.FastPebLock) == .SUCCESS);
  14950         const ptr = peb.ProcessParameters.Environment;
  14951 
  14952         var result: WindowsEnvironStrings = .{};
  14953         var i: usize = 0;
  14954         while (ptr[i] != 0) {
  14955             const key_start = i;
  14956 
  14957             // There are some special environment variables that start with =,
  14958             // so we need a special case to not treat = as a key/value separator
  14959             // if it's the first character.
  14960             // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
  14961             if (ptr[key_start] == '=') i += 1;
  14962 
  14963             while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
  14964             const key_w = ptr[key_start..i];
  14965 
  14966             if (ptr[i] == '=') i += 1;
  14967 
  14968             const value_start = i;
  14969             while (ptr[i] != 0) : (i += 1) {}
  14970             const value_w = ptr[value_start..i :0];
  14971 
  14972             i += 1; // skip over null byte
  14973 
  14974             inline for (@typeInfo(WindowsEnvironStrings).@"struct".fields) |field| {
  14975                 const field_name_w = comptime std.unicode.wtf8ToWtf16LeStringLiteral(field.name);
  14976                 if (windows.eqlIgnoreCaseWtf16(key_w, field_name_w)) @field(result, field.name) = value_w;
  14977             }
  14978         }
  14979 
  14980         return result;
  14981     }
  14982 };
  14983 
  14984 fn scanEnviron(t: *Threaded) void {
  14985     mutexLock(&t.mutex);
  14986     defer mutexUnlock(&t.mutex);
  14987     if (t.environ_initialized) return;
  14988     t.environ.scan(t.allocator);
  14989     t.environ_initialized = true;
  14990 }
  14991 
  14992 fn processReplace(userdata: ?*anyopaque, options: process.ReplaceOptions) process.ReplaceError {
  14993     const t: *Threaded = @ptrCast(@alignCast(userdata));
  14994 
  14995     if (!process.can_replace) return error.OperationUnsupported;
  14996 
  14997     t.scanEnviron(); // for PATH
  14998     const PATH = t.environ.string.PATH orelse default_PATH;
  14999 
  15000     var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
  15001     defer arena_allocator.deinit();
  15002     const arena = arena_allocator.allocator();
  15003 
  15004     const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null);
  15005     for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
  15006 
  15007     const env_block = env_block: {
  15008         const prog_fd: i32 = -1;
  15009         if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{
  15010             .zig_progress_fd = prog_fd,
  15011         });
  15012         break :env_block try t.environ.process_environ.createPosixBlock(arena, .{
  15013             .zig_progress_fd = prog_fd,
  15014         });
  15015     };
  15016 
  15017     return posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH);
  15018 }
  15019 
  15020 fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: process.ReplaceOptions) process.ReplaceError {
  15021     if (!process.can_replace) return error.OperationUnsupported;
  15022     _ = userdata;
  15023     _ = dir;
  15024     _ = options;
  15025     @panic("TODO processReplacePath");
  15026 }
  15027 
  15028 fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: process.SpawnOptions) process.SpawnError!process.Child {
  15029     if (!process.can_spawn) return error.OperationUnsupported;
  15030     _ = userdata;
  15031     _ = dir;
  15032     _ = options;
  15033     @panic("TODO processSpawnPath");
  15034 }
  15035 
  15036 const processSpawn = switch (native_os) {
  15037     .wasi, .emscripten, .ios, .tvos, .visionos, .watchos => processSpawnUnsupported,
  15038     .windows => processSpawnWindows,
  15039     else => processSpawnPosix,
  15040 };
  15041 
  15042 fn processSpawnUnsupported(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
  15043     _ = userdata;
  15044     _ = options;
  15045     return error.OperationUnsupported;
  15046 }
  15047 
  15048 const Spawned = struct {
  15049     pid: posix.pid_t,
  15050     err_fd: posix.fd_t,
  15051     stdin: ?File,
  15052     stdout: ?File,
  15053     stderr: ?File,
  15054 };
  15055 
  15056 fn spawnPosix(t: *Threaded, options: process.SpawnOptions) process.SpawnError!Spawned {
  15057     // The child process does need to access (one end of) these pipes. However,
  15058     // we must initially set CLOEXEC to avoid a race condition. If another thread
  15059     // is racing to spawn a different child process, we don't want it to inherit
  15060     // these FDs in any scenario; that would mean that, for instance, calls to
  15061     // `poll` from the parent would not report the child's stdout as closing when
  15062     // expected, since the other child may retain a reference to the write end of
  15063     // the pipe. So, we create the pipes with CLOEXEC initially. After fork, we
  15064     // need to do something in the new child to make sure we preserve the reference
  15065     // we want. We could use `fcntl` to remove CLOEXEC from the FD, but as it
  15066     // turns out, we `dup2` everything anyway, so there's no need!
  15067     const pipe_flags: posix.O = .{ .CLOEXEC = true };
  15068 
  15069     const stdin_pipe = if (options.stdin == .pipe) try pipe2(pipe_flags) else undefined;
  15070     errdefer if (options.stdin == .pipe) {
  15071         destroyPipe(stdin_pipe);
  15072     };
  15073 
  15074     const stdout_pipe = if (options.stdout == .pipe) try pipe2(pipe_flags) else undefined;
  15075     errdefer if (options.stdout == .pipe) {
  15076         destroyPipe(stdout_pipe);
  15077     };
  15078 
  15079     const stderr_pipe = if (options.stderr == .pipe) try pipe2(pipe_flags) else undefined;
  15080     errdefer if (options.stderr == .pipe) {
  15081         destroyPipe(stderr_pipe);
  15082     };
  15083 
  15084     const any_ignore = (options.stdin == .ignore or options.stdout == .ignore or options.stderr == .ignore);
  15085     const dev_null_fd = if (any_ignore) try getDevNullFd(t) else undefined;
  15086 
  15087     const prog_pipe: [2]posix.fd_t = if (options.progress_node.index != .none) pipe: {
  15088         // We use CLOEXEC for the same reason as in `pipe_flags`.
  15089         const pipe = try pipe2(.{ .NONBLOCK = true, .CLOEXEC = true });
  15090         switch (native_os) {
  15091             .linux => _ = posix.system.fcntl(pipe[0], posix.F.SETPIPE_SZ, @as(u32, std.Progress.max_packet_len * 2)),
  15092             else => {},
  15093         }
  15094         break :pipe pipe;
  15095     } else .{ -1, -1 };
  15096     errdefer destroyPipe(prog_pipe);
  15097 
  15098     var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
  15099     defer arena_allocator.deinit();
  15100     const arena = arena_allocator.allocator();
  15101 
  15102     // The POSIX standard does not allow malloc() between fork() and execve(),
  15103     // and this allocator may be a libc allocator.
  15104     // I have personally observed the child process deadlocking when it tries
  15105     // to call malloc() due to a heap allocation between fork() and execve(),
  15106     // in musl v1.1.24.
  15107     // Additionally, we want to reduce the number of possible ways things
  15108     // can fail between fork() and execve().
  15109     // Therefore, we do all the allocation for the execve() before the fork().
  15110     // This means we must do the null-termination of argv and env vars here.
  15111     const argv_buf = try arena.allocSentinel(?[*:0]const u8, options.argv.len, null);
  15112     for (options.argv, 0..) |arg, i| argv_buf[i] = (try arena.dupeZ(u8, arg)).ptr;
  15113 
  15114     const prog_fileno = 3;
  15115     comptime assert(@max(posix.STDIN_FILENO, posix.STDOUT_FILENO, posix.STDERR_FILENO) + 1 == prog_fileno);
  15116 
  15117     const env_block = env_block: {
  15118         const prog_fd: i32 = if (prog_pipe[1] == -1) -1 else prog_fileno;
  15119         if (options.environ_map) |environ_map| break :env_block try environ_map.createPosixBlock(arena, .{
  15120             .zig_progress_fd = prog_fd,
  15121         });
  15122         break :env_block try t.environ.process_environ.createPosixBlock(arena, .{
  15123             .zig_progress_fd = prog_fd,
  15124         });
  15125     };
  15126 
  15127     // This pipe communicates to the parent errors in the child between `fork` and `execvpe`.
  15128     // It is closed by the child (via CLOEXEC) without writing if `execvpe` succeeds.
  15129     const err_pipe = try pipe2(.{ .CLOEXEC = true });
  15130     errdefer destroyPipe(err_pipe);
  15131 
  15132     t.scanEnviron(); // for PATH
  15133     const PATH = t.environ.string.PATH orelse default_PATH;
  15134 
  15135     const pid_result: posix.pid_t = fork: {
  15136         const rc = posix.system.fork();
  15137         switch (posix.errno(rc)) {
  15138             .SUCCESS => break :fork @intCast(rc),
  15139             .AGAIN => return error.SystemResources,
  15140             .NOMEM => return error.SystemResources,
  15141             .NOSYS => return error.OperationUnsupported,
  15142             else => |err| return posix.unexpectedErrno(err),
  15143         }
  15144     };
  15145 
  15146     if (pid_result == 0) {
  15147         defer comptime unreachable; // We are the child.
  15148         if (Thread.current) |current_thread| current_thread.cancel_protection = .blocked;
  15149         const ep1 = err_pipe[1];
  15150 
  15151         setUpChildIo(options.stdin, stdin_pipe[0], posix.STDIN_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
  15152         setUpChildIo(options.stdout, stdout_pipe[1], posix.STDOUT_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
  15153         setUpChildIo(options.stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) catch |err| forkBail(ep1, err);
  15154 
  15155         switch (options.cwd) {
  15156             .inherit => {},
  15157             .dir => |cwd| {
  15158                 fchdir(cwd.handle) catch |err| forkBail(ep1, err);
  15159             },
  15160             .path => |cwd| {
  15161                 chdir(cwd) catch |err| forkBail(ep1, err);
  15162             },
  15163         }
  15164 
  15165         // Must happen after fchdir above, the cwd file descriptor might be
  15166         // equal to prog_fileno and be clobbered by this dup2 call.
  15167         if (prog_pipe[1] != -1) dup2(prog_pipe[1], prog_fileno) catch |err| forkBail(ep1, err);
  15168 
  15169         if (options.gid) |gid| {
  15170             switch (posix.errno(posix.system.setregid(gid, gid))) {
  15171                 .SUCCESS => {},
  15172                 .AGAIN => forkBail(ep1, error.ResourceLimitReached),
  15173                 .INVAL => forkBail(ep1, error.InvalidUserId),
  15174                 .PERM => forkBail(ep1, error.PermissionDenied),
  15175                 else => forkBail(ep1, error.Unexpected),
  15176             }
  15177         }
  15178 
  15179         if (options.uid) |uid| {
  15180             switch (posix.errno(posix.system.setreuid(uid, uid))) {
  15181                 .SUCCESS => {},
  15182                 .AGAIN => forkBail(ep1, error.ResourceLimitReached),
  15183                 .INVAL => forkBail(ep1, error.InvalidUserId),
  15184                 .PERM => forkBail(ep1, error.PermissionDenied),
  15185                 else => forkBail(ep1, error.Unexpected),
  15186             }
  15187         }
  15188 
  15189         if (options.pgid) |pid| {
  15190             switch (posix.errno(posix.system.setpgid(0, pid))) {
  15191                 .SUCCESS => {},
  15192                 .ACCES => forkBail(ep1, error.ProcessAlreadyExec),
  15193                 .INVAL => forkBail(ep1, error.InvalidProcessGroupId),
  15194                 .PERM => forkBail(ep1, error.PermissionDenied),
  15195                 else => forkBail(ep1, error.Unexpected),
  15196             }
  15197         }
  15198 
  15199         if (options.start_suspended) {
  15200             switch (posix.errno(posix.system.kill(0, .STOP))) {
  15201                 .SUCCESS => {},
  15202                 .PERM => forkBail(ep1, error.PermissionDenied),
  15203                 else => forkBail(ep1, error.Unexpected),
  15204             }
  15205         }
  15206 
  15207         const err = posixExecv(options.expand_arg0, argv_buf.ptr[0].?, argv_buf.ptr, env_block, PATH);
  15208         forkBail(ep1, err);
  15209     }
  15210 
  15211     const pid: posix.pid_t = @intCast(pid_result); // We are the parent.
  15212     errdefer comptime unreachable; // The child is forked; we must not error from now on
  15213 
  15214     closeFd(err_pipe[1]); // make sure only the child holds the write end open
  15215 
  15216     if (options.stdin == .pipe) closeFd(stdin_pipe[0]);
  15217     if (options.stdout == .pipe) closeFd(stdout_pipe[1]);
  15218     if (options.stderr == .pipe) closeFd(stderr_pipe[1]);
  15219 
  15220     if (prog_pipe[1] != -1) closeFd(prog_pipe[1]);
  15221     options.progress_node.setIpcFile(t, .{ .handle = prog_pipe[0], .flags = .{ .nonblocking = true } });
  15222 
  15223     return .{
  15224         .pid = pid,
  15225         .err_fd = err_pipe[0],
  15226         .stdin = switch (options.stdin) {
  15227             .pipe => .{ .handle = stdin_pipe[1], .flags = .{ .nonblocking = false } },
  15228             else => null,
  15229         },
  15230         .stdout = switch (options.stdout) {
  15231             .pipe => .{ .handle = stdout_pipe[0], .flags = .{ .nonblocking = false } },
  15232             else => null,
  15233         },
  15234         .stderr = switch (options.stderr) {
  15235             .pipe => .{ .handle = stderr_pipe[0], .flags = .{ .nonblocking = false } },
  15236             else => null,
  15237         },
  15238     };
  15239 }
  15240 
  15241 fn getDevNullFd(t: *Threaded) !posix.fd_t {
  15242     {
  15243         mutexLock(&t.mutex);
  15244         defer mutexUnlock(&t.mutex);
  15245         if (t.null_file.fd != -1) return t.null_file.fd;
  15246     }
  15247     const mode: u32 = 0;
  15248     const syscall: Syscall = try .start();
  15249     while (true) {
  15250         const rc = open_sym("/dev/null", .{ .ACCMODE = .RDWR }, mode);
  15251         switch (posix.errno(rc)) {
  15252             .SUCCESS => {
  15253                 syscall.finish();
  15254                 const fresh_fd: posix.fd_t = @intCast(rc);
  15255                 mutexLock(&t.mutex); // Another thread might have won the race.
  15256                 defer mutexUnlock(&t.mutex);
  15257                 if (t.null_file.fd != -1) {
  15258                     closeFd(fresh_fd);
  15259                     return t.null_file.fd;
  15260                 } else {
  15261                     t.null_file.fd = fresh_fd;
  15262                     return fresh_fd;
  15263                 }
  15264             },
  15265             .INTR => {
  15266                 try syscall.checkCancel();
  15267                 continue;
  15268             },
  15269             .ACCES => return syscall.fail(error.AccessDenied),
  15270             .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
  15271             .NFILE => return syscall.fail(error.SystemFdQuotaExceeded),
  15272             .NODEV => return syscall.fail(error.NoDevice),
  15273             .NOENT => return syscall.fail(error.FileNotFound),
  15274             .NOMEM => return syscall.fail(error.SystemResources),
  15275             .PERM => return syscall.fail(error.PermissionDenied),
  15276             else => |err| return syscall.unexpectedErrno(err),
  15277         }
  15278     }
  15279 }
  15280 
  15281 fn processSpawnPosix(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
  15282     const t: *Threaded = @ptrCast(@alignCast(userdata));
  15283     const spawned = try spawnPosix(t, options);
  15284     defer closeFd(spawned.err_fd);
  15285 
  15286     // Wait for the child to report any errors in or before `execvpe`.
  15287     if (readIntFd(spawned.err_fd)) |child_err_int| {
  15288         const child_err: process.SpawnError = @errorCast(@errorFromInt(child_err_int));
  15289         return child_err;
  15290     } else |read_err| switch (read_err) {
  15291         error.EndOfStream => {
  15292             // Write end closed by CLOEXEC at the time of the `execvpe` call,
  15293             // indicating success.
  15294         },
  15295         else => {
  15296             // Problem reading the error from the error reporting pipe. We
  15297             // don't know if the child is alive or dead. Better to assume it is
  15298             // alive so the resource does not risk being leaked.
  15299         },
  15300     }
  15301 
  15302     return .{
  15303         .id = spawned.pid,
  15304         .thread_handle = {},
  15305         .stdin = spawned.stdin,
  15306         .stdout = spawned.stdout,
  15307         .stderr = spawned.stderr,
  15308         .request_resource_usage_statistics = options.request_resource_usage_statistics,
  15309     };
  15310 }
  15311 
  15312 fn childWait(userdata: ?*anyopaque, child: *process.Child) process.Child.WaitError!process.Child.Term {
  15313     if (native_os == .wasi) unreachable;
  15314     const t: *Threaded = @ptrCast(@alignCast(userdata));
  15315     _ = t;
  15316     switch (native_os) {
  15317         .windows => return childWaitWindows(child),
  15318         else => return childWaitPosix(child),
  15319     }
  15320 }
  15321 
  15322 fn childKill(userdata: ?*anyopaque, child: *process.Child) void {
  15323     if (native_os == .wasi) unreachable;
  15324     const t: *Threaded = @ptrCast(@alignCast(userdata));
  15325     if (is_windows) {
  15326         childKillWindows(t, child, 1) catch childCleanupWindows(child);
  15327     } else {
  15328         childKillPosix(child) catch {};
  15329         childCleanupPosix(child);
  15330     }
  15331 }
  15332 
  15333 fn childKillWindows(t: *Threaded, child: *process.Child, exit_code: windows.UINT) !void {
  15334     _ = t; // TODO cancelation
  15335     const handle = child.id.?;
  15336     _ = windows.ntdll.RtlReportSilentProcessExit(handle, @enumFromInt(exit_code));
  15337     switch (windows.ntdll.NtTerminateProcess(handle, @enumFromInt(exit_code))) {
  15338         .SUCCESS, .PROCESS_IS_TERMINATING => {
  15339             const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
  15340             _ = windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &infinite_timeout);
  15341             childCleanupWindows(child);
  15342         },
  15343         .ACCESS_DENIED => {
  15344             // Usually when TerminateProcess triggers a ACCESS_DENIED error, it
  15345             // indicates that the process has already exited, but there may be
  15346             // some rare edge cases where our process handle no longer has the
  15347             // PROCESS_TERMINATE access right, so let's do another check to make
  15348             // sure the process is really no longer running:
  15349             const minimal_timeout: windows.LARGE_INTEGER = -1;
  15350             return switch (windows.ntdll.NtWaitForSingleObject(handle, windows.FALSE, &minimal_timeout)) {
  15351                 windows.NTSTATUS.WAIT_0 => error.AlreadyTerminated,
  15352                 else => error.AccessDenied,
  15353             };
  15354         },
  15355         else => |status| return windows.unexpectedStatus(status),
  15356     }
  15357 }
  15358 
  15359 fn childWaitWindows(child: *process.Child) process.Child.WaitError!process.Child.Term {
  15360     const handle = child.id.?;
  15361 
  15362     const alertable_syscall: AlertableSyscall = try .start();
  15363     const infinite_timeout: windows.LARGE_INTEGER = std.math.minInt(windows.LARGE_INTEGER);
  15364     while (true) switch (windows.ntdll.NtWaitForSingleObject(handle, windows.TRUE, &infinite_timeout)) {
  15365         windows.NTSTATUS.WAIT_0 => break alertable_syscall.finish(),
  15366         .USER_APC, .ALERTED, .TIMEOUT => {
  15367             try alertable_syscall.checkCancel();
  15368             continue;
  15369         },
  15370         else => |status| return alertable_syscall.unexpectedNtstatus(status),
  15371     };
  15372 
  15373     var info: windows.PROCESS.BASIC_INFORMATION = undefined;
  15374     const term: process.Child.Term = switch (windows.ntdll.NtQueryInformationProcess(
  15375         handle,
  15376         .BasicInformation,
  15377         &info,
  15378         @sizeOf(windows.PROCESS.BASIC_INFORMATION),
  15379         null,
  15380     )) {
  15381         .SUCCESS => .{ .exited = @as(u8, @truncate(@intFromEnum(info.ExitStatus))) },
  15382         else => .{ .unknown = 0 },
  15383     };
  15384 
  15385     childCleanupWindows(child);
  15386     return term;
  15387 }
  15388 
  15389 fn childCleanupWindows(child: *process.Child) void {
  15390     const handle = child.id orelse return;
  15391 
  15392     if (child.request_resource_usage_statistics) {
  15393         var vmc: windows.PROCESS.VM_COUNTERS = undefined;
  15394         switch (windows.ntdll.NtQueryInformationProcess(
  15395             handle,
  15396             .VmCounters,
  15397             &vmc,
  15398             @sizeOf(windows.PROCESS.VM_COUNTERS),
  15399             null,
  15400         )) {
  15401             .SUCCESS => child.resource_usage_statistics.rusage = vmc,
  15402             else => child.resource_usage_statistics.rusage = null,
  15403         }
  15404     }
  15405 
  15406     windows.CloseHandle(handle);
  15407     child.id = null;
  15408 
  15409     windows.CloseHandle(child.thread_handle);
  15410     child.thread_handle = undefined;
  15411 
  15412     if (child.stdin) |stdin| {
  15413         windows.CloseHandle(stdin.handle);
  15414         child.stdin = null;
  15415     }
  15416     if (child.stdout) |stdout| {
  15417         windows.CloseHandle(stdout.handle);
  15418         child.stdout = null;
  15419     }
  15420     if (child.stderr) |stderr| {
  15421         windows.CloseHandle(stderr.handle);
  15422         child.stderr = null;
  15423     }
  15424 }
  15425 
  15426 fn childWaitPosix(child: *process.Child) process.Child.WaitError!process.Child.Term {
  15427     defer childCleanupPosix(child);
  15428 
  15429     const pid = child.id.?;
  15430 
  15431     var ru: posix.rusage = undefined;
  15432     const ru_ptr = if (child.request_resource_usage_statistics) &ru else null;
  15433 
  15434     if (have_wait4) {
  15435         var status: if (builtin.link_libc) c_int else u32 = undefined;
  15436         const syscall: Syscall = try .start();
  15437         while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, ru_ptr))) {
  15438             .SUCCESS => {
  15439                 syscall.finish();
  15440                 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
  15441                 return statusToTerm(@bitCast(status));
  15442             },
  15443             .INTR => {
  15444                 try syscall.checkCancel();
  15445                 continue;
  15446             },
  15447             .CHILD => |err| return syscall.errnoBug(err), // Double-free.
  15448             else => |err| return syscall.unexpectedErrno(err),
  15449         };
  15450     }
  15451 
  15452     if (have_waitid) {
  15453         const linux = std.os.linux; // Bypass libc which has the wrong signature.
  15454         var info: linux.siginfo_t = undefined;
  15455         const syscall: Syscall = try .start();
  15456         while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, ru_ptr))) {
  15457             .SUCCESS => {
  15458                 syscall.finish();
  15459                 if (ru_ptr) |p| child.resource_usage_statistics.rusage = p.*;
  15460                 const status: u32 = @bitCast(info.fields.common.second.sigchld.status);
  15461                 const code: linux.CLD = @enumFromInt(info.code);
  15462                 return switch (code) {
  15463                     .EXITED => .{ .exited = @truncate(status) },
  15464                     .KILLED, .DUMPED => .{ .signal = @enumFromInt(status) },
  15465                     .TRAPPED, .STOPPED => .{ .stopped = status },
  15466                     _, .CONTINUED => .{ .unknown = status },
  15467                 };
  15468             },
  15469             .INTR => {
  15470                 try syscall.checkCancel();
  15471                 continue;
  15472             },
  15473             .CHILD => |err| return syscall.errnoBug(err), // Double-free.
  15474             else => |err| return syscall.unexpectedErrno(err),
  15475         };
  15476     }
  15477 
  15478     var status: if (builtin.link_libc) c_int else u32 = undefined;
  15479     const syscall: Syscall = try .start();
  15480     while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) {
  15481         .SUCCESS => {
  15482             syscall.finish();
  15483             return statusToTerm(@bitCast(status));
  15484         },
  15485         .INTR => {
  15486             try syscall.checkCancel();
  15487             continue;
  15488         },
  15489         .CHILD => |err| return syscall.errnoBug(err), // Double-free.
  15490         else => |err| return syscall.unexpectedErrno(err),
  15491     };
  15492 }
  15493 
  15494 pub fn statusToTerm(status: u32) process.Child.Term {
  15495     return if (posix.W.IFEXITED(status))
  15496         .{ .exited = posix.W.EXITSTATUS(status) }
  15497     else if (posix.W.IFSIGNALED(status))
  15498         .{ .signal = posix.W.TERMSIG(status) }
  15499     else if (posix.W.IFSTOPPED(status))
  15500         .{ .stopped = posix.W.STOPSIG(status) }
  15501     else
  15502         .{ .unknown = status };
  15503 }
  15504 
  15505 fn childKillPosix(child: *process.Child) !void {
  15506     // Entire function body is intentionally uncancelable.
  15507 
  15508     const pid = child.id.?;
  15509 
  15510     while (true) switch (posix.errno(posix.system.kill(pid, .TERM))) {
  15511         .SUCCESS => break,
  15512         .INTR => continue,
  15513         .PERM => return error.PermissionDenied,
  15514         .INVAL => |err| return errnoBug(err),
  15515         .SRCH => |err| return errnoBug(err),
  15516         else => |err| return posix.unexpectedErrno(err),
  15517     };
  15518 
  15519     if (have_wait4) {
  15520         var status: if (builtin.link_libc) c_int else u32 = undefined;
  15521         while (true) switch (posix.errno(posix.system.wait4(pid, &status, 0, null))) {
  15522             .SUCCESS => return,
  15523             .INTR => continue,
  15524             .CHILD => |err| return errnoBug(err), // Double-free.
  15525             else => |err| return posix.unexpectedErrno(err),
  15526         };
  15527     }
  15528 
  15529     if (have_waitid) {
  15530         const linux = std.os.linux; // Bypass libc which has the wrong signature.
  15531         var info: linux.siginfo_t = undefined;
  15532         while (true) switch (linux.errno(linux.waitid(.PID, pid, &info, linux.W.EXITED, null))) {
  15533             .SUCCESS => return,
  15534             .INTR => continue,
  15535             .CHILD => |err| return errnoBug(err), // Double-free.
  15536             else => |err| return posix.unexpectedErrno(err),
  15537         };
  15538     }
  15539 
  15540     var status: if (builtin.link_libc) c_int else u32 = undefined;
  15541     while (true) switch (posix.errno(posix.system.waitpid(pid, &status, 0))) {
  15542         .SUCCESS => return,
  15543         .INTR => continue,
  15544         .CHILD => |err| return errnoBug(err), // Double-free.
  15545         else => |err| return posix.unexpectedErrno(err),
  15546     };
  15547 }
  15548 
  15549 fn childCleanupPosix(child: *process.Child) void {
  15550     if (child.stdin) |stdin| {
  15551         closeFd(stdin.handle);
  15552         child.stdin = null;
  15553     }
  15554     if (child.stdout) |stdout| {
  15555         closeFd(stdout.handle);
  15556         child.stdout = null;
  15557     }
  15558     if (child.stderr) |stderr| {
  15559         closeFd(stderr.handle);
  15560         child.stderr = null;
  15561     }
  15562     child.id = null;
  15563 }
  15564 
  15565 /// Errors that can occur between fork() and execv()
  15566 const ForkBailError = process.SpawnError || process.ReplaceError;
  15567 
  15568 /// Child of fork calls this to report an error to the fork parent. Then the
  15569 /// child exits.
  15570 fn forkBail(fd: posix.fd_t, err: ForkBailError) noreturn {
  15571     writeIntFd(fd, @as(ErrInt, @intFromError(err))) catch {};
  15572     // If we're linking libc, some naughty applications may have registered atexit handlers
  15573     // which we really do not want to run in the fork child. I caught LLVM doing this and
  15574     // it caused a deadlock instead of doing an exit syscall. In the words of Avril Lavigne,
  15575     // "Why'd you have to go and make things so complicated?"
  15576     if (builtin.link_libc) {
  15577         // The `_exit` function does nothing but make the exit syscall, unlike `exit`.
  15578         std.c._exit(1);
  15579     } else if (native_os == .linux and !builtin.single_threaded) {
  15580         std.os.linux.exit_group(1);
  15581     } else {
  15582         posix.system.exit(1);
  15583     }
  15584 }
  15585 
  15586 fn writeIntFd(fd: posix.fd_t, value: ErrInt) !void {
  15587     var buffer: [8]u8 = undefined;
  15588     std.mem.writeInt(u64, &buffer, value, .little);
  15589     // Skip the cancel mechanism.
  15590     var i: usize = 0;
  15591     while (true) {
  15592         const rc = posix.system.write(fd, buffer[i..].ptr, buffer.len - i);
  15593         switch (posix.errno(rc)) {
  15594             .SUCCESS => {
  15595                 const n: usize = @intCast(rc);
  15596                 i += n;
  15597                 if (buffer.len - i == 0) return;
  15598             },
  15599             .INTR => continue,
  15600             else => return error.SystemResources,
  15601         }
  15602     }
  15603 }
  15604 
  15605 fn readIntFd(fd: posix.fd_t) !ErrInt {
  15606     var buffer: [8]u8 = undefined;
  15607     var i: usize = 0;
  15608     while (true) {
  15609         const rc = posix.system.read(fd, buffer[i..].ptr, buffer.len - i);
  15610         switch (posix.errno(rc)) {
  15611             .SUCCESS => {
  15612                 const n: usize = @intCast(rc);
  15613                 if (n == 0) break;
  15614                 i += n;
  15615                 continue;
  15616             },
  15617             .INTR => continue,
  15618             else => |err| return posix.unexpectedErrno(err),
  15619         }
  15620     }
  15621     if (buffer.len - i != 0) return error.EndOfStream;
  15622     return @intCast(std.mem.readInt(u64, &buffer, .little));
  15623 }
  15624 
  15625 const ErrInt = std.meta.Int(.unsigned, @sizeOf(anyerror) * 8);
  15626 
  15627 fn destroyPipe(pipe: [2]posix.fd_t) void {
  15628     if (pipe[0] != -1) closeFd(pipe[0]);
  15629     if (pipe[0] != pipe[1]) closeFd(pipe[1]);
  15630 }
  15631 
  15632 fn setUpChildIo(stdio: process.SpawnOptions.StdIo, pipe_fd: i32, std_fileno: i32, dev_null_fd: i32) !void {
  15633     switch (stdio) {
  15634         .pipe => try dup2(pipe_fd, std_fileno),
  15635         .close => closeFd(std_fileno),
  15636         .inherit => {},
  15637         .ignore => try dup2(dev_null_fd, std_fileno),
  15638         .file => |file| try dup2(file.handle, std_fileno),
  15639     }
  15640 }
  15641 
  15642 fn processSpawnWindows(userdata: ?*anyopaque, options: process.SpawnOptions) process.SpawnError!process.Child {
  15643     const t: *Threaded = @ptrCast(@alignCast(userdata));
  15644 
  15645     const any_ignore =
  15646         options.stdin == .ignore or
  15647         options.stdout == .ignore or
  15648         options.stderr == .ignore;
  15649     const nul_handle = if (any_ignore) try getNulDevice(t) else undefined;
  15650 
  15651     const any_inherit =
  15652         options.stdin == .inherit or
  15653         options.stdout == .inherit or
  15654         options.stderr == .inherit;
  15655     const peb = if (any_inherit) windows.peb() else undefined;
  15656 
  15657     const stdin_pipe = if (options.stdin == .pipe) try t.windowsCreatePipe(.{
  15658         .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } },
  15659         .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } },
  15660         .outbound = true,
  15661     }) else undefined;
  15662     errdefer if (options.stdin == .pipe) for (stdin_pipe) |handle| windows.CloseHandle(handle);
  15663 
  15664     const stdout_pipe = if (options.stdout == .pipe) try t.windowsCreatePipe(.{
  15665         .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } },
  15666         .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } },
  15667         .inbound = true,
  15668     }) else undefined;
  15669     errdefer if (options.stdout == .pipe) for (stdout_pipe) |handle| windows.CloseHandle(handle);
  15670 
  15671     const stderr_pipe = if (options.stderr == .pipe) try t.windowsCreatePipe(.{
  15672         .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } },
  15673         .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .SYNCHRONOUS_NONALERT } },
  15674         .inbound = true,
  15675     }) else undefined;
  15676     errdefer if (options.stderr == .pipe) for (stderr_pipe) |handle| windows.CloseHandle(handle);
  15677 
  15678     const prog_pipe = if (options.progress_node.index != .none) try t.windowsCreatePipe(.{
  15679         .server = .{ .attributes = .{ .INHERIT = false }, .mode = .{ .IO = .ASYNCHRONOUS } },
  15680         .client = .{ .attributes = .{ .INHERIT = true }, .mode = .{ .IO = .ASYNCHRONOUS } },
  15681         .inbound = true,
  15682         .quota = std.Progress.max_packet_len * 2,
  15683     }) else undefined;
  15684     errdefer if (options.progress_node.index != .none) for (prog_pipe) |handle| windows.CloseHandle(handle);
  15685 
  15686     var siStartInfo: windows.STARTUPINFOW = .{
  15687         .cb = @sizeOf(windows.STARTUPINFOW),
  15688         .dwFlags = windows.STARTF_USESTDHANDLES,
  15689         .hStdInput = switch (options.stdin) {
  15690             .inherit => peb.ProcessParameters.hStdInput,
  15691             .file => |file| try OpenFile(&.{}, .{
  15692                 .access_mask = .{
  15693                     .STANDARD = .{ .SYNCHRONIZE = true },
  15694                     .GENERIC = .{ .READ = true },
  15695                 },
  15696                 .dir = file.handle,
  15697                 .sa = &.{
  15698                     .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
  15699                     .lpSecurityDescriptor = null,
  15700                     .bInheritHandle = windows.TRUE,
  15701                 },
  15702                 .creation = .OPEN,
  15703             }),
  15704             .ignore => nul_handle,
  15705             .pipe => stdin_pipe[1],
  15706             .close => null,
  15707         },
  15708         .hStdOutput = switch (options.stdout) {
  15709             .inherit => peb.ProcessParameters.hStdOutput,
  15710             .file => |file| try OpenFile(&.{}, .{
  15711                 .access_mask = .{
  15712                     .STANDARD = .{ .SYNCHRONIZE = true },
  15713                     .GENERIC = .{ .WRITE = true },
  15714                 },
  15715                 .dir = file.handle,
  15716                 .sa = &.{
  15717                     .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
  15718                     .lpSecurityDescriptor = null,
  15719                     .bInheritHandle = windows.TRUE,
  15720                 },
  15721                 .creation = .OPEN,
  15722             }),
  15723             .ignore => nul_handle,
  15724             .pipe => stdout_pipe[1],
  15725             .close => null,
  15726         },
  15727         .hStdError = switch (options.stderr) {
  15728             .inherit => peb.ProcessParameters.hStdError,
  15729             .file => |file| try OpenFile(&.{}, .{
  15730                 .access_mask = .{
  15731                     .STANDARD = .{ .SYNCHRONIZE = true },
  15732                     .GENERIC = .{ .WRITE = true },
  15733                 },
  15734                 .dir = file.handle,
  15735                 .sa = &.{
  15736                     .nLength = @sizeOf(windows.SECURITY_ATTRIBUTES),
  15737                     .lpSecurityDescriptor = null,
  15738                     .bInheritHandle = windows.TRUE,
  15739                 },
  15740                 .creation = .OPEN,
  15741             }),
  15742             .ignore => nul_handle,
  15743             .pipe => stderr_pipe[1],
  15744             .close => null,
  15745         },
  15746 
  15747         .lpReserved = null,
  15748         .lpDesktop = null,
  15749         .lpTitle = null,
  15750         .dwX = 0,
  15751         .dwY = 0,
  15752         .dwXSize = 0,
  15753         .dwYSize = 0,
  15754         .dwXCountChars = 0,
  15755         .dwYCountChars = 0,
  15756         .dwFillAttribute = 0,
  15757         .wShowWindow = 0,
  15758         .cbReserved2 = 0,
  15759         .lpReserved2 = null,
  15760     };
  15761     var piProcInfo: windows.PROCESS.INFORMATION = undefined;
  15762 
  15763     var arena_allocator = std.heap.ArenaAllocator.init(t.allocator);
  15764     defer arena_allocator.deinit();
  15765     const arena = arena_allocator.allocator();
  15766 
  15767     const cwd_w = cwd_w: {
  15768         switch (options.cwd) {
  15769             .inherit => break :cwd_w null,
  15770             .dir => |cwd_dir| {
  15771                 var dir_path_buffer = try arena.alloc(u16, windows.PATH_MAX_WIDE + 1);
  15772                 const dir_path = try GetFinalPathNameByHandle(
  15773                     cwd_dir.handle,
  15774                     .{},
  15775                     dir_path_buffer[0..windows.PATH_MAX_WIDE],
  15776                 );
  15777                 dir_path_buffer[dir_path.len] = 0;
  15778                 // Shrink the allocation down to just the path buffer + sentinel
  15779                 dir_path_buffer = try arena.realloc(dir_path_buffer, dir_path.len + 1);
  15780                 break :cwd_w dir_path_buffer[0..dir_path.len :0];
  15781             },
  15782             .path => |cwd| {
  15783                 break :cwd_w try std.unicode.wtf8ToWtf16LeAllocZ(arena, cwd);
  15784             },
  15785         }
  15786     };
  15787     const cwd_w_ptr = if (cwd_w) |cwd| cwd.ptr else null;
  15788 
  15789     const env_block = env_block: {
  15790         const prog_handle = if (options.progress_node.index != .none)
  15791             prog_pipe[1]
  15792         else
  15793             windows.INVALID_HANDLE_VALUE;
  15794         if (options.environ_map) |environ_map| break :env_block try environ_map.createWindowsBlock(arena, .{
  15795             .zig_progress_handle = prog_handle,
  15796         });
  15797         break :env_block try t.environ.process_environ.createWindowsBlock(arena, .{
  15798             .zig_progress_handle = if (options.progress_node.index != .none) prog_pipe[1] else windows.INVALID_HANDLE_VALUE,
  15799         });
  15800     };
  15801 
  15802     const app_name_wtf8 = options.argv[0];
  15803     const app_name_is_absolute = Dir.path.isAbsolute(app_name_wtf8);
  15804 
  15805     // The cwd provided by options is in effect when choosing the executable
  15806     // path to match POSIX semantics.
  15807     const cwd_path_w = x: {
  15808         // If the app name is absolute, then we need to use its dirname as the cwd
  15809         if (app_name_is_absolute) {
  15810             const dir = Dir.path.dirname(app_name_wtf8).?;
  15811             break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, dir);
  15812         } else if (cwd_w) |cwd| {
  15813             break :x cwd;
  15814         } else {
  15815             break :x &[_:0]u16{}; // empty for cwd
  15816         }
  15817     };
  15818 
  15819     // If the app name has more than just a filename, then we need to separate
  15820     // that into the basename and dirname and use the dirname as an addition to
  15821     // the cwd path. This is because NtQueryDirectoryFile cannot accept
  15822     // FileName params with path separators.
  15823     const app_basename_wtf8 = Dir.path.basename(app_name_wtf8);
  15824     // If the app name is absolute, then the cwd will already have the app's dirname in it,
  15825     // so only populate app_dirname if app name is a relative path with > 0 path separators.
  15826     const maybe_app_dirname_wtf8 = if (!app_name_is_absolute) Dir.path.dirname(app_name_wtf8) else null;
  15827     const app_dirname_w: ?[:0]u16 = x: {
  15828         if (maybe_app_dirname_wtf8) |app_dirname_wtf8| {
  15829             break :x try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_dirname_wtf8);
  15830         }
  15831         break :x null;
  15832     };
  15833     const app_name_w = try std.unicode.wtf8ToWtf16LeAllocZ(arena, app_basename_wtf8);
  15834 
  15835     const flags: windows.CreateProcessFlags = .{
  15836         .create_suspended = options.start_suspended,
  15837         .create_unicode_environment = true,
  15838         .create_no_window = options.create_no_window,
  15839     };
  15840 
  15841     run: {
  15842         // We have to scan each time because the PEB environment pointer is not stable.
  15843         const env_strings: WindowsEnvironStrings = .scan();
  15844         const PATH = env_strings.PATH orelse &[_:0]u16{};
  15845         const PATHEXT = env_strings.PATHEXT orelse &[_:0]u16{};
  15846 
  15847         // In case the command ends up being a .bat/.cmd script, we need to escape things using the cmd.exe rules
  15848         // and invoke cmd.exe ourselves in order to mitigate arbitrary command execution from maliciously
  15849         // constructed arguments.
  15850         //
  15851         // We'll need to wait until we're actually trying to run the command to know for sure
  15852         // if the resolved command has the `.bat` or `.cmd` extension, so we defer actually
  15853         // serializing the command line until we determine how it should be serialized.
  15854         var cmd_line_cache = WindowsCommandLineCache.init(arena, options.argv);
  15855 
  15856         var app_buf: std.ArrayList(u16) = .empty;
  15857         try app_buf.appendSlice(arena, app_name_w);
  15858 
  15859         var dir_buf: std.ArrayList(u16) = .empty;
  15860 
  15861         if (cwd_path_w.len > 0) {
  15862             try dir_buf.appendSlice(arena, cwd_path_w);
  15863         }
  15864         if (app_dirname_w) |app_dir| {
  15865             if (dir_buf.items.len > 0) try dir_buf.append(arena, Dir.path.sep);
  15866             try dir_buf.appendSlice(arena, app_dir);
  15867         }
  15868 
  15869         windowsCreateProcessPathExt(
  15870             arena,
  15871             &dir_buf,
  15872             &app_buf,
  15873             PATHEXT,
  15874             &cmd_line_cache,
  15875             env_block,
  15876             cwd_w_ptr,
  15877             flags,
  15878             &siStartInfo,
  15879             &piProcInfo,
  15880         ) catch |no_path_err| {
  15881             const original_err = switch (no_path_err) {
  15882                 // argv[0] contains unsupported characters that will never resolve to a valid exe.
  15883                 error.InvalidArg0 => return error.FileNotFound,
  15884                 error.FileNotFound, error.InvalidExe, error.AccessDenied => |e| e,
  15885                 error.UnrecoverableInvalidExe => return error.InvalidExe,
  15886                 else => |e| return e,
  15887             };
  15888 
  15889             // If the app name had path separators, that disallows PATH searching,
  15890             // and there's no need to search the PATH if the app name is absolute.
  15891             // We still search the path if the cwd is absolute because of the
  15892             // "cwd provided by options is in effect when choosing the executable path
  15893             // to match posix semantics" behavior--we don't want to skip searching
  15894             // the PATH just because we were trying to set the cwd of the child process.
  15895             if (app_dirname_w != null or app_name_is_absolute) {
  15896                 return original_err;
  15897             }
  15898 
  15899             var it = std.mem.tokenizeScalar(u16, PATH, ';');
  15900             while (it.next()) |search_path| {
  15901                 dir_buf.clearRetainingCapacity();
  15902                 try dir_buf.appendSlice(arena, search_path);
  15903 
  15904                 if (windowsCreateProcessPathExt(
  15905                     arena,
  15906                     &dir_buf,
  15907                     &app_buf,
  15908                     PATHEXT,
  15909                     &cmd_line_cache,
  15910                     env_block,
  15911                     cwd_w_ptr,
  15912                     flags,
  15913                     &siStartInfo,
  15914                     &piProcInfo,
  15915                 )) {
  15916                     break :run;
  15917                 } else |err| switch (err) {
  15918                     // argv[0] contains unsupported characters that will never resolve to a valid exe.
  15919                     error.InvalidArg0 => return error.FileNotFound,
  15920                     error.FileNotFound, error.AccessDenied, error.InvalidExe => continue,
  15921                     error.UnrecoverableInvalidExe => return error.InvalidExe,
  15922                     else => |e| return e,
  15923                 }
  15924             } else {
  15925                 return original_err;
  15926             }
  15927         };
  15928     }
  15929 
  15930     if (options.progress_node.index != .none) {
  15931         windows.CloseHandle(prog_pipe[1]);
  15932         options.progress_node.setIpcFile(t, .{ .handle = prog_pipe[0], .flags = .{ .nonblocking = true } });
  15933     }
  15934 
  15935     return .{
  15936         .id = piProcInfo.hProcess,
  15937         .thread_handle = piProcInfo.hThread,
  15938         .stdin = stdin: switch (options.stdin) {
  15939             .file => {
  15940                 windows.CloseHandle(siStartInfo.hStdInput.?);
  15941                 break :stdin null;
  15942             },
  15943             .pipe => {
  15944                 windows.CloseHandle(stdin_pipe[1]);
  15945                 break :stdin .{ .handle = stdin_pipe[0], .flags = .{ .nonblocking = false } };
  15946             },
  15947             else => null,
  15948         },
  15949         .stdout = stdout: switch (options.stdout) {
  15950             .file => {
  15951                 windows.CloseHandle(siStartInfo.hStdOutput.?);
  15952                 break :stdout null;
  15953             },
  15954             .pipe => {
  15955                 windows.CloseHandle(stdout_pipe[1]);
  15956                 break :stdout .{ .handle = stdout_pipe[0], .flags = .{ .nonblocking = true } };
  15957             },
  15958             else => null,
  15959         },
  15960         .stderr = stderr: switch (options.stderr) {
  15961             .file => {
  15962                 windows.CloseHandle(siStartInfo.hStdError.?);
  15963                 break :stderr null;
  15964             },
  15965             .pipe => {
  15966                 windows.CloseHandle(stderr_pipe[1]);
  15967                 break :stderr .{ .handle = stderr_pipe[0], .flags = .{ .nonblocking = true } };
  15968             },
  15969             else => null,
  15970         },
  15971         .request_resource_usage_statistics = options.request_resource_usage_statistics,
  15972     };
  15973 }
  15974 
  15975 fn inheritFile() windows.HANDLE {}
  15976 
  15977 fn getCngDevice(t: *Threaded) Io.RandomSecureError!windows.HANDLE {
  15978     {
  15979         mutexLock(&t.mutex);
  15980         defer mutexUnlock(&t.mutex);
  15981         if (t.random_file.handle) |handle| return handle;
  15982     }
  15983 
  15984     var fresh_handle: windows.HANDLE = undefined;
  15985     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
  15986     var syscall: Syscall = try .start();
  15987     while (true) switch (windows.ntdll.NtOpenFile(
  15988         &fresh_handle,
  15989         .{
  15990             .STANDARD = .{ .SYNCHRONIZE = true },
  15991             .SPECIFIC = .{ .FILE = .{ .READ_DATA = true } },
  15992         },
  15993         &.{ .ObjectName = @constCast(&windows.UNICODE_STRING.init(
  15994             &.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'C', 'N', 'G' },
  15995         )) },
  15996         &io_status_block,
  15997         .VALID_FLAGS,
  15998         .{ .IO = .SYNCHRONOUS_NONALERT },
  15999     )) {
  16000         .SUCCESS => {
  16001             syscall.finish();
  16002             mutexLock(&t.mutex); // Another thread might have won the race.
  16003             defer mutexUnlock(&t.mutex);
  16004             if (t.random_file.handle) |prev_handle| {
  16005                 windows.CloseHandle(fresh_handle);
  16006                 return prev_handle;
  16007             } else {
  16008                 t.random_file.handle = fresh_handle;
  16009                 return fresh_handle;
  16010             }
  16011         },
  16012         .CANCELLED => {
  16013             try syscall.checkCancel();
  16014             continue;
  16015         },
  16016         .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.EntropyUnavailable), // Observed on wine 10.0
  16017         else => return syscall.fail(error.EntropyUnavailable),
  16018     };
  16019 }
  16020 
  16021 fn getNulDevice(t: *Threaded) !windows.HANDLE {
  16022     {
  16023         mutexLock(&t.mutex);
  16024         defer mutexUnlock(&t.mutex);
  16025         if (t.null_file.handle) |handle| return handle;
  16026     }
  16027 
  16028     var fresh_handle: windows.HANDLE = undefined;
  16029     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
  16030     var syscall: Syscall = try .start();
  16031     while (true) switch (windows.ntdll.NtOpenFile(
  16032         &fresh_handle,
  16033         .{
  16034             .STANDARD = .{ .SYNCHRONIZE = true },
  16035             .SPECIFIC = .{ .FILE = .{ .READ_DATA = true, .WRITE_DATA = true } },
  16036         },
  16037         &.{
  16038             .Attributes = .{ .INHERIT = true },
  16039             .ObjectName = @constCast(&windows.UNICODE_STRING.init(
  16040                 &.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'u', 'l', 'l' },
  16041             )),
  16042         },
  16043         &io_status_block,
  16044         .VALID_FLAGS,
  16045         .{ .IO = .SYNCHRONOUS_NONALERT },
  16046     )) {
  16047         .SUCCESS => {
  16048             syscall.finish();
  16049             mutexLock(&t.mutex); // Another thread might have won the race.
  16050             defer mutexUnlock(&t.mutex);
  16051             if (t.null_file.handle) |prev_handle| {
  16052                 windows.CloseHandle(fresh_handle);
  16053                 return prev_handle;
  16054             } else {
  16055                 t.null_file.handle = fresh_handle;
  16056                 return fresh_handle;
  16057             }
  16058         },
  16059         .CANCELLED => {
  16060             try syscall.checkCancel();
  16061             continue;
  16062         },
  16063         .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
  16064         .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
  16065         .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
  16066         .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
  16067         .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
  16068         .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
  16069         .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
  16070         .SHARING_VIOLATION => return syscall.fail(error.AccessDenied),
  16071         .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
  16072         .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice),
  16073         .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
  16074         .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
  16075         .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
  16076         else => |status| return syscall.unexpectedNtstatus(status),
  16077     };
  16078 }
  16079 
  16080 fn getNamedPipeDevice(t: *Threaded) !windows.HANDLE {
  16081     {
  16082         mutexLock(&t.mutex);
  16083         defer mutexUnlock(&t.mutex);
  16084         if (t.pipe_file.handle) |handle| return handle;
  16085     }
  16086 
  16087     var fresh_handle: windows.HANDLE = undefined;
  16088     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
  16089     var syscall: Syscall = try .start();
  16090     while (true) switch (windows.ntdll.NtOpenFile(
  16091         &fresh_handle,
  16092         .{ .STANDARD = .{ .SYNCHRONIZE = true } },
  16093         &.{
  16094             .ObjectName = @constCast(&windows.UNICODE_STRING.init(
  16095                 &.{ '\\', 'D', 'e', 'v', 'i', 'c', 'e', '\\', 'N', 'a', 'm', 'e', 'd', 'P', 'i', 'p', 'e', '\\' },
  16096             )),
  16097         },
  16098         &io_status_block,
  16099         .VALID_FLAGS,
  16100         .{ .IO = .SYNCHRONOUS_NONALERT },
  16101     )) {
  16102         .SUCCESS => {
  16103             syscall.finish();
  16104             mutexLock(&t.mutex); // Another thread might have won the race.
  16105             defer mutexUnlock(&t.mutex);
  16106             if (t.pipe_file.handle) |prev_handle| {
  16107                 windows.CloseHandle(fresh_handle);
  16108                 return prev_handle;
  16109             } else {
  16110                 t.pipe_file.handle = fresh_handle;
  16111                 return fresh_handle;
  16112             }
  16113         },
  16114         .DELETE_PENDING => {
  16115             // This error means that there *was* a file in this location on
  16116             // the file system, but it was deleted. However, the OS is not
  16117             // finished with the deletion operation, and so this CreateFile
  16118             // call has failed. There is not really a sane way to handle
  16119             // this other than retrying the creation after the OS finishes
  16120             // the deletion.
  16121             syscall.finish();
  16122             try parking_sleep.sleep(.{ .duration = .{
  16123                 .raw = .fromMilliseconds(1),
  16124                 .clock = .awake,
  16125             } });
  16126             syscall = try .start();
  16127             continue;
  16128         },
  16129         .CANCELLED => {
  16130             try syscall.checkCancel();
  16131             continue;
  16132         },
  16133         .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
  16134         .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
  16135         .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
  16136         .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
  16137         .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
  16138         .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
  16139         .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
  16140         .SHARING_VIOLATION => return syscall.fail(error.AccessDenied),
  16141         .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
  16142         .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice),
  16143         .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
  16144         .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
  16145         .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
  16146         else => |status| return syscall.unexpectedNtstatus(status),
  16147     };
  16148 }
  16149 
  16150 /// Expects `app_buf` to contain exactly the app name, and `dir_buf` to contain exactly the dir path.
  16151 /// After return, `app_buf` will always contain exactly the app name and `dir_buf` will always contain exactly the dir path.
  16152 /// Note: `app_buf` should not contain any leading path separators.
  16153 /// Note: If the dir is the cwd, dir_buf should be empty (len = 0).
  16154 fn windowsCreateProcessPathExt(
  16155     arena: Allocator,
  16156     dir_buf: *std.ArrayList(u16),
  16157     app_buf: *std.ArrayList(u16),
  16158     pathext: [:0]const u16,
  16159     cmd_line_cache: *WindowsCommandLineCache,
  16160     env_block: ?process.Environ.WindowsBlock,
  16161     cwd_ptr: ?[*:0]u16,
  16162     flags: windows.CreateProcessFlags,
  16163     lpStartupInfo: *windows.STARTUPINFOW,
  16164     lpProcessInformation: *windows.PROCESS.INFORMATION,
  16165 ) !void {
  16166     const app_name_len = app_buf.items.len;
  16167     const dir_path_len = dir_buf.items.len;
  16168 
  16169     if (app_name_len == 0) return error.FileNotFound;
  16170 
  16171     defer app_buf.shrinkRetainingCapacity(app_name_len);
  16172     defer dir_buf.shrinkRetainingCapacity(dir_path_len);
  16173 
  16174     // The name of the game here is to avoid CreateProcessW calls at all costs,
  16175     // and only ever try calling it when we have a real candidate for execution.
  16176     // Secondarily, we want to minimize the number of syscalls used when checking
  16177     // for each PATHEXT-appended version of the app name.
  16178     //
  16179     // An overview of the technique used:
  16180     // - Open the search directory for iteration (either cwd or a path from PATH)
  16181     // - Use NtQueryDirectoryFile with a wildcard filename of `<app name>*` to
  16182     //   check if anything that could possibly match either the unappended version
  16183     //   of the app name or any of the versions with a PATHEXT value appended exists.
  16184     // - If the wildcard NtQueryDirectoryFile call found nothing, we can exit early
  16185     //   without needing to use PATHEXT at all.
  16186     //
  16187     // This allows us to use a <open dir, NtQueryDirectoryFile, close dir> sequence
  16188     // for any directory that doesn't contain any possible matches, instead of having
  16189     // to use a separate look up for each individual filename combination (unappended +
  16190     // each PATHEXT appended). For directories where the wildcard *does* match something,
  16191     // we iterate the matches and take note of any that are either the unappended version,
  16192     // or a version with a supported PATHEXT appended. We then try calling CreateProcessW
  16193     // with the found versions in the appropriate order.
  16194     var dir = dir: {
  16195         // needs to be null-terminated
  16196         try dir_buf.append(arena, 0);
  16197         defer dir_buf.shrinkRetainingCapacity(dir_path_len);
  16198         const dir_path_z = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
  16199         const prefixed_path = try wToPrefixedFileW(null, dir_path_z);
  16200         break :dir dirOpenDirWindows(.cwd(), prefixed_path.span(), .{
  16201             .iterate = true,
  16202         }) catch |err| switch (err) {
  16203             // These errors must not be ignored because they should not be able
  16204             // to affect which file is chosen to execute. Also `error.Canceled`
  16205             // must never be swallowed.
  16206             error.Canceled,
  16207             error.SystemResources,
  16208             error.Unexpected,
  16209             error.ProcessFdQuotaExceeded,
  16210             error.SystemFdQuotaExceeded,
  16211             => |e| return e,
  16212 
  16213             error.AccessDenied,
  16214             error.PermissionDenied,
  16215             error.SymLinkLoop,
  16216             error.FileNotFound,
  16217             error.NotDir,
  16218             error.NoDevice,
  16219             error.NetworkNotFound,
  16220             error.NameTooLong,
  16221             error.BadPathName,
  16222             => return error.FileNotFound,
  16223         };
  16224     };
  16225     defer windows.CloseHandle(dir.handle);
  16226 
  16227     // Add wildcard and null-terminator
  16228     try app_buf.append(arena, '*');
  16229     try app_buf.append(arena, 0);
  16230     const app_name_wildcard = app_buf.items[0 .. app_buf.items.len - 1 :0];
  16231 
  16232     // This 2048 is arbitrary, we just want it to be large enough to get multiple FILE_DIRECTORY_INFORMATION entries
  16233     // returned per NtQueryDirectoryFile call.
  16234     var file_information_buf: [2048]u8 align(@alignOf(windows.FILE_DIRECTORY_INFORMATION)) = undefined;
  16235     const file_info_maximum_single_entry_size = @sizeOf(windows.FILE_DIRECTORY_INFORMATION) + (windows.NAME_MAX * 2);
  16236     if (file_information_buf.len < file_info_maximum_single_entry_size) {
  16237         @compileError("file_information_buf must be large enough to contain at least one maximum size FILE_DIRECTORY_INFORMATION entry");
  16238     }
  16239     var io_status: windows.IO_STATUS_BLOCK = undefined;
  16240 
  16241     const num_supported_pathext = @typeInfo(process.WindowsExtension).@"enum".fields.len;
  16242     var pathext_seen = [_]bool{false} ** num_supported_pathext;
  16243     var any_pathext_seen = false;
  16244     var unappended_exists = false;
  16245 
  16246     // Fully iterate the wildcard matches via NtQueryDirectoryFile and take note of all versions
  16247     // of the app_name we should try to spawn.
  16248     // Note: This is necessary because the order of the files returned is filesystem-dependent:
  16249     //       On NTFS, `blah.exe*` will always return `blah.exe` first if it exists.
  16250     //       On FAT32, it's possible for something like `blah.exe.obj` to be returned first.
  16251     while (true) {
  16252         // If we get nothing with the wildcard, then we can just bail out
  16253         // as we know appending PATHEXT will not yield anything.
  16254         switch (windows.ntdll.NtQueryDirectoryFile(
  16255             dir.handle,
  16256             null,
  16257             null,
  16258             null,
  16259             &io_status,
  16260             &file_information_buf,
  16261             file_information_buf.len,
  16262             .Directory,
  16263             windows.FALSE, // single result
  16264             &.init(app_name_wildcard),
  16265             windows.FALSE, // restart iteration
  16266         )) {
  16267             .SUCCESS => {},
  16268             .NO_SUCH_FILE => return error.FileNotFound,
  16269             .NO_MORE_FILES => break,
  16270             .ACCESS_DENIED => return error.AccessDenied,
  16271             else => |status| return windows.unexpectedStatus(status),
  16272         }
  16273 
  16274         // According to the docs, this can only happen if there is not enough room in the
  16275         // buffer to write at least one complete FILE_DIRECTORY_INFORMATION entry.
  16276         // Therefore, this condition should not be possible to hit with the buffer size we use.
  16277         std.debug.assert(io_status.Information != 0);
  16278 
  16279         var it = windows.FileInformationIterator(windows.FILE_DIRECTORY_INFORMATION){ .buf = &file_information_buf };
  16280         while (it.next()) |info| {
  16281             // Skip directories
  16282             if (info.FileAttributes.DIRECTORY) continue;
  16283             const filename = @as([*]u16, @ptrCast(&info.FileName))[0 .. info.FileNameLength / 2];
  16284             // Because all results start with the app_name since we're using the wildcard `app_name*`,
  16285             // if the length is equal to app_name then this is an exact match
  16286             if (filename.len == app_name_len) {
  16287                 // Note: We can't break early here because it's possible that the unappended version
  16288                 //       fails to spawn, in which case we still want to try the PATHEXT appended versions.
  16289                 unappended_exists = true;
  16290             } else if (windowsCreateProcessSupportsExtension(filename[app_name_len..])) |pathext_ext| {
  16291                 pathext_seen[@intFromEnum(pathext_ext)] = true;
  16292                 any_pathext_seen = true;
  16293             }
  16294         }
  16295     }
  16296 
  16297     const unappended_err = unappended: {
  16298         if (unappended_exists) {
  16299             if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) {
  16300                 '/', '\\' => {},
  16301                 else => try dir_buf.append(arena, Dir.path.sep),
  16302             };
  16303             try dir_buf.appendSlice(arena, app_buf.items[0..app_name_len]);
  16304             try dir_buf.append(arena, 0);
  16305             const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
  16306 
  16307             const is_bat_or_cmd = bat_or_cmd: {
  16308                 const app_name = app_buf.items[0..app_name_len];
  16309                 const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :bat_or_cmd false;
  16310                 const ext = app_name[ext_start..];
  16311                 const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse break :bat_or_cmd false;
  16312                 switch (ext_enum) {
  16313                     .cmd, .bat => break :bat_or_cmd true,
  16314                     else => break :bat_or_cmd false,
  16315                 }
  16316             };
  16317             const cmd_line_w = if (is_bat_or_cmd)
  16318                 try cmd_line_cache.scriptCommandLine(full_app_name)
  16319             else
  16320                 try cmd_line_cache.commandLine();
  16321             const app_name_w = if (is_bat_or_cmd)
  16322                 try cmd_line_cache.cmdExePath()
  16323             else
  16324                 full_app_name;
  16325 
  16326             if (windowsCreateProcess(
  16327                 app_name_w.ptr,
  16328                 cmd_line_w.ptr,
  16329                 env_block,
  16330                 cwd_ptr,
  16331                 flags,
  16332                 lpStartupInfo,
  16333                 lpProcessInformation,
  16334             )) |_| {
  16335                 return;
  16336             } else |err| switch (err) {
  16337                 error.FileNotFound,
  16338                 error.AccessDenied,
  16339                 => break :unappended err,
  16340                 error.InvalidExe => {
  16341                     // On InvalidExe, if the extension of the app name is .exe then
  16342                     // it's treated as an unrecoverable error. Otherwise, it'll be
  16343                     // skipped as normal.
  16344                     const app_name = app_buf.items[0..app_name_len];
  16345                     const ext_start = std.mem.lastIndexOfScalar(u16, app_name, '.') orelse break :unappended err;
  16346                     const ext = app_name[ext_start..];
  16347                     if (windows.eqlIgnoreCaseWtf16(ext, std.unicode.utf8ToUtf16LeStringLiteral(".EXE"))) {
  16348                         return error.UnrecoverableInvalidExe;
  16349                     }
  16350                     break :unappended err;
  16351                 },
  16352                 else => return err,
  16353             }
  16354         }
  16355         break :unappended error.FileNotFound;
  16356     };
  16357 
  16358     if (!any_pathext_seen) return unappended_err;
  16359 
  16360     // Now try any PATHEXT appended versions that we've seen
  16361     var ext_it = std.mem.tokenizeScalar(u16, pathext, ';');
  16362     while (ext_it.next()) |ext| {
  16363         const ext_enum = windowsCreateProcessSupportsExtension(ext) orelse continue;
  16364         if (!pathext_seen[@intFromEnum(ext_enum)]) continue;
  16365 
  16366         dir_buf.shrinkRetainingCapacity(dir_path_len);
  16367         if (dir_path_len != 0) switch (dir_buf.items[dir_buf.items.len - 1]) {
  16368             '/', '\\' => {},
  16369             else => try dir_buf.append(arena, Dir.path.sep),
  16370         };
  16371         try dir_buf.appendSlice(arena, app_buf.items[0..app_name_len]);
  16372         try dir_buf.appendSlice(arena, ext);
  16373         try dir_buf.append(arena, 0);
  16374         const full_app_name = dir_buf.items[0 .. dir_buf.items.len - 1 :0];
  16375 
  16376         const is_bat_or_cmd = switch (ext_enum) {
  16377             .cmd, .bat => true,
  16378             else => false,
  16379         };
  16380         const cmd_line_w = if (is_bat_or_cmd)
  16381             try cmd_line_cache.scriptCommandLine(full_app_name)
  16382         else
  16383             try cmd_line_cache.commandLine();
  16384         const app_name_w = if (is_bat_or_cmd)
  16385             try cmd_line_cache.cmdExePath()
  16386         else
  16387             full_app_name;
  16388 
  16389         if (windowsCreateProcess(app_name_w.ptr, cmd_line_w.ptr, env_block, cwd_ptr, flags, lpStartupInfo, lpProcessInformation)) |_| {
  16390             return;
  16391         } else |err| switch (err) {
  16392             error.FileNotFound => continue,
  16393             error.AccessDenied => continue,
  16394             error.InvalidExe => {
  16395                 // On InvalidExe, if the extension of the app name is .exe then
  16396                 // it's treated as an unrecoverable error. Otherwise, it'll be
  16397                 // skipped as normal.
  16398                 if (windows.eqlIgnoreCaseWtf16(ext, std.unicode.utf8ToUtf16LeStringLiteral(".EXE"))) {
  16399                     return error.UnrecoverableInvalidExe;
  16400                 }
  16401                 continue;
  16402             },
  16403             else => return err,
  16404         }
  16405     }
  16406 
  16407     return unappended_err;
  16408 }
  16409 
  16410 fn windowsCreateProcess(
  16411     app_name: [*:0]u16,
  16412     cmd_line: [*:0]u16,
  16413     env_block: ?process.Environ.WindowsBlock,
  16414     cwd_ptr: ?[*:0]u16,
  16415     flags: windows.CreateProcessFlags,
  16416     lpStartupInfo: *windows.STARTUPINFOW,
  16417     lpProcessInformation: *windows.PROCESS.INFORMATION,
  16418 ) !void {
  16419     const syscall: Syscall = try .start();
  16420     while (true) {
  16421         if (windows.kernel32.CreateProcessW(
  16422             app_name,
  16423             cmd_line,
  16424             null,
  16425             null,
  16426             windows.TRUE,
  16427             flags,
  16428             if (env_block) |block| block.slice.ptr else null,
  16429             cwd_ptr,
  16430             lpStartupInfo,
  16431             lpProcessInformation,
  16432         ) != 0) {
  16433             return syscall.finish();
  16434         } else switch (windows.GetLastError()) {
  16435             .INVALID_PARAMETER => unreachable,
  16436             .OPERATION_ABORTED => {
  16437                 try syscall.checkCancel();
  16438                 continue;
  16439             },
  16440             .FILE_NOT_FOUND => return syscall.fail(error.FileNotFound),
  16441             .PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
  16442             .DIRECTORY => return syscall.fail(error.FileNotFound),
  16443             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
  16444             .INVALID_NAME => return syscall.fail(error.InvalidName),
  16445             .FILENAME_EXCED_RANGE => return syscall.fail(error.NameTooLong),
  16446             .SHARING_VIOLATION => return syscall.fail(error.FileBusy),
  16447             .COMMITMENT_LIMIT => return syscall.fail(error.SystemResources),
  16448 
  16449             // These are all the system errors that are mapped to ENOEXEC by
  16450             // the undocumented _dosmaperr (old CRT) or __acrt_errno_map_os_error
  16451             // (newer CRT) functions. Their code can be found in crt/src/dosmap.c (old SDK)
  16452             // or urt/misc/errno.cpp (newer SDK) in the Windows SDK.
  16453             .BAD_FORMAT,
  16454             .INVALID_STARTING_CODESEG, // MIN_EXEC_ERROR in errno.cpp
  16455             .INVALID_STACKSEG,
  16456             .INVALID_MODULETYPE,
  16457             .INVALID_EXE_SIGNATURE,
  16458             .EXE_MARKED_INVALID,
  16459             .BAD_EXE_FORMAT,
  16460             .ITERATED_DATA_EXCEEDS_64k,
  16461             .INVALID_MINALLOCSIZE,
  16462             .DYNLINK_FROM_INVALID_RING,
  16463             .IOPL_NOT_ENABLED,
  16464             .INVALID_SEGDPL,
  16465             .AUTODATASEG_EXCEEDS_64k,
  16466             .RING2SEG_MUST_BE_MOVABLE,
  16467             .RELOC_CHAIN_XEEDS_SEGLIM,
  16468             .INFLOOP_IN_RELOC_CHAIN, // MAX_EXEC_ERROR in errno.cpp
  16469             // This one is not mapped to ENOEXEC but it is possible, for example
  16470             // when calling CreateProcessW on a plain text file with a .exe extension
  16471             .EXE_MACHINE_TYPE_MISMATCH,
  16472             => return syscall.fail(error.InvalidExe),
  16473 
  16474             else => |err| {
  16475                 syscall.finish();
  16476                 return windows.unexpectedError(err);
  16477             },
  16478         }
  16479     }
  16480 }
  16481 
  16482 /// Case-insensitive WTF-16 lookup
  16483 fn windowsCreateProcessSupportsExtension(ext: []const u16) ?process.WindowsExtension {
  16484     comptime {
  16485         // Ensures keeping this function in sync with the enum.
  16486         const fields = @typeInfo(process.WindowsExtension).@"enum".fields;
  16487         assert(fields.len == 4);
  16488         assert(@intFromEnum(process.WindowsExtension.bat) == 0);
  16489         assert(@intFromEnum(process.WindowsExtension.cmd) == 1);
  16490         assert(@intFromEnum(process.WindowsExtension.com) == 2);
  16491         assert(@intFromEnum(process.WindowsExtension.exe) == 3);
  16492     }
  16493 
  16494     if (ext.len != 4) return null;
  16495     const State = enum {
  16496         start,
  16497         dot,
  16498         b,
  16499         ba,
  16500         c,
  16501         cm,
  16502         co,
  16503         e,
  16504         ex,
  16505     };
  16506     var state: State = .start;
  16507     for (ext) |c| switch (state) {
  16508         .start => switch (c) {
  16509             '.' => state = .dot,
  16510             else => return null,
  16511         },
  16512         .dot => switch (c) {
  16513             'b', 'B' => state = .b,
  16514             'c', 'C' => state = .c,
  16515             'e', 'E' => state = .e,
  16516             else => return null,
  16517         },
  16518         .b => switch (c) {
  16519             'a', 'A' => state = .ba,
  16520             else => return null,
  16521         },
  16522         .c => switch (c) {
  16523             'm', 'M' => state = .cm,
  16524             'o', 'O' => state = .co,
  16525             else => return null,
  16526         },
  16527         .e => switch (c) {
  16528             'x', 'X' => state = .ex,
  16529             else => return null,
  16530         },
  16531         .ba => switch (c) {
  16532             't', 'T' => return .bat,
  16533             else => return null,
  16534         },
  16535         .cm => switch (c) {
  16536             'd', 'D' => return .cmd,
  16537             else => return null,
  16538         },
  16539         .co => switch (c) {
  16540             'm', 'M' => return .com,
  16541             else => return null,
  16542         },
  16543         .ex => switch (c) {
  16544             'e', 'E' => return .exe,
  16545             else => return null,
  16546         },
  16547     };
  16548     return null;
  16549 }
  16550 
  16551 test windowsCreateProcessSupportsExtension {
  16552     try std.testing.expectEqual(process.WindowsExtension.exe, windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e' }).?);
  16553     try std.testing.expect(windowsCreateProcessSupportsExtension(&[_]u16{ '.', 'e', 'X', 'e', 'c' }) == null);
  16554 }
  16555 
  16556 /// Serializes argv into a WTF-16 encoded command-line string for use with CreateProcessW.
  16557 ///
  16558 /// Serialization is done on-demand and the result is cached in order to allow for:
  16559 /// - Only serializing the particular type of command line needed (`.bat`/`.cmd`
  16560 ///   command line serialization is different from `.exe`/etc)
  16561 /// - Reusing the serialized command lines if necessary (i.e. if the execution
  16562 ///   of a command fails and the PATH is going to be continued to be searched
  16563 ///   for more candidates)
  16564 const WindowsCommandLineCache = struct {
  16565     cmd_line: ?[:0]u16 = null,
  16566     script_cmd_line: ?[:0]u16 = null,
  16567     cmd_exe_path: ?[:0]u16 = null,
  16568     argv: []const []const u8,
  16569     allocator: Allocator,
  16570 
  16571     fn init(allocator: Allocator, argv: []const []const u8) WindowsCommandLineCache {
  16572         return .{
  16573             .allocator = allocator,
  16574             .argv = argv,
  16575         };
  16576     }
  16577 
  16578     fn deinit(self: *WindowsCommandLineCache) void {
  16579         if (self.cmd_line) |cmd_line| self.allocator.free(cmd_line);
  16580         if (self.script_cmd_line) |script_cmd_line| self.allocator.free(script_cmd_line);
  16581         if (self.cmd_exe_path) |cmd_exe_path| self.allocator.free(cmd_exe_path);
  16582     }
  16583 
  16584     fn commandLine(self: *WindowsCommandLineCache) ![:0]u16 {
  16585         if (self.cmd_line == null) {
  16586             self.cmd_line = try argvToCommandLineWindows(self.allocator, self.argv);
  16587         }
  16588         return self.cmd_line.?;
  16589     }
  16590 
  16591     /// Not cached, since the path to the batch script will change during PATH searching.
  16592     /// `script_path` should be as qualified as possible, e.g. if the PATH is being searched,
  16593     /// then script_path should include both the search path and the script filename
  16594     /// (this allows avoiding cmd.exe having to search the PATH again).
  16595     fn scriptCommandLine(self: *WindowsCommandLineCache, script_path: []const u16) ![:0]u16 {
  16596         if (self.script_cmd_line) |v| self.allocator.free(v);
  16597         self.script_cmd_line = try argvToScriptCommandLineWindows(
  16598             self.allocator,
  16599             script_path,
  16600             self.argv[1..],
  16601         );
  16602         return self.script_cmd_line.?;
  16603     }
  16604 
  16605     fn cmdExePath(self: *WindowsCommandLineCache) Allocator.Error![:0]u16 {
  16606         if (self.cmd_exe_path == null) {
  16607             // Remove trailing slash from system directory path; we'll re-add it below
  16608             const system_dir = std.mem.trimEnd(u16, windows.getSystemDirectoryWtf16Le(), &.{ '/', '\\' });
  16609             const suffix = std.unicode.utf8ToUtf16LeStringLiteral("\\cmd.exe");
  16610             const buf = try self.allocator.allocSentinel(u16, system_dir.len + suffix.len, 0);
  16611             errdefer comptime unreachable;
  16612             @memcpy(buf[0..system_dir.len], system_dir);
  16613             @memcpy(buf[system_dir.len..], suffix);
  16614             self.cmd_exe_path = buf;
  16615         }
  16616         return self.cmd_exe_path.?;
  16617     }
  16618 };
  16619 
  16620 const ArgvToScriptCommandLineError = error{
  16621     OutOfMemory,
  16622     InvalidWtf8,
  16623     /// NUL (U+0000), LF (U+000A), CR (U+000D) are not allowed
  16624     /// within arguments when executing a `.bat`/`.cmd` script.
  16625     /// - NUL/LF signifiies end of arguments, so anything afterwards
  16626     ///   would be lost after execution.
  16627     /// - CR is stripped by `cmd.exe`, so any CR codepoints
  16628     ///   would be lost after execution.
  16629     InvalidBatchScriptArg,
  16630 };
  16631 
  16632 /// Serializes `argv` to a Windows command-line string that uses `cmd.exe /c` and `cmd.exe`-specific
  16633 /// escaping rules. The caller owns the returned slice.
  16634 ///
  16635 /// Escapes `argv` using the suggested mitigation against arbitrary command execution from:
  16636 /// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
  16637 ///
  16638 /// The return of this function will look like
  16639 /// `cmd.exe /d /e:ON /v:OFF /c "<escaped command line>"`
  16640 /// and should be used as the `lpCommandLine` of `CreateProcessW`, while the return of
  16641 /// `WindowsCommandLineCache.cmdExePath` should be used as `lpApplicationName`.
  16642 ///
  16643 /// Should only be used when spawning `.bat`/`.cmd` scripts, see `argvToCommandLineWindows` otherwise.
  16644 /// The `.bat`/`.cmd` file must be known to both have the `.bat`/`.cmd` extension and exist on the filesystem.
  16645 fn argvToScriptCommandLineWindows(
  16646     allocator: Allocator,
  16647     /// Path to the `.bat`/`.cmd` script. If this path is relative, it is assumed to be relative to the CWD.
  16648     /// The script must have been verified to exist at this path before calling this function.
  16649     script_path: []const u16,
  16650     /// Arguments, not including the script name itself. Expected to be encoded as WTF-8.
  16651     script_args: []const []const u8,
  16652 ) ArgvToScriptCommandLineError![:0]u16 {
  16653     var buf = try std.array_list.Managed(u8).initCapacity(allocator, 64);
  16654     defer buf.deinit();
  16655 
  16656     // `/d` disables execution of AutoRun commands.
  16657     // `/e:ON` and `/v:OFF` are needed for BatBadBut mitigation:
  16658     // > If delayed expansion is enabled via the registry value DelayedExpansion,
  16659     // > it must be disabled by explicitly calling cmd.exe with the /V:OFF option.
  16660     // > Escaping for % requires the command extension to be enabled.
  16661     // > If it’s disabled via the registry value EnableExtensions, it must be enabled with the /E:ON option.
  16662     // https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
  16663     buf.appendSliceAssumeCapacity("cmd.exe /d /e:ON /v:OFF /c \"");
  16664 
  16665     // Always quote the path to the script arg
  16666     buf.appendAssumeCapacity('"');
  16667     // We always want the path to the batch script to include a path separator in order to
  16668     // avoid cmd.exe searching the PATH for the script. This is not part of the arbitrary
  16669     // command execution mitigation, we just know exactly what script we want to execute
  16670     // at this point, and potentially making cmd.exe re-find it is unnecessary.
  16671     //
  16672     // If the script path does not have a path separator, then we know its relative to CWD and
  16673     // we can just put `.\` in the front.
  16674     if (std.mem.findAny(u16, script_path, &[_]u16{
  16675         std.mem.nativeToLittle(u16, '\\'), std.mem.nativeToLittle(u16, '/'),
  16676     }) == null) {
  16677         try buf.appendSlice(".\\");
  16678     }
  16679     // Note that we don't do any escaping/mitigations for this argument, since the relevant
  16680     // characters (", %, etc) are illegal in file paths and this function should only be called
  16681     // with script paths that have been verified to exist.
  16682     try std.unicode.wtf16LeToWtf8ArrayList(&buf, script_path);
  16683     buf.appendAssumeCapacity('"');
  16684 
  16685     for (script_args) |arg| {
  16686         // Literal carriage returns get stripped when run through cmd.exe
  16687         // and NUL/newlines act as 'end of command.' Because of this, it's basically
  16688         // always a mistake to include these characters in argv, so it's
  16689         // an error condition in order to ensure that the return of this
  16690         // function can always roundtrip through cmd.exe.
  16691         if (std.mem.findAny(u8, arg, "\x00\r\n") != null) {
  16692             return error.InvalidBatchScriptArg;
  16693         }
  16694 
  16695         // Separate args with a space.
  16696         try buf.append(' ');
  16697 
  16698         // Need to quote if the argument is empty (otherwise the arg would just be lost)
  16699         // or if the last character is a `\`, since then something like "%~2" in a .bat
  16700         // script would cause the closing " to be escaped which we don't want.
  16701         var needs_quotes = arg.len == 0 or arg[arg.len - 1] == '\\';
  16702         if (!needs_quotes) {
  16703             for (arg) |c| {
  16704                 switch (c) {
  16705                     // Known good characters that don't need to be quoted
  16706                     'A'...'Z', 'a'...'z', '0'...'9', '#', '$', '*', '+', '-', '.', '/', ':', '?', '@', '\\', '_' => {},
  16707                     // When in doubt, quote
  16708                     else => {
  16709                         needs_quotes = true;
  16710                         break;
  16711                     },
  16712                 }
  16713             }
  16714         }
  16715         if (needs_quotes) {
  16716             try buf.append('"');
  16717         }
  16718         var backslashes: usize = 0;
  16719         for (arg) |c| {
  16720             switch (c) {
  16721                 '\\' => {
  16722                     backslashes += 1;
  16723                 },
  16724                 '"' => {
  16725                     try buf.appendNTimes('\\', backslashes);
  16726                     try buf.append('"');
  16727                     backslashes = 0;
  16728                 },
  16729                 // Replace `%` with `%%cd:~,%`.
  16730                 //
  16731                 // cmd.exe allows extracting a substring from an environment
  16732                 // variable with the syntax: `%foo:~<start_index>,<end_index>%`.
  16733                 // Therefore, `%cd:~,%` will always expand to an empty string
  16734                 // since both the start and end index are blank, and it is assumed
  16735                 // that `%cd%` is always available since it is a built-in variable
  16736                 // that corresponds to the current directory.
  16737                 //
  16738                 // This means that replacing `%foo%` with `%%cd:~,%foo%%cd:~,%`
  16739                 // will stop `%foo%` from being expanded and *after* expansion
  16740                 // we'll still be left with `%foo%` (the literal string).
  16741                 '%' => {
  16742                     // the trailing `%` is appended outside the switch
  16743                     try buf.appendSlice("%%cd:~,");
  16744                     backslashes = 0;
  16745                 },
  16746                 else => {
  16747                     backslashes = 0;
  16748                 },
  16749             }
  16750             try buf.append(c);
  16751         }
  16752         if (needs_quotes) {
  16753             try buf.appendNTimes('\\', backslashes);
  16754             try buf.append('"');
  16755         }
  16756     }
  16757 
  16758     try buf.append('"');
  16759 
  16760     return try std.unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items);
  16761 }
  16762 
  16763 const ArgvToCommandLineError = error{ OutOfMemory, InvalidWtf8, InvalidArg0 };
  16764 
  16765 /// Serializes `argv` to a Windows command-line string suitable for passing to a child process and
  16766 /// parsing by the `CommandLineToArgvW` algorithm. The caller owns the returned slice.
  16767 ///
  16768 /// To avoid arbitrary command execution, this function should not be used when spawning `.bat`/`.cmd` scripts.
  16769 /// https://flatt.tech/research/posts/batbadbut-you-cant-securely-execute-commands-on-windows/
  16770 ///
  16771 /// When executing `.bat`/`.cmd` scripts, use `argvToScriptCommandLineWindows` instead.
  16772 fn argvToCommandLineWindows(
  16773     allocator: Allocator,
  16774     argv: []const []const u8,
  16775 ) ArgvToCommandLineError![:0]u16 {
  16776     var buf = std.array_list.Managed(u8).init(allocator);
  16777     defer buf.deinit();
  16778 
  16779     if (argv.len != 0) {
  16780         const arg0 = argv[0];
  16781 
  16782         // The first argument must be quoted if it contains spaces or ASCII control characters
  16783         // (excluding DEL). It also follows special quoting rules where backslashes have no special
  16784         // interpretation, which makes it impossible to pass certain first arguments containing
  16785         // double quotes to a child process without characters from the first argument leaking into
  16786         // subsequent ones (which could have security implications).
  16787         //
  16788         // Empty arguments technically don't need quotes, but we quote them anyway for maximum
  16789         // compatibility with different implementations of the 'CommandLineToArgvW' algorithm.
  16790         //
  16791         // Double quotes are illegal in paths on Windows, so for the sake of simplicity we reject
  16792         // all first arguments containing double quotes, even ones that we could theoretically
  16793         // serialize in unquoted form.
  16794         var needs_quotes = arg0.len == 0;
  16795         for (arg0) |c| {
  16796             if (c <= ' ') {
  16797                 needs_quotes = true;
  16798             } else if (c == '"') {
  16799                 return error.InvalidArg0;
  16800             }
  16801         }
  16802         if (needs_quotes) {
  16803             try buf.append('"');
  16804             try buf.appendSlice(arg0);
  16805             try buf.append('"');
  16806         } else {
  16807             try buf.appendSlice(arg0);
  16808         }
  16809 
  16810         for (argv[1..]) |arg| {
  16811             try buf.append(' ');
  16812 
  16813             // Subsequent arguments must be quoted if they contain spaces, tabs or double quotes,
  16814             // or if they are empty. For simplicity and for maximum compatibility with different
  16815             // implementations of the 'CommandLineToArgvW' algorithm, we also quote all ASCII
  16816             // control characters (again, excluding DEL).
  16817             needs_quotes = for (arg) |c| {
  16818                 if (c <= ' ' or c == '"') {
  16819                     break true;
  16820                 }
  16821             } else arg.len == 0;
  16822             if (!needs_quotes) {
  16823                 try buf.appendSlice(arg);
  16824                 continue;
  16825             }
  16826 
  16827             try buf.append('"');
  16828             var backslash_count: usize = 0;
  16829             for (arg) |byte| {
  16830                 switch (byte) {
  16831                     '\\' => {
  16832                         backslash_count += 1;
  16833                     },
  16834                     '"' => {
  16835                         try buf.appendNTimes('\\', backslash_count * 2 + 1);
  16836                         try buf.append('"');
  16837                         backslash_count = 0;
  16838                     },
  16839                     else => {
  16840                         try buf.appendNTimes('\\', backslash_count);
  16841                         try buf.append(byte);
  16842                         backslash_count = 0;
  16843                     },
  16844                 }
  16845             }
  16846             try buf.appendNTimes('\\', backslash_count * 2);
  16847             try buf.append('"');
  16848         }
  16849     }
  16850 
  16851     return try std.unicode.wtf8ToWtf16LeAllocZ(allocator, buf.items);
  16852 }
  16853 
  16854 test argvToCommandLineWindows {
  16855     const t = testArgvToCommandLineWindows;
  16856 
  16857     try t(&.{
  16858         \\C:\Program Files\zig\zig.exe
  16859         ,
  16860         \\run
  16861         ,
  16862         \\.\src\main.zig
  16863         ,
  16864         \\-target
  16865         ,
  16866         \\x86_64-windows-gnu
  16867         ,
  16868         \\-O
  16869         ,
  16870         \\ReleaseSafe
  16871         ,
  16872         \\--
  16873         ,
  16874         \\--emoji=🗿
  16875         ,
  16876         \\--eval=new Regex("Dwayne \"The Rock\" Johnson")
  16877         ,
  16878     },
  16879         \\"C:\Program Files\zig\zig.exe" run .\src\main.zig -target x86_64-windows-gnu -O ReleaseSafe -- --emoji=🗿 "--eval=new Regex(\"Dwayne \\\"The Rock\\\" Johnson\")"
  16880     );
  16881 
  16882     try t(&.{}, "");
  16883     try t(&.{""}, "\"\"");
  16884     try t(&.{" "}, "\" \"");
  16885     try t(&.{"\t"}, "\"\t\"");
  16886     try t(&.{"\x07"}, "\"\x07\"");
  16887     try t(&.{"🦎"}, "🦎");
  16888 
  16889     try t(
  16890         &.{ "zig", "aa aa", "bb\tbb", "cc\ncc", "dd\r\ndd", "ee\x7Fee" },
  16891         "zig \"aa aa\" \"bb\tbb\" \"cc\ncc\" \"dd\r\ndd\" ee\x7Fee",
  16892     );
  16893 
  16894     try t(
  16895         &.{ "\\\\foo bar\\foo bar\\", "\\\\zig zag\\zig zag\\" },
  16896         "\"\\\\foo bar\\foo bar\\\" \"\\\\zig zag\\zig zag\\\\\"",
  16897     );
  16898 
  16899     try std.testing.expectError(
  16900         error.InvalidArg0,
  16901         argvToCommandLineWindows(std.testing.allocator, &.{"\"quotes\"quotes\""}),
  16902     );
  16903     try std.testing.expectError(
  16904         error.InvalidArg0,
  16905         argvToCommandLineWindows(std.testing.allocator, &.{"quotes\"quotes"}),
  16906     );
  16907     try std.testing.expectError(
  16908         error.InvalidArg0,
  16909         argvToCommandLineWindows(std.testing.allocator, &.{"q u o t e s \" q u o t e s"}),
  16910     );
  16911 }
  16912 
  16913 fn testArgvToCommandLineWindows(argv: []const []const u8, expected_cmd_line: []const u8) !void {
  16914     const cmd_line_w = try argvToCommandLineWindows(std.testing.allocator, argv);
  16915     defer std.testing.allocator.free(cmd_line_w);
  16916 
  16917     const cmd_line = try std.unicode.wtf16LeToWtf8Alloc(std.testing.allocator, cmd_line_w);
  16918     defer std.testing.allocator.free(cmd_line);
  16919 
  16920     try std.testing.expectEqualStrings(expected_cmd_line, cmd_line);
  16921 }
  16922 
  16923 fn posixExecv(
  16924     arg0_expand: process.ArgExpansion,
  16925     file: [*:0]const u8,
  16926     child_argv: [*:null]?[*:0]const u8,
  16927     env_block: process.Environ.PosixBlock,
  16928     PATH: []const u8,
  16929 ) process.ReplaceError {
  16930     const file_slice = std.mem.sliceTo(file, 0);
  16931     if (std.mem.findScalar(u8, file_slice, '/') != null) return posixExecvPath(file, child_argv, env_block);
  16932 
  16933     // Use of PATH_MAX here is valid as the path_buf will be passed
  16934     // directly to the operating system in posixExecvPath.
  16935     var path_buf: [posix.PATH_MAX]u8 = undefined;
  16936     var it = std.mem.tokenizeScalar(u8, PATH, ':');
  16937     var seen_eacces = false;
  16938     var err: process.ReplaceError = error.FileNotFound;
  16939 
  16940     // In case of expanding arg0 we must put it back if we return with an error.
  16941     const prev_arg0 = child_argv[0];
  16942     defer switch (arg0_expand) {
  16943         .expand => child_argv[0] = prev_arg0,
  16944         .no_expand => {},
  16945     };
  16946 
  16947     while (it.next()) |search_path| {
  16948         const path_len = search_path.len + file_slice.len + 1;
  16949         if (path_buf.len < path_len + 1) return error.NameTooLong;
  16950         @memcpy(path_buf[0..search_path.len], search_path);
  16951         path_buf[search_path.len] = '/';
  16952         @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice);
  16953         path_buf[path_len] = 0;
  16954         const full_path = path_buf[0..path_len :0].ptr;
  16955         switch (arg0_expand) {
  16956             .expand => child_argv[0] = full_path,
  16957             .no_expand => {},
  16958         }
  16959         err = posixExecvPath(full_path, child_argv, env_block);
  16960         switch (err) {
  16961             error.AccessDenied => seen_eacces = true,
  16962             error.FileNotFound, error.NotDir => {},
  16963             else => |e| return e,
  16964         }
  16965     }
  16966     if (seen_eacces) return error.AccessDenied;
  16967     return err;
  16968 }
  16969 
  16970 /// This function ignores PATH environment variable.
  16971 pub fn posixExecvPath(
  16972     path: [*:0]const u8,
  16973     child_argv: [*:null]const ?[*:0]const u8,
  16974     env_block: process.Environ.PosixBlock,
  16975 ) process.ReplaceError {
  16976     try Thread.checkCancel();
  16977     switch (posix.errno(posix.system.execve(path, child_argv, env_block.slice.ptr))) {
  16978         .FAULT => |err| return errnoBug(err), // Bad pointer parameter.
  16979         .@"2BIG" => return error.SystemResources,
  16980         .MFILE => return error.ProcessFdQuotaExceeded,
  16981         .NAMETOOLONG => return error.NameTooLong,
  16982         .NFILE => return error.SystemFdQuotaExceeded,
  16983         .NOMEM => return error.SystemResources,
  16984         .ACCES => return error.AccessDenied,
  16985         .PERM => return error.PermissionDenied,
  16986         .INVAL => return error.InvalidExe,
  16987         .NOEXEC => return error.InvalidExe,
  16988         .IO => return error.FileSystem,
  16989         .LOOP => return error.FileSystem,
  16990         .ISDIR => return error.IsDir,
  16991         .NOENT => return error.FileNotFound,
  16992         .NOTDIR => return error.NotDir,
  16993         .TXTBSY => return error.FileBusy,
  16994         else => |err| switch (native_os) {
  16995             .driverkit, .ios, .maccatalyst, .macos, .tvos, .visionos, .watchos => switch (err) {
  16996                 .BADEXEC => return error.InvalidExe,
  16997                 .BADARCH => return error.InvalidExe,
  16998                 else => return posix.unexpectedErrno(err),
  16999             },
  17000             .linux => switch (err) {
  17001                 .LIBBAD => return error.InvalidExe,
  17002                 else => return posix.unexpectedErrno(err),
  17003             },
  17004             else => return posix.unexpectedErrno(err),
  17005         },
  17006     }
  17007 }
  17008 
  17009 pub const CreatePipeOptions = struct {
  17010     server: End,
  17011     client: End,
  17012     inbound: bool = false,
  17013     outbound: bool = false,
  17014     maximum_instances: u32 = 1,
  17015     quota: u32 = 4096,
  17016     default_timeout: windows.LARGE_INTEGER = -120 * std.time.ns_per_s / 100,
  17017 
  17018     pub const End = struct {
  17019         attributes: windows.OBJECT.ATTRIBUTES.Flags = .{},
  17020         mode: windows.FILE.MODE,
  17021     };
  17022 };
  17023 pub fn windowsCreatePipe(t: *Threaded, options: CreatePipeOptions) ![2]windows.HANDLE {
  17024     const named_pipe_device = try t.getNamedPipeDevice();
  17025     const server_handle = server_handle: {
  17026         var handle: windows.HANDLE = undefined;
  17027         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
  17028         const syscall: Syscall = try .start();
  17029         while (true) switch (windows.ntdll.NtCreateNamedPipeFile(
  17030             &handle,
  17031             .{
  17032                 .SPECIFIC = .{ .FILE_PIPE = .{
  17033                     .READ_DATA = options.inbound,
  17034                     .WRITE_DATA = options.outbound,
  17035                     .WRITE_ATTRIBUTES = true,
  17036                 } },
  17037                 .STANDARD = .{ .SYNCHRONIZE = true },
  17038             },
  17039             &.{
  17040                 .RootDirectory = named_pipe_device,
  17041                 .Attributes = options.server.attributes,
  17042             },
  17043             &io_status_block,
  17044             .{ .READ = true, .WRITE = true },
  17045             .CREATE,
  17046             options.server.mode,
  17047             .{ .TYPE = .BYTE_STREAM },
  17048             .{ .MODE = .BYTE_STREAM },
  17049             .{ .OPERATION = .QUEUE },
  17050             options.maximum_instances,
  17051             if (options.inbound) options.quota else 0,
  17052             if (options.outbound) options.quota else 0,
  17053             &options.default_timeout,
  17054         )) {
  17055             .SUCCESS => break syscall.finish(),
  17056             .CANCELLED => {
  17057                 try syscall.checkCancel();
  17058                 continue;
  17059             },
  17060             .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
  17061             .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
  17062             else => |status| return syscall.unexpectedNtstatus(status),
  17063         };
  17064         break :server_handle handle;
  17065     };
  17066     errdefer windows.CloseHandle(server_handle);
  17067     const client_handle = client_handle: {
  17068         var handle: windows.HANDLE = undefined;
  17069         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
  17070         const syscall: Syscall = try .start();
  17071         while (true) switch (windows.ntdll.NtOpenFile(
  17072             &handle,
  17073             .{
  17074                 .SPECIFIC = .{ .FILE_PIPE = .{
  17075                     .READ_DATA = options.outbound,
  17076                     .WRITE_DATA = options.inbound,
  17077                     .WRITE_ATTRIBUTES = true,
  17078                 } },
  17079                 .STANDARD = .{ .SYNCHRONIZE = true },
  17080             },
  17081             &.{
  17082                 .RootDirectory = server_handle,
  17083                 .Attributes = options.client.attributes,
  17084             },
  17085             &io_status_block,
  17086             .{ .READ = true, .WRITE = true },
  17087             options.client.mode,
  17088         )) {
  17089             .SUCCESS => break syscall.finish(),
  17090             .CANCELLED => {
  17091                 try syscall.checkCancel();
  17092                 continue;
  17093             },
  17094             .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
  17095             .INSUFFICIENT_RESOURCES => return syscall.fail(error.SystemResources),
  17096             else => |status| return syscall.unexpectedNtstatus(status),
  17097         };
  17098         break :client_handle handle;
  17099     };
  17100     errdefer windows.CloseHandle(client_handle);
  17101     return .{ server_handle, client_handle };
  17102 }
  17103 
  17104 fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File {
  17105     const t: *Threaded = @ptrCast(@alignCast(userdata));
  17106     t.scanEnviron();
  17107     return t.environ.zig_progress_file;
  17108 }
  17109 
  17110 pub fn environString(t: *Threaded, comptime name: []const u8) ?[:0]const u8 {
  17111     t.scanEnviron();
  17112     return @field(t.environ.string, name);
  17113 }
  17114 
  17115 fn random(userdata: ?*anyopaque, buffer: []u8) void {
  17116     const t: *Threaded = @ptrCast(@alignCast(userdata));
  17117     const thread = Thread.current orelse return randomMainThread(t, buffer);
  17118     if (!thread.csprng.isInitialized()) {
  17119         @branchHint(.unlikely);
  17120         var seed: [Csprng.seed_len]u8 = undefined;
  17121         randomMainThread(t, &seed);
  17122         thread.csprng.rng = .init(seed);
  17123     }
  17124     thread.csprng.rng.fill(buffer);
  17125 }
  17126 
  17127 fn randomMainThread(t: *Threaded, buffer: []u8) void {
  17128     mutexLock(&t.mutex);
  17129     defer mutexUnlock(&t.mutex);
  17130 
  17131     if (!t.csprng.isInitialized()) {
  17132         @branchHint(.unlikely);
  17133         var seed: [Csprng.seed_len]u8 = undefined;
  17134         {
  17135             mutexUnlock(&t.mutex);
  17136             defer mutexLock(&t.mutex);
  17137 
  17138             const prev = swapCancelProtection(t, .blocked);
  17139             defer _ = swapCancelProtection(t, prev);
  17140 
  17141             randomSecure(t, &seed) catch |err| switch (err) {
  17142                 error.Canceled => unreachable,
  17143                 error.EntropyUnavailable => fallbackSeed(t, &seed),
  17144             };
  17145         }
  17146         t.csprng.rng = .init(seed);
  17147     }
  17148 
  17149     t.csprng.rng.fill(buffer);
  17150 }
  17151 
  17152 pub fn fallbackSeed(aslr_addr: ?*anyopaque, seed: *[Csprng.seed_len]u8) void {
  17153     @memset(seed, 0);
  17154     std.mem.writeInt(usize, seed[seed.len - @sizeOf(usize) ..][0..@sizeOf(usize)], @intFromPtr(aslr_addr), .native);
  17155     const fallbackSeedImpl = switch (native_os) {
  17156         .windows => fallbackSeedWindows,
  17157         .wasi => if (builtin.link_libc) fallbackSeedPosix else fallbackSeedWasi,
  17158         else => fallbackSeedPosix,
  17159     };
  17160     fallbackSeedImpl(seed);
  17161 }
  17162 
  17163 fn fallbackSeedPosix(seed: *[Csprng.seed_len]u8) void {
  17164     std.mem.writeInt(posix.pid_t, seed[0..@sizeOf(posix.pid_t)], posix.system.getpid(), .native);
  17165     const i_1 = @sizeOf(posix.pid_t);
  17166 
  17167     var ts: posix.timespec = undefined;
  17168     const Sec = @TypeOf(ts.sec);
  17169     const Nsec = @TypeOf(ts.nsec);
  17170     const i_2 = i_1 + @sizeOf(Sec);
  17171     switch (posix.errno(posix.system.clock_gettime(.REALTIME, &ts))) {
  17172         .SUCCESS => {
  17173             std.mem.writeInt(Sec, seed[i_1..][0..@sizeOf(Sec)], ts.sec, .native);
  17174             std.mem.writeInt(Nsec, seed[i_2..][0..@sizeOf(Nsec)], ts.nsec, .native);
  17175         },
  17176         else => {},
  17177     }
  17178 }
  17179 
  17180 fn fallbackSeedWindows(seed: *[Csprng.seed_len]u8) void {
  17181     var pc: windows.LARGE_INTEGER = undefined;
  17182     _ = windows.ntdll.RtlQueryPerformanceCounter(&pc);
  17183     std.mem.writeInt(windows.LARGE_INTEGER, seed[0..@sizeOf(windows.LARGE_INTEGER)], pc, .native);
  17184 }
  17185 
  17186 fn fallbackSeedWasi(seed: *[Csprng.seed_len]u8) void {
  17187     var ts: std.os.wasi.timestamp_t = undefined;
  17188     if (std.os.wasi.clock_time_get(.REALTIME, 1, &ts) == .SUCCESS) {
  17189         std.mem.writeInt(std.os.wasi.timestamp_t, seed[0..@sizeOf(std.os.wasi.timestamp_t)], ts, .native);
  17190     }
  17191 }
  17192 
  17193 fn randomSecure(userdata: ?*anyopaque, buffer: []u8) Io.RandomSecureError!void {
  17194     const t: *Threaded = @ptrCast(@alignCast(userdata));
  17195 
  17196     if (is_windows) {
  17197         if (buffer.len == 0) return;
  17198         // ProcessPrng from bcryptprimitives.dll has the following properties:
  17199         // * introduces a dependency on bcryptprimitives.dll, which apparently
  17200         //   runs a test suite every time it is loaded
  17201         // * heap allocates a 48-byte buffer, handling failure by returning NO_MEMORY in a BOOL
  17202         //   despite the function being documented to always return TRUE
  17203         // * reads from "\\Device\\CNG" which then seeds a per-CPU AES CSPRNG
  17204         // Therefore, that function is avoided in favor of using the device directly.
  17205         const cng_device = try getCngDevice(t);
  17206         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
  17207         var i: usize = 0;
  17208         const syscall: Syscall = try .start();
  17209         while (true) {
  17210             const remaining_len = std.math.lossyCast(u32, buffer.len - i);
  17211             switch (windows.ntdll.NtDeviceIoControlFile(
  17212                 cng_device,
  17213                 null,
  17214                 null,
  17215                 null,
  17216                 &io_status_block,
  17217                 windows.IOCTL.KSEC.GEN_RANDOM,
  17218                 null,
  17219                 0,
  17220                 buffer[i..].ptr,
  17221                 remaining_len,
  17222             )) {
  17223                 .SUCCESS => {
  17224                     i += remaining_len;
  17225                     if (buffer.len - i == 0) {
  17226                         return syscall.finish();
  17227                     } else {
  17228                         try syscall.checkCancel();
  17229                         continue;
  17230                     }
  17231                 },
  17232                 .CANCELLED => {
  17233                     try syscall.checkCancel();
  17234                     continue;
  17235                 },
  17236                 else => return syscall.fail(error.EntropyUnavailable),
  17237             }
  17238         }
  17239     }
  17240 
  17241     if (builtin.link_libc and @TypeOf(posix.system.arc4random_buf) != void) {
  17242         if (buffer.len == 0) return;
  17243         posix.system.arc4random_buf(buffer.ptr, buffer.len);
  17244         return;
  17245     }
  17246 
  17247     if (native_os == .wasi) {
  17248         if (buffer.len == 0) return;
  17249         const syscall: Syscall = try .start();
  17250         while (true) switch (std.os.wasi.random_get(buffer.ptr, buffer.len)) {
  17251             .SUCCESS => return syscall.finish(),
  17252             .INTR => {
  17253                 try syscall.checkCancel();
  17254                 continue;
  17255             },
  17256             else => return syscall.fail(error.EntropyUnavailable),
  17257         };
  17258     }
  17259 
  17260     if (@TypeOf(posix.system.getrandom) != void) {
  17261         const getrandom = if (use_libc_getrandom) std.c.getrandom else std.os.linux.getrandom;
  17262         var i: usize = 0;
  17263         const syscall: Syscall = try .start();
  17264         while (buffer.len - i != 0) {
  17265             const buf = buffer[i..];
  17266             const rc = getrandom(buf.ptr, buf.len, 0);
  17267             switch (posix.errno(rc)) {
  17268                 .SUCCESS => {
  17269                     syscall.finish();
  17270                     const n: usize = @intCast(rc);
  17271                     i += n;
  17272                     continue;
  17273                 },
  17274                 .INTR => {
  17275                     try syscall.checkCancel();
  17276                     continue;
  17277                 },
  17278                 else => return syscall.fail(error.EntropyUnavailable),
  17279             }
  17280         }
  17281         return;
  17282     }
  17283 
  17284     if (native_os == .emscripten) {
  17285         if (buffer.len == 0) return;
  17286         const err = posix.errno(std.c.getentropy(buffer.ptr, buffer.len));
  17287         switch (err) {
  17288             .SUCCESS => return,
  17289             else => return error.EntropyUnavailable,
  17290         }
  17291     }
  17292 
  17293     if (native_os == .linux) {
  17294         comptime assert(use_dev_urandom);
  17295         const urandom_fd = try getRandomFd(t);
  17296 
  17297         var i: usize = 0;
  17298         while (buffer.len - i != 0) {
  17299             const syscall: Syscall = try .start();
  17300             const rc = posix.system.read(urandom_fd, buffer[i..].ptr, buffer.len - i);
  17301             switch (posix.errno(rc)) {
  17302                 .SUCCESS => {
  17303                     syscall.finish();
  17304                     const n: usize = @intCast(rc);
  17305                     if (n == 0) return error.EntropyUnavailable;
  17306                     i += n;
  17307                     continue;
  17308                 },
  17309                 .INTR => {
  17310                     try syscall.checkCancel();
  17311                     continue;
  17312                 },
  17313                 else => return syscall.fail(error.EntropyUnavailable),
  17314             }
  17315         }
  17316     }
  17317 
  17318     return error.EntropyUnavailable;
  17319 }
  17320 
  17321 fn getRandomFd(t: *Threaded) Io.RandomSecureError!posix.fd_t {
  17322     {
  17323         mutexLock(&t.mutex);
  17324         defer mutexUnlock(&t.mutex);
  17325 
  17326         if (t.random_file.fd == -2) return error.EntropyUnavailable;
  17327         if (t.random_file.fd != -1) return t.random_file.fd;
  17328     }
  17329 
  17330     const mode: posix.mode_t = 0;
  17331 
  17332     const fd: posix.fd_t = fd: {
  17333         const syscall: Syscall = try .start();
  17334         while (true) {
  17335             const rc = openat_sym(posix.AT.FDCWD, "/dev/urandom", .{
  17336                 .ACCMODE = .RDONLY,
  17337                 .CLOEXEC = true,
  17338             }, mode);
  17339             switch (posix.errno(rc)) {
  17340                 .SUCCESS => {
  17341                     syscall.finish();
  17342                     break :fd @intCast(rc);
  17343                 },
  17344                 .INTR => {
  17345                     try syscall.checkCancel();
  17346                     continue;
  17347                 },
  17348                 else => return syscall.fail(error.EntropyUnavailable),
  17349             }
  17350         }
  17351     };
  17352     errdefer closeFd(fd);
  17353 
  17354     switch (native_os) {
  17355         .linux => {
  17356             const sys = if (statx_use_c) std.c else std.os.linux;
  17357             const syscall: Syscall = try .start();
  17358             while (true) {
  17359                 var statx = std.mem.zeroes(std.os.linux.Statx);
  17360                 switch (sys.errno(sys.statx(fd, "", std.os.linux.AT.EMPTY_PATH, .{ .TYPE = true }, &statx))) {
  17361                     .SUCCESS => {
  17362                         syscall.finish();
  17363                         if (!statx.mask.TYPE) return error.EntropyUnavailable;
  17364                         mutexLock(&t.mutex); // Another thread might have won the race.
  17365                         defer mutexUnlock(&t.mutex);
  17366                         if (t.random_file.fd >= 0) {
  17367                             closeFd(fd);
  17368                             return t.random_file.fd;
  17369                         } else if (!posix.S.ISCHR(statx.mode)) {
  17370                             t.random_file.fd = -2;
  17371                             return error.EntropyUnavailable;
  17372                         } else {
  17373                             t.random_file.fd = fd;
  17374                             return fd;
  17375                         }
  17376                     },
  17377                     .INTR => {
  17378                         try syscall.checkCancel();
  17379                         continue;
  17380                     },
  17381                     else => return syscall.fail(error.EntropyUnavailable),
  17382                 }
  17383             }
  17384         },
  17385         else => {
  17386             const syscall: Syscall = try .start();
  17387             while (true) {
  17388                 var stat = std.mem.zeroes(posix.Stat);
  17389                 switch (posix.errno(fstat_sym(fd, &stat))) {
  17390                     .SUCCESS => {
  17391                         syscall.finish();
  17392                         mutexLock(&t.mutex); // Another thread might have won the race.
  17393                         defer mutexUnlock(&t.mutex);
  17394                         if (t.random_file.fd >= 0) {
  17395                             closeFd(fd);
  17396                             return t.random_file.fd;
  17397                         } else if (!posix.S.ISCHR(stat.mode)) {
  17398                             t.random_file.fd = -2;
  17399                             return error.EntropyUnavailable;
  17400                         } else {
  17401                             t.random_file.fd = fd;
  17402                             return fd;
  17403                         }
  17404                     },
  17405                     .INTR => {
  17406                         try syscall.checkCancel();
  17407                         continue;
  17408                     },
  17409                     else => return syscall.fail(error.EntropyUnavailable),
  17410                 }
  17411             }
  17412         },
  17413     }
  17414 }
  17415 
  17416 test {
  17417     _ = @import("Threaded/test.zig");
  17418 }
  17419 
  17420 const use_parking_futex = switch (native_os) {
  17421     .windows => true, // RtlWaitOnAddress is a userland implementation anyway
  17422     .netbsd => true, // NetBSD has `futex(2)`, but it's historically been quite buggy. TODO: evaluate whether it's okay to use now.
  17423     .illumos => true, // Illumos has no futex mechanism
  17424     else => false,
  17425 };
  17426 const use_parking_sleep = switch (native_os) {
  17427     // On Windows, we can implement sleep either with `NtDelayExecution` (which is how `SleepEx` in
  17428     // kernel32 works) or `NtWaitForAlertByThreadId` (thread parking). We're already using the
  17429     // latter for futex, so we may as well use it for sleeping too, to maximise code reuse. I'm
  17430     // also more confident that it will always correctly handle the cancelation race (so "unpark"
  17431     // before "park" causes "park" to return immediately): it *seems* like alertable sleeps paired
  17432     // with `NtAlertThread` do actually do this too, but there could be some caveat (e.g. it might
  17433     // fail under some specific condition), whereas `NtWaitForAlertByThreadId` must reliably trigger
  17434     // this behavior because `RtlWaitOnAddress` relies on it.
  17435     .windows => true,
  17436 
  17437     // These targets have `_lwp_park`, which is superior to POSIX nanosleep because it has a better
  17438     // cancelation mechanism.
  17439     .netbsd,
  17440     .illumos,
  17441     => true,
  17442 
  17443     else => false,
  17444 };
  17445 
  17446 const parking_futex = struct {
  17447     comptime {
  17448         assert(use_parking_futex);
  17449     }
  17450 
  17451     const Bucket = struct {
  17452         /// Used as a fast check for `wake` to avoid having to acquire `mutex` to discover there are no
  17453         /// waiters. It is important for `wait` to increment this *before* checking the futex value to
  17454         /// avoid a race.
  17455         num_waiters: std.atomic.Value(u32),
  17456         /// Protects `waiters`.
  17457         mutex: ParkingMutex,
  17458         waiters: std.DoublyLinkedList,
  17459 
  17460         /// Prevent false sharing between buckets.
  17461         _: void align(std.atomic.cache_line) = {},
  17462 
  17463         const init: Bucket = .{ .num_waiters = .init(0), .mutex = .init, .waiters = .{} };
  17464     };
  17465 
  17466     const Waiter = struct {
  17467         node: std.DoublyLinkedList.Node,
  17468         address: usize,
  17469         tid: std.Thread.Id,
  17470         /// `thread_status.cancelation` is `.parked` while the thread is waiting. The single thread
  17471         /// which atomically updates it (to `.none` or `.canceling`) is responsible for:
  17472         ///
  17473         /// * Removing the `Waiter` from `Bucket.waiters`
  17474         /// * Decrementing `Bucket.num_waiters`
  17475         /// * Unparking the thread (*after* the above, so that the `Waiter` does not go out of scope
  17476         ///   while it is still in the `Bucket`).
  17477         thread_status: *std.atomic.Value(Thread.Status),
  17478         unpark_flag: if (need_unpark_flag) *UnparkFlag else void,
  17479     };
  17480 
  17481     fn bucketForAddress(address: usize) *Bucket {
  17482         const global = struct {
  17483             /// Length must be a power of two. The longer this array, the less likely contention is
  17484             /// between different futexes. This length seems like it'll provide a reasonable balance
  17485             /// between contention and memory usage: assuming a 128-byte `Bucket` (due to cache line
  17486             /// alignment), this uses 32 KiB of memory.
  17487             var buckets: [256]Bucket = @splat(.init);
  17488         };
  17489 
  17490         // Here we use Fibonacci hashing: the golden ratio can be used to evenly redistribute input
  17491         // values across a range, giving a poor, but extremely quick to compute, hash.
  17492 
  17493         // This literal is the rounded value of '2^64 / phi' (where 'phi' is the golden ratio). The
  17494         // shift then converts it to '2^b / phi', where 'b' is the pointer bit width.
  17495         const fibonacci_multiplier = 0x9E3779B97F4A7C15 >> (64 - @bitSizeOf(usize));
  17496         const hashed = address *% fibonacci_multiplier;
  17497 
  17498         comptime assert(std.math.isPowerOfTwo(global.buckets.len));
  17499         // The high bits of `hashed` have better entropy than the low bits.
  17500         const index = hashed >> (@bitSizeOf(usize) - @ctz(global.buckets.len));
  17501 
  17502         return &global.buckets[index];
  17503     }
  17504 
  17505     fn wait(ptr: *const u32, expect: u32, uncancelable: bool, timeout: Io.Timeout) Io.Cancelable!void {
  17506         const bucket = bucketForAddress(@intFromPtr(ptr));
  17507 
  17508         // Put the threadlocal access outside of the critical section.
  17509         const opt_thread = Thread.current;
  17510         const self_tid = if (opt_thread) |thread| thread.id else std.Thread.getCurrentId();
  17511 
  17512         var waiter: Waiter = .{
  17513             .node = undefined, // populated by list append
  17514             .address = @intFromPtr(ptr),
  17515             .tid = self_tid,
  17516             .thread_status = undefined, // populated in critical section
  17517             .unpark_flag = undefined, // populated in critical section
  17518         };
  17519 
  17520         var status_buf: std.atomic.Value(Thread.Status) = undefined;
  17521         var unpark_flag_buf: UnparkFlag = unpark_flag_init;
  17522 
  17523         {
  17524             bucket.mutex.lock();
  17525             defer bucket.mutex.unlock();
  17526 
  17527             _ = bucket.num_waiters.fetchAdd(1, .acquire);
  17528 
  17529             if (@atomicLoad(u32, ptr, .monotonic) != expect) {
  17530                 assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
  17531                 return;
  17532             }
  17533 
  17534             // This is in the critical section to avoid marking the thread as parked until we're
  17535             // certain that we're actually going to park.
  17536             waiter.thread_status, waiter.unpark_flag = status: {
  17537                 cancelable: {
  17538                     if (uncancelable) break :cancelable;
  17539                     const thread = opt_thread orelse break :cancelable;
  17540                     switch (thread.cancel_protection) {
  17541                         .blocked => break :cancelable,
  17542                         .unblocked => {},
  17543                     }
  17544                     thread.futex_waiter = &waiter;
  17545                     const old_status = thread.status.fetchOr(
  17546                         .{ .cancelation = @enumFromInt(0b001), .awaitable = .null },
  17547                         .release, // release `thread.futex_waiter`
  17548                     );
  17549                     switch (old_status.cancelation) {
  17550                         .none => {}, // status is now `.parked`
  17551                         .canceling => {
  17552                             // status is now `.canceled`
  17553                             assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
  17554                             return error.Canceled;
  17555                         },
  17556                         .canceled => break :cancelable, // status is still `.canceled`
  17557                         .parked => unreachable,
  17558                         .blocked => unreachable,
  17559                         .blocked_alertable => unreachable,
  17560                         .blocked_alertable_canceling => unreachable,
  17561                         .blocked_canceling => unreachable,
  17562                     }
  17563                     // We could now be unparked for a cancelation at any time!
  17564                     break :status .{ &thread.status, if (need_unpark_flag) &thread.unpark_flag };
  17565                 }
  17566                 // This is an uncancelable wait, so just use `status_buf`. Note that the value of
  17567                 // `status_buf.awaitable` is irrelevant because this is only visible to futex code,
  17568                 // while only cancelation cares about `awaitable`.
  17569                 status_buf.raw = .{ .cancelation = .parked, .awaitable = .null };
  17570                 break :status .{ &status_buf, if (need_unpark_flag) &unpark_flag_buf };
  17571             };
  17572 
  17573             bucket.waiters.append(&waiter.node);
  17574         }
  17575 
  17576         if (park(timeout, ptr, waiter.unpark_flag)) {
  17577             // We were unparked by either `wake` or cancelation, so our current status is either
  17578             // `.none` or `.canceling`. In either case, they've already removed `waiter` from
  17579             // `bucket`, so we have nothing more to do!
  17580         } else |err| switch (err) {
  17581             error.Timeout => {
  17582                 // We're not out of the woods yet: an unpark could race with the timeout.
  17583                 const old_status = waiter.thread_status.fetchAnd(
  17584                     .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones },
  17585                     .monotonic,
  17586                 );
  17587                 switch (old_status.cancelation) {
  17588                     .parked => {
  17589                         // No race. It is our responsibility to remove `waiter` from `bucket`.
  17590                         // New status is `.none`.
  17591                         bucket.mutex.lock();
  17592                         defer bucket.mutex.unlock();
  17593                         bucket.waiters.remove(&waiter.node);
  17594                         assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
  17595                     },
  17596                     .none, .canceling => {
  17597                         // Race condition: the timeout was reached, then `wake` or a canceler tried
  17598                         // to unpark us. Whoever did that will remove us from `bucket`. Wait for
  17599                         // that (and drop the unpark request in doing so).
  17600                         // New status is `.none` or `.canceling` respectively.
  17601                         park(.none, ptr, waiter.unpark_flag) catch |e| switch (e) {
  17602                             error.Timeout => unreachable,
  17603                         };
  17604                     },
  17605                     .canceled => unreachable,
  17606                     .blocked => unreachable,
  17607                     .blocked_alertable => unreachable,
  17608                     .blocked_canceling => unreachable,
  17609                     .blocked_alertable_canceling => unreachable,
  17610                 }
  17611             },
  17612         }
  17613     }
  17614 
  17615     fn wake(ptr: *const u32, max_waiters: u32) void {
  17616         if (max_waiters == 0) return;
  17617 
  17618         const bucket = bucketForAddress(@intFromPtr(ptr));
  17619 
  17620         // To ensure the store to `ptr` is ordered before this check, we effectively want a `.release`
  17621         // load, but that doesn't exist in the C11 memory model, so emulate it with a non-mutating rmw.
  17622         if (bucket.num_waiters.fetchAdd(0, .release) == 0) {
  17623             @branchHint(.likely);
  17624             return; // no waiters
  17625         }
  17626 
  17627         // Waiters removed from the linked list under the mutex so we can unpark their threads outside
  17628         // of the critical section. This forms a singly-linked list of waiters using `Waiter.node.next`.
  17629         var waking_head: ?*std.DoublyLinkedList.Node = null;
  17630         {
  17631             bucket.mutex.lock();
  17632             defer bucket.mutex.unlock();
  17633 
  17634             var num_removed: u32 = 0;
  17635             var it = bucket.waiters.first;
  17636             while (num_removed < max_waiters) {
  17637                 const waiter: *Waiter = @fieldParentPtr("node", it orelse break);
  17638                 it = waiter.node.next;
  17639                 if (waiter.address != @intFromPtr(ptr)) continue;
  17640                 const old_status = waiter.thread_status.fetchAnd(
  17641                     .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones },
  17642                     .monotonic,
  17643                 );
  17644                 switch (old_status.cancelation) {
  17645                     .parked => {}, // state updated to `.none`
  17646                     .none => continue, // race with timeout; they are about to lock `bucket.mutex` and remove themselves from the bucket
  17647                     .canceling => continue, // race with a canceler who hasn't called `removeCanceledWaiter` yet
  17648                     .canceled => unreachable,
  17649                     .blocked => unreachable,
  17650                     .blocked_alertable => unreachable,
  17651                     .blocked_alertable_canceling => unreachable,
  17652                     .blocked_canceling => unreachable,
  17653                 }
  17654                 // We're waking this waiter. Remove them from the bucket and add them to our local list.
  17655                 bucket.waiters.remove(&waiter.node);
  17656                 waiter.node.next = waking_head;
  17657                 waking_head = &waiter.node;
  17658                 num_removed += 1;
  17659             }
  17660             _ = bucket.num_waiters.fetchSub(num_removed, .monotonic);
  17661         }
  17662 
  17663         var unpark_buf: [128]UnparkTid = undefined;
  17664         var unpark_len: usize = 0;
  17665 
  17666         // Finally, unpark the threads.
  17667         while (waking_head) |node| {
  17668             waking_head = node.next;
  17669             const waiter: *Waiter = @fieldParentPtr("node", node);
  17670             unpark_buf[unpark_len] = waiter.tid;
  17671             if (need_unpark_flag) setUnparkFlag(waiter.unpark_flag);
  17672             unpark_len += 1;
  17673             if (unpark_len == unpark_buf.len) {
  17674                 unpark(&unpark_buf, ptr);
  17675                 unpark_len = 0;
  17676             }
  17677         }
  17678         if (unpark_len > 0) {
  17679             unpark(unpark_buf[0..unpark_len], ptr);
  17680         }
  17681     }
  17682 
  17683     fn removeCanceledWaiter(waiter: *Waiter) void {
  17684         const bucket = bucketForAddress(waiter.address);
  17685         bucket.mutex.lock();
  17686         defer bucket.mutex.unlock();
  17687         bucket.waiters.remove(&waiter.node);
  17688         assert(bucket.num_waiters.fetchSub(1, .monotonic) > 0);
  17689     }
  17690 };
  17691 const parking_sleep = struct {
  17692     comptime {
  17693         assert(use_parking_sleep);
  17694     }
  17695     fn sleep(timeout: Io.Timeout) Io.Cancelable!void {
  17696         const opt_thread = Thread.current;
  17697         cancelable: {
  17698             const thread = opt_thread orelse break :cancelable;
  17699             switch (thread.cancel_protection) {
  17700                 .blocked => break :cancelable,
  17701                 .unblocked => {},
  17702             }
  17703             thread.futex_waiter = null;
  17704             {
  17705                 const old_status = thread.status.fetchOr(
  17706                     .{ .cancelation = @enumFromInt(0b001), .awaitable = .null },
  17707                     .release, // release `thread.futex_waiter`
  17708                 );
  17709                 switch (old_status.cancelation) {
  17710                     .none => {}, // status is now `.parked`
  17711                     .canceling => return error.Canceled, // status is now `.canceled`
  17712                     .canceled => break :cancelable, // status is still `.canceled`
  17713                     .parked => unreachable,
  17714                     .blocked => unreachable,
  17715                     .blocked_alertable => unreachable,
  17716                     .blocked_alertable_canceling => unreachable,
  17717                     .blocked_canceling => unreachable,
  17718                 }
  17719             }
  17720             if (park(timeout, null, if (need_unpark_flag) &thread.unpark_flag)) {
  17721                 // The only reason this could possibly happen is cancelation.
  17722                 const old_status = thread.status.load(.monotonic);
  17723                 assert(old_status.cancelation == .canceling);
  17724                 thread.status.store(
  17725                     .{ .cancelation = .canceled, .awaitable = old_status.awaitable },
  17726                     .monotonic,
  17727                 );
  17728                 return error.Canceled;
  17729             } else |err| switch (err) {
  17730                 error.Timeout => {
  17731                     // We're not out of the woods yet: an unpark could race with the timeout.
  17732                     const old_status = thread.status.fetchAnd(
  17733                         .{ .cancelation = @enumFromInt(0b110), .awaitable = .all_ones },
  17734                         .monotonic,
  17735                     );
  17736                     switch (old_status.cancelation) {
  17737                         .parked => return, // No race; new status is `.none`
  17738                         .canceling => {
  17739                             // Race condition: the timeout was reached, then someone tried to unpark
  17740                             // us for a cancelation. Whoever did that will have called `unpark`, so
  17741                             // drop that unpark request by waiting for it.
  17742                             // Status is still `.canceling`.
  17743                             park(.none, null, if (need_unpark_flag) &thread.unpark_flag) catch |e| switch (e) {
  17744                                 error.Timeout => unreachable,
  17745                             };
  17746                             return;
  17747                         },
  17748                         .none => unreachable,
  17749                         .canceled => unreachable,
  17750                         .blocked => unreachable,
  17751                         .blocked_alertable => unreachable,
  17752                         .blocked_canceling => unreachable,
  17753                         .blocked_alertable_canceling => unreachable,
  17754                     }
  17755                 },
  17756             }
  17757         }
  17758         // Uncancelable sleep; we expect not to be manually unparked.
  17759         var dummy_flag: UnparkFlag = unpark_flag_init;
  17760         if (park(timeout, null, if (need_unpark_flag) &dummy_flag)) {
  17761             unreachable; // unexpected unpark
  17762         } else |err| switch (err) {
  17763             error.Timeout => return,
  17764         }
  17765     }
  17766 };
  17767 const ParkingMutex = struct {
  17768     state: std.atomic.Value(State),
  17769 
  17770     const init: ParkingMutex = .{ .state = .init(.unlocked) };
  17771 
  17772     comptime {
  17773         assert(use_parking_futex);
  17774     }
  17775 
  17776     const State = enum(usize) {
  17777         unlocked = 1,
  17778         /// This value is intentionally 0 so that `waiter` returns `null`.
  17779         locked_once = 0,
  17780         /// Contended; value is a `*Waiter`.
  17781         _,
  17782         /// Returns the head of the waiter list. Illegal to call if `s == .unlocked`.
  17783         fn waiter(s: State) ?*Waiter {
  17784             return @ptrFromInt(@intFromEnum(s));
  17785         }
  17786         /// Returns a locked state where `w` is contending the lock.
  17787         /// If `w` is `null`, returns `.locked_once`.
  17788         fn fromWaiter(w: ?*Waiter) State {
  17789             return @enumFromInt(@intFromPtr(w));
  17790         }
  17791     };
  17792     const Waiter = struct {
  17793         unpark_flag: UnparkFlag,
  17794         /// Never modified once the `Waiter` is in the linked list.
  17795         next: ?*Waiter,
  17796         /// Never modified once the `Waiter` is in the linked list.
  17797         tid: std.Thread.Id,
  17798     };
  17799     fn lock(m: *ParkingMutex) void {
  17800         state: switch (State.unlocked) { // assume 'unlocked' to optimize for uncontended case
  17801             .unlocked => continue :state m.state.cmpxchgWeak(
  17802                 .unlocked,
  17803                 .locked_once,
  17804                 .acquire, // acquire lock
  17805                 .monotonic,
  17806             ) orelse {
  17807                 @branchHint(.likely);
  17808                 return;
  17809             },
  17810 
  17811             .locked_once, _ => |last_state| {
  17812                 const old_waiter = last_state.waiter();
  17813                 const self_tid = if (Thread.current) |t| t.id else std.Thread.getCurrentId();
  17814                 var waiter: Waiter = .{
  17815                     .next = old_waiter,
  17816                     .unpark_flag = unpark_flag_init,
  17817                     .tid = self_tid,
  17818                 };
  17819                 if (m.state.cmpxchgWeak(
  17820                     .fromWaiter(old_waiter),
  17821                     .fromWaiter(&waiter),
  17822                     .release, // release `waiter`
  17823                     .monotonic,
  17824                 )) |new_state| {
  17825                     continue :state new_state;
  17826                 }
  17827                 // We're now in the list of waiters---park until we're given the lock.
  17828                 park(.none, m, if (need_unpark_flag) &waiter.unpark_flag) catch |err| switch (err) {
  17829                     error.Timeout => unreachable,
  17830                 };
  17831                 return;
  17832             },
  17833         }
  17834     }
  17835     fn unlock(m: *ParkingMutex) void {
  17836         state: switch (State.locked_once) { // assume 'locked_once' to optimize for uncontended case
  17837             .unlocked => unreachable, // we hold the lock
  17838 
  17839             .locked_once => continue :state m.state.cmpxchgWeak(
  17840                 .locked_once,
  17841                 .unlocked,
  17842                 .release, // release lock
  17843                 .acquire, // acquire any `Waiter` memory
  17844             ) orelse {
  17845                 @branchHint(.likely);
  17846                 return;
  17847             },
  17848 
  17849             _ => |last_state| {
  17850                 // The logic here does not have ABA problems, and does some accesses non-atomically,
  17851                 // because `Waiter.next` is owned by the lock holder (that's us!) once the waiter is
  17852                 // in the linked list, up until we unpark the waiter.
  17853 
  17854                 // Run through the waiter list to the end to ensure fairness. This is obviously not
  17855                 // ideal, but it shouldn't be a big deal in practice provided the critical section
  17856                 // is fairly small (so we won't get too many threads contending the mutex at once).
  17857                 // There's a *chance* we could get away with a LIFO queue for our use case, but I
  17858                 // don't wanna risk that.
  17859                 var parent: ?*Waiter = null;
  17860                 var waiter: *Waiter = last_state.waiter().?;
  17861                 while (waiter.next) |next| {
  17862                     parent = waiter;
  17863                     waiter = next;
  17864                 }
  17865                 // `waiter` is next in line for the lock. Remove them from the list.
  17866                 if (parent) |p| {
  17867                     assert(p.next == waiter);
  17868                     p.next = null;
  17869                 } else {
  17870                     // We're waking the last waiter, so clear the list head.
  17871                     if (m.state.cmpxchgWeak(
  17872                         .fromWaiter(last_state.waiter().?),
  17873                         .locked_once,
  17874                         .acquire,
  17875                         .acquire, // acquire any new `Waiter` memory
  17876                     )) |new_state| {
  17877                         continue :state new_state;
  17878                     }
  17879                 }
  17880                 // Now we're ready to actually hand the lock over to them.
  17881                 const tid = waiter.tid; // load before the unpark below potentially invalidates `waiter`
  17882                 if (need_unpark_flag) setUnparkFlag(&waiter.unpark_flag);
  17883                 unpark(&.{tid}, m);
  17884                 return;
  17885             },
  17886         }
  17887     }
  17888 };
  17889 
  17890 fn timeoutToWindowsInterval(timeout: Io.Timeout) ?windows.LARGE_INTEGER {
  17891     // ntdll only supports two combinations:
  17892     // * real-time (`.real`) sleeps with absolute deadlines
  17893     // * monotonic (`.awake`/`.boot`) sleeps with relative durations
  17894     const clock = switch (timeout) {
  17895         .none => return null,
  17896         .duration => |d| d.clock,
  17897         .deadline => |d| d.clock,
  17898     };
  17899     switch (clock) {
  17900         .cpu_process, .cpu_thread => unreachable, // cannot sleep for CPU time
  17901         .real => {
  17902             const deadline = switch (timeout) {
  17903                 .none => unreachable,
  17904                 .duration => |d| nowWindows(clock).addDuration(d.raw),
  17905                 .deadline => |d| d.raw,
  17906             };
  17907             return @intCast(@max(@divTrunc(deadline.nanoseconds, 100), 0));
  17908         },
  17909         .awake, .boot => {
  17910             const duration = switch (timeout) {
  17911                 .none => unreachable,
  17912                 .duration => |d| d.raw,
  17913                 .deadline => |d| nowWindows(clock).durationTo(d.raw),
  17914             };
  17915             return @intCast(@min(@divTrunc(-duration.nanoseconds, 100), -1));
  17916         },
  17917     }
  17918 }
  17919 
  17920 /// The API on NetBSD and Illumos sucks and can unpark spuriously (well, it *can't*, but signals
  17921 /// cause an indistinguishable unblock, and libpthread really likes to leave unparks pending).
  17922 /// As such, on these targets only, we need to pass around a flag to track whether a thread is
  17923 /// "actually" being unparked.
  17924 const need_unpark_flag = switch (native_os) {
  17925     .netbsd, .illumos => true,
  17926     else => false,
  17927 };
  17928 const UnparkFlag = if (need_unpark_flag) std.atomic.Value(bool) else void;
  17929 const unpark_flag_init: UnparkFlag = if (need_unpark_flag) .init(false);
  17930 /// Must be called before `unpark`. After this function is called, the thread may be unparked at any
  17931 /// time, so the caller must not reference values on its stack.
  17932 fn setUnparkFlag(f: *UnparkFlag) void {
  17933     f.store(true, .release);
  17934 }
  17935 
  17936 /// The type passed into `unpark` for the thread ID. You'd think this was just a `std.Thread.Id`,
  17937 /// but it seems that someone at Microsoft forgot how big their TIDs are supposed to be.
  17938 const UnparkTid = switch (native_os) {
  17939     .windows => usize,
  17940     else => std.Thread.Id,
  17941 };
  17942 
  17943 fn park(
  17944     timeout: Io.Timeout,
  17945     /// This value has no semantic effect, but may allow the OS to optimize the operation.
  17946     addr_hint: ?*const anyopaque,
  17947     unpark_flag: if (need_unpark_flag) *UnparkFlag else void,
  17948 ) error{Timeout}!void {
  17949     comptime assert(use_parking_futex or use_parking_sleep);
  17950     switch (native_os) {
  17951         .windows => {
  17952             const raw_timeout = timeoutToWindowsInterval(timeout);
  17953             // `RtlWaitOnAddress` passes the futex address in as the first argument to this call,
  17954             // but it's unclear what that actually does, especially since `NtAlertThreadByThreadId`
  17955             // does *not* accept the address so the kernel can't really be using it as a hint. An
  17956             // old Microsoft blog post discusses a more traditional futex-like mechanism in the
  17957             // kernel which definitely isn't how `RtlWaitOnAddress` works today:
  17958             //
  17959             // https://devblogs.microsoft.com/oldnewthing/20160826-00/?p=94185
  17960             //
  17961             // ...so it's possible this argument is simply a remnant which no longer does anything
  17962             // (perhaps the implementation changed during development but someone forgot to remove
  17963             // this parameter). However, to err on the side of caution, let's match the behavior of
  17964             // `RtlWaitOnAddress` and pass the pointer, in case the kernel ever does something
  17965             // stupid such as trying to dereference it.
  17966             switch (windows.ntdll.NtWaitForAlertByThreadId(
  17967                 addr_hint,
  17968                 if (raw_timeout) |*t| t else null,
  17969             )) {
  17970                 .ALERTED => return,
  17971                 .TIMEOUT => return error.Timeout,
  17972                 else => unreachable,
  17973             }
  17974         },
  17975         .netbsd => {
  17976             var ts_buf: posix.timespec = undefined;
  17977             const ts: ?*posix.timespec, const abstime: bool, const clock_real: bool = switch (timeout) {
  17978                 .none => .{ null, false, false },
  17979                 .deadline => |timestamp| timeout: {
  17980                     ts_buf = timestampToPosix(timestamp.raw.nanoseconds);
  17981                     break :timeout .{ &ts_buf, true, timestamp.clock == .real };
  17982                 },
  17983                 .duration => |duration| timeout: {
  17984                     ts_buf = timestampToPosix(duration.raw.nanoseconds);
  17985                     break :timeout .{ &ts_buf, false, duration.clock == .real };
  17986                 },
  17987             };
  17988             // It's okay to pass the same timeout in a loop. If it's a duration, the OS actually
  17989             // writes the remaining time into the buffer when the syscall returns.
  17990             while (!unpark_flag.swap(false, .acquire)) {
  17991                 switch (posix.errno(std.c._lwp_park(
  17992                     if (clock_real) .REALTIME else .MONOTONIC,
  17993                     .{ .ABSTIME = abstime },
  17994                     ts,
  17995                     0,
  17996                     addr_hint,
  17997                     null,
  17998                 ))) {
  17999                     .SUCCESS, .ALREADY, .INTR => {},
  18000                     .TIMEDOUT => return error.Timeout,
  18001                     .INVAL => unreachable,
  18002                     .SRCH => unreachable,
  18003                     else => unreachable,
  18004                 }
  18005             }
  18006         },
  18007         .illumos => @panic("TODO: illumos lwp_park"),
  18008         else => comptime unreachable,
  18009     }
  18010 }
  18011 /// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation.
  18012 fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
  18013     comptime assert(use_parking_futex or use_parking_sleep);
  18014     switch (native_os) {
  18015         .windows => {
  18016             // TODO: this condition is currently disabled because mingw-w64 does not contain this
  18017             // symbol. Once it's added, enable this check to use the new bulk API where possible.
  18018             if (false and (builtin.os.version_range.windows.isAtLeast(.win11_dt) orelse false)) {
  18019                 _ = windows.ntdll.NtAlertMultipleThreadByThreadId(tids.ptr, @intCast(tids.len), null, null);
  18020             } else {
  18021                 for (tids) |tid| {
  18022                     _ = windows.ntdll.NtAlertThreadByThreadId(@intCast(tid));
  18023                 }
  18024             }
  18025         },
  18026         .netbsd => {
  18027             switch (posix.errno(std.c._lwp_unpark_all(@ptrCast(tids.ptr), tids.len, addr_hint))) {
  18028                 .SUCCESS => return,
  18029                 // For errors, fall through to a loop over `tids`, though this is only expected to
  18030                 // be possible for ENOMEM (even that is questionable) and ESRCH (see comment below).
  18031                 .SRCH => {},
  18032                 .FAULT => recoverableOsBugDetected(),
  18033                 .INVAL => recoverableOsBugDetected(),
  18034                 .NOMEM => {},
  18035                 else => recoverableOsBugDetected(),
  18036             }
  18037             for (tids) |tid| {
  18038                 switch (posix.errno(std.c._lwp_unpark(@bitCast(tid), addr_hint))) {
  18039                     .SUCCESS => {},
  18040                     .SRCH => {
  18041                         // This can happen in a rare race: the thread might have been spuriously
  18042                         // unparked, so already observed the changing status, and from there have
  18043                         // exited. That's okay, because the thread has woken up like we wanted.
  18044                     },
  18045                     else => recoverableOsBugDetected(),
  18046                 }
  18047             }
  18048         },
  18049         .illumos => @panic("TODO: illumos lwp_unpark"),
  18050         else => comptime unreachable,
  18051     }
  18052 }
  18053 
  18054 pub const PipeError = error{
  18055     SystemFdQuotaExceeded,
  18056     ProcessFdQuotaExceeded,
  18057 } || Io.UnexpectedError;
  18058 
  18059 pub fn pipe2(flags: posix.O) PipeError![2]posix.fd_t {
  18060     var fds: [2]posix.fd_t = undefined;
  18061 
  18062     if (@TypeOf(posix.system.pipe2) != void) {
  18063         switch (posix.errno(posix.system.pipe2(&fds, flags))) {
  18064             .SUCCESS => return fds,
  18065             .INVAL => |err| return errnoBug(err), // Invalid flags
  18066             .NFILE => return error.SystemFdQuotaExceeded,
  18067             .MFILE => return error.ProcessFdQuotaExceeded,
  18068             else => |err| return posix.unexpectedErrno(err),
  18069         }
  18070     }
  18071 
  18072     switch (posix.errno(posix.system.pipe(&fds))) {
  18073         .SUCCESS => {},
  18074         .NFILE => return error.SystemFdQuotaExceeded,
  18075         .MFILE => return error.ProcessFdQuotaExceeded,
  18076         else => |err| return posix.unexpectedErrno(err),
  18077     }
  18078     errdefer {
  18079         closeFd(fds[0]);
  18080         closeFd(fds[1]);
  18081     }
  18082 
  18083     // https://github.com/ziglang/zig/issues/18882
  18084     if (@as(u32, @bitCast(flags)) == 0) return fds;
  18085 
  18086     // CLOEXEC is special, it's a file descriptor flag and must be set using
  18087     // F.SETFD.
  18088     if (flags.CLOEXEC) for (fds) |fd| {
  18089         switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFD, @as(u32, posix.FD_CLOEXEC)))) {
  18090             .SUCCESS => {},
  18091             else => |err| return posix.unexpectedErrno(err),
  18092         }
  18093     };
  18094 
  18095     const new_flags: u32 = f: {
  18096         var new_flags = flags;
  18097         new_flags.CLOEXEC = false;
  18098         break :f @bitCast(new_flags);
  18099     };
  18100 
  18101     // Set every other flag affecting the file status using F.SETFL.
  18102     if (new_flags != 0) for (fds) |fd| {
  18103         switch (posix.errno(posix.system.fcntl(fd, posix.F.SETFL, new_flags))) {
  18104             .SUCCESS => {},
  18105             .INVAL => |err| return errnoBug(err),
  18106             else => |err| return posix.unexpectedErrno(err),
  18107         }
  18108     };
  18109 
  18110     return fds;
  18111 }
  18112 
  18113 pub const DupError = error{
  18114     ProcessFdQuotaExceeded,
  18115     SystemResources,
  18116 } || Io.UnexpectedError || Io.Cancelable;
  18117 
  18118 pub fn dup2(old_fd: posix.fd_t, new_fd: posix.fd_t) DupError!void {
  18119     const syscall: Syscall = try .start();
  18120     while (true) switch (posix.errno(posix.system.dup2(old_fd, new_fd))) {
  18121         .SUCCESS => return syscall.finish(),
  18122         .BUSY, .INTR => {
  18123             try syscall.checkCancel();
  18124             continue;
  18125         },
  18126         .INVAL => |err| return syscall.errnoBug(err), // invalid parameters
  18127         .BADF => |err| return syscall.errnoBug(err), // use after free
  18128         .MFILE => return syscall.fail(error.ProcessFdQuotaExceeded),
  18129         .NOMEM => return syscall.fail(error.SystemResources),
  18130         else => |err| return syscall.unexpectedErrno(err),
  18131     };
  18132 }
  18133 
  18134 pub const FchdirError = error{
  18135     AccessDenied,
  18136     NotDir,
  18137     FileSystem,
  18138 } || Io.Cancelable || Io.UnexpectedError;
  18139 
  18140 pub fn fchdir(fd: posix.fd_t) FchdirError!void {
  18141     if (fd == posix.AT.FDCWD) return;
  18142     const syscall: Syscall = try .start();
  18143     while (true) switch (posix.errno(posix.system.fchdir(fd))) {
  18144         .SUCCESS => return syscall.finish(),
  18145         .INTR => {
  18146             try syscall.checkCancel();
  18147             continue;
  18148         },
  18149         .ACCES => return syscall.fail(error.AccessDenied),
  18150         .NOTDIR => return syscall.fail(error.NotDir),
  18151         .IO => return syscall.fail(error.FileSystem),
  18152         .BADF => |err| return syscall.errnoBug(err),
  18153         else => |err| return syscall.unexpectedErrno(err),
  18154     };
  18155 }
  18156 
  18157 pub const ChdirError = error{
  18158     AccessDenied,
  18159     FileSystem,
  18160     SymLinkLoop,
  18161     NameTooLong,
  18162     FileNotFound,
  18163     SystemResources,
  18164     NotDir,
  18165     BadPathName,
  18166 } || Io.Cancelable || Io.UnexpectedError;
  18167 
  18168 pub fn chdir(dir_path: []const u8) ChdirError!void {
  18169     var path_buffer: [posix.PATH_MAX]u8 = undefined;
  18170     const dir_path_posix = try pathToPosix(dir_path, &path_buffer);
  18171     const syscall: Syscall = try .start();
  18172     while (true) switch (posix.errno(posix.system.chdir(dir_path_posix))) {
  18173         .SUCCESS => return syscall.finish(),
  18174         .INTR => {
  18175             try syscall.checkCancel();
  18176             continue;
  18177         },
  18178         .ACCES => return syscall.fail(error.AccessDenied),
  18179         .IO => return syscall.fail(error.FileSystem),
  18180         .LOOP => return syscall.fail(error.SymLinkLoop),
  18181         .NAMETOOLONG => return syscall.fail(error.NameTooLong),
  18182         .NOENT => return syscall.fail(error.FileNotFound),
  18183         .NOMEM => return syscall.fail(error.SystemResources),
  18184         .NOTDIR => return syscall.fail(error.NotDir),
  18185         .ILSEQ => return syscall.fail(error.BadPathName),
  18186         .FAULT => |err| return syscall.errnoBug(err),
  18187         else => |err| return syscall.unexpectedErrno(err),
  18188     };
  18189 }
  18190 
  18191 fn fileMemoryMapCreate(
  18192     userdata: ?*anyopaque,
  18193     file: File,
  18194     options: File.MemoryMap.CreateOptions,
  18195 ) File.MemoryMap.CreateError!File.MemoryMap {
  18196     const t: *Threaded = @ptrCast(@alignCast(userdata));
  18197     const offset = options.offset;
  18198     const len = options.len;
  18199 
  18200     if (!t.disable_memory_mapping) {
  18201         if (createFileMap(file, options.protection, offset, options.populate, len)) |result| {
  18202             return result;
  18203         } else |err| switch (err) {
  18204             error.Unseekable, error.Canceled, error.AccessDenied => |e| return e,
  18205             error.OperationUnsupported => {},
  18206             else => {
  18207                 if (builtin.mode == .Debug)
  18208                     std.log.warn("memory mapping failed with {t}, falling back to file operations", .{err});
  18209             },
  18210         }
  18211     }
  18212 
  18213     const gpa = t.allocator;
  18214     const page_size = std.heap.pageSize();
  18215     const alignment: Alignment = .fromByteUnits(page_size);
  18216     const memory = m: {
  18217         const ptr = gpa.rawAlloc(len, alignment, @returnAddress()) orelse return error.OutOfMemory;
  18218         break :m ptr[0..len];
  18219     };
  18220     errdefer gpa.rawFree(memory, alignment, @returnAddress());
  18221 
  18222     if (!options.undefined_contents) try mmSyncRead(file, memory, offset);
  18223 
  18224     return .{
  18225         .file = file,
  18226         .offset = offset,
  18227         .memory = @alignCast(memory),
  18228         .section = null,
  18229     };
  18230 }
  18231 
  18232 const CreateFileMapError = error{
  18233     /// MaximumSize is greater than the system-defined maximum for sections, or
  18234     /// greater than the specified file and the section is not writable.
  18235     SectionOversize,
  18236     /// A file descriptor refers to a non-regular file. Or a file mapping was requested,
  18237     /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested
  18238     /// and `PROT_WRITE` is set, but the file descriptor is not open in `RDWR` mode.
  18239     /// Or `PROT_WRITE` is set, but the file is append-only.
  18240     AccessDenied,
  18241     /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
  18242     /// a filesystem that was mounted no-exec.
  18243     PermissionDenied,
  18244     FileBusy,
  18245     LockedMemoryLimitExceeded,
  18246     OperationUnsupported,
  18247     ProcessFdQuotaExceeded,
  18248     SystemFdQuotaExceeded,
  18249     OutOfMemory,
  18250     MappingAlreadyExists,
  18251     Unseekable,
  18252     LockViolation,
  18253 } || Io.Cancelable || Io.UnexpectedError;
  18254 
  18255 fn createFileMap(
  18256     file: File,
  18257     protection: std.process.MemoryProtection,
  18258     offset: u64,
  18259     populate: bool,
  18260     len: usize,
  18261 ) CreateFileMapError!File.MemoryMap {
  18262     if (is_windows) {
  18263         try Thread.checkCancel();
  18264 
  18265         var section = windows.INVALID_HANDLE_VALUE;
  18266         const section_size: windows.LARGE_INTEGER = @intCast(len);
  18267         const page = windows.PAGE.fromProtection(protection) orelse return error.AccessDenied;
  18268         switch (windows.ntdll.NtCreateSection(
  18269             &section,
  18270             .{
  18271                 .SPECIFIC = .{ .SECTION = .{
  18272                     .QUERY = true,
  18273                     .MAP_WRITE = protection.write,
  18274                     .MAP_READ = protection.read,
  18275                     .MAP_EXECUTE = protection.execute,
  18276                     .EXTEND_SIZE = true,
  18277                 } },
  18278                 .STANDARD = .{ .RIGHTS = .REQUIRED },
  18279             },
  18280             null,
  18281             &section_size,
  18282             page,
  18283             .{ .COMMIT = populate },
  18284             file.handle,
  18285         )) {
  18286             .SUCCESS => {},
  18287             .FILE_LOCK_CONFLICT => return error.LockViolation,
  18288             .INVALID_FILE_FOR_SECTION => return error.OperationUnsupported,
  18289             .ACCESS_DENIED => return error.AccessDenied,
  18290             .SECTION_TOO_BIG => return error.SectionOversize,
  18291             else => |status| return windows.unexpectedStatus(status),
  18292         }
  18293         var contents_ptr: ?[*]align(std.heap.page_size_min) u8 = null;
  18294         var contents_len = len;
  18295         switch (windows.ntdll.NtMapViewOfSection(
  18296             section,
  18297             windows.current_process,
  18298             @ptrCast(&contents_ptr),
  18299             null,
  18300             0,
  18301             null,
  18302             &contents_len,
  18303             .Unmap,
  18304             .{},
  18305             page,
  18306         )) {
  18307             .SUCCESS => {},
  18308             .CONFLICTING_ADDRESSES => return error.MappingAlreadyExists,
  18309             .SECTION_PROTECTION => return error.PermissionDenied,
  18310             .ACCESS_DENIED => return error.AccessDenied,
  18311             .INVALID_VIEW_SIZE => |status| return windows.statusBug(status),
  18312             else => |status| return windows.unexpectedStatus(status),
  18313         }
  18314         if (builtin.mode == .Debug) {
  18315             const page_size = std.heap.pageSize();
  18316             const alignment: Alignment = .fromByteUnits(page_size);
  18317             assert(contents_len == alignment.forward(len));
  18318         }
  18319         return .{
  18320             .file = file,
  18321             .offset = offset,
  18322             .memory = contents_ptr.?[0..len],
  18323             .section = section,
  18324         };
  18325     } else if (have_mmap) {
  18326         const prot: posix.PROT = .{
  18327             .READ = protection.read,
  18328             .WRITE = protection.write,
  18329             .EXEC = protection.execute,
  18330         };
  18331         const flags: posix.MAP = switch (native_os) {
  18332             .linux => .{
  18333                 .TYPE = .SHARED_VALIDATE,
  18334                 .POPULATE = populate,
  18335             },
  18336             else => .{
  18337                 .TYPE = .SHARED,
  18338             },
  18339         };
  18340 
  18341         const page_align = std.heap.page_size_min;
  18342 
  18343         const contents = while (true) {
  18344             const syscall: Syscall = try .start();
  18345             const casted_offset = std.math.cast(i64, offset) orelse return error.Unseekable;
  18346             const rc = mmap_sym(null, len, prot, flags, file.handle, casted_offset);
  18347             syscall.finish();
  18348             const err: posix.E = if (builtin.link_libc) e: {
  18349                 if (rc != std.c.MAP_FAILED) {
  18350                     break @as([*]align(page_align) u8, @ptrCast(@alignCast(rc)))[0..len];
  18351                 }
  18352                 break :e @enumFromInt(posix.system._errno().*);
  18353             } else e: {
  18354                 const err = posix.errno(rc);
  18355                 if (err == .SUCCESS) {
  18356                     break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..len];
  18357                 }
  18358                 break :e err;
  18359             };
  18360             switch (err) {
  18361                 .SUCCESS => unreachable,
  18362                 .INTR => continue,
  18363                 .ACCES => return error.AccessDenied,
  18364                 .AGAIN => return error.LockedMemoryLimitExceeded,
  18365                 .EXIST => return error.MappingAlreadyExists,
  18366                 .MFILE => return error.ProcessFdQuotaExceeded,
  18367                 .NFILE => return error.SystemFdQuotaExceeded,
  18368                 .NODEV => return error.OperationUnsupported,
  18369                 .NOMEM => return error.OutOfMemory,
  18370                 .PERM => return error.PermissionDenied,
  18371                 .TXTBSY => return error.FileBusy,
  18372                 .OVERFLOW => return error.Unseekable,
  18373                 .BADF => return errnoBug(err), // Always a race condition.
  18374                 .INVAL => return errnoBug(err), // Invalid parameters to mmap()
  18375                 .OPNOTSUPP => return errnoBug(err), // Bad flags with MAP.SHARED_VALIDATE on Linux.
  18376                 else => return posix.unexpectedErrno(err),
  18377             }
  18378         };
  18379         return .{
  18380             .file = file,
  18381             .offset = offset,
  18382             .memory = contents,
  18383             .section = {},
  18384         };
  18385     }
  18386 
  18387     return error.OperationUnsupported;
  18388 }
  18389 
  18390 fn fileMemoryMapDestroy(userdata: ?*anyopaque, mm: *File.MemoryMap) void {
  18391     const t: *Threaded = @ptrCast(@alignCast(userdata));
  18392     const memory = mm.memory;
  18393     if (mm.section) |section| switch (native_os) {
  18394         .windows => {
  18395             if (section == windows.INVALID_HANDLE_VALUE) return;
  18396             _ = windows.ntdll.NtUnmapViewOfSection(windows.current_process, memory.ptr);
  18397             windows.CloseHandle(section);
  18398         },
  18399         .wasi => unreachable,
  18400         else => {
  18401             if (memory.len == 0) return;
  18402             switch (posix.errno(posix.system.munmap(memory.ptr, memory.len))) {
  18403                 .SUCCESS => {},
  18404                 else => |e| {
  18405                     if (builtin.mode == .Debug)
  18406                         std.log.err("failed to unmap {d} bytes at {*}: {t}", .{ memory.len, memory.ptr, e });
  18407                 },
  18408             }
  18409         },
  18410     } else {
  18411         const gpa = t.allocator;
  18412         gpa.rawFree(memory, .fromByteUnits(std.heap.pageSize()), @returnAddress());
  18413     }
  18414     mm.* = undefined;
  18415 }
  18416 
  18417 fn fileMemoryMapSetLength(
  18418     userdata: ?*anyopaque,
  18419     mm: *File.MemoryMap,
  18420     new_len: usize,
  18421 ) File.MemoryMap.SetLengthError!void {
  18422     const t: *Threaded = @ptrCast(@alignCast(userdata));
  18423     const page_size = std.heap.pageSize();
  18424     const alignment: Alignment = .fromByteUnits(page_size);
  18425     const page_align = std.heap.page_size_min;
  18426     const old_memory = mm.memory;
  18427 
  18428     if (mm.section) |section| {
  18429         _ = section;
  18430         if (alignment.forward(new_len) == alignment.forward(old_memory.len)) {
  18431             mm.memory.len = new_len;
  18432             return;
  18433         }
  18434         switch (native_os) {
  18435             .wasi => unreachable,
  18436             .linux => {
  18437                 const flags: posix.MREMAP = .{ .MAYMOVE = true };
  18438                 const addr_hint: ?[*]const u8 = null;
  18439                 const new_memory = while (true) {
  18440                     const syscall: Syscall = try .start();
  18441                     const rc = posix.system.mremap(old_memory.ptr, old_memory.len, new_len, flags, addr_hint);
  18442                     syscall.finish();
  18443                     const err: posix.E = if (builtin.link_libc) e: {
  18444                         if (rc != std.c.MAP_FAILED) break @as([*]align(page_align) u8, @ptrCast(@alignCast(rc)))[0..new_len];
  18445                         break :e @enumFromInt(posix.system._errno().*);
  18446                     } else e: {
  18447                         const err = posix.errno(rc);
  18448                         if (err == .SUCCESS) break @as([*]align(page_align) u8, @ptrFromInt(rc))[0..new_len];
  18449                         break :e err;
  18450                     };
  18451                     switch (err) {
  18452                         .SUCCESS => unreachable,
  18453                         .INTR => continue,
  18454                         .AGAIN => return error.LockedMemoryLimitExceeded,
  18455                         .NOMEM => return error.OutOfMemory,
  18456                         .INVAL => return errnoBug(err),
  18457                         .FAULT => return errnoBug(err),
  18458                         else => return posix.unexpectedErrno(err),
  18459                     }
  18460                 };
  18461                 mm.memory = new_memory;
  18462                 return;
  18463             },
  18464             else => return error.OperationUnsupported,
  18465         }
  18466     } else {
  18467         const gpa = t.allocator;
  18468         if (gpa.rawRemap(old_memory, alignment, new_len, @returnAddress())) |new_ptr| {
  18469             mm.memory = @alignCast(new_ptr[0..new_len]);
  18470         } else {
  18471             const new_ptr: [*]align(page_align) u8 = @alignCast(
  18472                 gpa.rawAlloc(new_len, alignment, @returnAddress()) orelse return error.OutOfMemory,
  18473             );
  18474             const copy_len = @min(new_len, old_memory.len);
  18475             @memcpy(new_ptr[0..copy_len], old_memory[0..copy_len]);
  18476             mm.memory = new_ptr[0..new_len];
  18477             gpa.rawFree(old_memory, alignment, @returnAddress());
  18478         }
  18479     }
  18480 }
  18481 
  18482 fn fileMemoryMapRead(userdata: ?*anyopaque, mm: *File.MemoryMap) File.ReadPositionalError!void {
  18483     const t: *Threaded = @ptrCast(@alignCast(userdata));
  18484     _ = t;
  18485     const section = mm.section orelse return mmSyncRead(mm.file, mm.memory, mm.offset);
  18486     _ = section;
  18487 }
  18488 
  18489 fn fileMemoryMapWrite(userdata: ?*anyopaque, mm: *File.MemoryMap) File.WritePositionalError!void {
  18490     const t: *Threaded = @ptrCast(@alignCast(userdata));
  18491     _ = t;
  18492     const section = mm.section orelse return mmSyncWrite(mm.file, mm.memory, mm.offset);
  18493     _ = section;
  18494 }
  18495 
  18496 fn mmSyncRead(file: File, memory: []u8, offset: u64) File.ReadPositionalError!void {
  18497     if (is_windows) {
  18498         var i: usize = 0;
  18499         while (true) {
  18500             const buf = memory[i..];
  18501             if (buf.len == 0) break;
  18502             const n = try readFilePositionalWindows(file, buf, offset + i);
  18503             if (n == 0) {
  18504                 @memset(memory[i..], 0);
  18505                 break;
  18506             }
  18507             i += n;
  18508         }
  18509     } else if (native_os == .wasi and !builtin.link_libc) {
  18510         var i: usize = 0;
  18511         const syscall: Syscall = try .start();
  18512         while (true) {
  18513             const buf = memory[i..];
  18514             if (buf.len == 0) {
  18515                 syscall.finish();
  18516                 break;
  18517             }
  18518             var n: usize = undefined;
  18519             const vec: std.os.wasi.iovec_t = .{ .base = buf.ptr, .len = buf.len };
  18520             switch (std.os.wasi.fd_pread(file.handle, (&vec)[0..1], 1, offset + i, &n)) {
  18521                 .SUCCESS => {
  18522                     if (n == 0) {
  18523                         syscall.finish();
  18524                         @memset(memory[i..], 0);
  18525                         break;
  18526                     }
  18527                     i += n;
  18528                     try syscall.checkCancel();
  18529                     continue;
  18530                 },
  18531                 .INTR, .TIMEDOUT => {
  18532                     try syscall.checkCancel();
  18533                     continue;
  18534                 },
  18535                 .NOTCONN => |err| return syscall.errnoBug(err), // not a socket
  18536                 .CONNRESET => |err| return syscall.errnoBug(err), // not a socket
  18537                 .BADF => |err| return syscall.errnoBug(err), // use after free
  18538                 .INVAL => |err| return syscall.errnoBug(err),
  18539                 .FAULT => |err| return syscall.errnoBug(err), // segmentation fault
  18540                 .AGAIN => |err| return syscall.errnoBug(err),
  18541                 .IO => return syscall.fail(error.InputOutput),
  18542                 .ISDIR => return syscall.fail(error.IsDir),
  18543                 .NOBUFS => return syscall.fail(error.SystemResources),
  18544                 .NOMEM => return syscall.fail(error.SystemResources),
  18545                 .NXIO => return syscall.fail(error.Unseekable),
  18546                 .SPIPE => return syscall.fail(error.Unseekable),
  18547                 .OVERFLOW => return syscall.fail(error.Unseekable),
  18548                 .NOTCAPABLE => return syscall.fail(error.AccessDenied),
  18549                 else => |err| return syscall.unexpectedErrno(err),
  18550             }
  18551         }
  18552     } else {
  18553         var i: usize = 0;
  18554         const syscall: Syscall = try .start();
  18555         while (true) {
  18556             const buf = memory[i..];
  18557             if (buf.len == 0) {
  18558                 syscall.finish();
  18559                 break;
  18560             }
  18561             const rc = pread_sym(file.handle, buf.ptr, buf.len, @intCast(offset + i));
  18562             switch (posix.errno(rc)) {
  18563                 .SUCCESS => {
  18564                     const n: usize = @intCast(rc);
  18565                     if (n == 0) {
  18566                         syscall.finish();
  18567                         @memset(memory[i..], 0);
  18568                         break;
  18569                     }
  18570                     i += n;
  18571                     try syscall.checkCancel();
  18572                     continue;
  18573                 },
  18574                 .INTR, .TIMEDOUT => {
  18575                     try syscall.checkCancel();
  18576                     continue;
  18577                 },
  18578                 .NXIO => return syscall.fail(error.Unseekable),
  18579                 .SPIPE => return syscall.fail(error.Unseekable),
  18580                 .OVERFLOW => return syscall.fail(error.Unseekable),
  18581                 .NOBUFS => return syscall.fail(error.SystemResources),
  18582                 .NOMEM => return syscall.fail(error.SystemResources),
  18583                 .AGAIN => return syscall.fail(error.WouldBlock),
  18584                 .IO => return syscall.fail(error.InputOutput),
  18585                 .ISDIR => return syscall.fail(error.IsDir),
  18586                 .NOTCONN => |err| return syscall.errnoBug(err), // not a socket
  18587                 .CONNRESET => |err| return syscall.errnoBug(err), // not a socket
  18588                 .INVAL => |err| return syscall.errnoBug(err),
  18589                 .FAULT => |err| return syscall.errnoBug(err),
  18590                 .BADF => |err| return syscall.errnoBug(err), // use after free
  18591                 else => |err| return syscall.unexpectedErrno(err),
  18592             }
  18593         }
  18594     }
  18595 }
  18596 
  18597 fn mmSyncWrite(file: File, memory: []u8, offset: u64) File.WritePositionalError!void {
  18598     if (is_windows) {
  18599         var i: usize = 0;
  18600         while (true) {
  18601             const buf = memory[i..];
  18602             if (buf.len == 0) break;
  18603             i += try writeFilePositionalWindows(file, memory[i..], offset + i);
  18604         }
  18605     } else if (native_os == .wasi and !builtin.link_libc) {
  18606         var i: usize = 0;
  18607         var n: usize = undefined;
  18608         const syscall: Syscall = try .start();
  18609         while (true) {
  18610             const buf = memory[i..];
  18611             if (buf.len == 0) {
  18612                 syscall.finish();
  18613                 break;
  18614             }
  18615             const iovec: std.os.wasi.ciovec_t = .{ .base = buf.ptr, .len = buf.len };
  18616             switch (std.os.wasi.fd_pwrite(file.handle, (&iovec)[0..1], 1, offset + i, &n)) {
  18617                 .SUCCESS => {
  18618                     i += n;
  18619                     try syscall.checkCancel();
  18620                     continue;
  18621                 },
  18622                 .INTR => {
  18623                     try syscall.checkCancel();
  18624                     continue;
  18625                 },
  18626                 .DQUOT => return syscall.fail(error.DiskQuota),
  18627                 .FBIG => return syscall.fail(error.FileTooBig),
  18628                 .IO => return syscall.fail(error.InputOutput),
  18629                 .NOSPC => return syscall.fail(error.NoSpaceLeft),
  18630                 .PERM => return syscall.fail(error.PermissionDenied),
  18631                 .PIPE => return syscall.fail(error.BrokenPipe),
  18632                 .NOTCAPABLE => return syscall.fail(error.AccessDenied),
  18633                 .NXIO => return syscall.fail(error.Unseekable),
  18634                 .SPIPE => return syscall.fail(error.Unseekable),
  18635                 .OVERFLOW => return syscall.fail(error.Unseekable),
  18636                 .INVAL => |err| return syscall.errnoBug(err),
  18637                 .FAULT => |err| return syscall.errnoBug(err),
  18638                 .AGAIN => |err| return syscall.errnoBug(err),
  18639                 .BADF => |err| return syscall.errnoBug(err), // use after free
  18640                 .DESTADDRREQ => |err| return syscall.errnoBug(err), // not a socket
  18641                 else => |err| return syscall.unexpectedErrno(err),
  18642             }
  18643         }
  18644     } else {
  18645         var i: usize = 0;
  18646         const syscall: Syscall = try .start();
  18647         while (true) {
  18648             const buf = memory[i..];
  18649             if (buf.len == 0) {
  18650                 syscall.finish();
  18651                 break;
  18652             }
  18653             const rc = pwrite_sym(file.handle, buf.ptr, buf.len, @intCast(offset + i));
  18654             switch (posix.errno(rc)) {
  18655                 .SUCCESS => {
  18656                     const n: usize = @bitCast(rc);
  18657                     i += n;
  18658                     try syscall.checkCancel();
  18659                     continue;
  18660                 },
  18661                 .INTR => {
  18662                     try syscall.checkCancel();
  18663                     continue;
  18664                 },
  18665                 .INVAL => |err| return syscall.errnoBug(err),
  18666                 .FAULT => |err| return syscall.errnoBug(err),
  18667                 .DESTADDRREQ => |err| return syscall.errnoBug(err), // not a socket
  18668                 .CONNRESET => |err| return syscall.errnoBug(err), // not a socket
  18669                 .BADF => return syscall.fail(error.NotOpenForWriting),
  18670                 .AGAIN => return syscall.fail(error.WouldBlock),
  18671                 .DQUOT => return syscall.fail(error.DiskQuota),
  18672                 .FBIG => return syscall.fail(error.FileTooBig),
  18673                 .IO => return syscall.fail(error.InputOutput),
  18674                 .NOSPC => return syscall.fail(error.NoSpaceLeft),
  18675                 .PERM => return syscall.fail(error.PermissionDenied),
  18676                 .PIPE => return syscall.fail(error.BrokenPipe),
  18677                 .BUSY => return syscall.fail(error.DeviceBusy),
  18678                 .TXTBSY => return syscall.fail(error.FileBusy),
  18679                 .NXIO => return syscall.fail(error.Unseekable),
  18680                 .SPIPE => return syscall.fail(error.Unseekable),
  18681                 .OVERFLOW => return syscall.fail(error.Unseekable),
  18682                 else => |err| return syscall.unexpectedErrno(err),
  18683             }
  18684         }
  18685     }
  18686 }
  18687 
  18688 fn deviceIoControl(o: *const Io.Operation.DeviceIoControl) Io.Cancelable!Io.Operation.DeviceIoControl.Result {
  18689     if (is_windows) {
  18690         const NtControlFile = switch (o.code.DeviceType) {
  18691             .FILE_SYSTEM, .NAMED_PIPE => &windows.ntdll.NtFsControlFile,
  18692             else => &windows.ntdll.NtDeviceIoControlFile,
  18693         };
  18694         var iosb: windows.IO_STATUS_BLOCK = undefined;
  18695         if (o.file.flags.nonblocking) {
  18696             var done: bool = false;
  18697             switch (NtControlFile(
  18698                 o.file.handle,
  18699                 null, // event
  18700                 flagApc,
  18701                 &done, // APC context
  18702                 &iosb,
  18703                 o.code,
  18704                 if (o.in.len > 0) o.in.ptr else null,
  18705                 @intCast(o.in.len),
  18706                 if (o.out.len > 0) o.out.ptr else null,
  18707                 @intCast(o.out.len),
  18708             )) {
  18709                 // We must wait for the APC routine.
  18710                 .PENDING, .SUCCESS => while (!done) {
  18711                     // Once we get here we must not return from the function until the
  18712                     // operation completes, thereby releasing reference to io_status_block.
  18713                     const alertable_syscall = AlertableSyscall.start() catch |err| switch (err) {
  18714                         error.Canceled => |e| {
  18715                             var cancel_iosb: windows.IO_STATUS_BLOCK = undefined;
  18716                             _ = windows.ntdll.NtCancelIoFileEx(o.file.handle, &iosb, &cancel_iosb);
  18717                             while (!done) waitForApcOrAlert();
  18718                             return e;
  18719                         },
  18720                     };
  18721                     waitForApcOrAlert();
  18722                     alertable_syscall.finish();
  18723                 },
  18724                 else => |status| iosb.u.Status = status,
  18725             }
  18726         } else {
  18727             const syscall: Syscall = try .start();
  18728             while (true) switch (NtControlFile(
  18729                 o.file.handle,
  18730                 null, // event
  18731                 null, // APC routine
  18732                 null, // APC context
  18733                 &iosb,
  18734                 o.code,
  18735                 if (o.in.len > 0) o.in.ptr else null,
  18736                 @intCast(o.in.len),
  18737                 if (o.out.len > 0) o.out.ptr else null,
  18738                 @intCast(o.out.len),
  18739             )) {
  18740                 .PENDING => unreachable, // unrecoverable: wrong asynchronous flag
  18741                 .CANCELLED => {
  18742                     try syscall.checkCancel();
  18743                     continue;
  18744                 },
  18745                 else => |status| {
  18746                     syscall.finish();
  18747                     iosb.u.Status = status;
  18748                     break;
  18749                 },
  18750             };
  18751         }
  18752         return iosb;
  18753     } else {
  18754         const syscall: Syscall = try .start();
  18755         while (true) {
  18756             const rc = posix.system.ioctl(o.file.handle, @bitCast(o.code), @intFromPtr(o.arg));
  18757             switch (posix.errno(rc)) {
  18758                 .SUCCESS => {
  18759                     syscall.finish();
  18760                     if (@TypeOf(rc) == usize) return @bitCast(@as(u32, @truncate(rc)));
  18761                     return rc;
  18762                 },
  18763                 .INTR => {
  18764                     try syscall.checkCancel();
  18765                     continue;
  18766                 },
  18767                 else => |err| {
  18768                     syscall.finish();
  18769                     return -@as(i32, @intFromEnum(err));
  18770                 },
  18771             }
  18772         }
  18773     }
  18774 }
  18775 
  18776 const WaitGroup = struct {
  18777     state: std.atomic.Value(usize),
  18778     event: Io.Event,
  18779 
  18780     const init: WaitGroup = .{ .state = .{ .raw = 0 }, .event = .unset };
  18781 
  18782     const is_waiting: usize = 1 << 0;
  18783     const one_pending: usize = 1 << 1;
  18784 
  18785     fn start(wg: *WaitGroup) void {
  18786         const prev_state = wg.state.fetchAdd(one_pending, .monotonic);
  18787         assert((prev_state / one_pending) < (std.math.maxInt(usize) / one_pending));
  18788     }
  18789 
  18790     fn value(wg: *WaitGroup) usize {
  18791         return wg.state.load(.monotonic) / one_pending;
  18792     }
  18793 
  18794     fn wait(wg: *WaitGroup) void {
  18795         const prev_state = wg.state.fetchAdd(is_waiting, .acquire);
  18796         assert(prev_state & is_waiting == 0);
  18797         if ((prev_state / one_pending) > 0) eventWait(&wg.event);
  18798     }
  18799 
  18800     fn finish(wg: *WaitGroup) void {
  18801         const state = wg.state.fetchSub(one_pending, .acq_rel);
  18802         assert((state / one_pending) > 0);
  18803 
  18804         if (state == (one_pending | is_waiting)) {
  18805             eventSet(&wg.event);
  18806         }
  18807     }
  18808 };
  18809 
  18810 /// Same as `Io.Event.wait` but avoids the VTable.
  18811 fn eventWait(event: *Io.Event) void {
  18812     if (@cmpxchgStrong(Io.Event, event, .unset, .waiting, .acquire, .acquire)) |prev| switch (prev) {
  18813         .unset => unreachable,
  18814         .waiting => {},
  18815         .is_set => return,
  18816     };
  18817     while (true) {
  18818         Thread.futexWaitUncancelable(@ptrCast(event), @intFromEnum(Io.Event.waiting), null);
  18819         switch (@atomicLoad(Io.Event, event, .acquire)) {
  18820             .unset => unreachable, // `reset` called before pending `wait` returned
  18821             .waiting => continue,
  18822             .is_set => return,
  18823         }
  18824     }
  18825 }
  18826 
  18827 /// Same as `Io.Event.set` but avoids the VTable.
  18828 fn eventSet(event: *Io.Event) void {
  18829     switch (@atomicRmw(Io.Event, event, .Xchg, .is_set, .release)) {
  18830         .unset, .is_set => {},
  18831         .waiting => Thread.futexWake(@ptrCast(event), std.math.maxInt(u32)),
  18832     }
  18833 }
  18834 
  18835 /// Same as `Io.Condition.broadcast` but avoids the VTable.
  18836 fn condBroadcast(cond: *Io.Condition) void {
  18837     var prev_state = cond.state.load(.monotonic);
  18838     while (prev_state.waiters > prev_state.signals) {
  18839         @branchHint(.unlikely);
  18840         prev_state = cond.state.cmpxchgWeak(prev_state, .{
  18841             .waiters = prev_state.waiters,
  18842             .signals = prev_state.waiters,
  18843         }, .release, .monotonic) orelse {
  18844             // Update the epoch to tell the waiting threads that there are new signals for them.
  18845             // Note that a waiting thread could miss a take if *exactly* (1<<32)-1 wakes happen
  18846             // between it observing the epoch and sleeping on it, but this is extraordinarily
  18847             // unlikely due to the precise number of calls required.
  18848             _ = cond.epoch.fetchAdd(1, .release); // `.release` to ensure ordered after `state` update
  18849             Thread.futexWake(&cond.epoch.raw, prev_state.waiters - prev_state.signals);
  18850             return;
  18851         };
  18852     }
  18853 }
  18854 
  18855 /// Same as `Io.Condition.signal` but avoids the VTable.
  18856 fn condSignal(cond: *Io.Condition) void {
  18857     var prev_state = cond.state.load(.monotonic);
  18858     while (prev_state.waiters > prev_state.signals) {
  18859         @branchHint(.unlikely);
  18860         prev_state = cond.state.cmpxchgWeak(prev_state, .{
  18861             .waiters = prev_state.waiters,
  18862             .signals = prev_state.signals + 1,
  18863         }, .release, .monotonic) orelse {
  18864             // Update the epoch to tell the waiting threads that there are new signals for them.
  18865             // Note that a waiting thread could miss a take if *exactly* (1<<32)-1 wakes happen
  18866             // between it observing the epoch and sleeping on it, but this is extraordinarily
  18867             // unlikely due to the precise number of calls required.
  18868             _ = cond.epoch.fetchAdd(1, .release); // `.release` to ensure ordered after `state` update
  18869             Thread.futexWake(&cond.epoch.raw, 1);
  18870             return;
  18871         };
  18872     }
  18873 }
  18874 
  18875 /// Same as `Io.Condition.waitUncancelable` but avoids the VTable.
  18876 fn condWait(cond: *Io.Condition, mutex: *Io.Mutex) void {
  18877     var epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before state load
  18878 
  18879     {
  18880         const prev_state = cond.state.fetchAdd(.{ .waiters = 1, .signals = 0 }, .monotonic);
  18881         assert(prev_state.waiters < std.math.maxInt(u16)); // overflow caused by too many waiters
  18882     }
  18883 
  18884     mutexUnlock(mutex);
  18885     defer mutexLock(mutex);
  18886 
  18887     while (true) {
  18888         Thread.futexWaitUncancelable(&cond.epoch.raw, epoch, null);
  18889 
  18890         epoch = cond.epoch.load(.acquire); // `.acquire` to ensure ordered before `state` laod
  18891 
  18892         var prev_state = cond.state.load(.monotonic);
  18893         while (prev_state.signals > 0) {
  18894             prev_state = cond.state.cmpxchgWeak(prev_state, .{
  18895                 .waiters = prev_state.waiters - 1,
  18896                 .signals = prev_state.signals - 1,
  18897             }, .acquire, .monotonic) orelse {
  18898                 // We successfully consumed a signal.
  18899                 return;
  18900             };
  18901         }
  18902     }
  18903 }
  18904 
  18905 /// Same as `Io.Mutex.lockUncancelable` but avoids the VTable.
  18906 pub fn mutexLock(m: *Io.Mutex) void {
  18907     const initial_state = m.state.cmpxchgWeak(
  18908         .unlocked,
  18909         .locked_once,
  18910         .acquire,
  18911         .monotonic,
  18912     ) orelse {
  18913         @branchHint(.likely);
  18914         return;
  18915     };
  18916     if (initial_state == .contended) {
  18917         Thread.futexWaitUncancelable(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null);
  18918     }
  18919     while (m.state.swap(.contended, .acquire) != .unlocked) {
  18920         Thread.futexWaitUncancelable(@ptrCast(&m.state.raw), @intFromEnum(Io.Mutex.State.contended), null);
  18921     }
  18922 }
  18923 
  18924 /// Same as `Io.Mutex.unlock` but avoids the VTable.
  18925 pub fn mutexUnlock(m: *Io.Mutex) void {
  18926     switch (m.state.swap(.unlocked, .release)) {
  18927         .unlocked => unreachable,
  18928         .locked_once => {},
  18929         .contended => {
  18930             @branchHint(.unlikely);
  18931             Thread.futexWake(@ptrCast(&m.state.raw), 1);
  18932         },
  18933     }
  18934 }
  18935 
  18936 const OpenError = error{
  18937     IsDir,
  18938     NotDir,
  18939     FileNotFound,
  18940     NoDevice,
  18941     AccessDenied,
  18942     PipeBusy,
  18943     PathAlreadyExists,
  18944     WouldBlock,
  18945     NetworkNotFound,
  18946     AntivirusInterference,
  18947     FileBusy,
  18948 } || Dir.PathNameError || Io.Cancelable || Io.UnexpectedError;
  18949 
  18950 const OpenFileOptions = struct {
  18951     access_mask: windows.ACCESS_MASK,
  18952     dir: ?windows.HANDLE = null,
  18953     sa: ?*const windows.SECURITY_ATTRIBUTES = null,
  18954     share_access: windows.FILE.SHARE = .VALID_FLAGS,
  18955     creation: windows.FILE.CREATE_DISPOSITION,
  18956     filter: Filter = .non_directory_only,
  18957     /// If false, tries to open path as a reparse point without dereferencing it.
  18958     /// Defaults to true.
  18959     follow_symlinks: bool = true,
  18960 
  18961     pub const Filter = enum {
  18962         /// Causes `OpenFile` to return `error.IsDir` if the opened handle would be a directory.
  18963         non_directory_only,
  18964         /// Causes `OpenFile` to return `error.NotDir` if the opened handle is not a directory.
  18965         dir_only,
  18966         /// `OpenFile` does not discriminate between opening files and directories.
  18967         any,
  18968     };
  18969 };
  18970 
  18971 /// TODO: inline this logic everywhere and delete this function
  18972 fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!windows.HANDLE {
  18973     if (std.mem.eql(u16, sub_path_w, &.{'.'}) and options.filter == .non_directory_only) {
  18974         return error.IsDir;
  18975     }
  18976     if (std.mem.eql(u16, sub_path_w, &.{ '.', '.' }) and options.filter == .non_directory_only) {
  18977         return error.IsDir;
  18978     }
  18979 
  18980     var result: windows.HANDLE = undefined;
  18981 
  18982     const attr: windows.OBJECT.ATTRIBUTES = .{
  18983         .RootDirectory = if (Dir.path.isAbsoluteWindowsWtf16(sub_path_w)) null else options.dir,
  18984         .Attributes = .{ .INHERIT = if (options.sa) |sa| sa.bInheritHandle != windows.FALSE else false },
  18985         .ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path_w)),
  18986         .SecurityDescriptor = if (options.sa) |ptr| ptr.lpSecurityDescriptor else null,
  18987     };
  18988 
  18989     var iosb: windows.IO_STATUS_BLOCK = undefined;
  18990     var attempt: u5 = 0;
  18991     var syscall: Syscall = try .start();
  18992     while (true) {
  18993         switch (windows.ntdll.NtCreateFile(
  18994             &result,
  18995             options.access_mask,
  18996             &attr,
  18997             &iosb,
  18998             null,
  18999             .{ .NORMAL = true },
  19000             options.share_access,
  19001             options.creation,
  19002             .{
  19003                 .DIRECTORY_FILE = options.filter == .dir_only,
  19004                 .NON_DIRECTORY_FILE = options.filter == .non_directory_only,
  19005                 .IO = if (options.follow_symlinks) .SYNCHRONOUS_NONALERT else .ASYNCHRONOUS,
  19006                 .OPEN_REPARSE_POINT = !options.follow_symlinks,
  19007             },
  19008             null,
  19009             0,
  19010         )) {
  19011             .SUCCESS => {
  19012                 syscall.finish();
  19013                 return result;
  19014             },
  19015             .CANCELLED => {
  19016                 try syscall.checkCancel();
  19017                 continue;
  19018             },
  19019             .SHARING_VIOLATION => {
  19020                 // This occurs if the file attempting to be opened is a running
  19021                 // executable. However, there's a kernel bug: the error may be
  19022                 // incorrectly returned for an indeterminate amount of time
  19023                 // after an executable file is closed. Here we work around the
  19024                 // kernel bug with retry attempts.
  19025                 syscall.finish();
  19026                 if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
  19027                 try parking_sleep.sleep(.{ .duration = .{
  19028                     .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
  19029                     .clock = .awake,
  19030                 } });
  19031                 attempt += 1;
  19032                 syscall = try .start();
  19033                 continue;
  19034             },
  19035             .DELETE_PENDING => {
  19036                 // This error means that there *was* a file in this location on
  19037                 // the file system, but it was deleted. However, the OS is not
  19038                 // finished with the deletion operation, and so this CreateFile
  19039                 // call has failed. There is not really a sane way to handle
  19040                 // this other than retrying the creation after the OS finishes
  19041                 // the deletion.
  19042                 syscall.finish();
  19043                 if (max_windows_kernel_bug_retries - attempt == 0) return error.FileBusy;
  19044                 try parking_sleep.sleep(.{ .duration = .{
  19045                     .raw = .fromMilliseconds((@as(u32, 1) << attempt) >> 1),
  19046                     .clock = .awake,
  19047                 } });
  19048                 attempt += 1;
  19049                 syscall = try .start();
  19050                 continue;
  19051             },
  19052             .OBJECT_NAME_INVALID => return syscall.fail(error.BadPathName),
  19053             .OBJECT_NAME_NOT_FOUND => return syscall.fail(error.FileNotFound),
  19054             .OBJECT_PATH_NOT_FOUND => return syscall.fail(error.FileNotFound),
  19055             .BAD_NETWORK_PATH => return syscall.fail(error.NetworkNotFound), // \\server was not found
  19056             .BAD_NETWORK_NAME => return syscall.fail(error.NetworkNotFound), // \\server was found but \\server\share wasn't
  19057             .NO_MEDIA_IN_DEVICE => return syscall.fail(error.NoDevice),
  19058             .ACCESS_DENIED => return syscall.fail(error.AccessDenied),
  19059             .PIPE_BUSY => return syscall.fail(error.PipeBusy),
  19060             .PIPE_NOT_AVAILABLE => return syscall.fail(error.NoDevice),
  19061             .OBJECT_NAME_COLLISION => return syscall.fail(error.PathAlreadyExists),
  19062             .FILE_IS_A_DIRECTORY => return syscall.fail(error.IsDir),
  19063             .NOT_A_DIRECTORY => return syscall.fail(error.NotDir),
  19064             .USER_MAPPED_FILE => return syscall.fail(error.AccessDenied),
  19065             .VIRUS_INFECTED, .VIRUS_DELETED => return syscall.fail(error.AntivirusInterference),
  19066             .INVALID_PARAMETER => |status| return syscall.ntstatusBug(status),
  19067             .OBJECT_PATH_SYNTAX_BAD => |status| return syscall.ntstatusBug(status),
  19068             .INVALID_HANDLE => |status| return syscall.ntstatusBug(status),
  19069             else => |status| return syscall.unexpectedNtstatus(status),
  19070         }
  19071     }
  19072 }
  19073 
  19074 pub fn closeFd(fd: posix.fd_t) void {
  19075     if (native_os == .wasi and !builtin.link_libc) {
  19076         switch (std.os.wasi.fd_close(fd)) {
  19077             .SUCCESS, .INTR => {},
  19078             .BADF => recoverableOsBugDetected(), // use after free
  19079             else => recoverableOsBugDetected(), // unexpected failure
  19080         }
  19081     } else switch (posix.errno(posix.system.close(fd))) {
  19082         .SUCCESS, .INTR => {}, // INTR still a success, see https://github.com/ziglang/zig/issues/2425
  19083         .BADF => recoverableOsBugDetected(), // use after free
  19084         else => recoverableOsBugDetected(), // unexpected failure
  19085     }
  19086 }