Virtual File System #10

Merged
apio merged 7 commits from vfs into main 2022-10-10 18:25:43 +00:00
18 changed files with 587 additions and 14 deletions

View File

@ -5,6 +5,8 @@
#include <sys/syscall.h>
#include <unistd.h>
typedef long ssize_t;
int main()
{
if (gettid() == 0) // why are we the idle task?
@ -25,14 +27,42 @@ int main()
sleep(2);
const char* filename = "/sys/config";
printf("Opening %s for reading...\n", filename);
int fd = syscall(SYS_open, filename, 1);
if (fd < 0)
{
char* variable = malloc(200);
*variable = 3;
printf("Allocated variable at address %p\n", variable);
free(variable);
perror("open");
// return 1;
}
else { printf("Got fd %d\n", fd); }
char buf[4096];
ssize_t nread = syscall(SYS_read, fd, sizeof(buf), buf);
if (nread < 0)
{
perror("read");
// return 1;
}
else
{
buf[nread] = 0;
printf("Read %zd bytes\n\n", nread);
printf("%s", buf);
}
printf("Press any key to restart.\n");
if (syscall(SYS_close, fd) < 0)
{
perror("close");
// return 1;
}
printf("\n\nPress any key to restart.\n");
return 0;
}

View File

@ -1,6 +1,9 @@
#pragma once
#define EPERM 1
#define ENOENT 2
#define EBADF 9
#define ENOMEM 12
#define EINVAL 22
#define EMFILE 24
#define ENOSYS 38

View File

@ -0,0 +1,34 @@
#pragma once
#include "fs/VFS.h"
#include <stdint.h>
struct Descriptor
{
bool is_open()
{
return m_is_open;
}
bool can_read()
{
return m_can_read && m_is_open;
}
void close()
{
m_is_open = false;
}
ssize_t read(size_t size, char* buffer);
void open(VFS::Node* node, bool can_read);
Descriptor(const Descriptor& other);
Descriptor();
private:
bool m_is_open;
bool m_can_read;
VFS::Node* m_node;
uint64_t m_offset;
};

30
kernel/include/fs/VFS.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
typedef long ssize_t;
namespace VFS
{
struct Node;
typedef ssize_t (*node_read)(Node*, size_t, size_t, char*);
typedef Node* (*node_finddir)(Node*, const char*);
struct Node
{
char name[64];
uint64_t inode;
uint64_t length;
node_read read_func;
node_finddir find_func;
};
ssize_t read(Node* node, size_t offset, size_t length, char* buffer);
void mount_root(Node* root);
Node* resolve_path(const char* filename, Node* root = nullptr);
Node* root();
}

View File

@ -12,6 +12,9 @@
#define SYS_gettid 7
#define SYS_mmap 8
#define SYS_munmap 9
#define SYS_open 10
#define SYS_read 11
#define SYS_close 12
namespace Syscall
{
@ -28,3 +31,6 @@ void sys_getversion(Context* context, char* buffer, size_t max);
void sys_gettid(Context* context);
void sys_mmap(Context* context, void* address, size_t size, int flags);
void sys_munmap(Context* context, void* address, size_t size);
void sys_open(Context* context, const char* filename, int flags);
void sys_read(Context* context, int fd, size_t size, char* buffer);
void sys_close(Context* context, int fd);

View File

@ -1,7 +1,10 @@
#pragma once
#include "fs/FileDescriptor.h"
#include "interrupts/Context.h"
#include "sys/elf/Image.h"
#define TASK_MAX_FDS 8
struct Task
{
enum TaskState
@ -36,6 +39,8 @@ struct Task
bool is_user_task();
ELFImage* image = nullptr;
Descriptor files[TASK_MAX_FDS];
};
void set_context_from_task(Task& task, Context* ctx);

View File

@ -0,0 +1,25 @@
#include "fs/FileDescriptor.h"
Descriptor::Descriptor() : m_is_open(false)
{
}
Descriptor::Descriptor(const Descriptor& other)
: m_is_open(other.m_is_open), m_can_read(other.m_can_read), m_node(other.m_node), m_offset(other.m_offset)
{
}
void Descriptor::open(VFS::Node* node, bool can_read)
{
m_can_read = can_read;
m_node = node;
m_offset = 0;
m_is_open = true;
}
ssize_t Descriptor::read(size_t size, char* buffer)
{
ssize_t result = VFS::read(m_node, m_offset, size, buffer);
m_offset += result;
return result;
}

93
kernel/src/fs/VFS.cpp Normal file
View File

@ -0,0 +1,93 @@
#define MODULE "vfs"
#include "fs/VFS.h"
#include "log/Log.h"
#include "std/stdlib.h"
#include "std/string.h"
static VFS::Node* vfs_root;
ssize_t VFS::read(Node* node, size_t offset, size_t length, char* buffer)
{
if (!node)
{
kwarnln("read() failed: trying to read from nullptr");
return -1;
}
if (!node->read_func)
{
kwarnln("read() failed: the chosen node doesn't support reading");
return -1;
}
kdbgln("read(): node %s (inode %ld), offset %zd, %zd bytes, into %p", node->name, node->inode, offset, length,
(void*)buffer);
return node->read_func(node, offset, length, buffer);
}
void VFS::mount_root(Node* root)
{
if (!root)
{
kwarnln("mount_root() failed: attempted to mount nullptr");
return;
}
if (vfs_root)
{
kwarnln("mount_root() failed: root filesystem already mounted");
return;
}
kinfoln("mounting node '%s' as vfs root", root->name);
vfs_root = root;
}
VFS::Node* VFS::root()
{
return vfs_root;
}
VFS::Node* VFS::resolve_path(const char* filename, Node* root)
{
if (!root) root = vfs_root;
if (strlen(filename) == 0) return 0;
if (*filename == '/') // Absolute path.
{
filename++;
root = vfs_root;
}
Node* current_node = root;
while (true)
{
while (*filename == '/') { filename++; }
if (*filename == 0) { return current_node; }
size_t path_section_size = 0;
while (filename[path_section_size] && filename[path_section_size] != '/') { path_section_size++; }
if (strncmp(filename, ".", path_section_size) != 0) // The current path section is not '.'
{
char* buffer = (char*)kmalloc(path_section_size + 1);
memcpy(buffer, filename, path_section_size);
buffer[path_section_size] = 0;
if (!current_node->find_func)
{
kwarnln("Current node has no way to find child nodes");
return 0;
}
Node* child = current_node->find_func(current_node, buffer);
if (!child)
{
kwarnln("Current node did not find our target node");
return 0;
}
current_node = child;
kfree(buffer);
}
filename += path_section_size;
}
}

View File

@ -2,6 +2,7 @@
#include "init/InitRD.h"
#include "bootboot.h"
#include "fs/VFS.h"
#include "io/Serial.h"
#include "log/Log.h"
#include "memory/MemoryManager.h"
@ -14,6 +15,8 @@ extern BOOTBOOT bootboot;
static void* initrd_base;
static bool initrd_initialized = false;
static VFS::Node initrd_root;
bool InitRD::is_initialized()
{
return initrd_initialized;
@ -112,10 +115,222 @@ void InitRD::for_each(void (*callback)(File& f))
}
}
#define INITRD_MAX_FILES_IN_DIR 8
#define INITRD_MAX_FILES 32
namespace InitRD
{
struct Directory
{
char name[64];
int entries = 0;
VFS::Node* files[INITRD_MAX_FILES_IN_DIR];
};
}
void initrd_for_each_dir(void (*callback)(InitRD::Directory& f))
{
uint64_t block = 0;
uint64_t total_blocks = InitRD::get_total_blocks();
while (block < total_blocks)
{
InitRD::TarHeader* hdr = (InitRD::TarHeader*)InitRD::get_block(block);
if (!InitRD::is_valid_header(hdr))
{
block++;
continue;
}
if (hdr->typeflag == 53)
{
InitRD::Directory dir;
strncpy(dir.name, hdr->name, sizeof(dir.name));
callback(dir);
block++;
continue;
}
auto f = get_file(hdr);
block += get_file_size_in_blocks(f) + 1;
}
}
static InitRD::File files[32];
static uint32_t total_files = 0;
static InitRD::Directory dirs[32];
static uint32_t total_dirs = 0;
static VFS::Node nodes[63]; // One of the dirs is the initrd_root
static uint32_t total_nodes = 0;
ssize_t initrd_read(VFS::Node* node, size_t offset, size_t length, char* buffer)
{
if (!node) return -1;
if (node->inode >= total_files) return -1;
InitRD::File& file = files[node->inode];
if (offset > file.size) return -1;
if (offset + length > file.size) { length = file.size - offset; }
memcpy(buffer, (void*)((uintptr_t)file.addr + offset), length);
return length;
}
VFS::Node* initrd_scan_dir(VFS::Node* node, const char* filename)
{
if (!node) return 0;
if (node->inode >= total_dirs) return 0;
InitRD::Directory dir = dirs[node->inode];
for (int i = 0; i < dir.entries; i++)
{
if (strncmp(dir.files[i]->name, filename, sizeof(VFS::Node::name)) == 0) { return dir.files[i]; }
}
return 0;
}
static bool initrd_register_dir(InitRD::Directory& dir, uint64_t inode)
{
const char* filename = dir.name;
VFS::Node* current_node = &initrd_root;
while (true)
{
while (*filename == '/') { filename++; }
if (*filename == 0) { return false; }
size_t path_section_size = 0;
while (filename[path_section_size] && filename[path_section_size] != '/') { path_section_size++; }
if (filename[path_section_size]) // We are in a '/'
{
char* buffer = (char*)kmalloc(path_section_size + 1);
memcpy(buffer, filename, path_section_size);
buffer[path_section_size] = 0;
if (!current_node->find_func) { return false; }
VFS::Node* child = current_node->find_func(current_node, buffer);
if (!child) { return false; }
current_node = child;
kfree(buffer);
}
else
{
if (strncmp(filename, ".", path_section_size) != 0) // The current path section is not '.'
{
if (strncmp(filename, "..", path_section_size) == 0) { return false; }
if (!current_node->find_func) { return false; }
InitRD::Directory& parent = dirs[current_node->inode];
if (parent.entries == INITRD_MAX_FILES_IN_DIR) { return false; }
char* buffer = (char*)kmalloc(path_section_size + 1);
memcpy(buffer, filename, path_section_size);
buffer[path_section_size] = 0;
VFS::Node& node = nodes[total_nodes++];
node.inode = inode;
node.find_func = initrd_scan_dir;
node.length = 0;
strncpy(node.name, buffer, sizeof(node.name));
strncpy(dir.name, buffer, sizeof(dir.name));
parent.files[parent.entries++] = &node;
kfree(buffer);
return true;
}
else { return false; }
}
filename += path_section_size;
}
}
static bool initrd_register_file(InitRD::File& f, uint64_t inode)
{
const char* filename = f.name;
VFS::Node* current_node = &initrd_root;
while (true)
{
while (*filename == '/') { filename++; }
if (*filename == 0) { return false; }
size_t path_section_size = 0;
while (filename[path_section_size] && filename[path_section_size] != '/') { path_section_size++; }
if (filename[path_section_size]) // We are in a '/'
{
char* buffer = (char*)kmalloc(path_section_size + 1);
memcpy(buffer, filename, path_section_size);
buffer[path_section_size] = 0;
if (!current_node->find_func) { return false; }
VFS::Node* child = current_node->find_func(current_node, buffer);
if (!child) { return false; }
current_node = child;
kfree(buffer);
}
else
{
if (strncmp(filename, ".", path_section_size) != 0) // The current path section is not '.'
{
if (strncmp(filename, "..", path_section_size) == 0) { return false; }
if (!current_node->find_func) { return false; }
InitRD::Directory& parent = dirs[current_node->inode];
if (parent.entries == INITRD_MAX_FILES_IN_DIR) { return false; }
char* buffer = (char*)kmalloc(path_section_size + 1);
memcpy(buffer, filename, path_section_size);
buffer[path_section_size] = 0;
VFS::Node& node = nodes[total_nodes++];
node.inode = inode;
node.read_func = initrd_read;
node.length = f.size;
strncpy(node.name, buffer, sizeof(node.name));
strncpy(f.name, buffer, sizeof(f.name));
parent.files[parent.entries++] = &node;
kfree(buffer);
return true;
}
else { return false; }
}
filename += path_section_size;
}
}
static void initrd_scan()
{
initrd_for_each_dir([](InitRD::Directory& dir) {
if (total_dirs >= 32) return;
uint64_t inode = total_dirs;
if (initrd_register_dir(dir, inode)) dirs[total_dirs++] = dir;
});
InitRD::for_each([](InitRD::File& f) {
if (total_files >= 32) return;
uint64_t inode = total_files;
if (initrd_register_file(f, inode)) files[total_files++] = f;
});
}
static void initrd_initialize_root()
{
initrd_root.length = 0;
initrd_root.inode = 0;
InitRD::Directory& root = dirs[0];
total_dirs++;
strncpy(initrd_root.name, "initrd", sizeof(initrd_root.name));
strncpy(root.name, "initrd", sizeof(root.name));
initrd_root.find_func = initrd_scan_dir;
}
void InitRD::init()
{
initrd_base =
MemoryManager::get_unaligned_mappings((void*)bootboot.initrd_ptr, bootboot.initrd_size / PAGE_SIZE + 1);
kdbgln("physical base at %lx, size %lx, mapped to %p", bootboot.initrd_ptr, bootboot.initrd_size, initrd_base);
kdbgln("total blocks: %ld", get_total_blocks());
initrd_initialize_root();
initrd_scan();
VFS::mount_root(&initrd_root);
initrd_initialized = true;
}

View File

@ -4,6 +4,7 @@
#include "assert.h"
#include "config.h"
#include "cpu/CPU.h"
#include "fs/VFS.h"
#include "gdt/GDT.h"
#include "init/Init.h"
#include "init/InitRD.h"
@ -159,6 +160,27 @@ extern "C" void _start()
kinfoln("Interrupts enabled");
kinfoln("Trying VFS");
VFS::Node* node = VFS::resolve_path("/sys/");
if (!node) { kerrorln("Unable to find /sys in VFS"); }
else
{
kinfoln("Found '%s'", node->name);
node = VFS::resolve_path("config", node);
if (!node) kerrorln("unable to find /sys/config in VFS");
else
{
kinfoln("Found '%s'", node->name);
char* buffer = (char*)kmalloc(node->length + 1);
buffer[node->length] = 0;
ssize_t nread = VFS::read(node, 0, node->length, buffer);
kdbgln("Read %zd bytes", nread);
kinfoln("Read: %s", buffer);
kfree(buffer);
}
}
PCI::scan([](PCI::Device& dev) {
kinfoln("Found PCI device %x:%x, %s", dev.id().vendor, dev.id().device, pci_type_name(dev.type()));
});

View File

@ -26,6 +26,9 @@ void Syscall::entry(Context* context)
case SYS_gettid: sys_gettid(context); break;
case SYS_mmap: sys_mmap(context, (void*)context->rdi, context->rsi, (int)context->rdx); break;
case SYS_munmap: sys_munmap(context, (void*)context->rdi, context->rsi); break;
case SYS_open: sys_open(context, (const char*)context->rdi, (int)context->rsi); break;
case SYS_read: sys_read(context, (int)context->rdi, context->rsi, (char*)context->rdx); break;
case SYS_close: sys_close(context, (int)context->rdi); break;
default: context->rax = -ENOSYS; break;
}
}

View File

@ -1,9 +1,108 @@
#define MODULE "stdio"
#include "errno.h"
#include "interrupts/Context.h"
#include "io/Serial.h"
#include "log/Log.h"
#include "render/TextRenderer.h"
#include "thread/Scheduler.h"
#include "thread/Task.h"
#define STDIO_FAIL(function, error) kwarnln("%s failed with %s", #function, #error)
void sys_write(Context* context, const char* addr, size_t size)
{
context->rax = size;
TextRenderer::write(addr, size);
}
void sys_open(Context* context, const char* filename, int flags)
{
Task* current_task = Scheduler::current_task();
int fd = -1;
for (fd = 0; fd < TASK_MAX_FDS; fd++)
{
if (!current_task->files[fd].is_open()) break;
}
if (fd == -1)
{
STDIO_FAIL(open, EMFILE);
context->rax = -EMFILE;
return;
}
VFS::Node* node = VFS::resolve_path(filename);
if (!node)
{
STDIO_FAIL(open, ENOENT);
context->rax = -ENOENT;
return;
}
current_task->files[fd].open(node,
(bool)flags); // FIXME: Implement more flags. (right now, 1 is can_read, 0 is not)
context->rax = fd;
return;
}
void sys_read(Context* context, int fd, size_t size, char* buffer)
{
if (!buffer)
{
STDIO_FAIL(read, EINVAL);
context->rax = -EINVAL;
return;
}
if (fd >= TASK_MAX_FDS)
{
STDIO_FAIL(read, EBADF);
context->rax = -EBADF;
return;
}
if (fd < 0)
{
STDIO_FAIL(read, EBADF);
context->rax = -EBADF;
return;
}
Task* current_task = Scheduler::current_task();
if (!current_task->files[fd].is_open())
{
STDIO_FAIL(read, EBADF);
context->rax = -EBADF;
return;
}
if (!current_task->files[fd].can_read())
{
STDIO_FAIL(read, EBADF);
context->rax = -EBADF;
return;
}
ssize_t result = current_task->files[fd].read(size, buffer);
context->rax = (size_t)result;
return;
}
void sys_close(Context* context, int fd)
{
if (fd >= TASK_MAX_FDS)
{
STDIO_FAIL(close, EBADF);
context->rax = -EBADF;
return;
}
if (fd < 0)
{
STDIO_FAIL(close, EBADF);
context->rax = -EBADF;
return;
}
Task* current_task = Scheduler::current_task();
if (!current_task->files[fd].is_open())
{
STDIO_FAIL(close, EBADF);
context->rax = -EBADF;
return;
}
current_task->files[fd].close();
context->rax = 0;
return;
}

View File

@ -21,7 +21,7 @@ typedef struct stackframe
void StackTracer::trace()
{
stackframe* frame = (stackframe*)m_base_pointer;
while (Memory::is_kernel_address((uintptr_t)frame))
while (frame)
{
char symbol_name[512];
get_symbol_name(frame->instruction, symbol_name);
@ -32,11 +32,6 @@ void StackTracer::trace()
void StackTracer::trace_with_ip(uintptr_t ip)
{
if (!Memory::is_kernel_address(ip))
{
printf("(user stack)");
return;
}
char symbol_name[512];
get_symbol_name(ip, symbol_name);
printf("%lx: %s\n", ip, symbol_name);

View File

@ -7,7 +7,7 @@
do { \
if (rc < 0) \
{ \
errno = (int)rc; \
errno = (int)(-rc); \
return -1; \
} \
return (type)rc; \

View File

@ -4,8 +4,11 @@
extern int errno;
#define EPERM 1
#define ENOENT 2
#define EBADF 9
#define ENOMEM 12
#define EINVAL 22
#define EMFILE 24
#define ENOSYS 38
#endif

View File

@ -11,6 +11,9 @@
#define SYS_gettid 7
#define SYS_mmap 8
#define SYS_munmap 9
#define SYS_open 10
#define SYS_read 11
#define SYS_close 12
#ifndef __want_syscalls
#ifdef __cplusplus

View File

@ -121,7 +121,11 @@ extern "C"
case EINVAL: return "Invalid argument";
case ENOMEM: return "Out of memory";
case ENOSYS: return "Function not implemented";
default: return 0;
case ENOENT: return "No such file or directory";
case EBADF: return "Bad file descriptor";
case EMFILE: return "Too many open files";
case 0: return "Success";
default: return (char*)(unsigned long int)errnum;
}
}

View File

@ -35,15 +35,18 @@ extern "C"
case SYS_gettid:
case SYS_rand: result = __luna_syscall0(number); break;
case SYS_exit:
case SYS_close:
case SYS_sleep: result = __luna_syscall1(number, va_arg(ap, arg)); break;
case SYS_write:
case SYS_munmap:
case SYS_open:
case SYS_getversion: {
arg arg0 = va_arg(ap, arg);
arg arg1 = va_arg(ap, arg);
result = __luna_syscall2(number, arg0, arg1);
break;
}
case SYS_read:
case SYS_mmap: {
arg arg0 = va_arg(ap, arg);
arg arg1 = va_arg(ap, arg);