| author | |
| committer | |
| log | 6216922a9d63982f11ff8ee551a6688bc873e51c |
| tree | 14a9946c3269a9b0c0b754f8a8a4eb859ca667f2 |
| parent | ff3fd950a74ab8ea8907a62dbf880eae08232417 |
Maintaining the POSIX `stat` bits for Zig is a pain. The order and
bit-length of members differ between all architectures, and int types
can be signed or unsigned. The libcs deal with this by introducing the
own version of `struct stat` and copying the kernel structure members to
it. In the case of glibc, they did it twice thanks to the largefile
transition!
In practice, the project needs to maintain three versions of `struct
stat`:
- What the kernel defines.
- What musl wants for `struct stat`.
- What glibc wants for `struct stat64`. Make sure to use `fstatat64`!
This isn't as simple as running `zig translate-c`. In #21440 I had to:
- Compile toolchains for each arch+glibc/musl combo.
- Create a test `fstat` program with/without `FILE_OFFSET_BITS=64`.
- Dump the value for `struct stat`.
- Stare at `std.os.linux`/`std.c` and cry.
- Add some missing padding.
The fact that so many target checks in the `linux` and `posix` tests
exist is most likely due to writing to padding bits and failing later.
The solution to this madness is `statx(2)`:
- It takes a single structure that is the same for all arches AND libcs.
- It uses a custom timestamp format, but it is 64-bit ready.
- It gives the same info as `fstatat(2)` and more!
- Unlike `fstatat(2)`, you can request a subset of the info required
based on passing a mask.
It's so good that modern Linux arches (e.g. riscv) don't even implement
`stat`, with the libcs using a generic `struct stat` and copying from
`struct statx`.
Therefore, this commit rips out all the `stat` bits from `std.os.linux`
and `std.c`. `std.posix.Stat` is now `void`, and calling
`std.posix.*stat` is an compile-time error. A wrapper around `statx` has
been added to `std.os.linux`, and callers have been upgraded to use it.
Tests have also been updated to use `statx` where possible.
While I was here, I converted the mask and file attributes to be packed
struct bitfields. A nice side effect is checking that you actually
recieved the members you asked for via `Statx.mask`, which I have used
by adding `assert`s at specific callsites.29 files changed, 315 insertions(+), 927 deletions(-)
lib/std/Io/Threaded.zig+16-2| ... | @@ -1533,12 +1533,19 @@ fn dirStatPathLinux( | ... | @@ -1533,12 +1533,19 @@ fn dirStatPathLinux( |
| 1533 | dir.handle, | 1533 | dir.handle, |
| 1534 | sub_path_posix, | 1534 | sub_path_posix, |
| 1535 | flags, | 1535 | flags, |
| 1536 | linux.STATX_INO | linux.STATX_SIZE | linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, | 1536 | .{ .TYPE = true, .MODE = true, .ATIME = true, .MTIME = true, .CTIME = true, .INO = true, .SIZE = true }, |
| 1537 | &statx, | 1537 | &statx, |
| 1538 | ); | 1538 | ); |
| 1539 | switch (linux.errno(rc)) { | 1539 | switch (linux.errno(rc)) { |
| 1540 | .SUCCESS => { | 1540 | .SUCCESS => { |
| 1541 | current_thread.endSyscall(); | 1541 | current_thread.endSyscall(); |
| 1542 | assert(statx.mask.TYPE); | ||
| 1543 | assert(statx.mask.MODE); | ||
| 1544 | assert(statx.mask.ATIME); | ||
| 1545 | assert(statx.mask.MTIME); | ||
| 1546 | assert(statx.mask.CTIME); | ||
| 1547 | assert(statx.mask.INO); | ||
| 1548 | assert(statx.mask.SIZE); | ||
| 1542 | return statFromLinux(&statx); | 1549 | return statFromLinux(&statx); |
| 1543 | }, | 1550 | }, |
| 1544 | .INTR => { | 1551 | .INTR => { |
| ... | @@ -1725,12 +1732,19 @@ fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File | ... | @@ -1725,12 +1732,19 @@ fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File |
| 1725 | file.handle, | 1732 | file.handle, |
| 1726 | "", | 1733 | "", |
| 1727 | linux.AT.EMPTY_PATH, | 1734 | linux.AT.EMPTY_PATH, |
| 1728 | linux.STATX_INO | linux.STATX_SIZE | linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, | 1735 | .{ .TYPE = true, .MODE = true, .ATIME = true, .MTIME = true, .CTIME = true, .INO = true, .SIZE = true }, |
| 1729 | &statx, | 1736 | &statx, |
| 1730 | ); | 1737 | ); |
| 1731 | switch (linux.errno(rc)) { | 1738 | switch (linux.errno(rc)) { |
| 1732 | .SUCCESS => { | 1739 | .SUCCESS => { |
| 1733 | current_thread.endSyscall(); | 1740 | current_thread.endSyscall(); |
| 1741 | assert(statx.mask.TYPE); | ||
| 1742 | assert(statx.mask.MODE); | ||
| 1743 | assert(statx.mask.ATIME); | ||
| 1744 | assert(statx.mask.MTIME); | ||
| 1745 | assert(statx.mask.CTIME); | ||
| 1746 | assert(statx.mask.INO); | ||
| 1747 | assert(statx.mask.SIZE); | ||
| 1734 | return statFromLinux(&statx); | 1748 | return statFromLinux(&statx); |
| 1735 | }, | 1749 | }, |
| 1736 | .INTR => { | 1750 | .INTR => { |
lib/std/c.zig+5-163| ... | @@ -7586,166 +7586,6 @@ pub const EAI = if (builtin.abi.isAndroid()) enum(c_int) { | ... | @@ -7586,166 +7586,6 @@ pub const EAI = if (builtin.abi.isAndroid()) enum(c_int) { |
| 7586 | pub const dl_iterate_phdr_callback = *const fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.c) c_int; | 7586 | pub const dl_iterate_phdr_callback = *const fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.c) c_int; |
| 7587 | 7587 | ||
| 7588 | pub const Stat = switch (native_os) { | 7588 | pub const Stat = switch (native_os) { |
| 7589 | .linux => switch (native_arch) { | ||
| 7590 | .sparc64 => extern struct { | ||
| 7591 | dev: u64, | ||
| 7592 | __pad1: u16, | ||
| 7593 | ino: ino_t, | ||
| 7594 | mode: u32, | ||
| 7595 | nlink: u32, | ||
| 7596 | |||
| 7597 | uid: u32, | ||
| 7598 | gid: u32, | ||
| 7599 | rdev: u64, | ||
| 7600 | __pad2: u16, | ||
| 7601 | |||
| 7602 | size: off_t, | ||
| 7603 | blksize: isize, | ||
| 7604 | blocks: i64, | ||
| 7605 | |||
| 7606 | atim: timespec, | ||
| 7607 | mtim: timespec, | ||
| 7608 | ctim: timespec, | ||
| 7609 | __reserved: [2]usize, | ||
| 7610 | |||
| 7611 | pub fn atime(self: @This()) timespec { | ||
| 7612 | return self.atim; | ||
| 7613 | } | ||
| 7614 | |||
| 7615 | pub fn mtime(self: @This()) timespec { | ||
| 7616 | return self.mtim; | ||
| 7617 | } | ||
| 7618 | |||
| 7619 | pub fn ctime(self: @This()) timespec { | ||
| 7620 | return self.ctim; | ||
| 7621 | } | ||
| 7622 | }, | ||
| 7623 | .mips, .mipsel => if (builtin.target.abi.isMusl()) extern struct { | ||
| 7624 | dev: dev_t, | ||
| 7625 | __pad0: [2]i32, | ||
| 7626 | ino: ino_t, | ||
| 7627 | mode: mode_t, | ||
| 7628 | nlink: nlink_t, | ||
| 7629 | uid: uid_t, | ||
| 7630 | gid: gid_t, | ||
| 7631 | rdev: dev_t, | ||
| 7632 | __pad1: [2]i32, | ||
| 7633 | size: off_t, | ||
| 7634 | atim: timespec, | ||
| 7635 | mtim: timespec, | ||
| 7636 | ctim: timespec, | ||
| 7637 | blksize: blksize_t, | ||
| 7638 | __pad3: i32, | ||
| 7639 | blocks: blkcnt_t, | ||
| 7640 | __pad4: [14]i32, | ||
| 7641 | |||
| 7642 | pub fn atime(self: @This()) timespec { | ||
| 7643 | return self.atim; | ||
| 7644 | } | ||
| 7645 | |||
| 7646 | pub fn mtime(self: @This()) timespec { | ||
| 7647 | return self.mtim; | ||
| 7648 | } | ||
| 7649 | |||
| 7650 | pub fn ctime(self: @This()) timespec { | ||
| 7651 | return self.ctim; | ||
| 7652 | } | ||
| 7653 | } else extern struct { | ||
| 7654 | dev: u32, | ||
| 7655 | __pad0: [3]u32, | ||
| 7656 | ino: ino_t, | ||
| 7657 | mode: mode_t, | ||
| 7658 | nlink: nlink_t, | ||
| 7659 | uid: uid_t, | ||
| 7660 | gid: gid_t, | ||
| 7661 | rdev: u32, | ||
| 7662 | __pad1: [3]u32, | ||
| 7663 | size: off_t, | ||
| 7664 | atim: timespec, | ||
| 7665 | mtim: timespec, | ||
| 7666 | ctim: timespec, | ||
| 7667 | blksize: blksize_t, | ||
| 7668 | __pad3: u32, | ||
| 7669 | blocks: blkcnt_t, | ||
| 7670 | __pad4: [14]u32, | ||
| 7671 | |||
| 7672 | pub fn atime(self: @This()) timespec { | ||
| 7673 | return self.atim; | ||
| 7674 | } | ||
| 7675 | |||
| 7676 | pub fn mtime(self: @This()) timespec { | ||
| 7677 | return self.mtim; | ||
| 7678 | } | ||
| 7679 | |||
| 7680 | pub fn ctime(self: @This()) timespec { | ||
| 7681 | return self.ctim; | ||
| 7682 | } | ||
| 7683 | }, | ||
| 7684 | .mips64, .mips64el => if (builtin.target.abi.isMusl()) extern struct { | ||
| 7685 | dev: dev_t, | ||
| 7686 | __pad0: [3]i32, | ||
| 7687 | ino: ino_t, | ||
| 7688 | mode: mode_t, | ||
| 7689 | nlink: nlink_t, | ||
| 7690 | uid: uid_t, | ||
| 7691 | gid: gid_t, | ||
| 7692 | rdev: dev_t, | ||
| 7693 | __pad1: [2]u32, | ||
| 7694 | size: off_t, | ||
| 7695 | __pad2: i32, | ||
| 7696 | atim: timespec, | ||
| 7697 | mtim: timespec, | ||
| 7698 | ctim: timespec, | ||
| 7699 | blksize: blksize_t, | ||
| 7700 | __pad3: u32, | ||
| 7701 | blocks: blkcnt_t, | ||
| 7702 | __pad4: [14]i32, | ||
| 7703 | |||
| 7704 | pub fn atime(self: @This()) timespec { | ||
| 7705 | return self.atim; | ||
| 7706 | } | ||
| 7707 | |||
| 7708 | pub fn mtime(self: @This()) timespec { | ||
| 7709 | return self.mtim; | ||
| 7710 | } | ||
| 7711 | |||
| 7712 | pub fn ctime(self: @This()) timespec { | ||
| 7713 | return self.ctim; | ||
| 7714 | } | ||
| 7715 | } else extern struct { | ||
| 7716 | dev: dev_t, | ||
| 7717 | __pad0: [3]u32, | ||
| 7718 | ino: ino_t, | ||
| 7719 | mode: mode_t, | ||
| 7720 | nlink: nlink_t, | ||
| 7721 | uid: uid_t, | ||
| 7722 | gid: gid_t, | ||
| 7723 | rdev: dev_t, | ||
| 7724 | __pad1: [3]u32, | ||
| 7725 | size: off_t, | ||
| 7726 | atim: timespec, | ||
| 7727 | mtim: timespec, | ||
| 7728 | ctim: timespec, | ||
| 7729 | blksize: blksize_t, | ||
| 7730 | __pad3: u32, | ||
| 7731 | blocks: blkcnt_t, | ||
| 7732 | __pad4: [14]i32, | ||
| 7733 | |||
| 7734 | pub fn atime(self: @This()) timespec { | ||
| 7735 | return self.atim; | ||
| 7736 | } | ||
| 7737 | |||
| 7738 | pub fn mtime(self: @This()) timespec { | ||
| 7739 | return self.mtim; | ||
| 7740 | } | ||
| 7741 | |||
| 7742 | pub fn ctime(self: @This()) timespec { | ||
| 7743 | return self.ctim; | ||
| 7744 | } | ||
| 7745 | }, | ||
| 7746 | |||
| 7747 | else => std.os.linux.Stat, // libc stat is the same as kernel stat. | ||
| 7748 | }, | ||
| 7749 | .emscripten => emscripten.Stat, | 7589 | .emscripten => emscripten.Stat, |
| 7750 | .wasi => extern struct { | 7590 | .wasi => extern struct { |
| 7751 | // Match wasi-libc's `struct stat` in lib/libc/include/wasm-wasi-musl/__struct_stat.h | 7591 | // Match wasi-libc's `struct stat` in lib/libc/include/wasm-wasi-musl/__struct_stat.h |
| ... | @@ -10422,6 +10262,7 @@ pub const fstat = switch (native_os) { | ... | @@ -10422,6 +10262,7 @@ pub const fstat = switch (native_os) { |
| 10422 | else => private.fstat, | 10262 | else => private.fstat, |
| 10423 | }, | 10263 | }, |
| 10424 | .netbsd => private.__fstat50, | 10264 | .netbsd => private.__fstat50, |
| 10265 | .linux => {}, | ||
| 10425 | else => private.fstat, | 10266 | else => private.fstat, |
| 10426 | }; | 10267 | }; |
| 10427 | 10268 | ||
| ... | @@ -10430,8 +10271,12 @@ pub const fstatat = switch (native_os) { | ... | @@ -10430,8 +10271,12 @@ pub const fstatat = switch (native_os) { |
| 10430 | .x86_64 => private.@"fstatat$INODE64", | 10271 | .x86_64 => private.@"fstatat$INODE64", |
| 10431 | else => private.fstatat, | 10272 | else => private.fstatat, |
| 10432 | }, | 10273 | }, |
| 10274 | .linux => {}, | ||
| 10433 | else => private.fstatat, | 10275 | else => private.fstatat, |
| 10434 | }; | 10276 | }; |
| 10277 | |||
| 10278 | pub extern "c" fn statx(dirfd: fd_t, path: [*:0]const u8, flags: u32, mask: linux.STATX, buf: *linux.Statx) c_int; | ||
| 10279 | |||
| 10435 | pub extern "c" fn getpwent() ?*passwd; | 10280 | pub extern "c" fn getpwent() ?*passwd; |
| 10436 | pub extern "c" fn endpwent() void; | 10281 | pub extern "c" fn endpwent() void; |
| 10437 | pub extern "c" fn setpwent() void; | 10282 | pub extern "c" fn setpwent() void; |
| ... | @@ -10505,8 +10350,6 @@ pub extern "c" fn inotify_init1(flags: c_uint) c_int; | ... | @@ -10505,8 +10350,6 @@ pub extern "c" fn inotify_init1(flags: c_uint) c_int; |
| 10505 | pub extern "c" fn inotify_add_watch(fd: fd_t, pathname: [*:0]const u8, mask: u32) c_int; | 10350 | pub extern "c" fn inotify_add_watch(fd: fd_t, pathname: [*:0]const u8, mask: u32) c_int; |
| 10506 | pub extern "c" fn inotify_rm_watch(fd: fd_t, wd: c_int) c_int; | 10351 | pub extern "c" fn inotify_rm_watch(fd: fd_t, wd: c_int) c_int; |
| 10507 | 10352 | ||
| 10508 | pub extern "c" fn fstat64(fd: fd_t, buf: *Stat) c_int; | ||
| 10509 | pub extern "c" fn fstatat64(dirfd: fd_t, noalias path: [*:0]const u8, noalias stat_buf: *Stat, flags: u32) c_int; | ||
| 10510 | pub extern "c" fn fallocate64(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int; | 10353 | pub extern "c" fn fallocate64(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int; |
| 10511 | pub extern "c" fn fopen64(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE; | 10354 | pub extern "c" fn fopen64(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE; |
| 10512 | pub extern "c" fn ftruncate64(fd: c_int, length: off_t) c_int; | 10355 | pub extern "c" fn ftruncate64(fd: c_int, length: off_t) c_int; |
| ... | @@ -11552,7 +11395,6 @@ const private = struct { | ... | @@ -11552,7 +11395,6 @@ const private = struct { |
| 11552 | extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; | 11395 | extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; |
| 11553 | extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; | 11396 | extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; |
| 11554 | extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint, sv: *[2]fd_t) c_int; | 11397 | extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint, sv: *[2]fd_t) c_int; |
| 11555 | extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int; | ||
| 11556 | extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; | 11398 | extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; |
| 11557 | extern "c" fn sysconf(sc: c_int) c_long; | 11399 | extern "c" fn sysconf(sc: c_int) c_long; |
| 11558 | extern "c" fn shm_open(name: [*:0]const u8, flag: c_int, mode: mode_t) c_int; | 11400 | extern "c" fn shm_open(name: [*:0]const u8, flag: c_int, mode: mode_t) c_int; |
lib/std/os/linux.zig+131-82| ... | @@ -95,15 +95,14 @@ pub fn clone( | ... | @@ -95,15 +95,14 @@ pub fn clone( |
| 95 | pub const ARCH = arch_bits.ARCH; | 95 | pub const ARCH = arch_bits.ARCH; |
| 96 | pub const HWCAP = arch_bits.HWCAP; | 96 | pub const HWCAP = arch_bits.HWCAP; |
| 97 | pub const SC = arch_bits.SC; | 97 | pub const SC = arch_bits.SC; |
| 98 | pub const Stat = arch_bits.Stat; | ||
| 99 | pub const VDSO = arch_bits.VDSO; | 98 | pub const VDSO = arch_bits.VDSO; |
| 100 | pub const blkcnt_t = arch_bits.blkcnt_t; | 99 | pub const blkcnt_t = u64; |
| 101 | pub const blksize_t = arch_bits.blksize_t; | 100 | pub const blksize_t = u32; |
| 102 | pub const dev_t = arch_bits.dev_t; | 101 | pub const dev_t = u64; |
| 103 | pub const ino_t = arch_bits.ino_t; | 102 | pub const ino_t = u64; |
| 104 | pub const mode_t = arch_bits.mode_t; | 103 | pub const mode_t = u32; |
| 105 | pub const nlink_t = arch_bits.nlink_t; | 104 | pub const nlink_t = u32; |
| 106 | pub const off_t = arch_bits.off_t; | 105 | pub const off_t = i64; |
| 107 | pub const time_t = arch_bits.time_t; | 106 | pub const time_t = arch_bits.time_t; |
| 108 | pub const user_desc = arch_bits.user_desc; | 107 | pub const user_desc = arch_bits.user_desc; |
| 109 | 108 | ||
| ... | @@ -2199,61 +2198,13 @@ pub fn accept4(fd: i32, noalias addr: ?*sockaddr, noalias len: ?*socklen_t, flag | ... | @@ -2199,61 +2198,13 @@ pub fn accept4(fd: i32, noalias addr: ?*sockaddr, noalias len: ?*socklen_t, flag |
| 2199 | return syscall4(.accept4, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len), flags); | 2198 | return syscall4(.accept4, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len), flags); |
| 2200 | } | 2199 | } |
| 2201 | 2200 | ||
| 2202 | pub fn fstat(fd: i32, stat_buf: *Stat) usize { | 2201 | pub fn statx(dirfd: i32, path: [*:0]const u8, flags: u32, mask: STATX, statx_buf: *Statx) usize { |
| 2203 | if (native_arch == .riscv32 or native_arch.isLoongArch()) { | ||
| 2204 | // riscv32 and loongarch have made the interesting decision to not implement some of | ||
| 2205 | // the older stat syscalls, including this one. | ||
| 2206 | @compileError("No fstat syscall on this architecture."); | ||
| 2207 | } else if (@hasField(SYS, "fstat64")) { | ||
| 2208 | return syscall2(.fstat64, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(stat_buf)); | ||
| 2209 | } else { | ||
| 2210 | return syscall2(.fstat, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(stat_buf)); | ||
| 2211 | } | ||
| 2212 | } | ||
| 2213 | |||
| 2214 | pub fn stat(pathname: [*:0]const u8, statbuf: *Stat) usize { | ||
| 2215 | if (native_arch == .riscv32 or native_arch.isLoongArch()) { | ||
| 2216 | // riscv32 and loongarch have made the interesting decision to not implement some of | ||
| 2217 | // the older stat syscalls, including this one. | ||
| 2218 | @compileError("No stat syscall on this architecture."); | ||
| 2219 | } else if (@hasField(SYS, "stat64")) { | ||
| 2220 | return syscall2(.stat64, @intFromPtr(pathname), @intFromPtr(statbuf)); | ||
| 2221 | } else { | ||
| 2222 | return syscall2(.stat, @intFromPtr(pathname), @intFromPtr(statbuf)); | ||
| 2223 | } | ||
| 2224 | } | ||
| 2225 | |||
| 2226 | pub fn lstat(pathname: [*:0]const u8, statbuf: *Stat) usize { | ||
| 2227 | if (native_arch == .riscv32 or native_arch.isLoongArch()) { | ||
| 2228 | // riscv32 and loongarch have made the interesting decision to not implement some of | ||
| 2229 | // the older stat syscalls, including this one. | ||
| 2230 | @compileError("No lstat syscall on this architecture."); | ||
| 2231 | } else if (@hasField(SYS, "lstat64")) { | ||
| 2232 | return syscall2(.lstat64, @intFromPtr(pathname), @intFromPtr(statbuf)); | ||
| 2233 | } else { | ||
| 2234 | return syscall2(.lstat, @intFromPtr(pathname), @intFromPtr(statbuf)); | ||
| 2235 | } | ||
| 2236 | } | ||
| 2237 | |||
| 2238 | pub fn fstatat(dirfd: i32, path: [*:0]const u8, stat_buf: *Stat, flags: u32) usize { | ||
| 2239 | if (native_arch == .riscv32 or native_arch.isLoongArch()) { | ||
| 2240 | // riscv32 and loongarch have made the interesting decision to not implement some of | ||
| 2241 | // the older stat syscalls, including this one. | ||
| 2242 | @compileError("No fstatat syscall on this architecture."); | ||
| 2243 | } else if (@hasField(SYS, "fstatat64")) { | ||
| 2244 | return syscall4(.fstatat64, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags); | ||
| 2245 | } else { | ||
| 2246 | return syscall4(.fstatat, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags); | ||
| 2247 | } | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | pub fn statx(dirfd: i32, path: [*:0]const u8, flags: u32, mask: u32, statx_buf: *Statx) usize { | ||
| 2251 | return syscall5( | 2202 | return syscall5( |
| 2252 | .statx, | 2203 | .statx, |
| 2253 | @as(usize, @bitCast(@as(isize, dirfd))), | 2204 | @as(usize, @bitCast(@as(isize, dirfd))), |
| 2254 | @intFromPtr(path), | 2205 | @intFromPtr(path), |
| 2255 | flags, | 2206 | flags, |
| 2256 | mask, | 2207 | @as(u32, @bitCast(mask)), |
| 2257 | @intFromPtr(statx_buf), | 2208 | @intFromPtr(statx_buf), |
| 2258 | ); | 2209 | ); |
| 2259 | } | 2210 | } |
| ... | @@ -6940,27 +6891,85 @@ pub const utsname = extern struct { | ... | @@ -6940,27 +6891,85 @@ pub const utsname = extern struct { |
| 6940 | }; | 6891 | }; |
| 6941 | pub const HOST_NAME_MAX = 64; | 6892 | pub const HOST_NAME_MAX = 64; |
| 6942 | 6893 | ||
| 6943 | pub const STATX_TYPE = 0x0001; | 6894 | /// Flags used to request specific members in `Statx` be filled out. |
| 6944 | pub const STATX_MODE = 0x0002; | 6895 | /// The `Statx.mask` member will be updated with what information the kernel |
| 6945 | pub const STATX_NLINK = 0x0004; | 6896 | /// returned. Callers must check this field since support varies by kernel |
| 6946 | pub const STATX_UID = 0x0008; | 6897 | /// version and filesystem. |
| 6947 | pub const STATX_GID = 0x0010; | 6898 | pub const STATX = packed struct(u32) { |
| 6948 | pub const STATX_ATIME = 0x0020; | 6899 | /// Want `mode & S.IFMT`. |
| 6949 | pub const STATX_MTIME = 0x0040; | 6900 | TYPE: bool = false, |
| 6950 | pub const STATX_CTIME = 0x0080; | 6901 | /// Want `mode & ~S.IFMT`. |
| 6951 | pub const STATX_INO = 0x0100; | 6902 | MODE: bool = false, |
| 6952 | pub const STATX_SIZE = 0x0200; | 6903 | /// Want the `nlink` member. |
| 6953 | pub const STATX_BLOCKS = 0x0400; | 6904 | NLINK: bool = false, |
| 6954 | pub const STATX_BASIC_STATS = 0x07ff; | 6905 | /// Want the `uid` member. |
| 6955 | 6906 | UID: bool = false, | |
| 6956 | pub const STATX_BTIME = 0x0800; | 6907 | /// Want the `gid` member. |
| 6957 | 6908 | GID: bool = false, | |
| 6958 | pub const STATX_ATTR_COMPRESSED = 0x0004; | 6909 | /// Want the `atime` member. |
| 6959 | pub const STATX_ATTR_IMMUTABLE = 0x0010; | 6910 | ATIME: bool = false, |
| 6960 | pub const STATX_ATTR_APPEND = 0x0020; | 6911 | /// Want the `mtime` member. |
| 6961 | pub const STATX_ATTR_NODUMP = 0x0040; | 6912 | MTIME: bool = false, |
| 6962 | pub const STATX_ATTR_ENCRYPTED = 0x0800; | 6913 | /// Want the `ctime` member. |
| 6963 | pub const STATX_ATTR_AUTOMOUNT = 0x1000; | 6914 | CTIME: bool = false, |
| 6915 | /// Want the `ino` member. | ||
| 6916 | INO: bool = false, | ||
| 6917 | /// Want the `size` member. | ||
| 6918 | SIZE: bool = false, | ||
| 6919 | /// Want the `blocks` member. | ||
| 6920 | BLOCKS: bool = false, | ||
| 6921 | /// Want the `btime` member. | ||
| 6922 | BTIME: bool = false, | ||
| 6923 | /// Want the `mnt_id` member. | ||
| 6924 | MNT_ID: bool = false, | ||
| 6925 | /// Want the `dio_mem_align` and `dio_offset_align` members. | ||
| 6926 | DIOALIGN: bool = false, | ||
| 6927 | /// Want the `stx_mnt_id` member. | ||
| 6928 | MNT_ID_UNIQUE: bool = false, | ||
| 6929 | /// Want the `sub` member. | ||
| 6930 | SUBVOL: bool = false, | ||
| 6931 | /// Want the `atomic_write_unit_min`, `atomic_write_unit_max` and | ||
| 6932 | /// `atomic_write_segments_max` members. | ||
| 6933 | WRITE_ATOMIC: bool = false, | ||
| 6934 | /// Want the `dio_read_offset_align` member. | ||
| 6935 | DIO_READ_ALIGN: bool = false, | ||
| 6936 | __pad: u13 = 0, | ||
| 6937 | /// Reserved for future expansion; must not be set. | ||
| 6938 | __RESERVED: bool = false, | ||
| 6939 | |||
| 6940 | pub const BASIC_STATS: STATX = @bitCast(@as(u32, 0x7ff)); | ||
| 6941 | }; | ||
| 6942 | |||
| 6943 | /// Attributes about the state or features of a file as a bitmask. | ||
| 6944 | /// Flags marked [I] correspond to the `FS_IOC_SETFLAGS` values semantically. | ||
| 6945 | /// See [FS_IOC_SETFLAGS(2const)](https://man7.org/linux/man-pages/man2/FS_IOC_GETFLAGS.2const.html) | ||
| 6946 | /// for more. | ||
| 6947 | pub const STATX_ATTR = packed struct(u64) { | ||
| 6948 | __pad1: u3 = 0, | ||
| 6949 | /// [I] File is compressed by the fs. | ||
| 6950 | COMPRESSED: bool = false, | ||
| 6951 | __pad2: u1 = 0, | ||
| 6952 | /// [I] File is marked immutable. | ||
| 6953 | IMMUTABLE: bool = false, | ||
| 6954 | /// [I] File is append-only. | ||
| 6955 | APPEND: bool = false, | ||
| 6956 | /// [I] File is not to be dumped. | ||
| 6957 | NODUMP: bool = false, | ||
| 6958 | /// [I] File requires a key to decrypt in the filesystem. | ||
| 6959 | ENCRYPTED: bool = false, | ||
| 6960 | /// File names a directory that triggers an automount. | ||
| 6961 | AUTOMOUNT: bool = false, | ||
| 6962 | /// File names the root of a mount. | ||
| 6963 | MOUNT_ROOT: bool = false, | ||
| 6964 | /// [I] File is protected by the `dm-verity` device. | ||
| 6965 | VERITY: bool = false, | ||
| 6966 | /// File is currently in the CPU direct access state. | ||
| 6967 | /// Does not correspond to the per-inode DAX flag that some filesystems support. | ||
| 6968 | DAX: bool = false, | ||
| 6969 | /// File supports atomic write operations. | ||
| 6970 | WRITE_ATOMIC: bool = false, | ||
| 6971 | __pad3: u50 = 0, | ||
| 6972 | }; | ||
| 6964 | 6973 | ||
| 6965 | pub const statx_timestamp = extern struct { | 6974 | pub const statx_timestamp = extern struct { |
| 6966 | /// Number of seconds before or after `1970-01-01T00:00:00Z`. | 6975 | /// Number of seconds before or after `1970-01-01T00:00:00Z`. |
| ... | @@ -6974,11 +6983,11 @@ pub const statx_timestamp = extern struct { | ... | @@ -6974,11 +6983,11 @@ pub const statx_timestamp = extern struct { |
| 6974 | /// Renamed to `Statx` to not conflict with the `statx` function. | 6983 | /// Renamed to `Statx` to not conflict with the `statx` function. |
| 6975 | pub const Statx = extern struct { | 6984 | pub const Statx = extern struct { |
| 6976 | /// Mask of bits indicating filled fields. | 6985 | /// Mask of bits indicating filled fields. |
| 6977 | mask: u32, | 6986 | mask: STATX, |
| 6978 | /// Block size for filesystem I/O. | 6987 | /// Block size for filesystem I/O. |
| 6979 | blksize: u32, | 6988 | blksize: u32, |
| 6980 | /// Extra file attribute indicators. | 6989 | /// Extra file attribute indicators. |
| 6981 | attributes: u64, | 6990 | attributes: STATX_ATTR, |
| 6982 | /// Number of hard links. | 6991 | /// Number of hard links. |
| 6983 | nlink: u32, | 6992 | nlink: u32, |
| 6984 | /// User ID of owner. | 6993 | /// User ID of owner. |
| ... | @@ -6995,7 +7004,7 @@ pub const Statx = extern struct { | ... | @@ -6995,7 +7004,7 @@ pub const Statx = extern struct { |
| 6995 | /// Number of 512B blocks allocated. | 7004 | /// Number of 512B blocks allocated. |
| 6996 | blocks: u64, | 7005 | blocks: u64, |
| 6997 | /// Mask to show what's supported in `attributes`. | 7006 | /// Mask to show what's supported in `attributes`. |
| 6998 | attributes_mask: u64, | 7007 | attributes_mask: STATX_ATTR, |
| 6999 | /// Last access file timestamp. | 7008 | /// Last access file timestamp. |
| 7000 | atime: statx_timestamp, | 7009 | atime: statx_timestamp, |
| 7001 | /// Creation file timestamp. | 7010 | /// Creation file timestamp. |
| ... | @@ -9977,6 +9986,46 @@ pub const wrapped = struct { | ... | @@ -9977,6 +9986,46 @@ pub const wrapped = struct { |
| 9977 | } | 9986 | } |
| 9978 | } | 9987 | } |
| 9979 | 9988 | ||
| 9989 | pub const StatxError = std.posix.UnexpectedError || error{ | ||
| 9990 | /// Search permission is denied for one of the directories in `path`. | ||
| 9991 | AccessDenied, | ||
| 9992 | /// Too many symbolic links were encountered traversing `path`. | ||
| 9993 | SymLinkLoop, | ||
| 9994 | /// `path` is too long. | ||
| 9995 | NameTooLong, | ||
| 9996 | /// One of: | ||
| 9997 | /// - A component of `path` does not exist. | ||
| 9998 | /// - A component of `path` is not a directory. | ||
| 9999 | /// - `path` is a relative and `dirfd` is not a directory file descriptor. | ||
| 10000 | FileNotFound, | ||
| 10001 | /// Insufficient memory is available. | ||
| 10002 | SystemResources, | ||
| 10003 | }; | ||
| 10004 | |||
| 10005 | pub fn statx(dirfd: fd_t, path: [*:0]const u8, flags: u32, mask: STATX) StatxError!Statx { | ||
| 10006 | const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) | ||
| 10007 | .{ .major = 30, .minor = 0, .patch = 0 } | ||
| 10008 | else | ||
| 10009 | .{ .major = 2, .minor = 28, .patch = 0 }); | ||
| 10010 | const sys = if (use_c) std.c else std.os.linux; | ||
| 10011 | |||
| 10012 | var stx = std.mem.zeroes(Statx); | ||
| 10013 | const rc = sys.statx(dirfd, path, flags, mask, &stx); | ||
| 10014 | return switch (sys.errno(rc)) { | ||
| 10015 | .SUCCESS => stx, | ||
| 10016 | .ACCES => error.AccessDenied, | ||
| 10017 | .BADF => invalidApiUsage(), | ||
| 10018 | .FAULT => invalidApiUsage(), | ||
| 10019 | .INVAL => invalidApiUsage(), | ||
| 10020 | .LOOP => error.SymLinkLoop, | ||
| 10021 | .NAMETOOLONG => error.NameTooLong, | ||
| 10022 | .NOENT => error.FileNotFound, | ||
| 10023 | .NOTDIR => error.FileNotFound, | ||
| 10024 | .NOMEM => error.SystemResources, | ||
| 10025 | else => |err| unexpectedErrno(err), | ||
| 10026 | }; | ||
| 10027 | } | ||
| 10028 | |||
| 9980 | const unexpectedErrno = std.posix.unexpectedErrno; | 10029 | const unexpectedErrno = std.posix.unexpectedErrno; |
| 9981 | 10030 | ||
| 9982 | fn invalidApiUsage() error{Unexpected} { | 10031 | fn invalidApiUsage() error{Unexpected} { |
lib/std/os/linux/IoUring.zig+3-3| ... | @@ -958,7 +958,7 @@ pub fn statx( | ... | @@ -958,7 +958,7 @@ pub fn statx( |
| 958 | fd: linux.fd_t, | 958 | fd: linux.fd_t, |
| 959 | path: [:0]const u8, | 959 | path: [:0]const u8, |
| 960 | flags: u32, | 960 | flags: u32, |
| 961 | mask: u32, | 961 | mask: linux.STATX, |
| 962 | buf: *linux.Statx, | 962 | buf: *linux.Statx, |
| 963 | ) !*linux.io_uring_sqe { | 963 | ) !*linux.io_uring_sqe { |
| 964 | const sqe = try self.get_sqe(); | 964 | const sqe = try self.get_sqe(); |
| ... | @@ -2691,7 +2691,7 @@ test "statx" { | ... | @@ -2691,7 +2691,7 @@ test "statx" { |
| 2691 | tmp.dir.fd, | 2691 | tmp.dir.fd, |
| 2692 | path, | 2692 | path, |
| 2693 | 0, | 2693 | 0, |
| 2694 | linux.STATX_SIZE, | 2694 | .{ .SIZE = true }, |
| 2695 | &buf, | 2695 | &buf, |
| 2696 | ); | 2696 | ); |
| 2697 | try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode); | 2697 | try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode); |
| ... | @@ -2718,7 +2718,7 @@ test "statx" { | ... | @@ -2718,7 +2718,7 @@ test "statx" { |
| 2718 | .flags = 0, | 2718 | .flags = 0, |
| 2719 | }, cqe); | 2719 | }, cqe); |
| 2720 | 2720 | ||
| 2721 | try testing.expect(buf.mask & linux.STATX_SIZE == linux.STATX_SIZE); | 2721 | try testing.expect(buf.mask.SIZE); |
| 2722 | try testing.expectEqual(@as(u64, 6), buf.size); | 2722 | try testing.expectEqual(@as(u64, 6), buf.size); |
| 2723 | } | 2723 | } |
| 2724 | 2724 |
lib/std/os/linux/aarch64.zig-32| ... | @@ -151,35 +151,3 @@ pub const off_t = i64; | ... | @@ -151,35 +151,3 @@ pub const off_t = i64; |
| 151 | pub const ino_t = u64; | 151 | pub const ino_t = u64; |
| 152 | pub const dev_t = u64; | 152 | pub const dev_t = u64; |
| 153 | pub const blkcnt_t = i64; | 153 | pub const blkcnt_t = i64; |
| 154 | |||
| 155 | // The `stat` definition used by the Linux kernel. | ||
| 156 | pub const Stat = extern struct { | ||
| 157 | dev: dev_t, | ||
| 158 | ino: ino_t, | ||
| 159 | mode: mode_t, | ||
| 160 | nlink: nlink_t, | ||
| 161 | uid: std.os.linux.uid_t, | ||
| 162 | gid: std.os.linux.gid_t, | ||
| 163 | rdev: dev_t, | ||
| 164 | __pad: u64, | ||
| 165 | size: off_t, | ||
| 166 | blksize: blksize_t, | ||
| 167 | __pad2: i32, | ||
| 168 | blocks: blkcnt_t, | ||
| 169 | atim: std.os.linux.timespec, | ||
| 170 | mtim: std.os.linux.timespec, | ||
| 171 | ctim: std.os.linux.timespec, | ||
| 172 | __unused: [2]u32, | ||
| 173 | |||
| 174 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 175 | return self.atim; | ||
| 176 | } | ||
| 177 | |||
| 178 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 179 | return self.mtim; | ||
| 180 | } | ||
| 181 | |||
| 182 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 183 | return self.ctim; | ||
| 184 | } | ||
| 185 | }; |
lib/std/os/linux/arm.zig-32| ... | @@ -187,35 +187,3 @@ pub const off_t = i64; | ... | @@ -187,35 +187,3 @@ pub const off_t = i64; |
| 187 | pub const ino_t = u64; | 187 | pub const ino_t = u64; |
| 188 | pub const dev_t = u64; | 188 | pub const dev_t = u64; |
| 189 | pub const blkcnt_t = i64; | 189 | pub const blkcnt_t = i64; |
| 190 | |||
| 191 | // The `stat` definition used by the Linux kernel. | ||
| 192 | pub const Stat = extern struct { | ||
| 193 | dev: dev_t, | ||
| 194 | __dev_padding: u32, | ||
| 195 | __ino_truncated: u32, | ||
| 196 | mode: mode_t, | ||
| 197 | nlink: nlink_t, | ||
| 198 | uid: std.os.linux.uid_t, | ||
| 199 | gid: std.os.linux.gid_t, | ||
| 200 | rdev: dev_t, | ||
| 201 | __rdev_padding: u32, | ||
| 202 | size: off_t, | ||
| 203 | blksize: blksize_t, | ||
| 204 | blocks: blkcnt_t, | ||
| 205 | atim: std.os.linux.timespec, | ||
| 206 | mtim: std.os.linux.timespec, | ||
| 207 | ctim: std.os.linux.timespec, | ||
| 208 | ino: ino_t, | ||
| 209 | |||
| 210 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 211 | return self.atim; | ||
| 212 | } | ||
| 213 | |||
| 214 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 215 | return self.mtim; | ||
| 216 | } | ||
| 217 | |||
| 218 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 219 | return self.ctim; | ||
| 220 | } | ||
| 221 | }; |
lib/std/os/linux/hexagon.zig-32| ... | @@ -128,36 +128,4 @@ pub const ino_t = u64; | ... | @@ -128,36 +128,4 @@ pub const ino_t = u64; |
| 128 | pub const dev_t = u64; | 128 | pub const dev_t = u64; |
| 129 | pub const blkcnt_t = i64; | 129 | pub const blkcnt_t = i64; |
| 130 | 130 | ||
| 131 | // The `stat` definition used by the Linux kernel. | ||
| 132 | pub const Stat = extern struct { | ||
| 133 | dev: dev_t, | ||
| 134 | ino: ino_t, | ||
| 135 | mode: mode_t, | ||
| 136 | nlink: nlink_t, | ||
| 137 | uid: std.os.linux.uid_t, | ||
| 138 | gid: std.os.linux.gid_t, | ||
| 139 | rdev: dev_t, | ||
| 140 | __pad: u32, | ||
| 141 | size: off_t, | ||
| 142 | blksize: blksize_t, | ||
| 143 | __pad2: i32, | ||
| 144 | blocks: blkcnt_t, | ||
| 145 | atim: std.os.linux.timespec, | ||
| 146 | mtim: std.os.linux.timespec, | ||
| 147 | ctim: std.os.linux.timespec, | ||
| 148 | __unused: [2]u32, | ||
| 149 | |||
| 150 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 151 | return self.atim; | ||
| 152 | } | ||
| 153 | |||
| 154 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 155 | return self.mtim; | ||
| 156 | } | ||
| 157 | |||
| 158 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 159 | return self.ctim; | ||
| 160 | } | ||
| 161 | }; | ||
| 162 | |||
| 163 | pub const VDSO = void; | 131 | pub const VDSO = void; |
lib/std/os/linux/io_uring_sqe.zig+2-2| ... | @@ -420,10 +420,10 @@ pub const io_uring_sqe = extern struct { | ... | @@ -420,10 +420,10 @@ pub const io_uring_sqe = extern struct { |
| 420 | fd: linux.fd_t, | 420 | fd: linux.fd_t, |
| 421 | path: [*:0]const u8, | 421 | path: [*:0]const u8, |
| 422 | flags: u32, | 422 | flags: u32, |
| 423 | mask: u32, | 423 | mask: linux.STATX, |
| 424 | buf: *linux.Statx, | 424 | buf: *linux.Statx, |
| 425 | ) void { | 425 | ) void { |
| 426 | sqe.prep_rw(.STATX, fd, @intFromPtr(path), mask, @intFromPtr(buf)); | 426 | sqe.prep_rw(.STATX, fd, @intFromPtr(path), @as(u32, @bitCast(mask)), @intFromPtr(buf)); |
| 427 | sqe.rw_flags = flags; | 427 | sqe.rw_flags = flags; |
| 428 | } | 428 | } |
| 429 | 429 |
lib/std/os/linux/loongarch64.zig-34| ... | @@ -125,47 +125,13 @@ pub fn clone() callconv(.naked) u64 { | ... | @@ -125,47 +125,13 @@ pub fn clone() callconv(.naked) u64 { |
| 125 | ); | 125 | ); |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | pub const blksize_t = i32; | ||
| 129 | pub const nlink_t = u32; | 128 | pub const nlink_t = u32; |
| 130 | pub const time_t = i64; | 129 | pub const time_t = i64; |
| 131 | pub const mode_t = u32; | ||
| 132 | pub const off_t = i64; | 130 | pub const off_t = i64; |
| 133 | pub const ino_t = u64; | 131 | pub const ino_t = u64; |
| 134 | pub const dev_t = u32; | 132 | pub const dev_t = u32; |
| 135 | pub const blkcnt_t = i64; | 133 | pub const blkcnt_t = i64; |
| 136 | 134 | ||
| 137 | // The `stat` definition used by the Linux kernel. | ||
| 138 | pub const Stat = extern struct { | ||
| 139 | dev: dev_t, | ||
| 140 | ino: ino_t, | ||
| 141 | mode: mode_t, | ||
| 142 | nlink: nlink_t, | ||
| 143 | uid: std.os.linux.uid_t, | ||
| 144 | gid: std.os.linux.gid_t, | ||
| 145 | rdev: dev_t, | ||
| 146 | _pad1: u64, | ||
| 147 | size: off_t, | ||
| 148 | blksize: blksize_t, | ||
| 149 | _pad2: i32, | ||
| 150 | blocks: blkcnt_t, | ||
| 151 | atim: std.os.linux.timespec, | ||
| 152 | mtim: std.os.linux.timespec, | ||
| 153 | ctim: std.os.linux.timespec, | ||
| 154 | _pad3: [2]u32, | ||
| 155 | |||
| 156 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 157 | return self.atim; | ||
| 158 | } | ||
| 159 | |||
| 160 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 161 | return self.mtim; | ||
| 162 | } | ||
| 163 | |||
| 164 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 165 | return self.ctim; | ||
| 166 | } | ||
| 167 | }; | ||
| 168 | |||
| 169 | pub const VDSO = struct { | 135 | pub const VDSO = struct { |
| 170 | pub const CGT_SYM = "__vdso_clock_gettime"; | 136 | pub const CGT_SYM = "__vdso_clock_gettime"; |
| 171 | pub const CGT_VER = "LINUX_5.10"; | 137 | pub const CGT_VER = "LINUX_5.10"; |
lib/std/os/linux/m68k.zig-31| ... | @@ -151,36 +151,5 @@ pub const ino_t = u64; | ... | @@ -151,36 +151,5 @@ pub const ino_t = u64; |
| 151 | pub const dev_t = u64; | 151 | pub const dev_t = u64; |
| 152 | pub const blkcnt_t = i64; | 152 | pub const blkcnt_t = i64; |
| 153 | 153 | ||
| 154 | pub const Stat = extern struct { | ||
| 155 | dev: dev_t, | ||
| 156 | __pad: i16, | ||
| 157 | __ino_truncated: i32, | ||
| 158 | mode: mode_t, | ||
| 159 | nlink: nlink_t, | ||
| 160 | uid: std.os.linux.uid_t, | ||
| 161 | gid: std.os.linux.gid_t, | ||
| 162 | rdev: dev_t, | ||
| 163 | __pad2: i16, | ||
| 164 | size: off_t, | ||
| 165 | blksize: blksize_t, | ||
| 166 | blocks: blkcnt_t, | ||
| 167 | atim: std.os.linux.timespec, | ||
| 168 | mtim: std.os.linux.timespec, | ||
| 169 | ctim: std.os.linux.timespec, | ||
| 170 | ino: ino_t, | ||
| 171 | |||
| 172 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 173 | return self.atim; | ||
| 174 | } | ||
| 175 | |||
| 176 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 177 | return self.mtim; | ||
| 178 | } | ||
| 179 | |||
| 180 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 181 | return self.ctim; | ||
| 182 | } | ||
| 183 | }; | ||
| 184 | |||
| 185 | // No VDSO used as of glibc 112a0ae18b831bf31f44d81b82666980312511d6. | 154 | // No VDSO used as of glibc 112a0ae18b831bf31f44d81b82666980312511d6. |
| 186 | pub const VDSO = void; | 155 | pub const VDSO = void; |
lib/std/os/linux/mips.zig-32| ... | @@ -238,35 +238,3 @@ pub const off_t = i64; | ... | @@ -238,35 +238,3 @@ pub const off_t = i64; |
| 238 | pub const ino_t = u64; | 238 | pub const ino_t = u64; |
| 239 | pub const dev_t = u64; | 239 | pub const dev_t = u64; |
| 240 | pub const blkcnt_t = i64; | 240 | pub const blkcnt_t = i64; |
| 241 | |||
| 242 | // The `stat64` definition used by the Linux kernel. | ||
| 243 | pub const Stat = extern struct { | ||
| 244 | dev: dev_t, | ||
| 245 | __pad0: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). | ||
| 246 | ino: ino_t, | ||
| 247 | mode: mode_t, | ||
| 248 | nlink: nlink_t, | ||
| 249 | uid: std.os.linux.uid_t, | ||
| 250 | gid: std.os.linux.gid_t, | ||
| 251 | rdev: dev_t, | ||
| 252 | __pad1: [2]u32, | ||
| 253 | size: off_t, | ||
| 254 | atim: std.os.linux.timespec, | ||
| 255 | mtim: std.os.linux.timespec, | ||
| 256 | ctim: std.os.linux.timespec, | ||
| 257 | blksize: blksize_t, | ||
| 258 | __pad3: u32, | ||
| 259 | blocks: blkcnt_t, | ||
| 260 | |||
| 261 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 262 | return self.atim; | ||
| 263 | } | ||
| 264 | |||
| 265 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 266 | return self.mtim; | ||
| 267 | } | ||
| 268 | |||
| 269 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 270 | return self.ctim; | ||
| 271 | } | ||
| 272 | }; |
lib/std/os/linux/mips64.zig-44| ... | @@ -192,47 +192,3 @@ pub const off_t = i64; | ... | @@ -192,47 +192,3 @@ pub const off_t = i64; |
| 192 | pub const ino_t = u64; | 192 | pub const ino_t = u64; |
| 193 | pub const dev_t = u64; | 193 | pub const dev_t = u64; |
| 194 | pub const blkcnt_t = i64; | 194 | pub const blkcnt_t = i64; |
| 195 | |||
| 196 | // The `stat` definition used by the Linux kernel. | ||
| 197 | pub const Stat = extern struct { | ||
| 198 | dev: dev_t, | ||
| 199 | __pad0: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). | ||
| 200 | ino: ino_t, | ||
| 201 | mode: mode_t, | ||
| 202 | nlink: nlink_t, | ||
| 203 | uid: std.os.linux.uid_t, | ||
| 204 | gid: std.os.linux.gid_t, | ||
| 205 | rdev: dev_t, | ||
| 206 | __pad1: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). | ||
| 207 | size: off_t, | ||
| 208 | atim: u32, | ||
| 209 | atim_nsec: u32, | ||
| 210 | mtim: u32, | ||
| 211 | mtim_nsec: u32, | ||
| 212 | ctim: u32, | ||
| 213 | ctim_nsec: u32, | ||
| 214 | blksize: blksize_t, | ||
| 215 | __pad3: u32, | ||
| 216 | blocks: blkcnt_t, | ||
| 217 | |||
| 218 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 219 | return .{ | ||
| 220 | .sec = self.atim, | ||
| 221 | .nsec = self.atim_nsec, | ||
| 222 | }; | ||
| 223 | } | ||
| 224 | |||
| 225 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 226 | return .{ | ||
| 227 | .sec = self.mtim, | ||
| 228 | .nsec = self.mtim_nsec, | ||
| 229 | }; | ||
| 230 | } | ||
| 231 | |||
| 232 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 233 | return .{ | ||
| 234 | .sec = self.ctim, | ||
| 235 | .nsec = self.ctim_nsec, | ||
| 236 | }; | ||
| 237 | } | ||
| 238 | }; |
lib/std/os/linux/mipsn32.zig-44| ... | @@ -192,47 +192,3 @@ pub const off_t = i64; | ... | @@ -192,47 +192,3 @@ pub const off_t = i64; |
| 192 | pub const ino_t = u64; | 192 | pub const ino_t = u64; |
| 193 | pub const dev_t = u64; | 193 | pub const dev_t = u64; |
| 194 | pub const blkcnt_t = i64; | 194 | pub const blkcnt_t = i64; |
| 195 | |||
| 196 | // The `stat` definition used by the Linux kernel. | ||
| 197 | pub const Stat = extern struct { | ||
| 198 | dev: dev_t, | ||
| 199 | __pad0: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). | ||
| 200 | ino: ino_t, | ||
| 201 | mode: mode_t, | ||
| 202 | nlink: nlink_t, | ||
| 203 | uid: std.os.linux.uid_t, | ||
| 204 | gid: std.os.linux.gid_t, | ||
| 205 | rdev: dev_t, | ||
| 206 | __pad1: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). | ||
| 207 | size: off_t, | ||
| 208 | atim: u32, | ||
| 209 | atim_nsec: u32, | ||
| 210 | mtim: u32, | ||
| 211 | mtim_nsec: u32, | ||
| 212 | ctim: u32, | ||
| 213 | ctim_nsec: u32, | ||
| 214 | blksize: blksize_t, | ||
| 215 | __pad3: u32, | ||
| 216 | blocks: blkcnt_t, | ||
| 217 | |||
| 218 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 219 | return .{ | ||
| 220 | .sec = self.atim, | ||
| 221 | .nsec = self.atim_nsec, | ||
| 222 | }; | ||
| 223 | } | ||
| 224 | |||
| 225 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 226 | return .{ | ||
| 227 | .sec = self.mtim, | ||
| 228 | .nsec = self.mtim_nsec, | ||
| 229 | }; | ||
| 230 | } | ||
| 231 | |||
| 232 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 233 | return .{ | ||
| 234 | .sec = self.ctim, | ||
| 235 | .nsec = self.ctim_nsec, | ||
| 236 | }; | ||
| 237 | } | ||
| 238 | }; |
lib/std/os/linux/or1k.zig-32| ... | @@ -139,35 +139,3 @@ pub const off_t = i64; | ... | @@ -139,35 +139,3 @@ pub const off_t = i64; |
| 139 | pub const ino_t = u64; | 139 | pub const ino_t = u64; |
| 140 | pub const dev_t = u64; | 140 | pub const dev_t = u64; |
| 141 | pub const blkcnt_t = i64; | 141 | pub const blkcnt_t = i64; |
| 142 | |||
| 143 | // The `stat64` definition used by the Linux kernel. | ||
| 144 | pub const Stat = extern struct { | ||
| 145 | dev: dev_t, | ||
| 146 | ino: ino_t, | ||
| 147 | mode: mode_t, | ||
| 148 | nlink: nlink_t, | ||
| 149 | uid: std.os.linux.uid_t, | ||
| 150 | gid: std.os.linux.gid_t, | ||
| 151 | rdev: dev_t, | ||
| 152 | _pad0: [2]u32, | ||
| 153 | size: off_t, | ||
| 154 | blksize: blksize_t, | ||
| 155 | _pad1: u32, | ||
| 156 | blocks: blkcnt_t, | ||
| 157 | atim: std.os.linux.timespec, | ||
| 158 | mtim: std.os.linux.timespec, | ||
| 159 | ctim: std.os.linux.timespec, | ||
| 160 | _pad2: [2]u32, | ||
| 161 | |||
| 162 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 163 | return self.atim; | ||
| 164 | } | ||
| 165 | |||
| 166 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 167 | return self.mtim; | ||
| 168 | } | ||
| 169 | |||
| 170 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 171 | return self.ctim; | ||
| 172 | } | ||
| 173 | }; |
lib/std/os/linux/powerpc.zig-32| ... | @@ -276,35 +276,3 @@ pub const mode_t = u32; | ... | @@ -276,35 +276,3 @@ pub const mode_t = u32; |
| 276 | pub const off_t = i64; | 276 | pub const off_t = i64; |
| 277 | pub const ino_t = u64; | 277 | pub const ino_t = u64; |
| 278 | pub const dev_t = u64; | 278 | pub const dev_t = u64; |
| 279 | pub const blkcnt_t = i64; | ||
| 280 | |||
| 281 | // The `stat` definition used by the Linux kernel. | ||
| 282 | pub const Stat = extern struct { | ||
| 283 | dev: dev_t, | ||
| 284 | ino: ino_t, | ||
| 285 | mode: mode_t, | ||
| 286 | nlink: nlink_t, | ||
| 287 | uid: std.os.linux.uid_t, | ||
| 288 | gid: std.os.linux.gid_t, | ||
| 289 | rdev: dev_t, | ||
| 290 | __rdev_padding: i16, | ||
| 291 | size: off_t, | ||
| 292 | blksize: blksize_t, | ||
| 293 | blocks: blkcnt_t, | ||
| 294 | atim: std.os.linux.timespec, | ||
| 295 | mtim: std.os.linux.timespec, | ||
| 296 | ctim: std.os.linux.timespec, | ||
| 297 | __unused: [2]u32, | ||
| 298 | |||
| 299 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 300 | return self.atim; | ||
| 301 | } | ||
| 302 | |||
| 303 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 304 | return self.mtim; | ||
| 305 | } | ||
| 306 | |||
| 307 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 308 | return self.ctim; | ||
| 309 | } | ||
| 310 | }; |
lib/std/os/linux/powerpc64.zig-30| ... | @@ -262,33 +262,3 @@ pub const off_t = i64; | ... | @@ -262,33 +262,3 @@ pub const off_t = i64; |
| 262 | pub const ino_t = u64; | 262 | pub const ino_t = u64; |
| 263 | pub const dev_t = u64; | 263 | pub const dev_t = u64; |
| 264 | pub const blkcnt_t = i64; | 264 | pub const blkcnt_t = i64; |
| 265 | |||
| 266 | // The `stat` definition used by the Linux kernel. | ||
| 267 | pub const Stat = extern struct { | ||
| 268 | dev: dev_t, | ||
| 269 | ino: ino_t, | ||
| 270 | nlink: nlink_t, | ||
| 271 | mode: mode_t, | ||
| 272 | uid: std.os.linux.uid_t, | ||
| 273 | gid: std.os.linux.gid_t, | ||
| 274 | rdev: dev_t, | ||
| 275 | size: off_t, | ||
| 276 | blksize: blksize_t, | ||
| 277 | blocks: blkcnt_t, | ||
| 278 | atim: std.os.linux.timespec, | ||
| 279 | mtim: std.os.linux.timespec, | ||
| 280 | ctim: std.os.linux.timespec, | ||
| 281 | __unused: [3]u64, | ||
| 282 | |||
| 283 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 284 | return self.atim; | ||
| 285 | } | ||
| 286 | |||
| 287 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 288 | return self.mtim; | ||
| 289 | } | ||
| 290 | |||
| 291 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 292 | return self.ctim; | ||
| 293 | } | ||
| 294 | }; |
lib/std/os/linux/riscv32.zig-33| ... | @@ -131,39 +131,6 @@ pub const mode_t = u32; | ... | @@ -131,39 +131,6 @@ pub const mode_t = u32; |
| 131 | pub const off_t = i64; | 131 | pub const off_t = i64; |
| 132 | pub const ino_t = u64; | 132 | pub const ino_t = u64; |
| 133 | pub const dev_t = u64; | 133 | pub const dev_t = u64; |
| 134 | pub const blkcnt_t = i64; | ||
| 135 | |||
| 136 | // The `stat` definition used by the Linux kernel. | ||
| 137 | pub const Stat = extern struct { | ||
| 138 | dev: dev_t, | ||
| 139 | ino: ino_t, | ||
| 140 | mode: mode_t, | ||
| 141 | nlink: nlink_t, | ||
| 142 | uid: std.os.linux.uid_t, | ||
| 143 | gid: std.os.linux.gid_t, | ||
| 144 | rdev: dev_t, | ||
| 145 | __pad: u32, | ||
| 146 | size: off_t, | ||
| 147 | blksize: blksize_t, | ||
| 148 | __pad2: i32, | ||
| 149 | blocks: blkcnt_t, | ||
| 150 | atim: std.os.linux.timespec, | ||
| 151 | mtim: std.os.linux.timespec, | ||
| 152 | ctim: std.os.linux.timespec, | ||
| 153 | __unused: [2]u32, | ||
| 154 | |||
| 155 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 156 | return self.atim; | ||
| 157 | } | ||
| 158 | |||
| 159 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 160 | return self.mtim; | ||
| 161 | } | ||
| 162 | |||
| 163 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 164 | return self.ctim; | ||
| 165 | } | ||
| 166 | }; | ||
| 167 | 134 | ||
| 168 | pub const VDSO = struct { | 135 | pub const VDSO = struct { |
| 169 | pub const CGT_SYM = "__vdso_clock_gettime"; | 136 | pub const CGT_SYM = "__vdso_clock_gettime"; |
lib/std/os/linux/riscv64.zig-33| ... | @@ -131,39 +131,6 @@ pub const mode_t = u32; | ... | @@ -131,39 +131,6 @@ pub const mode_t = u32; |
| 131 | pub const off_t = i64; | 131 | pub const off_t = i64; |
| 132 | pub const ino_t = u64; | 132 | pub const ino_t = u64; |
| 133 | pub const dev_t = u64; | 133 | pub const dev_t = u64; |
| 134 | pub const blkcnt_t = i64; | ||
| 135 | |||
| 136 | // The `stat` definition used by the Linux kernel. | ||
| 137 | pub const Stat = extern struct { | ||
| 138 | dev: dev_t, | ||
| 139 | ino: ino_t, | ||
| 140 | mode: mode_t, | ||
| 141 | nlink: nlink_t, | ||
| 142 | uid: std.os.linux.uid_t, | ||
| 143 | gid: std.os.linux.gid_t, | ||
| 144 | rdev: dev_t, | ||
| 145 | __pad: u64, | ||
| 146 | size: off_t, | ||
| 147 | blksize: blksize_t, | ||
| 148 | __pad2: i32, | ||
| 149 | blocks: blkcnt_t, | ||
| 150 | atim: std.os.linux.timespec, | ||
| 151 | mtim: std.os.linux.timespec, | ||
| 152 | ctim: std.os.linux.timespec, | ||
| 153 | __unused: [2]u32, | ||
| 154 | |||
| 155 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 156 | return self.atim; | ||
| 157 | } | ||
| 158 | |||
| 159 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 160 | return self.mtim; | ||
| 161 | } | ||
| 162 | |||
| 163 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 164 | return self.ctim; | ||
| 165 | } | ||
| 166 | }; | ||
| 167 | 134 | ||
| 168 | pub const VDSO = struct { | 135 | pub const VDSO = struct { |
| 169 | pub const CGT_SYM = "__vdso_clock_gettime"; | 136 | pub const CGT_SYM = "__vdso_clock_gettime"; |
lib/std/os/linux/s390x.zig-31| ... | @@ -159,37 +159,6 @@ pub const mode_t = u32; | ... | @@ -159,37 +159,6 @@ pub const mode_t = u32; |
| 159 | pub const off_t = i64; | 159 | pub const off_t = i64; |
| 160 | pub const ino_t = u64; | 160 | pub const ino_t = u64; |
| 161 | pub const dev_t = u64; | 161 | pub const dev_t = u64; |
| 162 | pub const blkcnt_t = i64; | ||
| 163 | |||
| 164 | // The `stat` definition used by the Linux kernel. | ||
| 165 | pub const Stat = extern struct { | ||
| 166 | dev: dev_t, | ||
| 167 | ino: ino_t, | ||
| 168 | nlink: nlink_t, | ||
| 169 | mode: mode_t, | ||
| 170 | uid: std.os.linux.uid_t, | ||
| 171 | gid: std.os.linux.gid_t, | ||
| 172 | rdev: dev_t, | ||
| 173 | size: off_t, | ||
| 174 | atim: std.os.linux.timespec, | ||
| 175 | mtim: std.os.linux.timespec, | ||
| 176 | ctim: std.os.linux.timespec, | ||
| 177 | blksize: blksize_t, | ||
| 178 | blocks: blkcnt_t, | ||
| 179 | __unused: [3]c_ulong, | ||
| 180 | |||
| 181 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 182 | return self.atim; | ||
| 183 | } | ||
| 184 | |||
| 185 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 186 | return self.mtim; | ||
| 187 | } | ||
| 188 | |||
| 189 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 190 | return self.ctim; | ||
| 191 | } | ||
| 192 | }; | ||
| 193 | 162 | ||
| 194 | pub const VDSO = struct { | 163 | pub const VDSO = struct { |
| 195 | pub const CGT_SYM = "__kernel_clock_gettime"; | 164 | pub const CGT_SYM = "__kernel_clock_gettime"; |
lib/std/os/linux/sparc64.zig-35| ... | @@ -236,38 +236,3 @@ pub const dev_t = u64; | ... | @@ -236,38 +236,3 @@ pub const dev_t = u64; |
| 236 | pub const nlink_t = u32; | 236 | pub const nlink_t = u32; |
| 237 | pub const blksize_t = i64; | 237 | pub const blksize_t = i64; |
| 238 | pub const blkcnt_t = i64; | 238 | pub const blkcnt_t = i64; |
| 239 | |||
| 240 | // The `stat64` definition used by the kernel. | ||
| 241 | pub const Stat = extern struct { | ||
| 242 | dev: dev_t, | ||
| 243 | ino: ino_t, | ||
| 244 | nlink: nlink_t, | ||
| 245 | _pad: i32, | ||
| 246 | |||
| 247 | mode: mode_t, | ||
| 248 | uid: std.os.linux.uid_t, | ||
| 249 | gid: std.os.linux.gid_t, | ||
| 250 | __pad0: u32, | ||
| 251 | |||
| 252 | rdev: dev_t, | ||
| 253 | size: i64, | ||
| 254 | blksize: blksize_t, | ||
| 255 | blocks: blkcnt_t, | ||
| 256 | |||
| 257 | atim: std.os.linux.timespec, | ||
| 258 | mtim: std.os.linux.timespec, | ||
| 259 | ctim: std.os.linux.timespec, | ||
| 260 | __unused: [3]u64, | ||
| 261 | |||
| 262 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 263 | return self.atim; | ||
| 264 | } | ||
| 265 | |||
| 266 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 267 | return self.mtim; | ||
| 268 | } | ||
| 269 | |||
| 270 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 271 | return self.ctim; | ||
| 272 | } | ||
| 273 | }; |
lib/std/os/linux/test.zig+12-18| ... | @@ -10,8 +10,6 @@ const expectEqual = std.testing.expectEqual; | ... | @@ -10,8 +10,6 @@ const expectEqual = std.testing.expectEqual; |
| 10 | const fs = std.fs; | 10 | const fs = std.fs; |
| 11 | 11 | ||
| 12 | test "fallocate" { | 12 | test "fallocate" { |
| 13 | if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23809 | ||
| 14 | |||
| 15 | var tmp = std.testing.tmpDir(.{}); | 13 | var tmp = std.testing.tmpDir(.{}); |
| 16 | defer tmp.cleanup(); | 14 | defer tmp.cleanup(); |
| 17 | 15 | ||
| ... | @@ -84,26 +82,22 @@ test "statx" { | ... | @@ -84,26 +82,22 @@ test "statx" { |
| 84 | var file = try tmp.dir.createFile(tmp_file_name, .{}); | 82 | var file = try tmp.dir.createFile(tmp_file_name, .{}); |
| 85 | defer file.close(); | 83 | defer file.close(); |
| 86 | 84 | ||
| 87 | var statx_buf: linux.Statx = undefined; | 85 | var buf: linux.Statx = undefined; |
| 88 | switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { | 86 | switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .BASIC_STATS, &buf))) { |
| 89 | .SUCCESS => {}, | ||
| 90 | else => unreachable, | ||
| 91 | } | ||
| 92 | |||
| 93 | if (builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) return error.SkipZigTest; // No fstatat, so the rest of the test is meaningless. | ||
| 94 | |||
| 95 | var stat_buf: linux.Stat = undefined; | ||
| 96 | switch (linux.errno(linux.fstatat(file.handle, "", &stat_buf, linux.AT.EMPTY_PATH))) { | ||
| 97 | .SUCCESS => {}, | 87 | .SUCCESS => {}, |
| 98 | else => unreachable, | 88 | else => unreachable, |
| 99 | } | 89 | } |
| 100 | 90 | ||
| 101 | try expect(stat_buf.mode == statx_buf.mode); | 91 | const uid = linux.getuid(); |
| 102 | try expect(@as(u32, @bitCast(stat_buf.uid)) == statx_buf.uid); | 92 | const gid = linux.getgid(); |
| 103 | try expect(@as(u32, @bitCast(stat_buf.gid)) == statx_buf.gid); | 93 | if (buf.mask.MODE) |
| 104 | try expect(@as(u64, @bitCast(@as(i64, stat_buf.size))) == statx_buf.size); | 94 | try expectEqual(@as(linux.mode_t, linux.S.IFREG), buf.mode & linux.S.IFMT); |
| 105 | try expect(@as(u64, @bitCast(@as(i64, stat_buf.blksize))) == statx_buf.blksize); | 95 | if (buf.mask.UID) |
| 106 | try expect(@as(u64, @bitCast(@as(i64, stat_buf.blocks))) == statx_buf.blocks); | 96 | try expectEqual(uid, buf.uid); |
| 97 | if (buf.mask.GID) | ||
| 98 | try expectEqual(gid, buf.gid); | ||
| 99 | if (buf.mask.SIZE) | ||
| 100 | try expectEqual(@as(u64, 0), buf.size); | ||
| 107 | } | 101 | } |
| 108 | 102 | ||
| 109 | test "user and group ids" { | 103 | test "user and group ids" { |
lib/std/os/linux/x32.zig-33| ... | @@ -155,36 +155,3 @@ pub const ARCH = struct { | ... | @@ -155,36 +155,3 @@ pub const ARCH = struct { |
| 155 | pub const GET_FS = 0x1003; | 155 | pub const GET_FS = 0x1003; |
| 156 | pub const GET_GS = 0x1004; | 156 | pub const GET_GS = 0x1004; |
| 157 | }; | 157 | }; |
| 158 | |||
| 159 | // The `stat` definition used by the Linux kernel. | ||
| 160 | pub const Stat = extern struct { | ||
| 161 | dev: dev_t, | ||
| 162 | ino: ino_t, | ||
| 163 | nlink: nlink_t, | ||
| 164 | |||
| 165 | mode: mode_t, | ||
| 166 | uid: std.os.linux.uid_t, | ||
| 167 | gid: std.os.linux.gid_t, | ||
| 168 | __pad0: u32, | ||
| 169 | rdev: dev_t, | ||
| 170 | size: off_t, | ||
| 171 | blksize: blksize_t, | ||
| 172 | blocks: i64, | ||
| 173 | |||
| 174 | atim: std.os.linux.timespec, | ||
| 175 | mtim: std.os.linux.timespec, | ||
| 176 | ctim: std.os.linux.timespec, | ||
| 177 | __unused: [3]i32, | ||
| 178 | |||
| 179 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 180 | return self.atim; | ||
| 181 | } | ||
| 182 | |||
| 183 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 184 | return self.mtim; | ||
| 185 | } | ||
| 186 | |||
| 187 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 188 | return self.ctim; | ||
| 189 | } | ||
| 190 | }; |
lib/std/os/linux/x86.zig-32| ... | @@ -204,38 +204,6 @@ pub const ino_t = u64; | ... | @@ -204,38 +204,6 @@ pub const ino_t = u64; |
| 204 | pub const dev_t = u64; | 204 | pub const dev_t = u64; |
| 205 | pub const blkcnt_t = i64; | 205 | pub const blkcnt_t = i64; |
| 206 | 206 | ||
| 207 | // The `stat` definition used by the Linux kernel. | ||
| 208 | pub const Stat = extern struct { | ||
| 209 | dev: dev_t, | ||
| 210 | __dev_padding: u32, | ||
| 211 | __ino_truncated: u32, | ||
| 212 | mode: mode_t, | ||
| 213 | nlink: nlink_t, | ||
| 214 | uid: std.os.linux.uid_t, | ||
| 215 | gid: std.os.linux.gid_t, | ||
| 216 | rdev: dev_t, | ||
| 217 | __rdev_padding: u32, | ||
| 218 | size: off_t, | ||
| 219 | blksize: blksize_t, | ||
| 220 | blocks: blkcnt_t, | ||
| 221 | atim: std.os.linux.timespec, | ||
| 222 | mtim: std.os.linux.timespec, | ||
| 223 | ctim: std.os.linux.timespec, | ||
| 224 | ino: ino_t, | ||
| 225 | |||
| 226 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 227 | return self.atim; | ||
| 228 | } | ||
| 229 | |||
| 230 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 231 | return self.mtim; | ||
| 232 | } | ||
| 233 | |||
| 234 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 235 | return self.ctim; | ||
| 236 | } | ||
| 237 | }; | ||
| 238 | |||
| 239 | pub const user_desc = extern struct { | 207 | pub const user_desc = extern struct { |
| 240 | entry_number: u32, | 208 | entry_number: u32, |
| 241 | base_addr: u32, | 209 | base_addr: u32, |
lib/std/os/linux/x86_64.zig-33| ... | @@ -155,36 +155,3 @@ pub const ARCH = struct { | ... | @@ -155,36 +155,3 @@ pub const ARCH = struct { |
| 155 | pub const GET_FS = 0x1003; | 155 | pub const GET_FS = 0x1003; |
| 156 | pub const GET_GS = 0x1004; | 156 | pub const GET_GS = 0x1004; |
| 157 | }; | 157 | }; |
| 158 | |||
| 159 | // The `stat` definition used by the Linux kernel. | ||
| 160 | pub const Stat = extern struct { | ||
| 161 | dev: dev_t, | ||
| 162 | ino: ino_t, | ||
| 163 | nlink: u64, | ||
| 164 | |||
| 165 | mode: u32, | ||
| 166 | uid: std.os.linux.uid_t, | ||
| 167 | gid: std.os.linux.gid_t, | ||
| 168 | __pad0: u32, | ||
| 169 | rdev: dev_t, | ||
| 170 | size: off_t, | ||
| 171 | blksize: i64, | ||
| 172 | blocks: i64, | ||
| 173 | |||
| 174 | atim: std.os.linux.timespec, | ||
| 175 | mtim: std.os.linux.timespec, | ||
| 176 | ctim: std.os.linux.timespec, | ||
| 177 | __unused: [3]i64, | ||
| 178 | |||
| 179 | pub fn atime(self: @This()) std.os.linux.timespec { | ||
| 180 | return self.atim; | ||
| 181 | } | ||
| 182 | |||
| 183 | pub fn mtime(self: @This()) std.os.linux.timespec { | ||
| 184 | return self.mtim; | ||
| 185 | } | ||
| 186 | |||
| 187 | pub fn ctime(self: @This()) std.os.linux.timespec { | ||
| 188 | return self.ctim; | ||
| 189 | } | ||
| 190 | }; |
lib/std/posix.zig+25-13| ... | @@ -119,7 +119,18 @@ pub const STDIN_FILENO = system.STDIN_FILENO; | ... | @@ -119,7 +119,18 @@ pub const STDIN_FILENO = system.STDIN_FILENO; |
| 119 | pub const STDOUT_FILENO = system.STDOUT_FILENO; | 119 | pub const STDOUT_FILENO = system.STDOUT_FILENO; |
| 120 | pub const SYS = system.SYS; | 120 | pub const SYS = system.SYS; |
| 121 | pub const Sigaction = system.Sigaction; | 121 | pub const Sigaction = system.Sigaction; |
| 122 | pub const Stat = system.Stat; | 122 | pub const Stat = switch (native_os) { |
| 123 | // Has no concept of `stat`. | ||
| 124 | .windows => void, | ||
| 125 | // The `stat` bits/wrappers are removed due to having to maintain the | ||
| 126 | // different varying `struct stat`s per target and libc, leading to runtime | ||
| 127 | // errors. | ||
| 128 | // | ||
| 129 | // Users targeting linux should add a comptime check and use `statx`, | ||
| 130 | // similar to how `std.fs.File.stat` does. | ||
| 131 | .linux => void, | ||
| 132 | else => system.Stat, | ||
| 133 | }; | ||
| 123 | pub const T = system.T; | 134 | pub const T = system.T; |
| 124 | pub const TCP = system.TCP; | 135 | pub const TCP = system.TCP; |
| 125 | pub const VDSO = system.VDSO; | 136 | pub const VDSO = system.VDSO; |
| ... | @@ -480,15 +491,21 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr | ... | @@ -480,15 +491,21 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr |
| 480 | } | 491 | } |
| 481 | defer close(pathfd); | 492 | defer close(pathfd); |
| 482 | 493 | ||
| 483 | const stat = fstatatZ(pathfd, "", AT.EMPTY_PATH) catch |err| switch (err) { | 494 | const path_mode = if (linux.wrapped.statx( |
| 495 | pathfd, | ||
| 496 | "", | ||
| 497 | AT.EMPTY_PATH, | ||
| 498 | .{ .TYPE = true }, | ||
| 499 | )) |stx| blk: { | ||
| 500 | assert(stx.mask.TYPE); | ||
| 501 | break :blk stx.mode; | ||
| 502 | } else |err| switch (err) { | ||
| 484 | error.NameTooLong => unreachable, | 503 | error.NameTooLong => unreachable, |
| 485 | error.FileNotFound => unreachable, | 504 | error.FileNotFound => unreachable, |
| 486 | error.Streaming => unreachable, | ||
| 487 | error.BadPathName => return error.Unexpected, | ||
| 488 | error.Canceled => return error.Canceled, | ||
| 489 | else => |e| return e, | 505 | else => |e| return e, |
| 490 | }; | 506 | }; |
| 491 | if ((stat.mode & S.IFMT) == S.IFLNK) | 507 | // Even though we only wanted TYPE, the kernel can still fill in the additional bits. |
| 508 | if ((path_mode & S.IFMT) == S.IFLNK) | ||
| 492 | return error.OperationNotSupported; | 509 | return error.OperationNotSupported; |
| 493 | 510 | ||
| 494 | var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; | 511 | var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; |
| ... | @@ -3843,13 +3860,9 @@ pub fn fstat(fd: fd_t) FStatError!Stat { | ... | @@ -3843,13 +3860,9 @@ pub fn fstat(fd: fd_t) FStatError!Stat { |
| 3843 | if (native_os == .wasi and !builtin.link_libc) { | 3860 | if (native_os == .wasi and !builtin.link_libc) { |
| 3844 | return Stat.fromFilestat(try std.os.fstat_wasi(fd)); | 3861 | return Stat.fromFilestat(try std.os.fstat_wasi(fd)); |
| 3845 | } | 3862 | } |
| 3846 | if (native_os == .windows) { | ||
| 3847 | @compileError("fstat is not yet implemented on Windows"); | ||
| 3848 | } | ||
| 3849 | 3863 | ||
| 3850 | const fstat_sym = if (lfs64_abi) system.fstat64 else system.fstat; | ||
| 3851 | var stat = mem.zeroes(Stat); | 3864 | var stat = mem.zeroes(Stat); |
| 3852 | switch (errno(fstat_sym(fd, &stat))) { | 3865 | switch (errno(system.fstat(fd, &stat))) { |
| 3853 | .SUCCESS => return stat, | 3866 | .SUCCESS => return stat, |
| 3854 | .INVAL => unreachable, | 3867 | .INVAL => unreachable, |
| 3855 | .BADF => unreachable, // Always a race condition. | 3868 | .BADF => unreachable, // Always a race condition. |
| ... | @@ -3889,9 +3902,8 @@ pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!S | ... | @@ -3889,9 +3902,8 @@ pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!S |
| 3889 | @compileError("use std.Io instead"); | 3902 | @compileError("use std.Io instead"); |
| 3890 | } | 3903 | } |
| 3891 | 3904 | ||
| 3892 | const fstatat_sym = if (lfs64_abi) system.fstatat64 else system.fstatat; | ||
| 3893 | var stat = mem.zeroes(Stat); | 3905 | var stat = mem.zeroes(Stat); |
| 3894 | switch (errno(fstatat_sym(dirfd, pathname, &stat, flags))) { | 3906 | switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) { |
| 3895 | .SUCCESS => return stat, | 3907 | .SUCCESS => return stat, |
| 3896 | .INVAL => unreachable, | 3908 | .INVAL => unreachable, |
| 3897 | .BADF => unreachable, // Always a race condition. | 3909 | .BADF => unreachable, // Always a race condition. |
lib/std/posix/test.zig+78-21| ... | @@ -16,6 +16,7 @@ const AtomicRmwOp = std.builtin.AtomicRmwOp; | ... | @@ -16,6 +16,7 @@ const AtomicRmwOp = std.builtin.AtomicRmwOp; |
| 16 | const AtomicOrder = std.builtin.AtomicOrder; | 16 | const AtomicOrder = std.builtin.AtomicOrder; |
| 17 | const native_os = builtin.target.os.tag; | 17 | const native_os = builtin.target.os.tag; |
| 18 | const tmpDir = std.testing.tmpDir; | 18 | const tmpDir = std.testing.tmpDir; |
| 19 | const AT = posix.AT; | ||
| 19 | 20 | ||
| 20 | // NOTE: several additional tests are in test/standalone/posix/. Any tests that mutate | 21 | // NOTE: several additional tests are in test/standalone/posix/. Any tests that mutate |
| 21 | // process-wide POSIX state (cwd, signals, etc) cannot be Zig unit tests and should be over there. | 22 | // process-wide POSIX state (cwd, signals, etc) cannot be Zig unit tests and should be over there. |
| ... | @@ -123,10 +124,24 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { | ... | @@ -123,10 +124,24 @@ fn testReadlink(target_path: []const u8, symlink_path: []const u8) !void { |
| 123 | try expect(mem.eql(u8, target_path, given)); | 124 | try expect(mem.eql(u8, target_path, given)); |
| 124 | } | 125 | } |
| 125 | 126 | ||
| 126 | test "linkat with different directories" { | 127 | fn getLinkInfo(fd: posix.fd_t) !struct { posix.ino_t, posix.nlink_t } { |
| 127 | if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstatat()`. | 128 | if (native_os == .linux) { |
| 128 | if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons. | 129 | const stx = try linux.wrapped.statx( |
| 130 | fd, | ||
| 131 | "", | ||
| 132 | posix.AT.EMPTY_PATH, | ||
| 133 | .{ .INO = true, .NLINK = true }, | ||
| 134 | ); | ||
| 135 | std.debug.assert(stx.mask.INO); | ||
| 136 | std.debug.assert(stx.mask.NLINK); | ||
| 137 | return .{ stx.ino, stx.nlink }; | ||
| 138 | } | ||
| 129 | 139 | ||
| 140 | const st = try posix.fstat(fd); | ||
| 141 | return .{ st.ino, st.nlink }; | ||
| 142 | } | ||
| 143 | |||
| 144 | test "linkat with different directories" { | ||
| 130 | switch (native_os) { | 145 | switch (native_os) { |
| 131 | .wasi, .linux, .illumos => {}, | 146 | .wasi, .linux, .illumos => {}, |
| 132 | else => return error.SkipZigTest, | 147 | else => return error.SkipZigTest, |
| ... | @@ -153,19 +168,49 @@ test "linkat with different directories" { | ... | @@ -153,19 +168,49 @@ test "linkat with different directories" { |
| 153 | defer nfd.close(); | 168 | defer nfd.close(); |
| 154 | 169 | ||
| 155 | { | 170 | { |
| 156 | const estat = try posix.fstat(efd.handle); | 171 | const eino, _ = try getLinkInfo(efd.handle); |
| 157 | const nstat = try posix.fstat(nfd.handle); | 172 | const nino, const nlink = try getLinkInfo(nfd.handle); |
| 158 | try testing.expectEqual(estat.ino, nstat.ino); | 173 | try testing.expectEqual(eino, nino); |
| 159 | try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); | 174 | try testing.expectEqual(@as(posix.nlink_t, 2), nlink); |
| 160 | } | 175 | } |
| 161 | 176 | ||
| 162 | // Test 2: remove link | 177 | // Test 2: remove link |
| 163 | try posix.unlinkat(subdir.fd, link_name, 0); | 178 | try posix.unlinkat(subdir.fd, link_name, 0); |
| 179 | _, const elink = try getLinkInfo(efd.handle); | ||
| 180 | try testing.expectEqual(@as(posix.nlink_t, 1), elink); | ||
| 181 | } | ||
| 164 | 182 | ||
| 165 | { | 183 | test "fstatat" { |
| 166 | const estat = try posix.fstat(efd.handle); | 184 | if (posix.Stat == void) return error.SkipZigTest; |
| 167 | try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); | 185 | if (native_os == .wasi and !builtin.link_libc) return error.SkipZigTest; |
| 168 | } | 186 | |
| 187 | var tmp = tmpDir(.{}); | ||
| 188 | defer tmp.cleanup(); | ||
| 189 | |||
| 190 | // create dummy file | ||
| 191 | const contents = "nonsense"; | ||
| 192 | try tmp.dir.writeFile(.{ .sub_path = "file.txt", .data = contents }); | ||
| 193 | |||
| 194 | // fetch file's info on the opened fd directly | ||
| 195 | const file = try tmp.dir.openFile("file.txt", .{}); | ||
| 196 | const stat = try posix.fstat(file.handle); | ||
| 197 | defer file.close(); | ||
| 198 | |||
| 199 | // now repeat but using `fstatat` instead | ||
| 200 | const statat = try posix.fstatat(tmp.dir.fd, "file.txt", posix.AT.SYMLINK_NOFOLLOW); | ||
| 201 | |||
| 202 | try expectEqual(stat.dev, statat.dev); | ||
| 203 | try expectEqual(stat.ino, statat.ino); | ||
| 204 | try expectEqual(stat.nlink, statat.nlink); | ||
| 205 | try expectEqual(stat.mode, statat.mode); | ||
| 206 | try expectEqual(stat.uid, statat.uid); | ||
| 207 | try expectEqual(stat.gid, statat.gid); | ||
| 208 | try expectEqual(stat.rdev, statat.rdev); | ||
| 209 | try expectEqual(stat.size, statat.size); | ||
| 210 | try expectEqual(stat.blksize, statat.blksize); | ||
| 211 | // The stat.blocks/statat.blocks count is managed by the filesystem and may | ||
| 212 | // change if the file is stored in a journal or "inline". | ||
| 213 | // try expectEqual(stat.blocks, statat.blocks); | ||
| 169 | } | 214 | } |
| 170 | 215 | ||
| 171 | test "readlinkat" { | 216 | test "readlinkat" { |
| ... | @@ -906,14 +951,31 @@ test "pwrite with empty buffer" { | ... | @@ -906,14 +951,31 @@ test "pwrite with empty buffer" { |
| 906 | try expectEqual(rc, 0); | 951 | try expectEqual(rc, 0); |
| 907 | } | 952 | } |
| 908 | 953 | ||
| 954 | fn getFileMode(dir: posix.fd_t, path: []const u8) !posix.mode_t { | ||
| 955 | const path_z = try posix.toPosixPath(path); | ||
| 956 | const mode: posix.mode_t = if (native_os == .linux) blk: { | ||
| 957 | const stx = try linux.wrapped.statx( | ||
| 958 | dir, | ||
| 959 | &path_z, | ||
| 960 | posix.AT.SYMLINK_NOFOLLOW, | ||
| 961 | .{ .MODE = true }, | ||
| 962 | ); | ||
| 963 | std.debug.assert(stx.mask.MODE); | ||
| 964 | break :blk stx.mode; | ||
| 965 | } else blk: { | ||
| 966 | const st = try posix.fstatatZ(dir, &path_z, posix.AT.SYMLINK_NOFOLLOW); | ||
| 967 | break :blk st.mode; | ||
| 968 | }; | ||
| 969 | |||
| 970 | return mode & 0b111_111_111; | ||
| 971 | } | ||
| 972 | |||
| 909 | fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void { | 973 | fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void { |
| 910 | const st = try posix.fstatat(dir, file, posix.AT.SYMLINK_NOFOLLOW); | 974 | const actual = try getFileMode(dir, file); |
| 911 | try expectEqual(mode, st.mode & 0b111_111_111); | 975 | try expectEqual(mode, actual & 0b111_111_111); |
| 912 | } | 976 | } |
| 913 | 977 | ||
| 914 | test "fchmodat smoke test" { | 978 | test "fchmodat smoke test" { |
| 915 | if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23808 | ||
| 916 | |||
| 917 | if (!std.fs.has_executable_bit) return error.SkipZigTest; | 979 | if (!std.fs.has_executable_bit) return error.SkipZigTest; |
| 918 | 980 | ||
| 919 | var tmp = tmpDir(.{}); | 981 | var tmp = tmpDir(.{}); |
| ... | @@ -928,13 +990,8 @@ test "fchmodat smoke test" { | ... | @@ -928,13 +990,8 @@ test "fchmodat smoke test" { |
| 928 | ); | 990 | ); |
| 929 | posix.close(fd); | 991 | posix.close(fd); |
| 930 | 992 | ||
| 931 | if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstatat()`. | ||
| 932 | |||
| 933 | try posix.symlinkat("regfile", tmp.dir.fd, "symlink"); | 993 | try posix.symlinkat("regfile", tmp.dir.fd, "symlink"); |
| 934 | const sym_mode = blk: { | 994 | const sym_mode = try getFileMode(tmp.dir.fd, "symlink"); |
| 935 | const st = try posix.fstatat(tmp.dir.fd, "symlink", posix.AT.SYMLINK_NOFOLLOW); | ||
| 936 | break :blk st.mode & 0b111_111_111; | ||
| 937 | }; | ||
| 938 | 995 | ||
| 939 | try posix.fchmodat(tmp.dir.fd, "regfile", 0o640, 0); | 996 | try posix.fchmodat(tmp.dir.fd, "regfile", 0o640, 0); |
| 940 | try expectMode(tmp.dir.fd, "regfile", 0o640); | 997 | try expectMode(tmp.dir.fd, "regfile", 0o640); |
src/link/MappedFile.zig+14| ... | @@ -54,6 +54,20 @@ pub fn init(file: std.Io.File, gpa: std.mem.Allocator) !MappedFile { | ... | @@ -54,6 +54,20 @@ pub fn init(file: std.Io.File, gpa: std.mem.Allocator) !MappedFile { |
| 54 | }, | 54 | }, |
| 55 | }; | 55 | }; |
| 56 | } | 56 | } |
| 57 | if (is_linux) { | ||
| 58 | const statx = try linux.wrapped.statx( | ||
| 59 | mf.file.handle, | ||
| 60 | "", | ||
| 61 | std.posix.AT.EMPTY_PATH, | ||
| 62 | .{ .TYPE = true, .SIZE = true, .BLOCKS = true }, | ||
| 63 | ); | ||
| 64 | assert(statx.mask.TYPE); | ||
| 65 | assert(statx.mask.SIZE); | ||
| 66 | assert(statx.mask.BLOCKS); | ||
| 67 | |||
| 68 | if (!std.posix.S.ISREG(statx.mode)) return error.PathAlreadyExists; | ||
| 69 | break :stat .{ statx.size, @max(std.heap.pageSize(), statx.blksize) }; | ||
| 70 | } | ||
| 57 | const stat = try std.posix.fstat(mf.file.handle); | 71 | const stat = try std.posix.fstat(mf.file.handle); |
| 58 | if (!std.posix.S.ISREG(stat.mode)) return error.PathAlreadyExists; | 72 | if (!std.posix.S.ISREG(stat.mode)) return error.PathAlreadyExists; |
| 59 | break :stat .{ @bitCast(stat.size), @max(std.heap.pageSize(), stat.blksize) }; | 73 | break :stat .{ @bitCast(stat.size), @max(std.heap.pageSize(), stat.blksize) }; |
test/standalone/glibc_compat/glibc_runtime_check.zig+6-3| ... | @@ -23,16 +23,19 @@ const c_string = @cImport( | ... | @@ -23,16 +23,19 @@ const c_string = @cImport( |
| 23 | // Version of glibc this test is being built to run against | 23 | // Version of glibc this test is being built to run against |
| 24 | const glibc_ver = builtin.os.versionRange().gnuLibCVersion().?; | 24 | const glibc_ver = builtin.os.versionRange().gnuLibCVersion().?; |
| 25 | 25 | ||
| 26 | extern "c" fn fstatat(dirfd: i32, path: [*:0]const u8, buf: [*]const u8, flag: u32) c_int; | ||
| 27 | extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: [*]const u8) c_int; | ||
| 28 | |||
| 26 | // PR #17034 - fstat moved between libc_nonshared and libc | 29 | // PR #17034 - fstat moved between libc_nonshared and libc |
| 27 | fn checkStat() !void { | 30 | fn checkStat() !void { |
| 28 | const cwdFd = std.fs.cwd().fd; | 31 | const cwdFd = std.fs.cwd().fd; |
| 29 | 32 | ||
| 30 | var stat = std.mem.zeroes(std.c.Stat); | 33 | var buf: [256]u8 = @splat(0); |
| 31 | var result = std.c.fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &stat, 0); | 34 | var result = fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &buf, 0); |
| 32 | assert(result == -1); | 35 | assert(result == -1); |
| 33 | assert(std.posix.errno(result) == .NOENT); | 36 | assert(std.posix.errno(result) == .NOENT); |
| 34 | 37 | ||
| 35 | result = std.c.stat("a_file_that_definitely_does_not_exist", &stat); | 38 | result = stat("a_file_that_definitely_does_not_exist", &buf); |
| 36 | assert(result == -1); | 39 | assert(result == -1); |
| 37 | assert(std.posix.errno(result) == .NOENT); | 40 | assert(std.posix.errno(result) == .NOENT); |
| 38 | } | 41 | } |
test/standalone/posix/relpaths.zig+23-15| ... | @@ -48,20 +48,29 @@ fn test_symlink(a: std.mem.Allocator, tmp: std.testing.TmpDir) !void { | ... | @@ -48,20 +48,29 @@ fn test_symlink(a: std.mem.Allocator, tmp: std.testing.TmpDir) !void { |
| 48 | try std.testing.expectEqualStrings(target_name, given); | 48 | try std.testing.expectEqualStrings(target_name, given); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | fn getLinkInfo(fd: std.posix.fd_t) !struct { std.posix.ino_t, std.posix.nlink_t } { | ||
| 52 | if (builtin.target.os.tag == .linux) { | ||
| 53 | const stx = try std.os.linux.wrapped.statx( | ||
| 54 | fd, | ||
| 55 | "", | ||
| 56 | std.posix.AT.EMPTY_PATH, | ||
| 57 | .{ .INO = true, .NLINK = true }, | ||
| 58 | ); | ||
| 59 | std.debug.assert(stx.mask.INO); | ||
| 60 | std.debug.assert(stx.mask.NLINK); | ||
| 61 | return .{ stx.ino, stx.nlink }; | ||
| 62 | } | ||
| 63 | |||
| 64 | const st = try std.posix.fstat(fd); | ||
| 65 | return .{ st.ino, st.nlink }; | ||
| 66 | } | ||
| 67 | |||
| 51 | fn test_link(tmp: std.testing.TmpDir) !void { | 68 | fn test_link(tmp: std.testing.TmpDir) !void { |
| 52 | switch (builtin.target.os.tag) { | 69 | switch (builtin.target.os.tag) { |
| 53 | .linux, .illumos => {}, | 70 | .linux, .illumos => {}, |
| 54 | else => return, | 71 | else => return, |
| 55 | } | 72 | } |
| 56 | 73 | ||
| 57 | if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.target.os.tag == .linux and !builtin.link_libc) { | ||
| 58 | return; // No `fstat()`. | ||
| 59 | } | ||
| 60 | |||
| 61 | if (builtin.cpu.arch.isMIPS64()) { | ||
| 62 | return; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons. | ||
| 63 | } | ||
| 64 | |||
| 65 | const target_name = "link-target"; | 74 | const target_name = "link-target"; |
| 66 | const link_name = "newlink"; | 75 | const link_name = "newlink"; |
| 67 | 76 | ||
| ... | @@ -78,17 +87,16 @@ fn test_link(tmp: std.testing.TmpDir) !void { | ... | @@ -78,17 +87,16 @@ fn test_link(tmp: std.testing.TmpDir) !void { |
| 78 | defer nfd.close(); | 87 | defer nfd.close(); |
| 79 | 88 | ||
| 80 | { | 89 | { |
| 81 | const estat = try std.posix.fstat(efd.handle); | 90 | const eino, _ = try getLinkInfo(efd.handle); |
| 82 | const nstat = try std.posix.fstat(nfd.handle); | 91 | const nino, const nlink = try getLinkInfo(nfd.handle); |
| 83 | try std.testing.expectEqual(estat.ino, nstat.ino); | 92 | try std.testing.expectEqual(eino, nino); |
| 84 | try std.testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); | 93 | try std.testing.expectEqual(@as(std.posix.nlink_t, 2), nlink); |
| 85 | } | 94 | } |
| 86 | 95 | ||
| 87 | // Test 2: Remove the link and see the stats update | 96 | // Test 2: Remove the link and see the stats update |
| 88 | try std.posix.unlink(link_name); | 97 | try std.posix.unlink(link_name); |
| 89 | |||
| 90 | { | 98 | { |
| 91 | const estat = try std.posix.fstat(efd.handle); | 99 | _, const elink = try getLinkInfo(efd.handle); |
| 92 | try std.testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); | 100 | try std.testing.expectEqual(@as(std.posix.nlink_t, 1), elink); |
| 93 | } | 101 | } |
| 94 | } | 102 | } |