Bladeren bron

rewrite Dentry module with rust

greatbridf 2 maanden geleden
bovenliggende
commit
1d5525f5c1

+ 0 - 2
CMakeLists.txt

@@ -64,7 +64,6 @@ set(KERNEL_MAIN_SOURCES src/dev/builtin-chardev.cc
                         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/signal.cpp
                         src/net/ethernet.cc
@@ -108,7 +107,6 @@ set(KERNEL_MAIN_SOURCES src/dev/builtin-chardev.cc
                         include/types/bitmap.hpp
                         include/types/buffer.hpp
                         include/types/elf.hpp
-                        include/types/hash.hpp
                         include/types/list.hpp
                         include/types/types.h
                         include/types/allocator.hpp

+ 25 - 14
Cargo.lock

@@ -13,9 +13,9 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
 
 [[package]]
 name = "bindgen"
@@ -80,6 +80,8 @@ name = "gbos-rust-part"
 version = "0.1.0"
 dependencies = [
  "bindgen",
+ "itertools",
+ "lazy_static",
  "spin",
 ]
 
@@ -98,11 +100,20 @@ dependencies = [
  "either",
 ]
 
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+dependencies = [
+ "spin",
+]
+
 [[package]]
 name = "libc"
-version = "0.2.158"
+version = "0.2.159"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
+checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
 
 [[package]]
 name = "libloading"
@@ -164,9 +175,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.86"
+version = "1.0.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a"
 dependencies = [
  "unicode-ident",
 ]
@@ -182,9 +193,9 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.10.6"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -194,9 +205,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.7"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -205,9 +216,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.4"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
 [[package]]
 name = "rustc-hash"
@@ -238,9 +249,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.77"
+version = "2.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed"
+checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
 dependencies = [
  "proc-macro2",
  "quote",

+ 3 - 3
Cargo.toml

@@ -7,6 +7,8 @@ edition = "2021"
 crate-type = ["staticlib"]
 
 [dependencies]
+itertools = { version = "0.13.0", default-features = false }
+lazy_static = { version = "1.5.0", features = ["spin_no_std"] }
 spin = "0.9.8"
 
 [build-dependencies]
@@ -14,13 +16,11 @@ bindgen = "0.70.1"
 
 [profile.dev]
 panic = "abort"
+opt-level = 1
 
 [profile.dev.package."*"]
 opt-level = 2
 
-[profile.dev.package."gbos-rust-part"]
-opt-level = 1
-
 [profile.dev.build-override]
 opt-level = 0
 codegen-units = 256

+ 15 - 15
include/kernel/vfs.hpp

@@ -71,9 +71,7 @@ extern "C" size_t fs_read(const struct rust_inode_handle* file, char* buf,
 extern "C" size_t fs_write(const struct rust_inode_handle* file,
                            const char* buf, size_t offset, size_t n);
 
-using readdir_callback_fn =
-    std::function<int(const char*, size_t, const struct rust_inode_handle*,
-                      const struct inode_data*, uint8_t)>;
+using readdir_callback_fn = std::function<int(const char*, size_t, ino_t)>;
 
 extern "C" ssize_t fs_readdir(const struct rust_inode_handle* file,
                               size_t offset,
@@ -83,19 +81,21 @@ extern "C" int fs_mount(dentry* mnt, const char* source,
                         const char* mount_point, const char* fstype,
                         unsigned long flags, const void* data);
 
-extern "C" struct dentry* r_get_mountpoint(struct dentry* mnt);
-extern "C" mode_t r_dentry_save_inode(struct dentry* dent,
-                                      const struct rust_inode_handle* inode);
-extern "C" mode_t r_get_inode_mode(const struct rust_inode_handle* inode);
-extern "C" size_t r_get_inode_size(const struct rust_inode_handle* inode);
+extern "C" mode_t r_get_inode_mode(struct rust_inode_handle* inode);
+extern "C" size_t r_get_inode_size(struct rust_inode_handle* inode);
+extern "C" bool r_dentry_is_directory(struct dentry* dentry);
+extern "C" bool r_dentry_is_invalid(struct dentry* dentry);
+
+// borrow from dentry->inode
+extern "C" struct rust_inode_handle* r_dentry_get_inode(struct dentry* dentry);
 extern "C" struct dentry* r_get_root_dentry();
 
-#define current_open(...)                                             \
-    fs::open(current_process->fs_context, current_process->cwd.get(), \
-             __VA_ARGS__)
-std::pair<dentry_pointer, int> open(const fs_context& context, dentry* cwd,
-                                    types::path_iterator path,
-                                    bool follow_symlinks = true,
-                                    int recurs_no = 0);
+#define current_open(...) \
+    fs::open(current_process->fs_context, current_process->cwd, __VA_ARGS__)
+
+std::pair<dentry_pointer, int> open(const fs_context& context,
+                                    const dentry_pointer& cwd,
+                                    types::string_view path,
+                                    bool follow_symlinks = true);
 
 } // namespace fs

+ 4 - 73
include/kernel/vfs/dentry.hpp

@@ -4,94 +4,25 @@
 
 #include <bits/alltypes.h>
 
-#include <types/hash.hpp>
 #include <types/path.hpp>
 
 #include <kernel/async/lock.hpp>
 
+struct dentry;
+
 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;
-static constexpr unsigned long D_SYMLINK = 1 << 4;
 
 struct rust_vfs_handle {
     void* data[2];
 };
 
-struct rust_inode_handle {
-    void* data[2];
-};
-
-struct inode_data {
-    uint64_t ino;
-    uint64_t size;
-    uint64_t nlink;
-
-    struct timespec atime;
-    struct timespec mtime;
-    struct timespec ctime;
-
-    uint32_t uid;
-    uint32_t gid;
-    uint32_t mode;
-};
-
-struct dentry {
-    struct rust_vfs_handle fs;
-    struct rust_inode_handle inode;
-
-    struct dcache* cache;
-    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 dentry_deleter {
     void operator()(struct dentry* dentry) const;
 };
 
 using dentry_pointer = std::unique_ptr<struct dentry, dentry_deleter>;
-
-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);
-
+extern "C" int d_path(struct dentry* dentry, struct dentry* root,
+                      char* out_path, size_t buflen);
 dentry_pointer d_get(const dentry_pointer& dp);
-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
-
-struct rust_get_cxx_string_result {
-    const char* data;
-    size_t len;
-};
-
-void rust_get_cxx_string(const std::string* str,
-                         rust_get_cxx_string_result* out_result);
-void rust_operator_eql_cxx_string(const std::string* str, std::string* dst);

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

@@ -82,9 +82,9 @@ struct file {
 struct regular_file : public virtual file {
     virtual ~regular_file() = default;
     std::size_t cursor{};
-    const rust_inode_handle* ind{};
+    struct rust_inode_handle* ind{};
 
-    regular_file(file_flags flags, size_t cursor, const rust_inode_handle* ind);
+    regular_file(file_flags flags, size_t cursor, rust_inode_handle* 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;

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

@@ -37,9 +37,9 @@ class filearray {
     int set_flags(int fd, int flags);
 
     int pipe(int (&pipefd)[2]);
-    int open(dentry* cwd, types::path_iterator filepath, int flags,
+    int open(const dentry_pointer& cwd, types::string_view filepath, int flags,
              mode_t mode);
-    int open(types::path_iterator filepath, int flags, mode_t mode);
+    int open(types::string_view filepath, int flags, mode_t mode);
 
     int close(int fd);
 

+ 5 - 3
include/types/elf.hpp

@@ -1,11 +1,13 @@
 #pragma once
 
-#include <errno.h>
+#include <vector>
+
 #include <stdint.h>
 
 #include <kernel/interrupt.hpp>
 #include <kernel/process.hpp>
 #include <kernel/vfs.hpp>
+#include <kernel/vfs/dentry.hpp>
 
 namespace types::elf {
 
@@ -146,7 +148,7 @@ struct PACKED elf32_section_header_entry {
 };
 
 struct elf32_load_data {
-    const fs::dentry* exec_dent;
+    fs::dentry_pointer exec_dent;
     const std::vector<std::string>& argv;
     const std::vector<std::string>& envp;
     uintptr_t ip;
@@ -281,7 +283,7 @@ struct PACKED elf64_section_header_entry {
 };
 
 struct elf64_load_data {
-    const fs::dentry* exec_dent;
+    fs::dentry_pointer exec_dent;
     std::vector<std::string> argv;
     std::vector<std::string> envp;
     unsigned long ip;

+ 0 - 46
include/types/hash.hpp

@@ -1,46 +0,0 @@
-#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(const 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 - 42
include/types/path.hpp

@@ -2,7 +2,6 @@
 
 #include <cstddef>
 #include <string>
-#include <vector>
 
 namespace types {
 
@@ -64,45 +63,4 @@ class string_view {
     }
 };
 
-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} {
-        m_is_absolute = !m_all.empty() && m_all[0] == '/';
-        this->operator++();
-    }
-
-    constexpr path_iterator(const std::string& str)
-        : path_iterator{string_view{str}} {}
-    inline path_iterator(const char* str) : path_iterator{string_view{str}} {}
-
-    constexpr operator bool() const { return !m_all.empty(); }
-    constexpr bool is_absolute() const { return m_is_absolute; }
-
-    constexpr string_view operator*() const {
-        return string_view{m_all.data(), m_curlen};
-    }
-
-    constexpr path_iterator& operator++() {
-        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;
-
-        m_curlen = 0;
-        while (m_curlen < m_all.size() && m_all[m_curlen] != '/')
-            ++m_curlen;
-
-        return *this;
-    }
-};
-
 } // namespace types

+ 7 - 1
src/asm/interrupt.s

@@ -38,7 +38,9 @@
 
 ISR_stub:
 	.cfi_startproc
-	.cfi_def_cfa %rsp, 0x18
+	.cfi_signal_frame
+	.cfi_def_cfa_offset 0x18
+	.cfi_offset %rsp, 0x10
 
 	sub $0x78, %rsp
 	.cfi_def_cfa_offset 0x90
@@ -161,7 +163,9 @@ asm_ctx_switch:
 	.type  ISR\name @function
 	ISR\name:
 		.cfi_startproc
+		.cfi_signal_frame
 		.cfi_def_cfa_offset 0x08
+		.cfi_offset %rsp, 0x10
 
 		.cfi_same_value %rax
 		.cfi_same_value %rbx
@@ -193,7 +197,9 @@ asm_ctx_switch:
 	.type  ISR\name @function
 	ISR\name:
 		.cfi_startproc
+		.cfi_signal_frame
 		.cfi_def_cfa_offset 0x10
+		.cfi_offset %rsp, 0x10
 
 		.cfi_same_value %rax
 		.cfi_same_value %rbx

+ 245 - 228
src/fs/fat32.rs

@@ -1,19 +1,19 @@
-use alloc::{
-    sync::{Arc, Weak},
-    vec::Vec,
-};
+use alloc::{sync::Arc, vec::Vec};
 use bindings::{EINVAL, EIO, S_IFDIR, S_IFREG};
 
+use itertools::Itertools;
+
 use crate::{
-    io::{RawBuffer, UninitBuffer},
+    io::{Buffer, RawBuffer, UninitBuffer},
     kernel::{
         block::{make_device, BlockDevice, BlockDeviceRequest},
         mem::{paging::Page, phys::PhysPtr},
         vfs::{
-            inode::{Ino, Inode, InodeCache, InodeData},
+            dentry::Dentry,
+            inode::{Ino, Inode, InodeCache, InodeOps},
             mount::{register_filesystem, Mount, MountCreator},
             vfs::Vfs,
-            DevId, ReadDirCallback, TimeSpec,
+            DevId, ReadDirCallback,
         },
     },
     prelude::*,
@@ -49,33 +49,26 @@ struct FatDirectoryEntry {
 }
 
 impl FatDirectoryEntry {
-    pub fn filename(&self) -> KResult<String> {
-        let basename = str::from_utf8(&self.name)
-            .map_err(|_| EINVAL)?
-            .trim_end_matches(char::from(' '));
-
-        let extension = if self.extension[0] != ' ' as u8 {
-            Some(
-                str::from_utf8(&self.extension)
-                    .map_err(|_| EINVAL)?
-                    .trim_end_matches(char::from(' ')),
-            )
-        } else {
-            None
-        };
-
-        let mut name = String::from(basename);
-
-        if let Some(extension) = extension {
-            name.push('.');
-            name += extension;
+    pub fn filename(&self) -> Arc<[u8]> {
+        let fnpos = self.name.iter().position(|&c| c == ' ' as u8).unwrap_or(8);
+        let mut name = self.name[..fnpos].to_vec();
+
+        let extpos = self
+            .extension
+            .iter()
+            .position(|&c| c == ' ' as u8)
+            .unwrap_or(3);
+
+        if extpos != 0 {
+            name.push('.' as u8);
+            name.extend_from_slice(&self.extension[..extpos]);
         }
 
         if self.reserved & RESERVED_FILENAME_LOWERCASE != 0 {
             name.make_ascii_lowercase();
         }
 
-        Ok(name)
+        name.into()
     }
 
     pub fn ino(&self) -> Ino {
@@ -169,19 +162,58 @@ impl FatFs {
     }
 }
 
+impl InodeCache<FatFs> {
+    fn get_or_alloc(
+        &mut self,
+        ino: Ino,
+        is_directory: bool,
+        size: u64,
+    ) -> KResult<Arc<Inode>> {
+        self.get(ino).map(|inode| Ok(inode)).unwrap_or_else(|| {
+            let nlink;
+            let mut mode = 0o777;
+
+            let ops: Box<dyn InodeOps>;
+
+            if is_directory {
+                nlink = 2;
+                mode |= S_IFDIR;
+                ops = Box::new(DirOps);
+            } else {
+                nlink = 1;
+                mode |= S_IFREG;
+                ops = Box::new(FileOps);
+            }
+
+            let mut inode = self.alloc(ino, ops);
+            let inode_mut = unsafe { Arc::get_mut_unchecked(&mut inode) };
+            let inode_idata = inode_mut.idata.get_mut();
+
+            inode_idata.mode = mode;
+            inode_idata.nlink = nlink;
+            inode_idata.size = size;
+
+            self.submit(&inode)?;
+
+            Ok(inode)
+        })
+    }
+}
+
 impl FatFs {
-    pub fn create(
-        device: DevId,
-    ) -> KResult<(Arc<Mutex<Self>>, Arc<dyn Inode>)> {
-        let mut fatfs = Self {
-            device: BlockDevice::get(device)?,
-            icache: Mutex::new(InodeCache::new()),
+    pub fn create(device: DevId) -> KResult<(Arc<Self>, Arc<Inode>)> {
+        let device = BlockDevice::get(device)?;
+        let mut fatfs_arc = Arc::new_cyclic(|weak| Self {
+            device,
+            icache: Mutex::new(InodeCache::new(weak.clone())),
             sectors_per_cluster: 0,
             rootdir_cluster: 0,
             data_start: 0,
             fat: Mutex::new(Vec::new()),
             volume_label: String::new(),
-        };
+        });
+
+        let fatfs = unsafe { Arc::get_mut_unchecked(&mut fatfs_arc) };
 
         let mut info: UninitBuffer<Bootsector> = UninitBuffer::new();
         fatfs.device.read_some(0, &mut info)?.ok_or(EIO)?;
@@ -192,22 +224,22 @@ impl FatFs {
         fatfs.data_start = info.reserved_sectors as u64
             + info.fat_copies as u64 * info.sectors_per_fat as u64;
 
-        {
-            let mut fat = fatfs.fat.lock();
-            fat.resize(
-                512 * info.sectors_per_fat as usize
-                    / core::mem::size_of::<ClusterNo>(),
-                0,
-            );
+        let fat = fatfs.fat.get_mut();
+        fat.resize(
+            512 * info.sectors_per_fat as usize
+                / core::mem::size_of::<ClusterNo>(),
+            0,
+        );
 
-            let mut buffer = RawBuffer::new_from_slice(fat.as_mut_slice());
+        let mut buffer = RawBuffer::new_from_slice(fat.as_mut_slice());
 
-            fatfs
-                .device
-                .read_some(info.reserved_sectors as usize * 512, &mut buffer)?
-                .ok_or(EIO)?;
+        fatfs
+            .device
+            .read_some(info.reserved_sectors as usize * 512, &mut buffer)?
+            .ok_or(EIO)?;
 
-            assert!(buffer.filled());
+        if !buffer.filled() {
+            return Err(EIO);
         }
 
         fatfs.volume_label = String::from(
@@ -216,39 +248,28 @@ impl FatFs {
                 .trim_end_matches(char::from(' ')),
         );
 
-        let root_dir_cluster_count = {
-            let fat = fatfs.fat.lock();
-
-            ClusterIterator::new(&fat, fatfs.rootdir_cluster).count()
-        };
+        let root_dir_cluster_count =
+            ClusterIterator::new(&fat, fatfs.rootdir_cluster).count();
 
-        let fatfs = Arc::new(Mutex::new(fatfs));
         let root_inode = {
-            let _fatfs = fatfs.lock();
-            let mut icache = _fatfs.icache.lock();
-
-            icache.set_vfs(Arc::downgrade(&fatfs));
-            let root_inode = FatInode {
-                idata: Mutex::new(InodeData {
-                    ino: info.root_cluster as Ino,
-                    mode: S_IFDIR | 0o777,
-                    nlink: 2,
-                    size: root_dir_cluster_count as u64
-                        * info.sectors_per_cluster as u64
-                        * 512,
-                    atime: TimeSpec { sec: 0, nsec: 0 },
-                    mtime: TimeSpec { sec: 0, nsec: 0 },
-                    ctime: TimeSpec { sec: 0, nsec: 0 },
-                    uid: 0,
-                    gid: 0,
-                }),
-                vfs: Arc::downgrade(&fatfs),
-            };
-
-            icache.submit(info.root_cluster as Ino, Arc::new(root_inode))?
+            let icache = fatfs.icache.get_mut();
+
+            let mut inode =
+                icache.alloc(info.root_cluster as Ino, Box::new(DirOps));
+            let inode_mut = unsafe { Arc::get_mut_unchecked(&mut inode) };
+            let inode_idata = inode_mut.idata.get_mut();
+
+            inode_idata.mode = S_IFDIR | 0o777;
+            inode_idata.nlink = 2;
+            inode_idata.size = root_dir_cluster_count as u64
+                * info.sectors_per_cluster as u64
+                * 512;
+
+            icache.submit(&inode)?;
+            inode
         };
 
-        Ok((fatfs, root_inode))
+        Ok((fatfs_arc, root_inode))
     }
 }
 
@@ -266,23 +287,75 @@ impl Vfs for FatFs {
     }
 }
 
-struct FatInode {
-    idata: Mutex<InodeData>,
-    vfs: Weak<Mutex<FatFs>>,
-}
-
-struct ClusterIterator<'lt> {
-    fat: &'lt [ClusterNo],
+struct ClusterIterator<'fat> {
+    fat: &'fat [ClusterNo],
     cur: ClusterNo,
 }
 
-impl<'lt> ClusterIterator<'lt> {
-    fn new(fat: &'lt [ClusterNo], start: ClusterNo) -> Self {
+impl<'fat> ClusterIterator<'fat> {
+    fn new(fat: &'fat [ClusterNo], start: ClusterNo) -> Self {
         Self { fat, cur: start }
     }
+
+    fn read<'closure, 'vfs>(
+        self,
+        vfs: &'vfs FatFs,
+        offset: usize,
+    ) -> impl Iterator<Item = KResult<&'closure [u8]>> + 'closure
+    where
+        'fat: 'closure,
+        'vfs: 'closure,
+    {
+        let cluster_size = vfs.sectors_per_cluster as usize * 512;
+
+        let skip_count = offset / cluster_size;
+        let mut inner_offset = offset % cluster_size;
+
+        let page_buffer = Page::alloc_one();
+
+        self.skip(skip_count).map(move |cluster| {
+            vfs.read_cluster(cluster, &page_buffer)?;
+
+            let data = page_buffer
+                .as_cached()
+                .as_slice::<u8>(page_buffer.len())
+                .split_at(inner_offset)
+                .1;
+            inner_offset = 0;
+
+            Ok(data)
+        })
+    }
+
+    fn dirs<'closure, 'vfs>(
+        self,
+        vfs: &'vfs FatFs,
+        offset: usize,
+    ) -> impl Iterator<Item = KResult<&'closure FatDirectoryEntry>> + 'closure
+    where
+        'fat: 'closure,
+        'vfs: 'closure,
+    {
+        const ENTRY_SIZE: usize = core::mem::size_of::<FatDirectoryEntry>();
+        self.read(vfs, offset)
+            .map(|result| {
+                let data = result?;
+                if data.len() % ENTRY_SIZE != 0 {
+                    return Err(EINVAL);
+                }
+
+                Ok(unsafe {
+                    core::slice::from_raw_parts(
+                        data.as_ptr() as *const FatDirectoryEntry,
+                        data.len() / ENTRY_SIZE,
+                    )
+                })
+            })
+            .flatten_ok()
+    }
 }
 
-impl<'lt> Iterator for ClusterIterator<'lt> {
+impl<'fat> Iterator for ClusterIterator<'fat> {
     type Item = ClusterNo;
 
     fn next(&mut self) -> Option<Self::Item> {
@@ -298,179 +371,122 @@ impl<'lt> Iterator for ClusterIterator<'lt> {
     }
 }
 
-impl Inode for FatInode {
-    fn idata(&self) -> &Mutex<InodeData> {
-        &self.idata
-    }
-
+struct FileOps;
+impl InodeOps for FileOps {
     fn as_any(&self) -> &dyn Any {
         self
     }
 
-    fn read(&self, buffer: &mut [u8], offset: usize) -> KResult<usize> {
-        let vfs = self.vfs.upgrade().ok_or(EIO)?;
-        let vfs = vfs.lock();
+    fn read(
+        &self,
+        inode: &Inode,
+        buffer: &mut dyn Buffer,
+        offset: usize,
+    ) -> KResult<usize> {
+        let vfs = inode.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.as_any().downcast_ref::<FatFs>().ok_or(EINVAL)?;
         let fat = vfs.fat.lock();
 
-        let cluster_size = vfs.sectors_per_cluster as usize * 512;
+        let iter = ClusterIterator::new(&fat, inode.ino as ClusterNo)
+            .read(vfs, offset);
 
-        let buffer_len = buffer.len();
-        let skip_count = offset / cluster_size;
-        let inner_offset = offset % cluster_size;
-        let cluster_count =
-            (inner_offset + buffer.len() + cluster_size - 1) / cluster_size;
+        for data in iter {
+            if buffer.fill(data?)?.should_stop() {
+                break;
+            }
+        }
 
-        let mut cluster_iter =
-            ClusterIterator::new(&fat, self.idata.lock().ino as ClusterNo)
-                .skip(skip_count)
-                .take(cluster_count);
+        Ok(buffer.wrote())
+    }
+}
 
-        let page_buffer = Page::alloc_one();
+struct DirOps;
+impl InodeOps for DirOps {
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
 
-        let mut nread = 0;
-        if let Some(cluster) = cluster_iter.next() {
-            vfs.read_cluster(cluster, &page_buffer)?;
+    fn lookup(
+        &self,
+        dir: &Inode,
+        dentry: &Arc<Dentry>,
+    ) -> KResult<Option<Arc<Inode>>> {
+        let vfs = dir.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.as_any().downcast_ref::<FatFs>().ok_or(EINVAL)?;
+        let fat = vfs.fat.lock();
 
-            let (_, data) = page_buffer
-                .as_cached()
-                .as_slice::<u8>(page_buffer.len())
-                .split_at(inner_offset);
+        let mut entries =
+            ClusterIterator::new(&fat, dir.ino as ClusterNo).dirs(vfs, 0);
 
-            if data.len() > buffer_len - nread {
-                buffer[nread..].copy_from_slice(&data[..buffer_len - nread]);
-                return Ok(buffer_len);
-            } else {
-                buffer[nread..nread + data.len()].copy_from_slice(data);
-                nread += data.len();
+        let entry = entries.find_map(|entry| {
+            if entry.is_err() {
+                return Some(entry);
             }
-        }
-
-        for cluster in cluster_iter {
-            vfs.read_cluster(cluster, &page_buffer)?;
 
-            let data =
-                page_buffer.as_cached().as_slice::<u8>(page_buffer.len());
+            let entry = entry.unwrap();
 
-            if data.len() > buffer_len - nread {
-                buffer[nread..].copy_from_slice(&data[..buffer_len - nread]);
-                return Ok(buffer_len);
+            if !entry.is_invalid() && entry.filename().eq(dentry.name()) {
+                Some(Ok(entry))
             } else {
-                buffer[nread..nread + data.len()].copy_from_slice(data);
-                nread += data.len();
+                None
             }
-        }
+        });
+
+        match entry {
+            None => Ok(None),
+            Some(Err(err)) => Err(err),
+            Some(Ok(entry)) => {
+                let ino = entry.ino();
 
-        Ok(nread)
+                Ok(Some(vfs.icache.lock().get_or_alloc(
+                    ino,
+                    entry.is_directory(),
+                    entry.size as u64,
+                )?))
+            }
+        }
     }
 
-    fn readdir(
-        &self,
+    fn readdir<'cb, 'r: 'cb>(
+        &'r self,
+        dir: &'r Inode,
         offset: usize,
-        callback: &mut ReadDirCallback,
+        callback: &ReadDirCallback<'cb>,
     ) -> KResult<usize> {
-        let vfs = self.vfs.upgrade().ok_or(EIO)?;
-        let vfs = vfs.lock();
-
+        let vfs = dir.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.as_any().downcast_ref::<FatFs>().ok_or(EINVAL)?;
         let fat = vfs.fat.lock();
 
-        let cluster_size = vfs.sectors_per_cluster as usize * 512;
-        let skip_count = offset / cluster_size;
-        let inner_offset = offset % cluster_size;
-
+        const ENTRY_SIZE: usize = core::mem::size_of::<FatDirectoryEntry>();
         let cluster_iter =
-            ClusterIterator::new(&fat, self.idata.lock().ino as ClusterNo)
-                .skip(skip_count)
-                .enumerate();
+            ClusterIterator::new(&fat, dir.ino as ClusterNo).dirs(vfs, offset);
 
         let mut nread = 0;
-        let buffer = Page::alloc_one();
-        for (idx, cluster) in cluster_iter {
-            vfs.read_cluster(cluster, &buffer)?;
-
-            const ENTRY_SIZE: usize = core::mem::size_of::<FatDirectoryEntry>();
-            let count = cluster_size / ENTRY_SIZE;
-
-            let entries = {
-                let entries = buffer
-                    .as_cached()
-                    .as_slice::<FatDirectoryEntry>(count)
-                    .iter();
-
-                entries.skip(if idx == 0 {
-                    inner_offset / ENTRY_SIZE
-                } else {
-                    0
-                })
-            };
+        for entry in cluster_iter {
+            let entry = entry?;
 
-            for entry in entries {
-                if entry.is_invalid() {
-                    nread += ENTRY_SIZE;
-                    continue;
-                }
-
-                let ino = entry.ino();
-                let name = entry.filename()?;
-
-                let inode = {
-                    let mut icache = vfs.icache.lock();
-
-                    match icache.get(ino) {
-                        Some(inode) => inode,
-                        None => {
-                            let nlink;
-                            let mut mode = 0o777;
-
-                            if entry.is_directory() {
-                                nlink = 2;
-                                mode |= S_IFDIR;
-                            } else {
-                                nlink = 1;
-                                mode |= S_IFREG;
-                            }
-
-                            let inode = Arc::new(FatInode {
-                                idata: Mutex::new(InodeData {
-                                    ino,
-                                    mode,
-                                    nlink,
-                                    size: entry.size as u64,
-                                    atime: TimeSpec::default(),
-                                    mtime: TimeSpec::default(),
-                                    ctime: TimeSpec::default(),
-                                    uid: 0,
-                                    gid: 0,
-                                }),
-                                vfs: self.vfs.clone(),
-                            });
-
-                            icache.submit(ino, inode)?
-                        }
-                    }
-                };
-
-                if callback(name.as_str(), &inode, &inode.idata().lock(), 0)
-                    .is_err()
-                {
-                    return Ok(nread);
-                }
-
-                nread += ENTRY_SIZE;
+            if entry.is_invalid() {
+                nread += 1;
+                continue;
             }
-        }
 
-        Ok(nread)
-    }
+            let ino = entry.ino();
+            let name = entry.filename();
 
-    fn vfs_weak(&self) -> Weak<Mutex<dyn Vfs>> {
-        self.vfs.clone()
-    }
+            vfs.icache.lock().get_or_alloc(
+                ino,
+                entry.is_directory(),
+                entry.size as u64,
+            )?;
 
-    fn vfs_strong(&self) -> Option<Arc<Mutex<dyn Vfs>>> {
-        match self.vfs.upgrade() {
-            Some(vfs) => Some(vfs),
-            None => None,
+            if callback(name.as_ref(), ino).is_err() {
+                break;
+            }
+
+            nread += 1;
         }
+
+        Ok(nread * ENTRY_SIZE)
     }
 }
 
@@ -482,10 +498,11 @@ impl MountCreator for FatMountCreator {
         _source: &str,
         _flags: u64,
         _data: &[u8],
+        mp: &Arc<Dentry>,
     ) -> KResult<Mount> {
         let (fatfs, root_inode) = FatFs::create(make_device(8, 1))?;
 
-        Ok(Mount::new(fatfs, root_inode))
+        Mount::new(mp, fatfs, root_inode)
     }
 }
 

+ 166 - 194
src/fs/procfs.rs

@@ -1,20 +1,31 @@
+use core::sync::atomic::Ordering;
+
 use alloc::sync::{Arc, Weak};
-use bindings::{EACCES, EINVAL, EISDIR, ENOTDIR, S_IFDIR, S_IFREG};
+use bindings::{EACCES, ENOTDIR, S_IFDIR, S_IFREG};
 
 use crate::{
-    io::copy_offset_count,
+    io::Buffer,
     kernel::{
         mem::paging::{Page, PageBuffer},
         vfs::{
-            inode::{Ino, Inode, InodeData},
+            dentry::Dentry,
+            inode::{AtomicIno, Inode, InodeCache, InodeData, InodeOps},
             mount::{dump_mounts, register_filesystem, Mount, MountCreator},
             vfs::Vfs,
-            DevId, ReadDirCallback, TimeSpec,
+            DevId, ReadDirCallback,
         },
     },
     prelude::*,
 };
 
+fn split_len_offset(data: &[u8], len: usize, offset: usize) -> Option<&[u8]> {
+    let real_data = data.split_at_checked(len).map(|(data, _)| data)?;
+
+    real_data.split_at_checked(offset).map(|(_, data)| data)
+}
+
+pub struct ProcFsNode(Arc<Inode>);
+
 pub trait ProcFsFile: Send + Sync {
     fn can_read(&self) -> bool {
         false
@@ -25,145 +36,92 @@ pub trait ProcFsFile: Send + Sync {
     }
 
     fn read(&self, _buffer: &mut PageBuffer) -> KResult<usize> {
-        Err(EINVAL)
+        Err(EACCES)
     }
 
     fn write(&self, _buffer: &[u8]) -> KResult<usize> {
-        Err(EINVAL)
+        Err(EACCES)
     }
 }
 
-pub enum ProcFsData {
-    File(Box<dyn ProcFsFile>),
-    Directory(Mutex<Vec<Arc<ProcFsNode>>>),
+struct ProcFsFileOps {
+    file: Box<dyn ProcFsFile>,
 }
 
-pub struct ProcFsNode {
-    indata: Mutex<InodeData>,
-
-    name: String,
-    data: ProcFsData,
-}
-
-impl ProcFsNode {
-    fn new(ino: Ino, name: String, data: ProcFsData) -> Self {
-        Self {
-            indata: Mutex::new(InodeData {
-                ino,
-                mode: 0,
-                uid: 0,
-                gid: 0,
-                size: 0,
-                atime: TimeSpec { sec: 0, nsec: 0 },
-                mtime: TimeSpec { sec: 0, nsec: 0 },
-                ctime: TimeSpec { sec: 0, nsec: 0 },
-                nlink: 0,
-            }),
-            name,
-            data,
-        }
-    }
-}
-
-impl Inode for ProcFsNode {
-    fn idata(&self) -> &Mutex<InodeData> {
-        &self.indata
-    }
-
+impl InodeOps for ProcFsFileOps {
     fn as_any(&self) -> &dyn Any {
         self
     }
 
-    fn readdir(
+    fn read(
         &self,
+        _: &Inode,
+        buffer: &mut dyn Buffer,
         offset: usize,
-        callback: &mut ReadDirCallback,
     ) -> KResult<usize> {
-        match self.data {
-            ProcFsData::Directory(ref lck) => {
-                let dir = lck.lock();
-
-                let mut nread = 0;
-                for entry in dir.iter().skip(offset) {
-                    let inode: Arc<dyn Inode> = entry.clone();
-                    callback(
-                        entry.name.as_str(),
-                        &inode,
-                        &entry.indata.lock(),
-                        0,
-                    )?;
-
-                    nread += 1;
-                }
-
-                Ok(nread)
-            }
-            _ => Err(ENOTDIR),
+        if !self.file.can_read() {
+            return Err(EACCES);
         }
-    }
 
-    fn read(&self, buffer: &mut [u8], offset: usize) -> KResult<usize> {
-        match self.data {
-            ProcFsData::File(ref file) => {
-                if !file.can_read() {
-                    return Err(EACCES);
-                }
-
-                let mut page_buffer = PageBuffer::new(Page::alloc_one());
-                let nread = file.read(&mut page_buffer)?;
-
-                let data = match page_buffer.as_slice().split_at_checked(nread)
-                {
-                    None => return Ok(0),
-                    Some((data, _)) => data,
-                };
-
-                Ok(copy_offset_count(data, buffer, offset, buffer.len()))
-            }
-            _ => Err(EISDIR),
+        let mut page_buffer = PageBuffer::new(Page::alloc_one());
+        let nread = self.file.read(&mut page_buffer)?;
+
+        let data = split_len_offset(page_buffer.as_slice(), nread, offset);
+
+        match data {
+            None => Ok(0),
+            Some(data) => Ok(buffer.fill(data)?.allow_partial()),
         }
     }
+}
+
+struct ProcFsDirectory {
+    entries: Mutex<Vec<(Arc<[u8]>, ProcFsNode)>>,
+}
 
-    fn vfs_weak(&self) -> Weak<Mutex<dyn Vfs>> {
-        ProcFsMountCreator::get_weak()
+impl InodeOps for ProcFsDirectory {
+    fn as_any(&self) -> &dyn Any {
+        self
     }
 
-    fn vfs_strong(&self) -> Option<Arc<Mutex<dyn Vfs>>> {
-        Some(ProcFsMountCreator::get())
+    fn lookup(
+        &self,
+        _: &Inode,
+        dentry: &Arc<Dentry>,
+    ) -> KResult<Option<Arc<Inode>>> {
+        Ok(self.entries.lock().iter().find_map(|(name, node)| {
+            name.as_ref()
+                .eq(dentry.name().as_ref())
+                .then(|| node.0.clone())
+        }))
     }
-}
 
-pub struct ProcFs {
-    root_node: Arc<ProcFsNode>,
-    next_ino: Ino,
+    fn readdir<'cb, 'r: 'cb>(
+        &self,
+        _: &Inode,
+        offset: usize,
+        callback: &ReadDirCallback<'cb>,
+    ) -> KResult<usize> {
+        Ok(self
+            .entries
+            .lock()
+            .iter()
+            .skip(offset)
+            .take_while(|(name, ProcFsNode(inode))| {
+                callback(name, inode.ino).is_ok()
+            })
+            .count())
+    }
 }
 
-impl ProcFs {
-    pub fn create() -> Arc<Mutex<Self>> {
-        let fs = Arc::new(Mutex::new(Self {
-            root_node: Arc::new(ProcFsNode::new(
-                0,
-                String::from("[root]"),
-                ProcFsData::Directory(Mutex::new(vec![])),
-            )),
-            next_ino: 1,
-        }));
-
-        {
-            let fs = fs.lock();
-
-            let mut indata = fs.root_node.indata.lock();
-            indata.mode = S_IFDIR | 0o755;
-            indata.nlink = 1;
-        };
-
-        fs
-    }
+pub struct ProcFs {
+    root_node: Arc<Inode>,
+    next_ino: AtomicIno,
 }
 
 impl Vfs for ProcFs {
     fn io_blksize(&self) -> usize {
-        1024
+        4096
     }
 
     fn fs_devid(&self) -> DevId {
@@ -175,20 +133,22 @@ impl Vfs for ProcFs {
     }
 }
 
-static GLOBAL_PROCFS: Mutex<Option<Arc<Mutex<ProcFs>>>> = Mutex::new(None);
+static mut GLOBAL_PROCFS: Option<Arc<ProcFs>> = None;
+static mut ICACHE: Option<InodeCache<ProcFs>> = None;
+
+fn get_icache() -> &'static InodeCache<ProcFs> {
+    unsafe { ICACHE.as_ref().unwrap() }
+}
 
 struct ProcFsMountCreator;
 
 impl ProcFsMountCreator {
-    pub fn get() -> Arc<Mutex<ProcFs>> {
-        let fs = GLOBAL_PROCFS.lock();
-        fs.as_ref().unwrap().clone()
+    pub fn get() -> Arc<ProcFs> {
+        unsafe { GLOBAL_PROCFS.as_ref().cloned().unwrap() }
     }
 
-    pub fn get_weak() -> Weak<Mutex<ProcFs>> {
-        let fs = GLOBAL_PROCFS.lock();
-        fs.as_ref()
-            .map_or(Weak::new(), |refproc| Arc::downgrade(refproc))
+    pub fn get_weak() -> Weak<ProcFs> {
+        unsafe { GLOBAL_PROCFS.as_ref().map(Arc::downgrade).unwrap() }
     }
 }
 
@@ -198,98 +158,86 @@ impl MountCreator for ProcFsMountCreator {
         _source: &str,
         _flags: u64,
         _data: &[u8],
+        mp: &Arc<Dentry>,
     ) -> KResult<Mount> {
         let vfs = ProcFsMountCreator::get();
-
-        let root_inode = vfs.lock().root_node.clone();
-        Ok(Mount::new(vfs, root_inode))
+        let root_inode = vfs.root_node.clone();
+        Mount::new(mp, vfs, root_inode)
     }
 }
 
-pub fn root() -> Arc<ProcFsNode> {
+pub fn root() -> ProcFsNode {
     let vfs = ProcFsMountCreator::get();
-    let root = vfs.lock().root_node.clone();
+    let root = vfs.root_node.clone();
 
-    root
+    ProcFsNode(root)
 }
 
 pub fn creat(
     parent: &ProcFsNode,
-    name: &str,
-    data: ProcFsData,
-) -> KResult<Arc<ProcFsNode>> {
+    name: &Arc<[u8]>,
+    file: Box<dyn ProcFsFile>,
+) -> KResult<ProcFsNode> {
     let mut mode = S_IFREG;
-    match data {
-        ProcFsData::File(ref file) => {
-            if file.can_read() {
-                mode |= 0o444;
-            }
-            if file.can_write() {
-                mode |= 0o200;
-            }
-        }
-        _ => return Err(EINVAL),
+    if file.can_read() {
+        mode |= 0o444;
+    }
+    if file.can_write() {
+        mode |= 0o200;
     }
 
-    match parent.data {
-        ProcFsData::Directory(ref lck) => {
-            let ino = {
-                let fs = ProcFsMountCreator::get();
-                let mut fs = fs.lock();
-
-                let ino = fs.next_ino;
-                fs.next_ino += 1;
+    let dir = parent
+        .0
+        .ops
+        .as_any()
+        .downcast_ref::<ProcFsDirectory>()
+        .ok_or(ENOTDIR)?;
 
-                ino
-            };
+    let fs = ProcFsMountCreator::get();
+    let ino = fs.next_ino.fetch_add(1, Ordering::SeqCst);
 
-            let node = Arc::new(ProcFsNode::new(ino, String::from(name), data));
+    let inode = get_icache().alloc(ino, Box::new(ProcFsFileOps { file }));
 
-            {
-                let mut indata = node.indata.lock();
-                indata.nlink = 1;
-                indata.mode = mode;
-            }
+    inode.idata.lock().mode = mode;
+    inode.idata.lock().nlink = 1;
 
-            lck.lock().push(node.clone());
+    dir.entries
+        .lock()
+        .push((name.clone(), ProcFsNode(inode.clone())));
 
-            Ok(node.clone())
-        }
-        _ => Err(ENOTDIR),
-    }
+    Ok(ProcFsNode(inode))
 }
 
-pub fn mkdir(parent: &mut ProcFsNode, name: &str) -> KResult<Arc<ProcFsNode>> {
-    match parent.data {
-        ProcFsData::Directory(ref lck) => {
-            let ino = {
-                let fs = ProcFsMountCreator::get();
-                let mut fs = fs.lock();
-
-                let ino = fs.next_ino;
-                fs.next_ino += 1;
-
-                ino
-            };
+pub fn mkdir(parent: &ProcFsNode, name: &[u8]) -> KResult<ProcFsNode> {
+    let dir = parent
+        .0
+        .ops
+        .as_any()
+        .downcast_ref::<ProcFsDirectory>()
+        .ok_or(ENOTDIR)?;
+
+    let ino = ProcFsMountCreator::get()
+        .next_ino
+        .fetch_add(1, Ordering::SeqCst);
+
+    let inode = get_icache().alloc(
+        ino,
+        Box::new(ProcFsDirectory {
+            entries: Mutex::new(vec![]),
+        }),
+    );
 
-            let node = Arc::new(ProcFsNode::new(
-                ino,
-                String::from(name),
-                ProcFsData::Directory(Mutex::new(vec![])),
-            ));
-
-            {
-                let mut indata = node.indata.lock();
-                indata.nlink = 2;
-                indata.mode = S_IFDIR | 0o755;
-            }
+    {
+        let mut idata = inode.idata.lock();
+        idata.nlink = 2;
+        idata.mode = S_IFDIR | 0o755;
+    }
 
-            lck.lock().push(node.clone());
+    dir.entries
+        .lock()
+        .push((Arc::from(name), ProcFsNode(inode.clone())));
 
-            Ok(node.clone())
-        }
-        _ => Err(ENOTDIR),
-    }
+    Ok(ProcFsNode(inode))
 }
 
 struct DumpMountsFile {}
@@ -306,10 +254,34 @@ impl ProcFsFile for DumpMountsFile {
 }
 
 pub fn init() {
+    let dir = ProcFsDirectory {
+        entries: Mutex::new(vec![]),
+    };
+
+    let fs: Arc<ProcFs> = Arc::new_cyclic(|weak: &Weak<ProcFs>| {
+        let root_node = Arc::new(Inode {
+            ino: 0,
+            vfs: weak.clone(),
+            idata: Mutex::new(InodeData::default()),
+            ops: Box::new(dir),
+        });
+
+        ProcFs {
+            root_node,
+            next_ino: AtomicIno::new(1),
+        }
+    });
+
     {
-        let mut vfs = GLOBAL_PROCFS.lock();
-        *vfs = Some(ProcFs::create());
-    }
+        let mut indata = fs.root_node.idata.lock();
+        indata.mode = S_IFDIR | 0o755;
+        indata.nlink = 1;
+    };
+
+    unsafe {
+        GLOBAL_PROCFS = Some(fs);
+        ICACHE = Some(InodeCache::new(ProcFsMountCreator::get_weak()));
+    };
 
     register_filesystem("procfs", Box::new(ProcFsMountCreator)).unwrap();
 
@@ -317,8 +289,8 @@ pub fn init() {
 
     creat(
         &root,
-        "mounts",
-        ProcFsData::File(Box::new(DumpMountsFile {})),
+        &Arc::from(b"mounts".as_slice()),
+        Box::new(DumpMountsFile {}),
     )
     .unwrap();
 }

+ 227 - 467
src/fs/tmpfs.rs

@@ -1,597 +1,356 @@
+use core::sync::atomic::Ordering;
+
 use crate::{
-    io::copy_offset_count,
+    io::Buffer,
     kernel::vfs::{
         dentry::Dentry,
-        inode::{Ino, Inode, InodeCache, InodeData, Mode},
+        inode::{AtomicIno, Ino, Inode, InodeCache, InodeOps, Mode},
         mount::{register_filesystem, Mount, MountCreator, MS_RDONLY},
         s_isblk, s_ischr,
         vfs::Vfs,
-        DevId, ReadDirCallback, TimeSpec,
+        DevId, ReadDirCallback,
     },
     prelude::*,
 };
 
-use alloc::sync::{Arc, Weak};
+use alloc::sync::Arc;
 
 use bindings::{
-    fs::{D_DIRECTORY, D_LOADED, D_PRESENT, D_SYMLINK},
-    EINVAL, EIO, EISDIR, ENODEV, ENOTDIR, EROFS, S_IFBLK, S_IFCHR, S_IFDIR,
-    S_IFLNK, S_IFREG,
+    EINVAL, EIO, EISDIR, EROFS, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFREG,
 };
 
-type TmpFsFile = Vec<u8>;
-type TmpFsDirectory = Vec<(Ino, String)>;
+struct FileOps {
+    data: Mutex<Vec<u8>>,
+}
 
-enum TmpFsData {
-    File(TmpFsFile),
-    Device(DevId),
-    Directory(TmpFsDirectory),
-    Symlink(String),
+struct NodeOps {
+    devid: DevId,
 }
 
-struct TmpFsInode {
-    idata: Mutex<InodeData>,
-    fsdata: Mutex<TmpFsData>,
-    vfs: Weak<Mutex<TmpFs>>,
+impl NodeOps {
+    fn new(devid: DevId) -> Self {
+        Self { devid }
+    }
 }
 
-impl TmpFsInode {
-    pub fn new(
-        idata: InodeData,
-        fsdata: TmpFsData,
-        vfs: Weak<Mutex<TmpFs>>,
-    ) -> Arc<Self> {
-        Arc::new(Self {
-            idata: Mutex::new(idata),
-            fsdata: Mutex::new(fsdata),
-            vfs,
-        })
+impl InodeOps for NodeOps {
+    fn as_any(&self) -> &dyn Any {
+        self
     }
 
-    fn vfs(&self) -> KResult<Arc<Mutex<TmpFs>>> {
-        self.vfs.upgrade().ok_or(EIO)
+    fn devid(&self, _: &Inode) -> KResult<DevId> {
+        Ok(self.devid)
     }
+}
 
-    /// Link a child inode to the parent inode
-    ///
-    /// # Safety
-    /// If parent is not a directory, this function will panic
-    ///
-    fn link_unchecked(
-        parent_fsdata: &mut TmpFsData,
-        parent_idata: &mut InodeData,
-        name: &str,
-        child_idata: &mut InodeData,
-    ) {
-        match parent_fsdata {
-            TmpFsData::Directory(dir) => {
-                dir.push((child_idata.ino, String::from(name)));
-
-                parent_idata.size += size_of::<TmpFsData>() as u64;
-                child_idata.nlink += 1;
-            }
+struct DirectoryOps {
+    entries: Mutex<Vec<(Arc<[u8]>, Ino)>>,
+}
 
-            _ => panic!("Parent is not a directory"),
+impl DirectoryOps {
+    fn new() -> Self {
+        Self {
+            entries: Mutex::new(vec![]),
         }
     }
 
-    /// Link a inode to itself
-    ///
-    /// # Safety
-    /// If the inode is not a directory, this function will panic
-    ///
-    fn self_link_unchecked(
-        fsdata: &mut TmpFsData,
-        idata: &mut InodeData,
-        name: &str,
-    ) {
-        match fsdata {
-            TmpFsData::Directory(dir) => {
-                dir.push((idata.ino, String::from(name)));
-
-                idata.size += size_of::<TmpFsData>() as u64;
-                idata.nlink += 1;
-            }
+    /// Locks the `inode.idata`
+    fn link(&self, dir: &Inode, file: &Inode, name: Arc<[u8]>) -> KResult<()> {
+        dir.idata.lock().size += 1;
+        self.entries.lock().push((name, file.ino));
 
-            _ => panic!("parent is not a directory"),
-        }
-    }
-}
+        file.idata.lock().nlink += 1;
 
-impl Inode for TmpFsInode {
-    fn idata(&self) -> &Mutex<InodeData> {
-        &self.idata
+        Ok(())
     }
+}
 
+impl InodeOps for DirectoryOps {
     fn as_any(&self) -> &dyn Any {
         self
     }
 
-    fn readdir(
+    fn readdir<'cb, 'r: 'cb>(
         &self,
+        _: &Inode,
         offset: usize,
-        callback: &mut ReadDirCallback,
+        callback: &ReadDirCallback<'cb>,
     ) -> KResult<usize> {
-        let _vfs = self.vfs.upgrade().ok_or(EIO)?;
-        let vfs = _vfs.lock();
-
-        match *self.fsdata.lock() {
-            TmpFsData::Directory(ref dir) => {
-                let icache = vfs.icache.lock();
-
-                let mut nread = 0;
-
-                for (ino, filename) in dir.iter().skip(offset) {
-                    let inode = icache.get(*ino).unwrap();
-
-                    let ret =
-                        callback(filename, &inode, &inode.idata().lock(), 0)?;
-                    if ret != 0 {
-                        break;
-                    }
-
-                    nread += 1;
-                }
-
-                Ok(nread)
-            }
-
-            _ => Err(ENOTDIR),
-        }
+        Ok(self
+            .entries
+            .lock()
+            .iter()
+            .skip(offset)
+            .take_while(|(name, ino)| callback(name, *ino).is_ok())
+            .count())
     }
 
-    fn read(&self, buffer: &mut [u8], offset: usize) -> KResult<usize> {
-        self.vfs()?;
-
-        match *self.fsdata.lock() {
-            TmpFsData::File(ref file) => Ok(copy_offset_count(
-                file,
-                buffer,
-                offset as usize,
-                buffer.len(),
-            )),
+    fn creat(&self, dir: &Inode, at: &Arc<Dentry>, mode: Mode) -> KResult<()> {
+        let vfs = dir.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.as_any().downcast_ref::<TmpFs>().unwrap();
 
-            _ => Err(EINVAL),
-        }
-    }
-
-    fn write(&self, buffer: &[u8], offset: usize) -> KResult<usize> {
-        if self.vfs()?.lock().readonly {
+        if vfs.readonly {
             return Err(EROFS);
         }
 
-        match *self.fsdata.lock() {
-            TmpFsData::File(ref mut file) => {
-                if file.len() < offset + buffer.len() {
-                    file.resize(offset + buffer.len(), 0);
-                }
-
-                file[offset..offset + buffer.len()].copy_from_slice(&buffer);
-
-                self.idata.lock().size = file.len() as u64;
-
-                Ok(buffer.len())
-            }
+        let ino = vfs.assign_ino();
+        let file = vfs.icache.lock().alloc_file(ino, mode)?;
 
-            _ => Err(EINVAL),
-        }
+        self.link(dir, file.as_ref(), at.name().clone())?;
+        at.save_reg(file)
     }
 
-    fn creat(&self, at: &mut Dentry, mode: Mode) -> KResult<()> {
-        let _vfs = self.vfs()?;
-        let mut vfs = _vfs.lock();
+    fn mknod(
+        &self,
+        dir: &Inode,
+        at: &Arc<Dentry>,
+        mode: Mode,
+        dev: DevId,
+    ) -> KResult<()> {
+        let vfs = dir.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.as_any().downcast_ref::<TmpFs>().unwrap();
+
         if vfs.readonly {
             return Err(EROFS);
         }
 
-        {
-            let self_fsdata = self.fsdata.lock();
-            match *self_fsdata {
-                TmpFsData::Directory(_) => {}
-                _ => return Err(ENOTDIR),
-            }
+        if !s_ischr(mode) && !s_isblk(mode) {
+            return Err(EINVAL);
         }
 
         let ino = vfs.assign_ino();
+        let mut icache = vfs.icache.lock();
+        let file = icache.alloc(ino, Box::new(NodeOps::new(dev)));
+        file.idata.lock().mode = mode & (0o777 | S_IFBLK | S_IFCHR);
+        icache.submit(&file)?;
 
-        let file = {
-            let mut locked_icache = vfs.icache.lock();
-            let file = TmpFsInode::new(
-                InodeData {
-                    ino,
-                    nlink: 0,
-                    size: 0,
-                    mode: S_IFREG | (mode & 0o777),
-                    atime: TimeSpec::new(),
-                    mtime: TimeSpec::new(),
-                    ctime: TimeSpec::new(),
-                    uid: 0,
-                    gid: 0,
-                },
-                TmpFsData::File(vec![]),
-                locked_icache.get_vfs(),
-            );
-
-            locked_icache.submit(ino, file.clone())?;
-
-            file
-        };
-
-        {
-            let mut self_fsdata = self.fsdata.lock();
-            let mut self_idata = self.idata.lock();
-            let mut child_idata = file.idata.lock();
-
-            TmpFsInode::link_unchecked(
-                &mut self_fsdata,
-                &mut self_idata,
-                at.get_name(),
-                &mut child_idata,
-            );
-        }
-
-        at.save_inode(file);
-        at.flags |= D_PRESENT;
-
-        Ok(())
+        self.link(dir, file.as_ref(), at.name().clone())?;
+        at.save_reg(file)
     }
 
-    fn mknod(&self, at: &mut Dentry, mode: Mode, dev: DevId) -> KResult<()> {
-        let _vfs = self.vfs()?;
-        let mut vfs = _vfs.lock();
+    fn symlink(
+        &self,
+        dir: &Inode,
+        at: &Arc<Dentry>,
+        target: &[u8],
+    ) -> KResult<()> {
+        let vfs = dir.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.as_any().downcast_ref::<TmpFs>().unwrap();
+
         if vfs.readonly {
             return Err(EROFS);
         }
 
-        if !s_ischr(mode) && !s_isblk(mode) {
-            return Err(EINVAL);
-        }
-
-        {
-            let self_fsdata = self.fsdata.lock();
-
-            match *self_fsdata {
-                TmpFsData::Directory(_) => {}
-                _ => return Err(ENOTDIR),
-            }
-        }
-
         let ino = vfs.assign_ino();
+        let mut icache = vfs.icache.lock();
 
-        let file = {
-            let mut locked_icache = vfs.icache.lock();
-            let file = TmpFsInode::new(
-                InodeData {
-                    ino,
-                    nlink: 0,
-                    size: 0,
-                    mode: mode & (0o777 | S_IFBLK | S_IFCHR),
-                    atime: TimeSpec::new(),
-                    mtime: TimeSpec::new(),
-                    ctime: TimeSpec::new(),
-                    uid: 0,
-                    gid: 0,
-                },
-                TmpFsData::Device(dev),
-                locked_icache.get_vfs(),
-            );
-
-            locked_icache.submit(ino, file.clone())?;
-
-            file
-        };
+        let target_len = target.len() as u64;
 
+        let file =
+            icache.alloc(ino, Box::new(SymlinkOps::new(Arc::from(target))));
         {
-            let mut self_fsdata = self.fsdata.lock();
-            let mut self_idata = self.idata.lock();
-            let mut child_idata = file.idata.lock();
-
-            TmpFsInode::link_unchecked(
-                &mut self_fsdata,
-                &mut self_idata,
-                at.get_name(),
-                &mut child_idata,
-            );
+            let mut idata = file.idata.lock();
+            idata.mode = S_IFLNK | 0o777;
+            idata.size = target_len;
         }
+        icache.submit(&file)?;
 
-        at.save_inode(file);
-        at.flags |= D_PRESENT;
-
-        Ok(())
+        self.link(dir, file.as_ref(), at.name().clone())?;
+        at.save_symlink(file)
     }
 
-    fn mkdir(&self, at: &mut Dentry, mode: Mode) -> KResult<()> {
-        let _vfs = self.vfs()?;
-        let mut vfs = _vfs.lock();
+    fn mkdir(&self, dir: &Inode, at: &Arc<Dentry>, mode: Mode) -> KResult<()> {
+        let vfs = dir.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.as_any().downcast_ref::<TmpFs>().unwrap();
+
         if vfs.readonly {
             return Err(EROFS);
         }
 
-        {
-            let self_fsdata = self.fsdata.lock();
-
-            match *self_fsdata {
-                TmpFsData::Directory(_) => {}
-                _ => return Err(ENOTDIR),
-            }
-        }
-
         let ino = vfs.assign_ino();
+        let mut icache = vfs.icache.lock();
 
-        let dir = {
-            let mut locked_icache = vfs.icache.lock();
-            let file = TmpFsInode::new(
-                InodeData {
-                    ino,
-                    nlink: 0,
-                    size: 0,
-                    mode: S_IFDIR | (mode & 0o777),
-                    atime: TimeSpec::new(),
-                    mtime: TimeSpec::new(),
-                    ctime: TimeSpec::new(),
-                    uid: 0,
-                    gid: 0,
-                },
-                TmpFsData::Directory(vec![]),
-                locked_icache.get_vfs(),
-            );
-
-            locked_icache.submit(ino, file.clone())?;
-
-            file
-        };
+        let mut newdir_ops = DirectoryOps::new();
+        let entries = newdir_ops.entries.get_mut();
+        entries.push((Arc::from(b".".as_slice()), ino));
+        entries.push((Arc::from(b"..".as_slice()), dir.ino));
 
+        let newdir = icache.alloc(ino, Box::new(newdir_ops));
         {
-            let mut self_fsdata = self.fsdata.lock();
-            let mut self_idata = self.idata.lock();
-            let mut child_fsdata = dir.fsdata.lock();
-            let mut child_idata = dir.idata.lock();
-
-            TmpFsInode::link_unchecked(
-                &mut child_fsdata,
-                &mut child_idata,
-                "..",
-                &mut self_idata,
-            );
-
-            TmpFsInode::self_link_unchecked(
-                &mut child_fsdata,
-                &mut child_idata,
-                ".",
-            );
-
-            TmpFsInode::link_unchecked(
-                &mut self_fsdata,
-                &mut self_idata,
-                at.get_name(),
-                &mut child_idata,
-            );
+            let mut newdir_idata = newdir.idata.lock();
+            newdir_idata.mode = S_IFDIR | (mode & 0o777);
+            newdir_idata.nlink = 1;
+            newdir_idata.size = 2;
         }
 
-        at.save_inode(dir);
-        // TODO: try remove D_LOADED and check if it works
-        at.flags |= D_PRESENT | D_DIRECTORY | D_LOADED;
+        icache.submit(&newdir)?;
+        dir.idata.lock().nlink += 1; // link from `newdir` to `dir`, (or parent)
 
-        Ok(())
+        self.link(dir, newdir.as_ref(), at.name().clone())?;
+        at.save_dir(newdir)
     }
 
-    fn symlink(&self, at: &mut Dentry, target: &str) -> KResult<()> {
-        let _vfs = self.vfs()?;
-        let mut vfs = _vfs.lock();
+    fn unlink(&self, dir: &Inode, at: &Arc<Dentry>) -> KResult<()> {
+        let vfs = dir.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.as_any().downcast_ref::<TmpFs>().unwrap();
+
         if vfs.readonly {
             return Err(EROFS);
         }
 
-        {
-            let self_fsdata = self.fsdata.lock();
+        let file = at.get_inode()?;
 
-            match *self_fsdata {
-                TmpFsData::Directory(_) => {}
-                _ => return Err(ENOTDIR),
-            }
-        }
+        let mut file_idata = file.idata.lock();
 
-        let ino = vfs.assign_ino();
+        if file_idata.mode & S_IFDIR != 0 {
+            return Err(EISDIR);
+        }
 
-        let file = {
-            let mut locked_icache = vfs.icache.lock();
-            let file = TmpFsInode::new(
-                InodeData {
-                    ino,
-                    nlink: 0,
-                    size: target.len() as u64,
-                    mode: S_IFLNK | 0o777,
-                    atime: TimeSpec::new(),
-                    mtime: TimeSpec::new(),
-                    ctime: TimeSpec::new(),
-                    uid: 0,
-                    gid: 0,
-                },
-                TmpFsData::Symlink(String::from(target)),
-                locked_icache.get_vfs(),
-            );
-
-            locked_icache.submit(ino, file.clone())?;
-
-            file
-        };
+        let mut self_idata = dir.idata.lock();
+        let mut entries = self.entries.lock();
 
-        {
-            let mut self_fsdata = self.fsdata.lock();
-            let mut self_idata = self.idata.lock();
-            let mut child_idata = file.idata.lock();
-
-            TmpFsInode::link_unchecked(
-                &mut self_fsdata,
-                &mut self_idata,
-                at.get_name(),
-                &mut child_idata,
-            );
-        }
+        let idx = entries
+            .iter()
+            .position(|(_, ino)| *ino == file.ino)
+            .expect("file not found in directory");
 
-        at.save_inode(file);
-        at.flags |= D_PRESENT | D_SYMLINK;
+        self_idata.size -= 1;
+        file_idata.nlink -= 1;
+        entries.remove(idx);
 
-        Ok(())
+        at.invalidate()
     }
+}
 
-    fn readlink(&self, buffer: &mut [u8]) -> KResult<usize> {
-        match *self.fsdata.lock() {
-            TmpFsData::Symlink(ref target) => {
-                let len = target.len().min(buffer.len());
-
-                buffer[..len].copy_from_slice(target.as_bytes());
+struct SymlinkOps {
+    target: Arc<[u8]>,
+}
 
-                Ok(len)
-            }
+impl SymlinkOps {
+    fn new(target: Arc<[u8]>) -> Self {
+        Self { target }
+    }
+}
 
-            _ => Err(EINVAL),
-        }
+impl InodeOps for SymlinkOps {
+    fn as_any(&self) -> &dyn Any {
+        self
     }
 
-    fn devid(&self) -> KResult<DevId> {
-        match *self.fsdata.lock() {
-            TmpFsData::Device(dev) => Ok(dev),
-            _ => Err(ENODEV),
-        }
+    fn readlink(&self, _: &Inode, buffer: &mut dyn Buffer) -> KResult<usize> {
+        buffer
+            .fill(self.target.as_ref())
+            .map(|result| result.allow_partial())
     }
+}
 
-    fn truncate(&self, length: usize) -> KResult<()> {
-        if self.vfs()?.lock().readonly {
-            return Err(EROFS);
+impl FileOps {
+    fn new() -> Self {
+        Self {
+            data: Mutex::new(vec![]),
         }
+    }
+}
 
-        match *self.fsdata.lock() {
-            TmpFsData::File(ref mut file) => {
-                file.resize(length, 0);
-                self.idata.lock().size = length as u64;
+impl InodeOps for FileOps {
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
 
-                Ok(())
-            }
+    fn read(
+        &self,
+        _: &Inode,
+        buffer: &mut dyn Buffer,
+        offset: usize,
+    ) -> KResult<usize> {
+        let data = self.data.lock();
+        let data = data.split_at_checked(offset).ok_or(EINVAL)?.1;
 
-            _ => Err(EINVAL),
-        }
+        buffer.fill(data).map(|result| result.allow_partial())
     }
 
-    fn unlink(&self, at: &mut Dentry) -> KResult<()> {
-        if self.vfs()?.lock().readonly {
-            return Err(EROFS);
+    fn write(
+        &self,
+        inode: &Inode,
+        buffer: &[u8],
+        offset: usize,
+    ) -> KResult<usize> {
+        let mut idata = inode.idata.lock();
+        let mut data = self.data.lock();
+
+        if data.len() < offset + buffer.len() {
+            data.resize(offset + buffer.len(), 0);
         }
 
-        let file = at.get_inode_clone();
-        let file = file.as_any().downcast_ref::<TmpFsInode>().unwrap();
+        data[offset..offset + buffer.len()].copy_from_slice(&buffer);
+        idata.size = data.len() as u64;
 
-        match *file.fsdata.lock() {
-            TmpFsData::Directory(_) => return Err(EISDIR),
-            _ => {}
-        }
-        let file_data = file.idata.lock();
-
-        let mut self_fsdata = self.fsdata.lock();
-
-        match *self_fsdata {
-            TmpFsData::Directory(ref mut dirs) => {
-                let idx = 'label: {
-                    for (idx, (ino, _)) in dirs.iter().enumerate() {
-                        if *ino != file_data.ino {
-                            continue;
-                        }
-                        break 'label idx;
-                    }
-                    panic!("file not found in directory");
-                };
-
-                drop(file_data);
-                {
-                    self.idata.lock().size -= size_of::<TmpFsData>() as u64;
-                    file.idata.lock().nlink -= 1;
-                }
-                dirs.remove(idx);
-
-                // TODO!!!: CHANGE THIS SINCE IT WILL CAUSE MEMORY LEAK
-                // AND WILL CREATE A RACE CONDITION
-                at.flags &= !D_PRESENT;
-                at.take_inode();
-                at.take_fs();
-
-                Ok(())
-            }
-            _ => return Err(ENOTDIR),
-        }
+        Ok(buffer.len())
     }
 
-    fn vfs_weak(&self) -> Weak<Mutex<dyn Vfs>> {
-        self.vfs.clone()
-    }
+    fn truncate(&self, inode: &Inode, length: usize) -> KResult<()> {
+        let mut idata = inode.idata.lock();
 
-    fn vfs_strong(&self) -> Option<Arc<Mutex<dyn Vfs>>> {
-        match self.vfs.upgrade() {
-            Some(vfs) => Some(vfs),
-            None => None,
-        }
+        idata.size = length as u64;
+        self.data.lock().resize(length, 0);
+
+        Ok(())
     }
 }
 
 /// # Lock order
-/// vfs -> icache -> fsdata -> data
+/// `vfs` -> `icache` -> `idata` -> `*ops`.`*data`
 struct TmpFs {
     icache: Mutex<InodeCache<TmpFs>>,
-    next_ino: Ino,
+    next_ino: AtomicIno,
     readonly: bool,
 }
 
-impl TmpFs {
-    fn assign_ino(&mut self) -> Ino {
-        let ino = self.next_ino;
-        self.next_ino += 1;
+impl InodeCache<TmpFs> {
+    fn alloc_file(&mut self, ino: Ino, mode: Mode) -> KResult<Arc<Inode>> {
+        let file = self.alloc(ino, Box::new(FileOps::new()));
+        file.idata.lock().mode = S_IFREG | (mode & 0o777);
+
+        self.submit(&file)?;
 
-        ino
+        Ok(file)
     }
+}
 
-    pub fn create(
-        readonly: bool,
-    ) -> KResult<(Arc<Mutex<TmpFs>>, Arc<TmpFsInode>)> {
-        let tmpfs = Arc::new(Mutex::new(Self {
-            icache: Mutex::new(InodeCache::new()),
-            next_ino: 1,
+impl TmpFs {
+    fn assign_ino(&self) -> Ino {
+        self.next_ino.fetch_add(1, Ordering::SeqCst)
+    }
+
+    pub fn create(readonly: bool) -> KResult<(Arc<TmpFs>, Arc<Inode>)> {
+        let tmpfs = Arc::new_cyclic(|weak| Self {
+            icache: Mutex::new(InodeCache::new(weak.clone())),
+            next_ino: AtomicIno::new(1),
             readonly,
-        }));
-
-        let root_inode = {
-            let locked_tmpfs = tmpfs.lock();
-            let mut locked_icache = locked_tmpfs.icache.lock();
-            locked_icache.set_vfs(Arc::downgrade(&tmpfs));
-
-            let file = TmpFsInode::new(
-                InodeData {
-                    ino: 0,
-                    nlink: 0,
-                    size: 0,
-                    mode: S_IFDIR | 0o755,
-                    atime: TimeSpec::new(),
-                    mtime: TimeSpec::new(),
-                    ctime: TimeSpec::new(),
-                    uid: 0,
-                    gid: 0,
-                },
-                TmpFsData::Directory(vec![]),
-                locked_icache.get_vfs(),
-            );
-
-            locked_icache.submit(0, file.clone())?;
-
-            file
-        };
+        });
+
+        let mut dir = DirectoryOps::new();
+        let entries = dir.entries.get_mut();
+        entries.push((Arc::from(b".".as_slice()), 0));
+        entries.push((Arc::from(b"..".as_slice()), 0));
+
+        let root_dir = {
+            let mut icache = tmpfs.icache.lock();
+            let root_dir = icache.alloc(0, Box::new(dir));
+            {
+                let mut idata = root_dir.idata.lock();
+
+                idata.mode = S_IFDIR | 0o755;
+                idata.nlink = 2;
+                idata.size = 2;
+            }
 
-        {
-            let mut fsdata = root_inode.fsdata.lock();
-            let mut idata = root_inode.idata.lock();
+            icache.submit(&root_dir)?;
 
-            TmpFsInode::self_link_unchecked(&mut fsdata, &mut idata, ".");
-            TmpFsInode::self_link_unchecked(&mut fsdata, &mut idata, "..");
-        }
+            root_dir
+        };
 
-        Ok((tmpfs, root_inode))
+        Ok((tmpfs, root_dir))
     }
 }
 
@@ -617,10 +376,11 @@ impl MountCreator for TmpFsMountCreator {
         _source: &str,
         flags: u64,
         _data: &[u8],
+        mp: &Arc<Dentry>,
     ) -> KResult<Mount> {
         let (fs, root_inode) = TmpFs::create(flags & MS_RDONLY != 0)?;
 
-        Ok(Mount::new(fs, root_inode))
+        Mount::new(mp, fs, root_inode)
     }
 }
 

+ 19 - 0
src/hash.rs

@@ -0,0 +1,19 @@
+use core::hash::Hasher;
+
+#[derive(Default)]
+pub struct KernelHasher {
+    cur: u64,
+}
+
+impl Hasher for KernelHasher {
+    fn finish(&self) -> u64 {
+        self.cur
+    }
+
+    fn write(&mut self, bytes: &[u8]) {
+        const SEED: u64 = 131;
+        for &byte in bytes {
+            self.cur = self.cur.wrapping_mul(SEED).wrapping_add(byte as u64)
+        }
+    }
+}

+ 31 - 33
src/io.rs

@@ -2,7 +2,7 @@ use bindings::EFAULT;
 
 use crate::prelude::*;
 
-use core::{ffi::c_char, fmt::Write, mem::MaybeUninit, pin::Pin};
+use core::{ffi::c_char, fmt::Write, mem::MaybeUninit};
 
 pub enum FillResult {
     Done(usize),
@@ -18,6 +18,10 @@ impl FillResult {
         }
     }
 
+    pub fn should_stop(self) -> bool {
+        return !matches!(self, FillResult::Done(_));
+    }
+
     pub fn allow_partial(self) -> usize {
         match self {
             FillResult::Done(n) | FillResult::Partial(n) => n,
@@ -28,6 +32,7 @@ impl FillResult {
 
 pub trait Buffer {
     fn total(&self) -> usize;
+    fn wrote(&self) -> usize;
     fn fill(&mut self, data: &[u8]) -> KResult<FillResult>;
 }
 
@@ -66,6 +71,10 @@ impl<'lt, T: Copy + Sized> Buffer for UninitBuffer<'lt, T> {
         self.buffer.total()
     }
 
+    fn wrote(&self) -> usize {
+        self.buffer.wrote()
+    }
+
     fn fill(&mut self, data: &[u8]) -> KResult<FillResult> {
         self.buffer.fill(data)
     }
@@ -97,6 +106,15 @@ impl<'lt> RawBuffer<'lt> {
         }
     }
 
+    pub fn new_from_raw(buf: &'lt mut *mut u8, tot: usize) -> Self {
+        Self {
+            buf: *buf,
+            tot,
+            cur: 0,
+            _phantom: core::marker::PhantomData,
+        }
+    }
+
     pub fn count(&self) -> usize {
         self.cur
     }
@@ -147,6 +165,10 @@ impl Buffer for RawBuffer<'_> {
         RawBuffer::total(self)
     }
 
+    fn wrote(&self) -> usize {
+        self.count()
+    }
+
     fn fill(&mut self, data: &[u8]) -> KResult<FillResult> {
         RawBuffer::fill(self, data)
     }
@@ -165,6 +187,10 @@ impl<'lt> ByteBuffer<'lt> {
     pub fn available(&self) -> usize {
         self.buf.len() - self.cur
     }
+
+    pub fn data(&self) -> &[u8] {
+        &self.buf[..self.cur]
+    }
 }
 
 impl Buffer for ByteBuffer<'_> {
@@ -187,6 +213,10 @@ impl Buffer for ByteBuffer<'_> {
             }
         }
     }
+
+    fn wrote(&self) -> usize {
+        self.cur
+    }
 }
 
 impl Write for RawBuffer<'_> {
@@ -207,38 +237,6 @@ pub fn get_str_from_cstr<'a>(cstr: *const c_char) -> KResult<&'a str> {
     cstr.to_str().map_err(|_| EFAULT)
 }
 
-pub fn get_cxx_std_string<'a>(
-    cxx_string: &'a bindings::std::string,
-) -> KResult<&'a str> {
-    let arr: &'a [u8] = unsafe {
-        let mut result = bindings::rust_get_cxx_string_result {
-            data: core::ptr::null(),
-            len: 0,
-        };
-
-        bindings::rust_get_cxx_string(
-            cxx_string.as_ptr() as _,
-            &raw mut result,
-        );
-
-        core::slice::from_raw_parts(result.data as *const u8, result.len)
-    };
-
-    core::str::from_utf8(arr).map_err(|_| EFAULT)
-}
-
-pub fn operator_eql_cxx_std_string(
-    lhs: &mut bindings::std::string,
-    rhs: &bindings::std::string,
-) {
-    unsafe {
-        bindings::rust_operator_eql_cxx_string(
-            rhs.as_ptr() as _,
-            lhs.as_ptr() as _,
-        )
-    };
-}
-
 /// Copy data from src to dst, starting from offset, and copy at most count bytes.
 ///
 /// # Return

+ 6 - 2
src/kernel.ld

@@ -145,7 +145,7 @@ SECTIONS
         AT(LOADADDR(.sentry) + SIZEOF(.sentry))
     {
         __eh_frame_start = .;
-        *(.eh_frame*)
+        KEEP(*(.eh_frame*))
         . = ALIGN(0x1000);
         __eh_frame_end = .;
     } > KIMAGE
@@ -171,7 +171,7 @@ SECTIONS
     .debug_aranges  0 : { *(.debug_aranges) }
     .debug_pubnames 0 : { *(.debug_pubnames) }
     /* DWARF 2 */
-    .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+    .debug_info     0 : { *(.debug_info) }
     .debug_abbrev   0 : { *(.debug_abbrev) }
     .debug_line     0 : { *(.debug_line) }
     .debug_frame    0 : { *(.debug_frame) }
@@ -183,6 +183,10 @@ SECTIONS
     .debug_funcnames 0 : { *(.debug_funcnames) }
     .debug_typenames 0 : { *(.debug_typenames) }
     .debug_varnames  0 : { *(.debug_varnames) }
+
+    /* DWARF Other */
+    .debug_ranges  0 : { *(.debug_ranges) }
+    .debug_line_str 0 : { *(.debug_line_str) }
     /* Rust stuff */
 
     /DISCARD/ :

+ 1 - 1
src/kernel/block.rs

@@ -217,7 +217,7 @@ impl BlockDevice {
     pub fn read_some(
         &self,
         offset: usize,
-        buffer: &mut impl Buffer,
+        buffer: &mut dyn Buffer,
     ) -> KResult<FillResult> {
         let mut sector_start = offset as u64 / 512;
         let mut first_sector_offset = offset as u64 % 512;

+ 1 - 0
src/kernel/interrupt.cpp

@@ -101,6 +101,7 @@ static inline void fault_handler(interrupt_stack* context, mmx_registers*) {
     switch (context->int_no) {
         case 6:
         case 8: {
+            assert(false);
             if (!current_process->attr.system)
                 kill_current(SIGSEGV); // noreturn
         } break;

+ 11 - 15
src/kernel/process.cpp

@@ -1,6 +1,3 @@
-#include <memory>
-#include <utility>
-
 #include <assert.h>
 #include <bits/alltypes.h>
 #include <stdint.h>
@@ -34,11 +31,11 @@ process::process(const process& parent, pid_t pid)
     , pgid{parent.pgid}
     , sid{parent.sid}
     , control_tty{parent.control_tty} {
-    if (parent.cwd)
-        cwd = fs::d_get(parent.cwd);
+    assert(parent.cwd);
+    cwd = fs::d_get(parent.cwd);
 
-    if (parent.fs_context.root)
-        fs_context.root = fs::d_get(parent.fs_context.root);
+    assert(parent.fs_context.root);
+    fs_context.root = fs::d_get(parent.fs_context.root);
 }
 
 process::process(pid_t pid, pid_t ppid)
@@ -230,15 +227,15 @@ void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn) {
     // mount fat32 /mnt directory
     // TODO: parse kernel parameters
     if (1) {
-        auto [mnt, status] = fs::open(context, context.root.get(), "/mnt");
+        auto [mnt, status] = fs::open(context, context.root, "/mnt");
         assert(mnt && status == -ENOENT);
 
-        if (int ret = fs_mkdir(mnt.get(), 0755); 1)
-            assert(ret == 0 && mnt->flags & fs::D_PRESENT);
+        if (int ret = fs::fs_mkdir(mnt.get(), 0755); 1)
+            assert(ret == 0);
 
-        int ret = fs_mount(mnt.get(), "/dev/sda", "/mnt", "fat32",
-                            MS_RDONLY | MS_NOATIME | MS_NODEV | MS_NOSUID,
-                            "ro,nodev");
+        int ret = fs::fs_mount(mnt.get(), "/dev/sda", "/mnt", "fat32",
+                               MS_RDONLY | MS_NOATIME | MS_NODEV | MS_NOSUID,
+                               "ro,nodev");
 
         assert(ret == 0);
     }
@@ -259,10 +256,9 @@ void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn) {
         freeze();
     }
 
-    d.exec_dent = exec.get();
+    d.exec_dent = std::move(exec);
     if (int ret = types::elf::elf32_load(d); 1)
         assert(ret == 0);
-    exec.reset();
 
     int ds = 0x33, cs = 0x2b;
 

+ 25 - 13
src/kernel/syscall/fileops.cc

@@ -76,18 +76,22 @@ int kernel::syscall::do_open(const char __user* path, int flags, mode_t mode) {
     mode &= ~current_process->umask;
 
     // TODO: use copy_from_user
-    return current_process->files.open(current_process->cwd.get(), path, flags,
+    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 [dent, status] = current_open(linkpath);
-    if (!dent || status != -ENOENT)
+    auto [dent, status] = current_open(linkpath, false);
+    if (!dent)
         return status;
 
-    return fs_symlink(dent.get(), target);
+    if (status == 0)
+        return -EEXIST;
+
+    assert(status == -ENOENT);
+    return fs::fs_symlink(dent.get(), target);
 }
 
 int kernel::syscall::do_readlink(const char __user* pathname, char __user* buf,
@@ -98,11 +102,11 @@ int kernel::syscall::do_readlink(const char __user* pathname, char __user* buf,
     if (!dent || status)
         return status;
 
-    if (buf_size <= 0)
+    if (buf_size & (1ull << 63))
         return -EINVAL;
 
     // TODO: use copy_to_user
-    return fs_readlink(&dent->inode, buf, buf_size);
+    return fs_readlink(fs::r_dentry_get_inode(dent.get()), buf, buf_size);
 }
 
 int kernel::syscall::do_ioctl(int fd, unsigned long request, uintptr_t arg3) {
@@ -382,7 +386,7 @@ int kernel::syscall::do_statx(int dirfd, const char __user* path, int flags,
         return status;
 
     // TODO: copy to user
-    return fs_statx(&dent->inode, statxbuf, mask);
+    return fs_statx(fs::r_dentry_get_inode(dent.get()), statxbuf, mask);
 }
 
 int kernel::syscall::do_fcntl(int fd, int cmd, unsigned long arg) {
@@ -408,10 +412,14 @@ int kernel::syscall::do_mkdir(const char __user* pathname, mode_t mode) {
 
     // TODO: use copy_from_user
     auto [dent, status] = current_open(pathname);
-    if (!dent || status != -ENOENT)
+    if (!dent)
         return status;
 
-    return fs_mkdir(dent.get(), mode);
+    if (status == 0)
+        return -EEXIST;
+
+    assert(status == -ENOENT);
+    return fs::fs_mkdir(dent.get(), mode);
 }
 
 int kernel::syscall::do_truncate(const char __user* pathname, long length) {
@@ -419,7 +427,7 @@ int kernel::syscall::do_truncate(const char __user* pathname, long length) {
     if (!dent || status)
         return status;
 
-    return fs_truncate(&dent->inode, length);
+    return fs_truncate(fs::r_dentry_get_inode(dent.get()), length);
 }
 
 int kernel::syscall::do_unlink(const char __user* pathname) {
@@ -428,7 +436,7 @@ int kernel::syscall::do_unlink(const char __user* pathname) {
     if (!dent || status)
         return status;
 
-    return fs_unlink(dent.get());
+    return fs::fs_unlink(dent.get());
 }
 
 int kernel::syscall::do_access(const char __user* pathname, int mode) {
@@ -453,10 +461,14 @@ int kernel::syscall::do_mknod(const char __user* pathname, mode_t mode,
                               dev_t dev) {
     mode &= S_IFMT | (~current_process->umask & 0777);
     auto [dent, status] = current_open(pathname);
-    if (!dent || status != -ENOENT)
+    if (!dent)
         return status;
 
-    return fs_mknod(dent.get(), mode, dev);
+    if (status == 0)
+        return -EEXIST;
+
+    assert(status == -ENOENT);
+    return fs::fs_mknod(dent.get(), mode, dev);
 }
 
 int kernel::syscall::do_poll(pollfd __user* fds, nfds_t nfds, int timeout) {

+ 1 - 1
src/kernel/syscall/mount.cc

@@ -18,5 +18,5 @@ int kernel::syscall::do_mount(const char __user* source,
     if (!mountpoint || status)
         return status;
 
-    return fs_mount(mountpoint.get(), source, target, fstype, flags, _fsdata);
+    return fs::fs_mount(mountpoint.get(), source, target, fstype, flags, _fsdata);
 }

+ 10 - 23
src/kernel/syscall/procops.cc

@@ -13,6 +13,7 @@
 #include <kernel/signal.hpp>
 #include <kernel/syscall.hpp>
 #include <kernel/utsname.hpp>
+#include <kernel/vfs.hpp>
 #include <kernel/vfs/dentry.hpp>
 
 using namespace kernel::syscall;
@@ -33,7 +34,7 @@ int kernel::syscall::do_chdir(const char __user* path) {
     if (!dir || ret)
         return ret;
 
-    if (!(dir->flags & fs::D_DIRECTORY))
+    if (!fs::r_dentry_is_directory(dir.get()))
         return -ENOTDIR;
 
     current_process->cwd = std::move(dir);
@@ -43,32 +44,25 @@ int kernel::syscall::do_chdir(const char __user* path) {
 execve_retval kernel::syscall::do_execve(const std::string& exec,
                                          const std::vector<std::string>& args,
                                          const std::vector<std::string>& envs) {
+    auto [dent, ret] = current_open(exec);
+
+    if (ret)
+        return {0, 0, ret};
+
     types::elf::elf32_load_data d{
-        .exec_dent{},
+        .exec_dent{std::move(dent)},
         .argv{args},
         .envp{envs},
         .ip{},
         .sp{},
     };
 
-    fs::dentry_pointer dent;
-    if (1) {
-        int ret;
-        std::tie(dent, ret) = current_open(exec);
-
-        if (!dent || ret)
-            return {0, 0, ret};
-
-        d.exec_dent = dent.get();
-    }
-
     current_process->files.onexec();
 
     async::preempt_disable();
 
     // TODO: set cs and ss to compatibility mode
     if (int ret = types::elf::elf32_load(d); ret != 0) {
-        dent.reset();
         async::preempt_enable();
 
         if (ret == types::elf::ELF_LOAD_FAIL_NORETURN)
@@ -142,16 +136,9 @@ 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 = fs::d_path(current_process->cwd.get(),
-                           current_process->fs_context.root.get());
-
-    int len = std::min(buf_size - 1, path.size());
-
     // TODO: use copy_to_user
-    strncpy(buf, path.c_str(), len);
-    buf[len] = 0;
-
-    return len;
+    return fs::d_path(current_process->cwd.get(),
+                      current_process->fs_context.root.get(), buf, buf_size);
 }
 
 pid_t kernel::syscall::do_setsid() {

+ 42 - 97
src/kernel/vfs.cpp

@@ -1,5 +1,4 @@
 #include <cstddef>
-#include <utility>
 
 #include <assert.h>
 #include <bits/alltypes.h>
@@ -19,7 +18,7 @@
 #include <kernel/vfs/dentry.hpp>
 
 fs::regular_file::regular_file(file_flags flags, size_t cursor,
-                               const struct rust_inode_handle* ind)
+                               struct rust_inode_handle* ind)
     : file(flags), cursor(cursor), ind(ind) {}
 
 ssize_t fs::regular_file::read(char* __user buf, size_t n) {
@@ -71,22 +70,20 @@ off_t fs::regular_file::seek(off_t n, int whence) {
 int fs::regular_file::getdents(char* __user buf, size_t cnt) {
     size_t orig_cnt = cnt;
     auto callback = readdir_callback_fn(
-        [&buf, &cnt](const char* fn, size_t fnlen,
-                     const struct rust_inode_handle*,
-                     const struct inode_data* ind, uint8_t type) {
+        [&buf, &cnt](const char* fn, size_t fnlen, ino_t ino) {
             size_t reclen = sizeof(fs::user_dirent) + 1 + fnlen;
             if (cnt < reclen)
                 return -EFAULT;
 
             auto* dirp = (fs::user_dirent*)buf;
-            dirp->d_ino = ind->ino;
+            dirp->d_ino = ino;
             dirp->d_reclen = reclen;
             // TODO: show offset
             // dirp->d_off = 0;
             // TODO: use copy_to_user
             memcpy(dirp->d_name, fn, fnlen);
             buf[reclen - 2] = 0;
-            buf[reclen - 1] = type;
+            buf[reclen - 1] = 0;
 
             buf += reclen;
             cnt -= reclen;
@@ -104,18 +101,16 @@ int fs::regular_file::getdents(char* __user buf, size_t cnt) {
 int fs::regular_file::getdents64(char* __user buf, size_t cnt) {
     size_t orig_cnt = cnt;
     auto callback = readdir_callback_fn(
-        [&buf, &cnt](const char* fn, size_t fnlen,
-                     const struct rust_inode_handle*,
-                     const struct inode_data* ind, uint8_t type) {
+        [&buf, &cnt](const char* fn, size_t fnlen, ino_t ino) {
             size_t reclen = sizeof(fs::user_dirent64) + fnlen;
             if (cnt < reclen)
                 return -EFAULT;
 
             auto* dirp = (fs::user_dirent64*)buf;
-            dirp->d_ino = ind->ino;
+            dirp->d_ino = ino;
             dirp->d_off = 114514;
             dirp->d_reclen = reclen;
-            dirp->d_type = type;
+            dirp->d_type = 0;
             // TODO: use copy_to_user
             memcpy(dirp->d_name, fn, fnlen);
             buf[reclen - 1] = 0;
@@ -157,68 +152,6 @@ fs::fifo_file::~fifo_file() {
 
 static fs::chrdev_ops** chrdevs[256];
 
-std::pair<fs::dentry_pointer, 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, -ELOOP};
-
-    dentry_pointer cwd{path.is_absolute() ? d_get(context.root) : d_get(_cwd)};
-
-    for (; path; ++path) {
-        auto item = *path;
-        if (item.empty() || item == ".")
-            continue;
-
-        if (!(cwd->flags & D_PRESENT))
-            return {nullptr, -ENOENT};
-
-        if (cwd->flags & D_SYMLINK) {
-            char linkpath[256];
-            int ret = fs_readlink(&cwd->inode, linkpath, sizeof(linkpath));
-            if (ret < 0)
-                return {nullptr, ret};
-            linkpath[ret] = 0;
-
-            std::tie(cwd, ret) =
-                fs::open(context, cwd->parent, linkpath, true, recurs_no + 1);
-            if (!cwd || ret)
-                return {nullptr, ret};
-        }
-
-        if (item == ".." && cwd.get() == context.root.get())
-            continue;
-
-        if (1) {
-            int status;
-            std::tie(cwd, status) = d_find(cwd.get(), item);
-            if (!cwd)
-                return {nullptr, status};
-        }
-
-        while (cwd->flags & D_MOUNTPOINT)
-            cwd = r_get_mountpoint(cwd.get());
-    }
-
-    if (!(cwd->flags & D_PRESENT))
-        return {std::move(cwd), -ENOENT};
-
-    if (follow && cwd->flags & D_SYMLINK) {
-        char linkpath[256];
-        int ret = fs_readlink(&cwd->inode, linkpath, sizeof(linkpath));
-        if (ret < 0)
-            return {nullptr, ret};
-        linkpath[ret] = 0;
-
-        return fs::open(context, cwd->parent, linkpath, true, recurs_no + 1);
-    }
-
-    return {std::move(cwd), 0};
-}
-
 int fs::register_char_device(dev_t node, const fs::chrdev_ops& ops) {
     int major = NODE_MAJOR(node);
     int minor = NODE_MINOR(node);
@@ -233,25 +166,6 @@ int fs::register_char_device(dev_t node, const fs::chrdev_ops& ops) {
     return 0;
 }
 
-// MBR partition table, used by partprobe()
-
-struct PACKED mbr_part_entry {
-    uint8_t attr;
-    uint8_t chs_start[3];
-    uint8_t type;
-    uint8_t chs_end[3];
-    uint32_t lba_start;
-    uint32_t cnt;
-};
-
-struct PACKED mbr {
-    uint8_t code[440];
-    uint32_t signature;
-    uint16_t reserved;
-    mbr_part_entry parts[4];
-    uint16_t magic;
-};
-
 ssize_t fs::char_device_read(dev_t node, char* buf, size_t buf_size, size_t n) {
     int major = NODE_MAJOR(node);
     int minor = NODE_MINOR(node);
@@ -375,8 +289,39 @@ int fs::pipe::read(char* buf, size_t n) {
 }
 
 extern "C" int call_callback(const fs::readdir_callback_fn* func,
-                             const char* filename, size_t fnlen,
-                             const struct fs::rust_inode_handle* inode,
-                             const struct fs::inode_data* idata, uint8_t type) {
-    return (*func)(filename, fnlen, inode, idata, type);
+                             const char* filename, size_t fnlen, ino_t ino) {
+    return (*func)(filename, fnlen, ino);
+}
+
+extern "C" struct dentry* dentry_open(struct dentry* context_root,
+                                      struct dentry* cwd, const char* path,
+                                      size_t path_length, bool follow);
+
+std::pair<fs::dentry_pointer, int> fs::open(const fs::fs_context& context,
+                                            const fs::dentry_pointer& cwd,
+                                            types::string_view path,
+                                            bool follow_symlinks) {
+    auto result = dentry_open(context.root.get(), cwd.get(), path.data(),
+                              path.size(), follow_symlinks);
+    auto result_int = reinterpret_cast<intptr_t>(result);
+
+    if (result_int > -128)
+        return {nullptr, result_int};
+
+    if (fs::r_dentry_is_invalid(result))
+        return {result, -ENOENT};
+
+    return {result, 0};
+}
+
+extern "C" void r_dput(struct dentry* dentry);
+extern "C" struct dentry* r_dget(struct dentry* dentry);
+
+void fs::dentry_deleter::operator()(struct dentry* dentry) const {
+    if (dentry)
+        r_dput(dentry);
+}
+
+fs::dentry_pointer fs::d_get(const dentry_pointer& dp) {
+    return dentry_pointer{r_dget(dp.get())};
 }

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

@@ -1,244 +0,0 @@
-#include <defs.hpp>
-
-#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.hpp>
-#include <kernel/vfs/dentry.hpp>
-
-using namespace fs;
-using types::hash_t, types::hash_str, types::hash_ptr;
-
-static inline struct dentry* __d_parent(struct dentry* dentry) {
-    assert(dentry->parent);
-    return dentry->parent;
-}
-
-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(const struct dentry* dentry,
-                             const struct dentry* parent,
-                             types::string_view name) {
-    return dentry->parent == parent && dentry->name == name;
-}
-
-static inline hash_t __d_hash(const 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(const 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(!dentry->parent);
-    assert(parent->refcount && dentry->refcount);
-
-    dentry->parent = d_get(parent);
-    dentry->prev = nullptr;
-    dentry->next = __d_first(parent->cache, dentry->hash);
-
-    __d_first(parent->cache, dentry->hash) = d_get(dentry);
-    parent->cache->size++;
-}
-
-static inline struct dentry* __d_find_fast(const 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 d_get(dentry);
-    }
-
-    return nullptr;
-}
-
-static inline int __d_load(struct dentry* parent) {
-    if (__d_is_loaded(parent))
-        return 0;
-
-    auto* inode = &parent->inode;
-
-    if (!__d_is_dir(parent))
-        return -ENOTDIR;
-
-    size_t offset = 0;
-    auto callback = readdir_callback_fn(
-        [parent](const char* fn, size_t fnlen,
-                 const struct rust_inode_handle* handle,
-                 const struct inode_data* inode, u8) -> int {
-            struct dentry* dentry = dcache_alloc(parent->cache);
-
-            r_dentry_save_inode(dentry, handle);
-            dentry->name.assign(fn, fnlen);
-
-            if (S_ISDIR(inode->mode))
-                dentry->flags = D_PRESENT | D_DIRECTORY;
-            else if (S_ISLNK(inode->mode))
-                dentry->flags = D_PRESENT | D_SYMLINK;
-            else
-                dentry->flags = D_PRESENT;
-
-            dentry->hash = __d_hash(parent, dentry->name);
-
-            __d_add(parent, dentry);
-
-            d_put(dentry);
-            return 0;
-        });
-
-    while (true) {
-        ssize_t off = fs_readdir(inode, offset, &callback);
-
-        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) [[unlikely]]
-        return {d_get(parent), 0};
-
-    // this only works for dentries that is not fs_root of some context
-    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->name.assign(name.data(), name.size());
-        dentry->hash = __d_hash(parent, dentry->name);
-
-        __d_add(parent, dentry);
-
-        return {dentry, -ENOENT};
-    }
-
-    return {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;
-}
-
-dentry_pointer fs::d_get(const dentry_pointer& dentry) {
-    return d_get(dentry.get());
-}
-
-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 dentry_deleter::operator()(struct dentry* dentry) const {
-    fs::d_put(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 d_get(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) = d_get(root);
-
-    cache->size++;
-}
-
-void rust_get_cxx_string(const std::string* str,
-                         rust_get_cxx_string_result* out_result) {
-    out_result->data = str->data();
-    out_result->len = str->size();
-}
-
-void rust_operator_eql_cxx_string(const std::string* str, std::string* dst) {
-    *dst = *str;
-}

+ 344 - 137
src/kernel/vfs/dentry.rs

@@ -1,203 +1,410 @@
-use crate::prelude::*;
+pub mod dcache;
 
-use core::ops::{Deref, DerefMut};
-
-use alloc::{
-    boxed::Box,
-    sync::{Arc, Weak},
+use core::{
+    hash::{BuildHasher, BuildHasherDefault, Hasher},
+    sync::atomic::AtomicPtr,
 };
 
-use crate::io::get_cxx_std_string;
-
-use super::{
-    bindings::{fs, EFAULT},
-    inode::Inode,
-    vfs::Vfs,
+use crate::{
+    hash::KernelHasher,
+    io::{ByteBuffer, RawBuffer},
+    path::{Path, PathComponent},
+    prelude::*,
+    rcu::{RCUNode, RCUPointer},
 };
 
-#[repr(C)]
-pub struct DentryInner {
-    fs: *const Mutex<dyn Vfs>,
-    inode: *const dyn Inode,
-
-    cache: *mut fs::dcache,
-    pub parent: *mut DentryInner,
-
-    prev: *mut DentryInner,
-    next: *mut DentryInner,
+use alloc::sync::Arc;
+use bindings::{EINVAL, ELOOP, ENOENT, ENOTDIR};
 
-    pub flags: u64,
-    pub hash: u64,
+use super::inode::Inode;
 
-    refcount: u64,
-    pub name: [u64; 4],
+struct DentryData {
+    inode: Arc<Inode>,
+    flags: u64,
 }
 
 pub struct Dentry {
-    raw: *mut DentryInner,
+    // Const after insertion into dcache
+    parent: Arc<Dentry>,
+    name: Arc<[u8]>,
+    hash: u64,
+
+    // Used by the dentry cache
+    prev: AtomicPtr<Dentry>,
+    next: AtomicPtr<Dentry>,
+
+    // RCU Mutable
+    data: RCUPointer<DentryData>,
 }
 
-/// We assume that the inode is owned and can not be invalidated
-/// during our access to it.
-///
-pub fn raw_inode_clone(handle: *const *const dyn Inode) -> Arc<dyn Inode> {
-    assert!(!handle.is_null());
-    unsafe {
-        Arc::increment_strong_count(*handle);
-        let inode = Arc::from_raw(*handle);
-        inode
+const D_DIRECTORY: u64 = 1;
+const D_MOUNTPOINT: u64 = 2;
+const D_SYMLINK: u64 = 4;
+const D_REGULAR: u64 = 8;
+
+impl RCUNode<Dentry> for Dentry {
+    fn rcu_prev(&self) -> &AtomicPtr<Self> {
+        &self.prev
     }
-}
 
-// TODO!!!: CHANGE THIS
-unsafe impl Sync for Dentry {}
-unsafe impl Send for Dentry {}
+    fn rcu_next(&self) -> &AtomicPtr<Self> {
+        &self.next
+    }
+}
 
 impl Dentry {
-    pub fn from_raw(raw: *mut DentryInner) -> KResult<Self> {
-        if raw.is_null() {
-            return Err(EFAULT);
+    fn rehash(self: &Arc<Self>) -> u64 {
+        let builder: BuildHasherDefault<KernelHasher> = Default::default();
+        let mut hasher = builder.build_hasher();
+
+        hasher.write_usize(self.parent_addr() as usize);
+        hasher.write(self.name.as_ref());
+
+        hasher.finish()
+    }
+
+    fn find(self: &Arc<Self>, name: &[u8]) -> KResult<Arc<Self>> {
+        let data = self.data.load();
+        let data = data.as_ref().ok_or(ENOENT)?;
+
+        if data.flags & D_DIRECTORY == 0 {
+            return Err(ENOTDIR);
         }
 
-        Ok(Dentry {
-            raw: unsafe { fs::d_get1(raw as *mut _) as *mut _ },
-        })
+        match name {
+            b"." => Ok(self.clone()),
+            b".." => Ok(self.parent.clone()),
+            _ => {
+                let dentry = Dentry::create(self.clone(), name);
+                Ok(dcache::d_find_fast(&dentry).unwrap_or_else(|| {
+                    dcache::d_try_revalidate(&dentry);
+                    dcache::d_add(&dentry);
+
+                    dentry
+                }))
+            }
+        }
     }
+}
 
-    pub fn parent_from_raw(raw: *mut DentryInner) -> KResult<Self> {
-        Dentry::from_raw(unsafe { raw.as_ref() }.ok_or(EFAULT)?.parent)
+impl Dentry {
+    pub fn create(parent: Arc<Dentry>, name: &[u8]) -> Arc<Self> {
+        let mut val = Arc::new(Self {
+            parent,
+            name: Arc::from(name),
+            hash: 0,
+            prev: AtomicPtr::default(),
+            next: AtomicPtr::default(),
+            data: RCUPointer::empty(),
+        });
+        let hash = val.rehash();
+        let val_mut = Arc::get_mut(&mut val).unwrap();
+        val_mut.hash = hash;
+
+        val
     }
 
-    pub fn leak(self) -> *mut DentryInner {
-        let raw = self.raw;
-        core::mem::forget(self);
-        raw
+    /// Check the equality of two denties inside the same dentry cache hash group
+    /// where `other` is identified by `hash`, `parent` and `name`
+    ///
+    fn hash_eq(self: &Arc<Self>, other: &Arc<Self>) -> bool {
+        self.hash == other.hash
+            && self.parent_addr() == other.parent_addr()
+            && self.name == other.name
     }
 
-    pub fn get_name(&self) -> &str {
-        get_cxx_std_string(&self.name).unwrap()
+    pub fn name(&self) -> &Arc<[u8]> {
+        &self.name
     }
 
-    pub fn save_inode(&mut self, inode: Arc<dyn Inode>) {
-        let vfs = inode.vfs_weak();
-        self.inode = Arc::into_raw(inode);
-        self.fs = Weak::into_raw(vfs);
+    pub fn parent(&self) -> &Arc<Self> {
+        &self.parent
     }
 
-    pub fn get_inode_clone(&self) -> Arc<dyn Inode> {
-        raw_inode_clone(&self.inode)
+    pub fn parent_addr(&self) -> *const Self {
+        Arc::as_ptr(&self.parent)
     }
 
-    pub fn take_inode(&self) -> Arc<dyn Inode> {
-        unsafe { Arc::from_raw(self.inode) }
+    fn save_data(&self, inode: Arc<Inode>, flags: u64) -> KResult<()> {
+        let new = DentryData { inode, flags };
+
+        let old = self.data.swap(Some(Arc::new(new)));
+        assert!(old.is_none());
+
+        Ok(())
     }
 
-    pub fn take_fs(&self) -> Weak<Mutex<dyn Vfs>> {
-        unsafe { Weak::from_raw(self.fs) }
+    pub fn save_reg(&self, file: Arc<Inode>) -> KResult<()> {
+        self.save_data(file, D_REGULAR)
     }
-}
 
-impl Deref for Dentry {
-    type Target = DentryInner;
+    pub fn save_symlink(&self, link: Arc<Inode>) -> KResult<()> {
+        self.save_data(link, D_SYMLINK)
+    }
 
-    fn deref(&self) -> &Self::Target {
-        unsafe { &*self.raw }
+    pub fn save_dir(&self, dir: Arc<Inode>) -> KResult<()> {
+        self.save_data(dir, D_DIRECTORY)
     }
-}
 
-impl DerefMut for Dentry {
-    fn deref_mut(&mut self) -> &mut Self::Target {
-        unsafe { &mut *self.raw }
+    pub fn invalidate(&self) -> KResult<()> {
+        let old = self.data.swap(None);
+        assert!(old.is_some());
+
+        Ok(())
     }
-}
 
-impl Clone for Dentry {
-    fn clone(&self) -> Self {
-        unsafe {
-            fs::d_get1(self.raw as *mut _) as *mut _;
-        }
-        Dentry { raw: self.raw }
+    pub fn get_inode(&self) -> KResult<Arc<Inode>> {
+        self.data
+            .load()
+            .as_ref()
+            .ok_or(EINVAL)
+            .map(|data| data.inode.clone())
     }
-}
 
-impl Drop for Dentry {
-    fn drop(&mut self) {
-        unsafe {
-            fs::d_put(self.raw as *mut _);
-        }
+    /// This function is used to get the **borrowed** dentry from a raw pointer
+    pub fn from_raw(raw: &*const Self) -> BorrowedArc<Self> {
+        assert!(!raw.is_null());
+
+        BorrowedArc::new(raw)
     }
-}
 
-impl PartialEq for Dentry {
-    fn eq(&self, other: &Self) -> bool {
-        self.raw == other.raw
+    pub fn is_directory(&self) -> bool {
+        let data = self.data.load();
+        data.as_ref()
+            .map_or(false, |data| data.flags & D_DIRECTORY != 0)
     }
 }
 
-impl PartialOrd for Dentry {
-    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
-        self.raw.partial_cmp(&other.raw)
-    }
+#[repr(C)]
+pub struct FsContext {
+    root: *const Dentry,
 }
 
-impl Eq for Dentry {}
+impl Dentry {
+    fn resolve_directory(
+        context: &FsContext,
+        dentry: Arc<Self>,
+        nrecur: u32,
+    ) -> KResult<Arc<Self>> {
+        if nrecur >= 16 {
+            return Err(ELOOP);
+        }
 
-impl Ord for Dentry {
-    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
-        self.raw.cmp(&other.raw)
+        let data = dentry.data.load();
+        let data = data.as_ref().ok_or(ENOENT)?;
+
+        match data.flags {
+            flags if flags & D_REGULAR != 0 => Err(ENOTDIR),
+            flags if flags & D_DIRECTORY != 0 => Ok(dentry),
+            flags if flags & D_SYMLINK != 0 => {
+                let mut buffer = [0u8; 256];
+                let mut buffer = ByteBuffer::new(&mut buffer);
+
+                data.inode.readlink(&data.inode, &mut buffer)?;
+                let path = Path::new(buffer.data())?;
+
+                let dentry = Self::open_recursive(
+                    context,
+                    &dentry.parent,
+                    path,
+                    true,
+                    nrecur + 1,
+                )?;
+
+                Self::resolve_directory(context, dentry, nrecur + 1)
+            }
+            _ => panic!("Invalid dentry flags"),
+        }
     }
-}
-
-const DCACHE_HASH_BITS: i32 = 8;
 
-pub struct DentryCache {
-    cache: Box<fs::dcache>,
-}
+    fn open_recursive(
+        context: &FsContext,
+        cwd: &Arc<Self>,
+        path: Path,
+        follow: bool,
+        nrecur: u32,
+    ) -> KResult<Arc<Self>> {
+        // too many recursive search layers will cause stack overflow
+        // so we use 16 for now
+        if nrecur >= 16 {
+            return Err(ELOOP);
+        }
 
-unsafe impl Sync for DentryCache {}
-unsafe impl Send for DentryCache {}
-
-impl DentryCache {
-    pub fn new() -> Self {
-        let mut dcache = Self {
-            cache: Box::new(fs::dcache {
-                arr: core::ptr::null_mut(),
-                hash_bits: Default::default(),
-                size: Default::default(),
-            }),
+        let mut cwd = if path.is_absolute() {
+            Dentry::from_raw(&context.root).clone()
+        } else {
+            cwd.clone()
         };
 
-        unsafe {
-            fs::dcache_init(
-                core::ptr::from_mut(&mut dcache.cache),
-                DCACHE_HASH_BITS,
-            )
-        };
+        let root_dentry = Dentry::from_raw(&context.root);
+
+        for item in path.iter() {
+            if let PathComponent::TrailingEmpty = item {
+                if cwd.data.load().as_ref().is_none() {
+                    return Ok(cwd);
+                }
+            }
+
+            cwd = Self::resolve_directory(context, cwd, nrecur)?;
+
+            match item {
+                PathComponent::TrailingEmpty | PathComponent::Current => {} // pass
+                PathComponent::Parent => {
+                    if !cwd.hash_eq(root_dentry.as_ref()) {
+                        cwd = Self::resolve_directory(
+                            context,
+                            cwd.parent.clone(),
+                            nrecur,
+                        )?;
+                    }
+                    continue;
+                }
+                PathComponent::Name(name) => {
+                    cwd = cwd.find(name)?;
+                }
+            }
+        }
 
-        dcache
+        if follow {
+            let data = cwd.data.load();
+
+            if let Some(data) = data.as_ref() {
+                if data.flags & D_SYMLINK != 0 {
+                    let data = cwd.data.load();
+                    let data = data.as_ref().unwrap();
+                    let mut buffer = [0u8; 256];
+                    let mut buffer = ByteBuffer::new(&mut buffer);
+
+                    data.inode.readlink(&data.inode, &mut buffer)?;
+                    let path = Path::new(buffer.data())?;
+
+                    cwd = Self::open_recursive(
+                        context,
+                        &cwd.parent,
+                        path,
+                        true,
+                        nrecur + 1,
+                    )?;
+                }
+            }
+        }
+
+        Ok(cwd)
     }
+}
 
-    pub fn alloc(&mut self) -> Dentry {
-        let raw =
-            unsafe { fs::dcache_alloc(core::ptr::from_mut(&mut self.cache)) };
-        assert!(!raw.is_null());
-        Dentry { raw: raw as *mut _ }
+#[no_mangle]
+pub extern "C" fn dentry_open(
+    context_root: *const Dentry,
+    cwd: *const Dentry, // borrowed
+    path: *const u8,
+    path_len: usize,
+    follow: bool,
+) -> *const Dentry {
+    match (|| -> KResult<Arc<Dentry>> {
+        let path =
+            Path::new(unsafe { core::slice::from_raw_parts(path, path_len) })?;
+
+        let context = FsContext { root: context_root };
+
+        Dentry::open_recursive(
+            &context,
+            Dentry::from_raw(&cwd).as_ref(),
+            path,
+            follow,
+            0,
+        )
+    })() {
+        Ok(dentry) => Arc::into_raw(dentry),
+        Err(err) => (-(err as i32) as usize) as *const Dentry,
     }
+}
+
+#[no_mangle]
+pub extern "C" fn d_path(
+    dentry: *const Dentry,
+    root: *const Dentry,
+    mut buffer: *mut u8,
+    bufsize: usize,
+) -> i32 {
+    let mut buffer = RawBuffer::new_from_raw(&mut buffer, bufsize);
+
+    match (|| {
+        let mut dentry = Dentry::from_raw(&dentry).clone();
+        let root = Dentry::from_raw(&root);
+
+        let mut path = vec![];
 
-    pub fn insert_root(&mut self, root: &mut Dentry) {
-        unsafe {
-            fs::dcache_init_root(
-                core::ptr::from_mut(&mut self.cache),
-                root.raw as *mut _,
-            )
+        while Arc::as_ptr(&dentry) != Arc::as_ptr(root.as_ref()) {
+            if path.len() > 32 {
+                return Err(ELOOP);
+            }
+
+            path.push(dentry.name().clone());
+            dentry = dentry.parent().clone();
+        }
+
+        const ERANGE: u32 = 34;
+        buffer.fill(b"/")?.ok_or(ERANGE)?;
+        for item in path.iter().rev().map(|name| name.as_ref()) {
+            buffer.fill(item)?.ok_or(ERANGE)?;
+            buffer.fill(b"/")?.ok_or(ERANGE)?;
         }
+
+        buffer.fill(&[0])?.ok_or(ERANGE)?;
+
+        Ok(())
+    })() {
+        Ok(_) => 0,
+        Err(err) => -(err as i32),
     }
 }
 
-impl Drop for DentryCache {
-    fn drop(&mut self) {
-        unsafe {
-            fs::dcache_drop(core::ptr::from_mut(&mut self.cache));
+#[no_mangle]
+pub extern "C" fn r_dget(dentry: *const Dentry) -> *const Dentry {
+    debug_assert!(!dentry.is_null());
+
+    unsafe { Arc::increment_strong_count(dentry) };
+    dentry
+}
+
+#[no_mangle]
+pub extern "C" fn r_dput(dentry: *const Dentry) {
+    debug_assert!(!dentry.is_null());
+
+    unsafe { Arc::from_raw(dentry) };
+}
+
+#[no_mangle]
+pub extern "C" fn r_dentry_get_inode(dentry: *const Dentry) -> *const Inode {
+    let dentry = Dentry::from_raw(&dentry);
+
+    match dentry.get_inode() {
+        Ok(inode) => Arc::into_raw(inode),
+        Err(err) => {
+            dont_check!(println!(
+                "[kernel:warn] r_dentry_get_inode: {:?}",
+                err
+            ));
+            core::ptr::null()
         }
     }
 }
+
+#[no_mangle]
+pub extern "C" fn r_dentry_is_directory(dentry: *const Dentry) -> bool {
+    let dentry = Dentry::from_raw(&dentry);
+
+    dentry
+        .data
+        .load()
+        .as_ref()
+        .map_or(false, |data| data.flags & D_DIRECTORY != 0)
+}
+
+#[no_mangle]
+pub extern "C" fn r_dentry_is_invalid(dentry: *const Dentry) -> bool {
+    let dentry = Dentry::from_raw(&dentry);
+
+    dentry.data.load().is_none()
+}

+ 85 - 0
src/kernel/vfs/dentry/dcache.rs

@@ -0,0 +1,85 @@
+use core::{mem::MaybeUninit, sync::atomic::AtomicPtr};
+
+use alloc::sync::Arc;
+use bindings::ENOENT;
+
+use crate::{
+    kernel::vfs::{s_isdir, s_islnk},
+    prelude::*,
+    rcu::{RCUIterator, RCUList, RCUPointer},
+};
+
+use super::{Dentry, Inode};
+
+use lazy_static::lazy_static;
+
+const DCACHE_HASH_BITS: u32 = 8;
+
+static DCACHE: [RCUList<Dentry>; 1 << DCACHE_HASH_BITS] =
+    [const { RCUList::new() }; 1 << DCACHE_HASH_BITS];
+
+lazy_static! {
+    static ref DROOT: Arc<Dentry> = {
+        let dentry = Arc::new_uninit();
+        let fake_parent = unsafe { dentry.clone().assume_init() };
+
+        unsafe { &mut *(Arc::as_ptr(&dentry) as *mut MaybeUninit<Dentry>) }
+            .write(Dentry {
+                parent: fake_parent,
+                name: b"[root]".as_slice().into(),
+                hash: 0,
+                prev: AtomicPtr::default(),
+                next: AtomicPtr::default(),
+                data: RCUPointer::empty(),
+            });
+
+        unsafe { dentry.assume_init() }
+    };
+}
+
+pub fn _looped_droot() -> &'static Arc<Dentry> {
+    &DROOT
+}
+
+pub fn d_hinted(hash: u64) -> &'static RCUList<Dentry> {
+    let hash = hash as usize & ((1 << DCACHE_HASH_BITS) - 1);
+    &DCACHE[hash]
+}
+
+pub fn d_iter_for(hash: u64) -> RCUIterator<'static, Dentry> {
+    d_hinted(hash).iter()
+}
+
+pub fn d_add(dentry: &Arc<Dentry>) {
+    d_hinted(dentry.hash).insert(dentry.clone());
+}
+
+pub fn d_find_fast(dentry: &Arc<Dentry>) -> Option<Arc<Dentry>> {
+    d_iter_for(dentry.rehash())
+        .find(|cur| cur.hash_eq(dentry))
+        .map(|dentry| dentry.clone())
+}
+
+/// Silently fail without any side effects
+pub fn d_try_revalidate(dentry: &Arc<Dentry>) {
+    (|| -> KResult<()> {
+        let parent = dentry.parent().get_inode()?;
+        let inode = parent.lookup(&parent, dentry)?.ok_or(ENOENT)?;
+
+        d_save(dentry, inode)
+    })()
+    .unwrap_or_default();
+}
+
+pub fn d_save(dentry: &Arc<Dentry>, inode: Arc<Inode>) -> KResult<()> {
+    let mode = inode.idata.lock().mode;
+    match mode {
+        mode if s_isdir(mode) => dentry.save_dir(inode),
+        mode if s_islnk(mode) => dentry.save_symlink(inode),
+        _ => dentry.save_reg(inode),
+    }
+}
+
+pub fn d_replace(old: &Arc<Dentry>, new: Arc<Dentry>) {
+    d_hinted(old.hash).replace(old, new);
+}

+ 124 - 158
src/kernel/vfs/ffi.rs

@@ -1,16 +1,20 @@
-use crate::{io::ByteBuffer, kernel::block::BlockDevice, prelude::*};
+use crate::{
+    io::{ByteBuffer, RawBuffer},
+    kernel::block::BlockDevice,
+    prelude::*,
+};
 
 use core::ffi::{c_char, c_void};
 
 use alloc::sync::Arc;
-use bindings::{dev_t, fs::D_PRESENT, mode_t, statx};
+use bindings::{dev_t, ino_t, mode_t, statx};
 
 use crate::io::get_str_from_cstr;
 
 use super::{
     bindings::{fs, EINVAL, EISDIR},
-    dentry::{raw_inode_clone, Dentry, DentryInner},
-    inode::{Inode, InodeData},
+    dentry::Dentry,
+    inode::Inode,
     s_isblk, s_ischr, s_isdir, s_isreg, DevId,
 };
 
@@ -22,18 +26,25 @@ fn into_mut_slice<'a>(buf: *mut u8, bufsize: &usize) -> &'a mut [u8] {
     unsafe { core::slice::from_raw_parts_mut(buf, *bufsize) }
 }
 
+macro_rules! map_err_ffi {
+    ($error:expr) => {
+        match $error {
+            Ok(_) => 0,
+            Err(e) => -(e as i32),
+        }
+    };
+}
+
 #[no_mangle]
 pub extern "C" fn fs_mount(
-    mountpoint: *mut DentryInner, // borrowed
+    mountpoint: *const Dentry, // borrowed
     source: *const c_char,
     mountpoint_str: *const c_char,
     fstype: *const c_char,
     flags: u64,
     _data: *const c_void,
 ) -> i32 {
-    let mountpoint = Dentry::from_raw(mountpoint).unwrap();
-
-    assert_ne!(mountpoint.flags & D_PRESENT, 0);
+    let mountpoint = Dentry::from_raw(&mountpoint);
 
     let source = get_str_from_cstr(source).unwrap();
     let mountpoint_str = get_str_from_cstr(mountpoint_str).unwrap();
@@ -41,7 +52,7 @@ pub extern "C" fn fs_mount(
 
     // TODO: data
     match super::mount::do_mount(
-        mountpoint,
+        &mountpoint,
         source,
         mountpoint_str,
         fstype,
@@ -54,23 +65,26 @@ pub extern "C" fn fs_mount(
 }
 
 fn do_read(
-    file: Arc<dyn Inode>,
+    file: &Arc<Inode>,
     buffer: &mut [u8],
     offset: usize,
 ) -> KResult<usize> {
-    let mode = { file.idata().lock().mode };
+    let mode = { file.idata.lock().mode };
 
     match mode {
         mode if s_isdir(mode) => Err(EISDIR),
-        mode if s_isreg(mode) => file.read(buffer, offset),
+        mode if s_isreg(mode) => {
+            let mut buffer = ByteBuffer::new(buffer);
+            file.read(file, &mut buffer, offset)
+        }
         mode if s_isblk(mode) => {
-            let device = BlockDevice::get(file.devid()?)?;
             let mut buffer = ByteBuffer::new(buffer);
+            let device = BlockDevice::get(file.devid(file)?)?;
 
             Ok(device.read_some(offset, &mut buffer)?.allow_partial())
         }
         mode if s_ischr(mode) => {
-            let devid = file.devid()?;
+            let devid = file.devid(file)?;
 
             let ret = unsafe {
                 fs::char_device_read(
@@ -91,19 +105,15 @@ fn do_read(
     }
 }
 
-fn do_write(
-    file: Arc<dyn Inode>,
-    buffer: &[u8],
-    offset: usize,
-) -> KResult<usize> {
-    let mode = { file.idata().lock().mode };
+fn do_write(file: &Arc<Inode>, buffer: &[u8], offset: usize) -> KResult<usize> {
+    let mode = file.idata.lock().mode;
 
     match mode {
         mode if s_isdir(mode) => Err(EISDIR),
-        mode if s_isreg(mode) => file.write(buffer, offset),
+        mode if s_isreg(mode) => file.write(file, buffer, offset),
         mode if s_isblk(mode) => Err(EINVAL), // TODO
         mode if s_ischr(mode) => {
-            let devid = file.devid()?;
+            let devid = file.devid(file)?;
 
             let ret = unsafe {
                 fs::char_device_write(
@@ -123,20 +133,24 @@ fn do_write(
     }
 }
 
+fn inode_from_raw<'lt>(file: &'lt mut *const Inode) -> BorrowedArc<'lt, Inode> {
+    BorrowedArc::new(file)
+}
+
 #[no_mangle]
 pub extern "C" fn fs_read(
-    file: *const *const dyn Inode, // borrowed
+    mut file: *const Inode, // borrowed
     buf: *mut u8,
     bufsize: usize,
     offset: usize,
     n: usize,
 ) -> isize {
-    let file = raw_inode_clone(file);
+    let file = inode_from_raw(&mut file);
 
     let bufsize = bufsize.min(n);
     let buffer = into_mut_slice(buf, &bufsize);
 
-    match do_read(file, buffer, offset) {
+    match do_read(&file, buffer, offset) {
         Ok(n) => n as isize,
         Err(e) => -(e as isize),
     }
@@ -144,15 +158,15 @@ pub extern "C" fn fs_read(
 
 #[no_mangle]
 pub extern "C" fn fs_write(
-    file: *const *const dyn Inode, // borrowed
+    mut file: *const Inode, // borrowed
     buf: *const u8,
     offset: usize,
     n: usize,
 ) -> isize {
-    let file = raw_inode_clone(file);
+    let file = inode_from_raw(&mut file);
     let buffer = into_slice(buf, &n);
 
-    match do_write(file, buffer, offset) {
+    match do_write(&file, buffer, offset) {
         Ok(n) => n as isize,
         Err(e) => -(e as isize),
     }
@@ -160,42 +174,39 @@ pub extern "C" fn fs_write(
 
 #[no_mangle]
 pub extern "C" fn fs_statx(
-    file: *const *const dyn Inode, // borrowed
+    mut file: *const Inode, // borrowed
     stat: *mut statx,
     mask: u32,
 ) -> i32 {
-    let file = raw_inode_clone(file);
-    let statx = unsafe { stat.as_mut() }.unwrap();
+    map_err_ffi!((|| {
+        let file = inode_from_raw(&mut file);
+        let statx = unsafe { stat.as_mut() }.unwrap();
 
-    match file.statx(statx, mask) {
-        Ok(_) => 0,
-        Err(e) => -(e as i32),
-    }
+        file.statx(file.as_ref(), statx, mask)
+    })())
 }
 
 #[no_mangle]
 pub extern "C" fn fs_truncate(
-    file: *const *const dyn Inode, // borrowed
+    mut file: *const Inode, // borrowed
     size: usize,
 ) -> i32 {
-    let file = raw_inode_clone(file);
-
-    match file.truncate(size) {
-        Ok(_) => 0,
-        Err(e) => -(e as i32),
-    }
+    map_err_ffi!((|| {
+        let file = inode_from_raw(&mut file);
+        file.truncate(file.as_ref(), size)
+    })())
 }
 
 #[no_mangle]
 pub extern "C" fn fs_readlink(
-    file: *const *const dyn Inode, // borrowed
-    buf: *mut c_char,
+    mut file: *const Inode, // borrowed
+    mut buf: *mut u8,
     bufsize: usize,
 ) -> i32 {
-    let file = raw_inode_clone(file);
-    let buffer = into_mut_slice(buf as *mut u8, &bufsize);
+    let file = inode_from_raw(&mut file);
+    let mut buffer = RawBuffer::new_from_raw(&mut buf, bufsize);
 
-    match file.readlink(buffer) {
+    match file.readlink(file.as_ref(), &mut buffer) {
         Ok(n) => n as i32,
         Err(e) => -(e as i32),
     }
@@ -203,121 +214,88 @@ pub extern "C" fn fs_readlink(
 
 #[no_mangle]
 pub extern "C" fn fs_creat(
-    at: *mut DentryInner, // borrowed
+    at: *const Dentry, // borrowed
     mode: mode_t,
 ) -> i32 {
-    let parent = Dentry::parent_from_raw(at).unwrap();
-    let mut at = Dentry::from_raw(at).unwrap();
-
-    assert_ne!(parent.flags & D_PRESENT, 0);
-    assert_eq!(at.flags & D_PRESENT, 0);
+    map_err_ffi!((|| {
+        let at = Dentry::from_raw(&at);
+        let parent = at.parent();
+        let inode = parent.get_inode()?;
 
-    match parent.get_inode_clone().creat(&mut at, mode as u32) {
-        Ok(_) => 0,
-        Err(e) => -(e as i32),
-    }
+        inode.creat(inode.as_ref(), &at, mode as u32)
+    })())
 }
 
 #[no_mangle]
 pub extern "C" fn fs_mkdir(
-    at: *mut DentryInner, // borrowed
+    at: *const Dentry, // borrowed
     mode: mode_t,
 ) -> i32 {
-    let parent = Dentry::parent_from_raw(at).unwrap();
-    let mut at = Dentry::from_raw(at).unwrap();
-
-    assert_ne!(parent.flags & D_PRESENT, 0);
-    assert_eq!(at.flags & D_PRESENT, 0);
+    map_err_ffi!((|| {
+        let at = Dentry::from_raw(&at);
+        let parent = at.parent();
+        let inode = parent.get_inode()?;
 
-    match parent.get_inode_clone().mkdir(&mut at, mode as u32) {
-        Ok(_) => 0,
-        Err(e) => -(e as i32),
-    }
+        inode.mkdir(inode.as_ref(), &at, mode as u32)
+    })())
 }
 
 #[no_mangle]
 pub extern "C" fn fs_mknod(
-    at: *mut DentryInner, // borrowed
+    at: *const Dentry, // borrowed
     mode: mode_t,
     dev: dev_t,
 ) -> i32 {
-    let parent = Dentry::parent_from_raw(at).unwrap();
-    let mut at = Dentry::from_raw(at).unwrap();
+    map_err_ffi!((|| {
+        let at = Dentry::from_raw(&at);
+        let parent = at.parent();
+        let inode = parent.get_inode()?;
 
-    assert_ne!(parent.flags & D_PRESENT, 0);
-    assert_eq!(at.flags & D_PRESENT, 0);
-
-    match parent
-        .get_inode_clone()
-        .mknod(&mut at, mode as u32, dev as DevId)
-    {
-        Ok(_) => 0,
-        Err(e) => -(e as i32),
-    }
+        inode.mknod(inode.as_ref(), &at, mode as u32, dev as DevId)
+    })())
 }
 
 #[no_mangle]
 pub extern "C" fn fs_symlink(
-    at: *mut DentryInner, // borrowed
+    at: *const Dentry, // borrowed
     target: *const c_char,
 ) -> i32 {
-    let parent = Dentry::parent_from_raw(at).unwrap();
-    let mut at = Dentry::from_raw(at).unwrap();
-
-    assert_ne!(parent.flags & D_PRESENT, 0);
-    assert_eq!(at.flags & D_PRESENT, 0);
-
-    match parent
-        .get_inode_clone()
-        .symlink(&mut at, get_str_from_cstr(target).unwrap())
-    {
-        Ok(_) => 0,
-        Err(e) => -(e as i32),
-    }
-}
-
-#[no_mangle]
-pub extern "C" fn fs_unlink(at: *mut DentryInner, // borrowed
-) -> i32 {
-    let parent = Dentry::parent_from_raw(at).unwrap();
-    let mut at = Dentry::from_raw(at).unwrap();
-
-    assert_ne!(parent.flags & D_PRESENT, 0);
-    assert_ne!(at.flags & D_PRESENT, 0);
-
-    match parent.get_inode_clone().unlink(&mut at) {
-        Ok(_) => 0,
-        Err(e) => -(e as i32),
-    }
+    map_err_ffi!((|| {
+        let at = Dentry::from_raw(&at);
+        let parent = at.parent();
+        let inode = parent.get_inode()?;
+
+        inode.symlink(
+            inode.as_ref(),
+            &at,
+            get_str_from_cstr(target)?.as_bytes(),
+        )
+    })())
 }
 
 #[no_mangle]
-pub extern "C" fn r_dentry_save_inode(
-    dent: *mut DentryInner,         // borrowed
-    inode: *const *const dyn Inode, // borrowed
-) {
-    let mut dent = Dentry::from_raw(dent).unwrap();
-    let inode = raw_inode_clone(inode);
-
-    dent.save_inode(inode);
+pub extern "C" fn fs_unlink(at: *const Dentry) -> i32 {
+    map_err_ffi!((|| {
+        let at = Dentry::from_raw(&at);
+        let parent = at.parent();
+        let inode = parent.get_inode()?;
+
+        inode.unlink(inode.as_ref(), &at)
+    })())
 }
 
 #[no_mangle]
-pub extern "C" fn r_get_inode_mode(
-    inode: *const *const dyn Inode, // borrowed
-) -> mode_t {
-    let inode = raw_inode_clone(inode);
-    let idata = inode.idata().lock();
+pub extern "C" fn r_get_inode_mode(mut inode: *const Inode) -> mode_t {
+    let inode = inode_from_raw(&mut inode);
+    let idata = inode.idata.lock();
 
     idata.mode as _
 }
 
 #[no_mangle]
-pub extern "C" fn r_get_inode_size(
-    inode: *const *const dyn Inode, // borrowed
-) -> mode_t {
-    let inode = raw_inode_clone(inode);
-    let idata = inode.idata().lock();
+pub extern "C" fn r_get_inode_size(mut inode: *const Inode) -> mode_t {
+    let inode = inode_from_raw(&mut inode);
+    let idata = inode.idata.lock();
 
     idata.size as _
 }
@@ -327,45 +305,33 @@ extern "C" {
         callback: *const c_void,
         filename: *const c_char,
         filename_len: usize,
-        inode: *const *const dyn Inode,
-        idata: *const InodeData,
-        always_zero: u8,
+        ino: ino_t,
     ) -> i32;
 }
 
 #[no_mangle]
 pub extern "C" fn fs_readdir(
-    file: *const *const dyn Inode, // borrowed
+    mut file: *const Inode, // borrowed
     offset: usize,
     callback: *const c_void,
 ) -> i64 {
-    let inode = raw_inode_clone(file);
-
-    let ret = inode.readdir(
-        offset,
-        &mut move |filename: &str,
-                   ind: &Arc<dyn Inode>,
-                   idata: &InodeData,
-                   zero: u8| {
-            // TODO!!!: CHANGE THIS
-            let handle = Arc::into_raw(ind.clone());
-
-            let ret = unsafe {
-                call_callback(
-                    callback,
-                    filename.as_ptr() as *const c_char,
-                    filename.len(),
-                    &handle,
-                    idata,
-                    zero,
-                )
-            };
-
-            unsafe { Arc::from_raw(handle) };
-
-            Ok(ret)
-        },
-    );
+    let inode = inode_from_raw(&mut file);
+
+    let ret = inode.readdir(inode.as_ref(), offset, &|filename, ino| {
+        let ret = unsafe {
+            call_callback(
+                callback,
+                filename.as_ptr() as *const c_char,
+                filename.len(),
+                ino,
+            )
+        };
+
+        match ret {
+            0 => Ok(()),
+            _ => Err(ret as u32),
+        }
+    });
 
     match ret {
         Ok(n) => n as i64,

+ 11 - 8
src/kernel/vfs/filearr.cc

@@ -2,6 +2,8 @@
 
 #include <assert.h>
 
+#include <types/path.hpp>
+
 #include <kernel/async/lock.hpp>
 #include <kernel/vfs.hpp>
 #include <kernel/vfs/dentry.hpp>
@@ -164,13 +166,13 @@ int filearray::close(int fd) {
 }
 
 static inline std::pair<dentry_pointer, int> _open_file(
-    const fs_context& context, dentry* cwd, types::path_iterator filepath,
-    int flags, mode_t mode) {
+    const fs_context& context, const dentry_pointer& cwd,
+    types::string_view filepath, int flags, mode_t mode) {
     auto [dent, ret] = fs::open(context, cwd, filepath);
     if (!dent)
         return {nullptr, ret};
 
-    if (dent->flags & D_PRESENT) {
+    if (!(r_dentry_is_invalid(dent.get()))) {
         if ((flags & O_CREAT) && (flags & O_EXCL))
             return {nullptr, -EEXIST};
         return {std::move(dent), 0};
@@ -187,8 +189,8 @@ static inline std::pair<dentry_pointer, int> _open_file(
 }
 
 // TODO: file opening permissions check
-int filearray::open(dentry* cwd, types::path_iterator filepath, int flags,
-                    mode_t mode) {
+int filearray::open(const dentry_pointer& cwd, types::string_view filepath,
+                    int flags, mode_t mode) {
     lock_guard lck{pimpl->mtx};
 
     auto [dent, ret] = _open_file(*pimpl->context, cwd, filepath, flags, mode);
@@ -197,7 +199,8 @@ int filearray::open(dentry* cwd, types::path_iterator filepath, int flags,
     if (ret != 0)
         return ret;
 
-    auto filemode = r_get_inode_mode(&dent->inode);
+    auto inode = r_dentry_get_inode(dent.get());
+    auto filemode = r_get_inode_mode(inode);
 
     int fdflag = (flags & O_CLOEXEC) ? FD_CLOEXEC : 0;
 
@@ -218,14 +221,14 @@ int filearray::open(dentry* cwd, types::path_iterator filepath, int flags,
     // truncate file
     if (flags & O_TRUNC) {
         if (fflags.write && S_ISREG(filemode)) {
-            auto ret = fs_truncate(&dent->inode, 0);
+            auto ret = fs_truncate(inode, 0);
             if (ret != 0)
                 return ret;
         }
     }
 
     return pimpl->place_new_file(
-        std::make_shared<regular_file>(fflags, 0, &dent->inode), fdflag);
+        std::make_shared<regular_file>(fflags, 0, inode), fdflag);
 }
 
 int filearray::pipe(int (&pipefd)[2]) {

+ 173 - 94
src/kernel/vfs/inode.rs

@@ -1,22 +1,23 @@
-use core::any::Any;
+use core::{ops::Deref, sync::atomic::AtomicU64};
 
 use alloc::{
     collections::btree_map::{BTreeMap, Entry},
     sync::{Arc, Weak},
 };
 use bindings::{
-    statx, EEXIST, EINVAL, EIO, STATX_ATIME, STATX_BLOCKS, STATX_CTIME,
-    STATX_GID, STATX_INO, STATX_MODE, STATX_MTIME, STATX_NLINK, STATX_SIZE,
-    STATX_TYPE, STATX_UID, S_IFMT,
+    statx, EEXIST, EINVAL, EIO, EISDIR, ENOTDIR, EPERM, STATX_ATIME,
+    STATX_BLOCKS, STATX_CTIME, STATX_GID, STATX_INO, STATX_MODE, STATX_MTIME,
+    STATX_NLINK, STATX_SIZE, STATX_TYPE, STATX_UID, S_IFDIR, S_IFMT,
 };
 
 use super::{
     dentry::Dentry, s_isblk, s_ischr, vfs::Vfs, DevId, ReadDirCallback,
     TimeSpec,
 };
-use crate::prelude::*;
+use crate::{io::Buffer, prelude::*};
 
 pub type Ino = u64;
+pub type AtomicIno = AtomicU64;
 pub type ISize = u64;
 pub type Nlink = u64;
 pub type Uid = u32;
@@ -24,58 +25,174 @@ pub type Gid = u32;
 pub type Mode = u32;
 
 #[repr(C)]
+#[derive(Default)]
 pub struct InodeData {
-    pub ino: Ino,
     pub size: ISize,
     pub nlink: Nlink,
 
+    pub uid: Uid,
+    pub gid: Gid,
+    pub mode: Mode,
+
     pub atime: TimeSpec,
     pub mtime: TimeSpec,
     pub ctime: TimeSpec,
+}
 
-    pub uid: Uid,
-    pub gid: Gid,
-    pub mode: Mode,
+pub struct Inode {
+    pub ino: Ino,
+    pub vfs: Weak<dyn Vfs>,
+
+    pub idata: Mutex<InodeData>,
+    pub ops: Box<dyn InodeOps>,
 }
 
-impl InodeData {
-    pub fn new(ino: Ino) -> Self {
-        Self {
-            ino,
-            size: 0,
-            nlink: 0,
-            atime: TimeSpec::new(),
-            mtime: TimeSpec::new(),
-            ctime: TimeSpec::new(),
-            uid: 0,
-            gid: 0,
-            mode: 0,
-        }
+impl Deref for Inode {
+    type Target = dyn InodeOps;
+
+    fn deref(&self) -> &Self::Target {
+        self.ops.as_ref()
     }
 }
 
 #[allow(unused_variables)]
-pub trait Inode {
-    fn idata(&self) -> &Mutex<InodeData>;
-    fn vfs_weak(&self) -> Weak<Mutex<dyn Vfs>>;
-    fn vfs_strong(&self) -> Option<Arc<Mutex<dyn Vfs>>>;
+pub trait InodeOps: Send + Sync {
     fn as_any(&self) -> &dyn Any;
 
-    fn readdir(
+    fn lookup(
+        &self,
+        dir: &Inode,
+        dentry: &Arc<Dentry>,
+    ) -> KResult<Option<Arc<Inode>>> {
+        if dir.idata.lock().mode & S_IFDIR == 0 {
+            Err(ENOTDIR)
+        } else {
+            Err(EPERM)
+        }
+    }
+
+    fn creat(&self, dir: &Inode, at: &Arc<Dentry>, mode: Mode) -> KResult<()> {
+        if dir.idata.lock().mode & S_IFDIR == 0 {
+            Err(ENOTDIR)
+        } else {
+            Err(EPERM)
+        }
+    }
+
+    fn mkdir(&self, dir: &Inode, at: &Arc<Dentry>, mode: Mode) -> KResult<()> {
+        if dir.idata.lock().mode & S_IFDIR == 0 {
+            Err(ENOTDIR)
+        } else {
+            Err(EPERM)
+        }
+    }
+
+    fn mknod(
+        &self,
+        dir: &Inode,
+        at: &Arc<Dentry>,
+        mode: Mode,
+        dev: DevId,
+    ) -> KResult<()> {
+        if dir.idata.lock().mode & S_IFDIR == 0 {
+            Err(ENOTDIR)
+        } else {
+            Err(EPERM)
+        }
+    }
+
+    fn unlink(&self, dir: &Inode, at: &Arc<Dentry>) -> KResult<()> {
+        if dir.idata.lock().mode & S_IFDIR == 0 {
+            Err(ENOTDIR)
+        } else {
+            Err(EPERM)
+        }
+    }
+
+    fn symlink(
+        &self,
+        dir: &Inode,
+        at: &Arc<Dentry>,
+        target: &[u8],
+    ) -> KResult<()> {
+        if dir.idata.lock().mode & S_IFDIR == 0 {
+            Err(ENOTDIR)
+        } else {
+            Err(EPERM)
+        }
+    }
+
+    fn read(
+        &self,
+        inode: &Inode,
+        buffer: &mut dyn Buffer,
+        offset: usize,
+    ) -> KResult<usize> {
+        if inode.idata.lock().mode & S_IFDIR != 0 {
+            Err(EISDIR)
+        } else {
+            Err(EINVAL)
+        }
+    }
+
+    fn write(
+        &self,
+        inode: &Inode,
+        buffer: &[u8],
+        offset: usize,
+    ) -> KResult<usize> {
+        if inode.idata.lock().mode & S_IFDIR != 0 {
+            Err(EISDIR)
+        } else {
+            Err(EINVAL)
+        }
+    }
+
+    fn devid(&self, inode: &Inode) -> KResult<DevId> {
+        if inode.idata.lock().mode & S_IFDIR != 0 {
+            Err(EISDIR)
+        } else {
+            Err(EINVAL)
+        }
+    }
+
+    fn readlink(
         &self,
+        inode: &Inode,
+        buffer: &mut dyn Buffer,
+    ) -> KResult<usize> {
+        Err(EINVAL)
+    }
+
+    fn truncate(&self, inode: &Inode, length: usize) -> KResult<()> {
+        if inode.idata.lock().mode & S_IFDIR != 0 {
+            Err(EISDIR)
+        } else {
+            Err(EPERM)
+        }
+    }
+
+    fn readdir<'cb, 'r: 'cb>(
+        &'r self,
+        inode: &'r Inode,
         offset: usize,
-        callback: &mut ReadDirCallback,
-    ) -> KResult<usize>;
+        callback: &ReadDirCallback<'cb>,
+    ) -> KResult<usize> {
+        if inode.idata.lock().mode & S_IFDIR == 0 {
+            Err(ENOTDIR)
+        } else {
+            Err(EPERM)
+        }
+    }
 
-    fn statx(&self, stat: &mut statx, mask: u32) -> KResult<()> {
+    fn statx(&self, inode: &Inode, stat: &mut statx, mask: u32) -> KResult<()> {
         let (fsdev, io_blksize) = {
-            let vfs = self.vfs_strong().ok_or(EIO)?;
-            let vfs = vfs.lock();
+            let vfs = inode.vfs.upgrade().ok_or(EIO)?;
             (vfs.fs_devid(), vfs.io_blksize())
         };
-        let devid = self.devid();
+        let devid = self.devid(inode);
 
-        let idata = self.idata().lock();
+        let idata = inode.idata.lock();
 
         if mask & STATX_NLINK != 0 {
             stat.stx_nlink = idata.nlink as _;
@@ -121,7 +238,7 @@ pub trait Inode {
         }
 
         if mask & STATX_INO != 0 {
-            stat.stx_ino = idata.ino as _;
+            stat.stx_ino = inode.ino as _;
             stat.stx_mask |= STATX_INO;
         }
 
@@ -149,83 +266,45 @@ pub trait Inode {
 
         Ok(())
     }
-
-    fn creat(&self, at: &mut Dentry, mode: Mode) -> KResult<()> {
-        Err(EINVAL)
-    }
-
-    fn mkdir(&self, at: &mut Dentry, mode: Mode) -> KResult<()> {
-        Err(EINVAL)
-    }
-
-    fn mknod(&self, at: &mut Dentry, mode: Mode, dev: DevId) -> KResult<()> {
-        Err(EINVAL)
-    }
-
-    fn unlink(&self, at: &mut Dentry) -> KResult<()> {
-        Err(EINVAL)
-    }
-
-    fn symlink(&self, at: &mut Dentry, target: &str) -> KResult<()> {
-        Err(EINVAL)
-    }
-
-    fn read(&self, buffer: &mut [u8], offset: usize) -> KResult<usize> {
-        Err(EINVAL)
-    }
-
-    fn write(&self, buffer: &[u8], offset: usize) -> KResult<usize> {
-        Err(EINVAL)
-    }
-
-    fn devid(&self) -> KResult<DevId> {
-        Err(EINVAL)
-    }
-
-    fn readlink(&self, buffer: &mut [u8]) -> KResult<usize> {
-        Err(EINVAL)
-    }
-
-    fn truncate(&self, length: usize) -> KResult<()> {
-        Err(EINVAL)
-    }
 }
 
-pub struct InodeCache<Fs: Vfs> {
-    cache: BTreeMap<Ino, Arc<dyn Inode>>,
-    vfs: Weak<Mutex<Fs>>,
+pub struct InodeCache<Fs: Vfs + 'static> {
+    cache: BTreeMap<Ino, Arc<Inode>>,
+    vfs: Weak<Fs>,
 }
 
 impl<Fs: Vfs> InodeCache<Fs> {
-    pub fn new() -> Self {
+    pub fn new(vfs: Weak<Fs>) -> Self {
         Self {
             cache: BTreeMap::new(),
-            vfs: Weak::new(),
+            vfs,
         }
     }
 
-    pub fn get_vfs(&self) -> Weak<Mutex<Fs>> {
+    pub fn vfs(&self) -> Weak<Fs> {
         self.vfs.clone()
     }
 
-    pub fn set_vfs(&mut self, vfs: Weak<Mutex<Fs>>) {
-        assert_eq!(self.vfs.upgrade().is_some(), false);
-
-        self.vfs = vfs;
+    pub fn alloc(&self, ino: Ino, ops: Box<dyn InodeOps>) -> Arc<Inode> {
+        Arc::new(Inode {
+            ino,
+            vfs: self.vfs.clone(),
+            idata: Mutex::new(InodeData::default()),
+            ops,
+        })
     }
 
-    pub fn submit(
-        &mut self,
-        ino: Ino,
-        inode: Arc<impl Inode + 'static>,
-    ) -> KResult<Arc<dyn Inode>> {
-        match self.cache.entry(ino) {
-            Entry::Occupied(_) => Err(EEXIST), // TODO: log error to console
-            Entry::Vacant(entry) => Ok(entry.insert(inode).clone()),
+    pub fn submit(&mut self, inode: &Arc<Inode>) -> KResult<()> {
+        match self.cache.entry(inode.ino) {
+            Entry::Occupied(_) => Err(EEXIST),
+            Entry::Vacant(entry) => {
+                entry.insert(inode.clone());
+                Ok(())
+            }
         }
     }
 
-    pub fn get(&self, ino: Ino) -> Option<Arc<dyn Inode>> {
+    pub fn get(&self, ino: Ino) -> Option<Arc<Inode>> {
         self.cache.get(&ino).cloned()
     }
 

+ 7 - 11
src/kernel/vfs/mod.rs

@@ -1,8 +1,7 @@
 use crate::prelude::*;
 
-use alloc::sync::Arc;
-use bindings::{dev_t, S_IFBLK, S_IFCHR, S_IFDIR, S_IFMT, S_IFREG};
-use inode::{Inode, InodeData, Mode};
+use bindings::{dev_t, S_IFBLK, S_IFCHR, S_IFDIR, S_IFLNK, S_IFMT, S_IFREG};
+use inode::{Ino, Mode};
 
 pub mod dentry;
 pub mod ffi;
@@ -19,8 +18,7 @@ pub type DevId = dev_t;
 /// Return 0 if no more entry available
 ///
 /// Otherwise, return bytes to be added to the offset
-pub type ReadDirCallback =
-    dyn FnMut(&str, &Arc<dyn Inode>, &InodeData, u8) -> KResult<i32>;
+pub type ReadDirCallback<'lt> = dyn Fn(&[u8], Ino) -> KResult<()> + 'lt;
 
 pub fn s_isreg(mode: Mode) -> bool {
     (mode & S_IFMT) == S_IFREG
@@ -38,15 +36,13 @@ pub fn s_isblk(mode: Mode) -> bool {
     (mode & S_IFMT) == S_IFBLK
 }
 
+pub fn s_islnk(mode: Mode) -> bool {
+    (mode & S_IFMT) == S_IFLNK
+}
+
 #[derive(Clone, Copy, Default)]
 #[repr(C)]
 pub struct TimeSpec {
     pub sec: u64,
     pub nsec: u64,
 }
-
-impl TimeSpec {
-    pub fn new() -> Self {
-        Self { sec: 0, nsec: 0 }
-    }
-}

+ 48 - 75
src/kernel/vfs/mount.rs

@@ -1,16 +1,13 @@
+use crate::prelude::*;
+
 use alloc::{
     collections::btree_map::{BTreeMap, Entry},
     sync::Arc,
 };
-use bindings::{
-    fs::{D_DIRECTORY, D_MOUNTPOINT, D_PRESENT},
-    EEXIST, ENODEV, ENOENT, ENOTDIR,
-};
-
-use crate::{io::operator_eql_cxx_std_string, prelude::*};
+use bindings::{EEXIST, ENODEV, ENOTDIR};
 
 use super::{
-    dentry::{Dentry, DentryCache, DentryInner},
+    dentry::{dcache, Dentry},
     inode::Inode,
     vfs::Vfs,
     Mutex,
@@ -37,38 +34,32 @@ const MOUNT_FLAGS: [(u64, &str); 6] = [
 static MOUNT_CREATORS: Mutex<BTreeMap<String, Box<dyn MountCreator>>> =
     Mutex::new(BTreeMap::new());
 
-static MOUNTS: Mutex<BTreeMap<Dentry, MountPointData>> =
-    Mutex::new(BTreeMap::new());
+static MOUNTS: Mutex<Vec<(Arc<Dentry>, MountPointData)>> = Mutex::new(vec![]);
 
-static mut ROOT_DENTRY: Option<Dentry> = None;
+static mut ROOTFS: Option<Arc<Dentry>> = None;
 
 pub struct Mount {
-    dcache: Mutex<DentryCache>,
-    vfs: Arc<Mutex<dyn Vfs>>,
-    root: Dentry,
+    vfs: Arc<dyn Vfs>,
+    root: Arc<Dentry>,
 }
 
 impl Mount {
-    pub fn new(vfs: Arc<Mutex<dyn Vfs>>, root_inode: Arc<dyn Inode>) -> Self {
-        let mut dcache = DentryCache::new();
-
-        // register root dentry
-        let mut dent = dcache.alloc();
-
-        dent.save_inode(root_inode);
-        dent.flags = D_DIRECTORY | D_PRESENT;
-
-        dcache.insert_root(&mut dent);
-
-        Self {
-            dcache: Mutex::new(dcache),
+    pub fn new(
+        mp: &Dentry,
+        vfs: Arc<dyn Vfs>,
+        root_inode: Arc<Inode>,
+    ) -> KResult<Self> {
+        let root_dentry = Dentry::create(mp.parent().clone(), mp.name());
+        root_dentry.save_dir(root_inode)?;
+
+        Ok(Self {
             vfs,
-            root: dent,
-        }
+            root: root_dentry,
+        })
     }
 
-    pub fn root(&self) -> Dentry {
-        self.root.clone()
+    pub fn root(&self) -> &Arc<Dentry> {
+        &self.root
     }
 }
 
@@ -81,6 +72,7 @@ pub trait MountCreator: Send + Sync {
         source: &str,
         flags: u64,
         data: &[u8],
+        mp: &Arc<Dentry>,
     ) -> KResult<Mount>;
 }
 
@@ -107,17 +99,13 @@ struct MountPointData {
 }
 
 pub fn do_mount(
-    mut mountpoint: Dentry,
+    mountpoint: &Arc<Dentry>,
     source: &str,
     mountpoint_str: &str,
     fstype: &str,
     flags: u64,
     data: &[u8],
 ) -> KResult<()> {
-    if mountpoint.flags & D_DIRECTORY == 0 {
-        return Err(ENOTDIR);
-    }
-
     let mut flags = flags;
     if flags & MS_NOATIME == 0 {
         flags |= MS_RELATIME;
@@ -127,17 +115,17 @@ pub fn do_mount(
         flags &= !(MS_RELATIME | MS_NOATIME);
     }
 
+    if !mountpoint.is_directory() {
+        return Err(ENOTDIR);
+    }
+
     let mount = {
         let creators = { MOUNT_CREATORS.lock() };
         let creator = creators.get(fstype).ok_or(ENODEV)?;
-        creator.create_mount(source, flags, data)?
+        creator.create_mount(source, flags, data, mountpoint)?
     };
 
-    let mut root = mount.root();
-
-    root.parent = mountpoint.parent;
-    operator_eql_cxx_std_string(&mut root.name, &mountpoint.name);
-    root.hash = mountpoint.hash;
+    let root_dentry = mount.root().clone();
 
     let mpdata = MountPointData {
         mount,
@@ -149,8 +137,8 @@ pub fn do_mount(
 
     {
         let mut mounts = MOUNTS.lock();
-        mountpoint.flags |= D_MOUNTPOINT;
-        mounts.insert(mountpoint.clone(), mpdata);
+        dcache::d_replace(mountpoint, root_dentry);
+        mounts.push((mountpoint.clone(), mpdata));
     }
 
     Ok(())
@@ -186,43 +174,24 @@ pub fn dump_mounts(buffer: &mut dyn core::fmt::Write) {
     }
 }
 
-pub fn get_mountpoint(mnt: &Dentry) -> KResult<Dentry> {
-    let mounts = MOUNTS.lock();
-    let mpdata = mounts.get(mnt).ok_or(ENOENT)?;
-
-    Ok(mpdata.mount.root().clone())
-}
-
-#[no_mangle]
-pub extern "C" fn r_get_mountpoint(mnt: *mut DentryInner) -> *mut DentryInner {
-    let mnt = Dentry::from_raw(mnt).unwrap();
-
-    match get_mountpoint(&mnt) {
-        Ok(mnt) => mnt.leak(),
-        Err(_) => core::ptr::null_mut(),
-    }
-}
-
-#[no_mangle]
-pub extern "C" fn r_get_root_dentry() -> *mut DentryInner {
-    let root = unsafe { ROOT_DENTRY.as_ref().unwrap() };
-
-    root.clone().leak()
-}
-
 pub fn create_rootfs() {
-    let source = String::from("none");
+    let source = String::from("rootfs");
     let fstype = String::from("tmpfs");
     let flags = MS_NOATIME;
 
     let mount = {
         let creators = MOUNT_CREATORS.lock();
         let creator = creators.get(&fstype).ok_or(ENODEV).unwrap();
-        creator.create_mount(&source, flags, &[]).unwrap()
+
+        creator
+            .create_mount(&source, flags, &[], dcache::_looped_droot())
+            .unwrap()
     };
 
-    let root = mount.root();
-    unsafe { ROOT_DENTRY = Some(root.clone()) };
+    let root_dentry = mount.root().clone();
+    dcache::d_add(&root_dentry);
+
+    unsafe { ROOTFS = Some(root_dentry) };
 
     let mpdata = MountPointData {
         mount,
@@ -232,8 +201,12 @@ pub fn create_rootfs() {
         flags,
     };
 
-    {
-        let mut mounts = MOUNTS.lock();
-        mounts.insert(root, mpdata);
-    }
+    MOUNTS
+        .lock()
+        .push((dcache::_looped_droot().clone(), mpdata));
+}
+
+#[no_mangle]
+pub extern "C" fn r_get_root_dentry() -> *const Dentry {
+    unsafe { ROOTFS.as_ref().cloned().map(Arc::into_raw).unwrap() }
 }

+ 1 - 1
src/kernel/vfs/vfs.rs

@@ -3,7 +3,7 @@ use crate::prelude::*;
 use super::DevId;
 
 #[allow(unused_variables)]
-pub trait Vfs {
+pub trait Vfs: Send + Sync {
     fn io_blksize(&self) -> usize;
     fn fs_devid(&self) -> DevId;
     fn as_any(&self) -> &dyn Any;

+ 6 - 0
src/lib.rs

@@ -2,6 +2,8 @@
 #![no_main]
 #![feature(c_size_t)]
 #![feature(concat_idents)]
+#![feature(arbitrary_self_types)]
+#![feature(get_mut_unchecked)]
 extern crate alloc;
 
 #[allow(warnings)]
@@ -9,10 +11,14 @@ mod bindings;
 
 mod driver;
 mod fs;
+mod hash;
 mod io;
 mod kernel;
 mod net;
+mod path;
 mod prelude;
+mod rcu;
+mod sync;
 
 use prelude::*;
 

+ 80 - 0
src/path.rs

@@ -0,0 +1,80 @@
+use crate::prelude::*;
+
+use bindings::ENOENT;
+
+pub struct Path<'lt> {
+    all: &'lt [u8],
+}
+
+pub struct PathIterator<'lt> {
+    rem: &'lt [u8],
+}
+
+impl<'lt> Path<'lt> {
+    pub fn new(all: &'lt [u8]) -> KResult<Self> {
+        if all.is_empty() {
+            Err(ENOENT)
+        } else {
+            Ok(Self { all })
+        }
+    }
+
+    pub fn from_str(all: &'lt str) -> KResult<Self> {
+        Self::new(all.as_bytes())
+    }
+
+    pub fn is_absolute(&self) -> bool {
+        self.all.starts_with(&['/' as u8])
+    }
+
+    pub fn iter(&self) -> PathIterator<'lt> {
+        PathIterator::new(self.all)
+    }
+}
+
+impl<'lt> PathIterator<'lt> {
+    fn new(all: &'lt [u8]) -> Self {
+        Self { rem: all }
+    }
+}
+
+#[derive(Debug)]
+pub enum PathComponent<'lt> {
+    Name(&'lt [u8]),
+    TrailingEmpty,
+    Current,
+    Parent,
+}
+
+impl<'lt> Iterator for PathIterator<'lt> {
+    type Item = PathComponent<'lt>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.rem.is_empty() {
+            return None;
+        }
+
+        let trimmed = self
+            .rem
+            .iter()
+            .position(|&c| c != '/' as u8)
+            .map(|pos| self.rem.split_at(pos).1)
+            .unwrap_or(&[]);
+
+        let next_start = trimmed
+            .iter()
+            .position(|&c| c == '/' as u8)
+            .unwrap_or(trimmed.len());
+
+        let (cur, rem) = trimmed.split_at(next_start);
+
+        self.rem = rem;
+
+        match cur {
+            cur if cur.is_empty() => Some(PathComponent::TrailingEmpty),
+            cur if cur == b"." => Some(PathComponent::Current),
+            cur if cur == b".." => Some(PathComponent::Parent),
+            cur => Some(PathComponent::Name(cur)),
+        }
+    }
+}

+ 54 - 0
src/prelude.rs

@@ -10,6 +10,7 @@ macro_rules! dont_check {
     };
 }
 
+use alloc::sync::Arc;
 #[allow(unused_imports)]
 pub(crate) use dont_check;
 
@@ -24,6 +25,7 @@ pub(crate) use alloc::{boxed::Box, string::String, vec, vec::Vec};
 
 #[allow(unused_imports)]
 pub(crate) use core::{any::Any, fmt::Write, marker::PhantomData, str};
+use core::{mem::ManuallyDrop, ops::Deref};
 
 pub struct Yield;
 
@@ -106,6 +108,20 @@ impl<'a, T: ?Sized> core::ops::DerefMut for MutexNoPreemptionGuard<'a, T> {
     }
 }
 
+impl<'a, T: ?Sized> AsRef<T> for MutexNoPreemptionGuard<'a, T> {
+    #[inline(always)]
+    fn as_ref(&self) -> &T {
+        &*self.data_guard
+    }
+}
+
+impl<'a, T: ?Sized> AsMut<T> for MutexNoPreemptionGuard<'a, T> {
+    #[inline(always)]
+    fn as_mut(&mut self) -> &mut T {
+        &mut *self.data_guard
+    }
+}
+
 #[repr(transparent)]
 pub struct MutexNoPreemption<T: ?Sized> {
     lock: spin::mutex::Mutex<T, spin::Spin>,
@@ -153,4 +169,42 @@ impl<T: ?Sized> MutexNoPreemption<T> {
 
 #[allow(dead_code)]
 pub type RwLock<T> = spin::rwlock::RwLock<T, Yield>;
+pub type RwLockReadGuard<'a, T> = spin::rwlock::RwLockReadGuard<'a, T>;
 pub type Mutex<T> = MutexNoPreemption<T>;
+
+pub struct BorrowedArc<'lt, T: ?Sized> {
+    arc: ManuallyDrop<Arc<T>>,
+    _phantom: PhantomData<&'lt ()>,
+}
+
+impl<'lt, T: ?Sized> BorrowedArc<'lt, T> {
+    pub fn from_raw(ptr: *const T) -> Self {
+        assert!(!ptr.is_null());
+        Self {
+            arc: ManuallyDrop::new(unsafe { Arc::from_raw(ptr) }),
+            _phantom: PhantomData,
+        }
+    }
+
+    pub fn new(ptr: &'lt *const T) -> Self {
+        assert!(!ptr.is_null());
+        Self {
+            arc: ManuallyDrop::new(unsafe { Arc::from_raw(*ptr) }),
+            _phantom: PhantomData,
+        }
+    }
+}
+
+impl<'lt, T: ?Sized> Deref for BorrowedArc<'lt, T> {
+    type Target = Arc<T>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.arc
+    }
+}
+
+impl<'lt, T: ?Sized> AsRef<Arc<T>> for BorrowedArc<'lt, T> {
+    fn as_ref(&self) -> &Arc<T> {
+        &self.arc
+    }
+}

+ 220 - 0
src/rcu.rs

@@ -0,0 +1,220 @@
+use core::{
+    ops::Deref,
+    sync::atomic::{AtomicPtr, Ordering},
+};
+
+use crate::prelude::*;
+
+use alloc::sync::Arc;
+
+pub struct RCUReadGuard<'data, T: 'data> {
+    value: T,
+    guard: RwLockReadGuard<'static, ()>,
+    _phantom: PhantomData<&'data T>,
+}
+
+static READ_GUARD: RwLock<()> = RwLock::new(());
+
+impl<'data, T: 'data> RCUReadGuard<'data, T> {
+    fn lock(value: T) -> Self {
+        Self {
+            value,
+            guard: READ_GUARD.read(),
+            _phantom: PhantomData,
+        }
+    }
+}
+
+impl<'data, T: 'data> Deref for RCUReadGuard<'data, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.value
+    }
+}
+
+fn rcu_sync() {
+    READ_GUARD.write();
+}
+
+pub trait RCUNode<MySelf> {
+    fn rcu_prev(&self) -> &AtomicPtr<MySelf>;
+    fn rcu_next(&self) -> &AtomicPtr<MySelf>;
+}
+
+pub struct RCUList<T: RCUNode<T>> {
+    head: AtomicPtr<T>,
+
+    reader_lock: RwLock<()>,
+    update_lock: Mutex<()>,
+}
+
+impl<T: RCUNode<T>> RCUList<T> {
+    pub const fn new() -> Self {
+        Self {
+            head: AtomicPtr::new(core::ptr::null_mut()),
+            reader_lock: RwLock::new(()),
+            update_lock: Mutex::new(()),
+        }
+    }
+
+    pub fn insert(&self, new_node: Arc<T>) {
+        let _lck = self.update_lock.lock();
+
+        let old_head = self.head.load(Ordering::Acquire);
+        new_node
+            .rcu_prev()
+            .store(core::ptr::null_mut(), Ordering::Release);
+        new_node.rcu_next().store(old_head, Ordering::Release);
+
+        if let Some(old_head) = unsafe { old_head.as_ref() } {
+            old_head.rcu_prev().store(
+                Arc::into_raw(new_node.clone()) as *mut _,
+                Ordering::Release,
+            );
+        }
+
+        self.head
+            .store(Arc::into_raw(new_node) as *mut _, Ordering::Release);
+    }
+
+    pub fn remove(&self, node: Arc<T>) {
+        let _lck = self.update_lock.lock();
+
+        let prev = node.rcu_prev().load(Ordering::Acquire);
+        let next = node.rcu_next().load(Ordering::Acquire);
+
+        if let Some(next) = unsafe { next.as_ref() } {
+            let me = next.rcu_prev().swap(prev, Ordering::AcqRel);
+            debug_assert!(me == Arc::as_ptr(&node) as *mut _);
+            unsafe { Arc::from_raw(me) };
+        }
+
+        {
+            let prev_next = unsafe { prev.as_ref().map(|rcu| rcu.rcu_next()) }
+                .unwrap_or(&self.head);
+
+            let me = prev_next.swap(next, Ordering::AcqRel);
+            debug_assert!(me == Arc::as_ptr(&node) as *mut _);
+            unsafe { Arc::from_raw(me) };
+        }
+
+        let _lck = self.reader_lock.write();
+        node.rcu_prev()
+            .store(core::ptr::null_mut(), Ordering::Release);
+        node.rcu_next()
+            .store(core::ptr::null_mut(), Ordering::Release);
+
+        drop(node);
+    }
+
+    pub fn replace(&self, old_node: &Arc<T>, new_node: Arc<T>) {
+        let _lck = self.update_lock.lock();
+
+        let prev = old_node.rcu_prev().load(Ordering::Acquire);
+        let next = old_node.rcu_next().load(Ordering::Acquire);
+
+        new_node.rcu_prev().store(prev, Ordering::Release);
+        new_node.rcu_next().store(next, Ordering::Release);
+
+        {
+            let prev_next = unsafe { prev.as_ref().map(|rcu| rcu.rcu_next()) }
+                .unwrap_or(&self.head);
+
+            let old = prev_next.swap(
+                Arc::into_raw(new_node.clone()) as *mut _,
+                Ordering::AcqRel,
+            );
+
+            debug_assert!(old == Arc::as_ptr(&old_node) as *mut _);
+            unsafe { Arc::from_raw(old) };
+        }
+
+        if let Some(next) = unsafe { next.as_ref() } {
+            let old = next.rcu_prev().swap(
+                Arc::into_raw(new_node.clone()) as *mut _,
+                Ordering::AcqRel,
+            );
+
+            debug_assert!(old == Arc::as_ptr(&old_node) as *mut _);
+            unsafe { Arc::from_raw(old) };
+        }
+
+        let _lck = self.reader_lock.write();
+        old_node
+            .rcu_prev()
+            .store(core::ptr::null_mut(), Ordering::Release);
+        old_node
+            .rcu_next()
+            .store(core::ptr::null_mut(), Ordering::Release);
+    }
+
+    pub fn iter(&self) -> RCUIterator<T> {
+        let _lck = self.reader_lock.read();
+
+        RCUIterator {
+            // SAFETY: We have a read lock, so the node is still alive.
+            cur: self.head.load(Ordering::SeqCst),
+            _lock: _lck,
+        }
+    }
+}
+
+pub struct RCUIterator<'lt, T: RCUNode<T>> {
+    cur: *const T,
+    _lock: RwLockReadGuard<'lt, ()>,
+}
+
+impl<'lt, T: RCUNode<T>> Iterator for RCUIterator<'lt, T> {
+    type Item = BorrowedArc<'lt, T>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        match unsafe { self.cur.as_ref() } {
+            None => None,
+            Some(real) => {
+                // SAFETY: We have a read lock, so the node is still alive.
+                let ret = self.cur;
+                self.cur = real.rcu_next().load(Ordering::SeqCst);
+
+                Some(BorrowedArc::from_raw(ret))
+            }
+        }
+    }
+}
+
+pub struct RCUPointer<T>(AtomicPtr<T>);
+
+impl<T> RCUPointer<T> {
+    pub fn new_with(value: Arc<T>) -> Self {
+        Self(AtomicPtr::new(Arc::into_raw(value) as *mut _))
+    }
+
+    pub fn empty() -> Self {
+        Self(AtomicPtr::new(core::ptr::null_mut()))
+    }
+
+    pub fn load<'lt>(&self) -> Option<RCUReadGuard<'lt, BorrowedArc<'lt, T>>> {
+        let ptr = self.0.load(Ordering::Acquire);
+
+        if ptr.is_null() {
+            None
+        } else {
+            Some(RCUReadGuard::lock(BorrowedArc::from_raw(ptr)))
+        }
+    }
+
+    pub fn swap(&self, new: Option<Arc<T>>) -> Option<Arc<T>> {
+        let new = new
+            .map(|arc| Arc::into_raw(arc) as *mut T)
+            .unwrap_or(core::ptr::null_mut());
+
+        let old = self.0.swap(new, Ordering::AcqRel);
+
+        if old.is_null() {
+            None
+        } else {
+            rcu_sync();
+            Some(unsafe { Arc::from_raw(old) })
+        }
+    }
+}

+ 26 - 0
src/sync.rs

@@ -0,0 +1,26 @@
+pub struct Locked<T: Sized + Sync, U: ?Sized> {
+    inner: T,
+    guard: *const U,
+}
+
+unsafe impl<T: Sized + Sync, U: ?Sized> Sync for Locked<T, U> {}
+unsafe impl<T: Sized + Sync, U: ?Sized> Send for Locked<T, U> {}
+
+impl<T: Sized + Sync, U: ?Sized> Locked<T, U> {
+    pub fn new(value: T, from: &U) -> Self {
+        Self {
+            inner: value,
+            guard: from,
+        }
+    }
+
+    pub fn access<'lt>(&'lt self, guard: &'lt U) -> &'lt T {
+        assert_eq!(self.guard, guard as *const U, "wrong guard");
+        &self.inner
+    }
+
+    pub fn access_mut<'lt>(&'lt self, guard: &'lt mut U) -> &'lt mut T {
+        assert_eq!(self.guard, guard as *const U, "wrong guard");
+        unsafe { &mut *(&raw const self.inner as *mut T) }
+    }
+}

+ 9 - 6
src/types/elf.cpp

@@ -32,9 +32,11 @@ int types::elf::elf32_load(types::elf::elf32_load_data& d) {
     if (!exec)
         return -ENOENT;
 
+    auto* inode = fs::r_dentry_get_inode(exec.get());
+
     types::elf::elf32_header hdr{};
     auto n_read =
-        fs_read(&exec->inode, (char*)&hdr, sizeof(types::elf::elf32_header), 0,
+        fs::fs_read(inode, (char*)&hdr, sizeof(types::elf::elf32_header), 0,
                  sizeof(types::elf::elf32_header));
 
     if (n_read != sizeof(types::elf::elf32_header))
@@ -47,7 +49,7 @@ 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_read(&exec->inode, (char*)phents.data(), phents_size, hdr.phoff,
+    n_read = fs_read(inode, (char*)phents.data(), phents_size, hdr.phoff,
                       phents_size);
 
     // broken file or I/O error
@@ -55,7 +57,7 @@ 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_read(&exec->inode, (char*)shents.data(), shents_size, hdr.shoff,
+    n_read = fs_read(inode, (char*)shents.data(), shents_size, hdr.shoff,
                       shents_size);
 
     // broken file or I/O error
@@ -84,7 +86,8 @@ int types::elf::elf32_load(types::elf::elf32_load_data& d) {
 
             args.vaddr = vaddr;
             args.length = flen;
-            args.file_inode = &exec->inode;
+            // TODO!!!!!!!: get ownership
+            args.file_inode = inode;
             args.file_offset = fileoff;
 
             args.flags = MM_MAPPED;
@@ -176,8 +179,8 @@ int types::elf::elf32_load(types::elf::elf32_load_data& d) {
     // push argc
     __user_push32(sp, args.size());
 
-    // rename current thread
-    current_thread->name = exec->name;
+    // TODO!!!: rename current thread
+    current_thread->name = "[thread]";
 
     return 0;
 }