Zig 0.16: use statx(), not stat() on Linux

This commit is contained in:
LN Liberda 2026-05-22 23:49:08 +02:00
parent 9103bae106
commit e447acd83c

View file

@ -2,6 +2,7 @@
// SPDX-License-Identifier: MIT
const std = @import("std");
const builtin = @import("builtin");
const main = @import("main.zig");
const util = @import("util.zig");
const model = @import("model.zig");
@ -47,42 +48,97 @@ fn truncate(comptime T: type, comptime field: anytype, x: anytype) std.meta.fiel
pub fn statAt(parent: std.Io.Dir, name: [:0]const u8, follow: bool, symlink: ?*bool) !sink.Stat {
// std.posix.fstatatZ() in Zig 0.14 is not suitable due to https://github.com/ziglang/zig/issues/23463
var stat: std.c.Stat = undefined;
if (std.c.fstatat(parent.fd, name, &stat, if (follow) 0 else std.c.AT.SYMLINK_NOFOLLOW) != 0) {
return switch (std.c._errno().*) {
@intFromEnum(std.c.E.NOENT) => error.FileNotFound,
@intFromEnum(std.c.E.NAMETOOLONG) => error.NameTooLong,
@intFromEnum(std.c.E.NOMEM) => error.OutOfMemory,
@intFromEnum(std.c.E.ACCES) => error.AccessDenied,
else => error.Unexpected,
};
}
if (symlink) |s| s.* = std.c.S.ISLNK(stat.mode);
return sink.Stat{
.etype =
if (std.c.S.ISDIR(stat.mode)) .dir
else if (stat.nlink > 1) .link
else if (!std.c.S.ISREG(stat.mode)) .nonreg
else .reg,
.blocks = clamp(sink.Stat, .blocks, stat.blocks),
.size = clamp(sink.Stat, .size, stat.size),
.dev = truncate(sink.Stat, .dev, stat.dev),
.ino = truncate(sink.Stat, .ino, stat.ino),
.nlink = clamp(sink.Stat, .nlink, stat.nlink),
.ext = .{
.pack = .{
.hasmtime = true,
.hasuid = true,
.hasgid = true,
.hasmode = true,
},
.mtime = clamp(model.Ext, .mtime, stat.mtime().sec),
.uid = truncate(model.Ext, .uid, stat.uid),
.gid = truncate(model.Ext, .gid, stat.gid),
.mode = truncate(model.Ext, .mode, stat.mode),
switch (builtin.os.tag) {
// stat() seems gone for Linux since Zig 0.16.
// https://ziglang.org/download/0.16.0/release-notes.html#FileStat-Make-Access-Time-Optional
.linux => {
var stat: std.os.linux.Statx = undefined;
return switch (std.os.linux.errno(std.os.linux.statx(
parent.handle,
name,
std.os.linux.AT.EMPTY_PATH,
std.os.linux.STATX{
.TYPE = true,
.BLOCKS = true,
.SIZE = true,
.INO = true,
.NLINK = true,
.UID = true,
.GID = true,
.MODE = true,
.MTIME = true,
},
&stat,
))) {
.SUCCESS => sink.Stat{
.etype =
if (std.os.linux.S.ISDIR(stat.mode)) .dir
else if (stat.nlink > 1) .link
else if (!std.os.linux.S.ISREG(stat.mode)) .nonreg
else .reg,
.blocks = @truncate(stat.blocks),
.size = stat.size,
.dev = stat.dev_major,
.ino = stat.ino,
.nlink = @truncate(stat.nlink),
.ext = .{
.pack = .{
.hasmtime = true,
.hasuid = true,
.hasgid = true,
.hasmode = true,
},
.mtime = @intCast(stat.mtime.sec),
.uid = stat.uid,
.gid = stat.gid,
.mode = stat.mode,
},
},
.NOENT => error.FileNotFound,
.NAMETOOLONG => error.NameTooLong,
.NOMEM => error.OutOfMemory,
.ACCES => error.AccessDenied,
else => error.Unexpected,
};
},
};
else => {
var stat: std.c.Stat = undefined;
if (std.c.fstatat(parent.fd, name, &stat, if (follow) 0 else std.c.AT.SYMLINK_NOFOLLOW) != 0) {
return switch (std.c._errno().*) {
@intFromEnum(std.c.E.NOENT) => error.FileNotFound,
@intFromEnum(std.c.E.NAMETOOLONG) => error.NameTooLong,
@intFromEnum(std.c.E.NOMEM) => error.OutOfMemory,
@intFromEnum(std.c.E.ACCES) => error.AccessDenied,
else => error.Unexpected,
};
}
if (symlink) |s| s.* = std.c.S.ISLNK(stat.mode);
return sink.Stat{
.etype =
if (std.c.S.ISDIR(stat.mode)) .dir
else if (stat.nlink > 1) .link
else if (!std.c.S.ISREG(stat.mode)) .nonreg
else .reg,
.blocks = clamp(sink.Stat, .blocks, stat.blocks),
.size = clamp(sink.Stat, .size, stat.size),
.dev = truncate(sink.Stat, .dev, stat.dev),
.ino = truncate(sink.Stat, .ino, stat.ino),
.nlink = clamp(sink.Stat, .nlink, stat.nlink),
.ext = .{
.pack = .{
.hasmtime = true,
.hasuid = true,
.hasgid = true,
.hasmode = true,
},
.mtime = clamp(model.Ext, .mtime, stat.mtime().sec),
.uid = truncate(model.Ext, .uid, stat.uid),
.gid = truncate(model.Ext, .gid, stat.gid),
.mode = truncate(model.Ext, .mode, stat.mode),
},
};
}
}
}