diff --git a/apps/Makefile b/apps/Makefile index b82251ca..3ae58d7e 100644 --- a/apps/Makefile +++ b/apps/Makefile @@ -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 diff --git a/apps/src/touch.c b/apps/src/touch.c new file mode 100644 index 00000000..b2b0a218 --- /dev/null +++ b/apps/src/touch.c @@ -0,0 +1,22 @@ +#include +#include +#include + +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); +} \ No newline at end of file diff --git a/apps/src/write.c b/apps/src/write.c new file mode 100644 index 00000000..1b541da9 --- /dev/null +++ b/apps/src/write.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include + +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); +} \ No newline at end of file diff --git a/kernel/include/fs/TmpFS.h b/kernel/include/fs/TmpFS.h index 159b406c..cb79f37a 100644 --- a/kernel/include/fs/TmpFS.h +++ b/kernel/include/fs/TmpFS.h @@ -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); } \ No newline at end of file diff --git a/kernel/include/fs/VFS.h b/kernel/include/fs/VFS.h index d8db1b15..410c9ec7 100644 --- a/kernel/include/fs/VFS.h +++ b/kernel/include/fs/VFS.h @@ -1,4 +1,5 @@ #pragma once +#include "utils/Result.h" #include #include #include @@ -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 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); diff --git a/kernel/src/fs/TmpFS.cpp b/kernel/src/fs/TmpFS.cpp index 9bc65fa2..e4e5fccb 100644 --- a/kernel/src/fs/TmpFS.cpp +++ b/kernel/src/fs/TmpFS.cpp @@ -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_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; } \ No newline at end of file diff --git a/kernel/src/fs/VFS.cpp b/kernel/src/fs/VFS.cpp index 9fe20d37..0d186efa 100644 --- a/kernel/src/fs/VFS.cpp +++ b/kernel/src/fs/VFS.cpp @@ -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::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; diff --git a/kernel/src/std/stdio.cpp b/kernel/src/std/stdio.cpp index 18af8db5..d17a70cc 100644 --- a/kernel/src/std/stdio.cpp +++ b/kernel/src/std/stdio.cpp @@ -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) { diff --git a/kernel/src/sys/exec.cpp b/kernel/src/sys/exec.cpp index 962d0c42..bcbd6572 100644 --- a/kernel/src/sys/exec.cpp +++ b/kernel/src/sys/exec.cpp @@ -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; diff --git a/kernel/src/sys/stdio.cpp b/kernel/src/sys/stdio.cpp index 453f0ff5..2058bd00 100644 --- a/kernel/src/sys/stdio.cpp +++ b/kernel/src/sys/stdio.cpp @@ -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; diff --git a/kernel/src/thread/Scheduler.cpp b/kernel/src/thread/Scheduler.cpp index ed73d08d..9ff79db3 100644 --- a/kernel/src/thread/Scheduler.cpp +++ b/kernel/src/thread/Scheduler.cpp @@ -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.