Browse Source

refactor(vfs): rewrite struct file

greatbridf 1 year ago
parent
commit
4a42fe3956
5 changed files with 235 additions and 242 deletions
  1. 28 72
      include/kernel/process.hpp
  2. 42 11
      include/kernel/vfs.hpp
  3. 6 13
      src/kernel/process.cpp
  4. 19 146
      src/kernel/syscall.cpp
  5. 140 0
      src/kernel/vfs.cpp

+ 28 - 72
include/kernel/process.hpp

@@ -2,6 +2,7 @@
 
 #include <map>
 #include <list>
+#include <memory>
 #include <queue>
 #include <set>
 #include <tuple>
@@ -104,42 +105,14 @@ public:
 
 class filearr {
 public:
-    using container_type = std::list<fs::file>;
-    using array_type = std::map<int, container_type::iterator>;
+    using array_type = std::map<int, std::shared_ptr<fs::file>>;
 
 private:
-    inline static container_type* files;
     array_type arr;
     std::priority_queue<int, std::vector<int>, std::greater<int>> _fds;
     int _greatest_fd;
 
-public:
-    inline static void init_global_file_container(void)
-    {
-        files = new container_type;
-    }
-
 private:
-    // iter should not be nullptr
-    constexpr void _close(container_type::iterator iter)
-    {
-        if (iter->ref == 1) {
-            if (iter->type == fs::file::types::pipe) {
-                assert(iter->flags.read | iter->flags.write);
-                if (iter->flags.read)
-                    iter->ptr.pp->close_read();
-                else
-                    iter->ptr.pp->close_write();
-
-                if (iter->ptr.pp->is_free())
-                    delete iter->ptr.pp;
-            }
-
-            files->erase(iter);
-        } else
-            --iter->ref;
-    }
-
     constexpr int next_fd()
     {
         if (_fds.empty())
@@ -172,10 +145,7 @@ public:
         if (!iter)
             return -EBADF;
 
-        auto [ _, iter_file ] = *iter;
-
-        this->arr.emplace(new_fd, iter_file);
-        ++iter_file->ref;
+        this->arr.emplace(new_fd, iter->second);
         return new_fd;
     }
 
@@ -183,10 +153,8 @@ public:
     {
         this->_fds = orig._fds;
         this->_greatest_fd = orig._greatest_fd;
-        for (auto [ fd, iter_file ] : orig.arr) {
-            this->arr.emplace(fd, iter_file);
-            ++iter_file->ref;
-        }
+        for (auto [ fd, fp ] : orig.arr)
+            this->arr.emplace(fd, fp);
     }
 
     constexpr fs::file* operator[](int i) const
@@ -194,49 +162,37 @@ public:
         auto iter = arr.find(i);
         if (!iter)
             return nullptr;
-        return &iter->second;
+        return iter->second.get();
     }
 
     int pipe(int pipefd[2])
     {
-        // TODO: set read/write flags
-        auto* pipe = new fs::pipe;
-
-        auto iter = files->emplace(files->cend(), fs::file {
-            fs::file::types::pipe,
-            { .pp = pipe },
-            nullptr,
-            0,
-            1,
-            {
-                .read = 1,
-                .write = 0,
-                .close_on_exec = 0,
-            },
-        });
+        std::shared_ptr<fs::pipe> ppipe { new fs::pipe };
 
         bool inserted = false;
         int fd = next_fd();
-        std::tie(std::ignore, inserted) = arr.emplace(fd, iter);
+        std::tie(std::ignore, inserted) = arr.emplace(fd,
+            std::shared_ptr<fs::file> {
+                new fs::fifo_file(nullptr, {
+                    .read = 1,
+                    .write = 0,
+                    .close_on_exec = 0,
+                }, ppipe),
+        });
         assert(inserted);
 
         // TODO: use copy_to_user()
         pipefd[0] = fd;
 
-        iter = files->emplace(files->cend(), fs::file {
-            fs::file::types::pipe,
-            { .pp = pipe },
-            nullptr,
-            0,
-            1,
-            {
-                .read = 0,
-                .write = 1,
-                .close_on_exec = 0,
-            },
-        });
         fd = next_fd();
-        std::tie(std::ignore, inserted) = arr.emplace(fd, iter);
+        std::tie(std::ignore, inserted) = arr.emplace(fd,
+            std::shared_ptr<fs::file> {
+                new fs::fifo_file(nullptr, {
+                    .read = 0,
+                    .write = 1,
+                    .close_on_exec = 0,
+                }, ppipe),
+        });
         assert(inserted);
 
         // TODO: use copy_to_user()
@@ -253,23 +209,23 @@ public:
         if (!iter)
             return;
 
-        _close(iter->second);
+        iter->second->close();
         _fds.push(fd);
         arr.erase(iter);
     }
 
     constexpr void onexec()
     {
-        for (auto [ fd, file ] : arr) {
-            if (file->flags.close_on_exec)
+        for (auto&& [ fd, fp ] : arr) {
+            if (fp->flags.close_on_exec)
                 close(fd);
         }
     }
 
     constexpr void close_all(void)
     {
-        for (auto&& [ fd, file ] : arr) {
-            _close(file);
+        for (auto&& [ fd, fp ] : arr) {
+            fp->close();
             _fds.push(fd);
         }
         arr.clear();

+ 42 - 11
include/kernel/vfs.hpp

@@ -6,6 +6,7 @@
 #include <functional>
 
 #include <sys/stat.h>
+#include <kernel/errno.h>
 #include <bits/alltypes.h>
 
 #include <assert.h>
@@ -257,23 +258,53 @@ public:
 };
 
 struct file {
-    enum class types {
-        ind,
-        pipe,
-        socket,
-    } type {};
-    union {
-        inode* ind;
-        pipe* pp;
-    } ptr {};
+    mode_t mode; // stores the file type in the same format as inode::mode
     vfs::dentry* parent {};
-    size_t cursor {};
-    size_t ref {};
     struct file_flags {
         uint32_t read : 1;
         uint32_t write : 1;
         uint32_t close_on_exec : 1;
     } flags {};
+
+    file(mode_t mode, vfs::dentry* parent, file_flags flags)
+        : mode(mode) , parent(parent), flags(flags) { }
+
+    virtual ~file() = default;
+
+    virtual ssize_t read(char* __user buf, size_t n) = 0;
+    virtual ssize_t write(const char* __user buf, size_t n) = 0;
+    virtual void close() = 0;
+
+    // regular files should override this method
+    virtual int getdents(char* __user buf, size_t cnt)
+    { return (void)buf, (void)cnt, -ENOTDIR; }
+    virtual int getdents64(char* __user buf, size_t cnt)
+    { return (void)buf, (void)cnt, -ENOTDIR; }
+};
+
+struct regular_file : public virtual file {
+    virtual ~regular_file() = default;
+    std::size_t cursor { };
+    inode* ind { };
+
+    regular_file(vfs::dentry* parent, file_flags flags, size_t cursor, inode* ind);
+
+    virtual ssize_t read(char* __user buf, size_t n) override;
+    virtual ssize_t write(const char* __user buf, size_t n) override;
+    virtual void close() override;
+    virtual int getdents(char* __user buf, size_t cnt) override;
+    virtual int getdents64(char* __user buf, size_t cnt) override;
+};
+
+struct fifo_file : public virtual file {
+    virtual ~fifo_file() = default;
+    std::shared_ptr<pipe> ppipe;
+
+    fifo_file(vfs::dentry* parent, file_flags flags, std::shared_ptr<fs::pipe> ppipe);
+
+    virtual ssize_t read(char* __user buf, size_t n) override;
+    virtual ssize_t write(const char* __user buf, size_t n) override;
+    virtual void close() override;
 };
 
 inline fs::vfs::dentry* fs_root;

+ 6 - 13
src/kernel/process.cpp

@@ -1,3 +1,4 @@
+#include <memory>
 #include <utility>
 
 #include <bits/alltypes.h>
@@ -137,21 +138,15 @@ int filearr::open(const process &current,
             return -EISDIR;
     }
 
-    auto iter = files->emplace(files->cend(), fs::file {
-        fs::file::types::ind,
-        { .ind = dentry->ind },
-        dentry->parent,
-        0,
-        1,
-        {
+    int fd = next_fd();
+    auto [ _, inserted ] = arr.emplace(fd, std::shared_ptr<fs::file> {
+        new fs::regular_file(dentry->parent, {
             .read = !(flags & O_WRONLY),
             .write = !!(flags & (O_WRONLY | O_RDWR)),
             .close_on_exec = !!(flags & O_CLOEXEC),
-        },
+            }, 0, dentry->ind
+        )
     });
-
-    int fd = next_fd();
-    auto [ _, inserted ] = arr.emplace(fd, iter);
     assert(inserted);
     return fd;
 }
@@ -378,8 +373,6 @@ void NORETURN init_scheduler(void)
     procs = new proclist;
     readythds = new readyqueue;
 
-    filearr::init_global_file_container();
-
     // init process has no parent
     auto& init = procs->emplace(0);
     assert(init.pid == 1);

+ 19 - 146
src/kernel/syscall.cpp

@@ -87,68 +87,27 @@ int _syscall_fork(interrupt_stack* data)
 int _syscall_write(interrupt_stack* data)
 {
     SYSCALL_ARG1(int, fd);
-    SYSCALL_ARG2(const char*, buf);
+    SYSCALL_ARG2(const char* __user, buf);
     SYSCALL_ARG3(size_t, n);
 
     auto* file = current_process->files[fd];
-
-    if (!file || !file->flags.write)
+    if (!file)
         return -EBADF;
 
-    switch (file->type) {
-    case fs::file::types::ind: {
-        if (S_ISDIR(file->ptr.ind->mode))
-            return -EBADF;
-
-        int n_wrote = fs::vfs_write(file->ptr.ind, buf, file->cursor, n);
-        if (n_wrote >= 0)
-            file->cursor += n_wrote;
-        return n_wrote;
-    }
-    case fs::file::types::pipe:
-        return file->ptr.pp->write(buf, n);
-
-    case fs::file::types::socket:
-        // TODO
-        return -EINVAL;
-    default:
-        assert(false);
-        for ( ; ; ) ;
-    }
+    return file->write(buf, n);
 }
 
 int _syscall_read(interrupt_stack* data)
 {
     SYSCALL_ARG1(int, fd);
-    SYSCALL_ARG2(char*, buf);
+    SYSCALL_ARG2(char* __user, buf);
     SYSCALL_ARG3(size_t, n);
 
     auto* file = current_process->files[fd];
-
-    if (!file || !file->flags.read)
+    if (!file)
         return -EBADF;
 
-    switch (file->type) {
-    case fs::file::types::ind: {
-        if (S_ISDIR(file->ptr.ind->mode))
-            return -EBADF;
-
-        // TODO: copy to user function !IMPORTANT
-        int n_wrote = fs::vfs_read(file->ptr.ind, buf, n, file->cursor, n);
-        if (n_wrote >= 0)
-            file->cursor += n_wrote;
-        return n_wrote;
-    }
-    case fs::file::types::pipe:
-        return file->ptr.pp->read(buf, n);
-
-    case fs::file::types::socket:
-        // TODO
-        return -EINVAL;
-    default:
-        assert(false);
-        for ( ; ; ) ;
-    }
+    return file->read(buf, n);
 }
 
 // TODO: sleep seconds
@@ -280,38 +239,7 @@ int _syscall_getdents(interrupt_stack* data)
     if (!dir)
         return -EBADF;
 
-    if (dir->type != fs::file::types::ind || !S_ISDIR(dir->ptr.ind->mode))
-        return -ENOTDIR;
-
-    size_t orig_cnt = cnt;
-    int nread = dir->ptr.ind->fs->inode_readdir(dir->ptr.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;
-
-    return orig_cnt - cnt;
+    return dir->getdents(buf, cnt);
 }
 
 int _syscall_open(interrupt_stack* data)
@@ -430,7 +358,7 @@ int _syscall_ioctl(interrupt_stack* data)
     //       not. and we suppose that stdin will be
     //       either a tty or a pipe.
     auto* file = current_process->files[fd];
-    if (!file || file->type != fs::file::types::ind)
+    if (!file || !S_ISCHR(file->mode))
         return -ENOTTY;
 
     switch (request) {
@@ -493,45 +421,20 @@ ssize_t _syscall_writev(interrupt_stack* data)
 
     auto* file = current_process->files[fd];
 
-    if (!file || !file->flags.write)
+    if (!file)
         return -EBADF;
 
-    switch (file->type) {
-    case fs::file::types::ind: {
-        if (S_ISDIR(file->ptr.ind->mode))
-            return -EBADF;
-
-        ssize_t n_wrote = 0;
-        for (int i = 0; i < iovcnt; ++i) {
-            int ret = fs::vfs_write(file->ptr.ind,
-                (const char*)iov[i].iov_base,
-                file->cursor, iov[i].iov_len);
-            if (ret < 0)
-                return ret;
-            n_wrote += ret;
-        }
-        file->cursor += n_wrote;
-        return n_wrote;
-    }
-    case fs::file::types::pipe: {
-        ssize_t tot = 0;
-        for (int i = 0; i < iovcnt; ++i) {
-            int retval = file->ptr.pp->write(
-                (const char*)iov->iov_base, iovcnt);
-            if (retval < 0)
-                return retval;
-            tot += retval;
-        }
-        return tot;
-    }
+    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);
 
-    case fs::file::types::socket:
-        // TODO
-        return -EINVAL;
-    default:
-        assert(false);
-        for ( ; ; ) ;
+        if (ret < 0)
+            return ret;
+        totn += ret;
     }
+
+    return totn;
 }
 
 int _syscall_prctl(interrupt_stack* data)
@@ -744,37 +647,7 @@ int _syscall_getdents64(interrupt_stack* data)
     if (!dir)
         return -EBADF;
 
-    if (dir->type != fs::file::types::ind || !S_ISDIR(dir->ptr.ind->mode))
-        return -ENOTDIR;
-
-    size_t orig_cnt = cnt;
-    int nread = dir->ptr.ind->fs->inode_readdir(dir->ptr.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_dirent64) + len;
-            if (cnt < reclen)
-                return GB_FAILED;
-
-            auto* dirp = (fs::user_dirent64*)buf;
-            dirp->d_ino = ino;
-            dirp->d_off = 114514;
-            dirp->d_reclen = reclen;
-            dirp->d_type = type;
-            // TODO: use copy_to_user
-            memcpy(dirp->d_name, fn, len);
-            buf[reclen - 1] = 0;
-
-            buf += reclen;
-            cnt -= reclen;
-            return GB_OK;
-        });
-
-    if (nread > 0)
-        dir->cursor += nread;
-
-    return orig_cnt - cnt;
+    return dir->getdents64(buf, cnt);
 }
 
 extern "C" void syscall_entry(interrupt_stack* data)

+ 140 - 0
src/kernel/vfs.cpp

@@ -418,6 +418,146 @@ public:
     }
 };
 
+fs::regular_file::regular_file(vfs::dentry* parent,
+    file_flags flags, size_t cursor, inode* ind)
+    : file(S_IFREG, parent, 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::vfs_read(ind, buf, n, cursor, n);
+    if (n_wrote >= 0)
+        cursor += n_wrote;
+
+    return n_wrote;
+}
+
+ssize_t fs::regular_file::write(const char* __user buf, size_t n)
+{
+    if (!flags.write)
+        return -EBADF;
+
+    if (S_ISDIR(ind->mode))
+        return -EISDIR;
+
+    // TODO: check privilege of user ptr
+    ssize_t n_wrote = fs::vfs_write(ind, buf, cursor, n);
+    if (n_wrote >= 0)
+        cursor += n_wrote;
+
+    return n_wrote;
+}
+
+void fs::regular_file::close(void) { } // TODO: mark inode as free
+
+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->inode_readdir(ind, cursor,
+        [&buf, &cnt](const char* fn, size_t len, fs::ino_t ino, uint8_t type) {
+            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)
+        cursor += nread;
+
+    return orig_cnt - 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->inode_readdir(ind, cursor,
+        [&buf, &cnt](const char* fn, size_t len, fs::ino_t ino, uint8_t type) {
+            if (!len)
+                len = strlen(fn);
+
+            size_t reclen = sizeof(fs::user_dirent64) + len;
+            if (cnt < reclen)
+                return GB_FAILED;
+
+            auto* dirp = (fs::user_dirent64*)buf;
+            dirp->d_ino = ino;
+            dirp->d_off = 114514;
+            dirp->d_reclen = reclen;
+            dirp->d_type = type;
+            // TODO: use copy_to_user
+            memcpy(dirp->d_name, fn, len);
+            buf[reclen - 1] = 0;
+
+            buf += reclen;
+            cnt -= reclen;
+            return GB_OK;
+        });
+
+    if (nread > 0)
+        cursor += nread;
+
+    return orig_cnt - cnt;
+}
+
+fs::fifo_file::fifo_file(vfs::dentry* parent, file_flags flags,
+    std::shared_ptr<fs::pipe> ppipe)
+    : file(S_IFIFO, parent, flags), ppipe(ppipe) { }
+
+ssize_t fs::fifo_file::read(char* __user buf, size_t n)
+{
+    if (!flags.read)
+        return -EBADF;
+
+    return ppipe->read(buf, n);
+}
+
+ssize_t fs::fifo_file::write(const char* __user buf, size_t n)
+{
+    if (!flags.write)
+        return -EBADF;
+
+    return ppipe->write(buf, n);
+}
+
+void fs::fifo_file::close(void)
+{
+    assert(flags.read ^ flags.write);
+    if (flags.read)
+        ppipe->close_read();
+    else
+        ppipe->close_write();
+
+    ppipe.reset();
+}
+
 // 8 * 8 for now
 static fs::special_node sns[8][8];