From 340bd4edeb0202a6b78cd5835215acfcfbfecc6a Mon Sep 17 00:00:00 2001 From: Freywar Ulvnaudgari Date: Thu, 18 Jun 2026 15:46:18 +0300 Subject: [PATCH] Convert file descriptors to generic streams --- src/kernel/fat16.c | 40 +++++++++++++++++++ src/kernel/fat16.h | 3 ++ src/kernel/fs.c | 4 ++ src/kernel/fs.h | 3 ++ src/kernel/kernel.c | 5 ++- src/kernel/keyboard.c | 5 ++- src/kernel/panic.h | 2 +- src/kernel/path.c | 11 ++++++ src/kernel/path.h | 4 +- src/kernel/process.c | 13 ++++++- src/kernel/process.h | 11 ++---- src/kernel/stream.h | 2 + src/kernel/syscall.c | 66 +++++++++++--------------------- src/kernel/vga.c | 5 ++- src/lib/util.h | 8 ++++ src/user/app/cat/cat.c | 16 ++++---- src/user/app/echo/echo.c | 5 ++- src/user/app/ls/ls.c | 16 ++++---- src/user/app/terminal/terminal.c | 5 ++- src/user/syscall.h | 5 ++- 20 files changed, 150 insertions(+), 79 deletions(-) diff --git a/src/kernel/fat16.c b/src/kernel/fat16.c index 2f8883c..e53c4b5 100644 --- a/src/kernel/fat16.c +++ b/src/kernel/fat16.c @@ -2,6 +2,7 @@ #include "src/kernel/ata.h" #include "src/kernel/fs.h" #include "src/kernel/panic.h" +#include "src/kernel/stream.h" #include "src/lib/memory.h" #include "src/lib/string.h" #include "src/lib/util.h" @@ -200,6 +201,45 @@ fs_node_t *fat16_open_again(const fs_node_t *source) { return result; } +typedef struct { + stream_t stream; + fs_node_t *node; + uint64_t offset; +} fat16_stream_t; + +static uint64_t stream_write(__attribute__((unused)) const stream_t *self, __attribute__((unused)) const char *from, + __attribute__((unused)) uint64_t bytes) { + return (uint64_t)-1; +} + +static uint64_t stream_read(const stream_t *self, uint64_t max, char *to) { + fat16_stream_t *fss = (fat16_stream_t *)self; + if (fss->offset >= fss->node->size) { + return 0; + } + uint64_t remaining = fss->node->size - fss->offset; + uint64_t to_read = remaining > max ? max : remaining; + fat16_read(fss->node, fss->offset, to_read, to); + fss->offset += to_read; + return to_read; +} + +static void stream_close(stream_t *self) { + fat16_stream_t *fss = (fat16_stream_t *)self; + fat16_close(fss->node); + memory_free(self); +} + +stream_t *fat16_open_stream(const fs_node_t *source) { + fat16_stream_t *result = memory_allocate(sizeof(fat16_node_t)); + result->stream.read = stream_read; + result->stream.write = stream_write; + result->stream.close = stream_close; + result->node = fat16_open_again(source); + result->offset = 0; + return (stream_t *)result; +} + void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) { ASSERT(file->type == FAT16, "fat16_read: file is not FAT16"); ASSERT(!file->is_dir, "fat16_read: can not read directory"); diff --git a/src/kernel/fat16.h b/src/kernel/fat16.h index e55573b..45616fc 100644 --- a/src/kernel/fat16.h +++ b/src/kernel/fat16.h @@ -1,6 +1,7 @@ #pragma once #include "src/kernel/fs.h" +#include "src/kernel/stream.h" #define FAT16 1 @@ -12,6 +13,8 @@ fs_node_t *fat16_open_at(const fs_node_t *directory, uint64_t index); fs_node_t *fat16_open_again(const fs_node_t *source); +stream_t *fat16_open_stream(const fs_node_t *source); + void fat16_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to); void fat16_close(fs_node_t *node); diff --git a/src/kernel/fs.c b/src/kernel/fs.c index 6d26978..43f5390 100644 --- a/src/kernel/fs.c +++ b/src/kernel/fs.c @@ -17,6 +17,10 @@ fs_node_t *fs_open_again(const fs_node_t *source) { return fat16_open_again(source); } +stream_t *fs_open_stream(const fs_node_t *source) { + return fat16_open_stream(source); +} + void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to) { fat16_read(file, offset, size, to); } diff --git a/src/kernel/fs.h b/src/kernel/fs.h index 571d5b5..e9b02cd 100644 --- a/src/kernel/fs.h +++ b/src/kernel/fs.h @@ -1,5 +1,6 @@ #pragma once +#include "src/kernel/stream.h" #include #define FILENAME_SIZE_LIMIT 255 @@ -19,6 +20,8 @@ fs_node_t *fs_open_at(const fs_node_t *directory, uint64_t index); fs_node_t *fs_open_again(const fs_node_t *source); +stream_t *fs_open_stream(const fs_node_t *source); + void fs_read(const fs_node_t *file, uint64_t offset, uint64_t size, void *to); void fs_close(fs_node_t *node); diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index 61fdb47..64c2318 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -53,8 +53,9 @@ void kernel_main() { kernel->pml4 = (void *)KERNEL_VIRTUAL_PML4; kernel->root = fs_mount(); kernel->cwd = memory_allocate(sizeof(path_t)); - kernel->stdin = keyboard_init(); - kernel->stdout = vga_init(); + kernel->fds[0] = keyboard_init(); + kernel->fds[1] = vga_init(); + kernel->free_fd = 2; fs_node_t *bin = path_open_file(kernel->root, kernel->cwd, "bin/terminal"); diff --git a/src/kernel/keyboard.c b/src/kernel/keyboard.c index e0aa7bc..a28ae2d 100644 --- a/src/kernel/keyboard.c +++ b/src/kernel/keyboard.c @@ -76,7 +76,10 @@ static uint64_t stream_read(__attribute__((unused)) const stream_t *self, uint64 return size; } -static stream_t stream = {stream_write, stream_read}; +static void stream_close(__attribute__((unused)) stream_t *self) { +} + +static stream_t stream = {stream_write, stream_read, stream_close}; static void on_key(uint8_t scancode) { const uint8_t pressed = !(scancode & 0x80); diff --git a/src/kernel/panic.h b/src/kernel/panic.h index 92ff484..3209f0c 100644 --- a/src/kernel/panic.h +++ b/src/kernel/panic.h @@ -2,7 +2,7 @@ #define ASSERT(cond, msg) \ if (!(cond)) { \ - kernel_panic(msg " (assertion: " #cond ")", __FILE__, __LINE__); \ + kernel_panic(msg " (assertion: " #cond ")", __FILE__, __LINE__); \ } void kernel_panic(const char *msg, const char *file, int line); diff --git a/src/kernel/path.c b/src/kernel/path.c index 8dde158..5af7ada 100644 --- a/src/kernel/path.c +++ b/src/kernel/path.c @@ -1,5 +1,6 @@ #include "src/kernel/path.h" #include "src/kernel/fs.h" +#include "src/kernel/stream.h" #include "src/lib/memory.h" #include "src/lib/string.h" #include "src/lib/util.h" @@ -86,6 +87,16 @@ fs_node_t *path_open_directory(const fs_node_t *root, const path_t *source, cons return n; } +stream_t *path_open_stream(const fs_node_t *root, const path_t *source, const char *path) { + path_t *p = path_open(root, source, path); + if (!p) { + return NUL; + } + stream_t *s = fs_open_stream(p->depth ? p->stack[p->depth - 1] : root); + path_close(p); + return s; +} + void path_close(path_t *path) { for (uint64_t i = 0; i < path->depth; i++) { fs_close(path->stack[i]); diff --git a/src/kernel/path.h b/src/kernel/path.h index 2952db3..b09fc7c 100644 --- a/src/kernel/path.h +++ b/src/kernel/path.h @@ -1,7 +1,7 @@ #pragma once #include "src/kernel/fs.h" -#include +#include "src/kernel/stream.h" #define PATH_DEPTH 255 @@ -18,4 +18,6 @@ fs_node_t *path_open_file(const fs_node_t *root, const path_t *source, const cha fs_node_t *path_open_directory(const fs_node_t *root, const path_t *source, const char *path); +stream_t *path_open_stream(const fs_node_t *root, const path_t *source, const char *path); + void path_close(path_t *path); diff --git a/src/kernel/process.c b/src/kernel/process.c index 99fb208..736a663 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -4,6 +4,7 @@ #include "src/kernel/path.h" #include "src/lib/layout.h" #include "src/lib/memory.h" +#include "src/lib/util.h" process_t *current_process; @@ -57,14 +58,22 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t proc->cwd = cwd; proc->root = parent->root; - proc->stdin = parent->stdin; - proc->stdout = parent->stdout; + proc->fds[0] = parent->fds[0]; + proc->fds[1] = parent->fds[1]; proc->free_fd = 2; + proc->code = EXIT_CODE_OK; return proc; } void process_destroy(process_t *proc) { + for (uint64_t i = 2; i < MAX_FDS; i++) { + if (proc->fds[i]) { + proc->fds[i]->close(proc->fds[i]); + proc->fds[i] = NUL; + } + } + path_close((path_t *)proc->cwd); for (uint16_t pml4i = 0; pml4i < 256; pml4i++) { diff --git a/src/kernel/process.h b/src/kernel/process.h index 375c633..1060f7b 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -3,16 +3,12 @@ #include "src/kernel/fs.h" #include "src/kernel/path.h" #include "src/kernel/stream.h" +#include "src/lib/util.h" #define USER_RFLAGS 0x202 #define MAX_FDS 16 -typedef struct { - fs_node_t *node; - uint64_t offset; -} fd_entry_t; - typedef struct process { const struct process *parent; uint64_t *pml4; @@ -22,10 +18,9 @@ typedef struct process { void *user_rsp; const fs_node_t *root; const path_t *cwd; - const stream_t *stdin; - const stream_t *stdout; - fd_entry_t fds[MAX_FDS]; + stream_t *fds[MAX_FDS]; uint64_t free_fd; + exit_code_t code; } process_t; typedef void (*app_t)(process_t *proc, uint8_t argc, char **argv); diff --git a/src/kernel/stream.h b/src/kernel/stream.h index 7efd353..a4d73f4 100644 --- a/src/kernel/stream.h +++ b/src/kernel/stream.h @@ -29,8 +29,10 @@ typedef struct stream stream_t; typedef uint64_t (*stream_write_t)(const stream_t *self, const char *from, uint64_t bytes); typedef uint64_t (*stream_read_t)(const stream_t *self, uint64_t max, char *to); +typedef void (*stream_close_t)(stream_t *self); typedef struct stream { stream_write_t write; stream_read_t read; + stream_close_t close; } stream_t; diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 531f475..f138c2f 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -3,6 +3,7 @@ #include "src/kernel/gdt.h" #include "src/kernel/path.h" #include "src/kernel/process.h" +#include "src/kernel/stream.h" #include "src/kernel/tss.h" #include "src/lib/layout.h" #include "src/lib/memory.h" @@ -34,37 +35,17 @@ void syscall_init() { } static uint64_t read(uint64_t fd, uint64_t max, char *to) { - switch (fd) { - case 0: - return current_process->stdin->read(current_process->stdin, max, to); - case 1: - return current_process->stdout->read(current_process->stdout, max, to); - default: - if (!current_process->fds[fd].node) { - return (uint64_t)-1; - } - if (current_process->fds[fd].offset >= current_process->fds[fd].node->size) { - return 0; - } - uint64_t remaining = current_process->fds[fd].node->size - current_process->fds[fd].offset; - uint64_t to_read = remaining > max ? max : remaining; - fs_read(current_process->fds[fd].node, current_process->fds[fd].offset, to_read, to); - current_process->fds[fd].offset += to_read; - return to_read; + if (fd >= MAX_FDS || !current_process->fds[fd]) { + return (uint64_t)-1; } - return (uint64_t)-1; + return current_process->fds[fd]->read(current_process->fds[fd], max, to); } static uint64_t write(uint64_t fd, const char *from, uint64_t bytes) { - switch (fd) { - case 0: - return current_process->stdin->write(current_process->stdin, from, bytes); - case 1: - return current_process->stdout->write(current_process->stdout, from, bytes); - default: + if (fd >= MAX_FDS || !current_process->fds[fd]) { return (uint64_t)-1; } - return (uint64_t)-1; + return current_process->fds[fd]->write(current_process->fds[fd], from, bytes); } static uint64_t getcwd(uint64_t max, char *to) { @@ -113,13 +94,13 @@ static uint64_t chdir(const char *path) { return current_process->cwd->depth; } -static uint64_t spawn(const char *path, uint64_t argc, const char **argv) { +static exit_code_t spawn(const char *path, uint64_t argc, const char **argv) { fs_node_t *node = path_open_file(current_process->root, current_process->cwd, path); if (!node) { - return (uint64_t)-1; + return EXIT_CODE_NOT_FOUND; } - uint8_t *code = memory_allocate(node->size); - fs_read(node, 0, node->size, code); + uint8_t *bin = memory_allocate(node->size); + fs_read(node, 0, node->size, bin); uint64_t size = 0; uint64_t sizes[16]; @@ -133,9 +114,9 @@ static uint64_t spawn(const char *path, uint64_t argc, const char **argv) { memory_copy(argv[i], sizes[i], blob + offsets[i]); } - process_t *child = process_create(current_process, code, node->size); + process_t *child = process_create(current_process, bin, node->size); - memory_free(code); + memory_free(bin); fs_close(node); __asm__ volatile("mov %0, %%cr3" : : "r"((uint64_t)VIRT_TO_PHYS((void *)KERNEL_VIRTUAL_PML4)) : "memory"); @@ -163,11 +144,12 @@ static uint64_t spawn(const char *path, uint64_t argc, const char **argv) { process_switch_to(&parent->kernel_rsp, child->kernel_rsp, VIRT_TO_PHYS(child->pml4)); + exit_code_t code = child->code; tss.rsp0 = (uint64_t)parent->kernel_stack; current_process = parent; process_destroy(child); - return 0; + return code; } static uint64_t readdir(const char *path, uint64_t index, uint64_t max, char *to) { @@ -199,27 +181,25 @@ static uint64_t open(const char *path) { if (current_process->free_fd >= MAX_FDS) { return (uint64_t)-1; } - fs_node_t *node = path_open_node(current_process->root, current_process->cwd, path); - if (!node) { + stream_t *stream = path_open_stream(current_process->root, current_process->cwd, path); + if (!stream) { return (uint64_t)-1; } - current_process->fds[current_process->free_fd].node = node; - current_process->fds[current_process->free_fd].offset++; - current_process->free_fd++; + current_process->fds[current_process->free_fd++] = stream; return current_process->free_fd - 1; } static uint64_t close(uint64_t fd) { - if (fd >= MAX_FDS || !current_process->fds[fd].node) { + if (fd >= MAX_FDS || !current_process->fds[fd]) { return (uint64_t)-1; } - fs_close(current_process->fds[fd].node); - current_process->fds[fd].node = NUL; - current_process->fds[fd].offset = 0; + current_process->fds[fd]->close(current_process->fds[fd]); + current_process->fds[fd] = NUL; return 0; } -static void exit() { +static void exit(exit_code_t code) { + current_process->code = code; process_switch_to(¤t_process->kernel_rsp, current_process->parent->kernel_rsp, VIRT_TO_PHYS(current_process->parent->pml4)); } @@ -242,7 +222,7 @@ uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t case SYSCALL_CLOSE: return close(arg1); case SYSCALL_EXIT: - exit(); + exit(arg1); return 0; default: return (uint64_t)-1; diff --git a/src/kernel/vga.c b/src/kernel/vga.c index fbc97cd..cb04e5e 100644 --- a/src/kernel/vga.c +++ b/src/kernel/vga.c @@ -155,7 +155,10 @@ static uint64_t stream_read(__attribute__((unused)) const stream_t *self, __attr return 0; } -static stream_t stream = {stream_write, stream_read}; +static void stream_close(__attribute__((unused)) stream_t *self) { +} + +static stream_t stream = {stream_write, stream_read, stream_close}; stream_t *vga_init() { clear(); diff --git a/src/lib/util.h b/src/lib/util.h index 6551b63..9cffaf5 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -1,3 +1,11 @@ #pragma once +#include + #define NUL 0 // TODO Fix VSCode thinking `NULL` conflicts with some other definition. + +typedef uint64_t exit_code_t; + +#define EXIT_CODE_OK 0 +#define EXIT_CODE_NOT_FOUND ((uint64_t)-2) +#define EXIT_CODE_GENERAL_FAILURE ((uint64_t)-1) diff --git a/src/user/app/cat/cat.c b/src/user/app/cat/cat.c index 38dd902..dbd5baa 100644 --- a/src/user/app/cat/cat.c +++ b/src/user/app/cat/cat.c @@ -1,3 +1,4 @@ +#include "src/lib/util.h" #include "src/user/syscall.h" #define BLOCK_SIZE 65536 @@ -5,12 +6,12 @@ #define PRINT_S(s) write(1, s, sizeof(s) - 1); #define PRINT_D(s) write(1, s, string_length(s)); -uint64_t cat(const char *path) { +exit_code_t cat(const char *path) { uint64_t fd = open(path); if (fd == (uint64_t)-1) { PRINT_S("cat: path does not exist\n"); - return (uint64_t)-1; + return EXIT_CODE_GENERAL_FAILURE; } static char buffer[BLOCK_SIZE]; @@ -19,7 +20,7 @@ uint64_t cat(const char *path) { if (bytes == (uint64_t)-1) { PRINT_S("cat: could not read file\n"); close(fd); - return (uint64_t)-1; + return EXIT_CODE_GENERAL_FAILURE; } write(1, buffer, bytes); } @@ -29,15 +30,16 @@ uint64_t cat(const char *path) { return 0; } -uint64_t main(uint64_t argc, const char **argv) { +exit_code_t main(uint64_t argc, const char **argv) { if (argc == 1) { return cat("."); } - uint64_t code = 0; + uint64_t code = EXIT_CODE_OK; for (uint64_t i = 1; i < argc; i++) { - if (cat(argv[i]) == (uint64_t)-1) { - code = (uint64_t)-1; + exit_code_t c = cat(argv[i]); + if (c != EXIT_CODE_OK) { + code = c; } } diff --git a/src/user/app/echo/echo.c b/src/user/app/echo/echo.c index 9a08cfd..8e06eb8 100644 --- a/src/user/app/echo/echo.c +++ b/src/user/app/echo/echo.c @@ -1,7 +1,8 @@ #include "src/lib/string.h" +#include "src/lib/util.h" #include "src/user/syscall.h" -uint64_t main(uint64_t argc, const char **argv) { +exit_code_t main(uint64_t argc, const char **argv) { for (uint64_t i = 1; i < argc; i++) { if (i > 1) { write(1, " ", 1); @@ -9,5 +10,5 @@ uint64_t main(uint64_t argc, const char **argv) { write(1, argv[i], string_length(argv[i])); } write(1, "\n", 1); - return 0; + return EXIT_CODE_OK; } diff --git a/src/user/app/ls/ls.c b/src/user/app/ls/ls.c index 7c04315..22ec67d 100644 --- a/src/user/app/ls/ls.c +++ b/src/user/app/ls/ls.c @@ -1,20 +1,21 @@ #include "src/lib/string.h" +#include "src/lib/util.h" #include "src/user/syscall.h" #define PRINT_S(s) write(1, s, sizeof(s) - 1); #define PRINT_D(s) write(1, s, string_length(s)); -uint64_t ls(const char *path) { +exit_code_t ls(const char *path) { uint64_t i = 0; while (1) { char out[100]; uint64_t s = readdir(path, i, 100, out); if (!s) { - return 0; + return EXIT_CODE_OK; } if (s == (uint64_t)-1) { PRINT_S("ls: path does not exist\n"); - return (uint64_t)-1; + return EXIT_CODE_GENERAL_FAILURE; } PRINT_D(out); PRINT_S("\n"); @@ -22,20 +23,21 @@ uint64_t ls(const char *path) { } } -uint64_t main(uint64_t argc, const char **argv) { +exit_code_t main(uint64_t argc, const char **argv) { if (argc == 1) { return ls("."); } - uint64_t code = 0; + exit_code_t code = EXIT_CODE_OK; for (uint64_t i = 1; i < argc; i++) { if (argc > 2) { PRINT_S("\n"); PRINT_D(argv[i]); PRINT_S(":\n"); } - if (ls(argv[i]) == (uint64_t)-1) { - code = (uint64_t)-1; + exit_code_t c = ls(argv[i]); + if (c != EXIT_CODE_OK) { + code = c; } } diff --git a/src/user/app/terminal/terminal.c b/src/user/app/terminal/terminal.c index a2641b2..f62e54c 100644 --- a/src/user/app/terminal/terminal.c +++ b/src/user/app/terminal/terminal.c @@ -1,5 +1,6 @@ #include "src/lib/memory.h" #include "src/lib/string.h" +#include "src/lib/util.h" #include "src/user/syscall.h" #include @@ -123,7 +124,7 @@ static void on_enter_pressed() { char *path = memory_allocate(size); memory_copy(PATH, sizeof(PATH), path); memory_copy(argv[0], size - sizeof(PATH), path + sizeof(PATH) - 1); - if (spawn(path, argc, (const char **)argv) == (uint64_t)-1) { + if (spawn(path, argc, (const char **)argv) == EXIT_CODE_NOT_FOUND) { PRINT_S("terminal: program not found\n"); } memory_free(path); @@ -140,7 +141,7 @@ static void on_cancel_pressed() { static void on_disconnect_pressed() { PRINT_S("^D\n"); - exit(); + exit(EXIT_CODE_OK); } static void on_ctrl_character_pressed(char c) { diff --git a/src/user/syscall.h b/src/user/syscall.h index 8f94ae0..d2878b1 100644 --- a/src/user/syscall.h +++ b/src/user/syscall.h @@ -1,5 +1,6 @@ #pragma once +#include "src/lib/util.h" #include uint64_t read(int64_t fd, uint64_t max, void *to); @@ -10,7 +11,7 @@ uint64_t getcwd(uint64_t max, void *to); uint64_t chdir(const char *path); -uint64_t spawn(const char *path, uint64_t argc, const char **argv); +exit_code_t spawn(const char *path, uint64_t argc, const char **argv); uint64_t readdir(const char *path, uint64_t index, uint64_t max, char *to); @@ -18,4 +19,4 @@ uint64_t open(const char *path); uint64_t close(uint64_t fd); -void exit(); +void exit(exit_code_t code);