summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordzwdz2022-05-06 14:41:58 +0200
committerdzwdz2022-05-06 14:41:58 +0200
commit8ee57c885a72854d1884a886de4db538a8468e07 (patch)
tree9c9f2eea8d7667ce7ed45dd71b6bbde40ce93f7e
parent53d21d1ccb75004d0085efedd688b695707a3138 (diff)
syscalls: merge fork() and fs_fork2()
-rw-r--r--src/init/fs/misc.c11
-rw-r--r--src/init/main.c6
-rw-r--r--src/init/shell.c2
-rw-r--r--src/init/syscalls.c8
-rw-r--r--src/init/tests/main.c32
-rw-r--r--src/kernel/proc.c12
-rw-r--r--src/kernel/syscalls.c67
-rw-r--r--src/shared/syscalls.h9
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