| ... | @@ -301,7 +301,7 @@ pub const IterableDir = struct { | ... | @@ -301,7 +301,7 @@ pub const IterableDir = struct { |
| 301 | .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris => struct { | 301 | .macos, .ios, .freebsd, .netbsd, .dragonfly, .openbsd, .solaris => struct { |
| 302 | dir: Dir, | 302 | dir: Dir, |
| 303 | seek: i64, | 303 | seek: i64, |
| 304 | buf: [8192]u8, // TODO align(@alignOf(os.system.dirent)), | 304 | buf: [1024]u8, // TODO align(@alignOf(os.system.dirent)), |
| 305 | index: usize, | 305 | index: usize, |
| 306 | end_index: usize, | 306 | end_index: usize, |
| 307 | first_iter: bool, | 307 | first_iter: bool, |
| ... | @@ -490,10 +490,16 @@ pub const IterableDir = struct { | ... | @@ -490,10 +490,16 @@ pub const IterableDir = struct { |
| 490 | }; | 490 | }; |
| 491 | } | 491 | } |
| 492 | } | 492 | } |
| | 493 | |
| | 494 | pub fn reset(self: *Self) void { |
| | 495 | self.index = 0; |
| | 496 | self.end_index = 0; |
| | 497 | self.first_iter = true; |
| | 498 | } |
| 493 | }, | 499 | }, |
| 494 | .haiku => struct { | 500 | .haiku => struct { |
| 495 | dir: Dir, | 501 | dir: Dir, |
| 496 | buf: [8192]u8, // TODO align(@alignOf(os.dirent64)), | 502 | buf: [1024]u8, // TODO align(@alignOf(os.dirent64)), |
| 497 | index: usize, | 503 | index: usize, |
| 498 | end_index: usize, | 504 | end_index: usize, |
| 499 | first_iter: bool, | 505 | first_iter: bool, |
| ... | @@ -577,12 +583,18 @@ pub const IterableDir = struct { | ... | @@ -577,12 +583,18 @@ pub const IterableDir = struct { |
| 577 | }; | 583 | }; |
| 578 | } | 584 | } |
| 579 | } | 585 | } |
| | 586 | |
| | 587 | pub fn reset(self: *Self) void { |
| | 588 | self.index = 0; |
| | 589 | self.end_index = 0; |
| | 590 | self.first_iter = true; |
| | 591 | } |
| 580 | }, | 592 | }, |
| 581 | .linux => struct { | 593 | .linux => struct { |
| 582 | dir: Dir, | 594 | dir: Dir, |
| 583 | // The if guard is solely there to prevent compile errors from missing `linux.dirent64` | 595 | // The if guard is solely there to prevent compile errors from missing `linux.dirent64` |
| 584 | // definition when compiling for other OSes. It doesn't do anything when compiling for Linux. | 596 | // definition when compiling for other OSes. It doesn't do anything when compiling for Linux. |
| 585 | buf: [8192]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)), | 597 | buf: [1024]u8 align(if (builtin.os.tag != .linux) 1 else @alignOf(linux.dirent64)), |
| 586 | index: usize, | 598 | index: usize, |
| 587 | end_index: usize, | 599 | end_index: usize, |
| 588 | first_iter: bool, | 600 | first_iter: bool, |
| ... | @@ -655,10 +667,16 @@ pub const IterableDir = struct { | ... | @@ -655,10 +667,16 @@ pub const IterableDir = struct { |
| 655 | }; | 667 | }; |
| 656 | } | 668 | } |
| 657 | } | 669 | } |
| | 670 | |
| | 671 | pub fn reset(self: *Self) void { |
| | 672 | self.index = 0; |
| | 673 | self.end_index = 0; |
| | 674 | self.first_iter = true; |
| | 675 | } |
| 658 | }, | 676 | }, |
| 659 | .windows => struct { | 677 | .windows => struct { |
| 660 | dir: Dir, | 678 | dir: Dir, |
| 661 | buf: [8192]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)), | 679 | buf: [1024]u8 align(@alignOf(os.windows.FILE_BOTH_DIR_INFORMATION)), |
| 662 | index: usize, | 680 | index: usize, |
| 663 | end_index: usize, | 681 | end_index: usize, |
| 664 | first_iter: bool, | 682 | first_iter: bool, |
| ... | @@ -727,10 +745,16 @@ pub const IterableDir = struct { | ... | @@ -727,10 +745,16 @@ pub const IterableDir = struct { |
| 727 | }; | 745 | }; |
| 728 | } | 746 | } |
| 729 | } | 747 | } |
| | 748 | |
| | 749 | pub fn reset(self: *Self) void { |
| | 750 | self.index = 0; |
| | 751 | self.end_index = 0; |
| | 752 | self.first_iter = true; |
| | 753 | } |
| 730 | }, | 754 | }, |
| 731 | .wasi => struct { | 755 | .wasi => struct { |
| 732 | dir: Dir, | 756 | dir: Dir, |
| 733 | buf: [8192]u8, // TODO align(@alignOf(os.wasi.dirent_t)), | 757 | buf: [1024]u8, // TODO align(@alignOf(os.wasi.dirent_t)), |
| 734 | cookie: u64, | 758 | cookie: u64, |
| 735 | index: usize, | 759 | index: usize, |
| 736 | end_index: usize, | 760 | end_index: usize, |
| ... | @@ -806,11 +830,28 @@ pub const IterableDir = struct { | ... | @@ -806,11 +830,28 @@ pub const IterableDir = struct { |
| 806 | }; | 830 | }; |
| 807 | } | 831 | } |
| 808 | } | 832 | } |
| | 833 | |
| | 834 | pub fn reset(self: *Self) void { |
| | 835 | self.index = 0; |
| | 836 | self.end_index = 0; |
| | 837 | self.cookie = os.wasi.DIRCOOKIE_START; |
| | 838 | } |
| 809 | }, | 839 | }, |
| 810 | else => @compileError("unimplemented"), | 840 | else => @compileError("unimplemented"), |
| 811 | }; | 841 | }; |
| 812 | | 842 | |
| 813 | pub fn iterate(self: IterableDir) Iterator { | 843 | pub fn iterate(self: IterableDir) Iterator { |
| | 844 | return self.iterateImpl(true); |
| | 845 | } |
| | 846 | |
| | 847 | /// Like `iterate`, but will not reset the directory cursor before the first |
| | 848 | /// iteration. This should only be used in cases where it is known that the |
| | 849 | /// `IterableDir` has not had its cursor modified yet (e.g. it was just opened). |
| | 850 | pub fn iterateAssumeFirstIteration(self: IterableDir) Iterator { |
| | 851 | return self.iterateImpl(false); |
| | 852 | } |
| | 853 | |
| | 854 | fn iterateImpl(self: IterableDir, first_iter_start_value: bool) Iterator { |
| 814 | switch (builtin.os.tag) { | 855 | switch (builtin.os.tag) { |
| 815 | .macos, | 856 | .macos, |
| 816 | .ios, | 857 | .ios, |
| ... | @@ -825,20 +866,20 @@ pub const IterableDir = struct { | ... | @@ -825,20 +866,20 @@ pub const IterableDir = struct { |
| 825 | .index = 0, | 866 | .index = 0, |
| 826 | .end_index = 0, | 867 | .end_index = 0, |
| 827 | .buf = undefined, | 868 | .buf = undefined, |
| 828 | .first_iter = true, | 869 | .first_iter = first_iter_start_value, |
| 829 | }, | 870 | }, |
| 830 | .linux, .haiku => return Iterator{ | 871 | .linux, .haiku => return Iterator{ |
| 831 | .dir = self.dir, | 872 | .dir = self.dir, |
| 832 | .index = 0, | 873 | .index = 0, |
| 833 | .end_index = 0, | 874 | .end_index = 0, |
| 834 | .buf = undefined, | 875 | .buf = undefined, |
| 835 | .first_iter = true, | 876 | .first_iter = first_iter_start_value, |
| 836 | }, | 877 | }, |
| 837 | .windows => return Iterator{ | 878 | .windows => return Iterator{ |
| 838 | .dir = self.dir, | 879 | .dir = self.dir, |
| 839 | .index = 0, | 880 | .index = 0, |
| 840 | .end_index = 0, | 881 | .end_index = 0, |
| 841 | .first_iter = true, | 882 | .first_iter = first_iter_start_value, |
| 842 | .buf = undefined, | 883 | .buf = undefined, |
| 843 | .name_data = undefined, | 884 | .name_data = undefined, |
| 844 | }, | 885 | }, |
| ... | @@ -2035,55 +2076,197 @@ pub const Dir = struct { | ... | @@ -2035,55 +2076,197 @@ pub const Dir = struct { |
| 2035 | /// this function recursively removes its entries and then tries again. | 2076 | /// this function recursively removes its entries and then tries again. |
| 2036 | /// This operation is not atomic on most file systems. | 2077 | /// This operation is not atomic on most file systems. |
| 2037 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { | 2078 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { |
| 2038 | start_over: while (true) { | 2079 | var initial_iterable_dir = (try self.deleteTreeOpenInitialSubpath(sub_path, .File)) orelse return; |
| 2039 | var got_access_denied = false; | 2080 | |
| 2040 | | 2081 | const StackItem = struct { |
| 2041 | // First, try deleting the item as a file. This way we don't follow sym links. | 2082 | name: []const u8, |
| 2042 | if (self.deleteFile(sub_path)) { | 2083 | parent_dir: Dir, |
| 2043 | return; | 2084 | iter: IterableDir.Iterator, |
| 2044 | } else |err| switch (err) { | 2085 | }; |
| 2045 | error.FileNotFound => return, | 2086 | |
| 2046 | error.IsDir => {}, | 2087 | var stack = std.BoundedArray(StackItem, 16){}; |
| 2047 | error.AccessDenied => got_access_denied = true, | 2088 | defer { |
| 2048 | | 2089 | for (stack.slice()) |*item| { |
| 2049 | error.InvalidUtf8, | 2090 | item.iter.dir.close(); |
| 2050 | error.SymLinkLoop, | | |
| 2051 | error.NameTooLong, | | |
| 2052 | error.SystemResources, | | |
| 2053 | error.ReadOnlyFileSystem, | | |
| 2054 | error.NotDir, | | |
| 2055 | error.FileSystem, | | |
| 2056 | error.FileBusy, | | |
| 2057 | error.BadPathName, | | |
| 2058 | error.Unexpected, | | |
| 2059 | => |e| return e, | | |
| 2060 | } | 2091 | } |
| 2061 | var iterable_dir = self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) { | 2092 | } |
| 2062 | error.NotDir => { | 2093 | |
| 2063 | if (got_access_denied) { | 2094 | stack.appendAssumeCapacity(StackItem{ |
| 2064 | return error.AccessDenied; | 2095 | .name = sub_path, |
| | 2096 | .parent_dir = self, |
| | 2097 | .iter = initial_iterable_dir.iterateAssumeFirstIteration(), |
| | 2098 | }); |
| | 2099 | |
| | 2100 | process_stack: while (stack.len != 0) { |
| | 2101 | var top = &(stack.slice()[stack.len - 1]); |
| | 2102 | while (try top.iter.next()) |entry| { |
| | 2103 | var treat_as_dir = entry.kind == .Directory; |
| | 2104 | handle_entry: while (true) { |
| | 2105 | if (treat_as_dir) { |
| | 2106 | if (stack.ensureUnusedCapacity(1)) { |
| | 2107 | var iterable_dir = top.iter.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) { |
| | 2108 | error.NotDir => { |
| | 2109 | treat_as_dir = false; |
| | 2110 | continue :handle_entry; |
| | 2111 | }, |
| | 2112 | error.FileNotFound => { |
| | 2113 | // That's fine, we were trying to remove this directory anyway. |
| | 2114 | break :handle_entry; |
| | 2115 | }, |
| | 2116 | |
| | 2117 | error.InvalidHandle, |
| | 2118 | error.AccessDenied, |
| | 2119 | error.SymLinkLoop, |
| | 2120 | error.ProcessFdQuotaExceeded, |
| | 2121 | error.NameTooLong, |
| | 2122 | error.SystemFdQuotaExceeded, |
| | 2123 | error.NoDevice, |
| | 2124 | error.SystemResources, |
| | 2125 | error.Unexpected, |
| | 2126 | error.InvalidUtf8, |
| | 2127 | error.BadPathName, |
| | 2128 | error.DeviceBusy, |
| | 2129 | => |e| return e, |
| | 2130 | }; |
| | 2131 | stack.appendAssumeCapacity(StackItem{ |
| | 2132 | .name = entry.name, |
| | 2133 | .parent_dir = top.iter.dir, |
| | 2134 | .iter = iterable_dir.iterateAssumeFirstIteration(), |
| | 2135 | }); |
| | 2136 | continue :process_stack; |
| | 2137 | } else |_| { |
| | 2138 | try top.iter.dir.deleteTreeMinStackSizeWithKindHint(entry.name, entry.kind); |
| | 2139 | break :handle_entry; |
| | 2140 | } |
| | 2141 | } else { |
| | 2142 | if (top.iter.dir.deleteFile(entry.name)) { |
| | 2143 | break :handle_entry; |
| | 2144 | } else |err| switch (err) { |
| | 2145 | error.FileNotFound => break :handle_entry, |
| | 2146 | |
| | 2147 | // Impossible because we do not pass any path separators. |
| | 2148 | error.NotDir => unreachable, |
| | 2149 | |
| | 2150 | error.IsDir => { |
| | 2151 | treat_as_dir = true; |
| | 2152 | continue :handle_entry; |
| | 2153 | }, |
| | 2154 | |
| | 2155 | error.AccessDenied, |
| | 2156 | error.InvalidUtf8, |
| | 2157 | error.SymLinkLoop, |
| | 2158 | error.NameTooLong, |
| | 2159 | error.SystemResources, |
| | 2160 | error.ReadOnlyFileSystem, |
| | 2161 | error.FileSystem, |
| | 2162 | error.FileBusy, |
| | 2163 | error.BadPathName, |
| | 2164 | error.Unexpected, |
| | 2165 | => |e| return e, |
| | 2166 | } |
| 2065 | } | 2167 | } |
| 2066 | continue :start_over; | 2168 | } |
| 2067 | }, | 2169 | } |
| 2068 | error.FileNotFound => { | | |
| 2069 | // That's fine, we were trying to remove this directory anyway. | | |
| 2070 | continue :start_over; | | |
| 2071 | }, | | |
| 2072 | | 2170 | |
| 2073 | error.InvalidHandle, | 2171 | // On Windows, we can't delete until the dir's handle has been closed, so |
| 2074 | error.AccessDenied, | 2172 | // close it before we try to delete. |
| 2075 | error.SymLinkLoop, | 2173 | top.iter.dir.close(); |
| 2076 | error.ProcessFdQuotaExceeded, | 2174 | |
| 2077 | error.NameTooLong, | 2175 | // In order to avoid double-closing the directory when cleaning up |
| 2078 | error.SystemFdQuotaExceeded, | 2176 | // the stack in the case of an error, we save the relevant portions and |
| 2079 | error.NoDevice, | 2177 | // pop the value from the stack. |
| 2080 | error.SystemResources, | 2178 | const parent_dir = top.parent_dir; |
| 2081 | error.Unexpected, | 2179 | const name = top.name; |
| 2082 | error.InvalidUtf8, | 2180 | _ = stack.pop(); |
| 2083 | error.BadPathName, | 2181 | |
| 2084 | error.DeviceBusy, | 2182 | var need_to_retry: bool = false; |
| 2085 | => |e| return e, | 2183 | parent_dir.deleteDir(name) catch |err| switch (err) { |
| | 2184 | error.FileNotFound => {}, |
| | 2185 | error.DirNotEmpty => need_to_retry = false, |
| | 2186 | else => |e| return e, |
| 2086 | }; | 2187 | }; |
| | 2188 | |
| | 2189 | if (need_to_retry) { |
| | 2190 | // Since we closed the handle that the previous iterator used, we |
| | 2191 | // need to re-open the dir and re-create the iterator. |
| | 2192 | var iterable_dir = iterable_dir: { |
| | 2193 | var treat_as_dir = true; |
| | 2194 | handle_entry: while (true) { |
| | 2195 | if (treat_as_dir) { |
| | 2196 | break :iterable_dir parent_dir.openIterableDir(name, .{ .no_follow = true }) catch |err| switch (err) { |
| | 2197 | error.NotDir => { |
| | 2198 | treat_as_dir = false; |
| | 2199 | continue :handle_entry; |
| | 2200 | }, |
| | 2201 | error.FileNotFound => { |
| | 2202 | // That's fine, we were trying to remove this directory anyway. |
| | 2203 | continue :process_stack; |
| | 2204 | }, |
| | 2205 | |
| | 2206 | error.InvalidHandle, |
| | 2207 | error.AccessDenied, |
| | 2208 | error.SymLinkLoop, |
| | 2209 | error.ProcessFdQuotaExceeded, |
| | 2210 | error.NameTooLong, |
| | 2211 | error.SystemFdQuotaExceeded, |
| | 2212 | error.NoDevice, |
| | 2213 | error.SystemResources, |
| | 2214 | error.Unexpected, |
| | 2215 | error.InvalidUtf8, |
| | 2216 | error.BadPathName, |
| | 2217 | error.DeviceBusy, |
| | 2218 | => |e| return e, |
| | 2219 | }; |
| | 2220 | } else { |
| | 2221 | if (parent_dir.deleteFile(name)) { |
| | 2222 | continue :process_stack; |
| | 2223 | } else |err| switch (err) { |
| | 2224 | error.FileNotFound => continue :process_stack, |
| | 2225 | |
| | 2226 | // Impossible because we do not pass any path separators. |
| | 2227 | error.NotDir => unreachable, |
| | 2228 | |
| | 2229 | error.IsDir => { |
| | 2230 | treat_as_dir = true; |
| | 2231 | continue :handle_entry; |
| | 2232 | }, |
| | 2233 | |
| | 2234 | error.AccessDenied, |
| | 2235 | error.InvalidUtf8, |
| | 2236 | error.SymLinkLoop, |
| | 2237 | error.NameTooLong, |
| | 2238 | error.SystemResources, |
| | 2239 | error.ReadOnlyFileSystem, |
| | 2240 | error.FileSystem, |
| | 2241 | error.FileBusy, |
| | 2242 | error.BadPathName, |
| | 2243 | error.Unexpected, |
| | 2244 | => |e| return e, |
| | 2245 | } |
| | 2246 | } |
| | 2247 | } |
| | 2248 | }; |
| | 2249 | // We know there is room on the stack since we are just re-adding |
| | 2250 | // the StackItem that we previously popped. |
| | 2251 | stack.appendAssumeCapacity(StackItem{ |
| | 2252 | .name = name, |
| | 2253 | .parent_dir = parent_dir, |
| | 2254 | .iter = iterable_dir.iterateAssumeFirstIteration(), |
| | 2255 | }); |
| | 2256 | continue :process_stack; |
| | 2257 | } |
| | 2258 | } |
| | 2259 | } |
| | 2260 | |
| | 2261 | /// Like `deleteTree`, but only keeps one `Iterator` active at a time to minimize the function's stack size. |
| | 2262 | /// This is slower than `deleteTree` but uses less stack space. |
| | 2263 | pub fn deleteTreeMinStackSize(self: Dir, sub_path: []const u8) DeleteTreeError!void { |
| | 2264 | return self.deleteTreeMinStackWithKindHint(sub_path, .File); |
| | 2265 | } |
| | 2266 | |
| | 2267 | fn deleteTreeMinStackSizeWithKindHint(self: Dir, sub_path: []const u8, kind_hint: File.Kind) DeleteTreeError!void { |
| | 2268 | start_over: while (true) { |
| | 2269 | var iterable_dir = (try self.deleteTreeOpenInitialSubpath(sub_path, kind_hint)) orelse return; |
| 2087 | var cleanup_dir_parent: ?IterableDir = null; | 2270 | var cleanup_dir_parent: ?IterableDir = null; |
| 2088 | defer if (cleanup_dir_parent) |*d| d.close(); | 2271 | defer if (cleanup_dir_parent) |*d| d.close(); |
| 2089 | | 2272 | |
| ... | @@ -2101,41 +2284,110 @@ pub const Dir = struct { | ... | @@ -2101,41 +2284,110 @@ pub const Dir = struct { |
| 2101 | // open it, and close the original directory. Repeat. Then start the entire operation over. | 2284 | // open it, and close the original directory. Repeat. Then start the entire operation over. |
| 2102 | | 2285 | |
| 2103 | scan_dir: while (true) { | 2286 | scan_dir: while (true) { |
| 2104 | var dir_it = iterable_dir.iterate(); | 2287 | var dir_it = iterable_dir.iterateAssumeFirstIteration(); |
| 2105 | while (try dir_it.next()) |entry| { | 2288 | dir_it: while (try dir_it.next()) |entry| { |
| 2106 | if (iterable_dir.dir.deleteFile(entry.name)) { | 2289 | var treat_as_dir = entry.kind == .Directory; |
| 2107 | continue; | 2290 | handle_entry: while (true) { |
| 2108 | } else |err| switch (err) { | 2291 | if (treat_as_dir) { |
| 2109 | error.FileNotFound => continue, | 2292 | const new_dir = iterable_dir.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) { |
| 2110 | | 2293 | error.NotDir => { |
| 2111 | // Impossible because we do not pass any path separators. | 2294 | treat_as_dir = false; |
| 2112 | error.NotDir => unreachable, | 2295 | continue :handle_entry; |
| | 2296 | }, |
| | 2297 | error.FileNotFound => { |
| | 2298 | // That's fine, we were trying to remove this directory anyway. |
| | 2299 | continue :dir_it; |
| | 2300 | }, |
| | 2301 | |
| | 2302 | error.InvalidHandle, |
| | 2303 | error.AccessDenied, |
| | 2304 | error.SymLinkLoop, |
| | 2305 | error.ProcessFdQuotaExceeded, |
| | 2306 | error.NameTooLong, |
| | 2307 | error.SystemFdQuotaExceeded, |
| | 2308 | error.NoDevice, |
| | 2309 | error.SystemResources, |
| | 2310 | error.Unexpected, |
| | 2311 | error.InvalidUtf8, |
| | 2312 | error.BadPathName, |
| | 2313 | error.DeviceBusy, |
| | 2314 | => |e| return e, |
| | 2315 | }; |
| | 2316 | if (cleanup_dir_parent) |*d| d.close(); |
| | 2317 | cleanup_dir_parent = iterable_dir; |
| | 2318 | iterable_dir = new_dir; |
| | 2319 | mem.copy(u8, &dir_name_buf, entry.name); |
| | 2320 | dir_name = dir_name_buf[0..entry.name.len]; |
| | 2321 | continue :scan_dir; |
| | 2322 | } else { |
| | 2323 | if (iterable_dir.dir.deleteFile(entry.name)) { |
| | 2324 | continue :dir_it; |
| | 2325 | } else |err| switch (err) { |
| | 2326 | error.FileNotFound => continue :dir_it, |
| | 2327 | |
| | 2328 | // Impossible because we do not pass any path separators. |
| | 2329 | error.NotDir => unreachable, |
| | 2330 | |
| | 2331 | error.IsDir => { |
| | 2332 | treat_as_dir = true; |
| | 2333 | continue :handle_entry; |
| | 2334 | }, |
| | 2335 | |
| | 2336 | error.AccessDenied, |
| | 2337 | error.InvalidUtf8, |
| | 2338 | error.SymLinkLoop, |
| | 2339 | error.NameTooLong, |
| | 2340 | error.SystemResources, |
| | 2341 | error.ReadOnlyFileSystem, |
| | 2342 | error.FileSystem, |
| | 2343 | error.FileBusy, |
| | 2344 | error.BadPathName, |
| | 2345 | error.Unexpected, |
| | 2346 | => |e| return e, |
| | 2347 | } |
| | 2348 | } |
| | 2349 | } |
| | 2350 | } |
| | 2351 | // Reached the end of the directory entries, which means we successfully deleted all of them. |
| | 2352 | // Now to remove the directory itself. |
| | 2353 | iterable_dir.close(); |
| | 2354 | cleanup_dir = false; |
| 2113 | | 2355 | |
| 2114 | error.IsDir => {}, | 2356 | if (cleanup_dir_parent) |d| { |
| 2115 | error.AccessDenied => got_access_denied = true, | 2357 | d.dir.deleteDir(dir_name) catch |err| switch (err) { |
| | 2358 | // These two things can happen due to file system race conditions. |
| | 2359 | error.FileNotFound, error.DirNotEmpty => continue :start_over, |
| | 2360 | else => |e| return e, |
| | 2361 | }; |
| | 2362 | continue :start_over; |
| | 2363 | } else { |
| | 2364 | self.deleteDir(sub_path) catch |err| switch (err) { |
| | 2365 | error.FileNotFound => return, |
| | 2366 | error.DirNotEmpty => continue :start_over, |
| | 2367 | else => |e| return e, |
| | 2368 | }; |
| | 2369 | return; |
| | 2370 | } |
| | 2371 | } |
| | 2372 | } |
| | 2373 | } |
| 2116 | | 2374 | |
| 2117 | error.InvalidUtf8, | 2375 | /// On successful delete, returns null. |
| 2118 | error.SymLinkLoop, | 2376 | fn deleteTreeOpenInitialSubpath(self: Dir, sub_path: []const u8, kind_hint: File.Kind) !?IterableDir { |
| 2119 | error.NameTooLong, | 2377 | return iterable_dir: { |
| 2120 | error.SystemResources, | 2378 | // Treat as a file by default |
| 2121 | error.ReadOnlyFileSystem, | 2379 | var treat_as_dir = kind_hint == .Directory; |
| 2122 | error.FileSystem, | | |
| 2123 | error.FileBusy, | | |
| 2124 | error.BadPathName, | | |
| 2125 | error.Unexpected, | | |
| 2126 | => |e| return e, | | |
| 2127 | } | | |
| 2128 | | 2380 | |
| 2129 | const new_dir = iterable_dir.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) { | 2381 | handle_entry: while (true) { |
| | 2382 | if (treat_as_dir) { |
| | 2383 | break :iterable_dir self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) { |
| 2130 | error.NotDir => { | 2384 | error.NotDir => { |
| 2131 | if (got_access_denied) { | 2385 | treat_as_dir = false; |
| 2132 | return error.AccessDenied; | 2386 | continue :handle_entry; |
| 2133 | } | | |
| 2134 | continue :scan_dir; | | |
| 2135 | }, | 2387 | }, |
| 2136 | error.FileNotFound => { | 2388 | error.FileNotFound => { |
| 2137 | // That's fine, we were trying to remove this directory anyway. | 2389 | // That's fine, we were trying to remove this directory anyway. |
| 2138 | continue :scan_dir; | 2390 | return null; |
| 2139 | }, | 2391 | }, |
| 2140 | | 2392 | |
| 2141 | error.InvalidHandle, | 2393 | error.InvalidHandle, |
| ... | @@ -2152,35 +2404,33 @@ pub const Dir = struct { | ... | @@ -2152,35 +2404,33 @@ pub const Dir = struct { |
| 2152 | error.DeviceBusy, | 2404 | error.DeviceBusy, |
| 2153 | => |e| return e, | 2405 | => |e| return e, |
| 2154 | }; | 2406 | }; |
| 2155 | if (cleanup_dir_parent) |*d| d.close(); | | |
| 2156 | cleanup_dir_parent = iterable_dir; | | |
| 2157 | iterable_dir = new_dir; | | |
| 2158 | mem.copy(u8, &dir_name_buf, entry.name); | | |
| 2159 | dir_name = dir_name_buf[0..entry.name.len]; | | |
| 2160 | continue :scan_dir; | | |
| 2161 | } | | |
| 2162 | // Reached the end of the directory entries, which means we successfully deleted all of them. | | |
| 2163 | // Now to remove the directory itself. | | |
| 2164 | iterable_dir.close(); | | |
| 2165 | cleanup_dir = false; | | |
| 2166 | | | |
| 2167 | if (cleanup_dir_parent) |d| { | | |
| 2168 | d.dir.deleteDir(dir_name) catch |err| switch (err) { | | |
| 2169 | // These two things can happen due to file system race conditions. | | |
| 2170 | error.FileNotFound, error.DirNotEmpty => continue :start_over, | | |
| 2171 | else => |e| return e, | | |
| 2172 | }; | | |
| 2173 | continue :start_over; | | |
| 2174 | } else { | 2407 | } else { |
| 2175 | self.deleteDir(sub_path) catch |err| switch (err) { | 2408 | if (self.deleteFile(sub_path)) { |
| 2176 | error.FileNotFound => return, | 2409 | return null; |
| 2177 | error.DirNotEmpty => continue :start_over, | 2410 | } else |err| switch (err) { |
| 2178 | else => |e| return e, | 2411 | error.FileNotFound => return null, |
| 2179 | }; | 2412 | |
| 2180 | return; | 2413 | error.IsDir => { |
| | 2414 | treat_as_dir = true; |
| | 2415 | continue :handle_entry; |
| | 2416 | }, |
| | 2417 | |
| | 2418 | error.AccessDenied, |
| | 2419 | error.InvalidUtf8, |
| | 2420 | error.SymLinkLoop, |
| | 2421 | error.NameTooLong, |
| | 2422 | error.SystemResources, |
| | 2423 | error.ReadOnlyFileSystem, |
| | 2424 | error.NotDir, |
| | 2425 | error.FileSystem, |
| | 2426 | error.FileBusy, |
| | 2427 | error.BadPathName, |
| | 2428 | error.Unexpected, |
| | 2429 | => |e| return e, |
| | 2430 | } |
| 2181 | } | 2431 | } |
| 2182 | } | 2432 | } |
| 2183 | } | 2433 | }; |
| 2184 | } | 2434 | } |
| 2185 | | 2435 | |
| 2186 | /// Writes content to the file system, creating a new file if it does not exist, truncating | 2436 | /// Writes content to the file system, creating a new file if it does not exist, truncating |