Browse Source

Merge branch 'vfs'

greatbridf 2 years ago
parent
commit
7741c9d172

+ 6 - 0
CMakeLists.txt

@@ -46,6 +46,7 @@ set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         src/kernel/tty.c
                         src/kernel/stdio.c
                         src/kernel/mem.c
+                        src/kernel/vfs.cpp
                         src/kernel/vga.c
                         src/kernel/hw/keyboard.cpp
                         src/kernel/hw/serial.c
@@ -63,6 +64,7 @@ set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         include/kernel/process.h
                         include/kernel/stdio.h
                         include/kernel/mem.h
+                        include/kernel/vfs.h
                         include/kernel/vga.h
                         include/kernel/hw/keyboard.h
                         include/kernel/hw/serial.h
@@ -76,7 +78,11 @@ set(KERNEL_MAIN_SOURCES src/kernel_main.c
                         include/types/status.h
                         include/types/stdint.h
                         include/types/list.h
+                        include/types/allocator.hpp
+                        include/types/cplusplus.hpp
                         include/types/list.hpp
+                        include/types/string.hpp
+                        include/types/vector.hpp
                         include/kernel_main.h
                         )
 add_library(kernel_main STATIC ${KERNEL_MAIN_SOURCES})

+ 12 - 0
include/kernel/errno.h

@@ -2,10 +2,22 @@
 
 #include <types/types.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 extern uint32_t* _get_errno(void);
 
 #define errno (*_get_errno())
 
+#ifdef __cplusplus
+}
+#endif
+
 #define ENOMEM 0
 #define ENOTFOUND 1
 #define EEXIST (1 << 1)
+#define ENOENT (1 << 2)
+#define EINVAL (1 << 3)
+#define EISDIR (1 << 4)
+#define ENOTDIR (1 << 5)

+ 2 - 1
include/kernel/stdio.h

@@ -17,7 +17,8 @@ extern "C" {
 void* memcpy(void* dst, const void* src, size_t n);
 void* memset(void* dst, int c, size_t n);
 size_t strlen(const char* str);
-char* strncpy(char* dst, const char* src, size_t max_n);
+char* strncpy(char* dst, const char* src, size_t n);
+int strcmp(const char* s1, const char* s2);
 
 ssize_t
 snprint_decimal(

+ 89 - 0
include/kernel/vfs.h

@@ -0,0 +1,89 @@
+#pragma once
+
+#include <types/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct inode;
+struct inode_flags;
+struct inode_ops;
+struct stat;
+struct dirent;
+
+typedef size_t (*inode_read)(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
+typedef size_t (*inode_write)(struct inode* file, const char* buf, size_t offset, size_t n);
+typedef int (*inode_readdir)(struct inode* dir, struct dirent* entry, size_t i);
+typedef struct inode* (*inode_findinode)(struct inode* dir, const char* filename);
+typedef int (*inode_mkfile)(struct inode* dir, const char* filename);
+typedef int (*inode_rmfile)(struct inode* dir, const char* filename);
+typedef int (*inode_mkdir)(struct inode* dir, const char* dirname);
+typedef int (*inode_stat)(struct inode* dir, struct stat* stat, const char* dirname);
+
+typedef uint32_t ino_t;
+typedef uint32_t blksize_t;
+typedef uint32_t blkcnt_t;
+
+struct inode_flags {
+    uint32_t file : 1;
+    uint32_t directory : 1;
+    uint32_t mount_point : 1;
+};
+
+struct inode_ops {
+    inode_read read;
+    inode_write write;
+    inode_readdir readdir;
+    inode_findinode findinode;
+    inode_mkfile mkfile;
+    inode_rmfile rmfile;
+    inode_mkdir mkdir;
+    inode_stat stat;
+};
+
+struct fs_info {
+    const struct inode_ops* ops;
+    void* impl;
+};
+
+struct inode {
+    struct inode_flags flags;
+    uint32_t perm;
+    void* impl;
+    ino_t ino;
+    struct fs_info* fs;
+};
+
+struct stat {
+    ino_t st_ino;
+    blksize_t st_blksize;
+    blkcnt_t st_blocks;
+};
+
+struct dirent {
+    char name[128];
+    uint32_t ino;
+};
+
+extern struct inode* fs_root;
+
+void init_vfs(void);
+
+size_t vfs_read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n);
+size_t vfs_write(struct inode* file, const char* buf, size_t offset, size_t n);
+int vfs_readdir(struct inode* dir, struct dirent* entry, size_t i);
+struct inode* vfs_findinode(struct inode* dir, const char* filename);
+int vfs_mkfile(struct inode* dir, const char* filename);
+int vfs_rmfile(struct inode* dir, const char* filename);
+int vfs_mkdir(struct inode* dir, const char* dirname);
+
+// @return pointer to the inode if found, nullptr if not
+struct inode* vfs_open(const char* path);
+
+// @return GB_OK if succeed, GB_FAILED if failed and set errno
+int vfs_stat(struct stat* stat, const char* path);
+
+#ifdef __cplusplus
+}
+#endif

+ 75 - 0
include/types/allocator.hpp

@@ -0,0 +1,75 @@
+#pragma once
+#include <types/types.h>
+
+inline void* operator new(size_t, void* ptr)
+{
+    return ptr;
+}
+
+namespace types {
+template <typename T>
+class kernel_allocator {
+public:
+    using value_type = T;
+
+    static value_type* allocate_memory(size_t count)
+    {
+        return static_cast<value_type*>(::k_malloc(sizeof(value_type) * count));
+    }
+
+    static void deallocate_memory(value_type* ptr)
+    {
+        ::k_free(ptr);
+    }
+};
+
+template <typename Allocator>
+class allocator_traits {
+public:
+    using value_type = typename Allocator::value_type;
+
+    static value_type* allocate(size_t count)
+    {
+        if (count == 0)
+            return nullptr;
+        return Allocator::allocate_memory(sizeof(value_type) * count);
+    }
+
+    template <typename... Args>
+    static value_type* construct(value_type* ptr, Args... args)
+    {
+        new (ptr) value_type(args...);
+        return ptr;
+    }
+
+    template <typename... Args>
+    static value_type* allocate_and_construct(Args... args)
+    {
+        auto* ptr = allocate(1);
+        construct(ptr, args...);
+        return ptr;
+    }
+
+    static void deconstruct(value_type* ptr)
+    {
+        if (!ptr)
+            return;
+        ptr->~value_type();
+    }
+
+    static void deallocate(value_type* ptr)
+    {
+        if (!ptr)
+            return;
+        Allocator::deallocate_memory(ptr);
+    }
+
+    static void deconstruct_and_deallocate(value_type* ptr)
+    {
+        if (!ptr)
+            return;
+        deconstruct(ptr);
+        deallocate(ptr);
+    }
+};
+} // namespace types

+ 53 - 0
include/types/cplusplus.hpp

@@ -0,0 +1,53 @@
+#pragma once
+
+#ifdef __cplusplus
+
+namespace types::traits::inner {
+
+template <typename Tp, typename>
+struct remove_pointer {
+    using type = Tp;
+};
+
+template <typename Tp, typename T>
+struct remove_pointer<Tp, T*> {
+    using type = T;
+};
+
+template <typename Tr, typename>
+struct remove_reference {
+    using type = Tr;
+};
+
+template <typename Tr, typename T>
+struct remove_reference<Tr, T&> {
+    using type = T;
+};
+
+} // namespace types::traits::inner
+
+namespace types::traits {
+
+template <typename Tp>
+struct remove_pointer
+    : inner::remove_pointer<Tp, Tp> {
+};
+
+template <typename Tr>
+struct remove_reference
+    : inner::remove_reference<Tr, Tr> {
+};
+
+template <typename T>
+struct add_pointer {
+    using type = T*;
+};
+
+template <typename T>
+struct add_reference {
+    using type = T&;
+};
+
+} // namespace types
+
+#endif

+ 22 - 57
include/types/list.hpp

@@ -1,56 +1,11 @@
 #pragma once
 
 #include <kernel/mem.h>
+#include <types/allocator.hpp>
 #include <types/types.h>
 
-inline void* operator new(size_t, void* ptr)
-{
-    return ptr;
-}
-
 namespace types {
 
-template <typename T>
-class kernel_allocator {
-public:
-    using value_type = T;
-
-    static value_type* allocate_memory(size_t count)
-    {
-        return static_cast<value_type*>(::k_malloc(sizeof(value_type) * count));
-    }
-
-    static void deallocate_memory(value_type* ptr)
-    {
-        ::k_free(ptr);
-    }
-};
-
-template <typename Allocator>
-class allocator_traits {
-public:
-    using value_type = typename Allocator::value_type;
-
-    static value_type* allocate_memory(size_t count)
-    {
-        return Allocator::allocate_memory(count);
-    }
-
-    template <typename... Args>
-    static value_type* allocate(Args... args)
-    {
-        value_type* ptr = allocate_memory(sizeof(value_type));
-        new (ptr) value_type(args...);
-        return ptr;
-    }
-
-    static void deallocate(value_type* ptr)
-    {
-        ptr->~value_type();
-        Allocator::deallocate_memory(ptr);
-    }
-};
-
 template <typename T, template <typename _value_type> class Allocator = kernel_allocator>
 class list {
 private:
@@ -105,12 +60,12 @@ private:
 public:
     class iterator {
     public:
-        explicit iterator(const iterator& iter) noexcept
+        iterator(const iterator& iter) noexcept
             : n(iter.n)
         {
         }
 
-        explicit iterator(iterator&& iter) noexcept
+        iterator(iterator&& iter) noexcept
             : n(iter.n)
         {
         }
@@ -163,7 +118,12 @@ public:
 
         pointer_type operator->() const noexcept
         {
-            return (static_cast<node_type*>(n))->value;
+            return &(static_cast<node_type*>(n))->value;
+        }
+
+        pointer_type ptr(void) const noexcept
+        {
+            return &(static_cast<node_type*>(n))->value;
         }
 
         node_base_type* _node(void) const noexcept
@@ -192,8 +152,8 @@ private:
 public:
     list() noexcept
         // size is stored in the 'head' node
-        : head(allocator_traits<sentry_allocator_type>::allocate(0))
-        , tail(allocator_traits<sentry_allocator_type>::allocate(0))
+        : head(allocator_traits<sentry_allocator_type>::allocate_and_construct(0))
+        , tail(allocator_traits<sentry_allocator_type>::allocate_and_construct(0))
     {
         head->connect(tail);
         tail->connect(head);
@@ -204,25 +164,30 @@ public:
         for (auto iter = begin(); iter != end(); ++iter) {
             erase(iter);
         }
-        allocator_traits<sentry_allocator_type>::deallocate(static_cast<sentry_node_type*>(head));
-        allocator_traits<sentry_allocator_type>::deallocate(static_cast<sentry_node_type*>(tail));
+        allocator_traits<sentry_allocator_type>::deconstruct_and_deallocate(static_cast<sentry_node_type*>(head));
+        allocator_traits<sentry_allocator_type>::deconstruct_and_deallocate(static_cast<sentry_node_type*>(tail));
     }
 
-    // TODO: find
+    iterator_type find(const value_type& v) noexcept
+    {
+        for (iterator_type iter = begin(); iter != end(); ++iter)
+            if (*iter == v)
+                return iter;
+    }
 
     // erase the node which iter points to
     void erase(const iterator_type& iter) noexcept
     {
         node_base_type* current_node = iter._node();
         current_node->prev->connect(current_node->next);
-        allocator_traits<allocator_type>::deallocate(static_cast<node_type*>(current_node));
+        allocator_traits<allocator_type>::deconstruct_and_deallocate(static_cast<node_type*>(current_node));
         --_size();
     }
 
     // insert the value v in front of the given iterator
     void insert(const iterator_type& iter, const value_type& v) noexcept
     {
-        node_base_type* new_node = allocator_traits<allocator_type>::allocate(v);
+        node_base_type* new_node = allocator_traits<allocator_type>::allocate_and_construct(v);
         iter._node()->prev->connect(new_node);
         new_node->connect(iter._node());
 
@@ -232,7 +197,7 @@ public:
     // insert the value v in front of the given iterator
     void insert(const iterator_type& iter, value_type&& v) noexcept
     {
-        node_base_type* new_node = allocator_traits<allocator_type>::allocate(v);
+        node_base_type* new_node = allocator_traits<allocator_type>::allocate_and_construct(v);
         iter._node().prev->connect(new_node);
         new_node->connect(iter._node());
 

+ 100 - 0
include/types/string.hpp

@@ -0,0 +1,100 @@
+#pragma once
+
+#include <kernel/stdio.h>
+#include <types/allocator.hpp>
+#include <types/types.h>
+#include <types/vector.hpp>
+
+#ifdef __cplusplus
+
+namespace types {
+template <template <typename _value_type> class Allocator = kernel_allocator>
+class string : public types::vector<char, Allocator> {
+public:
+    using inner_vector_type = types::vector<char, Allocator>;
+    using size_type = typename inner_vector_type::size_type;
+
+    static inline constexpr size_type npos = (-1U);
+
+public:
+    explicit string(size_type capacity = 8)
+        : inner_vector_type(capacity)
+    {
+        this->push_back(0x00);
+    }
+    string(const char* str, size_type n = npos)
+        : string()
+    {
+        this->append(str, n);
+    }
+    string(const string& str)
+        : inner_vector_type((const inner_vector_type&)str)
+    {
+    }
+    string& append(const char* str, size_type n = npos)
+    {
+        this->pop_back();
+
+        while (n-- && *str != 0x00) {
+            this->push_back(*str);
+            ++str;
+        }
+
+        this->push_back(0x00);
+        return *this;
+    }
+    string& append(const string& str)
+    {
+        return this->append(str.data());
+    }
+    string& operator+=(const char c)
+    {
+        *this->back() = c;
+        this->push_back(0x00);
+        return *this;
+    }
+    string& operator+=(const char* str)
+    {
+        return this->append(str);
+    }
+    string& operator+=(const string& str)
+    {
+        return this->append(str);
+    }
+    string substr(size_type pos, size_type n = npos)
+    {
+        return string(this->m_arr + pos, n);
+    }
+    const char* c_str(void) const noexcept
+    {
+        return this->data();
+    }
+    void clear(void)
+    {
+        inner_vector_type::clear();
+        this->push_back(0x00);
+    }
+    char pop(void)
+    {
+        this->pop_back();
+        auto iter = inner_vector_type::back();
+        char c = *iter;
+        *iter = 0x00;
+        return c;
+    }
+    typename inner_vector_type::iterator_type back(void)
+    {
+        return --inner_vector_type::back();
+    }
+    typename inner_vector_type::const_iterator_type back(void) const
+    {
+        return --inner_vector_type::back();
+    }
+    typename inner_vector_type::const_iterator_type cback(void) const
+    {
+        return --inner_vector_type::cback();
+    }
+};
+} // namespace types
+
+#endif

+ 320 - 0
include/types/vector.hpp

@@ -0,0 +1,320 @@
+#pragma once
+
+#include <kernel/mem.h>
+#include <types/allocator.hpp>
+#include <types/cplusplus.hpp>
+#include <types/types.h>
+
+namespace types {
+
+template <typename T, template <typename _value_type> class Allocator = kernel_allocator>
+class vector {
+public:
+    template <typename Pointer>
+    class iterator;
+
+    using value_type = T;
+    using pointer_type = value_type*;
+    using reference_type = value_type&;
+    using iterator_type = iterator<value_type*>;
+    using const_iterator_type = iterator<const value_type*>;
+    using size_type = size_t;
+    using difference_type = ssize_t;
+    using index_type = size_type;
+    using allocator_type = Allocator<value_type>;
+
+public:
+    template <typename Pointer>
+    class iterator {
+    public:
+        using Value = typename types::traits::remove_pointer<Pointer>::type;
+        using Reference = typename types::traits::add_reference<Value>::type;
+
+    public:
+        iterator(const iterator& iter) noexcept
+            : p(iter.p)
+        {
+        }
+
+        iterator(iterator&& iter) noexcept
+            : p(iter.p)
+        {
+        }
+
+        explicit iterator(Pointer p) noexcept
+            : p(p)
+        {
+        }
+
+        bool operator==(const iterator& iter) noexcept
+        {
+            return this->p == iter.p;
+        }
+
+        bool operator!=(const iterator& iter) noexcept
+        {
+            return !(*this == iter);
+        }
+
+        iterator& operator++() noexcept
+        {
+            ++p;
+            return *this;
+        }
+
+        iterator operator++(int) noexcept
+        {
+            iterator iter(*this);
+            ++p;
+            return iter;
+        }
+
+        iterator& operator--() noexcept
+        {
+            --p;
+            return *this;
+        }
+
+        iterator operator--(int) noexcept
+        {
+            iterator iter(*this);
+            --p;
+            return iter;
+        }
+
+        iterator operator+(size_type n) noexcept
+        {
+            iterator iter(p + n);
+            return iter;
+        }
+
+        iterator operator-(size_type n) noexcept
+        {
+            iterator iter(p - n);
+            return iter;
+        }
+
+        Reference operator*() const noexcept
+        {
+            return *p;
+        }
+
+        Pointer operator->() const noexcept
+        {
+            return p;
+        }
+
+    protected:
+        Pointer p;
+    };
+
+public:
+    explicit vector(size_type capacity = 1) noexcept
+        : m_arr(nullptr)
+        , m_size(0)
+    {
+        resize(capacity);
+    }
+
+    vector(const vector<T, Allocator>& arr) noexcept
+        : vector(arr.capacity())
+    {
+        for (const auto& item : arr)
+            push_back(item);
+    }
+
+    ~vector() noexcept
+    {
+        resize(0);
+    }
+
+    void resize(size_type n)
+    {
+        value_type* new_ptr = allocator_traits<allocator_type>::allocate(n);
+
+        m_capacity = n;
+        size_t orig_size = m_size;
+        if (m_size > m_capacity)
+            m_size = m_capacity;
+
+        for (size_t i = 0; i < m_size; ++i)
+            allocator_traits<allocator_type>::construct(new_ptr + i, _at(i));
+
+        for (size_t i = 0; i < orig_size; ++i)
+            allocator_traits<allocator_type>::deconstruct(m_arr + i);
+
+        if (m_arr)
+            allocator_traits<allocator_type>::deallocate(m_arr);
+        m_arr = new_ptr;
+    }
+
+    // TODO: find
+
+    // erase the node which iter points to
+    // void erase(const iterator_type& iter) noexcept
+    // {
+    //     allocator_traits<allocator_type>::deconstruct(iter.p);
+    //     --m_size;
+    // }
+
+    // insert the value v in front of the given iterator
+    // void insert(const iterator_type& iter, const value_type& v) noexcept
+    // {
+    //     node_base_type* new_node = allocator_traits<allocator_type>::allocate(v);
+    //     iter._node()->prev->connect(new_node);
+    //     new_node->connect(iter._node());
+
+    //     ++_size();
+    // }
+
+    // insert the value v in front of the given iterator
+    // void insert(const iterator_type& iter, value_type&& v) noexcept
+    // {
+    //     node_base_type* new_node = allocator_traits<allocator_type>::allocate(v);
+    //     iter._node().prev->connect(new_node);
+    //     new_node->connect(iter._node());
+
+    //     ++_size();
+    // }
+
+    value_type* data(void) noexcept
+    {
+        return m_arr;
+    }
+
+    const value_type* data(void) const noexcept
+    {
+        return m_arr;
+    }
+
+    value_type& at(index_type i) noexcept
+    {
+        // TODO: boundary check
+        return _at(i);
+    }
+
+    const value_type& at(index_type i) const noexcept
+    {
+        // TODO: boundary check
+        return _at(i);
+    }
+
+    value_type& operator[](index_type i) noexcept
+    {
+        return at(i);
+    }
+
+    const value_type& operator[](index_type i) const noexcept
+    {
+        return at(i);
+    }
+
+    void push_back(const value_type& v) noexcept
+    {
+        if (m_size == m_capacity)
+            resize(m_capacity * 2);
+        allocator_traits<allocator_type>::construct(m_arr + m_size, v);
+        ++m_size;
+    }
+
+    void push_back(value_type&& v) noexcept
+    {
+        if (m_size == m_capacity)
+            resize(m_capacity * 2);
+        allocator_traits<allocator_type>::construct(m_arr + m_size, v);
+        ++m_size;
+    }
+
+    void pop_back(void) noexcept
+    {
+        allocator_traits<allocator_type>::deconstruct(&*back());
+        --m_size;
+    }
+
+    size_type size(void) const noexcept
+    {
+        return m_size;
+    }
+
+    size_type capacity(void) const noexcept
+    {
+        return m_capacity;
+    }
+
+    const_iterator_type cbegin() const noexcept
+    {
+        return const_iterator_type(m_arr);
+    }
+
+    const_iterator_type cend() const noexcept
+    {
+        return const_iterator_type(m_arr + m_size);
+    }
+
+    iterator_type begin() noexcept
+    {
+        return iterator_type(m_arr);
+    }
+
+    const_iterator_type begin() const noexcept
+    {
+        return cbegin();
+    }
+
+    iterator_type end() noexcept
+    {
+        return iterator_type(m_arr + m_size);
+    }
+
+    const_iterator_type end() const noexcept
+    {
+        return cend();
+    }
+
+    iterator_type back() noexcept
+    {
+        return iterator_type(m_arr + m_size - 1);
+    }
+
+    const_iterator_type back() const noexcept
+    {
+        return const_iterator_type(m_arr + m_size - 1);
+    }
+
+    bool empty(void) const noexcept
+    {
+        return size() == 0;
+    }
+
+    void clear(void)
+    {
+        for (size_t i = 0; i < size(); ++i)
+            allocator_traits<allocator_type>::deconstruct(m_arr + i);
+        m_size = 0;
+    }
+
+    // TODO
+
+    // iterator_type r_start() noexcept;
+    // iterator_type r_end() noexcept;
+
+    // iterator_type cr_start() noexcept;
+    // iterator_type cr_end() noexcept;
+
+protected:
+    inline const value_type& _at(index_type i) const
+    {
+        return m_arr[i];
+    }
+    inline value_type& _at(index_type i)
+    {
+        return m_arr[i];
+    }
+
+protected:
+    value_type* m_arr;
+    size_type m_capacity;
+    size_type m_size;
+};
+
+} // namespace types

+ 25 - 1
src/kernel/stdio.c

@@ -412,7 +412,7 @@ snprintf(
     return n_write;
 }
 
-#define BYTES_PER_MAX_COPY_UNIT (sizeof(uint32_t)/sizeof(uint8_t))
+#define BYTES_PER_MAX_COPY_UNIT (sizeof(uint32_t) / sizeof(uint8_t))
 void* memcpy(void* dst, const void* src, size_t n)
 {
     void* orig_dst = dst;
@@ -448,3 +448,27 @@ size_t strlen(const char* str)
         ++n;
     return n;
 }
+
+char* strncpy(char* dst, const char* src, size_t n)
+{
+    size_t len = strlen(src);
+
+    if (len < n) {
+        memset(dst + len, 0x00, n - len);
+        memcpy(dst, src, len);
+    } else {
+        memcpy(dst, src, n);
+    }
+
+    return dst;
+}
+
+int strcmp(const char* s1, const char* s2)
+{
+    int c;
+    while ((c = *s1 - *s2) == 0 && *s1 != 0) {
+        ++s1;
+        ++s2;
+    }
+    return c;
+}

+ 421 - 0
src/kernel/vfs.cpp

@@ -0,0 +1,421 @@
+#include <kernel/errno.h>
+#include <kernel/mem.h>
+#include <kernel/stdio.h>
+#include <kernel/tty.h>
+#include <kernel/vfs.h>
+#include <types/allocator.hpp>
+#include <types/list.hpp>
+#include <types/string.hpp>
+#include <types/vector.hpp>
+
+using types::allocator_traits;
+using types::kernel_allocator;
+using types::list;
+using types::string;
+using types::vector;
+
+struct tmpfs_file_entry {
+    size_t ino;
+    char filename[128];
+};
+
+class tmpfs {
+private:
+    using inode_list_type = list<struct inode, kernel_allocator>;
+
+private:
+    size_t m_limit;
+    // TODO: hashtable etc.
+    inode_list_type m_inodes;
+    struct fs_info m_fs;
+    size_t m_last_inode_no;
+
+protected:
+    inline vector<struct tmpfs_file_entry>* mk_fe_vector(void)
+    {
+        return allocator_traits<kernel_allocator<vector<struct tmpfs_file_entry>>>::allocate_and_construct();
+    }
+
+    inline vector<char>* mk_data_vector(void)
+    {
+        return allocator_traits<kernel_allocator<vector<char>>>::allocate_and_construct();
+    }
+
+    inline struct inode mk_inode(unsigned int dir, unsigned int file, unsigned int mnt, void* data)
+    {
+        struct inode i { };
+        i.flags.directory = dir;
+        i.flags.file = file;
+        i.flags.mount_point = mnt;
+        i.fs = &m_fs;
+        i.impl = data;
+        i.ino = m_last_inode_no++;
+        i.perm = 0777;
+        return i;
+    }
+
+public:
+    explicit tmpfs(size_t limit);
+    void mklink(struct inode* dir, struct inode* inode, const char* filename);
+    void mkfile(struct inode* dir, const char* filename);
+    void mkdir(struct inode* dir, const char* dirname);
+    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 readdir(struct inode* dir, struct dirent* entry, size_t i);
+    struct inode* findinode(struct inode* dir, const char* filename);
+    int stat(struct inode* dir, struct stat* stat, const char* filename);
+
+    struct inode* root_inode(void)
+    {
+        return &*m_inodes.begin();
+    }
+};
+
+size_t tmpfs_read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+{
+    auto* fs = static_cast<tmpfs*>(file->fs->impl);
+    return fs->read(file, buf, buf_size, offset, n);
+}
+size_t tmpfs_write(struct inode* file, const char* buf, size_t offset, size_t n)
+{
+    auto* fs = static_cast<tmpfs*>(file->fs->impl);
+    return fs->write(file, buf, offset, n);
+}
+int tmpfs_readdir(struct inode* dir, struct dirent* entry, size_t i)
+{
+    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
+    return fs->readdir(dir, entry, i);
+}
+struct inode* tmpfs_findinode(struct inode* dir, const char* filename)
+{
+    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
+    return fs->findinode(dir, filename);
+}
+int tmpfs_mkfile(struct inode* dir, const char* filename)
+{
+    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
+    fs->mkfile(dir, filename);
+    return GB_OK;
+}
+// int tmpfs_rmfile(struct inode* dir, const char* filename)
+// {
+//     auto* fs = static_cast<tmpfs*>(dir->fs->impl);
+//     fs->rmfile(dir, filename);
+//     return GB_OK;
+// }
+int tmpfs_mkdir(struct inode* dir, const char* dirname)
+{
+    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
+    fs->mkdir(dir, dirname);
+    return GB_OK;
+}
+int tmpfs_stat(struct inode* dir, struct stat* stat, const char* filename)
+{
+    auto* fs = static_cast<tmpfs*>(dir->fs->impl);
+    return fs->stat(dir, stat, filename);
+}
+
+static const struct inode_ops tmpfs_inode_ops = {
+    .read = tmpfs_read,
+    .write = tmpfs_write,
+    .readdir = tmpfs_readdir,
+    .findinode = tmpfs_findinode,
+    .mkfile = tmpfs_mkfile,
+    .rmfile = 0,
+    .mkdir = tmpfs_mkdir,
+    .stat = tmpfs_stat,
+};
+
+tmpfs::tmpfs(size_t limit)
+    : m_limit(limit)
+    , m_fs { .ops = &tmpfs_inode_ops, .impl = this }
+    , m_last_inode_no(0)
+{
+    struct inode in = mk_inode(1, 0, 1, mk_fe_vector());
+
+    mklink(&in, &in, ".");
+    mklink(&in, &in, "..");
+
+    m_inodes.push_back(in);
+}
+
+void tmpfs::mklink(struct inode* dir, struct inode* inode, const char* filename)
+{
+    auto* fes = static_cast<vector<struct tmpfs_file_entry>*>(dir->impl);
+    struct tmpfs_file_entry ent = {
+        .ino = inode->ino,
+        .filename = { 0 },
+    };
+    snprintf(ent.filename, sizeof(ent.filename), filename);
+    fes->push_back(ent);
+}
+
+void tmpfs::mkfile(struct inode* dir, const char* filename)
+{
+    struct inode file = mk_inode(0, 1, 0, mk_data_vector());
+    m_inodes.push_back(file);
+    mklink(dir, &file, filename);
+}
+
+void tmpfs::mkdir(struct inode* dir, const char* dirname)
+{
+    struct inode new_dir = mk_inode(1, 0, 0, mk_fe_vector());
+    m_inodes.push_back(new_dir);
+    mklink(&new_dir, &new_dir, ".");
+
+    mklink(dir, &new_dir, dirname);
+    mklink(&new_dir, dir, "..");
+}
+
+size_t tmpfs::read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+{
+    if (file->flags.file != 1)
+        return 0;
+
+    auto* data = static_cast<vector<char>*>(file->impl);
+    size_t fsize = data->size();
+
+    if (offset + n > fsize)
+        n = fsize - offset;
+
+    if (buf_size < n) {
+        n = buf_size;
+    }
+
+    memcpy(buf, data->data() + offset, n);
+
+    return n;
+}
+
+size_t tmpfs::write(struct inode* file, const char* buf, size_t offset, size_t n)
+{
+    if (file->flags.file != 1)
+        return 0;
+
+    auto* data = static_cast<vector<char>*>(file->impl);
+
+    for (size_t i = data->size(); i < offset + n; ++i) {
+        data->push_back(0);
+    }
+    memcpy(data->data() + offset, buf, n);
+
+    return n;
+}
+
+int tmpfs::readdir(struct inode* dir, struct dirent* entry, size_t i)
+{
+    if (dir->flags.directory != 1) {
+        errno = ENOTDIR;
+        return GB_FAILED;
+    }
+
+    auto* fes = static_cast<vector<struct tmpfs_file_entry>*>(dir->impl);
+
+    if (i >= fes->size()) {
+        errno = ENOENT;
+        return GB_FAILED;
+    }
+
+    entry->ino = fes->at(i).ino;
+    snprintf(entry->name, sizeof(entry->name), fes->at(i).filename);
+
+    return GB_OK;
+}
+
+struct inode* tmpfs::findinode(struct inode* dir, const char* filename)
+{
+    struct dirent ent { };
+    size_t i = 0;
+    while (readdir(dir, &ent, i) == GB_OK) {
+        if (strcmp(ent.name, filename) == 0) {
+            // optimize: use hash table to build an index
+            auto& inodes = static_cast<tmpfs*>(dir->fs->impl)->m_inodes;
+            for (auto iter = inodes.begin(); iter != inodes.end(); ++iter)
+                if (iter->ino == ent.ino)
+                    return iter.ptr();
+        }
+        ++i;
+    }
+    return nullptr;
+}
+
+int tmpfs::stat(struct inode* dir, struct stat* stat, const char* filename)
+{
+    // for later use
+    // auto* fes = static_cast<vector<struct tmpfs_file_entry>*>(dir->impl);
+
+    auto* file_inode = vfs_findinode(dir, filename);
+
+    if (!file_inode) {
+        errno = ENOENT;
+        return GB_FAILED;
+    }
+
+    stat->st_ino = file_inode->ino;
+    if (file_inode->flags.file) {
+        stat->st_blksize = 1;
+        stat->st_blocks = static_cast<vector<char>*>(file_inode->impl)->size();
+    }
+    if (file_inode->flags.directory) {
+        stat->st_blksize = sizeof(struct tmpfs_file_entry);
+        stat->st_blocks = static_cast<vector<struct tmpfs_file_entry>*>(file_inode->impl)->size();
+    }
+
+    return GB_OK;
+}
+
+size_t vfs_read(struct inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
+{
+    if (file->fs->ops->read) {
+        return file->fs->ops->read(file, buf, buf_size, offset, n);
+    } else {
+        return 0;
+    }
+}
+size_t vfs_write(struct inode* file, const char* buf, size_t offset, size_t n)
+{
+    if (file->fs->ops->write) {
+        return file->fs->ops->write(file, buf, offset, n);
+    } else {
+        return 0;
+    }
+}
+int vfs_readdir(struct inode* dir, struct dirent* entry, size_t i)
+{
+    if (dir->fs->ops->readdir) {
+        return dir->fs->ops->readdir(dir, entry, i);
+    } else {
+        return 0;
+    }
+}
+struct inode* vfs_findinode(struct inode* dir, const char* filename)
+{
+    if (dir->fs->ops->findinode) {
+        return dir->fs->ops->findinode(dir, filename);
+    } else {
+        return nullptr;
+    }
+}
+int vfs_mkfile(struct inode* dir, const char* filename)
+{
+    if (dir->fs->ops->mkfile) {
+        return dir->fs->ops->mkfile(dir, filename);
+    } else {
+        return 0;
+    }
+}
+int vfs_rmfile(struct inode* dir, const char* filename)
+{
+    if (dir->fs->ops->rmfile) {
+        return dir->fs->ops->rmfile(dir, filename);
+    } else {
+        return 0;
+    }
+}
+int vfs_mkdir(struct inode* dir, const char* dirname)
+{
+    if (dir->fs->ops->mkdir) {
+        return dir->fs->ops->mkdir(dir, dirname);
+    } else {
+        return 0;
+    }
+}
+
+struct inode* vfs_open(const char* path)
+{
+    if (path[0] == '/' && path[1] == 0x00) {
+        return fs_root;
+    }
+
+    struct inode* cur = fs_root;
+    size_t n = 0;
+    switch (*(path++)) {
+    // absolute path
+    case '/':
+        while (true) {
+            if (path[n] == 0x00) {
+                string fname(path, n);
+                cur = vfs_findinode(cur, fname.c_str());
+                return cur;
+            }
+            if (path[n] == '/') {
+                string fname(path, n);
+                cur = vfs_findinode(cur, fname.c_str());
+                if (path[n + 1] == 0x00) {
+                    return cur;
+                } else {
+                    path += (n + 1);
+                    n = 0;
+                    continue;
+                }
+            }
+            ++n;
+        }
+        break;
+    // empty string
+    case 0x00:
+        return nullptr;
+        break;
+    // relative path
+    default:
+        return nullptr;
+        break;
+    }
+    return nullptr;
+}
+
+int vfs_stat(struct stat* stat, const char* _path)
+{
+    if (_path[0] == '/' && _path[1] == 0x00) {
+        if (fs_root->fs->ops->stat) {
+            return fs_root->fs->ops->stat(fs_root, stat, ".");
+        } else {
+            errno = EINVAL;
+            return GB_FAILED;
+        }
+    }
+
+    string path(_path);
+    auto iter = path.back();
+    while (*(iter - 1) != '/')
+        --iter;
+    string filename(&*iter);
+    string parent_path = path.substr(0, &*iter - path.data());
+
+    auto* dir_inode = vfs_open(parent_path.c_str());
+
+    if (!dir_inode) {
+        errno = ENOENT;
+        return GB_FAILED;
+    }
+
+    if (dir_inode->fs->ops->stat) {
+        return dir_inode->fs->ops->stat(dir_inode, stat, filename.c_str());
+    } else {
+        errno = EINVAL;
+        return GB_FAILED;
+    }
+}
+
+struct inode* fs_root;
+static tmpfs* rootfs;
+
+void init_vfs(void)
+{
+    rootfs = allocator_traits<kernel_allocator<tmpfs>>::allocate_and_construct(4096 * 1024);
+    fs_root = rootfs->root_inode();
+
+    vfs_mkdir(fs_root, "dev");
+    vfs_mkdir(fs_root, "root");
+    vfs_mkfile(fs_root, "init");
+
+    auto* init = vfs_open("/init");
+    const char* str = "#/bin/sh\nexec /bin/sh\n";
+    vfs_write(init, str, 0, strlen(str));
+
+    struct stat _stat { };
+
+    vfs_stat(&_stat, "/init");
+    vfs_stat(&_stat, "/");
+    vfs_stat(&_stat, "/dev");
+}

+ 7 - 1
src/kernel_main.c

@@ -11,6 +11,7 @@
 #include <kernel/mem.h>
 #include <kernel/stdio.h>
 #include <kernel/tty.h>
+#include <kernel/vfs.h>
 #include <kernel/vga.h>
 #include <types/bitmap.h>
 
@@ -129,7 +130,7 @@ void load_new_gdt(void)
 
 void init_bss_section(void)
 {
-    void* bss_addr = bss_section_start_addr;
+    void* bss_addr = (void*)bss_section_start_addr;
     size_t bss_size = bss_section_end_addr - bss_section_start_addr;
     memset(bss_addr, 0x00, bss_size);
 }
@@ -182,6 +183,11 @@ void kernel_main(void)
 
     k_malloc_buf[4096] = '\x89';
 
+    init_vfs();
+
+    struct inode* init = vfs_open("/init");
+    vfs_read(init, buf, 128, 1, 10);
+
     printkf("No work to do, halting...\n");
 
     while (1) {