mirror of
https://github.com/EnderIce2/Fennix.git
synced 2025-07-01 18:39:16 +00:00
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:
391
Kernel/fs/descriptor.cpp
Normal file
391
Kernel/fs/descriptor.cpp
Normal file
@ -0,0 +1,391 @@
|
||||
/*
|
||||
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 <fs/vfs.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)
|
||||
{
|
||||
eNode ret = fs->Create(pcb->CWD, AbsolutePath, Mode);
|
||||
if (Flags & O_EXCL && ret == false)
|
||||
{
|
||||
debug("%s: File already exists?, returning %s", AbsolutePath, ret.what());
|
||||
return -ret.Error;
|
||||
}
|
||||
}
|
||||
|
||||
if (Flags & O_CLOEXEC)
|
||||
{
|
||||
fixme("O_CLOEXEC");
|
||||
}
|
||||
|
||||
eNode ret = fs->Lookup(pcb->CWD, AbsolutePath);
|
||||
if (ret == false)
|
||||
{
|
||||
error("Failed to open file %s, %s", AbsolutePath, ret.what());
|
||||
return -ret.Error;
|
||||
}
|
||||
Node node = ret;
|
||||
|
||||
if (Flags & O_TRUNC)
|
||||
{
|
||||
debug("Truncating file %s", AbsolutePath);
|
||||
fs->Truncate(node, 0);
|
||||
}
|
||||
|
||||
Fildes fd{};
|
||||
|
||||
if (Flags & O_APPEND)
|
||||
{
|
||||
debug("Appending to file %s", AbsolutePath);
|
||||
struct kstat stat;
|
||||
fs->Stat(node, &stat);
|
||||
fd.Offset = fs->Seek(node, stat.Size);
|
||||
}
|
||||
|
||||
fd.Mode = Mode;
|
||||
fd.Flags = Flags;
|
||||
fd.node = node;
|
||||
|
||||
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(this->fdDir, linkName, AbsolutePath) == true);
|
||||
fs->Open(node, 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 fs->Read(it->second.node, buf, count, it->second.Offset);
|
||||
}
|
||||
|
||||
ssize_t FileDescriptorTable::usr_pread(int fd, void *buf, size_t count, off_t offset)
|
||||
{
|
||||
auto it = this->FileMap.find(fd);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", fd);
|
||||
|
||||
return fs->Read(it->second.node, buf, count, 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 fs->Write(it->second.node, buf, count, it->second.Offset);
|
||||
}
|
||||
|
||||
ssize_t FileDescriptorTable::usr_pwrite(int fd, const void *buf, size_t count, off_t offset)
|
||||
{
|
||||
auto it = this->FileMap.find(fd);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", fd);
|
||||
|
||||
return fs->Write(it->second.node, buf, count, 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 = fs->Seek(it->second.node, offset);
|
||||
break;
|
||||
}
|
||||
case SEEK_CUR:
|
||||
{
|
||||
newOffset = fs->Seek(it->second.node, newOffset + offset);
|
||||
break;
|
||||
}
|
||||
case SEEK_END:
|
||||
{
|
||||
struct kstat stat{};
|
||||
fs->Stat(it->second.node, &stat);
|
||||
newOffset = fs->Seek(it->second.node, stat.Size + offset);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return newOffset;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::usr_stat(const char *pathname, kstat *statbuf)
|
||||
{
|
||||
Node root = thisProcess->Info.RootNode;
|
||||
eNode ret = fs->Lookup(root, pathname);
|
||||
if (ret == false)
|
||||
ReturnLogError(-ret.Error, "Error on %s, %s", pathname, ret.what());
|
||||
Node node = ret;
|
||||
|
||||
if (node->IsSymbolicLink())
|
||||
{
|
||||
std::unique_ptr<char[]> buffer(new char[1024]);
|
||||
ssize_t len = fs->ReadLink(node, buffer.get(), 1024);
|
||||
if (len < 0)
|
||||
return len;
|
||||
|
||||
ret = fs->Lookup(root, buffer.get());
|
||||
if (ret == false)
|
||||
return -ret.Error;
|
||||
return fs->Stat(ret.Value, statbuf);
|
||||
}
|
||||
|
||||
return fs->Stat(node, statbuf);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::usr_fstat(int fd, 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 fs->Stat(fildes.node, statbuf);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::usr_lstat(const char *pathname, kstat *statbuf)
|
||||
{
|
||||
Node root = thisProcess->Info.RootNode;
|
||||
eNode ret = fs->Lookup(root, pathname);
|
||||
if (ret == false)
|
||||
ReturnLogError(-ret.Error, "Error on %s, %s", pathname, ret.what());
|
||||
|
||||
return fs->Stat(ret.Value, 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 fs->Ioctl(it->second.node, request, argp);
|
||||
}
|
||||
|
||||
FileDescriptorTable::FileDescriptorTable(void *_Owner)
|
||||
: Owner(_Owner)
|
||||
{
|
||||
debug("+ %#lx", this);
|
||||
|
||||
/* d r-x r-x r-x */
|
||||
mode_t Mode = S_IROTH | S_IXOTH |
|
||||
S_IRGRP | S_IXGRP |
|
||||
S_IRUSR | S_IXUSR |
|
||||
S_IFDIR;
|
||||
Tasking::PCB *pcb = (Tasking::PCB *)_Owner;
|
||||
this->fdDir = fs->Create(pcb->ProcDirectory, "fd", Mode);
|
||||
}
|
||||
}
|
412
Kernel/fs/ramfs.cpp
Normal file
412
Kernel/fs/ramfs.cpp
Normal file
@ -0,0 +1,412 @@
|
||||
/*
|
||||
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 <fs/ramfs.hpp>
|
||||
#include <memory.hpp>
|
||||
#include <functional>
|
||||
#include <debug.h>
|
||||
|
||||
#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, RootName.c_str()) == 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;
|
||||
|
||||
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;
|
||||
node->Node = inode;
|
||||
|
||||
auto file = Files.insert(std::make_pair(NextInode, node));
|
||||
assert(file.second == true);
|
||||
*Result = &Files.at(NextInode)->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<char *>(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;
|
||||
}
|
||||
}
|
||||
|
||||
int __ramfs_Lookup(struct Inode *Parent, const char *Name, struct Inode **Result)
|
||||
{
|
||||
return ((vfs::RAMFS *)Parent->PrivateData)->Lookup(Parent, Name, Result);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int __ramfs_SymLink(Inode *Parent, const char *Name, const char *Target, Inode **Result)
|
||||
{
|
||||
return ((vfs::RAMFS *)Parent->PrivateData)->SymLink(Parent, Name, Target, Result);
|
||||
}
|
||||
|
||||
ssize_t __ramfs_ReadLink(Inode *Node, char *Buffer, size_t Size)
|
||||
{
|
||||
return ((vfs::RAMFS *)Node->PrivateData)->ReadLink(Node, Buffer, Size);
|
||||
}
|
||||
|
||||
int __ramfs_Stat(struct Inode *Node, kstat *Stat)
|
||||
{
|
||||
return ((vfs::RAMFS *)Node->PrivateData)->Stat(Node, Stat);
|
||||
}
|
||||
|
||||
int __ramfs_DestroyInode(FileSystemInfo *Info, Inode *Node)
|
||||
{
|
||||
vfs::RAMFS::RAMFSInode *inode = (vfs::RAMFS::RAMFSInode *)Node;
|
||||
delete inode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __ramfs_Destroy(FileSystemInfo *fsi)
|
||||
{
|
||||
assert(fsi->PrivateData);
|
||||
delete (vfs::RAMFS *)fsi->PrivateData;
|
||||
delete fsi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool MountAndRootRAMFS(Node Parent, const char *Name, size_t Index)
|
||||
{
|
||||
vfs::RAMFS *ramfs = new vfs::RAMFS;
|
||||
ramfs->RootName.assign(Name);
|
||||
|
||||
FileSystemInfo *fsi = new FileSystemInfo;
|
||||
fsi->Name = "ramfs";
|
||||
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;
|
||||
ramfs->DeviceID = fs->RegisterFileSystem(fsi);
|
||||
|
||||
Inode *root = nullptr;
|
||||
ramfs->Create(nullptr, Name, S_IFDIR | 0755, &root);
|
||||
|
||||
fs->Mount(Parent, root, Name, fsi);
|
||||
fs->AddRoot(Index, fs->Convert(root));
|
||||
return true;
|
||||
}
|
885
Kernel/fs/ustar.cpp
Normal file
885
Kernel/fs/ustar.cpp
Normal file
@ -0,0 +1,885 @@
|
||||
/*
|
||||
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 <fs/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;
|
||||
debug("looking up for %s", Name);
|
||||
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;
|
||||
|
||||
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));
|
||||
|
||||
auto file = Files.insert(std::make_pair(NextInode, node));
|
||||
assert(file.second == true);
|
||||
*Result = &Files.at(NextInode)->Node;
|
||||
if (Parent)
|
||||
{
|
||||
Parent->Children.push_back(Files.at(NextInode));
|
||||
Files.at(NextInode)->Parent = Parent;
|
||||
}
|
||||
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;
|
||||
debug("reading directory %s", Node->Path.c_str());
|
||||
|
||||
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;
|
||||
debug(".");
|
||||
}
|
||||
|
||||
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;
|
||||
debug("..");
|
||||
}
|
||||
|
||||
// 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) + var->Name.size() + 1);
|
||||
|
||||
if (totalSize + reclen > Size)
|
||||
{
|
||||
debug("not enough space for %s (%zu + %zu = %zu > %zu)", var->Name.c_str(), totalSize, reclen, 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()));
|
||||
debug("%s", 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: \"%s\"", Size, Node->Index, Buffer);
|
||||
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)
|
||||
{
|
||||
/* For some reason if GRUB inflates the archive, the magic is "ustar " */
|
||||
if (strncmp(header->signature, TMAGIC, TMAGLEN - 1) == 0)
|
||||
return true;
|
||||
|
||||
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 - 1) != 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.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);
|
||||
}
|
||||
|
||||
O2 int __ustar_DestroyInode(FileSystemInfo *Info, Inode *Node)
|
||||
{
|
||||
((vfs::USTAR::USTARInode *)Node)->Deleted = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
O2 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, size_t Index)
|
||||
{
|
||||
vfs::USTAR *ustar = new vfs::USTAR();
|
||||
if (!ustar->TestArchive(Address))
|
||||
{
|
||||
delete ustar;
|
||||
return false;
|
||||
}
|
||||
|
||||
FileSystemInfo *fsi = new FileSystemInfo;
|
||||
fsi->Name = "ustar";
|
||||
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;
|
||||
|
||||
ustar->DeviceID = fs->RegisterFileSystem(fsi);
|
||||
ustar->ReadArchive(Address, Size);
|
||||
|
||||
Inode *rootfs = nullptr;
|
||||
ustar->Lookup(nullptr, "/", &rootfs);
|
||||
assert(rootfs != nullptr);
|
||||
|
||||
eNode _node = fs->Convert(rootfs);
|
||||
assert(_node.Error == 0);
|
||||
|
||||
Node node = _node;
|
||||
node->fsi = fsi;
|
||||
node->Flags.MountPoint = true;
|
||||
node->Name = "/";
|
||||
node->Path = "/";
|
||||
|
||||
fs->AddRoot(Index, node);
|
||||
return true;
|
||||
}
|
586
Kernel/fs/vfs.cpp
Normal file
586
Kernel/fs/vfs.cpp
Normal file
@ -0,0 +1,586 @@
|
||||
/*
|
||||
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 <fs/vfs.hpp>
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
eNode Virtual::Convert(Inode *inode)
|
||||
{
|
||||
Node cache = std::make_shared<NodeCache>();
|
||||
cache->inode = inode;
|
||||
return {cache, 0};
|
||||
}
|
||||
|
||||
eNode Virtual::Convert(Node &Parent, Inode *inode)
|
||||
{
|
||||
Node cache = std::make_shared<NodeCache>();
|
||||
cache->inode = inode;
|
||||
cache->fsi = Parent->fsi;
|
||||
cache->Parent = Parent;
|
||||
Parent->Children.push_back(cache);
|
||||
return {cache, 0};
|
||||
}
|
||||
|
||||
std::string Virtual::NormalizePath(Node &Parent, std::string Path, bool Join)
|
||||
{
|
||||
std::string result;
|
||||
if (Join)
|
||||
{
|
||||
size_t len = Path.size() + Parent->Path.size() + 2;
|
||||
result.reserve(len);
|
||||
len = cwk_path_join(Parent->Path.c_str(), Path.c_str(), result.data(), result.capacity());
|
||||
result.resize(len);
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t len = Path.size() + 2;
|
||||
result.reserve(len);
|
||||
len = cwk_path_normalize(Path.c_str(), result.data(), result.capacity());
|
||||
result.resize(len);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Virtual::RootExists(dev_t Index)
|
||||
{
|
||||
if (Roots.find(Index) == Roots.end())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
eNode Virtual::GetRoot(dev_t Index)
|
||||
{
|
||||
auto it = Roots.find(Index);
|
||||
if (it == Roots.end())
|
||||
return {nullptr, ENOENT};
|
||||
return {it->second, 0};
|
||||
}
|
||||
|
||||
ssize_t Virtual::GetRoot(Node Index)
|
||||
{
|
||||
for (auto it = Roots.begin(); it != Roots.end(); ++it)
|
||||
{
|
||||
if (it->second == Index)
|
||||
return it->first;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int Virtual::AddRoot(dev_t Index, Node Root, bool Replace)
|
||||
{
|
||||
assert(Root != nullptr);
|
||||
|
||||
auto it = Roots.find(Index);
|
||||
if (it == Roots.end())
|
||||
{
|
||||
Roots[Index] = Root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Replace)
|
||||
{
|
||||
Roots[Index] = Root;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("Root %ld already exists", Index);
|
||||
return EEXIST;
|
||||
}
|
||||
}
|
||||
|
||||
dev_t Virtual::RegisterFileSystem(FileSystemInfo *fsi)
|
||||
{
|
||||
assert(fsi != nullptr);
|
||||
FileSystems.insert({FileSystems.size(), fsi});
|
||||
return FileSystems.size() - 1;
|
||||
}
|
||||
|
||||
int Virtual::UnregisterFileSystem(dev_t Device)
|
||||
{
|
||||
auto it = FileSystems.find(Device);
|
||||
if (it == FileSystems.end())
|
||||
return -ENOENT;
|
||||
|
||||
FileSystemInfo *fsi = it->second;
|
||||
|
||||
/* TODO: unmount */
|
||||
fixme("Unmounting %d", Device);
|
||||
|
||||
if (fsi->SuperOps.Synchronize)
|
||||
fsi->SuperOps.Synchronize(fsi, nullptr);
|
||||
if (fsi->SuperOps.Destroy)
|
||||
fsi->SuperOps.Destroy(fsi);
|
||||
|
||||
FileSystems.erase(it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
eNode Virtual::Lookup(Node &Parent, std::string Path)
|
||||
{
|
||||
assert(Parent != nullptr);
|
||||
|
||||
debug("looking up \"%s\" in \"%s\"", Path.c_str(), Parent->Path.c_str());
|
||||
|
||||
if (Path == ".")
|
||||
return {Parent, 0};
|
||||
else if (Path == "..")
|
||||
return {Parent->Parent ? Parent->Parent : Parent, 0};
|
||||
|
||||
Node base = Parent;
|
||||
bool absolute = PathIsAbsolute(Path);
|
||||
if (absolute == true)
|
||||
{
|
||||
while (base->Parent)
|
||||
base = base->Parent;
|
||||
}
|
||||
|
||||
debug("base is \"%s\" and path is \"%s\" %d", base->Path.c_str(), Path.c_str(), absolute);
|
||||
Path = this->NormalizePath(base, Path, !absolute);
|
||||
debug("after normalizing, path is \"%s\" %d", Path.c_str(), absolute);
|
||||
|
||||
struct cwk_segment segment;
|
||||
if (!cwk_path_get_first_segment(Path.c_str(), &segment))
|
||||
{
|
||||
debug("%s no segments; %d", Path.c_str(), absolute);
|
||||
if (Path == "/")
|
||||
return {base, 0};
|
||||
|
||||
assert(!"Path doesn't have any segments.");
|
||||
}
|
||||
|
||||
Node node = base;
|
||||
/* We need to go to the root after NormalizePath even if Path is relative */
|
||||
if (absolute == false)
|
||||
{
|
||||
while (node->Parent)
|
||||
{
|
||||
debug("current parent \"%s\"", node->Parent->Path.c_str());
|
||||
node = node->Parent;
|
||||
debug("new parent \"%s\"", node->Parent ? node->Parent->Path.c_str() : "<null>");
|
||||
}
|
||||
}
|
||||
|
||||
std::string currentPath = node->Path;
|
||||
if (currentPath.empty())
|
||||
currentPath = "/";
|
||||
|
||||
do
|
||||
{
|
||||
std::string segmentStr(segment.begin, segment.size);
|
||||
debug("Current segment is \"%s\"", segmentStr.c_str());
|
||||
|
||||
eNode ret = node->CachedSearch(segmentStr);
|
||||
if (ret == false)
|
||||
{
|
||||
debug("cache miss for \"%s\"", segmentStr.c_str());
|
||||
|
||||
if (node->fsi->Ops.Lookup == nullptr)
|
||||
return {nullptr, ENOTSUP};
|
||||
|
||||
Inode *inode;
|
||||
int ret = node->fsi->Ops.Lookup(node->inode, segmentStr.c_str(), &inode);
|
||||
if (ret != 0)
|
||||
return {nullptr, ret};
|
||||
|
||||
if (currentPath == "/")
|
||||
currentPath += segmentStr;
|
||||
else
|
||||
currentPath += "/" + segmentStr;
|
||||
|
||||
node = Convert(node, inode);
|
||||
node->Name = segmentStr;
|
||||
node->Path = currentPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("cache hit for \"%s\"", segmentStr.c_str());
|
||||
node = ret;
|
||||
if (currentPath == "/")
|
||||
currentPath += segmentStr;
|
||||
else
|
||||
currentPath += "/" + segmentStr;
|
||||
}
|
||||
} while (cwk_path_get_next_segment(&segment));
|
||||
|
||||
return {node, 0};
|
||||
}
|
||||
|
||||
eNode Virtual::Create(Node &Parent, std::string Name, mode_t Mode, bool ErrorIfExists)
|
||||
{
|
||||
eNode exists = this->Lookup(Parent, Name);
|
||||
if (exists)
|
||||
{
|
||||
if (ErrorIfExists)
|
||||
return {nullptr, EEXIST};
|
||||
|
||||
/* I should handle this in a better way */
|
||||
assert((exists.Value->inode->Mode & S_IFMT) == (Mode & S_IFMT));
|
||||
debug("File \"%s\" already exists in cache", Name.c_str());
|
||||
return exists;
|
||||
}
|
||||
|
||||
if (!Parent)
|
||||
return {nullptr, EINVAL};
|
||||
if (Parent->fsi->Ops.Create == nullptr)
|
||||
return {nullptr, ENOTSUP};
|
||||
|
||||
Inode *inode;
|
||||
int ret = Parent->fsi->Ops.Create(Parent->inode, Name.c_str(), Mode, &inode);
|
||||
if (ret != 0)
|
||||
return {nullptr, ret};
|
||||
|
||||
Node node = Convert(Parent, inode);
|
||||
node->Name = Name;
|
||||
std::string unormalized = Parent->Path == "/" ? "/" + Name : Parent->Path + "/" + Name;
|
||||
node->Path = fs->NormalizePath(Parent, unormalized);
|
||||
return {node, 0};
|
||||
}
|
||||
|
||||
int Virtual::Remove(Node &Parent, std::string Name)
|
||||
{
|
||||
if (!Parent)
|
||||
return -EINVAL;
|
||||
if (Parent->fsi->Ops.Remove == nullptr)
|
||||
return -ENOTSUP;
|
||||
int ret = Parent->fsi->Ops.Remove(Parent->inode, Name.c_str());
|
||||
if (ret == 0)
|
||||
{
|
||||
for (auto it = Parent->Children.begin(); it != Parent->Children.end(); ++it)
|
||||
{
|
||||
if (it->get()->Name != Name)
|
||||
continue;
|
||||
Parent->Children.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Virtual::Remove(Node &node)
|
||||
{
|
||||
if (!node->Parent)
|
||||
return -EINVAL;
|
||||
if (node->Parent->fsi->Ops.Remove == nullptr)
|
||||
return -ENOTSUP;
|
||||
int ret = node->Parent->fsi->Ops.Remove(node->inode, node->Name.c_str());
|
||||
if (ret == 0)
|
||||
{
|
||||
Node &p = node->Parent;
|
||||
for (auto it = p->Children.begin(); it != p->Children.end(); ++it)
|
||||
{
|
||||
if (it->get() != node.get())
|
||||
continue;
|
||||
p->Children.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Virtual::Rename(Node &node, std::string NewName)
|
||||
{
|
||||
if (node->fsi->Ops.Rename == nullptr)
|
||||
return -ENOTSUP;
|
||||
int ret = node->fsi->Ops.Rename(node->inode, node->Name.c_str(), NewName.c_str());
|
||||
if (ret == 0)
|
||||
node->Name = NewName;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t Virtual::Read(Node &Target, void *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
if (Target->IsDirectory() || Target->IsMountPoint())
|
||||
return -EISDIR;
|
||||
|
||||
if (Target->IsSymbolicLink())
|
||||
return -EINVAL;
|
||||
|
||||
/* TODO: cache buffer */
|
||||
|
||||
return Target->__Read(Buffer, Size, Offset);
|
||||
}
|
||||
|
||||
ssize_t Virtual::Write(Node &Target, const void *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
if (Target->IsDirectory() || Target->IsMountPoint())
|
||||
return -EISDIR;
|
||||
|
||||
if (Target->IsSymbolicLink())
|
||||
return -EINVAL;
|
||||
|
||||
/* TODO: cache buffer */
|
||||
|
||||
return Target->__Write(Buffer, Size, Offset);
|
||||
}
|
||||
|
||||
int Virtual::Truncate(Node &Target, off_t Size)
|
||||
{
|
||||
if (Target->IsDirectory() || Target->IsMountPoint())
|
||||
return -EISDIR;
|
||||
|
||||
if (!Target->IsRegularFile())
|
||||
return -EINVAL;
|
||||
|
||||
/* TODO: cache buffer */
|
||||
|
||||
return Target->__Truncate(Size);
|
||||
}
|
||||
|
||||
__no_sanitize("alignment") ssize_t Virtual::ReadDirectory(Node &Target, kdirent *Buffer, size_t Size, off_t Offset, off_t Entries)
|
||||
{
|
||||
if (!Target->IsDirectory() && !Target->IsMountPoint())
|
||||
return -ENOTDIR;
|
||||
|
||||
ssize_t total = 0;
|
||||
off_t entryIndex = 0;
|
||||
std::list<std::string> seen;
|
||||
|
||||
uint8_t *bufPtr = reinterpret_cast<uint8_t *>(Buffer);
|
||||
|
||||
if (Target->fsi && Target->fsi->Ops.ReadDir)
|
||||
{
|
||||
const size_t tempBufSize = 4096;
|
||||
std::unique_ptr<uint8_t[]> tempBuf(new uint8_t[tempBufSize]);
|
||||
|
||||
off_t fsOffset = Offset;
|
||||
ssize_t read = Target->fsi->Ops.ReadDir(Target->inode, (kdirent *)tempBuf.get(), tempBufSize, fsOffset, Entries);
|
||||
if (read > 0)
|
||||
{
|
||||
ssize_t pos = 0;
|
||||
while (pos < read)
|
||||
{
|
||||
kdirent *ent = (kdirent *)(tempBuf.get() + pos);
|
||||
if (ent->d_reclen == 0)
|
||||
break;
|
||||
|
||||
size_t reclen = ent->d_reclen;
|
||||
if (total + reclen > Size)
|
||||
break;
|
||||
|
||||
memcpy(bufPtr, ent, reclen);
|
||||
seen.push_back(ent->d_name);
|
||||
bufPtr += reclen;
|
||||
total += reclen;
|
||||
pos += reclen;
|
||||
entryIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &child : Target->Children)
|
||||
{
|
||||
if (std::find(seen.begin(), seen.end(), child->Name) != seen.end())
|
||||
continue;
|
||||
|
||||
if (entryIndex < Offset)
|
||||
{
|
||||
entryIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint16_t reclen = (uint16_t)(offsetof(struct kdirent, d_name) + child->Name.size() + 1);
|
||||
if (total + reclen > (ssize_t)Size)
|
||||
break;
|
||||
|
||||
kdirent *ent = (kdirent *)bufPtr;
|
||||
ent->d_ino = child->inode ? child->inode->Index : 0;
|
||||
ent->d_off = entryIndex++;
|
||||
ent->d_reclen = reclen;
|
||||
ent->d_type = child->inode ? IFTODT(child->inode->Mode) : DT_UNKNOWN;
|
||||
strcpy(ent->d_name, child->Name.c_str());
|
||||
|
||||
bufPtr += reclen;
|
||||
total += reclen;
|
||||
seen.push_back(child->Name);
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
__no_sanitize("alignment") std::list<Node> Virtual::ReadDirectory(Node &Target)
|
||||
{
|
||||
if (!Target->IsDirectory() && !Target->IsMountPoint())
|
||||
return {};
|
||||
std::list<Node> ret;
|
||||
std::list<std::string> seen;
|
||||
|
||||
if (Target->fsi && Target->fsi->Ops.ReadDir)
|
||||
{
|
||||
const size_t bufSize = 4096;
|
||||
std::unique_ptr<uint8_t[]> buf(new uint8_t[bufSize]);
|
||||
off_t offset = 0;
|
||||
while (true)
|
||||
{
|
||||
ssize_t read = Target->fsi->Ops.ReadDir(Target->inode, (kdirent *)buf.get(), bufSize, offset, LONG_MAX);
|
||||
if (read <= 0)
|
||||
break;
|
||||
ssize_t pos = 0;
|
||||
while (pos < read)
|
||||
{
|
||||
kdirent *ent = (kdirent *)(buf.get() + pos);
|
||||
if (ent->d_reclen == 0)
|
||||
break;
|
||||
debug("%s", ent->d_name);
|
||||
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
|
||||
{
|
||||
pos += ent->d_reclen;
|
||||
continue;
|
||||
}
|
||||
|
||||
seen.push_back(ent->d_name);
|
||||
|
||||
auto it = std::find_if(Target->Children.begin(), Target->Children.end(),
|
||||
[&](const Node &n)
|
||||
{ return n->Name == ent->d_name; });
|
||||
|
||||
if (it != Target->Children.end())
|
||||
ret.push_back(*it);
|
||||
else
|
||||
{
|
||||
eNode result = Lookup(Target, ent->d_name);
|
||||
if (result.Error == 0 && result.Value)
|
||||
{
|
||||
Target->Children.push_back(result.Value);
|
||||
result.Value->Parent = Target;
|
||||
ret.push_back(result.Value);
|
||||
}
|
||||
}
|
||||
pos += ent->d_reclen;
|
||||
}
|
||||
offset += read;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &child : Target->Children)
|
||||
{
|
||||
if (std::find(seen.begin(), seen.end(), child->Name) != seen.end())
|
||||
continue;
|
||||
if (child->Name == "." || child->Name == "..")
|
||||
continue;
|
||||
ret.push_back(child);
|
||||
seen.push_back(child->Name.c_str());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
eNode Virtual::CreateLink(Node &Parent, std::string Name, std::string Target)
|
||||
{
|
||||
mode_t mode = S_IRWXU |
|
||||
S_IRWXG |
|
||||
S_IRWXO |
|
||||
S_IFLNK;
|
||||
|
||||
eNode enode = this->Create(Parent, Name, mode);
|
||||
if (!enode)
|
||||
return enode;
|
||||
Node node = enode;
|
||||
node->Link = Target;
|
||||
return {node, 0};
|
||||
}
|
||||
|
||||
int Virtual::Stat(Node &Target, struct kstat *Stat)
|
||||
{
|
||||
/* TODO: cache */
|
||||
|
||||
return Target->__Stat(Stat);
|
||||
}
|
||||
|
||||
off_t Virtual::Seek(Node &Target, off_t Offset)
|
||||
{
|
||||
/* TODO: cache */
|
||||
|
||||
return Target->__Seek(Offset);
|
||||
}
|
||||
|
||||
int Virtual::Open(Node &Target, int Flags, mode_t Mode)
|
||||
{
|
||||
/* TODO: cache */
|
||||
|
||||
return Target->__Open(Flags, Mode);
|
||||
}
|
||||
|
||||
int Virtual::Close(Node &Target)
|
||||
{
|
||||
/* TODO: cache */
|
||||
|
||||
return Target->__Close();
|
||||
}
|
||||
|
||||
eNode Virtual::Mount(Node &Parent, Inode *inode, std::string Name, FileSystemInfo *fsi)
|
||||
{
|
||||
assert(Parent);
|
||||
assert(inode);
|
||||
|
||||
Node ret = this->Convert(inode);
|
||||
ret->fsi = fsi;
|
||||
ret->Name = Name;
|
||||
|
||||
std::string unormalized = Parent->Path == "/" ? "/" + Name : Parent->Path + "/" + Name;
|
||||
ret->Path = fs->NormalizePath(Parent, unormalized);
|
||||
// ret->Link =
|
||||
ret->Parent = Parent;
|
||||
Parent->Children.push_back(ret);
|
||||
return {ret, 0};
|
||||
}
|
||||
|
||||
int Virtual::Umount(Node &node)
|
||||
{
|
||||
if (!node->Flags.MountPoint)
|
||||
{
|
||||
debug("node %s is not a mountpoint", node->Path.c_str());
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fixme("untested code");
|
||||
std::shared_ptr<NodeCache> &ptr = node;
|
||||
ptr.reset();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Virtual::Umount(Node &Parent, std::string Name)
|
||||
{
|
||||
eNode node = Parent->CachedSearch(Name);
|
||||
if (!node)
|
||||
{
|
||||
debug("mountpoint %s not found: %s", Name.c_str(), node.what());
|
||||
return -node.Error;
|
||||
}
|
||||
|
||||
return this->Umount(node.Value);
|
||||
}
|
||||
|
||||
void Virtual::Initialize()
|
||||
{
|
||||
debug("Initializing virtual file system...");
|
||||
Node root = this->GetRoot(0);
|
||||
|
||||
/* d rwx rwx rwx */
|
||||
mode_t mode = S_IRWXU |
|
||||
S_IRWXG |
|
||||
S_IRWXO |
|
||||
S_IFDIR;
|
||||
Node var = this->Create(root, "var", mode, false);
|
||||
Node log = this->Create(var, "log", mode, false);
|
||||
}
|
||||
|
||||
Virtual::Virtual() {}
|
||||
Virtual::~Virtual() {}
|
||||
}
|
Reference in New Issue
Block a user