diff --git a/Kernel/include/filesystem/ramfs.hpp b/Kernel/include/filesystem/ramfs.hpp new file mode 100644 index 00000000..f94c3c4c --- /dev/null +++ b/Kernel/include/filesystem/ramfs.hpp @@ -0,0 +1,149 @@ +/* + 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 + +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(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 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 Files; + + 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 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 MountRAMFS(Inode *Parent, const char *Name, size_t Index) diff --git a/Kernel/storage/fs/ramfs.cpp b/Kernel/storage/fs/ramfs.cpp new file mode 100644 index 00000000..cb83e622 --- /dev/null +++ b/Kernel/storage/fs/ramfs.cpp @@ -0,0 +1,421 @@ +/* + 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 . +*/ + +#include +#include +#include +#include + +#include "../../kernel.h" + +namespace vfs +{ + int RAMFS::Lookup(struct Inode *_Parent, const char *Name, struct Inode **Result) + { + auto Parent = (RAMFSInode *)_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 (auto &&child : Parent->Children) + { + if (strcmp(child->Name.c_str(), basename) != 0) + continue; + + *Result = &child->Node; + return 0; + } + + return -ENOENT; + } + + for (auto &&i : Files) + { + RAMFSInode *node = i.second; + if (strcmp(node->Name.c_str(), basename) != 0) + continue; + *Result = &i.second->Node; + return 0; + } + + return -ENOENT; + } + + int RAMFS::Create(struct Inode *_Parent, const char *Name, mode_t Mode, struct Inode **Result) + { + RAMFSInode *Parent = (RAMFSInode *)_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); + + RAMFSInode *node = new RAMFSInode; + node->Name.assign(basename, length); + node->Mode = Mode; + + auto &&file = Files.insert(std::make_pair(NextInode, node)); + assert(file.second == true); + *Result = &file.first->second->Node; + if (Parent) + Parent->AddChild(node); + NextInode++; + return 0; + } + + ssize_t RAMFS::Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset) + { + auto fileItr = Files.find(Node->Index); + assert(fileItr != Files.end()); + + RAMFSInode *node = fileItr->second; + size_t fileSize = node->Stat.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, node->Buffer.Data, Size); + return Size; + } + + ssize_t RAMFS::Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset) + { + auto fileItr = Files.find(Node->Index); + assert(fileItr != Files.end()); + + RAMFSInode *node = fileItr->second; + + if (node->Buffer.IsAllocated() == false) + node->Buffer.Allocate(node->Stat.Size); + + size_t fileSize = node->Stat.Size; + + if (Size <= 0) + { + debug("Size is less than or equal to 0"); + return -EINVAL; + } + + if ((size_t)Offset > fileSize) + { + debug("Offset %d is greater than file size %d", Offset, fileSize); + node->Buffer.Allocate(Offset + Size, true, true); + } + + if ((fileSize - Offset) == 0) + { + debug("Offset %d is equal to file size %d", Offset, fileSize); + node->Buffer.Allocate(Size, true, true); + } + + if ((size_t)Offset + Size > fileSize) + { + debug("Offset %d + Size %d is greater than file size %d", + Offset, Size, fileSize); + node->Buffer.Allocate(Offset + Size, true, true); + } + + memcpy(static_cast(node->Buffer.Data) + Offset, Buffer, Size); + node->Stat.Size = Size; + return Size; + } + + __no_sanitize("alignment") + ssize_t RAMFS::ReadDir(struct Inode *_Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries) + { + /* FIXME: FIX ALIGNMENT FOR DIRENT! */ + auto Node = (RAMFSInode *)_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; + } + + 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; + + 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; + + if (S_ISREG(var->Stat.Mode)) + ent->d_type = DT_REG; + else if (S_ISDIR(var->Stat.Mode)) + ent->d_type = DT_DIR; + else if (S_ISLNK(var->Stat.Mode)) + ent->d_type = DT_LNK; + else if (S_ISCHR(var->Stat.Mode)) + ent->d_type = DT_CHR; + else if (S_ISBLK(var->Stat.Mode)) + ent->d_type = DT_BLK; + else if (S_ISFIFO(var->Stat.Mode)) + ent->d_type = DT_FIFO; + else if (S_ISSOCK(var->Stat.Mode)) + ent->d_type = DT_SOCK; + else + ent->d_type = DT_UNKNOWN; + + 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 RAMFS::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; + + RAMFSInode *node = (RAMFSInode *)*Result; + node->SymLink.assign(Target, strlen(Target)); + return 0; + } + + ssize_t RAMFS::ReadLink(struct Inode *Node, char *Buffer, size_t Size) + { + auto fileItr = Files.find(Node->Index); + assert(fileItr != Files.end()); + + RAMFSInode *node = fileItr->second; + + if (node->SymLink.size() > Size) + Size = node->SymLink.size(); + + strncpy(Buffer, node->SymLink.data(), Size); + debug("Read link %d bytes from %d: \"%s\"", Size, Node->Index, Buffer); + return Size; + } + + int RAMFS::Stat(struct Inode *Node, struct kstat *Stat) + { + auto fileItr = Files.find(Node->Index); + assert(fileItr != Files.end()); + + RAMFSInode *node = fileItr->second; + *Stat = node->Stat; + return 0; + } +} + +O2 int __ramfs_Lookup(struct Inode *Parent, const char *Name, struct Inode **Result) +{ + return ((vfs::RAMFS *)Parent->PrivateData)->Lookup(Parent, Name, Result); +} + +O2 int __ramfs_Create(struct Inode *Parent, const char *Name, mode_t Mode, struct Inode **Result) +{ + return ((vfs::RAMFS *)Parent->PrivateData)->Create(Parent, Name, Mode, Result); +} + +O2 ssize_t __ramfs_Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset) +{ + return ((vfs::RAMFS *)Node->PrivateData)->Read(Node, Buffer, Size, Offset); +} + +O2 ssize_t __ramfs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset) +{ + return ((vfs::RAMFS *)Node->PrivateData)->Write(Node, Buffer, Size, Offset); +} + +O2 ssize_t __ramfs_Readdir(struct Inode *Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries) +{ + return ((vfs::RAMFS *)Node->PrivateData)->ReadDir(Node, Buffer, Size, Offset, Entries); +} + +O2 int __ramfs_SymLink(Inode *Parent, const char *Name, const char *Target, Inode **Result) +{ + return ((vfs::RAMFS *)Parent->PrivateData)->SymLink(Parent, Name, Target, Result); +} + +O2 ssize_t __ramfs_ReadLink(Inode *Node, char *Buffer, size_t Size) +{ + return ((vfs::RAMFS *)Node->PrivateData)->ReadLink(Node, Buffer, Size); +} + +O2 int __ramfs_Stat(struct Inode *Node, kstat *Stat) +{ + return ((vfs::RAMFS *)Node->PrivateData)->Stat(Node, Stat); +} + +O2 int __ramfs_DestroyInode(FileSystemInfo *Info, Inode *Node) +{ + vfs::RAMFS::RAMFSInode *inode = (vfs::RAMFS::RAMFSInode *)Node; + delete inode; + return 0; +} + +O2 int __ramfs_Destroy(FileSystemInfo *fsi) +{ + assert(fsi->PrivateData); + delete (vfs::RAMFS *)fsi->PrivateData; + delete fsi; + return 0; +} + +bool MountRAMFS(Inode *Parent, const char *Name, size_t Index) +{ + vfs::RAMFS *ramfs = new vfs::RAMFS; + ramfs->DeviceID = fs->EarlyReserveDevice(); + + if (Parent == nullptr) + { + ramfs->Create(nullptr, Name, S_IFDIR | 0755, &Parent); + if (Parent == nullptr) + { + error("Failed to create root inode"); + delete ramfs; + return false; + } + } + + FileSystemInfo *fsi = new FileSystemInfo; + fsi->Name = "ramfs"; + fsi->RootName = "/"; + fsi->Flags = I_FLAG_ROOT | I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP; + fsi->SuperOps.DeleteInode = __ramfs_DestroyInode; + fsi->SuperOps.Destroy = __ramfs_Destroy; + fsi->Ops.Lookup = __ramfs_Lookup; + fsi->Ops.Create = __ramfs_Create; + fsi->Ops.Read = __ramfs_Read; + fsi->Ops.Write = __ramfs_Write; + fsi->Ops.ReadDir = __ramfs_Readdir; + fsi->Ops.SymLink = __ramfs_SymLink; + fsi->Ops.ReadLink = __ramfs_ReadLink; + fsi->Ops.Stat = __ramfs_Stat; + fsi->PrivateData = ramfs; + + fs->LateRegisterFileSystem(ramfs->DeviceID, fsi, Parent); + fs->AddRootAt(Parent, Index); + return true; +}