From: Tulio A M Mendes Date: Wed, 11 Feb 2026 22:27:23 +0000 (-0300) Subject: feat: core utilities (cat, ls, mkdir, rm) X-Git-Url: https://projects.tadryanom.me/docs/POSIX_ROADMAP.md?a=commitdiff_plain;h=89f47c694258d1d2a196ae17af0a4ccba7fced7a;p=AdrOS.git feat: core utilities (cat, ls, mkdir, rm) - user/cat.c: reads files or stdin, outputs to stdout - user/ls.c: lists directory entries via getdents syscall - user/mkdir.c: creates directories via mkdir syscall - user/rm.c: removes files (unlink) or directories (-r/-d rmdir) - Makefile: build rules for all four + initrd packaging as /bin/* - echo already exists as shell builtin - cppcheck clean, 19/19 smoke tests pass --- diff --git a/Makefile b/Makefile index 88a8bf0..4b0a136 100644 --- a/Makefile +++ b/Makefile @@ -47,6 +47,10 @@ ifeq ($(ARCH),x86) USER_ELF := user/init.elf ECHO_ELF := user/echo.elf SH_ELF := user/sh.elf + CAT_ELF := user/cat.elf + LS_ELF := user/ls.elf + MKDIR_ELF := user/mkdir.elf + RM_ELF := user/rm.elf INITRD_IMG := initrd.img MKINITRD := tools/mkinitrd endif @@ -134,8 +138,20 @@ $(ECHO_ELF): user/echo.c user/linker.ld $(SH_ELF): user/sh.c user/linker.ld @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(SH_ELF) user/sh.c user/errno.c -$(INITRD_IMG): $(MKINITRD) $(USER_ELF) $(ECHO_ELF) $(SH_ELF) - @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF):bin/init.elf $(ECHO_ELF):bin/echo.elf $(SH_ELF):bin/sh +$(CAT_ELF): user/cat.c user/linker.ld + @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(CAT_ELF) user/cat.c user/errno.c + +$(LS_ELF): user/ls.c user/linker.ld + @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(LS_ELF) user/ls.c user/errno.c + +$(MKDIR_ELF): user/mkdir.c user/linker.ld + @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(MKDIR_ELF) user/mkdir.c user/errno.c + +$(RM_ELF): user/rm.c user/linker.ld + @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(RM_ELF) user/rm.c user/errno.c + +$(INITRD_IMG): $(MKINITRD) $(USER_ELF) $(ECHO_ELF) $(SH_ELF) $(CAT_ELF) $(LS_ELF) $(MKDIR_ELF) $(RM_ELF) + @./$(MKINITRD) $(INITRD_IMG) $(USER_ELF):bin/init.elf $(ECHO_ELF):bin/echo.elf $(SH_ELF):bin/sh $(CAT_ELF):bin/cat $(LS_ELF):bin/ls $(MKDIR_ELF):bin/mkdir $(RM_ELF):bin/rm run: iso @rm -f serial.log qemu.log diff --git a/user/cat.c b/user/cat.c new file mode 100644 index 0000000..d234951 --- /dev/null +++ b/user/cat.c @@ -0,0 +1,100 @@ +/* AdrOS cat utility */ +#include +#include "user_errno.h" + +enum { + SYSCALL_WRITE = 1, + SYSCALL_EXIT = 2, + SYSCALL_OPEN = 4, + SYSCALL_READ = 5, + SYSCALL_CLOSE = 6, +}; + +static int sys_write(int fd, const void* buf, uint32_t len) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len) : "memory"); + return __syscall_fix(ret); +} + +static int sys_read(int fd, void* buf, uint32_t len) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_READ), "b"(fd), "c"(buf), "d"(len) : "memory"); + return __syscall_fix(ret); +} + +static int sys_open(const char* path, int flags) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_OPEN), "b"(path), "c"(flags) : "memory"); + return __syscall_fix(ret); +} + +static int sys_close(int fd) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_CLOSE), "b"(fd) : "memory"); + return __syscall_fix(ret); +} + +static __attribute__((noreturn)) void sys_exit(int code) { + __asm__ volatile("int $0x80" : : "a"(SYSCALL_EXIT), "b"(code) : "memory"); + for (;;) __asm__ volatile("hlt"); +} + +static uint32_t slen(const char* s) { + uint32_t n = 0; + while (s && s[n]) n++; + return n; +} + +static void wr(int fd, const char* s) { + (void)sys_write(fd, s, slen(s)); +} + +static void cat_fd(int fd) { + char buf[256]; + int r; + while ((r = sys_read(fd, buf, sizeof(buf))) > 0) { + sys_write(1, buf, (uint32_t)r); + } +} + +static void cat_main(uint32_t* sp0) { + uint32_t argc = sp0 ? sp0[0] : 0; + const char* const* argv = (const char* const*)(sp0 + 1); + + if (argc <= 1) { + cat_fd(0); + sys_exit(0); + } + + int rc = 0; + for (uint32_t i = 1; i < argc; i++) { + int fd = sys_open(argv[i], 0); + if (fd < 0) { + wr(2, "cat: "); + wr(2, argv[i]); + wr(2, ": No such file\n"); + rc = 1; + continue; + } + cat_fd(fd); + sys_close(fd); + } + sys_exit(rc); +} + +__attribute__((naked)) void _start(void) { + __asm__ volatile( + "mov %esp, %eax\n" + "push %eax\n" + "call cat_main\n" + "add $4, %esp\n" + "mov $0, %ebx\n" + "mov $2, %eax\n" + "int $0x80\n" + "hlt\n" + ); +} diff --git a/user/ls.c b/user/ls.c new file mode 100644 index 0000000..7daca3b --- /dev/null +++ b/user/ls.c @@ -0,0 +1,124 @@ +/* AdrOS ls utility */ +#include +#include "user_errno.h" + +enum { + SYSCALL_WRITE = 1, + SYSCALL_EXIT = 2, + SYSCALL_OPEN = 4, + SYSCALL_CLOSE = 6, + SYSCALL_GETDENTS = 30, +}; + +static int sys_write(int fd, const void* buf, uint32_t len) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len) : "memory"); + return __syscall_fix(ret); +} + +static int sys_open(const char* path, int flags) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_OPEN), "b"(path), "c"(flags) : "memory"); + return __syscall_fix(ret); +} + +static int sys_close(int fd) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_CLOSE), "b"(fd) : "memory"); + return __syscall_fix(ret); +} + +static int sys_getdents(int fd, void* buf, uint32_t len) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_GETDENTS), "b"(fd), "c"(buf), "d"(len) : "memory"); + return __syscall_fix(ret); +} + +static __attribute__((noreturn)) void sys_exit(int code) { + __asm__ volatile("int $0x80" : : "a"(SYSCALL_EXIT), "b"(code) : "memory"); + for (;;) __asm__ volatile("hlt"); +} + +static uint32_t slen(const char* s) { + uint32_t n = 0; + while (s && s[n]) n++; + return n; +} + +static void wr(int fd, const char* s) { + (void)sys_write(fd, s, slen(s)); +} + +struct dirent { + uint32_t d_ino; + uint16_t d_reclen; + uint8_t d_type; + char d_name[24]; +}; + +static void ls_dir(const char* path) { + int fd = sys_open(path, 0); + if (fd < 0) { + wr(2, "ls: cannot open "); + wr(2, path); + wr(2, "\n"); + return; + } + + char buf[512]; + int rc = sys_getdents(fd, buf, sizeof(buf)); + if (rc > 0) { + uint32_t off = 0; + while (off + sizeof(struct dirent) <= (uint32_t)rc) { + struct dirent* d = (struct dirent*)(buf + off); + if (d->d_reclen == 0) break; + /* skip . and .. */ + if (d->d_name[0] == '.' && + (d->d_name[1] == 0 || + (d->d_name[1] == '.' && d->d_name[2] == 0))) { + off += d->d_reclen; + continue; + } + wr(1, d->d_name); + wr(1, "\n"); + off += d->d_reclen; + } + } + + sys_close(fd); +} + +static void ls_main(uint32_t* sp0) { + uint32_t argc = sp0 ? sp0[0] : 0; + const char* const* argv = (const char* const*)(sp0 + 1); + + if (argc <= 1) { + ls_dir("."); + } else { + for (uint32_t i = 1; i < argc; i++) { + if (argc > 2) { + wr(1, argv[i]); + wr(1, ":\n"); + } + ls_dir(argv[i]); + } + } + sys_exit(0); +} + +__attribute__((naked)) void _start(void) { + __asm__ volatile( + "mov %esp, %eax\n" + "push %eax\n" + "call ls_main\n" + "add $4, %esp\n" + "mov $0, %ebx\n" + "mov $2, %eax\n" + "int $0x80\n" + "hlt\n" + ); +} diff --git a/user/mkdir.c b/user/mkdir.c new file mode 100644 index 0000000..ee171ae --- /dev/null +++ b/user/mkdir.c @@ -0,0 +1,72 @@ +/* AdrOS mkdir utility */ +#include +#include "user_errno.h" + +enum { + SYSCALL_WRITE = 1, + SYSCALL_EXIT = 2, + SYSCALL_MKDIR = 28, +}; + +static int sys_write(int fd, const void* buf, uint32_t len) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len) : "memory"); + return __syscall_fix(ret); +} + +static int sys_mkdir(const char* path) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_MKDIR), "b"(path) : "memory"); + return __syscall_fix(ret); +} + +static __attribute__((noreturn)) void sys_exit(int code) { + __asm__ volatile("int $0x80" : : "a"(SYSCALL_EXIT), "b"(code) : "memory"); + for (;;) __asm__ volatile("hlt"); +} + +static uint32_t slen(const char* s) { + uint32_t n = 0; + while (s && s[n]) n++; + return n; +} + +static void wr(int fd, const char* s) { + (void)sys_write(fd, s, slen(s)); +} + +static void mkdir_main(uint32_t* sp0) { + uint32_t argc = sp0 ? sp0[0] : 0; + const char* const* argv = (const char* const*)(sp0 + 1); + + if (argc <= 1) { + wr(2, "mkdir: missing operand\n"); + sys_exit(1); + } + + int rc = 0; + for (uint32_t i = 1; i < argc; i++) { + if (sys_mkdir(argv[i]) < 0) { + wr(2, "mkdir: cannot create '"); + wr(2, argv[i]); + wr(2, "'\n"); + rc = 1; + } + } + sys_exit(rc); +} + +__attribute__((naked)) void _start(void) { + __asm__ volatile( + "mov %esp, %eax\n" + "push %eax\n" + "call mkdir_main\n" + "add $4, %esp\n" + "mov $0, %ebx\n" + "mov $2, %eax\n" + "int $0x80\n" + "hlt\n" + ); +} diff --git a/user/rm.c b/user/rm.c new file mode 100644 index 0000000..1f17524 --- /dev/null +++ b/user/rm.c @@ -0,0 +1,98 @@ +/* AdrOS rm utility */ +#include +#include "user_errno.h" + +enum { + SYSCALL_WRITE = 1, + SYSCALL_EXIT = 2, + SYSCALL_UNLINK = 29, + SYSCALL_RMDIR = 40, +}; + +static int sys_write(int fd, const void* buf, uint32_t len) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_WRITE), "b"(fd), "c"(buf), "d"(len) : "memory"); + return __syscall_fix(ret); +} + +static int sys_unlink(const char* path) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_UNLINK), "b"(path) : "memory"); + return __syscall_fix(ret); +} + +static int sys_rmdir(const char* path) { + int ret; + __asm__ volatile("int $0x80" : "=a"(ret) + : "a"(SYSCALL_RMDIR), "b"(path) : "memory"); + return __syscall_fix(ret); +} + +static __attribute__((noreturn)) void sys_exit(int code) { + __asm__ volatile("int $0x80" : : "a"(SYSCALL_EXIT), "b"(code) : "memory"); + for (;;) __asm__ volatile("hlt"); +} + +static uint32_t slen(const char* s) { + uint32_t n = 0; + while (s && s[n]) n++; + return n; +} + +static void wr(int fd, const char* s) { + (void)sys_write(fd, s, slen(s)); +} + +static int scmp(const char* a, const char* b) { + while (*a && *b && *a == *b) { a++; b++; } + return (unsigned char)*a - (unsigned char)*b; +} + +static void rm_main(uint32_t* sp0) { + uint32_t argc = sp0 ? sp0[0] : 0; + const char* const* argv = (const char* const*)(sp0 + 1); + + if (argc <= 1) { + wr(2, "rm: missing operand\n"); + sys_exit(1); + } + + int rflag = 0; + int rc = 0; + uint32_t start = 1; + + if (argv[1] && (scmp(argv[1], "-r") == 0 || scmp(argv[1], "-rf") == 0 || + scmp(argv[1], "-d") == 0)) { + rflag = 1; + start = 2; + } + + for (uint32_t i = start; i < argc; i++) { + int r = sys_unlink(argv[i]); + if (r < 0 && rflag) { + r = sys_rmdir(argv[i]); + } + if (r < 0) { + wr(2, "rm: cannot remove '"); + wr(2, argv[i]); + wr(2, "'\n"); + rc = 1; + } + } + sys_exit(rc); +} + +__attribute__((naked)) void _start(void) { + __asm__ volatile( + "mov %esp, %eax\n" + "push %eax\n" + "call rm_main\n" + "add $4, %esp\n" + "mov $0, %ebx\n" + "mov $2, %eax\n" + "int $0x80\n" + "hlt\n" + ); +}