authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-07-21 12:45:47+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2024-07-29 09:50:41+02:00
log13945548fcd4fe3aab0879b670db1fe8131d8b0e
treec3d78b69f446ebf0c62821a3b2866142818762e5
parent4e5068c35cef5e1b3366aa05de60023b9fcd98e6
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

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.

2 files changed, 102 insertions(+), 12 deletions(-)

lib/std/fs/Dir.zig+27-2
......@@ -334,7 +334,6 @@ pub const Iterator = switch (native_os) {
334334 first_iter: bool,
335335
336336 const Self = @This();
337 const linux = std.os.linux;
338337
339338 pub const Error = IteratorError;
340339
......@@ -2690,8 +2689,33 @@ pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
26902689 const st = try std.os.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true });
26912690 return Stat.fromWasi(st);
26922691 }
2692 if (native_os == .linux) {
2693 const sub_path_c = try posix.toPosixPath(sub_path);
2694 var stx = std.mem.zeroes(linux.Statx);
2695
2696 const rc = linux.statx(
2697 self.fd,
2698 &sub_path_c,
2699 linux.AT.NO_AUTOMOUNT,
2700 linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME,
2701 &stx,
2702 );
2703
2704 return switch (linux.E.init(rc)) {
2705 .SUCCESS => Stat.fromLinux(stx),
2706 .ACCES => error.AccessDenied,
2707 .BADF => unreachable,
2708 .FAULT => unreachable,
2709 .INVAL => unreachable,
2710 .LOOP => error.SymLinkLoop,
2711 .NAMETOOLONG => unreachable, // Handled by posix.toPosixPath() above.
2712 .NOENT, .NOTDIR => error.FileNotFound,
2713 .NOMEM => error.SystemResources,
2714 else => |err| posix.unexpectedErrno(err),
2715 };
2716 }
26932717 const st = try posix.fstatat(self.fd, sub_path, 0);
2694 return Stat.fromSystem(st);
2718 return Stat.fromPosix(st);
26952719}
26962720
26972721pub const ChmodError = File.ChmodError;
......@@ -2751,6 +2775,7 @@ const path = fs.path;
27512775const fs = std.fs;
27522776const Allocator = std.mem.Allocator;
27532777const assert = std.debug.assert;
2778const linux = std.os.linux;
27542779const windows = std.os.windows;
27552780const native_os = builtin.os.tag;
27562781const have_flock = @TypeOf(posix.system.flock) != void;
lib/std/fs/File.zig+75-10
......@@ -342,7 +342,7 @@ pub fn seekTo(self: File, offset: u64) SeekError!void {
342342 return posix.lseek_SET(self.handle, offset);
343343}
344344
345pub const GetSeekPosError = posix.SeekError || posix.FStatError;
345pub const GetSeekPosError = posix.SeekError || StatError;
346346
347347/// TODO: integrate with async I/O
348348pub fn getPos(self: File) GetSeekPosError!u64 {
......@@ -357,7 +357,7 @@ pub fn getEndPos(self: File) GetSeekPosError!u64 {
357357 return (try self.stat()).size;
358358}
359359
360pub const ModeError = posix.FStatError;
360pub const ModeError = StatError;
361361
362362/// TODO: integrate with async I/O
363363pub fn mode(self: File) ModeError!Mode {
......@@ -392,7 +392,7 @@ pub const Stat = struct {
392392 /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01.
393393 ctime: i128,
394394
395 pub fn fromSystem(st: posix.Stat) Stat {
395 pub fn fromPosix(st: posix.Stat) Stat {
396396 const atime = st.atime();
397397 const mtime = st.mtime();
398398 const ctime = st.ctime();
......@@ -426,6 +426,31 @@ pub const Stat = struct {
426426 };
427427 }
428428
429 pub fn fromLinux(stx: linux.Statx) Stat {
430 const atime = stx.atime;
431 const mtime = stx.mtime;
432 const ctime = stx.ctime;
433
434 return .{
435 .inode = stx.ino,
436 .size = stx.size,
437 .mode = stx.mode,
438 .kind = switch (stx.mode & linux.S.IFMT) {
439 linux.S.IFDIR => .directory,
440 linux.S.IFCHR => .character_device,
441 linux.S.IFBLK => .block_device,
442 linux.S.IFREG => .file,
443 linux.S.IFIFO => .named_pipe,
444 linux.S.IFLNK => .sym_link,
445 linux.S.IFSOCK => .unix_domain_socket,
446 else => .unknown,
447 },
448 .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec,
449 .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec,
450 .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec,
451 };
452 }
453
429454 pub fn fromWasi(st: std.os.wasi.filestat_t) Stat {
430455 return .{
431456 .inode = st.ino,
......@@ -502,8 +527,34 @@ pub fn stat(self: File) StatError!Stat {
502527 return Stat.fromWasi(st);
503528 }
504529
530 if (builtin.os.tag == .linux) {
531 var stx = std.mem.zeroes(linux.Statx);
532
533 const rc = linux.statx(
534 self.handle,
535 "",
536 linux.AT.EMPTY_PATH,
537 linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME,
538 &stx,
539 );
540
541 return switch (linux.E.init(rc)) {
542 .SUCCESS => Stat.fromLinux(stx),
543 .ACCES => unreachable,
544 .BADF => unreachable,
545 .FAULT => unreachable,
546 .INVAL => unreachable,
547 .LOOP => unreachable,
548 .NAMETOOLONG => unreachable,
549 .NOENT => unreachable,
550 .NOMEM => error.SystemResources,
551 .NOTDIR => unreachable,
552 else => |err| posix.unexpectedErrno(err),
553 };
554 }
555
505556 const st = try posix.fstat(self.handle);
506 return Stat.fromSystem(st);
557 return Stat.fromPosix(st);
507558}
508559
509560pub const ChmodError = posix.FChmodError;
......@@ -1009,16 +1060,29 @@ pub fn metadata(self: File) MetadataError!Metadata {
10091060 };
10101061 },
10111062 .linux => blk: {
1012 const l = std.os.linux;
1013 var stx = std.mem.zeroes(l.Statx);
1014 const rcx = l.statx(self.handle, "\x00", l.AT.EMPTY_PATH, l.STATX_TYPE |
1015 l.STATX_MODE | l.STATX_ATIME | l.STATX_MTIME | l.STATX_BTIME, &stx);
1016
1017 switch (posix.errno(rcx)) {
1063 var stx = std.mem.zeroes(linux.Statx);
1064
1065 // We are gathering information for Metadata, which is meant to contain all the
1066 // native OS information about the file, so use all known flags.
1067 const rc = linux.statx(
1068 self.handle,
1069 "",
1070 linux.AT.EMPTY_PATH,
1071 linux.STATX_BASIC_STATS | linux.STATX_BTIME,
1072 &stx,
1073 );
1074
1075 switch (posix.errno(rc)) {
10181076 .SUCCESS => {},
1077 .ACCES => unreachable,
10191078 .BADF => unreachable,
10201079 .FAULT => unreachable,
1080 .INVAL => unreachable,
1081 .LOOP => unreachable,
1082 .NAMETOOLONG => unreachable,
1083 .NOENT => unreachable,
10211084 .NOMEM => return error.SystemResources,
1085 .NOTDIR => unreachable,
10221086 else => |err| return posix.unexpectedErrno(err),
10231087 }
10241088
......@@ -1712,6 +1776,7 @@ const posix = std.posix;
17121776const io = std.io;
17131777const math = std.math;
17141778const assert = std.debug.assert;
1779const linux = std.os.linux;
17151780const windows = std.os.windows;
17161781const Os = std.builtin.Os;
17171782const maxInt = std.math.maxInt;