diff --git a/lib/std/Io/Threaded.zig b/lib/std/Io/Threaded.zig index 984d6e45296e22775fb3d74aa84c23a04ec4593d..f8e5cdc73d1ea7cea25d99f731bbe69dc568c632 100644 --- a/lib/std/Io/Threaded.zig +++ b/lib/std/Io/Threaded.zig @@ -1533,12 +1533,19 @@ fn dirStatPathLinux( dir.handle, sub_path_posix, flags, - linux.STATX_INO | linux.STATX_SIZE | linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, + .{ .TYPE = true, .MODE = true, .ATIME = true, .MTIME = true, .CTIME = true, .INO = true, .SIZE = true }, &statx, ); switch (linux.errno(rc)) { .SUCCESS => { current_thread.endSyscall(); + assert(statx.mask.TYPE); + assert(statx.mask.MODE); + assert(statx.mask.ATIME); + assert(statx.mask.MTIME); + assert(statx.mask.CTIME); + assert(statx.mask.INO); + assert(statx.mask.SIZE); return statFromLinux(&statx); }, .INTR => { @@ -1725,12 +1732,19 @@ fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File file.handle, "", linux.AT.EMPTY_PATH, - linux.STATX_INO | linux.STATX_SIZE | linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, + .{ .TYPE = true, .MODE = true, .ATIME = true, .MTIME = true, .CTIME = true, .INO = true, .SIZE = true }, &statx, ); switch (linux.errno(rc)) { .SUCCESS => { current_thread.endSyscall(); + assert(statx.mask.TYPE); + assert(statx.mask.MODE); + assert(statx.mask.ATIME); + assert(statx.mask.MTIME); + assert(statx.mask.CTIME); + assert(statx.mask.INO); + assert(statx.mask.SIZE); return statFromLinux(&statx); }, .INTR => { diff --git a/lib/std/c.zig b/lib/std/c.zig index 816074652b5d6f411fc3f5663bc456fbc846e9a6..6a8e142a0ce5bc07f92cc04cf4d5ae5d6a76762d 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -7586,166 +7586,6 @@ pub const EAI = if (builtin.abi.isAndroid()) enum(c_int) { pub const dl_iterate_phdr_callback = *const fn (info: *dl_phdr_info, size: usize, data: ?*anyopaque) callconv(.c) c_int; pub const Stat = switch (native_os) { - .linux => switch (native_arch) { - .sparc64 => extern struct { - dev: u64, - __pad1: u16, - ino: ino_t, - mode: u32, - nlink: u32, - - uid: u32, - gid: u32, - rdev: u64, - __pad2: u16, - - size: off_t, - blksize: isize, - blocks: i64, - - atim: timespec, - mtim: timespec, - ctim: timespec, - __reserved: [2]usize, - - pub fn atime(self: @This()) timespec { - return self.atim; - } - - pub fn mtime(self: @This()) timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) timespec { - return self.ctim; - } - }, - .mips, .mipsel => if (builtin.target.abi.isMusl()) extern struct { - dev: dev_t, - __pad0: [2]i32, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: uid_t, - gid: gid_t, - rdev: dev_t, - __pad1: [2]i32, - size: off_t, - atim: timespec, - mtim: timespec, - ctim: timespec, - blksize: blksize_t, - __pad3: i32, - blocks: blkcnt_t, - __pad4: [14]i32, - - pub fn atime(self: @This()) timespec { - return self.atim; - } - - pub fn mtime(self: @This()) timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) timespec { - return self.ctim; - } - } else extern struct { - dev: u32, - __pad0: [3]u32, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: uid_t, - gid: gid_t, - rdev: u32, - __pad1: [3]u32, - size: off_t, - atim: timespec, - mtim: timespec, - ctim: timespec, - blksize: blksize_t, - __pad3: u32, - blocks: blkcnt_t, - __pad4: [14]u32, - - pub fn atime(self: @This()) timespec { - return self.atim; - } - - pub fn mtime(self: @This()) timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) timespec { - return self.ctim; - } - }, - .mips64, .mips64el => if (builtin.target.abi.isMusl()) extern struct { - dev: dev_t, - __pad0: [3]i32, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: uid_t, - gid: gid_t, - rdev: dev_t, - __pad1: [2]u32, - size: off_t, - __pad2: i32, - atim: timespec, - mtim: timespec, - ctim: timespec, - blksize: blksize_t, - __pad3: u32, - blocks: blkcnt_t, - __pad4: [14]i32, - - pub fn atime(self: @This()) timespec { - return self.atim; - } - - pub fn mtime(self: @This()) timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) timespec { - return self.ctim; - } - } else extern struct { - dev: dev_t, - __pad0: [3]u32, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: uid_t, - gid: gid_t, - rdev: dev_t, - __pad1: [3]u32, - size: off_t, - atim: timespec, - mtim: timespec, - ctim: timespec, - blksize: blksize_t, - __pad3: u32, - blocks: blkcnt_t, - __pad4: [14]i32, - - pub fn atime(self: @This()) timespec { - return self.atim; - } - - pub fn mtime(self: @This()) timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) timespec { - return self.ctim; - } - }, - - else => std.os.linux.Stat, // libc stat is the same as kernel stat. - }, .emscripten => emscripten.Stat, .wasi => extern struct { // 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) { else => private.fstat, }, .netbsd => private.__fstat50, + .linux => {}, else => private.fstat, }; @@ -10430,8 +10271,12 @@ pub const fstatat = switch (native_os) { .x86_64 => private.@"fstatat$INODE64", else => private.fstatat, }, + .linux => {}, else => private.fstatat, }; + +pub extern "c" fn statx(dirfd: fd_t, path: [*:0]const u8, flags: u32, mask: linux.STATX, buf: *linux.Statx) c_int; + pub extern "c" fn getpwent() ?*passwd; pub extern "c" fn endpwent() void; pub extern "c" fn setpwent() void; @@ -10505,8 +10350,6 @@ pub extern "c" fn inotify_init1(flags: c_uint) c_int; pub extern "c" fn inotify_add_watch(fd: fd_t, pathname: [*:0]const u8, mask: u32) c_int; pub extern "c" fn inotify_rm_watch(fd: fd_t, wd: c_int) c_int; -pub extern "c" fn fstat64(fd: fd_t, buf: *Stat) c_int; -pub extern "c" fn fstatat64(dirfd: fd_t, noalias path: [*:0]const u8, noalias stat_buf: *Stat, flags: u32) c_int; pub extern "c" fn fallocate64(fd: fd_t, mode: c_int, offset: off_t, len: off_t) c_int; pub extern "c" fn fopen64(noalias filename: [*:0]const u8, noalias modes: [*:0]const u8) ?*FILE; pub extern "c" fn ftruncate64(fd: c_int, length: off_t) c_int; @@ -11552,7 +11395,6 @@ const private = struct { extern "c" fn sigprocmask(how: c_int, noalias set: ?*const sigset_t, noalias oset: ?*sigset_t) c_int; extern "c" fn socket(domain: c_uint, sock_type: c_uint, protocol: c_uint) c_int; extern "c" fn socketpair(domain: c_uint, sock_type: c_uint, protocol: c_uint, sv: *[2]fd_t) c_int; - extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: *Stat) c_int; extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; extern "c" fn sysconf(sc: c_int) c_long; extern "c" fn shm_open(name: [*:0]const u8, flag: c_int, mode: mode_t) c_int; diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index b80d7c606f0537582f7e7dc88b01693c80cdb16d..dc835fe81e76b13daf1d0bd43de87a6f5f9a8ccd 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -95,15 +95,14 @@ pub fn clone( pub const ARCH = arch_bits.ARCH; pub const HWCAP = arch_bits.HWCAP; pub const SC = arch_bits.SC; -pub const Stat = arch_bits.Stat; pub const VDSO = arch_bits.VDSO; -pub const blkcnt_t = arch_bits.blkcnt_t; -pub const blksize_t = arch_bits.blksize_t; -pub const dev_t = arch_bits.dev_t; -pub const ino_t = arch_bits.ino_t; -pub const mode_t = arch_bits.mode_t; -pub const nlink_t = arch_bits.nlink_t; -pub const off_t = arch_bits.off_t; +pub const blkcnt_t = u64; +pub const blksize_t = u32; +pub const dev_t = u64; +pub const ino_t = u64; +pub const mode_t = u32; +pub const nlink_t = u32; +pub const off_t = i64; pub const time_t = arch_bits.time_t; pub const user_desc = arch_bits.user_desc; @@ -2199,61 +2198,13 @@ pub fn accept4(fd: i32, noalias addr: ?*sockaddr, noalias len: ?*socklen_t, flag return syscall4(.accept4, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(addr), @intFromPtr(len), flags); } -pub fn fstat(fd: i32, stat_buf: *Stat) usize { - if (native_arch == .riscv32 or native_arch.isLoongArch()) { - // riscv32 and loongarch have made the interesting decision to not implement some of - // the older stat syscalls, including this one. - @compileError("No fstat syscall on this architecture."); - } else if (@hasField(SYS, "fstat64")) { - return syscall2(.fstat64, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(stat_buf)); - } else { - return syscall2(.fstat, @as(usize, @bitCast(@as(isize, fd))), @intFromPtr(stat_buf)); - } -} - -pub fn stat(pathname: [*:0]const u8, statbuf: *Stat) usize { - if (native_arch == .riscv32 or native_arch.isLoongArch()) { - // riscv32 and loongarch have made the interesting decision to not implement some of - // the older stat syscalls, including this one. - @compileError("No stat syscall on this architecture."); - } else if (@hasField(SYS, "stat64")) { - return syscall2(.stat64, @intFromPtr(pathname), @intFromPtr(statbuf)); - } else { - return syscall2(.stat, @intFromPtr(pathname), @intFromPtr(statbuf)); - } -} - -pub fn lstat(pathname: [*:0]const u8, statbuf: *Stat) usize { - if (native_arch == .riscv32 or native_arch.isLoongArch()) { - // riscv32 and loongarch have made the interesting decision to not implement some of - // the older stat syscalls, including this one. - @compileError("No lstat syscall on this architecture."); - } else if (@hasField(SYS, "lstat64")) { - return syscall2(.lstat64, @intFromPtr(pathname), @intFromPtr(statbuf)); - } else { - return syscall2(.lstat, @intFromPtr(pathname), @intFromPtr(statbuf)); - } -} - -pub fn fstatat(dirfd: i32, path: [*:0]const u8, stat_buf: *Stat, flags: u32) usize { - if (native_arch == .riscv32 or native_arch.isLoongArch()) { - // riscv32 and loongarch have made the interesting decision to not implement some of - // the older stat syscalls, including this one. - @compileError("No fstatat syscall on this architecture."); - } else if (@hasField(SYS, "fstatat64")) { - return syscall4(.fstatat64, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags); - } else { - return syscall4(.fstatat, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), @intFromPtr(stat_buf), flags); - } -} - -pub fn statx(dirfd: i32, path: [*:0]const u8, flags: u32, mask: u32, statx_buf: *Statx) usize { +pub fn statx(dirfd: i32, path: [*:0]const u8, flags: u32, mask: STATX, statx_buf: *Statx) usize { return syscall5( .statx, @as(usize, @bitCast(@as(isize, dirfd))), @intFromPtr(path), flags, - mask, + @as(u32, @bitCast(mask)), @intFromPtr(statx_buf), ); } @@ -6940,97 +6891,162 @@ pub const utsname = extern struct { }; pub const HOST_NAME_MAX = 64; -pub const STATX_TYPE = 0x0001; -pub const STATX_MODE = 0x0002; -pub const STATX_NLINK = 0x0004; -pub const STATX_UID = 0x0008; -pub const STATX_GID = 0x0010; -pub const STATX_ATIME = 0x0020; -pub const STATX_MTIME = 0x0040; -pub const STATX_CTIME = 0x0080; -pub const STATX_INO = 0x0100; -pub const STATX_SIZE = 0x0200; -pub const STATX_BLOCKS = 0x0400; -pub const STATX_BASIC_STATS = 0x07ff; +/// Flags used to request specific members in `Statx` be filled out. +/// The `Statx.mask` member will be updated with what information the kernel +/// returned. Callers must check this field since support varies by kernel +/// version and filesystem. +pub const STATX = packed struct(u32) { + /// Want `mode & S.IFMT`. + TYPE: bool = false, + /// Want `mode & ~S.IFMT`. + MODE: bool = false, + /// Want the `nlink` member. + NLINK: bool = false, + /// Want the `uid` member. + UID: bool = false, + /// Want the `gid` member. + GID: bool = false, + /// Want the `atime` member. + ATIME: bool = false, + /// Want the `mtime` member. + MTIME: bool = false, + /// Want the `ctime` member. + CTIME: bool = false, + /// Want the `ino` member. + INO: bool = false, + /// Want the `size` member. + SIZE: bool = false, + /// Want the `blocks` member. + BLOCKS: bool = false, + /// Want the `btime` member. + BTIME: bool = false, + /// Want the `mnt_id` member. + MNT_ID: bool = false, + /// Want the `dio_mem_align` and `dio_offset_align` members. + DIOALIGN: bool = false, + /// Want the `stx_mnt_id` member. + MNT_ID_UNIQUE: bool = false, + /// Want the `sub` member. + SUBVOL: bool = false, + /// Want the `atomic_write_unit_min`, `atomic_write_unit_max` and + /// `atomic_write_segments_max` members. + WRITE_ATOMIC: bool = false, + /// Want the `dio_read_offset_align` member. + DIO_READ_ALIGN: bool = false, + __pad: u13 = 0, + /// Reserved for future expansion; must not be set. + __RESERVED: bool = false, -pub const STATX_BTIME = 0x0800; + pub const BASIC_STATS: STATX = @bitCast(@as(u32, 0x7ff)); +}; -pub const STATX_ATTR_COMPRESSED = 0x0004; -pub const STATX_ATTR_IMMUTABLE = 0x0010; -pub const STATX_ATTR_APPEND = 0x0020; -pub const STATX_ATTR_NODUMP = 0x0040; -pub const STATX_ATTR_ENCRYPTED = 0x0800; -pub const STATX_ATTR_AUTOMOUNT = 0x1000; +/// Attributes about the state or features of a file as a bitmask. +/// Flags marked [I] correspond to the `FS_IOC_SETFLAGS` values semantically. +/// See [FS_IOC_SETFLAGS(2const)](https://man7.org/linux/man-pages/man2/FS_IOC_GETFLAGS.2const.html) +/// for more. +pub const STATX_ATTR = packed struct(u64) { + __pad1: u3 = 0, + /// [I] File is compressed by the fs. + COMPRESSED: bool = false, + __pad2: u1 = 0, + /// [I] File is marked immutable. + IMMUTABLE: bool = false, + /// [I] File is append-only. + APPEND: bool = false, + /// [I] File is not to be dumped. + NODUMP: bool = false, + /// [I] File requires a key to decrypt in the filesystem. + ENCRYPTED: bool = false, + /// File names a directory that triggers an automount. + AUTOMOUNT: bool = false, + /// File names the root of a mount. + MOUNT_ROOT: bool = false, + /// [I] File is protected by the `dm-verity` device. + VERITY: bool = false, + /// File is currently in the CPU direct access state. + /// Does not correspond to the per-inode DAX flag that some filesystems support. + DAX: bool = false, + /// File supports atomic write operations. + WRITE_ATOMIC: bool = false, + __pad3: u50 = 0, +}; pub const statx_timestamp = extern struct { + /// Number of seconds before or after `1970-01-01T00:00:00Z`. sec: i64, + /// Number of nanoseconds (0..999,999,999) after `sec`. nsec: u32, + // Reserved for future increases in resolution. __pad1: u32, }; /// Renamed to `Statx` to not conflict with the `statx` function. pub const Statx = extern struct { - /// Mask of bits indicating filled fields - mask: u32, - - /// Block size for filesystem I/O + /// Mask of bits indicating filled fields. + mask: STATX, + /// Block size for filesystem I/O. blksize: u32, - - /// Extra file attribute indicators - attributes: u64, - - /// Number of hard links + /// Extra file attribute indicators. + attributes: STATX_ATTR, + /// Number of hard links. nlink: u32, - - /// User ID of owner + /// User ID of owner. uid: uid_t, - - /// Group ID of owner + /// Group ID of owner. gid: gid_t, - - /// File type and mode + /// File type and mode. mode: u16, - __pad1: u16, - - /// Inode number + __spare0: u16, + /// Inode number. ino: u64, - - /// Total size in bytes + /// Total size in bytes. size: u64, - - /// Number of 512B blocks allocated + /// Number of 512B blocks allocated. blocks: u64, - /// Mask to show what's supported in `attributes`. - attributes_mask: u64, - - /// Last access file timestamp + attributes_mask: STATX_ATTR, + /// Last access file timestamp. atime: statx_timestamp, - - /// Creation file timestamp + /// Creation file timestamp. btime: statx_timestamp, - - /// Last status change file timestamp + /// Last status change file timestamp. ctime: statx_timestamp, - - /// Last modification file timestamp + /// Last modification file timestamp. mtime: statx_timestamp, - /// Major ID, if this file represents a device. rdev_major: u32, - /// Minor ID, if this file represents a device. rdev_minor: u32, - /// Major ID of the device containing the filesystem where this file resides. dev_major: u32, - /// Minor ID of the device containing the filesystem where this file resides. dev_minor: u32, - - __pad2: [14]u64, + /// Mount ID + mnt_id: u64, + /// Memory buffer alignment for direct I/O. + dio_mem_align: u32, + /// File offset alignment for direct I/O. + dio_offset_align: u32, + /// Subvolume identifier. + subvol: u64, + /// Min atomic write unit in bytes. + atomic_write_unit_min: u32, + /// Max atomic write unit in bytes. + atomic_write_unit_max: u32, + /// Max atomic write segment count. + atomic_write_segments_max: u32, + /// File offset alignment for direct I/O reads. + dio_read_offset_align: u32, + /// Optimised max atomic write unit in bytes. + atomic_write_unit_max_opt: u32, + __spare2: [1]u32, + __spare3: [8]u64, }; +comptime { + assert(@sizeOf(Statx) == 0x100); +} + pub const addrinfo = extern struct { flags: AI, family: i32, @@ -9970,6 +9986,46 @@ pub const wrapped = struct { } } + pub const StatxError = std.posix.UnexpectedError || error{ + /// Search permission is denied for one of the directories in `path`. + AccessDenied, + /// Too many symbolic links were encountered traversing `path`. + SymLinkLoop, + /// `path` is too long. + NameTooLong, + /// One of: + /// - A component of `path` does not exist. + /// - A component of `path` is not a directory. + /// - `path` is a relative and `dirfd` is not a directory file descriptor. + FileNotFound, + /// Insufficient memory is available. + SystemResources, + }; + + pub fn statx(dirfd: fd_t, path: [*:0]const u8, flags: u32, mask: STATX) StatxError!Statx { + const use_c = std.c.versionCheck(if (builtin.abi.isAndroid()) + .{ .major = 30, .minor = 0, .patch = 0 } + else + .{ .major = 2, .minor = 28, .patch = 0 }); + const sys = if (use_c) std.c else std.os.linux; + + var stx = std.mem.zeroes(Statx); + const rc = sys.statx(dirfd, path, flags, mask, &stx); + return switch (sys.errno(rc)) { + .SUCCESS => stx, + .ACCES => error.AccessDenied, + .BADF => invalidApiUsage(), + .FAULT => invalidApiUsage(), + .INVAL => invalidApiUsage(), + .LOOP => error.SymLinkLoop, + .NAMETOOLONG => error.NameTooLong, + .NOENT => error.FileNotFound, + .NOTDIR => error.FileNotFound, + .NOMEM => error.SystemResources, + else => |err| unexpectedErrno(err), + }; + } + const unexpectedErrno = std.posix.unexpectedErrno; fn invalidApiUsage() error{Unexpected} { diff --git a/lib/std/os/linux/IoUring.zig b/lib/std/os/linux/IoUring.zig index 4992a352d21b0e937a2cee93d1aae5db9af32b90..8568acbcc404b961c807d96e3f55700ba9fc7fdf 100644 --- a/lib/std/os/linux/IoUring.zig +++ b/lib/std/os/linux/IoUring.zig @@ -958,7 +958,7 @@ pub fn statx( fd: linux.fd_t, path: [:0]const u8, flags: u32, - mask: u32, + mask: linux.STATX, buf: *linux.Statx, ) !*linux.io_uring_sqe { const sqe = try self.get_sqe(); @@ -2691,7 +2691,7 @@ test "statx" { tmp.dir.fd, path, 0, - linux.STATX_SIZE, + .{ .SIZE = true }, &buf, ); try testing.expectEqual(linux.IORING_OP.STATX, sqe.opcode); @@ -2718,7 +2718,7 @@ test "statx" { .flags = 0, }, cqe); - try testing.expect(buf.mask & linux.STATX_SIZE == linux.STATX_SIZE); + try testing.expect(buf.mask.SIZE); try testing.expectEqual(@as(u64, 6), buf.size); } diff --git a/lib/std/os/linux/aarch64.zig b/lib/std/os/linux/aarch64.zig index 4977593ef5a99578439ac92c534a0ccdc61786b0..ddf1a61a253e650bdfffc32e92e6e9255f7d1d42 100644 --- a/lib/std/os/linux/aarch64.zig +++ b/lib/std/os/linux/aarch64.zig @@ -143,43 +143,4 @@ pub const VDSO = struct { pub const CGT_VER = "LINUX_2.6.39"; }; -pub const blksize_t = i32; -pub const nlink_t = u32; pub const time_t = i64; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __pad: u64, - size: off_t, - blksize: blksize_t, - __pad2: i32, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - __unused: [2]u32, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; diff --git a/lib/std/os/linux/arm.zig b/lib/std/os/linux/arm.zig index 0a5b25f9f0785cfa13846cd2b01694e4d51e0c60..0bcdaaf319311a458e12de5a4220ac182e1751c2 100644 --- a/lib/std/os/linux/arm.zig +++ b/lib/std/os/linux/arm.zig @@ -179,43 +179,4 @@ pub const HWCAP = struct { pub const EVTSTRM = 1 << 21; }; -pub const blksize_t = i32; -pub const nlink_t = u32; pub const time_t = i32; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - __dev_padding: u32, - __ino_truncated: u32, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __rdev_padding: u32, - size: off_t, - blksize: blksize_t, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - ino: ino_t, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; diff --git a/lib/std/os/linux/hexagon.zig b/lib/std/os/linux/hexagon.zig index d3b4149d65dd9a327ce8990932114b86adce2361..ff5331467d1510d1f60551625348e644d6c40462 100644 --- a/lib/std/os/linux/hexagon.zig +++ b/lib/std/os/linux/hexagon.zig @@ -119,45 +119,6 @@ pub fn clone() callconv(.naked) u32 { ); } -pub const blksize_t = i32; -pub const nlink_t = u32; pub const time_t = i64; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __pad: u32, - size: off_t, - blksize: blksize_t, - __pad2: i32, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - __unused: [2]u32, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; pub const VDSO = void; diff --git a/lib/std/os/linux/io_uring_sqe.zig b/lib/std/os/linux/io_uring_sqe.zig index 5658206a66a865305e6685365f106fbf8f017f29..808276170a227278936a01ba636dd127e78e35e8 100644 --- a/lib/std/os/linux/io_uring_sqe.zig +++ b/lib/std/os/linux/io_uring_sqe.zig @@ -420,10 +420,10 @@ pub const io_uring_sqe = extern struct { fd: linux.fd_t, path: [*:0]const u8, flags: u32, - mask: u32, + mask: linux.STATX, buf: *linux.Statx, ) void { - sqe.prep_rw(.STATX, fd, @intFromPtr(path), mask, @intFromPtr(buf)); + sqe.prep_rw(.STATX, fd, @intFromPtr(path), @as(u32, @bitCast(mask)), @intFromPtr(buf)); sqe.rw_flags = flags; } diff --git a/lib/std/os/linux/loongarch64.zig b/lib/std/os/linux/loongarch64.zig index 41450c99767064fbb7a16a5420ce3c2dcea0965c..8e7677f276ffa7ec86119c8dbc96a9d81b18195a 100644 --- a/lib/std/os/linux/loongarch64.zig +++ b/lib/std/os/linux/loongarch64.zig @@ -125,46 +125,7 @@ pub fn clone() callconv(.naked) u64 { ); } -pub const blksize_t = i32; -pub const nlink_t = u32; pub const time_t = i64; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u32; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - _pad1: u64, - size: off_t, - blksize: blksize_t, - _pad2: i32, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - _pad3: [2]u32, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; pub const VDSO = struct { pub const CGT_SYM = "__vdso_clock_gettime"; diff --git a/lib/std/os/linux/m68k.zig b/lib/std/os/linux/m68k.zig index 29d9adf1f7b0edab40e9a7c5d43ae569350c695d..2c5f13aa6321d3c65121deead7a629b3188a0c0d 100644 --- a/lib/std/os/linux/m68k.zig +++ b/lib/std/os/linux/m68k.zig @@ -142,45 +142,7 @@ pub fn restore_rt() callconv(.naked) noreturn { ); } -pub const blksize_t = i32; -pub const nlink_t = u32; pub const time_t = i32; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -pub const Stat = extern struct { - dev: dev_t, - __pad: i16, - __ino_truncated: i32, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __pad2: i16, - size: off_t, - blksize: blksize_t, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - ino: ino_t, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; // No VDSO used as of glibc 112a0ae18b831bf31f44d81b82666980312511d6. pub const VDSO = void; diff --git a/lib/std/os/linux/mips.zig b/lib/std/os/linux/mips.zig index 3bd7790bd5278e8485a512ce4f295b0acc9e213e..bcca7d1ef205ded83f2125097605c613b27d371b 100644 --- a/lib/std/os/linux/mips.zig +++ b/lib/std/os/linux/mips.zig @@ -230,43 +230,4 @@ pub const VDSO = struct { pub const CGT_VER = "LINUX_2.6"; }; -pub const blksize_t = u32; -pub const nlink_t = u32; pub const time_t = i32; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat64` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - __pad0: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __pad1: [2]u32, - size: off_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - blksize: blksize_t, - __pad3: u32, - blocks: blkcnt_t, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; diff --git a/lib/std/os/linux/mips64.zig b/lib/std/os/linux/mips64.zig index 82ad6184f1afa69dfbdbdc22f51f1c0ac40df674..ec5681b5138c217edc306abc3bff1f2aca00f579 100644 --- a/lib/std/os/linux/mips64.zig +++ b/lib/std/os/linux/mips64.zig @@ -184,55 +184,4 @@ pub const VDSO = struct { pub const CGT_VER = "LINUX_2.6"; }; -pub const blksize_t = u32; -pub const nlink_t = u32; pub const time_t = i32; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - __pad0: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __pad1: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). - size: off_t, - atim: u32, - atim_nsec: u32, - mtim: u32, - mtim_nsec: u32, - ctim: u32, - ctim_nsec: u32, - blksize: blksize_t, - __pad3: u32, - blocks: blkcnt_t, - - pub fn atime(self: @This()) std.os.linux.timespec { - return .{ - .sec = self.atim, - .nsec = self.atim_nsec, - }; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return .{ - .sec = self.mtim, - .nsec = self.mtim_nsec, - }; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return .{ - .sec = self.ctim, - .nsec = self.ctim_nsec, - }; - } -}; diff --git a/lib/std/os/linux/mipsn32.zig b/lib/std/os/linux/mipsn32.zig index 584edf7c8019a14834dc09273208b00d6cb3c60c..4f7c7d60fdfe2f305861c24f7872b3b17d20380a 100644 --- a/lib/std/os/linux/mipsn32.zig +++ b/lib/std/os/linux/mipsn32.zig @@ -184,55 +184,4 @@ pub const VDSO = struct { pub const CGT_VER = "LINUX_2.6"; }; -pub const blksize_t = u32; -pub const nlink_t = u32; pub const time_t = i32; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - __pad0: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __pad1: [2]u32, // -1 because our dev_t is u64 (kernel dev_t is really u32). - size: off_t, - atim: u32, - atim_nsec: u32, - mtim: u32, - mtim_nsec: u32, - ctim: u32, - ctim_nsec: u32, - blksize: blksize_t, - __pad3: u32, - blocks: blkcnt_t, - - pub fn atime(self: @This()) std.os.linux.timespec { - return .{ - .sec = self.atim, - .nsec = self.atim_nsec, - }; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return .{ - .sec = self.mtim, - .nsec = self.mtim_nsec, - }; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return .{ - .sec = self.ctim, - .nsec = self.ctim_nsec, - }; - } -}; diff --git a/lib/std/os/linux/or1k.zig b/lib/std/os/linux/or1k.zig index 1054e52d1991e769cbbc8a9a39931abfea5cab5e..45352e4791eefe818173d6f21aab66c92b0d74d8 100644 --- a/lib/std/os/linux/or1k.zig +++ b/lib/std/os/linux/or1k.zig @@ -131,43 +131,4 @@ pub fn clone() callconv(.naked) u32 { pub const VDSO = void; -pub const blksize_t = u32; -pub const nlink_t = u32; pub const time_t = i32; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat64` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - _pad0: [2]u32, - size: off_t, - blksize: blksize_t, - _pad1: u32, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - _pad2: [2]u32, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; diff --git a/lib/std/os/linux/powerpc.zig b/lib/std/os/linux/powerpc.zig index d96a9d67f01d6f53aac0e7bce8ed5270c5e52a4a..78c6aadda741dcaf87522c980057bc7572914196 100644 --- a/lib/std/os/linux/powerpc.zig +++ b/lib/std/os/linux/powerpc.zig @@ -269,42 +269,4 @@ pub const VDSO = struct { pub const CGT_VER = "LINUX_2.6.15"; }; -pub const blksize_t = i32; -pub const nlink_t = u32; pub const time_t = i32; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __rdev_padding: i16, - size: off_t, - blksize: blksize_t, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - __unused: [2]u32, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; diff --git a/lib/std/os/linux/powerpc64.zig b/lib/std/os/linux/powerpc64.zig index d1663feaa88e6baa307500c1b850be05bbea74a8..f4375a4545b7ad1f6ecb128e137929cfebe9d139 100644 --- a/lib/std/os/linux/powerpc64.zig +++ b/lib/std/os/linux/powerpc64.zig @@ -254,41 +254,4 @@ pub const VDSO = struct { pub const CGT_VER = "LINUX_2.6.15"; }; -pub const blksize_t = i64; -pub const nlink_t = u64; pub const time_t = i64; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - nlink: nlink_t, - mode: mode_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - size: off_t, - blksize: blksize_t, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - __unused: [3]u64, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; diff --git a/lib/std/os/linux/riscv32.zig b/lib/std/os/linux/riscv32.zig index 34f73506a1abde7c572b5ca6e6a32cf6fb3f6856..c1b216b38ede5ef2f8ee2cec955cba3802d027d2 100644 --- a/lib/std/os/linux/riscv32.zig +++ b/lib/std/os/linux/riscv32.zig @@ -124,46 +124,7 @@ pub fn clone() callconv(.naked) u32 { ); } -pub const blksize_t = i32; -pub const nlink_t = u32; pub const time_t = i64; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __pad: u32, - size: off_t, - blksize: blksize_t, - __pad2: i32, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - __unused: [2]u32, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; pub const VDSO = struct { pub const CGT_SYM = "__vdso_clock_gettime"; diff --git a/lib/std/os/linux/riscv64.zig b/lib/std/os/linux/riscv64.zig index e404693df08ebf07f6e38359b819b2b6a017699e..50a456836a16be78992138639a71c32d055157d0 100644 --- a/lib/std/os/linux/riscv64.zig +++ b/lib/std/os/linux/riscv64.zig @@ -124,46 +124,7 @@ pub fn clone() callconv(.naked) u64 { ); } -pub const blksize_t = i32; -pub const nlink_t = u32; pub const time_t = i64; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __pad: u64, - size: off_t, - blksize: blksize_t, - __pad2: i32, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - __unused: [2]u32, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; pub const VDSO = struct { pub const CGT_SYM = "__vdso_clock_gettime"; diff --git a/lib/std/os/linux/s390x.zig b/lib/std/os/linux/s390x.zig index 0a09982f2a1cdb00a2329d4b51e3760a0dbcf718..17a558e83d6e3a567c36444ec8834f487fa962a7 100644 --- a/lib/std/os/linux/s390x.zig +++ b/lib/std/os/linux/s390x.zig @@ -152,44 +152,7 @@ pub fn restore_rt() callconv(.naked) noreturn { ); } -pub const blksize_t = i64; -pub const nlink_t = u64; pub const time_t = i64; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - nlink: nlink_t, - mode: mode_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - size: off_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - blksize: blksize_t, - blocks: blkcnt_t, - __unused: [3]c_ulong, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; pub const VDSO = struct { pub const CGT_SYM = "__kernel_clock_gettime"; diff --git a/lib/std/os/linux/sparc64.zig b/lib/std/os/linux/sparc64.zig index 76506dbfa3dff0f11cb1e48ebd1f8ceb38a01812..59880bba873d67fad775cb8597d3661a3cd5fd21 100644 --- a/lib/std/os/linux/sparc64.zig +++ b/lib/std/os/linux/sparc64.zig @@ -228,46 +228,4 @@ pub const VDSO = struct { pub const CGT_VER = "LINUX_2.6"; }; -pub const off_t = i64; -pub const ino_t = u64; pub const time_t = i64; -pub const mode_t = u32; -pub const dev_t = u64; -pub const nlink_t = u32; -pub const blksize_t = i64; -pub const blkcnt_t = i64; - -// The `stat64` definition used by the kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - nlink: nlink_t, - _pad: i32, - - mode: mode_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - __pad0: u32, - - rdev: dev_t, - size: i64, - blksize: blksize_t, - blocks: blkcnt_t, - - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - __unused: [3]u64, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; diff --git a/lib/std/os/linux/test.zig b/lib/std/os/linux/test.zig index c3ffe82d3f9c1fd0bc8d083077e632a76cfd6c6d..5464472fbc87651e27ee25258a9b1a3c1d90732b 100644 --- a/lib/std/os/linux/test.zig +++ b/lib/std/os/linux/test.zig @@ -10,8 +10,6 @@ const expectEqual = std.testing.expectEqual; const fs = std.fs; test "fallocate" { - if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23809 - var tmp = std.testing.tmpDir(.{}); defer tmp.cleanup(); @@ -84,26 +82,22 @@ test "statx" { var file = try tmp.dir.createFile(tmp_file_name, .{}); defer file.close(); - var statx_buf: linux.Statx = undefined; - switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, linux.STATX_BASIC_STATS, &statx_buf))) { + var buf: linux.Statx = undefined; + switch (linux.errno(linux.statx(file.handle, "", linux.AT.EMPTY_PATH, .BASIC_STATS, &buf))) { .SUCCESS => {}, else => unreachable, } - if (builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) return error.SkipZigTest; // No fstatat, so the rest of the test is meaningless. - - var stat_buf: linux.Stat = undefined; - switch (linux.errno(linux.fstatat(file.handle, "", &stat_buf, linux.AT.EMPTY_PATH))) { - .SUCCESS => {}, - else => unreachable, - } - - try expect(stat_buf.mode == statx_buf.mode); - try expect(@as(u32, @bitCast(stat_buf.uid)) == statx_buf.uid); - try expect(@as(u32, @bitCast(stat_buf.gid)) == statx_buf.gid); - try expect(@as(u64, @bitCast(@as(i64, stat_buf.size))) == statx_buf.size); - try expect(@as(u64, @bitCast(@as(i64, stat_buf.blksize))) == statx_buf.blksize); - try expect(@as(u64, @bitCast(@as(i64, stat_buf.blocks))) == statx_buf.blocks); + const uid = linux.getuid(); + const gid = linux.getgid(); + if (buf.mask.MODE) + try expectEqual(@as(linux.mode_t, linux.S.IFREG), buf.mode & linux.S.IFMT); + if (buf.mask.UID) + try expectEqual(uid, buf.uid); + if (buf.mask.GID) + try expectEqual(gid, buf.gid); + if (buf.mask.SIZE) + try expectEqual(@as(u64, 0), buf.size); } test "user and group ids" { diff --git a/lib/std/os/linux/x32.zig b/lib/std/os/linux/x32.zig index ac596844d670fe61569f082959b4c9840f659308..97deb7640f13b04cfa907caa074f63d48116fb56 100644 --- a/lib/std/os/linux/x32.zig +++ b/lib/std/os/linux/x32.zig @@ -132,14 +132,7 @@ pub fn restore_rt() callconv(.naked) noreturn { } } -pub const mode_t = u32; pub const time_t = i32; -pub const nlink_t = u32; -pub const blksize_t = i32; -pub const blkcnt_t = i32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; pub const VDSO = struct { pub const CGT_SYM = "__vdso_clock_gettime"; @@ -155,36 +148,3 @@ pub const ARCH = struct { pub const GET_FS = 0x1003; pub const GET_GS = 0x1004; }; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - nlink: nlink_t, - - mode: mode_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - __pad0: u32, - rdev: dev_t, - size: off_t, - blksize: blksize_t, - blocks: i64, - - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - __unused: [3]i32, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; diff --git a/lib/std/os/linux/x86.zig b/lib/std/os/linux/x86.zig index a68a4af317b6848d65eefbbd6164e74c749b4e66..e95afe23a61bc94930169c7671bf49d4028e28a2 100644 --- a/lib/std/os/linux/x86.zig +++ b/lib/std/os/linux/x86.zig @@ -195,46 +195,7 @@ pub const VDSO = struct { pub const CGT_VER = "LINUX_2.6"; }; -pub const blksize_t = i32; -pub const nlink_t = u32; pub const time_t = i32; -pub const mode_t = u32; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; -pub const blkcnt_t = i64; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - __dev_padding: u32, - __ino_truncated: u32, - mode: mode_t, - nlink: nlink_t, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - rdev: dev_t, - __rdev_padding: u32, - size: off_t, - blksize: blksize_t, - blocks: blkcnt_t, - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - ino: ino_t, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; pub const user_desc = extern struct { entry_number: u32, diff --git a/lib/std/os/linux/x86_64.zig b/lib/std/os/linux/x86_64.zig index d9c2d17f09e33625180765c96f4853b306ee8a3d..cab71d03e059cdc53e2e95161dcfb82a4b1902cc 100644 --- a/lib/std/os/linux/x86_64.zig +++ b/lib/std/os/linux/x86_64.zig @@ -132,14 +132,7 @@ pub fn restore_rt() callconv(.naked) noreturn { } } -pub const mode_t = u64; pub const time_t = i64; -pub const nlink_t = u64; -pub const blksize_t = i64; -pub const blkcnt_t = i64; -pub const off_t = i64; -pub const ino_t = u64; -pub const dev_t = u64; pub const VDSO = struct { pub const CGT_SYM = "__vdso_clock_gettime"; @@ -155,36 +148,3 @@ pub const ARCH = struct { pub const GET_FS = 0x1003; pub const GET_GS = 0x1004; }; - -// The `stat` definition used by the Linux kernel. -pub const Stat = extern struct { - dev: dev_t, - ino: ino_t, - nlink: u64, - - mode: u32, - uid: std.os.linux.uid_t, - gid: std.os.linux.gid_t, - __pad0: u32, - rdev: dev_t, - size: off_t, - blksize: i64, - blocks: i64, - - atim: std.os.linux.timespec, - mtim: std.os.linux.timespec, - ctim: std.os.linux.timespec, - __unused: [3]i64, - - pub fn atime(self: @This()) std.os.linux.timespec { - return self.atim; - } - - pub fn mtime(self: @This()) std.os.linux.timespec { - return self.mtim; - } - - pub fn ctime(self: @This()) std.os.linux.timespec { - return self.ctim; - } -}; diff --git a/lib/std/posix.zig b/lib/std/posix.zig index fb6b5a51dea4e19cba8edf4cd60f9def14695ecc..aeed9e5d8e4816c24da7a0cb9c767344f7b9c414 100644 --- a/lib/std/posix.zig +++ b/lib/std/posix.zig @@ -119,7 +119,18 @@ pub const STDIN_FILENO = system.STDIN_FILENO; pub const STDOUT_FILENO = system.STDOUT_FILENO; pub const SYS = system.SYS; pub const Sigaction = system.Sigaction; -pub const Stat = system.Stat; +pub const Stat = switch (native_os) { + // Has no concept of `stat`. + .windows => void, + // The `stat` bits/wrappers are removed due to having to maintain the + // different varying `struct stat`s per target and libc, leading to runtime + // errors. + // + // Users targeting linux should add a comptime check and use `statx`, + // similar to how `std.fs.File.stat` does. + .linux => void, + else => system.Stat, +}; pub const T = system.T; pub const TCP = system.TCP; pub const VDSO = system.VDSO; @@ -480,15 +491,21 @@ fn fchmodat2(dirfd: fd_t, path: []const u8, mode: mode_t, flags: u32) FChmodAtEr } defer close(pathfd); - const stat = fstatatZ(pathfd, "", AT.EMPTY_PATH) catch |err| switch (err) { + const path_mode = if (linux.wrapped.statx( + pathfd, + "", + AT.EMPTY_PATH, + .{ .TYPE = true }, + )) |stx| blk: { + assert(stx.mask.TYPE); + break :blk stx.mode; + } else |err| switch (err) { error.NameTooLong => unreachable, error.FileNotFound => unreachable, - error.Streaming => unreachable, - error.BadPathName => return error.Unexpected, - error.Canceled => return error.Canceled, else => |e| return e, }; - if ((stat.mode & S.IFMT) == S.IFLNK) + // Even though we only wanted TYPE, the kernel can still fill in the additional bits. + if ((path_mode & S.IFMT) == S.IFLNK) return error.OperationNotSupported; var procfs_buf: ["/proc/self/fd/-2147483648\x00".len]u8 = undefined; @@ -3843,13 +3860,9 @@ pub fn fstat(fd: fd_t) FStatError!Stat { if (native_os == .wasi and !builtin.link_libc) { return Stat.fromFilestat(try std.os.fstat_wasi(fd)); } - if (native_os == .windows) { - @compileError("fstat is not yet implemented on Windows"); - } - const fstat_sym = if (lfs64_abi) system.fstat64 else system.fstat; var stat = mem.zeroes(Stat); - switch (errno(fstat_sym(fd, &stat))) { + switch (errno(system.fstat(fd, &stat))) { .SUCCESS => return stat, .INVAL => unreachable, .BADF => unreachable, // Always a race condition. @@ -3889,9 +3902,8 @@ pub fn fstatatZ(dirfd: fd_t, pathname: [*:0]const u8, flags: u32) FStatAtError!S @compileError("use std.Io instead"); } - const fstatat_sym = if (lfs64_abi) system.fstatat64 else system.fstatat; var stat = mem.zeroes(Stat); - switch (errno(fstatat_sym(dirfd, pathname, &stat, flags))) { + switch (errno(system.fstatat(dirfd, pathname, &stat, flags))) { .SUCCESS => return stat, .INVAL => unreachable, .BADF => unreachable, // Always a race condition. diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index c9c41dd042f335001712bb44696f9b4ea5e087b9..bc8d45527163d08fd8503766d8db569cc74bfab8 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -16,6 +16,7 @@ const AtomicRmwOp = std.builtin.AtomicRmwOp; const AtomicOrder = std.builtin.AtomicOrder; const native_os = builtin.target.os.tag; const tmpDir = std.testing.tmpDir; +const AT = posix.AT; // NOTE: several additional tests are in test/standalone/posix/. Any tests that mutate // 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 { try expect(mem.eql(u8, target_path, given)); } +fn getLinkInfo(fd: posix.fd_t) !struct { posix.ino_t, posix.nlink_t } { + if (native_os == .linux) { + const stx = try linux.wrapped.statx( + fd, + "", + posix.AT.EMPTY_PATH, + .{ .INO = true, .NLINK = true }, + ); + std.debug.assert(stx.mask.INO); + std.debug.assert(stx.mask.NLINK); + return .{ stx.ino, stx.nlink }; + } + + const st = try posix.fstat(fd); + return .{ st.ino, st.nlink }; +} + test "linkat with different directories" { - if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstatat()`. - if (builtin.cpu.arch.isMIPS64()) return error.SkipZigTest; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons. - switch (native_os) { .wasi, .linux, .illumos => {}, else => return error.SkipZigTest, @@ -153,19 +168,49 @@ test "linkat with different directories" { defer nfd.close(); { - const estat = try posix.fstat(efd.handle); - const nstat = try posix.fstat(nfd.handle); - try testing.expectEqual(estat.ino, nstat.ino); - try testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); + const eino, _ = try getLinkInfo(efd.handle); + const nino, const nlink = try getLinkInfo(nfd.handle); + try testing.expectEqual(eino, nino); + try testing.expectEqual(@as(posix.nlink_t, 2), nlink); } // Test 2: remove link try posix.unlinkat(subdir.fd, link_name, 0); + _, const elink = try getLinkInfo(efd.handle); + try testing.expectEqual(@as(posix.nlink_t, 1), elink); +} - { - const estat = try posix.fstat(efd.handle); - try testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); - } +test "fstatat" { + if (posix.Stat == void) return error.SkipZigTest; + if (native_os == .wasi and !builtin.link_libc) return error.SkipZigTest; + + var tmp = tmpDir(.{}); + defer tmp.cleanup(); + + // create dummy file + const contents = "nonsense"; + try tmp.dir.writeFile(.{ .sub_path = "file.txt", .data = contents }); + + // fetch file's info on the opened fd directly + const file = try tmp.dir.openFile("file.txt", .{}); + const stat = try posix.fstat(file.handle); + defer file.close(); + + // now repeat but using `fstatat` instead + const statat = try posix.fstatat(tmp.dir.fd, "file.txt", posix.AT.SYMLINK_NOFOLLOW); + + try expectEqual(stat.dev, statat.dev); + try expectEqual(stat.ino, statat.ino); + try expectEqual(stat.nlink, statat.nlink); + try expectEqual(stat.mode, statat.mode); + try expectEqual(stat.uid, statat.uid); + try expectEqual(stat.gid, statat.gid); + try expectEqual(stat.rdev, statat.rdev); + try expectEqual(stat.size, statat.size); + try expectEqual(stat.blksize, statat.blksize); + // The stat.blocks/statat.blocks count is managed by the filesystem and may + // change if the file is stored in a journal or "inline". + // try expectEqual(stat.blocks, statat.blocks); } test "readlinkat" { @@ -906,14 +951,31 @@ test "pwrite with empty buffer" { try expectEqual(rc, 0); } +fn getFileMode(dir: posix.fd_t, path: []const u8) !posix.mode_t { + const path_z = try posix.toPosixPath(path); + const mode: posix.mode_t = if (native_os == .linux) blk: { + const stx = try linux.wrapped.statx( + dir, + &path_z, + posix.AT.SYMLINK_NOFOLLOW, + .{ .MODE = true }, + ); + std.debug.assert(stx.mask.MODE); + break :blk stx.mode; + } else blk: { + const st = try posix.fstatatZ(dir, &path_z, posix.AT.SYMLINK_NOFOLLOW); + break :blk st.mode; + }; + + return mode & 0b111_111_111; +} + fn expectMode(dir: posix.fd_t, file: []const u8, mode: posix.mode_t) !void { - const st = try posix.fstatat(dir, file, posix.AT.SYMLINK_NOFOLLOW); - try expectEqual(mode, st.mode & 0b111_111_111); + const actual = try getFileMode(dir, file); + try expectEqual(mode, actual & 0b111_111_111); } test "fchmodat smoke test" { - if (builtin.cpu.arch.isMIPS64() and (builtin.abi == .gnuabin32 or builtin.abi == .muslabin32)) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/23808 - if (!std.fs.has_executable_bit) return error.SkipZigTest; var tmp = tmpDir(.{}); @@ -928,13 +990,8 @@ test "fchmodat smoke test" { ); posix.close(fd); - if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.os.tag == .linux and !builtin.link_libc) return error.SkipZigTest; // No `fstatat()`. - try posix.symlinkat("regfile", tmp.dir.fd, "symlink"); - const sym_mode = blk: { - const st = try posix.fstatat(tmp.dir.fd, "symlink", posix.AT.SYMLINK_NOFOLLOW); - break :blk st.mode & 0b111_111_111; - }; + const sym_mode = try getFileMode(tmp.dir.fd, "symlink"); try posix.fchmodat(tmp.dir.fd, "regfile", 0o640, 0); try expectMode(tmp.dir.fd, "regfile", 0o640); diff --git a/src/link/MappedFile.zig b/src/link/MappedFile.zig index 0f8d1855714b422ba9796c77691cbd20644e4c90..975b94578bbd4935e222f2e293b57d2b35d6785c 100644 --- a/src/link/MappedFile.zig +++ b/src/link/MappedFile.zig @@ -54,6 +54,20 @@ pub fn init(file: std.Io.File, gpa: std.mem.Allocator) !MappedFile { }, }; } + if (is_linux) { + const statx = try linux.wrapped.statx( + mf.file.handle, + "", + std.posix.AT.EMPTY_PATH, + .{ .TYPE = true, .SIZE = true, .BLOCKS = true }, + ); + assert(statx.mask.TYPE); + assert(statx.mask.SIZE); + assert(statx.mask.BLOCKS); + + if (!std.posix.S.ISREG(statx.mode)) return error.PathAlreadyExists; + break :stat .{ statx.size, @max(std.heap.pageSize(), statx.blksize) }; + } const stat = try std.posix.fstat(mf.file.handle); if (!std.posix.S.ISREG(stat.mode)) return error.PathAlreadyExists; break :stat .{ @bitCast(stat.size), @max(std.heap.pageSize(), stat.blksize) }; diff --git a/test/standalone/glibc_compat/glibc_runtime_check.zig b/test/standalone/glibc_compat/glibc_runtime_check.zig index 30265a3adce947615d9edcd14783be4304f0fcfe..82f4a54ee1e83b01ae548c97c5b3e7a21551ff9e 100644 --- a/test/standalone/glibc_compat/glibc_runtime_check.zig +++ b/test/standalone/glibc_compat/glibc_runtime_check.zig @@ -23,16 +23,19 @@ const c_string = @cImport( // Version of glibc this test is being built to run against const glibc_ver = builtin.os.versionRange().gnuLibCVersion().?; +extern "c" fn fstatat(dirfd: i32, path: [*:0]const u8, buf: [*]const u8, flag: u32) c_int; +extern "c" fn stat(noalias path: [*:0]const u8, noalias buf: [*]const u8) c_int; + // PR #17034 - fstat moved between libc_nonshared and libc fn checkStat() !void { const cwdFd = std.fs.cwd().fd; - var stat = std.mem.zeroes(std.c.Stat); - var result = std.c.fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &stat, 0); + var buf: [256]u8 = @splat(0); + var result = fstatat(cwdFd, "a_file_that_definitely_does_not_exist", &buf, 0); assert(result == -1); assert(std.posix.errno(result) == .NOENT); - result = std.c.stat("a_file_that_definitely_does_not_exist", &stat); + result = stat("a_file_that_definitely_does_not_exist", &buf); assert(result == -1); assert(std.posix.errno(result) == .NOENT); } diff --git a/test/standalone/posix/relpaths.zig b/test/standalone/posix/relpaths.zig index f7ced28cdbf1b654cd9921211393f112143c4bab..40e2e09464bf66a968a4c540ae05776602f605a7 100644 --- a/test/standalone/posix/relpaths.zig +++ b/test/standalone/posix/relpaths.zig @@ -48,20 +48,29 @@ fn test_symlink(a: std.mem.Allocator, tmp: std.testing.TmpDir) !void { try std.testing.expectEqualStrings(target_name, given); } +fn getLinkInfo(fd: std.posix.fd_t) !struct { std.posix.ino_t, std.posix.nlink_t } { + if (builtin.target.os.tag == .linux) { + const stx = try std.os.linux.wrapped.statx( + fd, + "", + std.posix.AT.EMPTY_PATH, + .{ .INO = true, .NLINK = true }, + ); + std.debug.assert(stx.mask.INO); + std.debug.assert(stx.mask.NLINK); + return .{ stx.ino, stx.nlink }; + } + + const st = try std.posix.fstat(fd); + return .{ st.ino, st.nlink }; +} + fn test_link(tmp: std.testing.TmpDir) !void { switch (builtin.target.os.tag) { .linux, .illumos => {}, else => return, } - if ((builtin.cpu.arch == .riscv32 or builtin.cpu.arch.isLoongArch()) and builtin.target.os.tag == .linux and !builtin.link_libc) { - return; // No `fstat()`. - } - - if (builtin.cpu.arch.isMIPS64()) { - return; // `nstat.nlink` assertion is failing with LLVM 20+ for unclear reasons. - } - const target_name = "link-target"; const link_name = "newlink"; @@ -78,17 +87,16 @@ fn test_link(tmp: std.testing.TmpDir) !void { defer nfd.close(); { - const estat = try std.posix.fstat(efd.handle); - const nstat = try std.posix.fstat(nfd.handle); - try std.testing.expectEqual(estat.ino, nstat.ino); - try std.testing.expectEqual(@as(@TypeOf(nstat.nlink), 2), nstat.nlink); + const eino, _ = try getLinkInfo(efd.handle); + const nino, const nlink = try getLinkInfo(nfd.handle); + try std.testing.expectEqual(eino, nino); + try std.testing.expectEqual(@as(std.posix.nlink_t, 2), nlink); } // Test 2: Remove the link and see the stats update try std.posix.unlink(link_name); - { - const estat = try std.posix.fstat(efd.handle); - try std.testing.expectEqual(@as(@TypeOf(estat.nlink), 1), estat.nlink); + _, const elink = try getLinkInfo(efd.handle); + try std.testing.expectEqual(@as(std.posix.nlink_t, 1), elink); } }