Bladeren bron

change(filearray): add filearray class

greatbridf 9 maanden geleden
bovenliggende
commit
6be43506e5

+ 2 - 0
CMakeLists.txt

@@ -61,6 +61,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         src/kernel/task/thread.cc
                         src/kernel/task/readyqueue.cc
                         src/kernel/user/thread_local.cc
+                        src/kernel/vfs/filearr.cc
                         src/kernel/vfs/tmpfs.cc
                         src/kernel/signal.cpp
                         src/types/elf.cpp
@@ -83,6 +84,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/kernel/vfs.hpp
                         include/kernel/vfs/dentry.hpp
                         include/kernel/vfs/file.hpp
+                        include/kernel/vfs/filearr.hpp
                         include/kernel/vfs/inode.hpp
                         include/kernel/vfs/vfs.hpp
                         include/kernel/vga.hpp

+ 2 - 122
include/kernel/process.hpp

@@ -29,6 +29,7 @@
 #include <kernel/tty.hpp>
 #include <kernel/user/thread_local.hpp>
 #include <kernel/vfs.hpp>
+#include <kernel/vfs/filearr.hpp>
 
 class process;
 
@@ -42,127 +43,6 @@ struct process_attr {
     uint16_t zombie : 1 = 0;
 };
 
-struct thread_attr {
-    uint32_t system : 1;
-    uint32_t ready : 1;
-};
-
-class filearr {
-private:
-    // TODO: change this
-    struct fditem {
-        int flags;
-        std::shared_ptr<fs::file> file;
-    };
-
-    std::map<int, fditem> arr;
-    int min_avail { };
-
-private:
-    int allocate_fd(int from);
-    void release_fd(int fd);
-    inline int next_fd() { return allocate_fd(min_avail); }
-
-public:
-    constexpr filearr() = default;
-    constexpr filearr(const filearr& val) = default;
-    constexpr filearr(filearr&& val) = default;
-
-    constexpr filearr& operator=(const filearr&) = delete;
-    constexpr filearr& operator=(filearr&&) = delete;
-
-    // TODO: the third parameter should be int flags
-    //       determining whether the fd should be closed
-    //       after exec() (FD_CLOEXEC)
-    int dup2(int old_fd, int new_fd);
-    int dup(int old_fd);
-
-    int dupfd(int fd, int minfd, int flags);
-
-    int set_flags(int fd, int flags);
-    int clear_flags(int fd, int flags);
-
-    constexpr fs::file* operator[](int i) const
-    {
-        auto iter = arr.find(i);
-        if (!iter)
-            return nullptr;
-        return iter->second.file.get();
-    }
-
-    int pipe(int pipefd[2])
-    {
-        std::shared_ptr<fs::pipe> ppipe { new fs::pipe };
-
-        bool inserted = false;
-        int fd = next_fd();
-        std::tie(std::ignore, inserted) = arr.emplace(fd, fditem {
-            0, std::shared_ptr<fs::file> {
-                new fs::fifo_file(nullptr, {
-                    .read = 1,
-                    .write = 0,
-                    .append = 0,
-                }, ppipe),
-        } } );
-        assert(inserted);
-
-        // TODO: use copy_to_user()
-        pipefd[0] = fd;
-
-        fd = next_fd();
-        std::tie(std::ignore, inserted) = arr.emplace(fd, fditem {
-            0, std::shared_ptr<fs::file> {
-                new fs::fifo_file(nullptr, {
-                    .read = 0,
-                    .write = 1,
-                    .append = 0,
-                }, ppipe),
-        } } );
-        assert(inserted);
-
-        // TODO: use copy_to_user()
-        pipefd[1] = fd;
-
-        return 0;
-    }
-
-    int open(const process& current, const types::path& filepath, int flags, mode_t mode);
-
-    constexpr void close(int fd)
-    {
-        auto iter = arr.find(fd);
-        if (!iter)
-            return;
-
-        release_fd(fd);
-        arr.erase(iter);
-    }
-
-    constexpr void onexec()
-    {
-        for (auto iter = arr.begin(); iter != arr.end(); ) {
-            if (!(iter->second.flags & FD_CLOEXEC)) {
-                ++iter;
-                continue;
-            }
-            release_fd(iter->first);
-            iter = arr.erase(iter);
-        }
-    }
-
-    constexpr void close_all(void)
-    {
-        for (const auto& item : arr)
-            release_fd(item.first);
-        arr.clear();
-    }
-
-    constexpr ~filearr()
-    {
-        close_all();
-    }
-};
-
 class process {
 public:
     struct wait_obj {
@@ -179,7 +59,7 @@ public:
     std::list<wait_obj> waitprocs;
 
     process_attr attr {};
-    filearr files;
+    fs::filearray files;
     types::path pwd;
     mode_t umask { 0022 };
 

+ 0 - 5
include/kernel/vfs/file.hpp

@@ -45,11 +45,6 @@ public:
     {
         return flags & WRITABLE;
     }
-
-    constexpr bool is_free() const
-    {
-        return !(flags & (READABLE | WRITABLE));
-    }
 };
 
 struct file {

+ 45 - 0
include/kernel/vfs/filearr.hpp

@@ -0,0 +1,45 @@
+#pragma once
+
+#include <memory>
+
+#include "dentry.hpp"
+#include "file.hpp"
+
+namespace fs {
+
+class filearray {
+private:
+    struct impl;
+    std::shared_ptr<impl> pimpl;
+    filearray(std::shared_ptr<impl>);
+
+public:
+    filearray();
+    filearray(filearray&& other) = default;
+
+    filearray copy() const;
+    filearray share() const;
+
+    // dup old_fd to some random fd
+    int dup(int old_fd);
+
+    // dup old_fd to new_fd, close new_fd if it is already open
+    int dup(int old_fd, int new_fd, int flags);
+
+    // dup old_fd to the first available fd starting from min_fd
+    int dupfd(int fd, int min_fd, int flags);
+
+    fs::file* operator[](int i) const;
+    int set_flags(int fd, int flags);
+
+    int pipe(int (&pipefd)[2]);
+    int open(dentry& root, const types::path& filepath, int flags, mode_t mode);
+
+    int close(int fd);
+
+    // any call to member methods will be invalid after clear()
+    void clear();
+    void onexec();
+};
+
+} // namespace fs

+ 5 - 154
src/kernel/process.cpp

@@ -52,160 +52,8 @@ struct no_irq_guard {
 
 } // namespace kernel
 
-int filearr::allocate_fd(int from)
-{
-    if (from < min_avail)
-        from = min_avail;
-
-    if (from == min_avail) {
-        int nextfd = min_avail + 1;
-        auto iter = arr.find(nextfd);
-        while (iter != arr.end() && nextfd == iter->first)
-            ++nextfd, ++iter;
-
-        int retval = min_avail;
-        min_avail = nextfd;
-        return retval;
-    }
-
-    int fd = from;
-    auto iter = arr.find(fd);
-    while (iter != arr.end() && fd == iter->first)
-        ++fd, ++iter;
-
-    return fd;
-}
-
-void filearr::release_fd(int fd)
-{
-    if (fd < min_avail)
-        min_avail = fd;
-}
-
-int filearr::dup(int old_fd)
-{
-    return dup2(old_fd, next_fd());
-}
-
-int filearr::dup2(int old_fd, int new_fd)
-{
-    close(new_fd);
-
-    auto iter = arr.find(old_fd);
-    if (!iter)
-        return -EBADF;
-
-    int fd = allocate_fd(new_fd);
-    assert(fd == new_fd);
-
-    auto [ newiter, inserted ] = this->arr.emplace(new_fd, iter->second);
-    assert(inserted);
-
-    newiter->second.flags = 0;
-
-    return new_fd;
-}
-
-int filearr::dupfd(int fd, int minfd, int flags)
-{
-    auto iter = arr.find(fd);
-    if (!iter)
-        return -EBADF;
-
-    int new_fd = allocate_fd(minfd);
-    auto [ newiter, inserted ] = arr.emplace(new_fd, iter->second);
-    assert(inserted);
-
-    newiter->second.flags = flags;
-    return new_fd;
-}
-
-int filearr::set_flags(int fd, int flags)
-{
-    auto iter = arr.find(fd);
-    if (!iter)
-        return -EBADF;
-    iter->second.flags |= flags;
-    return 0;
-}
-
-int filearr::clear_flags(int fd, int flags)
-{
-    auto iter = arr.find(fd);
-    if (!iter)
-        return -EBADF;
-    iter->second.flags &= ~flags;
-    return 0;
-}
-
-// TODO: file opening permissions check
-int filearr::open(const process &current,
-    const types::path& filepath, int flags, mode_t mode)
-{
-    auto* dentry = fs::vfs_open(*current.root, filepath);
-
-    if (flags & O_CREAT) {
-        if (!dentry) {
-            // create file
-            auto filename = filepath.last_name();
-            auto parent_path = filepath;
-            parent_path.remove_last();
-
-            auto* parent = fs::vfs_open(*current.root, parent_path);
-            if (!parent)
-                return -EINVAL;
-            int ret = fs::vfs_mkfile(parent, filename.c_str(), mode);
-            if (ret != 0)
-                return ret;
-            dentry = fs::vfs_open(*current.root, filepath);
-            assert(dentry);
-        } else {
-            // file already exists
-            if (flags & O_EXCL)
-                return -EEXIST;
-        }
-    } else {
-        if (!dentry)
-            return -ENOENT;
-    }
-
-    auto filemode = dentry->ind->mode;
-
-    // check whether dentry is a file if O_DIRECTORY is set
-    if (flags & O_DIRECTORY) {
-        if (!S_ISDIR(filemode))
-            return -ENOTDIR;
-    } else {
-        if (S_ISDIR(filemode) && (flags & (O_WRONLY | O_RDWR)))
-            return -EISDIR;
-    }
-
-    // truncate file
-    if (flags & O_TRUNC) {
-        if ((flags & (O_WRONLY | O_RDWR)) && S_ISREG(filemode)) {
-            auto ret = fs::vfs_truncate(dentry->ind, 0);
-            if (ret != 0)
-                return ret;
-        }
-    }
-
-    int fdflag = (flags & O_CLOEXEC) ? FD_CLOEXEC : 0;
-
-    int fd = next_fd();
-    auto [ _, inserted ] = arr.emplace(fd, fditem {
-        fdflag, std::shared_ptr<fs::file> {
-            new fs::regular_file(dentry->parent, {
-                .read = !(flags & O_WRONLY),
-                .write = !!(flags & (O_WRONLY | O_RDWR)),
-                .append = !!(S_ISREG(filemode) && flags & O_APPEND),
-            }, 0, dentry->ind),
-    } } );
-    assert(inserted);
-    return fd;
-}
-
 process::process(const process& parent, pid_t pid)
-    : mms { parent.mms }, attr { parent.attr } , files { parent.files }
+    : mms { parent.mms }, attr { parent.attr } , files { parent.files.copy() }
     , pwd { parent.pwd }, umask { parent.umask }, pid { pid }
     , ppid { parent.pid }, pgid { parent.pgid } , sid { parent.sid }
     , control_tty { parent.control_tty }, root { parent.root } { }
@@ -328,8 +176,11 @@ void proclist::kill(pid_t pid, int exit_code)
     for (auto& thd : proc.thds)
         thd.set_attr(kernel::task::thread::ZOMBIE);
 
+    // TODO: CHANGE THIS
+    //       files should only be closed when this is the last thread
+    //
     // write back mmap'ped files and close them
-    proc.files.close_all();
+    proc.files.clear();
 
     // unmap all user memory areas
     proc.mms.clear();

+ 4 - 3
src/kernel/syscall/fileops.cc

@@ -52,12 +52,13 @@ int kernel::syscall::do_dup(int old_fd)
 
 int kernel::syscall::do_dup2(int old_fd, int new_fd)
 {
-    return current_process->files.dup2(old_fd, new_fd);
+    return current_process->files.dup(old_fd, new_fd, 0);
 }
 
 int kernel::syscall::do_pipe(int __user* pipefd)
 {
-    return current_process->files.pipe(pipefd);
+    // TODO: use copy_from_user and copy_to_user
+    return current_process->files.pipe(*(int(*)[2])pipefd);
 }
 
 ssize_t kernel::syscall::do_getdents(int fd, char __user* buf, size_t cnt)
@@ -82,7 +83,7 @@ int kernel::syscall::do_open(const char __user* path, int flags, mode_t mode)
 {
     mode &= ~current_process->umask;
 
-    return current_process->files.open(*current_process,
+    return current_process->files.open(*current_process->root,
         current_process->pwd + path, flags, mode);
 }
 

+ 328 - 0
src/kernel/vfs/filearr.cc

@@ -0,0 +1,328 @@
+#include <set>
+
+#include <kernel/async/lock.hpp>
+#include <kernel/vfs.hpp>
+#include <kernel/vfs/filearr.hpp>
+
+using namespace fs;
+
+using kernel::async::mutex, kernel::async::lock_guard;
+
+struct fditem {
+    int fd;
+    int flags;
+    std::shared_ptr<file> pfile;
+};
+
+struct fditem_comparator {
+    constexpr bool operator()(const fditem& lhs, const fditem& rhs) const
+    {
+        return lhs.fd < rhs.fd;
+    }
+
+    constexpr bool operator()(int fd, const fditem& rhs) const
+    {
+        return fd < rhs.fd;
+    }
+
+    constexpr bool operator()(const fditem& lhs, int fd) const
+    {
+        return lhs.fd < fd;
+    }
+};
+
+// ALL METHODS SHOULD BE CALLED WITH LOCK HELD
+struct filearray::impl {
+    mutex mtx;
+
+    std::set<fditem, fditem_comparator> arr;
+    int min_avail{};
+
+    int allocate_fd(int from);
+    void release_fd(int fd);
+    int next_fd();
+
+    int do_dup(const fditem& oldfile, int new_fd, int flags);
+    int place_new_file(std::shared_ptr<file> pfile, int flags);
+};
+
+int filearray::impl::allocate_fd(int from)
+{
+    if (from < min_avail)
+        from = min_avail;
+
+    if (from == min_avail) {
+        int nextfd = min_avail + 1;
+        auto iter = arr.find(nextfd);
+        while (iter && nextfd == iter->fd)
+            ++nextfd, ++iter;
+
+        int retval = min_avail;
+        min_avail = nextfd;
+        return retval;
+    }
+
+    int fd = from;
+    auto iter = arr.find(fd);
+    while (iter && fd == iter->fd)
+        ++fd, ++iter;
+
+    return fd;
+}
+
+void filearray::impl::release_fd(int fd)
+{
+    if (fd < min_avail)
+        min_avail = fd;
+}
+
+int filearray::impl::next_fd()
+{
+    return allocate_fd(min_avail);
+}
+
+int filearray::impl::do_dup(const fditem& oldfile, int new_fd, int flags)
+{
+    bool inserted;
+    std::tie(std::ignore, inserted) = arr.emplace(new_fd, flags, oldfile.pfile);
+    assert(inserted);
+
+    return new_fd;
+}
+
+int filearray::impl::place_new_file(std::shared_ptr<file> pfile, int flags)
+{
+    int fd = next_fd();
+
+    bool inserted;
+    std::tie(std::ignore, inserted) = arr.emplace(fd, std::move(flags), pfile);
+    assert(inserted);
+
+    return fd;
+}
+
+int filearray::dup(int old_fd)
+{
+    lock_guard lck{pimpl->mtx};
+
+    auto iter = pimpl->arr.find(old_fd);
+    if (!iter)
+        return -EBADF;
+
+    int fd = pimpl->next_fd();
+    return pimpl->do_dup(*iter, fd, 0);
+}
+
+int filearray::dup(int old_fd, int new_fd, int flags)
+{
+    lock_guard lck{pimpl->mtx};
+
+    auto iter_old = pimpl->arr.find(old_fd);
+    if (!iter_old)
+        return -EBADF;
+
+    auto iter_new = pimpl->arr.find(new_fd);
+    if (iter_new) {
+        iter_new->pfile = iter_old->pfile;
+        iter_new->flags = flags;
+
+        return new_fd;
+    }
+
+    int fd = pimpl->allocate_fd(new_fd);
+    assert(fd == new_fd);
+    return pimpl->do_dup(*iter_old, fd, flags);
+}
+
+int filearray::dupfd(int fd, int min_fd, int flags)
+{
+    lock_guard lck{pimpl->mtx};
+
+    auto iter = pimpl->arr.find(fd);
+    if (!iter)
+        return -EBADF;
+
+    int new_fd = pimpl->allocate_fd(min_fd);
+    return pimpl->do_dup(*iter, new_fd, flags);
+}
+
+int filearray::set_flags(int fd, int flags)
+{
+    lock_guard lck{pimpl->mtx};
+
+    auto iter = pimpl->arr.find(fd);
+    if (!iter)
+        return -EBADF;
+
+    iter->flags |= flags;
+    return 0;
+}
+
+int filearray::close(int fd)
+{
+    lock_guard lck{pimpl->mtx};
+
+    auto iter = pimpl->arr.find(fd);
+    if (!iter)
+        return -EBADF;
+
+    pimpl->release_fd(fd);
+    pimpl->arr.erase(iter);
+
+    return 0;
+}
+
+static inline int _open_file(dentry*& out_dent, dentry& root, const types::path& filepath, int flags, mode_t mode)
+{
+    auto* dent = vfs_open(root, filepath);
+
+    if (dent) {
+        if ((flags & O_CREAT) && (flags & O_EXCL))
+            return -EEXIST;
+        out_dent = dent;
+        return 0;
+    }
+
+    if (!(flags & O_CREAT))
+        return -ENOENT;
+
+    // create file
+
+    auto filename = filepath.last_name();
+    auto parent_path = filepath;
+    parent_path.remove_last();
+
+    auto* parent = vfs_open(root, parent_path);
+    if (!parent)
+        return -EINVAL;
+
+    int ret = vfs_mkfile(parent, filename.c_str(), mode);
+    if (ret != 0)
+        return ret;
+
+    dent = parent->find(filename);
+    assert(dent);
+
+    out_dent = dent;
+    return 0;
+}
+
+// TODO: file opening permissions check
+int filearray::open(dentry& root, const types::path& filepath, int flags, mode_t mode)
+{
+    lock_guard lck{pimpl->mtx};
+
+    dentry* dent {};
+    int ret = _open_file(dent, root, filepath, flags, mode);
+    if (ret != 0)
+        return ret;
+
+    auto filemode = dent->ind->mode;
+
+    int fdflag = (flags & O_CLOEXEC) ? FD_CLOEXEC : 0;
+
+    file::file_flags fflags;
+    fflags.read = !(flags & O_WRONLY);
+    fflags.write = (flags & (O_WRONLY | O_RDWR));
+    fflags.append = S_ISREG(filemode) && (flags & O_APPEND);
+
+    // check whether dentry is a file if O_DIRECTORY is set
+    if (flags & O_DIRECTORY) {
+        if (!S_ISDIR(filemode))
+            return -ENOTDIR;
+    } else {
+        if (S_ISDIR(filemode) && fflags.write)
+            return -EISDIR;
+    }
+
+    // truncate file
+    if (flags & O_TRUNC) {
+        if (fflags.write && S_ISREG(filemode)) {
+            auto ret = vfs_truncate(dent->ind, 0);
+            if (ret != 0)
+                return ret;
+        }
+    }
+
+    return pimpl->place_new_file(
+        std::make_shared<regular_file>(
+            dent->parent, fflags, 0, dent->ind),
+        fdflag);
+}
+
+int filearray::pipe(int (&pipefd)[2])
+{
+    lock_guard lck{pimpl->mtx};
+
+    if (1) {
+        std::shared_ptr<fs::pipe> ppipe { new fs::pipe };
+
+        pipefd[0] = pimpl->place_new_file(
+            std::make_shared<fifo_file>(
+                nullptr, file::file_flags { 1, 0, 0 }, ppipe),
+            0);
+
+        pipefd[1] = pimpl->place_new_file(
+            std::make_shared<fifo_file>(
+                nullptr, file::file_flags { 0, 1, 0 }, ppipe),
+            0);
+    }
+
+    return 0;
+}
+
+filearray::filearray(std::shared_ptr<impl> ptr)
+    : pimpl { ptr }
+{
+}
+
+filearray::filearray()
+    : filearray { std::make_shared<impl>() }
+{
+}
+
+filearray filearray::copy() const
+{
+    lock_guard lck { pimpl->mtx };
+    filearray ret {};
+
+    ret.pimpl->min_avail = pimpl->min_avail;
+    ret.pimpl->arr = pimpl->arr;
+
+    return ret;
+}
+
+filearray filearray::share() const
+{
+    return filearray { pimpl };
+}
+
+void filearray::clear()
+{
+    pimpl.reset();
+}
+
+void filearray::onexec()
+{
+    lock_guard lck{pimpl->mtx};
+
+    for (auto iter = pimpl->arr.begin(); iter;) {
+        if (!(iter->flags & FD_CLOEXEC)) {
+            ++iter;
+            continue;
+        }
+        pimpl->release_fd(iter->fd);
+        iter = pimpl->arr.erase(iter);
+    }
+}
+
+file* filearray::operator[](int i) const
+{
+    lock_guard lck{pimpl->mtx};
+
+    auto iter = pimpl->arr.find(i);
+    if (!iter)
+        return nullptr;
+
+    return iter->pfile.get();
+}