From 78d72c2f0c240f6ef073140b27c2b0022ef713e8 Mon Sep 17 00:00:00 2001 From: apio Date: Sun, 23 Oct 2022 14:03:46 +0200 Subject: [PATCH] Kernel, libc: Add a getdents() system call This is meant to be a low-level interface to implement dirent.h on top of. --- kernel/include/fs/VFS.h | 4 +++ kernel/include/sys/Syscall.h | 3 ++ kernel/src/fs/FileDescriptor.cpp | 2 +- kernel/src/fs/VFS.cpp | 7 ++++ kernel/src/init/InitRD.cpp | 15 ++++++++ kernel/src/sys/Syscall.cpp | 3 ++ kernel/src/sys/dirent.cpp | 61 ++++++++++++++++++++++++++++++++ libs/libc/include/luna/dirent.h | 26 ++++++++++++++ libs/libc/include/sys/syscall.h | 1 + libs/libc/src/luna/dirent.cpp | 11 ++++++ libs/libc/src/syscall.cpp | 1 + 11 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 kernel/src/sys/dirent.cpp create mode 100644 libs/libc/include/luna/dirent.h create mode 100644 libs/libc/src/luna/dirent.cpp diff --git a/kernel/include/fs/VFS.h b/kernel/include/fs/VFS.h index b305efaa..cb1b6b1e 100644 --- a/kernel/include/fs/VFS.h +++ b/kernel/include/fs/VFS.h @@ -19,6 +19,7 @@ namespace VFS typedef Node* (*node_finddir)(Node*, const char*); typedef int (*node_mkdir)(Node*, const char*); typedef int (*node_block)(Node*); + typedef Node* (*node_readdir)(Node*, long); struct Node { @@ -29,6 +30,7 @@ namespace VFS int flags; node_read read_func; node_finddir find_func; + node_readdir readdir_func; node_mkdir mkdir_func; node_write write_func; node_block block_func; @@ -55,4 +57,6 @@ namespace VFS void unmount(Node* mountpoint); Node* root(); + + Node* readdir(Node* dir, long offset); } \ No newline at end of file diff --git a/kernel/include/sys/Syscall.h b/kernel/include/sys/Syscall.h index 6cbdce6e..c09ab9b8 100644 --- a/kernel/include/sys/Syscall.h +++ b/kernel/include/sys/Syscall.h @@ -24,9 +24,11 @@ #define SYS_access 19 #define SYS_fstat 20 #define SYS_pstat 21 +#define SYS_getdents 22 struct stat; struct pstat; +struct luna_dirent; namespace Syscall { @@ -55,3 +57,4 @@ void sys_waitpid(Context* context, long pid, int* wstatus, int options); void sys_access(Context* context, const char* path, int amode); void sys_fstat(Context* context, int fd, struct stat* buf); void sys_pstat(Context* context, long pid, struct pstat* buf); +void sys_getdents(Context* context, int fd, struct luna_dirent* buf, size_t count); \ No newline at end of file diff --git a/kernel/src/fs/FileDescriptor.cpp b/kernel/src/fs/FileDescriptor.cpp index 825552e7..59ce5da1 100644 --- a/kernel/src/fs/FileDescriptor.cpp +++ b/kernel/src/fs/FileDescriptor.cpp @@ -39,7 +39,7 @@ ssize_t Descriptor::write(size_t size, const char* buffer) int Descriptor::seek(long offset) { - if (m_node->type != VFS_DEVICE && (uint64_t)offset > m_node->length) + if (m_node->type == VFS_FILE && (uint64_t)offset > m_node->length) return -EINVAL; // FIXME: Support seeking beyond the current file's length. m_offset = (uint64_t)offset; return 0; diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index 29ca712b..8efb5e48 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -201,4 +201,11 @@ void VFS::unmount(Node* mountpoint) if (!mountpoint) return; if (!(mountpoint->flags & VFS_MOUNTPOINT)) return; mountpoint->flags &= ~VFS_MOUNTPOINT; +} + +VFS::Node* VFS::readdir(VFS::Node* dir, long offset) +{ + if (!dir) return 0; + if (!dir->readdir_func) return 0; + return dir->readdir_func(dir, offset); } \ No newline at end of file diff --git a/kernel/src/init/InitRD.cpp b/kernel/src/init/InitRD.cpp index 02add581..0588653a 100644 --- a/kernel/src/init/InitRD.cpp +++ b/kernel/src/init/InitRD.cpp @@ -186,6 +186,15 @@ VFS::Node* initrd_scan_dir(VFS::Node* node, const char* filename) return 0; } +VFS::Node* initrd_read_dir(VFS::Node* node, long offset) +{ + if (!node) return 0; + if (node->inode >= total_dirs) return 0; + InitRD::Directory dir = dirs[node->inode]; + if (offset >= dir.entries) return 0; + return dir.files[offset]; +} + int initrd_mkdir(VFS::Node* node, const char* name) // FIXME: Return proper error numbers. { if (total_dirs >= 32) @@ -222,6 +231,7 @@ int initrd_mkdir(VFS::Node* node, const char* name) // FIXME: Return proper erro dir.entries = 0; dirs[total_dirs++] = dir; // FIXME: Right now this isn't of worry, but there is a possibility for a TOCTOU bug here. // Should use a spinlock or something. + node->length++; parent.files[parent.entries++] = &new_node; return 0; } @@ -270,10 +280,13 @@ static bool initrd_register_dir(InitRD::Directory& dir, uint64_t inode) node.length = 0; node.type = VFS_DIRECTORY; node.mkdir_func = initrd_mkdir; + node.readdir_func = initrd_read_dir; + node.length = 0; strncpy(node.name, buffer, sizeof(node.name)); strncpy(dir.name, buffer, sizeof(dir.name)); parent.files[parent.entries++] = &node; + current_node->length++; kfree(buffer); return true; @@ -332,6 +345,7 @@ static bool initrd_register_file(InitRD::File& f, uint64_t inode) strncpy(f.name, buffer, sizeof(f.name)); parent.files[parent.entries++] = &node; + current_node->length++; kfree(buffer); return true; } @@ -375,6 +389,7 @@ static void initrd_initialize_root() strncpy(root.name, "initrd", sizeof(root.name)); initrd_root.find_func = initrd_scan_dir; initrd_root.mkdir_func = initrd_mkdir; + initrd_root.readdir_func = initrd_read_dir; } void InitRD::init() diff --git a/kernel/src/sys/Syscall.cpp b/kernel/src/sys/Syscall.cpp index 01d3df12..d4271bde 100644 --- a/kernel/src/sys/Syscall.cpp +++ b/kernel/src/sys/Syscall.cpp @@ -33,6 +33,9 @@ void Syscall::entry(Context* context) case SYS_access: sys_access(context, (const char*)context->rdi, (int)context->rsi); break; case SYS_fstat: sys_fstat(context, (int)context->rdi, (struct stat*)context->rsi); break; case SYS_pstat: sys_pstat(context, (long)context->rdi, (struct pstat*)context->rsi); break; + case SYS_getdents: + sys_getdents(context, (int)context->rdi, (struct luna_dirent*)context->rsi, (size_t)context->rdx); + break; default: context->rax = -ENOSYS; break; } VMM::exit_syscall_context(); diff --git a/kernel/src/sys/dirent.cpp b/kernel/src/sys/dirent.cpp new file mode 100644 index 00000000..89eb16c0 --- /dev/null +++ b/kernel/src/sys/dirent.cpp @@ -0,0 +1,61 @@ +#define MODULE "dir" + +#include "luna/dirent.h" +#include "fs/VFS.h" +#include "interrupts/Context.h" +#include "std/errno.h" +#include "std/string.h" +#include "sys/UserMemory.h" +#include "thread/Scheduler.h" + +void sys_getdents(Context* context, int fd, struct luna_dirent* buf, size_t count) +{ + if (fd < 0 || fd > TASK_MAX_FDS) + { + context->rax = -EBADF; + return; + } + Task* current_task = Scheduler::current_task(); + if (!current_task->files[fd].is_open()) + { + context->rax = -EBADF; + return; + } + Descriptor& dir = current_task->files[fd]; + VFS::Node* node = dir.node(); + if (node->type != VFS_DIRECTORY) + { + context->rax = -ENOTDIR; + return; + } + size_t nread = 0; + while (count) + { + VFS::Node* entry = VFS::readdir(node, dir.offset()); + if (!entry) + { + context->rax = nread; + return; + } + + auto* kdirent = obtain_user_ref(buf); + if (!kdirent) + { + context->rax = -EFAULT; + return; + } + + kdirent->total = node->length; + kdirent->offset = dir.offset(); + kdirent->inode = entry->inode; + strlcpy(kdirent->name, entry->name, sizeof(kdirent->name)); + + release_user_ref(kdirent); + + dir.seek(dir.offset() + 1); + buf++; + nread++; + count--; + } + context->rax = nread; +} \ No newline at end of file diff --git a/libs/libc/include/luna/dirent.h b/libs/libc/include/luna/dirent.h new file mode 100644 index 00000000..c9c2555c --- /dev/null +++ b/libs/libc/include/luna/dirent.h @@ -0,0 +1,26 @@ +#ifndef _LUNA_DIRENT_H +#define _LUNA_DIRENT_H + +#include + +struct luna_dirent +{ + ino_t inode; + char name[64]; + size_t total; + off_t offset; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + /* Retrieve directory entries from the kernel. This is the raw interface, use readdir() instead. */ + ssize_t getdents(int fd, struct luna_dirent* buf, size_t count); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/libs/libc/include/sys/syscall.h b/libs/libc/include/sys/syscall.h index 0966b6c1..4aee2f03 100644 --- a/libs/libc/include/sys/syscall.h +++ b/libs/libc/include/sys/syscall.h @@ -23,5 +23,6 @@ #define SYS_access 19 #define SYS_fstat 20 #define SYS_pstat 21 +#define SYS_getdents 22 #endif \ No newline at end of file diff --git a/libs/libc/src/luna/dirent.cpp b/libs/libc/src/luna/dirent.cpp new file mode 100644 index 00000000..cd1e490f --- /dev/null +++ b/libs/libc/src/luna/dirent.cpp @@ -0,0 +1,11 @@ +#include +#include +#include + +extern "C" +{ + ssize_t getdents(int fd, struct luna_dirent* buf, size_t count) + { + return syscall(SYS_getdents, fd, buf, count); + } +} \ No newline at end of file diff --git a/libs/libc/src/syscall.cpp b/libs/libc/src/syscall.cpp index ad26bd66..3fae82ff 100644 --- a/libs/libc/src/syscall.cpp +++ b/libs/libc/src/syscall.cpp @@ -31,6 +31,7 @@ extern "C" long syscall(long number, ...) result = __luna_syscall2(number, arg0, arg1); break; } + case SYS_getdents: case SYS_fcntl: case SYS_seek: case SYS_write: