authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-12-14 05:25:29+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-12-14 05:25:29+01:00
log2f2b09757682d4c3624d8e6d470852b1d1d6264d
tree127627d7a0c5d5336df3296fe45755609b86e04e
parentc13857e504f5893cabf182dde1e826131f2acf24
parentaec7bfb09219e32389ad666906166f690a158fbe

Merge pull request 'Linux: Nuke Stat bits in favour of Statx' (#30122) from The-King-of-Toasters/zig:statx into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/30122 Reviewed-by: Alex Rønne Petersen <alex@alexrp.com>

29 files changed, 358 insertions(+), 1083 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+174-118
...@@ -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,96 +6891,161 @@ pub const utsname = extern struct {...@@ -6940,96 +6891,161 @@ 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 {
6975 /// Number of seconds before or after `1970-01-01T00:00:00Z`.
6966 sec: i64,6976 sec: i64,
6977 /// Number of nanoseconds (0..999,999,999) after `sec`.
6967 nsec: u32,6978 nsec: u32,
6979 // Reserved for future increases in resolution.
6968 __pad1: u32,6980 __pad1: u32,
6969};6981};
69706982
6971/// Renamed to `Statx` to not conflict with the `statx` function.6983/// Renamed to `Statx` to not conflict with the `statx` function.
6972pub const Statx = extern struct {6984pub const Statx = extern struct {
6973 /// Mask of bits indicating filled fields6985 /// Mask of bits indicating filled fields.
6974 mask: u32,6986 mask: STATX,
69756987 /// Block size for filesystem I/O.
6976 /// Block size for filesystem I/O
6977 blksize: u32,6988 blksize: u32,
69786989 /// Extra file attribute indicators.
6979 /// Extra file attribute indicators6990 attributes: STATX_ATTR,
6980 attributes: u64,6991 /// Number of hard links.
6981
6982 /// Number of hard links
6983 nlink: u32,6992 nlink: u32,
69846993 /// User ID of owner.
6985 /// User ID of owner
6986 uid: uid_t,6994 uid: uid_t,
69876995 /// Group ID of owner.
6988 /// Group ID of owner
6989 gid: gid_t,6996 gid: gid_t,
69906997 /// File type and mode.
6991 /// File type and mode
6992 mode: u16,6998 mode: u16,
6993 __pad1: u16,6999 __spare0: u16,
69947000 /// Inode number.
6995 /// Inode number
6996 ino: u64,7001 ino: u64,
69977002 /// Total size in bytes.
6998 /// Total size in bytes
6999 size: u64,7003 size: u64,
70007004 /// Number of 512B blocks allocated.
7001 /// Number of 512B blocks allocated
7002 blocks: u64,7005 blocks: u64,
7003
7004 /// Mask to show what's supported in `attributes`.7006 /// Mask to show what's supported in `attributes`.
7005 attributes_mask: u64,7007 attributes_mask: STATX_ATTR,
70067008 /// Last access file timestamp.
7007 /// Last access file timestamp
7008 atime: statx_timestamp,7009 atime: statx_timestamp,
70097010 /// Creation file timestamp.
7010 /// Creation file timestamp
7011 btime: statx_timestamp,7011 btime: statx_timestamp,
70127012 /// Last status change file timestamp.
7013 /// Last status change file timestamp
7014 ctime: statx_timestamp,7013 ctime: statx_timestamp,
70157014 /// Last modification file timestamp.
7016 /// Last modification file timestamp
7017 mtime: statx_timestamp,7015 mtime: statx_timestamp,
7018
7019 /// Major ID, if this file represents a device.7016 /// Major ID, if this file represents a device.
7020 rdev_major: u32,7017 rdev_major: u32,
7021
7022 /// Minor ID, if this file represents a device.7018 /// Minor ID, if this file represents a device.
7023 rdev_minor: u32,7019 rdev_minor: u32,
7024
7025 /// Major ID of the device containing the filesystem where this file resides.7020 /// Major ID of the device containing the filesystem where this file resides.
7026 dev_major: u32,7021 dev_major: u32,
7027
7028 /// Minor ID of the device containing the filesystem where this file resides.7022 /// Minor ID of the device containing the filesystem where this file resides.
7029 dev_minor: u32,7023 dev_minor: u32,
70307024 /// Mount ID
7031 __pad2: [14]u64,7025 mnt_id: u64,
7032};7026 /// Memory buffer alignment for direct I/O.
7027 dio_mem_align: u32,
7028 /// File offset alignment for direct I/O.
7029 dio_offset_align: u32,
7030 /// Subvolume identifier.
7031 subvol: u64,
7032 /// Min atomic write unit in bytes.
7033 atomic_write_unit_min: u32,
7034 /// Max atomic write unit in bytes.
7035 atomic_write_unit_max: u32,
7036 /// Max atomic write segment count.
7037 atomic_write_segments_max: u32,
7038 /// File offset alignment for direct I/O reads.
7039 dio_read_offset_align: u32,
7040 /// Optimised max atomic write unit in bytes.
7041 atomic_write_unit_max_opt: u32,
7042 __spare2: [1]u32,
7043 __spare3: [8]u64,
7044};
7045
7046comptime {
7047 assert(@sizeOf(Statx) == 0x100);
7048}
70337049
7034pub const addrinfo = extern struct {7050pub const addrinfo = extern struct {
7035 flags: AI,7051 flags: AI,
...@@ -9970,6 +9986,46 @@ pub const wrapped = struct {...@@ -9970,6 +9986,46 @@ pub const wrapped = struct {
9970 }9986 }
9971 }9987 }
99729988
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
9973 const unexpectedErrno = std.posix.unexpectedErrno;10029 const unexpectedErrno = std.posix.unexpectedErrno;
997410030
9975 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-39
...@@ -143,43 +143,4 @@ pub const VDSO = struct {...@@ -143,43 +143,4 @@ pub const VDSO = struct {
143 pub const CGT_VER = "LINUX_2.6.39";143 pub const CGT_VER = "LINUX_2.6.39";
144};144};
145145
146pub const blksize_t = i32;
147pub const nlink_t = u32;
148pub const time_t = i64;146pub const time_t = i64;
149pub const mode_t = u32;
150pub const off_t = i64;
151pub const ino_t = u64;
152pub const dev_t = u64;
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-39
...@@ -179,43 +179,4 @@ pub const HWCAP = struct {...@@ -179,43 +179,4 @@ pub const HWCAP = struct {
179 pub const EVTSTRM = 1 << 21;179 pub const EVTSTRM = 1 << 21;
180};180};
181181
182pub const blksize_t = i32;
183pub const nlink_t = u32;
184pub const time_t = i32;182pub const time_t = i32;
185pub const mode_t = u32;
186pub const off_t = i64;
187pub const ino_t = u64;
188pub const dev_t = u64;
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-39
...@@ -119,45 +119,6 @@ pub fn clone() callconv(.naked) u32 {...@@ -119,45 +119,6 @@ pub fn clone() callconv(.naked) u32 {
119 );119 );
120}120}
121121
122pub const blksize_t = i32;
123pub const nlink_t = u32;
124pub const time_t = i64;122pub const time_t = i64;
125pub const mode_t = u32;
126pub const off_t = i64;
127pub const ino_t = u64;
128pub const dev_t = u64;
129pub const blkcnt_t = i64;
130
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};
162123
163pub const VDSO = void;124pub 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-39
...@@ -125,46 +125,7 @@ pub fn clone() callconv(.naked) u64 {...@@ -125,46 +125,7 @@ pub fn clone() callconv(.naked) u64 {
125 );125 );
126}126}
127127
128pub const blksize_t = i32;
129pub const nlink_t = u32;
130pub const time_t = i64;128pub const time_t = i64;
131pub const mode_t = u32;
132pub const off_t = i64;
133pub const ino_t = u64;
134pub const dev_t = u32;
135pub const blkcnt_t = i64;
136
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};
168129
169pub const VDSO = struct {130pub const VDSO = struct {
170 pub const CGT_SYM = "__vdso_clock_gettime";131 pub const CGT_SYM = "__vdso_clock_gettime";
lib/std/os/linux/m68k.zig-38
...@@ -142,45 +142,7 @@ pub fn restore_rt() callconv(.naked) noreturn {...@@ -142,45 +142,7 @@ pub fn restore_rt() callconv(.naked) noreturn {
142 );142 );
143}143}
144144
145pub const blksize_t = i32;
146pub const nlink_t = u32;
147pub const time_t = i32;145pub const time_t = i32;
148pub const mode_t = u32;
149pub const off_t = i64;
150pub const ino_t = u64;
151pub const dev_t = u64;
152pub const blkcnt_t = i64;
153
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};
184146
185// No VDSO used as of glibc 112a0ae18b831bf31f44d81b82666980312511d6.147// No VDSO used as of glibc 112a0ae18b831bf31f44d81b82666980312511d6.
186pub const VDSO = void;148pub const VDSO = void;
lib/std/os/linux/mips.zig-39
...@@ -230,43 +230,4 @@ pub const VDSO = struct {...@@ -230,43 +230,4 @@ pub const VDSO = struct {
230 pub const CGT_VER = "LINUX_2.6";230 pub const CGT_VER = "LINUX_2.6";
231};231};
232232
233pub const blksize_t = u32;
234pub const nlink_t = u32;
235pub const time_t = i32;233pub const time_t = i32;
236pub const mode_t = u32;
237pub const off_t = i64;
238pub const ino_t = u64;
239pub const dev_t = u64;
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-51
...@@ -184,55 +184,4 @@ pub const VDSO = struct {...@@ -184,55 +184,4 @@ pub const VDSO = struct {
184 pub const CGT_VER = "LINUX_2.6";184 pub const CGT_VER = "LINUX_2.6";
185};185};
186186
187pub const blksize_t = u32;
188pub const nlink_t = u32;
189pub const time_t = i32;187pub const time_t = i32;
190pub const mode_t = u32;
191pub const off_t = i64;
192pub const ino_t = u64;
193pub const dev_t = u64;
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-51
...@@ -184,55 +184,4 @@ pub const VDSO = struct {...@@ -184,55 +184,4 @@ pub const VDSO = struct {
184 pub const CGT_VER = "LINUX_2.6";184 pub const CGT_VER = "LINUX_2.6";
185};185};
186186
187pub const blksize_t = u32;
188pub const nlink_t = u32;
189pub const time_t = i32;187pub const time_t = i32;
190pub const mode_t = u32;
191pub const off_t = i64;
192pub const ino_t = u64;
193pub const dev_t = u64;
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-39
...@@ -131,43 +131,4 @@ pub fn clone() callconv(.naked) u32 {...@@ -131,43 +131,4 @@ pub fn clone() callconv(.naked) u32 {
131131
132pub const VDSO = void;132pub const VDSO = void;
133133
134pub const blksize_t = u32;
135pub const nlink_t = u32;
136pub const time_t = i32;134pub const time_t = i32;
137pub const mode_t = u32;
138pub const off_t = i64;
139pub const ino_t = u64;
140pub const dev_t = u64;
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-38
...@@ -269,42 +269,4 @@ pub const VDSO = struct {...@@ -269,42 +269,4 @@ pub const VDSO = struct {
269 pub const CGT_VER = "LINUX_2.6.15";269 pub const CGT_VER = "LINUX_2.6.15";
270};270};
271271
272pub const blksize_t = i32;
273pub const nlink_t = u32;
274pub const time_t = i32;272pub const time_t = i32;
275pub const mode_t = u32;
276pub const off_t = i64;
277pub const ino_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-37
...@@ -254,41 +254,4 @@ pub const VDSO = struct {...@@ -254,41 +254,4 @@ pub const VDSO = struct {
254 pub const CGT_VER = "LINUX_2.6.15";254 pub const CGT_VER = "LINUX_2.6.15";
255};255};
256256
257pub const blksize_t = i64;
258pub const nlink_t = u64;
259pub const time_t = i64;257pub const time_t = i64;
260pub const mode_t = u32;
261pub const off_t = i64;
262pub const ino_t = u64;
263pub const dev_t = u64;
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-39
...@@ -124,46 +124,7 @@ pub fn clone() callconv(.naked) u32 {...@@ -124,46 +124,7 @@ pub fn clone() callconv(.naked) u32 {
124 );124 );
125}125}
126126
127pub const blksize_t = i32;
128pub const nlink_t = u32;
129pub const time_t = i64;127pub const time_t = i64;
130pub const mode_t = u32;
131pub const off_t = i64;
132pub const ino_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};
167128
168pub const VDSO = struct {129pub const VDSO = struct {
169 pub const CGT_SYM = "__vdso_clock_gettime";130 pub const CGT_SYM = "__vdso_clock_gettime";
lib/std/os/linux/riscv64.zig-39
...@@ -124,46 +124,7 @@ pub fn clone() callconv(.naked) u64 {...@@ -124,46 +124,7 @@ pub fn clone() callconv(.naked) u64 {
124 );124 );
125}125}
126126
127pub const blksize_t = i32;
128pub const nlink_t = u32;
129pub const time_t = i64;127pub const time_t = i64;
130pub const mode_t = u32;
131pub const off_t = i64;
132pub const ino_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};
167128
168pub const VDSO = struct {129pub const VDSO = struct {
169 pub const CGT_SYM = "__vdso_clock_gettime";130 pub const CGT_SYM = "__vdso_clock_gettime";
lib/std/os/linux/s390x.zig-37
...@@ -152,44 +152,7 @@ pub fn restore_rt() callconv(.naked) noreturn {...@@ -152,44 +152,7 @@ pub fn restore_rt() callconv(.naked) noreturn {
152 );152 );
153}153}
154154
155pub const blksize_t = i64;
156pub const nlink_t = u64;
157pub const time_t = i64;155pub const time_t = i64;
158pub const mode_t = u32;
159pub const off_t = i64;
160pub const ino_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};
193156
194pub const VDSO = struct {157pub const VDSO = struct {
195 pub const CGT_SYM = "__kernel_clock_gettime";158 pub const CGT_SYM = "__kernel_clock_gettime";
lib/std/os/linux/sparc64.zig-42
...@@ -228,46 +228,4 @@ pub const VDSO = struct {...@@ -228,46 +228,4 @@ pub const VDSO = struct {
228 pub const CGT_VER = "LINUX_2.6";228 pub const CGT_VER = "LINUX_2.6";
229};229};
230230
231pub const off_t = i64;
232pub const ino_t = u64;
233pub const time_t = i64;231pub const time_t = i64;
234pub const mode_t = u32;
235pub const dev_t = u64;
236pub const nlink_t = u32;
237pub const blksize_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-40
...@@ -132,14 +132,7 @@ pub fn restore_rt() callconv(.naked) noreturn {...@@ -132,14 +132,7 @@ pub fn restore_rt() callconv(.naked) noreturn {
132 }132 }
133}133}
134134
135pub const mode_t = u32;
136pub const time_t = i32;135pub const time_t = i32;
137pub const nlink_t = u32;
138pub const blksize_t = i32;
139pub const blkcnt_t = i32;
140pub const off_t = i64;
141pub const ino_t = u64;
142pub const dev_t = u64;
143136
144pub const VDSO = struct {137pub const VDSO = struct {
145 pub const CGT_SYM = "__vdso_clock_gettime";138 pub const CGT_SYM = "__vdso_clock_gettime";
...@@ -155,36 +148,3 @@ pub const ARCH = struct {...@@ -155,36 +148,3 @@ pub const ARCH = struct {
155 pub const GET_FS = 0x1003;148 pub const GET_FS = 0x1003;
156 pub const GET_GS = 0x1004;149 pub const GET_GS = 0x1004;
157};150};
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-39
...@@ -195,46 +195,7 @@ pub const VDSO = struct {...@@ -195,46 +195,7 @@ pub const VDSO = struct {
195 pub const CGT_VER = "LINUX_2.6";195 pub const CGT_VER = "LINUX_2.6";
196};196};
197197
198pub const blksize_t = i32;
199pub const nlink_t = u32;
200pub const time_t = i32;198pub const time_t = i32;
201pub const mode_t = u32;
202pub const off_t = i64;
203pub const ino_t = u64;
204pub const dev_t = u64;
205pub const blkcnt_t = i64;
206
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};
238199
239pub const user_desc = extern struct {200pub const user_desc = extern struct {
240 entry_number: u32,201 entry_number: u32,
lib/std/os/linux/x86_64.zig-40
...@@ -132,14 +132,7 @@ pub fn restore_rt() callconv(.naked) noreturn {...@@ -132,14 +132,7 @@ pub fn restore_rt() callconv(.naked) noreturn {
132 }132 }
133}133}
134134
135pub const mode_t = u64;
136pub const time_t = i64;135pub const time_t = i64;
137pub const nlink_t = u64;
138pub const blksize_t = i64;
139pub const blkcnt_t = i64;
140pub const off_t = i64;
141pub const ino_t = u64;
142pub const dev_t = u64;
143136
144pub const VDSO = struct {137pub const VDSO = struct {
145 pub const CGT_SYM = "__vdso_clock_gettime";138 pub const CGT_SYM = "__vdso_clock_gettime";
...@@ -155,36 +148,3 @@ pub const ARCH = struct {...@@ -155,36 +148,3 @@ pub const ARCH = struct {
155 pub const GET_FS = 0x1003;148 pub const GET_FS = 0x1003;
156 pub const GET_GS = 0x1004;149 pub const GET_GS = 0x1004;
157};150};
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}