authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-07-16 12:18:21+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-16 12:18:21+03:00
logda94227f783ec3c92859c4713b80a668f1183f96
tree74e36b830f8fdff1c836df1c74024c5477bd0f7c
parent8f943b3d33432a26b7e242c1181e4220ed400501
parent262f4c7b3a850594a75ec154db2ba8d5f9f517ab
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12060 from Vexu/IterableDir

std.fs: split `Dir` into `IterableDir`

12 files changed, 201 insertions(+), 126 deletions(-)

lib/std/build.zig+3-3
...@@ -3245,7 +3245,7 @@ pub const LibExeObjStep = struct {...@@ -3245,7 +3245,7 @@ pub const LibExeObjStep = struct {
3245 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");3245 const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
32463246
3247 if (self.output_dir) |output_dir| {3247 if (self.output_dir) |output_dir| {
3248 var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true });3248 var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{});
3249 defer src_dir.close();3249 defer src_dir.close();
32503250
3251 // Create the output directory if it doesn't exist.3251 // Create the output directory if it doesn't exist.
...@@ -3265,7 +3265,7 @@ pub const LibExeObjStep = struct {...@@ -3265,7 +3265,7 @@ pub const LibExeObjStep = struct {
3265 mem.eql(u8, entry.name, "zld.id") or3265 mem.eql(u8, entry.name, "zld.id") or
3266 mem.eql(u8, entry.name, "lld.id")) continue;3266 mem.eql(u8, entry.name, "lld.id")) continue;
32673267
3268 _ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{});3268 _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{});
3269 }3269 }
3270 } else {3270 } else {
3271 self.output_dir = build_output_dir;3271 self.output_dir = build_output_dir;
...@@ -3480,7 +3480,7 @@ pub const InstallDirStep = struct {...@@ -3480,7 +3480,7 @@ pub const InstallDirStep = struct {
3480 const self = @fieldParentPtr(InstallDirStep, "step", step);3480 const self = @fieldParentPtr(InstallDirStep, "step", step);
3481 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);3481 const dest_prefix = self.builder.getInstallPath(self.options.install_dir, self.options.install_subdir);
3482 const full_src_dir = self.builder.pathFromRoot(self.options.source_dir);3482 const full_src_dir = self.builder.pathFromRoot(self.options.source_dir);
3483 var src_dir = try std.fs.cwd().openDir(full_src_dir, .{ .iterate = true });3483 var src_dir = try std.fs.cwd().openIterableDir(full_src_dir, .{});
3484 defer src_dir.close();3484 defer src_dir.close();
3485 var it = try src_dir.walk(self.builder.allocator);3485 var it = try src_dir.walk(self.builder.allocator);
3486 next_entry: while (try it.next()) |entry| {3486 next_entry: while (try it.next()) |entry| {
lib/std/fs.zig+99-64
...@@ -283,8 +283,10 @@ pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_...@@ -283,8 +283,10 @@ pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_
283 return os.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);283 return os.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);
284}284}
285285
286pub const Dir = struct {286/// A directory that can be iterated. It is *NOT* legal to initialize this with a regular `Dir`
287 fd: os.fd_t,287/// that has been opened without iteration permission.
288pub const IterableDir = struct {
289 dir: Dir,
288290
289 pub const Entry = struct {291 pub const Entry = struct {
290 name: []const u8,292 name: []const u8,
...@@ -779,7 +781,7 @@ pub const Dir = struct {...@@ -779,7 +781,7 @@ pub const Dir = struct {
779 else => @compileError("unimplemented"),781 else => @compileError("unimplemented"),
780 };782 };
781783
782 pub fn iterate(self: Dir) Iterator {784 pub fn iterate(self: IterableDir) Iterator {
783 switch (builtin.os.tag) {785 switch (builtin.os.tag) {
784 .macos,786 .macos,
785 .ios,787 .ios,
...@@ -789,7 +791,7 @@ pub const Dir = struct {...@@ -789,7 +791,7 @@ pub const Dir = struct {
789 .openbsd,791 .openbsd,
790 .solaris,792 .solaris,
791 => return Iterator{793 => return Iterator{
792 .dir = self,794 .dir = self.dir,
793 .seek = 0,795 .seek = 0,
794 .index = 0,796 .index = 0,
795 .end_index = 0,797 .end_index = 0,
...@@ -797,14 +799,14 @@ pub const Dir = struct {...@@ -797,14 +799,14 @@ pub const Dir = struct {
797 .first_iter = true,799 .first_iter = true,
798 },800 },
799 .linux, .haiku => return Iterator{801 .linux, .haiku => return Iterator{
800 .dir = self,802 .dir = self.dir,
801 .index = 0,803 .index = 0,
802 .end_index = 0,804 .end_index = 0,
803 .buf = undefined,805 .buf = undefined,
804 .first_iter = true,806 .first_iter = true,
805 },807 },
806 .windows => return Iterator{808 .windows => return Iterator{
807 .dir = self,809 .dir = self.dir,
808 .index = 0,810 .index = 0,
809 .end_index = 0,811 .end_index = 0,
810 .first_iter = true,812 .first_iter = true,
...@@ -812,7 +814,7 @@ pub const Dir = struct {...@@ -812,7 +814,7 @@ pub const Dir = struct {
812 .name_data = undefined,814 .name_data = undefined,
813 },815 },
814 .wasi => return Iterator{816 .wasi => return Iterator{
815 .dir = self,817 .dir = self.dir,
816 .cookie = os.wasi.DIRCOOKIE_START,818 .cookie = os.wasi.DIRCOOKIE_START,
817 .index = 0,819 .index = 0,
818 .end_index = 0,820 .end_index = 0,
...@@ -833,11 +835,11 @@ pub const Dir = struct {...@@ -833,11 +835,11 @@ pub const Dir = struct {
833 dir: Dir,835 dir: Dir,
834 basename: []const u8,836 basename: []const u8,
835 path: []const u8,837 path: []const u8,
836 kind: Dir.Entry.Kind,838 kind: IterableDir.Entry.Kind,
837 };839 };
838840
839 const StackItem = struct {841 const StackItem = struct {
840 iter: Dir.Iterator,842 iter: IterableDir.Iterator,
841 dirname_len: usize,843 dirname_len: usize,
842 };844 };
843845
...@@ -857,7 +859,7 @@ pub const Dir = struct {...@@ -857,7 +859,7 @@ pub const Dir = struct {
857 }859 }
858 try self.name_buffer.appendSlice(base.name);860 try self.name_buffer.appendSlice(base.name);
859 if (base.kind == .Directory) {861 if (base.kind == .Directory) {
860 var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {862 var new_dir = top.iter.dir.openIterableDir(base.name, .{}) catch |err| switch (err) {
861 error.NameTooLong => unreachable, // no path sep in base.name863 error.NameTooLong => unreachable, // no path sep in base.name
862 else => |e| return e,864 else => |e| return e,
863 };865 };
...@@ -896,11 +898,10 @@ pub const Dir = struct {...@@ -896,11 +898,10 @@ pub const Dir = struct {
896 };898 };
897899
898 /// Recursively iterates over a directory.900 /// Recursively iterates over a directory.
899 /// `self` must have been opened with `OpenDirOptions{.iterate = true}`.
900 /// Must call `Walker.deinit` when done.901 /// Must call `Walker.deinit` when done.
901 /// The order of returned file system entries is undefined.902 /// The order of returned file system entries is undefined.
902 /// `self` will not be closed after walking it.903 /// `self` will not be closed after walking it.
903 pub fn walk(self: Dir, allocator: Allocator) !Walker {904 pub fn walk(self: IterableDir, allocator: Allocator) !Walker {
904 var name_buffer = std.ArrayList(u8).init(allocator);905 var name_buffer = std.ArrayList(u8).init(allocator);
905 errdefer name_buffer.deinit();906 errdefer name_buffer.deinit();
906907
...@@ -918,6 +919,49 @@ pub const Dir = struct {...@@ -918,6 +919,49 @@ pub const Dir = struct {
918 };919 };
919 }920 }
920921
922 pub fn close(self: *IterableDir) void {
923 self.dir.close();
924 self.* = undefined;
925 }
926
927 pub const ChmodError = File.ChmodError;
928
929 /// Changes the mode of the directory.
930 /// The process must have the correct privileges in order to do this
931 /// successfully, or must have the effective user ID matching the owner
932 /// of the directory.
933 pub fn chmod(self: IterableDir, new_mode: File.Mode) ChmodError!void {
934 const file: File = .{
935 .handle = self.dir.fd,
936 .capable_io_mode = .blocking,
937 };
938 try file.chmod(new_mode);
939 }
940
941 /// Changes the owner and group of the directory.
942 /// The process must have the correct privileges in order to do this
943 /// successfully. The group may be changed by the owner of the directory to
944 /// any group of which the owner is a member. If the
945 /// owner or group is specified as `null`, the ID is not changed.
946 pub fn chown(self: IterableDir, owner: ?File.Uid, group: ?File.Gid) ChownError!void {
947 const file: File = .{
948 .handle = self.dir.fd,
949 .capable_io_mode = .blocking,
950 };
951 try file.chown(owner, group);
952 }
953
954 pub const ChownError = File.ChownError;
955};
956
957pub const Dir = struct {
958 fd: os.fd_t,
959
960 pub const iterate = @compileError("only 'IterableDir' can be iterated; 'IterableDir' can be obtained with 'openIterableDir'");
961 pub const walk = @compileError("only 'IterableDir' can be walked; 'IterableDir' can be obtained with 'openIterableDir'");
962 pub const chmod = @compileError("only 'IterableDir' can have its mode changed; 'IterableDir' can be obtained with 'openIterableDir'");
963 pub const chown = @compileError("only 'IterableDir' can have its owner changed; 'IterableDir' can be obtained with 'openIterableDir'");
964
921 pub const OpenError = error{965 pub const OpenError = error{
922 FileNotFound,966 FileNotFound,
923 NotDir,967 NotDir,
...@@ -1334,6 +1378,15 @@ pub const Dir = struct {...@@ -1334,6 +1378,15 @@ pub const Dir = struct {
1334 return self.openDir(sub_path, open_dir_options);1378 return self.openDir(sub_path, open_dir_options);
1335 }1379 }
13361380
1381 /// This function performs `makePath`, followed by `openIterableDir`.
1382 /// If supported by the OS, this operation is atomic. It is not atomic on
1383 /// all operating systems.
1384 pub fn makeOpenPathIterable(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !IterableDir {
1385 // TODO improve this implementation on Windows; we can avoid 1 call to NtClose
1386 try self.makePath(sub_path);
1387 return self.openIterableDir(sub_path, open_dir_options);
1388 }
1389
1337 /// This function returns the canonicalized absolute pathname of1390 /// This function returns the canonicalized absolute pathname of
1338 /// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this1391 /// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this
1339 /// `Dir` handle and returns the canonicalized absolute pathname of `pathname`1392 /// `Dir` handle and returns the canonicalized absolute pathname of `pathname`
...@@ -1483,10 +1536,6 @@ pub const Dir = struct {...@@ -1483,10 +1536,6 @@ pub const Dir = struct {
1483 /// such operations are Illegal Behavior.1536 /// such operations are Illegal Behavior.
1484 access_sub_paths: bool = true,1537 access_sub_paths: bool = true,
14851538
1486 /// `true` means the opened directory can be scanned for the files and sub-directories
1487 /// of the result. It means the `iterate` function can be called.
1488 iterate: bool = false,
1489
1490 /// `true` means it won't dereference the symlinks.1539 /// `true` means it won't dereference the symlinks.
1491 no_follow: bool = false,1540 no_follow: bool = false,
1492 };1541 };
...@@ -1498,12 +1547,28 @@ pub const Dir = struct {...@@ -1498,12 +1547,28 @@ pub const Dir = struct {
1498 pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {1547 pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
1499 if (builtin.os.tag == .windows) {1548 if (builtin.os.tag == .windows) {
1500 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);1549 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1501 return self.openDirW(sub_path_w.span().ptr, args);1550 return self.openDirW(sub_path_w.span().ptr, args, false);
1502 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {1551 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1503 return self.openDirWasi(sub_path, args);1552 return self.openDirWasi(sub_path, args);
1504 } else {1553 } else {
1505 const sub_path_c = try os.toPosixPath(sub_path);1554 const sub_path_c = try os.toPosixPath(sub_path);
1506 return self.openDirZ(&sub_path_c, args);1555 return self.openDirZ(&sub_path_c, args, false);
1556 }
1557 }
1558
1559 /// Opens an iterable directory at the given path. The directory is a system resource that remains
1560 /// open until `close` is called on the result.
1561 ///
1562 /// Asserts that the path parameter has no null bytes.
1563 pub fn openIterableDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!IterableDir {
1564 if (builtin.os.tag == .windows) {
1565 const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
1566 return IterableDir{ .dir = try self.openDirW(sub_path_w.span().ptr, args, true) };
1567 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1568 return IterableDir{ .dir = try self.openDirWasi(sub_path, args) };
1569 } else {
1570 const sub_path_c = try os.toPosixPath(sub_path);
1571 return IterableDir{ .dir = try self.openDirZ(&sub_path_c, args, true) };
1507 }1572 }
1508 }1573 }
15091574
...@@ -1556,13 +1621,13 @@ pub const Dir = struct {...@@ -1556,13 +1621,13 @@ pub const Dir = struct {
1556 }1621 }
15571622
1558 /// Same as `openDir` except the parameter is null-terminated.1623 /// Same as `openDir` except the parameter is null-terminated.
1559 pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {1624 pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions, iterable: bool) OpenError!Dir {
1560 if (builtin.os.tag == .windows) {1625 if (builtin.os.tag == .windows) {
1561 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);1626 const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
1562 return self.openDirW(sub_path_w.span().ptr, args);1627 return self.openDirW(sub_path_w.span().ptr, args);
1563 }1628 }
1564 const symlink_flags: u32 = if (args.no_follow) os.O.NOFOLLOW else 0x0;1629 const symlink_flags: u32 = if (args.no_follow) os.O.NOFOLLOW else 0x0;
1565 if (!args.iterate) {1630 if (!iterable) {
1566 const O_PATH = if (@hasDecl(os.O, "PATH")) os.O.PATH else 0;1631 const O_PATH = if (@hasDecl(os.O, "PATH")) os.O.PATH else 0;
1567 return self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | O_PATH | symlink_flags);1632 return self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | O_PATH | symlink_flags);
1568 } else {1633 } else {
...@@ -1572,13 +1637,14 @@ pub const Dir = struct {...@@ -1572,13 +1637,14 @@ pub const Dir = struct {
15721637
1573 /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed.1638 /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed.
1574 /// This function asserts the target OS is Windows.1639 /// This function asserts the target OS is Windows.
1575 pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir {1640 pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions, iterable: bool) OpenError!Dir {
1576 const w = os.windows;1641 const w = os.windows;
1577 // TODO remove some of these flags if args.access_sub_paths is false1642 // TODO remove some of these flags if args.access_sub_paths is false
1578 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |1643 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
1579 w.SYNCHRONIZE | w.FILE_TRAVERSE;1644 w.SYNCHRONIZE | w.FILE_TRAVERSE;
1580 const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags;1645 const flags: u32 = if (iterable) base_flags | w.FILE_LIST_DIRECTORY else base_flags;
1581 return self.openDirAccessMaskW(sub_path_w, flags, args.no_follow);1646 var dir = try self.openDirAccessMaskW(sub_path_w, flags, args.no_follow);
1647 return dir;
1582 }1648 }
15831649
1584 /// `flags` must contain `os.O.DIRECTORY`.1650 /// `flags` must contain `os.O.DIRECTORY`.
...@@ -1958,7 +2024,7 @@ pub const Dir = struct {...@@ -1958,7 +2024,7 @@ pub const Dir = struct {
1958 error.Unexpected,2024 error.Unexpected,
1959 => |e| return e,2025 => |e| return e,
1960 }2026 }
1961 var dir = self.openDir(sub_path, .{ .iterate = true, .no_follow = true }) catch |err| switch (err) {2027 var iterable_dir = self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) {
1962 error.NotDir => {2028 error.NotDir => {
1963 if (got_access_denied) {2029 if (got_access_denied) {
1964 return error.AccessDenied;2030 return error.AccessDenied;
...@@ -1984,11 +2050,11 @@ pub const Dir = struct {...@@ -1984,11 +2050,11 @@ pub const Dir = struct {
1984 error.DeviceBusy,2050 error.DeviceBusy,
1985 => |e| return e,2051 => |e| return e,
1986 };2052 };
1987 var cleanup_dir_parent: ?Dir = null;2053 var cleanup_dir_parent: ?IterableDir = null;
1988 defer if (cleanup_dir_parent) |*d| d.close();2054 defer if (cleanup_dir_parent) |*d| d.close();
19892055
1990 var cleanup_dir = true;2056 var cleanup_dir = true;
1991 defer if (cleanup_dir) dir.close();2057 defer if (cleanup_dir) iterable_dir.close();
19922058
1993 // Valid use of MAX_PATH_BYTES because dir_name_buf will only2059 // Valid use of MAX_PATH_BYTES because dir_name_buf will only
1994 // ever store a single path component that was returned from the2060 // ever store a single path component that was returned from the
...@@ -2001,9 +2067,9 @@ pub const Dir = struct {...@@ -2001,9 +2067,9 @@ pub const Dir = struct {
2001 // open it, and close the original directory. Repeat. Then start the entire operation over.2067 // open it, and close the original directory. Repeat. Then start the entire operation over.
20022068
2003 scan_dir: while (true) {2069 scan_dir: while (true) {
2004 var dir_it = dir.iterate();2070 var dir_it = iterable_dir.iterate();
2005 while (try dir_it.next()) |entry| {2071 while (try dir_it.next()) |entry| {
2006 if (dir.deleteFile(entry.name)) {2072 if (iterable_dir.dir.deleteFile(entry.name)) {
2007 continue;2073 continue;
2008 } else |err| switch (err) {2074 } else |err| switch (err) {
2009 error.FileNotFound => continue,2075 error.FileNotFound => continue,
...@@ -2026,7 +2092,7 @@ pub const Dir = struct {...@@ -2026,7 +2092,7 @@ pub const Dir = struct {
2026 => |e| return e,2092 => |e| return e,
2027 }2093 }
20282094
2029 const new_dir = dir.openDir(entry.name, .{ .iterate = true, .no_follow = true }) catch |err| switch (err) {2095 const new_dir = iterable_dir.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) {
2030 error.NotDir => {2096 error.NotDir => {
2031 if (got_access_denied) {2097 if (got_access_denied) {
2032 return error.AccessDenied;2098 return error.AccessDenied;
...@@ -2053,19 +2119,19 @@ pub const Dir = struct {...@@ -2053,19 +2119,19 @@ pub const Dir = struct {
2053 => |e| return e,2119 => |e| return e,
2054 };2120 };
2055 if (cleanup_dir_parent) |*d| d.close();2121 if (cleanup_dir_parent) |*d| d.close();
2056 cleanup_dir_parent = dir;2122 cleanup_dir_parent = iterable_dir;
2057 dir = new_dir;2123 iterable_dir = new_dir;
2058 mem.copy(u8, &dir_name_buf, entry.name);2124 mem.copy(u8, &dir_name_buf, entry.name);
2059 dir_name = dir_name_buf[0..entry.name.len];2125 dir_name = dir_name_buf[0..entry.name.len];
2060 continue :scan_dir;2126 continue :scan_dir;
2061 }2127 }
2062 // Reached the end of the directory entries, which means we successfully deleted all of them.2128 // Reached the end of the directory entries, which means we successfully deleted all of them.
2063 // Now to remove the directory itself.2129 // Now to remove the directory itself.
2064 dir.close();2130 iterable_dir.close();
2065 cleanup_dir = false;2131 cleanup_dir = false;
20662132
2067 if (cleanup_dir_parent) |d| {2133 if (cleanup_dir_parent) |d| {
2068 d.deleteDir(dir_name) catch |err| switch (err) {2134 d.dir.deleteDir(dir_name) catch |err| switch (err) {
2069 // These two things can happen due to file system race conditions.2135 // These two things can happen due to file system race conditions.
2070 error.FileNotFound, error.DirNotEmpty => continue :start_over,2136 error.FileNotFound, error.DirNotEmpty => continue :start_over,
2071 else => |e| return e,2137 else => |e| return e,
...@@ -2246,37 +2312,6 @@ pub const Dir = struct {...@@ -2246,37 +2312,6 @@ pub const Dir = struct {
2246 return file.stat();2312 return file.stat();
2247 }2313 }
22482314
2249 pub const ChmodError = File.ChmodError;
2250
2251 /// Changes the mode of the directory.
2252 /// The process must have the correct privileges in order to do this
2253 /// successfully, or must have the effective user ID matching the owner
2254 /// of the directory. Additionally, the directory must have been opened
2255 /// with `OpenDirOptions{ .iterate = true }`.
2256 pub fn chmod(self: Dir, new_mode: File.Mode) ChmodError!void {
2257 const file: File = .{
2258 .handle = self.fd,
2259 .capable_io_mode = .blocking,
2260 };
2261 try file.chmod(new_mode);
2262 }
2263
2264 /// Changes the owner and group of the directory.
2265 /// The process must have the correct privileges in order to do this
2266 /// successfully. The group may be changed by the owner of the directory to
2267 /// any group of which the owner is a member. Additionally, the directory
2268 /// must have been opened with `OpenDirOptions{ .iterate = true }`. If the
2269 /// owner or group is specified as `null`, the ID is not changed.
2270 pub fn chown(self: Dir, owner: ?File.Uid, group: ?File.Gid) ChownError!void {
2271 const file: File = .{
2272 .handle = self.fd,
2273 .capable_io_mode = .blocking,
2274 };
2275 try file.chown(owner, group);
2276 }
2277
2278 pub const ChownError = File.ChownError;
2279
2280 const Permissions = File.Permissions;2315 const Permissions = File.Permissions;
2281 pub const SetPermissionsError = File.SetPermissionsError;2316 pub const SetPermissionsError = File.SetPermissionsError;
22822317
lib/std/fs/test.zig+30-28
...@@ -8,8 +8,10 @@ const wasi = std.os.wasi;...@@ -8,8 +8,10 @@ const wasi = std.os.wasi;
88
9const ArenaAllocator = std.heap.ArenaAllocator;9const ArenaAllocator = std.heap.ArenaAllocator;
10const Dir = std.fs.Dir;10const Dir = std.fs.Dir;
11const IterableDir = std.fs.IterableDir;
11const File = std.fs.File;12const File = std.fs.File;
12const tmpDir = testing.tmpDir;13const tmpDir = testing.tmpDir;
14const tmpIterableDir = testing.tmpIterableDir;
1315
14test "Dir.readLink" {16test "Dir.readLink" {
15 var tmp = tmpDir(.{});17 var tmp = tmpDir(.{});
...@@ -155,44 +157,44 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void...@@ -155,44 +157,44 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void
155}157}
156158
157test "Dir.Iterator" {159test "Dir.Iterator" {
158 var tmp_dir = tmpDir(.{ .iterate = true });160 var tmp_dir = tmpIterableDir(.{});
159 defer tmp_dir.cleanup();161 defer tmp_dir.cleanup();
160162
161 // First, create a couple of entries to iterate over.163 // First, create a couple of entries to iterate over.
162 const file = try tmp_dir.dir.createFile("some_file", .{});164 const file = try tmp_dir.iterable_dir.dir.createFile("some_file", .{});
163 file.close();165 file.close();
164166
165 try tmp_dir.dir.makeDir("some_dir");167 try tmp_dir.iterable_dir.dir.makeDir("some_dir");
166168
167 var arena = ArenaAllocator.init(testing.allocator);169 var arena = ArenaAllocator.init(testing.allocator);
168 defer arena.deinit();170 defer arena.deinit();
169 const allocator = arena.allocator();171 const allocator = arena.allocator();
170172
171 var entries = std.ArrayList(Dir.Entry).init(allocator);173 var entries = std.ArrayList(IterableDir.Entry).init(allocator);
172174
173 // Create iterator.175 // Create iterator.
174 var iter = tmp_dir.dir.iterate();176 var iter = tmp_dir.iterable_dir.iterate();
175 while (try iter.next()) |entry| {177 while (try iter.next()) |entry| {
176 // We cannot just store `entry` as on Windows, we're re-using the name buffer178 // We cannot just store `entry` as on Windows, we're re-using the name buffer
177 // which means we'll actually share the `name` pointer between entries!179 // which means we'll actually share the `name` pointer between entries!
178 const name = try allocator.dupe(u8, entry.name);180 const name = try allocator.dupe(u8, entry.name);
179 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });181 try entries.append(.{ .name = name, .kind = entry.kind });
180 }182 }
181183
182 try testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'184 try testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'
183 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_file", .kind = Dir.Entry.Kind.File }));185 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .File }));
184 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));186 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .Directory }));
185}187}
186188
187test "Dir.Iterator twice" {189test "Dir.Iterator twice" {
188 var tmp_dir = tmpDir(.{ .iterate = true });190 var tmp_dir = tmpIterableDir(.{});
189 defer tmp_dir.cleanup();191 defer tmp_dir.cleanup();
190192
191 // First, create a couple of entries to iterate over.193 // First, create a couple of entries to iterate over.
192 const file = try tmp_dir.dir.createFile("some_file", .{});194 const file = try tmp_dir.iterable_dir.dir.createFile("some_file", .{});
193 file.close();195 file.close();
194196
195 try tmp_dir.dir.makeDir("some_dir");197 try tmp_dir.iterable_dir.dir.makeDir("some_dir");
196198
197 var arena = ArenaAllocator.init(testing.allocator);199 var arena = ArenaAllocator.init(testing.allocator);
198 defer arena.deinit();200 defer arena.deinit();
...@@ -200,28 +202,28 @@ test "Dir.Iterator twice" {...@@ -200,28 +202,28 @@ test "Dir.Iterator twice" {
200202
201 var i: u8 = 0;203 var i: u8 = 0;
202 while (i < 2) : (i += 1) {204 while (i < 2) : (i += 1) {
203 var entries = std.ArrayList(Dir.Entry).init(allocator);205 var entries = std.ArrayList(IterableDir.Entry).init(allocator);
204206
205 // Create iterator.207 // Create iterator.
206 var iter = tmp_dir.dir.iterate();208 var iter = tmp_dir.iterable_dir.iterate();
207 while (try iter.next()) |entry| {209 while (try iter.next()) |entry| {
208 // We cannot just store `entry` as on Windows, we're re-using the name buffer210 // We cannot just store `entry` as on Windows, we're re-using the name buffer
209 // which means we'll actually share the `name` pointer between entries!211 // which means we'll actually share the `name` pointer between entries!
210 const name = try allocator.dupe(u8, entry.name);212 const name = try allocator.dupe(u8, entry.name);
211 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });213 try entries.append(.{ .name = name, .kind = entry.kind });
212 }214 }
213215
214 try testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'216 try testing.expect(entries.items.len == 2); // note that the Iterator skips '.' and '..'
215 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_file", .kind = Dir.Entry.Kind.File }));217 try testing.expect(contains(&entries, .{ .name = "some_file", .kind = .File }));
216 try testing.expect(contains(&entries, Dir.Entry{ .name = "some_dir", .kind = Dir.Entry.Kind.Directory }));218 try testing.expect(contains(&entries, .{ .name = "some_dir", .kind = .Directory }));
217 }219 }
218}220}
219221
220fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {222fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {
221 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;223 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
222}224}
223225
224fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {226fn contains(entries: *const std.ArrayList(IterableDir.Entry), el: IterableDir.Entry) bool {
225 for (entries.items) |entry| {227 for (entries.items) |entry| {
226 if (entryEql(entry, el)) return true;228 if (entryEql(entry, el)) return true;
227 }229 }
...@@ -985,7 +987,7 @@ test "walker" {...@@ -985,7 +987,7 @@ test "walker" {
985 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;987 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
986 if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");988 if (builtin.os.tag == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
987989
988 var tmp = tmpDir(.{ .iterate = true });990 var tmp = tmpIterableDir(.{});
989 defer tmp.cleanup();991 defer tmp.cleanup();
990992
991 // iteration order of walker is undefined, so need lookup maps to check against993 // iteration order of walker is undefined, so need lookup maps to check against
...@@ -1011,10 +1013,10 @@ test "walker" {...@@ -1011,10 +1013,10 @@ test "walker" {
1011 });1013 });
10121014
1013 for (expected_paths.kvs) |kv| {1015 for (expected_paths.kvs) |kv| {
1014 try tmp.dir.makePath(kv.key);1016 try tmp.iterable_dir.dir.makePath(kv.key);
1015 }1017 }
10161018
1017 var walker = try tmp.dir.walk(testing.allocator);1019 var walker = try tmp.iterable_dir.walk(testing.allocator);
1018 defer walker.deinit();1020 defer walker.deinit();
10191021
1020 var num_walked: usize = 0;1022 var num_walked: usize = 0;
...@@ -1121,11 +1123,11 @@ test "chmod" {...@@ -1121,11 +1123,11 @@ test "chmod" {
1121 try testing.expect((try file.stat()).mode & 0o7777 == 0o644);1123 try testing.expect((try file.stat()).mode & 0o7777 == 0o644);
11221124
1123 try tmp.dir.makeDir("test_dir");1125 try tmp.dir.makeDir("test_dir");
1124 var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true });1126 var iterable_dir = try tmp.dir.openIterableDir("test_dir", .{});
1125 defer dir.close();1127 defer iterable_dir.close();
11261128
1127 try dir.chmod(0o700);1129 try iterable_dir.chmod(0o700);
1128 try testing.expect((try dir.stat()).mode & 0o7777 == 0o700);1130 try testing.expect((try iterable_dir.dir.stat()).mode & 0o7777 == 0o700);
1129}1131}
11301132
1131test "chown" {1133test "chown" {
...@@ -1141,9 +1143,9 @@ test "chown" {...@@ -1141,9 +1143,9 @@ test "chown" {
11411143
1142 try tmp.dir.makeDir("test_dir");1144 try tmp.dir.makeDir("test_dir");
11431145
1144 var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true });1146 var iterable_dir = try tmp.dir.openIterableDir("test_dir", .{});
1145 defer dir.close();1147 defer iterable_dir.close();
1146 try dir.chown(null, null);1148 try iterable_dir.chown(null, null);
1147}1149}
11481150
1149test "File.Metadata" {1151test "File.Metadata" {
lib/std/os.zig+2-2
...@@ -308,7 +308,7 @@ pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {...@@ -308,7 +308,7 @@ pub fn fchmod(fd: fd_t, mode: mode_t) FChmodError!void {
308 switch (system.getErrno(res)) {308 switch (system.getErrno(res)) {
309 .SUCCESS => return,309 .SUCCESS => return,
310 .INTR => continue,310 .INTR => continue,
311 .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }`311 .BADF => unreachable, // Can be reached if the fd refers to a non-iterable directory.
312312
313 .FAULT => unreachable,313 .FAULT => unreachable,
314 .INVAL => unreachable,314 .INVAL => unreachable,
...@@ -349,7 +349,7 @@ pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {...@@ -349,7 +349,7 @@ pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {
349 switch (system.getErrno(res)) {349 switch (system.getErrno(res)) {
350 .SUCCESS => return,350 .SUCCESS => return,
351 .INTR => continue,351 .INTR => continue,
352 .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }`352 .BADF => unreachable, // Can be reached if the fd refers to a non-iterable directory.
353353
354 .FAULT => unreachable,354 .FAULT => unreachable,
355 .INVAL => unreachable,355 .INVAL => unreachable,
lib/std/testing.zig+38
...@@ -363,6 +363,22 @@ pub const TmpDir = struct {...@@ -363,6 +363,22 @@ pub const TmpDir = struct {
363 }363 }
364};364};
365365
366pub const TmpIterableDir = struct {
367 iterable_dir: std.fs.IterableDir,
368 parent_dir: std.fs.Dir,
369 sub_path: [sub_path_len]u8,
370
371 const random_bytes_count = 12;
372 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
373
374 pub fn cleanup(self: *TmpIterableDir) void {
375 self.iterable_dir.close();
376 self.parent_dir.deleteTree(&self.sub_path) catch {};
377 self.parent_dir.close();
378 self.* = undefined;
379 }
380};
381
366fn getCwdOrWasiPreopen() std.fs.Dir {382fn getCwdOrWasiPreopen() std.fs.Dir {
367 if (builtin.os.tag == .wasi and !builtin.link_libc) {383 if (builtin.os.tag == .wasi and !builtin.link_libc) {
368 var preopens = std.fs.wasi.PreopenList.init(allocator);384 var preopens = std.fs.wasi.PreopenList.init(allocator);
...@@ -400,6 +416,28 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {...@@ -400,6 +416,28 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
400 };416 };
401}417}
402418
419pub fn tmpIterableDir(opts: std.fs.Dir.OpenDirOptions) TmpIterableDir {
420 var random_bytes: [TmpIterableDir.random_bytes_count]u8 = undefined;
421 std.crypto.random.bytes(&random_bytes);
422 var sub_path: [TmpIterableDir.sub_path_len]u8 = undefined;
423 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
424
425 var cwd = getCwdOrWasiPreopen();
426 var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch
427 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
428 defer cache_dir.close();
429 var parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch
430 @panic("unable to make tmp dir for testing: unable to make and open zig-cache/tmp dir");
431 var dir = parent_dir.makeOpenPathIterable(&sub_path, opts) catch
432 @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
433
434 return .{
435 .iterable_dir = dir,
436 .parent_dir = parent_dir,
437 .sub_path = sub_path,
438 };
439}
440
403test "expectEqual nested array" {441test "expectEqual nested array" {
404 const a = [2][2]f32{442 const a = [2][2]f32{
405 [_]f32{ 1.0, 0.0 },443 [_]f32{ 1.0, 0.0 },
src/main.zig+6-6
...@@ -4212,13 +4212,13 @@ fn fmtPathDir(...@@ -4212,13 +4212,13 @@ fn fmtPathDir(
4212 parent_dir: fs.Dir,4212 parent_dir: fs.Dir,
4213 parent_sub_path: []const u8,4213 parent_sub_path: []const u8,
4214) FmtError!void {4214) FmtError!void {
4215 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });4215 var iterable_dir = try parent_dir.openIterableDir(parent_sub_path, .{});
4216 defer dir.close();4216 defer iterable_dir.close();
42174217
4218 const stat = try dir.stat();4218 const stat = try iterable_dir.dir.stat();
4219 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;4219 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
42204220
4221 var dir_it = dir.iterate();4221 var dir_it = iterable_dir.iterate();
4222 while (try dir_it.next()) |entry| {4222 while (try dir_it.next()) |entry| {
4223 const is_dir = entry.kind == .Directory;4223 const is_dir = entry.kind == .Directory;
42244224
...@@ -4229,9 +4229,9 @@ fn fmtPathDir(...@@ -4229,9 +4229,9 @@ fn fmtPathDir(
4229 defer fmt.gpa.free(full_path);4229 defer fmt.gpa.free(full_path);
42304230
4231 if (is_dir) {4231 if (is_dir) {
4232 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);4232 try fmtPathDir(fmt, full_path, check_mode, iterable_dir.dir, entry.name);
4233 } else {4233 } else {
4234 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {4234 fmtPathFile(fmt, full_path, check_mode, iterable_dir.dir, entry.name) catch |err| {
4235 warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });4235 warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });
4236 fmt.any_error = true;4236 fmt.any_error = true;
4237 return;4237 return;
src/test.zig+5-5
...@@ -54,7 +54,7 @@ test {...@@ -54,7 +54,7 @@ test {
54 std.fs.path.dirname(@src().file).?, "..", "test", "cases",54 std.fs.path.dirname(@src().file).?, "..", "test", "cases",
55 });55 });
5656
57 var dir = try std.fs.cwd().openDir(dir_path, .{ .iterate = true });57 var dir = try std.fs.cwd().openIterableDir(dir_path, .{});
58 defer dir.close();58 defer dir.close();
5959
60 ctx.addTestCasesFromDir(dir);60 ctx.addTestCasesFromDir(dir);
...@@ -1080,7 +1080,7 @@ pub const TestContext = struct {...@@ -1080,7 +1080,7 @@ pub const TestContext = struct {
1080 /// Each file should include a test manifest as a contiguous block of comments at1080 /// Each file should include a test manifest as a contiguous block of comments at
1081 /// the end of the file. The first line should be the test type, followed by a set of1081 /// the end of the file. The first line should be the test type, followed by a set of
1082 /// key-value config values, followed by a blank line, then the expected output.1082 /// key-value config values, followed by a blank line, then the expected output.
1083 pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.Dir) void {1083 pub fn addTestCasesFromDir(ctx: *TestContext, dir: std.fs.IterableDir) void {
1084 var current_file: []const u8 = "none";1084 var current_file: []const u8 = "none";
1085 ctx.addTestCasesFromDirInner(dir, &current_file) catch |err| {1085 ctx.addTestCasesFromDirInner(dir, &current_file) catch |err| {
1086 std.debug.panic("test harness failed to process file '{s}': {s}\n", .{1086 std.debug.panic("test harness failed to process file '{s}': {s}\n", .{
...@@ -1091,12 +1091,12 @@ pub const TestContext = struct {...@@ -1091,12 +1091,12 @@ pub const TestContext = struct {
10911091
1092 fn addTestCasesFromDirInner(1092 fn addTestCasesFromDirInner(
1093 ctx: *TestContext,1093 ctx: *TestContext,
1094 dir: std.fs.Dir,1094 iterable_dir: std.fs.IterableDir,
1095 /// This is kept up to date with the currently being processed file so1095 /// This is kept up to date with the currently being processed file so
1096 /// that if any errors occur the caller knows it happened during this file.1096 /// that if any errors occur the caller knows it happened during this file.
1097 current_file: *[]const u8,1097 current_file: *[]const u8,
1098 ) !void {1098 ) !void {
1099 var it = try dir.walk(ctx.arena);1099 var it = try iterable_dir.walk(ctx.arena);
1100 var filenames = std.ArrayList([]const u8).init(ctx.arena);1100 var filenames = std.ArrayList([]const u8).init(ctx.arena);
11011101
1102 while (try it.next()) |entry| {1102 while (try it.next()) |entry| {
...@@ -1123,7 +1123,7 @@ pub const TestContext = struct {...@@ -1123,7 +1123,7 @@ pub const TestContext = struct {
1123 current_file.* = filename;1123 current_file.* = filename;
11241124
1125 const max_file_size = 10 * 1024 * 1024;1125 const max_file_size = 10 * 1024 * 1024;
1126 const src = try dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0);1126 const src = try iterable_dir.dir.readFileAllocOptions(ctx.arena, filename, max_file_size, null, 1, 0);
11271127
1128 // Parse the manifest1128 // Parse the manifest
1129 var manifest = try TestManifest.parse(ctx.arena, src);1129 var manifest = try TestManifest.parse(ctx.arena, src);
tools/process_headers.zig+3-3
...@@ -381,14 +381,14 @@ pub fn main() !void {...@@ -381,14 +381,14 @@ pub fn main() !void {
381 try dir_stack.append(target_include_dir);381 try dir_stack.append(target_include_dir);
382382
383 while (dir_stack.popOrNull()) |full_dir_name| {383 while (dir_stack.popOrNull()) |full_dir_name| {
384 var dir = std.fs.cwd().openDir(full_dir_name, .{ .iterate = true }) catch |err| switch (err) {384 var iterable_dir = std.fs.cwd().openIterableDir(full_dir_name, .{}) catch |err| switch (err) {
385 error.FileNotFound => continue :search,385 error.FileNotFound => continue :search,
386 error.AccessDenied => continue :search,386 error.AccessDenied => continue :search,
387 else => return err,387 else => return err,
388 };388 };
389 defer dir.close();389 defer iterable_dir.close();
390390
391 var dir_it = dir.iterate();391 var dir_it = iterable_dir.iterate();
392392
393 while (try dir_it.next()) |entry| {393 while (try dir_it.next()) |entry| {
394 const full_path = try std.fs.path.join(allocator, &[_][]const u8{ full_dir_name, entry.name });394 const full_path = try std.fs.path.join(allocator, &[_][]const u8{ full_dir_name, entry.name });
tools/update-license-headers.zig+4-4
...@@ -14,9 +14,9 @@ pub fn main() !void {...@@ -14,9 +14,9 @@ pub fn main() !void {
1414
15 const args = try std.process.argsAlloc(arena);15 const args = try std.process.argsAlloc(arena);
16 const path_to_walk = args[1];16 const path_to_walk = args[1];
17 const dir = try std.fs.cwd().openDir(path_to_walk, .{ .iterate = true });17 const iterable_dir = try std.fs.cwd().openIterableDir(path_to_walk, .{});
1818
19 var walker = try dir.walk(arena);19 var walker = try iterable_dir.walk(arena);
20 defer walker.deinit();20 defer walker.deinit();
2121
22 var buffer: [500]u8 = undefined;22 var buffer: [500]u8 = undefined;
...@@ -30,7 +30,7 @@ pub fn main() !void {...@@ -30,7 +30,7 @@ pub fn main() !void {
30 node.activate();30 node.activate();
31 defer node.end();31 defer node.end();
3232
33 const source = try dir.readFileAlloc(arena, entry.path, 20 * 1024 * 1024);33 const source = try iterable_dir.dir.readFileAlloc(arena, entry.path, 20 * 1024 * 1024);
34 if (!std.mem.startsWith(u8, source, expected_header)) {34 if (!std.mem.startsWith(u8, source, expected_header)) {
35 std.debug.print("no match: {s}\n", .{entry.path});35 std.debug.print("no match: {s}\n", .{entry.path});
36 continue;36 continue;
...@@ -42,6 +42,6 @@ pub fn main() !void {...@@ -42,6 +42,6 @@ pub fn main() !void {
42 std.mem.copy(u8, new_source, new_header);42 std.mem.copy(u8, new_source, new_header);
43 std.mem.copy(u8, new_source[new_header.len..], truncated_source);43 std.mem.copy(u8, new_source[new_header.len..], truncated_source);
4444
45 try dir.writeFile(entry.path, new_source);45 try iterable_dir.dir.writeFile(entry.path, new_source);
46 }46 }
47}47}
tools/update-linux-headers.zig+3-3
...@@ -181,14 +181,14 @@ pub fn main() !void {...@@ -181,14 +181,14 @@ pub fn main() !void {
181 try dir_stack.append(target_include_dir);181 try dir_stack.append(target_include_dir);
182182
183 while (dir_stack.popOrNull()) |full_dir_name| {183 while (dir_stack.popOrNull()) |full_dir_name| {
184 var dir = std.fs.cwd().openDir(full_dir_name, .{ .iterate = true }) catch |err| switch (err) {184 var iterable_dir = std.fs.cwd().openIterableDir(full_dir_name, .{}) catch |err| switch (err) {
185 error.FileNotFound => continue :search,185 error.FileNotFound => continue :search,
186 error.AccessDenied => continue :search,186 error.AccessDenied => continue :search,
187 else => return err,187 else => return err,
188 };188 };
189 defer dir.close();189 defer iterable_dir.close();
190190
191 var dir_it = dir.iterate();191 var dir_it = iterable_dir.iterate();
192192
193 while (try dir_it.next()) |entry| {193 while (try dir_it.next()) |entry| {
194 const full_path = try std.fs.path.join(arena, &[_][]const u8{ full_dir_name, entry.name });194 const full_path = try std.fs.path.join(arena, &[_][]const u8{ full_dir_name, entry.name });
tools/update_glibc.zig+5-5
...@@ -41,7 +41,7 @@ pub fn main() !void {...@@ -41,7 +41,7 @@ pub fn main() !void {
4141
42 const dest_dir_path = try std.fmt.allocPrint(arena, "{s}/lib/libc/glibc", .{zig_src_path});42 const dest_dir_path = try std.fmt.allocPrint(arena, "{s}/lib/libc/glibc", .{zig_src_path});
4343
44 var dest_dir = fs.cwd().openDir(dest_dir_path, .{ .iterate = true }) catch |err| {44 var dest_dir = fs.cwd().openIterableDir(dest_dir_path, .{}) catch |err| {
45 fatal("unable to open destination directory '{s}': {s}", .{45 fatal("unable to open destination directory '{s}': {s}", .{
46 dest_dir_path, @errorName(err),46 dest_dir_path, @errorName(err),
47 });47 });
...@@ -63,14 +63,14 @@ pub fn main() !void {...@@ -63,14 +63,14 @@ pub fn main() !void {
63 if (mem.eql(u8, entry.path, p)) continue :walk;63 if (mem.eql(u8, entry.path, p)) continue :walk;
64 }64 }
6565
66 glibc_src_dir.copyFile(entry.path, dest_dir, entry.path, .{}) catch |err| {66 glibc_src_dir.copyFile(entry.path, dest_dir.dir, entry.path, .{}) catch |err| {
67 log.warn("unable to copy '{s}/{s}' to '{s}/{s}': {s}", .{67 log.warn("unable to copy '{s}/{s}' to '{s}/{s}': {s}", .{
68 glibc_src_path, entry.path,68 glibc_src_path, entry.path,
69 dest_dir_path, entry.path,69 dest_dir_path, entry.path,
70 @errorName(err),70 @errorName(err),
71 });71 });
72 if (err == error.FileNotFound) {72 if (err == error.FileNotFound) {
73 try dest_dir.deleteFile(entry.path);73 try dest_dir.dir.deleteFile(entry.path);
74 }74 }
75 };75 };
76 }76 }
...@@ -79,7 +79,7 @@ pub fn main() !void {...@@ -79,7 +79,7 @@ pub fn main() !void {
79 // Warn about duplicated files inside glibc/include/* that can be omitted79 // Warn about duplicated files inside glibc/include/* that can be omitted
80 // because they are already in generic-glibc/*.80 // because they are already in generic-glibc/*.
8181
82 var include_dir = dest_dir.openDir("include", .{ .iterate = true }) catch |err| {82 var include_dir = dest_dir.dir.openIterableDir("include", .{}) catch |err| {
83 fatal("unable to open directory '{s}/include': {s}", .{83 fatal("unable to open directory '{s}/include': {s}", .{
84 dest_dir_path, @errorName(err),84 dest_dir_path, @errorName(err),
85 });85 });
...@@ -116,7 +116,7 @@ pub fn main() !void {...@@ -116,7 +116,7 @@ pub fn main() !void {
116 generic_glibc_path, entry.path, @errorName(e),116 generic_glibc_path, entry.path, @errorName(e),
117 }),117 }),
118 };118 };
119 const glibc_include_contents = include_dir.readFileAlloc(119 const glibc_include_contents = include_dir.dir.readFileAlloc(
120 arena,120 arena,
121 entry.path,121 entry.path,
122 max_file_size,122 max_file_size,
tools/update_spirv_features.zig+3-3
...@@ -218,7 +218,7 @@ pub fn main() !void {...@@ -218,7 +218,7 @@ pub fn main() !void {
218/// TODO: Unfortunately, neither repository contains a machine-readable list of extension dependencies.218/// TODO: Unfortunately, neither repository contains a machine-readable list of extension dependencies.
219fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]const []const u8 {219fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]const []const u8 {
220 const extensions_path = try fs.path.join(allocator, &.{ spirv_registry_root, "extensions" });220 const extensions_path = try fs.path.join(allocator, &.{ spirv_registry_root, "extensions" });
221 var extensions_dir = try fs.cwd().openDir(extensions_path, .{ .iterate = true });221 var extensions_dir = try fs.cwd().openIterableDir(extensions_path, .{});
222 defer extensions_dir.close();222 defer extensions_dir.close();
223223
224 var extensions = std.ArrayList([]const u8).init(allocator);224 var extensions = std.ArrayList([]const u8).init(allocator);
...@@ -227,7 +227,7 @@ fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]c...@@ -227,7 +227,7 @@ fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]c
227 while (try vendor_it.next()) |vendor_entry| {227 while (try vendor_it.next()) |vendor_entry| {
228 std.debug.assert(vendor_entry.kind == .Directory); // If this fails, the structure of SPIRV-Registry has changed.228 std.debug.assert(vendor_entry.kind == .Directory); // If this fails, the structure of SPIRV-Registry has changed.
229229
230 const vendor_dir = try extensions_dir.openDir(vendor_entry.name, .{ .iterate = true });230 const vendor_dir = try extensions_dir.dir.openIterableDir(vendor_entry.name, .{});
231 var ext_it = vendor_dir.iterate();231 var ext_it = vendor_dir.iterate();
232 while (try ext_it.next()) |ext_entry| {232 while (try ext_it.next()) |ext_entry| {
233 // There is both a HTML and asciidoc version of every spec (as well as some other directories),233 // 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...@@ -250,7 +250,7 @@ fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]c
250 // SPV_EXT_name250 // SPV_EXT_name
251 // ```251 // ```
252252
253 const ext_spec = try vendor_dir.readFileAlloc(allocator, ext_entry.name, std.math.maxInt(usize));253 const ext_spec = try vendor_dir.dir.readFileAlloc(allocator, ext_entry.name, std.math.maxInt(usize));
254 const name_strings = "Name Strings";254 const name_strings = "Name Strings";
255255
256 const name_strings_offset = std.mem.indexOf(u8, ext_spec, name_strings) orelse return error.InvalidRegistry;256 const name_strings_offset = std.mem.indexOf(u8, ext_spec, name_strings) orelse return error.InvalidRegistry;