authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-30 14:25:50+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-02-02 22:04:36-07:00
logac3ac255a2d9ef4279c8fea2510c81d5eea9d62b
tree1200b2e13cca0119fe93d31014d45aaab6b74b80
parent69f46cab555edd3fda9012a237aa7f708c144bfe

Merge pull request #10404 from ominitay/iterator

std: Fix using `fs.Dir.Iterator` twice

2 files changed, 62 insertions(+), 4 deletions(-)

lib/std/fs.zig+29-4
...@@ -300,6 +300,7 @@ pub const Dir = struct {...@@ -300,6 +300,7 @@ pub const Dir = struct {
300 buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),300 buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),
301 index: usize,301 index: usize,
302 end_index: usize,302 end_index: usize,
303 first_iter: bool,
303304
304 const Self = @This();305 const Self = @This();
305306
...@@ -319,6 +320,10 @@ pub const Dir = struct {...@@ -319,6 +320,10 @@ pub const Dir = struct {
319 fn nextDarwin(self: *Self) !?Entry {320 fn nextDarwin(self: *Self) !?Entry {
320 start_over: while (true) {321 start_over: while (true) {
321 if (self.index >= self.end_index) {322 if (self.index >= self.end_index) {
323 if (self.first_iter) {
324 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
325 self.first_iter = false;
326 }
322 const rc = os.system.__getdirentries64(327 const rc = os.system.__getdirentries64(
323 self.dir.fd,328 self.dir.fd,
324 &self.buf,329 &self.buf,
...@@ -369,6 +374,10 @@ pub const Dir = struct {...@@ -369,6 +374,10 @@ pub const Dir = struct {
369 fn nextSolaris(self: *Self) !?Entry {374 fn nextSolaris(self: *Self) !?Entry {
370 start_over: while (true) {375 start_over: while (true) {
371 if (self.index >= self.end_index) {376 if (self.index >= self.end_index) {
377 if (self.first_iter) {
378 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
379 self.first_iter = false;
380 }
372 const rc = os.system.getdents(self.dir.fd, &self.buf, self.buf.len);381 const rc = os.system.getdents(self.dir.fd, &self.buf, self.buf.len);
373 switch (os.errno(rc)) {382 switch (os.errno(rc)) {
374 .SUCCESS => {},383 .SUCCESS => {},
...@@ -423,6 +432,10 @@ pub const Dir = struct {...@@ -423,6 +432,10 @@ pub const Dir = struct {
423 fn nextBsd(self: *Self) !?Entry {432 fn nextBsd(self: *Self) !?Entry {
424 start_over: while (true) {433 start_over: while (true) {
425 if (self.index >= self.end_index) {434 if (self.index >= self.end_index) {
435 if (self.first_iter) {
436 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
437 self.first_iter = false;
438 }
426 const rc = if (builtin.os.tag == .netbsd)439 const rc = if (builtin.os.tag == .netbsd)
427 os.system.__getdents30(self.dir.fd, &self.buf, self.buf.len)440 os.system.__getdents30(self.dir.fd, &self.buf, self.buf.len)
428 else441 else
...@@ -479,6 +492,7 @@ pub const Dir = struct {...@@ -479,6 +492,7 @@ pub const Dir = struct {
479 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),492 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
480 index: usize,493 index: usize,
481 end_index: usize,494 end_index: usize,
495 first_iter: bool,
482496
483 const Self = @This();497 const Self = @This();
484498
...@@ -491,6 +505,10 @@ pub const Dir = struct {...@@ -491,6 +505,10 @@ pub const Dir = struct {
491 // TODO: find a better max505 // TODO: find a better max
492 const HAIKU_MAX_COUNT = 10000;506 const HAIKU_MAX_COUNT = 10000;
493 if (self.index >= self.end_index) {507 if (self.index >= self.end_index) {
508 if (self.first_iter) {
509 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
510 self.first_iter = false;
511 }
494 const rc = os.system._kern_read_dir(512 const rc = os.system._kern_read_dir(
495 self.dir.fd,513 self.dir.fd,
496 &self.buf,514 &self.buf,
...@@ -563,6 +581,7 @@ pub const Dir = struct {...@@ -563,6 +581,7 @@ pub const Dir = struct {
563 buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)),581 buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)),
564 index: usize,582 index: usize,
565 end_index: usize,583 end_index: usize,
584 first_iter: bool,
566585
567 const Self = @This();586 const Self = @This();
568 const linux = os.linux;587 const linux = os.linux;
...@@ -574,6 +593,10 @@ pub const Dir = struct {...@@ -574,6 +593,10 @@ pub const Dir = struct {
574 pub fn next(self: *Self) Error!?Entry {593 pub fn next(self: *Self) Error!?Entry {
575 start_over: while (true) {594 start_over: while (true) {
576 if (self.index >= self.end_index) {595 if (self.index >= self.end_index) {
596 if (self.first_iter) {
597 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
598 self.first_iter = false;
599 }
577 const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);600 const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
578 switch (linux.getErrno(rc)) {601 switch (linux.getErrno(rc)) {
579 .SUCCESS => {},602 .SUCCESS => {},
...@@ -620,7 +643,7 @@ pub const Dir = struct {...@@ -620,7 +643,7 @@ pub const Dir = struct {
620 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),643 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
621 index: usize,644 index: usize,
622 end_index: usize,645 end_index: usize,
623 first: bool,646 first_iter: bool,
624 name_data: [256]u8,647 name_data: [256]u8,
625648
626 const Self = @This();649 const Self = @This();
...@@ -645,9 +668,9 @@ pub const Dir = struct {...@@ -645,9 +668,9 @@ pub const Dir = struct {
645 .FileBothDirectoryInformation,668 .FileBothDirectoryInformation,
646 w.FALSE,669 w.FALSE,
647 null,670 null,
648 if (self.first) @as(w.BOOLEAN, w.TRUE) else @as(w.BOOLEAN, w.FALSE),671 if (self.first_iter) @as(w.BOOLEAN, w.TRUE) else @as(w.BOOLEAN, w.FALSE),
649 );672 );
650 self.first = false;673 self.first_iter = false;
651 if (io.Information == 0) return null;674 if (io.Information == 0) return null;
652 self.index = 0;675 self.index = 0;
653 self.end_index = io.Information;676 self.end_index = io.Information;
...@@ -769,18 +792,20 @@ pub const Dir = struct {...@@ -769,18 +792,20 @@ pub const Dir = struct {
769 .index = 0,792 .index = 0,
770 .end_index = 0,793 .end_index = 0,
771 .buf = undefined,794 .buf = undefined,
795 .first_iter = true,
772 },796 },
773 .linux, .haiku => return Iterator{797 .linux, .haiku => return Iterator{
774 .dir = self,798 .dir = self,
775 .index = 0,799 .index = 0,
776 .end_index = 0,800 .end_index = 0,
777 .buf = undefined,801 .buf = undefined,
802 .first_iter = true,
778 },803 },
779 .windows => return Iterator{804 .windows => return Iterator{
780 .dir = self,805 .dir = self,
781 .index = 0,806 .index = 0,
782 .end_index = 0,807 .end_index = 0,
783 .first = true,808 .first_iter = true,
784 .buf = undefined,809 .buf = undefined,
785 .name_data = undefined,810 .name_data = undefined,
786 },811 },
lib/std/fs/test.zig+33
...@@ -180,6 +180,39 @@ test "Dir.Iterator" {...@@ -180,6 +180,39 @@ test "Dir.Iterator" {
180 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));180 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
181}181}
182182
183test "Dir.Iterator twice" {
184 var tmp_dir = tmpDir(.{ .iterate = true });
185 defer tmp_dir.cleanup();
186
187 // First, create a couple of entries to iterate over.
188 const file = try tmp_dir.dir.createFile("some_file", .{});
189 file.close();
190
191 try tmp_dir.dir.makeDir("some_dir");
192
193 var arena = ArenaAllocator.init(testing.allocator);
194 defer arena.deinit();
195 const allocator = arena.allocator();
196
197 var i: u8 = 0;
198 while (i < 2) : (i += 1) {
199 var entries = std.ArrayList(Dir.Entry).init(allocator);
200
201 // Create iterator.
202 var iter = tmp_dir.dir.iterate();
203 while (try iter.next()) |entry| {
204 // We cannot just store `entry` as on Windows, we're re-using the name buffer
205 // which means we'll actually share the `name` pointer between entries!
206 const name = try allocator.dupe(u8, entry.name);
207 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
208 }
209
210 try testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'
211 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_file", .kind = Dir.Entry.Kind.File }));
212 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
213 }
214}
215
183fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {216fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
184 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;217 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
185}218}