]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
fs: add lseek/stat/fstat and minimal errno
authorTulio A M Mendes <[email protected]>
Sat, 7 Feb 2026 22:13:42 +0000 (19:13 -0300)
committerTulio A M Mendes <[email protected]>
Sat, 7 Feb 2026 22:13:42 +0000 (19:13 -0300)
include/errno.h [new file with mode: 0644]
include/stat.h [new file with mode: 0644]
include/syscall.h
src/kernel/syscall.c
user/init.c

diff --git a/include/errno.h b/include/errno.h
new file mode 100644 (file)
index 0000000..ead6079
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef ERRNO_H
+#define ERRNO_H
+
+#define EPERM 1
+#define ENOENT 2
+#define EIO 5
+#define EBADF 9
+#define EFAULT 14
+#define EINVAL 22
+#define ESPIPE 29
+
+#endif
diff --git a/include/stat.h b/include/stat.h
new file mode 100644 (file)
index 0000000..710dc91
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef STAT_H
+#define STAT_H
+
+#include <stdint.h>
+
+#define S_IFMT  0170000
+#define S_IFREG 0100000
+#define S_IFDIR 0040000
+#define S_IFCHR 0020000
+
+struct stat {
+    uint32_t st_ino;
+    uint32_t st_mode;
+    uint32_t st_nlink;
+    uint32_t st_size;
+};
+
+#endif
index d05ae3ee8f033c60c0d320d5bf3adc13bcf39c5f..b716ac16f5eb301f6b9ceebb63b4f29ca623d4aa 100644 (file)
@@ -18,6 +18,10 @@ enum {
 
     // Temporary: spawn a kernel-thread child for waitpid testing.
     SYSCALL_SPAWN = 8,
+
+    SYSCALL_LSEEK = 9,
+    SYSCALL_FSTAT = 10,
+    SYSCALL_STAT = 11,
 };
 
 #endif
index 2e34c8be8f092772f3677ea6a78f5daca1859f91..ae219dc9529cb7f314db1309162966f1bf987576 100644 (file)
@@ -7,6 +7,9 @@
 #include "uart_console.h"
 #include "uaccess.h"
 
+#include "errno.h"
+#include "stat.h"
+
 #include "hal/cpu.h"
 
 #include <stddef.h>
@@ -17,6 +20,21 @@ struct file {
     uint32_t flags;
 };
 
+static int stat_from_node(const fs_node_t* node, struct stat* st) {
+    if (!node || !st) return -EFAULT;
+
+    st->st_ino = node->inode;
+    st->st_nlink = 1;
+    st->st_size = node->length;
+
+    uint32_t mode = 0;
+    if (node->flags == FS_DIRECTORY) mode |= S_IFDIR;
+    else if (node->flags == FS_CHARDEVICE) mode |= S_IFCHR;
+    else mode |= S_IFREG;
+    st->st_mode = mode;
+    return 0;
+}
+
 static int fd_alloc(struct file* f) {
     if (!current_process || !f) return -1;
 
@@ -46,6 +64,72 @@ static int fd_close(int fd) {
     return 0;
 }
 
+static int syscall_stat_impl(const char* user_path, struct stat* user_st) {
+    if (!user_path || !user_st) return -EFAULT;
+    if (user_range_ok(user_st, sizeof(*user_st)) == 0) return -EFAULT;
+
+    char path[128];
+    for (size_t i = 0; i < sizeof(path); i++) {
+        if (copy_from_user(&path[i], &user_path[i], 1) < 0) {
+            return -EFAULT;
+        }
+        if (path[i] == 0) break;
+        if (i + 1 == sizeof(path)) {
+            path[sizeof(path) - 1] = 0;
+            break;
+        }
+    }
+
+    fs_node_t* node = vfs_lookup(path);
+    if (!node) return -ENOENT;
+
+    struct stat st;
+    int rc = stat_from_node(node, &st);
+    if (rc < 0) return rc;
+    if (copy_to_user(user_st, &st, sizeof(st)) < 0) return -EFAULT;
+    return 0;
+}
+
+static int syscall_fstat_impl(int fd, struct stat* user_st) {
+    if (!user_st) return -EFAULT;
+    if (user_range_ok(user_st, sizeof(*user_st)) == 0) return -EFAULT;
+
+    struct file* f = fd_get(fd);
+    if (!f || !f->node) return -EBADF;
+
+    struct stat st;
+    int rc = stat_from_node(f->node, &st);
+    if (rc < 0) return rc;
+    if (copy_to_user(user_st, &st, sizeof(st)) < 0) return -EFAULT;
+    return 0;
+}
+
+static int syscall_lseek_impl(int fd, int32_t offset, int whence) {
+    if (fd == 0 || fd == 1 || fd == 2) return -ESPIPE;
+
+    struct file* f = fd_get(fd);
+    if (!f || !f->node) return -EBADF;
+    if (f->node->flags != FS_FILE) return -ESPIPE;
+
+    int64_t base = 0;
+    if (whence == 0) {
+        base = 0;
+    } else if (whence == 1) {
+        base = (int64_t)f->offset;
+    } else if (whence == 2) {
+        base = (int64_t)f->node->length;
+    } else {
+        return -EINVAL;
+    }
+
+    int64_t noff = base + (int64_t)offset;
+    if (noff < 0) return -EINVAL;
+    if (noff > (int64_t)f->node->length) return -EINVAL;
+
+    f->offset = (uint32_t)noff;
+    return (int)f->offset;
+}
+
 static int syscall_open_impl(const char* user_path, uint32_t flags) {
     (void)flags;
     if (!user_path) return -1;
@@ -64,7 +148,7 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
 
     fs_node_t* node = vfs_lookup(path);
     if (!node) return -1;
-    if ((node->flags & FS_FILE) == 0) return -1;
+    if (node->flags != FS_FILE) return -1;
 
     struct file* f = (struct file*)kmalloc(sizeof(*f));
     if (!f) return -1;
@@ -211,6 +295,28 @@ static void syscall_handler(struct registers* regs) {
         return;
     }
 
+    if (syscall_no == SYSCALL_LSEEK) {
+        int fd = (int)regs->ebx;
+        int32_t off = (int32_t)regs->ecx;
+        int whence = (int)regs->edx;
+        regs->eax = (uint32_t)syscall_lseek_impl(fd, off, whence);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_FSTAT) {
+        int fd = (int)regs->ebx;
+        struct stat* st = (struct stat*)regs->ecx;
+        regs->eax = (uint32_t)syscall_fstat_impl(fd, st);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_STAT) {
+        const char* path = (const char*)regs->ebx;
+        struct stat* st = (struct stat*)regs->ecx;
+        regs->eax = (uint32_t)syscall_stat_impl(path, st);
+        return;
+    }
+
     regs->eax = (uint32_t)-1;
 }
 
index 1245dc81d262c7b202c5ec12f5f70ab3f8ed54af..8d1ecd08b49a47952c5da0ab8ba6742029b5893e 100644 (file)
@@ -8,6 +8,25 @@ enum {
     SYSCALL_CLOSE = 6,
     SYSCALL_WAITPID = 7,
     SYSCALL_SPAWN = 8,
+    SYSCALL_LSEEK = 9,
+    SYSCALL_FSTAT = 10,
+    SYSCALL_STAT = 11,
+};
+
+enum {
+    SEEK_SET = 0,
+    SEEK_CUR = 1,
+    SEEK_END = 2,
+};
+
+#define S_IFMT  0170000
+#define S_IFREG 0100000
+
+struct stat {
+    uint32_t st_ino;
+    uint32_t st_mode;
+    uint32_t st_nlink;
+    uint32_t st_size;
 };
 
 static int sys_write(int fd, const void* buf, uint32_t len) {
@@ -76,6 +95,39 @@ static int sys_close(int fd) {
     return ret;
 }
 
+static int sys_lseek(int fd, int32_t offset, int whence) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_LSEEK), "b"(fd), "c"(offset), "d"(whence)
+        : "memory"
+    );
+    return ret;
+}
+
+static int sys_fstat(int fd, struct stat* st) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_FSTAT), "b"(fd), "c"(st)
+        : "memory"
+    );
+    return ret;
+}
+
+static int sys_stat(const char* path, struct stat* st) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_STAT), "b"(path), "c"(st)
+        : "memory"
+    );
+    return ret;
+}
+
 __attribute__((noreturn)) static void sys_exit(int code) {
     __asm__ volatile(
         "int $0x80\n"
@@ -102,23 +154,73 @@ void _start(void) {
     static const char path[] = "/bin/init.elf";
     int fd = sys_open(path, 0);
     if (fd < 0) {
-        static const char emsg[] = "[init] open(/bin/init.elf) failed\n";
-        (void)sys_write(1, emsg, (uint32_t)(sizeof(emsg) - 1));
+        sys_write(1, "[init] open failed\n", 18);
         sys_exit(1);
     }
 
     uint8_t hdr[4];
     int rd = sys_read(fd, hdr, (uint32_t)sizeof(hdr));
-    (void)sys_close(fd);
+    if (sys_close(fd) < 0) {
+        sys_write(1, "[init] close failed\n", 19);
+        sys_exit(1);
+    }
 
     if (rd == 4 && hdr[0] == 0x7F && hdr[1] == 'E' && hdr[2] == 'L' && hdr[3] == 'F') {
-        static const char ok[] = "[init] open/read/close OK (ELF magic)\n";
-        (void)sys_write(1, ok, (uint32_t)(sizeof(ok) - 1));
+        sys_write(1, "[init] open/read/close OK (ELF magic)\n",
+                  (uint32_t)(sizeof("[init] open/read/close OK (ELF magic)\n") - 1));
     } else {
-        static const char bad[] = "[init] read failed or bad header\n";
-        (void)sys_write(1, bad, (uint32_t)(sizeof(bad) - 1));
+        sys_write(1, "[init] read failed or bad header\n", 30);
+        sys_exit(1);
+    }
+
+    fd = sys_open("/bin/init.elf", 0);
+    if (fd < 0) {
+        sys_write(1, "[init] open2 failed\n", 19);
+        sys_exit(1);
     }
 
+    struct stat st;
+    if (sys_fstat(fd, &st) < 0) {
+        sys_write(1, "[init] fstat failed\n", 19);
+        sys_exit(1);
+    }
+
+    if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size == 0) {
+        sys_write(1, "[init] fstat bad\n", 16);
+        sys_exit(1);
+    }
+
+    if (sys_lseek(fd, 0, SEEK_SET) < 0) {
+        sys_write(1, "[init] lseek set failed\n", 24);
+        sys_exit(1);
+    }
+
+    uint8_t m2[4];
+    if (sys_read(fd, m2, 4) != 4) {
+        sys_write(1, "[init] read2 failed\n", 19);
+        sys_exit(1);
+    }
+    if (m2[0] != 0x7F || m2[1] != 'E' || m2[2] != 'L' || m2[3] != 'F') {
+        sys_write(1, "[init] lseek/read mismatch\n", 27);
+        sys_exit(1);
+    }
+
+    if (sys_close(fd) < 0) {
+        sys_write(1, "[init] close2 failed\n", 20);
+        sys_exit(1);
+    }
+
+    if (sys_stat("/bin/init.elf", &st) < 0) {
+        sys_write(1, "[init] stat failed\n", 18);
+        sys_exit(1);
+    }
+    if ((st.st_mode & S_IFMT) != S_IFREG || st.st_size == 0) {
+        sys_write(1, "[init] stat bad\n", 15);
+        sys_exit(1);
+    }
+
+    sys_write(1, "[init] lseek/stat/fstat OK\n", 27);
+
     enum { NCHILD = 100 };
     int children[NCHILD];
     for (int i = 0; i < NCHILD; i++) {