| ... | @@ -2076,7 +2076,6 @@ pub const Dir = struct { | ... | @@ -2076,7 +2076,6 @@ pub const Dir = struct { |
| 2076 | /// this function recursively removes its entries and then tries again. | 2076 | /// this function recursively removes its entries and then tries again. |
| 2077 | /// This operation is not atomic on most file systems. | 2077 | /// This operation is not atomic on most file systems. |
| 2078 | pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { | 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 | if (self.deleteFile(sub_path)) { | 2079 | if (self.deleteFile(sub_path)) { |
| 2081 | return; | 2080 | return; |
| 2082 | } else |err| switch (err) { | 2081 | } else |err| switch (err) { |
| ... | @@ -2096,31 +2095,196 @@ pub const Dir = struct { | ... | @@ -2096,31 +2095,196 @@ pub const Dir = struct { |
| 2096 | => |e| return e, | 2095 | => |e| return e, |
| 2097 | } | 2096 | } |
| 2098 | | 2097 | |
| 2099 | start_over: while (true) { | 2098 | const StackItem = struct { |
| 2100 | var iterable_dir = self.openIterableDir(sub_path, .{ .no_follow = true }) catch |err| switch (err) { | 2099 | name: []const u8, |
| 2101 | error.NotDir => { | 2100 | parent_dir: Dir, |
| 2102 | // Somehow the sub_path got changed into a file while we were trying to delete the tree. | 2101 | iter: IterableDir.Iterator, |
| 2103 | // This implies that the dir at the sub_path was deleted at some point so we consider this | 2102 | }; |
| 2104 | // as a successful delete and return. | 2103 | |
| 2105 | return; | 2104 | var stack = std.BoundedArray(StackItem, 16){}; |
| 2106 | }, | 2105 | defer { |
| 2107 | error.FileNotFound => { | 2106 | for (stack.slice()) |*item| { |
| 2108 | // That's fine, we were trying to remove this directory anyway. | 2107 | item.iter.dir.close(); |
| 2109 | return; | 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, | 2220 | else => |e| return e, |
| 2112 | error.AccessDenied, | 2221 | }; |
| 2113 | error.SymLinkLoop, | 2222 | |
| 2114 | error.ProcessFdQuotaExceeded, | 2223 | top.iter.dir.close(); |
| 2115 | error.NameTooLong, | 2224 | _ = stack.pop(); |
| 2116 | error.SystemFdQuotaExceeded, | 2225 | } |
| 2117 | error.NoDevice, | 2226 | } |
| 2118 | error.SystemResources, | 2227 | |
| 2119 | error.Unexpected, | 2228 | /// Fallback version of deleteTree that is less efficient but works on arbitrarily |
| 2120 | error.InvalidUtf8, | 2229 | /// nested directories without needing recursion or allocation. |
| 2121 | error.BadPathName, | 2230 | fn deleteTreeFallback(self: Dir, sub_path: []const u8, kind_hint: File.Kind) DeleteTreeError!void { |
| 2122 | error.DeviceBusy, | 2231 | start_over: while (true) { |
| 2123 | => |e| return e, | 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 | var cleanup_dir_parent: ?IterableDir = null; | 2289 | var cleanup_dir_parent: ?IterableDir = null; |
| 2126 | defer if (cleanup_dir_parent) |*d| d.close(); | 2290 | defer if (cleanup_dir_parent) |*d| d.close(); |