From 35873f4952fd638afda383fa50a4e8971b18e8c6 Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Mon, 9 Feb 2026 23:04:53 -0300 Subject: [PATCH] posix: generic readdir/getdents across all VFS (tmpfs, devfs, overlayfs, diskfs) --- include/fs.h | 8 +++++ src/kernel/devfs.c | 80 ++++++++++++++++++++++++++++++++++++++++++ src/kernel/diskfs.c | 36 +++++++++++++++++++ src/kernel/overlayfs.c | 16 +++++++++ src/kernel/syscall.c | 10 ++---- src/kernel/tmpfs.c | 46 ++++++++++++++++++++++++ user/init.c | 36 +++++++++++++++++++ 7 files changed, 224 insertions(+), 8 deletions(-) diff --git a/include/fs.h b/include/fs.h index 16b9adc..db4f904 100644 --- a/include/fs.h +++ b/include/fs.h @@ -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); diff --git a/src/kernel/devfs.c b/src/kernel/devfs.c index 91d992d..9bb782a 100644 --- a/src/kernel/devfs.c +++ b/src/kernel/devfs.c @@ -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"); diff --git a/src/kernel/diskfs.c b/src/kernel/diskfs.c index e43a02e..8d4b981 100644 --- a/src/kernel/diskfs.c +++ b/src/kernel/diskfs.c @@ -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) { diff --git a/src/kernel/overlayfs.c b/src/kernel/overlayfs.c index a03335e..d441a56 100644 --- a/src/kernel/overlayfs.c +++ b/src/kernel/overlayfs.c @@ -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; diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index ced6c39..865e8bf 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -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; diff --git a/src/kernel/tmpfs.c b/src/kernel/tmpfs.c index db10d33..b3b3fba 100644 --- a/src/kernel/tmpfs.c +++ b/src/kernel/tmpfs.c @@ -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; } diff --git a/user/init.c b/user/init.c index d16a107..dbcfcd1 100644 --- a/user/init.c +++ b/user/init.c @@ -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++) { -- 2.43.0