std.fs: split Dir into IterableDir

Also adds safety check for attempting to iterate directory not opened with `iterate = true`.
This commit is contained in:
Veikka Tuominen 2022-07-09 18:59:21 +03:00
parent 577f9fdbae
commit 2b67f56c35
10 changed files with 145 additions and 104 deletions

View File

@ -3245,7 +3245,7 @@ pub const LibExeObjStep = struct {
const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
if (self.output_dir) |output_dir| {
var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true });
var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
defer src_dir.close();
// Create the output directory if it doesn't exist.
@ -3265,7 +3265,7 @@ pub const LibExeObjStep = struct {
mem.eql(u8, entry.name, "zld.id") or
mem.eql(u8, entry.name, "lld.id")) continue;
_ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{});
_ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
}
} else {
self.output_dir = build_output_dir;
@ -3480,7 +3480,7 @@ pub const InstallDirStep = struct {
const self = @fieldParentPtr(InstallDirStep, "step", step);
const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
const full_src_dir = self.builder.pathFromRoot(self.options.source_dir);
var src_dir = try std.fs.cwd().openDir(full_src_dir, .{ .iterate = true });
var src_dir = try std.fs.cwd().openIterableDir(full_src_dir, .{});
defer src_dir.close();
var it = try src_dir.walk(self.builder.allocator);
next_entry: while (try it.next()) |entry| {

View File

@ -283,8 +283,10 @@ pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_
return os.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);
}
pub const Dir = struct {
fd: os.fd_t,
/// A directory that can be iterated. It is *NOT* legal to initialize this with a regular `Dir`
/// that has been opened without iteration permission.
pub const IterableDir = struct {
dir: Dir,
pub const Entry = struct {
name: []const u8,
@ -779,7 +781,7 @@ pub const Dir = struct {
else => @compileError("unimplemented"),
};
pub fn iterate(self: Dir) Iterator {
pub fn iterate(self: IterableDir) Iterator {
switch (builtin.os.tag) {
.macos,
.ios,
@ -789,7 +791,7 @@ pub const Dir = struct {
.openbsd,
.solaris,
=> return Iterator{
.dir = self,
.dir = self.dir,
.seek = 0,
.index = 0,
.end_index = 0,
@ -797,14 +799,14 @@ pub const Dir = struct {
.first_iter = true,
},
.linux, .haiku => return Iterator{
.dir = self,
.dir = self.dir,
.index = 0,
.end_index = 0,
.buf = undefined,
.first_iter = true,
},
.windows => return Iterator{
.dir = self,
.dir = self.dir,
.index = 0,
.end_index = 0,
.first_iter = true,
@ -812,7 +814,7 @@ pub const Dir = struct {
.name_data = undefined,
},
.wasi => return Iterator{
.dir = self,
.dir = self.dir,
.cookie = os.wasi.DIRCOOKIE_START,
.index = 0,
.end_index = 0,
@ -833,11 +835,11 @@ pub const Dir = struct {
dir: Dir,
basename: []const u8,
path: []const u8,
kind: Dir.Entry.Kind,
kind: IterableDir.Entry.Kind,
};
const StackItem = struct {
iter: Dir.Iterator,
iter: IterableDir.Iterator,
dirname_len: usize,
};
@ -857,7 +859,7 @@ pub const Dir = struct {
}
try self.name_buffer.appendSlice(base.name);
if (base.kind == .Directory) {
var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
var new_dir = top.iter.dir.openIterableDir(base.name, .{}) catch |err| switch (err) {
error.NameTooLong => unreachable, // no path sep in base.name
else => |e| return e,
};
@ -896,11 +898,10 @@ pub const Dir = struct {
};
/// Recursively iterates over a directory.
/// `self` must have been opened with `OpenDirOptions{.iterate = true}`.
/// Must call `Walker.deinit` when done.
/// The order of returned file system entries is undefined.
/// `self` will not be closed after walking it.
pub fn walk(self: Dir, allocator: Allocator) !Walker {
pub fn walk(self: IterableDir, allocator: Allocator) !Walker {
var name_buffer = std.ArrayList(u8).init(allocator);
errdefer name_buffer.deinit();
@ -918,6 +919,52 @@ pub const Dir = struct {
};
}
pub fn close(self: *IterableDir) void {
self.dir.close();
self.* = undefined;
}
pub const ChmodError = File.ChmodError;
/// Changes the mode of the directory.
/// The process must have the correct privileges in order to do this
/// successfully, or must have the effective user ID matching the owner
/// of the directory.
pub fn chmod(self: IterableDir, new_mode: File.Mode) ChmodError!void {
const file: File = .{
.handle = self.dir.fd,
.capable_io_mode = .blocking,
};
try file.chmod(new_mode);
}
/// Changes the owner and group of the directory.
/// The process must have the correct privileges in order to do this
/// successfully. The group may be changed by the owner of the directory to
/// any group of which the owner is a member. If the
/// owner or group is specified as `null`, the ID is not changed.
pub fn chown(self: IterableDir, owner: ?File.Uid, group: ?File.Gid) ChownError!void {
const file: File = .{
.handle = self.dir.fd,
.capable_io_mode = .blocking,
};
try file.chown(owner, group);
}
pub const ChownError = File.ChownError;
};
pub const Dir = struct {
fd: os.fd_t,
iterable: @TypeOf(iterable_safety) = iterable_safety,
const iterable_safety = if (builtin.mode == .Debug) false else {};
pub const iterate = @compileError("only 'IterableDir' can be iterated; 'IterableDir' can be obtained with 'openIterableDir' or by opening with 'iterate = true' and using 'intoIterable'");
pub const walk = @compileError("only 'IterableDir' can be walked; 'IterableDir' can be obtained with 'openIterableDir' or by opening with 'iterate = true' and using 'intoIterable'");
pub const chmod = @compileError("only 'IterableDir' can have its mode changed; 'IterableDir' can be obtained with 'openIterableDir' or by opening with 'iterate = true' and using 'intoIterable'");
pub const chown = @compileError("only 'IterableDir' can have its owner changed; 'IterableDir' can be obtained with 'openIterableDir' or by opening with 'iterate = true' and using 'intoIterable'");
pub const OpenError = error{
FileNotFound,
NotDir,
@ -1507,6 +1554,26 @@ pub const Dir = struct {
}
}
/// Opens an iterable directory at the given path. The directory is a system resource that remains
/// open until `close` is called on the result.
///
/// Asserts that the path parameter has no null bytes.
pub fn openIterableDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!IterableDir {
var adjusted_args = args;
adjusted_args.iterate = true;
const new_dir = try self.openDir(sub_path, adjusted_args);
return IterableDir{ .dir = new_dir };
}
/// Convert `self` into an iterable directory.
/// Asserts that `self` was opened with `iterate = true`.
pub fn intoIterable(self: Dir) IterableDir {
if (builtin.mode == .Debug) {
assert(self.iterable);
}
return .{ .dir = self };
}
/// Same as `openDir` except only WASI.
pub fn openDirWasi(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
const w = os.wasi;
@ -1552,7 +1619,7 @@ pub const Dir = struct {
error.FileBusy => unreachable, // can't happen for directories
else => |e| return e,
};
return Dir{ .fd = fd };
return Dir{ .fd = fd, .iterable = if (builtin.mode == .Debug) args.iterate else {} };
}
/// Same as `openDir` except the parameter is null-terminated.
@ -1566,7 +1633,9 @@ pub const Dir = struct {
const O_PATH = if (@hasDecl(os.O, "PATH")) os.O.PATH else 0;
return self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | O_PATH | symlink_flags);
} else {
return self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | symlink_flags);
var dir = try self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | symlink_flags);
if (builtin.mode == .Debug) dir.iterable = true;
return dir;
}
}
@ -1578,7 +1647,9 @@ pub const Dir = struct {
const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
w.SYNCHRONIZE | w.FILE_TRAVERSE;
const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags;
return self.openDirAccessMaskW(sub_path_w, flags, args.no_follow);
var dir = try self.openDirAccessMaskW(sub_path_w, flags, args.no_follow);
if (builtin.mode == .Debug) dir.iterable = args.iterate;
return dir;
}
/// `flags` must contain `os.O.DIRECTORY`.
@ -1958,7 +2029,7 @@ pub const Dir = struct {
error.Unexpected,
=> |e| return e,
}
var dir = self.openDir(sub_path, .{ .iterate = true, .no_follow = true }) catch |err| switch (err) {
var iterable_dir = self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) {
error.NotDir => {
if (got_access_denied) {
return error.AccessDenied;
@ -1984,11 +2055,11 @@ pub const Dir = struct {
error.DeviceBusy,
=> |e| return e,
};
var cleanup_dir_parent: ?Dir = null;
var cleanup_dir_parent: ?IterableDir = null;
defer if (cleanup_dir_parent) |*d| d.close();
var cleanup_dir = true;
defer if (cleanup_dir) dir.close();
defer if (cleanup_dir) iterable_dir.close();
// Valid use of MAX_PATH_BYTES because dir_name_buf will only
// ever store a single path component that was returned from the
@ -2001,9 +2072,9 @@ pub const Dir = struct {
// open it, and close the original directory. Repeat. Then start the entire operation over.
scan_dir: while (true) {
var dir_it = dir.iterate();
var dir_it = iterable_dir.iterate();
while (try dir_it.next()) |entry| {
if (dir.deleteFile(entry.name)) {
if (iterable_dir.dir.deleteFile(entry.name)) {
continue;
} else |err| switch (err) {
error.FileNotFound => continue,
@ -2026,7 +2097,7 @@ pub const Dir = struct {
=> |e| return e,
}
const new_dir = dir.openDir(entry.name, .{ .iterate = true, .no_follow = true }) catch |err| switch (err) {
const new_dir = iterable_dir.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) {
error.NotDir => {
if (got_access_denied) {
return error.AccessDenied;
@ -2053,19 +2124,19 @@ pub const Dir = struct {
=> |e| return e,
};
if (cleanup_dir_parent) |*d| d.close();
cleanup_dir_parent = dir;
dir = new_dir;
cleanup_dir_parent = iterable_dir;
iterable_dir = new_dir;
mem.copy(u8, &dir_name_buf, entry.name);
dir_name = dir_name_buf[0..entry.name.len];
continue :scan_dir;
}
// Reached the end of the directory entries, which means we successfully deleted all of them.
// Now to remove the directory itself.
dir.close();
iterable_dir.close();
cleanup_dir = false;
if (cleanup_dir_parent) |d| {
d.deleteDir(dir_name) catch |err| switch (err) {
d.dir.deleteDir(dir_name) catch |err| switch (err) {
// These two things can happen due to file system race conditions.
error.FileNotFound, error.DirNotEmpty => continue :start_over,
else => |e| return e,
@ -2246,37 +2317,6 @@ pub const Dir = struct {
return file.stat();
}
pub const ChmodError = File.ChmodError;
/// Changes the mode of the directory.
/// The process must have the correct privileges in order to do this
/// successfully, or must have the effective user ID matching the owner
/// of the directory. Additionally, the directory must have been opened
/// with `OpenDirOptions{ .iterate = true }`.
pub fn chmod(self: Dir, new_mode: File.Mode) ChmodError!void {
const file: File = .{
.handle = self.fd,
.capable_io_mode = .blocking,
};
try file.chmod(new_mode);
}
/// Changes the owner and group of the directory.
/// The process must have the correct privileges in order to do this
/// successfully. The group may be changed by the owner of the directory to
/// any group of which the owner is a member. Additionally, the directory
/// must have been opened with `OpenDirOptions{ .iterate = true }`. If the
/// owner or group is specified as `null`, the ID is not changed.
pub fn chown(self: Dir, owner: ?File.Uid, group: ?File.Gid) ChownError!void {
const file: File = .{
.handle = self.fd,
.capable_io_mode = .blocking,
};
try file.chown(owner, group);
}
pub const ChownError = File.ChownError;
const Permissions = File.Permissions;
pub const SetPermissionsError = File.SetPermissionsError;

View File

@ -8,6 +8,7 @@ const wasi = std.os.wasi;
const ArenaAllocator = std.heap.ArenaAllocator;
const Dir = std.fs.Dir;
const IterableDir = std.fs.IterableDir;
const File = std.fs.File;
const tmpDir = testing.tmpDir;
@ -168,20 +169,20 @@ test "Dir.Iterator" {
defer arena.deinit();
const allocator = arena.allocator();
var entries = std.ArrayList(Dir.Entry).init(allocator);
var entries = std.ArrayList(IterableDir.Entry).init(allocator);
// Create iterator.
var iter = tmp_dir.dir.iterate();
var iter = tmp_dir.dir.intoIterable().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 entries.append(.{ .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 }));
try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .File }));
try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .Directory }));
}
test "Dir.Iterator twice" {
@ -200,28 +201,28 @@ test "Dir.Iterator twice" {
var i: u8 = 0;
while (i < 2) : (i += 1) {
var entries = std.ArrayList(Dir.Entry).init(allocator);
var entries = std.ArrayList(IterableDir.Entry).init(allocator);
// Create iterator.
var iter = tmp_dir.dir.iterate();
var iter = tmp_dir.dir.intoIterable().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 entries.append(.{ .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 }));
try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .File }));
try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .Directory }));
}
}
fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {
return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
}
fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
fn contains(entries: *const std.ArrayList(IterableDir.Entry), el: IterableDir.Entry) bool {
for (entries.items) |entry| {
if (entryEql(entry, el)) return true;
}
@ -1014,7 +1015,7 @@ test "walker" {
try tmp.dir.makePath(kv.key);
}
var walker = try tmp.dir.walk(testing.allocator);
var walker = try tmp.dir.intoIterable().walk(testing.allocator);
defer walker.deinit();
var num_walked: usize = 0;
@ -1121,11 +1122,11 @@ test "chmod" {
try testing.expect((try file.stat()).mode & 0o7777 == 0o644);
try tmp.dir.makeDir("test_dir");
var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true });
defer dir.close();
var iterable_dir = try tmp.dir.openIterableDir("test_dir", .{});
defer iterable_dir.close();
try dir.chmod(0o700);
try testing.expect((try dir.stat()).mode & 0o7777 == 0o700);
try iterable_dir.chmod(0o700);
try testing.expect((try iterable_dir.stat()).mode & 0o7777 == 0o700);
}
test "chown" {
@ -1141,9 +1142,9 @@ test "chown" {
try tmp.dir.makeDir("test_dir");
var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true });
defer dir.close();
try dir.chown(null, null);
var iterable_dir = try tmp.dir.openDir("test_dir", .{});
defer iterable_dir.close();
try iterable_dir.chown(null, null);
}
test "File.Metadata" {

View File

@ -4206,13 +4206,13 @@ fn fmtPathDir(
parent_dir: fs.Dir,
parent_sub_path: []const u8,
) FmtError!void {
var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
defer dir.close();
var iterable_dir = try parent_dir.openIterableDir(parent_sub_path, .{});
defer iterable_dir.close();
const stat = try dir.stat();
const stat = try iterable_dir.dir.stat();
if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
var dir_it = dir.iterate();
var dir_it = iterable_dir.iterate();
while (try dir_it.next()) |entry| {
const is_dir = entry.kind == .Directory;
@ -4223,9 +4223,9 @@ fn fmtPathDir(
defer fmt.gpa.free(full_path);
if (is_dir) {
try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
try fmtPathDir(fmt, full_path, check_mode, iterable_dir.dir, entry.name);
} else {
fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
fmtPathFile(fmt, full_path, check_mode, iterable_dir.dir, entry.name) catch |err| {
warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });
fmt.any_error = true;
return;

View File

@ -1096,7 +1096,7 @@ pub const TestContext = struct {
/// that if any errors occur the caller knows it happened during this file.
current_file: *[]const u8,
) !void {
var it = try dir.walk(ctx.arena);
var it = try dir.intoIterable().walk(ctx.arena);
var filenames = std.ArrayList([]const u8).init(ctx.arena);
while (try it.next()) |entry| {

View File

@ -381,14 +381,14 @@ pub fn main() !void {
try dir_stack.append(target_include_dir);
while (dir_stack.popOrNull()) |full_dir_name| {
var dir = std.fs.cwd().openDir(full_dir_name, .{ .iterate = true }) catch |err| switch (err) {
var iterable_dir = std.fs.cwd().openIterableDir(full_dir_name, .{}) catch |err| switch (err) {
error.FileNotFound => continue :search,
error.AccessDenied => continue :search,
else => return err,
};
defer dir.close();
defer iterable_dir.close();
var dir_it = dir.iterate();
var dir_it = iterable_dir.iterate();
while (try dir_it.next()) |entry| {
const full_path = try std.fs.path.join(allocator, &[_][]const u8{ full_dir_name, entry.name });

View File

@ -14,9 +14,9 @@ pub fn main() !void {
const args = try std.process.argsAlloc(arena);
const path_to_walk = args[1];
const dir = try std.fs.cwd().openDir(path_to_walk, .{ .iterate = true });
const iterable_dir = try std.fs.cwd().openIterableDir(path_to_walk, .{});
var walker = try dir.walk(arena);
var walker = try iterable_dir.walk(arena);
defer walker.deinit();
var buffer: [500]u8 = undefined;
@ -30,7 +30,7 @@ pub fn main() !void {
node.activate();
defer node.end();
const source = try dir.readFileAlloc(arena, entry.path, 20 * 1024 * 1024);
const source = try iterable_dir.dir.readFileAlloc(arena, entry.path, 20 * 1024 * 1024);
if (!std.mem.startsWith(u8, source, expected_header)) {
std.debug.print("no match: {s}\n", .{entry.path});
continue;
@ -42,6 +42,6 @@ pub fn main() !void {
std.mem.copy(u8, new_source, new_header);
std.mem.copy(u8, new_source[new_header.len..], truncated_source);
try dir.writeFile(entry.path, new_source);
try iterable_dir.dir.writeFile(entry.path, new_source);
}
}

View File

@ -181,14 +181,14 @@ pub fn main() !void {
try dir_stack.append(target_include_dir);
while (dir_stack.popOrNull()) |full_dir_name| {
var dir = std.fs.cwd().openDir(full_dir_name, .{ .iterate = true }) catch |err| switch (err) {
var iterable_dir = std.fs.cwd().openIterableDir(full_dir_name, .{}) catch |err| switch (err) {
error.FileNotFound => continue :search,
error.AccessDenied => continue :search,
else => return err,
};
defer dir.close();
defer iterable_dir.close();
var dir_it = dir.iterate();
var dir_it = iterable_dir.iterate();
while (try dir_it.next()) |entry| {
const full_path = try std.fs.path.join(arena, &[_][]const u8{ full_dir_name, entry.name });

View File

@ -41,7 +41,7 @@ pub fn main() !void {
const dest_dir_path = try std.fmt.allocPrint(arena, "{s}/lib/libc/glibc", .{zig_src_path});
var dest_dir = fs.cwd().openDir(dest_dir_path, .{ .iterate = true }) catch |err| {
var dest_dir = fs.cwd().openIterableDir(dest_dir_path, .{}) catch |err| {
fatal("unable to open destination directory '{s}': {s}", .{
dest_dir_path, @errorName(err),
});
@ -63,14 +63,14 @@ pub fn main() !void {
if (mem.eql(u8, entry.path, p)) continue :walk;
}
glibc_src_dir.copyFile(entry.path, dest_dir, entry.path, .{}) catch |err| {
glibc_src_dir.copyFile(entry.path, dest_dir.dir, entry.path, .{}) catch |err| {
log.warn("unable to copy '{s}/{s}' to '{s}/{s}': {s}", .{
glibc_src_path, entry.path,
dest_dir_path, entry.path,
@errorName(err),
});
if (err == error.FileNotFound) {
try dest_dir.deleteFile(entry.path);
try dest_dir.dir.deleteFile(entry.path);
}
};
}
@ -79,7 +79,7 @@ pub fn main() !void {
// Warn about duplicated files inside glibc/include/* that can be omitted
// because they are already in generic-glibc/*.
var include_dir = dest_dir.openDir("include", .{ .iterate = true }) catch |err| {
var include_dir = dest_dir.dir.openIterableDir("include", .{}) catch |err| {
fatal("unable to open directory '{s}/include': {s}", .{
dest_dir_path, @errorName(err),
});
@ -116,7 +116,7 @@ pub fn main() !void {
generic_glibc_path, entry.path, @errorName(e),
}),
};
const glibc_include_contents = include_dir.readFileAlloc(
const glibc_include_contents = include_dir.dir.readFileAlloc(
arena,
entry.path,
max_file_size,

View File

@ -218,7 +218,7 @@ pub fn main() !void {
/// TODO: Unfortunately, neither repository contains a machine-readable list of extension dependencies.
fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]const []const u8 {
const extensions_path = try fs.path.join(allocator, &.{ spirv_registry_root, "extensions" });
var extensions_dir = try fs.cwd().openDir(extensions_path, .{ .iterate = true });
var extensions_dir = try fs.cwd().openIterableDir(extensions_path, .{});
defer extensions_dir.close();
var extensions = std.ArrayList([]const u8).init(allocator);
@ -227,7 +227,7 @@ fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]c
while (try vendor_it.next()) |vendor_entry| {
std.debug.assert(vendor_entry.kind == .Directory); // If this fails, the structure of SPIRV-Registry has changed.
const vendor_dir = try extensions_dir.openDir(vendor_entry.name, .{ .iterate = true });
const vendor_dir = try extensions_dir.dir.openIterableDir(vendor_entry.name, .{});
var ext_it = vendor_dir.iterate();
while (try ext_it.next()) |ext_entry| {
// There is both a HTML and asciidoc version of every spec (as well as some other directories),
@ -250,7 +250,7 @@ fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]c
// SPV_EXT_name
// ```
const ext_spec = try vendor_dir.readFileAlloc(allocator, ext_entry.name, std.math.maxInt(usize));
const ext_spec = try vendor_dir.dir.readFileAlloc(allocator, ext_entry.name, std.math.maxInt(usize));
const name_strings = "Name Strings";
const name_strings_offset = std.mem.indexOf(u8, ext_spec, name_strings) orelse return error.InvalidRegistry;