zig

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

blob e8431c38 (210027B) - Raw


      1 // SPDX-License-Identifier: MIT
      2 // Copyright (c) 2015-2020 Zig Contributors
      3 // This file is part of [zig](https://ziglang.org/), which is MIT licensed.
      4 // The MIT license requires this copyright notice to be included in all copies
      5 // and substantial portions of the software.
      6 // This file contains thin wrappers around OS-specific APIs, with these
      7 // specific goals in mind:
      8 // * Convert "errno"-style error codes into Zig errors.
      9 // * When null-terminated byte buffers are required, provide APIs which accept
     10 //   slices as well as APIs which accept null-terminated byte buffers. Same goes
     11 //   for UTF-16LE encoding.
     12 // * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide
     13 //   cross platform abstracting.
     14 // * When there exists a corresponding libc function and linking libc, the libc
     15 //   implementation is used. Exceptions are made for known buggy areas of libc.
     16 //   On Linux libc can be side-stepped by using `std.os.linux` directly.
     17 // * For Windows, this file represents the API that libc would provide for
     18 //   Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.
     19 // Note: The Zig standard library does not support POSIX thread cancellation, and
     20 // in general EINTR is handled by trying again.
     21 
     22 const root = @import("root");
     23 const std = @import("std.zig");
     24 const builtin = @import("builtin");
     25 const assert = std.debug.assert;
     26 const math = std.math;
     27 const mem = std.mem;
     28 const elf = std.elf;
     29 const dl = @import("dynamic_library.zig");
     30 const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;
     31 
     32 pub const darwin = @import("os/darwin.zig");
     33 pub const dragonfly = @import("os/dragonfly.zig");
     34 pub const freebsd = @import("os/freebsd.zig");
     35 pub const netbsd = @import("os/netbsd.zig");
     36 pub const linux = @import("os/linux.zig");
     37 pub const uefi = @import("os/uefi.zig");
     38 pub const wasi = @import("os/wasi.zig");
     39 pub const windows = @import("os/windows.zig");
     40 
     41 comptime {
     42     assert(@import("std") == std); // std lib tests require --override-lib-dir
     43 }
     44 
     45 test "" {
     46     _ = darwin;
     47     _ = freebsd;
     48     _ = linux;
     49     _ = netbsd;
     50     _ = uefi;
     51     _ = wasi;
     52     _ = windows;
     53 
     54     _ = @import("os/test.zig");
     55 }
     56 
     57 /// Applications can override the `system` API layer in their root source file.
     58 /// Otherwise, when linking libc, this is the C API.
     59 /// When not linking libc, it is the OS-specific system interface.
     60 pub const system = if (@hasDecl(root, "os") and root.os != @This())
     61     root.os.system
     62 else if (builtin.link_libc)
     63     std.c
     64 else switch (builtin.os.tag) {
     65     .macosx, .ios, .watchos, .tvos => darwin,
     66     .freebsd => freebsd,
     67     .linux => linux,
     68     .netbsd => netbsd,
     69     .dragonfly => dragonfly,
     70     .wasi => wasi,
     71     .windows => windows,
     72     else => struct {},
     73 };
     74 
     75 pub usingnamespace @import("os/bits.zig");
     76 
     77 pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t;
     78 
     79 /// See also `getenv`. Populated by startup code before main().
     80 /// TODO this is a footgun because the value will be undefined when using `zig build-lib`.
     81 /// https://github.com/ziglang/zig/issues/4524
     82 pub var environ: [][*:0]u8 = undefined;
     83 
     84 /// Populated by startup code before main().
     85 /// Not available on Windows. See `std.process.args`
     86 /// for obtaining the process arguments.
     87 pub var argv: [][*:0]u8 = undefined;
     88 
     89 /// To obtain errno, call this function with the return value of the
     90 /// system function call. For some systems this will obtain the value directly
     91 /// from the return code; for others it will use a thread-local errno variable.
     92 /// Therefore, this function only returns a well-defined value when it is called
     93 /// directly after the system function call which one wants to learn the errno
     94 /// value of.
     95 pub const errno = system.getErrno;
     96 
     97 /// Closes the file descriptor.
     98 /// This function is not capable of returning any indication of failure. An
     99 /// application which wants to ensure writes have succeeded before closing
    100 /// must call `fsync` before `close`.
    101 /// Note: The Zig standard library does not support POSIX thread cancellation.
    102 pub fn close(fd: fd_t) void {
    103     if (builtin.os.tag == .windows) {
    104         return windows.CloseHandle(fd);
    105     }
    106     if (builtin.os.tag == .wasi) {
    107         _ = wasi.fd_close(fd);
    108         return;
    109     }
    110     if (comptime std.Target.current.isDarwin()) {
    111         // This avoids the EINTR problem.
    112         switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) {
    113             EBADF => unreachable, // Always a race condition.
    114             else => return,
    115         }
    116     }
    117     switch (errno(system.close(fd))) {
    118         EBADF => unreachable, // Always a race condition.
    119         EINTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
    120         else => return,
    121     }
    122 }
    123 
    124 pub const GetRandomError = OpenError;
    125 
    126 /// Obtain a series of random bytes. These bytes can be used to seed user-space
    127 /// random number generators or for cryptographic purposes.
    128 /// When linking against libc, this calls the
    129 /// appropriate OS-specific library call. Otherwise it uses the zig standard
    130 /// library implementation.
    131 pub fn getrandom(buffer: []u8) GetRandomError!void {
    132     if (builtin.os.tag == .windows) {
    133         return windows.RtlGenRandom(buffer);
    134     }
    135     if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) {
    136         var buf = buffer;
    137         const use_c = builtin.os.tag != .linux or
    138             std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok;
    139 
    140         while (buf.len != 0) {
    141             var err: u16 = undefined;
    142 
    143             const num_read = if (use_c) blk: {
    144                 const rc = std.c.getrandom(buf.ptr, buf.len, 0);
    145                 err = std.c.getErrno(rc);
    146                 break :blk @bitCast(usize, rc);
    147             } else blk: {
    148                 const rc = linux.getrandom(buf.ptr, buf.len, 0);
    149                 err = linux.getErrno(rc);
    150                 break :blk rc;
    151             };
    152 
    153             switch (err) {
    154                 0 => buf = buf[num_read..],
    155                 EINVAL => unreachable,
    156                 EFAULT => unreachable,
    157                 EINTR => continue,
    158                 ENOSYS => return getRandomBytesDevURandom(buf),
    159                 else => return unexpectedErrno(err),
    160             }
    161         }
    162         return;
    163     }
    164     if (builtin.os.tag == .netbsd) {
    165         netbsd.arc4random_buf(buffer.ptr, buffer.len);
    166         return;
    167     }
    168     if (builtin.os.tag == .wasi) {
    169         switch (wasi.random_get(buffer.ptr, buffer.len)) {
    170             0 => return,
    171             else => |err| return unexpectedErrno(err),
    172         }
    173     }
    174     return getRandomBytesDevURandom(buffer);
    175 }
    176 
    177 fn getRandomBytesDevURandom(buf: []u8) !void {
    178     const fd = try openZ("/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
    179     defer close(fd);
    180 
    181     const st = try fstat(fd);
    182     if (!S_ISCHR(st.mode)) {
    183         return error.NoDevice;
    184     }
    185 
    186     const file = std.fs.File{
    187         .handle = fd,
    188         .capable_io_mode = .blocking,
    189         .intended_io_mode = .blocking,
    190     };
    191     const stream = file.inStream();
    192     stream.readNoEof(buf) catch return error.Unexpected;
    193 }
    194 
    195 /// Causes abnormal process termination.
    196 /// If linking against libc, this calls the abort() libc function. Otherwise
    197 /// it raises SIGABRT followed by SIGKILL and finally lo
    198 pub fn abort() noreturn {
    199     @setCold(true);
    200     // MSVCRT abort() sometimes opens a popup window which is undesirable, so
    201     // even when linking libc on Windows we use our own abort implementation.
    202     // See https://github.com/ziglang/zig/issues/2071 for more details.
    203     if (builtin.os.tag == .windows) {
    204         if (builtin.mode == .Debug) {
    205             @breakpoint();
    206         }
    207         windows.kernel32.ExitProcess(3);
    208     }
    209     if (!builtin.link_libc and builtin.os.tag == .linux) {
    210         raise(SIGABRT) catch {};
    211 
    212         // TODO the rest of the implementation of abort() from musl libc here
    213 
    214         raise(SIGKILL) catch {};
    215         exit(127);
    216     }
    217     if (builtin.os.tag == .uefi) {
    218         exit(0); // TODO choose appropriate exit code
    219     }
    220     if (builtin.os.tag == .wasi) {
    221         @breakpoint();
    222         exit(1);
    223     }
    224 
    225     system.abort();
    226 }
    227 
    228 pub const RaiseError = UnexpectedError;
    229 
    230 pub fn raise(sig: u8) RaiseError!void {
    231     if (builtin.link_libc) {
    232         switch (errno(system.raise(sig))) {
    233             0 => return,
    234             else => |err| return unexpectedErrno(err),
    235         }
    236     }
    237 
    238     if (builtin.os.tag == .linux) {
    239         var set: linux.sigset_t = undefined;
    240         // block application signals
    241         _ = linux.sigprocmask(SIG_BLOCK, &linux.app_mask, &set);
    242 
    243         const tid = linux.gettid();
    244         const rc = linux.tkill(tid, sig);
    245 
    246         // restore signal mask
    247         _ = linux.sigprocmask(SIG_SETMASK, &set, null);
    248 
    249         switch (errno(rc)) {
    250             0 => return,
    251             else => |err| return unexpectedErrno(err),
    252         }
    253     }
    254 
    255     @compileError("std.os.raise unimplemented for this target");
    256 }
    257 
    258 pub const KillError = error{PermissionDenied} || UnexpectedError;
    259 
    260 pub fn kill(pid: pid_t, sig: u8) KillError!void {
    261     switch (errno(system.kill(pid, sig))) {
    262         0 => return,
    263         EINVAL => unreachable, // invalid signal
    264         EPERM => return error.PermissionDenied,
    265         ESRCH => unreachable, // always a race condition
    266         else => |err| return unexpectedErrno(err),
    267     }
    268 }
    269 
    270 /// Exits the program cleanly with the specified status code.
    271 pub fn exit(status: u8) noreturn {
    272     if (builtin.link_libc) {
    273         system.exit(status);
    274     }
    275     if (builtin.os.tag == .windows) {
    276         windows.kernel32.ExitProcess(status);
    277     }
    278     if (builtin.os.tag == .wasi) {
    279         wasi.proc_exit(status);
    280     }
    281     if (builtin.os.tag == .linux and !builtin.single_threaded) {
    282         linux.exit_group(status);
    283     }
    284     if (builtin.os.tag == .uefi) {
    285         // exit() is only avaliable if exitBootServices() has not been called yet.
    286         // This call to exit should not fail, so we don't care about its return value.
    287         if (uefi.system_table.boot_services) |bs| {
    288             _ = bs.exit(uefi.handle, @intToEnum(uefi.Status, status), 0, null);
    289         }
    290         // If we can't exit, reboot the system instead.
    291         uefi.system_table.runtime_services.resetSystem(uefi.tables.ResetType.ResetCold, @intToEnum(uefi.Status, status), 0, null);
    292     }
    293     system.exit(status);
    294 }
    295 
    296 pub const ReadError = error{
    297     InputOutput,
    298     SystemResources,
    299     IsDir,
    300     OperationAborted,
    301     BrokenPipe,
    302     ConnectionResetByPeer,
    303     ConnectionTimedOut,
    304     NotOpenForReading,
    305 
    306     /// This error occurs when no global event loop is configured,
    307     /// and reading from the file descriptor would block.
    308     WouldBlock,
    309 
    310     /// In WASI, this error occurs when the file descriptor does
    311     /// not hold the required rights to read from it.
    312     AccessDenied,
    313 } || UnexpectedError;
    314 
    315 /// Returns the number of bytes that were read, which can be less than
    316 /// buf.len. If 0 bytes were read, that means EOF.
    317 /// If the application has a global event loop enabled, EAGAIN is handled
    318 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
    319 ///
    320 /// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000`
    321 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
    322 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page.
    323 /// For POSIX the limit is `math.maxInt(isize)`.
    324 pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
    325     if (builtin.os.tag == .windows) {
    326         return windows.ReadFile(fd, buf, null, std.io.default_mode);
    327     }
    328     if (builtin.os.tag == .wasi and !builtin.link_libc) {
    329         const iovs = [1]iovec{iovec{
    330             .iov_base = buf.ptr,
    331             .iov_len = buf.len,
    332         }};
    333 
    334         var nread: usize = undefined;
    335         switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
    336             wasi.ESUCCESS => return nread,
    337             wasi.EINTR => unreachable,
    338             wasi.EINVAL => unreachable,
    339             wasi.EFAULT => unreachable,
    340             wasi.EAGAIN => unreachable,
    341             wasi.EBADF => return error.NotOpenForReading, // Can be a race condition.
    342             wasi.EIO => return error.InputOutput,
    343             wasi.EISDIR => return error.IsDir,
    344             wasi.ENOBUFS => return error.SystemResources,
    345             wasi.ENOMEM => return error.SystemResources,
    346             wasi.ECONNRESET => return error.ConnectionResetByPeer,
    347             wasi.ETIMEDOUT => return error.ConnectionTimedOut,
    348             wasi.ENOTCAPABLE => return error.AccessDenied,
    349             else => |err| return unexpectedErrno(err),
    350         }
    351     }
    352 
    353     // Prevents EINVAL.
    354     const max_count = switch (std.Target.current.os.tag) {
    355         .linux => 0x7ffff000,
    356         else => math.maxInt(isize),
    357     };
    358     const adjusted_len = math.min(max_count, buf.len);
    359 
    360     while (true) {
    361         const rc = system.read(fd, buf.ptr, adjusted_len);
    362         switch (errno(rc)) {
    363             0 => return @intCast(usize, rc),
    364             EINTR => continue,
    365             EINVAL => unreachable,
    366             EFAULT => unreachable,
    367             EAGAIN => if (std.event.Loop.instance) |loop| {
    368                 loop.waitUntilFdReadable(fd);
    369                 continue;
    370             } else {
    371                 return error.WouldBlock;
    372             },
    373             EBADF => return error.NotOpenForReading, // Can be a race condition.
    374             EIO => return error.InputOutput,
    375             EISDIR => return error.IsDir,
    376             ENOBUFS => return error.SystemResources,
    377             ENOMEM => return error.SystemResources,
    378             ECONNRESET => return error.ConnectionResetByPeer,
    379             ETIMEDOUT => return error.ConnectionTimedOut,
    380             else => |err| return unexpectedErrno(err),
    381         }
    382     }
    383     return index;
    384 }
    385 
    386 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
    387 ///
    388 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
    389 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
    390 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
    391 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
    392 ///
    393 /// This operation is non-atomic on the following systems:
    394 /// * Windows
    395 /// On these systems, the read races with concurrent writes to the same file descriptor.
    396 pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
    397     if (std.Target.current.os.tag == .windows) {
    398         // TODO improve this to use ReadFileScatter
    399         if (iov.len == 0) return @as(usize, 0);
    400         const first = iov[0];
    401         return read(fd, first.iov_base[0..first.iov_len]);
    402     }
    403     if (builtin.os.tag == .wasi) {
    404         var nread: usize = undefined;
    405         switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) {
    406             wasi.ESUCCESS => return nread,
    407             wasi.EINTR => unreachable,
    408             wasi.EINVAL => unreachable,
    409             wasi.EFAULT => unreachable,
    410             wasi.EAGAIN => unreachable, // currently not support in WASI
    411             wasi.EBADF => return error.NotOpenForReading, // can be a race condition
    412             wasi.EIO => return error.InputOutput,
    413             wasi.EISDIR => return error.IsDir,
    414             wasi.ENOBUFS => return error.SystemResources,
    415             wasi.ENOMEM => return error.SystemResources,
    416             wasi.ENOTCAPABLE => return error.AccessDenied,
    417             else => |err| return unexpectedErrno(err),
    418         }
    419     }
    420     const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
    421     while (true) {
    422         // TODO handle the case when iov_len is too large and get rid of this @intCast
    423         const rc = system.readv(fd, iov.ptr, iov_count);
    424         switch (errno(rc)) {
    425             0 => return @intCast(usize, rc),
    426             EINTR => continue,
    427             EINVAL => unreachable,
    428             EFAULT => unreachable,
    429             EAGAIN => if (std.event.Loop.instance) |loop| {
    430                 loop.waitUntilFdReadable(fd);
    431                 continue;
    432             } else {
    433                 return error.WouldBlock;
    434             },
    435             EBADF => return error.NotOpenForReading, // can be a race condition
    436             EIO => return error.InputOutput,
    437             EISDIR => return error.IsDir,
    438             ENOBUFS => return error.SystemResources,
    439             ENOMEM => return error.SystemResources,
    440             else => |err| return unexpectedErrno(err),
    441         }
    442     }
    443 }
    444 
    445 pub const PReadError = ReadError || error{Unseekable};
    446 
    447 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
    448 ///
    449 /// Retries when interrupted by a signal.
    450 ///
    451 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
    452 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
    453 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
    454 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
    455 pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
    456     if (builtin.os.tag == .windows) {
    457         return windows.ReadFile(fd, buf, offset, std.io.default_mode);
    458     }
    459     if (builtin.os.tag == .wasi) {
    460         const iovs = [1]iovec{iovec{
    461             .iov_base = buf.ptr,
    462             .iov_len = buf.len,
    463         }};
    464 
    465         var nread: usize = undefined;
    466         switch (wasi.fd_pread(fd, &iovs, iovs.len, offset, &nread)) {
    467             wasi.ESUCCESS => return nread,
    468             wasi.EINTR => unreachable,
    469             wasi.EINVAL => unreachable,
    470             wasi.EFAULT => unreachable,
    471             wasi.EAGAIN => unreachable,
    472             wasi.EBADF => return error.NotOpenForReading, // Can be a race condition.
    473             wasi.EIO => return error.InputOutput,
    474             wasi.EISDIR => return error.IsDir,
    475             wasi.ENOBUFS => return error.SystemResources,
    476             wasi.ENOMEM => return error.SystemResources,
    477             wasi.ECONNRESET => return error.ConnectionResetByPeer,
    478             wasi.ENXIO => return error.Unseekable,
    479             wasi.ESPIPE => return error.Unseekable,
    480             wasi.EOVERFLOW => return error.Unseekable,
    481             wasi.ENOTCAPABLE => return error.AccessDenied,
    482             else => |err| return unexpectedErrno(err),
    483         }
    484     }
    485 
    486     while (true) {
    487         const rc = system.pread(fd, buf.ptr, buf.len, offset);
    488         switch (errno(rc)) {
    489             0 => return @intCast(usize, rc),
    490             EINTR => continue,
    491             EINVAL => unreachable,
    492             EFAULT => unreachable,
    493             EAGAIN => if (std.event.Loop.instance) |loop| {
    494                 loop.waitUntilFdReadable(fd);
    495                 continue;
    496             } else {
    497                 return error.WouldBlock;
    498             },
    499             EBADF => return error.NotOpenForReading, // Can be a race condition.
    500             EIO => return error.InputOutput,
    501             EISDIR => return error.IsDir,
    502             ENOBUFS => return error.SystemResources,
    503             ENOMEM => return error.SystemResources,
    504             ECONNRESET => return error.ConnectionResetByPeer,
    505             ENXIO => return error.Unseekable,
    506             ESPIPE => return error.Unseekable,
    507             EOVERFLOW => return error.Unseekable,
    508             else => |err| return unexpectedErrno(err),
    509         }
    510     }
    511 }
    512 
    513 pub const TruncateError = error{
    514     FileTooBig,
    515     InputOutput,
    516     FileBusy,
    517 
    518     /// In WASI, this error occurs when the file descriptor does
    519     /// not hold the required rights to call `ftruncate` on it.
    520     AccessDenied,
    521 } || UnexpectedError;
    522 
    523 pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
    524     if (std.Target.current.os.tag == .windows) {
    525         var io_status_block: windows.IO_STATUS_BLOCK = undefined;
    526         var eof_info = windows.FILE_END_OF_FILE_INFORMATION{
    527             .EndOfFile = @bitCast(windows.LARGE_INTEGER, length),
    528         };
    529 
    530         const rc = windows.ntdll.NtSetInformationFile(
    531             fd,
    532             &io_status_block,
    533             &eof_info,
    534             @sizeOf(windows.FILE_END_OF_FILE_INFORMATION),
    535             .FileEndOfFileInformation,
    536         );
    537 
    538         switch (rc) {
    539             .SUCCESS => return,
    540             .INVALID_HANDLE => unreachable, // Handle not open for writing
    541             .ACCESS_DENIED => return error.AccessDenied,
    542             else => return windows.unexpectedStatus(rc),
    543         }
    544     }
    545     if (std.Target.current.os.tag == .wasi) {
    546         switch (wasi.fd_filestat_set_size(fd, length)) {
    547             wasi.ESUCCESS => return,
    548             wasi.EINTR => unreachable,
    549             wasi.EFBIG => return error.FileTooBig,
    550             wasi.EIO => return error.InputOutput,
    551             wasi.EPERM => return error.AccessDenied,
    552             wasi.ETXTBSY => return error.FileBusy,
    553             wasi.EBADF => unreachable, // Handle not open for writing
    554             wasi.EINVAL => unreachable, // Handle not open for writing
    555             wasi.ENOTCAPABLE => return error.AccessDenied,
    556             else => |err| return unexpectedErrno(err),
    557         }
    558     }
    559 
    560     while (true) {
    561         const rc = if (builtin.link_libc)
    562             if (std.Target.current.os.tag == .linux)
    563                 system.ftruncate64(fd, @bitCast(off_t, length))
    564             else
    565                 system.ftruncate(fd, @bitCast(off_t, length))
    566         else
    567             system.ftruncate(fd, length);
    568 
    569         switch (errno(rc)) {
    570             0 => return,
    571             EINTR => continue,
    572             EFBIG => return error.FileTooBig,
    573             EIO => return error.InputOutput,
    574             EPERM => return error.AccessDenied,
    575             ETXTBSY => return error.FileBusy,
    576             EBADF => unreachable, // Handle not open for writing
    577             EINVAL => unreachable, // Handle not open for writing
    578             else => |err| return unexpectedErrno(err),
    579         }
    580     }
    581 }
    582 
    583 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
    584 ///
    585 /// Retries when interrupted by a signal.
    586 ///
    587 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
    588 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
    589 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
    590 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
    591 ///
    592 /// This operation is non-atomic on the following systems:
    593 /// * Darwin
    594 /// * Windows
    595 /// On these systems, the read races with concurrent writes to the same file descriptor.
    596 pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
    597     const have_pread_but_not_preadv = switch (std.Target.current.os.tag) {
    598         .windows, .macosx, .ios, .watchos, .tvos => true,
    599         else => false,
    600     };
    601     if (have_pread_but_not_preadv) {
    602         // We could loop here; but proper usage of `preadv` must handle partial reads anyway.
    603         // So we simply read into the first vector only.
    604         if (iov.len == 0) return @as(usize, 0);
    605         const first = iov[0];
    606         return pread(fd, first.iov_base[0..first.iov_len], offset);
    607     }
    608     if (builtin.os.tag == .wasi) {
    609         var nread: usize = undefined;
    610         switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) {
    611             wasi.ESUCCESS => return nread,
    612             wasi.EINTR => unreachable,
    613             wasi.EINVAL => unreachable,
    614             wasi.EFAULT => unreachable,
    615             wasi.EAGAIN => unreachable,
    616             wasi.EBADF => return error.NotOpenForReading, // can be a race condition
    617             wasi.EIO => return error.InputOutput,
    618             wasi.EISDIR => return error.IsDir,
    619             wasi.ENOBUFS => return error.SystemResources,
    620             wasi.ENOMEM => return error.SystemResources,
    621             wasi.ENXIO => return error.Unseekable,
    622             wasi.ESPIPE => return error.Unseekable,
    623             wasi.EOVERFLOW => return error.Unseekable,
    624             wasi.ENOTCAPABLE => return error.AccessDenied,
    625             else => |err| return unexpectedErrno(err),
    626         }
    627     }
    628 
    629     const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
    630 
    631     while (true) {
    632         const rc = system.preadv(fd, iov.ptr, iov_count, offset);
    633         switch (errno(rc)) {
    634             0 => return @bitCast(usize, rc),
    635             EINTR => continue,
    636             EINVAL => unreachable,
    637             EFAULT => unreachable,
    638             EAGAIN => if (std.event.Loop.instance) |loop| {
    639                 loop.waitUntilFdReadable(fd);
    640                 continue;
    641             } else {
    642                 return error.WouldBlock;
    643             },
    644             EBADF => return error.NotOpenForReading, // can be a race condition
    645             EIO => return error.InputOutput,
    646             EISDIR => return error.IsDir,
    647             ENOBUFS => return error.SystemResources,
    648             ENOMEM => return error.SystemResources,
    649             ENXIO => return error.Unseekable,
    650             ESPIPE => return error.Unseekable,
    651             EOVERFLOW => return error.Unseekable,
    652             else => |err| return unexpectedErrno(err),
    653         }
    654     }
    655 }
    656 
    657 pub const WriteError = error{
    658     DiskQuota,
    659     FileTooBig,
    660     InputOutput,
    661     NoSpaceLeft,
    662 
    663     /// In WASI, this error may occur when the file descriptor does
    664     /// not hold the required rights to write to it.
    665     AccessDenied,
    666     BrokenPipe,
    667     SystemResources,
    668     OperationAborted,
    669     NotOpenForWriting,
    670 
    671     /// This error occurs when no global event loop is configured,
    672     /// and reading from the file descriptor would block.
    673     WouldBlock,
    674 } || UnexpectedError;
    675 
    676 /// Write to a file descriptor.
    677 /// Retries when interrupted by a signal.
    678 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
    679 ///
    680 /// Note that a successful write() may transfer fewer than count bytes.  Such partial  writes  can
    681 /// occur  for  various reasons; for example, because there was insufficient space on the disk
    682 /// device to write all of the requested bytes, or because a blocked write() to a socket,  pipe,  or
    683 /// similar  was  interrupted by a signal handler after it had transferred some, but before it had
    684 /// transferred all of the requested bytes.  In the event of a partial write, the caller can  make
    685 /// another  write() call to transfer the remaining bytes.  The subsequent call will either
    686 /// transfer further bytes or may result in an error (e.g., if the disk is now full).
    687 ///
    688 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
    689 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
    690 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
    691 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
    692 ///
    693 /// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000`
    694 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
    695 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
    696 /// The corresponding POSIX limit is `math.maxInt(isize)`.
    697 pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
    698     if (builtin.os.tag == .windows) {
    699         return windows.WriteFile(fd, bytes, null, std.io.default_mode);
    700     }
    701 
    702     if (builtin.os.tag == .wasi and !builtin.link_libc) {
    703         const ciovs = [_]iovec_const{iovec_const{
    704             .iov_base = bytes.ptr,
    705             .iov_len = bytes.len,
    706         }};
    707         var nwritten: usize = undefined;
    708         switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
    709             wasi.ESUCCESS => return nwritten,
    710             wasi.EINTR => unreachable,
    711             wasi.EINVAL => unreachable,
    712             wasi.EFAULT => unreachable,
    713             wasi.EAGAIN => unreachable,
    714             wasi.EBADF => return error.NotOpenForWriting, // can be a race condition.
    715             wasi.EDESTADDRREQ => unreachable, // `connect` was never called.
    716             wasi.EDQUOT => return error.DiskQuota,
    717             wasi.EFBIG => return error.FileTooBig,
    718             wasi.EIO => return error.InputOutput,
    719             wasi.ENOSPC => return error.NoSpaceLeft,
    720             wasi.EPERM => return error.AccessDenied,
    721             wasi.EPIPE => return error.BrokenPipe,
    722             wasi.ENOTCAPABLE => return error.AccessDenied,
    723             else => |err| return unexpectedErrno(err),
    724         }
    725     }
    726 
    727     const max_count = switch (std.Target.current.os.tag) {
    728         .linux => 0x7ffff000,
    729         else => math.maxInt(isize),
    730     };
    731     const adjusted_len = math.min(max_count, bytes.len);
    732 
    733     while (true) {
    734         const rc = system.write(fd, bytes.ptr, adjusted_len);
    735         switch (errno(rc)) {
    736             0 => return @intCast(usize, rc),
    737             EINTR => continue,
    738             EINVAL => unreachable,
    739             EFAULT => unreachable,
    740             EAGAIN => if (std.event.Loop.instance) |loop| {
    741                 loop.waitUntilFdWritable(fd);
    742                 continue;
    743             } else {
    744                 return error.WouldBlock;
    745             },
    746             EBADF => return error.NotOpenForWriting, // can be a race condition.
    747             EDESTADDRREQ => unreachable, // `connect` was never called.
    748             EDQUOT => return error.DiskQuota,
    749             EFBIG => return error.FileTooBig,
    750             EIO => return error.InputOutput,
    751             ENOSPC => return error.NoSpaceLeft,
    752             EPERM => return error.AccessDenied,
    753             EPIPE => return error.BrokenPipe,
    754             else => |err| return unexpectedErrno(err),
    755         }
    756     }
    757 }
    758 
    759 /// Write multiple buffers to a file descriptor.
    760 /// Retries when interrupted by a signal.
    761 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
    762 ///
    763 /// Note that a successful write() may transfer fewer bytes than supplied.  Such partial  writes  can
    764 /// occur  for  various reasons; for example, because there was insufficient space on the disk
    765 /// device to write all of the requested bytes, or because a blocked write() to a socket,  pipe,  or
    766 /// similar  was  interrupted by a signal handler after it had transferred some, but before it had
    767 /// transferred all of the requested bytes.  In the event of a partial write, the caller can  make
    768 /// another  write() call to transfer the remaining bytes.  The subsequent call will either
    769 /// transfer further bytes or may result in an error (e.g., if the disk is now full).
    770 ///
    771 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
    772 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
    773 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
    774 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
    775 ///
    776 /// If `iov.len` is larger than will fit in a `u31`, a partial write will occur.
    777 pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
    778     if (std.Target.current.os.tag == .windows) {
    779         // TODO improve this to use WriteFileScatter
    780         if (iov.len == 0) return @as(usize, 0);
    781         const first = iov[0];
    782         return write(fd, first.iov_base[0..first.iov_len]);
    783     }
    784     if (builtin.os.tag == .wasi) {
    785         var nwritten: usize = undefined;
    786         switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) {
    787             wasi.ESUCCESS => return nwritten,
    788             wasi.EINTR => unreachable,
    789             wasi.EINVAL => unreachable,
    790             wasi.EFAULT => unreachable,
    791             wasi.EAGAIN => unreachable,
    792             wasi.EBADF => return error.NotOpenForWriting, // can be a race condition.
    793             wasi.EDESTADDRREQ => unreachable, // `connect` was never called.
    794             wasi.EDQUOT => return error.DiskQuota,
    795             wasi.EFBIG => return error.FileTooBig,
    796             wasi.EIO => return error.InputOutput,
    797             wasi.ENOSPC => return error.NoSpaceLeft,
    798             wasi.EPERM => return error.AccessDenied,
    799             wasi.EPIPE => return error.BrokenPipe,
    800             wasi.ENOTCAPABLE => return error.AccessDenied,
    801             else => |err| return unexpectedErrno(err),
    802         }
    803     }
    804 
    805     const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
    806     while (true) {
    807         const rc = system.writev(fd, iov.ptr, iov_count);
    808         switch (errno(rc)) {
    809             0 => return @intCast(usize, rc),
    810             EINTR => continue,
    811             EINVAL => unreachable,
    812             EFAULT => unreachable,
    813             EAGAIN => if (std.event.Loop.instance) |loop| {
    814                 loop.waitUntilFdWritable(fd);
    815                 continue;
    816             } else {
    817                 return error.WouldBlock;
    818             },
    819             EBADF => return error.NotOpenForWriting, // Can be a race condition.
    820             EDESTADDRREQ => unreachable, // `connect` was never called.
    821             EDQUOT => return error.DiskQuota,
    822             EFBIG => return error.FileTooBig,
    823             EIO => return error.InputOutput,
    824             ENOSPC => return error.NoSpaceLeft,
    825             EPERM => return error.AccessDenied,
    826             EPIPE => return error.BrokenPipe,
    827             else => |err| return unexpectedErrno(err),
    828         }
    829     }
    830 }
    831 
    832 pub const PWriteError = WriteError || error{Unseekable};
    833 
    834 /// Write to a file descriptor, with a position offset.
    835 /// Retries when interrupted by a signal.
    836 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
    837 ///
    838 /// Note that a successful write() may transfer fewer bytes than supplied.  Such partial  writes  can
    839 /// occur  for  various reasons; for example, because there was insufficient space on the disk
    840 /// device to write all of the requested bytes, or because a blocked write() to a socket,  pipe,  or
    841 /// similar  was  interrupted by a signal handler after it had transferred some, but before it had
    842 /// transferred all of the requested bytes.  In the event of a partial write, the caller can  make
    843 /// another  write() call to transfer the remaining bytes.  The subsequent call will either
    844 /// transfer further bytes or may result in an error (e.g., if the disk is now full).
    845 ///
    846 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled
    847 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
    848 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are
    849 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows.
    850 ///
    851 /// Linux has a limit on how many bytes may be transferred in one `pwrite` call, which is `0x7ffff000`
    852 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
    853 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page.
    854 /// The corresponding POSIX limit is `math.maxInt(isize)`.
    855 pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
    856     if (std.Target.current.os.tag == .windows) {
    857         return windows.WriteFile(fd, bytes, offset, std.io.default_mode);
    858     }
    859     if (builtin.os.tag == .wasi) {
    860         const ciovs = [1]iovec_const{iovec_const{
    861             .iov_base = bytes.ptr,
    862             .iov_len = bytes.len,
    863         }};
    864 
    865         var nwritten: usize = undefined;
    866         switch (wasi.fd_pwrite(fd, &ciovs, ciovs.len, offset, &nwritten)) {
    867             wasi.ESUCCESS => return nwritten,
    868             wasi.EINTR => unreachable,
    869             wasi.EINVAL => unreachable,
    870             wasi.EFAULT => unreachable,
    871             wasi.EAGAIN => unreachable,
    872             wasi.EBADF => return error.NotOpenForWriting, // can be a race condition.
    873             wasi.EDESTADDRREQ => unreachable, // `connect` was never called.
    874             wasi.EDQUOT => return error.DiskQuota,
    875             wasi.EFBIG => return error.FileTooBig,
    876             wasi.EIO => return error.InputOutput,
    877             wasi.ENOSPC => return error.NoSpaceLeft,
    878             wasi.EPERM => return error.AccessDenied,
    879             wasi.EPIPE => return error.BrokenPipe,
    880             wasi.ENXIO => return error.Unseekable,
    881             wasi.ESPIPE => return error.Unseekable,
    882             wasi.EOVERFLOW => return error.Unseekable,
    883             wasi.ENOTCAPABLE => return error.AccessDenied,
    884             else => |err| return unexpectedErrno(err),
    885         }
    886     }
    887 
    888     // Prevent EINVAL.
    889     const max_count = switch (std.Target.current.os.tag) {
    890         .linux => 0x7ffff000,
    891         else => math.maxInt(isize),
    892     };
    893     const adjusted_len = math.min(max_count, bytes.len);
    894 
    895     while (true) {
    896         const rc = system.pwrite(fd, bytes.ptr, adjusted_len, offset);
    897         switch (errno(rc)) {
    898             0 => return @intCast(usize, rc),
    899             EINTR => continue,
    900             EINVAL => unreachable,
    901             EFAULT => unreachable,
    902             EAGAIN => if (std.event.Loop.instance) |loop| {
    903                 loop.waitUntilFdWritable(fd);
    904                 continue;
    905             } else {
    906                 return error.WouldBlock;
    907             },
    908             EBADF => return error.NotOpenForWriting, // Can be a race condition.
    909             EDESTADDRREQ => unreachable, // `connect` was never called.
    910             EDQUOT => return error.DiskQuota,
    911             EFBIG => return error.FileTooBig,
    912             EIO => return error.InputOutput,
    913             ENOSPC => return error.NoSpaceLeft,
    914             EPERM => return error.AccessDenied,
    915             EPIPE => return error.BrokenPipe,
    916             ENXIO => return error.Unseekable,
    917             ESPIPE => return error.Unseekable,
    918             EOVERFLOW => return error.Unseekable,
    919             else => |err| return unexpectedErrno(err),
    920         }
    921     }
    922 }
    923 
    924 /// Write multiple buffers to a file descriptor, with a position offset.
    925 /// Retries when interrupted by a signal.
    926 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.
    927 ///
    928 /// Note that a successful write() may transfer fewer than count bytes.  Such partial  writes  can
    929 /// occur  for  various reasons; for example, because there was insufficient space on the disk
    930 /// device to write all of the requested bytes, or because a blocked write() to a socket,  pipe,  or
    931 /// similar  was  interrupted by a signal handler after it had transferred some, but before it had
    932 /// transferred all of the requested bytes.  In the event of a partial write, the caller can  make
    933 /// another  write() call to transfer the remaining bytes.  The subsequent call will either
    934 /// transfer further bytes or may result in an error (e.g., if the disk is now full).
    935 ///
    936 /// If the application has a global event loop enabled, EAGAIN is handled
    937 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`.
    938 ///
    939 /// The following systems do not have this syscall, and will return partial writes if more than one
    940 /// vector is provided:
    941 /// * Darwin
    942 /// * Windows
    943 ///
    944 /// If `iov.len` is larger than will fit in a `u31`, a partial write will occur.
    945 pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {
    946     const have_pwrite_but_not_pwritev = switch (std.Target.current.os.tag) {
    947         .windows, .macosx, .ios, .watchos, .tvos => true,
    948         else => false,
    949     };
    950 
    951     if (have_pwrite_but_not_pwritev) {
    952         // We could loop here; but proper usage of `pwritev` must handle partial writes anyway.
    953         // So we simply write the first vector only.
    954         if (iov.len == 0) return @as(usize, 0);
    955         const first = iov[0];
    956         return pwrite(fd, first.iov_base[0..first.iov_len], offset);
    957     }
    958     if (builtin.os.tag == .wasi) {
    959         var nwritten: usize = undefined;
    960         switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) {
    961             wasi.ESUCCESS => return nwritten,
    962             wasi.EINTR => unreachable,
    963             wasi.EINVAL => unreachable,
    964             wasi.EFAULT => unreachable,
    965             wasi.EAGAIN => unreachable,
    966             wasi.EBADF => return error.NotOpenForWriting, // Can be a race condition.
    967             wasi.EDESTADDRREQ => unreachable, // `connect` was never called.
    968             wasi.EDQUOT => return error.DiskQuota,
    969             wasi.EFBIG => return error.FileTooBig,
    970             wasi.EIO => return error.InputOutput,
    971             wasi.ENOSPC => return error.NoSpaceLeft,
    972             wasi.EPERM => return error.AccessDenied,
    973             wasi.EPIPE => return error.BrokenPipe,
    974             wasi.ENXIO => return error.Unseekable,
    975             wasi.ESPIPE => return error.Unseekable,
    976             wasi.EOVERFLOW => return error.Unseekable,
    977             wasi.ENOTCAPABLE => return error.AccessDenied,
    978             else => |err| return unexpectedErrno(err),
    979         }
    980     }
    981 
    982     const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31);
    983     while (true) {
    984         const rc = system.pwritev(fd, iov.ptr, iov_count, offset);
    985         switch (errno(rc)) {
    986             0 => return @intCast(usize, rc),
    987             EINTR => continue,
    988             EINVAL => unreachable,
    989             EFAULT => unreachable,
    990             EAGAIN => if (std.event.Loop.instance) |loop| {
    991                 loop.waitUntilFdWritable(fd);
    992                 continue;
    993             } else {
    994                 return error.WouldBlock;
    995             },
    996             EBADF => return error.NotOpenForWriting, // Can be a race condition.
    997             EDESTADDRREQ => unreachable, // `connect` was never called.
    998             EDQUOT => return error.DiskQuota,
    999             EFBIG => return error.FileTooBig,
   1000             EIO => return error.InputOutput,
   1001             ENOSPC => return error.NoSpaceLeft,
   1002             EPERM => return error.AccessDenied,
   1003             EPIPE => return error.BrokenPipe,
   1004             ENXIO => return error.Unseekable,
   1005             ESPIPE => return error.Unseekable,
   1006             EOVERFLOW => return error.Unseekable,
   1007             else => |err| return unexpectedErrno(err),
   1008         }
   1009     }
   1010 }
   1011 
   1012 pub const OpenError = error{
   1013     /// In WASI, this error may occur when the file descriptor does
   1014     /// not hold the required rights to open a new resource relative to it.
   1015     AccessDenied,
   1016     SymLinkLoop,
   1017     ProcessFdQuotaExceeded,
   1018     SystemFdQuotaExceeded,
   1019     NoDevice,
   1020     FileNotFound,
   1021 
   1022     /// The path exceeded `MAX_PATH_BYTES` bytes.
   1023     NameTooLong,
   1024 
   1025     /// Insufficient kernel memory was available, or
   1026     /// the named file is a FIFO and per-user hard limit on
   1027     /// memory allocation for pipes has been reached.
   1028     SystemResources,
   1029 
   1030     /// The file is too large to be opened. This error is unreachable
   1031     /// for 64-bit targets, as well as when opening directories.
   1032     FileTooBig,
   1033 
   1034     /// The path refers to directory but the `O_DIRECTORY` flag was not provided.
   1035     IsDir,
   1036 
   1037     /// A new path cannot be created because the device has no room for the new file.
   1038     /// This error is only reachable when the `O_CREAT` flag is provided.
   1039     NoSpaceLeft,
   1040 
   1041     /// A component used as a directory in the path was not, in fact, a directory, or
   1042     /// `O_DIRECTORY` was specified and the path was not a directory.
   1043     NotDir,
   1044 
   1045     /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided.
   1046     PathAlreadyExists,
   1047     DeviceBusy,
   1048 
   1049     /// The underlying filesystem does not support file locks
   1050     FileLocksNotSupported,
   1051 
   1052     BadPathName,
   1053     InvalidUtf8,
   1054 } || UnexpectedError;
   1055 
   1056 /// Open and possibly create a file. Keeps trying if it gets interrupted.
   1057 /// See also `openC`.
   1058 pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t {
   1059     if (std.Target.current.os.tag == .windows) {
   1060         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   1061         return openW(file_path_w.span(), flags, perm);
   1062     }
   1063     const file_path_c = try toPosixPath(file_path);
   1064     return openZ(&file_path_c, flags, perm);
   1065 }
   1066 
   1067 pub const openC = @compileError("deprecated: renamed to openZ");
   1068 
   1069 /// Open and possibly create a file. Keeps trying if it gets interrupted.
   1070 /// See also `open`.
   1071 pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t {
   1072     if (std.Target.current.os.tag == .windows) {
   1073         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   1074         return openW(file_path_w.span(), flags, perm);
   1075     }
   1076     while (true) {
   1077         const rc = system.open(file_path, flags, perm);
   1078         switch (errno(rc)) {
   1079             0 => return @intCast(fd_t, rc),
   1080             EINTR => continue,
   1081 
   1082             EFAULT => unreachable,
   1083             EINVAL => unreachable,
   1084             EACCES => return error.AccessDenied,
   1085             EFBIG => return error.FileTooBig,
   1086             EOVERFLOW => return error.FileTooBig,
   1087             EISDIR => return error.IsDir,
   1088             ELOOP => return error.SymLinkLoop,
   1089             EMFILE => return error.ProcessFdQuotaExceeded,
   1090             ENAMETOOLONG => return error.NameTooLong,
   1091             ENFILE => return error.SystemFdQuotaExceeded,
   1092             ENODEV => return error.NoDevice,
   1093             ENOENT => return error.FileNotFound,
   1094             ENOMEM => return error.SystemResources,
   1095             ENOSPC => return error.NoSpaceLeft,
   1096             ENOTDIR => return error.NotDir,
   1097             EPERM => return error.AccessDenied,
   1098             EEXIST => return error.PathAlreadyExists,
   1099             EBUSY => return error.DeviceBusy,
   1100             else => |err| return unexpectedErrno(err),
   1101         }
   1102     }
   1103 }
   1104 
   1105 fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
   1106     const w = windows;
   1107 
   1108     var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE;
   1109     if (flags & O_RDWR != 0) {
   1110         access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
   1111     } else if (flags & O_WRONLY != 0) {
   1112         access_mask |= w.GENERIC_WRITE;
   1113     } else {
   1114         access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
   1115     }
   1116 
   1117     const open_dir: bool = flags & O_DIRECTORY != 0;
   1118     const follow_symlinks: bool = flags & O_NOFOLLOW == 0;
   1119 
   1120     const creation: w.ULONG = blk: {
   1121         if (flags & O_CREAT != 0) {
   1122             if (flags & O_EXCL != 0) {
   1123                 break :blk w.FILE_CREATE;
   1124             }
   1125         }
   1126         break :blk w.FILE_OPEN;
   1127     };
   1128 
   1129     return .{
   1130         .access_mask = access_mask,
   1131         .io_mode = .blocking,
   1132         .creation = creation,
   1133         .open_dir = open_dir,
   1134         .follow_symlinks = follow_symlinks,
   1135     };
   1136 }
   1137 
   1138 /// Windows-only. The path parameter is
   1139 /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
   1140 /// Translates the POSIX open API call to a Windows API call.
   1141 /// TODO currently, this function does not handle all flag combinations
   1142 /// or makes use of perm argument.
   1143 pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t {
   1144     var options = openOptionsFromFlags(flags);
   1145     options.dir = std.fs.cwd().fd;
   1146     return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
   1147         error.WouldBlock => unreachable,
   1148         error.PipeBusy => unreachable,
   1149         else => |e| return e,
   1150     };
   1151 }
   1152 
   1153 /// Open and possibly create a file. Keeps trying if it gets interrupted.
   1154 /// `file_path` is relative to the open directory handle `dir_fd`.
   1155 /// See also `openatC`.
   1156 pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
   1157     if (builtin.os.tag == .wasi) {
   1158         @compileError("use openatWasi instead");
   1159     }
   1160     if (builtin.os.tag == .windows) {
   1161         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   1162         return openatW(dir_fd, file_path_w.span(), flags, mode);
   1163     }
   1164     const file_path_c = try toPosixPath(file_path);
   1165     return openatZ(dir_fd, &file_path_c, flags, mode);
   1166 }
   1167 
   1168 /// Open and possibly create a file in WASI.
   1169 pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, lookup_flags: lookupflags_t, oflags: oflags_t, fdflags: fdflags_t, base: rights_t, inheriting: rights_t) OpenError!fd_t {
   1170     while (true) {
   1171         var fd: fd_t = undefined;
   1172         switch (wasi.path_open(dir_fd, lookup_flags, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) {
   1173             wasi.ESUCCESS => return fd,
   1174             wasi.EINTR => continue,
   1175 
   1176             wasi.EFAULT => unreachable,
   1177             wasi.EINVAL => unreachable,
   1178             wasi.EACCES => return error.AccessDenied,
   1179             wasi.EFBIG => return error.FileTooBig,
   1180             wasi.EOVERFLOW => return error.FileTooBig,
   1181             wasi.EISDIR => return error.IsDir,
   1182             wasi.ELOOP => return error.SymLinkLoop,
   1183             wasi.EMFILE => return error.ProcessFdQuotaExceeded,
   1184             wasi.ENAMETOOLONG => return error.NameTooLong,
   1185             wasi.ENFILE => return error.SystemFdQuotaExceeded,
   1186             wasi.ENODEV => return error.NoDevice,
   1187             wasi.ENOENT => return error.FileNotFound,
   1188             wasi.ENOMEM => return error.SystemResources,
   1189             wasi.ENOSPC => return error.NoSpaceLeft,
   1190             wasi.ENOTDIR => return error.NotDir,
   1191             wasi.EPERM => return error.AccessDenied,
   1192             wasi.EEXIST => return error.PathAlreadyExists,
   1193             wasi.EBUSY => return error.DeviceBusy,
   1194             wasi.ENOTCAPABLE => return error.AccessDenied,
   1195             else => |err| return unexpectedErrno(err),
   1196         }
   1197     }
   1198 }
   1199 
   1200 pub const openatC = @compileError("deprecated: renamed to openatZ");
   1201 
   1202 /// Open and possibly create a file. Keeps trying if it gets interrupted.
   1203 /// `file_path` is relative to the open directory handle `dir_fd`.
   1204 /// See also `openat`.
   1205 pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
   1206     if (builtin.os.tag == .windows) {
   1207         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   1208         return openatW(dir_fd, file_path_w.span(), flags, mode);
   1209     }
   1210     while (true) {
   1211         const rc = system.openat(dir_fd, file_path, flags, mode);
   1212         switch (errno(rc)) {
   1213             0 => return @intCast(fd_t, rc),
   1214             EINTR => continue,
   1215 
   1216             EFAULT => unreachable,
   1217             EINVAL => unreachable,
   1218             EACCES => return error.AccessDenied,
   1219             EFBIG => return error.FileTooBig,
   1220             EOVERFLOW => return error.FileTooBig,
   1221             EISDIR => return error.IsDir,
   1222             ELOOP => return error.SymLinkLoop,
   1223             EMFILE => return error.ProcessFdQuotaExceeded,
   1224             ENAMETOOLONG => return error.NameTooLong,
   1225             ENFILE => return error.SystemFdQuotaExceeded,
   1226             ENODEV => return error.NoDevice,
   1227             ENOENT => return error.FileNotFound,
   1228             ENOMEM => return error.SystemResources,
   1229             ENOSPC => return error.NoSpaceLeft,
   1230             ENOTDIR => return error.NotDir,
   1231             EPERM => return error.AccessDenied,
   1232             EEXIST => return error.PathAlreadyExists,
   1233             EBUSY => return error.DeviceBusy,
   1234             EOPNOTSUPP => return error.FileLocksNotSupported,
   1235             else => |err| return unexpectedErrno(err),
   1236         }
   1237     }
   1238 }
   1239 
   1240 /// Windows-only. Similar to `openat` but with pathname argument null-terminated
   1241 /// WTF16 encoded.
   1242 /// TODO currently, this function does not handle all flag combinations
   1243 /// or makes use of perm argument.
   1244 pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t {
   1245     var options = openOptionsFromFlags(flags);
   1246     options.dir = dir_fd;
   1247     return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
   1248         error.WouldBlock => unreachable,
   1249         error.PipeBusy => unreachable,
   1250         else => |e| return e,
   1251     };
   1252 }
   1253 
   1254 pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
   1255     while (true) {
   1256         switch (errno(system.dup2(old_fd, new_fd))) {
   1257             0 => return,
   1258             EBUSY, EINTR => continue,
   1259             EMFILE => return error.ProcessFdQuotaExceeded,
   1260             EINVAL => unreachable, // invalid parameters passed to dup2
   1261             EBADF => unreachable, // invalid file descriptor
   1262             else => |err| return unexpectedErrno(err),
   1263         }
   1264     }
   1265 }
   1266 
   1267 pub const ExecveError = error{
   1268     SystemResources,
   1269     AccessDenied,
   1270     InvalidExe,
   1271     FileSystem,
   1272     IsDir,
   1273     FileNotFound,
   1274     NotDir,
   1275     FileBusy,
   1276     ProcessFdQuotaExceeded,
   1277     SystemFdQuotaExceeded,
   1278     NameTooLong,
   1279 } || UnexpectedError;
   1280 
   1281 pub const execveC = @compileError("deprecated: use execveZ");
   1282 
   1283 /// Like `execve` except the parameters are null-terminated,
   1284 /// matching the syscall API on all targets. This removes the need for an allocator.
   1285 /// This function ignores PATH environment variable. See `execvpeZ` for that.
   1286 pub fn execveZ(
   1287     path: [*:0]const u8,
   1288     child_argv: [*:null]const ?[*:0]const u8,
   1289     envp: [*:null]const ?[*:0]const u8,
   1290 ) ExecveError {
   1291     switch (errno(system.execve(path, child_argv, envp))) {
   1292         0 => unreachable,
   1293         EFAULT => unreachable,
   1294         E2BIG => return error.SystemResources,
   1295         EMFILE => return error.ProcessFdQuotaExceeded,
   1296         ENAMETOOLONG => return error.NameTooLong,
   1297         ENFILE => return error.SystemFdQuotaExceeded,
   1298         ENOMEM => return error.SystemResources,
   1299         EACCES => return error.AccessDenied,
   1300         EPERM => return error.AccessDenied,
   1301         EINVAL => return error.InvalidExe,
   1302         ENOEXEC => return error.InvalidExe,
   1303         EIO => return error.FileSystem,
   1304         ELOOP => return error.FileSystem,
   1305         EISDIR => return error.IsDir,
   1306         ENOENT => return error.FileNotFound,
   1307         ENOTDIR => return error.NotDir,
   1308         ETXTBSY => return error.FileBusy,
   1309         else => |err| return unexpectedErrno(err),
   1310     }
   1311 }
   1312 
   1313 pub const execvpeC = @compileError("deprecated in favor of execvpeZ");
   1314 
   1315 pub const Arg0Expand = enum {
   1316     expand,
   1317     no_expand,
   1318 };
   1319 
   1320 /// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable,
   1321 /// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall.
   1322 /// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in.
   1323 pub fn execvpeZ_expandArg0(
   1324     comptime arg0_expand: Arg0Expand,
   1325     file: [*:0]const u8,
   1326     child_argv: switch (arg0_expand) {
   1327         .expand => [*:null]?[*:0]const u8,
   1328         .no_expand => [*:null]const ?[*:0]const u8,
   1329     },
   1330     envp: [*:null]const ?[*:0]const u8,
   1331 ) ExecveError {
   1332     const file_slice = mem.spanZ(file);
   1333     if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);
   1334 
   1335     const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
   1336     // Use of MAX_PATH_BYTES here is valid as the path_buf will be passed
   1337     // directly to the operating system in execveZ.
   1338     var path_buf: [MAX_PATH_BYTES]u8 = undefined;
   1339     var it = mem.tokenize(PATH, ":");
   1340     var seen_eacces = false;
   1341     var err: ExecveError = undefined;
   1342 
   1343     // In case of expanding arg0 we must put it back if we return with an error.
   1344     const prev_arg0 = child_argv[0];
   1345     defer switch (arg0_expand) {
   1346         .expand => child_argv[0] = prev_arg0,
   1347         .no_expand => {},
   1348     };
   1349 
   1350     while (it.next()) |search_path| {
   1351         if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong;
   1352         mem.copy(u8, &path_buf, search_path);
   1353         path_buf[search_path.len] = '/';
   1354         mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);
   1355         const path_len = search_path.len + file_slice.len + 1;
   1356         path_buf[path_len] = 0;
   1357         const full_path = path_buf[0..path_len :0].ptr;
   1358         switch (arg0_expand) {
   1359             .expand => child_argv[0] = full_path,
   1360             .no_expand => {},
   1361         }
   1362         err = execveZ(full_path, child_argv, envp);
   1363         switch (err) {
   1364             error.AccessDenied => seen_eacces = true,
   1365             error.FileNotFound, error.NotDir => {},
   1366             else => |e| return e,
   1367         }
   1368     }
   1369     if (seen_eacces) return error.AccessDenied;
   1370     return err;
   1371 }
   1372 
   1373 /// Like `execvpe` except the parameters are null-terminated,
   1374 /// matching the syscall API on all targets. This removes the need for an allocator.
   1375 /// This function also uses the PATH environment variable to get the full path to the executable.
   1376 /// If `file` is an absolute path, this is the same as `execveZ`.
   1377 pub fn execvpeZ(
   1378     file: [*:0]const u8,
   1379     argv: [*:null]const ?[*:0]const u8,
   1380     envp: [*:null]const ?[*:0]const u8,
   1381 ) ExecveError {
   1382     return execvpeZ_expandArg0(.no_expand, file, argv, envp);
   1383 }
   1384 
   1385 /// This is the same as `execvpe` except if the `arg0_expand` parameter is set to `.expand`,
   1386 /// then argv[0] will be replaced with the expanded version of it, after resolving in accordance
   1387 /// with the PATH environment variable.
   1388 pub fn execvpe_expandArg0(
   1389     allocator: *mem.Allocator,
   1390     arg0_expand: Arg0Expand,
   1391     argv_slice: []const []const u8,
   1392     env_map: *const std.BufMap,
   1393 ) (ExecveError || error{OutOfMemory}) {
   1394     const argv_buf = try allocator.alloc(?[*:0]u8, argv_slice.len + 1);
   1395     mem.set(?[*:0]u8, argv_buf, null);
   1396     defer {
   1397         for (argv_buf) |arg| {
   1398             const arg_buf = mem.spanZ(arg) orelse break;
   1399             allocator.free(arg_buf);
   1400         }
   1401         allocator.free(argv_buf);
   1402     }
   1403     for (argv_slice) |arg, i| {
   1404         const arg_buf = try allocator.alloc(u8, arg.len + 1);
   1405         @memcpy(arg_buf.ptr, arg.ptr, arg.len);
   1406         arg_buf[arg.len] = 0;
   1407         argv_buf[i] = arg_buf[0..arg.len :0].ptr;
   1408     }
   1409     argv_buf[argv_slice.len] = null;
   1410     const argv_ptr = argv_buf[0..argv_slice.len :null].ptr;
   1411 
   1412     const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
   1413     defer freeNullDelimitedEnvMap(allocator, envp_buf);
   1414 
   1415     switch (arg0_expand) {
   1416         .expand => return execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr),
   1417         .no_expand => return execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr),
   1418     }
   1419 }
   1420 
   1421 /// This function must allocate memory to add a null terminating bytes on path and each arg.
   1422 /// It must also convert to KEY=VALUE\0 format for environment variables, and include null
   1423 /// pointers after the args and after the environment variables.
   1424 /// `argv_slice[0]` is the executable path.
   1425 /// This function also uses the PATH environment variable to get the full path to the executable.
   1426 pub fn execvpe(
   1427     allocator: *mem.Allocator,
   1428     argv_slice: []const []const u8,
   1429     env_map: *const std.BufMap,
   1430 ) (ExecveError || error{OutOfMemory}) {
   1431     return execvpe_expandArg0(allocator, .no_expand, argv_slice, env_map);
   1432 }
   1433 
   1434 pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![:null]?[*:0]u8 {
   1435     const envp_count = env_map.count();
   1436     const envp_buf = try allocator.alloc(?[*:0]u8, envp_count + 1);
   1437     mem.set(?[*:0]u8, envp_buf, null);
   1438     errdefer freeNullDelimitedEnvMap(allocator, envp_buf);
   1439     {
   1440         var it = env_map.iterator();
   1441         var i: usize = 0;
   1442         while (it.next()) |pair| : (i += 1) {
   1443             const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2);
   1444             @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len);
   1445             env_buf[pair.key.len] = '=';
   1446             @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);
   1447             const len = env_buf.len - 1;
   1448             env_buf[len] = 0;
   1449             envp_buf[i] = env_buf[0..len :0].ptr;
   1450         }
   1451         assert(i == envp_count);
   1452     }
   1453     return envp_buf[0..envp_count :null];
   1454 }
   1455 
   1456 pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void {
   1457     for (envp_buf) |env| {
   1458         const env_buf = if (env) |ptr| ptr[0 .. mem.len(ptr) + 1] else break;
   1459         allocator.free(env_buf);
   1460     }
   1461     allocator.free(envp_buf);
   1462 }
   1463 
   1464 /// Get an environment variable.
   1465 /// See also `getenvZ`.
   1466 pub fn getenv(key: []const u8) ?[]const u8 {
   1467     if (builtin.link_libc) {
   1468         var small_key_buf: [64]u8 = undefined;
   1469         if (key.len < small_key_buf.len) {
   1470             mem.copy(u8, &small_key_buf, key);
   1471             small_key_buf[key.len] = 0;
   1472             const key0 = small_key_buf[0..key.len :0];
   1473             return getenvZ(key0);
   1474         }
   1475         // Search the entire `environ` because we don't have a null terminated pointer.
   1476         var ptr = std.c.environ;
   1477         while (ptr.*) |line| : (ptr += 1) {
   1478             var line_i: usize = 0;
   1479             while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
   1480             const this_key = line[0..line_i];
   1481 
   1482             if (!mem.eql(u8, this_key, key)) continue;
   1483 
   1484             var end_i: usize = line_i;
   1485             while (line[end_i] != 0) : (end_i += 1) {}
   1486             const value = line[line_i + 1 .. end_i];
   1487 
   1488             return value;
   1489         }
   1490         return null;
   1491     }
   1492     if (builtin.os.tag == .windows) {
   1493         @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.");
   1494     }
   1495     // TODO see https://github.com/ziglang/zig/issues/4524
   1496     for (environ) |ptr| {
   1497         var line_i: usize = 0;
   1498         while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
   1499         const this_key = ptr[0..line_i];
   1500         if (!mem.eql(u8, key, this_key)) continue;
   1501 
   1502         var end_i: usize = line_i;
   1503         while (ptr[end_i] != 0) : (end_i += 1) {}
   1504         const this_value = ptr[line_i + 1 .. end_i];
   1505 
   1506         return this_value;
   1507     }
   1508     return null;
   1509 }
   1510 
   1511 pub const getenvC = @compileError("Deprecated in favor of `getenvZ`");
   1512 
   1513 /// Get an environment variable with a null-terminated name.
   1514 /// See also `getenv`.
   1515 pub fn getenvZ(key: [*:0]const u8) ?[]const u8 {
   1516     if (builtin.link_libc) {
   1517         const value = system.getenv(key) orelse return null;
   1518         return mem.spanZ(value);
   1519     }
   1520     if (builtin.os.tag == .windows) {
   1521         @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.");
   1522     }
   1523     return getenv(mem.spanZ(key));
   1524 }
   1525 
   1526 /// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name.
   1527 /// See also `getenv`.
   1528 /// This function first attempts a case-sensitive lookup. If no match is found, and `key`
   1529 /// is ASCII, then it attempts a second case-insensitive lookup.
   1530 pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 {
   1531     if (builtin.os.tag != .windows) {
   1532         @compileError("std.os.getenvW is a Windows-only API");
   1533     }
   1534     const key_slice = mem.spanZ(key);
   1535     const ptr = windows.peb().ProcessParameters.Environment;
   1536     var ascii_match: ?[:0]const u16 = null;
   1537     var i: usize = 0;
   1538     while (ptr[i] != 0) {
   1539         const key_start = i;
   1540 
   1541         while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
   1542         const this_key = ptr[key_start..i];
   1543 
   1544         if (ptr[i] == '=') i += 1;
   1545 
   1546         const value_start = i;
   1547         while (ptr[i] != 0) : (i += 1) {}
   1548         const this_value = ptr[value_start..i :0];
   1549 
   1550         if (mem.eql(u16, key_slice, this_key)) return this_value;
   1551 
   1552         ascii_check: {
   1553             if (ascii_match != null) break :ascii_check;
   1554             if (key_slice.len != this_key.len) break :ascii_check;
   1555             for (key_slice) |a_c, key_index| {
   1556                 const a = math.cast(u8, a_c) catch break :ascii_check;
   1557                 const b = math.cast(u8, this_key[key_index]) catch break :ascii_check;
   1558                 if (std.ascii.toLower(a) != std.ascii.toLower(b)) break :ascii_check;
   1559             }
   1560             ascii_match = this_value;
   1561         }
   1562 
   1563         i += 1; // skip over null byte
   1564     }
   1565     return ascii_match;
   1566 }
   1567 
   1568 pub const GetCwdError = error{
   1569     NameTooLong,
   1570     CurrentWorkingDirectoryUnlinked,
   1571 } || UnexpectedError;
   1572 
   1573 /// The result is a slice of out_buffer, indexed from 0.
   1574 pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
   1575     if (builtin.os.tag == .windows) {
   1576         return windows.GetCurrentDirectory(out_buffer);
   1577     }
   1578     if (builtin.os.tag == .wasi) {
   1579         @compileError("WASI doesn't have a concept of cwd(); use std.fs.wasi.PreopenList to get available Dir handles instead");
   1580     }
   1581 
   1582     const err = if (builtin.link_libc) blk: {
   1583         break :blk if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;
   1584     } else blk: {
   1585         break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len));
   1586     };
   1587     switch (err) {
   1588         0 => return mem.spanZ(@ptrCast([*:0]u8, out_buffer.ptr)),
   1589         EFAULT => unreachable,
   1590         EINVAL => unreachable,
   1591         ENOENT => return error.CurrentWorkingDirectoryUnlinked,
   1592         ERANGE => return error.NameTooLong,
   1593         else => return unexpectedErrno(@intCast(usize, err)),
   1594     }
   1595 }
   1596 
   1597 pub const SymLinkError = error{
   1598     /// In WASI, this error may occur when the file descriptor does
   1599     /// not hold the required rights to create a new symbolic link relative to it.
   1600     AccessDenied,
   1601     DiskQuota,
   1602     PathAlreadyExists,
   1603     FileSystem,
   1604     SymLinkLoop,
   1605     FileNotFound,
   1606     SystemResources,
   1607     NoSpaceLeft,
   1608     ReadOnlyFileSystem,
   1609     NotDir,
   1610     NameTooLong,
   1611     InvalidUtf8,
   1612     BadPathName,
   1613 } || UnexpectedError;
   1614 
   1615 /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
   1616 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
   1617 /// one; the latter case is known as a dangling link.
   1618 /// If `sym_link_path` exists, it will not be overwritten.
   1619 /// See also `symlinkZ.
   1620 pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
   1621     if (builtin.os.tag == .wasi) {
   1622         @compileError("symlink is not supported in WASI; use symlinkat instead");
   1623     }
   1624     if (builtin.os.tag == .windows) {
   1625         @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
   1626     }
   1627     const target_path_c = try toPosixPath(target_path);
   1628     const sym_link_path_c = try toPosixPath(sym_link_path);
   1629     return symlinkZ(&target_path_c, &sym_link_path_c);
   1630 }
   1631 
   1632 pub const symlinkC = @compileError("deprecated: renamed to symlinkZ");
   1633 
   1634 /// This is the same as `symlink` except the parameters are null-terminated pointers.
   1635 /// See also `symlink`.
   1636 pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void {
   1637     if (builtin.os.tag == .windows) {
   1638         @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
   1639     }
   1640     switch (errno(system.symlink(target_path, sym_link_path))) {
   1641         0 => return,
   1642         EFAULT => unreachable,
   1643         EINVAL => unreachable,
   1644         EACCES => return error.AccessDenied,
   1645         EPERM => return error.AccessDenied,
   1646         EDQUOT => return error.DiskQuota,
   1647         EEXIST => return error.PathAlreadyExists,
   1648         EIO => return error.FileSystem,
   1649         ELOOP => return error.SymLinkLoop,
   1650         ENAMETOOLONG => return error.NameTooLong,
   1651         ENOENT => return error.FileNotFound,
   1652         ENOTDIR => return error.NotDir,
   1653         ENOMEM => return error.SystemResources,
   1654         ENOSPC => return error.NoSpaceLeft,
   1655         EROFS => return error.ReadOnlyFileSystem,
   1656         else => |err| return unexpectedErrno(err),
   1657     }
   1658 }
   1659 
   1660 /// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string
   1661 /// `target_path` **relative** to `newdirfd` directory handle.
   1662 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
   1663 /// one; the latter case is known as a dangling link.
   1664 /// If `sym_link_path` exists, it will not be overwritten.
   1665 /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
   1666 pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
   1667     if (builtin.os.tag == .wasi) {
   1668         return symlinkatWasi(target_path, newdirfd, sym_link_path);
   1669     }
   1670     if (builtin.os.tag == .windows) {
   1671         @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
   1672     }
   1673     const target_path_c = try toPosixPath(target_path);
   1674     const sym_link_path_c = try toPosixPath(sym_link_path);
   1675     return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
   1676 }
   1677 
   1678 pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ");
   1679 
   1680 /// WASI-only. The same as `symlinkat` but targeting WASI.
   1681 /// See also `symlinkat`.
   1682 pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
   1683     switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
   1684         wasi.ESUCCESS => {},
   1685         wasi.EFAULT => unreachable,
   1686         wasi.EINVAL => unreachable,
   1687         wasi.EACCES => return error.AccessDenied,
   1688         wasi.EPERM => return error.AccessDenied,
   1689         wasi.EDQUOT => return error.DiskQuota,
   1690         wasi.EEXIST => return error.PathAlreadyExists,
   1691         wasi.EIO => return error.FileSystem,
   1692         wasi.ELOOP => return error.SymLinkLoop,
   1693         wasi.ENAMETOOLONG => return error.NameTooLong,
   1694         wasi.ENOENT => return error.FileNotFound,
   1695         wasi.ENOTDIR => return error.NotDir,
   1696         wasi.ENOMEM => return error.SystemResources,
   1697         wasi.ENOSPC => return error.NoSpaceLeft,
   1698         wasi.EROFS => return error.ReadOnlyFileSystem,
   1699         wasi.ENOTCAPABLE => return error.AccessDenied,
   1700         else => |err| return unexpectedErrno(err),
   1701     }
   1702 }
   1703 
   1704 /// The same as `symlinkat` except the parameters are null-terminated pointers.
   1705 /// See also `symlinkat`.
   1706 pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
   1707     if (builtin.os.tag == .windows) {
   1708         @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
   1709     }
   1710     switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
   1711         0 => return,
   1712         EFAULT => unreachable,
   1713         EINVAL => unreachable,
   1714         EACCES => return error.AccessDenied,
   1715         EPERM => return error.AccessDenied,
   1716         EDQUOT => return error.DiskQuota,
   1717         EEXIST => return error.PathAlreadyExists,
   1718         EIO => return error.FileSystem,
   1719         ELOOP => return error.SymLinkLoop,
   1720         ENAMETOOLONG => return error.NameTooLong,
   1721         ENOENT => return error.FileNotFound,
   1722         ENOTDIR => return error.NotDir,
   1723         ENOMEM => return error.SystemResources,
   1724         ENOSPC => return error.NoSpaceLeft,
   1725         EROFS => return error.ReadOnlyFileSystem,
   1726         else => |err| return unexpectedErrno(err),
   1727     }
   1728 }
   1729 
   1730 pub const UnlinkError = error{
   1731     FileNotFound,
   1732 
   1733     /// In WASI, this error may occur when the file descriptor does
   1734     /// not hold the required rights to unlink a resource by path relative to it.
   1735     AccessDenied,
   1736     FileBusy,
   1737     FileSystem,
   1738     IsDir,
   1739     SymLinkLoop,
   1740     NameTooLong,
   1741     NotDir,
   1742     SystemResources,
   1743     ReadOnlyFileSystem,
   1744 
   1745     /// On Windows, file paths must be valid Unicode.
   1746     InvalidUtf8,
   1747 
   1748     /// On Windows, file paths cannot contain these characters:
   1749     /// '/', '*', '?', '"', '<', '>', '|'
   1750     BadPathName,
   1751 } || UnexpectedError;
   1752 
   1753 /// Delete a name and possibly the file it refers to.
   1754 /// See also `unlinkC`.
   1755 pub fn unlink(file_path: []const u8) UnlinkError!void {
   1756     if (builtin.os.tag == .wasi) {
   1757         @compileError("unlink is not supported in WASI; use unlinkat instead");
   1758     } else if (builtin.os.tag == .windows) {
   1759         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   1760         return unlinkW(file_path_w.span());
   1761     } else {
   1762         const file_path_c = try toPosixPath(file_path);
   1763         return unlinkZ(&file_path_c);
   1764     }
   1765 }
   1766 
   1767 pub const unlinkC = @compileError("deprecated: renamed to unlinkZ");
   1768 
   1769 /// Same as `unlink` except the parameter is a null terminated UTF8-encoded string.
   1770 pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
   1771     if (builtin.os.tag == .windows) {
   1772         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   1773         return unlinkW(file_path_w.span());
   1774     }
   1775     switch (errno(system.unlink(file_path))) {
   1776         0 => return,
   1777         EACCES => return error.AccessDenied,
   1778         EPERM => return error.AccessDenied,
   1779         EBUSY => return error.FileBusy,
   1780         EFAULT => unreachable,
   1781         EINVAL => unreachable,
   1782         EIO => return error.FileSystem,
   1783         EISDIR => return error.IsDir,
   1784         ELOOP => return error.SymLinkLoop,
   1785         ENAMETOOLONG => return error.NameTooLong,
   1786         ENOENT => return error.FileNotFound,
   1787         ENOTDIR => return error.NotDir,
   1788         ENOMEM => return error.SystemResources,
   1789         EROFS => return error.ReadOnlyFileSystem,
   1790         else => |err| return unexpectedErrno(err),
   1791     }
   1792 }
   1793 
   1794 /// Windows-only. Same as `unlink` except the parameter is null-terminated, WTF16 encoded.
   1795 pub fn unlinkW(file_path_w: []const u16) UnlinkError!void {
   1796     return windows.DeleteFile(file_path_w, .{ .dir = std.fs.cwd().fd });
   1797 }
   1798 
   1799 pub const UnlinkatError = UnlinkError || error{
   1800     /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.
   1801     DirNotEmpty,
   1802 };
   1803 
   1804 /// Delete a file name and possibly the file it refers to, based on an open directory handle.
   1805 /// Asserts that the path parameter has no null bytes.
   1806 pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
   1807     if (builtin.os.tag == .windows) {
   1808         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   1809         return unlinkatW(dirfd, file_path_w.span(), flags);
   1810     } else if (builtin.os.tag == .wasi) {
   1811         return unlinkatWasi(dirfd, file_path, flags);
   1812     } else {
   1813         const file_path_c = try toPosixPath(file_path);
   1814         return unlinkatZ(dirfd, &file_path_c, flags);
   1815     }
   1816 }
   1817 
   1818 pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ");
   1819 
   1820 /// WASI-only. Same as `unlinkat` but targeting WASI.
   1821 /// See also `unlinkat`.
   1822 pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
   1823     const remove_dir = (flags & AT_REMOVEDIR) != 0;
   1824     const res = if (remove_dir)
   1825         wasi.path_remove_directory(dirfd, file_path.ptr, file_path.len)
   1826     else
   1827         wasi.path_unlink_file(dirfd, file_path.ptr, file_path.len);
   1828     switch (res) {
   1829         wasi.ESUCCESS => return,
   1830         wasi.EACCES => return error.AccessDenied,
   1831         wasi.EPERM => return error.AccessDenied,
   1832         wasi.EBUSY => return error.FileBusy,
   1833         wasi.EFAULT => unreachable,
   1834         wasi.EIO => return error.FileSystem,
   1835         wasi.EISDIR => return error.IsDir,
   1836         wasi.ELOOP => return error.SymLinkLoop,
   1837         wasi.ENAMETOOLONG => return error.NameTooLong,
   1838         wasi.ENOENT => return error.FileNotFound,
   1839         wasi.ENOTDIR => return error.NotDir,
   1840         wasi.ENOMEM => return error.SystemResources,
   1841         wasi.EROFS => return error.ReadOnlyFileSystem,
   1842         wasi.ENOTEMPTY => return error.DirNotEmpty,
   1843         wasi.ENOTCAPABLE => return error.AccessDenied,
   1844 
   1845         wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component
   1846         wasi.EBADF => unreachable, // always a race condition
   1847 
   1848         else => |err| return unexpectedErrno(err),
   1849     }
   1850 }
   1851 
   1852 /// Same as `unlinkat` but `file_path` is a null-terminated string.
   1853 pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
   1854     if (builtin.os.tag == .windows) {
   1855         const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
   1856         return unlinkatW(dirfd, file_path_w.span(), flags);
   1857     }
   1858     switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
   1859         0 => return,
   1860         EACCES => return error.AccessDenied,
   1861         EPERM => return error.AccessDenied,
   1862         EBUSY => return error.FileBusy,
   1863         EFAULT => unreachable,
   1864         EIO => return error.FileSystem,
   1865         EISDIR => return error.IsDir,
   1866         ELOOP => return error.SymLinkLoop,
   1867         ENAMETOOLONG => return error.NameTooLong,
   1868         ENOENT => return error.FileNotFound,
   1869         ENOTDIR => return error.NotDir,
   1870         ENOMEM => return error.SystemResources,
   1871         EROFS => return error.ReadOnlyFileSystem,
   1872         ENOTEMPTY => return error.DirNotEmpty,
   1873 
   1874         EINVAL => unreachable, // invalid flags, or pathname has . as last component
   1875         EBADF => unreachable, // always a race condition
   1876 
   1877         else => |err| return unexpectedErrno(err),
   1878     }
   1879 }
   1880 
   1881 /// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
   1882 pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
   1883     const remove_dir = (flags & AT_REMOVEDIR) != 0;
   1884     return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
   1885 }
   1886 
   1887 const RenameError = error{
   1888     /// In WASI, this error may occur when the file descriptor does
   1889     /// not hold the required rights to rename a resource by path relative to it.
   1890     AccessDenied,
   1891     FileBusy,
   1892     DiskQuota,
   1893     IsDir,
   1894     SymLinkLoop,
   1895     LinkQuotaExceeded,
   1896     NameTooLong,
   1897     FileNotFound,
   1898     NotDir,
   1899     SystemResources,
   1900     NoSpaceLeft,
   1901     PathAlreadyExists,
   1902     ReadOnlyFileSystem,
   1903     RenameAcrossMountPoints,
   1904     InvalidUtf8,
   1905     BadPathName,
   1906     NoDevice,
   1907     SharingViolation,
   1908     PipeBusy,
   1909 } || UnexpectedError;
   1910 
   1911 /// Change the name or location of a file.
   1912 pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
   1913     if (builtin.os.tag == .wasi) {
   1914         @compileError("rename is not supported in WASI; use renameat instead");
   1915     } else if (builtin.os.tag == .windows) {
   1916         const old_path_w = try windows.sliceToPrefixedFileW(old_path);
   1917         const new_path_w = try windows.sliceToPrefixedFileW(new_path);
   1918         return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
   1919     } else {
   1920         const old_path_c = try toPosixPath(old_path);
   1921         const new_path_c = try toPosixPath(new_path);
   1922         return renameZ(&old_path_c, &new_path_c);
   1923     }
   1924 }
   1925 
   1926 pub const renameC = @compileError("deprecated: renamed to renameZ");
   1927 
   1928 /// Same as `rename` except the parameters are null-terminated byte arrays.
   1929 pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void {
   1930     if (builtin.os.tag == .windows) {
   1931         const old_path_w = try windows.cStrToPrefixedFileW(old_path);
   1932         const new_path_w = try windows.cStrToPrefixedFileW(new_path);
   1933         return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
   1934     }
   1935     switch (errno(system.rename(old_path, new_path))) {
   1936         0 => return,
   1937         EACCES => return error.AccessDenied,
   1938         EPERM => return error.AccessDenied,
   1939         EBUSY => return error.FileBusy,
   1940         EDQUOT => return error.DiskQuota,
   1941         EFAULT => unreachable,
   1942         EINVAL => unreachable,
   1943         EISDIR => return error.IsDir,
   1944         ELOOP => return error.SymLinkLoop,
   1945         EMLINK => return error.LinkQuotaExceeded,
   1946         ENAMETOOLONG => return error.NameTooLong,
   1947         ENOENT => return error.FileNotFound,
   1948         ENOTDIR => return error.NotDir,
   1949         ENOMEM => return error.SystemResources,
   1950         ENOSPC => return error.NoSpaceLeft,
   1951         EEXIST => return error.PathAlreadyExists,
   1952         ENOTEMPTY => return error.PathAlreadyExists,
   1953         EROFS => return error.ReadOnlyFileSystem,
   1954         EXDEV => return error.RenameAcrossMountPoints,
   1955         else => |err| return unexpectedErrno(err),
   1956     }
   1957 }
   1958 
   1959 /// Same as `rename` except the parameters are null-terminated UTF16LE encoded byte arrays.
   1960 /// Assumes target is Windows.
   1961 pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!void {
   1962     const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
   1963     return windows.MoveFileExW(old_path, new_path, flags);
   1964 }
   1965 
   1966 /// Change the name or location of a file based on an open directory handle.
   1967 pub fn renameat(
   1968     old_dir_fd: fd_t,
   1969     old_path: []const u8,
   1970     new_dir_fd: fd_t,
   1971     new_path: []const u8,
   1972 ) RenameError!void {
   1973     if (builtin.os.tag == .windows) {
   1974         const old_path_w = try windows.sliceToPrefixedFileW(old_path);
   1975         const new_path_w = try windows.sliceToPrefixedFileW(new_path);
   1976         return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
   1977     } else if (builtin.os.tag == .wasi) {
   1978         return renameatWasi(old_dir_fd, old_path, new_dir_fd, new_path);
   1979     } else {
   1980         const old_path_c = try toPosixPath(old_path);
   1981         const new_path_c = try toPosixPath(new_path);
   1982         return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c);
   1983     }
   1984 }
   1985 
   1986 /// WASI-only. Same as `renameat` expect targeting WASI.
   1987 /// See also `renameat`.
   1988 pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void {
   1989     switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) {
   1990         wasi.ESUCCESS => return,
   1991         wasi.EACCES => return error.AccessDenied,
   1992         wasi.EPERM => return error.AccessDenied,
   1993         wasi.EBUSY => return error.FileBusy,
   1994         wasi.EDQUOT => return error.DiskQuota,
   1995         wasi.EFAULT => unreachable,
   1996         wasi.EINVAL => unreachable,
   1997         wasi.EISDIR => return error.IsDir,
   1998         wasi.ELOOP => return error.SymLinkLoop,
   1999         wasi.EMLINK => return error.LinkQuotaExceeded,
   2000         wasi.ENAMETOOLONG => return error.NameTooLong,
   2001         wasi.ENOENT => return error.FileNotFound,
   2002         wasi.ENOTDIR => return error.NotDir,
   2003         wasi.ENOMEM => return error.SystemResources,
   2004         wasi.ENOSPC => return error.NoSpaceLeft,
   2005         wasi.EEXIST => return error.PathAlreadyExists,
   2006         wasi.ENOTEMPTY => return error.PathAlreadyExists,
   2007         wasi.EROFS => return error.ReadOnlyFileSystem,
   2008         wasi.EXDEV => return error.RenameAcrossMountPoints,
   2009         wasi.ENOTCAPABLE => return error.AccessDenied,
   2010         else => |err| return unexpectedErrno(err),
   2011     }
   2012 }
   2013 
   2014 /// Same as `renameat` except the parameters are null-terminated byte arrays.
   2015 pub fn renameatZ(
   2016     old_dir_fd: fd_t,
   2017     old_path: [*:0]const u8,
   2018     new_dir_fd: fd_t,
   2019     new_path: [*:0]const u8,
   2020 ) RenameError!void {
   2021     if (builtin.os.tag == .windows) {
   2022         const old_path_w = try windows.cStrToPrefixedFileW(old_path);
   2023         const new_path_w = try windows.cStrToPrefixedFileW(new_path);
   2024         return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
   2025     }
   2026 
   2027     switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) {
   2028         0 => return,
   2029         EACCES => return error.AccessDenied,
   2030         EPERM => return error.AccessDenied,
   2031         EBUSY => return error.FileBusy,
   2032         EDQUOT => return error.DiskQuota,
   2033         EFAULT => unreachable,
   2034         EINVAL => unreachable,
   2035         EISDIR => return error.IsDir,
   2036         ELOOP => return error.SymLinkLoop,
   2037         EMLINK => return error.LinkQuotaExceeded,
   2038         ENAMETOOLONG => return error.NameTooLong,
   2039         ENOENT => return error.FileNotFound,
   2040         ENOTDIR => return error.NotDir,
   2041         ENOMEM => return error.SystemResources,
   2042         ENOSPC => return error.NoSpaceLeft,
   2043         EEXIST => return error.PathAlreadyExists,
   2044         ENOTEMPTY => return error.PathAlreadyExists,
   2045         EROFS => return error.ReadOnlyFileSystem,
   2046         EXDEV => return error.RenameAcrossMountPoints,
   2047         else => |err| return unexpectedErrno(err),
   2048     }
   2049 }
   2050 
   2051 /// Same as `renameat` but Windows-only and the path parameters are
   2052 /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
   2053 pub fn renameatW(
   2054     old_dir_fd: fd_t,
   2055     old_path_w: []const u16,
   2056     new_dir_fd: fd_t,
   2057     new_path_w: []const u16,
   2058     ReplaceIfExists: windows.BOOLEAN,
   2059 ) RenameError!void {
   2060     const src_fd = windows.OpenFile(old_path_w, .{
   2061         .dir = old_dir_fd,
   2062         .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE,
   2063         .creation = windows.FILE_OPEN,
   2064         .io_mode = .blocking,
   2065     }) catch |err| switch (err) {
   2066         error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
   2067         else => |e| return e,
   2068     };
   2069     defer windows.CloseHandle(src_fd);
   2070 
   2071     const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1);
   2072     var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined;
   2073     const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2;
   2074     if (struct_len > struct_buf_len) return error.NameTooLong;
   2075 
   2076     const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf);
   2077 
   2078     rename_info.* = .{
   2079         .ReplaceIfExists = ReplaceIfExists,
   2080         .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
   2081         .FileNameLength = @intCast(u32, new_path_w.len * 2), // already checked error.NameTooLong
   2082         .FileName = undefined,
   2083     };
   2084     std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w);
   2085 
   2086     var io_status_block: windows.IO_STATUS_BLOCK = undefined;
   2087 
   2088     const rc = windows.ntdll.NtSetInformationFile(
   2089         src_fd,
   2090         &io_status_block,
   2091         rename_info,
   2092         @intCast(u32, struct_len), // already checked for error.NameTooLong
   2093         .FileRenameInformation,
   2094     );
   2095 
   2096     switch (rc) {
   2097         .SUCCESS => return,
   2098         .INVALID_HANDLE => unreachable,
   2099         .INVALID_PARAMETER => unreachable,
   2100         .OBJECT_PATH_SYNTAX_BAD => unreachable,
   2101         .ACCESS_DENIED => return error.AccessDenied,
   2102         .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   2103         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   2104         else => return windows.unexpectedStatus(rc),
   2105     }
   2106 }
   2107 
   2108 pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
   2109     if (builtin.os.tag == .windows) {
   2110         const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
   2111         return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
   2112     } else if (builtin.os.tag == .wasi) {
   2113         return mkdiratWasi(dir_fd, sub_dir_path, mode);
   2114     } else {
   2115         const sub_dir_path_c = try toPosixPath(sub_dir_path);
   2116         return mkdiratZ(dir_fd, &sub_dir_path_c, mode);
   2117     }
   2118 }
   2119 
   2120 pub const mkdiratC = @compileError("deprecated: renamed to mkdiratZ");
   2121 
   2122 pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
   2123     switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) {
   2124         wasi.ESUCCESS => return,
   2125         wasi.EACCES => return error.AccessDenied,
   2126         wasi.EBADF => unreachable,
   2127         wasi.EPERM => return error.AccessDenied,
   2128         wasi.EDQUOT => return error.DiskQuota,
   2129         wasi.EEXIST => return error.PathAlreadyExists,
   2130         wasi.EFAULT => unreachable,
   2131         wasi.ELOOP => return error.SymLinkLoop,
   2132         wasi.EMLINK => return error.LinkQuotaExceeded,
   2133         wasi.ENAMETOOLONG => return error.NameTooLong,
   2134         wasi.ENOENT => return error.FileNotFound,
   2135         wasi.ENOMEM => return error.SystemResources,
   2136         wasi.ENOSPC => return error.NoSpaceLeft,
   2137         wasi.ENOTDIR => return error.NotDir,
   2138         wasi.EROFS => return error.ReadOnlyFileSystem,
   2139         wasi.ENOTCAPABLE => return error.AccessDenied,
   2140         else => |err| return unexpectedErrno(err),
   2141     }
   2142 }
   2143 
   2144 pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
   2145     if (builtin.os.tag == .windows) {
   2146         const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path);
   2147         return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);
   2148     }
   2149     switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) {
   2150         0 => return,
   2151         EACCES => return error.AccessDenied,
   2152         EBADF => unreachable,
   2153         EPERM => return error.AccessDenied,
   2154         EDQUOT => return error.DiskQuota,
   2155         EEXIST => return error.PathAlreadyExists,
   2156         EFAULT => unreachable,
   2157         ELOOP => return error.SymLinkLoop,
   2158         EMLINK => return error.LinkQuotaExceeded,
   2159         ENAMETOOLONG => return error.NameTooLong,
   2160         ENOENT => return error.FileNotFound,
   2161         ENOMEM => return error.SystemResources,
   2162         ENOSPC => return error.NoSpaceLeft,
   2163         ENOTDIR => return error.NotDir,
   2164         EROFS => return error.ReadOnlyFileSystem,
   2165         else => |err| return unexpectedErrno(err),
   2166     }
   2167 }
   2168 
   2169 pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
   2170     const sub_dir_handle = windows.OpenFile(sub_path_w, .{
   2171         .dir = dir_fd,
   2172         .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
   2173         .creation = windows.FILE_CREATE,
   2174         .io_mode = .blocking,
   2175         .open_dir = true,
   2176     }) catch |err| switch (err) {
   2177         error.IsDir => unreachable,
   2178         error.PipeBusy => unreachable,
   2179         error.WouldBlock => unreachable,
   2180         else => |e| return e,
   2181     };
   2182     windows.CloseHandle(sub_dir_handle);
   2183 }
   2184 
   2185 pub const MakeDirError = error{
   2186     /// In WASI, this error may occur when the file descriptor does
   2187     /// not hold the required rights to create a new directory relative to it.
   2188     AccessDenied,
   2189     DiskQuota,
   2190     PathAlreadyExists,
   2191     SymLinkLoop,
   2192     LinkQuotaExceeded,
   2193     NameTooLong,
   2194     FileNotFound,
   2195     SystemResources,
   2196     NoSpaceLeft,
   2197     NotDir,
   2198     ReadOnlyFileSystem,
   2199     InvalidUtf8,
   2200     BadPathName,
   2201     NoDevice,
   2202 } || UnexpectedError;
   2203 
   2204 /// Create a directory.
   2205 /// `mode` is ignored on Windows.
   2206 pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
   2207     if (builtin.os.tag == .wasi) {
   2208         @compileError("mkdir is not supported in WASI; use mkdirat instead");
   2209     } else if (builtin.os.tag == .windows) {
   2210         const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
   2211         return mkdirW(dir_path_w.span(), mode);
   2212     } else {
   2213         const dir_path_c = try toPosixPath(dir_path);
   2214         return mkdirZ(&dir_path_c, mode);
   2215     }
   2216 }
   2217 
   2218 /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
   2219 pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
   2220     if (builtin.os.tag == .windows) {
   2221         const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
   2222         return mkdirW(dir_path_w.span(), mode);
   2223     }
   2224     switch (errno(system.mkdir(dir_path, mode))) {
   2225         0 => return,
   2226         EACCES => return error.AccessDenied,
   2227         EPERM => return error.AccessDenied,
   2228         EDQUOT => return error.DiskQuota,
   2229         EEXIST => return error.PathAlreadyExists,
   2230         EFAULT => unreachable,
   2231         ELOOP => return error.SymLinkLoop,
   2232         EMLINK => return error.LinkQuotaExceeded,
   2233         ENAMETOOLONG => return error.NameTooLong,
   2234         ENOENT => return error.FileNotFound,
   2235         ENOMEM => return error.SystemResources,
   2236         ENOSPC => return error.NoSpaceLeft,
   2237         ENOTDIR => return error.NotDir,
   2238         EROFS => return error.ReadOnlyFileSystem,
   2239         else => |err| return unexpectedErrno(err),
   2240     }
   2241 }
   2242 
   2243 /// Windows-only. Same as `mkdir` but the parameters is  WTF16 encoded.
   2244 pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
   2245     const sub_dir_handle = windows.OpenFile(dir_path_w, .{
   2246         .dir = std.fs.cwd().fd,
   2247         .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
   2248         .creation = windows.FILE_CREATE,
   2249         .io_mode = .blocking,
   2250         .open_dir = true,
   2251     }) catch |err| switch (err) {
   2252         error.IsDir => unreachable,
   2253         error.PipeBusy => unreachable,
   2254         error.WouldBlock => unreachable,
   2255         else => |e| return e,
   2256     };
   2257     windows.CloseHandle(sub_dir_handle);
   2258 }
   2259 
   2260 pub const DeleteDirError = error{
   2261     AccessDenied,
   2262     FileBusy,
   2263     SymLinkLoop,
   2264     NameTooLong,
   2265     FileNotFound,
   2266     SystemResources,
   2267     NotDir,
   2268     DirNotEmpty,
   2269     ReadOnlyFileSystem,
   2270     InvalidUtf8,
   2271     BadPathName,
   2272 } || UnexpectedError;
   2273 
   2274 /// Deletes an empty directory.
   2275 pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
   2276     if (builtin.os.tag == .wasi) {
   2277         @compileError("rmdir is not supported in WASI; use unlinkat instead");
   2278     } else if (builtin.os.tag == .windows) {
   2279         const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
   2280         return rmdirW(dir_path_w.span());
   2281     } else {
   2282         const dir_path_c = try toPosixPath(dir_path);
   2283         return rmdirZ(&dir_path_c);
   2284     }
   2285 }
   2286 
   2287 pub const rmdirC = @compileError("deprecated: renamed to rmdirZ");
   2288 
   2289 /// Same as `rmdir` except the parameter is null-terminated.
   2290 pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
   2291     if (builtin.os.tag == .windows) {
   2292         const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
   2293         return rmdirW(dir_path_w.span());
   2294     }
   2295     switch (errno(system.rmdir(dir_path))) {
   2296         0 => return,
   2297         EACCES => return error.AccessDenied,
   2298         EPERM => return error.AccessDenied,
   2299         EBUSY => return error.FileBusy,
   2300         EFAULT => unreachable,
   2301         EINVAL => unreachable,
   2302         ELOOP => return error.SymLinkLoop,
   2303         ENAMETOOLONG => return error.NameTooLong,
   2304         ENOENT => return error.FileNotFound,
   2305         ENOMEM => return error.SystemResources,
   2306         ENOTDIR => return error.NotDir,
   2307         EEXIST => return error.DirNotEmpty,
   2308         ENOTEMPTY => return error.DirNotEmpty,
   2309         EROFS => return error.ReadOnlyFileSystem,
   2310         else => |err| return unexpectedErrno(err),
   2311     }
   2312 }
   2313 
   2314 /// Windows-only. Same as `rmdir` except the parameter is WTF16 encoded.
   2315 pub fn rmdirW(dir_path_w: []const u16) DeleteDirError!void {
   2316     return windows.DeleteFile(dir_path_w, .{ .dir = std.fs.cwd().fd, .remove_dir = true }) catch |err| switch (err) {
   2317         error.IsDir => unreachable,
   2318         else => |e| return e,
   2319     };
   2320 }
   2321 
   2322 pub const ChangeCurDirError = error{
   2323     AccessDenied,
   2324     FileSystem,
   2325     SymLinkLoop,
   2326     NameTooLong,
   2327     FileNotFound,
   2328     SystemResources,
   2329     NotDir,
   2330 } || UnexpectedError;
   2331 
   2332 /// Changes the current working directory of the calling process.
   2333 /// `dir_path` is recommended to be a UTF-8 encoded string.
   2334 pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
   2335     if (builtin.os.tag == .wasi) {
   2336         @compileError("chdir is not supported in WASI");
   2337     } else if (builtin.os.tag == .windows) {
   2338         const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
   2339         @compileError("TODO implement chdir for Windows");
   2340     } else {
   2341         const dir_path_c = try toPosixPath(dir_path);
   2342         return chdirZ(&dir_path_c);
   2343     }
   2344 }
   2345 
   2346 pub const chdirC = @compileError("deprecated: renamed to chdirZ");
   2347 
   2348 /// Same as `chdir` except the parameter is null-terminated.
   2349 pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
   2350     if (builtin.os.tag == .windows) {
   2351         const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
   2352         @compileError("TODO implement chdir for Windows");
   2353     }
   2354     switch (errno(system.chdir(dir_path))) {
   2355         0 => return,
   2356         EACCES => return error.AccessDenied,
   2357         EFAULT => unreachable,
   2358         EIO => return error.FileSystem,
   2359         ELOOP => return error.SymLinkLoop,
   2360         ENAMETOOLONG => return error.NameTooLong,
   2361         ENOENT => return error.FileNotFound,
   2362         ENOMEM => return error.SystemResources,
   2363         ENOTDIR => return error.NotDir,
   2364         else => |err| return unexpectedErrno(err),
   2365     }
   2366 }
   2367 
   2368 pub const FchdirError = error{
   2369     AccessDenied,
   2370     NotDir,
   2371     FileSystem,
   2372 } || UnexpectedError;
   2373 
   2374 pub fn fchdir(dirfd: fd_t) FchdirError!void {
   2375     while (true) {
   2376         switch (errno(system.fchdir(dirfd))) {
   2377             0 => return,
   2378             EACCES => return error.AccessDenied,
   2379             EBADF => unreachable,
   2380             ENOTDIR => return error.NotDir,
   2381             EINTR => continue,
   2382             EIO => return error.FileSystem,
   2383             else => |err| return unexpectedErrno(err),
   2384         }
   2385     }
   2386 }
   2387 
   2388 pub const ReadLinkError = error{
   2389     /// In WASI, this error may occur when the file descriptor does
   2390     /// not hold the required rights to read value of a symbolic link relative to it.
   2391     AccessDenied,
   2392     FileSystem,
   2393     SymLinkLoop,
   2394     NameTooLong,
   2395     FileNotFound,
   2396     SystemResources,
   2397     NotDir,
   2398     InvalidUtf8,
   2399     BadPathName,
   2400     /// Windows-only. This error may occur if the opened reparse point is
   2401     /// of unsupported type.
   2402     UnsupportedReparsePointType,
   2403 } || UnexpectedError;
   2404 
   2405 /// Read value of a symbolic link.
   2406 /// The return value is a slice of `out_buffer` from index 0.
   2407 pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
   2408     if (builtin.os.tag == .wasi) {
   2409         @compileError("readlink is not supported in WASI; use readlinkat instead");
   2410     } else if (builtin.os.tag == .windows) {
   2411         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   2412         return readlinkW(file_path_w.span(), out_buffer);
   2413     } else {
   2414         const file_path_c = try toPosixPath(file_path);
   2415         return readlinkZ(&file_path_c, out_buffer);
   2416     }
   2417 }
   2418 
   2419 pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
   2420 
   2421 /// Windows-only. Same as `readlink` except `file_path` is WTF16 encoded.
   2422 /// See also `readlinkZ`.
   2423 pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
   2424     return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
   2425 }
   2426 
   2427 /// Same as `readlink` except `file_path` is null-terminated.
   2428 pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
   2429     if (builtin.os.tag == .windows) {
   2430         const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);
   2431         return readlinkW(file_path_w.span(), out_buffer);
   2432     }
   2433     const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
   2434     switch (errno(rc)) {
   2435         0 => return out_buffer[0..@bitCast(usize, rc)],
   2436         EACCES => return error.AccessDenied,
   2437         EFAULT => unreachable,
   2438         EINVAL => unreachable,
   2439         EIO => return error.FileSystem,
   2440         ELOOP => return error.SymLinkLoop,
   2441         ENAMETOOLONG => return error.NameTooLong,
   2442         ENOENT => return error.FileNotFound,
   2443         ENOMEM => return error.SystemResources,
   2444         ENOTDIR => return error.NotDir,
   2445         else => |err| return unexpectedErrno(err),
   2446     }
   2447 }
   2448 
   2449 /// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
   2450 /// The return value is a slice of `out_buffer` from index 0.
   2451 /// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
   2452 pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
   2453     if (builtin.os.tag == .wasi) {
   2454         return readlinkatWasi(dirfd, file_path, out_buffer);
   2455     }
   2456     if (builtin.os.tag == .windows) {
   2457         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   2458         return readlinkatW(dirfd, file_path_w.span(), out_buffer);
   2459     }
   2460     const file_path_c = try toPosixPath(file_path);
   2461     return readlinkatZ(dirfd, &file_path_c, out_buffer);
   2462 }
   2463 
   2464 pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ");
   2465 
   2466 /// WASI-only. Same as `readlinkat` but targets WASI.
   2467 /// See also `readlinkat`.
   2468 pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
   2469     var bufused: usize = undefined;
   2470     switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
   2471         wasi.ESUCCESS => return out_buffer[0..bufused],
   2472         wasi.EACCES => return error.AccessDenied,
   2473         wasi.EFAULT => unreachable,
   2474         wasi.EINVAL => unreachable,
   2475         wasi.EIO => return error.FileSystem,
   2476         wasi.ELOOP => return error.SymLinkLoop,
   2477         wasi.ENAMETOOLONG => return error.NameTooLong,
   2478         wasi.ENOENT => return error.FileNotFound,
   2479         wasi.ENOMEM => return error.SystemResources,
   2480         wasi.ENOTDIR => return error.NotDir,
   2481         wasi.ENOTCAPABLE => return error.AccessDenied,
   2482         else => |err| return unexpectedErrno(err),
   2483     }
   2484 }
   2485 
   2486 /// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
   2487 /// See also `readlinkat`.
   2488 pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
   2489     return windows.ReadLink(dirfd, file_path, out_buffer);
   2490 }
   2491 
   2492 /// Same as `readlinkat` except `file_path` is null-terminated.
   2493 /// See also `readlinkat`.
   2494 pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
   2495     if (builtin.os.tag == .windows) {
   2496         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   2497         return readlinkatW(dirfd, file_path_w.span(), out_buffer);
   2498     }
   2499     const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
   2500     switch (errno(rc)) {
   2501         0 => return out_buffer[0..@bitCast(usize, rc)],
   2502         EACCES => return error.AccessDenied,
   2503         EFAULT => unreachable,
   2504         EINVAL => unreachable,
   2505         EIO => return error.FileSystem,
   2506         ELOOP => return error.SymLinkLoop,
   2507         ENAMETOOLONG => return error.NameTooLong,
   2508         ENOENT => return error.FileNotFound,
   2509         ENOMEM => return error.SystemResources,
   2510         ENOTDIR => return error.NotDir,
   2511         else => |err| return unexpectedErrno(err),
   2512     }
   2513 }
   2514 
   2515 pub const SetIdError = error{
   2516     ResourceLimitReached,
   2517     InvalidUserId,
   2518     PermissionDenied,
   2519 } || UnexpectedError;
   2520 
   2521 pub fn setuid(uid: u32) SetIdError!void {
   2522     switch (errno(system.setuid(uid))) {
   2523         0 => return,
   2524         EAGAIN => return error.ResourceLimitReached,
   2525         EINVAL => return error.InvalidUserId,
   2526         EPERM => return error.PermissionDenied,
   2527         else => |err| return unexpectedErrno(err),
   2528     }
   2529 }
   2530 
   2531 pub fn setreuid(ruid: u32, euid: u32) SetIdError!void {
   2532     switch (errno(system.setreuid(ruid, euid))) {
   2533         0 => return,
   2534         EAGAIN => return error.ResourceLimitReached,
   2535         EINVAL => return error.InvalidUserId,
   2536         EPERM => return error.PermissionDenied,
   2537         else => |err| return unexpectedErrno(err),
   2538     }
   2539 }
   2540 
   2541 pub fn setgid(gid: u32) SetIdError!void {
   2542     switch (errno(system.setgid(gid))) {
   2543         0 => return,
   2544         EAGAIN => return error.ResourceLimitReached,
   2545         EINVAL => return error.InvalidUserId,
   2546         EPERM => return error.PermissionDenied,
   2547         else => |err| return unexpectedErrno(err),
   2548     }
   2549 }
   2550 
   2551 pub fn setregid(rgid: u32, egid: u32) SetIdError!void {
   2552     switch (errno(system.setregid(rgid, egid))) {
   2553         0 => return,
   2554         EAGAIN => return error.ResourceLimitReached,
   2555         EINVAL => return error.InvalidUserId,
   2556         EPERM => return error.PermissionDenied,
   2557         else => |err| return unexpectedErrno(err),
   2558     }
   2559 }
   2560 
   2561 /// Test whether a file descriptor refers to a terminal.
   2562 pub fn isatty(handle: fd_t) bool {
   2563     if (builtin.os.tag == .windows) {
   2564         if (isCygwinPty(handle))
   2565             return true;
   2566 
   2567         var out: windows.DWORD = undefined;
   2568         return windows.kernel32.GetConsoleMode(handle, &out) != 0;
   2569     }
   2570     if (builtin.link_libc) {
   2571         return system.isatty(handle) != 0;
   2572     }
   2573     if (builtin.os.tag == .wasi) {
   2574         var statbuf: fdstat_t = undefined;
   2575         const err = system.fd_fdstat_get(handle, &statbuf);
   2576         if (err != 0) {
   2577             // errno = err;
   2578             return false;
   2579         }
   2580 
   2581         // A tty is a character device that we can't seek or tell on.
   2582         if (statbuf.fs_filetype != FILETYPE_CHARACTER_DEVICE or
   2583             (statbuf.fs_rights_base & (RIGHT_FD_SEEK | RIGHT_FD_TELL)) != 0)
   2584         {
   2585             // errno = ENOTTY;
   2586             return false;
   2587         }
   2588 
   2589         return true;
   2590     }
   2591     if (builtin.os.tag == .linux) {
   2592         while (true) {
   2593             var wsz: linux.winsize = undefined;
   2594             const fd = @bitCast(usize, @as(isize, handle));
   2595             switch (linux.syscall3(.ioctl, fd, linux.TIOCGWINSZ, @ptrToInt(&wsz))) {
   2596                 0 => return true,
   2597                 EINTR => continue,
   2598                 else => return false,
   2599             }
   2600         }
   2601     }
   2602     unreachable;
   2603 }
   2604 
   2605 pub fn isCygwinPty(handle: fd_t) bool {
   2606     if (builtin.os.tag != .windows) return false;
   2607 
   2608     const size = @sizeOf(windows.FILE_NAME_INFO);
   2609     var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH);
   2610 
   2611     if (windows.kernel32.GetFileInformationByHandleEx(
   2612         handle,
   2613         windows.FileNameInfo,
   2614         @ptrCast(*c_void, &name_info_bytes),
   2615         name_info_bytes.len,
   2616     ) == 0) {
   2617         return false;
   2618     }
   2619 
   2620     const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]);
   2621     const name_bytes = name_info_bytes[size .. size + @as(usize, name_info.FileNameLength)];
   2622     const name_wide = mem.bytesAsSlice(u16, name_bytes);
   2623     return mem.indexOf(u16, name_wide, &[_]u16{ 'm', 's', 'y', 's', '-' }) != null or
   2624         mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null;
   2625 }
   2626 
   2627 pub const SocketError = error{
   2628     /// Permission to create a socket of the specified type and/or
   2629     /// pro‐tocol is denied.
   2630     PermissionDenied,
   2631 
   2632     /// The implementation does not support the specified address family.
   2633     AddressFamilyNotSupported,
   2634 
   2635     /// Unknown protocol, or protocol family not available.
   2636     ProtocolFamilyNotAvailable,
   2637 
   2638     /// The per-process limit on the number of open file descriptors has been reached.
   2639     ProcessFdQuotaExceeded,
   2640 
   2641     /// The system-wide limit on the total number of open files has been reached.
   2642     SystemFdQuotaExceeded,
   2643 
   2644     /// Insufficient memory is available. The socket cannot be created until sufficient
   2645     /// resources are freed.
   2646     SystemResources,
   2647 
   2648     /// The protocol type or the specified protocol is not supported within this domain.
   2649     ProtocolNotSupported,
   2650 
   2651     /// The socket type is not supported by the protocol.
   2652     SocketTypeNotSupported,
   2653 } || UnexpectedError;
   2654 
   2655 pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
   2656     if (builtin.os.tag == .windows) {
   2657         // NOTE: windows translates the SOCK_NONBLOCK/SOCK_CLOEXEC flags into windows-analagous operations
   2658         const filtered_sock_type = socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC);
   2659         const flags: u32 = if ((socket_type & SOCK_CLOEXEC) != 0) windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT else 0;
   2660         const rc = windows.ws2_32.WSASocketW(@intCast(c_int, domain), @intCast(c_int, filtered_sock_type), @intCast(c_int, protocol), null, 0, flags);
   2661         if (rc == windows.ws2_32.INVALID_SOCKET) switch (windows.ws2_32.WSAGetLastError()) {
   2662             .WSAEMFILE => return error.ProcessFdQuotaExceeded,
   2663             .WSAENOBUFS => return error.SystemResources,
   2664             .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
   2665             .WSAEPROTONOSUPPORT => return error.ProtocolNotSupported,
   2666             else => |err| return windows.unexpectedWSAError(err),
   2667         };
   2668         errdefer windows.closesocket(rc) catch unreachable;
   2669         if ((socket_type & SOCK_NONBLOCK) != 0) {
   2670             var mode: c_ulong = 1; // nonblocking
   2671             if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) {
   2672                 switch (windows.ws2_32.WSAGetLastError()) {
   2673                     // have not identified any error codes that should be handled yet
   2674                     else => unreachable,
   2675                 }
   2676             }
   2677         }
   2678         return rc;
   2679     }
   2680 
   2681     const have_sock_flags = comptime !std.Target.current.isDarwin();
   2682     const filtered_sock_type = if (!have_sock_flags)
   2683         socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC)
   2684     else
   2685         socket_type;
   2686     const rc = system.socket(domain, filtered_sock_type, protocol);
   2687     switch (errno(rc)) {
   2688         0 => {
   2689             const fd = @intCast(fd_t, rc);
   2690             if (!have_sock_flags) {
   2691                 try setSockFlags(fd, socket_type);
   2692             }
   2693             return fd;
   2694         },
   2695         EACCES => return error.PermissionDenied,
   2696         EAFNOSUPPORT => return error.AddressFamilyNotSupported,
   2697         EINVAL => return error.ProtocolFamilyNotAvailable,
   2698         EMFILE => return error.ProcessFdQuotaExceeded,
   2699         ENFILE => return error.SystemFdQuotaExceeded,
   2700         ENOBUFS => return error.SystemResources,
   2701         ENOMEM => return error.SystemResources,
   2702         EPROTONOSUPPORT => return error.ProtocolNotSupported,
   2703         EPROTOTYPE => return error.SocketTypeNotSupported,
   2704         else => |err| return unexpectedErrno(err),
   2705     }
   2706 }
   2707 
   2708 pub const BindError = error{
   2709     /// The address is protected, and the user is not the superuser.
   2710     /// For UNIX domain sockets: Search permission is denied on  a  component
   2711     /// of  the  path  prefix.
   2712     AccessDenied,
   2713 
   2714     /// The given address is already in use, or in the case of Internet domain sockets,
   2715     /// The  port number was specified as zero in the socket
   2716     /// address structure, but, upon attempting to bind to  an  ephemeral  port,  it  was
   2717     /// determined  that  all  port  numbers in the ephemeral port range are currently in
   2718     /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
   2719     AddressInUse,
   2720 
   2721     /// A nonexistent interface was requested or the requested address was not local.
   2722     AddressNotAvailable,
   2723 
   2724     /// Too many symbolic links were encountered in resolving addr.
   2725     SymLinkLoop,
   2726 
   2727     /// addr is too long.
   2728     NameTooLong,
   2729 
   2730     /// A component in the directory prefix of the socket pathname does not exist.
   2731     FileNotFound,
   2732 
   2733     /// Insufficient kernel memory was available.
   2734     SystemResources,
   2735 
   2736     /// A component of the path prefix is not a directory.
   2737     NotDir,
   2738 
   2739     /// The socket inode would reside on a read-only filesystem.
   2740     ReadOnlyFileSystem,
   2741 } || UnexpectedError;
   2742 
   2743 /// addr is `*const T` where T is one of the sockaddr
   2744 pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void {
   2745     const rc = system.bind(sockfd, addr, len);
   2746     switch (errno(rc)) {
   2747         0 => return,
   2748         EACCES => return error.AccessDenied,
   2749         EADDRINUSE => return error.AddressInUse,
   2750         EBADF => unreachable, // always a race condition if this error is returned
   2751         EINVAL => unreachable, // invalid parameters
   2752         ENOTSOCK => unreachable, // invalid `sockfd`
   2753         EADDRNOTAVAIL => return error.AddressNotAvailable,
   2754         EFAULT => unreachable, // invalid `addr` pointer
   2755         ELOOP => return error.SymLinkLoop,
   2756         ENAMETOOLONG => return error.NameTooLong,
   2757         ENOENT => return error.FileNotFound,
   2758         ENOMEM => return error.SystemResources,
   2759         ENOTDIR => return error.NotDir,
   2760         EROFS => return error.ReadOnlyFileSystem,
   2761         else => |err| return unexpectedErrno(err),
   2762     }
   2763 }
   2764 
   2765 const ListenError = error{
   2766     /// Another socket is already listening on the same port.
   2767     /// For Internet domain sockets, the  socket referred to by sockfd had not previously
   2768     /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
   2769     /// was determined that all port numbers in the ephemeral port range are currently in
   2770     /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
   2771     AddressInUse,
   2772 
   2773     /// The file descriptor sockfd does not refer to a socket.
   2774     FileDescriptorNotASocket,
   2775 
   2776     /// The socket is not of a type that supports the listen() operation.
   2777     OperationNotSupported,
   2778 } || UnexpectedError;
   2779 
   2780 pub fn listen(sockfd: fd_t, backlog: u32) ListenError!void {
   2781     const rc = system.listen(sockfd, backlog);
   2782     switch (errno(rc)) {
   2783         0 => return,
   2784         EADDRINUSE => return error.AddressInUse,
   2785         EBADF => unreachable,
   2786         ENOTSOCK => return error.FileDescriptorNotASocket,
   2787         EOPNOTSUPP => return error.OperationNotSupported,
   2788         else => |err| return unexpectedErrno(err),
   2789     }
   2790 }
   2791 
   2792 pub const AcceptError = error{
   2793     ConnectionAborted,
   2794 
   2795     /// The per-process limit on the number of open file descriptors has been reached.
   2796     ProcessFdQuotaExceeded,
   2797 
   2798     /// The system-wide limit on the total number of open files has been reached.
   2799     SystemFdQuotaExceeded,
   2800 
   2801     /// Not enough free memory.  This often means that the memory allocation  is  limited
   2802     /// by the socket buffer limits, not by the system memory.
   2803     SystemResources,
   2804 
   2805     ProtocolFailure,
   2806 
   2807     /// Firewall rules forbid connection.
   2808     BlockedByFirewall,
   2809 
   2810     /// This error occurs when no global event loop is configured,
   2811     /// and accepting from the socket would block.
   2812     WouldBlock,
   2813 
   2814     /// Permission to create a socket of the specified type and/or
   2815     /// protocol is denied.
   2816     PermissionDenied,
   2817 } || UnexpectedError;
   2818 
   2819 /// Accept a connection on a socket.
   2820 /// If the application has a global event loop enabled, EAGAIN is handled
   2821 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
   2822 pub fn accept(
   2823     /// This argument is a socket that has been created with `socket`, bound to a local address
   2824     /// with `bind`, and is listening for connections after a `listen`.
   2825     sockfd: fd_t,
   2826     /// This argument is a pointer to a sockaddr structure.  This structure is filled in with  the
   2827     /// address  of  the  peer  socket, as known to the communications layer.  The exact format of the
   2828     /// address returned addr is determined by the socket's address  family  (see  `socket`  and  the
   2829     /// respective  protocol  man  pages).
   2830     addr: *sockaddr,
   2831     /// This argument is a value-result argument: the caller must initialize it to contain  the
   2832     /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
   2833     /// of the peer address.
   2834     ///
   2835     /// The returned address is truncated if the buffer provided is too small; in this  case,  `addr_size`
   2836     /// will return a value greater than was supplied to the call.
   2837     addr_size: *socklen_t,
   2838     /// The following values can be bitwise ORed in flags to obtain different behavior:
   2839     /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`)
   2840     ///   referred  to by the new file descriptor.  Using this flag saves extra calls to `fcntl` to achieve
   2841     ///   the same result.
   2842     /// * `SOCK_CLOEXEC`  - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor.   See  the
   2843     ///   description  of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
   2844     flags: u32,
   2845 ) AcceptError!fd_t {
   2846     const have_accept4 = comptime !std.Target.current.isDarwin();
   2847     assert(0 == (flags & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC))); // Unsupported flag(s)
   2848 
   2849     while (true) {
   2850         const rc = if (have_accept4)
   2851             system.accept4(sockfd, addr, addr_size, flags)
   2852         else
   2853             system.accept(sockfd, addr, addr_size);
   2854 
   2855         switch (errno(rc)) {
   2856             0 => {
   2857                 const fd = @intCast(fd_t, rc);
   2858                 if (!have_accept4) {
   2859                     try setSockFlags(fd, flags);
   2860                 }
   2861                 return fd;
   2862             },
   2863             EINTR => continue,
   2864             EAGAIN => if (std.event.Loop.instance) |loop| {
   2865                 loop.waitUntilFdReadable(sockfd);
   2866                 continue;
   2867             } else {
   2868                 return error.WouldBlock;
   2869             },
   2870             EBADF => unreachable, // always a race condition
   2871             ECONNABORTED => return error.ConnectionAborted,
   2872             EFAULT => unreachable,
   2873             EINVAL => unreachable,
   2874             ENOTSOCK => unreachable,
   2875             EMFILE => return error.ProcessFdQuotaExceeded,
   2876             ENFILE => return error.SystemFdQuotaExceeded,
   2877             ENOBUFS => return error.SystemResources,
   2878             ENOMEM => return error.SystemResources,
   2879             EOPNOTSUPP => unreachable,
   2880             EPROTO => return error.ProtocolFailure,
   2881             EPERM => return error.BlockedByFirewall,
   2882             else => |err| return unexpectedErrno(err),
   2883         }
   2884     }
   2885 }
   2886 
   2887 pub const EpollCreateError = error{
   2888     /// The  per-user   limit   on   the   number   of   epoll   instances   imposed   by
   2889     /// /proc/sys/fs/epoll/max_user_instances  was encountered.  See epoll(7) for further
   2890     /// details.
   2891     /// Or, The per-process limit on the number of open file descriptors has been reached.
   2892     ProcessFdQuotaExceeded,
   2893 
   2894     /// The system-wide limit on the total number of open files has been reached.
   2895     SystemFdQuotaExceeded,
   2896 
   2897     /// There was insufficient memory to create the kernel object.
   2898     SystemResources,
   2899 } || UnexpectedError;
   2900 
   2901 pub fn epoll_create1(flags: u32) EpollCreateError!i32 {
   2902     const rc = system.epoll_create1(flags);
   2903     switch (errno(rc)) {
   2904         0 => return @intCast(i32, rc),
   2905         else => |err| return unexpectedErrno(err),
   2906 
   2907         EINVAL => unreachable,
   2908         EMFILE => return error.ProcessFdQuotaExceeded,
   2909         ENFILE => return error.SystemFdQuotaExceeded,
   2910         ENOMEM => return error.SystemResources,
   2911     }
   2912 }
   2913 
   2914 pub const EpollCtlError = error{
   2915     /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is  already  registered
   2916     /// with this epoll instance.
   2917     FileDescriptorAlreadyPresentInSet,
   2918 
   2919     /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
   2920     /// circular loop of epoll instances monitoring one another.
   2921     OperationCausesCircularLoop,
   2922 
   2923     /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with  this  epoll
   2924     /// instance.
   2925     FileDescriptorNotRegistered,
   2926 
   2927     /// There was insufficient memory to handle the requested op control operation.
   2928     SystemResources,
   2929 
   2930     /// The  limit  imposed  by /proc/sys/fs/epoll/max_user_watches was encountered while
   2931     /// trying to register (EPOLL_CTL_ADD) a new file descriptor on  an  epoll  instance.
   2932     /// See epoll(7) for further details.
   2933     UserResourceLimitReached,
   2934 
   2935     /// The target file fd does not support epoll.  This error can occur if fd refers to,
   2936     /// for example, a regular file or a directory.
   2937     FileDescriptorIncompatibleWithEpoll,
   2938 } || UnexpectedError;
   2939 
   2940 pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*epoll_event) EpollCtlError!void {
   2941     const rc = system.epoll_ctl(epfd, op, fd, event);
   2942     switch (errno(rc)) {
   2943         0 => return,
   2944         else => |err| return unexpectedErrno(err),
   2945 
   2946         EBADF => unreachable, // always a race condition if this happens
   2947         EEXIST => return error.FileDescriptorAlreadyPresentInSet,
   2948         EINVAL => unreachable,
   2949         ELOOP => return error.OperationCausesCircularLoop,
   2950         ENOENT => return error.FileDescriptorNotRegistered,
   2951         ENOMEM => return error.SystemResources,
   2952         ENOSPC => return error.UserResourceLimitReached,
   2953         EPERM => return error.FileDescriptorIncompatibleWithEpoll,
   2954     }
   2955 }
   2956 
   2957 /// Waits for an I/O event on an epoll file descriptor.
   2958 /// Returns the number of file descriptors ready for the requested I/O,
   2959 /// or zero if no file descriptor became ready during the requested timeout milliseconds.
   2960 pub fn epoll_wait(epfd: i32, events: []epoll_event, timeout: i32) usize {
   2961     while (true) {
   2962         // TODO get rid of the @intCast
   2963         const rc = system.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout);
   2964         switch (errno(rc)) {
   2965             0 => return @intCast(usize, rc),
   2966             EINTR => continue,
   2967             EBADF => unreachable,
   2968             EFAULT => unreachable,
   2969             EINVAL => unreachable,
   2970             else => unreachable,
   2971         }
   2972     }
   2973 }
   2974 
   2975 pub const EventFdError = error{
   2976     SystemResources,
   2977     ProcessFdQuotaExceeded,
   2978     SystemFdQuotaExceeded,
   2979 } || UnexpectedError;
   2980 
   2981 pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
   2982     const rc = system.eventfd(initval, flags);
   2983     switch (errno(rc)) {
   2984         0 => return @intCast(i32, rc),
   2985         else => |err| return unexpectedErrno(err),
   2986 
   2987         EINVAL => unreachable, // invalid parameters
   2988         EMFILE => return error.ProcessFdQuotaExceeded,
   2989         ENFILE => return error.SystemFdQuotaExceeded,
   2990         ENODEV => return error.SystemResources,
   2991         ENOMEM => return error.SystemResources,
   2992     }
   2993 }
   2994 
   2995 pub const GetSockNameError = error{
   2996     /// Insufficient resources were available in the system to perform the operation.
   2997     SystemResources,
   2998 } || UnexpectedError;
   2999 
   3000 pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
   3001     switch (errno(system.getsockname(sockfd, addr, addrlen))) {
   3002         0 => return,
   3003         else => |err| return unexpectedErrno(err),
   3004 
   3005         EBADF => unreachable, // always a race condition
   3006         EFAULT => unreachable,
   3007         EINVAL => unreachable, // invalid parameters
   3008         ENOTSOCK => unreachable,
   3009         ENOBUFS => return error.SystemResources,
   3010     }
   3011 }
   3012 
   3013 pub const ConnectError = error{
   3014     /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on  the  socket
   3015     /// file,  or  search  permission  is  denied  for  one of the directories in the path prefix.
   3016     /// or
   3017     /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled  or
   3018     /// the connection request failed because of a local firewall rule.
   3019     PermissionDenied,
   3020 
   3021     /// Local address is already in use.
   3022     AddressInUse,
   3023 
   3024     /// (Internet  domain  sockets)  The  socket  referred  to  by sockfd had not previously been bound to an
   3025     /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
   3026     /// in    the    ephemeral    port    range    are   currently   in   use.    See   the   discussion   of
   3027     /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
   3028     AddressNotAvailable,
   3029 
   3030     /// The passed address didn't have the correct address family in its sa_family field.
   3031     AddressFamilyNotSupported,
   3032 
   3033     /// Insufficient entries in the routing cache.
   3034     SystemResources,
   3035 
   3036     /// A connect() on a stream socket found no one listening on the remote address.
   3037     ConnectionRefused,
   3038 
   3039     /// Network is unreachable.
   3040     NetworkUnreachable,
   3041 
   3042     /// Timeout  while  attempting  connection.   The server may be too busy to accept new connections.  Note
   3043     /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
   3044     ConnectionTimedOut,
   3045 
   3046     /// This error occurs when no global event loop is configured,
   3047     /// and connecting to the socket would block.
   3048     WouldBlock,
   3049 
   3050     /// The given path for the unix socket does not exist.
   3051     FileNotFound,
   3052 } || UnexpectedError;
   3053 
   3054 /// Initiate a connection on a socket.
   3055 pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
   3056     if (builtin.os.tag == .windows) {
   3057         const rc = windows.ws2_32.connect(sockfd, sock_addr, len);
   3058         if (rc == 0) return;
   3059         switch (windows.ws2_32.WSAGetLastError()) {
   3060             .WSAEADDRINUSE => return error.AddressInUse,
   3061             .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
   3062             .WSAECONNREFUSED => return error.ConnectionRefused,
   3063             .WSAETIMEDOUT => return error.ConnectionTimedOut,
   3064             .WSAEHOSTUNREACH // TODO: should we return NetworkUnreachable in this case as well?
   3065                 , .WSAENETUNREACH => return error.NetworkUnreachable,
   3066             .WSAEFAULT => unreachable,
   3067             .WSAEINVAL => unreachable,
   3068             .WSAEISCONN => unreachable,
   3069             .WSAENOTSOCK => unreachable,
   3070             .WSAEWOULDBLOCK => unreachable,
   3071             .WSAEACCES => unreachable,
   3072             .WSAENOBUFS => return error.SystemResources,
   3073             .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
   3074             else => |err| return windows.unexpectedWSAError(err),
   3075         }
   3076         return;
   3077     }
   3078 
   3079     while (true) {
   3080         switch (errno(system.connect(sockfd, sock_addr, len))) {
   3081             0 => return,
   3082             EACCES => return error.PermissionDenied,
   3083             EPERM => return error.PermissionDenied,
   3084             EADDRINUSE => return error.AddressInUse,
   3085             EADDRNOTAVAIL => return error.AddressNotAvailable,
   3086             EAFNOSUPPORT => return error.AddressFamilyNotSupported,
   3087             EAGAIN, EINPROGRESS => {
   3088                 const loop = std.event.Loop.instance orelse return error.WouldBlock;
   3089                 loop.waitUntilFdWritable(sockfd);
   3090                 return getsockoptError(sockfd);
   3091             },
   3092             EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
   3093             EBADF => unreachable, // sockfd is not a valid open file descriptor.
   3094             ECONNREFUSED => return error.ConnectionRefused,
   3095             EFAULT => unreachable, // The socket structure address is outside the user's address space.
   3096             EINTR => continue,
   3097             EISCONN => unreachable, // The socket is already connected.
   3098             ENETUNREACH => return error.NetworkUnreachable,
   3099             ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   3100             EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
   3101             ETIMEDOUT => return error.ConnectionTimedOut,
   3102             ENOENT => return error.FileNotFound, // Returned when socket is AF_UNIX and the given path does not exist.
   3103             else => |err| return unexpectedErrno(err),
   3104         }
   3105     }
   3106 }
   3107 
   3108 pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
   3109     var err_code: u32 = undefined;
   3110     var size: u32 = @sizeOf(u32);
   3111     const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size);
   3112     assert(size == 4);
   3113     switch (errno(rc)) {
   3114         0 => switch (err_code) {
   3115             0 => return,
   3116             EACCES => return error.PermissionDenied,
   3117             EPERM => return error.PermissionDenied,
   3118             EADDRINUSE => return error.AddressInUse,
   3119             EADDRNOTAVAIL => return error.AddressNotAvailable,
   3120             EAFNOSUPPORT => return error.AddressFamilyNotSupported,
   3121             EAGAIN => return error.SystemResources,
   3122             EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
   3123             EBADF => unreachable, // sockfd is not a valid open file descriptor.
   3124             ECONNREFUSED => return error.ConnectionRefused,
   3125             EFAULT => unreachable, // The socket structure address is outside the user's address space.
   3126             EISCONN => unreachable, // The socket is already connected.
   3127             ENETUNREACH => return error.NetworkUnreachable,
   3128             ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   3129             EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
   3130             ETIMEDOUT => return error.ConnectionTimedOut,
   3131             else => |err| return unexpectedErrno(err),
   3132         },
   3133         EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
   3134         EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
   3135         EINVAL => unreachable,
   3136         ENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
   3137         ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   3138         else => |err| return unexpectedErrno(err),
   3139     }
   3140 }
   3141 
   3142 pub fn waitpid(pid: i32, flags: u32) u32 {
   3143     // TODO allow implicit pointer cast from *u32 to *c_uint ?
   3144     const Status = if (builtin.link_libc) c_uint else u32;
   3145     var status: Status = undefined;
   3146     while (true) {
   3147         switch (errno(system.waitpid(pid, &status, flags))) {
   3148             0 => return @bitCast(u32, status),
   3149             EINTR => continue,
   3150             ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
   3151             EINVAL => unreachable, // The options argument was invalid
   3152             else => unreachable,
   3153         }
   3154     }
   3155 }
   3156 
   3157 pub const FStatError = error{
   3158     SystemResources,
   3159 
   3160     /// In WASI, this error may occur when the file descriptor does
   3161     /// not hold the required rights to get its filestat information.
   3162     AccessDenied,
   3163 } || UnexpectedError;
   3164 
   3165 /// Return information about a file descriptor.
   3166 pub fn fstat(fd: fd_t) FStatError!Stat {
   3167     if (builtin.os.tag == .wasi) {
   3168         var stat: wasi.filestat_t = undefined;
   3169         switch (wasi.fd_filestat_get(fd, &stat)) {
   3170             wasi.ESUCCESS => return Stat.fromFilestat(stat),
   3171             wasi.EINVAL => unreachable,
   3172             wasi.EBADF => unreachable, // Always a race condition.
   3173             wasi.ENOMEM => return error.SystemResources,
   3174             wasi.EACCES => return error.AccessDenied,
   3175             wasi.ENOTCAPABLE => return error.AccessDenied,
   3176             else => |err| return unexpectedErrno(err),
   3177         }
   3178     }
   3179     if (builtin.os.tag == .windows) {
   3180         @compileError("fstat is not yet implemented on Windows");
   3181     }
   3182 
   3183     var stat: Stat = undefined;
   3184     switch (errno(system.fstat(fd, &stat))) {
   3185         0 => return stat,
   3186         EINVAL => unreachable,
   3187         EBADF => unreachable, // Always a race condition.
   3188         ENOMEM => return error.SystemResources,
   3189         EACCES => return error.AccessDenied,
   3190         else => |err| return unexpectedErrno(err),
   3191     }
   3192 }
   3193 
   3194 pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound };
   3195 
   3196 /// Similar to `fstat`, but returns stat of a resource pointed to by `pathname`
   3197 /// which is relative to `dirfd` handle.
   3198 /// See also `fstatatZ` and `fstatatWasi`.
   3199 pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
   3200     if (builtin.os.tag == .wasi) {
   3201         return fstatatWasi(dirfd, pathname, flags);
   3202     } else if (builtin.os.tag == .windows) {
   3203         @compileError("fstatat is not yet implemented on Windows");
   3204     } else {
   3205         const pathname_c = try toPosixPath(pathname);
   3206         return fstatatZ(dirfd, &pathname_c, flags);
   3207     }
   3208 }
   3209 
   3210 pub const fstatatC = @compileError("deprecated: renamed to fstatatZ");
   3211 
   3212 /// WASI-only. Same as `fstatat` but targeting WASI.
   3213 /// See also `fstatat`.
   3214 pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
   3215     var stat: wasi.filestat_t = undefined;
   3216     switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
   3217         wasi.ESUCCESS => return Stat.fromFilestat(stat),
   3218         wasi.EINVAL => unreachable,
   3219         wasi.EBADF => unreachable, // Always a race condition.
   3220         wasi.ENOMEM => return error.SystemResources,
   3221         wasi.EACCES => return error.AccessDenied,
   3222         wasi.EFAULT => unreachable,
   3223         wasi.ENAMETOOLONG => return error.NameTooLong,
   3224         wasi.ENOENT => return error.FileNotFound,
   3225         wasi.ENOTDIR => return error.FileNotFound,
   3226         wasi.ENOTCAPABLE => return error.AccessDenied,
   3227         else => |err| return unexpectedErrno(err),
   3228     }
   3229 }
   3230 
   3231 /// Same as `fstatat` but `pathname` is null-terminated.
   3232 /// See also `fstatat`.
   3233 pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
   3234     var stat: Stat = undefined;
   3235     switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) {
   3236         0 => return stat,
   3237         EINVAL => unreachable,
   3238         EBADF => unreachable, // Always a race condition.
   3239         ENOMEM => return error.SystemResources,
   3240         EACCES => return error.AccessDenied,
   3241         EFAULT => unreachable,
   3242         ENAMETOOLONG => return error.NameTooLong,
   3243         ENOENT => return error.FileNotFound,
   3244         ENOTDIR => return error.FileNotFound,
   3245         else => |err| return unexpectedErrno(err),
   3246     }
   3247 }
   3248 
   3249 pub const KQueueError = error{
   3250     /// The per-process limit on the number of open file descriptors has been reached.
   3251     ProcessFdQuotaExceeded,
   3252 
   3253     /// The system-wide limit on the total number of open files has been reached.
   3254     SystemFdQuotaExceeded,
   3255 } || UnexpectedError;
   3256 
   3257 pub fn kqueue() KQueueError!i32 {
   3258     const rc = system.kqueue();
   3259     switch (errno(rc)) {
   3260         0 => return @intCast(i32, rc),
   3261         EMFILE => return error.ProcessFdQuotaExceeded,
   3262         ENFILE => return error.SystemFdQuotaExceeded,
   3263         else => |err| return unexpectedErrno(err),
   3264     }
   3265 }
   3266 
   3267 pub const KEventError = error{
   3268     /// The process does not have permission to register a filter.
   3269     AccessDenied,
   3270 
   3271     /// The event could not be found to be modified or deleted.
   3272     EventNotFound,
   3273 
   3274     /// No memory was available to register the event.
   3275     SystemResources,
   3276 
   3277     /// The specified process to attach to does not exist.
   3278     ProcessNotFound,
   3279 
   3280     /// changelist or eventlist had too many items on it.
   3281     /// TODO remove this possibility
   3282     Overflow,
   3283 };
   3284 
   3285 pub fn kevent(
   3286     kq: i32,
   3287     changelist: []const Kevent,
   3288     eventlist: []Kevent,
   3289     timeout: ?*const timespec,
   3290 ) KEventError!usize {
   3291     while (true) {
   3292         const rc = system.kevent(
   3293             kq,
   3294             changelist.ptr,
   3295             try math.cast(c_int, changelist.len),
   3296             eventlist.ptr,
   3297             try math.cast(c_int, eventlist.len),
   3298             timeout,
   3299         );
   3300         switch (errno(rc)) {
   3301             0 => return @intCast(usize, rc),
   3302             EACCES => return error.AccessDenied,
   3303             EFAULT => unreachable,
   3304             EBADF => unreachable, // Always a race condition.
   3305             EINTR => continue,
   3306             EINVAL => unreachable,
   3307             ENOENT => return error.EventNotFound,
   3308             ENOMEM => return error.SystemResources,
   3309             ESRCH => return error.ProcessNotFound,
   3310             else => unreachable,
   3311         }
   3312     }
   3313 }
   3314 
   3315 pub const INotifyInitError = error{
   3316     ProcessFdQuotaExceeded,
   3317     SystemFdQuotaExceeded,
   3318     SystemResources,
   3319 } || UnexpectedError;
   3320 
   3321 /// initialize an inotify instance
   3322 pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
   3323     const rc = system.inotify_init1(flags);
   3324     switch (errno(rc)) {
   3325         0 => return @intCast(i32, rc),
   3326         EINVAL => unreachable,
   3327         EMFILE => return error.ProcessFdQuotaExceeded,
   3328         ENFILE => return error.SystemFdQuotaExceeded,
   3329         ENOMEM => return error.SystemResources,
   3330         else => |err| return unexpectedErrno(err),
   3331     }
   3332 }
   3333 
   3334 pub const INotifyAddWatchError = error{
   3335     AccessDenied,
   3336     NameTooLong,
   3337     FileNotFound,
   3338     SystemResources,
   3339     UserResourceLimitReached,
   3340 } || UnexpectedError;
   3341 
   3342 /// add a watch to an initialized inotify instance
   3343 pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
   3344     const pathname_c = try toPosixPath(pathname);
   3345     return inotify_add_watchZ(inotify_fd, &pathname_c, mask);
   3346 }
   3347 
   3348 pub const inotify_add_watchC = @compileError("deprecated: renamed to inotify_add_watchZ");
   3349 
   3350 /// Same as `inotify_add_watch` except pathname is null-terminated.
   3351 pub fn inotify_add_watchZ(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) INotifyAddWatchError!i32 {
   3352     const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
   3353     switch (errno(rc)) {
   3354         0 => return @intCast(i32, rc),
   3355         EACCES => return error.AccessDenied,
   3356         EBADF => unreachable,
   3357         EFAULT => unreachable,
   3358         EINVAL => unreachable,
   3359         ENAMETOOLONG => return error.NameTooLong,
   3360         ENOENT => return error.FileNotFound,
   3361         ENOMEM => return error.SystemResources,
   3362         ENOSPC => return error.UserResourceLimitReached,
   3363         else => |err| return unexpectedErrno(err),
   3364     }
   3365 }
   3366 
   3367 /// remove an existing watch from an inotify instance
   3368 pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
   3369     switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
   3370         0 => return,
   3371         EBADF => unreachable,
   3372         EINVAL => unreachable,
   3373         else => unreachable,
   3374     }
   3375 }
   3376 
   3377 pub const MProtectError = error{
   3378     /// The memory cannot be given the specified access.  This can happen, for example, if you
   3379     /// mmap(2)  a  file  to  which  you have read-only access, then ask mprotect() to mark it
   3380     /// PROT_WRITE.
   3381     AccessDenied,
   3382 
   3383     /// Changing  the  protection  of a memory region would result in the total number of map‐
   3384     /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the
   3385     /// allowed maximum.  (For example, making the protection of a range PROT_READ in the mid‐
   3386     /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐
   3387     /// pings: two read/write mappings at each end and a read-only mapping in the middle.)
   3388     OutOfMemory,
   3389 } || UnexpectedError;
   3390 
   3391 /// `memory.len` must be page-aligned.
   3392 pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void {
   3393     assert(mem.isAligned(memory.len, mem.page_size));
   3394     switch (errno(system.mprotect(memory.ptr, memory.len, protection))) {
   3395         0 => return,
   3396         EINVAL => unreachable,
   3397         EACCES => return error.AccessDenied,
   3398         ENOMEM => return error.OutOfMemory,
   3399         else => |err| return unexpectedErrno(err),
   3400     }
   3401 }
   3402 
   3403 pub const ForkError = error{SystemResources} || UnexpectedError;
   3404 
   3405 pub fn fork() ForkError!pid_t {
   3406     const rc = system.fork();
   3407     switch (errno(rc)) {
   3408         0 => return @intCast(pid_t, rc),
   3409         EAGAIN => return error.SystemResources,
   3410         ENOMEM => return error.SystemResources,
   3411         else => |err| return unexpectedErrno(err),
   3412     }
   3413 }
   3414 
   3415 pub const MMapError = error{
   3416     /// The underlying filesystem of the specified file does not support memory mapping.
   3417     MemoryMappingNotSupported,
   3418 
   3419     /// A file descriptor refers to a non-regular file. Or a file mapping was requested,
   3420     /// but the file descriptor is not open for reading. Or `MAP_SHARED` was requested
   3421     /// and `PROT_WRITE` is set, but the file descriptor is not open in `O_RDWR` mode.
   3422     /// Or `PROT_WRITE` is set, but the file is append-only.
   3423     AccessDenied,
   3424 
   3425     /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
   3426     /// a filesystem that was mounted no-exec.
   3427     PermissionDenied,
   3428     LockedMemoryLimitExceeded,
   3429     OutOfMemory,
   3430 } || UnexpectedError;
   3431 
   3432 /// Map files or devices into memory.
   3433 /// `length` does not need to be aligned.
   3434 /// Use of a mapped region can result in these signals:
   3435 /// * SIGSEGV - Attempted write into a region mapped as read-only.
   3436 /// * SIGBUS - Attempted  access to a portion of the buffer that does not correspond to the file
   3437 pub fn mmap(
   3438     ptr: ?[*]align(mem.page_size) u8,
   3439     length: usize,
   3440     prot: u32,
   3441     flags: u32,
   3442     fd: fd_t,
   3443     offset: u64,
   3444 ) MMapError![]align(mem.page_size) u8 {
   3445     const err = if (builtin.link_libc) blk: {
   3446         const rc = std.c.mmap(ptr, length, prot, flags, fd, offset);
   3447         if (rc != std.c.MAP_FAILED) return @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, rc))[0..length];
   3448         break :blk @intCast(usize, system._errno().*);
   3449     } else blk: {
   3450         const rc = system.mmap(ptr, length, prot, flags, fd, offset);
   3451         const err = errno(rc);
   3452         if (err == 0) return @intToPtr([*]align(mem.page_size) u8, rc)[0..length];
   3453         break :blk err;
   3454     };
   3455     switch (err) {
   3456         ETXTBSY => return error.AccessDenied,
   3457         EACCES => return error.AccessDenied,
   3458         EPERM => return error.PermissionDenied,
   3459         EAGAIN => return error.LockedMemoryLimitExceeded,
   3460         EBADF => unreachable, // Always a race condition.
   3461         EOVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
   3462         ENODEV => return error.MemoryMappingNotSupported,
   3463         EINVAL => unreachable, // Invalid parameters to mmap()
   3464         ENOMEM => return error.OutOfMemory,
   3465         else => return unexpectedErrno(err),
   3466     }
   3467 }
   3468 
   3469 /// Deletes the mappings for the specified address range, causing
   3470 /// further references to addresses within the range to generate invalid memory references.
   3471 /// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
   3472 /// Zig's munmap function does not, for two reasons:
   3473 /// * It violates the Zig principle that resource deallocation must succeed.
   3474 /// * The Windows function, VirtualFree, has this restriction.
   3475 pub fn munmap(memory: []align(mem.page_size) u8) void {
   3476     switch (errno(system.munmap(memory.ptr, memory.len))) {
   3477         0 => return,
   3478         EINVAL => unreachable, // Invalid parameters.
   3479         ENOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
   3480         else => unreachable,
   3481     }
   3482 }
   3483 
   3484 pub const AccessError = error{
   3485     PermissionDenied,
   3486     FileNotFound,
   3487     NameTooLong,
   3488     InputOutput,
   3489     SystemResources,
   3490     BadPathName,
   3491     FileBusy,
   3492     SymLinkLoop,
   3493     ReadOnlyFileSystem,
   3494 
   3495     /// On Windows, file paths must be valid Unicode.
   3496     InvalidUtf8,
   3497 } || UnexpectedError;
   3498 
   3499 /// check user's permissions for a file
   3500 /// TODO currently this assumes `mode` is `F_OK` on Windows.
   3501 pub fn access(path: []const u8, mode: u32) AccessError!void {
   3502     if (builtin.os.tag == .windows) {
   3503         const path_w = try windows.sliceToPrefixedFileW(path);
   3504         _ = try windows.GetFileAttributesW(path_w.span().ptr);
   3505         return;
   3506     }
   3507     const path_c = try toPosixPath(path);
   3508     return accessZ(&path_c, mode);
   3509 }
   3510 
   3511 pub const accessC = @compileError("Deprecated in favor of `accessZ`");
   3512 
   3513 /// Same as `access` except `path` is null-terminated.
   3514 pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
   3515     if (builtin.os.tag == .windows) {
   3516         const path_w = try windows.cStrToPrefixedFileW(path);
   3517         _ = try windows.GetFileAttributesW(path_w.span().ptr);
   3518         return;
   3519     }
   3520     switch (errno(system.access(path, mode))) {
   3521         0 => return,
   3522         EACCES => return error.PermissionDenied,
   3523         EROFS => return error.ReadOnlyFileSystem,
   3524         ELOOP => return error.SymLinkLoop,
   3525         ETXTBSY => return error.FileBusy,
   3526         ENOTDIR => return error.FileNotFound,
   3527         ENOENT => return error.FileNotFound,
   3528         ENAMETOOLONG => return error.NameTooLong,
   3529         EINVAL => unreachable,
   3530         EFAULT => unreachable,
   3531         EIO => return error.InputOutput,
   3532         ENOMEM => return error.SystemResources,
   3533         else => |err| return unexpectedErrno(err),
   3534     }
   3535 }
   3536 
   3537 /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
   3538 /// Otherwise use `access` or `accessC`.
   3539 /// TODO currently this ignores `mode`.
   3540 pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!void {
   3541     const ret = try windows.GetFileAttributesW(path);
   3542     if (ret != windows.INVALID_FILE_ATTRIBUTES) {
   3543         return;
   3544     }
   3545     switch (windows.kernel32.GetLastError()) {
   3546         .FILE_NOT_FOUND => return error.FileNotFound,
   3547         .PATH_NOT_FOUND => return error.FileNotFound,
   3548         .ACCESS_DENIED => return error.PermissionDenied,
   3549         else => |err| return windows.unexpectedError(err),
   3550     }
   3551 }
   3552 
   3553 /// Check user's permissions for a file, based on an open directory handle.
   3554 /// TODO currently this ignores `mode` and `flags` on Windows.
   3555 pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
   3556     if (builtin.os.tag == .windows) {
   3557         const path_w = try windows.sliceToPrefixedFileW(path);
   3558         return faccessatW(dirfd, path_w.span().ptr, mode, flags);
   3559     }
   3560     const path_c = try toPosixPath(path);
   3561     return faccessatZ(dirfd, &path_c, mode, flags);
   3562 }
   3563 
   3564 /// Same as `faccessat` except the path parameter is null-terminated.
   3565 pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
   3566     if (builtin.os.tag == .windows) {
   3567         const path_w = try windows.cStrToPrefixedFileW(path);
   3568         return faccessatW(dirfd, path_w.span().ptr, mode, flags);
   3569     }
   3570     switch (errno(system.faccessat(dirfd, path, mode, flags))) {
   3571         0 => return,
   3572         EACCES => return error.PermissionDenied,
   3573         EROFS => return error.ReadOnlyFileSystem,
   3574         ELOOP => return error.SymLinkLoop,
   3575         ETXTBSY => return error.FileBusy,
   3576         ENOTDIR => return error.FileNotFound,
   3577         ENOENT => return error.FileNotFound,
   3578         ENAMETOOLONG => return error.NameTooLong,
   3579         EINVAL => unreachable,
   3580         EFAULT => unreachable,
   3581         EIO => return error.InputOutput,
   3582         ENOMEM => return error.SystemResources,
   3583         else => |err| return unexpectedErrno(err),
   3584     }
   3585 }
   3586 
   3587 /// Same as `faccessat` except asserts the target is Windows and the path parameter
   3588 /// is NtDll-prefixed, null-terminated, WTF-16 encoded.
   3589 /// TODO currently this ignores `mode` and `flags`
   3590 pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32) AccessError!void {
   3591     if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
   3592         return;
   3593     }
   3594     if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
   3595         return;
   3596     }
   3597 
   3598     const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
   3599         error.Overflow => return error.NameTooLong,
   3600     };
   3601     var nt_name = windows.UNICODE_STRING{
   3602         .Length = path_len_bytes,
   3603         .MaximumLength = path_len_bytes,
   3604         .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
   3605     };
   3606     var attr = windows.OBJECT_ATTRIBUTES{
   3607         .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
   3608         .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
   3609         .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
   3610         .ObjectName = &nt_name,
   3611         .SecurityDescriptor = null,
   3612         .SecurityQualityOfService = null,
   3613     };
   3614     var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
   3615     switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
   3616         .SUCCESS => return,
   3617         .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   3618         .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
   3619         .INVALID_PARAMETER => unreachable,
   3620         .ACCESS_DENIED => return error.PermissionDenied,
   3621         .OBJECT_PATH_SYNTAX_BAD => unreachable,
   3622         else => |rc| return windows.unexpectedStatus(rc),
   3623     }
   3624 }
   3625 
   3626 pub const PipeError = error{
   3627     SystemFdQuotaExceeded,
   3628     ProcessFdQuotaExceeded,
   3629 } || UnexpectedError;
   3630 
   3631 /// Creates a unidirectional data channel that can be used for interprocess communication.
   3632 pub fn pipe() PipeError![2]fd_t {
   3633     var fds: [2]fd_t = undefined;
   3634     switch (errno(system.pipe(&fds))) {
   3635         0 => return fds,
   3636         EINVAL => unreachable, // Invalid parameters to pipe()
   3637         EFAULT => unreachable, // Invalid fds pointer
   3638         ENFILE => return error.SystemFdQuotaExceeded,
   3639         EMFILE => return error.ProcessFdQuotaExceeded,
   3640         else => |err| return unexpectedErrno(err),
   3641     }
   3642 }
   3643 
   3644 pub fn pipe2(flags: u32) PipeError![2]fd_t {
   3645     if (comptime std.Target.current.isDarwin()) {
   3646         var fds: [2]fd_t = try pipe();
   3647         if (flags == 0) return fds;
   3648         errdefer {
   3649             close(fds[0]);
   3650             close(fds[1]);
   3651         }
   3652         for (fds) |fd| switch (errno(system.fcntl(fd, F_SETFL, flags))) {
   3653             0 => {},
   3654             EINVAL => unreachable, // Invalid flags
   3655             EBADF => unreachable, // Always a race condition
   3656             else => |err| return unexpectedErrno(err),
   3657         };
   3658         return fds;
   3659     }
   3660 
   3661     var fds: [2]fd_t = undefined;
   3662     switch (errno(system.pipe2(&fds, flags))) {
   3663         0 => return fds,
   3664         EINVAL => unreachable, // Invalid flags
   3665         EFAULT => unreachable, // Invalid fds pointer
   3666         ENFILE => return error.SystemFdQuotaExceeded,
   3667         EMFILE => return error.ProcessFdQuotaExceeded,
   3668         else => |err| return unexpectedErrno(err),
   3669     }
   3670 }
   3671 
   3672 pub const SysCtlError = error{
   3673     PermissionDenied,
   3674     SystemResources,
   3675     NameTooLong,
   3676     UnknownName,
   3677 } || UnexpectedError;
   3678 
   3679 pub fn sysctl(
   3680     name: []const c_int,
   3681     oldp: ?*c_void,
   3682     oldlenp: ?*usize,
   3683     newp: ?*c_void,
   3684     newlen: usize,
   3685 ) SysCtlError!void {
   3686     if (builtin.os.tag == .wasi) {
   3687         @panic("unsupported");
   3688     }
   3689 
   3690     const name_len = math.cast(c_uint, name.len) catch return error.NameTooLong;
   3691     switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {
   3692         0 => return,
   3693         EFAULT => unreachable,
   3694         EPERM => return error.PermissionDenied,
   3695         ENOMEM => return error.SystemResources,
   3696         ENOENT => return error.UnknownName,
   3697         else => |err| return unexpectedErrno(err),
   3698     }
   3699 }
   3700 
   3701 pub const sysctlbynameC = @compileError("deprecated: renamed to sysctlbynameZ");
   3702 
   3703 pub fn sysctlbynameZ(
   3704     name: [*:0]const u8,
   3705     oldp: ?*c_void,
   3706     oldlenp: ?*usize,
   3707     newp: ?*c_void,
   3708     newlen: usize,
   3709 ) SysCtlError!void {
   3710     if (builtin.os.tag == .wasi) {
   3711         @panic("unsupported");
   3712     }
   3713 
   3714     switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
   3715         0 => return,
   3716         EFAULT => unreachable,
   3717         EPERM => return error.PermissionDenied,
   3718         ENOMEM => return error.SystemResources,
   3719         ENOENT => return error.UnknownName,
   3720         else => |err| return unexpectedErrno(err),
   3721     }
   3722 }
   3723 
   3724 pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
   3725     switch (errno(system.gettimeofday(tv, tz))) {
   3726         0 => return,
   3727         EINVAL => unreachable,
   3728         else => unreachable,
   3729     }
   3730 }
   3731 
   3732 pub const SeekError = error{
   3733     Unseekable,
   3734 
   3735     /// In WASI, this error may occur when the file descriptor does
   3736     /// not hold the required rights to seek on it.
   3737     AccessDenied,
   3738 } || UnexpectedError;
   3739 
   3740 /// Repositions read/write file offset relative to the beginning.
   3741 pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
   3742     if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   3743         var result: u64 = undefined;
   3744         switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) {
   3745             0 => return,
   3746             EBADF => unreachable, // always a race condition
   3747             EINVAL => return error.Unseekable,
   3748             EOVERFLOW => return error.Unseekable,
   3749             ESPIPE => return error.Unseekable,
   3750             ENXIO => return error.Unseekable,
   3751             else => |err| return unexpectedErrno(err),
   3752         }
   3753     }
   3754     if (builtin.os.tag == .windows) {
   3755         return windows.SetFilePointerEx_BEGIN(fd, offset);
   3756     }
   3757     if (builtin.os.tag == .wasi) {
   3758         var new_offset: wasi.filesize_t = undefined;
   3759         switch (wasi.fd_seek(fd, @bitCast(wasi.filedelta_t, offset), wasi.WHENCE_SET, &new_offset)) {
   3760             wasi.ESUCCESS => return,
   3761             wasi.EBADF => unreachable, // always a race condition
   3762             wasi.EINVAL => return error.Unseekable,
   3763             wasi.EOVERFLOW => return error.Unseekable,
   3764             wasi.ESPIPE => return error.Unseekable,
   3765             wasi.ENXIO => return error.Unseekable,
   3766             wasi.ENOTCAPABLE => return error.AccessDenied,
   3767             else => |err| return unexpectedErrno(err),
   3768         }
   3769     }
   3770     const ipos = @bitCast(i64, offset); // the OS treats this as unsigned
   3771     switch (errno(system.lseek(fd, ipos, SEEK_SET))) {
   3772         0 => return,
   3773         EBADF => unreachable, // always a race condition
   3774         EINVAL => return error.Unseekable,
   3775         EOVERFLOW => return error.Unseekable,
   3776         ESPIPE => return error.Unseekable,
   3777         ENXIO => return error.Unseekable,
   3778         else => |err| return unexpectedErrno(err),
   3779     }
   3780 }
   3781 
   3782 /// Repositions read/write file offset relative to the current offset.
   3783 pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
   3784     if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   3785         var result: u64 = undefined;
   3786         switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) {
   3787             0 => return,
   3788             EBADF => unreachable, // always a race condition
   3789             EINVAL => return error.Unseekable,
   3790             EOVERFLOW => return error.Unseekable,
   3791             ESPIPE => return error.Unseekable,
   3792             ENXIO => return error.Unseekable,
   3793             else => |err| return unexpectedErrno(err),
   3794         }
   3795     }
   3796     if (builtin.os.tag == .windows) {
   3797         return windows.SetFilePointerEx_CURRENT(fd, offset);
   3798     }
   3799     if (builtin.os.tag == .wasi) {
   3800         var new_offset: wasi.filesize_t = undefined;
   3801         switch (wasi.fd_seek(fd, offset, wasi.WHENCE_CUR, &new_offset)) {
   3802             wasi.ESUCCESS => return,
   3803             wasi.EBADF => unreachable, // always a race condition
   3804             wasi.EINVAL => return error.Unseekable,
   3805             wasi.EOVERFLOW => return error.Unseekable,
   3806             wasi.ESPIPE => return error.Unseekable,
   3807             wasi.ENXIO => return error.Unseekable,
   3808             wasi.ENOTCAPABLE => return error.AccessDenied,
   3809             else => |err| return unexpectedErrno(err),
   3810         }
   3811     }
   3812     switch (errno(system.lseek(fd, offset, SEEK_CUR))) {
   3813         0 => return,
   3814         EBADF => unreachable, // always a race condition
   3815         EINVAL => return error.Unseekable,
   3816         EOVERFLOW => return error.Unseekable,
   3817         ESPIPE => return error.Unseekable,
   3818         ENXIO => return error.Unseekable,
   3819         else => |err| return unexpectedErrno(err),
   3820     }
   3821 }
   3822 
   3823 /// Repositions read/write file offset relative to the end.
   3824 pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
   3825     if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   3826         var result: u64 = undefined;
   3827         switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) {
   3828             0 => return,
   3829             EBADF => unreachable, // always a race condition
   3830             EINVAL => return error.Unseekable,
   3831             EOVERFLOW => return error.Unseekable,
   3832             ESPIPE => return error.Unseekable,
   3833             ENXIO => return error.Unseekable,
   3834             else => |err| return unexpectedErrno(err),
   3835         }
   3836     }
   3837     if (builtin.os.tag == .windows) {
   3838         return windows.SetFilePointerEx_END(fd, offset);
   3839     }
   3840     if (builtin.os.tag == .wasi) {
   3841         var new_offset: wasi.filesize_t = undefined;
   3842         switch (wasi.fd_seek(fd, offset, wasi.WHENCE_END, &new_offset)) {
   3843             wasi.ESUCCESS => return,
   3844             wasi.EBADF => unreachable, // always a race condition
   3845             wasi.EINVAL => return error.Unseekable,
   3846             wasi.EOVERFLOW => return error.Unseekable,
   3847             wasi.ESPIPE => return error.Unseekable,
   3848             wasi.ENXIO => return error.Unseekable,
   3849             wasi.ENOTCAPABLE => return error.AccessDenied,
   3850             else => |err| return unexpectedErrno(err),
   3851         }
   3852     }
   3853     switch (errno(system.lseek(fd, offset, SEEK_END))) {
   3854         0 => return,
   3855         EBADF => unreachable, // always a race condition
   3856         EINVAL => return error.Unseekable,
   3857         EOVERFLOW => return error.Unseekable,
   3858         ESPIPE => return error.Unseekable,
   3859         ENXIO => return error.Unseekable,
   3860         else => |err| return unexpectedErrno(err),
   3861     }
   3862 }
   3863 
   3864 /// Returns the read/write file offset relative to the beginning.
   3865 pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
   3866     if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   3867         var result: u64 = undefined;
   3868         switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) {
   3869             0 => return result,
   3870             EBADF => unreachable, // always a race condition
   3871             EINVAL => return error.Unseekable,
   3872             EOVERFLOW => return error.Unseekable,
   3873             ESPIPE => return error.Unseekable,
   3874             ENXIO => return error.Unseekable,
   3875             else => |err| return unexpectedErrno(err),
   3876         }
   3877     }
   3878     if (builtin.os.tag == .windows) {
   3879         return windows.SetFilePointerEx_CURRENT_get(fd);
   3880     }
   3881     if (builtin.os.tag == .wasi) {
   3882         var new_offset: wasi.filesize_t = undefined;
   3883         switch (wasi.fd_seek(fd, 0, wasi.WHENCE_CUR, &new_offset)) {
   3884             wasi.ESUCCESS => return new_offset,
   3885             wasi.EBADF => unreachable, // always a race condition
   3886             wasi.EINVAL => return error.Unseekable,
   3887             wasi.EOVERFLOW => return error.Unseekable,
   3888             wasi.ESPIPE => return error.Unseekable,
   3889             wasi.ENXIO => return error.Unseekable,
   3890             wasi.ENOTCAPABLE => return error.AccessDenied,
   3891             else => |err| return unexpectedErrno(err),
   3892         }
   3893     }
   3894     const rc = system.lseek(fd, 0, SEEK_CUR);
   3895     switch (errno(rc)) {
   3896         0 => return @bitCast(u64, rc),
   3897         EBADF => unreachable, // always a race condition
   3898         EINVAL => return error.Unseekable,
   3899         EOVERFLOW => return error.Unseekable,
   3900         ESPIPE => return error.Unseekable,
   3901         ENXIO => return error.Unseekable,
   3902         else => |err| return unexpectedErrno(err),
   3903     }
   3904 }
   3905 
   3906 pub const FcntlError = error{
   3907     PermissionDenied,
   3908     FileBusy,
   3909     ProcessFdQuotaExceeded,
   3910     Locked,
   3911 } || UnexpectedError;
   3912 
   3913 pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
   3914     while (true) {
   3915         const rc = system.fcntl(fd, cmd, arg);
   3916         switch (errno(rc)) {
   3917             0 => return @intCast(usize, rc),
   3918             EINTR => continue,
   3919             EACCES => return error.Locked,
   3920             EBADF => unreachable,
   3921             EBUSY => return error.FileBusy,
   3922             EINVAL => unreachable, // invalid parameters
   3923             EPERM => return error.PermissionDenied,
   3924             EMFILE => return error.ProcessFdQuotaExceeded,
   3925             ENOTDIR => unreachable, // invalid parameter
   3926             else => |err| return unexpectedErrno(err),
   3927         }
   3928     }
   3929 }
   3930 
   3931 fn setSockFlags(fd: fd_t, flags: u32) !void {
   3932     if ((flags & SOCK_CLOEXEC) != 0) {
   3933         var fd_flags = fcntl(fd, F_GETFD, 0) catch |err| switch (err) {
   3934             error.FileBusy => unreachable,
   3935             error.Locked => unreachable,
   3936             else => |e| return e,
   3937         };
   3938         fd_flags |= FD_CLOEXEC;
   3939         _ = fcntl(fd, F_SETFD, fd_flags) catch |err| switch (err) {
   3940             error.FileBusy => unreachable,
   3941             error.Locked => unreachable,
   3942             else => |e| return e,
   3943         };
   3944     }
   3945     if ((flags & SOCK_NONBLOCK) != 0) {
   3946         var fl_flags = fcntl(fd, F_GETFL, 0) catch |err| switch (err) {
   3947             error.FileBusy => unreachable,
   3948             error.Locked => unreachable,
   3949             else => |e| return e,
   3950         };
   3951         fl_flags |= O_NONBLOCK;
   3952         _ = fcntl(fd, F_SETFL, fl_flags) catch |err| switch (err) {
   3953             error.FileBusy => unreachable,
   3954             error.Locked => unreachable,
   3955             else => |e| return e,
   3956         };
   3957     }
   3958 }
   3959 
   3960 pub const FlockError = error{
   3961     WouldBlock,
   3962 
   3963     /// The kernel ran out of memory for allocating file locks
   3964     SystemResources,
   3965 } || UnexpectedError;
   3966 
   3967 pub fn flock(fd: fd_t, operation: i32) FlockError!void {
   3968     while (true) {
   3969         const rc = system.flock(fd, operation);
   3970         switch (errno(rc)) {
   3971             0 => return,
   3972             EBADF => unreachable,
   3973             EINTR => continue,
   3974             EINVAL => unreachable, // invalid parameters
   3975             ENOLCK => return error.SystemResources,
   3976             EWOULDBLOCK => return error.WouldBlock, // TODO: integrate with async instead of just returning an error
   3977             else => |err| return unexpectedErrno(err),
   3978         }
   3979     }
   3980 }
   3981 
   3982 pub const RealPathError = error{
   3983     FileNotFound,
   3984     AccessDenied,
   3985     NameTooLong,
   3986     NotSupported,
   3987     NotDir,
   3988     SymLinkLoop,
   3989     InputOutput,
   3990     FileTooBig,
   3991     IsDir,
   3992     ProcessFdQuotaExceeded,
   3993     SystemFdQuotaExceeded,
   3994     NoDevice,
   3995     SystemResources,
   3996     NoSpaceLeft,
   3997     FileSystem,
   3998     BadPathName,
   3999     DeviceBusy,
   4000 
   4001     SharingViolation,
   4002     PipeBusy,
   4003 
   4004     /// On Windows, file paths must be valid Unicode.
   4005     InvalidUtf8,
   4006 
   4007     PathAlreadyExists,
   4008 } || UnexpectedError;
   4009 
   4010 /// Return the canonicalized absolute pathname.
   4011 /// Expands all symbolic links and resolves references to `.`, `..`, and
   4012 /// extra `/` characters in `pathname`.
   4013 /// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
   4014 /// See also `realpathC` and `realpathW`.
   4015 pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   4016     if (builtin.os.tag == .windows) {
   4017         const pathname_w = try windows.sliceToPrefixedFileW(pathname);
   4018         return realpathW(pathname_w.span(), out_buffer);
   4019     }
   4020     if (builtin.os.tag == .wasi) {
   4021         @compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths");
   4022     }
   4023     const pathname_c = try toPosixPath(pathname);
   4024     return realpathZ(&pathname_c, out_buffer);
   4025 }
   4026 
   4027 pub const realpathC = @compileError("deprecated: renamed realpathZ");
   4028 
   4029 /// Same as `realpath` except `pathname` is null-terminated.
   4030 pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   4031     if (builtin.os.tag == .windows) {
   4032         const pathname_w = try windows.cStrToPrefixedFileW(pathname);
   4033         return realpathW(pathname_w.span(), out_buffer);
   4034     }
   4035     if (!builtin.link_libc) {
   4036         const flags = if (builtin.os.tag == .linux) O_PATH | O_NONBLOCK | O_CLOEXEC else O_NONBLOCK | O_CLOEXEC;
   4037         const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
   4038             error.FileLocksNotSupported => unreachable,
   4039             else => |e| return e,
   4040         };
   4041         defer close(fd);
   4042 
   4043         return getFdPath(fd, out_buffer);
   4044     }
   4045     const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) {
   4046         EINVAL => unreachable,
   4047         EBADF => unreachable,
   4048         EFAULT => unreachable,
   4049         EACCES => return error.AccessDenied,
   4050         ENOENT => return error.FileNotFound,
   4051         ENOTSUP => return error.NotSupported,
   4052         ENOTDIR => return error.NotDir,
   4053         ENAMETOOLONG => return error.NameTooLong,
   4054         ELOOP => return error.SymLinkLoop,
   4055         EIO => return error.InputOutput,
   4056         else => |err| return unexpectedErrno(@intCast(usize, err)),
   4057     };
   4058     return mem.spanZ(result_path);
   4059 }
   4060 
   4061 /// Same as `realpath` except `pathname` is UTF16LE-encoded.
   4062 pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   4063     const w = windows;
   4064 
   4065     const dir = std.fs.cwd().fd;
   4066     const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
   4067     const share_access = w.FILE_SHARE_READ;
   4068     const creation = w.FILE_OPEN;
   4069     const h_file = blk: {
   4070         const res = w.OpenFile(pathname, .{
   4071             .dir = dir,
   4072             .access_mask = access_mask,
   4073             .share_access = share_access,
   4074             .creation = creation,
   4075             .io_mode = .blocking,
   4076         }) catch |err| switch (err) {
   4077             error.IsDir => break :blk w.OpenFile(pathname, .{
   4078                 .dir = dir,
   4079                 .access_mask = access_mask,
   4080                 .share_access = share_access,
   4081                 .creation = creation,
   4082                 .io_mode = .blocking,
   4083                 .open_dir = true,
   4084             }) catch |er| switch (er) {
   4085                 error.WouldBlock => unreachable,
   4086                 else => |e2| return e2,
   4087             },
   4088             error.WouldBlock => unreachable,
   4089             else => |e| return e,
   4090         };
   4091         break :blk res;
   4092     };
   4093     defer w.CloseHandle(h_file);
   4094 
   4095     return getFdPath(h_file, out_buffer);
   4096 }
   4097 
   4098 /// Return canonical path of handle `fd`.
   4099 /// This function is very host-specific and is not universally supported by all hosts.
   4100 /// For example, while it generally works on Linux, macOS or Windows, it is unsupported
   4101 /// on FreeBSD, or WASI.
   4102 pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   4103     switch (builtin.os.tag) {
   4104         .windows => {
   4105             var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
   4106             const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);
   4107 
   4108             // Trust that Windows gives us valid UTF-16LE.
   4109             const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
   4110             return out_buffer[0..end_index];
   4111         },
   4112         .macosx, .ios, .watchos, .tvos => {
   4113             // On macOS, we can use F_GETPATH fcntl command to query the OS for
   4114             // the path to the file descriptor.
   4115             @memset(out_buffer, 0, MAX_PATH_BYTES);
   4116             switch (errno(system.fcntl(fd, F_GETPATH, out_buffer))) {
   4117                 0 => {},
   4118                 EBADF => return error.FileNotFound,
   4119                 // TODO man pages for fcntl on macOS don't really tell you what
   4120                 // errno values to expect when command is F_GETPATH...
   4121                 else => |err| return unexpectedErrno(err),
   4122             }
   4123             const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES;
   4124             return out_buffer[0..len];
   4125         },
   4126         .linux => {
   4127             var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined;
   4128             const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable;
   4129 
   4130             const target = readlinkZ(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer) catch |err| {
   4131                 switch (err) {
   4132                     error.UnsupportedReparsePointType => unreachable, // Windows only,
   4133                     else => |e| return e,
   4134                 }
   4135             };
   4136             return target;
   4137         },
   4138         else => @compileError("querying for canonical path of a handle is unsupported on this host"),
   4139     }
   4140 }
   4141 
   4142 /// Spurious wakeups are possible and no precision of timing is guaranteed.
   4143 pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
   4144     var req = timespec{
   4145         .tv_sec = math.cast(isize, seconds) catch math.maxInt(isize),
   4146         .tv_nsec = math.cast(isize, nanoseconds) catch math.maxInt(isize),
   4147     };
   4148     var rem: timespec = undefined;
   4149     while (true) {
   4150         switch (errno(system.nanosleep(&req, &rem))) {
   4151             EFAULT => unreachable,
   4152             EINVAL => {
   4153                 // Sometimes Darwin returns EINVAL for no reason.
   4154                 // We treat it as a spurious wakeup.
   4155                 return;
   4156             },
   4157             EINTR => {
   4158                 req = rem;
   4159                 continue;
   4160             },
   4161             // This prong handles success as well as unexpected errors.
   4162             else => return,
   4163         }
   4164     }
   4165 }
   4166 
   4167 pub fn dl_iterate_phdr(
   4168     context: anytype,
   4169     comptime Error: type,
   4170     comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
   4171 ) Error!void {
   4172     const Context = @TypeOf(context);
   4173 
   4174     if (builtin.object_format != .elf)
   4175         @compileError("dl_iterate_phdr is not available for this target");
   4176 
   4177     if (builtin.link_libc) {
   4178         switch (system.dl_iterate_phdr(struct {
   4179             fn callbackC(info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int {
   4180                 const context_ptr = @ptrCast(*const Context, @alignCast(@alignOf(*const Context), data));
   4181                 callback(info, size, context_ptr.*) catch |err| return @errorToInt(err);
   4182                 return 0;
   4183             }
   4184         }.callbackC, @intToPtr(?*c_void, @ptrToInt(&context)))) {
   4185             0 => return,
   4186             else => |err| return @errSetCast(Error, @intToError(@intCast(u16, err))), // TODO don't hardcode u16
   4187         }
   4188     }
   4189 
   4190     const elf_base = std.process.getBaseAddress();
   4191     const ehdr = @intToPtr(*elf.Ehdr, elf_base);
   4192     // Make sure the base address points to an ELF image
   4193     assert(mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF"));
   4194     const n_phdr = ehdr.e_phnum;
   4195     const phdrs = (@intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff))[0..n_phdr];
   4196 
   4197     var it = dl.linkmap_iterator(phdrs) catch unreachable;
   4198 
   4199     // The executable has no dynamic link segment, create a single entry for
   4200     // the whole ELF image
   4201     if (it.end()) {
   4202         var info = dl_phdr_info{
   4203             .dlpi_addr = 0,
   4204             .dlpi_name = "/proc/self/exe",
   4205             .dlpi_phdr = phdrs.ptr,
   4206             .dlpi_phnum = ehdr.e_phnum,
   4207         };
   4208 
   4209         return callback(&info, @sizeOf(dl_phdr_info), context);
   4210     }
   4211 
   4212     // Last return value from the callback function
   4213     while (it.next()) |entry| {
   4214         var dlpi_phdr: [*]elf.Phdr = undefined;
   4215         var dlpi_phnum: u16 = undefined;
   4216 
   4217         if (entry.l_addr != 0) {
   4218             const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr);
   4219             dlpi_phdr = @intToPtr([*]elf.Phdr, entry.l_addr + elf_header.e_phoff);
   4220             dlpi_phnum = elf_header.e_phnum;
   4221         } else {
   4222             // This is the running ELF image
   4223             dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff);
   4224             dlpi_phnum = ehdr.e_phnum;
   4225         }
   4226 
   4227         var info = dl_phdr_info{
   4228             .dlpi_addr = entry.l_addr,
   4229             .dlpi_name = entry.l_name,
   4230             .dlpi_phdr = dlpi_phdr,
   4231             .dlpi_phnum = dlpi_phnum,
   4232         };
   4233 
   4234         try callback(&info, @sizeOf(dl_phdr_info), context);
   4235     }
   4236 }
   4237 
   4238 pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
   4239 
   4240 /// TODO: change this to return the timespec as a return value
   4241 /// TODO: look into making clk_id an enum
   4242 pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
   4243     if (std.Target.current.os.tag == .wasi) {
   4244         var ts: timestamp_t = undefined;
   4245         switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) {
   4246             0 => {
   4247                 tp.* = .{
   4248                     .tv_sec = @intCast(i64, ts / std.time.ns_per_s),
   4249                     .tv_nsec = @intCast(isize, ts % std.time.ns_per_s),
   4250                 };
   4251             },
   4252             EINVAL => return error.UnsupportedClock,
   4253             else => |err| return unexpectedErrno(err),
   4254         }
   4255         return;
   4256     }
   4257     if (std.Target.current.os.tag == .windows) {
   4258         if (clk_id == CLOCK_REALTIME) {
   4259             var ft: windows.FILETIME = undefined;
   4260             windows.kernel32.GetSystemTimeAsFileTime(&ft);
   4261             // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch.
   4262             const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
   4263             const ft_per_s = std.time.ns_per_s / 100;
   4264             tp.* = .{
   4265                 .tv_sec = @intCast(i64, ft64 / ft_per_s) + std.time.epoch.windows,
   4266                 .tv_nsec = @intCast(c_long, ft64 % ft_per_s) * 100,
   4267             };
   4268             return;
   4269         } else {
   4270             // TODO POSIX implementation of CLOCK_MONOTONIC on Windows.
   4271             return error.UnsupportedClock;
   4272         }
   4273     }
   4274 
   4275     switch (errno(system.clock_gettime(clk_id, tp))) {
   4276         0 => return,
   4277         EFAULT => unreachable,
   4278         EINVAL => return error.UnsupportedClock,
   4279         else => |err| return unexpectedErrno(err),
   4280     }
   4281 }
   4282 
   4283 pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
   4284     if (std.Target.current.os.tag == .wasi) {
   4285         var ts: timestamp_t = undefined;
   4286         switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) {
   4287             0 => res.* = .{
   4288                 .tv_sec = @intCast(i64, ts / std.time.ns_per_s),
   4289                 .tv_nsec = @intCast(isize, ts % std.time.ns_per_s),
   4290             },
   4291             EINVAL => return error.UnsupportedClock,
   4292             else => |err| return unexpectedErrno(err),
   4293         }
   4294         return;
   4295     }
   4296 
   4297     switch (errno(system.clock_getres(clk_id, res))) {
   4298         0 => return,
   4299         EFAULT => unreachable,
   4300         EINVAL => return error.UnsupportedClock,
   4301         else => |err| return unexpectedErrno(err),
   4302     }
   4303 }
   4304 
   4305 pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
   4306 
   4307 pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
   4308     var set: cpu_set_t = undefined;
   4309     switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
   4310         0 => return set,
   4311         EFAULT => unreachable,
   4312         EINVAL => unreachable,
   4313         ESRCH => unreachable,
   4314         EPERM => return error.PermissionDenied,
   4315         else => |err| return unexpectedErrno(err),
   4316     }
   4317 }
   4318 
   4319 /// Used to convert a slice to a null terminated slice on the stack.
   4320 /// TODO https://github.com/ziglang/zig/issues/287
   4321 pub fn toPosixPath(file_path: []const u8) ![PATH_MAX - 1:0]u8 {
   4322     if (std.debug.runtime_safety) assert(std.mem.indexOfScalar(u8, file_path, 0) == null);
   4323     var path_with_null: [PATH_MAX - 1:0]u8 = undefined;
   4324     // >= rather than > to make room for the null byte
   4325     if (file_path.len >= PATH_MAX) return error.NameTooLong;
   4326     mem.copy(u8, &path_with_null, file_path);
   4327     path_with_null[file_path.len] = 0;
   4328     return path_with_null;
   4329 }
   4330 
   4331 /// Whether or not error.Unexpected will print its value and a stack trace.
   4332 /// if this happens the fix is to add the error code to the corresponding
   4333 /// switch expression, possibly introduce a new error in the error set, and
   4334 /// send a patch to Zig.
   4335 pub const unexpected_error_tracing = builtin.mode == .Debug;
   4336 
   4337 pub const UnexpectedError = error{
   4338     /// The Operating System returned an undocumented error code.
   4339     /// This error is in theory not possible, but it would be better
   4340     /// to handle this error than to invoke undefined behavior.
   4341     Unexpected,
   4342 };
   4343 
   4344 /// Call this when you made a syscall or something that sets errno
   4345 /// and you get an unexpected error.
   4346 pub fn unexpectedErrno(err: usize) UnexpectedError {
   4347     if (unexpected_error_tracing) {
   4348         std.debug.warn("unexpected errno: {}\n", .{err});
   4349         std.debug.dumpCurrentStackTrace(null);
   4350     }
   4351     return error.Unexpected;
   4352 }
   4353 
   4354 pub const SigaltstackError = error{
   4355     /// The supplied stack size was less than MINSIGSTKSZ.
   4356     SizeTooSmall,
   4357 
   4358     /// Attempted to change the signal stack while it was active.
   4359     PermissionDenied,
   4360 } || UnexpectedError;
   4361 
   4362 pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
   4363     switch (errno(system.sigaltstack(ss, old_ss))) {
   4364         0 => return,
   4365         EFAULT => unreachable,
   4366         EINVAL => unreachable,
   4367         ENOMEM => return error.SizeTooSmall,
   4368         EPERM => return error.PermissionDenied,
   4369         else => |err| return unexpectedErrno(err),
   4370     }
   4371 }
   4372 
   4373 /// Examine and change a signal action.
   4374 pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void {
   4375     switch (errno(system.sigaction(sig, act, oact))) {
   4376         0 => return,
   4377         EFAULT => unreachable,
   4378         EINVAL => unreachable,
   4379         else => unreachable,
   4380     }
   4381 }
   4382 
   4383 pub const FutimensError = error{
   4384     /// times is NULL, or both tv_nsec values are UTIME_NOW, and either:
   4385     /// *  the effective user ID of the caller does not match the  owner
   4386     ///    of  the  file,  the  caller does not have write access to the
   4387     ///    file, and the caller is not privileged (Linux: does not  have
   4388     ///    either  the  CAP_FOWNER  or the CAP_DAC_OVERRIDE capability);
   4389     ///    or,
   4390     /// *  the file is marked immutable (see chattr(1)).
   4391     AccessDenied,
   4392 
   4393     /// The caller attempted to change one or both timestamps to a value
   4394     /// other than the current time, or to change one of the  timestamps
   4395     /// to the current time while leaving the other timestamp unchanged,
   4396     /// (i.e., times is not NULL, neither tv_nsec  field  is  UTIME_NOW,
   4397     /// and neither tv_nsec field is UTIME_OMIT) and either:
   4398     /// *  the  caller's  effective  user ID does not match the owner of
   4399     ///    file, and the caller is not privileged (Linux: does not  have
   4400     ///    the CAP_FOWNER capability); or,
   4401     /// *  the file is marked append-only or immutable (see chattr(1)).
   4402     PermissionDenied,
   4403 
   4404     ReadOnlyFileSystem,
   4405 } || UnexpectedError;
   4406 
   4407 pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
   4408     if (builtin.os.tag == .wasi) {
   4409         // TODO WASI encodes `wasi.fstflags` to signify magic values
   4410         // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore
   4411         // this here, but we should really handle it somehow.
   4412         const atim = times[0].toTimestamp();
   4413         const mtim = times[1].toTimestamp();
   4414         switch (wasi.fd_filestat_set_times(fd, atim, mtim, wasi.FILESTAT_SET_ATIM | wasi.FILESTAT_SET_MTIM)) {
   4415             wasi.ESUCCESS => return,
   4416             wasi.EACCES => return error.AccessDenied,
   4417             wasi.EPERM => return error.PermissionDenied,
   4418             wasi.EBADF => unreachable, // always a race condition
   4419             wasi.EFAULT => unreachable,
   4420             wasi.EINVAL => unreachable,
   4421             wasi.EROFS => return error.ReadOnlyFileSystem,
   4422             else => |err| return unexpectedErrno(err),
   4423         }
   4424     }
   4425 
   4426     switch (errno(system.futimens(fd, times))) {
   4427         0 => return,
   4428         EACCES => return error.AccessDenied,
   4429         EPERM => return error.PermissionDenied,
   4430         EBADF => unreachable, // always a race condition
   4431         EFAULT => unreachable,
   4432         EINVAL => unreachable,
   4433         EROFS => return error.ReadOnlyFileSystem,
   4434         else => |err| return unexpectedErrno(err),
   4435     }
   4436 }
   4437 
   4438 pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;
   4439 
   4440 pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
   4441     if (builtin.link_libc) {
   4442         switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
   4443             0 => return mem.spanZ(@ptrCast([*:0]u8, name_buffer)),
   4444             EFAULT => unreachable,
   4445             ENAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
   4446             EPERM => return error.PermissionDenied,
   4447             else => |err| return unexpectedErrno(err),
   4448         }
   4449     }
   4450     if (builtin.os.tag == .linux) {
   4451         const uts = uname();
   4452         const hostname = mem.spanZ(@ptrCast([*:0]const u8, &uts.nodename));
   4453         mem.copy(u8, name_buffer, hostname);
   4454         return name_buffer[0..hostname.len];
   4455     }
   4456 
   4457     @compileError("TODO implement gethostname for this OS");
   4458 }
   4459 
   4460 pub fn uname() utsname {
   4461     var uts: utsname = undefined;
   4462     switch (errno(system.uname(&uts))) {
   4463         0 => return uts,
   4464         EFAULT => unreachable,
   4465         else => unreachable,
   4466     }
   4467 }
   4468 
   4469 pub fn res_mkquery(
   4470     op: u4,
   4471     dname: []const u8,
   4472     class: u8,
   4473     ty: u8,
   4474     data: []const u8,
   4475     newrr: ?[*]const u8,
   4476     buf: []u8,
   4477 ) usize {
   4478     // This implementation is ported from musl libc.
   4479     // A more idiomatic "ziggy" implementation would be welcome.
   4480     var name = dname;
   4481     if (mem.endsWith(u8, name, ".")) name.len -= 1;
   4482     assert(name.len <= 253);
   4483     const n = 17 + name.len + @boolToInt(name.len != 0);
   4484 
   4485     // Construct query template - ID will be filled later
   4486     var q: [280]u8 = undefined;
   4487     @memset(&q, 0, n);
   4488     q[2] = @as(u8, op) * 8 + 1;
   4489     q[5] = 1;
   4490     mem.copy(u8, q[13..], name);
   4491     var i: usize = 13;
   4492     var j: usize = undefined;
   4493     while (q[i] != 0) : (i = j + 1) {
   4494         j = i;
   4495         while (q[j] != 0 and q[j] != '.') : (j += 1) {}
   4496         // TODO determine the circumstances for this and whether or
   4497         // not this should be an error.
   4498         if (j - i - 1 > 62) unreachable;
   4499         q[i - 1] = @intCast(u8, j - i);
   4500     }
   4501     q[i + 1] = ty;
   4502     q[i + 3] = class;
   4503 
   4504     // Make a reasonably unpredictable id
   4505     var ts: timespec = undefined;
   4506     clock_gettime(CLOCK_REALTIME, &ts) catch {};
   4507     const UInt = std.meta.Int(false, @TypeOf(ts.tv_nsec).bit_count);
   4508     const unsec = @bitCast(UInt, ts.tv_nsec);
   4509     const id = @truncate(u32, unsec + unsec / 65536);
   4510     q[0] = @truncate(u8, id / 256);
   4511     q[1] = @truncate(u8, id);
   4512 
   4513     mem.copy(u8, buf, q[0..n]);
   4514     return n;
   4515 }
   4516 
   4517 pub const SendError = error{
   4518     /// (For UNIX domain sockets, which are identified by pathname) Write permission is  denied
   4519     /// on  the destination socket file, or search permission is denied for one of the
   4520     /// directories the path prefix.  (See path_resolution(7).)
   4521     /// (For UDP sockets) An attempt was made to send to a network/broadcast address as  though
   4522     /// it was a unicast address.
   4523     AccessDenied,
   4524 
   4525     /// The socket is marked nonblocking and the requested operation would block, and
   4526     /// there is no global event loop configured.
   4527     /// It's also possible to get this error under the following condition:
   4528     /// (Internet  domain datagram sockets) The socket referred to by sockfd had not previously
   4529     /// been bound to an address and, upon attempting to bind it to an ephemeral port,  it  was
   4530     /// determined that all port numbers in the ephemeral port range are currently in use.  See
   4531     /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
   4532     WouldBlock,
   4533 
   4534     /// Another Fast Open is already in progress.
   4535     FastOpenAlreadyInProgress,
   4536 
   4537     /// Connection reset by peer.
   4538     ConnectionResetByPeer,
   4539 
   4540     /// The  socket  type requires that message be sent atomically, and the size of the message
   4541     /// to be sent made this impossible. The message is not transmitted.
   4542     MessageTooBig,
   4543 
   4544     /// The output queue for a network interface was full.  This generally indicates  that  the
   4545     /// interface  has  stopped sending, but may be caused by transient congestion.  (Normally,
   4546     /// this does not occur in Linux.  Packets are just silently dropped when  a  device  queue
   4547     /// overflows.)
   4548     /// This is also caused when there is not enough kernel memory available.
   4549     SystemResources,
   4550 
   4551     /// The  local  end  has been shut down on a connection oriented socket.  In this case, the
   4552     /// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.
   4553     BrokenPipe,
   4554 } || UnexpectedError;
   4555 
   4556 /// Transmit a message to another socket.
   4557 ///
   4558 /// The `sendto` call may be used only when the socket is in a connected state (so that the intended
   4559 /// recipient  is  known). The  following call
   4560 ///
   4561 ///     send(sockfd, buf, len, flags);
   4562 ///
   4563 /// is equivalent to
   4564 ///
   4565 ///     sendto(sockfd, buf, len, flags, NULL, 0);
   4566 ///
   4567 /// If  sendto()  is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments
   4568 /// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
   4569 /// that the socket was actually connected.
   4570 /// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying  its  size.
   4571 ///
   4572 /// If the message is too long to pass atomically through the underlying protocol,
   4573 /// `SendError.MessageTooBig` is returned, and the message is not transmitted.
   4574 ///
   4575 /// There is no  indication  of  failure  to  deliver.
   4576 ///
   4577 /// When the message does not fit into the send buffer of  the  socket,  `sendto`  normally  blocks,
   4578 /// unless  the socket has been placed in nonblocking I/O mode.  In nonblocking mode it would fail
   4579 /// with `SendError.WouldBlock`.  The `select` call may be used  to  determine when it is
   4580 /// possible to send more data.
   4581 pub fn sendto(
   4582     /// The file descriptor of the sending socket.
   4583     sockfd: fd_t,
   4584     /// Message to send.
   4585     buf: []const u8,
   4586     flags: u32,
   4587     dest_addr: ?*const sockaddr,
   4588     addrlen: socklen_t,
   4589 ) SendError!usize {
   4590     while (true) {
   4591         const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
   4592         switch (errno(rc)) {
   4593             0 => return @intCast(usize, rc),
   4594 
   4595             EACCES => return error.AccessDenied,
   4596             EAGAIN => if (std.event.Loop.instance) |loop| {
   4597                 loop.waitUntilFdWritable(sockfd);
   4598                 continue;
   4599             } else {
   4600                 return error.WouldBlock;
   4601             },
   4602             EALREADY => return error.FastOpenAlreadyInProgress,
   4603             EBADF => unreachable, // always a race condition
   4604             ECONNRESET => return error.ConnectionResetByPeer,
   4605             EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
   4606             EFAULT => unreachable, // An invalid user space address was specified for an argument.
   4607             EINTR => continue,
   4608             EINVAL => unreachable, // Invalid argument passed.
   4609             EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
   4610             EMSGSIZE => return error.MessageTooBig,
   4611             ENOBUFS => return error.SystemResources,
   4612             ENOMEM => return error.SystemResources,
   4613             ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
   4614             ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   4615             EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
   4616             EPIPE => return error.BrokenPipe,
   4617             else => |err| return unexpectedErrno(err),
   4618         }
   4619     }
   4620 }
   4621 
   4622 /// Transmit a message to another socket.
   4623 ///
   4624 /// The `send` call may be used only when the socket is in a connected state (so that the intended
   4625 /// recipient  is  known).   The  only  difference  between `send` and `write` is the presence of
   4626 /// flags.  With a zero flags argument, `send` is equivalent to  `write`.   Also,  the  following
   4627 /// call
   4628 ///
   4629 ///     send(sockfd, buf, len, flags);
   4630 ///
   4631 /// is equivalent to
   4632 ///
   4633 ///     sendto(sockfd, buf, len, flags, NULL, 0);
   4634 ///
   4635 /// There is no  indication  of  failure  to  deliver.
   4636 ///
   4637 /// When the message does not fit into the send buffer of  the  socket,  `send`  normally  blocks,
   4638 /// unless  the socket has been placed in nonblocking I/O mode.  In nonblocking mode it would fail
   4639 /// with `SendError.WouldBlock`.  The `select` call may be used  to  determine when it is
   4640 /// possible to send more data.
   4641 pub fn send(
   4642     /// The file descriptor of the sending socket.
   4643     sockfd: fd_t,
   4644     buf: []const u8,
   4645     flags: u32,
   4646 ) SendError!usize {
   4647     return sendto(sockfd, buf, flags, null, 0);
   4648 }
   4649 
   4650 pub const SendFileError = PReadError || WriteError || SendError;
   4651 
   4652 fn count_iovec_bytes(iovs: []const iovec_const) usize {
   4653     var count: usize = 0;
   4654     for (iovs) |iov| {
   4655         count += iov.iov_len;
   4656     }
   4657     return count;
   4658 }
   4659 
   4660 /// Transfer data between file descriptors, with optional headers and trailers.
   4661 /// Returns the number of bytes written, which can be zero.
   4662 ///
   4663 /// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible,
   4664 /// this is done within the operating system kernel, which can provide better performance
   4665 /// characteristics than transferring data from kernel to user space and back, such as with
   4666 /// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been
   4667 /// reached. Note, however, that partial writes are still possible in this case.
   4668 ///
   4669 /// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor
   4670 /// opened for writing. They may be any kind of file descriptor; however, if `in_fd` is not a regular
   4671 /// file system file, it may cause this function to fall back to calling `read` and `write`, in which case
   4672 /// atomicity guarantees no longer apply.
   4673 ///
   4674 /// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated.
   4675 /// If the output file descriptor has a seek position, it is updated as bytes are written. When
   4676 /// `in_offset` is past the end of the input file, it successfully reads 0 bytes.
   4677 ///
   4678 /// `flags` has different meanings per operating system; refer to the respective man pages.
   4679 ///
   4680 /// These systems support atomically sending everything, including headers and trailers:
   4681 /// * macOS
   4682 /// * FreeBSD
   4683 ///
   4684 /// These systems support in-kernel data copying, but headers and trailers are not sent atomically:
   4685 /// * Linux
   4686 ///
   4687 /// Other systems fall back to calling `read` / `write`.
   4688 ///
   4689 /// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000`
   4690 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as
   4691 /// well as stuffing the errno codes into the last `4096` values. This is cited on the `sendfile` man page.
   4692 /// The corresponding POSIX limit on this is `math.maxInt(isize)`.
   4693 pub fn sendfile(
   4694     out_fd: fd_t,
   4695     in_fd: fd_t,
   4696     in_offset: u64,
   4697     in_len: u64,
   4698     headers: []const iovec_const,
   4699     trailers: []const iovec_const,
   4700     flags: u32,
   4701 ) SendFileError!usize {
   4702     var header_done = false;
   4703     var total_written: usize = 0;
   4704 
   4705     // Prevents EOVERFLOW.
   4706     const size_t = @Type(std.builtin.TypeInfo{
   4707         .Int = .{
   4708             .is_signed = false,
   4709             .bits = @typeInfo(usize).Int.bits - 1,
   4710         },
   4711     });
   4712     const max_count = switch (std.Target.current.os.tag) {
   4713         .linux => 0x7ffff000,
   4714         else => math.maxInt(size_t),
   4715     };
   4716 
   4717     switch (std.Target.current.os.tag) {
   4718         .linux => sf: {
   4719             // sendfile() first appeared in Linux 2.2, glibc 2.1.
   4720             const call_sf = comptime if (builtin.link_libc)
   4721                 std.c.versionCheck(.{ .major = 2, .minor = 1 }).ok
   4722             else
   4723                 std.Target.current.os.version_range.linux.range.max.order(.{ .major = 2, .minor = 2 }) != .lt;
   4724             if (!call_sf) break :sf;
   4725 
   4726             if (headers.len != 0) {
   4727                 const amt = try writev(out_fd, headers);
   4728                 total_written += amt;
   4729                 if (amt < count_iovec_bytes(headers)) return total_written;
   4730                 header_done = true;
   4731             }
   4732 
   4733             // Here we match BSD behavior, making a zero count value send as many bytes as possible.
   4734             const adjusted_count = if (in_len == 0) max_count else math.min(in_len, @as(size_t, max_count));
   4735 
   4736             while (true) {
   4737                 var offset: off_t = @bitCast(off_t, in_offset);
   4738                 const rc = system.sendfile(out_fd, in_fd, &offset, adjusted_count);
   4739                 switch (errno(rc)) {
   4740                     0 => {
   4741                         const amt = @bitCast(usize, rc);
   4742                         total_written += amt;
   4743                         if (in_len == 0 and amt == 0) {
   4744                             // We have detected EOF from `in_fd`.
   4745                             break;
   4746                         } else if (amt < in_len) {
   4747                             return total_written;
   4748                         } else {
   4749                             break;
   4750                         }
   4751                     },
   4752 
   4753                     EBADF => unreachable, // Always a race condition.
   4754                     EFAULT => unreachable, // Segmentation fault.
   4755                     EOVERFLOW => unreachable, // We avoid passing too large of a `count`.
   4756                     ENOTCONN => unreachable, // `out_fd` is an unconnected socket.
   4757 
   4758                     EINVAL, ENOSYS => {
   4759                         // EINVAL could be any of the following situations:
   4760                         // * Descriptor is not valid or locked
   4761                         // * an mmap(2)-like operation is  not  available  for in_fd
   4762                         // * count is negative
   4763                         // * out_fd has the O_APPEND flag set
   4764                         // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write
   4765                         // manually, the same as ENOSYS.
   4766                         break :sf;
   4767                     },
   4768                     EAGAIN => if (std.event.Loop.instance) |loop| {
   4769                         loop.waitUntilFdWritable(out_fd);
   4770                         continue;
   4771                     } else {
   4772                         return error.WouldBlock;
   4773                     },
   4774                     EIO => return error.InputOutput,
   4775                     EPIPE => return error.BrokenPipe,
   4776                     ENOMEM => return error.SystemResources,
   4777                     ENXIO => return error.Unseekable,
   4778                     ESPIPE => return error.Unseekable,
   4779                     else => |err| {
   4780                         const discard = unexpectedErrno(err);
   4781                         break :sf;
   4782                     },
   4783                 }
   4784             }
   4785 
   4786             if (trailers.len != 0) {
   4787                 total_written += try writev(out_fd, trailers);
   4788             }
   4789 
   4790             return total_written;
   4791         },
   4792         .freebsd => sf: {
   4793             var hdtr_data: std.c.sf_hdtr = undefined;
   4794             var hdtr: ?*std.c.sf_hdtr = null;
   4795             if (headers.len != 0 or trailers.len != 0) {
   4796                 // Here we carefully avoid `@intCast` by returning partial writes when
   4797                 // too many io vectors are provided.
   4798                 const hdr_cnt = math.cast(u31, headers.len) catch math.maxInt(u31);
   4799                 if (headers.len > hdr_cnt) return writev(out_fd, headers);
   4800 
   4801                 const trl_cnt = math.cast(u31, trailers.len) catch math.maxInt(u31);
   4802 
   4803                 hdtr_data = std.c.sf_hdtr{
   4804                     .headers = headers.ptr,
   4805                     .hdr_cnt = hdr_cnt,
   4806                     .trailers = trailers.ptr,
   4807                     .trl_cnt = trl_cnt,
   4808                 };
   4809                 hdtr = &hdtr_data;
   4810             }
   4811 
   4812             const adjusted_count = math.min(in_len, max_count);
   4813 
   4814             while (true) {
   4815                 var sbytes: off_t = undefined;
   4816                 const offset = @bitCast(off_t, in_offset);
   4817                 const err = errno(system.sendfile(in_fd, out_fd, offset, adjusted_count, hdtr, &sbytes, flags));
   4818                 const amt = @bitCast(usize, sbytes);
   4819                 switch (err) {
   4820                     0 => return amt,
   4821 
   4822                     EBADF => unreachable, // Always a race condition.
   4823                     EFAULT => unreachable, // Segmentation fault.
   4824                     ENOTCONN => unreachable, // `out_fd` is an unconnected socket.
   4825 
   4826                     EINVAL, EOPNOTSUPP, ENOTSOCK, ENOSYS => {
   4827                         // EINVAL could be any of the following situations:
   4828                         // * The fd argument is not a regular file.
   4829                         // * The s argument is not a SOCK_STREAM type socket.
   4830                         // * The offset argument is negative.
   4831                         // Because of some of these possibilities, we fall back to doing read/write
   4832                         // manually, the same as ENOSYS.
   4833                         break :sf;
   4834                     },
   4835 
   4836                     EINTR => if (amt != 0) return amt else continue,
   4837 
   4838                     EAGAIN => if (amt != 0) {
   4839                         return amt;
   4840                     } else if (std.event.Loop.instance) |loop| {
   4841                         loop.waitUntilFdWritable(out_fd);
   4842                         continue;
   4843                     } else {
   4844                         return error.WouldBlock;
   4845                     },
   4846 
   4847                     EBUSY => if (amt != 0) {
   4848                         return amt;
   4849                     } else if (std.event.Loop.instance) |loop| {
   4850                         loop.waitUntilFdReadable(in_fd);
   4851                         continue;
   4852                     } else {
   4853                         return error.WouldBlock;
   4854                     },
   4855 
   4856                     EIO => return error.InputOutput,
   4857                     ENOBUFS => return error.SystemResources,
   4858                     EPIPE => return error.BrokenPipe,
   4859 
   4860                     else => {
   4861                         const discard = unexpectedErrno(err);
   4862                         if (amt != 0) {
   4863                             return amt;
   4864                         } else {
   4865                             break :sf;
   4866                         }
   4867                     },
   4868                 }
   4869             }
   4870         },
   4871         .macosx, .ios, .tvos, .watchos => sf: {
   4872             var hdtr_data: std.c.sf_hdtr = undefined;
   4873             var hdtr: ?*std.c.sf_hdtr = null;
   4874             if (headers.len != 0 or trailers.len != 0) {
   4875                 // Here we carefully avoid `@intCast` by returning partial writes when
   4876                 // too many io vectors are provided.
   4877                 const hdr_cnt = math.cast(u31, headers.len) catch math.maxInt(u31);
   4878                 if (headers.len > hdr_cnt) return writev(out_fd, headers);
   4879 
   4880                 const trl_cnt = math.cast(u31, trailers.len) catch math.maxInt(u31);
   4881 
   4882                 hdtr_data = std.c.sf_hdtr{
   4883                     .headers = headers.ptr,
   4884                     .hdr_cnt = hdr_cnt,
   4885                     .trailers = trailers.ptr,
   4886                     .trl_cnt = trl_cnt,
   4887                 };
   4888                 hdtr = &hdtr_data;
   4889             }
   4890 
   4891             const adjusted_count = math.min(in_len, @as(u63, max_count));
   4892 
   4893             while (true) {
   4894                 var sbytes: off_t = adjusted_count;
   4895                 const signed_offset = @bitCast(i64, in_offset);
   4896                 const err = errno(system.sendfile(in_fd, out_fd, signed_offset, &sbytes, hdtr, flags));
   4897                 const amt = @bitCast(usize, sbytes);
   4898                 switch (err) {
   4899                     0 => return amt,
   4900 
   4901                     EBADF => unreachable, // Always a race condition.
   4902                     EFAULT => unreachable, // Segmentation fault.
   4903                     EINVAL => unreachable,
   4904                     ENOTCONN => unreachable, // `out_fd` is an unconnected socket.
   4905 
   4906                     ENOTSUP, ENOTSOCK, ENOSYS => break :sf,
   4907 
   4908                     EINTR => if (amt != 0) return amt else continue,
   4909 
   4910                     EAGAIN => if (amt != 0) {
   4911                         return amt;
   4912                     } else if (std.event.Loop.instance) |loop| {
   4913                         loop.waitUntilFdWritable(out_fd);
   4914                         continue;
   4915                     } else {
   4916                         return error.WouldBlock;
   4917                     },
   4918 
   4919                     EIO => return error.InputOutput,
   4920                     EPIPE => return error.BrokenPipe,
   4921 
   4922                     else => {
   4923                         const discard = unexpectedErrno(err);
   4924                         if (amt != 0) {
   4925                             return amt;
   4926                         } else {
   4927                             break :sf;
   4928                         }
   4929                     },
   4930                 }
   4931             }
   4932         },
   4933         else => {}, // fall back to read/write
   4934     }
   4935 
   4936     if (headers.len != 0 and !header_done) {
   4937         const amt = try writev(out_fd, headers);
   4938         total_written += amt;
   4939         if (amt < count_iovec_bytes(headers)) return total_written;
   4940     }
   4941 
   4942     rw: {
   4943         var buf: [8 * 4096]u8 = undefined;
   4944         // Here we match BSD behavior, making a zero count value send as many bytes as possible.
   4945         const adjusted_count = if (in_len == 0) buf.len else math.min(buf.len, in_len);
   4946         const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
   4947         if (amt_read == 0) {
   4948             if (in_len == 0) {
   4949                 // We have detected EOF from `in_fd`.
   4950                 break :rw;
   4951             } else {
   4952                 return total_written;
   4953             }
   4954         }
   4955         const amt_written = try write(out_fd, buf[0..amt_read]);
   4956         total_written += amt_written;
   4957         if (amt_written < in_len or in_len == 0) return total_written;
   4958     }
   4959 
   4960     if (trailers.len != 0) {
   4961         total_written += try writev(out_fd, trailers);
   4962     }
   4963 
   4964     return total_written;
   4965 }
   4966 
   4967 pub const CopyFileRangeError = error{
   4968     FileTooBig,
   4969     InputOutput,
   4970     IsDir,
   4971     OutOfMemory,
   4972     NoSpaceLeft,
   4973     Unseekable,
   4974     PermissionDenied,
   4975     FileBusy,
   4976 } || PReadError || PWriteError || UnexpectedError;
   4977 
   4978 /// Transfer data between file descriptors at specified offsets.
   4979 /// Returns the number of bytes written, which can less than requested.
   4980 ///
   4981 /// The `copy_file_range` call copies `len` bytes from one file descriptor to another. When possible,
   4982 /// this is done within the operating system kernel, which can provide better performance
   4983 /// characteristics than transferring data from kernel to user space and back, such as with
   4984 /// `pread` and `pwrite` calls.
   4985 ///
   4986 /// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be a file descriptor
   4987 /// opened for writing. They may be any kind of file descriptor; however, if `fd_in` is not a regular
   4988 /// file system file, it may cause this function to fall back to calling `pread` and `pwrite`, in which case
   4989 /// atomicity guarantees no longer apply.
   4990 ///
   4991 /// If `fd_in` and `fd_out` are the same, source and target ranges must not overlap.
   4992 /// The file descriptor seek positions are ignored and not updated.
   4993 /// When `off_in` is past the end of the input file, it successfully reads 0 bytes.
   4994 ///
   4995 /// `flags` has different meanings per operating system; refer to the respective man pages.
   4996 ///
   4997 /// These systems support in-kernel data copying:
   4998 /// * Linux 4.5 (cross-filesystem 5.3)
   4999 ///
   5000 /// Other systems fall back to calling `pread` / `pwrite`.
   5001 ///
   5002 /// Maximum offsets on Linux are `math.maxInt(i64)`.
   5003 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 {
   5004     const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;
   5005 
   5006     // TODO support for other systems than linux
   5007     const try_syscall = comptime std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) != false;
   5008 
   5009     if (use_c or try_syscall) {
   5010         const sys = if (use_c) std.c else linux;
   5011 
   5012         var off_in_copy = @bitCast(i64, off_in);
   5013         var off_out_copy = @bitCast(i64, off_out);
   5014 
   5015         const rc = sys.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);
   5016 
   5017         // TODO avoid wasting a syscall every time if kernel is too old and returns ENOSYS https://github.com/ziglang/zig/issues/1018
   5018 
   5019         switch (sys.getErrno(rc)) {
   5020             0 => return @intCast(usize, rc),
   5021             EBADF => unreachable,
   5022             EFBIG => return error.FileTooBig,
   5023             EIO => return error.InputOutput,
   5024             EISDIR => return error.IsDir,
   5025             ENOMEM => return error.OutOfMemory,
   5026             ENOSPC => return error.NoSpaceLeft,
   5027             EOVERFLOW => return error.Unseekable,
   5028             EPERM => return error.PermissionDenied,
   5029             ETXTBSY => return error.FileBusy,
   5030             EINVAL => {}, // these may not be regular files, try fallback
   5031             EXDEV => {}, // support for cross-filesystem copy added in Linux 5.3, use fallback
   5032             ENOSYS => {}, // syscall added in Linux 4.5, use fallback
   5033             else => |err| return unexpectedErrno(err),
   5034         }
   5035     }
   5036 
   5037     var buf: [8 * 4096]u8 = undefined;
   5038     const adjusted_count = math.min(buf.len, len);
   5039     const amt_read = try pread(fd_in, buf[0..adjusted_count], off_in);
   5040     // TODO without @as the line below fails to compile for wasm32-wasi:
   5041     // error: integer value 0 cannot be coerced to type 'os.PWriteError!usize'
   5042     if (amt_read == 0) return @as(usize, 0);
   5043     return pwrite(fd_out, buf[0..amt_read], off_out);
   5044 }
   5045 
   5046 pub const PollError = error{
   5047     /// The kernel had no space to allocate file descriptor tables.
   5048     SystemResources,
   5049 } || UnexpectedError;
   5050 
   5051 pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
   5052     while (true) {
   5053         const rc = system.poll(fds.ptr, fds.len, timeout);
   5054         switch (errno(rc)) {
   5055             0 => return @intCast(usize, rc),
   5056             EFAULT => unreachable,
   5057             EINTR => continue,
   5058             EINVAL => unreachable,
   5059             ENOMEM => return error.SystemResources,
   5060             else => |err| return unexpectedErrno(err),
   5061         }
   5062     }
   5063 }
   5064 
   5065 pub const RecvFromError = error{
   5066     /// The socket is marked nonblocking and the requested operation would block, and
   5067     /// there is no global event loop configured.
   5068     WouldBlock,
   5069 
   5070     /// A remote host refused to allow the network connection, typically because it is not
   5071     /// running the requested service.
   5072     ConnectionRefused,
   5073 
   5074     /// Could not allocate kernel memory.
   5075     SystemResources,
   5076 } || UnexpectedError;
   5077 
   5078 pub fn recvfrom(
   5079     sockfd: fd_t,
   5080     buf: []u8,
   5081     flags: u32,
   5082     src_addr: ?*sockaddr,
   5083     addrlen: ?*socklen_t,
   5084 ) RecvFromError!usize {
   5085     while (true) {
   5086         const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
   5087         switch (errno(rc)) {
   5088             0 => return @intCast(usize, rc),
   5089             EBADF => unreachable, // always a race condition
   5090             EFAULT => unreachable,
   5091             EINVAL => unreachable,
   5092             ENOTCONN => unreachable,
   5093             ENOTSOCK => unreachable,
   5094             EINTR => continue,
   5095             EAGAIN => if (std.event.Loop.instance) |loop| {
   5096                 loop.waitUntilFdReadable(sockfd);
   5097                 continue;
   5098             } else {
   5099                 return error.WouldBlock;
   5100             },
   5101             ENOMEM => return error.SystemResources,
   5102             ECONNREFUSED => return error.ConnectionRefused,
   5103             else => |err| return unexpectedErrno(err),
   5104         }
   5105     }
   5106 }
   5107 
   5108 pub const DnExpandError = error{InvalidDnsPacket};
   5109 
   5110 pub fn dn_expand(
   5111     msg: []const u8,
   5112     comp_dn: []const u8,
   5113     exp_dn: []u8,
   5114 ) DnExpandError!usize {
   5115     // This implementation is ported from musl libc.
   5116     // A more idiomatic "ziggy" implementation would be welcome.
   5117     var p = comp_dn.ptr;
   5118     var len: usize = std.math.maxInt(usize);
   5119     const end = msg.ptr + msg.len;
   5120     if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket;
   5121     var dest = exp_dn.ptr;
   5122     const dend = dest + std.math.min(exp_dn.len, 254);
   5123     // detect reference loop using an iteration counter
   5124     var i: usize = 0;
   5125     while (i < msg.len) : (i += 2) {
   5126         // loop invariants: p<end, dest<dend
   5127         if ((p[0] & 0xc0) != 0) {
   5128             if (p + 1 == end) return error.InvalidDnsPacket;
   5129             var j = ((p[0] & @as(usize, 0x3f)) << 8) | p[1];
   5130             if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 2 - @ptrToInt(comp_dn.ptr);
   5131             if (j >= msg.len) return error.InvalidDnsPacket;
   5132             p = msg.ptr + j;
   5133         } else if (p[0] != 0) {
   5134             if (dest != exp_dn.ptr) {
   5135                 dest.* = '.';
   5136                 dest += 1;
   5137             }
   5138             var j = p[0];
   5139             p += 1;
   5140             if (j >= @ptrToInt(end) - @ptrToInt(p) or j >= @ptrToInt(dend) - @ptrToInt(dest)) {
   5141                 return error.InvalidDnsPacket;
   5142             }
   5143             while (j != 0) {
   5144                 j -= 1;
   5145                 dest.* = p[0];
   5146                 dest += 1;
   5147                 p += 1;
   5148             }
   5149         } else {
   5150             dest.* = 0;
   5151             if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 1 - @ptrToInt(comp_dn.ptr);
   5152             return len;
   5153         }
   5154     }
   5155     return error.InvalidDnsPacket;
   5156 }
   5157 
   5158 pub const SchedYieldError = error{
   5159     /// The system is not configured to allow yielding
   5160     SystemCannotYield,
   5161 };
   5162 
   5163 pub fn sched_yield() SchedYieldError!void {
   5164     if (builtin.os.tag == .windows) {
   5165         // The return value has to do with how many other threads there are; it is not
   5166         // an error condition on Windows.
   5167         _ = windows.kernel32.SwitchToThread();
   5168         return;
   5169     }
   5170     switch (errno(system.sched_yield())) {
   5171         0 => return,
   5172         ENOSYS => return error.SystemCannotYield,
   5173         else => return error.SystemCannotYield,
   5174     }
   5175 }
   5176 
   5177 pub const SetSockOptError = error{
   5178     /// The socket is already connected, and a specified option cannot be set while the socket is connected.
   5179     AlreadyConnected,
   5180 
   5181     /// The option is not supported by the protocol.
   5182     InvalidProtocolOption,
   5183 
   5184     /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure.
   5185     TimeoutTooBig,
   5186 
   5187     /// Insufficient resources are available in the system to complete the call.
   5188     SystemResources,
   5189 } || UnexpectedError;
   5190 
   5191 /// Set a socket's options.
   5192 pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
   5193     switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) {
   5194         0 => {},
   5195         EBADF => unreachable, // always a race condition
   5196         ENOTSOCK => unreachable, // always a race condition
   5197         EINVAL => unreachable,
   5198         EFAULT => unreachable,
   5199         EDOM => return error.TimeoutTooBig,
   5200         EISCONN => return error.AlreadyConnected,
   5201         ENOPROTOOPT => return error.InvalidProtocolOption,
   5202         ENOMEM => return error.SystemResources,
   5203         ENOBUFS => return error.SystemResources,
   5204         else => |err| return unexpectedErrno(err),
   5205     }
   5206 }
   5207 
   5208 pub const MemFdCreateError = error{
   5209     SystemFdQuotaExceeded,
   5210     ProcessFdQuotaExceeded,
   5211     OutOfMemory,
   5212 
   5213     /// memfd_create is available in Linux 3.17 and later. This error is returned
   5214     /// for older kernel versions.
   5215     SystemOutdated,
   5216 } || UnexpectedError;
   5217 
   5218 pub const memfd_createC = @compileError("deprecated: renamed to memfd_createZ");
   5219 
   5220 pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t {
   5221     // memfd_create is available only in glibc versions starting with 2.27.
   5222     const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;
   5223     const sys = if (use_c) std.c else linux;
   5224     const getErrno = if (use_c) std.c.getErrno else linux.getErrno;
   5225     const rc = sys.memfd_create(name, flags);
   5226     switch (getErrno(rc)) {
   5227         0 => return @intCast(fd_t, rc),
   5228         EFAULT => unreachable, // name has invalid memory
   5229         EINVAL => unreachable, // name/flags are faulty
   5230         ENFILE => return error.SystemFdQuotaExceeded,
   5231         EMFILE => return error.ProcessFdQuotaExceeded,
   5232         ENOMEM => return error.OutOfMemory,
   5233         ENOSYS => return error.SystemOutdated,
   5234         else => |err| return unexpectedErrno(err),
   5235     }
   5236 }
   5237 
   5238 pub const MFD_NAME_PREFIX = "memfd:";
   5239 pub const MFD_MAX_NAME_LEN = NAME_MAX - MFD_NAME_PREFIX.len;
   5240 fn toMemFdPath(name: []const u8) ![MFD_MAX_NAME_LEN:0]u8 {
   5241     var path_with_null: [MFD_MAX_NAME_LEN:0]u8 = undefined;
   5242     // >= rather than > to make room for the null byte
   5243     if (name.len >= MFD_MAX_NAME_LEN) return error.NameTooLong;
   5244     mem.copy(u8, &path_with_null, name);
   5245     path_with_null[name.len] = 0;
   5246     return path_with_null;
   5247 }
   5248 
   5249 pub fn memfd_create(name: []const u8, flags: u32) !fd_t {
   5250     const name_t = try toMemFdPath(name);
   5251     return memfd_createZ(&name_t, flags);
   5252 }
   5253 
   5254 pub fn getrusage(who: i32) rusage {
   5255     var result: rusage = undefined;
   5256     const rc = system.getrusage(who, &result);
   5257     switch (errno(rc)) {
   5258         0 => return result,
   5259         EINVAL => unreachable,
   5260         EFAULT => unreachable,
   5261         else => unreachable,
   5262     }
   5263 }
   5264 
   5265 pub const TermiosGetError = error{NotATerminal} || UnexpectedError;
   5266 
   5267 pub fn tcgetattr(handle: fd_t) TermiosGetError!termios {
   5268     while (true) {
   5269         var term: termios = undefined;
   5270         switch (errno(system.tcgetattr(handle, &term))) {
   5271             0 => return term,
   5272             EINTR => continue,
   5273             EBADF => unreachable,
   5274             ENOTTY => return error.NotATerminal,
   5275             else => |err| return unexpectedErrno(err),
   5276         }
   5277     }
   5278 }
   5279 
   5280 pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned};
   5281 
   5282 pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) TermiosSetError!void {
   5283     while (true) {
   5284         switch (errno(system.tcsetattr(handle, optional_action, &termios_p))) {
   5285             0 => return,
   5286             EBADF => unreachable,
   5287             EINTR => continue,
   5288             EINVAL => unreachable,
   5289             ENOTTY => return error.NotATerminal,
   5290             EIO => return error.ProcessOrphaned,
   5291             else => |err| return unexpectedErrno(err),
   5292         }
   5293     }
   5294 }
   5295 
   5296 const IoCtl_SIOCGIFINDEX_Error = error{
   5297     FileSystem,
   5298     InterfaceNotFound,
   5299 } || UnexpectedError;
   5300 
   5301 pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
   5302     while (true) {
   5303         switch (errno(system.ioctl(fd, SIOCGIFINDEX, @ptrToInt(ifr)))) {
   5304             0 => return,
   5305             EINVAL => unreachable, // Bad parameters.
   5306             ENOTTY => unreachable,
   5307             ENXIO => unreachable,
   5308             EBADF => unreachable, // Always a race condition.
   5309             EFAULT => unreachable, // Bad pointer parameter.
   5310             EINTR => continue,
   5311             EIO => return error.FileSystem,
   5312             ENODEV => return error.InterfaceNotFound,
   5313             else => |err| return unexpectedErrno(err),
   5314         }
   5315     }
   5316 }
   5317 
   5318 pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
   5319     const rc = system.signalfd(fd, mask, flags);
   5320     switch (errno(rc)) {
   5321         0 => return @intCast(fd_t, rc),
   5322         EBADF, EINVAL => unreachable,
   5323         ENFILE => return error.SystemFdQuotaExceeded,
   5324         ENOMEM => return error.SystemResources,
   5325         EMFILE => return error.ProcessResources,
   5326         ENODEV => return error.InodeMountFail,
   5327         ENOSYS => return error.SystemOutdated,
   5328         else => |err| return std.os.unexpectedErrno(err),
   5329     }
   5330 }