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 {
302302 buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),
303303 index: usize,
304304 end_index: usize,
305 first_iter: bool,
305306
306307 const Self = @This();
307308
......@@ -321,6 +322,10 @@ pub const Dir = struct {
321322 fn nextDarwin(self: *Self) !?Entry {
322323 start_over: while (true) {
323324 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 }
324329 const rc = os.system.__getdirentries64(
325330 self.dir.fd,
326331 &self.buf,
......@@ -371,6 +376,10 @@ pub const Dir = struct {
371376 fn nextSolaris(self: *Self) !?Entry {
372377 start_over: while (true) {
373378 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 }
374383 const rc = os.system.getdents(self.dir.fd, &self.buf, self.buf.len);
375384 switch (os.errno(rc)) {
376385 .SUCCESS => {},
......@@ -425,6 +434,10 @@ pub const Dir = struct {
425434 fn nextBsd(self: *Self) !?Entry {
426435 start_over: while (true) {
427436 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 }
428441 const rc = if (builtin.os.tag == .netbsd)
429442 os.system.__getdents30(self.dir.fd, &self.buf, self.buf.len)
430443 else
......@@ -481,6 +494,7 @@ pub const Dir = struct {
481494 buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
482495 index: usize,
483496 end_index: usize,
497 first_iter: bool,
484498
485499 const Self = @This();
486500
......@@ -493,6 +507,10 @@ pub const Dir = struct {
493507 // TODO: find a better max
494508 const HAIKU_MAX_COUNT = 10000;
495509 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 }
496514 const rc = os.system._kern_read_dir(
497515 self.dir.fd,
498516 &self.buf,
......@@ -565,6 +583,7 @@ pub const Dir = struct {
565583 buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)),
566584 index: usize,
567585 end_index: usize,
586 first_iter: bool,
568587
569588 const Self = @This();
570589 const linux = os.linux;
......@@ -576,6 +595,10 @@ pub const Dir = struct {
576595 pub fn next(self: *Self) Error!?Entry {
577596 start_over: while (true) {
578597 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 }
579602 const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
580603 switch (linux.getErrno(rc)) {
581604 .SUCCESS => {},
......@@ -622,7 +645,7 @@ pub const Dir = struct {
622645 buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
623646 index: usize,
624647 end_index: usize,
625 first: bool,
648 first_iter: bool,
626649 name_data: [256]u8,
627650
628651 const Self = @This();
......@@ -647,9 +670,9 @@ pub const Dir = struct {
647670 .FileBothDirectoryInformation,
648671 w.FALSE,
649672 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),
651674 );
652 self.first = false;
675 self.first_iter = false;
653676 if (io.Information == 0) return null;
654677 self.index = 0;
655678 self.end_index = io.Information;
......@@ -771,18 +794,20 @@ pub const Dir = struct {
771794 .index = 0,
772795 .end_index = 0,
773796 .buf = undefined,
797 .first_iter = true,
774798 },
775799 .linux, .haiku => return Iterator{
776800 .dir = self,
777801 .index = 0,
778802 .end_index = 0,
779803 .buf = undefined,
804 .first_iter = true,
780805 },
781806 .windows => return Iterator{
782807 .dir = self,
783808 .index = 0,
784809 .end_index = 0,
785 .first = true,
810 .first_iter = true,
786811 .buf = undefined,
787812 .name_data = undefined,
788813 },
lib/std/fs/test.zig+33
......@@ -180,6 +180,39 @@ test "Dir.Iterator" {
180180 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
181181}
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
183216fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
184217 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
185218}