authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-01-30 14:25:50+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-01-30 14:25:50+01:00
logdd7309bde4aaf7921976a6d70668ef589f308ff0
treeb2096a1828b06732d5193141dd516f1b2c3ad477
parent9c36ae46260cc680663ba3347da06e5e2a16590b
parentdc11fe4047450167f6b5b2b0e786881b86b3eb27
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

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
...@@ -302,6 +302,7 @@ pub const Dir = struct {...@@ -302,6 +302,7 @@ pub const Dir = struct {
302 buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),302 buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),
303 index: usize,303 index: usize,
304 end_index: usize,304 end_index: usize,
305 first_iter: bool,
305306
306 const Self = @This();307 const Self = @This();
307308
...@@ -321,6 +322,10 @@ pub const Dir = struct {...@@ -321,6 +322,10 @@ pub const Dir = struct {
321 fn nextDarwin(self: *Self) !?Entry {322 fn nextDarwin(self: *Self) !?Entry {
322 start_over: while (true) {323 start_over: while (true) {
323 if (self.index >= self.end_index) {324 if (self.index >= self.end_index) {
325 if (self.first_iter) {
326 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
327 self.first_iter = false;
328 }
324 const rc = os.system.__getdirentries64(329 const rc = os.system.__getdirentries64(
325 self.dir.fd,330 self.dir.fd,
326 &self.buf,331 &self.buf,
...@@ -371,6 +376,10 @@ pub const Dir = struct {...@@ -371,6 +376,10 @@ pub const Dir = struct {
371 fn nextSolaris(self: *Self) !?Entry {376 fn nextSolaris(self: *Self) !?Entry {
372 start_over: while (true) {377 start_over: while (true) {
373 if (self.index >= self.end_index) {378 if (self.index >= self.end_index) {
379 if (self.first_iter) {
380 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
381 self.first_iter = false;
382 }
374 const rc = os.system.getdents(self.dir.fd, &self.buf, self.buf.len);383 const rc = os.system.getdents(self.dir.fd, &self.buf, self.buf.len);
375 switch (os.errno(rc)) {384 switch (os.errno(rc)) {
376 .SUCCESS => {},385 .SUCCESS => {},
...@@ -425,6 +434,10 @@ pub const Dir = struct {...@@ -425,6 +434,10 @@ pub const Dir = struct {
425 fn nextBsd(self: *Self) !?Entry {434 fn nextBsd(self: *Self) !?Entry {
426 start_over: while (true) {435 start_over: while (true) {
427 if (self.index >= self.end_index) {436 if (self.index >= self.end_index) {
437 if (self.first_iter) {
438 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
439 self.first_iter = false;
440 }
428 const rc = if (builtin.os.tag == .netbsd)441 const rc = if (builtin.os.tag == .netbsd)
429 os.system.__getdents30(self.dir.fd, &self.buf, self.buf.len)442 os.system.__getdents30(self.dir.fd, &self.buf, self.buf.len)
430 else443 else
...@@ -481,6 +494,7 @@ pub const Dir = struct {...@@ -481,6 +494,7 @@ pub const Dir = struct {
481 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),494 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
482 index: usize,495 index: usize,
483 end_index: usize,496 end_index: usize,
497 first_iter: bool,
484498
485 const Self = @This();499 const Self = @This();
486500
...@@ -493,6 +507,10 @@ pub const Dir = struct {...@@ -493,6 +507,10 @@ pub const Dir = struct {
493 // TODO: find a better max507 // TODO: find a better max
494 const HAIKU_MAX_COUNT = 10000;508 const HAIKU_MAX_COUNT = 10000;
495 if (self.index >= self.end_index) {509 if (self.index >= self.end_index) {
510 if (self.first_iter) {
511 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
512 self.first_iter = false;
513 }
496 const rc = os.system._kern_read_dir(514 const rc = os.system._kern_read_dir(
497 self.dir.fd,515 self.dir.fd,
498 &self.buf,516 &self.buf,
...@@ -565,6 +583,7 @@ pub const Dir = struct {...@@ -565,6 +583,7 @@ pub const Dir = struct {
565 buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)),583 buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)),
566 index: usize,584 index: usize,
567 end_index: usize,585 end_index: usize,
586 first_iter: bool,
568587
569 const Self = @This();588 const Self = @This();
570 const linux = os.linux;589 const linux = os.linux;
...@@ -576,6 +595,10 @@ pub const Dir = struct {...@@ -576,6 +595,10 @@ pub const Dir = struct {
576 pub fn next(self: *Self) Error!?Entry {595 pub fn next(self: *Self) Error!?Entry {
577 start_over: while (true) {596 start_over: while (true) {
578 if (self.index >= self.end_index) {597 if (self.index >= self.end_index) {
598 if (self.first_iter) {
599 std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
600 self.first_iter = false;
601 }
579 const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);602 const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
580 switch (linux.getErrno(rc)) {603 switch (linux.getErrno(rc)) {
581 .SUCCESS => {},604 .SUCCESS => {},
...@@ -622,7 +645,7 @@ pub const Dir = struct {...@@ -622,7 +645,7 @@ pub const Dir = struct {
622 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),645 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
623 index: usize,646 index: usize,
624 end_index: usize,647 end_index: usize,
625 first: bool,648 first_iter: bool,
626 name_data: [256]u8,649 name_data: [256]u8,
627650
628 const Self = @This();651 const Self = @This();
...@@ -647,9 +670,9 @@ pub const Dir = struct {...@@ -647,9 +670,9 @@ pub const Dir = struct {
647 .FileBothDirectoryInformation,670 .FileBothDirectoryInformation,
648 w.FALSE,671 w.FALSE,
649 null,672 null,
650 if (self.first) @as(w.BOOLEAN, w.TRUE) else @as(w.BOOLEAN, w.FALSE),673 if (self.first_iter) @as(w.BOOLEAN, w.TRUE) else @as(w.BOOLEAN, w.FALSE),
651 );674 );
652 self.first = false;675 self.first_iter = false;
653 if (io.Information == 0) return null;676 if (io.Information == 0) return null;
654 self.index = 0;677 self.index = 0;
655 self.end_index = io.Information;678 self.end_index = io.Information;
...@@ -771,18 +794,20 @@ pub const Dir = struct {...@@ -771,18 +794,20 @@ pub const Dir = struct {
771 .index = 0,794 .index = 0,
772 .end_index = 0,795 .end_index = 0,
773 .buf = undefined,796 .buf = undefined,
797 .first_iter = true,
774 },798 },
775 .linux, .haiku => return Iterator{799 .linux, .haiku => return Iterator{
776 .dir = self,800 .dir = self,
777 .index = 0,801 .index = 0,
778 .end_index = 0,802 .end_index = 0,
779 .buf = undefined,803 .buf = undefined,
804 .first_iter = true,
780 },805 },
781 .windows => return Iterator{806 .windows => return Iterator{
782 .dir = self,807 .dir = self,
783 .index = 0,808 .index = 0,
784 .end_index = 0,809 .end_index = 0,
785 .first = true,810 .first_iter = true,
786 .buf = undefined,811 .buf = undefined,
787 .name_data = undefined,812 .name_data = undefined,
788 },813 },
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}