Sfoglia il codice sorgente

refactor: rewrite fs submodule with rust

greatbridf 2 mesi fa
parent
commit
b43277c46c
57 ha cambiato i file con 3358 aggiunte e 2532 eliminazioni
  1. 0 11
      CMakeLists.txt
  2. 1 1
      Cargo.toml
  3. 4 1
      build.rs
  4. 10 1
      gblibc/src/errno.c
  5. 29 4
      gblibc/src/init.c
  6. 24 6
      gblibc/src/stdlib.c
  7. 17 41
      gblibc/src/string.c
  8. 0 174
      include/fs/fat.hpp
  9. 3 1
      include/kernel/mem/mm_list.hpp
  10. 6 3
      include/kernel/mem/vm_area.hpp
  11. 0 3
      include/kernel/process.hpp
  12. 0 30
      include/kernel/procfs.hpp
  13. 36 39
      include/kernel/vfs.hpp
  14. 37 5
      include/kernel/vfs/dentry.hpp
  15. 3 4
      include/kernel/vfs/file.hpp
  16. 0 28
      include/kernel/vfs/inode.hpp
  17. 0 89
      include/kernel/vfs/vfs.hpp
  18. 1 1
      include/types/hash.hpp
  19. 26 10
      init_script.sh
  20. 2 0
      src/driver/e1000e/defs.rs
  21. 0 260
      src/fs/fat.cpp
  22. 480 0
      src/fs/fat32.rs
  23. 3 0
      src/fs/mod.rs
  24. 0 259
      src/fs/procfs.cc
  25. 329 0
      src/fs/procfs.rs
  26. 0 296
      src/fs/tmpfs.cc
  27. 631 0
      src/fs/tmpfs.rs
  28. 75 1
      src/io.rs
  29. 2 0
      src/kernel.rs
  30. 8 0
      src/kernel/async/lock.cc
  31. 67 0
      src/kernel/block.rs
  32. 4 3
      src/kernel/console.rs
  33. 0 1
      src/kernel/mem/mm_list.cc
  34. 2 19
      src/kernel/mem/paging.cc
  35. 65 55
      src/kernel/mem/paging.rs
  36. 8 0
      src/kernel/mem/phys.rs
  37. 7 17
      src/kernel/process.cpp
  38. 39 19
      src/kernel/syscall/fileops.cc
  39. 1 1
      src/kernel/syscall/mount.cc
  40. 40 178
      src/kernel/vfs.cpp
  41. 46 32
      src/kernel/vfs/dentry.cc
  42. 203 0
      src/kernel/vfs/dentry.rs
  43. 403 0
      src/kernel/vfs/ffi.rs
  44. 4 4
      src/kernel/vfs/filearr.cc
  45. 0 92
      src/kernel/vfs/inode.cc
  46. 240 0
      src/kernel/vfs/inode.rs
  47. 51 0
      src/kernel/vfs/mod.rs
  48. 239 0
      src/kernel/vfs/mount.rs
  49. 0 153
      src/kernel/vfs/vfs.cc
  50. 10 0
      src/kernel/vfs/vfs.rs
  51. 9 10
      src/lib.rs
  52. 156 0
      src/prelude.rs
  53. 4 5
      src/types/elf.cpp
  54. 1 4
      user-space-program/CMakeLists.txt
  55. 32 9
      user-space-program/init.c
  56. 0 140
      user-space-program/lazybox.c
  57. 0 522
      user-space-program/sh.c

+ 0 - 11
CMakeLists.txt

@@ -39,9 +39,6 @@ set(BOOTLOADER_SOURCES src/boot.s
                        )
 
 set(KERNEL_MAIN_SOURCES src/dev/builtin-chardev.cc
-                        src/fs/fat.cpp
-                        src/fs/tmpfs.cc
-                        src/fs/procfs.cc
                         src/kinit.cpp
                         src/kernel/async/waitlist.cc
                         src/kernel/async/lock.cc
@@ -70,15 +67,12 @@ set(KERNEL_MAIN_SOURCES src/dev/builtin-chardev.cc
                         src/kernel/user/thread_local.cc
                         src/kernel/vfs/dentry.cc
                         src/kernel/vfs/filearr.cc
-                        src/kernel/vfs/inode.cc
-                        src/kernel/vfs/vfs.cc
                         src/kernel/signal.cpp
                         src/net/ethernet.cc
                         src/types/crc.cc
                         src/types/elf.cpp
                         src/types/libstdcpp.cpp
                         include/defs.hpp
-                        include/fs/fat.hpp
                         include/kernel/async/waitlist.hpp
                         include/kernel/async/lock.hpp
                         include/kernel/tty.hpp
@@ -97,8 +91,6 @@ set(KERNEL_MAIN_SOURCES src/dev/builtin-chardev.cc
                         include/kernel/vfs/dentry.hpp
                         include/kernel/vfs/file.hpp
                         include/kernel/vfs/filearr.hpp
-                        include/kernel/vfs/inode.hpp
-                        include/kernel/vfs/vfs.hpp
                         include/kernel/vga.hpp
                         include/kernel/signal.hpp
                         include/kernel/task/forward.hpp
@@ -158,10 +150,7 @@ add_custom_target(boot.img
     COMMAND mcopy -i boot.img@@1M ${CMAKE_BINARY_DIR}/user-space-program/interrupt-test.out ::int
     COMMAND mcopy -i boot.img@@1M ${CMAKE_BINARY_DIR}/user-space-program/stack-test.out ::stack
     COMMAND mcopy -i boot.img@@1M ${CMAKE_BINARY_DIR}/user-space-program/init.out ::init
-    COMMAND mcopy -i boot.img@@1M ${CMAKE_BINARY_DIR}/user-space-program/sh.out ::sh
     COMMAND mcopy -i boot.img@@1M ${CMAKE_BINARY_DIR}/user-space-program/priv-test.out ::priv
-    COMMAND mcopy -i boot.img@@1M ${CMAKE_BINARY_DIR}/user-space-program/lazybox.out ::lazybox
-    COMMAND mcopy -i boot.img@@1M ${CMAKE_BINARY_DIR}/user-space-program/lazybox.out ::pwd
     COMMAND mcopy -i boot.img@@1M ${CMAKE_SOURCE_DIR}/busybox-minimal ::busybox_
     COMMAND mcopy -i boot.img@@1M ${CMAKE_SOURCE_DIR}/busybox ::busybox
     COMMAND mcopy -i boot.img@@1M ${CMAKE_SOURCE_DIR}/init_script.sh ::initsh

+ 1 - 1
Cargo.toml

@@ -14,4 +14,4 @@ bindgen = "0.70.1"
 
 [profile.dev]
 panic = "abort"
-opt-level = "s"
+opt-level = "z"

+ 4 - 1
build.rs

@@ -2,7 +2,10 @@ fn main() {
     println!("cargo:rustc-link-search=native=./build/gblibstdc++");
     println!("cargo:rustc-link-lib=static=gblibstdc++");
 
-    let headers = ["include/kernel/process.hpp", "include/kernel/hw/pci.hpp"];
+    let headers = [
+        "include/kernel/process.hpp",
+        "include/kernel/hw/pci.hpp",
+    ];
 
     let bindings = bindgen::Builder::default()
         .use_core()

+ 10 - 1
gblibc/src/errno.c

@@ -8,6 +8,15 @@ int* __errno_location(void)
     return &__errno;
 }
 
+static size_t _strlen(const char* str)
+{
+    size_t len = 0;
+    while (str[len] != '\0') {
+        len++;
+    }
+    return len;
+}
+
 void
 __attribute__((noreturn))
 __attribute__((weak))
@@ -15,7 +24,7 @@ __stack_chk_fail(void)
 {
     const char* msg = "***** stack overflow detected *****\n"
                       "quiting...\n";
-    write(STDERR_FILENO, msg, strlen(msg));
+    write(STDERR_FILENO, msg, _strlen(msg));
     exit(-1);
 }
 

+ 29 - 4
gblibc/src/init.c

@@ -2,7 +2,6 @@
 #include <priv-vars.h>
 #include <stdlib.h>
 #include <syscall.h>
-#include <string.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <list.h>
@@ -11,6 +10,32 @@ FILE* stdout;
 FILE* stdin;
 FILE* stderr;
 
+#define BYTES_PER_MAX_COPY_UNIT (sizeof(uint32_t) / sizeof(uint8_t))
+static void* _memset(void* _dst, int c, size_t n)
+{
+    uint8_t* dst = (uint8_t*)_dst;
+    c &= 0xff;
+    int cc = (c + (c << 8) + (c << 16) + (c << 24));
+    for (size_t i = 0; i < n / BYTES_PER_MAX_COPY_UNIT; ++i) {
+        *(uint32_t*)dst = cc;
+        dst += BYTES_PER_MAX_COPY_UNIT;
+    }
+    for (size_t i = 0; i < (n % BYTES_PER_MAX_COPY_UNIT); ++i) {
+        *((char*)dst++) = c;
+    }
+    return dst;
+}
+
+static char* strchr(const char* s, int c)
+{
+    while (*s) {
+        if (*s == c)
+            return (char*)s;
+        ++s;
+    }
+    return NULL;
+}
+
 list_head* __io_files_location(void)
 {
     static list_head __io_files;
@@ -58,7 +83,7 @@ void __init_gblibc(int argc, char** argv, char** envp)
     // stdout
     node = NEWNODE(FILE);
     stdout = &NDDATA(*node, FILE);
-    memset(stdout, 0x00, sizeof(FILE));
+    _memset(stdout, 0x00, sizeof(FILE));
 
     stdout->fd = STDOUT_FILENO;
     stdout->flags = FILE_WRITE;
@@ -70,7 +95,7 @@ void __init_gblibc(int argc, char** argv, char** envp)
     // stdin
     node = NEWNODE(FILE);
     stdin = &NDDATA(*node, FILE);
-    memset(stdin, 0x00, sizeof(FILE));
+    _memset(stdin, 0x00, sizeof(FILE));
 
     stdin->fd = STDIN_FILENO;
     stdin->flags = FILE_READ;
@@ -82,7 +107,7 @@ void __init_gblibc(int argc, char** argv, char** envp)
     // stderr
     node = NEWNODE(FILE);
     stderr = &NDDATA(*node, FILE);
-    memset(stderr, 0x00, sizeof(FILE));
+    _memset(stderr, 0x00, sizeof(FILE));
 
     stderr->fd = STDERR_FILENO;
     stderr->flags = FILE_WRITE;

+ 24 - 6
gblibc/src/stdlib.c

@@ -7,6 +7,24 @@
 #include <unistd.h>
 #include <string.h>
 
+#define BYTES_PER_MAX_COPY_UNIT (sizeof(uint32_t) / sizeof(uint8_t))
+
+static void* _memcpy(void* _dst, const void* _src, size_t n)
+{
+    void* orig_dst = _dst;
+    uint8_t* dst = (uint8_t*)_dst;
+    const uint8_t* src = (const uint8_t*)_src;
+    for (size_t i = 0; i < n / BYTES_PER_MAX_COPY_UNIT; ++i) {
+        *(uint32_t*)dst = *(uint32_t*)src;
+        dst += BYTES_PER_MAX_COPY_UNIT;
+        src += BYTES_PER_MAX_COPY_UNIT;
+    }
+    for (size_t i = 0; i < (n % BYTES_PER_MAX_COPY_UNIT); ++i) {
+        *((char*)dst++) = *((char*)src++);
+    }
+    return orig_dst;
+}
+
 int atoi(const char* str)
 {
     int ret = 0;
@@ -155,7 +173,7 @@ void* realloc(void* ptr, size_t newsize)
     if (!newptr)
         return NULL;
     
-    memcpy(newptr, ptr, oldsize);
+    _memcpy(newptr, ptr, oldsize);
     free(ptr);
     return newptr;
 }
@@ -170,9 +188,9 @@ void free(void* ptr)
 static inline void _swap(void* a, void* b, size_t sz)
 {
     void* tmp = alloca(sz);
-    memcpy(tmp, a, sz);
-    memcpy(a, b, sz);
-    memcpy(b, tmp, sz);
+    _memcpy(tmp, a, sz);
+    _memcpy(a, b, sz);
+    _memcpy(b, tmp, sz);
 }
 
 void qsort(void* arr, size_t len, size_t sz, comparator_t cmp) {
@@ -180,7 +198,7 @@ void qsort(void* arr, size_t len, size_t sz, comparator_t cmp) {
         return;
 
     char* pivot = alloca(sz);
-    memcpy(pivot, arr + sz * (rand() % len), sz);
+    _memcpy(pivot, arr + sz * (rand() % len), sz);
 
     int i = 0, j = 0, k = len;
     while (i < k) {
@@ -252,7 +270,7 @@ int setenv(const char* name, const char* value, int overwrite)
         if (!newarr)
             return -1;
         
-        memcpy(newarr, environ, sizeof(char*) * environ_size / 2);
+        _memcpy(newarr, environ, sizeof(char*) * environ_size / 2);
         free(environ);
         environ = newarr;
     }

+ 17 - 41
gblibc/src/string.c

@@ -6,26 +6,7 @@
 
 #define BYTES_PER_MAX_COPY_UNIT (sizeof(uint32_t) / sizeof(uint8_t))
 
-int memcmp(const void* ptr1, const void* ptr2, size_t num)
-{
-    while (num--) {
-        if (*(const char*)ptr1 < *(const char*)ptr2)
-            return -1;
-        else if (*(const char*)ptr1 > *(const char*)ptr2)
-            return 1;
-    }
-    return 0;
-}
-
-void* memmove(void* dst, const void* src, size_t n)
-{
-    void* orig_dst = dst;
-    while (n--)
-        *(char*)(dst++) = *(const char*)(src++);
-    return orig_dst;
-}
-
-void* memcpy(void* _dst, const void* _src, size_t n)
+static void* _memcpy(void* _dst, const void* _src, size_t n)
 {
     void* orig_dst = _dst;
     uint8_t* dst = (uint8_t*)_dst;
@@ -41,12 +22,7 @@ void* memcpy(void* _dst, const void* _src, size_t n)
     return orig_dst;
 }
 
-void* mempcpy(void* dst, const void* src, size_t n)
-{
-    return memcpy(dst, src, n) + n;
-}
-
-void* memset(void* _dst, int c, size_t n)
+static void* _memset(void* _dst, int c, size_t n)
 {
     uint8_t* dst = (uint8_t*)_dst;
     c &= 0xff;
@@ -61,7 +37,7 @@ void* memset(void* _dst, int c, size_t n)
     return dst;
 }
 
-size_t strlen(const char* str)
+static size_t _strlen(const char* str)
 {
     size_t n = 0;
     while (*(str++) != '\0')
@@ -82,7 +58,7 @@ char* strchr(const char* str, int c)
 
 char* strrchr(const char* str, int c)
 {
-    const char* p = str + strlen(str) - 1;
+    const char* p = str + _strlen(str) - 1;
     while (p >= str) {
         if (*p == c)
             return (char*)p;
@@ -96,23 +72,23 @@ char* strchrnul(const char* str, int c)
     char* ret = strchr(str, c);
     if (ret)
         return ret;
-    return (char*)str + strlen(str);
+    return (char*)str + _strlen(str);
 }
 
 char* strcpy(char* dst, const char* src)
 {
-    return memcpy(dst, src, strlen(src) + 1);
+    return _memcpy(dst, src, _strlen(src) + 1);
 }
 
 char* strncpy(char* dst, const char* src, size_t n)
 {
-    size_t len = strlen(src);
+    size_t len = _strlen(src);
 
     if (len < n) {
-        memset(dst + len, 0x00, n - len);
-        memcpy(dst, src, len);
+        _memset(dst + len, 0x00, n - len);
+        _memcpy(dst, src, len);
     } else {
-        memcpy(dst, src, n);
+        _memcpy(dst, src, n);
     }
 
     return dst;
@@ -120,18 +96,18 @@ char* strncpy(char* dst, const char* src, size_t n)
 
 char* stpcpy(char* restrict dst, const char* restrict src)
 {
-    return memcpy(dst, src, strlen(src) + 1) + strlen(src);
+    return _memcpy(dst, src, _strlen(src) + 1) + _strlen(src);
 }
 
 char* stpncpy(char* restrict dst, const char* restrict src, size_t n)
 {
-    size_t len = strlen(src);
+    size_t len = _strlen(src);
 
     if (len < n) {
-        memset(dst + len, 0x00, n - len);
-        memcpy(dst, src, len);
+        _memset(dst + len, 0x00, n - len);
+        _memcpy(dst, src, len);
     } else {
-        memcpy(dst, src, n);
+        _memcpy(dst, src, n);
     }
 
     return dst + len;
@@ -259,14 +235,14 @@ char* strerror(int errnum)
 
 char* strndup(const char* str, size_t n)
 {
-    size_t len = strlen(str);
+    size_t len = _strlen(str);
     if (len > n)
         len = n;
     char* ret = malloc(len + 1);
     if (!ret)
         return NULL;
     
-    memcpy(ret, str, len);
+    _memcpy(ret, str, len);
     ret[len] = 0;
     return ret;
 }

+ 0 - 174
include/fs/fat.hpp

@@ -1,174 +0,0 @@
-#pragma once
-
-#include <map>
-#include <vector>
-
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <kernel/vfs.hpp>
-
-namespace fs::fat {
-
-using cluster_t = uint32_t;
-
-// for FAT32
-struct PACKED old_boot_sector {
-    uint8_t jmp_instruction[3];
-    char oem_name[8];
-    // usually 512
-    uint16_t bytes_per_sector;
-    uint8_t sectors_per_cluster;
-    // 32 for FAT32
-    uint16_t reserved_sectors;
-    // usually 2
-    uint8_t fat_copies;
-    // 0 for FAT32
-    uint16_t root_directory_entries;
-    // valid before FAT32
-    uint16_t _sectors_cnt;
-    // 0xf8 for hard disk
-    uint8_t type;
-    // valid before FAT32
-    uint16_t _sectors_per_fat;
-    // 12
-    uint16_t sectors_per_track;
-    // 2
-    uint16_t heads;
-    // 0
-    uint16_t hidden_sectors;
-};
-
-// for FAT32
-struct PACKED ext_boot_sector {
-    struct old_boot_sector old;
-    // 0
-    uint16_t hidden_sector_ext;
-    uint32_t sectors_cnt;
-    uint32_t sectors_per_fat;
-    uint16_t mirror_flags;
-    uint16_t fs_version;
-    // 2
-    cluster_t root_directory;
-    // 1
-    uint16_t fs_info_sector;
-    // usually at 6, 0x0000 or 0xffff if none
-    uint16_t backup_boot_sector;
-    uint8_t _reserved[12];
-    // for int $0x13
-    uint8_t drive_number;
-    uint8_t _reserved_for_current_head;
-    // 0x29
-    uint8_t ext_signature;
-    uint32_t serial_number;
-    char label[11];
-    char fs_type[8];
-    uint8_t _reserved_blank[420];
-    // 0x55, 0xaa
-    uint16_t magic;
-};
-
-struct PACKED fs_info_sector {
-    // 0x41615252
-    uint32_t signature_one;
-    uint8_t _reserved[480];
-    // 0x61417272
-    uint32_t signature_two;
-    // may be incorrect
-    uint32_t free_clusters;
-    // hint only
-    uint32_t next_free_cluster;
-    uint8_t _reserved_two[12];
-    // 0xaa550000
-    uint32_t sector_signature;
-};
-
-struct PACKED directory_entry {
-    char filename[8];
-    char extension[3];
-    struct PACKED {
-        uint8_t ro : 1;
-        uint8_t hidden : 1;
-        uint8_t system : 1;
-        uint8_t volume_label : 1;
-        uint8_t subdir : 1;
-        uint8_t archive : 1;
-        uint8_t _reserved : 2;
-    } attributes;
-    uint8_t _reserved;
-    uint8_t c_time_date[5];
-    uint16_t access_date;
-    uint16_t cluster_hi;
-    uint8_t m_time_date[4];
-    uint16_t cluster_lo;
-    uint32_t size;
-};
-
-// TODO: deallocate inodes when dentry is destroyed
-class fat32 : public virtual fs::vfs {
-   private:
-    constexpr static size_t SECTOR_SIZE = 512;
-    constexpr static cluster_t EOC = 0xffffff8;
-
-   private:
-    uint32_t sector_cnt;
-    uint32_t sectors_per_fat;
-    uint32_t serial_number;
-    uint32_t free_clusters;
-    uint32_t next_free_cluster_hint;
-    cluster_t root_dir;
-    cluster_t data_region_offset;
-    uint16_t reserved_sectors;
-    uint8_t fat_copies;
-    uint8_t sectors_per_cluster;
-    char label[12];
-    std::vector<cluster_t> fat;
-
-    // TODO: dirty flag
-    struct buf_object {
-        char* data;
-        int ref;
-    };
-    std::map<cluster_t, buf_object> buf;
-
-    // buf MUST be larger than 512 bytes
-    void _raw_read_sector(void* buf, uint32_t sector_no);
-
-    // buf MUST be larger than 4096 bytes
-    void _raw_read_cluster(void* buf, cluster_t no);
-
-    ssize_t _read_sector_range(void* buf, size_t buf_size,
-                               uint32_t sector_offset, size_t sector_cnt);
-
-    // buffered version, release_cluster(cluster_no) after used
-    char* read_cluster(cluster_t no);
-    void release_cluster(cluster_t no);
-
-    static constexpr cluster_t cl(const inode* ind) { return ind->ino; }
-
-    static inline cluster_t _rearrange(directory_entry* d) {
-        return (((cluster_t)d->cluster_hi) << 16) + d->cluster_lo;
-    }
-
-    static inline size_t _write_buf_n(char* buf, size_t buf_size,
-                                      const char* src, size_t n) {
-        if (n <= buf_size) {
-            memcpy(buf, src, n);
-            return n;
-        } else {
-            memcpy(buf, src, buf_size);
-            return buf_size;
-        }
-    }
-
-   public:
-    explicit fat32(dev_t device);
-
-    virtual ssize_t read(inode* file, char* buf, size_t buf_size, size_t count,
-                         off_t offset) override;
-    virtual ssize_t readdir(fs::inode* dir, size_t offset,
-                            const vfs::filldir_func& callback) override;
-};
-
-}; // namespace fs::fat

+ 3 - 1
include/kernel/mem/mm_list.hpp

@@ -7,6 +7,8 @@
 
 #include <stdint.h>
 
+#include <kernel/vfs/dentry.hpp>
+
 namespace kernel::mem {
 
 constexpr uintptr_t KERNEL_SPACE_START = 0x8000000000000000ULL;
@@ -44,7 +46,7 @@ class mm_list {
 
         unsigned long flags;
 
-        fs::inode* file_inode;
+        const fs::rust_inode_handle* file_inode;
         // MUSE BE aligned to 4kb boundary
         std::size_t file_offset;
     };

+ 6 - 3
include/kernel/mem/vm_area.hpp

@@ -3,6 +3,7 @@
 #include <stdint.h>
 
 #include <kernel/vfs.hpp>
+#include <kernel/vfs/dentry.hpp>
 
 namespace kernel::mem {
 
@@ -19,7 +20,7 @@ struct vm_area {
 
     unsigned long flags;
 
-    fs::inode* mapped_file;
+    const fs::rust_inode_handle* mapped_file;
     std::size_t file_offset;
 
     constexpr bool is_avail(uintptr_t ostart, uintptr_t oend) const noexcept {
@@ -38,7 +39,8 @@ struct vm_area {
     }
 
     constexpr vm_area(uintptr_t start, unsigned long flags, uintptr_t end,
-                      fs::inode* mapped_file = nullptr, std::size_t offset = 0)
+                      const fs::rust_inode_handle* mapped_file = nullptr,
+                      std::size_t offset = 0)
         : start{start}
         , end{end}
         , flags{flags}
@@ -46,7 +48,8 @@ struct vm_area {
         , file_offset{offset} {}
 
     constexpr vm_area(uintptr_t start, unsigned long flags,
-                      fs::inode* mapped_file = nullptr, std::size_t offset = 0)
+                      const fs::rust_inode_handle* mapped_file = nullptr,
+                      std::size_t offset = 0)
         : start{start}
         , end{start}
         , flags{flags}

+ 0 - 3
include/kernel/process.hpp

@@ -2,14 +2,11 @@
 
 #include <list>
 #include <map>
-#include <memory>
-#include <queue>
 #include <set>
 #include <tuple>
 #include <utility>
 
 #include <assert.h>
-#include <errno.h>
 #include <fcntl.h>
 #include <stdint.h>
 #include <sys/types.h>

+ 0 - 30
include/kernel/procfs.hpp

@@ -1,30 +0,0 @@
-#pragma once
-
-#include <defs.hpp>
-#include <string>
-#include <vector>
-
-#include <sys/types.h>
-
-namespace kernel::procfs {
-
-using read_fn = std::function<isize(u8*, usize)>;
-using write_fn = std::function<isize(const u8*, usize)>;
-
-struct procfs_file {
-    std::string name;
-    ino_t ino;
-
-    read_fn read;
-    write_fn write;
-    std::vector<procfs_file>* children;
-};
-
-const procfs_file* root();
-
-const procfs_file* find(const procfs_file* parent, std::string name);
-const procfs_file* mkdir(const procfs_file* parent, std::string name);
-const procfs_file* create(const procfs_file* parent, std::string name,
-                          read_fn read, write_fn write);
-
-} // namespace kernel::procfs

+ 36 - 39
include/kernel/vfs.hpp

@@ -1,7 +1,5 @@
 #pragma once
 
-#include <memory>
-
 #include <bits/alltypes.h>
 #include <stdint.h>
 #include <sys/stat.h>
@@ -9,13 +7,12 @@
 
 #include <types/path.hpp>
 
+#include <kernel/mem/paging.hpp>
 #include <kernel/vfs/dentry.hpp>
 #include <kernel/vfs/file.hpp>
-#include <kernel/vfs/inode.hpp>
-#include <kernel/vfs/vfs.hpp>
 
 #define NODE_MAJOR(node) (((node) >> 8) & 0xFFU)
-#define NODE_MINOR(node) ((node)&0xFFU)
+#define NODE_MINOR(node) ((node) & 0xFFU)
 
 namespace fs {
 
@@ -67,28 +64,9 @@ struct fs_context {
     dentry_pointer root;
 };
 
-struct mount_data {
-    fs::vfs* fs;
-    std::string source;
-    std::string mount_point;
-    std::string fstype;
-    unsigned long flags;
-};
-
-inline std::map<struct dentry*, mount_data> mounts;
-
 int register_block_device(dev_t node, const blkdev_ops& ops);
 int register_char_device(dev_t node, const chrdev_ops& ops);
 
-// return value: pointer to created vfs object
-// 1. const char*: source, such as "/dev/sda" or "proc"
-// 2. unsigned long: flags, such as MS_RDONLY | MS_RELATIME
-// 3. const void*: data, for filesystem use, such as "uid=1000"
-using create_fs_func_t =
-    std::function<vfs*(const char*, unsigned long, const void*)>;
-
-int register_fs(const char* name, create_fs_func_t);
-
 void partprobe();
 
 ssize_t block_device_read(dev_t node, char* buf, size_t buf_size, size_t offset,
@@ -99,21 +77,40 @@ ssize_t block_device_write(dev_t node, const char* buf, size_t offset,
 ssize_t char_device_read(dev_t node, char* buf, size_t buf_size, size_t n);
 ssize_t char_device_write(dev_t node, const char* buf, size_t n);
 
-int creat(struct dentry* at, mode_t mode);
-int mkdir(struct dentry* at, mode_t mode);
-int mknod(struct dentry* at, mode_t mode, dev_t sn);
-int unlink(struct dentry* at);
-int symlink(struct dentry* at, const char* target);
-
-int statx(struct inode* inode, struct statx* stat, unsigned int mask);
-int readlink(struct inode* inode, char* buf, size_t buf_size);
-int truncate(struct inode* file, size_t size);
-size_t read(struct inode* file, char* buf, size_t buf_size, size_t offset,
-            size_t n);
-size_t write(struct inode* file, const char* buf, size_t offset, size_t n);
-
-int mount(dentry* mnt, const char* source, const char* mount_point,
-          const char* fstype, unsigned long flags, const void* data);
+extern "C" int fs_creat(struct dentry* at, mode_t mode);
+extern "C" int fs_mkdir(struct dentry* at, mode_t mode);
+extern "C" int fs_mknod(struct dentry* at, mode_t mode, dev_t sn);
+extern "C" int fs_unlink(struct dentry* at);
+extern "C" int fs_symlink(struct dentry* at, const char* target);
+
+extern "C" int fs_statx(const struct rust_inode_handle* inode,
+                        struct statx* stat, unsigned int mask);
+extern "C" int fs_readlink(const struct rust_inode_handle* inode, char* buf,
+                           size_t buf_size);
+extern "C" int fs_truncate(const struct rust_inode_handle* file, size_t size);
+extern "C" size_t fs_read(const struct rust_inode_handle* file, char* buf,
+                          size_t buf_size, size_t offset, size_t n);
+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)>;
+
+extern "C" ssize_t fs_readdir(const struct rust_inode_handle* file,
+                              size_t offset,
+                              const readdir_callback_fn* callback);
+
+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" struct dentry* r_get_root_dentry();
 
 #define current_open(...)                                             \
     fs::open(current_process->fs_context, current_process->cwd.get(), \

+ 37 - 5
include/kernel/vfs/dentry.hpp

@@ -1,25 +1,48 @@
 #pragma once
 
-#include <list>
 #include <string>
 
+#include <bits/alltypes.h>
+
 #include <types/hash.hpp>
 #include <types/path.hpp>
 
 #include <kernel/async/lock.hpp>
-#include <kernel/vfs/inode.hpp>
 
 namespace fs {
 static constexpr unsigned long D_PRESENT = 1 << 0;
 static constexpr unsigned long D_DIRECTORY = 1 << 1;
 static constexpr unsigned long D_LOADED = 1 << 2;
 static constexpr unsigned long D_MOUNTPOINT = 1 << 3;
+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 dcache* cache;
-    vfs* fs;
+    struct rust_vfs_handle fs;
+    struct rust_inode_handle inode;
 
-    struct inode* inode;
+    struct dcache* cache;
     struct dentry* parent;
 
     // list head
@@ -63,3 +86,12 @@ 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);

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

@@ -42,14 +42,13 @@ class pipe : public types::non_copyable {
 };
 
 struct file {
-    mode_t mode; // stores the file type in the same format as inode::mode
     struct file_flags {
         uint32_t read : 1;
         uint32_t write : 1;
         uint32_t append : 1;
     } flags{};
 
-    file(mode_t mode, file_flags flags) : mode(mode), flags(flags) {}
+    file(file_flags flags) : flags(flags) {}
 
     virtual ~file() = default;
 
@@ -83,9 +82,9 @@ struct file {
 struct regular_file : public virtual file {
     virtual ~regular_file() = default;
     std::size_t cursor{};
-    inode* ind{};
+    const rust_inode_handle* ind{};
 
-    regular_file(file_flags flags, size_t cursor, inode* ind);
+    regular_file(file_flags flags, size_t cursor, const 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;

+ 0 - 28
include/kernel/vfs/inode.hpp

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

+ 0 - 89
include/kernel/vfs/vfs.hpp

@@ -1,89 +0,0 @@
-#pragma once
-
-#include <defs.hpp>
-#include <functional>
-#include <map>
-
-#include <stdint.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <kernel/vfs/dentry.hpp>
-#include <kernel/vfs/inode.hpp>
-
-namespace fs {
-
-class vfs {
-   public:
-    using filldir_func = std::function<ssize_t(const char*, inode*, u8)>;
-
-   private:
-    struct dcache m_dcache;
-    struct dentry* m_root{};
-    std::map<ino_t, inode> m_inodes;
-
-   protected:
-    dev_t m_device;
-    size_t m_io_blksize;
-
-   protected:
-    vfs(dev_t device, size_t io_blksize);
-
-    inode* alloc_inode(ino_t ino);
-
-    void free_inode(ino_t ino);
-    inode* get_inode(ino_t ino);
-    void register_root_node(inode* root);
-
-   public:
-    static std::pair<vfs*, int> create(const char* source, const char* fstype,
-                                       unsigned long flags, const void* data);
-
-    vfs(const vfs&) = delete;
-    vfs& operator=(const vfs&) = delete;
-    vfs(vfs&&) = delete;
-    vfs& operator=(vfs&&) = delete;
-
-    struct dentry* root() const noexcept;
-    dev_t fs_device() const noexcept;
-    size_t io_blksize() const noexcept;
-
-    int mount(dentry* mnt, const char* source, const char* mount_point,
-              const char* fstype, unsigned long flags, const void* data);
-
-    // directory operations
-    virtual int creat(struct inode* dir, dentry* at, mode_t mode);
-    virtual int mkdir(struct inode* dir, dentry* at, mode_t mode);
-    virtual int mknod(struct inode* dir, dentry* at, mode_t mode, dev_t device);
-    virtual int unlink(struct inode* dir, dentry* at);
-
-    virtual int symlink(struct inode* dir, dentry* at, const char* target);
-
-    // metadata operations
-    int statx(inode* ind, struct statx* st, unsigned int mask);
-
-    // file operations
-    virtual ssize_t read(inode* file, char* buf, size_t buf_size, size_t count,
-                         off_t offset);
-    virtual ssize_t write(inode* file, const char* buf, size_t count,
-                          off_t offset);
-
-    virtual dev_t i_device(inode* ind);
-    virtual int readlink(inode* file, char* buf, size_t buf_size);
-    virtual int truncate(inode* file, size_t size);
-
-    // directory operations
-
-    // parameter 'length' in callback:
-    // if 0, 'name' should be null terminated
-    // else, 'name' size
-    //
-    // @return
-    // return -1 if an error occurred
-    // return 0 if no more entry available
-    // otherwise, return bytes to be added to the offset
-    virtual ssize_t readdir(inode* dir, size_t offset,
-                            const filldir_func& callback) = 0;
-};
-
-} // namespace fs

+ 1 - 1
include/types/hash.hpp

@@ -19,7 +19,7 @@ constexpr hash_t hash(uint64_t val, int bits) {
     return (val * GOLDEN_RATIO_64) >> (64 - bits);
 }
 
-inline hash_t hash_ptr(void* p, int bits) {
+inline hash_t hash_ptr(const void* p, int bits) {
     return hash(std::bit_cast<uintptr_t>(p), bits);
 }
 

+ 26 - 10
init_script.sh

@@ -2,25 +2,41 @@
 
 BUSYBOX=/mnt/busybox
 
-$BUSYBOX mkdir -p /dev
+freeze() {
+    echo "an error occurred while executing '''$@''', freezing..." > /dev/console
 
-$BUSYBOX mknod -m 666 /dev/console c 5 1
-$BUSYBOX mknod -m 666 /dev/null c 1 3
-$BUSYBOX mknod -m 666 /dev/zero c 1 5
-$BUSYBOX mknod -m 666 /dev/sda b 8 0
-$BUSYBOX mknod -m 666 /dev/sda1 b 8 1
+    while true; do
+        true
+    done
+}
+
+do_or_freeze() {
+    if $@; then
+        return
+    fi
+
+    freeze $@
+}
+
+do_or_freeze $BUSYBOX mkdir -p /dev
+
+do_or_freeze $BUSYBOX mknod -m 666 /dev/console c 5 1
+do_or_freeze $BUSYBOX mknod -m 666 /dev/null c 1 3
+do_or_freeze $BUSYBOX mknod -m 666 /dev/zero c 1 5
+do_or_freeze $BUSYBOX mknod -m 666 /dev/sda b 8 0
+do_or_freeze $BUSYBOX mknod -m 666 /dev/sda1 b 8 1
 
 echo -n -e "deploying busybox... " > /dev/console
 
-$BUSYBOX mkdir -p /bin
-$BUSYBOX --install -s /bin
+do_or_freeze $BUSYBOX mkdir -p /bin
+do_or_freeze $BUSYBOX --install -s /bin
 
 export PATH="/bin"
 
 echo ok > /dev/console
 
-mkdir -p /etc /root /proc
-mount -t procfs proc proc
+do_or_freeze mkdir -p /etc /root /proc
+do_or_freeze mount -t procfs proc proc
 
 cat > /etc/passwd <<EOF
 root:x:0:0:root:/root:/mnt/busybox sh

+ 2 - 0
src/driver/e1000e/defs.rs

@@ -1,3 +1,5 @@
+#![allow(dead_code)]
+
 // Register names
 // Control register
 pub const REG_CTRL: u32 = 0x00000;

+ 0 - 260
src/fs/fat.cpp

@@ -1,260 +0,0 @@
-#include <algorithm>
-#include <string>
-
-#include <assert.h>
-#include <ctype.h>
-#include <stdint.h>
-#include <stdio.h>
-
-#include <types/allocator.hpp>
-
-#include <fs/fat.hpp>
-#include <kernel/mem/paging.hpp>
-#include <kernel/mem/phys.hpp>
-#include <kernel/module.hpp>
-#include <kernel/vfs.hpp>
-
-#define VFAT_FILENAME_LOWERCASE (0x08)
-#define VFAT_EXTENSION_LOWERCASE (0x10)
-
-using namespace kernel::kmod;
-
-namespace fs::fat {
-
-// buf MUST be larger than 512 bytes
-void fat32::_raw_read_sector(void* buf, uint32_t sector_no) {
-    auto nread = _read_sector_range(buf, SECTOR_SIZE, sector_no, 1);
-
-    assert((size_t)nread == SECTOR_SIZE);
-}
-
-// buf MUST be larger than 4096 bytes
-void fat32::_raw_read_cluster(void* buf, cluster_t no) {
-    // data cluster start from cluster #2
-    no -= 2;
-
-    auto nread = _read_sector_range(
-        buf, sectors_per_cluster * SECTOR_SIZE,
-        data_region_offset + no * sectors_per_cluster, sectors_per_cluster);
-
-    assert((size_t)nread == SECTOR_SIZE * sectors_per_cluster);
-}
-
-ssize_t fat32::_read_sector_range(void* _buf, size_t buf_size,
-                                  uint32_t sector_offset, size_t sector_cnt) {
-    buf_size &= ~(SECTOR_SIZE - 1);
-
-    sector_cnt = std::min(sector_cnt, buf_size / SECTOR_SIZE);
-
-    auto* buf = (char*)_buf;
-
-    auto n =
-        block_device_read(m_device, buf, buf_size, sector_offset * SECTOR_SIZE,
-                          sector_cnt * SECTOR_SIZE);
-
-    return n;
-}
-
-char* fat32::read_cluster(cluster_t no) {
-    auto iter = buf.find(no);
-    if (iter) {
-        auto& [idx, buf] = *iter;
-        ++buf.ref;
-        return buf.data;
-    }
-    // TODO: page buffer class
-    using namespace kernel::mem;
-    using namespace paging;
-    assert(sectors_per_cluster * SECTOR_SIZE <= 0x1000);
-
-    char* data = physaddr<char>{page_to_pfn(alloc_page())};
-    _raw_read_cluster(data, no);
-    buf.emplace(no, buf_object{data, 1});
-
-    return data;
-}
-
-void fat32::release_cluster(cluster_t no) {
-    auto iter = buf.find(no);
-    if (iter)
-        --iter->second.ref;
-}
-
-ssize_t fat32::readdir(inode* dir, size_t offset,
-                       const vfs::filldir_func& filldir) {
-    cluster_t next = cl(dir);
-    for (size_t i = 0; i < (offset / (sectors_per_cluster * SECTOR_SIZE));
-         ++i) {
-        if (next >= EOC)
-            return 0;
-        next = fat[next];
-    }
-    size_t nread = 0;
-    do {
-        char* buf = read_cluster(next);
-        auto* d = reinterpret_cast<directory_entry*>(buf) +
-                  (offset % (sectors_per_cluster * SECTOR_SIZE)) /
-                      sizeof(directory_entry);
-        offset = 0;
-        auto* end =
-            d + (sectors_per_cluster * SECTOR_SIZE / sizeof(directory_entry));
-        for (; d < end && d->filename[0]; ++d) {
-            if (d->attributes.volume_label) {
-                nread += sizeof(directory_entry);
-                continue;
-            }
-
-            ino_t ino = _rearrange(d);
-            auto* ind = get_inode(ino);
-            if (!ind) {
-                mode_t mode = 0777;
-                if (d->attributes.subdir)
-                    mode |= S_IFDIR;
-                else
-                    mode |= S_IFREG;
-
-                ind = alloc_inode(ino);
-                ind->size = d->size;
-                ind->mode = mode;
-                ind->nlink = d->attributes.subdir ? 2 : 1;
-            }
-
-            std::string fname;
-            for (int i = 0; i < 8; ++i) {
-                if (d->filename[i] == ' ')
-                    break;
-                if (d->_reserved & VFAT_FILENAME_LOWERCASE)
-                    fname += tolower(d->filename[i]);
-                else
-                    fname += toupper(d->filename[i]);
-            }
-            if (d->extension[0] != ' ')
-                fname += '.';
-            for (int i = 1; i < 3; ++i) {
-                if (d->extension[i] == ' ')
-                    break;
-                if (d->_reserved & VFAT_EXTENSION_LOWERCASE)
-                    fname += tolower(d->extension[i]);
-                else
-                    fname += toupper(d->extension[i]);
-            }
-            auto ret = filldir(fname.c_str(), ind, 0);
-
-            if (ret != 0) {
-                release_cluster(next);
-                return nread;
-            }
-
-            nread += sizeof(directory_entry);
-        }
-        release_cluster(next);
-        next = fat[next];
-    } while (next < EOC);
-    return nread;
-}
-
-fat32::fat32(dev_t _device) : vfs(_device, 4096), label{} {
-    auto* buf = new char[SECTOR_SIZE];
-    _raw_read_sector(buf, 0);
-
-    auto* info = reinterpret_cast<ext_boot_sector*>(buf);
-
-    sector_cnt = info->sectors_cnt;
-    sectors_per_fat = info->sectors_per_fat;
-    sectors_per_cluster = info->old.sectors_per_cluster;
-    serial_number = info->serial_number;
-    root_dir = info->root_directory;
-    reserved_sectors = info->old.reserved_sectors;
-    fat_copies = info->old.fat_copies;
-
-    data_region_offset = reserved_sectors + fat_copies * sectors_per_fat;
-
-    // read file allocation table
-    fat.resize(SECTOR_SIZE * sectors_per_fat / sizeof(cluster_t));
-    _read_sector_range(fat.data(), SECTOR_SIZE * sectors_per_fat,
-                       reserved_sectors, sectors_per_fat);
-
-    int i = 0;
-    while (i < 11 && info->label[i] != 0x20) {
-        label[i] = info->label[i];
-        ++i;
-    }
-    label[i] = 0x00;
-
-    _raw_read_sector(buf, info->fs_info_sector);
-
-    auto* fsinfo = reinterpret_cast<fs_info_sector*>(buf);
-    free_clusters = fsinfo->free_clusters;
-    next_free_cluster_hint = fsinfo->next_free_cluster;
-
-    delete[] buf;
-
-    size_t _root_dir_clusters = 1;
-    cluster_t next = root_dir;
-    while ((next = fat[next]) < EOC)
-        ++_root_dir_clusters;
-
-    auto* n = alloc_inode(root_dir);
-    n->size = _root_dir_clusters * sectors_per_cluster * SECTOR_SIZE;
-    n->mode = S_IFDIR | 0777;
-    n->nlink = 2;
-
-    register_root_node(n);
-}
-
-ssize_t fat32::read(inode* file, char* buf, size_t buf_size, size_t n,
-                    off_t offset) {
-    uint32_t cluster_size = SECTOR_SIZE * sectors_per_cluster;
-    size_t orig_n = n;
-
-    for (cluster_t cno = cl(file); n && cno < EOC; cno = fat[cno]) {
-        if (offset >= cluster_size) {
-            offset -= cluster_size;
-            continue;
-        }
-
-        auto* data = read_cluster(cno);
-        data += offset;
-
-        auto to_copy = std::min(n, (size_t)(cluster_size - offset));
-        auto ncopied = _write_buf_n(buf, buf_size, data, to_copy);
-
-        buf += ncopied, n -= ncopied;
-
-        release_cluster(cno);
-        if (ncopied != to_copy)
-            break;
-
-        offset = 0;
-    }
-
-    return orig_n - n;
-}
-
-static fat32* create_fat32(const char* source, unsigned long, const void*) {
-    // TODO: flags
-    // TODO: parse source
-    (void)source;
-    return new fat32(fs::make_device(8, 1));
-}
-
-class fat32_module : public virtual kmod {
-   public:
-    fat32_module() : kmod("fat32") {}
-    ~fat32_module() {
-        // TODO: unregister filesystem
-    }
-
-    virtual int init() override {
-        int ret = fs::register_fs("fat32", create_fat32);
-
-        if (ret != 0)
-            return ret;
-
-        return 0;
-    }
-};
-
-} // namespace fs::fat
-
-INTERNAL_MODULE(fat32, fs::fat::fat32_module);

+ 480 - 0
src/fs/fat32.rs

@@ -0,0 +1,480 @@
+use alloc::{
+    sync::{Arc, Weak},
+    vec::Vec,
+};
+use bindings::{EINVAL, EIO, S_IFDIR, S_IFREG};
+
+use crate::{
+    io::copy_offset_count,
+    kernel::{
+        block::{make_device, BlockDevice, BlockDeviceRequest},
+        mem::{paging::Page, phys::PhysPtr},
+        vfs::{
+            inode::{Ino, Inode, InodeCache, InodeData},
+            mount::{register_filesystem, Mount, MountCreator},
+            vfs::Vfs,
+            DevId, ReadDirCallback, TimeSpec,
+        },
+    },
+    prelude::*,
+    KResult,
+};
+
+const EOC: ClusterNo = 0x0FFFFFF8;
+
+/// Convert a mutable reference to a slice of bytes
+/// This is a safe wrapper around `core::slice::from_raw_parts_mut`
+///
+fn as_slice<T>(object: &mut [T]) -> &mut [u8] {
+    unsafe {
+        core::slice::from_raw_parts_mut(
+            object.as_mut_ptr() as *mut u8,
+            object.len() * core::mem::size_of::<T>(),
+        )
+    }
+}
+
+/// Convert a slice of bytes to a mutable reference
+///
+fn as_object<T>(slice: &[u8]) -> &T {
+    assert_eq!(slice.len(), core::mem::size_of::<T>());
+    unsafe { &*(slice.as_ptr() as *const T) }
+}
+
+type ClusterNo = u32;
+
+const ATTR_RO: u8 = 0x01;
+const ATTR_HIDDEN: u8 = 0x02;
+const ATTR_SYSTEM: u8 = 0x04;
+const ATTR_VOLUME_ID: u8 = 0x08;
+const ATTR_DIRECTORY: u8 = 0x10;
+const ATTR_ARCHIVE: u8 = 0x20;
+
+const RESERVED_FILENAME_LOWERCASE: u8 = 0x08;
+
+#[repr(C, packed)]
+struct FatDirectoryEntry {
+    name: [u8; 8],
+    extension: [u8; 3],
+    attr: u8,
+    reserved: u8,
+    create_time_tenth: u8,
+    create_time: u16,
+    create_date: u16,
+    access_date: u16,
+    cluster_high: u16,
+    modify_time: u16,
+    modify_date: u16,
+    cluster_low: u16,
+    size: u32,
+}
+
+#[repr(C, packed)]
+struct Bootsector {
+    jmp: [u8; 3],
+    oem: [u8; 8],
+    bytes_per_sector: u16,
+    sectors_per_cluster: u8,
+    reserved_sectors: u16,
+    fat_copies: u8,
+    root_entries: u16,   // should be 0 for FAT32
+    _total_sectors: u16, // outdated
+    media: u8,
+    _sectors_per_fat: u16, // outdated
+    sectors_per_track: u16,
+    heads: u16,
+    hidden_sectors: u32,
+    total_sectors: u32,
+    sectors_per_fat: u32,
+    flags: u16,
+    fat_version: u16,
+    root_cluster: ClusterNo,
+    fsinfo_sector: u16,
+    backup_bootsector: u16,
+    _reserved: [u8; 12],
+    drive_number: u8,
+    _reserved2: u8,
+    ext_sig: u8,
+    serial: u32,
+    volume_label: [u8; 11],
+    fs_type: [u8; 8],
+    bootcode: [u8; 420],
+    mbr_signature: u16,
+}
+
+/// # Lock order
+/// 1. FatFs
+/// 2. FatTable
+/// 3. Inodes
+///
+struct FatFs {
+    device: BlockDevice,
+    icache: Mutex<InodeCache<FatFs>>,
+    sectors_per_cluster: u8,
+    rootdir_cluster: ClusterNo,
+    data_start: u64,
+    fat: Mutex<Vec<ClusterNo>>,
+    volume_label: String,
+}
+
+impl FatFs {
+    // /// Read a sector
+    // fn read_sector(&self, sector: u64, buf: &mut [u8]) -> KResult<()> {
+    //     assert_eq!(buf.len(), 512);
+    //     let mut rq = BlockDeviceRequest {
+    //         sector,
+    //         count: 1,
+    //         buffer: Page::alloc_one(),
+    //     };
+    //     self.read(&mut rq)?;
+
+    //     buf.copy_from_slice(rq.buffer.as_cached().as_slice(512));
+
+    //     Ok(())
+    // }
+
+    fn read_cluster(&self, cluster: ClusterNo, buf: &mut [u8]) -> KResult<()> {
+        let cluster = cluster - 2;
+
+        let mut rq = BlockDeviceRequest {
+            sector: self.data_start as u64
+                + cluster as u64 * self.sectors_per_cluster as u64,
+            count: self.sectors_per_cluster as u64,
+            buffer: buf,
+        };
+        self.device.read(&mut rq)?;
+
+        Ok(())
+    }
+}
+
+impl FatFs {
+    pub fn create(
+        device: DevId,
+    ) -> KResult<(Arc<Mutex<Self>>, Arc<dyn Inode>)> {
+        let mut fatfs = Self {
+            device: BlockDevice::new(device),
+            icache: Mutex::new(InodeCache::new()),
+            sectors_per_cluster: 0,
+            rootdir_cluster: 0,
+            data_start: 0,
+            fat: Mutex::new(Vec::new()),
+            volume_label: String::new(),
+        };
+
+        let mut info = [0u8; 512];
+
+        let info = {
+            let mut rq = BlockDeviceRequest {
+                sector: 0,
+                count: 1,
+                // buffer: Page::alloc_one(),
+                buffer: &mut info,
+            };
+            fatfs.device.read(&mut rq)?;
+
+            as_object::<Bootsector>(&info)
+        };
+
+        fatfs.sectors_per_cluster = info.sectors_per_cluster;
+        fatfs.rootdir_cluster = info.root_cluster;
+        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 mut rq = BlockDeviceRequest {
+                sector: info.reserved_sectors as u64,
+                count: info.sectors_per_fat as u64,
+                buffer: unsafe {
+                    core::slice::from_raw_parts_mut(
+                        fat.as_mut_ptr() as *mut _,
+                        fat.len() * core::mem::size_of::<ClusterNo>(),
+                    )
+                },
+            };
+            fatfs.device.read(&mut rq)?;
+        }
+
+        fatfs.volume_label = String::from(
+            str::from_utf8(&info.volume_label)
+                .map_err(|_| EINVAL)?
+                .trim_end_matches(char::from(' ')),
+        );
+
+        let root_dir_cluster_count = {
+            let fat = fatfs.fat.lock();
+            let mut next = fatfs.rootdir_cluster;
+            let mut count = 1;
+            loop {
+                next = fat[next as usize];
+                if next >= EOC {
+                    break;
+                }
+                count += 1;
+            }
+
+            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))?
+        };
+
+        Ok((fatfs, root_inode))
+    }
+}
+
+impl Vfs for FatFs {
+    fn io_blksize(&self) -> usize {
+        4096
+    }
+
+    fn fs_devid(&self) -> DevId {
+        self.device.devid()
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+}
+
+struct FatInode {
+    idata: Mutex<InodeData>,
+    vfs: Weak<Mutex<FatFs>>,
+}
+
+impl Inode for FatInode {
+    fn idata(&self) -> &Mutex<InodeData> {
+        &self.idata
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+
+    fn read(
+        &self,
+        mut buffer: &mut [u8],
+        mut count: usize,
+        mut offset: usize,
+    ) -> KResult<usize> {
+        let vfs = self.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.lock();
+        let fat = vfs.fat.lock();
+
+        let cluster_size = vfs.sectors_per_cluster as usize * 512;
+        let mut cno = {
+            let idata = self.idata.lock();
+            idata.ino as ClusterNo
+        };
+
+        while offset >= cluster_size {
+            cno = fat[cno as usize];
+            offset -= cluster_size;
+
+            if cno >= EOC {
+                return Ok(0);
+            }
+        }
+
+        let page_buffer = Page::alloc_one();
+        let page_buffer = page_buffer
+            .as_cached()
+            .as_mut_slice::<u8>(page_buffer.len());
+
+        let orig_count = count;
+        while count != 0 {
+            vfs.read_cluster(cno, page_buffer)?;
+
+            let ncopied = copy_offset_count(page_buffer, buffer, offset, count);
+            offset = 0;
+
+            if ncopied == 0 {
+                break;
+            }
+
+            count -= ncopied;
+            buffer = &mut buffer[ncopied..];
+
+            cno = fat[cno as usize];
+            if cno >= EOC {
+                break;
+            }
+        }
+
+        Ok(orig_count - count)
+    }
+
+    fn readdir(
+        &self,
+        offset: usize,
+        callback: &mut ReadDirCallback,
+    ) -> KResult<usize> {
+        let vfs = self.vfs.upgrade().ok_or(EIO)?;
+        let vfs = vfs.lock();
+
+        let fat = vfs.fat.lock();
+
+        let idata = self.idata.lock();
+        let mut next = idata.ino as ClusterNo;
+
+        let skip = offset / 512 / vfs.sectors_per_cluster as usize;
+        let mut offset = offset % (512 * vfs.sectors_per_cluster as usize);
+        for _ in 0..skip {
+            if next >= EOC {
+                return Ok(0);
+            }
+            next = fat[next as usize];
+        }
+        if next >= EOC {
+            return Ok(0);
+        }
+
+        let mut nread = 0;
+        let buffer = Page::alloc_one();
+        let buffer = buffer.as_cached().as_mut_slice::<FatDirectoryEntry>(
+            vfs.sectors_per_cluster as usize * 512
+                / core::mem::size_of::<FatDirectoryEntry>(),
+        );
+        loop {
+            vfs.read_cluster(next, as_slice(buffer))?;
+            let start = offset / core::mem::size_of::<FatDirectoryEntry>();
+            let end = vfs.sectors_per_cluster as usize * 512
+                / core::mem::size_of::<FatDirectoryEntry>();
+            offset = 0;
+
+            for entry in buffer.iter().skip(start).take(end - start) {
+                if entry.attr & ATTR_VOLUME_ID != 0 {
+                    nread += core::mem::size_of::<FatDirectoryEntry>();
+                    continue;
+                }
+
+                let cluster_high = (entry.cluster_high as u32) << 16;
+                let ino = (entry.cluster_low as u32 | cluster_high) as Ino;
+
+                let name = {
+                    let mut name = String::new();
+                    name += str::from_utf8(&entry.name)
+                        .map_err(|_| EINVAL)?
+                        .trim_end_matches(char::from(' '));
+
+                    if entry.extension[0] != ' ' as u8 {
+                        name.push('.');
+                    }
+
+                    name += str::from_utf8(&entry.extension)
+                        .map_err(|_| EINVAL)?
+                        .trim_end_matches(char::from(' '));
+
+                    if entry.reserved & RESERVED_FILENAME_LOWERCASE != 0 {
+                        name.make_ascii_lowercase();
+                    }
+                    name
+                };
+
+                let inode = {
+                    let mut icache = vfs.icache.lock();
+
+                    match icache.get(ino) {
+                        Some(inode) => inode,
+                        None => {
+                            let is_directory = entry.attr & ATTR_DIRECTORY != 0;
+                            let inode = Arc::new(FatInode {
+                                idata: Mutex::new(InodeData {
+                                    ino,
+                                    mode: 0o777
+                                        | if is_directory {
+                                            S_IFDIR
+                                        } else {
+                                            S_IFREG
+                                        },
+                                    nlink: if is_directory { 2 } else { 1 },
+                                    size: entry.size as u64,
+                                    atime: TimeSpec { sec: 0, nsec: 0 },
+                                    mtime: TimeSpec { sec: 0, nsec: 0 },
+                                    ctime: TimeSpec { sec: 0, nsec: 0 },
+                                    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 += core::mem::size_of::<FatDirectoryEntry>();
+            }
+            next = fat[next as usize];
+            if next >= EOC {
+                return Ok(nread);
+            }
+        }
+    }
+
+    fn vfs_weak(&self) -> Weak<Mutex<dyn Vfs>> {
+        self.vfs.clone()
+    }
+
+    fn vfs_strong(&self) -> Option<Arc<Mutex<dyn Vfs>>> {
+        match self.vfs.upgrade() {
+            Some(vfs) => Some(vfs),
+            None => None,
+        }
+    }
+}
+
+struct FatMountCreator;
+
+impl MountCreator for FatMountCreator {
+    fn create_mount(
+        &self,
+        _source: &str,
+        _flags: u64,
+        _data: &[u8],
+    ) -> KResult<Mount> {
+        let (fatfs, root_inode) = FatFs::create(make_device(8, 1))?;
+
+        Ok(Mount::new(fatfs, root_inode))
+    }
+}
+
+pub fn init() {
+    register_filesystem("fat32", Box::new(FatMountCreator)).unwrap();
+}

+ 3 - 0
src/fs/mod.rs

@@ -0,0 +1,3 @@
+pub mod fat32;
+pub mod procfs;
+pub mod tmpfs;

+ 0 - 259
src/fs/procfs.cc

@@ -1,259 +0,0 @@
-#include <map>
-
-#include <errno.h>
-#include <stdint.h>
-#include <sys/mount.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <kernel/async/lock.hpp>
-#include <kernel/hw/timer.hpp>
-#include <kernel/mem/paging.hpp>
-#include <kernel/mem/phys.hpp>
-#include <kernel/module.hpp>
-#include <kernel/process.hpp>
-#include <kernel/procfs.hpp>
-#include <kernel/vfs.hpp>
-#include <kernel/vfs/inode.hpp>
-#include <kernel/vfs/vfs.hpp>
-
-using namespace kernel::kmod;
-using namespace kernel::procfs;
-using fs::inode, fs::make_device;
-
-struct mount_flags_opt {
-    unsigned long flag;
-    const char* name;
-};
-
-static struct mount_flags_opt mount_opts[] = {
-    {MS_NOSUID, ",nosuid"},     {MS_NODEV, ",nodev"},
-    {MS_NOEXEC, ",noexec"},     {MS_NOATIME, ",noatime"},
-    {MS_RELATIME, ",relatime"}, {MS_LAZYTIME, ",lazytime"},
-};
-
-static std::string get_mount_opts(unsigned long mnt_flags) {
-    std::string retval;
-
-    if (mnt_flags & MS_RDONLY)
-        retval += "ro";
-    else
-        retval += "rw";
-
-    for (const auto& opt : mount_opts) {
-        if (mnt_flags & opt.flag)
-            retval += opt.name;
-    }
-
-    return retval;
-}
-
-static isize mounts_read(u8* page, usize bufsize) {
-    auto orig_bufsize = bufsize;
-
-    for (const auto& [_, mdata] : fs::mounts) {
-        // TODO: get vfs options
-        auto mount_flags = get_mount_opts(mdata.flags);
-
-        isize nwrote = snprintf((char*)page, bufsize, "%s %s %s %s 0 0\n",
-                              mdata.source.c_str(), mdata.mount_point.c_str(),
-                              mdata.fstype.c_str(), mount_flags.c_str());
-        if (nwrote < 0)
-            return nwrote;
-
-        assert((usize)nwrote < bufsize);
-
-        bufsize -= nwrote;
-        page += nwrote;
-    }
-
-    return orig_bufsize - bufsize;
-}
-
-static isize schedstat_read(u8* page, usize bufsize) {
-    auto orig_bufsize = bufsize;
-
-    int nw = snprintf((char*)page, bufsize, "%d\n",
-                      kernel::hw::timer::current_ticks());
-    if (nw < 0)
-        return nw;
-
-    assert((usize)nw < bufsize);
-    bufsize -= nw, page += nw;
-
-    for (const auto& proc : *procs) {
-        for (const auto& thd : proc.second.thds) {
-            int nwrote = snprintf((char*)page, bufsize, "%d %x %d\n",
-                                  proc.first, thd.tid(), thd.elected_times);
-            if (nwrote < 0)
-                return nwrote;
-
-            assert((usize)nwrote < bufsize);
-
-            bufsize -= nwrote;
-            page += nwrote;
-        }
-    }
-
-    return orig_bufsize - bufsize;
-}
-
-namespace kernel::procfs {
-
-static procfs_file* s_root;
-static ino_t s_next_ino = 1;
-
-static mode_t _get_mode(const procfs_file& file) {
-    if (file.children)
-        return S_IFDIR | 0755;
-
-    mode_t mode = S_IFREG;
-    if (file.read)
-        mode |= 0444;
-    if (file.write)
-        mode |= 0200;
-
-    return mode;
-}
-
-class procfs : public virtual fs::vfs {
-   private:
-    std::string source;
-
-    procfs(const char* _source)
-        : vfs(make_device(0, 10), 4096), source{_source} {
-        assert(s_root);
-
-        auto* ind = alloc_inode(s_root->ino);
-        ind->fs_data = s_root;
-        ind->mode = _get_mode(*s_root);
-
-        register_root_node(ind);
-    }
-
-   public:
-    static int init() {
-        auto children = std::make_unique<std::vector<procfs_file>>();
-        auto procfsFile = std::make_unique<procfs_file>();
-
-        procfsFile->name = "[root]";
-        procfsFile->ino = 0;
-        procfsFile->read = [](u8*, usize) { return -EISDIR; };
-        procfsFile->write = [](const u8*, usize) { return -EISDIR; };
-        procfsFile->children = children.release();
-        s_root = procfsFile.release();
-
-        kernel::procfs::create(s_root, "mounts", mounts_read, nullptr);
-        kernel::procfs::create(s_root, "schedstat", schedstat_read, nullptr);
-
-        return 0;
-    }
-
-    static procfs* create(const char* source, unsigned long, const void*) {
-        return new procfs(source);
-    }
-
-    ssize_t read(inode* file, char* buf, size_t buf_size, size_t n,
-                 off_t offset) override {
-        if (offset < 0)
-            return -EINVAL;
-
-        n = std::min(n, buf_size);
-
-        auto pFile = (procfs_file*)file->fs_data;
-
-        if (!pFile->read)
-            return -EACCES;
-        if (pFile->children)
-            return -EISDIR;
-
-        using namespace kernel::mem;
-        using namespace kernel::mem::paging;
-        auto* page = alloc_page();
-        auto pPageBuffer = physaddr<u8>(page_to_pfn(page));
-
-        ssize_t nread = pFile->read(pPageBuffer, 4096);
-        if (nread < offset) {
-            free_page(page);
-
-            return nread < 0 ? nread : 0;
-        }
-
-        n = std::min(n, (usize)nread - offset);
-        memcpy(buf, pPageBuffer + offset, n);
-
-        free_page(page);
-        return n;
-    }
-
-    ssize_t readdir(inode* dir, size_t offset,
-                    const filldir_func& callback) override {
-        auto* pFile = (procfs_file*)dir->fs_data;
-        if (!pFile->children)
-            return -ENOTDIR;
-
-        const auto& children = *pFile->children;
-
-        usize cur = offset;
-        for (; cur < pFile->children->size(); ++cur) {
-            auto& file = children[cur];
-            auto* inode = get_inode(file.ino);
-            if (!inode) {
-                inode = alloc_inode(file.ino);
-
-                inode->fs_data = (void*)&file;
-                inode->mode = _get_mode(file);
-            }
-
-            int ret = callback(file.name.c_str(), inode, 0);
-            if (ret != 0)
-                break;
-        }
-
-        return cur - offset;
-    }
-};
-
-const procfs_file* root() { return s_root; }
-
-const procfs_file* create(const procfs_file* parent, std::string name,
-                          read_fn read, write_fn write) {
-    auto& file = parent->children->emplace_back();
-
-    file.name = std::move(name);
-    file.ino = s_next_ino++;
-    file.read = std::move(read);
-    file.write = std::move(write);
-    file.children = nullptr;
-
-    return 0;
-}
-
-const procfs_file* mkdir(const procfs_file* parent, std::string name) {
-    auto& file = parent->children->emplace_back();
-
-    file.name = std::move(name);
-    file.ino = s_next_ino++;
-    file.read = nullptr;
-    file.write = nullptr;
-    file.children = new std::vector<procfs_file>();
-
-    return &file;
-}
-
-class procfs_module : public virtual kernel::kmod::kmod {
-   public:
-    procfs_module() : kmod("procfs") {}
-
-    virtual int init() override {
-        int ret = procfs::init();
-        if (ret < 0)
-            return ret;
-
-        return fs::register_fs("procfs", procfs::create);
-    }
-};
-
-} // namespace kernel::procfs
-
-INTERNAL_MODULE(procfs, procfs_module);

+ 329 - 0
src/fs/procfs.rs

@@ -0,0 +1,329 @@
+use alloc::sync::{Arc, Weak};
+use bindings::{EACCES, EINVAL, EISDIR, ENOTDIR, S_IFDIR, S_IFREG};
+
+use crate::{
+    io::copy_offset_count,
+    kernel::{
+        mem::paging::{Page, PageBuffer},
+        vfs::{
+            inode::{Ino, Inode, InodeData},
+            mount::{dump_mounts, register_filesystem, Mount, MountCreator},
+            vfs::Vfs,
+            DevId, ReadDirCallback, TimeSpec,
+        },
+    },
+    prelude::*,
+};
+
+pub trait ProcFsFile: Send + Sync {
+    fn can_read(&self) -> bool {
+        false
+    }
+
+    fn can_write(&self) -> bool {
+        false
+    }
+
+    fn read(&self, _buffer: &mut PageBuffer) -> KResult<usize> {
+        Err(EINVAL)
+    }
+
+    fn write(&self, _buffer: &[u8]) -> KResult<usize> {
+        Err(EINVAL)
+    }
+}
+
+pub enum ProcFsData {
+    File(Box<dyn ProcFsFile>),
+    Directory(Mutex<Vec<Arc<ProcFsNode>>>),
+}
+
+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
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+
+    fn readdir(
+        &self,
+        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),
+        }
+    }
+
+    fn read(
+        &self,
+        buffer: &mut [u8],
+        count: usize,
+        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, count))
+            }
+            _ => Err(EISDIR),
+        }
+    }
+
+    fn vfs_weak(&self) -> Weak<Mutex<dyn Vfs>> {
+        ProcFsMountCreator::get_weak()
+    }
+
+    fn vfs_strong(&self) -> Option<Arc<Mutex<dyn Vfs>>> {
+        Some(ProcFsMountCreator::get())
+    }
+}
+
+pub struct ProcFs {
+    root_node: Arc<ProcFsNode>,
+    next_ino: Ino,
+}
+
+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
+    }
+}
+
+impl Vfs for ProcFs {
+    fn io_blksize(&self) -> usize {
+        1024
+    }
+
+    fn fs_devid(&self) -> DevId {
+        10
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+}
+
+static GLOBAL_PROCFS: Mutex<Option<Arc<Mutex<ProcFs>>>> = Mutex::new(None);
+
+struct ProcFsMountCreator;
+
+impl ProcFsMountCreator {
+    pub fn get() -> Arc<Mutex<ProcFs>> {
+        let fs = GLOBAL_PROCFS.lock();
+        fs.as_ref().unwrap().clone()
+    }
+
+    pub fn get_weak() -> Weak<Mutex<ProcFs>> {
+        let fs = GLOBAL_PROCFS.lock();
+        fs.as_ref()
+            .map_or(Weak::new(), |refproc| Arc::downgrade(refproc))
+    }
+}
+
+impl MountCreator for ProcFsMountCreator {
+    fn create_mount(
+        &self,
+        _source: &str,
+        _flags: u64,
+        _data: &[u8],
+    ) -> KResult<Mount> {
+        let vfs = ProcFsMountCreator::get();
+
+        let root_inode = vfs.lock().root_node.clone();
+        Ok(Mount::new(vfs, root_inode))
+    }
+}
+
+pub fn root() -> Arc<ProcFsNode> {
+    let vfs = ProcFsMountCreator::get();
+    let root = vfs.lock().root_node.clone();
+
+    root
+}
+
+pub fn creat(
+    parent: &ProcFsNode,
+    name: &str,
+    data: ProcFsData,
+) -> KResult<Arc<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),
+    }
+
+    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
+            };
+
+            let node = Arc::new(ProcFsNode::new(ino, String::from(name), data));
+
+            {
+                let mut indata = node.indata.lock();
+                indata.nlink = 1;
+                indata.mode = mode;
+            }
+
+            lck.lock().push(node.clone());
+
+            Ok(node.clone())
+        }
+        _ => Err(ENOTDIR),
+    }
+}
+
+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
+            };
+
+            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;
+            }
+
+            lck.lock().push(node.clone());
+
+            Ok(node.clone())
+        }
+        _ => Err(ENOTDIR),
+    }
+}
+
+struct DumpMountsFile {}
+impl ProcFsFile for DumpMountsFile {
+    fn can_read(&self) -> bool {
+        true
+    }
+
+    fn read(&self, buffer: &mut PageBuffer) -> KResult<usize> {
+        dump_mounts(buffer);
+
+        Ok(buffer.len())
+    }
+}
+
+pub fn init() {
+    {
+        let mut vfs = GLOBAL_PROCFS.lock();
+        *vfs = Some(ProcFs::create());
+    }
+
+    register_filesystem("procfs", Box::new(ProcFsMountCreator)).unwrap();
+
+    let root = root();
+
+    creat(
+        &root,
+        "mounts",
+        ProcFsData::File(Box::new(DumpMountsFile {})),
+    )
+    .unwrap();
+}

+ 0 - 296
src/fs/tmpfs.cc

@@ -1,296 +0,0 @@
-#include <algorithm>
-#include <map>
-#include <vector>
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/mount.h>
-
-#include <kernel/log.hpp>
-#include <kernel/module.hpp>
-#include <kernel/vfs.hpp>
-
-using namespace fs;
-
-struct tmpfs_file_entry {
-    ino_t ino;
-    std::string filename;
-};
-
-class tmpfs : public virtual vfs {
-   private:
-    using fe_t = tmpfs_file_entry;
-    using vfe_t = std::vector<fe_t>;
-    using fdata_t = std::vector<char>;
-
-   private:
-    ino_t m_next_ino;
-    bool m_readonly;
-
-   private:
-    ino_t assign_ino() { return m_next_ino++; }
-
-   protected:
-    inline vfe_t* make_vfe() { return new vfe_t{}; }
-    inline fdata_t* make_fdata() { return new fdata_t{}; }
-
-    void mklink(inode* dir, inode* ind, std::string filename) {
-        auto& fes = *(vfe_t*)dir->fs_data;
-        fes.emplace_back(fe_t{ind->ino, std::move(filename)});
-
-        dir->size += sizeof(fe_t);
-        ++ind->nlink;
-    }
-
-    virtual ssize_t readdir(inode* dir, size_t offset,
-                            const vfs::filldir_func& filldir) override {
-        if (!S_ISDIR(dir->mode))
-            return -ENOTDIR;
-
-        auto& entries = *(vfe_t*)dir->fs_data;
-        size_t off = offset / sizeof(fe_t);
-
-        size_t nread = 0;
-
-        for (; (off + 1) <= entries.size(); ++off, nread += sizeof(fe_t)) {
-            const auto& entry = entries[off];
-            auto* ind = get_inode(entry.ino);
-
-            auto ret = filldir(entry.filename.c_str(), ind, 0);
-            if (ret != 0)
-                break;
-        }
-
-        return nread;
-    }
-
-    explicit tmpfs(unsigned long flags)
-        : vfs(make_device(0, 2), 4096)
-        , m_next_ino{1}
-        , m_readonly(flags & MS_RDONLY) {
-        auto* in = alloc_inode(assign_ino());
-
-        in->fs_data = make_vfe();
-        in->mode = S_IFDIR | 0777;
-
-        mklink(in, in, ".");
-        mklink(in, in, "..");
-
-        register_root_node(in);
-    }
-
-   public:
-    static tmpfs* create(const char*, unsigned long flags, const void*) {
-        return new tmpfs(flags);
-    }
-
-    virtual ssize_t read(struct inode* file, char* buf, size_t buf_size,
-                         size_t count, off_t offset) override {
-        if (!S_ISREG(file->mode))
-            return -EINVAL;
-
-        auto* data = (fdata_t*)file->fs_data;
-        size_t fsize = data->size();
-
-        if (offset + count > fsize)
-            count = fsize - offset;
-
-        if (buf_size < count) {
-            count = buf_size;
-        }
-
-        memcpy(buf, data->data() + offset, count);
-
-        return count;
-    }
-
-    virtual ssize_t write(struct inode* file, const char* buf, size_t count,
-                          off_t offset) override {
-        if (m_readonly)
-            return -EROFS;
-        if (!S_ISREG(file->mode))
-            return -EINVAL;
-
-        auto* data = (fdata_t*)file->fs_data;
-
-        if (data->size() < offset + count)
-            data->resize(offset + count);
-        memcpy(data->data() + offset, buf, count);
-
-        file->size = data->size();
-
-        return count;
-    }
-
-    virtual int creat(struct inode* dir, struct dentry* at,
-                      mode_t mode) override {
-        if (m_readonly)
-            return -EROFS;
-        if (!S_ISDIR(dir->mode))
-            return -ENOTDIR;
-        assert(at->parent && at->parent->inode == dir);
-
-        auto* file = alloc_inode(assign_ino());
-        file->mode = S_IFREG | (mode & 0777);
-        file->fs_data = make_fdata();
-
-        mklink(dir, file, at->name);
-
-        at->inode = file;
-        at->flags |= fs::D_PRESENT;
-        return 0;
-    }
-
-    virtual int mknod(struct inode* dir, struct dentry* at, mode_t mode,
-                      dev_t dev) override {
-        if (m_readonly)
-            return -EROFS;
-        if (!S_ISDIR(dir->mode))
-            return -ENOTDIR;
-        assert(at->parent && at->parent->inode == dir);
-
-        if (!S_ISBLK(mode) && !S_ISCHR(mode))
-            return -EINVAL;
-
-        if (dev & ~0xffff)
-            return -EINVAL;
-
-        auto* node = alloc_inode(assign_ino());
-        node->mode = mode;
-        node->fs_data = (void*)(uintptr_t)dev;
-
-        mklink(dir, node, at->name);
-
-        at->inode = node;
-        at->flags |= fs::D_PRESENT;
-        return 0;
-    }
-
-    virtual int mkdir(struct inode* dir, struct dentry* at,
-                      mode_t mode) override {
-        if (m_readonly)
-            return -EROFS;
-        if (!S_ISDIR(dir->mode))
-            return -ENOTDIR;
-        assert(at->parent && at->parent->inode == dir);
-
-        auto* new_dir = alloc_inode(assign_ino());
-        new_dir->mode = S_IFDIR | (mode & 0777);
-        new_dir->fs_data = make_vfe();
-
-        mklink(new_dir, new_dir, ".");
-        mklink(new_dir, dir, "..");
-
-        mklink(dir, new_dir, at->name);
-
-        at->inode = new_dir;
-        at->flags |= fs::D_PRESENT | fs::D_DIRECTORY | fs::D_LOADED;
-        return 0;
-    }
-
-    virtual int symlink(struct inode* dir, struct dentry* at,
-                        const char* target) override {
-        if (m_readonly)
-            return -EROFS;
-        if (!S_ISDIR(dir->mode))
-            return -ENOTDIR;
-        assert(at->parent && at->parent->inode == dir);
-
-        auto* data = make_fdata();
-        data->resize(strlen(target));
-        memcpy(data->data(), target, data->size());
-
-        auto* file = alloc_inode(assign_ino());
-        file->mode = S_IFLNK | 0777;
-        file->fs_data = data;
-        file->size = data->size();
-
-        mklink(dir, file, at->name);
-
-        at->inode = file;
-        at->flags |= fs::D_PRESENT;
-        return 0;
-    }
-
-    virtual int readlink(struct inode* file, char* buf,
-                         size_t buf_size) override {
-        if (!S_ISLNK(file->mode))
-            return -EINVAL;
-
-        auto* data = (fdata_t*)file->fs_data;
-        size_t size = data->size();
-
-        size = std::min(size, buf_size);
-
-        memcpy(buf, data->data(), size);
-
-        return size;
-    }
-
-    virtual int unlink(struct inode* dir, struct dentry* at) override {
-        if (m_readonly)
-            return -EROFS;
-        if (!S_ISDIR(dir->mode))
-            return -ENOTDIR;
-        assert(at->parent && at->parent->inode == dir);
-
-        auto* vfe = (vfe_t*)dir->fs_data;
-        assert(vfe);
-
-        for (auto iter = vfe->begin(); iter != vfe->end();) {
-            if (iter->ino != at->inode->ino) {
-                ++iter;
-                continue;
-            }
-
-            if (S_ISDIR(at->inode->mode))
-                return -EISDIR;
-
-            if (S_ISREG(at->inode->mode)) {
-                // since we do not allow hard links in tmpfs, there is no need
-                // to check references, we remove the file data directly
-                auto* filedata = (fdata_t*)at->inode->fs_data;
-                assert(filedata);
-
-                delete filedata;
-            }
-
-            free_inode(iter->ino);
-            at->flags &= ~fs::D_PRESENT;
-            at->inode = nullptr;
-
-            vfe->erase(iter);
-            return 0;
-        }
-
-        kmsg("[tmpfs] warning: file entry not found in vfe");
-        return -EIO;
-    }
-
-    virtual dev_t i_device(inode* file) override {
-        if (file->mode & S_IFMT & (S_IFBLK | S_IFCHR))
-            return (dev_t)(uintptr_t)file->fs_data;
-        return -ENODEV;
-    }
-
-    virtual int truncate(inode* file, size_t size) override {
-        if (m_readonly)
-            return -EROFS;
-        if (!S_ISREG(file->mode))
-            return -EINVAL;
-
-        auto* data = (fdata_t*)file->fs_data;
-        data->resize(size);
-        file->size = size;
-        return 0;
-    }
-};
-
-class tmpfs_module : public virtual kernel::kmod::kmod {
-   public:
-    tmpfs_module() : kmod{"tmpfs"} {}
-
-    int init() override { return fs::register_fs("tmpfs", tmpfs::create); }
-};
-
-INTERNAL_MODULE(tmpfs, tmpfs_module);

+ 631 - 0
src/fs/tmpfs.rs

@@ -0,0 +1,631 @@
+use crate::{
+    io::copy_offset_count,
+    kernel::vfs::{
+        dentry::Dentry,
+        inode::{Ino, Inode, InodeCache, InodeData, Mode},
+        mount::{register_filesystem, Mount, MountCreator, MS_RDONLY},
+        s_isblk, s_ischr,
+        vfs::Vfs,
+        DevId, ReadDirCallback, TimeSpec,
+    },
+    prelude::*,
+};
+
+use alloc::sync::{Arc, Weak};
+
+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,
+};
+
+type TmpFsFile = Vec<u8>;
+type TmpFsDirectory = Vec<(Ino, String)>;
+
+enum TmpFsData {
+    File(TmpFsFile),
+    Device(DevId),
+    Directory(TmpFsDirectory),
+    Symlink(String),
+}
+
+struct TmpFsInode {
+    idata: Mutex<InodeData>,
+    fsdata: Mutex<TmpFsData>,
+    vfs: Weak<Mutex<TmpFs>>,
+}
+
+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,
+        })
+    }
+
+    fn vfs(&self) -> KResult<Arc<Mutex<TmpFs>>> {
+        self.vfs.upgrade().ok_or(EIO)
+    }
+
+    /// 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;
+            }
+
+            _ => panic!("Parent is not a directory"),
+        }
+    }
+
+    /// 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;
+            }
+
+            _ => panic!("parent is not a directory"),
+        }
+    }
+}
+
+impl Inode for TmpFsInode {
+    fn idata(&self) -> &Mutex<InodeData> {
+        &self.idata
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+
+    fn readdir(
+        &self,
+        offset: usize,
+        callback: &mut ReadDirCallback,
+    ) -> 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),
+        }
+    }
+
+    fn read(
+        &self,
+        buffer: &mut [u8],
+        count: usize,
+        offset: usize,
+    ) -> KResult<usize> {
+        self.vfs()?;
+
+        match *self.fsdata.lock() {
+            TmpFsData::File(ref file) => {
+                Ok(copy_offset_count(file, buffer, offset as usize, count))
+            }
+
+            _ => Err(EINVAL),
+        }
+    }
+
+    fn write(&self, buffer: &[u8], offset: usize) -> KResult<usize> {
+        if self.vfs()?.lock().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())
+            }
+
+            _ => Err(EINVAL),
+        }
+    }
+
+    fn creat(&self, at: &mut Dentry, mode: Mode) -> KResult<()> {
+        let _vfs = self.vfs()?;
+        let mut vfs = _vfs.lock();
+        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 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(())
+    }
+
+    fn mknod(&self, at: &mut Dentry, mode: Mode, dev: DevId) -> KResult<()> {
+        let _vfs = self.vfs()?;
+        let mut vfs = _vfs.lock();
+        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 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 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(())
+    }
+
+    fn mkdir(&self, at: &mut Dentry, mode: Mode) -> KResult<()> {
+        let _vfs = self.vfs()?;
+        let mut vfs = _vfs.lock();
+        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 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 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,
+            );
+        }
+
+        at.save_inode(dir);
+        // TODO: try remove D_LOADED and check if it works
+        at.flags |= D_PRESENT | D_DIRECTORY | D_LOADED;
+
+        Ok(())
+    }
+
+    fn symlink(&self, at: &mut Dentry, target: &str) -> KResult<()> {
+        let _vfs = self.vfs()?;
+        let mut vfs = _vfs.lock();
+        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 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_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 | D_SYMLINK;
+
+        Ok(())
+    }
+
+    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());
+
+                Ok(len)
+            }
+
+            _ => Err(EINVAL),
+        }
+    }
+
+    fn devid(&self) -> KResult<DevId> {
+        match *self.fsdata.lock() {
+            TmpFsData::Device(dev) => Ok(dev),
+            _ => Err(ENODEV),
+        }
+    }
+
+    fn truncate(&self, length: usize) -> KResult<()> {
+        if self.vfs()?.lock().readonly {
+            return Err(EROFS);
+        }
+
+        match *self.fsdata.lock() {
+            TmpFsData::File(ref mut file) => {
+                file.resize(length, 0);
+                self.idata.lock().size = length as u64;
+
+                Ok(())
+            }
+
+            _ => Err(EINVAL),
+        }
+    }
+
+    fn unlink(&self, at: &mut Dentry) -> KResult<()> {
+        if self.vfs()?.lock().readonly {
+            return Err(EROFS);
+        }
+
+        let file = at.get_inode_clone();
+        let file = file.as_any().downcast_ref::<TmpFsInode>().unwrap();
+
+        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),
+        }
+    }
+
+    fn vfs_weak(&self) -> Weak<Mutex<dyn Vfs>> {
+        self.vfs.clone()
+    }
+
+    fn vfs_strong(&self) -> Option<Arc<Mutex<dyn Vfs>>> {
+        match self.vfs.upgrade() {
+            Some(vfs) => Some(vfs),
+            None => None,
+        }
+    }
+}
+
+/// # Lock order
+/// vfs -> icache -> fsdata -> data
+struct TmpFs {
+    icache: Mutex<InodeCache<TmpFs>>,
+    next_ino: Ino,
+    readonly: bool,
+}
+
+impl TmpFs {
+    fn assign_ino(&mut self) -> Ino {
+        let ino = self.next_ino;
+        self.next_ino += 1;
+
+        ino
+    }
+
+    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,
+            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 fsdata = root_inode.fsdata.lock();
+            let mut idata = root_inode.idata.lock();
+
+            TmpFsInode::self_link_unchecked(&mut fsdata, &mut idata, ".");
+            TmpFsInode::self_link_unchecked(&mut fsdata, &mut idata, "..");
+        }
+
+        Ok((tmpfs, root_inode))
+    }
+}
+
+impl Vfs for TmpFs {
+    fn io_blksize(&self) -> usize {
+        4096
+    }
+
+    fn fs_devid(&self) -> DevId {
+        2
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+}
+
+struct TmpFsMountCreator;
+
+impl MountCreator for TmpFsMountCreator {
+    fn create_mount(
+        &self,
+        _source: &str,
+        flags: u64,
+        _data: &[u8],
+    ) -> KResult<Mount> {
+        let (fs, root_inode) = TmpFs::create(flags & MS_RDONLY != 0)?;
+
+        Ok(Mount::new(fs, root_inode))
+    }
+}
+
+pub fn init() {
+    register_filesystem("tmpfs", Box::new(TmpFsMountCreator)).unwrap();
+}

+ 75 - 1
src/io.rs

@@ -1,4 +1,7 @@
-use core::ffi::{c_size_t, c_uchar};
+use bindings::EFAULT;
+
+use crate::prelude::*;
+use core::ffi::{c_char, c_size_t, c_uchar};
 
 pub struct Buffer {
     buf: *mut c_uchar,
@@ -40,3 +43,74 @@ impl Write for Buffer {
         Ok(())
     }
 }
+
+pub fn get_str_from_cstr<'a>(cstr: *const c_char) -> KResult<&'a str> {
+    if cstr.is_null() {
+        return Err(EFAULT);
+    }
+
+    let cstr = unsafe { core::ffi::CStr::from_ptr::<'a>(cstr) };
+    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
+///
+/// The number of bytes copied.
+pub fn copy_offset_count(
+    src: &[u8],
+    dst: &mut [u8],
+    offset: usize,
+    count: usize,
+) -> usize {
+    if offset >= src.len() {
+        return 0;
+    }
+
+    let count = {
+        let count = count.min(dst.len());
+
+        if offset + count > src.len() {
+            src.len() - offset
+        } else {
+            count
+        }
+    };
+
+    dst[..count].copy_from_slice(&src[offset..offset + count]);
+
+    count
+}

+ 2 - 0
src/kernel.rs

@@ -1,3 +1,5 @@
+pub mod block;
 pub mod console;
 pub mod interrupt;
 pub mod mem;
+pub mod vfs;

+ 8 - 0
src/kernel/async/lock.cc

@@ -64,6 +64,14 @@ void preempt_enable() {
     --_preempt_count();
 }
 
+extern "C" void r_preempt_disable() {
+    ++_preempt_count();
+}
+
+extern "C" void r_preempt_enable() {
+    --_preempt_count();
+}
+
 preempt_count_t preempt_count() {
     return _preempt_count();
 }

+ 67 - 0
src/kernel/block.rs

@@ -0,0 +1,67 @@
+use crate::{
+    bindings::root::{fs::block_device_read, EINVAL, EIO},
+    KResult,
+};
+
+use super::vfs::DevId;
+
+pub fn make_device(major: u32, minor: u32) -> DevId {
+    (major << 8) & 0xff00u32 | minor & 0xffu32
+}
+
+pub struct BlockDevice {
+    device: DevId,
+}
+
+// pub struct BlockDeviceRequest<'lt> {
+//     pub sector: u64, // Sector to read from, in 512-byte blocks
+//     pub count: u64,  // Number of sectors to read
+//     pub buffer: &'lt [Page],
+// }
+
+pub struct BlockDeviceRequest<'lt> {
+    pub sector: u64, // Sector to read from, in 512-byte blocks
+    pub count: u64,  // Number of sectors to read
+    pub buffer: &'lt mut [u8],
+}
+
+impl BlockDevice {
+    pub fn new(device: DevId) -> Self {
+        BlockDevice { device }
+    }
+
+    pub fn devid(&self) -> DevId {
+        self.device
+    }
+
+    pub fn read(&self, req: &mut BlockDeviceRequest) -> KResult<()> {
+        // // Verify that the buffer is big enough
+        // let buffer_size = req.buffer.iter().fold(0, |acc, e| acc + e.len());
+        // if buffer_size / 512 < req.count as usize {
+        //     return Err(EINVAL);
+        // }
+
+        // Verify that the buffer is big enough
+        if req.buffer.len() < req.count as usize * 512 {
+            return Err(EINVAL);
+        }
+
+        let buffer = req.buffer.as_mut_ptr();
+
+        let nread = unsafe {
+            block_device_read(
+                self.device as u32,
+                buffer as *mut _,
+                req.buffer.len(),
+                req.sector as usize * 512,
+                req.count as usize * 512,
+            )
+        };
+
+        match nread {
+            i if i < 0 => return Err(i as u32),
+            i if i as u64 == req.count * 512 => Ok(()),
+            _ => Err(EIO),
+        }
+    }
+}

+ 4 - 3
src/kernel/console.rs

@@ -1,6 +1,7 @@
+use crate::prelude::*;
+
 pub struct Console {}
 
-use core::fmt::Write;
 impl Write for Console {
     fn write_str(&mut self, s: &str) -> core::fmt::Result {
         use crate::bindings::root::kernel::tty::console as _console;
@@ -24,14 +25,12 @@ pub fn _print(args: core::fmt::Arguments) -> core::fmt::Result {
 
 pub static CONSOLE: spin::Mutex<Console> = spin::Mutex::new(Console {});
 
-#[macro_export]
 macro_rules! print {
     ($($arg:tt)*) => {
         $crate::kernel::console::_print(format_args!($($arg)*))
     };
 }
 
-#[macro_export]
 macro_rules! println {
     () => {
         $crate::print!("\n")
@@ -40,3 +39,5 @@ macro_rules! println {
         $crate::print!("{}\n", format_args!($($arg)*))
     };
 }
+
+pub(crate) use {print, println};

+ 0 - 1
src/kernel/mem/mm_list.cc

@@ -299,7 +299,6 @@ int mm_list::mmap(const map_args& args) {
 
     if (flags & MM_MAPPED) {
         assert(finode);
-        assert(S_ISREG(finode->mode) || S_ISBLK(finode->mode));
 
         auto [area, inserted] = m_areas.emplace(
             vaddr, flags & ~MM_INTERNAL_MASK, vaddr + length, finode, foff);

+ 2 - 19
src/kernel/mem/paging.cc

@@ -10,7 +10,6 @@
 #include <kernel/mem/slab.hpp>
 #include <kernel/mem/vm_area.hpp>
 #include <kernel/process.hpp>
-#include <kernel/procfs.hpp>
 
 using namespace types::list;
 
@@ -333,8 +332,8 @@ void kernel::mem::paging::handle_page_fault(unsigned long err) {
         size_t offset = (vaddr & ~0xfff) - mm_area->start;
         char* data = physaddr<char>{pfn};
 
-        int n = fs::read(mm_area->mapped_file, data, 4096,
-                         mm_area->file_offset + offset, 4096);
+        int n = fs_read(mm_area->mapped_file, data, 4096,
+                        mm_area->file_offset + offset, 4096);
 
         // TODO: send SIGBUS if offset is greater than real size
         if (n != 4096)
@@ -422,19 +421,3 @@ vaddr_range::operator bool() const noexcept {
 bool vaddr_range::operator==(const vaddr_range& other) const noexcept {
     return n == other.n;
 }
-
-extern "C" isize real_dump_buddy(const zone_info* zones, u8* buf,
-                                 usize buf_size);
-
-static isize _dump_buddy(u8* buf, usize buf_size) {
-    return real_dump_buddy(zones, buf, buf_size);
-}
-
-static void _init_procfs_files() {
-    auto* root = kernel::procfs::root();
-
-    kernel::procfs::create(root, "buddyinfo", _dump_buddy, nullptr);
-}
-
-__attribute__((used))
-SECTION(".late_init") void (*const _paging_late_init)() = _init_procfs_files;

+ 65 - 55
src/kernel/mem/paging.rs

@@ -1,8 +1,9 @@
 use crate::bindings::root::EFAULT;
-use crate::io::Buffer;
 use crate::kernel::mem::phys;
 use core::fmt;
 
+use super::phys::PhysPtr;
+
 pub struct Page {
     page_ptr: *mut crate::bindings::root::kernel::mem::paging::page,
     order: u32,
@@ -98,6 +99,69 @@ impl fmt::Debug for Page {
     }
 }
 
+pub struct PageBuffer {
+    page: Page,
+    offset: usize,
+}
+
+impl PageBuffer {
+    pub fn new(page: Page) -> Self {
+        Self { page, offset: 0 }
+    }
+
+    pub fn len(&self) -> usize {
+        self.offset
+    }
+
+    pub fn remaining(&self) -> usize {
+        self.page.len() - self.offset
+    }
+
+    pub fn as_slice(&self) -> &[u8] {
+        unsafe {
+            core::slice::from_raw_parts(
+                self.page.as_cached().as_ptr::<u8>(),
+                self.offset,
+            )
+        }
+    }
+
+    pub fn as_mut_slice(&self) -> &mut [u8] {
+        unsafe {
+            core::slice::from_raw_parts_mut(
+                self.page.as_cached().as_ptr::<u8>(),
+                self.offset,
+            )
+        }
+    }
+
+    fn available_as_slice(&self) -> &mut [u8] {
+        unsafe {
+            core::slice::from_raw_parts_mut(
+                self.page.as_cached().as_ptr::<u8>().add(self.offset),
+                self.remaining(),
+            )
+        }
+    }
+
+    pub fn consume(&mut self, len: usize) {
+        self.offset += len;
+    }
+}
+
+impl core::fmt::Write for PageBuffer {
+    fn write_str(&mut self, s: &str) -> core::fmt::Result {
+        if s.len() > self.remaining() {
+            return Err(core::fmt::Error);
+        }
+
+        self.available_as_slice()[..s.len()].copy_from_slice(s.as_bytes());
+        self.consume(s.len());
+
+        Ok(())
+    }
+}
+
 /// Copy data from a slice to a `Page`
 ///
 /// DONT USE THIS FUNCTION TO COPY DATA TO MMIO ADDRESSES
@@ -122,57 +186,3 @@ pub fn copy_to_page(src: &[u8], dst: &Page) -> Result<(), u32> {
 
     Ok(())
 }
-
-#[repr(C)]
-struct ZoneInfo {
-    head: *mut core::ffi::c_void,
-    count: core::ffi::c_size_t,
-}
-
-fn do_dump_buddy(
-    zones: &[ZoneInfo],
-    buffer: &mut Buffer,
-) -> Result<usize, core::fmt::Error> {
-    let maxi = {
-        let mut maxi = 0;
-        for (idx, zone) in zones.iter().enumerate() {
-            if zone.count > 0 {
-                maxi = idx;
-            }
-        }
-        maxi
-    };
-
-    use core::fmt::Write;
-    write!(buffer, "Order")?;
-
-    for idx in 0..=maxi {
-        write!(buffer, "\t{}", idx)?;
-    }
-
-    write!(buffer, "\nCount")?;
-
-    for zone in zones.iter().take(maxi + 1) {
-        write!(buffer, "\t{}", zone.count)?;
-    }
-
-    write!(buffer, "\n")?;
-
-    Ok(buffer.count())
-}
-
-#[no_mangle]
-extern "C" fn real_dump_buddy(
-    zones: *const ZoneInfo,
-    buf: *mut core::ffi::c_uchar,
-    buf_size: core::ffi::c_size_t,
-) -> core::ffi::c_ssize_t {
-    let zones = unsafe { core::slice::from_raw_parts(zones, 52) };
-    let mut buffer = Buffer::new(buf, buf_size);
-
-    use crate::bindings::root::ENOMEM;
-    match do_dump_buddy(zones, &mut buffer) {
-        Ok(size) => size as core::ffi::c_ssize_t,
-        Err(_) => -(ENOMEM as core::ffi::c_ssize_t),
-    }
-}

+ 8 - 0
src/kernel/mem/phys.rs

@@ -10,6 +10,14 @@ pub trait PhysPtr {
     fn as_mut<'lifetime, T>(&self) -> &'lifetime mut T {
         unsafe { &mut *(self.as_ptr()) }
     }
+
+    fn as_slice<'lifetime, T>(&self, len: usize) -> &'lifetime [T] {
+        unsafe { core::slice::from_raw_parts(self.as_ptr(), len) }
+    }
+
+    fn as_mut_slice<'lifetime, T>(&self, len: usize) -> &'lifetime mut [T] {
+        unsafe { core::slice::from_raw_parts_mut(self.as_ptr(), len) }
+    }
 }
 
 #[derive(Clone, Copy, PartialEq, Eq)]

+ 7 - 17
src/kernel/process.cpp

@@ -1,11 +1,9 @@
 #include <memory>
-#include <queue>
 #include <utility>
 
 #include <assert.h>
 #include <bits/alltypes.h>
 #include <stdint.h>
-#include <stdio.h>
 #include <sys/mount.h>
 #include <sys/wait.h>
 
@@ -217,17 +215,8 @@ void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn) {
 
     asm volatile("sti");
 
-    // mount rootfs
-
-    fs::vfs* rootfs;
-    if (1) {
-        int ret;
-        std::tie(rootfs, ret) =
-            fs::vfs::create("none", "tmpfs", MS_NOATIME, nullptr);
-        assert(ret == 0);
-    }
-    current_process->fs_context.root = d_get(rootfs->root());
-    current_process->cwd = d_get(rootfs->root());
+    current_process->fs_context.root = fs::r_get_root_dentry();
+    current_process->cwd = fs::r_get_root_dentry();
 
     // ------------------------------------------
     // interrupt enabled
@@ -244,12 +233,13 @@ void NORETURN _kernel_init(kernel::mem::paging::pfn_t kernel_stack_pfn) {
         auto [mnt, status] = fs::open(context, context.root.get(), "/mnt");
         assert(mnt && status == -ENOENT);
 
-        if (int ret = fs::mkdir(mnt.get(), 0755); 1)
+        if (int ret = fs_mkdir(mnt.get(), 0755); 1)
             assert(ret == 0 && mnt->flags & fs::D_PRESENT);
 
-        int ret = rootfs->mount(mnt.get(), "/dev/sda", "/mnt", "fat32",
-                                MS_RDONLY | MS_NOATIME | MS_NODEV | MS_NOSUID,
-                                "ro,nodev");
+        int ret = fs_mount(mnt.get(), "/dev/sda", "/mnt", "fat32",
+                            MS_RDONLY | MS_NOATIME | MS_NODEV | MS_NOSUID,
+                            "ro,nodev");
+
         assert(ret == 0);
     }
 

+ 39 - 19
src/kernel/syscall/fileops.cc

@@ -87,7 +87,7 @@ int kernel::syscall::do_symlink(const char __user* target,
     if (!dent || status != -ENOENT)
         return status;
 
-    return fs::symlink(dent.get(), target);
+    return fs_symlink(dent.get(), target);
 }
 
 int kernel::syscall::do_readlink(const char __user* pathname, char __user* buf,
@@ -98,11 +98,11 @@ int kernel::syscall::do_readlink(const char __user* pathname, char __user* buf,
     if (!dent || status)
         return status;
 
-    if (buf_size <= 0 || !S_ISLNK(dent->inode->mode))
+    if (buf_size <= 0)
         return -EINVAL;
 
     // TODO: use copy_to_user
-    return fs::readlink(dent->inode, buf, buf_size);
+    return fs_readlink(&dent->inode, buf, buf_size);
 }
 
 int kernel::syscall::do_ioctl(int fd, unsigned long request, uintptr_t arg3) {
@@ -113,7 +113,8 @@ int kernel::syscall::do_ioctl(int fd, unsigned long request, uintptr_t arg3) {
     //       not. and we suppose that stdin will be
     //       either a tty or a pipe.
     auto* file = current_process->files[fd];
-    if (!file || !S_ISCHR(file->mode))
+    // TODO!!!: check whether the file is a tty or not
+    if (!file) // || !S_ISCHR(file->mode))
         return -ENOTTY;
 
     switch (request) {
@@ -188,7 +189,19 @@ ssize_t kernel::syscall::do_readv(int fd, const iovec* iov, int iovcnt) {
     // TODO: fix fake EOF
     ssize_t totn = 0;
     for (int i = 0; i < iovcnt; ++i) {
-        ssize_t ret = file->read((char*)iov[i].iov_base, iov[i].iov_len);
+        auto* base = (char*)iov[i].iov_base;
+        auto len = iov[i].iov_len;
+
+        if (len == 0)
+            break;
+
+        if (len < 0)
+            return -EINVAL;
+
+        if (!base)
+            return -EFAULT;
+
+        ssize_t ret = file->read(base, len);
 
         if (ret < 0)
             return ret;
@@ -214,7 +227,19 @@ ssize_t kernel::syscall::do_writev(int fd, const iovec* iov, int iovcnt) {
 
     ssize_t totn = 0;
     for (int i = 0; i < iovcnt; ++i) {
-        ssize_t ret = file->write((const char*)iov[i].iov_base, iov[i].iov_len);
+        auto* base = (const char*)iov[i].iov_base;
+        auto len = iov[i].iov_len;
+
+        if (len == 0)
+            continue;
+
+        if (len < 0)
+            return -EINVAL;
+
+        if (!base)
+            return -EFAULT;
+
+        ssize_t ret = file->write(base, len);
 
         if (ret < 0)
             return ret;
@@ -308,8 +333,9 @@ ssize_t kernel::syscall::do_sendfile(int out_fd, int in_fd,
         return -EBADF;
 
     // TODO: check whether in_fd supports mmapping
-    if (!S_ISREG(in_file->mode) && !S_ISBLK(in_file->mode))
-        return -EINVAL;
+    // TODO!!!: figure a way to recover this
+    // if (!S_ISREG(in_file->mode) && !S_ISBLK(in_file->mode))
+    return -EINVAL;
 
     if (offset) {
         NOT_IMPLEMENTED;
@@ -356,7 +382,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(&dent->inode, statxbuf, mask);
 }
 
 int kernel::syscall::do_fcntl(int fd, int cmd, unsigned long arg) {
@@ -385,7 +411,7 @@ int kernel::syscall::do_mkdir(const char __user* pathname, mode_t mode) {
     if (!dent || status != -ENOENT)
         return status;
 
-    return fs::mkdir(dent.get(), mode);
+    return fs_mkdir(dent.get(), mode);
 }
 
 int kernel::syscall::do_truncate(const char __user* pathname, long length) {
@@ -393,10 +419,7 @@ int kernel::syscall::do_truncate(const char __user* pathname, long length) {
     if (!dent || status)
         return status;
 
-    if (S_ISDIR(dent->inode->mode))
-        return -EISDIR;
-
-    return fs::truncate(dent->inode, length);
+    return fs_truncate(&dent->inode, length);
 }
 
 int kernel::syscall::do_unlink(const char __user* pathname) {
@@ -405,10 +428,7 @@ int kernel::syscall::do_unlink(const char __user* pathname) {
     if (!dent || status)
         return status;
 
-    if (S_ISDIR(dent->inode->mode))
-        return -EISDIR;
-
-    return fs::unlink(dent.get());
+    return fs_unlink(dent.get());
 }
 
 int kernel::syscall::do_access(const char __user* pathname, int mode) {
@@ -436,7 +456,7 @@ int kernel::syscall::do_mknod(const char __user* pathname, mode_t mode,
     if (!dent || status != -ENOENT)
         return status;
 
-    return fs::mknod(dent.get(), mode, dev);
+    return 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_mount(mountpoint.get(), source, target, fstype, flags, _fsdata);
 }

+ 40 - 178
src/kernel/vfs.cpp

@@ -1,9 +1,5 @@
-#include <bit>
 #include <cstddef>
-#include <map>
-#include <string>
 #include <utility>
-#include <vector>
 
 #include <assert.h>
 #include <bits/alltypes.h>
@@ -21,23 +17,17 @@
 #include <kernel/tty.hpp>
 #include <kernel/vfs.hpp>
 #include <kernel/vfs/dentry.hpp>
-#include <kernel/vfs/vfs.hpp>
-
-using fs::dentry;
 
 fs::regular_file::regular_file(file_flags flags, size_t cursor,
-                               struct inode* ind)
-    : file(ind->mode, flags), cursor(cursor), ind(ind) {}
+                               const struct rust_inode_handle* ind)
+    : file(flags), cursor(cursor), ind(ind) {}
 
 ssize_t fs::regular_file::read(char* __user buf, size_t n) {
     if (!flags.read)
         return -EBADF;
 
-    if (S_ISDIR(ind->mode))
-        return -EISDIR;
-
     // TODO: copy to user function !IMPORTANT
-    ssize_t n_wrote = fs::read(ind, buf, n, cursor, n);
+    ssize_t n_wrote = fs_read(ind, buf, n, cursor, n);
     if (n_wrote >= 0)
         cursor += n_wrote;
 
@@ -45,11 +35,8 @@ ssize_t fs::regular_file::read(char* __user buf, size_t n) {
 }
 
 ssize_t fs::regular_file::do_write(const char* __user buf, size_t n) {
-    if (S_ISDIR(mode))
-        return -EISDIR;
-
     // TODO: check privilege of user ptr
-    ssize_t n_wrote = fs::write(ind, buf, cursor, n);
+    ssize_t n_wrote = fs_write(ind, buf, cursor, n);
     if (n_wrote >= 0)
         cursor += n_wrote;
 
@@ -57,9 +44,7 @@ ssize_t fs::regular_file::do_write(const char* __user buf, size_t n) {
 }
 
 off_t fs::regular_file::seek(off_t n, int whence) {
-    if (!S_ISREG(mode))
-        return -ESPIPE;
-
+    size_t ind_size = r_get_inode_size(ind);
     size_t pos;
     switch (whence) {
         case SEEK_SET:
@@ -69,13 +54,13 @@ off_t fs::regular_file::seek(off_t n, int whence) {
             pos = cursor + n;
             break;
         case SEEK_END:
-            pos = ind->size + n;
+            pos = ind_size + n;
             break;
         default:
             return -EINVAL;
     }
 
-    if (pos > ind->size)
+    if (pos > ind_size)
         return -EINVAL;
 
     cursor = pos;
@@ -84,16 +69,12 @@ off_t fs::regular_file::seek(off_t n, int whence) {
 }
 
 int fs::regular_file::getdents(char* __user buf, size_t cnt) {
-    if (!S_ISDIR(ind->mode))
-        return -ENOTDIR;
-
     size_t orig_cnt = cnt;
-    int nread = ind->fs->readdir(
-        ind, cursor,
-        [&buf, &cnt](const char* fn, struct inode* ind, uint8_t type) {
-            size_t filename_len = strlen(fn);
-
-            size_t reclen = sizeof(fs::user_dirent) + 1 + filename_len;
+    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) {
+            size_t reclen = sizeof(fs::user_dirent) + 1 + fnlen;
             if (cnt < reclen)
                 return -EFAULT;
 
@@ -103,7 +84,7 @@ int fs::regular_file::getdents(char* __user buf, size_t cnt) {
             // TODO: show offset
             // dirp->d_off = 0;
             // TODO: use copy_to_user
-            memcpy(dirp->d_name, fn, filename_len);
+            memcpy(dirp->d_name, fn, fnlen);
             buf[reclen - 2] = 0;
             buf[reclen - 1] = type;
 
@@ -112,6 +93,8 @@ int fs::regular_file::getdents(char* __user buf, size_t cnt) {
             return 0;
         });
 
+    int nread = fs_readdir(ind, cursor, &callback);
+
     if (nread > 0)
         cursor += nread;
 
@@ -119,16 +102,12 @@ int fs::regular_file::getdents(char* __user buf, size_t cnt) {
 }
 
 int fs::regular_file::getdents64(char* __user buf, size_t cnt) {
-    if (!S_ISDIR(ind->mode))
-        return -ENOTDIR;
-
     size_t orig_cnt = cnt;
-    int nread = ind->fs->readdir(
-        ind, cursor,
-        [&buf, &cnt](const char* fn, struct inode* ind, uint8_t type) {
-            size_t filename_len = strlen(fn);
-
-            size_t reclen = sizeof(fs::user_dirent64) + filename_len;
+    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) {
+            size_t reclen = sizeof(fs::user_dirent64) + fnlen;
             if (cnt < reclen)
                 return -EFAULT;
 
@@ -138,7 +117,7 @@ int fs::regular_file::getdents64(char* __user buf, size_t cnt) {
             dirp->d_reclen = reclen;
             dirp->d_type = type;
             // TODO: use copy_to_user
-            memcpy(dirp->d_name, fn, filename_len);
+            memcpy(dirp->d_name, fn, fnlen);
             buf[reclen - 1] = 0;
 
             buf += reclen;
@@ -146,6 +125,8 @@ int fs::regular_file::getdents64(char* __user buf, size_t cnt) {
             return 0;
         });
 
+    int nread = fs_readdir(ind, cursor, &callback);
+
     if (nread > 0)
         cursor += nread;
 
@@ -153,7 +134,7 @@ int fs::regular_file::getdents64(char* __user buf, size_t cnt) {
 }
 
 fs::fifo_file::fifo_file(file_flags flags, std::shared_ptr<fs::pipe> ppipe)
-    : file(S_IFIFO, flags), ppipe(ppipe) {}
+    : file(flags), ppipe(ppipe) {}
 
 ssize_t fs::fifo_file::read(char* __user buf, size_t n) {
     if (!flags.read)
@@ -177,75 +158,6 @@ fs::fifo_file::~fifo_file() {
 static fs::blkdev_ops** blkdevs[256];
 static fs::chrdev_ops** chrdevs[256];
 
-size_t fs::read(struct fs::inode* file, char* buf, size_t buf_size,
-                size_t offset, size_t n) {
-    if (S_ISDIR(file->mode)) {
-        errno = EISDIR;
-        return -1U;
-    }
-
-    if (S_ISREG(file->mode))
-        return file->fs->read(file, buf, buf_size, n, offset);
-
-    if (S_ISBLK(file->mode) || S_ISCHR(file->mode)) {
-        dev_t dev = file->fs->i_device(file);
-        if (dev & 0x80000000) {
-            errno = EINVAL;
-            return -1U;
-        }
-
-        ssize_t ret;
-        if (S_ISBLK(file->mode))
-            ret = block_device_read(dev, buf, buf_size, offset, n);
-        else
-            ret = char_device_read(dev, buf, buf_size, n);
-
-        if (ret < 0) {
-            errno = -ret;
-            return -1U;
-        }
-
-        return ret;
-    }
-
-    errno = EINVAL;
-    return -1U;
-}
-size_t fs::write(struct fs::inode* file, const char* buf, size_t offset,
-                 size_t n) {
-    if (S_ISDIR(file->mode)) {
-        errno = EISDIR;
-        return -1U;
-    }
-
-    if (S_ISREG(file->mode))
-        return file->fs->write(file, buf, n, offset);
-
-    if (S_ISBLK(file->mode) || S_ISCHR(file->mode)) {
-        dev_t dev = file->fs->i_device(file);
-        if (dev & 0x80000000) {
-            errno = EINVAL;
-            return -1U;
-        }
-
-        ssize_t ret;
-        if (S_ISBLK(file->mode))
-            ret = block_device_write(dev, buf, offset, n);
-        else
-            ret = char_device_write(dev, buf, n);
-
-        if (ret < 0) {
-            errno = -ret;
-            return -1U;
-        }
-
-        return ret;
-    }
-
-    errno = EINVAL;
-    return -1U;
-}
-
 std::pair<fs::dentry_pointer, int> fs::open(const fs_context& context,
                                             dentry* _cwd,
                                             types::path_iterator path,
@@ -265,10 +177,9 @@ std::pair<fs::dentry_pointer, int> fs::open(const fs_context& context,
         if (!(cwd->flags & D_PRESENT))
             return {nullptr, -ENOENT};
 
-        assert(cwd->inode);
-        if (S_ISLNK(cwd->inode->mode)) {
+        if (cwd->flags & D_SYMLINK) {
             char linkpath[256];
-            int ret = fs::readlink(cwd->inode, linkpath, sizeof(linkpath));
+            int ret = fs_readlink(&cwd->inode, linkpath, sizeof(linkpath));
             if (ret < 0)
                 return {nullptr, ret};
             linkpath[ret] = 0;
@@ -279,6 +190,9 @@ std::pair<fs::dentry_pointer, int> fs::open(const fs_context& context,
                 return {nullptr, ret};
         }
 
+        if (item == ".." && cwd.get() == context.root.get())
+            continue;
+
         if (1) {
             int status;
             std::tie(cwd, status) = d_find(cwd.get(), item);
@@ -286,23 +200,16 @@ std::pair<fs::dentry_pointer, int> fs::open(const fs_context& context,
                 return {nullptr, status};
         }
 
-        while (cwd->flags & D_MOUNTPOINT) {
-            auto iter = mounts.find(cwd.get());
-            assert(iter);
-
-            auto* fs = iter->second.fs;
-            assert(fs);
-
-            cwd = d_get(fs->root());
-        }
+        while (cwd->flags & D_MOUNTPOINT)
+            cwd = r_get_mountpoint(cwd.get());
     }
 
     if (!(cwd->flags & D_PRESENT))
         return {std::move(cwd), -ENOENT};
 
-    if (follow && S_ISLNK(cwd->inode->mode)) {
+    if (follow && cwd->flags & D_SYMLINK) {
         char linkpath[256];
-        int ret = fs::readlink(cwd->inode, linkpath, sizeof(linkpath));
+        int ret = fs_readlink(&cwd->inode, linkpath, sizeof(linkpath));
         if (ret < 0)
             return {nullptr, ret};
         linkpath[ret] = 0;
@@ -313,58 +220,6 @@ std::pair<fs::dentry_pointer, int> fs::open(const fs_context& context,
     return {std::move(cwd), 0};
 }
 
-int fs::statx(struct inode* inode, struct statx* stat, unsigned int mask) {
-    assert(inode && inode->fs);
-    return inode->fs->statx(inode, stat, mask);
-}
-
-int fs::truncate(struct inode* file, size_t size) {
-    assert(file && file->fs);
-    return file->fs->truncate(file, size);
-}
-
-int fs::readlink(struct inode* inode, char* buf, size_t size) {
-    assert(inode && inode->fs);
-    return inode->fs->readlink(inode, buf, size);
-}
-
-int fs::symlink(struct dentry* at, const char* target) {
-    assert(at && at->parent && at->parent->fs);
-    return at->parent->fs->symlink(at->parent->inode, at, target);
-}
-
-int fs::unlink(struct dentry* at) {
-    assert(at && at->parent && at->parent->fs);
-    return at->parent->fs->unlink(at->parent->inode, at);
-}
-
-int fs::mknod(struct dentry* at, mode_t mode, dev_t dev) {
-    assert(at && at->parent && at->parent->fs);
-    return at->parent->fs->mknod(at->parent->inode, at, mode, dev);
-}
-
-int fs::creat(struct dentry* at, mode_t mode) {
-    assert(at && at->parent && at->parent->fs);
-    return at->parent->fs->creat(at->parent->inode, at, mode);
-}
-
-int fs::mount(struct dentry* mnt, const char* source, const char* mount_point,
-              const char* fstype, unsigned long flags, const void* data) {
-    assert(mnt && mnt->fs);
-    return mnt->fs->mount(mnt, source, mount_point, fstype, flags, data);
-}
-
-int fs::mkdir(struct dentry* at, mode_t mode) {
-    assert(at && at->parent && at->parent->fs);
-    return at->parent->fs->mkdir(at->parent->inode, at, mode);
-}
-
-int mount(dentry* mnt, const char* source, const char* mount_point,
-          const char* fstype, unsigned long flags, const void* data) {
-    assert(mnt && mnt->fs);
-    return mnt->fs->mount(mnt, source, mount_point, fstype, flags, data);
-}
-
 int fs::register_block_device(dev_t node, const fs::blkdev_ops& ops) {
     int major = NODE_MAJOR(node);
     int minor = NODE_MINOR(node);
@@ -611,3 +466,10 @@ int fs::pipe::read(char* buf, size_t n) {
     waitlist_w.notify_all();
     return orig_n - 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);
+}

+ 46 - 32
src/kernel/vfs/dentry.cc

@@ -1,3 +1,5 @@
+#include <defs.hpp>
+
 #include <assert.h>
 #include <errno.h>
 #include <sys/stat.h>
@@ -6,16 +8,15 @@
 #include <types/list.hpp>
 #include <types/path.hpp>
 
+#include <kernel/vfs.hpp>
 #include <kernel/vfs/dentry.hpp>
-#include <kernel/vfs/vfs.hpp>
 
 using namespace fs;
 using types::hash_t, types::hash_str, types::hash_ptr;
 
 static inline struct dentry* __d_parent(struct dentry* dentry) {
-    if (dentry->parent)
-        return dentry->parent;
-    return dentry;
+    assert(dentry->parent);
+    return dentry->parent;
 }
 
 static inline bool __d_is_present(struct dentry* dentry) {
@@ -30,19 +31,22 @@ static inline bool __d_is_loaded(struct dentry* dentry) {
     return dentry->flags & D_LOADED;
 }
 
-static inline bool __d_equal(struct dentry* dentry, struct dentry* parent,
+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(struct dentry* parent, types::string_view 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(struct dcache* cache, hash_t hash) {
+static inline struct dentry*& __d_first(const struct dcache* cache,
+                                        hash_t hash) {
     return cache->arr[hash & ((1 << cache->hash_bits) - 1)];
 }
 
@@ -58,7 +62,7 @@ static inline void __d_add(struct dentry* parent, struct dentry* dentry) {
     parent->cache->size++;
 }
 
-static inline struct dentry* __d_find_fast(struct dentry* parent,
+static inline struct dentry* __d_find_fast(const struct dentry* parent,
                                            types::string_view name) {
     auto* cache = parent->cache;
     assert(cache);
@@ -79,39 +83,38 @@ static inline int __d_load(struct dentry* parent) {
     if (__d_is_loaded(parent))
         return 0;
 
-    auto* inode = parent->inode;
-    assert(inode);
+    auto* inode = &parent->inode;
 
     if (!__d_is_dir(parent))
         return -ENOTDIR;
-    assert(S_ISDIR(inode->mode));
-
-    auto* fs = parent->inode->fs;
-    assert(fs);
 
     size_t offset = 0;
-    while (true) {
-        ssize_t off = fs->readdir(
-            inode, offset,
-            [parent](const char* fn, struct inode* inode, u8) -> int {
-                struct dentry* dentry = dcache_alloc(parent->cache);
+    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);
 
-                dentry->fs = inode->fs;
-                dentry->inode = inode;
-                dentry->name = fn;
+            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;
 
-                if (S_ISDIR(inode->mode))
-                    dentry->flags = D_PRESENT | D_DIRECTORY;
-                else
-                    dentry->flags = D_PRESENT;
+            dentry->hash = __d_hash(parent, dentry->name);
 
-                dentry->hash = __d_hash(parent, dentry->name);
+            __d_add(parent, dentry);
 
-                __d_add(parent, dentry);
+            d_put(dentry);
+            return 0;
+        });
 
-                d_put(dentry);
-                return 0;
-            });
+    while (true) {
+        ssize_t off = fs_readdir(inode, offset, &callback);
 
         if (off == 0)
             break;
@@ -132,9 +135,10 @@ std::pair<struct dentry*, int> fs::d_find(struct dentry* parent,
     constexpr types::string_view dot{".", 1};
     constexpr types::string_view dotdot{"..", 2};
 
-    if (name == dot)
+    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};
 
@@ -228,3 +232,13 @@ void fs::dcache_init_root(struct dcache* cache, struct dentry* 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;
+}

+ 203 - 0
src/kernel/vfs/dentry.rs

@@ -0,0 +1,203 @@
+use crate::prelude::*;
+
+use core::ops::{Deref, DerefMut};
+
+use alloc::{
+    boxed::Box,
+    sync::{Arc, Weak},
+};
+
+use crate::io::get_cxx_std_string;
+
+use super::{
+    bindings::{fs, EFAULT},
+    inode::Inode,
+    vfs::Vfs,
+};
+
+#[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,
+
+    pub flags: u64,
+    pub hash: u64,
+
+    refcount: u64,
+    pub name: [u64; 4],
+}
+
+pub struct Dentry {
+    raw: *mut DentryInner,
+}
+
+/// 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
+    }
+}
+
+// TODO!!!: CHANGE THIS
+unsafe impl Sync for Dentry {}
+unsafe impl Send for Dentry {}
+
+impl Dentry {
+    pub fn from_raw(raw: *mut DentryInner) -> KResult<Self> {
+        if raw.is_null() {
+            return Err(EFAULT);
+        }
+
+        Ok(Dentry {
+            raw: unsafe { fs::d_get1(raw as *mut _) as *mut _ },
+        })
+    }
+
+    pub fn parent_from_raw(raw: *mut DentryInner) -> KResult<Self> {
+        Dentry::from_raw(unsafe { raw.as_ref() }.ok_or(EFAULT)?.parent)
+    }
+
+    pub fn leak(self) -> *mut DentryInner {
+        let raw = self.raw;
+        core::mem::forget(self);
+        raw
+    }
+
+    pub fn get_name(&self) -> &str {
+        get_cxx_std_string(&self.name).unwrap()
+    }
+
+    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 get_inode_clone(&self) -> Arc<dyn Inode> {
+        raw_inode_clone(&self.inode)
+    }
+
+    pub fn take_inode(&self) -> Arc<dyn Inode> {
+        unsafe { Arc::from_raw(self.inode) }
+    }
+
+    pub fn take_fs(&self) -> Arc<Mutex<dyn Vfs>> {
+        unsafe { Arc::from_raw(self.fs) }
+    }
+}
+
+impl Deref for Dentry {
+    type Target = DentryInner;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe { &*self.raw }
+    }
+}
+
+impl DerefMut for Dentry {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        unsafe { &mut *self.raw }
+    }
+}
+
+impl Clone for Dentry {
+    fn clone(&self) -> Self {
+        unsafe {
+            fs::d_get1(self.raw as *mut _) as *mut _;
+        }
+        Dentry { raw: self.raw }
+    }
+}
+
+impl Drop for Dentry {
+    fn drop(&mut self) {
+        unsafe {
+            fs::d_put(self.raw as *mut _);
+        }
+    }
+}
+
+impl PartialEq for Dentry {
+    fn eq(&self, other: &Self) -> bool {
+        self.raw == other.raw
+    }
+}
+
+impl PartialOrd for Dentry {
+    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
+        self.raw.partial_cmp(&other.raw)
+    }
+}
+
+impl Eq for Dentry {}
+
+impl Ord for Dentry {
+    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+        self.raw.cmp(&other.raw)
+    }
+}
+
+const DCACHE_HASH_BITS: i32 = 8;
+
+pub struct DentryCache {
+    cache: Box<fs::dcache>,
+}
+
+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(),
+            }),
+        };
+
+        unsafe {
+            fs::dcache_init(
+                core::ptr::from_mut(&mut dcache.cache),
+                DCACHE_HASH_BITS,
+            )
+        };
+
+        dcache
+    }
+
+    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 _ }
+    }
+
+    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 _,
+            )
+        }
+    }
+}
+
+impl Drop for DentryCache {
+    fn drop(&mut self) {
+        unsafe {
+            fs::dcache_drop(core::ptr::from_mut(&mut self.cache));
+        }
+    }
+}

+ 403 - 0
src/kernel/vfs/ffi.rs

@@ -0,0 +1,403 @@
+use crate::prelude::*;
+
+use core::ffi::{c_char, c_void};
+
+use alloc::sync::Arc;
+use bindings::{dev_t, fs::D_PRESENT, 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},
+    s_isblk, s_ischr, s_isdir, s_isreg, DevId,
+};
+
+fn into_slice<'a>(buf: *const u8, bufsize: &usize) -> &'a [u8] {
+    unsafe { core::slice::from_raw_parts(buf, *bufsize) }
+}
+
+fn into_mut_slice<'a>(buf: *mut u8, bufsize: &usize) -> &'a mut [u8] {
+    unsafe { core::slice::from_raw_parts_mut(buf, *bufsize) }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_mount(
+    mountpoint: *mut DentryInner, // 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 source = get_str_from_cstr(source).unwrap();
+    let mountpoint_str = get_str_from_cstr(mountpoint_str).unwrap();
+    let fstype = get_str_from_cstr(fstype).unwrap();
+
+    // TODO: data
+    match super::mount::do_mount(
+        mountpoint,
+        source,
+        mountpoint_str,
+        fstype,
+        flags,
+        &[],
+    ) {
+        Ok(_) => 0,
+        Err(e) => -(e as i32),
+    }
+}
+
+fn do_read(
+    file: Arc<dyn Inode>,
+    buffer: &mut [u8],
+    count: usize,
+    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.read(buffer, count, offset),
+        mode if s_isblk(mode) => {
+            let devid = file.devid()?;
+
+            let ret = unsafe {
+                fs::block_device_read(
+                    devid,
+                    buffer.as_mut_ptr() as *mut _,
+                    buffer.len(),
+                    offset,
+                    count,
+                )
+            };
+
+            if ret < 0 {
+                Err(-ret as u32)
+            } else {
+                Ok(ret as usize)
+            }
+        }
+        mode if s_ischr(mode) => {
+            let devid = file.devid()?;
+
+            let ret = unsafe {
+                fs::char_device_read(
+                    devid,
+                    buffer.as_mut_ptr() as *mut _,
+                    buffer.len(),
+                    count,
+                )
+            };
+
+            if ret < 0 {
+                Err(-ret as u32)
+            } else {
+                Ok(ret as usize)
+            }
+        }
+        _ => Err(EINVAL),
+    }
+}
+
+fn do_write(
+    file: Arc<dyn 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_isblk(mode) => {
+            let devid = file.devid()?;
+
+            let ret = unsafe {
+                fs::block_device_write(
+                    devid,
+                    buffer.as_ptr() as *const _,
+                    offset,
+                    buffer.len(),
+                )
+            };
+
+            if ret < 0 {
+                Err(-ret as u32)
+            } else {
+                Ok(ret as usize)
+            }
+        }
+        mode if s_ischr(mode) => {
+            let devid = file.devid()?;
+
+            let ret = unsafe {
+                fs::char_device_write(
+                    devid,
+                    buffer.as_ptr() as *const _,
+                    buffer.len(),
+                )
+            };
+
+            if ret < 0 {
+                Err(-ret as u32)
+            } else {
+                Ok(ret as usize)
+            }
+        }
+        _ => Err(EINVAL),
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_read(
+    file: *const *const dyn Inode, // borrowed
+    buf: *mut u8,
+    bufsize: usize,
+    offset: usize,
+    n: usize,
+) -> isize {
+    let file = raw_inode_clone(file);
+    let buffer = into_mut_slice(buf, &bufsize);
+
+    match do_read(file, buffer, n, offset) {
+        Ok(n) => n as isize,
+        Err(e) => -(e as isize),
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_write(
+    file: *const *const dyn Inode, // borrowed
+    buf: *const u8,
+    offset: usize,
+    n: usize,
+) -> isize {
+    let file = raw_inode_clone(file);
+    let buffer = into_slice(buf, &n);
+
+    match do_write(file, buffer, offset) {
+        Ok(n) => n as isize,
+        Err(e) => -(e as isize),
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_statx(
+    file: *const *const dyn Inode, // borrowed
+    stat: *mut statx,
+    mask: u32,
+) -> i32 {
+    let file = raw_inode_clone(file);
+    let statx = unsafe { stat.as_mut() }.unwrap();
+
+    match file.statx(statx, mask) {
+        Ok(_) => 0,
+        Err(e) => -(e as i32),
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_truncate(
+    file: *const *const dyn Inode, // borrowed
+    size: usize,
+) -> i32 {
+    let file = raw_inode_clone(file);
+
+    match file.truncate(size) {
+        Ok(_) => 0,
+        Err(e) => -(e as i32),
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_readlink(
+    file: *const *const dyn Inode, // borrowed
+    buf: *mut c_char,
+    bufsize: usize,
+) -> i32 {
+    let file = raw_inode_clone(file);
+    let buffer = into_mut_slice(buf as *mut u8, &bufsize);
+
+    match file.readlink(buffer) {
+        Ok(n) => n as i32,
+        Err(e) => -(e as i32),
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_creat(
+    at: *mut DentryInner, // 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);
+
+    match parent.get_inode_clone().creat(&mut at, mode as u32) {
+        Ok(_) => 0,
+        Err(e) => -(e as i32),
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_mkdir(
+    at: *mut DentryInner, // 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);
+
+    match parent.get_inode_clone().mkdir(&mut at, mode as u32) {
+        Ok(_) => 0,
+        Err(e) => -(e as i32),
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_mknod(
+    at: *mut DentryInner, // borrowed
+    mode: mode_t,
+    dev: dev_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);
+
+    match parent
+        .get_inode_clone()
+        .mknod(&mut at, mode as u32, dev as DevId)
+    {
+        Ok(_) => 0,
+        Err(e) => -(e as i32),
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn fs_symlink(
+    at: *mut DentryInner, // 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),
+    }
+}
+
+#[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);
+}
+
+#[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();
+
+    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();
+
+    idata.size as _
+}
+
+extern "C" {
+    fn call_callback(
+        callback: *const c_void,
+        filename: *const c_char,
+        filename_len: usize,
+        inode: *const *const dyn Inode,
+        idata: *const InodeData,
+        always_zero: u8,
+    ) -> i32;
+}
+
+#[no_mangle]
+pub extern "C" fn fs_readdir(
+    file: *const *const dyn 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)
+        },
+    );
+
+    match ret {
+        Ok(n) => n as i64,
+        Err(e) => -(e as i64),
+    }
+}

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

@@ -180,7 +180,7 @@ static inline std::pair<dentry_pointer, int> _open_file(
         return {nullptr, -ENOENT};
 
     // create file
-    if (int ret = fs::creat(dent.get(), mode); ret != 0)
+    if (int ret = fs_creat(dent.get(), mode); ret != 0)
         return {nullptr, ret};
 
     return {std::move(dent), 0};
@@ -197,7 +197,7 @@ int filearray::open(dentry* cwd, types::path_iterator filepath, int flags,
     if (ret != 0)
         return ret;
 
-    auto filemode = dent->inode->mode;
+    auto filemode = r_get_inode_mode(&dent->inode);
 
     int fdflag = (flags & O_CLOEXEC) ? FD_CLOEXEC : 0;
 
@@ -218,14 +218,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(&dent->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, &dent->inode), fdflag);
 }
 
 int filearray::pipe(int (&pipefd)[2]) {

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

@@ -1,92 +0,0 @@
-#include <assert.h>
-
-#include <kernel/vfs.hpp>
-#include <kernel/vfs/inode.hpp>
-
-using namespace fs;
-
-int vfs::statx(inode* ind, struct statx* st, unsigned int mask) {
-    st->stx_mask = 0;
-
-    if (mask & STATX_NLINK) {
-        st->stx_nlink = ind->nlink;
-        st->stx_mask |= STATX_NLINK;
-    }
-
-    if (mask & STATX_ATIME) {
-        st->stx_atime.tv_nsec = ind->atime.tv_nsec;
-        st->stx_atime.tv_sec = ind->atime.tv_sec;
-
-        st->stx_mask |= STATX_ATIME;
-    }
-
-    if (mask & STATX_CTIME) {
-        st->stx_ctime.tv_nsec = ind->ctime.tv_nsec;
-        st->stx_ctime.tv_sec = ind->ctime.tv_sec;
-
-        st->stx_mask |= STATX_CTIME;
-    }
-
-    if (mask & STATX_MTIME) {
-        st->stx_mtime.tv_nsec = ind->mtime.tv_nsec;
-        st->stx_mtime.tv_sec = ind->mtime.tv_sec;
-
-        st->stx_mask |= STATX_MTIME;
-    }
-
-    if (mask & STATX_SIZE) {
-        st->stx_size = ind->size;
-        st->stx_mask |= STATX_SIZE;
-    }
-
-    st->stx_mode = 0;
-    if (mask & STATX_MODE) {
-        st->stx_mode |= ind->mode & ~S_IFMT;
-        st->stx_mask |= STATX_MODE;
-    }
-
-    if (mask & STATX_TYPE) {
-        st->stx_mode |= ind->mode & S_IFMT;
-        if (S_ISBLK(ind->mode) || S_ISCHR(ind->mode)) {
-            auto dev = i_device(ind);
-            assert(!(dev & 0x80000000));
-
-            st->stx_rdev_major = NODE_MAJOR(dev);
-            st->stx_rdev_minor = NODE_MINOR(dev);
-        }
-        st->stx_mask |= STATX_TYPE;
-    }
-
-    if (mask & STATX_INO) {
-        st->stx_ino = ind->ino;
-        st->stx_mask |= STATX_INO;
-    }
-
-    if (mask & STATX_BLOCKS) {
-        st->stx_blocks = (ind->size + 512 - 1) / 512;
-        st->stx_blksize = io_blksize();
-        st->stx_mask |= STATX_BLOCKS;
-    }
-
-    if (mask & STATX_UID) {
-        st->stx_uid = ind->uid;
-        st->stx_mask |= STATX_UID;
-    }
-
-    if (mask & STATX_GID) {
-        st->stx_gid = ind->gid;
-        st->stx_mask |= STATX_GID;
-    }
-
-    st->stx_dev_major = NODE_MAJOR(fs_device());
-    st->stx_dev_minor = NODE_MINOR(fs_device());
-
-    // TODO: support more attributes
-    st->stx_attributes_mask = 0;
-
-    return 0;
-}
-
-dev_t vfs::i_device(inode*) {
-    return -ENODEV;
-}

+ 240 - 0
src/kernel/vfs/inode.rs

@@ -0,0 +1,240 @@
+use core::any::Any;
+
+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,
+};
+
+use super::{
+    dentry::Dentry, s_isblk, s_ischr, vfs::Vfs, DevId, ReadDirCallback,
+    TimeSpec,
+};
+use crate::prelude::*;
+
+pub type Ino = u64;
+pub type ISize = u64;
+pub type Nlink = u64;
+pub type Uid = u32;
+pub type Gid = u32;
+pub type Mode = u32;
+
+#[repr(C)]
+pub struct InodeData {
+    pub ino: Ino,
+    pub size: ISize,
+    pub nlink: Nlink,
+
+    pub atime: TimeSpec,
+    pub mtime: TimeSpec,
+    pub ctime: TimeSpec,
+
+    pub uid: Uid,
+    pub gid: Gid,
+    pub mode: Mode,
+}
+
+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,
+        }
+    }
+}
+
+#[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>>>;
+    fn as_any(&self) -> &dyn Any;
+
+    fn readdir(
+        &self,
+        offset: usize,
+        callback: &mut ReadDirCallback,
+    ) -> KResult<usize>;
+
+    fn statx(&self, stat: &mut statx, mask: u32) -> KResult<()> {
+        let (fsdev, io_blksize) = {
+            let vfs = self.vfs_strong().ok_or(EIO)?;
+            let vfs = vfs.lock();
+            (vfs.fs_devid(), vfs.io_blksize())
+        };
+        let devid = self.devid();
+
+        let idata = self.idata().lock();
+
+        if mask & STATX_NLINK != 0 {
+            stat.stx_nlink = idata.nlink as _;
+            stat.stx_mask |= STATX_NLINK;
+        }
+
+        if mask & STATX_ATIME != 0 {
+            stat.stx_atime.tv_nsec = idata.atime.nsec as _;
+            stat.stx_atime.tv_sec = idata.atime.sec as _;
+            stat.stx_mask |= STATX_ATIME;
+        }
+
+        if mask & STATX_MTIME != 0 {
+            stat.stx_mtime.tv_nsec = idata.mtime.nsec as _;
+            stat.stx_mtime.tv_sec = idata.mtime.sec as _;
+            stat.stx_mask |= STATX_MTIME;
+        }
+
+        if mask & STATX_CTIME != 0 {
+            stat.stx_ctime.tv_nsec = idata.ctime.nsec as _;
+            stat.stx_ctime.tv_sec = idata.ctime.sec as _;
+            stat.stx_mask |= STATX_CTIME;
+        }
+
+        if mask & STATX_SIZE != 0 {
+            stat.stx_size = idata.size as _;
+            stat.stx_mask |= STATX_SIZE;
+        }
+
+        stat.stx_mode = 0;
+        if mask & STATX_MODE != 0 {
+            stat.stx_mode |= (idata.mode & !S_IFMT) as u16;
+            stat.stx_mask |= STATX_MODE;
+        }
+
+        if mask & STATX_TYPE != 0 {
+            stat.stx_mode |= (idata.mode & S_IFMT) as u16;
+            if s_isblk(idata.mode) || s_ischr(idata.mode) {
+                stat.stx_rdev_major = (devid? >> 8) & 0xff;
+                stat.stx_rdev_minor = devid? & 0xff;
+            }
+            stat.stx_mask |= STATX_TYPE;
+        }
+
+        if mask & STATX_INO != 0 {
+            stat.stx_ino = idata.ino as _;
+            stat.stx_mask |= STATX_INO;
+        }
+
+        if mask & STATX_BLOCKS != 0 {
+            stat.stx_blocks = (idata.size + 512 - 1) / 512;
+            stat.stx_blksize = io_blksize as _;
+            stat.stx_mask |= STATX_BLOCKS;
+        }
+
+        if mask & STATX_UID != 0 {
+            stat.stx_uid = idata.uid as _;
+            stat.stx_mask |= STATX_UID;
+        }
+
+        if mask & STATX_GID != 0 {
+            stat.stx_gid = idata.gid as _;
+            stat.stx_mask |= STATX_GID;
+        }
+
+        stat.stx_dev_major = (fsdev >> 8) & 0xff;
+        stat.stx_dev_minor = fsdev & 0xff;
+
+        // TODO: support more attributes
+        stat.stx_attributes_mask = 0;
+
+        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],
+        count: usize,
+        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>>,
+}
+
+impl<Fs: Vfs> InodeCache<Fs> {
+    pub fn new() -> Self {
+        Self {
+            cache: BTreeMap::new(),
+            vfs: Weak::new(),
+        }
+    }
+
+    pub fn get_vfs(&self) -> Weak<Mutex<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 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 get(&self, ino: Ino) -> Option<Arc<dyn Inode>> {
+        self.cache.get(&ino).cloned()
+    }
+
+    pub fn free(&mut self, ino: Ino) {
+        self.cache.remove(&ino);
+    }
+}

+ 51 - 0
src/kernel/vfs/mod.rs

@@ -0,0 +1,51 @@
+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};
+
+pub mod dentry;
+pub mod ffi;
+pub mod inode;
+pub mod mount;
+pub mod vfs;
+
+pub type DevId = dev_t;
+
+/// # Return
+///
+/// Return -1 if an error occurred
+///
+/// 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 fn s_isreg(mode: Mode) -> bool {
+    (mode & S_IFMT) == S_IFREG
+}
+
+pub fn s_isdir(mode: Mode) -> bool {
+    (mode & S_IFMT) == S_IFDIR
+}
+
+pub fn s_ischr(mode: Mode) -> bool {
+    (mode & S_IFMT) == S_IFCHR
+}
+
+pub fn s_isblk(mode: Mode) -> bool {
+    (mode & S_IFMT) == S_IFBLK
+}
+
+#[repr(C)]
+pub struct TimeSpec {
+    pub sec: u64,
+    pub nsec: u64,
+}
+
+impl TimeSpec {
+    pub fn new() -> Self {
+        Self { sec: 0, nsec: 0 }
+    }
+}

+ 239 - 0
src/kernel/vfs/mount.rs

@@ -0,0 +1,239 @@
+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 super::{
+    dentry::{Dentry, DentryCache, DentryInner},
+    inode::Inode,
+    vfs::Vfs,
+    Mutex,
+};
+
+pub const MS_RDONLY: u64 = 1 << 0;
+pub const MS_NOSUID: u64 = 1 << 1;
+pub const MS_NODEV: u64 = 1 << 2;
+pub const MS_NOEXEC: u64 = 1 << 3;
+pub const MS_NOATIME: u64 = 1 << 10;
+pub const MS_RELATIME: u64 = 1 << 21;
+pub const MS_STRICTATIME: u64 = 1 << 24;
+pub const MS_LAZYTIME: u64 = 1 << 25;
+
+const MOUNT_FLAGS: [(u64, &str); 6] = [
+    (MS_NOSUID, ",nosuid"),
+    (MS_NODEV, ",nodev"),
+    (MS_NOEXEC, ",noexec"),
+    (MS_NOATIME, ",noatime"),
+    (MS_RELATIME, ",relatime"),
+    (MS_LAZYTIME, ",lazytime"),
+];
+
+static MOUNT_CREATORS: Mutex<BTreeMap<String, Box<dyn MountCreator>>> =
+    Mutex::new(BTreeMap::new());
+
+static MOUNTS: Mutex<BTreeMap<Dentry, MountPointData>> =
+    Mutex::new(BTreeMap::new());
+
+static mut ROOT_DENTRY: Option<Dentry> = None;
+
+pub struct Mount {
+    dcache: Mutex<DentryCache>,
+    vfs: Arc<Mutex<dyn Vfs>>,
+    root: 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),
+            vfs,
+            root: dent,
+        }
+    }
+
+    pub fn root(&self) -> Dentry {
+        self.root.clone()
+    }
+}
+
+unsafe impl Send for Mount {}
+unsafe impl Sync for Mount {}
+
+pub trait MountCreator: Send + Sync {
+    fn create_mount(
+        &self,
+        source: &str,
+        flags: u64,
+        data: &[u8],
+    ) -> KResult<Mount>;
+}
+
+pub fn register_filesystem(
+    fstype: &str,
+    creator: Box<dyn MountCreator>,
+) -> KResult<()> {
+    let mut creators = MOUNT_CREATORS.lock();
+    match creators.entry(String::from(fstype)) {
+        Entry::Occupied(_) => Err(EEXIST),
+        Entry::Vacant(entry) => {
+            entry.insert(creator);
+            Ok(())
+        }
+    }
+}
+
+struct MountPointData {
+    mount: Mount,
+    source: String,
+    mountpoint: String,
+    fstype: String,
+    flags: u64,
+}
+
+pub fn do_mount(
+    mut mountpoint: 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;
+    }
+
+    if flags & MS_STRICTATIME != 0 {
+        flags &= !(MS_RELATIME | MS_NOATIME);
+    }
+
+    let mount = {
+        let creators = { MOUNT_CREATORS.lock() };
+        let creator = creators.get(fstype).ok_or(ENODEV)?;
+        creator.create_mount(source, flags, data)?
+    };
+
+    let mut root = mount.root();
+
+    root.parent = mountpoint.parent;
+    operator_eql_cxx_std_string(&mut root.name, &mountpoint.name);
+    root.hash = mountpoint.hash;
+
+    let mpdata = MountPointData {
+        mount,
+        source: String::from(source),
+        mountpoint: String::from(mountpoint_str),
+        fstype: String::from(fstype),
+        flags,
+    };
+
+    {
+        let mut mounts = MOUNTS.lock();
+        mountpoint.flags |= D_MOUNTPOINT;
+        mounts.insert(mountpoint.clone(), mpdata);
+    }
+
+    Ok(())
+}
+
+fn mount_opts(flags: u64) -> String {
+    let mut out = String::new();
+    if flags & MS_RDONLY != 0 {
+        out += "ro";
+    } else {
+        out += "rw";
+    }
+
+    for (flag, name) in MOUNT_FLAGS {
+        if flags & flag != 0 {
+            out += name;
+        }
+    }
+
+    out
+}
+
+pub fn dump_mounts(buffer: &mut dyn core::fmt::Write) {
+    for (_, mpdata) in MOUNTS.lock().iter() {
+        dont_check!(writeln!(
+            buffer,
+            "{} {} {} {} 0 0",
+            mpdata.source,
+            mpdata.mountpoint,
+            mpdata.fstype,
+            mount_opts(mpdata.flags)
+        ))
+    }
+}
+
+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 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()
+    };
+
+    let root = mount.root();
+    unsafe { ROOT_DENTRY = Some(root.clone()) };
+
+    let mpdata = MountPointData {
+        mount,
+        source,
+        mountpoint: String::from("/"),
+        fstype,
+        flags,
+    };
+
+    {
+        let mut mounts = MOUNTS.lock();
+        mounts.insert(root, mpdata);
+    }
+}

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

@@ -1,153 +0,0 @@
-#include <assert.h>
-#include <errno.h>
-#include <sys/mount.h>
-
-#include <kernel/vfs.hpp>
-#include <kernel/vfs/dentry.hpp>
-#include <kernel/vfs/vfs.hpp>
-
-using namespace fs;
-
-static std::map<std::string, fs::create_fs_func_t> fs_list;
-
-int fs::register_fs(const char* name, fs::create_fs_func_t func) {
-    fs_list.emplace(name, func);
-
-    return 0;
-}
-
-vfs::vfs(dev_t device, size_t io_blksize)
-    : m_device(device), m_io_blksize(io_blksize) {
-    dcache_init(&m_dcache, 8);
-}
-
-std::pair<vfs*, int> vfs::create(const char* source, const char* fstype,
-                                 unsigned long flags, const void* data) {
-    auto iter = fs_list.find(fstype);
-    if (!iter)
-        return {nullptr, -ENODEV};
-
-    auto& [_, func] = *iter;
-
-    if (!(flags & MS_NOATIME))
-        flags |= MS_RELATIME;
-
-    if (flags & MS_STRICTATIME)
-        flags &= ~(MS_RELATIME | MS_NOATIME);
-
-    return {func(source, flags, data), 0};
-}
-
-fs::inode* vfs::alloc_inode(ino_t ino) {
-    auto [iter, inserted] = m_inodes.try_emplace(ino);
-    iter->second.ino = ino;
-    iter->second.fs = this;
-
-    assert(inserted);
-    return &iter->second;
-}
-
-void vfs::free_inode(ino_t ino) {
-    int n = m_inodes.erase(ino);
-    assert(n == 1);
-}
-
-fs::inode* vfs::get_inode(ino_t ino) {
-    auto iter = m_inodes.find(ino);
-    // TODO: load inode from disk if not found
-    if (iter)
-        return &iter->second;
-    else
-        return nullptr;
-}
-
-void vfs::register_root_node(struct inode* root_inode) {
-    assert(!root());
-
-    m_root = fs::dcache_alloc(&m_dcache);
-    m_root->fs = this;
-
-    m_root->inode = root_inode;
-    m_root->flags = D_DIRECTORY | D_PRESENT;
-
-    fs::dcache_init_root(&m_dcache, m_root);
-}
-
-int vfs::mount(dentry* mnt, const char* source, const char* mount_point,
-               const char* fstype, unsigned long flags, const void* data) {
-    if (!(mnt->flags & D_DIRECTORY))
-        return -ENOTDIR;
-
-    auto [new_fs, ret] = vfs::create(source, fstype, flags, data);
-    if (ret != 0)
-        return ret;
-
-    mounts.emplace(d_get(mnt), mount_data{
-                                   .fs = new_fs,
-                                   .source = source,
-                                   .mount_point = mount_point,
-                                   .fstype = fstype,
-                                   .flags = flags,
-                               });
-    mnt->flags |= D_MOUNTPOINT;
-
-    auto* new_ent = new_fs->root();
-
-    new_ent->parent = mnt->parent;
-    new_ent->name = mnt->name;
-    new_ent->hash = mnt->hash;
-
-    return 0;
-}
-
-// default behavior is to
-// return -EINVAL to show that the operation
-// is not supported by the fs
-
-ssize_t vfs::read(inode*, char*, size_t, size_t, off_t) {
-    return -EINVAL;
-}
-
-ssize_t vfs::write(inode*, const char*, size_t, off_t) {
-    return -EINVAL;
-}
-
-int vfs::creat(inode*, dentry*, mode_t) {
-    return -EINVAL;
-}
-
-int vfs::mknod(inode*, dentry*, mode_t, dev_t) {
-    return -EINVAL;
-}
-
-int vfs::unlink(inode*, dentry*) {
-    return -EINVAL;
-}
-
-int vfs::mkdir(inode*, dentry*, mode_t) {
-    return -EINVAL;
-}
-
-int vfs::symlink(inode*, dentry*, const char*) {
-    return -EINVAL;
-}
-
-int vfs::readlink(inode*, char*, size_t) {
-    return -EINVAL;
-}
-
-int vfs::truncate(inode*, size_t) {
-    return -EINVAL;
-}
-
-struct dentry* vfs::root() const noexcept {
-    return m_root;
-}
-
-dev_t vfs::fs_device() const noexcept {
-    return m_device;
-}
-
-size_t vfs::io_blksize() const noexcept {
-    return m_io_blksize;
-}

+ 10 - 0
src/kernel/vfs/vfs.rs

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

+ 9 - 10
src/lib.rs

@@ -8,20 +8,13 @@ extern crate alloc;
 mod bindings;
 
 mod driver;
+mod fs;
 mod io;
 mod kernel;
 mod net;
+mod prelude;
 
-macro_rules! dont_check {
-    ($arg:expr) => {
-        match $arg {
-            Ok(_) => (),
-            Err(_) => (),
-        }
-    };
-}
-
-pub(crate) use dont_check;
+use prelude::*;
 
 #[panic_handler]
 fn panic(info: &core::panic::PanicInfo) -> ! {
@@ -66,6 +59,12 @@ static ALLOCATOR: Allocator = Allocator {};
 #[no_mangle]
 pub extern "C" fn late_init_rust() {
     driver::e1000e::register_e1000e_driver();
+
+    fs::tmpfs::init();
+    fs::procfs::init();
+    fs::fat32::init();
+
+    kernel::vfs::mount::create_rootfs();
 }
 
 //

+ 156 - 0
src/prelude.rs

@@ -0,0 +1,156 @@
+#[allow(dead_code)]
+pub type KResult<T> = Result<T, u32>;
+
+macro_rules! dont_check {
+    ($arg:expr) => {
+        match $arg {
+            Ok(_) => (),
+            Err(_) => (),
+        }
+    };
+}
+
+#[allow(unused_imports)]
+pub(crate) use dont_check;
+
+#[allow(unused_imports)]
+pub use crate::bindings::root as bindings;
+
+#[allow(unused_imports)]
+pub(crate) use crate::kernel::console::{print, println};
+
+#[allow(unused_imports)]
+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};
+
+pub struct Yield;
+
+extern "C" {
+    fn r_preempt_disable();
+    fn r_preempt_enable();
+}
+
+#[inline(always)]
+pub fn preempt_disable() {
+    unsafe {
+        r_preempt_disable();
+    }
+}
+
+#[inline(always)]
+pub fn preempt_enable() {
+    unsafe {
+        r_preempt_enable();
+    }
+}
+
+impl spin::RelaxStrategy for Yield {
+    fn relax() {
+        panic!("ohohoh");
+    }
+}
+
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct PreemptGuard;
+
+impl PreemptGuard {
+    #[inline(always)]
+    pub fn new() -> Self {
+        preempt_disable();
+        Self
+    }
+}
+
+impl Drop for PreemptGuard {
+    #[inline(always)]
+    fn drop(&mut self) {
+        preempt_enable();
+    }
+}
+
+#[repr(transparent)]
+pub struct MutexNoPreemptionGuard<'a, T: ?Sized> {
+    data_guard: spin::mutex::MutexGuard<'a, T>,
+    preempt_guard: PreemptGuard,
+}
+
+impl<'a, T: ?Sized> MutexNoPreemptionGuard<'a, T> {
+    #[inline(always)]
+    pub fn new(
+        preempt_guard: PreemptGuard,
+        data_guard: spin::mutex::MutexGuard<'a, T>,
+    ) -> Self {
+        Self {
+            data_guard,
+            preempt_guard,
+        }
+    }
+}
+
+impl<'a, T: ?Sized> core::ops::Deref for MutexNoPreemptionGuard<'a, T> {
+    type Target = <spin::mutex::MutexGuard<'a, T> as core::ops::Deref>::Target;
+
+    #[inline(always)]
+    fn deref(&self) -> &Self::Target {
+        &*self.data_guard
+    }
+}
+
+impl<'a, T: ?Sized> core::ops::DerefMut for MutexNoPreemptionGuard<'a, T> {
+    #[inline(always)]
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut *self.data_guard
+    }
+}
+
+#[repr(transparent)]
+pub struct MutexNoPreemption<T: ?Sized> {
+    lock: spin::mutex::Mutex<T, spin::Spin>,
+}
+
+impl<T> MutexNoPreemption<T> {
+    #[inline(always)]
+    pub const fn new(value: T) -> Self {
+        Self {
+            lock: spin::mutex::Mutex::new(value),
+        }
+    }
+}
+
+#[allow(dead_code)]
+impl<T: ?Sized> MutexNoPreemption<T> {
+    #[inline(always)]
+    pub fn lock(&self) -> MutexNoPreemptionGuard<T> {
+        let preempt_guard = PreemptGuard::new();
+        let data_guard = self.lock.lock();
+
+        MutexNoPreemptionGuard::new(preempt_guard, data_guard)
+    }
+
+    #[inline(always)]
+    pub fn is_locked(&self) -> bool {
+        self.lock.is_locked()
+    }
+
+    #[inline(always)]
+    pub fn try_lock(&self) -> Option<MutexNoPreemptionGuard<T>> {
+        let preempt_guard = PreemptGuard::new();
+        let data_guard = self.lock.try_lock();
+
+        data_guard.map(|data_guard| {
+            MutexNoPreemptionGuard::new(preempt_guard, data_guard)
+        })
+    }
+
+    #[inline(always)]
+    pub fn get_mut(&mut self) -> &mut T {
+        self.lock.get_mut()
+    }
+}
+
+#[allow(dead_code)]
+pub type RwLock<T> = spin::rwlock::RwLock<T, Yield>;
+pub type Mutex<T> = MutexNoPreemption<T>;

+ 4 - 5
src/types/elf.cpp

@@ -1,7 +1,6 @@
 #include <string>
 #include <vector>
 
-#include <assert.h>
 #include <errno.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -35,7 +34,7 @@ int types::elf::elf32_load(types::elf::elf32_load_data& d) {
 
     types::elf::elf32_header hdr{};
     auto n_read =
-        fs::read(exec->inode, (char*)&hdr, sizeof(types::elf::elf32_header), 0,
+        fs_read(&exec->inode, (char*)&hdr, sizeof(types::elf::elf32_header), 0,
                  sizeof(types::elf::elf32_header));
 
     if (n_read != sizeof(types::elf::elf32_header))
@@ -48,7 +47,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(&exec->inode, (char*)phents.data(), phents_size, hdr.phoff,
                       phents_size);
 
     // broken file or I/O error
@@ -56,7 +55,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(&exec->inode, (char*)shents.data(), shents_size, hdr.shoff,
                       shents_size);
 
     // broken file or I/O error
@@ -85,7 +84,7 @@ int types::elf::elf32_load(types::elf::elf32_load_data& d) {
 
             args.vaddr = vaddr;
             args.length = flen;
-            args.file_inode = exec->inode;
+            args.file_inode = &exec->inode;
             args.file_offset = fileoff;
 
             args.flags = MM_MAPPED;

+ 1 - 4
user-space-program/CMakeLists.txt

@@ -14,11 +14,8 @@ add_executable(hello-world.out hello-world.s)
 add_executable(interrupt-test.out interrupt-test.s)
 add_executable(stack-test.out stack-test.s)
 add_executable(init.out init.c)
-add_executable(sh.out sh.c)
 add_executable(priv-test.out priv-test.c)
-add_executable(lazybox.out lazybox.c)
 
 add_custom_target(user_space_programs
-    DEPENDS hello-world.out interrupt-test.out stack-test.out init.out sh.out priv-test.out
-    DEPENDS lazybox.out
+    DEPENDS hello-world.out interrupt-test.out stack-test.out init.out priv-test.out
 )

+ 32 - 9
user-space-program/init.c

@@ -1,19 +1,41 @@
 #include <assert.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
 #include <sys/wait.h>
+#include <unistd.h>
+
+#define print(str) write(STDERR_FILENO, str, _strlen(str))
 
-#define print(str) write(STDERR_FILENO, str, strlen(str))
+static size_t _strlen(const char* str) {
+    size_t len = 0;
+    while (str[len] != '\0') {
+        len++;
+    }
+    return len;
+}
+
+static __attribute__((used)) size_t strlen(const char* s) {
+    size_t len = 0;
+    while (*s++)
+        ++len;
+    return len;
+}
+
+static __attribute__((used)) void* memcpy(void* dst, const void* src,
+                                          size_t n) {
+    uint8_t* d = (uint8_t*)dst;
+    const uint8_t* s = (const uint8_t*)src;
+    for (size_t i = 0; i < n; ++i)
+        d[i] = s[i];
+    return dst;
+}
 
-int main(int argc, char** argv)
-{
+int main(int argc, char** argv) {
     int fd = 0;
     // Assumes three file descriptors open.
-    while((fd = open("/dev/console", 0)) >= 0){
-        if(fd >= 3){
+    while ((fd = open("/dev/console", 0)) >= 0) {
+        if (fd >= 3) {
             close(fd);
             break;
         }
@@ -56,7 +78,8 @@ _run_sh:;
     for (;;) {
         pid = wait(&ret);
         char* buf = NULL;
-        assert(asprintf(&buf, "[init] pid%d has exited with code %d\n", pid, ret) >= 0);
+        assert(asprintf(&buf, "[init] pid%d has exited with code %d\n", pid,
+                        ret) >= 0);
         print(buf);
         free(buf);
         // sh

+ 0 - 140
user-space-program/lazybox.c

@@ -1,140 +0,0 @@
-#include <unistd.h>
-#include <dirent.h>
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-struct applet {
-    const char* name;
-    int (*func)(const char** args);
-};
-
-int lazybox_version(void)
-{
-    printf("lazybox by greatbridf\n");
-    return 0;
-}
-
-int pwd(const char** _)
-{
-    (void)_;
-    char buf[256];
-    if (getcwd(buf, sizeof(buf)) == 0) {
-        printf("cannot get cwd\n");
-        return -1;
-    }
-    puts(buf);
-    return 0;
-}
-
-int ls(const char** args)
-{
-    const char* path = args[0];
-    DIR* dir = NULL;
-
-    if (path == NULL) {
-        char buf[256];
-        if (getcwd(buf, sizeof(buf)) == 0)
-            return -1;
-
-        dir = opendir(buf);
-    } else {
-        dir = opendir(args[0]);
-    }
-
-    if (!dir)
-        return -1;
-
-    struct dirent* dp = NULL;
-    while ((dp = readdir(dir)) != NULL) {
-        printf("%s ", dp->d_name);
-    }
-
-    printf("\n");
-
-    return 0;
-}
-
-struct applet applets[] = {
-    {
-        "lazybox",
-        NULL,
-    },
-    {
-        "pwd",
-        pwd,
-    },
-    {
-        "ls",
-        ls,
-    }
-};
-
-static inline int tolower(int c)
-{
-    if (c >= 'A' && c <= 'Z')
-        return c - 'A' + 'a';
-    return c;
-}
-
-int strcmpi(const char* a, const char* b)
-{
-    int ret = 0;
-    while (*a && *b) {
-        if (tolower(*a) != tolower(*b)) {
-            ret = 1;
-            break;
-        }
-        ++a, ++b;
-    }
-    if ((*a && !*b) || (*b && !*a)) {
-        ret = 1;
-    }
-    return ret;
-}
-
-const char* find_file_name(const char* path)
-{
-    const char* last = path + strlen(path);
-    for (; last != path; --last) {
-        if (*last == '/') {
-            ++last;
-            break;
-        }
-    }
-    return last == path ? path : last + 1;
-}
-
-int parse_applet(const char* name)
-{
-    if (!name)
-        return -1;
-
-    for (size_t i = 0; i < (sizeof(applets) / sizeof(struct applet)); ++i) {
-        if (strcmpi(applets[i].name, name) == 0) {
-            return i;
-        }
-    }
-
-    return -1;
-}
-
-int main(int argc, const char** argv)
-{
-    if (argc == 0)
-        return lazybox_version();
-
-    const char* name = find_file_name(*argv);
-    int type = parse_applet(find_file_name(*argv));
-
-    if (type < 0) {
-        printf("applet not found: %s\n", name);
-        return -1;
-    }
-
-    if (type == 0)
-        return main(argc - 1, argv + 1);
-    
-    return applets[type].func(argv + 1);
-}

+ 0 - 522
user-space-program/sh.c

@@ -1,522 +0,0 @@
-#include <stdint.h>
-#include <stdarg.h>
-#include <sys/wait.h>
-#include <stdio.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
-
-// Parsed command representation
-#define EXEC  1
-#define REDIR 2
-#define PIPE  3
-#define LIST  4
-#define BACK  5
-
-#define MAXARGS 10
-
-struct cmd {
-  int type;
-};
-
-struct execcmd {
-  int type;
-  char *argv[MAXARGS];
-  char *eargv[MAXARGS];
-};
-
-struct redircmd {
-  int type;
-  struct cmd *cmd;
-  char *file;
-  char *efile;
-  int mode;
-  int fd;
-};
-
-struct pipecmd {
-  int type;
-  struct cmd *left;
-  struct cmd *right;
-};
-
-struct listcmd {
-  int type;
-  struct cmd *left;
-  struct cmd *right;
-};
-
-struct backcmd {
-  int type;
-  struct cmd *cmd;
-};
-
-int fork1(void);  // Fork but panics on failure.
-void panic(char*);
-struct cmd *parsecmd(char*);
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Winfinite-recursion"
-#else
-#ifdef __GNUC__
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Winfinite-recursion"
-#endif
-#endif
-// Execute cmd.  Never returns.
-void
-runcmd(struct cmd *cmd)
-{
-  int p[2];
-  int code;
-  struct backcmd *bcmd;
-  struct execcmd *ecmd;
-  struct listcmd *lcmd;
-  struct pipecmd *pcmd;
-  struct redircmd *rcmd;
-
-  if(cmd == 0)
-    _exit(-1);
-  
-  switch(cmd->type){
-  default:
-    panic("runcmd");
-
-  case EXEC:
-    ecmd = (struct execcmd*)cmd;
-    if(ecmd->argv[0] == 0)
-      _exit(-1);
-    execve(ecmd->argv[0], ecmd->argv, environ);
-    printf("exec %s failed\n", ecmd->argv[0]);
-    break;
-
-  case REDIR:
-    rcmd = (struct redircmd*)cmd;
-    close(rcmd->fd);
-    if(open(rcmd->file, rcmd->mode, 0666) < 0){
-      printf("open %s failed\n", rcmd->file);
-      _exit(-1);
-    }
-    runcmd(rcmd->cmd);
-    break;
-
-  case LIST:
-    lcmd = (struct listcmd*)cmd;
-    if(fork1() == 0)
-      runcmd(lcmd->left);
-    wait(&code);
-    runcmd(lcmd->right);
-    break;
-
-   case PIPE:
-     pcmd = (struct pipecmd*)cmd;
-     if(pipe(p) < 0)
-       panic("pipe");
-     if(fork1() == 0){
-       close(1);
-       dup(p[1]);
-       close(p[0]);
-       close(p[1]);
-       runcmd(pcmd->left);
-     }
-     if(fork1() == 0){
-       close(0);
-       dup(p[0]);
-       close(p[0]);
-       close(p[1]);
-       runcmd(pcmd->right);
-     }
-     close(p[0]);
-     close(p[1]);
-     wait(&code);
-     wait(&code);
-     break;
-    
-  case BACK:
-    bcmd = (struct backcmd*)cmd;
-    if(fork1() == 0)
-      runcmd(bcmd->cmd);
-    break;
-  }
-  _exit(0);
-}
-#ifdef __clang__
-#pragma clang diagnostic pop
-#else
-#ifdef __GNUC__
-#pragma GCC diagnostic pop
-#endif
-#endif
-
-int
-getcmd(char *buf, int nbuf)
-{
-  printf("[root@localhost] #\n");
-  memset(buf, 0, nbuf);
-  gets(buf);
-  if(buf[0] == 0) // EOF
-    return -1;
-  return 0;
-}
-
-int
-main(void)
-{
-  static char buf[100];
-  
-  int fd = 0;
-  // Assumes three file descriptors open.
-  while((fd = open("/dev/console", 0)) >= 0){
-    if(fd >= 3){
-      close(fd);
-      break;
-    }
-  }
-  
-  // Read and run input commands.
-  while(getcmd(buf, sizeof(buf)) >= 0){
-    if(buf[0] == 'c' && buf[1] == 'd' && buf[2] == ' ')
-    {
-      // Clumsy but will have to do for now.
-      // Chdir has no effect on the parent if run in the child.
-      if(chdir(buf+3) < 0)
-        printf("cannot cd %s\n", buf+3);
-      continue;
-    }
-    pid_t pid = 0;
-    if((pid = fork1()) == 0) {
-      setpgid(0, 0);
-      runcmd(parsecmd(buf));
-    }
-    tcsetpgrp(STDOUT_FILENO, pid);
-    setpgid(pid, 0);
-    int code;
-    wait(&code);
-    tcsetpgrp(STDOUT_FILENO, getpid());
-    printf("[status: %d] ", code);
-  }
-  _exit(0);
-}
-
-void __attribute__((noreturn))
-panic(char *s)
-{
-  printf("%s\n", s);
-  _exit(-1);
-}
-
-int
-fork1(void)
-{
-  int pid;
-  
-  pid = fork();
-  if(pid == -1)
-    panic("fork");
-  return pid;
-}
-
-//PAGEBREAK!
-// Constructors
-
-struct cmd*
-execcmd(void)
-{
-  struct execcmd *cmd;
-
-  cmd = malloc(sizeof(*cmd));
-  memset(cmd, 0, sizeof(*cmd));
-  cmd->type = EXEC;
-  return (struct cmd*)cmd;
-}
-
-struct cmd*
-redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
-{
-  struct redircmd *cmd;
-
-  cmd = malloc(sizeof(*cmd));
-  memset(cmd, 0, sizeof(*cmd));
-  cmd->type = REDIR;
-  cmd->cmd = subcmd;
-  cmd->file = file;
-  cmd->efile = efile;
-  cmd->mode = mode;
-  cmd->fd = fd;
-  return (struct cmd*)cmd;
-}
-
-struct cmd*
-pipecmd(struct cmd *left, struct cmd *right)
-{
-  struct pipecmd *cmd;
-
-  cmd = malloc(sizeof(*cmd));
-  memset(cmd, 0, sizeof(*cmd));
-  cmd->type = PIPE;
-  cmd->left = left;
-  cmd->right = right;
-  return (struct cmd*)cmd;
-}
-
-struct cmd*
-listcmd(struct cmd *left, struct cmd *right)
-{
-  struct listcmd *cmd;
-
-  cmd = malloc(sizeof(*cmd));
-  memset(cmd, 0, sizeof(*cmd));
-  cmd->type = LIST;
-  cmd->left = left;
-  cmd->right = right;
-  return (struct cmd*)cmd;
-}
-
-struct cmd*
-backcmd(struct cmd *subcmd)
-{
-  struct backcmd *cmd;
-
-  cmd = malloc(sizeof(*cmd));
-  memset(cmd, 0, sizeof(*cmd));
-  cmd->type = BACK;
-  cmd->cmd = subcmd;
-  return (struct cmd*)cmd;
-}
-//PAGEBREAK!
-// Parsing
-
-char whitespace[] = " \t\r\n\v";
-char symbols[] = "<|>&;()";
-
-int
-gettoken(char **ps, char *es, char **q, char **eq)
-{
-  char *s;
-  int ret;
-  
-  s = *ps;
-  while(s < es && strchr(whitespace, *s))
-    s++;
-  if(q)
-    *q = s;
-  ret = *s;
-  switch(*s){
-  case 0:
-    break;
-  case '|':
-  case '(':
-  case ')':
-  case ';':
-  case '&':
-  case '<':
-    s++;
-    break;
-  case '>':
-    s++;
-    if(*s == '>'){
-      ret = '+';
-      s++;
-    }
-    break;
-  default:
-    ret = 'a';
-    while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
-      s++;
-    break;
-  }
-  if(eq)
-    *eq = s;
-  
-  while(s < es && strchr(whitespace, *s))
-    s++;
-  *ps = s;
-  return ret;
-}
-
-int
-peek(char **ps, char *es, char *toks)
-{
-  char *s;
-  
-  s = *ps;
-  while(s < es && strchr(whitespace, *s))
-    s++;
-  *ps = s;
-  return *s && strchr(toks, *s);
-}
-
-struct cmd *parseline(char**, char*);
-struct cmd *parsepipe(char**, char*);
-struct cmd *parseexec(char**, char*);
-struct cmd *nulterminate(struct cmd*);
-
-struct cmd*
-parsecmd(char *s)
-{
-  char *es;
-  struct cmd *cmd;
-
-  es = s + strlen(s);
-  cmd = parseline(&s, es);
-  peek(&s, es, "");
-  if(s != es){
-    printf("leftovers: %s\n", s);
-    panic("syntax");
-  }
-  nulterminate(cmd);
-  return cmd;
-}
-
-struct cmd*
-parseline(char **ps, char *es)
-{
-  struct cmd *cmd;
-
-  cmd = parsepipe(ps, es);
-  while(peek(ps, es, "&")){
-    gettoken(ps, es, 0, 0);
-    cmd = backcmd(cmd);
-  }
-  if(peek(ps, es, ";")){
-    gettoken(ps, es, 0, 0);
-    cmd = listcmd(cmd, parseline(ps, es));
-  }
-  return cmd;
-}
-
-struct cmd*
-parsepipe(char **ps, char *es)
-{
-  struct cmd *cmd;
-
-  cmd = parseexec(ps, es);
-  if(peek(ps, es, "|")){
-    gettoken(ps, es, 0, 0);
-    cmd = pipecmd(cmd, parsepipe(ps, es));
-  }
-  return cmd;
-}
-
-struct cmd*
-parseredirs(struct cmd *cmd, char **ps, char *es)
-{
-  int tok;
-  char *q, *eq;
-
-  while(peek(ps, es, "<>")){
-    tok = gettoken(ps, es, 0, 0);
-    if(gettoken(ps, es, &q, &eq) != 'a')
-      panic("missing file for redirection");
-    switch(tok){
-    case '<':
-      cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
-      break;
-    case '>':
-      cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREAT | O_TRUNC, 1);
-      break;
-    case '+':  // >>
-      cmd = redircmd(cmd, q, eq, O_WRONLY | O_CREAT | O_APPEND, 1);
-      break;
-    }
-  }
-  return cmd;
-}
-
-struct cmd*
-parseblock(char **ps, char *es)
-{
-  struct cmd *cmd;
-
-  if(!peek(ps, es, "("))
-    panic("parseblock");
-  gettoken(ps, es, 0, 0);
-  cmd = parseline(ps, es);
-  if(!peek(ps, es, ")"))
-    panic("syntax - missing )");
-  gettoken(ps, es, 0, 0);
-  cmd = parseredirs(cmd, ps, es);
-  return cmd;
-}
-
-struct cmd*
-parseexec(char **ps, char *es)
-{
-  char *q, *eq;
-  int tok, argc;
-  struct execcmd *cmd;
-  struct cmd *ret;
-  
-  if(peek(ps, es, "("))
-    return parseblock(ps, es);
-
-  ret = execcmd();
-  cmd = (struct execcmd*)ret;
-
-  argc = 0;
-  ret = parseredirs(ret, ps, es);
-  while(!peek(ps, es, "|)&;")){
-    if((tok=gettoken(ps, es, &q, &eq)) == 0)
-      break;
-    if(tok != 'a')
-      panic("syntax");
-    cmd->argv[argc] = q;
-    cmd->eargv[argc] = eq;
-    argc++;
-    if(argc >= MAXARGS)
-      panic("too many args");
-    ret = parseredirs(ret, ps, es);
-  }
-  cmd->argv[argc] = 0;
-  cmd->eargv[argc] = 0;
-  return ret;
-}
-
-// NUL-terminate all the counted strings.
-struct cmd*
-nulterminate(struct cmd *cmd)
-{
-  int i;
-  struct backcmd *bcmd;
-  struct execcmd *ecmd;
-  struct listcmd *lcmd;
-  struct pipecmd *pcmd;
-  struct redircmd *rcmd;
-
-  if(cmd == 0)
-    return 0;
-  
-  switch(cmd->type){
-  case EXEC:
-    ecmd = (struct execcmd*)cmd;
-    for(i=0; ecmd->argv[i]; i++)
-      *ecmd->eargv[i] = 0;
-    break;
-
-  case REDIR:
-    rcmd = (struct redircmd*)cmd;
-    nulterminate(rcmd->cmd);
-    *rcmd->efile = 0;
-    break;
-
-  case PIPE:
-    pcmd = (struct pipecmd*)cmd;
-    nulterminate(pcmd->left);
-    nulterminate(pcmd->right);
-    break;
-    
-  case LIST:
-    lcmd = (struct listcmd*)cmd;
-    nulterminate(lcmd->left);
-    nulterminate(lcmd->right);
-    break;
-
-  case BACK:
-    bcmd = (struct backcmd*)cmd;
-    nulterminate(bcmd->cmd);
-    break;
-  }
-  return cmd;
-}