| ... | @@ -398,6 +398,485 @@ pub const File = struct { | ... | @@ -398,6 +398,485 @@ pub const File = struct { |
| 398 | try os.fchown(self.handle, owner, group); | 398 | try os.fchown(self.handle, owner, group); |
| 399 | } | 399 | } |
| 400 | | 400 | |
| | 401 | /// Cross-platform representation of permissions on a file. |
| | 402 | /// The `readonly` and `setReadonly` are the only methods available across all platforms. |
| | 403 | /// Platform-specific functionality is available through the `inner` field. |
| | 404 | pub const Permissions = struct { |
| | 405 | /// You may use the `inner` field to use platform-specific functionality |
| | 406 | inner: switch (builtin.os.tag) { |
| | 407 | .windows => PermissionsWindows, |
| | 408 | else => PermissionsUnix, |
| | 409 | }, |
| | 410 | |
| | 411 | const Self = @This(); |
| | 412 | |
| | 413 | /// Returns `true` if permissions represent an unwritable file. |
| | 414 | /// On Unix, `true` is returned only if no class has write permissions. |
| | 415 | pub fn readOnly(self: Self) bool { |
| | 416 | return self.inner.readOnly(); |
| | 417 | } |
| | 418 | |
| | 419 | /// Sets whether write permissions are provided. |
| | 420 | /// On Unix, this affects *all* classes. If this is undesired, use `unixSet` |
| | 421 | /// This method *DOES NOT* set permissions on the filesystem: use `File.setPermissions(permissions)` |
| | 422 | pub fn setReadOnly(self: *Self, read_only: bool) void { |
| | 423 | self.inner.setReadOnly(read_only); |
| | 424 | } |
| | 425 | }; |
| | 426 | |
| | 427 | pub const PermissionsWindows = struct { |
| | 428 | attributes: os.windows.DWORD, |
| | 429 | |
| | 430 | const Self = @This(); |
| | 431 | |
| | 432 | /// Returns `true` if permissions represent an unwritable file. |
| | 433 | pub fn readOnly(self: Self) bool { |
| | 434 | return self.attributes & os.windows.FILE_ATTRIBUTE_READONLY != 0; |
| | 435 | } |
| | 436 | |
| | 437 | /// Sets whether write permissions are provided. |
| | 438 | /// This method *DOES NOT* set permissions on the filesystem: use `File.setPermissions(permissions)` |
| | 439 | pub fn setReadOnly(self: *Self, read_only: bool) void { |
| | 440 | if (read_only) { |
| | 441 | self.attributes |= os.windows.FILE_ATTRIBUTE_READONLY; |
| | 442 | } else { |
| | 443 | self.attributes &= ~@as(os.windows.DWORD, os.windows.FILE_ATTRIBUTE_READONLY); |
| | 444 | } |
| | 445 | } |
| | 446 | }; |
| | 447 | |
| | 448 | pub const PermissionsUnix = struct { |
| | 449 | mode: Mode, |
| | 450 | |
| | 451 | const Self = @This(); |
| | 452 | |
| | 453 | /// Returns `true` if permissions represent an unwritable file. |
| | 454 | /// `true` is returned only if no class has write permissions. |
| | 455 | pub fn readOnly(self: Self) bool { |
| | 456 | return self.mode & 0o222 == 0; |
| | 457 | } |
| | 458 | |
| | 459 | /// Sets whether write permissions are provided. |
| | 460 | /// This affects *all* classes. If this is undesired, use `unixSet` |
| | 461 | /// This method *DOES NOT* set permissions on the filesystem: use `File.setPermissions(permissions)` |
| | 462 | pub fn setReadOnly(self: *Self, read_only: bool) void { |
| | 463 | if (read_only) { |
| | 464 | self.mode &= ~@as(Mode, 0o222); |
| | 465 | } else { |
| | 466 | self.mode |= @as(Mode, 0o222); |
| | 467 | } |
| | 468 | } |
| | 469 | |
| | 470 | pub const Class = enum(u2) { |
| | 471 | user = 2, |
| | 472 | group = 1, |
| | 473 | other = 0, |
| | 474 | }; |
| | 475 | |
| | 476 | pub const Permission = enum(u3) { |
| | 477 | read = 0o4, |
| | 478 | write = 0o2, |
| | 479 | execute = 0o1, |
| | 480 | }; |
| | 481 | |
| | 482 | /// Returns `true` if the chosen class has the selected permission. |
| | 483 | /// This method is only available on Unix platforms. |
| | 484 | pub fn unixHas(self: Self, class: Class, permission: Permission) bool { |
| | 485 | const mask = @as(Mode, @enumToInt(permission)) << @as(u3, @enumToInt(class)) * 3; |
| | 486 | return self.mode & mask != 0; |
| | 487 | } |
| | 488 | |
| | 489 | /// Sets the permissions for the chosen class. Any permissions set to `null` are left unchanged. |
| | 490 | /// This method *DOES NOT* set permissions on the filesystem: use `File.setPermissions(permissions)` |
| | 491 | pub fn unixSet(self: *Self, class: Class, permissions: struct { |
| | 492 | read: ?bool = null, |
| | 493 | write: ?bool = null, |
| | 494 | execute: ?bool = null, |
| | 495 | }) void { |
| | 496 | const shift = @as(u3, @enumToInt(class)) * 3; |
| | 497 | if (permissions.read) |r| { |
| | 498 | if (r) { |
| | 499 | self.mode |= @as(Mode, 0o4) << shift; |
| | 500 | } else { |
| | 501 | self.mode &= ~(@as(Mode, 0o4) << shift); |
| | 502 | } |
| | 503 | } |
| | 504 | if (permissions.write) |w| { |
| | 505 | if (w) { |
| | 506 | self.mode |= @as(Mode, 0o2) << shift; |
| | 507 | } else { |
| | 508 | self.mode &= ~(@as(Mode, 0o2) << shift); |
| | 509 | } |
| | 510 | } |
| | 511 | if (permissions.execute) |x| { |
| | 512 | if (x) { |
| | 513 | self.mode |= @as(Mode, 0o1) << shift; |
| | 514 | } else { |
| | 515 | self.mode &= ~(@as(Mode, 0o1) << shift); |
| | 516 | } |
| | 517 | } |
| | 518 | } |
| | 519 | |
| | 520 | /// Returns a `Permissions` struct representing the permissions from the passed mode. |
| | 521 | pub fn unixNew(new_mode: Mode) Self { |
| | 522 | return Self{ |
| | 523 | .mode = new_mode, |
| | 524 | }; |
| | 525 | } |
| | 526 | }; |
| | 527 | |
| | 528 | pub const SetPermissionsError = ChmodError; |
| | 529 | |
| | 530 | /// Sets permissions according to the provided `Permissions` struct. |
| | 531 | /// This method is *NOT* available on WASI |
| | 532 | pub fn setPermissions(self: File, permissions: Permissions) SetPermissionsError!void { |
| | 533 | switch (builtin.os.tag) { |
| | 534 | .windows => { |
| | 535 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| | 536 | var info = windows.FILE_BASIC_INFORMATION{ |
| | 537 | .CreationTime = 0, |
| | 538 | .LastAccessTime = 0, |
| | 539 | .LastWriteTime = 0, |
| | 540 | .ChangeTime = 0, |
| | 541 | .FileAttributes = permissions.inner.attributes, |
| | 542 | }; |
| | 543 | const rc = windows.ntdll.NtSetInformationFile( |
| | 544 | self.handle, |
| | 545 | &io_status_block, |
| | 546 | &info, |
| | 547 | @sizeOf(windows.FILE_BASIC_INFORMATION), |
| | 548 | .FileBasicInformation, |
| | 549 | ); |
| | 550 | switch (rc) { |
| | 551 | .SUCCESS => return, |
| | 552 | .INVALID_HANDLE => unreachable, |
| | 553 | .ACCESS_DENIED => return error.AccessDenied, |
| | 554 | else => return windows.unexpectedStatus(rc), |
| | 555 | } |
| | 556 | }, |
| | 557 | .wasi => @compileError("Unsupported OS"), // Wasi filesystem does not *yet* support chmod |
| | 558 | else => { |
| | 559 | try self.chmod(permissions.inner.mode); |
| | 560 | }, |
| | 561 | } |
| | 562 | } |
| | 563 | |
| | 564 | /// Cross-platform representation of file metadata. |
| | 565 | /// Platform-specific functionality is available through the `inner` field. |
| | 566 | pub const Metadata = struct { |
| | 567 | /// You may use the `inner` field to use platform-specific functionality |
| | 568 | inner: switch (builtin.os.tag) { |
| | 569 | .windows => MetadataWindows, |
| | 570 | .linux => MetadataLinux, |
| | 571 | else => MetadataUnix, |
| | 572 | }, |
| | 573 | |
| | 574 | const Self = @This(); |
| | 575 | |
| | 576 | /// Returns the size of the file |
| | 577 | pub fn size(self: Self) u64 { |
| | 578 | return self.inner.size(); |
| | 579 | } |
| | 580 | |
| | 581 | /// Returns a `Permissions` struct, representing the permissions on the file |
| | 582 | pub fn permissions(self: Self) Permissions { |
| | 583 | return self.inner.permissions(); |
| | 584 | } |
| | 585 | |
| | 586 | /// Returns the `Kind` of file. |
| | 587 | /// On Windows, can only return: `.File`, `.Directory`, `.SymLink` or `.Unknown` |
| | 588 | pub fn kind(self: Self) Kind { |
| | 589 | return self.inner.kind(); |
| | 590 | } |
| | 591 | |
| | 592 | /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01 |
| | 593 | pub fn accessed(self: Self) i128 { |
| | 594 | return self.inner.accessed(); |
| | 595 | } |
| | 596 | |
| | 597 | /// Returns the time the file was modified in nanoseconds since UTC 1970-01-01 |
| | 598 | pub fn modified(self: Self) i128 { |
| | 599 | return self.inner.modified(); |
| | 600 | } |
| | 601 | |
| | 602 | /// Returns the time the file was created in nanoseconds since UTC 1970-01-01 |
| | 603 | /// On Windows, this cannot return null |
| | 604 | /// On Linux, this returns null if the filesystem does not support creation times, or if the kernel is older than 4.11 |
| | 605 | /// On Unices, this returns null if the filesystem or OS does not support creation times |
| | 606 | /// On MacOS, this returns the ctime if the filesystem does not support creation times; this is insanity, and yet another reason to hate on Apple |
| | 607 | pub fn created(self: Self) ?i128 { |
| | 608 | return self.inner.created(); |
| | 609 | } |
| | 610 | }; |
| | 611 | |
| | 612 | pub const MetadataUnix = struct { |
| | 613 | stat: os.Stat, |
| | 614 | |
| | 615 | const Self = @This(); |
| | 616 | |
| | 617 | /// Returns the size of the file |
| | 618 | pub fn size(self: Self) u64 { |
| | 619 | return @intCast(u64, self.stat.size); |
| | 620 | } |
| | 621 | |
| | 622 | /// Returns a `Permissions` struct, representing the permissions on the file |
| | 623 | pub fn permissions(self: Self) Permissions { |
| | 624 | return Permissions{ .inner = PermissionsUnix{ .mode = self.stat.mode } }; |
| | 625 | } |
| | 626 | |
| | 627 | /// Returns the `Kind` of the file |
| | 628 | pub fn kind(self: Self) Kind { |
| | 629 | if (builtin.os.tag == .wasi and !builtin.link_libc) return switch (self.stat.filetype) { |
| | 630 | .BLOCK_DEVICE => Kind.BlockDevice, |
| | 631 | .CHARACTER_DEVICE => Kind.CharacterDevice, |
| | 632 | .DIRECTORY => Kind.Directory, |
| | 633 | .SYMBOLIC_LINK => Kind.SymLink, |
| | 634 | .REGULAR_FILE => Kind.File, |
| | 635 | .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket, |
| | 636 | else => Kind.Unknown, |
| | 637 | }; |
| | 638 | |
| | 639 | const m = self.stat.mode & os.S.IFMT; |
| | 640 | |
| | 641 | switch (m) { |
| | 642 | os.S.IFBLK => return Kind.BlockDevice, |
| | 643 | os.S.IFCHR => return Kind.CharacterDevice, |
| | 644 | os.S.IFDIR => return Kind.Directory, |
| | 645 | os.S.IFIFO => return Kind.NamedPipe, |
| | 646 | os.S.IFLNK => return Kind.SymLink, |
| | 647 | os.S.IFREG => return Kind.File, |
| | 648 | os.S.IFSOCK => return Kind.UnixDomainSocket, |
| | 649 | else => {}, |
| | 650 | } |
| | 651 | |
| | 652 | if (builtin.os.tag == .solaris) switch (m) { |
| | 653 | os.S.IFDOOR => return Kind.Door, |
| | 654 | os.S.IFPORT => return Kind.EventPort, |
| | 655 | else => {}, |
| | 656 | }; |
| | 657 | |
| | 658 | return .Unknown; |
| | 659 | } |
| | 660 | |
| | 661 | /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01 |
| | 662 | pub fn accessed(self: Self) i128 { |
| | 663 | const atime = self.stat.atime(); |
| | 664 | return @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec; |
| | 665 | } |
| | 666 | |
| | 667 | /// Returns the last time the file was modified in nanoseconds since UTC 1970-01-01 |
| | 668 | pub fn modified(self: Self) i128 { |
| | 669 | const mtime = self.stat.mtime(); |
| | 670 | return @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec; |
| | 671 | } |
| | 672 | |
| | 673 | /// Returns the time the file was created in nanoseconds since UTC 1970-01-01 |
| | 674 | /// Returns null if this is not supported by the OS or filesystem |
| | 675 | pub fn created(self: Self) ?i128 { |
| | 676 | if (!@hasDecl(@TypeOf(self.stat), "birthtime")) return null; |
| | 677 | const birthtime = self.stat.birthtime(); |
| | 678 | |
| | 679 | // If the filesystem doesn't support this the value *should* be: |
| | 680 | // On FreeBSD: tv_nsec = 0, tv_sec = -1 |
| | 681 | // On NetBSD and OpenBSD: tv_nsec = 0, tv_sec = 0 |
| | 682 | // On MacOS, it is set to ctime -- we cannot detect this!! |
| | 683 | switch (builtin.os.tag) { |
| | 684 | .freebsd => if (birthtime.tv_sec == -1 and birthtime.tv_nsec == 0) return null, |
| | 685 | .netbsd, .openbsd => if (birthtime.tv_sec == 0 and birthtime.tv_nsec == 0) return null, |
| | 686 | .macos => {}, |
| | 687 | else => @compileError("Creation time detection not implemented for OS"), |
| | 688 | } |
| | 689 | |
| | 690 | return @as(i128, birthtime.tv_sec) * std.time.ns_per_s + birthtime.tv_nsec; |
| | 691 | } |
| | 692 | }; |
| | 693 | |
| | 694 | /// `MetadataUnix`, but using Linux's `statx` syscall. |
| | 695 | /// On Linux versions below 4.11, `statx` will be filled with data from stat. |
| | 696 | pub const MetadataLinux = struct { |
| | 697 | statx: os.linux.Statx, |
| | 698 | |
| | 699 | const Self = @This(); |
| | 700 | |
| | 701 | /// Returns the size of the file |
| | 702 | pub fn size(self: Self) u64 { |
| | 703 | return self.statx.size; |
| | 704 | } |
| | 705 | |
| | 706 | /// Returns a `Permissions` struct, representing the permissions on the file |
| | 707 | pub fn permissions(self: Self) Permissions { |
| | 708 | return Permissions{ .inner = PermissionsUnix{ .mode = self.statx.mode } }; |
| | 709 | } |
| | 710 | |
| | 711 | /// Returns the `Kind` of the file |
| | 712 | pub fn kind(self: Self) Kind { |
| | 713 | const m = self.statx.mode & os.S.IFMT; |
| | 714 | |
| | 715 | switch (m) { |
| | 716 | os.S.IFBLK => return Kind.BlockDevice, |
| | 717 | os.S.IFCHR => return Kind.CharacterDevice, |
| | 718 | os.S.IFDIR => return Kind.Directory, |
| | 719 | os.S.IFIFO => return Kind.NamedPipe, |
| | 720 | os.S.IFLNK => return Kind.SymLink, |
| | 721 | os.S.IFREG => return Kind.File, |
| | 722 | os.S.IFSOCK => return Kind.UnixDomainSocket, |
| | 723 | else => {}, |
| | 724 | } |
| | 725 | |
| | 726 | return .Unknown; |
| | 727 | } |
| | 728 | |
| | 729 | /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01 |
| | 730 | pub fn accessed(self: Self) i128 { |
| | 731 | return @as(i128, self.statx.atime.tv_sec) * std.time.ns_per_s + self.statx.atime.tv_nsec; |
| | 732 | } |
| | 733 | |
| | 734 | /// Returns the last time the file was modified in nanoseconds since UTC 1970-01-01 |
| | 735 | pub fn modified(self: Self) i128 { |
| | 736 | return @as(i128, self.statx.mtime.tv_sec) * std.time.ns_per_s + self.statx.mtime.tv_nsec; |
| | 737 | } |
| | 738 | |
| | 739 | /// Returns the time the file was created in nanoseconds since UTC 1970-01-01 |
| | 740 | /// Returns null if this is not supported by the filesystem, or on kernels before than version 4.11 |
| | 741 | pub fn created(self: Self) ?i128 { |
| | 742 | if (self.statx.mask & os.linux.STATX_BTIME == 0) return null; |
| | 743 | return @as(i128, self.statx.btime.tv_sec) * std.time.ns_per_s + self.statx.btime.tv_nsec; |
| | 744 | } |
| | 745 | }; |
| | 746 | |
| | 747 | pub const MetadataWindows = struct { |
| | 748 | attributes: windows.DWORD, |
| | 749 | reparse_tag: windows.DWORD, |
| | 750 | _size: u64, |
| | 751 | access_time: i128, |
| | 752 | modified_time: i128, |
| | 753 | creation_time: i128, |
| | 754 | |
| | 755 | const Self = @This(); |
| | 756 | |
| | 757 | /// Returns the size of the file |
| | 758 | pub fn size(self: Self) u64 { |
| | 759 | return self._size; |
| | 760 | } |
| | 761 | |
| | 762 | /// Returns a `Permissions` struct, representing the permissions on the file |
| | 763 | pub fn permissions(self: Self) Permissions { |
| | 764 | return Permissions{ .inner = PermissionsWindows{ .attributes = self.attributes } }; |
| | 765 | } |
| | 766 | |
| | 767 | /// Returns the `Kind` of the file. |
| | 768 | /// Can only return: `.File`, `.Directory`, `.SymLink` or `.Unknown` |
| | 769 | pub fn kind(self: Self) Kind { |
| | 770 | if (self.attributes & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) { |
| | 771 | if (self.reparse_tag & 0x20000000 != 0) { |
| | 772 | return .SymLink; |
| | 773 | } |
| | 774 | } else if (self.attributes & windows.FILE_ATTRIBUTE_DIRECTORY != 0) { |
| | 775 | return .Directory; |
| | 776 | } else { |
| | 777 | return .File; |
| | 778 | } |
| | 779 | return .Unknown; |
| | 780 | } |
| | 781 | |
| | 782 | /// Returns the last time the file was accessed in nanoseconds since UTC 1970-01-01 |
| | 783 | pub fn accessed(self: Self) i128 { |
| | 784 | return self.access_time; |
| | 785 | } |
| | 786 | |
| | 787 | /// Returns the time the file was modified in nanoseconds since UTC 1970-01-01 |
| | 788 | pub fn modified(self: Self) i128 { |
| | 789 | return self.modified_time; |
| | 790 | } |
| | 791 | |
| | 792 | /// Returns the time the file was created in nanoseconds since UTC 1970-01-01 |
| | 793 | /// This never returns null, only returning an optional for compatibility with other OSes |
| | 794 | pub fn created(self: Self) ?i128 { |
| | 795 | return self.creation_time; |
| | 796 | } |
| | 797 | }; |
| | 798 | |
| | 799 | pub const MetadataError = os.FStatError; |
| | 800 | |
| | 801 | pub fn metadata(self: File) MetadataError!Metadata { |
| | 802 | return Metadata{ |
| | 803 | .inner = switch (builtin.os.tag) { |
| | 804 | .windows => blk: { |
| | 805 | var io_status_block: windows.IO_STATUS_BLOCK = undefined; |
| | 806 | var info: windows.FILE_ALL_INFORMATION = undefined; |
| | 807 | |
| | 808 | const rc = windows.ntdll.NtQueryInformationFile(self.handle, &io_status_block, &info, @sizeOf(windows.FILE_ALL_INFORMATION), .FileAllInformation); |
| | 809 | switch (rc) { |
| | 810 | .SUCCESS => {}, |
| | 811 | .BUFFER_OVERFLOW => {}, |
| | 812 | .INVALID_PARAMETER => unreachable, |
| | 813 | .ACCESS_DENIED => return error.AccessDenied, |
| | 814 | else => return windows.unexpectedStatus(rc), |
| | 815 | } |
| | 816 | |
| | 817 | const reparse_tag: windows.DWORD = reparse_blk: { |
| | 818 | if (info.BasicInformation.FileAttributes & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) { |
| | 819 | var reparse_buf: [windows.MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined; |
| | 820 | try windows.DeviceIoControl(self.handle, windows.FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]); |
| | 821 | const reparse_struct = @ptrCast(*const windows.REPARSE_DATA_BUFFER, @alignCast(@alignOf(windows.REPARSE_DATA_BUFFER), &reparse_buf[0])); |
| | 822 | break :reparse_blk reparse_struct.ReparseTag; |
| | 823 | } |
| | 824 | break :reparse_blk 0; |
| | 825 | }; |
| | 826 | |
| | 827 | break :blk MetadataWindows{ |
| | 828 | .attributes = info.BasicInformation.FileAttributes, |
| | 829 | .reparse_tag = reparse_tag, |
| | 830 | ._size = @bitCast(u64, info.StandardInformation.EndOfFile), |
| | 831 | .access_time = windows.fromSysTime(info.BasicInformation.LastAccessTime), |
| | 832 | .modified_time = windows.fromSysTime(info.BasicInformation.LastWriteTime), |
| | 833 | .creation_time = windows.fromSysTime(info.BasicInformation.CreationTime), |
| | 834 | }; |
| | 835 | }, |
| | 836 | .linux => blk: { |
| | 837 | var stx = mem.zeroes(os.linux.Statx); |
| | 838 | const rcx = os.linux.statx(self.handle, "\x00", os.linux.AT.EMPTY_PATH, os.linux.STATX_TYPE | os.linux.STATX_MODE | os.linux.STATX_ATIME | os.linux.STATX_MTIME | os.linux.STATX_BTIME, &stx); |
| | 839 | |
| | 840 | switch (os.errno(rcx)) { |
| | 841 | .SUCCESS => {}, |
| | 842 | // NOSYS happens when `statx` is unsupported, which is the case on kernel versions before 4.11 |
| | 843 | // Here, we call `fstat` and fill `stx` with the data we need |
| | 844 | .NOSYS => { |
| | 845 | const st = try os.fstat(self.handle); |
| | 846 | |
| | 847 | stx.mode = @intCast(u16, st.mode); |
| | 848 | |
| | 849 | // Hacky conversion from timespec to statx_timestamp |
| | 850 | stx.atime = std.mem.zeroes(os.linux.statx_timestamp); |
| | 851 | stx.atime.tv_sec = st.atim.tv_sec; |
| | 852 | stx.atime.tv_nsec = @intCast(u32, st.atim.tv_nsec); // Guaranteed to succeed (tv_nsec is always below 10^9) |
| | 853 | |
| | 854 | stx.mtime = std.mem.zeroes(os.linux.statx_timestamp); |
| | 855 | stx.mtime.tv_sec = st.mtim.tv_sec; |
| | 856 | stx.mtime.tv_nsec = @intCast(u32, st.mtim.tv_nsec); |
| | 857 | |
| | 858 | stx.mask = os.linux.STATX_BASIC_STATS | os.linux.STATX_MTIME; |
| | 859 | }, |
| | 860 | .BADF => unreachable, |
| | 861 | .FAULT => unreachable, |
| | 862 | .NOMEM => return error.SystemResources, |
| | 863 | else => |err| return os.unexpectedErrno(err), |
| | 864 | } |
| | 865 | |
| | 866 | break :blk MetadataLinux{ |
| | 867 | .statx = stx, |
| | 868 | }; |
| | 869 | }, |
| | 870 | else => blk: { |
| | 871 | const st = try os.fstat(self.handle); |
| | 872 | break :blk MetadataUnix{ |
| | 873 | .stat = st, |
| | 874 | }; |
| | 875 | }, |
| | 876 | }, |
| | 877 | }; |
| | 878 | } |
| | 879 | |
| 401 | pub const UpdateTimesError = os.FutimensError || windows.SetFileTimeError; | 880 | pub const UpdateTimesError = os.FutimensError || windows.SetFileTimeError; |
| 402 | | 881 | |
| 403 | /// The underlying file system may have a different granularity than nanoseconds, | 882 | /// The underlying file system may have a different granularity than nanoseconds, |