| ... | ... | @@ -2076,7 +2076,6 @@ pub const Dir = struct { |
| 2076 | 2076 | /// this function recursively removes its entries and then tries again. |
| 2077 | 2077 | /// This operation is not atomic on most file systems. |
| 2078 | 2078 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { |
| 2079 | | // First, try deleting the item as a file. This way we don't follow sym links. |
| 2080 | 2079 | if (self.deleteFile(sub_path)) { |
| 2081 | 2080 | return; |
| 2082 | 2081 | } else |err| switch (err) { |
| ... | ... | @@ -2096,31 +2095,196 @@ pub const Dir = struct { |
| 2096 | 2095 | => |e| return e, |
| 2097 | 2096 | } |
| 2098 | 2097 | |
| 2099 | | start_over: while (true) { |
| 2100 | | var iterable_dir = self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) { |
| 2101 | | error.NotDir => { |
| 2102 | | // Somehow the sub_path got changed into a file while we were trying to delete the tree. |
| 2103 | | // This implies that the dir at the sub_path was deleted at some point so we consider this |
| 2104 | | // as a successful delete and return. |
| 2105 | | return; |
| 2106 | | }, |
| 2107 | | error.FileNotFound => { |
| 2108 | | // That's fine, we were trying to remove this directory anyway. |
| 2109 | | return; |
| 2098 | const StackItem = struct { |
| 2099 | name: []const u8, |
| 2100 | parent_dir: Dir, |
| 2101 | iter: IterableDir.Iterator, |
| 2102 | }; |
| 2103 | |
| 2104 | var stack = std.BoundedArray(StackItem, 16){}; |
| 2105 | defer { |
| 2106 | for (stack.slice()) |*item| { |
| 2107 | item.iter.dir.close(); |
| 2108 | } |
| 2109 | } |
| 2110 | |
| 2111 | var initial_iterable_dir = self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) { |
| 2112 | error.NotDir => { |
| 2113 | // Somehow the sub_path got changed into a file while we were trying to delete the tree. |
| 2114 | // This implies that the dir at the sub_path was deleted at some point so we consider this |
| 2115 | // as a successful delete and return. |
| 2116 | return; |
| 2117 | }, |
| 2118 | error.FileNotFound => { |
| 2119 | // That's fine, we were trying to remove this directory anyway. |
| 2120 | return; |
| 2121 | }, |
| 2122 | error.InvalidHandle, |
| 2123 | error.AccessDenied, |
| 2124 | error.SymLinkLoop, |
| 2125 | error.ProcessFdQuotaExceeded, |
| 2126 | error.NameTooLong, |
| 2127 | error.SystemFdQuotaExceeded, |
| 2128 | error.NoDevice, |
| 2129 | error.SystemResources, |
| 2130 | error.Unexpected, |
| 2131 | error.InvalidUtf8, |
| 2132 | error.BadPathName, |
| 2133 | error.DeviceBusy, |
| 2134 | => |e| return e, |
| 2135 | }; |
| 2136 | |
| 2137 | stack.appendAssumeCapacity(StackItem{ |
| 2138 | .name = sub_path, |
| 2139 | .parent_dir = self, |
| 2140 | .iter = initial_iterable_dir.iterateAssumeFirstIteration(), |
| 2141 | }); |
| 2142 | |
| 2143 | process_stack: while (stack.len != 0) { |
| 2144 | var top = &(stack.slice()[stack.len - 1]); |
| 2145 | while (try top.iter.next()) |entry| { |
| 2146 | var treat_as_dir = entry.kind == .Directory; |
| 2147 | handle_entry: while (true) { |
| 2148 | if (treat_as_dir) { |
| 2149 | if (stack.ensureUnusedCapacity(1)) { |
| 2150 | var iterable_dir = top.iter.dir.openIterableDir(entry.name, .{ .no_follow = true }) catch |err| switch (err) { |
| 2151 | error.NotDir => { |
| 2152 | treat_as_dir = false; |
| 2153 | continue :handle_entry; |
| 2154 | }, |
| 2155 | error.FileNotFound => { |
| 2156 | // That's fine, we were trying to remove this directory anyway. |
| 2157 | break :handle_entry; |
| 2158 | }, |
| 2159 | |
| 2160 | error.InvalidHandle, |
| 2161 | error.AccessDenied, |
| 2162 | error.SymLinkLoop, |
| 2163 | error.ProcessFdQuotaExceeded, |
| 2164 | error.NameTooLong, |
| 2165 | error.SystemFdQuotaExceeded, |
| 2166 | error.NoDevice, |
| 2167 | error.SystemResources, |
| 2168 | error.Unexpected, |
| 2169 | error.InvalidUtf8, |
| 2170 | error.BadPathName, |
| 2171 | error.DeviceBusy, |
| 2172 | => |e| return e, |
| 2173 | }; |
| 2174 | stack.appendAssumeCapacity(StackItem{ |
| 2175 | .name = entry.name, |
| 2176 | .parent_dir = top.iter.dir, |
| 2177 | .iter = iterable_dir.iterateAssumeFirstIteration(), |
| 2178 | }); |
| 2179 | continue :process_stack; |
| 2180 | } else |_| { |
| 2181 | try top.iter.dir.deleteTreeFallback(entry.name, entry.kind); |
| 2182 | break :handle_entry; |
| 2183 | } |
| 2184 | } else { |
| 2185 | if (top.iter.dir.deleteFile(entry.name)) { |
| 2186 | break :handle_entry; |
| 2187 | } else |err| switch (err) { |
| 2188 | error.FileNotFound => break :handle_entry, |
| 2189 | |
| 2190 | error.NotDir => unreachable, |
| 2191 | |
| 2192 | error.IsDir => { |
| 2193 | treat_as_dir = true; |
| 2194 | continue :handle_entry; |
| 2195 | }, |
| 2196 | |
| 2197 | error.AccessDenied, |
| 2198 | error.InvalidUtf8, |
| 2199 | error.SymLinkLoop, |
| 2200 | error.NameTooLong, |
| 2201 | error.SystemResources, |
| 2202 | error.ReadOnlyFileSystem, |
| 2203 | error.FileSystem, |
| 2204 | error.FileBusy, |
| 2205 | error.BadPathName, |
| 2206 | error.Unexpected, |
| 2207 | => |e| return e, |
| 2208 | } |
| 2209 | } |
| 2210 | } |
| 2211 | } |
| 2212 | |
| 2213 | top.parent_dir.deleteDir(top.name) catch |err| switch (err) { |
| 2214 | error.FileNotFound => {}, |
| 2215 | error.DirNotEmpty => { |
| 2216 | // reset the iterator and try again |
| 2217 | top.iter.reset(); |
| 2218 | continue :process_stack; |
| 2110 | 2219 | }, |
| 2111 | | error.InvalidHandle, |
| 2112 | | error.AccessDenied, |
| 2113 | | error.SymLinkLoop, |
| 2114 | | error.ProcessFdQuotaExceeded, |
| 2115 | | error.NameTooLong, |
| 2116 | | error.SystemFdQuotaExceeded, |
| 2117 | | error.NoDevice, |
| 2118 | | error.SystemResources, |
| 2119 | | error.Unexpected, |
| 2120 | | error.InvalidUtf8, |
| 2121 | | error.BadPathName, |
| 2122 | | error.DeviceBusy, |
| 2123 | | => |e| return e, |
| 2220 | else => |e| return e, |
| 2221 | }; |
| 2222 | |
| 2223 | top.iter.dir.close(); |
| 2224 | _ = stack.pop(); |
| 2225 | } |
| 2226 | } |
| 2227 | |
| 2228 | /// Fallback version of deleteTree that is less efficient but works on arbitrarily |
| 2229 | /// nested directories without needing recursion or allocation. |
| 2230 | fn deleteTreeFallback(self: Dir, sub_path: []const u8, kind_hint: File.Kind) DeleteTreeError!void { |
| 2231 | start_over: while (true) { |
| 2232 | var iterable_dir = iterable_dir: { |
| 2233 | var treat_as_dir = kind_hint == .Directory; |
| 2234 | |
| 2235 | handle_entry: while (true) { |
| 2236 | if (treat_as_dir) { |
| 2237 | break :iterable_dir self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) { |
| 2238 | error.NotDir => { |
| 2239 | treat_as_dir = false; |
| 2240 | continue :handle_entry; |
| 2241 | }, |
| 2242 | error.FileNotFound => { |
| 2243 | // That's fine, we were trying to remove this directory anyway. |
| 2244 | return; |
| 2245 | }, |
| 2246 | |
| 2247 | error.InvalidHandle, |
| 2248 | error.AccessDenied, |
| 2249 | error.SymLinkLoop, |
| 2250 | error.ProcessFdQuotaExceeded, |
| 2251 | error.NameTooLong, |
| 2252 | error.SystemFdQuotaExceeded, |
| 2253 | error.NoDevice, |
| 2254 | error.SystemResources, |
| 2255 | error.Unexpected, |
| 2256 | error.InvalidUtf8, |
| 2257 | error.BadPathName, |
| 2258 | error.DeviceBusy, |
| 2259 | => |e| return e, |
| 2260 | }; |
| 2261 | } else { |
| 2262 | if (self.deleteFile(sub_path)) { |
| 2263 | return; |
| 2264 | } else |err| switch (err) { |
| 2265 | error.FileNotFound => return, |
| 2266 | |
| 2267 | error.NotDir => unreachable, |
| 2268 | |
| 2269 | error.IsDir => { |
| 2270 | treat_as_dir = true; |
| 2271 | continue :handle_entry; |
| 2272 | }, |
| 2273 | |
| 2274 | error.AccessDenied, |
| 2275 | error.InvalidUtf8, |
| 2276 | error.SymLinkLoop, |
| 2277 | error.NameTooLong, |
| 2278 | error.SystemResources, |
| 2279 | error.ReadOnlyFileSystem, |
| 2280 | error.FileSystem, |
| 2281 | error.FileBusy, |
| 2282 | error.BadPathName, |
| 2283 | error.Unexpected, |
| 2284 | => |e| return e, |
| 2285 | } |
| 2286 | } |
| 2287 | } |
| 2124 | 2288 | }; |
| 2125 | 2289 | var cleanup_dir_parent: ?IterableDir = null; |
| 2126 | 2290 | defer if (cleanup_dir_parent) |*d| d.close(); |