From 82e7b0e860b80d60ff6829ba583f172a2ba1b1e5 Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 15 Apr 2023 20:26:15 +0200 Subject: [PATCH] kernel: Introduce *at() syscall framework, add openat() and fstatat() --- kernel/src/fs/VFS.cpp | 2 ++ kernel/src/sys/open.cpp | 23 ++++++++++++---------- kernel/src/sys/stat.cpp | 36 +++++++++++----------------------- kernel/src/thread/Thread.cpp | 21 ++++++++++++++++++++ kernel/src/thread/Thread.h | 2 ++ libc/include/bits/atfile.h | 10 ++++++++++ libc/include/fcntl.h | 1 + libc/src/fcntl.cpp | 2 +- libc/src/sys/stat.cpp | 5 +++-- libluna/include/luna/Syscall.h | 4 ++-- libos/src/File.cpp | 2 +- 11 files changed, 67 insertions(+), 41 deletions(-) create mode 100644 libc/include/bits/atfile.h diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index 05837a3b..90f24971 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -15,6 +15,8 @@ namespace VFS Result> resolve_path(const char* path, Credentials auth, SharedPtr working_directory) { + if (*path == '\0') return err(ENOENT); + auto parser = TRY(PathParser::create(path)); SharedPtr current_inode; diff --git a/kernel/src/sys/open.cpp b/kernel/src/sys/open.cpp index b21f19d6..b464daab 100644 --- a/kernel/src/sys/open.cpp +++ b/kernel/src/sys/open.cpp @@ -9,11 +9,12 @@ // These flags are needed after open(), the rest only affect open(). constexpr int FLAGS_TO_KEEP = O_RDWR | O_APPEND | O_NONBLOCK | O_CLOEXEC; -Result sys_open(Registers*, SyscallArgs args) +Result sys_openat(Registers*, SyscallArgs args) { - auto path = TRY(MemoryManager::strdup_from_user(args[0])); - int flags = (int)args[1]; - mode_t mode = (mode_t)args[2]; + int dirfd = (int)args[0]; + auto path = TRY(MemoryManager::strdup_from_user(args[1])); + int flags = (int)args[2]; + mode_t mode = (mode_t)args[3]; Thread* current = Scheduler::current(); @@ -22,14 +23,16 @@ Result sys_open(Registers*, SyscallArgs args) // Caller did not pass either O_RDONLY, O_WRONLY or O_RDWR if ((flags & O_RDWR) == 0) { return err(EINVAL); } + if ((flags & O_DIRECTORY) & (flags & O_CREAT)) return err(EINVAL); + int error; - bool ok = - VFS::resolve_path(path.chars(), current->auth, current->current_directory).try_set_value_or_error(inode, error); + SharedPtr parent_inode; + bool ok = current->resolve_atfile(dirfd, path, false, &parent_inode).try_set_value_or_error(inode, error); if (!ok) { - if (error == ENOENT && (flags & O_CREAT)) + if (error == ENOENT && (flags & O_CREAT) && !path.is_empty()) { - inode = TRY(VFS::create_file(path.chars(), current->auth, current->current_directory)); + inode = TRY(VFS::create_file(path.chars(), current->auth, parent_inode)); inode->chmod(mode); inode->chown(current->auth.euid, current->auth.egid); } @@ -50,9 +53,9 @@ Result sys_open(Registers*, SyscallArgs args) int fd = TRY(current->allocate_fd(0)); - current->fd_table[fd] = FileDescriptor { inode, 0, flags & FLAGS_TO_KEEP }; + kinfoln("openat: opening file %s from dirfd %d, flags %d, mode %#o = fd %d", path.chars(), dirfd, flags, mode, fd); - kinfoln("open: opening file %s, flags %d, mode %#o = fd %d", path.chars(), flags, mode, fd); + current->fd_table[fd] = FileDescriptor { inode, 0, flags & FLAGS_TO_KEEP }; return (u64)fd; } diff --git a/kernel/src/sys/stat.cpp b/kernel/src/sys/stat.cpp index 751043e3..c5a525e8 100644 --- a/kernel/src/sys/stat.cpp +++ b/kernel/src/sys/stat.cpp @@ -1,6 +1,7 @@ #include "memory/MemoryManager.h" #include "sys/Syscall.h" #include "thread/Scheduler.h" +#include #include #include @@ -19,8 +20,17 @@ static mode_t make_mode(mode_t mode, VFS::InodeType type) return result; } -static Result do_stat(SharedPtr inode, stat* st) +Result sys_fstatat(Registers*, SyscallArgs args) { + int dirfd = (int)args[0]; + auto path = TRY(MemoryManager::strdup_from_user(args[1])); + stat* st = (stat*)args[2]; + int flags = (int)args[3]; + + Thread* current = Scheduler::current(); + + auto inode = TRY(current->resolve_atfile(dirfd, path, flags & AT_EMPTY_PATH)); + stat kstat; kstat.st_ino = inode->inode_number(); @@ -34,27 +44,3 @@ static Result do_stat(SharedPtr inode, stat* st) return 0; } - -Result sys_stat(Registers*, SyscallArgs args) -{ - auto path = TRY(MemoryManager::strdup_from_user(args[0])); - stat* st = (stat*)args[1]; - - Thread* current = Scheduler::current(); - - auto inode = TRY(VFS::resolve_path(path.chars(), current->auth, current->current_directory)); - - return do_stat(inode, st); -} - -Result sys_fstat(Registers*, SyscallArgs args) -{ - int fd = (int)args[0]; - stat* st = (stat*)args[1]; - - Thread* current = Scheduler::current(); - - auto descriptor = *TRY(current->resolve_fd(fd)); - - return do_stat(descriptor.inode, st); -} diff --git a/kernel/src/thread/Thread.cpp b/kernel/src/thread/Thread.cpp index 93e08a2b..6b6ed296 100644 --- a/kernel/src/thread/Thread.cpp +++ b/kernel/src/thread/Thread.cpp @@ -1,7 +1,10 @@ #include "thread/Thread.h" +#include "memory/MemoryManager.h" +#include #include #include #include +#include static Atomic g_next_id; @@ -45,6 +48,24 @@ Result Thread::resolve_fd(int fd) return maybe_descriptor.value_ptr(); } +Result> Thread::resolve_atfile(int dirfd, const String& path, bool allow_empty_path, + SharedPtr* parent_inode) +{ + if (parent_inode) *parent_inode = this->current_directory; + + if (PathParser::is_absolute(path.view())) return VFS::resolve_path(path.chars(), this->auth); + + if (dirfd == AT_FDCWD) return VFS::resolve_path(path.chars(), this->auth, this->current_directory); + + auto descriptor = TRY(resolve_fd(dirfd)); + + if (parent_inode) *parent_inode = descriptor->inode; + + if (path.is_empty() && allow_empty_path) return descriptor->inode; + + return VFS::resolve_path(path.chars(), this->auth, descriptor->inode); +} + bool FileDescriptor::should_append() { return flags & O_APPEND; diff --git a/kernel/src/thread/Thread.h b/kernel/src/thread/Thread.h index 685736ae..8ddbff2e 100644 --- a/kernel/src/thread/Thread.h +++ b/kernel/src/thread/Thread.h @@ -73,6 +73,8 @@ struct Thread : public LinkedListNode Result allocate_fd(int min); Result resolve_fd(int fd); + Result> resolve_atfile(int dirfd, const String& path, bool allow_empty_path, + SharedPtr* parent_inode = nullptr); FPData fp_data; diff --git a/libc/include/bits/atfile.h b/libc/include/bits/atfile.h new file mode 100644 index 00000000..0393b0e1 --- /dev/null +++ b/libc/include/bits/atfile.h @@ -0,0 +1,10 @@ +/* bits/atfile.h: Definitions for *at() functions. */ + +#ifndef _BITS_ATFILE_H +#define _BITS_ATFILE_H + +#define AT_FDCWD -100 + +#define AT_EMPTY_PATH 1 + +#endif diff --git a/libc/include/fcntl.h b/libc/include/fcntl.h index dd9bc746..6a90deaa 100644 --- a/libc/include/fcntl.h +++ b/libc/include/fcntl.h @@ -3,6 +3,7 @@ #ifndef _FCNTL_H #define _FCNTL_H +#include #include #include #include diff --git a/libc/src/fcntl.cpp b/libc/src/fcntl.cpp index 87de09c4..02314909 100644 --- a/libc/src/fcntl.cpp +++ b/libc/src/fcntl.cpp @@ -13,7 +13,7 @@ extern "C" mode_t mode = (mode_t)va_arg(ap, int); - long rc = syscall(SYS_open, path, flags, mode); + long rc = syscall(SYS_openat, AT_FDCWD, path, flags, mode); va_end(ap); __errno_return(rc, int); diff --git a/libc/src/sys/stat.cpp b/libc/src/sys/stat.cpp index 7eb709bf..89cb3a45 100644 --- a/libc/src/sys/stat.cpp +++ b/libc/src/sys/stat.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -19,13 +20,13 @@ extern "C" int stat(const char* path, struct stat* st) { - long rc = syscall(SYS_stat, path, st); + long rc = syscall(SYS_fstatat, AT_FDCWD, path, st, 0); __errno_return(rc, int); } int fstat(int fd, struct stat* st) { - long rc = syscall(SYS_fstat, fd, st); + long rc = syscall(SYS_fstatat, fd, "", st, AT_EMPTY_PATH); __errno_return(rc, int); } } diff --git a/libluna/include/luna/Syscall.h b/libluna/include/luna/Syscall.h index 48305f72..9c42c71d 100644 --- a/libluna/include/luna/Syscall.h +++ b/libluna/include/luna/Syscall.h @@ -1,10 +1,10 @@ #pragma once #define enumerate_syscalls(_e) \ - _e(exit) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(open) _e(close) _e(read) _e(getpid) _e(write) \ + _e(exit) _e(clock_gettime) _e(mmap) _e(munmap) _e(usleep) _e(openat) _e(close) _e(read) _e(getpid) _e(write) \ _e(lseek) _e(mkdir) _e(execve) _e(mknod) _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(chmod) _e(chown) \ - _e(ioctl) _e(stat) _e(fstat) _e(chdir) _e(getcwd) _e(unlink) + _e(ioctl) _e(fstatat) _e(chdir) _e(getcwd) _e(unlink) enum Syscalls { diff --git a/libos/src/File.cpp b/libos/src/File.cpp index 6fa92fe6..c00dbf47 100644 --- a/libos/src/File.cpp +++ b/libos/src/File.cpp @@ -59,7 +59,7 @@ namespace os { auto file = TRY(adopt_shared_if_nonnull(new (std::nothrow) File({}))); - long rc = syscall(SYS_open, path.chars(), flags, mode); + long rc = syscall(SYS_openat, AT_FDCWD, path.chars(), flags, mode); int fd = TRY(Result::from_syscall(rc)); file->m_fd = fd;