zig

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

blob f2bd8bda (282476B) - Raw


      1 //! This file contains thin wrappers around OS-specific APIs, with these
      2 //! specific goals in mind:
      3 //! * Convert "errno"-style error codes into Zig errors.
      4 //! * When null-terminated byte buffers are required, provide APIs which accept
      5 //!   slices as well as APIs which accept null-terminated byte buffers. Same goes
      6 //!   for UTF-16LE encoding.
      7 //! * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide
      8 //!   cross platform abstracting.
      9 //! * When there exists a corresponding libc function and linking libc, the libc
     10 //!   implementation is used. Exceptions are made for known buggy areas of libc.
     11 //!   On Linux libc can be side-stepped by using `std.os.linux` directly.
     12 //! * For Windows, this file represents the API that libc would provide for
     13 //!   Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.
     14 //! Note: The Zig standard library does not support POSIX thread cancellation, and
     15 //! in general EINTR is handled by trying again.
     16 
     17 const root = @import("root");
     18 const std = @import("std.zig");
     19 const builtin = @import("builtin");
     20 const assert = std.debug.assert;
     21 const math = std.math;
     22 const mem = std.mem;
     23 const elf = std.elf;
     24 const fs = std.fs;
     25 const dl = @import("dynamic_library.zig");
     26 const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;
     27 const is_windows = builtin.os.tag == .windows;
     28 const Allocator = std.mem.Allocator;
     29 const Preopen = std.fs.wasi.Preopen;
     30 const PreopenList = std.fs.wasi.PreopenList;
     31 
     32 pub const darwin = @import("os/darwin.zig");
     33 pub const dragonfly = std.c;
     34 pub const freebsd = std.c;
     35 pub const haiku = std.c;
     36 pub const netbsd = std.c;
     37 pub const openbsd = std.c;
     38 pub const solaris = std.c;
     39 pub const linux = @import("os/linux.zig");
     40 pub const plan9 = @import("os/plan9.zig");
     41 pub const uefi = @import("os/uefi.zig");
     42 pub const wasi = @import("os/wasi.zig");
     43 pub const windows = @import("os/windows.zig");
     44 pub const posix_spawn = @import("os/posix_spawn.zig");
     45 pub const ptrace = @import("os/ptrace.zig");
     46 
     47 comptime {
     48     assert(@import("std") == std); // std lib tests require --zig-lib-dir
     49 }
     50 
     51 test {
     52     _ = darwin;
     53     _ = linux;
     54     if (builtin.os.tag == .uefi) {
     55         _ = uefi;
     56     }
     57     _ = wasi;
     58     _ = windows;
     59     _ = posix_spawn;
     60 
     61     _ = @import("os/test.zig");
     62 }
     63 
     64 /// Applications can override the `system` API layer in their root source file.
     65 /// Otherwise, when linking libc, this is the C API.
     66 /// When not linking libc, it is the OS-specific system interface.
     67 pub const system = if (@hasDecl(root, "os") and root.os != @This())
     68     root.os.system
     69 else if (builtin.link_libc or is_windows)
     70     std.c
     71 else switch (builtin.os.tag) {
     72     .linux => linux,
     73     .wasi => wasi,
     74     .uefi => uefi,
     75     else => struct {},
     76 };
     77 
     78 pub const AF = system.AF;
     79 pub const AF_SUN = system.AF_SUN;
     80 pub const ARCH = system.ARCH;
     81 pub const AT = system.AT;
     82 pub const AT_SUN = system.AT_SUN;
     83 pub const CLOCK = system.CLOCK;
     84 pub const CPU_COUNT = system.CPU_COUNT;
     85 pub const CTL = system.CTL;
     86 pub const DT = system.DT;
     87 pub const E = system.E;
     88 pub const Elf_Symndx = system.Elf_Symndx;
     89 pub const F = system.F;
     90 pub const FD_CLOEXEC = system.FD_CLOEXEC;
     91 pub const Flock = system.Flock;
     92 pub const HOST_NAME_MAX = system.HOST_NAME_MAX;
     93 pub const IFNAMESIZE = system.IFNAMESIZE;
     94 pub const IOV_MAX = system.IOV_MAX;
     95 pub const IPPROTO = system.IPPROTO;
     96 pub const KERN = system.KERN;
     97 pub const Kevent = system.Kevent;
     98 pub const LOCK = system.LOCK;
     99 pub const MADV = system.MADV;
    100 pub const MAP = system.MAP;
    101 pub const MSF = system.MSF;
    102 pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;
    103 pub const MFD = system.MFD;
    104 pub const MMAP2_UNIT = system.MMAP2_UNIT;
    105 pub const MSG = system.MSG;
    106 pub const NAME_MAX = system.NAME_MAX;
    107 pub const O = switch (builtin.os.tag) {
    108     // We want to expose the POSIX-like OFLAGS, so we use std.c.wasi.O instead
    109     // of std.os.wasi.O, which is for non-POSIX-like `wasi.path_open`, etc.
    110     .wasi => std.c.O,
    111     else => system.O,
    112 };
    113 pub const PATH_MAX = system.PATH_MAX;
    114 pub const POLL = system.POLL;
    115 pub const POSIX_FADV = system.POSIX_FADV;
    116 pub const PR = system.PR;
    117 pub const PROT = system.PROT;
    118 pub const REG = system.REG;
    119 pub const RIGHT = system.RIGHT;
    120 pub const RLIM = system.RLIM;
    121 pub const RR = system.RR;
    122 pub const S = system.S;
    123 pub const SA = system.SA;
    124 pub const SC = system.SC;
    125 pub const _SC = system._SC;
    126 pub const SEEK = system.SEEK;
    127 pub const SHUT = system.SHUT;
    128 pub const SIG = system.SIG;
    129 pub const SIOCGIFINDEX = system.SIOCGIFINDEX;
    130 pub const SO = system.SO;
    131 pub const SOCK = system.SOCK;
    132 pub const SOL = system.SOL;
    133 pub const STDERR_FILENO = system.STDERR_FILENO;
    134 pub const STDIN_FILENO = system.STDIN_FILENO;
    135 pub const STDOUT_FILENO = system.STDOUT_FILENO;
    136 pub const SYS = system.SYS;
    137 pub const Sigaction = system.Sigaction;
    138 pub const Stat = system.Stat;
    139 pub const TCSA = system.TCSA;
    140 pub const TCP = system.TCP;
    141 pub const VDSO = system.VDSO;
    142 pub const W = system.W;
    143 pub const addrinfo = system.addrinfo;
    144 pub const blkcnt_t = system.blkcnt_t;
    145 pub const blksize_t = system.blksize_t;
    146 pub const clock_t = system.clock_t;
    147 pub const cpu_set_t = system.cpu_set_t;
    148 pub const dev_t = system.dev_t;
    149 pub const dl_phdr_info = system.dl_phdr_info;
    150 pub const empty_sigset = system.empty_sigset;
    151 pub const fd_t = system.fd_t;
    152 pub const fdflags_t = system.fdflags_t;
    153 pub const fdstat_t = system.fdstat_t;
    154 pub const gid_t = system.gid_t;
    155 pub const ifreq = system.ifreq;
    156 pub const ino_t = system.ino_t;
    157 pub const lookupflags_t = system.lookupflags_t;
    158 pub const mcontext_t = system.mcontext_t;
    159 pub const mode_t = system.mode_t;
    160 pub const msghdr = system.msghdr;
    161 pub const msghdr_const = system.msghdr_const;
    162 pub const nfds_t = system.nfds_t;
    163 pub const nlink_t = system.nlink_t;
    164 pub const off_t = system.off_t;
    165 pub const oflags_t = system.oflags_t;
    166 pub const pid_t = system.pid_t;
    167 pub const pollfd = system.pollfd;
    168 pub const port_t = system.port_t;
    169 pub const port_event = system.port_event;
    170 pub const port_notify = system.port_notify;
    171 pub const file_obj = system.file_obj;
    172 pub const rights_t = system.rights_t;
    173 pub const rlim_t = system.rlim_t;
    174 pub const rlimit = system.rlimit;
    175 pub const rlimit_resource = system.rlimit_resource;
    176 pub const rusage = system.rusage;
    177 pub const sa_family_t = system.sa_family_t;
    178 pub const siginfo_t = system.siginfo_t;
    179 pub const sigset_t = system.sigset_t;
    180 pub const sockaddr = system.sockaddr;
    181 pub const socklen_t = system.socklen_t;
    182 pub const stack_t = system.stack_t;
    183 pub const tcflag_t = system.tcflag_t;
    184 pub const termios = system.termios;
    185 pub const time_t = system.time_t;
    186 pub const timespec = system.timespec;
    187 pub const timestamp_t = system.timestamp_t;
    188 pub const timeval = system.timeval;
    189 pub const timezone = system.timezone;
    190 pub const ucontext_t = system.ucontext_t;
    191 pub const uid_t = system.uid_t;
    192 pub const user_desc = system.user_desc;
    193 pub const utsname = system.utsname;
    194 
    195 pub const F_OK = system.F_OK;
    196 pub const R_OK = system.R_OK;
    197 pub const W_OK = system.W_OK;
    198 pub const X_OK = system.X_OK;
    199 
    200 pub const iovec = extern struct {
    201     iov_base: [*]u8,
    202     iov_len: usize,
    203 };
    204 
    205 pub const iovec_const = extern struct {
    206     iov_base: [*]const u8,
    207     iov_len: usize,
    208 };
    209 
    210 pub const LOG = struct {
    211     /// system is unusable
    212     pub const EMERG = 0;
    213     /// action must be taken immediately
    214     pub const ALERT = 1;
    215     /// critical conditions
    216     pub const CRIT = 2;
    217     /// error conditions
    218     pub const ERR = 3;
    219     /// warning conditions
    220     pub const WARNING = 4;
    221     /// normal but significant condition
    222     pub const NOTICE = 5;
    223     /// informational
    224     pub const INFO = 6;
    225     /// debug-level messages
    226     pub const DEBUG = 7;
    227 };
    228 
    229 /// An fd-relative file path
    230 ///
    231 /// This is currently only used for WASI-specific functionality, but the concept
    232 /// is the same as the dirfd/pathname pairs in the `*at(...)` POSIX functions.
    233 pub const RelativePathWasi = struct {
    234     /// Handle to directory
    235     dir_fd: fd_t,
    236     /// Path to resource within `dir_fd`.
    237     relative_path: []const u8,
    238 };
    239 
    240 pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t;
    241 
    242 /// See also `getenv`. Populated by startup code before main().
    243 /// TODO this is a footgun because the value will be undefined when using `zig build-lib`.
    244 /// https://github.com/ziglang/zig/issues/4524
    245 pub var environ: [][*:0]u8 = undefined;
    246 
    247 /// Populated by startup code before main().
    248 /// Not available on WASI or Windows without libc. See `std.process.argsAlloc`
    249 /// or `std.process.argsWithAllocator` for a cross-platform alternative.
    250 pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin.os.tag) {
    251     .windows => @compileError("argv isn't supported on Windows: use std.process.argsAlloc instead"),
    252     .wasi => @compileError("argv isn't supported on WASI: use std.process.argsAlloc instead"),
    253     else => undefined,
    254 };
    255 
    256 /// To obtain errno, call this function with the return value of the
    257 /// system function call. For some systems this will obtain the value directly
    258 /// from the return code; for others it will use a thread-local errno variable.
    259 /// Therefore, this function only returns a well-defined value when it is called
    260 /// directly after the system function call which one wants to learn the errno
    261 /// value of.
    262 pub const errno = system.getErrno;
    263 
    264 /// Closes the file descriptor.
    265 /// This function is not capable of returning any indication of failure. An
    266 /// application which wants to ensure writes have succeeded before closing
    267 /// must call `fsync` before `close`.
    268 /// Note: The Zig standard library does not support POSIX thread cancellation.
    269 pub fn close(fd: fd_t) void {
    270     if (builtin.os.tag == .windows) {
    271         return windows.CloseHandle(fd);
    272     }
    273     if (builtin.os.tag == .wasi and !builtin.link_libc) {
    274         _ = wasi.fd_close(fd);
    275         return;
    276     }
    277     if (comptime builtin.target.isDarwin()) {
    278         // This avoids the EINTR problem.
    279         switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) {
    280             .BADF => unreachable, // Always a race condition.
    281             else => return,
    282         }
    283     }
    284     switch (errno(system.close(fd))) {
    285         .BADF => unreachable, // Always a race condition.
    286         .INTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
    287         else => return,
    288     }
    289 }
    290 
    291 pub const FChmodError = error{
    292     AccessDenied,
    293     InputOutput,
    294     SymLinkLoop,
    295     FileNotFound,
    296     SystemResources,
    297     ReadOnlyFileSystem,
    298 } || UnexpectedError;
    299 
    300 /// Changes the mode of the file referred to by the file descriptor.
    301 /// The process must have the correct privileges in order to do this
    302 /// successfully, or must have the effective user ID matching the owner
    303 /// of the file.
    304 pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
    305     if (!std.fs.has_executable_bit) @compileError("fchmod unsupported by target OS");
    306 
    307     while (true) {
    308         const res = system.fchmod(fd, mode);
    309 
    310         switch (system.getErrno(res)) {
    311             .SUCCESS => return,
    312             .INTR => continue,
    313             .BADF => unreachable,
    314             .FAULT => unreachable,
    315             .INVAL => unreachable,
    316             .ACCES => return error.AccessDenied,
    317             .IO => return error.InputOutput,
    318             .LOOP => return error.SymLinkLoop,
    319             .NOENT => return error.FileNotFound,
    320             .NOMEM => return error.SystemResources,
    321             .NOTDIR => return error.FileNotFound,
    322             .PERM => return error.AccessDenied,
    323             .ROFS => return error.ReadOnlyFileSystem,
    324             else => |err| return unexpectedErrno(err),
    325         }
    326     }
    327 }
    328 
    329 const FChmodAtError = FChmodError || error{
    330     NameTooLong,
    331 };
    332 
    333 pub fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
    334     if (!std.fs.has_executable_bit) @compileError("fchmodat unsupported by target OS");
    335 
    336     const path_c = try toPosixPath(path);
    337 
    338     while (true) {
    339         const res = system.fchmodat(dirfd, &path_c, mode, flags);
    340 
    341         switch (system.getErrno(res)) {
    342             .SUCCESS => return,
    343             .INTR => continue,
    344             .BADF => unreachable,
    345             .FAULT => unreachable,
    346             .INVAL => unreachable,
    347             .ACCES => return error.AccessDenied,
    348             .IO => return error.InputOutput,
    349             .LOOP => return error.SymLinkLoop,
    350             .NOENT => return error.FileNotFound,
    351             .NOMEM => return error.SystemResources,
    352             .NOTDIR => return error.FileNotFound,
    353             .PERM => return error.AccessDenied,
    354             .ROFS => return error.ReadOnlyFileSystem,
    355             else => |err| return unexpectedErrno(err),
    356         }
    357     }
    358 }
    359 
    360 pub const FChownError = error{
    361     AccessDenied,
    362     InputOutput,
    363     SymLinkLoop,
    364     FileNotFound,
    365     SystemResources,
    366     ReadOnlyFileSystem,
    367 } || UnexpectedError;
    368 
    369 /// Changes the owner and group of the file referred to by the file descriptor.
    370 /// The process must have the correct privileges in order to do this
    371 /// successfully. The group may be changed by the owner of the directory to
    372 /// any group of which the owner is a member. If the owner or group is
    373 /// specified as `null`, the ID is not changed.
    374 pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {
    375     if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
    376         @compileError("Unsupported OS");
    377 
    378     while (true) {
    379         const res = system.fchown(fd, owner orelse @as(u32, 0) -% 1, group orelse @as(u32, 0) -% 1);
    380 
    381         switch (system.getErrno(res)) {
    382             .SUCCESS => return,
    383             .INTR => continue,
    384             .BADF => unreachable, // Can be reached if the fd refers to a non-iterable directory.
    385 
    386             .FAULT => unreachable,
    387             .INVAL => unreachable,
    388             .ACCES => return error.AccessDenied,
    389             .IO => return error.InputOutput,
    390             .LOOP => return error.SymLinkLoop,
    391             .NOENT => return error.FileNotFound,
    392             .NOMEM => return error.SystemResources,
    393             .NOTDIR => return error.FileNotFound,
    394             .PERM => return error.AccessDenied,
    395             .ROFS => return error.ReadOnlyFileSystem,
    396             else => |err| return unexpectedErrno(err),
    397         }
    398     }
    399 }
    400 
    401 pub const RebootError = error{
    402     PermissionDenied,
    403 } || UnexpectedError;
    404 
    405 pub const RebootCommand = switch (builtin.os.tag) {
    406     .linux => union(linux.LINUX_REBOOT.CMD) {
    407         RESTART: void,
    408         HALT: void,
    409         CAD_ON: void,
    410         CAD_OFF: void,
    411         POWER_OFF: void,
    412         RESTART2: [*:0]const u8,
    413         SW_SUSPEND: void,
    414         KEXEC: void,
    415     },
    416     else => @compileError("Unsupported OS"),
    417 };
    418 
    419 pub fn reboot(cmd: RebootCommand) RebootError!void {
    420     switch (builtin.os.tag) {
    421         .linux => {
    422             switch (system.getErrno(linux.reboot(
    423                 .MAGIC1,
    424                 .MAGIC2,
    425                 @as(linux.LINUX_REBOOT.CMD, cmd),
    426                 switch (cmd) {
    427                     .RESTART2 => |s| s,
    428                     else => null,
    429                 },
    430             ))) {
    431                 .SUCCESS => {},
    432                 .PERM => return error.PermissionDenied,
    433                 else => |err| return std.os.unexpectedErrno(err),
    434             }
    435             switch (cmd) {
    436                 .CAD_OFF => {},
    437                 .CAD_ON => {},
    438                 .SW_SUSPEND => {},
    439 
    440                 .HALT => unreachable,
    441                 .KEXEC => unreachable,
    442                 .POWER_OFF => unreachable,
    443                 .RESTART => unreachable,
    444                 .RESTART2 => unreachable,
    445             }
    446         },
    447         else => @compileError("Unsupported OS"),
    448     }
    449 }
    450 
    451 pub const GetRandomError = OpenError;
    452 
    453 /// Obtain a series of random bytes. These bytes can be used to seed user-space
    454 /// random number generators or for cryptographic purposes.
    455 /// When linking against libc, this calls the
    456 /// appropriate OS-specific library call. Otherwise it uses the zig standard
    457 /// library implementation.
    458 pub fn getrandom(buffer: []u8) GetRandomError!void {
    459     if (builtin.os.tag == .windows) {
    460         return windows.RtlGenRandom(buffer);
    461     }
    462     if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) {
    463         var buf = buffer;
    464         const use_c = builtin.os.tag != .linux or
    465             std.c.versionCheck(std.builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok;
    466 
    467         while (buf.len != 0) {
    468             const res = if (use_c) blk: {
    469                 const rc = std.c.getrandom(buf.ptr, buf.len, 0);
    470                 break :blk .{
    471                     .num_read = @bitCast(usize, rc),
    472                     .err = std.c.getErrno(rc),
    473                 };
    474             } else blk: {
    475                 const rc = linux.getrandom(buf.ptr, buf.len, 0);
    476                 break :blk .{
    477                     .num_read = rc,
    478                     .err = linux.getErrno(rc),
    479                 };
    480             };
    481 
    482             switch (res.err) {
    483                 .SUCCESS => buf = buf[res.num_read..],
    484                 .INVAL => unreachable,
    485                 .FAULT => unreachable,
    486                 .INTR => continue,
    487                 .NOSYS => return getRandomBytesDevURandom(buf),
    488                 else => return unexpectedErrno(res.err),
    489             }
    490         }
    491         return;
    492     }
    493     switch (builtin.os.tag) {
    494         .netbsd, .openbsd, .macos, .ios, .tvos, .watchos => {
    495             system.arc4random_buf(buffer.ptr, buffer.len);
    496             return;
    497         },
    498         .wasi => switch (wasi.random_get(buffer.ptr, buffer.len)) {
    499             .SUCCESS => return,
    500             else => |err| return unexpectedErrno(err),
    501         },
    502         else => return getRandomBytesDevURandom(buffer),
    503     }
    504 }
    505 
    506 fn getRandomBytesDevURandom(buf: []u8) !void {
    507     const fd = try openZ("/dev/urandom", O.RDONLY | O.CLOEXEC, 0);
    508     defer close(fd);
    509 
    510     const st = try fstat(fd);
    511     if (!S.ISCHR(st.mode)) {
    512         return error.NoDevice;
    513     }
    514 
    515     const file = std.fs.File{
    516         .handle = fd,
    517         .capable_io_mode = .blocking,
    518         .intended_io_mode = .blocking,
    519     };
    520     const stream = file.reader();
    521     stream.readNoEof(buf) catch return error.Unexpected;
    522 }
    523 
    524 /// Causes abnormal process termination.
    525 /// If linking against libc, this calls the abort() libc function. Otherwise
    526 /// it raises SIGABRT followed by SIGKILL and finally lo
    527 /// Invokes the current signal handler for SIGABRT, if any.
    528 pub fn abort() noreturn {
    529     @setCold(true);
    530     // MSVCRT abort() sometimes opens a popup window which is undesirable, so
    531     // even when linking libc on Windows we use our own abort implementation.
    532     // See https://github.com/ziglang/zig/issues/2071 for more details.
    533     if (builtin.os.tag == .windows) {
    534         if (builtin.mode == .Debug) {
    535             @breakpoint();
    536         }
    537         windows.kernel32.ExitProcess(3);
    538     }
    539     if (!builtin.link_libc and builtin.os.tag == .linux) {
    540         // The Linux man page says that the libc abort() function
    541         // "first unblocks the SIGABRT signal", but this is a footgun
    542         // for user-defined signal handlers that want to restore some state in
    543         // some program sections and crash in others.
    544         // So, the user-installed SIGABRT handler is run, if present.
    545         raise(SIG.ABRT) catch {};
    546 
    547         // Disable all signal handlers.
    548         sigprocmask(SIG.BLOCK, &linux.all_mask, null);
    549 
    550         // Only one thread may proceed to the rest of abort().
    551         if (!builtin.single_threaded) {
    552             const global = struct {
    553                 var abort_entered: bool = false;
    554             };
    555             while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .SeqCst, .SeqCst)) |_| {}
    556         }
    557 
    558         // Install default handler so that the tkill below will terminate.
    559         const sigact = Sigaction{
    560             .handler = .{ .handler = SIG.DFL },
    561             .mask = empty_sigset,
    562             .flags = 0,
    563         };
    564         sigaction(SIG.ABRT, &sigact, null) catch |err| switch (err) {
    565             error.OperationNotSupported => unreachable,
    566         };
    567 
    568         _ = linux.tkill(linux.gettid(), SIG.ABRT);
    569 
    570         const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)};
    571         sigprocmask(SIG.UNBLOCK, &sigabrtmask, null);
    572 
    573         // Beyond this point should be unreachable.
    574         @intToPtr(*allowzero volatile u8, 0).* = 0;
    575         raise(SIG.KILL) catch {};
    576         exit(127); // Pid 1 might not be signalled in some containers.
    577     }
    578     switch (builtin.os.tag) {
    579         .uefi, .wasi, .cuda => @trap(),
    580         else => system.abort(),
    581     }
    582 }
    583 
    584 pub const RaiseError = UnexpectedError;
    585 
    586 pub fn raise(sig: u8) RaiseError!void {
    587     if (builtin.link_libc) {
    588         switch (errno(system.raise(sig))) {
    589             .SUCCESS => return,
    590             else => |err| return unexpectedErrno(err),
    591         }
    592     }
    593 
    594     if (builtin.os.tag == .linux) {
    595         var set: sigset_t = undefined;
    596         // block application signals
    597         sigprocmask(SIG.BLOCK, &linux.app_mask, &set);
    598 
    599         const tid = linux.gettid();
    600         const rc = linux.tkill(tid, sig);
    601 
    602         // restore signal mask
    603         sigprocmask(SIG.SETMASK, &set, null);
    604 
    605         switch (errno(rc)) {
    606             .SUCCESS => return,
    607             else => |err| return unexpectedErrno(err),
    608         }
    609     }
    610 
    611     @compileError("std.os.raise unimplemented for this target");
    612 }
    613 
    614 pub const KillError = error{PermissionDenied} || UnexpectedError;
    615 
    616 pub fn kill(pid: pid_t, sig: u8) KillError!void {
    617     switch (errno(system.kill(pid, sig))) {
    618         .SUCCESS => return,
    619         .INVAL => unreachable, // invalid signal
    620         .PERM => return error.PermissionDenied,
    621         .SRCH => unreachable, // always a race condition
    622         else => |err| return unexpectedErrno(err),
    623     }
    624 }
    625 
    626 /// Exits the program cleanly with the specified status code.
    627 pub fn exit(status: u8) noreturn {
    628     if (builtin.link_libc) {
    629         system.exit(status);
    630     }
    631     if (builtin.os.tag == .windows) {
    632         windows.kernel32.ExitProcess(status);
    633     }
    634     if (builtin.os.tag == .wasi) {
    635         wasi.proc_exit(status);
    636     }
    637     if (builtin.os.tag == .linux and !builtin.single_threaded) {
    638         linux.exit_group(status);
    639     }
    640     if (builtin.os.tag == .uefi) {
    641         // exit() is only avaliable if exitBootServices() has not been called yet.
    642         // This call to exit should not fail, so we don't care about its return value.
    643         if (uefi.system_table.boot_services) |bs| {
    644             _ = bs.exit(uefi.handle, @intToEnum(uefi.Status, status), 0, null);
    645         }
    646         // If we can't exit, reboot the system instead.
    647         uefi.system_table.runtime_services.resetSystem(uefi.tables.ResetType.ResetCold, @intToEnum(uefi.Status, status), 0, null);
    648     }
    649     system.exit(status);
    650 }
    651 
    652 pub const ReadError = error{
    653     InputOutput,
    654     SystemResources,
    655     IsDir,
    656     OperationAborted,
    657     BrokenPipe,
    658     ConnectionResetByPeer,
    659     ConnectionTimedOut,
    660     NotOpenForReading,
    661 
    662     // Windows only
    663     NetNameDeleted,
    664 
    665     /// This error occurs when no global event loop is configured,
    666     /// and reading from the file descriptor would block.
    667     WouldBlock,
    668 
    669     /// In WASI, this error occurs when the file descriptor does
    670     /// not hold the required rights to read from it.
    671     AccessDenied,
    672 } || UnexpectedError;
    673 
    674 /// Returns the number of bytes that were read, which can be less than
    675 /// buf.len. If 0 bytes were read, that means EOF.
    676 /// If `fd` is opened in non blocking mode, the function will return error.WouldBlock
    677 /// when EAGAIN is received.
    678 ///
    679 /// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
    680 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
    681 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
    682 /// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
    683 /// The corresponding POSIX limit is `math.maxInt(isize)`.
    684 pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
    685     if (buf.len == 0) return 0;
    686     if (builtin.os.tag == .windows) {
    687         return windows.ReadFile(fd, buf, null, std.io.default_mode);
    688     }
    689     if (builtin.os.tag == .wasi and !builtin.link_libc) {
    690         const iovs = [1]iovec{iovec{
    691             .iov_base = buf.ptr,
    692             .iov_len = buf.len,
    693         }};
    694 
    695         var nread: usize = undefined;
    696         switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
    697             .SUCCESS => return nread,
    698             .INTR => unreachable,
    699             .INVAL => unreachable,
    700             .FAULT => unreachable,
    701             .AGAIN => unreachable,
    702             .BADF => return error.NotOpenForReading, // Can be a race condition.
    703             .IO => return error.InputOutput,
    704             .ISDIR => return error.IsDir,
    705             .NOBUFS => return error.SystemResources,
    706             .NOMEM => return error.SystemResources,
    707             .CONNRESET => return error.ConnectionResetByPeer,
    708             .TIMEDOUT => return error.ConnectionTimedOut,
    709             .NOTCAPABLE => return error.AccessDenied,
    710             else => |err| return unexpectedErrno(err),
    711         }
    712     }
    713 
    714     // Prevents EINVAL.
    715     const max_count = switch (builtin.os.tag) {
    716         .linux => 0x7ffff000,
    717         .macos, .ios, .watchos, .tvos => math.maxInt(i32),
    718         else => math.maxInt(isize),
    719     };
    720     const adjusted_len = @min(max_count, buf.len);
    721 
    722     while (true) {
    723         const rc = system.read(fd, buf.ptr, adjusted_len);
    724         switch (errno(rc)) {
    725             .SUCCESS => return @intCast(usize, rc),
    726             .INTR => continue,
    727             .INVAL => unreachable,
    728             .FAULT => unreachable,
    729             .AGAIN => return error.WouldBlock,
    730             .BADF => return error.NotOpenForReading, // Can be a race condition.
    731             .IO => return error.InputOutput,
    732             .ISDIR => return error.IsDir,
    733             .NOBUFS => return error.SystemResources,
    734             .NOMEM => return error.SystemResources,
    735             .CONNRESET => return error.ConnectionResetByPeer,
    736             .TIMEDOUT => return error.ConnectionTimedOut,
    737             else => |err| return unexpectedErrno(err),
    738         }
    739     }
    740 }
    741 
    742 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
    743 ///
    744 /// For POSIX systems, if `fd` is opened in non blocking mode, the function will
    745 /// return error.WouldBlock when EAGAIN is received.
    746 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
    747 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
    748 ///
    749 /// This operation is non-atomic on the following systems:
    750 /// * Windows
    751 /// On these systems, the read races with concurrent writes to the same file descriptor.
    752 pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
    753     if (builtin.os.tag == .windows) {
    754         // TODO improve this to use ReadFileScatter
    755         if (iov.len == 0) return @as(usize, 0);
    756         const first = iov[0];
    757         return read(fd, first.iov_base[0..first.iov_len]);
    758     }
    759     if (builtin.os.tag == .wasi and !builtin.link_libc) {
    760         var nread: usize = undefined;
    761         switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) {
    762             .SUCCESS => return nread,
    763             .INTR => unreachable,
    764             .INVAL => unreachable,
    765             .FAULT => unreachable,
    766             .AGAIN => unreachable, // currently not support in WASI
    767             .BADF => return error.NotOpenForReading, // can be a race condition
    768             .IO => return error.InputOutput,
    769             .ISDIR => return error.IsDir,
    770             .NOBUFS => return error.SystemResources,
    771             .NOMEM => return error.SystemResources,
    772             .NOTCAPABLE => return error.AccessDenied,
    773             else => |err| return unexpectedErrno(err),
    774         }
    775     }
    776     const iov_count = math.cast(u31, iov.len) orelse math.maxInt(u31);
    777     while (true) {
    778         // TODO handle the case when iov_len is too large and get rid of this @intCast
    779         const rc = system.readv(fd, iov.ptr, iov_count);
    780         switch (errno(rc)) {
    781             .SUCCESS => return @intCast(usize, rc),
    782             .INTR => continue,
    783             .INVAL => unreachable,
    784             .FAULT => unreachable,
    785             .AGAIN => return error.WouldBlock,
    786             .BADF => return error.NotOpenForReading, // can be a race condition
    787             .IO => return error.InputOutput,
    788             .ISDIR => return error.IsDir,
    789             .NOBUFS => return error.SystemResources,
    790             .NOMEM => return error.SystemResources,
    791             .CONNRESET => return error.ConnectionResetByPeer,
    792             else => |err| return unexpectedErrno(err),
    793         }
    794     }
    795 }
    796 
    797 pub const PReadError = ReadError || error{Unseekable};
    798 
    799 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
    800 ///
    801 /// Retries when interrupted by a signal.
    802 ///
    803 /// For POSIX systems, if `fd` is opened in non blocking mode, the function will
    804 /// return error.WouldBlock when EAGAIN is received.
    805 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
    806 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
    807 ///
    808 /// Linux has a limit on how many bytes may be transferred in one `pread` call, which is `0x7ffff000`
    809 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
    810 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
    811 /// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
    812 /// The corresponding POSIX limit is `math.maxInt(isize)`.
    813 pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
    814     if (buf.len == 0) return 0;
    815     if (builtin.os.tag == .windows) {
    816         return windows.ReadFile(fd, buf, offset, std.io.default_mode);
    817     }
    818     if (builtin.os.tag == .wasi and !builtin.link_libc) {
    819         const iovs = [1]iovec{iovec{
    820             .iov_base = buf.ptr,
    821             .iov_len = buf.len,
    822         }};
    823 
    824         var nread: usize = undefined;
    825         switch (wasi.fd_pread(fd, &iovs, iovs.len, offset, &nread)) {
    826             .SUCCESS => return nread,
    827             .INTR => unreachable,
    828             .INVAL => unreachable,
    829             .FAULT => unreachable,
    830             .AGAIN => unreachable,
    831             .BADF => return error.NotOpenForReading, // Can be a race condition.
    832             .IO => return error.InputOutput,
    833             .ISDIR => return error.IsDir,
    834             .NOBUFS => return error.SystemResources,
    835             .NOMEM => return error.SystemResources,
    836             .CONNRESET => return error.ConnectionResetByPeer,
    837             .NXIO => return error.Unseekable,
    838             .SPIPE => return error.Unseekable,
    839             .OVERFLOW => return error.Unseekable,
    840             .NOTCAPABLE => return error.AccessDenied,
    841             else => |err| return unexpectedErrno(err),
    842         }
    843     }
    844 
    845     // Prevent EINVAL.
    846     const max_count = switch (builtin.os.tag) {
    847         .linux => 0x7ffff000,
    848         .macos, .ios, .watchos, .tvos => math.maxInt(i32),
    849         else => math.maxInt(isize),
    850     };
    851     const adjusted_len = @min(max_count, buf.len);
    852 
    853     const pread_sym = if (builtin.os.tag == .linux and builtin.link_libc)
    854         system.pread64
    855     else
    856         system.pread;
    857 
    858     const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned
    859     while (true) {
    860         const rc = pread_sym(fd, buf.ptr, adjusted_len, ioffset);
    861         switch (errno(rc)) {
    862             .SUCCESS => return @intCast(usize, rc),
    863             .INTR => continue,
    864             .INVAL => unreachable,
    865             .FAULT => unreachable,
    866             .AGAIN => return error.WouldBlock,
    867             .BADF => return error.NotOpenForReading, // Can be a race condition.
    868             .IO => return error.InputOutput,
    869             .ISDIR => return error.IsDir,
    870             .NOBUFS => return error.SystemResources,
    871             .NOMEM => return error.SystemResources,
    872             .CONNRESET => return error.ConnectionResetByPeer,
    873             .NXIO => return error.Unseekable,
    874             .SPIPE => return error.Unseekable,
    875             .OVERFLOW => return error.Unseekable,
    876             else => |err| return unexpectedErrno(err),
    877         }
    878     }
    879 }
    880 
    881 pub const TruncateError = error{
    882     FileTooBig,
    883     InputOutput,
    884     FileBusy,
    885 
    886     /// In WASI, this error occurs when the file descriptor does
    887     /// not hold the required rights to call `ftruncate` on it.
    888     AccessDenied,
    889 } || UnexpectedError;
    890 
    891 pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
    892     if (builtin.os.tag == .windows) {
    893         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
    894         var eof_info = windows.FILE_END_OF_FILE_INFORMATION{
    895             .EndOfFile = @bitCast(windows.LARGE_INTEGER, length),
    896         };
    897 
    898         const rc = windows.ntdll.NtSetInformationFile(
    899             fd,
    900             &io_status_block,
    901             &eof_info,
    902             @sizeOf(windows.FILE_END_OF_FILE_INFORMATION),
    903             .FileEndOfFileInformation,
    904         );
    905 
    906         switch (rc) {
    907             .SUCCESS => return,
    908             .INVALID_HANDLE => unreachable, // Handle not open for writing
    909             .ACCESS_DENIED => return error.AccessDenied,
    910             else => return windows.unexpectedStatus(rc),
    911         }
    912     }
    913     if (builtin.os.tag == .wasi and !builtin.link_libc) {
    914         switch (wasi.fd_filestat_set_size(fd, length)) {
    915             .SUCCESS => return,
    916             .INTR => unreachable,
    917             .FBIG => return error.FileTooBig,
    918             .IO => return error.InputOutput,
    919             .PERM => return error.AccessDenied,
    920             .TXTBSY => return error.FileBusy,
    921             .BADF => unreachable, // Handle not open for writing
    922             .INVAL => unreachable, // Handle not open for writing
    923             .NOTCAPABLE => return error.AccessDenied,
    924             else => |err| return unexpectedErrno(err),
    925         }
    926     }
    927 
    928     while (true) {
    929         const ftruncate_sym = if (builtin.os.tag == .linux and builtin.link_libc)
    930             system.ftruncate64
    931         else
    932             system.ftruncate;
    933 
    934         const ilen = @bitCast(i64, length); // the OS treats this as unsigned
    935         switch (errno(ftruncate_sym(fd, ilen))) {
    936             .SUCCESS => return,
    937             .INTR => continue,
    938             .FBIG => return error.FileTooBig,
    939             .IO => return error.InputOutput,
    940             .PERM => return error.AccessDenied,
    941             .TXTBSY => return error.FileBusy,
    942             .BADF => unreachable, // Handle not open for writing
    943             .INVAL => unreachable, // Handle not open for writing
    944             else => |err| return unexpectedErrno(err),
    945         }
    946     }
    947 }
    948 
    949 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
    950 ///
    951 /// Retries when interrupted by a signal.
    952 ///
    953 /// For POSIX systems, if `fd` is opened in non blocking mode, the function will
    954 /// return error.WouldBlock when EAGAIN is received.
    955 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
    956 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
    957 ///
    958 /// This operation is non-atomic on the following systems:
    959 /// * Darwin
    960 /// * Windows
    961 /// On these systems, the read races with concurrent writes to the same file descriptor.
    962 pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
    963     const have_pread_but_not_preadv = switch (builtin.os.tag) {
    964         .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
    965         else => false,
    966     };
    967     if (have_pread_but_not_preadv) {
    968         // We could loop here; but proper usage of `preadv` must handle partial reads anyway.
    969         // So we simply read into the first vector only.
    970         if (iov.len == 0) return @as(usize, 0);
    971         const first = iov[0];
    972         return pread(fd, first.iov_base[0..first.iov_len], offset);
    973     }
    974     if (builtin.os.tag == .wasi and !builtin.link_libc) {
    975         var nread: usize = undefined;
    976         switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) {
    977             .SUCCESS => return nread,
    978             .INTR => unreachable,
    979             .INVAL => unreachable,
    980             .FAULT => unreachable,
    981             .AGAIN => unreachable,
    982             .BADF => return error.NotOpenForReading, // can be a race condition
    983             .IO => return error.InputOutput,
    984             .ISDIR => return error.IsDir,
    985             .NOBUFS => return error.SystemResources,
    986             .NOMEM => return error.SystemResources,
    987             .NXIO => return error.Unseekable,
    988             .SPIPE => return error.Unseekable,
    989             .OVERFLOW => return error.Unseekable,
    990             .NOTCAPABLE => return error.AccessDenied,
    991             else => |err| return unexpectedErrno(err),
    992         }
    993     }
    994 
    995     const iov_count = math.cast(u31, iov.len) orelse math.maxInt(u31);
    996 
    997     const preadv_sym = if (builtin.os.tag == .linux and builtin.link_libc)
    998         system.preadv64
    999     else
   1000         system.preadv;
   1001 
   1002     const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned
   1003     while (true) {
   1004         const rc = preadv_sym(fd, iov.ptr, iov_count, ioffset);
   1005         switch (errno(rc)) {
   1006             .SUCCESS => return @bitCast(usize, rc),
   1007             .INTR => continue,
   1008             .INVAL => unreachable,
   1009             .FAULT => unreachable,
   1010             .AGAIN => return error.WouldBlock,
   1011             .BADF => return error.NotOpenForReading, // can be a race condition
   1012             .IO => return error.InputOutput,
   1013             .ISDIR => return error.IsDir,
   1014             .NOBUFS => return error.SystemResources,
   1015             .NOMEM => return error.SystemResources,
   1016             .NXIO => return error.Unseekable,
   1017             .SPIPE => return error.Unseekable,
   1018             .OVERFLOW => return error.Unseekable,
   1019             else => |err| return unexpectedErrno(err),
   1020         }
   1021     }
   1022 }
   1023 
   1024 pub const WriteError = error{
   1025     DiskQuota,
   1026     FileTooBig,
   1027     InputOutput,
   1028     NoSpaceLeft,
   1029     DeviceBusy,
   1030 
   1031     /// In WASI, this error may occur when the file descriptor does
   1032     /// not hold the required rights to write to it.
   1033     AccessDenied,
   1034     BrokenPipe,
   1035     SystemResources,
   1036     OperationAborted,
   1037     NotOpenForWriting,
   1038 
   1039     /// The process cannot access the file because another process has locked
   1040     /// a portion of the file. Windows-only.
   1041     LockViolation,
   1042 
   1043     /// This error occurs when no global event loop is configured,
   1044     /// and reading from the file descriptor would block.
   1045     WouldBlock,
   1046 
   1047     /// Connection reset by peer.
   1048     ConnectionResetByPeer,
   1049 } || UnexpectedError;
   1050 
   1051 /// Write to a file descriptor.
   1052 /// Retries when interrupted by a signal.
   1053 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
   1054 ///
   1055 /// Note that a successful write() may transfer fewer than count bytes.  Such partial  writes  can
   1056 /// occur  for  various reasons; for example, because there was insufficient space on the disk
   1057 /// device to write all of the requested bytes, or because a blocked write() to a socket,  pipe,  or
   1058 /// similar  was  interrupted by a signal handler after it had transferred some, but before it had
   1059 /// transferred all of the requested bytes.  In the event of a partial write, the caller can  make
   1060 /// another  write() call to transfer the remaining bytes.  The subsequent call will either
   1061 /// transfer further bytes or may result in an error (e.g., if the disk is now full).
   1062 ///
   1063 /// For POSIX systems, if `fd` is opened in non blocking mode, the function will
   1064 /// return error.WouldBlock when EAGAIN is received.
   1065 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
   1066 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
   1067 ///
   1068 /// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000`
   1069 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
   1070 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
   1071 /// The limit on Darwin is `0x7fffffff`, trying to read more than that returns EINVAL.
   1072 /// The corresponding POSIX limit is `math.maxInt(isize)`.
   1073 pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
   1074     if (bytes.len == 0) return 0;
   1075     if (builtin.os.tag == .windows) {
   1076         return windows.WriteFile(fd, bytes, null, std.io.default_mode);
   1077     }
   1078 
   1079     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   1080         const ciovs = [_]iovec_const{iovec_const{
   1081             .iov_base = bytes.ptr,
   1082             .iov_len = bytes.len,
   1083         }};
   1084         var nwritten: usize = undefined;
   1085         switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
   1086             .SUCCESS => return nwritten,
   1087             .INTR => unreachable,
   1088             .INVAL => unreachable,
   1089             .FAULT => unreachable,
   1090             .AGAIN => unreachable,
   1091             .BADF => return error.NotOpenForWriting, // can be a race condition.
   1092             .DESTADDRREQ => unreachable, // `connect` was never called.
   1093             .DQUOT => return error.DiskQuota,
   1094             .FBIG => return error.FileTooBig,
   1095             .IO => return error.InputOutput,
   1096             .NOSPC => return error.NoSpaceLeft,
   1097             .PERM => return error.AccessDenied,
   1098             .PIPE => return error.BrokenPipe,
   1099             .NOTCAPABLE => return error.AccessDenied,
   1100             else => |err| return unexpectedErrno(err),
   1101         }
   1102     }
   1103 
   1104     const max_count = switch (builtin.os.tag) {
   1105         .linux => 0x7ffff000,
   1106         .macos, .ios, .watchos, .tvos => math.maxInt(i32),
   1107         else => math.maxInt(isize),
   1108     };
   1109     const adjusted_len = @min(max_count, bytes.len);
   1110 
   1111     while (true) {
   1112         const rc = system.write(fd, bytes.ptr, adjusted_len);
   1113         switch (errno(rc)) {
   1114             .SUCCESS => return @intCast(usize, rc),
   1115             .INTR => continue,
   1116             .INVAL => unreachable,
   1117             .FAULT => unreachable,
   1118             .AGAIN => return error.WouldBlock,
   1119             .BADF => return error.NotOpenForWriting, // can be a race condition.
   1120             .DESTADDRREQ => unreachable, // `connect` was never called.
   1121             .DQUOT => return error.DiskQuota,
   1122             .FBIG => return error.FileTooBig,
   1123             .IO => return error.InputOutput,
   1124             .NOSPC => return error.NoSpaceLeft,
   1125             .PERM => return error.AccessDenied,
   1126             .PIPE => return error.BrokenPipe,
   1127             .CONNRESET => return error.ConnectionResetByPeer,
   1128             .BUSY => return error.DeviceBusy,
   1129             else => |err| return unexpectedErrno(err),
   1130         }
   1131     }
   1132 }
   1133 
   1134 /// Write multiple buffers to a file descriptor.
   1135 /// Retries when interrupted by a signal.
   1136 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
   1137 ///
   1138 /// Note that a successful write() may transfer fewer bytes than supplied.  Such partial  writes  can
   1139 /// occur  for  various reasons; for example, because there was insufficient space on the disk
   1140 /// device to write all of the requested bytes, or because a blocked write() to a socket,  pipe,  or
   1141 /// similar  was  interrupted by a signal handler after it had transferred some, but before it had
   1142 /// transferred all of the requested bytes.  In the event of a partial write, the caller can  make
   1143 /// another  write() call to transfer the remaining bytes.  The subsequent call will either
   1144 /// transfer further bytes or may result in an error (e.g., if the disk is now full).
   1145 ///
   1146 /// For POSIX systems, if `fd` is opened in non blocking mode, the function will
   1147 /// return error.WouldBlock when EAGAIN is received.
   1148 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
   1149 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
   1150 ///
   1151 /// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
   1152 pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
   1153     if (builtin.os.tag == .windows) {
   1154         // TODO improve this to use WriteFileScatter
   1155         if (iov.len == 0) return @as(usize, 0);
   1156         const first = iov[0];
   1157         return write(fd, first.iov_base[0..first.iov_len]);
   1158     }
   1159     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   1160         var nwritten: usize = undefined;
   1161         switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) {
   1162             .SUCCESS => return nwritten,
   1163             .INTR => unreachable,
   1164             .INVAL => unreachable,
   1165             .FAULT => unreachable,
   1166             .AGAIN => unreachable,
   1167             .BADF => return error.NotOpenForWriting, // can be a race condition.
   1168             .DESTADDRREQ => unreachable, // `connect` was never called.
   1169             .DQUOT => return error.DiskQuota,
   1170             .FBIG => return error.FileTooBig,
   1171             .IO => return error.InputOutput,
   1172             .NOSPC => return error.NoSpaceLeft,
   1173             .PERM => return error.AccessDenied,
   1174             .PIPE => return error.BrokenPipe,
   1175             .NOTCAPABLE => return error.AccessDenied,
   1176             else => |err| return unexpectedErrno(err),
   1177         }
   1178     }
   1179 
   1180     const iov_count = if (iov.len > IOV_MAX) IOV_MAX else @intCast(u31, iov.len);
   1181     while (true) {
   1182         const rc = system.writev(fd, iov.ptr, iov_count);
   1183         switch (errno(rc)) {
   1184             .SUCCESS => return @intCast(usize, rc),
   1185             .INTR => continue,
   1186             .INVAL => unreachable,
   1187             .FAULT => unreachable,
   1188             .AGAIN => return error.WouldBlock,
   1189             .BADF => return error.NotOpenForWriting, // Can be a race condition.
   1190             .DESTADDRREQ => unreachable, // `connect` was never called.
   1191             .DQUOT => return error.DiskQuota,
   1192             .FBIG => return error.FileTooBig,
   1193             .IO => return error.InputOutput,
   1194             .NOSPC => return error.NoSpaceLeft,
   1195             .PERM => return error.AccessDenied,
   1196             .PIPE => return error.BrokenPipe,
   1197             .CONNRESET => return error.ConnectionResetByPeer,
   1198             .BUSY => return error.DeviceBusy,
   1199             else => |err| return unexpectedErrno(err),
   1200         }
   1201     }
   1202 }
   1203 
   1204 pub const PWriteError = WriteError || error{Unseekable};
   1205 
   1206 /// Write to a file descriptor, with a position offset.
   1207 /// Retries when interrupted by a signal.
   1208 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
   1209 ///
   1210 /// Note that a successful write() may transfer fewer bytes than supplied.  Such partial  writes  can
   1211 /// occur  for  various reasons; for example, because there was insufficient space on the disk
   1212 /// device to write all of the requested bytes, or because a blocked write() to a socket,  pipe,  or
   1213 /// similar  was  interrupted by a signal handler after it had transferred some, but before it had
   1214 /// transferred all of the requested bytes.  In the event of a partial write, the caller can  make
   1215 /// another  write() call to transfer the remaining bytes.  The subsequent call will either
   1216 /// transfer further bytes or may result in an error (e.g., if the disk is now full).
   1217 ///
   1218 /// For POSIX systems, if `fd` is opened in non blocking mode, the function will
   1219 /// return error.WouldBlock when EAGAIN is received.
   1220 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
   1221 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
   1222 ///
   1223 /// Linux has a limit on how many bytes may be transferred in one `pwrite` call, which is `0x7ffff000`
   1224 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
   1225 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
   1226 /// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
   1227 /// The corresponding POSIX limit is `math.maxInt(isize)`.
   1228 pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
   1229     if (bytes.len == 0) return 0;
   1230     if (builtin.os.tag == .windows) {
   1231         return windows.WriteFile(fd, bytes, offset, std.io.default_mode);
   1232     }
   1233     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   1234         const ciovs = [1]iovec_const{iovec_const{
   1235             .iov_base = bytes.ptr,
   1236             .iov_len = bytes.len,
   1237         }};
   1238 
   1239         var nwritten: usize = undefined;
   1240         switch (wasi.fd_pwrite(fd, &ciovs, ciovs.len, offset, &nwritten)) {
   1241             .SUCCESS => return nwritten,
   1242             .INTR => unreachable,
   1243             .INVAL => unreachable,
   1244             .FAULT => unreachable,
   1245             .AGAIN => unreachable,
   1246             .BADF => return error.NotOpenForWriting, // can be a race condition.
   1247             .DESTADDRREQ => unreachable, // `connect` was never called.
   1248             .DQUOT => return error.DiskQuota,
   1249             .FBIG => return error.FileTooBig,
   1250             .IO => return error.InputOutput,
   1251             .NOSPC => return error.NoSpaceLeft,
   1252             .PERM => return error.AccessDenied,
   1253             .PIPE => return error.BrokenPipe,
   1254             .NXIO => return error.Unseekable,
   1255             .SPIPE => return error.Unseekable,
   1256             .OVERFLOW => return error.Unseekable,
   1257             .NOTCAPABLE => return error.AccessDenied,
   1258             else => |err| return unexpectedErrno(err),
   1259         }
   1260     }
   1261 
   1262     // Prevent EINVAL.
   1263     const max_count = switch (builtin.os.tag) {
   1264         .linux => 0x7ffff000,
   1265         .macos, .ios, .watchos, .tvos => math.maxInt(i32),
   1266         else => math.maxInt(isize),
   1267     };
   1268     const adjusted_len = @min(max_count, bytes.len);
   1269 
   1270     const pwrite_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   1271         system.pwrite64
   1272     else
   1273         system.pwrite;
   1274 
   1275     const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned
   1276     while (true) {
   1277         const rc = pwrite_sym(fd, bytes.ptr, adjusted_len, ioffset);
   1278         switch (errno(rc)) {
   1279             .SUCCESS => return @intCast(usize, rc),
   1280             .INTR => continue,
   1281             .INVAL => unreachable,
   1282             .FAULT => unreachable,
   1283             .AGAIN => return error.WouldBlock,
   1284             .BADF => return error.NotOpenForWriting, // Can be a race condition.
   1285             .DESTADDRREQ => unreachable, // `connect` was never called.
   1286             .DQUOT => return error.DiskQuota,
   1287             .FBIG => return error.FileTooBig,
   1288             .IO => return error.InputOutput,
   1289             .NOSPC => return error.NoSpaceLeft,
   1290             .PERM => return error.AccessDenied,
   1291             .PIPE => return error.BrokenPipe,
   1292             .NXIO => return error.Unseekable,
   1293             .SPIPE => return error.Unseekable,
   1294             .OVERFLOW => return error.Unseekable,
   1295             .BUSY => return error.DeviceBusy,
   1296             else => |err| return unexpectedErrno(err),
   1297         }
   1298     }
   1299 }
   1300 
   1301 /// Write multiple buffers to a file descriptor, with a position offset.
   1302 /// Retries when interrupted by a signal.
   1303 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
   1304 ///
   1305 /// Note that a successful write() may transfer fewer than count bytes.  Such partial  writes  can
   1306 /// occur  for  various reasons; for example, because there was insufficient space on the disk
   1307 /// device to write all of the requested bytes, or because a blocked write() to a socket,  pipe,  or
   1308 /// similar  was  interrupted by a signal handler after it had transferred some, but before it had
   1309 /// transferred all of the requested bytes.  In the event of a partial write, the caller can  make
   1310 /// another  write() call to transfer the remaining bytes.  The subsequent call will either
   1311 /// transfer further bytes or may result in an error (e.g., if the disk is now full).
   1312 ///
   1313 /// If `fd` is opened in non blocking mode, the function will
   1314 /// return error.WouldBlock when EAGAIN is received.
   1315 ///
   1316 /// The following systems do not have this syscall, and will return partial writes if more than one
   1317 /// vector is provided:
   1318 /// * Darwin
   1319 /// * Windows
   1320 ///
   1321 /// If `iov.len` is larger than `IOV_MAX`, a partial write will occur.
   1322 pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {
   1323     const have_pwrite_but_not_pwritev = switch (builtin.os.tag) {
   1324         .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
   1325         else => false,
   1326     };
   1327 
   1328     if (have_pwrite_but_not_pwritev) {
   1329         // We could loop here; but proper usage of `pwritev` must handle partial writes anyway.
   1330         // So we simply write the first vector only.
   1331         if (iov.len == 0) return @as(usize, 0);
   1332         const first = iov[0];
   1333         return pwrite(fd, first.iov_base[0..first.iov_len], offset);
   1334     }
   1335     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   1336         var nwritten: usize = undefined;
   1337         switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) {
   1338             .SUCCESS => return nwritten,
   1339             .INTR => unreachable,
   1340             .INVAL => unreachable,
   1341             .FAULT => unreachable,
   1342             .AGAIN => unreachable,
   1343             .BADF => return error.NotOpenForWriting, // Can be a race condition.
   1344             .DESTADDRREQ => unreachable, // `connect` was never called.
   1345             .DQUOT => return error.DiskQuota,
   1346             .FBIG => return error.FileTooBig,
   1347             .IO => return error.InputOutput,
   1348             .NOSPC => return error.NoSpaceLeft,
   1349             .PERM => return error.AccessDenied,
   1350             .PIPE => return error.BrokenPipe,
   1351             .NXIO => return error.Unseekable,
   1352             .SPIPE => return error.Unseekable,
   1353             .OVERFLOW => return error.Unseekable,
   1354             .NOTCAPABLE => return error.AccessDenied,
   1355             else => |err| return unexpectedErrno(err),
   1356         }
   1357     }
   1358 
   1359     const pwritev_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   1360         system.pwritev64
   1361     else
   1362         system.pwritev;
   1363 
   1364     const iov_count = if (iov.len > IOV_MAX) IOV_MAX else @intCast(u31, iov.len);
   1365     const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned
   1366     while (true) {
   1367         const rc = pwritev_sym(fd, iov.ptr, iov_count, ioffset);
   1368         switch (errno(rc)) {
   1369             .SUCCESS => return @intCast(usize, rc),
   1370             .INTR => continue,
   1371             .INVAL => unreachable,
   1372             .FAULT => unreachable,
   1373             .AGAIN => return error.WouldBlock,
   1374             .BADF => return error.NotOpenForWriting, // Can be a race condition.
   1375             .DESTADDRREQ => unreachable, // `connect` was never called.
   1376             .DQUOT => return error.DiskQuota,
   1377             .FBIG => return error.FileTooBig,
   1378             .IO => return error.InputOutput,
   1379             .NOSPC => return error.NoSpaceLeft,
   1380             .PERM => return error.AccessDenied,
   1381             .PIPE => return error.BrokenPipe,
   1382             .NXIO => return error.Unseekable,
   1383             .SPIPE => return error.Unseekable,
   1384             .OVERFLOW => return error.Unseekable,
   1385             .BUSY => return error.DeviceBusy,
   1386             else => |err| return unexpectedErrno(err),
   1387         }
   1388     }
   1389 }
   1390 
   1391 pub const OpenError = error{
   1392     /// In WASI, this error may occur when the provided file handle is invalid.
   1393     InvalidHandle,
   1394 
   1395     /// In WASI, this error may occur when the file descriptor does
   1396     /// not hold the required rights to open a new resource relative to it.
   1397     AccessDenied,
   1398     SymLinkLoop,
   1399     ProcessFdQuotaExceeded,
   1400     SystemFdQuotaExceeded,
   1401     NoDevice,
   1402     FileNotFound,
   1403 
   1404     /// The path exceeded `MAX_PATH_BYTES` bytes.
   1405     NameTooLong,
   1406 
   1407     /// Insufficient kernel memory was available, or
   1408     /// the named file is a FIFO and per-user hard limit on
   1409     /// memory allocation for pipes has been reached.
   1410     SystemResources,
   1411 
   1412     /// The file is too large to be opened. This error is unreachable
   1413     /// for 64-bit targets, as well as when opening directories.
   1414     FileTooBig,
   1415 
   1416     /// The path refers to directory but the `O.DIRECTORY` flag was not provided.
   1417     IsDir,
   1418 
   1419     /// A new path cannot be created because the device has no room for the new file.
   1420     /// This error is only reachable when the `O.CREAT` flag is provided.
   1421     NoSpaceLeft,
   1422 
   1423     /// A component used as a directory in the path was not, in fact, a directory, or
   1424     /// `O.DIRECTORY` was specified and the path was not a directory.
   1425     NotDir,
   1426 
   1427     /// The path already exists and the `O.CREAT` and `O.EXCL` flags were provided.
   1428     PathAlreadyExists,
   1429     DeviceBusy,
   1430 
   1431     /// The underlying filesystem does not support file locks
   1432     FileLocksNotSupported,
   1433 
   1434     BadPathName,
   1435     InvalidUtf8,
   1436 
   1437     /// One of these three things:
   1438     /// * pathname  refers to an executable image which is currently being
   1439     ///   executed and write access was requested.
   1440     /// * pathname refers to a file that is currently in  use  as  a  swap
   1441     ///   file, and the O_TRUNC flag was specified.
   1442     /// * pathname  refers  to  a file that is currently being read by the
   1443     ///   kernel (e.g., for module/firmware loading), and write access was
   1444     ///   requested.
   1445     FileBusy,
   1446 
   1447     WouldBlock,
   1448 } || UnexpectedError;
   1449 
   1450 /// Open and possibly create a file. Keeps trying if it gets interrupted.
   1451 /// See also `openZ`.
   1452 pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t {
   1453     if (builtin.os.tag == .windows) {
   1454         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   1455         return openW(file_path_w.span(), flags, perm);
   1456     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   1457         return openat(wasi.AT.FDCWD, file_path, flags, perm);
   1458     }
   1459     const file_path_c = try toPosixPath(file_path);
   1460     return openZ(&file_path_c, flags, perm);
   1461 }
   1462 
   1463 /// Open and possibly create a file. Keeps trying if it gets interrupted.
   1464 /// See also `open`.
   1465 pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t {
   1466     if (builtin.os.tag == .windows) {
   1467         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   1468         return openW(file_path_w.span(), flags, perm);
   1469     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   1470         return open(mem.sliceTo(file_path, 0), flags, perm);
   1471     }
   1472 
   1473     const open_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   1474         system.open64
   1475     else
   1476         system.open;
   1477 
   1478     while (true) {
   1479         const rc = open_sym(file_path, flags, perm);
   1480         switch (errno(rc)) {
   1481             .SUCCESS => return @intCast(fd_t, rc),
   1482             .INTR => continue,
   1483 
   1484             .FAULT => unreachable,
   1485             .INVAL => unreachable,
   1486             .ACCES => return error.AccessDenied,
   1487             .FBIG => return error.FileTooBig,
   1488             .OVERFLOW => return error.FileTooBig,
   1489             .ISDIR => return error.IsDir,
   1490             .LOOP => return error.SymLinkLoop,
   1491             .MFILE => return error.ProcessFdQuotaExceeded,
   1492             .NAMETOOLONG => return error.NameTooLong,
   1493             .NFILE => return error.SystemFdQuotaExceeded,
   1494             .NODEV => return error.NoDevice,
   1495             .NOENT => return error.FileNotFound,
   1496             .NOMEM => return error.SystemResources,
   1497             .NOSPC => return error.NoSpaceLeft,
   1498             .NOTDIR => return error.NotDir,
   1499             .PERM => return error.AccessDenied,
   1500             .EXIST => return error.PathAlreadyExists,
   1501             .BUSY => return error.DeviceBusy,
   1502             else => |err| return unexpectedErrno(err),
   1503         }
   1504     }
   1505 }
   1506 
   1507 fn openOptionsFromFlagsWindows(flags: u32) windows.OpenFileOptions {
   1508     const w = windows;
   1509 
   1510     var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE;
   1511     if (flags & O.RDWR != 0) {
   1512         access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
   1513     } else if (flags & O.WRONLY != 0) {
   1514         access_mask |= w.GENERIC_WRITE;
   1515     } else {
   1516         access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
   1517     }
   1518 
   1519     const filter: windows.OpenFileOptions.Filter = if (flags & O.DIRECTORY != 0) .dir_only else .file_only;
   1520     const follow_symlinks: bool = flags & O.NOFOLLOW == 0;
   1521 
   1522     const creation: w.ULONG = blk: {
   1523         if (flags & O.CREAT != 0) {
   1524             if (flags & O.EXCL != 0) {
   1525                 break :blk w.FILE_CREATE;
   1526             }
   1527         }
   1528         break :blk w.FILE_OPEN;
   1529     };
   1530 
   1531     return .{
   1532         .access_mask = access_mask,
   1533         .io_mode = .blocking,
   1534         .creation = creation,
   1535         .filter = filter,
   1536         .follow_symlinks = follow_symlinks,
   1537     };
   1538 }
   1539 
   1540 /// Windows-only. The path parameter is
   1541 /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
   1542 /// Translates the POSIX open API call to a Windows API call.
   1543 /// TODO currently, this function does not handle all flag combinations
   1544 /// or makes use of perm argument.
   1545 pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t {
   1546     _ = perm;
   1547     var options = openOptionsFromFlagsWindows(flags);
   1548     options.dir = std.fs.cwd().fd;
   1549     return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
   1550         error.WouldBlock => unreachable,
   1551         error.PipeBusy => unreachable,
   1552         else => |e| return e,
   1553     };
   1554 }
   1555 
   1556 /// Open and possibly create a file. Keeps trying if it gets interrupted.
   1557 /// `file_path` is relative to the open directory handle `dir_fd`.
   1558 /// See also `openatZ`.
   1559 pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
   1560     if (builtin.os.tag == .windows) {
   1561         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   1562         return openatW(dir_fd, file_path_w.span(), flags, mode);
   1563     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   1564         // `mode` is ignored on WASI, which does not support unix-style file permissions
   1565         const opts = try openOptionsFromFlagsWasi(dir_fd, flags);
   1566         const fd = try openatWasi(
   1567             dir_fd,
   1568             file_path,
   1569             opts.lookup_flags,
   1570             opts.oflags,
   1571             opts.fs_flags,
   1572             opts.fs_rights_base,
   1573             opts.fs_rights_inheriting,
   1574         );
   1575         errdefer close(fd);
   1576 
   1577         if (flags & O.WRONLY != 0) {
   1578             const info = try fstat(fd);
   1579             if (info.filetype == .DIRECTORY)
   1580                 return error.IsDir;
   1581         }
   1582 
   1583         return fd;
   1584     }
   1585     const file_path_c = try toPosixPath(file_path);
   1586     return openatZ(dir_fd, &file_path_c, flags, mode);
   1587 }
   1588 
   1589 /// A struct to contain all lookup/rights flags accepted by `wasi.path_open`
   1590 const WasiOpenOptions = struct {
   1591     oflags: wasi.oflags_t,
   1592     lookup_flags: wasi.lookupflags_t,
   1593     fs_rights_base: wasi.rights_t,
   1594     fs_rights_inheriting: wasi.rights_t,
   1595     fs_flags: wasi.fdflags_t,
   1596 };
   1597 
   1598 /// Compute rights + flags corresponding to the provided POSIX access mode.
   1599 fn openOptionsFromFlagsWasi(fd: fd_t, oflag: u32) OpenError!WasiOpenOptions {
   1600     const w = std.os.wasi;
   1601 
   1602     // First, discover the rights that we can derive from `fd`
   1603     var fsb_cur: wasi.fdstat_t = undefined;
   1604     _ = switch (w.fd_fdstat_get(fd, &fsb_cur)) {
   1605         .SUCCESS => .{},
   1606         .BADF => return error.InvalidHandle,
   1607         else => |err| return unexpectedErrno(err),
   1608     };
   1609 
   1610     // Next, calculate the read/write rights to request, depending on the
   1611     // provided POSIX access mode
   1612     var rights: w.rights_t = 0;
   1613     if (oflag & O.RDONLY != 0) {
   1614         rights |= w.RIGHT.FD_READ | w.RIGHT.FD_READDIR;
   1615     }
   1616     if (oflag & O.WRONLY != 0) {
   1617         rights |= w.RIGHT.FD_DATASYNC | w.RIGHT.FD_WRITE |
   1618             w.RIGHT.FD_ALLOCATE | w.RIGHT.FD_FILESTAT_SET_SIZE;
   1619     }
   1620 
   1621     // Request all other rights unconditionally
   1622     rights |= ~(w.RIGHT.FD_DATASYNC | w.RIGHT.FD_READ |
   1623         w.RIGHT.FD_WRITE | w.RIGHT.FD_ALLOCATE |
   1624         w.RIGHT.FD_READDIR | w.RIGHT.FD_FILESTAT_SET_SIZE);
   1625 
   1626     // But only take rights that we can actually inherit
   1627     rights &= fsb_cur.fs_rights_inheriting;
   1628 
   1629     return WasiOpenOptions{
   1630         .oflags = @truncate(w.oflags_t, (oflag >> 12)) & 0xfff,
   1631         .lookup_flags = if (oflag & O.NOFOLLOW == 0) w.LOOKUP_SYMLINK_FOLLOW else 0,
   1632         .fs_rights_base = rights,
   1633         .fs_rights_inheriting = fsb_cur.fs_rights_inheriting,
   1634         .fs_flags = @truncate(w.fdflags_t, oflag & 0xfff),
   1635     };
   1636 }
   1637 
   1638 /// Open and possibly create a file in WASI.
   1639 pub fn openatWasi(
   1640     dir_fd: fd_t,
   1641     file_path: []const u8,
   1642     lookup_flags: lookupflags_t,
   1643     oflags: oflags_t,
   1644     fdflags: fdflags_t,
   1645     base: rights_t,
   1646     inheriting: rights_t,
   1647 ) OpenError!fd_t {
   1648     while (true) {
   1649         var fd: fd_t = undefined;
   1650         switch (wasi.path_open(dir_fd, lookup_flags, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) {
   1651             .SUCCESS => return fd,
   1652             .INTR => continue,
   1653 
   1654             .FAULT => unreachable,
   1655             .INVAL => unreachable,
   1656             .ACCES => return error.AccessDenied,
   1657             .FBIG => return error.FileTooBig,
   1658             .OVERFLOW => return error.FileTooBig,
   1659             .ISDIR => return error.IsDir,
   1660             .LOOP => return error.SymLinkLoop,
   1661             .MFILE => return error.ProcessFdQuotaExceeded,
   1662             .NAMETOOLONG => return error.NameTooLong,
   1663             .NFILE => return error.SystemFdQuotaExceeded,
   1664             .NODEV => return error.NoDevice,
   1665             .NOENT => return error.FileNotFound,
   1666             .NOMEM => return error.SystemResources,
   1667             .NOSPC => return error.NoSpaceLeft,
   1668             .NOTDIR => return error.NotDir,
   1669             .PERM => return error.AccessDenied,
   1670             .EXIST => return error.PathAlreadyExists,
   1671             .BUSY => return error.DeviceBusy,
   1672             .NOTCAPABLE => return error.AccessDenied,
   1673             else => |err| return unexpectedErrno(err),
   1674         }
   1675     }
   1676 }
   1677 
   1678 /// Open and possibly create a file. Keeps trying if it gets interrupted.
   1679 /// `file_path` is relative to the open directory handle `dir_fd`.
   1680 /// See also `openat`.
   1681 pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
   1682     if (builtin.os.tag == .windows) {
   1683         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   1684         return openatW(dir_fd, file_path_w.span(), flags, mode);
   1685     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   1686         return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode);
   1687     }
   1688 
   1689     const openat_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   1690         system.openat64
   1691     else
   1692         system.openat;
   1693 
   1694     while (true) {
   1695         const rc = openat_sym(dir_fd, file_path, flags, mode);
   1696         switch (errno(rc)) {
   1697             .SUCCESS => return @intCast(fd_t, rc),
   1698             .INTR => continue,
   1699 
   1700             .FAULT => unreachable,
   1701             .INVAL => unreachable,
   1702             .BADF => unreachable,
   1703             .ACCES => return error.AccessDenied,
   1704             .FBIG => return error.FileTooBig,
   1705             .OVERFLOW => return error.FileTooBig,
   1706             .ISDIR => return error.IsDir,
   1707             .LOOP => return error.SymLinkLoop,
   1708             .MFILE => return error.ProcessFdQuotaExceeded,
   1709             .NAMETOOLONG => return error.NameTooLong,
   1710             .NFILE => return error.SystemFdQuotaExceeded,
   1711             .NODEV => return error.NoDevice,
   1712             .NOENT => return error.FileNotFound,
   1713             .NOMEM => return error.SystemResources,
   1714             .NOSPC => return error.NoSpaceLeft,
   1715             .NOTDIR => return error.NotDir,
   1716             .PERM => return error.AccessDenied,
   1717             .EXIST => return error.PathAlreadyExists,
   1718             .BUSY => return error.DeviceBusy,
   1719             .OPNOTSUPP => return error.FileLocksNotSupported,
   1720             .AGAIN => return error.WouldBlock,
   1721             .TXTBSY => return error.FileBusy,
   1722             else => |err| return unexpectedErrno(err),
   1723         }
   1724     }
   1725 }
   1726 
   1727 /// Windows-only. Similar to `openat` but with pathname argument null-terminated
   1728 /// WTF16 encoded.
   1729 /// TODO currently, this function does not handle all flag combinations
   1730 /// or makes use of perm argument.
   1731 pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t {
   1732     _ = mode;
   1733     var options = openOptionsFromFlagsWindows(flags);
   1734     options.dir = dir_fd;
   1735     return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
   1736         error.WouldBlock => unreachable,
   1737         error.PipeBusy => unreachable,
   1738         else => |e| return e,
   1739     };
   1740 }
   1741 
   1742 pub fn dup(old_fd: fd_t) !fd_t {
   1743     const rc = system.dup(old_fd);
   1744     return switch (errno(rc)) {
   1745         .SUCCESS => return @intCast(fd_t, rc),
   1746         .MFILE => error.ProcessFdQuotaExceeded,
   1747         .BADF => unreachable, // invalid file descriptor
   1748         else => |err| return unexpectedErrno(err),
   1749     };
   1750 }
   1751 
   1752 pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
   1753     while (true) {
   1754         switch (errno(system.dup2(old_fd, new_fd))) {
   1755             .SUCCESS => return,
   1756             .BUSY, .INTR => continue,
   1757             .MFILE => return error.ProcessFdQuotaExceeded,
   1758             .INVAL => unreachable, // invalid parameters passed to dup2
   1759             .BADF => unreachable, // invalid file descriptor
   1760             else => |err| return unexpectedErrno(err),
   1761         }
   1762     }
   1763 }
   1764 
   1765 pub const ExecveError = error{
   1766     SystemResources,
   1767     AccessDenied,
   1768     InvalidExe,
   1769     FileSystem,
   1770     IsDir,
   1771     FileNotFound,
   1772     NotDir,
   1773     FileBusy,
   1774     ProcessFdQuotaExceeded,
   1775     SystemFdQuotaExceeded,
   1776     NameTooLong,
   1777 } || UnexpectedError;
   1778 
   1779 /// This function ignores PATH environment variable. See `execvpeZ` for that.
   1780 pub fn execveZ(
   1781     path: [*:0]const u8,
   1782     child_argv: [*:null]const ?[*:0]const u8,
   1783     envp: [*:null]const ?[*:0]const u8,
   1784 ) ExecveError {
   1785     switch (errno(system.execve(path, child_argv, envp))) {
   1786         .SUCCESS => unreachable,
   1787         .FAULT => unreachable,
   1788         .@"2BIG" => return error.SystemResources,
   1789         .MFILE => return error.ProcessFdQuotaExceeded,
   1790         .NAMETOOLONG => return error.NameTooLong,
   1791         .NFILE => return error.SystemFdQuotaExceeded,
   1792         .NOMEM => return error.SystemResources,
   1793         .ACCES => return error.AccessDenied,
   1794         .PERM => return error.AccessDenied,
   1795         .INVAL => return error.InvalidExe,
   1796         .NOEXEC => return error.InvalidExe,
   1797         .IO => return error.FileSystem,
   1798         .LOOP => return error.FileSystem,
   1799         .ISDIR => return error.IsDir,
   1800         .NOENT => return error.FileNotFound,
   1801         .NOTDIR => return error.NotDir,
   1802         .TXTBSY => return error.FileBusy,
   1803         else => |err| switch (builtin.os.tag) {
   1804             .macos, .ios, .tvos, .watchos => switch (err) {
   1805                 .BADEXEC => return error.InvalidExe,
   1806                 .BADARCH => return error.InvalidExe,
   1807                 else => return unexpectedErrno(err),
   1808             },
   1809             .linux, .solaris => switch (err) {
   1810                 .LIBBAD => return error.InvalidExe,
   1811                 else => return unexpectedErrno(err),
   1812             },
   1813             else => return unexpectedErrno(err),
   1814         },
   1815     }
   1816 }
   1817 
   1818 pub const Arg0Expand = enum {
   1819     expand,
   1820     no_expand,
   1821 };
   1822 
   1823 /// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,
   1824 /// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall.
   1825 /// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in.
   1826 pub fn execvpeZ_expandArg0(
   1827     comptime arg0_expand: Arg0Expand,
   1828     file: [*:0]const u8,
   1829     child_argv: switch (arg0_expand) {
   1830         .expand => [*:null]?[*:0]const u8,
   1831         .no_expand => [*:null]const ?[*:0]const u8,
   1832     },
   1833     envp: [*:null]const ?[*:0]const u8,
   1834 ) ExecveError {
   1835     const file_slice = mem.sliceTo(file, 0);
   1836     if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);
   1837 
   1838     const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
   1839     // Use of MAX_PATH_BYTES here is valid as the path_buf will be passed
   1840     // directly to the operating system in execveZ.
   1841     var path_buf: [MAX_PATH_BYTES]u8 = undefined;
   1842     var it = mem.tokenize(u8, PATH, ":");
   1843     var seen_eacces = false;
   1844     var err: ExecveError = error.FileNotFound;
   1845 
   1846     // In case of expanding arg0 we must put it back if we return with an error.
   1847     const prev_arg0 = child_argv[0];
   1848     defer switch (arg0_expand) {
   1849         .expand => child_argv[0] = prev_arg0,
   1850         .no_expand => {},
   1851     };
   1852 
   1853     while (it.next()) |search_path| {
   1854         const path_len = search_path.len + file_slice.len + 1;
   1855         if (path_buf.len < path_len + 1) return error.NameTooLong;
   1856         mem.copy(u8, &path_buf, search_path);
   1857         path_buf[search_path.len] = '/';
   1858         mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);
   1859         path_buf[path_len] = 0;
   1860         const full_path = path_buf[0..path_len :0].ptr;
   1861         switch (arg0_expand) {
   1862             .expand => child_argv[0] = full_path,
   1863             .no_expand => {},
   1864         }
   1865         err = execveZ(full_path, child_argv, envp);
   1866         switch (err) {
   1867             error.AccessDenied => seen_eacces = true,
   1868             error.FileNotFound, error.NotDir => {},
   1869             else => |e| return e,
   1870         }
   1871     }
   1872     if (seen_eacces) return error.AccessDenied;
   1873     return err;
   1874 }
   1875 
   1876 /// This function also uses the PATH environment variable to get the full path to the executable.
   1877 /// If `file` is an absolute path, this is the same as `execveZ`.
   1878 pub fn execvpeZ(
   1879     file: [*:0]const u8,
   1880     argv_ptr: [*:null]const ?[*:0]const u8,
   1881     envp: [*:null]const ?[*:0]const u8,
   1882 ) ExecveError {
   1883     return execvpeZ_expandArg0(.no_expand, file, argv_ptr, envp);
   1884 }
   1885 
   1886 /// Get an environment variable.
   1887 /// See also `getenvZ`.
   1888 pub fn getenv(key: []const u8) ?[]const u8 {
   1889     if (builtin.link_libc) {
   1890         var small_key_buf: [64]u8 = undefined;
   1891         if (key.len < small_key_buf.len) {
   1892             mem.copy(u8, &small_key_buf, key);
   1893             small_key_buf[key.len] = 0;
   1894             const key0 = small_key_buf[0..key.len :0];
   1895             return getenvZ(key0);
   1896         }
   1897         // Search the entire `environ` because we don't have a null terminated pointer.
   1898         var ptr = std.c.environ;
   1899         while (ptr[0]) |line| : (ptr += 1) {
   1900             var line_i: usize = 0;
   1901             while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
   1902             const this_key = line[0..line_i];
   1903 
   1904             if (!mem.eql(u8, this_key, key)) continue;
   1905 
   1906             var end_i: usize = line_i;
   1907             while (line[end_i] != 0) : (end_i += 1) {}
   1908             const value = line[line_i + 1 .. end_i];
   1909 
   1910             return value;
   1911         }
   1912         return null;
   1913     }
   1914     if (builtin.os.tag == .windows) {
   1915         @compileError("std.os.getenv is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API.");
   1916     }
   1917     // TODO see https://github.com/ziglang/zig/issues/4524
   1918     for (environ) |ptr| {
   1919         var line_i: usize = 0;
   1920         while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
   1921         const this_key = ptr[0..line_i];
   1922         if (!mem.eql(u8, key, this_key)) continue;
   1923 
   1924         var end_i: usize = line_i;
   1925         while (ptr[end_i] != 0) : (end_i += 1) {}
   1926         const this_value = ptr[line_i + 1 .. end_i];
   1927 
   1928         return this_value;
   1929     }
   1930     return null;
   1931 }
   1932 
   1933 /// Get an environment variable with a null-terminated name.
   1934 /// See also `getenv`.
   1935 pub fn getenvZ(key: [*:0]const u8) ?[]const u8 {
   1936     if (builtin.link_libc) {
   1937         const value = system.getenv(key) orelse return null;
   1938         return mem.sliceTo(value, 0);
   1939     }
   1940     if (builtin.os.tag == .windows) {
   1941         @compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API.");
   1942     }
   1943     return getenv(mem.sliceTo(key, 0));
   1944 }
   1945 
   1946 /// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name.
   1947 /// See also `getenv`.
   1948 /// This function performs a Unicode-aware case-insensitive lookup using RtlEqualUnicodeString.
   1949 pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 {
   1950     if (builtin.os.tag != .windows) {
   1951         @compileError("std.os.getenvW is a Windows-only API");
   1952     }
   1953     const key_slice = mem.sliceTo(key, 0);
   1954     const ptr = windows.peb().ProcessParameters.Environment;
   1955     var i: usize = 0;
   1956     while (ptr[i] != 0) {
   1957         const key_start = i;
   1958 
   1959         // There are some special environment variables that start with =,
   1960         // so we need a special case to not treat = as a key/value separator
   1961         // if it's the first character.
   1962         // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
   1963         if (ptr[key_start] == '=') i += 1;
   1964 
   1965         while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
   1966         const this_key = ptr[key_start..i];
   1967 
   1968         if (ptr[i] == '=') i += 1;
   1969 
   1970         const value_start = i;
   1971         while (ptr[i] != 0) : (i += 1) {}
   1972         const this_value = ptr[value_start..i :0];
   1973 
   1974         if (windows.eqlIgnoreCaseWTF16(key_slice, this_key)) {
   1975             return this_value;
   1976         }
   1977 
   1978         i += 1; // skip over null byte
   1979     }
   1980     return null;
   1981 }
   1982 
   1983 pub const GetCwdError = error{
   1984     NameTooLong,
   1985     CurrentWorkingDirectoryUnlinked,
   1986 } || UnexpectedError;
   1987 
   1988 /// The result is a slice of out_buffer, indexed from 0.
   1989 pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
   1990     if (builtin.os.tag == .windows) {
   1991         return windows.GetCurrentDirectory(out_buffer);
   1992     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   1993         const path = ".";
   1994         if (out_buffer.len < path.len) return error.NameTooLong;
   1995         std.mem.copy(u8, out_buffer, path);
   1996         return out_buffer[0..path.len];
   1997     }
   1998 
   1999     const err = if (builtin.link_libc) blk: {
   2000         const c_err = if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;
   2001         break :blk @intToEnum(E, c_err);
   2002     } else blk: {
   2003         break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len));
   2004     };
   2005     switch (err) {
   2006         .SUCCESS => return mem.sliceTo(out_buffer, 0),
   2007         .FAULT => unreachable,
   2008         .INVAL => unreachable,
   2009         .NOENT => return error.CurrentWorkingDirectoryUnlinked,
   2010         .RANGE => return error.NameTooLong,
   2011         else => return unexpectedErrno(err),
   2012     }
   2013 }
   2014 
   2015 pub const SymLinkError = error{
   2016     /// In WASI, this error may occur when the file descriptor does
   2017     /// not hold the required rights to create a new symbolic link relative to it.
   2018     AccessDenied,
   2019     DiskQuota,
   2020     PathAlreadyExists,
   2021     FileSystem,
   2022     SymLinkLoop,
   2023     FileNotFound,
   2024     SystemResources,
   2025     NoSpaceLeft,
   2026     ReadOnlyFileSystem,
   2027     NotDir,
   2028     NameTooLong,
   2029     InvalidUtf8,
   2030     BadPathName,
   2031 } || UnexpectedError;
   2032 
   2033 /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
   2034 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
   2035 /// one; the latter case is known as a dangling link.
   2036 /// If `sym_link_path` exists, it will not be overwritten.
   2037 /// See also `symlinkZ.
   2038 pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
   2039     if (builtin.os.tag == .windows) {
   2040         @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
   2041     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2042         return symlinkat(target_path, wasi.AT.FDCWD, sym_link_path);
   2043     }
   2044     const target_path_c = try toPosixPath(target_path);
   2045     const sym_link_path_c = try toPosixPath(sym_link_path);
   2046     return symlinkZ(&target_path_c, &sym_link_path_c);
   2047 }
   2048 
   2049 /// This is the same as `symlink` except the parameters are null-terminated pointers.
   2050 /// See also `symlink`.
   2051 pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void {
   2052     if (builtin.os.tag == .windows) {
   2053         @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
   2054     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2055         return symlinkatZ(target_path, fs.cwd().fd, sym_link_path);
   2056     }
   2057     switch (errno(system.symlink(target_path, sym_link_path))) {
   2058         .SUCCESS => return,
   2059         .FAULT => unreachable,
   2060         .INVAL => unreachable,
   2061         .ACCES => return error.AccessDenied,
   2062         .PERM => return error.AccessDenied,
   2063         .DQUOT => return error.DiskQuota,
   2064         .EXIST => return error.PathAlreadyExists,
   2065         .IO => return error.FileSystem,
   2066         .LOOP => return error.SymLinkLoop,
   2067         .NAMETOOLONG => return error.NameTooLong,
   2068         .NOENT => return error.FileNotFound,
   2069         .NOTDIR => return error.NotDir,
   2070         .NOMEM => return error.SystemResources,
   2071         .NOSPC => return error.NoSpaceLeft,
   2072         .ROFS => return error.ReadOnlyFileSystem,
   2073         else => |err| return unexpectedErrno(err),
   2074     }
   2075 }
   2076 
   2077 /// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string
   2078 /// `target_path` **relative** to `newdirfd` directory handle.
   2079 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
   2080 /// one; the latter case is known as a dangling link.
   2081 /// If `sym_link_path` exists, it will not be overwritten.
   2082 /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
   2083 pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
   2084     if (builtin.os.tag == .windows) {
   2085         @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
   2086     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2087         return symlinkatWasi(target_path, newdirfd, sym_link_path);
   2088     }
   2089     const target_path_c = try toPosixPath(target_path);
   2090     const sym_link_path_c = try toPosixPath(sym_link_path);
   2091     return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
   2092 }
   2093 
   2094 /// WASI-only. The same as `symlinkat` but targeting WASI.
   2095 /// See also `symlinkat`.
   2096 pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
   2097     switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
   2098         .SUCCESS => {},
   2099         .FAULT => unreachable,
   2100         .INVAL => unreachable,
   2101         .BADF => unreachable,
   2102         .ACCES => return error.AccessDenied,
   2103         .PERM => return error.AccessDenied,
   2104         .DQUOT => return error.DiskQuota,
   2105         .EXIST => return error.PathAlreadyExists,
   2106         .IO => return error.FileSystem,
   2107         .LOOP => return error.SymLinkLoop,
   2108         .NAMETOOLONG => return error.NameTooLong,
   2109         .NOENT => return error.FileNotFound,
   2110         .NOTDIR => return error.NotDir,
   2111         .NOMEM => return error.SystemResources,
   2112         .NOSPC => return error.NoSpaceLeft,
   2113         .ROFS => return error.ReadOnlyFileSystem,
   2114         .NOTCAPABLE => return error.AccessDenied,
   2115         else => |err| return unexpectedErrno(err),
   2116     }
   2117 }
   2118 
   2119 /// The same as `symlinkat` except the parameters are null-terminated pointers.
   2120 /// See also `symlinkat`.
   2121 pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
   2122     if (builtin.os.tag == .windows) {
   2123         @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
   2124     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2125         return symlinkat(mem.sliceTo(target_path, 0), newdirfd, mem.sliceTo(sym_link_path, 0));
   2126     }
   2127     switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
   2128         .SUCCESS => return,
   2129         .FAULT => unreachable,
   2130         .INVAL => unreachable,
   2131         .ACCES => return error.AccessDenied,
   2132         .PERM => return error.AccessDenied,
   2133         .DQUOT => return error.DiskQuota,
   2134         .EXIST => return error.PathAlreadyExists,
   2135         .IO => return error.FileSystem,
   2136         .LOOP => return error.SymLinkLoop,
   2137         .NAMETOOLONG => return error.NameTooLong,
   2138         .NOENT => return error.FileNotFound,
   2139         .NOTDIR => return error.NotDir,
   2140         .NOMEM => return error.SystemResources,
   2141         .NOSPC => return error.NoSpaceLeft,
   2142         .ROFS => return error.ReadOnlyFileSystem,
   2143         else => |err| return unexpectedErrno(err),
   2144     }
   2145 }
   2146 
   2147 pub const LinkError = UnexpectedError || error{
   2148     AccessDenied,
   2149     DiskQuota,
   2150     PathAlreadyExists,
   2151     FileSystem,
   2152     SymLinkLoop,
   2153     LinkQuotaExceeded,
   2154     NameTooLong,
   2155     FileNotFound,
   2156     SystemResources,
   2157     NoSpaceLeft,
   2158     ReadOnlyFileSystem,
   2159     NotSameFileSystem,
   2160 };
   2161 
   2162 pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void {
   2163     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2164         return link(mem.sliceTo(oldpath, 0), mem.sliceTo(newpath, 0), flags);
   2165     }
   2166     switch (errno(system.link(oldpath, newpath, flags))) {
   2167         .SUCCESS => return,
   2168         .ACCES => return error.AccessDenied,
   2169         .DQUOT => return error.DiskQuota,
   2170         .EXIST => return error.PathAlreadyExists,
   2171         .FAULT => unreachable,
   2172         .IO => return error.FileSystem,
   2173         .LOOP => return error.SymLinkLoop,
   2174         .MLINK => return error.LinkQuotaExceeded,
   2175         .NAMETOOLONG => return error.NameTooLong,
   2176         .NOENT => return error.FileNotFound,
   2177         .NOMEM => return error.SystemResources,
   2178         .NOSPC => return error.NoSpaceLeft,
   2179         .PERM => return error.AccessDenied,
   2180         .ROFS => return error.ReadOnlyFileSystem,
   2181         .XDEV => return error.NotSameFileSystem,
   2182         .INVAL => unreachable,
   2183         else => |err| return unexpectedErrno(err),
   2184     }
   2185 }
   2186 
   2187 pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void {
   2188     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2189         return linkat(wasi.AT.FDCWD, oldpath, wasi.AT.FDCWD, newpath, flags) catch |err| switch (err) {
   2190             error.NotDir => unreachable, // link() does not support directories
   2191             else => |e| return e,
   2192         };
   2193     }
   2194     const old = try toPosixPath(oldpath);
   2195     const new = try toPosixPath(newpath);
   2196     return try linkZ(&old, &new, flags);
   2197 }
   2198 
   2199 pub const LinkatError = LinkError || error{NotDir};
   2200 
   2201 pub fn linkatZ(
   2202     olddir: fd_t,
   2203     oldpath: [*:0]const u8,
   2204     newdir: fd_t,
   2205     newpath: [*:0]const u8,
   2206     flags: i32,
   2207 ) LinkatError!void {
   2208     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2209         return linkat(olddir, mem.sliceTo(oldpath, 0), newdir, mem.sliceTo(newpath, 0), flags);
   2210     }
   2211     switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) {
   2212         .SUCCESS => return,
   2213         .ACCES => return error.AccessDenied,
   2214         .DQUOT => return error.DiskQuota,
   2215         .EXIST => return error.PathAlreadyExists,
   2216         .FAULT => unreachable,
   2217         .IO => return error.FileSystem,
   2218         .LOOP => return error.SymLinkLoop,
   2219         .MLINK => return error.LinkQuotaExceeded,
   2220         .NAMETOOLONG => return error.NameTooLong,
   2221         .NOENT => return error.FileNotFound,
   2222         .NOMEM => return error.SystemResources,
   2223         .NOSPC => return error.NoSpaceLeft,
   2224         .NOTDIR => return error.NotDir,
   2225         .PERM => return error.AccessDenied,
   2226         .ROFS => return error.ReadOnlyFileSystem,
   2227         .XDEV => return error.NotSameFileSystem,
   2228         .INVAL => unreachable,
   2229         else => |err| return unexpectedErrno(err),
   2230     }
   2231 }
   2232 
   2233 pub fn linkat(
   2234     olddir: fd_t,
   2235     oldpath: []const u8,
   2236     newdir: fd_t,
   2237     newpath: []const u8,
   2238     flags: i32,
   2239 ) LinkatError!void {
   2240     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2241         const old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath };
   2242         const new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = newpath };
   2243         return linkatWasi(old, new, flags);
   2244     }
   2245     const old = try toPosixPath(oldpath);
   2246     const new = try toPosixPath(newpath);
   2247     return try linkatZ(olddir, &old, newdir, &new, flags);
   2248 }
   2249 
   2250 /// WASI-only. The same as `linkat` but targeting WASI.
   2251 /// See also `linkat`.
   2252 pub fn linkatWasi(old: RelativePathWasi, new: RelativePathWasi, flags: i32) LinkatError!void {
   2253     var old_flags: wasi.lookupflags_t = 0;
   2254     // TODO: Why is this not defined in wasi-libc?
   2255     if (flags & linux.AT.SYMLINK_FOLLOW != 0) old_flags |= wasi.LOOKUP_SYMLINK_FOLLOW;
   2256 
   2257     switch (wasi.path_link(old.dir_fd, old_flags, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) {
   2258         .SUCCESS => return,
   2259         .ACCES => return error.AccessDenied,
   2260         .DQUOT => return error.DiskQuota,
   2261         .EXIST => return error.PathAlreadyExists,
   2262         .FAULT => unreachable,
   2263         .IO => return error.FileSystem,
   2264         .LOOP => return error.SymLinkLoop,
   2265         .MLINK => return error.LinkQuotaExceeded,
   2266         .NAMETOOLONG => return error.NameTooLong,
   2267         .NOENT => return error.FileNotFound,
   2268         .NOMEM => return error.SystemResources,
   2269         .NOSPC => return error.NoSpaceLeft,
   2270         .NOTDIR => return error.NotDir,
   2271         .PERM => return error.AccessDenied,
   2272         .ROFS => return error.ReadOnlyFileSystem,
   2273         .XDEV => return error.NotSameFileSystem,
   2274         .INVAL => unreachable,
   2275         else => |err| return unexpectedErrno(err),
   2276     }
   2277 }
   2278 
   2279 pub const UnlinkError = error{
   2280     FileNotFound,
   2281 
   2282     /// In WASI, this error may occur when the file descriptor does
   2283     /// not hold the required rights to unlink a resource by path relative to it.
   2284     AccessDenied,
   2285     FileBusy,
   2286     FileSystem,
   2287     IsDir,
   2288     SymLinkLoop,
   2289     NameTooLong,
   2290     NotDir,
   2291     SystemResources,
   2292     ReadOnlyFileSystem,
   2293 
   2294     /// On Windows, file paths must be valid Unicode.
   2295     InvalidUtf8,
   2296 
   2297     /// On Windows, file paths cannot contain these characters:
   2298     /// '/', '*', '?', '"', '<', '>', '|'
   2299     BadPathName,
   2300 } || UnexpectedError;
   2301 
   2302 /// Delete a name and possibly the file it refers to.
   2303 /// See also `unlinkZ`.
   2304 pub fn unlink(file_path: []const u8) UnlinkError!void {
   2305     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2306         return unlinkat(wasi.AT.FDCWD, file_path, 0) catch |err| switch (err) {
   2307             error.DirNotEmpty => unreachable, // only occurs when targeting directories
   2308             else => |e| return e,
   2309         };
   2310     } else if (builtin.os.tag == .windows) {
   2311         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   2312         return unlinkW(file_path_w.span());
   2313     } else {
   2314         const file_path_c = try toPosixPath(file_path);
   2315         return unlinkZ(&file_path_c);
   2316     }
   2317 }
   2318 
   2319 /// Same as `unlink` except the parameter is a null terminated UTF8-encoded string.
   2320 pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
   2321     if (builtin.os.tag == .windows) {
   2322         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   2323         return unlinkW(file_path_w.span());
   2324     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2325         return unlink(mem.sliceTo(file_path, 0));
   2326     }
   2327     switch (errno(system.unlink(file_path))) {
   2328         .SUCCESS => return,
   2329         .ACCES => return error.AccessDenied,
   2330         .PERM => return error.AccessDenied,
   2331         .BUSY => return error.FileBusy,
   2332         .FAULT => unreachable,
   2333         .INVAL => unreachable,
   2334         .IO => return error.FileSystem,
   2335         .ISDIR => return error.IsDir,
   2336         .LOOP => return error.SymLinkLoop,
   2337         .NAMETOOLONG => return error.NameTooLong,
   2338         .NOENT => return error.FileNotFound,
   2339         .NOTDIR => return error.NotDir,
   2340         .NOMEM => return error.SystemResources,
   2341         .ROFS => return error.ReadOnlyFileSystem,
   2342         else => |err| return unexpectedErrno(err),
   2343     }
   2344 }
   2345 
   2346 /// Windows-only. Same as `unlink` except the parameter is null-terminated, WTF16 encoded.
   2347 pub fn unlinkW(file_path_w: []const u16) UnlinkError!void {
   2348     return windows.DeleteFile(file_path_w, .{ .dir = std.fs.cwd().fd });
   2349 }
   2350 
   2351 pub const UnlinkatError = UnlinkError || error{
   2352     /// When passing `AT.REMOVEDIR`, this error occurs when the named directory is not empty.
   2353     DirNotEmpty,
   2354 };
   2355 
   2356 /// Delete a file name and possibly the file it refers to, based on an open directory handle.
   2357 /// Asserts that the path parameter has no null bytes.
   2358 pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
   2359     if (builtin.os.tag == .windows) {
   2360         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   2361         return unlinkatW(dirfd, file_path_w.span(), flags);
   2362     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2363         return unlinkatWasi(dirfd, file_path, flags);
   2364     } else {
   2365         const file_path_c = try toPosixPath(file_path);
   2366         return unlinkatZ(dirfd, &file_path_c, flags);
   2367     }
   2368 }
   2369 
   2370 /// WASI-only. Same as `unlinkat` but targeting WASI.
   2371 /// See also `unlinkat`.
   2372 pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
   2373     const remove_dir = (flags & AT.REMOVEDIR) != 0;
   2374     const res = if (remove_dir)
   2375         wasi.path_remove_directory(dirfd, file_path.ptr, file_path.len)
   2376     else
   2377         wasi.path_unlink_file(dirfd, file_path.ptr, file_path.len);
   2378     switch (res) {
   2379         .SUCCESS => return,
   2380         .ACCES => return error.AccessDenied,
   2381         .PERM => return error.AccessDenied,
   2382         .BUSY => return error.FileBusy,
   2383         .FAULT => unreachable,
   2384         .IO => return error.FileSystem,
   2385         .ISDIR => return error.IsDir,
   2386         .LOOP => return error.SymLinkLoop,
   2387         .NAMETOOLONG => return error.NameTooLong,
   2388         .NOENT => return error.FileNotFound,
   2389         .NOTDIR => return error.NotDir,
   2390         .NOMEM => return error.SystemResources,
   2391         .ROFS => return error.ReadOnlyFileSystem,
   2392         .NOTEMPTY => return error.DirNotEmpty,
   2393         .NOTCAPABLE => return error.AccessDenied,
   2394 
   2395         .INVAL => unreachable, // invalid flags, or pathname has . as last component
   2396         .BADF => unreachable, // always a race condition
   2397 
   2398         else => |err| return unexpectedErrno(err),
   2399     }
   2400 }
   2401 
   2402 /// Same as `unlinkat` but `file_path` is a null-terminated string.
   2403 pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
   2404     if (builtin.os.tag == .windows) {
   2405         const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
   2406         return unlinkatW(dirfd, file_path_w.span(), flags);
   2407     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2408         return unlinkat(dirfd, mem.sliceTo(file_path_c, 0), flags);
   2409     }
   2410     switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
   2411         .SUCCESS => return,
   2412         .ACCES => return error.AccessDenied,
   2413         .PERM => return error.AccessDenied,
   2414         .BUSY => return error.FileBusy,
   2415         .FAULT => unreachable,
   2416         .IO => return error.FileSystem,
   2417         .ISDIR => return error.IsDir,
   2418         .LOOP => return error.SymLinkLoop,
   2419         .NAMETOOLONG => return error.NameTooLong,
   2420         .NOENT => return error.FileNotFound,
   2421         .NOTDIR => return error.NotDir,
   2422         .NOMEM => return error.SystemResources,
   2423         .ROFS => return error.ReadOnlyFileSystem,
   2424         .EXIST => return error.DirNotEmpty,
   2425         .NOTEMPTY => return error.DirNotEmpty,
   2426 
   2427         .INVAL => unreachable, // invalid flags, or pathname has . as last component
   2428         .BADF => unreachable, // always a race condition
   2429 
   2430         else => |err| return unexpectedErrno(err),
   2431     }
   2432 }
   2433 
   2434 /// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
   2435 pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
   2436     const remove_dir = (flags & AT.REMOVEDIR) != 0;
   2437     return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
   2438 }
   2439 
   2440 pub const RenameError = error{
   2441     /// In WASI, this error may occur when the file descriptor does
   2442     /// not hold the required rights to rename a resource by path relative to it.
   2443     ///
   2444     /// On Windows, this error may be returned instead of PathAlreadyExists when
   2445     /// renaming a directory over an existing directory.
   2446     AccessDenied,
   2447     FileBusy,
   2448     DiskQuota,
   2449     IsDir,
   2450     SymLinkLoop,
   2451     LinkQuotaExceeded,
   2452     NameTooLong,
   2453     FileNotFound,
   2454     NotDir,
   2455     SystemResources,
   2456     NoSpaceLeft,
   2457     PathAlreadyExists,
   2458     ReadOnlyFileSystem,
   2459     RenameAcrossMountPoints,
   2460     InvalidUtf8,
   2461     BadPathName,
   2462     NoDevice,
   2463     SharingViolation,
   2464     PipeBusy,
   2465 } || UnexpectedError;
   2466 
   2467 /// Change the name or location of a file.
   2468 pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
   2469     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2470         return renameat(wasi.AT.FDCWD, old_path, wasi.AT.FDCWD, new_path);
   2471     } else if (builtin.os.tag == .windows) {
   2472         const old_path_w = try windows.sliceToPrefixedFileW(old_path);
   2473         const new_path_w = try windows.sliceToPrefixedFileW(new_path);
   2474         return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
   2475     } else {
   2476         const old_path_c = try toPosixPath(old_path);
   2477         const new_path_c = try toPosixPath(new_path);
   2478         return renameZ(&old_path_c, &new_path_c);
   2479     }
   2480 }
   2481 
   2482 /// Same as `rename` except the parameters are null-terminated byte arrays.
   2483 pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void {
   2484     if (builtin.os.tag == .windows) {
   2485         const old_path_w = try windows.cStrToPrefixedFileW(old_path);
   2486         const new_path_w = try windows.cStrToPrefixedFileW(new_path);
   2487         return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
   2488     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2489         return rename(mem.sliceTo(old_path, 0), mem.sliceTo(new_path, 0));
   2490     }
   2491     switch (errno(system.rename(old_path, new_path))) {
   2492         .SUCCESS => return,
   2493         .ACCES => return error.AccessDenied,
   2494         .PERM => return error.AccessDenied,
   2495         .BUSY => return error.FileBusy,
   2496         .DQUOT => return error.DiskQuota,
   2497         .FAULT => unreachable,
   2498         .INVAL => unreachable,
   2499         .ISDIR => return error.IsDir,
   2500         .LOOP => return error.SymLinkLoop,
   2501         .MLINK => return error.LinkQuotaExceeded,
   2502         .NAMETOOLONG => return error.NameTooLong,
   2503         .NOENT => return error.FileNotFound,
   2504         .NOTDIR => return error.NotDir,
   2505         .NOMEM => return error.SystemResources,
   2506         .NOSPC => return error.NoSpaceLeft,
   2507         .EXIST => return error.PathAlreadyExists,
   2508         .NOTEMPTY => return error.PathAlreadyExists,
   2509         .ROFS => return error.ReadOnlyFileSystem,
   2510         .XDEV => return error.RenameAcrossMountPoints,
   2511         else => |err| return unexpectedErrno(err),
   2512     }
   2513 }
   2514 
   2515 /// Same as `rename` except the parameters are null-terminated UTF16LE encoded byte arrays.
   2516 /// Assumes target is Windows.
   2517 pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!void {
   2518     const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
   2519     return windows.MoveFileExW(old_path, new_path, flags);
   2520 }
   2521 
   2522 /// Change the name or location of a file based on an open directory handle.
   2523 pub fn renameat(
   2524     old_dir_fd: fd_t,
   2525     old_path: []const u8,
   2526     new_dir_fd: fd_t,
   2527     new_path: []const u8,
   2528 ) RenameError!void {
   2529     if (builtin.os.tag == .windows) {
   2530         const old_path_w = try windows.sliceToPrefixedFileW(old_path);
   2531         const new_path_w = try windows.sliceToPrefixedFileW(new_path);
   2532         return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
   2533     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2534         const old: RelativePathWasi = .{ .dir_fd = old_dir_fd, .relative_path = old_path };
   2535         const new: RelativePathWasi = .{ .dir_fd = new_dir_fd, .relative_path = new_path };
   2536         return renameatWasi(old, new);
   2537     } else {
   2538         const old_path_c = try toPosixPath(old_path);
   2539         const new_path_c = try toPosixPath(new_path);
   2540         return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c);
   2541     }
   2542 }
   2543 
   2544 /// WASI-only. Same as `renameat` expect targeting WASI.
   2545 /// See also `renameat`.
   2546 pub fn renameatWasi(old: RelativePathWasi, new: RelativePathWasi) RenameError!void {
   2547     switch (wasi.path_rename(old.dir_fd, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) {
   2548         .SUCCESS => return,
   2549         .ACCES => return error.AccessDenied,
   2550         .PERM => return error.AccessDenied,
   2551         .BUSY => return error.FileBusy,
   2552         .DQUOT => return error.DiskQuota,
   2553         .FAULT => unreachable,
   2554         .INVAL => unreachable,
   2555         .ISDIR => return error.IsDir,
   2556         .LOOP => return error.SymLinkLoop,
   2557         .MLINK => return error.LinkQuotaExceeded,
   2558         .NAMETOOLONG => return error.NameTooLong,
   2559         .NOENT => return error.FileNotFound,
   2560         .NOTDIR => return error.NotDir,
   2561         .NOMEM => return error.SystemResources,
   2562         .NOSPC => return error.NoSpaceLeft,
   2563         .EXIST => return error.PathAlreadyExists,
   2564         .NOTEMPTY => return error.PathAlreadyExists,
   2565         .ROFS => return error.ReadOnlyFileSystem,
   2566         .XDEV => return error.RenameAcrossMountPoints,
   2567         .NOTCAPABLE => return error.AccessDenied,
   2568         else => |err| return unexpectedErrno(err),
   2569     }
   2570 }
   2571 
   2572 /// Same as `renameat` except the parameters are null-terminated byte arrays.
   2573 pub fn renameatZ(
   2574     old_dir_fd: fd_t,
   2575     old_path: [*:0]const u8,
   2576     new_dir_fd: fd_t,
   2577     new_path: [*:0]const u8,
   2578 ) RenameError!void {
   2579     if (builtin.os.tag == .windows) {
   2580         const old_path_w = try windows.cStrToPrefixedFileW(old_path);
   2581         const new_path_w = try windows.cStrToPrefixedFileW(new_path);
   2582         return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
   2583     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2584         return renameat(old_dir_fd, mem.sliceTo(old_path, 0), new_dir_fd, mem.sliceTo(new_path, 0));
   2585     }
   2586 
   2587     switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) {
   2588         .SUCCESS => return,
   2589         .ACCES => return error.AccessDenied,
   2590         .PERM => return error.AccessDenied,
   2591         .BUSY => return error.FileBusy,
   2592         .DQUOT => return error.DiskQuota,
   2593         .FAULT => unreachable,
   2594         .INVAL => unreachable,
   2595         .ISDIR => return error.IsDir,
   2596         .LOOP => return error.SymLinkLoop,
   2597         .MLINK => return error.LinkQuotaExceeded,
   2598         .NAMETOOLONG => return error.NameTooLong,
   2599         .NOENT => return error.FileNotFound,
   2600         .NOTDIR => return error.NotDir,
   2601         .NOMEM => return error.SystemResources,
   2602         .NOSPC => return error.NoSpaceLeft,
   2603         .EXIST => return error.PathAlreadyExists,
   2604         .NOTEMPTY => return error.PathAlreadyExists,
   2605         .ROFS => return error.ReadOnlyFileSystem,
   2606         .XDEV => return error.RenameAcrossMountPoints,
   2607         else => |err| return unexpectedErrno(err),
   2608     }
   2609 }
   2610 
   2611 /// Same as `renameat` but Windows-only and the path parameters are
   2612 /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
   2613 pub fn renameatW(
   2614     old_dir_fd: fd_t,
   2615     old_path_w: []const u16,
   2616     new_dir_fd: fd_t,
   2617     new_path_w: []const u16,
   2618     ReplaceIfExists: windows.BOOLEAN,
   2619 ) RenameError!void {
   2620     const src_fd = windows.OpenFile(old_path_w, .{
   2621         .dir = old_dir_fd,
   2622         .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE,
   2623         .creation = windows.FILE_OPEN,
   2624         .io_mode = .blocking,
   2625         .filter = .any, // This function is supposed to rename both files and directories.
   2626         .follow_symlinks = false,
   2627     }) catch |err| switch (err) {
   2628         error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
   2629         else => |e| return e,
   2630     };
   2631     defer windows.CloseHandle(src_fd);
   2632 
   2633     const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1);
   2634     var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined;
   2635     const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2;
   2636     if (struct_len > struct_buf_len) return error.NameTooLong;
   2637 
   2638     const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf);
   2639 
   2640     rename_info.* = .{
   2641         .ReplaceIfExists = ReplaceIfExists,
   2642         .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
   2643         .FileNameLength = @intCast(u32, new_path_w.len * 2), // already checked error.NameTooLong
   2644         .FileName = undefined,
   2645     };
   2646     std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w);
   2647 
   2648     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   2649 
   2650     const rc = windows.ntdll.NtSetInformationFile(
   2651         src_fd,
   2652         &io_status_block,
   2653         rename_info,
   2654         @intCast(u32, struct_len), // already checked for error.NameTooLong
   2655         .FileRenameInformation,
   2656     );
   2657 
   2658     switch (rc) {
   2659         .SUCCESS => return,
   2660         .INVALID_HANDLE => unreachable,
   2661         .INVALID_PARAMETER => unreachable,
   2662         .OBJECT_PATH_SYNTAX_BAD => unreachable,
   2663         .ACCESS_DENIED => return error.AccessDenied,
   2664         .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   2665         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   2666         .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,
   2667         else => return windows.unexpectedStatus(rc),
   2668     }
   2669 }
   2670 
   2671 pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
   2672     if (builtin.os.tag == .windows) {
   2673         const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
   2674         return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
   2675     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2676         return mkdiratWasi(dir_fd, sub_dir_path, mode);
   2677     } else {
   2678         const sub_dir_path_c = try toPosixPath(sub_dir_path);
   2679         return mkdiratZ(dir_fd, &sub_dir_path_c, mode);
   2680     }
   2681 }
   2682 
   2683 pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
   2684     _ = mode;
   2685     switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) {
   2686         .SUCCESS => return,
   2687         .ACCES => return error.AccessDenied,
   2688         .BADF => unreachable,
   2689         .PERM => return error.AccessDenied,
   2690         .DQUOT => return error.DiskQuota,
   2691         .EXIST => return error.PathAlreadyExists,
   2692         .FAULT => unreachable,
   2693         .LOOP => return error.SymLinkLoop,
   2694         .MLINK => return error.LinkQuotaExceeded,
   2695         .NAMETOOLONG => return error.NameTooLong,
   2696         .NOENT => return error.FileNotFound,
   2697         .NOMEM => return error.SystemResources,
   2698         .NOSPC => return error.NoSpaceLeft,
   2699         .NOTDIR => return error.NotDir,
   2700         .ROFS => return error.ReadOnlyFileSystem,
   2701         .NOTCAPABLE => return error.AccessDenied,
   2702         else => |err| return unexpectedErrno(err),
   2703     }
   2704 }
   2705 
   2706 pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
   2707     if (builtin.os.tag == .windows) {
   2708         const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path);
   2709         return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);
   2710     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2711         return mkdirat(dir_fd, mem.sliceTo(sub_dir_path, 0), mode);
   2712     }
   2713     switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) {
   2714         .SUCCESS => return,
   2715         .ACCES => return error.AccessDenied,
   2716         .BADF => unreachable,
   2717         .PERM => return error.AccessDenied,
   2718         .DQUOT => return error.DiskQuota,
   2719         .EXIST => return error.PathAlreadyExists,
   2720         .FAULT => unreachable,
   2721         .LOOP => return error.SymLinkLoop,
   2722         .MLINK => return error.LinkQuotaExceeded,
   2723         .NAMETOOLONG => return error.NameTooLong,
   2724         .NOENT => return error.FileNotFound,
   2725         .NOMEM => return error.SystemResources,
   2726         .NOSPC => return error.NoSpaceLeft,
   2727         .NOTDIR => return error.NotDir,
   2728         .ROFS => return error.ReadOnlyFileSystem,
   2729         // dragonfly: when dir_fd is unlinked from filesystem
   2730         .NOTCONN => return error.FileNotFound,
   2731         else => |err| return unexpectedErrno(err),
   2732     }
   2733 }
   2734 
   2735 pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
   2736     _ = mode;
   2737     const sub_dir_handle = windows.OpenFile(sub_path_w, .{
   2738         .dir = dir_fd,
   2739         .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
   2740         .creation = windows.FILE_CREATE,
   2741         .io_mode = .blocking,
   2742         .filter = .dir_only,
   2743     }) catch |err| switch (err) {
   2744         error.IsDir => unreachable,
   2745         error.PipeBusy => unreachable,
   2746         error.WouldBlock => unreachable,
   2747         else => |e| return e,
   2748     };
   2749     windows.CloseHandle(sub_dir_handle);
   2750 }
   2751 
   2752 pub const MakeDirError = error{
   2753     /// In WASI, this error may occur when the file descriptor does
   2754     /// not hold the required rights to create a new directory relative to it.
   2755     AccessDenied,
   2756     DiskQuota,
   2757     PathAlreadyExists,
   2758     SymLinkLoop,
   2759     LinkQuotaExceeded,
   2760     NameTooLong,
   2761     FileNotFound,
   2762     SystemResources,
   2763     NoSpaceLeft,
   2764     NotDir,
   2765     ReadOnlyFileSystem,
   2766     InvalidUtf8,
   2767     BadPathName,
   2768     NoDevice,
   2769 } || UnexpectedError;
   2770 
   2771 /// Create a directory.
   2772 /// `mode` is ignored on Windows and WASI.
   2773 pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
   2774     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2775         return mkdirat(wasi.AT.FDCWD, dir_path, mode);
   2776     } else if (builtin.os.tag == .windows) {
   2777         const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
   2778         return mkdirW(dir_path_w.span(), mode);
   2779     } else {
   2780         const dir_path_c = try toPosixPath(dir_path);
   2781         return mkdirZ(&dir_path_c, mode);
   2782     }
   2783 }
   2784 
   2785 /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
   2786 pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
   2787     if (builtin.os.tag == .windows) {
   2788         const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
   2789         return mkdirW(dir_path_w.span(), mode);
   2790     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2791         return mkdir(mem.sliceTo(dir_path, 0), mode);
   2792     }
   2793     switch (errno(system.mkdir(dir_path, mode))) {
   2794         .SUCCESS => return,
   2795         .ACCES => return error.AccessDenied,
   2796         .PERM => return error.AccessDenied,
   2797         .DQUOT => return error.DiskQuota,
   2798         .EXIST => return error.PathAlreadyExists,
   2799         .FAULT => unreachable,
   2800         .LOOP => return error.SymLinkLoop,
   2801         .MLINK => return error.LinkQuotaExceeded,
   2802         .NAMETOOLONG => return error.NameTooLong,
   2803         .NOENT => return error.FileNotFound,
   2804         .NOMEM => return error.SystemResources,
   2805         .NOSPC => return error.NoSpaceLeft,
   2806         .NOTDIR => return error.NotDir,
   2807         .ROFS => return error.ReadOnlyFileSystem,
   2808         else => |err| return unexpectedErrno(err),
   2809     }
   2810 }
   2811 
   2812 /// Windows-only. Same as `mkdir` but the parameters is  WTF16 encoded.
   2813 pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
   2814     _ = mode;
   2815     const sub_dir_handle = windows.OpenFile(dir_path_w, .{
   2816         .dir = std.fs.cwd().fd,
   2817         .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
   2818         .creation = windows.FILE_CREATE,
   2819         .io_mode = .blocking,
   2820         .filter = .dir_only,
   2821     }) catch |err| switch (err) {
   2822         error.IsDir => unreachable,
   2823         error.PipeBusy => unreachable,
   2824         error.WouldBlock => unreachable,
   2825         else => |e| return e,
   2826     };
   2827     windows.CloseHandle(sub_dir_handle);
   2828 }
   2829 
   2830 pub const DeleteDirError = error{
   2831     AccessDenied,
   2832     FileBusy,
   2833     SymLinkLoop,
   2834     NameTooLong,
   2835     FileNotFound,
   2836     SystemResources,
   2837     NotDir,
   2838     DirNotEmpty,
   2839     ReadOnlyFileSystem,
   2840     InvalidUtf8,
   2841     BadPathName,
   2842 } || UnexpectedError;
   2843 
   2844 /// Deletes an empty directory.
   2845 pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
   2846     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2847         return unlinkat(wasi.AT.FDCWD, dir_path, AT.REMOVEDIR) catch |err| switch (err) {
   2848             error.FileSystem => unreachable, // only occurs when targeting files
   2849             error.IsDir => unreachable, // only occurs when targeting files
   2850             else => |e| return e,
   2851         };
   2852     } else if (builtin.os.tag == .windows) {
   2853         const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
   2854         return rmdirW(dir_path_w.span());
   2855     } else {
   2856         const dir_path_c = try toPosixPath(dir_path);
   2857         return rmdirZ(&dir_path_c);
   2858     }
   2859 }
   2860 
   2861 /// Same as `rmdir` except the parameter is null-terminated.
   2862 pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
   2863     if (builtin.os.tag == .windows) {
   2864         const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
   2865         return rmdirW(dir_path_w.span());
   2866     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2867         return rmdir(mem.sliceTo(dir_path, 0));
   2868     }
   2869     switch (errno(system.rmdir(dir_path))) {
   2870         .SUCCESS => return,
   2871         .ACCES => return error.AccessDenied,
   2872         .PERM => return error.AccessDenied,
   2873         .BUSY => return error.FileBusy,
   2874         .FAULT => unreachable,
   2875         .INVAL => return error.BadPathName,
   2876         .LOOP => return error.SymLinkLoop,
   2877         .NAMETOOLONG => return error.NameTooLong,
   2878         .NOENT => return error.FileNotFound,
   2879         .NOMEM => return error.SystemResources,
   2880         .NOTDIR => return error.NotDir,
   2881         .EXIST => return error.DirNotEmpty,
   2882         .NOTEMPTY => return error.DirNotEmpty,
   2883         .ROFS => return error.ReadOnlyFileSystem,
   2884         else => |err| return unexpectedErrno(err),
   2885     }
   2886 }
   2887 
   2888 /// Windows-only. Same as `rmdir` except the parameter is WTF16 encoded.
   2889 pub fn rmdirW(dir_path_w: []const u16) DeleteDirError!void {
   2890     return windows.DeleteFile(dir_path_w, .{ .dir = std.fs.cwd().fd, .remove_dir = true }) catch |err| switch (err) {
   2891         error.IsDir => unreachable,
   2892         else => |e| return e,
   2893     };
   2894 }
   2895 
   2896 pub const ChangeCurDirError = error{
   2897     AccessDenied,
   2898     FileSystem,
   2899     SymLinkLoop,
   2900     NameTooLong,
   2901     FileNotFound,
   2902     SystemResources,
   2903     NotDir,
   2904     BadPathName,
   2905 
   2906     /// On Windows, file paths must be valid Unicode.
   2907     InvalidUtf8,
   2908 } || UnexpectedError;
   2909 
   2910 /// Changes the current working directory of the calling process.
   2911 /// `dir_path` is recommended to be a UTF-8 encoded string.
   2912 pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
   2913     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2914         @compileError("WASI does not support os.chdir");
   2915     } else if (builtin.os.tag == .windows) {
   2916         var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
   2917         const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path);
   2918         if (len > utf16_dir_path.len) return error.NameTooLong;
   2919         return chdirW(utf16_dir_path[0..len]);
   2920     } else {
   2921         const dir_path_c = try toPosixPath(dir_path);
   2922         return chdirZ(&dir_path_c);
   2923     }
   2924 }
   2925 
   2926 /// Same as `chdir` except the parameter is null-terminated.
   2927 pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
   2928     if (builtin.os.tag == .windows) {
   2929         var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
   2930         const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path);
   2931         if (len > utf16_dir_path.len) return error.NameTooLong;
   2932         return chdirW(utf16_dir_path[0..len]);
   2933     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   2934         return chdir(mem.sliceTo(dir_path, 0));
   2935     }
   2936     switch (errno(system.chdir(dir_path))) {
   2937         .SUCCESS => return,
   2938         .ACCES => return error.AccessDenied,
   2939         .FAULT => unreachable,
   2940         .IO => return error.FileSystem,
   2941         .LOOP => return error.SymLinkLoop,
   2942         .NAMETOOLONG => return error.NameTooLong,
   2943         .NOENT => return error.FileNotFound,
   2944         .NOMEM => return error.SystemResources,
   2945         .NOTDIR => return error.NotDir,
   2946         else => |err| return unexpectedErrno(err),
   2947     }
   2948 }
   2949 
   2950 /// Windows-only. Same as `chdir` except the paramter is WTF16 encoded.
   2951 pub fn chdirW(dir_path: []const u16) ChangeCurDirError!void {
   2952     windows.SetCurrentDirectory(dir_path) catch |err| switch (err) {
   2953         error.NoDevice => return error.FileSystem,
   2954         else => |e| return e,
   2955     };
   2956 }
   2957 
   2958 pub const FchdirError = error{
   2959     AccessDenied,
   2960     NotDir,
   2961     FileSystem,
   2962 } || UnexpectedError;
   2963 
   2964 pub fn fchdir(dirfd: fd_t) FchdirError!void {
   2965     while (true) {
   2966         switch (errno(system.fchdir(dirfd))) {
   2967             .SUCCESS => return,
   2968             .ACCES => return error.AccessDenied,
   2969             .BADF => unreachable,
   2970             .NOTDIR => return error.NotDir,
   2971             .INTR => continue,
   2972             .IO => return error.FileSystem,
   2973             else => |err| return unexpectedErrno(err),
   2974         }
   2975     }
   2976 }
   2977 
   2978 pub const ReadLinkError = error{
   2979     /// In WASI, this error may occur when the file descriptor does
   2980     /// not hold the required rights to read value of a symbolic link relative to it.
   2981     AccessDenied,
   2982     FileSystem,
   2983     SymLinkLoop,
   2984     NameTooLong,
   2985     FileNotFound,
   2986     SystemResources,
   2987     NotLink,
   2988     NotDir,
   2989     InvalidUtf8,
   2990     BadPathName,
   2991     /// Windows-only. This error may occur if the opened reparse point is
   2992     /// of unsupported type.
   2993     UnsupportedReparsePointType,
   2994 } || UnexpectedError;
   2995 
   2996 /// Read value of a symbolic link.
   2997 /// The return value is a slice of `out_buffer` from index 0.
   2998 pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
   2999     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   3000         return readlinkat(wasi.AT.FDCWD, file_path, out_buffer);
   3001     } else if (builtin.os.tag == .windows) {
   3002         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   3003         return readlinkW(file_path_w.span(), out_buffer);
   3004     } else {
   3005         const file_path_c = try toPosixPath(file_path);
   3006         return readlinkZ(&file_path_c, out_buffer);
   3007     }
   3008 }
   3009 
   3010 /// Windows-only. Same as `readlink` except `file_path` is WTF16 encoded.
   3011 /// See also `readlinkZ`.
   3012 pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
   3013     return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
   3014 }
   3015 
   3016 /// Same as `readlink` except `file_path` is null-terminated.
   3017 pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
   3018     if (builtin.os.tag == .windows) {
   3019         const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);
   3020         return readlinkW(file_path_w.span(), out_buffer);
   3021     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   3022         return readlink(mem.sliceTo(file_path, 0), out_buffer);
   3023     }
   3024     const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
   3025     switch (errno(rc)) {
   3026         .SUCCESS => return out_buffer[0..@bitCast(usize, rc)],
   3027         .ACCES => return error.AccessDenied,
   3028         .FAULT => unreachable,
   3029         .INVAL => return error.NotLink,
   3030         .IO => return error.FileSystem,
   3031         .LOOP => return error.SymLinkLoop,
   3032         .NAMETOOLONG => return error.NameTooLong,
   3033         .NOENT => return error.FileNotFound,
   3034         .NOMEM => return error.SystemResources,
   3035         .NOTDIR => return error.NotDir,
   3036         else => |err| return unexpectedErrno(err),
   3037     }
   3038 }
   3039 
   3040 /// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
   3041 /// The return value is a slice of `out_buffer` from index 0.
   3042 /// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
   3043 pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
   3044     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   3045         return readlinkatWasi(dirfd, file_path, out_buffer);
   3046     }
   3047     if (builtin.os.tag == .windows) {
   3048         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   3049         return readlinkatW(dirfd, file_path_w.span(), out_buffer);
   3050     }
   3051     const file_path_c = try toPosixPath(file_path);
   3052     return readlinkatZ(dirfd, &file_path_c, out_buffer);
   3053 }
   3054 
   3055 /// WASI-only. Same as `readlinkat` but targets WASI.
   3056 /// See also `readlinkat`.
   3057 pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
   3058     var bufused: usize = undefined;
   3059     switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
   3060         .SUCCESS => return out_buffer[0..bufused],
   3061         .ACCES => return error.AccessDenied,
   3062         .FAULT => unreachable,
   3063         .INVAL => return error.NotLink,
   3064         .IO => return error.FileSystem,
   3065         .LOOP => return error.SymLinkLoop,
   3066         .NAMETOOLONG => return error.NameTooLong,
   3067         .NOENT => return error.FileNotFound,
   3068         .NOMEM => return error.SystemResources,
   3069         .NOTDIR => return error.NotDir,
   3070         .NOTCAPABLE => return error.AccessDenied,
   3071         else => |err| return unexpectedErrno(err),
   3072     }
   3073 }
   3074 
   3075 /// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
   3076 /// See also `readlinkat`.
   3077 pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
   3078     return windows.ReadLink(dirfd, file_path, out_buffer);
   3079 }
   3080 
   3081 /// Same as `readlinkat` except `file_path` is null-terminated.
   3082 /// See also `readlinkat`.
   3083 pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
   3084     if (builtin.os.tag == .windows) {
   3085         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   3086         return readlinkatW(dirfd, file_path_w.span(), out_buffer);
   3087     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   3088         return readlinkat(dirfd, mem.sliceTo(file_path, 0), out_buffer);
   3089     }
   3090     const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
   3091     switch (errno(rc)) {
   3092         .SUCCESS => return out_buffer[0..@bitCast(usize, rc)],
   3093         .ACCES => return error.AccessDenied,
   3094         .FAULT => unreachable,
   3095         .INVAL => return error.NotLink,
   3096         .IO => return error.FileSystem,
   3097         .LOOP => return error.SymLinkLoop,
   3098         .NAMETOOLONG => return error.NameTooLong,
   3099         .NOENT => return error.FileNotFound,
   3100         .NOMEM => return error.SystemResources,
   3101         .NOTDIR => return error.NotDir,
   3102         else => |err| return unexpectedErrno(err),
   3103     }
   3104 }
   3105 
   3106 pub const SetEidError = error{
   3107     InvalidUserId,
   3108     PermissionDenied,
   3109 } || UnexpectedError;
   3110 
   3111 pub const SetIdError = error{ResourceLimitReached} || SetEidError;
   3112 
   3113 pub fn setuid(uid: uid_t) SetIdError!void {
   3114     switch (errno(system.setuid(uid))) {
   3115         .SUCCESS => return,
   3116         .AGAIN => return error.ResourceLimitReached,
   3117         .INVAL => return error.InvalidUserId,
   3118         .PERM => return error.PermissionDenied,
   3119         else => |err| return unexpectedErrno(err),
   3120     }
   3121 }
   3122 
   3123 pub fn seteuid(uid: uid_t) SetEidError!void {
   3124     switch (errno(system.seteuid(uid))) {
   3125         .SUCCESS => return,
   3126         .INVAL => return error.InvalidUserId,
   3127         .PERM => return error.PermissionDenied,
   3128         else => |err| return unexpectedErrno(err),
   3129     }
   3130 }
   3131 
   3132 pub fn setreuid(ruid: uid_t, euid: uid_t) SetIdError!void {
   3133     switch (errno(system.setreuid(ruid, euid))) {
   3134         .SUCCESS => return,
   3135         .AGAIN => return error.ResourceLimitReached,
   3136         .INVAL => return error.InvalidUserId,
   3137         .PERM => return error.PermissionDenied,
   3138         else => |err| return unexpectedErrno(err),
   3139     }
   3140 }
   3141 
   3142 pub fn setgid(gid: gid_t) SetIdError!void {
   3143     switch (errno(system.setgid(gid))) {
   3144         .SUCCESS => return,
   3145         .AGAIN => return error.ResourceLimitReached,
   3146         .INVAL => return error.InvalidUserId,
   3147         .PERM => return error.PermissionDenied,
   3148         else => |err| return unexpectedErrno(err),
   3149     }
   3150 }
   3151 
   3152 pub fn setegid(uid: uid_t) SetEidError!void {
   3153     switch (errno(system.setegid(uid))) {
   3154         .SUCCESS => return,
   3155         .INVAL => return error.InvalidUserId,
   3156         .PERM => return error.PermissionDenied,
   3157         else => |err| return unexpectedErrno(err),
   3158     }
   3159 }
   3160 
   3161 pub fn setregid(rgid: gid_t, egid: gid_t) SetIdError!void {
   3162     switch (errno(system.setregid(rgid, egid))) {
   3163         .SUCCESS => return,
   3164         .AGAIN => return error.ResourceLimitReached,
   3165         .INVAL => return error.InvalidUserId,
   3166         .PERM => return error.PermissionDenied,
   3167         else => |err| return unexpectedErrno(err),
   3168     }
   3169 }
   3170 
   3171 /// Test whether a file descriptor refers to a terminal.
   3172 pub fn isatty(handle: fd_t) bool {
   3173     if (builtin.os.tag == .windows) {
   3174         if (isCygwinPty(handle))
   3175             return true;
   3176 
   3177         var out: windows.DWORD = undefined;
   3178         return windows.kernel32.GetConsoleMode(handle, &out) != 0;
   3179     }
   3180     if (builtin.link_libc) {
   3181         return system.isatty(handle) != 0;
   3182     }
   3183     if (builtin.os.tag == .wasi) {
   3184         var statbuf: fdstat_t = undefined;
   3185         const err = system.fd_fdstat_get(handle, &statbuf);
   3186         if (err != .SUCCESS) {
   3187             // errno = err;
   3188             return false;
   3189         }
   3190 
   3191         // A tty is a character device that we can't seek or tell on.
   3192         if (statbuf.fs_filetype != .CHARACTER_DEVICE or
   3193             (statbuf.fs_rights_base & (RIGHT.FD_SEEK | RIGHT.FD_TELL)) != 0)
   3194         {
   3195             // errno = ENOTTY;
   3196             return false;
   3197         }
   3198 
   3199         return true;
   3200     }
   3201     if (builtin.os.tag == .linux) {
   3202         while (true) {
   3203             var wsz: linux.winsize = undefined;
   3204             const fd = @bitCast(usize, @as(isize, handle));
   3205             const rc = linux.syscall3(.ioctl, fd, linux.T.IOCGWINSZ, @ptrToInt(&wsz));
   3206             switch (linux.getErrno(rc)) {
   3207                 .SUCCESS => return true,
   3208                 .INTR => continue,
   3209                 else => return false,
   3210             }
   3211         }
   3212     }
   3213     return system.isatty(handle) != 0;
   3214 }
   3215 
   3216 pub fn isCygwinPty(handle: fd_t) bool {
   3217     if (builtin.os.tag != .windows) return false;
   3218 
   3219     const size = @sizeOf(windows.FILE_NAME_INFO);
   3220     var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH);
   3221 
   3222     if (windows.kernel32.GetFileInformationByHandleEx(
   3223         handle,
   3224         windows.FileNameInfo,
   3225         @ptrCast(*anyopaque, &name_info_bytes),
   3226         name_info_bytes.len,
   3227     ) == 0) {
   3228         return false;
   3229     }
   3230 
   3231     const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]);
   3232     const name_bytes = name_info_bytes[size .. size + @as(usize, name_info.FileNameLength)];
   3233     const name_wide = mem.bytesAsSlice(u16, name_bytes);
   3234     return mem.indexOf(u16, name_wide, &[_]u16{ 'm', 's', 'y', 's', '-' }) != null or
   3235         mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null;
   3236 }
   3237 
   3238 pub const SocketError = error{
   3239     /// Permission to create a socket of the specified type and/or
   3240     /// pro‐tocol is denied.
   3241     PermissionDenied,
   3242 
   3243     /// The implementation does not support the specified address family.
   3244     AddressFamilyNotSupported,
   3245 
   3246     /// Unknown protocol, or protocol family not available.
   3247     ProtocolFamilyNotAvailable,
   3248 
   3249     /// The per-process limit on the number of open file descriptors has been reached.
   3250     ProcessFdQuotaExceeded,
   3251 
   3252     /// The system-wide limit on the total number of open files has been reached.
   3253     SystemFdQuotaExceeded,
   3254 
   3255     /// Insufficient memory is available. The socket cannot be created until sufficient
   3256     /// resources are freed.
   3257     SystemResources,
   3258 
   3259     /// The protocol type or the specified protocol is not supported within this domain.
   3260     ProtocolNotSupported,
   3261 
   3262     /// The socket type is not supported by the protocol.
   3263     SocketTypeNotSupported,
   3264 } || UnexpectedError;
   3265 
   3266 pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
   3267     if (builtin.os.tag == .windows) {
   3268         // NOTE: windows translates the SOCK.NONBLOCK/SOCK.CLOEXEC flags into
   3269         // windows-analagous operations
   3270         const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC);
   3271         const flags: u32 = if ((socket_type & SOCK.CLOEXEC) != 0)
   3272             windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT
   3273         else
   3274             0;
   3275         const rc = try windows.WSASocketW(
   3276             @bitCast(i32, domain),
   3277             @bitCast(i32, filtered_sock_type),
   3278             @bitCast(i32, protocol),
   3279             null,
   3280             0,
   3281             flags,
   3282         );
   3283         errdefer windows.closesocket(rc) catch unreachable;
   3284         if ((socket_type & SOCK.NONBLOCK) != 0) {
   3285             var mode: c_ulong = 1; // nonblocking
   3286             if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) {
   3287                 switch (windows.ws2_32.WSAGetLastError()) {
   3288                     // have not identified any error codes that should be handled yet
   3289                     else => unreachable,
   3290                 }
   3291             }
   3292         }
   3293         return rc;
   3294     }
   3295 
   3296     const have_sock_flags = comptime !builtin.target.isDarwin();
   3297     const filtered_sock_type = if (!have_sock_flags)
   3298         socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC)
   3299     else
   3300         socket_type;
   3301     const rc = system.socket(domain, filtered_sock_type, protocol);
   3302     switch (errno(rc)) {
   3303         .SUCCESS => {
   3304             const fd = @intCast(fd_t, rc);
   3305             if (!have_sock_flags) {
   3306                 try setSockFlags(fd, socket_type);
   3307             }
   3308             return fd;
   3309         },
   3310         .ACCES => return error.PermissionDenied,
   3311         .AFNOSUPPORT => return error.AddressFamilyNotSupported,
   3312         .INVAL => return error.ProtocolFamilyNotAvailable,
   3313         .MFILE => return error.ProcessFdQuotaExceeded,
   3314         .NFILE => return error.SystemFdQuotaExceeded,
   3315         .NOBUFS => return error.SystemResources,
   3316         .NOMEM => return error.SystemResources,
   3317         .PROTONOSUPPORT => return error.ProtocolNotSupported,
   3318         .PROTOTYPE => return error.SocketTypeNotSupported,
   3319         else => |err| return unexpectedErrno(err),
   3320     }
   3321 }
   3322 
   3323 pub const ShutdownError = error{
   3324     ConnectionAborted,
   3325 
   3326     /// Connection was reset by peer, application should close socket as it is no longer usable.
   3327     ConnectionResetByPeer,
   3328     BlockingOperationInProgress,
   3329 
   3330     /// The network subsystem has failed.
   3331     NetworkSubsystemFailed,
   3332 
   3333     /// The socket is not connected (connection-oriented sockets only).
   3334     SocketNotConnected,
   3335     SystemResources,
   3336 } || UnexpectedError;
   3337 
   3338 pub const ShutdownHow = enum { recv, send, both };
   3339 
   3340 /// Shutdown socket send/receive operations
   3341 pub fn shutdown(sock: socket_t, how: ShutdownHow) ShutdownError!void {
   3342     if (builtin.os.tag == .windows) {
   3343         const result = windows.ws2_32.shutdown(sock, switch (how) {
   3344             .recv => windows.ws2_32.SD_RECEIVE,
   3345             .send => windows.ws2_32.SD_SEND,
   3346             .both => windows.ws2_32.SD_BOTH,
   3347         });
   3348         if (0 != result) switch (windows.ws2_32.WSAGetLastError()) {
   3349             .WSAECONNABORTED => return error.ConnectionAborted,
   3350             .WSAECONNRESET => return error.ConnectionResetByPeer,
   3351             .WSAEINPROGRESS => return error.BlockingOperationInProgress,
   3352             .WSAEINVAL => unreachable,
   3353             .WSAENETDOWN => return error.NetworkSubsystemFailed,
   3354             .WSAENOTCONN => return error.SocketNotConnected,
   3355             .WSAENOTSOCK => unreachable,
   3356             .WSANOTINITIALISED => unreachable,
   3357             else => |err| return windows.unexpectedWSAError(err),
   3358         };
   3359     } else {
   3360         const rc = system.shutdown(sock, switch (how) {
   3361             .recv => SHUT.RD,
   3362             .send => SHUT.WR,
   3363             .both => SHUT.RDWR,
   3364         });
   3365         switch (errno(rc)) {
   3366             .SUCCESS => return,
   3367             .BADF => unreachable,
   3368             .INVAL => unreachable,
   3369             .NOTCONN => return error.SocketNotConnected,
   3370             .NOTSOCK => unreachable,
   3371             .NOBUFS => return error.SystemResources,
   3372             else => |err| return unexpectedErrno(err),
   3373         }
   3374     }
   3375 }
   3376 
   3377 pub fn closeSocket(sock: socket_t) void {
   3378     if (builtin.os.tag == .windows) {
   3379         windows.closesocket(sock) catch unreachable;
   3380     } else {
   3381         close(sock);
   3382     }
   3383 }
   3384 
   3385 pub const BindError = error{
   3386     /// The address is protected, and the user is not the superuser.
   3387     /// For UNIX domain sockets: Search permission is denied on  a  component
   3388     /// of  the  path  prefix.
   3389     AccessDenied,
   3390 
   3391     /// The given address is already in use, or in the case of Internet domain sockets,
   3392     /// The  port number was specified as zero in the socket
   3393     /// address structure, but, upon attempting to bind to  an  ephemeral  port,  it  was
   3394     /// determined  that  all  port  numbers in the ephemeral port range are currently in
   3395     /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
   3396     AddressInUse,
   3397 
   3398     /// A nonexistent interface was requested or the requested address was not local.
   3399     AddressNotAvailable,
   3400 
   3401     /// The address is not valid for the address family of socket.
   3402     AddressFamilyNotSupported,
   3403 
   3404     /// Too many symbolic links were encountered in resolving addr.
   3405     SymLinkLoop,
   3406 
   3407     /// addr is too long.
   3408     NameTooLong,
   3409 
   3410     /// A component in the directory prefix of the socket pathname does not exist.
   3411     FileNotFound,
   3412 
   3413     /// Insufficient kernel memory was available.
   3414     SystemResources,
   3415 
   3416     /// A component of the path prefix is not a directory.
   3417     NotDir,
   3418 
   3419     /// The socket inode would reside on a read-only filesystem.
   3420     ReadOnlyFileSystem,
   3421 
   3422     /// The network subsystem has failed.
   3423     NetworkSubsystemFailed,
   3424 
   3425     FileDescriptorNotASocket,
   3426 
   3427     AlreadyBound,
   3428 } || UnexpectedError;
   3429 
   3430 /// addr is `*const T` where T is one of the sockaddr
   3431 pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!void {
   3432     if (builtin.os.tag == .windows) {
   3433         const rc = windows.bind(sock, addr, len);
   3434         if (rc == windows.ws2_32.SOCKET_ERROR) {
   3435             switch (windows.ws2_32.WSAGetLastError()) {
   3436                 .WSANOTINITIALISED => unreachable, // not initialized WSA
   3437                 .WSAEACCES => return error.AccessDenied,
   3438                 .WSAEADDRINUSE => return error.AddressInUse,
   3439                 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
   3440                 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
   3441                 .WSAEFAULT => unreachable, // invalid pointers
   3442                 .WSAEINVAL => return error.AlreadyBound,
   3443                 .WSAENOBUFS => return error.SystemResources,
   3444                 .WSAENETDOWN => return error.NetworkSubsystemFailed,
   3445                 else => |err| return windows.unexpectedWSAError(err),
   3446             }
   3447             unreachable;
   3448         }
   3449         return;
   3450     } else {
   3451         const rc = system.bind(sock, addr, len);
   3452         switch (errno(rc)) {
   3453             .SUCCESS => return,
   3454             .ACCES => return error.AccessDenied,
   3455             .ADDRINUSE => return error.AddressInUse,
   3456             .BADF => unreachable, // always a race condition if this error is returned
   3457             .INVAL => unreachable, // invalid parameters
   3458             .NOTSOCK => unreachable, // invalid `sockfd`
   3459             .AFNOSUPPORT => return error.AddressFamilyNotSupported,
   3460             .ADDRNOTAVAIL => return error.AddressNotAvailable,
   3461             .FAULT => unreachable, // invalid `addr` pointer
   3462             .LOOP => return error.SymLinkLoop,
   3463             .NAMETOOLONG => return error.NameTooLong,
   3464             .NOENT => return error.FileNotFound,
   3465             .NOMEM => return error.SystemResources,
   3466             .NOTDIR => return error.NotDir,
   3467             .ROFS => return error.ReadOnlyFileSystem,
   3468             else => |err| return unexpectedErrno(err),
   3469         }
   3470     }
   3471     unreachable;
   3472 }
   3473 
   3474 pub const ListenError = error{
   3475     /// Another socket is already listening on the same port.
   3476     /// For Internet domain sockets, the  socket referred to by sockfd had not previously
   3477     /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
   3478     /// was determined that all port numbers in the ephemeral port range are currently in
   3479     /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
   3480     AddressInUse,
   3481 
   3482     /// The file descriptor sockfd does not refer to a socket.
   3483     FileDescriptorNotASocket,
   3484 
   3485     /// The socket is not of a type that supports the listen() operation.
   3486     OperationNotSupported,
   3487 
   3488     /// The network subsystem has failed.
   3489     NetworkSubsystemFailed,
   3490 
   3491     /// Ran out of system resources
   3492     /// On Windows it can either run out of socket descriptors or buffer space
   3493     SystemResources,
   3494 
   3495     /// Already connected
   3496     AlreadyConnected,
   3497 
   3498     /// Socket has not been bound yet
   3499     SocketNotBound,
   3500 } || UnexpectedError;
   3501 
   3502 pub fn listen(sock: socket_t, backlog: u31) ListenError!void {
   3503     if (builtin.os.tag == .windows) {
   3504         const rc = windows.listen(sock, backlog);
   3505         if (rc == windows.ws2_32.SOCKET_ERROR) {
   3506             switch (windows.ws2_32.WSAGetLastError()) {
   3507                 .WSANOTINITIALISED => unreachable, // not initialized WSA
   3508                 .WSAENETDOWN => return error.NetworkSubsystemFailed,
   3509                 .WSAEADDRINUSE => return error.AddressInUse,
   3510                 .WSAEISCONN => return error.AlreadyConnected,
   3511                 .WSAEINVAL => return error.SocketNotBound,
   3512                 .WSAEMFILE, .WSAENOBUFS => return error.SystemResources,
   3513                 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
   3514                 .WSAEOPNOTSUPP => return error.OperationNotSupported,
   3515                 .WSAEINPROGRESS => unreachable,
   3516                 else => |err| return windows.unexpectedWSAError(err),
   3517             }
   3518         }
   3519         return;
   3520     } else {
   3521         const rc = system.listen(sock, backlog);
   3522         switch (errno(rc)) {
   3523             .SUCCESS => return,
   3524             .ADDRINUSE => return error.AddressInUse,
   3525             .BADF => unreachable,
   3526             .NOTSOCK => return error.FileDescriptorNotASocket,
   3527             .OPNOTSUPP => return error.OperationNotSupported,
   3528             else => |err| return unexpectedErrno(err),
   3529         }
   3530     }
   3531 }
   3532 
   3533 pub const AcceptError = error{
   3534     ConnectionAborted,
   3535 
   3536     /// The file descriptor sockfd does not refer to a socket.
   3537     FileDescriptorNotASocket,
   3538 
   3539     /// The per-process limit on the number of open file descriptors has been reached.
   3540     ProcessFdQuotaExceeded,
   3541 
   3542     /// The system-wide limit on the total number of open files has been reached.
   3543     SystemFdQuotaExceeded,
   3544 
   3545     /// Not enough free memory.  This often means that the memory allocation  is  limited
   3546     /// by the socket buffer limits, not by the system memory.
   3547     SystemResources,
   3548 
   3549     /// Socket is not listening for new connections.
   3550     SocketNotListening,
   3551 
   3552     ProtocolFailure,
   3553 
   3554     /// Firewall rules forbid connection.
   3555     BlockedByFirewall,
   3556 
   3557     /// This error occurs when no global event loop is configured,
   3558     /// and accepting from the socket would block.
   3559     WouldBlock,
   3560 
   3561     /// An incoming connection was indicated, but was subsequently terminated by the
   3562     /// remote peer prior to accepting the call.
   3563     ConnectionResetByPeer,
   3564 
   3565     /// The network subsystem has failed.
   3566     NetworkSubsystemFailed,
   3567 
   3568     /// The referenced socket is not a type that supports connection-oriented service.
   3569     OperationNotSupported,
   3570 } || UnexpectedError;
   3571 
   3572 /// Accept a connection on a socket.
   3573 /// If `sockfd` is opened in non blocking mode, the function will
   3574 /// return error.WouldBlock when EAGAIN is received.
   3575 pub fn accept(
   3576     /// This argument is a socket that has been created with `socket`, bound to a local address
   3577     /// with `bind`, and is listening for connections after a `listen`.
   3578     sock: socket_t,
   3579     /// This argument is a pointer to a sockaddr structure.  This structure is filled in with  the
   3580     /// address  of  the  peer  socket, as known to the communications layer.  The exact format of the
   3581     /// address returned addr is determined by the socket's address  family  (see  `socket`  and  the
   3582     /// respective  protocol  man  pages).
   3583     addr: ?*sockaddr,
   3584     /// This argument is a value-result argument: the caller must initialize it to contain  the
   3585     /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
   3586     /// of the peer address.
   3587     ///
   3588     /// The returned address is truncated if the buffer provided is too small; in this  case,  `addr_size`
   3589     /// will return a value greater than was supplied to the call.
   3590     addr_size: ?*socklen_t,
   3591     /// The following values can be bitwise ORed in flags to obtain different behavior:
   3592     /// * `SOCK.NONBLOCK` - Set the `O.NONBLOCK` file status flag on the open file description (see `open`)
   3593     ///   referred  to by the new file descriptor.  Using this flag saves extra calls to `fcntl` to achieve
   3594     ///   the same result.
   3595     /// * `SOCK.CLOEXEC`  - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor.   See  the
   3596     ///   description  of the `O.CLOEXEC` flag in `open` for reasons why this may be useful.
   3597     flags: u32,
   3598 ) AcceptError!socket_t {
   3599     const have_accept4 = comptime !(builtin.target.isDarwin() or builtin.os.tag == .windows);
   3600     assert(0 == (flags & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC))); // Unsupported flag(s)
   3601 
   3602     const accepted_sock = while (true) {
   3603         const rc = if (have_accept4)
   3604             system.accept4(sock, addr, addr_size, flags)
   3605         else if (builtin.os.tag == .windows)
   3606             windows.accept(sock, addr, addr_size)
   3607         else
   3608             system.accept(sock, addr, addr_size);
   3609 
   3610         if (builtin.os.tag == .windows) {
   3611             if (rc == windows.ws2_32.INVALID_SOCKET) {
   3612                 switch (windows.ws2_32.WSAGetLastError()) {
   3613                     .WSANOTINITIALISED => unreachable, // not initialized WSA
   3614                     .WSAECONNRESET => return error.ConnectionResetByPeer,
   3615                     .WSAEFAULT => unreachable,
   3616                     .WSAEINVAL => return error.SocketNotListening,
   3617                     .WSAEMFILE => return error.ProcessFdQuotaExceeded,
   3618                     .WSAENETDOWN => return error.NetworkSubsystemFailed,
   3619                     .WSAENOBUFS => return error.FileDescriptorNotASocket,
   3620                     .WSAEOPNOTSUPP => return error.OperationNotSupported,
   3621                     .WSAEWOULDBLOCK => return error.WouldBlock,
   3622                     else => |err| return windows.unexpectedWSAError(err),
   3623                 }
   3624             } else {
   3625                 break rc;
   3626             }
   3627         } else {
   3628             switch (errno(rc)) {
   3629                 .SUCCESS => {
   3630                     break @intCast(socket_t, rc);
   3631                 },
   3632                 .INTR => continue,
   3633                 .AGAIN => return error.WouldBlock,
   3634                 .BADF => unreachable, // always a race condition
   3635                 .CONNABORTED => return error.ConnectionAborted,
   3636                 .FAULT => unreachable,
   3637                 .INVAL => return error.SocketNotListening,
   3638                 .NOTSOCK => unreachable,
   3639                 .MFILE => return error.ProcessFdQuotaExceeded,
   3640                 .NFILE => return error.SystemFdQuotaExceeded,
   3641                 .NOBUFS => return error.SystemResources,
   3642                 .NOMEM => return error.SystemResources,
   3643                 .OPNOTSUPP => unreachable,
   3644                 .PROTO => return error.ProtocolFailure,
   3645                 .PERM => return error.BlockedByFirewall,
   3646                 else => |err| return unexpectedErrno(err),
   3647             }
   3648         }
   3649     };
   3650 
   3651     if (!have_accept4) {
   3652         try setSockFlags(accepted_sock, flags);
   3653     }
   3654     return accepted_sock;
   3655 }
   3656 
   3657 pub const EpollCreateError = error{
   3658     /// The  per-user   limit   on   the   number   of   epoll   instances   imposed   by
   3659     /// /proc/sys/fs/epoll/max_user_instances  was encountered.  See epoll(7) for further
   3660     /// details.
   3661     /// Or, The per-process limit on the number of open file descriptors has been reached.
   3662     ProcessFdQuotaExceeded,
   3663 
   3664     /// The system-wide limit on the total number of open files has been reached.
   3665     SystemFdQuotaExceeded,
   3666 
   3667     /// There was insufficient memory to create the kernel object.
   3668     SystemResources,
   3669 } || UnexpectedError;
   3670 
   3671 pub fn epoll_create1(flags: u32) EpollCreateError!i32 {
   3672     const rc = system.epoll_create1(flags);
   3673     switch (errno(rc)) {
   3674         .SUCCESS => return @intCast(i32, rc),
   3675         else => |err| return unexpectedErrno(err),
   3676 
   3677         .INVAL => unreachable,
   3678         .MFILE => return error.ProcessFdQuotaExceeded,
   3679         .NFILE => return error.SystemFdQuotaExceeded,
   3680         .NOMEM => return error.SystemResources,
   3681     }
   3682 }
   3683 
   3684 pub const EpollCtlError = error{
   3685     /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is  already  registered
   3686     /// with this epoll instance.
   3687     FileDescriptorAlreadyPresentInSet,
   3688 
   3689     /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
   3690     /// circular loop of epoll instances monitoring one another.
   3691     OperationCausesCircularLoop,
   3692 
   3693     /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with  this  epoll
   3694     /// instance.
   3695     FileDescriptorNotRegistered,
   3696 
   3697     /// There was insufficient memory to handle the requested op control operation.
   3698     SystemResources,
   3699 
   3700     /// The  limit  imposed  by /proc/sys/fs/epoll/max_user_watches was encountered while
   3701     /// trying to register (EPOLL_CTL_ADD) a new file descriptor on  an  epoll  instance.
   3702     /// See epoll(7) for further details.
   3703     UserResourceLimitReached,
   3704 
   3705     /// The target file fd does not support epoll.  This error can occur if fd refers to,
   3706     /// for example, a regular file or a directory.
   3707     FileDescriptorIncompatibleWithEpoll,
   3708 } || UnexpectedError;
   3709 
   3710 pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*linux.epoll_event) EpollCtlError!void {
   3711     const rc = system.epoll_ctl(epfd, op, fd, event);
   3712     switch (errno(rc)) {
   3713         .SUCCESS => return,
   3714         else => |err| return unexpectedErrno(err),
   3715 
   3716         .BADF => unreachable, // always a race condition if this happens
   3717         .EXIST => return error.FileDescriptorAlreadyPresentInSet,
   3718         .INVAL => unreachable,
   3719         .LOOP => return error.OperationCausesCircularLoop,
   3720         .NOENT => return error.FileDescriptorNotRegistered,
   3721         .NOMEM => return error.SystemResources,
   3722         .NOSPC => return error.UserResourceLimitReached,
   3723         .PERM => return error.FileDescriptorIncompatibleWithEpoll,
   3724     }
   3725 }
   3726 
   3727 /// Waits for an I/O event on an epoll file descriptor.
   3728 /// Returns the number of file descriptors ready for the requested I/O,
   3729 /// or zero if no file descriptor became ready during the requested timeout milliseconds.
   3730 pub fn epoll_wait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
   3731     while (true) {
   3732         // TODO get rid of the @intCast
   3733         const rc = system.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout);
   3734         switch (errno(rc)) {
   3735             .SUCCESS => return @intCast(usize, rc),
   3736             .INTR => continue,
   3737             .BADF => unreachable,
   3738             .FAULT => unreachable,
   3739             .INVAL => unreachable,
   3740             else => unreachable,
   3741         }
   3742     }
   3743 }
   3744 
   3745 pub const EventFdError = error{
   3746     SystemResources,
   3747     ProcessFdQuotaExceeded,
   3748     SystemFdQuotaExceeded,
   3749 } || UnexpectedError;
   3750 
   3751 pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
   3752     const rc = system.eventfd(initval, flags);
   3753     switch (errno(rc)) {
   3754         .SUCCESS => return @intCast(i32, rc),
   3755         else => |err| return unexpectedErrno(err),
   3756 
   3757         .INVAL => unreachable, // invalid parameters
   3758         .MFILE => return error.ProcessFdQuotaExceeded,
   3759         .NFILE => return error.SystemFdQuotaExceeded,
   3760         .NODEV => return error.SystemResources,
   3761         .NOMEM => return error.SystemResources,
   3762     }
   3763 }
   3764 
   3765 pub const GetSockNameError = error{
   3766     /// Insufficient resources were available in the system to perform the operation.
   3767     SystemResources,
   3768 
   3769     /// The network subsystem has failed.
   3770     NetworkSubsystemFailed,
   3771 
   3772     /// Socket hasn't been bound yet
   3773     SocketNotBound,
   3774 
   3775     FileDescriptorNotASocket,
   3776 } || UnexpectedError;
   3777 
   3778 pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
   3779     if (builtin.os.tag == .windows) {
   3780         const rc = windows.getsockname(sock, addr, addrlen);
   3781         if (rc == windows.ws2_32.SOCKET_ERROR) {
   3782             switch (windows.ws2_32.WSAGetLastError()) {
   3783                 .WSANOTINITIALISED => unreachable,
   3784                 .WSAENETDOWN => return error.NetworkSubsystemFailed,
   3785                 .WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
   3786                 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
   3787                 .WSAEINVAL => return error.SocketNotBound,
   3788                 else => |err| return windows.unexpectedWSAError(err),
   3789             }
   3790         }
   3791         return;
   3792     } else {
   3793         const rc = system.getsockname(sock, addr, addrlen);
   3794         switch (errno(rc)) {
   3795             .SUCCESS => return,
   3796             else => |err| return unexpectedErrno(err),
   3797 
   3798             .BADF => unreachable, // always a race condition
   3799             .FAULT => unreachable,
   3800             .INVAL => unreachable, // invalid parameters
   3801             .NOTSOCK => return error.FileDescriptorNotASocket,
   3802             .NOBUFS => return error.SystemResources,
   3803         }
   3804     }
   3805 }
   3806 
   3807 pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
   3808     if (builtin.os.tag == .windows) {
   3809         const rc = windows.getpeername(sock, addr, addrlen);
   3810         if (rc == windows.ws2_32.SOCKET_ERROR) {
   3811             switch (windows.ws2_32.WSAGetLastError()) {
   3812                 .WSANOTINITIALISED => unreachable,
   3813                 .WSAENETDOWN => return error.NetworkSubsystemFailed,
   3814                 .WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
   3815                 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
   3816                 .WSAEINVAL => return error.SocketNotBound,
   3817                 else => |err| return windows.unexpectedWSAError(err),
   3818             }
   3819         }
   3820         return;
   3821     } else {
   3822         const rc = system.getpeername(sock, addr, addrlen);
   3823         switch (errno(rc)) {
   3824             .SUCCESS => return,
   3825             else => |err| return unexpectedErrno(err),
   3826 
   3827             .BADF => unreachable, // always a race condition
   3828             .FAULT => unreachable,
   3829             .INVAL => unreachable, // invalid parameters
   3830             .NOTSOCK => return error.FileDescriptorNotASocket,
   3831             .NOBUFS => return error.SystemResources,
   3832         }
   3833     }
   3834 }
   3835 
   3836 pub const ConnectError = error{
   3837     /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on  the  socket
   3838     /// file,  or  search  permission  is  denied  for  one of the directories in the path prefix.
   3839     /// or
   3840     /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled  or
   3841     /// the connection request failed because of a local firewall rule.
   3842     PermissionDenied,
   3843 
   3844     /// Local address is already in use.
   3845     AddressInUse,
   3846 
   3847     /// (Internet  domain  sockets)  The  socket  referred  to  by sockfd had not previously been bound to an
   3848     /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
   3849     /// in    the    ephemeral    port    range    are   currently   in   use.    See   the   discussion   of
   3850     /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
   3851     AddressNotAvailable,
   3852 
   3853     /// The passed address didn't have the correct address family in its sa_family field.
   3854     AddressFamilyNotSupported,
   3855 
   3856     /// Insufficient entries in the routing cache.
   3857     SystemResources,
   3858 
   3859     /// A connect() on a stream socket found no one listening on the remote address.
   3860     ConnectionRefused,
   3861 
   3862     /// Network is unreachable.
   3863     NetworkUnreachable,
   3864 
   3865     /// Timeout  while  attempting  connection.   The server may be too busy to accept new connections.  Note
   3866     /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
   3867     ConnectionTimedOut,
   3868 
   3869     /// This error occurs when no global event loop is configured,
   3870     /// and connecting to the socket would block.
   3871     WouldBlock,
   3872 
   3873     /// The given path for the unix socket does not exist.
   3874     FileNotFound,
   3875 
   3876     /// Connection was reset by peer before connect could complete.
   3877     ConnectionResetByPeer,
   3878 
   3879     /// Socket is non-blocking and already has a pending connection in progress.
   3880     ConnectionPending,
   3881 } || UnexpectedError;
   3882 
   3883 /// Initiate a connection on a socket.
   3884 /// If `sockfd` is opened in non blocking mode, the function will
   3885 /// return error.WouldBlock when EAGAIN or EINPROGRESS is received.
   3886 pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
   3887     if (builtin.os.tag == .windows) {
   3888         const rc = windows.ws2_32.connect(sock, sock_addr, @intCast(i32, len));
   3889         if (rc == 0) return;
   3890         switch (windows.ws2_32.WSAGetLastError()) {
   3891             .WSAEADDRINUSE => return error.AddressInUse,
   3892             .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
   3893             .WSAECONNREFUSED => return error.ConnectionRefused,
   3894             .WSAECONNRESET => return error.ConnectionResetByPeer,
   3895             .WSAETIMEDOUT => return error.ConnectionTimedOut,
   3896             .WSAEHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well?
   3897             .WSAENETUNREACH,
   3898             => return error.NetworkUnreachable,
   3899             .WSAEFAULT => unreachable,
   3900             .WSAEINVAL => unreachable,
   3901             .WSAEISCONN => unreachable,
   3902             .WSAENOTSOCK => unreachable,
   3903             .WSAEWOULDBLOCK => unreachable,
   3904             .WSAEACCES => unreachable,
   3905             .WSAENOBUFS => return error.SystemResources,
   3906             .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
   3907             else => |err| return windows.unexpectedWSAError(err),
   3908         }
   3909         return;
   3910     }
   3911 
   3912     while (true) {
   3913         switch (errno(system.connect(sock, sock_addr, len))) {
   3914             .SUCCESS => return,
   3915             .ACCES => return error.PermissionDenied,
   3916             .PERM => return error.PermissionDenied,
   3917             .ADDRINUSE => return error.AddressInUse,
   3918             .ADDRNOTAVAIL => return error.AddressNotAvailable,
   3919             .AFNOSUPPORT => return error.AddressFamilyNotSupported,
   3920             .AGAIN, .INPROGRESS => return error.WouldBlock,
   3921             .ALREADY => return error.ConnectionPending,
   3922             .BADF => unreachable, // sockfd is not a valid open file descriptor.
   3923             .CONNREFUSED => return error.ConnectionRefused,
   3924             .CONNRESET => return error.ConnectionResetByPeer,
   3925             .FAULT => unreachable, // The socket structure address is outside the user's address space.
   3926             .INTR => continue,
   3927             .ISCONN => unreachable, // The socket is already connected.
   3928             .HOSTUNREACH => return error.NetworkUnreachable,
   3929             .NETUNREACH => return error.NetworkUnreachable,
   3930             .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   3931             .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
   3932             .TIMEDOUT => return error.ConnectionTimedOut,
   3933             .NOENT => return error.FileNotFound, // Returned when socket is AF.UNIX and the given path does not exist.
   3934             else => |err| return unexpectedErrno(err),
   3935         }
   3936     }
   3937 }
   3938 
   3939 pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
   3940     var err_code: i32 = undefined;
   3941     var size: u32 = @sizeOf(u32);
   3942     const rc = system.getsockopt(sockfd, SOL.SOCKET, SO.ERROR, @ptrCast([*]u8, &err_code), &size);
   3943     assert(size == 4);
   3944     switch (errno(rc)) {
   3945         .SUCCESS => switch (@intToEnum(E, err_code)) {
   3946             .SUCCESS => return,
   3947             .ACCES => return error.PermissionDenied,
   3948             .PERM => return error.PermissionDenied,
   3949             .ADDRINUSE => return error.AddressInUse,
   3950             .ADDRNOTAVAIL => return error.AddressNotAvailable,
   3951             .AFNOSUPPORT => return error.AddressFamilyNotSupported,
   3952             .AGAIN => return error.SystemResources,
   3953             .ALREADY => return error.ConnectionPending,
   3954             .BADF => unreachable, // sockfd is not a valid open file descriptor.
   3955             .CONNREFUSED => return error.ConnectionRefused,
   3956             .FAULT => unreachable, // The socket structure address is outside the user's address space.
   3957             .ISCONN => unreachable, // The socket is already connected.
   3958             .HOSTUNREACH => return error.NetworkUnreachable,
   3959             .NETUNREACH => return error.NetworkUnreachable,
   3960             .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   3961             .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
   3962             .TIMEDOUT => return error.ConnectionTimedOut,
   3963             .CONNRESET => return error.ConnectionResetByPeer,
   3964             else => |err| return unexpectedErrno(err),
   3965         },
   3966         .BADF => unreachable, // The argument sockfd is not a valid file descriptor.
   3967         .FAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
   3968         .INVAL => unreachable,
   3969         .NOPROTOOPT => unreachable, // The option is unknown at the level indicated.
   3970         .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   3971         else => |err| return unexpectedErrno(err),
   3972     }
   3973 }
   3974 
   3975 pub const WaitPidResult = struct {
   3976     pid: pid_t,
   3977     status: u32,
   3978 };
   3979 
   3980 /// Use this version of the `waitpid` wrapper if you spawned your child process using explicit
   3981 /// `fork` and `execve` method. If you spawned your child process using `posix_spawn` method,
   3982 /// use `std.os.posix_spawn.waitpid` instead.
   3983 pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
   3984     const Status = if (builtin.link_libc) c_int else u32;
   3985     var status: Status = undefined;
   3986     while (true) {
   3987         const rc = system.waitpid(pid, &status, if (builtin.link_libc) @intCast(c_int, flags) else flags);
   3988         switch (errno(rc)) {
   3989             .SUCCESS => return .{
   3990                 .pid = @intCast(pid_t, rc),
   3991                 .status = @bitCast(u32, status),
   3992             },
   3993             .INTR => continue,
   3994             .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
   3995             .INVAL => unreachable, // Invalid flags.
   3996             else => unreachable,
   3997         }
   3998     }
   3999 }
   4000 
   4001 pub const FStatError = error{
   4002     SystemResources,
   4003 
   4004     /// In WASI, this error may occur when the file descriptor does
   4005     /// not hold the required rights to get its filestat information.
   4006     AccessDenied,
   4007 } || UnexpectedError;
   4008 
   4009 /// Return information about a file descriptor.
   4010 pub fn fstat(fd: fd_t) FStatError!Stat {
   4011     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4012         var stat: wasi.filestat_t = undefined;
   4013         switch (wasi.fd_filestat_get(fd, &stat)) {
   4014             .SUCCESS => return Stat.fromFilestat(stat),
   4015             .INVAL => unreachable,
   4016             .BADF => unreachable, // Always a race condition.
   4017             .NOMEM => return error.SystemResources,
   4018             .ACCES => return error.AccessDenied,
   4019             .NOTCAPABLE => return error.AccessDenied,
   4020             else => |err| return unexpectedErrno(err),
   4021         }
   4022     }
   4023     if (builtin.os.tag == .windows) {
   4024         @compileError("fstat is not yet implemented on Windows");
   4025     }
   4026 
   4027     const fstat_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   4028         system.fstat64
   4029     else
   4030         system.fstat;
   4031 
   4032     var stat = mem.zeroes(Stat);
   4033     switch (errno(fstat_sym(fd, &stat))) {
   4034         .SUCCESS => return stat,
   4035         .INVAL => unreachable,
   4036         .BADF => unreachable, // Always a race condition.
   4037         .NOMEM => return error.SystemResources,
   4038         .ACCES => return error.AccessDenied,
   4039         else => |err| return unexpectedErrno(err),
   4040     }
   4041 }
   4042 
   4043 pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLinkLoop };
   4044 
   4045 /// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
   4046 /// which is relative to `dirfd` handle.
   4047 /// See also `fstatatZ` and `fstatatWasi`.
   4048 pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
   4049     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4050         const wasi_flags = if (flags & linux.AT.SYMLINK_NOFOLLOW == 0) wasi.LOOKUP_SYMLINK_FOLLOW else 0;
   4051         return fstatatWasi(dirfd, pathname, wasi_flags);
   4052     } else if (builtin.os.tag == .windows) {
   4053         @compileError("fstatat is not yet implemented on Windows");
   4054     } else {
   4055         const pathname_c = try toPosixPath(pathname);
   4056         return fstatatZ(dirfd, &pathname_c, flags);
   4057     }
   4058 }
   4059 
   4060 /// WASI-only. Same as `fstatat` but targeting WASI.
   4061 /// See also `fstatat`.
   4062 pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
   4063     var stat: wasi.filestat_t = undefined;
   4064     switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
   4065         .SUCCESS => return Stat.fromFilestat(stat),
   4066         .INVAL => unreachable,
   4067         .BADF => unreachable, // Always a race condition.
   4068         .NOMEM => return error.SystemResources,
   4069         .ACCES => return error.AccessDenied,
   4070         .FAULT => unreachable,
   4071         .NAMETOOLONG => return error.NameTooLong,
   4072         .NOENT => return error.FileNotFound,
   4073         .NOTDIR => return error.FileNotFound,
   4074         .NOTCAPABLE => return error.AccessDenied,
   4075         else => |err| return unexpectedErrno(err),
   4076     }
   4077 }
   4078 
   4079 /// Same as `fstatat` but `pathname` is null-terminated.
   4080 /// See also `fstatat`.
   4081 pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
   4082     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4083         return fstatatWasi(dirfd, mem.sliceTo(pathname), flags);
   4084     }
   4085 
   4086     const fstatat_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   4087         system.fstatat64
   4088     else
   4089         system.fstatat;
   4090 
   4091     var stat = mem.zeroes(Stat);
   4092     switch (errno(fstatat_sym(dirfd, pathname, &stat, flags))) {
   4093         .SUCCESS => return stat,
   4094         .INVAL => unreachable,
   4095         .BADF => unreachable, // Always a race condition.
   4096         .NOMEM => return error.SystemResources,
   4097         .ACCES => return error.AccessDenied,
   4098         .PERM => return error.AccessDenied,
   4099         .FAULT => unreachable,
   4100         .NAMETOOLONG => return error.NameTooLong,
   4101         .LOOP => return error.SymLinkLoop,
   4102         .NOENT => return error.FileNotFound,
   4103         .NOTDIR => return error.FileNotFound,
   4104         else => |err| return unexpectedErrno(err),
   4105     }
   4106 }
   4107 
   4108 pub const KQueueError = error{
   4109     /// The per-process limit on the number of open file descriptors has been reached.
   4110     ProcessFdQuotaExceeded,
   4111 
   4112     /// The system-wide limit on the total number of open files has been reached.
   4113     SystemFdQuotaExceeded,
   4114 } || UnexpectedError;
   4115 
   4116 pub fn kqueue() KQueueError!i32 {
   4117     const rc = system.kqueue();
   4118     switch (errno(rc)) {
   4119         .SUCCESS => return @intCast(i32, rc),
   4120         .MFILE => return error.ProcessFdQuotaExceeded,
   4121         .NFILE => return error.SystemFdQuotaExceeded,
   4122         else => |err| return unexpectedErrno(err),
   4123     }
   4124 }
   4125 
   4126 pub const KEventError = error{
   4127     /// The process does not have permission to register a filter.
   4128     AccessDenied,
   4129 
   4130     /// The event could not be found to be modified or deleted.
   4131     EventNotFound,
   4132 
   4133     /// No memory was available to register the event.
   4134     SystemResources,
   4135 
   4136     /// The specified process to attach to does not exist.
   4137     ProcessNotFound,
   4138 
   4139     /// changelist or eventlist had too many items on it.
   4140     /// TODO remove this possibility
   4141     Overflow,
   4142 };
   4143 
   4144 pub fn kevent(
   4145     kq: i32,
   4146     changelist: []const Kevent,
   4147     eventlist: []Kevent,
   4148     timeout: ?*const timespec,
   4149 ) KEventError!usize {
   4150     while (true) {
   4151         const rc = system.kevent(
   4152             kq,
   4153             changelist.ptr,
   4154             math.cast(c_int, changelist.len) orelse return error.Overflow,
   4155             eventlist.ptr,
   4156             math.cast(c_int, eventlist.len) orelse return error.Overflow,
   4157             timeout,
   4158         );
   4159         switch (errno(rc)) {
   4160             .SUCCESS => return @intCast(usize, rc),
   4161             .ACCES => return error.AccessDenied,
   4162             .FAULT => unreachable,
   4163             .BADF => unreachable, // Always a race condition.
   4164             .INTR => continue,
   4165             .INVAL => unreachable,
   4166             .NOENT => return error.EventNotFound,
   4167             .NOMEM => return error.SystemResources,
   4168             .SRCH => return error.ProcessNotFound,
   4169             else => unreachable,
   4170         }
   4171     }
   4172 }
   4173 
   4174 pub const INotifyInitError = error{
   4175     ProcessFdQuotaExceeded,
   4176     SystemFdQuotaExceeded,
   4177     SystemResources,
   4178 } || UnexpectedError;
   4179 
   4180 /// initialize an inotify instance
   4181 pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
   4182     const rc = system.inotify_init1(flags);
   4183     switch (errno(rc)) {
   4184         .SUCCESS => return @intCast(i32, rc),
   4185         .INVAL => unreachable,
   4186         .MFILE => return error.ProcessFdQuotaExceeded,
   4187         .NFILE => return error.SystemFdQuotaExceeded,
   4188         .NOMEM => return error.SystemResources,
   4189         else => |err| return unexpectedErrno(err),
   4190     }
   4191 }
   4192 
   4193 pub const INotifyAddWatchError = error{
   4194     AccessDenied,
   4195     NameTooLong,
   4196     FileNotFound,
   4197     SystemResources,
   4198     UserResourceLimitReached,
   4199     NotDir,
   4200     WatchAlreadyExists,
   4201 } || UnexpectedError;
   4202 
   4203 /// add a watch to an initialized inotify instance
   4204 pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
   4205     const pathname_c = try toPosixPath(pathname);
   4206     return inotify_add_watchZ(inotify_fd, &pathname_c, mask);
   4207 }
   4208 
   4209 /// Same as `inotify_add_watch` except pathname is null-terminated.
   4210 pub fn inotify_add_watchZ(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) INotifyAddWatchError!i32 {
   4211     const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
   4212     switch (errno(rc)) {
   4213         .SUCCESS => return @intCast(i32, rc),
   4214         .ACCES => return error.AccessDenied,
   4215         .BADF => unreachable,
   4216         .FAULT => unreachable,
   4217         .INVAL => unreachable,
   4218         .NAMETOOLONG => return error.NameTooLong,
   4219         .NOENT => return error.FileNotFound,
   4220         .NOMEM => return error.SystemResources,
   4221         .NOSPC => return error.UserResourceLimitReached,
   4222         .NOTDIR => return error.NotDir,
   4223         .EXIST => return error.WatchAlreadyExists,
   4224         else => |err| return unexpectedErrno(err),
   4225     }
   4226 }
   4227 
   4228 /// remove an existing watch from an inotify instance
   4229 pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
   4230     switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
   4231         .SUCCESS => return,
   4232         .BADF => unreachable,
   4233         .INVAL => unreachable,
   4234         else => unreachable,
   4235     }
   4236 }
   4237 
   4238 pub const MProtectError = error{
   4239     /// The memory cannot be given the specified access.  This can happen, for example, if you
   4240     /// mmap(2)  a  file  to  which  you have read-only access, then ask mprotect() to mark it
   4241     /// PROT_WRITE.
   4242     AccessDenied,
   4243 
   4244     /// Changing  the  protection  of a memory region would result in the total number of map‐
   4245     /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the
   4246     /// allowed maximum.  (For example, making the protection of a range PROT_READ in the mid‐
   4247     /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐
   4248     /// pings: two read/write mappings at each end and a read-only mapping in the middle.)
   4249     OutOfMemory,
   4250 } || UnexpectedError;
   4251 
   4252 /// `memory.len` must be page-aligned.
   4253 pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void {
   4254     assert(mem.isAligned(memory.len, mem.page_size));
   4255     if (builtin.os.tag == .windows) {
   4256         const win_prot: windows.DWORD = switch (@truncate(u3, protection)) {
   4257             0b000 => windows.PAGE_NOACCESS,
   4258             0b001 => windows.PAGE_READONLY,
   4259             0b010 => unreachable, // +w -r not allowed
   4260             0b011 => windows.PAGE_READWRITE,
   4261             0b100 => windows.PAGE_EXECUTE,
   4262             0b101 => windows.PAGE_EXECUTE_READ,
   4263             0b110 => unreachable, // +w -r not allowed
   4264             0b111 => windows.PAGE_EXECUTE_READWRITE,
   4265         };
   4266         var old: windows.DWORD = undefined;
   4267         windows.VirtualProtect(memory.ptr, memory.len, win_prot, &old) catch |err| switch (err) {
   4268             error.InvalidAddress => return error.AccessDenied,
   4269             error.Unexpected => return error.Unexpected,
   4270         };
   4271     } else {
   4272         switch (errno(system.mprotect(memory.ptr, memory.len, protection))) {
   4273             .SUCCESS => return,
   4274             .INVAL => unreachable,
   4275             .ACCES => return error.AccessDenied,
   4276             .NOMEM => return error.OutOfMemory,
   4277             else => |err| return unexpectedErrno(err),
   4278         }
   4279     }
   4280 }
   4281 
   4282 pub const ForkError = error{SystemResources} || UnexpectedError;
   4283 
   4284 pub fn fork() ForkError!pid_t {
   4285     const rc = system.fork();
   4286     switch (errno(rc)) {
   4287         .SUCCESS => return @intCast(pid_t, rc),
   4288         .AGAIN => return error.SystemResources,
   4289         .NOMEM => return error.SystemResources,
   4290         else => |err| return unexpectedErrno(err),
   4291     }
   4292 }
   4293 
   4294 pub const MMapError = error{
   4295     /// The underlying filesystem of the specified file does not support memory mapping.
   4296     MemoryMappingNotSupported,
   4297 
   4298     /// A file descriptor refers to a non-regular file. Or a file mapping was requested,
   4299     /// but the file descriptor is not open for reading. Or `MAP.SHARED` was requested
   4300     /// and `PROT_WRITE` is set, but the file descriptor is not open in `O.RDWR` mode.
   4301     /// Or `PROT_WRITE` is set, but the file is append-only.
   4302     AccessDenied,
   4303 
   4304     /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
   4305     /// a filesystem that was mounted no-exec.
   4306     PermissionDenied,
   4307     LockedMemoryLimitExceeded,
   4308     OutOfMemory,
   4309 } || UnexpectedError;
   4310 
   4311 /// Map files or devices into memory.
   4312 /// `length` does not need to be aligned.
   4313 /// Use of a mapped region can result in these signals:
   4314 /// * SIGSEGV - Attempted write into a region mapped as read-only.
   4315 /// * SIGBUS - Attempted  access to a portion of the buffer that does not correspond to the file
   4316 pub fn mmap(
   4317     ptr: ?[*]align(mem.page_size) u8,
   4318     length: usize,
   4319     prot: u32,
   4320     flags: u32,
   4321     fd: fd_t,
   4322     offset: u64,
   4323 ) MMapError![]align(mem.page_size) u8 {
   4324     const mmap_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   4325         system.mmap64
   4326     else
   4327         system.mmap;
   4328 
   4329     const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned
   4330     const rc = mmap_sym(ptr, length, prot, flags, fd, ioffset);
   4331     const err = if (builtin.link_libc) blk: {
   4332         if (rc != std.c.MAP.FAILED) return @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, rc))[0..length];
   4333         break :blk @intToEnum(E, system._errno().*);
   4334     } else blk: {
   4335         const err = errno(rc);
   4336         if (err == .SUCCESS) return @intToPtr([*]align(mem.page_size) u8, rc)[0..length];
   4337         break :blk err;
   4338     };
   4339     switch (err) {
   4340         .SUCCESS => unreachable,
   4341         .TXTBSY => return error.AccessDenied,
   4342         .ACCES => return error.AccessDenied,
   4343         .PERM => return error.PermissionDenied,
   4344         .AGAIN => return error.LockedMemoryLimitExceeded,
   4345         .BADF => unreachable, // Always a race condition.
   4346         .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
   4347         .NODEV => return error.MemoryMappingNotSupported,
   4348         .INVAL => unreachable, // Invalid parameters to mmap()
   4349         .NOMEM => return error.OutOfMemory,
   4350         else => return unexpectedErrno(err),
   4351     }
   4352 }
   4353 
   4354 /// Deletes the mappings for the specified address range, causing
   4355 /// further references to addresses within the range to generate invalid memory references.
   4356 /// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
   4357 /// Zig's munmap function does not, for two reasons:
   4358 /// * It violates the Zig principle that resource deallocation must succeed.
   4359 /// * The Windows function, VirtualFree, has this restriction.
   4360 pub fn munmap(memory: []align(mem.page_size) const u8) void {
   4361     switch (errno(system.munmap(memory.ptr, memory.len))) {
   4362         .SUCCESS => return,
   4363         .INVAL => unreachable, // Invalid parameters.
   4364         .NOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
   4365         else => unreachable,
   4366     }
   4367 }
   4368 
   4369 pub const MSyncError = error{
   4370     UnmappedMemory,
   4371 } || UnexpectedError;
   4372 
   4373 pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void {
   4374     switch (errno(system.msync(memory.ptr, memory.len, flags))) {
   4375         .SUCCESS => return,
   4376         .NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory
   4377         .INVAL => unreachable, // Invalid parameters.
   4378         else => unreachable,
   4379     }
   4380 }
   4381 
   4382 pub const AccessError = error{
   4383     PermissionDenied,
   4384     FileNotFound,
   4385     NameTooLong,
   4386     InputOutput,
   4387     SystemResources,
   4388     BadPathName,
   4389     FileBusy,
   4390     SymLinkLoop,
   4391     ReadOnlyFileSystem,
   4392 
   4393     /// On Windows, file paths must be valid Unicode.
   4394     InvalidUtf8,
   4395 } || UnexpectedError;
   4396 
   4397 /// check user's permissions for a file
   4398 /// TODO currently this assumes `mode` is `F.OK` on Windows.
   4399 pub fn access(path: []const u8, mode: u32) AccessError!void {
   4400     if (builtin.os.tag == .windows) {
   4401         const path_w = try windows.sliceToPrefixedFileW(path);
   4402         _ = try windows.GetFileAttributesW(path_w.span().ptr);
   4403         return;
   4404     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4405         return faccessat(wasi.AT.FDCWD, path, mode, 0);
   4406     }
   4407     const path_c = try toPosixPath(path);
   4408     return accessZ(&path_c, mode);
   4409 }
   4410 
   4411 /// Same as `access` except `path` is null-terminated.
   4412 pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
   4413     if (builtin.os.tag == .windows) {
   4414         const path_w = try windows.cStrToPrefixedFileW(path);
   4415         _ = try windows.GetFileAttributesW(path_w.span().ptr);
   4416         return;
   4417     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4418         return access(mem.sliceTo(path, 0), mode);
   4419     }
   4420     switch (errno(system.access(path, mode))) {
   4421         .SUCCESS => return,
   4422         .ACCES => return error.PermissionDenied,
   4423         .ROFS => return error.ReadOnlyFileSystem,
   4424         .LOOP => return error.SymLinkLoop,
   4425         .TXTBSY => return error.FileBusy,
   4426         .NOTDIR => return error.FileNotFound,
   4427         .NOENT => return error.FileNotFound,
   4428         .NAMETOOLONG => return error.NameTooLong,
   4429         .INVAL => unreachable,
   4430         .FAULT => unreachable,
   4431         .IO => return error.InputOutput,
   4432         .NOMEM => return error.SystemResources,
   4433         else => |err| return unexpectedErrno(err),
   4434     }
   4435 }
   4436 
   4437 /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
   4438 /// Otherwise use `access` or `accessC`.
   4439 /// TODO currently this ignores `mode`.
   4440 pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!void {
   4441     _ = mode;
   4442     const ret = try windows.GetFileAttributesW(path);
   4443     if (ret != windows.INVALID_FILE_ATTRIBUTES) {
   4444         return;
   4445     }
   4446     switch (windows.kernel32.GetLastError()) {
   4447         .FILE_NOT_FOUND => return error.FileNotFound,
   4448         .PATH_NOT_FOUND => return error.FileNotFound,
   4449         .ACCESS_DENIED => return error.PermissionDenied,
   4450         else => |err| return windows.unexpectedError(err),
   4451     }
   4452 }
   4453 
   4454 /// Check user's permissions for a file, based on an open directory handle.
   4455 /// TODO currently this ignores `mode` and `flags` on Windows.
   4456 pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
   4457     if (builtin.os.tag == .windows) {
   4458         const path_w = try windows.sliceToPrefixedFileW(path);
   4459         return faccessatW(dirfd, path_w.span().ptr, mode, flags);
   4460     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4461         var resolved = RelativePathWasi{ .dir_fd = dirfd, .relative_path = path };
   4462 
   4463         const file = blk: {
   4464             break :blk fstatat(dirfd, path, flags);
   4465         } catch |err| switch (err) {
   4466             error.AccessDenied => return error.PermissionDenied,
   4467             else => |e| return e,
   4468         };
   4469 
   4470         if (mode != F_OK) {
   4471             var directory: wasi.fdstat_t = undefined;
   4472             if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) {
   4473                 return error.PermissionDenied;
   4474             }
   4475 
   4476             var rights: wasi.rights_t = 0;
   4477             if (mode & R_OK != 0) {
   4478                 rights |= if (file.filetype == .DIRECTORY)
   4479                     wasi.RIGHT.FD_READDIR
   4480                 else
   4481                     wasi.RIGHT.FD_READ;
   4482             }
   4483             if (mode & W_OK != 0) {
   4484                 rights |= wasi.RIGHT.FD_WRITE;
   4485             }
   4486             // No validation for X_OK
   4487 
   4488             if ((rights & directory.fs_rights_inheriting) != rights) {
   4489                 return error.PermissionDenied;
   4490             }
   4491         }
   4492         return;
   4493     }
   4494     const path_c = try toPosixPath(path);
   4495     return faccessatZ(dirfd, &path_c, mode, flags);
   4496 }
   4497 
   4498 /// Same as `faccessat` except the path parameter is null-terminated.
   4499 pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
   4500     if (builtin.os.tag == .windows) {
   4501         const path_w = try windows.cStrToPrefixedFileW(path);
   4502         return faccessatW(dirfd, path_w.span().ptr, mode, flags);
   4503     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4504         return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
   4505     }
   4506     switch (errno(system.faccessat(dirfd, path, mode, flags))) {
   4507         .SUCCESS => return,
   4508         .ACCES => return error.PermissionDenied,
   4509         .ROFS => return error.ReadOnlyFileSystem,
   4510         .LOOP => return error.SymLinkLoop,
   4511         .TXTBSY => return error.FileBusy,
   4512         .NOTDIR => return error.FileNotFound,
   4513         .NOENT => return error.FileNotFound,
   4514         .NAMETOOLONG => return error.NameTooLong,
   4515         .INVAL => unreachable,
   4516         .FAULT => unreachable,
   4517         .IO => return error.InputOutput,
   4518         .NOMEM => return error.SystemResources,
   4519         else => |err| return unexpectedErrno(err),
   4520     }
   4521 }
   4522 
   4523 /// Same as `faccessat` except asserts the target is Windows and the path parameter
   4524 /// is NtDll-prefixed, null-terminated, WTF-16 encoded.
   4525 /// TODO currently this ignores `mode` and `flags`
   4526 pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32) AccessError!void {
   4527     _ = mode;
   4528     _ = flags;
   4529     if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
   4530         return;
   4531     }
   4532     if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
   4533         return;
   4534     }
   4535 
   4536     const path_len_bytes = math.cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong;
   4537     var nt_name = windows.UNICODE_STRING{
   4538         .Length = path_len_bytes,
   4539         .MaximumLength = path_len_bytes,
   4540         .Buffer = @constCast(sub_path_w),
   4541     };
   4542     var attr = windows.OBJECT_ATTRIBUTES{
   4543         .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
   4544         .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
   4545         .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
   4546         .ObjectName = &nt_name,
   4547         .SecurityDescriptor = null,
   4548         .SecurityQualityOfService = null,
   4549     };
   4550     var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
   4551     switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
   4552         .SUCCESS => return,
   4553         .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   4554         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   4555         .OBJECT_NAME_INVALID => unreachable,
   4556         .INVALID_PARAMETER => unreachable,
   4557         .ACCESS_DENIED => return error.PermissionDenied,
   4558         .OBJECT_PATH_SYNTAX_BAD => unreachable,
   4559         else => |rc| return windows.unexpectedStatus(rc),
   4560     }
   4561 }
   4562 
   4563 pub const PipeError = error{
   4564     SystemFdQuotaExceeded,
   4565     ProcessFdQuotaExceeded,
   4566 } || UnexpectedError;
   4567 
   4568 /// Creates a unidirectional data channel that can be used for interprocess communication.
   4569 pub fn pipe() PipeError![2]fd_t {
   4570     var fds: [2]fd_t = undefined;
   4571     switch (errno(system.pipe(&fds))) {
   4572         .SUCCESS => return fds,
   4573         .INVAL => unreachable, // Invalid parameters to pipe()
   4574         .FAULT => unreachable, // Invalid fds pointer
   4575         .NFILE => return error.SystemFdQuotaExceeded,
   4576         .MFILE => return error.ProcessFdQuotaExceeded,
   4577         else => |err| return unexpectedErrno(err),
   4578     }
   4579 }
   4580 
   4581 pub fn pipe2(flags: u32) PipeError![2]fd_t {
   4582     if (@hasDecl(system, "pipe2")) {
   4583         var fds: [2]fd_t = undefined;
   4584         switch (errno(system.pipe2(&fds, flags))) {
   4585             .SUCCESS => return fds,
   4586             .INVAL => unreachable, // Invalid flags
   4587             .FAULT => unreachable, // Invalid fds pointer
   4588             .NFILE => return error.SystemFdQuotaExceeded,
   4589             .MFILE => return error.ProcessFdQuotaExceeded,
   4590             else => |err| return unexpectedErrno(err),
   4591         }
   4592     }
   4593 
   4594     var fds: [2]fd_t = try pipe();
   4595     errdefer {
   4596         close(fds[0]);
   4597         close(fds[1]);
   4598     }
   4599 
   4600     if (flags == 0)
   4601         return fds;
   4602 
   4603     // O.CLOEXEC is special, it's a file descriptor flag and must be set using
   4604     // F.SETFD.
   4605     if (flags & O.CLOEXEC != 0) {
   4606         for (fds) |fd| {
   4607             switch (errno(system.fcntl(fd, F.SETFD, @as(u32, FD_CLOEXEC)))) {
   4608                 .SUCCESS => {},
   4609                 .INVAL => unreachable, // Invalid flags
   4610                 .BADF => unreachable, // Always a race condition
   4611                 else => |err| return unexpectedErrno(err),
   4612             }
   4613         }
   4614     }
   4615 
   4616     const new_flags = flags & ~@as(u32, O.CLOEXEC);
   4617     // Set every other flag affecting the file status using F.SETFL.
   4618     if (new_flags != 0) {
   4619         for (fds) |fd| {
   4620             switch (errno(system.fcntl(fd, F.SETFL, new_flags))) {
   4621                 .SUCCESS => {},
   4622                 .INVAL => unreachable, // Invalid flags
   4623                 .BADF => unreachable, // Always a race condition
   4624                 else => |err| return unexpectedErrno(err),
   4625             }
   4626         }
   4627     }
   4628 
   4629     return fds;
   4630 }
   4631 
   4632 pub const SysCtlError = error{
   4633     PermissionDenied,
   4634     SystemResources,
   4635     NameTooLong,
   4636     UnknownName,
   4637 } || UnexpectedError;
   4638 
   4639 pub fn sysctl(
   4640     name: []const c_int,
   4641     oldp: ?*anyopaque,
   4642     oldlenp: ?*usize,
   4643     newp: ?*anyopaque,
   4644     newlen: usize,
   4645 ) SysCtlError!void {
   4646     if (builtin.os.tag == .wasi) {
   4647         @panic("unsupported"); // TODO should be compile error, not panic
   4648     }
   4649     if (builtin.os.tag == .haiku) {
   4650         @panic("unsupported"); // TODO should be compile error, not panic
   4651     }
   4652 
   4653     const name_len = math.cast(c_uint, name.len) orelse return error.NameTooLong;
   4654     switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {
   4655         .SUCCESS => return,
   4656         .FAULT => unreachable,
   4657         .PERM => return error.PermissionDenied,
   4658         .NOMEM => return error.SystemResources,
   4659         .NOENT => return error.UnknownName,
   4660         else => |err| return unexpectedErrno(err),
   4661     }
   4662 }
   4663 
   4664 pub fn sysctlbynameZ(
   4665     name: [*:0]const u8,
   4666     oldp: ?*anyopaque,
   4667     oldlenp: ?*usize,
   4668     newp: ?*anyopaque,
   4669     newlen: usize,
   4670 ) SysCtlError!void {
   4671     if (builtin.os.tag == .wasi) {
   4672         @panic("unsupported"); // TODO should be compile error, not panic
   4673     }
   4674     if (builtin.os.tag == .haiku) {
   4675         @panic("unsupported"); // TODO should be compile error, not panic
   4676     }
   4677 
   4678     switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
   4679         .SUCCESS => return,
   4680         .FAULT => unreachable,
   4681         .PERM => return error.PermissionDenied,
   4682         .NOMEM => return error.SystemResources,
   4683         .NOENT => return error.UnknownName,
   4684         else => |err| return unexpectedErrno(err),
   4685     }
   4686 }
   4687 
   4688 pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
   4689     switch (errno(system.gettimeofday(tv, tz))) {
   4690         .SUCCESS => return,
   4691         .INVAL => unreachable,
   4692         else => unreachable,
   4693     }
   4694 }
   4695 
   4696 pub const SeekError = error{
   4697     Unseekable,
   4698 
   4699     /// In WASI, this error may occur when the file descriptor does
   4700     /// not hold the required rights to seek on it.
   4701     AccessDenied,
   4702 } || UnexpectedError;
   4703 
   4704 /// Repositions read/write file offset relative to the beginning.
   4705 pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
   4706     if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   4707         var result: u64 = undefined;
   4708         switch (errno(system.llseek(fd, offset, &result, SEEK.SET))) {
   4709             .SUCCESS => return,
   4710             .BADF => unreachable, // always a race condition
   4711             .INVAL => return error.Unseekable,
   4712             .OVERFLOW => return error.Unseekable,
   4713             .SPIPE => return error.Unseekable,
   4714             .NXIO => return error.Unseekable,
   4715             else => |err| return unexpectedErrno(err),
   4716         }
   4717     }
   4718     if (builtin.os.tag == .windows) {
   4719         return windows.SetFilePointerEx_BEGIN(fd, offset);
   4720     }
   4721     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4722         var new_offset: wasi.filesize_t = undefined;
   4723         switch (wasi.fd_seek(fd, @bitCast(wasi.filedelta_t, offset), .SET, &new_offset)) {
   4724             .SUCCESS => return,
   4725             .BADF => unreachable, // always a race condition
   4726             .INVAL => return error.Unseekable,
   4727             .OVERFLOW => return error.Unseekable,
   4728             .SPIPE => return error.Unseekable,
   4729             .NXIO => return error.Unseekable,
   4730             .NOTCAPABLE => return error.AccessDenied,
   4731             else => |err| return unexpectedErrno(err),
   4732         }
   4733     }
   4734 
   4735     const lseek_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   4736         system.lseek64
   4737     else
   4738         system.lseek;
   4739 
   4740     const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned
   4741     switch (errno(lseek_sym(fd, ioffset, SEEK.SET))) {
   4742         .SUCCESS => return,
   4743         .BADF => unreachable, // always a race condition
   4744         .INVAL => return error.Unseekable,
   4745         .OVERFLOW => return error.Unseekable,
   4746         .SPIPE => return error.Unseekable,
   4747         .NXIO => return error.Unseekable,
   4748         else => |err| return unexpectedErrno(err),
   4749     }
   4750 }
   4751 
   4752 /// Repositions read/write file offset relative to the current offset.
   4753 pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
   4754     if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   4755         var result: u64 = undefined;
   4756         switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK.CUR))) {
   4757             .SUCCESS => return,
   4758             .BADF => unreachable, // always a race condition
   4759             .INVAL => return error.Unseekable,
   4760             .OVERFLOW => return error.Unseekable,
   4761             .SPIPE => return error.Unseekable,
   4762             .NXIO => return error.Unseekable,
   4763             else => |err| return unexpectedErrno(err),
   4764         }
   4765     }
   4766     if (builtin.os.tag == .windows) {
   4767         return windows.SetFilePointerEx_CURRENT(fd, offset);
   4768     }
   4769     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4770         var new_offset: wasi.filesize_t = undefined;
   4771         switch (wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
   4772             .SUCCESS => return,
   4773             .BADF => unreachable, // always a race condition
   4774             .INVAL => return error.Unseekable,
   4775             .OVERFLOW => return error.Unseekable,
   4776             .SPIPE => return error.Unseekable,
   4777             .NXIO => return error.Unseekable,
   4778             .NOTCAPABLE => return error.AccessDenied,
   4779             else => |err| return unexpectedErrno(err),
   4780         }
   4781     }
   4782     const lseek_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   4783         system.lseek64
   4784     else
   4785         system.lseek;
   4786 
   4787     const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned
   4788     switch (errno(lseek_sym(fd, ioffset, SEEK.CUR))) {
   4789         .SUCCESS => return,
   4790         .BADF => unreachable, // always a race condition
   4791         .INVAL => return error.Unseekable,
   4792         .OVERFLOW => return error.Unseekable,
   4793         .SPIPE => return error.Unseekable,
   4794         .NXIO => return error.Unseekable,
   4795         else => |err| return unexpectedErrno(err),
   4796     }
   4797 }
   4798 
   4799 /// Repositions read/write file offset relative to the end.
   4800 pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
   4801     if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   4802         var result: u64 = undefined;
   4803         switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK.END))) {
   4804             .SUCCESS => return,
   4805             .BADF => unreachable, // always a race condition
   4806             .INVAL => return error.Unseekable,
   4807             .OVERFLOW => return error.Unseekable,
   4808             .SPIPE => return error.Unseekable,
   4809             .NXIO => return error.Unseekable,
   4810             else => |err| return unexpectedErrno(err),
   4811         }
   4812     }
   4813     if (builtin.os.tag == .windows) {
   4814         return windows.SetFilePointerEx_END(fd, offset);
   4815     }
   4816     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4817         var new_offset: wasi.filesize_t = undefined;
   4818         switch (wasi.fd_seek(fd, offset, .END, &new_offset)) {
   4819             .SUCCESS => return,
   4820             .BADF => unreachable, // always a race condition
   4821             .INVAL => return error.Unseekable,
   4822             .OVERFLOW => return error.Unseekable,
   4823             .SPIPE => return error.Unseekable,
   4824             .NXIO => return error.Unseekable,
   4825             .NOTCAPABLE => return error.AccessDenied,
   4826             else => |err| return unexpectedErrno(err),
   4827         }
   4828     }
   4829     const lseek_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   4830         system.lseek64
   4831     else
   4832         system.lseek;
   4833 
   4834     const ioffset = @bitCast(i64, offset); // the OS treats this as unsigned
   4835     switch (errno(lseek_sym(fd, ioffset, SEEK.END))) {
   4836         .SUCCESS => return,
   4837         .BADF => unreachable, // always a race condition
   4838         .INVAL => return error.Unseekable,
   4839         .OVERFLOW => return error.Unseekable,
   4840         .SPIPE => return error.Unseekable,
   4841         .NXIO => return error.Unseekable,
   4842         else => |err| return unexpectedErrno(err),
   4843     }
   4844 }
   4845 
   4846 /// Returns the read/write file offset relative to the beginning.
   4847 pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
   4848     if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   4849         var result: u64 = undefined;
   4850         switch (errno(system.llseek(fd, 0, &result, SEEK.CUR))) {
   4851             .SUCCESS => return result,
   4852             .BADF => unreachable, // always a race condition
   4853             .INVAL => return error.Unseekable,
   4854             .OVERFLOW => return error.Unseekable,
   4855             .SPIPE => return error.Unseekable,
   4856             .NXIO => return error.Unseekable,
   4857             else => |err| return unexpectedErrno(err),
   4858         }
   4859     }
   4860     if (builtin.os.tag == .windows) {
   4861         return windows.SetFilePointerEx_CURRENT_get(fd);
   4862     }
   4863     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   4864         var new_offset: wasi.filesize_t = undefined;
   4865         switch (wasi.fd_seek(fd, 0, .CUR, &new_offset)) {
   4866             .SUCCESS => return new_offset,
   4867             .BADF => unreachable, // always a race condition
   4868             .INVAL => return error.Unseekable,
   4869             .OVERFLOW => return error.Unseekable,
   4870             .SPIPE => return error.Unseekable,
   4871             .NXIO => return error.Unseekable,
   4872             .NOTCAPABLE => return error.AccessDenied,
   4873             else => |err| return unexpectedErrno(err),
   4874         }
   4875     }
   4876     const lseek_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   4877         system.lseek64
   4878     else
   4879         system.lseek;
   4880 
   4881     const rc = lseek_sym(fd, 0, SEEK.CUR);
   4882     switch (errno(rc)) {
   4883         .SUCCESS => return @bitCast(u64, rc),
   4884         .BADF => unreachable, // always a race condition
   4885         .INVAL => return error.Unseekable,
   4886         .OVERFLOW => return error.Unseekable,
   4887         .SPIPE => return error.Unseekable,
   4888         .NXIO => return error.Unseekable,
   4889         else => |err| return unexpectedErrno(err),
   4890     }
   4891 }
   4892 
   4893 pub const FcntlError = error{
   4894     PermissionDenied,
   4895     FileBusy,
   4896     ProcessFdQuotaExceeded,
   4897     Locked,
   4898     DeadLock,
   4899     LockedRegionLimitExceeded,
   4900 } || UnexpectedError;
   4901 
   4902 pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
   4903     while (true) {
   4904         const rc = system.fcntl(fd, cmd, arg);
   4905         switch (errno(rc)) {
   4906             .SUCCESS => return @intCast(usize, rc),
   4907             .INTR => continue,
   4908             .AGAIN, .ACCES => return error.Locked,
   4909             .BADF => unreachable,
   4910             .BUSY => return error.FileBusy,
   4911             .INVAL => unreachable, // invalid parameters
   4912             .PERM => return error.PermissionDenied,
   4913             .MFILE => return error.ProcessFdQuotaExceeded,
   4914             .NOTDIR => unreachable, // invalid parameter
   4915             .DEADLK => return error.DeadLock,
   4916             .NOLCK => return error.LockedRegionLimitExceeded,
   4917             else => |err| return unexpectedErrno(err),
   4918         }
   4919     }
   4920 }
   4921 
   4922 fn setSockFlags(sock: socket_t, flags: u32) !void {
   4923     if ((flags & SOCK.CLOEXEC) != 0) {
   4924         if (builtin.os.tag == .windows) {
   4925             // TODO: Find out if this is supported for sockets
   4926         } else {
   4927             var fd_flags = fcntl(sock, F.GETFD, 0) catch |err| switch (err) {
   4928                 error.FileBusy => unreachable,
   4929                 error.Locked => unreachable,
   4930                 error.PermissionDenied => unreachable,
   4931                 error.DeadLock => unreachable,
   4932                 error.LockedRegionLimitExceeded => unreachable,
   4933                 else => |e| return e,
   4934             };
   4935             fd_flags |= FD_CLOEXEC;
   4936             _ = fcntl(sock, F.SETFD, fd_flags) catch |err| switch (err) {
   4937                 error.FileBusy => unreachable,
   4938                 error.Locked => unreachable,
   4939                 error.PermissionDenied => unreachable,
   4940                 error.DeadLock => unreachable,
   4941                 error.LockedRegionLimitExceeded => unreachable,
   4942                 else => |e| return e,
   4943             };
   4944         }
   4945     }
   4946     if ((flags & SOCK.NONBLOCK) != 0) {
   4947         if (builtin.os.tag == .windows) {
   4948             var mode: c_ulong = 1;
   4949             if (windows.ws2_32.ioctlsocket(sock, windows.ws2_32.FIONBIO, &mode) == windows.ws2_32.SOCKET_ERROR) {
   4950                 switch (windows.ws2_32.WSAGetLastError()) {
   4951                     .WSANOTINITIALISED => unreachable,
   4952                     .WSAENETDOWN => return error.NetworkSubsystemFailed,
   4953                     .WSAENOTSOCK => return error.FileDescriptorNotASocket,
   4954                     // TODO: handle more errors
   4955                     else => |err| return windows.unexpectedWSAError(err),
   4956                 }
   4957             }
   4958         } else {
   4959             var fl_flags = fcntl(sock, F.GETFL, 0) catch |err| switch (err) {
   4960                 error.FileBusy => unreachable,
   4961                 error.Locked => unreachable,
   4962                 error.PermissionDenied => unreachable,
   4963                 error.DeadLock => unreachable,
   4964                 error.LockedRegionLimitExceeded => unreachable,
   4965                 else => |e| return e,
   4966             };
   4967             fl_flags |= O.NONBLOCK;
   4968             _ = fcntl(sock, F.SETFL, fl_flags) catch |err| switch (err) {
   4969                 error.FileBusy => unreachable,
   4970                 error.Locked => unreachable,
   4971                 error.PermissionDenied => unreachable,
   4972                 error.DeadLock => unreachable,
   4973                 error.LockedRegionLimitExceeded => unreachable,
   4974                 else => |e| return e,
   4975             };
   4976         }
   4977     }
   4978 }
   4979 
   4980 pub const FlockError = error{
   4981     WouldBlock,
   4982 
   4983     /// The kernel ran out of memory for allocating file locks
   4984     SystemResources,
   4985 
   4986     /// The underlying filesystem does not support file locks
   4987     FileLocksNotSupported,
   4988 } || UnexpectedError;
   4989 
   4990 /// Depending on the operating system `flock` may or may not interact with
   4991 /// `fcntl` locks made by other processes.
   4992 pub fn flock(fd: fd_t, operation: i32) FlockError!void {
   4993     while (true) {
   4994         const rc = system.flock(fd, operation);
   4995         switch (errno(rc)) {
   4996             .SUCCESS => return,
   4997             .BADF => unreachable,
   4998             .INTR => continue,
   4999             .INVAL => unreachable, // invalid parameters
   5000             .NOLCK => return error.SystemResources,
   5001             .AGAIN => return error.WouldBlock, // TODO: integrate with async instead of just returning an error
   5002             .OPNOTSUPP => return error.FileLocksNotSupported,
   5003             else => |err| return unexpectedErrno(err),
   5004         }
   5005     }
   5006 }
   5007 
   5008 pub const RealPathError = error{
   5009     FileNotFound,
   5010     AccessDenied,
   5011     NameTooLong,
   5012     NotSupported,
   5013     NotDir,
   5014     SymLinkLoop,
   5015     InputOutput,
   5016     FileTooBig,
   5017     IsDir,
   5018     ProcessFdQuotaExceeded,
   5019     SystemFdQuotaExceeded,
   5020     NoDevice,
   5021     SystemResources,
   5022     NoSpaceLeft,
   5023     FileSystem,
   5024     BadPathName,
   5025     DeviceBusy,
   5026 
   5027     SharingViolation,
   5028     PipeBusy,
   5029 
   5030     /// On WASI, the current CWD may not be associated with an absolute path.
   5031     InvalidHandle,
   5032 
   5033     /// On Windows, file paths must be valid Unicode.
   5034     InvalidUtf8,
   5035 
   5036     PathAlreadyExists,
   5037 } || UnexpectedError;
   5038 
   5039 /// Return the canonicalized absolute pathname.
   5040 /// Expands all symbolic links and resolves references to `.`, `..`, and
   5041 /// extra `/` characters in `pathname`.
   5042 /// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
   5043 /// See also `realpathZ` and `realpathW`.
   5044 pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   5045     if (builtin.os.tag == .windows) {
   5046         const pathname_w = try windows.sliceToPrefixedFileW(pathname);
   5047         return realpathW(pathname_w.span(), out_buffer);
   5048     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   5049         @compileError("WASI does not support os.realpath");
   5050     }
   5051     const pathname_c = try toPosixPath(pathname);
   5052     return realpathZ(&pathname_c, out_buffer);
   5053 }
   5054 
   5055 /// Same as `realpath` except `pathname` is null-terminated.
   5056 pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   5057     if (builtin.os.tag == .windows) {
   5058         const pathname_w = try windows.cStrToPrefixedFileW(pathname);
   5059         return realpathW(pathname_w.span(), out_buffer);
   5060     } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
   5061         return realpath(mem.sliceTo(pathname, 0), out_buffer);
   5062     }
   5063     if (!builtin.link_libc) {
   5064         const flags = if (builtin.os.tag == .linux) O.PATH | O.NONBLOCK | O.CLOEXEC else O.NONBLOCK | O.CLOEXEC;
   5065         const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
   5066             error.FileLocksNotSupported => unreachable,
   5067             error.WouldBlock => unreachable,
   5068             error.FileBusy => unreachable, // not asking for write permissions
   5069             error.InvalidHandle => unreachable, // WASI-only
   5070             else => |e| return e,
   5071         };
   5072         defer close(fd);
   5073 
   5074         return getFdPath(fd, out_buffer);
   5075     }
   5076     const result_path = std.c.realpath(pathname, out_buffer) orelse switch (@intToEnum(E, std.c._errno().*)) {
   5077         .SUCCESS => unreachable,
   5078         .INVAL => unreachable,
   5079         .BADF => unreachable,
   5080         .FAULT => unreachable,
   5081         .ACCES => return error.AccessDenied,
   5082         .NOENT => return error.FileNotFound,
   5083         .OPNOTSUPP => return error.NotSupported,
   5084         .NOTDIR => return error.NotDir,
   5085         .NAMETOOLONG => return error.NameTooLong,
   5086         .LOOP => return error.SymLinkLoop,
   5087         .IO => return error.InputOutput,
   5088         else => |err| return unexpectedErrno(err),
   5089     };
   5090     return mem.sliceTo(result_path, 0);
   5091 }
   5092 
   5093 /// Same as `realpath` except `pathname` is UTF16LE-encoded.
   5094 pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   5095     const w = windows;
   5096 
   5097     const dir = std.fs.cwd().fd;
   5098     const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
   5099     const share_access = w.FILE_SHARE_READ;
   5100     const creation = w.FILE_OPEN;
   5101     const h_file = blk: {
   5102         const res = w.OpenFile(pathname, .{
   5103             .dir = dir,
   5104             .access_mask = access_mask,
   5105             .share_access = share_access,
   5106             .creation = creation,
   5107             .io_mode = .blocking,
   5108         }) catch |err| switch (err) {
   5109             error.IsDir => break :blk w.OpenFile(pathname, .{
   5110                 .dir = dir,
   5111                 .access_mask = access_mask,
   5112                 .share_access = share_access,
   5113                 .creation = creation,
   5114                 .io_mode = .blocking,
   5115                 .filter = .dir_only,
   5116             }) catch |er| switch (er) {
   5117                 error.WouldBlock => unreachable,
   5118                 else => |e2| return e2,
   5119             },
   5120             error.WouldBlock => unreachable,
   5121             else => |e| return e,
   5122         };
   5123         break :blk res;
   5124     };
   5125     defer w.CloseHandle(h_file);
   5126 
   5127     return getFdPath(h_file, out_buffer);
   5128 }
   5129 
   5130 /// Return canonical path of handle `fd`.
   5131 /// This function is very host-specific and is not universally supported by all hosts.
   5132 /// For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is
   5133 /// unsupported on WASI.
   5134 pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   5135     switch (builtin.os.tag) {
   5136         .windows => {
   5137             var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
   5138             const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
   5139 
   5140             // Trust that Windows gives us valid UTF-16LE.
   5141             const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
   5142             return out_buffer[0..end_index];
   5143         },
   5144         .macos, .ios, .watchos, .tvos => {
   5145             // On macOS, we can use F.GETPATH fcntl command to query the OS for
   5146             // the path to the file descriptor.
   5147             @memset(out_buffer, 0, MAX_PATH_BYTES);
   5148             switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
   5149                 .SUCCESS => {},
   5150                 .BADF => return error.FileNotFound,
   5151                 .NOSPC => return error.NameTooLong,
   5152                 // TODO man pages for fcntl on macOS don't really tell you what
   5153                 // errno values to expect when command is F.GETPATH...
   5154                 else => |err| return unexpectedErrno(err),
   5155             }
   5156             const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES;
   5157             return out_buffer[0..len];
   5158         },
   5159         .linux => {
   5160             var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
   5161             const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}) catch unreachable;
   5162 
   5163             const target = readlinkZ(proc_path, out_buffer) catch |err| {
   5164                 switch (err) {
   5165                     error.UnsupportedReparsePointType => unreachable, // Windows only,
   5166                     error.NotLink => unreachable,
   5167                     else => |e| return e,
   5168                 }
   5169             };
   5170             return target;
   5171         },
   5172         .solaris => {
   5173             var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
   5174             const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/path/{d}", .{fd}) catch unreachable;
   5175 
   5176             const target = readlinkZ(proc_path, out_buffer) catch |err| switch (err) {
   5177                 error.UnsupportedReparsePointType => unreachable,
   5178                 error.NotLink => unreachable,
   5179                 else => |e| return e,
   5180             };
   5181             return target;
   5182         },
   5183         .freebsd => {
   5184             if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0 }) == .gt) {
   5185                 var kfile: system.kinfo_file = undefined;
   5186                 kfile.structsize = system.KINFO_FILE_SIZE;
   5187                 switch (errno(system.fcntl(fd, system.F.KINFO, @ptrToInt(&kfile)))) {
   5188                     .SUCCESS => {},
   5189                     .BADF => return error.FileNotFound,
   5190                     else => |err| return unexpectedErrno(err),
   5191                 }
   5192                 const len = mem.indexOfScalar(u8, &kfile.path, 0) orelse MAX_PATH_BYTES;
   5193                 if (len == 0) return error.NameTooLong;
   5194                 mem.copy(u8, out_buffer, kfile.path[0..len]);
   5195                 return out_buffer[0..len];
   5196             } else {
   5197                 // This fallback implementation reimplements libutil's `kinfo_getfile()`.
   5198                 // The motivation is to avoid linking -lutil when building zig or general
   5199                 // user executables.
   5200                 var mib = [4]c_int{ CTL.KERN, KERN.PROC, KERN.PROC_FILEDESC, system.getpid() };
   5201                 var len: usize = undefined;
   5202                 sysctl(&mib, null, &len, null, 0) catch |err| switch (err) {
   5203                     error.PermissionDenied => unreachable,
   5204                     error.SystemResources => return error.SystemResources,
   5205                     error.NameTooLong => unreachable,
   5206                     error.UnknownName => unreachable,
   5207                     else => return error.Unexpected,
   5208                 };
   5209                 len = len * 4 / 3;
   5210                 const buf = std.heap.c_allocator.alloc(u8, len) catch return error.SystemResources;
   5211                 defer std.heap.c_allocator.free(buf);
   5212                 len = buf.len;
   5213                 sysctl(&mib, &buf[0], &len, null, 0) catch |err| switch (err) {
   5214                     error.PermissionDenied => unreachable,
   5215                     error.SystemResources => return error.SystemResources,
   5216                     error.NameTooLong => unreachable,
   5217                     error.UnknownName => unreachable,
   5218                     else => return error.Unexpected,
   5219                 };
   5220                 var i: usize = 0;
   5221                 while (i < len) {
   5222                     const kf: *align(1) system.kinfo_file = @ptrCast(*align(1) system.kinfo_file, &buf[i]);
   5223                     if (kf.fd == fd) {
   5224                         len = mem.indexOfScalar(u8, &kf.path, 0) orelse MAX_PATH_BYTES;
   5225                         if (len == 0) return error.NameTooLong;
   5226                         mem.copy(u8, out_buffer, kf.path[0..len]);
   5227                         return out_buffer[0..len];
   5228                     }
   5229                     i += @intCast(usize, kf.structsize);
   5230                 }
   5231                 return error.InvalidHandle;
   5232             }
   5233         },
   5234         .dragonfly => {
   5235             if (comptime builtin.os.version_range.semver.max.order(.{ .major = 6, .minor = 0 }) == .lt) {
   5236                 @compileError("querying for canonical path of a handle is unsupported on this host");
   5237             }
   5238             @memset(out_buffer, 0, MAX_PATH_BYTES);
   5239             switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
   5240                 .SUCCESS => {},
   5241                 .BADF => return error.FileNotFound,
   5242                 .RANGE => return error.NameTooLong,
   5243                 else => |err| return unexpectedErrno(err),
   5244             }
   5245             const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES;
   5246             return out_buffer[0..len];
   5247         },
   5248         .netbsd => {
   5249             if (comptime builtin.os.version_range.semver.max.order(.{ .major = 10, .minor = 0 }) == .lt) {
   5250                 @compileError("querying for canonical path of a handle is unsupported on this host");
   5251             }
   5252             @memset(out_buffer, 0, MAX_PATH_BYTES);
   5253             switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
   5254                 .SUCCESS => {},
   5255                 .ACCES => return error.AccessDenied,
   5256                 .BADF => return error.FileNotFound,
   5257                 .NOENT => return error.FileNotFound,
   5258                 .NOMEM => return error.SystemResources,
   5259                 .RANGE => return error.NameTooLong,
   5260                 else => |err| return unexpectedErrno(err),
   5261             }
   5262             const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES;
   5263             return out_buffer[0..len];
   5264         },
   5265         else => @compileError("querying for canonical path of a handle is unsupported on this host"),
   5266     }
   5267 }
   5268 
   5269 /// Spurious wakeups are possible and no precision of timing is guaranteed.
   5270 pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
   5271     var req = timespec{
   5272         .tv_sec = math.cast(isize, seconds) orelse math.maxInt(isize),
   5273         .tv_nsec = math.cast(isize, nanoseconds) orelse math.maxInt(isize),
   5274     };
   5275     var rem: timespec = undefined;
   5276     while (true) {
   5277         switch (errno(system.nanosleep(&req, &rem))) {
   5278             .FAULT => unreachable,
   5279             .INVAL => {
   5280                 // Sometimes Darwin returns EINVAL for no reason.
   5281                 // We treat it as a spurious wakeup.
   5282                 return;
   5283             },
   5284             .INTR => {
   5285                 req = rem;
   5286                 continue;
   5287             },
   5288             // This prong handles success as well as unexpected errors.
   5289             else => return,
   5290         }
   5291     }
   5292 }
   5293 
   5294 pub fn dl_iterate_phdr(
   5295     context: anytype,
   5296     comptime Error: type,
   5297     comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
   5298 ) Error!void {
   5299     const Context = @TypeOf(context);
   5300 
   5301     if (builtin.object_format != .elf)
   5302         @compileError("dl_iterate_phdr is not available for this target");
   5303 
   5304     if (builtin.link_libc) {
   5305         switch (system.dl_iterate_phdr(struct {
   5306             fn callbackC(info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.C) c_int {
   5307                 const context_ptr = @ptrCast(*const Context, @alignCast(@alignOf(*const Context), data));
   5308                 callback(info, size, context_ptr.*) catch |err| return @errorToInt(err);
   5309                 return 0;
   5310             }
   5311         }.callbackC, @intToPtr(?*anyopaque, @ptrToInt(&context)))) {
   5312             0 => return,
   5313             else => |err| return @errSetCast(Error, @intToError(@intCast(u16, err))), // TODO don't hardcode u16
   5314         }
   5315     }
   5316 
   5317     const elf_base = std.process.getBaseAddress();
   5318     const ehdr = @intToPtr(*elf.Ehdr, elf_base);
   5319     // Make sure the base address points to an ELF image.
   5320     assert(mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC));
   5321     const n_phdr = ehdr.e_phnum;
   5322     const phdrs = (@intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff))[0..n_phdr];
   5323 
   5324     var it = dl.linkmap_iterator(phdrs) catch unreachable;
   5325 
   5326     // The executable has no dynamic link segment, create a single entry for
   5327     // the whole ELF image.
   5328     if (it.end()) {
   5329         // Find the base address for the ELF image, if this is a PIE the value
   5330         // is non-zero.
   5331         const base_address = for (phdrs) |*phdr| {
   5332             if (phdr.p_type == elf.PT_PHDR) {
   5333                 break @ptrToInt(phdrs.ptr) - phdr.p_vaddr;
   5334                 // We could try computing the difference between _DYNAMIC and
   5335                 // the p_vaddr of the PT_DYNAMIC section, but using the phdr is
   5336                 // good enough (Is it?).
   5337             }
   5338         } else unreachable;
   5339 
   5340         var info = dl_phdr_info{
   5341             .dlpi_addr = base_address,
   5342             .dlpi_name = "/proc/self/exe",
   5343             .dlpi_phdr = phdrs.ptr,
   5344             .dlpi_phnum = ehdr.e_phnum,
   5345         };
   5346 
   5347         return callback(&info, @sizeOf(dl_phdr_info), context);
   5348     }
   5349 
   5350     // Last return value from the callback function.
   5351     while (it.next()) |entry| {
   5352         var dlpi_phdr: [*]elf.Phdr = undefined;
   5353         var dlpi_phnum: u16 = undefined;
   5354 
   5355         if (entry.l_addr != 0) {
   5356             const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr);
   5357             dlpi_phdr = @intToPtr([*]elf.Phdr, entry.l_addr + elf_header.e_phoff);
   5358             dlpi_phnum = elf_header.e_phnum;
   5359         } else {
   5360             // This is the running ELF image
   5361             dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff);
   5362             dlpi_phnum = ehdr.e_phnum;
   5363         }
   5364 
   5365         var info = dl_phdr_info{
   5366             .dlpi_addr = entry.l_addr,
   5367             .dlpi_name = entry.l_name,
   5368             .dlpi_phdr = dlpi_phdr,
   5369             .dlpi_phnum = dlpi_phnum,
   5370         };
   5371 
   5372         try callback(&info, @sizeOf(dl_phdr_info), context);
   5373     }
   5374 }
   5375 
   5376 pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
   5377 
   5378 /// TODO: change this to return the timespec as a return value
   5379 /// TODO: look into making clk_id an enum
   5380 pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
   5381     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   5382         var ts: timestamp_t = undefined;
   5383         switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) {
   5384             .SUCCESS => {
   5385                 tp.* = .{
   5386                     .tv_sec = @intCast(i64, ts / std.time.ns_per_s),
   5387                     .tv_nsec = @intCast(isize, ts % std.time.ns_per_s),
   5388                 };
   5389             },
   5390             .INVAL => return error.UnsupportedClock,
   5391             else => |err| return unexpectedErrno(err),
   5392         }
   5393         return;
   5394     }
   5395     if (builtin.os.tag == .windows) {
   5396         if (clk_id == CLOCK.REALTIME) {
   5397             var ft: windows.FILETIME = undefined;
   5398             windows.kernel32.GetSystemTimeAsFileTime(&ft);
   5399             // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch.
   5400             const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
   5401             const ft_per_s = std.time.ns_per_s / 100;
   5402             tp.* = .{
   5403                 .tv_sec = @intCast(i64, ft64 / ft_per_s) + std.time.epoch.windows,
   5404                 .tv_nsec = @intCast(c_long, ft64 % ft_per_s) * 100,
   5405             };
   5406             return;
   5407         } else {
   5408             // TODO POSIX implementation of CLOCK.MONOTONIC on Windows.
   5409             return error.UnsupportedClock;
   5410         }
   5411     }
   5412 
   5413     switch (errno(system.clock_gettime(clk_id, tp))) {
   5414         .SUCCESS => return,
   5415         .FAULT => unreachable,
   5416         .INVAL => return error.UnsupportedClock,
   5417         else => |err| return unexpectedErrno(err),
   5418     }
   5419 }
   5420 
   5421 pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
   5422     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   5423         var ts: timestamp_t = undefined;
   5424         switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) {
   5425             .SUCCESS => res.* = .{
   5426                 .tv_sec = @intCast(i64, ts / std.time.ns_per_s),
   5427                 .tv_nsec = @intCast(isize, ts % std.time.ns_per_s),
   5428             },
   5429             .INVAL => return error.UnsupportedClock,
   5430             else => |err| return unexpectedErrno(err),
   5431         }
   5432         return;
   5433     }
   5434 
   5435     switch (errno(system.clock_getres(clk_id, res))) {
   5436         .SUCCESS => return,
   5437         .FAULT => unreachable,
   5438         .INVAL => return error.UnsupportedClock,
   5439         else => |err| return unexpectedErrno(err),
   5440     }
   5441 }
   5442 
   5443 pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
   5444 
   5445 pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
   5446     var set: cpu_set_t = undefined;
   5447     switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
   5448         .SUCCESS => return set,
   5449         .FAULT => unreachable,
   5450         .INVAL => unreachable,
   5451         .SRCH => unreachable,
   5452         .PERM => return error.PermissionDenied,
   5453         else => |err| return unexpectedErrno(err),
   5454     }
   5455 }
   5456 
   5457 /// Used to convert a slice to a null terminated slice on the stack.
   5458 /// TODO https://github.com/ziglang/zig/issues/287
   5459 pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
   5460     if (std.debug.runtime_safety) assert(std.mem.indexOfScalar(u8, file_path, 0) == null);
   5461     var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;
   5462     // >= rather than > to make room for the null byte
   5463     if (file_path.len >= MAX_PATH_BYTES) return error.NameTooLong;
   5464     mem.copy(u8, &path_with_null, file_path);
   5465     path_with_null[file_path.len] = 0;
   5466     return path_with_null;
   5467 }
   5468 
   5469 /// Whether or not error.Unexpected will print its value and a stack trace.
   5470 /// if this happens the fix is to add the error code to the corresponding
   5471 /// switch expression, possibly introduce a new error in the error set, and
   5472 /// send a patch to Zig.
   5473 pub const unexpected_error_tracing = builtin.zig_backend == .stage2_llvm and builtin.mode == .Debug;
   5474 
   5475 pub const UnexpectedError = error{
   5476     /// The Operating System returned an undocumented error code.
   5477     /// This error is in theory not possible, but it would be better
   5478     /// to handle this error than to invoke undefined behavior.
   5479     Unexpected,
   5480 };
   5481 
   5482 /// Call this when you made a syscall or something that sets errno
   5483 /// and you get an unexpected error.
   5484 pub fn unexpectedErrno(err: E) UnexpectedError {
   5485     if (unexpected_error_tracing) {
   5486         std.debug.print("unexpected errno: {d}\n", .{@enumToInt(err)});
   5487         std.debug.dumpCurrentStackTrace(null);
   5488     }
   5489     return error.Unexpected;
   5490 }
   5491 
   5492 pub const SigaltstackError = error{
   5493     /// The supplied stack size was less than MINSIGSTKSZ.
   5494     SizeTooSmall,
   5495 
   5496     /// Attempted to change the signal stack while it was active.
   5497     PermissionDenied,
   5498 } || UnexpectedError;
   5499 
   5500 pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
   5501     switch (errno(system.sigaltstack(ss, old_ss))) {
   5502         .SUCCESS => return,
   5503         .FAULT => unreachable,
   5504         .INVAL => unreachable,
   5505         .NOMEM => return error.SizeTooSmall,
   5506         .PERM => return error.PermissionDenied,
   5507         else => |err| return unexpectedErrno(err),
   5508     }
   5509 }
   5510 
   5511 /// Examine and change a signal action.
   5512 pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) error{OperationNotSupported}!void {
   5513     switch (errno(system.sigaction(sig, act, oact))) {
   5514         .SUCCESS => return,
   5515         .INVAL, .NOSYS => return error.OperationNotSupported,
   5516         else => unreachable,
   5517     }
   5518 }
   5519 
   5520 /// Sets the thread signal mask.
   5521 pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void {
   5522     switch (errno(system.sigprocmask(flags, set, oldset))) {
   5523         .SUCCESS => return,
   5524         .FAULT => unreachable,
   5525         .INVAL => unreachable,
   5526         else => unreachable,
   5527     }
   5528 }
   5529 
   5530 pub const FutimensError = error{
   5531     /// times is NULL, or both tv_nsec values are UTIME_NOW, and either:
   5532     /// *  the effective user ID of the caller does not match the  owner
   5533     ///    of  the  file,  the  caller does not have write access to the
   5534     ///    file, and the caller is not privileged (Linux: does not  have
   5535     ///    either  the  CAP_FOWNER  or the CAP_DAC_OVERRIDE capability);
   5536     ///    or,
   5537     /// *  the file is marked immutable (see chattr(1)).
   5538     AccessDenied,
   5539 
   5540     /// The caller attempted to change one or both timestamps to a value
   5541     /// other than the current time, or to change one of the  timestamps
   5542     /// to the current time while leaving the other timestamp unchanged,
   5543     /// (i.e., times is not NULL, neither tv_nsec  field  is  UTIME_NOW,
   5544     /// and neither tv_nsec field is UTIME_OMIT) and either:
   5545     /// *  the  caller's  effective  user ID does not match the owner of
   5546     ///    file, and the caller is not privileged (Linux: does not  have
   5547     ///    the CAP_FOWNER capability); or,
   5548     /// *  the file is marked append-only or immutable (see chattr(1)).
   5549     PermissionDenied,
   5550 
   5551     ReadOnlyFileSystem,
   5552 } || UnexpectedError;
   5553 
   5554 pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
   5555     if (builtin.os.tag == .wasi and !builtin.link_libc) {
   5556         // TODO WASI encodes `wasi.fstflags` to signify magic values
   5557         // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore
   5558         // this here, but we should really handle it somehow.
   5559         const atim = times[0].toTimestamp();
   5560         const mtim = times[1].toTimestamp();
   5561         switch (wasi.fd_filestat_set_times(fd, atim, mtim, wasi.FILESTAT_SET_ATIM | wasi.FILESTAT_SET_MTIM)) {
   5562             .SUCCESS => return,
   5563             .ACCES => return error.AccessDenied,
   5564             .PERM => return error.PermissionDenied,
   5565             .BADF => unreachable, // always a race condition
   5566             .FAULT => unreachable,
   5567             .INVAL => unreachable,
   5568             .ROFS => return error.ReadOnlyFileSystem,
   5569             else => |err| return unexpectedErrno(err),
   5570         }
   5571     }
   5572 
   5573     switch (errno(system.futimens(fd, times))) {
   5574         .SUCCESS => return,
   5575         .ACCES => return error.AccessDenied,
   5576         .PERM => return error.PermissionDenied,
   5577         .BADF => unreachable, // always a race condition
   5578         .FAULT => unreachable,
   5579         .INVAL => unreachable,
   5580         .ROFS => return error.ReadOnlyFileSystem,
   5581         else => |err| return unexpectedErrno(err),
   5582     }
   5583 }
   5584 
   5585 pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;
   5586 
   5587 pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
   5588     if (builtin.link_libc) {
   5589         switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
   5590             .SUCCESS => return mem.sliceTo(name_buffer, 0),
   5591             .FAULT => unreachable,
   5592             .NAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
   5593             .PERM => return error.PermissionDenied,
   5594             else => |err| return unexpectedErrno(err),
   5595         }
   5596     }
   5597     if (builtin.os.tag == .linux) {
   5598         const uts = uname();
   5599         const hostname = mem.sliceTo(&uts.nodename, 0);
   5600         mem.copy(u8, name_buffer, hostname);
   5601         return name_buffer[0..hostname.len];
   5602     }
   5603 
   5604     @compileError("TODO implement gethostname for this OS");
   5605 }
   5606 
   5607 pub fn uname() utsname {
   5608     var uts: utsname = undefined;
   5609     switch (errno(system.uname(&uts))) {
   5610         .SUCCESS => return uts,
   5611         .FAULT => unreachable,
   5612         else => unreachable,
   5613     }
   5614 }
   5615 
   5616 pub fn res_mkquery(
   5617     op: u4,
   5618     dname: []const u8,
   5619     class: u8,
   5620     ty: u8,
   5621     data: []const u8,
   5622     newrr: ?[*]const u8,
   5623     buf: []u8,
   5624 ) usize {
   5625     _ = data;
   5626     _ = newrr;
   5627     // This implementation is ported from musl libc.
   5628     // A more idiomatic "ziggy" implementation would be welcome.
   5629     var name = dname;
   5630     if (mem.endsWith(u8, name, ".")) name.len -= 1;
   5631     assert(name.len <= 253);
   5632     const n = 17 + name.len + @boolToInt(name.len != 0);
   5633 
   5634     // Construct query template - ID will be filled later
   5635     var q: [280]u8 = undefined;
   5636     @memset(&q, 0, n);
   5637     q[2] = @as(u8, op) * 8 + 1;
   5638     q[5] = 1;
   5639     mem.copy(u8, q[13..], name);
   5640     var i: usize = 13;
   5641     var j: usize = undefined;
   5642     while (q[i] != 0) : (i = j + 1) {
   5643         j = i;
   5644         while (q[j] != 0 and q[j] != '.') : (j += 1) {}
   5645         // TODO determine the circumstances for this and whether or
   5646         // not this should be an error.
   5647         if (j - i - 1 > 62) unreachable;
   5648         q[i - 1] = @intCast(u8, j - i);
   5649     }
   5650     q[i + 1] = ty;
   5651     q[i + 3] = class;
   5652 
   5653     // Make a reasonably unpredictable id
   5654     var ts: timespec = undefined;
   5655     clock_gettime(CLOCK.REALTIME, &ts) catch {};
   5656     const UInt = std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(ts.tv_nsec)));
   5657     const unsec = @bitCast(UInt, ts.tv_nsec);
   5658     const id = @truncate(u32, unsec + unsec / 65536);
   5659     q[0] = @truncate(u8, id / 256);
   5660     q[1] = @truncate(u8, id);
   5661 
   5662     mem.copy(u8, buf, q[0..n]);
   5663     return n;
   5664 }
   5665 
   5666 pub const SendError = error{
   5667     /// (For UNIX domain sockets, which are identified by pathname) Write permission is  denied
   5668     /// on  the destination socket file, or search permission is denied for one of the
   5669     /// directories the path prefix.  (See path_resolution(7).)
   5670     /// (For UDP sockets) An attempt was made to send to a network/broadcast address as  though
   5671     /// it was a unicast address.
   5672     AccessDenied,
   5673 
   5674     /// The socket is marked nonblocking and the requested operation would block, and
   5675     /// there is no global event loop configured.
   5676     /// It's also possible to get this error under the following condition:
   5677     /// (Internet  domain datagram sockets) The socket referred to by sockfd had not previously
   5678     /// been bound to an address and, upon attempting to bind it to an ephemeral port,  it  was
   5679     /// determined that all port numbers in the ephemeral port range are currently in use.  See
   5680     /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
   5681     WouldBlock,
   5682 
   5683     /// Another Fast Open is already in progress.
   5684     FastOpenAlreadyInProgress,
   5685 
   5686     /// Connection reset by peer.
   5687     ConnectionResetByPeer,
   5688 
   5689     /// The  socket  type requires that message be sent atomically, and the size of the message
   5690     /// to be sent made this impossible. The message is not transmitted.
   5691     MessageTooBig,
   5692 
   5693     /// The output queue for a network interface was full.  This generally indicates  that  the
   5694     /// interface  has  stopped sending, but may be caused by transient congestion.  (Normally,
   5695     /// this does not occur in Linux.  Packets are just silently dropped when  a  device  queue
   5696     /// overflows.)
   5697     /// This is also caused when there is not enough kernel memory available.
   5698     SystemResources,
   5699 
   5700     /// The  local  end  has been shut down on a connection oriented socket.  In this case, the
   5701     /// process will also receive a SIGPIPE unless MSG.NOSIGNAL is set.
   5702     BrokenPipe,
   5703 
   5704     FileDescriptorNotASocket,
   5705 
   5706     /// Network is unreachable.
   5707     NetworkUnreachable,
   5708 
   5709     /// The local network interface used to reach the destination is down.
   5710     NetworkSubsystemFailed,
   5711 } || UnexpectedError;
   5712 
   5713 pub const SendMsgError = SendError || error{
   5714     /// The passed address didn't have the correct address family in its sa_family field.
   5715     AddressFamilyNotSupported,
   5716 
   5717     /// Returned when socket is AF.UNIX and the given path has a symlink loop.
   5718     SymLinkLoop,
   5719 
   5720     /// Returned when socket is AF.UNIX and the given path length exceeds `MAX_PATH_BYTES` bytes.
   5721     NameTooLong,
   5722 
   5723     /// Returned when socket is AF.UNIX and the given path does not point to an existing file.
   5724     FileNotFound,
   5725     NotDir,
   5726 
   5727     /// The socket is not connected (connection-oriented sockets only).
   5728     SocketNotConnected,
   5729     AddressNotAvailable,
   5730 };
   5731 
   5732 pub fn sendmsg(
   5733     /// The file descriptor of the sending socket.
   5734     sockfd: socket_t,
   5735     /// Message header and iovecs
   5736     msg: *const msghdr_const,
   5737     flags: u32,
   5738 ) SendMsgError!usize {
   5739     while (true) {
   5740         const rc = system.sendmsg(sockfd, msg, flags);
   5741         if (builtin.os.tag == .windows) {
   5742             if (rc == windows.ws2_32.SOCKET_ERROR) {
   5743                 switch (windows.ws2_32.WSAGetLastError()) {
   5744                     .WSAEACCES => return error.AccessDenied,
   5745                     .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
   5746                     .WSAECONNRESET => return error.ConnectionResetByPeer,
   5747                     .WSAEMSGSIZE => return error.MessageTooBig,
   5748                     .WSAENOBUFS => return error.SystemResources,
   5749                     .WSAENOTSOCK => return error.FileDescriptorNotASocket,
   5750                     .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
   5751                     .WSAEDESTADDRREQ => unreachable, // A destination address is required.
   5752                     .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
   5753                     .WSAEHOSTUNREACH => return error.NetworkUnreachable,
   5754                     // TODO: WSAEINPROGRESS, WSAEINTR
   5755                     .WSAEINVAL => unreachable,
   5756                     .WSAENETDOWN => return error.NetworkSubsystemFailed,
   5757                     .WSAENETRESET => return error.ConnectionResetByPeer,
   5758                     .WSAENETUNREACH => return error.NetworkUnreachable,
   5759                     .WSAENOTCONN => return error.SocketNotConnected,
   5760                     .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
   5761                     .WSAEWOULDBLOCK => return error.WouldBlock,
   5762                     .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
   5763                     else => |err| return windows.unexpectedWSAError(err),
   5764                 }
   5765             } else {
   5766                 return @intCast(usize, rc);
   5767             }
   5768         } else {
   5769             switch (errno(rc)) {
   5770                 .SUCCESS => return @intCast(usize, rc),
   5771 
   5772                 .ACCES => return error.AccessDenied,
   5773                 .AGAIN => return error.WouldBlock,
   5774                 .ALREADY => return error.FastOpenAlreadyInProgress,
   5775                 .BADF => unreachable, // always a race condition
   5776                 .CONNRESET => return error.ConnectionResetByPeer,
   5777                 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
   5778                 .FAULT => unreachable, // An invalid user space address was specified for an argument.
   5779                 .INTR => continue,
   5780                 .INVAL => unreachable, // Invalid argument passed.
   5781                 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
   5782                 .MSGSIZE => return error.MessageTooBig,
   5783                 .NOBUFS => return error.SystemResources,
   5784                 .NOMEM => return error.SystemResources,
   5785                 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   5786                 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
   5787                 .PIPE => return error.BrokenPipe,
   5788                 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
   5789                 .LOOP => return error.SymLinkLoop,
   5790                 .NAMETOOLONG => return error.NameTooLong,
   5791                 .NOENT => return error.FileNotFound,
   5792                 .NOTDIR => return error.NotDir,
   5793                 .HOSTUNREACH => return error.NetworkUnreachable,
   5794                 .NETUNREACH => return error.NetworkUnreachable,
   5795                 .NOTCONN => return error.SocketNotConnected,
   5796                 .NETDOWN => return error.NetworkSubsystemFailed,
   5797                 else => |err| return unexpectedErrno(err),
   5798             }
   5799         }
   5800     }
   5801 }
   5802 
   5803 pub const SendToError = SendMsgError || error{
   5804     /// The destination address is not reachable by the bound address.
   5805     UnreachableAddress,
   5806 };
   5807 
   5808 /// Transmit a message to another socket.
   5809 ///
   5810 /// The `sendto` call may be used only when the socket is in a connected state (so that the intended
   5811 /// recipient  is  known). The  following call
   5812 ///
   5813 ///     send(sockfd, buf, len, flags);
   5814 ///
   5815 /// is equivalent to
   5816 ///
   5817 ///     sendto(sockfd, buf, len, flags, NULL, 0);
   5818 ///
   5819 /// If  sendto()  is used on a connection-mode (`SOCK.STREAM`, `SOCK.SEQPACKET`) socket, the arguments
   5820 /// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
   5821 /// that the socket was actually connected.
   5822 /// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying  its  size.
   5823 ///
   5824 /// If the message is too long to pass atomically through the underlying protocol,
   5825 /// `SendError.MessageTooBig` is returned, and the message is not transmitted.
   5826 ///
   5827 /// There is no  indication  of  failure  to  deliver.
   5828 ///
   5829 /// When the message does not fit into the send buffer of  the  socket,  `sendto`  normally  blocks,
   5830 /// unless  the socket has been placed in nonblocking I/O mode.  In nonblocking mode it would fail
   5831 /// with `SendError.WouldBlock`.  The `select` call may be used  to  determine when it is
   5832 /// possible to send more data.
   5833 pub fn sendto(
   5834     /// The file descriptor of the sending socket.
   5835     sockfd: socket_t,
   5836     /// Message to send.
   5837     buf: []const u8,
   5838     flags: u32,
   5839     dest_addr: ?*const sockaddr,
   5840     addrlen: socklen_t,
   5841 ) SendToError!usize {
   5842     while (true) {
   5843         const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
   5844         if (builtin.os.tag == .windows) {
   5845             if (rc == windows.ws2_32.SOCKET_ERROR) {
   5846                 switch (windows.ws2_32.WSAGetLastError()) {
   5847                     .WSAEACCES => return error.AccessDenied,
   5848                     .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
   5849                     .WSAECONNRESET => return error.ConnectionResetByPeer,
   5850                     .WSAEMSGSIZE => return error.MessageTooBig,
   5851                     .WSAENOBUFS => return error.SystemResources,
   5852                     .WSAENOTSOCK => return error.FileDescriptorNotASocket,
   5853                     .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
   5854                     .WSAEDESTADDRREQ => unreachable, // A destination address is required.
   5855                     .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
   5856                     .WSAEHOSTUNREACH => return error.NetworkUnreachable,
   5857                     // TODO: WSAEINPROGRESS, WSAEINTR
   5858                     .WSAEINVAL => unreachable,
   5859                     .WSAENETDOWN => return error.NetworkSubsystemFailed,
   5860                     .WSAENETRESET => return error.ConnectionResetByPeer,
   5861                     .WSAENETUNREACH => return error.NetworkUnreachable,
   5862                     .WSAENOTCONN => return error.SocketNotConnected,
   5863                     .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
   5864                     .WSAEWOULDBLOCK => return error.WouldBlock,
   5865                     .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
   5866                     else => |err| return windows.unexpectedWSAError(err),
   5867                 }
   5868             } else {
   5869                 return @intCast(usize, rc);
   5870             }
   5871         } else {
   5872             switch (errno(rc)) {
   5873                 .SUCCESS => return @intCast(usize, rc),
   5874 
   5875                 .ACCES => return error.AccessDenied,
   5876                 .AGAIN => return error.WouldBlock,
   5877                 .ALREADY => return error.FastOpenAlreadyInProgress,
   5878                 .BADF => unreachable, // always a race condition
   5879                 .CONNRESET => return error.ConnectionResetByPeer,
   5880                 .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
   5881                 .FAULT => unreachable, // An invalid user space address was specified for an argument.
   5882                 .INTR => continue,
   5883                 .INVAL => return error.UnreachableAddress,
   5884                 .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
   5885                 .MSGSIZE => return error.MessageTooBig,
   5886                 .NOBUFS => return error.SystemResources,
   5887                 .NOMEM => return error.SystemResources,
   5888                 .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   5889                 .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
   5890                 .PIPE => return error.BrokenPipe,
   5891                 .AFNOSUPPORT => return error.AddressFamilyNotSupported,
   5892                 .LOOP => return error.SymLinkLoop,
   5893                 .NAMETOOLONG => return error.NameTooLong,
   5894                 .NOENT => return error.FileNotFound,
   5895                 .NOTDIR => return error.NotDir,
   5896                 .HOSTUNREACH => return error.NetworkUnreachable,
   5897                 .NETUNREACH => return error.NetworkUnreachable,
   5898                 .NOTCONN => return error.SocketNotConnected,
   5899                 .NETDOWN => return error.NetworkSubsystemFailed,
   5900                 else => |err| return unexpectedErrno(err),
   5901             }
   5902         }
   5903     }
   5904 }
   5905 
   5906 /// Transmit a message to another socket.
   5907 ///
   5908 /// The `send` call may be used only when the socket is in a connected state (so that the intended
   5909 /// recipient  is  known).   The  only  difference  between `send` and `write` is the presence of
   5910 /// flags.  With a zero flags argument, `send` is equivalent to  `write`.   Also,  the  following
   5911 /// call
   5912 ///
   5913 ///     send(sockfd, buf, len, flags);
   5914 ///
   5915 /// is equivalent to
   5916 ///
   5917 ///     sendto(sockfd, buf, len, flags, NULL, 0);
   5918 ///
   5919 /// There is no  indication  of  failure  to  deliver.
   5920 ///
   5921 /// When the message does not fit into the send buffer of  the  socket,  `send`  normally  blocks,
   5922 /// unless  the socket has been placed in nonblocking I/O mode.  In nonblocking mode it would fail
   5923 /// with `SendError.WouldBlock`.  The `select` call may be used  to  determine when it is
   5924 /// possible to send more data.
   5925 pub fn send(
   5926     /// The file descriptor of the sending socket.
   5927     sockfd: socket_t,
   5928     buf: []const u8,
   5929     flags: u32,
   5930 ) SendError!usize {
   5931     return sendto(sockfd, buf, flags, null, 0) catch |err| switch (err) {
   5932         error.AddressFamilyNotSupported => unreachable,
   5933         error.SymLinkLoop => unreachable,
   5934         error.NameTooLong => unreachable,
   5935         error.FileNotFound => unreachable,
   5936         error.NotDir => unreachable,
   5937         error.NetworkUnreachable => unreachable,
   5938         error.AddressNotAvailable => unreachable,
   5939         error.SocketNotConnected => unreachable,
   5940         error.UnreachableAddress => unreachable,
   5941         else => |e| return e,
   5942     };
   5943 }
   5944 
   5945 pub const SendFileError = PReadError || WriteError || SendError;
   5946 
   5947 fn count_iovec_bytes(iovs: []const iovec_const) usize {
   5948     var count: usize = 0;
   5949     for (iovs) |iov| {
   5950         count += iov.iov_len;
   5951     }
   5952     return count;
   5953 }
   5954 
   5955 /// Transfer data between file descriptors, with optional headers and trailers.
   5956 /// Returns the number of bytes written, which can be zero.
   5957 ///
   5958 /// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible,
   5959 /// this is done within the operating system kernel, which can provide better performance
   5960 /// characteristics than transferring data from kernel to user space and back, such as with
   5961 /// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been
   5962 /// reached. Note, however, that partial writes are still possible in this case.
   5963 ///
   5964 /// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor
   5965 /// opened for writing. They may be any kind of file descriptor; however, if `in_fd` is not a regular
   5966 /// file system file, it may cause this function to fall back to calling `read` and `write`, in which case
   5967 /// atomicity guarantees no longer apply.
   5968 ///
   5969 /// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated.
   5970 /// If the output file descriptor has a seek position, it is updated as bytes are written. When
   5971 /// `in_offset` is past the end of the input file, it successfully reads 0 bytes.
   5972 ///
   5973 /// `flags` has different meanings per operating system; refer to the respective man pages.
   5974 ///
   5975 /// These systems support atomically sending everything, including headers and trailers:
   5976 /// * macOS
   5977 /// * FreeBSD
   5978 ///
   5979 /// These systems support in-kernel data copying, but headers and trailers are not sent atomically:
   5980 /// * Linux
   5981 ///
   5982 /// Other systems fall back to calling `read` / `write`.
   5983 ///
   5984 /// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`
   5985 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
   5986 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `sendfile` man page.
   5987 /// The limit on Darwin is `0x7fffffff`, trying to write more than that returns EINVAL.
   5988 /// The corresponding POSIX limit on this is `math.maxInt(isize)`.
   5989 pub fn sendfile(
   5990     out_fd: fd_t,
   5991     in_fd: fd_t,
   5992     in_offset: u64,
   5993     in_len: u64,
   5994     headers: []const iovec_const,
   5995     trailers: []const iovec_const,
   5996     flags: u32,
   5997 ) SendFileError!usize {
   5998     var header_done = false;
   5999     var total_written: usize = 0;
   6000 
   6001     // Prevents EOVERFLOW.
   6002     const size_t = std.meta.Int(.unsigned, @typeInfo(usize).Int.bits - 1);
   6003     const max_count = switch (builtin.os.tag) {
   6004         .linux => 0x7ffff000,
   6005         .macos, .ios, .watchos, .tvos => math.maxInt(i32),
   6006         else => math.maxInt(size_t),
   6007     };
   6008 
   6009     switch (builtin.os.tag) {
   6010         .linux => sf: {
   6011             // sendfile() first appeared in Linux 2.2, glibc 2.1.
   6012             const call_sf = comptime if (builtin.link_libc)
   6013                 std.c.versionCheck(.{ .major = 2, .minor = 1 }).ok
   6014             else
   6015                 builtin.os.version_range.linux.range.max.order(.{ .major = 2, .minor = 2 }) != .lt;
   6016             if (!call_sf) break :sf;
   6017 
   6018             if (headers.len != 0) {
   6019                 const amt = try writev(out_fd, headers);
   6020                 total_written += amt;
   6021                 if (amt < count_iovec_bytes(headers)) return total_written;
   6022                 header_done = true;
   6023             }
   6024 
   6025             // Here we match BSD behavior, making a zero count value send as many bytes as possible.
   6026             const adjusted_count_tmp = if (in_len == 0) max_count else @min(in_len, @as(size_t, max_count));
   6027             // TODO we should not need this cast; improve return type of @min
   6028             const adjusted_count = @intCast(usize, adjusted_count_tmp);
   6029 
   6030             const sendfile_sym = if (builtin.link_libc)
   6031                 system.sendfile64
   6032             else
   6033                 system.sendfile;
   6034 
   6035             while (true) {
   6036                 var offset: off_t = @bitCast(off_t, in_offset);
   6037                 const rc = sendfile_sym(out_fd, in_fd, &offset, adjusted_count);
   6038                 switch (errno(rc)) {
   6039                     .SUCCESS => {
   6040                         const amt = @bitCast(usize, rc);
   6041                         total_written += amt;
   6042                         if (in_len == 0 and amt == 0) {
   6043                             // We have detected EOF from `in_fd`.
   6044                             break;
   6045                         } else if (amt < in_len) {
   6046                             return total_written;
   6047                         } else {
   6048                             break;
   6049                         }
   6050                     },
   6051 
   6052                     .BADF => unreachable, // Always a race condition.
   6053                     .FAULT => unreachable, // Segmentation fault.
   6054                     .OVERFLOW => unreachable, // We avoid passing too large of a `count`.
   6055                     .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
   6056 
   6057                     .INVAL, .NOSYS => {
   6058                         // EINVAL could be any of the following situations:
   6059                         // * Descriptor is not valid or locked
   6060                         // * an mmap(2)-like operation is  not  available  for in_fd
   6061                         // * count is negative
   6062                         // * out_fd has the O.APPEND flag set
   6063                         // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write
   6064                         // manually, the same as ENOSYS.
   6065                         break :sf;
   6066                     },
   6067                     .AGAIN => if (std.event.Loop.instance) |loop| {
   6068                         loop.waitUntilFdWritable(out_fd);
   6069                         continue;
   6070                     } else {
   6071                         return error.WouldBlock;
   6072                     },
   6073                     .IO => return error.InputOutput,
   6074                     .PIPE => return error.BrokenPipe,
   6075                     .NOMEM => return error.SystemResources,
   6076                     .NXIO => return error.Unseekable,
   6077                     .SPIPE => return error.Unseekable,
   6078                     else => |err| {
   6079                         unexpectedErrno(err) catch {};
   6080                         break :sf;
   6081                     },
   6082                 }
   6083             }
   6084 
   6085             if (trailers.len != 0) {
   6086                 total_written += try writev(out_fd, trailers);
   6087             }
   6088 
   6089             return total_written;
   6090         },
   6091         .freebsd => sf: {
   6092             var hdtr_data: std.c.sf_hdtr = undefined;
   6093             var hdtr: ?*std.c.sf_hdtr = null;
   6094             if (headers.len != 0 or trailers.len != 0) {
   6095                 // Here we carefully avoid `@intCast` by returning partial writes when
   6096                 // too many io vectors are provided.
   6097                 const hdr_cnt = math.cast(u31, headers.len) orelse math.maxInt(u31);
   6098                 if (headers.len > hdr_cnt) return writev(out_fd, headers);
   6099 
   6100                 const trl_cnt = math.cast(u31, trailers.len) orelse math.maxInt(u31);
   6101 
   6102                 hdtr_data = std.c.sf_hdtr{
   6103                     .headers = headers.ptr,
   6104                     .hdr_cnt = hdr_cnt,
   6105                     .trailers = trailers.ptr,
   6106                     .trl_cnt = trl_cnt,
   6107                 };
   6108                 hdtr = &hdtr_data;
   6109             }
   6110 
   6111             const adjusted_count = @min(in_len, max_count);
   6112 
   6113             while (true) {
   6114                 var sbytes: off_t = undefined;
   6115                 const offset = @bitCast(off_t, in_offset);
   6116                 const err = errno(system.sendfile(in_fd, out_fd, offset, adjusted_count, hdtr, &sbytes, flags));
   6117                 const amt = @bitCast(usize, sbytes);
   6118                 switch (err) {
   6119                     .SUCCESS => return amt,
   6120 
   6121                     .BADF => unreachable, // Always a race condition.
   6122                     .FAULT => unreachable, // Segmentation fault.
   6123                     .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
   6124 
   6125                     .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
   6126                         // EINVAL could be any of the following situations:
   6127                         // * The fd argument is not a regular file.
   6128                         // * The s argument is not a SOCK.STREAM type socket.
   6129                         // * The offset argument is negative.
   6130                         // Because of some of these possibilities, we fall back to doing read/write
   6131                         // manually, the same as ENOSYS.
   6132                         break :sf;
   6133                     },
   6134 
   6135                     .INTR => if (amt != 0) return amt else continue,
   6136 
   6137                     .AGAIN => if (amt != 0) {
   6138                         return amt;
   6139                     } else if (std.event.Loop.instance) |loop| {
   6140                         loop.waitUntilFdWritable(out_fd);
   6141                         continue;
   6142                     } else {
   6143                         return error.WouldBlock;
   6144                     },
   6145 
   6146                     .BUSY => if (amt != 0) {
   6147                         return amt;
   6148                     } else if (std.event.Loop.instance) |loop| {
   6149                         loop.waitUntilFdReadable(in_fd);
   6150                         continue;
   6151                     } else {
   6152                         return error.WouldBlock;
   6153                     },
   6154 
   6155                     .IO => return error.InputOutput,
   6156                     .NOBUFS => return error.SystemResources,
   6157                     .PIPE => return error.BrokenPipe,
   6158 
   6159                     else => {
   6160                         unexpectedErrno(err) catch {};
   6161                         if (amt != 0) {
   6162                             return amt;
   6163                         } else {
   6164                             break :sf;
   6165                         }
   6166                     },
   6167                 }
   6168             }
   6169         },
   6170         .macos, .ios, .tvos, .watchos => sf: {
   6171             var hdtr_data: std.c.sf_hdtr = undefined;
   6172             var hdtr: ?*std.c.sf_hdtr = null;
   6173             if (headers.len != 0 or trailers.len != 0) {
   6174                 // Here we carefully avoid `@intCast` by returning partial writes when
   6175                 // too many io vectors are provided.
   6176                 const hdr_cnt = math.cast(u31, headers.len) orelse math.maxInt(u31);
   6177                 if (headers.len > hdr_cnt) return writev(out_fd, headers);
   6178 
   6179                 const trl_cnt = math.cast(u31, trailers.len) orelse math.maxInt(u31);
   6180 
   6181                 hdtr_data = std.c.sf_hdtr{
   6182                     .headers = headers.ptr,
   6183                     .hdr_cnt = hdr_cnt,
   6184                     .trailers = trailers.ptr,
   6185                     .trl_cnt = trl_cnt,
   6186                 };
   6187                 hdtr = &hdtr_data;
   6188             }
   6189 
   6190             const adjusted_count_temporary = @min(in_len, @as(u63, max_count));
   6191             // TODO we should not need this int cast; improve the return type of `@min`
   6192             const adjusted_count = @intCast(u63, adjusted_count_temporary);
   6193 
   6194             while (true) {
   6195                 var sbytes: off_t = adjusted_count;
   6196                 const signed_offset = @bitCast(i64, in_offset);
   6197                 const err = errno(system.sendfile(in_fd, out_fd, signed_offset, &sbytes, hdtr, flags));
   6198                 const amt = @bitCast(usize, sbytes);
   6199                 switch (err) {
   6200                     .SUCCESS => return amt,
   6201 
   6202                     .BADF => unreachable, // Always a race condition.
   6203                     .FAULT => unreachable, // Segmentation fault.
   6204                     .INVAL => unreachable,
   6205                     .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket
   6206 
   6207                     .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf,
   6208 
   6209                     .INTR => if (amt != 0) return amt else continue,
   6210 
   6211                     .AGAIN => if (amt != 0) {
   6212                         return amt;
   6213                     } else if (std.event.Loop.instance) |loop| {
   6214                         loop.waitUntilFdWritable(out_fd);
   6215                         continue;
   6216                     } else {
   6217                         return error.WouldBlock;
   6218                     },
   6219 
   6220                     .IO => return error.InputOutput,
   6221                     .PIPE => return error.BrokenPipe,
   6222 
   6223                     else => {
   6224                         unexpectedErrno(err) catch {};
   6225                         if (amt != 0) {
   6226                             return amt;
   6227                         } else {
   6228                             break :sf;
   6229                         }
   6230                     },
   6231                 }
   6232             }
   6233         },
   6234         else => {}, // fall back to read/write
   6235     }
   6236 
   6237     if (headers.len != 0 and !header_done) {
   6238         const amt = try writev(out_fd, headers);
   6239         total_written += amt;
   6240         if (amt < count_iovec_bytes(headers)) return total_written;
   6241     }
   6242 
   6243     rw: {
   6244         var buf: [8 * 4096]u8 = undefined;
   6245         // Here we match BSD behavior, making a zero count value send as many bytes as possible.
   6246         const adjusted_count_tmp = if (in_len == 0) buf.len else @min(buf.len, in_len);
   6247         // TODO we should not need this cast; improve return type of @min
   6248         const adjusted_count = @intCast(usize, adjusted_count_tmp);
   6249         const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
   6250         if (amt_read == 0) {
   6251             if (in_len == 0) {
   6252                 // We have detected EOF from `in_fd`.
   6253                 break :rw;
   6254             } else {
   6255                 return total_written;
   6256             }
   6257         }
   6258         const amt_written = try write(out_fd, buf[0..amt_read]);
   6259         total_written += amt_written;
   6260         if (amt_written < in_len or in_len == 0) return total_written;
   6261     }
   6262 
   6263     if (trailers.len != 0) {
   6264         total_written += try writev(out_fd, trailers);
   6265     }
   6266 
   6267     return total_written;
   6268 }
   6269 
   6270 pub const CopyFileRangeError = error{
   6271     FileTooBig,
   6272     InputOutput,
   6273     /// `fd_in` is not open for reading; or `fd_out` is not open  for  writing;
   6274     /// or the  `O.APPEND`  flag  is  set  for `fd_out`.
   6275     FilesOpenedWithWrongFlags,
   6276     IsDir,
   6277     OutOfMemory,
   6278     NoSpaceLeft,
   6279     Unseekable,
   6280     PermissionDenied,
   6281     SwapFile,
   6282     CorruptedData,
   6283 } || PReadError || PWriteError || UnexpectedError;
   6284 
   6285 var has_copy_file_range_syscall = std.atomic.Atomic(bool).init(true);
   6286 
   6287 /// Transfer data between file descriptors at specified offsets.
   6288 /// Returns the number of bytes written, which can less than requested.
   6289 ///
   6290 /// The `copy_file_range` call copies `len` bytes from one file descriptor to another. When possible,
   6291 /// this is done within the operating system kernel, which can provide better performance
   6292 /// characteristics than transferring data from kernel to user space and back, such as with
   6293 /// `pread` and `pwrite` calls.
   6294 ///
   6295 /// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be a file descriptor
   6296 /// opened for writing. They may be any kind of file descriptor; however, if `fd_in` is not a regular
   6297 /// file system file, it may cause this function to fall back to calling `pread` and `pwrite`, in which case
   6298 /// atomicity guarantees no longer apply.
   6299 ///
   6300 /// If `fd_in` and `fd_out` are the same, source and target ranges must not overlap.
   6301 /// The file descriptor seek positions are ignored and not updated.
   6302 /// When `off_in` is past the end of the input file, it successfully reads 0 bytes.
   6303 ///
   6304 /// `flags` has different meanings per operating system; refer to the respective man pages.
   6305 ///
   6306 /// These systems support in-kernel data copying:
   6307 /// * Linux 4.5 (cross-filesystem 5.3)
   6308 /// * FreeBSD 13.0
   6309 ///
   6310 /// Other systems fall back to calling `pread` / `pwrite`.
   6311 ///
   6312 /// Maximum offsets on Linux and FreeBSD are `math.maxInt(i64)`.
   6313 pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {
   6314     if ((comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0 }) orelse false) or
   6315         ((comptime builtin.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) orelse false and
   6316         std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok) and
   6317         has_copy_file_range_syscall.load(.Monotonic)))
   6318     {
   6319         var off_in_copy = @bitCast(i64, off_in);
   6320         var off_out_copy = @bitCast(i64, off_out);
   6321 
   6322         while (true) {
   6323             const rc = system.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);
   6324             if (builtin.os.tag == .freebsd) {
   6325                 switch (system.getErrno(rc)) {
   6326                     .SUCCESS => return @intCast(usize, rc),
   6327                     .BADF => return error.FilesOpenedWithWrongFlags,
   6328                     .FBIG => return error.FileTooBig,
   6329                     .IO => return error.InputOutput,
   6330                     .ISDIR => return error.IsDir,
   6331                     .NOSPC => return error.NoSpaceLeft,
   6332                     .INVAL => break, // these may not be regular files, try fallback
   6333                     .INTEGRITY => return error.CorruptedData,
   6334                     .INTR => continue,
   6335                     else => |err| return unexpectedErrno(err),
   6336                 }
   6337             } else { // assume linux
   6338                 switch (system.getErrno(rc)) {
   6339                     .SUCCESS => return @intCast(usize, rc),
   6340                     .BADF => return error.FilesOpenedWithWrongFlags,
   6341                     .FBIG => return error.FileTooBig,
   6342                     .IO => return error.InputOutput,
   6343                     .ISDIR => return error.IsDir,
   6344                     .NOSPC => return error.NoSpaceLeft,
   6345                     .INVAL => break, // these may not be regular files, try fallback
   6346                     .NOMEM => return error.OutOfMemory,
   6347                     .OVERFLOW => return error.Unseekable,
   6348                     .PERM => return error.PermissionDenied,
   6349                     .TXTBSY => return error.SwapFile,
   6350                     .XDEV => break, // support for cross-filesystem copy added in Linux 5.3, use fallback
   6351                     .NOSYS => { // syscall added in Linux 4.5, use fallback
   6352                         has_copy_file_range_syscall.store(false, .Monotonic);
   6353                         break;
   6354                     },
   6355                     else => |err| return unexpectedErrno(err),
   6356                 }
   6357             }
   6358         }
   6359     }
   6360 
   6361     var buf: [8 * 4096]u8 = undefined;
   6362     const adjusted_count = @min(buf.len, len);
   6363     const amt_read = try pread(fd_in, buf[0..adjusted_count], off_in);
   6364     // TODO without @as the line below fails to compile for wasm32-wasi:
   6365     // error: integer value 0 cannot be coerced to type 'os.PWriteError!usize'
   6366     if (amt_read == 0) return @as(usize, 0);
   6367     return pwrite(fd_out, buf[0..amt_read], off_out);
   6368 }
   6369 
   6370 pub const PollError = error{
   6371     /// The network subsystem has failed.
   6372     NetworkSubsystemFailed,
   6373 
   6374     /// The kernel had no space to allocate file descriptor tables.
   6375     SystemResources,
   6376 } || UnexpectedError;
   6377 
   6378 pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
   6379     while (true) {
   6380         const fds_count = math.cast(nfds_t, fds.len) orelse return error.SystemResources;
   6381         const rc = system.poll(fds.ptr, fds_count, timeout);
   6382         if (builtin.os.tag == .windows) {
   6383             if (rc == windows.ws2_32.SOCKET_ERROR) {
   6384                 switch (windows.ws2_32.WSAGetLastError()) {
   6385                     .WSANOTINITIALISED => unreachable,
   6386                     .WSAENETDOWN => return error.NetworkSubsystemFailed,
   6387                     .WSAENOBUFS => return error.SystemResources,
   6388                     // TODO: handle more errors
   6389                     else => |err| return windows.unexpectedWSAError(err),
   6390                 }
   6391             } else {
   6392                 return @intCast(usize, rc);
   6393             }
   6394         } else {
   6395             switch (errno(rc)) {
   6396                 .SUCCESS => return @intCast(usize, rc),
   6397                 .FAULT => unreachable,
   6398                 .INTR => continue,
   6399                 .INVAL => unreachable,
   6400                 .NOMEM => return error.SystemResources,
   6401                 else => |err| return unexpectedErrno(err),
   6402             }
   6403         }
   6404         unreachable;
   6405     }
   6406 }
   6407 
   6408 pub const PPollError = error{
   6409     /// The operation was interrupted by a delivery of a signal before it could complete.
   6410     SignalInterrupt,
   6411 
   6412     /// The kernel had no space to allocate file descriptor tables.
   6413     SystemResources,
   6414 } || UnexpectedError;
   6415 
   6416 pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) PPollError!usize {
   6417     var ts: timespec = undefined;
   6418     var ts_ptr: ?*timespec = null;
   6419     if (timeout) |timeout_ns| {
   6420         ts_ptr = &ts;
   6421         ts = timeout_ns.*;
   6422     }
   6423     const fds_count = math.cast(nfds_t, fds.len) orelse return error.SystemResources;
   6424     const rc = system.ppoll(fds.ptr, fds_count, ts_ptr, mask);
   6425     switch (errno(rc)) {
   6426         .SUCCESS => return @intCast(usize, rc),
   6427         .FAULT => unreachable,
   6428         .INTR => return error.SignalInterrupt,
   6429         .INVAL => unreachable,
   6430         .NOMEM => return error.SystemResources,
   6431         else => |err| return unexpectedErrno(err),
   6432     }
   6433 }
   6434 
   6435 pub const RecvFromError = error{
   6436     /// The socket is marked nonblocking and the requested operation would block, and
   6437     /// there is no global event loop configured.
   6438     WouldBlock,
   6439 
   6440     /// A remote host refused to allow the network connection, typically because it is not
   6441     /// running the requested service.
   6442     ConnectionRefused,
   6443 
   6444     /// Could not allocate kernel memory.
   6445     SystemResources,
   6446 
   6447     ConnectionResetByPeer,
   6448 
   6449     /// The socket has not been bound.
   6450     SocketNotBound,
   6451 
   6452     /// The UDP message was too big for the buffer and part of it has been discarded
   6453     MessageTooBig,
   6454 
   6455     /// The network subsystem has failed.
   6456     NetworkSubsystemFailed,
   6457 
   6458     /// The socket is not connected (connection-oriented sockets only).
   6459     SocketNotConnected,
   6460 } || UnexpectedError;
   6461 
   6462 pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize {
   6463     return recvfrom(sock, buf, flags, null, null);
   6464 }
   6465 
   6466 /// If `sockfd` is opened in non blocking mode, the function will
   6467 /// return error.WouldBlock when EAGAIN is received.
   6468 pub fn recvfrom(
   6469     sockfd: socket_t,
   6470     buf: []u8,
   6471     flags: u32,
   6472     src_addr: ?*sockaddr,
   6473     addrlen: ?*socklen_t,
   6474 ) RecvFromError!usize {
   6475     while (true) {
   6476         const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
   6477         if (builtin.os.tag == .windows) {
   6478             if (rc == windows.ws2_32.SOCKET_ERROR) {
   6479                 switch (windows.ws2_32.WSAGetLastError()) {
   6480                     .WSANOTINITIALISED => unreachable,
   6481                     .WSAECONNRESET => return error.ConnectionResetByPeer,
   6482                     .WSAEINVAL => return error.SocketNotBound,
   6483                     .WSAEMSGSIZE => return error.MessageTooBig,
   6484                     .WSAENETDOWN => return error.NetworkSubsystemFailed,
   6485                     .WSAENOTCONN => return error.SocketNotConnected,
   6486                     .WSAEWOULDBLOCK => return error.WouldBlock,
   6487                     // TODO: handle more errors
   6488                     else => |err| return windows.unexpectedWSAError(err),
   6489                 }
   6490             } else {
   6491                 return @intCast(usize, rc);
   6492             }
   6493         } else {
   6494             switch (errno(rc)) {
   6495                 .SUCCESS => return @intCast(usize, rc),
   6496                 .BADF => unreachable, // always a race condition
   6497                 .FAULT => unreachable,
   6498                 .INVAL => unreachable,
   6499                 .NOTCONN => return error.SocketNotConnected,
   6500                 .NOTSOCK => unreachable,
   6501                 .INTR => continue,
   6502                 .AGAIN => return error.WouldBlock,
   6503                 .NOMEM => return error.SystemResources,
   6504                 .CONNREFUSED => return error.ConnectionRefused,
   6505                 .CONNRESET => return error.ConnectionResetByPeer,
   6506                 else => |err| return unexpectedErrno(err),
   6507             }
   6508         }
   6509     }
   6510 }
   6511 
   6512 pub const DnExpandError = error{InvalidDnsPacket};
   6513 
   6514 pub fn dn_expand(
   6515     msg: []const u8,
   6516     comp_dn: []const u8,
   6517     exp_dn: []u8,
   6518 ) DnExpandError!usize {
   6519     // This implementation is ported from musl libc.
   6520     // A more idiomatic "ziggy" implementation would be welcome.
   6521     var p = comp_dn.ptr;
   6522     var len: usize = std.math.maxInt(usize);
   6523     const end = msg.ptr + msg.len;
   6524     if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket;
   6525     var dest = exp_dn.ptr;
   6526     const dend = dest + @min(exp_dn.len, 254);
   6527     // detect reference loop using an iteration counter
   6528     var i: usize = 0;
   6529     while (i < msg.len) : (i += 2) {
   6530         // loop invariants: p<end, dest<dend
   6531         if ((p[0] & 0xc0) != 0) {
   6532             if (p + 1 == end) return error.InvalidDnsPacket;
   6533             var j = ((p[0] & @as(usize, 0x3f)) << 8) | p[1];
   6534             if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 2 - @ptrToInt(comp_dn.ptr);
   6535             if (j >= msg.len) return error.InvalidDnsPacket;
   6536             p = msg.ptr + j;
   6537         } else if (p[0] != 0) {
   6538             if (dest != exp_dn.ptr) {
   6539                 dest[0] = '.';
   6540                 dest += 1;
   6541             }
   6542             var j = p[0];
   6543             p += 1;
   6544             if (j >= @ptrToInt(end) - @ptrToInt(p) or j >= @ptrToInt(dend) - @ptrToInt(dest)) {
   6545                 return error.InvalidDnsPacket;
   6546             }
   6547             while (j != 0) {
   6548                 j -= 1;
   6549                 dest[0] = p[0];
   6550                 dest += 1;
   6551                 p += 1;
   6552             }
   6553         } else {
   6554             dest[0] = 0;
   6555             if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 1 - @ptrToInt(comp_dn.ptr);
   6556             return len;
   6557         }
   6558     }
   6559     return error.InvalidDnsPacket;
   6560 }
   6561 
   6562 pub const SetSockOptError = error{
   6563     /// The socket is already connected, and a specified option cannot be set while the socket is connected.
   6564     AlreadyConnected,
   6565 
   6566     /// The option is not supported by the protocol.
   6567     InvalidProtocolOption,
   6568 
   6569     /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure.
   6570     TimeoutTooBig,
   6571 
   6572     /// Insufficient resources are available in the system to complete the call.
   6573     SystemResources,
   6574 
   6575     // Setting the socket option requires more elevated permissions.
   6576     PermissionDenied,
   6577 
   6578     NetworkSubsystemFailed,
   6579     FileDescriptorNotASocket,
   6580     SocketNotBound,
   6581     NoDevice,
   6582 } || UnexpectedError;
   6583 
   6584 /// Set a socket's options.
   6585 pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
   6586     if (builtin.os.tag == .windows) {
   6587         const rc = windows.ws2_32.setsockopt(fd, @intCast(i32, level), @intCast(i32, optname), opt.ptr, @intCast(i32, opt.len));
   6588         if (rc == windows.ws2_32.SOCKET_ERROR) {
   6589             switch (windows.ws2_32.WSAGetLastError()) {
   6590                 .WSANOTINITIALISED => unreachable,
   6591                 .WSAENETDOWN => return error.NetworkSubsystemFailed,
   6592                 .WSAEFAULT => unreachable,
   6593                 .WSAENOTSOCK => return error.FileDescriptorNotASocket,
   6594                 .WSAEINVAL => return error.SocketNotBound,
   6595                 else => |err| return windows.unexpectedWSAError(err),
   6596             }
   6597         }
   6598         return;
   6599     } else {
   6600         switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) {
   6601             .SUCCESS => {},
   6602             .BADF => unreachable, // always a race condition
   6603             .NOTSOCK => unreachable, // always a race condition
   6604             .INVAL => unreachable,
   6605             .FAULT => unreachable,
   6606             .DOM => return error.TimeoutTooBig,
   6607             .ISCONN => return error.AlreadyConnected,
   6608             .NOPROTOOPT => return error.InvalidProtocolOption,
   6609             .NOMEM => return error.SystemResources,
   6610             .NOBUFS => return error.SystemResources,
   6611             .PERM => return error.PermissionDenied,
   6612             .NODEV => return error.NoDevice,
   6613             else => |err| return unexpectedErrno(err),
   6614         }
   6615     }
   6616 }
   6617 
   6618 pub const MemFdCreateError = error{
   6619     SystemFdQuotaExceeded,
   6620     ProcessFdQuotaExceeded,
   6621     OutOfMemory,
   6622 
   6623     /// memfd_create is available in Linux 3.17 and later. This error is returned
   6624     /// for older kernel versions.
   6625     SystemOutdated,
   6626 } || UnexpectedError;
   6627 
   6628 pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t {
   6629     switch (builtin.os.tag) {
   6630         .linux => {
   6631             // memfd_create is available only in glibc versions starting with 2.27.
   6632             const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;
   6633             const sys = if (use_c) std.c else linux;
   6634             const getErrno = if (use_c) std.c.getErrno else linux.getErrno;
   6635             const rc = sys.memfd_create(name, flags);
   6636             switch (getErrno(rc)) {
   6637                 .SUCCESS => return @intCast(fd_t, rc),
   6638                 .FAULT => unreachable, // name has invalid memory
   6639                 .INVAL => unreachable, // name/flags are faulty
   6640                 .NFILE => return error.SystemFdQuotaExceeded,
   6641                 .MFILE => return error.ProcessFdQuotaExceeded,
   6642                 .NOMEM => return error.OutOfMemory,
   6643                 .NOSYS => return error.SystemOutdated,
   6644                 else => |err| return unexpectedErrno(err),
   6645             }
   6646         },
   6647         .freebsd => {
   6648             if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0 }) == .lt)
   6649                 @compileError("memfd_create is unavailable on FreeBSD < 13.0");
   6650             const rc = system.memfd_create(name, flags);
   6651             switch (errno(rc)) {
   6652                 .SUCCESS => return rc,
   6653                 .BADF => unreachable, // name argument NULL
   6654                 .INVAL => unreachable, // name too long or invalid/unsupported flags.
   6655                 .MFILE => return error.ProcessFdQuotaExceeded,
   6656                 .NFILE => return error.SystemFdQuotaExceeded,
   6657                 .NOSYS => return error.SystemOutdated,
   6658                 else => |err| return unexpectedErrno(err),
   6659             }
   6660         },
   6661         else => @compileError("target OS does not support memfd_create()"),
   6662     }
   6663 }
   6664 
   6665 pub const MFD_NAME_PREFIX = "memfd:";
   6666 pub const MFD_MAX_NAME_LEN = NAME_MAX - MFD_NAME_PREFIX.len;
   6667 fn toMemFdPath(name: []const u8) ![MFD_MAX_NAME_LEN:0]u8 {
   6668     var path_with_null: [MFD_MAX_NAME_LEN:0]u8 = undefined;
   6669     // >= rather than > to make room for the null byte
   6670     if (name.len >= MFD_MAX_NAME_LEN) return error.NameTooLong;
   6671     mem.copy(u8, &path_with_null, name);
   6672     path_with_null[name.len] = 0;
   6673     return path_with_null;
   6674 }
   6675 
   6676 pub fn memfd_create(name: []const u8, flags: u32) !fd_t {
   6677     const name_t = try toMemFdPath(name);
   6678     return memfd_createZ(&name_t, flags);
   6679 }
   6680 
   6681 pub fn getrusage(who: i32) rusage {
   6682     var result: rusage = undefined;
   6683     const rc = system.getrusage(who, &result);
   6684     switch (errno(rc)) {
   6685         .SUCCESS => return result,
   6686         .INVAL => unreachable,
   6687         .FAULT => unreachable,
   6688         else => unreachable,
   6689     }
   6690 }
   6691 
   6692 pub const TermiosGetError = error{NotATerminal} || UnexpectedError;
   6693 
   6694 pub fn tcgetattr(handle: fd_t) TermiosGetError!termios {
   6695     while (true) {
   6696         var term: termios = undefined;
   6697         switch (errno(system.tcgetattr(handle, &term))) {
   6698             .SUCCESS => return term,
   6699             .INTR => continue,
   6700             .BADF => unreachable,
   6701             .NOTTY => return error.NotATerminal,
   6702             else => |err| return unexpectedErrno(err),
   6703         }
   6704     }
   6705 }
   6706 
   6707 pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned};
   6708 
   6709 pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) TermiosSetError!void {
   6710     while (true) {
   6711         switch (errno(system.tcsetattr(handle, optional_action, &termios_p))) {
   6712             .SUCCESS => return,
   6713             .BADF => unreachable,
   6714             .INTR => continue,
   6715             .INVAL => unreachable,
   6716             .NOTTY => return error.NotATerminal,
   6717             .IO => return error.ProcessOrphaned,
   6718             else => |err| return unexpectedErrno(err),
   6719         }
   6720     }
   6721 }
   6722 
   6723 pub const IoCtl_SIOCGIFINDEX_Error = error{
   6724     FileSystem,
   6725     InterfaceNotFound,
   6726 } || UnexpectedError;
   6727 
   6728 pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
   6729     while (true) {
   6730         switch (errno(system.ioctl(fd, SIOCGIFINDEX, @ptrToInt(ifr)))) {
   6731             .SUCCESS => return,
   6732             .INVAL => unreachable, // Bad parameters.
   6733             .NOTTY => unreachable,
   6734             .NXIO => unreachable,
   6735             .BADF => unreachable, // Always a race condition.
   6736             .FAULT => unreachable, // Bad pointer parameter.
   6737             .INTR => continue,
   6738             .IO => return error.FileSystem,
   6739             .NODEV => return error.InterfaceNotFound,
   6740             else => |err| return unexpectedErrno(err),
   6741         }
   6742     }
   6743 }
   6744 
   6745 pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
   6746     const rc = system.signalfd(fd, mask, flags);
   6747     switch (errno(rc)) {
   6748         .SUCCESS => return @intCast(fd_t, rc),
   6749         .BADF, .INVAL => unreachable,
   6750         .NFILE => return error.SystemFdQuotaExceeded,
   6751         .NOMEM => return error.SystemResources,
   6752         .MFILE => return error.ProcessResources,
   6753         .NODEV => return error.InodeMountFail,
   6754         .NOSYS => return error.SystemOutdated,
   6755         else => |err| return unexpectedErrno(err),
   6756     }
   6757 }
   6758 
   6759 pub const SyncError = error{
   6760     InputOutput,
   6761     NoSpaceLeft,
   6762     DiskQuota,
   6763     AccessDenied,
   6764 } || UnexpectedError;
   6765 
   6766 /// Write all pending file contents and metadata modifications to all filesystems.
   6767 pub fn sync() void {
   6768     system.sync();
   6769 }
   6770 
   6771 /// Write all pending file contents and metadata modifications to the filesystem which contains the specified file.
   6772 pub fn syncfs(fd: fd_t) SyncError!void {
   6773     const rc = system.syncfs(fd);
   6774     switch (errno(rc)) {
   6775         .SUCCESS => return,
   6776         .BADF, .INVAL, .ROFS => unreachable,
   6777         .IO => return error.InputOutput,
   6778         .NOSPC => return error.NoSpaceLeft,
   6779         .DQUOT => return error.DiskQuota,
   6780         else => |err| return unexpectedErrno(err),
   6781     }
   6782 }
   6783 
   6784 /// Write all pending file contents and metadata modifications for the specified file descriptor to the underlying filesystem.
   6785 pub fn fsync(fd: fd_t) SyncError!void {
   6786     if (builtin.os.tag == .windows) {
   6787         if (windows.kernel32.FlushFileBuffers(fd) != 0)
   6788             return;
   6789         switch (windows.kernel32.GetLastError()) {
   6790             .SUCCESS => return,
   6791             .INVALID_HANDLE => unreachable,
   6792             .ACCESS_DENIED => return error.AccessDenied, // a sync was performed but the system couldn't update the access time
   6793             .UNEXP_NET_ERR => return error.InputOutput,
   6794             else => return error.InputOutput,
   6795         }
   6796     }
   6797     const rc = system.fsync(fd);
   6798     switch (errno(rc)) {
   6799         .SUCCESS => return,
   6800         .BADF, .INVAL, .ROFS => unreachable,
   6801         .IO => return error.InputOutput,
   6802         .NOSPC => return error.NoSpaceLeft,
   6803         .DQUOT => return error.DiskQuota,
   6804         else => |err| return unexpectedErrno(err),
   6805     }
   6806 }
   6807 
   6808 /// Write all pending file contents for the specified file descriptor to the underlying filesystem, but not necessarily the metadata.
   6809 pub fn fdatasync(fd: fd_t) SyncError!void {
   6810     if (builtin.os.tag == .windows) {
   6811         return fsync(fd) catch |err| switch (err) {
   6812             SyncError.AccessDenied => return, // fdatasync doesn't promise that the access time was synced
   6813             else => return err,
   6814         };
   6815     }
   6816     const rc = system.fdatasync(fd);
   6817     switch (errno(rc)) {
   6818         .SUCCESS => return,
   6819         .BADF, .INVAL, .ROFS => unreachable,
   6820         .IO => return error.InputOutput,
   6821         .NOSPC => return error.NoSpaceLeft,
   6822         .DQUOT => return error.DiskQuota,
   6823         else => |err| return unexpectedErrno(err),
   6824     }
   6825 }
   6826 
   6827 pub const PrctlError = error{
   6828     /// Can only occur with PR_SET_SECCOMP/SECCOMP_MODE_FILTER or
   6829     /// PR_SET_MM/PR_SET_MM_EXE_FILE
   6830     AccessDenied,
   6831     /// Can only occur with PR_SET_MM/PR_SET_MM_EXE_FILE
   6832     InvalidFileDescriptor,
   6833     InvalidAddress,
   6834     /// Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT,
   6835     /// or PR_MPX_DISABLE_MANAGEMENT
   6836     UnsupportedFeature,
   6837     /// Can only occur wih PR_SET_FP_MODE
   6838     OperationNotSupported,
   6839     PermissionDenied,
   6840 } || UnexpectedError;
   6841 
   6842 pub fn prctl(option: PR, args: anytype) PrctlError!u31 {
   6843     if (@typeInfo(@TypeOf(args)) != .Struct)
   6844         @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
   6845     if (args.len > 4)
   6846         @compileError("prctl takes a maximum of 4 optional arguments");
   6847 
   6848     var buf: [4]usize = undefined;
   6849     {
   6850         comptime var i = 0;
   6851         inline while (i < args.len) : (i += 1) buf[i] = args[i];
   6852     }
   6853 
   6854     const rc = system.prctl(@enumToInt(option), buf[0], buf[1], buf[2], buf[3]);
   6855     switch (errno(rc)) {
   6856         .SUCCESS => return @intCast(u31, rc),
   6857         .ACCES => return error.AccessDenied,
   6858         .BADF => return error.InvalidFileDescriptor,
   6859         .FAULT => return error.InvalidAddress,
   6860         .INVAL => unreachable,
   6861         .NODEV, .NXIO => return error.UnsupportedFeature,
   6862         .OPNOTSUPP => return error.OperationNotSupported,
   6863         .PERM, .BUSY => return error.PermissionDenied,
   6864         .RANGE => unreachable,
   6865         else => |err| return unexpectedErrno(err),
   6866     }
   6867 }
   6868 
   6869 pub const GetrlimitError = UnexpectedError;
   6870 
   6871 pub fn getrlimit(resource: rlimit_resource) GetrlimitError!rlimit {
   6872     const getrlimit_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   6873         system.getrlimit64
   6874     else
   6875         system.getrlimit;
   6876 
   6877     var limits: rlimit = undefined;
   6878     switch (errno(getrlimit_sym(resource, &limits))) {
   6879         .SUCCESS => return limits,
   6880         .FAULT => unreachable, // bogus pointer
   6881         .INVAL => unreachable,
   6882         else => |err| return unexpectedErrno(err),
   6883     }
   6884 }
   6885 
   6886 pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError;
   6887 
   6888 pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void {
   6889     const setrlimit_sym = if (builtin.os.tag == .linux and builtin.link_libc)
   6890         system.setrlimit64
   6891     else
   6892         system.setrlimit;
   6893 
   6894     switch (errno(setrlimit_sym(resource, &limits))) {
   6895         .SUCCESS => return,
   6896         .FAULT => unreachable, // bogus pointer
   6897         .INVAL => return error.LimitTooBig, // this could also mean "invalid resource", but that would be unreachable
   6898         .PERM => return error.PermissionDenied,
   6899         else => |err| return unexpectedErrno(err),
   6900     }
   6901 }
   6902 
   6903 pub const MadviseError = error{
   6904     /// advice is MADV.REMOVE, but the specified address range is not a shared writable mapping.
   6905     AccessDenied,
   6906     /// advice is MADV.HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability.
   6907     PermissionDenied,
   6908     /// A kernel resource was temporarily unavailable.
   6909     SystemResources,
   6910     /// One of the following:
   6911     /// * addr is not page-aligned or length is negative
   6912     /// * advice is not valid
   6913     /// * advice is MADV.DONTNEED or MADV.REMOVE and the specified address range
   6914     ///   includes locked, Huge TLB pages, or VM_PFNMAP pages.
   6915     /// * advice is MADV.MERGEABLE or MADV.UNMERGEABLE, but the kernel was not
   6916     ///   configured with CONFIG_KSM.
   6917     /// * advice is MADV.FREE or MADV.WIPEONFORK but the specified address range
   6918     ///   includes file, Huge TLB, MAP.SHARED, or VM_PFNMAP ranges.
   6919     InvalidSyscall,
   6920     /// (for MADV.WILLNEED) Paging in this area would exceed the process's
   6921     /// maximum resident set size.
   6922     WouldExceedMaximumResidentSetSize,
   6923     /// One of the following:
   6924     /// * (for MADV.WILLNEED) Not enough memory: paging in failed.
   6925     /// * Addresses in the specified range are not currently mapped, or
   6926     ///   are outside the address space of the process.
   6927     OutOfMemory,
   6928     /// The madvise syscall is not available on this version and configuration
   6929     /// of the Linux kernel.
   6930     MadviseUnavailable,
   6931     /// The operating system returned an undocumented error code.
   6932     Unexpected,
   6933 };
   6934 
   6935 /// Give advice about use of memory.
   6936 /// This syscall is optional and is sometimes configured to be disabled.
   6937 pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
   6938     switch (errno(system.madvise(ptr, length, advice))) {
   6939         .SUCCESS => return,
   6940         .ACCES => return error.AccessDenied,
   6941         .AGAIN => return error.SystemResources,
   6942         .BADF => unreachable, // The map exists, but the area maps something that isn't a file.
   6943         .INVAL => return error.InvalidSyscall,
   6944         .IO => return error.WouldExceedMaximumResidentSetSize,
   6945         .NOMEM => return error.OutOfMemory,
   6946         .NOSYS => return error.MadviseUnavailable,
   6947         else => |err| return unexpectedErrno(err),
   6948     }
   6949 }
   6950 
   6951 pub const PerfEventOpenError = error{
   6952     /// Returned if the perf_event_attr size value is too small (smaller
   6953     /// than PERF_ATTR_SIZE_VER0), too big (larger than the page  size),
   6954     /// or  larger  than the kernel supports and the extra bytes are not
   6955     /// zero.  When E2BIG is returned, the perf_event_attr size field is
   6956     /// overwritten by the kernel to be the size of the structure it was
   6957     /// expecting.
   6958     TooBig,
   6959     /// Returned when the requested event requires CAP_SYS_ADMIN permis‐
   6960     /// sions  (or a more permissive perf_event paranoid setting).  Some
   6961     /// common cases where an unprivileged process  may  encounter  this
   6962     /// error:  attaching  to a process owned by a different user; moni‐
   6963     /// toring all processes on a given CPU (i.e.,  specifying  the  pid
   6964     /// argument  as  -1); and not setting exclude_kernel when the para‐
   6965     /// noid setting requires it.
   6966     /// Also:
   6967     /// Returned on many (but not all) architectures when an unsupported
   6968     /// exclude_hv,  exclude_idle,  exclude_user, or exclude_kernel set‐
   6969     /// ting is specified.
   6970     /// It can also happen, as with EACCES, when the requested event re‐
   6971     /// quires   CAP_SYS_ADMIN   permissions   (or   a  more  permissive
   6972     /// perf_event paranoid setting).  This includes  setting  a  break‐
   6973     /// point on a kernel address, and (since Linux 3.13) setting a ker‐
   6974     /// nel function-trace tracepoint.
   6975     PermissionDenied,
   6976     /// Returned if another event already has exclusive  access  to  the
   6977     /// PMU.
   6978     DeviceBusy,
   6979     /// Each  opened  event uses one file descriptor.  If a large number
   6980     /// of events are opened, the per-process limit  on  the  number  of
   6981     /// open file descriptors will be reached, and no more events can be
   6982     /// created.
   6983     ProcessResources,
   6984     EventRequiresUnsupportedCpuFeature,
   6985     /// Returned if  you  try  to  add  more  breakpoint
   6986     /// events than supported by the hardware.
   6987     TooManyBreakpoints,
   6988     /// Returned  if PERF_SAMPLE_STACK_USER is set in sample_type and it
   6989     /// is not supported by hardware.
   6990     SampleStackNotSupported,
   6991     /// Returned if an event requiring a specific  hardware  feature  is
   6992     /// requested  but  there is no hardware support.  This includes re‐
   6993     /// questing low-skid events if not supported, branch tracing if  it
   6994     /// is not available, sampling if no PMU interrupt is available, and
   6995     /// branch stacks for software events.
   6996     EventNotSupported,
   6997     /// Returned  if  PERF_SAMPLE_CALLCHAIN  is   requested   and   sam‐
   6998     /// ple_max_stack   is   larger   than   the  maximum  specified  in
   6999     /// /proc/sys/kernel/perf_event_max_stack.
   7000     SampleMaxStackOverflow,
   7001     /// Returned if attempting to attach to a process that does not  exist.
   7002     ProcessNotFound,
   7003 } || UnexpectedError;
   7004 
   7005 pub fn perf_event_open(
   7006     attr: *linux.perf_event_attr,
   7007     pid: pid_t,
   7008     cpu: i32,
   7009     group_fd: fd_t,
   7010     flags: usize,
   7011 ) PerfEventOpenError!fd_t {
   7012     const rc = system.perf_event_open(attr, pid, cpu, group_fd, flags);
   7013     switch (errno(rc)) {
   7014         .SUCCESS => return @intCast(fd_t, rc),
   7015         .@"2BIG" => return error.TooBig,
   7016         .ACCES => return error.PermissionDenied,
   7017         .BADF => unreachable, // group_fd file descriptor is not valid.
   7018         .BUSY => return error.DeviceBusy,
   7019         .FAULT => unreachable, // Segmentation fault.
   7020         .INVAL => unreachable, // Bad attr settings.
   7021         .INTR => unreachable, // Mixed perf and ftrace handling for a uprobe.
   7022         .MFILE => return error.ProcessResources,
   7023         .NODEV => return error.EventRequiresUnsupportedCpuFeature,
   7024         .NOENT => unreachable, // Invalid type setting.
   7025         .NOSPC => return error.TooManyBreakpoints,
   7026         .NOSYS => return error.SampleStackNotSupported,
   7027         .OPNOTSUPP => return error.EventNotSupported,
   7028         .OVERFLOW => return error.SampleMaxStackOverflow,
   7029         .PERM => return error.PermissionDenied,
   7030         .SRCH => return error.ProcessNotFound,
   7031         else => |err| return unexpectedErrno(err),
   7032     }
   7033 }
   7034 
   7035 pub const TimerFdCreateError = error{
   7036     AccessDenied,
   7037     ProcessFdQuotaExceeded,
   7038     SystemFdQuotaExceeded,
   7039     NoDevice,
   7040     SystemResources,
   7041 } || UnexpectedError;
   7042 
   7043 pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError;
   7044 pub const TimerFdSetError = TimerFdGetError || error{Canceled};
   7045 
   7046 pub fn timerfd_create(clokid: i32, flags: u32) TimerFdCreateError!fd_t {
   7047     var rc = linux.timerfd_create(clokid, flags);
   7048     return switch (errno(rc)) {
   7049         .SUCCESS => @intCast(fd_t, rc),
   7050         .INVAL => unreachable,
   7051         .MFILE => return error.ProcessFdQuotaExceeded,
   7052         .NFILE => return error.SystemFdQuotaExceeded,
   7053         .NODEV => return error.NoDevice,
   7054         .NOMEM => return error.SystemResources,
   7055         .PERM => return error.AccessDenied,
   7056         else => |err| return unexpectedErrno(err),
   7057     };
   7058 }
   7059 
   7060 pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const linux.itimerspec, old_value: ?*linux.itimerspec) TimerFdSetError!void {
   7061     var rc = linux.timerfd_settime(fd, flags, new_value, old_value);
   7062     return switch (errno(rc)) {
   7063         .SUCCESS => {},
   7064         .BADF => error.InvalidHandle,
   7065         .FAULT => unreachable,
   7066         .INVAL => unreachable,
   7067         .CANCELED => error.Canceled,
   7068         else => |err| return unexpectedErrno(err),
   7069     };
   7070 }
   7071 
   7072 pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
   7073     var curr_value: linux.itimerspec = undefined;
   7074     var rc = linux.timerfd_gettime(fd, &curr_value);
   7075     return switch (errno(rc)) {
   7076         .SUCCESS => return curr_value,
   7077         .BADF => error.InvalidHandle,
   7078         .FAULT => unreachable,
   7079         .INVAL => unreachable,
   7080         else => |err| return unexpectedErrno(err),
   7081     };
   7082 }
   7083 
   7084 pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE");
   7085 
   7086 fn noopSigHandler(_: c_int) callconv(.C) void {}
   7087 
   7088 pub fn maybeIgnoreSigpipe() void {
   7089     if (have_sigpipe_support and !std.options.keep_sigpipe) {
   7090         const act = Sigaction{
   7091             // We set handler to a noop function instead of SIG.IGN so we don't leak our
   7092             // signal disposition to a child process
   7093             .handler = .{ .handler = noopSigHandler },
   7094             .mask = empty_sigset,
   7095             .flags = 0,
   7096         };
   7097         sigaction(SIG.PIPE, &act, null) catch |err|
   7098             std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)});
   7099     }
   7100 }