]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
posix: add chdir/getcwd and relative path resolution
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 01:21:12 +0000 (22:21 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 01:21:12 +0000 (22:21 -0300)
include/errno.h
include/process.h
include/syscall.h
src/kernel/scheduler.c
src/kernel/syscall.c
user/init.c

index 603b20dabcbcb49916bac1c1b2b65fc47f0d78f0..18bd1bf0ab5cec6b6b1a9dc359d4c8c4aa95aa21 100644 (file)
@@ -22,6 +22,8 @@
 #define EMFILE 24
 #define ESPIPE 29
 #define ENOTTY 25
+#define ERANGE 34
+#define ENAMETOOLONG 36
 #define ENOSYS 38
 
 #endif
index c1aee4893f9357b63b9d91515cc9d7c1d85383ba..f6d88137bed86dd11bd796ba0fadd791f8dbc255 100644 (file)
@@ -51,6 +51,8 @@ struct process {
     // For SIGSEGV: last page fault address (CR2) captured in ring3.
     uintptr_t last_fault_addr;
 
+    char cwd[128];
+
     int waiting;
     int wait_pid;
     int wait_result_pid;
index 8026b6d9f42e1394b8dbd18497a9ee176dab3967..ebc4794e3b2423c0329e24936b36de425c8d1be5 100644 (file)
@@ -44,6 +44,9 @@ enum {
     SYSCALL_GETDENTS = 30,
 
     SYSCALL_FCNTL = 31,
+
+    SYSCALL_CHDIR = 32,
+    SYSCALL_GETCWD = 33,
 };
 
 #endif
index 827d5cfb51d20bc8526ba051f57317a1704f6a9a..2adbe024fd9c432ff564a606b623d023d118a73e 100644 (file)
@@ -280,6 +280,12 @@ struct process* process_fork_create(uintptr_t child_as, const struct registers*
     proc->wait_result_pid = -1;
     proc->wait_result_status = 0;
 
+    if (current_process) {
+        strcpy(proc->cwd, current_process->cwd);
+    } else {
+        strcpy(proc->cwd, "/");
+    }
+
     proc->has_user_regs = 1;
     proc->user_regs = *child_regs;
 
@@ -342,6 +348,8 @@ void process_init(void) {
     kernel_proc->wait_result_pid = -1;
     kernel_proc->wait_result_status = 0;
 
+    strcpy(kernel_proc->cwd, "/");
+
     for (int i = 0; i < PROCESS_MAX_FILES; i++) {
         kernel_proc->files[i] = NULL;
     }
index fcdd2489ee4ca7501ca277fbff5ca36ebd1bb54e..45acdd211e89f13ad86e48f71dcbecedbdfe7a46 100644 (file)
@@ -30,6 +30,8 @@ enum {
     FCNTL_F_SETFL = 4,
 };
 
+static int path_resolve_user(const char* user_path, char* out, size_t out_sz);
+
 #if defined(__i386__)
 static const uint32_t SIGFRAME_MAGIC = 0x53494746U; // 'SIGF'
 struct sigframe {
@@ -695,16 +697,8 @@ static int syscall_stat_impl(const char* user_path, struct stat* user_st) {
     if (user_range_ok(user_st, sizeof(*user_st)) == 0) return -EFAULT;
 
     char path[128];
-    for (size_t i = 0; i < sizeof(path); i++) {
-        if (copy_from_user(&path[i], &user_path[i], 1) < 0) {
-            return -EFAULT;
-        }
-        if (path[i] == 0) break;
-        if (i + 1 == sizeof(path)) {
-            path[sizeof(path) - 1] = 0;
-            break;
-        }
-    }
+    int prc = path_resolve_user(user_path, path, sizeof(path));
+    if (prc < 0) return prc;
 
     fs_node_t* node = vfs_lookup(path);
     if (!node) return -ENOENT;
@@ -760,16 +754,8 @@ static int syscall_open_impl(const char* user_path, uint32_t flags) {
     if (!user_path) return -EFAULT;
 
     char path[128];
-    for (size_t i = 0; i < sizeof(path); i++) {
-        if (copy_from_user(&path[i], &user_path[i], 1) < 0) {
-            return -EFAULT;
-        }
-        if (path[i] == 0) break;
-        if (i + 1 == sizeof(path)) {
-            path[sizeof(path) - 1] = 0;
-            break;
-        }
-    }
+    int prc = path_resolve_user(user_path, path, sizeof(path));
+    if (prc < 0) return prc;
 
     fs_node_t* node = NULL;
     if (path[0] == '/' && path[1] == 'd' && path[2] == 'i' && path[3] == 's' && path[4] == 'k' && path[5] == '/') {
@@ -821,21 +807,124 @@ static int syscall_fcntl_impl(int fd, int cmd, uint32_t arg) {
     return -EINVAL;
 }
 
-static int syscall_mkdir_impl(const char* user_path) {
+static int path_is_absolute(const char* p) {
+    return p && p[0] == '/';
+}
+
+static void path_normalize_inplace(char* s) {
+    if (!s) return;
+    // Collapse duplicate slashes and remove trailing slash (except for root).
+    char tmp[128];
+    size_t w = 0;
+    size_t r = 0;
+    if (s[0] == 0) {
+        strcpy(s, "/");
+        return;
+    }
+
+    while (s[r] != 0 && w + 1 < sizeof(tmp)) {
+        char c = s[r++];
+        if (c == '/') {
+            if (w == 0 || tmp[w - 1] != '/') {
+                tmp[w++] = '/';
+            }
+        } else {
+            tmp[w++] = c;
+        }
+    }
+    tmp[w] = 0;
+
+    size_t l = strlen(tmp);
+    while (l > 1 && tmp[l - 1] == '/') {
+        tmp[l - 1] = 0;
+        l--;
+    }
+
+    strcpy(s, tmp);
+}
+
+static int path_resolve_user(const char* user_path, char* out, size_t out_sz) {
+    if (!out || out_sz == 0) return -EINVAL;
+    out[0] = 0;
     if (!user_path) return -EFAULT;
 
-    char path[128];
-    for (size_t i = 0; i < sizeof(path); i++) {
-        if (copy_from_user(&path[i], &user_path[i], 1) < 0) {
+    char in[128];
+    for (size_t i = 0; i < sizeof(in); i++) {
+        if (copy_from_user(&in[i], &user_path[i], 1) < 0) {
             return -EFAULT;
         }
-        if (path[i] == 0) break;
-        if (i + 1 == sizeof(path)) {
-            path[sizeof(path) - 1] = 0;
+        if (in[i] == 0) break;
+        if (i + 1 == sizeof(in)) {
+            in[sizeof(in) - 1] = 0;
             break;
         }
     }
 
+    if (path_is_absolute(in)) {
+        // bounded copy
+        size_t i = 0;
+        while (in[i] != 0 && i + 1 < out_sz) {
+            out[i] = in[i];
+            i++;
+        }
+        out[i] = 0;
+        path_normalize_inplace(out);
+        return 0;
+    }
+
+    const char* base = (current_process && current_process->cwd[0]) ? current_process->cwd : "/";
+    size_t w = 0;
+    if (strcmp(base, "/") == 0) {
+        if (out_sz < 2) return -ENAMETOOLONG;
+        out[w++] = '/';
+    } else {
+        for (size_t i = 0; base[i] != 0 && w + 1 < out_sz; i++) {
+            out[w++] = base[i];
+        }
+        if (w + 1 < out_sz) out[w++] = '/';
+    }
+
+    for (size_t i = 0; in[i] != 0 && w + 1 < out_sz; i++) {
+        out[w++] = in[i];
+    }
+    out[w] = 0;
+    path_normalize_inplace(out);
+    return 0;
+}
+
+static int syscall_chdir_impl(const char* user_path) {
+    if (!current_process) return -EINVAL;
+    char path[128];
+    int rc = path_resolve_user(user_path, path, sizeof(path));
+    if (rc < 0) return rc;
+
+    fs_node_t* n = vfs_lookup(path);
+    if (!n) return -ENOENT;
+    if (n->flags != FS_DIRECTORY) return -ENOTDIR;
+    strcpy(current_process->cwd, path);
+    return 0;
+}
+
+static int syscall_getcwd_impl(char* user_buf, uint32_t size) {
+    if (!current_process) return -EINVAL;
+    if (!user_buf) return -EFAULT;
+    if (size == 0) return -EINVAL;
+    if (user_range_ok(user_buf, (size_t)size) == 0) return -EFAULT;
+
+    const char* cwd = current_process->cwd[0] ? current_process->cwd : "/";
+    uint32_t need = (uint32_t)strlen(cwd) + 1U;
+    if (need > size) return -ERANGE;
+    if (copy_to_user(user_buf, cwd, need) < 0) return -EFAULT;
+    return 0;
+}
+
+static int syscall_mkdir_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;
@@ -879,16 +968,8 @@ static int syscall_unlink_impl(const char* user_path) {
     if (!user_path) return -EFAULT;
 
     char path[128];
-    for (size_t i = 0; i < sizeof(path); i++) {
-        if (copy_from_user(&path[i], &user_path[i], 1) < 0) {
-            return -EFAULT;
-        }
-        if (path[i] == 0) break;
-        if (i + 1 == sizeof(path)) {
-            path[sizeof(path) - 1] = 0;
-            break;
-        }
-    }
+    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;
@@ -1156,6 +1237,19 @@ static void syscall_handler(struct registers* regs) {
         return;
     }
 
+    if (syscall_no == SYSCALL_CHDIR) {
+        const char* path = (const char*)regs->ebx;
+        regs->eax = (uint32_t)syscall_chdir_impl(path);
+        return;
+    }
+
+    if (syscall_no == SYSCALL_GETCWD) {
+        char* buf = (char*)regs->ebx;
+        uint32_t size = (uint32_t)regs->ecx;
+        regs->eax = (uint32_t)syscall_getcwd_impl(buf, size);
+        return;
+    }
+
     if (syscall_no == SYSCALL_READ) {
         int fd = (int)regs->ebx;
         void* buf = (void*)regs->ecx;
index 9082795099e5d95f40931802af5afd236b15697e..1bb4edfc72616bb402f02f997f7fda38761f5e8f 100644 (file)
@@ -69,6 +69,9 @@ enum {
     SYSCALL_GETDENTS = 30,
 
     SYSCALL_FCNTL = 31,
+
+    SYSCALL_CHDIR = 32,
+    SYSCALL_GETCWD = 33,
 };
 
 enum {
@@ -156,6 +159,28 @@ static int sys_write(int fd, const void* buf, uint32_t len) {
     return __syscall_fix(ret);
 }
 
+static int sys_chdir(const char* path) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_CHDIR), "b"(path)
+        : "memory"
+    );
+    return __syscall_fix(ret);
+}
+
+static int sys_getcwd(char* buf, uint32_t size) {
+    int ret;
+    __asm__ volatile(
+        "int $0x80"
+        : "=a"(ret)
+        : "a"(SYSCALL_GETCWD), "b"(buf), "c"(size)
+        : "memory"
+    );
+    return __syscall_fix(ret);
+}
+
 static int sys_fcntl(int fd, int cmd, uint32_t arg) {
     int ret;
     __asm__ volatile(
@@ -1796,6 +1821,51 @@ void _start(void) {
                   (uint32_t)(sizeof("[init] O_NONBLOCK OK\n") - 1));
     }
 
+    // B7: chdir/getcwd smoke + relative paths
+    {
+        int r = sys_mkdir("/disk/cwd");
+        if (r < 0 && errno != 17) {
+            sys_write(1, "[init] mkdir /disk/cwd failed\n",
+                      (uint32_t)(sizeof("[init] mkdir /disk/cwd failed\n") - 1));
+            sys_exit(1);
+        }
+
+        r = sys_chdir("/disk/cwd");
+        if (r < 0) {
+            sys_write(1, "[init] chdir failed\n",
+                      (uint32_t)(sizeof("[init] chdir failed\n") - 1));
+            sys_exit(1);
+        }
+
+        char cwd[64];
+        for (uint32_t i = 0; i < (uint32_t)sizeof(cwd); i++) cwd[i] = 0;
+        if (sys_getcwd(cwd, (uint32_t)sizeof(cwd)) < 0) {
+            sys_write(1, "[init] getcwd failed\n",
+                      (uint32_t)(sizeof("[init] getcwd failed\n") - 1));
+            sys_exit(1);
+        }
+
+        // Create file using relative path.
+        int fd = sys_open("rel", O_CREAT | O_TRUNC);
+        if (fd < 0) {
+            sys_write(1, "[init] open relative failed\n",
+                      (uint32_t)(sizeof("[init] open relative failed\n") - 1));
+            sys_exit(1);
+        }
+        (void)sys_close(fd);
+
+        // Stat with relative path.
+        struct stat st;
+        if (sys_stat("rel", &st) < 0) {
+            sys_write(1, "[init] stat relative failed\n",
+                      (uint32_t)(sizeof("[init] stat relative failed\n") - 1));
+            sys_exit(1);
+        }
+
+        sys_write(1, "[init] chdir/getcwd OK\n",
+                  (uint32_t)(sizeof("[init] chdir/getcwd OK\n") - 1));
+    }
+
     enum { NCHILD = 100 };
     int children[NCHILD];
     for (int i = 0; i < NCHILD; i++) {