authorgravatar for 37453713+ominitay@users.noreply.github.comominitay <37453713+ominitay@users.noreply.github.com> 2022-01-27 09:59:30+00:00
committergravatar for 37453713+ominitay@users.noreply.github.comominitay <37453713+ominitay@users.noreply.github.com> 2022-01-28 16:12:05+00:00
logdc11fe4047450167f6b5b2b0e786881b86b3eb27
tree7e25612faf4332948edcc8f165d1dfe9d8c52b04
parentc3ef85aa7654af7f6f45ae9d6497d9fff42e9aff
signaturelock-open Commit is signed but in an unrecognized format.

std: Fix using fs.Dir.Iterator twice

This fixes the use of multiple `Iterator`s in a row on a directory. Previously, on many platforms, using an `Iterator` on an already-iterated directory would give no entries. Fixing this involved seeking to the beginning of the directory on the first call of `next()`.

2 files changed, 29 insertions(+), 7 deletions(-)

lib/std/fs.zig+29-4
......@@ -300,6 +300,7 @@ pub const Dir = struct {
300300 buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),
301301 index: usize,
302302 end_index: usize,
303 first_iter: bool,
303304
304305 const Self = @This();
305306
......@@ -319,6 +320,10 @@ pub const Dir = struct {
319320 fn nextDarwin(self: *Self) !?Entry {
320321 start_over: while (true) {
321322 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 }
322327 const rc = os.system.__getdirentries64(
323328 self.dir.fd,
324329 &self.buf,
......@@ -369,6 +374,10 @@ pub const Dir = struct {
369374 fn nextSolaris(self: *Self) !?Entry {
370375 start_over: while (true) {
371376 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 }
372381 const rc = os.system.getdents(self.dir.fd, &self.buf, self.buf.len);
373382 switch (os.errno(rc)) {
374383 .SUCCESS => {},
......@@ -423,6 +432,10 @@ pub const Dir = struct {
423432 fn nextBsd(self: *Self) !?Entry {
424433 start_over: while (true) {
425434 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 }
426439 const rc = if (builtin.os.tag == .netbsd)
427440 os.system.__getdents30(self.dir.fd, &self.buf, self.buf.len)
428441 else
......@@ -479,6 +492,7 @@ pub const Dir = struct {
479492 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
480493 index: usize,
481494 end_index: usize,
495 first_iter: bool,
482496
483497 const Self = @This();
484498
......@@ -491,6 +505,10 @@ pub const Dir = struct {
491505 // TODO: find a better max
492506 const HAIKU_MAX_COUNT = 10000;
493507 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 }
494512 const rc = os.system._kern_read_dir(
495513 self.dir.fd,
496514 &self.buf,
......@@ -563,6 +581,7 @@ pub const Dir = struct {
563581 buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)),
564582 index: usize,
565583 end_index: usize,
584 first_iter: bool,
566585
567586 const Self = @This();
568587 const linux = os.linux;
......@@ -574,6 +593,10 @@ pub const Dir = struct {
574593 pub fn next(self: *Self) Error!?Entry {
575594 start_over: while (true) {
576595 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 }
577600 const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
578601 switch (linux.getErrno(rc)) {
579602 .SUCCESS => {},
......@@ -620,7 +643,7 @@ pub const Dir = struct {
620643 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
621644 index: usize,
622645 end_index: usize,
623 first: bool,
646 first_iter: bool,
624647 name_data: [256]u8,
625648
626649 const Self = @This();
......@@ -645,9 +668,9 @@ pub const Dir = struct {
645668 .FileBothDirectoryInformation,
646669 w.FALSE,
647670 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),
649672 );
650 self.first = false;
673 self.first_iter = false;
651674 if (io.Information == 0) return null;
652675 self.index = 0;
653676 self.end_index = io.Information;
......@@ -769,18 +792,20 @@ pub const Dir = struct {
769792 .index = 0,
770793 .end_index = 0,
771794 .buf = undefined,
795 .first_iter = true,
772796 },
773797 .linux, .haiku => return Iterator{
774798 .dir = self,
775799 .index = 0,
776800 .end_index = 0,
777801 .buf = undefined,
802 .first_iter = true,
778803 },
779804 .windows => return Iterator{
780805 .dir = self,
781806 .index = 0,
782807 .end_index = 0,
783 .first = true,
808 .first_iter = true,
784809 .buf = undefined,
785810 .name_data = undefined,
786811 },
lib/std/fs/test.zig-3
......@@ -180,10 +180,7 @@ test "Dir.Iterator" {
180180 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
181181}
182182
183// TODO Only passes on Windows, see https://github.com/ziglang/zig/issues/10317
184183test "Dir.Iterator twice" {
185 if (builtin.os.tag != .windows) return error.SkipZigTest;
186
187184 var tmp_dir = tmpDir(.{ .iterate = true });
188185 defer tmp_dir.cleanup();
189186