/*
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