Browse Source

feat(syscall): add getdents

greatbridf 2 years ago
parent
commit
c8ff5b072a

+ 16 - 7
include/fs/fat.hpp

@@ -122,15 +122,26 @@ private:
     char label[12];
     cluster_t* fat;
 
+    struct buf_object {
+        char* data;
+        int ref;
+        // bool dirty;
+    };
+    types::hash_map<cluster_t, buf_object, types::linux_hasher<cluster_t>> buf;
+
     // buf MUST be larger than 512 bytes
-    inline void read_sector(void* buf, uint32_t sector_no);
+    inline void _raw_read_sector(void* buf, uint32_t sector_no);
 
     // buf MUST be larger than 4096 bytes
-    inline void read_cluster(void* buf, cluster_t no);
+    inline void _raw_read_cluster(void* buf, cluster_t no);
+
+    // buffered version, release_cluster(cluster_no) after used
+    char* read_cluster(cluster_t no);
+    void release_cluster(cluster_t no);
 
-    static inline cluster_t cl(const inode* ind)
+    static constexpr cluster_t cl(const inode* ind)
     {
-        return reinterpret_cast<cluster_t>(ind->impl);
+        return ind->ino;
     }
 
     static inline cluster_t _rearrange(directory_entry* d)
@@ -149,9 +160,6 @@ private:
         }
     }
 
-protected:
-    virtual int load_dentry(dentry* ent) override;
-
 public:
     fat32(const fat32&) = delete;
     explicit fat32(inode* _device);
@@ -159,6 +167,7 @@ public:
 
     virtual size_t inode_read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n) override;
     virtual int inode_stat(dentry* ent, stat* st) override;
+    virtual int inode_readdir(fs::inode* dir, size_t offset, fs::vfs::filldir_func callback) override;
 };
 
 }; // namespace fs::fat

+ 8 - 1
include/kernel/process.hpp

@@ -215,8 +215,15 @@ public:
             //     return -1;
             // }
 
+            // TODO: unify file, inode, dentry TYPE
+            fs::file::types type = fs::file::types::regular_file;
+            if (dentry->ind->flags.in.directory)
+                type = fs::file::types::directory;
+            if (dentry->ind->flags.in.special_node)
+                type = fs::file::types::block_dev;
+
             auto iter = files->emplace_back(fs::file {
-                fs::file::types::regular_file,
+                type,
                 dentry->ind,
                 dentry->parent,
                 0,

+ 46 - 12
include/kernel/vfs.hpp

@@ -1,8 +1,10 @@
 #pragma once
 
 #include <types/allocator.hpp>
+#include <types/function.hpp>
 #include <types/hash_map.hpp>
 #include <types/list.hpp>
+#include <types/map.hpp>
 #include <types/stdint.h>
 #include <types/types.h>
 #include <types/vector.hpp>
@@ -12,6 +14,19 @@
 #define INODE_MNT (1 << 2)
 #define INODE_NODE (1 << 3)
 
+// dirent file types
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+#define DT_MAX (S_DT_MASK + 1) /* 16 */
+
 namespace fs {
 using ino_t = size_t;
 using blksize_t = size_t;
@@ -32,12 +47,12 @@ union inode_flags {
 struct inode {
     inode_flags flags;
     uint32_t perm;
-    void* impl;
     ino_t ino;
     vfs* fs;
     size_t size;
 };
 
+#define SN_INVALID (0xffffffff)
 union node_t {
     uint32_t v;
     struct {
@@ -70,6 +85,14 @@ struct stat {
     blkcnt_t st_blocks;
 };
 
+struct PACKED user_dirent {
+    ino_t d_ino; // inode number
+    uint32_t d_off; // ignored
+    uint16_t d_reclen; // length of this struct user_dirent
+    char d_name[1]; // file name with a padding zero
+    // uint8_t d_type; // file type, with offset of (d_reclen - 1)
+};
+
 class vfs {
 public:
     struct dentry {
@@ -105,8 +128,8 @@ public:
 
         ~dentry();
 
-        dentry* append(inode* ind, const name_type& name);
-        dentry* append(inode* ind, name_type&& name);
+        dentry* append(inode* ind, const name_type& name, bool set_dirty);
+        dentry* append(inode* ind, name_type&& name, bool set_dirty);
 
         dentry* find(const name_type& name);
 
@@ -115,29 +138,26 @@ public:
         void invalidate(void);
     };
 
+public:
+    using filldir_func = std::function<int(const char*, size_t, ino_t, uint8_t)>;
+
 private:
     // TODO: use allocator designed for small objects
-    using inode_list = types::list<inode>;
-    using inode_index_cache_list = types::hash_map<ino_t, inode*, types::linux_hasher<ino_t>>;
+    using inode_list = types::map<ino_t, inode>;
 
 private:
     inode_list _inodes;
-    inode_index_cache_list _idx_inodes;
     types::hash_map<dentry*, dentry*, types::linux_hasher<dentry*>> _mount_recover_list;
-    ino_t _last_inode_no;
-
-private:
-    ino_t _assign_inode_id(void);
 
 protected:
     dentry _root;
 
 protected:
-    inode* cache_inode(inode_flags flags, uint32_t perm, size_t size, void* impl_data);
+    inode* cache_inode(inode_flags flags, uint32_t perm, size_t size, ino_t ino);
     inode* get_inode(ino_t ino);
     void register_root_node(inode* root);
 
-    virtual int load_dentry(dentry* ent);
+    int load_dentry(dentry* ent);
 
 public:
     explicit vfs(void);
@@ -160,11 +180,25 @@ public:
     virtual int inode_rmfile(dentry* dir, const char* filename);
     virtual int inode_mkdir(dentry* dir, const char* dirname);
     virtual int inode_stat(dentry* dir, stat* stat);
+    virtual uint32_t inode_getnode(inode* file);
+
+    // 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 int inode_readdir(inode* dir, size_t offset, filldir_func callback) = 0;
 };
 
 struct file {
     enum class types {
         regular_file,
+        directory,
+        block_dev,
+        char_dev,
     } type;
     inode* ind;
     vfs::dentry* parent;

+ 1 - 1
src/asm/interrupt.s

@@ -197,7 +197,7 @@ asm_load_idt_skip:
 .globl syscall_stub
 .type  syscall_stub @function
 syscall_stub:
-    cmpl $8, %eax
+    cmpl $16, %eax
     jge syscall_stub_end
     pushal
 

+ 86 - 36
src/fs/fat.cpp

@@ -1,6 +1,7 @@
 #include <fs/fat.hpp>
 #include <kernel/mem.h>
 #include <kernel/mm.hpp>
+#include <kernel/stdio.hpp>
 #include <kernel/vfs.hpp>
 #include <types/allocator.hpp>
 #include <types/assert.h>
@@ -10,7 +11,7 @@
 
 namespace fs::fat {
 // buf MUST be larger than 512 bytes
-inline void fat32::read_sector(void* buf, uint32_t sector_no)
+inline void fat32::_raw_read_sector(void* buf, uint32_t sector_no)
 {
     assert(vfs_read(
                device,
@@ -22,33 +23,71 @@ inline void fat32::read_sector(void* buf, uint32_t sector_no)
 }
 
 // buf MUST be larger than 4096 bytes
-inline void fat32::read_cluster(void* buf, cluster_t no)
+inline void fat32::_raw_read_cluster(void* buf, cluster_t no)
 {
     // data cluster start from cluster #2
     no -= 2;
     for (int i = 0; i < sectors_per_cluster; ++i) {
         // skip reserved sectors
-        read_sector((char*)buf + SECTOR_SIZE * i, data_region_offset + no * sectors_per_cluster + i);
+        _raw_read_sector((char*)buf + SECTOR_SIZE * i, data_region_offset + no * sectors_per_cluster + i);
     }
 }
 
-int fat32::load_dentry(dentry* ent)
+char* fat32::read_cluster(cluster_t no)
 {
-    cluster_t next = cl(ent->ind);
-    auto buf = (char*)k_malloc(4096);
+    auto iter = buf.find(no);
+    if (iter) {
+        ++iter->value.ref;
+        return iter->value.data;
+    }
+    auto* data = (char*)k_malloc(sectors_per_cluster * SECTOR_SIZE);
+    _raw_read_cluster(data, no);
+    buf.emplace(no,
+        buf_object {
+            data,
+            1,
+            // false,
+        });
+    return data;
+}
+
+void fat32::release_cluster(cluster_t no)
+{
+    auto iter = buf.find(no);
+    if (iter)
+        --iter->value.ref;
+}
+
+int fat32::inode_readdir(fs::inode* dir, size_t offset, fs::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 {
-        read_cluster(buf, next);
-        auto* d = reinterpret_cast<directory_entry*>(buf);
-        for (; d->filename[0]; ++d) {
+        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)
                 continue;
-            auto* ind = cache_inode({ .in {
-                                        .file = !d->attributes.subdir,
-                                        .directory = d->attributes.subdir,
-                                        .mount_point = 0,
-                                        .special_node = 0,
-                                    } },
-                0777, d->size, (void*)_rearrange(d));
+
+            fs::ino_t ino = _rearrange(d);
+            auto* ind = get_inode(ino);
+            if (!ind) {
+                ind = cache_inode({ .in {
+                                      .file = !d->attributes.subdir,
+                                      .directory = d->attributes.subdir,
+                                      .mount_point = 0,
+                                      .special_node = 0,
+                                  } },
+                    0777, d->size, ino);
+            }
+
             types::string<> fname;
             for (int i = 0; i < 8; ++i) {
                 if (d->filename[i] == ' ')
@@ -64,12 +103,20 @@ int fat32::load_dentry(dentry* ent)
                     break;
                 fname += d->extension[i];
             }
-            ent->append(ind, fname);
+            auto ret = filldir(fname.c_str(), 0, ind->ino,
+                (ind->flags.in.directory || ind->flags.in.mount_point) ? DT_DIR : DT_REG);
+
+            if (ret != GB_OK) {
+                release_cluster(next);
+                return nread;
+            }
+
+            nread += sizeof(directory_entry);
         }
+        release_cluster(next);
         next = fat[next];
     } while (next < EOC);
-    k_free(buf);
-    return GB_OK;
+    return nread;
 }
 
 fat32::fat32(inode* _device)
@@ -77,7 +124,7 @@ fat32::fat32(inode* _device)
     , label { 0 }
 {
     char* buf = (char*)k_malloc(SECTOR_SIZE);
-    read_sector(buf, 0);
+    _raw_read_sector(buf, 0);
 
     auto* info = reinterpret_cast<ext_boot_sector*>(buf);
 
@@ -93,7 +140,7 @@ fat32::fat32(inode* _device)
     fat = (cluster_t*)k_malloc(SECTOR_SIZE * sectors_per_fat);
     // TODO: optimize
     for (uint32_t i = 0; i < 4; ++i)
-        read_sector((char*)fat + i * SECTOR_SIZE, reserved_sectors + i);
+        _raw_read_sector((char*)fat + i * SECTOR_SIZE, reserved_sectors + i);
     for (uint32_t i = 4; i < sectors_per_fat; ++i)
         memset((char*)fat + i * SECTOR_SIZE, 0x00, SECTOR_SIZE);
 
@@ -104,7 +151,7 @@ fat32::fat32(inode* _device)
     }
     label[i] = 0x00;
 
-    read_sector(buf, info->fs_info_sector);
+    _raw_read_sector(buf, info->fs_info_sector);
 
     auto* fsinfo = reinterpret_cast<fs_info_sector*>(buf);
     free_clusters = fsinfo->free_clusters;
@@ -116,7 +163,11 @@ fat32::fat32(inode* _device)
     cluster_t next = root_dir;
     while ((next = fat[next]) < EOC)
         ++_root_dir_clusters;
-    auto* n = cache_inode({ INODE_MNT | INODE_DIR }, 0777, _root_dir_clusters * sectors_per_cluster * SECTOR_SIZE, (void*)root_dir);
+    auto* n = cache_inode(
+        { INODE_MNT | INODE_DIR },
+        0777,
+        _root_dir_clusters * sectors_per_cluster * SECTOR_SIZE,
+        root_dir);
     register_root_node(n);
 }
 
@@ -127,40 +178,43 @@ fat32::~fat32()
 
 size_t fat32::inode_read(inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
 {
-    cluster_t next = reinterpret_cast<cluster_t>(file->impl);
+    cluster_t next = cl(file);
     uint32_t cluster_size = SECTOR_SIZE * sectors_per_cluster;
-    auto* b = (char*)k_malloc(cluster_size);
     size_t orig_n = n;
 
     do {
         if (offset == 0) {
             if (n > cluster_size) {
-                read_cluster(buf, next);
+                auto* data = read_cluster(next);
+                memcpy(buf, data, cluster_size);
+                release_cluster(next);
+
                 buf_size -= cluster_size;
                 buf += cluster_size;
                 n -= cluster_size;
             } else {
-                read_cluster(b, next);
-                auto read = _write_buf_n(buf, buf_size, b, n);
-                k_free(b);
+                auto* data = read_cluster(next);
+                auto read = _write_buf_n(buf, buf_size, data, n);
+                release_cluster(next);
+
                 return orig_n - n + read;
             }
         } else {
             if (offset > cluster_size) {
                 offset -= cluster_size;
             } else {
-                read_cluster(b, next);
+                auto* data = read_cluster(next);
 
                 auto to_read = cluster_size - offset;
                 if (to_read > n)
                     to_read = n;
 
-                auto read = _write_buf_n(buf, buf_size, b + offset, to_read);
+                auto read = _write_buf_n(buf, buf_size, data + offset, to_read);
                 buf += read;
                 n -= read;
 
+                release_cluster(next);
                 if (read != to_read) {
-                    k_free(b);
                     return orig_n - n;
                 }
 
@@ -170,7 +224,6 @@ size_t fat32::inode_read(inode* file, char* buf, size_t buf_size, size_t offset,
         next = fat[next];
     } while (n && next < EOC);
 
-    k_free(b);
     return orig_n - n;
 }
 
@@ -180,9 +233,6 @@ int fat32::inode_stat(dentry* ent, stat* st)
     st->st_blksize = 4096;
     st->st_blocks = (ent->ind->size + 4095) / 4096;
     st->st_ino = ent->ind->ino;
-    if (ent->ind->flags.in.special_node) {
-        st->st_rdev.v = reinterpret_cast<uint32_t>(ent->ind->impl);
-    }
     return GB_OK;
 }
 } // namespace fs::fat

+ 57 - 3
src/kernel/syscall.cpp

@@ -25,7 +25,8 @@
     SYSCALL_SET_RETURN_VAL_EAX(_eax);      \
     SYSCALL_SET_RETURN_VAL_EDX(_edx)
 
-syscall_handler syscall_handlers[8];
+#define SYSCALL_HANDLERS_SIZE (16)
+syscall_handler syscall_handlers[SYSCALL_HANDLERS_SIZE];
 
 void _syscall_not_impl(interrupt_stack* data)
 {
@@ -81,7 +82,7 @@ void _syscall_write(interrupt_stack* data)
     size_t n = data->s_regs.edx;
 
     auto* file = current_process->files[fd];
-    if (file->type != fs::file::types::regular_file) {
+    if (file->type == fs::file::types::directory) {
         SYSCALL_SET_RETURN_VAL(GB_FAILED, EINVAL);
         return;
     }
@@ -98,7 +99,7 @@ void _syscall_read(interrupt_stack* data)
     size_t n = data->s_regs.edx;
 
     auto* file = current_process->files[fd];
-    if (file->type != fs::file::types::regular_file) {
+    if (file->type == fs::file::types::directory) {
         SYSCALL_SET_RETURN_VAL(GB_FAILED, EINVAL);
         return;
     }
@@ -233,6 +234,57 @@ void _syscall_wait(interrupt_stack* data)
     SYSCALL_SET_RETURN_VAL(pid, 0);
 }
 
+void _syscall_getdents(interrupt_stack* data)
+{
+    int fd = data->s_regs.edi;
+    auto* buf = (char*)(data->s_regs.esi);
+    size_t cnt = data->s_regs.edx;
+
+    auto* dir = current_process->files[fd];
+    if (dir->type != fs::file::types::directory) {
+        data->s_regs.eax = -1;
+        return;
+    }
+
+    size_t orig_cnt = cnt;
+    int nread = dir->ind->fs->inode_readdir(dir->ind, dir->cursor,
+        [&buf, &cnt](const char* fn, size_t len, fs::ino_t ino, uint8_t type) -> int {
+            if (!len)
+                len = strlen(fn);
+
+            size_t reclen = sizeof(fs::user_dirent) + 1 + len;
+            if (cnt < reclen)
+                return GB_FAILED;
+
+            auto* dirp = (fs::user_dirent*)buf;
+            dirp->d_ino = ino;
+            dirp->d_reclen = reclen;
+            // TODO: show offset
+            // dirp->d_off = 0;
+            // TODO: use copy_to_user
+            memcpy(dirp->d_name, fn, len);
+            buf[reclen - 2] = 0;
+            buf[reclen - 1] = type;
+
+            buf += reclen;
+            cnt -= reclen;
+            return GB_OK;
+        });
+
+    if (nread > 0)
+        dir->cursor += nread;
+
+    data->s_regs.eax = orig_cnt - cnt;
+}
+
+void _syscall_open(interrupt_stack* data)
+{
+    auto* path = (const char*)data->s_regs.edi;
+    // flags are ignored for now
+    uint32_t flags = data->s_regs.esi;
+    data->s_regs.eax = current_process->files.open(path, flags);
+}
+
 void init_syscall(void)
 {
     syscall_handlers[0] = _syscall_fork;
@@ -243,4 +295,6 @@ void init_syscall(void)
     syscall_handlers[5] = _syscall_exit;
     syscall_handlers[6] = _syscall_wait;
     syscall_handlers[7] = _syscall_read;
+    syscall_handlers[8] = _syscall_getdents;
+    syscall_handlers[9] = _syscall_open;
 }

+ 153 - 55
src/kernel/vfs.cpp

@@ -6,6 +6,8 @@
 #include <types/allocator.hpp>
 #include <types/assert.h>
 #include <types/list.hpp>
+#include <types/map.hpp>
+#include <types/pair.hpp>
 #include <types/status.h>
 #include <types/stdint.h>
 #include <types/string.hpp>
@@ -62,16 +64,20 @@ fs::vfs::dentry::~dentry()
         types::pdelete<allocator_type>(idx_children);
     }
 }
-fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, const name_type& name)
+fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, const name_type& name, bool set_dirty)
 {
     auto iter = children->emplace_back(this, ind, name);
     idx_children->emplace(iter->name, &iter);
+    if (set_dirty)
+        this->flags.in.dirty = 1;
     return &iter;
 }
-fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type&& name)
+fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type&& name, bool set_dirty)
 {
     auto iter = children->emplace_back(this, ind, types::move(name));
     idx_children->emplace(iter->name, &iter);
+    if (set_dirty)
+        this->flags.in.dirty = 1;
     return &iter;
 }
 fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
@@ -102,38 +108,54 @@ void fs::vfs::dentry::invalidate(void)
     flags.in.present = 0;
 }
 fs::vfs::vfs(void)
-    : _last_inode_no(0)
-    , _root(nullptr, nullptr, "/")
+    : _root(nullptr, nullptr, "/")
 {
 }
-fs::ino_t fs::vfs::_assign_inode_id(void)
+fs::inode* fs::vfs::cache_inode(inode_flags flags, uint32_t perm, size_t size, ino_t ino)
 {
-    return ++_last_inode_no;
-}
-fs::inode* fs::vfs::cache_inode(inode_flags flags, uint32_t perm, size_t size, void* impl_data)
-{
-    auto iter = _inodes.emplace_back(inode { flags, perm, impl_data, _assign_inode_id(), this, size });
-    _idx_inodes.emplace(iter->ino, &iter);
-    return &iter;
+    auto iter = _inodes.insert(types::make_pair(ino, inode { flags, perm, ino, this, size }));
+    return &iter->value;
 }
 fs::inode* fs::vfs::get_inode(ino_t ino)
 {
-    auto iter = _idx_inodes.find(ino);
+    auto iter = _inodes.find(ino);
     // TODO: load inode from disk if not found
-    if (!iter)
-        return nullptr;
+    if (iter)
+        return &iter->value;
     else
-        return iter->value;
+        return nullptr;
 }
 void fs::vfs::register_root_node(inode* root)
 {
     if (!_root.ind)
         _root.ind = root;
 }
-int fs::vfs::load_dentry(dentry*)
+int fs::vfs::load_dentry(dentry* ent)
 {
-    assert(false);
-    return GB_FAILED;
+    auto* ind = ent->ind;
+
+    if (!ind->flags.in.directory) {
+        errno = ENOTDIR;
+        return GB_FAILED;
+    }
+
+    size_t offset = 0;
+
+    for (int ret = 1; ret > 0; offset += ret) {
+        ret = this->inode_readdir(ind, offset,
+            [&, this](const char* name, size_t len, ino_t ino, uint8_t) -> int {
+                if (!len)
+                    ent->append(get_inode(ino), name, false);
+                else
+                    ent->append(get_inode(ino), dentry::name_type(name, len), false);
+
+                return GB_OK;
+            });
+    }
+
+    ent->flags.in.present = 1;
+
+    return GB_OK;
 }
 int fs::vfs::mount(dentry* mnt, vfs* new_fs)
 {
@@ -189,50 +211,111 @@ int fs::vfs::inode_stat(dentry*, stat*)
     assert(false);
     return GB_FAILED;
 }
+uint32_t fs::vfs::inode_getnode(fs::inode*)
+{
+    assert(false);
+    return 0xffffffff;
+}
 
 class tmpfs : public virtual fs::vfs {
+private:
+    using fe_t = tmpfs_file_entry;
+    using vfe_t = vector<fe_t>;
+    using fdata_t = vector<char>;
+
+private:
+    fs::ino_t _next_ino;
+    types::map<fs::ino_t, void*> inode_data;
+
+private:
+    fs::ino_t _assign_ino(void)
+    {
+        return _next_ino++;
+    }
+
+    static constexpr vfe_t* as_vfe(void* data)
+    {
+        return static_cast<vfe_t*>(data);
+    }
+    static constexpr fdata_t* as_fdata(void* data)
+    {
+        return static_cast<fdata_t*>(data);
+    }
+    static inline ptr_t as_val(void* data)
+    {
+        return reinterpret_cast<ptr_t>(data);
+    }
+    inline void* _getdata(fs::ino_t ino) const
+    {
+        return inode_data.find(ino)->value;
+    }
+    inline fs::ino_t _savedata(void* data)
+    {
+        fs::ino_t ino = _assign_ino();
+        inode_data.insert(types::make_pair(ino, data));
+        return ino;
+    }
+    inline fs::ino_t _savedata(ptr_t data)
+    {
+        return _savedata((void*)data);
+    }
+
 protected:
-    inline vector<tmpfs_file_entry>* mk_fe_vector(void)
+    inline vfe_t* mk_fe_vector(void)
     {
-        return allocator_traits<kernel_allocator<vector<tmpfs_file_entry>>>::allocate_and_construct();
+        return allocator_traits<kernel_allocator<vfe_t>>::allocate_and_construct();
     }
 
-    inline vector<char>* mk_data_vector(void)
+    inline fdata_t* mk_data_vector(void)
     {
-        return allocator_traits<kernel_allocator<vector<char>>>::allocate_and_construct();
+        return allocator_traits<kernel_allocator<fdata_t>>::allocate_and_construct();
     }
 
     void mklink(fs::inode* dir, fs::inode* inode, const char* filename)
     {
-        auto* fes = static_cast<vector<struct tmpfs_file_entry>*>(dir->impl);
-        struct tmpfs_file_entry ent = {
+        auto* fes = as_vfe(_getdata(dir->ino));
+        auto iter = fes->emplace_back(fe_t {
             .ino = inode->ino,
-            .filename = { 0 },
-        };
-        snprintf(ent.filename, sizeof(ent.filename), filename);
-        fes->push_back(ent);
-        dir->size += sizeof(tmpfs_file_entry);
+            .filename = {} });
+        strncpy(iter->filename, filename, sizeof(iter->filename));
+        iter->filename[sizeof(iter->filename) - 1] = 0;
+        dir->size += sizeof(fe_t);
     }
 
-    virtual int load_dentry(dentry* ent) override
+    virtual int inode_readdir(fs::inode* dir, size_t offset, fs::vfs::filldir_func filldir) override
     {
-        if (!ent->ind->flags.in.directory) {
-            errno = ENOTDIR;
-            return GB_FAILED;
+        if (!dir->flags.in.directory) {
+            return -1;
         }
 
-        auto& entries = *static_cast<vector<tmpfs_file_entry>*>(ent->ind->impl);
-        for (const auto& entry : entries)
-            ent->append(get_inode(entry.ino), entry.filename);
+        auto& entries = *as_vfe(_getdata(dir->ino));
+        size_t off = offset / sizeof(fe_t);
 
-        ent->flags.in.present = 1;
-        return GB_OK;
+        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 type = DT_REG;
+            if (ind->flags.in.directory)
+                type = DT_DIR;
+            if (ind->flags.in.special_node)
+                type = DT_BLK;
+
+            auto ret = filldir(entry.filename, 0, entry.ino, type);
+            if (ret != GB_OK)
+                break;
+        }
+
+        return nread;
     }
 
 public:
     explicit tmpfs(void)
+        : _next_ino(1)
     {
-        auto& in = *cache_inode({ INODE_DIR | INODE_MNT }, 0777, 0, mk_fe_vector());
+        auto& in = *cache_inode({ INODE_DIR | INODE_MNT }, 0777, 0, _savedata(mk_fe_vector()));
 
         mklink(&in, &in, ".");
         mklink(&in, &in, "..");
@@ -242,29 +325,29 @@ public:
 
     virtual int inode_mkfile(dentry* dir, const char* filename) override
     {
-        auto& file = *cache_inode({ .v = INODE_FILE }, 0777, 0, mk_data_vector());
+        auto& file = *cache_inode({ .v = INODE_FILE }, 0777, 0, _savedata(mk_data_vector()));
         mklink(dir->ind, &file, filename);
-        dir->invalidate();
+        dir->append(get_inode(file.ino), filename, true);
         return GB_OK;
     }
 
     virtual int inode_mknode(dentry* dir, const char* filename, fs::node_t sn) override
     {
-        auto& node = *cache_inode({ .v = INODE_NODE }, 0777, 0, (void*)sn.v);
+        auto& node = *cache_inode({ .v = INODE_NODE }, 0777, 0, _savedata(sn.v));
         mklink(dir->ind, &node, filename);
-        dir->invalidate();
+        dir->append(get_inode(node.ino), filename, true);
         return GB_OK;
     }
 
     virtual int inode_mkdir(dentry* dir, const char* dirname) override
     {
-        auto& new_dir = *cache_inode({ .v = INODE_DIR }, 0777, 0, mk_fe_vector());
-        mklink(&new_dir, &new_dir, ".");
+        auto new_dir = cache_inode({ .v = INODE_DIR }, 0777, 0, _savedata(mk_fe_vector()));
+        mklink(new_dir, new_dir, ".");
 
-        mklink(dir->ind, &new_dir, dirname);
-        mklink(&new_dir, dir->ind, "..");
+        mklink(dir->ind, new_dir, dirname);
+        mklink(new_dir, dir->ind, "..");
 
-        dir->invalidate();
+        dir->append(new_dir, dirname, true);
         return GB_OK;
     }
 
@@ -273,7 +356,7 @@ public:
         if (file->flags.in.file != 1)
             return 0;
 
-        auto* data = static_cast<vector<char>*>(file->impl);
+        auto* data = as_fdata(_getdata(file->ino));
         size_t fsize = data->size();
 
         if (offset + n > fsize)
@@ -293,7 +376,7 @@ public:
         if (file->flags.in.file != 1)
             return 0;
 
-        auto* data = static_cast<vector<char>*>(file->impl);
+        auto* data = as_fdata(_getdata(file->ino));
 
         for (size_t i = data->size(); i < offset + n; ++i) {
             data->push_back(0);
@@ -316,17 +399,22 @@ public:
         }
         if (file_inode->flags.in.directory) {
             stat->st_rdev.v = 0;
-            stat->st_blksize = sizeof(tmpfs_file_entry);
+            stat->st_blksize = sizeof(fe_t);
             stat->st_blocks = file_inode->size;
         }
         if (file_inode->flags.in.special_node) {
-            stat->st_rdev.v = (uint32_t)file_inode->impl;
+            stat->st_rdev.v = as_val(_getdata(file_inode->ino));
             stat->st_blksize = 0;
             stat->st_blocks = 0;
         }
 
         return GB_OK;
     }
+
+    virtual uint32_t inode_getnode(fs::inode* file) override
+    {
+        return as_val(_getdata(file->ino));
+    }
 };
 
 // 8 * 8 for now
@@ -335,8 +423,13 @@ static fs::special_node sns[8][8];
 size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset, size_t n)
 {
     if (file->flags.in.special_node) {
+        uint32_t ret = file->fs->inode_getnode(file);
+        if (ret == SN_INVALID) {
+            errno = EINVAL;
+            return 0xffffffff;
+        }
         fs::node_t sn {
-            .v = (uint32_t)file->impl
+            .v = ret
         };
         auto* ptr = &sns[sn.in.major][sn.in.minor];
         auto* ops = &ptr->ops;
@@ -353,8 +446,13 @@ size_t fs::vfs_read(fs::inode* file, char* buf, size_t buf_size, size_t offset,
 size_t fs::vfs_write(fs::inode* file, const char* buf, size_t offset, size_t n)
 {
     if (file->flags.in.special_node) {
+        uint32_t ret = file->fs->inode_getnode(file);
+        if (ret == SN_INVALID) {
+            errno = EINVAL;
+            return 0xffffffff;
+        }
         fs::node_t sn {
-            .v = (uint32_t)file->impl
+            .v = ret
         };
         auto* ptr = &sns[sn.in.major][sn.in.minor];
         auto* ops = &ptr->ops;

+ 35 - 0
user-space-program/basic-lib.h

@@ -4,10 +4,27 @@ typedef __UINT8_TYPE__ uint8_t;
 
 typedef uint32_t pid_t;
 typedef uint32_t size_t;
+typedef size_t ino_t;
 
 #define GNU_ATTRIBUTE(attr) __attribute__((attr))
 #define NORETURN GNU_ATTRIBUTE(noreturn)
 
+#define O_RDONLY (0)
+#define O_DIRECTORY (0x4)
+
+// dirent file types
+#define DT_UNKNOWN 0
+#define DT_FIFO 1
+#define DT_CHR 2
+#define DT_DIR 4
+#define DT_BLK 6
+#define DT_REG 8
+#define DT_LNK 10
+#define DT_SOCK 12
+#define DT_WHT 14
+
+#define DT_MAX (S_DT_MASK + 1) /* 16 */
+
 static inline uint32_t syscall(uint32_t num, uint32_t arg1, uint32_t arg2, uint32_t arg3)
 {
     asm volatile(
@@ -67,3 +84,21 @@ static inline uint32_t wait(int* return_value)
 {
     return syscall(0x06, (uint32_t)return_value, 0, 0);
 }
+
+struct __attribute__((__packed__)) user_dirent {
+    ino_t d_ino; // inode number
+    uint32_t d_off; // ignored
+    uint16_t d_reclen; // length of this struct user_dirent
+    char d_name[1]; // file name with a padding zero
+    // uint8_t d_type; // file type, with offset of (d_reclen - 1)
+};
+
+static inline size_t getdents(int fd, struct user_dirent* buf, size_t cnt)
+{
+    return syscall(0x08, fd, (uint32_t)buf, cnt);
+}
+
+static inline int open(const char* path, uint32_t flags)
+{
+    return syscall(0x09, (uint32_t)path, flags, 0);
+}

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

@@ -1,30 +1,102 @@
 #include "basic-lib.h"
 
+static inline char tc(int n) {
+    return '0' + n;
+}
+
+void print_int(int n) {
+    char buf[12];
+    int len = 0;
+
+    while (n) {
+        int dig = n % 10;
+        buf[len++] = tc(dig);
+        n /= 10;
+    }
+
+    write(0, buf, len);
+}
+
+size_t strlen(const char* str)
+{
+    const char* p = str;
+    while (*p)
+        ++p;
+    return p - str;
+}
+
+static inline void print(const char* str)
+{
+    write(0, str, strlen(str));
+}
+
 int main(int argc, char** argv)
 {
     for (int i = 0; i < argc; ++i)
         write(0, argv[i], 0);
 
-    const char* data = "Hello World from user space init\n";
-    write(0, data, 33);
+    print("Hello World from user space init\n");
     int ret = fork();
     if (ret == 0) {
-        write(0, "child\n", 6);
+        print("child\n");
         exit(255);
     } else {
-        write(0, "parent\n", 7);
+        print("parent\n");
     }
 
     char buf[128] = {};
+
+    const char* path = "/dev";
+    print("content in ");
+    print(path);
+    print(":\n");
+
+    int dir = open(path, O_RDONLY | O_DIRECTORY);
+    if (dir >= 0) {
+        for (;;) {
+            int n = getdents(dir, (struct user_dirent*)buf, 128);
+            if (n < 0)
+                print("error\n");
+            if (n <= 0)
+                break;
+
+            int bpos = 0;
+            for (; bpos < n;) {
+                struct user_dirent* dirp = (struct user_dirent*)(buf + bpos);
+                print("ino: ");
+                print_int(dirp->d_ino);
+                print(", filename: \"");
+                print(dirp->d_name);
+                print("\", filetype: ");
+                switch (buf[bpos + dirp->d_reclen - 1]) {
+                    case DT_REG:
+                        print("regular file");
+                        break;
+                    case DT_DIR:
+                        print("directory");
+                        break;
+                    case DT_BLK:
+                        print("block device");
+                        break;
+                    default:
+                        print("unknown");
+                        break;
+                }
+                print("\n");
+                bpos += dirp->d_reclen;
+            }
+        }
+    }
+
     for (;;) {
-        int n = read(0, buf, 5);
+        int n = read(0, buf, 128);
         if (n)
             write(0, buf, n);
         else
-            write(0, "fuck!\n", 6);
+            print("fuck!\n");
         
         if (buf[0] == 'e' && buf[1] == 'x' && buf[2] == 'i' && buf[3] == 't') {
-            write(0, "\nexited echo mode!\n", 19);
+            print("\nexited echo mode!\n");
             break;
         }
     }
@@ -32,8 +104,11 @@ int main(int argc, char** argv)
     for (;;) {
         int ret;
         pid_t pid = wait(&ret);
-        (void)pid;
-        pid += 1000;
+        print("ret: ");
+        print_int(ret);
+        print(", pid: ");
+        print_int(pid);
+        print("\n");
     }
     return 0;
 }