]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
vfs: add getdents syscall for diskfs directories
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 00:54:17 +0000 (21:54 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 00:54:17 +0000 (21:54 -0300)
include/diskfs.h
include/syscall.h
src/kernel/diskfs.c
src/kernel/syscall.c
user/init.c

index 63d60009a5ef93783942bfd111755b60c0ec360c..b3e6bd8027f2aa8b8f9e637acb94bcd5955905d5 100644 (file)
@@ -14,4 +14,8 @@ int diskfs_open_file(const char* rel_path, uint32_t flags, fs_node_t** out_node)
 int diskfs_mkdir(const char* rel_path);
 int diskfs_unlink(const char* rel_path);
 
+// Writes fixed-size dirent records into out buffer.
+// Returns number of bytes written or negative errno.
+int diskfs_getdents(uint16_t dir_ino, uint32_t* inout_index, void* out, uint32_t out_len);
+
 #endif
index 5dbddcf4b9f7de2544a4b16e6096630b9c4c101c..a31dd0bd358e2b9a9794cf81917a2a36cab7b58d 100644 (file)
@@ -40,6 +40,8 @@ enum {
 
     SYSCALL_MKDIR = 28,
     SYSCALL_UNLINK = 29,
+
+    SYSCALL_GETDENTS = 30,
 };
 
 #endif
index 72faa146b5322e76d0ea048e00794932ce635c81..94fcf0bc5e9f879df5358c1491c62d49466ad79f 100644 (file)
@@ -72,7 +72,7 @@ struct diskfs_node {
     uint16_t ino;
 };
 
-static fs_node_t g_root;
+static struct diskfs_node g_root;
 static uint32_t g_ready = 0;
 
 static int diskfs_super_store(const struct diskfs_super* sb);
@@ -306,6 +306,13 @@ static int diskfs_lookup_path(struct diskfs_super* sb, const char* path, uint16_
     return 0;
 }
 
+struct diskfs_kdirent {
+    uint32_t d_ino;
+    uint16_t d_reclen;
+    uint8_t d_type;
+    char d_name[DISKFS_NAME_MAX];
+};
+
 static uint32_t diskfs_read_impl(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
     if (!node || !buffer) return 0;
     if (node->flags != FS_FILE) return 0;
@@ -422,10 +429,7 @@ static struct fs_node* diskfs_root_finddir(struct fs_node* node, const char* nam
     if (!name || name[0] == 0) return 0;
     if (!diskfs_segment_valid(name)) return 0;
 
-    uint16_t parent_ino = 0;
-    if (parent) {
-        parent_ino = parent->ino;
-    }
+    uint16_t parent_ino = parent ? parent->ino : 0;
 
     struct diskfs_super sb;
     if (diskfs_super_load(&sb) < 0) return 0;
@@ -578,6 +582,65 @@ int diskfs_unlink(const char* rel_path) {
     return diskfs_super_store(&sb);
 }
 
+int diskfs_getdents(uint16_t dir_ino, uint32_t* inout_index, void* out, uint32_t out_len) {
+    if (!inout_index || !out) return -EINVAL;
+    if (!g_ready) return -ENODEV;
+    if (out_len < sizeof(struct diskfs_kdirent)) return -EINVAL;
+
+    struct diskfs_super sb;
+    if (diskfs_super_load(&sb) < 0) return -EIO;
+
+    if (dir_ino >= DISKFS_MAX_INODES) return -ENOENT;
+    if (sb.inodes[dir_ino].type != DISKFS_INODE_DIR) return -ENOTDIR;
+
+    uint32_t idx = *inout_index;
+    uint32_t written = 0;
+    struct diskfs_kdirent* ents = (struct diskfs_kdirent*)out;
+    uint32_t cap = out_len / (uint32_t)sizeof(struct diskfs_kdirent);
+
+    // index 0 => '.' ; index 1 => '..' ; index >=2 => scan inodes
+    while (written < cap) {
+        struct diskfs_kdirent e;
+        memset(&e, 0, sizeof(e));
+
+        if (idx == 0) {
+            e.d_ino = (uint32_t)dir_ino;
+            e.d_type = (uint8_t)DISKFS_INODE_DIR;
+            diskfs_strlcpy(e.d_name, ".", sizeof(e.d_name));
+        } else if (idx == 1) {
+            e.d_ino = (uint32_t)sb.inodes[dir_ino].parent;
+            e.d_type = (uint8_t)DISKFS_INODE_DIR;
+            diskfs_strlcpy(e.d_name, "..", sizeof(e.d_name));
+        } else {
+            uint16_t scan = (uint16_t)(idx - 2);
+            int found = 0;
+            for (; scan < DISKFS_MAX_INODES; scan++) {
+                if (sb.inodes[scan].type == DISKFS_INODE_FREE) continue;
+                if (sb.inodes[scan].parent != dir_ino) continue;
+                if (sb.inodes[scan].name[0] == 0) continue;
+                e.d_ino = (uint32_t)scan;
+                e.d_type = sb.inodes[scan].type;
+                diskfs_strlcpy(e.d_name, sb.inodes[scan].name, sizeof(e.d_name));
+                found = 1;
+                scan++;
+                idx = (uint32_t)scan + 2U;
+                break;
+            }
+            if (!found) break;
+        }
+
+        e.d_reclen = (uint16_t)sizeof(e);
+        ents[written] = e;
+        written++;
+
+        if (idx == 0) idx = 1;
+        else if (idx == 1) idx = 2;
+    }
+
+    *inout_index = idx;
+    return (int)(written * (uint32_t)sizeof(struct diskfs_kdirent));
+}
+
 fs_node_t* diskfs_create_root(void) {
     if (!g_ready) {
         if (ata_pio_init_primary_master() == 0) {
@@ -587,15 +650,16 @@ fs_node_t* diskfs_create_root(void) {
         }
 
         memset(&g_root, 0, sizeof(g_root));
-        strcpy(g_root.name, "disk");
-        g_root.flags = FS_DIRECTORY;
-        g_root.inode = 1;
-        g_root.length = 0;
-        g_root.read = 0;
-        g_root.write = 0;
-        g_root.open = 0;
-        g_root.close = 0;
-        g_root.finddir = &diskfs_root_finddir;
+        strcpy(g_root.vfs.name, "disk");
+        g_root.vfs.flags = FS_DIRECTORY;
+        g_root.vfs.inode = 100;
+        g_root.vfs.length = 0;
+        g_root.vfs.read = 0;
+        g_root.vfs.write = 0;
+        g_root.vfs.open = 0;
+        g_root.vfs.close = 0;
+        g_root.vfs.finddir = &diskfs_root_finddir;
+        g_root.ino = 0;
 
         if (g_ready) {
             struct diskfs_super sb;
@@ -603,5 +667,5 @@ fs_node_t* diskfs_create_root(void) {
         }
     }
 
-    return g_ready ? &g_root : 0;
+    return g_ready ? &g_root.vfs : 0;
 }
index f255edd05a8b0ec76c2eb613424fabd44b1c39f6..61b14c7a76d644841f7686a5c9b0f286abf981da 100644 (file)
@@ -764,10 +764,17 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
 
     fs_node_t* node = NULL;
     if (path[0] == '/' && path[1] == 'd' && path[2] == 'i' && path[3] == 's' && path[4] == 'k' && path[5] == '/') {
-        const char* rel = path + 6;
-        if (rel[0] == 0) return -ENOENT;
-        int rc = diskfs_open_file(rel, flags, &node);
-        if (rc < 0) return rc;
+        // With hierarchical diskfs, /disk may contain directories.
+        // Use diskfs_open_file only when creation/truncation is requested.
+        if ((flags & 0x40U) != 0U || (flags & 0x200U) != 0U) {
+            const char* rel = path + 6;
+            if (rel[0] == 0) return -ENOENT;
+            int rc = diskfs_open_file(rel, flags, &node);
+            if (rc < 0) return rc;
+        } else {
+            node = vfs_lookup(path);
+            if (!node) return -ENOENT;
+        }
     } else {
         node = vfs_lookup(path);
         if (!node) return -ENOENT;
@@ -812,6 +819,36 @@ static int syscall_mkdir_impl(const char* user_path) {
     return -ENOSYS;
 }
 
+static int syscall_getdents_impl(int fd, void* user_buf, uint32_t len) {
+    if (len == 0) return 0;
+    if (!user_buf) return -EFAULT;
+    if (user_range_ok(user_buf, (size_t)len) == 0) return -EFAULT;
+
+    struct file* f = fd_get(fd);
+    if (!f || !f->node) return -EBADF;
+    if (f->node->flags != FS_DIRECTORY) return -ENOTDIR;
+
+    // For now only support diskfs dirs (mounted at /disk). We encode diskfs inode as 100+ino.
+    if (f->node->inode < 100U) return -ENOSYS;
+    uint16_t dir_ino = (uint16_t)(f->node->inode - 100U);
+
+    uint8_t kbuf[256];
+    if (len < (uint32_t)sizeof(kbuf)) {
+        // keep behavior simple: require small buffers too
+    }
+    uint32_t klen = len;
+    if (klen > (uint32_t)sizeof(kbuf)) klen = (uint32_t)sizeof(kbuf);
+
+    uint32_t idx = f->offset;
+    int rc = diskfs_getdents(dir_ino, &idx, kbuf, klen);
+    if (rc < 0) return rc;
+    if (rc == 0) return 0;
+
+    if (copy_to_user(user_buf, kbuf, (uint32_t)rc) < 0) return -EFAULT;
+    f->offset = idx;
+    return rc;
+}
+
 static int syscall_unlink_impl(const char* user_path) {
     if (!user_path) return -EFAULT;
 
@@ -1257,6 +1294,14 @@ static void syscall_handler(struct registers* regs) {
         return;
     }
 
+    if (syscall_no == SYSCALL_GETDENTS) {
+        int fd = (int)regs->ebx;
+        void* buf = (void*)regs->ecx;
+        uint32_t len = (uint32_t)regs->edx;
+        regs->eax = (uint32_t)syscall_getdents_impl(fd, buf, len);
+        return;
+    }
+
     regs->eax = (uint32_t)-ENOSYS;
 }
 
index bce209b948fe6d8b3c9985a1367dfad3d70d3077..1288879b25c7335085f78053d38cb25c9d74dccb 100644 (file)
@@ -65,6 +65,8 @@ enum {
 
     SYSCALL_MKDIR = 28,
     SYSCALL_UNLINK = 29,
+
+    SYSCALL_GETDENTS = 30,
 };
 
 enum {
@@ -138,6 +140,17 @@ static int sys_write(int fd, const void* buf, uint32_t len) {
     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 void write_int_dec(int v) {
     char buf[16];
     int i = 0;
@@ -184,14 +197,24 @@ static void write_hex32(uint32_t v) {
 }
 
 static int memeq(const void* a, const void* b, uint32_t n) {
-    const uint8_t* pa = (const uint8_t*)a;
-    const uint8_t* pb = (const uint8_t*)b;
+    const uint8_t* x = (const uint8_t*)a;
+    const uint8_t* y = (const uint8_t*)b;
     for (uint32_t i = 0; i < n; i++) {
-        if (pa[i] != pb[i]) return 0;
+        if (x[i] != y[i]) return 0;
     }
     return 1;
 }
 
+static int streq(const char* a, const char* b) {
+    if (!a || !b) return 0;
+    uint32_t i = 0;
+    while (a[i] != 0 && b[i] != 0) {
+        if (a[i] != b[i]) return 0;
+        i++;
+    }
+    return a[i] == b[i];
+}
+
 static int sys_sigaction2(int sig, const struct sigaction* act, struct sigaction* oldact) {
     int ret;
     __asm__ volatile(
@@ -1534,9 +1557,10 @@ void _start(void) {
     // B3: diskfs mkdir/unlink smoke
     {
         int r = sys_mkdir("/disk/dir");
-        if (r < 0 && r != -17) {
-            sys_write(1, "[init] mkdir /disk/dir failed\n",
-                      (uint32_t)(sizeof("[init] mkdir /disk/dir failed\n") - 1));
+        if (r < 0 && errno != 17) {
+            sys_write(1, "[init] mkdir /disk/dir failed errno=", (uint32_t)(sizeof("[init] mkdir /disk/dir failed errno=") - 1));
+            write_int_dec(errno);
+            sys_write(1, "\n", 1);
             sys_exit(1);
         }
 
@@ -1572,6 +1596,73 @@ void _start(void) {
                   (uint32_t)(sizeof("[init] diskfs mkdir/unlink OK\n") - 1));
     }
 
+    // B4: diskfs getdents smoke
+    {
+        int r = sys_mkdir("/disk/ls");
+        if (r < 0 && errno != 17) {
+            sys_write(1, "[init] mkdir /disk/ls failed errno=", (uint32_t)(sizeof("[init] mkdir /disk/ls failed errno=") - 1));
+            write_int_dec(errno);
+            sys_write(1, "\n", 1);
+            sys_exit(1);
+        }
+
+        int fd = sys_open("/disk/ls/file1", O_CREAT | O_TRUNC);
+        if (fd < 0) {
+            sys_write(1, "[init] create /disk/ls/file1 failed\n",
+                      (uint32_t)(sizeof("[init] create /disk/ls/file1 failed\n") - 1));
+            sys_exit(1);
+        }
+        (void)sys_close(fd);
+
+        fd = sys_open("/disk/ls/file2", O_CREAT | O_TRUNC);
+        if (fd < 0) {
+            sys_write(1, "[init] create /disk/ls/file2 failed\n",
+                      (uint32_t)(sizeof("[init] create /disk/ls/file2 failed\n") - 1));
+            sys_exit(1);
+        }
+        (void)sys_close(fd);
+
+        int dfd = sys_open("/disk/ls", 0);
+        if (dfd < 0) {
+            sys_write(1, "[init] open dir /disk/ls failed\n",
+                      (uint32_t)(sizeof("[init] open dir /disk/ls failed\n") - 1));
+            sys_exit(1);
+        }
+
+        struct {
+            uint32_t d_ino;
+            uint16_t d_reclen;
+            uint8_t d_type;
+            char d_name[24];
+        } ents[8];
+
+        int n = sys_getdents(dfd, ents, (uint32_t)sizeof(ents));
+        (void)sys_close(dfd);
+        if (n <= 0) {
+            sys_write(1, "[init] getdents failed\n",
+                      (uint32_t)(sizeof("[init] getdents failed\n") - 1));
+            sys_exit(1);
+        }
+
+        int saw_dot = 0, saw_dotdot = 0, saw_f1 = 0, saw_f2 = 0;
+        int cnt = n / (int)sizeof(ents[0]);
+        for (int i = 0; i < cnt; i++) {
+            if (streq(ents[i].d_name, ".")) saw_dot = 1;
+            else if (streq(ents[i].d_name, "..")) saw_dotdot = 1;
+            else if (streq(ents[i].d_name, "file1")) saw_f1 = 1;
+            else if (streq(ents[i].d_name, "file2")) saw_f2 = 1;
+        }
+
+        if (!saw_dot || !saw_dotdot || !saw_f1 || !saw_f2) {
+            sys_write(1, "[init] getdents verify failed\n",
+                      (uint32_t)(sizeof("[init] getdents verify failed\n") - 1));
+            sys_exit(1);
+        }
+
+        sys_write(1, "[init] diskfs getdents OK\n",
+                  (uint32_t)(sizeof("[init] diskfs getdents OK\n") - 1));
+    }
+
     enum { NCHILD = 100 };
     int children[NCHILD];
     for (int i = 0; i < NCHILD; i++) {