authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-09-09 09:55:09-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-12-14 14:26:02-07:00
loga62c8d36d530dc78f88dc0f41ce3f244c3c273bd
treefd92869eefe7bd99af8cf3fbcad1533bb5797ec8
parentf65cdef7c8bfe7a47e1c30cfa3903ca2d53495cf

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.

3 files changed, 51 insertions(+), 94 deletions(-)

lib/std/fs.zig+22-16
...@@ -2600,24 +2600,30 @@ pub const Dir = struct {...@@ -2600,24 +2600,30 @@ pub const Dir = struct {
26002600
2601 pub const StatFileError = File.OpenError || File.StatError || os.FStatAtError;2601 pub const StatFileError = File.OpenError || File.StatError || os.FStatAtError;
26022602
2603 /// Provides info on a file (File.Stat) for any file in the opened directory,2603 /// Returns metadata for a file inside the directory.
2604 /// with a single syscall (fstatat), except on Windows.2604 ///
2605 /// Currently on Windows, files are opened then closed (implying several syscalls, unfortunately).2605 /// On Windows, this requires three syscalls. On other operating systems, it
2606 /// Symlinks are not followed on linux, haiku, solaris and *BSDs.2606 /// only takes one.
2607 /// Other OSs have a default behavior (they currently lack an os.AT.SYMLINK_NOFOLLOW flag).2607 ///
2608 /// Symlinks are followed.
2609 ///
2610 /// `sub_path` may be absolute, in which case `self` is ignored.
2608 pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {2611 pub fn statFile(self: Dir, sub_path: []const u8) StatFileError!Stat {
2609 if (builtin.os.tag == .windows) {2612 switch (builtin.os.tag) {
2610 var file = try self.openFile(sub_path, .{});2613 .windows => {
2611 defer file.close();2614 var file = try self.openFile(sub_path, .{});
2612 return file.stat();2615 defer file.close();
2616 return file.stat();
2617 },
2618 .wasi => {
2619 const st = try os.fstatatWasi(self.fd, sub_path, os.wasi.LOOKUP_SYMLINK_FOLLOW);
2620 return Stat.fromSystem(st);
2621 },
2622 else => {
2623 const st = try os.fstatat(self.fd, sub_path, 0);
2624 return Stat.fromSystem(st);
2625 },
2613 }2626 }
2614
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 };
2619
2620 return Stat.fromSystemStat(try os.fstatat(self.fd, sub_path, flags));
2621 }2627 }
26222628
2623 const Permissions = File.Permissions;2629 const Permissions = File.Permissions;
lib/std/fs/file.zig+23-67
...@@ -294,14 +294,17 @@ pub const File = struct {...@@ -294,14 +294,17 @@ pub const File = struct {
294 }294 }
295295
296 pub const Stat = struct {296 pub const Stat = struct {
297 /// A number that the system uses to point to the file metadata. This number is not guaranteed to be297 /// A number that the system uses to point to the file metadata. This
298 /// unique across time, as some file systems may reuse an inode after its file has been deleted.298 /// number is not guaranteed to be unique across time, as some file
299 /// Some systems may change the inode of a file over time.299 /// systems may reuse an inode after its file has been deleted. Some
300 /// systems may change the inode of a file over time.
300 ///301 ///
301 /// On Linux, the inode is a structure that stores the metadata, and the inode _number_ is what302 /// On Linux, the inode is a structure that stores the metadata, and
302 /// you see here: the index number of the inode.303 /// the inode _number_ is what you see here: the index number of the
304 /// inode.
303 ///305 ///
304 /// The FileIndex on Windows is similar. It is a number for a file that is unique to each filesystem.306 /// The FileIndex on Windows is similar. It is a number for a file that
307 /// is unique to each filesystem.
305 inode: INode,308 inode: INode,
306 size: u64,309 size: u64,
307 mode: Mode,310 mode: Mode,
...@@ -314,18 +317,19 @@ pub const File = struct {...@@ -314,18 +317,19 @@ pub const File = struct {
314 /// Creation time in nanoseconds, relative to UTC 1970-01-01.317 /// Creation time in nanoseconds, relative to UTC 1970-01-01.
315 ctime: i128,318 ctime: i128,
316319
317 pub fn systemStatKindToFsKind(st: os.system.Stat) Kind {320 pub fn fromSystem(st: os.system.Stat) Stat {
318 const kind: File.Kind = if (builtin.os.tag == .wasi and !builtin.link_libc)321 const atime = st.atime();
319 switch (st.filetype) {322 const mtime = st.mtime();
320 .BLOCK_DEVICE => Kind.BlockDevice,323 const ctime = st.ctime();
321 .CHARACTER_DEVICE => Kind.CharacterDevice,324 const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) {
322 .DIRECTORY => Kind.Directory,325 .BLOCK_DEVICE => Kind.BlockDevice,
323 .SYMBOLIC_LINK => Kind.SymLink,326 .CHARACTER_DEVICE => Kind.CharacterDevice,
324 .REGULAR_FILE => Kind.File,327 .DIRECTORY => Kind.Directory,
325 .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket,328 .SYMBOLIC_LINK => Kind.SymLink,
326 else => Kind.Unknown,329 .REGULAR_FILE => Kind.File,
327 }330 .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket,
328 else blk: {331 else => Kind.Unknown,
332 } else blk: {
329 const m = st.mode & os.S.IFMT;333 const m = st.mode & os.S.IFMT;
330 switch (m) {334 switch (m) {
331 os.S.IFBLK => break :blk Kind.BlockDevice,335 os.S.IFBLK => break :blk Kind.BlockDevice,
...@@ -345,14 +349,6 @@ pub const File = struct {...@@ -345,14 +349,6 @@ pub const File = struct {
345349
346 break :blk .Unknown;350 break :blk .Unknown;
347 };351 };
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);
356352
357 return Stat{353 return Stat{
358 .inode = st.ino,354 .inode = st.ino,
...@@ -393,47 +389,7 @@ pub const File = struct {...@@ -393,47 +389,7 @@ pub const File = struct {
393 }389 }
394390
395 const st = try os.fstat(self.handle);391 const st = try os.fstat(self.handle);
396 const atime = st.atime();392 return Stat.fromSystem(st);
397 const mtime = st.mtime();
398 const ctime = st.ctime();
399 const kind: Kind = if (builtin.os.tag == .wasi and !builtin.link_libc) switch (st.filetype) {
400 .BLOCK_DEVICE => Kind.BlockDevice,
401 .CHARACTER_DEVICE => Kind.CharacterDevice,
402 .DIRECTORY => Kind.Directory,
403 .SYMBOLIC_LINK => Kind.SymLink,
404 .REGULAR_FILE => Kind.File,
405 .SOCKET_STREAM, .SOCKET_DGRAM => Kind.UnixDomainSocket,
406 else => Kind.Unknown,
407 } else blk: {
408 const m = st.mode & os.S.IFMT;
409 switch (m) {
410 os.S.IFBLK => break :blk Kind.BlockDevice,
411 os.S.IFCHR => break :blk Kind.CharacterDevice,
412 os.S.IFDIR => break :blk Kind.Directory,
413 os.S.IFIFO => break :blk Kind.NamedPipe,
414 os.S.IFLNK => break :blk Kind.SymLink,
415 os.S.IFREG => break :blk Kind.File,
416 os.S.IFSOCK => break :blk Kind.UnixDomainSocket,
417 else => {},
418 }
419 if (builtin.os.tag == .solaris) switch (m) {
420 os.S.IFDOOR => break :blk Kind.Door,
421 os.S.IFPORT => break :blk Kind.EventPort,
422 else => {},
423 };
424
425 break :blk .Unknown;
426 };
427
428 return Stat{
429 .inode = st.ino,
430 .size = @bitCast(u64, st.size),
431 .mode = st.mode,
432 .kind = kind,
433 .atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
434 .mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
435 .ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
436 };
437 }393 }
438394
439 pub const ChmodError = std.os.FChmodError;395 pub const ChmodError = std.os.FChmodError;
src/Module.zig+6-11
...@@ -4137,19 +4137,14 @@ pub fn populateBuiltinFile(mod: *Module) !void {...@@ -4137,19 +4137,14 @@ pub fn populateBuiltinFile(mod: *Module) !void {
4137 };4137 };
4138 }4138 }
4139 } else |err| switch (err) {4139 } else |err| switch (err) {
4140 error.BadPathName => unreachable, // it's always "builtin.zig"
4140 error.NameTooLong => unreachable, // it's always "builtin.zig"4141 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
4141 error.FileNotFound => try writeBuiltinFile(file, builtin_pkg),4145 error.FileNotFound => try writeBuiltinFile(file, builtin_pkg),
4142 else => |e| {4146
4143 if (builtin.os.tag == .windows) {4147 else => |e| return e,
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 },
4153 }4148 }
41544149
4155 file.tree = try std.zig.parse(gpa, file.source);4150 file.tree = try std.zig.parse(gpa, file.source);