commit dd7309bde4aaf7921976a6d70668ef589f308ff0 (tree)
parent 9c36ae46260cc680663ba3347da06e5e2a16590b
Author: Jakub Konka <kubkon@jakubkonka.com>
Date: Sun, 30 Jan 2022 14:25:50 +0100
Merge pull request #10404 from ominitay/iterator
std: Fix using `fs.Dir.Iterator` twice
Diffstat:
2 files changed, 62 insertions(+), 4 deletions(-)
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
@@ -302,6 +302,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();
@@ -321,6 +322,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,
@@ -371,6 +376,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 => {},
@@ -425,6 +434,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
@@ -481,6 +494,7 @@ pub const Dir = struct {
buf: [8192]u8, // TODO align(@alignOf(os.dirent64)),
index: usize,
end_index: usize,
+ first_iter: bool,
const Self = @This();
@@ -493,6 +507,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,
@@ -565,6 +583,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;
@@ -576,6 +595,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 => {},
@@ -622,7 +645,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();
@@ -647,9 +670,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;
@@ -771,18 +794,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,
},
diff --git a/lib/std/fs/test.zig b/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;
}