commit 40c1a518ac60a7afc49446086d7b07f3a613cdb2
parent 13bde14f31ef083217cc04e4ea07f66d1741379f
Author: Drew DeVault <sir@cmpwn.com>
Date: Sat, 27 Feb 2021 10:49:40 -0500
fs, os: add mkdir, mkdirs
Diffstat:
5 files changed, 78 insertions(+), 1 deletion(-)
diff --git a/fs/fs.ha b/fs/fs.ha
@@ -1,4 +1,5 @@
use io;
+use path;
// Closes a filesystem. The fs cannot be used after this function is called.
export fn close(fs: *fs) void = {
@@ -43,7 +44,9 @@ export fn stat(fs: *fs, path: path::path) (filestat | error) = {
};
};
-// Opens a new filesystem for a subdirectory.
+// Opens a new filesystem for a subdirectory. The subdirectory must be closed
+// separately from the parent filesystem, and its lifetime can outlive that of
+// its parent.
export fn subdir(fs: *fs, path: path::path) (*fs | error) = {
return match (fs.subdir) {
null => io::unsupported,
@@ -51,6 +54,39 @@ export fn subdir(fs: *fs, path: path::path) (*fs | error) = {
};
};
+// Creates a directory.
+export fn mkdir(fs: *fs, path: path::path) (void | error) = {
+ return match (fs.mkdir) {
+ null => io::unsupported,
+ f: *mkdirfunc => f(fs, path),
+ };
+};
+
+// Makes a directory, and all non-extant directories in its path.
+export fn mkdirs(fs: *fs, path: path::path) (void | error) = {
+ let parent = path::dirname(path);
+ if (!path::equal(path, parent)) {
+ match (mkdirs(fs, parent)) {
+ noaccess => void,
+ err: error => return err,
+ void => void,
+ };
+ };
+ return mkdir(fs, path);
+};
+
+// Creates a directory and returns a subdir for it. Some filesystems support
+// doing this operation atomically, but if not, a fallback is used.
+export fn mksubdir(fs: *fs, path: path::path) (*fs | error) = {
+ return match (fs.mksubdir) {
+ null => {
+ mkdir(fs, path)?;
+ subdir(fs, path);
+ },
+ f: *mksubdirfunc => f(fs, path),
+ };
+};
+
// Returns the next directory entry from an interator, or void if none remain.
// It is a programming error to call this again after it has returned void. The
// file stat returned may only have the type bits set on the file mode; callers
diff --git a/fs/types.ha b/fs/types.ha
@@ -142,6 +142,8 @@ export type createfunc = fn(fs: *fs, path: path::path, mode: io::mode) (*io::str
export type iterfunc = fn(fs: *fs, path: path::path) (*iterator | error);
export type statfunc = fn(fs: *fs, path: path::path) (filestat | error);
export type subdirfunc = fn(fs: *fs, path: path::path) (*fs | error);
+export type mkdirfunc = fn(fs: *fs, path: path::path) (void | error);
+export type mksubdirfunc = fn(fs: *fs, path: path::path) (*fs | error);
// An abstract implementation of a filesystem. To create a custom stream, embed
// this type as the first member of a struct with user-specific data and fill
@@ -169,6 +171,12 @@ export type fs = struct {
// Opens a new filesystem for a subdirectory.
subdir: nullable *subdirfunc,
+
+ // Creates a directory.
+ mkdir: nullable *mkdirfunc,
+
+ // Creates a directory and returns a subdir for it.
+ mksubdir: nullable *mksubdirfunc,
};
export type nextfunc = fn(iter: *iterator) (dirent | void);
diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha
@@ -48,6 +48,7 @@ fn static_dirfdopen(fd: int, filesystem: *os_filesystem) *fs::fs = {
iter = &fs_iter,
stat = &fs_stat,
subdir = &fs_subdir,
+ mkdir = &fs_mkdir,
...
},
dirfd = fd,
@@ -185,6 +186,14 @@ fn fs_subdir(fs: *fs::fs, path: path::path) (*fs::fs | fs::error) = {
return dirfdopen(fd);
};
+fn fs_mkdir(fs: *fs::fs, path: path::path) (void | fs::error) = {
+ let fs = fs: *os_filesystem;
+ return match (rt::mkdirat(fs.dirfd, path, 0o755)) {
+ err: rt::errno => return errno_to_io(err),
+ void => void,
+ };
+};
+
fn fs_close(fs: *fs::fs) void = {
let fs = fs: *os_filesystem;
rt::close(fs.dirfd);
diff --git a/os/fs.ha b/os/fs.ha
@@ -21,3 +21,14 @@ export fn stat(path: path::path) (fs::filestat | fs::error) = fs::stat(cwd, path
// Opens a directory as a filesystem.
export fn diropen(path: path::path) (*fs::fs | fs::error) = fs::subdir(cwd, path);
+
+// Creates a directory.
+export fn mkdir(path: path::path) (void | fs::error) = fs::mkdir(cwd, path);
+
+// Creates a directory, and all non-extant directories in its path.
+export fn mkdirs(path: path::path) (void | fs::error) = fs::mkdirs(cwd, path);
+
+// Creates a directory and returns a subdir for it. Some filesystems support
+// doing this operation atomically, but if not, a fallback is used.
+export fn mksubdir(path: path::path) (*fs::fs | fs::error) =
+ fs::mksubdir(cwd, path);
diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha
@@ -85,6 +85,19 @@ export fn chroot(path: path) (void | errno) = {
return;
};
+export fn mkdir(path: path, mode: uint) (void | errno) = {
+ let path = kpath(path)?;
+ wrap_return(syscall2(SYS_mkdir, path: uintptr: u64, mode: u64))?;
+ return;
+};
+
+export fn mkdirat(dirfd: int, path: path, mode: uint) (void | errno) = {
+ let path = kpath(path)?;
+ wrap_return(syscall3(SYS_mkdirat,
+ dirfd: u64, path: uintptr: u64, mode: u64))?;
+ return;
+};
+
export fn execveat(dirfd: int, path: path, argv: *[*]nullable *const char,
envp: *[*]nullable *const char, flags: int) errno = {
let path = kpath(path)?;