| author | |
| committer | |
| log | da94227f783ec3c92859c4713b80a668f1183f96 |
| tree | 74e36b830f8fdff1c836df1c74024c5477bd0f7c |
| parent | 8f943b3d33432a26b7e242c1181e4220ed400501 |
| parent | 262f4c7b3a850594a75ec154db2ba8d5f9f517ab |
| signature |
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"); |
| 3246 | 3246 | ||
| 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(); |
| 3250 | 3250 | ||
| 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") or | 3265 | 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; |
| 3267 | 3267 | ||
| 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 | } |
| 285 | 285 | ||
| 286 | pub 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. |
| 288 | pub const IterableDir = struct { | ||
| 289 | dir: Dir, | ||
| 288 | 290 | ||
| 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 | }; |
| 781 | 783 | ||
| 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 | }; |
| 838 | 840 | ||
| 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 | }; |
| 843 | 845 | ||
| ... | @@ -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.name | 863 | 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 | }; |
| 897 | 899 | ||
| 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(); |
| 906 | 907 | ||
| ... | @@ -918,6 +919,49 @@ pub const Dir = struct { | ... | @@ -918,6 +919,49 @@ pub const Dir = struct { |
| 918 | }; | 919 | }; |
| 919 | } | 920 | } |
| 920 | 921 | ||
| 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 | |||
| 957 | pub 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 | } |
| 1336 | 1380 | ||
| 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 of | 1390 | /// This function returns the canonicalized absolute pathname of |
| 1338 | /// `pathname` relative to this `Dir`. If `pathname` is absolute, ignores this | 1391 | /// `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, |
| 1485 | 1538 | ||
| 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 | } |
| 1509 | 1574 | ||
| ... | @@ -1556,13 +1621,13 @@ pub const Dir = struct { | ... | @@ -1556,13 +1621,13 @@ pub const Dir = struct { |
| 1556 | } | 1621 | } |
| 1557 | 1622 | ||
| 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 { |
| 1572 | 1637 | ||
| 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 false | 1642 | // 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 | } |
| 1583 | 1649 | ||
| 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(); |
| 1989 | 2055 | ||
| 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(); |
| 1992 | 2058 | ||
| 1993 | // Valid use of MAX_PATH_BYTES because dir_name_buf will only | 2059 | // Valid use of MAX_PATH_BYTES because dir_name_buf will only |
| 1994 | // ever store a single path component that was returned from the | 2060 | // 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. |
| 2002 | 2068 | ||
| 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 | } |
| 2028 | 2094 | ||
| 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; |
| 2066 | 2132 | ||
| 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 | } |
| 2248 | 2314 | ||
| 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; |
| 2282 | 2317 |
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; |
| 8 | 8 | ||
| 9 | const ArenaAllocator = std.heap.ArenaAllocator; | 9 | const ArenaAllocator = std.heap.ArenaAllocator; |
| 10 | const Dir = std.fs.Dir; | 10 | const Dir = std.fs.Dir; |
| 11 | const IterableDir = std.fs.IterableDir; | ||
| 11 | const File = std.fs.File; | 12 | const File = std.fs.File; |
| 12 | const tmpDir = testing.tmpDir; | 13 | const tmpDir = testing.tmpDir; |
| 14 | const tmpIterableDir = testing.tmpIterableDir; | ||
| 13 | 15 | ||
| 14 | test "Dir.readLink" { | 16 | test "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 | } |
| 156 | 158 | ||
| 157 | test "Dir.Iterator" { | 159 | test "Dir.Iterator" { |
| 158 | var tmp_dir = tmpDir(.{ .iterate = true }); | 160 | var tmp_dir = tmpIterableDir(.{}); |
| 159 | defer tmp_dir.cleanup(); | 161 | defer tmp_dir.cleanup(); |
| 160 | 162 | ||
| 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(); |
| 164 | 166 | ||
| 165 | try tmp_dir.dir.makeDir("some_dir"); | 167 | try tmp_dir.iterable_dir.dir.makeDir("some_dir"); |
| 166 | 168 | ||
| 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(); |
| 170 | 172 | ||
| 171 | var entries = std.ArrayList(Dir.Entry).init(allocator); | 173 | var entries = std.ArrayList(IterableDir.Entry).init(allocator); |
| 172 | 174 | ||
| 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 buffer | 178 | // 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 | } |
| 181 | 183 | ||
| 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 | } |
| 186 | 188 | ||
| 187 | test "Dir.Iterator twice" { | 189 | test "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(); |
| 190 | 192 | ||
| 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(); |
| 194 | 196 | ||
| 195 | try tmp_dir.dir.makeDir("some_dir"); | 197 | try tmp_dir.iterable_dir.dir.makeDir("some_dir"); |
| 196 | 198 | ||
| 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" { |
| 200 | 202 | ||
| 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); |
| 204 | 206 | ||
| 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 buffer | 210 | // 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 | } |
| 213 | 215 | ||
| 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 | } |
| 219 | 221 | ||
| 220 | fn entryEql(lhs: Dir.Entry, rhs: Dir.Entry) bool { | 222 | fn 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 | } |
| 223 | 225 | ||
| 224 | fn contains(entries: *const std.ArrayList(Dir.Entry), el: Dir.Entry) bool { | 226 | fn 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, "/"); |
| 987 | 989 | ||
| 988 | var tmp = tmpDir(.{ .iterate = true }); | 990 | var tmp = tmpIterableDir(.{}); |
| 989 | defer tmp.cleanup(); | 991 | defer tmp.cleanup(); |
| 990 | 992 | ||
| 991 | // iteration order of walker is undefined, so need lookup maps to check against | 993 | // 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 | }); |
| 1012 | 1014 | ||
| 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 | } |
| 1016 | 1018 | ||
| 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(); |
| 1019 | 1021 | ||
| 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); |
| 1122 | 1124 | ||
| 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(); |
| 1126 | 1128 | ||
| 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 | } |
| 1130 | 1132 | ||
| 1131 | test "chown" { | 1133 | test "chown" { |
| ... | @@ -1141,9 +1143,9 @@ test "chown" { | ... | @@ -1141,9 +1143,9 @@ test "chown" { |
| 1141 | 1143 | ||
| 1142 | try tmp.dir.makeDir("test_dir"); | 1144 | try tmp.dir.makeDir("test_dir"); |
| 1143 | 1145 | ||
| 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 | } |
| 1148 | 1150 | ||
| 1149 | test "File.Metadata" { | 1151 | test "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. |
| 312 | 312 | ||
| 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. |
| 353 | 353 | ||
| 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 | }; |
| 365 | 365 | ||
| 366 | pub 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 | |||
| 366 | fn getCwdOrWasiPreopen() std.fs.Dir { | 382 | fn 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 | } |
| 402 | 418 | ||
| 419 | pub 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 | |||
| 403 | test "expectEqual nested array" { | 441 | test "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(); |
| 4217 | 4217 | ||
| 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; |
| 4220 | 4220 | ||
| 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; |
| 4224 | 4224 | ||
| ... | @@ -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); |
| 4230 | 4230 | ||
| 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 | }); |
| 56 | 56 | ||
| 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(); |
| 59 | 59 | ||
| 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 at | 1080 | /// 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 of | 1081 | /// 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 { |
| 1091 | 1091 | ||
| 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 so | 1095 | /// 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); |
| 1101 | 1101 | ||
| 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; |
| 1124 | 1124 | ||
| 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); |
| 1127 | 1127 | ||
| 1128 | // Parse the manifest | 1128 | // 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); |
| 382 | 382 | ||
| 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(); |
| 390 | 390 | ||
| 391 | var dir_it = dir.iterate(); | 391 | var dir_it = iterable_dir.iterate(); |
| 392 | 392 | ||
| 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 { |
| 14 | 14 | ||
| 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, .{}); |
| 18 | 18 | ||
| 19 | var walker = try dir.walk(arena); | 19 | var walker = try iterable_dir.walk(arena); |
| 20 | defer walker.deinit(); | 20 | defer walker.deinit(); |
| 21 | 21 | ||
| 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(); |
| 32 | 32 | ||
| 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); |
| 44 | 44 | ||
| 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); |
| 182 | 182 | ||
| 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(); |
| 190 | 190 | ||
| 191 | var dir_it = dir.iterate(); | 191 | var dir_it = iterable_dir.iterate(); |
| 192 | 192 | ||
| 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 { |
| 41 | 41 | ||
| 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}); |
| 43 | 43 | ||
| 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 | } |
| 65 | 65 | ||
| 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 omitted | 79 | // 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/*. |
| 81 | 81 | ||
| 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. |
| 219 | fn gather_extensions(allocator: Allocator, spirv_registry_root: []const u8) ![]const []const u8 { | 219 | fn 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(); |
| 223 | 223 | ||
| 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. |
| 229 | 229 | ||
| 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_name | 250 | // SPV_EXT_name |
| 251 | // ``` | 251 | // ``` |
| 252 | 252 | ||
| 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"; |
| 255 | 255 | ||
| 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; |