Start implementing read and write support in tmpfs

write seems to work fine, read just hangs there, waiting.
This commit is contained in:
apio 2022-11-12 20:11:26 +01:00
parent 046065c533
commit 42efc21110
11 changed files with 281 additions and 8 deletions

View File

@ -1,4 +1,4 @@
C_APPS := init sh uname uptime hello ps ls args cat stat su session date mkdir screen
C_APPS := init sh uname uptime hello ps ls args cat stat su session date mkdir screen touch write
CXX_APPS := hello-cpp
APPS_DIR := $(LUNA_ROOT)/apps

22
apps/src/touch.c Normal file
View File

@ -0,0 +1,22 @@
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc == 1)
{
fprintf(stderr, "Usage: %s [directory]\n", argv[0]);
return 1;
}
int fd = creat(argv[1], 0755);
if (fd < 0)
{
perror("creat");
return 1;
}
close(fd);
}

75
apps/src/write.c Normal file
View File

@ -0,0 +1,75 @@
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static char* echoing_fgets(char* buf, size_t size, FILE* stream)
{
char* s = buf;
memset(buf, 0, size);
size_t oldsize = size;
while (size)
{
int c = fgetc(stream);
if (c == EOF)
{
if (ferror(stream)) return NULL;
if (feof(stream))
{
if (s != buf) return s;
else
return NULL;
};
}
if ((char)c == '\b')
{
if (size != oldsize)
{
buf--;
size++;
putchar('\b');
}
}
else
{
size--;
*buf = (char)c;
buf++;
putchar((char)c);
if ((char)c == '\n') return s;
}
*buf = 0;
}
return s;
}
int main(int argc, char** argv)
{
if (argc == 1)
{
fprintf(stderr, "Usage: %s [directory]\n", argv[0]);
return 1;
}
int fd = open(argv[1], O_WRONLY | O_CREAT | O_APPEND, 0755);
if (fd < 0)
{
perror("open");
return 1;
}
FILE* fp = fdopen(fd, "a");
char buf[BUFSIZ];
echoing_fgets(buf, BUFSIZ, stdin);
fputs(buf, fp);
if (ferror(fp))
{
perror("fputs");
return 1;
}
fclose(fp);
}

View File

@ -12,5 +12,7 @@ namespace TmpFS
int mkdir(VFS::Node* node, const char* name, mode_t mode);
ssize_t read(VFS::Node* node, size_t offset, size_t length, char* buffer);
ssize_t write(VFS::Node* node, size_t offset, size_t length, char* buffer);
ssize_t write(VFS::Node* node, size_t offset, size_t length, const char* buffer);
VFS::Node* create(VFS::Node* node, const char* name, mode_t mode, uid_t uid, gid_t gid);
}

View File

@ -1,4 +1,5 @@
#pragma once
#include "utils/Result.h"
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
@ -25,6 +26,7 @@ namespace VFS
typedef Node* (*node_readdir)(Node*, long);
typedef uintptr_t (*node_mmap)(Node*, uintptr_t, size_t, int, off_t);
typedef long (*node_ioctl)(Node*, int, uintptr_t);
typedef Node* (*node_create)(Node*, const char*, mode_t, uid_t, gid_t);
struct Node
{
@ -49,6 +51,7 @@ namespace VFS
node_block block_func;
node_mmap mmap_func;
node_ioctl ioctl_func;
node_create create_func;
Node* link;
};
@ -60,11 +63,14 @@ namespace VFS
int do_mkdir(const char* path, const char* name, int uid, int gid, mode_t mode);
int do_mkdir(const char* pathname, int uid, int gid, mode_t mode);
Result<Node*> create(const char* pathname, mode_t mode, uid_t uid, gid_t gid);
int would_block(Node* node);
void mount_root(Node* root);
Node* resolve_path(const char* filename, Node* root = nullptr);
Node* resolve_parent(const char* filename, Node* root = nullptr);
bool exists(const char* pathname);

View File

@ -1,4 +1,9 @@
#define MODULE "tmpfs"
#include "fs/TmpFS.h"
#include "log/Log.h"
#include "memory/MemoryManager.h"
#include "misc/utils.h"
#include "std/errno.h"
#include "std/string.h"
#include "utils/Dynamic.h"
@ -8,7 +13,7 @@ namespace TmpFS
{
struct File
{
char* ptr;
void* ptr;
uint64_t pages;
uint64_t length;
};
@ -26,9 +31,30 @@ Dynamic<TmpFS::File> tmpfs_files;
extern uint64_t clock_now();
static bool resize_file(TmpFS::File& file, size_t new_size)
{
size_t pages = Utilities::get_blocks_from_size(PAGE_SIZE, new_size);
if (pages > file.pages) // resize only if we need a bigger chunk of memory
{
auto rc = MemoryManager::get_pages(pages, MAP_READ_WRITE);
if (rc.has_error()) return false;
void* new_ptr = rc.release_value();
if (file.ptr)
{
memcpy(new_ptr, file.ptr, file.length);
MemoryManager::release_pages(file.ptr, file.pages);
}
file.pages = pages;
file.ptr = new_ptr;
}
file.length = new_size;
return true;
}
VFS::Node* TmpFS::get()
{
if (tmpfs_root) return tmpfs_root;
kinfoln("initializing tmpfs instance");
tmpfs_root = new VFS::Node;
tmpfs_root->length = 0;
tmpfs_root->inode = 0;
@ -36,6 +62,7 @@ VFS::Node* TmpFS::get()
tmpfs_root->find_func = TmpFS::finddir;
tmpfs_root->readdir_func = TmpFS::readdir;
tmpfs_root->mkdir_func = TmpFS::mkdir;
tmpfs_root->create_func = TmpFS::create;
tmpfs_root->mode = 0777;
tmpfs_root->uid = tmpfs_root->gid = 0;
tmpfs_root->atime = tmpfs_root->ctime = tmpfs_root->mtime = clock_now();
@ -53,6 +80,7 @@ VFS::Node* TmpFS::finddir(VFS::Node* node, const char* filename)
if (!node) return nullptr;
if (node->inode >= tmpfs_dirs.size()) return nullptr;
auto& dir = tmpfs_dirs[node->inode];
kdbgln("searching for '%s' in '%s'", filename, node->name);
for (size_t i = 0; i < dir.files.size(); i++)
{
if (!strncmp(dir.files[i]->name, filename, sizeof(dir.files[i]->name))) return dir.files[i];
@ -67,6 +95,7 @@ VFS::Node* TmpFS::readdir(VFS::Node* node, long offset)
auto& dir = tmpfs_dirs[node->inode];
if (offset < 0) return nullptr;
if ((size_t)offset >= dir.files.size()) return nullptr;
kdbgln("reading offset %ld in '%s'", offset, node->name);
return dir.files[offset];
}
@ -80,6 +109,7 @@ int TmpFS::mkdir(VFS::Node* node, const char* name, mode_t mode)
new_node->find_func = TmpFS::finddir;
new_node->readdir_func = TmpFS::readdir;
new_node->mkdir_func = TmpFS::mkdir;
new_node->create_func = TmpFS::create;
new_node->length = 0;
new_node->type = VFS_DIRECTORY;
new_node->mode = mode;
@ -93,5 +123,64 @@ int TmpFS::mkdir(VFS::Node* node, const char* name, mode_t mode)
// object. Thus our changes to the parent would be erased as soon as we exit this function.
ensure(tmpfs_dirs.push({{}})); // FIXME: Handle OOM instead of panicking
node->length++;
kdbgln("created directory '%s' in '%s' (which now has %lu entries), mode %o", name, node->name, node->length, mode);
return 0;
}
VFS::Node* TmpFS::create(VFS::Node* node, const char* name, mode_t mode, uid_t uid, gid_t gid)
{
if (node->inode >= tmpfs_dirs.size()) return nullptr;
if (!(node->type & VFS_DIRECTORY)) return nullptr;
auto& parent = tmpfs_dirs[node->inode];
VFS::Node* new_node = new VFS::Node;
new_node->inode = tmpfs_files.size();
new_node->length = 0;
new_node->read_func = TmpFS::read;
new_node->write_func = TmpFS::write;
new_node->type = VFS_FILE;
new_node->mode = mode;
new_node->uid = uid;
new_node->gid = gid;
new_node->atime = new_node->ctime = new_node->mtime = clock_now();
strncpy(new_node->name, name, sizeof(new_node->name));
ensure(parent.files.push(
new_node)); // We have to push the new node to the parent BEFORE we push the directory object to the list of
// tmpfs directories. This is crucial, since pushing to the list of tmpfs directories could relocate
// said list, where the parent object is stored, while we would still be using the old parent
// object. Thus our changes to the parent would be erased as soon as we exit this function.
ensure(tmpfs_files.push({nullptr, 0, 0})); // FIXME: Handle OOM instead of panicking
node->length++;
kdbgln("created file '%s' in '%s' (which now has %lu entries), mode %o", name, node->name, node->length, mode);
return 0;
}
ssize_t TmpFS::read(VFS::Node* node, size_t offset, size_t length, char* buffer)
{
kdbgln("got to read()");
if (!node) return -1;
if (node->inode >= tmpfs_files.size()) return -1;
File& file = tmpfs_files[node->inode];
size_t old_len = length;
if (offset > file.length) return -1;
if (offset + length > file.length) { length = file.length - offset; }
kdbgln("reading %zu bytes from '%s' at offset %zu (requested length was %zu)", length, node->name, offset, old_len);
memcpy(buffer, (void*)((uintptr_t)file.ptr + offset), length);
return length;
}
ssize_t TmpFS::write(VFS::Node* node, size_t offset, size_t length, const char* buffer)
{
if (!node) return -1;
if (node->inode >= tmpfs_files.size()) return -1;
File& file = tmpfs_files[node->inode];
size_t new_length = file.length;
if (offset + length > file.length) new_length = offset + length;
if (new_length != file.length)
{
if (!resize_file(file, new_length)) return -ENOMEM;
node->length = new_length;
}
kdbgln("writing %zu bytes to '%s' at offset %zu", length, node->name, offset);
memcpy((void*)((uintptr_t)file.ptr + offset), buffer, length);
return length;
}

View File

@ -129,6 +129,14 @@ VFS::Node* VFS::resolve_path(const char* filename, Node* root)
}
}
VFS::Node* VFS::resolve_parent(const char* filename, Node* root)
{
char* dir = better_dirname(filename);
auto* result = resolve_path(dir, root);
kfree(dir);
return result;
}
int VFS::mkdir(const char* path, const char* name)
{
Node* node = resolve_path(path, vfs_root);
@ -236,6 +244,38 @@ int VFS::do_mkdir(const char* pathname, int uid, int gid, mode_t mode)
return result;
}
Result<VFS::Node*> VFS::create(const char* pathname, mode_t mode, uid_t uid, gid_t gid)
{
VFS::Node* parent = VFS::resolve_parent(pathname);
if (!parent) return {ENOENT};
if (parent->type != VFS_DIRECTORY)
{
kwarnln("Attempting to create %s, parent is not a directory", pathname);
return {ENOTDIR};
}
if (!parent->create_func)
{
kwarnln("Chosen node does not support create()");
return {ENOTSUP}; // FIXME: Probably EROFS.
}
if (!parent->find_func)
{
kwarnln("Chosen node does not support finddir()");
return {ENOTSUP};
}
if (!can_write(parent, uid, gid)) return {EACCES};
char* child = better_basename(pathname);
if (parent->find_func(parent, child))
{
kwarnln("Already exists");
kfree(child);
return {EEXIST};
}
VFS::Node* child_node = parent->create_func(parent, child, mode, uid, gid);
kfree(child);
return child_node;
}
bool VFS::exists(const char* pathname)
{
return resolve_path(pathname) != nullptr;

View File

@ -115,6 +115,28 @@ static int internal_printf(const char* format, PutString put_string_callback, ss
}
break;
}
case 'o': {
if (is_unsigned_long || is_long)
{
char result[25];
ultoa(va_arg(ap, uint64_t), result, 8);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
is_unsigned_long = is_long = false;
}
else
{
char result[25];
utoa(va_arg(ap, uint32_t), result, 8);
if (buffer_insert_index + strlen(result) > 1024) flush_buffer();
memcpy(buffer + buffer_insert_index, result, strlen(result));
buffer_insert_index += strlen(result);
if (buffer_insert_index == 1024) flush_buffer();
}
break;
}
case 'u': {
if (is_unsigned_long || is_long)
{

View File

@ -50,6 +50,8 @@ void sys_fork(Context* context)
child->gid = parent->gid;
child->egid = parent->egid;
child->umask = parent->umask;
child->regs.rax = 0;
context->rax = child->id;

View File

@ -160,7 +160,7 @@ void sys_write(Context* context, int fd, size_t size, const char* addr)
return;
}
void sys_open(Context* context, const char* filename, int flags, mode_t) // FIXME: mode is not used.
void sys_open(Context* context, const char* filename, int flags, mode_t mode)
{
Task* current_task = Scheduler::current_task();
int fd = current_task->alloc_fd();
@ -182,9 +182,21 @@ void sys_open(Context* context, const char* filename, int flags, mode_t) // FIXM
if (!node)
{
bool create = (flags & OPEN_CREATE) > 0;
if (create) kwarnln("FIXME: open(O_CREAT) is not implemented");
if (create)
{
mode = mode & (~current_task->umask);
auto rc = VFS::create(kfilename, mode, current_task->euid, current_task->egid);
if (rc.has_error()) context->rax = -rc.error();
else
{
kinfoln("Created file %s with mode %o, uid %d and gid %d", kfilename, mode, current_task->euid,
current_task->egid);
node = rc.release_value();
goto do_open;
}
}
else { context->rax = -ENOENT; }
kfree(kfilename);
context->rax = -ENOENT;
return;
}
else
@ -199,6 +211,8 @@ void sys_open(Context* context, const char* filename, int flags, mode_t) // FIXM
}
}
do_open:
bool can_read = (flags & OPEN_READ) > 0;
bool can_write = (flags & OPEN_WRITE) > 0;
if (!can_read && !can_write)
@ -210,7 +224,7 @@ void sys_open(Context* context, const char* filename, int flags, mode_t) // FIXM
if (can_read && !VFS::can_read(node, current_task->euid, current_task->egid))
{
kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %d for reading",
kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %o for reading",
current_task->euid, current_task->egid, kfilename, node->mode);
kfree(kfilename);
context->rax = -EACCES;
@ -219,7 +233,7 @@ void sys_open(Context* context, const char* filename, int flags, mode_t) // FIXM
if (can_write && !VFS::can_write(node, current_task->euid, current_task->egid))
{
kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %d for writing",
kwarnln("open failed because process with uid %d and gid %d couldn't open file %s with mode %o for writing",
current_task->euid, current_task->egid, kfilename, node->mode);
kfree(kfilename);
context->rax = -EACCES;

View File

@ -198,6 +198,7 @@ long Scheduler::load_user_task(const char* filename)
new_task->user_task = true;
new_task->regs.rip = image->entry;
new_task->image = image;
new_task->umask = 0;
new_task->allocated_stack = (uint64_t)MemoryManager::get_pages_at(0x100000, TASK_PAGES_IN_STACK,
MAP_READ_WRITE | MAP_USER | MAP_AS_OWNED_BY_TASK)
.release_value(); // FIXME: Propagate errors.