| ... | @@ -3378,7 +3378,7 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir | ... | @@ -3378,7 +3378,7 @@ fn dirReadLinux(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir |
| 3378 | // To be consistent across platforms, iteration | 3378 | // To be consistent across platforms, iteration |
| 3379 | // ends if the directory being iterated is deleted | 3379 | // ends if the directory being iterated is deleted |
| 3380 | // during iteration. This matches the behavior of | 3380 | // during iteration. This matches the behavior of |
| 3381 | // non-Linux UNIX platforms. | 3381 | // non-Linux, non-WASI UNIX platforms. |
| 3382 | .NOENT => { | 3382 | .NOENT => { |
| 3383 | dr.state = .finished; | 3383 | dr.state = .finished; |
| 3384 | return 0; | 3384 | return 0; |
| ... | @@ -3481,7 +3481,7 @@ fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Di | ... | @@ -3481,7 +3481,7 @@ fn dirReadDarwin(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Di |
| 3481 | else => |e| { | 3481 | else => |e| { |
| 3482 | current_thread.endSyscall(); | 3482 | current_thread.endSyscall(); |
| 3483 | switch (e) { | 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 | .FAULT => |err| return errnoBug(err), | 3485 | .FAULT => |err| return errnoBug(err), |
| 3486 | .NOTDIR => |err| return errnoBug(err), | 3486 | .NOTDIR => |err| return errnoBug(err), |
| 3487 | .INVAL => |err| return errnoBug(err), | 3487 | .INVAL => |err| return errnoBug(err), |
| ... | @@ -3839,10 +3839,109 @@ fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D | ... | @@ -3839,10 +3839,109 @@ fn dirReadWindows(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) D |
| 3839 | } | 3839 | } |
| 3840 | | 3840 | |
| 3841 | fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { | 3841 | fn dirReadWasi(userdata: ?*anyopaque, dr: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |
| 3842 | _ = userdata; | 3842 | // We intentinally use fd_readdir even when linked with libc, since its |
| 3843 | _ = dr; | 3843 | // implementation is exactly the same as below, and we avoid the code |
| 3844 | _ = buffer; | 3844 | // complexity here. |
| 3845 | @panic("TODO"); | 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 | fn dirReadUnimplemented(userdata: ?*anyopaque, dir_reader: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { | 3947 | fn dirReadUnimplemented(userdata: ?*anyopaque, dir_reader: *Dir.Reader, buffer: []Dir.Entry) Dir.Reader.Error!usize { |