From bcf2e5bac69a1132821c8ab6f18b91c45b60803e Mon Sep 17 00:00:00 2001 From: Freywar Ulvnaudgari Date: Wed, 17 Jun 2026 23:20:43 +0300 Subject: [PATCH] Add file descriptors and basic `cat` --- build.sh | 1 + meson.build | 39 ++++++++++++++++++++ src/kernel/path.c | 12 ++++++- src/kernel/path.h | 2 ++ src/kernel/process.c | 1 + src/kernel/process.h | 9 +++++ src/kernel/syscall.c | 61 +++++++++++++++++++++++++++++--- src/kernel/syscall.h | 2 ++ src/user/app/cat/cat.c | 45 +++++++++++++++++++++++ src/user/app/ls/ls.c | 16 ++++----- src/user/app/terminal/terminal.c | 32 ++++++++--------- src/user/syscall.asm | 12 +++++++ src/user/syscall.h | 4 +++ 13 files changed, 207 insertions(+), 29 deletions(-) create mode 100644 src/user/app/cat/cat.c diff --git a/build.sh b/build.sh index 82e6d68..eebcb38 100755 --- a/build.sh +++ b/build.sh @@ -19,3 +19,4 @@ mmd -i build/os.img@@"$((partition_offset * sector_size))" ::bin mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/terminal ::bin/terminal mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/echo ::bin/echo mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/ls ::bin/ls +mcopy -i build/os.img@@"$((partition_offset * sector_size))" build/cat ::bin/cat diff --git a/meson.build b/meson.build index 9002f91..21c1c60 100644 --- a/meson.build +++ b/meson.build @@ -223,3 +223,42 @@ custom_target( command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'], build_by_default: true, ) + +cat_start = custom_target( + 'cat_start', + input: 'src/user/start.asm', + output: 'cat_start.o', + command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], +) + +cat_syscall = custom_target( + 'cat_syscall', + input: 'src/user/syscall.asm', + output: 'cat_syscall.o', + command: [nasm, '-f', 'elf64', '-o', '@OUTPUT@', '@INPUT@'], +) + +cat_elf = executable( + 'cat.elf', + sources: [cat_start, cat_syscall, lib_sources, 'src/user/app/cat/cat.c'], + c_args: [ + '-ffreestanding', + '-nostdlib', + '-fno-stack-protector', + '-mcmodel=large', + '-DVERSION="' + meson.project_version() + '"', + ], + link_args: [ + '-T', meson.project_source_root() / 'src/user/linker.ld', + '-nostdlib', + ], + link_depends: 'src/user/linker.ld', +) + +custom_target( + 'cat', + input: cat_elf, + output: 'cat', + command: ['objcopy', '-O', 'binary', '@INPUT@', '@OUTPUT@'], + build_by_default: true, +) diff --git a/src/kernel/path.c b/src/kernel/path.c index 93a29aa..8dde158 100644 --- a/src/kernel/path.c +++ b/src/kernel/path.c @@ -44,6 +44,16 @@ path_t *path_open(const fs_node_t *root, const path_t *source, const char *path) return result; } +fs_node_t *path_open_node(const fs_node_t *root, const path_t *source, const char *path) { + path_t *p = path_open(root, source, path); + if (!p) { + return NUL; + } + fs_node_t *n = fs_open_again(p->depth ? p->stack[p->depth - 1] : root); + path_close(p); + return n; +} + fs_node_t *path_open_file(const fs_node_t *root, const path_t *source, const char *path) { path_t *p = path_open(root, source, path); if (!p) { @@ -65,7 +75,7 @@ fs_node_t *path_open_directory(const fs_node_t *root, const path_t *source, cons } if (!p->depth) { path_close(p); - return (fs_node_t *)root; + return fs_open_again(root); } if (!p->stack[p->depth - 1]->is_dir) { path_close(p); diff --git a/src/kernel/path.h b/src/kernel/path.h index 35023fa..2952db3 100644 --- a/src/kernel/path.h +++ b/src/kernel/path.h @@ -12,6 +12,8 @@ typedef struct { path_t *path_open(const fs_node_t *root, const path_t *source, const char *path); +fs_node_t *path_open_node(const fs_node_t *root, const path_t *source, const char *path); + fs_node_t *path_open_file(const fs_node_t *root, const path_t *source, const char *path); fs_node_t *path_open_directory(const fs_node_t *root, const path_t *source, const char *path); diff --git a/src/kernel/process.c b/src/kernel/process.c index 9b05f68..99fb208 100644 --- a/src/kernel/process.c +++ b/src/kernel/process.c @@ -59,6 +59,7 @@ process_t *process_create(const process_t *parent, const uint8_t *code, uint64_t proc->root = parent->root; proc->stdin = parent->stdin; proc->stdout = parent->stdout; + proc->free_fd = 2; return proc; } diff --git a/src/kernel/process.h b/src/kernel/process.h index 78468bd..375c633 100644 --- a/src/kernel/process.h +++ b/src/kernel/process.h @@ -6,6 +6,13 @@ #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; @@ -17,6 +24,8 @@ typedef struct process { const path_t *cwd; const stream_t *stdin; const stream_t *stdout; + fd_entry_t fds[MAX_FDS]; + uint64_t free_fd; } process_t; typedef void (*app_t)(process_t *proc, uint8_t argc, char **argv); diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index 9313085..531f475 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -7,6 +7,7 @@ #include "src/lib/layout.h" #include "src/lib/memory.h" #include "src/lib/string.h" +#include "src/lib/util.h" #define MSR_EFER 0xC0000080 #define MSR_STAR 0xC0000081 @@ -33,13 +34,37 @@ void syscall_init() { } static uint64_t read(uint64_t fd, uint64_t max, char *to) { - (void)fd; - return current_process->stdin->read(current_process->stdin, max, 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; + } + return (uint64_t)-1; } static uint64_t write(uint64_t fd, const char *from, uint64_t bytes) { - (void)fd; - return current_process->stdout->write(current_process->stdout, from, 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: + return (uint64_t)-1; + } + return (uint64_t)-1; } static uint64_t getcwd(uint64_t max, char *to) { @@ -170,6 +195,30 @@ static uint64_t readdir(const char *path, uint64_t index, uint64_t max, char *to return size; } +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) { + 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++; + return current_process->free_fd - 1; +} + +static uint64_t close(uint64_t fd) { + if (fd >= MAX_FDS || !current_process->fds[fd].node) { + return (uint64_t)-1; + } + fs_close(current_process->fds[fd].node); + current_process->fds[fd].node = NUL; + current_process->fds[fd].offset = 0; + return 0; +} + static void exit() { process_switch_to(¤t_process->kernel_rsp, current_process->parent->kernel_rsp, VIRT_TO_PHYS(current_process->parent->pml4)); } @@ -188,6 +237,10 @@ uint64_t syscall_dispatch(uint64_t func, uint64_t arg1, uint64_t arg2, uint64_t return spawn((const char *)arg1, arg2, (const char **)arg3); case SYSCALL_READDIR: return readdir((const char *)arg1, arg2, arg3, (char *)arg4); + case SYSCALL_OPEN: + return open((const char *)arg1); + case SYSCALL_CLOSE: + return close(arg1); case SYSCALL_EXIT: exit(); return 0; diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index 68b29af..0848c65 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -8,6 +8,8 @@ #define SYSCALL_CHDIR 3 #define SYSCALL_SPAWN 4 #define SYSCALL_READDIR 5 +#define SYSCALL_OPEN 6 +#define SYSCALL_CLOSE 7 #define SYSCALL_EXIT 60 void syscall_init(); diff --git a/src/user/app/cat/cat.c b/src/user/app/cat/cat.c new file mode 100644 index 0000000..38dd902 --- /dev/null +++ b/src/user/app/cat/cat.c @@ -0,0 +1,45 @@ +#include "src/user/syscall.h" + +#define BLOCK_SIZE 65536 + +#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) { + uint64_t fd = open(path); + + if (fd == (uint64_t)-1) { + PRINT_S("cat: path does not exist\n"); + return (uint64_t)-1; + } + + static char buffer[BLOCK_SIZE]; + uint64_t bytes; + while ((bytes = read(fd, BLOCK_SIZE, buffer))) { + if (bytes == (uint64_t)-1) { + PRINT_S("cat: could not read file\n"); + close(fd); + return (uint64_t)-1; + } + write(1, buffer, bytes); + } + + close(fd); + + return 0; +} + +uint64_t main(uint64_t argc, const char **argv) { + if (argc == 1) { + return cat("."); + } + + uint64_t code = 0; + for (uint64_t i = 1; i < argc; i++) { + if (cat(argv[i]) == (uint64_t)-1) { + code = (uint64_t)-1; + } + } + + return code; +} diff --git a/src/user/app/ls/ls.c b/src/user/app/ls/ls.c index 1ac2974..7c04315 100644 --- a/src/user/app/ls/ls.c +++ b/src/user/app/ls/ls.c @@ -1,8 +1,8 @@ #include "src/lib/string.h" #include "src/user/syscall.h" -#define WRITE_S(s) write(1, s, sizeof(s) - 1); -#define WRITE_D(s) write(1, s, string_length(s)); +#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) { uint64_t i = 0; @@ -13,11 +13,11 @@ uint64_t ls(const char *path) { return 0; } if (s == (uint64_t)-1) { - WRITE_S("ls: path does not exist\n"); + PRINT_S("ls: path does not exist\n"); return (uint64_t)-1; } - WRITE_D(out); - WRITE_S("\n"); + PRINT_D(out); + PRINT_S("\n"); i++; } } @@ -30,9 +30,9 @@ uint64_t main(uint64_t argc, const char **argv) { uint64_t code = 0; for (uint64_t i = 1; i < argc; i++) { if (argc > 2) { - WRITE_S("\n"); - WRITE_D(argv[i]); - WRITE_S(":\n"); + PRINT_S("\n"); + PRINT_D(argv[i]); + PRINT_S(":\n"); } if (ls(argv[i]) == (uint64_t)-1) { code = (uint64_t)-1; diff --git a/src/user/app/terminal/terminal.c b/src/user/app/terminal/terminal.c index 7287c33..a2641b2 100644 --- a/src/user/app/terminal/terminal.c +++ b/src/user/app/terminal/terminal.c @@ -8,8 +8,8 @@ static void cd(uint8_t argc, char **argv); #define PATH "/bin/" -#define WRITE_S(s) write(1, s, sizeof(s) - 1); -#define WRITE_D(s) write(1, s, string_length(s)); +#define PRINT_S(s) write(1, s, sizeof(s) - 1); +#define PRINT_D(s) write(1, s, string_length(s)); #define PROMPT_LENGTH 255 @@ -34,25 +34,25 @@ static app_entry_t builtins[] = { static void help(uint8_t argc, __attribute__((unused)) char **argv) { if (argc) { - WRITE_S("help: expects no arguments"); + PRINT_S("help: expects no arguments"); return; } - WRITE_S("Available commands:\n"); + PRINT_S("Available commands:\n"); for (uint64_t i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) { - WRITE_D(builtins[i].name); - WRITE_S("\n"); + PRINT_D(builtins[i].name); + PRINT_S("\n"); } } static void cd(uint8_t argc, char **argv) { if (argc != 2) { - WRITE_S("cd: requires a single path\n"); + PRINT_S("cd: requires a single path\n"); return; } if (chdir(argv[1]) == (uint64_t)-1) { - WRITE_S("cd: path does not exist\n"); + PRINT_S("cd: path does not exist\n"); } } @@ -62,9 +62,9 @@ static void print_prompt() { char *components[16]; uint8_t cl = string_split(path, '/', 16, components); - WRITE_S("[") - WRITE_D(pl == 1 ? "/" : components[cl - 1]); - WRITE_S("]$ "); + PRINT_S("[") + PRINT_D(pl == 1 ? "/" : components[cl - 1]); + PRINT_S("]$ "); } static void on_home_pressed() { @@ -109,7 +109,7 @@ static void on_enter_pressed() { char *argv[16]; uint8_t argc = (uint8_t)string_split(prompt, ' ', 16, argv); - WRITE_S("\n"); + PRINT_S("\n"); uint8_t i; for (i = 0; i < sizeof(builtins) / sizeof(app_entry_t); i++) { @@ -124,7 +124,7 @@ static void on_enter_pressed() { 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) { - WRITE_S("terminal: program not found\n"); + PRINT_S("terminal: program not found\n"); } memory_free(path); } @@ -133,13 +133,13 @@ static void on_enter_pressed() { } static void on_cancel_pressed() { - WRITE_S("^C\n"); + PRINT_S("^C\n"); prompt_length = prompt_offset = 0; print_prompt(); } static void on_disconnect_pressed() { - WRITE_S("^D\n"); + PRINT_S("^D\n"); exit(); } @@ -269,7 +269,7 @@ uint64_t main() { memory_set('\b', PROMPT_LENGTH, (char *)bs); memory_set(' ', PROMPT_LENGTH, (char *)ws); - WRITE_D("Welcome to FreywarOS v" VERSION "!\n\n"); + PRINT_D("Welcome to FreywarOS v" VERSION "!\n\n"); print_prompt(); diff --git a/src/user/syscall.asm b/src/user/syscall.asm index 912c511..16b0d71 100644 --- a/src/user/syscall.asm +++ b/src/user/syscall.asm @@ -37,6 +37,18 @@ readdir: syscall ret +global open +open: + mov rax, 6 + syscall + ret + +global close +close: + mov rax, 7 + syscall + ret + global exit exit: mov rax, 60 diff --git a/src/user/syscall.h b/src/user/syscall.h index 059c143..8f94ae0 100644 --- a/src/user/syscall.h +++ b/src/user/syscall.h @@ -14,4 +14,8 @@ uint64_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); +uint64_t open(const char *path); + +uint64_t close(uint64_t fd); + void exit();