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