fix(kernel/vfs): 🎉 a complete rewrite of the vfs

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.
This commit is contained in:
2025-05-13 15:59:12 +00:00
parent 83a7f83f81
commit 557c7e6235
83 changed files with 3252 additions and 2487 deletions

86
Kernel/include/fs/fdt.hpp Normal file
View File

@ -0,0 +1,86 @@
/*
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 <fs/node.hpp>
#include <unordered_map>
namespace vfs
{
class FileDescriptorTable
{
public:
struct Fildes
{
enum FildesType
{
FD_INODE,
FD_PIPE,
FD_SOCKET,
} Type;
mode_t Mode = 0;
int Flags = 0;
Node node = nullptr;
int References = 0;
off_t Offset = 0;
int operator==(const Fildes &other)
{
return Type == other.Type &&
Mode == other.Mode &&
Flags == other.Flags &&
node.get() == other.node.get() &&
References == other.References &&
Offset == other.Offset;
}
};
private:
Node fdDir = nullptr;
void *Owner;
int AddFileDescriptor(const char *AbsolutePath, mode_t Mode, int Flags);
int RemoveFileDescriptor(int FileDescriptor);
int GetFreeFileDescriptor();
public:
std::unordered_map<int, Fildes> FileMap;
int GetFlags(int FileDescriptor);
int SetFlags(int FileDescriptor, int Flags);
void Fork(FileDescriptorTable *Parent);
int usr_open(const char *pathname, int flags, mode_t mode);
int usr_creat(const char *pathname, mode_t mode);
ssize_t usr_read(int fd, void *buf, size_t count);
ssize_t usr_write(int fd, const void *buf, size_t count);
ssize_t usr_pread(int fd, void *buf, size_t count, off_t offset);
ssize_t usr_pwrite(int fd, const void *buf, size_t count, off_t offset);
int usr_close(int fd);
off_t usr_lseek(int fd, off_t offset, int whence);
int usr_stat(const char *pathname, kstat *statbuf);
int usr_fstat(int fd, kstat *statbuf);
int usr_lstat(const char *pathname, kstat *statbuf);
int usr_dup(int oldfd);
int usr_dup2(int oldfd, int newfd);
int usr_ioctl(int fd, unsigned long request, void *argp);
FileDescriptorTable(void *Owner);
~FileDescriptorTable() = default;
};
}

View File

@ -0,0 +1,77 @@
/*
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/>.
*/
#ifndef __FENNIX_KERNEL_FILESYSTEM_IOCTL_H__
#define __FENNIX_KERNEL_FILESYSTEM_IOCTL_H__
#include <types.h>
#include <stropts.h>
#include <termios.h>
#define _IOC_NRBITS 8
#define _IOC_TYPEBITS 8
#define _IOC_SIZEBITS 14
#define _IOC_DIRBITS 2
#define _IOC_NRMASK \
((1 << _IOC_NRBITS) - 1)
#define _IOC_TYPEMASK \
((1 << _IOC_TYPEBITS) - 1)
#define _IOC_SIZEMASK \
((1 << _IOC_SIZEBITS) - 1)
#define _IOC_DIRMASK \
((1 << _IOC_DIRBITS) - 1)
#define _IOC_NRSHIFT 0
#define _IOC_TYPESHIFT \
(_IOC_NRSHIFT + _IOC_NRBITS)
#define _IOC_SIZESHIFT \
(_IOC_TYPESHIFT + _IOC_TYPEBITS)
#define _IOC_DIRSHIFT \
(_IOC_SIZESHIFT + _IOC_SIZEBITS)
#define _IOC(dir, type, nr, size) \
(((dir) << _IOC_DIRSHIFT) | \
((type) << _IOC_TYPESHIFT) | \
((nr) << _IOC_NRSHIFT) | \
((size) << _IOC_SIZESHIFT))
#define _IOC_NONE 0U
#define _IOC_WRITE 1U
#define _IOC_READ 2U
#define _IOC_TYPECHECK(t) (sizeof(t))
#define _IO(type, nr) \
_IOC(_IOC_NONE, (type), (nr), 0)
#define _IOR(type, nr, size) \
_IOC(_IOC_READ, (type), (nr), (_IOC_TYPECHECK(size)))
#define _IOW(type, nr, size) \
_IOC(_IOC_WRITE, (type), (nr), (_IOC_TYPECHECK(size)))
#define _IOWR(type, nr, size) \
_IOC(_IOC_READ | _IOC_WRITE, (type), (nr), (_IOC_TYPECHECK(size)))
#define _IOR_BAD(type, nr, size) \
_IOC(_IOC_READ, (type), (nr), sizeof(size))
#define _IOW_BAD(type, nr, size) \
_IOC(_IOC_WRITE, (type), (nr), sizeof(size))
#define _IOWR_BAD(type, nr, size) \
_IOC(_IOC_READ | _IOC_WRITE, (type), (nr), sizeof(size))
#define TIOCGPTN _IOR('T', 0x30, unsigned int)
#define TIOCSPTLCK _IOW('T', 0x31, int)
#endif // !__FENNIX_KERNEL_FILESYSTEM_IOCTL_H__

207
Kernel/include/fs/node.hpp Normal file
View File

@ -0,0 +1,207 @@
/*
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

150
Kernel/include/fs/ramfs.hpp Normal file
View File

@ -0,0 +1,150 @@
/*
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 <fs/vfs.hpp>
#include <memory.hpp>
namespace vfs
{
class RAMFS
{
public:
class InodeBuffer
{
public:
void *Data = nullptr;
size_t DataSize = 0;
void Allocate(size_t size, bool extend = false, bool atEnd = true)
{
if (extend == false)
{
if (Data)
Free();
Data = kmalloc(size);
if (!Data)
throw std::bad_alloc();
DataSize = size;
}
else
{
if (Data == nullptr)
{
Data = kmalloc(size);
if (!Data)
throw std::bad_alloc();
DataSize = size;
}
else
{
size_t newSize = DataSize + size;
void *newData = kmalloc(newSize);
if (!newData)
throw std::bad_alloc();
if (atEnd)
memcpy(newData, Data, DataSize);
else
memcpy(static_cast<char *>(newData) + size, Data, DataSize);
kfree(Data);
Data = newData;
DataSize = newSize;
}
}
}
void Free()
{
if (Data)
{
kfree(Data);
Data = nullptr;
DataSize = 0;
}
}
bool IsAllocated() const
{
return Data != nullptr;
}
InodeBuffer() = default;
~InodeBuffer() { Free(); }
};
class RAMFSInode
{
public:
struct Inode Node;
RAMFSInode *Parent = nullptr;
std::string Name;
kstat Stat{};
mode_t Mode = 0;
InodeBuffer Buffer;
std::string SymLink;
std::vector<RAMFSInode *> Children;
void AddChild(RAMFSInode *child)
{
Children.push_back(child);
child->Parent = this;
}
void RemoveChild(RAMFSInode *child)
{
auto it = std::find(Children.begin(), Children.end(), child);
if (it != Children.end())
{
Children.erase(it);
child->Parent = nullptr;
}
}
RAMFSInode() = default;
~RAMFSInode()
{
for (auto child : Children)
delete child;
}
};
private:
std::unordered_map<ino_t, RAMFSInode *> Files;
public:
dev_t DeviceID = -1;
ino_t NextInode = 0;
std::string RootName;
int Lookup(struct Inode *Parent, const char *Name, struct Inode **Result);
int Create(struct Inode *Parent, const char *Name, mode_t Mode, struct Inode **Result);
ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset);
ssize_t Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset);
ssize_t ReadDir(struct Inode *Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries);
int SymLink(struct Inode *Node, const char *Name, const char *Target, struct Inode **Result);
ssize_t ReadLink(struct Inode *Node, char *Buffer, size_t Size);
int Stat(struct Inode *Node, struct kstat *Stat);
RAMFS() = default;
~RAMFS() = default;
};
}
bool MountAndRootRAMFS(Node Parent, const char *Name, size_t Index);

137
Kernel/include/fs/ustar.hpp Normal file
View File

@ -0,0 +1,137 @@
/*
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/>.
*/
#ifndef __FENNIX_KERNEL_FILESYSTEM_USTAR_H__
#define __FENNIX_KERNEL_FILESYSTEM_USTAR_H__
#include <fs/vfs.hpp>
namespace vfs
{
class USTAR
{
public:
enum TypeFlag
{
AREGTYPE = '\0',
REGTYPE = '0',
LNKTYPE = '1',
SYMTYPE = '2',
CHRTYPE = '3',
BLKTYPE = '4',
DIRTYPE = '5',
FIFOTYPE = '6',
CONTTYPE = '7'
};
enum ModeFlag
{
TSUID = 04000,
TSGID = 02000,
TSVTX = 01000,
TUREAD = 00400,
TUWRITE = 00200,
TUEXEC = 00100,
TGREAD = 00040,
TGWRITE = 00020,
TGEXEC = 00010,
TOREAD = 00004,
TOWRITE = 00002,
TOEXEC = 00001,
};
struct FileHeader
{
char name[100];
char mode[8];
char uid[8];
char gid[8];
char size[12];
char mtime[12];
char chksum[8];
char typeflag[1];
char link[100];
char signature[6];
char version[2];
char owner[32];
char group[32];
char dev_maj[8];
char dev_min[8];
char prefix[155];
char pad[12];
};
constexpr static int INODE_CHECKSUM = 0x7757A4;
struct USTARInode
{
struct Inode Node;
FileHeader *Header;
USTARInode *Parent;
std::string Name;
std::string Path;
std::vector<USTARInode *> Children;
bool Deleted;
int Checksum;
};
private:
std::unordered_map<ino_t, USTARInode *> Files;
inline uint32_t GetSize(const char *String)
{
uint32_t ret = 0;
while (*String)
{
ret *= 8;
ret += *String - '0';
String++;
}
return ret;
}
inline int StringToInt(const char *String)
{
int ret = 0;
for (int i = 0; String[i] != '\0'; ++i)
ret = ret * 10 + String[i] - '0';
return ret;
}
public:
dev_t DeviceID = -1;
ino_t NextInode = 0;
int Lookup(struct Inode *Parent, const char *Name, struct Inode **Result);
int Create(struct Inode *Parent, const char *Name, mode_t Mode, struct Inode **Result);
ssize_t Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset);
ssize_t ReadDir(struct Inode *Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries);
int SymLink(struct Inode *Node, const char *Name, const char *Target, struct Inode **Result);
ssize_t ReadLink(struct Inode *Node, char *Buffer, size_t Size);
int Stat(struct Inode *Node, struct kstat *Stat);
bool TestArchive(uintptr_t Address);
void ReadArchive(uintptr_t Address, size_t Size);
USTAR() = default;
~USTAR() = default;
};
}
bool TestAndInitializeUSTAR(uintptr_t Address, size_t Size, size_t Index);
#endif // !__FENNIX_KERNEL_FILESYSTEM_USTAR_H__

179
Kernel/include/fs/vfs.hpp Normal file
View File

@ -0,0 +1,179 @@
/*
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 <fs/fdt.hpp>
#include <errno.h>
#include <cwalk.h>
#include <atomic>
#include <string>
#include <list>
/* sanity checks */
static_assert(DTTOIF(DT_FIFO) == S_IFIFO);
static_assert(IFTODT(S_IFCHR) == DT_CHR);
namespace vfs
{
class Virtual
{
private:
std::unordered_map<dev_t, Node> Roots;
std::unordered_map<dev_t, FileSystemInfo *> FileSystems;
public:
#pragma region Utilities
inline bool PathIsRelative(const char *Path) { return cwk_path_is_relative(Path); }
inline bool PathIsAbsolute(const char *Path) { return !PathIsRelative(Path); }
inline bool PathIsRelative(std::string &Path) { return cwk_path_is_relative(Path.c_str()); }
inline bool PathIsAbsolute(std::string &Path) { return !PathIsRelative(Path.c_str()); }
eNode Convert(Node node) { return {node, 0}; }
/**
* @brief Converts an Inode to a Node
*
* Result of this function will be empty.
* You are responsible to populate the Node.
*
* @param inode Inode to convert
* @return eNode{Node, errno}
*/
eNode Convert(Inode *inode);
/**
* @brief Converts an Inode to a Node
*
* Result of this function will be populated using the Parent Node.
* This function will automatically assign the FSI, parents and children.
*
* @note Name and Path won't be set.
*
* @param Parent Parent Node
* @param inode Inode to convert
* @return eNode{Node, errno}
*/
eNode Convert(Node &Parent, Inode *inode);
/**
* @brief Normalizes a path
*
* If the path is relative, it will be normalized using the Parent Node.
* Example if Join is true:
* Parent = /home/user/Desktop
* Path = ../Documents
* Result = /home/user/Documents
*
* If Join is false:
* Path = /var/foo/bar/../../
* Result = /var
*
* @param Parent Parent Node
* @param Path Path to normalize
* @param Join If true, the path will be joined with the Parent Node
* @return Normalized Path
*/
std::string NormalizePath(Node &Parent, std::string Path, bool Join = false);
#pragma endregion Utilities
#pragma region Roots
bool RootExists(dev_t Index);
eNode GetRoot(dev_t Index);
ssize_t GetRoot(Node Index);
int AddRoot(dev_t Index, Node Root, bool Replace = false);
int AddRoot(dev_t Index, eNode Root, bool Replace = false) { return AddRoot(Index, Root.Value, Replace); }
#pragma endregion Roots
#pragma region Registrations
dev_t RegisterFileSystem(FileSystemInfo *fsi);
int UnregisterFileSystem(dev_t Device);
#pragma endregion Registrations
#pragma region Node Operations
eNode Lookup(Node &Parent, std::string Path);
eNode Create(Node &Parent, std::string Name, mode_t Mode, bool ErrorIfExists = true);
int Remove(Node &Parent, std::string Name);
int Remove(Node &node);
int Rename(Node &node, std::string NewName);
ssize_t Read(Node &Target, void *Buffer, size_t Size, off_t Offset);
ssize_t Write(Node &Target, const void *Buffer, size_t Size, off_t Offset);
int Truncate(Node &Target, off_t Size);
/**
* @brief Read directory entries
*
* @note This function includes "." and ".."
*
* @param Target
* @param Buffer
* @param Size
* @param Offset
* @param Entries
* @return
*/
ssize_t ReadDirectory(Node &Target, kdirent *Buffer, size_t Size, off_t Offset, off_t Entries);
/**
* @brief Read directory entries
*
* @note This function does NOT include "." and ".."
*
* @param Target
* @return
*/
std::list<Node> ReadDirectory(Node &Target);
eNode CreateLink(Node &Parent, std::string Name, std::string Target);
eNode CreateLink(Node &Parent, std::string Name, Node &Target) { return this->CreateLink(Parent, Name, Target->Path); }
int Stat(Node &Target, kstat *Stat);
int ReadLink(Node &Target, char *Buffer, size_t Size) { return Target->__ReadLink(Buffer, Size); }
off_t Seek(Node &Target, off_t Offset);
int Open(Node &Target, int Flags, mode_t Mode);
int Close(Node &Target);
int Ioctl(Node &Target, unsigned long Request, void *Argp) { return Target->__Ioctl(Request, Argp); }
#pragma endregion Node Operations
#pragma region Mounting
eNode Mount(Node &Parent, Inode *inode, std::string Name, FileSystemInfo *fsi);
int Umount(Node &node);
int Umount(Node &Parent, std::string Name);
#pragma endregion Mounting
void Initialize();
Virtual();
~Virtual();
};
}