From: Tulio A M Mendes Date: Tue, 10 Feb 2026 01:21:12 +0000 (-0300) Subject: posix: add chdir/getcwd and relative path resolution X-Git-Url: https://projects.tadryanom.me/sitemap.xml?a=commitdiff_plain;h=8cece123761e3eec3049eed729a00781bb0aaf8a;p=AdrOS.git posix: add chdir/getcwd and relative path resolution --- diff --git a/include/errno.h b/include/errno.h index 603b20d..18bd1bf 100644 --- a/include/errno.h +++ b/include/errno.h @@ -22,6 +22,8 @@ #define EMFILE 24 #define ESPIPE 29 #define ENOTTY 25 +#define ERANGE 34 +#define ENAMETOOLONG 36 #define ENOSYS 38 #endif diff --git a/include/process.h b/include/process.h index c1aee48..f6d8813 100644 --- a/include/process.h +++ b/include/process.h @@ -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; diff --git a/include/syscall.h b/include/syscall.h index 8026b6d..ebc4794 100644 --- a/include/syscall.h +++ b/include/syscall.h @@ -44,6 +44,9 @@ enum { SYSCALL_GETDENTS = 30, SYSCALL_FCNTL = 31, + + SYSCALL_CHDIR = 32, + SYSCALL_GETCWD = 33, }; #endif diff --git a/src/kernel/scheduler.c b/src/kernel/scheduler.c index 827d5cf..2adbe02 100644 --- a/src/kernel/scheduler.c +++ b/src/kernel/scheduler.c @@ -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; } diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c index fcdd248..45acdd2 100644 --- a/src/kernel/syscall.c +++ b/src/kernel/syscall.c @@ -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; diff --git a/user/init.c b/user/init.c index 9082795..1bb4edf 100644 --- a/user/init.c +++ b/user/init.c @@ -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++) {