hare

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

commit d3ac290ee408868d18b6eea429911d62305c8c83
parent 0f4a70255ad96c9959addcc8345cb80d20a02d99
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri,  4 Mar 2022 11:57:02 +0100

fs: add link, symlink

Plus the corresponding implementations for Linux

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mfs/fs.ha | 20++++++++++++++++++++
Mfs/types.ha | 8++++++++
Mos/+linux/dirfdfs.ha | 20++++++++++++++++++++
Mrt/+linux/syscalls.ha | 27+++++++++++++++++++++++++++
4 files changed, 75 insertions(+), 0 deletions(-)

diff --git a/fs/fs.ha b/fs/fs.ha @@ -278,6 +278,26 @@ export fn resolve(fs: *fs, path: str) str = { return path::string(&buf); }; +// Creates a new (hard) link at 'new' for the file at 'old'. +export fn link(fs: *fs, old: str, new: str) (void | error) = { + match (fs.link) { + case null => + return errors::unsupported; + case let f: *linkfunc => + return f(fs, old, new); + }; +}; + +// Creates a new symbolic link at 'path' which points to 'target'. +export fn symlink(fs: *fs, target: str, path: str) (void | error) = { + match (fs.symlink) { + case null => + return errors::unsupported; + case let f: *symlinkfunc => + return f(fs, target, 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 @@ -200,6 +200,8 @@ export type chmodfunc = fn(fs: *fs, path: str, mode: mode) (void | error); export type chownfunc = fn(fs: *fs, path: str, uid: uint, gid: uint) (void | error); export type resolvefunc = fn(fs: *fs, path: str) str; export type readlinkfunc = fn(fs: *fs, path: str) (str | error); +export type linkfunc = fn(fs: *fs, old: str, new: str) (void | error); +export type symlinkfunc = fn(fs: *fs, target: str, path: str) (void | error); export type openfunc = fn( fs: *fs, @@ -294,6 +296,12 @@ export type fs = struct { // implementation does not provide this, [resolve] presumes that // relative paths are rooted (i.e. "foo" == "/foo"). resolve: nullable *resolvefunc, + + // Creates a new (hard) link. + link: nullable *linkfunc, + + // Creates a new symbolic link. + symlink: nullable *symlinkfunc, }; export type nextfunc = fn(iter: *iterator) (dirent | void); diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha @@ -84,6 +84,8 @@ fn static_dirfdopen(fd: io::file, filesystem: *os_filesystem) *fs::fs = { chmod = &fs_chmod, chown = &fs_chown, resolve = &fs_resolve, + link = &fs_link, + symlink = &fs_symlink, ... }, dirfd = fd, @@ -377,6 +379,24 @@ fn fs_resolve(fs: *fs::fs, path: str) str = { return path::string(&buf); }; +fn fs_link(fs: *fs::fs, old: str, new: str) (void | fs::error) = { + let fs = fs: *os_filesystem; + match (rt::linkat(fs.dirfd, old, fs.dirfd, new, 0)) { + case let err: rt::errno => + return errno_to_fs(err); + case void => void; + }; +}; + +fn fs_symlink(fs: *fs::fs, target: str, path: str) (void | fs::error) = { + let fs = fs: *os_filesystem; + match (rt::symlinkat(target, fs.dirfd, path)) { + case let err: rt::errno => + return errno_to_fs(err); + case void => void; + }; +}; + fn fs_close(fs: *fs::fs) void = { let fs = fs: *os_filesystem; rt::close(fs.dirfd)!; diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha @@ -103,6 +103,33 @@ export fn unlinkat(dirfd: int, path: path, flags: int) (void | errno) = { dirfd: u64, path: uintptr: u64, flags: u64))?; }; +export fn linkat( + olddirfd: int, + oldpath: str, + newdirfd: int, + newpath: str, + flags: int, +) (void | errno) = { + let oldpath = kpath(oldpath)?; + static let newpathbuf: [PATH_MAX + 1]u8 = [0...]; + let newpath = copy_kpath(newpath, newpathbuf)?; + wrap_return(syscall5(SYS_linkat, + olddirfd: u64, oldpath: uintptr: u64, + newdirfd: u64, newpath: uintptr: u64, flags: u64))?; +}; + +export fn symlinkat( + target: str, + newdirfd: int, + linkpath: str, +) (void | errno) = { + let target = kpath(target)?; + static let linkpathbuf: [PATH_MAX + 1]u8 = [0...]; + let linkpath = copy_kpath(linkpath, linkpathbuf)?; + wrap_return(syscall3(SYS_symlinkat, target: uintptr: u64, + newdirfd: u64, linkpath: uintptr: u64))?; +}; + export fn mknodat( dirfd: int, path: path,