diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index bf0db90a73b2..7ad6d3685e54 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; @@ -2642,8 +2641,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; @@ -2700,6 +2724,7 @@ const mem = std.mem; 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 05a3b4028e46..36e7999bf79d 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); - - switch (posix.errno(rcx)) { + var stx = std.mem.zeroes(linux.Statx); + + // 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;