diff options
| author | Richard Qin <richard.qin.x@gmail.com> | 2026-07-10 11:24:56 +0800 |
|---|---|---|
| committer | Richard Qin <richard.qin.x@gmail.com> | 2026-07-10 11:24:56 +0800 |
| commit | 1767d390fbf3d0c65389de46caf0b6d43ef3b6ad (patch) | |
| tree | 0f5e01cd7c7a05abb6b1c7472d7f00f49caf02cc /na/kernel/sys_fs.cc | |
| parent | 63dc025705f6533b91fb1a7d26e6ed864efd9900 (diff) | |
| download | lume-1767d390fbf3d0c65389de46caf0b6d43ef3b6ad.tar.gz lume-1767d390fbf3d0c65389de46caf0b6d43ef3b6ad.tar.xz | |
feat: establish bootable new architecture checkpoint
Replace the compressed boot path with a relocatable higher-half RISC-V entry, expand memory and SMP support, and add the driver, TTY, VFS, exec, signal, syscall, initramfs, and self-test infrastructure needed for a bootable kernel checkpoint.
Diffstat (limited to 'na/kernel/sys_fs.cc')
| -rw-r--r-- | na/kernel/sys_fs.cc | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/na/kernel/sys_fs.cc b/na/kernel/sys_fs.cc new file mode 100644 index 0000000..1d26fdd --- /dev/null +++ b/na/kernel/sys_fs.cc @@ -0,0 +1,679 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * sys_fs.cc — Filesystem-related syscall handlers + * + * Implements: openat, close, read, write, lseek, fstat, getdents64, + * dup, dup3, getcwd, chdir, mkdirat, unlinkat + */ + +#include <lume/syscall.h> +#include <lume/syscall_nr.h> +#include <lume/errno.h> +#include <lume/console.h> +#include <lume/task.h> +#include <lume/fs.h> +#include <lume/file.h> +#include <lume/dcache.h> +#include <lume/namei.h> +#include <lume/fdtable.h> +#include <lume/fs_struct.h> +#include <lume/mount.h> +#include <lume/fs_types.h> +#include <lume/klog.h> + +/* ================================================================== */ +/* Helpers */ +/* ================================================================== */ + +static files_struct* current_files() +{ + TaskControlBlock* t = current_task(); + return t ? t->files : nullptr; +} + +static fs_struct* current_fs() +{ + TaskControlBlock* t = current_task(); + return t ? t->fs : nullptr; +} + +/* ================================================================== */ +/* sys_openat(dirfd, pathname, flags, mode) */ +/* ================================================================== */ + +int64 sys_openat(uint64 a0, uint64 a1, uint64 a2, + uint64 a3, uint64 /*a4*/, uint64 /*a5*/) +{ + int dirfd = static_cast<int>(a0); + const char* pathname = reinterpret_cast<const char*>(a1); + int flags = static_cast<int>(a2); + uint32 mode = static_cast<uint32>(a3); + + files_struct* files = current_files(); + if (!files) return -EMFILE; + + + /* Pathname lookup */ + path p{}; + int err = filename_lookup(dirfd, pathname, 0, &p); + + if (err == -ENOENT && (flags & O_CREAT)) { + /* Create: look up the parent directory */ + /* Find last '/' to split parent path / filename */ + const char* last_slash = nullptr; + for (const char* s = pathname; *s; s++) { + if (*s == '/') last_slash = s; + } + + if (!last_slash || last_slash == pathname) { + /* File in root or current directory */ + path parent{}; + if (last_slash == pathname) { + /* Absolute: parent is root */ + parent.de = g_root_dentry; + parent.mnt = g_root_mnt; + } else { + /* Relative: parent is cwd */ + fs_struct* fs = current_fs(); + if (!fs) return -ENOENT; + parent = fs->pwd; + } + + const char* name = last_slash ? last_slash + 1 : pathname; + inode* dir_ip = parent.de->d_inode; + if (!dir_ip || !dir_ip->i_op || !dir_ip->i_op->create) { + return -EACCES; + } + + /* Create the inode via the directory's i_op->create */ + qstr q{}; + q.name = name; + q.len = 0; + for (const char* s = name; *s; s++) q.len++; + dentry* new_de = d_alloc(parent.de, &q); + if (!new_de) return -ENOMEM; + + + err = dir_ip->i_op->create(dir_ip, new_de, mode & ~S_IFMT, false); + if (err) { + /* TODO: free new_de */ + return err; + } + + p.de = new_de; + p.mnt = parent.mnt; + } else { + /* TODO: multi-component parent lookup */ + return -ENOENT; + } + } else if (err) { + return err; + } + + /* Allocate fd */ + int fd = alloc_fd(files, 0); + if (fd < 0) { + return fd; + } + + /* Allocate file object */ + file* fp = alloc_empty_file(flags); + if (!fp) { + /* TODO: release fd */ + return -ENFILE; + } + + fp->f_inode = p.de->d_inode; + fp->f_path = p; + fp->f_op = p.de->d_inode->i_fop; + fp->f_pos = 0; + + /* Chardev: look up cdev by i_rdev and replace f_op */ + if (S_ISCHR(fp->f_inode->i_mode)) { + extern int devfs_chrdev_open(inode* ip, file* fp); + err = devfs_chrdev_open(fp->f_inode, fp); + if (err) { + fput(fp); + return err; + } + } else if (fp->f_op && fp->f_op->open) { + err = fp->f_op->open(fp->f_inode, fp); + if (err) { + fput(fp); + return err; + } + } + + /* O_TRUNC for regular files */ + if ((flags & O_TRUNC) && S_ISREG(fp->f_inode->i_mode)) { + fp->f_inode->i_size = 0; + } + + fd_install(files, fd, fp); + return fd; +} + +/* ================================================================== */ +/* sys_close(fd) */ +/* ================================================================== */ + +int64 sys_close(uint64 a0, uint64 /*a1*/, uint64 /*a2*/, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int fd = static_cast<int>(a0); + files_struct* files = current_files(); + if (!files) return -EBADF; + return close_fd(files, fd); +} + +/* ================================================================== */ +/* sys_read(fd, buf, count) */ +/* ================================================================== */ + +int64 sys_read(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int fd = static_cast<int>(a0); + char* buf = reinterpret_cast<char*>(a1); + uint64 count = a2; + + files_struct* files = current_files(); + if (!files) return -EBADF; + + file* fp = fcheck(files, fd); + if (!fp) return -EBADF; + + if (!(fp->f_mode & FMODE_READ)) return -EBADF; + + return vfs_read(fp, buf, count, &fp->f_pos); +} + +/* ================================================================== */ +/* sys_write(fd, buf, count) */ +/* ================================================================== */ + +int64 sys_write(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int fd = static_cast<int>(a0); + const char* buf = reinterpret_cast<const char*>(a1); + uint64 count = a2; + + files_struct* files = current_files(); + if (!files) return -EBADF; + + file* fp = fcheck(files, fd); + if (!fp) return -EBADF; + + if (!(fp->f_mode & FMODE_WRITE)) return -EBADF; + + return vfs_write(fp, buf, count, &fp->f_pos); +} + +/* ================================================================== */ +/* sys_lseek(fd, offset, whence) */ +/* ================================================================== */ + +int64 sys_lseek(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int fd = static_cast<int>(a0); + int64 offset = static_cast<int64>(a1); + int whence = static_cast<int>(a2); + + files_struct* files = current_files(); + if (!files) return -EBADF; + + file* fp = fcheck(files, fd); + if (!fp) return -EBADF; + + return vfs_llseek(fp, offset, whence); +} + +/* ================================================================== */ +/* sys_fstat(fd, statbuf) */ +/* ================================================================== */ + +int64 sys_fstat_wrapper(uint64 a0, uint64 a1, uint64 /*a2*/, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int fd = static_cast<int>(a0); + auto* statbuf = reinterpret_cast<kstat*>(a1); + + files_struct* files = current_files(); + if (!files) return -EBADF; + + file* fp = fcheck(files, fd); + if (!fp) return -EBADF; + + return vfs_fstat(fp, statbuf); +} + +/* ================================================================== */ +/* sys_getdents64(fd, dirp, count) */ +/* ================================================================== */ + +struct linux_dirent64 { + uint64 d_ino; + int64 d_off; + uint16 d_reclen; + uint8 d_type; + char d_name[]; +}; + +struct getdents_ctx { + dir_context ctx; + char* buf; + int count; + int written; + int error; +}; + +static bool filldir64(dir_context* ctx, const char* name, int namelen, + int64 offset, uint64 ino, unsigned int d_type) +{ + auto* gctx = container_of(ctx, getdents_ctx, ctx); + + /* Calculate record length (rounded up to 8 bytes) */ + int reclen = static_cast<int>( + (sizeof(linux_dirent64) + namelen + 1 + 7) & ~7ULL); + + if (gctx->written + reclen > gctx->count) { + return false; /* Buffer full */ + } + + auto* dirent = reinterpret_cast<linux_dirent64*>( + gctx->buf + gctx->written); + + dirent->d_ino = ino; + dirent->d_off = offset + 1; + dirent->d_reclen = static_cast<uint16>(reclen); + dirent->d_type = static_cast<uint8>(d_type); + + /* Copy name */ + for (int i = 0; i < namelen; i++) { + dirent->d_name[i] = name[i]; + } + dirent->d_name[namelen] = '\0'; + + gctx->written += reclen; + return true; +} + +int64 sys_getdents64(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int fd = static_cast<int>(a0); + char* buf = reinterpret_cast<char*>(a1); + int count = static_cast<int>(a2); + + files_struct* files = current_files(); + if (!files) return -EBADF; + + file* fp = fcheck(files, fd); + if (!fp) return -EBADF; + + if (!fp->f_op || !fp->f_op->iterate_shared) { + return -ENOTDIR; + } + + getdents_ctx gctx{}; + gctx.ctx.actor = filldir64; + gctx.ctx.pos = fp->f_pos; + gctx.buf = buf; + gctx.count = count; + gctx.written = 0; + gctx.error = 0; + + fp->f_op->iterate_shared(fp, &gctx.ctx); + fp->f_pos = gctx.ctx.pos; + + return gctx.written; +} + +/* ================================================================== */ +/* sys_dup(oldfd) */ +/* ================================================================== */ + +int64 sys_dup(uint64 a0, uint64 /*a1*/, uint64 /*a2*/, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int oldfd = static_cast<int>(a0); + files_struct* files = current_files(); + if (!files) return -EBADF; + + file* fp = fcheck(files, oldfd); + if (!fp) return -EBADF; + + int newfd = alloc_fd(files, 0); + if (newfd < 0) return newfd; + + get_file(fp); + fd_install(files, newfd, fp); + return newfd; +} + +/* ================================================================== */ +/* sys_dup3(oldfd, newfd, flags) */ +/* ================================================================== */ + +int64 sys_dup3(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int oldfd = static_cast<int>(a0); + int newfd = static_cast<int>(a1); + int flags = static_cast<int>(a2); + + files_struct* files = current_files(); + if (!files) return -EBADF; + + return do_dup2(files, oldfd, newfd, flags); +} + +/* ================================================================== */ +/* sys_getcwd(buf, size) */ +/* ================================================================== */ + +int64 sys_getcwd(uint64 a0, uint64 a1, uint64 /*a2*/, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + char* buf = reinterpret_cast<char*>(a0); + uint64 size = a1; + + if (!buf || size == 0) return -EINVAL; + + fs_struct* fs = current_fs(); + if (!fs) return -ENOENT; + + char tmp[256]; + char* result = d_path(&fs->pwd, tmp, sizeof(tmp)); + if (!result) return -ERANGE; + + /* Calculate length */ + int len = 0; + for (const char* s = result; *s; s++) len++; + len++; /* Include NUL */ + + if (static_cast<uint64>(len) > size) return -ERANGE; + + for (int i = 0; i < len; i++) { + buf[i] = result[i]; + } + + return len; +} + +/* ================================================================== */ +/* sys_chdir(pathname) */ +/* ================================================================== */ + +int64 sys_chdir(uint64 a0, uint64 /*a1*/, uint64 /*a2*/, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + const char* pathname = reinterpret_cast<const char*>(a0); + + path p{}; + int err = filename_lookup(AT_FDCWD, pathname, LOOKUP_DIRECTORY, &p); + if (err) return err; + + if (!S_ISDIR(p.de->d_inode->i_mode)) { + path_put(&p); + return -ENOTDIR; + } + + fs_struct* fs = current_fs(); + if (!fs) { + path_put(&p); + return -ENOENT; + } + + set_fs_pwd(fs, &p); + path_put(&p); + return 0; +} + +/* ================================================================== */ +/* sys_mkdirat(dirfd, pathname, mode) */ +/* ================================================================== */ + +int64 sys_mkdirat(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int dirfd = static_cast<int>(a0); + const char* pathname = reinterpret_cast<const char*>(a1); + uint32 mode = static_cast<uint32>(a2); + + /* Walk to the parent directory */ + /* For simplicity, support only "single component" paths for now */ + path parent{}; + int err; + + if (pathname[0] == '/') { + parent.de = g_root_dentry; + parent.mnt = g_root_mnt; + pathname++; /* skip leading / */ + } else { + fs_struct* fs = current_fs(); + if (!fs) return -ENOENT; + parent = fs->pwd; + (void)dirfd; /* TODO: respect dirfd for AT_FDCWD check */ + } + + inode* dir_ip = parent.de->d_inode; + if (!dir_ip || !dir_ip->i_op || !dir_ip->i_op->mkdir) { + return -EPERM; + } + + qstr q{}; + q.name = pathname; + q.len = 0; + for (const char* s = pathname; *s; s++) q.len++; + dentry* new_de = d_alloc(parent.de, &q); + if (!new_de) return -ENOMEM; + + err = dir_ip->i_op->mkdir(dir_ip, new_de, mode | S_IFDIR); + if (err) { + return err; + } + + return 0; +} + +/* ================================================================== */ +/* sys_unlinkat(dirfd, pathname, flags) */ +/* ================================================================== */ + +int64 sys_unlinkat(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + (void)a0; (void)a1; (void)a2; + /* TODO: implement unlink/rmdir */ + return -ENOSYS; +} + +/* ================================================================== */ +/* sys_writev(fd, iov, iovcnt) */ +/* ================================================================== */ + +struct iovec { + void* iov_base; + uint64 iov_len; +}; + +int64 sys_writev(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int fd = static_cast<int>(a0); + const iovec* iov = reinterpret_cast<const iovec*>(a1); + int iovcnt = static_cast<int>(a2); + + files_struct* files = current_files(); + if (!files) return -EBADF; + + file* fp = fcheck(files, fd); + if (!fp) return -EBADF; + if (!(fp->f_mode & FMODE_WRITE)) return -EBADF; + + int64 total = 0; + for (int i = 0; i < iovcnt; i++) { + if (iov[i].iov_len == 0) continue; + int64 ret = vfs_write(fp, static_cast<const char*>(iov[i].iov_base), + iov[i].iov_len, &fp->f_pos); + if (ret < 0) { + return total > 0 ? total : ret; + } + total += ret; + if (static_cast<uint64>(ret) < iov[i].iov_len) break; + } + return total; +} + +/* ================================================================== */ +/* sys_ioctl — dispatch to file_operations */ +/* ================================================================== */ + +int64 sys_ioctl(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int fd = static_cast<int>(a0); + unsigned int cmd = static_cast<unsigned int>(a1); + uint64 arg = a2; + + TaskControlBlock* curr = current_task(); + if (!curr || !curr->files) return -EBADF; + + file* fp = fcheck(curr->files, fd); + if (!fp) return -EBADF; + + if (fp->f_op && fp->f_op->unlocked_ioctl) + return fp->f_op->unlocked_ioctl(fp, cmd, arg); + + return -ENOTTY; +} + +/* ================================================================== */ +/* sys_pipe2(fds, flags) */ +/* ================================================================== */ + +#include <lume/pipe.h> + +int64 sys_pipe2(uint64 a0, uint64 a1, uint64 /*a2*/, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int* user_fds = reinterpret_cast<int*>(a0); + int flags = static_cast<int>(a1); + + files_struct* files = current_files(); + if (!files) return -EMFILE; + + int fds[2]; + int err = do_pipe2(files, fds, flags); + if (err) return err; + + user_fds[0] = fds[0]; + user_fds[1] = fds[1]; + return 0; +} + +/* ================================================================== */ +/* sys_fcntl(fd, cmd, arg) */ +/* ================================================================== */ + +/* fcntl commands — Linux ABI values */ +inline constexpr int F_DUPFD = 0; +inline constexpr int F_GETFD = 1; +inline constexpr int F_SETFD = 2; +inline constexpr int F_GETFL = 3; +inline constexpr int F_SETFL = 4; +inline constexpr int F_DUPFD_CLOEXEC = 1030; + +inline constexpr int FD_CLOEXEC = 1; + +/* Bitmap helpers (duplicated from fd.cc — TODO: move to shared header) */ +static inline bool fcntl_bit_test(const uint64* bmap, int bit) { + return (bmap[bit / 64] >> (bit % 64)) & 1; +} +static inline void fcntl_bit_set(uint64* bmap, int bit) { + bmap[bit / 64] |= (1ULL << (bit % 64)); +} +static inline void fcntl_bit_clear(uint64* bmap, int bit) { + bmap[bit / 64] &= ~(1ULL << (bit % 64)); +} + +int64 sys_fcntl(uint64 a0, uint64 a1, uint64 a2, + uint64 /*a3*/, uint64 /*a4*/, uint64 /*a5*/) +{ + int fd = static_cast<int>(a0); + int cmd = static_cast<int>(a1); + uint64 arg = a2; + + files_struct* files = current_files(); + if (!files) return -EBADF; + + file* fp = fcheck(files, fd); + if (!fp) return -EBADF; + + switch (cmd) { + case F_DUPFD: + case F_DUPFD_CLOEXEC: { + int new_fd = alloc_fd(files, static_cast<unsigned int>(arg)); + if (new_fd < 0) return new_fd; + get_file(fp); + fd_install(files, new_fd, fp); + if (cmd == F_DUPFD_CLOEXEC) { + LockGuard guard(files->file_lock); + fcntl_bit_set(files->close_on_exec_init, new_fd); + } + return new_fd; + } + + case F_GETFD: { + LockGuard guard(files->file_lock); + return fcntl_bit_test(files->close_on_exec_init, fd) ? FD_CLOEXEC : 0; + } + + case F_SETFD: { + LockGuard guard(files->file_lock); + if (arg & FD_CLOEXEC) + fcntl_bit_set(files->close_on_exec_init, fd); + else + fcntl_bit_clear(files->close_on_exec_init, fd); + return 0; + } + + case F_GETFL: + return fp->f_flags; + + case F_SETFL: + /* Only allow changing O_APPEND, O_NONBLOCK, O_ASYNC */ + fp->f_flags = (fp->f_flags & ~(O_APPEND | O_NONBLOCK)) | + (static_cast<unsigned int>(arg) & (O_APPEND | O_NONBLOCK)); + return 0; + + default: + return -EINVAL; + } +} + +/* ================================================================== */ +/* sys_socket (SYS_socket = 198) & sys_sendto (SYS_sendto = 206) */ +/* ================================================================== */ + +int64 sys_socket(uint64, uint64, uint64, uint64, uint64, uint64) +{ + /* + * COMPATIBILITY STUB: + * LumeOS currently lacks a networking stack and Unix Domain Sockets (AF_UNIX). + * We return -EAFNOSUPPORT (Address family not supported by protocol) as expected + * by Linux applications (like BusyBox syslogd connecting to /dev/log). + */ + return -EAFNOSUPPORT; +} + +int64 sys_sendto(uint64, uint64, uint64, uint64, uint64, uint64) +{ + /* COMPATIBILITY STUB */ + return -EAFNOSUPPORT; +} |
