authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-08 20:35:34-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-10-29 06:20:48-07:00
log89412fda775aecdedf4047355f2c45b48334a285
tree1aa3818600ec8bdead689f0a60bf507b4c259370
parent69b54b0cd12fb90f8ff101bc66e433a81d4b8409

std.Io: implement fileStat


7 files changed, 202 insertions(+), 156 deletions(-)

lib/std/Io/File.zig+11-83
......@@ -47,90 +47,14 @@ pub const Stat = struct {
4747 kind: Kind,
4848
4949 /// Last access time in nanoseconds, relative to UTC 1970-01-01.
50 /// TODO change this to Io.Timestamp except don't waste storage on clock
5051 atime: i128,
5152 /// Last modification time in nanoseconds, relative to UTC 1970-01-01.
53 /// TODO change this to Io.Timestamp except don't waste storage on clock
5254 mtime: i128,
5355 /// Last status/metadata change time in nanoseconds, relative to UTC 1970-01-01.
56 /// TODO change this to Io.Timestamp except don't waste storage on clock
5457 ctime: i128,
55
56 pub fn fromPosix(st: std.posix.Stat) Stat {
57 const atime = st.atime();
58 const mtime = st.mtime();
59 const ctime = st.ctime();
60 return .{
61 .inode = st.ino,
62 .size = @bitCast(st.size),
63 .mode = st.mode,
64 .kind = k: {
65 const m = st.mode & std.posix.S.IFMT;
66 switch (m) {
67 std.posix.S.IFBLK => break :k .block_device,
68 std.posix.S.IFCHR => break :k .character_device,
69 std.posix.S.IFDIR => break :k .directory,
70 std.posix.S.IFIFO => break :k .named_pipe,
71 std.posix.S.IFLNK => break :k .sym_link,
72 std.posix.S.IFREG => break :k .file,
73 std.posix.S.IFSOCK => break :k .unix_domain_socket,
74 else => {},
75 }
76 if (builtin.os.tag == .illumos) switch (m) {
77 std.posix.S.IFDOOR => break :k .door,
78 std.posix.S.IFPORT => break :k .event_port,
79 else => {},
80 };
81
82 break :k .unknown;
83 },
84 .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec,
85 .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec,
86 .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec,
87 };
88 }
89
90 pub fn fromLinux(stx: std.os.linux.Statx) Stat {
91 const atime = stx.atime;
92 const mtime = stx.mtime;
93 const ctime = stx.ctime;
94
95 return .{
96 .inode = stx.ino,
97 .size = stx.size,
98 .mode = stx.mode,
99 .kind = switch (stx.mode & std.os.linux.S.IFMT) {
100 std.os.linux.S.IFDIR => .directory,
101 std.os.linux.S.IFCHR => .character_device,
102 std.os.linux.S.IFBLK => .block_device,
103 std.os.linux.S.IFREG => .file,
104 std.os.linux.S.IFIFO => .named_pipe,
105 std.os.linux.S.IFLNK => .sym_link,
106 std.os.linux.S.IFSOCK => .unix_domain_socket,
107 else => .unknown,
108 },
109 .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec,
110 .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec,
111 .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec,
112 };
113 }
114
115 pub fn fromWasi(st: std.os.wasi.filestat_t) Stat {
116 return .{
117 .inode = st.ino,
118 .size = @bitCast(st.size),
119 .mode = 0,
120 .kind = switch (st.filetype) {
121 .BLOCK_DEVICE => .block_device,
122 .CHARACTER_DEVICE => .character_device,
123 .DIRECTORY => .directory,
124 .SYMBOLIC_LINK => .sym_link,
125 .REGULAR_FILE => .file,
126 .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket,
127 else => .unknown,
128 },
129 .atime = st.atim,
130 .mtime = st.mtim,
131 .ctime = st.ctim,
132 };
133 }
13458};
13559
13660pub fn stdout() File {
......@@ -145,13 +69,17 @@ pub fn stdin() File {
14569 return .{ .handle = if (is_windows) std.os.windows.peb().ProcessParameters.hStdInput else std.posix.STDIN_FILENO };
14670}
14771
148pub const StatError = std.posix.FStatError || Io.Cancelable;
72pub const StatError = error{
73 SystemResources,
74 /// In WASI, this error may occur when the file descriptor does
75 /// not hold the required rights to get its filestat information.
76 AccessDenied,
77 PermissionDenied,
78} || Io.Cancelable || Io.UnexpectedError;
14979
15080/// Returns `Stat` containing basic information about the `File`.
15181pub fn stat(file: File, io: Io) StatError!Stat {
152 _ = file;
153 _ = io;
154 @panic("TODO");
82 return io.vtable.fileStat(io.userdata, file);
15583}
15684
15785pub const OpenFlags = std.fs.File.OpenFlags;
lib/std/Io/Threaded.zig+152-3
......@@ -163,7 +163,12 @@ pub fn io(pool: *Pool) Io {
163163 .dirMake = dirMake,
164164 .dirStat = dirStat,
165165 .dirStatPath = dirStatPath,
166 .fileStat = fileStat,
166 .fileStat = switch (builtin.os.tag) {
167 .linux => fileStatLinux,
168 .windows => fileStatWindows,
169 .wasi => fileStatWasi,
170 else => fileStatPosix,
171 },
167172 .createFile = createFile,
168173 .fileOpen = fileOpen,
169174 .fileClose = fileClose,
......@@ -781,14 +786,80 @@ fn dirStatPath(userdata: ?*anyopaque, dir: Io.Dir, sub_path: []const u8) Io.Dir.
781786 @panic("TODO");
782787}
783788
784fn fileStat(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
789fn fileStatPosix(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
785790 const pool: *Pool = @ptrCast(@alignCast(userdata));
786 try pool.checkCancel();
791 const fstat_sym = if (posix.lfs64_abi) posix.system.fstat64 else posix.system.fstat;
792 while (true) {
793 try pool.checkCancel();
794 var stat = std.mem.zeroes(posix.Stat);
795 switch (posix.errno(fstat_sym(file.handle, &stat))) {
796 .SUCCESS => return statFromPosix(&stat),
797 .INTR => continue,
798 .INVAL => |err| return errnoBug(err),
799 .BADF => |err| return errnoBug(err),
800 .NOMEM => return error.SystemResources,
801 .ACCES => return error.AccessDenied,
802 else => |err| return posix.unexpectedErrno(err),
803 }
804 }
805}
806
807fn fileStatLinux(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
808 const pool: *Pool = @ptrCast(@alignCast(userdata));
809 const linux = std.os.linux;
810 while (true) {
811 try pool.checkCancel();
812 var statx = std.mem.zeroes(linux.Statx);
813 const rc = linux.statx(
814 file.handle,
815 "",
816 linux.AT.EMPTY_PATH,
817 linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME,
818 &statx,
819 );
820 switch (linux.E.init(rc)) {
821 .SUCCESS => return statFromLinux(&statx),
822 .INTR => continue,
823 .ACCES => |err| return errnoBug(err),
824 .BADF => |err| return errnoBug(err),
825 .FAULT => |err| return errnoBug(err),
826 .INVAL => |err| return errnoBug(err),
827 .LOOP => |err| return errnoBug(err),
828 .NAMETOOLONG => |err| return errnoBug(err),
829 .NOENT => |err| return errnoBug(err),
830 .NOMEM => return error.SystemResources,
831 .NOTDIR => |err| return errnoBug(err),
832 else => |err| return posix.unexpectedErrno(err),
833 }
834 }
835}
787836
837fn fileStatWindows(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
838 const pool: *Pool = @ptrCast(@alignCast(userdata));
839 try pool.checkCancel();
788840 _ = file;
789841 @panic("TODO");
790842}
791843
844fn fileStatWasi(userdata: ?*anyopaque, file: Io.File) Io.File.StatError!Io.File.Stat {
845 if (builtin.link_libc) return fileStatPosix(userdata, file);
846 const pool: *Pool = @ptrCast(@alignCast(userdata));
847 while (true) {
848 try pool.checkCancel();
849 var stat: std.os.wasi.filestat_t = undefined;
850 switch (std.os.wasi.fd_filestat_get(file.handle, &stat)) {
851 .SUCCESS => return statFromWasi(&stat),
852 .INTR => continue,
853 .INVAL => |err| return errnoBug(err),
854 .BADF => |err| return errnoBug(err),
855 .NOMEM => return error.SystemResources,
856 .ACCES => return error.AccessDenied,
857 .NOTCAPABLE => return error.AccessDenied,
858 else => |err| return posix.unexpectedErrno(err),
859 }
860 }
861}
862
792863fn createFile(
793864 userdata: ?*anyopaque,
794865 dir: Io.Dir,
......@@ -2114,3 +2185,81 @@ fn clockToWasi(clock: Io.Timestamp.Clock) std.os.wasi.clockid_t {
21142185 .cpu_thread => .THREAD_CPUTIME_ID,
21152186 };
21162187}
2188
2189fn statFromLinux(stx: *const std.os.linux.Statx) Io.File.Stat {
2190 const atime = stx.atime;
2191 const mtime = stx.mtime;
2192 const ctime = stx.ctime;
2193 return .{
2194 .inode = stx.ino,
2195 .size = stx.size,
2196 .mode = stx.mode,
2197 .kind = switch (stx.mode & std.os.linux.S.IFMT) {
2198 std.os.linux.S.IFDIR => .directory,
2199 std.os.linux.S.IFCHR => .character_device,
2200 std.os.linux.S.IFBLK => .block_device,
2201 std.os.linux.S.IFREG => .file,
2202 std.os.linux.S.IFIFO => .named_pipe,
2203 std.os.linux.S.IFLNK => .sym_link,
2204 std.os.linux.S.IFSOCK => .unix_domain_socket,
2205 else => .unknown,
2206 },
2207 .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec,
2208 .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec,
2209 .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec,
2210 };
2211}
2212
2213fn statFromPosix(st: *const std.posix.Stat) Io.File.Stat {
2214 const atime = st.atime();
2215 const mtime = st.mtime();
2216 const ctime = st.ctime();
2217 return .{
2218 .inode = st.ino,
2219 .size = @bitCast(st.size),
2220 .mode = st.mode,
2221 .kind = k: {
2222 const m = st.mode & std.posix.S.IFMT;
2223 switch (m) {
2224 std.posix.S.IFBLK => break :k .block_device,
2225 std.posix.S.IFCHR => break :k .character_device,
2226 std.posix.S.IFDIR => break :k .directory,
2227 std.posix.S.IFIFO => break :k .named_pipe,
2228 std.posix.S.IFLNK => break :k .sym_link,
2229 std.posix.S.IFREG => break :k .file,
2230 std.posix.S.IFSOCK => break :k .unix_domain_socket,
2231 else => {},
2232 }
2233 if (builtin.os.tag == .illumos) switch (m) {
2234 std.posix.S.IFDOOR => break :k .door,
2235 std.posix.S.IFPORT => break :k .event_port,
2236 else => {},
2237 };
2238
2239 break :k .unknown;
2240 },
2241 .atime = @as(i128, atime.sec) * std.time.ns_per_s + atime.nsec,
2242 .mtime = @as(i128, mtime.sec) * std.time.ns_per_s + mtime.nsec,
2243 .ctime = @as(i128, ctime.sec) * std.time.ns_per_s + ctime.nsec,
2244 };
2245}
2246
2247fn statFromWasi(st: *const std.os.wasi.filestat_t) Io.File.Stat {
2248 return .{
2249 .inode = st.ino,
2250 .size = @bitCast(st.size),
2251 .mode = 0,
2252 .kind = switch (st.filetype) {
2253 .BLOCK_DEVICE => .block_device,
2254 .CHARACTER_DEVICE => .character_device,
2255 .DIRECTORY => .directory,
2256 .SYMBOLIC_LINK => .sym_link,
2257 .REGULAR_FILE => .file,
2258 .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket,
2259 else => .unknown,
2260 },
2261 .atime = st.atim,
2262 .mtime = st.mtim,
2263 .ctime = st.ctim,
2264 };
2265}
lib/std/debug.zig+3-1
......@@ -82,6 +82,7 @@ pub const SelfInfoError = error{
8282 /// The required debug info could not be read from disk due to some IO error.
8383 ReadFailed,
8484 OutOfMemory,
85 Canceled,
8586 Unexpected,
8687};
8788
......@@ -691,6 +692,7 @@ pub noinline fn writeCurrentStackTrace(options: StackUnwindOptions, writer: *Wri
691692 error.UnsupportedDebugInfo => "unwind info unsupported",
692693 error.ReadFailed => "filesystem error",
693694 error.OutOfMemory => "out of memory",
695 error.Canceled => "operation canceled",
694696 error.Unexpected => "unexpected error",
695697 };
696698 if (it.stratOk(options.allow_unsafe_unwind)) {
......@@ -1079,7 +1081,7 @@ fn printSourceAtAddress(gpa: Allocator, debug_info: *SelfInfo, writer: *Writer,
10791081 error.UnsupportedDebugInfo,
10801082 error.InvalidDebugInfo,
10811083 => .unknown,
1082 error.ReadFailed, error.Unexpected => s: {
1084 error.ReadFailed, error.Unexpected, error.Canceled => s: {
10831085 tty_config.setColor(writer, .dim) catch {};
10841086 try writer.print("Failed to read debug info from filesystem, trace may be incomplete\n\n", .{});
10851087 tty_config.setColor(writer, .reset) catch {};
lib/std/debug/ElfFile.zig+2-1
......@@ -108,6 +108,7 @@ pub const LoadError = error{
108108 LockedMemoryLimitExceeded,
109109 ProcessFdQuotaExceeded,
110110 SystemFdQuotaExceeded,
111 Canceled,
111112 Unexpected,
112113};
113114
......@@ -408,7 +409,7 @@ fn loadInner(
408409 arena: Allocator,
409410 elf_file: std.fs.File,
410411 opt_crc: ?u32,
411) (LoadError || error{CrcMismatch})!LoadInnerResult {
412) (LoadError || error{ CrcMismatch, Canceled })!LoadInnerResult {
412413 const mapped_mem: []align(std.heap.page_size_min) const u8 = mapped: {
413414 const file_len = std.math.cast(
414415 usize,
lib/std/debug/SelfInfo/Elf.zig+1
......@@ -336,6 +336,7 @@ const Module = struct {
336336 var elf_file = load_result catch |err| switch (err) {
337337 error.OutOfMemory,
338338 error.Unexpected,
339 error.Canceled,
339340 => |e| return e,
340341
341342 error.Overflow,
lib/std/fs/File.zig+32-60
......@@ -1,10 +1,12 @@
1const File = @This();
2
13const builtin = @import("builtin");
2const Os = std.builtin.Os;
34const native_os = builtin.os.tag;
45const is_windows = native_os == .windows;
56
6const File = @This();
77const std = @import("../std.zig");
8const Io = std.Io;
9const Os = std.builtin.Os;
810const Allocator = std.mem.Allocator;
911const posix = std.posix;
1012const math = std.math;
......@@ -17,12 +19,12 @@ const Alignment = std.mem.Alignment;
1719/// The OS-specific file descriptor or file handle.
1820handle: Handle,
1921
20pub const Handle = std.Io.File.Handle;
21pub const Mode = std.Io.File.Mode;
22pub const INode = std.Io.File.INode;
22pub const Handle = Io.File.Handle;
23pub const Mode = Io.File.Mode;
24pub const INode = Io.File.INode;
2325pub const Uid = posix.uid_t;
2426pub const Gid = posix.gid_t;
25pub const Kind = std.Io.File.Kind;
27pub const Kind = Io.File.Kind;
2628
2729/// This is the default mode given to POSIX operating systems for creating
2830/// files. `0o666` is "-rw-rw-rw-" which is counter-intuitive at first,
......@@ -386,7 +388,7 @@ pub fn mode(self: File) ModeError!Mode {
386388 return (try self.stat()).mode;
387389}
388390
389pub const Stat = std.Io.File.Stat;
391pub const Stat = Io.File.Stat;
390392
391393pub const StatError = posix.FStatError;
392394
......@@ -436,39 +438,9 @@ pub fn stat(self: File) StatError!Stat {
436438 };
437439 }
438440
439 if (builtin.os.tag == .wasi and !builtin.link_libc) {
440 const st = try std.os.fstat_wasi(self.handle);
441 return Stat.fromWasi(st);
442 }
443
444 if (builtin.os.tag == .linux) {
445 var stx = std.mem.zeroes(linux.Statx);
446
447 const rc = linux.statx(
448 self.handle,
449 "",
450 linux.AT.EMPTY_PATH,
451 linux.STATX_TYPE | linux.STATX_MODE | linux.STATX_ATIME | linux.STATX_MTIME | linux.STATX_CTIME,
452 &stx,
453 );
454
455 return switch (linux.E.init(rc)) {
456 .SUCCESS => Stat.fromLinux(stx),
457 .ACCES => unreachable,
458 .BADF => unreachable,
459 .FAULT => unreachable,
460 .INVAL => unreachable,
461 .LOOP => unreachable,
462 .NAMETOOLONG => unreachable,
463 .NOENT => unreachable,
464 .NOMEM => error.SystemResources,
465 .NOTDIR => unreachable,
466 else => |err| posix.unexpectedErrno(err),
467 };
468 }
469
470 const st = try posix.fstat(self.handle);
471 return Stat.fromPosix(st);
441 var threaded: Io.Threaded = .init_single_threaded;
442 const io = threaded.io();
443 return Io.File.stat(.{ .handle = self.handle }, io);
472444}
473445
474446pub const ChmodError = posix.FChmodError;
......@@ -785,8 +757,8 @@ pub fn pwritev(self: File, iovecs: []posix.iovec_const, offset: u64) PWriteError
785757 return posix.pwritev(self.handle, iovecs, offset);
786758}
787759
788/// Deprecated in favor of `std.Io.File.Reader`.
789pub const Reader = std.Io.File.Reader;
760/// Deprecated in favor of `Io.File.Reader`.
761pub const Reader = Io.File.Reader;
790762
791763pub const Writer = struct {
792764 file: File,
......@@ -799,7 +771,7 @@ pub const Writer = struct {
799771 copy_file_range_err: ?CopyFileRangeError = null,
800772 fcopyfile_err: ?FcopyfileError = null,
801773 seek_err: ?Writer.SeekError = null,
802 interface: std.Io.Writer,
774 interface: Io.Writer,
803775
804776 pub const Mode = Reader.Mode;
805777
......@@ -845,13 +817,13 @@ pub const Writer = struct {
845817 };
846818 }
847819
848 pub fn initInterface(buffer: []u8) std.Io.Writer {
820 pub fn initInterface(buffer: []u8) Io.Writer {
849821 return .{
850822 .vtable = &.{
851823 .drain = drain,
852824 .sendFile = switch (builtin.zig_backend) {
853825 else => sendFile,
854 .stage2_aarch64 => std.Io.Writer.unimplementedSendFile,
826 .stage2_aarch64 => Io.Writer.unimplementedSendFile,
855827 },
856828 },
857829 .buffer = buffer,
......@@ -859,7 +831,7 @@ pub const Writer = struct {
859831 }
860832
861833 /// TODO when this logic moves from fs.File to Io.File the io parameter should be deleted
862 pub fn moveToReader(w: *Writer, io: std.Io) Reader {
834 pub fn moveToReader(w: *Writer, io: Io) Reader {
863835 defer w.* = undefined;
864836 return .{
865837 .io = io,
......@@ -871,7 +843,7 @@ pub const Writer = struct {
871843 };
872844 }
873845
874 pub fn drain(io_w: *std.Io.Writer, data: []const []const u8, splat: usize) std.Io.Writer.Error!usize {
846 pub fn drain(io_w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize {
875847 const w: *Writer = @alignCast(@fieldParentPtr("interface", io_w));
876848 const handle = w.file.handle;
877849 const buffered = io_w.buffered();
......@@ -1021,10 +993,10 @@ pub const Writer = struct {
1021993 }
1022994
1023995 pub fn sendFile(
1024 io_w: *std.Io.Writer,
1025 file_reader: *std.Io.File.Reader,
1026 limit: std.Io.Limit,
1027 ) std.Io.Writer.FileError!usize {
996 io_w: *Io.Writer,
997 file_reader: *Io.File.Reader,
998 limit: Io.Limit,
999 ) Io.Writer.FileError!usize {
10281000 const reader_buffered = file_reader.interface.buffered();
10291001 if (reader_buffered.len >= @intFromEnum(limit))
10301002 return sendFileBuffered(io_w, file_reader, limit.slice(reader_buffered));
......@@ -1288,16 +1260,16 @@ pub const Writer = struct {
12881260 }
12891261
12901262 fn sendFileBuffered(
1291 io_w: *std.Io.Writer,
1292 file_reader: *std.Io.File.Reader,
1263 io_w: *Io.Writer,
1264 file_reader: *Io.File.Reader,
12931265 reader_buffered: []const u8,
1294 ) std.Io.Writer.FileError!usize {
1266 ) Io.Writer.FileError!usize {
12951267 const n = try drain(io_w, &.{reader_buffered}, 1);
12961268 file_reader.seekBy(@intCast(n)) catch return error.ReadFailed;
12971269 return n;
12981270 }
12991271
1300 pub fn seekTo(w: *Writer, offset: u64) (Writer.SeekError || std.Io.Writer.Error)!void {
1272 pub fn seekTo(w: *Writer, offset: u64) (Writer.SeekError || Io.Writer.Error)!void {
13011273 try w.interface.flush();
13021274 try seekToUnbuffered(w, offset);
13031275 }
......@@ -1321,7 +1293,7 @@ pub const Writer = struct {
13211293 }
13221294 }
13231295
1324 pub const EndError = SetEndPosError || std.Io.Writer.Error;
1296 pub const EndError = SetEndPosError || Io.Writer.Error;
13251297
13261298 /// Flushes any buffered data and sets the end position of the file.
13271299 ///
......@@ -1352,14 +1324,14 @@ pub const Writer = struct {
13521324///
13531325/// Positional is more threadsafe, since the global seek position is not
13541326/// affected.
1355pub fn reader(file: File, io: std.Io, buffer: []u8) Reader {
1327pub fn reader(file: File, io: Io, buffer: []u8) Reader {
13561328 return .init(.{ .handle = file.handle }, io, buffer);
13571329}
13581330
13591331/// Positional is more threadsafe, since the global seek position is not
13601332/// affected, but when such syscalls are not available, preemptively
13611333/// initializing in streaming mode skips a failed syscall.
1362pub fn readerStreaming(file: File, io: std.Io, buffer: []u8) Reader {
1334pub fn readerStreaming(file: File, io: Io, buffer: []u8) Reader {
13631335 return .initStreaming(.{ .handle = file.handle }, io, buffer);
13641336}
13651337
......@@ -1541,10 +1513,10 @@ pub fn downgradeLock(file: File) LockError!void {
15411513 }
15421514}
15431515
1544pub fn adaptToNewApi(file: File) std.Io.File {
1516pub fn adaptToNewApi(file: File) Io.File {
15451517 return .{ .handle = file.handle };
15461518}
15471519
1548pub fn adaptFromNewApi(file: std.Io.File) File {
1520pub fn adaptFromNewApi(file: Io.File) File {
15491521 return .{ .handle = file.handle };
15501522}
lib/std/posix.zig+1-8
......@@ -4458,14 +4458,7 @@ pub fn wait4(pid: pid_t, flags: u32, ru: ?*rusage) WaitPidResult {
44584458 }
44594459}
44604460
4461pub const FStatError = error{
4462 SystemResources,
4463
4464 /// In WASI, this error may occur when the file descriptor does
4465 /// not hold the required rights to get its filestat information.
4466 AccessDenied,
4467 PermissionDenied,
4468} || UnexpectedError;
4461pub const FStatError = std.Io.File.StatError;
44694462
44704463/// Return information about a file descriptor.
44714464pub fn fstat(fd: fd_t) FStatError!Stat {