]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
posix: add rename and rmdir syscalls (diskfs)
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 01:56:35 +0000 (22:56 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 01:56:35 +0000 (22:56 -0300)
include/diskfs.h
include/errno.h
include/syscall.h
src/kernel/diskfs.c
src/kernel/syscall.c
user/init.c

index b3e6bd8027f2aa8b8f9e637acb94bcd5955905d5..4caca51dc1a916419198adca7f89c3a73852f854 100644 (file)
@@ -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.
index 18bd1bf0ab5cec6b6b1a9dc359d4c8c4aa95aa21..3e607cebdd60009902762822b6a4c8dc70fcc8e3 100644 (file)
@@ -25,5 +25,6 @@
 #define ERANGE 34
 #define ENAMETOOLONG 36
 #define ENOSYS 38
+#define ENOTEMPTY 39
 
 #endif
index 28e5e5e386e4cad95d928a54a5f4ff6806566626..9e3bc7b1bf8d15dc96e0d43cf520ee33fc6314a2 100644 (file)
@@ -53,6 +53,9 @@ enum {
     SYSCALL_OPENAT = 36,
     SYSCALL_FSTATAT = 37,
     SYSCALL_UNLINKAT = 38,
+
+    SYSCALL_RENAME = 39,
+    SYSCALL_RMDIR = 40,
 };
 
 #endif
index 94fcf0bc5e9f879df5358c1491c62d49466ad79f..e43a02e9dfe9f46d478cf9130a9c5262c2b576d4 100644 (file)
@@ -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;
index a04a6853cbdb4e91f38fb582ab595010c843a1de..ced6c39cc6b1c4cdbec752551cb4f04373313245 100644 (file)
@@ -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;
 }
 
index 4b1e73db4790fb65f7629ead62bbf9c3d28ace56..d16a10740428303160a0738d2f9c839312102e90 100644 (file)
@@ -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++) {