zig

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

blob 2f19ee2a (118862B) - Raw


      1 // This file contains thin wrappers around OS-specific APIs, with these
      2 // specific goals in mind:
      3 // * Convert "errno"-style error codes into Zig errors.
      4 // * When null-terminated byte buffers are required, provide APIs which accept
      5 //   slices as well as APIs which accept null-terminated byte buffers. Same goes
      6 //   for UTF-16LE encoding.
      7 // * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide
      8 //   cross platform abstracting.
      9 // * When there exists a corresponding libc function and linking libc, the libc
     10 //   implementation is used. Exceptions are made for known buggy areas of libc.
     11 //   On Linux libc can be side-stepped by using `std.os.linux` directly.
     12 // * For Windows, this file represents the API that libc would provide for
     13 //   Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`.
     14 // Note: The Zig standard library does not support POSIX thread cancellation, and
     15 // in general EINTR is handled by trying again.
     16 
     17 const std = @import("std.zig");
     18 const builtin = @import("builtin");
     19 const assert = std.debug.assert;
     20 const math = std.math;
     21 const mem = std.mem;
     22 const elf = std.elf;
     23 const dl = @import("dynamic_library.zig");
     24 const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;
     25 
     26 pub const darwin = @import("os/darwin.zig");
     27 pub const dragonfly = @import("os/dragonfly.zig");
     28 pub const freebsd = @import("os/freebsd.zig");
     29 pub const netbsd = @import("os/netbsd.zig");
     30 pub const linux = @import("os/linux.zig");
     31 pub const uefi = @import("os/uefi.zig");
     32 pub const wasi = @import("os/wasi.zig");
     33 pub const windows = @import("os/windows.zig");
     34 pub const zen = @import("os/zen.zig");
     35 
     36 comptime {
     37     assert(@import("std") == std); // std lib tests require --override-lib-dir
     38 }
     39 
     40 test "" {
     41     _ = darwin;
     42     _ = freebsd;
     43     _ = linux;
     44     _ = netbsd;
     45     _ = uefi;
     46     _ = wasi;
     47     _ = windows;
     48     _ = zen;
     49 
     50     _ = @import("os/test.zig");
     51 }
     52 
     53 /// When linking libc, this is the C API. Otherwise, it is the OS-specific system interface.
     54 pub const system = if (builtin.link_libc) std.c else switch (builtin.os) {
     55     .macosx, .ios, .watchos, .tvos => darwin,
     56     .freebsd => freebsd,
     57     .linux => linux,
     58     .netbsd => netbsd,
     59     .dragonfly => dragonfly,
     60     .wasi => wasi,
     61     .windows => windows,
     62     .zen => zen,
     63     else => struct {},
     64 };
     65 
     66 pub usingnamespace @import("os/bits.zig");
     67 
     68 /// See also `getenv`. Populated by startup code before main().
     69 pub var environ: [][*]u8 = undefined;
     70 
     71 /// Populated by startup code before main().
     72 /// Not available on Windows. See `std.process.args`
     73 /// for obtaining the process arguments.
     74 pub var argv: [][*]u8 = undefined;
     75 
     76 /// To obtain errno, call this function with the return value of the
     77 /// system function call. For some systems this will obtain the value directly
     78 /// from the return code; for others it will use a thread-local errno variable.
     79 /// Therefore, this function only returns a well-defined value when it is called
     80 /// directly after the system function call which one wants to learn the errno
     81 /// value of.
     82 pub const errno = system.getErrno;
     83 
     84 /// Closes the file descriptor.
     85 /// This function is not capable of returning any indication of failure. An
     86 /// application which wants to ensure writes have succeeded before closing
     87 /// must call `fsync` before `close`.
     88 /// Note: The Zig standard library does not support POSIX thread cancellation.
     89 pub fn close(fd: fd_t) void {
     90     if (builtin.os == .windows) {
     91         return windows.CloseHandle(fd);
     92     }
     93     if (builtin.os == .wasi) {
     94         _ = wasi.fd_close(fd);
     95     }
     96     if (comptime std.Target.current.isDarwin()) {
     97         // This avoids the EINTR problem.
     98         switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) {
     99             EBADF => unreachable, // Always a race condition.
    100             else => return,
    101         }
    102     }
    103     switch (errno(system.close(fd))) {
    104         EBADF => unreachable, // Always a race condition.
    105         EINTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
    106         else => return,
    107     }
    108 }
    109 
    110 pub const GetRandomError = OpenError;
    111 
    112 /// Obtain a series of random bytes. These bytes can be used to seed user-space
    113 /// random number generators or for cryptographic purposes.
    114 /// When linking against libc, this calls the
    115 /// appropriate OS-specific library call. Otherwise it uses the zig standard
    116 /// library implementation.
    117 pub fn getrandom(buffer: []u8) GetRandomError!void {
    118     if (builtin.os == .windows) {
    119         return windows.RtlGenRandom(buffer);
    120     }
    121     if (builtin.os == .linux or builtin.os == .freebsd) {
    122         var buf = buffer;
    123         const use_c = builtin.os != .linux or
    124             std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok;
    125 
    126         while (buf.len != 0) {
    127             var err: u16 = undefined;
    128 
    129             const num_read = if (use_c) blk: {
    130                 const rc = std.c.getrandom(buf.ptr, buf.len, 0);
    131                 err = std.c.getErrno(rc);
    132                 break :blk @bitCast(usize, rc);
    133             } else blk: {
    134                 const rc = linux.getrandom(buf.ptr, buf.len, 0);
    135                 err = linux.getErrno(rc);
    136                 break :blk rc;
    137             };
    138 
    139             switch (err) {
    140                 0 => buf = buf[num_read..],
    141                 EINVAL => unreachable,
    142                 EFAULT => unreachable,
    143                 EINTR => continue,
    144                 ENOSYS => return getRandomBytesDevURandom(buf),
    145                 else => return unexpectedErrno(err),
    146             }
    147         }
    148         return;
    149     }
    150     if (builtin.os == .wasi) {
    151         switch (wasi.random_get(buffer.ptr, buffer.len)) {
    152             0 => return,
    153             else => |err| return unexpectedErrno(err),
    154         }
    155     }
    156     return getRandomBytesDevURandom(buffer);
    157 }
    158 
    159 fn getRandomBytesDevURandom(buf: []u8) !void {
    160     const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
    161     defer close(fd);
    162 
    163     const st = try fstat(fd);
    164     if (!S_ISCHR(st.mode)) {
    165         return error.NoDevice;
    166     }
    167 
    168     const stream = &std.fs.File.openHandle(fd).inStream().stream;
    169     stream.readNoEof(buf) catch return error.Unexpected;
    170 }
    171 
    172 /// Causes abnormal process termination.
    173 /// If linking against libc, this calls the abort() libc function. Otherwise
    174 /// it raises SIGABRT followed by SIGKILL and finally lo
    175 pub fn abort() noreturn {
    176     @setCold(true);
    177     // MSVCRT abort() sometimes opens a popup window which is undesirable, so
    178     // even when linking libc on Windows we use our own abort implementation.
    179     // See https://github.com/ziglang/zig/issues/2071 for more details.
    180     if (builtin.os == .windows) {
    181         if (builtin.mode == .Debug) {
    182             @breakpoint();
    183         }
    184         windows.kernel32.ExitProcess(3);
    185     }
    186     if (builtin.link_libc) {
    187         system.abort();
    188     }
    189     if (builtin.os == .uefi) {
    190         exit(0); // TODO choose appropriate exit code
    191     }
    192 
    193     raise(SIGABRT) catch {};
    194 
    195     // TODO the rest of the implementation of abort() from musl libc here
    196 
    197     raise(SIGKILL) catch {};
    198     exit(127);
    199 }
    200 
    201 pub const RaiseError = UnexpectedError;
    202 
    203 pub fn raise(sig: u8) RaiseError!void {
    204     if (builtin.link_libc) {
    205         switch (errno(system.raise(sig))) {
    206             0 => return,
    207             else => |err| return unexpectedErrno(err),
    208         }
    209     }
    210 
    211     if (builtin.os == .wasi) {
    212         switch (wasi.proc_raise(SIGABRT)) {
    213             0 => return,
    214             else => |err| return unexpectedErrno(err),
    215         }
    216     }
    217 
    218     if (builtin.os == .linux) {
    219         var set: linux.sigset_t = undefined;
    220         linux.blockAppSignals(&set);
    221         const tid = linux.syscall0(linux.SYS_gettid);
    222         const rc = linux.syscall2(linux.SYS_tkill, tid, sig);
    223         linux.restoreSignals(&set);
    224         switch (errno(rc)) {
    225             0 => return,
    226             else => |err| return unexpectedErrno(err),
    227         }
    228     }
    229 
    230     @compileError("std.os.raise unimplemented for this target");
    231 }
    232 
    233 pub const KillError = error{PermissionDenied} || UnexpectedError;
    234 
    235 pub fn kill(pid: pid_t, sig: u8) KillError!void {
    236     switch (errno(system.kill(pid, sig))) {
    237         0 => return,
    238         EINVAL => unreachable, // invalid signal
    239         EPERM => return error.PermissionDenied,
    240         ESRCH => unreachable, // always a race condition
    241         else => |err| return unexpectedErrno(err),
    242     }
    243 }
    244 
    245 /// Exits the program cleanly with the specified status code.
    246 pub fn exit(status: u8) noreturn {
    247     if (builtin.link_libc) {
    248         system.exit(status);
    249     }
    250     if (builtin.os == .windows) {
    251         windows.kernel32.ExitProcess(status);
    252     }
    253     if (builtin.os == .wasi) {
    254         wasi.proc_exit(status);
    255     }
    256     if (builtin.os == .linux and !builtin.single_threaded) {
    257         linux.exit_group(status);
    258     }
    259     if (builtin.os == .uefi) {
    260         // exit() is only avaliable if exitBootServices() has not been called yet.
    261         // This call to exit should not fail, so we don't care about its return value.
    262         if (uefi.system_table.boot_services) |bs| {
    263             _ = bs.exit(uefi.handle, status, 0, null);
    264         }
    265         // If we can't exit, reboot the system instead.
    266         uefi.system_table.runtime_services.resetSystem(uefi.tables.ResetType.ResetCold, status, 0, null);
    267     }
    268     system.exit(status);
    269 }
    270 
    271 pub const ReadError = error{
    272     InputOutput,
    273     SystemResources,
    274     IsDir,
    275     OperationAborted,
    276     BrokenPipe,
    277 
    278     /// This error occurs when no global event loop is configured,
    279     /// and reading from the file descriptor would block.
    280     WouldBlock,
    281 } || UnexpectedError;
    282 
    283 /// Returns the number of bytes that were read, which can be less than
    284 /// buf.len. If 0 bytes were read, that means EOF.
    285 /// If the application has a global event loop enabled, EAGAIN is handled
    286 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
    287 pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
    288     if (builtin.os == .windows) {
    289         return windows.ReadFile(fd, buf);
    290     }
    291 
    292     if (builtin.os == .wasi and !builtin.link_libc) {
    293         const iovs = [1]iovec{iovec{
    294             .iov_base = buf.ptr,
    295             .iov_len = buf.len,
    296         }};
    297 
    298         var nread: usize = undefined;
    299         switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
    300             0 => return nread,
    301             else => |err| return unexpectedErrno(err),
    302         }
    303     }
    304 
    305     while (true) {
    306         const rc = system.read(fd, buf.ptr, buf.len);
    307         switch (errno(rc)) {
    308             0 => return @intCast(usize, rc),
    309             EINTR => continue,
    310             EINVAL => unreachable,
    311             EFAULT => unreachable,
    312             EAGAIN => if (std.event.Loop.instance) |loop| {
    313                 loop.waitUntilFdReadable(fd);
    314                 continue;
    315             } else {
    316                 return error.WouldBlock;
    317             },
    318             EBADF => unreachable, // Always a race condition.
    319             EIO => return error.InputOutput,
    320             EISDIR => return error.IsDir,
    321             ENOBUFS => return error.SystemResources,
    322             ENOMEM => return error.SystemResources,
    323             else => |err| return unexpectedErrno(err),
    324         }
    325     }
    326     return index;
    327 }
    328 
    329 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
    330 /// If the application has a global event loop enabled, EAGAIN is handled
    331 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
    332 pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
    333     while (true) {
    334         // TODO handle the case when iov_len is too large and get rid of this @intCast
    335         const rc = system.readv(fd, iov.ptr, @intCast(u32, iov.len));
    336         switch (errno(rc)) {
    337             0 => return @bitCast(usize, rc),
    338             EINTR => continue,
    339             EINVAL => unreachable,
    340             EFAULT => unreachable,
    341             EAGAIN => if (std.event.Loop.instance) |loop| {
    342                 loop.waitUntilFdReadable(fd);
    343                 continue;
    344             } else {
    345                 return error.WouldBlock;
    346             },
    347             EBADF => unreachable, // always a race condition
    348             EIO => return error.InputOutput,
    349             EISDIR => return error.IsDir,
    350             ENOBUFS => return error.SystemResources,
    351             ENOMEM => return error.SystemResources,
    352             else => |err| return unexpectedErrno(err),
    353         }
    354     }
    355 }
    356 
    357 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned.
    358 /// If the application has a global event loop enabled, EAGAIN is handled
    359 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
    360 pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) ReadError!usize {
    361     if (comptime std.Target.current.isDarwin()) {
    362         // Darwin does not have preadv but it does have pread.
    363         var off: usize = 0;
    364         var iov_i: usize = 0;
    365         var inner_off: usize = 0;
    366         while (true) {
    367             const v = iov[iov_i];
    368             const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
    369             const err = darwin.getErrno(rc);
    370             switch (err) {
    371                 0 => {
    372                     const amt_read = @bitCast(usize, rc);
    373                     off += amt_read;
    374                     inner_off += amt_read;
    375                     if (inner_off == v.iov_len) {
    376                         iov_i += 1;
    377                         inner_off = 0;
    378                         if (iov_i == iov.len) {
    379                             return off;
    380                         }
    381                     }
    382                     if (rc == 0) return off; // EOF
    383                     continue;
    384                 },
    385                 EINTR => continue,
    386                 EINVAL => unreachable,
    387                 EFAULT => unreachable,
    388                 ESPIPE => unreachable, // fd is not seekable
    389                 EAGAIN => if (std.event.Loop.instance) |loop| {
    390                     loop.waitUntilFdReadable(fd);
    391                     continue;
    392                 } else {
    393                     return error.WouldBlock;
    394                 },
    395                 EBADF => unreachable, // always a race condition
    396                 EIO => return error.InputOutput,
    397                 EISDIR => return error.IsDir,
    398                 ENOBUFS => return error.SystemResources,
    399                 ENOMEM => return error.SystemResources,
    400                 else => return unexpectedErrno(err),
    401             }
    402         }
    403     }
    404     while (true) {
    405         // TODO handle the case when iov_len is too large and get rid of this @intCast
    406         const rc = system.preadv(fd, iov.ptr, @intCast(u32, iov.len), offset);
    407         switch (errno(rc)) {
    408             0 => return @bitCast(usize, rc),
    409             EINTR => continue,
    410             EINVAL => unreachable,
    411             EFAULT => unreachable,
    412             EAGAIN => if (std.event.Loop.instance) |loop| {
    413                 loop.waitUntilFdReadable(fd);
    414                 continue;
    415             } else {
    416                 return error.WouldBlock;
    417             },
    418             EBADF => unreachable, // always a race condition
    419             EIO => return error.InputOutput,
    420             EISDIR => return error.IsDir,
    421             ENOBUFS => return error.SystemResources,
    422             ENOMEM => return error.SystemResources,
    423             else => |err| return unexpectedErrno(err),
    424         }
    425     }
    426 }
    427 
    428 pub const WriteError = error{
    429     DiskQuota,
    430     FileTooBig,
    431     InputOutput,
    432     NoSpaceLeft,
    433     AccessDenied,
    434     BrokenPipe,
    435     SystemResources,
    436     OperationAborted,
    437 
    438     /// This error occurs when no global event loop is configured,
    439     /// and reading from the file descriptor would block.
    440     WouldBlock,
    441 } || UnexpectedError;
    442 
    443 /// Write to a file descriptor. Keeps trying if it gets interrupted.
    444 /// If the application has a global event loop enabled, EAGAIN is handled
    445 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
    446 /// TODO evented I/O integration is disabled until
    447 /// https://github.com/ziglang/zig/issues/3557 is solved.
    448 pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
    449     if (builtin.os == .windows) {
    450         return windows.WriteFile(fd, bytes);
    451     }
    452 
    453     if (builtin.os == .wasi and !builtin.link_libc) {
    454         const ciovs = [1]iovec_const{iovec_const{
    455             .iov_base = bytes.ptr,
    456             .iov_len = bytes.len,
    457         }};
    458         var nwritten: usize = undefined;
    459         switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
    460             0 => return,
    461             else => |err| return unexpectedErrno(err),
    462         }
    463     }
    464 
    465     // Linux can return EINVAL when write amount is > 0x7ffff000
    466     // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856
    467     // TODO audit this. Shawn Landden says that this is not actually true.
    468     // if this logic should stay, move it to std.os.linux
    469     const max_bytes_len = 0x7ffff000;
    470 
    471     var index: usize = 0;
    472     while (index < bytes.len) {
    473         const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len));
    474         const rc = system.write(fd, bytes.ptr + index, amt_to_write);
    475         switch (errno(rc)) {
    476             0 => {
    477                 index += @intCast(usize, rc);
    478                 continue;
    479             },
    480             EINTR => continue,
    481             EINVAL => unreachable,
    482             EFAULT => unreachable,
    483             // TODO https://github.com/ziglang/zig/issues/3557
    484             EAGAIN => return error.WouldBlock,
    485             //EAGAIN => if (std.event.Loop.instance) |loop| {
    486             //    loop.waitUntilFdWritable(fd);
    487             //    continue;
    488             //} else {
    489             //    return error.WouldBlock;
    490             //},
    491             EBADF => unreachable, // Always a race condition.
    492             EDESTADDRREQ => unreachable, // `connect` was never called.
    493             EDQUOT => return error.DiskQuota,
    494             EFBIG => return error.FileTooBig,
    495             EIO => return error.InputOutput,
    496             ENOSPC => return error.NoSpaceLeft,
    497             EPERM => return error.AccessDenied,
    498             EPIPE => return error.BrokenPipe,
    499             else => |err| return unexpectedErrno(err),
    500         }
    501     }
    502 }
    503 
    504 /// Write multiple buffers to a file descriptor.
    505 /// If the application has a global event loop enabled, EAGAIN is handled
    506 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
    507 pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
    508     while (true) {
    509         // TODO handle the case when iov_len is too large and get rid of this @intCast
    510         const rc = system.writev(fd, iov.ptr, @intCast(u32, iov.len));
    511         switch (errno(rc)) {
    512             0 => return,
    513             EINTR => continue,
    514             EINVAL => unreachable,
    515             EFAULT => unreachable,
    516             EAGAIN => if (std.event.Loop.instance) |loop| {
    517                 loop.waitUntilFdWritable(fd);
    518                 continue;
    519             } else {
    520                 return error.WouldBlock;
    521             },
    522             EBADF => unreachable, // Always a race condition.
    523             EDESTADDRREQ => unreachable, // `connect` was never called.
    524             EDQUOT => return error.DiskQuota,
    525             EFBIG => return error.FileTooBig,
    526             EIO => return error.InputOutput,
    527             ENOSPC => return error.NoSpaceLeft,
    528             EPERM => return error.AccessDenied,
    529             EPIPE => return error.BrokenPipe,
    530             else => |err| return unexpectedErrno(err),
    531         }
    532     }
    533 }
    534 
    535 /// Write multiple buffers to a file descriptor, with a position offset.
    536 /// Keeps trying if it gets interrupted.
    537 pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void {
    538     if (comptime std.Target.current.isDarwin()) {
    539         // Darwin does not have pwritev but it does have pwrite.
    540         var off: usize = 0;
    541         var iov_i: usize = 0;
    542         var inner_off: usize = 0;
    543         while (true) {
    544             const v = iov[iov_i];
    545             const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off);
    546             const err = darwin.getErrno(rc);
    547             switch (err) {
    548                 0 => {
    549                     const amt_written = @bitCast(usize, rc);
    550                     off += amt_written;
    551                     inner_off += amt_written;
    552                     if (inner_off == v.iov_len) {
    553                         iov_i += 1;
    554                         inner_off = 0;
    555                         if (iov_i == iov.len) {
    556                             return;
    557                         }
    558                     }
    559                     continue;
    560                 },
    561                 EINTR => continue,
    562                 ESPIPE => unreachable, // `fd` is not seekable.
    563                 EINVAL => unreachable,
    564                 EFAULT => unreachable,
    565                 EAGAIN => if (std.event.Loop.instance) |loop| {
    566                     loop.waitUntilFdWritable(fd);
    567                     continue;
    568                 } else {
    569                     return error.WouldBlock;
    570                 },
    571                 EBADF => unreachable, // Always a race condition.
    572                 EDESTADDRREQ => unreachable, // `connect` was never called.
    573                 EDQUOT => return error.DiskQuota,
    574                 EFBIG => return error.FileTooBig,
    575                 EIO => return error.InputOutput,
    576                 ENOSPC => return error.NoSpaceLeft,
    577                 EPERM => return error.AccessDenied,
    578                 EPIPE => return error.BrokenPipe,
    579                 else => return unexpectedErrno(err),
    580             }
    581         }
    582     }
    583 
    584     while (true) {
    585         // TODO handle the case when iov_len is too large and get rid of this @intCast
    586         const rc = system.pwritev(fd, iov.ptr, @intCast(u32, iov.len), offset);
    587         switch (errno(rc)) {
    588             0 => return,
    589             EINTR => continue,
    590             EINVAL => unreachable,
    591             EFAULT => unreachable,
    592             EAGAIN => if (std.event.Loop.instance) |loop| {
    593                 loop.waitUntilFdWritable(fd);
    594                 continue;
    595             } else {
    596                 return error.WouldBlock;
    597             },
    598             EBADF => unreachable, // Always a race condition.
    599             EDESTADDRREQ => unreachable, // `connect` was never called.
    600             EDQUOT => return error.DiskQuota,
    601             EFBIG => return error.FileTooBig,
    602             EIO => return error.InputOutput,
    603             ENOSPC => return error.NoSpaceLeft,
    604             EPERM => return error.AccessDenied,
    605             EPIPE => return error.BrokenPipe,
    606             else => |err| return unexpectedErrno(err),
    607         }
    608     }
    609 }
    610 
    611 pub const OpenError = error{
    612     AccessDenied,
    613     SymLinkLoop,
    614     ProcessFdQuotaExceeded,
    615     SystemFdQuotaExceeded,
    616     NoDevice,
    617     FileNotFound,
    618 
    619     /// The path exceeded `MAX_PATH_BYTES` bytes.
    620     NameTooLong,
    621 
    622     /// Insufficient kernel memory was available, or
    623     /// the named file is a FIFO and per-user hard limit on
    624     /// memory allocation for pipes has been reached.
    625     SystemResources,
    626 
    627     /// The file is too large to be opened. This error is unreachable
    628     /// for 64-bit targets, as well as when opening directories.
    629     FileTooBig,
    630 
    631     /// The path refers to directory but the `O_DIRECTORY` flag was not provided.
    632     IsDir,
    633 
    634     /// A new path cannot be created because the device has no room for the new file.
    635     /// This error is only reachable when the `O_CREAT` flag is provided.
    636     NoSpaceLeft,
    637 
    638     /// A component used as a directory in the path was not, in fact, a directory, or
    639     /// `O_DIRECTORY` was specified and the path was not a directory.
    640     NotDir,
    641 
    642     /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided.
    643     PathAlreadyExists,
    644     DeviceBusy,
    645 } || UnexpectedError;
    646 
    647 /// Open and possibly create a file. Keeps trying if it gets interrupted.
    648 /// See also `openC`.
    649 pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
    650     const file_path_c = try toPosixPath(file_path);
    651     return openC(&file_path_c, flags, perm);
    652 }
    653 
    654 /// Open and possibly create a file. Keeps trying if it gets interrupted.
    655 /// See also `open`.
    656 /// TODO https://github.com/ziglang/zig/issues/265
    657 pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t {
    658     while (true) {
    659         const rc = system.open(file_path, flags, perm);
    660         switch (errno(rc)) {
    661             0 => return @intCast(fd_t, rc),
    662             EINTR => continue,
    663 
    664             EFAULT => unreachable,
    665             EINVAL => unreachable,
    666             EACCES => return error.AccessDenied,
    667             EFBIG => return error.FileTooBig,
    668             EOVERFLOW => return error.FileTooBig,
    669             EISDIR => return error.IsDir,
    670             ELOOP => return error.SymLinkLoop,
    671             EMFILE => return error.ProcessFdQuotaExceeded,
    672             ENAMETOOLONG => return error.NameTooLong,
    673             ENFILE => return error.SystemFdQuotaExceeded,
    674             ENODEV => return error.NoDevice,
    675             ENOENT => return error.FileNotFound,
    676             ENOMEM => return error.SystemResources,
    677             ENOSPC => return error.NoSpaceLeft,
    678             ENOTDIR => return error.NotDir,
    679             EPERM => return error.AccessDenied,
    680             EEXIST => return error.PathAlreadyExists,
    681             EBUSY => return error.DeviceBusy,
    682             else => |err| return unexpectedErrno(err),
    683         }
    684     }
    685 }
    686 
    687 /// Open and possibly create a file. Keeps trying if it gets interrupted.
    688 /// `file_path` is relative to the open directory handle `dir_fd`.
    689 /// See also `openatC`.
    690 pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: usize) OpenError!fd_t {
    691     const file_path_c = try toPosixPath(file_path);
    692     return openatC(dir_fd, &file_path_c, flags, mode);
    693 }
    694 
    695 /// Open and possibly create a file. Keeps trying if it gets interrupted.
    696 /// `file_path` is relative to the open directory handle `dir_fd`.
    697 /// See also `openat`.
    698 pub fn openatC(dir_fd: fd_t, file_path: [*]const u8, flags: u32, mode: usize) OpenError!fd_t {
    699     while (true) {
    700         const rc = system.openat(dir_fd, file_path, flags, mode);
    701         switch (errno(rc)) {
    702             0 => return @intCast(fd_t, rc),
    703             EINTR => continue,
    704 
    705             EFAULT => unreachable,
    706             EINVAL => unreachable,
    707             EACCES => return error.AccessDenied,
    708             EFBIG => return error.FileTooBig,
    709             EOVERFLOW => return error.FileTooBig,
    710             EISDIR => return error.IsDir,
    711             ELOOP => return error.SymLinkLoop,
    712             EMFILE => return error.ProcessFdQuotaExceeded,
    713             ENAMETOOLONG => return error.NameTooLong,
    714             ENFILE => return error.SystemFdQuotaExceeded,
    715             ENODEV => return error.NoDevice,
    716             ENOENT => return error.FileNotFound,
    717             ENOMEM => return error.SystemResources,
    718             ENOSPC => return error.NoSpaceLeft,
    719             ENOTDIR => return error.NotDir,
    720             EPERM => return error.AccessDenied,
    721             EEXIST => return error.PathAlreadyExists,
    722             EBUSY => return error.DeviceBusy,
    723             else => |err| return unexpectedErrno(err),
    724         }
    725     }
    726 }
    727 
    728 pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
    729     while (true) {
    730         switch (errno(system.dup2(old_fd, new_fd))) {
    731             0 => return,
    732             EBUSY, EINTR => continue,
    733             EMFILE => return error.ProcessFdQuotaExceeded,
    734             EINVAL => unreachable, // invalid parameters passed to dup2
    735             EBADF => unreachable, // always a race condition
    736             else => |err| return unexpectedErrno(err),
    737         }
    738     }
    739 }
    740 
    741 pub const ExecveError = error{
    742     SystemResources,
    743     AccessDenied,
    744     InvalidExe,
    745     FileSystem,
    746     IsDir,
    747     FileNotFound,
    748     NotDir,
    749     FileBusy,
    750     ProcessFdQuotaExceeded,
    751     SystemFdQuotaExceeded,
    752     NameTooLong,
    753 } || UnexpectedError;
    754 
    755 /// Like `execve` except the parameters are null-terminated,
    756 /// matching the syscall API on all targets. This removes the need for an allocator.
    757 /// This function ignores PATH environment variable. See `execvpeC` for that.
    758 pub fn execveC(path: [*]const u8, child_argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) ExecveError {
    759     switch (errno(system.execve(path, child_argv, envp))) {
    760         0 => unreachable,
    761         EFAULT => unreachable,
    762         E2BIG => return error.SystemResources,
    763         EMFILE => return error.ProcessFdQuotaExceeded,
    764         ENAMETOOLONG => return error.NameTooLong,
    765         ENFILE => return error.SystemFdQuotaExceeded,
    766         ENOMEM => return error.SystemResources,
    767         EACCES => return error.AccessDenied,
    768         EPERM => return error.AccessDenied,
    769         EINVAL => return error.InvalidExe,
    770         ENOEXEC => return error.InvalidExe,
    771         EIO => return error.FileSystem,
    772         ELOOP => return error.FileSystem,
    773         EISDIR => return error.IsDir,
    774         ENOENT => return error.FileNotFound,
    775         ENOTDIR => return error.NotDir,
    776         ETXTBSY => return error.FileBusy,
    777         else => |err| return unexpectedErrno(err),
    778     }
    779 }
    780 
    781 /// Like `execvpe` except the parameters are null-terminated,
    782 /// matching the syscall API on all targets. This removes the need for an allocator.
    783 /// This function also uses the PATH environment variable to get the full path to the executable.
    784 /// If `file` is an absolute path, this is the same as `execveC`.
    785 pub fn execvpeC(file: [*]const u8, child_argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) ExecveError {
    786     const file_slice = mem.toSliceConst(u8, file);
    787     if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveC(file, child_argv, envp);
    788 
    789     const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
    790     var path_buf: [MAX_PATH_BYTES]u8 = undefined;
    791     var it = mem.tokenize(PATH, ":");
    792     var seen_eacces = false;
    793     var err: ExecveError = undefined;
    794     while (it.next()) |search_path| {
    795         if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong;
    796         mem.copy(u8, &path_buf, search_path);
    797         path_buf[search_path.len] = '/';
    798         mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);
    799         path_buf[search_path.len + file_slice.len + 1] = 0;
    800         err = execveC(&path_buf, child_argv, envp);
    801         switch (err) {
    802             error.AccessDenied => seen_eacces = true,
    803             error.FileNotFound, error.NotDir => {},
    804             else => |e| return e,
    805         }
    806     }
    807     if (seen_eacces) return error.AccessDenied;
    808     return err;
    809 }
    810 
    811 /// This function must allocate memory to add a null terminating bytes on path and each arg.
    812 /// It must also convert to KEY=VALUE\0 format for environment variables, and include null
    813 /// pointers after the args and after the environment variables.
    814 /// `argv_slice[0]` is the executable path.
    815 /// This function also uses the PATH environment variable to get the full path to the executable.
    816 pub fn execvpe(
    817     allocator: *mem.Allocator,
    818     argv_slice: []const []const u8,
    819     env_map: *const std.BufMap,
    820 ) (ExecveError || error{OutOfMemory}) {
    821     const argv_buf = try allocator.alloc(?[*]u8, argv_slice.len + 1);
    822     mem.set(?[*]u8, argv_buf, null);
    823     defer {
    824         for (argv_buf) |arg| {
    825             const arg_buf = if (arg) |ptr| mem.toSlice(u8, ptr) else break;
    826             allocator.free(arg_buf);
    827         }
    828         allocator.free(argv_buf);
    829     }
    830     for (argv_slice) |arg, i| {
    831         const arg_buf = try allocator.alloc(u8, arg.len + 1);
    832         @memcpy(arg_buf.ptr, arg.ptr, arg.len);
    833         arg_buf[arg.len] = 0;
    834 
    835         argv_buf[i] = arg_buf.ptr;
    836     }
    837     argv_buf[argv_slice.len] = null;
    838 
    839     const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
    840     defer freeNullDelimitedEnvMap(allocator, envp_buf);
    841 
    842     return execvpeC(argv_buf.ptr[0].?, argv_buf.ptr, envp_buf.ptr);
    843 }
    844 
    845 pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![]?[*]u8 {
    846     const envp_count = env_map.count();
    847     const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1);
    848     mem.set(?[*]u8, envp_buf, null);
    849     errdefer freeNullDelimitedEnvMap(allocator, envp_buf);
    850     {
    851         var it = env_map.iterator();
    852         var i: usize = 0;
    853         while (it.next()) |pair| : (i += 1) {
    854             const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2);
    855             @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len);
    856             env_buf[pair.key.len] = '=';
    857             @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);
    858             env_buf[env_buf.len - 1] = 0;
    859 
    860             envp_buf[i] = env_buf.ptr;
    861         }
    862         assert(i == envp_count);
    863     }
    864     assert(envp_buf[envp_count] == null);
    865     return envp_buf;
    866 }
    867 
    868 pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*]u8) void {
    869     for (envp_buf) |env| {
    870         const env_buf = if (env) |ptr| ptr[0 .. mem.len(u8, ptr) + 1] else break;
    871         allocator.free(env_buf);
    872     }
    873     allocator.free(envp_buf);
    874 }
    875 
    876 /// Get an environment variable.
    877 /// See also `getenvC`.
    878 /// TODO make this go through libc when we have it
    879 pub fn getenv(key: []const u8) ?[]const u8 {
    880     for (environ) |ptr| {
    881         var line_i: usize = 0;
    882         while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
    883         const this_key = ptr[0..line_i];
    884         if (!mem.eql(u8, key, this_key)) continue;
    885 
    886         var end_i: usize = line_i;
    887         while (ptr[end_i] != 0) : (end_i += 1) {}
    888         const this_value = ptr[line_i + 1 .. end_i];
    889 
    890         return this_value;
    891     }
    892     return null;
    893 }
    894 
    895 /// Get an environment variable with a null-terminated name.
    896 /// See also `getenv`.
    897 /// TODO https://github.com/ziglang/zig/issues/265
    898 pub fn getenvC(key: [*]const u8) ?[]const u8 {
    899     if (builtin.link_libc) {
    900         const value = system.getenv(key) orelse return null;
    901         return mem.toSliceConst(u8, value);
    902     }
    903     return getenv(mem.toSliceConst(u8, key));
    904 }
    905 
    906 pub const GetCwdError = error{
    907     NameTooLong,
    908     CurrentWorkingDirectoryUnlinked,
    909 } || UnexpectedError;
    910 
    911 /// The result is a slice of out_buffer, indexed from 0.
    912 pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
    913     if (builtin.os == .windows) {
    914         return windows.GetCurrentDirectory(out_buffer);
    915     }
    916 
    917     const err = if (builtin.link_libc) blk: {
    918         break :blk if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;
    919     } else blk: {
    920         break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len));
    921     };
    922     switch (err) {
    923         0 => return mem.toSlice(u8, out_buffer.ptr),
    924         EFAULT => unreachable,
    925         EINVAL => unreachable,
    926         ENOENT => return error.CurrentWorkingDirectoryUnlinked,
    927         ERANGE => return error.NameTooLong,
    928         else => return unexpectedErrno(@intCast(usize, err)),
    929     }
    930 }
    931 
    932 pub const SymLinkError = error{
    933     AccessDenied,
    934     DiskQuota,
    935     PathAlreadyExists,
    936     FileSystem,
    937     SymLinkLoop,
    938     FileNotFound,
    939     SystemResources,
    940     NoSpaceLeft,
    941     ReadOnlyFileSystem,
    942     NotDir,
    943     NameTooLong,
    944     InvalidUtf8,
    945     BadPathName,
    946 } || UnexpectedError;
    947 
    948 /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
    949 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
    950 /// one; the latter case is known as a dangling link.
    951 /// If `sym_link_path` exists, it will not be overwritten.
    952 /// See also `symlinkC` and `symlinkW`.
    953 pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
    954     if (builtin.os == .windows) {
    955         const target_path_w = try windows.sliceToPrefixedFileW(target_path);
    956         const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
    957         return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0);
    958     } else {
    959         const target_path_c = try toPosixPath(target_path);
    960         const sym_link_path_c = try toPosixPath(sym_link_path);
    961         return symlinkC(&target_path_c, &sym_link_path_c);
    962     }
    963 }
    964 
    965 /// This is the same as `symlink` except the parameters are null-terminated pointers.
    966 /// See also `symlink`.
    967 pub fn symlinkC(target_path: [*]const u8, sym_link_path: [*]const u8) SymLinkError!void {
    968     if (builtin.os == .windows) {
    969         const target_path_w = try windows.cStrToPrefixedFileW(target_path);
    970         const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
    971         return windows.CreateSymbolicLinkW(&sym_link_path_w, &target_path_w, 0);
    972     }
    973     switch (errno(system.symlink(target_path, sym_link_path))) {
    974         0 => return,
    975         EFAULT => unreachable,
    976         EINVAL => unreachable,
    977         EACCES => return error.AccessDenied,
    978         EPERM => return error.AccessDenied,
    979         EDQUOT => return error.DiskQuota,
    980         EEXIST => return error.PathAlreadyExists,
    981         EIO => return error.FileSystem,
    982         ELOOP => return error.SymLinkLoop,
    983         ENAMETOOLONG => return error.NameTooLong,
    984         ENOENT => return error.FileNotFound,
    985         ENOTDIR => return error.NotDir,
    986         ENOMEM => return error.SystemResources,
    987         ENOSPC => return error.NoSpaceLeft,
    988         EROFS => return error.ReadOnlyFileSystem,
    989         else => |err| return unexpectedErrno(err),
    990     }
    991 }
    992 
    993 pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
    994     const target_path_c = try toPosixPath(target_path);
    995     const sym_link_path_c = try toPosixPath(sym_link_path);
    996     return symlinkatC(target_path_c, newdirfd, sym_link_path_c);
    997 }
    998 
    999 pub fn symlinkatC(target_path: [*]const u8, newdirfd: fd_t, sym_link_path: [*]const u8) SymLinkError!void {
   1000     switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
   1001         0 => return,
   1002         EFAULT => unreachable,
   1003         EINVAL => unreachable,
   1004         EACCES => return error.AccessDenied,
   1005         EPERM => return error.AccessDenied,
   1006         EDQUOT => return error.DiskQuota,
   1007         EEXIST => return error.PathAlreadyExists,
   1008         EIO => return error.FileSystem,
   1009         ELOOP => return error.SymLinkLoop,
   1010         ENAMETOOLONG => return error.NameTooLong,
   1011         ENOENT => return error.FileNotFound,
   1012         ENOTDIR => return error.NotDir,
   1013         ENOMEM => return error.SystemResources,
   1014         ENOSPC => return error.NoSpaceLeft,
   1015         EROFS => return error.ReadOnlyFileSystem,
   1016         else => |err| return unexpectedErrno(err),
   1017     }
   1018 }
   1019 
   1020 pub const UnlinkError = error{
   1021     FileNotFound,
   1022     AccessDenied,
   1023     FileBusy,
   1024     FileSystem,
   1025     IsDir,
   1026     SymLinkLoop,
   1027     NameTooLong,
   1028     NotDir,
   1029     SystemResources,
   1030     ReadOnlyFileSystem,
   1031 
   1032     /// On Windows, file paths must be valid Unicode.
   1033     InvalidUtf8,
   1034 
   1035     /// On Windows, file paths cannot contain these characters:
   1036     /// '/', '*', '?', '"', '<', '>', '|'
   1037     BadPathName,
   1038 } || UnexpectedError;
   1039 
   1040 /// Delete a name and possibly the file it refers to.
   1041 /// See also `unlinkC`.
   1042 pub fn unlink(file_path: []const u8) UnlinkError!void {
   1043     if (builtin.os == .windows) {
   1044         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   1045         return windows.DeleteFileW(&file_path_w);
   1046     } else {
   1047         const file_path_c = try toPosixPath(file_path);
   1048         return unlinkC(&file_path_c);
   1049     }
   1050 }
   1051 
   1052 /// Same as `unlink` except the parameter is a null terminated UTF8-encoded string.
   1053 pub fn unlinkC(file_path: [*]const u8) UnlinkError!void {
   1054     if (builtin.os == .windows) {
   1055         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   1056         return windows.DeleteFileW(&file_path_w);
   1057     }
   1058     switch (errno(system.unlink(file_path))) {
   1059         0 => return,
   1060         EACCES => return error.AccessDenied,
   1061         EPERM => return error.AccessDenied,
   1062         EBUSY => return error.FileBusy,
   1063         EFAULT => unreachable,
   1064         EINVAL => unreachable,
   1065         EIO => return error.FileSystem,
   1066         EISDIR => return error.IsDir,
   1067         ELOOP => return error.SymLinkLoop,
   1068         ENAMETOOLONG => return error.NameTooLong,
   1069         ENOENT => return error.FileNotFound,
   1070         ENOTDIR => return error.NotDir,
   1071         ENOMEM => return error.SystemResources,
   1072         EROFS => return error.ReadOnlyFileSystem,
   1073         else => |err| return unexpectedErrno(err),
   1074     }
   1075 }
   1076 
   1077 pub const UnlinkatError = UnlinkError || error{
   1078     /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.
   1079     DirNotEmpty,
   1080 };
   1081 
   1082 /// Delete a file name and possibly the file it refers to, based on an open directory handle.
   1083 pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
   1084     if (builtin.os == .windows) {
   1085         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   1086         return unlinkatW(dirfd, &file_path_w, flags);
   1087     }
   1088     const file_path_c = try toPosixPath(file_path);
   1089     return unlinkatC(dirfd, &file_path_c, flags);
   1090 }
   1091 
   1092 /// Same as `unlinkat` but `file_path` is a null-terminated string.
   1093 pub fn unlinkatC(dirfd: fd_t, file_path_c: [*]const u8, flags: u32) UnlinkatError!void {
   1094     if (builtin.os == .windows) {
   1095         const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
   1096         return unlinkatW(dirfd, &file_path_w, flags);
   1097     }
   1098     switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
   1099         0 => return,
   1100         EACCES => return error.AccessDenied,
   1101         EPERM => return error.AccessDenied,
   1102         EBUSY => return error.FileBusy,
   1103         EFAULT => unreachable,
   1104         EIO => return error.FileSystem,
   1105         EISDIR => return error.IsDir,
   1106         ELOOP => return error.SymLinkLoop,
   1107         ENAMETOOLONG => return error.NameTooLong,
   1108         ENOENT => return error.FileNotFound,
   1109         ENOTDIR => return error.NotDir,
   1110         ENOMEM => return error.SystemResources,
   1111         EROFS => return error.ReadOnlyFileSystem,
   1112         ENOTEMPTY => return error.DirNotEmpty,
   1113 
   1114         EINVAL => unreachable, // invalid flags, or pathname has . as last component
   1115         EBADF => unreachable, // always a race condition
   1116 
   1117         else => |err| return unexpectedErrno(err),
   1118     }
   1119 }
   1120 
   1121 /// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
   1122 pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*]const u16, flags: u32) UnlinkatError!void {
   1123     const w = windows;
   1124 
   1125     const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0;
   1126     const create_options_flags = if (want_rmdir_behavior)
   1127         w.ULONG(w.FILE_DELETE_ON_CLOSE)
   1128     else
   1129         w.ULONG(w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE);
   1130 
   1131     const path_len_bytes = @intCast(u16, mem.toSliceConst(u16, sub_path_w).len * 2);
   1132     var nt_name = w.UNICODE_STRING{
   1133         .Length = path_len_bytes,
   1134         .MaximumLength = path_len_bytes,
   1135         // The Windows API makes this mutable, but it will not mutate here.
   1136         .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
   1137     };
   1138 
   1139     if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
   1140         // Windows does not recognize this, but it does work with empty string.
   1141         nt_name.Length = 0;
   1142     }
   1143     if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
   1144         // Can't remove the parent directory with an open handle.
   1145         return error.FileBusy;
   1146     }
   1147 
   1148     var attr = w.OBJECT_ATTRIBUTES{
   1149         .Length = @sizeOf(w.OBJECT_ATTRIBUTES),
   1150         .RootDirectory = dirfd,
   1151         .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
   1152         .ObjectName = &nt_name,
   1153         .SecurityDescriptor = null,
   1154         .SecurityQualityOfService = null,
   1155     };
   1156     var io: w.IO_STATUS_BLOCK = undefined;
   1157     var tmp_handle: w.HANDLE = undefined;
   1158     var rc = w.ntdll.NtCreateFile(
   1159         &tmp_handle,
   1160         w.SYNCHRONIZE | w.DELETE,
   1161         &attr,
   1162         &io,
   1163         null,
   1164         0,
   1165         w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE,
   1166         w.FILE_OPEN,
   1167         create_options_flags,
   1168         null,
   1169         0,
   1170     );
   1171     if (rc == w.STATUS.SUCCESS) {
   1172         rc = w.ntdll.NtClose(tmp_handle);
   1173     }
   1174     switch (rc) {
   1175         w.STATUS.SUCCESS => return,
   1176         w.STATUS.OBJECT_NAME_INVALID => unreachable,
   1177         w.STATUS.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
   1178         w.STATUS.INVALID_PARAMETER => unreachable,
   1179         w.STATUS.FILE_IS_A_DIRECTORY => return error.IsDir,
   1180         else => return w.unexpectedStatus(rc),
   1181     }
   1182 }
   1183 
   1184 const RenameError = error{
   1185     AccessDenied,
   1186     FileBusy,
   1187     DiskQuota,
   1188     IsDir,
   1189     SymLinkLoop,
   1190     LinkQuotaExceeded,
   1191     NameTooLong,
   1192     FileNotFound,
   1193     NotDir,
   1194     SystemResources,
   1195     NoSpaceLeft,
   1196     PathAlreadyExists,
   1197     ReadOnlyFileSystem,
   1198     RenameAcrossMountPoints,
   1199     InvalidUtf8,
   1200     BadPathName,
   1201 } || UnexpectedError;
   1202 
   1203 /// Change the name or location of a file.
   1204 pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
   1205     if (builtin.os == .windows) {
   1206         const old_path_w = try windows.sliceToPrefixedFileW(old_path);
   1207         const new_path_w = try windows.sliceToPrefixedFileW(new_path);
   1208         return renameW(&old_path_w, &new_path_w);
   1209     } else {
   1210         const old_path_c = try toPosixPath(old_path);
   1211         const new_path_c = try toPosixPath(new_path);
   1212         return renameC(&old_path_c, &new_path_c);
   1213     }
   1214 }
   1215 
   1216 /// Same as `rename` except the parameters are null-terminated byte arrays.
   1217 pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) RenameError!void {
   1218     if (builtin.os == .windows) {
   1219         const old_path_w = try windows.cStrToPrefixedFileW(old_path);
   1220         const new_path_w = try windows.cStrToPrefixedFileW(new_path);
   1221         return renameW(&old_path_w, &new_path_w);
   1222     }
   1223     switch (errno(system.rename(old_path, new_path))) {
   1224         0 => return,
   1225         EACCES => return error.AccessDenied,
   1226         EPERM => return error.AccessDenied,
   1227         EBUSY => return error.FileBusy,
   1228         EDQUOT => return error.DiskQuota,
   1229         EFAULT => unreachable,
   1230         EINVAL => unreachable,
   1231         EISDIR => return error.IsDir,
   1232         ELOOP => return error.SymLinkLoop,
   1233         EMLINK => return error.LinkQuotaExceeded,
   1234         ENAMETOOLONG => return error.NameTooLong,
   1235         ENOENT => return error.FileNotFound,
   1236         ENOTDIR => return error.NotDir,
   1237         ENOMEM => return error.SystemResources,
   1238         ENOSPC => return error.NoSpaceLeft,
   1239         EEXIST => return error.PathAlreadyExists,
   1240         ENOTEMPTY => return error.PathAlreadyExists,
   1241         EROFS => return error.ReadOnlyFileSystem,
   1242         EXDEV => return error.RenameAcrossMountPoints,
   1243         else => |err| return unexpectedErrno(err),
   1244     }
   1245 }
   1246 
   1247 /// Same as `rename` except the parameters are null-terminated UTF16LE encoded byte arrays.
   1248 /// Assumes target is Windows.
   1249 pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) RenameError!void {
   1250     const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
   1251     return windows.MoveFileExW(old_path, new_path, flags);
   1252 }
   1253 
   1254 pub const MakeDirError = error{
   1255     AccessDenied,
   1256     DiskQuota,
   1257     PathAlreadyExists,
   1258     SymLinkLoop,
   1259     LinkQuotaExceeded,
   1260     NameTooLong,
   1261     FileNotFound,
   1262     SystemResources,
   1263     NoSpaceLeft,
   1264     NotDir,
   1265     ReadOnlyFileSystem,
   1266     InvalidUtf8,
   1267     BadPathName,
   1268 } || UnexpectedError;
   1269 
   1270 /// Create a directory.
   1271 /// `mode` is ignored on Windows.
   1272 pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
   1273     if (builtin.os == .windows) {
   1274         const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
   1275         return windows.CreateDirectoryW(&dir_path_w, null);
   1276     } else {
   1277         const dir_path_c = try toPosixPath(dir_path);
   1278         return mkdirC(&dir_path_c, mode);
   1279     }
   1280 }
   1281 
   1282 /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string.
   1283 pub fn mkdirC(dir_path: [*]const u8, mode: u32) MakeDirError!void {
   1284     if (builtin.os == .windows) {
   1285         const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
   1286         return windows.CreateDirectoryW(&dir_path_w, null);
   1287     }
   1288     switch (errno(system.mkdir(dir_path, mode))) {
   1289         0 => return,
   1290         EACCES => return error.AccessDenied,
   1291         EPERM => return error.AccessDenied,
   1292         EDQUOT => return error.DiskQuota,
   1293         EEXIST => return error.PathAlreadyExists,
   1294         EFAULT => unreachable,
   1295         ELOOP => return error.SymLinkLoop,
   1296         EMLINK => return error.LinkQuotaExceeded,
   1297         ENAMETOOLONG => return error.NameTooLong,
   1298         ENOENT => return error.FileNotFound,
   1299         ENOMEM => return error.SystemResources,
   1300         ENOSPC => return error.NoSpaceLeft,
   1301         ENOTDIR => return error.NotDir,
   1302         EROFS => return error.ReadOnlyFileSystem,
   1303         else => |err| return unexpectedErrno(err),
   1304     }
   1305 }
   1306 
   1307 pub const DeleteDirError = error{
   1308     AccessDenied,
   1309     FileBusy,
   1310     SymLinkLoop,
   1311     NameTooLong,
   1312     FileNotFound,
   1313     SystemResources,
   1314     NotDir,
   1315     DirNotEmpty,
   1316     ReadOnlyFileSystem,
   1317     InvalidUtf8,
   1318     BadPathName,
   1319 } || UnexpectedError;
   1320 
   1321 /// Deletes an empty directory.
   1322 pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
   1323     if (builtin.os == .windows) {
   1324         const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
   1325         return windows.RemoveDirectoryW(&dir_path_w);
   1326     } else {
   1327         const dir_path_c = try toPosixPath(dir_path);
   1328         return rmdirC(&dir_path_c);
   1329     }
   1330 }
   1331 
   1332 /// Same as `rmdir` except the parameter is null-terminated.
   1333 pub fn rmdirC(dir_path: [*]const u8) DeleteDirError!void {
   1334     if (builtin.os == .windows) {
   1335         const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
   1336         return windows.RemoveDirectoryW(&dir_path_w);
   1337     }
   1338     switch (errno(system.rmdir(dir_path))) {
   1339         0 => return,
   1340         EACCES => return error.AccessDenied,
   1341         EPERM => return error.AccessDenied,
   1342         EBUSY => return error.FileBusy,
   1343         EFAULT => unreachable,
   1344         EINVAL => unreachable,
   1345         ELOOP => return error.SymLinkLoop,
   1346         ENAMETOOLONG => return error.NameTooLong,
   1347         ENOENT => return error.FileNotFound,
   1348         ENOMEM => return error.SystemResources,
   1349         ENOTDIR => return error.NotDir,
   1350         EEXIST => return error.DirNotEmpty,
   1351         ENOTEMPTY => return error.DirNotEmpty,
   1352         EROFS => return error.ReadOnlyFileSystem,
   1353         else => |err| return unexpectedErrno(err),
   1354     }
   1355 }
   1356 
   1357 pub const ChangeCurDirError = error{
   1358     AccessDenied,
   1359     FileSystem,
   1360     SymLinkLoop,
   1361     NameTooLong,
   1362     FileNotFound,
   1363     SystemResources,
   1364     NotDir,
   1365 } || UnexpectedError;
   1366 
   1367 /// Changes the current working directory of the calling process.
   1368 /// `dir_path` is recommended to be a UTF-8 encoded string.
   1369 pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
   1370     if (builtin.os == .windows) {
   1371         const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
   1372         @compileError("TODO implement chdir for Windows");
   1373     } else {
   1374         const dir_path_c = try toPosixPath(dir_path);
   1375         return chdirC(&dir_path_c);
   1376     }
   1377 }
   1378 
   1379 /// Same as `chdir` except the parameter is null-terminated.
   1380 pub fn chdirC(dir_path: [*]const u8) ChangeCurDirError!void {
   1381     if (builtin.os == .windows) {
   1382         const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
   1383         @compileError("TODO implement chdir for Windows");
   1384     }
   1385     switch (errno(system.chdir(dir_path))) {
   1386         0 => return,
   1387         EACCES => return error.AccessDenied,
   1388         EFAULT => unreachable,
   1389         EIO => return error.FileSystem,
   1390         ELOOP => return error.SymLinkLoop,
   1391         ENAMETOOLONG => return error.NameTooLong,
   1392         ENOENT => return error.FileNotFound,
   1393         ENOMEM => return error.SystemResources,
   1394         ENOTDIR => return error.NotDir,
   1395         else => |err| return unexpectedErrno(err),
   1396     }
   1397 }
   1398 
   1399 pub const ReadLinkError = error{
   1400     AccessDenied,
   1401     FileSystem,
   1402     SymLinkLoop,
   1403     NameTooLong,
   1404     FileNotFound,
   1405     SystemResources,
   1406     NotDir,
   1407 } || UnexpectedError;
   1408 
   1409 /// Read value of a symbolic link.
   1410 /// The return value is a slice of `out_buffer` from index 0.
   1411 pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
   1412     if (builtin.os == .windows) {
   1413         const file_path_w = try windows.sliceToPrefixedFileW(file_path);
   1414         @compileError("TODO implement readlink for Windows");
   1415     } else {
   1416         const file_path_c = try toPosixPath(file_path);
   1417         return readlinkC(&file_path_c, out_buffer);
   1418     }
   1419 }
   1420 
   1421 /// Same as `readlink` except `file_path` is null-terminated.
   1422 pub fn readlinkC(file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
   1423     if (builtin.os == .windows) {
   1424         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   1425         @compileError("TODO implement readlink for Windows");
   1426     }
   1427     const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
   1428     switch (errno(rc)) {
   1429         0 => return out_buffer[0..@bitCast(usize, rc)],
   1430         EACCES => return error.AccessDenied,
   1431         EFAULT => unreachable,
   1432         EINVAL => unreachable,
   1433         EIO => return error.FileSystem,
   1434         ELOOP => return error.SymLinkLoop,
   1435         ENAMETOOLONG => return error.NameTooLong,
   1436         ENOENT => return error.FileNotFound,
   1437         ENOMEM => return error.SystemResources,
   1438         ENOTDIR => return error.NotDir,
   1439         else => |err| return unexpectedErrno(err),
   1440     }
   1441 }
   1442 
   1443 pub fn readlinkatC(dirfd: fd_t, file_path: [*]const u8, out_buffer: []u8) ReadLinkError![]u8 {
   1444     if (builtin.os == .windows) {
   1445         const file_path_w = try windows.cStrToPrefixedFileW(file_path);
   1446         @compileError("TODO implement readlink for Windows");
   1447     }
   1448     const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
   1449     switch (errno(rc)) {
   1450         0 => return out_buffer[0..@bitCast(usize, rc)],
   1451         EACCES => return error.AccessDenied,
   1452         EFAULT => unreachable,
   1453         EINVAL => unreachable,
   1454         EIO => return error.FileSystem,
   1455         ELOOP => return error.SymLinkLoop,
   1456         ENAMETOOLONG => return error.NameTooLong,
   1457         ENOENT => return error.FileNotFound,
   1458         ENOMEM => return error.SystemResources,
   1459         ENOTDIR => return error.NotDir,
   1460         else => |err| return unexpectedErrno(err),
   1461     }
   1462 }
   1463 
   1464 pub const SetIdError = error{
   1465     ResourceLimitReached,
   1466     InvalidUserId,
   1467     PermissionDenied,
   1468 } || UnexpectedError;
   1469 
   1470 pub fn setuid(uid: u32) SetIdError!void {
   1471     switch (errno(system.setuid(uid))) {
   1472         0 => return,
   1473         EAGAIN => return error.ResourceLimitReached,
   1474         EINVAL => return error.InvalidUserId,
   1475         EPERM => return error.PermissionDenied,
   1476         else => |err| return unexpectedErrno(err),
   1477     }
   1478 }
   1479 
   1480 pub fn setreuid(ruid: u32, euid: u32) SetIdError!void {
   1481     switch (errno(system.setreuid(ruid, euid))) {
   1482         0 => return,
   1483         EAGAIN => return error.ResourceLimitReached,
   1484         EINVAL => return error.InvalidUserId,
   1485         EPERM => return error.PermissionDenied,
   1486         else => |err| return unexpectedErrno(err),
   1487     }
   1488 }
   1489 
   1490 pub fn setgid(gid: u32) SetIdError!void {
   1491     switch (errno(system.setgid(gid))) {
   1492         0 => return,
   1493         EAGAIN => return error.ResourceLimitReached,
   1494         EINVAL => return error.InvalidUserId,
   1495         EPERM => return error.PermissionDenied,
   1496         else => |err| return unexpectedErrno(err),
   1497     }
   1498 }
   1499 
   1500 pub fn setregid(rgid: u32, egid: u32) SetIdError!void {
   1501     switch (errno(system.setregid(rgid, egid))) {
   1502         0 => return,
   1503         EAGAIN => return error.ResourceLimitReached,
   1504         EINVAL => return error.InvalidUserId,
   1505         EPERM => return error.PermissionDenied,
   1506         else => |err| return unexpectedErrno(err),
   1507     }
   1508 }
   1509 
   1510 /// Test whether a file descriptor refers to a terminal.
   1511 pub fn isatty(handle: fd_t) bool {
   1512     if (builtin.os == .windows) {
   1513         if (isCygwinPty(handle))
   1514             return true;
   1515 
   1516         var out: windows.DWORD = undefined;
   1517         return windows.kernel32.GetConsoleMode(handle, &out) != 0;
   1518     }
   1519     if (builtin.link_libc) {
   1520         return system.isatty(handle) != 0;
   1521     }
   1522     if (builtin.os == .wasi) {
   1523         @compileError("TODO implement std.os.isatty for WASI");
   1524     }
   1525     if (builtin.os == .linux) {
   1526         var wsz: linux.winsize = undefined;
   1527         return linux.syscall3(linux.SYS_ioctl, @bitCast(usize, isize(handle)), linux.TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
   1528     }
   1529     unreachable;
   1530 }
   1531 
   1532 pub fn isCygwinPty(handle: fd_t) bool {
   1533     if (builtin.os != .windows) return false;
   1534 
   1535     const size = @sizeOf(windows.FILE_NAME_INFO);
   1536     var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH);
   1537 
   1538     if (windows.kernel32.GetFileInformationByHandleEx(
   1539         handle,
   1540         windows.FileNameInfo,
   1541         @ptrCast(*c_void, &name_info_bytes),
   1542         name_info_bytes.len,
   1543     ) == 0) {
   1544         return false;
   1545     }
   1546 
   1547     const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]);
   1548     const name_bytes = name_info_bytes[size .. size + usize(name_info.FileNameLength)];
   1549     const name_wide = @bytesToSlice(u16, name_bytes);
   1550     return mem.indexOf(u16, name_wide, [_]u16{ 'm', 's', 'y', 's', '-' }) != null or
   1551         mem.indexOf(u16, name_wide, [_]u16{ '-', 'p', 't', 'y' }) != null;
   1552 }
   1553 
   1554 pub const SocketError = error{
   1555     /// Permission to create a socket of the specified type and/or
   1556     /// pro‐tocol is denied.
   1557     PermissionDenied,
   1558 
   1559     /// The implementation does not support the specified address family.
   1560     AddressFamilyNotSupported,
   1561 
   1562     /// Unknown protocol, or protocol family not available.
   1563     ProtocolFamilyNotAvailable,
   1564 
   1565     /// The per-process limit on the number of open file descriptors has been reached.
   1566     ProcessFdQuotaExceeded,
   1567 
   1568     /// The system-wide limit on the total number of open files has been reached.
   1569     SystemFdQuotaExceeded,
   1570 
   1571     /// Insufficient memory is available. The socket cannot be created until sufficient
   1572     /// resources are freed.
   1573     SystemResources,
   1574 
   1575     /// The protocol type or the specified protocol is not supported within this domain.
   1576     ProtocolNotSupported,
   1577 } || UnexpectedError;
   1578 
   1579 pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!fd_t {
   1580     const rc = system.socket(domain, socket_type, protocol);
   1581     switch (errno(rc)) {
   1582         0 => return @intCast(fd_t, rc),
   1583         EACCES => return error.PermissionDenied,
   1584         EAFNOSUPPORT => return error.AddressFamilyNotSupported,
   1585         EINVAL => return error.ProtocolFamilyNotAvailable,
   1586         EMFILE => return error.ProcessFdQuotaExceeded,
   1587         ENFILE => return error.SystemFdQuotaExceeded,
   1588         ENOBUFS => return error.SystemResources,
   1589         ENOMEM => return error.SystemResources,
   1590         EPROTONOSUPPORT => return error.ProtocolNotSupported,
   1591         else => |err| return unexpectedErrno(err),
   1592     }
   1593 }
   1594 
   1595 pub const BindError = error{
   1596     /// The address is protected, and the user is not the superuser.
   1597     /// For UNIX domain sockets: Search permission is denied on  a  component
   1598     /// of  the  path  prefix.
   1599     AccessDenied,
   1600 
   1601     /// The given address is already in use, or in the case of Internet domain sockets,
   1602     /// The  port number was specified as zero in the socket
   1603     /// address structure, but, upon attempting to bind to  an  ephemeral  port,  it  was
   1604     /// determined  that  all  port  numbers in the ephemeral port range are currently in
   1605     /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7).
   1606     AddressInUse,
   1607 
   1608     /// A nonexistent interface was requested or the requested address was not local.
   1609     AddressNotAvailable,
   1610 
   1611     /// Too many symbolic links were encountered in resolving addr.
   1612     SymLinkLoop,
   1613 
   1614     /// addr is too long.
   1615     NameTooLong,
   1616 
   1617     /// A component in the directory prefix of the socket pathname does not exist.
   1618     FileNotFound,
   1619 
   1620     /// Insufficient kernel memory was available.
   1621     SystemResources,
   1622 
   1623     /// A component of the path prefix is not a directory.
   1624     NotDir,
   1625 
   1626     /// The socket inode would reside on a read-only filesystem.
   1627     ReadOnlyFileSystem,
   1628 } || UnexpectedError;
   1629 
   1630 /// addr is `*const T` where T is one of the sockaddr
   1631 pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void {
   1632     const rc = system.bind(sockfd, addr, len);
   1633     switch (errno(rc)) {
   1634         0 => return,
   1635         EACCES => return error.AccessDenied,
   1636         EADDRINUSE => return error.AddressInUse,
   1637         EBADF => unreachable, // always a race condition if this error is returned
   1638         EINVAL => unreachable, // invalid parameters
   1639         ENOTSOCK => unreachable, // invalid `sockfd`
   1640         EADDRNOTAVAIL => return error.AddressNotAvailable,
   1641         EFAULT => unreachable, // invalid `addr` pointer
   1642         ELOOP => return error.SymLinkLoop,
   1643         ENAMETOOLONG => return error.NameTooLong,
   1644         ENOENT => return error.FileNotFound,
   1645         ENOMEM => return error.SystemResources,
   1646         ENOTDIR => return error.NotDir,
   1647         EROFS => return error.ReadOnlyFileSystem,
   1648         else => |err| return unexpectedErrno(err),
   1649     }
   1650 }
   1651 
   1652 const ListenError = error{
   1653     /// Another socket is already listening on the same port.
   1654     /// For Internet domain sockets, the  socket referred to by sockfd had not previously
   1655     /// been bound to an address and, upon attempting to bind it to an ephemeral port, it
   1656     /// was determined that all port numbers in the ephemeral port range are currently in
   1657     /// use.  See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
   1658     AddressInUse,
   1659 
   1660     /// The file descriptor sockfd does not refer to a socket.
   1661     FileDescriptorNotASocket,
   1662 
   1663     /// The socket is not of a type that supports the listen() operation.
   1664     OperationNotSupported,
   1665 } || UnexpectedError;
   1666 
   1667 pub fn listen(sockfd: i32, backlog: u32) ListenError!void {
   1668     const rc = system.listen(sockfd, backlog);
   1669     switch (errno(rc)) {
   1670         0 => return,
   1671         EADDRINUSE => return error.AddressInUse,
   1672         EBADF => unreachable,
   1673         ENOTSOCK => return error.FileDescriptorNotASocket,
   1674         EOPNOTSUPP => return error.OperationNotSupported,
   1675         else => |err| return unexpectedErrno(err),
   1676     }
   1677 }
   1678 
   1679 pub const AcceptError = error{
   1680     ConnectionAborted,
   1681 
   1682     /// The per-process limit on the number of open file descriptors has been reached.
   1683     ProcessFdQuotaExceeded,
   1684 
   1685     /// The system-wide limit on the total number of open files has been reached.
   1686     SystemFdQuotaExceeded,
   1687 
   1688     /// Not enough free memory.  This often means that the memory allocation  is  limited
   1689     /// by the socket buffer limits, not by the system memory.
   1690     SystemResources,
   1691 
   1692     ProtocolFailure,
   1693 
   1694     /// Firewall rules forbid connection.
   1695     BlockedByFirewall,
   1696 
   1697     /// This error occurs when no global event loop is configured,
   1698     /// and accepting from the socket would block.
   1699     WouldBlock,
   1700 } || UnexpectedError;
   1701 
   1702 /// Accept a connection on a socket.
   1703 /// If the application has a global event loop enabled, EAGAIN is handled
   1704 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock.
   1705 pub fn accept4(
   1706     /// This argument is a socket that has been created with `socket`, bound to a local address
   1707     /// with `bind`, and is listening for connections after a `listen`.
   1708     sockfd: fd_t,
   1709     /// This argument is a pointer to a sockaddr structure.  This structure is filled in with  the
   1710     /// address  of  the  peer  socket, as known to the communications layer.  The exact format of the
   1711     /// address returned addr is determined by the socket's address  family  (see  `socket`  and  the
   1712     /// respective  protocol  man  pages).
   1713     addr: *sockaddr,
   1714     /// This argument is a value-result argument: the caller must initialize it to contain  the
   1715     /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size
   1716     /// of the peer address.
   1717     ///
   1718     /// The returned address is truncated if the buffer provided is too small; in this  case,  `addr_size`
   1719     /// will return a value greater than was supplied to the call.
   1720     addr_size: *socklen_t,
   1721     /// If  flags  is  0, then `accept4` is the same as `accept`.  The following values can be bitwise
   1722     /// ORed in flags to obtain different behavior:
   1723     /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`)
   1724     ///   referred  to by the new file descriptor.  Using this flag saves extra calls to `fcntl` to achieve
   1725     ///   the same result.
   1726     /// * `SOCK_CLOEXEC`  - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor.   See  the
   1727     ///   description  of the `O_CLOEXEC` flag in `open` for reasons why this may be useful.
   1728     flags: u32,
   1729 ) AcceptError!fd_t {
   1730     while (true) {
   1731         const rc = system.accept4(sockfd, addr, addr_size, flags);
   1732         switch (errno(rc)) {
   1733             0 => return @intCast(fd_t, rc),
   1734             EINTR => continue,
   1735 
   1736             EAGAIN => if (std.event.Loop.instance) |loop| {
   1737                 loop.waitUntilFdReadable(sockfd);
   1738                 continue;
   1739             } else {
   1740                 return error.WouldBlock;
   1741             },
   1742             EBADF => unreachable, // always a race condition
   1743             ECONNABORTED => return error.ConnectionAborted,
   1744             EFAULT => unreachable,
   1745             EINVAL => unreachable,
   1746             ENOTSOCK => unreachable,
   1747             EMFILE => return error.ProcessFdQuotaExceeded,
   1748             ENFILE => return error.SystemFdQuotaExceeded,
   1749             ENOBUFS => return error.SystemResources,
   1750             ENOMEM => return error.SystemResources,
   1751             EOPNOTSUPP => unreachable,
   1752             EPROTO => return error.ProtocolFailure,
   1753             EPERM => return error.BlockedByFirewall,
   1754 
   1755             else => |err| return unexpectedErrno(err),
   1756         }
   1757     }
   1758 }
   1759 
   1760 pub const EpollCreateError = error{
   1761     /// The  per-user   limit   on   the   number   of   epoll   instances   imposed   by
   1762     /// /proc/sys/fs/epoll/max_user_instances  was encountered.  See epoll(7) for further
   1763     /// details.
   1764     /// Or, The per-process limit on the number of open file descriptors has been reached.
   1765     ProcessFdQuotaExceeded,
   1766 
   1767     /// The system-wide limit on the total number of open files has been reached.
   1768     SystemFdQuotaExceeded,
   1769 
   1770     /// There was insufficient memory to create the kernel object.
   1771     SystemResources,
   1772 } || UnexpectedError;
   1773 
   1774 pub fn epoll_create1(flags: u32) EpollCreateError!i32 {
   1775     const rc = system.epoll_create1(flags);
   1776     switch (errno(rc)) {
   1777         0 => return @intCast(i32, rc),
   1778         else => |err| return unexpectedErrno(err),
   1779 
   1780         EINVAL => unreachable,
   1781         EMFILE => return error.ProcessFdQuotaExceeded,
   1782         ENFILE => return error.SystemFdQuotaExceeded,
   1783         ENOMEM => return error.SystemResources,
   1784     }
   1785 }
   1786 
   1787 pub const EpollCtlError = error{
   1788     /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is  already  registered
   1789     /// with this epoll instance.
   1790     FileDescriptorAlreadyPresentInSet,
   1791 
   1792     /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a
   1793     /// circular loop of epoll instances monitoring one another.
   1794     OperationCausesCircularLoop,
   1795 
   1796     /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with  this  epoll
   1797     /// instance.
   1798     FileDescriptorNotRegistered,
   1799 
   1800     /// There was insufficient memory to handle the requested op control operation.
   1801     SystemResources,
   1802 
   1803     /// The  limit  imposed  by /proc/sys/fs/epoll/max_user_watches was encountered while
   1804     /// trying to register (EPOLL_CTL_ADD) a new file descriptor on  an  epoll  instance.
   1805     /// See epoll(7) for further details.
   1806     UserResourceLimitReached,
   1807 
   1808     /// The target file fd does not support epoll.  This error can occur if fd refers to,
   1809     /// for example, a regular file or a directory.
   1810     FileDescriptorIncompatibleWithEpoll,
   1811 } || UnexpectedError;
   1812 
   1813 pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*epoll_event) EpollCtlError!void {
   1814     const rc = system.epoll_ctl(epfd, op, fd, event);
   1815     switch (errno(rc)) {
   1816         0 => return,
   1817         else => |err| return unexpectedErrno(err),
   1818 
   1819         EBADF => unreachable, // always a race condition if this happens
   1820         EEXIST => return error.FileDescriptorAlreadyPresentInSet,
   1821         EINVAL => unreachable,
   1822         ELOOP => return error.OperationCausesCircularLoop,
   1823         ENOENT => return error.FileDescriptorNotRegistered,
   1824         ENOMEM => return error.SystemResources,
   1825         ENOSPC => return error.UserResourceLimitReached,
   1826         EPERM => return error.FileDescriptorIncompatibleWithEpoll,
   1827     }
   1828 }
   1829 
   1830 /// Waits for an I/O event on an epoll file descriptor.
   1831 /// Returns the number of file descriptors ready for the requested I/O,
   1832 /// or zero if no file descriptor became ready during the requested timeout milliseconds.
   1833 pub fn epoll_wait(epfd: i32, events: []epoll_event, timeout: i32) usize {
   1834     while (true) {
   1835         // TODO get rid of the @intCast
   1836         const rc = system.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout);
   1837         switch (errno(rc)) {
   1838             0 => return @intCast(usize, rc),
   1839             EINTR => continue,
   1840             EBADF => unreachable,
   1841             EFAULT => unreachable,
   1842             EINVAL => unreachable,
   1843             else => unreachable,
   1844         }
   1845     }
   1846 }
   1847 
   1848 pub const EventFdError = error{
   1849     SystemResources,
   1850     ProcessFdQuotaExceeded,
   1851     SystemFdQuotaExceeded,
   1852 } || UnexpectedError;
   1853 
   1854 pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
   1855     const rc = system.eventfd(initval, flags);
   1856     switch (errno(rc)) {
   1857         0 => return @intCast(i32, rc),
   1858         else => |err| return unexpectedErrno(err),
   1859 
   1860         EINVAL => unreachable, // invalid parameters
   1861         EMFILE => return error.ProcessFdQuotaExceeded,
   1862         ENFILE => return error.SystemFdQuotaExceeded,
   1863         ENODEV => return error.SystemResources,
   1864         ENOMEM => return error.SystemResources,
   1865     }
   1866 }
   1867 
   1868 pub const GetSockNameError = error{
   1869     /// Insufficient resources were available in the system to perform the operation.
   1870     SystemResources,
   1871 } || UnexpectedError;
   1872 
   1873 pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
   1874     switch (errno(system.getsockname(sockfd, addr, addrlen))) {
   1875         0 => return,
   1876         else => |err| return unexpectedErrno(err),
   1877 
   1878         EBADF => unreachable, // always a race condition
   1879         EFAULT => unreachable,
   1880         EINVAL => unreachable, // invalid parameters
   1881         ENOTSOCK => unreachable,
   1882         ENOBUFS => return error.SystemResources,
   1883     }
   1884 }
   1885 
   1886 pub const ConnectError = error{
   1887     /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on  the  socket
   1888     /// file,  or  search  permission  is  denied  for  one of the directories in the path prefix.
   1889     /// or
   1890     /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled  or
   1891     /// the connection request failed because of a local firewall rule.
   1892     PermissionDenied,
   1893 
   1894     /// Local address is already in use.
   1895     AddressInUse,
   1896 
   1897     /// (Internet  domain  sockets)  The  socket  referred  to  by sockfd had not previously been bound to an
   1898     /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers
   1899     /// in    the    ephemeral    port    range    are   currently   in   use.    See   the   discussion   of
   1900     /// /proc/sys/net/ipv4/ip_local_port_range in ip(7).
   1901     AddressNotAvailable,
   1902 
   1903     /// The passed address didn't have the correct address family in its sa_family field.
   1904     AddressFamilyNotSupported,
   1905 
   1906     /// Insufficient entries in the routing cache.
   1907     SystemResources,
   1908 
   1909     /// A connect() on a stream socket found no one listening on the remote address.
   1910     ConnectionRefused,
   1911 
   1912     /// Network is unreachable.
   1913     NetworkUnreachable,
   1914 
   1915     /// Timeout  while  attempting  connection.   The server may be too busy to accept new connections.  Note
   1916     /// that for IP sockets the timeout may be very long when syncookies are enabled on the server.
   1917     ConnectionTimedOut,
   1918 
   1919     /// This error occurs when no global event loop is configured,
   1920     /// and connecting to the socket would block.
   1921     WouldBlock,
   1922 
   1923     /// The given path for the unix socket does not exist.
   1924     FileNotFound,
   1925 } || UnexpectedError;
   1926 
   1927 /// Initiate a connection on a socket.
   1928 pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
   1929     while (true) {
   1930         switch (errno(system.connect(sockfd, sock_addr, len))) {
   1931             0 => return,
   1932             EACCES => return error.PermissionDenied,
   1933             EPERM => return error.PermissionDenied,
   1934             EADDRINUSE => return error.AddressInUse,
   1935             EADDRNOTAVAIL => return error.AddressNotAvailable,
   1936             EAFNOSUPPORT => return error.AddressFamilyNotSupported,
   1937             EAGAIN, EINPROGRESS => {
   1938                 const loop = std.event.Loop.instance orelse return error.WouldBlock;
   1939                 loop.waitUntilFdWritableOrReadable(sockfd);
   1940                 return getsockoptError(sockfd);
   1941             },
   1942             EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
   1943             EBADF => unreachable, // sockfd is not a valid open file descriptor.
   1944             ECONNREFUSED => return error.ConnectionRefused,
   1945             EFAULT => unreachable, // The socket structure address is outside the user's address space.
   1946             EINTR => continue,
   1947             EISCONN => unreachable, // The socket is already connected.
   1948             ENETUNREACH => return error.NetworkUnreachable,
   1949             ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   1950             EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
   1951             ETIMEDOUT => return error.ConnectionTimedOut,
   1952             ENOENT => return error.FileNotFound, // Returned when socket is AF_UNIX and the given path does not exist.
   1953             else => |err| return unexpectedErrno(err),
   1954         }
   1955     }
   1956 }
   1957 
   1958 pub fn getsockoptError(sockfd: i32) ConnectError!void {
   1959     var err_code: u32 = undefined;
   1960     var size: u32 = @sizeOf(u32);
   1961     const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size);
   1962     assert(size == 4);
   1963     switch (errno(rc)) {
   1964         0 => switch (err_code) {
   1965             0 => return,
   1966             EACCES => return error.PermissionDenied,
   1967             EPERM => return error.PermissionDenied,
   1968             EADDRINUSE => return error.AddressInUse,
   1969             EADDRNOTAVAIL => return error.AddressNotAvailable,
   1970             EAFNOSUPPORT => return error.AddressFamilyNotSupported,
   1971             EAGAIN => return error.SystemResources,
   1972             EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed.
   1973             EBADF => unreachable, // sockfd is not a valid open file descriptor.
   1974             ECONNREFUSED => return error.ConnectionRefused,
   1975             EFAULT => unreachable, // The socket structure address is outside the user's address space.
   1976             EISCONN => unreachable, // The socket is already connected.
   1977             ENETUNREACH => return error.NetworkUnreachable,
   1978             ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   1979             EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
   1980             ETIMEDOUT => return error.ConnectionTimedOut,
   1981             else => |err| return unexpectedErrno(err),
   1982         },
   1983         EBADF => unreachable, // The argument sockfd is not a valid file descriptor.
   1984         EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
   1985         EINVAL => unreachable,
   1986         ENOPROTOOPT => unreachable, // The option is unknown at the level indicated.
   1987         ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   1988         else => |err| return unexpectedErrno(err),
   1989     }
   1990 }
   1991 
   1992 pub fn waitpid(pid: i32, flags: u32) u32 {
   1993     // TODO allow implicit pointer cast from *u32 to *c_uint ?
   1994     const Status = if (builtin.link_libc) c_uint else u32;
   1995     var status: Status = undefined;
   1996     while (true) {
   1997         switch (errno(system.waitpid(pid, &status, flags))) {
   1998             0 => return @bitCast(u32, status),
   1999             EINTR => continue,
   2000             ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
   2001             EINVAL => unreachable, // The options argument was invalid
   2002             else => unreachable,
   2003         }
   2004     }
   2005 }
   2006 
   2007 pub const FStatError = error{SystemResources} || UnexpectedError;
   2008 
   2009 pub fn fstat(fd: fd_t) FStatError!Stat {
   2010     var stat: Stat = undefined;
   2011     if (comptime std.Target.current.isDarwin()) {
   2012         switch (darwin.getErrno(darwin.@"fstat$INODE64"(fd, &stat))) {
   2013             0 => return stat,
   2014             EINVAL => unreachable,
   2015             EBADF => unreachable, // Always a race condition.
   2016             ENOMEM => return error.SystemResources,
   2017             else => |err| return unexpectedErrno(err),
   2018         }
   2019     }
   2020 
   2021     switch (errno(system.fstat(fd, &stat))) {
   2022         0 => return stat,
   2023         EINVAL => unreachable,
   2024         EBADF => unreachable, // Always a race condition.
   2025         ENOMEM => return error.SystemResources,
   2026         else => |err| return unexpectedErrno(err),
   2027     }
   2028 }
   2029 
   2030 pub const KQueueError = error{
   2031     /// The per-process limit on the number of open file descriptors has been reached.
   2032     ProcessFdQuotaExceeded,
   2033 
   2034     /// The system-wide limit on the total number of open files has been reached.
   2035     SystemFdQuotaExceeded,
   2036 } || UnexpectedError;
   2037 
   2038 pub fn kqueue() KQueueError!i32 {
   2039     const rc = system.kqueue();
   2040     switch (errno(rc)) {
   2041         0 => return @intCast(i32, rc),
   2042         EMFILE => return error.ProcessFdQuotaExceeded,
   2043         ENFILE => return error.SystemFdQuotaExceeded,
   2044         else => |err| return unexpectedErrno(err),
   2045     }
   2046 }
   2047 
   2048 pub const KEventError = error{
   2049     /// The process does not have permission to register a filter.
   2050     AccessDenied,
   2051 
   2052     /// The event could not be found to be modified or deleted.
   2053     EventNotFound,
   2054 
   2055     /// No memory was available to register the event.
   2056     SystemResources,
   2057 
   2058     /// The specified process to attach to does not exist.
   2059     ProcessNotFound,
   2060 
   2061     /// changelist or eventlist had too many items on it.
   2062     /// TODO remove this possibility
   2063     Overflow,
   2064 };
   2065 
   2066 pub fn kevent(
   2067     kq: i32,
   2068     changelist: []const Kevent,
   2069     eventlist: []Kevent,
   2070     timeout: ?*const timespec,
   2071 ) KEventError!usize {
   2072     while (true) {
   2073         const rc = system.kevent(
   2074             kq,
   2075             changelist.ptr,
   2076             try math.cast(c_int, changelist.len),
   2077             eventlist.ptr,
   2078             try math.cast(c_int, eventlist.len),
   2079             timeout,
   2080         );
   2081         switch (errno(rc)) {
   2082             0 => return @intCast(usize, rc),
   2083             EACCES => return error.AccessDenied,
   2084             EFAULT => unreachable,
   2085             EBADF => unreachable, // Always a race condition.
   2086             EINTR => continue,
   2087             EINVAL => unreachable,
   2088             ENOENT => return error.EventNotFound,
   2089             ENOMEM => return error.SystemResources,
   2090             ESRCH => return error.ProcessNotFound,
   2091             else => unreachable,
   2092         }
   2093     }
   2094 }
   2095 
   2096 pub const INotifyInitError = error{
   2097     ProcessFdQuotaExceeded,
   2098     SystemFdQuotaExceeded,
   2099     SystemResources,
   2100 } || UnexpectedError;
   2101 
   2102 /// initialize an inotify instance
   2103 pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
   2104     const rc = system.inotify_init1(flags);
   2105     switch (errno(rc)) {
   2106         0 => return @intCast(i32, rc),
   2107         EINVAL => unreachable,
   2108         EMFILE => return error.ProcessFdQuotaExceeded,
   2109         ENFILE => return error.SystemFdQuotaExceeded,
   2110         ENOMEM => return error.SystemResources,
   2111         else => |err| return unexpectedErrno(err),
   2112     }
   2113 }
   2114 
   2115 pub const INotifyAddWatchError = error{
   2116     AccessDenied,
   2117     NameTooLong,
   2118     FileNotFound,
   2119     SystemResources,
   2120     UserResourceLimitReached,
   2121 } || UnexpectedError;
   2122 
   2123 /// add a watch to an initialized inotify instance
   2124 pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
   2125     const pathname_c = try toPosixPath(pathname);
   2126     return inotify_add_watchC(inotify_fd, &pathname_c, mask);
   2127 }
   2128 
   2129 /// Same as `inotify_add_watch` except pathname is null-terminated.
   2130 pub fn inotify_add_watchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) INotifyAddWatchError!i32 {
   2131     const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
   2132     switch (errno(rc)) {
   2133         0 => return @intCast(i32, rc),
   2134         EACCES => return error.AccessDenied,
   2135         EBADF => unreachable,
   2136         EFAULT => unreachable,
   2137         EINVAL => unreachable,
   2138         ENAMETOOLONG => return error.NameTooLong,
   2139         ENOENT => return error.FileNotFound,
   2140         ENOMEM => return error.SystemResources,
   2141         ENOSPC => return error.UserResourceLimitReached,
   2142         else => |err| return unexpectedErrno(err),
   2143     }
   2144 }
   2145 
   2146 /// remove an existing watch from an inotify instance
   2147 pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
   2148     switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
   2149         0 => return,
   2150         EBADF => unreachable,
   2151         EINVAL => unreachable,
   2152         else => unreachable,
   2153     }
   2154 }
   2155 
   2156 pub const MProtectError = error{
   2157     /// The memory cannot be given the specified access.  This can happen, for example, if you
   2158     /// mmap(2)  a  file  to  which  you have read-only access, then ask mprotect() to mark it
   2159     /// PROT_WRITE.
   2160     AccessDenied,
   2161 
   2162     /// Changing  the  protection  of a memory region would result in the total number of map‐
   2163     /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the
   2164     /// allowed maximum.  (For example, making the protection of a range PROT_READ in the mid‐
   2165     /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐
   2166     /// pings: two read/write mappings at each end and a read-only mapping in the middle.)
   2167     OutOfMemory,
   2168 } || UnexpectedError;
   2169 
   2170 /// `memory.len` must be page-aligned.
   2171 pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void {
   2172     assert(mem.isAligned(memory.len, mem.page_size));
   2173     switch (errno(system.mprotect(memory.ptr, memory.len, protection))) {
   2174         0 => return,
   2175         EINVAL => unreachable,
   2176         EACCES => return error.AccessDenied,
   2177         ENOMEM => return error.OutOfMemory,
   2178         else => |err| return unexpectedErrno(err),
   2179     }
   2180 }
   2181 
   2182 pub const ForkError = error{SystemResources} || UnexpectedError;
   2183 
   2184 pub fn fork() ForkError!pid_t {
   2185     const rc = system.fork();
   2186     switch (errno(rc)) {
   2187         0 => return @intCast(pid_t, rc),
   2188         EAGAIN => return error.SystemResources,
   2189         ENOMEM => return error.SystemResources,
   2190         else => |err| return unexpectedErrno(err),
   2191     }
   2192 }
   2193 
   2194 pub const MMapError = error{
   2195     /// The underlying filesystem of the specified file does not support memory mapping.
   2196     MemoryMappingNotSupported,
   2197 
   2198     /// A file descriptor refers to a non-regular file. Or a file mapping was requested,
   2199     /// but the file descriptor is not open for reading. Or `MAP_SHARED` was requested
   2200     /// and `PROT_WRITE` is set, but the file descriptor is not open in `O_RDWR` mode.
   2201     /// Or `PROT_WRITE` is set, but the file is append-only.
   2202     AccessDenied,
   2203 
   2204     /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on
   2205     /// a filesystem that was mounted no-exec.
   2206     PermissionDenied,
   2207     LockedMemoryLimitExceeded,
   2208     OutOfMemory,
   2209 } || UnexpectedError;
   2210 
   2211 /// Map files or devices into memory.
   2212 /// Use of a mapped region can result in these signals:
   2213 /// * SIGSEGV - Attempted write into a region mapped as read-only.
   2214 /// * SIGBUS - Attempted  access to a portion of the buffer that does not correspond to the file
   2215 pub fn mmap(
   2216     ptr: ?[*]align(mem.page_size) u8,
   2217     length: usize,
   2218     prot: u32,
   2219     flags: u32,
   2220     fd: fd_t,
   2221     offset: u64,
   2222 ) MMapError![]align(mem.page_size) u8 {
   2223     const err = if (builtin.link_libc) blk: {
   2224         const rc = std.c.mmap(ptr, length, prot, flags, fd, offset);
   2225         if (rc != std.c.MAP_FAILED) return @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, rc))[0..length];
   2226         break :blk @intCast(usize, system._errno().*);
   2227     } else blk: {
   2228         const rc = system.mmap(ptr, length, prot, flags, fd, offset);
   2229         const err = errno(rc);
   2230         if (err == 0) return @intToPtr([*]align(mem.page_size) u8, rc)[0..length];
   2231         break :blk err;
   2232     };
   2233     switch (err) {
   2234         ETXTBSY => return error.AccessDenied,
   2235         EACCES => return error.AccessDenied,
   2236         EPERM => return error.PermissionDenied,
   2237         EAGAIN => return error.LockedMemoryLimitExceeded,
   2238         EBADF => unreachable, // Always a race condition.
   2239         EOVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
   2240         ENODEV => return error.MemoryMappingNotSupported,
   2241         EINVAL => unreachable, // Invalid parameters to mmap()
   2242         ENOMEM => return error.OutOfMemory,
   2243         else => return unexpectedErrno(err),
   2244     }
   2245 }
   2246 
   2247 /// Deletes the mappings for the specified address range, causing
   2248 /// further references to addresses within the range to generate invalid memory references.
   2249 /// Note that while POSIX allows unmapping a region in the middle of an existing mapping,
   2250 /// Zig's munmap function does not, for two reasons:
   2251 /// * It violates the Zig principle that resource deallocation must succeed.
   2252 /// * The Windows function, VirtualFree, has this restriction.
   2253 pub fn munmap(memory: []align(mem.page_size) u8) void {
   2254     switch (errno(system.munmap(memory.ptr, memory.len))) {
   2255         0 => return,
   2256         EINVAL => unreachable, // Invalid parameters.
   2257         ENOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
   2258         else => unreachable,
   2259     }
   2260 }
   2261 
   2262 pub const AccessError = error{
   2263     PermissionDenied,
   2264     FileNotFound,
   2265     NameTooLong,
   2266     InputOutput,
   2267     SystemResources,
   2268     BadPathName,
   2269 
   2270     /// On Windows, file paths must be valid Unicode.
   2271     InvalidUtf8,
   2272 } || UnexpectedError;
   2273 
   2274 /// check user's permissions for a file
   2275 /// TODO currently this assumes `mode` is `F_OK` on Windows.
   2276 pub fn access(path: []const u8, mode: u32) AccessError!void {
   2277     if (builtin.os == .windows) {
   2278         const path_w = try windows.sliceToPrefixedFileW(path);
   2279         _ = try windows.GetFileAttributesW(&path_w);
   2280         return;
   2281     }
   2282     const path_c = try toPosixPath(path);
   2283     return accessC(&path_c, mode);
   2284 }
   2285 
   2286 /// Same as `access` except `path` is null-terminated.
   2287 pub fn accessC(path: [*]const u8, mode: u32) AccessError!void {
   2288     if (builtin.os == .windows) {
   2289         const path_w = try windows.cStrToPrefixedFileW(path);
   2290         _ = try windows.GetFileAttributesW(&path_w);
   2291         return;
   2292     }
   2293     switch (errno(system.access(path, mode))) {
   2294         0 => return,
   2295         EACCES => return error.PermissionDenied,
   2296         EROFS => return error.PermissionDenied,
   2297         ELOOP => return error.PermissionDenied,
   2298         ETXTBSY => return error.PermissionDenied,
   2299         ENOTDIR => return error.FileNotFound,
   2300         ENOENT => return error.FileNotFound,
   2301 
   2302         ENAMETOOLONG => return error.NameTooLong,
   2303         EINVAL => unreachable,
   2304         EFAULT => unreachable,
   2305         EIO => return error.InputOutput,
   2306         ENOMEM => return error.SystemResources,
   2307         else => |err| return unexpectedErrno(err),
   2308     }
   2309 }
   2310 
   2311 /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string.
   2312 /// Otherwise use `access` or `accessC`.
   2313 /// TODO currently this ignores `mode`.
   2314 pub fn accessW(path: [*]const u16, mode: u32) windows.GetFileAttributesError!void {
   2315     const ret = try windows.GetFileAttributesW(path);
   2316     if (ret != windows.INVALID_FILE_ATTRIBUTES) {
   2317         return;
   2318     }
   2319     switch (windows.kernel32.GetLastError()) {
   2320         windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound,
   2321         windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound,
   2322         windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
   2323         else => |err| return windows.unexpectedError(err),
   2324     }
   2325 }
   2326 
   2327 pub const PipeError = error{
   2328     SystemFdQuotaExceeded,
   2329     ProcessFdQuotaExceeded,
   2330 } || UnexpectedError;
   2331 
   2332 /// Creates a unidirectional data channel that can be used for interprocess communication.
   2333 pub fn pipe() PipeError![2]fd_t {
   2334     var fds: [2]fd_t = undefined;
   2335     switch (errno(system.pipe(&fds))) {
   2336         0 => return fds,
   2337         EINVAL => unreachable, // Invalid parameters to pipe()
   2338         EFAULT => unreachable, // Invalid fds pointer
   2339         ENFILE => return error.SystemFdQuotaExceeded,
   2340         EMFILE => return error.ProcessFdQuotaExceeded,
   2341         else => |err| return unexpectedErrno(err),
   2342     }
   2343 }
   2344 
   2345 pub fn pipe2(flags: u32) PipeError![2]fd_t {
   2346     var fds: [2]fd_t = undefined;
   2347     switch (errno(system.pipe2(&fds, flags))) {
   2348         0 => return fds,
   2349         EINVAL => unreachable, // Invalid flags
   2350         EFAULT => unreachable, // Invalid fds pointer
   2351         ENFILE => return error.SystemFdQuotaExceeded,
   2352         EMFILE => return error.ProcessFdQuotaExceeded,
   2353         else => |err| return unexpectedErrno(err),
   2354     }
   2355 }
   2356 
   2357 pub const SysCtlError = error{
   2358     PermissionDenied,
   2359     SystemResources,
   2360     NameTooLong,
   2361 } || UnexpectedError;
   2362 
   2363 pub fn sysctl(
   2364     name: []const c_int,
   2365     oldp: ?*c_void,
   2366     oldlenp: ?*usize,
   2367     newp: ?*c_void,
   2368     newlen: usize,
   2369 ) SysCtlError!void {
   2370     const name_len = math.cast(c_uint, name.len) catch return error.NameTooLong;
   2371     switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {
   2372         0 => return,
   2373         EFAULT => unreachable,
   2374         EPERM => return error.PermissionDenied,
   2375         ENOMEM => return error.SystemResources,
   2376         else => |err| return unexpectedErrno(err),
   2377     }
   2378 }
   2379 
   2380 pub fn sysctlbynameC(
   2381     name: [*]const u8,
   2382     oldp: ?*c_void,
   2383     oldlenp: ?*usize,
   2384     newp: ?*c_void,
   2385     newlen: usize,
   2386 ) SysCtlError!void {
   2387     switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
   2388         0 => return,
   2389         EFAULT => unreachable,
   2390         EPERM => return error.PermissionDenied,
   2391         ENOMEM => return error.SystemResources,
   2392         else => |err| return unexpectedErrno(err),
   2393     }
   2394 }
   2395 
   2396 pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
   2397     switch (errno(system.gettimeofday(tv, tz))) {
   2398         0 => return,
   2399         EINVAL => unreachable,
   2400         else => unreachable,
   2401     }
   2402 }
   2403 
   2404 pub const SeekError = error{Unseekable} || UnexpectedError;
   2405 
   2406 /// Repositions read/write file offset relative to the beginning.
   2407 pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
   2408     if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   2409         var result: u64 = undefined;
   2410         switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) {
   2411             0 => return,
   2412             EBADF => unreachable, // always a race condition
   2413             EINVAL => return error.Unseekable,
   2414             EOVERFLOW => return error.Unseekable,
   2415             ESPIPE => return error.Unseekable,
   2416             ENXIO => return error.Unseekable,
   2417             else => |err| return unexpectedErrno(err),
   2418         }
   2419     }
   2420     if (builtin.os == .windows) {
   2421         return windows.SetFilePointerEx_BEGIN(fd, offset);
   2422     }
   2423     const ipos = @bitCast(i64, offset); // the OS treats this as unsigned
   2424     switch (errno(system.lseek(fd, ipos, SEEK_SET))) {
   2425         0 => return,
   2426         EBADF => unreachable, // always a race condition
   2427         EINVAL => return error.Unseekable,
   2428         EOVERFLOW => return error.Unseekable,
   2429         ESPIPE => return error.Unseekable,
   2430         ENXIO => return error.Unseekable,
   2431         else => |err| return unexpectedErrno(err),
   2432     }
   2433 }
   2434 
   2435 /// Repositions read/write file offset relative to the current offset.
   2436 pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
   2437     if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   2438         var result: u64 = undefined;
   2439         switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) {
   2440             0 => return,
   2441             EBADF => unreachable, // always a race condition
   2442             EINVAL => return error.Unseekable,
   2443             EOVERFLOW => return error.Unseekable,
   2444             ESPIPE => return error.Unseekable,
   2445             ENXIO => return error.Unseekable,
   2446             else => |err| return unexpectedErrno(err),
   2447         }
   2448     }
   2449     if (builtin.os == .windows) {
   2450         return windows.SetFilePointerEx_CURRENT(fd, offset);
   2451     }
   2452     switch (errno(system.lseek(fd, offset, SEEK_CUR))) {
   2453         0 => return,
   2454         EBADF => unreachable, // always a race condition
   2455         EINVAL => return error.Unseekable,
   2456         EOVERFLOW => return error.Unseekable,
   2457         ESPIPE => return error.Unseekable,
   2458         ENXIO => return error.Unseekable,
   2459         else => |err| return unexpectedErrno(err),
   2460     }
   2461 }
   2462 
   2463 /// Repositions read/write file offset relative to the end.
   2464 pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
   2465     if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   2466         var result: u64 = undefined;
   2467         switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) {
   2468             0 => return,
   2469             EBADF => unreachable, // always a race condition
   2470             EINVAL => return error.Unseekable,
   2471             EOVERFLOW => return error.Unseekable,
   2472             ESPIPE => return error.Unseekable,
   2473             ENXIO => return error.Unseekable,
   2474             else => |err| return unexpectedErrno(err),
   2475         }
   2476     }
   2477     if (builtin.os == .windows) {
   2478         return windows.SetFilePointerEx_END(fd, offset);
   2479     }
   2480     switch (errno(system.lseek(fd, offset, SEEK_END))) {
   2481         0 => return,
   2482         EBADF => unreachable, // always a race condition
   2483         EINVAL => return error.Unseekable,
   2484         EOVERFLOW => return error.Unseekable,
   2485         ESPIPE => return error.Unseekable,
   2486         ENXIO => return error.Unseekable,
   2487         else => |err| return unexpectedErrno(err),
   2488     }
   2489 }
   2490 
   2491 /// Returns the read/write file offset relative to the beginning.
   2492 pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
   2493     if (builtin.os == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
   2494         var result: u64 = undefined;
   2495         switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) {
   2496             0 => return result,
   2497             EBADF => unreachable, // always a race condition
   2498             EINVAL => return error.Unseekable,
   2499             EOVERFLOW => return error.Unseekable,
   2500             ESPIPE => return error.Unseekable,
   2501             ENXIO => return error.Unseekable,
   2502             else => |err| return unexpectedErrno(err),
   2503         }
   2504     }
   2505     if (builtin.os == .windows) {
   2506         return windows.SetFilePointerEx_CURRENT_get(fd);
   2507     }
   2508     const rc = system.lseek(fd, 0, SEEK_CUR);
   2509     switch (errno(rc)) {
   2510         0 => return @bitCast(u64, rc),
   2511         EBADF => unreachable, // always a race condition
   2512         EINVAL => return error.Unseekable,
   2513         EOVERFLOW => return error.Unseekable,
   2514         ESPIPE => return error.Unseekable,
   2515         ENXIO => return error.Unseekable,
   2516         else => |err| return unexpectedErrno(err),
   2517     }
   2518 }
   2519 
   2520 pub const RealPathError = error{
   2521     FileNotFound,
   2522     AccessDenied,
   2523     NameTooLong,
   2524     NotSupported,
   2525     NotDir,
   2526     SymLinkLoop,
   2527     InputOutput,
   2528     FileTooBig,
   2529     IsDir,
   2530     ProcessFdQuotaExceeded,
   2531     SystemFdQuotaExceeded,
   2532     NoDevice,
   2533     SystemResources,
   2534     NoSpaceLeft,
   2535     FileSystem,
   2536     BadPathName,
   2537     DeviceBusy,
   2538 
   2539     SharingViolation,
   2540     PipeBusy,
   2541 
   2542     /// On Windows, file paths must be valid Unicode.
   2543     InvalidUtf8,
   2544 
   2545     PathAlreadyExists,
   2546 } || UnexpectedError;
   2547 
   2548 /// Return the canonicalized absolute pathname.
   2549 /// Expands all symbolic links and resolves references to `.`, `..`, and
   2550 /// extra `/` characters in `pathname`.
   2551 /// The return value is a slice of `out_buffer`, but not necessarily from the beginning.
   2552 /// See also `realpathC` and `realpathW`.
   2553 pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   2554     if (builtin.os == .windows) {
   2555         const pathname_w = try windows.sliceToPrefixedFileW(pathname);
   2556         return realpathW(&pathname_w, out_buffer);
   2557     }
   2558     const pathname_c = try toPosixPath(pathname);
   2559     return realpathC(&pathname_c, out_buffer);
   2560 }
   2561 
   2562 /// Same as `realpath` except `pathname` is null-terminated.
   2563 pub fn realpathC(pathname: [*]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   2564     if (builtin.os == .windows) {
   2565         const pathname_w = try windows.cStrToPrefixedFileW(pathname);
   2566         return realpathW(&pathname_w, out_buffer);
   2567     }
   2568     if (builtin.os == .linux and !builtin.link_libc) {
   2569         const fd = try openC(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0);
   2570         defer close(fd);
   2571 
   2572         var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
   2573         const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", fd) catch unreachable;
   2574 
   2575         return readlinkC(proc_path.ptr, out_buffer);
   2576     }
   2577     const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) {
   2578         EINVAL => unreachable,
   2579         EBADF => unreachable,
   2580         EFAULT => unreachable,
   2581         EACCES => return error.AccessDenied,
   2582         ENOENT => return error.FileNotFound,
   2583         ENOTSUP => return error.NotSupported,
   2584         ENOTDIR => return error.NotDir,
   2585         ENAMETOOLONG => return error.NameTooLong,
   2586         ELOOP => return error.SymLinkLoop,
   2587         EIO => return error.InputOutput,
   2588         else => |err| return unexpectedErrno(@intCast(usize, err)),
   2589     };
   2590     return mem.toSlice(u8, result_path);
   2591 }
   2592 
   2593 /// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
   2594 pub fn realpathW(pathname: [*]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
   2595     const h_file = try windows.CreateFileW(
   2596         pathname,
   2597         windows.GENERIC_READ,
   2598         windows.FILE_SHARE_READ,
   2599         null,
   2600         windows.OPEN_EXISTING,
   2601         windows.FILE_ATTRIBUTE_NORMAL,
   2602         null,
   2603     );
   2604     defer windows.CloseHandle(h_file);
   2605 
   2606     var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
   2607     const wide_len = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS);
   2608     assert(wide_len <= wide_buf.len);
   2609     const wide_slice = wide_buf[0..wide_len];
   2610 
   2611     // Windows returns \\?\ prepended to the path.
   2612     // We strip it to make this function consistent across platforms.
   2613     const prefix = [_]u16{ '\\', '\\', '?', '\\' };
   2614     const start_index = if (mem.startsWith(u16, wide_slice, prefix)) prefix.len else 0;
   2615 
   2616     // Trust that Windows gives us valid UTF-16LE.
   2617     const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice[start_index..]) catch unreachable;
   2618     return out_buffer[0..end_index];
   2619 }
   2620 
   2621 /// Spurious wakeups are possible and no precision of timing is guaranteed.
   2622 pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
   2623     var req = timespec{
   2624         .tv_sec = math.cast(isize, seconds) catch math.maxInt(isize),
   2625         .tv_nsec = math.cast(isize, nanoseconds) catch math.maxInt(isize),
   2626     };
   2627     var rem: timespec = undefined;
   2628     while (true) {
   2629         switch (errno(system.nanosleep(&req, &rem))) {
   2630             EFAULT => unreachable,
   2631             EINVAL => {
   2632                 // Sometimes Darwin returns EINVAL for no reason.
   2633                 // We treat it as a spurious wakeup.
   2634                 return;
   2635             },
   2636             EINTR => {
   2637                 req = rem;
   2638                 continue;
   2639             },
   2640             // This prong handles success as well as unexpected errors.
   2641             else => return,
   2642         }
   2643     }
   2644 }
   2645 
   2646 pub fn dl_iterate_phdr(
   2647     comptime T: type,
   2648     callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32,
   2649     data: ?*T,
   2650 ) isize {
   2651     if (builtin.object_format != .elf)
   2652         @compileError("dl_iterate_phdr is not available for this target");
   2653 
   2654     if (builtin.link_libc) {
   2655         return system.dl_iterate_phdr(
   2656             @ptrCast(std.c.dl_iterate_phdr_callback, callback),
   2657             @ptrCast(?*c_void, data),
   2658         );
   2659     }
   2660 
   2661     const elf_base = std.process.getBaseAddress();
   2662     const ehdr = @intToPtr(*elf.Ehdr, elf_base);
   2663     // Make sure the base address points to an ELF image
   2664     assert(mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF"));
   2665     const n_phdr = ehdr.e_phnum;
   2666     const phdrs = (@intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff))[0..n_phdr];
   2667 
   2668     var it = dl.linkmap_iterator(phdrs) catch unreachable;
   2669 
   2670     // The executable has no dynamic link segment, create a single entry for
   2671     // the whole ELF image
   2672     if (it.end()) {
   2673         var info = dl_phdr_info{
   2674             .dlpi_addr = elf_base,
   2675             .dlpi_name = c"/proc/self/exe",
   2676             .dlpi_phdr = phdrs.ptr,
   2677             .dlpi_phnum = ehdr.e_phnum,
   2678         };
   2679 
   2680         return callback(&info, @sizeOf(dl_phdr_info), data);
   2681     }
   2682 
   2683     // Last return value from the callback function
   2684     var last_r: isize = 0;
   2685     while (it.next()) |entry| {
   2686         var dlpi_phdr: [*]elf.Phdr = undefined;
   2687         var dlpi_phnum: u16 = undefined;
   2688 
   2689         if (entry.l_addr != 0) {
   2690             const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr);
   2691             dlpi_phdr = @intToPtr([*]elf.Phdr, entry.l_addr + elf_header.e_phoff);
   2692             dlpi_phnum = elf_header.e_phnum;
   2693         } else {
   2694             // This is the running ELF image
   2695             dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff);
   2696             dlpi_phnum = ehdr.e_phnum;
   2697         }
   2698 
   2699         var info = dl_phdr_info{
   2700             .dlpi_addr = entry.l_addr,
   2701             .dlpi_name = entry.l_name,
   2702             .dlpi_phdr = dlpi_phdr,
   2703             .dlpi_phnum = dlpi_phnum,
   2704         };
   2705 
   2706         last_r = callback(&info, @sizeOf(dl_phdr_info), data);
   2707         if (last_r != 0) break;
   2708     }
   2709 
   2710     return last_r;
   2711 }
   2712 
   2713 pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;
   2714 
   2715 pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
   2716     switch (errno(system.clock_gettime(clk_id, tp))) {
   2717         0 => return,
   2718         EFAULT => unreachable,
   2719         EINVAL => return error.UnsupportedClock,
   2720         else => |err| return unexpectedErrno(err),
   2721     }
   2722 }
   2723 
   2724 pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
   2725     switch (errno(system.clock_getres(clk_id, res))) {
   2726         0 => return,
   2727         EFAULT => unreachable,
   2728         EINVAL => return error.UnsupportedClock,
   2729         else => |err| return unexpectedErrno(err),
   2730     }
   2731 }
   2732 
   2733 pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;
   2734 
   2735 pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
   2736     var set: cpu_set_t = undefined;
   2737     switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
   2738         0 => return set,
   2739         EFAULT => unreachable,
   2740         EINVAL => unreachable,
   2741         ESRCH => unreachable,
   2742         EPERM => return error.PermissionDenied,
   2743         else => |err| return unexpectedErrno(err),
   2744     }
   2745 }
   2746 
   2747 /// Used to convert a slice to a null terminated slice on the stack.
   2748 /// TODO https://github.com/ziglang/zig/issues/287
   2749 pub fn toPosixPath(file_path: []const u8) ![PATH_MAX]u8 {
   2750     var path_with_null: [PATH_MAX]u8 = undefined;
   2751     // >= rather than > to make room for the null byte
   2752     if (file_path.len >= PATH_MAX) return error.NameTooLong;
   2753     mem.copy(u8, &path_with_null, file_path);
   2754     path_with_null[file_path.len] = 0;
   2755     return path_with_null;
   2756 }
   2757 
   2758 /// Whether or not error.Unexpected will print its value and a stack trace.
   2759 /// if this happens the fix is to add the error code to the corresponding
   2760 /// switch expression, possibly introduce a new error in the error set, and
   2761 /// send a patch to Zig.
   2762 pub const unexpected_error_tracing = builtin.mode == .Debug;
   2763 
   2764 pub const UnexpectedError = error{
   2765     /// The Operating System returned an undocumented error code.
   2766     /// This error is in theory not possible, but it would be better
   2767     /// to handle this error than to invoke undefined behavior.
   2768     Unexpected,
   2769 };
   2770 
   2771 /// Call this when you made a syscall or something that sets errno
   2772 /// and you get an unexpected error.
   2773 pub fn unexpectedErrno(err: usize) UnexpectedError {
   2774     if (unexpected_error_tracing) {
   2775         std.debug.warn("unexpected errno: {}\n", err);
   2776         std.debug.dumpCurrentStackTrace(null);
   2777     }
   2778     return error.Unexpected;
   2779 }
   2780 
   2781 pub const SigaltstackError = error{
   2782     /// The supplied stack size was less than MINSIGSTKSZ.
   2783     SizeTooSmall,
   2784 
   2785     /// Attempted to change the signal stack while it was active.
   2786     PermissionDenied,
   2787 } || UnexpectedError;
   2788 
   2789 pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
   2790     if (builtin.os == .windows or builtin.os == .uefi or builtin.os == .wasi)
   2791         @compileError("std.os.sigaltstack not available for this target");
   2792 
   2793     switch (errno(system.sigaltstack(ss, old_ss))) {
   2794         0 => return,
   2795         EFAULT => unreachable,
   2796         EINVAL => unreachable,
   2797         ENOMEM => return error.SizeTooSmall,
   2798         EPERM => return error.PermissionDenied,
   2799         else => |err| return unexpectedErrno(err),
   2800     }
   2801 }
   2802 
   2803 /// Examine and change a signal action.
   2804 pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void {
   2805     switch (errno(system.sigaction(sig, act, oact))) {
   2806         0 => return,
   2807         EFAULT => unreachable,
   2808         EINVAL => unreachable,
   2809         else => unreachable,
   2810     }
   2811 }
   2812 
   2813 pub const FutimensError = error{
   2814     /// times is NULL, or both tv_nsec values are UTIME_NOW, and either:
   2815     /// *  the effective user ID of the caller does not match the  owner
   2816     ///    of  the  file,  the  caller does not have write access to the
   2817     ///    file, and the caller is not privileged (Linux: does not  have
   2818     ///    either  the  CAP_FOWNER  or the CAP_DAC_OVERRIDE capability);
   2819     ///    or,
   2820     /// *  the file is marked immutable (see chattr(1)).
   2821     AccessDenied,
   2822 
   2823     /// The caller attempted to change one or both timestamps to a value
   2824     /// other than the current time, or to change one of the  timestamps
   2825     /// to the current time while leaving the other timestamp unchanged,
   2826     /// (i.e., times is not NULL, neither tv_nsec  field  is  UTIME_NOW,
   2827     /// and neither tv_nsec field is UTIME_OMIT) and either:
   2828     /// *  the  caller's  effective  user ID does not match the owner of
   2829     ///    file, and the caller is not privileged (Linux: does not  have
   2830     ///    the CAP_FOWNER capability); or,
   2831     /// *  the file is marked append-only or immutable (see chattr(1)).
   2832     PermissionDenied,
   2833 
   2834     ReadOnlyFileSystem,
   2835 } || UnexpectedError;
   2836 
   2837 pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
   2838     switch (errno(system.futimens(fd, times))) {
   2839         0 => return,
   2840         EACCES => return error.AccessDenied,
   2841         EPERM => return error.PermissionDenied,
   2842         EBADF => unreachable, // always a race condition
   2843         EFAULT => unreachable,
   2844         EINVAL => unreachable,
   2845         EROFS => return error.ReadOnlyFileSystem,
   2846         else => |err| return unexpectedErrno(err),
   2847     }
   2848 }
   2849 
   2850 pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;
   2851 
   2852 pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
   2853     if (builtin.link_libc) {
   2854         switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
   2855             0 => return mem.toSlice(u8, name_buffer),
   2856             EFAULT => unreachable,
   2857             ENAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
   2858             EPERM => return error.PermissionDenied,
   2859             else => |err| return unexpectedErrno(err),
   2860         }
   2861     }
   2862     if (builtin.os == .linux) {
   2863         var uts: utsname = undefined;
   2864         switch (errno(system.uname(&uts))) {
   2865             0 => {
   2866                 const hostname = mem.toSlice(u8, &uts.nodename);
   2867                 mem.copy(u8, name_buffer, hostname);
   2868                 return name_buffer[0..hostname.len];
   2869             },
   2870             EFAULT => unreachable,
   2871             EPERM => return error.PermissionDenied,
   2872             else => |err| return unexpectedErrno(err),
   2873         }
   2874     }
   2875 
   2876     @compileError("TODO implement gethostname for this OS");
   2877 }
   2878 
   2879 pub fn res_mkquery(
   2880     op: u4,
   2881     dname: []const u8,
   2882     class: u8,
   2883     ty: u8,
   2884     data: []const u8,
   2885     newrr: ?[*]const u8,
   2886     buf: []u8,
   2887 ) usize {
   2888     // This implementation is ported from musl libc.
   2889     // A more idiomatic "ziggy" implementation would be welcome.
   2890     var name = dname;
   2891     if (mem.endsWith(u8, name, ".")) name.len -= 1;
   2892     assert(name.len <= 253);
   2893     const n = 17 + name.len + @boolToInt(name.len != 0);
   2894 
   2895     // Construct query template - ID will be filled later
   2896     var q: [280]u8 = undefined;
   2897     @memset(&q, 0, n);
   2898     q[2] = u8(op) * 8 + 1;
   2899     q[5] = 1;
   2900     mem.copy(u8, q[13..], name);
   2901     var i: usize = 13;
   2902     var j: usize = undefined;
   2903     while (q[i] != 0) : (i = j + 1) {
   2904         j = i;
   2905         while (q[j] != 0 and q[j] != '.') : (j += 1) {}
   2906         // TODO determine the circumstances for this and whether or
   2907         // not this should be an error.
   2908         if (j - i - 1 > 62) unreachable;
   2909         q[i - 1] = @intCast(u8, j - i);
   2910     }
   2911     q[i + 1] = ty;
   2912     q[i + 3] = class;
   2913 
   2914     // Make a reasonably unpredictable id
   2915     var ts: timespec = undefined;
   2916     clock_gettime(CLOCK_REALTIME, &ts) catch {};
   2917     const UInt = @IntType(false, @typeOf(ts.tv_nsec).bit_count);
   2918     const unsec = @bitCast(UInt, ts.tv_nsec);
   2919     const id = @truncate(u32, unsec + unsec / 65536);
   2920     q[0] = @truncate(u8, id / 256);
   2921     q[1] = @truncate(u8, id);
   2922 
   2923     mem.copy(u8, buf, q[0..n]);
   2924     return n;
   2925 }
   2926 
   2927 pub const SendError = error{
   2928     /// (For UNIX domain sockets, which are identified by pathname) Write permission is  denied
   2929     /// on  the destination socket file, or search permission is denied for one of the
   2930     /// directories the path prefix.  (See path_resolution(7).)
   2931     /// (For UDP sockets) An attempt was made to send to a network/broadcast address as  though
   2932     /// it was a unicast address.
   2933     AccessDenied,
   2934 
   2935     /// The socket is marked nonblocking and the requested operation would block, and
   2936     /// there is no global event loop configured.
   2937     /// It's also possible to get this error under the following condition:
   2938     /// (Internet  domain datagram sockets) The socket referred to by sockfd had not previously
   2939     /// been bound to an address and, upon attempting to bind it to an ephemeral port,  it  was
   2940     /// determined that all port numbers in the ephemeral port range are currently in use.  See
   2941     /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7).
   2942     WouldBlock,
   2943 
   2944     /// Another Fast Open is already in progress.
   2945     FastOpenAlreadyInProgress,
   2946 
   2947     /// Connection reset by peer.
   2948     ConnectionResetByPeer,
   2949 
   2950     /// The  socket  type requires that message be sent atomically, and the size of the message
   2951     /// to be sent made this impossible. The message is not transmitted.
   2952     ///
   2953     MessageTooBig,
   2954 
   2955     /// The output queue for a network interface was full.  This generally indicates  that  the
   2956     /// interface  has  stopped sending, but may be caused by transient congestion.  (Normally,
   2957     /// this does not occur in Linux.  Packets are just silently dropped when  a  device  queue
   2958     /// overflows.)
   2959     /// This is also caused when there is not enough kernel memory available.
   2960     SystemResources,
   2961 
   2962     /// The  local  end  has been shut down on a connection oriented socket.  In this case, the
   2963     /// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.
   2964     BrokenPipe,
   2965 } || UnexpectedError;
   2966 
   2967 /// Transmit a message to another socket.
   2968 ///
   2969 /// The `sendto` call may be used only when the socket is in a connected state (so that the intended
   2970 /// recipient  is  known). The  following call
   2971 ///
   2972 ///     send(sockfd, buf, len, flags);
   2973 ///
   2974 /// is equivalent to
   2975 ///
   2976 ///     sendto(sockfd, buf, len, flags, NULL, 0);
   2977 ///
   2978 /// If  sendto()  is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments
   2979 /// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted
   2980 /// that the socket was actually connected.
   2981 /// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying  its  size.
   2982 ///
   2983 /// If the message is too long to pass atomically through the underlying protocol,
   2984 /// `SendError.MessageTooBig` is returned, and the message is not transmitted.
   2985 ///
   2986 /// There is no  indication  of  failure  to  deliver.
   2987 ///
   2988 /// When the message does not fit into the send buffer of  the  socket,  `sendto`  normally  blocks,
   2989 /// unless  the socket has been placed in nonblocking I/O mode.  In nonblocking mode it would fail
   2990 /// with `SendError.WouldBlock`.  The `select` call may be used  to  determine when it is
   2991 /// possible to send more data.
   2992 pub fn sendto(
   2993     /// The file descriptor of the sending socket.
   2994     sockfd: fd_t,
   2995     /// Message to send.
   2996     buf: []const u8,
   2997     flags: u32,
   2998     dest_addr: ?*const sockaddr,
   2999     addrlen: socklen_t,
   3000 ) SendError!usize {
   3001     while (true) {
   3002         const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
   3003         switch (errno(rc)) {
   3004             0 => return @intCast(usize, rc),
   3005 
   3006             EACCES => return error.AccessDenied,
   3007             EAGAIN => if (std.event.Loop.instance) |loop| {
   3008                 loop.waitUntilFdWritable(sockfd);
   3009                 continue;
   3010             } else {
   3011                 return error.WouldBlock;
   3012             },
   3013             EALREADY => return error.FastOpenAlreadyInProgress,
   3014             EBADF => unreachable, // always a race condition
   3015             ECONNRESET => return error.ConnectionResetByPeer,
   3016             EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
   3017             EFAULT => unreachable, // An invalid user space address was specified for an argument.
   3018             EINTR => continue,
   3019             EINVAL => unreachable, // Invalid argument passed.
   3020             EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
   3021             EMSGSIZE => return error.MessageTooBig,
   3022             ENOBUFS => return error.SystemResources,
   3023             ENOMEM => return error.SystemResources,
   3024             ENOTCONN => unreachable, // The socket is not connected, and no target has been given.
   3025             ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
   3026             EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
   3027             EPIPE => return error.BrokenPipe,
   3028             else => |err| return unexpectedErrno(err),
   3029         }
   3030     }
   3031 }
   3032 
   3033 /// Transmit a message to another socket.
   3034 ///
   3035 /// The `send` call may be used only when the socket is in a connected state (so that the intended
   3036 /// recipient  is  known).   The  only  difference  between `send` and `write` is the presence of
   3037 /// flags.  With a zero flags argument, `send` is equivalent to  `write`.   Also,  the  following
   3038 /// call
   3039 ///
   3040 ///     send(sockfd, buf, len, flags);
   3041 ///
   3042 /// is equivalent to
   3043 ///
   3044 ///     sendto(sockfd, buf, len, flags, NULL, 0);
   3045 ///
   3046 /// There is no  indication  of  failure  to  deliver.
   3047 ///
   3048 /// When the message does not fit into the send buffer of  the  socket,  `send`  normally  blocks,
   3049 /// unless  the socket has been placed in nonblocking I/O mode.  In nonblocking mode it would fail
   3050 /// with `SendError.WouldBlock`.  The `select` call may be used  to  determine when it is
   3051 /// possible to send more data.
   3052 pub fn send(
   3053     /// The file descriptor of the sending socket.
   3054     sockfd: fd_t,
   3055     buf: []const u8,
   3056     flags: u32,
   3057 ) SendError!usize {
   3058     return sendto(sockfd, buf, flags, null, 0);
   3059 }
   3060 
   3061 pub const PollError = error{
   3062     /// The kernel had no space to allocate file descriptor tables.
   3063     SystemResources,
   3064 } || UnexpectedError;
   3065 
   3066 pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
   3067     while (true) {
   3068         const rc = system.poll(fds.ptr, fds.len, timeout);
   3069         switch (errno(rc)) {
   3070             0 => return @intCast(usize, rc),
   3071             EFAULT => unreachable,
   3072             EINTR => continue,
   3073             EINVAL => unreachable,
   3074             ENOMEM => return error.SystemResources,
   3075             else => |err| return unexpectedErrno(err),
   3076         }
   3077     }
   3078 }
   3079 
   3080 pub const RecvFromError = error{
   3081     /// The socket is marked nonblocking and the requested operation would block, and
   3082     /// there is no global event loop configured.
   3083     WouldBlock,
   3084 
   3085     /// A remote host refused to allow the network connection, typically because it is not
   3086     /// running the requested service.
   3087     ConnectionRefused,
   3088 
   3089     /// Could not allocate kernel memory.
   3090     SystemResources,
   3091 } || UnexpectedError;
   3092 
   3093 pub fn recvfrom(
   3094     sockfd: fd_t,
   3095     buf: []u8,
   3096     flags: u32,
   3097     src_addr: ?*sockaddr,
   3098     addrlen: ?*socklen_t,
   3099 ) RecvFromError!usize {
   3100     while (true) {
   3101         const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
   3102         switch (errno(rc)) {
   3103             0 => return @intCast(usize, rc),
   3104             EBADF => unreachable, // always a race condition
   3105             EFAULT => unreachable,
   3106             EINVAL => unreachable,
   3107             ENOTCONN => unreachable,
   3108             ENOTSOCK => unreachable,
   3109             EINTR => continue,
   3110             EAGAIN => if (std.event.Loop.instance) |loop| {
   3111                 loop.waitUntilFdReadable(sockfd);
   3112                 continue;
   3113             } else {
   3114                 return error.WouldBlock;
   3115             },
   3116             ENOMEM => return error.SystemResources,
   3117             ECONNREFUSED => return error.ConnectionRefused,
   3118             else => |err| return unexpectedErrno(err),
   3119         }
   3120     }
   3121 }
   3122 
   3123 pub const DnExpandError = error{InvalidDnsPacket};
   3124 
   3125 pub fn dn_expand(
   3126     msg: []const u8,
   3127     comp_dn: []const u8,
   3128     exp_dn: []u8,
   3129 ) DnExpandError!usize {
   3130     // This implementation is ported from musl libc.
   3131     // A more idiomatic "ziggy" implementation would be welcome.
   3132     var p = comp_dn.ptr;
   3133     var len: usize = std.math.maxInt(usize);
   3134     const end = msg.ptr + msg.len;
   3135     if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket;
   3136     var dest = exp_dn.ptr;
   3137     const dend = dest + std.math.min(exp_dn.len, 254);
   3138     // detect reference loop using an iteration counter
   3139     var i: usize = 0;
   3140     while (i < msg.len) : (i += 2) {
   3141         // loop invariants: p<end, dest<dend
   3142         if ((p[0] & 0xc0) != 0) {
   3143             if (p + 1 == end) return error.InvalidDnsPacket;
   3144             var j = ((p[0] & usize(0x3f)) << 8) | p[1];
   3145             if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 2 - @ptrToInt(comp_dn.ptr);
   3146             if (j >= msg.len) return error.InvalidDnsPacket;
   3147             p = msg.ptr + j;
   3148         } else if (p[0] != 0) {
   3149             if (dest != exp_dn.ptr) {
   3150                 dest.* = '.';
   3151                 dest += 1;
   3152             }
   3153             var j = p[0];
   3154             p += 1;
   3155             if (j >= @ptrToInt(end) - @ptrToInt(p) or j >= @ptrToInt(dend) - @ptrToInt(dest)) {
   3156                 return error.InvalidDnsPacket;
   3157             }
   3158             while (j != 0) {
   3159                 j -= 1;
   3160                 dest.* = p[0];
   3161                 dest += 1;
   3162                 p += 1;
   3163             }
   3164         } else {
   3165             dest.* = 0;
   3166             if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 1 - @ptrToInt(comp_dn.ptr);
   3167             return len;
   3168         }
   3169     }
   3170     return error.InvalidDnsPacket;
   3171 }
   3172 
   3173 pub fn sched_yield() void {
   3174     if (builtin.os == .windows) {
   3175         _ = windows.kernel32.SwitchToThread();
   3176     } else if (builtin.os == .linux and !builtin.link_libc) {
   3177         assert(linux.sched_yield() == 0);
   3178     } else if (builtin.link_libc) {
   3179         assert(std.c.sched_yield() == 0);
   3180     }
   3181 }