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