From d58ca5f37a97e6a2c8237578831c40ca563a0e29 Mon Sep 17 00:00:00 2001 From: Bananymous Date: Mon, 2 Dec 2024 17:44:25 +0200 Subject: [PATCH] Kernel/LibC: Implement symlink{,at} --- kernel/include/kernel/FS/Ext2/Inode.h | 1 + kernel/include/kernel/FS/Inode.h | 2 + kernel/include/kernel/Process.h | 3 + kernel/kernel/FS/Ext2/Inode.cpp | 31 +++++++- kernel/kernel/FS/Inode.cpp | 8 ++ kernel/kernel/Process.cpp | 74 ++++++++++++------- .../libraries/LibC/include/sys/syscall.h | 1 + userspace/libraries/LibC/unistd.cpp | 8 +- 8 files changed, 97 insertions(+), 31 deletions(-) diff --git a/kernel/include/kernel/FS/Ext2/Inode.h b/kernel/include/kernel/FS/Ext2/Inode.h index 46baa9cf..b8824ca2 100644 --- a/kernel/include/kernel/FS/Ext2/Inode.h +++ b/kernel/include/kernel/FS/Ext2/Inode.h @@ -39,6 +39,7 @@ namespace Kernel virtual BAN::ErrorOr unlink_impl(BAN::StringView) override; virtual BAN::ErrorOr link_target_impl() override; + virtual BAN::ErrorOr set_link_target_impl(BAN::StringView) override; virtual BAN::ErrorOr read_impl(off_t, BAN::ByteSpan) override; virtual BAN::ErrorOr write_impl(off_t, BAN::ConstByteSpan) override; diff --git a/kernel/include/kernel/FS/Inode.h b/kernel/include/kernel/FS/Inode.h index 4b714169..540e56cf 100644 --- a/kernel/include/kernel/FS/Inode.h +++ b/kernel/include/kernel/FS/Inode.h @@ -99,6 +99,7 @@ namespace Kernel // Link API BAN::ErrorOr link_target(); + BAN::ErrorOr set_link_target(BAN::StringView); // Socket API BAN::ErrorOr accept(sockaddr* address, socklen_t* address_len, int flags); @@ -134,6 +135,7 @@ namespace Kernel // Link API virtual BAN::ErrorOr link_target_impl() { return BAN::Error::from_errno(ENOTSUP); } + virtual BAN::ErrorOr set_link_target_impl(BAN::StringView) { return BAN::Error::from_errno(ENOTSUP); } // Socket API virtual BAN::ErrorOr accept_impl(sockaddr*, socklen_t*, int) { return BAN::Error::from_errno(ENOTSUP); } diff --git a/kernel/include/kernel/Process.h b/kernel/include/kernel/Process.h index e7c0b721..46d253dd 100644 --- a/kernel/include/kernel/Process.h +++ b/kernel/include/kernel/Process.h @@ -113,6 +113,8 @@ namespace Kernel BAN::ErrorOr sys_unlink(const char*); BAN::ErrorOr sys_readlinkat(int fd, const char* path, char* buffer, size_t bufsize); + BAN::ErrorOr sys_symlinkat(const char* path1, int fd, const char* path2); + BAN::ErrorOr sys_pread(int fd, void* buffer, size_t count, off_t offset); BAN::ErrorOr sys_fchmodat(int fd, const char* path, mode_t mode, int flag); @@ -221,6 +223,7 @@ namespace Kernel static Process* create_process(const Credentials&, pid_t parent, pid_t sid = 0, pid_t pgrp = 0); BAN::ErrorOr find_file(int fd, const char* path, int flags); + BAN::ErrorOr find_parent(int fd, const char* path); BAN::ErrorOr validate_string_access(const char*); BAN::ErrorOr validate_pointer_access_check(const void*, size_t, bool needs_write); diff --git a/kernel/kernel/FS/Ext2/Inode.cpp b/kernel/kernel/FS/Ext2/Inode.cpp index c33d3224..fe77f9b2 100644 --- a/kernel/kernel/FS/Ext2/Inode.cpp +++ b/kernel/kernel/FS/Ext2/Inode.cpp @@ -104,7 +104,27 @@ namespace Kernel { ASSERT(mode().iflnk()); if (m_inode.size < sizeof(m_inode.block)) - return BAN::String((const char*)m_inode.block); + { + BAN::String result; + TRY(result.append(BAN::StringView(reinterpret_cast(m_inode.block), m_inode.size))); + return result; + } + dwarnln("TODO: ext2 get symlink target from {} byte inode", m_inode.size); + return BAN::Error::from_errno(ENOTSUP); + } + + BAN::ErrorOr Ext2Inode::set_link_target_impl(BAN::StringView target) + { + ASSERT(mode().iflnk()); + if (m_inode.size < sizeof(m_inode.block) && target.size() < sizeof(m_inode.block)) + { + memset(m_inode.block, 0, sizeof(m_inode.block)); + memcpy(m_inode.block, target.data(), target.size()); + m_inode.size = target.size(); + TRY(sync()); + return {}; + } + dwarnln("TODO: ext2 set symlink target to {} bytes from {} byte inode", target.size(), m_inode.size); return BAN::Error::from_errno(ENOTSUP); } @@ -440,8 +460,15 @@ done: if (!find_inode_impl(name).is_error()) return BAN::Error::from_errno(EEXIST); - if (!(Mode(mode).ifreg())) + switch (mode & Inode::Mode::TYPE_MASK) + { + case Inode::Mode::IFLNK: + case Inode::Mode::IFREG: + case Inode::Mode::IFIFO: + case Inode::Mode::IFSOCK: + break; return BAN::Error::from_errno(ENOTSUP); + } const uint32_t new_ino = TRY(m_fs.create_inode(initialize_new_inode_info(mode, uid, gid))); diff --git a/kernel/kernel/FS/Inode.cpp b/kernel/kernel/FS/Inode.cpp index 69d81afc..9feac253 100644 --- a/kernel/kernel/FS/Inode.cpp +++ b/kernel/kernel/FS/Inode.cpp @@ -111,6 +111,14 @@ namespace Kernel return link_target_impl(); } + BAN::ErrorOr Inode::set_link_target(BAN::StringView target) + { + LockGuard _(m_mutex); + if (!mode().iflnk()) + return BAN::Error::from_errno(EINVAL); + return set_link_target_impl(target); + } + BAN::ErrorOr Inode::accept(sockaddr* address, socklen_t* address_len, int flags) { LockGuard _(m_mutex); diff --git a/kernel/kernel/Process.cpp b/kernel/kernel/Process.cpp index bc69f32c..af8d823a 100644 --- a/kernel/kernel/Process.cpp +++ b/kernel/kernel/Process.cpp @@ -368,6 +368,26 @@ namespace Kernel return read_from_vec_of_str(m_environ, offset, buffer); } + BAN::ErrorOr Process::find_parent(int fd, const char* path) + { + ASSERT(m_process_lock.is_locked()); + + if (path) + TRY(validate_string_access(path)); + + if (path && path[0] == '/') + return VirtualFileSystem::get().root_file(); + + if (fd == AT_FDCWD) + return TRY(m_working_directory.clone()); + + int flags = TRY(m_open_file_descriptors.flags_of(fd)); + if (!(flags & O_RDONLY) && !(flags & O_SEARCH)) + return BAN::Error::from_errno(EBADF); + + return TRY(m_open_file_descriptors.file_of(fd)); + } + BAN::ErrorOr Process::find_file(int fd, const char* path, int flags) { ASSERT(m_process_lock.is_locked()); @@ -375,14 +395,7 @@ namespace Kernel if (path) TRY(validate_string_access(path)); - VirtualFileSystem::File parent_file; - if (path && path[0] == '/') - parent_file = VirtualFileSystem::get().root_file(); - else if (fd == AT_FDCWD) - parent_file = TRY(m_working_directory.clone()); - else - parent_file = TRY(m_open_file_descriptors.file_of(fd)); - + auto parent_file = TRY(find_parent(fd, path)); auto file = path ? TRY(VirtualFileSystem::get().file_from_relative_path(parent_file, m_credentials, path, flags)) : BAN::move(parent_file); @@ -854,10 +867,12 @@ namespace Kernel { switch (mode & Inode::Mode::TYPE_MASK) { - case Inode::Mode::IFREG: break; - case Inode::Mode::IFDIR: break; - case Inode::Mode::IFIFO: break; - case Inode::Mode::IFSOCK: break; + case Inode::Mode::IFREG: + case Inode::Mode::IFDIR: + case Inode::Mode::IFLNK: + case Inode::Mode::IFIFO: + case Inode::Mode::IFSOCK: + break; default: return BAN::Error::from_errno(ENOTSUP); } @@ -925,21 +940,7 @@ namespace Kernel TRY(validate_string_access(path)); - VirtualFileSystem::File parent_file; - if (path[0] == '/') - parent_file = VirtualFileSystem::get().root_file(); - else if (fd == AT_FDCWD) - parent_file = TRY(m_working_directory.clone()); - else - { - int flags = TRY(m_open_file_descriptors.flags_of(fd)); - if (!(flags & O_RDONLY) && !(flags & O_SEARCH)) - return BAN::Error::from_errno(EBADF); - if (!TRY(m_open_file_descriptors.inode_of(fd))->mode().ifdir()) - return BAN::Error::from_errno(ENOTDIR); - parent_file = TRY(m_open_file_descriptors.file_of(fd)); - } - + auto parent_file = TRY(find_parent(fd, path)); auto file_or_error = VirtualFileSystem::get().file_from_relative_path(parent_file, m_credentials, path, flags | O_NOFOLLOW); VirtualFileSystem::File file; @@ -1077,6 +1078,25 @@ namespace Kernel return byte_count; } + BAN::ErrorOr Process::sys_symlinkat(const char* path1, int fd, const char* path2) + { + LockGuard _(m_process_lock); + TRY(validate_string_access(path1)); + TRY(validate_string_access(path2)); + + auto parent_file = TRY(find_parent(fd, path2)); + auto file_or_error = VirtualFileSystem::get().file_from_relative_path(parent_file, m_credentials, path2, O_NOFOLLOW); + if (!file_or_error.is_error()) + return BAN::Error::from_errno(EEXIST); + + TRY(create_file_or_dir(parent_file, path2, 0777 | Inode::Mode::IFLNK)); + + auto symlink = TRY(VirtualFileSystem::get().file_from_relative_path(parent_file, m_credentials, path2, O_NOFOLLOW)); + TRY(symlink.inode->set_link_target(path1)); + + return 0; + } + BAN::ErrorOr Process::sys_pread(int fd, void* buffer, size_t count, off_t offset) { LockGuard _(m_process_lock); diff --git a/userspace/libraries/LibC/include/sys/syscall.h b/userspace/libraries/LibC/include/sys/syscall.h index 78a43565..7806f401 100644 --- a/userspace/libraries/LibC/include/sys/syscall.h +++ b/userspace/libraries/LibC/include/sys/syscall.h @@ -89,6 +89,7 @@ __BEGIN_DECLS O(SYS_POSIX_OPENPT, posix_openpt) \ O(SYS_PTSNAME, ptsname) \ O(SYS_FSYNC, fsync) \ + O(SYS_SYMLINKAT, symlinkat) \ enum Syscall { diff --git a/userspace/libraries/LibC/unistd.cpp b/userspace/libraries/LibC/unistd.cpp index 36615286..2eb57bb6 100644 --- a/userspace/libraries/LibC/unistd.cpp +++ b/userspace/libraries/LibC/unistd.cpp @@ -623,6 +623,10 @@ unsigned alarm(unsigned seconds) int symlink(const char* path1, const char* path2) { - dwarnln("FIXME: symlink({}, {})", path1, path2); - ASSERT_NOT_REACHED(); + return symlinkat(path1, AT_FDCWD, path2); +} + +int symlinkat(const char* path1, int fd, const char* path2) +{ + return syscall(SYS_SYMLINKAT, path1, fd, path2); }