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