authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2022-10-05 19:55:23-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2022-10-05 19:58:36-07:00
log063c5f43e9ff6add28ecbbcd79316ede23af3ae8
treeca767319f252d3eb21868cc09f4d540b0015c418
parentdb0829c15ab883b9efe704414fcf8149a3d83026

fs.Dir.deleteTree: Fix FileBusy errors on Windows

Windows requires the directory handle to be closed before attempting to delete the directory, so now we do that and then re-open it if we need to retry (from getting DirNotEmpty when trying to delete).

1 files changed, 83 insertions(+), 8 deletions(-)

lib/std/fs.zig+83-8
......@@ -2211,18 +2211,93 @@ pub const Dir = struct {
22112211 }
22122212 }
22132213
2214 top.parent_dir.deleteDir(top.name) catch |err| switch (err) {
2214 // On Windows, we can't delete until the dir's handle has been closed, so
2215 // close it before we try to delete.
2216 top.iter.dir.close();
2217
2218 // In order to avoid double-closing the directory when cleaning up
2219 // the stack in the case of an error, we save the relevant portions and
2220 // pop the value from the stack.
2221 const parent_dir = top.parent_dir;
2222 const name = top.name;
2223 _ = stack.pop();
2224
2225 var need_to_retry: bool = false;
2226 parent_dir.deleteDir(name) catch |err| switch (err) {
22152227 error.FileNotFound => {},
2216 error.DirNotEmpty => {
2217 // reset the iterator and try again
2218 top.iter.reset();
2219 continue :process_stack;
2220 },
2228 error.DirNotEmpty => need_to_retry = false,
22212229 else => |e| return e,
22222230 };
22232231
2224 top.iter.dir.close();
2225 _ = stack.pop();
2232 if (need_to_retry) {
2233 // Since we closed the handle that the previous iterator used, we
2234 // need to re-open the dir and re-create the iterator.
2235 var iterable_dir = iterable_dir: {
2236 var treat_as_dir = true;
2237 handle_entry: while (true) {
2238 if (treat_as_dir) {
2239 break :iterable_dir parent_dir.openIterableDir(name, .{ .no_follow = true }) catch |err| switch (err) {
2240 error.NotDir => {
2241 treat_as_dir = false;
2242 continue :handle_entry;
2243 },
2244 error.FileNotFound => {
2245 // That's fine, we were trying to remove this directory anyway.
2246 continue :process_stack;
2247 },
2248
2249 error.InvalidHandle,
2250 error.AccessDenied,
2251 error.SymLinkLoop,
2252 error.ProcessFdQuotaExceeded,
2253 error.NameTooLong,
2254 error.SystemFdQuotaExceeded,
2255 error.NoDevice,
2256 error.SystemResources,
2257 error.Unexpected,
2258 error.InvalidUtf8,
2259 error.BadPathName,
2260 error.DeviceBusy,
2261 => |e| return e,
2262 };
2263 } else {
2264 if (parent_dir.deleteFile(name)) {
2265 continue :process_stack;
2266 } else |err| switch (err) {
2267 error.FileNotFound => continue :process_stack,
2268
2269 // Impossible because we do not pass any path separators.
2270 error.NotDir => unreachable,
2271
2272 error.IsDir => {
2273 treat_as_dir = true;
2274 continue :handle_entry;
2275 },
2276
2277 error.AccessDenied,
2278 error.InvalidUtf8,
2279 error.SymLinkLoop,
2280 error.NameTooLong,
2281 error.SystemResources,
2282 error.ReadOnlyFileSystem,
2283 error.FileSystem,
2284 error.FileBusy,
2285 error.BadPathName,
2286 error.Unexpected,
2287 => |e| return e,
2288 }
2289 }
2290 }
2291 };
2292 // We know there is room on the stack since we are just re-adding
2293 // the StackItem that we previously popped.
2294 stack.appendAssumeCapacity(StackItem{
2295 .name = name,
2296 .parent_dir = parent_dir,
2297 .iter = iterable_dir.iterateAssumeFirstIteration(),
2298 });
2299 continue :process_stack;
2300 }
22262301 }
22272302 }
22282303