aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
authorRyan Liptak <squeek502@hotmail.com>2022-10-05 19:55:23 -0700
committerRyan Liptak <squeek502@hotmail.com>2022-10-05 19:58:36 -0700
commit063c5f43e9ff6add28ecbbcd79316ede23af3ae8 (patch)
treeca767319f252d3eb21868cc09f4d540b0015c418 /lib/std
parentdb0829c15ab883b9efe704414fcf8149a3d83026 (diff)
downloadzig-063c5f43e9ff6add28ecbbcd79316ede23af3ae8.tar.gz
zig-063c5f43e9ff6add28ecbbcd79316ede23af3ae8.zip
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).
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/fs.zig91
1 files changed, 83 insertions, 8 deletions
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index fb7ae54848..a17e8123b9 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -2211,18 +2211,93 @@ pub const Dir = struct {
}
}
- top.parent_dir.deleteDir(top.name) catch |err| switch (err) {
+ // On Windows, we can't delete until the dir's handle has been closed, so
+ // close it before we try to delete.
+ top.iter.dir.close();
+
+ // In order to avoid double-closing the directory when cleaning up
+ // the stack in the case of an error, we save the relevant portions and
+ // pop the value from the stack.
+ const parent_dir = top.parent_dir;
+ const name = top.name;
+ _ = stack.pop();
+
+ var need_to_retry: bool = false;
+ parent_dir.deleteDir(name) catch |err| switch (err) {
error.FileNotFound => {},
- error.DirNotEmpty => {
- // reset the iterator and try again
- top.iter.reset();
- continue :process_stack;
- },
+ error.DirNotEmpty => need_to_retry = false,
else => |e| return e,
};
- top.iter.dir.close();
- _ = stack.pop();
+ if (need_to_retry) {
+ // Since we closed the handle that the previous iterator used, we
+ // need to re-open the dir and re-create the iterator.
+ var iterable_dir = iterable_dir: {
+ var treat_as_dir = true;
+ handle_entry: while (true) {
+ if (treat_as_dir) {
+ break :iterable_dir parent_dir.openIterableDir(name, .{ .no_follow = true }) catch |err| switch (err) {
+ error.NotDir => {
+ treat_as_dir = false;
+ continue :handle_entry;
+ },
+ error.FileNotFound => {
+ // That's fine, we were trying to remove this directory anyway.
+ continue :process_stack;
+ },
+
+ error.InvalidHandle,
+ error.AccessDenied,
+ error.SymLinkLoop,
+ error.ProcessFdQuotaExceeded,
+ error.NameTooLong,
+ error.SystemFdQuotaExceeded,
+ error.NoDevice,
+ error.SystemResources,
+ error.Unexpected,
+ error.InvalidUtf8,
+ error.BadPathName,
+ error.DeviceBusy,
+ => |e| return e,
+ };
+ } else {
+ if (parent_dir.deleteFile(name)) {
+ continue :process_stack;
+ } else |err| switch (err) {
+ error.FileNotFound => continue :process_stack,
+
+ // Impossible because we do not pass any path separators.
+ error.NotDir => unreachable,
+
+ error.IsDir => {
+ treat_as_dir = true;
+ continue :handle_entry;
+ },
+
+ error.AccessDenied,
+ error.InvalidUtf8,
+ error.SymLinkLoop,
+ error.NameTooLong,
+ error.SystemResources,
+ error.ReadOnlyFileSystem,
+ error.FileSystem,
+ error.FileBusy,
+ error.BadPathName,
+ error.Unexpected,
+ => |e| return e,
+ }
+ }
+ }
+ };
+ // We know there is room on the stack since we are just re-adding
+ // the StackItem that we previously popped.
+ stack.appendAssumeCapacity(StackItem{
+ .name = name,
+ .parent_dir = parent_dir,
+ .iter = iterable_dir.iterateAssumeFirstIteration(),
+ });
+ continue :process_stack;
+ }
}
}