blob e8431c38 (210027B) - Raw
1 // SPDX-License-Identifier: MIT 2 // Copyright (c) 2015-2020 Zig Contributors 3 // This file is part of [zig](https://ziglang.org/), which is MIT licensed. 4 // The MIT license requires this copyright notice to be included in all copies 5 // and substantial portions of the software. 6 // This file contains thin wrappers around OS-specific APIs, with these 7 // specific goals in mind: 8 // * Convert "errno"-style error codes into Zig errors. 9 // * When null-terminated byte buffers are required, provide APIs which accept 10 // slices as well as APIs which accept null-terminated byte buffers. Same goes 11 // for UTF-16LE encoding. 12 // * Where operating systems share APIs, e.g. POSIX, these thin wrappers provide 13 // cross platform abstracting. 14 // * When there exists a corresponding libc function and linking libc, the libc 15 // implementation is used. Exceptions are made for known buggy areas of libc. 16 // On Linux libc can be side-stepped by using `std.os.linux` directly. 17 // * For Windows, this file represents the API that libc would provide for 18 // Windows. For thin wrappers around Windows-specific APIs, see `std.os.windows`. 19 // Note: The Zig standard library does not support POSIX thread cancellation, and 20 // in general EINTR is handled by trying again. 21 22 const root = @import("root"); 23 const std = @import("std.zig"); 24 const builtin = @import("builtin"); 25 const assert = std.debug.assert; 26 const math = std.math; 27 const mem = std.mem; 28 const elf = std.elf; 29 const dl = @import("dynamic_library.zig"); 30 const MAX_PATH_BYTES = std.fs.MAX_PATH_BYTES; 31 32 pub const darwin = @import("os/darwin.zig"); 33 pub const dragonfly = @import("os/dragonfly.zig"); 34 pub const freebsd = @import("os/freebsd.zig"); 35 pub const netbsd = @import("os/netbsd.zig"); 36 pub const linux = @import("os/linux.zig"); 37 pub const uefi = @import("os/uefi.zig"); 38 pub const wasi = @import("os/wasi.zig"); 39 pub const windows = @import("os/windows.zig"); 40 41 comptime { 42 assert(@import("std") == std); // std lib tests require --override-lib-dir 43 } 44 45 test "" { 46 _ = darwin; 47 _ = freebsd; 48 _ = linux; 49 _ = netbsd; 50 _ = uefi; 51 _ = wasi; 52 _ = windows; 53 54 _ = @import("os/test.zig"); 55 } 56 57 /// Applications can override the `system` API layer in their root source file. 58 /// Otherwise, when linking libc, this is the C API. 59 /// When not linking libc, it is the OS-specific system interface. 60 pub const system = if (@hasDecl(root, "os") and root.os != @This()) 61 root.os.system 62 else if (builtin.link_libc) 63 std.c 64 else switch (builtin.os.tag) { 65 .macosx, .ios, .watchos, .tvos => darwin, 66 .freebsd => freebsd, 67 .linux => linux, 68 .netbsd => netbsd, 69 .dragonfly => dragonfly, 70 .wasi => wasi, 71 .windows => windows, 72 else => struct {}, 73 }; 74 75 pub usingnamespace @import("os/bits.zig"); 76 77 pub const socket_t = if (builtin.os.tag == .windows) windows.ws2_32.SOCKET else fd_t; 78 79 /// See also `getenv`. Populated by startup code before main(). 80 /// TODO this is a footgun because the value will be undefined when using `zig build-lib`. 81 /// https://github.com/ziglang/zig/issues/4524 82 pub var environ: [][*:0]u8 = undefined; 83 84 /// Populated by startup code before main(). 85 /// Not available on Windows. See `std.process.args` 86 /// for obtaining the process arguments. 87 pub var argv: [][*:0]u8 = undefined; 88 89 /// To obtain errno, call this function with the return value of the 90 /// system function call. For some systems this will obtain the value directly 91 /// from the return code; for others it will use a thread-local errno variable. 92 /// Therefore, this function only returns a well-defined value when it is called 93 /// directly after the system function call which one wants to learn the errno 94 /// value of. 95 pub const errno = system.getErrno; 96 97 /// Closes the file descriptor. 98 /// This function is not capable of returning any indication of failure. An 99 /// application which wants to ensure writes have succeeded before closing 100 /// must call `fsync` before `close`. 101 /// Note: The Zig standard library does not support POSIX thread cancellation. 102 pub fn close(fd: fd_t) void { 103 if (builtin.os.tag == .windows) { 104 return windows.CloseHandle(fd); 105 } 106 if (builtin.os.tag == .wasi) { 107 _ = wasi.fd_close(fd); 108 return; 109 } 110 if (comptime std.Target.current.isDarwin()) { 111 // This avoids the EINTR problem. 112 switch (darwin.getErrno(darwin.@"close$NOCANCEL"(fd))) { 113 EBADF => unreachable, // Always a race condition. 114 else => return, 115 } 116 } 117 switch (errno(system.close(fd))) { 118 EBADF => unreachable, // Always a race condition. 119 EINTR => return, // This is still a success. See https://github.com/ziglang/zig/issues/2425 120 else => return, 121 } 122 } 123 124 pub const GetRandomError = OpenError; 125 126 /// Obtain a series of random bytes. These bytes can be used to seed user-space 127 /// random number generators or for cryptographic purposes. 128 /// When linking against libc, this calls the 129 /// appropriate OS-specific library call. Otherwise it uses the zig standard 130 /// library implementation. 131 pub fn getrandom(buffer: []u8) GetRandomError!void { 132 if (builtin.os.tag == .windows) { 133 return windows.RtlGenRandom(buffer); 134 } 135 if (builtin.os.tag == .linux or builtin.os.tag == .freebsd) { 136 var buf = buffer; 137 const use_c = builtin.os.tag != .linux or 138 std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok; 139 140 while (buf.len != 0) { 141 var err: u16 = undefined; 142 143 const num_read = if (use_c) blk: { 144 const rc = std.c.getrandom(buf.ptr, buf.len, 0); 145 err = std.c.getErrno(rc); 146 break :blk @bitCast(usize, rc); 147 } else blk: { 148 const rc = linux.getrandom(buf.ptr, buf.len, 0); 149 err = linux.getErrno(rc); 150 break :blk rc; 151 }; 152 153 switch (err) { 154 0 => buf = buf[num_read..], 155 EINVAL => unreachable, 156 EFAULT => unreachable, 157 EINTR => continue, 158 ENOSYS => return getRandomBytesDevURandom(buf), 159 else => return unexpectedErrno(err), 160 } 161 } 162 return; 163 } 164 if (builtin.os.tag == .netbsd) { 165 netbsd.arc4random_buf(buffer.ptr, buffer.len); 166 return; 167 } 168 if (builtin.os.tag == .wasi) { 169 switch (wasi.random_get(buffer.ptr, buffer.len)) { 170 0 => return, 171 else => |err| return unexpectedErrno(err), 172 } 173 } 174 return getRandomBytesDevURandom(buffer); 175 } 176 177 fn getRandomBytesDevURandom(buf: []u8) !void { 178 const fd = try openZ("/dev/urandom", O_RDONLY | O_CLOEXEC, 0); 179 defer close(fd); 180 181 const st = try fstat(fd); 182 if (!S_ISCHR(st.mode)) { 183 return error.NoDevice; 184 } 185 186 const file = std.fs.File{ 187 .handle = fd, 188 .capable_io_mode = .blocking, 189 .intended_io_mode = .blocking, 190 }; 191 const stream = file.inStream(); 192 stream.readNoEof(buf) catch return error.Unexpected; 193 } 194 195 /// Causes abnormal process termination. 196 /// If linking against libc, this calls the abort() libc function. Otherwise 197 /// it raises SIGABRT followed by SIGKILL and finally lo 198 pub fn abort() noreturn { 199 @setCold(true); 200 // MSVCRT abort() sometimes opens a popup window which is undesirable, so 201 // even when linking libc on Windows we use our own abort implementation. 202 // See https://github.com/ziglang/zig/issues/2071 for more details. 203 if (builtin.os.tag == .windows) { 204 if (builtin.mode == .Debug) { 205 @breakpoint(); 206 } 207 windows.kernel32.ExitProcess(3); 208 } 209 if (!builtin.link_libc and builtin.os.tag == .linux) { 210 raise(SIGABRT) catch {}; 211 212 // TODO the rest of the implementation of abort() from musl libc here 213 214 raise(SIGKILL) catch {}; 215 exit(127); 216 } 217 if (builtin.os.tag == .uefi) { 218 exit(0); // TODO choose appropriate exit code 219 } 220 if (builtin.os.tag == .wasi) { 221 @breakpoint(); 222 exit(1); 223 } 224 225 system.abort(); 226 } 227 228 pub const RaiseError = UnexpectedError; 229 230 pub fn raise(sig: u8) RaiseError!void { 231 if (builtin.link_libc) { 232 switch (errno(system.raise(sig))) { 233 0 => return, 234 else => |err| return unexpectedErrno(err), 235 } 236 } 237 238 if (builtin.os.tag == .linux) { 239 var set: linux.sigset_t = undefined; 240 // block application signals 241 _ = linux.sigprocmask(SIG_BLOCK, &linux.app_mask, &set); 242 243 const tid = linux.gettid(); 244 const rc = linux.tkill(tid, sig); 245 246 // restore signal mask 247 _ = linux.sigprocmask(SIG_SETMASK, &set, null); 248 249 switch (errno(rc)) { 250 0 => return, 251 else => |err| return unexpectedErrno(err), 252 } 253 } 254 255 @compileError("std.os.raise unimplemented for this target"); 256 } 257 258 pub const KillError = error{PermissionDenied} || UnexpectedError; 259 260 pub fn kill(pid: pid_t, sig: u8) KillError!void { 261 switch (errno(system.kill(pid, sig))) { 262 0 => return, 263 EINVAL => unreachable, // invalid signal 264 EPERM => return error.PermissionDenied, 265 ESRCH => unreachable, // always a race condition 266 else => |err| return unexpectedErrno(err), 267 } 268 } 269 270 /// Exits the program cleanly with the specified status code. 271 pub fn exit(status: u8) noreturn { 272 if (builtin.link_libc) { 273 system.exit(status); 274 } 275 if (builtin.os.tag == .windows) { 276 windows.kernel32.ExitProcess(status); 277 } 278 if (builtin.os.tag == .wasi) { 279 wasi.proc_exit(status); 280 } 281 if (builtin.os.tag == .linux and !builtin.single_threaded) { 282 linux.exit_group(status); 283 } 284 if (builtin.os.tag == .uefi) { 285 // exit() is only avaliable if exitBootServices() has not been called yet. 286 // This call to exit should not fail, so we don't care about its return value. 287 if (uefi.system_table.boot_services) |bs| { 288 _ = bs.exit(uefi.handle, @intToEnum(uefi.Status, status), 0, null); 289 } 290 // If we can't exit, reboot the system instead. 291 uefi.system_table.runtime_services.resetSystem(uefi.tables.ResetType.ResetCold, @intToEnum(uefi.Status, status), 0, null); 292 } 293 system.exit(status); 294 } 295 296 pub const ReadError = error{ 297 InputOutput, 298 SystemResources, 299 IsDir, 300 OperationAborted, 301 BrokenPipe, 302 ConnectionResetByPeer, 303 ConnectionTimedOut, 304 NotOpenForReading, 305 306 /// This error occurs when no global event loop is configured, 307 /// and reading from the file descriptor would block. 308 WouldBlock, 309 310 /// In WASI, this error occurs when the file descriptor does 311 /// not hold the required rights to read from it. 312 AccessDenied, 313 } || UnexpectedError; 314 315 /// Returns the number of bytes that were read, which can be less than 316 /// buf.len. If 0 bytes were read, that means EOF. 317 /// If the application has a global event loop enabled, EAGAIN is handled 318 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 319 /// 320 /// Linux has a limit on how many bytes may be transferred in one `read` call, which is `0x7ffff000` 321 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as 322 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `read` man page. 323 /// For POSIX the limit is `math.maxInt(isize)`. 324 pub fn read(fd: fd_t, buf: []u8) ReadError!usize { 325 if (builtin.os.tag == .windows) { 326 return windows.ReadFile(fd, buf, null, std.io.default_mode); 327 } 328 if (builtin.os.tag == .wasi and !builtin.link_libc) { 329 const iovs = [1]iovec{iovec{ 330 .iov_base = buf.ptr, 331 .iov_len = buf.len, 332 }}; 333 334 var nread: usize = undefined; 335 switch (wasi.fd_read(fd, &iovs, iovs.len, &nread)) { 336 wasi.ESUCCESS => return nread, 337 wasi.EINTR => unreachable, 338 wasi.EINVAL => unreachable, 339 wasi.EFAULT => unreachable, 340 wasi.EAGAIN => unreachable, 341 wasi.EBADF => return error.NotOpenForReading, // Can be a race condition. 342 wasi.EIO => return error.InputOutput, 343 wasi.EISDIR => return error.IsDir, 344 wasi.ENOBUFS => return error.SystemResources, 345 wasi.ENOMEM => return error.SystemResources, 346 wasi.ECONNRESET => return error.ConnectionResetByPeer, 347 wasi.ETIMEDOUT => return error.ConnectionTimedOut, 348 wasi.ENOTCAPABLE => return error.AccessDenied, 349 else => |err| return unexpectedErrno(err), 350 } 351 } 352 353 // Prevents EINVAL. 354 const max_count = switch (std.Target.current.os.tag) { 355 .linux => 0x7ffff000, 356 else => math.maxInt(isize), 357 }; 358 const adjusted_len = math.min(max_count, buf.len); 359 360 while (true) { 361 const rc = system.read(fd, buf.ptr, adjusted_len); 362 switch (errno(rc)) { 363 0 => return @intCast(usize, rc), 364 EINTR => continue, 365 EINVAL => unreachable, 366 EFAULT => unreachable, 367 EAGAIN => if (std.event.Loop.instance) |loop| { 368 loop.waitUntilFdReadable(fd); 369 continue; 370 } else { 371 return error.WouldBlock; 372 }, 373 EBADF => return error.NotOpenForReading, // Can be a race condition. 374 EIO => return error.InputOutput, 375 EISDIR => return error.IsDir, 376 ENOBUFS => return error.SystemResources, 377 ENOMEM => return error.SystemResources, 378 ECONNRESET => return error.ConnectionResetByPeer, 379 ETIMEDOUT => return error.ConnectionTimedOut, 380 else => |err| return unexpectedErrno(err), 381 } 382 } 383 return index; 384 } 385 386 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. 387 /// 388 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled 389 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. 390 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are 391 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. 392 /// 393 /// This operation is non-atomic on the following systems: 394 /// * Windows 395 /// On these systems, the read races with concurrent writes to the same file descriptor. 396 pub fn readv(fd: fd_t, iov: []const iovec) ReadError!usize { 397 if (std.Target.current.os.tag == .windows) { 398 // TODO improve this to use ReadFileScatter 399 if (iov.len == 0) return @as(usize, 0); 400 const first = iov[0]; 401 return read(fd, first.iov_base[0..first.iov_len]); 402 } 403 if (builtin.os.tag == .wasi) { 404 var nread: usize = undefined; 405 switch (wasi.fd_read(fd, iov.ptr, iov.len, &nread)) { 406 wasi.ESUCCESS => return nread, 407 wasi.EINTR => unreachable, 408 wasi.EINVAL => unreachable, 409 wasi.EFAULT => unreachable, 410 wasi.EAGAIN => unreachable, // currently not support in WASI 411 wasi.EBADF => return error.NotOpenForReading, // can be a race condition 412 wasi.EIO => return error.InputOutput, 413 wasi.EISDIR => return error.IsDir, 414 wasi.ENOBUFS => return error.SystemResources, 415 wasi.ENOMEM => return error.SystemResources, 416 wasi.ENOTCAPABLE => return error.AccessDenied, 417 else => |err| return unexpectedErrno(err), 418 } 419 } 420 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31); 421 while (true) { 422 // TODO handle the case when iov_len is too large and get rid of this @intCast 423 const rc = system.readv(fd, iov.ptr, iov_count); 424 switch (errno(rc)) { 425 0 => return @intCast(usize, rc), 426 EINTR => continue, 427 EINVAL => unreachable, 428 EFAULT => unreachable, 429 EAGAIN => if (std.event.Loop.instance) |loop| { 430 loop.waitUntilFdReadable(fd); 431 continue; 432 } else { 433 return error.WouldBlock; 434 }, 435 EBADF => return error.NotOpenForReading, // can be a race condition 436 EIO => return error.InputOutput, 437 EISDIR => return error.IsDir, 438 ENOBUFS => return error.SystemResources, 439 ENOMEM => return error.SystemResources, 440 else => |err| return unexpectedErrno(err), 441 } 442 } 443 } 444 445 pub const PReadError = ReadError || error{Unseekable}; 446 447 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. 448 /// 449 /// Retries when interrupted by a signal. 450 /// 451 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled 452 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. 453 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are 454 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. 455 pub fn pread(fd: fd_t, buf: []u8, offset: u64) PReadError!usize { 456 if (builtin.os.tag == .windows) { 457 return windows.ReadFile(fd, buf, offset, std.io.default_mode); 458 } 459 if (builtin.os.tag == .wasi) { 460 const iovs = [1]iovec{iovec{ 461 .iov_base = buf.ptr, 462 .iov_len = buf.len, 463 }}; 464 465 var nread: usize = undefined; 466 switch (wasi.fd_pread(fd, &iovs, iovs.len, offset, &nread)) { 467 wasi.ESUCCESS => return nread, 468 wasi.EINTR => unreachable, 469 wasi.EINVAL => unreachable, 470 wasi.EFAULT => unreachable, 471 wasi.EAGAIN => unreachable, 472 wasi.EBADF => return error.NotOpenForReading, // Can be a race condition. 473 wasi.EIO => return error.InputOutput, 474 wasi.EISDIR => return error.IsDir, 475 wasi.ENOBUFS => return error.SystemResources, 476 wasi.ENOMEM => return error.SystemResources, 477 wasi.ECONNRESET => return error.ConnectionResetByPeer, 478 wasi.ENXIO => return error.Unseekable, 479 wasi.ESPIPE => return error.Unseekable, 480 wasi.EOVERFLOW => return error.Unseekable, 481 wasi.ENOTCAPABLE => return error.AccessDenied, 482 else => |err| return unexpectedErrno(err), 483 } 484 } 485 486 while (true) { 487 const rc = system.pread(fd, buf.ptr, buf.len, offset); 488 switch (errno(rc)) { 489 0 => return @intCast(usize, rc), 490 EINTR => continue, 491 EINVAL => unreachable, 492 EFAULT => unreachable, 493 EAGAIN => if (std.event.Loop.instance) |loop| { 494 loop.waitUntilFdReadable(fd); 495 continue; 496 } else { 497 return error.WouldBlock; 498 }, 499 EBADF => return error.NotOpenForReading, // Can be a race condition. 500 EIO => return error.InputOutput, 501 EISDIR => return error.IsDir, 502 ENOBUFS => return error.SystemResources, 503 ENOMEM => return error.SystemResources, 504 ECONNRESET => return error.ConnectionResetByPeer, 505 ENXIO => return error.Unseekable, 506 ESPIPE => return error.Unseekable, 507 EOVERFLOW => return error.Unseekable, 508 else => |err| return unexpectedErrno(err), 509 } 510 } 511 } 512 513 pub const TruncateError = error{ 514 FileTooBig, 515 InputOutput, 516 FileBusy, 517 518 /// In WASI, this error occurs when the file descriptor does 519 /// not hold the required rights to call `ftruncate` on it. 520 AccessDenied, 521 } || UnexpectedError; 522 523 pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void { 524 if (std.Target.current.os.tag == .windows) { 525 var io_status_block: windows.IO_STATUS_BLOCK = undefined; 526 var eof_info = windows.FILE_END_OF_FILE_INFORMATION{ 527 .EndOfFile = @bitCast(windows.LARGE_INTEGER, length), 528 }; 529 530 const rc = windows.ntdll.NtSetInformationFile( 531 fd, 532 &io_status_block, 533 &eof_info, 534 @sizeOf(windows.FILE_END_OF_FILE_INFORMATION), 535 .FileEndOfFileInformation, 536 ); 537 538 switch (rc) { 539 .SUCCESS => return, 540 .INVALID_HANDLE => unreachable, // Handle not open for writing 541 .ACCESS_DENIED => return error.AccessDenied, 542 else => return windows.unexpectedStatus(rc), 543 } 544 } 545 if (std.Target.current.os.tag == .wasi) { 546 switch (wasi.fd_filestat_set_size(fd, length)) { 547 wasi.ESUCCESS => return, 548 wasi.EINTR => unreachable, 549 wasi.EFBIG => return error.FileTooBig, 550 wasi.EIO => return error.InputOutput, 551 wasi.EPERM => return error.AccessDenied, 552 wasi.ETXTBSY => return error.FileBusy, 553 wasi.EBADF => unreachable, // Handle not open for writing 554 wasi.EINVAL => unreachable, // Handle not open for writing 555 wasi.ENOTCAPABLE => return error.AccessDenied, 556 else => |err| return unexpectedErrno(err), 557 } 558 } 559 560 while (true) { 561 const rc = if (builtin.link_libc) 562 if (std.Target.current.os.tag == .linux) 563 system.ftruncate64(fd, @bitCast(off_t, length)) 564 else 565 system.ftruncate(fd, @bitCast(off_t, length)) 566 else 567 system.ftruncate(fd, length); 568 569 switch (errno(rc)) { 570 0 => return, 571 EINTR => continue, 572 EFBIG => return error.FileTooBig, 573 EIO => return error.InputOutput, 574 EPERM => return error.AccessDenied, 575 ETXTBSY => return error.FileBusy, 576 EBADF => unreachable, // Handle not open for writing 577 EINVAL => unreachable, // Handle not open for writing 578 else => |err| return unexpectedErrno(err), 579 } 580 } 581 } 582 583 /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. 584 /// 585 /// Retries when interrupted by a signal. 586 /// 587 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled 588 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. 589 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are 590 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. 591 /// 592 /// This operation is non-atomic on the following systems: 593 /// * Darwin 594 /// * Windows 595 /// On these systems, the read races with concurrent writes to the same file descriptor. 596 pub fn preadv(fd: fd_t, iov: []const iovec, offset: u64) PReadError!usize { 597 const have_pread_but_not_preadv = switch (std.Target.current.os.tag) { 598 .windows, .macosx, .ios, .watchos, .tvos => true, 599 else => false, 600 }; 601 if (have_pread_but_not_preadv) { 602 // We could loop here; but proper usage of `preadv` must handle partial reads anyway. 603 // So we simply read into the first vector only. 604 if (iov.len == 0) return @as(usize, 0); 605 const first = iov[0]; 606 return pread(fd, first.iov_base[0..first.iov_len], offset); 607 } 608 if (builtin.os.tag == .wasi) { 609 var nread: usize = undefined; 610 switch (wasi.fd_pread(fd, iov.ptr, iov.len, offset, &nread)) { 611 wasi.ESUCCESS => return nread, 612 wasi.EINTR => unreachable, 613 wasi.EINVAL => unreachable, 614 wasi.EFAULT => unreachable, 615 wasi.EAGAIN => unreachable, 616 wasi.EBADF => return error.NotOpenForReading, // can be a race condition 617 wasi.EIO => return error.InputOutput, 618 wasi.EISDIR => return error.IsDir, 619 wasi.ENOBUFS => return error.SystemResources, 620 wasi.ENOMEM => return error.SystemResources, 621 wasi.ENXIO => return error.Unseekable, 622 wasi.ESPIPE => return error.Unseekable, 623 wasi.EOVERFLOW => return error.Unseekable, 624 wasi.ENOTCAPABLE => return error.AccessDenied, 625 else => |err| return unexpectedErrno(err), 626 } 627 } 628 629 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31); 630 631 while (true) { 632 const rc = system.preadv(fd, iov.ptr, iov_count, offset); 633 switch (errno(rc)) { 634 0 => return @bitCast(usize, rc), 635 EINTR => continue, 636 EINVAL => unreachable, 637 EFAULT => unreachable, 638 EAGAIN => if (std.event.Loop.instance) |loop| { 639 loop.waitUntilFdReadable(fd); 640 continue; 641 } else { 642 return error.WouldBlock; 643 }, 644 EBADF => return error.NotOpenForReading, // can be a race condition 645 EIO => return error.InputOutput, 646 EISDIR => return error.IsDir, 647 ENOBUFS => return error.SystemResources, 648 ENOMEM => return error.SystemResources, 649 ENXIO => return error.Unseekable, 650 ESPIPE => return error.Unseekable, 651 EOVERFLOW => return error.Unseekable, 652 else => |err| return unexpectedErrno(err), 653 } 654 } 655 } 656 657 pub const WriteError = error{ 658 DiskQuota, 659 FileTooBig, 660 InputOutput, 661 NoSpaceLeft, 662 663 /// In WASI, this error may occur when the file descriptor does 664 /// not hold the required rights to write to it. 665 AccessDenied, 666 BrokenPipe, 667 SystemResources, 668 OperationAborted, 669 NotOpenForWriting, 670 671 /// This error occurs when no global event loop is configured, 672 /// and reading from the file descriptor would block. 673 WouldBlock, 674 } || UnexpectedError; 675 676 /// Write to a file descriptor. 677 /// Retries when interrupted by a signal. 678 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero. 679 /// 680 /// Note that a successful write() may transfer fewer than count bytes. Such partial writes can 681 /// occur for various reasons; for example, because there was insufficient space on the disk 682 /// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or 683 /// similar was interrupted by a signal handler after it had transferred some, but before it had 684 /// transferred all of the requested bytes. In the event of a partial write, the caller can make 685 /// another write() call to transfer the remaining bytes. The subsequent call will either 686 /// transfer further bytes or may result in an error (e.g., if the disk is now full). 687 /// 688 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled 689 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. 690 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are 691 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. 692 /// 693 /// Linux has a limit on how many bytes may be transferred in one `write` call, which is `0x7ffff000` 694 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as 695 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page. 696 /// The corresponding POSIX limit is `math.maxInt(isize)`. 697 pub fn write(fd: fd_t, bytes: []const u8) WriteError!usize { 698 if (builtin.os.tag == .windows) { 699 return windows.WriteFile(fd, bytes, null, std.io.default_mode); 700 } 701 702 if (builtin.os.tag == .wasi and !builtin.link_libc) { 703 const ciovs = [_]iovec_const{iovec_const{ 704 .iov_base = bytes.ptr, 705 .iov_len = bytes.len, 706 }}; 707 var nwritten: usize = undefined; 708 switch (wasi.fd_write(fd, &ciovs, ciovs.len, &nwritten)) { 709 wasi.ESUCCESS => return nwritten, 710 wasi.EINTR => unreachable, 711 wasi.EINVAL => unreachable, 712 wasi.EFAULT => unreachable, 713 wasi.EAGAIN => unreachable, 714 wasi.EBADF => return error.NotOpenForWriting, // can be a race condition. 715 wasi.EDESTADDRREQ => unreachable, // `connect` was never called. 716 wasi.EDQUOT => return error.DiskQuota, 717 wasi.EFBIG => return error.FileTooBig, 718 wasi.EIO => return error.InputOutput, 719 wasi.ENOSPC => return error.NoSpaceLeft, 720 wasi.EPERM => return error.AccessDenied, 721 wasi.EPIPE => return error.BrokenPipe, 722 wasi.ENOTCAPABLE => return error.AccessDenied, 723 else => |err| return unexpectedErrno(err), 724 } 725 } 726 727 const max_count = switch (std.Target.current.os.tag) { 728 .linux => 0x7ffff000, 729 else => math.maxInt(isize), 730 }; 731 const adjusted_len = math.min(max_count, bytes.len); 732 733 while (true) { 734 const rc = system.write(fd, bytes.ptr, adjusted_len); 735 switch (errno(rc)) { 736 0 => return @intCast(usize, rc), 737 EINTR => continue, 738 EINVAL => unreachable, 739 EFAULT => unreachable, 740 EAGAIN => if (std.event.Loop.instance) |loop| { 741 loop.waitUntilFdWritable(fd); 742 continue; 743 } else { 744 return error.WouldBlock; 745 }, 746 EBADF => return error.NotOpenForWriting, // can be a race condition. 747 EDESTADDRREQ => unreachable, // `connect` was never called. 748 EDQUOT => return error.DiskQuota, 749 EFBIG => return error.FileTooBig, 750 EIO => return error.InputOutput, 751 ENOSPC => return error.NoSpaceLeft, 752 EPERM => return error.AccessDenied, 753 EPIPE => return error.BrokenPipe, 754 else => |err| return unexpectedErrno(err), 755 } 756 } 757 } 758 759 /// Write multiple buffers to a file descriptor. 760 /// Retries when interrupted by a signal. 761 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero. 762 /// 763 /// Note that a successful write() may transfer fewer bytes than supplied. Such partial writes can 764 /// occur for various reasons; for example, because there was insufficient space on the disk 765 /// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or 766 /// similar was interrupted by a signal handler after it had transferred some, but before it had 767 /// transferred all of the requested bytes. In the event of a partial write, the caller can make 768 /// another write() call to transfer the remaining bytes. The subsequent call will either 769 /// transfer further bytes or may result in an error (e.g., if the disk is now full). 770 /// 771 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled 772 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. 773 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are 774 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. 775 /// 776 /// If `iov.len` is larger than will fit in a `u31`, a partial write will occur. 777 pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!usize { 778 if (std.Target.current.os.tag == .windows) { 779 // TODO improve this to use WriteFileScatter 780 if (iov.len == 0) return @as(usize, 0); 781 const first = iov[0]; 782 return write(fd, first.iov_base[0..first.iov_len]); 783 } 784 if (builtin.os.tag == .wasi) { 785 var nwritten: usize = undefined; 786 switch (wasi.fd_write(fd, iov.ptr, iov.len, &nwritten)) { 787 wasi.ESUCCESS => return nwritten, 788 wasi.EINTR => unreachable, 789 wasi.EINVAL => unreachable, 790 wasi.EFAULT => unreachable, 791 wasi.EAGAIN => unreachable, 792 wasi.EBADF => return error.NotOpenForWriting, // can be a race condition. 793 wasi.EDESTADDRREQ => unreachable, // `connect` was never called. 794 wasi.EDQUOT => return error.DiskQuota, 795 wasi.EFBIG => return error.FileTooBig, 796 wasi.EIO => return error.InputOutput, 797 wasi.ENOSPC => return error.NoSpaceLeft, 798 wasi.EPERM => return error.AccessDenied, 799 wasi.EPIPE => return error.BrokenPipe, 800 wasi.ENOTCAPABLE => return error.AccessDenied, 801 else => |err| return unexpectedErrno(err), 802 } 803 } 804 805 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31); 806 while (true) { 807 const rc = system.writev(fd, iov.ptr, iov_count); 808 switch (errno(rc)) { 809 0 => return @intCast(usize, rc), 810 EINTR => continue, 811 EINVAL => unreachable, 812 EFAULT => unreachable, 813 EAGAIN => if (std.event.Loop.instance) |loop| { 814 loop.waitUntilFdWritable(fd); 815 continue; 816 } else { 817 return error.WouldBlock; 818 }, 819 EBADF => return error.NotOpenForWriting, // Can be a race condition. 820 EDESTADDRREQ => unreachable, // `connect` was never called. 821 EDQUOT => return error.DiskQuota, 822 EFBIG => return error.FileTooBig, 823 EIO => return error.InputOutput, 824 ENOSPC => return error.NoSpaceLeft, 825 EPERM => return error.AccessDenied, 826 EPIPE => return error.BrokenPipe, 827 else => |err| return unexpectedErrno(err), 828 } 829 } 830 } 831 832 pub const PWriteError = WriteError || error{Unseekable}; 833 834 /// Write to a file descriptor, with a position offset. 835 /// Retries when interrupted by a signal. 836 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero. 837 /// 838 /// Note that a successful write() may transfer fewer bytes than supplied. Such partial writes can 839 /// occur for various reasons; for example, because there was insufficient space on the disk 840 /// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or 841 /// similar was interrupted by a signal handler after it had transferred some, but before it had 842 /// transferred all of the requested bytes. In the event of a partial write, the caller can make 843 /// another write() call to transfer the remaining bytes. The subsequent call will either 844 /// transfer further bytes or may result in an error (e.g., if the disk is now full). 845 /// 846 /// For POSIX systems, if the application has a global event loop enabled, EAGAIN is handled 847 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. 848 /// On Windows, if the application has a global event loop enabled, I/O Completion Ports are 849 /// used to perform the I/O. `error.WouldBlock` is not possible on Windows. 850 /// 851 /// Linux has a limit on how many bytes may be transferred in one `pwrite` call, which is `0x7ffff000` 852 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as 853 /// well as stuffing the errno codes into the last `4096` values. This is noted on the `write` man page. 854 /// The corresponding POSIX limit is `math.maxInt(isize)`. 855 pub fn pwrite(fd: fd_t, bytes: []const u8, offset: u64) PWriteError!usize { 856 if (std.Target.current.os.tag == .windows) { 857 return windows.WriteFile(fd, bytes, offset, std.io.default_mode); 858 } 859 if (builtin.os.tag == .wasi) { 860 const ciovs = [1]iovec_const{iovec_const{ 861 .iov_base = bytes.ptr, 862 .iov_len = bytes.len, 863 }}; 864 865 var nwritten: usize = undefined; 866 switch (wasi.fd_pwrite(fd, &ciovs, ciovs.len, offset, &nwritten)) { 867 wasi.ESUCCESS => return nwritten, 868 wasi.EINTR => unreachable, 869 wasi.EINVAL => unreachable, 870 wasi.EFAULT => unreachable, 871 wasi.EAGAIN => unreachable, 872 wasi.EBADF => return error.NotOpenForWriting, // can be a race condition. 873 wasi.EDESTADDRREQ => unreachable, // `connect` was never called. 874 wasi.EDQUOT => return error.DiskQuota, 875 wasi.EFBIG => return error.FileTooBig, 876 wasi.EIO => return error.InputOutput, 877 wasi.ENOSPC => return error.NoSpaceLeft, 878 wasi.EPERM => return error.AccessDenied, 879 wasi.EPIPE => return error.BrokenPipe, 880 wasi.ENXIO => return error.Unseekable, 881 wasi.ESPIPE => return error.Unseekable, 882 wasi.EOVERFLOW => return error.Unseekable, 883 wasi.ENOTCAPABLE => return error.AccessDenied, 884 else => |err| return unexpectedErrno(err), 885 } 886 } 887 888 // Prevent EINVAL. 889 const max_count = switch (std.Target.current.os.tag) { 890 .linux => 0x7ffff000, 891 else => math.maxInt(isize), 892 }; 893 const adjusted_len = math.min(max_count, bytes.len); 894 895 while (true) { 896 const rc = system.pwrite(fd, bytes.ptr, adjusted_len, offset); 897 switch (errno(rc)) { 898 0 => return @intCast(usize, rc), 899 EINTR => continue, 900 EINVAL => unreachable, 901 EFAULT => unreachable, 902 EAGAIN => if (std.event.Loop.instance) |loop| { 903 loop.waitUntilFdWritable(fd); 904 continue; 905 } else { 906 return error.WouldBlock; 907 }, 908 EBADF => return error.NotOpenForWriting, // Can be a race condition. 909 EDESTADDRREQ => unreachable, // `connect` was never called. 910 EDQUOT => return error.DiskQuota, 911 EFBIG => return error.FileTooBig, 912 EIO => return error.InputOutput, 913 ENOSPC => return error.NoSpaceLeft, 914 EPERM => return error.AccessDenied, 915 EPIPE => return error.BrokenPipe, 916 ENXIO => return error.Unseekable, 917 ESPIPE => return error.Unseekable, 918 EOVERFLOW => return error.Unseekable, 919 else => |err| return unexpectedErrno(err), 920 } 921 } 922 } 923 924 /// Write multiple buffers to a file descriptor, with a position offset. 925 /// Retries when interrupted by a signal. 926 /// Returns the number of bytes written. If nonzero bytes were supplied, this will be nonzero. 927 /// 928 /// Note that a successful write() may transfer fewer than count bytes. Such partial writes can 929 /// occur for various reasons; for example, because there was insufficient space on the disk 930 /// device to write all of the requested bytes, or because a blocked write() to a socket, pipe, or 931 /// similar was interrupted by a signal handler after it had transferred some, but before it had 932 /// transferred all of the requested bytes. In the event of a partial write, the caller can make 933 /// another write() call to transfer the remaining bytes. The subsequent call will either 934 /// transfer further bytes or may result in an error (e.g., if the disk is now full). 935 /// 936 /// If the application has a global event loop enabled, EAGAIN is handled 937 /// via the event loop. Otherwise EAGAIN results in `error.WouldBlock`. 938 /// 939 /// The following systems do not have this syscall, and will return partial writes if more than one 940 /// vector is provided: 941 /// * Darwin 942 /// * Windows 943 /// 944 /// If `iov.len` is larger than will fit in a `u31`, a partial write will occur. 945 pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) PWriteError!usize { 946 const have_pwrite_but_not_pwritev = switch (std.Target.current.os.tag) { 947 .windows, .macosx, .ios, .watchos, .tvos => true, 948 else => false, 949 }; 950 951 if (have_pwrite_but_not_pwritev) { 952 // We could loop here; but proper usage of `pwritev` must handle partial writes anyway. 953 // So we simply write the first vector only. 954 if (iov.len == 0) return @as(usize, 0); 955 const first = iov[0]; 956 return pwrite(fd, first.iov_base[0..first.iov_len], offset); 957 } 958 if (builtin.os.tag == .wasi) { 959 var nwritten: usize = undefined; 960 switch (wasi.fd_pwrite(fd, iov.ptr, iov.len, offset, &nwritten)) { 961 wasi.ESUCCESS => return nwritten, 962 wasi.EINTR => unreachable, 963 wasi.EINVAL => unreachable, 964 wasi.EFAULT => unreachable, 965 wasi.EAGAIN => unreachable, 966 wasi.EBADF => return error.NotOpenForWriting, // Can be a race condition. 967 wasi.EDESTADDRREQ => unreachable, // `connect` was never called. 968 wasi.EDQUOT => return error.DiskQuota, 969 wasi.EFBIG => return error.FileTooBig, 970 wasi.EIO => return error.InputOutput, 971 wasi.ENOSPC => return error.NoSpaceLeft, 972 wasi.EPERM => return error.AccessDenied, 973 wasi.EPIPE => return error.BrokenPipe, 974 wasi.ENXIO => return error.Unseekable, 975 wasi.ESPIPE => return error.Unseekable, 976 wasi.EOVERFLOW => return error.Unseekable, 977 wasi.ENOTCAPABLE => return error.AccessDenied, 978 else => |err| return unexpectedErrno(err), 979 } 980 } 981 982 const iov_count = math.cast(u31, iov.len) catch math.maxInt(u31); 983 while (true) { 984 const rc = system.pwritev(fd, iov.ptr, iov_count, offset); 985 switch (errno(rc)) { 986 0 => return @intCast(usize, rc), 987 EINTR => continue, 988 EINVAL => unreachable, 989 EFAULT => unreachable, 990 EAGAIN => if (std.event.Loop.instance) |loop| { 991 loop.waitUntilFdWritable(fd); 992 continue; 993 } else { 994 return error.WouldBlock; 995 }, 996 EBADF => return error.NotOpenForWriting, // Can be a race condition. 997 EDESTADDRREQ => unreachable, // `connect` was never called. 998 EDQUOT => return error.DiskQuota, 999 EFBIG => return error.FileTooBig, 1000 EIO => return error.InputOutput, 1001 ENOSPC => return error.NoSpaceLeft, 1002 EPERM => return error.AccessDenied, 1003 EPIPE => return error.BrokenPipe, 1004 ENXIO => return error.Unseekable, 1005 ESPIPE => return error.Unseekable, 1006 EOVERFLOW => return error.Unseekable, 1007 else => |err| return unexpectedErrno(err), 1008 } 1009 } 1010 } 1011 1012 pub const OpenError = error{ 1013 /// In WASI, this error may occur when the file descriptor does 1014 /// not hold the required rights to open a new resource relative to it. 1015 AccessDenied, 1016 SymLinkLoop, 1017 ProcessFdQuotaExceeded, 1018 SystemFdQuotaExceeded, 1019 NoDevice, 1020 FileNotFound, 1021 1022 /// The path exceeded `MAX_PATH_BYTES` bytes. 1023 NameTooLong, 1024 1025 /// Insufficient kernel memory was available, or 1026 /// the named file is a FIFO and per-user hard limit on 1027 /// memory allocation for pipes has been reached. 1028 SystemResources, 1029 1030 /// The file is too large to be opened. This error is unreachable 1031 /// for 64-bit targets, as well as when opening directories. 1032 FileTooBig, 1033 1034 /// The path refers to directory but the `O_DIRECTORY` flag was not provided. 1035 IsDir, 1036 1037 /// A new path cannot be created because the device has no room for the new file. 1038 /// This error is only reachable when the `O_CREAT` flag is provided. 1039 NoSpaceLeft, 1040 1041 /// A component used as a directory in the path was not, in fact, a directory, or 1042 /// `O_DIRECTORY` was specified and the path was not a directory. 1043 NotDir, 1044 1045 /// The path already exists and the `O_CREAT` and `O_EXCL` flags were provided. 1046 PathAlreadyExists, 1047 DeviceBusy, 1048 1049 /// The underlying filesystem does not support file locks 1050 FileLocksNotSupported, 1051 1052 BadPathName, 1053 InvalidUtf8, 1054 } || UnexpectedError; 1055 1056 /// Open and possibly create a file. Keeps trying if it gets interrupted. 1057 /// See also `openC`. 1058 pub fn open(file_path: []const u8, flags: u32, perm: mode_t) OpenError!fd_t { 1059 if (std.Target.current.os.tag == .windows) { 1060 const file_path_w = try windows.sliceToPrefixedFileW(file_path); 1061 return openW(file_path_w.span(), flags, perm); 1062 } 1063 const file_path_c = try toPosixPath(file_path); 1064 return openZ(&file_path_c, flags, perm); 1065 } 1066 1067 pub const openC = @compileError("deprecated: renamed to openZ"); 1068 1069 /// Open and possibly create a file. Keeps trying if it gets interrupted. 1070 /// See also `open`. 1071 pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t { 1072 if (std.Target.current.os.tag == .windows) { 1073 const file_path_w = try windows.cStrToPrefixedFileW(file_path); 1074 return openW(file_path_w.span(), flags, perm); 1075 } 1076 while (true) { 1077 const rc = system.open(file_path, flags, perm); 1078 switch (errno(rc)) { 1079 0 => return @intCast(fd_t, rc), 1080 EINTR => continue, 1081 1082 EFAULT => unreachable, 1083 EINVAL => unreachable, 1084 EACCES => return error.AccessDenied, 1085 EFBIG => return error.FileTooBig, 1086 EOVERFLOW => return error.FileTooBig, 1087 EISDIR => return error.IsDir, 1088 ELOOP => return error.SymLinkLoop, 1089 EMFILE => return error.ProcessFdQuotaExceeded, 1090 ENAMETOOLONG => return error.NameTooLong, 1091 ENFILE => return error.SystemFdQuotaExceeded, 1092 ENODEV => return error.NoDevice, 1093 ENOENT => return error.FileNotFound, 1094 ENOMEM => return error.SystemResources, 1095 ENOSPC => return error.NoSpaceLeft, 1096 ENOTDIR => return error.NotDir, 1097 EPERM => return error.AccessDenied, 1098 EEXIST => return error.PathAlreadyExists, 1099 EBUSY => return error.DeviceBusy, 1100 else => |err| return unexpectedErrno(err), 1101 } 1102 } 1103 } 1104 1105 fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions { 1106 const w = windows; 1107 1108 var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE; 1109 if (flags & O_RDWR != 0) { 1110 access_mask |= w.GENERIC_READ | w.GENERIC_WRITE; 1111 } else if (flags & O_WRONLY != 0) { 1112 access_mask |= w.GENERIC_WRITE; 1113 } else { 1114 access_mask |= w.GENERIC_READ | w.GENERIC_WRITE; 1115 } 1116 1117 const open_dir: bool = flags & O_DIRECTORY != 0; 1118 const follow_symlinks: bool = flags & O_NOFOLLOW == 0; 1119 1120 const creation: w.ULONG = blk: { 1121 if (flags & O_CREAT != 0) { 1122 if (flags & O_EXCL != 0) { 1123 break :blk w.FILE_CREATE; 1124 } 1125 } 1126 break :blk w.FILE_OPEN; 1127 }; 1128 1129 return .{ 1130 .access_mask = access_mask, 1131 .io_mode = .blocking, 1132 .creation = creation, 1133 .open_dir = open_dir, 1134 .follow_symlinks = follow_symlinks, 1135 }; 1136 } 1137 1138 /// Windows-only. The path parameter is 1139 /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. 1140 /// Translates the POSIX open API call to a Windows API call. 1141 /// TODO currently, this function does not handle all flag combinations 1142 /// or makes use of perm argument. 1143 pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t { 1144 var options = openOptionsFromFlags(flags); 1145 options.dir = std.fs.cwd().fd; 1146 return windows.OpenFile(file_path_w, options) catch |err| switch (err) { 1147 error.WouldBlock => unreachable, 1148 error.PipeBusy => unreachable, 1149 else => |e| return e, 1150 }; 1151 } 1152 1153 /// Open and possibly create a file. Keeps trying if it gets interrupted. 1154 /// `file_path` is relative to the open directory handle `dir_fd`. 1155 /// See also `openatC`. 1156 pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t { 1157 if (builtin.os.tag == .wasi) { 1158 @compileError("use openatWasi instead"); 1159 } 1160 if (builtin.os.tag == .windows) { 1161 const file_path_w = try windows.sliceToPrefixedFileW(file_path); 1162 return openatW(dir_fd, file_path_w.span(), flags, mode); 1163 } 1164 const file_path_c = try toPosixPath(file_path); 1165 return openatZ(dir_fd, &file_path_c, flags, mode); 1166 } 1167 1168 /// Open and possibly create a file in WASI. 1169 pub fn openatWasi(dir_fd: fd_t, file_path: []const u8, lookup_flags: lookupflags_t, oflags: oflags_t, fdflags: fdflags_t, base: rights_t, inheriting: rights_t) OpenError!fd_t { 1170 while (true) { 1171 var fd: fd_t = undefined; 1172 switch (wasi.path_open(dir_fd, lookup_flags, file_path.ptr, file_path.len, oflags, base, inheriting, fdflags, &fd)) { 1173 wasi.ESUCCESS => return fd, 1174 wasi.EINTR => continue, 1175 1176 wasi.EFAULT => unreachable, 1177 wasi.EINVAL => unreachable, 1178 wasi.EACCES => return error.AccessDenied, 1179 wasi.EFBIG => return error.FileTooBig, 1180 wasi.EOVERFLOW => return error.FileTooBig, 1181 wasi.EISDIR => return error.IsDir, 1182 wasi.ELOOP => return error.SymLinkLoop, 1183 wasi.EMFILE => return error.ProcessFdQuotaExceeded, 1184 wasi.ENAMETOOLONG => return error.NameTooLong, 1185 wasi.ENFILE => return error.SystemFdQuotaExceeded, 1186 wasi.ENODEV => return error.NoDevice, 1187 wasi.ENOENT => return error.FileNotFound, 1188 wasi.ENOMEM => return error.SystemResources, 1189 wasi.ENOSPC => return error.NoSpaceLeft, 1190 wasi.ENOTDIR => return error.NotDir, 1191 wasi.EPERM => return error.AccessDenied, 1192 wasi.EEXIST => return error.PathAlreadyExists, 1193 wasi.EBUSY => return error.DeviceBusy, 1194 wasi.ENOTCAPABLE => return error.AccessDenied, 1195 else => |err| return unexpectedErrno(err), 1196 } 1197 } 1198 } 1199 1200 pub const openatC = @compileError("deprecated: renamed to openatZ"); 1201 1202 /// Open and possibly create a file. Keeps trying if it gets interrupted. 1203 /// `file_path` is relative to the open directory handle `dir_fd`. 1204 /// See also `openat`. 1205 pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t { 1206 if (builtin.os.tag == .windows) { 1207 const file_path_w = try windows.cStrToPrefixedFileW(file_path); 1208 return openatW(dir_fd, file_path_w.span(), flags, mode); 1209 } 1210 while (true) { 1211 const rc = system.openat(dir_fd, file_path, flags, mode); 1212 switch (errno(rc)) { 1213 0 => return @intCast(fd_t, rc), 1214 EINTR => continue, 1215 1216 EFAULT => unreachable, 1217 EINVAL => unreachable, 1218 EACCES => return error.AccessDenied, 1219 EFBIG => return error.FileTooBig, 1220 EOVERFLOW => return error.FileTooBig, 1221 EISDIR => return error.IsDir, 1222 ELOOP => return error.SymLinkLoop, 1223 EMFILE => return error.ProcessFdQuotaExceeded, 1224 ENAMETOOLONG => return error.NameTooLong, 1225 ENFILE => return error.SystemFdQuotaExceeded, 1226 ENODEV => return error.NoDevice, 1227 ENOENT => return error.FileNotFound, 1228 ENOMEM => return error.SystemResources, 1229 ENOSPC => return error.NoSpaceLeft, 1230 ENOTDIR => return error.NotDir, 1231 EPERM => return error.AccessDenied, 1232 EEXIST => return error.PathAlreadyExists, 1233 EBUSY => return error.DeviceBusy, 1234 EOPNOTSUPP => return error.FileLocksNotSupported, 1235 else => |err| return unexpectedErrno(err), 1236 } 1237 } 1238 } 1239 1240 /// Windows-only. Similar to `openat` but with pathname argument null-terminated 1241 /// WTF16 encoded. 1242 /// TODO currently, this function does not handle all flag combinations 1243 /// or makes use of perm argument. 1244 pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t { 1245 var options = openOptionsFromFlags(flags); 1246 options.dir = dir_fd; 1247 return windows.OpenFile(file_path_w, options) catch |err| switch (err) { 1248 error.WouldBlock => unreachable, 1249 error.PipeBusy => unreachable, 1250 else => |e| return e, 1251 }; 1252 } 1253 1254 pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void { 1255 while (true) { 1256 switch (errno(system.dup2(old_fd, new_fd))) { 1257 0 => return, 1258 EBUSY, EINTR => continue, 1259 EMFILE => return error.ProcessFdQuotaExceeded, 1260 EINVAL => unreachable, // invalid parameters passed to dup2 1261 EBADF => unreachable, // invalid file descriptor 1262 else => |err| return unexpectedErrno(err), 1263 } 1264 } 1265 } 1266 1267 pub const ExecveError = error{ 1268 SystemResources, 1269 AccessDenied, 1270 InvalidExe, 1271 FileSystem, 1272 IsDir, 1273 FileNotFound, 1274 NotDir, 1275 FileBusy, 1276 ProcessFdQuotaExceeded, 1277 SystemFdQuotaExceeded, 1278 NameTooLong, 1279 } || UnexpectedError; 1280 1281 pub const execveC = @compileError("deprecated: use execveZ"); 1282 1283 /// Like `execve` except the parameters are null-terminated, 1284 /// matching the syscall API on all targets. This removes the need for an allocator. 1285 /// This function ignores PATH environment variable. See `execvpeZ` for that. 1286 pub fn execveZ( 1287 path: [*:0]const u8, 1288 child_argv: [*:null]const ?[*:0]const u8, 1289 envp: [*:null]const ?[*:0]const u8, 1290 ) ExecveError { 1291 switch (errno(system.execve(path, child_argv, envp))) { 1292 0 => unreachable, 1293 EFAULT => unreachable, 1294 E2BIG => return error.SystemResources, 1295 EMFILE => return error.ProcessFdQuotaExceeded, 1296 ENAMETOOLONG => return error.NameTooLong, 1297 ENFILE => return error.SystemFdQuotaExceeded, 1298 ENOMEM => return error.SystemResources, 1299 EACCES => return error.AccessDenied, 1300 EPERM => return error.AccessDenied, 1301 EINVAL => return error.InvalidExe, 1302 ENOEXEC => return error.InvalidExe, 1303 EIO => return error.FileSystem, 1304 ELOOP => return error.FileSystem, 1305 EISDIR => return error.IsDir, 1306 ENOENT => return error.FileNotFound, 1307 ENOTDIR => return error.NotDir, 1308 ETXTBSY => return error.FileBusy, 1309 else => |err| return unexpectedErrno(err), 1310 } 1311 } 1312 1313 pub const execvpeC = @compileError("deprecated in favor of execvpeZ"); 1314 1315 pub const Arg0Expand = enum { 1316 expand, 1317 no_expand, 1318 }; 1319 1320 /// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable, 1321 /// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall. 1322 /// If this function returns with an error, `argv[0]` will be restored to the value it was when it was passed in. 1323 pub fn execvpeZ_expandArg0( 1324 comptime arg0_expand: Arg0Expand, 1325 file: [*:0]const u8, 1326 child_argv: switch (arg0_expand) { 1327 .expand => [*:null]?[*:0]const u8, 1328 .no_expand => [*:null]const ?[*:0]const u8, 1329 }, 1330 envp: [*:null]const ?[*:0]const u8, 1331 ) ExecveError { 1332 const file_slice = mem.spanZ(file); 1333 if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveZ(file, child_argv, envp); 1334 1335 const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; 1336 // Use of MAX_PATH_BYTES here is valid as the path_buf will be passed 1337 // directly to the operating system in execveZ. 1338 var path_buf: [MAX_PATH_BYTES]u8 = undefined; 1339 var it = mem.tokenize(PATH, ":"); 1340 var seen_eacces = false; 1341 var err: ExecveError = undefined; 1342 1343 // In case of expanding arg0 we must put it back if we return with an error. 1344 const prev_arg0 = child_argv[0]; 1345 defer switch (arg0_expand) { 1346 .expand => child_argv[0] = prev_arg0, 1347 .no_expand => {}, 1348 }; 1349 1350 while (it.next()) |search_path| { 1351 if (path_buf.len < search_path.len + file_slice.len + 1) return error.NameTooLong; 1352 mem.copy(u8, &path_buf, search_path); 1353 path_buf[search_path.len] = '/'; 1354 mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); 1355 const path_len = search_path.len + file_slice.len + 1; 1356 path_buf[path_len] = 0; 1357 const full_path = path_buf[0..path_len :0].ptr; 1358 switch (arg0_expand) { 1359 .expand => child_argv[0] = full_path, 1360 .no_expand => {}, 1361 } 1362 err = execveZ(full_path, child_argv, envp); 1363 switch (err) { 1364 error.AccessDenied => seen_eacces = true, 1365 error.FileNotFound, error.NotDir => {}, 1366 else => |e| return e, 1367 } 1368 } 1369 if (seen_eacces) return error.AccessDenied; 1370 return err; 1371 } 1372 1373 /// Like `execvpe` except the parameters are null-terminated, 1374 /// matching the syscall API on all targets. This removes the need for an allocator. 1375 /// This function also uses the PATH environment variable to get the full path to the executable. 1376 /// If `file` is an absolute path, this is the same as `execveZ`. 1377 pub fn execvpeZ( 1378 file: [*:0]const u8, 1379 argv: [*:null]const ?[*:0]const u8, 1380 envp: [*:null]const ?[*:0]const u8, 1381 ) ExecveError { 1382 return execvpeZ_expandArg0(.no_expand, file, argv, envp); 1383 } 1384 1385 /// This is the same as `execvpe` except if the `arg0_expand` parameter is set to `.expand`, 1386 /// then argv[0] will be replaced with the expanded version of it, after resolving in accordance 1387 /// with the PATH environment variable. 1388 pub fn execvpe_expandArg0( 1389 allocator: *mem.Allocator, 1390 arg0_expand: Arg0Expand, 1391 argv_slice: []const []const u8, 1392 env_map: *const std.BufMap, 1393 ) (ExecveError || error{OutOfMemory}) { 1394 const argv_buf = try allocator.alloc(?[*:0]u8, argv_slice.len + 1); 1395 mem.set(?[*:0]u8, argv_buf, null); 1396 defer { 1397 for (argv_buf) |arg| { 1398 const arg_buf = mem.spanZ(arg) orelse break; 1399 allocator.free(arg_buf); 1400 } 1401 allocator.free(argv_buf); 1402 } 1403 for (argv_slice) |arg, i| { 1404 const arg_buf = try allocator.alloc(u8, arg.len + 1); 1405 @memcpy(arg_buf.ptr, arg.ptr, arg.len); 1406 arg_buf[arg.len] = 0; 1407 argv_buf[i] = arg_buf[0..arg.len :0].ptr; 1408 } 1409 argv_buf[argv_slice.len] = null; 1410 const argv_ptr = argv_buf[0..argv_slice.len :null].ptr; 1411 1412 const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); 1413 defer freeNullDelimitedEnvMap(allocator, envp_buf); 1414 1415 switch (arg0_expand) { 1416 .expand => return execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr), 1417 .no_expand => return execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr), 1418 } 1419 } 1420 1421 /// This function must allocate memory to add a null terminating bytes on path and each arg. 1422 /// It must also convert to KEY=VALUE\0 format for environment variables, and include null 1423 /// pointers after the args and after the environment variables. 1424 /// `argv_slice[0]` is the executable path. 1425 /// This function also uses the PATH environment variable to get the full path to the executable. 1426 pub fn execvpe( 1427 allocator: *mem.Allocator, 1428 argv_slice: []const []const u8, 1429 env_map: *const std.BufMap, 1430 ) (ExecveError || error{OutOfMemory}) { 1431 return execvpe_expandArg0(allocator, .no_expand, argv_slice, env_map); 1432 } 1433 1434 pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![:null]?[*:0]u8 { 1435 const envp_count = env_map.count(); 1436 const envp_buf = try allocator.alloc(?[*:0]u8, envp_count + 1); 1437 mem.set(?[*:0]u8, envp_buf, null); 1438 errdefer freeNullDelimitedEnvMap(allocator, envp_buf); 1439 { 1440 var it = env_map.iterator(); 1441 var i: usize = 0; 1442 while (it.next()) |pair| : (i += 1) { 1443 const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); 1444 @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); 1445 env_buf[pair.key.len] = '='; 1446 @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); 1447 const len = env_buf.len - 1; 1448 env_buf[len] = 0; 1449 envp_buf[i] = env_buf[0..len :0].ptr; 1450 } 1451 assert(i == envp_count); 1452 } 1453 return envp_buf[0..envp_count :null]; 1454 } 1455 1456 pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void { 1457 for (envp_buf) |env| { 1458 const env_buf = if (env) |ptr| ptr[0 .. mem.len(ptr) + 1] else break; 1459 allocator.free(env_buf); 1460 } 1461 allocator.free(envp_buf); 1462 } 1463 1464 /// Get an environment variable. 1465 /// See also `getenvZ`. 1466 pub fn getenv(key: []const u8) ?[]const u8 { 1467 if (builtin.link_libc) { 1468 var small_key_buf: [64]u8 = undefined; 1469 if (key.len < small_key_buf.len) { 1470 mem.copy(u8, &small_key_buf, key); 1471 small_key_buf[key.len] = 0; 1472 const key0 = small_key_buf[0..key.len :0]; 1473 return getenvZ(key0); 1474 } 1475 // Search the entire `environ` because we don't have a null terminated pointer. 1476 var ptr = std.c.environ; 1477 while (ptr.*) |line| : (ptr += 1) { 1478 var line_i: usize = 0; 1479 while (line[line_i] != 0 and line[line_i] != '=') : (line_i += 1) {} 1480 const this_key = line[0..line_i]; 1481 1482 if (!mem.eql(u8, this_key, key)) continue; 1483 1484 var end_i: usize = line_i; 1485 while (line[end_i] != 0) : (end_i += 1) {} 1486 const value = line[line_i + 1 .. end_i]; 1487 1488 return value; 1489 } 1490 return null; 1491 } 1492 if (builtin.os.tag == .windows) { 1493 @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."); 1494 } 1495 // TODO see https://github.com/ziglang/zig/issues/4524 1496 for (environ) |ptr| { 1497 var line_i: usize = 0; 1498 while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} 1499 const this_key = ptr[0..line_i]; 1500 if (!mem.eql(u8, key, this_key)) continue; 1501 1502 var end_i: usize = line_i; 1503 while (ptr[end_i] != 0) : (end_i += 1) {} 1504 const this_value = ptr[line_i + 1 .. end_i]; 1505 1506 return this_value; 1507 } 1508 return null; 1509 } 1510 1511 pub const getenvC = @compileError("Deprecated in favor of `getenvZ`"); 1512 1513 /// Get an environment variable with a null-terminated name. 1514 /// See also `getenv`. 1515 pub fn getenvZ(key: [*:0]const u8) ?[]const u8 { 1516 if (builtin.link_libc) { 1517 const value = system.getenv(key) orelse return null; 1518 return mem.spanZ(value); 1519 } 1520 if (builtin.os.tag == .windows) { 1521 @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."); 1522 } 1523 return getenv(mem.spanZ(key)); 1524 } 1525 1526 /// Windows-only. Get an environment variable with a null-terminated, WTF-16 encoded name. 1527 /// See also `getenv`. 1528 /// This function first attempts a case-sensitive lookup. If no match is found, and `key` 1529 /// is ASCII, then it attempts a second case-insensitive lookup. 1530 pub fn getenvW(key: [*:0]const u16) ?[:0]const u16 { 1531 if (builtin.os.tag != .windows) { 1532 @compileError("std.os.getenvW is a Windows-only API"); 1533 } 1534 const key_slice = mem.spanZ(key); 1535 const ptr = windows.peb().ProcessParameters.Environment; 1536 var ascii_match: ?[:0]const u16 = null; 1537 var i: usize = 0; 1538 while (ptr[i] != 0) { 1539 const key_start = i; 1540 1541 while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} 1542 const this_key = ptr[key_start..i]; 1543 1544 if (ptr[i] == '=') i += 1; 1545 1546 const value_start = i; 1547 while (ptr[i] != 0) : (i += 1) {} 1548 const this_value = ptr[value_start..i :0]; 1549 1550 if (mem.eql(u16, key_slice, this_key)) return this_value; 1551 1552 ascii_check: { 1553 if (ascii_match != null) break :ascii_check; 1554 if (key_slice.len != this_key.len) break :ascii_check; 1555 for (key_slice) |a_c, key_index| { 1556 const a = math.cast(u8, a_c) catch break :ascii_check; 1557 const b = math.cast(u8, this_key[key_index]) catch break :ascii_check; 1558 if (std.ascii.toLower(a) != std.ascii.toLower(b)) break :ascii_check; 1559 } 1560 ascii_match = this_value; 1561 } 1562 1563 i += 1; // skip over null byte 1564 } 1565 return ascii_match; 1566 } 1567 1568 pub const GetCwdError = error{ 1569 NameTooLong, 1570 CurrentWorkingDirectoryUnlinked, 1571 } || UnexpectedError; 1572 1573 /// The result is a slice of out_buffer, indexed from 0. 1574 pub fn getcwd(out_buffer: []u8) GetCwdError![]u8 { 1575 if (builtin.os.tag == .windows) { 1576 return windows.GetCurrentDirectory(out_buffer); 1577 } 1578 if (builtin.os.tag == .wasi) { 1579 @compileError("WASI doesn't have a concept of cwd(); use std.fs.wasi.PreopenList to get available Dir handles instead"); 1580 } 1581 1582 const err = if (builtin.link_libc) blk: { 1583 break :blk if (std.c.getcwd(out_buffer.ptr, out_buffer.len)) |_| 0 else std.c._errno().*; 1584 } else blk: { 1585 break :blk errno(system.getcwd(out_buffer.ptr, out_buffer.len)); 1586 }; 1587 switch (err) { 1588 0 => return mem.spanZ(@ptrCast([*:0]u8, out_buffer.ptr)), 1589 EFAULT => unreachable, 1590 EINVAL => unreachable, 1591 ENOENT => return error.CurrentWorkingDirectoryUnlinked, 1592 ERANGE => return error.NameTooLong, 1593 else => return unexpectedErrno(@intCast(usize, err)), 1594 } 1595 } 1596 1597 pub const SymLinkError = error{ 1598 /// In WASI, this error may occur when the file descriptor does 1599 /// not hold the required rights to create a new symbolic link relative to it. 1600 AccessDenied, 1601 DiskQuota, 1602 PathAlreadyExists, 1603 FileSystem, 1604 SymLinkLoop, 1605 FileNotFound, 1606 SystemResources, 1607 NoSpaceLeft, 1608 ReadOnlyFileSystem, 1609 NotDir, 1610 NameTooLong, 1611 InvalidUtf8, 1612 BadPathName, 1613 } || UnexpectedError; 1614 1615 /// Creates a symbolic link named `sym_link_path` which contains the string `target_path`. 1616 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent 1617 /// one; the latter case is known as a dangling link. 1618 /// If `sym_link_path` exists, it will not be overwritten. 1619 /// See also `symlinkZ. 1620 pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void { 1621 if (builtin.os.tag == .wasi) { 1622 @compileError("symlink is not supported in WASI; use symlinkat instead"); 1623 } 1624 if (builtin.os.tag == .windows) { 1625 @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); 1626 } 1627 const target_path_c = try toPosixPath(target_path); 1628 const sym_link_path_c = try toPosixPath(sym_link_path); 1629 return symlinkZ(&target_path_c, &sym_link_path_c); 1630 } 1631 1632 pub const symlinkC = @compileError("deprecated: renamed to symlinkZ"); 1633 1634 /// This is the same as `symlink` except the parameters are null-terminated pointers. 1635 /// See also `symlink`. 1636 pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLinkError!void { 1637 if (builtin.os.tag == .windows) { 1638 @compileError("symlink is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); 1639 } 1640 switch (errno(system.symlink(target_path, sym_link_path))) { 1641 0 => return, 1642 EFAULT => unreachable, 1643 EINVAL => unreachable, 1644 EACCES => return error.AccessDenied, 1645 EPERM => return error.AccessDenied, 1646 EDQUOT => return error.DiskQuota, 1647 EEXIST => return error.PathAlreadyExists, 1648 EIO => return error.FileSystem, 1649 ELOOP => return error.SymLinkLoop, 1650 ENAMETOOLONG => return error.NameTooLong, 1651 ENOENT => return error.FileNotFound, 1652 ENOTDIR => return error.NotDir, 1653 ENOMEM => return error.SystemResources, 1654 ENOSPC => return error.NoSpaceLeft, 1655 EROFS => return error.ReadOnlyFileSystem, 1656 else => |err| return unexpectedErrno(err), 1657 } 1658 } 1659 1660 /// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string 1661 /// `target_path` **relative** to `newdirfd` directory handle. 1662 /// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent 1663 /// one; the latter case is known as a dangling link. 1664 /// If `sym_link_path` exists, it will not be overwritten. 1665 /// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`. 1666 pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { 1667 if (builtin.os.tag == .wasi) { 1668 return symlinkatWasi(target_path, newdirfd, sym_link_path); 1669 } 1670 if (builtin.os.tag == .windows) { 1671 @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); 1672 } 1673 const target_path_c = try toPosixPath(target_path); 1674 const sym_link_path_c = try toPosixPath(sym_link_path); 1675 return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c); 1676 } 1677 1678 pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ"); 1679 1680 /// WASI-only. The same as `symlinkat` but targeting WASI. 1681 /// See also `symlinkat`. 1682 pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void { 1683 switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) { 1684 wasi.ESUCCESS => {}, 1685 wasi.EFAULT => unreachable, 1686 wasi.EINVAL => unreachable, 1687 wasi.EACCES => return error.AccessDenied, 1688 wasi.EPERM => return error.AccessDenied, 1689 wasi.EDQUOT => return error.DiskQuota, 1690 wasi.EEXIST => return error.PathAlreadyExists, 1691 wasi.EIO => return error.FileSystem, 1692 wasi.ELOOP => return error.SymLinkLoop, 1693 wasi.ENAMETOOLONG => return error.NameTooLong, 1694 wasi.ENOENT => return error.FileNotFound, 1695 wasi.ENOTDIR => return error.NotDir, 1696 wasi.ENOMEM => return error.SystemResources, 1697 wasi.ENOSPC => return error.NoSpaceLeft, 1698 wasi.EROFS => return error.ReadOnlyFileSystem, 1699 wasi.ENOTCAPABLE => return error.AccessDenied, 1700 else => |err| return unexpectedErrno(err), 1701 } 1702 } 1703 1704 /// The same as `symlinkat` except the parameters are null-terminated pointers. 1705 /// See also `symlinkat`. 1706 pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void { 1707 if (builtin.os.tag == .windows) { 1708 @compileError("symlinkat is not supported on Windows; use std.os.windows.CreateSymbolicLink instead"); 1709 } 1710 switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) { 1711 0 => return, 1712 EFAULT => unreachable, 1713 EINVAL => unreachable, 1714 EACCES => return error.AccessDenied, 1715 EPERM => return error.AccessDenied, 1716 EDQUOT => return error.DiskQuota, 1717 EEXIST => return error.PathAlreadyExists, 1718 EIO => return error.FileSystem, 1719 ELOOP => return error.SymLinkLoop, 1720 ENAMETOOLONG => return error.NameTooLong, 1721 ENOENT => return error.FileNotFound, 1722 ENOTDIR => return error.NotDir, 1723 ENOMEM => return error.SystemResources, 1724 ENOSPC => return error.NoSpaceLeft, 1725 EROFS => return error.ReadOnlyFileSystem, 1726 else => |err| return unexpectedErrno(err), 1727 } 1728 } 1729 1730 pub const UnlinkError = error{ 1731 FileNotFound, 1732 1733 /// In WASI, this error may occur when the file descriptor does 1734 /// not hold the required rights to unlink a resource by path relative to it. 1735 AccessDenied, 1736 FileBusy, 1737 FileSystem, 1738 IsDir, 1739 SymLinkLoop, 1740 NameTooLong, 1741 NotDir, 1742 SystemResources, 1743 ReadOnlyFileSystem, 1744 1745 /// On Windows, file paths must be valid Unicode. 1746 InvalidUtf8, 1747 1748 /// On Windows, file paths cannot contain these characters: 1749 /// '/', '*', '?', '"', '<', '>', '|' 1750 BadPathName, 1751 } || UnexpectedError; 1752 1753 /// Delete a name and possibly the file it refers to. 1754 /// See also `unlinkC`. 1755 pub fn unlink(file_path: []const u8) UnlinkError!void { 1756 if (builtin.os.tag == .wasi) { 1757 @compileError("unlink is not supported in WASI; use unlinkat instead"); 1758 } else if (builtin.os.tag == .windows) { 1759 const file_path_w = try windows.sliceToPrefixedFileW(file_path); 1760 return unlinkW(file_path_w.span()); 1761 } else { 1762 const file_path_c = try toPosixPath(file_path); 1763 return unlinkZ(&file_path_c); 1764 } 1765 } 1766 1767 pub const unlinkC = @compileError("deprecated: renamed to unlinkZ"); 1768 1769 /// Same as `unlink` except the parameter is a null terminated UTF8-encoded string. 1770 pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void { 1771 if (builtin.os.tag == .windows) { 1772 const file_path_w = try windows.cStrToPrefixedFileW(file_path); 1773 return unlinkW(file_path_w.span()); 1774 } 1775 switch (errno(system.unlink(file_path))) { 1776 0 => return, 1777 EACCES => return error.AccessDenied, 1778 EPERM => return error.AccessDenied, 1779 EBUSY => return error.FileBusy, 1780 EFAULT => unreachable, 1781 EINVAL => unreachable, 1782 EIO => return error.FileSystem, 1783 EISDIR => return error.IsDir, 1784 ELOOP => return error.SymLinkLoop, 1785 ENAMETOOLONG => return error.NameTooLong, 1786 ENOENT => return error.FileNotFound, 1787 ENOTDIR => return error.NotDir, 1788 ENOMEM => return error.SystemResources, 1789 EROFS => return error.ReadOnlyFileSystem, 1790 else => |err| return unexpectedErrno(err), 1791 } 1792 } 1793 1794 /// Windows-only. Same as `unlink` except the parameter is null-terminated, WTF16 encoded. 1795 pub fn unlinkW(file_path_w: []const u16) UnlinkError!void { 1796 return windows.DeleteFile(file_path_w, .{ .dir = std.fs.cwd().fd }); 1797 } 1798 1799 pub const UnlinkatError = UnlinkError || error{ 1800 /// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty. 1801 DirNotEmpty, 1802 }; 1803 1804 /// Delete a file name and possibly the file it refers to, based on an open directory handle. 1805 /// Asserts that the path parameter has no null bytes. 1806 pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { 1807 if (builtin.os.tag == .windows) { 1808 const file_path_w = try windows.sliceToPrefixedFileW(file_path); 1809 return unlinkatW(dirfd, file_path_w.span(), flags); 1810 } else if (builtin.os.tag == .wasi) { 1811 return unlinkatWasi(dirfd, file_path, flags); 1812 } else { 1813 const file_path_c = try toPosixPath(file_path); 1814 return unlinkatZ(dirfd, &file_path_c, flags); 1815 } 1816 } 1817 1818 pub const unlinkatC = @compileError("deprecated: renamed to unlinkatZ"); 1819 1820 /// WASI-only. Same as `unlinkat` but targeting WASI. 1821 /// See also `unlinkat`. 1822 pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void { 1823 const remove_dir = (flags & AT_REMOVEDIR) != 0; 1824 const res = if (remove_dir) 1825 wasi.path_remove_directory(dirfd, file_path.ptr, file_path.len) 1826 else 1827 wasi.path_unlink_file(dirfd, file_path.ptr, file_path.len); 1828 switch (res) { 1829 wasi.ESUCCESS => return, 1830 wasi.EACCES => return error.AccessDenied, 1831 wasi.EPERM => return error.AccessDenied, 1832 wasi.EBUSY => return error.FileBusy, 1833 wasi.EFAULT => unreachable, 1834 wasi.EIO => return error.FileSystem, 1835 wasi.EISDIR => return error.IsDir, 1836 wasi.ELOOP => return error.SymLinkLoop, 1837 wasi.ENAMETOOLONG => return error.NameTooLong, 1838 wasi.ENOENT => return error.FileNotFound, 1839 wasi.ENOTDIR => return error.NotDir, 1840 wasi.ENOMEM => return error.SystemResources, 1841 wasi.EROFS => return error.ReadOnlyFileSystem, 1842 wasi.ENOTEMPTY => return error.DirNotEmpty, 1843 wasi.ENOTCAPABLE => return error.AccessDenied, 1844 1845 wasi.EINVAL => unreachable, // invalid flags, or pathname has . as last component 1846 wasi.EBADF => unreachable, // always a race condition 1847 1848 else => |err| return unexpectedErrno(err), 1849 } 1850 } 1851 1852 /// Same as `unlinkat` but `file_path` is a null-terminated string. 1853 pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void { 1854 if (builtin.os.tag == .windows) { 1855 const file_path_w = try windows.cStrToPrefixedFileW(file_path_c); 1856 return unlinkatW(dirfd, file_path_w.span(), flags); 1857 } 1858 switch (errno(system.unlinkat(dirfd, file_path_c, flags))) { 1859 0 => return, 1860 EACCES => return error.AccessDenied, 1861 EPERM => return error.AccessDenied, 1862 EBUSY => return error.FileBusy, 1863 EFAULT => unreachable, 1864 EIO => return error.FileSystem, 1865 EISDIR => return error.IsDir, 1866 ELOOP => return error.SymLinkLoop, 1867 ENAMETOOLONG => return error.NameTooLong, 1868 ENOENT => return error.FileNotFound, 1869 ENOTDIR => return error.NotDir, 1870 ENOMEM => return error.SystemResources, 1871 EROFS => return error.ReadOnlyFileSystem, 1872 ENOTEMPTY => return error.DirNotEmpty, 1873 1874 EINVAL => unreachable, // invalid flags, or pathname has . as last component 1875 EBADF => unreachable, // always a race condition 1876 1877 else => |err| return unexpectedErrno(err), 1878 } 1879 } 1880 1881 /// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only. 1882 pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void { 1883 const remove_dir = (flags & AT_REMOVEDIR) != 0; 1884 return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir }); 1885 } 1886 1887 const RenameError = error{ 1888 /// In WASI, this error may occur when the file descriptor does 1889 /// not hold the required rights to rename a resource by path relative to it. 1890 AccessDenied, 1891 FileBusy, 1892 DiskQuota, 1893 IsDir, 1894 SymLinkLoop, 1895 LinkQuotaExceeded, 1896 NameTooLong, 1897 FileNotFound, 1898 NotDir, 1899 SystemResources, 1900 NoSpaceLeft, 1901 PathAlreadyExists, 1902 ReadOnlyFileSystem, 1903 RenameAcrossMountPoints, 1904 InvalidUtf8, 1905 BadPathName, 1906 NoDevice, 1907 SharingViolation, 1908 PipeBusy, 1909 } || UnexpectedError; 1910 1911 /// Change the name or location of a file. 1912 pub fn rename(old_path: []const u8, new_path: []const u8) RenameError!void { 1913 if (builtin.os.tag == .wasi) { 1914 @compileError("rename is not supported in WASI; use renameat instead"); 1915 } else if (builtin.os.tag == .windows) { 1916 const old_path_w = try windows.sliceToPrefixedFileW(old_path); 1917 const new_path_w = try windows.sliceToPrefixedFileW(new_path); 1918 return renameW(old_path_w.span().ptr, new_path_w.span().ptr); 1919 } else { 1920 const old_path_c = try toPosixPath(old_path); 1921 const new_path_c = try toPosixPath(new_path); 1922 return renameZ(&old_path_c, &new_path_c); 1923 } 1924 } 1925 1926 pub const renameC = @compileError("deprecated: renamed to renameZ"); 1927 1928 /// Same as `rename` except the parameters are null-terminated byte arrays. 1929 pub fn renameZ(old_path: [*:0]const u8, new_path: [*:0]const u8) RenameError!void { 1930 if (builtin.os.tag == .windows) { 1931 const old_path_w = try windows.cStrToPrefixedFileW(old_path); 1932 const new_path_w = try windows.cStrToPrefixedFileW(new_path); 1933 return renameW(old_path_w.span().ptr, new_path_w.span().ptr); 1934 } 1935 switch (errno(system.rename(old_path, new_path))) { 1936 0 => return, 1937 EACCES => return error.AccessDenied, 1938 EPERM => return error.AccessDenied, 1939 EBUSY => return error.FileBusy, 1940 EDQUOT => return error.DiskQuota, 1941 EFAULT => unreachable, 1942 EINVAL => unreachable, 1943 EISDIR => return error.IsDir, 1944 ELOOP => return error.SymLinkLoop, 1945 EMLINK => return error.LinkQuotaExceeded, 1946 ENAMETOOLONG => return error.NameTooLong, 1947 ENOENT => return error.FileNotFound, 1948 ENOTDIR => return error.NotDir, 1949 ENOMEM => return error.SystemResources, 1950 ENOSPC => return error.NoSpaceLeft, 1951 EEXIST => return error.PathAlreadyExists, 1952 ENOTEMPTY => return error.PathAlreadyExists, 1953 EROFS => return error.ReadOnlyFileSystem, 1954 EXDEV => return error.RenameAcrossMountPoints, 1955 else => |err| return unexpectedErrno(err), 1956 } 1957 } 1958 1959 /// Same as `rename` except the parameters are null-terminated UTF16LE encoded byte arrays. 1960 /// Assumes target is Windows. 1961 pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!void { 1962 const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; 1963 return windows.MoveFileExW(old_path, new_path, flags); 1964 } 1965 1966 /// Change the name or location of a file based on an open directory handle. 1967 pub fn renameat( 1968 old_dir_fd: fd_t, 1969 old_path: []const u8, 1970 new_dir_fd: fd_t, 1971 new_path: []const u8, 1972 ) RenameError!void { 1973 if (builtin.os.tag == .windows) { 1974 const old_path_w = try windows.sliceToPrefixedFileW(old_path); 1975 const new_path_w = try windows.sliceToPrefixedFileW(new_path); 1976 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); 1977 } else if (builtin.os.tag == .wasi) { 1978 return renameatWasi(old_dir_fd, old_path, new_dir_fd, new_path); 1979 } else { 1980 const old_path_c = try toPosixPath(old_path); 1981 const new_path_c = try toPosixPath(new_path); 1982 return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c); 1983 } 1984 } 1985 1986 /// WASI-only. Same as `renameat` expect targeting WASI. 1987 /// See also `renameat`. 1988 pub fn renameatWasi(old_dir_fd: fd_t, old_path: []const u8, new_dir_fd: fd_t, new_path: []const u8) RenameError!void { 1989 switch (wasi.path_rename(old_dir_fd, old_path.ptr, old_path.len, new_dir_fd, new_path.ptr, new_path.len)) { 1990 wasi.ESUCCESS => return, 1991 wasi.EACCES => return error.AccessDenied, 1992 wasi.EPERM => return error.AccessDenied, 1993 wasi.EBUSY => return error.FileBusy, 1994 wasi.EDQUOT => return error.DiskQuota, 1995 wasi.EFAULT => unreachable, 1996 wasi.EINVAL => unreachable, 1997 wasi.EISDIR => return error.IsDir, 1998 wasi.ELOOP => return error.SymLinkLoop, 1999 wasi.EMLINK => return error.LinkQuotaExceeded, 2000 wasi.ENAMETOOLONG => return error.NameTooLong, 2001 wasi.ENOENT => return error.FileNotFound, 2002 wasi.ENOTDIR => return error.NotDir, 2003 wasi.ENOMEM => return error.SystemResources, 2004 wasi.ENOSPC => return error.NoSpaceLeft, 2005 wasi.EEXIST => return error.PathAlreadyExists, 2006 wasi.ENOTEMPTY => return error.PathAlreadyExists, 2007 wasi.EROFS => return error.ReadOnlyFileSystem, 2008 wasi.EXDEV => return error.RenameAcrossMountPoints, 2009 wasi.ENOTCAPABLE => return error.AccessDenied, 2010 else => |err| return unexpectedErrno(err), 2011 } 2012 } 2013 2014 /// Same as `renameat` except the parameters are null-terminated byte arrays. 2015 pub fn renameatZ( 2016 old_dir_fd: fd_t, 2017 old_path: [*:0]const u8, 2018 new_dir_fd: fd_t, 2019 new_path: [*:0]const u8, 2020 ) RenameError!void { 2021 if (builtin.os.tag == .windows) { 2022 const old_path_w = try windows.cStrToPrefixedFileW(old_path); 2023 const new_path_w = try windows.cStrToPrefixedFileW(new_path); 2024 return renameatW(old_dir_fd, old_path_w.span(), new_dir_fd, new_path_w.span(), windows.TRUE); 2025 } 2026 2027 switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) { 2028 0 => return, 2029 EACCES => return error.AccessDenied, 2030 EPERM => return error.AccessDenied, 2031 EBUSY => return error.FileBusy, 2032 EDQUOT => return error.DiskQuota, 2033 EFAULT => unreachable, 2034 EINVAL => unreachable, 2035 EISDIR => return error.IsDir, 2036 ELOOP => return error.SymLinkLoop, 2037 EMLINK => return error.LinkQuotaExceeded, 2038 ENAMETOOLONG => return error.NameTooLong, 2039 ENOENT => return error.FileNotFound, 2040 ENOTDIR => return error.NotDir, 2041 ENOMEM => return error.SystemResources, 2042 ENOSPC => return error.NoSpaceLeft, 2043 EEXIST => return error.PathAlreadyExists, 2044 ENOTEMPTY => return error.PathAlreadyExists, 2045 EROFS => return error.ReadOnlyFileSystem, 2046 EXDEV => return error.RenameAcrossMountPoints, 2047 else => |err| return unexpectedErrno(err), 2048 } 2049 } 2050 2051 /// Same as `renameat` but Windows-only and the path parameters are 2052 /// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded. 2053 pub fn renameatW( 2054 old_dir_fd: fd_t, 2055 old_path_w: []const u16, 2056 new_dir_fd: fd_t, 2057 new_path_w: []const u16, 2058 ReplaceIfExists: windows.BOOLEAN, 2059 ) RenameError!void { 2060 const src_fd = windows.OpenFile(old_path_w, .{ 2061 .dir = old_dir_fd, 2062 .access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE, 2063 .creation = windows.FILE_OPEN, 2064 .io_mode = .blocking, 2065 }) catch |err| switch (err) { 2066 error.WouldBlock => unreachable, // Not possible without `.share_access_nonblocking = true`. 2067 else => |e| return e, 2068 }; 2069 defer windows.CloseHandle(src_fd); 2070 2071 const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1); 2072 var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined; 2073 const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path_w.len * 2; 2074 if (struct_len > struct_buf_len) return error.NameTooLong; 2075 2076 const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf); 2077 2078 rename_info.* = .{ 2079 .ReplaceIfExists = ReplaceIfExists, 2080 .RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(new_path_w)) null else new_dir_fd, 2081 .FileNameLength = @intCast(u32, new_path_w.len * 2), // already checked error.NameTooLong 2082 .FileName = undefined, 2083 }; 2084 std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path_w.len], new_path_w); 2085 2086 var io_status_block: windows.IO_STATUS_BLOCK = undefined; 2087 2088 const rc = windows.ntdll.NtSetInformationFile( 2089 src_fd, 2090 &io_status_block, 2091 rename_info, 2092 @intCast(u32, struct_len), // already checked for error.NameTooLong 2093 .FileRenameInformation, 2094 ); 2095 2096 switch (rc) { 2097 .SUCCESS => return, 2098 .INVALID_HANDLE => unreachable, 2099 .INVALID_PARAMETER => unreachable, 2100 .OBJECT_PATH_SYNTAX_BAD => unreachable, 2101 .ACCESS_DENIED => return error.AccessDenied, 2102 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, 2103 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, 2104 else => return windows.unexpectedStatus(rc), 2105 } 2106 } 2107 2108 pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { 2109 if (builtin.os.tag == .windows) { 2110 const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path); 2111 return mkdiratW(dir_fd, sub_dir_path_w.span(), mode); 2112 } else if (builtin.os.tag == .wasi) { 2113 return mkdiratWasi(dir_fd, sub_dir_path, mode); 2114 } else { 2115 const sub_dir_path_c = try toPosixPath(sub_dir_path); 2116 return mkdiratZ(dir_fd, &sub_dir_path_c, mode); 2117 } 2118 } 2119 2120 pub const mkdiratC = @compileError("deprecated: renamed to mkdiratZ"); 2121 2122 pub fn mkdiratWasi(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void { 2123 switch (wasi.path_create_directory(dir_fd, sub_dir_path.ptr, sub_dir_path.len)) { 2124 wasi.ESUCCESS => return, 2125 wasi.EACCES => return error.AccessDenied, 2126 wasi.EBADF => unreachable, 2127 wasi.EPERM => return error.AccessDenied, 2128 wasi.EDQUOT => return error.DiskQuota, 2129 wasi.EEXIST => return error.PathAlreadyExists, 2130 wasi.EFAULT => unreachable, 2131 wasi.ELOOP => return error.SymLinkLoop, 2132 wasi.EMLINK => return error.LinkQuotaExceeded, 2133 wasi.ENAMETOOLONG => return error.NameTooLong, 2134 wasi.ENOENT => return error.FileNotFound, 2135 wasi.ENOMEM => return error.SystemResources, 2136 wasi.ENOSPC => return error.NoSpaceLeft, 2137 wasi.ENOTDIR => return error.NotDir, 2138 wasi.EROFS => return error.ReadOnlyFileSystem, 2139 wasi.ENOTCAPABLE => return error.AccessDenied, 2140 else => |err| return unexpectedErrno(err), 2141 } 2142 } 2143 2144 pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirError!void { 2145 if (builtin.os.tag == .windows) { 2146 const sub_dir_path_w = try windows.cStrToPrefixedFileW(sub_dir_path); 2147 return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode); 2148 } 2149 switch (errno(system.mkdirat(dir_fd, sub_dir_path, mode))) { 2150 0 => return, 2151 EACCES => return error.AccessDenied, 2152 EBADF => unreachable, 2153 EPERM => return error.AccessDenied, 2154 EDQUOT => return error.DiskQuota, 2155 EEXIST => return error.PathAlreadyExists, 2156 EFAULT => unreachable, 2157 ELOOP => return error.SymLinkLoop, 2158 EMLINK => return error.LinkQuotaExceeded, 2159 ENAMETOOLONG => return error.NameTooLong, 2160 ENOENT => return error.FileNotFound, 2161 ENOMEM => return error.SystemResources, 2162 ENOSPC => return error.NoSpaceLeft, 2163 ENOTDIR => return error.NotDir, 2164 EROFS => return error.ReadOnlyFileSystem, 2165 else => |err| return unexpectedErrno(err), 2166 } 2167 } 2168 2169 pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void { 2170 const sub_dir_handle = windows.OpenFile(sub_path_w, .{ 2171 .dir = dir_fd, 2172 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, 2173 .creation = windows.FILE_CREATE, 2174 .io_mode = .blocking, 2175 .open_dir = true, 2176 }) catch |err| switch (err) { 2177 error.IsDir => unreachable, 2178 error.PipeBusy => unreachable, 2179 error.WouldBlock => unreachable, 2180 else => |e| return e, 2181 }; 2182 windows.CloseHandle(sub_dir_handle); 2183 } 2184 2185 pub const MakeDirError = error{ 2186 /// In WASI, this error may occur when the file descriptor does 2187 /// not hold the required rights to create a new directory relative to it. 2188 AccessDenied, 2189 DiskQuota, 2190 PathAlreadyExists, 2191 SymLinkLoop, 2192 LinkQuotaExceeded, 2193 NameTooLong, 2194 FileNotFound, 2195 SystemResources, 2196 NoSpaceLeft, 2197 NotDir, 2198 ReadOnlyFileSystem, 2199 InvalidUtf8, 2200 BadPathName, 2201 NoDevice, 2202 } || UnexpectedError; 2203 2204 /// Create a directory. 2205 /// `mode` is ignored on Windows. 2206 pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void { 2207 if (builtin.os.tag == .wasi) { 2208 @compileError("mkdir is not supported in WASI; use mkdirat instead"); 2209 } else if (builtin.os.tag == .windows) { 2210 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); 2211 return mkdirW(dir_path_w.span(), mode); 2212 } else { 2213 const dir_path_c = try toPosixPath(dir_path); 2214 return mkdirZ(&dir_path_c, mode); 2215 } 2216 } 2217 2218 /// Same as `mkdir` but the parameter is a null-terminated UTF8-encoded string. 2219 pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void { 2220 if (builtin.os.tag == .windows) { 2221 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); 2222 return mkdirW(dir_path_w.span(), mode); 2223 } 2224 switch (errno(system.mkdir(dir_path, mode))) { 2225 0 => return, 2226 EACCES => return error.AccessDenied, 2227 EPERM => return error.AccessDenied, 2228 EDQUOT => return error.DiskQuota, 2229 EEXIST => return error.PathAlreadyExists, 2230 EFAULT => unreachable, 2231 ELOOP => return error.SymLinkLoop, 2232 EMLINK => return error.LinkQuotaExceeded, 2233 ENAMETOOLONG => return error.NameTooLong, 2234 ENOENT => return error.FileNotFound, 2235 ENOMEM => return error.SystemResources, 2236 ENOSPC => return error.NoSpaceLeft, 2237 ENOTDIR => return error.NotDir, 2238 EROFS => return error.ReadOnlyFileSystem, 2239 else => |err| return unexpectedErrno(err), 2240 } 2241 } 2242 2243 /// Windows-only. Same as `mkdir` but the parameters is WTF16 encoded. 2244 pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void { 2245 const sub_dir_handle = windows.OpenFile(dir_path_w, .{ 2246 .dir = std.fs.cwd().fd, 2247 .access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE, 2248 .creation = windows.FILE_CREATE, 2249 .io_mode = .blocking, 2250 .open_dir = true, 2251 }) catch |err| switch (err) { 2252 error.IsDir => unreachable, 2253 error.PipeBusy => unreachable, 2254 error.WouldBlock => unreachable, 2255 else => |e| return e, 2256 }; 2257 windows.CloseHandle(sub_dir_handle); 2258 } 2259 2260 pub const DeleteDirError = error{ 2261 AccessDenied, 2262 FileBusy, 2263 SymLinkLoop, 2264 NameTooLong, 2265 FileNotFound, 2266 SystemResources, 2267 NotDir, 2268 DirNotEmpty, 2269 ReadOnlyFileSystem, 2270 InvalidUtf8, 2271 BadPathName, 2272 } || UnexpectedError; 2273 2274 /// Deletes an empty directory. 2275 pub fn rmdir(dir_path: []const u8) DeleteDirError!void { 2276 if (builtin.os.tag == .wasi) { 2277 @compileError("rmdir is not supported in WASI; use unlinkat instead"); 2278 } else if (builtin.os.tag == .windows) { 2279 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); 2280 return rmdirW(dir_path_w.span()); 2281 } else { 2282 const dir_path_c = try toPosixPath(dir_path); 2283 return rmdirZ(&dir_path_c); 2284 } 2285 } 2286 2287 pub const rmdirC = @compileError("deprecated: renamed to rmdirZ"); 2288 2289 /// Same as `rmdir` except the parameter is null-terminated. 2290 pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void { 2291 if (builtin.os.tag == .windows) { 2292 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); 2293 return rmdirW(dir_path_w.span()); 2294 } 2295 switch (errno(system.rmdir(dir_path))) { 2296 0 => return, 2297 EACCES => return error.AccessDenied, 2298 EPERM => return error.AccessDenied, 2299 EBUSY => return error.FileBusy, 2300 EFAULT => unreachable, 2301 EINVAL => unreachable, 2302 ELOOP => return error.SymLinkLoop, 2303 ENAMETOOLONG => return error.NameTooLong, 2304 ENOENT => return error.FileNotFound, 2305 ENOMEM => return error.SystemResources, 2306 ENOTDIR => return error.NotDir, 2307 EEXIST => return error.DirNotEmpty, 2308 ENOTEMPTY => return error.DirNotEmpty, 2309 EROFS => return error.ReadOnlyFileSystem, 2310 else => |err| return unexpectedErrno(err), 2311 } 2312 } 2313 2314 /// Windows-only. Same as `rmdir` except the parameter is WTF16 encoded. 2315 pub fn rmdirW(dir_path_w: []const u16) DeleteDirError!void { 2316 return windows.DeleteFile(dir_path_w, .{ .dir = std.fs.cwd().fd, .remove_dir = true }) catch |err| switch (err) { 2317 error.IsDir => unreachable, 2318 else => |e| return e, 2319 }; 2320 } 2321 2322 pub const ChangeCurDirError = error{ 2323 AccessDenied, 2324 FileSystem, 2325 SymLinkLoop, 2326 NameTooLong, 2327 FileNotFound, 2328 SystemResources, 2329 NotDir, 2330 } || UnexpectedError; 2331 2332 /// Changes the current working directory of the calling process. 2333 /// `dir_path` is recommended to be a UTF-8 encoded string. 2334 pub fn chdir(dir_path: []const u8) ChangeCurDirError!void { 2335 if (builtin.os.tag == .wasi) { 2336 @compileError("chdir is not supported in WASI"); 2337 } else if (builtin.os.tag == .windows) { 2338 const dir_path_w = try windows.sliceToPrefixedFileW(dir_path); 2339 @compileError("TODO implement chdir for Windows"); 2340 } else { 2341 const dir_path_c = try toPosixPath(dir_path); 2342 return chdirZ(&dir_path_c); 2343 } 2344 } 2345 2346 pub const chdirC = @compileError("deprecated: renamed to chdirZ"); 2347 2348 /// Same as `chdir` except the parameter is null-terminated. 2349 pub fn chdirZ(dir_path: [*:0]const u8) ChangeCurDirError!void { 2350 if (builtin.os.tag == .windows) { 2351 const dir_path_w = try windows.cStrToPrefixedFileW(dir_path); 2352 @compileError("TODO implement chdir for Windows"); 2353 } 2354 switch (errno(system.chdir(dir_path))) { 2355 0 => return, 2356 EACCES => return error.AccessDenied, 2357 EFAULT => unreachable, 2358 EIO => return error.FileSystem, 2359 ELOOP => return error.SymLinkLoop, 2360 ENAMETOOLONG => return error.NameTooLong, 2361 ENOENT => return error.FileNotFound, 2362 ENOMEM => return error.SystemResources, 2363 ENOTDIR => return error.NotDir, 2364 else => |err| return unexpectedErrno(err), 2365 } 2366 } 2367 2368 pub const FchdirError = error{ 2369 AccessDenied, 2370 NotDir, 2371 FileSystem, 2372 } || UnexpectedError; 2373 2374 pub fn fchdir(dirfd: fd_t) FchdirError!void { 2375 while (true) { 2376 switch (errno(system.fchdir(dirfd))) { 2377 0 => return, 2378 EACCES => return error.AccessDenied, 2379 EBADF => unreachable, 2380 ENOTDIR => return error.NotDir, 2381 EINTR => continue, 2382 EIO => return error.FileSystem, 2383 else => |err| return unexpectedErrno(err), 2384 } 2385 } 2386 } 2387 2388 pub const ReadLinkError = error{ 2389 /// In WASI, this error may occur when the file descriptor does 2390 /// not hold the required rights to read value of a symbolic link relative to it. 2391 AccessDenied, 2392 FileSystem, 2393 SymLinkLoop, 2394 NameTooLong, 2395 FileNotFound, 2396 SystemResources, 2397 NotDir, 2398 InvalidUtf8, 2399 BadPathName, 2400 /// Windows-only. This error may occur if the opened reparse point is 2401 /// of unsupported type. 2402 UnsupportedReparsePointType, 2403 } || UnexpectedError; 2404 2405 /// Read value of a symbolic link. 2406 /// The return value is a slice of `out_buffer` from index 0. 2407 pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { 2408 if (builtin.os.tag == .wasi) { 2409 @compileError("readlink is not supported in WASI; use readlinkat instead"); 2410 } else if (builtin.os.tag == .windows) { 2411 const file_path_w = try windows.sliceToPrefixedFileW(file_path); 2412 return readlinkW(file_path_w.span(), out_buffer); 2413 } else { 2414 const file_path_c = try toPosixPath(file_path); 2415 return readlinkZ(&file_path_c, out_buffer); 2416 } 2417 } 2418 2419 pub const readlinkC = @compileError("deprecated: renamed to readlinkZ"); 2420 2421 /// Windows-only. Same as `readlink` except `file_path` is WTF16 encoded. 2422 /// See also `readlinkZ`. 2423 pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 { 2424 return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer); 2425 } 2426 2427 /// Same as `readlink` except `file_path` is null-terminated. 2428 pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { 2429 if (builtin.os.tag == .windows) { 2430 const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path); 2431 return readlinkW(file_path_w.span(), out_buffer); 2432 } 2433 const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len); 2434 switch (errno(rc)) { 2435 0 => return out_buffer[0..@bitCast(usize, rc)], 2436 EACCES => return error.AccessDenied, 2437 EFAULT => unreachable, 2438 EINVAL => unreachable, 2439 EIO => return error.FileSystem, 2440 ELOOP => return error.SymLinkLoop, 2441 ENAMETOOLONG => return error.NameTooLong, 2442 ENOENT => return error.FileNotFound, 2443 ENOMEM => return error.SystemResources, 2444 ENOTDIR => return error.NotDir, 2445 else => |err| return unexpectedErrno(err), 2446 } 2447 } 2448 2449 /// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle. 2450 /// The return value is a slice of `out_buffer` from index 0. 2451 /// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`. 2452 pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { 2453 if (builtin.os.tag == .wasi) { 2454 return readlinkatWasi(dirfd, file_path, out_buffer); 2455 } 2456 if (builtin.os.tag == .windows) { 2457 const file_path_w = try windows.sliceToPrefixedFileW(file_path); 2458 return readlinkatW(dirfd, file_path_w.span(), out_buffer); 2459 } 2460 const file_path_c = try toPosixPath(file_path); 2461 return readlinkatZ(dirfd, &file_path_c, out_buffer); 2462 } 2463 2464 pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ"); 2465 2466 /// WASI-only. Same as `readlinkat` but targets WASI. 2467 /// See also `readlinkat`. 2468 pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 { 2469 var bufused: usize = undefined; 2470 switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) { 2471 wasi.ESUCCESS => return out_buffer[0..bufused], 2472 wasi.EACCES => return error.AccessDenied, 2473 wasi.EFAULT => unreachable, 2474 wasi.EINVAL => unreachable, 2475 wasi.EIO => return error.FileSystem, 2476 wasi.ELOOP => return error.SymLinkLoop, 2477 wasi.ENAMETOOLONG => return error.NameTooLong, 2478 wasi.ENOENT => return error.FileNotFound, 2479 wasi.ENOMEM => return error.SystemResources, 2480 wasi.ENOTDIR => return error.NotDir, 2481 wasi.ENOTCAPABLE => return error.AccessDenied, 2482 else => |err| return unexpectedErrno(err), 2483 } 2484 } 2485 2486 /// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded. 2487 /// See also `readlinkat`. 2488 pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 { 2489 return windows.ReadLink(dirfd, file_path, out_buffer); 2490 } 2491 2492 /// Same as `readlinkat` except `file_path` is null-terminated. 2493 /// See also `readlinkat`. 2494 pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 { 2495 if (builtin.os.tag == .windows) { 2496 const file_path_w = try windows.cStrToPrefixedFileW(file_path); 2497 return readlinkatW(dirfd, file_path_w.span(), out_buffer); 2498 } 2499 const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len); 2500 switch (errno(rc)) { 2501 0 => return out_buffer[0..@bitCast(usize, rc)], 2502 EACCES => return error.AccessDenied, 2503 EFAULT => unreachable, 2504 EINVAL => unreachable, 2505 EIO => return error.FileSystem, 2506 ELOOP => return error.SymLinkLoop, 2507 ENAMETOOLONG => return error.NameTooLong, 2508 ENOENT => return error.FileNotFound, 2509 ENOMEM => return error.SystemResources, 2510 ENOTDIR => return error.NotDir, 2511 else => |err| return unexpectedErrno(err), 2512 } 2513 } 2514 2515 pub const SetIdError = error{ 2516 ResourceLimitReached, 2517 InvalidUserId, 2518 PermissionDenied, 2519 } || UnexpectedError; 2520 2521 pub fn setuid(uid: u32) SetIdError!void { 2522 switch (errno(system.setuid(uid))) { 2523 0 => return, 2524 EAGAIN => return error.ResourceLimitReached, 2525 EINVAL => return error.InvalidUserId, 2526 EPERM => return error.PermissionDenied, 2527 else => |err| return unexpectedErrno(err), 2528 } 2529 } 2530 2531 pub fn setreuid(ruid: u32, euid: u32) SetIdError!void { 2532 switch (errno(system.setreuid(ruid, euid))) { 2533 0 => return, 2534 EAGAIN => return error.ResourceLimitReached, 2535 EINVAL => return error.InvalidUserId, 2536 EPERM => return error.PermissionDenied, 2537 else => |err| return unexpectedErrno(err), 2538 } 2539 } 2540 2541 pub fn setgid(gid: u32) SetIdError!void { 2542 switch (errno(system.setgid(gid))) { 2543 0 => return, 2544 EAGAIN => return error.ResourceLimitReached, 2545 EINVAL => return error.InvalidUserId, 2546 EPERM => return error.PermissionDenied, 2547 else => |err| return unexpectedErrno(err), 2548 } 2549 } 2550 2551 pub fn setregid(rgid: u32, egid: u32) SetIdError!void { 2552 switch (errno(system.setregid(rgid, egid))) { 2553 0 => return, 2554 EAGAIN => return error.ResourceLimitReached, 2555 EINVAL => return error.InvalidUserId, 2556 EPERM => return error.PermissionDenied, 2557 else => |err| return unexpectedErrno(err), 2558 } 2559 } 2560 2561 /// Test whether a file descriptor refers to a terminal. 2562 pub fn isatty(handle: fd_t) bool { 2563 if (builtin.os.tag == .windows) { 2564 if (isCygwinPty(handle)) 2565 return true; 2566 2567 var out: windows.DWORD = undefined; 2568 return windows.kernel32.GetConsoleMode(handle, &out) != 0; 2569 } 2570 if (builtin.link_libc) { 2571 return system.isatty(handle) != 0; 2572 } 2573 if (builtin.os.tag == .wasi) { 2574 var statbuf: fdstat_t = undefined; 2575 const err = system.fd_fdstat_get(handle, &statbuf); 2576 if (err != 0) { 2577 // errno = err; 2578 return false; 2579 } 2580 2581 // A tty is a character device that we can't seek or tell on. 2582 if (statbuf.fs_filetype != FILETYPE_CHARACTER_DEVICE or 2583 (statbuf.fs_rights_base & (RIGHT_FD_SEEK | RIGHT_FD_TELL)) != 0) 2584 { 2585 // errno = ENOTTY; 2586 return false; 2587 } 2588 2589 return true; 2590 } 2591 if (builtin.os.tag == .linux) { 2592 while (true) { 2593 var wsz: linux.winsize = undefined; 2594 const fd = @bitCast(usize, @as(isize, handle)); 2595 switch (linux.syscall3(.ioctl, fd, linux.TIOCGWINSZ, @ptrToInt(&wsz))) { 2596 0 => return true, 2597 EINTR => continue, 2598 else => return false, 2599 } 2600 } 2601 } 2602 unreachable; 2603 } 2604 2605 pub fn isCygwinPty(handle: fd_t) bool { 2606 if (builtin.os.tag != .windows) return false; 2607 2608 const size = @sizeOf(windows.FILE_NAME_INFO); 2609 var name_info_bytes align(@alignOf(windows.FILE_NAME_INFO)) = [_]u8{0} ** (size + windows.MAX_PATH); 2610 2611 if (windows.kernel32.GetFileInformationByHandleEx( 2612 handle, 2613 windows.FileNameInfo, 2614 @ptrCast(*c_void, &name_info_bytes), 2615 name_info_bytes.len, 2616 ) == 0) { 2617 return false; 2618 } 2619 2620 const name_info = @ptrCast(*const windows.FILE_NAME_INFO, &name_info_bytes[0]); 2621 const name_bytes = name_info_bytes[size .. size + @as(usize, name_info.FileNameLength)]; 2622 const name_wide = mem.bytesAsSlice(u16, name_bytes); 2623 return mem.indexOf(u16, name_wide, &[_]u16{ 'm', 's', 'y', 's', '-' }) != null or 2624 mem.indexOf(u16, name_wide, &[_]u16{ '-', 'p', 't', 'y' }) != null; 2625 } 2626 2627 pub const SocketError = error{ 2628 /// Permission to create a socket of the specified type and/or 2629 /// pro‐tocol is denied. 2630 PermissionDenied, 2631 2632 /// The implementation does not support the specified address family. 2633 AddressFamilyNotSupported, 2634 2635 /// Unknown protocol, or protocol family not available. 2636 ProtocolFamilyNotAvailable, 2637 2638 /// The per-process limit on the number of open file descriptors has been reached. 2639 ProcessFdQuotaExceeded, 2640 2641 /// The system-wide limit on the total number of open files has been reached. 2642 SystemFdQuotaExceeded, 2643 2644 /// Insufficient memory is available. The socket cannot be created until sufficient 2645 /// resources are freed. 2646 SystemResources, 2647 2648 /// The protocol type or the specified protocol is not supported within this domain. 2649 ProtocolNotSupported, 2650 2651 /// The socket type is not supported by the protocol. 2652 SocketTypeNotSupported, 2653 } || UnexpectedError; 2654 2655 pub fn socket(domain: u32, socket_type: u32, protocol: u32) SocketError!socket_t { 2656 if (builtin.os.tag == .windows) { 2657 // NOTE: windows translates the SOCK_NONBLOCK/SOCK_CLOEXEC flags into windows-analagous operations 2658 const filtered_sock_type = socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC); 2659 const flags: u32 = if ((socket_type & SOCK_CLOEXEC) != 0) windows.ws2_32.WSA_FLAG_NO_HANDLE_INHERIT else 0; 2660 const rc = windows.ws2_32.WSASocketW(@intCast(c_int, domain), @intCast(c_int, filtered_sock_type), @intCast(c_int, protocol), null, 0, flags); 2661 if (rc == windows.ws2_32.INVALID_SOCKET) switch (windows.ws2_32.WSAGetLastError()) { 2662 .WSAEMFILE => return error.ProcessFdQuotaExceeded, 2663 .WSAENOBUFS => return error.SystemResources, 2664 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, 2665 .WSAEPROTONOSUPPORT => return error.ProtocolNotSupported, 2666 else => |err| return windows.unexpectedWSAError(err), 2667 }; 2668 errdefer windows.closesocket(rc) catch unreachable; 2669 if ((socket_type & SOCK_NONBLOCK) != 0) { 2670 var mode: c_ulong = 1; // nonblocking 2671 if (windows.ws2_32.SOCKET_ERROR == windows.ws2_32.ioctlsocket(rc, windows.ws2_32.FIONBIO, &mode)) { 2672 switch (windows.ws2_32.WSAGetLastError()) { 2673 // have not identified any error codes that should be handled yet 2674 else => unreachable, 2675 } 2676 } 2677 } 2678 return rc; 2679 } 2680 2681 const have_sock_flags = comptime !std.Target.current.isDarwin(); 2682 const filtered_sock_type = if (!have_sock_flags) 2683 socket_type & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC) 2684 else 2685 socket_type; 2686 const rc = system.socket(domain, filtered_sock_type, protocol); 2687 switch (errno(rc)) { 2688 0 => { 2689 const fd = @intCast(fd_t, rc); 2690 if (!have_sock_flags) { 2691 try setSockFlags(fd, socket_type); 2692 } 2693 return fd; 2694 }, 2695 EACCES => return error.PermissionDenied, 2696 EAFNOSUPPORT => return error.AddressFamilyNotSupported, 2697 EINVAL => return error.ProtocolFamilyNotAvailable, 2698 EMFILE => return error.ProcessFdQuotaExceeded, 2699 ENFILE => return error.SystemFdQuotaExceeded, 2700 ENOBUFS => return error.SystemResources, 2701 ENOMEM => return error.SystemResources, 2702 EPROTONOSUPPORT => return error.ProtocolNotSupported, 2703 EPROTOTYPE => return error.SocketTypeNotSupported, 2704 else => |err| return unexpectedErrno(err), 2705 } 2706 } 2707 2708 pub const BindError = error{ 2709 /// The address is protected, and the user is not the superuser. 2710 /// For UNIX domain sockets: Search permission is denied on a component 2711 /// of the path prefix. 2712 AccessDenied, 2713 2714 /// The given address is already in use, or in the case of Internet domain sockets, 2715 /// The port number was specified as zero in the socket 2716 /// address structure, but, upon attempting to bind to an ephemeral port, it was 2717 /// determined that all port numbers in the ephemeral port range are currently in 2718 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). 2719 AddressInUse, 2720 2721 /// A nonexistent interface was requested or the requested address was not local. 2722 AddressNotAvailable, 2723 2724 /// Too many symbolic links were encountered in resolving addr. 2725 SymLinkLoop, 2726 2727 /// addr is too long. 2728 NameTooLong, 2729 2730 /// A component in the directory prefix of the socket pathname does not exist. 2731 FileNotFound, 2732 2733 /// Insufficient kernel memory was available. 2734 SystemResources, 2735 2736 /// A component of the path prefix is not a directory. 2737 NotDir, 2738 2739 /// The socket inode would reside on a read-only filesystem. 2740 ReadOnlyFileSystem, 2741 } || UnexpectedError; 2742 2743 /// addr is `*const T` where T is one of the sockaddr 2744 pub fn bind(sockfd: fd_t, addr: *const sockaddr, len: socklen_t) BindError!void { 2745 const rc = system.bind(sockfd, addr, len); 2746 switch (errno(rc)) { 2747 0 => return, 2748 EACCES => return error.AccessDenied, 2749 EADDRINUSE => return error.AddressInUse, 2750 EBADF => unreachable, // always a race condition if this error is returned 2751 EINVAL => unreachable, // invalid parameters 2752 ENOTSOCK => unreachable, // invalid `sockfd` 2753 EADDRNOTAVAIL => return error.AddressNotAvailable, 2754 EFAULT => unreachable, // invalid `addr` pointer 2755 ELOOP => return error.SymLinkLoop, 2756 ENAMETOOLONG => return error.NameTooLong, 2757 ENOENT => return error.FileNotFound, 2758 ENOMEM => return error.SystemResources, 2759 ENOTDIR => return error.NotDir, 2760 EROFS => return error.ReadOnlyFileSystem, 2761 else => |err| return unexpectedErrno(err), 2762 } 2763 } 2764 2765 const ListenError = error{ 2766 /// Another socket is already listening on the same port. 2767 /// For Internet domain sockets, the socket referred to by sockfd had not previously 2768 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it 2769 /// was determined that all port numbers in the ephemeral port range are currently in 2770 /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). 2771 AddressInUse, 2772 2773 /// The file descriptor sockfd does not refer to a socket. 2774 FileDescriptorNotASocket, 2775 2776 /// The socket is not of a type that supports the listen() operation. 2777 OperationNotSupported, 2778 } || UnexpectedError; 2779 2780 pub fn listen(sockfd: fd_t, backlog: u32) ListenError!void { 2781 const rc = system.listen(sockfd, backlog); 2782 switch (errno(rc)) { 2783 0 => return, 2784 EADDRINUSE => return error.AddressInUse, 2785 EBADF => unreachable, 2786 ENOTSOCK => return error.FileDescriptorNotASocket, 2787 EOPNOTSUPP => return error.OperationNotSupported, 2788 else => |err| return unexpectedErrno(err), 2789 } 2790 } 2791 2792 pub const AcceptError = error{ 2793 ConnectionAborted, 2794 2795 /// The per-process limit on the number of open file descriptors has been reached. 2796 ProcessFdQuotaExceeded, 2797 2798 /// The system-wide limit on the total number of open files has been reached. 2799 SystemFdQuotaExceeded, 2800 2801 /// Not enough free memory. This often means that the memory allocation is limited 2802 /// by the socket buffer limits, not by the system memory. 2803 SystemResources, 2804 2805 ProtocolFailure, 2806 2807 /// Firewall rules forbid connection. 2808 BlockedByFirewall, 2809 2810 /// This error occurs when no global event loop is configured, 2811 /// and accepting from the socket would block. 2812 WouldBlock, 2813 2814 /// Permission to create a socket of the specified type and/or 2815 /// protocol is denied. 2816 PermissionDenied, 2817 } || UnexpectedError; 2818 2819 /// Accept a connection on a socket. 2820 /// If the application has a global event loop enabled, EAGAIN is handled 2821 /// via the event loop. Otherwise EAGAIN results in error.WouldBlock. 2822 pub fn accept( 2823 /// This argument is a socket that has been created with `socket`, bound to a local address 2824 /// with `bind`, and is listening for connections after a `listen`. 2825 sockfd: fd_t, 2826 /// This argument is a pointer to a sockaddr structure. This structure is filled in with the 2827 /// address of the peer socket, as known to the communications layer. The exact format of the 2828 /// address returned addr is determined by the socket's address family (see `socket` and the 2829 /// respective protocol man pages). 2830 addr: *sockaddr, 2831 /// This argument is a value-result argument: the caller must initialize it to contain the 2832 /// size (in bytes) of the structure pointed to by addr; on return it will contain the actual size 2833 /// of the peer address. 2834 /// 2835 /// The returned address is truncated if the buffer provided is too small; in this case, `addr_size` 2836 /// will return a value greater than was supplied to the call. 2837 addr_size: *socklen_t, 2838 /// The following values can be bitwise ORed in flags to obtain different behavior: 2839 /// * `SOCK_NONBLOCK` - Set the `O_NONBLOCK` file status flag on the open file description (see `open`) 2840 /// referred to by the new file descriptor. Using this flag saves extra calls to `fcntl` to achieve 2841 /// the same result. 2842 /// * `SOCK_CLOEXEC` - Set the close-on-exec (`FD_CLOEXEC`) flag on the new file descriptor. See the 2843 /// description of the `O_CLOEXEC` flag in `open` for reasons why this may be useful. 2844 flags: u32, 2845 ) AcceptError!fd_t { 2846 const have_accept4 = comptime !std.Target.current.isDarwin(); 2847 assert(0 == (flags & ~@as(u32, SOCK_NONBLOCK | SOCK_CLOEXEC))); // Unsupported flag(s) 2848 2849 while (true) { 2850 const rc = if (have_accept4) 2851 system.accept4(sockfd, addr, addr_size, flags) 2852 else 2853 system.accept(sockfd, addr, addr_size); 2854 2855 switch (errno(rc)) { 2856 0 => { 2857 const fd = @intCast(fd_t, rc); 2858 if (!have_accept4) { 2859 try setSockFlags(fd, flags); 2860 } 2861 return fd; 2862 }, 2863 EINTR => continue, 2864 EAGAIN => if (std.event.Loop.instance) |loop| { 2865 loop.waitUntilFdReadable(sockfd); 2866 continue; 2867 } else { 2868 return error.WouldBlock; 2869 }, 2870 EBADF => unreachable, // always a race condition 2871 ECONNABORTED => return error.ConnectionAborted, 2872 EFAULT => unreachable, 2873 EINVAL => unreachable, 2874 ENOTSOCK => unreachable, 2875 EMFILE => return error.ProcessFdQuotaExceeded, 2876 ENFILE => return error.SystemFdQuotaExceeded, 2877 ENOBUFS => return error.SystemResources, 2878 ENOMEM => return error.SystemResources, 2879 EOPNOTSUPP => unreachable, 2880 EPROTO => return error.ProtocolFailure, 2881 EPERM => return error.BlockedByFirewall, 2882 else => |err| return unexpectedErrno(err), 2883 } 2884 } 2885 } 2886 2887 pub const EpollCreateError = error{ 2888 /// The per-user limit on the number of epoll instances imposed by 2889 /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further 2890 /// details. 2891 /// Or, The per-process limit on the number of open file descriptors has been reached. 2892 ProcessFdQuotaExceeded, 2893 2894 /// The system-wide limit on the total number of open files has been reached. 2895 SystemFdQuotaExceeded, 2896 2897 /// There was insufficient memory to create the kernel object. 2898 SystemResources, 2899 } || UnexpectedError; 2900 2901 pub fn epoll_create1(flags: u32) EpollCreateError!i32 { 2902 const rc = system.epoll_create1(flags); 2903 switch (errno(rc)) { 2904 0 => return @intCast(i32, rc), 2905 else => |err| return unexpectedErrno(err), 2906 2907 EINVAL => unreachable, 2908 EMFILE => return error.ProcessFdQuotaExceeded, 2909 ENFILE => return error.SystemFdQuotaExceeded, 2910 ENOMEM => return error.SystemResources, 2911 } 2912 } 2913 2914 pub const EpollCtlError = error{ 2915 /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered 2916 /// with this epoll instance. 2917 FileDescriptorAlreadyPresentInSet, 2918 2919 /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a 2920 /// circular loop of epoll instances monitoring one another. 2921 OperationCausesCircularLoop, 2922 2923 /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll 2924 /// instance. 2925 FileDescriptorNotRegistered, 2926 2927 /// There was insufficient memory to handle the requested op control operation. 2928 SystemResources, 2929 2930 /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while 2931 /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. 2932 /// See epoll(7) for further details. 2933 UserResourceLimitReached, 2934 2935 /// The target file fd does not support epoll. This error can occur if fd refers to, 2936 /// for example, a regular file or a directory. 2937 FileDescriptorIncompatibleWithEpoll, 2938 } || UnexpectedError; 2939 2940 pub fn epoll_ctl(epfd: i32, op: u32, fd: i32, event: ?*epoll_event) EpollCtlError!void { 2941 const rc = system.epoll_ctl(epfd, op, fd, event); 2942 switch (errno(rc)) { 2943 0 => return, 2944 else => |err| return unexpectedErrno(err), 2945 2946 EBADF => unreachable, // always a race condition if this happens 2947 EEXIST => return error.FileDescriptorAlreadyPresentInSet, 2948 EINVAL => unreachable, 2949 ELOOP => return error.OperationCausesCircularLoop, 2950 ENOENT => return error.FileDescriptorNotRegistered, 2951 ENOMEM => return error.SystemResources, 2952 ENOSPC => return error.UserResourceLimitReached, 2953 EPERM => return error.FileDescriptorIncompatibleWithEpoll, 2954 } 2955 } 2956 2957 /// Waits for an I/O event on an epoll file descriptor. 2958 /// Returns the number of file descriptors ready for the requested I/O, 2959 /// or zero if no file descriptor became ready during the requested timeout milliseconds. 2960 pub fn epoll_wait(epfd: i32, events: []epoll_event, timeout: i32) usize { 2961 while (true) { 2962 // TODO get rid of the @intCast 2963 const rc = system.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); 2964 switch (errno(rc)) { 2965 0 => return @intCast(usize, rc), 2966 EINTR => continue, 2967 EBADF => unreachable, 2968 EFAULT => unreachable, 2969 EINVAL => unreachable, 2970 else => unreachable, 2971 } 2972 } 2973 } 2974 2975 pub const EventFdError = error{ 2976 SystemResources, 2977 ProcessFdQuotaExceeded, 2978 SystemFdQuotaExceeded, 2979 } || UnexpectedError; 2980 2981 pub fn eventfd(initval: u32, flags: u32) EventFdError!i32 { 2982 const rc = system.eventfd(initval, flags); 2983 switch (errno(rc)) { 2984 0 => return @intCast(i32, rc), 2985 else => |err| return unexpectedErrno(err), 2986 2987 EINVAL => unreachable, // invalid parameters 2988 EMFILE => return error.ProcessFdQuotaExceeded, 2989 ENFILE => return error.SystemFdQuotaExceeded, 2990 ENODEV => return error.SystemResources, 2991 ENOMEM => return error.SystemResources, 2992 } 2993 } 2994 2995 pub const GetSockNameError = error{ 2996 /// Insufficient resources were available in the system to perform the operation. 2997 SystemResources, 2998 } || UnexpectedError; 2999 3000 pub fn getsockname(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t) GetSockNameError!void { 3001 switch (errno(system.getsockname(sockfd, addr, addrlen))) { 3002 0 => return, 3003 else => |err| return unexpectedErrno(err), 3004 3005 EBADF => unreachable, // always a race condition 3006 EFAULT => unreachable, 3007 EINVAL => unreachable, // invalid parameters 3008 ENOTSOCK => unreachable, 3009 ENOBUFS => return error.SystemResources, 3010 } 3011 } 3012 3013 pub const ConnectError = error{ 3014 /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket 3015 /// file, or search permission is denied for one of the directories in the path prefix. 3016 /// or 3017 /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or 3018 /// the connection request failed because of a local firewall rule. 3019 PermissionDenied, 3020 3021 /// Local address is already in use. 3022 AddressInUse, 3023 3024 /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an 3025 /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers 3026 /// in the ephemeral port range are currently in use. See the discussion of 3027 /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). 3028 AddressNotAvailable, 3029 3030 /// The passed address didn't have the correct address family in its sa_family field. 3031 AddressFamilyNotSupported, 3032 3033 /// Insufficient entries in the routing cache. 3034 SystemResources, 3035 3036 /// A connect() on a stream socket found no one listening on the remote address. 3037 ConnectionRefused, 3038 3039 /// Network is unreachable. 3040 NetworkUnreachable, 3041 3042 /// Timeout while attempting connection. The server may be too busy to accept new connections. Note 3043 /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. 3044 ConnectionTimedOut, 3045 3046 /// This error occurs when no global event loop is configured, 3047 /// and connecting to the socket would block. 3048 WouldBlock, 3049 3050 /// The given path for the unix socket does not exist. 3051 FileNotFound, 3052 } || UnexpectedError; 3053 3054 /// Initiate a connection on a socket. 3055 pub fn connect(sockfd: socket_t, sock_addr: *const sockaddr, len: socklen_t) ConnectError!void { 3056 if (builtin.os.tag == .windows) { 3057 const rc = windows.ws2_32.connect(sockfd, sock_addr, len); 3058 if (rc == 0) return; 3059 switch (windows.ws2_32.WSAGetLastError()) { 3060 .WSAEADDRINUSE => return error.AddressInUse, 3061 .WSAEADDRNOTAVAIL => return error.AddressNotAvailable, 3062 .WSAECONNREFUSED => return error.ConnectionRefused, 3063 .WSAETIMEDOUT => return error.ConnectionTimedOut, 3064 .WSAEHOSTUNREACH // TODO: should we return NetworkUnreachable in this case as well? 3065 , .WSAENETUNREACH => return error.NetworkUnreachable, 3066 .WSAEFAULT => unreachable, 3067 .WSAEINVAL => unreachable, 3068 .WSAEISCONN => unreachable, 3069 .WSAENOTSOCK => unreachable, 3070 .WSAEWOULDBLOCK => unreachable, 3071 .WSAEACCES => unreachable, 3072 .WSAENOBUFS => return error.SystemResources, 3073 .WSAEAFNOSUPPORT => return error.AddressFamilyNotSupported, 3074 else => |err| return windows.unexpectedWSAError(err), 3075 } 3076 return; 3077 } 3078 3079 while (true) { 3080 switch (errno(system.connect(sockfd, sock_addr, len))) { 3081 0 => return, 3082 EACCES => return error.PermissionDenied, 3083 EPERM => return error.PermissionDenied, 3084 EADDRINUSE => return error.AddressInUse, 3085 EADDRNOTAVAIL => return error.AddressNotAvailable, 3086 EAFNOSUPPORT => return error.AddressFamilyNotSupported, 3087 EAGAIN, EINPROGRESS => { 3088 const loop = std.event.Loop.instance orelse return error.WouldBlock; 3089 loop.waitUntilFdWritable(sockfd); 3090 return getsockoptError(sockfd); 3091 }, 3092 EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. 3093 EBADF => unreachable, // sockfd is not a valid open file descriptor. 3094 ECONNREFUSED => return error.ConnectionRefused, 3095 EFAULT => unreachable, // The socket structure address is outside the user's address space. 3096 EINTR => continue, 3097 EISCONN => unreachable, // The socket is already connected. 3098 ENETUNREACH => return error.NetworkUnreachable, 3099 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. 3100 EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. 3101 ETIMEDOUT => return error.ConnectionTimedOut, 3102 ENOENT => return error.FileNotFound, // Returned when socket is AF_UNIX and the given path does not exist. 3103 else => |err| return unexpectedErrno(err), 3104 } 3105 } 3106 } 3107 3108 pub fn getsockoptError(sockfd: fd_t) ConnectError!void { 3109 var err_code: u32 = undefined; 3110 var size: u32 = @sizeOf(u32); 3111 const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size); 3112 assert(size == 4); 3113 switch (errno(rc)) { 3114 0 => switch (err_code) { 3115 0 => return, 3116 EACCES => return error.PermissionDenied, 3117 EPERM => return error.PermissionDenied, 3118 EADDRINUSE => return error.AddressInUse, 3119 EADDRNOTAVAIL => return error.AddressNotAvailable, 3120 EAFNOSUPPORT => return error.AddressFamilyNotSupported, 3121 EAGAIN => return error.SystemResources, 3122 EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. 3123 EBADF => unreachable, // sockfd is not a valid open file descriptor. 3124 ECONNREFUSED => return error.ConnectionRefused, 3125 EFAULT => unreachable, // The socket structure address is outside the user's address space. 3126 EISCONN => unreachable, // The socket is already connected. 3127 ENETUNREACH => return error.NetworkUnreachable, 3128 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. 3129 EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. 3130 ETIMEDOUT => return error.ConnectionTimedOut, 3131 else => |err| return unexpectedErrno(err), 3132 }, 3133 EBADF => unreachable, // The argument sockfd is not a valid file descriptor. 3134 EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. 3135 EINVAL => unreachable, 3136 ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. 3137 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. 3138 else => |err| return unexpectedErrno(err), 3139 } 3140 } 3141 3142 pub fn waitpid(pid: i32, flags: u32) u32 { 3143 // TODO allow implicit pointer cast from *u32 to *c_uint ? 3144 const Status = if (builtin.link_libc) c_uint else u32; 3145 var status: Status = undefined; 3146 while (true) { 3147 switch (errno(system.waitpid(pid, &status, flags))) { 3148 0 => return @bitCast(u32, status), 3149 EINTR => continue, 3150 ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. 3151 EINVAL => unreachable, // The options argument was invalid 3152 else => unreachable, 3153 } 3154 } 3155 } 3156 3157 pub const FStatError = error{ 3158 SystemResources, 3159 3160 /// In WASI, this error may occur when the file descriptor does 3161 /// not hold the required rights to get its filestat information. 3162 AccessDenied, 3163 } || UnexpectedError; 3164 3165 /// Return information about a file descriptor. 3166 pub fn fstat(fd: fd_t) FStatError!Stat { 3167 if (builtin.os.tag == .wasi) { 3168 var stat: wasi.filestat_t = undefined; 3169 switch (wasi.fd_filestat_get(fd, &stat)) { 3170 wasi.ESUCCESS => return Stat.fromFilestat(stat), 3171 wasi.EINVAL => unreachable, 3172 wasi.EBADF => unreachable, // Always a race condition. 3173 wasi.ENOMEM => return error.SystemResources, 3174 wasi.EACCES => return error.AccessDenied, 3175 wasi.ENOTCAPABLE => return error.AccessDenied, 3176 else => |err| return unexpectedErrno(err), 3177 } 3178 } 3179 if (builtin.os.tag == .windows) { 3180 @compileError("fstat is not yet implemented on Windows"); 3181 } 3182 3183 var stat: Stat = undefined; 3184 switch (errno(system.fstat(fd, &stat))) { 3185 0 => return stat, 3186 EINVAL => unreachable, 3187 EBADF => unreachable, // Always a race condition. 3188 ENOMEM => return error.SystemResources, 3189 EACCES => return error.AccessDenied, 3190 else => |err| return unexpectedErrno(err), 3191 } 3192 } 3193 3194 pub const FStatAtError = FStatError || error{ NameTooLong, FileNotFound }; 3195 3196 /// Similar to `fstat`, but returns stat of a resource pointed to by `pathname` 3197 /// which is relative to `dirfd` handle. 3198 /// See also `fstatatZ` and `fstatatWasi`. 3199 pub fn fstatat(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { 3200 if (builtin.os.tag == .wasi) { 3201 return fstatatWasi(dirfd, pathname, flags); 3202 } else if (builtin.os.tag == .windows) { 3203 @compileError("fstatat is not yet implemented on Windows"); 3204 } else { 3205 const pathname_c = try toPosixPath(pathname); 3206 return fstatatZ(dirfd, &pathname_c, flags); 3207 } 3208 } 3209 3210 pub const fstatatC = @compileError("deprecated: renamed to fstatatZ"); 3211 3212 /// WASI-only. Same as `fstatat` but targeting WASI. 3213 /// See also `fstatat`. 3214 pub fn fstatatWasi(dirfd: fd_t, pathname: []const u8, flags: u32) FStatAtError!Stat { 3215 var stat: wasi.filestat_t = undefined; 3216 switch (wasi.path_filestat_get(dirfd, flags, pathname.ptr, pathname.len, &stat)) { 3217 wasi.ESUCCESS => return Stat.fromFilestat(stat), 3218 wasi.EINVAL => unreachable, 3219 wasi.EBADF => unreachable, // Always a race condition. 3220 wasi.ENOMEM => return error.SystemResources, 3221 wasi.EACCES => return error.AccessDenied, 3222 wasi.EFAULT => unreachable, 3223 wasi.ENAMETOOLONG => return error.NameTooLong, 3224 wasi.ENOENT => return error.FileNotFound, 3225 wasi.ENOTDIR => return error.FileNotFound, 3226 wasi.ENOTCAPABLE => return error.AccessDenied, 3227 else => |err| return unexpectedErrno(err), 3228 } 3229 } 3230 3231 /// Same as `fstatat` but `pathname` is null-terminated. 3232 /// See also `fstatat`. 3233 pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!Stat { 3234 var stat: Stat = undefined; 3235 switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) { 3236 0 => return stat, 3237 EINVAL => unreachable, 3238 EBADF => unreachable, // Always a race condition. 3239 ENOMEM => return error.SystemResources, 3240 EACCES => return error.AccessDenied, 3241 EFAULT => unreachable, 3242 ENAMETOOLONG => return error.NameTooLong, 3243 ENOENT => return error.FileNotFound, 3244 ENOTDIR => return error.FileNotFound, 3245 else => |err| return unexpectedErrno(err), 3246 } 3247 } 3248 3249 pub const KQueueError = error{ 3250 /// The per-process limit on the number of open file descriptors has been reached. 3251 ProcessFdQuotaExceeded, 3252 3253 /// The system-wide limit on the total number of open files has been reached. 3254 SystemFdQuotaExceeded, 3255 } || UnexpectedError; 3256 3257 pub fn kqueue() KQueueError!i32 { 3258 const rc = system.kqueue(); 3259 switch (errno(rc)) { 3260 0 => return @intCast(i32, rc), 3261 EMFILE => return error.ProcessFdQuotaExceeded, 3262 ENFILE => return error.SystemFdQuotaExceeded, 3263 else => |err| return unexpectedErrno(err), 3264 } 3265 } 3266 3267 pub const KEventError = error{ 3268 /// The process does not have permission to register a filter. 3269 AccessDenied, 3270 3271 /// The event could not be found to be modified or deleted. 3272 EventNotFound, 3273 3274 /// No memory was available to register the event. 3275 SystemResources, 3276 3277 /// The specified process to attach to does not exist. 3278 ProcessNotFound, 3279 3280 /// changelist or eventlist had too many items on it. 3281 /// TODO remove this possibility 3282 Overflow, 3283 }; 3284 3285 pub fn kevent( 3286 kq: i32, 3287 changelist: []const Kevent, 3288 eventlist: []Kevent, 3289 timeout: ?*const timespec, 3290 ) KEventError!usize { 3291 while (true) { 3292 const rc = system.kevent( 3293 kq, 3294 changelist.ptr, 3295 try math.cast(c_int, changelist.len), 3296 eventlist.ptr, 3297 try math.cast(c_int, eventlist.len), 3298 timeout, 3299 ); 3300 switch (errno(rc)) { 3301 0 => return @intCast(usize, rc), 3302 EACCES => return error.AccessDenied, 3303 EFAULT => unreachable, 3304 EBADF => unreachable, // Always a race condition. 3305 EINTR => continue, 3306 EINVAL => unreachable, 3307 ENOENT => return error.EventNotFound, 3308 ENOMEM => return error.SystemResources, 3309 ESRCH => return error.ProcessNotFound, 3310 else => unreachable, 3311 } 3312 } 3313 } 3314 3315 pub const INotifyInitError = error{ 3316 ProcessFdQuotaExceeded, 3317 SystemFdQuotaExceeded, 3318 SystemResources, 3319 } || UnexpectedError; 3320 3321 /// initialize an inotify instance 3322 pub fn inotify_init1(flags: u32) INotifyInitError!i32 { 3323 const rc = system.inotify_init1(flags); 3324 switch (errno(rc)) { 3325 0 => return @intCast(i32, rc), 3326 EINVAL => unreachable, 3327 EMFILE => return error.ProcessFdQuotaExceeded, 3328 ENFILE => return error.SystemFdQuotaExceeded, 3329 ENOMEM => return error.SystemResources, 3330 else => |err| return unexpectedErrno(err), 3331 } 3332 } 3333 3334 pub const INotifyAddWatchError = error{ 3335 AccessDenied, 3336 NameTooLong, 3337 FileNotFound, 3338 SystemResources, 3339 UserResourceLimitReached, 3340 } || UnexpectedError; 3341 3342 /// add a watch to an initialized inotify instance 3343 pub fn inotify_add_watch(inotify_fd: i32, pathname: []const u8, mask: u32) INotifyAddWatchError!i32 { 3344 const pathname_c = try toPosixPath(pathname); 3345 return inotify_add_watchZ(inotify_fd, &pathname_c, mask); 3346 } 3347 3348 pub const inotify_add_watchC = @compileError("deprecated: renamed to inotify_add_watchZ"); 3349 3350 /// Same as `inotify_add_watch` except pathname is null-terminated. 3351 pub fn inotify_add_watchZ(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) INotifyAddWatchError!i32 { 3352 const rc = system.inotify_add_watch(inotify_fd, pathname, mask); 3353 switch (errno(rc)) { 3354 0 => return @intCast(i32, rc), 3355 EACCES => return error.AccessDenied, 3356 EBADF => unreachable, 3357 EFAULT => unreachable, 3358 EINVAL => unreachable, 3359 ENAMETOOLONG => return error.NameTooLong, 3360 ENOENT => return error.FileNotFound, 3361 ENOMEM => return error.SystemResources, 3362 ENOSPC => return error.UserResourceLimitReached, 3363 else => |err| return unexpectedErrno(err), 3364 } 3365 } 3366 3367 /// remove an existing watch from an inotify instance 3368 pub fn inotify_rm_watch(inotify_fd: i32, wd: i32) void { 3369 switch (errno(system.inotify_rm_watch(inotify_fd, wd))) { 3370 0 => return, 3371 EBADF => unreachable, 3372 EINVAL => unreachable, 3373 else => unreachable, 3374 } 3375 } 3376 3377 pub const MProtectError = error{ 3378 /// The memory cannot be given the specified access. This can happen, for example, if you 3379 /// mmap(2) a file to which you have read-only access, then ask mprotect() to mark it 3380 /// PROT_WRITE. 3381 AccessDenied, 3382 3383 /// Changing the protection of a memory region would result in the total number of map‐ 3384 /// pings with distinct attributes (e.g., read versus read/write protection) exceeding the 3385 /// allowed maximum. (For example, making the protection of a range PROT_READ in the mid‐ 3386 /// dle of a region currently protected as PROT_READ|PROT_WRITE would result in three map‐ 3387 /// pings: two read/write mappings at each end and a read-only mapping in the middle.) 3388 OutOfMemory, 3389 } || UnexpectedError; 3390 3391 /// `memory.len` must be page-aligned. 3392 pub fn mprotect(memory: []align(mem.page_size) u8, protection: u32) MProtectError!void { 3393 assert(mem.isAligned(memory.len, mem.page_size)); 3394 switch (errno(system.mprotect(memory.ptr, memory.len, protection))) { 3395 0 => return, 3396 EINVAL => unreachable, 3397 EACCES => return error.AccessDenied, 3398 ENOMEM => return error.OutOfMemory, 3399 else => |err| return unexpectedErrno(err), 3400 } 3401 } 3402 3403 pub const ForkError = error{SystemResources} || UnexpectedError; 3404 3405 pub fn fork() ForkError!pid_t { 3406 const rc = system.fork(); 3407 switch (errno(rc)) { 3408 0 => return @intCast(pid_t, rc), 3409 EAGAIN => return error.SystemResources, 3410 ENOMEM => return error.SystemResources, 3411 else => |err| return unexpectedErrno(err), 3412 } 3413 } 3414 3415 pub const MMapError = error{ 3416 /// The underlying filesystem of the specified file does not support memory mapping. 3417 MemoryMappingNotSupported, 3418 3419 /// A file descriptor refers to a non-regular file. Or a file mapping was requested, 3420 /// but the file descriptor is not open for reading. Or `MAP_SHARED` was requested 3421 /// and `PROT_WRITE` is set, but the file descriptor is not open in `O_RDWR` mode. 3422 /// Or `PROT_WRITE` is set, but the file is append-only. 3423 AccessDenied, 3424 3425 /// The `prot` argument asks for `PROT_EXEC` but the mapped area belongs to a file on 3426 /// a filesystem that was mounted no-exec. 3427 PermissionDenied, 3428 LockedMemoryLimitExceeded, 3429 OutOfMemory, 3430 } || UnexpectedError; 3431 3432 /// Map files or devices into memory. 3433 /// `length` does not need to be aligned. 3434 /// Use of a mapped region can result in these signals: 3435 /// * SIGSEGV - Attempted write into a region mapped as read-only. 3436 /// * SIGBUS - Attempted access to a portion of the buffer that does not correspond to the file 3437 pub fn mmap( 3438 ptr: ?[*]align(mem.page_size) u8, 3439 length: usize, 3440 prot: u32, 3441 flags: u32, 3442 fd: fd_t, 3443 offset: u64, 3444 ) MMapError![]align(mem.page_size) u8 { 3445 const err = if (builtin.link_libc) blk: { 3446 const rc = std.c.mmap(ptr, length, prot, flags, fd, offset); 3447 if (rc != std.c.MAP_FAILED) return @ptrCast([*]align(mem.page_size) u8, @alignCast(mem.page_size, rc))[0..length]; 3448 break :blk @intCast(usize, system._errno().*); 3449 } else blk: { 3450 const rc = system.mmap(ptr, length, prot, flags, fd, offset); 3451 const err = errno(rc); 3452 if (err == 0) return @intToPtr([*]align(mem.page_size) u8, rc)[0..length]; 3453 break :blk err; 3454 }; 3455 switch (err) { 3456 ETXTBSY => return error.AccessDenied, 3457 EACCES => return error.AccessDenied, 3458 EPERM => return error.PermissionDenied, 3459 EAGAIN => return error.LockedMemoryLimitExceeded, 3460 EBADF => unreachable, // Always a race condition. 3461 EOVERFLOW => unreachable, // The number of pages used for length + offset would overflow. 3462 ENODEV => return error.MemoryMappingNotSupported, 3463 EINVAL => unreachable, // Invalid parameters to mmap() 3464 ENOMEM => return error.OutOfMemory, 3465 else => return unexpectedErrno(err), 3466 } 3467 } 3468 3469 /// Deletes the mappings for the specified address range, causing 3470 /// further references to addresses within the range to generate invalid memory references. 3471 /// Note that while POSIX allows unmapping a region in the middle of an existing mapping, 3472 /// Zig's munmap function does not, for two reasons: 3473 /// * It violates the Zig principle that resource deallocation must succeed. 3474 /// * The Windows function, VirtualFree, has this restriction. 3475 pub fn munmap(memory: []align(mem.page_size) u8) void { 3476 switch (errno(system.munmap(memory.ptr, memory.len))) { 3477 0 => return, 3478 EINVAL => unreachable, // Invalid parameters. 3479 ENOMEM => unreachable, // Attempted to unmap a region in the middle of an existing mapping. 3480 else => unreachable, 3481 } 3482 } 3483 3484 pub const AccessError = error{ 3485 PermissionDenied, 3486 FileNotFound, 3487 NameTooLong, 3488 InputOutput, 3489 SystemResources, 3490 BadPathName, 3491 FileBusy, 3492 SymLinkLoop, 3493 ReadOnlyFileSystem, 3494 3495 /// On Windows, file paths must be valid Unicode. 3496 InvalidUtf8, 3497 } || UnexpectedError; 3498 3499 /// check user's permissions for a file 3500 /// TODO currently this assumes `mode` is `F_OK` on Windows. 3501 pub fn access(path: []const u8, mode: u32) AccessError!void { 3502 if (builtin.os.tag == .windows) { 3503 const path_w = try windows.sliceToPrefixedFileW(path); 3504 _ = try windows.GetFileAttributesW(path_w.span().ptr); 3505 return; 3506 } 3507 const path_c = try toPosixPath(path); 3508 return accessZ(&path_c, mode); 3509 } 3510 3511 pub const accessC = @compileError("Deprecated in favor of `accessZ`"); 3512 3513 /// Same as `access` except `path` is null-terminated. 3514 pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { 3515 if (builtin.os.tag == .windows) { 3516 const path_w = try windows.cStrToPrefixedFileW(path); 3517 _ = try windows.GetFileAttributesW(path_w.span().ptr); 3518 return; 3519 } 3520 switch (errno(system.access(path, mode))) { 3521 0 => return, 3522 EACCES => return error.PermissionDenied, 3523 EROFS => return error.ReadOnlyFileSystem, 3524 ELOOP => return error.SymLinkLoop, 3525 ETXTBSY => return error.FileBusy, 3526 ENOTDIR => return error.FileNotFound, 3527 ENOENT => return error.FileNotFound, 3528 ENAMETOOLONG => return error.NameTooLong, 3529 EINVAL => unreachable, 3530 EFAULT => unreachable, 3531 EIO => return error.InputOutput, 3532 ENOMEM => return error.SystemResources, 3533 else => |err| return unexpectedErrno(err), 3534 } 3535 } 3536 3537 /// Call from Windows-specific code if you already have a UTF-16LE encoded, null terminated string. 3538 /// Otherwise use `access` or `accessC`. 3539 /// TODO currently this ignores `mode`. 3540 pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!void { 3541 const ret = try windows.GetFileAttributesW(path); 3542 if (ret != windows.INVALID_FILE_ATTRIBUTES) { 3543 return; 3544 } 3545 switch (windows.kernel32.GetLastError()) { 3546 .FILE_NOT_FOUND => return error.FileNotFound, 3547 .PATH_NOT_FOUND => return error.FileNotFound, 3548 .ACCESS_DENIED => return error.PermissionDenied, 3549 else => |err| return windows.unexpectedError(err), 3550 } 3551 } 3552 3553 /// Check user's permissions for a file, based on an open directory handle. 3554 /// TODO currently this ignores `mode` and `flags` on Windows. 3555 pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void { 3556 if (builtin.os.tag == .windows) { 3557 const path_w = try windows.sliceToPrefixedFileW(path); 3558 return faccessatW(dirfd, path_w.span().ptr, mode, flags); 3559 } 3560 const path_c = try toPosixPath(path); 3561 return faccessatZ(dirfd, &path_c, mode, flags); 3562 } 3563 3564 /// Same as `faccessat` except the path parameter is null-terminated. 3565 pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void { 3566 if (builtin.os.tag == .windows) { 3567 const path_w = try windows.cStrToPrefixedFileW(path); 3568 return faccessatW(dirfd, path_w.span().ptr, mode, flags); 3569 } 3570 switch (errno(system.faccessat(dirfd, path, mode, flags))) { 3571 0 => return, 3572 EACCES => return error.PermissionDenied, 3573 EROFS => return error.ReadOnlyFileSystem, 3574 ELOOP => return error.SymLinkLoop, 3575 ETXTBSY => return error.FileBusy, 3576 ENOTDIR => return error.FileNotFound, 3577 ENOENT => return error.FileNotFound, 3578 ENAMETOOLONG => return error.NameTooLong, 3579 EINVAL => unreachable, 3580 EFAULT => unreachable, 3581 EIO => return error.InputOutput, 3582 ENOMEM => return error.SystemResources, 3583 else => |err| return unexpectedErrno(err), 3584 } 3585 } 3586 3587 /// Same as `faccessat` except asserts the target is Windows and the path parameter 3588 /// is NtDll-prefixed, null-terminated, WTF-16 encoded. 3589 /// TODO currently this ignores `mode` and `flags` 3590 pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32) AccessError!void { 3591 if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { 3592 return; 3593 } 3594 if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { 3595 return; 3596 } 3597 3598 const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) { 3599 error.Overflow => return error.NameTooLong, 3600 }; 3601 var nt_name = windows.UNICODE_STRING{ 3602 .Length = path_len_bytes, 3603 .MaximumLength = path_len_bytes, 3604 .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), 3605 }; 3606 var attr = windows.OBJECT_ATTRIBUTES{ 3607 .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), 3608 .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd, 3609 .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. 3610 .ObjectName = &nt_name, 3611 .SecurityDescriptor = null, 3612 .SecurityQualityOfService = null, 3613 }; 3614 var basic_info: windows.FILE_BASIC_INFORMATION = undefined; 3615 switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) { 3616 .SUCCESS => return, 3617 .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, 3618 .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, 3619 .INVALID_PARAMETER => unreachable, 3620 .ACCESS_DENIED => return error.PermissionDenied, 3621 .OBJECT_PATH_SYNTAX_BAD => unreachable, 3622 else => |rc| return windows.unexpectedStatus(rc), 3623 } 3624 } 3625 3626 pub const PipeError = error{ 3627 SystemFdQuotaExceeded, 3628 ProcessFdQuotaExceeded, 3629 } || UnexpectedError; 3630 3631 /// Creates a unidirectional data channel that can be used for interprocess communication. 3632 pub fn pipe() PipeError![2]fd_t { 3633 var fds: [2]fd_t = undefined; 3634 switch (errno(system.pipe(&fds))) { 3635 0 => return fds, 3636 EINVAL => unreachable, // Invalid parameters to pipe() 3637 EFAULT => unreachable, // Invalid fds pointer 3638 ENFILE => return error.SystemFdQuotaExceeded, 3639 EMFILE => return error.ProcessFdQuotaExceeded, 3640 else => |err| return unexpectedErrno(err), 3641 } 3642 } 3643 3644 pub fn pipe2(flags: u32) PipeError![2]fd_t { 3645 if (comptime std.Target.current.isDarwin()) { 3646 var fds: [2]fd_t = try pipe(); 3647 if (flags == 0) return fds; 3648 errdefer { 3649 close(fds[0]); 3650 close(fds[1]); 3651 } 3652 for (fds) |fd| switch (errno(system.fcntl(fd, F_SETFL, flags))) { 3653 0 => {}, 3654 EINVAL => unreachable, // Invalid flags 3655 EBADF => unreachable, // Always a race condition 3656 else => |err| return unexpectedErrno(err), 3657 }; 3658 return fds; 3659 } 3660 3661 var fds: [2]fd_t = undefined; 3662 switch (errno(system.pipe2(&fds, flags))) { 3663 0 => return fds, 3664 EINVAL => unreachable, // Invalid flags 3665 EFAULT => unreachable, // Invalid fds pointer 3666 ENFILE => return error.SystemFdQuotaExceeded, 3667 EMFILE => return error.ProcessFdQuotaExceeded, 3668 else => |err| return unexpectedErrno(err), 3669 } 3670 } 3671 3672 pub const SysCtlError = error{ 3673 PermissionDenied, 3674 SystemResources, 3675 NameTooLong, 3676 UnknownName, 3677 } || UnexpectedError; 3678 3679 pub fn sysctl( 3680 name: []const c_int, 3681 oldp: ?*c_void, 3682 oldlenp: ?*usize, 3683 newp: ?*c_void, 3684 newlen: usize, 3685 ) SysCtlError!void { 3686 if (builtin.os.tag == .wasi) { 3687 @panic("unsupported"); 3688 } 3689 3690 const name_len = math.cast(c_uint, name.len) catch return error.NameTooLong; 3691 switch (errno(system.sysctl(name.ptr, name_len, oldp, oldlenp, newp, newlen))) { 3692 0 => return, 3693 EFAULT => unreachable, 3694 EPERM => return error.PermissionDenied, 3695 ENOMEM => return error.SystemResources, 3696 ENOENT => return error.UnknownName, 3697 else => |err| return unexpectedErrno(err), 3698 } 3699 } 3700 3701 pub const sysctlbynameC = @compileError("deprecated: renamed to sysctlbynameZ"); 3702 3703 pub fn sysctlbynameZ( 3704 name: [*:0]const u8, 3705 oldp: ?*c_void, 3706 oldlenp: ?*usize, 3707 newp: ?*c_void, 3708 newlen: usize, 3709 ) SysCtlError!void { 3710 if (builtin.os.tag == .wasi) { 3711 @panic("unsupported"); 3712 } 3713 3714 switch (errno(system.sysctlbyname(name, oldp, oldlenp, newp, newlen))) { 3715 0 => return, 3716 EFAULT => unreachable, 3717 EPERM => return error.PermissionDenied, 3718 ENOMEM => return error.SystemResources, 3719 ENOENT => return error.UnknownName, 3720 else => |err| return unexpectedErrno(err), 3721 } 3722 } 3723 3724 pub fn gettimeofday(tv: ?*timeval, tz: ?*timezone) void { 3725 switch (errno(system.gettimeofday(tv, tz))) { 3726 0 => return, 3727 EINVAL => unreachable, 3728 else => unreachable, 3729 } 3730 } 3731 3732 pub const SeekError = error{ 3733 Unseekable, 3734 3735 /// In WASI, this error may occur when the file descriptor does 3736 /// not hold the required rights to seek on it. 3737 AccessDenied, 3738 } || UnexpectedError; 3739 3740 /// Repositions read/write file offset relative to the beginning. 3741 pub fn lseek_SET(fd: fd_t, offset: u64) SeekError!void { 3742 if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { 3743 var result: u64 = undefined; 3744 switch (errno(system.llseek(fd, offset, &result, SEEK_SET))) { 3745 0 => return, 3746 EBADF => unreachable, // always a race condition 3747 EINVAL => return error.Unseekable, 3748 EOVERFLOW => return error.Unseekable, 3749 ESPIPE => return error.Unseekable, 3750 ENXIO => return error.Unseekable, 3751 else => |err| return unexpectedErrno(err), 3752 } 3753 } 3754 if (builtin.os.tag == .windows) { 3755 return windows.SetFilePointerEx_BEGIN(fd, offset); 3756 } 3757 if (builtin.os.tag == .wasi) { 3758 var new_offset: wasi.filesize_t = undefined; 3759 switch (wasi.fd_seek(fd, @bitCast(wasi.filedelta_t, offset), wasi.WHENCE_SET, &new_offset)) { 3760 wasi.ESUCCESS => return, 3761 wasi.EBADF => unreachable, // always a race condition 3762 wasi.EINVAL => return error.Unseekable, 3763 wasi.EOVERFLOW => return error.Unseekable, 3764 wasi.ESPIPE => return error.Unseekable, 3765 wasi.ENXIO => return error.Unseekable, 3766 wasi.ENOTCAPABLE => return error.AccessDenied, 3767 else => |err| return unexpectedErrno(err), 3768 } 3769 } 3770 const ipos = @bitCast(i64, offset); // the OS treats this as unsigned 3771 switch (errno(system.lseek(fd, ipos, SEEK_SET))) { 3772 0 => return, 3773 EBADF => unreachable, // always a race condition 3774 EINVAL => return error.Unseekable, 3775 EOVERFLOW => return error.Unseekable, 3776 ESPIPE => return error.Unseekable, 3777 ENXIO => return error.Unseekable, 3778 else => |err| return unexpectedErrno(err), 3779 } 3780 } 3781 3782 /// Repositions read/write file offset relative to the current offset. 3783 pub fn lseek_CUR(fd: fd_t, offset: i64) SeekError!void { 3784 if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { 3785 var result: u64 = undefined; 3786 switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_CUR))) { 3787 0 => return, 3788 EBADF => unreachable, // always a race condition 3789 EINVAL => return error.Unseekable, 3790 EOVERFLOW => return error.Unseekable, 3791 ESPIPE => return error.Unseekable, 3792 ENXIO => return error.Unseekable, 3793 else => |err| return unexpectedErrno(err), 3794 } 3795 } 3796 if (builtin.os.tag == .windows) { 3797 return windows.SetFilePointerEx_CURRENT(fd, offset); 3798 } 3799 if (builtin.os.tag == .wasi) { 3800 var new_offset: wasi.filesize_t = undefined; 3801 switch (wasi.fd_seek(fd, offset, wasi.WHENCE_CUR, &new_offset)) { 3802 wasi.ESUCCESS => return, 3803 wasi.EBADF => unreachable, // always a race condition 3804 wasi.EINVAL => return error.Unseekable, 3805 wasi.EOVERFLOW => return error.Unseekable, 3806 wasi.ESPIPE => return error.Unseekable, 3807 wasi.ENXIO => return error.Unseekable, 3808 wasi.ENOTCAPABLE => return error.AccessDenied, 3809 else => |err| return unexpectedErrno(err), 3810 } 3811 } 3812 switch (errno(system.lseek(fd, offset, SEEK_CUR))) { 3813 0 => return, 3814 EBADF => unreachable, // always a race condition 3815 EINVAL => return error.Unseekable, 3816 EOVERFLOW => return error.Unseekable, 3817 ESPIPE => return error.Unseekable, 3818 ENXIO => return error.Unseekable, 3819 else => |err| return unexpectedErrno(err), 3820 } 3821 } 3822 3823 /// Repositions read/write file offset relative to the end. 3824 pub fn lseek_END(fd: fd_t, offset: i64) SeekError!void { 3825 if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { 3826 var result: u64 = undefined; 3827 switch (errno(system.llseek(fd, @bitCast(u64, offset), &result, SEEK_END))) { 3828 0 => return, 3829 EBADF => unreachable, // always a race condition 3830 EINVAL => return error.Unseekable, 3831 EOVERFLOW => return error.Unseekable, 3832 ESPIPE => return error.Unseekable, 3833 ENXIO => return error.Unseekable, 3834 else => |err| return unexpectedErrno(err), 3835 } 3836 } 3837 if (builtin.os.tag == .windows) { 3838 return windows.SetFilePointerEx_END(fd, offset); 3839 } 3840 if (builtin.os.tag == .wasi) { 3841 var new_offset: wasi.filesize_t = undefined; 3842 switch (wasi.fd_seek(fd, offset, wasi.WHENCE_END, &new_offset)) { 3843 wasi.ESUCCESS => return, 3844 wasi.EBADF => unreachable, // always a race condition 3845 wasi.EINVAL => return error.Unseekable, 3846 wasi.EOVERFLOW => return error.Unseekable, 3847 wasi.ESPIPE => return error.Unseekable, 3848 wasi.ENXIO => return error.Unseekable, 3849 wasi.ENOTCAPABLE => return error.AccessDenied, 3850 else => |err| return unexpectedErrno(err), 3851 } 3852 } 3853 switch (errno(system.lseek(fd, offset, SEEK_END))) { 3854 0 => return, 3855 EBADF => unreachable, // always a race condition 3856 EINVAL => return error.Unseekable, 3857 EOVERFLOW => return error.Unseekable, 3858 ESPIPE => return error.Unseekable, 3859 ENXIO => return error.Unseekable, 3860 else => |err| return unexpectedErrno(err), 3861 } 3862 } 3863 3864 /// Returns the read/write file offset relative to the beginning. 3865 pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 { 3866 if (builtin.os.tag == .linux and !builtin.link_libc and @sizeOf(usize) == 4) { 3867 var result: u64 = undefined; 3868 switch (errno(system.llseek(fd, 0, &result, SEEK_CUR))) { 3869 0 => return result, 3870 EBADF => unreachable, // always a race condition 3871 EINVAL => return error.Unseekable, 3872 EOVERFLOW => return error.Unseekable, 3873 ESPIPE => return error.Unseekable, 3874 ENXIO => return error.Unseekable, 3875 else => |err| return unexpectedErrno(err), 3876 } 3877 } 3878 if (builtin.os.tag == .windows) { 3879 return windows.SetFilePointerEx_CURRENT_get(fd); 3880 } 3881 if (builtin.os.tag == .wasi) { 3882 var new_offset: wasi.filesize_t = undefined; 3883 switch (wasi.fd_seek(fd, 0, wasi.WHENCE_CUR, &new_offset)) { 3884 wasi.ESUCCESS => return new_offset, 3885 wasi.EBADF => unreachable, // always a race condition 3886 wasi.EINVAL => return error.Unseekable, 3887 wasi.EOVERFLOW => return error.Unseekable, 3888 wasi.ESPIPE => return error.Unseekable, 3889 wasi.ENXIO => return error.Unseekable, 3890 wasi.ENOTCAPABLE => return error.AccessDenied, 3891 else => |err| return unexpectedErrno(err), 3892 } 3893 } 3894 const rc = system.lseek(fd, 0, SEEK_CUR); 3895 switch (errno(rc)) { 3896 0 => return @bitCast(u64, rc), 3897 EBADF => unreachable, // always a race condition 3898 EINVAL => return error.Unseekable, 3899 EOVERFLOW => return error.Unseekable, 3900 ESPIPE => return error.Unseekable, 3901 ENXIO => return error.Unseekable, 3902 else => |err| return unexpectedErrno(err), 3903 } 3904 } 3905 3906 pub const FcntlError = error{ 3907 PermissionDenied, 3908 FileBusy, 3909 ProcessFdQuotaExceeded, 3910 Locked, 3911 } || UnexpectedError; 3912 3913 pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize { 3914 while (true) { 3915 const rc = system.fcntl(fd, cmd, arg); 3916 switch (errno(rc)) { 3917 0 => return @intCast(usize, rc), 3918 EINTR => continue, 3919 EACCES => return error.Locked, 3920 EBADF => unreachable, 3921 EBUSY => return error.FileBusy, 3922 EINVAL => unreachable, // invalid parameters 3923 EPERM => return error.PermissionDenied, 3924 EMFILE => return error.ProcessFdQuotaExceeded, 3925 ENOTDIR => unreachable, // invalid parameter 3926 else => |err| return unexpectedErrno(err), 3927 } 3928 } 3929 } 3930 3931 fn setSockFlags(fd: fd_t, flags: u32) !void { 3932 if ((flags & SOCK_CLOEXEC) != 0) { 3933 var fd_flags = fcntl(fd, F_GETFD, 0) catch |err| switch (err) { 3934 error.FileBusy => unreachable, 3935 error.Locked => unreachable, 3936 else => |e| return e, 3937 }; 3938 fd_flags |= FD_CLOEXEC; 3939 _ = fcntl(fd, F_SETFD, fd_flags) catch |err| switch (err) { 3940 error.FileBusy => unreachable, 3941 error.Locked => unreachable, 3942 else => |e| return e, 3943 }; 3944 } 3945 if ((flags & SOCK_NONBLOCK) != 0) { 3946 var fl_flags = fcntl(fd, F_GETFL, 0) catch |err| switch (err) { 3947 error.FileBusy => unreachable, 3948 error.Locked => unreachable, 3949 else => |e| return e, 3950 }; 3951 fl_flags |= O_NONBLOCK; 3952 _ = fcntl(fd, F_SETFL, fl_flags) catch |err| switch (err) { 3953 error.FileBusy => unreachable, 3954 error.Locked => unreachable, 3955 else => |e| return e, 3956 }; 3957 } 3958 } 3959 3960 pub const FlockError = error{ 3961 WouldBlock, 3962 3963 /// The kernel ran out of memory for allocating file locks 3964 SystemResources, 3965 } || UnexpectedError; 3966 3967 pub fn flock(fd: fd_t, operation: i32) FlockError!void { 3968 while (true) { 3969 const rc = system.flock(fd, operation); 3970 switch (errno(rc)) { 3971 0 => return, 3972 EBADF => unreachable, 3973 EINTR => continue, 3974 EINVAL => unreachable, // invalid parameters 3975 ENOLCK => return error.SystemResources, 3976 EWOULDBLOCK => return error.WouldBlock, // TODO: integrate with async instead of just returning an error 3977 else => |err| return unexpectedErrno(err), 3978 } 3979 } 3980 } 3981 3982 pub const RealPathError = error{ 3983 FileNotFound, 3984 AccessDenied, 3985 NameTooLong, 3986 NotSupported, 3987 NotDir, 3988 SymLinkLoop, 3989 InputOutput, 3990 FileTooBig, 3991 IsDir, 3992 ProcessFdQuotaExceeded, 3993 SystemFdQuotaExceeded, 3994 NoDevice, 3995 SystemResources, 3996 NoSpaceLeft, 3997 FileSystem, 3998 BadPathName, 3999 DeviceBusy, 4000 4001 SharingViolation, 4002 PipeBusy, 4003 4004 /// On Windows, file paths must be valid Unicode. 4005 InvalidUtf8, 4006 4007 PathAlreadyExists, 4008 } || UnexpectedError; 4009 4010 /// Return the canonicalized absolute pathname. 4011 /// Expands all symbolic links and resolves references to `.`, `..`, and 4012 /// extra `/` characters in `pathname`. 4013 /// The return value is a slice of `out_buffer`, but not necessarily from the beginning. 4014 /// See also `realpathC` and `realpathW`. 4015 pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { 4016 if (builtin.os.tag == .windows) { 4017 const pathname_w = try windows.sliceToPrefixedFileW(pathname); 4018 return realpathW(pathname_w.span(), out_buffer); 4019 } 4020 if (builtin.os.tag == .wasi) { 4021 @compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths"); 4022 } 4023 const pathname_c = try toPosixPath(pathname); 4024 return realpathZ(&pathname_c, out_buffer); 4025 } 4026 4027 pub const realpathC = @compileError("deprecated: renamed realpathZ"); 4028 4029 /// Same as `realpath` except `pathname` is null-terminated. 4030 pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { 4031 if (builtin.os.tag == .windows) { 4032 const pathname_w = try windows.cStrToPrefixedFileW(pathname); 4033 return realpathW(pathname_w.span(), out_buffer); 4034 } 4035 if (!builtin.link_libc) { 4036 const flags = if (builtin.os.tag == .linux) O_PATH | O_NONBLOCK | O_CLOEXEC else O_NONBLOCK | O_CLOEXEC; 4037 const fd = openZ(pathname, flags, 0) catch |err| switch (err) { 4038 error.FileLocksNotSupported => unreachable, 4039 else => |e| return e, 4040 }; 4041 defer close(fd); 4042 4043 return getFdPath(fd, out_buffer); 4044 } 4045 const result_path = std.c.realpath(pathname, out_buffer) orelse switch (std.c._errno().*) { 4046 EINVAL => unreachable, 4047 EBADF => unreachable, 4048 EFAULT => unreachable, 4049 EACCES => return error.AccessDenied, 4050 ENOENT => return error.FileNotFound, 4051 ENOTSUP => return error.NotSupported, 4052 ENOTDIR => return error.NotDir, 4053 ENAMETOOLONG => return error.NameTooLong, 4054 ELOOP => return error.SymLinkLoop, 4055 EIO => return error.InputOutput, 4056 else => |err| return unexpectedErrno(@intCast(usize, err)), 4057 }; 4058 return mem.spanZ(result_path); 4059 } 4060 4061 /// Same as `realpath` except `pathname` is UTF16LE-encoded. 4062 pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { 4063 const w = windows; 4064 4065 const dir = std.fs.cwd().fd; 4066 const access_mask = w.GENERIC_READ | w.SYNCHRONIZE; 4067 const share_access = w.FILE_SHARE_READ; 4068 const creation = w.FILE_OPEN; 4069 const h_file = blk: { 4070 const res = w.OpenFile(pathname, .{ 4071 .dir = dir, 4072 .access_mask = access_mask, 4073 .share_access = share_access, 4074 .creation = creation, 4075 .io_mode = .blocking, 4076 }) catch |err| switch (err) { 4077 error.IsDir => break :blk w.OpenFile(pathname, .{ 4078 .dir = dir, 4079 .access_mask = access_mask, 4080 .share_access = share_access, 4081 .creation = creation, 4082 .io_mode = .blocking, 4083 .open_dir = true, 4084 }) catch |er| switch (er) { 4085 error.WouldBlock => unreachable, 4086 else => |e2| return e2, 4087 }, 4088 error.WouldBlock => unreachable, 4089 else => |e| return e, 4090 }; 4091 break :blk res; 4092 }; 4093 defer w.CloseHandle(h_file); 4094 4095 return getFdPath(h_file, out_buffer); 4096 } 4097 4098 /// Return canonical path of handle `fd`. 4099 /// This function is very host-specific and is not universally supported by all hosts. 4100 /// For example, while it generally works on Linux, macOS or Windows, it is unsupported 4101 /// on FreeBSD, or WASI. 4102 pub fn getFdPath(fd: fd_t, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 { 4103 switch (builtin.os.tag) { 4104 .windows => { 4105 var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined; 4106 const wide_slice = try windows.GetFinalPathNameByHandle(fd, .{}, wide_buf[0..]); 4107 4108 // Trust that Windows gives us valid UTF-16LE. 4109 const end_index = std.unicode.utf16leToUtf8(out_buffer, wide_slice) catch unreachable; 4110 return out_buffer[0..end_index]; 4111 }, 4112 .macosx, .ios, .watchos, .tvos => { 4113 // On macOS, we can use F_GETPATH fcntl command to query the OS for 4114 // the path to the file descriptor. 4115 @memset(out_buffer, 0, MAX_PATH_BYTES); 4116 switch (errno(system.fcntl(fd, F_GETPATH, out_buffer))) { 4117 0 => {}, 4118 EBADF => return error.FileNotFound, 4119 // TODO man pages for fcntl on macOS don't really tell you what 4120 // errno values to expect when command is F_GETPATH... 4121 else => |err| return unexpectedErrno(err), 4122 } 4123 const len = mem.indexOfScalar(u8, out_buffer[0..], @as(u8, 0)) orelse MAX_PATH_BYTES; 4124 return out_buffer[0..len]; 4125 }, 4126 .linux => { 4127 var procfs_buf: ["/proc/self/fd/-2147483648".len:0]u8 = undefined; 4128 const proc_path = std.fmt.bufPrint(procfs_buf[0..], "/proc/self/fd/{}\x00", .{fd}) catch unreachable; 4129 4130 const target = readlinkZ(@ptrCast([*:0]const u8, proc_path.ptr), out_buffer) catch |err| { 4131 switch (err) { 4132 error.UnsupportedReparsePointType => unreachable, // Windows only, 4133 else => |e| return e, 4134 } 4135 }; 4136 return target; 4137 }, 4138 else => @compileError("querying for canonical path of a handle is unsupported on this host"), 4139 } 4140 } 4141 4142 /// Spurious wakeups are possible and no precision of timing is guaranteed. 4143 pub fn nanosleep(seconds: u64, nanoseconds: u64) void { 4144 var req = timespec{ 4145 .tv_sec = math.cast(isize, seconds) catch math.maxInt(isize), 4146 .tv_nsec = math.cast(isize, nanoseconds) catch math.maxInt(isize), 4147 }; 4148 var rem: timespec = undefined; 4149 while (true) { 4150 switch (errno(system.nanosleep(&req, &rem))) { 4151 EFAULT => unreachable, 4152 EINVAL => { 4153 // Sometimes Darwin returns EINVAL for no reason. 4154 // We treat it as a spurious wakeup. 4155 return; 4156 }, 4157 EINTR => { 4158 req = rem; 4159 continue; 4160 }, 4161 // This prong handles success as well as unexpected errors. 4162 else => return, 4163 } 4164 } 4165 } 4166 4167 pub fn dl_iterate_phdr( 4168 context: anytype, 4169 comptime Error: type, 4170 comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void, 4171 ) Error!void { 4172 const Context = @TypeOf(context); 4173 4174 if (builtin.object_format != .elf) 4175 @compileError("dl_iterate_phdr is not available for this target"); 4176 4177 if (builtin.link_libc) { 4178 switch (system.dl_iterate_phdr(struct { 4179 fn callbackC(info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int { 4180 const context_ptr = @ptrCast(*const Context, @alignCast(@alignOf(*const Context), data)); 4181 callback(info, size, context_ptr.*) catch |err| return @errorToInt(err); 4182 return 0; 4183 } 4184 }.callbackC, @intToPtr(?*c_void, @ptrToInt(&context)))) { 4185 0 => return, 4186 else => |err| return @errSetCast(Error, @intToError(@intCast(u16, err))), // TODO don't hardcode u16 4187 } 4188 } 4189 4190 const elf_base = std.process.getBaseAddress(); 4191 const ehdr = @intToPtr(*elf.Ehdr, elf_base); 4192 // Make sure the base address points to an ELF image 4193 assert(mem.eql(u8, ehdr.e_ident[0..4], "\x7fELF")); 4194 const n_phdr = ehdr.e_phnum; 4195 const phdrs = (@intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff))[0..n_phdr]; 4196 4197 var it = dl.linkmap_iterator(phdrs) catch unreachable; 4198 4199 // The executable has no dynamic link segment, create a single entry for 4200 // the whole ELF image 4201 if (it.end()) { 4202 var info = dl_phdr_info{ 4203 .dlpi_addr = 0, 4204 .dlpi_name = "/proc/self/exe", 4205 .dlpi_phdr = phdrs.ptr, 4206 .dlpi_phnum = ehdr.e_phnum, 4207 }; 4208 4209 return callback(&info, @sizeOf(dl_phdr_info), context); 4210 } 4211 4212 // Last return value from the callback function 4213 while (it.next()) |entry| { 4214 var dlpi_phdr: [*]elf.Phdr = undefined; 4215 var dlpi_phnum: u16 = undefined; 4216 4217 if (entry.l_addr != 0) { 4218 const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr); 4219 dlpi_phdr = @intToPtr([*]elf.Phdr, entry.l_addr + elf_header.e_phoff); 4220 dlpi_phnum = elf_header.e_phnum; 4221 } else { 4222 // This is the running ELF image 4223 dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + ehdr.e_phoff); 4224 dlpi_phnum = ehdr.e_phnum; 4225 } 4226 4227 var info = dl_phdr_info{ 4228 .dlpi_addr = entry.l_addr, 4229 .dlpi_name = entry.l_name, 4230 .dlpi_phdr = dlpi_phdr, 4231 .dlpi_phnum = dlpi_phnum, 4232 }; 4233 4234 try callback(&info, @sizeOf(dl_phdr_info), context); 4235 } 4236 } 4237 4238 pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; 4239 4240 /// TODO: change this to return the timespec as a return value 4241 /// TODO: look into making clk_id an enum 4242 pub fn clock_gettime(clk_id: i32, tp: *timespec) ClockGetTimeError!void { 4243 if (std.Target.current.os.tag == .wasi) { 4244 var ts: timestamp_t = undefined; 4245 switch (system.clock_time_get(@bitCast(u32, clk_id), 1, &ts)) { 4246 0 => { 4247 tp.* = .{ 4248 .tv_sec = @intCast(i64, ts / std.time.ns_per_s), 4249 .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), 4250 }; 4251 }, 4252 EINVAL => return error.UnsupportedClock, 4253 else => |err| return unexpectedErrno(err), 4254 } 4255 return; 4256 } 4257 if (std.Target.current.os.tag == .windows) { 4258 if (clk_id == CLOCK_REALTIME) { 4259 var ft: windows.FILETIME = undefined; 4260 windows.kernel32.GetSystemTimeAsFileTime(&ft); 4261 // FileTime has a granularity of 100 nanoseconds and uses the NTFS/Windows epoch. 4262 const ft64 = (@as(u64, ft.dwHighDateTime) << 32) | ft.dwLowDateTime; 4263 const ft_per_s = std.time.ns_per_s / 100; 4264 tp.* = .{ 4265 .tv_sec = @intCast(i64, ft64 / ft_per_s) + std.time.epoch.windows, 4266 .tv_nsec = @intCast(c_long, ft64 % ft_per_s) * 100, 4267 }; 4268 return; 4269 } else { 4270 // TODO POSIX implementation of CLOCK_MONOTONIC on Windows. 4271 return error.UnsupportedClock; 4272 } 4273 } 4274 4275 switch (errno(system.clock_gettime(clk_id, tp))) { 4276 0 => return, 4277 EFAULT => unreachable, 4278 EINVAL => return error.UnsupportedClock, 4279 else => |err| return unexpectedErrno(err), 4280 } 4281 } 4282 4283 pub fn clock_getres(clk_id: i32, res: *timespec) ClockGetTimeError!void { 4284 if (std.Target.current.os.tag == .wasi) { 4285 var ts: timestamp_t = undefined; 4286 switch (system.clock_res_get(@bitCast(u32, clk_id), &ts)) { 4287 0 => res.* = .{ 4288 .tv_sec = @intCast(i64, ts / std.time.ns_per_s), 4289 .tv_nsec = @intCast(isize, ts % std.time.ns_per_s), 4290 }, 4291 EINVAL => return error.UnsupportedClock, 4292 else => |err| return unexpectedErrno(err), 4293 } 4294 return; 4295 } 4296 4297 switch (errno(system.clock_getres(clk_id, res))) { 4298 0 => return, 4299 EFAULT => unreachable, 4300 EINVAL => return error.UnsupportedClock, 4301 else => |err| return unexpectedErrno(err), 4302 } 4303 } 4304 4305 pub const SchedGetAffinityError = error{PermissionDenied} || UnexpectedError; 4306 4307 pub fn sched_getaffinity(pid: pid_t) SchedGetAffinityError!cpu_set_t { 4308 var set: cpu_set_t = undefined; 4309 switch (errno(system.sched_getaffinity(pid, @sizeOf(cpu_set_t), &set))) { 4310 0 => return set, 4311 EFAULT => unreachable, 4312 EINVAL => unreachable, 4313 ESRCH => unreachable, 4314 EPERM => return error.PermissionDenied, 4315 else => |err| return unexpectedErrno(err), 4316 } 4317 } 4318 4319 /// Used to convert a slice to a null terminated slice on the stack. 4320 /// TODO https://github.com/ziglang/zig/issues/287 4321 pub fn toPosixPath(file_path: []const u8) ![PATH_MAX - 1:0]u8 { 4322 if (std.debug.runtime_safety) assert(std.mem.indexOfScalar(u8, file_path, 0) == null); 4323 var path_with_null: [PATH_MAX - 1:0]u8 = undefined; 4324 // >= rather than > to make room for the null byte 4325 if (file_path.len >= PATH_MAX) return error.NameTooLong; 4326 mem.copy(u8, &path_with_null, file_path); 4327 path_with_null[file_path.len] = 0; 4328 return path_with_null; 4329 } 4330 4331 /// Whether or not error.Unexpected will print its value and a stack trace. 4332 /// if this happens the fix is to add the error code to the corresponding 4333 /// switch expression, possibly introduce a new error in the error set, and 4334 /// send a patch to Zig. 4335 pub const unexpected_error_tracing = builtin.mode == .Debug; 4336 4337 pub const UnexpectedError = error{ 4338 /// The Operating System returned an undocumented error code. 4339 /// This error is in theory not possible, but it would be better 4340 /// to handle this error than to invoke undefined behavior. 4341 Unexpected, 4342 }; 4343 4344 /// Call this when you made a syscall or something that sets errno 4345 /// and you get an unexpected error. 4346 pub fn unexpectedErrno(err: usize) UnexpectedError { 4347 if (unexpected_error_tracing) { 4348 std.debug.warn("unexpected errno: {}\n", .{err}); 4349 std.debug.dumpCurrentStackTrace(null); 4350 } 4351 return error.Unexpected; 4352 } 4353 4354 pub const SigaltstackError = error{ 4355 /// The supplied stack size was less than MINSIGSTKSZ. 4356 SizeTooSmall, 4357 4358 /// Attempted to change the signal stack while it was active. 4359 PermissionDenied, 4360 } || UnexpectedError; 4361 4362 pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void { 4363 switch (errno(system.sigaltstack(ss, old_ss))) { 4364 0 => return, 4365 EFAULT => unreachable, 4366 EINVAL => unreachable, 4367 ENOMEM => return error.SizeTooSmall, 4368 EPERM => return error.PermissionDenied, 4369 else => |err| return unexpectedErrno(err), 4370 } 4371 } 4372 4373 /// Examine and change a signal action. 4374 pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void { 4375 switch (errno(system.sigaction(sig, act, oact))) { 4376 0 => return, 4377 EFAULT => unreachable, 4378 EINVAL => unreachable, 4379 else => unreachable, 4380 } 4381 } 4382 4383 pub const FutimensError = error{ 4384 /// times is NULL, or both tv_nsec values are UTIME_NOW, and either: 4385 /// * the effective user ID of the caller does not match the owner 4386 /// of the file, the caller does not have write access to the 4387 /// file, and the caller is not privileged (Linux: does not have 4388 /// either the CAP_FOWNER or the CAP_DAC_OVERRIDE capability); 4389 /// or, 4390 /// * the file is marked immutable (see chattr(1)). 4391 AccessDenied, 4392 4393 /// The caller attempted to change one or both timestamps to a value 4394 /// other than the current time, or to change one of the timestamps 4395 /// to the current time while leaving the other timestamp unchanged, 4396 /// (i.e., times is not NULL, neither tv_nsec field is UTIME_NOW, 4397 /// and neither tv_nsec field is UTIME_OMIT) and either: 4398 /// * the caller's effective user ID does not match the owner of 4399 /// file, and the caller is not privileged (Linux: does not have 4400 /// the CAP_FOWNER capability); or, 4401 /// * the file is marked append-only or immutable (see chattr(1)). 4402 PermissionDenied, 4403 4404 ReadOnlyFileSystem, 4405 } || UnexpectedError; 4406 4407 pub fn futimens(fd: fd_t, times: *const [2]timespec) FutimensError!void { 4408 if (builtin.os.tag == .wasi) { 4409 // TODO WASI encodes `wasi.fstflags` to signify magic values 4410 // similar to UTIME_NOW and UTIME_OMIT. Currently, we ignore 4411 // this here, but we should really handle it somehow. 4412 const atim = times[0].toTimestamp(); 4413 const mtim = times[1].toTimestamp(); 4414 switch (wasi.fd_filestat_set_times(fd, atim, mtim, wasi.FILESTAT_SET_ATIM | wasi.FILESTAT_SET_MTIM)) { 4415 wasi.ESUCCESS => return, 4416 wasi.EACCES => return error.AccessDenied, 4417 wasi.EPERM => return error.PermissionDenied, 4418 wasi.EBADF => unreachable, // always a race condition 4419 wasi.EFAULT => unreachable, 4420 wasi.EINVAL => unreachable, 4421 wasi.EROFS => return error.ReadOnlyFileSystem, 4422 else => |err| return unexpectedErrno(err), 4423 } 4424 } 4425 4426 switch (errno(system.futimens(fd, times))) { 4427 0 => return, 4428 EACCES => return error.AccessDenied, 4429 EPERM => return error.PermissionDenied, 4430 EBADF => unreachable, // always a race condition 4431 EFAULT => unreachable, 4432 EINVAL => unreachable, 4433 EROFS => return error.ReadOnlyFileSystem, 4434 else => |err| return unexpectedErrno(err), 4435 } 4436 } 4437 4438 pub const GetHostNameError = error{PermissionDenied} || UnexpectedError; 4439 4440 pub fn gethostname(name_buffer: *[HOST_NAME_MAX]u8) GetHostNameError![]u8 { 4441 if (builtin.link_libc) { 4442 switch (errno(system.gethostname(name_buffer, name_buffer.len))) { 4443 0 => return mem.spanZ(@ptrCast([*:0]u8, name_buffer)), 4444 EFAULT => unreachable, 4445 ENAMETOOLONG => unreachable, // HOST_NAME_MAX prevents this 4446 EPERM => return error.PermissionDenied, 4447 else => |err| return unexpectedErrno(err), 4448 } 4449 } 4450 if (builtin.os.tag == .linux) { 4451 const uts = uname(); 4452 const hostname = mem.spanZ(@ptrCast([*:0]const u8, &uts.nodename)); 4453 mem.copy(u8, name_buffer, hostname); 4454 return name_buffer[0..hostname.len]; 4455 } 4456 4457 @compileError("TODO implement gethostname for this OS"); 4458 } 4459 4460 pub fn uname() utsname { 4461 var uts: utsname = undefined; 4462 switch (errno(system.uname(&uts))) { 4463 0 => return uts, 4464 EFAULT => unreachable, 4465 else => unreachable, 4466 } 4467 } 4468 4469 pub fn res_mkquery( 4470 op: u4, 4471 dname: []const u8, 4472 class: u8, 4473 ty: u8, 4474 data: []const u8, 4475 newrr: ?[*]const u8, 4476 buf: []u8, 4477 ) usize { 4478 // This implementation is ported from musl libc. 4479 // A more idiomatic "ziggy" implementation would be welcome. 4480 var name = dname; 4481 if (mem.endsWith(u8, name, ".")) name.len -= 1; 4482 assert(name.len <= 253); 4483 const n = 17 + name.len + @boolToInt(name.len != 0); 4484 4485 // Construct query template - ID will be filled later 4486 var q: [280]u8 = undefined; 4487 @memset(&q, 0, n); 4488 q[2] = @as(u8, op) * 8 + 1; 4489 q[5] = 1; 4490 mem.copy(u8, q[13..], name); 4491 var i: usize = 13; 4492 var j: usize = undefined; 4493 while (q[i] != 0) : (i = j + 1) { 4494 j = i; 4495 while (q[j] != 0 and q[j] != '.') : (j += 1) {} 4496 // TODO determine the circumstances for this and whether or 4497 // not this should be an error. 4498 if (j - i - 1 > 62) unreachable; 4499 q[i - 1] = @intCast(u8, j - i); 4500 } 4501 q[i + 1] = ty; 4502 q[i + 3] = class; 4503 4504 // Make a reasonably unpredictable id 4505 var ts: timespec = undefined; 4506 clock_gettime(CLOCK_REALTIME, &ts) catch {}; 4507 const UInt = std.meta.Int(false, @TypeOf(ts.tv_nsec).bit_count); 4508 const unsec = @bitCast(UInt, ts.tv_nsec); 4509 const id = @truncate(u32, unsec + unsec / 65536); 4510 q[0] = @truncate(u8, id / 256); 4511 q[1] = @truncate(u8, id); 4512 4513 mem.copy(u8, buf, q[0..n]); 4514 return n; 4515 } 4516 4517 pub const SendError = error{ 4518 /// (For UNIX domain sockets, which are identified by pathname) Write permission is denied 4519 /// on the destination socket file, or search permission is denied for one of the 4520 /// directories the path prefix. (See path_resolution(7).) 4521 /// (For UDP sockets) An attempt was made to send to a network/broadcast address as though 4522 /// it was a unicast address. 4523 AccessDenied, 4524 4525 /// The socket is marked nonblocking and the requested operation would block, and 4526 /// there is no global event loop configured. 4527 /// It's also possible to get this error under the following condition: 4528 /// (Internet domain datagram sockets) The socket referred to by sockfd had not previously 4529 /// been bound to an address and, upon attempting to bind it to an ephemeral port, it was 4530 /// determined that all port numbers in the ephemeral port range are currently in use. See 4531 /// the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). 4532 WouldBlock, 4533 4534 /// Another Fast Open is already in progress. 4535 FastOpenAlreadyInProgress, 4536 4537 /// Connection reset by peer. 4538 ConnectionResetByPeer, 4539 4540 /// The socket type requires that message be sent atomically, and the size of the message 4541 /// to be sent made this impossible. The message is not transmitted. 4542 MessageTooBig, 4543 4544 /// The output queue for a network interface was full. This generally indicates that the 4545 /// interface has stopped sending, but may be caused by transient congestion. (Normally, 4546 /// this does not occur in Linux. Packets are just silently dropped when a device queue 4547 /// overflows.) 4548 /// This is also caused when there is not enough kernel memory available. 4549 SystemResources, 4550 4551 /// The local end has been shut down on a connection oriented socket. In this case, the 4552 /// process will also receive a SIGPIPE unless MSG_NOSIGNAL is set. 4553 BrokenPipe, 4554 } || UnexpectedError; 4555 4556 /// Transmit a message to another socket. 4557 /// 4558 /// The `sendto` call may be used only when the socket is in a connected state (so that the intended 4559 /// recipient is known). The following call 4560 /// 4561 /// send(sockfd, buf, len, flags); 4562 /// 4563 /// is equivalent to 4564 /// 4565 /// sendto(sockfd, buf, len, flags, NULL, 0); 4566 /// 4567 /// If sendto() is used on a connection-mode (`SOCK_STREAM`, `SOCK_SEQPACKET`) socket, the arguments 4568 /// `dest_addr` and `addrlen` are asserted to be `null` and `0` respectively, and asserted 4569 /// that the socket was actually connected. 4570 /// Otherwise, the address of the target is given by `dest_addr` with `addrlen` specifying its size. 4571 /// 4572 /// If the message is too long to pass atomically through the underlying protocol, 4573 /// `SendError.MessageTooBig` is returned, and the message is not transmitted. 4574 /// 4575 /// There is no indication of failure to deliver. 4576 /// 4577 /// When the message does not fit into the send buffer of the socket, `sendto` normally blocks, 4578 /// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail 4579 /// with `SendError.WouldBlock`. The `select` call may be used to determine when it is 4580 /// possible to send more data. 4581 pub fn sendto( 4582 /// The file descriptor of the sending socket. 4583 sockfd: fd_t, 4584 /// Message to send. 4585 buf: []const u8, 4586 flags: u32, 4587 dest_addr: ?*const sockaddr, 4588 addrlen: socklen_t, 4589 ) SendError!usize { 4590 while (true) { 4591 const rc = system.sendto(sockfd, buf.ptr, buf.len, flags, dest_addr, addrlen); 4592 switch (errno(rc)) { 4593 0 => return @intCast(usize, rc), 4594 4595 EACCES => return error.AccessDenied, 4596 EAGAIN => if (std.event.Loop.instance) |loop| { 4597 loop.waitUntilFdWritable(sockfd); 4598 continue; 4599 } else { 4600 return error.WouldBlock; 4601 }, 4602 EALREADY => return error.FastOpenAlreadyInProgress, 4603 EBADF => unreachable, // always a race condition 4604 ECONNRESET => return error.ConnectionResetByPeer, 4605 EDESTADDRREQ => unreachable, // The socket is not connection-mode, and no peer address is set. 4606 EFAULT => unreachable, // An invalid user space address was specified for an argument. 4607 EINTR => continue, 4608 EINVAL => unreachable, // Invalid argument passed. 4609 EISCONN => unreachable, // connection-mode socket was connected already but a recipient was specified 4610 EMSGSIZE => return error.MessageTooBig, 4611 ENOBUFS => return error.SystemResources, 4612 ENOMEM => return error.SystemResources, 4613 ENOTCONN => unreachable, // The socket is not connected, and no target has been given. 4614 ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. 4615 EOPNOTSUPP => unreachable, // Some bit in the flags argument is inappropriate for the socket type. 4616 EPIPE => return error.BrokenPipe, 4617 else => |err| return unexpectedErrno(err), 4618 } 4619 } 4620 } 4621 4622 /// Transmit a message to another socket. 4623 /// 4624 /// The `send` call may be used only when the socket is in a connected state (so that the intended 4625 /// recipient is known). The only difference between `send` and `write` is the presence of 4626 /// flags. With a zero flags argument, `send` is equivalent to `write`. Also, the following 4627 /// call 4628 /// 4629 /// send(sockfd, buf, len, flags); 4630 /// 4631 /// is equivalent to 4632 /// 4633 /// sendto(sockfd, buf, len, flags, NULL, 0); 4634 /// 4635 /// There is no indication of failure to deliver. 4636 /// 4637 /// When the message does not fit into the send buffer of the socket, `send` normally blocks, 4638 /// unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail 4639 /// with `SendError.WouldBlock`. The `select` call may be used to determine when it is 4640 /// possible to send more data. 4641 pub fn send( 4642 /// The file descriptor of the sending socket. 4643 sockfd: fd_t, 4644 buf: []const u8, 4645 flags: u32, 4646 ) SendError!usize { 4647 return sendto(sockfd, buf, flags, null, 0); 4648 } 4649 4650 pub const SendFileError = PReadError || WriteError || SendError; 4651 4652 fn count_iovec_bytes(iovs: []const iovec_const) usize { 4653 var count: usize = 0; 4654 for (iovs) |iov| { 4655 count += iov.iov_len; 4656 } 4657 return count; 4658 } 4659 4660 /// Transfer data between file descriptors, with optional headers and trailers. 4661 /// Returns the number of bytes written, which can be zero. 4662 /// 4663 /// The `sendfile` call copies `in_len` bytes from one file descriptor to another. When possible, 4664 /// this is done within the operating system kernel, which can provide better performance 4665 /// characteristics than transferring data from kernel to user space and back, such as with 4666 /// `read` and `write` calls. When `in_len` is `0`, it means to copy until the end of the input file has been 4667 /// reached. Note, however, that partial writes are still possible in this case. 4668 /// 4669 /// `in_fd` must be a file descriptor opened for reading, and `out_fd` must be a file descriptor 4670 /// opened for writing. They may be any kind of file descriptor; however, if `in_fd` is not a regular 4671 /// file system file, it may cause this function to fall back to calling `read` and `write`, in which case 4672 /// atomicity guarantees no longer apply. 4673 /// 4674 /// Copying begins reading at `in_offset`. The input file descriptor seek position is ignored and not updated. 4675 /// If the output file descriptor has a seek position, it is updated as bytes are written. When 4676 /// `in_offset` is past the end of the input file, it successfully reads 0 bytes. 4677 /// 4678 /// `flags` has different meanings per operating system; refer to the respective man pages. 4679 /// 4680 /// These systems support atomically sending everything, including headers and trailers: 4681 /// * macOS 4682 /// * FreeBSD 4683 /// 4684 /// These systems support in-kernel data copying, but headers and trailers are not sent atomically: 4685 /// * Linux 4686 /// 4687 /// Other systems fall back to calling `read` / `write`. 4688 /// 4689 /// Linux has a limit on how many bytes may be transferred in one `sendfile` call, which is `0x7ffff000` 4690 /// on both 64-bit and 32-bit systems. This is due to using a signed C int as the return value, as 4691 /// well as stuffing the errno codes into the last `4096` values. This is cited on the `sendfile` man page. 4692 /// The corresponding POSIX limit on this is `math.maxInt(isize)`. 4693 pub fn sendfile( 4694 out_fd: fd_t, 4695 in_fd: fd_t, 4696 in_offset: u64, 4697 in_len: u64, 4698 headers: []const iovec_const, 4699 trailers: []const iovec_const, 4700 flags: u32, 4701 ) SendFileError!usize { 4702 var header_done = false; 4703 var total_written: usize = 0; 4704 4705 // Prevents EOVERFLOW. 4706 const size_t = @Type(std.builtin.TypeInfo{ 4707 .Int = .{ 4708 .is_signed = false, 4709 .bits = @typeInfo(usize).Int.bits - 1, 4710 }, 4711 }); 4712 const max_count = switch (std.Target.current.os.tag) { 4713 .linux => 0x7ffff000, 4714 else => math.maxInt(size_t), 4715 }; 4716 4717 switch (std.Target.current.os.tag) { 4718 .linux => sf: { 4719 // sendfile() first appeared in Linux 2.2, glibc 2.1. 4720 const call_sf = comptime if (builtin.link_libc) 4721 std.c.versionCheck(.{ .major = 2, .minor = 1 }).ok 4722 else 4723 std.Target.current.os.version_range.linux.range.max.order(.{ .major = 2, .minor = 2 }) != .lt; 4724 if (!call_sf) break :sf; 4725 4726 if (headers.len != 0) { 4727 const amt = try writev(out_fd, headers); 4728 total_written += amt; 4729 if (amt < count_iovec_bytes(headers)) return total_written; 4730 header_done = true; 4731 } 4732 4733 // Here we match BSD behavior, making a zero count value send as many bytes as possible. 4734 const adjusted_count = if (in_len == 0) max_count else math.min(in_len, @as(size_t, max_count)); 4735 4736 while (true) { 4737 var offset: off_t = @bitCast(off_t, in_offset); 4738 const rc = system.sendfile(out_fd, in_fd, &offset, adjusted_count); 4739 switch (errno(rc)) { 4740 0 => { 4741 const amt = @bitCast(usize, rc); 4742 total_written += amt; 4743 if (in_len == 0 and amt == 0) { 4744 // We have detected EOF from `in_fd`. 4745 break; 4746 } else if (amt < in_len) { 4747 return total_written; 4748 } else { 4749 break; 4750 } 4751 }, 4752 4753 EBADF => unreachable, // Always a race condition. 4754 EFAULT => unreachable, // Segmentation fault. 4755 EOVERFLOW => unreachable, // We avoid passing too large of a `count`. 4756 ENOTCONN => unreachable, // `out_fd` is an unconnected socket. 4757 4758 EINVAL, ENOSYS => { 4759 // EINVAL could be any of the following situations: 4760 // * Descriptor is not valid or locked 4761 // * an mmap(2)-like operation is not available for in_fd 4762 // * count is negative 4763 // * out_fd has the O_APPEND flag set 4764 // Because of the "mmap(2)-like operation" possibility, we fall back to doing read/write 4765 // manually, the same as ENOSYS. 4766 break :sf; 4767 }, 4768 EAGAIN => if (std.event.Loop.instance) |loop| { 4769 loop.waitUntilFdWritable(out_fd); 4770 continue; 4771 } else { 4772 return error.WouldBlock; 4773 }, 4774 EIO => return error.InputOutput, 4775 EPIPE => return error.BrokenPipe, 4776 ENOMEM => return error.SystemResources, 4777 ENXIO => return error.Unseekable, 4778 ESPIPE => return error.Unseekable, 4779 else => |err| { 4780 const discard = unexpectedErrno(err); 4781 break :sf; 4782 }, 4783 } 4784 } 4785 4786 if (trailers.len != 0) { 4787 total_written += try writev(out_fd, trailers); 4788 } 4789 4790 return total_written; 4791 }, 4792 .freebsd => sf: { 4793 var hdtr_data: std.c.sf_hdtr = undefined; 4794 var hdtr: ?*std.c.sf_hdtr = null; 4795 if (headers.len != 0 or trailers.len != 0) { 4796 // Here we carefully avoid `@intCast` by returning partial writes when 4797 // too many io vectors are provided. 4798 const hdr_cnt = math.cast(u31, headers.len) catch math.maxInt(u31); 4799 if (headers.len > hdr_cnt) return writev(out_fd, headers); 4800 4801 const trl_cnt = math.cast(u31, trailers.len) catch math.maxInt(u31); 4802 4803 hdtr_data = std.c.sf_hdtr{ 4804 .headers = headers.ptr, 4805 .hdr_cnt = hdr_cnt, 4806 .trailers = trailers.ptr, 4807 .trl_cnt = trl_cnt, 4808 }; 4809 hdtr = &hdtr_data; 4810 } 4811 4812 const adjusted_count = math.min(in_len, max_count); 4813 4814 while (true) { 4815 var sbytes: off_t = undefined; 4816 const offset = @bitCast(off_t, in_offset); 4817 const err = errno(system.sendfile(in_fd, out_fd, offset, adjusted_count, hdtr, &sbytes, flags)); 4818 const amt = @bitCast(usize, sbytes); 4819 switch (err) { 4820 0 => return amt, 4821 4822 EBADF => unreachable, // Always a race condition. 4823 EFAULT => unreachable, // Segmentation fault. 4824 ENOTCONN => unreachable, // `out_fd` is an unconnected socket. 4825 4826 EINVAL, EOPNOTSUPP, ENOTSOCK, ENOSYS => { 4827 // EINVAL could be any of the following situations: 4828 // * The fd argument is not a regular file. 4829 // * The s argument is not a SOCK_STREAM type socket. 4830 // * The offset argument is negative. 4831 // Because of some of these possibilities, we fall back to doing read/write 4832 // manually, the same as ENOSYS. 4833 break :sf; 4834 }, 4835 4836 EINTR => if (amt != 0) return amt else continue, 4837 4838 EAGAIN => if (amt != 0) { 4839 return amt; 4840 } else if (std.event.Loop.instance) |loop| { 4841 loop.waitUntilFdWritable(out_fd); 4842 continue; 4843 } else { 4844 return error.WouldBlock; 4845 }, 4846 4847 EBUSY => if (amt != 0) { 4848 return amt; 4849 } else if (std.event.Loop.instance) |loop| { 4850 loop.waitUntilFdReadable(in_fd); 4851 continue; 4852 } else { 4853 return error.WouldBlock; 4854 }, 4855 4856 EIO => return error.InputOutput, 4857 ENOBUFS => return error.SystemResources, 4858 EPIPE => return error.BrokenPipe, 4859 4860 else => { 4861 const discard = unexpectedErrno(err); 4862 if (amt != 0) { 4863 return amt; 4864 } else { 4865 break :sf; 4866 } 4867 }, 4868 } 4869 } 4870 }, 4871 .macosx, .ios, .tvos, .watchos => sf: { 4872 var hdtr_data: std.c.sf_hdtr = undefined; 4873 var hdtr: ?*std.c.sf_hdtr = null; 4874 if (headers.len != 0 or trailers.len != 0) { 4875 // Here we carefully avoid `@intCast` by returning partial writes when 4876 // too many io vectors are provided. 4877 const hdr_cnt = math.cast(u31, headers.len) catch math.maxInt(u31); 4878 if (headers.len > hdr_cnt) return writev(out_fd, headers); 4879 4880 const trl_cnt = math.cast(u31, trailers.len) catch math.maxInt(u31); 4881 4882 hdtr_data = std.c.sf_hdtr{ 4883 .headers = headers.ptr, 4884 .hdr_cnt = hdr_cnt, 4885 .trailers = trailers.ptr, 4886 .trl_cnt = trl_cnt, 4887 }; 4888 hdtr = &hdtr_data; 4889 } 4890 4891 const adjusted_count = math.min(in_len, @as(u63, max_count)); 4892 4893 while (true) { 4894 var sbytes: off_t = adjusted_count; 4895 const signed_offset = @bitCast(i64, in_offset); 4896 const err = errno(system.sendfile(in_fd, out_fd, signed_offset, &sbytes, hdtr, flags)); 4897 const amt = @bitCast(usize, sbytes); 4898 switch (err) { 4899 0 => return amt, 4900 4901 EBADF => unreachable, // Always a race condition. 4902 EFAULT => unreachable, // Segmentation fault. 4903 EINVAL => unreachable, 4904 ENOTCONN => unreachable, // `out_fd` is an unconnected socket. 4905 4906 ENOTSUP, ENOTSOCK, ENOSYS => break :sf, 4907 4908 EINTR => if (amt != 0) return amt else continue, 4909 4910 EAGAIN => if (amt != 0) { 4911 return amt; 4912 } else if (std.event.Loop.instance) |loop| { 4913 loop.waitUntilFdWritable(out_fd); 4914 continue; 4915 } else { 4916 return error.WouldBlock; 4917 }, 4918 4919 EIO => return error.InputOutput, 4920 EPIPE => return error.BrokenPipe, 4921 4922 else => { 4923 const discard = unexpectedErrno(err); 4924 if (amt != 0) { 4925 return amt; 4926 } else { 4927 break :sf; 4928 } 4929 }, 4930 } 4931 } 4932 }, 4933 else => {}, // fall back to read/write 4934 } 4935 4936 if (headers.len != 0 and !header_done) { 4937 const amt = try writev(out_fd, headers); 4938 total_written += amt; 4939 if (amt < count_iovec_bytes(headers)) return total_written; 4940 } 4941 4942 rw: { 4943 var buf: [8 * 4096]u8 = undefined; 4944 // Here we match BSD behavior, making a zero count value send as many bytes as possible. 4945 const adjusted_count = if (in_len == 0) buf.len else math.min(buf.len, in_len); 4946 const amt_read = try pread(in_fd, buf[0..adjusted_count], in_offset); 4947 if (amt_read == 0) { 4948 if (in_len == 0) { 4949 // We have detected EOF from `in_fd`. 4950 break :rw; 4951 } else { 4952 return total_written; 4953 } 4954 } 4955 const amt_written = try write(out_fd, buf[0..amt_read]); 4956 total_written += amt_written; 4957 if (amt_written < in_len or in_len == 0) return total_written; 4958 } 4959 4960 if (trailers.len != 0) { 4961 total_written += try writev(out_fd, trailers); 4962 } 4963 4964 return total_written; 4965 } 4966 4967 pub const CopyFileRangeError = error{ 4968 FileTooBig, 4969 InputOutput, 4970 IsDir, 4971 OutOfMemory, 4972 NoSpaceLeft, 4973 Unseekable, 4974 PermissionDenied, 4975 FileBusy, 4976 } || PReadError || PWriteError || UnexpectedError; 4977 4978 /// Transfer data between file descriptors at specified offsets. 4979 /// Returns the number of bytes written, which can less than requested. 4980 /// 4981 /// The `copy_file_range` call copies `len` bytes from one file descriptor to another. When possible, 4982 /// this is done within the operating system kernel, which can provide better performance 4983 /// characteristics than transferring data from kernel to user space and back, such as with 4984 /// `pread` and `pwrite` calls. 4985 /// 4986 /// `fd_in` must be a file descriptor opened for reading, and `fd_out` must be a file descriptor 4987 /// opened for writing. They may be any kind of file descriptor; however, if `fd_in` is not a regular 4988 /// file system file, it may cause this function to fall back to calling `pread` and `pwrite`, in which case 4989 /// atomicity guarantees no longer apply. 4990 /// 4991 /// If `fd_in` and `fd_out` are the same, source and target ranges must not overlap. 4992 /// The file descriptor seek positions are ignored and not updated. 4993 /// When `off_in` is past the end of the input file, it successfully reads 0 bytes. 4994 /// 4995 /// `flags` has different meanings per operating system; refer to the respective man pages. 4996 /// 4997 /// These systems support in-kernel data copying: 4998 /// * Linux 4.5 (cross-filesystem 5.3) 4999 /// 5000 /// Other systems fall back to calling `pread` / `pwrite`. 5001 /// 5002 /// Maximum offsets on Linux are `math.maxInt(i64)`. 5003 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 { 5004 const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok; 5005 5006 // TODO support for other systems than linux 5007 const try_syscall = comptime std.Target.current.os.isAtLeast(.linux, .{ .major = 4, .minor = 5 }) != false; 5008 5009 if (use_c or try_syscall) { 5010 const sys = if (use_c) std.c else linux; 5011 5012 var off_in_copy = @bitCast(i64, off_in); 5013 var off_out_copy = @bitCast(i64, off_out); 5014 5015 const rc = sys.copy_file_range(fd_in, &off_in_copy, fd_out, &off_out_copy, len, flags); 5016 5017 // TODO avoid wasting a syscall every time if kernel is too old and returns ENOSYS https://github.com/ziglang/zig/issues/1018 5018 5019 switch (sys.getErrno(rc)) { 5020 0 => return @intCast(usize, rc), 5021 EBADF => unreachable, 5022 EFBIG => return error.FileTooBig, 5023 EIO => return error.InputOutput, 5024 EISDIR => return error.IsDir, 5025 ENOMEM => return error.OutOfMemory, 5026 ENOSPC => return error.NoSpaceLeft, 5027 EOVERFLOW => return error.Unseekable, 5028 EPERM => return error.PermissionDenied, 5029 ETXTBSY => return error.FileBusy, 5030 EINVAL => {}, // these may not be regular files, try fallback 5031 EXDEV => {}, // support for cross-filesystem copy added in Linux 5.3, use fallback 5032 ENOSYS => {}, // syscall added in Linux 4.5, use fallback 5033 else => |err| return unexpectedErrno(err), 5034 } 5035 } 5036 5037 var buf: [8 * 4096]u8 = undefined; 5038 const adjusted_count = math.min(buf.len, len); 5039 const amt_read = try pread(fd_in, buf[0..adjusted_count], off_in); 5040 // TODO without @as the line below fails to compile for wasm32-wasi: 5041 // error: integer value 0 cannot be coerced to type 'os.PWriteError!usize' 5042 if (amt_read == 0) return @as(usize, 0); 5043 return pwrite(fd_out, buf[0..amt_read], off_out); 5044 } 5045 5046 pub const PollError = error{ 5047 /// The kernel had no space to allocate file descriptor tables. 5048 SystemResources, 5049 } || UnexpectedError; 5050 5051 pub fn poll(fds: []pollfd, timeout: i32) PollError!usize { 5052 while (true) { 5053 const rc = system.poll(fds.ptr, fds.len, timeout); 5054 switch (errno(rc)) { 5055 0 => return @intCast(usize, rc), 5056 EFAULT => unreachable, 5057 EINTR => continue, 5058 EINVAL => unreachable, 5059 ENOMEM => return error.SystemResources, 5060 else => |err| return unexpectedErrno(err), 5061 } 5062 } 5063 } 5064 5065 pub const RecvFromError = error{ 5066 /// The socket is marked nonblocking and the requested operation would block, and 5067 /// there is no global event loop configured. 5068 WouldBlock, 5069 5070 /// A remote host refused to allow the network connection, typically because it is not 5071 /// running the requested service. 5072 ConnectionRefused, 5073 5074 /// Could not allocate kernel memory. 5075 SystemResources, 5076 } || UnexpectedError; 5077 5078 pub fn recvfrom( 5079 sockfd: fd_t, 5080 buf: []u8, 5081 flags: u32, 5082 src_addr: ?*sockaddr, 5083 addrlen: ?*socklen_t, 5084 ) RecvFromError!usize { 5085 while (true) { 5086 const rc = system.recvfrom(sockfd, buf.ptr, buf.len, flags, src_addr, addrlen); 5087 switch (errno(rc)) { 5088 0 => return @intCast(usize, rc), 5089 EBADF => unreachable, // always a race condition 5090 EFAULT => unreachable, 5091 EINVAL => unreachable, 5092 ENOTCONN => unreachable, 5093 ENOTSOCK => unreachable, 5094 EINTR => continue, 5095 EAGAIN => if (std.event.Loop.instance) |loop| { 5096 loop.waitUntilFdReadable(sockfd); 5097 continue; 5098 } else { 5099 return error.WouldBlock; 5100 }, 5101 ENOMEM => return error.SystemResources, 5102 ECONNREFUSED => return error.ConnectionRefused, 5103 else => |err| return unexpectedErrno(err), 5104 } 5105 } 5106 } 5107 5108 pub const DnExpandError = error{InvalidDnsPacket}; 5109 5110 pub fn dn_expand( 5111 msg: []const u8, 5112 comp_dn: []const u8, 5113 exp_dn: []u8, 5114 ) DnExpandError!usize { 5115 // This implementation is ported from musl libc. 5116 // A more idiomatic "ziggy" implementation would be welcome. 5117 var p = comp_dn.ptr; 5118 var len: usize = std.math.maxInt(usize); 5119 const end = msg.ptr + msg.len; 5120 if (p == end or exp_dn.len == 0) return error.InvalidDnsPacket; 5121 var dest = exp_dn.ptr; 5122 const dend = dest + std.math.min(exp_dn.len, 254); 5123 // detect reference loop using an iteration counter 5124 var i: usize = 0; 5125 while (i < msg.len) : (i += 2) { 5126 // loop invariants: p<end, dest<dend 5127 if ((p[0] & 0xc0) != 0) { 5128 if (p + 1 == end) return error.InvalidDnsPacket; 5129 var j = ((p[0] & @as(usize, 0x3f)) << 8) | p[1]; 5130 if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 2 - @ptrToInt(comp_dn.ptr); 5131 if (j >= msg.len) return error.InvalidDnsPacket; 5132 p = msg.ptr + j; 5133 } else if (p[0] != 0) { 5134 if (dest != exp_dn.ptr) { 5135 dest.* = '.'; 5136 dest += 1; 5137 } 5138 var j = p[0]; 5139 p += 1; 5140 if (j >= @ptrToInt(end) - @ptrToInt(p) or j >= @ptrToInt(dend) - @ptrToInt(dest)) { 5141 return error.InvalidDnsPacket; 5142 } 5143 while (j != 0) { 5144 j -= 1; 5145 dest.* = p[0]; 5146 dest += 1; 5147 p += 1; 5148 } 5149 } else { 5150 dest.* = 0; 5151 if (len == std.math.maxInt(usize)) len = @ptrToInt(p) + 1 - @ptrToInt(comp_dn.ptr); 5152 return len; 5153 } 5154 } 5155 return error.InvalidDnsPacket; 5156 } 5157 5158 pub const SchedYieldError = error{ 5159 /// The system is not configured to allow yielding 5160 SystemCannotYield, 5161 }; 5162 5163 pub fn sched_yield() SchedYieldError!void { 5164 if (builtin.os.tag == .windows) { 5165 // The return value has to do with how many other threads there are; it is not 5166 // an error condition on Windows. 5167 _ = windows.kernel32.SwitchToThread(); 5168 return; 5169 } 5170 switch (errno(system.sched_yield())) { 5171 0 => return, 5172 ENOSYS => return error.SystemCannotYield, 5173 else => return error.SystemCannotYield, 5174 } 5175 } 5176 5177 pub const SetSockOptError = error{ 5178 /// The socket is already connected, and a specified option cannot be set while the socket is connected. 5179 AlreadyConnected, 5180 5181 /// The option is not supported by the protocol. 5182 InvalidProtocolOption, 5183 5184 /// The send and receive timeout values are too big to fit into the timeout fields in the socket structure. 5185 TimeoutTooBig, 5186 5187 /// Insufficient resources are available in the system to complete the call. 5188 SystemResources, 5189 } || UnexpectedError; 5190 5191 /// Set a socket's options. 5192 pub fn setsockopt(fd: fd_t, level: u32, optname: u32, opt: []const u8) SetSockOptError!void { 5193 switch (errno(system.setsockopt(fd, level, optname, opt.ptr, @intCast(socklen_t, opt.len)))) { 5194 0 => {}, 5195 EBADF => unreachable, // always a race condition 5196 ENOTSOCK => unreachable, // always a race condition 5197 EINVAL => unreachable, 5198 EFAULT => unreachable, 5199 EDOM => return error.TimeoutTooBig, 5200 EISCONN => return error.AlreadyConnected, 5201 ENOPROTOOPT => return error.InvalidProtocolOption, 5202 ENOMEM => return error.SystemResources, 5203 ENOBUFS => return error.SystemResources, 5204 else => |err| return unexpectedErrno(err), 5205 } 5206 } 5207 5208 pub const MemFdCreateError = error{ 5209 SystemFdQuotaExceeded, 5210 ProcessFdQuotaExceeded, 5211 OutOfMemory, 5212 5213 /// memfd_create is available in Linux 3.17 and later. This error is returned 5214 /// for older kernel versions. 5215 SystemOutdated, 5216 } || UnexpectedError; 5217 5218 pub const memfd_createC = @compileError("deprecated: renamed to memfd_createZ"); 5219 5220 pub fn memfd_createZ(name: [*:0]const u8, flags: u32) MemFdCreateError!fd_t { 5221 // memfd_create is available only in glibc versions starting with 2.27. 5222 const use_c = std.c.versionCheck(.{ .major = 2, .minor = 27, .patch = 0 }).ok; 5223 const sys = if (use_c) std.c else linux; 5224 const getErrno = if (use_c) std.c.getErrno else linux.getErrno; 5225 const rc = sys.memfd_create(name, flags); 5226 switch (getErrno(rc)) { 5227 0 => return @intCast(fd_t, rc), 5228 EFAULT => unreachable, // name has invalid memory 5229 EINVAL => unreachable, // name/flags are faulty 5230 ENFILE => return error.SystemFdQuotaExceeded, 5231 EMFILE => return error.ProcessFdQuotaExceeded, 5232 ENOMEM => return error.OutOfMemory, 5233 ENOSYS => return error.SystemOutdated, 5234 else => |err| return unexpectedErrno(err), 5235 } 5236 } 5237 5238 pub const MFD_NAME_PREFIX = "memfd:"; 5239 pub const MFD_MAX_NAME_LEN = NAME_MAX - MFD_NAME_PREFIX.len; 5240 fn toMemFdPath(name: []const u8) ![MFD_MAX_NAME_LEN:0]u8 { 5241 var path_with_null: [MFD_MAX_NAME_LEN:0]u8 = undefined; 5242 // >= rather than > to make room for the null byte 5243 if (name.len >= MFD_MAX_NAME_LEN) return error.NameTooLong; 5244 mem.copy(u8, &path_with_null, name); 5245 path_with_null[name.len] = 0; 5246 return path_with_null; 5247 } 5248 5249 pub fn memfd_create(name: []const u8, flags: u32) !fd_t { 5250 const name_t = try toMemFdPath(name); 5251 return memfd_createZ(&name_t, flags); 5252 } 5253 5254 pub fn getrusage(who: i32) rusage { 5255 var result: rusage = undefined; 5256 const rc = system.getrusage(who, &result); 5257 switch (errno(rc)) { 5258 0 => return result, 5259 EINVAL => unreachable, 5260 EFAULT => unreachable, 5261 else => unreachable, 5262 } 5263 } 5264 5265 pub const TermiosGetError = error{NotATerminal} || UnexpectedError; 5266 5267 pub fn tcgetattr(handle: fd_t) TermiosGetError!termios { 5268 while (true) { 5269 var term: termios = undefined; 5270 switch (errno(system.tcgetattr(handle, &term))) { 5271 0 => return term, 5272 EINTR => continue, 5273 EBADF => unreachable, 5274 ENOTTY => return error.NotATerminal, 5275 else => |err| return unexpectedErrno(err), 5276 } 5277 } 5278 } 5279 5280 pub const TermiosSetError = TermiosGetError || error{ProcessOrphaned}; 5281 5282 pub fn tcsetattr(handle: fd_t, optional_action: TCSA, termios_p: termios) TermiosSetError!void { 5283 while (true) { 5284 switch (errno(system.tcsetattr(handle, optional_action, &termios_p))) { 5285 0 => return, 5286 EBADF => unreachable, 5287 EINTR => continue, 5288 EINVAL => unreachable, 5289 ENOTTY => return error.NotATerminal, 5290 EIO => return error.ProcessOrphaned, 5291 else => |err| return unexpectedErrno(err), 5292 } 5293 } 5294 } 5295 5296 const IoCtl_SIOCGIFINDEX_Error = error{ 5297 FileSystem, 5298 InterfaceNotFound, 5299 } || UnexpectedError; 5300 5301 pub fn ioctl_SIOCGIFINDEX(fd: fd_t, ifr: *ifreq) IoCtl_SIOCGIFINDEX_Error!void { 5302 while (true) { 5303 switch (errno(system.ioctl(fd, SIOCGIFINDEX, @ptrToInt(ifr)))) { 5304 0 => return, 5305 EINVAL => unreachable, // Bad parameters. 5306 ENOTTY => unreachable, 5307 ENXIO => unreachable, 5308 EBADF => unreachable, // Always a race condition. 5309 EFAULT => unreachable, // Bad pointer parameter. 5310 EINTR => continue, 5311 EIO => return error.FileSystem, 5312 ENODEV => return error.InterfaceNotFound, 5313 else => |err| return unexpectedErrno(err), 5314 } 5315 } 5316 } 5317 5318 pub fn signalfd(fd: fd_t, mask: *const sigset_t, flags: u32) !fd_t { 5319 const rc = system.signalfd(fd, mask, flags); 5320 switch (errno(rc)) { 5321 0 => return @intCast(fd_t, rc), 5322 EBADF, EINVAL => unreachable, 5323 ENFILE => return error.SystemFdQuotaExceeded, 5324 ENOMEM => return error.SystemResources, 5325 EMFILE => return error.ProcessResources, 5326 ENODEV => return error.InodeMountFail, 5327 ENOSYS => return error.SystemOutdated, 5328 else => |err| return std.os.unexpectedErrno(err), 5329 } 5330 }