From 35a9faadb2b7b7f8a21a6f5def29bad9c61b5cfa Mon Sep 17 00:00:00 2001 From: Yorhel Date: Sun, 6 Apr 2025 10:36:33 +0200 Subject: [PATCH] Work around panic in Zig fstatat wrapper https://github.com/ziglang/zig/issues/23463 --- src/c.zig | 3 +++ src/scan.zig | 37 +++++++++++++++++++++++-------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/c.zig b/src/c.zig index 88f8e12..cfe201b 100644 --- a/src/c.zig +++ b/src/c.zig @@ -10,6 +10,9 @@ pub const c = @cImport({ @cInclude("locale.h"); // setlocale() and localeconv() @cInclude("fnmatch.h"); // fnmatch() @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("pwd.h"); // getpwnam(), getpwuid() if (@import("builtin").os.tag == .linux) { diff --git a/src/scan.zig b/src/scan.zig index 637c3d3..9094597 100644 --- a/src/scan.zig +++ b/src/scan.zig @@ -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 { - const stat = try std.posix.fstatatZ(parent.fd, name, if (follow) 0 else std.posix.AT.SYMLINK_NOFOLLOW); - symlink.* = std.posix.S.ISLNK(stat.mode); + // std.posix.fstatatZ() is currently unusable due to https://github.com/ziglang/zig/issues/23463 + 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{ .etype = - if (std.posix.S.ISDIR(stat.mode)) .dir - else if (stat.nlink > 1) .link - else if (!std.posix.S.ISREG(stat.mode)) .nonreg + if (c.S_ISDIR(stat.st_mode)) .dir + else if (stat.st_nlink > 1) .link + else if (!c.S_ISREG(stat.st_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), + .blocks = clamp(sink.Stat, .blocks, stat.st_blocks), + .size = clamp(sink.Stat, .size, stat.st_size), + .dev = truncate(sink.Stat, .dev, stat.st_dev), + .ino = truncate(sink.Stat, .ino, stat.st_ino), + .nlink = clamp(sink.Stat, .nlink, stat.st_nlink), .ext = .{ .pack = .{ .hasmtime = true, @@ -67,10 +76,10 @@ fn statAt(parent: std.fs.Dir, name: [:0]const u8, follow: bool, symlink: *bool) .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), + .mtime = clamp(model.Ext, .mtime, stat.st_mtim.tv_sec), + .uid = truncate(model.Ext, .uid, stat.st_uid), + .gid = truncate(model.Ext, .gid, stat.st_gid), + .mode = truncate(model.Ext, .mode, stat.st_mode), }, }; }