Commit ac3ac255a2

Jakub Konka <kubkon@jakubkonka.com>
2022-01-30 14:25:50
Merge pull request #10404 from ominitay/iterator
std: Fix using `fs.Dir.Iterator` twice
1 parent 69f46ca
Changed files (2)
lib
lib/std/fs/test.zig
@@ -180,6 +180,39 @@ test "Dir.Iterator" {
     try testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
 }
 
+test "Dir.Iterator twice" {
+    var tmp_dir = tmpDir(.{ .iterate = true });
+    defer tmp_dir.cleanup();
+
+    // First, create a couple of entries to iterate over.
+    const file = try tmp_dir.dir.createFile("some_file", .{});
+    file.close();
+
+    try tmp_dir.dir.makeDir("some_dir");
+
+    var arena = ArenaAllocator.init(testing.allocator);
+    defer arena.deinit();
+    const allocator = arena.allocator();
+
+    var i: u8 = 0;
+    while (i < 2) : (i += 1) {
+        var entries = std.ArrayList(Dir.Entry).init(allocator);
+
+        // Create iterator.
+        var iter = tmp_dir.dir.iterate();
+        while (try iter.next()) |entry| {
+            // We cannot just store `entry` as on Windows, we're re-using the name buffer
+            // which means we'll actually share the `name` pointer between entries!
+            const name = try allocator.dupe(u8, entry.name);
+            try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
+        }
+
+        try testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'
+        try testing.expect(contains(&entries, Dir.Entry{ .name = "some_file", .kind = Dir.Entry.Kind.File }));
+        try testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));
+    }
+}
+
 fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
     return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
 }
lib/std/fs.zig
@@ -300,6 +300,7 @@ pub const Dir = struct {
             buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)),
             index: usize,
             end_index: usize,
+            first_iter: bool,
 
             const Self = @This();
 
@@ -319,6 +320,10 @@ pub const Dir = struct {
             fn nextDarwin(self: *Self) !?Entry {
                 start_over: while (true) {
                     if (self.index >= self.end_index) {
+                        if (self.first_iter) {
+                            std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
+                            self.first_iter = false;
+                        }
                         const rc = os.system.__getdirentries64(
                             self.dir.fd,
                             &self.buf,
@@ -369,6 +374,10 @@ pub const Dir = struct {
             fn nextSolaris(self: *Self) !?Entry {
                 start_over: while (true) {
                     if (self.index >= self.end_index) {
+                        if (self.first_iter) {
+                            std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
+                            self.first_iter = false;
+                        }
                         const rc = os.system.getdents(self.dir.fd, &self.buf, self.buf.len);
                         switch (os.errno(rc)) {
                             .SUCCESS => {},
@@ -423,6 +432,10 @@ pub const Dir = struct {
             fn nextBsd(self: *Self) !?Entry {
                 start_over: while (true) {
                     if (self.index >= self.end_index) {
+                        if (self.first_iter) {
+                            std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
+                            self.first_iter = false;
+                        }
                         const rc = if (builtin.os.tag == .netbsd)
                             os.system.__getdents30(self.dir.fd, &self.buf, self.buf.len)
                         else
@@ -479,6 +492,7 @@ pub const Dir = struct {
             buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
             index: usize,
             end_index: usize,
+            first_iter: bool,
 
             const Self = @This();
 
@@ -491,6 +505,10 @@ pub const Dir = struct {
                     // TODO: find a better max
                     const HAIKU_MAX_COUNT = 10000;
                     if (self.index >= self.end_index) {
+                        if (self.first_iter) {
+                            std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
+                            self.first_iter = false;
+                        }
                         const rc = os.system._kern_read_dir(
                             self.dir.fd,
                             &self.buf,
@@ -563,6 +581,7 @@ pub const Dir = struct {
             buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)),
             index: usize,
             end_index: usize,
+            first_iter: bool,
 
             const Self = @This();
             const linux = os.linux;
@@ -574,6 +593,10 @@ pub const Dir = struct {
             pub fn next(self: *Self) Error!?Entry {
                 start_over: while (true) {
                     if (self.index >= self.end_index) {
+                        if (self.first_iter) {
+                            std.os.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions
+                            self.first_iter = false;
+                        }
                         const rc = linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
                         switch (linux.getErrno(rc)) {
                             .SUCCESS => {},
@@ -620,7 +643,7 @@ pub const Dir = struct {
             buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)),
             index: usize,
             end_index: usize,
-            first: bool,
+            first_iter: bool,
             name_data: [256]u8,
 
             const Self = @This();
@@ -645,9 +668,9 @@ pub const Dir = struct {
                             .FileBothDirectoryInformation,
                             w.FALSE,
                             null,
-                            if (self.first) @as(w.BOOLEAN, w.TRUE) else @as(w.BOOLEAN, w.FALSE),
+                            if (self.first_iter) @as(w.BOOLEAN, w.TRUE) else @as(w.BOOLEAN, w.FALSE),
                         );
-                        self.first = false;
+                        self.first_iter = false;
                         if (io.Information == 0) return null;
                         self.index = 0;
                         self.end_index = io.Information;
@@ -769,18 +792,20 @@ pub const Dir = struct {
                 .index = 0,
                 .end_index = 0,
                 .buf = undefined,
+                .first_iter = true,
             },
             .linux, .haiku => return Iterator{
                 .dir = self,
                 .index = 0,
                 .end_index = 0,
                 .buf = undefined,
+                .first_iter = true,
             },
             .windows => return Iterator{
                 .dir = self,
                 .index = 0,
                 .end_index = 0,
-                .first = true,
+                .first_iter = true,
                 .buf = undefined,
                 .name_data = undefined,
             },