hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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:
Mfs/fs.ha | 38+++++++++++++++++++++++++++++++++++++-
Mfs/types.ha | 8++++++++
Mos/+linux/dirfdfs.ha | 9+++++++++
Mos/fs.ha | 11+++++++++++
Mrt/+linux/syscalls.ha | 13+++++++++++++
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)?;