| ... | ... | @@ -274,7 +274,7 @@ pub fn deleteTree(full_path: []const u8) !void { |
| 274 | 274 | CannotDeleteRootDirectory, |
| 275 | 275 | }.CannotDeleteRootDirectory; |
| 276 | 276 | |
| 277 | | var dir = try cwd().openDirList(dirname); |
| 277 | var dir = try cwd().openDir(dirname, .{}); |
| 278 | 278 | defer dir.close(); |
| 279 | 279 | |
| 280 | 280 | return dir.deleteTree(path.basename(full_path)); |
| ... | ... | @@ -339,7 +339,7 @@ pub const Dir = struct { |
| 339 | 339 | if (rc == 0) return null; |
| 340 | 340 | if (rc < 0) { |
| 341 | 341 | switch (os.errno(rc)) { |
| 342 | | os.EBADF => unreachable, |
| 342 | os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability |
| 343 | 343 | os.EFAULT => unreachable, |
| 344 | 344 | os.ENOTDIR => unreachable, |
| 345 | 345 | os.EINVAL => unreachable, |
| ... | ... | @@ -388,7 +388,7 @@ pub const Dir = struct { |
| 388 | 388 | ); |
| 389 | 389 | switch (os.errno(rc)) { |
| 390 | 390 | 0 => {}, |
| 391 | | os.EBADF => unreachable, |
| 391 | os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability |
| 392 | 392 | os.EFAULT => unreachable, |
| 393 | 393 | os.ENOTDIR => unreachable, |
| 394 | 394 | os.EINVAL => unreachable, |
| ... | ... | @@ -444,7 +444,7 @@ pub const Dir = struct { |
| 444 | 444 | const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len); |
| 445 | 445 | switch (os.linux.getErrno(rc)) { |
| 446 | 446 | 0 => {}, |
| 447 | | os.EBADF => unreachable, |
| 447 | os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability |
| 448 | 448 | os.EFAULT => unreachable, |
| 449 | 449 | os.ENOTDIR => unreachable, |
| 450 | 450 | os.EINVAL => unreachable, |
| ... | ... | @@ -555,36 +555,6 @@ pub const Dir = struct { |
| 555 | 555 | }; |
| 556 | 556 | |
| 557 | 557 | pub fn iterate(self: Dir) Iterator { |
| 558 | | // Make sure the directory was not open with openDirTraverse |
| 559 | | if (std.debug.runtime_safety) { |
| 560 | | var ok = true; |
| 561 | | |
| 562 | | if (builtin.os.tag == .windows) { |
| 563 | | const w = os.windows; |
| 564 | | |
| 565 | | var io_status_block: w.IO_STATUS_BLOCK = undefined; |
| 566 | | var info: w.FILE_ACCESS_INFORMATION = undefined; |
| 567 | | |
| 568 | | const rc = w.ntdll.NtQueryInformationFile( |
| 569 | | self.fd, |
| 570 | | &io_status_block, |
| 571 | | &info, |
| 572 | | @sizeOf(w.FILE_ACCESS_INFORMATION), |
| 573 | | .FileAccessInformation, |
| 574 | | ); |
| 575 | | assert(rc == .SUCCESS); |
| 576 | | |
| 577 | | ok = (info.AccessFlags & w.FILE_LIST_DIRECTORY) != 0; |
| 578 | | } else if (@hasDecl(os, "O_PATH")) { |
| 579 | | const f = os.fcntl(self.fd, os.F_GETFL, 0) catch unreachable; |
| 580 | | ok = (f & os.O_PATH) == 0; |
| 581 | | } |
| 582 | | |
| 583 | | if (!ok) { |
| 584 | | std.debug.panic("iterate() called on Dir open with openDirTraverse", .{}); |
| 585 | | } |
| 586 | | } |
| 587 | | |
| 588 | 558 | switch (builtin.os.tag) { |
| 589 | 559 | .macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{ |
| 590 | 560 | .dir = self, |
| ... | ... | @@ -626,16 +596,6 @@ pub const Dir = struct { |
| 626 | 596 | DeviceBusy, |
| 627 | 597 | } || os.UnexpectedError; |
| 628 | 598 | |
| 629 | | /// Deprecated; call `cwd().openDirList` directly. |
| 630 | | pub fn open(dir_path: []const u8) OpenError!Dir { |
| 631 | | return cwd().openDirList(dir_path); |
| 632 | | } |
| 633 | | |
| 634 | | /// Deprecated; call `cwd().openDirListC` directly. |
| 635 | | pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir { |
| 636 | | return cwd().openDirListC(dir_path_c); |
| 637 | | } |
| 638 | | |
| 639 | 599 | pub fn close(self: *Dir) void { |
| 640 | 600 | if (need_async_thread) { |
| 641 | 601 | std.event.Loop.instance.?.close(self.fd); |
| ... | ... | @@ -822,79 +782,61 @@ pub const Dir = struct { |
| 822 | 782 | try os.fchdir(self.fd); |
| 823 | 783 | } |
| 824 | 784 | |
| 825 | | /// Deprecated; call `openDirList` directly. |
| 826 | | pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir { |
| 827 | | return self.openDirList(sub_path); |
| 828 | | } |
| 785 | pub const OpenDirOptions = struct { |
| 786 | /// `true` means the opened directory can be used as the `Dir` parameter |
| 787 | /// for functions which operate based on an open directory handle. When `false`, |
| 788 | /// such operations are Illegal Behavior. |
| 789 | access_sub_paths: bool = true, |
| 829 | 790 | |
| 830 | | /// Deprecated; call `openDirListC` directly. |
| 831 | | pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { |
| 832 | | return self.openDirListC(sub_path_c); |
| 833 | | } |
| 791 | /// `true` means the opened directory can be scanned for the files and sub-directories |
| 792 | /// of the result. It means the `iterate` function can be called. |
| 793 | iterate: bool = false, |
| 794 | }; |
| 834 | 795 | |
| 835 | | /// Opens a directory at the given path with the ability to access subpaths |
| 836 | | /// of the result. Calling `iterate` on the result is illegal behavior; to |
| 837 | | /// list the contents of a directory, open it with `openDirList`. |
| 838 | | /// |
| 839 | | /// Call `close` on the result when done. |
| 796 | /// Opens a directory at the given path. The directory is a system resource that remains |
| 797 | /// open until `close` is called on the result. |
| 840 | 798 | /// |
| 841 | 799 | /// Asserts that the path parameter has no null bytes. |
| 842 | | /// TODO collapse this and `openDirList` into one function with an options parameter |
| 843 | | pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { |
| 800 | pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { |
| 844 | 801 | if (builtin.os.tag == .windows) { |
| 845 | 802 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 846 | | return self.openDirTraverseW(&sub_path_w); |
| 847 | | } |
| 848 | | |
| 849 | | const sub_path_c = try os.toPosixPath(sub_path); |
| 850 | | return self.openDirTraverseC(&sub_path_c); |
| 851 | | } |
| 852 | | |
| 853 | | /// Opens a directory at the given path with the ability to access subpaths and list contents |
| 854 | | /// of the result. If the ability to list contents is unneeded, `openDirTraverse` acts the |
| 855 | | /// same and may be more efficient. |
| 856 | | /// |
| 857 | | /// Call `close` on the result when done. |
| 858 | | /// |
| 859 | | /// Asserts that the path parameter has no null bytes. |
| 860 | | /// TODO collapse this and `openDirTraverse` into one function with an options parameter |
| 861 | | pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir { |
| 862 | | if (builtin.os.tag == .windows) { |
| 863 | | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 864 | | return self.openDirListW(&sub_path_w); |
| 803 | return self.openDirW(&sub_path_w, args); |
| 804 | } else { |
| 805 | const sub_path_c = try os.toPosixPath(sub_path); |
| 806 | return self.openDirC(&sub_path_c, args); |
| 865 | 807 | } |
| 866 | | |
| 867 | | const sub_path_c = try os.toPosixPath(sub_path); |
| 868 | | return self.openDirListC(&sub_path_c); |
| 869 | 808 | } |
| 870 | 809 | |
| 871 | | /// Same as `openDirTraverse` except the parameter is null-terminated. |
| 872 | | pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { |
| 810 | /// Same as `openDir` except the parameter is null-terminated. |
| 811 | pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir { |
| 873 | 812 | if (builtin.os.tag == .windows) { |
| 874 | 813 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 875 | | return self.openDirTraverseW(&sub_path_w); |
| 876 | | } else { |
| 814 | return self.openDirW(&sub_path_w, args); |
| 815 | } else if (!args.iterate) { |
| 877 | 816 | const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; |
| 878 | | return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC | O_PATH); |
| 817 | return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH); |
| 818 | } else { |
| 819 | return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC); |
| 879 | 820 | } |
| 880 | 821 | } |
| 881 | 822 | |
| 882 | | /// Same as `openDirList` except the parameter is null-terminated. |
| 883 | | pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { |
| 884 | | if (builtin.os.tag == .windows) { |
| 885 | | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 886 | | return self.openDirListW(&sub_path_w); |
| 887 | | } else { |
| 888 | | return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC); |
| 889 | | } |
| 823 | /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed. |
| 824 | /// This function asserts the target OS is Windows. |
| 825 | pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir { |
| 826 | const w = os.windows; |
| 827 | // TODO remove some of these flags if args.access_sub_paths is false |
| 828 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 829 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| 830 | const flags: u32 = if (args.iterate) base_flags else base_flags | w.FILE_LIST_DIRECTORY; |
| 831 | return self.openDirAccessMaskW(sub_path_w, flags); |
| 890 | 832 | } |
| 891 | 833 | |
| 834 | /// `flags` must contain `os.O_DIRECTORY`. |
| 892 | 835 | fn openDirFlagsC(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir { |
| 893 | | const os_flags = flags | os.O_DIRECTORY; |
| 894 | 836 | const result = if (need_async_thread) |
| 895 | | std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, 0) |
| 837 | std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, flags, 0) |
| 896 | 838 | else |
| 897 | | os.openatC(self.fd, sub_path_c, os_flags, 0); |
| 839 | os.openatC(self.fd, sub_path_c, flags, 0); |
| 898 | 840 | const fd = result catch |err| switch (err) { |
| 899 | 841 | error.FileTooBig => unreachable, // can't happen for directories |
| 900 | 842 | error.IsDir => unreachable, // we're providing O_DIRECTORY |
| ... | ... | @@ -905,22 +847,6 @@ pub const Dir = struct { |
| 905 | 847 | return Dir{ .fd = fd }; |
| 906 | 848 | } |
| 907 | 849 | |
| 908 | | /// Same as `openDirTraverse` except the path parameter is UTF16LE, NT-prefixed. |
| 909 | | /// This function is Windows-only. |
| 910 | | pub fn openDirTraverseW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir { |
| 911 | | const w = os.windows; |
| 912 | | |
| 913 | | return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE); |
| 914 | | } |
| 915 | | |
| 916 | | /// Same as `openDirList` except the path parameter is UTF16LE, NT-prefixed. |
| 917 | | /// This function is Windows-only. |
| 918 | | pub fn openDirListW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir { |
| 919 | | const w = os.windows; |
| 920 | | |
| 921 | | return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE | w.FILE_LIST_DIRECTORY); |
| 922 | | } |
| 923 | | |
| 924 | 850 | fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir { |
| 925 | 851 | const w = os.windows; |
| 926 | 852 | |
| ... | ... | @@ -1141,7 +1067,7 @@ pub const Dir = struct { |
| 1141 | 1067 | error.Unexpected, |
| 1142 | 1068 | => |e| return e, |
| 1143 | 1069 | } |
| 1144 | | var dir = self.openDirList(sub_path) catch |err| switch (err) { |
| 1070 | var dir = self.openDir(sub_path, .{ .iterate = true }) catch |err| switch (err) { |
| 1145 | 1071 | error.NotDir => { |
| 1146 | 1072 | if (got_access_denied) { |
| 1147 | 1073 | return error.AccessDenied; |
| ... | ... | @@ -1174,7 +1100,6 @@ pub const Dir = struct { |
| 1174 | 1100 | |
| 1175 | 1101 | var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1176 | 1102 | var dir_name: []const u8 = sub_path; |
| 1177 | | var parent_dir = self; |
| 1178 | 1103 | |
| 1179 | 1104 | // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function. |
| 1180 | 1105 | // Go through each entry and if it is not a directory, delete it. If it is a directory, |
| ... | ... | @@ -1206,7 +1131,7 @@ pub const Dir = struct { |
| 1206 | 1131 | => |e| return e, |
| 1207 | 1132 | } |
| 1208 | 1133 | |
| 1209 | | const new_dir = dir.openDirList(entry.name) catch |err| switch (err) { |
| 1134 | const new_dir = dir.openDir(entry.name, .{ .iterate = true }) catch |err| switch (err) { |
| 1210 | 1135 | error.NotDir => { |
| 1211 | 1136 | if (got_access_denied) { |
| 1212 | 1137 | return error.AccessDenied; |
| ... | ... | @@ -1491,7 +1416,7 @@ pub const Walker = struct { |
| 1491 | 1416 | try self.name_buffer.appendByte(path.sep); |
| 1492 | 1417 | try self.name_buffer.append(base.name); |
| 1493 | 1418 | if (base.kind == .Directory) { |
| 1494 | | var new_dir = top.dir_it.dir.openDirList(base.name) catch |err| switch (err) { |
| 1419 | var new_dir = top.dir_it.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { |
| 1495 | 1420 | error.NameTooLong => unreachable, // no path sep in base.name |
| 1496 | 1421 | else => |e| return e, |
| 1497 | 1422 | }; |
| ... | ... | @@ -1529,7 +1454,7 @@ pub const Walker = struct { |
| 1529 | 1454 | pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { |
| 1530 | 1455 | assert(!mem.endsWith(u8, dir_path, path.sep_str)); |
| 1531 | 1456 | |
| 1532 | | var dir = try cwd().openDirList(dir_path); |
| 1457 | var dir = try cwd().openDir(dir_path, .{ .iterate = true }); |
| 1533 | 1458 | errdefer dir.close(); |
| 1534 | 1459 | |
| 1535 | 1460 | var name_buffer = try std.Buffer.init(allocator, dir_path); |