Skip to content

Commit 1394554

Browse files
committed
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.
1 parent 4e5068c commit 1394554

File tree

2 files changed

+102
-12
lines changed

2 files changed

+102
-12
lines changed

lib/std/fs/Dir.zig

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

26972721
pub const ChmodError = File.ChmodError;
@@ -2751,6 +2775,7 @@ const path = fs.path;
27512775
const fs = std.fs;
27522776
const Allocator = std.mem.Allocator;
27532777
const assert = std.debug.assert;
2778+
const linux = std.os.linux;
27542779
const windows = std.os.windows;
27552780
const native_os = builtin.os.tag;
27562781
const have_flock = @TypeOf(posix.system.flock) != void;

lib/std/fs/File.zig

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ pub fn seekTo(self: File, offset: u64) SeekError!void {
342342
return posix.lseek_SET(self.handle, offset);
343343
}
344344

345-
pub const GetSeekPosError = posix.SeekError || posix.FStatError;
345+
pub const GetSeekPosError = posix.SeekError || StatError;
346346

347347
/// TODO: integrate with async I/O
348348
pub 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

360-
pub const ModeError = posix.FStatError;
360+
pub const ModeError = StatError;
361361

362362
/// TODO: integrate with async I/O
363363
pub 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

509560
pub 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;
17121776
const io = std.io;
17131777
const math = std.math;
17141778
const assert = std.debug.assert;
1779+
const linux = std.os.linux;
17151780
const windows = std.os.windows;
17161781
const Os = std.builtin.Os;
17171782
const maxInt = std.math.maxInt;

0 commit comments

Comments
 (0)