Skip to content

Commit f65cdef

Browse files
KaneRootandrewrk
authored andcommitted
std.fs.Dir.statFile: use fstatat
This avoids extra syscalls.
1 parent 4af305b commit f65cdef

3 files changed

Lines changed: 80 additions & 12 deletions

File tree

lib/std/fs.zig

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2598,14 +2598,26 @@ pub const Dir = struct {
25982598
return file.stat();
25992599
}
26002600

2601-
pub const StatFileError = File.OpenError || StatError;
2601+
pub const StatFileError = File.OpenError || File.StatError || os.FStatAtError;
2602+
2603+
/// Provides info on a file (File.Stat) for any file in the opened directory,
2604+
/// with a single syscall (fstatat), except on Windows.
2605+
/// Currently on Windows, files are opened then closed (implying several syscalls, unfortunately).
2606+
/// Symlinks are not followed on linux, haiku, solaris and *BSDs.
2607+
/// Other OSs have a default behavior (they currently lack an os.AT.SYMLINK_NOFOLLOW flag).
2608+
pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
2609+
if (builtin.os.tag == .windows) {
2610+
var file = try self.openFile(sub_path, .{});
2611+
defer file.close();
2612+
return file.stat();
2613+
}
26022614

2603-
// TODO: improve this to use the fstatat syscall instead of making 2 syscalls here.
2604-
pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!File.Stat {
2605-
var file = try self.openFile(sub_path, .{});
2606-
defer file.close();
2615+
const flags = switch (builtin.os.tag) {
2616+
.linux, .haiku, .solaris, .freebsd, .netbsd, .dragonfly, .openbsd => os.AT.SYMLINK_NOFOLLOW,
2617+
else => 0, // TODO: correct flags not yet implemented
2618+
};
26072619

2608-
return file.stat();
2620+
return Stat.fromSystemStat(try os.fstatat(self.fd, sub_path, flags));
26092621
}
26102622

26112623
const Permissions = File.Permissions;

lib/std/fs/file.zig

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,57 @@ pub const File = struct {
313313
mtime: i128,
314314
/// Creation time in nanoseconds, relative to UTC 1970-01-01.
315315
ctime: i128,
316+
317+
pub fn systemStatKindToFsKind(st: os.system.Stat) Kind {
318+
const kind: File.Kind = if (builtin.os.tag == .wasi and !builtin.link_libc)
319+
switch (st.filetype) {
320+
.BLOCK_DEVICE => Kind.BlockDevice,
321+
.CHARACTER_DEVICE => Kind.CharacterDevice,
322+
.DIRECTORY => Kind.Directory,
323+
.SYMBOLIC_LINK => Kind.SymLink,
324+
.REGULAR_FILE => Kind.File,
325+
.SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket,
326+
else => Kind.Unknown,
327+
}
328+
else blk: {
329+
const m = st.mode & os.S.IFMT;
330+
switch (m) {
331+
os.S.IFBLK => break :blk Kind.BlockDevice,
332+
os.S.IFCHR => break :blk Kind.CharacterDevice,
333+
os.S.IFDIR => break :blk Kind.Directory,
334+
os.S.IFIFO => break :blk Kind.NamedPipe,
335+
os.S.IFLNK => break :blk Kind.SymLink,
336+
os.S.IFREG => break :blk Kind.File,
337+
os.S.IFSOCK => break :blk Kind.UnixDomainSocket,
338+
else => {},
339+
}
340+
if (builtin.os.tag == .solaris) switch (m) {
341+
os.S.IFDOOR => break :blk Kind.Door,
342+
os.S.IFPORT => break :blk Kind.EventPort,
343+
else => {},
344+
};
345+
346+
break :blk .Unknown;
347+
};
348+
return kind;
349+
}
350+
351+
pub fn fromSystemStat(st: os.system.Stat) File.StatError!Stat {
352+
const atime = st.atime();
353+
const mtime = st.mtime();
354+
const ctime = st.ctime();
355+
const kind = systemStatKindToFsKind(st);
356+
357+
return Stat{
358+
.inode = st.ino,
359+
.size = @bitCast(u64, st.size),
360+
.mode = st.mode,
361+
.kind = kind,
362+
.atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
363+
.mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
364+
.ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
365+
};
366+
}
316367
};
317368

318369
pub const StatError = os.FStatError;

src/Module.zig

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4137,14 +4137,19 @@ pub fn populateBuiltinFile(mod: *Module) !void {
41374137
};
41384138
}
41394139
} else |err| switch (err) {
4140-
error.BadPathName => unreachable, // it's always "builtin.zig"
41414140
error.NameTooLong => unreachable, // it's always "builtin.zig"
4142-
error.PipeBusy => unreachable, // it's not a pipe
4143-
error.WouldBlock => unreachable, // not asking for non-blocking I/O
4144-
41454141
error.FileNotFound => try writeBuiltinFile(file, builtin_pkg),
4146-
4147-
else => |e| return e,
4142+
else => |e| {
4143+
if (builtin.os.tag == .windows) {
4144+
switch (e) {
4145+
error.BadPathName => unreachable, // it's always "builtin.zig"
4146+
error.PipeBusy => unreachable, // it's not a pipe
4147+
error.WouldBlock => unreachable, // not asking for non-blocking I/O
4148+
else => return e,
4149+
}
4150+
}
4151+
return e;
4152+
},
41484153
}
41494154

41504155
file.tree = try std.zig.parse(gpa, file.source);

0 commit comments

Comments
 (0)