aboutsummaryrefslogtreecommitdiff
path: root/std/os
diff options
context:
space:
mode:
authorAndrew Kelley <superjoe30@gmail.com>2017-04-20 02:26:36 -0400
committerAndrew Kelley <superjoe30@gmail.com>2017-04-20 02:26:36 -0400
commit8654bc18104d64c7a7f9f80bdba75ed4e0c005fa (patch)
tree398f7b9d18988b61ad12db21aa72e21da3aec4d4 /std/os
parent1ff73a8e69a09b3bc993cffb755cc2ca98c7040b (diff)
downloadzig-8654bc18104d64c7a7f9f80bdba75ed4e0c005fa.tar.gz
zig-8654bc18104d64c7a7f9f80bdba75ed4e0c005fa.zip
delete test_artifacts directory when tests complete
* add std.os.deleteTree * add std.os.deleteDir * add std.os.page_size * add std.os API for iterating over directories * refactor duplication in build.zig * update documentation on how to run tests
Diffstat (limited to 'std/os')
-rw-r--r--std/os/index.zig178
-rw-r--r--std/os/linux.zig17
2 files changed, 195 insertions, 0 deletions
diff --git a/std/os/index.zig b/std/os/index.zig
index 7da623d3ce..0113706796 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -17,6 +17,8 @@ pub const line_sep = switch (@compileVar("os")) {
else => "\n",
};
+pub const page_size = 4 * 1024;
+
const debug = @import("../debug.zig");
const assert = debug.assert;
@@ -32,6 +34,7 @@ const cstr = @import("../cstr.zig");
const io = @import("../io.zig");
const base64 = @import("../base64.zig");
+const List = @import("../list.zig").List;
error Unexpected;
error SystemResources;
@@ -46,6 +49,7 @@ error SymLinkLoop;
error ReadOnlyFileSystem;
error LinkQuotaExceeded;
error RenameAcrossMountPoints;
+error DirNotEmpty;
/// Fills `buf` with random bytes. If linking against libc, this calls the
/// appropriate OS-specific library call. Otherwise it uses the zig standard
@@ -585,3 +589,177 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) -> %void {
// TODO stat the file and return an error if it's not a directory
};
}
+
+/// Returns ::error.DirNotEmpty if the directory is not empty.
+/// To delete a directory recursively, see ::deleteTree
+pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) -> %void {
+ const path_buf = %return allocator.alloc(u8, dir_path.len + 1);
+ defer allocator.free(path_buf);
+
+ mem.copy(u8, path_buf, dir_path);
+ path_buf[dir_path.len] = 0;
+
+ const err = posix.getErrno(posix.rmdir(path_buf.ptr));
+ if (err > 0) {
+ return switch (err) {
+ errno.EACCES, errno.EPERM => error.AccessDenied,
+ errno.EBUSY => error.FileBusy,
+ errno.EFAULT, errno.EINVAL => unreachable,
+ errno.ELOOP => error.SymLinkLoop,
+ errno.ENAMETOOLONG => error.NameTooLong,
+ errno.ENOENT => error.FileNotFound,
+ errno.ENOMEM => error.SystemResources,
+ errno.ENOTDIR => error.NotDir,
+ errno.EEXIST, errno.ENOTEMPTY => error.DirNotEmpty,
+ errno.EROFS => error.ReadOnlyFileSystem,
+ else => error.Unexpected,
+ };
+ }
+}
+
+/// Whether ::full_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.
+// TODO non-recursive implementation
+pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void {
+start_over:
+ // First, try deleting the item as a file. This way we don't follow sym links.
+ try (deleteFile(allocator, full_path)) {
+ return;
+ } else |err| {
+ if (err == error.FileNotFound)
+ return;
+ if (err != error.IsDir)
+ return err;
+ }
+ {
+ var dir = Dir.open(allocator, full_path) %% |err| {
+ if (err == error.FileNotFound)
+ return;
+ if (err == error.NotDir)
+ goto start_over;
+ return err;
+ };
+ defer dir.close();
+
+ var full_entry_buf = List(u8).init(allocator);
+ defer full_entry_buf.deinit();
+
+ while (true) {
+ const entry = (%return dir.next()) ?? break;
+
+ %return full_entry_buf.resize(full_path.len + entry.name.len + 1);
+ const full_entry_path = full_entry_buf.toSlice();
+ mem.copy(u8, full_entry_path, full_path);
+ full_entry_path[full_path.len] = '/';
+ mem.copy(u8, full_entry_path[full_path.len + 1...], entry.name);
+
+ %return deleteTree(allocator, full_entry_path);
+ }
+ }
+ return deleteDir(allocator, full_path);
+}
+
+pub const Dir = struct {
+ fd: i32,
+ allocator: &Allocator,
+ buf: []u8,
+ index: usize,
+ end_index: usize,
+
+ const LinuxEntry = extern struct {
+ d_ino: usize,
+ d_off: usize,
+ d_reclen: u16,
+ d_name: u8, // field address is the address of first byte of name
+ };
+
+ pub const Entry = struct {
+ name: []const u8,
+ kind: Kind,
+
+ pub const Kind = enum {
+ BlockDevice,
+ CharacterDevice,
+ Directory,
+ NamedPipe,
+ SymLink,
+ File,
+ UnixDomainSocket,
+ Unknown,
+ };
+ };
+
+ pub fn open(allocator: &Allocator, dir_path: []const u8) -> %Dir {
+ const fd = %return posixOpen(dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0, allocator);
+ return Dir {
+ .allocator = allocator,
+ .fd = fd,
+ .index = 0,
+ .end_index = 0,
+ .buf = []u8{},
+ };
+ }
+
+ pub fn close(self: &Dir) {
+ self.allocator.free(self.buf);
+ posixClose(self.fd);
+ }
+
+ /// Memory such as file names referenced in this returned entry becomes invalid
+ /// with subsequent calls to next, as well as when this ::Dir is deinitialized.
+ pub fn next(self: &Dir) -> %?Entry {
+ start_over:
+ if (self.index >= self.end_index) {
+ if (self.buf.len == 0) {
+ self.buf = %return self.allocator.alloc(u8, 2); //page_size);
+ }
+
+ while (true) {
+ const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
+ const err = linux.getErrno(result);
+ if (err > 0) {
+ switch (err) {
+ errno.EBADF, errno.EFAULT, errno.ENOTDIR => unreachable,
+ errno.EINVAL => {
+ self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
+ continue;
+ },
+ else => return error.Unexpected,
+ };
+ }
+ if (result == 0)
+ return null;
+ self.index = 0;
+ self.end_index = result;
+ break;
+ }
+ }
+ const linux_entry = @ptrcast(&LinuxEntry, &self.buf[self.index]);
+ const next_index = self.index + linux_entry.d_reclen;
+ self.index = next_index;
+
+ const name = (&linux_entry.d_name)[0...cstr.len(&linux_entry.d_name)];
+
+ // skip . and .. entries
+ if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
+ goto start_over;
+ }
+
+ const type_char = self.buf[next_index - 1];
+ const entry_kind = switch (type_char) {
+ posix.DT_BLK => Entry.Kind.BlockDevice,
+ posix.DT_CHR => Entry.Kind.CharacterDevice,
+ posix.DT_DIR => Entry.Kind.Directory,
+ posix.DT_FIFO => Entry.Kind.NamedPipe,
+ posix.DT_LNK => Entry.Kind.SymLink,
+ posix.DT_REG => Entry.Kind.File,
+ posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
+ else => Entry.Kind.Unknown,
+ };
+ return Entry {
+ .name = name,
+ .kind = entry_kind,
+ };
+ }
+};
diff --git a/std/os/linux.zig b/std/os/linux.zig
index 7c841fafad..1ebade3baf 100644
--- a/std/os/linux.zig
+++ b/std/os/linux.zig
@@ -241,6 +241,15 @@ pub const AF_NFC = PF_NFC;
pub const AF_VSOCK = PF_VSOCK;
pub const AF_MAX = PF_MAX;
+pub const DT_UNKNOWN = 0;
+pub const DT_FIFO = 1;
+pub const DT_CHR = 2;
+pub const DT_DIR = 4;
+pub const DT_BLK = 6;
+pub const DT_REG = 8;
+pub const DT_LNK = 10;
+pub const DT_SOCK = 12;
+pub const DT_WHT = 14;
fn unsigned(s: i32) -> u32 { *@ptrcast(&u32, &s) }
fn signed(s: u32) -> i32 { *@ptrcast(&i32, &s) }
@@ -273,6 +282,10 @@ pub fn getcwd(buf: &u8, size: usize) -> usize {
arch.syscall2(arch.SYS_getcwd, usize(buf), size)
}
+pub fn getdents(fd: i32, dirp: &u8, count: usize) -> usize {
+ arch.syscall3(arch.SYS_getdents, usize(fd), usize(dirp), usize(count))
+}
+
pub fn mkdir(path: &const u8, mode: usize) -> usize {
arch.syscall2(arch.SYS_mkdir, usize(path), mode)
}
@@ -291,6 +304,10 @@ pub fn read(fd: i32, buf: &u8, count: usize) -> usize {
arch.syscall3(arch.SYS_read, usize(fd), usize(buf), count)
}
+pub fn rmdir(path: &const u8) -> usize {
+ arch.syscall1(arch.SYS_rmdir, usize(path))
+}
+
pub fn symlink(existing: &const u8, new: &const u8) -> usize {
arch.syscall2(arch.SYS_symlink, usize(existing), usize(new))
}