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:
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,