From 13945548fcd4fe3aab0879b670db1fe8131d8b0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Sun, 21 Jul 2024 12:45:47 +0200 Subject: [PATCH] std.fs: Rework to always use statx() instead of fstat()/fstatat() on Linux. statx() is strictly superior to stat() and friends. We can do this because the standard library declares Linux 4.19 to be the minimum version supported in std.Target. This is also necessary on riscv32 where there is only statx(). While here, I improved std.fs.File.metadata() to gather as much information as possible when calling statx() since that is the expectation from this particular API. --- lib/std/fs/Dir.zig | 29 ++++++++++++++-- lib/std/fs/File.zig | 83 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 101 insertions(+), 11 deletions(-) diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 2743f3ed7d976d0197e370d8639e2da349129c7e..34967301e115d2b7679da0cef5a12e41f9e3715b 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -334,7 +334,6 @@ pub const Iterator = switch (native_os) { first_iter: bool, const Self = @This(); - const linux = std.os.linux; pub const Error = IteratorError; @@ -2690,8 +2689,33 @@ pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat { const st = try std.os.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true }); return Stat.fromWasi(st); } + if (native_os == .linux) { + const sub_path_c = try posix.toPosixPath(sub_path); + var stx = std.mem.zeroes(linux.Statx); + + const rc = linux.statx( + self.fd, + &sub_path_c, + linux.AT.NO_AUTOMOUNT, + linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, + &stx, + ); + + return switch (linux.E.init(rc)) { + .SUCCESS => Stat.fromLinux(stx), + .ACCES => error.AccessDenied, + .BADF => unreachable, + .FAULT => unreachable, + .INVAL => unreachable, + .LOOP => error.SymLinkLoop, + .NAMETOOLONG => unreachable, // Handled by posix.toPosixPath() above. + .NOENT, .NOTDIR => error.FileNotFound, + .NOMEM => error.SystemResources, + else => |err| posix.unexpectedErrno(err), + }; + } const st = try posix.fstatat(self.fd, sub_path, 0); - return Stat.fromSystem(st); + return Stat.fromPosix(st); } pub const ChmodError = File.ChmodError; @@ -2751,6 +2775,7 @@ const path = fs.path; const fs = std.fs; const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const linux = std.os.linux; const windows = std.os.windows; const native_os = builtin.os.tag; const have_flock = @TypeOf(posix.system.flock) != void; diff --git a/lib/std/fs/File.zig b/lib/std/fs/File.zig index 05a3b4028e461b13146a98df7fbdd0f3be70f1e1..36e7999bf79daca89a721de328d28c900c6f40d3 100644 --- a/lib/std/fs/File.zig +++ b/lib/std/fs/File.zig @@ -342,7 +342,7 @@ pub fn seekTo(self: File, offset: u64) SeekError!void { return posix.lseek_SET(self.handle, offset); } -pub const GetSeekPosError = posix.SeekError || posix.FStatError; +pub const GetSeekPosError = posix.SeekError || StatError; /// TODO: integrate with async I/O pub fn getPos(self: File) GetSeekPosError!u64 { @@ -357,7 +357,7 @@ pub fn getEndPos(self: File) GetSeekPosError!u64 { return (try self.stat()).size; } -pub const ModeError = posix.FStatError; +pub const ModeError = StatError; /// TODO: integrate with async I/O pub fn mode(self: File) ModeError!Mode { @@ -392,7 +392,7 @@ pub const Stat = struct { /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01. ctime: i128, - pub fn fromSystem(st: posix.Stat) Stat { + pub fn fromPosix(st: posix.Stat) Stat { const atime = st.atime(); const mtime = st.mtime(); const ctime = st.ctime(); @@ -426,6 +426,31 @@ pub const Stat = struct { }; } + pub fn fromLinux(stx: linux.Statx) Stat { + const atime = stx.atime; + const mtime = stx.mtime; + const ctime = stx.ctime; + + return .{ + .inode = stx.ino, + .size = stx.size, + .mode = stx.mode, + .kind = switch (stx.mode & linux.S.IFMT) { + linux.S.IFDIR => .directory, + linux.S.IFCHR => .character_device, + linux.S.IFBLK => .block_device, + linux.S.IFREG => .file, + linux.S.IFIFO => .named_pipe, + linux.S.IFLNK => .sym_link, + linux.S.IFSOCK => .unix_domain_socket, + else => .unknown, + }, + .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec, + .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec, + .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec, + }; + } + pub fn fromWasi(st: std.os.wasi.filestat_t) Stat { return .{ .inode = st.ino, @@ -502,8 +527,34 @@ pub fn stat(self: File) StatError!Stat { return Stat.fromWasi(st); } + if (builtin.os.tag == .linux) { + var stx = std.mem.zeroes(linux.Statx); + + const rc = linux.statx( + self.handle, + "", + linux.AT.EMPTY_PATH, + linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME, + &stx, + ); + + return switch (linux.E.init(rc)) { + .SUCCESS => Stat.fromLinux(stx), + .ACCES => unreachable, + .BADF => unreachable, + .FAULT => unreachable, + .INVAL => unreachable, + .LOOP => unreachable, + .NAMETOOLONG => unreachable, + .NOENT => unreachable, + .NOMEM => error.SystemResources, + .NOTDIR => unreachable, + else => |err| posix.unexpectedErrno(err), + }; + } + const st = try posix.fstat(self.handle); - return Stat.fromSystem(st); + return Stat.fromPosix(st); } pub const ChmodError = posix.FChmodError; @@ -1009,16 +1060,29 @@ pub fn metadata(self: File) MetadataError!Metadata { }; }, .linux => blk: { - const l = std.os.linux; - var stx = std.mem.zeroes(l.Statx); - const rcx = l.statx(self.handle, "\x00", l.AT.EMPTY_PATH, l.STATX_TYPE | - l.STATX_MODE | l.STATX_ATIME | l.STATX_MTIME | l.STATX_BTIME, &stx); + var stx = std.mem.zeroes(linux.Statx); - switch (posix.errno(rcx)) { + // We are gathering information for Metadata, which is meant to contain all the + // native OS information about the file, so use all known flags. + const rc = linux.statx( + self.handle, + "", + linux.AT.EMPTY_PATH, + linux.STATX_BASIC_STATS | linux.STATX_BTIME, + &stx, + ); + + switch (posix.errno(rc)) { .SUCCESS => {}, + .ACCES => unreachable, .BADF => unreachable, .FAULT => unreachable, + .INVAL => unreachable, + .LOOP => unreachable, + .NAMETOOLONG => unreachable, + .NOENT => unreachable, .NOMEM => return error.SystemResources, + .NOTDIR => unreachable, else => |err| return posix.unexpectedErrno(err), } @@ -1712,6 +1776,7 @@ const posix = std.posix; const io = std.io; const math = std.math; const assert = std.debug.assert; +const linux = std.os.linux; const windows = std.os.windows; const Os = std.builtin.Os; const maxInt = std.math.maxInt; -- 2.54.0