From 1a6ad11462907bc4320ce943df275e4ce86cfea0 Mon Sep 17 00:00:00 2001 From: apio Date: Tue, 23 May 2023 15:42:38 +0200 Subject: [PATCH] kernel+libc+libos+ls: Add readlink() --- apps/ls.cpp | 10 ++++++---- kernel/src/sys/link.cpp | 26 ++++++++++++++++++++++++++ libc/include/unistd.h | 6 ++++++ libc/src/unistd.cpp | 12 ++++++++++++ libluna/include/luna/Syscall.h | 2 +- libos/include/os/FileSystem.h | 2 ++ libos/src/FileSystem.cpp | 18 ++++++++++++++++++ 7 files changed, 71 insertions(+), 5 deletions(-) diff --git a/apps/ls.cpp b/apps/ls.cpp index 4a100033..1b8d73d6 100644 --- a/apps/ls.cpp +++ b/apps/ls.cpp @@ -85,6 +85,8 @@ Result luna_main(int argc, char** argv) struct stat st; TRY(os::FileSystem::stat({ dirfd, file.view() }, st, false)); + auto link = TRY(os::FileSystem::readlink({ dirfd, file.view() })); + StringView owner; StringView group; @@ -92,14 +94,14 @@ Result luna_main(int argc, char** argv) if (!human_readable && !si) { - os::println("%6o %u %4s %4s %10lu %s", st.st_mode, st.st_nlink, owner.chars(), group.chars(), - st.st_size, file.chars()); + os::println("%6o %u %4s %4s %10lu %s%s%s", st.st_mode, st.st_nlink, owner.chars(), group.chars(), + st.st_size, file.chars(), link.is_empty() ? "" : " -> ", link.chars()); } else { auto size = TRY(to_dynamic_unit(st.st_size, 10, false, si ? Unit::SI : Unit::Binary, false)); - os::println("%6o %u %4s %4s %6s %s", st.st_mode, st.st_nlink, owner.chars(), group.chars(), - size.chars(), file.chars()); + os::println("%6o %u %4s %4s %6s %s%s%s", st.st_mode, st.st_nlink, owner.chars(), group.chars(), + size.chars(), file.chars(), link.is_empty() ? "" : " -> ", link.chars()); } } } diff --git a/kernel/src/sys/link.cpp b/kernel/src/sys/link.cpp index ecefdd23..6fed234d 100644 --- a/kernel/src/sys/link.cpp +++ b/kernel/src/sys/link.cpp @@ -62,3 +62,29 @@ Result sys_symlinkat(Registers*, SyscallArgs args) return 0; } + +Result sys_readlinkat(Registers*, SyscallArgs args) +{ + int dirfd = (int)args[0]; + auto path = TRY(MemoryManager::strdup_from_user(args[1])); + char* buf = (char*)args[2]; + usize bufsiz = (usize)args[3]; + + auto* current = Scheduler::current(); + + auto symlink = TRY(current->resolve_atfile(dirfd, path, true, false)); + + if (symlink->type() != VFS::InodeType::Symlink) return err(EINVAL); + + auto linkpath = TRY(symlink->readlink()); + check(!linkpath.is_empty()); + + usize nread = linkpath.length(); + if (nread > bufsiz) nread = bufsiz; + + kdbgln("readlink: reading %zu bytes from symlink (%s)", nread, linkpath.chars()); + + if (!MemoryManager::copy_to_user(buf, linkpath.chars(), nread)) return err(EFAULT); + + return nread; +} diff --git a/libc/include/unistd.h b/libc/include/unistd.h index d01aecec..fca34c81 100644 --- a/libc/include/unistd.h +++ b/libc/include/unistd.h @@ -139,6 +139,12 @@ extern "C" /* Create a symbolic link relative to a file descriptor. */ int symlinkat(const char* target, int dirfd, const char* linkpath); + /* Read the contents of a symbolic link. */ + ssize_t readlink(const char* path, char* buf, size_t max); + + /* Read the contents of a symbolic link relative to a file descriptor. */ + ssize_t readlinkat(int dirfd, const char* path, char* buf, size_t max); + #ifdef __cplusplus } #endif diff --git a/libc/src/unistd.cpp b/libc/src/unistd.cpp index 3758ef1d..5be9a16e 100644 --- a/libc/src/unistd.cpp +++ b/libc/src/unistd.cpp @@ -418,4 +418,16 @@ extern "C" long rc = syscall(SYS_symlinkat, target, dirfd, linkpath); __errno_return(rc, int); } + + ssize_t readlink(const char* path, char* buf, size_t max) + { + long rc = syscall(SYS_readlinkat, AT_FDCWD, path, buf, max); + __errno_return(rc, ssize_t); + } + + ssize_t readlinkat(int dirfd, const char* path, char* buf, size_t max) + { + long rc = syscall(SYS_readlinkat, dirfd, path, buf, max); + __errno_return(rc, ssize_t); + } } diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 3280d8a8..f450dde6 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -5,7 +5,7 @@ _e(lseek) _e(mkdir) _e(execve) _e(fork) _e(waitpid) _e(getppid) _e(fcntl) _e(getdents) _e(getuid) _e(geteuid) \ _e(getgid) _e(getegid) _e(setuid) _e(setgid) _e(seteuid) _e(setegid) _e(fchmodat) _e(fchownat) _e(ioctl) \ _e(fstatat) _e(chdir) _e(getcwd) _e(unlinkat) _e(uname) _e(sethostname) _e(dup2) _e(pipe) _e(mount) \ - _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) + _e(umount) _e(pstat) _e(getrusage) _e(symlinkat) _e(readlinkat) enum Syscalls { diff --git a/libos/include/os/FileSystem.h b/libos/include/os/FileSystem.h index 26653af8..c5553f52 100644 --- a/libos/include/os/FileSystem.h +++ b/libos/include/os/FileSystem.h @@ -21,6 +21,8 @@ namespace os Result remove_tree(const Path& path); + Result readlink(const Path& path); + Result working_directory(); Result home_directory(); diff --git a/libos/src/FileSystem.cpp b/libos/src/FileSystem.cpp index bf7b2dc2..5b6dc748 100644 --- a/libos/src/FileSystem.cpp +++ b/libos/src/FileSystem.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,23 @@ namespace os::FileSystem return remove(path); } + Result readlink(const Path& path) + { + struct stat st; + TRY(stat(path, st, false)); + if (!S_ISLNK(st.st_mode)) return String {}; + + char* buf = TRY(make_array(st.st_size + 1)); + auto guard = make_scope_guard([buf] { delete[] buf; }); + usize nread = TRY( + Result::from_syscall(syscall(SYS_readlinkat, path.dirfd(), path.name().chars(), buf, st.st_size))); + + buf[nread] = '\0'; + guard.deactivate(); + + return String { buf, nread }; + } + Result working_directory() { char* ptr = getcwd(NULL, 0);