| ... | ... | @@ -3378,7 +3378,7 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir |
| 3378 | 3378 | // To be consistent across platforms, iteration |
| 3379 | 3379 | // ends if the directory being iterated is deleted |
| 3380 | 3380 | // during iteration. This matches the behavior of |
| 3381 | | // non-Linux UNIX platforms. |
| 3381 | // non-Linux, non-WASI UNIX platforms. |
| 3382 | 3382 | .NOENT => { |
| 3383 | 3383 | dr.state = .finished; |
| 3384 | 3384 | return 0; |
| ... | ... | @@ -3481,7 +3481,7 @@ fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Di |
| 3481 | 3481 | else => |e| { |
| 3482 | 3482 | current_thread.endSyscall(); |
| 3483 | 3483 | switch (e) { |
| 3484 | | .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability |
| 3484 | .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability. |
| 3485 | 3485 | .FAULT => |err| return errnoBug(err), |
| 3486 | 3486 | .NOTDIR => |err| return errnoBug(err), |
| 3487 | 3487 | .INVAL => |err| return errnoBug(err), |
| ... | ... | @@ -3839,10 +3839,109 @@ fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D |
| 3839 | 3839 | } |
| 3840 | 3840 | |
| 3841 | 3841 | fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |
| 3842 | | _ = userdata; |
| 3843 | | _ = dr; |
| 3844 | | _ = buffer; |
| 3845 | | @panic("TODO"); |
| 3842 | // We intentinally use fd_readdir even when linked with libc, since its |
| 3843 | // implementation is exactly the same as below, and we avoid the code |
| 3844 | // complexity here. |
| 3845 | const wasi = std.os.wasi; |
| 3846 | const t: *Threaded = @ptrCast(@alignCast(userdata)); |
| 3847 | const current_thread = Thread.getCurrent(t); |
| 3848 | const Header = extern struct { |
| 3849 | cookie: u64, |
| 3850 | }; |
| 3851 | const header: *align(@alignOf(usize)) Header = @ptrCast(dr.buffer.ptr); |
| 3852 | const header_end: usize = @sizeOf(Header); |
| 3853 | if (dr.index < header_end) { |
| 3854 | // Initialize header. |
| 3855 | dr.index = header_end; |
| 3856 | dr.end = header_end; |
| 3857 | header.* = .{ .cookie = wasi.DIRCOOKIE_START }; |
| 3858 | } |
| 3859 | var buffer_index: usize = 0; |
| 3860 | while (buffer.len - buffer_index != 0) { |
| 3861 | // According to the WASI spec, the last entry might be truncated, so we |
| 3862 | // need to check if the remaining buffer contains the whole dirent. |
| 3863 | if (dr.end - dr.index < @sizeOf(wasi.dirent_t)) { |
| 3864 | // Refill the buffer, unless we've already created references to |
| 3865 | // buffered data. |
| 3866 | if (buffer_index != 0) break; |
| 3867 | if (dr.state == .reset) { |
| 3868 | header.* = .{ .cookie = wasi.DIRCOOKIE_START }; |
| 3869 | dr.state = .reading; |
| 3870 | } |
| 3871 | const dents_buffer = dr.buffer[header_end..]; |
| 3872 | var n: usize = undefined; |
| 3873 | try current_thread.beginSyscall(); |
| 3874 | while (true) { |
| 3875 | switch (wasi.fd_readdir(dr.dir.handle, dents_buffer.ptr, dents_buffer.len, header.cookie, &n)) { |
| 3876 | .SUCCESS => { |
| 3877 | current_thread.endSyscall(); |
| 3878 | break; |
| 3879 | }, |
| 3880 | .INTR => { |
| 3881 | try current_thread.checkCancel(); |
| 3882 | continue; |
| 3883 | }, |
| 3884 | else => |e| { |
| 3885 | current_thread.endSyscall(); |
| 3886 | switch (e) { |
| 3887 | .BADF => |err| return errnoBug(err), // Dir is invalid or was opened without iteration ability. |
| 3888 | .FAULT => |err| return errnoBug(err), |
| 3889 | .NOTDIR => |err| return errnoBug(err), |
| 3890 | .INVAL => |err| return errnoBug(err), |
| 3891 | // To be consistent across platforms, iteration |
| 3892 | // ends if the directory being iterated is deleted |
| 3893 | // during iteration. This matches the behavior of |
| 3894 | // non-Linux, non-WASI UNIX platforms. |
| 3895 | .NOENT => { |
| 3896 | dr.state = .finished; |
| 3897 | return 0; |
| 3898 | }, |
| 3899 | .NOTCAPABLE => return error.AccessDenied, |
| 3900 | else => |err| return posix.unexpectedErrno(err), |
| 3901 | } |
| 3902 | }, |
| 3903 | } |
| 3904 | } |
| 3905 | if (n == 0) { |
| 3906 | dr.state = .finished; |
| 3907 | return 0; |
| 3908 | } |
| 3909 | dr.index = header_end; |
| 3910 | dr.end = header_end + n; |
| 3911 | } |
| 3912 | const entry: *align(1) wasi.dirent_t = @ptrCast(&dr.buffer[dr.index]); |
| 3913 | const entry_size = @sizeOf(wasi.dirent_t); |
| 3914 | const name_index = dr.index + entry_size; |
| 3915 | if (name_index + entry.namlen > dr.end) { |
| 3916 | // This case, the name is truncated, so we need to call readdir to store the entire name. |
| 3917 | dr.end = dr.index; // Force fd_readdir in the next loop. |
| 3918 | continue; |
| 3919 | } |
| 3920 | const name = dr.buffer[name_index..][0..entry.namlen]; |
| 3921 | const next_index = name_index + entry.namlen; |
| 3922 | dr.index = next_index; |
| 3923 | header.cookie = entry.next; |
| 3924 | |
| 3925 | if (std.mem.eql(u8, name, ".") or std.mem.eql(u8, name, "..")) |
| 3926 | continue; |
| 3927 | |
| 3928 | const entry_kind: File.Kind = switch (entry.type) { |
| 3929 | .BLOCK_DEVICE => .block_device, |
| 3930 | .CHARACTER_DEVICE => .character_device, |
| 3931 | .DIRECTORY => .directory, |
| 3932 | .SYMBOLIC_LINK => .sym_link, |
| 3933 | .REGULAR_FILE => .file, |
| 3934 | .SOCKET_STREAM, .SOCKET_DGRAM => .unix_domain_socket, |
| 3935 | else => .unknown, |
| 3936 | }; |
| 3937 | buffer[buffer_index] = .{ |
| 3938 | .name = name, |
| 3939 | .kind = entry_kind, |
| 3940 | .inode = entry.ino, |
| 3941 | }; |
| 3942 | buffer_index += 1; |
| 3943 | } |
| 3944 | return buffer_index; |
| 3846 | 3945 | } |
| 3847 | 3946 | |
| 3848 | 3947 | fn dirReadUnimplemented(userdata: ?*anyopaque, dir_reader: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |