WIP: Implement a read-write in-memory tmpfs #15
59
kernel/include/fs/TmpFS.h
Normal file
59
kernel/include/fs/TmpFS.h
Normal file
@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
#include "fs/VFS.h"
|
||||
|
||||
namespace TmpFSImpl
|
||||
{
|
||||
ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer);
|
||||
ssize_t write(VFS::Node* node, size_t offset, size_t size, char* buffer);
|
||||
VFS::Node* find(VFS::Node* node, const char* filename);
|
||||
VFS::Node* readdir(VFS::Node* node, long index);
|
||||
int mkdir(VFS::Node* node, const char* filename, mode_t mode);
|
||||
}
|
||||
|
||||
class TmpFS
|
||||
{
|
||||
public:
|
||||
VFS::Node* root()
|
||||
{
|
||||
return m_root;
|
||||
}
|
||||
|
||||
static TmpFS* create(const char* name);
|
||||
static void release(TmpFS* fs);
|
||||
|
||||
ssize_t read(VFS::Node* node, size_t offset, size_t size, char* buffer);
|
||||
ssize_t write(VFS::Node* node, size_t offset, size_t size, char* buffer);
|
||||
|
||||
VFS::Node* find(VFS::Node* node, const char* filename);
|
||||
VFS::Node* readdir(VFS::Node* node, long index);
|
||||
|
||||
int mkdir(VFS::Node* node, const char* filename, mode_t mode);
|
||||
|
||||
void purge();
|
||||
|
||||
private:
|
||||
struct File
|
||||
{
|
||||
char* buf;
|
||||
size_t len;
|
||||
int valid;
|
||||
};
|
||||
|
||||
struct Directory
|
||||
{
|
||||
VFS::Node* buf;
|
||||
size_t len;
|
||||
int valid;
|
||||
};
|
||||
|
||||
template <typename T> struct Array
|
||||
{
|
||||
T* buf;
|
||||
size_t len;
|
||||
};
|
||||
|
||||
TmpFS(const char* name);
|
||||
Array<File> m_files;
|
||||
Array<Directory> m_dirs;
|
||||
VFS::Node* m_root;
|
||||
};
|
208
kernel/src/fs/TmpFS.cpp
Normal file
208
kernel/src/fs/TmpFS.cpp
Normal file
@ -0,0 +1,208 @@
|
||||
#define MODULE "tmpfs"
|
||||
|
||||
#include "fs/TmpFS.h"
|
||||
#include "log/Log.h"
|
||||
#include "std/errno.h"
|
||||
#include "std/stdlib.h"
|
||||
#include "std/string.h"
|
||||
|
||||
ssize_t TmpFSImpl::read(VFS::Node* node, size_t offset, size_t size, char* buffer)
|
||||
{
|
||||
if (!node) return -1;
|
||||
if (!node->impl) return -1;
|
||||
return ((TmpFS*)node->impl)->read(node, offset, size, buffer);
|
||||
}
|
||||
|
||||
ssize_t TmpFSImpl::write(VFS::Node* node, size_t offset, size_t size, char* buffer)
|
||||
{
|
||||
if (!node) return -1;
|
||||
if (!node->impl) return -1;
|
||||
return ((TmpFS*)node->impl)->write(node, offset, size, buffer);
|
||||
}
|
||||
|
||||
VFS::Node* TmpFSImpl::find(VFS::Node* node, const char* filename)
|
||||
{
|
||||
if (!node) return 0;
|
||||
if (!node->impl) return 0;
|
||||
return ((TmpFS*)node->impl)->find(node, filename);
|
||||
}
|
||||
|
||||
VFS::Node* TmpFSImpl::readdir(VFS::Node* node, long index)
|
||||
{
|
||||
if (!node) return 0;
|
||||
if (!node->impl) return 0;
|
||||
return ((TmpFS*)node->impl)->readdir(node, index);
|
||||
}
|
||||
|
||||
int TmpFSImpl::mkdir(VFS::Node* node, const char* filename, mode_t mode)
|
||||
{
|
||||
if (!node) return -1;
|
||||
if (!node->impl) return -1;
|
||||
return ((TmpFS*)node->impl)->mkdir(node, filename, mode);
|
||||
}
|
||||
|
||||
TmpFS* TmpFS::create(const char* name)
|
||||
{
|
||||
return new TmpFS(name);
|
||||
}
|
||||
|
||||
void TmpFS::release(TmpFS* fs)
|
||||
{
|
||||
fs->purge();
|
||||
kfree(fs->m_dirs.buf);
|
||||
delete fs->root();
|
||||
delete fs;
|
||||
}
|
||||
|
||||
extern uint64_t clock_now();
|
||||
|
||||
TmpFS::TmpFS(const char* name)
|
||||
{
|
||||
m_root = new VFS::Node;
|
||||
memset(m_root, 0, sizeof(VFS::Node));
|
||||
m_root->atime = m_root->ctime = m_root->mtime = clock_now();
|
||||
m_root->impl = (uint64_t)this;
|
||||
m_root->inode = 0;
|
||||
m_root->gid = m_root->uid = 0;
|
||||
m_root->mode = 0755;
|
||||
m_root->length = 0;
|
||||
m_root->type = VFS_DIRECTORY;
|
||||
m_root->tty = 0;
|
||||
m_root->readdir_func = TmpFSImpl::readdir;
|
||||
m_root->find_func = TmpFSImpl::find;
|
||||
m_root->mkdir_func = TmpFSImpl::mkdir;
|
||||
m_root->flags = 0;
|
||||
strlcpy(m_root->name, name, sizeof(m_root->name));
|
||||
m_dirs.buf = (Directory*)krealloc(nullptr, sizeof(Directory));
|
||||
m_dirs.len = 1;
|
||||
m_dirs.buf[0] = {nullptr, 0, 1};
|
||||
}
|
||||
|
||||
void TmpFS::purge()
|
||||
{
|
||||
for (size_t i = 0; i < m_dirs.len; i++)
|
||||
{
|
||||
if (m_dirs.buf[i].valid && m_dirs.buf[i].len) kfree(m_dirs.buf[i].buf);
|
||||
}
|
||||
|
||||
m_dirs.buf = (Directory*)krealloc(m_dirs.buf, sizeof(Directory)); // leave space for the root directory
|
||||
|
||||
for (size_t i = 0; i < m_files.len; i++)
|
||||
{
|
||||
if (m_files.buf[i].valid && m_files.buf[i].len) kfree(m_files.buf[i].buf);
|
||||
}
|
||||
|
||||
kfree(m_files.buf);
|
||||
}
|
||||
|
||||
ssize_t TmpFS::read(VFS::Node* node, size_t offset, size_t size, char* buffer)
|
||||
{
|
||||
if (!node) return -1;
|
||||
if (node->inode >= m_files.len) return -1;
|
||||
File& file = m_files.buf[node->inode];
|
||||
if (!file.valid) return -1;
|
||||
if (offset > file.len) return 0;
|
||||
if (offset + size > file.len) { size = file.len - offset; }
|
||||
memcpy(buffer, (void*)(file.buf + offset), size);
|
||||
return size;
|
||||
}
|
||||
|
||||
ssize_t TmpFS::write(VFS::Node* node, size_t offset, size_t size, char* buffer)
|
||||
{
|
||||
if (!node) return -1;
|
||||
if (node->inode >= m_files.len) return -1;
|
||||
File& file = m_files.buf[node->inode];
|
||||
if (!file.valid) return -1;
|
||||
if (offset + size > file.len)
|
||||
{
|
||||
char* newbuf = (char*)krealloc(file.buf, offset + size);
|
||||
if (!newbuf) { size = file.len - offset; }
|
||||
else
|
||||
{
|
||||
file.buf = newbuf;
|
||||
file.len = offset + size;
|
||||
node->length = file.len;
|
||||
}
|
||||
}
|
||||
memcpy((void*)(file.buf + offset), buffer, size);
|
||||
return size;
|
||||
}
|
||||
|
||||
VFS::Node* TmpFS::find(VFS::Node* node, const char* filename)
|
||||
{
|
||||
if (!node) return 0;
|
||||
if (node->inode >= m_dirs.len) return 0;
|
||||
Directory& dir = m_dirs.buf[node->inode];
|
||||
if (!dir.valid) return 0;
|
||||
for (size_t i = 0; i < dir.len; i++)
|
||||
{
|
||||
if (!strncmp(dir.buf[i].name, filename, sizeof(dir.buf[i].name))) { return &dir.buf[i]; }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
VFS::Node* TmpFS::readdir(VFS::Node* node, long index)
|
||||
{
|
||||
if (!node) return 0;
|
||||
if (node->inode >= m_dirs.len) return 0;
|
||||
Directory& dir = m_dirs.buf[node->inode];
|
||||
if (!dir.valid) return 0;
|
||||
if (index >= (long)dir.len) return 0;
|
||||
return &dir.buf[index];
|
||||
}
|
||||
|
||||
int TmpFS::mkdir(VFS::Node* node, const char* filename, mode_t mode)
|
||||
{
|
||||
if (!node) return 0;
|
||||
if (node->inode >= m_dirs.len) return 0;
|
||||
Directory& dir = m_dirs.buf[node->inode];
|
||||
if (!dir.valid) return 0;
|
||||
|
||||
kinfoln("creating directory %s in %s", filename, node->name);
|
||||
|
||||
size_t new_len = m_dirs.len + 1;
|
||||
Directory* new_buf = (Directory*)krealloc(m_dirs.buf, new_len);
|
||||
if (!new_buf) return -ENOMEM;
|
||||
|
||||
m_dirs.len = new_len;
|
||||
m_dirs.buf = new_buf;
|
||||
|
||||
uint64_t new_inode = m_dirs.len - 1;
|
||||
|
||||
m_dirs.buf[new_inode].valid = 0;
|
||||
|
||||
size_t new_dir_len = dir.len + 1;
|
||||
VFS::Node* new_dir_buf = (VFS::Node*)krealloc(dir.buf, new_dir_len);
|
||||
if (!new_dir_buf) return -ENOMEM;
|
||||
|
||||
m_dirs.buf[new_inode].valid = 1;
|
||||
m_dirs.buf[new_inode].len = 0;
|
||||
m_dirs.buf[new_inode].buf = nullptr;
|
||||
|
||||
dir.buf = new_dir_buf;
|
||||
dir.len = new_dir_len;
|
||||
|
||||
node->mtime = clock_now();
|
||||
node->ctime = clock_now();
|
||||
node->length++;
|
||||
|
||||
VFS::Node* new_node = &dir.buf[dir.len - 1];
|
||||
|
||||
memset(new_node, 0, sizeof(VFS::Node));
|
||||
|
||||
new_node->inode = new_inode;
|
||||
new_node->atime = new_node->ctime = new_node->mtime = clock_now();
|
||||
new_node->impl = (uint64_t)this;
|
||||
new_node->gid = new_node->uid = 0;
|
||||
new_node->mode = mode;
|
||||
new_node->length = 0;
|
||||
new_node->type = VFS_DIRECTORY;
|
||||
new_node->tty = 0;
|
||||
new_node->readdir_func = TmpFSImpl::readdir;
|
||||
new_node->find_func = TmpFSImpl::find;
|
||||
new_node->mkdir_func = TmpFSImpl::mkdir;
|
||||
new_node->flags = 0;
|
||||
strlcpy(new_node->name, filename, sizeof(new_node->name));
|
||||
|
||||
return 0;
|
||||
}
|
@ -25,12 +25,12 @@ ssize_t VFS::read(Node* node, size_t offset, size_t length, char* buffer)
|
||||
}
|
||||
if (node->type == VFS_DIRECTORY)
|
||||
{
|
||||
kwarnln("read() failed: is a directory");
|
||||
kwarnln("read() failed for %s: is a directory", node->name);
|
||||
return -EISDIR;
|
||||
}
|
||||
if (!node->read_func)
|
||||
{
|
||||
kwarnln("read() failed: the chosen node doesn't support reading");
|
||||
kwarnln("read() failed for %s: the chosen node doesn't support reading", node->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -46,12 +46,12 @@ ssize_t VFS::write(Node* node, size_t offset, size_t length, const char* buffer)
|
||||
}
|
||||
if (node->type == VFS_DIRECTORY)
|
||||
{
|
||||
kwarnln("write() failed: is a directory");
|
||||
kwarnln("write() failed for %s: is a directory", node->name);
|
||||
return -EISDIR;
|
||||
}
|
||||
if (!node->write_func)
|
||||
{
|
||||
kwarnln("write() failed: the chosen node doesn't support writing");
|
||||
kwarnln("write() failed for %s: the chosen node doesn't support writing", node->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ VFS::Node* VFS::resolve_path(const char* filename, Node* root)
|
||||
buffer[path_section_size] = 0;
|
||||
if (!current_node->find_func)
|
||||
{
|
||||
kwarnln("Current node has no way to find child nodes");
|
||||
kwarnln("Current node %s has no way to find child nodes", current_node->name);
|
||||
return 0;
|
||||
}
|
||||
Node* child = current_node->find_func(current_node, buffer);
|
||||
@ -239,8 +239,16 @@ bool VFS::exists(const char* pathname)
|
||||
|
||||
void VFS::mount(Node* mountpoint, Node* mounted)
|
||||
{
|
||||
if (!mountpoint || !mounted) return;
|
||||
if (mountpoint->flags & VFS_MOUNTPOINT || mounted->flags & VFS_MOUNTPOINT) return;
|
||||
if (!mountpoint || !mounted)
|
||||
{
|
||||
kinfoln("mount failed: invalid pointers");
|
||||
return;
|
||||
}
|
||||
if (mountpoint->flags & VFS_MOUNTPOINT || mounted->flags & VFS_MOUNTPOINT)
|
||||
{
|
||||
kinfoln("mount failed: already a mountpoint");
|
||||
return;
|
||||
}
|
||||
mountpoint->link = mounted;
|
||||
mountpoint->flags |= VFS_MOUNTPOINT;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "config.h"
|
||||
#include "cpu/CPU.h"
|
||||
#include "fs/TmpFS.h"
|
||||
#include "fs/devices/DeviceFS.h"
|
||||
#include "gdt/GDT.h"
|
||||
#include "init/Init.h"
|
||||
@ -69,6 +70,8 @@ extern "C" void _start()
|
||||
|
||||
ASSERT(VFS::mkdir("/dev") == 0);
|
||||
VFS::mount("/dev", DeviceFS::get());
|
||||
ASSERT(VFS::mkdir("/tmp") == 0);
|
||||
VFS::mount("/tmp", TmpFS::create("tmp")->root());
|
||||
|
||||
Init::finish_kernel_boot();
|
||||
|
||||
|
@ -43,6 +43,7 @@ template <typename Callback> void sched_for_each_task(Callback callback)
|
||||
bool will_continue = callback(task);
|
||||
if (!will_continue) break;
|
||||
task = task->next_task;
|
||||
if (!task) break;
|
||||
} while (task != base_task);
|
||||
}
|
||||
|
||||
@ -369,6 +370,7 @@ void Scheduler::reap_tasks()
|
||||
static void sched_decrement_sleep_times()
|
||||
{
|
||||
sched_for_each_task([](Task* task) {
|
||||
if (!task) return false;
|
||||
if (task->task_sleep > 0)
|
||||
{
|
||||
task->task_sleep -= frequency;
|
||||
|
Loading…
Reference in New Issue
Block a user