Function deleteTree [src]

Whether sub_path describes a symlink, file, or directory, this function removes it. If it cannot be removed because it is a non-empty directory, this function recursively removes its entries and then tries again. This operation is not atomic on most file systems. On Windows, sub_path should be encoded as WTF-8. On WASI, sub_path should be encoded as valid UTF-8. On other platforms, sub_path is an opaque sequence of bytes with no particular encoding.

Prototype

pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void

Parameters

self: Dirsub_path: []const u8

Possible Errors

AccessDenied
BadPathName

On Windows, file paths cannot contain these characters: '/', '*', '?', '"', '<', '>', '|'

DeviceBusy
FileBusy
FileSystem
FileTooBig
InvalidUtf8

WASI-only; file paths must be valid UTF-8.

InvalidWtf8

Windows-only; file paths provided by the user must be valid WTF-8. https://simonsapin.github.io/wtf-8/

NameTooLong
NetworkNotFound

On Windows, \\server or \\server\share was not found.

NoDevice
NotDir

One of the path components was not a directory. This error is unreachable if sub_path does not contain a path separator.

PermissionDenied
ProcessFdQuotaExceeded
ReadOnlyFileSystem
SymLinkLoop
SystemFdQuotaExceeded
SystemResources
Unexpected UnexpectedError

The Operating System returned an undocumented error code.

This error is in theory not possible, but it would be better to handle this error than to invoke undefined behavior.

When this error code is observed, it usually means the Zig Standard Library needs a small patch to add the error code to the error set for the respective function.

Source

pub fn deleteTree(self: Dir, sub_path: []const u8) DeleteTreeError!void { var initial_iterable_dir = (try self.deleteTreeOpenInitialSubpath(sub_path, .file)) orelse return; const StackItem = struct { name: []const u8, parent_dir: Dir, iter: Dir.Iterator, fn closeAll(items: []@This()) void { for (items) |*item| item.iter.dir.close(); } }; var stack_buffer: [16]StackItem = undefined; var stack = std.ArrayListUnmanaged(StackItem).initBuffer(&stack_buffer); defer StackItem.closeAll(stack.items); stack.appendAssumeCapacity(.{ .name = sub_path, .parent_dir = self, .iter = initial_iterable_dir.iterateAssumeFirstIteration(), }); process_stack: while (stack.items.len != 0) { var top = &stack.items[stack.items.len - 1]; while (try top.iter.next()) |entry| { var treat_as_dir = entry.kind == .directory; handle_entry: while (true) { if (treat_as_dir) { if (stack.unusedCapacitySlice().len >= 1) { var iterable_dir = top.iter.dir.openDir(entry.name, .{ .no_follow = true, .iterate = 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. break :handle_entry; }, error.AccessDenied, error.PermissionDenied, error.SymLinkLoop, error.ProcessFdQuotaExceeded, error.NameTooLong, error.SystemFdQuotaExceeded, error.NoDevice, error.SystemResources, error.Unexpected, error.InvalidUtf8, error.InvalidWtf8, error.BadPathName, error.NetworkNotFound, error.DeviceBusy, => |e| return e, }; stack.appendAssumeCapacity(.{ .name = entry.name, .parent_dir = top.iter.dir, .iter = iterable_dir.iterateAssumeFirstIteration(), }); continue :process_stack; } else { try top.iter.dir.deleteTreeMinStackSizeWithKindHint(entry.name, entry.kind); break :handle_entry; } } else { if (top.iter.dir.deleteFile(entry.name)) { break :handle_entry; } else |err| switch (err) { error.FileNotFound => break :handle_entry, // Impossible because we do not pass any path separators. error.NotDir => unreachable, error.IsDir => { treat_as_dir = true; continue :handle_entry; }, error.AccessDenied, error.PermissionDenied, error.InvalidUtf8, error.InvalidWtf8, error.SymLinkLoop, error.NameTooLong, error.SystemResources, error.ReadOnlyFileSystem, error.FileSystem, error.FileBusy, error.BadPathName, error.NetworkNotFound, error.Unexpected, => |e| return e, } } } } // 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.items.len -= 1; var need_to_retry: bool = false; parent_dir.deleteDir(name) catch |err| switch (err) { error.FileNotFound => {}, error.DirNotEmpty => need_to_retry = true, else => |e| return e, }; 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.openDir(name, .{ .no_follow = true, .iterate = 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.AccessDenied, error.PermissionDenied, error.SymLinkLoop, error.ProcessFdQuotaExceeded, error.NameTooLong, error.SystemFdQuotaExceeded, error.NoDevice, error.SystemResources, error.Unexpected, error.InvalidUtf8, error.InvalidWtf8, error.BadPathName, error.NetworkNotFound, 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.PermissionDenied, error.InvalidUtf8, error.InvalidWtf8, error.SymLinkLoop, error.NameTooLong, error.SystemResources, error.ReadOnlyFileSystem, error.FileSystem, error.FileBusy, error.BadPathName, error.NetworkNotFound, 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(.{ .name = name, .parent_dir = parent_dir, .iter = iterable_dir.iterateAssumeFirstIteration(), }); continue :process_stack; } } }