Kernel/storage/virtual.cpp

258 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/>.
*/
#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()
{
}
}