Merge remote-tracking branch 'Kernel/master'

This commit is contained in:
EnderIce2
2024-11-20 05:00:33 +02:00
468 changed files with 112800 additions and 1 deletions

226
Kernel/storage/cache.cpp Normal file
View File

@ -0,0 +1,226 @@
/*
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/>.
*/
#include <filesystem.hpp>
#include <convert.h>
#include <printf.h>
#include <rand.hpp>
#include <cwalk.h>
#include "../kernel.h"
namespace vfs
{
FileNode *Virtual::CacheSearchReturnLast(FileNode *Parent, const char **Path)
{
assert(Parent != nullptr);
struct cwk_segment segment;
if (!cwk_path_get_first_segment(*Path, &segment))
{
if (strcmp(*Path, Parent->fsi->RootName) == 0)
return Parent;
ReturnLogError(nullptr, "Failed to get first segment of path");
}
size_t segments = 0;
while (cwk_path_get_next_segment(&segment))
segments++;
if (segments == 0)
return Parent;
const char *tmpPath = *Path;
if (strncmp(tmpPath, "\x06root-", 6) == 0) /* FIXME: deduce the index */
{
tmpPath += 6;
while (*tmpPath != '\0' && *tmpPath != '\x06')
tmpPath++;
if (*tmpPath == '\x06')
tmpPath++;
}
else
tmpPath = *Path;
FileNode *__Parent = Parent;
if (this->PathIsAbsolute(tmpPath))
{
while (__Parent->Parent)
__Parent = __Parent->Parent;
}
cwk_path_get_first_segment(tmpPath, &segment);
do
{
std::string segmentName(segment.begin, segment.size);
bool found = false;
for (FileNode *fn : __Parent->Children)
{
if (fn->Name != segmentName)
continue;
cwk_segment __seg = segment;
assert(cwk_path_get_next_segment(&__seg)); /* There's something wrong */
__Parent = fn;
found = true;
break;
}
if (!found)
{
*Path = segment.begin;
break;
}
} while (cwk_path_get_next_segment(&segment));
return __Parent;
}
FileNode *Virtual::CacheRecursiveSearch(FileNode *Root, const char *NameOrPath, bool IsName)
{
if (Root == nullptr)
return nullptr;
debug("%s cache search for \"%s\" in \"%s\"",
IsName ? "Relative" : "Absolute",
NameOrPath,
Root->Path.c_str());
struct cwk_segment segment;
if (!cwk_path_get_first_segment(NameOrPath, &segment))
{
if (strcmp(NameOrPath, Root->fsi->RootName) == 0)
return Root;
ReturnLogError(nullptr, "Failed to get first segment of path");
}
size_t segments = 0;
while (cwk_path_get_next_segment(&segment))
segments++;
if (IsName && segments == 0)
{
for (FileNode *fn : Root->Children)
{
if (fn->Name == NameOrPath)
return fn;
}
ReturnLogError(nullptr, "Failed to find \"%s\" in \"%s\"", NameOrPath, Root->Path.c_str());
}
const char *path = NameOrPath;
if (strncmp(path, "\x06root-", 6) == 0) /* FIXME: deduce the index */
{
path += 6;
while (*path != '\0' && *path != '\x06')
path++;
if (*path == '\x06')
path++;
}
else
path = NameOrPath;
FileNode *__Parent = Root;
if (this->PathIsAbsolute(path))
{
/* Get the root if Root is not the root 【・_・?】 */
while (__Parent->Parent)
__Parent = __Parent->Parent;
}
cwk_path_get_first_segment(path, &segment);
do
{
std::string segmentName(segment.begin, segment.size);
bool found = false;
for (FileNode *fn : __Parent->Children)
{
if (fn->Name != segmentName)
continue;
cwk_segment __seg = segment;
if (!cwk_path_get_next_segment(&__seg))
return fn;
__Parent = fn;
found = true;
break;
}
if (!found)
break;
} while (cwk_path_get_next_segment(&segment));
debug("Failed to find \"%s\" in \"%s\"", NameOrPath, Root->Path.c_str());
return nullptr;
}
FileNode *Virtual::CacheLookup(const char *Path)
{
debug("Cache lookup for \"%s\"", Path);
FileNode *rootNode = thisProcess ? thisProcess->Info.RootNode : this->GetRoot(0);
FileNode *ret = CacheRecursiveSearch(rootNode, Path, false);
if (ret)
return ret;
debug("Path \"%s\" not found", Path);
return nullptr;
}
FileNode *Virtual::CreateCacheNode(FileNode *Parent, Inode *Node, const char *Name, mode_t Mode)
{
FileNode *fn = new FileNode;
fn->Name = Name;
if (Parent)
{
fn->Path = Parent->Path + "/" + Name;
Parent->Children.push_back(fn);
}
else
fn->Path = Name;
fn->Parent = Parent;
fn->Node = Node;
fn->fsi = DeviceMap[Node->Device].fsi;
if (fn->fsi == nullptr)
warn("Failed to find filesystem for device %d", Node->Device);
debug("Created cache node %s", fn->Path.c_str());
return fn;
}
int Virtual::RemoveCacheNode(FileNode *Node)
{
if (Node == nullptr)
return -EINVAL;
if (Node->Parent)
{
Node->Parent->Children.erase(std::find(Node->Parent->Children.begin(), Node->Parent->Children.end(), Node));
// delete Node;
fixme("Node deletion is disabled for now (for debugging purposes)");
}
return 0;
}
}

View File

@ -0,0 +1,378 @@
/*
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/>.
*/
#include <filesystem.hpp>
#include <convert.h>
#include <stropts.h>
#include <task.hpp>
#include <printf.h>
#include <lock.hpp>
#include <cwalk.h>
#include "../kernel.h"
namespace vfs
{
int FileDescriptorTable::GetFlags(int FileDescriptor)
{
auto it = this->FileMap.find(FileDescriptor);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", FileDescriptor);
return it->second.Flags;
}
int FileDescriptorTable::SetFlags(int FileDescriptor, int Flags)
{
auto it = this->FileMap.find(FileDescriptor);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", FileDescriptor);
it->second.Flags = Flags;
return 0;
}
int FileDescriptorTable::AddFileDescriptor(const char *AbsolutePath,
mode_t Mode, int Flags)
{
Tasking::PCB *pcb = thisProcess;
auto ProbeMode = [](mode_t Mode, int Flags) -> int
{
if (!(Flags & O_CREAT))
return 0;
if (Flags & O_RDONLY)
{
if (!(Mode & S_IRUSR))
{
debug("No read permission (%d)", Mode);
return -EACCES;
}
}
if (Flags & O_WRONLY)
{
if (!(Mode & S_IWUSR))
{
debug("No write permission (%d)", Mode);
return -EACCES;
}
}
if (Flags & O_RDWR)
{
if (!(Mode & S_IRUSR) ||
!(Mode & S_IWUSR))
{
debug("No read/write permission (%d)", Mode);
return -EACCES;
}
}
return 0;
};
if (ProbeMode(Mode, Flags) < 0)
return -EACCES;
fixme("Do not follow symlinks when O_CREAT and O_EXCL are set");
if (Flags & O_CREAT)
{
FileNode *ret = fs->Create(pcb->CWD, AbsolutePath, Mode);
if (Flags & O_EXCL && ret == nullptr)
{
debug("%s: File already exists?, returning EEXIST",
AbsolutePath);
return -EEXIST;
}
}
if (Flags & O_CLOEXEC)
{
fixme("O_CLOEXEC");
}
FileNode *File = fs->GetByPath(AbsolutePath, pcb->CWD);
if (!File)
{
error("Failed to open file %s", AbsolutePath);
return -ENOENT;
}
if (Flags & O_TRUNC)
{
debug("Truncating file %s", AbsolutePath);
File->Truncate(0);
}
Fildes fd{};
if (Flags & O_APPEND)
{
debug("Appending to file %s", AbsolutePath);
struct kstat stat;
File->Stat(&stat);
fd.Offset = File->Seek(stat.Size);
}
fd.Mode = Mode;
fd.Flags = Flags;
fd.Node = File;
int fdn = this->GetFreeFileDescriptor();
if (fdn < 0)
return fdn;
this->FileMap.insert({fdn, fd});
char linkName[64];
snprintf(linkName, 64, "%d", fdn);
assert(fs->CreateLink(linkName, this->fdDir, AbsolutePath) != nullptr);
File->Open(Flags, Mode);
return fdn;
}
int FileDescriptorTable::RemoveFileDescriptor(int FileDescriptor)
{
auto it = this->FileMap.find(FileDescriptor);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", FileDescriptor);
fs->Remove(it->second.Node);
this->FileMap.erase(it);
return 0;
}
int FileDescriptorTable::GetFreeFileDescriptor()
{
Tasking::PCB *pcb = thisProcess;
for (size_t i = 0; i < pcb->SoftLimits.OpenFiles; i++)
{
auto it = this->FileMap.find(i);
if (it == this->FileMap.end())
return i;
}
return -EMFILE;
}
void FileDescriptorTable::Fork(FileDescriptorTable *Parent)
{
this->FileMap = Parent->FileMap;
for (const auto &fd : this->FileMap)
{
if (fd.second.Flags & O_CLOEXEC)
{
debug("O_CLOEXEC flag set, removing fd %d", fd.first);
this->FileMap.erase(fd.first);
}
}
}
int FileDescriptorTable::usr_open(const char *pathname, int flags, mode_t mode)
{
if (pathname == nullptr)
return -EFAULT;
return AddFileDescriptor(pathname, mode, flags);
}
int FileDescriptorTable::usr_creat(const char *pathname, mode_t mode)
{
return usr_open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
}
ssize_t FileDescriptorTable::usr_read(int fd, void *buf, size_t count)
{
auto it = this->FileMap.find(fd);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", fd);
return it->second.Node->Read(buf, count, it->second.Offset);
}
ssize_t FileDescriptorTable::usr_write(int fd, const void *buf, size_t count)
{
auto it = this->FileMap.find(fd);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", fd);
return it->second.Node->Write(buf, count, it->second.Offset);
}
int FileDescriptorTable::usr_close(int fd)
{
auto it = this->FileMap.find(fd);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", fd);
return RemoveFileDescriptor(fd);
}
off_t FileDescriptorTable::usr_lseek(int fd, off_t offset, int whence)
{
auto it = this->FileMap.find(fd);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", fd);
off_t &newOffset = it->second.Offset;
switch (whence)
{
case SEEK_SET:
{
newOffset = it->second.Node->Seek(offset);
break;
}
case SEEK_CUR:
{
newOffset = it->second.Node->Seek(newOffset + offset);
break;
}
case SEEK_END:
{
struct kstat stat
{
};
it->second.Node->Stat(&stat);
newOffset = it->second.Node->Seek(stat.Size + offset);
break;
}
default:
return -EINVAL;
}
return newOffset;
}
int FileDescriptorTable::usr_stat(const char *pathname,
struct kstat *statbuf)
{
FileNode *node = fs->GetByPath(pathname, nullptr);
if (node == nullptr)
ReturnLogError(-ENOENT, "Failed to find %s", pathname);
if (node->IsSymbolicLink())
{
std::unique_ptr<char[]> buffer(new char[1024]);
ssize_t ret = node->ReadLink(buffer.get(), 1024);
if (ret < 0)
return ret;
FileNode *target = fs->GetByPath(buffer.get(), nullptr);
if (target == nullptr)
return -ENOENT;
return target->Stat(statbuf);
}
return node->Stat(statbuf);
}
int FileDescriptorTable::usr_fstat(int fd, struct kstat *statbuf)
{
auto it = this->FileMap.find(fd);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", fd);
vfs::FileDescriptorTable::Fildes &fildes = it->second;
return fildes.Node->Stat(statbuf);
}
int FileDescriptorTable::usr_lstat(const char *pathname,
struct kstat *statbuf)
{
FileNode *node = fs->GetByPath(pathname, nullptr);
if (node == nullptr)
ReturnLogError(-ENOENT, "Failed to find %s", pathname);
return node->Stat(statbuf);
}
int FileDescriptorTable::usr_dup(int oldfd)
{
auto it = this->FileMap.find(oldfd);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", oldfd);
int newfd = this->GetFreeFileDescriptor();
if (newfd < 0)
return -EMFILE;
Fildes new_dfd{};
new_dfd.Node = it->second.Node;
new_dfd.Mode = it->second.Mode;
this->FileMap.insert({newfd, new_dfd});
debug("Duplicated file descriptor %d to %d", oldfd, newfd);
return newfd;
}
int FileDescriptorTable::usr_dup2(int oldfd, int newfd)
{
if (newfd < 0)
ReturnLogError(-EBADF, "Invalid newfd %d", newfd);
auto it = this->FileMap.find(oldfd);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid oldfd %d", oldfd);
if (newfd == oldfd)
return newfd;
/* Even if it's not valid we ignore it. */
this->usr_close(newfd);
Fildes new_dfd{};
new_dfd.Node = it->second.Node;
new_dfd.Mode = it->second.Mode;
this->FileMap.insert({newfd, new_dfd});
debug("Duplicated file descriptor %d to %d", oldfd, newfd);
return newfd;
}
int FileDescriptorTable::usr_ioctl(int fd, unsigned long request, void *argp)
{
auto it = this->FileMap.find(fd);
if (it == this->FileMap.end())
ReturnLogError(-EBADF, "Invalid fd %d", fd);
return it->second.Node->Ioctl(request, argp);
}
FileDescriptorTable::FileDescriptorTable(void *_Owner)
: Owner(_Owner)
{
debug("+ %#lx", this);
mode_t Mode = S_IXOTH | S_IROTH |
S_IXGRP | S_IRGRP |
S_IXUSR | S_IRUSR |
S_IFDIR;
this->fdDir = fs->Create(((Tasking::PCB *)_Owner)->ProcDirectory, "fd", Mode);
}
}

View File

@ -0,0 +1,310 @@
/*
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/>.
*/
#include <filesystem.hpp>
#include <convert.h>
#include <printf.h>
#include <rand.hpp>
#include <cwalk.h>
#include "../kernel.h"
namespace vfs
{
bool Virtual::PathIsRelative(const char *Path)
{
return cwk_path_is_relative(Path);
}
void Virtual::AddRoot(Inode *Root)
{
SmartLock(VirtualLock);
FileSystemRoots->Children.push_back(Root);
}
FileNode *Virtual::GetRoot(size_t Index)
{
assert(Index < FileSystemRoots->Children.size());
auto it = FileRoots.find(Index);
if (it != FileRoots.end())
return it->second;
Inode *rootNode = FileSystemRoots->Children[Index];
char rootName[128]{};
snprintf(rootName, sizeof(rootName), "\x06root-%ld\x06", Index);
FileNode *ret = this->CreateCacheNode(nullptr, rootNode, rootName, 0);
FileRoots.insert({Index, ret});
return ret;
}
FileNode *Virtual::Create(FileNode *Parent, const char *Name, mode_t Mode)
{
FileNode *existingNode = this->GetByPath(Name, Parent);
if (existingNode != nullptr)
ReturnLogError(existingNode, "File %s already exists", Name);
if (Parent == nullptr)
{
assert(thisProcess != nullptr);
Parent = thisProcess->Info.RootNode;
}
auto it = DeviceMap.find(Parent->Node->Device);
if (it == DeviceMap.end())
ReturnLogError(nullptr, "Device %d not found", Parent->Node->Device);
Inode *Node = NULL;
if (it->second.fsi->Ops.Create == NULL)
ReturnLogError(nullptr, "Create not supported for %d", it->first);
int ret = it->second.fsi->Ops.Create(Parent->Node, Name, Mode, &Node);
if (ret < 0)
ReturnLogError(nullptr, "Create for %d failed with %d", it->first, ret);
return this->CreateCacheNode(Parent, Node, Name, Mode);
}
FileNode *Virtual::ForceCreate(FileNode *Parent, const char *Name, mode_t Mode)
{
fixme("ForceCreate: %s", Name);
return this->Create(Parent, Name, Mode);
}
FileNode *Virtual::Mount(FileNode *Parent, Inode *Node, const char *Path)
{
char *path = strdup(Path);
char *lastSlash = strrchr(path, '/');
if (lastSlash == path)
lastSlash++;
*lastSlash = '\0';
FileNode *parentNode = this->GetByPath(path, Parent);
free(path);
lastSlash = strrchr(Path, '/');
lastSlash++;
return this->CreateCacheNode(parentNode, Node, lastSlash, Node->Mode);
}
int Virtual::Unmount(const char *Path)
{
FileNode *node = this->GetByPath(Path, nullptr);
if (node == nullptr)
ReturnLogError(-ENOENT, "Path %s not found", Path);
return this->RemoveCacheNode(node);
}
FileNode *Virtual::GetByPath(const char *Path, FileNode *Parent)
{
debug("GetByPath: %s", Path);
if (Parent == nullptr || this->PathIsAbsolute(Path))
Parent = thisProcess ? thisProcess->Info.RootNode : this->GetRoot(0);
if (strcmp(Path, ".") == 0)
return Parent;
if (strcmp(Path, "..") == 0)
return Parent->Parent ? Parent->Parent : Parent;
FileNode *fn = this->CacheRecursiveSearch(Parent, Path, this->PathIsRelative(Path));
if (fn)
return fn;
if (strncmp(Path, "\x06root-", 6) == 0) /* FIXME: deduce the index */
{
Path += 7;
while (*Path != '\0' && *Path != '\x06')
Path++;
if (*Path == '\x06')
Path++;
}
FileNode *__Parent = CacheSearchReturnLast(Parent, &Path);
struct cwk_segment segment;
if (!cwk_path_get_first_segment(Path, &segment))
{
auto it = DeviceMap.find(Parent->Node->Device);
if (unlikely(it == DeviceMap.end()))
ReturnLogError(nullptr, "Device %d not found", Parent->Node->Device);
if (it->second.fsi->Ops.Lookup == NULL)
ReturnLogError(nullptr, "Lookup not supported for %d", it->first);
Inode *Node = NULL;
int ret = it->second.fsi->Ops.Lookup(Parent->Node, Path, &Node);
if (ret < 0)
ReturnLogError(nullptr, "Lookup for \"%s\"(%d) failed with %d", Path, it->first, ret);
if (Parent->Node == Node) /* root / */
{
debug("Returning root (%#lx)", Node);
return Parent;
}
ReturnLogError(nullptr, "Path has no segments");
}
Inode *Node = NULL;
do
{
auto it = DeviceMap.find(__Parent->Node->Device);
if (unlikely(it == DeviceMap.end()))
ReturnLogError(nullptr, "Device %d not found", __Parent->Node->Device);
if (it->second.fsi->Ops.Lookup == NULL)
ReturnLogError(nullptr, "Lookup not supported for %d", it->first);
std::string segmentName(segment.begin, segment.size);
int ret = it->second.fsi->Ops.Lookup(__Parent->Node, segmentName.c_str(), &Node);
if (ret < 0)
ReturnLogError(nullptr, "Lookup for \"%s\"(%d) failed with %d", segmentName.c_str(), it->first, ret);
__Parent = this->CreateCacheNode(__Parent, Node, segmentName.c_str(), 0);
} while (cwk_path_get_next_segment(&segment));
FileNode *ret = __Parent;
if (!ret->IsDirectory())
return ret;
auto it = DeviceMap.find(__Parent->Node->Device);
if (unlikely(it == DeviceMap.end()))
ReturnLogError(nullptr, "Device %d not found", __Parent->Node->Device);
size_t dirAllocLen = sizeof(struct kdirent) + strlen(Path);
struct kdirent *dirent = (struct kdirent *)malloc(dirAllocLen);
size_t offset = 2; /* Skip . and .. */
while (it->second.fsi->Ops.ReadDir(Node, dirent, dirAllocLen, offset++, 1) > 0)
{
Inode *ChildNode = NULL;
int luRet = it->second.fsi->Ops.Lookup(Node, dirent->d_name, &ChildNode);
if (luRet < 0)
{
debug("Lookup for %d failed with %d", it->first, luRet);
break;
}
this->CreateCacheNode(ret, ChildNode, dirent->d_name, 0);
}
free(dirent);
return ret;
}
std::string Virtual::GetByNode(FileNode *Node)
{
assert(Node != nullptr);
if (Node->Parent == nullptr)
{
if (Node->Node->Flags & I_FLAG_ROOT)
return Node->fsi->RootName;
assert(Node->Parent != nullptr);
}
std::string path;
auto appendPath = [&path](const char *name)
{
if (path.size() > 0)
path += "/";
path += name;
};
FileNode *current = Node;
while (current->Parent != nullptr)
{
appendPath(current->Name.c_str());
current = current->Parent;
}
return path;
}
FileNode *Virtual::CreateLink(const char *Path, FileNode *Parent, const char *Target)
{
auto it = DeviceMap.find(Parent->Node->Device);
if (it == DeviceMap.end())
ReturnLogError(nullptr, "Device %d not found", Parent->Node->Device);
Inode *Node = NULL;
if (it->second.fsi->Ops.SymLink == NULL)
ReturnLogError(nullptr, "SymLink not supported for %d", it->first);
int ret = it->second.fsi->Ops.SymLink(Parent->Node, Path, Target, &Node);
if (ret < 0)
ReturnLogError(nullptr, "SymLink for %d failed with %d", it->first, ret);
return this->CreateCacheNode(Parent, Node, Path, 0);
}
FileNode *Virtual::CreateLink(const char *Path, FileNode *Parent, FileNode *Target)
{
return this->CreateLink(Path, Parent, Target->Path.c_str());
}
bool Virtual::PathExists(const char *Path, FileNode *Parent)
{
FileNode *fn = this->CacheLookup(Path);
if (fn)
return true;
FileNode *Node = this->GetByPath(Path, Parent);
if (Node)
return true;
return false;
}
int Virtual::Remove(FileNode *Node)
{
auto it = DeviceMap.find(Node->Node->Device);
if (it == DeviceMap.end())
ReturnLogError(-ENODEV, "Device %d not found", Node->Node->Device);
if (it->second.fsi->Ops.Remove == NULL)
ReturnLogError(-ENOTSUP, "Remove not supported for %d", it->first);
int ret = it->second.fsi->Ops.Remove(Node->Parent->Node, Node->Name.c_str());
if (ret < 0)
ReturnLogError(ret, "Remove for %d failed with %d", it->first, ret);
this->RemoveCacheNode(Node);
return 0;
}
}
std::string FileNode::GetName()
{
return this->Name;
}
std::string FileNode::GetPath()
{
const char *path = this->Path.c_str();
if (strncmp(path, "\x06root-", 6) == 0) /* FIXME: deduce the index */
{
path += 6;
while (*path != '\0' && *path != '\x06')
path++;
if (*path == '\x06')
path++;
}
else
return this->Path;
if (path[0] == '\0')
return std::string(this->fsi->RootName);
return std::string(path);
}

868
Kernel/storage/fs/ustar.cpp Normal file
View File

@ -0,0 +1,868 @@
/*
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/>.
*/
#include <filesystem/ustar.hpp>
#include <memory.hpp>
#include <functional>
#include <debug.h>
#include "../../kernel.h"
#define TMAGIC "ustar"
#define TMAGLEN 6
#define TVERSION "00"
#define TVERSLEN 2
namespace vfs
{
int USTAR::Lookup(struct Inode *_Parent, const char *Name, struct Inode **Result)
{
auto Parent = (USTARInode *)_Parent;
const char *basename;
size_t length;
cwk_path_get_basename(Name, &basename, &length);
if (basename == NULL)
{
if (strcmp(Name, "/") == 0)
{
auto &it = Files.at(0);
*Result = &it->Node;
return 0;
}
error("Invalid name %s", Name);
return -EINVAL;
}
if (_Parent)
{
for (const auto &child : Parent->Children)
{
if (child->Deleted || strcmp(child->Name.c_str(), basename) != 0)
continue;
*Result = &child->Node;
return 0;
}
return -ENOENT;
}
auto fileItr = Files.begin();
while (fileItr != Files.end())
{
USTARInode *node = fileItr->second;
if (node->Deleted || strcmp(node->Name.c_str(), basename) != 0)
{
fileItr++;
continue;
}
*Result = &fileItr->second->Node;
return 0;
}
return -ENOENT;
}
int USTAR::Create(struct Inode *_Parent, const char *Name, mode_t Mode, struct Inode **Result)
{
USTARInode *Parent = (USTARInode *)_Parent;
Inode inode{};
inode.Mode = Mode;
inode.Device = this->DeviceID;
inode.RawDevice = 0;
inode.Index = NextInode;
inode.Offset = 0;
inode.PrivateData = this;
inode.Flags = I_FLAG_CACHE_KEEP;
const char *basename;
size_t length;
cwk_path_get_basename(Name, &basename, &length);
auto SetMode = [&](mode_t &Mode, FileHeader *header)
{
if (Mode & S_IFREG)
header->typeflag[0] = REGTYPE;
else if (Mode & S_IFLNK)
header->typeflag[0] = SYMTYPE;
else if (Mode & S_IFCHR)
header->typeflag[0] = CHRTYPE;
else if (Mode & S_IFBLK)
header->typeflag[0] = BLKTYPE;
else if (Mode & S_IFDIR)
header->typeflag[0] = DIRTYPE;
else if (Mode & S_IFIFO)
header->typeflag[0] = FIFOTYPE;
mode_t final = 0;
if (Mode & S_ISUID)
final |= TSUID;
else if (Mode & S_ISGID)
final |= TSGID;
else if (Mode & S_ISVTX)
final |= TSVTX;
else if (Mode & S_IRUSR)
final |= TUREAD;
else if (Mode & S_IWUSR)
final |= TUWRITE;
else if (Mode & S_IXUSR)
final |= TUEXEC;
else if (Mode & S_IRGRP)
final |= TGREAD;
else if (Mode & S_IWGRP)
final |= TGWRITE;
else if (Mode & S_IXGRP)
final |= TGEXEC;
else if (Mode & S_IROTH)
final |= TOREAD;
else if (Mode & S_IWOTH)
final |= TOWRITE;
else if (Mode & S_IXOTH)
final |= TOEXEC;
snprintf(header->mode, sizeof(header->mode), "%07o", final);
};
FileHeader *hdr = new FileHeader{};
SetMode(inode.Mode, hdr);
strncpy(hdr->name, basename, sizeof(hdr->name));
strncpy(hdr->signature, TMAGIC, TMAGLEN);
strncpy(hdr->version, TVERSION, TVERSLEN);
USTARInode *node = new USTARInode{.Node = inode,
.Header = hdr,
.Parent = Parent,
.Name{},
.Path{},
.Children{},
.Deleted = false,
.Checksum = INODE_CHECKSUM};
node->Name.assign(basename, length);
node->Path.assign(Name, strlen(Name));
Files.insert(std::make_pair(NextInode, node));
*Result = &Files.at(NextInode)->Node;
if (Parent)
Parent->Children.push_back(Files.at(NextInode));
NextInode++;
return 0;
}
ssize_t USTAR::Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
{
auto fileItr = Files.find(Node->Index);
assert(fileItr != Files.end());
if (fileItr->second->Deleted)
return -ENOENT;
USTARInode *node = fileItr->second;
size_t fileSize = GetSize(node->Header->size);
if (Size <= 0)
{
debug("Size is less than or equal to 0");
Size = fileSize;
}
if ((size_t)Offset > fileSize)
{
debug("Offset %d is greater than file size %d", Offset, fileSize);
return 0;
}
if ((fileSize - Offset) == 0)
{
debug("Offset %d is equal to file size %d", Offset, fileSize);
return 0; /* EOF */
}
if ((size_t)Offset + Size > fileSize)
{
debug("Offset %d + Size %d is greater than file size %d",
Offset, Size, fileSize);
Size = fileSize;
}
memcpy(Buffer, (uint8_t *)((uintptr_t)node->Header + sizeof(FileHeader) + Offset), Size);
// debug("Read %d bytes from %d[%d]", Size, Node->Index, Offset);
return Size;
}
__no_sanitize("alignment")
ssize_t USTAR::ReadDir(struct Inode *_Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries)
{
/* FIXME: FIX ALIGNMENT FOR DIRENT! */
auto Node = (USTARInode *)_Node;
off_t realOffset = Offset;
size_t totalSize = 0;
uint16_t reclen = 0;
struct kdirent *ent = nullptr;
if (Offset == 0)
{
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(".") + 1);
if (totalSize + reclen >= Size)
return -EINVAL;
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
ent->d_ino = Node->Node.Index;
ent->d_off = Offset++;
ent->d_reclen = reclen;
ent->d_type = DT_DIR;
strcpy(ent->d_name, ".");
totalSize += reclen;
}
if (Offset <= 1)
{
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen("..") + 1);
if (totalSize + reclen >= Size)
{
if (realOffset == 1)
return -EINVAL;
return totalSize;
}
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
if (Node->Parent)
ent->d_ino = Node->Parent->Node.Index;
else
{
warn("Parent is null for %s", Node->Name.c_str());
ent->d_ino = Node->Node.Index;
}
ent->d_off = Offset++;
ent->d_reclen = reclen;
ent->d_type = DT_DIR;
strcpy(ent->d_name, "..");
totalSize += reclen;
}
// off_t entriesSkipped = 0;
// auto fileItr = Files.begin();
// while (fileItr != Files.end())
// {
// if (fileItr->second->Deleted)
// continue;
// reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(fileItr->second->Name.c_str()) + 1);
// if (Offset > entriesSkipped)
// {
// entriesSkipped++;
// continue;
// }
// if (totalSize + reclen >= Size)
// break;
// ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
// ent->d_ino = fileItr->first;
// ent->d_off = Offset++;
// ent->d_reclen = reclen;
// ent->d_type = IFTODT(StringToInt(fileItr->second->Header->mode));
// strncpy(ent->d_name,
// fileItr->second->Name.c_str(),
// strlen(fileItr->second->Name.c_str()));
// totalSize += reclen;
// fileItr++;
// }
if (!S_ISDIR(Node->Node.Mode))
return -ENOTDIR;
if ((Offset >= 2 ? (Offset - 2) : Offset) > (off_t)Node->Children.size())
return -EINVAL;
off_t entries = 0;
for (const auto &var : Node->Children)
{
if (var->Node.Offset < Offset)
continue;
if (entries >= Entries)
break;
if (var->Deleted)
continue;
reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen(var->Name.c_str()) + 1);
if (totalSize + reclen >= Size)
break;
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
ent->d_ino = var->Node.Index;
ent->d_off = var->Node.Offset;
ent->d_reclen = reclen;
switch (var->Header->typeflag[0])
{
case AREGTYPE:
case REGTYPE:
ent->d_type = DT_REG;
break;
case LNKTYPE:
fixme("Hard link not implemented for %s", var->Header->name);
ent->d_type = DT_LNK;
break;
case SYMTYPE:
ent->d_type = DT_LNK;
break;
case CHRTYPE:
ent->d_type = DT_CHR;
break;
case BLKTYPE:
ent->d_type = DT_BLK;
break;
case DIRTYPE:
ent->d_type = DT_DIR;
break;
case FIFOTYPE:
ent->d_type = DT_FIFO;
break;
case CONTTYPE:
default:
ent->d_type = 0;
break;
}
strncpy(ent->d_name, var->Name.c_str(), strlen(var->Name.c_str()));
totalSize += reclen;
entries++;
}
if (totalSize + sizeof(struct kdirent) >= Size)
return totalSize;
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
ent->d_ino = 0;
ent->d_off = 0;
ent->d_reclen = 0;
ent->d_type = DT_UNKNOWN;
ent->d_name[0] = '\0';
return totalSize;
}
int USTAR::SymLink(struct Inode *Node, const char *Name, const char *Target, struct Inode **Result)
{
int ret = this->Create(Node, Name, S_IFLNK, Result);
if (ret < 0)
return ret;
USTARInode *node = (USTARInode *)*Result;
FileHeader *hdr = node->Header;
strncpy(hdr->link, Target, MIN(sizeof(hdr->link) - 1, strlen(Target)));
return 0;
}
ssize_t USTAR::ReadLink(struct Inode *Node, char *Buffer, size_t Size)
{
auto fileItr = Files.find(Node->Index);
assert(fileItr != Files.end());
if (fileItr->second->Deleted)
return -ENOENT;
USTARInode *node = fileItr->second;
if (strlen(node->Header->link) > Size)
Size = strlen(node->Header->link);
strncpy(Buffer, node->Header->link, Size);
debug("Read %d bytes from %d", Size, Node->Index);
return Size;
}
int USTAR::Stat(struct Inode *Node, struct kstat *Stat)
{
auto fileItr = Files.find(Node->Index);
assert(fileItr != Files.end());
if (fileItr->second->Deleted)
return -ENOENT;
USTARInode *node = fileItr->second;
size_t fileSize = GetSize(node->Header->size);
debug("Header: \"%.*s\"", sizeof(struct FileHeader), node->Header);
Stat->Device = this->DeviceID;
Stat->Index = Node->Index;
Stat->HardLinks = 1;
Stat->UserID = GetSize(node->Header->uid);
Stat->GroupID = GetSize(node->Header->gid);
Stat->RawDevice = Stat->MakeDevice(GetSize(node->Header->dev_maj), GetSize(node->Header->dev_min));
Stat->Size = fileSize;
Stat->AccessTime = GetSize(node->Header->mtime);
Stat->ModifyTime = GetSize(node->Header->mtime);
Stat->ChangeTime = GetSize(node->Header->mtime);
Stat->BlockSize = 512;
Stat->Blocks = (fileSize + 511) / 512;
Stat->Attribute = 0;
mode_t hdrMode = StringToInt(node->Header->mode);
if (hdrMode & TSUID)
Stat->Mode |= S_ISUID;
else if (hdrMode & TSGID)
Stat->Mode |= S_ISGID;
else if (hdrMode & TSVTX)
Stat->Mode |= S_ISVTX;
else if (hdrMode & TUREAD)
Stat->Mode |= S_IRUSR;
else if (hdrMode & TUWRITE)
Stat->Mode |= S_IWUSR;
else if (hdrMode & TUEXEC)
Stat->Mode |= S_IXUSR;
else if (hdrMode & TGREAD)
Stat->Mode |= S_IRGRP;
else if (hdrMode & TGWRITE)
Stat->Mode |= S_IWGRP;
else if (hdrMode & TGEXEC)
Stat->Mode |= S_IXGRP;
else if (hdrMode & TOREAD)
Stat->Mode |= S_IROTH;
else if (hdrMode & TOWRITE)
Stat->Mode |= S_IWOTH;
else if (hdrMode & TOEXEC)
Stat->Mode |= S_IXOTH;
switch (node->Header->typeflag[0])
{
case AREGTYPE:
case REGTYPE:
Stat->Mode |= S_IFREG;
break;
case LNKTYPE:
fixme("Hard link not implemented for %s", node->Header->name);
Stat->Mode |= S_IFLNK;
break;
case SYMTYPE:
Stat->Mode |= S_IFLNK;
break;
case CHRTYPE:
Stat->Mode |= S_IFCHR;
break;
case BLKTYPE:
Stat->Mode |= S_IFBLK;
break;
case DIRTYPE:
Stat->Mode |= S_IFDIR;
break;
case FIFOTYPE:
Stat->Mode |= S_IFIFO;
break;
case CONTTYPE:
warn("Reserved type for %s", node->Header->name);
__fallthrough;
default:
error("Unknown type: %d for %s", node->Header->typeflag[0], node->Header->name);
break;
}
return 0;
}
bool USTAR::TestArchive(uintptr_t Address)
{
if (!Memory::Virtual().Check((void *)Address))
{
error("Address %#lx is not mapped!", Address);
return false;
}
FileHeader *header = (FileHeader *)Address;
if (strncmp(header->signature, TMAGIC, TMAGLEN) != 0)
{
error("Invalid signature!");
return false;
}
return true;
}
void USTAR::ReadArchive(uintptr_t Address, size_t Size)
{
trace("Initializing USTAR with address %#lx and size %d", Address, Size);
auto SetMode = [&](Inode &uNode, FileHeader *header)
{
mode_t hdrMode = StringToInt(header->mode);
if (hdrMode & TSUID)
uNode.Mode |= S_ISUID;
else if (hdrMode & TSGID)
uNode.Mode |= S_ISGID;
else if (hdrMode & TSVTX)
uNode.Mode |= S_ISVTX;
else if (hdrMode & TUREAD)
uNode.Mode |= S_IRUSR;
else if (hdrMode & TUWRITE)
uNode.Mode |= S_IWUSR;
else if (hdrMode & TUEXEC)
uNode.Mode |= S_IXUSR;
else if (hdrMode & TGREAD)
uNode.Mode |= S_IRGRP;
else if (hdrMode & TGWRITE)
uNode.Mode |= S_IWGRP;
else if (hdrMode & TGEXEC)
uNode.Mode |= S_IXGRP;
else if (hdrMode & TOREAD)
uNode.Mode |= S_IROTH;
else if (hdrMode & TOWRITE)
uNode.Mode |= S_IWOTH;
else if (hdrMode & TOEXEC)
uNode.Mode |= S_IXOTH;
switch (header->typeflag[0])
{
case AREGTYPE:
case REGTYPE:
uNode.Mode |= S_IFREG;
break;
case LNKTYPE:
uNode.Mode |= S_IFLNK;
break;
case SYMTYPE:
uNode.Mode |= S_IFLNK;
break;
case CHRTYPE:
uNode.Mode |= S_IFCHR;
break;
case BLKTYPE:
uNode.Mode |= S_IFBLK;
break;
case DIRTYPE:
uNode.Mode |= S_IFDIR;
break;
case FIFOTYPE:
uNode.Mode |= S_IFIFO;
break;
case CONTTYPE:
warn("Reserved type for %s", header->name);
__fallthrough;
default:
error("Unknown type: %d for %s", header->typeflag[0], header->name);
break;
}
};
FileHeader *header = (FileHeader *)Address;
debug("USTAR signature valid! Name:%s Signature:%s Mode:%d Size:%lu",
header->name, header->signature, StringToInt(header->mode), header->size);
Memory::Virtual vmm;
std::vector<USTARInode *> tmpNodes; /* FIXME: bug in unordered_map for iterators */
for (size_t i = 0;; i++)
{
if (!vmm.Check((void *)header))
{
error("Address %#lx is not mapped!", header);
return;
}
if (strncmp(header->signature, TMAGIC, TMAGLEN) != 0)
break;
// debug("\"%s\"", header->name);
/* This removes the "." at the beginning of the file name
"./foo/bar" > "/foo/bar" */
if (header->name[0] == '.' && header->name[1] == '/')
memmove(header->name, header->name + 1, strlen(header->name));
if (isempty((char *)header->name))
fixme("Ignoring empty file name \"%.*s\"", sizeof(struct FileHeader), header);
struct Inode uNode;
uNode.Device = this->DeviceID;
uNode.RawDevice = 0;
uNode.Index = NextInode;
SetMode(uNode, header);
uNode.Flags = I_FLAG_CACHE_KEEP;
uNode.Offset = 0;
uNode.PrivateData = this;
const char *basename;
size_t length;
cwk_path_get_basename(header->name, &basename, &length);
USTARInode *node = new USTARInode{.Node = uNode,
.Header = header,
.Parent = nullptr,
.Name{},
.Path{},
.Children{},
.Deleted = false,
.Checksum = INODE_CHECKSUM};
if (basename)
node->Name.assign(basename, length);
else
node->Name.assign((const char *)header->name, strlen(header->name));
node->Path.assign((const char *)header->name, strlen(header->name));
Files.insert(std::make_pair(NextInode, node));
tmpNodes.push_back(node);
size_t size = GetSize(header->size);
Address += ((size / 512) + 1) * 512;
if (size % 512)
Address += 512;
header = (FileHeader *)Address;
NextInode++;
}
/* TODO: This code can be significantly optimized but good luck understanding it */
USTARInode *parent = nullptr;
std::vector<USTARInode *> parentStack;
std::vector<std::string *> pathStack;
for (auto &file : tmpNodes)
{
if (file->Path == "/") /* This is root / */
{
parentStack.push_back(file);
pathStack.push_back(&file->Path);
// debug("root / generated");
continue;
}
/* pathStack is never empty */
if (file->Path.back() == '/') /* This is a directory */
{
const char *path = file->Path.c_str();
size_t length;
/* This converts /one/two/path.txt to /one/two/ */
cwk_path_get_dirname(path, &length);
std::string dirName(path, length);
/* Check if the directory is at the same level as the current directory */
if (dirName == *pathStack.back())
{
parent = parentStack.back();
parentStack.push_back(file);
pathStack.push_back(&file->Path);
parent->Children.push_back(file);
file->Parent = parent;
// debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str());
continue;
}
else
{
/* Check if the directory is at a higher level */
for (size_t i = 0; i < parentStack.size(); i++)
{
if (dirName != *pathStack[i])
continue;
/* Adjust vectors */
while (!parentStack.empty())
{
if (dirName == *pathStack.back())
break;
// debug("popping \"%s\"", pathStack.back()->c_str());
parentStack.pop_back();
pathStack.pop_back();
}
parent = parentStack.back();
parentStack.push_back(file);
pathStack.push_back(&file->Path);
parent->Children.push_back(file);
file->Parent = parent;
// debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str());
goto foundEnd;
}
// This is a new directory level
parentStack.pop_back();
pathStack.pop_back();
parent = parentStack.back();
parentStack.push_back(file);
pathStack.push_back(&file->Path);
parent->Children.push_back(file);
file->Parent = parent;
// debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str());
foundEnd:
continue;
}
}
/* From here, it's a file */
const char *path = file->Path.c_str();
size_t length;
/* This converts /one/two/path.txt to /one/two/ */
cwk_path_get_dirname(path, &length);
std::string dirName(path, length);
/* Check if the file is at the same level as the current directory */
if (dirName == *pathStack.back())
{
parent = parentStack.back();
parent->Children.push_back(file);
file->Parent = parent;
// debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str());
continue;
}
/* Check if the file is at a higher level */
for (size_t i = 0; i < parentStack.size(); i++)
{
if (dirName != *pathStack[i])
continue;
/* Adjust vectors */
while (!parentStack.empty())
{
if (dirName == *pathStack.back())
break;
// debug("popping \"%s\"", pathStack.back()->c_str());
parentStack.pop_back();
pathStack.pop_back();
}
parent = parentStack.back();
parent->Children.push_back(file);
file->Parent = parent;
// debug("adding \"%s\" to \"%s\"", file->Path.c_str(), parent->Path.c_str());
}
}
std::function<void(vfs::USTAR::USTARInode *, int, const std::string &)> ustarTree = [&](vfs::USTAR::USTARInode *node, int level, const std::string &prefix)
{
debug("%*s\"%s\"%ld", level * 4, prefix.c_str(), node->Name.c_str(), node->Node.Offset);
off_t offset = 2; /* 0 . | 1 .. */
for (auto &child : node->Children)
{
#ifdef DEBUG
if (offset <= 2)
{
if (offset == 2)
offset = 0;
/* Pseudo directories . and .. */
USTARInode pseudoDot{};
pseudoDot.Node = node->Node;
pseudoDot.Name = ".";
pseudoDot.Node.Offset = offset++;
USTARInode pseudoDDot{};
pseudoDDot.Node = node->Parent ? node->Parent->Node : node->Node;
pseudoDDot.Name = "..";
pseudoDDot.Node.Offset = offset++;
ustarTree(&pseudoDot, level + 1, "|-- ");
ustarTree(&pseudoDDot, level + 1, "|-- ");
}
#endif
child->Node.Offset = offset++;
ustarTree(child, level + 1, child == node->Children.back() ? "`-- " : "|-- ");
}
};
ustarTree(tmpNodes[0], 0, "");
}
}
O2 int __ustar_Lookup(struct Inode *Parent, const char *Name, struct Inode **Result)
{
return ((vfs::USTAR *)Parent->PrivateData)->Lookup(Parent, Name, Result);
}
O2 int __ustar_Create(struct Inode *Parent, const char *Name, mode_t Mode, struct Inode **Result)
{
return ((vfs::USTAR *)Parent->PrivateData)->Create(Parent, Name, Mode, Result);
}
O2 ssize_t __ustar_Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
{
return ((vfs::USTAR *)Node->PrivateData)->Read(Node, Buffer, Size, Offset);
}
O2 ssize_t __ustar_Readdir(struct Inode *Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries)
{
return ((vfs::USTAR *)Node->PrivateData)->ReadDir(Node, Buffer, Size, Offset, Entries);
}
O2 int __ustar_SymLink(Inode *Parent, const char *Name, const char *Target, Inode **Result)
{
return ((vfs::USTAR *)Parent->PrivateData)->SymLink(Parent, Name, Target, Result);
}
O2 ssize_t __ustar_ReadLink(Inode *Node, char *Buffer, size_t Size)
{
return ((vfs::USTAR *)Node->PrivateData)->ReadLink(Node, Buffer, Size);
}
O2 int __ustar_Stat(struct Inode *Node, kstat *Stat)
{
return ((vfs::USTAR *)Node->PrivateData)->Stat(Node, Stat);
}
int __ustar_DestroyInode(FileSystemInfo *Info, Inode *Node)
{
((vfs::USTAR::USTARInode *)Node)->Deleted = true;
return 0;
}
int __ustar_Destroy(FileSystemInfo *fsi)
{
assert(fsi->PrivateData);
delete (vfs::USTAR *)fsi->PrivateData;
delete fsi;
return 0;
}
bool TestAndInitializeUSTAR(uintptr_t Address, size_t Size)
{
vfs::USTAR *ustar = new vfs::USTAR();
if (!ustar->TestArchive(Address))
{
delete ustar;
return false;
}
ustar->DeviceID = fs->EarlyReserveDevice();
ustar->ReadArchive(Address, Size);
Inode *initrd = nullptr;
ustar->Lookup(nullptr, "/", &initrd);
assert(initrd != nullptr);
FileSystemInfo *fsi = new FileSystemInfo;
fsi->Name = "ustar";
fsi->RootName = "/";
fsi->Flags = I_FLAG_ROOT | I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
fsi->SuperOps.DeleteInode = __ustar_DestroyInode;
fsi->SuperOps.Destroy = __ustar_Destroy;
fsi->Ops.Lookup = __ustar_Lookup;
fsi->Ops.Create = __ustar_Create;
fsi->Ops.Read = __ustar_Read;
fsi->Ops.ReadDir = __ustar_Readdir;
fsi->Ops.SymLink = __ustar_SymLink;
fsi->Ops.ReadLink = __ustar_ReadLink;
fsi->Ops.Stat = __ustar_Stat;
fsi->PrivateData = ustar;
fs->LateRegisterFileSystem(ustar->DeviceID, fsi, initrd);
fs->AddRoot(initrd);
return true;
}

257
Kernel/storage/virtual.cpp Normal file
View File

@ -0,0 +1,257 @@
/*
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/>.
*/
#include <filesystem.hpp>
#include <convert.h>
#include <printf.h>
#include <cwalk.h>
#include "../kernel.h"
namespace vfs
{
/* maj = 0
min:
0 - <ROOT>
1 - /proc/self
...
*/
int __vfs_Lookup(struct Inode *_Parent, const char *Name, struct Inode **Result)
{
vfsInode *Parent = (vfsInode *)_Parent;
if (!S_ISDIR(Parent->Node.Mode))
return -ENOTDIR;
assert(Parent->Node.Flags & I_FLAG_MOUNTPOINT);
if (Parent->Children.empty())
return -ENOENT;
off_t offset = 0;
for (const auto &Root : Parent->Children)
{
char rootName[128]{};
snprintf(rootName, sizeof(rootName), "\x06root-%ld\x06", offset);
if (strcmp(rootName, Name) == 0)
{
*Result = Root;
return 0;
}
offset++;
}
return -ENOENT;
}
int __vfs_Create(struct Inode *Parent, const char *Name, mode_t Mode, struct Inode **Result)
{
assert(Parent != nullptr);
assert(!"Not implemented");
}
/* This implementation is used internally by the kernel, so no "." & ".." */
__no_sanitize("alignment")
ssize_t __vfs_Readdir(struct Inode *_Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries)
{
if (_Node->GetMinor() != 0)
{
debug("_Node->GetMinor() != 0");
return -ENOENT;
}
assert(_Node->Flags & I_FLAG_MOUNTPOINT);
fixme("maybe wrong implementation of readdir");
size_t totalSize = 0;
off_t entriesSkipped = 0;
struct kdirent *ent = nullptr;
vfsInode *Node = (vfsInode *)_Node;
off_t entries = 0;
foreach (const auto &Root in Node->Children)
{
if (entries >= Entries)
break;
uint16_t reclen = (uint16_t)(offsetof(struct kdirent, d_name) + strlen("root") + 1);
if (Offset > entriesSkipped)
{
entriesSkipped++;
continue;
}
if (totalSize + reclen >= Size)
break;
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
ent->d_ino = Root->Index;
ent->d_off = Root->Offset;
ent->d_reclen = reclen;
ent->d_type = IFTODT(Root->Mode);
strncpy(ent->d_name, "root", strlen("root"));
totalSize += reclen;
entries++;
}
if (ent)
ent->d_off = INT32_MAX;
if (totalSize + sizeof(struct kdirent) >= Size)
return totalSize;
ent = (struct kdirent *)((uintptr_t)Buffer + totalSize);
ent->d_ino = 0;
ent->d_off = 0;
ent->d_reclen = 0;
ent->d_type = DT_UNKNOWN;
ent->d_name[0] = '\0';
return totalSize;
}
ssize_t __vfs_ReadLink(struct Inode *Node, char *Buffer, size_t Size)
{
switch (Node->GetMinor())
{
case 1:
{
/* FIXME: https://github.com/torvalds/linux/blob/c942a0cd3603e34dd2d7237e064d9318cb7f9654/fs/proc/self.c#L11
https://lxr.linux.no/#linux+v3.2.9/fs/proc/base.c#L2482 */
int ret = snprintf(Buffer, Size, "/proc/%d", thisProcess->ID);
debug("ReadLink: %s (%d bytes)", Buffer, ret);
return ret;
}
default:
return -ENOENT;
}
}
void Virtual::Initialize()
{
SmartLock(VirtualLock);
trace("Initializing virtual file system...");
uint32_t iFlags = I_FLAG_CACHE_KEEP;
/* d rwx rwx rwx */
mode_t mode = S_IRWXU |
S_IRWXG |
S_IRWXO |
S_IFDIR;
FileNode *proc = this->ForceCreate(this->GetRoot(0), "proc", mode);
FileNode *var = this->ForceCreate(this->GetRoot(0), "var", mode);
FileNode *log = this->ForceCreate(var, "log", mode);
proc->Node->Flags = iFlags;
log->Node->Flags = iFlags;
/* l rwx rwx rwx */
mode = S_IRWXU |
S_IRWXG |
S_IRWXO |
S_IFLNK;
FileNode *self = this->ForceCreate(proc, "self", mode);
self->Node->Device = FileSystemRoots->Node.Device;
self->Node->SetDevice(0, 1);
self->Node->Flags = iFlags;
}
dev_t Virtual::EarlyReserveDevice()
{
RegisterLock.store(true);
size_t len = DeviceMap.size();
return len;
}
int Virtual::LateRegisterFileSystem(dev_t Device, FileSystemInfo *fsi, Inode *Root)
{
auto it = DeviceMap.find(Device);
if (it != DeviceMap.end())
ReturnLogError(-EEXIST, "Device %d already registered", Device);
Root->Flags |= I_FLAG_ROOT;
FSMountInfo fsmi{.fsi = fsi, .Root = Root};
DeviceMap.insert({Device, fsmi});
RegisterLock.store(false);
return 0;
}
dev_t Virtual::RegisterFileSystem(FileSystemInfo *fsi, Inode *Root)
{
RegisterLock.store(true);
size_t len = DeviceMap.size();
Root->Flags |= I_FLAG_ROOT;
FSMountInfo fsmi{.fsi = fsi, .Root = Root};
DeviceMap.insert({len, fsmi});
RegisterLock.store(false);
return len;
}
int Virtual::UnregisterFileSystem(dev_t Device)
{
auto it = DeviceMap.find(Device);
if (it == DeviceMap.end())
ReturnLogError(-ENOENT, "Device %d not found", Device);
if (it->second.fsi->SuperOps.Synchronize)
it->second.fsi->SuperOps.Synchronize(it->second.fsi, NULL);
if (it->second.fsi->SuperOps.Destroy)
it->second.fsi->SuperOps.Destroy(it->second.fsi);
DeviceMap.erase(it);
return 0;
}
Virtual::Virtual()
{
SmartLock(VirtualLock);
FileSystemRoots = new vfsInode;
FileSystemRoots->Node.Index = -1;
FileSystemRoots->Node.Mode = S_IRWXU |
S_IRWXG |
S_IROTH | S_IXOTH |
S_IFDIR;
FileSystemRoots->Node.Flags = I_FLAG_ROOT | I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
FileSystemRoots->Node.Offset = INT32_MAX;
FileSystemRoots->Name = "<ROOT>";
FileSystemInfo *fsi = new FileSystemInfo;
fsi->Name = "Virtual Roots";
fsi->RootName = "ROOT";
fsi->Flags = I_FLAG_ROOT | I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
fsi->SuperOps = {};
fsi->Ops.Lookup = __vfs_Lookup;
fsi->Ops.Create = __vfs_Create;
fsi->Ops.ReadDir = __vfs_Readdir;
fsi->Ops.ReadLink = __vfs_ReadLink;
FileSystemRoots->Node.Device = this->RegisterFileSystem(fsi, &FileSystemRoots->Node);
FileSystemRoots->Node.SetDevice(0, 0);
}
Virtual::~Virtual()
{
}
}