From f65cdef7c8bfe7a47e1c30cfa3903ca2d53495cf Mon Sep 17 00:00:00 2001 From: Philippe Pittoli Date: Fri, 6 May 2022 01:03:03 +0200 Subject: std.fs.Dir.statFile: use fstatat This avoids extra syscalls. --- lib/std/fs/file.zig | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) (limited to 'lib/std/fs') diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index a1e81c9b94..4dd40fe0a2 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -313,6 +313,57 @@ pub const File = struct { mtime: i128, /// Creation time in nanoseconds, relative to UTC 1970-01-01. ctime: i128, + + pub fn systemStatKindToFsKind(st: os.system.Stat) Kind { + const kind: File.Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) + switch (st.filetype) { + .BLOCK_DEVICE => Kind.BlockDevice, + .CHARACTER_DEVICE => Kind.CharacterDevice, + .DIRECTORY => Kind.Directory, + .SYMBOLIC_LINK => Kind.SymLink, + .REGULAR_FILE => Kind.File, + .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket, + else => Kind.Unknown, + } + else blk: { + const m = st.mode & os.S.IFMT; + switch (m) { + os.S.IFBLK => break :blk Kind.BlockDevice, + os.S.IFCHR => break :blk Kind.CharacterDevice, + os.S.IFDIR => break :blk Kind.Directory, + os.S.IFIFO => break :blk Kind.NamedPipe, + os.S.IFLNK => break :blk Kind.SymLink, + os.S.IFREG => break :blk Kind.File, + os.S.IFSOCK => break :blk Kind.UnixDomainSocket, + else => {}, + } + if (builtin.os.tag == .solaris) switch (m) { + os.S.IFDOOR => break :blk Kind.Door, + os.S.IFPORT => break :blk Kind.EventPort, + else => {}, + }; + + break :blk .Unknown; + }; + return kind; + } + + pub fn fromSystemStat(st: os.system.Stat) File.StatError!Stat { + const atime = st.atime(); + const mtime = st.mtime(); + const ctime = st.ctime(); + const kind = systemStatKindToFsKind(st); + + return Stat{ + .inode = st.ino, + .size = @bitCast(u64, st.size), + .mode = st.mode, + .kind = kind, + .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec, + .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec, + .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec, + }; + } }; pub const StatError = os.FStatError; -- cgit v1.2.3 From a62c8d36d530dc78f88dc0f41ce3f244c3c273bd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 9 Sep 2022 09:55:09 -0700 Subject: std.fs.Dir.statFile rework * revert changes to Module because the error set is consistent across operating systems. * remove duplicated Stat.fromSystem code and use a less redundant name. * make fs.Dir.statFile follow symlinks, and avoid pointless control flow through the posix layer. --- lib/std/fs.zig | 38 ++++++++++++---------- lib/std/fs/file.zig | 90 ++++++++++++++--------------------------------------- src/Module.zig | 17 ++++------ 3 files changed, 51 insertions(+), 94 deletions(-) (limited to 'lib/std/fs') diff --git a/lib/std/fs.zig b/lib/std/fs.zig index f47f06a2d9..31f118c190 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -2600,24 +2600,30 @@ pub const Dir = struct { pub const StatFileError = File.OpenError || File.StatError || os.FStatAtError; - /// Provides info on a file (File.Stat) for any file in the opened directory, - /// with a single syscall (fstatat), except on Windows. - /// Currently on Windows, files are opened then closed (implying several syscalls, unfortunately). - /// Symlinks are not followed on linux, haiku, solaris and *BSDs. - /// Other OSs have a default behavior (they currently lack an os.AT.SYMLINK_NOFOLLOW flag). + /// Returns metadata for a file inside the directory. + /// + /// On Windows, this requires three syscalls. On other operating systems, it + /// only takes one. + /// + /// Symlinks are followed. + /// + /// `sub_path` may be absolute, in which case `self` is ignored. pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat { - if (builtin.os.tag == .windows) { - var file = try self.openFile(sub_path, .{}); - defer file.close(); - return file.stat(); + switch (builtin.os.tag) { + .windows => { + var file = try self.openFile(sub_path, .{}); + defer file.close(); + return file.stat(); + }, + .wasi => { + const st = try os.fstatatWasi(self.fd, sub_path, os.wasi.LOOKUP_SYMLINK_FOLLOW); + return Stat.fromSystem(st); + }, + else => { + const st = try os.fstatat(self.fd, sub_path, 0); + return Stat.fromSystem(st); + }, } - - const flags = switch (builtin.os.tag) { - .linux, .haiku, .solaris, .freebsd, .netbsd, .dragonfly, .openbsd => os.AT.SYMLINK_NOFOLLOW, - else => 0, // TODO: correct flags not yet implemented - }; - - return Stat.fromSystemStat(try os.fstatat(self.fd, sub_path, flags)); } const Permissions = File.Permissions; diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 4dd40fe0a2..cb048adb30 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -294,14 +294,17 @@ pub const File = struct { } pub const Stat = struct { - /// A number that the system uses to point to the file metadata. This number is not guaranteed to be - /// unique across time, as some file systems may reuse an inode after its file has been deleted. - /// Some systems may change the inode of a file over time. + /// A number that the system uses to point to the file metadata. This + /// number is not guaranteed to be unique across time, as some file + /// systems may reuse an inode after its file has been deleted. Some + /// systems may change the inode of a file over time. /// - /// On Linux, the inode is a structure that stores the metadata, and the inode _number_ is what - /// you see here: the index number of the inode. + /// On Linux, the inode is a structure that stores the metadata, and + /// the inode _number_ is what you see here: the index number of the + /// inode. /// - /// The FileIndex on Windows is similar. It is a number for a file that is unique to each filesystem. + /// The FileIndex on Windows is similar. It is a number for a file that + /// is unique to each filesystem. inode: INode, size: u64, mode: Mode, @@ -314,18 +317,19 @@ pub const File = struct { /// Creation time in nanoseconds, relative to UTC 1970-01-01. ctime: i128, - pub fn systemStatKindToFsKind(st: os.system.Stat) Kind { - const kind: File.Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) - switch (st.filetype) { - .BLOCK_DEVICE => Kind.BlockDevice, - .CHARACTER_DEVICE => Kind.CharacterDevice, - .DIRECTORY => Kind.Directory, - .SYMBOLIC_LINK => Kind.SymLink, - .REGULAR_FILE => Kind.File, - .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket, - else => Kind.Unknown, - } - else blk: { + pub fn fromSystem(st: os.system.Stat) Stat { + const atime = st.atime(); + const mtime = st.mtime(); + const ctime = st.ctime(); + const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) { + .BLOCK_DEVICE => Kind.BlockDevice, + .CHARACTER_DEVICE => Kind.CharacterDevice, + .DIRECTORY => Kind.Directory, + .SYMBOLIC_LINK => Kind.SymLink, + .REGULAR_FILE => Kind.File, + .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket, + else => Kind.Unknown, + } else blk: { const m = st.mode & os.S.IFMT; switch (m) { os.S.IFBLK => break :blk Kind.BlockDevice, @@ -345,14 +349,6 @@ pub const File = struct { break :blk .Unknown; }; - return kind; - } - - pub fn fromSystemStat(st: os.system.Stat) File.StatError!Stat { - const atime = st.atime(); - const mtime = st.mtime(); - const ctime = st.ctime(); - const kind = systemStatKindToFsKind(st); return Stat{ .inode = st.ino, @@ -393,47 +389,7 @@ pub const File = struct { } const st = try os.fstat(self.handle); - const atime = st.atime(); - const mtime = st.mtime(); - const ctime = st.ctime(); - const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) { - .BLOCK_DEVICE => Kind.BlockDevice, - .CHARACTER_DEVICE => Kind.CharacterDevice, - .DIRECTORY => Kind.Directory, - .SYMBOLIC_LINK => Kind.SymLink, - .REGULAR_FILE => Kind.File, - .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket, - else => Kind.Unknown, - } else blk: { - const m = st.mode & os.S.IFMT; - switch (m) { - os.S.IFBLK => break :blk Kind.BlockDevice, - os.S.IFCHR => break :blk Kind.CharacterDevice, - os.S.IFDIR => break :blk Kind.Directory, - os.S.IFIFO => break :blk Kind.NamedPipe, - os.S.IFLNK => break :blk Kind.SymLink, - os.S.IFREG => break :blk Kind.File, - os.S.IFSOCK => break :blk Kind.UnixDomainSocket, - else => {}, - } - if (builtin.os.tag == .solaris) switch (m) { - os.S.IFDOOR => break :blk Kind.Door, - os.S.IFPORT => break :blk Kind.EventPort, - else => {}, - }; - - break :blk .Unknown; - }; - - return Stat{ - .inode = st.ino, - .size = @bitCast(u64, st.size), - .mode = st.mode, - .kind = kind, - .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec, - .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec, - .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec, - }; + return Stat.fromSystem(st); } pub const ChmodError = std.os.FChmodError; diff --git a/src/Module.zig b/src/Module.zig index d4af06f896..83294f3068 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -4137,19 +4137,14 @@ pub fn populateBuiltinFile(mod: *Module) !void { }; } } else |err| switch (err) { + error.BadPathName => unreachable, // it's always "builtin.zig" error.NameTooLong => unreachable, // it's always "builtin.zig" + error.PipeBusy => unreachable, // it's not a pipe + error.WouldBlock => unreachable, // not asking for non-blocking I/O + error.FileNotFound => try writeBuiltinFile(file, builtin_pkg), - else => |e| { - if (builtin.os.tag == .windows) { - switch (e) { - error.BadPathName => unreachable, // it's always "builtin.zig" - error.PipeBusy => unreachable, // it's not a pipe - error.WouldBlock => unreachable, // not asking for non-blocking I/O - else => return e, - } - } - return e; - }, + + else => |e| return e, } file.tree = try std.zig.parse(gpa, file.source); -- cgit v1.2.3