| ... | @@ -274,7 +274,7 @@ pub fn deleteTree(full_path: []const u8) !void { | ... | @@ -274,7 +274,7 @@ pub fn deleteTree(full_path: []const u8) !void { |
| 274 | CannotDeleteRootDirectory, | 274 | CannotDeleteRootDirectory, |
| 275 | }.CannotDeleteRootDirectory; | 275 | }.CannotDeleteRootDirectory; |
| 276 | | 276 | |
| 277 | var dir = try cwd().openDirList(dirname); | 277 | var dir = try cwd().openDir(dirname, .{}); |
| 278 | defer dir.close(); | 278 | defer dir.close(); |
| 279 | | 279 | |
| 280 | return dir.deleteTree(path.basename(full_path)); | 280 | return dir.deleteTree(path.basename(full_path)); |
| ... | @@ -339,7 +339,7 @@ pub const Dir = struct { | ... | @@ -339,7 +339,7 @@ pub const Dir = struct { |
| 339 | if (rc == 0) return null; | 339 | if (rc == 0) return null; |
| 340 | if (rc < 0) { | 340 | if (rc < 0) { |
| 341 | switch (os.errno(rc)) { | 341 | switch (os.errno(rc)) { |
| 342 | os.EBADF => unreachable, | 342 | os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability |
| 343 | os.EFAULT => unreachable, | 343 | os.EFAULT => unreachable, |
| 344 | os.ENOTDIR => unreachable, | 344 | os.ENOTDIR => unreachable, |
| 345 | os.EINVAL => unreachable, | 345 | os.EINVAL => unreachable, |
| ... | @@ -388,7 +388,7 @@ pub const Dir = struct { | ... | @@ -388,7 +388,7 @@ pub const Dir = struct { |
| 388 | ); | 388 | ); |
| 389 | switch (os.errno(rc)) { | 389 | switch (os.errno(rc)) { |
| 390 | 0 => {}, | 390 | 0 => {}, |
| 391 | os.EBADF => unreachable, | 391 | os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability |
| 392 | os.EFAULT => unreachable, | 392 | os.EFAULT => unreachable, |
| 393 | os.ENOTDIR => unreachable, | 393 | os.ENOTDIR => unreachable, |
| 394 | os.EINVAL => unreachable, | 394 | os.EINVAL => unreachable, |
| ... | @@ -444,7 +444,7 @@ pub const Dir = struct { | ... | @@ -444,7 +444,7 @@ pub const Dir = struct { |
| 444 | const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len); | 444 | const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len); |
| 445 | switch (os.linux.getErrno(rc)) { | 445 | switch (os.linux.getErrno(rc)) { |
| 446 | 0 => {}, | 446 | 0 => {}, |
| 447 | os.EBADF => unreachable, | 447 | os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability |
| 448 | os.EFAULT => unreachable, | 448 | os.EFAULT => unreachable, |
| 449 | os.ENOTDIR => unreachable, | 449 | os.ENOTDIR => unreachable, |
| 450 | os.EINVAL => unreachable, | 450 | os.EINVAL => unreachable, |
| ... | @@ -555,36 +555,6 @@ pub const Dir = struct { | ... | @@ -555,36 +555,6 @@ pub const Dir = struct { |
| 555 | }; | 555 | }; |
| 556 | | 556 | |
| 557 | pub fn iterate(self: Dir) Iterator { | 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 | switch (builtin.os.tag) { | 558 | switch (builtin.os.tag) { |
| 589 | .macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{ | 559 | .macosx, .ios, .freebsd, .netbsd, .dragonfly => return Iterator{ |
| 590 | .dir = self, | 560 | .dir = self, |
| ... | @@ -626,16 +596,6 @@ pub const Dir = struct { | ... | @@ -626,16 +596,6 @@ pub const Dir = struct { |
| 626 | DeviceBusy, | 596 | DeviceBusy, |
| 627 | } || os.UnexpectedError; | 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 | pub fn close(self: *Dir) void { | 599 | pub fn close(self: *Dir) void { |
| 640 | if (need_async_thread) { | 600 | if (need_async_thread) { |
| 641 | std.event.Loop.instance.?.close(self.fd); | 601 | std.event.Loop.instance.?.close(self.fd); |
| ... | @@ -822,79 +782,61 @@ pub const Dir = struct { | ... | @@ -822,79 +782,61 @@ pub const Dir = struct { |
| 822 | try os.fchdir(self.fd); | 782 | try os.fchdir(self.fd); |
| 823 | } | 783 | } |
| 824 | | 784 | |
| 825 | /// Deprecated; call `openDirList` directly. | 785 | pub const OpenDirOptions = struct { |
| 826 | pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir { | 786 | /// `true` means the opened directory can be used as the `Dir` parameter |
| 827 | return self.openDirList(sub_path); | 787 | /// for functions which operate based on an open directory handle. When `false`, |
| 828 | } | 788 | /// such operations are Illegal Behavior. |
| | 789 | access_sub_paths: bool = true, |
| 829 | | 790 | |
| 830 | /// Deprecated; call `openDirListC` directly. | 791 | /// `true` means the opened directory can be scanned for the files and sub-directories |
| 831 | pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { | 792 | /// of the result. It means the `iterate` function can be called. |
| 832 | return self.openDirListC(sub_path_c); | 793 | iterate: bool = false, |
| 833 | } | 794 | }; |
| 834 | | 795 | |
| 835 | /// Opens a directory at the given path with the ability to access subpaths | 796 | /// Opens a directory at the given path. The directory is a system resource that remains |
| 836 | /// of the result. Calling `iterate` on the result is illegal behavior; to | 797 | /// open until `close` is called on the result. |
| 837 | /// list the contents of a directory, open it with `openDirList`. | | |
| 838 | /// | | |
| 839 | /// Call `close` on the result when done. | | |
| 840 | /// | 798 | /// |
| 841 | /// Asserts that the path parameter has no null bytes. | 799 | /// Asserts that the path parameter has no null bytes. |
| 842 | /// TODO collapse this and `openDirList` into one function with an options parameter | 800 | pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir { |
| 843 | pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir { | | |
| 844 | if (builtin.os.tag == .windows) { | 801 | if (builtin.os.tag == .windows) { |
| 845 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); | 802 | const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); |
| 846 | return self.openDirTraverseW(&sub_path_w); | 803 | return self.openDirW(&sub_path_w, args); |
| 847 | } | 804 | } else { |
| 848 | | 805 | const sub_path_c = try os.toPosixPath(sub_path); |
| 849 | const sub_path_c = try os.toPosixPath(sub_path); | 806 | return self.openDirC(&sub_path_c, args); |
| 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); | | |
| 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. | 810 | /// Same as `openDir` except the parameter is null-terminated. |
| 872 | pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { | 811 | pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir { |
| 873 | if (builtin.os.tag == .windows) { | 812 | if (builtin.os.tag == .windows) { |
| 874 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); | 813 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); |
| 875 | return self.openDirTraverseW(&sub_path_w); | 814 | return self.openDirW(&sub_path_w, args); |
| 876 | } else { | 815 | } else if (!args.iterate) { |
| 877 | const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; | 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. | 823 | /// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed. |
| 883 | pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir { | 824 | /// This function asserts the target OS is Windows. |
| 884 | if (builtin.os.tag == .windows) { | 825 | pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir { |
| 885 | const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c); | 826 | const w = os.windows; |
| 886 | return self.openDirListW(&sub_path_w); | 827 | // TODO remove some of these flags if args.access_sub_paths is false |
| 887 | } else { | 828 | const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | |
| 888 | return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC); | 829 | w.SYNCHRONIZE | w.FILE_TRAVERSE; |
| 889 | } | 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 | fn openDirFlagsC(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir { | 835 | fn openDirFlagsC(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir { |
| 893 | const os_flags = flags | os.O_DIRECTORY; | | |
| 894 | const result = if (need_async_thread) | 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 | else | 838 | else |
| 897 | os.openatC(self.fd, sub_path_c, os_flags, 0); | 839 | os.openatC(self.fd, sub_path_c, flags, 0); |
| 898 | const fd = result catch |err| switch (err) { | 840 | const fd = result catch |err| switch (err) { |
| 899 | error.FileTooBig => unreachable, // can't happen for directories | 841 | error.FileTooBig => unreachable, // can't happen for directories |
| 900 | error.IsDir => unreachable, // we're providing O_DIRECTORY | 842 | error.IsDir => unreachable, // we're providing O_DIRECTORY |
| ... | @@ -905,22 +847,6 @@ pub const Dir = struct { | ... | @@ -905,22 +847,6 @@ pub const Dir = struct { |
| 905 | return Dir{ .fd = fd }; | 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 | fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir { | 850 | fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir { |
| 925 | const w = os.windows; | 851 | const w = os.windows; |
| 926 | | 852 | |
| ... | @@ -1141,7 +1067,7 @@ pub const Dir = struct { | ... | @@ -1141,7 +1067,7 @@ pub const Dir = struct { |
| 1141 | error.Unexpected, | 1067 | error.Unexpected, |
| 1142 | => |e| return e, | 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 | error.NotDir => { | 1071 | error.NotDir => { |
| 1146 | if (got_access_denied) { | 1072 | if (got_access_denied) { |
| 1147 | return error.AccessDenied; | 1073 | return error.AccessDenied; |
| ... | @@ -1174,7 +1100,6 @@ pub const Dir = struct { | ... | @@ -1174,7 +1100,6 @@ pub const Dir = struct { |
| 1174 | | 1100 | |
| 1175 | var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined; | 1101 | var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1176 | var dir_name: []const u8 = sub_path; | 1102 | var dir_name: []const u8 = sub_path; |
| 1177 | var parent_dir = self; | | |
| 1178 | | 1103 | |
| 1179 | // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function. | 1104 | // Here we must avoid recursion, in order to provide O(1) memory guarantee of this function. |
| 1180 | // Go through each entry and if it is not a directory, delete it. If it is a directory, | 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,7 +1131,7 @@ pub const Dir = struct { |
| 1206 | => |e| return e, | 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 | error.NotDir => { | 1135 | error.NotDir => { |
| 1211 | if (got_access_denied) { | 1136 | if (got_access_denied) { |
| 1212 | return error.AccessDenied; | 1137 | return error.AccessDenied; |
| ... | @@ -1491,7 +1416,7 @@ pub const Walker = struct { | ... | @@ -1491,7 +1416,7 @@ pub const Walker = struct { |
| 1491 | try self.name_buffer.appendByte(path.sep); | 1416 | try self.name_buffer.appendByte(path.sep); |
| 1492 | try self.name_buffer.append(base.name); | 1417 | try self.name_buffer.append(base.name); |
| 1493 | if (base.kind == .Directory) { | 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 | error.NameTooLong => unreachable, // no path sep in base.name | 1420 | error.NameTooLong => unreachable, // no path sep in base.name |
| 1496 | else => |e| return e, | 1421 | else => |e| return e, |
| 1497 | }; | 1422 | }; |
| ... | @@ -1529,7 +1454,7 @@ pub const Walker = struct { | ... | @@ -1529,7 +1454,7 @@ pub const Walker = struct { |
| 1529 | pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { | 1454 | pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker { |
| 1530 | assert(!mem.endsWith(u8, dir_path, path.sep_str)); | 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 | errdefer dir.close(); | 1458 | errdefer dir.close(); |
| 1534 | | 1459 | |
| 1535 | var name_buffer = try std.Buffer.init(allocator, dir_path); | 1460 | var name_buffer = try std.Buffer.init(allocator, dir_path); |