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 {
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;
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] == '/') {
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;
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;
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;
SYSCALL_GETDENTS = 30,
SYSCALL_FCNTL = 31,
+
+ SYSCALL_CHDIR = 32,
+ SYSCALL_GETCWD = 33,
};
enum {
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(
(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++) {