Work around panic in Zig fstatat wrapper

https://github.com/ziglang/zig/issues/23463
This commit is contained in:
Yorhel 2025-04-06 10:36:33 +02:00
parent e43d22ba3f
commit 35a9faadb2
2 changed files with 26 additions and 14 deletions

View file

@ -10,6 +10,9 @@ pub const c = @cImport({
@cInclude("locale.h"); // setlocale() and localeconv() @cInclude("locale.h"); // setlocale() and localeconv()
@cInclude("fnmatch.h"); // fnmatch() @cInclude("fnmatch.h"); // fnmatch()
@cInclude("unistd.h"); // getuid() @cInclude("unistd.h"); // getuid()
@cInclude("errno.h"); // error codes
@cInclude("fcntl.h"); // AT_SYMLINK_NOFOLLOW
@cInclude("sys/stat.h"); // fstatat()
@cInclude("sys/types.h"); // struct passwd @cInclude("sys/types.h"); // struct passwd
@cInclude("pwd.h"); // getpwnam(), getpwuid() @cInclude("pwd.h"); // getpwnam(), getpwuid()
if (@import("builtin").os.tag == .linux) { if (@import("builtin").os.tag == .linux) {

View file

@ -47,19 +47,28 @@ fn truncate(comptime T: type, comptime field: anytype, x: anytype) std.meta.fiel
fn statAt(parent: std.fs.Dir, name: [:0]const u8, follow: bool, symlink: *bool) !sink.Stat { fn statAt(parent: std.fs.Dir, name: [:0]const u8, follow: bool, symlink: *bool) !sink.Stat {
const stat = try std.posix.fstatatZ(parent.fd, name, if (follow) 0 else std.posix.AT.SYMLINK_NOFOLLOW); // std.posix.fstatatZ() is currently unusable due to https://github.com/ziglang/zig/issues/23463
symlink.* = std.posix.S.ISLNK(stat.mode); var stat: c.struct_stat = undefined;
if (c.fstatat(parent.fd, name, &stat, if (follow) 0 else c.AT_SYMLINK_NOFOLLOW) != 0)
return switch (std.c._errno().*) {
c.ENOENT => error.FileNotFound,
c.ENAMETOOLONG => error.NameTooLong,
c.ENOMEM => error.OutOfMemory,
c.EACCES => error.AccessDenied,
else => error.Unexpected,
};
symlink.* = c.S_ISLNK(stat.st_mode);
return sink.Stat{ return sink.Stat{
.etype = .etype =
if (std.posix.S.ISDIR(stat.mode)) .dir if (c.S_ISDIR(stat.st_mode)) .dir
else if (stat.nlink > 1) .link else if (stat.st_nlink > 1) .link
else if (!std.posix.S.ISREG(stat.mode)) .nonreg else if (!c.S_ISREG(stat.st_mode)) .nonreg
else .reg, else .reg,
.blocks = clamp(sink.Stat, .blocks, stat.blocks), .blocks = clamp(sink.Stat, .blocks, stat.st_blocks),
.size = clamp(sink.Stat, .size, stat.size), .size = clamp(sink.Stat, .size, stat.st_size),
.dev = truncate(sink.Stat, .dev, stat.dev), .dev = truncate(sink.Stat, .dev, stat.st_dev),
.ino = truncate(sink.Stat, .ino, stat.ino), .ino = truncate(sink.Stat, .ino, stat.st_ino),
.nlink = clamp(sink.Stat, .nlink, stat.nlink), .nlink = clamp(sink.Stat, .nlink, stat.st_nlink),
.ext = .{ .ext = .{
.pack = .{ .pack = .{
.hasmtime = true, .hasmtime = true,
@ -67,10 +76,10 @@ fn statAt(parent: std.fs.Dir, name: [:0]const u8, follow: bool, symlink: *bool)
.hasgid = true, .hasgid = true,
.hasmode = true, .hasmode = true,
}, },
.mtime = clamp(model.Ext, .mtime, stat.mtime().sec), .mtime = clamp(model.Ext, .mtime, stat.st_mtim.tv_sec),
.uid = truncate(model.Ext, .uid, stat.uid), .uid = truncate(model.Ext, .uid, stat.st_uid),
.gid = truncate(model.Ext, .gid, stat.gid), .gid = truncate(model.Ext, .gid, stat.st_gid),
.mode = truncate(model.Ext, .mode, stat.mode), .mode = truncate(model.Ext, .mode, stat.st_mode),
}, },
}; };
} }