diff options
author | dzwdz | 2022-05-06 14:41:58 +0200 |
---|---|---|
committer | dzwdz | 2022-05-06 14:41:58 +0200 |
commit | 8ee57c885a72854d1884a886de4db538a8468e07 (patch) | |
tree | 9c9f2eea8d7667ce7ed45dd71b6bbde40ce93f7e | |
parent | 53d21d1ccb75004d0085efedd688b695707a3138 (diff) |
syscalls: merge fork() and fs_fork2()
-rw-r--r-- | src/init/fs/misc.c | 11 | ||||
-rw-r--r-- | src/init/main.c | 6 | ||||
-rw-r--r-- | src/init/shell.c | 2 | ||||
-rw-r--r-- | src/init/syscalls.c | 8 | ||||
-rw-r--r-- | src/init/tests/main.c | 32 | ||||
-rw-r--r-- | src/kernel/proc.c | 12 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 67 | ||||
-rw-r--r-- | src/shared/syscalls.h | 9 |
8 files changed, 79 insertions, 68 deletions
diff --git a/src/init/fs/misc.c b/src/init/fs/misc.c index ca3128d..f4965d5 100644 --- a/src/init/fs/misc.c +++ b/src/init/fs/misc.c @@ -6,9 +6,12 @@ #include <stdbool.h> bool fork2_n_mount(const char *path) { - handle_t h = _syscall_fs_fork2(); - if (h) _syscall_mount(h, path, strlen(path)); - return h; + handle_t h; + if (_syscall_fork(FORK_NEWFS, &h) > 0) { /* parent */ + _syscall_mount(h, path, strlen(path)); + return true; + } + return false; } static void fs_respond_delegate(struct fs_wait_response *res, handle_t delegate, const char *og_buf) { @@ -53,7 +56,7 @@ static void fs_respond_delegate(struct fs_wait_response *res, handle_t delegate, switch (res->op) { case VFSOP_READ: - if (_syscall_fork(FORK_NOREAP)) { + if (_syscall_fork(FORK_NOREAP, NULL)) { // handle reads in a child // this is a HORRIBLE workaround for making concurrent IO work without proper delegates break; diff --git a/src/init/main.c b/src/init/main.c index a5323a3..06c267a 100644 --- a/src/init/main.c +++ b/src/init/main.c @@ -33,7 +33,7 @@ int main(void) { file_close(&__stdout); - if (_syscall_fork(0)) { + if (_syscall_fork(0, NULL)) { /* (used to) expose a bug in the kernel * the program will flow like this: * 1. we launch the forked init @@ -50,7 +50,7 @@ int main(void) { _syscall_exit(1); } - if (!_syscall_fork(0)) { + if (!_syscall_fork(0, NULL)) { if (file_open(&__stdout, "/com1") < 0 || file_open(&__stdin, "/com1") < 0) _syscall_exit(1); @@ -59,7 +59,7 @@ int main(void) { } - if (!_syscall_fork(0)) { + if (!_syscall_fork(0, NULL)) { if (file_open(&__stdout, "/vga_tty") < 0) _syscall_exit(1); diff --git a/src/init/shell.c b/src/init/shell.c index e7c836f..105231e 100644 --- a/src/init/shell.c +++ b/src/init/shell.c @@ -145,7 +145,7 @@ void shell_loop(void) { } else if (!strcmp(cmd, "exit")) { _syscall_exit(0); } else if (!strcmp(cmd, "fork")) { - if (_syscall_fork(0)) + if (_syscall_fork(0, NULL)) _syscall_await(); else level++; } else if (!strcmp(cmd, "run_tests")) { diff --git a/src/init/syscalls.c b/src/init/syscalls.c index daddde9..ed7420e 100644 --- a/src/init/syscalls.c +++ b/src/init/syscalls.c @@ -14,8 +14,8 @@ int _syscall_await(void) { return _syscall(_SYSCALL_AWAIT, 0, 0, 0, 0); } -int _syscall_fork(int flags) { - return _syscall(_SYSCALL_FORK, flags, 0, 0, 0); +int _syscall_fork(int flags, handle_t __user *fs_front) { + return _syscall(_SYSCALL_FORK, flags, (int)fs_front, 0, 0); } handle_t _syscall_open(const char __user *path, int len) { @@ -38,10 +38,6 @@ int _syscall_close(handle_t h) { return _syscall(_SYSCALL_CLOSE, (int)h, 0, 0, 0); } -handle_t _syscall_fs_fork2(void) { - return _syscall(_SYSCALL_FS_FORK2, 0, 0, 0, 0); -} - int _syscall_fs_wait(char __user *buf, int max_len, struct fs_wait_response __user *res) { return _syscall(_SYSCALL_FS_WAIT, (int)buf, max_len, (int)res, 0); } diff --git a/src/init/tests/main.c b/src/init/tests/main.c index ac8874f..182c545 100644 --- a/src/init/tests/main.c +++ b/src/init/tests/main.c @@ -12,7 +12,7 @@ #define assert(cond) if (!(cond)) test_fail(); static void run_forked(void (*fn)()) { - if (!_syscall_fork(0)) { + if (!_syscall_fork(0, NULL)) { fn(); _syscall_exit(0); } else { @@ -31,7 +31,7 @@ static void test_await(void) { int counts[16] = {0}; for (int i = 0; i < 16; i++) - if (!_syscall_fork(0)) + if (!_syscall_fork(0, NULL)) _syscall_exit(i); while ((ret = _syscall_await()) != ~0) { @@ -49,12 +49,12 @@ static void test_faults(void) { * reap all its children */ int await_cnt = 0; - if (!_syscall_fork(0)) { // invalid memory access + if (!_syscall_fork(0, NULL)) { // invalid memory access asm volatile("movb $69, 0" ::: "memory"); printf("this shouldn't happen"); _syscall_exit(-1); } - if (!_syscall_fork(0)) { // #GP + if (!_syscall_fork(0, NULL)) { // #GP asm volatile("hlt" ::: "memory"); printf("this shouldn't happen"); _syscall_exit(-1); @@ -65,36 +65,36 @@ static void test_faults(void) { } static void test_interrupted_fs(void) { - handle_t h = _syscall_fs_fork2(); - if (h) { - _syscall_mount(h, "/", 1); - int ret = _syscall_open("/", 1); - // the handler quits while handling that call - but this syscall should return anyways - _syscall_exit(ret < 0 ? 0 : -1); - } else { + handle_t h; + if (_syscall_fork(FORK_NEWFS, &h)) { /* child */ // TODO make a similar test with all 0s passed to fs_wait struct fs_wait_response res; _syscall_fs_wait(NULL, 0, &res); _syscall_exit(0); + } else { /* parent */ + _syscall_mount(h, "/", 1); + int ret = _syscall_open("/", 1); + // the handler quits while handling that call - but this syscall should return anyways + _syscall_exit(ret < 0 ? 0 : -1); } } static void test_orphaned_fs(void) { - handle_t h = _syscall_fs_fork2(); - if (h) { + handle_t h; + if (_syscall_fork(FORK_NEWFS, &h)) { /* child */ + _syscall_exit(0); + } else { /* parent */ _syscall_mount(h, "/", 1); int ret = _syscall_open("/", 1); // no handler will ever be available to handle this call - the syscall should instantly return _syscall_exit(ret < 0 ? 0 : -1); - } else { - _syscall_exit(0); } } static void stress_fork(void) { /* run a lot of processes */ for (size_t i = 0; i < 2048; i++) { - if (!_syscall_fork(0)) _syscall_exit(0); + if (!_syscall_fork(0, NULL)) _syscall_exit(0); _syscall_await(); } } diff --git a/src/kernel/proc.c b/src/kernel/proc.c index ed257fd..7ef4579 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -44,7 +44,7 @@ struct process *process_seed(struct kmain_info *info) { struct process *process_fork(struct process *parent, int flags) { struct process *child = kmalloc(sizeof *child); - memcpy(child, parent, sizeof *child); + memcpy(child, parent, sizeof *child); // TODO manually set fields child->pages = pagedir_copy(parent->pages); child->sibling = parent->child; @@ -55,9 +55,13 @@ struct process *process_fork(struct process *parent, int flags) { parent->handled_req = NULL; // TODO control this with a flag - if (child->controlled) { - child->controlled->potential_handlers++; - child->controlled->refcount++; + if ((flags & FORK_NEWFS) == 0) { + if (child->controlled) { + child->controlled->potential_handlers++; + child->controlled->refcount++; + } + } else { + child->controlled = NULL; } for (handle_t h = 0; h < HANDLE_MAX; h++) { diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 3443b60..59358a8 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -39,9 +39,42 @@ int _syscall_await(void) { } } -int _syscall_fork(int flags) { - struct process *child = process_fork(process_current, flags); +int _syscall_fork(int flags, handle_t __user *fs_front) { + struct process *child; + handle_t front; + + if ((flags & FORK_NEWFS) && fs_front) { + /* we'll need to return a handle, check if that's possible */ + front = process_find_free_handle(process_current, 1); + if (front < 0) SYSCALL_RETURN(-1); + } + + child = process_fork(process_current, flags); regs_savereturn(&child->regs, 0); + + if ((flags & FORK_NEWFS) && fs_front) { + struct vfs_backend *backend = kmalloc(sizeof *backend); + + process_current->handles[front] = handle_init(HANDLE_FS_FRONT); + + backend->heap = true; + backend->is_user = true; + backend->potential_handlers = 1; + backend->refcount = 2; // child + handle + backend->user.handler = NULL; + backend->queue = NULL; + + child->controlled = backend; + + process_current->handles[front]->fs.backend = backend; + + if (fs_front) { + /* failure ignored. if you pass an invalid pointer to this function, + * you just don't receive the handle. you'll probably segfault + * trying to access it anyways */ + virt_cpy_to(process_current->pages, fs_front, &front, sizeof front); + } + } SYSCALL_RETURN(1); } @@ -179,32 +212,6 @@ int _syscall_close(handle_t hid) { SYSCALL_RETURN(0); } -handle_t _syscall_fs_fork2(void) { - struct vfs_backend *backend; - struct process *child; - handle_t front; - - front = process_find_free_handle(process_current, 1); - if (front < 0) SYSCALL_RETURN(-1); - process_current->handles[front] = handle_init(HANDLE_FS_FRONT); - - backend = kmalloc(sizeof *backend); - backend->heap = true; - backend->is_user = true; - backend->potential_handlers = 1; - backend->refcount = 2; // child + handle - backend->user.handler = NULL; - backend->queue = NULL; - - child = process_fork(process_current, 0); - if (child->controlled) vfs_backend_refdown(child->controlled); - child->controlled = backend; - regs_savereturn(&child->regs, 0); - - process_current->handles[front]->fs.backend = backend; - SYSCALL_RETURN(front); -} - int _syscall_fs_wait(char __user *buf, int max_len, struct fs_wait_response __user *res) { struct vfs_backend *backend = process_current->controlled; if (!backend) SYSCALL_RETURN(-1); @@ -267,7 +274,7 @@ int _syscall(int num, int a, int b, int c, int d) { case _SYSCALL_AWAIT: return _syscall_await(); case _SYSCALL_FORK: - return _syscall_fork(a); + return _syscall_fork(a, (userptr_t)b); case _SYSCALL_OPEN: return _syscall_open((userptr_t)a, b); case _SYSCALL_MOUNT: @@ -278,8 +285,6 @@ int _syscall(int num, int a, int b, int c, int d) { return _syscall_write(a, (userptr_t)b, c, d); case _SYSCALL_CLOSE: return _syscall_close(a); - case _SYSCALL_FS_FORK2: - return _syscall_fs_fork2(); case _SYSCALL_FS_WAIT: return _syscall_fs_wait((userptr_t)a, b, (userptr_t)c); case _SYSCALL_FS_RESPOND: diff --git a/src/shared/syscalls.h b/src/shared/syscalls.h index d986d11..8dee783 100644 --- a/src/shared/syscalls.h +++ b/src/shared/syscalls.h @@ -3,6 +3,7 @@ #include <stddef.h> #define FORK_NOREAP 1 +#define FORK_NEWFS 2 enum { // idc about stable syscall numbers just yet @@ -38,9 +39,13 @@ int _syscall_await(void); /** Creates a copy of the current process, and executes it. * All user memory pages get copied too. + * + * @param flags FORK_NOREAP, FORK_NEWFS + * @param fs_front requires FORK_NEWFS. the front handle to the new fs is put there + * * @return 0 in the child, a meaningless positive value in the parent. */ -int _syscall_fork(int flags); +int _syscall_fork(int flags, handle_t __user *fs_front); handle_t _syscall_open(const char __user *path, int len); @@ -49,8 +54,6 @@ int _syscall_read(handle_t h, void __user *buf, size_t len, int offset); int _syscall_write(handle_t h, const void __user *buf, size_t len, int offset); int _syscall_close(handle_t h); -handle_t _syscall_fs_fork2(void); - struct fs_wait_response { enum vfs_operation op; size_t len; // how much was put in *buf |