From: Tulio A M Mendes Date: Tue, 10 Feb 2026 01:56:35 +0000 (-0300) Subject: posix: add rename and rmdir syscalls (diskfs) X-Git-Url: https://projects.tadryanom.me/docs/static/gitweb.js?a=commitdiff_plain;h=37f4c0ae9eb760c3cea0349ab30f415a5651febf;p=AdrOS.git posix: add rename and rmdir syscalls (diskfs) --- diff --git a/include/diskfs.h b/include/diskfs.h index b3e6bd8..4caca51 100644 --- a/include/diskfs.h +++ b/include/diskfs.h @@ -13,6 +13,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); +int diskfs_rmdir(const char* rel_path); +int diskfs_rename(const char* old_rel, const char* new_rel); // Writes fixed-size dirent records into out buffer. // Returns number of bytes written or negative errno. diff --git a/include/errno.h b/include/errno.h index 18bd1bf..3e607ce 100644 --- a/include/errno.h +++ b/include/errno.h @@ -25,5 +25,6 @@ #define ERANGE 34 #define ENAMETOOLONG 36 #define ENOSYS 38 +#define ENOTEMPTY 39 #endif diff --git a/include/syscall.h b/include/syscall.h index 28e5e5e..9e3bc7b 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -53,6 +53,9 @@ enum { SYSCALL_OPENAT = 36, SYSCALL_FSTATAT = 37, SYSCALL_UNLINKAT = 38, + + SYSCALL_RENAME = 39, + SYSCALL_RMDIR = 40, }; #endif diff --git a/src/kernel/diskfs.c b/src/kernel/diskfs.c index 94fcf0b..e43a02e 100644 --- a/src/kernel/diskfs.c +++ b/src/kernel/diskfs.c @@ -582,6 +582,72 @@ int diskfs_unlink(const char* rel_path) { return diskfs_super_store(&sb); } +int diskfs_rmdir(const char* rel_path) { + if (!g_ready) return -ENODEV; + if (!rel_path || rel_path[0] == 0) return -EINVAL; + + struct diskfs_super sb; + if (diskfs_super_load(&sb) < 0) return -EIO; + + uint16_t ino = 0; + int rc = diskfs_lookup_path(&sb, rel_path, &ino, 0, 0, 0); + if (rc < 0) return rc; + if (ino == 0) return -EPERM; + if (ino >= DISKFS_MAX_INODES) return -EIO; + if (sb.inodes[ino].type != DISKFS_INODE_DIR) return -ENOTDIR; + + // Check directory is empty (no children). + for (uint16_t i = 0; i < DISKFS_MAX_INODES; i++) { + if (sb.inodes[i].type == DISKFS_INODE_FREE) continue; + if (sb.inodes[i].parent == ino && i != ino) return -ENOTEMPTY; + } + + memset(&sb.inodes[ino], 0, sizeof(sb.inodes[ino])); + return diskfs_super_store(&sb); +} + +int diskfs_rename(const char* old_rel, const char* new_rel) { + if (!g_ready) return -ENODEV; + if (!old_rel || old_rel[0] == 0) return -EINVAL; + if (!new_rel || new_rel[0] == 0) return -EINVAL; + + struct diskfs_super sb; + if (diskfs_super_load(&sb) < 0) return -EIO; + + uint16_t src_ino = 0; + int rc = diskfs_lookup_path(&sb, old_rel, &src_ino, 0, 0, 0); + if (rc < 0) return rc; + if (src_ino == 0) return -EPERM; + if (src_ino >= DISKFS_MAX_INODES) return -EIO; + + // Resolve destination: if it exists, it must be same type or we fail. + uint16_t dst_ino = 0; + uint16_t dst_parent = 0; + char dst_last[DISKFS_NAME_MAX]; + dst_last[0] = 0; + rc = diskfs_lookup_path(&sb, new_rel, &dst_ino, &dst_parent, dst_last, sizeof(dst_last)); + if (rc == 0) { + // Destination exists: if it's a dir and source is file (or vice-versa), error. + if (sb.inodes[dst_ino].type != sb.inodes[src_ino].type) return -EINVAL; + if (dst_ino == src_ino) return 0; // same inode + // Remove destination. + memset(&sb.inodes[dst_ino], 0, sizeof(sb.inodes[dst_ino])); + } else if (rc == -ENOENT) { + // Parent must exist and be a dir. + if (dst_parent >= DISKFS_MAX_INODES) return -EIO; + if (sb.inodes[dst_parent].type != DISKFS_INODE_DIR) return -ENOTDIR; + } else { + return rc; + } + + // Move: update parent and name. + sb.inodes[src_ino].parent = dst_parent; + memset(sb.inodes[src_ino].name, 0, sizeof(sb.inodes[src_ino].name)); + diskfs_strlcpy(sb.inodes[src_ino].name, dst_last, sizeof(sb.inodes[src_ino].name)); + + 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; diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index a04a685..ced6c39 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -1051,6 +1051,44 @@ static int syscall_unlinkat_impl(int dirfd, const char* user_path, uint32_t flag return syscall_unlink_impl(user_path); } +static int syscall_rmdir_impl(const char* user_path) { + if (!user_path) return -EFAULT; + + char path[128]; + int prc = path_resolve_user(user_path, path, sizeof(path)); + if (prc < 0) return prc; + + 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 -EINVAL; + return diskfs_rmdir(rel); + } + + return -ENOSYS; +} + +static int syscall_rename_impl(const char* user_old, const char* user_new) { + if (!user_old || !user_new) return -EFAULT; + + char oldp[128]; + char newp[128]; + int rc = path_resolve_user(user_old, oldp, sizeof(oldp)); + if (rc < 0) return rc; + rc = path_resolve_user(user_new, newp, sizeof(newp)); + if (rc < 0) return rc; + + // Both must be under /disk/ + if (oldp[0] == '/' && oldp[1] == 'd' && oldp[2] == 'i' && oldp[3] == 's' && oldp[4] == 'k' && oldp[5] == '/' && + newp[0] == '/' && newp[1] == 'd' && newp[2] == 'i' && newp[3] == 's' && newp[4] == 'k' && newp[5] == '/') { + const char* old_rel = oldp + 6; + const char* new_rel = newp + 6; + if (old_rel[0] == 0 || new_rel[0] == 0) return -EINVAL; + return diskfs_rename(old_rel, new_rel); + } + + return -ENOSYS; +} + static int syscall_read_impl(int fd, void* user_buf, uint32_t len) { if (len > 1024 * 1024) return -EINVAL; if (user_range_ok(user_buf, (size_t)len) == 0) return -EFAULT; @@ -1581,6 +1619,19 @@ static void syscall_handler(struct registers* regs) { return; } + if (syscall_no == SYSCALL_RENAME) { + const char* oldpath = (const char*)regs->ebx; + const char* newpath = (const char*)regs->ecx; + regs->eax = (uint32_t)syscall_rename_impl(oldpath, newpath); + return; + } + + if (syscall_no == SYSCALL_RMDIR) { + const char* path = (const char*)regs->ebx; + regs->eax = (uint32_t)syscall_rmdir_impl(path); + return; + } + regs->eax = (uint32_t)-ENOSYS; } diff --git a/user/init.c b/user/init.c index 4b1e73d..d16a107 100644 --- a/user/init.c +++ b/user/init.c @@ -78,6 +78,9 @@ enum { SYSCALL_OPENAT = 36, SYSCALL_FSTATAT = 37, SYSCALL_UNLINKAT = 38, + + SYSCALL_RENAME = 39, + SYSCALL_RMDIR = 40, }; enum { @@ -203,6 +206,28 @@ static int sys_unlinkat(int dirfd, const char* path, uint32_t flags) { return __syscall_fix(ret); } +static int sys_rename(const char* oldpath, const char* newpath) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_RENAME), "b"(oldpath), "c"(newpath) + : "memory" + ); + return __syscall_fix(ret); +} + +static int sys_rmdir(const char* path) { + int ret; + __asm__ volatile( + "int $0x80" + : "=a"(ret) + : "a"(SYSCALL_RMDIR), "b"(path) + : "memory" + ); + return __syscall_fix(ret); +} + static int sys_pipe2(int fds[2], uint32_t flags) { int ret; __asm__ volatile( @@ -1995,6 +2020,59 @@ void _start(void) { (uint32_t)(sizeof("[init] *at OK\n") - 1)); } + // B9: rename + rmdir smoke + { + // Create a file, rename it, verify old gone and new exists. + int fd = sys_open("/disk/rnold", O_CREAT | O_TRUNC); + if (fd < 0) { + sys_write(1, "[init] rename: create failed\n", + (uint32_t)(sizeof("[init] rename: create failed\n") - 1)); + sys_exit(1); + } + (void)sys_write(fd, "RN", 2); + (void)sys_close(fd); + + if (sys_rename("/disk/rnold", "/disk/rnnew") < 0) { + sys_write(1, "[init] rename failed\n", + (uint32_t)(sizeof("[init] rename failed\n") - 1)); + sys_exit(1); + } + + struct stat st; + if (sys_stat("/disk/rnold", &st) >= 0) { + sys_write(1, "[init] rename: old still exists\n", + (uint32_t)(sizeof("[init] rename: old still exists\n") - 1)); + sys_exit(1); + } + if (sys_stat("/disk/rnnew", &st) < 0) { + sys_write(1, "[init] rename: new not found\n", + (uint32_t)(sizeof("[init] rename: new not found\n") - 1)); + sys_exit(1); + } + + (void)sys_unlink("/disk/rnnew"); + + // mkdir, then rmdir + if (sys_mkdir("/disk/rmtmp") < 0 && errno != 17) { + sys_write(1, "[init] rmdir: mkdir failed\n", + (uint32_t)(sizeof("[init] rmdir: mkdir failed\n") - 1)); + sys_exit(1); + } + if (sys_rmdir("/disk/rmtmp") < 0) { + sys_write(1, "[init] rmdir failed\n", + (uint32_t)(sizeof("[init] rmdir failed\n") - 1)); + sys_exit(1); + } + if (sys_stat("/disk/rmtmp", &st) >= 0) { + sys_write(1, "[init] rmdir: dir still exists\n", + (uint32_t)(sizeof("[init] rmdir: dir still exists\n") - 1)); + sys_exit(1); + } + + sys_write(1, "[init] rename/rmdir OK\n", + (uint32_t)(sizeof("[init] rename/rmdir OK\n") - 1)); + } + enum { NCHILD = 100 }; int children[NCHILD]; for (int i = 0; i < NCHILD; i++) {