| 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 | 1533 | dir.handle, |
| 1534 | 1534 | sub_path_posix, |
| 1535 | 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 | 1537 | &statx, |
| 1538 | 1538 | ); |
| 1539 | 1539 | switch (linux.errno(rc)) { |
| 1540 | 1540 | .SUCCESS => { |
| 1541 | 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 | 1549 | return statFromLinux(&statx); |
| 1543 | 1550 | }, |
| 1544 | 1551 | .INTR => { |
| ... | ... | @@ -1725,12 +1732,19 @@ fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File |
| 1725 | 1732 | file.handle, |
| 1726 | 1733 | "", |
| 1727 | 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 | 1736 | &statx, |
| 1730 | 1737 | ); |
| 1731 | 1738 | switch (linux.errno(rc)) { |
| 1732 | 1739 | .SUCCESS => { |
| 1733 | 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 | 1748 | return statFromLinux(&statx); |
| 1735 | 1749 | }, |
| 1736 | 1750 | .INTR => { |
lib/std/c.zig+5-163| ... | ... | @@ -7586,166 +7586,6 @@ pub const EAI = if (builtin.abi.isAndroid()) enum(c_int) { |
| 7586 | 7586 | pub const dl_iterate_phdr_callback = *const fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.c) c_int; |
| 7587 | 7587 | |
| 7588 | 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 | 7589 | .emscripten => emscripten.Stat, |
| 7750 | 7590 | .wasi => extern struct { |
| 7751 | 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 | 10262 | else => private.fstat, |
| 10423 | 10263 | }, |
| 10424 | 10264 | .netbsd => private.__fstat50, |
| 10265 | .linux => {}, | |
| 10425 | 10266 | else => private.fstat, |
| 10426 | 10267 | }; |
| 10427 | 10268 | |
| ... | ... | @@ -10430,8 +10271,12 @@ pub const fstatat = switch (native_os) { |
| 10430 | 10271 | .x86_64 => private.@"fstatat$INODE64", |
| 10431 | 10272 | else => private.fstatat, |
| 10432 | 10273 | }, |
| 10274 | .linux => {}, | |
| 10433 | 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 | 10280 | pub extern "c" fn getpwent() ?*passwd; |
| 10436 | 10281 | pub extern "c" fn endpwent() void; |
| 10437 | 10282 | pub extern "c" fn setpwent() void; |
| ... | ... | @@ -10505,8 +10350,6 @@ pub extern "c" fn inotify_init1(flags: c_uint) c_int; |
| 10505 | 10350 | pub extern "c" fn inotify_add_watch(fd: fd_t, pathname: [*:0]const u8, mask: u32) c_int; |
| 10506 | 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 | 10353 | pub extern "c" fn fallocate64(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int; |
| 10511 | 10354 | pub extern "c" fn fopen64(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE; |
| 10512 | 10355 | pub extern "c" fn ftruncate64(fd: c_int, length: off_t) c_int; |
| ... | ... | @@ -11552,7 +11395,6 @@ const private = struct { |
| 11552 | 11395 | extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; |
| 11553 | 11396 | extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; |
| 11554 | 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 | 11398 | extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; |
| 11557 | 11399 | extern "c" fn sysconf(sc: c_int) c_long; |
| 11558 | 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 | 95 | pub const ARCH = arch_bits.ARCH; |
| 96 | 96 | pub const HWCAP = arch_bits.HWCAP; |
| 97 | 97 | pub const SC = arch_bits.SC; |
| 98 | pub const Stat = arch_bits.Stat; | |
| 99 | 98 | pub const VDSO = arch_bits.VDSO; |
| 100 | pub const blkcnt_t = arch_bits.blkcnt_t; | |
| 101 | pub const blksize_t = arch_bits.blksize_t; | |
| 102 | pub const dev_t = arch_bits.dev_t; | |
| 103 | pub const ino_t = arch_bits.ino_t; | |
| 104 | pub const mode_t = arch_bits.mode_t; | |
| 105 | pub const nlink_t = arch_bits.nlink_t; | |
| 106 | pub const off_t = arch_bits.off_t; | |
| 99 | pub const blkcnt_t = u64; | |
| 100 | pub const blksize_t = u32; | |
| 101 | pub const dev_t = u64; | |
| 102 | pub const ino_t = u64; | |
| 103 | pub const mode_t = u32; | |
| 104 | pub const nlink_t = u32; | |
| 105 | pub const off_t = i64; | |
| 107 | 106 | pub const time_t = arch_bits.time_t; |
| 108 | 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 | 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 { | |
| 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 { | |
| 2201 | pub fn statx(dirfd: i32, path: [*:0]const u8, flags: u32, mask: STATX, statx_buf: *Statx) usize { | |
| 2251 | 2202 | return syscall5( |
| 2252 | 2203 | .statx, |
| 2253 | 2204 | @as(usize, @bitCast(@as(isize, dirfd))), |
| 2254 | 2205 | @intFromPtr(path), |
| 2255 | 2206 | flags, |
| 2256 | mask, | |
| 2207 | @as(u32, @bitCast(mask)), | |
| 2257 | 2208 | @intFromPtr(statx_buf), |
| 2258 | 2209 | ); |
| 2259 | 2210 | } |
| ... | ... | @@ -6940,27 +6891,85 @@ pub const utsname = extern struct { |
| 6940 | 6891 | }; |
| 6941 | 6892 | pub const HOST_NAME_MAX = 64; |
| 6942 | 6893 | |
| 6943 | pub const STATX_TYPE = 0x0001; | |
| 6944 | pub const STATX_MODE = 0x0002; | |
| 6945 | pub const STATX_NLINK = 0x0004; | |
| 6946 | pub const STATX_UID = 0x0008; | |
| 6947 | pub const STATX_GID = 0x0010; | |
| 6948 | pub const STATX_ATIME = 0x0020; | |
| 6949 | pub const STATX_MTIME = 0x0040; | |
| 6950 | pub const STATX_CTIME = 0x0080; | |
| 6951 | pub const STATX_INO = 0x0100; | |
| 6952 | pub const STATX_SIZE = 0x0200; | |
| 6953 | pub const STATX_BLOCKS = 0x0400; | |
| 6954 | pub const STATX_BASIC_STATS = 0x07ff; | |
| 6955 | ||
| 6956 | pub const STATX_BTIME = 0x0800; | |
| 6957 | ||
| 6958 | pub const STATX_ATTR_COMPRESSED = 0x0004; | |
| 6959 | pub const STATX_ATTR_IMMUTABLE = 0x0010; | |
| 6960 | pub const STATX_ATTR_APPEND = 0x0020; | |
| 6961 | pub const STATX_ATTR_NODUMP = 0x0040; | |
| 6962 | pub const STATX_ATTR_ENCRYPTED = 0x0800; | |
| 6963 | pub const STATX_ATTR_AUTOMOUNT = 0x1000; | |
| 6894 | /// Flags used to request specific members in `Statx` be filled out. | |
| 6895 | /// The `Statx.mask` member will be updated with what information the kernel | |
| 6896 | /// returned. Callers must check this field since support varies by kernel | |
| 6897 | /// version and filesystem. | |
| 6898 | pub const STATX = packed struct(u32) { | |
| 6899 | /// Want `mode & S.IFMT`. | |
| 6900 | TYPE: bool = false, | |
| 6901 | /// Want `mode & ~S.IFMT`. | |
| 6902 | MODE: bool = false, | |
| 6903 | /// Want the `nlink` member. | |
| 6904 | NLINK: bool = false, | |
| 6905 | /// Want the `uid` member. | |
| 6906 | UID: bool = false, | |
| 6907 | /// Want the `gid` member. | |
| 6908 | GID: bool = false, | |
| 6909 | /// Want the `atime` member. | |
| 6910 | ATIME: bool = false, | |
| 6911 | /// Want the `mtime` member. | |
| 6912 | MTIME: bool = false, | |
| 6913 | /// Want the `ctime` member. | |
| 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 | 6974 | pub const statx_timestamp = extern struct { |
| 6966 | 6975 | /// Number of seconds before or after `1970-01-01T00:00:00Z`. |
| ... | ... | @@ -6974,11 +6983,11 @@ pub const statx_timestamp = extern struct { |
| 6974 | 6983 | /// Renamed to `Statx` to not conflict with the `statx` function. |
| 6975 | 6984 | pub const Statx = extern struct { |
| 6976 | 6985 | /// Mask of bits indicating filled fields. |
| 6977 | mask: u32, | |
| 6986 | mask: STATX, | |
| 6978 | 6987 | /// Block size for filesystem I/O. |
| 6979 | 6988 | blksize: u32, |
| 6980 | 6989 | /// Extra file attribute indicators. |
| 6981 | attributes: u64, | |
| 6990 | attributes: STATX_ATTR, | |
| 6982 | 6991 | /// Number of hard links. |
| 6983 | 6992 | nlink: u32, |
| 6984 | 6993 | /// User ID of owner. |
| ... | ... | @@ -6995,7 +7004,7 @@ pub const Statx = extern struct { |
| 6995 | 7004 | /// Number of 512B blocks allocated. |
| 6996 | 7005 | blocks: u64, |
| 6997 | 7006 | /// Mask to show what's supported in `attributes`. |
| 6998 | attributes_mask: u64, | |
| 7007 | attributes_mask: STATX_ATTR, | |
| 6999 | 7008 | /// Last access file timestamp. |
| 7000 | 7009 | atime: statx_timestamp, |
| 7001 | 7010 | /// Creation file timestamp. |
| ... | ... | @@ -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 | 10029 | const unexpectedErrno = std.posix.unexpectedErrno; |
| 9981 | 10030 | |
| 9982 | 10031 | fn invalidApiUsage() error{Unexpected} { |
lib/std/os/linux/IoUring.zig+3-3| ... | ... | @@ -958,7 +958,7 @@ pub fn statx( |
| 958 | 958 | fd: linux.fd_t, |
| 959 | 959 | path: [:0]const u8, |
| 960 | 960 | flags: u32, |
| 961 | mask: u32, | |
| 961 | mask: linux.STATX, | |
| 962 | 962 | buf: *linux.Statx, |
| 963 | 963 | ) !*linux.io_uring_sqe { |
| 964 | 964 | const sqe = try self.get_sqe(); |
| ... | ... | @@ -2691,7 +2691,7 @@ test "statx" { |
| 2691 | 2691 | tmp.dir.fd, |
| 2692 | 2692 | path, |
| 2693 | 2693 | 0, |
| 2694 | linux.STATX_SIZE, | |
| 2694 | .{ .SIZE = true }, | |
| 2695 | 2695 | &buf, |
| 2696 | 2696 | ); |
| 2697 | 2697 | try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode); |
| ... | ... | @@ -2718,7 +2718,7 @@ test "statx" { |
| 2718 | 2718 | .flags = 0, |
| 2719 | 2719 | }, cqe); |
| 2720 | 2720 | |
| 2721 | try testing.expect(buf.mask & linux.STATX_SIZE == linux.STATX_SIZE); | |
| 2721 | try testing.expect(buf.mask.SIZE); | |
| 2722 | 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 | 151 | pub const ino_t = u64; |
| 152 | 152 | pub const dev_t = u64; |
| 153 | 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 | 187 | pub const ino_t = u64; |
| 188 | 188 | pub const dev_t = u64; |
| 189 | 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 | 128 | pub const dev_t = u64; |
| 129 | 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 | 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 | 420 | fd: linux.fd_t, |
| 421 | 421 | path: [*:0]const u8, |
| 422 | 422 | flags: u32, |
| 423 | mask: u32, | |
| 423 | mask: linux.STATX, | |
| 424 | 424 | buf: *linux.Statx, |
| 425 | 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 | 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 | 125 | ); |
| 126 | 126 | } |
| 127 | 127 | |
| 128 | pub const blksize_t = i32; | |
| 129 | 128 | pub const nlink_t = u32; |
| 130 | 129 | pub const time_t = i64; |
| 131 | pub const mode_t = u32; | |
| 132 | 130 | pub const off_t = i64; |
| 133 | 131 | pub const ino_t = u64; |
| 134 | 132 | pub const dev_t = u32; |
| 135 | 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 | 135 | pub const VDSO = struct { |
| 170 | 136 | pub const CGT_SYM = "__vdso_clock_gettime"; |
| 171 | 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 | 151 | pub const dev_t = u64; |
| 152 | 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 | 154 | // No VDSO used as of glibc 112a0ae18b831bf31f44d81b82666980312511d6. |
| 186 | 155 | pub const VDSO = void; |
lib/std/os/linux/mips.zig-32| ... | ... | @@ -238,35 +238,3 @@ pub const off_t = i64; |
| 238 | 238 | pub const ino_t = u64; |
| 239 | 239 | pub const dev_t = u64; |
| 240 | 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 | 192 | pub const ino_t = u64; |
| 193 | 193 | pub const dev_t = u64; |
| 194 | 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 | 192 | pub const ino_t = u64; |
| 193 | 193 | pub const dev_t = u64; |
| 194 | 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 | 139 | pub const ino_t = u64; |
| 140 | 140 | pub const dev_t = u64; |
| 141 | 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 | 276 | pub const off_t = i64; |
| 277 | 277 | pub const ino_t = u64; |
| 278 | 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 | 262 | pub const ino_t = u64; |
| 263 | 263 | pub const dev_t = u64; |
| 264 | 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 | 131 | pub const off_t = i64; |
| 132 | 132 | pub const ino_t = u64; |
| 133 | 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 | 135 | pub const VDSO = struct { |
| 169 | 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 | 131 | pub const off_t = i64; |
| 132 | 132 | pub const ino_t = u64; |
| 133 | 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 | 135 | pub const VDSO = struct { |
| 169 | 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 | 159 | pub const off_t = i64; |
| 160 | 160 | pub const ino_t = u64; |
| 161 | 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 | 163 | pub const VDSO = struct { |
| 195 | 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 | 236 | pub const nlink_t = u32; |
| 237 | 237 | pub const blksize_t = i64; |
| 238 | 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 | 10 | const fs = std.fs; |
| 11 | 11 | |
| 12 | 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 | 13 | var tmp = std.testing.tmpDir(.{}); |
| 16 | 14 | defer tmp.cleanup(); |
| 17 | 15 | |
| ... | ... | @@ -84,26 +82,22 @@ test "statx" { |
| 84 | 82 | var file = try tmp.dir.createFile(tmp_file_name, .{}); |
| 85 | 83 | defer file.close(); |
| 86 | 84 | |
| 87 | var statx_buf: linux.Statx = undefined; | |
| 88 | switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_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))) { | |
| 85 | var buf: linux.Statx = undefined; | |
| 86 | switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .BASIC_STATS, &buf))) { | |
| 97 | 87 | .SUCCESS => {}, |
| 98 | 88 | else => unreachable, |
| 99 | 89 | } |
| 100 | 90 | |
| 101 | try expect(stat_buf.mode == statx_buf.mode); | |
| 102 | try expect(@as(u32, @bitCast(stat_buf.uid)) == statx_buf.uid); | |
| 103 | try expect(@as(u32, @bitCast(stat_buf.gid)) == statx_buf.gid); | |
| 104 | try expect(@as(u64, @bitCast(@as(i64, stat_buf.size))) == statx_buf.size); | |
| 105 | try expect(@as(u64, @bitCast(@as(i64, stat_buf.blksize))) == statx_buf.blksize); | |
| 106 | try expect(@as(u64, @bitCast(@as(i64, stat_buf.blocks))) == statx_buf.blocks); | |
| 91 | const uid = linux.getuid(); | |
| 92 | const gid = linux.getgid(); | |
| 93 | if (buf.mask.MODE) | |
| 94 | try expectEqual(@as(linux.mode_t, linux.S.IFREG), buf.mode & linux.S.IFMT); | |
| 95 | if (buf.mask.UID) | |
| 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 | 103 | test "user and group ids" { |
lib/std/os/linux/x32.zig-33| ... | ... | @@ -155,36 +155,3 @@ pub const ARCH = struct { |
| 155 | 155 | pub const GET_FS = 0x1003; |
| 156 | 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 | 204 | pub const dev_t = u64; |
| 205 | 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 | 207 | pub const user_desc = extern struct { |
| 240 | 208 | entry_number: u32, |
| 241 | 209 | base_addr: u32, |
lib/std/os/linux/x86_64.zig-33| ... | ... | @@ -155,36 +155,3 @@ pub const ARCH = struct { |
| 155 | 155 | pub const GET_FS = 0x1003; |
| 156 | 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 | 119 | pub const STDOUT_FILENO = system.STDOUT_FILENO; |
| 120 | 120 | pub const SYS = system.SYS; |
| 121 | 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 | 134 | pub const T = system.T; |
| 124 | 135 | pub const TCP = system.TCP; |
| 125 | 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 | 491 | } |
| 481 | 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 | 503 | error.NameTooLong => unreachable, |
| 485 | 504 | error.FileNotFound => unreachable, |
| 486 | error.Streaming => unreachable, | |
| 487 | error.BadPathName => return error.Unexpected, | |
| 488 | error.Canceled => return error.Canceled, | |
| 489 | 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 | 509 | return error.OperationNotSupported; |
| 493 | 510 | |
| 494 | 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 | 3860 | if (native_os == .wasi and !builtin.link_libc) { |
| 3844 | 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 | 3864 | var stat = mem.zeroes(Stat); |
| 3852 | switch (errno(fstat_sym(fd, &stat))) { | |
| 3865 | switch (errno(system.fstat(fd, &stat))) { | |
| 3853 | 3866 | .SUCCESS => return stat, |
| 3854 | 3867 | .INVAL => unreachable, |
| 3855 | 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 | 3902 | @compileError("use std.Io instead"); |
| 3890 | 3903 | } |
| 3891 | 3904 | |
| 3892 | const fstatat_sym = if (lfs64_abi) system.fstatat64 else system.fstatat; | |
| 3893 | 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 | 3907 | .SUCCESS => return stat, |
| 3896 | 3908 | .INVAL => unreachable, |
| 3897 | 3909 | .BADF => unreachable, // Always a race condition. |
lib/std/posix/test.zig+78-21| ... | ... | @@ -16,6 +16,7 @@ const AtomicRmwOp = std.builtin.AtomicRmwOp; |
| 16 | 16 | const AtomicOrder = std.builtin.AtomicOrder; |
| 17 | 17 | const native_os = builtin.target.os.tag; |
| 18 | 18 | const tmpDir = std.testing.tmpDir; |
| 19 | const AT = posix.AT; | |
| 19 | 20 | |
| 20 | 21 | // NOTE: several additional tests are in test/standalone/posix/. Any tests that mutate |
| 21 | 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 | 124 | try expect(mem.eql(u8, target_path, given)); |
| 124 | 125 | } |
| 125 | 126 | |
| 126 | test "linkat with different directories" { | |
| 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 (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons. | |
| 127 | fn getLinkInfo(fd: posix.fd_t) !struct { posix.ino_t, posix.nlink_t } { | |
| 128 | if (native_os == .linux) { | |
| 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 | 145 | switch (native_os) { |
| 131 | 146 | .wasi, .linux, .illumos => {}, |
| 132 | 147 | else => return error.SkipZigTest, |
| ... | ... | @@ -153,19 +168,49 @@ test "linkat with different directories" { |
| 153 | 168 | defer nfd.close(); |
| 154 | 169 | |
| 155 | 170 | { |
| 156 | const estat = try posix.fstat(efd.handle); | |
| 157 | const nstat = try posix.fstat(nfd.handle); | |
| 158 | try testing.expectEqual(estat.ino, nstat.ino); | |
| 159 | try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); | |
| 171 | const eino, _ = try getLinkInfo(efd.handle); | |
| 172 | const nino, const nlink = try getLinkInfo(nfd.handle); | |
| 173 | try testing.expectEqual(eino, nino); | |
| 174 | try testing.expectEqual(@as(posix.nlink_t, 2), nlink); | |
| 160 | 175 | } |
| 161 | 176 | |
| 162 | 177 | // Test 2: remove link |
| 163 | 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 | { | |
| 166 | const estat = try posix.fstat(efd.handle); | |
| 167 | try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); | |
| 168 | } | |
| 183 | test "fstatat" { | |
| 184 | if (posix.Stat == void) return error.SkipZigTest; | |
| 185 | if (native_os == .wasi and !builtin.link_libc) return error.SkipZigTest; | |
| 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 | 216 | test "readlinkat" { |
| ... | ... | @@ -906,14 +951,31 @@ test "pwrite with empty buffer" { |
| 906 | 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 | 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); | |
| 911 | try expectEqual(mode, st.mode & 0b111_111_111); | |
| 974 | const actual = try getFileMode(dir, file); | |
| 975 | try expectEqual(mode, actual & 0b111_111_111); | |
| 912 | 976 | } |
| 913 | 977 | |
| 914 | 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 | 979 | if (!std.fs.has_executable_bit) return error.SkipZigTest; |
| 918 | 980 | |
| 919 | 981 | var tmp = tmpDir(.{}); |
| ... | ... | @@ -928,13 +990,8 @@ test "fchmodat smoke test" { |
| 928 | 990 | ); |
| 929 | 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 | 993 | try posix.symlinkat("regfile", tmp.dir.fd, "symlink"); |
| 934 | const sym_mode = blk: { | |
| 935 | const st = try posix.fstatat(tmp.dir.fd, "symlink", posix.AT.SYMLINK_NOFOLLOW); | |
| 936 | break :blk st.mode & 0b111_111_111; | |
| 937 | }; | |
| 994 | const sym_mode = try getFileMode(tmp.dir.fd, "symlink"); | |
| 938 | 995 | |
| 939 | 996 | try posix.fchmodat(tmp.dir.fd, "regfile", 0o640, 0); |
| 940 | 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 | 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 | 71 | const stat = try std.posix.fstat(mf.file.handle); |
| 58 | 72 | if (!std.posix.S.ISREG(stat.mode)) return error.PathAlreadyExists; |
| 59 | 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 | 23 | // Version of glibc this test is being built to run against |
| 24 | 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 | 29 | // PR #17034 - fstat moved between libc_nonshared and libc |
| 27 | 30 | fn checkStat() !void { |
| 28 | 31 | const cwdFd = std.fs.cwd().fd; |
| 29 | 32 | |
| 30 | var stat = std.mem.zeroes(std.c.Stat); | |
| 31 | var result = std.c.fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &stat, 0); | |
| 33 | var buf: [256]u8 = @splat(0); | |
| 34 | var result = fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &buf, 0); | |
| 32 | 35 | assert(result == -1); |
| 33 | 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 | 39 | assert(result == -1); |
| 37 | 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 | 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 | 68 | fn test_link(tmp: std.testing.TmpDir) !void { |
| 52 | 69 | switch (builtin.target.os.tag) { |
| 53 | 70 | .linux, .illumos => {}, |
| 54 | 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 | 74 | const target_name = "link-target"; |
| 66 | 75 | const link_name = "newlink"; |
| 67 | 76 | |
| ... | ... | @@ -78,17 +87,16 @@ fn test_link(tmp: std.testing.TmpDir) !void { |
| 78 | 87 | defer nfd.close(); |
| 79 | 88 | |
| 80 | 89 | { |
| 81 | const estat = try std.posix.fstat(efd.handle); | |
| 82 | const nstat = try std.posix.fstat(nfd.handle); | |
| 83 | try std.testing.expectEqual(estat.ino, nstat.ino); | |
| 84 | try std.testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); | |
| 90 | const eino, _ = try getLinkInfo(efd.handle); | |
| 91 | const nino, const nlink = try getLinkInfo(nfd.handle); | |
| 92 | try std.testing.expectEqual(eino, nino); | |
| 93 | try std.testing.expectEqual(@as(std.posix.nlink_t, 2), nlink); | |
| 85 | 94 | } |
| 86 | 95 | |
| 87 | 96 | // Test 2: Remove the link and see the stats update |
| 88 | 97 | try std.posix.unlink(link_name); |
| 89 | ||
| 90 | 98 | { |
| 91 | const estat = try std.posix.fstat(efd.handle); | |
| 92 | try std.testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); | |
| 99 | _, const elink = try getLinkInfo(efd.handle); | |
| 100 | try std.testing.expectEqual(@as(std.posix.nlink_t, 1), elink); | |
| 93 | 101 | } |
| 94 | 102 | } |