hare

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

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:
Mfs/fs.ha | 11+++++++++++
Mfs/types.ha | 8+++++++-
Mhare/module/scan.ha | 20++++++++++++++++++--
Aiobus/+linux | 2++
Mos/+linux/dirfdfs.ha | 13+++++++++++++
Mrt/+linux/syscalls.ha | 14+++++++++++++-
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,