diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index 033fcd1541..92cfc37f1e 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -101,6 +101,9 @@ namespace Kernel BAN::ErrorOr sys_create(const char*, mode_t); BAN::ErrorOr sys_create_dir(const char*, mode_t); BAN::ErrorOr sys_unlink(const char*); + BAN::ErrorOr readlink_impl(BAN::StringView absolute_path, char* buffer, size_t bufsize); + BAN::ErrorOr sys_readlink(const char* path, char* buffer, size_t bufsize); + BAN::ErrorOr sys_readlinkat(int fd, const char* path, char* buffer, size_t bufsize); BAN::ErrorOr sys_chmod(const char*, mode_t); diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index e8bee629fc..c709741f78 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -782,6 +782,47 @@ namespace Kernel return 0; } + BAN::ErrorOr Process::readlink_impl(BAN::StringView absolute_path, char* buffer, size_t bufsize) + { + auto inode = TRY(VirtualFileSystem::get().file_from_absolute_path(m_credentials, absolute_path, O_NOFOLLOW | O_RDONLY)).inode; + + // FIXME: no allocation needed + auto link_target = TRY(inode->link_target()); + + size_t byte_count = BAN::Math::min(link_target.size(), bufsize); + memcpy(buffer, link_target.data(), byte_count); + + return byte_count; + } + + BAN::ErrorOr Process::sys_readlink(const char* path, char* buffer, size_t bufsize) + { + LockGuard _(m_lock); + validate_string_access(path); + validate_pointer_access(buffer, bufsize); + + auto absolute_path = TRY(absolute_path_of(path)); + + return readlink_impl(absolute_path.sv(), buffer, bufsize); + } + + BAN::ErrorOr Process::sys_readlinkat(int fd, const char* path, char* buffer, size_t bufsize) + { + LockGuard _(m_lock); + validate_string_access(path); + validate_pointer_access(buffer, bufsize); + + // FIXME: handle O_SEARCH in fd + auto parent_path = TRY(m_open_file_descriptors.path_of(fd)); + + BAN::String absolute_path; + TRY(absolute_path.append(parent_path)); + TRY(absolute_path.push_back('/')); + TRY(absolute_path.append(path)); + + return readlink_impl(absolute_path.sv(), buffer, bufsize); + } + BAN::ErrorOr Process::sys_chmod(const char* path, mode_t mode) { if (mode & S_IFMASK) diff --git a/kernel/kernel/Syscall.cpp b/kernel/kernel/Syscall.cpp index ab7c3ce854..b3c574cdb5 100644 --- a/kernel/kernel/Syscall.cpp +++ b/kernel/kernel/Syscall.cpp @@ -211,6 +211,12 @@ namespace Kernel case SYS_UNLINK: ret = Process::current().sys_unlink((const char*)arg1); break; + case SYS_READLINK: + ret = Process::current().sys_readlink((const char*)arg1, (char*)arg2, (size_t)arg3); + break; + case SYS_READLINKAT: + ret = Process::current().sys_readlinkat((int)arg1, (const char*)arg2, (char*)arg3, (size_t)arg4); + break; default: dwarnln("Unknown syscall {}", syscall); break; diff --git a/libc/include/sys/syscall.h b/libc/include/sys/syscall.h index b6eacdf8e9..b07079a4a4 100644 --- a/libc/include/sys/syscall.h +++ b/libc/include/sys/syscall.h @@ -59,6 +59,8 @@ __BEGIN_DECLS #define SYS_CREATE 56 // creat, mkfifo #define SYS_CREATE_DIR 57 // mkdir #define SYS_UNLINK 58 +#define SYS_READLINK 59 +#define SYS_READLINKAT 60 __END_DECLS diff --git a/libc/unistd.cpp b/libc/unistd.cpp index b118da9514..0e35d6259a 100644 --- a/libc/unistd.cpp +++ b/libc/unistd.cpp @@ -78,6 +78,16 @@ ssize_t write(int fildes, const void* buf, size_t nbyte) return syscall(SYS_WRITE, fildes, buf, nbyte); } +ssize_t readlink(const char* __restrict path, char* __restrict buf, size_t bufsize) +{ + return syscall(SYS_READLINK, path, buf, bufsize); +} + +ssize_t readlinkat(int fd, const char* __restrict path, char* __restrict buf, size_t bufsize) +{ + return syscall(SYS_READLINKAT, fd, path, buf, bufsize); +} + int dup(int fildes) { return syscall(SYS_DUP, fildes);