:github_url: https://github.com/nyx-org/gaia .. _program_listing_file_posix_fd.hpp: Program Listing for File fd.hpp =============================== |exhale_lsh| :ref:`Return to documentation for file ` (``posix/fd.hpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp /* SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include "lib/stream.hpp" #include #include #include #include #include #define HAVE_ARCH_STRUCT_FLOCK #include #include #include #include #include #define OFLAG_WRITABLE(flag) (((flag)&O_RDWR) || ((flag)&O_WRONLY)) namespace Gaia::Posix { using namespace Fs; constexpr auto OPEN_MAX = 64; class Fd { public: static Result open(const char *path, int status) { auto res = vfs_find(path); // O_CREAT and O_EXCL are set, and the named file exists. if (res.is_ok() && (status & O_CREAT) && (status & O_EXCL)) { return Err(EEXIST); } if (res.is_ok()) { auto vnode = res.value().value(); // The named file is a directory and oflag includes O_WRONLY or O_RDWR. if (vnode->type == Vnode::Type::DIR && OFLAG_WRITABLE(status)) { return Err(EISDIR); } if (status & O_DIRECTORY && vnode->type != Vnode::Type::DIR) { return Err(ENOTDIR); } } if (res.is_err() && status & O_CREAT) { res = vfs_find_and(path, -1, vfs_create_file); } if (res.is_err()) { return Err(error_to_errno(res.error().value())); } auto vnode = res.value().value(); return Ok(Fd{vnode, status}); } static Result openat(Fd dirfd, const char *path, int status) { auto res = vfs_find(path, dirfd.vnode); // O_CREAT and O_EXCL are set, and the named file exists. if (res.is_ok() && (status & O_CREAT) && (status & O_EXCL)) { return Err(EEXIST); } if (res.is_ok()) { auto vnode = res.value().value(); // The named file is a directory and oflag includes O_WRONLY or O_RDWR. if (vnode->type == Vnode::Type::DIR && OFLAG_WRITABLE(status)) { return Err(EISDIR); } } if (res.is_err() && status & O_CREAT) { res = vfs_find_and(path, -1, vfs_create_file, dirfd.vnode); } if (res.is_err()) { return Err(error_to_errno(res.error().value())); } auto vnode = res.value().value(); return Ok(Fd{vnode, status}); } Result seek(off_t offset, Stream::Whence whence) { auto res = stream.seek(offset, whence); if (res.is_err()) { return Err(error_to_errno(res.error().value())); } return Ok(res.value().value()); } Result write(void *buf, size_t count) { if (!(status & O_RDWR) && !(status & O_WRONLY)) { return Err(EBADF); } auto res = stream.write(buf, count); if (res.is_err()) { return Err(error_to_errno(res.error().value())); } return Ok(res.value().value()); } Result read(void *buf, size_t count) { // O_RDONLY is 0 so that we need to check flags != O_RDONLY if (!(status & O_RDWR) && !(status & O_RDONLY) && status != O_RDONLY) { return Err(EBADF); } auto res = stream.read(buf, count); if (res.is_err()) { return Err(error_to_errno(res.error().value())); } return Ok(res.value().value()); } Result ioctl(uint64_t request, void *arg) { auto res = vnode->ops->ioctl(vnode, request, arg); if (res.is_err()) { return Err(error_to_errno(res.error().value())); } return Ok(res.value().value()); } Result stat() { struct stat ret {}; auto attr = DefaultVnodeAttr; auto res = vnode->ops->getattr(vnode); if (res.is_err()) { return Err(error_to_errno(res.error().value())); } attr = res.unwrap(); ret.st_mode = attr.mode; ret.st_blksize = 512; ret.st_size = attr.size; ret.st_dev = 0; ret.st_ino = (ino_t)vnode->data; ret.st_rdev = vnode->dev; ret.st_blocks = attr.size / 512; switch (vnode->type) { case Vnode::Type::REG: ret.st_mode |= S_IFREG; break; case Vnode::Type::DIR: ret.st_mode |= S_IFDIR; break; case Vnode::Type::LNK: ret.st_mode |= S_IFLNK; break; case Vnode::Type::CHR: ret.st_mode |= S_IFCHR; break; default: break; } return Ok(ret); } Result readdir(void *buf, size_t max_size) { auto position = stream.seek(0, Stream::Whence::CURRENT).unwrap(); auto res = vfs_readdir(vnode, buf, max_size, position); if (res.is_err()) { return Err(error_to_errno(res.error().value())); } auto ret = res.value().value(); stream.seek(ret, Stream::Whence::CURRENT); return Ok(ret); } int get_flags() { return flags; } void set_flags(int val) { flags = val; } int get_status() { return status; } void set_status(int val) { status = val; } void close(); Fd(Vnode *vn, int status) : vnode(vn), stream(vn), status(status) {} Fd() : vnode(nullptr), stream(nullptr), flags(0) {} Fd(Fd &&other) : vnode(other.vnode), stream(other.stream), flags(other.flags), status(other.status){}; Fd(Fd &other) : vnode(other.vnode), stream(other.stream), flags(other.flags), status(other.status){}; Fd(Fd const &other) : vnode(other.vnode), stream(other.stream), flags(other.flags), status(other.status){}; private: Vnode *vnode; VnodeStream stream; int flags, status; }; constexpr auto FD_MAX = 256; class Fds { public: frg::optional allocate(Fd *fd) { for (size_t i = 0; i < FD_MAX; i++) { if (!bitmap.test(i)) { bitmap.set(i); fds[i] = fd; return i; } } return frg::null_opt; } frg::optional allocate_greater_than_or_equal(int fdnum, Fd *fd) { for (size_t i = fdnum; i < FD_MAX; i++) { if (!bitmap.test(i)) { bitmap.set(i); fds[i] = fd; return i; } } return frg::null_opt; } Result free(int fdnum) { if (fdnum > FD_MAX || fdnum < 0) return Err(EBADF); if (!bitmap.test(fdnum)) return Err(EBADF); bitmap.flip(fdnum); return Ok({}); } frg::optional get(int number) { if (number > FD_MAX || number < 0) { return frg::null_opt; } if (bitmap.test(number)) { return fds[number]; } return frg::null_opt; } Result set(int number, Fd *fd) { if (number > FD_MAX || number < 0) { return Err(EINVAL); } if (bitmap.test(number)) { delete fds[number]; // we might need to delete the allocated FD here free(number); } bitmap.set(number); fds[number] = fd; return Ok({}); } inline Fds &operator=(const Fds &other) { fds = other.fds; bitmap = other.bitmap; return *this; } frg::array data() { return fds; } Fds() { bitmap.reset(); } Fds(Fds &other) : fds(other.fds), bitmap(other.bitmap) {} private: frg::array fds; frg::bitset bitmap; }; } // namespace Gaia::Posix