authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-12-01 17:30:00+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-09 14:52:57-07:00
logffb27a8bd0afae63a7083d1b3eb946060cb911dd
tree86eac7508b469190a128a15c7ba9c9129df5bd52
parentbe5b1ab7ede23c972cb5816a7581ac463c77321f

Merge pull request #13725 from mathetake/fixreaddir

wasi: fixes IterableDir.nextWasi for large directory

2 files changed, 47 insertions(+), 1 deletions(-)

lib/std/fs.zig+10-1
...@@ -809,7 +809,11 @@ pub const IterableDir = struct {...@@ -809,7 +809,11 @@ pub const IterableDir = struct {
809 // and we avoid the code complexity here.809 // and we avoid the code complexity here.
810 const w = os.wasi;810 const w = os.wasi;
811 start_over: while (true) {811 start_over: while (true) {
812 if (self.index >= self.end_index) {812 // TODO https://github.com/ziglang/zig/issues/12498
813 _ = @sizeOf(w.dirent_t) + 1;
814 // According to the WASI spec, the last entry might be truncated,
815 // so we need to check if the left buffer contains the whole dirent.
816 if (self.end_index - self.index < @sizeOf(w.dirent_t)) {
813 var bufused: usize = undefined;817 var bufused: usize = undefined;
814 switch (w.fd_readdir(self.dir.fd, &self.buf, self.buf.len, self.cookie, &bufused)) {818 switch (w.fd_readdir(self.dir.fd, &self.buf, self.buf.len, self.cookie, &bufused)) {
815 .SUCCESS => {},819 .SUCCESS => {},
...@@ -828,6 +832,11 @@ pub const IterableDir = struct {...@@ -828,6 +832,11 @@ pub const IterableDir = struct {
828 const entry = @ptrCast(*align(1) w.dirent_t, &self.buf[self.index]);832 const entry = @ptrCast(*align(1) w.dirent_t, &self.buf[self.index]);
829 const entry_size = @sizeOf(w.dirent_t);833 const entry_size = @sizeOf(w.dirent_t);
830 const name_index = self.index + entry_size;834 const name_index = self.index + entry_size;
835 if (name_index + entry.d_namlen > self.end_index) {
836 // This case, the name is truncated, so we need to call readdir to store the entire name.
837 self.end_index = self.index; // Force fd_readdir in the next loop.
838 continue :start_over;
839 }
831 const name = mem.span(self.buf[name_index .. name_index + entry.d_namlen]);840 const name = mem.span(self.buf[name_index .. name_index + entry.d_namlen]);
832841
833 const next_index = name_index + entry.d_namlen;842 const next_index = name_index + entry.d_namlen;
lib/std/fs/test.zig+37
...@@ -186,6 +186,43 @@ test "Dir.Iterator" {...@@ -186,6 +186,43 @@ test "Dir.Iterator" {
186 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .Directory }));186 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .Directory }));
187}187}
188188
189test "Dir.Iterator many entries" {
190 if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
191
192 var tmp_dir = tmpIterableDir(.{});
193 defer tmp_dir.cleanup();
194
195 const num = 1024;
196 var i: usize = 0;
197 var buf: [4]u8 = undefined; // Enough to store "1024".
198 while (i < num) : (i += 1) {
199 const name = try std.fmt.bufPrint(&buf, "{}", .{i});
200 const file = try tmp_dir.iterable_dir.dir.createFile(name, .{});
201 file.close();
202 }
203
204 var arena = ArenaAllocator.init(testing.allocator);
205 defer arena.deinit();
206 const allocator = arena.allocator();
207
208 var entries = std.ArrayList(IterableDir.Entry).init(allocator);
209
210 // Create iterator.
211 var iter = tmp_dir.iterable_dir.iterate();
212 while (try iter.next()) |entry| {
213 // We cannot just store `entry` as on Windows, we're re-using the name buffer
214 // which means we'll actually share the `name` pointer between entries!
215 const name = try allocator.dupe(u8, entry.name);
216 try entries.append(.{ .name = name, .kind = entry.kind });
217 }
218
219 i = 0;
220 while (i < num) : (i += 1) {
221 const name = try std.fmt.bufPrint(&buf, "{}", .{i});
222 try testing.expect(contains(&entries, .{ .name = name, .kind = .File }));
223 }
224}
225
189test "Dir.Iterator twice" {226test "Dir.Iterator twice" {
190 var tmp_dir = tmpIterableDir(.{});227 var tmp_dir = tmpIterableDir(.{});
191 defer tmp_dir.cleanup();228 defer tmp_dir.cleanup();