mirror of
https://github.com/Fennix-Project/Kernel.git
synced 2025-07-10 23:09:18 +00:00
Refactor filesystem & stl code
This commit is contained in:
@ -1,31 +0,0 @@
|
||||
# File System Implementation
|
||||
|
||||
---
|
||||
|
||||
## Nodes
|
||||
|
||||
### /storage
|
||||
|
||||
- `node.cpp`
|
||||
- **Node <=> device**
|
||||
- Handles open/close/read/write operations for the device and holds information about the file
|
||||
|
||||
<br>
|
||||
|
||||
- `reference.cpp`
|
||||
- **kernel/user <=> node.cpp**
|
||||
- Maintains the count of references to a node and the seek position
|
||||
|
||||
<br>
|
||||
|
||||
- `descriptor.cpp`
|
||||
- **user <=> reference.cpp**
|
||||
- Manages the file descriptor table for user processes
|
||||
|
||||
### /storage/fs
|
||||
|
||||
This directory contains the implementations of various file systems, such as `fat32.cpp` and `ustar.cpp`.
|
||||
|
||||
### /storage/devices
|
||||
|
||||
This directory houses implementations of various devices, including /dev/null, /dev/zero, /dev/random, and more.
|
121
storage/cache.cpp
Normal file
121
storage/cache.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
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::__CacheRecursiveSearch(FileNode *Root, const char *NameOrPath, bool IsName)
|
||||
{
|
||||
if (Root == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (IsName)
|
||||
{
|
||||
if (strcmp(Root->Name.c_str(), NameOrPath) == 0)
|
||||
return Root;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strcmp(Root->Path.c_str(), NameOrPath) == 0)
|
||||
return Root;
|
||||
}
|
||||
|
||||
for (const auto &Child : Root->Children)
|
||||
{
|
||||
FileNode *ret = __CacheRecursiveSearch(Child, NameOrPath, IsName);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
debug("Failed to find %s in %s", NameOrPath, Root->Path.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileNode *Virtual::CacheLookup(const char *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;
|
||||
__unreachable;
|
||||
|
||||
debug("Path \"%s\" not found; attempting to search by segments", Path);
|
||||
/* FIXME: This may not be the greatest idea */
|
||||
|
||||
struct cwk_segment segment;
|
||||
if (!cwk_path_get_first_segment(Path, &segment))
|
||||
return __CacheRecursiveSearch(rootNode, Path, true);
|
||||
|
||||
do
|
||||
{
|
||||
std::string segmentStr(segment.begin, segment.size);
|
||||
ret = __CacheRecursiveSearch(rootNode, segmentStr.c_str(), true);
|
||||
if (ret)
|
||||
return ret;
|
||||
} while (cwk_path_get_next_segment(&segment));
|
||||
|
||||
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 -1;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -17,7 +17,6 @@
|
||||
|
||||
#include <filesystem.hpp>
|
||||
|
||||
#include <smart_ptr.hpp>
|
||||
#include <convert.h>
|
||||
#include <stropts.h>
|
||||
#include <task.hpp>
|
||||
@ -29,147 +28,22 @@
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
// ReadFSFunction(fd_Read)
|
||||
// {
|
||||
// if (Size <= 0)
|
||||
// Size = node->Length;
|
||||
|
||||
// if (RefOffset > node->Length)
|
||||
// return 0;
|
||||
|
||||
// if ((node->Length - RefOffset) == 0)
|
||||
// return 0; /* EOF */
|
||||
|
||||
// if (RefOffset + (off_t)Size > node->Length)
|
||||
// Size = node->Length;
|
||||
|
||||
// memcpy(Buffer, (uint8_t *)(node->Address + RefOffset), Size);
|
||||
// return Size;
|
||||
// }
|
||||
|
||||
// WriteFSFunction(fd_Write)
|
||||
// {
|
||||
// if (Size <= 0)
|
||||
// Size = node->Length;
|
||||
|
||||
// if (RefOffset > node->Length)
|
||||
// return 0;
|
||||
|
||||
// if (RefOffset + (off_t)Size > node->Length)
|
||||
// Size = node->Length;
|
||||
|
||||
// memcpy((uint8_t *)(node->Address + RefOffset), Buffer, Size);
|
||||
// return Size;
|
||||
// }
|
||||
|
||||
// vfs::FileSystemOperations fd_op = {
|
||||
// .Name = "fd",
|
||||
// // .Read = fd_Read,
|
||||
// // .Write = fd_Write,
|
||||
// };
|
||||
|
||||
FileDescriptorTable::Fildes &
|
||||
FileDescriptorTable::GetFileDescriptor(int FileDescriptor)
|
||||
{
|
||||
foreach (auto &fd in FileDescriptors)
|
||||
{
|
||||
if (fd.Descriptor == FileDescriptor)
|
||||
{
|
||||
// debug("Found file descriptor %d", FileDescriptor);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
return nullfd;
|
||||
}
|
||||
|
||||
FileDescriptorTable::Fildes &
|
||||
FileDescriptorTable::GetDupFildes(int FileDescriptor)
|
||||
{
|
||||
foreach (auto &fd in FildesDuplicates)
|
||||
{
|
||||
if (fd.Descriptor == FileDescriptor)
|
||||
{
|
||||
debug("Found duplicated file descriptor %d", FileDescriptor);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
return nullfd;
|
||||
}
|
||||
|
||||
FileDescriptorTable::Fildes &
|
||||
FileDescriptorTable::GetDescriptor(int FileDescriptor)
|
||||
{
|
||||
Fildes &fd = this->GetFileDescriptor(FileDescriptor);
|
||||
Fildes &dfd = this->GetDupFildes(FileDescriptor);
|
||||
|
||||
if (fd.Descriptor == -1 &&
|
||||
dfd.Descriptor == -1)
|
||||
return nullfd;
|
||||
|
||||
if (fd.Descriptor != -1)
|
||||
return fd;
|
||||
else
|
||||
return dfd;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::GetFlags(int FileDescriptor)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(FileDescriptor);
|
||||
if (fd == nullfd)
|
||||
{
|
||||
debug("invalid fd %d", FileDescriptor);
|
||||
return -EBADF;
|
||||
}
|
||||
auto it = this->FileMap.find(FileDescriptor);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", FileDescriptor);
|
||||
|
||||
return fd.Flags;
|
||||
return it->second.Flags;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::SetFlags(int FileDescriptor, int Flags)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(FileDescriptor);
|
||||
if (fd == nullfd)
|
||||
{
|
||||
debug("invalid fd %d", FileDescriptor);
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
fd.Flags = Flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::ProbeMode(mode_t Mode, int Flags)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
auto it = this->FileMap.find(FileDescriptor);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", FileDescriptor);
|
||||
|
||||
it->second.Flags = Flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -178,39 +52,56 @@ namespace vfs
|
||||
{
|
||||
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)
|
||||
{
|
||||
int ret;
|
||||
bool absolute = cwk_path_is_absolute(AbsolutePath);
|
||||
new Node(pcb->CurrentWorkingDirectory,
|
||||
AbsolutePath, NodeType::FILE,
|
||||
absolute, fs, &ret);
|
||||
|
||||
if (ret == -EEXIST)
|
||||
FileNode *ret = fs->Create(pcb->CWD, AbsolutePath, Mode);
|
||||
if (Flags & O_EXCL && ret == nullptr)
|
||||
{
|
||||
debug("%s: File already exists, continuing...",
|
||||
AbsolutePath);
|
||||
}
|
||||
else if (Flags & O_EXCL)
|
||||
{
|
||||
debug("%s: File already exists, returning EEXIST",
|
||||
debug("%s: File already exists?, returning EEXIST",
|
||||
AbsolutePath);
|
||||
return -EEXIST;
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
error("Failed to create file %s: %d",
|
||||
AbsolutePath, ret);
|
||||
assert(ret < 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (Flags & O_TRUNC)
|
||||
{
|
||||
fixme("O_TRUNC");
|
||||
}
|
||||
|
||||
if (Flags & O_CLOEXEC)
|
||||
@ -218,166 +109,88 @@ namespace vfs
|
||||
fixme("O_CLOEXEC");
|
||||
}
|
||||
|
||||
RefNode *File = fs->Open(AbsolutePath,
|
||||
pcb->CurrentWorkingDirectory);
|
||||
FileNode *File = fs->GetByPath(AbsolutePath, pcb->CWD);
|
||||
|
||||
if (!File)
|
||||
{
|
||||
error("Failed to open file %s",
|
||||
AbsolutePath);
|
||||
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);
|
||||
File->seek(0, SEEK_END);
|
||||
struct kstat stat;
|
||||
File->Stat(&stat);
|
||||
fd.Offset = File->Seek(stat.Size);
|
||||
}
|
||||
|
||||
Fildes fd = {.Descriptor = GetFreeFileDescriptor()};
|
||||
|
||||
if (fd.Descriptor < 0)
|
||||
return -EMFILE;
|
||||
|
||||
fd.Mode = Mode;
|
||||
fd.Flags = Flags;
|
||||
fd.Handle = File;
|
||||
fd.Node = File;
|
||||
|
||||
FileDescriptors.push_back(fd);
|
||||
int fdn = this->GetFreeFileDescriptor();
|
||||
if (fdn < 0)
|
||||
return fdn;
|
||||
|
||||
char FileName[64];
|
||||
itoa(fd.Descriptor, FileName, 10);
|
||||
assert(fs->CreateLink(FileName, AbsolutePath, this->fdDir) != nullptr);
|
||||
this->FileMap.insert({fdn, fd});
|
||||
|
||||
int rfd = File->node->open(Flags, Mode);
|
||||
if (rfd <= 0)
|
||||
return fd.Descriptor;
|
||||
else
|
||||
return rfd;
|
||||
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)
|
||||
{
|
||||
forItr(itr, FileDescriptors)
|
||||
{
|
||||
if (itr->Descriptor == FileDescriptor)
|
||||
{
|
||||
FileDescriptors.erase(itr);
|
||||
auto it = this->FileMap.find(FileDescriptor);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", FileDescriptor);
|
||||
|
||||
char FileName[64];
|
||||
itoa(FileDescriptor, FileName, 10);
|
||||
fs->Delete(FileName, false, this->fdDir);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
forItr(itr, FildesDuplicates)
|
||||
{
|
||||
if (itr->Descriptor == FileDescriptor)
|
||||
{
|
||||
FildesDuplicates.erase(itr);
|
||||
|
||||
char FileName[64];
|
||||
itoa(FileDescriptor, FileName, 10);
|
||||
fs->Delete(FileName, false, this->fdDir);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EBADF;
|
||||
fs->Remove(it->second.Node);
|
||||
this->FileMap.erase(it);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::GetFreeFileDescriptor()
|
||||
{
|
||||
int i = 0;
|
||||
while (true)
|
||||
Tasking::PCB *pcb = thisProcess;
|
||||
|
||||
for (size_t i = 0; i < pcb->Limits.OpenFiles; i++)
|
||||
{
|
||||
bool Found = false;
|
||||
foreach (auto fd in FileDescriptors)
|
||||
{
|
||||
if (fd.Descriptor == i)
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Found)
|
||||
{
|
||||
foreach (auto fd in FildesDuplicates)
|
||||
{
|
||||
if (fd.Descriptor == i)
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Found)
|
||||
auto it = this->FileMap.find(i);
|
||||
if (it == this->FileMap.end())
|
||||
return i;
|
||||
i++;
|
||||
}
|
||||
|
||||
return -EMFILE;
|
||||
}
|
||||
|
||||
const char *FileDescriptorTable::GetAbsolutePath(int FileDescriptor)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(FileDescriptor);
|
||||
if (fd == nullfd)
|
||||
return "";
|
||||
|
||||
Node *node = fd.Handle->node;
|
||||
const char *path = new char[strlen(node->FullPath) + 1];
|
||||
strcpy((char *)path, node->FullPath);
|
||||
return path;
|
||||
}
|
||||
|
||||
RefNode *FileDescriptorTable::GetRefNode(int FileDescriptor)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(FileDescriptor);
|
||||
if (fd == nullfd)
|
||||
return nullptr;
|
||||
|
||||
return fd.Handle;
|
||||
}
|
||||
|
||||
void FileDescriptorTable::Fork(FileDescriptorTable *Parent)
|
||||
{
|
||||
foreach (auto &fd in Parent->FileDescriptors)
|
||||
this->FileMap = Parent->FileMap;
|
||||
|
||||
for (const auto &fd : this->FileMap)
|
||||
{
|
||||
debug("Forking fd: %d", fd.Descriptor);
|
||||
RefNode *node = fs->Open(fd.Handle->node->FullPath,
|
||||
thisProcess->CurrentWorkingDirectory);
|
||||
assert(node != nullptr);
|
||||
|
||||
Fildes new_fd;
|
||||
new_fd.Descriptor = fd.Descriptor;
|
||||
new_fd.Flags = fd.Flags;
|
||||
new_fd.Mode = fd.Mode;
|
||||
new_fd.Handle = node;
|
||||
this->FileDescriptors.push_back(new_fd);
|
||||
}
|
||||
|
||||
foreach (auto &fd in Parent->FildesDuplicates)
|
||||
{
|
||||
debug("Forking duplicated fd: %d", fd.Descriptor);
|
||||
RefNode *node = fs->Open(fd.Handle->node->FullPath,
|
||||
thisProcess->CurrentWorkingDirectory);
|
||||
assert(node != nullptr);
|
||||
|
||||
Fildes new_fd;
|
||||
new_fd.Descriptor = fd.Descriptor;
|
||||
new_fd.Flags = fd.Flags;
|
||||
new_fd.Mode = fd.Mode;
|
||||
new_fd.Handle = node;
|
||||
this->FildesDuplicates.push_back(new_fd);
|
||||
if (fd.second.Flags & O_CLOEXEC)
|
||||
{
|
||||
debug("O_CLOEXEC flag set, removing fd %d", fd.first);
|
||||
this->FileMap.erase(fd.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int FileDescriptorTable::_open(const char *pathname, int flags,
|
||||
mode_t mode)
|
||||
int FileDescriptorTable::usr_open(const char *pathname, int flags, mode_t mode)
|
||||
{
|
||||
if (pathname == nullptr)
|
||||
return -EFAULT;
|
||||
@ -385,271 +198,181 @@ namespace vfs
|
||||
return AddFileDescriptor(pathname, mode, flags);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::_creat(const char *pathname, mode_t mode)
|
||||
int FileDescriptorTable::usr_creat(const char *pathname, mode_t mode)
|
||||
{
|
||||
return _open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
||||
return usr_open(pathname, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
||||
}
|
||||
|
||||
ssize_t FileDescriptorTable::_read(int _fd, void *buf, size_t count)
|
||||
ssize_t FileDescriptorTable::usr_read(int fd, void *buf, size_t count)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(_fd);
|
||||
if (fd == nullfd)
|
||||
{
|
||||
debug("invalid fd %d", _fd);
|
||||
return -EBADF;
|
||||
}
|
||||
auto it = this->FileMap.find(fd);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", fd);
|
||||
|
||||
return fd.Handle->read((uint8_t *)buf, count);
|
||||
return it->second.Node->Read(buf, count, it->second.Offset);
|
||||
}
|
||||
|
||||
ssize_t FileDescriptorTable::_write(int _fd, const void *buf,
|
||||
size_t count)
|
||||
ssize_t FileDescriptorTable::usr_write(int fd, const void *buf, size_t count)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(_fd);
|
||||
if (fd == nullfd)
|
||||
{
|
||||
debug("invalid fd %d", _fd);
|
||||
return -EBADF;
|
||||
}
|
||||
auto it = this->FileMap.find(fd);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", fd);
|
||||
|
||||
return fd.Handle->write((uint8_t *)buf, count);
|
||||
return it->second.Node->Write(buf, count, it->second.Offset);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::_close(int _fd)
|
||||
int FileDescriptorTable::usr_close(int fd)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(_fd);
|
||||
if (fd == nullfd)
|
||||
{
|
||||
debug("invalid fd %d", _fd);
|
||||
return -EBADF;
|
||||
}
|
||||
auto it = this->FileMap.find(fd);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", fd);
|
||||
|
||||
if (RemoveFileDescriptor(_fd) < 0)
|
||||
{
|
||||
debug("invalid fd %d", _fd);
|
||||
return -EBADF;
|
||||
}
|
||||
return RemoveFileDescriptor(fd);
|
||||
}
|
||||
|
||||
bool Found = false;
|
||||
foreach (auto dfd in FileDescriptors)
|
||||
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)
|
||||
{
|
||||
if (dfd.Handle == fd.Handle)
|
||||
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
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
it->second.Node->Stat(&stat);
|
||||
newOffset = it->second.Node->Seek(stat.Size + offset);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!Found)
|
||||
foreach (auto dfd in FildesDuplicates)
|
||||
{
|
||||
if (dfd.Handle == fd.Handle)
|
||||
{
|
||||
Found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the file descriptor is a duplicate,
|
||||
we don't need to close the handle,
|
||||
because it's a duplicate of another
|
||||
file descriptor. */
|
||||
if (!Found)
|
||||
delete fd.Handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
off_t FileDescriptorTable::_lseek(int _fd, off_t offset, int whence)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(_fd);
|
||||
if (fd == nullfd)
|
||||
{
|
||||
debug("invalid fd %d", _fd);
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
return fd.Handle->seek(offset, whence);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::_stat(const char *pathname,
|
||||
struct kstat *statbuf)
|
||||
{
|
||||
if (pathname == nullptr)
|
||||
default:
|
||||
return -EINVAL;
|
||||
|
||||
RefNode *file = fs->Open(pathname,
|
||||
thisProcess->CurrentWorkingDirectory);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
error("Failed to open file %s",
|
||||
pathname);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
Node *node = file->node;
|
||||
statbuf->st_dev = 0; /* FIXME: stub */
|
||||
statbuf->st_ino = node->IndexNode;
|
||||
statbuf->st_mode = node->Type | (node->Mode & ~S_IFMT);
|
||||
statbuf->st_nlink = 0; /* FIXME: stub */
|
||||
statbuf->st_uid = node->UserIdentifier;
|
||||
statbuf->st_gid = node->GroupIdentifier;
|
||||
statbuf->st_rdev = 0; /* FIXME: stub */
|
||||
statbuf->st_size = node->Size;
|
||||
statbuf->st_atime = node->AccessTime;
|
||||
statbuf->st_mtime = node->ModifyTime;
|
||||
statbuf->st_ctime = node->ChangeTime;
|
||||
statbuf->st_blksize = 0; /* FIXME: stub */
|
||||
statbuf->st_blocks = 0; /* FIXME: stub */
|
||||
statbuf->st_attr = 0; /* FIXME: stub */
|
||||
return 0;
|
||||
return newOffset;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::_fstat(int _fd, struct kstat *statbuf)
|
||||
int FileDescriptorTable::usr_stat(const char *pathname,
|
||||
struct kstat *statbuf)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(_fd);
|
||||
if (fd == nullfd)
|
||||
FileNode *node = fs->GetByPath(pathname, nullptr);
|
||||
if (node == nullptr)
|
||||
ReturnLogError(-ENOENT, "Failed to find %s", pathname);
|
||||
|
||||
if (node->IsSymbolicLink())
|
||||
{
|
||||
debug("invalid fd %d", _fd);
|
||||
return -EBADF;
|
||||
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);
|
||||
}
|
||||
|
||||
Node *node = fd.Handle->node;
|
||||
statbuf->st_dev = 0; /* FIXME: stub */
|
||||
statbuf->st_ino = node->IndexNode;
|
||||
statbuf->st_mode = node->Type | (node->Mode & ~S_IFMT);
|
||||
statbuf->st_nlink = 0; /* FIXME: stub */
|
||||
statbuf->st_uid = node->UserIdentifier;
|
||||
statbuf->st_gid = node->GroupIdentifier;
|
||||
statbuf->st_rdev = 0; /* FIXME: stub */
|
||||
statbuf->st_size = node->Size;
|
||||
statbuf->st_atime = node->AccessTime;
|
||||
statbuf->st_mtime = node->ModifyTime;
|
||||
statbuf->st_ctime = node->ChangeTime;
|
||||
statbuf->st_blksize = 0; /* FIXME: stub */
|
||||
statbuf->st_blocks = 0; /* FIXME: stub */
|
||||
statbuf->st_attr = 0; /* FIXME: stub */
|
||||
return 0;
|
||||
return node->Stat(statbuf);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::_lstat(const char *pathname,
|
||||
struct kstat *statbuf)
|
||||
int FileDescriptorTable::usr_fstat(int fd, struct kstat *statbuf)
|
||||
{
|
||||
if (pathname == nullptr)
|
||||
return -EINVAL;
|
||||
auto it = this->FileMap.find(fd);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", fd);
|
||||
|
||||
RefNode *file = fs->Open(pathname,
|
||||
thisProcess->CurrentWorkingDirectory);
|
||||
vfs::FileDescriptorTable::Fildes &fildes = it->second;
|
||||
|
||||
if (!file)
|
||||
{
|
||||
error("Failed to open file %s",
|
||||
pathname);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
Node *node = file->node;
|
||||
statbuf->st_dev = 0; /* FIXME: stub */
|
||||
statbuf->st_ino = node->IndexNode;
|
||||
statbuf->st_mode = node->Type | (node->Mode & ~S_IFMT);
|
||||
statbuf->st_nlink = 0; /* FIXME: stub */
|
||||
statbuf->st_uid = node->UserIdentifier;
|
||||
statbuf->st_gid = node->GroupIdentifier;
|
||||
statbuf->st_rdev = 0; /* FIXME: stub */
|
||||
statbuf->st_size = node->Size;
|
||||
statbuf->st_atime = node->AccessTime;
|
||||
statbuf->st_mtime = node->ModifyTime;
|
||||
statbuf->st_ctime = node->ChangeTime;
|
||||
statbuf->st_blksize = 0; /* FIXME: stub */
|
||||
statbuf->st_blocks = 0; /* FIXME: stub */
|
||||
statbuf->st_attr = 0; /* FIXME: stub */
|
||||
return 0;
|
||||
return fildes.Node->Stat(statbuf);
|
||||
}
|
||||
|
||||
int FileDescriptorTable::_dup(int oldfd)
|
||||
int FileDescriptorTable::usr_lstat(const char *pathname,
|
||||
struct kstat *statbuf)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(oldfd);
|
||||
if (fd == nullfd)
|
||||
{
|
||||
debug("invalid fd %d", oldfd);
|
||||
return -EBADF;
|
||||
}
|
||||
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.Handle = fd.Handle;
|
||||
new_dfd.Mode = fd.Mode;
|
||||
new_dfd.Node = it->second.Node;
|
||||
new_dfd.Mode = it->second.Mode;
|
||||
|
||||
new_dfd.Descriptor = newfd;
|
||||
this->FildesDuplicates.push_back(new_dfd);
|
||||
debug("Duplicated file descriptor %d to %d",
|
||||
oldfd, newfd);
|
||||
this->FileMap.insert({newfd, new_dfd});
|
||||
|
||||
debug("Duplicated file descriptor %d to %d", oldfd, newfd);
|
||||
return newfd;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::_dup2(int oldfd, int newfd)
|
||||
int FileDescriptorTable::usr_dup2(int oldfd, int newfd)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(oldfd);
|
||||
if (fd == nullfd)
|
||||
{
|
||||
debug("invalid fd %d", oldfd);
|
||||
return -EBADF;
|
||||
}
|
||||
|
||||
if (newfd < 0)
|
||||
{
|
||||
debug("invalid fd %d", newfd);
|
||||
return -EBADF;
|
||||
}
|
||||
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->_close(newfd);
|
||||
/* Even if it's not valid we ignore it. */
|
||||
this->usr_close(newfd);
|
||||
|
||||
Fildes new_dfd{};
|
||||
new_dfd.Handle = fd.Handle;
|
||||
new_dfd.Mode = fd.Mode;
|
||||
new_dfd.Node = it->second.Node;
|
||||
new_dfd.Mode = it->second.Mode;
|
||||
|
||||
new_dfd.Descriptor = newfd;
|
||||
this->FildesDuplicates.push_back(new_dfd);
|
||||
debug("Duplicated file descriptor %d to %d",
|
||||
oldfd, newfd);
|
||||
this->FileMap.insert({newfd, new_dfd});
|
||||
debug("Duplicated file descriptor %d to %d", oldfd, newfd);
|
||||
return newfd;
|
||||
}
|
||||
|
||||
int FileDescriptorTable::_ioctl(int _fd, unsigned long request, void *argp)
|
||||
int FileDescriptorTable::usr_ioctl(int fd, unsigned long request, void *argp)
|
||||
{
|
||||
Fildes &fd = this->GetDescriptor(_fd);
|
||||
if (fd == nullfd)
|
||||
{
|
||||
debug("invalid fd %d", _fd);
|
||||
return -EBADF;
|
||||
}
|
||||
auto it = this->FileMap.find(fd);
|
||||
if (it == this->FileMap.end())
|
||||
ReturnLogError(-EBADF, "Invalid fd %d", fd);
|
||||
|
||||
return fd.Handle->ioctl(request, argp);
|
||||
return it->second.Node->Ioctl(request, argp);
|
||||
}
|
||||
|
||||
FileDescriptorTable::FileDescriptorTable(void *Owner)
|
||||
FileDescriptorTable::FileDescriptorTable(void *_Owner)
|
||||
: Owner(_Owner)
|
||||
{
|
||||
debug("+ %#lx", this);
|
||||
this->fdDir = fs->Create("fd", vfs::NodeType::DIRECTORY,
|
||||
((Tasking::PCB *)Owner));
|
||||
}
|
||||
|
||||
FileDescriptorTable::~FileDescriptorTable()
|
||||
{
|
||||
debug("- %#lx", this);
|
||||
foreach (auto &fd in FileDescriptors)
|
||||
{
|
||||
debug("Removing fd: %d", fd.Descriptor);
|
||||
this->RemoveFileDescriptor(fd.Descriptor);
|
||||
delete fd.Handle;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
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/mounts.hpp>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
size_t NullDevice::read(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t NullDevice::write(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
|
||||
NullDevice::NullDevice() : Node(DevFS, "null", CHARDEVICE) {}
|
||||
NullDevice::~NullDevice() {}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
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/mounts.hpp>
|
||||
#include <rand.hpp>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
size_t RandomDevice::read(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
if (Size <= 0)
|
||||
return 0;
|
||||
|
||||
uint64_t *buf = (uint64_t *)Buffer;
|
||||
for (size_t i = 0; i < Size / sizeof(uint64_t); i++)
|
||||
buf[i] = Random::rand64();
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t RandomDevice::write(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
|
||||
RandomDevice::RandomDevice() : Node(DevFS, "random", CHARDEVICE) {}
|
||||
RandomDevice::~RandomDevice() {}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
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/mounts.hpp>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
vfsRoot::vfsRoot(const char *Name, Virtual *vfs_ctx)
|
||||
: Node(nullptr,
|
||||
Name,
|
||||
MOUNTPOINT)
|
||||
{
|
||||
this->vFS = fs;
|
||||
vfs_ctx->GetRootNode()->Children.push_back(this);
|
||||
}
|
||||
}
|
@ -1,170 +0,0 @@
|
||||
/*
|
||||
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/mounts.hpp>
|
||||
#include <filesystem/ioctl.hpp>
|
||||
#include <smp.hpp>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../../../kernel.h"
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
size_t KConDevice::read(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
return DriverManager->InputKeyboardDev->read(Buffer, Size, Offset);
|
||||
}
|
||||
|
||||
size_t KConDevice::write(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
if (Offset != 0)
|
||||
fixme("Offset is %d", Offset);
|
||||
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
putchar(((char *)Buffer)[i]);
|
||||
|
||||
if (!Config.Quiet)
|
||||
Display->UpdateBuffer();
|
||||
return Size;
|
||||
}
|
||||
|
||||
int KConDevice::ioctl(unsigned long Request, void *Argp)
|
||||
{
|
||||
static_assert(sizeof(struct termios) < PAGE_SIZE);
|
||||
|
||||
void *pArgp = thisProcess->PageTable->Get(Argp);
|
||||
switch (Request)
|
||||
{
|
||||
case TCGETS:
|
||||
{
|
||||
struct termios *t = (struct termios *)pArgp;
|
||||
memcpy(t, &this->term, sizeof(struct termios));
|
||||
break;
|
||||
}
|
||||
case TCSETS:
|
||||
{
|
||||
struct termios *t = (struct termios *)pArgp;
|
||||
memcpy(&this->term, t, sizeof(struct termios));
|
||||
break;
|
||||
}
|
||||
case TIOCGPGRP:
|
||||
{
|
||||
*((pid_t *)Argp) = 0;
|
||||
fixme("TIOCGPGRP not implemented");
|
||||
return 0;
|
||||
}
|
||||
case TIOCSPGRP:
|
||||
{
|
||||
*((pid_t *)Argp) = 0;
|
||||
fixme("TIOCSPGRP not implemented");
|
||||
return 0;
|
||||
}
|
||||
case TIOCGWINSZ:
|
||||
{
|
||||
struct winsize *ws = (struct winsize *)pArgp;
|
||||
memcpy(ws, &this->termSize, sizeof(struct winsize));
|
||||
break;
|
||||
}
|
||||
case TIOCSWINSZ:
|
||||
{
|
||||
struct winsize *ws = (struct winsize *)pArgp;
|
||||
memcpy(&this->termSize, ws, sizeof(struct winsize));
|
||||
break;
|
||||
}
|
||||
case TCSETSW:
|
||||
case TCSETSF:
|
||||
case TCGETA:
|
||||
case TCSETA:
|
||||
case TCSETAW:
|
||||
case TCSETAF:
|
||||
case TCSBRK:
|
||||
case TCXONC:
|
||||
case TCFLSH:
|
||||
case TIOCEXCL:
|
||||
case TIOCNXCL:
|
||||
case TIOCSCTTY:
|
||||
case TIOCOUTQ:
|
||||
case TIOCSTI:
|
||||
case TIOCMGET:
|
||||
case TIOCMBIS:
|
||||
case TIOCMBIC:
|
||||
case TIOCMSET:
|
||||
{
|
||||
fixme("ioctl %#lx not implemented", Request);
|
||||
return -ENOSYS;
|
||||
}
|
||||
case TIOCGPTN:
|
||||
case 0xffffffff80045430: /* FIXME: ???? */
|
||||
{
|
||||
int *n = (int *)pArgp;
|
||||
*n = -1;
|
||||
break;
|
||||
}
|
||||
case TIOCSPTLCK:
|
||||
{
|
||||
int *n = (int *)pArgp;
|
||||
*n = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
debug("Unknown ioctl %#lx", Request);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
KConDevice::KConDevice() : Node(DevFS, "kcon", CHARDEVICE)
|
||||
{
|
||||
/*
|
||||
- ICRNL - Map Carriage Return to New Line
|
||||
- IXON - Enable XON/XOFF flow control
|
||||
|
||||
- OPOST - Enable output processing
|
||||
- ONLCR - Map New Line to Carriage Return - New Line
|
||||
|
||||
- CS8 - 8-bit characters
|
||||
- CREAD - Enable receiver
|
||||
- HUPCL - Hang up on last close
|
||||
|
||||
- ECHO - Echo input characters
|
||||
- ICANON - Enable canonical input (enable line editing)
|
||||
*/
|
||||
this->term.c_iflag = /*ICRNL |*/ IXON;
|
||||
this->term.c_oflag = OPOST | ONLCR;
|
||||
this->term.c_cflag = CS8 | CREAD | HUPCL;
|
||||
this->term.c_lflag = ECHO | ICANON;
|
||||
this->term.c_cc[VEOF] = 0x04; /* ^D */
|
||||
this->term.c_cc[VEOL] = 0x00; /* NUL */
|
||||
this->term.c_cc[VERASE] = 0x7f; /* DEL */
|
||||
this->term.c_cc[VINTR] = 0x03; /* ^C */
|
||||
this->term.c_cc[VKILL] = 0x15; /* ^U */
|
||||
this->term.c_cc[VMIN] = 1; /* Minimum number of characters for non-canonical read */
|
||||
this->term.c_cc[VQUIT] = 0x1c; /* ^\ */
|
||||
this->term.c_cc[VSTART] = 0x11; /* ^Q */
|
||||
this->term.c_cc[VSTOP] = 0x13; /* ^S */
|
||||
this->term.c_cc[VSUSP] = 0x1a; /* ^Z */
|
||||
this->term.c_cc[VTIME] = 0; /* Timeout for non-canonical read */
|
||||
this->term.c_cc[VWERASE] = 0x17; /* ^W */
|
||||
}
|
||||
|
||||
KConDevice::~KConDevice()
|
||||
{
|
||||
}
|
||||
}
|
@ -24,69 +24,67 @@
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
int PTMXDevice::open(int Flags, mode_t Mode)
|
||||
int PTMXDevice::Open(Inode *Node, int Flags, mode_t Mode, struct Inode *Result)
|
||||
{
|
||||
SmartLock(PTMXLock);
|
||||
int id = -1;
|
||||
for (size_t i = 0; i < ptysId.Size; i++)
|
||||
{
|
||||
if (unlikely(ptysId.Buffer[i] == false))
|
||||
{
|
||||
id = int(i);
|
||||
ptysId.Buffer[i] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// SmartLock(PTMXLock);
|
||||
|
||||
if (id == -1)
|
||||
return -ENFILE;
|
||||
// int ptyID = -1;
|
||||
// for (int i = 0; i < (int)ptysId.Size; i++)
|
||||
// {
|
||||
// if (ptysId.Buffer[i])
|
||||
// continue;
|
||||
|
||||
PTYDevice *pty = new PTYDevice(pts, id);
|
||||
ptysList.push_back(pty);
|
||||
return pty->OpenMaster(Flags, Mode);
|
||||
// ptyID = i;
|
||||
// ptysId.Buffer[i] = true;
|
||||
// break;
|
||||
// }
|
||||
|
||||
// if (ptyID == -1)
|
||||
// return -ENFILE;
|
||||
|
||||
// PTYDevice *pty = new PTYDevice(pts, ptyID);
|
||||
// ptysList.insert(std::make_pair(ptyID, pty));
|
||||
// // return pty->OpenMaster(Flags, Mode);
|
||||
assert(!"Function not implemented");
|
||||
}
|
||||
|
||||
void PTMXDevice::RemovePTY(int fd, Tasking::PCB *pcb)
|
||||
int PTMXDevice::Close(struct Inode *Node)
|
||||
{
|
||||
SmartLock(PTMXLock);
|
||||
if (!pcb)
|
||||
pcb = thisProcess;
|
||||
assert(pcb != nullptr);
|
||||
|
||||
FileDescriptorTable *fdt = pcb->FileDescriptors;
|
||||
RefNode *node = fdt->GetRefNode(fd);
|
||||
|
||||
assert(node->SpecialData != nullptr);
|
||||
PTYDevice *pty = (PTYDevice *)node->SpecialData;
|
||||
int id = pty->ptyId;
|
||||
|
||||
forItr(itr, ptysList)
|
||||
{
|
||||
if (*itr != pty)
|
||||
continue;
|
||||
|
||||
ptysList.erase(itr);
|
||||
delete *itr;
|
||||
break;
|
||||
}
|
||||
ptysId.Buffer[id] = false;
|
||||
PTYDevice *pty = ptysList.at(Node->Index);
|
||||
ptysList.erase(Node->Index);
|
||||
assert(!"Function not implemented");
|
||||
}
|
||||
|
||||
PTMXDevice::PTMXDevice() : Node(DevFS, "ptmx", CHARDEVICE)
|
||||
PTMXDevice::PTMXDevice()
|
||||
{
|
||||
this->Mode = 0644;
|
||||
this->UserIdentifier = 0;
|
||||
this->GroupIdentifier = 0;
|
||||
pts = new Node(DevFS, "pts", DIRECTORY);
|
||||
|
||||
ptysId.Buffer = new uint8_t[0x1000];
|
||||
ptysId.Size = 0x1000;
|
||||
// /* c rw- rw- rw- */
|
||||
// mode_t mode = S_IRUSR | S_IWUSR |
|
||||
// S_IRGRP | S_IWGRP |
|
||||
// S_IROTH | S_IWOTH |
|
||||
// S_IFCHR;
|
||||
|
||||
// ptmx = fs->Create(ptmx, "pts", mode);
|
||||
// assert(!"Function not implemented");
|
||||
// // ptmx->SetDevice(5, 2);
|
||||
|
||||
// /* d rwx r-x r-x */
|
||||
// mode_t ptsMode = S_IRWXU |
|
||||
// S_IRGRP | S_IXGRP |
|
||||
// S_IROTH | S_IXOTH |
|
||||
// S_IFDIR;
|
||||
// pts = fs->Create(ptmx, "pts", ptsMode);
|
||||
// assert(pts != nullptr);
|
||||
|
||||
// ptysId.Buffer = new uint8_t[0x1000];
|
||||
// ptysId.Size = 0x1000;
|
||||
}
|
||||
|
||||
PTMXDevice::~PTMXDevice()
|
||||
{
|
||||
SmartLock(PTMXLock);
|
||||
delete pts;
|
||||
delete[] ptysId.Buffer;
|
||||
}
|
||||
}
|
||||
|
@ -24,157 +24,13 @@
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
int PTYDevice::open(int Flags, mode_t Mode)
|
||||
PTYDevice::PTYDevice(Inode *_pts, int _id)
|
||||
{
|
||||
stub;
|
||||
return -ENOSYS;
|
||||
}
|
||||
assert(!"Function not implemented");
|
||||
char nameBuffer[16];
|
||||
snprintf(nameBuffer, 16, "%d", id);
|
||||
// this->Name = strdup(nameBuffer);
|
||||
|
||||
int PTYDevice::close()
|
||||
{
|
||||
stub;
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
size_t PTYDevice::read(uint8_t *Buffer,
|
||||
size_t Size,
|
||||
off_t Offset)
|
||||
{
|
||||
if (this->isMaster)
|
||||
{
|
||||
if (MasterDev != nullptr)
|
||||
return MasterDev->read(Buffer, Size, Offset);
|
||||
else
|
||||
fixme("MasterDev is nullptr");
|
||||
}
|
||||
|
||||
if (this->SlaveDev == nullptr)
|
||||
{
|
||||
fixme("SlaveDev is nullptr");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return SlaveDev->read(Buffer, Size, Offset);
|
||||
}
|
||||
|
||||
size_t PTYDevice::write(uint8_t *Buffer,
|
||||
size_t Size,
|
||||
off_t Offset)
|
||||
{
|
||||
if (this->isMaster)
|
||||
{
|
||||
if (MasterDev != nullptr)
|
||||
return MasterDev->write(Buffer, Size, Offset);
|
||||
else
|
||||
fixme("MasterDev is nullptr");
|
||||
}
|
||||
|
||||
if (this->SlaveDev == nullptr)
|
||||
{
|
||||
fixme("SlaveDev is nullptr");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return SlaveDev->write(Buffer, Size, Offset);
|
||||
}
|
||||
|
||||
int PTYDevice::ioctl(unsigned long Request,
|
||||
void *Argp)
|
||||
{
|
||||
static_assert(sizeof(struct termios) < PAGE_SIZE);
|
||||
void *pArgp = thisProcess->PageTable->Get(Argp);
|
||||
|
||||
switch (Request)
|
||||
{
|
||||
case TCGETS:
|
||||
{
|
||||
struct termios *t = (struct termios *)pArgp;
|
||||
memcpy(t, &this->term, sizeof(struct termios));
|
||||
break;
|
||||
}
|
||||
case TCSETS:
|
||||
{
|
||||
struct termios *t = (struct termios *)pArgp;
|
||||
memcpy(&this->term, t, sizeof(struct termios));
|
||||
break;
|
||||
}
|
||||
case TCSETSW:
|
||||
case TCSETSF:
|
||||
case TCGETA:
|
||||
case TCSETA:
|
||||
case TCSETAW:
|
||||
case TCSETAF:
|
||||
case TCSBRK:
|
||||
case TCXONC:
|
||||
case TCFLSH:
|
||||
case TIOCEXCL:
|
||||
case TIOCNXCL:
|
||||
case TIOCSCTTY:
|
||||
case TIOCGPGRP:
|
||||
case TIOCSPGRP:
|
||||
case TIOCOUTQ:
|
||||
case TIOCSTI:
|
||||
{
|
||||
fixme("ioctl %#lx not implemented", Request);
|
||||
return -ENOSYS;
|
||||
}
|
||||
case TIOCGWINSZ:
|
||||
{
|
||||
struct winsize *ws = (struct winsize *)pArgp;
|
||||
memcpy(ws, &this->termSize, sizeof(struct winsize));
|
||||
break;
|
||||
}
|
||||
case TIOCSWINSZ:
|
||||
{
|
||||
struct winsize *ws = (struct winsize *)pArgp;
|
||||
memcpy(&this->termSize, ws, sizeof(struct winsize));
|
||||
break;
|
||||
}
|
||||
case TIOCMGET:
|
||||
case TIOCMBIS:
|
||||
case TIOCMBIC:
|
||||
case TIOCMSET:
|
||||
{
|
||||
fixme("ioctl %#lx not implemented", Request);
|
||||
return -ENOSYS;
|
||||
}
|
||||
case TIOCGPTN:
|
||||
case 0xffffffff80045430: /* FIXME: ???? */
|
||||
{
|
||||
int *n = (int *)pArgp;
|
||||
*n = this->id;
|
||||
break;
|
||||
}
|
||||
case TIOCSPTLCK:
|
||||
{
|
||||
int *n = (int *)pArgp;
|
||||
*n = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
debug("Unknown ioctl %#lx", Request);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PTYDevice::OpenMaster(int Flags, mode_t Mode)
|
||||
{
|
||||
debug("Opening master PTY device %s", this->FullPath);
|
||||
FileDescriptorTable *fdt = thisProcess->FileDescriptors;
|
||||
int rfd = fdt->_open(this->FullPath, Flags, Mode);
|
||||
debug("Opened master PTY device %s with fd %d",
|
||||
this->FullPath, rfd);
|
||||
return rfd;
|
||||
}
|
||||
|
||||
PTYDevice::PTYDevice(Node *pts, int id) : Node(pts,
|
||||
std::to_string(id),
|
||||
CHARDEVICE)
|
||||
{
|
||||
/*
|
||||
- ICRNL - Map Carriage Return to New Line
|
||||
- IXON - Enable XON/XOFF flow control
|
||||
@ -206,7 +62,7 @@ namespace vfs
|
||||
this->term.c_cc[VTIME] = 0; /* Timeout for non-canonical read */
|
||||
this->term.c_cc[VWERASE] = 0x17; /* ^W */
|
||||
|
||||
debug("Created PTY device %s", FullPath);
|
||||
// debug("Created PTY device %s", this->Name);
|
||||
}
|
||||
|
||||
PTYDevice::~PTYDevice() {}
|
||||
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
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/mounts.hpp>
|
||||
#include <filesystem/ioctl.hpp>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../../../kernel.h"
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
size_t MasterPTY::read(uint8_t *Buffer,
|
||||
size_t Size,
|
||||
off_t Offset)
|
||||
{
|
||||
fixme("%.*s", Size, Buffer);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
size_t MasterPTY::write(uint8_t *Buffer,
|
||||
size_t Size,
|
||||
off_t Offset)
|
||||
{
|
||||
fixme("%.*s", Size, Buffer);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
MasterPTY::MasterPTY()
|
||||
{
|
||||
}
|
||||
|
||||
MasterPTY::~MasterPTY()
|
||||
{
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
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/mounts.hpp>
|
||||
#include <filesystem/ioctl.hpp>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../../../kernel.h"
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
size_t SlavePTY::read(uint8_t *Buffer,
|
||||
size_t Size,
|
||||
off_t Offset)
|
||||
{
|
||||
fixme("%.*s", Size, Buffer);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
size_t SlavePTY::write(uint8_t *Buffer,
|
||||
size_t Size,
|
||||
off_t Offset)
|
||||
{
|
||||
fixme("%.*s", Size, Buffer);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
SlavePTY::SlavePTY()
|
||||
{
|
||||
}
|
||||
|
||||
SlavePTY::~SlavePTY()
|
||||
{
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
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/mounts.hpp>
|
||||
#include <filesystem/ioctl.hpp>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../../../kernel.h"
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
size_t TTYDevice::write(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
putchar(((char *)Buffer)[i]);
|
||||
|
||||
Display->UpdateBuffer(); /* FIXME: stub */
|
||||
return Size;
|
||||
}
|
||||
|
||||
int TTYDevice::ioctl(unsigned long Request, void *Argp)
|
||||
{
|
||||
switch (Request)
|
||||
{
|
||||
case TIOCGWINSZ:
|
||||
{
|
||||
struct winsize *ws = (struct winsize *)Argp;
|
||||
Video::FontInfo fi = Display->GetCurrentFont()->GetInfo();
|
||||
|
||||
fixme("TIOCGWINSZ: stub");
|
||||
ws->ws_xpixel = uint16_t(Display->GetWidth);
|
||||
ws->ws_ypixel = uint16_t(Display->GetHeight);
|
||||
ws->ws_col = uint16_t(Display->GetWidth / fi.Width);
|
||||
ws->ws_row = uint16_t(Display->GetHeight / fi.Height);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fixme("Unknown request %#lx", Request);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TTYDevice::TTYDevice() : Node(DevFS, "tty", CHARDEVICE) {}
|
||||
|
||||
TTYDevice::~TTYDevice() {}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
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/mounts.hpp>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
using namespace vfs;
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
size_t ZeroDevice::read(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
if (Size <= 0)
|
||||
return 0;
|
||||
|
||||
memset(Buffer, 0, Size);
|
||||
return Size;
|
||||
}
|
||||
|
||||
size_t ZeroDevice::write(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
|
||||
ZeroDevice::ZeroDevice() : Node(DevFS, "zero", CHARDEVICE) {}
|
||||
ZeroDevice::~ZeroDevice() {}
|
||||
}
|
@ -17,416 +17,217 @@
|
||||
|
||||
#include <filesystem.hpp>
|
||||
|
||||
#include <smart_ptr.hpp>
|
||||
#include <convert.h>
|
||||
#include <printf.h>
|
||||
#include <rand.hpp>
|
||||
#include <cwalk.h>
|
||||
|
||||
#include "../kernel.h"
|
||||
|
||||
// show debug messages
|
||||
// #define DEBUG_FILESYSTEM 1
|
||||
|
||||
#ifdef DEBUG_FILESYSTEM
|
||||
#define vfsdbg(m, ...) debug(m, ##__VA_ARGS__)
|
||||
#else
|
||||
#define vfsdbg(m, ...)
|
||||
#endif
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
Node *Virtual::GetNodeFromPath_Unsafe(const char *Path, Node *Parent)
|
||||
{
|
||||
vfsdbg("GetNodeFromPath( Path: \"%s\" Parent: \"%s\" )",
|
||||
Path, Parent ? Parent->Name : "(null)");
|
||||
|
||||
if (strcmp(Path, "/") == 0)
|
||||
return FileSystemRoot->Children[0]; // 0 - filesystem root
|
||||
|
||||
if (strcmp(Path, ".") == 0)
|
||||
return Parent;
|
||||
|
||||
if (strcmp(Path, "..") == 0)
|
||||
{
|
||||
if (Parent)
|
||||
{
|
||||
if (Parent->Parent)
|
||||
return Parent->Parent;
|
||||
else
|
||||
return Parent;
|
||||
}
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node *ReturnNode = Parent;
|
||||
bool IsAbsolutePath = cwk_path_is_absolute(Path);
|
||||
|
||||
if (!ReturnNode)
|
||||
ReturnNode = FileSystemRoot->Children[0]; // 0 - filesystem root
|
||||
|
||||
if (IsAbsolutePath)
|
||||
ReturnNode = FileSystemRoot->Children[0]; // 0 - filesystem root
|
||||
|
||||
cwk_segment segment;
|
||||
if (unlikely(!cwk_path_get_first_segment(Path, &segment)))
|
||||
{
|
||||
error("Path doesn't have any segments.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
char *SegmentName = new char[segment.end - segment.begin + 1];
|
||||
memcpy(SegmentName, segment.begin, segment.end - segment.begin);
|
||||
vfsdbg("GetNodeFromPath()->SegmentName: \"%s\"", SegmentName);
|
||||
GetNodeFromPathNextParent:
|
||||
foreach (auto Child in ReturnNode->Children)
|
||||
{
|
||||
vfsdbg("comparing \"%s\" with \"%s\"",
|
||||
Child->Name, SegmentName);
|
||||
if (strcmp(Child->Name, SegmentName) == 0)
|
||||
{
|
||||
ReturnNode = Child;
|
||||
goto GetNodeFromPathNextParent;
|
||||
}
|
||||
}
|
||||
delete[] SegmentName;
|
||||
} while (cwk_path_get_next_segment(&segment));
|
||||
|
||||
const char *basename;
|
||||
cwk_path_get_basename(Path, &basename, nullptr);
|
||||
vfsdbg("BaseName: \"%s\" NodeName: \"%s\"",
|
||||
basename, ReturnNode->Name);
|
||||
|
||||
if (strcmp(basename, ReturnNode->Name) == 0)
|
||||
{
|
||||
vfsdbg("GetNodeFromPath()->\"%s\"", ReturnNode->Name);
|
||||
return ReturnNode;
|
||||
}
|
||||
|
||||
vfsdbg("GetNodeFromPath()->\"(null)\"");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node *Virtual::GetNodeFromPath(const char *Path, Node *Parent)
|
||||
{
|
||||
SmartLock(VirtualLock);
|
||||
return GetNodeFromPath_Unsafe(Path, Parent);
|
||||
}
|
||||
|
||||
bool Virtual::PathIsRelative(const char *Path)
|
||||
{
|
||||
vfsdbg("PathIsRelative( Path: \"%s\" )", Path);
|
||||
bool IsRelative = cwk_path_is_relative(Path);
|
||||
vfsdbg("PathIsRelative()->\"%s\"",
|
||||
IsRelative ? "true" : "false");
|
||||
return IsRelative;
|
||||
return cwk_path_is_relative(Path);
|
||||
}
|
||||
|
||||
Node *Virtual::GetParent(const char *Path, Node *Parent)
|
||||
dev_t Virtual::EarlyReserveDevice()
|
||||
{
|
||||
vfsdbg("GetParent( Path: \"%s\" Parent: \"%s\" )",
|
||||
Path, Parent ? Parent->Name : "(nil)");
|
||||
|
||||
if (Parent)
|
||||
{
|
||||
vfsdbg("GetParent()->\"%s\"", Parent->Name);
|
||||
return Parent;
|
||||
}
|
||||
|
||||
Parent = FileSystemRoot->Children[0];
|
||||
|
||||
size_t length;
|
||||
cwk_path_get_root(Path, &length);
|
||||
if (length > 0)
|
||||
{
|
||||
foreach (auto Child in FileSystemRoot->Children)
|
||||
{
|
||||
if (strcmp(Child->Name, Path) == 0)
|
||||
{
|
||||
Parent = Child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vfsdbg("GetParent()->\"%s\"", ParentNode->Name);
|
||||
return Parent;
|
||||
RegisterLock.store(true);
|
||||
size_t len = DeviceMap.size();
|
||||
return len;
|
||||
}
|
||||
|
||||
const char *Virtual::NormalizePath(const char *Path, Node *Parent)
|
||||
int Virtual::LateRegisterFileSystem(dev_t Device, FileSystemInfo *fsi, Inode *Root)
|
||||
{
|
||||
assert(Parent != nullptr);
|
||||
auto it = DeviceMap.find(Device);
|
||||
if (it != DeviceMap.end())
|
||||
ReturnLogError(-EEXIST, "Device %d already registered", Device);
|
||||
|
||||
vfsdbg("NormalizePath( Path: \"%s\" Parent: \"%s\" )",
|
||||
Path, Parent->Name);
|
||||
|
||||
size_t PathSize = strlen((char *)Path) + 1;
|
||||
char *NormalizedPath = new char[PathSize];
|
||||
|
||||
{
|
||||
Memory::SmartHeap sh(PathSize);
|
||||
memcpy(sh, (char *)Path, PathSize);
|
||||
cwk_path_normalize(sh, NormalizedPath, PathSize);
|
||||
}
|
||||
|
||||
const char *FinalPath;
|
||||
if (cwk_path_is_relative(NormalizedPath))
|
||||
{
|
||||
size_t PathSize = cwk_path_join(Parent->FullPath,
|
||||
NormalizedPath,
|
||||
nullptr, 0);
|
||||
|
||||
FinalPath = new char[PathSize + 1];
|
||||
cwk_path_join(Parent->FullPath, NormalizedPath,
|
||||
(char *)FinalPath, PathSize + 1);
|
||||
|
||||
delete[] NormalizedPath;
|
||||
}
|
||||
else
|
||||
FinalPath = NormalizedPath;
|
||||
|
||||
vfsdbg("NormalizePath()->\"%s\"", FinalPath);
|
||||
return FinalPath;
|
||||
}
|
||||
|
||||
bool Virtual::PathExists(const char *Path, Node *Parent)
|
||||
{
|
||||
if (isempty((char *)Path))
|
||||
{
|
||||
vfsdbg("PathExists()->PathIsEmpty");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Parent == nullptr)
|
||||
Parent = FileSystemRoot;
|
||||
|
||||
vfsdbg("PathExists( Path: \"%s\" Parent: \"%s\" )",
|
||||
Path, Parent->Name);
|
||||
|
||||
const char *CleanPath = NormalizePath(Path, Parent);
|
||||
bool ret = GetNodeFromPath(CleanPath, Parent) != nullptr;
|
||||
delete[] CleanPath;
|
||||
vfsdbg("PathExists()->\"%s\"",
|
||||
ret ? "true" : "false");
|
||||
return ret;
|
||||
}
|
||||
|
||||
Node *Virtual::Create(const char *Path, NodeType Type, Node *Parent)
|
||||
{
|
||||
if (isempty((char *)Path))
|
||||
return nullptr;
|
||||
|
||||
SmartLock(VirtualLock);
|
||||
Node *RootNode = FileSystemRoot->Children[0];
|
||||
Node *CurrentParent = this->GetParent(Path, Parent);
|
||||
vfsdbg("Virtual::Create( Path: \"%s\" Parent: \"%s\" )",
|
||||
Path, Parent ? Parent->Name : CurrentParent->Name);
|
||||
|
||||
const char *CleanPath = this->NormalizePath(Path, CurrentParent);
|
||||
vfsdbg("CleanPath: \"%s\"", CleanPath);
|
||||
|
||||
VirtualLock.Unlock();
|
||||
if (PathExists(CleanPath, CurrentParent))
|
||||
{
|
||||
error("Path \"%s\" already exists.", CleanPath);
|
||||
goto CreatePathError;
|
||||
}
|
||||
VirtualLock.Lock(__FUNCTION__);
|
||||
|
||||
cwk_segment segment;
|
||||
if (!cwk_path_get_first_segment(CleanPath, &segment))
|
||||
{
|
||||
error("Path doesn't have any segments.");
|
||||
goto CreatePathError;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
char *SegmentName = new char[segment.end - segment.begin + 1];
|
||||
memcpy(SegmentName, segment.begin, segment.end - segment.begin);
|
||||
vfsdbg("SegmentName: \"%s\"", SegmentName);
|
||||
|
||||
auto GetChild = [](const char *Name, Node *Parent)
|
||||
{
|
||||
vfsdbg("GetChild( Name: \"%s\" Parent: \"%s\" )",
|
||||
Name, Parent->Name);
|
||||
|
||||
if (!Parent)
|
||||
{
|
||||
vfsdbg("GetChild()->nullptr");
|
||||
return (Node *)nullptr;
|
||||
}
|
||||
|
||||
foreach (auto Child in Parent->Children)
|
||||
{
|
||||
if (strcmp(Child->Name, Name) == 0)
|
||||
{
|
||||
vfsdbg("GetChild()->\"%s\"", Child->Name);
|
||||
return Child;
|
||||
}
|
||||
}
|
||||
|
||||
vfsdbg("GetChild()->nullptr (not found)");
|
||||
return (Node *)nullptr;
|
||||
};
|
||||
|
||||
if (Parent)
|
||||
{
|
||||
if (GetChild(SegmentName, RootNode) != nullptr)
|
||||
{
|
||||
RootNode = GetChild(SegmentName, RootNode);
|
||||
delete[] SegmentName;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetChild(SegmentName, CurrentParent) != nullptr)
|
||||
CurrentParent = GetChild(SegmentName, CurrentParent);
|
||||
else
|
||||
{
|
||||
CurrentParent = new Node(CurrentParent,
|
||||
SegmentName,
|
||||
NodeType::DIRECTORY);
|
||||
}
|
||||
|
||||
delete[] SegmentName;
|
||||
} while (cwk_path_get_next_segment(&segment));
|
||||
|
||||
CurrentParent->Type = Type;
|
||||
// CurrentParent->FullPath = CleanPath;
|
||||
|
||||
vfsdbg("Virtual::Create()->\"%s\"", CurrentParent->Name);
|
||||
#ifdef DEBUG
|
||||
VirtualLock.Unlock();
|
||||
debug("Path created: \"%s\"",
|
||||
CurrentParent->FullPath);
|
||||
VirtualLock.Lock(__FUNCTION__);
|
||||
#endif
|
||||
return CurrentParent;
|
||||
|
||||
CreatePathError:
|
||||
delete[] CleanPath;
|
||||
vfsdbg("Virtual::Create()->nullptr");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Node *Virtual::CreateLink(const char *Path, const char *Target, Node *Parent)
|
||||
{
|
||||
Node *node = this->Create(Path, NodeType::SYMLINK, Parent);
|
||||
if (node)
|
||||
{
|
||||
node->Symlink = new char[strlen(Target) + 1];
|
||||
strncpy((char *)node->Symlink,
|
||||
Target,
|
||||
strlen(Target));
|
||||
|
||||
node->SymlinkTarget = node->vFS->GetNodeFromPath(node->Symlink);
|
||||
return node;
|
||||
}
|
||||
|
||||
error("Failed to create link \"%s\" -> \"%s\"",
|
||||
Path, Target);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int Virtual::Delete(const char *Path, bool Recursive, Node *Parent)
|
||||
{
|
||||
vfsdbg("Virtual::Delete( Path: \"%s\" Parent: \"%s\" )",
|
||||
Path, Parent ? Parent->Name : "(null)");
|
||||
|
||||
if (isempty((char *)Path))
|
||||
return -EINVAL;
|
||||
|
||||
if (Parent == nullptr)
|
||||
Parent = FileSystemRoot;
|
||||
|
||||
const char *CleanPath = this->NormalizePath(Path, Parent);
|
||||
vfsdbg("CleanPath: \"%s\"", CleanPath);
|
||||
|
||||
if (!PathExists(CleanPath, Parent))
|
||||
{
|
||||
vfsdbg("Path \"%s\" doesn't exist.", CleanPath);
|
||||
delete[] CleanPath;
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
Node *NodeToDelete = GetNodeFromPath(CleanPath, Parent);
|
||||
|
||||
if (!NodeToDelete->References.empty())
|
||||
fixme("Path \"%s\" is referenced by %d objects.",
|
||||
CleanPath, NodeToDelete->References.size());
|
||||
|
||||
delete[] CleanPath;
|
||||
delete NodeToDelete;
|
||||
FSMountInfo fsmi{.fsi = fsi, .Root = Root};
|
||||
DeviceMap.insert({Device, fsmi});
|
||||
RegisterLock.store(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Virtual::Delete(Node *Path, bool Recursive, Node *Parent)
|
||||
dev_t Virtual::RegisterFileSystem(FileSystemInfo *fsi, Inode *Root)
|
||||
{
|
||||
return Delete(Path->FullPath, Recursive, Parent);
|
||||
RegisterLock.store(true);
|
||||
size_t len = DeviceMap.size();
|
||||
FSMountInfo fsmi{.fsi = fsi, .Root = Root};
|
||||
DeviceMap.insert({len, fsmi});
|
||||
RegisterLock.store(false);
|
||||
return len;
|
||||
}
|
||||
|
||||
RefNode *Virtual::Open(const char *Path, Node *Parent)
|
||||
int Virtual::UnregisterFileSystem(dev_t Device)
|
||||
{
|
||||
vfsdbg("Opening \"%s\" with parent \"%s\"",
|
||||
Path, Parent ? Parent->Name : "(null)");
|
||||
auto it = DeviceMap.find(Device);
|
||||
if (it == DeviceMap.end())
|
||||
ReturnLogError(-ENOENT, "Device %d not found", Device);
|
||||
|
||||
if (strcmp(Path, "/") == 0)
|
||||
return FileSystemRoot->CreateReference();
|
||||
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;
|
||||
}
|
||||
|
||||
if (!Parent)
|
||||
Parent = FileSystemRoot->Children[0];
|
||||
void Virtual::AddRoot(Inode *Root)
|
||||
{
|
||||
SmartLock(VirtualLock);
|
||||
FileSystemRoots->Children.push_back(Root);
|
||||
}
|
||||
|
||||
if (strcmp(Path, ".") == 0)
|
||||
return Parent->CreateReference();
|
||||
FileNode *Virtual::GetRoot(size_t Index)
|
||||
{
|
||||
if (Index >= FileSystemRoots->Children.size())
|
||||
return nullptr;
|
||||
|
||||
if (strcmp(Path, "..") == 0)
|
||||
Inode *RootNode = FileSystemRoots->Children[Index];
|
||||
|
||||
char rootName[128]{};
|
||||
snprintf(rootName, sizeof(rootName), "root-%ld", Index);
|
||||
|
||||
return this->CreateCacheNode(nullptr, RootNode, rootName, 0);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (Parent->Parent)
|
||||
return Parent->Parent->CreateReference();
|
||||
else
|
||||
return Parent->CreateReference();
|
||||
assert(thisProcess != nullptr);
|
||||
Parent = thisProcess->Info.RootNode;
|
||||
}
|
||||
|
||||
Node *CurrentParent = this->GetParent(Path, Parent);
|
||||
const char *CleanPath = NormalizePath(Path, CurrentParent);
|
||||
auto it = DeviceMap.find(Parent->Node->Device);
|
||||
if (it == DeviceMap.end())
|
||||
ReturnLogError(nullptr, "Device %d not found", Parent->Node->Device);
|
||||
|
||||
if (PathExists(CleanPath, CurrentParent))
|
||||
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::GetByPath(const char *Path, FileNode *Parent)
|
||||
{
|
||||
FileNode *fn = this->CacheLookup(Path);
|
||||
if (fn)
|
||||
return fn;
|
||||
|
||||
if (Parent == nullptr)
|
||||
Parent = thisProcess ? thisProcess->Info.RootNode : this->GetRoot(0);
|
||||
|
||||
auto it = DeviceMap.find(Parent->Node->Device);
|
||||
if (it == DeviceMap.end())
|
||||
ReturnLogError(nullptr, "Device %d not found", Parent->Node->Device);
|
||||
|
||||
struct cwk_segment segment;
|
||||
if (!cwk_path_get_first_segment(Path, &segment))
|
||||
ReturnLogError(nullptr, "Path has no segments");
|
||||
|
||||
Inode *Node = NULL;
|
||||
FileNode *__Parent = Parent;
|
||||
do
|
||||
{
|
||||
Node *node = GetNodeFromPath(CleanPath, CurrentParent);
|
||||
if (node)
|
||||
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 %d failed with %d", 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;
|
||||
|
||||
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)
|
||||
{
|
||||
delete[] CleanPath;
|
||||
/* TODO: Check if dir or file? */
|
||||
return node->CreateReference();
|
||||
debug("Lookup for %d failed with %d", it->first, luRet);
|
||||
break;
|
||||
}
|
||||
|
||||
this->CreateCacheNode(ret, ChildNode, dirent->d_name, 0);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
free(dirent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Node *Virtual::CreateIfNotExists(const char *Path, NodeType Type, Node *Parent)
|
||||
FileNode *Virtual::CreateLink(const char *Path, FileNode *Parent, const char *Target)
|
||||
{
|
||||
Node *node = GetNodeFromPath(Path, Parent);
|
||||
if (node)
|
||||
return node;
|
||||
return Create(Path, Type, Parent);
|
||||
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);
|
||||
}
|
||||
|
||||
Virtual::Virtual()
|
||||
FileNode *Virtual::CreateLink(const char *Path, FileNode *Parent, FileNode *Target)
|
||||
{
|
||||
SmartLock(VirtualLock);
|
||||
trace("Initializing virtual file system...");
|
||||
FileSystemRoot = new Node(nullptr, "<root>", NodeType::MOUNTPOINT);
|
||||
FileSystemRoot->vFS = this;
|
||||
return this->CreateLink(Path, Parent, Target->Path.c_str());
|
||||
}
|
||||
|
||||
Virtual::~Virtual()
|
||||
bool Virtual::PathExists(const char *Path, FileNode *Parent)
|
||||
{
|
||||
SmartLock(VirtualLock);
|
||||
stub;
|
||||
/* TODO: sync, cache */
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
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 <debug.h>
|
||||
|
||||
#include "../../kernel.h"
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
}
|
@ -18,40 +18,427 @@
|
||||
#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
|
||||
{
|
||||
size_t USTARNode::read(uint8_t *Buffer,
|
||||
size_t Size,
|
||||
off_t Offset)
|
||||
int USTAR::Lookup(struct Inode *_Parent, const char *Name, struct Inode **Result)
|
||||
{
|
||||
if (Size <= 0)
|
||||
Size = this->Size;
|
||||
auto Parent = (USTARInode *)_Parent;
|
||||
|
||||
if (Offset > this->Size)
|
||||
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;
|
||||
}
|
||||
|
||||
if ((this->Size - Offset) == 0)
|
||||
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(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 (Offset + (off_t)Size > this->Size)
|
||||
Size = this->Size;
|
||||
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 *)(this->Address + Offset), Size);
|
||||
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;
|
||||
}
|
||||
|
||||
USTARNode::USTARNode(uintptr_t Address, const char *Name,
|
||||
NodeType Type, Virtual *vfs_ctx)
|
||||
: Node(nullptr, Name, Type, true, vfs_ctx, nullptr),
|
||||
Address(Address)
|
||||
ssize_t USTAR::ReadDir(struct Inode *_Node, struct kdirent *Buffer, size_t Size, off_t Offset, off_t Entries)
|
||||
{
|
||||
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;
|
||||
fixme("Wrong d_type returned");
|
||||
ent->d_type = IFTODT(StringToInt(var->Header->typeflag));
|
||||
strncpy(ent->d_name, var->Name.c_str(), strlen(var->Name.c_str()));
|
||||
|
||||
totalSize += reclen;
|
||||
entries++;
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
USTARNode::~USTARNode() {}
|
||||
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;
|
||||
strncpy(node->Header->link, Target, sizeof(node->Header->link));
|
||||
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)
|
||||
{
|
||||
@ -62,28 +449,86 @@ namespace vfs
|
||||
}
|
||||
|
||||
FileHeader *header = (FileHeader *)Address;
|
||||
if (memcmp(header->signature, "ustar", 5) != 0)
|
||||
if (strncmp(header->signature, TMAGIC, TMAGLEN) != 0)
|
||||
{
|
||||
error("ustar signature invalid!");
|
||||
error("Invalid signature!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void USTAR::ReadArchive(uintptr_t Address, Virtual *vfs_ctx)
|
||||
void USTAR::ReadArchive(uintptr_t Address, size_t Size)
|
||||
{
|
||||
trace("Initializing USTAR with address %#lx", Address);
|
||||
trace("Initializing USTAR with address %#lx and size %d", Address, Size);
|
||||
|
||||
if (!this->TestArchive(Address))
|
||||
return; /* Check whether the archive is deflated */
|
||||
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);
|
||||
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))
|
||||
@ -92,82 +537,291 @@ namespace vfs
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp(header->signature, "ustar", 5) != 0)
|
||||
if (strncmp(header->signature, TMAGIC, TMAGLEN) != 0)
|
||||
break;
|
||||
// debug("\"%s\"", header->name);
|
||||
|
||||
memmove(header->name,
|
||||
header->name + 1,
|
||||
strlen(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 (header->name[strlen(header->name) - 1] == '/')
|
||||
{
|
||||
debug("Removing trailing slash from %s", header->name);
|
||||
header->name[strlen(header->name) - 1] = 0;
|
||||
}
|
||||
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);
|
||||
Node *node;
|
||||
NodeType type = NODE_TYPE_NONE;
|
||||
if (isempty((char *)header->name))
|
||||
goto NextFileAddress;
|
||||
|
||||
switch (header->typeflag[0])
|
||||
{
|
||||
case REGULAR_FILE:
|
||||
type = NodeType::FILE;
|
||||
break;
|
||||
case SYMLINK:
|
||||
type = NodeType::SYMLINK;
|
||||
break;
|
||||
case DIRECTORY:
|
||||
type = NodeType::DIRECTORY;
|
||||
break;
|
||||
case CHARDEV:
|
||||
type = NodeType::CHARDEVICE;
|
||||
break;
|
||||
case BLOCKDEV:
|
||||
type = NodeType::BLOCKDEVICE;
|
||||
break;
|
||||
default:
|
||||
warn("Unknown type: %d", header->typeflag[0]);
|
||||
break;
|
||||
}
|
||||
|
||||
node = new USTARNode((Address + 512), header->name,
|
||||
type, vfs_ctx);
|
||||
|
||||
// debug("%s %d KiB, Type:%c", header->name,
|
||||
// TO_KiB(size), header->typeflag[0]);
|
||||
node->Mode = StringToInt(header->mode);
|
||||
node->Size = size;
|
||||
node->GroupIdentifier = GetSize(header->gid);
|
||||
node->UserIdentifier = GetSize(header->uid);
|
||||
node->DeviceMajor = GetSize(header->dev_maj);
|
||||
node->DeviceMinor = GetSize(header->dev_min);
|
||||
|
||||
node->AccessTime = GetSize(header->mtime);
|
||||
node->ModifyTime = GetSize(header->mtime);
|
||||
node->ChangeTime = GetSize(header->mtime);
|
||||
node->IndexNode = i;
|
||||
|
||||
if (type == NodeType::SYMLINK)
|
||||
{
|
||||
node->Symlink = new char[strlen(header->link) + 1];
|
||||
strncpy((char *)node->Symlink,
|
||||
header->link,
|
||||
strlen(header->link));
|
||||
}
|
||||
|
||||
NextFileAddress:
|
||||
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::USTAR() {}
|
||||
ustar->DeviceID = fs->EarlyReserveDevice();
|
||||
ustar->ReadArchive(Address, Size);
|
||||
|
||||
USTAR::~USTAR() {}
|
||||
Inode *initrd = nullptr;
|
||||
ustar->Lookup(nullptr, "/", &initrd);
|
||||
assert(initrd != nullptr);
|
||||
|
||||
FileSystemInfo *fsi = new FileSystemInfo;
|
||||
fsi->Name = "ustar";
|
||||
fsi->Flags = 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;
|
||||
int ret = fs->LateRegisterFileSystem(ustar->DeviceID, fsi, initrd);
|
||||
|
||||
fs->AddRoot(initrd);
|
||||
return true;
|
||||
}
|
||||
|
252
storage/node.cpp
252
storage/node.cpp
@ -1,252 +0,0 @@
|
||||
/*
|
||||
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 <cwalk.h>
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
int Node::open(int Flags, mode_t Mode)
|
||||
{
|
||||
if (likely(open_ptr))
|
||||
return open_ptr(Flags, Mode);
|
||||
|
||||
debug("Operation not handled for %s(%#lx)",
|
||||
this->FullPath, this);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int Node::close()
|
||||
{
|
||||
if (likely(close_ptr))
|
||||
return close_ptr();
|
||||
|
||||
debug("Operation not handled for %s(%#lx)",
|
||||
this->FullPath, this);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
size_t Node::read(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
if (likely(read_ptr))
|
||||
return read_ptr(Buffer, Size, Offset);
|
||||
|
||||
debug("Operation not handled for %s(%#lx)",
|
||||
this->FullPath, this);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
size_t Node::write(uint8_t *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
if (likely(write_ptr))
|
||||
return write_ptr(Buffer, Size, Offset);
|
||||
|
||||
debug("Operation not handled for %s(%#lx)",
|
||||
this->FullPath, this);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
int Node::ioctl(unsigned long Request, void *Argp)
|
||||
{
|
||||
if (likely(ioctl_ptr))
|
||||
return ioctl_ptr(Request, Argp);
|
||||
|
||||
debug("Operation not handled for %s(%#lx)",
|
||||
this->FullPath, this);
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
RefNode *Node::CreateReference()
|
||||
{
|
||||
SmartLock(NodeLock);
|
||||
RefNode *ref = new RefNode(this);
|
||||
References.push_back(ref);
|
||||
debug("Created reference %#lx for node %#lx(%s)",
|
||||
ref, this, this->Name);
|
||||
return ref;
|
||||
}
|
||||
|
||||
void Node::RemoveReference(RefNode *Reference)
|
||||
{
|
||||
SmartLock(NodeLock);
|
||||
debug("Removing reference %#lx for node %#lx", Reference, this);
|
||||
References.erase(std::find(References.begin(),
|
||||
References.end(),
|
||||
Reference));
|
||||
}
|
||||
|
||||
Node::Node(Node *Parent, const char *Name, NodeType Type,
|
||||
bool NoParent, Virtual *_fs, int *Err)
|
||||
{
|
||||
assert(Name != nullptr);
|
||||
assert(strlen(Name) != 0);
|
||||
|
||||
if (Parent && _fs == nullptr)
|
||||
_fs = Parent->vFS;
|
||||
|
||||
if (Err != nullptr)
|
||||
*Err = 0;
|
||||
|
||||
this->Type = Type;
|
||||
|
||||
auto GetChild = [](const char *Name, Node *Parent)
|
||||
{
|
||||
if (!Parent)
|
||||
return (Node *)nullptr;
|
||||
|
||||
foreach (auto Child in Parent->Children)
|
||||
{
|
||||
if (strcmp(Child->Name, Name) == 0)
|
||||
return Child;
|
||||
}
|
||||
|
||||
return (Node *)nullptr;
|
||||
};
|
||||
|
||||
auto CreateWithParent = [this](const char *Name, Node *Parent)
|
||||
{
|
||||
assert(Parent->vFS != nullptr);
|
||||
assert(Parent->Type == DIRECTORY ||
|
||||
Parent->Type == MOUNTPOINT);
|
||||
|
||||
this->vFS = Parent->vFS;
|
||||
this->Parent = Parent;
|
||||
|
||||
this->Name = new char[strlen(Name) + 1];
|
||||
strcpy((char *)this->Name, Name);
|
||||
|
||||
this->FullPath = new char[strlen(Parent->FullPath) +
|
||||
strlen(this->Name) + 2];
|
||||
strcpy((char *)this->FullPath, Parent->FullPath);
|
||||
if (strcmp(this->FullPath, "/") != 0)
|
||||
strcat((char *)this->FullPath, "/");
|
||||
strcat((char *)this->FullPath, this->Name);
|
||||
|
||||
this->Parent->Children.push_back(this);
|
||||
};
|
||||
|
||||
if (NoParent)
|
||||
{
|
||||
Parent = nullptr;
|
||||
const char *Path = Name;
|
||||
|
||||
Node *RootNode = _fs->FileSystemRoot->Children[0];
|
||||
Node *CurrentParent = _fs->GetParent(Path, Parent);
|
||||
|
||||
if (_fs->PathExists(Path, CurrentParent))
|
||||
{
|
||||
debug("Path \"%s\" already exists.", Path);
|
||||
delete[] Path;
|
||||
if (Err != nullptr)
|
||||
*Err = -EEXIST;
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
const char *CleanPath = _fs->NormalizePath(Path, CurrentParent);
|
||||
|
||||
cwk_segment segment;
|
||||
if (!cwk_path_get_first_segment(CleanPath, &segment))
|
||||
{
|
||||
debug("Path doesn't have any segments.");
|
||||
delete[] CleanPath;
|
||||
if (Err != nullptr)
|
||||
*Err = -EINVAL;
|
||||
delete this;
|
||||
return;
|
||||
}
|
||||
|
||||
cwk_segment last_segment;
|
||||
cwk_path_get_last_segment(CleanPath, &last_segment);
|
||||
|
||||
do
|
||||
{
|
||||
char *SegmentName = new char[segment.end - segment.begin + 1];
|
||||
memcpy(SegmentName, segment.begin, segment.end - segment.begin);
|
||||
|
||||
if (Parent)
|
||||
{
|
||||
if (GetChild(SegmentName, RootNode) != nullptr)
|
||||
{
|
||||
RootNode = GetChild(SegmentName, RootNode);
|
||||
delete[] SegmentName;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetChild(SegmentName, CurrentParent) == nullptr)
|
||||
{
|
||||
if (segment.begin == last_segment.begin)
|
||||
{
|
||||
CreateWithParent(SegmentName, CurrentParent);
|
||||
delete[] SegmentName;
|
||||
break; /* This is the last segment anyway... */
|
||||
}
|
||||
|
||||
CurrentParent = new Node(CurrentParent,
|
||||
SegmentName,
|
||||
Type);
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentParent = GetChild(SegmentName, CurrentParent);
|
||||
}
|
||||
|
||||
delete[] SegmentName;
|
||||
} while (cwk_path_get_next_segment(&segment));
|
||||
}
|
||||
else if (Parent)
|
||||
CreateWithParent(Name, Parent);
|
||||
else
|
||||
{
|
||||
this->Name = new char[strlen(Name) + 1];
|
||||
strcpy((char *)this->Name, Name);
|
||||
this->FullPath = Name;
|
||||
|
||||
trace("Node %s(%#lx) has no parent",
|
||||
this->Name, this);
|
||||
}
|
||||
|
||||
// debug("Created node %s(%#lx)", this->FullPath, this);
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{
|
||||
debug("Destroyed node %s(%#lx)", this->FullPath, this);
|
||||
// assert(this->Children.size() == 0);
|
||||
|
||||
foreach (auto Child in this->Children)
|
||||
delete Child;
|
||||
|
||||
if (this->Parent)
|
||||
{
|
||||
debug("Removing node %s(%#lx) from parent %s(%#lx)",
|
||||
this->FullPath, this,
|
||||
this->Parent->FullPath, this->Parent);
|
||||
|
||||
this->Parent->Children.erase(std::find(this->Parent->Children.begin(),
|
||||
this->Parent->Children.end(),
|
||||
this));
|
||||
}
|
||||
|
||||
delete[] this->Name;
|
||||
if (this->Parent)
|
||||
delete[] this->FullPath;
|
||||
if (this->Symlink)
|
||||
delete[] this->Symlink;
|
||||
}
|
||||
}
|
@ -1,163 +0,0 @@
|
||||
/*
|
||||
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>
|
||||
|
||||
#ifdef DEBUG
|
||||
const char *SeekStrings[] =
|
||||
{"SEEK_SET",
|
||||
"SEEK_CUR",
|
||||
"SEEK_END"};
|
||||
#endif
|
||||
|
||||
namespace vfs
|
||||
{
|
||||
size_t RefNode::read(uint8_t *Buffer, size_t Size)
|
||||
{
|
||||
if (this->SymlinkTo)
|
||||
return this->SymlinkTo->read(Buffer, Size);
|
||||
|
||||
debug("Reading %d bytes from %s[%d]",
|
||||
Size, this->node->FullPath, this->FileOffset.load());
|
||||
return this->node->read(Buffer, Size, this->FileOffset.load());
|
||||
}
|
||||
|
||||
size_t RefNode::write(uint8_t *Buffer, size_t Size)
|
||||
{
|
||||
if (this->SymlinkTo)
|
||||
return this->SymlinkTo->write(Buffer, Size);
|
||||
|
||||
debug("Writing %d bytes to %s[%d]",
|
||||
Size, this->node->FullPath, this->FileOffset.load());
|
||||
return this->node->write(Buffer, Size, this->FileOffset.load());
|
||||
}
|
||||
|
||||
off_t RefNode::seek(off_t Offset, int Whence)
|
||||
{
|
||||
if (this->SymlinkTo)
|
||||
return this->SymlinkTo->seek(Offset, Whence);
|
||||
|
||||
// debug("Current offset is %d", this->Offset.load());
|
||||
switch (Whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
{
|
||||
if (Offset > this->FileSize)
|
||||
return -EINVAL;
|
||||
|
||||
if (Offset < 0)
|
||||
{
|
||||
fixme("Negative offset %d is not implemented", Offset);
|
||||
Offset = 0;
|
||||
}
|
||||
|
||||
if (Offset > this->FileSize)
|
||||
{
|
||||
fixme("Offset %d is bigger than file size %d",
|
||||
Offset, this->FileSize);
|
||||
Offset = this->FileSize;
|
||||
}
|
||||
|
||||
this->FileOffset.store(Offset);
|
||||
break;
|
||||
}
|
||||
case SEEK_CUR:
|
||||
{
|
||||
off_t NewOffset = off_t(this->FileOffset.load()) + Offset;
|
||||
if (NewOffset > this->FileSize ||
|
||||
NewOffset < 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
this->FileOffset.store(NewOffset);
|
||||
break;
|
||||
}
|
||||
case SEEK_END:
|
||||
{
|
||||
off_t NewOffset = this->FileSize + Offset;
|
||||
if (NewOffset > this->FileSize ||
|
||||
NewOffset < 0)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
this->FileOffset.store(NewOffset);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error("Invalid whence!");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
off_t RetOffset = off_t(this->FileOffset.load());
|
||||
// debug("( %d %ld %s[%d] ) -> %d",
|
||||
// Offset, this->Offset.load(),
|
||||
// SeekStrings[Whence], Whence,
|
||||
// RetOffset);
|
||||
return RetOffset;
|
||||
}
|
||||
|
||||
int RefNode::ioctl(unsigned long Request, void *Argp)
|
||||
{
|
||||
if (this->SymlinkTo)
|
||||
return this->SymlinkTo->ioctl(Request, Argp);
|
||||
|
||||
return this->node->ioctl(Request, Argp);
|
||||
}
|
||||
|
||||
RefNode::RefNode(Node *node)
|
||||
{
|
||||
this->node = node;
|
||||
this->FileSize = node->Size;
|
||||
if (this->node->Type == SYMLINK)
|
||||
{
|
||||
if (!this->node->SymlinkTarget)
|
||||
{
|
||||
this->node->SymlinkTarget =
|
||||
node->vFS->GetNodeFromPath(this->node->Symlink);
|
||||
}
|
||||
|
||||
if (!this->node->SymlinkTarget)
|
||||
{
|
||||
error("Symlink target %s not found!",
|
||||
this->node->Symlink);
|
||||
return;
|
||||
}
|
||||
|
||||
/* not standard but useful in kernel-space */
|
||||
this->node->Size = this->node->SymlinkTarget->Size;
|
||||
this->SymlinkTo = this->node->SymlinkTarget->CreateReference();
|
||||
}
|
||||
|
||||
debug("Created reference node for %s [%#lx]",
|
||||
this->node->FullPath, (uintptr_t)this);
|
||||
}
|
||||
|
||||
RefNode::~RefNode()
|
||||
{
|
||||
if (this->SymlinkTo)
|
||||
this->node->SymlinkTarget->RemoveReference(this);
|
||||
|
||||
this->node->RemoveReference(this);
|
||||
|
||||
debug("Destroyed reference node for %s [%#lx]",
|
||||
this->node->FullPath, (uintptr_t)this);
|
||||
}
|
||||
}
|
323
storage/virtual.cpp
Normal file
323
storage/virtual.cpp
Normal file
@ -0,0 +1,323 @@
|
||||
/*
|
||||
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
|
||||
{
|
||||
/* maj = 0
|
||||
min:
|
||||
0 - <ROOT>
|
||||
1 - /proc/self
|
||||
2 - /dev/null
|
||||
3 - /dev/zero
|
||||
4 - /dev/random
|
||||
5 - /dev/mem
|
||||
*/
|
||||
|
||||
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;
|
||||
foreach (const auto &Root in Parent->Children)
|
||||
{
|
||||
char rootName[128]{};
|
||||
snprintf(rootName, sizeof(rootName), "root-%ld", 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");
|
||||
}
|
||||
|
||||
ssize_t __vfs_Read(struct Inode *Node, void *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
case 3: /* /dev/zero */
|
||||
{
|
||||
if (Size <= 0)
|
||||
return 0;
|
||||
|
||||
memset(Buffer, 0, Size);
|
||||
return Size;
|
||||
}
|
||||
case 4: /* /dev/random */
|
||||
{
|
||||
if (Size <= 0)
|
||||
return 0;
|
||||
|
||||
if (Size < sizeof(uint64_t))
|
||||
{
|
||||
uint8_t *buf = (uint8_t *)Buffer;
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
buf[i] = (uint8_t)(Random::rand16() & 0xFF);
|
||||
return Size;
|
||||
}
|
||||
|
||||
uint64_t *buf = (uint64_t *)Buffer;
|
||||
for (size_t i = 0; i < Size / sizeof(uint64_t); i++)
|
||||
buf[i] = Random::rand64();
|
||||
return Size;
|
||||
}
|
||||
case 5: /* /dev/mem */
|
||||
{
|
||||
stub;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
|
||||
ssize_t __vfs_Write(struct Inode *Node, const void *Buffer, size_t Size, off_t Offset)
|
||||
{
|
||||
switch (Node->GetMinor())
|
||||
{
|
||||
case 2: /* /dev/null */
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
case 3: /* /dev/zero */
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
case 4: /* /dev/random */
|
||||
{
|
||||
return Size;
|
||||
}
|
||||
case 5: /* /dev/mem */
|
||||
{
|
||||
stub;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return -ENOENT;
|
||||
};
|
||||
}
|
||||
|
||||
/* This implementation is used internally by the kernel, so no "." & ".." */
|
||||
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;
|
||||
|
||||
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 *dev = this->ForceCreate(this->GetRoot(0), "dev", mode);
|
||||
FileNode *mnt = this->ForceCreate(this->GetRoot(0), "mnt", mode);
|
||||
FileNode *proc = this->ForceCreate(this->GetRoot(0), "proc", mode);
|
||||
FileNode *log = this->ForceCreate(this->GetRoot(0), "var", mode);
|
||||
log = this->ForceCreate(log, "log", mode);
|
||||
dev->Node->Flags = iFlags;
|
||||
mnt->Node->Flags = iFlags;
|
||||
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;
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH |
|
||||
S_IFCHR;
|
||||
FileNode *null = this->ForceCreate(dev, "null", mode);
|
||||
null->Node->Device = FileSystemRoots->Node.Device;
|
||||
null->Node->SetDevice(0, 2);
|
||||
null->Node->Flags = iFlags;
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH |
|
||||
S_IFCHR;
|
||||
FileNode *zero = this->ForceCreate(dev, "zero", mode);
|
||||
zero->Node->Device = FileSystemRoots->Node.Device;
|
||||
zero->Node->SetDevice(0, 3);
|
||||
zero->Node->Flags = iFlags;
|
||||
|
||||
/* c rw- rw- rw- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP | S_IWGRP |
|
||||
S_IROTH | S_IWOTH |
|
||||
S_IFCHR;
|
||||
FileNode *random = this->ForceCreate(dev, "random", mode);
|
||||
random->Node->Device = FileSystemRoots->Node.Device;
|
||||
random->Node->SetDevice(0, 4);
|
||||
random->Node->Flags = iFlags;
|
||||
|
||||
/* c rw- r-- --- */
|
||||
mode = S_IRUSR | S_IWUSR |
|
||||
S_IRGRP |
|
||||
|
||||
S_IFCHR;
|
||||
FileNode *mem = this->ForceCreate(dev, "mem", mode);
|
||||
mem->Node->Device = FileSystemRoots->Node.Device;
|
||||
mem->Node->SetDevice(0, 5);
|
||||
mem->Node->Flags = iFlags;
|
||||
|
||||
new vfs::PTMXDevice();
|
||||
}
|
||||
|
||||
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_MOUNTPOINT | I_FLAG_CACHE_KEEP;
|
||||
|
||||
FileSystemRoots->Node.Offset = INT32_MAX;
|
||||
FileSystemRoots->Name = "<ROOT>";
|
||||
|
||||
FileSystemInfo *fsi = new FileSystemInfo;
|
||||
fsi->Name = "Virtual Roots";
|
||||
fsi->Flags = I_FLAG_MOUNTPOINT | I_FLAG_CACHE_KEEP;
|
||||
fsi->SuperOps = {};
|
||||
fsi->Ops.Lookup = __vfs_Lookup;
|
||||
fsi->Ops.Create = __vfs_Create;
|
||||
fsi->Ops.Read = __vfs_Read;
|
||||
fsi->Ops.Write = __vfs_Write;
|
||||
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()
|
||||
{
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user