瀏覽代碼

refactor: rewrite all dentry part of vfs

remove old path class, add path_iterator

add string_view in types namespace for path_iterator

remove old hash_map
greatbridf 8 月之前
父節點
當前提交
cafeec4a85

+ 2 - 1
CMakeLists.txt

@@ -61,6 +61,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         src/kernel/task/thread.cc
                         src/kernel/task/readyqueue.cc
                         src/kernel/user/thread_local.cc
+                        src/kernel/vfs/dentry.cc
                         src/kernel/vfs/filearr.cc
                         src/kernel/vfs/inode.cc
                         src/kernel/vfs/tmpfs.cc
@@ -103,7 +104,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/types/bitmap.hpp
                         include/types/buffer.hpp
                         include/types/elf.hpp
-                        include/types/hash_map.hpp
+                        include/types/hash.hpp
                         include/types/list.hpp
                         include/types/types.h
                         include/types/allocator.hpp

+ 1 - 0
gblibc/include/errno.h

@@ -28,6 +28,7 @@ extern int* __errno_location(void);
 #define ENOTTY 25
 #define ESPIPE 29
 #define EPIPE 32
+#define ELOOP 40
 
 #ifdef __cplusplus
 }

+ 3 - 2
include/kernel/process.hpp

@@ -8,6 +8,7 @@
 #include <tuple>
 #include <utility>
 
+#include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <stdint.h>
@@ -60,7 +61,7 @@ public:
 
     process_attr attr {};
     fs::filearray files;
-    types::path pwd;
+    fs::dentry* cwd {};
     mode_t umask { 0022 };
 
     pid_t pid {};
@@ -69,7 +70,7 @@ public:
     pid_t sid {};
 
     kernel::tty::tty* control_tty {};
-    fs::dentry* root { fs::fs_root };
+    struct fs::fs_context fs_context;
     std::set<pid_t> children;
 
 public:

+ 24 - 25
include/kernel/vfs.hpp

@@ -60,16 +60,19 @@ struct PACKED user_dirent64 {
     char d_name[1]; // file name with a padding zero
 };
 
-inline dentry* fs_root;
+struct fs_context {
+    dentry* root;
+};
 
 struct mount_data {
+    fs::vfs* fs;
     std::string source;
     std::string mount_point;
     std::string fstype;
     unsigned long flags;
 };
 
-inline std::map<fs::vfs*, mount_data> mounts;
+inline std::map<struct dentry*, 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);
@@ -85,11 +88,6 @@ int register_fs(const char* name, create_fs_func_t);
 // in tmpfs.cc
 int register_tmpfs();
 
-// returns a pointer to the vfs object
-// vfs objects are managed by the kernel
-int create_fs(const char* source, const char* mount_point, const char* fstype,
-        unsigned long flags, const void* data, vfs*& out_vfs);
-
 void partprobe();
 
 ssize_t block_device_read(dev_t node, char* buf, size_t buf_size, size_t offset, size_t n);
@@ -98,24 +96,25 @@ ssize_t block_device_write(dev_t node, const char* buf, size_t offset, size_t n)
 ssize_t char_device_read(dev_t node, char* buf, size_t buf_size, size_t n);
 ssize_t char_device_write(dev_t node, const char* buf, size_t n);
 
-size_t vfs_read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
-size_t vfs_write(inode* file, const char* buf, size_t offset, size_t n);
-int vfs_mkfile(dentry* dir, const char* filename, mode_t mode);
-int vfs_mknode(dentry* dir, const char* filename, mode_t mode, dev_t sn);
-int vfs_rmfile(dentry* dir, const char* filename);
-int vfs_mkdir(dentry* dir, const char* dirname, mode_t mode);
-int vfs_stat(dentry* dent, statx* stat, unsigned int mask);
-int vfs_truncate(inode* file, size_t size);
-
-/**
- * @brief Opens a file or directory specified by the given path.
- *
- * @param root The root directory of the file system.
- * @param path The absolute path to the file or directory to be opened.
- * @return A pointer to the opened file or directory entry if found.
- *         Otherwise, nullptr is returned.
- */
-dentry* vfs_open(dentry& root, const types::path& path, bool follow_symlinks = true, int recurs_no = 0);
+int creat(struct dentry* at, mode_t mode);
+int mkdir(struct dentry* at, mode_t mode);
+int mknod(struct dentry* at, mode_t mode, dev_t sn);
+int unlink(struct dentry* at);
+int symlink(struct dentry* at, const char* target);
+
+int statx(struct inode* inode, struct statx* stat, unsigned int mask);
+int readlink(struct inode* inode, char* buf, size_t buf_size);
+int truncate(struct inode* file, size_t size);
+size_t read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
+size_t write(struct inode* file, const char* buf, size_t offset, size_t n);
+
+int mount(dentry* mnt, const char* source, const char* mount_point,
+        const char* fstype, unsigned long flags, const void *data);
+
+std::pair<dentry*, int> open(const fs_context& context, dentry* cwd, types::path_iterator path,
+        bool follow_symlinks = true, int recurs_no = 0);
+
+std::pair<dentry*, int> current_open(dentry* cwd, types::path_iterator path, bool follow_symlinks = true);
 
 } // namespace fs
 

+ 42 - 62
include/kernel/vfs/dentry.hpp

@@ -3,75 +3,55 @@
 #include <list>
 #include <string>
 
-#include <types/hash_map.hpp>
+#include <types/hash.hpp>
 #include <types/path.hpp>
 
+#include <kernel/async/lock.hpp>
 #include <kernel/vfs/inode.hpp>
 
 namespace fs {
+static constexpr unsigned long D_PRESENT    = 1 << 0;
+static constexpr unsigned long D_DIRECTORY  = 1 << 1;
+static constexpr unsigned long D_LOADED     = 1 << 2;
+static constexpr unsigned long D_MOUNTPOINT = 1 << 3;
 
 struct dentry {
-public:
-    using name_type = std::string;
-
-private:
-    std::list<dentry>* children = nullptr;
-    types::hash_map<name_type, dentry*>* idx_children = nullptr;
-
-public:
-    dentry* parent;
-    inode* ind;
-    struct {
-        uint32_t dir : 1; // whether the dentry is a directory.
-        // if dir is 1, whether children contains valid data.
-        // otherwise, ignored
-        uint32_t present : 1;
-    } flags;
-    name_type name;
-
-    explicit dentry(dentry* parent, inode* ind, name_type name);
-    dentry(const dentry& val) = delete;
-    constexpr dentry(dentry&& val)
-        : children(std::exchange(val.children, nullptr))
-        , idx_children(std::exchange(val.idx_children, nullptr))
-        , parent(std::exchange(val.parent, nullptr))
-        , ind(std::exchange(val.ind, nullptr))
-        , flags { val.flags }
-        , name(std::move(val.name))
-    {
-        if (children) {
-            for (auto& item : *children)
-                item.parent = this;
-        }
-    }
-
-    dentry& operator=(const dentry& val) = delete;
-    dentry& operator=(dentry&& val) = delete;
-
-    constexpr ~dentry()
-    {
-        if (children) {
-            delete children;
-            children = nullptr;
-        }
-        if (idx_children) {
-            delete idx_children;
-            idx_children = nullptr;
-        }
-    }
-
-    int load();
-
-    dentry* append(inode* ind, name_type name);
-
-    dentry* find(const name_type& name);
-
-    dentry* replace(dentry* val);
-
-    void remove(const name_type& name);
-
-    // out_dst SHOULD be empty
-    void path(const dentry& root, types::path& out_dst) const;
+    struct dcache* cache;
+    vfs* fs;
+
+    struct inode* inode;
+    struct dentry* parent;
+
+    // list head
+    struct dentry* prev;
+    struct dentry* next;
+
+    unsigned long flags;
+    types::hash_t hash;
+
+    // TODO: use atomic
+    std::size_t refcount;
+
+    std::string name;
+};
+
+struct dcache {
+    struct dentry** arr;
+    int hash_bits;
+
+    std::size_t size;
 };
 
+std::pair<struct dentry*, int> d_find(struct dentry* parent, types::string_view name);
+std::string d_path(const struct dentry* dentry, const struct dentry* root);
+
+struct dentry* d_get(struct dentry* dentry);
+struct dentry* d_put(struct dentry* dentry);
+
+void dcache_init(struct dcache* cache, int hash_bits);
+void dcache_drop(struct dcache* cache);
+
+struct dentry* dcache_alloc(struct dcache* cache);
+void dcache_init_root(struct dcache* cache, struct dentry* root);
+
 } // namespace fs

+ 7 - 2
include/kernel/vfs/filearr.hpp

@@ -2,6 +2,10 @@
 
 #include <memory>
 
+#include <types/path.hpp>
+
+#include <kernel/vfs.hpp>
+
 #include "dentry.hpp"
 #include "file.hpp"
 
@@ -14,7 +18,7 @@ private:
     filearray(std::shared_ptr<impl>);
 
 public:
-    filearray();
+    filearray(const fs_context* ctx);
     filearray(filearray&& other) = default;
 
     filearray copy() const;
@@ -33,7 +37,8 @@ public:
     int set_flags(int fd, int flags);
 
     int pipe(int (&pipefd)[2]);
-    int open(dentry& root, const types::path& filepath, int flags, mode_t mode);
+    int open(dentry* cwd, types::path_iterator filepath, int flags, mode_t mode);
+    int open(types::path_iterator filepath, int flags, mode_t mode);
 
     int close(int fd);
 

+ 12 - 18
include/kernel/vfs/vfs.hpp

@@ -10,21 +10,18 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#include <types/hash_map.hpp>
-
 namespace fs {
 
 class vfs {
 public:
-    using filldir_func = std::function<ssize_t(const char*, size_t, inode*, uint8_t)>;
+    using filldir_func = std::function<ssize_t(const char*, inode*, uint8_t)>;
 
 private:
+    struct dcache m_dcache;
+    struct dentry* m_root {};
     std::map<ino_t, inode> m_inodes;
-    types::hash_map<dentry*, dentry*> m_mount_recover_list;
 
 protected:
-    dentry m_root;
-
     dev_t m_device;
     size_t m_io_blksize;
 
@@ -37,19 +34,16 @@ protected:
     inode* get_inode(ino_t ino);
     void register_root_node(inode* root);
 
-    int load_dentry(dentry* ent);
-
 public:
+    static std::pair<vfs*, int> create(const char* source,
+        const char* fstype, unsigned long flags, const void* data);
+
     vfs(const vfs&) = delete;
     vfs& operator=(const vfs&) = delete;
     vfs(vfs&&) = delete;
     vfs& operator=(vfs&&) = delete;
 
-    constexpr dentry* root(void)
-    {
-        return &m_root;
-    }
-
+    struct dentry* root() const noexcept;
     dev_t fs_device() const noexcept;
     size_t io_blksize() const noexcept;
 
@@ -57,12 +51,12 @@ public:
             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);
-    virtual int inode_mkdir(dentry* dir, const char* dirname, mode_t mode);
+    virtual int creat(struct inode* dir, dentry* at, mode_t mode);
+    virtual int mkdir(struct inode* dir, dentry* at, mode_t mode);
+    virtual int mknod(struct inode* dir, dentry* at, mode_t mode, dev_t device);
+    virtual int unlink(struct inode* dir, dentry* at);
 
-    virtual int symlink(dentry* dir, const char* linkname, const char* target);
+    virtual int symlink(struct inode* dir, dentry* at, const char* target);
 
     // metadata operations
     int statx(inode* ind, struct statx* st, unsigned int mask);

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

@@ -3,6 +3,7 @@
 namespace fs {
 
 // in dentry.hpp
+struct dcache;
 struct dentry;
 
 // in file.hpp

+ 50 - 0
include/types/hash.hpp

@@ -0,0 +1,50 @@
+#pragma once
+#include <bit>
+#include <utility>
+
+#include <stdint.h>
+
+#include <types/path.hpp>
+#include <types/types.h>
+
+namespace types {
+
+// taken from linux
+constexpr uint64_t GOLDEN_RATIO_64 = 0x61C8864680B583EBull;
+
+using hash_t = std::size_t;
+
+constexpr hash_t hash(uint64_t val, int bits)
+{
+    // higher bits are more random
+    return (val * GOLDEN_RATIO_64) >> (64 - bits);
+}
+
+inline hash_t hash_ptr(void* p, int bits)
+{
+    return hash(std::bit_cast<uintptr_t>(p), bits);
+}
+
+inline hash_t hash_str(const char* str, int bits)
+{
+    constexpr hash_t seed = 131;
+    hash_t tmp = 0;
+
+    while (*str)
+        tmp = tmp * seed + (*str++);
+
+    return hash(tmp, bits);
+};
+
+inline hash_t hash_str(string_view str, int bits)
+{
+    constexpr hash_t seed = 131;
+    hash_t tmp = 0;
+
+    for (auto c : str)
+        tmp = tmp * seed + c;
+
+    return hash(tmp, bits);
+};
+
+} // namespace types

+ 0 - 348
include/types/hash_map.hpp

@@ -1,348 +0,0 @@
-#pragma once
-#include <assert.h>
-#include <bit>
-#include <map>
-#include <string>
-#include <type_traits>
-#include <utility>
-#include <vector>
-
-#include <stdint.h>
-#include <types/allocator.hpp>
-#include <types/cplusplus.hpp>
-#include <types/types.h>
-
-namespace types {
-
-// taken from linux
-constexpr uint32_t GOLDEN_RATIO_32 = 0x61C88647;
-constexpr uint64_t GOLDEN_RATIO_64 = 0x61C8864680B583EBull;
-
-using hash_t = std::size_t;
-
-static inline constexpr hash_t _hash32(uint32_t val)
-{
-    return val * GOLDEN_RATIO_32;
-}
-
-static inline constexpr hash_t hash32(uint32_t val, std::size_t bits)
-{
-    // higher bits are more random
-    return _hash32(val) >> (8 * sizeof(hash_t) - bits);
-}
-
-static inline constexpr hash_t _hash64(uint64_t val)
-{
-    return val * GOLDEN_RATIO_64;
-}
-
-static inline constexpr hash_t hash64(uint64_t val, std::size_t bits)
-{
-    // higher bits are more random
-    return _hash64(val) >> (8 * sizeof(hash_t) - bits);
-}
-
-template <typename T>
-constexpr bool is_c_string_v = std::is_same_v<std::decay_t<T>, char*>
-    || std::is_same_v<std::decay_t<T>, const char*>;
-
-template <typename T,
-    std::enable_if_t<std::is_convertible_v<T, uint64_t>, bool> = true>
-inline hash_t hash(T val, std::size_t bits)
-{
-    return hash64(static_cast<uint64_t>(val), bits);
-}
-
-template <typename T,
-    std::enable_if_t<std::is_pointer_v<T> && !is_c_string_v<T>, bool> = true>
-inline hash_t hash(T val, std::size_t bits)
-{
-    return hash(std::bit_cast<uintptr_t>(val), bits);
-}
-
-inline hash_t hash(const char* str, std::size_t bits)
-{
-        constexpr uint32_t seed = 131;
-        uint32_t hash = 0;
-
-        while (*str)
-            hash = hash * seed + (*str++);
-
-        return hash64(hash, bits);
-};
-
-template <template <typename, typename, typename> typename String,
-    typename Char, typename Traits, typename Allocator,
-    std::enable_if_t<
-        std::is_same_v<
-            std::decay_t<String<Char, Traits, Allocator>>,
-            std::basic_string<Char, Traits, Allocator>
-        >, bool
-    > = true>
-inline hash_t hash(const String<Char, Traits, Allocator>& str, std::size_t bits)
-{
-    return hash(str.c_str(), bits);
-}
-
-template <typename Key, typename Value,
-    typename Allocator = std::allocator<std::pair<const Key, Value> >,
-    std::enable_if_t<std::is_convertible_v<hash_t, decltype(
-        hash(std::declval<Key>(), std::declval<std::size_t>())
-    )>, bool> = true>
-class hash_map {
-public:
-    template <bool Const>
-    class iterator;
-
-    using key_type = std::add_const_t<Key>;
-    using value_type = Value;
-    using size_type = size_t;
-    using difference_type = ssize_t;
-    using iterator_type = iterator<false>;
-    using const_iterator_type = iterator<true>;
-
-    using bucket_type = std::map<key_type,
-          value_type,std::less<key_type>, Allocator>;
-
-    using bucket_array_type = std::vector<bucket_type, typename
-        std::allocator_traits<Allocator>:: template rebind_alloc<bucket_type>>;
-
-    static constexpr size_type INITIAL_BUCKETS_ALLOCATED = 64;
-
-public:
-    template <bool Const>
-    class iterator {
-    public:
-        using bucket_iterator = std::conditional_t<Const,
-              typename bucket_type::const_iterator,
-              typename bucket_type::iterator>;
-        using _Value = typename bucket_iterator::value_type;
-        using Pointer = typename bucket_iterator::pointer;
-        using Reference = typename bucket_iterator::reference;
-        using hash_map_pointer = std::conditional_t<Const,
-              const hash_map*, hash_map*>;
-
-        friend class hash_map;
-
-    public:
-        constexpr iterator(const iterator& iter) noexcept
-            : n(iter.n), iter(iter.iter), hmap(iter.hmap)
-        {
-        }
-
-        constexpr iterator(iterator&& iter) noexcept
-            : n(std::exchange(iter.n, 0))
-            , iter(std::move(iter.iter))
-            , hmap(std::exchange(iter.hmap, nullptr))
-        {
-        }
-
-        constexpr iterator& operator=(const iterator& iter)
-        {
-            n = iter.n;
-            this->iter = iter.iter;
-            hmap = iter.hmap;
-            return *this;
-        }
-
-        explicit constexpr iterator(std::size_t n, bucket_iterator iter,
-                hash_map_pointer hmap) noexcept
-            : n(n), iter(iter), hmap(hmap)
-        {
-        }
-
-        constexpr bool operator==(const iterator& iter) const noexcept
-        {
-            return (!*this && !iter) || (hmap == iter.hmap && n == iter.n && this->iter == iter.iter);
-        }
-
-        constexpr bool operator!=(const iterator& iter) const noexcept
-        {
-            return !(*this == iter);
-        }
-
-        constexpr iterator operator++()
-        {
-            assert((bool)*this);
-
-            ++iter;
-            while (iter == hmap->buckets[n].end()) {
-                ++n;
-                if (n < hmap->buckets.size())
-                    iter = hmap->buckets[n].begin();
-                else
-                    break;
-            }
-
-            return *this;
-        }
-
-        constexpr iterator operator++(int)
-        {
-            iterator ret { *this };
-
-            (void)this->operator++();
-
-            return ret;
-        }
-
-        constexpr operator bool(void) const
-        {
-            return hmap && n < hmap->buckets.size() && !!iter;
-        }
-
-        constexpr Reference operator*(void) const noexcept
-        {
-            return *iter;
-        }
-        constexpr Pointer operator->(void) const noexcept
-        {
-            return &*iter;
-        }
-
-        constexpr operator const_iterator_type() const noexcept
-        {
-            return const_iterator_type(n, iter, hmap);
-        }
-
-    protected:
-        std::size_t n;
-        bucket_iterator iter;
-        hash_map_pointer hmap;
-    };
-
-private:
-    bucket_array_type buckets;
-
-protected:
-    constexpr uint32_t hash_length(void) const
-    {
-        switch (buckets.capacity()) {
-        case 32:
-            return 5;
-        case 64:
-            return 6;
-        case 128:
-            return 7;
-        case 256:
-            return 8;
-        // TODO
-        default:
-            return 9;
-        }
-    }
-
-public:
-    explicit constexpr hash_map(void)
-        : buckets(INITIAL_BUCKETS_ALLOCATED) {}
-
-    constexpr hash_map(const hash_map& v)
-        : buckets(v.buckets) {}
-
-    constexpr hash_map(hash_map&& v)
-        : buckets(std::move(v.buckets)) {}
-
-    constexpr ~hash_map()
-    {
-        buckets.clear();
-    }
-
-    template <typename... Args>
-    constexpr void emplace(Args&&... args)
-    {
-        std::pair<Key, Value> to_insert{std::forward<Args>(args)...};
-
-        auto hash_value = hash(to_insert.first, hash_length());
-        buckets.at(hash_value).emplace(to_insert);
-    }
-
-    constexpr void remove(const_iterator_type iter)
-    {
-        auto& bucket = buckets[iter.n];
-        bucket.erase(iter.iter);
-    }
-
-    constexpr void remove(iterator_type iter)
-    {
-        return remove((const_iterator_type)iter);
-    }
-
-    constexpr void remove(const key_type& key)
-    {
-        const_iterator_type iter = find(key);
-        if (!iter)
-            return;
-
-        remove(iter);
-    }
-
-    constexpr iterator_type find(const key_type& key)
-    {
-        auto hash_value = hash(key, hash_length());
-        auto& bucket = buckets.at(hash_value);
-        for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) {
-            if (key == iter->first)
-                return iterator_type(hash_value, iter, this);
-        }
-        return this->end();
-    }
-
-    constexpr const_iterator_type find(const key_type& key) const
-    {
-        auto hash_value = hash(key, hash_length());
-        const auto& bucket = buckets.at(hash_value);
-        for (auto iter = bucket.cbegin(); iter != bucket.cend(); ++iter) {
-            if (key == iter->first)
-                return const_iterator_type(hash_value, iter, this);
-        }
-        return this->cend();
-    }
-
-    constexpr void clear(void)
-    {
-        for (auto& bucket : buckets)
-            bucket.clear();
-    }
-
-    constexpr const_iterator_type cend() const noexcept
-    {
-        return const_iterator_type(buckets.size(), buckets[0].end(), this);
-    }
-
-    constexpr const_iterator_type end() const noexcept
-    {
-        return cend();
-    }
-
-    constexpr iterator_type end() noexcept
-    {
-        return iterator_type(buckets.size(), buckets[0].end(), this);
-    }
-
-    constexpr const_iterator_type cbegin() const noexcept
-    {
-        for (std::size_t i = 0; i < buckets.size(); ++i) {
-            if (buckets[i].empty())
-                continue;
-            return const_iterator_type(i, buckets[i].begin(), this);
-        }
-        return cend();
-    }
-
-    constexpr const_iterator_type begin() const noexcept
-    {
-        return cbegin();
-    }
-
-    constexpr iterator_type begin() noexcept
-    {
-        for (std::size_t i = 0; i < buckets.size(); ++i) {
-            if (buckets[i].empty())
-                continue;
-            return iterator_type(i, buckets[i].begin(), this);
-        }
-        return end();
-    }
-};
-
-} // namespace types

+ 69 - 83
include/types/path.hpp

@@ -6,110 +6,96 @@
 
 namespace types {
 
-class path {
-public:
-    using item_string = std::string;
-    using item_vector = std::vector<item_string>;
-    using string_type = std::string;
-    using size_type = std::size_t;
-    using iterator = item_vector::const_iterator;
-
+class string_view {
 private:
-    item_vector m_vec;
+    const char* m_str;
+    std::size_t m_len;
 
 public:
-    constexpr path() = default;
-    constexpr path(const path& val) = default;
-    constexpr path(path&& val) = default;
-    explicit constexpr path(const char* str, size_type len = -1U)
-    { append(str, len); }
-
-    constexpr path& operator=(const path& val) = default;
-    constexpr path& operator=(path&& val) = default;
-    constexpr path& operator=(const char* str)
-    {
-        m_vec.clear();
-        append(str);
-        return *this;
-    }
+    constexpr string_view() : m_str(nullptr), m_len(0) {}
+    constexpr string_view(const char* str, std::size_t len)
+        : m_str(str), m_len(len) {}
+    constexpr string_view(const std::string& str)
+        : m_str(str.c_str()), m_len(str.size()) {}
+    inline string_view(const char* str)
+        : m_str(str), m_len(std::char_traits<char>::length(str)) {}
 
-    constexpr string_type full_path() const
-    {
-        string_type str;
-        for (auto iter = m_vec.begin(); iter != m_vec.end(); ++iter) {
-            if (iter != m_vec.begin()
-                || (m_vec.front().empty() && m_vec.size() == 1))
-                str += '/';
-            str += *iter;
-        }
-        return str;
-    }
-    constexpr item_string last_name() const
-    { return m_vec.empty() ? item_string {} : m_vec.back(); }
-    constexpr bool empty() const
-    { return m_vec.empty(); }
+    constexpr const char* data() const { return m_str; }
+    constexpr std::size_t size() const { return m_len; }
+    constexpr bool empty() const { return m_len == 0; }
 
-    constexpr bool is_absolute() const { return !empty() && !m_vec[0][0]; }
-    constexpr bool is_relative() const { return !empty() && !is_absolute(); }
+    constexpr const char* begin() const { return m_str; }
+    constexpr const char* end() const { return m_str + m_len; }
 
-    constexpr path& append(const char* str, size_type len = -1U)
+    constexpr char operator[](std::size_t pos) const
+    { return m_str[pos]; }
+
+    constexpr bool operator==(const string_view& val) const
     {
-        const char* start = str;
-
-        if (len && *start == '/')
-            clear();
-
-        while (len-- && *str) {
-            if (*str == '/') {
-                if (m_vec.empty() || str != start)
-                    m_vec.emplace_back(start, str - start);
-                start = str + 1;
-            }
-            ++str;
+        if (m_len != val.m_len)
+            return false;
+        for (std::size_t i = 0; i < m_len; ++i) {
+            if (m_str[i] != val.m_str[i])
+                return false;
         }
-        if (str != start || m_vec.size() != 1 || !m_vec.front().empty())
-            m_vec.emplace_back(start, str - start);
-
-        return *this;
+        return true;
     }
-    constexpr path& append(const path& val)
-    {
-        if (&val == this)
-            return *this;
 
-        if (val.is_absolute()) {
-            *this = val;
-            return *this;
+    constexpr bool operator==(const char* str) const
+    {
+        for (std::size_t i = 0; i < m_len; ++i) {
+            if (m_str[i] != str[i])
+                return false;
         }
+        return str[m_len] == '\0';
+    }
 
-        m_vec.insert(m_vec.end(), val.m_vec.begin(), val.m_vec.end());
-        return *this;
+    constexpr bool operator==(const std::string& str) const
+    {
+        if (m_len != str.size())
+            return false;
+        return operator==(str.c_str());
     }
+};
 
-    constexpr void clear() { m_vec.clear(); }
-    constexpr void remove_last()
+class path_iterator {
+private:
+    string_view m_all;
+    unsigned m_curlen = 0;
+    int m_is_absolute;
+
+public:
+    constexpr path_iterator() = default;
+    constexpr path_iterator(string_view str): m_all{str}
     {
-        if (m_vec.size() > 1)
-            m_vec.pop_back();
+        m_is_absolute = !m_all.empty() && m_all[0] == '/';
+        this->operator++();
     }
 
-    constexpr path& operator+=(const char* str)
-    { return append(str); }
-    constexpr path& operator+=(const path& val)
-    { return append(val); }
+    constexpr path_iterator(const std::string& str): path_iterator{string_view{str}} { }
+    inline path_iterator(const char* str): path_iterator{string_view{str}} { }
 
-    constexpr path operator+(const char* str) const
-    { return path{*this}.append(str); }
-    constexpr path operator+(const path& val)
-    { return path{*this}.append(val); }
+    constexpr operator bool() const { return !m_all.empty(); }
+    constexpr bool is_absolute() const { return m_is_absolute; }
 
-    constexpr bool operator==(const char* str) const
+    constexpr string_view operator*() const { return string_view{m_all.data(), m_curlen}; }
+
+    constexpr path_iterator& operator++()
     {
-        return full_path() == str;
-    }
+        std::size_t start = m_curlen;
+        while (start < m_all.size() && m_all[start] == '/')
+            ++start;
+
+        m_all = string_view{m_all.data() + start, m_all.size() - start};
+        if (m_all.empty())
+            return *this;
 
-    constexpr iterator begin() const { return m_vec.cbegin(); }
-    constexpr iterator end() const { return m_vec.cend(); }
+        m_curlen = 0;
+        while (m_curlen < m_all.size() && m_all[m_curlen] != '/')
+            ++m_curlen;
+
+        return *this;
+    }
 };
 
 } // namespace types

+ 1 - 1
src/fs/fat.cpp

@@ -142,7 +142,7 @@ ssize_t fat32::readdir(inode* dir, size_t offset, const vfs::filldir_func& filld
                 else
                     fname += toupper(d->extension[i]);
             }
-            auto ret = filldir(fname.c_str(), 0, ind, ind->mode & S_IFMT);
+            auto ret = filldir(fname.c_str(), ind, ind->mode & S_IFMT);
 
             if (ret != 0) {
                 release_cluster(next);

+ 1 - 1
src/fs/procfs.cc

@@ -185,7 +185,7 @@ public:
         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);
+            int ret = callback(pf.name.c_str(), ind, ind->mode);
             if (ret != 0)
                 return -EIO;
             ++nread;

+ 1 - 1
src/kernel/mem/paging.cc

@@ -355,7 +355,7 @@ void kernel::mem::paging::handle_page_fault(unsigned long err)
         size_t offset = (vaddr & ~0xfff) - mm_area->start;
         char* data = physaddr<char>{pfn};
 
-        int n = vfs_read(
+        int n = fs::read(
             mm_area->mapped_file,
             data,
             4096,

+ 32 - 17
src/kernel/process.cpp

@@ -27,13 +27,17 @@
 
 process::process(const process& parent, pid_t pid)
     : mms { parent.mms }, attr { parent.attr } , files { parent.files.copy() }
-    , pwd { parent.pwd }, umask { parent.umask }, pid { pid }
+    , cwd { parent.cwd }, umask { parent.umask }, pid { pid }
     , ppid { parent.pid }, pgid { parent.pgid } , sid { parent.sid }
-    , control_tty { parent.control_tty }, root { parent.root } { }
+    , control_tty { parent.control_tty }, fs_context { parent.fs_context }
+{
+    fs::d_get(cwd);
+    fs::d_get(fs_context.root);
+}
 
 process::process(pid_t pid, pid_t ppid)
-    : attr { .system = true }
-    , pwd { "/" } , pid { pid } , ppid { ppid }
+    : attr { .system = true }, files{&fs_context}
+    , pid { pid } , ppid { ppid }
 {
     bool inserted;
     std::tie(std::ignore, inserted) = thds.emplace("", pid);
@@ -204,6 +208,18 @@ void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn)
 
     asm volatile("sti");
 
+    // mount rootfs
+
+    fs::vfs* rootfs;
+    if (1) {
+        int ret;
+        std::tie(rootfs, ret) = fs::vfs::create("none",
+                "tmpfs", MS_NOATIME, nullptr);
+        assert(ret == 0);
+    }
+    current_process->fs_context.root = rootfs->root();
+    current_process->cwd = rootfs->root();
+
     // ------------------------------------------
     // interrupt enabled
     // ------------------------------------------
@@ -220,20 +236,18 @@ void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn)
         kmsgf("[kernel] An error occured while loading \"%s\"", mod->name);
     }
 
+    const auto& context = current_process->fs_context;
+
     // mount fat32 /mnt directory
     // TODO: parse kernel parameters
     if (1) {
-        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 == 0);
+        auto [ mnt, status ] = fs::open(context, context.root, "/mnt");
+        assert(mnt && status == -ENOENT);
 
-            mount_point = fs::vfs_open(*fs::fs_root, types::path{"/mnt"});
-        }
+        if (int ret = fs::mkdir(mnt, 0755); 1)
+            assert(ret == 0 && mnt->flags & fs::D_PRESENT);
 
-        assert(mount_point);
-
-        int ret = fs::fs_root->ind->fs->mount(mount_point, "/dev/sda", "/mnt",
+        int ret = rootfs->mount(mnt, "/dev/sda", "/mnt",
                 "fat32", MS_RDONLY | MS_NOATIME | MS_NODEV | MS_NOSUID, "ro,nodev");
         assert(ret == 0);
     }
@@ -248,14 +262,15 @@ void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn)
         .ip{}, .sp{}
     };
 
-    d.exec_dent = fs::vfs_open(*fs::fs_root, types::path{d.argv[0].c_str()});
-    if (!d.exec_dent) {
+    int ret;
+    std::tie(d.exec_dent, ret) = fs::open(context, context.root, d.argv[0]);
+    if (!d.exec_dent || ret) {
         kmsg("kernel panic: init not found!");
         freeze();
     }
 
-    int ret = types::elf::elf32_load(d);
-    assert(ret == 0);
+    if (int ret = types::elf::elf32_load(d); 1)
+        assert(ret == 0);
 
     int ds = 0x33, cs = 0x2b;
 

+ 40 - 88
src/kernel/syscall/fileops.cc

@@ -83,43 +83,33 @@ int kernel::syscall::do_open(const char __user* path, int flags, mode_t mode)
 {
     mode &= ~current_process->umask;
 
-    return current_process->files.open(*current_process->root,
-        current_process->pwd + path, flags, mode);
+    // TODO: use copy_from_user
+    return current_process->files.open(current_process->cwd, path, flags, mode);
 }
 
 int kernel::syscall::do_symlink(const char __user* target, const char __user* linkpath)
 {
     // TODO: use copy_from_user
-    auto path = current_process->pwd + linkpath;
-    auto* dent = fs::vfs_open(*current_process->root, path);
-
-    if (dent)
-        return -EEXIST;
-
-    auto linkname = path.last_name();
-    path.remove_last();
-
-    dent = fs::vfs_open(*current_process->root, path);
-    if (!dent)
-        return -ENOENT;
+    auto [ dent, status ] = fs::current_open(current_process->cwd, linkpath);
+    if (!dent || status != -ENOENT)
+        return status;
 
-    return dent->ind->fs->symlink(dent, linkname.c_str(), target);
+    return fs::symlink(dent, target);
 }
 
 int kernel::syscall::do_readlink(const char __user* pathname, char __user* buf, size_t buf_size)
 {
     // TODO: use copy_from_user
-    auto path = current_process->pwd + pathname;
-    auto* dent = fs::vfs_open(*current_process->root, path, false);
+    auto [ dent, status ] = fs::current_open(current_process->cwd, pathname, false);
 
-    if (!dent)
-        return -ENOENT;
+    if (!dent || status)
+        return status;
 
-    if (buf_size <= 0 || !S_ISLNK(dent->ind->mode))
+    if (buf_size <= 0 || !S_ISLNK(dent->inode->mode))
         return -EINVAL;
 
     // TODO: use copy_to_user
-    return dent->ind->fs->readlink(dent->ind, buf, buf_size);
+    return fs::readlink(dent->inode, buf, buf_size);
 }
 
 int kernel::syscall::do_ioctl(int fd, unsigned long request, uintptr_t arg3)
@@ -378,17 +368,13 @@ int kernel::syscall::do_statx(int dirfd, const char __user* path,
         return -EINVAL;
     }
 
-    auto* dent = fs::vfs_open(*current_process->root,
-            current_process->pwd + path,
-            !(flags & AT_SYMLINK_NOFOLLOW));
-
-    if (!dent)
-        return -ENOENT;
+    auto [ dent, status ] = fs::current_open(
+            current_process->cwd, path, !(flags & AT_SYMLINK_NOFOLLOW));
+    if (!dent || status)
+        return status;
 
     // TODO: copy to user
-    auto ret = fs::vfs_stat(dent, statxbuf, mask);
-
-    return ret;
+    return fs::statx(dent->inode, statxbuf, mask);
 }
 
 int kernel::syscall::do_fcntl(int fd, int cmd, unsigned long arg)
@@ -414,71 +400,45 @@ int kernel::syscall::do_mkdir(const char __user* pathname, mode_t mode)
 {
     mode &= (~current_process->umask & 0777);
 
-    auto path = current_process->pwd + pathname;
-
-    auto* dent = fs::vfs_open(*current_process->root, path);
-    if (dent)
-        return -EEXIST;
-
-    // get parent path
-    auto dirname = path.last_name();
-    path.remove_last();
-
-    dent = fs::vfs_open(*current_process->root, path);
-    if (!dent)
-        return -ENOENT;
-
-    if (!S_ISDIR(dent->ind->mode))
-        return -ENOTDIR;
-
-    auto ret = fs::vfs_mkdir(dent, dirname.c_str(), mode);
-
-    if (ret != 0)
-        return ret;
+    // TODO: use copy_from_user
+    auto [ dent, status ] = fs::current_open(current_process->cwd, pathname);
+    if (!dent || status != -ENOENT)
+        return status;
 
-    return 0;
+    return fs::mkdir(dent, mode);
 }
 
 int kernel::syscall::do_truncate(const char __user* pathname, long length)
 {
-    auto path = current_process->pwd + pathname;
+    auto [ dent, status ] = fs::current_open(current_process->cwd, pathname);
+    if (!dent || status)
+        return status;
 
-    auto* dent = fs::vfs_open(*current_process->root, path);
-    if (!dent)
-        return -ENOENT;
-
-    if (S_ISDIR(dent->ind->mode))
+    if (S_ISDIR(dent->inode->mode))
         return -EISDIR;
 
-    auto ret = fs::vfs_truncate(dent->ind, length);
-
-    if (ret != 0)
-        return ret;
-
-    return 0;
+    return fs::truncate(dent->inode, length);
 }
 
 int kernel::syscall::do_unlink(const char __user* pathname)
 {
-    auto path = current_process->pwd + pathname;
-    auto* dent = fs::vfs_open(*current_process->root, path, false);
+    auto [ dent, status ] = fs::current_open(
+            current_process->cwd, pathname, false);
 
-    if (!dent)
-        return -ENOENT;
+    if (!dent || status)
+        return status;
 
-    if (S_ISDIR(dent->ind->mode))
+    if (S_ISDIR(dent->inode->mode))
         return -EISDIR;
 
-    return fs::vfs_rmfile(dent->parent, dent->name.c_str());
+    return fs::unlink(dent);
 }
 
 int kernel::syscall::do_access(const char __user* pathname, int mode)
 {
-    auto path = current_process->pwd + pathname;
-    auto* dent = fs::vfs_open(*current_process->root, path);
-
-    if (!dent)
-        return -ENOENT;
+    auto [ dent, status ] = fs::current_open(current_process->cwd, pathname);
+    if (!dent || status)
+        return status;
 
     switch (mode) {
     case F_OK:
@@ -495,20 +455,12 @@ int kernel::syscall::do_access(const char __user* pathname, int mode)
 
 int kernel::syscall::do_mknod(const char __user* pathname, mode_t mode, dev_t dev)
 {
-    auto path = current_process->pwd + pathname;
-    auto* dent = fs::vfs_open(*current_process->root, path);
-
-    if (dent)
-        return -EEXIST;
-
-    auto filename = path.last_name();
-    path.remove_last();
-
-    dent = fs::vfs_open(*current_process->root, path);
-    if (!dent)
-        return -ENOENT;
+    mode &= S_IFMT | (~current_process->umask & 0777);
+    auto [ dent, status ] = fs::current_open(current_process->cwd, pathname);
+    if (!dent || status != -ENOENT)
+        return status;
 
-    return fs::vfs_mknode(dent, filename.c_str(), mode, dev);
+    return fs::mknod(dent, mode, dev);
 }
 
 int kernel::syscall::do_poll(pollfd __user* fds, nfds_t nfds, int timeout)

+ 4 - 7
src/kernel/syscall/mount.cc

@@ -17,12 +17,9 @@ int kernel::syscall::do_mount(
         return -EINVAL;
 
     // TODO: use copy_from_user
-    auto path = current_process->pwd + target;
-    auto* mountpoint = fs::vfs_open(*current_process->root, path);
+    auto [ mountpoint, status ] = fs::current_open(current_process->cwd, target);
+    if (!mountpoint || status)
+        return status;
 
-    if (!mountpoint)
-        return -ENOENT;
-
-    return mountpoint->ind->fs->mount(mountpoint, source,
-            path.full_path().c_str(), fstype, flags, _fsdata);
+    return fs::mount(mountpoint, source, target, fstype, flags, _fsdata);
 }

+ 14 - 13
src/kernel/syscall/procops.cc

@@ -13,6 +13,7 @@
 #include <kernel/signal.hpp>
 #include <kernel/syscall.hpp>
 #include <kernel/utsname.hpp>
+#include <kernel/vfs/dentry.hpp>
 
 using namespace kernel::syscall;
 
@@ -27,17 +28,15 @@ static inline void not_implemented(const char* pos, int line)
 
 int kernel::syscall::do_chdir(const char __user* path)
 {
-    auto* dir = fs::vfs_open(*current_process->root,
-            current_process->pwd + path);
-    if (!dir)
-        return -ENOENT;
+    // TODO: use copy_from_user
+    auto [dir, ret] = fs::current_open(current_process->cwd, path);
+    if (!dir || ret)
+        return ret;
 
-    if (!S_ISDIR(dir->ind->mode))
+    if (!(dir->flags & fs::D_DIRECTORY))
         return -ENOTDIR;
 
-    current_process->pwd.clear();
-    dir->path(*current_process->root, current_process->pwd);
-
+    current_process->cwd = dir;
     return 0;
 }
 
@@ -53,11 +52,13 @@ execve_retval kernel::syscall::do_execve(
         .ip{}, .sp{},
     };
 
-    d.exec_dent = fs::vfs_open(*current_process->root,
-            current_process->pwd + exec.c_str());
+    if (1) {
+        int ret;
+        std::tie(d.exec_dent, ret) = fs::current_open(current_process->cwd, exec);
 
-    if (!d.exec_dent)
-        return { 0, 0, -ENOENT };
+        if (!d.exec_dent || ret)
+            return { 0, 0, ret };
+    }
 
     current_process->files.onexec();
 
@@ -141,7 +142,7 @@ int kernel::syscall::do_waitpid(pid_t waitpid, int __user* arg1, int options)
 
 int kernel::syscall::do_getcwd(char __user* buf, size_t buf_size)
 {
-    auto path = current_process->pwd.full_path();
+    auto path = fs::d_path(current_process->cwd, current_process->fs_context.root);
 
     int len = std::min(buf_size-1, path.size());
 

+ 116 - 227
src/kernel/vfs.cpp

@@ -23,125 +23,10 @@
 #include <kernel/vfs/dentry.hpp>
 #include <kernel/vfs/vfs.hpp>
 
-using fs::vfs, fs::dentry;
-
-dentry::dentry(dentry* _parent, inode* _ind, name_type _name)
-    : parent(_parent) , ind(_ind) , flags { } , name(_name)
-{
-    // the dentry is filesystem root or _ind MUST be non null
-    assert(_ind || !_parent);
-    if (!ind || S_ISDIR(ind->mode)) {
-        flags.dir = 1;
-        children = new std::list<dentry>;
-        idx_children = new types::hash_map<name_type, dentry*>;
-    }
-}
-
-int dentry::load()
-{
-    if (!flags.dir || !S_ISDIR(ind->mode))
-        return -ENOTDIR;
-
-    size_t offset = 0;
-    vfs* fs = ind->fs;
-
-    while (true) {
-        int ret = fs->readdir(ind, offset,
-            [this](const char* name, size_t len, inode* ind, uint8_t) -> int {
-                if (!len)
-                    append(ind, name);
-                else
-                    append(ind, dentry::name_type(name, len));
-
-                return 0;
-            });
-
-        if (ret == 0)
-            break;
-
-        offset += ret;
-    }
-
-    flags.present = 1;
-
-    return 0;
-}
-
-dentry* dentry::append(inode* ind, name_type name)
-{
-    auto& ent = children->emplace_back(this, ind, name);
-    idx_children->emplace(ent.name, &ent);
-    return &ent;
-}
-
-dentry* dentry::find(const name_type& name)
-{
-    if (!flags.dir)
-        return nullptr;
-
-    if (name[0] == '.') {
-        if (!name[1])
-            return this;
-        if (name[1] == '.' && !name[2])
-            return parent ? parent : this;
-    }
-
-    if (!flags.present) {
-        int ret = load();
-        if (ret != 0) {
-            errno = -ret;
-            return nullptr;
-        }
-    }
-
-    auto iter = idx_children->find(name);
-    if (!iter) {
-        errno = ENOENT;
-        return nullptr;
-    }
-
-    return iter->second;
-}
-
-dentry* dentry::replace(dentry* val)
-{
-    // TODO: prevent the dirent to be swapped out of memory
-    parent->idx_children->find(this->name)->second = val;
-    return this;
-}
-
-void dentry::remove(const name_type& name)
-{
-    for (auto iter = children->begin(); iter != children->end(); ++iter) {
-        if (iter->name != name)
-            continue;
-        children->erase(iter);
-        break;
-    }
-
-    idx_children->remove(name);
-}
-
-void dentry::path(
-    const dentry& root, types::path &out_dst) const
-{
-    const dentry* dents[32];
-    int cnt = 0;
-
-    const dentry* cur = this;
-    while (cur != &root) {
-        assert(cnt < 32);
-        dents[cnt++] = cur;
-        cur = cur->parent;
-    }
-
-    out_dst.append("/");
-    for (int i = cnt - 1; i >= 0; --i)
-        out_dst.append(dents[i]->name.c_str());
-}
+using fs::dentry;
 
 fs::regular_file::regular_file(
-    file_flags flags, size_t cursor, inode* ind)
+    file_flags flags, size_t cursor, struct inode* ind)
     : file(ind->mode, flags)
     , cursor(cursor)
     , ind(ind)
@@ -157,7 +42,7 @@ ssize_t fs::regular_file::read(char* __user buf, size_t n)
         return -EISDIR;
 
     // TODO: copy to user function !IMPORTANT
-    ssize_t n_wrote = fs::vfs_read(ind, buf, n, cursor, n);
+    ssize_t n_wrote = fs::read(ind, buf, n, cursor, n);
     if (n_wrote >= 0)
         cursor += n_wrote;
 
@@ -170,7 +55,7 @@ ssize_t fs::regular_file::do_write(const char* __user buf, size_t n)
         return -EISDIR;
 
     // TODO: check privilege of user ptr
-    ssize_t n_wrote = fs::vfs_write(ind, buf, cursor, n);
+    ssize_t n_wrote = fs::write(ind, buf, cursor, n);
     if (n_wrote >= 0)
         cursor += n_wrote;
 
@@ -212,11 +97,10 @@ int fs::regular_file::getdents(char* __user buf, size_t cnt)
 
     size_t orig_cnt = cnt;
     int nread = ind->fs->readdir(ind, cursor,
-        [&buf, &cnt](const char* fn, size_t len, inode* ind, uint8_t type) {
-            if (!len)
-                len = strlen(fn);
+        [&buf, &cnt](const char* fn, struct inode* ind, uint8_t type) {
+            size_t filename_len = strlen(fn);
 
-            size_t reclen = sizeof(fs::user_dirent) + 1 + len;
+            size_t reclen = sizeof(fs::user_dirent) + 1 + filename_len;
             if (cnt < reclen)
                 return -EFAULT;
 
@@ -226,7 +110,7 @@ int fs::regular_file::getdents(char* __user buf, size_t cnt)
             // TODO: show offset
             // dirp->d_off = 0;
             // TODO: use copy_to_user
-            memcpy(dirp->d_name, fn, len);
+            memcpy(dirp->d_name, fn, filename_len);
             buf[reclen - 2] = 0;
             buf[reclen - 1] = type;
 
@@ -248,11 +132,10 @@ int fs::regular_file::getdents64(char* __user buf, size_t cnt)
 
     size_t orig_cnt = cnt;
     int nread = ind->fs->readdir(ind, cursor,
-        [&buf, &cnt](const char* fn, size_t len, inode* ind, uint8_t type) {
-            if (!len)
-                len = strlen(fn);
+        [&buf, &cnt](const char* fn, struct inode* ind, uint8_t type) {
+            size_t filename_len = strlen(fn);
 
-            size_t reclen = sizeof(fs::user_dirent64) + len;
+            size_t reclen = sizeof(fs::user_dirent64) + filename_len;
             if (cnt < reclen)
                 return -EFAULT;
 
@@ -262,7 +145,7 @@ int fs::regular_file::getdents64(char* __user buf, size_t cnt)
             dirp->d_reclen = reclen;
             dirp->d_type = type;
             // TODO: use copy_to_user
-            memcpy(dirp->d_name, fn, len);
+            memcpy(dirp->d_name, fn, filename_len);
             buf[reclen - 1] = 0;
 
             buf += reclen;
@@ -308,7 +191,7 @@ fs::fifo_file::~fifo_file()
 static fs::blkdev_ops** blkdevs[256];
 static fs::chrdev_ops** chrdevs[256];
 
-size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+size_t fs::read(struct fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
 {
     if (S_ISDIR(file->mode)) {
         errno = EISDIR;
@@ -342,7 +225,7 @@ size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset,
     errno = EINVAL;
     return -1U;
 }
-size_t fs::vfs_write(fs::inode* file, const char* buf, size_t offset, size_t n)
+size_t fs::write(struct fs::inode* file, const char* buf, size_t offset, size_t n)
 {
     if (S_ISDIR(file->mode)) {
         errno = EISDIR;
@@ -376,88 +259,140 @@ size_t fs::vfs_write(fs::inode* file, const char* buf, size_t offset, size_t n)
     errno = EINVAL;
     return -1U;
 }
-int fs::vfs_mkfile(dentry* dir, const char* filename, mode_t mode)
-{
-    return dir->ind->fs->inode_mkfile(dir, filename, mode);
-}
-int fs::vfs_mknode(dentry* dir, const char* filename, mode_t mode, dev_t dev)
-{
-    return dir->ind->fs->inode_mknode(dir, filename, mode, dev);
-}
-int fs::vfs_rmfile(dentry* dir, const char* filename)
-{
-    return dir->ind->fs->inode_rmfile(dir, filename);
-}
-int fs::vfs_mkdir(dentry* dir, const char* dirname, mode_t mode)
+
+std::pair<dentry*, int> fs::current_open(dentry* cwd, types::path_iterator path, bool follow_symlinks)
 {
-    return dir->ind->fs->inode_mkdir(dir, dirname, mode);
+    return fs::open(current_process->fs_context, cwd, path, follow_symlinks);
 }
 
-dentry* fs::vfs_open(dentry& root, const types::path& path, bool follow, int recurs_no)
+std::pair<dentry*, int> fs::open(const fs_context& context, dentry* cwd,
+    types::path_iterator path, bool follow, int recurs_no)
 {
     // too many recursive search layers will cause stack overflow
     // so we use 16 for now
     if (recurs_no >= 16)
-        return nullptr;
+        return {nullptr, -ELOOP};
 
-    dentry* cur = &root;
+    if (path.is_absolute())
+        cwd = context.root;
 
-    types::path curpath("/");
-    for (const auto& item : path) {
-        if (S_ISLNK(cur->ind->mode)) {
-            char linkpath[256];
-            int ret = cur->ind->fs->readlink(cur->ind, linkpath, sizeof(linkpath));
+    for ( ; path; ++path) {
+        auto item = *path;
+        if (item.empty())
+            continue;
+
+        if (!(cwd->flags & D_PRESENT))
+            return {nullptr, -ENOENT};
 
-            // TODO: return error code
+        assert(cwd->inode);
+        if (S_ISLNK(cwd->inode->mode)) {
+            char linkpath[256];
+            int ret = fs::readlink(cwd->inode, linkpath, sizeof(linkpath));
             if (ret < 0)
-                return nullptr;
+                return {nullptr, ret};
+            linkpath[ret] = 0;
 
-            curpath.remove_last();
-            curpath.append(linkpath, ret);
-            cur = fs::vfs_open(root, curpath, true, recurs_no+1);
+            std::tie(cwd, ret) = fs::open(context, cwd, linkpath, true, recurs_no + 1);
 
-            if (!cur)
-                return nullptr;
+            if (!cwd || (cwd->flags & D_PRESENT))
+                return {nullptr, ret};
         }
 
-        if (item.empty())
-            continue;
-        cur = cur->find(item);
-        if (!cur)
-            return nullptr;
+        int ret;
+        std::tie(cwd, ret) = d_find(cwd, item);
+        if (!cwd)
+            return {nullptr, ret};
+
+        if (cwd->flags & D_MOUNTPOINT) {
+            auto iter = mounts.find(cwd);
+            assert(iter);
 
-        curpath.append(item.c_str());
+            auto* fs = iter->second.fs;
+            assert(fs);
+
+            cwd = fs->root();
+            assert(cwd);
+        }
     }
 
-    if (follow && S_ISLNK(cur->ind->mode)) {
-        char linkpath[256];
-        int ret = cur->ind->fs->readlink(cur->ind, linkpath, sizeof(linkpath));
+    if (!(cwd->flags & D_PRESENT))
+        return {cwd, -ENOENT};
 
-        // TODO: return error code
+    if (follow && S_ISLNK(cwd->inode->mode)) {
+        char linkpath[256];
+        int ret = fs::readlink(cwd->inode, linkpath, sizeof(linkpath));
         if (ret < 0)
-            return nullptr;
+            return {nullptr, ret};
+        linkpath[ret] = 0;
 
-        curpath.remove_last();
-        curpath.append(linkpath, ret);
-        cur = fs::vfs_open(root, curpath, true, recurs_no+1);
-
-        if (!cur)
-            return nullptr;
+        return fs::open(context, cwd, linkpath, true, recurs_no+1);
     }
 
-    return cur;
+    return {cwd, 0};
 }
 
-int fs::vfs_stat(dentry* ent, statx* stat, unsigned int mask)
+int fs::statx(struct inode* inode, struct statx* stat, unsigned int mask)
 {
-    return ent->ind->fs->statx(ent->ind, stat, mask);
+    assert(inode && inode->fs);
+    return inode->fs->statx(inode, stat, mask);
 }
 
-int fs::vfs_truncate(inode *file, size_t size)
+int fs::truncate(struct inode *file, size_t size)
 {
+    assert(file && file->fs);
     return file->fs->truncate(file, size);
 }
 
+int fs::readlink(struct inode* inode, char* buf, size_t size)
+{
+    assert(inode && inode->fs);
+    return inode->fs->readlink(inode, buf, size);
+}
+
+int fs::symlink(struct dentry* at, const char* target)
+{
+    assert(at && at->parent && at->parent->fs);
+    return at->parent->fs->symlink(at->parent->inode, at, target);
+}
+
+int fs::unlink(struct dentry* at)
+{
+    assert(at && at->parent && at->parent->fs);
+    return at->parent->fs->unlink(at->parent->inode, at);
+}
+
+int fs::mknod(struct dentry* at, mode_t mode, dev_t dev)
+{
+    assert(at && at->parent && at->parent->fs);
+    return at->parent->fs->mknod(at->parent->inode, at, mode, dev);
+}
+
+int fs::creat(struct dentry* at, mode_t mode)
+{
+    assert(at && at->parent && at->parent->fs);
+    return at->parent->fs->creat(at->parent->inode, at, mode);
+}
+
+int fs::mount(struct dentry* mnt, const char* source, const char* mount_point,
+        const char* fstype, unsigned long flags, const void *data)
+{
+    assert(mnt && mnt->fs);
+    return mnt->fs->mount(mnt, source, mount_point, fstype, flags, data);
+}
+
+int fs::mkdir(struct dentry* at, mode_t mode)
+{
+    assert(at && at->parent && at->parent->fs);
+    return at->parent->fs->mkdir(at->parent->inode, at, mode);
+}
+
+int mount(dentry* mnt, const char* source, const char* mount_point,
+        const char* fstype, unsigned long flags, const void *data)
+{
+    assert(mnt && mnt->fs);
+    return mnt->fs->mount(mnt, source, mount_point, fstype, flags, data);
+}
+
 int fs::register_block_device(dev_t node, const fs::blkdev_ops& ops)
 {
     int major = NODE_MAJOR(node);
@@ -488,46 +423,6 @@ int fs::register_char_device(dev_t node, const fs::chrdev_ops& ops)
     return 0;
 }
 
-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.emplace(name, func);
-
-    return 0;
-}
-
-int fs::create_fs(const char* source, const char* mount_point, const char* fstype,
-        unsigned long flags, const void* data, vfs*& out_vfs)
-{
-    auto iter = fs_list.find(fstype);
-    if (!iter)
-        return -ENODEV;
-
-    auto& [ _, func ] = *iter;
-
-    if (!(flags & MS_NOATIME))
-        flags |= MS_RELATIME;
-
-    if (flags & MS_STRICTATIME)
-        flags &= ~(MS_RELATIME | MS_NOATIME);
-
-    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()
 
 struct PACKED mbr_part_entry {
@@ -794,10 +689,4 @@ void init_vfs(void)
 
     // register tmpfs
     fs::register_tmpfs();
-
-    vfs* rootfs;
-    int ret = create_fs("none", "/", "tmpfs", MS_NOATIME, nullptr, rootfs);
-
-    assert(ret == 0);
-    fs_root = rootfs->root();
 }

+ 235 - 0
src/kernel/vfs/dentry.cc

@@ -0,0 +1,235 @@
+#include <assert.h>
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <types/hash.hpp>
+#include <types/list.hpp>
+#include <types/path.hpp>
+
+#include <kernel/vfs/dentry.hpp>
+#include <kernel/vfs/vfs.hpp>
+
+using namespace fs;
+using types::hash_t, types::hash_str, types::hash_ptr;
+
+static inline struct dentry* __d_parent(struct dentry* dentry)
+{
+    if (dentry->parent)
+        return dentry->parent;
+    return dentry;
+}
+
+static inline bool __d_is_present(struct dentry* dentry)
+{
+    return dentry->flags & D_PRESENT;
+}
+
+static inline bool __d_is_dir(struct dentry* dentry)
+{
+    return dentry->flags & D_DIRECTORY;
+}
+
+static inline bool __d_is_loaded(struct dentry* dentry)
+{
+    return dentry->flags & D_LOADED;
+}
+
+static inline bool __d_equal(struct dentry* dentry, struct dentry* parent, types::string_view name)
+{
+    return dentry->parent == parent && dentry->name == name;
+}
+
+static inline hash_t __d_hash(struct dentry* parent, types::string_view name)
+{
+    assert(parent && parent->cache);
+    int bits = parent->cache->hash_bits;
+
+    return hash_str(name, bits) ^ hash_ptr(parent, bits);
+}
+
+static inline struct dentry*& __d_first(struct dcache* cache, hash_t hash)
+{
+    return cache->arr[hash & ((1 << cache->hash_bits) - 1)];
+}
+
+static inline void __d_add(struct dentry* parent, struct dentry* dentry)
+{
+    assert(parent == dentry->parent);
+
+    dentry->prev = nullptr;
+    dentry->next = __d_first(parent->cache, dentry->hash);
+
+    __d_first(parent->cache, dentry->hash) = dentry;
+    parent->cache->size++;
+}
+
+static inline struct dentry* __d_find_fast(struct dentry* parent, types::string_view name)
+{
+    auto* cache = parent->cache;
+    assert(cache);
+
+    hash_t hash = __d_hash(parent, name);
+    for (struct dentry* dentry = __d_first(cache, hash); dentry; dentry = dentry->next) {
+        if (!__d_equal(dentry, parent, name))
+            continue;
+
+        return dentry;
+    }
+
+    return nullptr;
+}
+
+static inline int __d_load(struct dentry* parent)
+{
+    if (__d_is_loaded(parent))
+        return 0;
+
+    auto* inode = parent->inode;
+    assert(inode);
+
+    if (!__d_is_dir(parent))
+        return -ENOTDIR;
+    assert(S_ISDIR(inode->mode));
+
+    auto* fs = parent->inode->fs;
+    assert(fs);
+
+    size_t offset = 0;
+    while (true) {
+        ssize_t off = fs->readdir(inode, offset,
+            [parent](const char* fn, struct inode* inode, uint8_t mode) -> int {
+                struct dentry* dentry = dcache_alloc(parent->cache);
+
+                dentry->fs = inode->fs;
+
+                dentry->inode = inode;
+                dentry->parent = parent;
+
+                dentry->name = fn;
+
+                if (S_ISDIR(mode))
+                    dentry->flags = D_PRESENT | D_DIRECTORY;
+                else
+                    dentry->flags = D_PRESENT;
+
+                dentry->hash = __d_hash(parent, dentry->name);
+
+                __d_add(parent, dentry);
+
+                return 0;
+            });
+
+        if (off == 0)
+            break;
+
+        offset += off;
+    }
+
+    parent->flags |= D_LOADED;
+    return 0;
+}
+
+std::pair<struct dentry*, int> fs::d_find(struct dentry* parent, types::string_view name)
+{
+    assert(__d_is_present(parent));
+    if (!__d_is_dir(parent))
+        return {nullptr, -ENOTDIR};
+
+    constexpr types::string_view dot{".", 1};
+    constexpr types::string_view dotdot{"..", 2};
+
+    if (name == dot)
+        return {d_get(parent), 0};
+
+    if (name == dotdot)
+        return {d_get(__d_parent(parent)), 0};
+
+    if (!__d_is_loaded(parent)) {
+        if (int ret = __d_load(parent); ret != 0)
+            return {nullptr, ret};
+    }
+
+    struct dentry* ret = __d_find_fast(parent, name);
+    if (!ret) {
+        auto* dentry = dcache_alloc(parent->cache);
+        dentry->fs = parent->fs;
+
+        dentry->parent = parent;
+        dentry->name.assign(name.data(), name.size());
+        dentry->hash = __d_hash(parent, dentry->name);
+
+        __d_add(parent, dentry);
+
+        return {d_get(dentry), -ENOENT};
+    }
+
+    return {d_get(ret), 0};
+}
+
+std::string fs::d_path(const struct dentry* dentry, const struct dentry* root)
+{
+    const struct dentry* dents[32];
+    int cnt = 0;
+
+    const struct dentry* cur = dentry;
+    while (cur != root) {
+        assert(cur && cnt < 32);
+        dents[cnt++] = cur;
+        cur = cur->parent;
+    }
+
+    std::string ret = "/";
+    for (int i = cnt - 1; i >= 0; --i) {
+        ret += dents[i]->name;
+        ret += '/';
+    }
+
+    return ret;
+}
+
+struct dentry* fs::d_get(struct dentry* dentry)
+{
+    assert(dentry);
+    ++dentry->refcount;
+    return dentry;
+}
+
+struct dentry* fs::d_put(struct dentry* dentry)
+{
+    assert(dentry);
+
+    // TODO: if refcount is zero, mark dentry as unused
+    --dentry->refcount;
+    return dentry;;
+}
+
+void fs::dcache_init(struct dcache* cache, int hash_bits)
+{
+    cache->hash_bits = hash_bits;
+    cache->arr = new struct dentry*[1 << hash_bits]();
+    cache->size = 0;
+}
+
+void fs::dcache_drop(struct dcache* cache)
+{
+    assert(cache->size == 0);
+    delete[] cache->arr;
+}
+
+struct dentry* fs::dcache_alloc(struct dcache* cache)
+{
+    struct dentry* dentry = new struct dentry();
+    dentry->cache = cache;
+
+    return dentry;
+}
+
+void fs::dcache_init_root(struct dcache* cache, struct dentry* root)
+{
+    assert(cache->size == 0);
+
+    root->prev = root->next = nullptr;
+    __d_first(cache, root->hash) = root;
+
+    cache->size++;
+}

+ 23 - 32
src/kernel/vfs/filearr.cc

@@ -1,5 +1,7 @@
 #include <set>
 
+#include <assert.h>
+
 #include <kernel/async/lock.hpp>
 #include <kernel/vfs.hpp>
 #include <kernel/vfs/filearr.hpp>
@@ -35,6 +37,7 @@ struct fditem_comparator {
 struct filearray::impl {
     mutex mtx;
 
+    const fs_context* context;
     std::set<fditem, fditem_comparator> arr;
     int min_avail{};
 
@@ -172,52 +175,39 @@ int filearray::close(int fd)
     return 0;
 }
 
-static inline int _open_file(dentry*& out_dent, dentry& root, const types::path& filepath, int flags, mode_t mode)
+static inline std::pair<dentry*, int>
+_open_file(const fs_context& context, dentry* cwd, types::path_iterator filepath, int flags, mode_t mode)
 {
-    auto* dent = vfs_open(root, filepath);
+    auto [ dent, ret ] = fs::open(context, cwd, filepath);
+    if (!dent)
+        return {nullptr, ret};
 
-    if (dent) {
+    if (dent->flags & D_PRESENT) {
         if ((flags & O_CREAT) && (flags & O_EXCL))
-            return -EEXIST;
-        out_dent = dent;
-        return 0;
+            return {nullptr, -EEXIST};
+        return {dent, 0};
     }
 
     if (!(flags & O_CREAT))
-        return -ENOENT;
+        return {nullptr, -ENOENT};
 
     // create file
+    if (int ret = fs::creat(dent, mode); ret != 0)
+        return {nullptr, ret};
 
-    auto filename = filepath.last_name();
-    auto parent_path = filepath;
-    parent_path.remove_last();
-
-    auto* parent = vfs_open(root, parent_path);
-    if (!parent)
-        return -EINVAL;
-
-    int ret = vfs_mkfile(parent, filename.c_str(), mode);
-    if (ret != 0)
-        return ret;
-
-    dent = parent->find(filename);
-    assert(dent);
-
-    out_dent = dent;
-    return 0;
+    return {dent, 0};
 }
 
 // TODO: file opening permissions check
-int filearray::open(dentry& root, const types::path& filepath, int flags, mode_t mode)
+int filearray::open(dentry* cwd, types::path_iterator filepath, int flags, mode_t mode)
 {
     lock_guard lck{pimpl->mtx};
 
-    dentry* dent {};
-    int ret = _open_file(dent, root, filepath, flags, mode);
+    auto [dent, ret] = _open_file(*pimpl->context, cwd, filepath, flags, mode);
     if (ret != 0)
         return ret;
 
-    auto filemode = dent->ind->mode;
+    auto filemode = dent->inode->mode;
 
     int fdflag = (flags & O_CLOEXEC) ? FD_CLOEXEC : 0;
 
@@ -238,14 +228,14 @@ int filearray::open(dentry& root, const types::path& filepath, int flags, mode_t
     // truncate file
     if (flags & O_TRUNC) {
         if (fflags.write && S_ISREG(filemode)) {
-            auto ret = vfs_truncate(dent->ind, 0);
+            auto ret = fs::truncate(dent->inode, 0);
             if (ret != 0)
                 return ret;
         }
     }
 
     return pimpl->place_new_file(
-        std::make_shared<regular_file>(fflags, 0, dent->ind),
+        std::make_shared<regular_file>(fflags, 0, dent->inode),
         fdflag);
 }
 
@@ -275,15 +265,16 @@ filearray::filearray(std::shared_ptr<impl> ptr)
 {
 }
 
-filearray::filearray()
+filearray::filearray(const fs_context* context)
     : filearray { std::make_shared<impl>() }
 {
+    pimpl->context = context;
 }
 
 filearray filearray::copy() const
 {
     lock_guard lck { pimpl->mtx };
-    filearray ret {};
+    filearray ret { pimpl->context };
 
     ret.pimpl->min_avail = pimpl->min_avail;
     ret.pimpl->arr = pimpl->arr;

+ 44 - 43
src/kernel/vfs/tmpfs.cc

@@ -2,6 +2,7 @@
 #include <map>
 #include <vector>
 
+#include <assert.h>
 #include <stdint.h>
 
 #include <kernel/log.hpp>
@@ -33,10 +34,10 @@ protected:
     inline vfe_t* make_vfe() { return new vfe_t{}; }
     inline fdata_t* make_fdata() { return new fdata_t{}; }
 
-    void mklink(inode* dir, inode* ind, const char* filename)
+    void mklink(inode* dir, inode* ind, std::string filename)
     {
         auto& fes = *(vfe_t*)dir->fs_data;
-        fes.emplace_back(fe_t { ind->ino, filename });
+        fes.emplace_back(fe_t { ind->ino, std::move(filename) });
 
         dir->size += sizeof(fe_t);
         ++ind->nlink;
@@ -57,7 +58,7 @@ protected:
             auto* ind = get_inode(entry.ino);
 
             // inode mode filetype is compatible with user dentry filetype
-            auto ret = filldir(entry.filename.c_str(), 0, ind, ind->mode & S_IFMT);
+            auto ret = filldir(entry.filename.c_str(), ind, ind->mode & S_IFMT);
             if (ret != 0)
                 break;
         }
@@ -81,7 +82,7 @@ public:
         register_root_node(in);
     }
 
-    virtual ssize_t read(inode* file, char* buf, size_t buf_size, size_t count, off_t offset) override
+    virtual ssize_t read(struct inode* file, char* buf, size_t buf_size, size_t count, off_t offset) override
     {
         if (!S_ISREG(file->mode))
             return -EINVAL;
@@ -101,7 +102,7 @@ public:
         return count;
     }
 
-    virtual ssize_t write(inode* file, const char* buf, size_t count, off_t offset) override
+    virtual ssize_t write(struct inode* file, const char* buf, size_t count, off_t offset) override
     {
         if (!S_ISREG(file->mode))
             return -EINVAL;
@@ -117,27 +118,28 @@ public:
         return count;
     }
 
-    virtual int inode_mkfile(dentry* dir, const char* filename, mode_t mode) override
+    virtual int creat(struct inode* dir, struct dentry* at, mode_t mode) override
     {
-        if (!dir->flags.dir)
+        if (!S_ISDIR(dir->mode))
             return -ENOTDIR;
+        assert(at->parent && at->parent->inode == dir);
 
         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(file, filename);
+        mklink(dir, file, at->name);
 
+        at->inode = file;
+        at->flags |= fs::D_PRESENT;
         return 0;
     }
 
-    virtual int inode_mknode(dentry* dir, const char* filename, mode_t mode, dev_t dev) override
+    virtual int mknod(struct inode* dir, struct dentry* at, mode_t mode, dev_t dev) override
     {
-        if (!dir->flags.dir)
+        if (!S_ISDIR(dir->mode))
             return -ENOTDIR;
+        assert(at->parent && at->parent->inode == dir);
 
         if (!S_ISBLK(mode) && !S_ISCHR(mode))
             return -EINVAL;
@@ -149,38 +151,38 @@ public:
         node->mode = mode;
         node->fs_data = (void*)(uintptr_t)dev;
 
-        mklink(dir->ind, node, filename);
-
-        if (dir->flags.present)
-            dir->append(node, filename);
+        mklink(dir, node, at->name);
 
+        at->inode = node;
+        at->flags |= fs::D_PRESENT;
         return 0;
     }
 
-    virtual int inode_mkdir(dentry* dir, const char* dirname, mode_t mode) override
+    virtual int mkdir(struct inode* dir, struct dentry* at, mode_t mode) override
     {
-        if (!dir->flags.dir)
+        if (!S_ISDIR(dir->mode))
             return -ENOTDIR;
+        assert(at->parent && at->parent->inode == dir);
 
         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(new_dir, dir, "..");
 
-        mklink(dir->ind, new_dir, dirname);
-        mklink(new_dir, dir->ind, "..");
-
-        if (dir->flags.present)
-            dir->append(new_dir, dirname);
+        mklink(dir, new_dir, at->name);
 
+        at->inode = new_dir;
+        at->flags |= fs::D_PRESENT | fs::D_DIRECTORY | fs::D_LOADED;
         return 0;
     }
 
-    virtual int symlink(dentry* dir, const char* linkname, const char* target) override
+    virtual int symlink(struct inode* dir, struct dentry* at, const char* target) override
     {
-        if (!dir->flags.dir)
+        if (!S_ISDIR(dir->mode))
             return -ENOTDIR;
+        assert(at->parent && at->parent->inode == dir);
 
         auto* data = make_fdata();
         data->resize(strlen(target));
@@ -191,15 +193,14 @@ public:
         file->fs_data = data;
         file->size = data->size();
 
-        mklink(dir->ind, file, linkname);
-
-        if (dir->flags.present)
-            dir->append(file, linkname);
+        mklink(dir, file, at->name);
 
+        at->inode = file;
+        at->flags |= fs::D_PRESENT;
         return 0;
     }
 
-    virtual int readlink(inode* file, char* buf, size_t buf_size) override
+    virtual int readlink(struct inode* file, char* buf, size_t buf_size) override
     {
         if (!S_ISLNK(file->mode))
             return -EINVAL;
@@ -214,38 +215,38 @@ public:
         return size;
     }
 
-    virtual int inode_rmfile(dentry* dir, const char* filename) override
+    virtual int unlink(struct inode* dir, struct dentry* at) override
     {
-        if (!dir->flags.dir)
+        if (!S_ISDIR(dir->mode))
             return -ENOTDIR;
+        assert(at->parent && at->parent->inode == dir);
 
-        auto* vfe = (vfe_t*)dir->ind->fs_data;
+        auto* vfe = (vfe_t*)dir->fs_data;
         assert(vfe);
 
-        auto* dent = dir->find(filename);
-        if (!dent)
-            return -ENOENT;
-
         for (auto iter = vfe->begin(); iter != vfe->end(); ) {
-            if (iter->ino != dent->ind->ino) {
+            if (iter->ino != at->inode->ino) {
                 ++iter;
                 continue;
             }
 
-            if (S_ISREG(dent->ind->mode)) {
+            if (S_ISDIR(at->inode->mode))
+                return -EISDIR;
+
+            if (S_ISREG(at->inode->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 = (fdata_t*)dent->ind->fs_data;
+                auto* filedata = (fdata_t*)at->inode->fs_data;
                 assert(filedata);
 
                 delete filedata;
             }
 
             free_inode(iter->ino);
-            dir->remove(filename);
+            at->flags &= ~fs::D_PRESENT;
+            at->inode = nullptr;
 
             vfe->erase(iter);
-
             return 0;
         }
 

+ 64 - 17
src/kernel/vfs/vfs.cc

@@ -1,14 +1,44 @@
+#include <assert.h>
 #include <errno.h>
+#include <sys/mount.h>
 
 #include <kernel/vfs.hpp>
+#include <kernel/vfs/dentry.hpp>
 #include <kernel/vfs/vfs.hpp>
 
 using namespace fs;
 
+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.emplace(name, func);
+
+    return 0;
+}
+
 vfs::vfs(dev_t device, size_t io_blksize)
-    : m_root { nullptr, nullptr, "" }
-    , m_device(device), m_io_blksize(io_blksize)
+    : m_device(device), m_io_blksize(io_blksize)
 {
+    dcache_init(&m_dcache, 8);
+}
+
+std::pair<vfs*, int> vfs::create(const char* source,
+    const char* fstype, unsigned long flags, const void* data)
+{
+    auto iter = fs_list.find(fstype);
+    if (!iter)
+        return {nullptr, -ENODEV};
+
+    auto& [ _, func ] = *iter;
+
+    if (!(flags & MS_NOATIME))
+        flags |= MS_RELATIME;
+
+    if (flags & MS_STRICTATIME)
+        flags &= ~(MS_RELATIME | MS_NOATIME);
+
+    return {func(source, flags, data), 0};
 }
 
 fs::inode* vfs::alloc_inode(ino_t ino)
@@ -37,31 +67,43 @@ fs::inode* vfs::get_inode(ino_t ino)
         return nullptr;
 }
 
-void vfs::register_root_node(inode* root)
+void vfs::register_root_node(struct inode* root_inode)
 {
-    if (!m_root.ind)
-        m_root.ind = root;
+    assert(!root());
+
+    m_root = fs::dcache_alloc(&m_dcache);
+    m_root->fs = this;
+
+    m_root->inode = root_inode;
+    m_root->flags = D_DIRECTORY | D_PRESENT;
+
+    fs::dcache_init_root(&m_dcache, m_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)
+    if (!(mnt->flags & D_DIRECTORY))
         return -ENOTDIR;
 
-    vfs* new_fs;
-    int ret = fs::create_fs(source, mount_point, fstype, flags, data, new_fs);
-
+    auto [new_fs, ret] = vfs::create(source, fstype, flags, data);
     if (ret != 0)
         return ret;
 
+    mounts.emplace(mnt, mount_data {
+                            .fs = new_fs,
+                            .source = source,
+                            .mount_point = mount_point,
+                            .fstype = fstype,
+                            .flags = flags,
+                        });
+    mnt->flags |= D_MOUNTPOINT;
+
     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);
+    new_ent->hash = mnt->hash;
 
     return 0;
 }
@@ -80,27 +122,27 @@ ssize_t vfs::write(inode*, const char*, size_t, off_t)
     return -EINVAL;
 }
 
-int vfs::inode_mkfile(dentry*, const char*, mode_t)
+int vfs::creat(inode*, dentry*, mode_t)
 {
     return -EINVAL;
 }
 
-int vfs::inode_mknode(dentry*, const char*, mode_t, dev_t)
+int vfs::mknod(inode*, dentry*, mode_t, dev_t)
 {
     return -EINVAL;
 }
 
-int vfs::inode_rmfile(dentry*, const char*)
+int vfs::unlink(inode*, dentry*)
 {
     return -EINVAL;
 }
 
-int vfs::inode_mkdir(dentry*, const char*, mode_t)
+int vfs::mkdir(inode*, dentry*, mode_t)
 {
     return -EINVAL;
 }
 
-int vfs::symlink(dentry*, const char*, const char*)
+int vfs::symlink(inode*, dentry*, const char*)
 {
     return -EINVAL;
 }
@@ -115,6 +157,11 @@ int vfs::truncate(inode*, size_t)
     return -EINVAL;
 }
 
+struct dentry* vfs::root() const noexcept
+{
+    return m_root;
+}
+
 dev_t vfs::fs_device() const noexcept
 {
     return m_device;

+ 7 - 7
src/types/elf.cpp

@@ -37,8 +37,8 @@ int types::elf::elf32_load(types::elf::elf32_load_data& d)
         return -ENOENT;
 
     types::elf::elf32_header hdr {};
-    auto n_read = fs::vfs_read(
-        exec->ind,
+    auto n_read = fs::read(
+        exec->inode,
         (char*)&hdr,
         sizeof(types::elf::elf32_header),
         0, sizeof(types::elf::elf32_header));
@@ -53,8 +53,8 @@ int types::elf::elf32_load(types::elf::elf32_load_data& d)
     size_t phents_size = hdr.phentsize * hdr.phnum;
     size_t shents_size = hdr.shentsize * hdr.shnum;
     std::vector<types::elf::elf32_program_header_entry> phents(hdr.phnum);
-    n_read = fs::vfs_read(
-        exec->ind,
+    n_read = fs::read(
+        exec->inode,
         (char*)phents.data(),
         phents_size,
         hdr.phoff, phents_size);
@@ -64,8 +64,8 @@ int types::elf::elf32_load(types::elf::elf32_load_data& d)
         return -EINVAL;
 
     std::vector<types::elf::elf32_section_header_entry> shents(hdr.shnum);
-    n_read = fs::vfs_read(
-        exec->ind,
+    n_read = fs::read(
+        exec->inode,
         (char*)shents.data(),
         shents_size,
         hdr.shoff, shents_size);
@@ -96,7 +96,7 @@ int types::elf::elf32_load(types::elf::elf32_load_data& d)
 
             args.vaddr = vaddr;
             args.length = flen;
-            args.file_inode = exec->ind;
+            args.file_inode = exec->inode;
             args.file_offset = fileoff;
 
             args.flags = MM_MAPPED;

+ 2 - 2
user-space-program/init.c

@@ -42,10 +42,10 @@ _run_sh:;
             shell_argv[0] = "/bin/sh";
         else
             shell_argv[0] = argv[1];
-        
+
         for (int i = 2; i < argc; ++i)
             shell_argv[i - 1] = argv[i];
-        
+
         execve(shell_argv[0], shell_argv, environ);
 
         print("[init] unable to run sh, exiting...\n");