zig/lib/std / os.zig

This file contains thin wrappers around OS-specific APIs, with these specific goals in mind: * Convert "errno"-style error codes into Zig errors. * When null-terminated byte buffers are required, provide APIs which accept slices as well as APIs which accept null-terminated byte buffers. Same goes for UTF-16LE encoding. * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide cross platform abstracting. * When there exists a corresponding libc function and linking libc, the libc implementation is used. Exceptions are made for known buggy areas of libc. On Linux libc can be side-stepped by using std.os.linux directly. * For Windows, this file represents the API that libc would provide for Windows. For thin wrappers around Windows-specific APIs, see std.os.windows. Note: The Zig standard library does not support POSIX thread cancellation, and in general EINTR is handled by trying again.


const root = @import("root");
const std = @import("std.zig");
const builtin = @import("builtin");
const assert = std.debug.assert;
const math = std.math;
const mem = std.mem;
const elf = std.elf;
const fs = std.fs;
const dl = @import("dynamic_library.zig");
const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES;
const is_windows = builtin.os.tag == .windows;

darwin

pub const darwin = std.c;

dragonfly

pub const dragonfly = std.c;

freebsd

pub const freebsd = std.c;

haiku

pub const haiku = std.c;

netbsd

pub const netbsd = std.c;

openbsd

pub const openbsd = std.c;

solaris

pub const solaris = std.c;

illumos

pub const illumos = std.c;

linux

os/linux.zig
pub const linux = @import("os/linux.zig");

plan9

os/plan9.zig
pub const plan9 = @import("os/plan9.zig");

uefi

os/uefi.zig
pub const uefi = @import("os/uefi.zig");

wasi

os/wasi.zig
pub const wasi = @import("os/wasi.zig");

emscripten

os/emscripten.zig
pub const emscripten = @import("os/emscripten.zig");

windows

os/windows.zig
pub const windows = @import("os/windows.zig");

comptime {
    assert(@import("std") == std); // std lib tests require --zig-lib-dir
}

test {
    _ = darwin;
    _ = linux;
    if (builtin.os.tag == .uefi) {
        _ = uefi;
    }
    _ = wasi;
    _ = windows;

    _ = @import("os/test.zig");
}

system

Applications can override the system API layer in their root source file. Otherwise, when linking libc, this is the C API. When not linking libc, it is the OS-specific system interface.

pub const system = if (@hasDecl(root, "os") and root.os != @This())
    root.os.system
else if (builtin.link_libc or is_windows)
    std.c
else switch (builtin.os.tag) {
    .linux => linux,
    .plan9 => plan9,
    .wasi => wasi,
    .uefi => uefi,
    else => struct {},
};

AF

pub const AF = system.AF;

AF_SUN

pub const AF_SUN = system.AF_SUN;

ARCH

pub const ARCH = system.ARCH;

AT

pub const AT = system.AT;

AT_SUN

pub const AT_SUN = system.AT_SUN;

CLOCK

pub const CLOCK = system.CLOCK;

CPU_COUNT

pub const CPU_COUNT = system.CPU_COUNT;

CTL

pub const CTL = system.CTL;

DT

pub const DT = system.DT;

E

pub const E = system.E;

Elf_Symndx

pub const Elf_Symndx = system.Elf_Symndx;

F

pub const F = system.F;

FD_CLOEXEC

pub const FD_CLOEXEC = system.FD_CLOEXEC;

Flock

pub const Flock = system.Flock;

HOST_NAME_MAX

pub const HOST_NAME_MAX = system.HOST_NAME_MAX;

HW

pub const HW = switch (builtin.os.tag) {
    .openbsd => system.HW,
    else => .{},
};

IFNAMESIZE

pub const IFNAMESIZE = system.IFNAMESIZE;

IOV_MAX

pub const IOV_MAX = system.IOV_MAX;

IPPROTO

pub const IPPROTO = system.IPPROTO;

KERN

pub const KERN = system.KERN;

Kevent

pub const Kevent = system.Kevent;

LOCK

pub const LOCK = system.LOCK;

MADV

pub const MADV = system.MADV;

MAP

pub const MAP = system.MAP;

MSF

pub const MSF = system.MSF;

MAX_ADDR_LEN

pub const MAX_ADDR_LEN = system.MAX_ADDR_LEN;

MFD

pub const MFD = system.MFD;

MMAP2_UNIT

pub const MMAP2_UNIT = system.MMAP2_UNIT;

MSG

pub const MSG = system.MSG;

NAME_MAX

pub const NAME_MAX = system.NAME_MAX;

O

pub const O = switch (builtin.os.tag) {
    // We want to expose the POSIX-like OFLAGS, so we use std.c.wasi.O instead
    // of std.os.wasi.O, which is for non-POSIX-like `wasi.path_open`, etc.
    .wasi => std.c.O,
    else => system.O,
};

PATH_MAX

pub const PATH_MAX = system.PATH_MAX;

POLL

pub const POLL = system.POLL;

POSIX_FADV

pub const POSIX_FADV = system.POSIX_FADV;

PR

pub const PR = system.PR;

PROT

pub const PROT = system.PROT;

REG

pub const REG = system.REG;

RIGHT

pub const RIGHT = system.RIGHT;

RLIM

pub const RLIM = system.RLIM;

RR

pub const RR = system.RR;

S

pub const S = system.S;

SA

pub const SA = system.SA;

SC

pub const SC = system.SC;

_SC

pub const _SC = system._SC;

SEEK

pub const SEEK = system.SEEK;

SHUT

pub const SHUT = system.SHUT;

SIG

pub const SIG = system.SIG;

SIOCGIFINDEX

pub const SIOCGIFINDEX = system.SIOCGIFINDEX;

SO

pub const SO = system.SO;

SOCK

pub const SOCK = system.SOCK;

SOL

pub const SOL = system.SOL;

STDERR_FILENO

pub const STDERR_FILENO = system.STDERR_FILENO;

STDIN_FILENO

pub const STDIN_FILENO = system.STDIN_FILENO;

STDOUT_FILENO

pub const STDOUT_FILENO = system.STDOUT_FILENO;

SYS

pub const SYS = system.SYS;

Sigaction

pub const Sigaction = system.Sigaction;

Stat

pub const Stat = system.Stat;

TCSA

pub const TCSA = system.TCSA;

TCP

pub const TCP = system.TCP;

VDSO

pub const VDSO = system.VDSO;

W

pub const W = system.W;

addrinfo

pub const addrinfo = system.addrinfo;

blkcnt_t

pub const blkcnt_t = system.blkcnt_t;

blksize_t

pub const blksize_t = system.blksize_t;

clock_t

pub const clock_t = system.clock_t;

cpu_set_t

pub const cpu_set_t = system.cpu_set_t;

dev_t

pub const dev_t = system.dev_t;

dl_phdr_info

pub const dl_phdr_info = system.dl_phdr_info;

empty_sigset

pub const empty_sigset = system.empty_sigset;

filled_sigset

pub const filled_sigset = system.filled_sigset;

fd_t

pub const fd_t = system.fd_t;

fdflags_t

pub const fdflags_t = system.fdflags_t;

fdstat_t

pub const fdstat_t = system.fdstat_t;

gid_t

pub const gid_t = system.gid_t;

ifreq

pub const ifreq = system.ifreq;

ino_t

pub const ino_t = system.ino_t;

lookupflags_t

pub const lookupflags_t = system.lookupflags_t;

mcontext_t

pub const mcontext_t = system.mcontext_t;

mode_t

pub const mode_t = system.mode_t;

msghdr

pub const msghdr = system.msghdr;

msghdr_const

pub const msghdr_const = system.msghdr_const;

nfds_t

pub const nfds_t = system.nfds_t;

nlink_t

pub const nlink_t = system.nlink_t;

off_t

pub const off_t = system.off_t;

oflags_t

pub const oflags_t = system.oflags_t;

pid_t

pub const pid_t = system.pid_t;

pollfd

pub const pollfd = system.pollfd;

port_t

pub const port_t = system.port_t;

port_event

pub const port_event = system.port_event;

port_notify

pub const port_notify = system.port_notify;

file_obj

pub const file_obj = system.file_obj;

rights_t

pub const rights_t = system.rights_t;

rlim_t

pub const rlim_t = system.rlim_t;

rlimit

pub const rlimit = system.rlimit;

rlimit_resource

pub const rlimit_resource = system.rlimit_resource;

rusage

pub const rusage = system.rusage;

sa_family_t

pub const sa_family_t = system.sa_family_t;

siginfo_t

pub const siginfo_t = system.siginfo_t;

sigset_t

pub const sigset_t = system.sigset_t;

sockaddr

pub const sockaddr = system.sockaddr;

socklen_t

pub const socklen_t = system.socklen_t;

stack_t

pub const stack_t = system.stack_t;

tcflag_t

pub const tcflag_t = system.tcflag_t;

termios

pub const termios = system.termios;

time_t

pub const time_t = system.time_t;

timespec

pub const timespec = system.timespec;

timestamp_t

pub const timestamp_t = system.timestamp_t;

timeval

pub const timeval = system.timeval;

timezone

pub const timezone = system.timezone;

ucontext_t

pub const ucontext_t = system.ucontext_t;

uid_t

pub const uid_t = system.uid_t;

user_desc

pub const user_desc = system.user_desc;

utsname

pub const utsname = system.utsname;

F_OK

pub const F_OK = system.F_OK;

R_OK

pub const R_OK = system.R_OK;

W_OK

pub const W_OK = system.W_OK;

X_OK

pub const X_OK = system.X_OK;

iovec

pub const iovec = extern struct {
    iov_base: [*]u8,
    iov_len: usize,
};

iovec_const

pub const iovec_const = extern struct {
    iov_base: [*]const u8,
    iov_len: usize,
};

LOG

pub const LOG = struct {
    pub const EMERG = 0;
    pub const ALERT = 1;
    pub const CRIT = 2;
    pub const ERR = 3;
    pub const WARNING = 4;
    pub const NOTICE = 5;
    pub const INFO = 6;
    pub const DEBUG = 7;
};

RelativePathWasi

system is unusable action must be taken immediately critical conditions error conditions warning conditions normal but significant condition informational debug-level messages An fd-relative file path

This is currently only used for WASI-specific functionality, but the concept is the same as the dirfd/pathname pairs in the *at(...) POSIX functions.

pub const RelativePathWasi = struct {
    dir_fd: fd_t,
    relative_path: []const u8,
};

socket_t

Handle to directory Path to resource within dir_fd.

pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t;

pub var environ: [][*:0]u8 = undefined;

pub var argv: [][*:0]u8 = if (builtin.link_libc) undefined else switch (builtin.os.tag) {
    .windows => @compileError("argv isn't supported on Windows: use std.process.argsAlloc instead"),
    .wasi => @compileError("argv isn't supported on WASI: use std.process.argsAlloc instead"),
    else => undefined,
};

have_sigpipe_support

See also getenv. Populated by startup code before main(). TODO this is a footgun because the value will be undefined when using zig build-lib. https://github.com/ziglang/zig/issues/4524 Populated by startup code before main(). Not available on WASI or Windows without libc. See std.process.argsAlloc or std.process.argsWithAllocator for a cross-platform alternative.

pub const have_sigpipe_support = @hasDecl(@This(), "SIG") and @hasDecl(SIG, "PIPE");

fn noopSigHandler(_: c_int) callconv(.C) void {}

maybeIgnoreSigpipe()

On default executed by posix startup code before main(), if SIGPIPE is supported.

pub fn maybeIgnoreSigpipe() void {
    if (have_sigpipe_support and !std.options.keep_sigpipe) {
        const act = Sigaction{
            // We set handler to a noop function instead of SIG.IGN so we don't leak our
            // signal disposition to a child process
            .handler = .{ .handler = noopSigHandler },
            .mask = empty_sigset,
            .flags = 0,
        };
        sigaction(SIG.PIPE, &act, null) catch |err|
            std.debug.panic("failed to install noop SIGPIPE handler with '{s}'", .{@errorName(err)});
    }
}

errno

To obtain errno, call this function with the return value of the system function call. For some systems this will obtain the value directly from the return code; for others it will use a thread-local errno variable. Therefore, this function only returns a well-defined value when it is called directly after the system function call which one wants to learn the errno value of.

pub const errno = system.getErrno;

close()

Closes the file descriptor. This function is not capable of returning any indication of failure. An application which wants to ensure writes have succeeded before closing must call fsync before close. Note: The Zig standard library does not support POSIX thread cancellation.

pub fn close(fd: fd_t) void {
    if (builtin.os.tag == .windows) {
        return windows.CloseHandle(fd);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        _ = wasi.fd_close(fd);
        return;
    }
    if (comptime builtin.target.isDarwin()) {
        // This avoids the EINTR problem.
        switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) {
            .BADF => unreachable, // Always a race condition.
            else => return,
        }
    }
    switch (errno(system.close(fd))) {
        .BADF => unreachable, // Always a race condition.
        .INTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425
        else => return,
    }
}

FChmodError

pub const FChmodError = error{
    AccessDenied,
    InputOutput,
    SymLinkLoop,
    FileNotFound,
    SystemResources,
    ReadOnlyFileSystem,
} || UnexpectedError;

fchmod()

Changes the mode of the file referred to by the file descriptor. The process must have the correct privileges in order to do this successfully, or must have the effective user ID matching the owner of the file.

pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
    if (!std.fs.has_executable_bit) @compileError("fchmod unsupported by target OS");

    while (true) {
        const res = system.fchmod(fd, mode);

        switch (system.getErrno(res)) {
            .SUCCESS => return,
            .INTR => continue,
            .BADF => unreachable,
            .FAULT => unreachable,
            .INVAL => unreachable,
            .ACCES => return error.AccessDenied,
            .IO => return error.InputOutput,
            .LOOP => return error.SymLinkLoop,
            .NOENT => return error.FileNotFound,
            .NOMEM => return error.SystemResources,
            .NOTDIR => return error.FileNotFound,
            .PERM => return error.AccessDenied,
            .ROFS => return error.ReadOnlyFileSystem,
            else => |err| return unexpectedErrno(err),
        }
    }
}

const FChmodAtError = FChmodError || error{
    NameTooLong,
};

fchmodat()

pub fn fchmodat(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtError!void {
    if (!std.fs.has_executable_bit) @compileError("fchmodat unsupported by target OS");

    const path_c = try toPosixPath(path);

    while (true) {
        const res = system.fchmodat(dirfd, &path_c, mode, flags);

        switch (system.getErrno(res)) {
            .SUCCESS => return,
            .INTR => continue,
            .BADF => unreachable,
            .FAULT => unreachable,
            .INVAL => unreachable,
            .ACCES => return error.AccessDenied,
            .IO => return error.InputOutput,
            .LOOP => return error.SymLinkLoop,
            .NOENT => return error.FileNotFound,
            .NOMEM => return error.SystemResources,
            .NOTDIR => return error.FileNotFound,
            .PERM => return error.AccessDenied,
            .ROFS => return error.ReadOnlyFileSystem,
            else => |err| return unexpectedErrno(err),
        }
    }
}

FChownError

pub const FChownError = error{
    AccessDenied,
    InputOutput,
    SymLinkLoop,
    FileNotFound,
    SystemResources,
    ReadOnlyFileSystem,
} || UnexpectedError;

fchown()

Changes the owner and group of the file referred to by the file descriptor. The process must have the correct privileges in order to do this successfully. The group may be changed by the owner of the directory to any group of which the owner is a member. If the owner or group is specified as null, the ID is not changed.

pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {
    if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
        @compileError("Unsupported OS");

    while (true) {
        const res = system.fchown(fd, owner orelse @as(u32, 0) -% 1, group orelse @as(u32, 0) -% 1);

        switch (system.getErrno(res)) {
            .SUCCESS => return,
            .INTR => continue,
            .BADF => unreachable, // Can be reached if the fd refers to a non-iterable directory.

            .FAULT => unreachable,
            .INVAL => unreachable,
            .ACCES => return error.AccessDenied,
            .IO => return error.InputOutput,
            .LOOP => return error.SymLinkLoop,
            .NOENT => return error.FileNotFound,
            .NOMEM => return error.SystemResources,
            .NOTDIR => return error.FileNotFound,
            .PERM => return error.AccessDenied,
            .ROFS => return error.ReadOnlyFileSystem,
            else => |err| return unexpectedErrno(err),
        }
    }
}

RebootError

pub const RebootError = error{
    PermissionDenied,
} || UnexpectedError;

RebootCommand

pub const RebootCommand = switch (builtin.os.tag) {
    .linux => union(linux.LINUX_REBOOT.CMD) {
        RESTART: void,
        HALT: void,
        CAD_ON: void,
        CAD_OFF: void,
        POWER_OFF: void,
        RESTART2: [*:0]const u8,
        SW_SUSPEND: void,
        KEXEC: void,
    },
    else => @compileError("Unsupported OS"),
};

reboot()

pub fn reboot(cmd: RebootCommand) RebootError!void {
    switch (builtin.os.tag) {
        .linux => {
            switch (system.getErrno(linux.reboot(
                .MAGIC1,
                .MAGIC2,
                @as(linux.LINUX_REBOOT.CMD, cmd),
                switch (cmd) {
                    .RESTART2 => |s| s,
                    else => null,
                },
            ))) {
                .SUCCESS => {},
                .PERM => return error.PermissionDenied,
                else => |err| return std.os.unexpectedErrno(err),
            }
            switch (cmd) {
                .CAD_OFF => {},
                .CAD_ON => {},
                .SW_SUSPEND => {},

                .HALT => unreachable,
                .KEXEC => unreachable,
                .POWER_OFF => unreachable,
                .RESTART => unreachable,
                .RESTART2 => unreachable,
            }
        },
        else => @compileError("Unsupported OS"),
    }
}

GetRandomError

pub const GetRandomError = OpenError;

getrandom()

Obtain a series of random bytes. These bytes can be used to seed user-space random number generators or for cryptographic purposes. When linking against libc, this calls the appropriate OS-specific library call. Otherwise it uses the zig standard library implementation.

pub fn getrandom(buffer: []u8) GetRandomError!void {
    if (builtin.os.tag == .windows) {
        return windows.RtlGenRandom(buffer);
    }
    if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) {
        var buf = buffer;
        const use_c = builtin.os.tag != .linux or
            std.c.versionCheck(std.SemanticVersion{ .major = 2, .minor = 25, .patch = 0 }).ok;

        while (buf.len != 0) {
            const res = if (use_c) blk: {
                const rc = std.c.getrandom(buf.ptr, buf.len, 0);
                break :blk .{
                    .num_read = @as(usize, @bitCast(rc)),
                    .err = std.c.getErrno(rc),
                };
            } else blk: {
                const rc = linux.getrandom(buf.ptr, buf.len, 0);
                break :blk .{
                    .num_read = rc,
                    .err = linux.getErrno(rc),
                };
            };

            switch (res.err) {
                .SUCCESS => buf = buf[res.num_read..],
                .INVAL => unreachable,
                .FAULT => unreachable,
                .INTR => continue,
                .NOSYS => return getRandomBytesDevURandom(buf),
                else => return unexpectedErrno(res.err),
            }
        }
        return;
    }
    if (builtin.os.tag == .emscripten) {
        const err = std.c.getErrno(std.c.getentropy(buffer.ptr, buffer.len));
        switch (err) {
            .SUCCESS => return,
            else => return unexpectedErrno(err),
        }
    }
    switch (builtin.os.tag) {
        .netbsd, .openbsd, .macos, .ios, .tvos, .watchos => {
            system.arc4random_buf(buffer.ptr, buffer.len);
            return;
        },
        .wasi => switch (wasi.random_get(buffer.ptr, buffer.len)) {
            .SUCCESS => return,
            else => |err| return unexpectedErrno(err),
        },
        else => return getRandomBytesDevURandom(buffer),
    }
}

fn getRandomBytesDevURandom(buf: []u8) !void {
    const fd = try openZ("/dev/urandom", O.RDONLY | O.CLOEXEC, 0);
    defer close(fd);

    const st = try fstat(fd);
    if (!S.ISCHR(st.mode)) {
        return error.NoDevice;
    }

    const file = std.fs.File{
        .handle = fd,
        .capable_io_mode = .blocking,
        .intended_io_mode = .blocking,
    };
    const stream = file.reader();
    stream.readNoEof(buf) catch return error.Unexpected;
}

abort()

Causes abnormal process termination. If linking against libc, this calls the abort() libc function. Otherwise it raises SIGABRT followed by SIGKILL and finally lo Invokes the current signal handler for SIGABRT, if any.

pub fn abort() noreturn {
    @setCold(true);
    // MSVCRT abort() sometimes opens a popup window which is undesirable, so
    // even when linking libc on Windows we use our own abort implementation.
    // See https://github.com/ziglang/zig/issues/2071 for more details.
    if (builtin.os.tag == .windows) {
        if (builtin.mode == .Debug) {
            @breakpoint();
        }
        windows.kernel32.ExitProcess(3);
    }
    if (!builtin.link_libc and builtin.os.tag == .linux) {
        // The Linux man page says that the libc abort() function
        // "first unblocks the SIGABRT signal", but this is a footgun
        // for user-defined signal handlers that want to restore some state in
        // some program sections and crash in others.
        // So, the user-installed SIGABRT handler is run, if present.
        raise(SIG.ABRT) catch {};

        // Disable all signal handlers.
        sigprocmask(SIG.BLOCK, &linux.all_mask, null);

        // Only one thread may proceed to the rest of abort().
        if (!builtin.single_threaded) {
            const global = struct {
                var abort_entered: bool = false;
            };
            while (@cmpxchgWeak(bool, &global.abort_entered, false, true, .SeqCst, .SeqCst)) |_| {}
        }

        // Install default handler so that the tkill below will terminate.
        const sigact = Sigaction{
            .handler = .{ .handler = SIG.DFL },
            .mask = empty_sigset,
            .flags = 0,
        };
        sigaction(SIG.ABRT, &sigact, null) catch |err| switch (err) {
            error.OperationNotSupported => unreachable,
        };

        _ = linux.tkill(linux.gettid(), SIG.ABRT);

        const sigabrtmask: linux.sigset_t = [_]u32{0} ** 31 ++ [_]u32{1 << (SIG.ABRT - 1)};
        sigprocmask(SIG.UNBLOCK, &sigabrtmask, null);

        // Beyond this point should be unreachable.
        @as(*allowzero volatile u8, @ptrFromInt(0)).* = 0;
        raise(SIG.KILL) catch {};
        exit(127); // Pid 1 might not be signalled in some containers.
    }
    switch (builtin.os.tag) {
        .uefi, .wasi, .cuda, .amdhsa => @trap(),
        else => system.abort(),
    }
}

RaiseError

pub const RaiseError = UnexpectedError;

raise()

pub fn raise(sig: u8) RaiseError!void {
    if (builtin.link_libc) {
        switch (errno(system.raise(sig))) {
            .SUCCESS => return,
            else => |err| return unexpectedErrno(err),
        }
    }

    if (builtin.os.tag == .linux) {
        var set: sigset_t = undefined;
        // block application signals
        sigprocmask(SIG.BLOCK, &linux.app_mask, &set);

        const tid = linux.gettid();
        const rc = linux.tkill(tid, sig);

        // restore signal mask
        sigprocmask(SIG.SETMASK, &set, null);

        switch (errno(rc)) {
            .SUCCESS => return,
            else => |err| return unexpectedErrno(err),
        }
    }

    @compileError("std.os.raise unimplemented for this target");
}

KillError

pub const KillError = error{ ProcessNotFound, PermissionDenied } || UnexpectedError;

kill()

pub fn kill(pid: pid_t, sig: u8) KillError!void {
    switch (errno(system.kill(pid, sig))) {
        .SUCCESS => return,
        .INVAL => unreachable, // invalid signal
        .PERM => return error.PermissionDenied,
        .SRCH => return error.ProcessNotFound,
        else => |err| return unexpectedErrno(err),
    }
}

exit()

Exits the program cleanly with the specified status code.

pub fn exit(status: u8) noreturn {
    if (builtin.link_libc) {
        system.exit(status);
    }
    if (builtin.os.tag == .windows) {
        windows.kernel32.ExitProcess(status);
    }
    if (builtin.os.tag == .wasi) {
        wasi.proc_exit(status);
    }
    if (builtin.os.tag == .linux and !builtin.single_threaded) {
        linux.exit_group(status);
    }
    if (builtin.os.tag == .uefi) {
        // exit() is only available if exitBootServices() has not been called yet.
        // This call to exit should not fail, so we don't care about its return value.
        if (uefi.system_table.boot_services) |bs| {
            _ = bs.exit(uefi.handle, @as(uefi.Status, @enumFromInt(status)), 0, null);
        }
        // If we can't exit, reboot the system instead.
        uefi.system_table.runtime_services.resetSystem(uefi.tables.ResetType.ResetCold, @as(uefi.Status, @enumFromInt(status)), 0, null);
    }
    system.exit(status);
}

ReadError

pub const ReadError = error{
    InputOutput,
    SystemResources,
    IsDir,
    OperationAborted,
    BrokenPipe,
    ConnectionResetByPeer,
    ConnectionTimedOut,
    NotOpenForReading,

    // Windows only
    NetNameDeleted,

    WouldBlock,

    AccessDenied,
} || UnexpectedError;

read()

This error occurs when no global event loop is configured, and reading from the file descriptor would block. In WASI, this error occurs when the file descriptor does not hold the required rights to read from it. Returns the number of bytes that were read, which can be less than buf.len. If 0 bytes were read, that means EOF. If fd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received.

Linux has a limit on how many bytes may be transferred in one read call, which is 0x7ffff000 on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as well as stuffing the errno codes into the last 4096 values. This is noted on the read man page. The limit on Darwin is 0x7fffffff, trying to read more than that returns EINVAL. The corresponding POSIX limit is math.maxInt(isize).

pub fn read(fd: fd_t, buf: []u8) ReadError!usize {
    if (buf.len == 0) return 0;
    if (builtin.os.tag == .windows) {
        return windows.ReadFile(fd, buf, null, std.io.default_mode);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        const iovs = [1]iovec{iovec{
            .iov_base = buf.ptr,
            .iov_len = buf.len,
        }};

        var nread: usize = undefined;
        switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) {
            .SUCCESS => return nread,
            .INTR => unreachable,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => unreachable,
            .BADF => return error.NotOpenForReading, // Can be a race condition.
            .IO => return error.InputOutput,
            .ISDIR => return error.IsDir,
            .NOBUFS => return error.SystemResources,
            .NOMEM => return error.SystemResources,
            .CONNRESET => return error.ConnectionResetByPeer,
            .TIMEDOUT => return error.ConnectionTimedOut,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }

    // Prevents EINVAL.
    const max_count = switch (builtin.os.tag) {
        .linux => 0x7ffff000,
        .macos, .ios, .watchos, .tvos => math.maxInt(i32),
        else => math.maxInt(isize),
    };
    const adjusted_len = @min(max_count, buf.len);

    while (true) {
        const rc = system.read(fd, buf.ptr, adjusted_len);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .INTR => continue,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => return error.WouldBlock,
            .BADF => return error.NotOpenForReading, // Can be a race condition.
            .IO => return error.InputOutput,
            .ISDIR => return error.IsDir,
            .NOBUFS => return error.SystemResources,
            .NOMEM => return error.SystemResources,
            .CONNRESET => return error.ConnectionResetByPeer,
            .TIMEDOUT => return error.ConnectionTimedOut,
            else => |err| return unexpectedErrno(err),
        }
    }
}

readv()

Number of bytes read is returned. Upon reading end-of-file, zero is returned.

For POSIX systems, if fd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received. On Windows, if the application has a global event loop enabled, I/O Completion Ports are used to perform the I/O. error.WouldBlock is not possible on Windows.

This operation is non-atomic on the following systems: * Windows On these systems, the read races with concurrent writes to the same file descriptor.

This function assumes that all vectors, including zero-length vectors, have a pointer within the address space of the application.

pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize {
    if (builtin.os.tag == .windows) {
        // TODO improve this to use ReadFileScatter
        if (iov.len == 0) return @as(usize, 0);
        const first = iov[0];
        return read(fd, first.iov_base[0..first.iov_len]);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var nread: usize = undefined;
        switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) {
            .SUCCESS => return nread,
            .INTR => unreachable,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => unreachable, // currently not support in WASI
            .BADF => return error.NotOpenForReading, // can be a race condition
            .IO => return error.InputOutput,
            .ISDIR => return error.IsDir,
            .NOBUFS => return error.SystemResources,
            .NOMEM => return error.SystemResources,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }
    const iov_count = math.cast(u31, iov.len) orelse math.maxInt(u31);
    while (true) {
        // TODO handle the case when iov_len is too large and get rid of this @intCast
        const rc = system.readv(fd, iov.ptr, iov_count);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .INTR => continue,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => return error.WouldBlock,
            .BADF => return error.NotOpenForReading, // can be a race condition
            .IO => return error.InputOutput,
            .ISDIR => return error.IsDir,
            .NOBUFS => return error.SystemResources,
            .NOMEM => return error.SystemResources,
            .CONNRESET => return error.ConnectionResetByPeer,
            else => |err| return unexpectedErrno(err),
        }
    }
}

PReadError

pub const PReadError = ReadError || error{Unseekable};

pread()

Number of bytes read is returned. Upon reading end-of-file, zero is returned.

Retries when interrupted by a signal.

For POSIX systems, if fd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received. On Windows, if the application has a global event loop enabled, I/O Completion Ports are used to perform the I/O. error.WouldBlock is not possible on Windows.

Linux has a limit on how many bytes may be transferred in one pread call, which is 0x7ffff000 on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as well as stuffing the errno codes into the last 4096 values. This is noted on the read man page. The limit on Darwin is 0x7fffffff, trying to read more than that returns EINVAL. The corresponding POSIX limit is math.maxInt(isize).

pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize {
    if (buf.len == 0) return 0;
    if (builtin.os.tag == .windows) {
        return windows.ReadFile(fd, buf, offset, std.io.default_mode);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        const iovs = [1]iovec{iovec{
            .iov_base = buf.ptr,
            .iov_len = buf.len,
        }};

        var nread: usize = undefined;
        switch (wasi.fd_pread(fd, &iovs, iovs.len, offset, &nread)) {
            .SUCCESS => return nread,
            .INTR => unreachable,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => unreachable,
            .BADF => return error.NotOpenForReading, // Can be a race condition.
            .IO => return error.InputOutput,
            .ISDIR => return error.IsDir,
            .NOBUFS => return error.SystemResources,
            .NOMEM => return error.SystemResources,
            .CONNRESET => return error.ConnectionResetByPeer,
            .NXIO => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }

    // Prevent EINVAL.
    const max_count = switch (builtin.os.tag) {
        .linux => 0x7ffff000,
        .macos, .ios, .watchos, .tvos => math.maxInt(i32),
        else => math.maxInt(isize),
    };
    const adjusted_len = @min(max_count, buf.len);

    const pread_sym = if (lfs64_abi) system.pread64 else system.pread;

    const ioffset = @as(i64, @bitCast(offset)); // the OS treats this as unsigned
    while (true) {
        const rc = pread_sym(fd, buf.ptr, adjusted_len, ioffset);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .INTR => continue,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => return error.WouldBlock,
            .BADF => return error.NotOpenForReading, // Can be a race condition.
            .IO => return error.InputOutput,
            .ISDIR => return error.IsDir,
            .NOBUFS => return error.SystemResources,
            .NOMEM => return error.SystemResources,
            .CONNRESET => return error.ConnectionResetByPeer,
            .NXIO => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            else => |err| return unexpectedErrno(err),
        }
    }
}

TruncateError

pub const TruncateError = error{
    FileTooBig,
    InputOutput,
    FileBusy,

    AccessDenied,
} || UnexpectedError;

ftruncate()

In WASI, this error occurs when the file descriptor does not hold the required rights to call ftruncate on it.

pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
    if (builtin.os.tag == .windows) {
        var io_status_block: windows.IO_STATUS_BLOCK = undefined;
        var eof_info = windows.FILE_END_OF_FILE_INFORMATION{
            .EndOfFile = @as(windows.LARGE_INTEGER, @bitCast(length)),
        };

        const rc = windows.ntdll.NtSetInformationFile(
            fd,
            &io_status_block,
            &eof_info,
            @sizeOf(windows.FILE_END_OF_FILE_INFORMATION),
            .FileEndOfFileInformation,
        );

        switch (rc) {
            .SUCCESS => return,
            .INVALID_HANDLE => unreachable, // Handle not open for writing
            .ACCESS_DENIED => return error.AccessDenied,
            else => return windows.unexpectedStatus(rc),
        }
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        switch (wasi.fd_filestat_set_size(fd, length)) {
            .SUCCESS => return,
            .INTR => unreachable,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .PERM => return error.AccessDenied,
            .TXTBSY => return error.FileBusy,
            .BADF => unreachable, // Handle not open for writing
            .INVAL => unreachable, // Handle not open for writing
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }

    while (true) {
        const ftruncate_sym = if (lfs64_abi) system.ftruncate64 else system.ftruncate;

        const ilen = @as(i64, @bitCast(length)); // the OS treats this as unsigned
        switch (errno(ftruncate_sym(fd, ilen))) {
            .SUCCESS => return,
            .INTR => continue,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .PERM => return error.AccessDenied,
            .TXTBSY => return error.FileBusy,
            .BADF => unreachable, // Handle not open for writing
            .INVAL => unreachable, // Handle not open for writing
            else => |err| return unexpectedErrno(err),
        }
    }
}

preadv()

Number of bytes read is returned. Upon reading end-of-file, zero is returned.

Retries when interrupted by a signal.

For POSIX systems, if fd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received. On Windows, if the application has a global event loop enabled, I/O Completion Ports are used to perform the I/O. error.WouldBlock is not possible on Windows.

This operation is non-atomic on the following systems: * Darwin * Windows On these systems, the read races with concurrent writes to the same file descriptor.

pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize {
    const have_pread_but_not_preadv = switch (builtin.os.tag) {
        .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
        else => false,
    };
    if (have_pread_but_not_preadv) {
        // We could loop here; but proper usage of `preadv` must handle partial reads anyway.
        // So we simply read into the first vector only.
        if (iov.len == 0) return 0;
        const first = iov[0];
        return pread(fd, first.iov_base[0..first.iov_len], offset);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var nread: usize = undefined;
        switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) {
            .SUCCESS => return nread,
            .INTR => unreachable,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => unreachable,
            .BADF => return error.NotOpenForReading, // can be a race condition
            .IO => return error.InputOutput,
            .ISDIR => return error.IsDir,
            .NOBUFS => return error.SystemResources,
            .NOMEM => return error.SystemResources,
            .NXIO => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }

    const iov_count = math.cast(u31, iov.len) orelse math.maxInt(u31);

    const preadv_sym = if (lfs64_abi) system.preadv64 else system.preadv;

    const ioffset = @as(i64, @bitCast(offset)); // the OS treats this as unsigned
    while (true) {
        const rc = preadv_sym(fd, iov.ptr, iov_count, ioffset);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @bitCast(rc)),
            .INTR => continue,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => return error.WouldBlock,
            .BADF => return error.NotOpenForReading, // can be a race condition
            .IO => return error.InputOutput,
            .ISDIR => return error.IsDir,
            .NOBUFS => return error.SystemResources,
            .NOMEM => return error.SystemResources,
            .NXIO => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            else => |err| return unexpectedErrno(err),
        }
    }
}

WriteError

pub const WriteError = error{
    DiskQuota,
    FileTooBig,
    InputOutput,
    NoSpaceLeft,
    DeviceBusy,
    InvalidArgument,

    AccessDenied,
    BrokenPipe,
    SystemResources,
    OperationAborted,
    NotOpenForWriting,

    LockViolation,

    WouldBlock,

    ConnectionResetByPeer,
} || UnexpectedError;

write()

In WASI, this error may occur when the file descriptor does not hold the required rights to write to it. The process cannot access the file because another process has locked a portion of the file. Windows-only. This error occurs when no global event loop is configured, and reading from the file descriptor would block. Connection reset by peer. Write to a file descriptor. Retries when interrupted by a signal. Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.

Note that a successful write() may transfer fewer than count bytes. Such partial writes can occur for various reasons; for example, because there was insufficient space on the disk device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or similar was interrupted by a signal handler after it had transferred some, but before it had transferred all of the requested bytes. In the event of a partial write, the caller can make another write() call to transfer the remaining bytes. The subsequent call will either transfer further bytes or may result in an error (e.g., if the disk is now full).

For POSIX systems, if fd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received. On Windows, if the application has a global event loop enabled, I/O Completion Ports are used to perform the I/O. error.WouldBlock is not possible on Windows.

Linux has a limit on how many bytes may be transferred in one write call, which is 0x7ffff000 on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as well as stuffing the errno codes into the last 4096 values. This is noted on the write man page. The limit on Darwin is 0x7fffffff, trying to read more than that returns EINVAL. The corresponding POSIX limit is math.maxInt(isize).

pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize {
    if (bytes.len == 0) return 0;
    if (builtin.os.tag == .windows) {
        return windows.WriteFile(fd, bytes, null, std.io.default_mode);
    }

    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        const ciovs = [_]iovec_const{iovec_const{
            .iov_base = bytes.ptr,
            .iov_len = bytes.len,
        }};
        var nwritten: usize = undefined;
        switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) {
            .SUCCESS => return nwritten,
            .INTR => unreachable,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => unreachable,
            .BADF => return error.NotOpenForWriting, // can be a race condition.
            .DESTADDRREQ => unreachable, // `connect` was never called.
            .DQUOT => return error.DiskQuota,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .NOSPC => return error.NoSpaceLeft,
            .PERM => return error.AccessDenied,
            .PIPE => return error.BrokenPipe,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }

    const max_count = switch (builtin.os.tag) {
        .linux => 0x7ffff000,
        .macos, .ios, .watchos, .tvos => math.maxInt(i32),
        else => math.maxInt(isize),
    };
    const adjusted_len = @min(max_count, bytes.len);

    while (true) {
        const rc = system.write(fd, bytes.ptr, adjusted_len);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .INTR => continue,
            .INVAL => return error.InvalidArgument,
            .FAULT => unreachable,
            .AGAIN => return error.WouldBlock,
            .BADF => return error.NotOpenForWriting, // can be a race condition.
            .DESTADDRREQ => unreachable, // `connect` was never called.
            .DQUOT => return error.DiskQuota,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .NOSPC => return error.NoSpaceLeft,
            .PERM => return error.AccessDenied,
            .PIPE => return error.BrokenPipe,
            .CONNRESET => return error.ConnectionResetByPeer,
            .BUSY => return error.DeviceBusy,
            else => |err| return unexpectedErrno(err),
        }
    }
}

writev()

Write multiple buffers to a file descriptor. Retries when interrupted by a signal. Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.

Note that a successful write() may transfer fewer bytes than supplied. Such partial writes can occur for various reasons; for example, because there was insufficient space on the disk device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or similar was interrupted by a signal handler after it had transferred some, but before it had transferred all of the requested bytes. In the event of a partial write, the caller can make another write() call to transfer the remaining bytes. The subsequent call will either transfer further bytes or may result in an error (e.g., if the disk is now full).

For POSIX systems, if fd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received. On Windows, if the application has a global event loop enabled, I/O Completion Ports are used to perform the I/O. error.WouldBlock is not possible on Windows.

If iov.len is larger than IOV_MAX, a partial write will occur.

This function assumes that all vectors, including zero-length vectors, have a pointer within the address space of the application.

pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize {
    if (builtin.os.tag == .windows) {
        // TODO improve this to use WriteFileScatter
        if (iov.len == 0) return @as(usize, 0);
        const first = iov[0];
        return write(fd, first.iov_base[0..first.iov_len]);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var nwritten: usize = undefined;
        switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) {
            .SUCCESS => return nwritten,
            .INTR => unreachable,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => unreachable,
            .BADF => return error.NotOpenForWriting, // can be a race condition.
            .DESTADDRREQ => unreachable, // `connect` was never called.
            .DQUOT => return error.DiskQuota,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .NOSPC => return error.NoSpaceLeft,
            .PERM => return error.AccessDenied,
            .PIPE => return error.BrokenPipe,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }

    const iov_count = if (iov.len > IOV_MAX) IOV_MAX else @as(u31, @intCast(iov.len));
    while (true) {
        const rc = system.writev(fd, iov.ptr, iov_count);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .INTR => continue,
            .INVAL => return error.InvalidArgument,
            .FAULT => unreachable,
            .AGAIN => return error.WouldBlock,
            .BADF => return error.NotOpenForWriting, // Can be a race condition.
            .DESTADDRREQ => unreachable, // `connect` was never called.
            .DQUOT => return error.DiskQuota,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .NOSPC => return error.NoSpaceLeft,
            .PERM => return error.AccessDenied,
            .PIPE => return error.BrokenPipe,
            .CONNRESET => return error.ConnectionResetByPeer,
            .BUSY => return error.DeviceBusy,
            else => |err| return unexpectedErrno(err),
        }
    }
}

PWriteError

pub const PWriteError = WriteError || error{Unseekable};

pwrite()

Write to a file descriptor, with a position offset. Retries when interrupted by a signal. Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.

Note that a successful write() may transfer fewer bytes than supplied. Such partial writes can occur for various reasons; for example, because there was insufficient space on the disk device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or similar was interrupted by a signal handler after it had transferred some, but before it had transferred all of the requested bytes. In the event of a partial write, the caller can make another write() call to transfer the remaining bytes. The subsequent call will either transfer further bytes or may result in an error (e.g., if the disk is now full).

For POSIX systems, if fd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received. On Windows, if the application has a global event loop enabled, I/O Completion Ports are used to perform the I/O. error.WouldBlock is not possible on Windows.

Linux has a limit on how many bytes may be transferred in one pwrite call, which is 0x7ffff000 on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as well as stuffing the errno codes into the last 4096 values. This is noted on the write man page. The limit on Darwin is 0x7fffffff, trying to write more than that returns EINVAL. The corresponding POSIX limit is math.maxInt(isize).

pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize {
    if (bytes.len == 0) return 0;
    if (builtin.os.tag == .windows) {
        return windows.WriteFile(fd, bytes, offset, std.io.default_mode);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        const ciovs = [1]iovec_const{iovec_const{
            .iov_base = bytes.ptr,
            .iov_len = bytes.len,
        }};

        var nwritten: usize = undefined;
        switch (wasi.fd_pwrite(fd, &ciovs, ciovs.len, offset, &nwritten)) {
            .SUCCESS => return nwritten,
            .INTR => unreachable,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => unreachable,
            .BADF => return error.NotOpenForWriting, // can be a race condition.
            .DESTADDRREQ => unreachable, // `connect` was never called.
            .DQUOT => return error.DiskQuota,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .NOSPC => return error.NoSpaceLeft,
            .PERM => return error.AccessDenied,
            .PIPE => return error.BrokenPipe,
            .NXIO => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }

    // Prevent EINVAL.
    const max_count = switch (builtin.os.tag) {
        .linux => 0x7ffff000,
        .macos, .ios, .watchos, .tvos => math.maxInt(i32),
        else => math.maxInt(isize),
    };
    const adjusted_len = @min(max_count, bytes.len);

    const pwrite_sym = if (lfs64_abi) system.pwrite64 else system.pwrite;

    const ioffset = @as(i64, @bitCast(offset)); // the OS treats this as unsigned
    while (true) {
        const rc = pwrite_sym(fd, bytes.ptr, adjusted_len, ioffset);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .INTR => continue,
            .INVAL => return error.InvalidArgument,
            .FAULT => unreachable,
            .AGAIN => return error.WouldBlock,
            .BADF => return error.NotOpenForWriting, // Can be a race condition.
            .DESTADDRREQ => unreachable, // `connect` was never called.
            .DQUOT => return error.DiskQuota,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .NOSPC => return error.NoSpaceLeft,
            .PERM => return error.AccessDenied,
            .PIPE => return error.BrokenPipe,
            .NXIO => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .BUSY => return error.DeviceBusy,
            else => |err| return unexpectedErrno(err),
        }
    }
}

pwritev()

Write multiple buffers to a file descriptor, with a position offset. Retries when interrupted by a signal. Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero.

Note that a successful write() may transfer fewer than count bytes. Such partial writes can occur for various reasons; for example, because there was insufficient space on the disk device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or similar was interrupted by a signal handler after it had transferred some, but before it had transferred all of the requested bytes. In the event of a partial write, the caller can make another write() call to transfer the remaining bytes. The subsequent call will either transfer further bytes or may result in an error (e.g., if the disk is now full).

If fd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received.

The following systems do not have this syscall, and will return partial writes if more than one vector is provided: * Darwin * Windows

If iov.len is larger than IOV_MAX, a partial write will occur.

pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize {
    const have_pwrite_but_not_pwritev = switch (builtin.os.tag) {
        .windows, .macos, .ios, .watchos, .tvos, .haiku => true,
        else => false,
    };

    if (have_pwrite_but_not_pwritev) {
        // We could loop here; but proper usage of `pwritev` must handle partial writes anyway.
        // So we simply write the first vector only.
        if (iov.len == 0) return @as(usize, 0);
        const first = iov[0];
        return pwrite(fd, first.iov_base[0..first.iov_len], offset);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var nwritten: usize = undefined;
        switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) {
            .SUCCESS => return nwritten,
            .INTR => unreachable,
            .INVAL => unreachable,
            .FAULT => unreachable,
            .AGAIN => unreachable,
            .BADF => return error.NotOpenForWriting, // Can be a race condition.
            .DESTADDRREQ => unreachable, // `connect` was never called.
            .DQUOT => return error.DiskQuota,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .NOSPC => return error.NoSpaceLeft,
            .PERM => return error.AccessDenied,
            .PIPE => return error.BrokenPipe,
            .NXIO => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }

    const pwritev_sym = if (lfs64_abi) system.pwritev64 else system.pwritev;

    const iov_count = if (iov.len > IOV_MAX) IOV_MAX else @as(u31, @intCast(iov.len));
    const ioffset = @as(i64, @bitCast(offset)); // the OS treats this as unsigned
    while (true) {
        const rc = pwritev_sym(fd, iov.ptr, iov_count, ioffset);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .INTR => continue,
            .INVAL => return error.InvalidArgument,
            .FAULT => unreachable,
            .AGAIN => return error.WouldBlock,
            .BADF => return error.NotOpenForWriting, // Can be a race condition.
            .DESTADDRREQ => unreachable, // `connect` was never called.
            .DQUOT => return error.DiskQuota,
            .FBIG => return error.FileTooBig,
            .IO => return error.InputOutput,
            .NOSPC => return error.NoSpaceLeft,
            .PERM => return error.AccessDenied,
            .PIPE => return error.BrokenPipe,
            .NXIO => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .BUSY => return error.DeviceBusy,
            else => |err| return unexpectedErrno(err),
        }
    }
}

OpenError

pub const OpenError = error{
    InvalidHandle,

    AccessDenied,
    SymLinkLoop,
    ProcessFdQuotaExceeded,
    SystemFdQuotaExceeded,
    NoDevice,
    FileNotFound,

    NameTooLong,

    SystemResources,

    FileTooBig,

    IsDir,

    NoSpaceLeft,

    NotDir,

    PathAlreadyExists,
    DeviceBusy,

    FileLocksNotSupported,

    BadPathName,
    InvalidUtf8,

    NetworkNotFound,

    FileBusy,

    WouldBlock,
} || UnexpectedError;

open()

In WASI, this error may occur when the provided file handle is invalid. In WASI, this error may occur when the file descriptor does not hold the required rights to open a new resource relative to it. The path exceeded MAX_PATH_BYTES bytes. Insufficient kernel memory was available, or the named file is a FIFO and per-user hard limit on memory allocation for pipes has been reached. The file is too large to be opened. This error is unreachable for 64-bit targets, as well as when opening directories. The path refers to directory but the O.DIRECTORY flag was not provided. A new path cannot be created because the device has no room for the new file. This error is only reachable when the O.CREAT flag is provided. A component used as a directory in the path was not, in fact, a directory, or O.DIRECTORY was specified and the path was not a directory. The path already exists and the O.CREAT and O.EXCL flags were provided. The underlying filesystem does not support file locks On Windows, \\server or \\server\share was not found. One of these three things: * pathname refers to an executable image which is currently being executed and write access was requested. * pathname refers to a file that is currently in use as a swap file, and the O_TRUNC flag was specified. * pathname refers to a file that is currently being read by the kernel (e.g., for module/firmware loading), and write access was requested. Open and possibly create a file. Keeps trying if it gets interrupted. See also openZ.

pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t {
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
        return openW(file_path_w.span(), flags, perm);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return openat(wasi.AT.FDCWD, file_path, flags, perm);
    }
    const file_path_c = try toPosixPath(file_path);
    return openZ(&file_path_c, flags, perm);
}

openZ()

Open and possibly create a file. Keeps trying if it gets interrupted. See also open.

pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t {
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
        return openW(file_path_w.span(), flags, perm);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return open(mem.sliceTo(file_path, 0), flags, perm);
    }

    const open_sym = if (lfs64_abi) system.open64 else system.open;

    while (true) {
        const rc = open_sym(file_path, flags, perm);
        switch (errno(rc)) {
            .SUCCESS => return @as(fd_t, @intCast(rc)),
            .INTR => continue,

            .FAULT => unreachable,
            .INVAL => unreachable,
            .ACCES => return error.AccessDenied,
            .FBIG => return error.FileTooBig,
            .OVERFLOW => return error.FileTooBig,
            .ISDIR => return error.IsDir,
            .LOOP => return error.SymLinkLoop,
            .MFILE => return error.ProcessFdQuotaExceeded,
            .NAMETOOLONG => return error.NameTooLong,
            .NFILE => return error.SystemFdQuotaExceeded,
            .NODEV => return error.NoDevice,
            .NOENT => return error.FileNotFound,
            .NOMEM => return error.SystemResources,
            .NOSPC => return error.NoSpaceLeft,
            .NOTDIR => return error.NotDir,
            .PERM => return error.AccessDenied,
            .EXIST => return error.PathAlreadyExists,
            .BUSY => return error.DeviceBusy,
            else => |err| return unexpectedErrno(err),
        }
    }
}

fn openOptionsFromFlagsWindows(flags: u32) windows.OpenFileOptions {
    const w = windows;

    var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE;
    if (flags & O.RDWR != 0) {
        access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
    } else if (flags & O.WRONLY != 0) {
        access_mask |= w.GENERIC_WRITE;
    } else {
        access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
    }

    const filter: windows.OpenFileOptions.Filter = if (flags & O.DIRECTORY != 0) .dir_only else .file_only;
    const follow_symlinks: bool = flags & O.NOFOLLOW == 0;

    const creation: w.ULONG = blk: {
        if (flags & O.CREAT != 0) {
            if (flags & O.EXCL != 0) {
                break :blk w.FILE_CREATE;
            }
        }
        break :blk w.FILE_OPEN;
    };

    return .{
        .access_mask = access_mask,
        .io_mode = .blocking,
        .creation = creation,
        .filter = filter,
        .follow_symlinks = follow_symlinks,
    };
}

openW()

Windows-only. The path parameter is [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. Translates the POSIX open API call to a Windows API call. TODO currently, this function does not handle all flag combinations or makes use of perm argument.

pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t {
    _ = perm;
    var options = openOptionsFromFlagsWindows(flags);
    options.dir = std.fs.cwd().fd;
    return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
        error.WouldBlock => unreachable,
        error.PipeBusy => unreachable,
        else => |e| return e,
    };
}

openat()

Open and possibly create a file. Keeps trying if it gets interrupted. file_path is relative to the open directory handle dir_fd. See also openatZ.

pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.sliceToPrefixedFileW(dir_fd, file_path);
        return openatW(dir_fd, file_path_w.span(), flags, mode);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        // `mode` is ignored on WASI, which does not support unix-style file permissions
        const opts = try openOptionsFromFlagsWasi(dir_fd, flags);
        const fd = try openatWasi(
            dir_fd,
            file_path,
            opts.lookup_flags,
            opts.oflags,
            opts.fs_flags,
            opts.fs_rights_base,
            opts.fs_rights_inheriting,
        );
        errdefer close(fd);

        if (flags & O.WRONLY != 0) {
            const info = try fstat(fd);
            if (info.filetype == .DIRECTORY)
                return error.IsDir;
        }

        return fd;
    }
    const file_path_c = try toPosixPath(file_path);
    return openatZ(dir_fd, &file_path_c, flags, mode);
}

const WasiOpenOptions = struct {
    oflags: wasi.oflags_t,
    lookup_flags: wasi.lookupflags_t,
    fs_rights_base: wasi.rights_t,
    fs_rights_inheriting: wasi.rights_t,
    fs_flags: wasi.fdflags_t,
};

fn openOptionsFromFlagsWasi(fd: fd_t, oflag: u32) OpenError!WasiOpenOptions {
    const w = std.os.wasi;

    // First, discover the rights that we can derive from `fd`
    var fsb_cur: wasi.fdstat_t = undefined;
    _ = switch (w.fd_fdstat_get(fd, &fsb_cur)) {
        .SUCCESS => .{},
        .BADF => return error.InvalidHandle,
        else => |err| return unexpectedErrno(err),
    };

    // Next, calculate the read/write rights to request, depending on the
    // provided POSIX access mode
    var rights: w.rights_t = 0;
    if (oflag & O.RDONLY != 0) {
        rights |= w.RIGHT.FD_READ | w.RIGHT.FD_READDIR;
    }
    if (oflag & O.WRONLY != 0) {
        rights |= w.RIGHT.FD_DATASYNC | w.RIGHT.FD_WRITE |
            w.RIGHT.FD_ALLOCATE | w.RIGHT.FD_FILESTAT_SET_SIZE;
    }

    // Request all other rights unconditionally
    rights |= ~(w.RIGHT.FD_DATASYNC | w.RIGHT.FD_READ |
        w.RIGHT.FD_WRITE | w.RIGHT.FD_ALLOCATE |
        w.RIGHT.FD_READDIR | w.RIGHT.FD_FILESTAT_SET_SIZE);

    // But only take rights that we can actually inherit
    rights &= fsb_cur.fs_rights_inheriting;

    return WasiOpenOptions{
        .oflags = @as(w.oflags_t, @truncate((oflag >> 12))) & 0xfff,
        .lookup_flags = if (oflag & O.NOFOLLOW == 0) w.LOOKUP_SYMLINK_FOLLOW else 0,
        .fs_rights_base = rights,
        .fs_rights_inheriting = fsb_cur.fs_rights_inheriting,
        .fs_flags = @as(w.fdflags_t, @truncate(oflag & 0xfff)),
    };
}

openatWasi()

A struct to contain all lookup/rights flags accepted by wasi.path_open Compute rights + flags corresponding to the provided POSIX access mode. Open and possibly create a file in WASI.

pub fn openatWasi(
    dir_fd: fd_t,
    file_path: []const u8,
    lookup_flags: lookupflags_t,
    oflags: oflags_t,
    fdflags: fdflags_t,
    base: rights_t,
    inheriting: rights_t,
) OpenError!fd_t {
    while (true) {
        var fd: fd_t = undefined;
        switch (wasi.path_open(dir_fd, lookup_flags, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) {
            .SUCCESS => return fd,
            .INTR => continue,

            .FAULT => unreachable,
            .INVAL => unreachable,
            .ACCES => return error.AccessDenied,
            .FBIG => return error.FileTooBig,
            .OVERFLOW => return error.FileTooBig,
            .ISDIR => return error.IsDir,
            .LOOP => return error.SymLinkLoop,
            .MFILE => return error.ProcessFdQuotaExceeded,
            .NAMETOOLONG => return error.NameTooLong,
            .NFILE => return error.SystemFdQuotaExceeded,
            .NODEV => return error.NoDevice,
            .NOENT => return error.FileNotFound,
            .NOMEM => return error.SystemResources,
            .NOSPC => return error.NoSpaceLeft,
            .NOTDIR => return error.NotDir,
            .PERM => return error.AccessDenied,
            .EXIST => return error.PathAlreadyExists,
            .BUSY => return error.DeviceBusy,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }
}

openatZ()

Open and possibly create a file. Keeps trying if it gets interrupted. file_path is relative to the open directory handle dir_fd. See also openat.

pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.cStrToPrefixedFileW(dir_fd, file_path);
        return openatW(dir_fd, file_path_w.span(), flags, mode);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return openat(dir_fd, mem.sliceTo(file_path, 0), flags, mode);
    }

    const openat_sym = if (lfs64_abi) system.openat64 else system.openat;

    while (true) {
        const rc = openat_sym(dir_fd, file_path, flags, mode);
        switch (errno(rc)) {
            .SUCCESS => return @as(fd_t, @intCast(rc)),
            .INTR => continue,

            .FAULT => unreachable,
            .INVAL => unreachable,
            .BADF => unreachable,
            .ACCES => return error.AccessDenied,
            .FBIG => return error.FileTooBig,
            .OVERFLOW => return error.FileTooBig,
            .ISDIR => return error.IsDir,
            .LOOP => return error.SymLinkLoop,
            .MFILE => return error.ProcessFdQuotaExceeded,
            .NAMETOOLONG => return error.NameTooLong,
            .NFILE => return error.SystemFdQuotaExceeded,
            .NODEV => return error.NoDevice,
            .NOENT => return error.FileNotFound,
            .NOMEM => return error.SystemResources,
            .NOSPC => return error.NoSpaceLeft,
            .NOTDIR => return error.NotDir,
            .PERM => return error.AccessDenied,
            .EXIST => return error.PathAlreadyExists,
            .BUSY => return error.DeviceBusy,
            .OPNOTSUPP => return error.FileLocksNotSupported,
            .AGAIN => return error.WouldBlock,
            .TXTBSY => return error.FileBusy,
            else => |err| return unexpectedErrno(err),
        }
    }
}

openatW()

Windows-only. Similar to openat but with pathname argument null-terminated WTF16 encoded. TODO currently, this function does not handle all flag combinations or makes use of perm argument.

pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t {
    _ = mode;
    var options = openOptionsFromFlagsWindows(flags);
    options.dir = dir_fd;
    return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
        error.WouldBlock => unreachable,
        error.PipeBusy => unreachable,
        else => |e| return e,
    };
}

dup()

pub fn dup(old_fd: fd_t) !fd_t {
    const rc = system.dup(old_fd);
    return switch (errno(rc)) {
        .SUCCESS => return @as(fd_t, @intCast(rc)),
        .MFILE => error.ProcessFdQuotaExceeded,
        .BADF => unreachable, // invalid file descriptor
        else => |err| return unexpectedErrno(err),
    };
}

dup2()

pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
    while (true) {
        switch (errno(system.dup2(old_fd, new_fd))) {
            .SUCCESS => return,
            .BUSY, .INTR => continue,
            .MFILE => return error.ProcessFdQuotaExceeded,
            .INVAL => unreachable, // invalid parameters passed to dup2
            .BADF => unreachable, // invalid file descriptor
            else => |err| return unexpectedErrno(err),
        }
    }
}

ExecveError

pub const ExecveError = error{
    SystemResources,
    AccessDenied,
    InvalidExe,
    FileSystem,
    IsDir,
    FileNotFound,
    NotDir,
    FileBusy,
    ProcessFdQuotaExceeded,
    SystemFdQuotaExceeded,
    NameTooLong,
} || UnexpectedError;

execveZ()

This function ignores PATH environment variable. See execvpeZ for that.

pub fn execveZ(
    path: [*:0]const u8,
    child_argv: [*:null]const ?[*:0]const u8,
    envp: [*:null]const ?[*:0]const u8,
) ExecveError {
    switch (errno(system.execve(path, child_argv, envp))) {
        .SUCCESS => unreachable,
        .FAULT => unreachable,
        .@"2BIG" => return error.SystemResources,
        .MFILE => return error.ProcessFdQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NFILE => return error.SystemFdQuotaExceeded,
        .NOMEM => return error.SystemResources,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .INVAL => return error.InvalidExe,
        .NOEXEC => return error.InvalidExe,
        .IO => return error.FileSystem,
        .LOOP => return error.FileSystem,
        .ISDIR => return error.IsDir,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .TXTBSY => return error.FileBusy,
        else => |err| switch (builtin.os.tag) {
            .macos, .ios, .tvos, .watchos => switch (err) {
                .BADEXEC => return error.InvalidExe,
                .BADARCH => return error.InvalidExe,
                else => return unexpectedErrno(err),
            },
            .linux => switch (err) {
                .LIBBAD => return error.InvalidExe,
                else => return unexpectedErrno(err),
            },
            else => return unexpectedErrno(err),
        },
    }
}

Arg0Expand

pub const Arg0Expand = enum {
    expand,
    no_expand,
};

execvpeZ_expandArg0()

Like execvpeZ except if arg0_expand is .expand, then argv is mutable, and argv[0] is expanded to be the same absolute path that is passed to the execve syscall. If this function returns with an error, argv[0] will be restored to the value it was when it was passed in.

pub fn execvpeZ_expandArg0(
    comptime arg0_expand: Arg0Expand,
    file: [*:0]const u8,
    child_argv: switch (arg0_expand) {
        .expand => [*:null]?[*:0]const u8,
        .no_expand => [*:null]const ?[*:0]const u8,
    },
    envp: [*:null]const ?[*:0]const u8,
) ExecveError {
    const file_slice = mem.sliceTo(file, 0);
    if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp);

    const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
    // Use of MAX_PATH_BYTES here is valid as the path_buf will be passed
    // directly to the operating system in execveZ.
    var path_buf: [MAX_PATH_BYTES]u8 = undefined;
    var it = mem.tokenizeScalar(u8, PATH, ':');
    var seen_eacces = false;
    var err: ExecveError = error.FileNotFound;

    // In case of expanding arg0 we must put it back if we return with an error.
    const prev_arg0 = child_argv[0];
    defer switch (arg0_expand) {
        .expand => child_argv[0] = prev_arg0,
        .no_expand => {},
    };

    while (it.next()) |search_path| {
        const path_len = search_path.len + file_slice.len + 1;
        if (path_buf.len < path_len + 1) return error.NameTooLong;
        @memcpy(path_buf[0..search_path.len], search_path);
        path_buf[search_path.len] = '/';
        @memcpy(path_buf[search_path.len + 1 ..][0..file_slice.len], file_slice);
        path_buf[path_len] = 0;
        const full_path = path_buf[0..path_len :0].ptr;
        switch (arg0_expand) {
            .expand => child_argv[0] = full_path,
            .no_expand => {},
        }
        err = execveZ(full_path, child_argv, envp);
        switch (err) {
            error.AccessDenied => seen_eacces = true,
            error.FileNotFound, error.NotDir => {},
            else => |e| return e,
        }
    }
    if (seen_eacces) return error.AccessDenied;
    return err;
}

execvpeZ()

This function also uses the PATH environment variable to get the full path to the executable. If file is an absolute path, this is the same as execveZ.

pub fn execvpeZ(
    file: [*:0]const u8,
    argv_ptr: [*:null]const ?[*:0]const u8,
    envp: [*:null]const ?[*:0]const u8,
) ExecveError {
    return execvpeZ_expandArg0(.no_expand, file, argv_ptr, envp);
}

getenv()

Get an environment variable. See also getenvZ.

pub fn getenv(key: []const u8) ?[:0]const u8 {
    if (builtin.os.tag == .windows) {
        @compileError("std.os.getenv is unavailable for Windows because environment strings are in WTF-16 format. See std.process.getEnvVarOwned for a cross-platform API or std.os.getenvW for a Windows-specific API.");
    }
    if (builtin.link_libc) {
        var ptr = std.c.environ;
        while (ptr[0]) |line| : (ptr += 1) {
            var line_i: usize = 0;
            while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {}
            const this_key = line[0..line_i];

            if (!mem.eql(u8, this_key, key)) continue;

            return mem.sliceTo(line + line_i + 1, 0);
        }
        return null;
    }
    if (builtin.os.tag == .wasi) {
        @compileError("std.os.getenv is unavailable for WASI. See std.process.getEnvMap or std.process.getEnvVarOwned for a cross-platform API.");
    }
    // The simplified start logic doesn't populate environ.
    if (std.start.simplified_logic) return null;
    // TODO see https://github.com/ziglang/zig/issues/4524
    for (environ) |ptr| {
        var line_i: usize = 0;
        while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {}
        const this_key = ptr[0..line_i];
        if (!mem.eql(u8, key, this_key)) continue;

        return mem.sliceTo(ptr + line_i + 1, 0);
    }
    return null;
}

getenvZ()

Get an environment variable with a null-terminated name. See also getenv.

pub fn getenvZ(key: [*:0]const u8) ?[:0]const u8 {
    if (builtin.link_libc) {
        const value = system.getenv(key) orelse return null;
        return mem.sliceTo(value, 0);
    }
    if (builtin.os.tag == .windows) {
        @compileError("std.os.getenvZ is unavailable for Windows because environment string is in WTF-16 format. See std.process.getEnvVarOwned for cross-platform API or std.os.getenvW for Windows-specific API.");
    }
    return getenv(mem.sliceTo(key, 0));
}

getenvW()

Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name. See also getenv. This function performs a Unicode-aware case-insensitive lookup using RtlEqualUnicodeString.

pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 {
    if (builtin.os.tag != .windows) {
        @compileError("std.os.getenvW is a Windows-only API");
    }
    const key_slice = mem.sliceTo(key, 0);
    const ptr = windows.peb().ProcessParameters.Environment;
    var i: usize = 0;
    while (ptr[i] != 0) {
        const key_start = i;

        // There are some special environment variables that start with =,
        // so we need a special case to not treat = as a key/value separator
        // if it's the first character.
        // https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
        if (ptr[key_start] == '=') i += 1;

        while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {}
        const this_key = ptr[key_start..i];

        if (ptr[i] == '=') i += 1;

        const value_start = i;
        while (ptr[i] != 0) : (i += 1) {}
        const this_value = ptr[value_start..i :0];

        if (windows.eqlIgnoreCaseWTF16(key_slice, this_key)) {
            return this_value;
        }

        i += 1; // skip over null byte
    }
    return null;
}

GetCwdError

pub const GetCwdError = error{
    NameTooLong,
    CurrentWorkingDirectoryUnlinked,
} || UnexpectedError;

getcwd()

The result is a slice of out_buffer, indexed from 0.

pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 {
    if (builtin.os.tag == .windows) {
        return windows.GetCurrentDirectory(out_buffer);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        const path = ".";
        if (out_buffer.len < path.len) return error.NameTooLong;
        const result = out_buffer[0..path.len];
        @memcpy(result, path);
        return result;
    }

    const err = if (builtin.link_libc) blk: {
        const c_err = if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*;
        break :blk @as(E, @enumFromInt(c_err));
    } else blk: {
        break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len));
    };
    switch (err) {
        .SUCCESS => return mem.sliceTo(out_buffer, 0),
        .FAULT => unreachable,
        .INVAL => unreachable,
        .NOENT => return error.CurrentWorkingDirectoryUnlinked,
        .RANGE => return error.NameTooLong,
        else => return unexpectedErrno(err),
    }
}

SymLinkError

pub const SymLinkError = error{
    AccessDenied,
    DiskQuota,
    PathAlreadyExists,
    FileSystem,
    SymLinkLoop,
    FileNotFound,
    SystemResources,
    NoSpaceLeft,
    ReadOnlyFileSystem,
    NotDir,
    NameTooLong,
    InvalidUtf8,
    BadPathName,
} || UnexpectedError;

symlink()

In WASI, this error may occur when the file descriptor does not hold the required rights to create a new symbolic link relative to it. Creates a symbolic link named sym_link_path which contains the string target_path. A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent one; the latter case is known as a dangling link. If sym_link_path exists, it will not be overwritten. See also `symlinkZ.

pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
    if (builtin.os.tag == .windows) {
        @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return symlinkat(target_path, wasi.AT.FDCWD, sym_link_path);
    }
    const target_path_c = try toPosixPath(target_path);
    const sym_link_path_c = try toPosixPath(sym_link_path);
    return symlinkZ(&target_path_c, &sym_link_path_c);
}

symlinkZ()

This is the same as symlink except the parameters are null-terminated pointers. See also symlink.

pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void {
    if (builtin.os.tag == .windows) {
        @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return symlinkatZ(target_path, fs.cwd().fd, sym_link_path);
    }
    switch (errno(system.symlink(target_path, sym_link_path))) {
        .SUCCESS => return,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .DQUOT => return error.DiskQuota,
        .EXIST => return error.PathAlreadyExists,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .ROFS => return error.ReadOnlyFileSystem,
        else => |err| return unexpectedErrno(err),
    }
}

symlinkat()

Similar to symlink, however, creates a symbolic link named sym_link_path which contains the string target_path **relative** to newdirfd directory handle. A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent one; the latter case is known as a dangling link. If sym_link_path exists, it will not be overwritten. See also symlinkatWasi, symlinkatZ and symlinkatW.

pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
    if (builtin.os.tag == .windows) {
        @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return symlinkatWasi(target_path, newdirfd, sym_link_path);
    }
    const target_path_c = try toPosixPath(target_path);
    const sym_link_path_c = try toPosixPath(sym_link_path);
    return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
}

symlinkatWasi()

WASI-only. The same as symlinkat but targeting WASI. See also symlinkat.

pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
    switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
        .SUCCESS => {},
        .FAULT => unreachable,
        .INVAL => unreachable,
        .BADF => unreachable,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .DQUOT => return error.DiskQuota,
        .EXIST => return error.PathAlreadyExists,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .ROFS => return error.ReadOnlyFileSystem,
        .NOTCAPABLE => return error.AccessDenied,
        else => |err| return unexpectedErrno(err),
    }
}

symlinkatZ()

The same as symlinkat except the parameters are null-terminated pointers. See also symlinkat.

pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
    if (builtin.os.tag == .windows) {
        @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead");
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return symlinkat(mem.sliceTo(target_path, 0), newdirfd, mem.sliceTo(sym_link_path, 0));
    }
    switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
        .SUCCESS => return,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .DQUOT => return error.DiskQuota,
        .EXIST => return error.PathAlreadyExists,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .ROFS => return error.ReadOnlyFileSystem,
        else => |err| return unexpectedErrno(err),
    }
}

LinkError

pub const LinkError = UnexpectedError || error{
    AccessDenied,
    DiskQuota,
    PathAlreadyExists,
    FileSystem,
    SymLinkLoop,
    LinkQuotaExceeded,
    NameTooLong,
    FileNotFound,
    SystemResources,
    NoSpaceLeft,
    ReadOnlyFileSystem,
    NotSameFileSystem,
};

linkZ()

pub fn linkZ(oldpath: [*:0]const u8, newpath: [*:0]const u8, flags: i32) LinkError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return link(mem.sliceTo(oldpath, 0), mem.sliceTo(newpath, 0), flags);
    }
    switch (errno(system.link(oldpath, newpath, flags))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .DQUOT => return error.DiskQuota,
        .EXIST => return error.PathAlreadyExists,
        .FAULT => unreachable,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .MLINK => return error.LinkQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .PERM => return error.AccessDenied,
        .ROFS => return error.ReadOnlyFileSystem,
        .XDEV => return error.NotSameFileSystem,
        .INVAL => unreachable,
        else => |err| return unexpectedErrno(err),
    }
}

link()

pub fn link(oldpath: []const u8, newpath: []const u8, flags: i32) LinkError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return linkat(wasi.AT.FDCWD, oldpath, wasi.AT.FDCWD, newpath, flags) catch |err| switch (err) {
            error.NotDir => unreachable, // link() does not support directories
            else => |e| return e,
        };
    }
    const old = try toPosixPath(oldpath);
    const new = try toPosixPath(newpath);
    return try linkZ(&old, &new, flags);
}

LinkatError

pub const LinkatError = LinkError || error{NotDir};

linkatZ()

pub fn linkatZ(
    olddir: fd_t,
    oldpath: [*:0]const u8,
    newdir: fd_t,
    newpath: [*:0]const u8,
    flags: i32,
) LinkatError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return linkat(olddir, mem.sliceTo(oldpath, 0), newdir, mem.sliceTo(newpath, 0), flags);
    }
    switch (errno(system.linkat(olddir, oldpath, newdir, newpath, flags))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .DQUOT => return error.DiskQuota,
        .EXIST => return error.PathAlreadyExists,
        .FAULT => unreachable,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .MLINK => return error.LinkQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .NOTDIR => return error.NotDir,
        .PERM => return error.AccessDenied,
        .ROFS => return error.ReadOnlyFileSystem,
        .XDEV => return error.NotSameFileSystem,
        .INVAL => unreachable,
        else => |err| return unexpectedErrno(err),
    }
}

linkat()

pub fn linkat(
    olddir: fd_t,
    oldpath: []const u8,
    newdir: fd_t,
    newpath: []const u8,
    flags: i32,
) LinkatError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        const old: RelativePathWasi = .{ .dir_fd = olddir, .relative_path = oldpath };
        const new: RelativePathWasi = .{ .dir_fd = newdir, .relative_path = newpath };
        return linkatWasi(old, new, flags);
    }
    const old = try toPosixPath(oldpath);
    const new = try toPosixPath(newpath);
    return try linkatZ(olddir, &old, newdir, &new, flags);
}

linkatWasi()

WASI-only. The same as linkat but targeting WASI. See also linkat.

pub fn linkatWasi(old: RelativePathWasi, new: RelativePathWasi, flags: i32) LinkatError!void {
    var old_flags: wasi.lookupflags_t = 0;
    // TODO: Why is this not defined in wasi-libc?
    if (flags & linux.AT.SYMLINK_FOLLOW != 0) old_flags |= wasi.LOOKUP_SYMLINK_FOLLOW;

    switch (wasi.path_link(old.dir_fd, old_flags, old.relative_path.ptr, old.relative_path.len, new.dir_fd, new.relative_path.ptr, new.relative_path.len)) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .DQUOT => return error.DiskQuota,
        .EXIST => return error.PathAlreadyExists,
        .FAULT => unreachable,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .MLINK => return error.LinkQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .NOTDIR => return error.NotDir,
        .PERM => return error.AccessDenied,
        .ROFS => return error.ReadOnlyFileSystem,
        .XDEV => return error.NotSameFileSystem,
        .INVAL => unreachable,
        else => |err| return unexpectedErrno(err),
    }
}

UnlinkError

pub const UnlinkError = error{
    FileNotFound,

    AccessDenied,
    FileBusy,
    FileSystem,
    IsDir,
    SymLinkLoop,
    NameTooLong,
    NotDir,
    SystemResources,
    ReadOnlyFileSystem,

    InvalidUtf8,

    BadPathName,

    NetworkNotFound,
} || UnexpectedError;

unlink()

In WASI, this error may occur when the file descriptor does not hold the required rights to unlink a resource by path relative to it. On Windows, file paths must be valid Unicode. On Windows, file paths cannot contain these characters: '/', '*', '?', '"', '<', '>', '|' On Windows, \\server or \\server\share was not found. Delete a name and possibly the file it refers to. See also unlinkZ.

pub fn unlink(file_path: []const u8) UnlinkError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return unlinkat(wasi.AT.FDCWD, file_path, 0) catch |err| switch (err) {
            error.DirNotEmpty => unreachable, // only occurs when targeting directories
            else => |e| return e,
        };
    } else if (builtin.os.tag == .windows) {
        const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
        return unlinkW(file_path_w.span());
    } else {
        const file_path_c = try toPosixPath(file_path);
        return unlinkZ(&file_path_c);
    }
}

unlinkZ()

Same as unlink except the parameter is a null terminated UTF8-encoded string.

pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.cStrToPrefixedFileW(null, file_path);
        return unlinkW(file_path_w.span());
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return unlink(mem.sliceTo(file_path, 0));
    }
    switch (errno(system.unlink(file_path))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .BUSY => return error.FileBusy,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .IO => return error.FileSystem,
        .ISDIR => return error.IsDir,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .NOMEM => return error.SystemResources,
        .ROFS => return error.ReadOnlyFileSystem,
        else => |err| return unexpectedErrno(err),
    }
}

unlinkW()

Windows-only. Same as unlink except the parameter is null-terminated, WTF16 encoded.

pub fn unlinkW(file_path_w: []const u16) UnlinkError!void {
    windows.DeleteFile(file_path_w, .{ .dir = std.fs.cwd().fd }) catch |err| switch (err) {
        error.DirNotEmpty => unreachable, // we're not passing .remove_dir = true
        else => |e| return e,
    };
}

UnlinkatError

pub const UnlinkatError = UnlinkError || error{
    DirNotEmpty,
};

unlinkat()

When passing AT.REMOVEDIR, this error occurs when the named directory is not empty. Delete a file name and possibly the file it refers to, based on an open directory handle. Asserts that the path parameter has no null bytes.

pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
        return unlinkatW(dirfd, file_path_w.span(), flags);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return unlinkatWasi(dirfd, file_path, flags);
    } else {
        const file_path_c = try toPosixPath(file_path);
        return unlinkatZ(dirfd, &file_path_c, flags);
    }
}

unlinkatWasi()

WASI-only. Same as unlinkat but targeting WASI. See also unlinkat.

pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
    const remove_dir = (flags & AT.REMOVEDIR) != 0;
    const res = if (remove_dir)
        wasi.path_remove_directory(dirfd, file_path.ptr, file_path.len)
    else
        wasi.path_unlink_file(dirfd, file_path.ptr, file_path.len);
    switch (res) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .BUSY => return error.FileBusy,
        .FAULT => unreachable,
        .IO => return error.FileSystem,
        .ISDIR => return error.IsDir,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .NOMEM => return error.SystemResources,
        .ROFS => return error.ReadOnlyFileSystem,
        .NOTEMPTY => return error.DirNotEmpty,
        .NOTCAPABLE => return error.AccessDenied,

        .INVAL => unreachable, // invalid flags, or pathname has . as last component
        .BADF => unreachable, // always a race condition

        else => |err| return unexpectedErrno(err),
    }
}

unlinkatZ()

Same as unlinkat but file_path is a null-terminated string.

pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path_c);
        return unlinkatW(dirfd, file_path_w.span(), flags);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return unlinkat(dirfd, mem.sliceTo(file_path_c, 0), flags);
    }
    switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .BUSY => return error.FileBusy,
        .FAULT => unreachable,
        .IO => return error.FileSystem,
        .ISDIR => return error.IsDir,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .NOMEM => return error.SystemResources,
        .ROFS => return error.ReadOnlyFileSystem,
        .EXIST => return error.DirNotEmpty,
        .NOTEMPTY => return error.DirNotEmpty,

        .INVAL => unreachable, // invalid flags, or pathname has . as last component
        .BADF => unreachable, // always a race condition

        else => |err| return unexpectedErrno(err),
    }
}

unlinkatW()

Same as unlinkat but sub_path_w is UTF16LE, NT prefixed. Windows only.

pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
    const remove_dir = (flags & AT.REMOVEDIR) != 0;
    return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
}

RenameError

pub const RenameError = error{
    AccessDenied,
    FileBusy,
    DiskQuota,
    IsDir,
    SymLinkLoop,
    LinkQuotaExceeded,
    NameTooLong,
    FileNotFound,
    NotDir,
    SystemResources,
    NoSpaceLeft,
    PathAlreadyExists,
    ReadOnlyFileSystem,
    RenameAcrossMountPoints,
    InvalidUtf8,
    BadPathName,
    NoDevice,
    SharingViolation,
    PipeBusy,
    NetworkNotFound,
} || UnexpectedError;

rename()

In WASI, this error may occur when the file descriptor does not hold the required rights to rename a resource by path relative to it.

On Windows, this error may be returned instead of PathAlreadyExists when renaming a directory over an existing directory. On Windows, \\server or \\server\share was not found. Change the name or location of a file.

pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return renameat(wasi.AT.FDCWD, old_path, wasi.AT.FDCWD, new_path);
    } else if (builtin.os.tag == .windows) {
        const old_path_w = try windows.sliceToPrefixedFileW(null, old_path);
        const new_path_w = try windows.sliceToPrefixedFileW(null, new_path);
        return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
    } else {
        const old_path_c = try toPosixPath(old_path);
        const new_path_c = try toPosixPath(new_path);
        return renameZ(&old_path_c, &new_path_c);
    }
}

renameZ()

Same as rename except the parameters are null-terminated byte arrays.

pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void {
    if (builtin.os.tag == .windows) {
        const old_path_w = try windows.cStrToPrefixedFileW(null, old_path);
        const new_path_w = try windows.cStrToPrefixedFileW(null, new_path);
        return renameW(old_path_w.span().ptr, new_path_w.span().ptr);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return rename(mem.sliceTo(old_path, 0), mem.sliceTo(new_path, 0));
    }
    switch (errno(system.rename(old_path, new_path))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .BUSY => return error.FileBusy,
        .DQUOT => return error.DiskQuota,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .ISDIR => return error.IsDir,
        .LOOP => return error.SymLinkLoop,
        .MLINK => return error.LinkQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .EXIST => return error.PathAlreadyExists,
        .NOTEMPTY => return error.PathAlreadyExists,
        .ROFS => return error.ReadOnlyFileSystem,
        .XDEV => return error.RenameAcrossMountPoints,
        else => |err| return unexpectedErrno(err),
    }
}

renameW()

Same as rename except the parameters are null-terminated UTF16LE encoded byte arrays. Assumes target is Windows.

pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!void {
    const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH;
    return windows.MoveFileExW(old_path, new_path, flags);
}

renameat()

Change the name or location of a file based on an open directory handle.

pub fn renameat(
    old_dir_fd: fd_t,
    old_path: []const u8,
    new_dir_fd: fd_t,
    new_path: []const u8,
) RenameError!void {
    if (builtin.os.tag == .windows) {
        const old_path_w = try windows.sliceToPrefixedFileW(old_dir_fd, old_path);
        const new_path_w = try windows.sliceToPrefixedFileW(new_dir_fd, new_path);
        return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        const old: RelativePathWasi = .{ .dir_fd = old_dir_fd, .relative_path = old_path };
        const new: RelativePathWasi = .{ .dir_fd = new_dir_fd, .relative_path = new_path };
        return renameatWasi(old, new);
    } else {
        const old_path_c = try toPosixPath(old_path);
        const new_path_c = try toPosixPath(new_path);
        return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c);
    }
}

renameatWasi()

WASI-only. Same as renameat expect targeting WASI. See also renameat.

pub fn renameatWasi(old: RelativePathWasi, new: RelativePathWasi) RenameError!void {
    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)) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .BUSY => return error.FileBusy,
        .DQUOT => return error.DiskQuota,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .ISDIR => return error.IsDir,
        .LOOP => return error.SymLinkLoop,
        .MLINK => return error.LinkQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .EXIST => return error.PathAlreadyExists,
        .NOTEMPTY => return error.PathAlreadyExists,
        .ROFS => return error.ReadOnlyFileSystem,
        .XDEV => return error.RenameAcrossMountPoints,
        .NOTCAPABLE => return error.AccessDenied,
        else => |err| return unexpectedErrno(err),
    }
}

renameatZ()

Same as renameat except the parameters are null-terminated byte arrays.

pub fn renameatZ(
    old_dir_fd: fd_t,
    old_path: [*:0]const u8,
    new_dir_fd: fd_t,
    new_path: [*:0]const u8,
) RenameError!void {
    if (builtin.os.tag == .windows) {
        const old_path_w = try windows.cStrToPrefixedFileW(old_dir_fd, old_path);
        const new_path_w = try windows.cStrToPrefixedFileW(new_dir_fd, new_path);
        return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return renameat(old_dir_fd, mem.sliceTo(old_path, 0), new_dir_fd, mem.sliceTo(new_path, 0));
    }

    switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .BUSY => return error.FileBusy,
        .DQUOT => return error.DiskQuota,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .ISDIR => return error.IsDir,
        .LOOP => return error.SymLinkLoop,
        .MLINK => return error.LinkQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.NotDir,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .EXIST => return error.PathAlreadyExists,
        .NOTEMPTY => return error.PathAlreadyExists,
        .ROFS => return error.ReadOnlyFileSystem,
        .XDEV => return error.RenameAcrossMountPoints,
        else => |err| return unexpectedErrno(err),
    }
}

renameatW()

Same as renameat but Windows-only and the path parameters are [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.

pub fn renameatW(
    old_dir_fd: fd_t,
    old_path_w: []const u16,
    new_dir_fd: fd_t,
    new_path_w: []const u16,
    ReplaceIfExists: windows.BOOLEAN,
) RenameError!void {
    const src_fd = windows.OpenFile(old_path_w, .{
        .dir = old_dir_fd,
        .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE,
        .creation = windows.FILE_OPEN,
        .io_mode = .blocking,
        .filter = .any, // This function is supposed to rename both files and directories.
        .follow_symlinks = false,
    }) catch |err| switch (err) {
        error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`.
        else => |e| return e,
    };
    defer windows.CloseHandle(src_fd);

    var need_fallback = true;
    var rc: windows.NTSTATUS = undefined;
    // FILE_RENAME_INFORMATION_EX and FILE_RENAME_POSIX_SEMANTICS require >= win10_rs1,
    // but FILE_RENAME_IGNORE_READONLY_ATTRIBUTE requires >= win10_rs5. We check >= rs5 here
    // so that we only use POSIX_SEMANTICS when we know IGNORE_READONLY_ATTRIBUTE will also be
    // supported in order to avoid either (1) using a redundant call that we can know in advance will return
    // STATUS_NOT_SUPPORTED or (2) only setting IGNORE_READONLY_ATTRIBUTE when >= rs5
    // and therefore having different behavior when the Windows version is >= rs1 but < rs5.
    if (comptime builtin.target.os.version_range.windows.min.isAtLeast(.win10_rs5)) {
        const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION_EX) + (MAX_PATH_BYTES - 1);
        var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION_EX)) = undefined;
        const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION_EX) - 1 + new_path_w.len * 2;
        if (struct_len > struct_buf_len) return error.NameTooLong;

        const rename_info = @as(*windows.FILE_RENAME_INFORMATION_EX, @ptrCast(&rename_info_buf));
        var io_status_block: windows.IO_STATUS_BLOCK = undefined;

        var flags: windows.ULONG = windows.FILE_RENAME_POSIX_SEMANTICS | windows.FILE_RENAME_IGNORE_READONLY_ATTRIBUTE;
        if (ReplaceIfExists == windows.TRUE) flags |= windows.FILE_RENAME_REPLACE_IF_EXISTS;
        rename_info.* = .{
            .Flags = flags,
            .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
            .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
            .FileName = undefined,
        };
        @memcpy(@as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w);
        rc = windows.ntdll.NtSetInformationFile(
            src_fd,
            &io_status_block,
            rename_info,
            @intCast(struct_len), // already checked for error.NameTooLong
            .FileRenameInformationEx,
        );
        switch (rc) {
            .SUCCESS => return,
            // INVALID_PARAMETER here means that the filesystem does not support FileRenameInformationEx
            .INVALID_PARAMETER => {},
            .DIRECTORY_NOT_EMPTY => return error.PathAlreadyExists,
            .FILE_IS_A_DIRECTORY => return error.IsDir,
            .NOT_A_DIRECTORY => return error.NotDir,
            // For all other statuses, fall down to the switch below to handle them.
            else => need_fallback = false,
        }
    }

    if (need_fallback) {
        const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1);
        var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined;
        const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2;
        if (struct_len > struct_buf_len) return error.NameTooLong;

        const rename_info = @as(*windows.FILE_RENAME_INFORMATION, @ptrCast(&rename_info_buf));
        var io_status_block: windows.IO_STATUS_BLOCK = undefined;

        rename_info.* = .{
            .Flags = ReplaceIfExists,
            .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd,
            .FileNameLength = @intCast(new_path_w.len * 2), // already checked error.NameTooLong
            .FileName = undefined,
        };
        @memcpy(@as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w);

        rc =
            windows.ntdll.NtSetInformationFile(
            src_fd,
            &io_status_block,
            rename_info,
            @intCast(struct_len), // already checked for error.NameTooLong
            .FileRenameInformation,
        );
    }

    switch (rc) {
        .SUCCESS => {},
        .INVALID_HANDLE => unreachable,
        .INVALID_PARAMETER => unreachable,
        .OBJECT_PATH_SYNTAX_BAD => unreachable,
        .ACCESS_DENIED => return error.AccessDenied,
        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
        .NOT_SAME_DEVICE => return error.RenameAcrossMountPoints,
        .OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
        else => return windows.unexpectedStatus(rc),
    }
}

mkdirat()

pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
    if (builtin.os.tag == .windows) {
        const sub_dir_path_w = try windows.sliceToPrefixedFileW(dir_fd, sub_dir_path);
        return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return mkdiratWasi(dir_fd, sub_dir_path, mode);
    } else {
        const sub_dir_path_c = try toPosixPath(sub_dir_path);
        return mkdiratZ(dir_fd, &sub_dir_path_c, mode);
    }
}

mkdiratWasi()

pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
    _ = mode;
    switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .BADF => unreachable,
        .PERM => return error.AccessDenied,
        .DQUOT => return error.DiskQuota,
        .EXIST => return error.PathAlreadyExists,
        .FAULT => unreachable,
        .LOOP => return error.SymLinkLoop,
        .MLINK => return error.LinkQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .NOTDIR => return error.NotDir,
        .ROFS => return error.ReadOnlyFileSystem,
        .NOTCAPABLE => return error.AccessDenied,
        else => |err| return unexpectedErrno(err),
    }
}

mkdiratZ()

pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
    if (builtin.os.tag == .windows) {
        const sub_dir_path_w = try windows.cStrToPrefixedFileW(dir_fd, sub_dir_path);
        return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return mkdirat(dir_fd, mem.sliceTo(sub_dir_path, 0), mode);
    }
    switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .BADF => unreachable,
        .PERM => return error.AccessDenied,
        .DQUOT => return error.DiskQuota,
        .EXIST => return error.PathAlreadyExists,
        .FAULT => unreachable,
        .LOOP => return error.SymLinkLoop,
        .MLINK => return error.LinkQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .NOTDIR => return error.NotDir,
        .ROFS => return error.ReadOnlyFileSystem,
        // dragonfly: when dir_fd is unlinked from filesystem
        .NOTCONN => return error.FileNotFound,
        else => |err| return unexpectedErrno(err),
    }
}

mkdiratW()

pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
    _ = mode;
    const sub_dir_handle = windows.OpenFile(sub_path_w, .{
        .dir = dir_fd,
        .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
        .creation = windows.FILE_CREATE,
        .io_mode = .blocking,
        .filter = .dir_only,
    }) catch |err| switch (err) {
        error.IsDir => unreachable,
        error.PipeBusy => unreachable,
        error.WouldBlock => unreachable,
        else => |e| return e,
    };
    windows.CloseHandle(sub_dir_handle);
}

MakeDirError

pub const MakeDirError = error{
    AccessDenied,
    DiskQuota,
    PathAlreadyExists,
    SymLinkLoop,
    LinkQuotaExceeded,
    NameTooLong,
    FileNotFound,
    SystemResources,
    NoSpaceLeft,
    NotDir,
    ReadOnlyFileSystem,
    InvalidUtf8,
    BadPathName,
    NoDevice,
    NetworkNotFound,
} || UnexpectedError;

mkdir()

In WASI, this error may occur when the file descriptor does not hold the required rights to create a new directory relative to it. On Windows, \\server or \\server\share was not found. Create a directory. mode is ignored on Windows and WASI.

pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return mkdirat(wasi.AT.FDCWD, dir_path, mode);
    } else if (builtin.os.tag == .windows) {
        const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
        return mkdirW(dir_path_w.span(), mode);
    } else {
        const dir_path_c = try toPosixPath(dir_path);
        return mkdirZ(&dir_path_c, mode);
    }
}

mkdirZ()

Same as mkdir but the parameter is a null-terminated UTF8-encoded string.

pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
    if (builtin.os.tag == .windows) {
        const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path);
        return mkdirW(dir_path_w.span(), mode);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return mkdir(mem.sliceTo(dir_path, 0), mode);
    }
    switch (errno(system.mkdir(dir_path, mode))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .DQUOT => return error.DiskQuota,
        .EXIST => return error.PathAlreadyExists,
        .FAULT => unreachable,
        .LOOP => return error.SymLinkLoop,
        .MLINK => return error.LinkQuotaExceeded,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.NoSpaceLeft,
        .NOTDIR => return error.NotDir,
        .ROFS => return error.ReadOnlyFileSystem,
        else => |err| return unexpectedErrno(err),
    }
}

mkdirW()

Windows-only. Same as mkdir but the parameters is WTF16 encoded.

pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
    _ = mode;
    const sub_dir_handle = windows.OpenFile(dir_path_w, .{
        .dir = std.fs.cwd().fd,
        .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
        .creation = windows.FILE_CREATE,
        .io_mode = .blocking,
        .filter = .dir_only,
    }) catch |err| switch (err) {
        error.IsDir => unreachable,
        error.PipeBusy => unreachable,
        error.WouldBlock => unreachable,
        else => |e| return e,
    };
    windows.CloseHandle(sub_dir_handle);
}

DeleteDirError

pub const DeleteDirError = error{
    AccessDenied,
    FileBusy,
    SymLinkLoop,
    NameTooLong,
    FileNotFound,
    SystemResources,
    NotDir,
    DirNotEmpty,
    ReadOnlyFileSystem,
    InvalidUtf8,
    BadPathName,
    NetworkNotFound,
} || UnexpectedError;

rmdir()

On Windows, \\server or \\server\share was not found. Deletes an empty directory.

pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return unlinkat(wasi.AT.FDCWD, dir_path, AT.REMOVEDIR) catch |err| switch (err) {
            error.FileSystem => unreachable, // only occurs when targeting files
            error.IsDir => unreachable, // only occurs when targeting files
            else => |e| return e,
        };
    } else if (builtin.os.tag == .windows) {
        const dir_path_w = try windows.sliceToPrefixedFileW(null, dir_path);
        return rmdirW(dir_path_w.span());
    } else {
        const dir_path_c = try toPosixPath(dir_path);
        return rmdirZ(&dir_path_c);
    }
}

rmdirZ()

Same as rmdir except the parameter is null-terminated.

pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
    if (builtin.os.tag == .windows) {
        const dir_path_w = try windows.cStrToPrefixedFileW(null, dir_path);
        return rmdirW(dir_path_w.span());
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return rmdir(mem.sliceTo(dir_path, 0));
    }
    switch (errno(system.rmdir(dir_path))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .BUSY => return error.FileBusy,
        .FAULT => unreachable,
        .INVAL => return error.BadPathName,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOTDIR => return error.NotDir,
        .EXIST => return error.DirNotEmpty,
        .NOTEMPTY => return error.DirNotEmpty,
        .ROFS => return error.ReadOnlyFileSystem,
        else => |err| return unexpectedErrno(err),
    }
}

rmdirW()

Windows-only. Same as rmdir except the parameter is WTF16 encoded.

pub fn rmdirW(dir_path_w: []const u16) DeleteDirError!void {
    return windows.DeleteFile(dir_path_w, .{ .dir = std.fs.cwd().fd, .remove_dir = true }) catch |err| switch (err) {
        error.IsDir => unreachable,
        else => |e| return e,
    };
}

ChangeCurDirError

pub const ChangeCurDirError = error{
    AccessDenied,
    FileSystem,
    SymLinkLoop,
    NameTooLong,
    FileNotFound,
    SystemResources,
    NotDir,
    BadPathName,

    InvalidUtf8,
} || UnexpectedError;

chdir()

On Windows, file paths must be valid Unicode. Changes the current working directory of the calling process. dir_path is recommended to be a UTF-8 encoded string.

pub fn chdir(dir_path: []const u8) ChangeCurDirError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        @compileError("WASI does not support os.chdir");
    } else if (builtin.os.tag == .windows) {
        var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
        const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path);
        if (len > utf16_dir_path.len) return error.NameTooLong;
        return chdirW(utf16_dir_path[0..len]);
    } else {
        const dir_path_c = try toPosixPath(dir_path);
        return chdirZ(&dir_path_c);
    }
}

chdirZ()

Same as chdir except the parameter is null-terminated.

pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void {
    if (builtin.os.tag == .windows) {
        var utf16_dir_path: [windows.PATH_MAX_WIDE]u16 = undefined;
        const len = try std.unicode.utf8ToUtf16Le(utf16_dir_path[0..], dir_path);
        if (len > utf16_dir_path.len) return error.NameTooLong;
        return chdirW(utf16_dir_path[0..len]);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return chdir(mem.sliceTo(dir_path, 0));
    }
    switch (errno(system.chdir(dir_path))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .FAULT => unreachable,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOTDIR => return error.NotDir,
        else => |err| return unexpectedErrno(err),
    }
}

chdirW()

Windows-only. Same as chdir except the parameter is WTF16 encoded.

pub fn chdirW(dir_path: []const u16) ChangeCurDirError!void {
    windows.SetCurrentDirectory(dir_path) catch |err| switch (err) {
        error.NoDevice => return error.FileSystem,
        else => |e| return e,
    };
}

FchdirError

pub const FchdirError = error{
    AccessDenied,
    NotDir,
    FileSystem,
} || UnexpectedError;

fchdir()

pub fn fchdir(dirfd: fd_t) FchdirError!void {
    if (dirfd == AT.FDCWD) return;
    while (true) {
        switch (errno(system.fchdir(dirfd))) {
            .SUCCESS => return,
            .ACCES => return error.AccessDenied,
            .BADF => unreachable,
            .NOTDIR => return error.NotDir,
            .INTR => continue,
            .IO => return error.FileSystem,
            else => |err| return unexpectedErrno(err),
        }
    }
}

ReadLinkError

pub const ReadLinkError = error{
    AccessDenied,
    FileSystem,
    SymLinkLoop,
    NameTooLong,
    FileNotFound,
    SystemResources,
    NotLink,
    NotDir,
    InvalidUtf8,
    BadPathName,
    UnsupportedReparsePointType,
    NetworkNotFound,
} || UnexpectedError;

readlink()

In WASI, this error may occur when the file descriptor does not hold the required rights to read value of a symbolic link relative to it. Windows-only. This error may occur if the opened reparse point is of unsupported type. On Windows, \\server or \\server\share was not found. Read value of a symbolic link. The return value is a slice of out_buffer from index 0.

pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return readlinkat(wasi.AT.FDCWD, file_path, out_buffer);
    } else if (builtin.os.tag == .windows) {
        const file_path_w = try windows.sliceToPrefixedFileW(null, file_path);
        return readlinkW(file_path_w.span(), out_buffer);
    } else {
        const file_path_c = try toPosixPath(file_path);
        return readlinkZ(&file_path_c, out_buffer);
    }
}

readlinkW()

Windows-only. Same as readlink except file_path is WTF16 encoded. See also readlinkZ.

pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
    return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
}

readlinkZ()

Same as readlink except file_path is null-terminated.

pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);
        return readlinkW(file_path_w.span(), out_buffer);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return readlink(mem.sliceTo(file_path, 0), out_buffer);
    }
    const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
    switch (errno(rc)) {
        .SUCCESS => return out_buffer[0..@as(usize, @bitCast(rc))],
        .ACCES => return error.AccessDenied,
        .FAULT => unreachable,
        .INVAL => return error.NotLink,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOTDIR => return error.NotDir,
        else => |err| return unexpectedErrno(err),
    }
}

readlinkat()

Similar to readlink except reads value of a symbolink link **relative** to dirfd directory handle. The return value is a slice of out_buffer from index 0. See also readlinkatWasi, realinkatZ and realinkatW.

pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return readlinkatWasi(dirfd, file_path, out_buffer);
    }
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.sliceToPrefixedFileW(dirfd, file_path);
        return readlinkatW(dirfd, file_path_w.span(), out_buffer);
    }
    const file_path_c = try toPosixPath(file_path);
    return readlinkatZ(dirfd, &file_path_c, out_buffer);
}

readlinkatWasi()

WASI-only. Same as readlinkat but targets WASI. See also readlinkat.

pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
    var bufused: usize = undefined;
    switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
        .SUCCESS => return out_buffer[0..bufused],
        .ACCES => return error.AccessDenied,
        .FAULT => unreachable,
        .INVAL => return error.NotLink,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOTDIR => return error.NotDir,
        .NOTCAPABLE => return error.AccessDenied,
        else => |err| return unexpectedErrno(err),
    }
}

readlinkatW()

Windows-only. Same as readlinkat except file_path is null-terminated, WTF16 encoded. See also readlinkat.

pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
    return windows.ReadLink(dirfd, file_path, out_buffer);
}

readlinkatZ()

Same as readlinkat except file_path is null-terminated. See also readlinkat.

pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
    if (builtin.os.tag == .windows) {
        const file_path_w = try windows.cStrToPrefixedFileW(dirfd, file_path);
        return readlinkatW(dirfd, file_path_w.span(), out_buffer);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return readlinkat(dirfd, mem.sliceTo(file_path, 0), out_buffer);
    }
    const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
    switch (errno(rc)) {
        .SUCCESS => return out_buffer[0..@as(usize, @bitCast(rc))],
        .ACCES => return error.AccessDenied,
        .FAULT => unreachable,
        .INVAL => return error.NotLink,
        .IO => return error.FileSystem,
        .LOOP => return error.SymLinkLoop,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOTDIR => return error.NotDir,
        else => |err| return unexpectedErrno(err),
    }
}

SetEidError

pub const SetEidError = error{
    InvalidUserId,
    PermissionDenied,
} || UnexpectedError;

SetIdError

pub const SetIdError = error{ResourceLimitReached} || SetEidError;

setuid()

pub fn setuid(uid: uid_t) SetIdError!void {
    switch (errno(system.setuid(uid))) {
        .SUCCESS => return,
        .AGAIN => return error.ResourceLimitReached,
        .INVAL => return error.InvalidUserId,
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

seteuid()

pub fn seteuid(uid: uid_t) SetEidError!void {
    switch (errno(system.seteuid(uid))) {
        .SUCCESS => return,
        .INVAL => return error.InvalidUserId,
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

setreuid()

pub fn setreuid(ruid: uid_t, euid: uid_t) SetIdError!void {
    switch (errno(system.setreuid(ruid, euid))) {
        .SUCCESS => return,
        .AGAIN => return error.ResourceLimitReached,
        .INVAL => return error.InvalidUserId,
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

setgid()

pub fn setgid(gid: gid_t) SetIdError!void {
    switch (errno(system.setgid(gid))) {
        .SUCCESS => return,
        .AGAIN => return error.ResourceLimitReached,
        .INVAL => return error.InvalidUserId,
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

setegid()

pub fn setegid(uid: uid_t) SetEidError!void {
    switch (errno(system.setegid(uid))) {
        .SUCCESS => return,
        .INVAL => return error.InvalidUserId,
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

setregid()

pub fn setregid(rgid: gid_t, egid: gid_t) SetIdError!void {
    switch (errno(system.setregid(rgid, egid))) {
        .SUCCESS => return,
        .AGAIN => return error.ResourceLimitReached,
        .INVAL => return error.InvalidUserId,
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

isatty()

Test whether a file descriptor refers to a terminal.

pub fn isatty(handle: fd_t) bool {
    if (builtin.os.tag == .windows) {
        if (isCygwinPty(handle))
            return true;

        var out: windows.DWORD = undefined;
        return windows.kernel32.GetConsoleMode(handle, &out) != 0;
    }
    if (builtin.link_libc) {
        return system.isatty(handle) != 0;
    }
    if (builtin.os.tag == .wasi) {
        var statbuf: fdstat_t = undefined;
        const err = system.fd_fdstat_get(handle, &statbuf);
        if (err != .SUCCESS) {
            // errno = err;
            return false;
        }

        // A tty is a character device that we can't seek or tell on.
        if (statbuf.fs_filetype != .CHARACTER_DEVICE or
            (statbuf.fs_rights_base & (RIGHT.FD_SEEK | RIGHT.FD_TELL)) != 0)
        {
            // errno = ENOTTY;
            return false;
        }

        return true;
    }
    if (builtin.os.tag == .linux) {
        while (true) {
            var wsz: linux.winsize = undefined;
            const fd = @as(usize, @bitCast(@as(isize, handle)));
            const rc = linux.syscall3(.ioctl, fd, linux.T.IOCGWINSZ, @intFromPtr(&wsz));
            switch (linux.getErrno(rc)) {
                .SUCCESS => return true,
                .INTR => continue,
                else => return false,
            }
        }
    }
    return system.isatty(handle) != 0;
}

isCygwinPty()

pub fn isCygwinPty(handle: fd_t) bool {
    if (builtin.os.tag != .windows) return false;

    // If this is a MSYS2/cygwin pty, then it will be a named pipe with a name in one of these formats:
    //   msys-[...]-ptyN-[...]
    //   cygwin-[...]-ptyN-[...]
    //
    // Example: msys-1888ae32e00d56aa-pty0-to-master

    // First, just check that the handle is a named pipe.
    // This allows us to avoid the more costly NtQueryInformationFile call
    // for handles that aren't named pipes.
    {
        var io_status: windows.IO_STATUS_BLOCK = undefined;
        var device_info: windows.FILE_FS_DEVICE_INFORMATION = undefined;
        const rc = windows.ntdll.NtQueryVolumeInformationFile(handle, &io_status, &device_info, @sizeOf(windows.FILE_FS_DEVICE_INFORMATION), .FileFsDeviceInformation);
        switch (rc) {
            .SUCCESS => {},
            else => return false,
        }
        if (device_info.DeviceType != windows.FILE_DEVICE_NAMED_PIPE) return false;
    }

    const name_bytes_offset = @offsetOf(windows.FILE_NAME_INFO, "FileName");
    // `NAME_MAX` UTF-16 code units (2 bytes each)
    // Note: This buffer may not be long enough to handle *all* possible paths (PATH_MAX_WIDE would be necessary for that),
    //       but because we only care about certain paths and we know they must be within a reasonable length,
    //       we can use this smaller buffer and just return false on any error from NtQueryInformationFile.
    const num_name_bytes = windows.MAX_PATH * 2;
    var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (name_bytes_offset + num_name_bytes);

    var io_status_block: windows.IO_STATUS_BLOCK = undefined;
    const rc = windows.ntdll.NtQueryInformationFile(handle, &io_status_block, &name_info_bytes, @as(u32, @intCast(name_info_bytes.len)), .FileNameInformation);
    switch (rc) {
        .SUCCESS => {},
        .INVALID_PARAMETER => unreachable,
        else => return false,
    }

    const name_info = @as(*const windows.FILE_NAME_INFO, @ptrCast(&name_info_bytes[0]));
    const name_bytes = name_info_bytes[name_bytes_offset .. name_bytes_offset + @as(usize, name_info.FileNameLength)];
    const name_wide = mem.bytesAsSlice(u16, name_bytes);
    // Note: The name we get from NtQueryInformationFile will be prefixed with a '\', e.g. \msys-1888ae32e00d56aa-pty0-to-master
    return (mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'm', 's', 'y', 's', '-' }) or
        mem.startsWith(u16, name_wide, &[_]u16{ '\\', 'c', 'y', 'g', 'w', 'i', 'n', '-' })) and
        mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null;
}

SocketError

pub const SocketError = error{
    PermissionDenied,

    AddressFamilyNotSupported,

    ProtocolFamilyNotAvailable,

    ProcessFdQuotaExceeded,

    SystemFdQuotaExceeded,

    SystemResources,

    ProtocolNotSupported,

    SocketTypeNotSupported,
} || UnexpectedError;

socket()

Permission to create a socket of the specified type and/or pro‐tocol is denied. The implementation does not support the specified address family. Unknown protocol, or protocol family not available. The per-process limit on the number of open file descriptors has been reached. The system-wide limit on the total number of open files has been reached. Insufficient memory is available. The socket cannot be created until sufficient resources are freed. The protocol type or the specified protocol is not supported within this domain. The socket type is not supported by the protocol.

pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t {
    if (builtin.os.tag == .windows) {
        // NOTE: windows translates the SOCK.NONBLOCK/SOCK.CLOEXEC flags into
        // windows-analagous operations
        const filtered_sock_type = socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC);
        const flags: u32 = if ((socket_type & SOCK.CLOEXEC) != 0)
            windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT
        else
            0;
        const rc = try windows.WSASocketW(
            @as(i32, @bitCast(domain)),
            @as(i32, @bitCast(filtered_sock_type)),
            @as(i32, @bitCast(protocol)),
            null,
            0,
            flags,
        );
        errdefer windows.closesocket(rc) catch unreachable;
        if ((socket_type & SOCK.NONBLOCK) != 0) {
            var mode: c_ulong = 1; // nonblocking
            if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) {
                switch (windows.ws2_32.WSAGetLastError()) {
                    // have not identified any error codes that should be handled yet
                    else => unreachable,
                }
            }
        }
        return rc;
    }

    const have_sock_flags = comptime !builtin.target.isDarwin();
    const filtered_sock_type = if (!have_sock_flags)
        socket_type & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC)
    else
        socket_type;
    const rc = system.socket(domain, filtered_sock_type, protocol);
    switch (errno(rc)) {
        .SUCCESS => {
            const fd = @as(fd_t, @intCast(rc));
            if (!have_sock_flags) {
                try setSockFlags(fd, socket_type);
            }
            return fd;
        },
        .ACCES => return error.PermissionDenied,
        .AFNOSUPPORT => return error.AddressFamilyNotSupported,
        .INVAL => return error.ProtocolFamilyNotAvailable,
        .MFILE => return error.ProcessFdQuotaExceeded,
        .NFILE => return error.SystemFdQuotaExceeded,
        .NOBUFS => return error.SystemResources,
        .NOMEM => return error.SystemResources,
        .PROTONOSUPPORT => return error.ProtocolNotSupported,
        .PROTOTYPE => return error.SocketTypeNotSupported,
        else => |err| return unexpectedErrno(err),
    }
}

ShutdownError

pub const ShutdownError = error{
    ConnectionAborted,

    ConnectionResetByPeer,
    BlockingOperationInProgress,

    NetworkSubsystemFailed,

    SocketNotConnected,
    SystemResources,
} || UnexpectedError;

ShutdownHow

Connection was reset by peer, application should close socket as it is no longer usable. The network subsystem has failed. The socket is not connected (connection-oriented sockets only).

pub const ShutdownHow = enum { recv, send, both };

shutdown()

Shutdown socket send/receive operations

pub fn shutdown(sock: socket_t, how: ShutdownHow) ShutdownError!void {
    if (builtin.os.tag == .windows) {
        const result = windows.ws2_32.shutdown(sock, switch (how) {
            .recv => windows.ws2_32.SD_RECEIVE,
            .send => windows.ws2_32.SD_SEND,
            .both => windows.ws2_32.SD_BOTH,
        });
        if (0 != result) switch (windows.ws2_32.WSAGetLastError()) {
            .WSAECONNABORTED => return error.ConnectionAborted,
            .WSAECONNRESET => return error.ConnectionResetByPeer,
            .WSAEINPROGRESS => return error.BlockingOperationInProgress,
            .WSAEINVAL => unreachable,
            .WSAENETDOWN => return error.NetworkSubsystemFailed,
            .WSAENOTCONN => return error.SocketNotConnected,
            .WSAENOTSOCK => unreachable,
            .WSANOTINITIALISED => unreachable,
            else => |err| return windows.unexpectedWSAError(err),
        };
    } else {
        const rc = system.shutdown(sock, switch (how) {
            .recv => SHUT.RD,
            .send => SHUT.WR,
            .both => SHUT.RDWR,
        });
        switch (errno(rc)) {
            .SUCCESS => return,
            .BADF => unreachable,
            .INVAL => unreachable,
            .NOTCONN => return error.SocketNotConnected,
            .NOTSOCK => unreachable,
            .NOBUFS => return error.SystemResources,
            else => |err| return unexpectedErrno(err),
        }
    }
}

closeSocket()

pub fn closeSocket(sock: socket_t) void {
    if (builtin.os.tag == .windows) {
        windows.closesocket(sock) catch unreachable;
    } else {
        close(sock);
    }
}

BindError

pub const BindError = error{
    AccessDenied,

    AddressInUse,

    AddressNotAvailable,

    AddressFamilyNotSupported,

    SymLinkLoop,

    NameTooLong,

    FileNotFound,

    SystemResources,

    NotDir,

    ReadOnlyFileSystem,

    NetworkSubsystemFailed,

    FileDescriptorNotASocket,

    AlreadyBound,
} || UnexpectedError;

bind()

The address is protected, and the user is not the superuser. For UNIX domain sockets: Search permission is denied on a component of the path prefix. The given address is already in use, or in the case of Internet domain sockets, The port number was specified as zero in the socket address structure, but, upon attempting to bind to an ephemeral port, it was determined that all port numbers in the ephemeral port range are currently in use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). A nonexistent interface was requested or the requested address was not local. The address is not valid for the address family of socket. Too many symbolic links were encountered in resolving addr. addr is too long. A component in the directory prefix of the socket pathname does not exist. Insufficient kernel memory was available. A component of the path prefix is not a directory. The socket inode would reside on a read-only filesystem. The network subsystem has failed. addr is *const T where T is one of the sockaddr

pub fn bind(sock: socket_t, addr: *const sockaddr, len: socklen_t) BindError!void {
    if (builtin.os.tag == .windows) {
        const rc = windows.bind(sock, addr, len);
        if (rc == windows.ws2_32.SOCKET_ERROR) {
            switch (windows.ws2_32.WSAGetLastError()) {
                .WSANOTINITIALISED => unreachable, // not initialized WSA
                .WSAEACCES => return error.AccessDenied,
                .WSAEADDRINUSE => return error.AddressInUse,
                .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
                .WSAENOTSOCK => return error.FileDescriptorNotASocket,
                .WSAEFAULT => unreachable, // invalid pointers
                .WSAEINVAL => return error.AlreadyBound,
                .WSAENOBUFS => return error.SystemResources,
                .WSAENETDOWN => return error.NetworkSubsystemFailed,
                else => |err| return windows.unexpectedWSAError(err),
            }
            unreachable;
        }
        return;
    } else {
        const rc = system.bind(sock, addr, len);
        switch (errno(rc)) {
            .SUCCESS => return,
            .ACCES, .PERM => return error.AccessDenied,
            .ADDRINUSE => return error.AddressInUse,
            .BADF => unreachable, // always a race condition if this error is returned
            .INVAL => unreachable, // invalid parameters
            .NOTSOCK => unreachable, // invalid `sockfd`
            .AFNOSUPPORT => return error.AddressFamilyNotSupported,
            .ADDRNOTAVAIL => return error.AddressNotAvailable,
            .FAULT => unreachable, // invalid `addr` pointer
            .LOOP => return error.SymLinkLoop,
            .NAMETOOLONG => return error.NameTooLong,
            .NOENT => return error.FileNotFound,
            .NOMEM => return error.SystemResources,
            .NOTDIR => return error.NotDir,
            .ROFS => return error.ReadOnlyFileSystem,
            else => |err| return unexpectedErrno(err),
        }
    }
    unreachable;
}

ListenError

pub const ListenError = error{
    AddressInUse,

    FileDescriptorNotASocket,

    OperationNotSupported,

    NetworkSubsystemFailed,

    SystemResources,

    AlreadyConnected,

    SocketNotBound,
} || UnexpectedError;

listen()

Another socket is already listening on the same port. For Internet domain sockets, the socket referred to by sockfd had not previously been bound to an address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers in the ephemeral port range are currently in use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). The file descriptor sockfd does not refer to a socket. The socket is not of a type that supports the listen() operation. The network subsystem has failed. Ran out of system resources On Windows it can either run out of socket descriptors or buffer space Already connected Socket has not been bound yet

pub fn listen(sock: socket_t, backlog: u31) ListenError!void {
    if (builtin.os.tag == .windows) {
        const rc = windows.listen(sock, backlog);
        if (rc == windows.ws2_32.SOCKET_ERROR) {
            switch (windows.ws2_32.WSAGetLastError()) {
                .WSANOTINITIALISED => unreachable, // not initialized WSA
                .WSAENETDOWN => return error.NetworkSubsystemFailed,
                .WSAEADDRINUSE => return error.AddressInUse,
                .WSAEISCONN => return error.AlreadyConnected,
                .WSAEINVAL => return error.SocketNotBound,
                .WSAEMFILE, .WSAENOBUFS => return error.SystemResources,
                .WSAENOTSOCK => return error.FileDescriptorNotASocket,
                .WSAEOPNOTSUPP => return error.OperationNotSupported,
                .WSAEINPROGRESS => unreachable,
                else => |err| return windows.unexpectedWSAError(err),
            }
        }
        return;
    } else {
        const rc = system.listen(sock, backlog);
        switch (errno(rc)) {
            .SUCCESS => return,
            .ADDRINUSE => return error.AddressInUse,
            .BADF => unreachable,
            .NOTSOCK => return error.FileDescriptorNotASocket,
            .OPNOTSUPP => return error.OperationNotSupported,
            else => |err| return unexpectedErrno(err),
        }
    }
}

AcceptError

pub const AcceptError = error{
    ConnectionAborted,

    FileDescriptorNotASocket,

    ProcessFdQuotaExceeded,

    SystemFdQuotaExceeded,

    SystemResources,

    SocketNotListening,

    ProtocolFailure,

    BlockedByFirewall,

    WouldBlock,

    ConnectionResetByPeer,

    NetworkSubsystemFailed,

    OperationNotSupported,
} || UnexpectedError;

accept()

The file descriptor sockfd does not refer to a socket. The per-process limit on the number of open file descriptors has been reached. The system-wide limit on the total number of open files has been reached. Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory. Socket is not listening for new connections. Firewall rules forbid connection. This error occurs when no global event loop is configured, and accepting from the socket would block. An incoming connection was indicated, but was subsequently terminated by the remote peer prior to accepting the call. The network subsystem has failed. The referenced socket is not a type that supports connection-oriented service. Accept a connection on a socket. If sockfd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received.

pub fn accept(
    sock: socket_t,
    addr: ?*sockaddr,
    addr_size: ?*socklen_t,
    flags: u32,
) AcceptError!socket_t {
    const have_accept4 = comptime !(builtin.target.isDarwin() or builtin.os.tag == .windows);
    assert(0 == (flags & ~@as(u32, SOCK.NONBLOCK | SOCK.CLOEXEC))); // Unsupported flag(s)

    const accepted_sock = while (true) {
        const rc = if (have_accept4)
            system.accept4(sock, addr, addr_size, flags)
        else if (builtin.os.tag == .windows)
            windows.accept(sock, addr, addr_size)
        else
            system.accept(sock, addr, addr_size);

        if (builtin.os.tag == .windows) {
            if (rc == windows.ws2_32.INVALID_SOCKET) {
                switch (windows.ws2_32.WSAGetLastError()) {
                    .WSANOTINITIALISED => unreachable, // not initialized WSA
                    .WSAECONNRESET => return error.ConnectionResetByPeer,
                    .WSAEFAULT => unreachable,
                    .WSAEINVAL => return error.SocketNotListening,
                    .WSAEMFILE => return error.ProcessFdQuotaExceeded,
                    .WSAENETDOWN => return error.NetworkSubsystemFailed,
                    .WSAENOBUFS => return error.FileDescriptorNotASocket,
                    .WSAEOPNOTSUPP => return error.OperationNotSupported,
                    .WSAEWOULDBLOCK => return error.WouldBlock,
                    else => |err| return windows.unexpectedWSAError(err),
                }
            } else {
                break rc;
            }
        } else {
            switch (errno(rc)) {
                .SUCCESS => {
                    break @as(socket_t, @intCast(rc));
                },
                .INTR => continue,
                .AGAIN => return error.WouldBlock,
                .BADF => unreachable, // always a race condition
                .CONNABORTED => return error.ConnectionAborted,
                .FAULT => unreachable,
                .INVAL => return error.SocketNotListening,
                .NOTSOCK => unreachable,
                .MFILE => return error.ProcessFdQuotaExceeded,
                .NFILE => return error.SystemFdQuotaExceeded,
                .NOBUFS => return error.SystemResources,
                .NOMEM => return error.SystemResources,
                .OPNOTSUPP => unreachable,
                .PROTO => return error.ProtocolFailure,
                .PERM => return error.BlockedByFirewall,
                else => |err| return unexpectedErrno(err),
            }
        }
    };

    if (!have_accept4) {
        try setSockFlags(accepted_sock, flags);
    }
    return accepted_sock;
}

EpollCreateError

This argument is a socket that has been created with socket, bound to a local address with bind, and is listening for connections after a listen. This argument is a pointer to a sockaddr structure. This structure is filled in with the address of the peer socket, as known to the communications layer. The exact format of the address returned addr is determined by the socket's address family (see socket and the respective protocol man pages). This argument is a value-result argument: the caller must initialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address.

The returned address is truncated if the buffer provided is too small; in this case, addr_size will return a value greater than was supplied to the call. The following values can be bitwise ORed in flags to obtain different behavior: * SOCK.NONBLOCK - Set the O.NONBLOCK file status flag on the open file description (see open) referred to by the new file descriptor. Using this flag saves extra calls to fcntl to achieve the same result. * SOCK.CLOEXEC - Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description of the O.CLOEXEC flag in open for reasons why this may be useful.

pub const EpollCreateError = error{
    ProcessFdQuotaExceeded,

    SystemFdQuotaExceeded,

    SystemResources,
} || UnexpectedError;

epoll_create1()

The per-user limit on the number of epoll instances imposed by /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further details. Or, The per-process limit on the number of open file descriptors has been reached. The system-wide limit on the total number of open files has been reached. There was insufficient memory to create the kernel object.

pub fn epoll_create1(flags: u32) EpollCreateError!i32 {
    const rc = system.epoll_create1(flags);
    switch (errno(rc)) {
        .SUCCESS => return @as(i32, @intCast(rc)),
        else => |err| return unexpectedErrno(err),

        .INVAL => unreachable,
        .MFILE => return error.ProcessFdQuotaExceeded,
        .NFILE => return error.SystemFdQuotaExceeded,
        .NOMEM => return error.SystemResources,
    }
}

EpollCtlError

pub const EpollCtlError = error{
    FileDescriptorAlreadyPresentInSet,

    OperationCausesCircularLoop,

    FileDescriptorNotRegistered,

    SystemResources,

    UserResourceLimitReached,

    FileDescriptorIncompatibleWithEpoll,
} || UnexpectedError;

epoll_ctl()

op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered with this epoll instance. fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a circular loop of epoll instances monitoring one another. op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll instance. There was insufficient memory to handle the requested op control operation. The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. See epoll(7) for further details. The target file fd does not support epoll. This error can occur if fd refers to, for example, a regular file or a directory.

pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*linux.epoll_event) EpollCtlError!void {
    const rc = system.epoll_ctl(epfd, op, fd, event);
    switch (errno(rc)) {
        .SUCCESS => return,
        else => |err| return unexpectedErrno(err),

        .BADF => unreachable, // always a race condition if this happens
        .EXIST => return error.FileDescriptorAlreadyPresentInSet,
        .INVAL => unreachable,
        .LOOP => return error.OperationCausesCircularLoop,
        .NOENT => return error.FileDescriptorNotRegistered,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.UserResourceLimitReached,
        .PERM => return error.FileDescriptorIncompatibleWithEpoll,
    }
}

epoll_wait()

Waits for an I/O event on an epoll file descriptor. Returns the number of file descriptors ready for the requested I/O, or zero if no file descriptor became ready during the requested timeout milliseconds.

pub fn epoll_wait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize {
    while (true) {
        // TODO get rid of the @intCast
        const rc = system.epoll_wait(epfd, events.ptr, @as(u32, @intCast(events.len)), timeout);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .INTR => continue,
            .BADF => unreachable,
            .FAULT => unreachable,
            .INVAL => unreachable,
            else => unreachable,
        }
    }
}

EventFdError

pub const EventFdError = error{
    SystemResources,
    ProcessFdQuotaExceeded,
    SystemFdQuotaExceeded,
} || UnexpectedError;

eventfd()

pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 {
    const rc = system.eventfd(initval, flags);
    switch (errno(rc)) {
        .SUCCESS => return @as(i32, @intCast(rc)),
        else => |err| return unexpectedErrno(err),

        .INVAL => unreachable, // invalid parameters
        .MFILE => return error.ProcessFdQuotaExceeded,
        .NFILE => return error.SystemFdQuotaExceeded,
        .NODEV => return error.SystemResources,
        .NOMEM => return error.SystemResources,
    }
}

GetSockNameError

pub const GetSockNameError = error{
    SystemResources,

    NetworkSubsystemFailed,

    SocketNotBound,

    FileDescriptorNotASocket,
} || UnexpectedError;

getsockname()

Insufficient resources were available in the system to perform the operation. The network subsystem has failed. Socket hasn't been bound yet

pub fn getsockname(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
    if (builtin.os.tag == .windows) {
        const rc = windows.getsockname(sock, addr, addrlen);
        if (rc == windows.ws2_32.SOCKET_ERROR) {
            switch (windows.ws2_32.WSAGetLastError()) {
                .WSANOTINITIALISED => unreachable,
                .WSAENETDOWN => return error.NetworkSubsystemFailed,
                .WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
                .WSAENOTSOCK => return error.FileDescriptorNotASocket,
                .WSAEINVAL => return error.SocketNotBound,
                else => |err| return windows.unexpectedWSAError(err),
            }
        }
        return;
    } else {
        const rc = system.getsockname(sock, addr, addrlen);
        switch (errno(rc)) {
            .SUCCESS => return,
            else => |err| return unexpectedErrno(err),

            .BADF => unreachable, // always a race condition
            .FAULT => unreachable,
            .INVAL => unreachable, // invalid parameters
            .NOTSOCK => return error.FileDescriptorNotASocket,
            .NOBUFS => return error.SystemResources,
        }
    }
}

getpeername()

pub fn getpeername(sock: socket_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void {
    if (builtin.os.tag == .windows) {
        const rc = windows.getpeername(sock, addr, addrlen);
        if (rc == windows.ws2_32.SOCKET_ERROR) {
            switch (windows.ws2_32.WSAGetLastError()) {
                .WSANOTINITIALISED => unreachable,
                .WSAENETDOWN => return error.NetworkSubsystemFailed,
                .WSAEFAULT => unreachable, // addr or addrlen have invalid pointers or addrlen points to an incorrect value
                .WSAENOTSOCK => return error.FileDescriptorNotASocket,
                .WSAEINVAL => return error.SocketNotBound,
                else => |err| return windows.unexpectedWSAError(err),
            }
        }
        return;
    } else {
        const rc = system.getpeername(sock, addr, addrlen);
        switch (errno(rc)) {
            .SUCCESS => return,
            else => |err| return unexpectedErrno(err),

            .BADF => unreachable, // always a race condition
            .FAULT => unreachable,
            .INVAL => unreachable, // invalid parameters
            .NOTSOCK => return error.FileDescriptorNotASocket,
            .NOBUFS => return error.SystemResources,
        }
    }
}

ConnectError

pub const ConnectError = error{
    PermissionDenied,

    AddressInUse,

    AddressNotAvailable,

    AddressFamilyNotSupported,

    SystemResources,

    ConnectionRefused,

    NetworkUnreachable,

    ConnectionTimedOut,

    WouldBlock,

    FileNotFound,

    ConnectionResetByPeer,

    ConnectionPending,
} || UnexpectedError;

connect()

For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket file, or search permission is denied for one of the directories in the path prefix. or The user tried to connect to a broadcast address without having the socket broadcast flag enabled or the connection request failed because of a local firewall rule. Local address is already in use. (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers in the ephemeral port range are currently in use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). The passed address didn't have the correct address family in its sa_family field. Insufficient entries in the routing cache. A connect() on a stream socket found no one listening on the remote address. Network is unreachable. Timeout while attempting connection. The server may be too busy to accept new connections. Note that for IP sockets the timeout may be very long when syncookies are enabled on the server. This error occurs when no global event loop is configured, and connecting to the socket would block. The given path for the unix socket does not exist. Connection was reset by peer before connect could complete. Socket is non-blocking and already has a pending connection in progress. Initiate a connection on a socket. If sockfd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN or EINPROGRESS is received.

pub fn connect(sock: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void {
    if (builtin.os.tag == .windows) {
        const rc = windows.ws2_32.connect(sock, sock_addr, @as(i32, @intCast(len)));
        if (rc == 0) return;
        switch (windows.ws2_32.WSAGetLastError()) {
            .WSAEADDRINUSE => return error.AddressInUse,
            .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
            .WSAECONNREFUSED => return error.ConnectionRefused,
            .WSAECONNRESET => return error.ConnectionResetByPeer,
            .WSAETIMEDOUT => return error.ConnectionTimedOut,
            .WSAEHOSTUNREACH, // TODO: should we return NetworkUnreachable in this case as well?
            .WSAENETUNREACH,
            => return error.NetworkUnreachable,
            .WSAEFAULT => unreachable,
            .WSAEINVAL => unreachable,
            .WSAEISCONN => unreachable,
            .WSAENOTSOCK => unreachable,
            .WSAEWOULDBLOCK => return error.WouldBlock,
            .WSAEACCES => unreachable,
            .WSAENOBUFS => return error.SystemResources,
            .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
            else => |err| return windows.unexpectedWSAError(err),
        }
        return;
    }

    while (true) {
        switch (errno(system.connect(sock, sock_addr, len))) {
            .SUCCESS => return,
            .ACCES => return error.PermissionDenied,
            .PERM => return error.PermissionDenied,
            .ADDRINUSE => return error.AddressInUse,
            .ADDRNOTAVAIL => return error.AddressNotAvailable,
            .AFNOSUPPORT => return error.AddressFamilyNotSupported,
            .AGAIN, .INPROGRESS => return error.WouldBlock,
            .ALREADY => return error.ConnectionPending,
            .BADF => unreachable, // sockfd is not a valid open file descriptor.
            .CONNREFUSED => return error.ConnectionRefused,
            .CONNRESET => return error.ConnectionResetByPeer,
            .FAULT => unreachable, // The socket structure address is outside the user's address space.
            .INTR => continue,
            .ISCONN => unreachable, // The socket is already connected.
            .HOSTUNREACH => return error.NetworkUnreachable,
            .NETUNREACH => return error.NetworkUnreachable,
            .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
            .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
            .TIMEDOUT => return error.ConnectionTimedOut,
            .NOENT => return error.FileNotFound, // Returned when socket is AF.UNIX and the given path does not exist.
            .CONNABORTED => unreachable, // Tried to reuse socket that previously received error.ConnectionRefused.
            else => |err| return unexpectedErrno(err),
        }
    }
}

getsockoptError()

pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
    var err_code: i32 = undefined;
    var size: u32 = @sizeOf(u32);
    const rc = system.getsockopt(sockfd, SOL.SOCKET, SO.ERROR, @as([*]u8, @ptrCast(&err_code)), &size);
    assert(size == 4);
    switch (errno(rc)) {
        .SUCCESS => switch (@as(E, @enumFromInt(err_code))) {
            .SUCCESS => return,
            .ACCES => return error.PermissionDenied,
            .PERM => return error.PermissionDenied,
            .ADDRINUSE => return error.AddressInUse,
            .ADDRNOTAVAIL => return error.AddressNotAvailable,
            .AFNOSUPPORT => return error.AddressFamilyNotSupported,
            .AGAIN => return error.SystemResources,
            .ALREADY => return error.ConnectionPending,
            .BADF => unreachable, // sockfd is not a valid open file descriptor.
            .CONNREFUSED => return error.ConnectionRefused,
            .FAULT => unreachable, // The socket structure address is outside the user's address space.
            .ISCONN => unreachable, // The socket is already connected.
            .HOSTUNREACH => return error.NetworkUnreachable,
            .NETUNREACH => return error.NetworkUnreachable,
            .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
            .PROTOTYPE => unreachable, // The socket type does not support the requested communications protocol.
            .TIMEDOUT => return error.ConnectionTimedOut,
            .CONNRESET => return error.ConnectionResetByPeer,
            else => |err| return unexpectedErrno(err),
        },
        .BADF => unreachable, // The argument sockfd is not a valid file descriptor.
        .FAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space.
        .INVAL => unreachable,
        .NOPROTOOPT => unreachable, // The option is unknown at the level indicated.
        .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
        else => |err| return unexpectedErrno(err),
    }
}

WaitPidResult

pub const WaitPidResult = struct {
    pid: pid_t,
    status: u32,
};

waitpid()

Use this version of the waitpid wrapper if you spawned your child process using explicit fork and execve method.

pub fn waitpid(pid: pid_t, flags: u32) WaitPidResult {
    const Status = if (builtin.link_libc) c_int else u32;
    var status: Status = undefined;
    const coerced_flags = if (builtin.link_libc) @as(c_int, @intCast(flags)) else flags;
    while (true) {
        const rc = system.waitpid(pid, &status, coerced_flags);
        switch (errno(rc)) {
            .SUCCESS => return .{
                .pid = @as(pid_t, @intCast(rc)),
                .status = @as(u32, @bitCast(status)),
            },
            .INTR => continue,
            .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
            .INVAL => unreachable, // Invalid flags.
            else => unreachable,
        }
    }
}

wait4()

pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult {
    const Status = if (builtin.link_libc) c_int else u32;
    var status: Status = undefined;
    const coerced_flags = if (builtin.link_libc) @as(c_int, @intCast(flags)) else flags;
    while (true) {
        const rc = system.wait4(pid, &status, coerced_flags, ru);
        switch (errno(rc)) {
            .SUCCESS => return .{
                .pid = @as(pid_t, @intCast(rc)),
                .status = @as(u32, @bitCast(status)),
            },
            .INTR => continue,
            .CHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error.
            .INVAL => unreachable, // Invalid flags.
            else => unreachable,
        }
    }
}

FStatError

pub const FStatError = error{
    SystemResources,

    AccessDenied,
} || UnexpectedError;

fstat()

In WASI, this error may occur when the file descriptor does not hold the required rights to get its filestat information. Return information about a file descriptor.

pub fn fstat(fd: fd_t) FStatError!Stat {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var stat: wasi.filestat_t = undefined;
        switch (wasi.fd_filestat_get(fd, &stat)) {
            .SUCCESS => return Stat.fromFilestat(stat),
            .INVAL => unreachable,
            .BADF => unreachable, // Always a race condition.
            .NOMEM => return error.SystemResources,
            .ACCES => return error.AccessDenied,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }
    if (builtin.os.tag == .windows) {
        @compileError("fstat is not yet implemented on Windows");
    }

    const fstat_sym = if (lfs64_abi) system.fstat64 else system.fstat;

    var stat = mem.zeroes(Stat);
    switch (errno(fstat_sym(fd, &stat))) {
        .SUCCESS => return stat,
        .INVAL => unreachable,
        .BADF => unreachable, // Always a race condition.
        .NOMEM => return error.SystemResources,
        .ACCES => return error.AccessDenied,
        else => |err| return unexpectedErrno(err),
    }
}

FStatAtError

pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound, SymLinkLoop };

fstatat()

Similar to fstat, but returns stat of a resource pointed to by pathname which is relative to dirfd handle. See also fstatatZ and fstatatWasi.

pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        const wasi_flags = if (flags & linux.AT.SYMLINK_NOFOLLOW == 0) wasi.LOOKUP_SYMLINK_FOLLOW else 0;
        return fstatatWasi(dirfd, pathname, wasi_flags);
    } else if (builtin.os.tag == .windows) {
        @compileError("fstatat is not yet implemented on Windows");
    } else {
        const pathname_c = try toPosixPath(pathname);
        return fstatatZ(dirfd, &pathname_c, flags);
    }
}

fstatatWasi()

WASI-only. Same as fstatat but targeting WASI. See also fstatat.

pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat {
    var stat: wasi.filestat_t = undefined;
    switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) {
        .SUCCESS => return Stat.fromFilestat(stat),
        .INVAL => unreachable,
        .BADF => unreachable, // Always a race condition.
        .NOMEM => return error.SystemResources,
        .ACCES => return error.AccessDenied,
        .FAULT => unreachable,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.FileNotFound,
        .NOTCAPABLE => return error.AccessDenied,
        else => |err| return unexpectedErrno(err),
    }
}

fstatatZ()

Same as fstatat but pathname is null-terminated. See also fstatat.

pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return fstatatWasi(dirfd, mem.sliceTo(pathname), flags);
    }

    const fstatat_sym = if (lfs64_abi) system.fstatat64 else system.fstatat;

    var stat = mem.zeroes(Stat);
    switch (errno(fstatat_sym(dirfd, pathname, &stat, flags))) {
        .SUCCESS => return stat,
        .INVAL => unreachable,
        .BADF => unreachable, // Always a race condition.
        .NOMEM => return error.SystemResources,
        .ACCES => return error.AccessDenied,
        .PERM => return error.AccessDenied,
        .FAULT => unreachable,
        .NAMETOOLONG => return error.NameTooLong,
        .LOOP => return error.SymLinkLoop,
        .NOENT => return error.FileNotFound,
        .NOTDIR => return error.FileNotFound,
        else => |err| return unexpectedErrno(err),
    }
}

KQueueError

pub const KQueueError = error{
    ProcessFdQuotaExceeded,

    SystemFdQuotaExceeded,
} || UnexpectedError;

kqueue()

The per-process limit on the number of open file descriptors has been reached. The system-wide limit on the total number of open files has been reached.

pub fn kqueue() KQueueError!i32 {
    const rc = system.kqueue();
    switch (errno(rc)) {
        .SUCCESS => return @as(i32, @intCast(rc)),
        .MFILE => return error.ProcessFdQuotaExceeded,
        .NFILE => return error.SystemFdQuotaExceeded,
        else => |err| return unexpectedErrno(err),
    }
}

KEventError

pub const KEventError = error{
    AccessDenied,

    EventNotFound,

    SystemResources,

    ProcessNotFound,

    Overflow,
};

kevent()

The process does not have permission to register a filter. The event could not be found to be modified or deleted. No memory was available to register the event. The specified process to attach to does not exist. changelist or eventlist had too many items on it. TODO remove this possibility

pub fn kevent(
    kq: i32,
    changelist: []const Kevent,
    eventlist: []Kevent,
    timeout: ?*const timespec,
) KEventError!usize {
    while (true) {
        const rc = system.kevent(
            kq,
            changelist.ptr,
            math.cast(c_int, changelist.len) orelse return error.Overflow,
            eventlist.ptr,
            math.cast(c_int, eventlist.len) orelse return error.Overflow,
            timeout,
        );
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .ACCES => return error.AccessDenied,
            .FAULT => unreachable,
            .BADF => unreachable, // Always a race condition.
            .INTR => continue,
            .INVAL => unreachable,
            .NOENT => return error.EventNotFound,
            .NOMEM => return error.SystemResources,
            .SRCH => return error.ProcessNotFound,
            else => unreachable,
        }
    }
}

INotifyInitError

pub const INotifyInitError = error{
    ProcessFdQuotaExceeded,
    SystemFdQuotaExceeded,
    SystemResources,
} || UnexpectedError;

inotify_init1()

initialize an inotify instance

pub fn inotify_init1(flags: u32) INotifyInitError!i32 {
    const rc = system.inotify_init1(flags);
    switch (errno(rc)) {
        .SUCCESS => return @as(i32, @intCast(rc)),
        .INVAL => unreachable,
        .MFILE => return error.ProcessFdQuotaExceeded,
        .NFILE => return error.SystemFdQuotaExceeded,
        .NOMEM => return error.SystemResources,
        else => |err| return unexpectedErrno(err),
    }
}

INotifyAddWatchError

pub const INotifyAddWatchError = error{
    AccessDenied,
    NameTooLong,
    FileNotFound,
    SystemResources,
    UserResourceLimitReached,
    NotDir,
    WatchAlreadyExists,
} || UnexpectedError;

inotify_add_watch()

add a watch to an initialized inotify instance

pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 {
    const pathname_c = try toPosixPath(pathname);
    return inotify_add_watchZ(inotify_fd, &pathname_c, mask);
}

inotify_add_watchZ()

Same as inotify_add_watch except pathname is null-terminated.

pub fn inotify_add_watchZ(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) INotifyAddWatchError!i32 {
    const rc = system.inotify_add_watch(inotify_fd, pathname, mask);
    switch (errno(rc)) {
        .SUCCESS => return @as(i32, @intCast(rc)),
        .ACCES => return error.AccessDenied,
        .BADF => unreachable,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .NAMETOOLONG => return error.NameTooLong,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.UserResourceLimitReached,
        .NOTDIR => return error.NotDir,
        .EXIST => return error.WatchAlreadyExists,
        else => |err| return unexpectedErrno(err),
    }
}

inotify_rm_watch()

remove an existing watch from an inotify instance

pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void {
    switch (errno(system.inotify_rm_watch(inotify_fd, wd))) {
        .SUCCESS => return,
        .BADF => unreachable,
        .INVAL => unreachable,
        else => unreachable,
    }
}

FanotifyInitError

pub const FanotifyInitError = error{
    ProcessFdQuotaExceeded,
    SystemFdQuotaExceeded,
    SystemResources,
    OperationNotSupported,
    PermissionDenied,
} || UnexpectedError;

fanotify_init()

pub fn fanotify_init(flags: u32, event_f_flags: u32) FanotifyInitError!i32 {
    const rc = system.fanotify_init(flags, event_f_flags);
    switch (errno(rc)) {
        .SUCCESS => return @as(i32, @intCast(rc)),
        .INVAL => unreachable,
        .MFILE => return error.ProcessFdQuotaExceeded,
        .NFILE => return error.SystemFdQuotaExceeded,
        .NOMEM => return error.SystemResources,
        .NOSYS => return error.OperationNotSupported,
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

FanotifyMarkError

pub const FanotifyMarkError = error{
    MarkAlreadyExists,
    IsDir,
    NotAssociatedWithFileSystem,
    FileNotFound,
    SystemResources,
    UserMarkQuotaExceeded,
    NotImplemented,
    NotDir,
    OperationNotSupported,
    PermissionDenied,
    NotSameFileSystem,
    NameTooLong,
} || UnexpectedError;

fanotify_mark()

pub fn fanotify_mark(fanotify_fd: i32, flags: u32, mask: u64, dirfd: i32, pathname: ?[]const u8) FanotifyMarkError!void {
    if (pathname) |path| {
        const path_c = try toPosixPath(path);
        return fanotify_markZ(fanotify_fd, flags, mask, dirfd, &path_c);
    }

    return fanotify_markZ(fanotify_fd, flags, mask, dirfd, null);
}

fanotify_markZ()

pub fn fanotify_markZ(fanotify_fd: i32, flags: u32, mask: u64, dirfd: i32, pathname: ?[*:0]const u8) FanotifyMarkError!void {
    const rc = system.fanotify_mark(fanotify_fd, flags, mask, dirfd, pathname);
    switch (errno(rc)) {
        .SUCCESS => return,
        .BADF => unreachable,
        .EXIST => return error.MarkAlreadyExists,
        .INVAL => unreachable,
        .ISDIR => return error.IsDir,
        .NODEV => return error.NotAssociatedWithFileSystem,
        .NOENT => return error.FileNotFound,
        .NOMEM => return error.SystemResources,
        .NOSPC => return error.UserMarkQuotaExceeded,
        .NOSYS => return error.NotImplemented,
        .NOTDIR => return error.NotDir,
        .OPNOTSUPP => return error.OperationNotSupported,
        .PERM => return error.PermissionDenied,
        .XDEV => return error.NotSameFileSystem,
        else => |err| return unexpectedErrno(err),
    }
}

MProtectError

pub const MProtectError = error{
    AccessDenied,

    OutOfMemory,
} || UnexpectedError;

mprotect()

The memory cannot be given the specified access. This can happen, for example, if you mmap(2) a file to which you have read-only access, then ask mprotect() to mark it PROT_WRITE. Changing the protection of a memory region would result in the total number of map‐ pings with distinct attributes (e.g., read versus read/write protection) exceeding the allowed maximum. (For example, making the protection of a range PROT_READ in the mid‐ dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐ pings: two read/write mappings at each end and a read-only mapping in the middle.) memory.len must be page-aligned.

pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void {
    assert(mem.isAligned(memory.len, mem.page_size));
    if (builtin.os.tag == .windows) {
        const win_prot: windows.DWORD = switch (@as(u3, @truncate(protection))) {
            0b000 => windows.PAGE_NOACCESS,
            0b001 => windows.PAGE_READONLY,
            0b010 => unreachable, // +w -r not allowed
            0b011 => windows.PAGE_READWRITE,
            0b100 => windows.PAGE_EXECUTE,
            0b101 => windows.PAGE_EXECUTE_READ,
            0b110 => unreachable, // +w -r not allowed
            0b111 => windows.PAGE_EXECUTE_READWRITE,
        };
        var old: windows.DWORD = undefined;
        windows.VirtualProtect(memory.ptr, memory.len, win_prot, &old) catch |err| switch (err) {
            error.InvalidAddress => return error.AccessDenied,
            error.Unexpected => return error.Unexpected,
        };
    } else {
        switch (errno(system.mprotect(memory.ptr, memory.len, protection))) {
            .SUCCESS => return,
            .INVAL => unreachable,
            .ACCES => return error.AccessDenied,
            .NOMEM => return error.OutOfMemory,
            else => |err| return unexpectedErrno(err),
        }
    }
}

ForkError

pub const ForkError = error{SystemResources} || UnexpectedError;

fork()

pub fn fork() ForkError!pid_t {
    const rc = system.fork();
    switch (errno(rc)) {
        .SUCCESS => return @as(pid_t, @intCast(rc)),
        .AGAIN => return error.SystemResources,
        .NOMEM => return error.SystemResources,
        else => |err| return unexpectedErrno(err),
    }
}

MMapError

pub const MMapError = error{
    MemoryMappingNotSupported,

    AccessDenied,

    PermissionDenied,
    LockedMemoryLimitExceeded,
    ProcessFdQuotaExceeded,
    SystemFdQuotaExceeded,
    OutOfMemory,
} || UnexpectedError;

mmap()

The underlying filesystem of the specified file does not support memory mapping. A file descriptor refers to a non-regular file. Or a file mapping was requested, but the file descriptor is not open for reading. Or MAP.SHARED was requested and PROT_WRITE is set, but the file descriptor is not open in O.RDWR mode. Or PROT_WRITE is set, but the file is append-only. The prot argument asks for PROT_EXEC but the mapped area belongs to a file on a filesystem that was mounted no-exec. Map files or devices into memory. length does not need to be aligned. Use of a mapped region can result in these signals: * SIGSEGV - Attempted write into a region mapped as read-only. * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file

pub fn mmap(
    ptr: ?[*]align(mem.page_size) u8,
    length: usize,
    prot: u32,
    flags: u32,
    fd: fd_t,
    offset: u64,
) MMapError![]align(mem.page_size) u8 {
    const mmap_sym = if (lfs64_abi) system.mmap64 else system.mmap;

    const ioffset = @as(i64, @bitCast(offset)); // the OS treats this as unsigned
    const rc = mmap_sym(ptr, length, prot, flags, fd, ioffset);
    const err = if (builtin.link_libc) blk: {
        if (rc != std.c.MAP.FAILED) return @as([*]align(mem.page_size) u8, @ptrCast(@alignCast(rc)))[0..length];
        break :blk @as(E, @enumFromInt(system._errno().*));
    } else blk: {
        const err = errno(rc);
        if (err == .SUCCESS) return @as([*]align(mem.page_size) u8, @ptrFromInt(rc))[0..length];
        break :blk err;
    };
    switch (err) {
        .SUCCESS => unreachable,
        .TXTBSY => return error.AccessDenied,
        .ACCES => return error.AccessDenied,
        .PERM => return error.PermissionDenied,
        .AGAIN => return error.LockedMemoryLimitExceeded,
        .BADF => unreachable, // Always a race condition.
        .OVERFLOW => unreachable, // The number of pages used for length + offset would overflow.
        .NODEV => return error.MemoryMappingNotSupported,
        .INVAL => unreachable, // Invalid parameters to mmap()
        .MFILE => return error.ProcessFdQuotaExceeded,
        .NFILE => return error.SystemFdQuotaExceeded,
        .NOMEM => return error.OutOfMemory,
        else => return unexpectedErrno(err),
    }
}

munmap()

Deletes the mappings for the specified address range, causing further references to addresses within the range to generate invalid memory references. Note that while POSIX allows unmapping a region in the middle of an existing mapping, Zig's munmap function does not, for two reasons: * It violates the Zig principle that resource deallocation must succeed. * The Windows function, VirtualFree, has this restriction.

pub fn munmap(memory: []align(mem.page_size) const u8) void {
    switch (errno(system.munmap(memory.ptr, memory.len))) {
        .SUCCESS => return,
        .INVAL => unreachable, // Invalid parameters.
        .NOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping.
        else => unreachable,
    }
}

MSyncError

pub const MSyncError = error{
    UnmappedMemory,
} || UnexpectedError;

msync()

pub fn msync(memory: []align(mem.page_size) u8, flags: i32) MSyncError!void {
    switch (errno(system.msync(memory.ptr, memory.len, flags))) {
        .SUCCESS => return,
        .NOMEM => return error.UnmappedMemory, // Unsuccessful, provided pointer does not point mapped memory
        .INVAL => unreachable, // Invalid parameters.
        else => unreachable,
    }
}

AccessError

pub const AccessError = error{
    PermissionDenied,
    FileNotFound,
    NameTooLong,
    InputOutput,
    SystemResources,
    BadPathName,
    FileBusy,
    SymLinkLoop,
    ReadOnlyFileSystem,

    InvalidUtf8,
} || UnexpectedError;

access()

On Windows, file paths must be valid Unicode. check user's permissions for a file TODO currently this assumes mode is F.OK on Windows.

pub fn access(path: []const u8, mode: u32) AccessError!void {
    if (builtin.os.tag == .windows) {
        const path_w = windows.sliceToPrefixedFileW(null, path) catch |err| switch (err) {
            error.AccessDenied => return error.PermissionDenied,
            else => |e| return e,
        };
        _ = try windows.GetFileAttributesW(path_w.span().ptr);
        return;
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return faccessat(wasi.AT.FDCWD, path, mode, 0);
    }
    const path_c = try toPosixPath(path);
    return accessZ(&path_c, mode);
}

accessZ()

Same as access except path is null-terminated.

pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
    if (builtin.os.tag == .windows) {
        const path_w = windows.cStrToPrefixedFileW(null, path) catch |err| switch (err) {
            error.AccessDenied => return error.PermissionDenied,
            else => |e| return e,
        };
        _ = try windows.GetFileAttributesW(path_w.span().ptr);
        return;
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return access(mem.sliceTo(path, 0), mode);
    }
    switch (errno(system.access(path, mode))) {
        .SUCCESS => return,
        .ACCES => return error.PermissionDenied,
        .ROFS => return error.ReadOnlyFileSystem,
        .LOOP => return error.SymLinkLoop,
        .TXTBSY => return error.FileBusy,
        .NOTDIR => return error.FileNotFound,
        .NOENT => return error.FileNotFound,
        .NAMETOOLONG => return error.NameTooLong,
        .INVAL => unreachable,
        .FAULT => unreachable,
        .IO => return error.InputOutput,
        .NOMEM => return error.SystemResources,
        else => |err| return unexpectedErrno(err),
    }
}

accessW()

Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. Otherwise use access or accessC. TODO currently this ignores mode.

pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!void {
    _ = mode;
    const ret = try windows.GetFileAttributesW(path);
    if (ret != windows.INVALID_FILE_ATTRIBUTES) {
        return;
    }
    switch (windows.kernel32.GetLastError()) {
        .FILE_NOT_FOUND => return error.FileNotFound,
        .PATH_NOT_FOUND => return error.FileNotFound,
        .ACCESS_DENIED => return error.PermissionDenied,
        else => |err| return windows.unexpectedError(err),
    }
}

faccessat()

Check user's permissions for a file, based on an open directory handle. TODO currently this ignores mode and flags on Windows.

pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
    if (builtin.os.tag == .windows) {
        const path_w = try windows.sliceToPrefixedFileW(dirfd, path);
        return faccessatW(dirfd, path_w.span().ptr, mode, flags);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var resolved = RelativePathWasi{ .dir_fd = dirfd, .relative_path = path };

        const file = blk: {
            break :blk fstatat(dirfd, path, flags);
        } catch |err| switch (err) {
            error.AccessDenied => return error.PermissionDenied,
            else => |e| return e,
        };

        if (mode != F_OK) {
            var directory: wasi.fdstat_t = undefined;
            if (wasi.fd_fdstat_get(resolved.dir_fd, &directory) != .SUCCESS) {
                return error.PermissionDenied;
            }

            var rights: wasi.rights_t = 0;
            if (mode & R_OK != 0) {
                rights |= if (file.filetype == .DIRECTORY)
                    wasi.RIGHT.FD_READDIR
                else
                    wasi.RIGHT.FD_READ;
            }
            if (mode & W_OK != 0) {
                rights |= wasi.RIGHT.FD_WRITE;
            }
            // No validation for X_OK

            if ((rights & directory.fs_rights_inheriting) != rights) {
                return error.PermissionDenied;
            }
        }
        return;
    }
    const path_c = try toPosixPath(path);
    return faccessatZ(dirfd, &path_c, mode, flags);
}

faccessatZ()

Same as faccessat except the path parameter is null-terminated.

pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
    if (builtin.os.tag == .windows) {
        const path_w = try windows.cStrToPrefixedFileW(dirfd, path);
        return faccessatW(dirfd, path_w.span().ptr, mode, flags);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return faccessat(dirfd, mem.sliceTo(path, 0), mode, flags);
    }
    switch (errno(system.faccessat(dirfd, path, mode, flags))) {
        .SUCCESS => return,
        .ACCES => return error.PermissionDenied,
        .ROFS => return error.ReadOnlyFileSystem,
        .LOOP => return error.SymLinkLoop,
        .TXTBSY => return error.FileBusy,
        .NOTDIR => return error.FileNotFound,
        .NOENT => return error.FileNotFound,
        .NAMETOOLONG => return error.NameTooLong,
        .INVAL => unreachable,
        .FAULT => unreachable,
        .IO => return error.InputOutput,
        .NOMEM => return error.SystemResources,
        else => |err| return unexpectedErrno(err),
    }
}

faccessatW()

Same as faccessat except asserts the target is Windows and the path parameter is NtDll-prefixed, null-terminated, WTF-16 encoded. TODO currently this ignores mode and flags

pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32) AccessError!void {
    _ = mode;
    _ = flags;
    if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
        return;
    }
    if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
        return;
    }

    const path_len_bytes = math.cast(u16, mem.sliceTo(sub_path_w, 0).len * 2) orelse return error.NameTooLong;
    var nt_name = windows.UNICODE_STRING{
        .Length = path_len_bytes,
        .MaximumLength = path_len_bytes,
        .Buffer = @constCast(sub_path_w),
    };
    var attr = windows.OBJECT_ATTRIBUTES{
        .Length = @sizeOf(windows.OBJECT_ATTRIBUTES),
        .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
        .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
        .ObjectName = &nt_name,
        .SecurityDescriptor = null,
        .SecurityQualityOfService = null,
    };
    var basic_info: windows.FILE_BASIC_INFORMATION = undefined;
    switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) {
        .SUCCESS => return,
        .OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
        .OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
        .OBJECT_NAME_INVALID => unreachable,
        .INVALID_PARAMETER => unreachable,
        .ACCESS_DENIED => return error.PermissionDenied,
        .OBJECT_PATH_SYNTAX_BAD => unreachable,
        else => |rc| return windows.unexpectedStatus(rc),
    }
}

PipeError

pub const PipeError = error{
    SystemFdQuotaExceeded,
    ProcessFdQuotaExceeded,
} || UnexpectedError;

pipe()

Creates a unidirectional data channel that can be used for interprocess communication.

pub fn pipe() PipeError![2]fd_t {
    var fds: [2]fd_t = undefined;
    switch (errno(system.pipe(&fds))) {
        .SUCCESS => return fds,
        .INVAL => unreachable, // Invalid parameters to pipe()
        .FAULT => unreachable, // Invalid fds pointer
        .NFILE => return error.SystemFdQuotaExceeded,
        .MFILE => return error.ProcessFdQuotaExceeded,
        else => |err| return unexpectedErrno(err),
    }
}

pipe2()

pub fn pipe2(flags: u32) PipeError![2]fd_t {
    if (@hasDecl(system, "pipe2")) {
        var fds: [2]fd_t = undefined;
        switch (errno(system.pipe2(&fds, flags))) {
            .SUCCESS => return fds,
            .INVAL => unreachable, // Invalid flags
            .FAULT => unreachable, // Invalid fds pointer
            .NFILE => return error.SystemFdQuotaExceeded,
            .MFILE => return error.ProcessFdQuotaExceeded,
            else => |err| return unexpectedErrno(err),
        }
    }

    var fds: [2]fd_t = try pipe();
    errdefer {
        close(fds[0]);
        close(fds[1]);
    }

    if (flags == 0)
        return fds;

    // O.CLOEXEC is special, it's a file descriptor flag and must be set using
    // F.SETFD.
    if (flags & O.CLOEXEC != 0) {
        for (fds) |fd| {
            switch (errno(system.fcntl(fd, F.SETFD, @as(u32, FD_CLOEXEC)))) {
                .SUCCESS => {},
                .INVAL => unreachable, // Invalid flags
                .BADF => unreachable, // Always a race condition
                else => |err| return unexpectedErrno(err),
            }
        }
    }

    const new_flags = flags & ~@as(u32, O.CLOEXEC);
    // Set every other flag affecting the file status using F.SETFL.
    if (new_flags != 0) {
        for (fds) |fd| {
            switch (errno(system.fcntl(fd, F.SETFL, new_flags))) {
                .SUCCESS => {},
                .INVAL => unreachable, // Invalid flags
                .BADF => unreachable, // Always a race condition
                else => |err| return unexpectedErrno(err),
            }
        }
    }

    return fds;
}

SysCtlError

pub const SysCtlError = error{
    PermissionDenied,
    SystemResources,
    NameTooLong,
    UnknownName,
} || UnexpectedError;

sysctl()

pub fn sysctl(
    name: []const c_int,
    oldp: ?*anyopaque,
    oldlenp: ?*usize,
    newp: ?*anyopaque,
    newlen: usize,
) SysCtlError!void {
    if (builtin.os.tag == .wasi) {
        @panic("unsupported"); // TODO should be compile error, not panic
    }
    if (builtin.os.tag == .haiku) {
        @panic("unsupported"); // TODO should be compile error, not panic
    }

    const name_len = math.cast(c_uint, name.len) orelse return error.NameTooLong;
    switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) {
        .SUCCESS => return,
        .FAULT => unreachable,
        .PERM => return error.PermissionDenied,
        .NOMEM => return error.SystemResources,
        .NOENT => return error.UnknownName,
        else => |err| return unexpectedErrno(err),
    }
}

sysctlbynameZ()

pub fn sysctlbynameZ(
    name: [*:0]const u8,
    oldp: ?*anyopaque,
    oldlenp: ?*usize,
    newp: ?*anyopaque,
    newlen: usize,
) SysCtlError!void {
    if (builtin.os.tag == .wasi) {
        @panic("unsupported"); // TODO should be compile error, not panic
    }
    if (builtin.os.tag == .haiku) {
        @panic("unsupported"); // TODO should be compile error, not panic
    }

    switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) {
        .SUCCESS => return,
        .FAULT => unreachable,
        .PERM => return error.PermissionDenied,
        .NOMEM => return error.SystemResources,
        .NOENT => return error.UnknownName,
        else => |err| return unexpectedErrno(err),
    }
}

gettimeofday()

pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void {
    switch (errno(system.gettimeofday(tv, tz))) {
        .SUCCESS => return,
        .INVAL => unreachable,
        else => unreachable,
    }
}

SeekError

pub const SeekError = error{
    Unseekable,

    AccessDenied,
} || UnexpectedError;

lseek_SET()

In WASI, this error may occur when the file descriptor does not hold the required rights to seek on it. Repositions read/write file offset relative to the beginning.

pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void {
    if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
        var result: u64 = undefined;
        switch (errno(system.llseek(fd, offset, &result, SEEK.SET))) {
            .SUCCESS => return,
            .BADF => unreachable, // always a race condition
            .INVAL => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .NXIO => return error.Unseekable,
            else => |err| return unexpectedErrno(err),
        }
    }
    if (builtin.os.tag == .windows) {
        return windows.SetFilePointerEx_BEGIN(fd, offset);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var new_offset: wasi.filesize_t = undefined;
        switch (wasi.fd_seek(fd, @as(wasi.filedelta_t, @bitCast(offset)), .SET, &new_offset)) {
            .SUCCESS => return,
            .BADF => unreachable, // always a race condition
            .INVAL => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .NXIO => return error.Unseekable,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }

    const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;

    const ioffset = @as(i64, @bitCast(offset)); // the OS treats this as unsigned
    switch (errno(lseek_sym(fd, ioffset, SEEK.SET))) {
        .SUCCESS => return,
        .BADF => unreachable, // always a race condition
        .INVAL => return error.Unseekable,
        .OVERFLOW => return error.Unseekable,
        .SPIPE => return error.Unseekable,
        .NXIO => return error.Unseekable,
        else => |err| return unexpectedErrno(err),
    }
}

lseek_CUR()

Repositions read/write file offset relative to the current offset.

pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void {
    if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
        var result: u64 = undefined;
        switch (errno(system.llseek(fd, @as(u64, @bitCast(offset)), &result, SEEK.CUR))) {
            .SUCCESS => return,
            .BADF => unreachable, // always a race condition
            .INVAL => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .NXIO => return error.Unseekable,
            else => |err| return unexpectedErrno(err),
        }
    }
    if (builtin.os.tag == .windows) {
        return windows.SetFilePointerEx_CURRENT(fd, offset);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var new_offset: wasi.filesize_t = undefined;
        switch (wasi.fd_seek(fd, offset, .CUR, &new_offset)) {
            .SUCCESS => return,
            .BADF => unreachable, // always a race condition
            .INVAL => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .NXIO => return error.Unseekable,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }
    const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;

    const ioffset = @as(i64, @bitCast(offset)); // the OS treats this as unsigned
    switch (errno(lseek_sym(fd, ioffset, SEEK.CUR))) {
        .SUCCESS => return,
        .BADF => unreachable, // always a race condition
        .INVAL => return error.Unseekable,
        .OVERFLOW => return error.Unseekable,
        .SPIPE => return error.Unseekable,
        .NXIO => return error.Unseekable,
        else => |err| return unexpectedErrno(err),
    }
}

lseek_END()

Repositions read/write file offset relative to the end.

pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void {
    if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
        var result: u64 = undefined;
        switch (errno(system.llseek(fd, @as(u64, @bitCast(offset)), &result, SEEK.END))) {
            .SUCCESS => return,
            .BADF => unreachable, // always a race condition
            .INVAL => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .NXIO => return error.Unseekable,
            else => |err| return unexpectedErrno(err),
        }
    }
    if (builtin.os.tag == .windows) {
        return windows.SetFilePointerEx_END(fd, offset);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var new_offset: wasi.filesize_t = undefined;
        switch (wasi.fd_seek(fd, offset, .END, &new_offset)) {
            .SUCCESS => return,
            .BADF => unreachable, // always a race condition
            .INVAL => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .NXIO => return error.Unseekable,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }
    const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;

    const ioffset = @as(i64, @bitCast(offset)); // the OS treats this as unsigned
    switch (errno(lseek_sym(fd, ioffset, SEEK.END))) {
        .SUCCESS => return,
        .BADF => unreachable, // always a race condition
        .INVAL => return error.Unseekable,
        .OVERFLOW => return error.Unseekable,
        .SPIPE => return error.Unseekable,
        .NXIO => return error.Unseekable,
        else => |err| return unexpectedErrno(err),
    }
}

lseek_CUR_get()

Returns the read/write file offset relative to the beginning.

pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
    if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) {
        var result: u64 = undefined;
        switch (errno(system.llseek(fd, 0, &result, SEEK.CUR))) {
            .SUCCESS => return result,
            .BADF => unreachable, // always a race condition
            .INVAL => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .NXIO => return error.Unseekable,
            else => |err| return unexpectedErrno(err),
        }
    }
    if (builtin.os.tag == .windows) {
        return windows.SetFilePointerEx_CURRENT_get(fd);
    }
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var new_offset: wasi.filesize_t = undefined;
        switch (wasi.fd_seek(fd, 0, .CUR, &new_offset)) {
            .SUCCESS => return new_offset,
            .BADF => unreachable, // always a race condition
            .INVAL => return error.Unseekable,
            .OVERFLOW => return error.Unseekable,
            .SPIPE => return error.Unseekable,
            .NXIO => return error.Unseekable,
            .NOTCAPABLE => return error.AccessDenied,
            else => |err| return unexpectedErrno(err),
        }
    }
    const lseek_sym = if (lfs64_abi) system.lseek64 else system.lseek;

    const rc = lseek_sym(fd, 0, SEEK.CUR);
    switch (errno(rc)) {
        .SUCCESS => return @as(u64, @bitCast(rc)),
        .BADF => unreachable, // always a race condition
        .INVAL => return error.Unseekable,
        .OVERFLOW => return error.Unseekable,
        .SPIPE => return error.Unseekable,
        .NXIO => return error.Unseekable,
        else => |err| return unexpectedErrno(err),
    }
}

FcntlError

pub const FcntlError = error{
    PermissionDenied,
    FileBusy,
    ProcessFdQuotaExceeded,
    Locked,
    DeadLock,
    LockedRegionLimitExceeded,
} || UnexpectedError;

fcntl()

pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
    while (true) {
        const rc = system.fcntl(fd, cmd, arg);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),
            .INTR => continue,
            .AGAIN, .ACCES => return error.Locked,
            .BADF => unreachable,
            .BUSY => return error.FileBusy,
            .INVAL => unreachable, // invalid parameters
            .PERM => return error.PermissionDenied,
            .MFILE => return error.ProcessFdQuotaExceeded,
            .NOTDIR => unreachable, // invalid parameter
            .DEADLK => return error.DeadLock,
            .NOLCK => return error.LockedRegionLimitExceeded,
            else => |err| return unexpectedErrno(err),
        }
    }
}

fn setSockFlags(sock: socket_t, flags: u32) !void {
    if ((flags & SOCK.CLOEXEC) != 0) {
        if (builtin.os.tag == .windows) {
            // TODO: Find out if this is supported for sockets
        } else {
            var fd_flags = fcntl(sock, F.GETFD, 0) catch |err| switch (err) {
                error.FileBusy => unreachable,
                error.Locked => unreachable,
                error.PermissionDenied => unreachable,
                error.DeadLock => unreachable,
                error.LockedRegionLimitExceeded => unreachable,
                else => |e| return e,
            };
            fd_flags |= FD_CLOEXEC;
            _ = fcntl(sock, F.SETFD, fd_flags) catch |err| switch (err) {
                error.FileBusy => unreachable,
                error.Locked => unreachable,
                error.PermissionDenied => unreachable,
                error.DeadLock => unreachable,
                error.LockedRegionLimitExceeded => unreachable,
                else => |e| return e,
            };
        }
    }
    if ((flags & SOCK.NONBLOCK) != 0) {
        if (builtin.os.tag == .windows) {
            var mode: c_ulong = 1;
            if (windows.ws2_32.ioctlsocket(sock, windows.ws2_32.FIONBIO, &mode) == windows.ws2_32.SOCKET_ERROR) {
                switch (windows.ws2_32.WSAGetLastError()) {
                    .WSANOTINITIALISED => unreachable,
                    .WSAENETDOWN => return error.NetworkSubsystemFailed,
                    .WSAENOTSOCK => return error.FileDescriptorNotASocket,
                    // TODO: handle more errors
                    else => |err| return windows.unexpectedWSAError(err),
                }
            }
        } else {
            var fl_flags = fcntl(sock, F.GETFL, 0) catch |err| switch (err) {
                error.FileBusy => unreachable,
                error.Locked => unreachable,
                error.PermissionDenied => unreachable,
                error.DeadLock => unreachable,
                error.LockedRegionLimitExceeded => unreachable,
                else => |e| return e,
            };
            fl_flags |= O.NONBLOCK;
            _ = fcntl(sock, F.SETFL, fl_flags) catch |err| switch (err) {
                error.FileBusy => unreachable,
                error.Locked => unreachable,
                error.PermissionDenied => unreachable,
                error.DeadLock => unreachable,
                error.LockedRegionLimitExceeded => unreachable,
                else => |e| return e,
            };
        }
    }
}

FlockError

pub const FlockError = error{
    WouldBlock,

    SystemResources,

    FileLocksNotSupported,
} || UnexpectedError;

flock()

The kernel ran out of memory for allocating file locks The underlying filesystem does not support file locks Depending on the operating system flock may or may not interact with fcntl locks made by other processes.

pub fn flock(fd: fd_t, operation: i32) FlockError!void {
    while (true) {
        const rc = system.flock(fd, operation);
        switch (errno(rc)) {
            .SUCCESS => return,
            .BADF => unreachable,
            .INTR => continue,
            .INVAL => unreachable, // invalid parameters
            .NOLCK => return error.SystemResources,
            .AGAIN => return error.WouldBlock, // TODO: integrate with async instead of just returning an error
            .OPNOTSUPP => return error.FileLocksNotSupported,
            else => |err| return unexpectedErrno(err),
        }
    }
}

RealPathError

pub const RealPathError = error{
    FileNotFound,
    AccessDenied,
    NameTooLong,
    NotSupported,
    NotDir,
    SymLinkLoop,
    InputOutput,
    FileTooBig,
    IsDir,
    ProcessFdQuotaExceeded,
    SystemFdQuotaExceeded,
    NoDevice,
    SystemResources,
    NoSpaceLeft,
    FileSystem,
    BadPathName,
    DeviceBusy,

    SharingViolation,
    PipeBusy,

    InvalidHandle,

    InvalidUtf8,

    NetworkNotFound,

    PathAlreadyExists,
} || UnexpectedError;

realpath()

On WASI, the current CWD may not be associated with an absolute path. On Windows, file paths must be valid Unicode. On Windows, \\server or \\server\share was not found. Return the canonicalized absolute pathname. Expands all symbolic links and resolves references to ., .., and extra / characters in pathname. The return value is a slice of out_buffer, but not necessarily from the beginning. See also realpathZ and realpathW.

pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
    if (builtin.os.tag == .windows) {
        const pathname_w = try windows.sliceToPrefixedFileW(null, pathname);
        return realpathW(pathname_w.span(), out_buffer);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        @compileError("WASI does not support os.realpath");
    }
    const pathname_c = try toPosixPath(pathname);
    return realpathZ(&pathname_c, out_buffer);
}

realpathZ()

Same as realpath except pathname is null-terminated.

pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
    if (builtin.os.tag == .windows) {
        const pathname_w = try windows.cStrToPrefixedFileW(null, pathname);
        return realpathW(pathname_w.span(), out_buffer);
    } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
        return realpath(mem.sliceTo(pathname, 0), out_buffer);
    }
    if (!builtin.link_libc) {
        const flags = if (builtin.os.tag == .linux) O.PATH | O.NONBLOCK | O.CLOEXEC else O.NONBLOCK | O.CLOEXEC;
        const fd = openZ(pathname, flags, 0) catch |err| switch (err) {
            error.FileLocksNotSupported => unreachable,
            error.WouldBlock => unreachable,
            error.FileBusy => unreachable, // not asking for write permissions
            error.InvalidHandle => unreachable, // WASI-only
            else => |e| return e,
        };
        defer close(fd);

        return getFdPath(fd, out_buffer);
    }
    const result_path = std.c.realpath(pathname, out_buffer) orelse switch (@as(E, @enumFromInt(std.c._errno().*))) {
        .SUCCESS => unreachable,
        .INVAL => unreachable,
        .BADF => unreachable,
        .FAULT => unreachable,
        .ACCES => return error.AccessDenied,
        .NOENT => return error.FileNotFound,
        .OPNOTSUPP => return error.NotSupported,
        .NOTDIR => return error.NotDir,
        .NAMETOOLONG => return error.NameTooLong,
        .LOOP => return error.SymLinkLoop,
        .IO => return error.InputOutput,
        else => |err| return unexpectedErrno(err),
    };
    return mem.sliceTo(result_path, 0);
}

realpathW()

Same as realpath except pathname is UTF16LE-encoded.

pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
    const w = windows;

    const dir = std.fs.cwd().fd;
    const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
    const share_access = w.FILE_SHARE_READ;
    const creation = w.FILE_OPEN;
    const h_file = blk: {
        const res = w.OpenFile(pathname, .{
            .dir = dir,
            .access_mask = access_mask,
            .share_access = share_access,
            .creation = creation,
            .io_mode = .blocking,
            .filter = .any,
        }) catch |err| switch (err) {
            error.WouldBlock => unreachable,
            else => |e| return e,
        };
        break :blk res;
    };
    defer w.CloseHandle(h_file);

    return getFdPath(h_file, out_buffer);
}

isGetFdPathSupportedOnTarget()

pub fn isGetFdPathSupportedOnTarget(os: std.Target.Os) bool {
    return switch (os.tag) {
        // zig fmt: off
        .windows,
        .macos, .ios, .watchos, .tvos,
        .linux,
        .solaris,
        .illumos,
        .freebsd,
        => true,
        // zig fmt: on
        .dragonfly => os.version_range.semver.max.order(.{ .major = 6, .minor = 0, .patch = 0 }) != .lt,
        .netbsd => os.version_range.semver.max.order(.{ .major = 10, .minor = 0, .patch = 0 }) != .lt,
        else => false,
    };
}

getFdPath()

Return canonical path of handle fd. This function is very host-specific and is not universally supported by all hosts. For example, while it generally works on Linux, macOS, FreeBSD or Windows, it is unsupported on WASI.

pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
    if (!comptime isGetFdPathSupportedOnTarget(builtin.os)) {
        @compileError("querying for canonical path of a handle is unsupported on this host");
    }
    switch (builtin.os.tag) {
        .windows => {
            var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
            const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]);

            // Trust that Windows gives us valid UTF-16LE.
            const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable;
            return out_buffer[0..end_index];
        },
        .macos, .ios, .watchos, .tvos => {
            // On macOS, we can use F.GETPATH fcntl command to query the OS for
            // the path to the file descriptor.
            @memset(out_buffer[0..MAX_PATH_BYTES], 0);
            switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
                .SUCCESS => {},
                .BADF => return error.FileNotFound,
                .NOSPC => return error.NameTooLong,
                // TODO man pages for fcntl on macOS don't really tell you what
                // errno values to expect when command is F.GETPATH...
                else => |err| return unexpectedErrno(err),
            }
            const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES;
            return out_buffer[0..len];
        },
        .linux => {
            var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined;
            const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/fd/{d}", .{fd}) catch unreachable;

            const target = readlinkZ(proc_path, out_buffer) catch |err| {
                switch (err) {
                    error.UnsupportedReparsePointType => unreachable, // Windows only,
                    error.NotLink => unreachable,
                    else => |e| return e,
                }
            };
            return target;
        },
        .solaris, .illumos => {
            var procfs_buf: ["/proc/self/path/-2147483648\x00".len]u8 = undefined;
            const proc_path = std.fmt.bufPrintZ(procfs_buf[0..], "/proc/self/path/{d}", .{fd}) catch unreachable;

            const target = readlinkZ(proc_path, out_buffer) catch |err| switch (err) {
                error.UnsupportedReparsePointType => unreachable,
                error.NotLink => unreachable,
                else => |e| return e,
            };
            return target;
        },
        .freebsd => {
            if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .gt) {
                var kfile: system.kinfo_file = undefined;
                kfile.structsize = system.KINFO_FILE_SIZE;
                switch (errno(system.fcntl(fd, system.F.KINFO, @intFromPtr(&kfile)))) {
                    .SUCCESS => {},
                    .BADF => return error.FileNotFound,
                    else => |err| return unexpectedErrno(err),
                }
                const len = mem.indexOfScalar(u8, &kfile.path, 0) orelse MAX_PATH_BYTES;
                if (len == 0) return error.NameTooLong;
                const result = out_buffer[0..len];
                @memcpy(result, kfile.path[0..len]);
                return result;
            } else {
                // This fallback implementation reimplements libutil's `kinfo_getfile()`.
                // The motivation is to avoid linking -lutil when building zig or general
                // user executables.
                var mib = [4]c_int{ CTL.KERN, KERN.PROC, KERN.PROC_FILEDESC, system.getpid() };
                var len: usize = undefined;
                sysctl(&mib, null, &len, null, 0) catch |err| switch (err) {
                    error.PermissionDenied => unreachable,
                    error.SystemResources => return error.SystemResources,
                    error.NameTooLong => unreachable,
                    error.UnknownName => unreachable,
                    else => return error.Unexpected,
                };
                len = len * 4 / 3;
                const buf = std.heap.c_allocator.alloc(u8, len) catch return error.SystemResources;
                defer std.heap.c_allocator.free(buf);
                len = buf.len;
                sysctl(&mib, &buf[0], &len, null, 0) catch |err| switch (err) {
                    error.PermissionDenied => unreachable,
                    error.SystemResources => return error.SystemResources,
                    error.NameTooLong => unreachable,
                    error.UnknownName => unreachable,
                    else => return error.Unexpected,
                };
                var i: usize = 0;
                while (i < len) {
                    const kf: *align(1) system.kinfo_file = @as(*align(1) system.kinfo_file, @ptrCast(&buf[i]));
                    if (kf.fd == fd) {
                        len = mem.indexOfScalar(u8, &kf.path, 0) orelse MAX_PATH_BYTES;
                        if (len == 0) return error.NameTooLong;
                        const result = out_buffer[0..len];
                        @memcpy(result, kf.path[0..len]);
                        return result;
                    }
                    i += @as(usize, @intCast(kf.structsize));
                }
                return error.InvalidHandle;
            }
        },
        .dragonfly => {
            @memset(out_buffer[0..MAX_PATH_BYTES], 0);
            switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
                .SUCCESS => {},
                .BADF => return error.FileNotFound,
                .RANGE => return error.NameTooLong,
                else => |err| return unexpectedErrno(err),
            }
            const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES;
            return out_buffer[0..len];
        },
        .netbsd => {
            @memset(out_buffer[0..MAX_PATH_BYTES], 0);
            switch (errno(system.fcntl(fd, F.GETPATH, out_buffer))) {
                .SUCCESS => {},
                .ACCES => return error.AccessDenied,
                .BADF => return error.FileNotFound,
                .NOENT => return error.FileNotFound,
                .NOMEM => return error.SystemResources,
                .RANGE => return error.NameTooLong,
                else => |err| return unexpectedErrno(err),
            }
            const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES;
            return out_buffer[0..len];
        },
        else => unreachable, // made unreachable by isGetFdPathSupportedOnTarget above
    }
}

nanosleep()

Spurious wakeups are possible and no precision of timing is guaranteed.

pub fn nanosleep(seconds: u64, nanoseconds: u64) void {
    var req = timespec{
        .tv_sec = math.cast(isize, seconds) orelse math.maxInt(isize),
        .tv_nsec = math.cast(isize, nanoseconds) orelse math.maxInt(isize),
    };
    var rem: timespec = undefined;
    while (true) {
        switch (errno(system.nanosleep(&req, &rem))) {
            .FAULT => unreachable,
            .INVAL => {
                // Sometimes Darwin returns EINVAL for no reason.
                // We treat it as a spurious wakeup.
                return;
            },
            .INTR => {
                req = rem;
                continue;
            },
            // This prong handles success as well as unexpected errors.
            else => return,
        }
    }
}

dl_iterate_phdr()

pub fn dl_iterate_phdr(
    context: anytype,
    comptime Error: type,
    comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void,
) Error!void {
    const Context = @TypeOf(context);

    switch (builtin.object_format) {
        .elf, .c => {},
        else => @compileError("dl_iterate_phdr is not available for this target"),
    }

    if (builtin.link_libc) {
        switch (system.dl_iterate_phdr(struct {
            fn callbackC(info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.C) c_int {
                const context_ptr: *const Context = @ptrCast(@alignCast(data));
                callback(info, size, context_ptr.*) catch |err| return @intFromError(err);
                return 0;
            }
        }.callbackC, @as(?*anyopaque, @ptrFromInt(@intFromPtr(&context))))) {
            0 => return,
            else => |err| return @as(Error, @errorCast(@errorFromInt(@as(u16, @intCast(err))))), // TODO don't hardcode u16
        }
    }

    const elf_base = std.process.getBaseAddress();
    const ehdr = @as(*elf.Ehdr, @ptrFromInt(elf_base));
    // Make sure the base address points to an ELF image.
    assert(mem.eql(u8, ehdr.e_ident[0..4], elf.MAGIC));
    const n_phdr = ehdr.e_phnum;
    const phdrs = (@as([*]elf.Phdr, @ptrFromInt(elf_base + ehdr.e_phoff)))[0..n_phdr];

    var it = dl.linkmap_iterator(phdrs) catch unreachable;

    // The executable has no dynamic link segment, create a single entry for
    // the whole ELF image.
    if (it.end()) {
        // Find the base address for the ELF image, if this is a PIE the value
        // is non-zero.
        const base_address = for (phdrs) |*phdr| {
            if (phdr.p_type == elf.PT_PHDR) {
                break @intFromPtr(phdrs.ptr) - phdr.p_vaddr;
                // We could try computing the difference between _DYNAMIC and
                // the p_vaddr of the PT_DYNAMIC section, but using the phdr is
                // good enough (Is it?).
            }
        } else unreachable;

        var info = dl_phdr_info{
            .dlpi_addr = base_address,
            .dlpi_name = "/proc/self/exe",
            .dlpi_phdr = phdrs.ptr,
            .dlpi_phnum = ehdr.e_phnum,
        };

        return callback(&info, @sizeOf(dl_phdr_info), context);
    }

    // Last return value from the callback function.
    while (it.next()) |entry| {
        var dlpi_phdr: [*]elf.Phdr = undefined;
        var dlpi_phnum: u16 = undefined;

        if (entry.l_addr != 0) {
            const elf_header = @as(*elf.Ehdr, @ptrFromInt(entry.l_addr));
            dlpi_phdr = @as([*]elf.Phdr, @ptrFromInt(entry.l_addr + elf_header.e_phoff));
            dlpi_phnum = elf_header.e_phnum;
        } else {
            // This is the running ELF image
            dlpi_phdr = @as([*]elf.Phdr, @ptrFromInt(elf_base + ehdr.e_phoff));
            dlpi_phnum = ehdr.e_phnum;
        }

        var info = dl_phdr_info{
            .dlpi_addr = entry.l_addr,
            .dlpi_name = entry.l_name,
            .dlpi_phdr = dlpi_phdr,
            .dlpi_phnum = dlpi_phnum,
        };

        try callback(&info, @sizeOf(dl_phdr_info), context);
    }
}

ClockGetTimeError

pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError;

clock_gettime()

TODO: change this to return the timespec as a return value TODO: look into making clk_id an enum

pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var ts: timestamp_t = undefined;
        switch (system.clock_time_get(@as(u32, @bitCast(clk_id)), 1, &ts)) {
            .SUCCESS => {
                tp.* = .{
                    .tv_sec = @as(i64, @intCast(ts / std.time.ns_per_s)),
                    .tv_nsec = @as(isize, @intCast(ts % std.time.ns_per_s)),
                };
            },
            .INVAL => return error.UnsupportedClock,
            else => |err| return unexpectedErrno(err),
        }
        return;
    }
    if (builtin.os.tag == .windows) {
        if (clk_id == CLOCK.REALTIME) {
            var ft: windows.FILETIME = undefined;
            windows.kernel32.GetSystemTimeAsFileTime(&ft);
            // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch.
            const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
            const ft_per_s = std.time.ns_per_s / 100;
            tp.* = .{
                .tv_sec = @as(i64, @intCast(ft64 / ft_per_s)) + std.time.epoch.windows,
                .tv_nsec = @as(c_long, @intCast(ft64 % ft_per_s)) * 100,
            };
            return;
        } else {
            // TODO POSIX implementation of CLOCK.MONOTONIC on Windows.
            return error.UnsupportedClock;
        }
    }

    switch (errno(system.clock_gettime(clk_id, tp))) {
        .SUCCESS => return,
        .FAULT => unreachable,
        .INVAL => return error.UnsupportedClock,
        else => |err| return unexpectedErrno(err),
    }
}

clock_getres()

pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        var ts: timestamp_t = undefined;
        switch (system.clock_res_get(@as(u32, @bitCast(clk_id)), &ts)) {
            .SUCCESS => res.* = .{
                .tv_sec = @as(i64, @intCast(ts / std.time.ns_per_s)),
                .tv_nsec = @as(isize, @intCast(ts % std.time.ns_per_s)),
            },
            .INVAL => return error.UnsupportedClock,
            else => |err| return unexpectedErrno(err),
        }
        return;
    }

    switch (errno(system.clock_getres(clk_id, res))) {
        .SUCCESS => return,
        .FAULT => unreachable,
        .INVAL => return error.UnsupportedClock,
        else => |err| return unexpectedErrno(err),
    }
}

SchedGetAffinityError

pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError;

sched_getaffinity()

pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t {
    var set: cpu_set_t = undefined;
    switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) {
        .SUCCESS => return set,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .SRCH => unreachable,
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

toPosixPath()

Used to convert a slice to a null terminated slice on the stack. TODO https://github.com/ziglang/zig/issues/287

pub fn toPosixPath(file_path: []const u8) ![MAX_PATH_BYTES - 1:0]u8 {
    if (std.debug.runtime_safety) assert(std.mem.indexOfScalar(u8, file_path, 0) == null);
    var path_with_null: [MAX_PATH_BYTES - 1:0]u8 = undefined;
    // >= rather than > to make room for the null byte
    if (file_path.len >= MAX_PATH_BYTES) return error.NameTooLong;
    @memcpy(path_with_null[0..file_path.len], file_path);
    path_with_null[file_path.len] = 0;
    return path_with_null;
}

unexpected_error_tracing

Whether or not error.Unexpected will print its value and a stack trace. if this happens the fix is to add the error code to the corresponding switch expression, possibly introduce a new error in the error set, and send a patch to Zig.

pub const unexpected_error_tracing = builtin.zig_backend == .stage2_llvm and builtin.mode == .Debug;

UnexpectedError

pub const UnexpectedError = error{
    Unexpected,
};

unexpectedErrno()

The Operating System returned an undocumented error code. This error is in theory not possible, but it would be better to handle this error than to invoke undefined behavior. Call this when you made a syscall or something that sets errno and you get an unexpected error.

pub fn unexpectedErrno(err: E) UnexpectedError {
    if (unexpected_error_tracing) {
        std.debug.print("unexpected errno: {d}\n", .{@intFromEnum(err)});
        std.debug.dumpCurrentStackTrace(null);
    }
    return error.Unexpected;
}

SigaltstackError

pub const SigaltstackError = error{
    SizeTooSmall,

    PermissionDenied,
} || UnexpectedError;

sigaltstack()

The supplied stack size was less than MINSIGSTKSZ. Attempted to change the signal stack while it was active.

pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
    switch (errno(system.sigaltstack(ss, old_ss))) {
        .SUCCESS => return,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .NOMEM => return error.SizeTooSmall,
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

sigaction()

Examine and change a signal action.

pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) error{OperationNotSupported}!void {
    switch (errno(system.sigaction(sig, act, oact))) {
        .SUCCESS => return,
        .INVAL, .NOSYS => return error.OperationNotSupported,
        else => unreachable,
    }
}

sigprocmask()

Sets the thread signal mask.

pub fn sigprocmask(flags: u32, noalias set: ?*const sigset_t, noalias oldset: ?*sigset_t) void {
    switch (errno(system.sigprocmask(@bitCast(flags), set, oldset))) {
        .SUCCESS => return,
        .FAULT => unreachable,
        .INVAL => unreachable,
        else => unreachable,
    }
}

FutimensError

pub const FutimensError = error{
    AccessDenied,

    PermissionDenied,

    ReadOnlyFileSystem,
} || UnexpectedError;

futimens()

times is NULL, or both tv_nsec values are UTIME_NOW, and either: * the effective user ID of the caller does not match the owner of the file, the caller does not have write access to the file, and the caller is not privileged (Linux: does not have either the CAP_FOWNER or the CAP_DAC_OVERRIDE capability); or, * the file is marked immutable (see chattr(1)). The caller attempted to change one or both timestamps to a value other than the current time, or to change one of the timestamps to the current time while leaving the other timestamp unchanged, (i.e., times is not NULL, neither tv_nsec field is UTIME_NOW, and neither tv_nsec field is UTIME_OMIT) and either: * the caller's effective user ID does not match the owner of file, and the caller is not privileged (Linux: does not have the CAP_FOWNER capability); or, * the file is marked append-only or immutable (see chattr(1)).

pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void {
    if (builtin.os.tag == .wasi and !builtin.link_libc) {
        // TODO WASI encodes `wasi.fstflags` to signify magic values
        // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore
        // this here, but we should really handle it somehow.
        const atim = times[0].toTimestamp();
        const mtim = times[1].toTimestamp();
        switch (wasi.fd_filestat_set_times(fd, atim, mtim, wasi.FILESTAT_SET_ATIM | wasi.FILESTAT_SET_MTIM)) {
            .SUCCESS => return,
            .ACCES => return error.AccessDenied,
            .PERM => return error.PermissionDenied,
            .BADF => unreachable, // always a race condition
            .FAULT => unreachable,
            .INVAL => unreachable,
            .ROFS => return error.ReadOnlyFileSystem,
            else => |err| return unexpectedErrno(err),
        }
    }

    switch (errno(system.futimens(fd, times))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .PERM => return error.PermissionDenied,
        .BADF => unreachable, // always a race condition
        .FAULT => unreachable,
        .INVAL => unreachable,
        .ROFS => return error.ReadOnlyFileSystem,
        else => |err| return unexpectedErrno(err),
    }
}

GetHostNameError

pub const GetHostNameError = error{PermissionDenied} || UnexpectedError;

gethostname()

pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 {
    if (builtin.link_libc) {
        switch (errno(system.gethostname(name_buffer, name_buffer.len))) {
            .SUCCESS => return mem.sliceTo(name_buffer, 0),
            .FAULT => unreachable,
            .NAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this
            .PERM => return error.PermissionDenied,
            else => |err| return unexpectedErrno(err),
        }
    }
    if (builtin.os.tag == .linux) {
        const uts = uname();
        const hostname = mem.sliceTo(&uts.nodename, 0);
        const result = name_buffer[0..hostname.len];
        @memcpy(result, hostname);
        return result;
    }

    @compileError("TODO implement gethostname for this OS");
}

uname()

pub fn uname() utsname {
    var uts: utsname = undefined;
    switch (errno(system.uname(&uts))) {
        .SUCCESS => return uts,
        .FAULT => unreachable,
        else => unreachable,
    }
}

res_mkquery()

pub fn res_mkquery(
    op: u4,
    dname: []const u8,
    class: u8,
    ty: u8,
    data: []const u8,
    newrr: ?[*]const u8,
    buf: []u8,
) usize {
    _ = data;
    _ = newrr;
    // This implementation is ported from musl libc.
    // A more idiomatic "ziggy" implementation would be welcome.
    var name = dname;
    if (mem.endsWith(u8, name, ".")) name.len -= 1;
    assert(name.len <= 253);
    const n = 17 + name.len + @intFromBool(name.len != 0);

    // Construct query template - ID will be filled later
    var q: [280]u8 = undefined;
    @memset(q[0..n], 0);
    q[2] = @as(u8, op) * 8 + 1;
    q[5] = 1;
    @memcpy(q[13..][0..name.len], name);
    var i: usize = 13;
    var j: usize = undefined;
    while (q[i] != 0) : (i = j + 1) {
        j = i;
        while (q[j] != 0 and q[j] != '.') : (j += 1) {}
        // TODO determine the circumstances for this and whether or
        // not this should be an error.
        if (j - i - 1 > 62) unreachable;
        q[i - 1] = @as(u8, @intCast(j - i));
    }
    q[i + 1] = ty;
    q[i + 3] = class;

    // Make a reasonably unpredictable id
    var ts: timespec = undefined;
    clock_gettime(CLOCK.REALTIME, &ts) catch {};
    const UInt = std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(ts.tv_nsec)));
    const unsec = @as(UInt, @bitCast(ts.tv_nsec));
    const id = @as(u32, @truncate(unsec + unsec / 65536));
    q[0] = @as(u8, @truncate(id / 256));
    q[1] = @as(u8, @truncate(id));

    @memcpy(buf[0..n], q[0..n]);
    return n;
}

SendError

pub const SendError = error{
    AccessDenied,

    WouldBlock,

    FastOpenAlreadyInProgress,

    ConnectionResetByPeer,

    MessageTooBig,

    SystemResources,

    BrokenPipe,

    FileDescriptorNotASocket,

    NetworkUnreachable,

    NetworkSubsystemFailed,
} || UnexpectedError;

SendMsgError

(For UNIX domain sockets, which are identified by pathname) Write permission is denied on the destination socket file, or search permission is denied for one of the directories the path prefix. (See path_resolution(7).) (For UDP sockets) An attempt was made to send to a network/broadcast address as though it was a unicast address. The socket is marked nonblocking and the requested operation would block, and there is no global event loop configured. It's also possible to get this error under the following condition: (Internet domain datagram sockets) The socket referred to by sockfd had not previously been bound to an address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers in the ephemeral port range are currently in use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). Another Fast Open is already in progress. Connection reset by peer. The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible. The message is not transmitted. The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.) This is also caused when there is not enough kernel memory available. The local end has been shut down on a connection oriented socket. In this case, the process will also receive a SIGPIPE unless MSG.NOSIGNAL is set. Network is unreachable. The local network interface used to reach the destination is down.

pub const SendMsgError = SendError || error{
    AddressFamilyNotSupported,

    SymLinkLoop,

    NameTooLong,

    FileNotFound,
    NotDir,

    SocketNotConnected,
    AddressNotAvailable,
};

sendmsg()

The passed address didn't have the correct address family in its sa_family field. Returned when socket is AF.UNIX and the given path has a symlink loop. Returned when socket is AF.UNIX and the given path length exceeds MAX_PATH_BYTES bytes. Returned when socket is AF.UNIX and the given path does not point to an existing file. The socket is not connected (connection-oriented sockets only).

pub fn sendmsg(
    sockfd: socket_t,
    msg: *const msghdr_const,
    flags: u32,
) SendMsgError!usize {
    while (true) {
        const rc = system.sendmsg(sockfd, msg, flags);
        if (builtin.os.tag == .windows) {
            if (rc == windows.ws2_32.SOCKET_ERROR) {
                switch (windows.ws2_32.WSAGetLastError()) {
                    .WSAEACCES => return error.AccessDenied,
                    .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
                    .WSAECONNRESET => return error.ConnectionResetByPeer,
                    .WSAEMSGSIZE => return error.MessageTooBig,
                    .WSAENOBUFS => return error.SystemResources,
                    .WSAENOTSOCK => return error.FileDescriptorNotASocket,
                    .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
                    .WSAEDESTADDRREQ => unreachable, // A destination address is required.
                    .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
                    .WSAEHOSTUNREACH => return error.NetworkUnreachable,
                    // TODO: WSAEINPROGRESS, WSAEINTR
                    .WSAEINVAL => unreachable,
                    .WSAENETDOWN => return error.NetworkSubsystemFailed,
                    .WSAENETRESET => return error.ConnectionResetByPeer,
                    .WSAENETUNREACH => return error.NetworkUnreachable,
                    .WSAENOTCONN => return error.SocketNotConnected,
                    .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
                    .WSAEWOULDBLOCK => return error.WouldBlock,
                    .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
                    else => |err| return windows.unexpectedWSAError(err),
                }
            } else {
                return @as(usize, @intCast(rc));
            }
        } else {
            switch (errno(rc)) {
                .SUCCESS => return @as(usize, @intCast(rc)),

                .ACCES => return error.AccessDenied,
                .AGAIN => return error.WouldBlock,
                .ALREADY => return error.FastOpenAlreadyInProgress,
                .BADF => unreachable, // always a race condition
                .CONNRESET => return error.ConnectionResetByPeer,
                .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
                .FAULT => unreachable, // An invalid user space address was specified for an argument.
                .INTR => continue,
                .INVAL => unreachable, // Invalid argument passed.
                .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
                .MSGSIZE => return error.MessageTooBig,
                .NOBUFS => return error.SystemResources,
                .NOMEM => return error.SystemResources,
                .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
                .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
                .PIPE => return error.BrokenPipe,
                .AFNOSUPPORT => return error.AddressFamilyNotSupported,
                .LOOP => return error.SymLinkLoop,
                .NAMETOOLONG => return error.NameTooLong,
                .NOENT => return error.FileNotFound,
                .NOTDIR => return error.NotDir,
                .HOSTUNREACH => return error.NetworkUnreachable,
                .NETUNREACH => return error.NetworkUnreachable,
                .NOTCONN => return error.SocketNotConnected,
                .NETDOWN => return error.NetworkSubsystemFailed,
                else => |err| return unexpectedErrno(err),
            }
        }
    }
}

SendToError

The file descriptor of the sending socket. Message header and iovecs

pub const SendToError = SendMsgError || error{
    UnreachableAddress,
};

sendto()

The destination address is not reachable by the bound address. Transmit a message to another socket.

The sendto call may be used only when the socket is in a connected state (so that the intended recipient is known). The following call

send(sockfd, buf, len, flags);

is equivalent to

sendto(sockfd, buf, len, flags, NULL, 0);

If sendto() is used on a connection-mode (SOCK.STREAM, SOCK.SEQPACKET) socket, the arguments dest_addr and addrlen are asserted to be null and 0 respectively, and asserted that the socket was actually connected. Otherwise, the address of the target is given by dest_addr with addrlen specifying its size.

If the message is too long to pass atomically through the underlying protocol, SendError.MessageTooBig is returned, and the message is not transmitted.

There is no indication of failure to deliver.

When the message does not fit into the send buffer of the socket, sendto normally blocks, unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail with SendError.WouldBlock. The select call may be used to determine when it is possible to send more data.

pub fn sendto(
    sockfd: socket_t,
    buf: []const u8,
    flags: u32,
    dest_addr: ?*const sockaddr,
    addrlen: socklen_t,
) SendToError!usize {
    if (builtin.os.tag == .windows) {
        switch (windows.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen)) {
            windows.ws2_32.SOCKET_ERROR => switch (windows.ws2_32.WSAGetLastError()) {
                .WSAEACCES => return error.AccessDenied,
                .WSAEADDRNOTAVAIL => return error.AddressNotAvailable,
                .WSAECONNRESET => return error.ConnectionResetByPeer,
                .WSAEMSGSIZE => return error.MessageTooBig,
                .WSAENOBUFS => return error.SystemResources,
                .WSAENOTSOCK => return error.FileDescriptorNotASocket,
                .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported,
                .WSAEDESTADDRREQ => unreachable, // A destination address is required.
                .WSAEFAULT => unreachable, // The lpBuffers, lpTo, lpOverlapped, lpNumberOfBytesSent, or lpCompletionRoutine parameters are not part of the user address space, or the lpTo parameter is too small.
                .WSAEHOSTUNREACH => return error.NetworkUnreachable,
                // TODO: WSAEINPROGRESS, WSAEINTR
                .WSAEINVAL => unreachable,
                .WSAENETDOWN => return error.NetworkSubsystemFailed,
                .WSAENETRESET => return error.ConnectionResetByPeer,
                .WSAENETUNREACH => return error.NetworkUnreachable,
                .WSAENOTCONN => return error.SocketNotConnected,
                .WSAESHUTDOWN => unreachable, // The socket has been shut down; it is not possible to WSASendTo on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
                .WSAEWOULDBLOCK => return error.WouldBlock,
                .WSANOTINITIALISED => unreachable, // A successful WSAStartup call must occur before using this function.
                else => |err| return windows.unexpectedWSAError(err),
            },
            else => |rc| return @as(usize, @intCast(rc)),
        }
    }
    while (true) {
        const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen);
        switch (errno(rc)) {
            .SUCCESS => return @as(usize, @intCast(rc)),

            .ACCES => return error.AccessDenied,
            .AGAIN => return error.WouldBlock,
            .ALREADY => return error.FastOpenAlreadyInProgress,
            .BADF => unreachable, // always a race condition
            .CONNRESET => return error.ConnectionResetByPeer,
            .DESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set.
            .FAULT => unreachable, // An invalid user space address was specified for an argument.
            .INTR => continue,
            .INVAL => return error.UnreachableAddress,
            .ISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified
            .MSGSIZE => return error.MessageTooBig,
            .NOBUFS => return error.SystemResources,
            .NOMEM => return error.SystemResources,
            .NOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket.
            .OPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type.
            .PIPE => return error.BrokenPipe,
            .AFNOSUPPORT => return error.AddressFamilyNotSupported,
            .LOOP => return error.SymLinkLoop,
            .NAMETOOLONG => return error.NameTooLong,
            .NOENT => return error.FileNotFound,
            .NOTDIR => return error.NotDir,
            .HOSTUNREACH => return error.NetworkUnreachable,
            .NETUNREACH => return error.NetworkUnreachable,
            .NOTCONN => return error.SocketNotConnected,
            .NETDOWN => return error.NetworkSubsystemFailed,
            else => |err| return unexpectedErrno(err),
        }
    }
}

send()

The file descriptor of the sending socket. Message to send. Transmit a message to another socket.

The send call may be used only when the socket is in a connected state (so that the intended recipient is known). The only difference between send and write is the presence of flags. With a zero flags argument, send is equivalent to write. Also, the following call

send(sockfd, buf, len, flags);

is equivalent to

sendto(sockfd, buf, len, flags, NULL, 0);

There is no indication of failure to deliver.

When the message does not fit into the send buffer of the socket, send normally blocks, unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail with SendError.WouldBlock. The select call may be used to determine when it is possible to send more data.

pub fn send(
    sockfd: socket_t,
    buf: []const u8,
    flags: u32,
) SendError!usize {
    return sendto(sockfd, buf, flags, null, 0) catch |err| switch (err) {
        error.AddressFamilyNotSupported => unreachable,
        error.SymLinkLoop => unreachable,
        error.NameTooLong => unreachable,
        error.FileNotFound => unreachable,
        error.NotDir => unreachable,
        error.NetworkUnreachable => unreachable,
        error.AddressNotAvailable => unreachable,
        error.SocketNotConnected => unreachable,
        error.UnreachableAddress => unreachable,
        else => |e| return e,
    };
}

SendFileError

The file descriptor of the sending socket.

pub const SendFileError = PReadError || WriteError || SendError;

fn count_iovec_bytes(iovs: []const iovec_const) usize {
    var count: usize = 0;
    for (iovs) |iov| {
        count += iov.iov_len;
    }
    return count;
}

sendfile()

Transfer data between file descriptors, with optional headers and trailers. Returns the number of bytes written, which can be zero.

The sendfile call copies in_len bytes from one file descriptor to another. When possible, this is done within the operating system kernel, which can provide better performance characteristics than transferring data from kernel to user space and back, such as with read and write calls. When in_len is 0, it means to copy until the end of the input file has been reached. Note, however, that partial writes are still possible in this case.

in_fd must be a file descriptor opened for reading, and out_fd must be a file descriptor opened for writing. They may be any kind of file descriptor; however, if in_fd is not a regular file system file, it may cause this function to fall back to calling read and write, in which case atomicity guarantees no longer apply.

Copying begins reading at in_offset. The input file descriptor seek position is ignored and not updated. If the output file descriptor has a seek position, it is updated as bytes are written. When in_offset is past the end of the input file, it successfully reads 0 bytes.

flags has different meanings per operating system; refer to the respective man pages.

These systems support atomically sending everything, including headers and trailers: * macOS * FreeBSD

These systems support in-kernel data copying, but headers and trailers are not sent atomically: * Linux

Other systems fall back to calling read / write.

Linux has a limit on how many bytes may be transferred in one sendfile call, which is 0x7ffff000 on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as well as stuffing the errno codes into the last 4096 values. This is noted on the sendfile man page. The limit on Darwin is 0x7fffffff, trying to write more than that returns EINVAL. The corresponding POSIX limit on this is math.maxInt(isize).

pub fn sendfile(
    out_fd: fd_t,
    in_fd: fd_t,
    in_offset: u64,
    in_len: u64,
    headers: []const iovec_const,
    trailers: []const iovec_const,
    flags: u32,
) SendFileError!usize {
    var header_done = false;
    var total_written: usize = 0;

    // Prevents EOVERFLOW.
    const size_t = std.meta.Int(.unsigned, @typeInfo(usize).Int.bits - 1);
    const max_count = switch (builtin.os.tag) {
        .linux => 0x7ffff000,
        .macos, .ios, .watchos, .tvos => math.maxInt(i32),
        else => math.maxInt(size_t),
    };

    switch (builtin.os.tag) {
        .linux => sf: {
            // sendfile() first appeared in Linux 2.2, glibc 2.1.
            const call_sf = comptime if (builtin.link_libc)
                std.c.versionCheck(.{ .major = 2, .minor = 1, .patch = 0 }).ok
            else
                builtin.os.version_range.linux.range.max.order(.{ .major = 2, .minor = 2, .patch = 0 }) != .lt;
            if (!call_sf) break :sf;

            if (headers.len != 0) {
                const amt = try writev(out_fd, headers);
                total_written += amt;
                if (amt < count_iovec_bytes(headers)) return total_written;
                header_done = true;
            }

            // Here we match BSD behavior, making a zero count value send as many bytes as possible.
            const adjusted_count_tmp = if (in_len == 0) max_count else @min(in_len, @as(size_t, max_count));
            // TODO we should not need this cast; improve return type of @min
            const adjusted_count = @as(usize, @intCast(adjusted_count_tmp));

            const sendfile_sym = if (lfs64_abi) system.sendfile64 else system.sendfile;

            while (true) {
                var offset: off_t = @as(off_t, @bitCast(in_offset));
                const rc = sendfile_sym(out_fd, in_fd, &offset, adjusted_count);
                switch (errno(rc)) {
                    .SUCCESS => {
                        const amt = @as(usize, @bitCast(rc));
                        total_written += amt;
                        if (in_len == 0 and amt == 0) {
                            // We have detected EOF from `in_fd`.
                            break;
                        } else if (amt < in_len) {
                            return total_written;
                        } else {
                            break;
                        }
                    },

                    .BADF => unreachable, // Always a race condition.
                    .FAULT => unreachable, // Segmentation fault.
                    .OVERFLOW => unreachable, // We avoid passing too large of a `count`.
                    .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket

                    .INVAL, .NOSYS => {
                        // EINVAL could be any of the following situations:
                        // * Descriptor is not valid or locked
                        // * an mmap(2)-like operation is  not  available  for in_fd
                        // * count is negative
                        // * out_fd has the O.APPEND flag set
                        // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write
                        // manually, the same as ENOSYS.
                        break :sf;
                    },
                    .AGAIN => if (std.event.Loop.instance) |loop| {
                        loop.waitUntilFdWritable(out_fd);
                        continue;
                    } else {
                        return error.WouldBlock;
                    },
                    .IO => return error.InputOutput,
                    .PIPE => return error.BrokenPipe,
                    .NOMEM => return error.SystemResources,
                    .NXIO => return error.Unseekable,
                    .SPIPE => return error.Unseekable,
                    else => |err| {
                        unexpectedErrno(err) catch {};
                        break :sf;
                    },
                }
            }

            if (trailers.len != 0) {
                total_written += try writev(out_fd, trailers);
            }

            return total_written;
        },
        .freebsd => sf: {
            var hdtr_data: std.c.sf_hdtr = undefined;
            var hdtr: ?*std.c.sf_hdtr = null;
            if (headers.len != 0 or trailers.len != 0) {
                // Here we carefully avoid `@intCast` by returning partial writes when
                // too many io vectors are provided.
                const hdr_cnt = math.cast(u31, headers.len) orelse math.maxInt(u31);
                if (headers.len > hdr_cnt) return writev(out_fd, headers);

                const trl_cnt = math.cast(u31, trailers.len) orelse math.maxInt(u31);

                hdtr_data = std.c.sf_hdtr{
                    .headers = headers.ptr,
                    .hdr_cnt = hdr_cnt,
                    .trailers = trailers.ptr,
                    .trl_cnt = trl_cnt,
                };
                hdtr = &hdtr_data;
            }

            const adjusted_count = @min(in_len, max_count);

            while (true) {
                var sbytes: off_t = undefined;
                const offset = @as(off_t, @bitCast(in_offset));
                const err = errno(system.sendfile(in_fd, out_fd, offset, adjusted_count, hdtr, &sbytes, flags));
                const amt = @as(usize, @bitCast(sbytes));
                switch (err) {
                    .SUCCESS => return amt,

                    .BADF => unreachable, // Always a race condition.
                    .FAULT => unreachable, // Segmentation fault.
                    .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket

                    .INVAL, .OPNOTSUPP, .NOTSOCK, .NOSYS => {
                        // EINVAL could be any of the following situations:
                        // * The fd argument is not a regular file.
                        // * The s argument is not a SOCK.STREAM type socket.
                        // * The offset argument is negative.
                        // Because of some of these possibilities, we fall back to doing read/write
                        // manually, the same as ENOSYS.
                        break :sf;
                    },

                    .INTR => if (amt != 0) return amt else continue,

                    .AGAIN => if (amt != 0) {
                        return amt;
                    } else if (std.event.Loop.instance) |loop| {
                        loop.waitUntilFdWritable(out_fd);
                        continue;
                    } else {
                        return error.WouldBlock;
                    },

                    .BUSY => if (amt != 0) {
                        return amt;
                    } else if (std.event.Loop.instance) |loop| {
                        loop.waitUntilFdReadable(in_fd);
                        continue;
                    } else {
                        return error.WouldBlock;
                    },

                    .IO => return error.InputOutput,
                    .NOBUFS => return error.SystemResources,
                    .PIPE => return error.BrokenPipe,

                    else => {
                        unexpectedErrno(err) catch {};
                        if (amt != 0) {
                            return amt;
                        } else {
                            break :sf;
                        }
                    },
                }
            }
        },
        .macos, .ios, .tvos, .watchos => sf: {
            var hdtr_data: std.c.sf_hdtr = undefined;
            var hdtr: ?*std.c.sf_hdtr = null;
            if (headers.len != 0 or trailers.len != 0) {
                // Here we carefully avoid `@intCast` by returning partial writes when
                // too many io vectors are provided.
                const hdr_cnt = math.cast(u31, headers.len) orelse math.maxInt(u31);
                if (headers.len > hdr_cnt) return writev(out_fd, headers);

                const trl_cnt = math.cast(u31, trailers.len) orelse math.maxInt(u31);

                hdtr_data = std.c.sf_hdtr{
                    .headers = headers.ptr,
                    .hdr_cnt = hdr_cnt,
                    .trailers = trailers.ptr,
                    .trl_cnt = trl_cnt,
                };
                hdtr = &hdtr_data;
            }

            const adjusted_count_temporary = @min(in_len, @as(u63, max_count));
            // TODO we should not need this int cast; improve the return type of `@min`
            const adjusted_count = @as(u63, @intCast(adjusted_count_temporary));

            while (true) {
                var sbytes: off_t = adjusted_count;
                const signed_offset = @as(i64, @bitCast(in_offset));
                const err = errno(system.sendfile(in_fd, out_fd, signed_offset, &sbytes, hdtr, flags));
                const amt = @as(usize, @bitCast(sbytes));
                switch (err) {
                    .SUCCESS => return amt,

                    .BADF => unreachable, // Always a race condition.
                    .FAULT => unreachable, // Segmentation fault.
                    .INVAL => unreachable,
                    .NOTCONN => return error.BrokenPipe, // `out_fd` is an unconnected socket

                    .OPNOTSUPP, .NOTSOCK, .NOSYS => break :sf,

                    .INTR => if (amt != 0) return amt else continue,

                    .AGAIN => if (amt != 0) {
                        return amt;
                    } else if (std.event.Loop.instance) |loop| {
                        loop.waitUntilFdWritable(out_fd);
                        continue;
                    } else {
                        return error.WouldBlock;
                    },

                    .IO => return error.InputOutput,
                    .PIPE => return error.BrokenPipe,

                    else => {
                        unexpectedErrno(err) catch {};
                        if (amt != 0) {
                            return amt;
                        } else {
                            break :sf;
                        }
                    },
                }
            }
        },
        else => {}, // fall back to read/write
    }

    if (headers.len != 0 and !header_done) {
        const amt = try writev(out_fd, headers);
        total_written += amt;
        if (amt < count_iovec_bytes(headers)) return total_written;
    }

    rw: {
        var buf: [8 * 4096]u8 = undefined;
        // Here we match BSD behavior, making a zero count value send as many bytes as possible.
        const adjusted_count_tmp = if (in_len == 0) buf.len else @min(buf.len, in_len);
        // TODO we should not need this cast; improve return type of @min
        const adjusted_count = @as(usize, @intCast(adjusted_count_tmp));
        const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset);
        if (amt_read == 0) {
            if (in_len == 0) {
                // We have detected EOF from `in_fd`.
                break :rw;
            } else {
                return total_written;
            }
        }
        const amt_written = try write(out_fd, buf[0..amt_read]);
        total_written += amt_written;
        if (amt_written < in_len or in_len == 0) return total_written;
    }

    if (trailers.len != 0) {
        total_written += try writev(out_fd, trailers);
    }

    return total_written;
}

CopyFileRangeError

pub const CopyFileRangeError = error{
    FileTooBig,
    InputOutput,
    FilesOpenedWithWrongFlags,
    IsDir,
    OutOfMemory,
    NoSpaceLeft,
    Unseekable,
    PermissionDenied,
    SwapFile,
    CorruptedData,
} || PReadError || PWriteError || UnexpectedError;

var has_copy_file_range_syscall = std.atomic.Atomic(bool).init(true);

copy_file_range()

fd_in is not open for reading; or fd_out is not open for writing; or the O.APPEND flag is set for fd_out. Transfer data between file descriptors at specified offsets. Returns the number of bytes written, which can less than requested.

The copy_file_range call copies len bytes from one file descriptor to another. When possible, this is done within the operating system kernel, which can provide better performance characteristics than transferring data from kernel to user space and back, such as with pread and pwrite calls.

fd_in must be a file descriptor opened for reading, and fd_out must be a file descriptor opened for writing. They may be any kind of file descriptor; however, if fd_in is not a regular file system file, it may cause this function to fall back to calling pread and pwrite, in which case atomicity guarantees no longer apply.

If fd_in and fd_out are the same, source and target ranges must not overlap. The file descriptor seek positions are ignored and not updated. When off_in is past the end of the input file, it successfully reads 0 bytes.

flags has different meanings per operating system; refer to the respective man pages.

These systems support in-kernel data copying: * Linux 4.5 (cross-filesystem 5.3) * FreeBSD 13.0

Other systems fall back to calling pread / pwrite.

Maximum offsets on Linux and FreeBSD are math.maxInt(i64).

pub fn copy_file_range(fd_in: fd_t, off_in: u64, fd_out: fd_t, off_out: u64, len: usize, flags: u32) CopyFileRangeError!usize {
    if ((comptime builtin.os.isAtLeast(.freebsd, .{ .major = 13, .minor = 0, .patch = 0 }) orelse false) or
        ((comptime builtin.os.isAtLeast(.linux, .{ .major = 4, .minor = 5, .patch = 0 }) orelse false and
        std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok) and
        has_copy_file_range_syscall.load(.Monotonic)))
    {
        var off_in_copy = @as(i64, @bitCast(off_in));
        var off_out_copy = @as(i64, @bitCast(off_out));

        while (true) {
            const rc = system.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags);
            if (builtin.os.tag == .freebsd) {
                switch (system.getErrno(rc)) {
                    .SUCCESS => return @as(usize, @intCast(rc)),
                    .BADF => return error.FilesOpenedWithWrongFlags,
                    .FBIG => return error.FileTooBig,
                    .IO => return error.InputOutput,
                    .ISDIR => return error.IsDir,
                    .NOSPC => return error.NoSpaceLeft,
                    .INVAL => break, // these may not be regular files, try fallback
                    .INTEGRITY => return error.CorruptedData,
                    .INTR => continue,
                    else => |err| return unexpectedErrno(err),
                }
            } else { // assume linux
                switch (system.getErrno(rc)) {
                    .SUCCESS => return @as(usize, @intCast(rc)),
                    .BADF => return error.FilesOpenedWithWrongFlags,
                    .FBIG => return error.FileTooBig,
                    .IO => return error.InputOutput,
                    .ISDIR => return error.IsDir,
                    .NOSPC => return error.NoSpaceLeft,
                    .INVAL => break, // these may not be regular files, try fallback
                    .NOMEM => return error.OutOfMemory,
                    .OVERFLOW => return error.Unseekable,
                    .PERM => return error.PermissionDenied,
                    .TXTBSY => return error.SwapFile,
                    .XDEV => break, // support for cross-filesystem copy added in Linux 5.3, use fallback
                    .NOSYS => { // syscall added in Linux 4.5, use fallback
                        has_copy_file_range_syscall.store(false, .Monotonic);
                        break;
                    },
                    else => |err| return unexpectedErrno(err),
                }
            }
        }
    }

    var buf: [8 * 4096]u8 = undefined;
    const adjusted_count = @min(buf.len, len);
    const amt_read = try pread(fd_in, buf[0..adjusted_count], off_in);
    // TODO without @as the line below fails to compile for wasm32-wasi:
    // error: integer value 0 cannot be coerced to type 'os.PWriteError!usize'
    if (amt_read == 0) return @as(usize, 0);
    return pwrite(fd_out, buf[0..amt_read], off_out);
}

PollError

pub const PollError = error{
    NetworkSubsystemFailed,

    SystemResources,
} || UnexpectedError;

poll()

The network subsystem has failed. The kernel had no space to allocate file descriptor tables.

pub fn poll(fds: []pollfd, timeout: i32) PollError!usize {
    while (true) {
        const fds_count = math.cast(nfds_t, fds.len) orelse return error.SystemResources;
        const rc = system.poll(fds.ptr, fds_count, timeout);
        if (builtin.os.tag == .windows) {
            if (rc == windows.ws2_32.SOCKET_ERROR) {
                switch (windows.ws2_32.WSAGetLastError()) {
                    .WSANOTINITIALISED => unreachable,
                    .WSAENETDOWN => return error.NetworkSubsystemFailed,
                    .WSAENOBUFS => return error.SystemResources,
                    // TODO: handle more errors
                    else => |err| return windows.unexpectedWSAError(err),
                }
            } else {
                return @as(usize, @intCast(rc));
            }
        } else {
            switch (errno(rc)) {
                .SUCCESS => return @as(usize, @intCast(rc)),
                .FAULT => unreachable,
                .INTR => continue,
                .INVAL => unreachable,
                .NOMEM => return error.SystemResources,
                else => |err| return unexpectedErrno(err),
            }
        }
        unreachable;
    }
}

PPollError

pub const PPollError = error{
    SignalInterrupt,

    SystemResources,
} || UnexpectedError;

ppoll()

The operation was interrupted by a delivery of a signal before it could complete. The kernel had no space to allocate file descriptor tables.

pub fn ppoll(fds: []pollfd, timeout: ?*const timespec, mask: ?*const sigset_t) PPollError!usize {
    var ts: timespec = undefined;
    var ts_ptr: ?*timespec = null;
    if (timeout) |timeout_ns| {
        ts_ptr = &ts;
        ts = timeout_ns.*;
    }
    const fds_count = math.cast(nfds_t, fds.len) orelse return error.SystemResources;
    const rc = system.ppoll(fds.ptr, fds_count, ts_ptr, mask);
    switch (errno(rc)) {
        .SUCCESS => return @as(usize, @intCast(rc)),
        .FAULT => unreachable,
        .INTR => return error.SignalInterrupt,
        .INVAL => unreachable,
        .NOMEM => return error.SystemResources,
        else => |err| return unexpectedErrno(err),
    }
}

RecvFromError

pub const RecvFromError = error{
    WouldBlock,

    ConnectionRefused,

    SystemResources,

    ConnectionResetByPeer,

    SocketNotBound,

    MessageTooBig,

    NetworkSubsystemFailed,

    SocketNotConnected,
} || UnexpectedError;

recv()

The socket is marked nonblocking and the requested operation would block, and there is no global event loop configured. A remote host refused to allow the network connection, typically because it is not running the requested service. Could not allocate kernel memory. The socket has not been bound. The UDP message was too big for the buffer and part of it has been discarded The network subsystem has failed. The socket is not connected (connection-oriented sockets only).

pub fn recv(sock: socket_t, buf: []u8, flags: u32) RecvFromError!usize {
    return recvfrom(sock, buf, flags, null, null);
}

recvfrom()

If sockfd is opened in non blocking mode, the function will return error.WouldBlock when EAGAIN is received.

pub fn recvfrom(
    sockfd: socket_t,
    buf: []u8,
    flags: u32,
    src_addr: ?*sockaddr,
    addrlen: ?*socklen_t,
) RecvFromError!usize {
    while (true) {
        const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen);
        if (builtin.os.tag == .windows) {
            if (rc == windows.ws2_32.SOCKET_ERROR) {
                switch (windows.ws2_32.WSAGetLastError()) {
                    .WSANOTINITIALISED => unreachable,
                    .WSAECONNRESET => return error.ConnectionResetByPeer,
                    .WSAEINVAL => return error.SocketNotBound,
                    .WSAEMSGSIZE => return error.MessageTooBig,
                    .WSAENETDOWN => return error.NetworkSubsystemFailed,
                    .WSAENOTCONN => return error.SocketNotConnected,
                    .WSAEWOULDBLOCK => return error.WouldBlock,
                    // TODO: handle more errors
                    else => |err| return windows.unexpectedWSAError(err),
                }
            } else {
                return @as(usize, @intCast(rc));
            }
        } else {
            switch (errno(rc)) {
                .SUCCESS => return @as(usize, @intCast(rc)),
                .BADF => unreachable, // always a race condition
                .FAULT => unreachable,
                .INVAL => unreachable,
                .NOTCONN => return error.SocketNotConnected,
                .NOTSOCK => unreachable,
                .INTR => continue,
                .AGAIN => return error.WouldBlock,
                .NOMEM => return error.SystemResources,
                .CONNREFUSED => return error.ConnectionRefused,
                .CONNRESET => return error.ConnectionResetByPeer,
                else => |err| return unexpectedErrno(err),
            }
        }
    }
}

DnExpandError

pub const DnExpandError = error{InvalidDnsPacket};

dn_expand()

pub fn dn_expand(
    msg: []const u8,
    comp_dn: []const u8,
    exp_dn: []u8,
) DnExpandError!usize {
    // This implementation is ported from musl libc.
    // A more idiomatic "ziggy" implementation would be welcome.
    var p = comp_dn.ptr;
    var len: usize = std.math.maxInt(usize);
    const end = msg.ptr + msg.len;
    if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket;
    var dest = exp_dn.ptr;
    const dend = dest + @min(exp_dn.len, 254);
    // detect reference loop using an iteration counter
    var i: usize = 0;
    while (i < msg.len) : (i += 2) {
        // loop invariants: p= msg.len) return error.InvalidDnsPacket;
            p = msg.ptr + j;
        } else if (p[0] != 0) {
            if (dest != exp_dn.ptr) {
                dest[0] = '.';
                dest += 1;
            }
            var j = p[0];
            p += 1;
            if (j >= @intFromPtr(end) - @intFromPtr(p) or j >= @intFromPtr(dend) - @intFromPtr(dest)) {
                return error.InvalidDnsPacket;
            }
            while (j != 0) {
                j -= 1;
                dest[0] = p[0];
                dest += 1;
                p += 1;
            }
        } else {
            dest[0] = 0;
            if (len == std.math.maxInt(usize)) len = @intFromPtr(p) + 1 - @intFromPtr(comp_dn.ptr);
            return len;
        }
    }
    return error.InvalidDnsPacket;
}

SetSockOptError

pub const SetSockOptError = error{
    AlreadyConnected,

    InvalidProtocolOption,

    TimeoutTooBig,

    SystemResources,

    // Setting the socket option requires more elevated permissions.
    PermissionDenied,

    NetworkSubsystemFailed,
    FileDescriptorNotASocket,
    SocketNotBound,
    NoDevice,
} || UnexpectedError;

setsockopt()

The socket is already connected, and a specified option cannot be set while the socket is connected. The option is not supported by the protocol. The send and receive timeout values are too big to fit into the timeout fields in the socket structure. Insufficient resources are available in the system to complete the call. Set a socket's options.

pub fn setsockopt(fd: socket_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void {
    if (builtin.os.tag == .windows) {
        const rc = windows.ws2_32.setsockopt(fd, @as(i32, @intCast(level)), @as(i32, @intCast(optname)), opt.ptr, @as(i32, @intCast(opt.len)));
        if (rc == windows.ws2_32.SOCKET_ERROR) {
            switch (windows.ws2_32.WSAGetLastError()) {
                .WSANOTINITIALISED => unreachable,
                .WSAENETDOWN => return error.NetworkSubsystemFailed,
                .WSAEFAULT => unreachable,
                .WSAENOTSOCK => return error.FileDescriptorNotASocket,
                .WSAEINVAL => return error.SocketNotBound,
                else => |err| return windows.unexpectedWSAError(err),
            }
        }
        return;
    } else {
        switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @as(socklen_t, @intCast(opt.len))))) {
            .SUCCESS => {},
            .BADF => unreachable, // always a race condition
            .NOTSOCK => unreachable, // always a race condition
            .INVAL => unreachable,
            .FAULT => unreachable,
            .DOM => return error.TimeoutTooBig,
            .ISCONN => return error.AlreadyConnected,
            .NOPROTOOPT => return error.InvalidProtocolOption,
            .NOMEM => return error.SystemResources,
            .NOBUFS => return error.SystemResources,
            .PERM => return error.PermissionDenied,
            .NODEV => return error.NoDevice,
            else => |err| return unexpectedErrno(err),
        }
    }
}

MemFdCreateError

pub const MemFdCreateError = error{
    SystemFdQuotaExceeded,
    ProcessFdQuotaExceeded,
    OutOfMemory,

    SystemOutdated,
} || UnexpectedError;

memfd_createZ()

memfd_create is available in Linux 3.17 and later. This error is returned for older kernel versions.

pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t {
    switch (builtin.os.tag) {
        .linux => {
            // memfd_create is available only in glibc versions starting with 2.27.
            const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok;
            const sys = if (use_c) std.c else linux;
            const getErrno = if (use_c) std.c.getErrno else linux.getErrno;
            const rc = sys.memfd_create(name, flags);
            switch (getErrno(rc)) {
                .SUCCESS => return @as(fd_t, @intCast(rc)),
                .FAULT => unreachable, // name has invalid memory
                .INVAL => unreachable, // name/flags are faulty
                .NFILE => return error.SystemFdQuotaExceeded,
                .MFILE => return error.ProcessFdQuotaExceeded,
                .NOMEM => return error.OutOfMemory,
                .NOSYS => return error.SystemOutdated,
                else => |err| return unexpectedErrno(err),
            }
        },
        .freebsd => {
            if (comptime builtin.os.version_range.semver.max.order(.{ .major = 13, .minor = 0, .patch = 0 }) == .lt)
                @compileError("memfd_create is unavailable on FreeBSD < 13.0");
            const rc = system.memfd_create(name, flags);
            switch (errno(rc)) {
                .SUCCESS => return rc,
                .BADF => unreachable, // name argument NULL
                .INVAL => unreachable, // name too long or invalid/unsupported flags.
                .MFILE => return error.ProcessFdQuotaExceeded,
                .NFILE => return error.SystemFdQuotaExceeded,
                .NOSYS => return error.SystemOutdated,
                else => |err| return unexpectedErrno(err),
            }
        },
        else => @compileError("target OS does not support memfd_create()"),
    }
}

MFD_NAME_PREFIX

pub const MFD_NAME_PREFIX = "memfd:";

MFD_MAX_NAME_LEN

pub const MFD_MAX_NAME_LEN = NAME_MAX - MFD_NAME_PREFIX.len;
fn toMemFdPath(name: []const u8) ![MFD_MAX_NAME_LEN:0]u8 {
    var path_with_null: [MFD_MAX_NAME_LEN:0]u8 = undefined;
    // >= rather than > to make room for the null byte
    if (name.len >= MFD_MAX_NAME_LEN) return error.NameTooLong;
    @memcpy(path_with_null[0..name.len], name);
    path_with_null[name.len] = 0;
    return path_with_null;
}

memfd_create()

pub fn memfd_create(name: []const u8, flags: u32) !fd_t {
    const name_t = try toMemFdPath(name);
    return memfd_createZ(&name_t, flags);
}

getrusage()

pub fn getrusage(who: i32) rusage {
    var result: rusage = undefined;
    const rc = system.getrusage(who, &result);
    switch (errno(rc)) {
        .SUCCESS => return result,
        .INVAL => unreachable,
        .FAULT => unreachable,
        else => unreachable,
    }
}

TIOCError

pub const TIOCError = error{NotATerminal};

TermiosGetError

pub const TermiosGetError = TIOCError || UnexpectedError;

tcgetattr()

pub fn tcgetattr(handle: fd_t) TermiosGetError!termios {
    while (true) {
        var term: termios = undefined;
        switch (errno(system.tcgetattr(handle, &term))) {
            .SUCCESS => return term,
            .INTR => continue,
            .BADF => unreachable,
            .NOTTY => return error.NotATerminal,
            else => |err| return unexpectedErrno(err),
        }
    }
}

TermiosSetError

pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned};

tcsetattr()

pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) TermiosSetError!void {
    while (true) {
        switch (errno(system.tcsetattr(handle, optional_action, &termios_p))) {
            .SUCCESS => return,
            .BADF => unreachable,
            .INTR => continue,
            .INVAL => unreachable,
            .NOTTY => return error.NotATerminal,
            .IO => return error.ProcessOrphaned,
            else => |err| return unexpectedErrno(err),
        }
    }
}

TermioGetPgrpError

pub const TermioGetPgrpError = TIOCError || UnexpectedError;

tcgetpgrp()

Returns the process group ID for the TTY associated with the given handle.

pub fn tcgetpgrp(handle: fd_t) TermioGetPgrpError!pid_t {
    while (true) {
        var pgrp: pid_t = undefined;
        switch (errno(system.tcgetpgrp(handle, &pgrp))) {
            .SUCCESS => return pgrp,
            .BADF => unreachable,
            .INVAL => unreachable,
            .INTR => continue,
            .NOTTY => return error.NotATerminal,
            else => |err| return unexpectedErrno(err),
        }
    }
}

TermioSetPgrpError

pub const TermioSetPgrpError = TermioGetPgrpError || error{NotAPgrpMember};

tcsetpgrp()

Sets the controlling process group ID for given TTY. handle must be valid fd_t to a TTY associated with calling process. pgrp must be a valid process group, and the calling process must be a member of that group.

pub fn tcsetpgrp(handle: fd_t, pgrp: pid_t) TermioSetPgrpError!void {
    while (true) {
        switch (errno(system.tcsetpgrp(handle, &pgrp))) {
            .SUCCESS => return,
            .BADF => unreachable,
            .INVAL => unreachable,
            .INTR => continue,
            .NOTTY => return error.NotATerminal,
            .PERM => return TermioSetPgrpError.NotAPgrpMember,
            else => |err| return unexpectedErrno(err),
        }
    }
}

IoCtl_SIOCGIFINDEX_Error

pub const IoCtl_SIOCGIFINDEX_Error = error{
    FileSystem,
    InterfaceNotFound,
} || UnexpectedError;

ioctl_SIOCGIFINDEX()

pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void {
    while (true) {
        switch (errno(system.ioctl(fd, SIOCGIFINDEX, @intFromPtr(ifr)))) {
            .SUCCESS => return,
            .INVAL => unreachable, // Bad parameters.
            .NOTTY => unreachable,
            .NXIO => unreachable,
            .BADF => unreachable, // Always a race condition.
            .FAULT => unreachable, // Bad pointer parameter.
            .INTR => continue,
            .IO => return error.FileSystem,
            .NODEV => return error.InterfaceNotFound,
            else => |err| return unexpectedErrno(err),
        }
    }
}

signalfd()

pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t {
    const rc = system.signalfd(fd, mask, flags);
    switch (errno(rc)) {
        .SUCCESS => return @as(fd_t, @intCast(rc)),
        .BADF, .INVAL => unreachable,
        .NFILE => return error.SystemFdQuotaExceeded,
        .NOMEM => return error.SystemResources,
        .MFILE => return error.ProcessResources,
        .NODEV => return error.InodeMountFail,
        .NOSYS => return error.SystemOutdated,
        else => |err| return unexpectedErrno(err),
    }
}

SyncError

pub const SyncError = error{
    InputOutput,
    NoSpaceLeft,
    DiskQuota,
    AccessDenied,
} || UnexpectedError;

sync()

Write all pending file contents and metadata modifications to all filesystems.

pub fn sync() void {
    system.sync();
}

syncfs()

Write all pending file contents and metadata modifications to the filesystem which contains the specified file.

pub fn syncfs(fd: fd_t) SyncError!void {
    const rc = system.syncfs(fd);
    switch (errno(rc)) {
        .SUCCESS => return,
        .BADF, .INVAL, .ROFS => unreachable,
        .IO => return error.InputOutput,
        .NOSPC => return error.NoSpaceLeft,
        .DQUOT => return error.DiskQuota,
        else => |err| return unexpectedErrno(err),
    }
}

fsync()

Write all pending file contents and metadata modifications for the specified file descriptor to the underlying filesystem.

pub fn fsync(fd: fd_t) SyncError!void {
    if (builtin.os.tag == .windows) {
        if (windows.kernel32.FlushFileBuffers(fd) != 0)
            return;
        switch (windows.kernel32.GetLastError()) {
            .SUCCESS => return,
            .INVALID_HANDLE => unreachable,
            .ACCESS_DENIED => return error.AccessDenied, // a sync was performed but the system couldn't update the access time
            .UNEXP_NET_ERR => return error.InputOutput,
            else => return error.InputOutput,
        }
    }
    const rc = system.fsync(fd);
    switch (errno(rc)) {
        .SUCCESS => return,
        .BADF, .INVAL, .ROFS => unreachable,
        .IO => return error.InputOutput,
        .NOSPC => return error.NoSpaceLeft,
        .DQUOT => return error.DiskQuota,
        else => |err| return unexpectedErrno(err),
    }
}

fdatasync()

Write all pending file contents for the specified file descriptor to the underlying filesystem, but not necessarily the metadata.

pub fn fdatasync(fd: fd_t) SyncError!void {
    if (builtin.os.tag == .windows) {
        return fsync(fd) catch |err| switch (err) {
            SyncError.AccessDenied => return, // fdatasync doesn't promise that the access time was synced
            else => return err,
        };
    }
    const rc = system.fdatasync(fd);
    switch (errno(rc)) {
        .SUCCESS => return,
        .BADF, .INVAL, .ROFS => unreachable,
        .IO => return error.InputOutput,
        .NOSPC => return error.NoSpaceLeft,
        .DQUOT => return error.DiskQuota,
        else => |err| return unexpectedErrno(err),
    }
}

PrctlError

pub const PrctlError = error{
    AccessDenied,
    InvalidFileDescriptor,
    InvalidAddress,
    UnsupportedFeature,
    OperationNotSupported,
    PermissionDenied,
} || UnexpectedError;

prctl()

Can only occur with PR_SET_SECCOMP/SECCOMP_MODE_FILTER or PR_SET_MM/PR_SET_MM_EXE_FILE Can only occur with PR_SET_MM/PR_SET_MM_EXE_FILE Can only occur with PR_SET_SPECULATION_CTRL, PR_MPX_ENABLE_MANAGEMENT, or PR_MPX_DISABLE_MANAGEMENT Can only occur with PR_SET_FP_MODE

pub fn prctl(option: PR, args: anytype) PrctlError!u31 {
    if (@typeInfo(@TypeOf(args)) != .Struct)
        @compileError("Expected tuple or struct argument, found " ++ @typeName(@TypeOf(args)));
    if (args.len > 4)
        @compileError("prctl takes a maximum of 4 optional arguments");

    var buf: [4]usize = undefined;
    {
        comptime var i = 0;
        inline while (i < args.len) : (i += 1) buf[i] = args[i];
    }

    const rc = system.prctl(@intFromEnum(option), buf[0], buf[1], buf[2], buf[3]);
    switch (errno(rc)) {
        .SUCCESS => return @as(u31, @intCast(rc)),
        .ACCES => return error.AccessDenied,
        .BADF => return error.InvalidFileDescriptor,
        .FAULT => return error.InvalidAddress,
        .INVAL => unreachable,
        .NODEV, .NXIO => return error.UnsupportedFeature,
        .OPNOTSUPP => return error.OperationNotSupported,
        .PERM, .BUSY => return error.PermissionDenied,
        .RANGE => unreachable,
        else => |err| return unexpectedErrno(err),
    }
}

GetrlimitError

pub const GetrlimitError = UnexpectedError;

getrlimit()

pub fn getrlimit(resource: rlimit_resource) GetrlimitError!rlimit {
    const getrlimit_sym = if (lfs64_abi) system.getrlimit64 else system.getrlimit;

    var limits: rlimit = undefined;
    switch (errno(getrlimit_sym(resource, &limits))) {
        .SUCCESS => return limits,
        .FAULT => unreachable, // bogus pointer
        .INVAL => unreachable,
        else => |err| return unexpectedErrno(err),
    }
}

SetrlimitError

pub const SetrlimitError = error{ PermissionDenied, LimitTooBig } || UnexpectedError;

setrlimit()

pub fn setrlimit(resource: rlimit_resource, limits: rlimit) SetrlimitError!void {
    const setrlimit_sym = if (lfs64_abi) system.setrlimit64 else system.setrlimit;

    switch (errno(setrlimit_sym(resource, &limits))) {
        .SUCCESS => return,
        .FAULT => unreachable, // bogus pointer
        .INVAL => return error.LimitTooBig, // this could also mean "invalid resource", but that would be unreachable
        .PERM => return error.PermissionDenied,
        else => |err| return unexpectedErrno(err),
    }
}

MincoreError

pub const MincoreError = error{
    SystemResources,
    InvalidAddress,
    InvalidSyscall,
    OutOfMemory,
    MincoreUnavailable,
} || UnexpectedError;

mincore()

A kernel resource was temporarily unavailable. vec points to an invalid address. addr is not page-aligned. One of the following: * length is greater than user space TASK_SIZE - addr * addr + length contains unmapped memory The mincore syscall is not available on this version and configuration of this UNIX-like kernel. Determine whether pages are resident in memory.

pub fn mincore(ptr: [*]align(mem.page_size) u8, length: usize, vec: [*]u8) MincoreError!void {
    return switch (errno(system.mincore(ptr, length, vec))) {
        .SUCCESS => {},
        .AGAIN => error.SystemResources,
        .FAULT => error.InvalidAddress,
        .INVAL => error.InvalidSyscall,
        .NOMEM => error.OutOfMemory,
        .NOSYS => error.MincoreUnavailable,
        else => |err| unexpectedErrno(err),
    };
}

MadviseError

pub const MadviseError = error{
    AccessDenied,
    PermissionDenied,
    SystemResources,
    InvalidSyscall,
    WouldExceedMaximumResidentSetSize,
    OutOfMemory,
    MadviseUnavailable,
    Unexpected,
};

madvise()

advice is MADV.REMOVE, but the specified address range is not a shared writable mapping. advice is MADV.HWPOISON, but the caller does not have the CAP_SYS_ADMIN capability. A kernel resource was temporarily unavailable. One of the following: * addr is not page-aligned or length is negative * advice is not valid * advice is MADV.DONTNEED or MADV.REMOVE and the specified address range includes locked, Huge TLB pages, or VM_PFNMAP pages. * advice is MADV.MERGEABLE or MADV.UNMERGEABLE, but the kernel was not configured with CONFIG_KSM. * advice is MADV.FREE or MADV.WIPEONFORK but the specified address range includes file, Huge TLB, MAP.SHARED, or VM_PFNMAP ranges. (for MADV.WILLNEED) Paging in this area would exceed the process's maximum resident set size. One of the following: * (for MADV.WILLNEED) Not enough memory: paging in failed. * Addresses in the specified range are not currently mapped, or are outside the address space of the process. The madvise syscall is not available on this version and configuration of the Linux kernel. The operating system returned an undocumented error code. Give advice about use of memory. This syscall is optional and is sometimes configured to be disabled.

pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) MadviseError!void {
    switch (errno(system.madvise(ptr, length, advice))) {
        .SUCCESS => return,
        .ACCES => return error.AccessDenied,
        .AGAIN => return error.SystemResources,
        .BADF => unreachable, // The map exists, but the area maps something that isn't a file.
        .INVAL => return error.InvalidSyscall,
        .IO => return error.WouldExceedMaximumResidentSetSize,
        .NOMEM => return error.OutOfMemory,
        .NOSYS => return error.MadviseUnavailable,
        else => |err| return unexpectedErrno(err),
    }
}

PerfEventOpenError

pub const PerfEventOpenError = error{
    TooBig,
    PermissionDenied,
    DeviceBusy,
    ProcessResources,
    EventRequiresUnsupportedCpuFeature,
    TooManyBreakpoints,
    SampleStackNotSupported,
    EventNotSupported,
    SampleMaxStackOverflow,
    ProcessNotFound,
} || UnexpectedError;

perf_event_open()

Returned if the perf_event_attr size value is too small (smaller than PERF_ATTR_SIZE_VER0), too big (larger than the page size), or larger than the kernel supports and the extra bytes are not zero. When E2BIG is returned, the perf_event_attr size field is overwritten by the kernel to be the size of the structure it was expecting. Returned when the requested event requires CAP_SYS_ADMIN permis‐ sions (or a more permissive perf_event paranoid setting). Some common cases where an unprivileged process may encounter this error: attaching to a process owned by a different user; moni‐ toring all processes on a given CPU (i.e., specifying the pid argument as -1); and not setting exclude_kernel when the para‐ noid setting requires it. Also: Returned on many (but not all) architectures when an unsupported exclude_hv, exclude_idle, exclude_user, or exclude_kernel set‐ ting is specified. It can also happen, as with EACCES, when the requested event re‐ quires CAP_SYS_ADMIN permissions (or a more permissive perf_event paranoid setting). This includes setting a break‐ point on a kernel address, and (since Linux 3.13) setting a ker‐ nel function-trace tracepoint. Returned if another event already has exclusive access to the PMU. Each opened event uses one file descriptor. If a large number of events are opened, the per-process limit on the number of open file descriptors will be reached, and no more events can be created. Returned if you try to add more breakpoint events than supported by the hardware. Returned if PERF_SAMPLE_STACK_USER is set in sample_type and it is not supported by hardware. Returned if an event requiring a specific hardware feature is requested but there is no hardware support. This includes re‐ questing low-skid events if not supported, branch tracing if it is not available, sampling if no PMU interrupt is available, and branch stacks for software events. Returned if PERF_SAMPLE_CALLCHAIN is requested and sam‐ ple_max_stack is larger than the maximum specified in /proc/sys/kernel/perf_event_max_stack. Returned if attempting to attach to a process that does not exist.

pub fn perf_event_open(
    attr: *linux.perf_event_attr,
    pid: pid_t,
    cpu: i32,
    group_fd: fd_t,
    flags: usize,
) PerfEventOpenError!fd_t {
    const rc = system.perf_event_open(attr, pid, cpu, group_fd, flags);
    switch (errno(rc)) {
        .SUCCESS => return @as(fd_t, @intCast(rc)),
        .@"2BIG" => return error.TooBig,
        .ACCES => return error.PermissionDenied,
        .BADF => unreachable, // group_fd file descriptor is not valid.
        .BUSY => return error.DeviceBusy,
        .FAULT => unreachable, // Segmentation fault.
        .INVAL => unreachable, // Bad attr settings.
        .INTR => unreachable, // Mixed perf and ftrace handling for a uprobe.
        .MFILE => return error.ProcessResources,
        .NODEV => return error.EventRequiresUnsupportedCpuFeature,
        .NOENT => unreachable, // Invalid type setting.
        .NOSPC => return error.TooManyBreakpoints,
        .NOSYS => return error.SampleStackNotSupported,
        .OPNOTSUPP => return error.EventNotSupported,
        .OVERFLOW => return error.SampleMaxStackOverflow,
        .PERM => return error.PermissionDenied,
        .SRCH => return error.ProcessNotFound,
        else => |err| return unexpectedErrno(err),
    }
}

TimerFdCreateError

pub const TimerFdCreateError = error{
    AccessDenied,
    ProcessFdQuotaExceeded,
    SystemFdQuotaExceeded,
    NoDevice,
    SystemResources,
} || UnexpectedError;

TimerFdGetError

pub const TimerFdGetError = error{InvalidHandle} || UnexpectedError;

TimerFdSetError

pub const TimerFdSetError = TimerFdGetError || error{Canceled};

timerfd_create()

pub fn timerfd_create(clokid: i32, flags: u32) TimerFdCreateError!fd_t {
    var rc = linux.timerfd_create(clokid, flags);
    return switch (errno(rc)) {
        .SUCCESS => @as(fd_t, @intCast(rc)),
        .INVAL => unreachable,
        .MFILE => return error.ProcessFdQuotaExceeded,
        .NFILE => return error.SystemFdQuotaExceeded,
        .NODEV => return error.NoDevice,
        .NOMEM => return error.SystemResources,
        .PERM => return error.AccessDenied,
        else => |err| return unexpectedErrno(err),
    };
}

timerfd_settime()

pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const linux.itimerspec, old_value: ?*linux.itimerspec) TimerFdSetError!void {
    var rc = linux.timerfd_settime(fd, flags, new_value, old_value);
    return switch (errno(rc)) {
        .SUCCESS => {},
        .BADF => error.InvalidHandle,
        .FAULT => unreachable,
        .INVAL => unreachable,
        .CANCELED => error.Canceled,
        else => |err| return unexpectedErrno(err),
    };
}

timerfd_gettime()

pub fn timerfd_gettime(fd: i32) TimerFdGetError!linux.itimerspec {
    var curr_value: linux.itimerspec = undefined;
    var rc = linux.timerfd_gettime(fd, &curr_value);
    return switch (errno(rc)) {
        .SUCCESS => return curr_value,
        .BADF => error.InvalidHandle,
        .FAULT => unreachable,
        .INVAL => unreachable,
        else => |err| return unexpectedErrno(err),
    };
}

PtraceError

pub const PtraceError = error{
    DeviceBusy,
    InputOutput,
    Overflow,
    ProcessNotFound,
    PermissionDenied,
} || UnexpectedError;

ptrace()

pub fn ptrace(request: u32, pid: pid_t, addr: usize, signal: usize) PtraceError!void {
    if (builtin.os.tag == .windows or builtin.os.tag == .wasi)
        @compileError("Unsupported OS");

    return switch (builtin.os.tag) {
        .linux => switch (errno(linux.ptrace(request, pid, addr, signal, 0))) {
            .SUCCESS => {},
            .SRCH => error.ProcessNotFound,
            .FAULT => unreachable,
            .INVAL => unreachable,
            .IO => return error.InputOutput,
            .PERM => error.PermissionDenied,
            .BUSY => error.DeviceBusy,
            else => |err| return unexpectedErrno(err),
        },

        .macos, .ios, .tvos, .watchos => switch (errno(darwin.ptrace(
            math.cast(i32, request) orelse return error.Overflow,
            pid,
            @as(?[*]u8, @ptrFromInt(addr)),
            math.cast(i32, signal) orelse return error.Overflow,
        ))) {
            .SUCCESS => {},
            .SRCH => error.ProcessNotFound,
            .INVAL => unreachable,
            .PERM => error.PermissionDenied,
            .BUSY => error.DeviceBusy,
            else => |err| return unexpectedErrno(err),
        },

        else => switch (errno(system.ptrace(request, pid, addr, signal))) {
            .SUCCESS => {},
            .SRCH => error.ProcessNotFound,
            .INVAL => unreachable,
            .PERM => error.PermissionDenied,
            .BUSY => error.DeviceBusy,
            else => |err| return unexpectedErrno(err),
        },
    };
}

const lfs64_abi = builtin.os.tag == .linux and builtin.link_libc and builtin.abi.isGnu();