commit 9d3d27ab0c951716d17ec7fcf1e2ec7d5c888de9
parent c83ea0ad8f75018700ce6942abd6e29988dfab5c
Author: Drew DeVault <sir@cmpwn.com>
Date: Tue, 19 Oct 2021 13:58:49 +0200
hare::module: resolve symlinks
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
6 files changed, 64 insertions(+), 4 deletions(-)
diff --git a/fs/fs.ha b/fs/fs.ha
@@ -140,6 +140,17 @@ export fn stat(fs: *fs, path: str) (filestat | error) = {
};
};
+// Returns the path referred to by a symbolic link. The caller must free the
+// return value.
+export fn readlink(fs: *fs, path: str) (str | error) = {
+ match (fs.readlink) {
+ case null =>
+ return errors::unsupported;
+ case f: *readlinkfunc =>
+ return f(fs, path);
+ };
+};
+
// 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.
diff --git a/fs/types.ha b/fs/types.ha
@@ -177,6 +177,7 @@ export type mksubdirfunc = fn(fs: *fs, path: str) (*fs | error);
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 openfunc = fn(
fs: *fs,
@@ -241,9 +242,14 @@ export type fs = struct {
iter: nullable *iterfunc,
// Obtains information about a file or directory. If the target is a
- // symlink, information is returned about the link, not its target.
+ // symbolic link, information is returned about the link, not its
+ // target.
stat: nullable *statfunc,
+ // Returns the path referred to by a symbolic link. The caller will free
+ // the return value.
+ readlink: nullable *readlinkfunc,
+
// Opens a new filesystem for a subdirectory.
subdir: nullable *subdirfunc,
diff --git a/hare/module/scan.ha b/hare/module/scan.ha
@@ -13,7 +13,6 @@ use slice;
use sort;
use strings;
use strio;
-use fmt;
def ABI_VERSION: u8 = 0;
@@ -143,7 +142,24 @@ fn scan_directory(
switch (ent.ftype) {
case fs::mode::LINK =>
- abort(); // TODO
+ let linkpath = path::join(path, ent.name);
+ defer free(linkpath);
+ let linkpath = fs::readlink(ctx.fs, linkpath)?;
+ defer free(linkpath);
+ if (!path::abs(linkpath)) {
+ let newpath = path::join(path, linkpath);
+ free(linkpath);
+ linkpath = newpath;
+ };
+
+ const st = fs::stat(ctx.fs, linkpath)?;
+ if (fs::isfile(st.mode)) {
+ append(files, strings::dup(ent.name));
+ } else if (fs::isdir(st.mode)) {
+ append(dirs, strings::dup(ent.name));
+ } else if (fs::islink(st.mode)) {
+ abort(); // TODO: Resolve recursively
+ };
case fs::mode::DIR =>
append(dirs, strings::dup(ent.name));
case fs::mode::REG =>
diff --git a/iobus/+linux b/iobus/+linux
@@ -0,0 +1 @@
+io_uring
+\ No newline at end of file
diff --git a/os/+linux/dirfdfs.ha b/os/+linux/dirfdfs.ha
@@ -72,6 +72,7 @@ fn static_dirfdopen(fd: io::file, filesystem: *os_filesystem) *fs::fs = {
rename = &fs_rename,
iter = &fs_iter,
stat = &fs_stat,
+ readlink = &fs_readlink,
subdir = &fs_subdir,
mkdir = &fs_mkdir,
rmdir = &fs_rmdir,
@@ -301,6 +302,18 @@ fn fs_stat(fs: *fs::fs, path: str) (fs::filestat | fs::error) = {
};
};
+fn fs_readlink(fs: *fs::fs, path: str) (str | fs::error) = {
+ let fs = fs: *os_filesystem;
+ static let buf: [rt::PATH_MAX]u8 = [0...];
+ let z = match (rt::readlinkat(fs.dirfd, path, buf[..])) {
+ case err: rt::errno =>
+ return errno_to_fs(err);
+ case z: size =>
+ yield z;
+ };
+ return strings::dup(strings::fromutf8(buf[..z]));
+};
+
fn fs_subdir(fs: *fs::fs, path: str) (*fs::fs | fs::error) = {
let fs = fs: *os_filesystem;
let oh = rt::open_how {
diff --git a/rt/+linux/syscalls.ha b/rt/+linux/syscalls.ha
@@ -6,7 +6,7 @@ fn syscall4(_: u64, _: u64, _: u64, _: u64, _: u64) u64;
fn syscall5(_: u64, _: u64, _: u64, _: u64, _: u64, _: u64) u64;
fn syscall6(_: u64, _: u64, _: u64, _: u64, _: u64, _: u64, _: u64) u64;
-def PATH_MAX: size = 4096z;
+export def PATH_MAX: size = 4096z;
export type path = (str | []u8 | *const char);
let pathbuf: [PATH_MAX + 1]u8 = [0...];
@@ -72,6 +72,18 @@ export fn openat2(
return openat(dirfd, path, how.flags: int, how.mode: uint);
};
+export fn readlinkat(
+ dirfd: int,
+ path: path,
+ buf: []u8,
+) (size | errno) = {
+ let path = kpath(path)?;
+ return wrap_return(syscall4(SYS_readlinkat,
+ dirfd: u64, path: uintptr: u64,
+ buf: *[*]u8: uintptr: u64,
+ len(buf): u64))?: size;
+};
+
export fn unlink(path: path) (void | errno) = {
let path = kpath(path)?;
wrap_return(syscall3(SYS_unlinkat,