authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-22 12:35:33-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-11-22 12:35:33-07:00
log519ba9bb654a4d5caf7440160f018b7a3ae1e95a
tree2308d007f7e1bf1a8aed846ad4faef8e729e0598
parente4977f3e89fcc164a4d02cd38eb066cfe1a1124f

Revert "Merge pull request #12060 from Vexu/IterableDir"

This reverts commit da94227f783ec3c92859c4713b80a668f1183f96, reversing changes made to 8f943b3d33432a26b7e242c1181e4220ed400501. I was against this change originally, but decided to approve it to keep an open mind. After a year of trying it in practice, I firmly believe that the previous way of doing it was better.

10 files changed, 146 insertions(+), 244 deletions(-)

lib/std/fs.zig+88-146
...@@ -310,10 +310,8 @@ pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_...@@ -310,10 +310,8 @@ pub fn renameW(old_dir: Dir, old_sub_path_w: []const u16, new_dir: Dir, new_sub_
310 return os.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);310 return os.renameatW(old_dir.fd, old_sub_path_w, new_dir.fd, new_sub_path_w);
311}311}
312312
313/// A directory that can be iterated. It is *NOT* legal to initialize this with a regular `Dir`313pub const Dir = struct {
314/// that has been opened without iteration permission.314 fd: os.fd_t,
315pub const IterableDir = struct {
316 dir: Dir,
317315
318 pub const Entry = struct {316 pub const Entry = struct {
319 name: []const u8,317 name: []const u8,
...@@ -879,18 +877,18 @@ pub const IterableDir = struct {...@@ -879,18 +877,18 @@ pub const IterableDir = struct {
879 else => @compileError("unimplemented"),877 else => @compileError("unimplemented"),
880 };878 };
881879
882 pub fn iterate(self: IterableDir) Iterator {880 pub fn iterate(self: Dir) Iterator {
883 return self.iterateImpl(true);881 return self.iterateImpl(true);
884 }882 }
885883
886 /// Like `iterate`, but will not reset the directory cursor before the first884 /// Like `iterate`, but will not reset the directory cursor before the first
887 /// iteration. This should only be used in cases where it is known that the885 /// iteration. This should only be used in cases where it is known that the
888 /// `IterableDir` has not had its cursor modified yet (e.g. it was just opened).886 /// `Dir` has not had its cursor modified yet (e.g. it was just opened).
889 pub fn iterateAssumeFirstIteration(self: IterableDir) Iterator {887 pub fn iterateAssumeFirstIteration(self: Dir) Iterator {
890 return self.iterateImpl(false);888 return self.iterateImpl(false);
891 }889 }
892890
893 fn iterateImpl(self: IterableDir, first_iter_start_value: bool) Iterator {891 fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator {
894 switch (builtin.os.tag) {892 switch (builtin.os.tag) {
895 .macos,893 .macos,
896 .ios,894 .ios,
...@@ -901,7 +899,7 @@ pub const IterableDir = struct {...@@ -901,7 +899,7 @@ pub const IterableDir = struct {
901 .solaris,899 .solaris,
902 .illumos,900 .illumos,
903 => return Iterator{901 => return Iterator{
904 .dir = self.dir,902 .dir = self,
905 .seek = 0,903 .seek = 0,
906 .index = 0,904 .index = 0,
907 .end_index = 0,905 .end_index = 0,
...@@ -909,14 +907,14 @@ pub const IterableDir = struct {...@@ -909,14 +907,14 @@ pub const IterableDir = struct {
909 .first_iter = first_iter_start_value,907 .first_iter = first_iter_start_value,
910 },908 },
911 .linux, .haiku => return Iterator{909 .linux, .haiku => return Iterator{
912 .dir = self.dir,910 .dir = self,
913 .index = 0,911 .index = 0,
914 .end_index = 0,912 .end_index = 0,
915 .buf = undefined,913 .buf = undefined,
916 .first_iter = first_iter_start_value,914 .first_iter = first_iter_start_value,
917 },915 },
918 .windows => return Iterator{916 .windows => return Iterator{
919 .dir = self.dir,917 .dir = self,
920 .index = 0,918 .index = 0,
921 .end_index = 0,919 .end_index = 0,
922 .first_iter = first_iter_start_value,920 .first_iter = first_iter_start_value,
...@@ -924,7 +922,7 @@ pub const IterableDir = struct {...@@ -924,7 +922,7 @@ pub const IterableDir = struct {
924 .name_data = undefined,922 .name_data = undefined,
925 },923 },
926 .wasi => return Iterator{924 .wasi => return Iterator{
927 .dir = self.dir,925 .dir = self,
928 .cookie = os.wasi.DIRCOOKIE_START,926 .cookie = os.wasi.DIRCOOKIE_START,
929 .index = 0,927 .index = 0,
930 .end_index = 0,928 .end_index = 0,
...@@ -945,11 +943,11 @@ pub const IterableDir = struct {...@@ -945,11 +943,11 @@ pub const IterableDir = struct {
945 dir: Dir,943 dir: Dir,
946 basename: []const u8,944 basename: []const u8,
947 path: []const u8,945 path: []const u8,
948 kind: IterableDir.Entry.Kind,946 kind: Dir.Entry.Kind,
949 };947 };
950948
951 const StackItem = struct {949 const StackItem = struct {
952 iter: IterableDir.Iterator,950 iter: Dir.Iterator,
953 dirname_len: usize,951 dirname_len: usize,
954 };952 };
955953
...@@ -980,7 +978,7 @@ pub const IterableDir = struct {...@@ -980,7 +978,7 @@ pub const IterableDir = struct {
980 }978 }
981 try self.name_buffer.appendSlice(base.name);979 try self.name_buffer.appendSlice(base.name);
982 if (base.kind == .directory) {980 if (base.kind == .directory) {
983 var new_dir = top.iter.dir.openIterableDir(base.name, .{}) catch |err| switch (err) {981 var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
984 error.NameTooLong => unreachable, // no path sep in base.name982 error.NameTooLong => unreachable, // no path sep in base.name
985 else => |e| return e,983 else => |e| return e,
986 };984 };
...@@ -1023,10 +1021,11 @@ pub const IterableDir = struct {...@@ -1023,10 +1021,11 @@ pub const IterableDir = struct {
1023 };1021 };
10241022
1025 /// Recursively iterates over a directory.1023 /// Recursively iterates over a directory.
1024 /// `self` must have been opened with `OpenDirOptions{.iterate = true}`.
1026 /// Must call `Walker.deinit` when done.1025 /// Must call `Walker.deinit` when done.
1027 /// The order of returned file system entries is undefined.1026 /// The order of returned file system entries is undefined.
1028 /// `self` will not be closed after walking it.1027 /// `self` will not be closed after walking it.
1029 pub fn walk(self: IterableDir, allocator: Allocator) !Walker {1028 pub fn walk(self: Dir, allocator: Allocator) !Walker {
1030 var name_buffer = std.ArrayList(u8).init(allocator);1029 var name_buffer = std.ArrayList(u8).init(allocator);
1031 errdefer name_buffer.deinit();1030 errdefer name_buffer.deinit();
10321031
...@@ -1044,49 +1043,6 @@ pub const IterableDir = struct {...@@ -1044,49 +1043,6 @@ pub const IterableDir = struct {
1044 };1043 };
1045 }1044 }
10461045
1047 pub fn close(self: *IterableDir) void {
1048 self.dir.close();
1049 self.* = undefined;
1050 }
1051
1052 pub const ChmodError = File.ChmodError;
1053
1054 /// Changes the mode of the directory.
1055 /// The process must have the correct privileges in order to do this
1056 /// successfully, or must have the effective user ID matching the owner
1057 /// of the directory.
1058 pub fn chmod(self: IterableDir, new_mode: File.Mode) ChmodError!void {
1059 const file: File = .{
1060 .handle = self.dir.fd,
1061 .capable_io_mode = .blocking,
1062 };
1063 try file.chmod(new_mode);
1064 }
1065
1066 /// Changes the owner and group of the directory.
1067 /// The process must have the correct privileges in order to do this
1068 /// successfully. The group may be changed by the owner of the directory to
1069 /// any group of which the owner is a member. If the
1070 /// owner or group is specified as `null`, the ID is not changed.
1071 pub fn chown(self: IterableDir, owner: ?File.Uid, group: ?File.Gid) ChownError!void {
1072 const file: File = .{
1073 .handle = self.dir.fd,
1074 .capable_io_mode = .blocking,
1075 };
1076 try file.chown(owner, group);
1077 }
1078
1079 pub const ChownError = File.ChownError;
1080};
1081
1082pub const Dir = struct {
1083 fd: os.fd_t,
1084
1085 pub const iterate = @compileError("only 'IterableDir' can be iterated; 'IterableDir' can be obtained with 'openIterableDir'");
1086 pub const walk = @compileError("only 'IterableDir' can be walked; 'IterableDir' can be obtained with 'openIterableDir'");
1087 pub const chmod = @compileError("only 'IterableDir' can have its mode changed; 'IterableDir' can be obtained with 'openIterableDir'");
1088 pub const chown = @compileError("only 'IterableDir' can have its owner changed; 'IterableDir' can be obtained with 'openIterableDir'");
1089
1090 pub const OpenError = error{1046 pub const OpenError = error{
1091 FileNotFound,1047 FileNotFound,
1092 NotDir,1048 NotDir,
...@@ -1529,7 +1485,8 @@ pub const Dir = struct {...@@ -1529,7 +1485,8 @@ pub const Dir = struct {
1529 .windows => {1485 .windows => {
1530 const w = os.windows;1486 const w = os.windows;
1531 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |1487 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
1532 w.SYNCHRONIZE | w.FILE_TRAVERSE;1488 w.SYNCHRONIZE | w.FILE_TRAVERSE |
1489 (if (open_dir_options.iterate) w.FILE_LIST_DIRECTORY else 0);
15331490
1534 return self.makeOpenPathAccessMaskW(sub_path, base_flags, open_dir_options.no_follow);1491 return self.makeOpenPathAccessMaskW(sub_path, base_flags, open_dir_options.no_follow);
1535 },1492 },
...@@ -1545,32 +1502,6 @@ pub const Dir = struct {...@@ -1545,32 +1502,6 @@ pub const Dir = struct {
1545 };1502 };
1546 }1503 }
15471504
1548 /// This function performs `makePath`, followed by `openIterableDir`.
1549 /// If supported by the OS, this operation is atomic. It is not atomic on
1550 /// all operating systems.
1551 pub fn makeOpenPathIterable(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !IterableDir {
1552 return switch (builtin.os.tag) {
1553 .windows => {
1554 const w = os.windows;
1555 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
1556 w.SYNCHRONIZE | w.FILE_TRAVERSE | w.FILE_LIST_DIRECTORY;
1557
1558 return IterableDir{
1559 .dir = try self.makeOpenPathAccessMaskW(sub_path, base_flags, open_dir_options.no_follow),
1560 };
1561 },
1562 else => {
1563 return self.openIterableDir(sub_path, open_dir_options) catch |err| switch (err) {
1564 error.FileNotFound => {
1565 try self.makePath(sub_path);
1566 return self.openIterableDir(sub_path, open_dir_options);
1567 },
1568 else => |e| return e,
1569 };
1570 },
1571 };
1572 }
1573
1574 /// This function returns the canonicalized absolute pathname of1505 /// This function returns the canonicalized absolute pathname of
1575 /// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this1506 /// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this
1576 /// `Dir` handle and returns the canonicalized absolute pathname of `pathname`1507 /// `Dir` handle and returns the canonicalized absolute pathname of `pathname`
...@@ -1706,39 +1637,28 @@ pub const Dir = struct {...@@ -1706,39 +1637,28 @@ pub const Dir = struct {
1706 /// such operations are Illegal Behavior.1637 /// such operations are Illegal Behavior.
1707 access_sub_paths: bool = true,1638 access_sub_paths: bool = true,
17081639
1640 /// `true` means the opened directory can be scanned for the files and sub-directories
1641 /// of the result. It means the `iterate` function can be called.
1642 iterate: bool = false,
1643
1709 /// `true` means it won't dereference the symlinks.1644 /// `true` means it won't dereference the symlinks.
1710 no_follow: bool = false,1645 no_follow: bool = false,
1711 };1646 };
17121647
1713 /// Opens a directory at the given path. The directory is a system resource that remains1648 /// Opens a directory at the given path. The directory is a system resource that remains
1714 /// open until `close` is called on the result.1649 /// open until `close` is called on the result.
1650 /// The directory cannot be iterated unless the `iterate` option is set to `true`.
1715 ///1651 ///
1716 /// Asserts that the path parameter has no null bytes.1652 /// Asserts that the path parameter has no null bytes.
1717 pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {1653 pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
1718 if (builtin.os.tag == .windows) {1654 if (builtin.os.tag == .windows) {
1719 const sub_path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);1655 const sub_path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);
1720 return self.openDirW(sub_path_w.span().ptr, args, false);1656 return .{ .dir = try self.openDirW(sub_path_w.span().ptr, args) };
1721 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1722 return self.openDirWasi(sub_path, args);
1723 } else {
1724 const sub_path_c = try os.toPosixPath(sub_path);
1725 return self.openDirZ(&sub_path_c, args, false);
1726 }
1727 }
1728
1729 /// Opens an iterable directory at the given path. The directory is a system resource that remains
1730 /// open until `close` is called on the result.
1731 ///
1732 /// Asserts that the path parameter has no null bytes.
1733 pub fn openIterableDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!IterableDir {
1734 if (builtin.os.tag == .windows) {
1735 const sub_path_w = try os.windows.sliceToPrefixedFileW(self.fd, sub_path);
1736 return IterableDir{ .dir = try self.openDirW(sub_path_w.span().ptr, args, true) };
1737 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {1657 } else if (builtin.os.tag == .wasi and !builtin.link_libc) {
1738 return IterableDir{ .dir = try self.openDirWasi(sub_path, args) };1658 return .{ .dir = try self.openDirWasi(sub_path, args) };
1739 } else {1659 } else {
1740 const sub_path_c = try os.toPosixPath(sub_path);1660 const sub_path_c = try os.toPosixPath(sub_path);
1741 return IterableDir{ .dir = try self.openDirZ(&sub_path_c, args, true) };1661 return .{ .dir = try self.openDirZ(&sub_path_c, args) };
1742 }1662 }
1743 }1663 }
17441664
...@@ -1790,13 +1710,13 @@ pub const Dir = struct {...@@ -1790,13 +1710,13 @@ pub const Dir = struct {
1790 }1710 }
17911711
1792 /// Same as `openDir` except the parameter is null-terminated.1712 /// Same as `openDir` except the parameter is null-terminated.
1793 pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions, iterable: bool) OpenError!Dir {1713 pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {
1794 if (builtin.os.tag == .windows) {1714 if (builtin.os.tag == .windows) {
1795 const sub_path_w = try os.windows.cStrToPrefixedFileW(self.fd, sub_path_c);1715 const sub_path_w = try os.windows.cStrToPrefixedFileW(self.fd, sub_path_c);
1796 return self.openDirW(sub_path_w.span().ptr, args, iterable);1716 return self.openDirW(sub_path_w.span().ptr, args);
1797 }1717 }
1798 const symlink_flags: u32 = if (args.no_follow) os.O.NOFOLLOW else 0x0;1718 const symlink_flags: u32 = if (args.no_follow) os.O.NOFOLLOW else 0x0;
1799 if (!iterable) {1719 if (!args.iterate) {
1800 const O_PATH = if (@hasDecl(os.O, "PATH")) os.O.PATH else 0;1720 const O_PATH = if (@hasDecl(os.O, "PATH")) os.O.PATH else 0;
1801 return self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | O_PATH | symlink_flags);1721 return self.openDirFlagsZ(sub_path_c, os.O.DIRECTORY | os.O.RDONLY | os.O.CLOEXEC | O_PATH | symlink_flags);
1802 } else {1722 } else {
...@@ -1806,12 +1726,12 @@ pub const Dir = struct {...@@ -1806,12 +1726,12 @@ pub const Dir = struct {
18061726
1807 /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed.1727 /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed.
1808 /// This function asserts the target OS is Windows.1728 /// This function asserts the target OS is Windows.
1809 pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions, iterable: bool) OpenError!Dir {1729 pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir {
1810 const w = os.windows;1730 const w = os.windows;
1811 // TODO remove some of these flags if args.access_sub_paths is false1731 // TODO remove some of these flags if args.access_sub_paths is false
1812 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |1732 const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
1813 w.SYNCHRONIZE | w.FILE_TRAVERSE;1733 w.SYNCHRONIZE | w.FILE_TRAVERSE;
1814 const flags: u32 = if (iterable) base_flags | w.FILE_LIST_DIRECTORY else base_flags;1734 const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags;
1815 const dir = try self.makeOpenDirAccessMaskW(sub_path_w, flags, .{1735 const dir = try self.makeOpenDirAccessMaskW(sub_path_w, flags, .{
1816 .no_follow = args.no_follow,1736 .no_follow = args.no_follow,
1817 .create_disposition = w.FILE_OPEN,1737 .create_disposition = w.FILE_OPEN,
...@@ -2203,7 +2123,7 @@ pub const Dir = struct {...@@ -2203,7 +2123,7 @@ pub const Dir = struct {
2203 const StackItem = struct {2123 const StackItem = struct {
2204 name: []const u8,2124 name: []const u8,
2205 parent_dir: Dir,2125 parent_dir: Dir,
2206 iter: IterableDir.Iterator,2126 iter: Dir.Iterator,
22072127
2208 fn closeAll(items: []@This()) void {2128 fn closeAll(items: []@This()) void {
2209 for (items) |*item| item.iter.dir.close();2129 for (items) |*item| item.iter.dir.close();
...@@ -2227,7 +2147,10 @@ pub const Dir = struct {...@@ -2227,7 +2147,10 @@ pub const Dir = struct {
2227 handle_entry: while (true) {2147 handle_entry: while (true) {
2228 if (treat_as_dir) {2148 if (treat_as_dir) {
2229 if (stack.unusedCapacitySlice().len >= 1) {2149 if (stack.unusedCapacitySlice().len >= 1) {
2230 var iterable_dir = top.iter.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) {2150 var iterable_dir = top.iter.dir.openDir(entry.name, .{
2151 .no_follow = true,
2152 .iterate = true,
2153 }) catch |err| switch (err) {
2231 error.NotDir => {2154 error.NotDir => {
2232 treat_as_dir = false;2155 treat_as_dir = false;
2233 continue :handle_entry;2156 continue :handle_entry;
...@@ -2318,7 +2241,10 @@ pub const Dir = struct {...@@ -2318,7 +2241,10 @@ pub const Dir = struct {
2318 var treat_as_dir = true;2241 var treat_as_dir = true;
2319 handle_entry: while (true) {2242 handle_entry: while (true) {
2320 if (treat_as_dir) {2243 if (treat_as_dir) {
2321 break :iterable_dir parent_dir.openIterableDir(name, .{ .no_follow = true }) catch |err| switch (err) {2244 break :iterable_dir parent_dir.openDir(name, .{
2245 .no_follow = true,
2246 .iterate = true,
2247 }) catch |err| switch (err) {
2322 error.NotDir => {2248 error.NotDir => {
2323 treat_as_dir = false;2249 treat_as_dir = false;
2324 continue :handle_entry;2250 continue :handle_entry;
...@@ -2393,12 +2319,12 @@ pub const Dir = struct {...@@ -2393,12 +2319,12 @@ pub const Dir = struct {
23932319
2394 fn deleteTreeMinStackSizeWithKindHint(self: Dir, sub_path: []const u8, kind_hint: File.Kind) DeleteTreeError!void {2320 fn deleteTreeMinStackSizeWithKindHint(self: Dir, sub_path: []const u8, kind_hint: File.Kind) DeleteTreeError!void {
2395 start_over: while (true) {2321 start_over: while (true) {
2396 var iterable_dir = (try self.deleteTreeOpenInitialSubpath(sub_path, kind_hint)) orelse return;2322 var dir = (try self.deleteTreeOpenInitialSubpath(sub_path, kind_hint)) orelse return;
2397 var cleanup_dir_parent: ?IterableDir = null;2323 var cleanup_dir_parent: ?Dir = null;
2398 defer if (cleanup_dir_parent) |*d| d.close();2324 defer if (cleanup_dir_parent) |*d| d.close();
23992325
2400 var cleanup_dir = true;2326 var cleanup_dir = true;
2401 defer if (cleanup_dir) iterable_dir.close();2327 defer if (cleanup_dir) dir.close();
24022328
2403 // Valid use of MAX_PATH_BYTES because dir_name_buf will only2329 // Valid use of MAX_PATH_BYTES because dir_name_buf will only
2404 // ever store a single path component that was returned from the2330 // ever store a single path component that was returned from the
...@@ -2411,12 +2337,15 @@ pub const Dir = struct {...@@ -2411,12 +2337,15 @@ pub const Dir = struct {
2411 // open it, and close the original directory. Repeat. Then start the entire operation over.2337 // open it, and close the original directory. Repeat. Then start the entire operation over.
24122338
2413 scan_dir: while (true) {2339 scan_dir: while (true) {
2414 var dir_it = iterable_dir.iterateAssumeFirstIteration();2340 var dir_it = dir.iterateAssumeFirstIteration();
2415 dir_it: while (try dir_it.next()) |entry| {2341 dir_it: while (try dir_it.next()) |entry| {
2416 var treat_as_dir = entry.kind == .directory;2342 var treat_as_dir = entry.kind == .directory;
2417 handle_entry: while (true) {2343 handle_entry: while (true) {
2418 if (treat_as_dir) {2344 if (treat_as_dir) {
2419 const new_dir = iterable_dir.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) {2345 const new_dir = dir.openDir(entry.name, .{
2346 .no_follow = true,
2347 .iterate = true,
2348 }) catch |err| switch (err) {
2420 error.NotDir => {2349 error.NotDir => {
2421 treat_as_dir = false;2350 treat_as_dir = false;
2422 continue :handle_entry;2351 continue :handle_entry;
...@@ -2442,14 +2371,14 @@ pub const Dir = struct {...@@ -2442,14 +2371,14 @@ pub const Dir = struct {
2442 => |e| return e,2371 => |e| return e,
2443 };2372 };
2444 if (cleanup_dir_parent) |*d| d.close();2373 if (cleanup_dir_parent) |*d| d.close();
2445 cleanup_dir_parent = iterable_dir;2374 cleanup_dir_parent = dir;
2446 iterable_dir = new_dir;2375 dir = new_dir;
2447 const result = dir_name_buf[0..entry.name.len];2376 const result = dir_name_buf[0..entry.name.len];
2448 @memcpy(result, entry.name);2377 @memcpy(result, entry.name);
2449 dir_name = result;2378 dir_name = result;
2450 continue :scan_dir;2379 continue :scan_dir;
2451 } else {2380 } else {
2452 if (iterable_dir.dir.deleteFile(entry.name)) {2381 if (dir.deleteFile(entry.name)) {
2453 continue :dir_it;2382 continue :dir_it;
2454 } else |err| switch (err) {2383 } else |err| switch (err) {
2455 error.FileNotFound => continue :dir_it,2384 error.FileNotFound => continue :dir_it,
...@@ -2480,11 +2409,11 @@ pub const Dir = struct {...@@ -2480,11 +2409,11 @@ pub const Dir = struct {
2480 }2409 }
2481 // Reached the end of the directory entries, which means we successfully deleted all of them.2410 // Reached the end of the directory entries, which means we successfully deleted all of them.
2482 // Now to remove the directory itself.2411 // Now to remove the directory itself.
2483 iterable_dir.close();2412 dir.close();
2484 cleanup_dir = false;2413 cleanup_dir = false;
24852414
2486 if (cleanup_dir_parent) |d| {2415 if (cleanup_dir_parent) |d| {
2487 d.dir.deleteDir(dir_name) catch |err| switch (err) {2416 d.deleteDir(dir_name) catch |err| switch (err) {
2488 // These two things can happen due to file system race conditions.2417 // These two things can happen due to file system race conditions.
2489 error.FileNotFound, error.DirNotEmpty => continue :start_over,2418 error.FileNotFound, error.DirNotEmpty => continue :start_over,
2490 else => |e| return e,2419 else => |e| return e,
...@@ -2503,14 +2432,17 @@ pub const Dir = struct {...@@ -2503,14 +2432,17 @@ pub const Dir = struct {
2503 }2432 }
25042433
2505 /// On successful delete, returns null.2434 /// On successful delete, returns null.
2506 fn deleteTreeOpenInitialSubpath(self: Dir, sub_path: []const u8, kind_hint: File.Kind) !?IterableDir {2435 fn deleteTreeOpenInitialSubpath(self: Dir, sub_path: []const u8, kind_hint: File.Kind) !?Dir {
2507 return iterable_dir: {2436 return iterable_dir: {
2508 // Treat as a file by default2437 // Treat as a file by default
2509 var treat_as_dir = kind_hint == .directory;2438 var treat_as_dir = kind_hint == .directory;
25102439
2511 handle_entry: while (true) {2440 handle_entry: while (true) {
2512 if (treat_as_dir) {2441 if (treat_as_dir) {
2513 break :iterable_dir self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) {2442 break :iterable_dir self.openDir(sub_path, .{
2443 .no_follow = true,
2444 .iterate = true,
2445 }) catch |err| switch (err) {
2514 error.NotDir => {2446 error.NotDir => {
2515 treat_as_dir = false;2447 treat_as_dir = false;
2516 continue :handle_entry;2448 continue :handle_entry;
...@@ -2764,6 +2696,37 @@ pub const Dir = struct {...@@ -2764,6 +2696,37 @@ pub const Dir = struct {
2764 return Stat.fromSystem(st);2696 return Stat.fromSystem(st);
2765 }2697 }
27662698
2699 pub const ChmodError = File.ChmodError;
2700
2701 /// Changes the mode of the directory.
2702 /// The process must have the correct privileges in order to do this
2703 /// successfully, or must have the effective user ID matching the owner
2704 /// of the directory. Additionally, the directory must have been opened
2705 /// with `OpenDirOptions{ .iterate = true }`.
2706 pub fn chmod(self: Dir, new_mode: File.Mode) ChmodError!void {
2707 const file: File = .{
2708 .handle = self.fd,
2709 .capable_io_mode = .blocking,
2710 };
2711 try file.chmod(new_mode);
2712 }
2713
2714 /// Changes the owner and group of the directory.
2715 /// The process must have the correct privileges in order to do this
2716 /// successfully. The group may be changed by the owner of the directory to
2717 /// any group of which the owner is a member. Additionally, the directory
2718 /// must have been opened with `OpenDirOptions{ .iterate = true }`. If the
2719 /// owner or group is specified as `null`, the ID is not changed.
2720 pub fn chown(self: Dir, owner: ?File.Uid, group: ?File.Gid) ChownError!void {
2721 const file: File = .{
2722 .handle = self.fd,
2723 .capable_io_mode = .blocking,
2724 };
2725 try file.chown(owner, group);
2726 }
2727
2728 pub const ChownError = File.ChownError;
2729
2767 const Permissions = File.Permissions;2730 const Permissions = File.Permissions;
2768 pub const SetPermissionsError = File.SetPermissionsError;2731 pub const SetPermissionsError = File.SetPermissionsError;
27692732
...@@ -2829,27 +2792,6 @@ pub fn openDirAbsoluteW(absolute_path_c: [*:0]const u16, flags: Dir.OpenDirOptio...@@ -2829,27 +2792,6 @@ pub fn openDirAbsoluteW(absolute_path_c: [*:0]const u16, flags: Dir.OpenDirOptio
2829 return cwd().openDirW(absolute_path_c, flags, false);2792 return cwd().openDirW(absolute_path_c, flags, false);
2830}2793}
28312794
2832/// Opens a directory at the given path. The directory is a system resource that remains
2833/// open until `close` is called on the result.
2834/// See `openIterableDirAbsoluteZ` for a function that accepts a null-terminated path.
2835///
2836/// Asserts that the path parameter has no null bytes.
2837pub fn openIterableDirAbsolute(absolute_path: []const u8, flags: Dir.OpenDirOptions) File.OpenError!IterableDir {
2838 assert(path.isAbsolute(absolute_path));
2839 return cwd().openIterableDir(absolute_path, flags);
2840}
2841
2842/// Same as `openIterableDirAbsolute` but the path parameter is null-terminated.
2843pub fn openIterableDirAbsoluteZ(absolute_path_c: [*:0]const u8, flags: Dir.OpenDirOptions) File.OpenError!IterableDir {
2844 assert(path.isAbsoluteZ(absolute_path_c));
2845 return IterableDir{ .dir = try cwd().openDirZ(absolute_path_c, flags, true) };
2846}
2847/// Same as `openIterableDirAbsolute` but the path parameter is null-terminated.
2848pub fn openIterableDirAbsoluteW(absolute_path_c: [*:0]const u16, flags: Dir.OpenDirOptions) File.OpenError!IterableDir {
2849 assert(path.isAbsoluteWindowsW(absolute_path_c));
2850 return IterableDir{ .dir = try cwd().openDirW(absolute_path_c, flags, true) };
2851}
2852
2853/// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path.2795/// Opens a file for reading or writing, without attempting to create a new file, based on an absolute path.
2854/// Call `File.close` to release the resource.2796/// Call `File.close` to release the resource.
2855/// Asserts that the path is absolute. See `Dir.openFile` for a function that2797/// Asserts that the path is absolute. See `Dir.openFile` for a function that
lib/std/fs/test.zig+33-35
...@@ -8,10 +8,8 @@ const wasi = std.os.wasi;...@@ -8,10 +8,8 @@ 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;
12const File = std.fs.File;11const File = std.fs.File;
13const tmpDir = testing.tmpDir;12const tmpDir = testing.tmpDir;
14const tmpIterableDir = testing.tmpIterableDir;
1513
16const PathType = enum {14const PathType = enum {
17 relative,15 relative,
...@@ -76,11 +74,11 @@ const TestContext = struct {...@@ -76,11 +74,11 @@ const TestContext = struct {
76 arena: ArenaAllocator,74 arena: ArenaAllocator,
77 tmp: testing.TmpIterableDir,75 tmp: testing.TmpIterableDir,
78 dir: std.fs.Dir,76 dir: std.fs.Dir,
79 iterable_dir: std.fs.IterableDir,77 iterable_dir: std.fs.Dir,
80 transform_fn: *const PathType.TransformFn,78 transform_fn: *const PathType.TransformFn,
8179
82 pub fn init(path_type: PathType, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext {80 pub fn init(path_type: PathType, allocator: mem.Allocator, transform_fn: *const PathType.TransformFn) TestContext {
83 const tmp = tmpIterableDir(.{});81 const tmp = tmpDir(.{ .iterate = true });
84 return .{82 return .{
85 .path_type = path_type,83 .path_type = path_type,
86 .arena = ArenaAllocator.init(allocator),84 .arena = ArenaAllocator.init(allocator),
...@@ -323,28 +321,28 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void...@@ -323,28 +321,28 @@ fn testReadLinkAbsolute(target_path: []const u8, symlink_path: []const u8) !void
323}321}
324322
325test "Dir.Iterator" {323test "Dir.Iterator" {
326 var tmp_dir = tmpIterableDir(.{});324 var tmp_dir = tmpDir(.{ .iterate = true });
327 defer tmp_dir.cleanup();325 defer tmp_dir.cleanup();
328326
329 // First, create a couple of entries to iterate over.327 // First, create a couple of entries to iterate over.
330 const file = try tmp_dir.iterable_dir.dir.createFile("some_file", .{});328 const file = try tmp_dir.dir.createFile("some_file", .{});
331 file.close();329 file.close();
332330
333 try tmp_dir.iterable_dir.dir.makeDir("some_dir");331 try tmp_dir.dir.makeDir("some_dir");
334332
335 var arena = ArenaAllocator.init(testing.allocator);333 var arena = ArenaAllocator.init(testing.allocator);
336 defer arena.deinit();334 defer arena.deinit();
337 const allocator = arena.allocator();335 const allocator = arena.allocator();
338336
339 var entries = std.ArrayList(IterableDir.Entry).init(allocator);337 var entries = std.ArrayList(Dir.Entry).init(allocator);
340338
341 // Create iterator.339 // Create iterator.
342 var iter = tmp_dir.iterable_dir.iterate();340 var iter = tmp_dir.dir.iterate();
343 while (try iter.next()) |entry| {341 while (try iter.next()) |entry| {
344 // We cannot just store `entry` as on Windows, we're re-using the name buffer342 // We cannot just store `entry` as on Windows, we're re-using the name buffer
345 // which means we'll actually share the `name` pointer between entries!343 // which means we'll actually share the `name` pointer between entries!
346 const name = try allocator.dupe(u8, entry.name);344 const name = try allocator.dupe(u8, entry.name);
347 try entries.append(.{ .name = name, .kind = entry.kind });345 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
348 }346 }
349347
350 try testing.expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'348 try testing.expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
...@@ -353,7 +351,7 @@ test "Dir.Iterator" {...@@ -353,7 +351,7 @@ test "Dir.Iterator" {
353}351}
354352
355test "Dir.Iterator many entries" {353test "Dir.Iterator many entries" {
356 var tmp_dir = tmpIterableDir(.{});354 var tmp_dir = tmpDir(.{ .iterate = true });
357 defer tmp_dir.cleanup();355 defer tmp_dir.cleanup();
358356
359 const num = 1024;357 const num = 1024;
...@@ -369,7 +367,7 @@ test "Dir.Iterator many entries" {...@@ -369,7 +367,7 @@ test "Dir.Iterator many entries" {
369 defer arena.deinit();367 defer arena.deinit();
370 const allocator = arena.allocator();368 const allocator = arena.allocator();
371369
372 var entries = std.ArrayList(IterableDir.Entry).init(allocator);370 var entries = std.ArrayList(Dir.Entry).init(allocator);
373371
374 // Create iterator.372 // Create iterator.
375 var iter = tmp_dir.iterable_dir.iterate();373 var iter = tmp_dir.iterable_dir.iterate();
...@@ -388,14 +386,14 @@ test "Dir.Iterator many entries" {...@@ -388,14 +386,14 @@ test "Dir.Iterator many entries" {
388}386}
389387
390test "Dir.Iterator twice" {388test "Dir.Iterator twice" {
391 var tmp_dir = tmpIterableDir(.{});389 var tmp_dir = tmpDir(.{ .iterate = true });
392 defer tmp_dir.cleanup();390 defer tmp_dir.cleanup();
393391
394 // First, create a couple of entries to iterate over.392 // First, create a couple of entries to iterate over.
395 const file = try tmp_dir.iterable_dir.dir.createFile("some_file", .{});393 const file = try tmp_dir.dir.createFile("some_file", .{});
396 file.close();394 file.close();
397395
398 try tmp_dir.iterable_dir.dir.makeDir("some_dir");396 try tmp_dir.dir.makeDir("some_dir");
399397
400 var arena = ArenaAllocator.init(testing.allocator);398 var arena = ArenaAllocator.init(testing.allocator);
401 defer arena.deinit();399 defer arena.deinit();
...@@ -403,15 +401,15 @@ test "Dir.Iterator twice" {...@@ -403,15 +401,15 @@ test "Dir.Iterator twice" {
403401
404 var i: u8 = 0;402 var i: u8 = 0;
405 while (i < 2) : (i += 1) {403 while (i < 2) : (i += 1) {
406 var entries = std.ArrayList(IterableDir.Entry).init(allocator);404 var entries = std.ArrayList(Dir.Entry).init(allocator);
407405
408 // Create iterator.406 // Create iterator.
409 var iter = tmp_dir.iterable_dir.iterate();407 var iter = tmp_dir.dir.iterate();
410 while (try iter.next()) |entry| {408 while (try iter.next()) |entry| {
411 // We cannot just store `entry` as on Windows, we're re-using the name buffer409 // We cannot just store `entry` as on Windows, we're re-using the name buffer
412 // which means we'll actually share the `name` pointer between entries!410 // which means we'll actually share the `name` pointer between entries!
413 const name = try allocator.dupe(u8, entry.name);411 const name = try allocator.dupe(u8, entry.name);
414 try entries.append(.{ .name = name, .kind = entry.kind });412 try entries.append(Dir.Entry{ .name = name, .kind = entry.kind });
415 }413 }
416414
417 try testing.expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'415 try testing.expectEqual(@as(usize, 2), entries.items.len); // note that the Iterator skips '.' and '..'
...@@ -421,7 +419,7 @@ test "Dir.Iterator twice" {...@@ -421,7 +419,7 @@ test "Dir.Iterator twice" {
421}419}
422420
423test "Dir.Iterator reset" {421test "Dir.Iterator reset" {
424 var tmp_dir = tmpIterableDir(.{});422 var tmp_dir = tmpDir(.{ .iterate = true });
425 defer tmp_dir.cleanup();423 defer tmp_dir.cleanup();
426424
427 // First, create a couple of entries to iterate over.425 // First, create a couple of entries to iterate over.
...@@ -439,7 +437,7 @@ test "Dir.Iterator reset" {...@@ -439,7 +437,7 @@ test "Dir.Iterator reset" {
439437
440 var i: u8 = 0;438 var i: u8 = 0;
441 while (i < 2) : (i += 1) {439 while (i < 2) : (i += 1) {
442 var entries = std.ArrayList(IterableDir.Entry).init(allocator);440 var entries = std.ArrayList(Dir.Entry).init(allocator);
443441
444 while (try iter.next()) |entry| {442 while (try iter.next()) |entry| {
445 // We cannot just store `entry` as on Windows, we're re-using the name buffer443 // We cannot just store `entry` as on Windows, we're re-using the name buffer
...@@ -485,11 +483,11 @@ test "Dir.Iterator but dir is deleted during iteration" {...@@ -485,11 +483,11 @@ test "Dir.Iterator but dir is deleted during iteration" {
485 }483 }
486}484}
487485
488fn entryEql(lhs: IterableDir.Entry, rhs: IterableDir.Entry) bool {486fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool {
489 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;487 return mem.eql(u8, lhs.name, rhs.name) and lhs.kind == rhs.kind;
490}488}
491489
492fn contains(entries: *const std.ArrayList(IterableDir.Entry), el: IterableDir.Entry) bool {490fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool {
493 for (entries.items) |entry| {491 for (entries.items) |entry| {
494 if (entryEql(entry, el)) return true;492 if (entryEql(entry, el)) return true;
495 }493 }
...@@ -963,7 +961,7 @@ test "makePath in a directory that no longer exists" {...@@ -963,7 +961,7 @@ test "makePath in a directory that no longer exists" {
963 try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path"));961 try testing.expectError(error.FileNotFound, tmp.dir.makePath("sub-path"));
964}962}
965963
966fn testFilenameLimits(iterable_dir: IterableDir, maxed_filename: []const u8) !void {964fn testFilenameLimits(iterable_dir: Dir, maxed_filename: []const u8) !void {
967 // setup, create a dir and a nested file both with maxed filenames, and walk the dir965 // setup, create a dir and a nested file both with maxed filenames, and walk the dir
968 {966 {
969 var maxed_dir = try iterable_dir.dir.makeOpenPath(maxed_filename, .{});967 var maxed_dir = try iterable_dir.dir.makeOpenPath(maxed_filename, .{});
...@@ -987,7 +985,7 @@ fn testFilenameLimits(iterable_dir: IterableDir, maxed_filename: []const u8) !vo...@@ -987,7 +985,7 @@ fn testFilenameLimits(iterable_dir: IterableDir, maxed_filename: []const u8) !vo
987}985}
988986
989test "max file name component lengths" {987test "max file name component lengths" {
990 var tmp = tmpIterableDir(.{});988 var tmp = tmpDir(.{ .iterate = true });
991 defer tmp.cleanup();989 defer tmp.cleanup();
992990
993 if (builtin.os.tag == .windows) {991 if (builtin.os.tag == .windows) {
...@@ -1384,7 +1382,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {...@@ -1384,7 +1382,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
1384test "walker" {1382test "walker" {
1385 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;1383 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
13861384
1387 var tmp = tmpIterableDir(.{});1385 var tmp = tmpDir(.{ .iterate = true });
1388 defer tmp.cleanup();1386 defer tmp.cleanup();
13891387
1390 // iteration order of walker is undefined, so need lookup maps to check against1388 // iteration order of walker is undefined, so need lookup maps to check against
...@@ -1410,10 +1408,10 @@ test "walker" {...@@ -1410,10 +1408,10 @@ test "walker" {
1410 });1408 });
14111409
1412 for (expected_paths.kvs) |kv| {1410 for (expected_paths.kvs) |kv| {
1413 try tmp.iterable_dir.dir.makePath(kv.key);1411 try tmp.dir.makePath(kv.key);
1414 }1412 }
14151413
1416 var walker = try tmp.iterable_dir.walk(testing.allocator);1414 var walker = try tmp.dir.walk(testing.allocator);
1417 defer walker.deinit();1415 defer walker.deinit();
14181416
1419 var num_walked: usize = 0;1417 var num_walked: usize = 0;
...@@ -1437,7 +1435,7 @@ test "walker" {...@@ -1437,7 +1435,7 @@ test "walker" {
1437test "walker without fully iterating" {1435test "walker without fully iterating" {
1438 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;1436 if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
14391437
1440 var tmp = tmpIterableDir(.{});1438 var tmp = tmpDir(.{ .iterate = true });
1441 defer tmp.cleanup();1439 defer tmp.cleanup();
14421440
1443 var walker = try tmp.iterable_dir.walk(testing.allocator);1441 var walker = try tmp.iterable_dir.walk(testing.allocator);
...@@ -1556,11 +1554,11 @@ test "chmod" {...@@ -1556,11 +1554,11 @@ test "chmod" {
1556 try testing.expectEqual(@as(File.Mode, 0o644), (try file.stat()).mode & 0o7777);1554 try testing.expectEqual(@as(File.Mode, 0o644), (try file.stat()).mode & 0o7777);
15571555
1558 try tmp.dir.makeDir("test_dir");1556 try tmp.dir.makeDir("test_dir");
1559 var iterable_dir = try tmp.dir.openIterableDir("test_dir", .{});1557 var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true });
1560 defer iterable_dir.close();1558 defer dir.close();
15611559
1562 try iterable_dir.chmod(0o700);1560 try dir.chmod(0o700);
1563 try testing.expectEqual(@as(File.Mode, 0o700), (try iterable_dir.dir.stat()).mode & 0o7777);1561 try testing.expectEqual(@as(File.Mode, 0o700), (try dir.stat()).mode & 0o7777);
1564}1562}
15651563
1566test "chown" {1564test "chown" {
...@@ -1576,9 +1574,9 @@ test "chown" {...@@ -1576,9 +1574,9 @@ test "chown" {
15761574
1577 try tmp.dir.makeDir("test_dir");1575 try tmp.dir.makeDir("test_dir");
15781576
1579 var iterable_dir = try tmp.dir.openIterableDir("test_dir", .{});1577 var dir = try tmp.dir.openDir("test_dir", .{ .iterate = true });
1580 defer iterable_dir.close();1578 defer dir.close();
1581 try iterable_dir.chown(null, null);1579 try dir.chown(null, null);
1582}1580}
15831581
1584test "File.Metadata" {1582test "File.Metadata" {
lib/std/os.zig+1-1
...@@ -402,7 +402,7 @@ pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {...@@ -402,7 +402,7 @@ pub fn fchown(fd: fd_t, owner: ?uid_t, group: ?gid_t) FChownError!void {
402 switch (system.getErrno(res)) {402 switch (system.getErrno(res)) {
403 .SUCCESS => return,403 .SUCCESS => return,
404 .INTR => continue,404 .INTR => continue,
405 .BADF => unreachable, // Can be reached if the fd refers to a non-iterable directory.405 .BADF => unreachable, // Can be reached if the fd refers to a directory opened without `OpenDirOptions{ .iterate = true }`
406406
407 .FAULT => unreachable,407 .FAULT => unreachable,
408 .INVAL => unreachable,408 .INVAL => unreachable,
lib/std/testing.zig-38
...@@ -543,22 +543,6 @@ pub const TmpDir = struct {...@@ -543,22 +543,6 @@ pub const TmpDir = struct {
543 }543 }
544};544};
545545
546pub const TmpIterableDir = struct {
547 iterable_dir: std.fs.IterableDir,
548 parent_dir: std.fs.Dir,
549 sub_path: [sub_path_len]u8,
550
551 const random_bytes_count = 12;
552 const sub_path_len = std.fs.base64_encoder.calcSize(random_bytes_count);
553
554 pub fn cleanup(self: *TmpIterableDir) void {
555 self.iterable_dir.close();
556 self.parent_dir.deleteTree(&self.sub_path) catch {};
557 self.parent_dir.close();
558 self.* = undefined;
559 }
560};
561
562pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {546pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
563 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;547 var random_bytes: [TmpDir.random_bytes_count]u8 = undefined;
564 std.crypto.random.bytes(&random_bytes);548 std.crypto.random.bytes(&random_bytes);
...@@ -581,28 +565,6 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {...@@ -581,28 +565,6 @@ pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir {
581 };565 };
582}566}
583567
584pub fn tmpIterableDir(opts: std.fs.Dir.OpenDirOptions) TmpIterableDir {
585 var random_bytes: [TmpIterableDir.random_bytes_count]u8 = undefined;
586 std.crypto.random.bytes(&random_bytes);
587 var sub_path: [TmpIterableDir.sub_path_len]u8 = undefined;
588 _ = std.fs.base64_encoder.encode(&sub_path, &random_bytes);
589
590 const cwd = std.fs.cwd();
591 var cache_dir = cwd.makeOpenPath("zig-cache", .{}) catch
592 @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir");
593 defer cache_dir.close();
594 const parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch
595 @panic("unable to make tmp dir for testing: unable to make and open zig-cache/tmp dir");
596 const dir = parent_dir.makeOpenPathIterable(&sub_path, opts) catch
597 @panic("unable to make tmp dir for testing: unable to make and open the tmp dir");
598
599 return .{
600 .iterable_dir = dir,
601 .parent_dir = parent_dir,
602 .sub_path = sub_path,
603 };
604}
605
606test "expectEqual nested array" {568test "expectEqual nested array" {
607 const a = [2][2]f32{569 const a = [2][2]f32{
608 [_]f32{ 1.0, 0.0 },570 [_]f32{ 1.0, 0.0 },
src/main.zig+6-6
...@@ -5698,13 +5698,13 @@ fn fmtPathDir(...@@ -5698,13 +5698,13 @@ fn fmtPathDir(
5698 parent_dir: fs.Dir,5698 parent_dir: fs.Dir,
5699 parent_sub_path: []const u8,5699 parent_sub_path: []const u8,
5700) FmtError!void {5700) FmtError!void {
5701 var iterable_dir = try parent_dir.openIterableDir(parent_sub_path, .{});5701 var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
5702 defer iterable_dir.close();5702 defer dir.close();
57035703
5704 const stat = try iterable_dir.dir.stat();5704 const stat = try dir.stat();
5705 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;5705 if (try fmt.seen.fetchPut(stat.inode, {})) |_| return;
57065706
5707 var dir_it = iterable_dir.iterate();5707 var dir_it = dir.iterate();
5708 while (try dir_it.next()) |entry| {5708 while (try dir_it.next()) |entry| {
5709 const is_dir = entry.kind == .directory;5709 const is_dir = entry.kind == .directory;
57105710
...@@ -5715,9 +5715,9 @@ fn fmtPathDir(...@@ -5715,9 +5715,9 @@ fn fmtPathDir(
5715 defer fmt.gpa.free(full_path);5715 defer fmt.gpa.free(full_path);
57165716
5717 if (is_dir) {5717 if (is_dir) {
5718 try fmtPathDir(fmt, full_path, check_mode, iterable_dir.dir, entry.name);5718 try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
5719 } else {5719 } else {
5720 fmtPathFile(fmt, full_path, check_mode, iterable_dir.dir, entry.name) catch |err| {5720 fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
5721 warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });5721 warn("unable to format '{s}': {s}", .{ full_path, @errorName(err) });
5722 fmt.any_error = true;5722 fmt.any_error = true;
5723 return;5723 return;
tools/process_headers.zig+3-3
...@@ -382,14 +382,14 @@ pub fn main() !void {...@@ -382,14 +382,14 @@ pub fn main() !void {
382 try dir_stack.append(target_include_dir);382 try dir_stack.append(target_include_dir);
383383
384 while (dir_stack.popOrNull()) |full_dir_name| {384 while (dir_stack.popOrNull()) |full_dir_name| {
385 var iterable_dir = std.fs.cwd().openIterableDir(full_dir_name, .{}) catch |err| switch (err) {385 var dir = std.fs.cwd().openDir(full_dir_name, .{ .iterate = true }) catch |err| switch (err) {
386 error.FileNotFound => continue :search,386 error.FileNotFound => continue :search,
387 error.AccessDenied => continue :search,387 error.AccessDenied => continue :search,
388 else => return err,388 else => return err,
389 };389 };
390 defer iterable_dir.close();390 defer dir.close();
391391
392 var dir_it = iterable_dir.iterate();392 var dir_it = dir.iterate();
393393
394 while (try dir_it.next()) |entry| {394 while (try dir_it.next()) |entry| {
395 const full_path = try std.fs.path.join(allocator, &[_][]const u8{ full_dir_name, entry.name });395 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 iterable_dir = try std.fs.cwd().openIterableDir(path_to_walk, .{});17 const dir = try std.fs.cwd().openDir(path_to_walk, .{ .iterate = true });
1818
19 var walker = try iterable_dir.walk(arena);19 var walker = try 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 iterable_dir.dir.readFileAlloc(arena, entry.path, 20 * 1024 * 1024);33 const source = try 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 iterable_dir.dir.writeFile(entry.path, new_source);45 try dir.writeFile(entry.path, new_source);
46 }46 }
47}47}
tools/update-linux-headers.zig+3-3
...@@ -190,14 +190,14 @@ pub fn main() !void {...@@ -190,14 +190,14 @@ pub fn main() !void {
190 try dir_stack.append(target_include_dir);190 try dir_stack.append(target_include_dir);
191191
192 while (dir_stack.popOrNull()) |full_dir_name| {192 while (dir_stack.popOrNull()) |full_dir_name| {
193 var iterable_dir = std.fs.cwd().openIterableDir(full_dir_name, .{}) catch |err| switch (err) {193 var dir = std.fs.cwd().openDir(full_dir_name, .{ .iterate = true }) catch |err| switch (err) {
194 error.FileNotFound => continue :search,194 error.FileNotFound => continue :search,
195 error.AccessDenied => continue :search,195 error.AccessDenied => continue :search,
196 else => return err,196 else => return err,
197 };197 };
198 defer iterable_dir.close();198 defer dir.close();
199199
200 var dir_it = iterable_dir.iterate();200 var dir_it = dir.iterate();
201201
202 while (try dir_it.next()) |entry| {202 while (try dir_it.next()) |entry| {
203 const full_path = try std.fs.path.join(arena, &[_][]const u8{ full_dir_name, entry.name });203 const full_path = try std.fs.path.join(arena, &[_][]const u8{ full_dir_name, entry.name });
tools/update_glibc.zig+5-5
...@@ -47,7 +47,7 @@ pub fn main() !void {...@@ -47,7 +47,7 @@ pub fn main() !void {
4747
48 const dest_dir_path = try std.fmt.allocPrint(arena, "{s}/lib/libc/glibc", .{zig_src_path});48 const dest_dir_path = try std.fmt.allocPrint(arena, "{s}/lib/libc/glibc", .{zig_src_path});
4949
50 var dest_dir = fs.cwd().openIterableDir(dest_dir_path, .{}) catch |err| {50 var dest_dir = fs.cwd().openDir(dest_dir_path, .{ .iterate = true }) catch |err| {
51 fatal("unable to open destination directory '{s}': {s}", .{51 fatal("unable to open destination directory '{s}': {s}", .{
52 dest_dir_path, @errorName(err),52 dest_dir_path, @errorName(err),
53 });53 });
...@@ -72,14 +72,14 @@ pub fn main() !void {...@@ -72,14 +72,14 @@ pub fn main() !void {
72 if (mem.endsWith(u8, entry.path, ext)) continue :walk;72 if (mem.endsWith(u8, entry.path, ext)) continue :walk;
73 }73 }
7474
75 glibc_src_dir.copyFile(entry.path, dest_dir.dir, entry.path, .{}) catch |err| {75 glibc_src_dir.copyFile(entry.path, dest_dir, entry.path, .{}) catch |err| {
76 log.warn("unable to copy '{s}/{s}' to '{s}/{s}': {s}", .{76 log.warn("unable to copy '{s}/{s}' to '{s}/{s}': {s}", .{
77 glibc_src_path, entry.path,77 glibc_src_path, entry.path,
78 dest_dir_path, entry.path,78 dest_dir_path, entry.path,
79 @errorName(err),79 @errorName(err),
80 });80 });
81 if (err == error.FileNotFound) {81 if (err == error.FileNotFound) {
82 try dest_dir.dir.deleteFile(entry.path);82 try dest_dir.deleteFile(entry.path);
83 }83 }
84 };84 };
85 }85 }
...@@ -88,7 +88,7 @@ pub fn main() !void {...@@ -88,7 +88,7 @@ pub fn main() !void {
88 // Warn about duplicated files inside glibc/include/* that can be omitted88 // Warn about duplicated files inside glibc/include/* that can be omitted
89 // because they are already in generic-glibc/*.89 // because they are already in generic-glibc/*.
9090
91 var include_dir = dest_dir.dir.openIterableDir("include", .{}) catch |err| {91 var include_dir = dest_dir.openDir("include", .{ .iterate = true }) catch |err| {
92 fatal("unable to open directory '{s}/include': {s}", .{92 fatal("unable to open directory '{s}/include': {s}", .{
93 dest_dir_path, @errorName(err),93 dest_dir_path, @errorName(err),
94 });94 });
...@@ -125,7 +125,7 @@ pub fn main() !void {...@@ -125,7 +125,7 @@ pub fn main() !void {
125 generic_glibc_path, entry.path, @errorName(e),125 generic_glibc_path, entry.path, @errorName(e),
126 }),126 }),
127 };127 };
128 const glibc_include_contents = include_dir.dir.readFileAlloc(128 const glibc_include_contents = include_dir.readFileAlloc(
129 arena,129 arena,
130 entry.path,130 entry.path,
131 max_file_size,131 max_file_size,
tools/update_spirv_features.zig+3-3
...@@ -226,7 +226,7 @@ pub fn main() !void {...@@ -226,7 +226,7 @@ pub fn main() !void {
226/// TODO: Unfortunately, neither repository contains a machine-readable list of extension dependencies.226/// TODO: Unfortunately, neither repository contains a machine-readable list of extension dependencies.
227fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]const []const u8 {227fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]const []const u8 {
228 const extensions_path = try fs.path.join(allocator, &.{ spirv_registry_root, "extensions" });228 const extensions_path = try fs.path.join(allocator, &.{ spirv_registry_root, "extensions" });
229 var extensions_dir = try fs.cwd().openIterableDir(extensions_path, .{});229 var extensions_dir = try fs.cwd().openDir(extensions_path, .{ .iterate = true });
230 defer extensions_dir.close();230 defer extensions_dir.close();
231231
232 var extensions = std.ArrayList([]const u8).init(allocator);232 var extensions = std.ArrayList([]const u8).init(allocator);
...@@ -235,7 +235,7 @@ fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]c...@@ -235,7 +235,7 @@ fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]c
235 while (try vendor_it.next()) |vendor_entry| {235 while (try vendor_it.next()) |vendor_entry| {
236 std.debug.assert(vendor_entry.kind == .directory); // If this fails, the structure of SPIRV-Registry has changed.236 std.debug.assert(vendor_entry.kind == .directory); // If this fails, the structure of SPIRV-Registry has changed.
237237
238 const vendor_dir = try extensions_dir.dir.openIterableDir(vendor_entry.name, .{});238 const vendor_dir = try extensions_dir.openDir(vendor_entry.name, .{ .iterate = true });
239 var ext_it = vendor_dir.iterate();239 var ext_it = vendor_dir.iterate();
240 while (try ext_it.next()) |ext_entry| {240 while (try ext_it.next()) |ext_entry| {
241 // There is both a HTML and asciidoc version of every spec (as well as some other directories),241 // There is both a HTML and asciidoc version of every spec (as well as some other directories),
...@@ -258,7 +258,7 @@ fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]c...@@ -258,7 +258,7 @@ fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]c
258 // SPV_EXT_name258 // SPV_EXT_name
259 // ```259 // ```
260260
261 const ext_spec = try vendor_dir.dir.readFileAlloc(allocator, ext_entry.name, std.math.maxInt(usize));261 const ext_spec = try vendor_dir.readFileAlloc(allocator, ext_entry.name, std.math.maxInt(usize));
262 const name_strings = "Name Strings";262 const name_strings = "Name Strings";
263263
264 const name_strings_offset = std.mem.indexOf(u8, ext_spec, name_strings) orelse return error.InvalidRegistry;264 const name_strings_offset = std.mem.indexOf(u8, ext_spec, name_strings) orelse return error.InvalidRegistry;