/* This file is part of Fennix Kernel. Fennix Kernel is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Fennix Kernel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Fennix Kernel. If not, see . */ #pragma once #include #include #include #include /** * This macro is used to check if a filesystem operation is available. * * TL;DR * * @code * if FileSystemInfo.Ops.op == nullptr * return -err * else * return FileSystemInfo.Ops.op(this->Node, ...); * @endcode * * @param op The operation to check. * @param err The error to return if the operation is not available. * @param ... The arguments to pass to the operation. * * @return The result of the operation. */ #define __check_op(op, err, ...) \ if (unlikely(fsi->Ops.op == nullptr)) \ return -err; \ else \ return fsi->Ops.op(this->inode, ##__VA_ARGS__) namespace vfs { class NodeObject { public: Inode *inode; FileSystemInfo *fsi; union { struct { uint8_t MountPoint : 1; uint8_t __reserved : 7; }; uint8_t raw; } Flags; int __Lookup(const char *Name, Inode **Node) { __check_op(Lookup, ENOTSUP, Name, Node); } int __Create(const char *Name, mode_t Mode, Inode **Node) { __check_op(Create, ENOTSUP, Name, Mode, Node); } int __Remove(const char *Name) { __check_op(Remove, ENOTSUP, Name); } int __Rename(const char *OldName, const char *NewName) { __check_op(Rename, ENOTSUP, OldName, NewName); } ssize_t __Read(auto Buffer, size_t Size, off_t Offset) { __check_op(Read, ENOTSUP, (void *)Buffer, Size, Offset); } ssize_t __Write(const auto Buffer, size_t Size, off_t Offset) { __check_op(Write, ENOTSUP, (const void *)Buffer, Size, Offset); } int __Truncate(off_t Size) { __check_op(Truncate, ENOTSUP, Size); } int __Open(int Flags, mode_t Mode) { __check_op(Open, ENOTSUP, Flags, Mode); } int __Close() { __check_op(Close, ENOTSUP); } int __Ioctl(unsigned long Request, void *Argp) { __check_op(Ioctl, ENOTSUP, Request, Argp); } ssize_t __ReadDir(struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries) { __check_op(ReadDir, ENOTSUP, Buffer, Size, Offset, Entries); } int __MkDir(const char *Name, mode_t Mode, struct Inode **Result) { __check_op(MkDir, ENOTSUP, Name, Mode, Result); } int __RmDir(const char *Name) { __check_op(RmDir, ENOTSUP, Name); } int __SymLink(const char *Name, const char *Target, struct Inode **Result) { __check_op(SymLink, ENOTSUP, Name, Target, Result); } ssize_t __ReadLink(auto Buffer, size_t Size) { __check_op(ReadLink, ENOTSUP, (char *)Buffer, Size); } off_t __Seek(off_t Offset) { __check_op(Seek, ENOTSUP, Offset); } int __Stat(struct kstat *Stat) { __check_op(Stat, ENOTSUP, Stat); } ~NodeObject() { debug("%#lx destructor called", this); } }; } class NodeCache; /** * @brief Node is a type that represents a filesystem node. * It is a shared pointer to a NodeCache object. * * If the refcount of the NodeCache object goes to zero, the data is synced to disk. */ typedef std::shared_ptr Node; /** * @brief NodeResult is a type that represents the result of a filesystem operation. * It contains a Node object and an error code. */ typedef struct NodeResult { /* Value must be the first member of the struct to allow for implicit conversion */ Node Value; int Error; operator bool() const { return Error == 0 && Value.get() != nullptr; } operator Node() const { return Value; } const char *what() const { return strerror(Error); } } eNode; class NodeCache : public vfs::NodeObject { public: std::string Name, Path, Link; /** * @brief Parent of this node * * Maximum depth is 1, otherwise undefined behavior. */ Node Parent; /** * @brief Childrens of this node * * On access, children are loaded, but not children of children! * Accessing children of children is undefined behavior. */ std::vector Children; std::string GetName() { return Name; } std::string GetPath() { return Path; } std::string GetLink() { return Link; } bool IsDirectory() { return S_ISDIR(inode->Mode); } bool IsCharacterDevice() { return S_ISCHR(inode->Mode); } bool IsBlockDevice() { return S_ISBLK(inode->Mode); } bool IsRegularFile() { return S_ISREG(inode->Mode); } bool IsFIFO() { return S_ISFIFO(inode->Mode); } bool IsSymbolicLink() { return S_ISLNK(inode->Mode); } bool IsSocket() { return S_ISSOCK(inode->Mode); } bool IsMountPoint() { return Flags.MountPoint; } /** * @brief Search through the cached children of this node * * Searches through the cached children of this node. * * @param Name The name to search for * @return 0 and a Node on success, ENOENT on failure */ eNode CachedSearch(std::string Name) { for (auto it = Children.begin(); it != Children.end(); ++it) { if ((*it).get() == nullptr) { Children.erase(it); continue; } debug("comparing \"%s\" with \"%s\"", (*it)->Name.c_str(), Name.c_str()); if ((*it)->Name == Name) { debug("\"%s\" found", Name.c_str()); return {*it, 0}; } } return {nullptr, ENOENT}; } /** * @brief Get the allocation size of this object * * @return The allocated size */ size_t GetAllocationSize() { size_t size = sizeof(NodeCache); size += Name.capacity(); size += Path.capacity(); size += Link.capacity(); size += Children.capacity(); return size; } ~NodeCache() { debug("%#lx\"%s\" destructor called", this, Name.c_str()); if (this->Parent) this->Parent->Children.erase(std::remove(this->Parent->Children.begin(), this->Parent->Children.end(), Node(this)), this->Parent->Children.end()); if (fsi->SuperOps.Synchronize) fsi->SuperOps.Synchronize(this->fsi, this->inode); // FIXME: recursive deletion of nodes children } }; #undef __check_op