]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
posix: generic readdir/getdents across all VFS (tmpfs, devfs, overlayfs, diskfs)
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 02:04:53 +0000 (23:04 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 02:04:53 +0000 (23:04 -0300)
include/fs.h
src/kernel/devfs.c
src/kernel/diskfs.c
src/kernel/overlayfs.c
src/kernel/syscall.c
src/kernel/tmpfs.c
user/init.c

index 16b9adc2d771c72d7e693217c43cd14f16a14078..db4f9044918634923b972df44e2d533808462f08 100644 (file)
@@ -21,8 +21,16 @@ typedef struct fs_node {
     void (*open)(struct fs_node* node);
     void (*close)(struct fs_node* node);
     struct fs_node* (*finddir)(struct fs_node* node, const char* name);
+    int (*readdir)(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len);
 } fs_node_t;
 
+struct vfs_dirent {
+    uint32_t d_ino;
+    uint16_t d_reclen;
+    uint8_t  d_type;
+    char     d_name[24];
+};
+
 // Standard VFS functions
 uint32_t vfs_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer);
 uint32_t vfs_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer);
index 91d992d70fded887c9463f89903bd6903c9e4d22..9bb782ada56cecc218654300219dd2836523344a 100644 (file)
@@ -98,6 +98,84 @@ static struct fs_node* devfs_pts_finddir_impl(struct fs_node* node, const char*
     return 0;
 }
 
+static int devfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len) {
+    (void)node;
+    if (!inout_index || !buf) return -1;
+    if (buf_len < sizeof(struct vfs_dirent)) return -1;
+
+    static const struct { const char* name; uint32_t ino; uint8_t type; } devs[] = {
+        { "null", 2, FS_CHARDEVICE },
+        { "tty",  3, FS_CHARDEVICE },
+        { "ptmx", 4, FS_CHARDEVICE },
+        { "pts",  5, FS_DIRECTORY },
+    };
+    enum { NDEVS = 4 };
+
+    uint32_t idx = *inout_index;
+    uint32_t cap = buf_len / (uint32_t)sizeof(struct vfs_dirent);
+    struct vfs_dirent* ents = (struct vfs_dirent*)buf;
+    uint32_t written = 0;
+
+    while (written < cap) {
+        struct vfs_dirent e;
+        memset(&e, 0, sizeof(e));
+
+        if (idx == 0) {
+            e.d_ino = 1; e.d_type = FS_DIRECTORY; strcpy(e.d_name, ".");
+        } else if (idx == 1) {
+            e.d_ino = 1; e.d_type = FS_DIRECTORY; strcpy(e.d_name, "..");
+        } else {
+            uint32_t di = idx - 2;
+            if (di >= NDEVS) break;
+            e.d_ino = devs[di].ino;
+            e.d_type = devs[di].type;
+            strcpy(e.d_name, devs[di].name);
+        }
+
+        e.d_reclen = (uint16_t)sizeof(e);
+        ents[written] = e;
+        written++;
+        idx++;
+    }
+
+    *inout_index = idx;
+    return (int)(written * (uint32_t)sizeof(struct vfs_dirent));
+}
+
+static int devfs_pts_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len) {
+    (void)node;
+    if (!inout_index || !buf) return -1;
+    if (buf_len < sizeof(struct vfs_dirent)) return -1;
+
+    uint32_t idx = *inout_index;
+    uint32_t cap = buf_len / (uint32_t)sizeof(struct vfs_dirent);
+    struct vfs_dirent* ents = (struct vfs_dirent*)buf;
+    uint32_t written = 0;
+
+    while (written < cap) {
+        struct vfs_dirent e;
+        memset(&e, 0, sizeof(e));
+
+        if (idx == 0) {
+            e.d_ino = 5; e.d_type = FS_DIRECTORY; strcpy(e.d_name, ".");
+        } else if (idx == 1) {
+            e.d_ino = 1; e.d_type = FS_DIRECTORY; strcpy(e.d_name, "..");
+        } else if (idx == 2) {
+            e.d_ino = 6; e.d_type = FS_CHARDEVICE; strcpy(e.d_name, "0");
+        } else {
+            break;
+        }
+
+        e.d_reclen = (uint16_t)sizeof(e);
+        ents[written] = e;
+        written++;
+        idx++;
+    }
+
+    *inout_index = idx;
+    return (int)(written * (uint32_t)sizeof(struct vfs_dirent));
+}
+
 static void devfs_init_once(void) {
     if (g_devfs_inited) return;
     g_devfs_inited = 1;
@@ -112,6 +190,7 @@ static void devfs_init_once(void) {
     g_dev_root.vfs.open = 0;
     g_dev_root.vfs.close = 0;
     g_dev_root.vfs.finddir = &devfs_finddir_impl;
+    g_dev_root.vfs.readdir = &devfs_readdir_impl;
 
     memset(&g_dev_null, 0, sizeof(g_dev_null));
     strcpy(g_dev_null.name, "null");
@@ -156,6 +235,7 @@ static void devfs_init_once(void) {
     g_dev_pts_dir.open = 0;
     g_dev_pts_dir.close = 0;
     g_dev_pts_dir.finddir = &devfs_pts_finddir_impl;
+    g_dev_pts_dir.readdir = &devfs_pts_readdir_impl;
 
     memset(&g_dev_pts0, 0, sizeof(g_dev_pts0));
     strcpy(g_dev_pts0.name, "0");
index e43a02e9dfe9f46d478cf9130a9c5262c2b576d4..8d4b981b83081269b10a2ebd02aacc8cfbbfd47d 100644 (file)
@@ -423,6 +423,40 @@ static uint32_t diskfs_write_impl(fs_node_t* node, uint32_t offset, uint32_t siz
     return total;
 }
 
+static int diskfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len) {
+    if (!node || !inout_index || !buf) return -1;
+    if (node->flags != FS_DIRECTORY) return -1;
+    if (buf_len < sizeof(struct vfs_dirent)) return -1;
+
+    struct diskfs_node* dn = (struct diskfs_node*)node;
+    uint16_t dir_ino = dn->ino;
+
+    // Use diskfs_getdents which fills diskfs_kdirent; convert to vfs_dirent.
+    struct diskfs_kdirent kbuf[8];
+    uint32_t klen = sizeof(kbuf);
+    if (klen > buf_len) klen = buf_len;
+
+    uint32_t idx = *inout_index;
+    int rc = diskfs_getdents(dir_ino, &idx, kbuf, klen);
+    if (rc <= 0) return rc;
+
+    uint32_t nents = (uint32_t)rc / (uint32_t)sizeof(struct diskfs_kdirent);
+    uint32_t cap = buf_len / (uint32_t)sizeof(struct vfs_dirent);
+    if (nents > cap) nents = cap;
+
+    struct vfs_dirent* out = (struct vfs_dirent*)buf;
+    for (uint32_t i = 0; i < nents; i++) {
+        memset(&out[i], 0, sizeof(out[i]));
+        out[i].d_ino = kbuf[i].d_ino;
+        out[i].d_reclen = (uint16_t)sizeof(struct vfs_dirent);
+        out[i].d_type = kbuf[i].d_type;
+        diskfs_strlcpy(out[i].d_name, kbuf[i].d_name, sizeof(out[i].d_name));
+    }
+
+    *inout_index = idx;
+    return (int)(nents * (uint32_t)sizeof(struct vfs_dirent));
+}
+
 static struct fs_node* diskfs_root_finddir(struct fs_node* node, const char* name) {
     struct diskfs_node* parent = (struct diskfs_node*)node;
     if (!g_ready) return 0;
@@ -456,6 +490,7 @@ static struct fs_node* diskfs_root_finddir(struct fs_node* node, const char* nam
         dn->vfs.read = 0;
         dn->vfs.write = 0;
         dn->vfs.finddir = &diskfs_root_finddir;
+        dn->vfs.readdir = &diskfs_readdir_impl;
     } else {
         dn->vfs.flags = FS_FILE;
         dn->vfs.length = sb.inodes[cino].size_bytes;
@@ -725,6 +760,7 @@ fs_node_t* diskfs_create_root(void) {
         g_root.vfs.open = 0;
         g_root.vfs.close = 0;
         g_root.vfs.finddir = &diskfs_root_finddir;
+        g_root.vfs.readdir = &diskfs_readdir_impl;
         g_root.ino = 0;
 
         if (g_ready) {
index a03335eb45ebb1247b0ecf6d240ac3c07b27e98d..d441a56a78e4184e4aec3e40d734c62d028fd7c6 100644 (file)
@@ -22,6 +22,7 @@ struct overlay_node {
 };
 
 static struct fs_node* overlay_finddir_impl(struct fs_node* node, const char* name);
+static int overlay_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len);
 
 static void overlay_str_copy_n(char* dst, size_t dst_sz, const char* src, size_t src_n) {
     if (!dst || dst_sz == 0) return;
@@ -114,6 +115,7 @@ static fs_node_t* overlay_wrap_child(struct overlay_node* parent, const char* na
     c->vfs.open = 0;
     c->vfs.close = 0;
     c->vfs.finddir = (c->vfs.flags == FS_DIRECTORY) ? &overlay_finddir_impl : 0;
+    c->vfs.readdir = (c->vfs.flags == FS_DIRECTORY) ? &overlay_readdir_impl : 0;
 
     if (parent->path[0] == 0) {
         c->path[0] = '/';
@@ -142,6 +144,19 @@ static fs_node_t* overlay_wrap_child(struct overlay_node* parent, const char* na
     return &c->vfs;
 }
 
+static int overlay_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len) {
+    if (!node || !inout_index || !buf) return -1;
+    if (node->flags != FS_DIRECTORY) return -1;
+    if (buf_len < sizeof(struct vfs_dirent)) return -1;
+
+    struct overlay_node* dir = (struct overlay_node*)node;
+
+    // Prefer upper layer readdir; fall back to lower.
+    fs_node_t* src = dir->upper ? dir->upper : dir->lower;
+    if (!src || !src->readdir) return 0;
+    return src->readdir(src, inout_index, buf, buf_len);
+}
+
 static struct fs_node* overlay_finddir_impl(struct fs_node* node, const char* name) {
     if (!node || !name) return 0;
     if (node->flags != FS_DIRECTORY) return 0;
@@ -190,6 +205,7 @@ fs_node_t* overlayfs_create_root(fs_node_t* lower_root, fs_node_t* upper_root) {
     root->vfs.open = 0;
     root->vfs.close = 0;
     root->vfs.finddir = &overlay_finddir_impl;
+    root->vfs.readdir = &overlay_readdir_impl;
 
     root->path[0] = 0;
 
index ced6c39cc6b1c4cdbec752551cb4f04373313245..865e8bfb5e2d647b8b84a4cc069d9e0cddf03a85 100644 (file)
@@ -1007,20 +1007,14 @@ static int syscall_getdents_impl(int fd, void* user_buf, uint32_t len) {
     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);
+    if (!f->node->readdir) return -ENOSYS;
 
     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);
+    int rc = f->node->readdir(f->node, &idx, kbuf, klen);
     if (rc < 0) return rc;
     if (rc == 0) return 0;
 
index db10d331cbbcb94d3f7567e2f96bbcd209e6327f..b3b3fbac2fd58be9687a2755796a32a5e1eff11b 100644 (file)
@@ -17,6 +17,7 @@ struct tmpfs_node {
 static uint32_t g_tmpfs_next_inode = 1;
 
 static struct fs_node* tmpfs_finddir_impl(struct fs_node* node, const char* name);
+static int tmpfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len);
 static uint32_t tmpfs_write_impl(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer);
 
 static struct tmpfs_node* tmpfs_node_alloc(const char* name, uint32_t flags) {
@@ -68,6 +69,7 @@ static struct tmpfs_node* tmpfs_child_ensure_dir(struct tmpfs_node* dir, const c
     nd->vfs.open = 0;
     nd->vfs.close = 0;
     nd->vfs.finddir = &tmpfs_finddir_impl;
+    nd->vfs.readdir = &tmpfs_readdir_impl;
     tmpfs_child_add(dir, nd);
     return nd;
 }
@@ -148,6 +150,49 @@ static struct fs_node* tmpfs_finddir_impl(struct fs_node* node, const char* name
     return &child->vfs;
 }
 
+static int tmpfs_readdir_impl(struct fs_node* node, uint32_t* inout_index, void* buf, uint32_t buf_len) {
+    if (!node || !inout_index || !buf) return -1;
+    if (node->flags != FS_DIRECTORY) return -1;
+    if (buf_len < sizeof(struct vfs_dirent)) return -1;
+
+    struct tmpfs_node* dir = (struct tmpfs_node*)node;
+    uint32_t idx = *inout_index;
+    uint32_t cap = buf_len / (uint32_t)sizeof(struct vfs_dirent);
+    struct vfs_dirent* ents = (struct vfs_dirent*)buf;
+    uint32_t written = 0;
+
+    while (written < cap) {
+        struct vfs_dirent e;
+        memset(&e, 0, sizeof(e));
+
+        if (idx == 0) {
+            e.d_ino = node->inode;
+            e.d_type = FS_DIRECTORY;
+            strcpy(e.d_name, ".");
+        } else if (idx == 1) {
+            e.d_ino = dir->parent ? dir->parent->vfs.inode : node->inode;
+            e.d_type = FS_DIRECTORY;
+            strcpy(e.d_name, "..");
+        } else {
+            uint32_t skip = idx - 2;
+            struct tmpfs_node* c = dir->first_child;
+            while (c && skip > 0) { c = c->next_sibling; skip--; }
+            if (!c) break;
+            e.d_ino = c->vfs.inode;
+            e.d_type = (uint8_t)c->vfs.flags;
+            strcpy(e.d_name, c->vfs.name);
+        }
+
+        e.d_reclen = (uint16_t)sizeof(e);
+        ents[written] = e;
+        written++;
+        idx++;
+    }
+
+    *inout_index = idx;
+    return (int)(written * (uint32_t)sizeof(struct vfs_dirent));
+}
+
 fs_node_t* tmpfs_create_root(void) {
     struct tmpfs_node* root = tmpfs_node_alloc("", FS_DIRECTORY);
     if (!root) return NULL;
@@ -157,6 +202,7 @@ fs_node_t* tmpfs_create_root(void) {
     root->vfs.open = 0;
     root->vfs.close = 0;
     root->vfs.finddir = &tmpfs_finddir_impl;
+    root->vfs.readdir = &tmpfs_readdir_impl;
 
     return &root->vfs;
 }
index d16a10740428303160a0738d2f9c839312102e90..dbcfcd171d6f58a3cf01005457f9ed42aafc5db5 100644 (file)
@@ -2073,6 +2073,42 @@ void _start(void) {
                   (uint32_t)(sizeof("[init] rename/rmdir OK\n") - 1));
     }
 
+    // B10: getdents on /dev (devfs) and /tmp (tmpfs)
+    {
+        int devfd = sys_open("/dev", 0);
+        if (devfd < 0) {
+            sys_write(1, "[init] open /dev failed\n",
+                      (uint32_t)(sizeof("[init] open /dev failed\n") - 1));
+            sys_exit(1);
+        }
+        char dbuf[256];
+        int dr = sys_getdents(devfd, dbuf, (uint32_t)sizeof(dbuf));
+        (void)sys_close(devfd);
+        if (dr <= 0) {
+            sys_write(1, "[init] getdents /dev failed\n",
+                      (uint32_t)(sizeof("[init] getdents /dev failed\n") - 1));
+            sys_exit(1);
+        }
+
+        int tmpfd = sys_open("/tmp", 0);
+        if (tmpfd < 0) {
+            sys_write(1, "[init] open /tmp failed\n",
+                      (uint32_t)(sizeof("[init] open /tmp failed\n") - 1));
+            sys_exit(1);
+        }
+        char tbuf[256];
+        int tr = sys_getdents(tmpfd, tbuf, (uint32_t)sizeof(tbuf));
+        (void)sys_close(tmpfd);
+        if (tr <= 0) {
+            sys_write(1, "[init] getdents /tmp failed\n",
+                      (uint32_t)(sizeof("[init] getdents /tmp failed\n") - 1));
+            sys_exit(1);
+        }
+
+        sys_write(1, "[init] getdents multi-fs OK\n",
+                  (uint32_t)(sizeof("[init] getdents multi-fs OK\n") - 1));
+    }
+
     enum { NCHILD = 100 };
     int children[NCHILD];
     for (int i = 0; i < NCHILD; i++) {