ソースを参照

change(vfs): move class vfs out

greatbridf 9 ヶ月 前
コミット
4851662c29

+ 2 - 0
CMakeLists.txt

@@ -62,7 +62,9 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         src/kernel/task/readyqueue.cc
                         src/kernel/user/thread_local.cc
                         src/kernel/vfs/filearr.cc
+                        src/kernel/vfs/inode.cc
                         src/kernel/vfs/tmpfs.cc
+                        src/kernel/vfs/vfs.cc
                         src/kernel/signal.cpp
                         src/types/elf.cpp
                         src/types/libstdcpp.cpp

+ 2 - 7
include/fs/fat.hpp

@@ -119,7 +119,6 @@ private:
     uint32_t next_free_cluster_hint;
     cluster_t root_dir;
     cluster_t data_region_offset;
-    dev_t device;
     uint16_t reserved_sectors;
     uint8_t fat_copies;
     uint8_t sectors_per_cluster;
@@ -167,14 +166,10 @@ private:
     }
 
 public:
-    fat32(const fat32&) = delete;
     explicit fat32(dev_t device);
 
-    virtual size_t read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override;
-    virtual int readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_func& callback) override;
-
-    virtual int inode_statx(dentry* ent, statx* st, unsigned int mask) override;
-    virtual int inode_stat(dentry* ent, struct stat* st) override;
+    virtual ssize_t read(inode* file, char* buf, size_t buf_size, size_t count, off_t offset) override;
+    virtual ssize_t readdir(fs::inode* dir, size_t offset, const vfs::filldir_func& callback) override;
 };
 
 }; // namespace fs::fat

+ 4 - 5
include/kernel/vfs/file.hpp

@@ -49,15 +49,14 @@ public:
 
 struct file {
     mode_t mode; // stores the file type in the same format as inode::mode
-    dentry* parent {};
     struct file_flags {
         uint32_t read : 1;
         uint32_t write : 1;
         uint32_t append : 1;
     } flags {};
 
-    file(mode_t mode, dentry* parent, file_flags flags)
-        : mode(mode) , parent(parent), flags(flags) { }
+    file(mode_t mode, file_flags flags)
+        : mode(mode), flags(flags) { }
 
     virtual ~file() = default;
 
@@ -91,7 +90,7 @@ struct regular_file : public virtual file {
     std::size_t cursor { };
     inode* ind { };
 
-    regular_file(dentry* parent, file_flags flags, size_t cursor, inode* ind);
+    regular_file(file_flags flags, size_t cursor, inode* ind);
 
     virtual ssize_t read(char* __user buf, size_t n) override;
     virtual ssize_t do_write(const char* __user buf, size_t n) override;
@@ -104,7 +103,7 @@ struct fifo_file : public virtual file {
     virtual ~fifo_file() override;
     std::shared_ptr<pipe> ppipe;
 
-    fifo_file(dentry* parent, file_flags flags, std::shared_ptr<fs::pipe> ppipe);
+    fifo_file(file_flags flags, std::shared_ptr<fs::pipe> ppipe);
 
     virtual ssize_t read(char* __user buf, size_t n) override;
     virtual ssize_t do_write(const char* __user buf, size_t n) override;

+ 15 - 9
include/kernel/vfs/inode.hpp

@@ -1,22 +1,28 @@
 #pragma once
 
+#include <bits/alltypes.h>
 #include <stdint.h>
 #include <sys/types.h>
 
-namespace fs {
+#include <kernel/vfs/vfsfwd.hpp>
 
-class vfs;
+namespace fs {
 
 struct inode {
-    ino_t ino;
-    vfs* fs;
-    size_t size;
+    ino_t ino {};
+    size_t size {};
+    nlink_t nlink {};
+
+    vfs* fs {};
+    void* fs_data {};
 
-    nlink_t nlink;
+    struct timespec atime {};
+    struct timespec ctime {};
+    struct timespec mtime {};
 
-    mode_t mode;
-    uid_t uid;
-    gid_t gid;
+    mode_t mode {};
+    uid_t uid {};
+    gid_t gid {};
 };
 
 } // namespace fs

+ 23 - 23
include/kernel/vfs/vfs.hpp

@@ -16,23 +16,23 @@ namespace fs {
 
 class vfs {
 public:
-
-public:
-    using filldir_func = std::function<int(const char*, size_t, inode*, uint8_t)>;
-
-private:
-    // TODO: use allocator designed for small objects
-    using inode_list = std::map<ino_t, inode>;
+    using filldir_func = std::function<ssize_t(const char*, size_t, inode*, uint8_t)>;
 
 private:
-    inode_list _inodes;
-    types::hash_map<dentry*, dentry*> _mount_recover_list;
+    std::map<ino_t, inode> m_inodes;
+    types::hash_map<dentry*, dentry*> m_mount_recover_list;
 
 protected:
-    dentry _root;
+    dentry m_root;
+
+    dev_t m_device;
+    size_t m_io_blksize;
 
 protected:
-    inode* cache_inode(size_t size, ino_t ino, mode_t mode, uid_t uid, gid_t gid);
+    vfs(dev_t device, size_t io_blksize);
+
+    inode* alloc_inode(ino_t ino);
+
     void free_inode(ino_t ino);
     inode* get_inode(ino_t ino);
     void register_root_node(inode* root);
@@ -40,8 +40,6 @@ protected:
     int load_dentry(dentry* ent);
 
 public:
-    vfs();
-
     vfs(const vfs&) = delete;
     vfs& operator=(const vfs&) = delete;
     vfs(vfs&&) = delete;
@@ -49,14 +47,16 @@ public:
 
     constexpr dentry* root(void)
     {
-        return &_root;
+        return &m_root;
     }
 
+    dev_t fs_device() const noexcept;
+    size_t io_blksize() const noexcept;
+
     int mount(dentry* mnt, const char* source, const char* mount_point,
             const char* fstype, unsigned long flags, const void* data);
 
     // directory operations
-
     virtual int inode_mkfile(dentry* dir, const char* filename, mode_t mode);
     virtual int inode_mknode(dentry* dir, const char* filename, mode_t mode, dev_t sn);
     virtual int inode_rmfile(dentry* dir, const char* filename);
@@ -64,19 +64,19 @@ public:
 
     virtual int symlink(dentry* dir, const char* linkname, const char* target);
 
-    // metadata operation
-
-    virtual int inode_statx(dentry* dent, statx* buf, unsigned int mask);
-    virtual int inode_stat(dentry* dent, struct stat* stat);
+    // metadata operations
+    int statx(inode* ind, struct statx* st, unsigned int mask);
 
     // file operations
+    virtual ssize_t read(inode* file, char* buf, size_t buf_size, size_t count, off_t offset);
+    virtual ssize_t write(inode* file, const char* buf, size_t count, off_t offset);
 
-    virtual size_t read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
-    virtual size_t write(inode* file, const char* buf, size_t offset, size_t n);
-    virtual int dev_id(inode* file, dev_t& out_dev);
+    virtual dev_t i_device(inode* ind);
     virtual int readlink(inode* file, char* buf, size_t buf_size);
     virtual int truncate(inode* file, size_t size);
 
+    // directory operations
+
     // parameter 'length' in callback:
     // if 0, 'name' should be null terminated
     // else, 'name' size
@@ -85,7 +85,7 @@ public:
     // return -1 if an error occurred
     // return 0 if no more entry available
     // otherwise, return bytes to be added to the offset
-    virtual int readdir(inode* dir, size_t offset, const filldir_func& callback) = 0;
+    virtual ssize_t readdir(inode* dir, size_t offset, const filldir_func& callback) = 0;
 };
 
 } // namespace fs

+ 24 - 0
include/kernel/vfs/vfsfwd.hpp

@@ -0,0 +1,24 @@
+#pragma once
+
+namespace fs {
+
+// in dentry.hpp
+struct dentry;
+
+// in file.hpp
+struct file;
+struct regular_file;
+struct fifo_file;
+
+class pipe;
+
+// in filearray.hpp
+class file_array;
+
+// in inode.hpp
+struct inode;
+
+// in vfs.hpp
+class vfs;
+
+} // namespace fs

+ 11 - 72
src/fs/fat.cpp

@@ -52,7 +52,7 @@ ssize_t fat32::_read_sector_range(void* _buf, size_t buf_size, uint32_t sector_o
 
     auto* buf = (char*)_buf;
 
-    auto n = fs::block_device_read(device,
+    auto n = block_device_read(m_device,
             buf, buf_size,
             sector_offset * SECTOR_SIZE,
             sector_cnt * SECTOR_SIZE
@@ -88,7 +88,7 @@ void fat32::release_cluster(cluster_t no)
         --iter->second.ref;
 }
 
-int fat32::readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_func& filldir)
+ssize_t fat32::readdir(inode* dir, size_t offset, const vfs::filldir_func& filldir)
 {
     cluster_t next = cl(dir);
     for (size_t i = 0; i < (offset / (sectors_per_cluster * SECTOR_SIZE)); ++i) {
@@ -116,8 +116,10 @@ int fat32::readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_func& f
                     mode |= S_IFDIR;
                 else
                     mode |= S_IFREG;
-                ind = cache_inode(d->size, ino, mode, 0, 0);
 
+                ind = alloc_inode(ino);
+                ind->size = d->size;
+                ind->mode = mode;
                 ind->nlink = d->attributes.subdir ? 2 : 1;
             }
 
@@ -156,7 +158,7 @@ int fat32::readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_func& f
 }
 
 fat32::fat32(dev_t _device)
-    : device { _device }
+    : vfs(_device, 4096)
     , label { }
 {
     auto* buf = new char[SECTOR_SIZE];
@@ -199,16 +201,16 @@ fat32::fat32(dev_t _device)
     cluster_t next = root_dir;
     while ((next = fat[next]) < EOC)
         ++_root_dir_clusters;
-    auto* n = cache_inode(
-        _root_dir_clusters * sectors_per_cluster * SECTOR_SIZE,
-        root_dir, S_IFDIR | 0777, 0, 0);
 
+    auto* n = alloc_inode(root_dir);
+    n->size = _root_dir_clusters * sectors_per_cluster * SECTOR_SIZE;
+    n->mode = S_IFDIR | 0777;
     n->nlink = 2;
 
     register_root_node(n);
 }
 
-size_t fat32::read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+ssize_t fat32::read(inode* file, char* buf, size_t buf_size, size_t n, off_t offset)
 {
     uint32_t cluster_size = SECTOR_SIZE * sectors_per_cluster;
     size_t orig_n = n;
@@ -222,7 +224,7 @@ size_t fat32::read(inode* file, char* buf, size_t buf_size, size_t offset, size_
         auto* data = read_cluster(cno);
         data += offset;
 
-        auto to_copy = std::min(n, cluster_size - offset);
+        auto to_copy = std::min(n, (size_t)(cluster_size - offset));
         auto ncopied = _write_buf_n(buf, buf_size, data, to_copy);
 
         buf += ncopied, n -= ncopied;
@@ -237,69 +239,6 @@ size_t fat32::read(inode* file, char* buf, size_t buf_size, size_t offset, size_
     return orig_n - n;
 }
 
-int fat32::inode_statx(dentry* ent, statx* st, unsigned int mask)
-{
-    st->stx_mask = 0;
-    if (mask & STATX_SIZE) {
-        st->stx_size = ent->ind->size;
-        st->stx_mask |= STATX_SIZE;
-    }
-
-    if (mask & STATX_BLOCKS) {
-        st->stx_blocks = ((ent->ind->size + 0xfff) & ~0xfff) / 512;
-        st->stx_blksize = 4096;
-        st->stx_mask |= STATX_BLOCKS;
-    }
-
-    if (mask & STATX_NLINK) {
-        st->stx_nlink = ent->ind->nlink;
-        st->stx_mask |= STATX_NLINK;
-    }
-
-    st->stx_mode = 0;
-    if (mask & STATX_MODE) {
-        st->stx_mode |= ent->ind->mode & ~S_IFMT;
-        st->stx_mask |= STATX_MODE;
-    }
-
-    if (mask & STATX_TYPE) {
-        st->stx_mode |= ent->ind->mode & S_IFMT;
-        st->stx_mask |= STATX_TYPE;
-    }
-
-    if (mask & STATX_INO) {
-        st->stx_ino = ent->ind->ino;
-        st->stx_mask |= STATX_INO;
-    }
-
-    if (mask & STATX_UID) {
-        st->stx_uid = ent->ind->uid;
-        st->stx_mask |= STATX_UID;
-    }
-
-    if (mask & STATX_GID) {
-        st->stx_gid = ent->ind->gid;
-        st->stx_mask |= STATX_GID;
-    }
-
-    return 0;
-}
-
-int fat32::inode_stat(dentry* dent, struct stat* st)
-{
-    auto* ind = dent->ind;
-
-    memset(st, 0x00, sizeof(struct stat));
-    st->st_mode = ind->mode;
-    st->st_dev = device;
-    st->st_nlink = S_ISDIR(ind->mode) ? 2 : 1;
-    st->st_size = ind->size;
-    st->st_blksize = 4096;
-    st->st_blocks = (ind->size + 511) / 512;
-    st->st_ino = ind->ino;
-    return 0;
-}
-
 static fat32* create_fat32(const char* source, unsigned long, const void*)
 {
     // TODO: flags

+ 8 - 62
src/fs/procfs.cc

@@ -118,15 +118,18 @@ public:
         auto [ _, inserted ] =
             files.insert({ino, proc_file {name, read_func, write_func}});
 
-        cache_inode(0, ino, S_IFREG | 0666, 0, 0);
+        auto* ind = alloc_inode(ino);
+        ind->mode = S_IFREG | 0666;
 
         return inserted ? 0 : -EEXIST;
     }
 
     procfs(const char* _source)
-        : source{_source}
+        : vfs(make_device(0, 10), 4096)
+        , source{_source}
     {
-        auto* ind = cache_inode(0, 0, S_IFDIR | 0777, 0, 0);
+        auto* ind = alloc_inode(0);
+        ind->mode = S_IFDIR | 0777;
 
         create_file("mounts", mounts_read, nullptr);
         create_file("schedstat", schedstat_read, nullptr);
@@ -134,7 +137,7 @@ public:
         register_root_node(ind);
     }
 
-    size_t read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override
+    ssize_t read(inode* file, char* buf, size_t buf_size, size_t n, off_t offset) override
     {
         if (file->ino == 0)
             return -EISDIR;
@@ -170,7 +173,7 @@ public:
         return n;
     }
 
-    int readdir(inode *dir, size_t offset, const filldir_func &callback) override
+    ssize_t readdir(inode *dir, size_t offset, const filldir_func &callback) override
     {
         if (dir->ino != 0)
             return -ENOTDIR;
@@ -190,63 +193,6 @@ public:
 
         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 {

+ 17 - 132
src/kernel/vfs.cpp

@@ -140,132 +140,14 @@ void dentry::path(
         out_dst.append(dents[i]->name.c_str());
 }
 
-vfs::vfs()
-    : _root { nullptr, nullptr, "" }
-{
-}
-
-fs::inode* vfs::cache_inode(size_t size, ino_t ino,
-    mode_t mode, uid_t uid, gid_t gid)
-{
-    auto [ iter, inserted ] =
-        _inodes.try_emplace(ino, inode { ino, this, size, 0, mode, uid, gid });
-    return &iter->second;
-}
-
-void vfs::free_inode(ino_t ino)
-{
-    int n = _inodes.erase(ino);
-    assert(n == 1);
-}
-
-fs::inode* vfs::get_inode(ino_t ino)
-{
-    auto iter = _inodes.find(ino);
-    // TODO: load inode from disk if not found
-    if (iter)
-        return &iter->second;
-    else
-        return nullptr;
-}
-
-void vfs::register_root_node(inode* root)
-{
-    if (!_root.ind)
-        _root.ind = root;
-}
-
-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)
-        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();
-
-    new_ent->parent = mnt->parent;
-    new_ent->name = mnt->name;
-
-    auto* orig_ent = mnt->replace(new_ent);
-    _mount_recover_list.emplace(new_ent, orig_ent);
-
-    return 0;
-}
-
-// default behavior is to
-// return -EINVAL to show that the operation
-// is not supported by the fs
-
-size_t vfs::read(inode*, char*, size_t, size_t, size_t)
-{
-    return -EINVAL;
-}
-
-size_t vfs::write(inode*, const char*, size_t, size_t)
-{
-    return -EINVAL;
-}
-
-int vfs::inode_mkfile(dentry*, const char*, mode_t)
-{
-    return -EINVAL;
-}
-
-int vfs::inode_mknode(dentry*, const char*, mode_t, dev_t)
-{
-    return -EINVAL;
-}
-
-int vfs::inode_rmfile(dentry*, const char*)
-{
-    return -EINVAL;
-}
-
-int vfs::inode_mkdir(dentry*, const char*, mode_t)
-{
-    return -EINVAL;
-}
-
-int vfs::symlink(dentry*, const char*, const char*)
-{
-    return -EINVAL;
-}
-
-int vfs::inode_statx(dentry*, statx*, unsigned int)
-{
-    return -EINVAL;
-}
-
-int vfs::inode_stat(dentry*, struct stat*)
-{
-    return -EINVAL;
-}
-
-int vfs::dev_id(inode*, dev_t&)
-{
-    return -EINVAL;
-}
-
-int vfs::readlink(inode*, char*, size_t)
-{
-    return -EINVAL;
-}
-
-int vfs::truncate(inode*, size_t)
+fs::regular_file::regular_file(
+    file_flags flags, size_t cursor, inode* ind)
+    : file(ind->mode, flags)
+    , cursor(cursor)
+    , ind(ind)
 {
-    return -EINVAL;
 }
 
-fs::regular_file::regular_file(dentry* parent,
-    file_flags flags, size_t cursor, inode* ind)
-    : file(ind->mode, parent, flags), cursor(cursor), ind(ind) { }
-
 ssize_t fs::regular_file::read(char* __user buf, size_t n)
 {
     if (!flags.read)
@@ -394,9 +276,12 @@ int fs::regular_file::getdents64(char* __user buf, size_t cnt)
     return orig_cnt - cnt;
 }
 
-fs::fifo_file::fifo_file(dentry* parent, file_flags flags,
+fs::fifo_file::fifo_file(file_flags flags,
     std::shared_ptr<fs::pipe> ppipe)
-    : file(S_IFIFO, parent, flags), ppipe(ppipe) { }
+    : file(S_IFIFO, flags)
+    , ppipe(ppipe)
+{
+}
 
 ssize_t fs::fifo_file::read(char* __user buf, size_t n)
 {
@@ -431,11 +316,11 @@ size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset,
     }
 
     if (S_ISREG(file->mode))
-        return file->fs->read(file, buf, buf_size, offset, n);
+        return file->fs->read(file, buf, buf_size, n, offset);
 
     if (S_ISBLK(file->mode) || S_ISCHR(file->mode)) {
-        dev_t dev;
-        if (file->fs->dev_id(file, dev) != 0) {
+        dev_t dev = file->fs->i_device(file);
+        if (dev & 0x80000000) {
             errno = EINVAL;
             return -1U;
         }
@@ -465,11 +350,11 @@ size_t fs::vfs_write(fs::inode* file, const char* buf, size_t offset, size_t n)
     }
 
     if (S_ISREG(file->mode))
-        return file->fs->write(file, buf, offset, n);
+        return file->fs->write(file, buf, n, offset);
 
     if (S_ISBLK(file->mode) || S_ISCHR(file->mode)) {
-        dev_t dev;
-        if (file->fs->dev_id(file, dev) != 0) {
+        dev_t dev = file->fs->i_device(file);
+        if (dev & 0x80000000) {
             errno = EINVAL;
             return -1U;
         }
@@ -565,7 +450,7 @@ dentry* fs::vfs_open(dentry& root, const types::path& path, bool follow, int rec
 
 int fs::vfs_stat(dentry* ent, statx* stat, unsigned int mask)
 {
-    return ent->ind->fs->inode_statx(ent, stat, mask);
+    return ent->ind->fs->statx(ent->ind, stat, mask);
 }
 
 int fs::vfs_truncate(inode *file, size_t size)

+ 3 - 4
src/kernel/vfs/filearr.cc

@@ -245,8 +245,7 @@ int filearray::open(dentry& root, const types::path& filepath, int flags, mode_t
     }
 
     return pimpl->place_new_file(
-        std::make_shared<regular_file>(
-            dent->parent, fflags, 0, dent->ind),
+        std::make_shared<regular_file>(fflags, 0, dent->ind),
         fdflag);
 }
 
@@ -259,12 +258,12 @@ int filearray::pipe(int (&pipefd)[2])
 
         pipefd[0] = pimpl->place_new_file(
             std::make_shared<fifo_file>(
-                nullptr, file::file_flags { 1, 0, 0 }, ppipe),
+                file::file_flags { 1, 0, 0 }, ppipe),
             0);
 
         pipefd[1] = pimpl->place_new_file(
             std::make_shared<fifo_file>(
-                nullptr, file::file_flags { 0, 1, 0 }, ppipe),
+                file::file_flags { 0, 1, 0 }, ppipe),
             0);
     }
 

+ 94 - 0
src/kernel/vfs/inode.cc

@@ -0,0 +1,94 @@
+#include <assert.h>
+
+#include <kernel/vfs.hpp>
+#include <kernel/vfs/inode.hpp>
+
+using namespace fs;
+
+int vfs::statx(inode* ind, struct statx* st, unsigned int mask)
+{
+    st->stx_mask = 0;
+
+    if (mask & STATX_NLINK) {
+        st->stx_nlink = ind->nlink;
+        st->stx_mask |= STATX_NLINK;
+    }
+
+    if (mask & STATX_ATIME) {
+        st->stx_atime.tv_nsec = ind->atime.tv_nsec;
+        st->stx_atime.tv_sec = ind->atime.tv_sec;
+
+        st->stx_mask |= STATX_ATIME;
+    }
+
+    if (mask & STATX_CTIME) {
+        st->stx_ctime.tv_nsec = ind->ctime.tv_nsec;
+        st->stx_ctime.tv_sec = ind->ctime.tv_sec;
+
+        st->stx_mask |= STATX_CTIME;
+    }
+
+    if (mask & STATX_MTIME) {
+        st->stx_mtime.tv_nsec = ind->mtime.tv_nsec;
+        st->stx_mtime.tv_sec = ind->mtime.tv_sec;
+
+        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;
+        if (S_ISBLK(ind->mode) || S_ISCHR(ind->mode)) {
+            auto dev = i_device(ind);
+            assert(!(dev & 0x80000000));
+
+            st->stx_rdev_major = NODE_MAJOR(dev);
+            st->stx_rdev_minor = NODE_MINOR(dev);
+        }
+        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 = (ind->size + 512-1) / 512;
+        st->stx_blksize = io_blksize();
+        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;
+    }
+
+    st->stx_dev_major = NODE_MAJOR(fs_device());
+    st->stx_dev_minor = NODE_MINOR(fs_device());
+
+    // TODO: support more attributes
+    st->stx_attributes_mask = 0;
+
+    return 0;
+}
+
+dev_t vfs::i_device(inode*)
+{
+    return -ENODEV;
+}

+ 80 - 157
src/kernel/vfs/tmpfs.cc

@@ -7,11 +7,11 @@
 #include <kernel/log.hpp>
 #include <kernel/vfs.hpp>
 
-using fs::vfs, fs::inode, fs::dentry;
+using namespace fs;
 
 struct tmpfs_file_entry {
-    size_t ino;
-    char filename[128];
+    ino_t ino;
+    std::string filename;
 };
 
 class tmpfs : public virtual vfs {
@@ -21,69 +21,33 @@ private:
     using fdata_t = std::vector<char>;
 
 private:
-    std::map<ino_t, void*> inode_data;
-    ino_t _next_ino;
+    ino_t m_next_ino;
 
 private:
-    ino_t _assign_ino(void)
+    ino_t assign_ino()
     {
-        return _next_ino++;
-    }
-
-    static constexpr vfe_t* as_vfe(void* data)
-    {
-        return static_cast<vfe_t*>(data);
-    }
-    static constexpr fdata_t* as_fdata(void* data)
-    {
-        return static_cast<fdata_t*>(data);
-    }
-    static constexpr uintptr_t as_val(void* data)
-    {
-        return std::bit_cast<uintptr_t>(data);
-    }
-    inline void* _getdata(ino_t ino) const
-    {
-        return inode_data.find(ino)->second;
-    }
-    inline ino_t _savedata(void* data)
-    {
-        ino_t ino = _assign_ino();
-        inode_data.insert(std::make_pair(ino, data));
-        return ino;
-    }
-    inline ino_t _savedata(uintptr_t data)
-    {
-        return _savedata((void*)data);
+        return m_next_ino++;
     }
 
 protected:
-    inline vfe_t* mk_fe_vector() { return new vfe_t{}; }
-    inline fdata_t* mk_data_vector() { return new fdata_t{}; }
+    inline vfe_t* make_vfe() { return new vfe_t{}; }
+    inline fdata_t* make_fdata() { return new fdata_t{}; }
 
-    void mklink(inode* dir, inode* inode, const char* filename)
+    void mklink(inode* dir, inode* ind, const char* filename)
     {
-        auto* fes = as_vfe(_getdata(dir->ino));
-        fes->emplace_back(fe_t {
-            .ino = inode->ino,
-            .filename = {} });
-        dir->size += sizeof(fe_t);
-
-        auto& emplaced = fes->back();
+        auto& fes = *(vfe_t*)dir->fs_data;
+        fes.emplace_back(fe_t { ind->ino, filename });
 
-        strncpy(emplaced.filename, filename, sizeof(emplaced.filename));
-        emplaced.filename[sizeof(emplaced.filename) - 1] = 0;
-
-        ++inode->nlink;
+        dir->size += sizeof(fe_t);
+        ++ind->nlink;
     }
 
-    virtual int readdir(inode* dir, size_t offset, const fs::vfs::filldir_func& filldir) override
+    virtual ssize_t readdir(inode* dir, size_t offset, const vfs::filldir_func& filldir) override
     {
-        if (!S_ISDIR(dir->mode)) {
-            return -1;
-        }
+        if (!S_ISDIR(dir->mode))
+            return -ENOTDIR;
 
-        auto& entries = *as_vfe(_getdata(dir->ino));
+        auto& entries = *(vfe_t*)dir->fs_data;
         size_t off = offset / sizeof(fe_t);
 
         size_t nread = 0;
@@ -93,7 +57,7 @@ protected:
             auto* ind = get_inode(entry.ino);
 
             // inode mode filetype is compatible with user dentry filetype
-            auto ret = filldir(entry.filename, 0, ind, ind->mode & S_IFMT);
+            auto ret = filldir(entry.filename.c_str(), 0, ind, ind->mode & S_IFMT);
             if (ret != 0)
                 break;
         }
@@ -102,50 +66,55 @@ protected:
     }
 
 public:
-    explicit tmpfs(void)
-        : _next_ino(1)
+    explicit tmpfs()
+        : vfs(make_device(0, 2), 4096)
+        , m_next_ino{1}
     {
-        auto& in = *cache_inode(0, _savedata(mk_fe_vector()), S_IFDIR | 0777, 0, 0);
+        auto* in = alloc_inode(assign_ino());
 
-        mklink(&in, &in, ".");
-        mklink(&in, &in, "..");
+        in->fs_data = make_vfe();
+        in->mode = S_IFDIR | 0777;
 
-        register_root_node(&in);
+        mklink(in, in, ".");
+        mklink(in, in, "..");
+
+        register_root_node(in);
     }
-    virtual size_t read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override
+
+    virtual ssize_t read(inode* file, char* buf, size_t buf_size, size_t count, off_t offset) override
     {
         if (!S_ISREG(file->mode))
-            return 0;
+            return -EINVAL;
 
-        auto* data = as_fdata(_getdata(file->ino));
+        auto* data = (fdata_t*)file->fs_data;
         size_t fsize = data->size();
 
-        if (offset + n > fsize)
-            n = fsize - offset;
+        if (offset + count > fsize)
+            count = fsize - offset;
 
-        if (buf_size < n) {
-            n = buf_size;
+        if (buf_size < count) {
+            count = buf_size;
         }
 
-        memcpy(buf, data->data() + offset, n);
+        memcpy(buf, data->data() + offset, count);
 
-        return n;
+        return count;
     }
 
-    virtual size_t write(inode* file, const char* buf, size_t offset, size_t n) override
+    virtual ssize_t write(inode* file, const char* buf, size_t count, off_t offset) override
     {
         if (!S_ISREG(file->mode))
-            return 0;
+            return -EINVAL;
 
-        auto* data = as_fdata(_getdata(file->ino));
+        auto* data = (fdata_t*)file->fs_data;
 
-        if (data->size() < offset + n)
-            data->resize(offset+n);
-        memcpy(data->data() + offset, buf, n);
+        if (data->size() < offset + count)
+            data->resize(offset+count);
+        memcpy(data->data() + offset, buf, count);
 
         file->size = data->size();
 
-        return n;
+        return count;
     }
 
     virtual int inode_mkfile(dentry* dir, const char* filename, mode_t mode) override
@@ -153,11 +122,14 @@ public:
         if (!dir->flags.dir)
             return -ENOTDIR;
 
-        auto& file = *cache_inode(0, _savedata(mk_data_vector()), S_IFREG | mode, 0, 0);
-        mklink(dir->ind, &file, filename);
+        auto* file = alloc_inode(assign_ino());
+        file->mode = S_IFREG | (mode & 0777);
+        file->fs_data = make_fdata();
+
+        mklink(dir->ind, file, filename);
 
         if (dir->flags.present)
-            dir->append(get_inode(file.ino), filename);
+            dir->append(file, filename);
 
         return 0;
     }
@@ -170,11 +142,17 @@ public:
         if (!S_ISBLK(mode) && !S_ISCHR(mode))
             return -EINVAL;
 
-        auto& node = *cache_inode(0, _savedata(dev), mode, 0, 0);
-        mklink(dir->ind, &node, filename);
+        if (dev & ~0xffff)
+            return -EINVAL;
+
+        auto* node = alloc_inode(assign_ino());
+        node->mode = mode;
+        node->fs_data = (void*)(uintptr_t)dev;
+
+        mklink(dir->ind, node, filename);
 
         if (dir->flags.present)
-            dir->append(get_inode(node.ino), filename);
+            dir->append(node, filename);
 
         return 0;
     }
@@ -184,7 +162,10 @@ public:
         if (!dir->flags.dir)
             return -ENOTDIR;
 
-        auto new_dir = cache_inode(0, _savedata(mk_fe_vector()), S_IFDIR | (mode & 0777), 0, 0);
+        auto* new_dir = alloc_inode(assign_ino());
+        new_dir->mode = S_IFDIR | (mode & 0777);
+        new_dir->fs_data = make_vfe();
+
         mklink(new_dir, new_dir, ".");
 
         mklink(dir->ind, new_dir, dirname);
@@ -201,15 +182,19 @@ public:
         if (!dir->flags.dir)
             return -ENOTDIR;
 
-        auto* data = mk_data_vector();
+        auto* data = make_fdata();
         data->resize(strlen(target));
         memcpy(data->data(), target, data->size());
 
-        auto& file = *cache_inode(data->size(), _savedata(data), S_IFLNK | 0777, 0, 0);
-        mklink(dir->ind, &file, linkname);
+        auto* file = alloc_inode(assign_ino());
+        file->mode = S_IFLNK | 0777;
+        file->fs_data = data;
+        file->size = data->size();
+
+        mklink(dir->ind, file, linkname);
 
         if (dir->flags.present)
-            dir->append(get_inode(file.ino), linkname);
+            dir->append(file, linkname);
 
         return 0;
     }
@@ -219,7 +204,7 @@ public:
         if (!S_ISLNK(file->mode))
             return -EINVAL;
 
-        auto* data = as_fdata(_getdata(file->ino));
+        auto* data = (fdata_t*)file->fs_data;
         size_t size = data->size();
 
         size = std::min(size, buf_size);
@@ -229,75 +214,12 @@ public:
         return size;
     }
 
-    virtual int inode_statx(dentry* dent, statx* st, unsigned int mask) override
-    {
-        auto* ind = dent->ind;
-        const mode_t mode = ind->mode;
-
-        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;
-            if (S_ISBLK(mode) || S_ISCHR(mode)) {
-                auto nd = (dev_t)as_val(_getdata(ind->ino));
-                st->stx_rdev_major = NODE_MAJOR(nd);
-                st->stx_rdev_minor = NODE_MINOR(nd);
-            }
-            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 = ((ind->size + 0x1ff) & ~0x1ff) / 512;
-            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;
-    }
-
     virtual int inode_rmfile(dentry* dir, const char* filename) override
     {
         if (!dir->flags.dir)
             return -ENOTDIR;
 
-        auto* vfe = as_vfe(_getdata(dir->ind->ino));
+        auto* vfe = (vfe_t*)dir->ind->fs_data;
         assert(vfe);
 
         auto* dent = dir->find(filename);
@@ -313,7 +235,7 @@ public:
             if (S_ISREG(dent->ind->mode)) {
                 // since we do not allow hard links in tmpfs, there is no need
                 // to check references, we remove the file data directly
-                auto* filedata = as_fdata(_getdata(iter->ino));
+                auto* filedata = (fdata_t*)dent->ind->fs_data;
                 assert(filedata);
 
                 delete filedata;
@@ -331,10 +253,11 @@ public:
         return -EIO;
     }
 
-    virtual int dev_id(inode* file, dev_t& out_dev) override
+    virtual dev_t i_device(inode* file) override
     {
-        out_dev = as_val(_getdata(file->ino));
-        return 0;
+        if (file->mode & S_IFMT & (S_IFBLK | S_IFCHR))
+            return (dev_t)(uintptr_t)file->fs_data;
+        return -ENODEV;
     }
 
     virtual int truncate(inode* file, size_t size) override
@@ -342,7 +265,7 @@ public:
         if (!S_ISREG(file->mode))
             return -EINVAL;
 
-        auto* data = as_fdata(_getdata(file->ino));
+        auto* data = (fdata_t*)file->fs_data;
         data->resize(size);
         file->size = size;
         return 0;

+ 126 - 0
src/kernel/vfs/vfs.cc

@@ -0,0 +1,126 @@
+#include <errno.h>
+
+#include <kernel/vfs.hpp>
+#include <kernel/vfs/vfs.hpp>
+
+using namespace fs;
+
+vfs::vfs(dev_t device, size_t io_blksize)
+    : m_root { nullptr, nullptr, "" }
+    , m_device(device), m_io_blksize(io_blksize)
+{
+}
+
+fs::inode* vfs::alloc_inode(ino_t ino)
+{
+    auto [iter, inserted] = m_inodes.try_emplace(ino);
+    iter->second.ino = ino;
+    iter->second.fs = this;
+
+    assert(inserted);
+    return &iter->second;
+}
+
+void vfs::free_inode(ino_t ino)
+{
+    int n = m_inodes.erase(ino);
+    assert(n == 1);
+}
+
+fs::inode* vfs::get_inode(ino_t ino)
+{
+    auto iter = m_inodes.find(ino);
+    // TODO: load inode from disk if not found
+    if (iter)
+        return &iter->second;
+    else
+        return nullptr;
+}
+
+void vfs::register_root_node(inode* root)
+{
+    if (!m_root.ind)
+        m_root.ind = root;
+}
+
+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)
+        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();
+
+    new_ent->parent = mnt->parent;
+    new_ent->name = mnt->name;
+
+    auto* orig_ent = mnt->replace(new_ent);
+    m_mount_recover_list.emplace(new_ent, orig_ent);
+
+    return 0;
+}
+
+// default behavior is to
+// return -EINVAL to show that the operation
+// is not supported by the fs
+
+ssize_t vfs::read(inode*, char*, size_t, size_t, off_t)
+{
+    return -EINVAL;
+}
+
+ssize_t vfs::write(inode*, const char*, size_t, off_t)
+{
+    return -EINVAL;
+}
+
+int vfs::inode_mkfile(dentry*, const char*, mode_t)
+{
+    return -EINVAL;
+}
+
+int vfs::inode_mknode(dentry*, const char*, mode_t, dev_t)
+{
+    return -EINVAL;
+}
+
+int vfs::inode_rmfile(dentry*, const char*)
+{
+    return -EINVAL;
+}
+
+int vfs::inode_mkdir(dentry*, const char*, mode_t)
+{
+    return -EINVAL;
+}
+
+int vfs::symlink(dentry*, const char*, const char*)
+{
+    return -EINVAL;
+}
+
+int vfs::readlink(inode*, char*, size_t)
+{
+    return -EINVAL;
+}
+
+int vfs::truncate(inode*, size_t)
+{
+    return -EINVAL;
+}
+
+dev_t vfs::fs_device() const noexcept
+{
+    return m_device;
+}
+
+size_t vfs::io_blksize() const noexcept
+{
+    return m_io_blksize;
+}