From: Tulio A M Mendes Date: Tue, 10 Feb 2026 00:54:17 +0000 (-0300) Subject: vfs: add getdents syscall for diskfs directories X-Git-Url: https://projects.tadryanom.me/docs/POSIX_ROADMAP.md?a=commitdiff_plain;h=19170b73aa8bc4584ded48b6bef18ba1c20ff50a;p=AdrOS.git vfs: add getdents syscall for diskfs directories --- diff --git a/include/diskfs.h b/include/diskfs.h index 63d6000..b3e6bd8 100644 --- a/include/diskfs.h +++ b/include/diskfs.h @@ -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 diff --git a/include/syscall.h b/include/syscall.h index 5dbddcf..a31dd0b 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -40,6 +40,8 @@ enum { SYSCALL_MKDIR = 28, SYSCALL_UNLINK = 29, + + SYSCALL_GETDENTS = 30, }; #endif diff --git a/src/kernel/diskfs.c b/src/kernel/diskfs.c index 72faa14..94fcf0b 100644 --- a/src/kernel/diskfs.c +++ b/src/kernel/diskfs.c @@ -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; } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index f255edd..61b14c7 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -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; } diff --git a/user/init.c b/user/init.c index bce209b..1288879 100644 --- a/user/init.c +++ b/user/init.c @@ -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++) {