Forráskód Böngészése

feature(syscall): unlink and access

greatbridf 11 hónapja
szülő
commit
e75d8437f5
6 módosított fájl, 192 hozzáadás és 48 törlés
  1. 5 0
      gblibc/include/unistd.h
  2. 2 2
      include/fs/fat.hpp
  3. 15 12
      include/kernel/vfs.hpp
  4. 14 4
      src/fs/fat.cpp
  5. 43 0
      src/kernel/syscall.cpp
  6. 113 30
      src/kernel/vfs.cpp

+ 5 - 0
gblibc/include/unistd.h

@@ -10,6 +10,11 @@
 #define STDOUT_FILENO (1)
 #define STDERR_FILENO (2)
 
+#define F_OK 0
+#define R_OK 1
+#define W_OK 2
+#define X_OK 4
+
 #ifdef __cplusplus
 extern "C" {
 #endif

+ 2 - 2
include/fs/fat.hpp

@@ -4,6 +4,7 @@
 #include <kernel/vfs.hpp>
 #include <stdint.h>
 #include <string.h>
+#include <sys/types.h>
 #include <types/size.h>
 
 namespace fs::fat {
@@ -115,8 +116,7 @@ private:
     uint32_t next_free_cluster_hint;
     cluster_t root_dir;
     cluster_t data_region_offset;
-    // TODO: use block device special node id
-    inode* device;
+    dev_t device;
     uint16_t reserved_sectors;
     uint8_t fat_copies;
     uint8_t sectors_per_cluster;

+ 15 - 12
include/kernel/vfs.hpp

@@ -52,6 +52,8 @@ struct inode {
     vfs* fs;
     size_t size;
 
+    nlink_t nlink;
+
     mode_t mode;
     uid_t uid;
     gid_t gid;
@@ -114,16 +116,14 @@ public:
         std::list<dentry>* children = nullptr;
         types::hash_map<name_type, dentry*>* idx_children = nullptr;
 
-    public:
+public:
         dentry* parent;
         inode* ind;
-        // if the entry is a file, this flag is ignored
-        union {
-            uint32_t v;
-            struct {
-                uint32_t present : 1;
-                uint32_t dirty : 1;
-            } in;
+        struct {
+            uint32_t dir : 1; // whether the dentry is a directory.
+            // if dir is 1, whether children contains valid data.
+            // otherwise, ignored
+            uint32_t present : 1;
         } flags;
         name_type name;
 
@@ -158,13 +158,14 @@ public:
             }
         }
 
-        dentry* append(inode* ind, const name_type& name, bool set_dirty);
-        dentry* append(inode* ind, name_type&& name, bool set_dirty);
+        dentry* append(inode* ind, name_type name);
 
         dentry* find(const name_type& name);
 
         dentry* replace(dentry* val);
 
+        void remove(const name_type& name);
+
         // out_dst SHOULD be empty
         void path(const dentry& root, types::path& out_dst) const;
 
@@ -187,13 +188,15 @@ protected:
 
 protected:
     inode* cache_inode(size_t size, ino_t ino, mode_t mode, uid_t uid, gid_t gid);
+    void free_inode(ino_t ino);
     inode* get_inode(ino_t ino);
     void register_root_node(inode* root);
 
     int load_dentry(dentry* ent);
 
 public:
-    explicit vfs(void);
+    vfs();
+
     vfs(const vfs&) = delete;
     vfs& operator=(const vfs&) = delete;
     vfs(vfs&&) = delete;
@@ -354,7 +357,7 @@ int vfs_truncate(inode* file, size_t size);
 
 /**
  * @brief Opens a file or directory specified by the given path.
- * 
+ *
  * @param root The root directory of the file system.
  * @param path The absolute path to the file or directory to be opened.
  * @return A pointer to the opened file or directory entry if found.

+ 14 - 4
src/fs/fat.cpp

@@ -17,7 +17,7 @@ namespace fs::fat {
 // buf MUST be larger than 512 bytes
 inline void fat32::_raw_read_sector(void* buf, uint32_t sector_no)
 {
-    size_t n = vfs_read(
+    size_t n = fs::block_device_read(
         device,
         (char*)buf,
         SECTOR_SIZE,
@@ -92,6 +92,8 @@ int fat32::inode_readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_f
                 else
                     mode |= S_IFREG;
                 ind = cache_inode(d->size, ino, mode, 0, 0);
+
+                ind->nlink = d->attributes.subdir ? 2 : 1;
             }
 
             types::string<> fname;
@@ -129,7 +131,7 @@ int fat32::inode_readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_f
 }
 
 fat32::fat32(inode* _device)
-    : device(_device)
+    : device(_device->fs->inode_devid(_device))
     , label { 0 }
 {
     auto* buf = new char[SECTOR_SIZE];
@@ -172,6 +174,9 @@ fat32::fat32(inode* _device)
     auto* n = cache_inode(
         _root_dir_clusters * sectors_per_cluster * SECTOR_SIZE,
         root_dir, S_IFDIR | 0777, 0, 0);
+
+    n->nlink = 2;
+
     register_root_node(n);
 }
 
@@ -238,13 +243,18 @@ int fat32::inode_statx(dentry* ent, statx* st, unsigned int mask)
         st->stx_size = ent->ind->size;
         st->stx_mask |= STATX_SIZE;
     }
-    
+
     if (mask & STATX_BLOCKS) {
         st->stx_blocks = align_up<12>(ent->ind->size) / 512;
         st->stx_blksize = 4096;
         st->stx_mask |= STATX_BLOCKS;
     }
 
+    if (mask & STATX_NLINK) {
+        st->stx_nlink = ent->ind->nlink;
+        st->stx_mask |= STATX_NLINK;
+    }
+
     st->stx_mode = 0;
     if (mask & STATX_MODE) {
         st->stx_mode |= ent->ind->mode & ~S_IFMT;
@@ -280,7 +290,7 @@ int fat32::inode_stat(dentry* dent, struct stat* st)
 
     memset(st, 0x00, sizeof(struct stat));
     st->st_mode = ind->mode;
-    st->st_dev = device->fs->inode_devid(device);
+    st->st_dev = device;
     st->st_nlink = S_ISDIR(ind->mode) ? 2 : 1;
     st->st_size = ind->size;
     st->st_blksize = 4096;

+ 43 - 0
src/kernel/syscall.cpp

@@ -7,6 +7,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
+#include <unistd.h>
 #include <bits/alltypes.h>
 #include <bits/ioctl.h>
 #include <sys/types.h>
@@ -955,6 +956,46 @@ int _syscall_truncate(interrupt_stack* data)
     return 0;
 }
 
+int _syscall_unlink(interrupt_stack* data)
+{
+    SYSCALL_ARG1(const char* __user, pathname);
+
+    auto path = types::make_path(pathname, current_process->pwd);
+    auto* dent = fs::vfs_open(*current_process->root, path);
+
+    if (!dent)
+        return -ENOENT;
+
+    if (S_ISDIR(dent->ind->mode))
+        return -EISDIR;
+
+    return fs::vfs_rmfile(dent->parent, dent->name.c_str());
+}
+
+int _syscall_access(interrupt_stack* data)
+{
+    SYSCALL_ARG1(const char* __user, pathname);
+    SYSCALL_ARG2(int, mode);
+
+    auto path = types::make_path(pathname, current_process->pwd);
+    auto* dent = fs::vfs_open(*current_process->root, path);
+
+    if (!dent)
+        return -ENOENT;
+
+    switch (mode) {
+    case F_OK:
+        return 0;
+    case R_OK:
+    case W_OK:
+    case X_OK:
+        // TODO: check privilege
+        return 0;
+    default:
+        return -EINVAL;
+    }
+}
+
 extern "C" void syscall_entry(
     interrupt_stack* data,
     mmx_registers* mmxregs)
@@ -991,9 +1032,11 @@ void init_syscall(void)
     syscall_handlers[0x05] = _syscall_open;
     syscall_handlers[0x06] = _syscall_close;
     syscall_handlers[0x07] = _syscall_waitpid;
+    syscall_handlers[0x0a] = _syscall_unlink;
     syscall_handlers[0x0b] = _syscall_execve;
     syscall_handlers[0x0c] = _syscall_chdir;
     syscall_handlers[0x14] = _syscall_getpid;
+    syscall_handlers[0x21] = _syscall_access;
     syscall_handlers[0x25] = _syscall_kill;
     syscall_handlers[0x27] = _syscall_mkdir;
     syscall_handlers[0x29] = _syscall_dup;

+ 113 - 30
src/kernel/vfs.cpp

@@ -29,31 +29,24 @@ struct tmpfs_file_entry {
 fs::vfs::dentry::dentry(dentry* _parent, inode* _ind, name_type _name)
     : parent(_parent) , ind(_ind) , flags { } , name(_name)
 {
-    if (!_ind || S_ISDIR(ind->mode)) {
+    // the dentry is filesystem root or _ind MUST be non null
+    assert(_ind || !_parent);
+    if (!ind || S_ISDIR(ind->mode)) {
+        flags.dir = 1;
         children = new std::list<dentry>;
         idx_children = new types::hash_map<name_type, dentry*>;
     }
 }
 
-fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, const name_type& name, bool set_dirty)
+fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type name)
 {
     auto& ent = children->emplace_back(this, ind, name);
     idx_children->emplace(ent.name, &ent);
-    if (set_dirty)
-        this->flags.in.dirty = 1;
-    return &ent;
-}
-fs::vfs::dentry* fs::vfs::dentry::append(inode* ind, name_type&& name, bool set_dirty)
-{
-    auto& ent = children->emplace_back(this, ind, std::move(name));
-    idx_children->emplace(ent.name, &ent);
-    if (set_dirty)
-        this->flags.in.dirty = 1;
     return &ent;
 }
 fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
 {
-    if (!S_ISDIR(ind->mode))
+    if (!flags.dir)
         return nullptr;
 
     if (name[0] == '.') {
@@ -63,7 +56,7 @@ fs::vfs::dentry* fs::vfs::dentry::find(const name_type& name)
             return parent ? parent : this;
     }
 
-    if (!flags.in.present)
+    if (!flags.present)
         ind->fs->load_dentry(this);
 
     auto iter = idx_children->find(name);
@@ -82,14 +75,25 @@ fs::vfs::dentry* fs::vfs::dentry::replace(dentry* val)
 }
 void fs::vfs::dentry::invalidate(void)
 {
-    // TODO: write back
-    flags.in.dirty = 0;
     children->clear();
     idx_children->clear();
-    flags.in.present = 0;
+    flags.present = 0;
 }
-fs::vfs::vfs(void)
-    : _root(nullptr, nullptr, "")
+
+void fs::vfs::dentry::remove(const name_type& name)
+{
+    for (auto iter = children->begin(); iter != children->end(); ++iter) {
+        if (iter->name != name)
+            continue;
+        children->erase(iter);
+        break;
+    }
+
+    idx_children->remove(name);
+}
+
+fs::vfs::vfs()
+    : _root { nullptr, nullptr, "" }
 {
 }
 
@@ -115,9 +119,15 @@ fs::inode* fs::vfs::cache_inode(size_t size, ino_t ino,
     mode_t mode, uid_t uid, gid_t gid)
 {
     auto [ iter, inserted ] =
-        _inodes.try_emplace(ino, inode { ino, this, size, mode, uid, gid });
+        _inodes.try_emplace(ino, inode { ino, this, size, 0, mode, uid, gid });
     return &iter->second;
 }
+
+void fs::vfs::free_inode(ino_t ino)
+{
+    assert(_inodes.erase(ino) == 1);
+}
+
 fs::inode* fs::vfs::get_inode(ino_t ino)
 {
     auto iter = _inodes.find(ino);
@@ -136,7 +146,7 @@ int fs::vfs::load_dentry(dentry* ent)
 {
     auto* ind = ent->ind;
 
-    if (!S_ISDIR(ind->mode)) {
+    if (!ent->flags.dir || !S_ISDIR(ind->mode)) {
         errno = ENOTDIR;
         return GB_FAILED;
     }
@@ -147,21 +157,21 @@ int fs::vfs::load_dentry(dentry* ent)
         ret = this->inode_readdir(ind, offset,
             [ent, this](const char* name, size_t len, ino_t ino, uint8_t) -> int {
                 if (!len)
-                    ent->append(get_inode(ino), name, false);
+                    ent->append(get_inode(ino), name);
                 else
-                    ent->append(get_inode(ino), dentry::name_type(name, len), false);
+                    ent->append(get_inode(ino), dentry::name_type(name, len));
 
                 return GB_OK;
             });
     }
 
-    ent->flags.in.present = 1;
+    ent->flags.present = 1;
 
     return GB_OK;
 }
 int fs::vfs::mount(dentry* mnt, vfs* new_fs)
 {
-    if (!S_ISDIR(mnt->ind->mode)) {
+    if (!mnt->flags.dir) {
         errno = ENOTDIR;
         return GB_FAILED;
     }
@@ -250,10 +260,14 @@ protected:
         fes->emplace_back(fe_t {
             .ino = inode->ino,
             .filename = {} });
+        dir->size += sizeof(fe_t);
+
         auto& emplaced = fes->back();
+
         strncpy(emplaced.filename, filename, sizeof(emplaced.filename));
         emplaced.filename[sizeof(emplaced.filename) - 1] = 0;
-        dir->size += sizeof(fe_t);
+
+        ++inode->nlink;
     }
 
     virtual int inode_readdir(fs::inode* dir, size_t offset, const fs::vfs::filldir_func& filldir) override
@@ -294,32 +308,49 @@ public:
 
     virtual int inode_mkfile(dentry* dir, const char* filename, mode_t mode) override
     {
+        if (!dir->flags.dir)
+            return -ENOTDIR;
+
         auto& file = *cache_inode(0, _savedata(mk_data_vector()), S_IFREG | mode, 0, 0);
         mklink(dir->ind, &file, filename);
-        dir->append(get_inode(file.ino), filename, true);
+
+        if (dir->flags.present)
+            dir->append(get_inode(file.ino), filename);
+
         return GB_OK;
     }
 
     virtual int inode_mknode(dentry* dir, const char* filename, mode_t mode, dev_t dev) override
     {
+        if (!dir->flags.dir)
+            return -ENOTDIR;
+
         if (!S_ISBLK(mode) && !S_ISCHR(mode))
             return -EINVAL;
 
         auto& node = *cache_inode(0, _savedata(dev), mode, 0, 0);
         mklink(dir->ind, &node, filename);
-        dir->append(get_inode(node.ino), filename, true);
+
+        if (dir->flags.present)
+            dir->append(get_inode(node.ino), filename);
+
         return GB_OK;
     }
 
     virtual int inode_mkdir(dentry* dir, const char* dirname, mode_t mode) override
     {
+        if (!dir->flags.dir)
+            return -ENOTDIR;
+
         auto new_dir = cache_inode(0, _savedata(mk_fe_vector()), S_IFDIR | (mode & 0777), 0, 0);
         mklink(new_dir, new_dir, ".");
 
         mklink(dir->ind, new_dir, dirname);
         mklink(new_dir, dir->ind, "..");
 
-        dir->append(new_dir, dirname, true);
+        if (dir->flags.present)
+            dir->append(new_dir, dirname);
+
         return GB_OK;
     }
 
@@ -364,6 +395,19 @@ public:
         auto* ind = dent->ind;
         const mode_t mode = ind->mode;
 
+        st->stx_mask = 0;
+
+        if (mask & STATX_NLINK) {
+            st->stx_nlink = ind->nlink;
+            st->stx_mask |= STATX_NLINK;
+        }
+
+        // TODO: set modification time
+        if (mask & STATX_MTIME) {
+            st->stx_mtime = {};
+            st->stx_mask |= STATX_MTIME;
+        }
+
         if (mask & STATX_SIZE) {
             st->stx_size = ind->size;
             st->stx_mask |= STATX_SIZE;
@@ -389,7 +433,7 @@ public:
             st->stx_ino = ind->ino;
             st->stx_mask |= STATX_INO;
         }
-        
+
         if (mask & STATX_BLOCKS) {
             st->stx_blocks = align_up<9>(ind->size) / 512;
             st->stx_blksize = 4096;
@@ -409,6 +453,45 @@ public:
         return GB_OK;
     }
 
+    virtual int inode_rmfile(dentry* dir, const char* filename) override
+    {
+        if (!dir->flags.dir)
+            return -ENOTDIR;
+
+        auto* vfe = as_vfe(_getdata(dir->ind->ino));
+        assert(vfe);
+
+        auto* dent = dir->find(filename);
+        if (!dent)
+            return -ENOENT;
+
+        for (auto iter = vfe->begin(); iter != vfe->end(); ) {
+            if (iter->ino != dent->ind->ino) {
+                ++iter;
+                continue;
+            }
+
+            if (S_ISREG(dent->ind->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 = as_fdata(_getdata(iter->ino));
+                assert(filedata);
+
+                delete filedata;
+            }
+
+            free_inode(iter->ino);
+            dir->remove(filename);
+
+            vfe->erase(iter);
+
+            return 0;
+        }
+
+        kmsg("[tmpfs] warning: file entry not found in vfe\n");
+        return -EIO;
+    }
+
     virtual dev_t inode_devid(fs::inode* file) override
     {
         return as_val(_getdata(file->ino));