mirror of
https://github.com/EnderIce2/Fennix.git
synced 2025-05-27 15:04:31 +00:00
This is the fourth time re-writing the VFS, hope this will be the last. Tried to make it as modular as possible so this won't be necessary in the future. 🙏
This change required the entire kernel code to be modified.
208 lines
6.2 KiB
C++
208 lines
6.2 KiB
C++
/*
|
|
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 <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <interface/fs.h>
|
|
#include <errno.h>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
/**
|
|
* 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<NodeCache> 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<Node> 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
|