Ver código fonte

feat(procfs): impl procfs and mount() syscall

greatbridf 10 meses atrás
pai
commit
2a7dde2f9e

+ 2 - 0
CMakeLists.txt

@@ -36,6 +36,7 @@ set(BOOTLOADER_SOURCES src/boot.s
                        )
 
 set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
+                        src/fs/procfs.cc
                         src/kinit.cpp
                         src/kernel/async/waitlist.cc
                         src/kernel/async/lock.cc
@@ -45,6 +46,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         src/kernel/tty.cpp
                         src/kernel/syscall.cpp
                         src/kernel/syscall/fileops.cc
+                        src/kernel/syscall/mount.cc
                         src/kernel/mem.cpp
                         src/kernel/module.cc
                         src/kernel/vfs.cpp

+ 21 - 0
gblibc/include/sys/mount.h

@@ -0,0 +1,21 @@
+#ifndef __GBLIBC_SYS_MOUNT_H
+#define __GBLIBC_SYS_MOUNT_H
+
+#define MS_RDONLY      (1 <<  0)
+#define MS_NOSUID      (1 <<  1)
+#define MS_NODEV       (1 <<  2)
+#define MS_NOEXEC      (1 <<  3)
+#define MS_NOATIME     (1 << 10)
+#define MS_RELATIME    (1 << 21)
+#define MS_STRICTATIME (1 << 24)
+#define MS_LAZYTIME    (1 << 25)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 15 - 3
include/kernel/vfs.hpp

@@ -62,12 +62,23 @@ struct user_dirent64 {
 
 inline dentry* fs_root;
 
+struct mount_data {
+    std::string source;
+    std::string mount_point;
+    std::string fstype;
+    unsigned long flags;
+};
+
+inline std::map<fs::vfs*, mount_data> mounts;
+
 int register_block_device(dev_t node, const blkdev_ops& ops);
 int register_char_device(dev_t node, const chrdev_ops& ops);
 
 // return value: pointer to created vfs object
-// 1. dev_t: device number
-using create_fs_func_t = std::function<vfs*(dev_t)>;
+// 1. const char*: source, such as "/dev/sda" or "proc"
+// 2. unsigned long: flags, such as MS_RDONLY | MS_RELATIME
+// 3. const void*: data, for filesystem use, such as "uid=1000"
+using create_fs_func_t = std::function<vfs*(const char*, unsigned long, const void*)>;
 
 int register_fs(const char* name, create_fs_func_t);
 
@@ -76,7 +87,8 @@ int register_tmpfs();
 
 // returns a pointer to the vfs object
 // vfs objects are managed by the kernel
-int create_fs(const char* name, dev_t device, vfs*& out_vfs);
+int create_fs(const char* source, const char* mount_point, const char* fstype,
+        unsigned long flags, const void* data, vfs*& out_vfs);
 
 void partprobe();
 

+ 2 - 1
include/kernel/vfs/vfs.hpp

@@ -52,7 +52,8 @@ public:
         return &_root;
     }
 
-    int mount(dentry* mnt, vfs* new_fs);
+    int mount(dentry* mnt, const char* source, const char* mount_point,
+            const char* fstype, unsigned long flags, const void* data);
 
     // directory operations
 

+ 3 - 2
init_script.sh

@@ -2,8 +2,6 @@
 
 BUSYBOX=/mnt/busybox
 
-$BUSYBOX mkdir -p /etc
-$BUSYBOX mkdir -p /root
 $BUSYBOX mkdir -p /dev
 
 $BUSYBOX mknod -m 666 /dev/console c 2 0
@@ -20,6 +18,9 @@ export PATH="/bin"
 
 echo ok > /dev/console
 
+mkdir -p /etc /root /proc
+mount -t proc proc proc
+
 cat > /etc/passwd <<EOF
 root:x:0:0:root:/root:/mnt/busybox sh
 EOF

+ 5 - 2
src/fs/fat.cpp

@@ -322,9 +322,12 @@ int fat32::inode_stat(dentry* dent, struct stat* st)
     return GB_OK;
 }
 
-static fat32* create_fat32(dev_t device)
+static fat32* create_fat32(const char* source, unsigned long, const void*)
 {
-    return new fat32(device);
+    // TODO: flags
+    // TODO: parse source
+    (void)source;
+    return new fat32(fs::make_device(8, 1));
 }
 
 class fat32_module : public virtual kernel::module::module {

+ 249 - 0
src/fs/procfs.cc

@@ -0,0 +1,249 @@
+#include <map>
+
+#include <errno.h>
+#include <stdint.h>
+#include <sys/mount.h>
+#include <unistd.h>
+
+#include <types/status.h>
+
+#include <kernel/module.hpp>
+#include <kernel/vfs.hpp>
+#include <kernel/vfs/vfs.hpp>
+
+struct mount_flags_opt {
+    unsigned long flag;
+    const char* name;
+};
+
+static struct mount_flags_opt mount_opts[] = {
+    {MS_NOSUID, ",nosuid"},
+    {MS_NODEV, ",nodev"},
+    {MS_NOEXEC, ",noexec"},
+    {MS_NOATIME, ",noatime"},
+    {MS_RELATIME, ",relatime"},
+    {MS_LAZYTIME, ",lazytime"},
+};
+
+static std::string get_mount_opts(unsigned long mnt_flags)
+{
+    std::string retval;
+
+    if (mnt_flags & MS_RDONLY)
+        retval += "ro";
+    else
+        retval += "rw";
+
+    for (const auto& opt : mount_opts) {
+        if (mnt_flags & opt.flag)
+            retval += opt.name;
+    }
+
+    return retval;
+}
+
+static ssize_t mounts_read(char* page, size_t n)
+{
+    auto orig_n = n;
+
+    for (const auto& [ _, mdata ] : fs::mounts) {
+        if (n == 0)
+            break;
+
+        // TODO: get vfs options
+        auto mount_flags = get_mount_opts(mdata.flags);
+
+        int nwrote = snprintf(page, n, "%s %s %s %s 0 0\n",
+                mdata.source.c_str(), mdata.mount_point.c_str(),
+                mdata.fstype.c_str(), mount_flags.c_str());
+
+        n -= nwrote;
+        page += nwrote;
+    }
+
+    return orig_n - n;
+}
+
+namespace fs::proc {
+
+struct proc_file {
+    std::string name;
+
+    ssize_t (*read)(char* page_buffer, size_t n);
+    ssize_t (*write)(const char* data, size_t n);
+};
+
+class procfs : public virtual fs::vfs {
+private:
+    std::string source;
+    std::map<ino_t, proc_file> files;
+
+    ino_t free_ino = 1;
+
+public:
+    static procfs* create(const char* source, unsigned long, const void*)
+    {
+        // TODO: flags
+        return new procfs(source);
+    }
+
+    int create_file(std::string name,
+            ssize_t (*read_func)(char*, size_t),
+            ssize_t (*write_func)(const char*, size_t))
+    {
+        auto ino = free_ino++;
+
+        auto [ _, inserted ] =
+            files.insert({ino, proc_file {name, read_func, write_func}});
+
+        cache_inode(0, ino, S_IFREG | 0666, 0, 0);
+
+        return inserted ? 0 : -EEXIST;
+    }
+
+    procfs(const char* _source)
+        : source{_source}
+    {
+        auto* ind = cache_inode(0, 0, S_IFDIR | 0777, 0, 0);
+
+        create_file("mounts", mounts_read, nullptr);
+
+        register_root_node(ind);
+    }
+
+    size_t read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override
+    {
+        if (file->ino == 0)
+            return -EISDIR;
+
+        auto iter = files.find(file->ino);
+        if (!iter)
+            return -EIO;
+
+        auto& [ ino, pf ] = *iter;
+
+        if (!pf.read)
+            return -EINVAL;
+
+        // TODO: fix it
+        if (offset)
+            return 0;
+
+        // TODO: allocate page buffer
+        char* page_buffer = new char[4096];
+
+        ssize_t nread = pf.read(page_buffer, 4096);
+        if (nread < 0) {
+            delete[] page_buffer;
+            return nread;
+        }
+
+        n = std::min(n, buf_size);
+        n = std::min(n, (size_t)nread);
+
+        strncpy(buf, page_buffer, n);
+
+        delete[] page_buffer;
+        return n;
+    }
+
+    int readdir(inode *dir, size_t offset, const filldir_func &callback) override
+    {
+        if (dir->ino != 0)
+            return -ENOTDIR;
+
+        // TODO: fix it
+        if (offset)
+            return 0;
+
+        int nread = 0;
+        for (const auto& [ ino, pf ] : files) {
+            auto* ind = get_inode(ino);
+            int ret = callback(pf.name.c_str(), 0, ind, ind->mode);
+            if (ret != GB_OK)
+                return -EIO;
+            ++nread;
+        }
+
+        return nread;
+    }
+
+    virtual int inode_statx(dentry* dent, statx* st, unsigned int mask) override
+    {
+        auto* ind = dent->ind;
+
+        st->stx_mask = 0;
+
+        if (mask & STATX_NLINK) {
+            st->stx_nlink = ind->nlink;
+            st->stx_mask |= STATX_NLINK;
+        }
+
+        // TODO: set modification time
+        if (mask & STATX_MTIME) {
+            st->stx_mtime = {};
+            st->stx_mask |= STATX_MTIME;
+        }
+
+        if (mask & STATX_SIZE) {
+            st->stx_size = ind->size;
+            st->stx_mask |= STATX_SIZE;
+        }
+
+        st->stx_mode = 0;
+        if (mask & STATX_MODE) {
+            st->stx_mode |= ind->mode & ~S_IFMT;
+            st->stx_mask |= STATX_MODE;
+        }
+
+        if (mask & STATX_TYPE) {
+            st->stx_mode |= ind->mode & S_IFMT;
+            st->stx_mask |= STATX_TYPE;
+        }
+
+        if (mask & STATX_INO) {
+            st->stx_ino = ind->ino;
+            st->stx_mask |= STATX_INO;
+        }
+
+        if (mask & STATX_BLOCKS) {
+            st->stx_blocks = 0;
+            st->stx_blksize = 4096;
+            st->stx_mask |= STATX_BLOCKS;
+        }
+
+        if (mask & STATX_UID) {
+            st->stx_uid = ind->uid;
+            st->stx_mask |= STATX_UID;
+        }
+
+        if (mask & STATX_GID) {
+            st->stx_gid = ind->gid;
+            st->stx_mask |= STATX_GID;
+        }
+
+        return 0;
+    }
+};
+
+class procfs_module : public virtual kernel::module::module {
+public:
+    procfs_module() : module("procfs") { }
+
+    virtual int init() override
+    {
+        int ret = fs::register_fs("procfs", procfs::create);
+        if (ret != 0)
+            return kernel::module::MODULE_FAILED;
+        return kernel::module::MODULE_SUCCESS;
+    }
+};
+
+static kernel::module::module* procfs_init()
+{
+    return new procfs_module;
+}
+
+INTERNAL_MODULE(procfs, procfs_init);
+
+} // namespace fs::proc

+ 10 - 12
src/kernel/process.cpp

@@ -3,9 +3,10 @@
 #include <utility>
 
 #include <assert.h>
+#include <bits/alltypes.h>
 #include <stdint.h>
 #include <stdio.h>
-#include <bits/alltypes.h>
+#include <sys/mount.h>
 #include <sys/wait.h>
 
 #include <types/allocator.hpp>
@@ -18,6 +19,7 @@
 
 #include <asm/port_io.h>
 #include <asm/sys.h>
+#include <kernel/async/lock.hpp>
 #include <kernel/interrupt.h>
 #include <kernel/log.hpp>
 #include <kernel/mem.h>
@@ -25,11 +27,10 @@
 #include <kernel/module.hpp>
 #include <kernel/process.hpp>
 #include <kernel/signal.hpp>
-#include <kernel/vfs.hpp>
-#include <kernel/async/lock.hpp>
-#include <kernel/user/thread_local.hpp>
-#include <kernel/task/thread.hpp>
 #include <kernel/task/readyqueue.hpp>
+#include <kernel/task/thread.hpp>
+#include <kernel/user/thread_local.hpp>
+#include <kernel/vfs.hpp>
 
 using kernel::async::mutex;
 using kernel::async::lock_guard, kernel::async::lock_guard_irq;
@@ -437,22 +438,19 @@ void NORETURN _kernel_init(void)
     // mount fat32 /mnt directory
     // TODO: parse kernel parameters
     if (1) {
-        fs::vfs* new_fs;
-        int ret = fs::create_fs("fat32", fs::make_device(8, 1), new_fs);
-        assert(ret == 0);
-
         auto* mount_point = fs::vfs_open(*fs::fs_root, types::path{"/mnt"});
         if (!mount_point) {
             int ret = fs::vfs_mkdir(fs::fs_root, "mnt", 0755);
-            assert(ret == GB_OK);
+            assert(ret == 0);
 
             mount_point = fs::vfs_open(*fs::fs_root, types::path{"/mnt"});
         }
 
         assert(mount_point);
 
-        ret = fs::fs_root->ind->fs->mount(mount_point, new_fs);
-        assert(ret == GB_OK);
+        int ret = fs::fs_root->ind->fs->mount(mount_point, "/dev/sda", "/mnt",
+                "fat32", MS_RDONLY | MS_NOATIME | MS_NODEV | MS_NOSUID, "ro,nodev");
+        assert(ret == 0);
     }
 
     current_process->attr.system = 0;

+ 2 - 0
src/kernel/syscall.cpp

@@ -1176,6 +1176,8 @@ void init_syscall(void)
     syscall_handlers[0x0c] = _syscall_chdir;
     syscall_handlers[0x0e] = _syscall_mknod;
     syscall_handlers[0x14] = _syscall_getpid;
+    extern int _syscall_mount(interrupt_stack*);
+    syscall_handlers[0x15] = _syscall_mount;
     syscall_handlers[0x21] = _syscall_access;
     syscall_handlers[0x25] = _syscall_kill;
     syscall_handlers[0x27] = _syscall_mkdir;

+ 29 - 0
src/kernel/syscall/mount.cc

@@ -0,0 +1,29 @@
+#include <errno.h>
+
+#include <types/path.hpp>
+
+#include <kernel/process.hpp>
+#include <kernel/syscall.hpp>
+#include <kernel/vfs.hpp>
+
+int _syscall_mount(interrupt_stack* data)
+{
+    SYSCALL_ARG1(const char __user*, source);
+    SYSCALL_ARG2(const char __user*, target);
+    SYSCALL_ARG3(const char __user*, fstype);
+    SYSCALL_ARG4(unsigned long, flags);
+    SYSCALL_ARG5(const void __user*, _fsdata);
+
+    if (!fstype)
+        return -EINVAL;
+
+    // TODO: use copy_from_user
+    auto path = current_process->pwd + target;
+    auto* mountpoint = fs::vfs_open(*current_process->root, path);
+
+    if (!mountpoint)
+        return -ENOENT;
+
+    return mountpoint->ind->fs->mount(mountpoint, source,
+            path.full_path().c_str(), fstype, flags, _fsdata);
+}

+ 45 - 24
src/kernel/vfs.cpp

@@ -10,16 +10,20 @@
 #include <errno.h>
 #include <stdint.h>
 #include <stdio.h>
+#include <sys/mount.h>
 #include <sys/types.h>
 
+#include <types/allocator.hpp>
+#include <types/path.hpp>
+#include <types/status.h>
+
 #include <kernel/log.hpp>
 #include <kernel/mem.h>
 #include <kernel/process.hpp>
 #include <kernel/tty.hpp>
 #include <kernel/vfs.hpp>
-#include <types/allocator.hpp>
-#include <types/path.hpp>
-#include <types/status.h>
+#include <kernel/vfs/dentry.hpp>
+#include <kernel/vfs/vfs.hpp>
 
 using fs::vfs, fs::dentry;
 
@@ -172,12 +176,17 @@ void vfs::register_root_node(inode* root)
         _root.ind = root;
 }
 
-int vfs::mount(dentry* mnt, vfs* new_fs)
+int vfs::mount(dentry* mnt, const char* source, const char* mount_point,
+        const char* fstype, unsigned long flags, const void *data)
 {
-    if (!mnt->flags.dir) {
-        errno = ENOTDIR;
-        return GB_FAILED;
-    }
+    if (!mnt->flags.dir)
+        return -ENOTDIR;
+
+    vfs* new_fs;
+    int ret = fs::create_fs(source, mount_point, fstype, flags, data, new_fs);
+
+    if (ret != 0)
+        return ret;
 
     auto* new_ent = new_fs->root();
 
@@ -187,7 +196,7 @@ int vfs::mount(dentry* mnt, vfs* new_fs)
     auto* orig_ent = mnt->replace(new_ent);
     _mount_recover_list.emplace(new_ent, orig_ent);
 
-    return GB_OK;
+    return 0;
 }
 
 // default behavior is to
@@ -565,8 +574,6 @@ int fs::vfs_truncate(inode *file, size_t size)
     return file->fs->truncate(file, size);
 }
 
-static std::list<fs::vfs*> fs_es;
-
 int fs::register_block_device(dev_t node, const fs::blkdev_ops& ops)
 {
     int major = NODE_MAJOR(node);
@@ -597,30 +604,44 @@ int fs::register_char_device(dev_t node, const fs::chrdev_ops& ops)
     return 0;
 }
 
-static std::list<std::pair<std::string, fs::create_fs_func_t>> fs_list;
+static std::map<std::string, fs::create_fs_func_t> fs_list;
 
 int fs::register_fs(const char* name, fs::create_fs_func_t func)
 {
-    fs_list.push_back({ name, func });
+    fs_list.emplace(name, func);
 
     return 0;
 }
 
-int fs::create_fs(const char* name, dev_t device, vfs*& out_vfs)
+int fs::create_fs(const char* source, const char* mount_point, const char* fstype,
+        unsigned long flags, const void* data, vfs*& out_vfs)
 {
-    for (const auto& [ fsname, func ] : fs_list) {
-        if (fsname != name)
-            continue;
+    auto iter = fs_list.find(fstype);
+    if (!iter)
+        return -ENODEV;
 
-        vfs* created_vfs = func(device);
-        fs_es.emplace_back(created_vfs);
+    auto& [ _, func ] = *iter;
 
-        out_vfs = created_vfs;
+    if (!(flags & MS_NOATIME))
+        flags |= MS_RELATIME;
 
-        return 0;
-    }
+    if (flags & MS_STRICTATIME)
+        flags &= ~(MS_RELATIME | MS_NOATIME);
 
-    return -ENODEV;
+    vfs* created_vfs = func(source, flags, data);
+
+    mount_data mnt_data {
+        .source = source,
+        .mount_point = mount_point,
+        .fstype = fstype,
+        .flags = flags,
+    };
+
+    mounts.emplace(created_vfs, mnt_data);
+
+    out_vfs = created_vfs;
+
+    return 0;
 }
 
 // MBR partition table, used by partprobe()
@@ -879,7 +900,7 @@ void init_vfs(void)
     fs::register_tmpfs();
 
     vfs* rootfs;
-    int ret = create_fs("tmpfs", make_device(0, 0), rootfs);
+    int ret = create_fs("none", "/", "tmpfs", MS_NOATIME, nullptr, rootfs);
 
     assert(ret == 0);
     fs_root = rootfs->root();

+ 2 - 1
src/kernel/vfs/tmpfs.cc

@@ -348,8 +348,9 @@ public:
     }
 };
 
-static tmpfs* create_tmpfs(dev_t)
+static tmpfs* create_tmpfs(const char*, unsigned long, const void*)
 {
+    // TODO: flags
     return new tmpfs;
 }