Commit a62c8d36d5

Andrew Kelley <andrew@ziglang.org>
2022-09-09 18:55:09
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.
1 parent f65cdef
Changed files (3)
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;
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;
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);