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) {...@@ -334,7 +334,6 @@ pub const Iterator = switch (native_os) {
334 first_iter: bool,334 first_iter: bool,
335335
336 const Self = @This();336 const Self = @This();
337 const linux = std.os.linux;
338337
339 pub const Error = IteratorError;338 pub const Error = IteratorError;
340339
...@@ -2690,8 +2689,33 @@ pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {...@@ -2690,8 +2689,33 @@ pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
2690 const st = try std.os.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true });2689 const st = try std.os.fstatat_wasi(self.fd, sub_path, .{ .SYMLINK_FOLLOW = true });
2691 return Stat.fromWasi(st);2690 return Stat.fromWasi(st);
2692 }2691 }
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 }
2693 const st = try posix.fstatat(self.fd, sub_path, 0);2717 const st = try posix.fstatat(self.fd, sub_path, 0);
2694 return Stat.fromSystem(st);2718 return Stat.fromPosix(st);
2695}2719}
26962720
2697pub const ChmodError = File.ChmodError;2721pub const ChmodError = File.ChmodError;
...@@ -2751,6 +2775,7 @@ const path = fs.path;...@@ -2751,6 +2775,7 @@ const path = fs.path;
2751const fs = std.fs;2775const fs = std.fs;
2752const Allocator = std.mem.Allocator;2776const Allocator = std.mem.Allocator;
2753const assert = std.debug.assert;2777const assert = std.debug.assert;
2778const linux = std.os.linux;
2754const windows = std.os.windows;2779const windows = std.os.windows;
2755const native_os = builtin.os.tag;2780const native_os = builtin.os.tag;
2756const have_flock = @TypeOf(posix.system.flock) != void;2781const 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 {...@@ -342,7 +342,7 @@ pub fn seekTo(self: File, offset: u64) SeekError!void {
342 return posix.lseek_SET(self.handle, offset);342 return posix.lseek_SET(self.handle, offset);
343}343}
344344
345pub const GetSeekPosError = posix.SeekError || posix.FStatError;345pub const GetSeekPosError = posix.SeekError || StatError;
346346
347/// TODO: integrate with async I/O347/// TODO: integrate with async I/O
348pub fn getPos(self: File) GetSeekPosError!u64 {348pub fn getPos(self: File) GetSeekPosError!u64 {
...@@ -357,7 +357,7 @@ pub fn getEndPos(self: File) GetSeekPosError!u64 {...@@ -357,7 +357,7 @@ pub fn getEndPos(self: File) GetSeekPosError!u64 {
357 return (try self.stat()).size;357 return (try self.stat()).size;
358}358}
359359
360pub const ModeError = posix.FStatError;360pub const ModeError = StatError;
361361
362/// TODO: integrate with async I/O362/// TODO: integrate with async I/O
363pub fn mode(self: File) ModeError!Mode {363pub fn mode(self: File) ModeError!Mode {
...@@ -392,7 +392,7 @@ pub const Stat = struct {...@@ -392,7 +392,7 @@ pub const Stat = struct {
392 /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01.392 /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01.
393 ctime: i128,393 ctime: i128,
394394
395 pub fn fromSystem(st: posix.Stat) Stat {395 pub fn fromPosix(st: posix.Stat) Stat {
396 const atime = st.atime();396 const atime = st.atime();
397 const mtime = st.mtime();397 const mtime = st.mtime();
398 const ctime = st.ctime();398 const ctime = st.ctime();
...@@ -426,6 +426,31 @@ pub const Stat = struct {...@@ -426,6 +426,31 @@ pub const Stat = struct {
426 };426 };
427 }427 }
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
429 pub fn fromWasi(st: std.os.wasi.filestat_t) Stat {454 pub fn fromWasi(st: std.os.wasi.filestat_t) Stat {
430 return .{455 return .{
431 .inode = st.ino,456 .inode = st.ino,
...@@ -502,8 +527,34 @@ pub fn stat(self: File) StatError!Stat {...@@ -502,8 +527,34 @@ pub fn stat(self: File) StatError!Stat {
502 return Stat.fromWasi(st);527 return Stat.fromWasi(st);
503 }528 }
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
505 const st = try posix.fstat(self.handle);556 const st = try posix.fstat(self.handle);
506 return Stat.fromSystem(st);557 return Stat.fromPosix(st);
507}558}
508559
509pub const ChmodError = posix.FChmodError;560pub const ChmodError = posix.FChmodError;
...@@ -1009,16 +1060,29 @@ pub fn metadata(self: File) MetadataError!Metadata {...@@ -1009,16 +1060,29 @@ pub fn metadata(self: File) MetadataError!Metadata {
1009 };1060 };
1010 },1061 },
1011 .linux => blk: {1062 .linux => blk: {
1012 const l = std.os.linux;1063 var stx = std.mem.zeroes(linux.Statx);
1013 var stx = std.mem.zeroes(l.Statx);1064
1014 const rcx = l.statx(self.handle, "\x00", l.AT.EMPTY_PATH, l.STATX_TYPE |1065 // We are gathering information for Metadata, which is meant to contain all the
1015 l.STATX_MODE | l.STATX_ATIME | l.STATX_MTIME | l.STATX_BTIME, &stx);1066 // native OS information about the file, so use all known flags.
10161067 const rc = linux.statx(
1017 switch (posix.errno(rcx)) {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)) {
1018 .SUCCESS => {},1076 .SUCCESS => {},
1077 .ACCES => unreachable,
1019 .BADF => unreachable,1078 .BADF => unreachable,
1020 .FAULT => unreachable,1079 .FAULT => unreachable,
1080 .INVAL => unreachable,
1081 .LOOP => unreachable,
1082 .NAMETOOLONG => unreachable,
1083 .NOENT => unreachable,
1021 .NOMEM => return error.SystemResources,1084 .NOMEM => return error.SystemResources,
1085 .NOTDIR => unreachable,
1022 else => |err| return posix.unexpectedErrno(err),1086 else => |err| return posix.unexpectedErrno(err),
1023 }1087 }
10241088
...@@ -1712,6 +1776,7 @@ const posix = std.posix;...@@ -1712,6 +1776,7 @@ const posix = std.posix;
1712const io = std.io;1776const io = std.io;
1713const math = std.math;1777const math = std.math;
1714const assert = std.debug.assert;1778const assert = std.debug.assert;
1779const linux = std.os.linux;
1715const windows = std.os.windows;1780const windows = std.os.windows;
1716const Os = std.builtin.Os;1781const Os = std.builtin.Os;
1717const maxInt = std.math.maxInt;1782const maxInt = std.math.maxInt;