deleteTree
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.
Function parameters
Parameters
Type definitions in this namespace
Types
- SymLinkFlags
- Use with `Dir.symLink`, `Dir.atomicSymLink`, and `symLinkAbsolute` to
Functions in this namespace
Functions
- iterateAssumeFirstIteration
- Like `iterate`, but will not reset the directory cursor before the first
- walk
- Recursively iterates over a directory.
- openFile
- Opens a file for reading or writing, without attempting to create a new file.
- openFileZ
- Same as `openFile` but the path parameter is null-terminated.
- openFileW
- Same as `openFile` but Windows-only and the path parameter is
- createFile
- Creates, opens, or overwrites a file with write access.
- createFileZ
- Same as `createFile` but the path parameter is null-terminated.
- createFileW
- Same as `createFile` but Windows-only and the path parameter is
- makeDir
- Creates a single directory with a relative or absolute path.
- makeDirZ
- Same as `makeDir`, but `sub_path` is null-terminated.
- makeDirW
- Creates a single directory with a relative or absolute null-terminated WTF-16 LE-encoded path.
- makePath
- Calls makeDir iteratively to make an entire path
- makePathStatus
- Same as `makePath` except returns whether the path already existed or was successfully created.
- makeOpenPath
- This function performs `makePath`, followed by `openDir`.
- realpath
- This function returns the canonicalized absolute pathname of
- realpathZ
- Same as `Dir.realpath` except `pathname` is null-terminated.
- realpathW
- Windows-only.
- realpathAlloc
- Same as `Dir.realpath` except caller must free the returned memory.
- setAsCwd
- Changes the current working directory to the open directory handle.
- openDir
- Opens a directory at the given path.
- openDirZ
- Same as `openDir` except the parameter is null-terminated.
- openDirW
- Same as `openDir` except the path parameter is WTF-16 LE encoded, NT-prefixed.
- deleteFile
- Delete a file name and possibly the file it refers to, based on an open directory handle.
- deleteFileZ
- Same as `deleteFile` except the parameter is null-terminated.
- deleteFileW
- Same as `deleteFile` except the parameter is WTF-16 LE encoded.
- deleteDir
- Returns `error.DirNotEmpty` if the directory is not empty.
- deleteDirZ
- Same as `deleteDir` except the parameter is null-terminated.
- deleteDirW
- Same as `deleteDir` except the parameter is WTF16LE, NT prefixed.
- rename
- Change the name or location of a file or directory.
- renameZ
- Same as `rename` except the parameters are null-terminated.
- renameW
- Same as `rename` except the parameters are WTF16LE, NT prefixed.
- symLink
- Creates a symbolic link named `sym_link_path` which contains the string `target_path`.
- symLinkWasi
- WASI-only.
- symLinkZ
- Same as `symLink`, except the pathname parameters are null-terminated.
- symLinkW
- Windows-only.
- atomicSymLink
- Same as `symLink`, except tries to create the symbolic link until it
- readLink
- Read value of a symbolic link.
- readLinkWasi
- WASI-only.
- readLinkZ
- Same as `readLink`, except the `sub_path_c` parameter is null-terminated.
- readLinkW
- Windows-only.
- readFile
- Read all of file contents using a preallocated buffer.
- readFileAlloc
- On success, caller owns returned buffer.
- readFileAllocOptions
- On success, caller owns returned buffer.
- deleteTree
- Whether `sub_path` describes a symlink, file, or directory, this function
- deleteTreeMinStackSize
- Like `deleteTree`, but only keeps one `Iterator` active at a time to minimize the function's stack size.
- writeFile
- Writes content to the file system, using the file creation flags provided.
- access
- Test accessing `sub_path`.
- accessZ
- Same as `access` except the path parameter is null-terminated.
- accessW
- Same as `access` except asserts the target OS is Windows and the path parameter is
- updateFile
- Check the file size, mtime, and mode of `source_path` and `dest_path`.
- copyFile
- Atomically creates a new file at `dest_path` within `dest_dir` with the
- atomicFile
- Directly access the `.file` field, and then call `AtomicFile.finish` to
- statFile
- Returns metadata for a file inside the directory.
- chmod
- Changes the mode of the directory.
- chown
- Changes the owner and group of the directory.
- setPermissions
- Sets permissions according to the provided `Permissions` struct.
Error sets in this namespace
Error Sets
= posix.fd_t
Values
- Handle
- = posix.fd_t
- default_mode
- = 0o755
Source
Implementation
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.ProcessNotFound,
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.ProcessNotFound,
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;
}
}
}