authorgravatar for dev@sgregoratto.meStephen Gregoratto <dev@sgregoratto.me> 2025-10-19 22:48:54+11:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-12-14 01:41:47+01:00
log6216922a9d63982f11ff8ee551a6688bc873e51c
tree14a9946c3269a9b0c0b754f8a8a4eb859ca667f2
parentff3fd950a74ab8ea8907a62dbf880eae08232417

Linux: Nuke `Stat` bits in favour of statx

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) {
7586pub const dl_iterate_phdr_callback = *const fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.c) c_int;7586pub const dl_iterate_phdr_callback = *const fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.c) c_int;
75877587
7588pub const Stat = switch (native_os) {7588pub 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.h7591 // 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};
1042710268
...@@ -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
10278pub extern "c" fn statx(dirfd: fd_t, path: [*:0]const u8, flags: u32, mask: linux.STATX, buf: *linux.Statx) c_int;
10279
10435pub extern "c" fn getpwent() ?*passwd;10280pub extern "c" fn getpwent() ?*passwd;
10436pub extern "c" fn endpwent() void;10281pub extern "c" fn endpwent() void;
10437pub extern "c" fn setpwent() void;10282pub 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;
10505pub extern "c" fn inotify_add_watch(fd: fd_t, pathname: [*:0]const u8, mask: u32) c_int;10350pub extern "c" fn inotify_add_watch(fd: fd_t, pathname: [*:0]const u8, mask: u32) c_int;
10506pub extern "c" fn inotify_rm_watch(fd: fd_t, wd: c_int) c_int;10351pub extern "c" fn inotify_rm_watch(fd: fd_t, wd: c_int) c_int;
1050710352
10508pub extern "c" fn fstat64(fd: fd_t, buf: *Stat) c_int;
10509pub extern "c" fn fstatat64(dirfd: fd_t, noalias path: [*:0]const u8, noalias stat_buf: *Stat, flags: u32) c_int;
10510pub extern "c" fn fallocate64(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int;10353pub extern "c" fn fallocate64(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int;
10511pub extern "c" fn fopen64(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE;10354pub extern "c" fn fopen64(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE;
10512pub extern "c" fn ftruncate64(fd: c_int, length: off_t) c_int;10355pub 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(
95pub const ARCH = arch_bits.ARCH;95pub const ARCH = arch_bits.ARCH;
96pub const HWCAP = arch_bits.HWCAP;96pub const HWCAP = arch_bits.HWCAP;
97pub const SC = arch_bits.SC;97pub const SC = arch_bits.SC;
98pub const Stat = arch_bits.Stat;
99pub const VDSO = arch_bits.VDSO;98pub const VDSO = arch_bits.VDSO;
100pub const blkcnt_t = arch_bits.blkcnt_t;99pub const blkcnt_t = u64;
101pub const blksize_t = arch_bits.blksize_t;100pub const blksize_t = u32;
102pub const dev_t = arch_bits.dev_t;101pub const dev_t = u64;
103pub const ino_t = arch_bits.ino_t;102pub const ino_t = u64;
104pub const mode_t = arch_bits.mode_t;103pub const mode_t = u32;
105pub const nlink_t = arch_bits.nlink_t;104pub const nlink_t = u32;
106pub const off_t = arch_bits.off_t;105pub const off_t = i64;
107pub const time_t = arch_bits.time_t;106pub const time_t = arch_bits.time_t;
108pub const user_desc = arch_bits.user_desc;107pub const user_desc = arch_bits.user_desc;
109108
...@@ -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}
22012200
2202pub fn fstat(fd: i32, stat_buf: *Stat) usize {2201pub 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
2214pub 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
2226pub 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
2238pub 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
2250pub 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};
6941pub const HOST_NAME_MAX = 64;6892pub const HOST_NAME_MAX = 64;
69426893
6943pub const STATX_TYPE = 0x0001;6894/// Flags used to request specific members in `Statx` be filled out.
6944pub const STATX_MODE = 0x0002;6895/// The `Statx.mask` member will be updated with what information the kernel
6945pub const STATX_NLINK = 0x0004;6896/// returned. Callers must check this field since support varies by kernel
6946pub const STATX_UID = 0x0008;6897/// version and filesystem.
6947pub const STATX_GID = 0x0010;6898pub const STATX = packed struct(u32) {
6948pub const STATX_ATIME = 0x0020;6899 /// Want `mode & S.IFMT`.
6949pub const STATX_MTIME = 0x0040;6900 TYPE: bool = false,
6950pub const STATX_CTIME = 0x0080;6901 /// Want `mode & ~S.IFMT`.
6951pub const STATX_INO = 0x0100;6902 MODE: bool = false,
6952pub const STATX_SIZE = 0x0200;6903 /// Want the `nlink` member.
6953pub const STATX_BLOCKS = 0x0400;6904 NLINK: bool = false,
6954pub const STATX_BASIC_STATS = 0x07ff;6905 /// Want the `uid` member.
69556906 UID: bool = false,
6956pub const STATX_BTIME = 0x0800;6907 /// Want the `gid` member.
69576908 GID: bool = false,
6958pub const STATX_ATTR_COMPRESSED = 0x0004;6909 /// Want the `atime` member.
6959pub const STATX_ATTR_IMMUTABLE = 0x0010;6910 ATIME: bool = false,
6960pub const STATX_ATTR_APPEND = 0x0020;6911 /// Want the `mtime` member.
6961pub const STATX_ATTR_NODUMP = 0x0040;6912 MTIME: bool = false,
6962pub const STATX_ATTR_ENCRYPTED = 0x0800;6913 /// Want the `ctime` member.
6963pub 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.
6947pub 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};
69646973
6965pub const statx_timestamp = extern struct {6974pub 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.
6975pub const Statx = extern struct {6984pub 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 }
99799988
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;
998110030
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);
27202720
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}
27242724
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;
151pub const ino_t = u64;151pub const ino_t = u64;
152pub const dev_t = u64;152pub const dev_t = u64;
153pub const blkcnt_t = i64;153pub const blkcnt_t = i64;
154
155// The `stat` definition used by the Linux kernel.
156pub 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;
187pub const ino_t = u64;187pub const ino_t = u64;
188pub const dev_t = u64;188pub const dev_t = u64;
189pub const blkcnt_t = i64;189pub const blkcnt_t = i64;
190
191// The `stat` definition used by the Linux kernel.
192pub 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;
128pub const dev_t = u64;128pub const dev_t = u64;
129pub const blkcnt_t = i64;129pub const blkcnt_t = i64;
130130
131// The `stat` definition used by the Linux kernel.
132pub 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
163pub const VDSO = void;131pub 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 }
429429
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}
127127
128pub const blksize_t = i32;
129pub const nlink_t = u32;128pub const nlink_t = u32;
130pub const time_t = i64;129pub const time_t = i64;
131pub const mode_t = u32;
132pub const off_t = i64;130pub const off_t = i64;
133pub const ino_t = u64;131pub const ino_t = u64;
134pub const dev_t = u32;132pub const dev_t = u32;
135pub const blkcnt_t = i64;133pub const blkcnt_t = i64;
136134
137// The `stat` definition used by the Linux kernel.
138pub 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
169pub const VDSO = struct {135pub 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;
151pub const dev_t = u64;151pub const dev_t = u64;
152pub const blkcnt_t = i64;152pub const blkcnt_t = i64;
153153
154pub 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.
186pub const VDSO = void;155pub 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;
238pub const ino_t = u64;238pub const ino_t = u64;
239pub const dev_t = u64;239pub const dev_t = u64;
240pub const blkcnt_t = i64;240pub const blkcnt_t = i64;
241
242// The `stat64` definition used by the Linux kernel.
243pub 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;
192pub const ino_t = u64;192pub const ino_t = u64;
193pub const dev_t = u64;193pub const dev_t = u64;
194pub const blkcnt_t = i64;194pub const blkcnt_t = i64;
195
196// The `stat` definition used by the Linux kernel.
197pub 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;
192pub const ino_t = u64;192pub const ino_t = u64;
193pub const dev_t = u64;193pub const dev_t = u64;
194pub const blkcnt_t = i64;194pub const blkcnt_t = i64;
195
196// The `stat` definition used by the Linux kernel.
197pub 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;
139pub const ino_t = u64;139pub const ino_t = u64;
140pub const dev_t = u64;140pub const dev_t = u64;
141pub const blkcnt_t = i64;141pub const blkcnt_t = i64;
142
143// The `stat64` definition used by the Linux kernel.
144pub 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;
276pub const off_t = i64;276pub const off_t = i64;
277pub const ino_t = u64;277pub const ino_t = u64;
278pub const dev_t = u64;278pub const dev_t = u64;
279pub const blkcnt_t = i64;
280
281// The `stat` definition used by the Linux kernel.
282pub 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;
262pub const ino_t = u64;262pub const ino_t = u64;
263pub const dev_t = u64;263pub const dev_t = u64;
264pub const blkcnt_t = i64;264pub const blkcnt_t = i64;
265
266// The `stat` definition used by the Linux kernel.
267pub 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;
131pub const off_t = i64;131pub const off_t = i64;
132pub const ino_t = u64;132pub const ino_t = u64;
133pub const dev_t = u64;133pub const dev_t = u64;
134pub const blkcnt_t = i64;
135
136// The `stat` definition used by the Linux kernel.
137pub 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};
167134
168pub const VDSO = struct {135pub 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;
131pub const off_t = i64;131pub const off_t = i64;
132pub const ino_t = u64;132pub const ino_t = u64;
133pub const dev_t = u64;133pub const dev_t = u64;
134pub const blkcnt_t = i64;
135
136// The `stat` definition used by the Linux kernel.
137pub 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};
167134
168pub const VDSO = struct {135pub 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;
159pub const off_t = i64;159pub const off_t = i64;
160pub const ino_t = u64;160pub const ino_t = u64;
161pub const dev_t = u64;161pub const dev_t = u64;
162pub const blkcnt_t = i64;
163
164// The `stat` definition used by the Linux kernel.
165pub 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};
193162
194pub const VDSO = struct {163pub 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;
236pub const nlink_t = u32;236pub const nlink_t = u32;
237pub const blksize_t = i64;237pub const blksize_t = i64;
238pub const blkcnt_t = i64;238pub const blkcnt_t = i64;
239
240// The `stat64` definition used by the kernel.
241pub 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;
10const fs = std.fs;10const fs = std.fs;
1111
12test "fallocate" {12test "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();
1715
...@@ -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();
8684
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 }
10090
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}
108102
109test "user and group ids" {103test "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.
160pub 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;
204pub const dev_t = u64;204pub const dev_t = u64;
205pub const blkcnt_t = i64;205pub const blkcnt_t = i64;
206206
207// The `stat` definition used by the Linux kernel.
208pub 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
239pub const user_desc = extern struct {207pub 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.
160pub 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;
119pub const STDOUT_FILENO = system.STDOUT_FILENO;119pub const STDOUT_FILENO = system.STDOUT_FILENO;
120pub const SYS = system.SYS;120pub const SYS = system.SYS;
121pub const Sigaction = system.Sigaction;121pub const Sigaction = system.Sigaction;
122pub const Stat = system.Stat;122pub 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};
123pub const T = system.T;134pub const T = system.T;
124pub const TCP = system.TCP;135pub const TCP = system.TCP;
125pub const VDSO = system.VDSO;136pub 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);
482493
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;
493510
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 }
38493863
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 }
38913904
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;
16const AtomicOrder = std.builtin.AtomicOrder;16const AtomicOrder = std.builtin.AtomicOrder;
17const native_os = builtin.target.os.tag;17const native_os = builtin.target.os.tag;
18const tmpDir = std.testing.tmpDir;18const tmpDir = std.testing.tmpDir;
19const AT = posix.AT;
1920
20// NOTE: several additional tests are in test/standalone/posix/. Any tests that mutate21// 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}
125126
126test "linkat with different directories" {127fn 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 }
129139
140 const st = try posix.fstat(fd);
141 return .{ st.ino, st.nlink };
142}
143
144test "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();
154169
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 }
161176
162 // Test 2: remove link177 // 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}
164182
165 {183test "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}
170215
171test "readlinkat" {216test "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}
908953
954fn 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
909fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void {973fn 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}
913977
914test "fchmodat smoke test" {978test "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;
918980
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);
930992
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 };
938995
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 against23// Version of glibc this test is being built to run against
24const glibc_ver = builtin.os.versionRange().gnuLibCVersion().?;24const glibc_ver = builtin.os.versionRange().gnuLibCVersion().?;
2525
26extern "c" fn fstatat(dirfd: i32, path: [*:0]const u8, buf: [*]const u8, flag: u32) c_int;
27extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: [*]const u8) c_int;
28
26// PR #17034 - fstat moved between libc_nonshared and libc29// PR #17034 - fstat moved between libc_nonshared and libc
27fn checkStat() !void {30fn checkStat() !void {
28 const cwdFd = std.fs.cwd().fd;31 const cwdFd = std.fs.cwd().fd;
2932
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);
3437
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}
5050
51fn 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
51fn test_link(tmp: std.testing.TmpDir) !void {68fn 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 }
5673
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";
6776
...@@ -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();
7988
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 }
8695
87 // Test 2: Remove the link and see the stats update96 // 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}