summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/init/syscalls.c4
-rw-r--r--src/init/tests/main.c54
-rw-r--r--src/kernel/syscalls.c27
-rw-r--r--src/shared/syscalls.h4
4 files changed, 88 insertions, 1 deletions
diff --git a/src/init/syscalls.c b/src/init/syscalls.c
index 25fa134..7c2bb68 100644
--- a/src/init/syscalls.c
+++ b/src/init/syscalls.c
@@ -26,6 +26,10 @@ int _syscall_mount(handle_t h, const char __user *path, int len) {
return _syscall(_SYSCALL_MOUNT, (int)h, (int)path, len, 0);
}
+handle_t _syscall_dup(handle_t from, handle_t to, int flags) {
+ return (handle_t)_syscall(_SYSCALL_DUP, (int)from, (int)to, flags, 0);
+}
+
int _syscall_read(handle_t h, void __user *buf, size_t len, int offset) {
return _syscall(_SYSCALL_READ, (int)h, (int)buf, (int)len, offset);
}
diff --git a/src/init/tests/main.c b/src/init/tests/main.c
index 9adb50c..97ed798 100644
--- a/src/init/tests/main.c
+++ b/src/init/tests/main.c
@@ -113,6 +113,59 @@ static void test_memflag(void) {
// TODO check if reclaims
}
+static void test_dup(void) {
+ handle_t pipe[2];
+ handle_t h1, h2;
+ assert(_syscall_pipe(pipe, 0) >= 0);
+
+ if (!_syscall_fork(0, NULL)) {
+ _syscall_close(pipe[0]);
+
+ h1 = _syscall_dup(pipe[1], -1, 0);
+ assert(h1 >= 0);
+ assert(h1 != pipe[1]);
+ h2 = _syscall_dup(h1, -1, 0);
+ assert(h2 >= 0);
+ assert(h2 != pipe[1] && h2 != h1);
+
+ _syscall_write(pipe[1], "og", 2, 0);
+ _syscall_write(h1, "h1", 2, 0);
+ _syscall_write(h2, "h2", 2, 0);
+
+ _syscall_close(pipe[1]);
+ _syscall_write(h1, "h1", 2, 0);
+ _syscall_write(h2, "h2", 2, 0);
+
+ assert(_syscall_dup(h1, pipe[1], 0) == pipe[1]);
+ assert(_syscall_dup(h2, pipe[1], 0) == pipe[1]);
+ assert(_syscall_dup(h1, pipe[1], 0) == pipe[1]);
+ assert(_syscall_dup(h2, pipe[1], 0) == pipe[1]);
+ _syscall_close(h1);
+ _syscall_close(h2);
+
+ assert(_syscall_dup(pipe[1], h2, 0) == h2);
+ _syscall_write(h2, "h2", 2, 0);
+ _syscall_close(h2);
+
+ assert(_syscall_dup(pipe[1], h1, 0) == h1);
+ _syscall_write(h1, "h1", 2, 0);
+ _syscall_close(h1);
+
+ _syscall_exit(0);
+ } else {
+ char buf[16];
+ size_t count = 0;
+ _syscall_close(pipe[1]);
+ while (_syscall_read(pipe[0], buf, sizeof buf, 0) >= 0)
+ count++;
+ assert(count == 7);
+ _syscall_await();
+ }
+
+
+ _syscall_close(pipe[0]);
+}
+
static void test_malloc(void) {
// not really a test
void *p1, *p2;
@@ -144,6 +197,7 @@ void test_all(void) {
run_forked(test_interrupted_fs);
run_forked(test_orphaned_fs);
run_forked(test_memflag);
+ run_forked(test_dup);
run_forked(test_malloc);
run_forked(test_pipe);
run_forked(test_semaphore);
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
index 421b5ce..1d18ad1 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -176,6 +176,30 @@ fail:
SYSCALL_RETURN(-1);
}
+handle_t _syscall_dup(handle_t from, handle_t to, int flags) {
+ struct handle *fromh, **toh;
+ if (flags) SYSCALL_RETURN(-1);
+
+ if (to < 0) {
+ to = process_find_free_handle(process_current, 0);
+ if (to < 0) SYSCALL_RETURN(-1);
+ } else if (to >= HANDLE_MAX) {
+ SYSCALL_RETURN(-1);
+ }
+
+ if (to == from)
+ SYSCALL_RETURN(to);
+ toh = &process_current->handles[to];
+ fromh = (from >= 0 && from < HANDLE_MAX) ?
+ process_current->handles[from] : NULL;
+
+ if (*toh) handle_close(*toh);
+ *toh = fromh;
+ if (fromh) fromh->refcount++;
+
+ SYSCALL_RETURN(to);
+}
+
int _syscall_read(handle_t handle_num, void __user *buf, size_t len, int offset) {
struct handle *h = process_handle_get(process_current, handle_num);
if (!h) SYSCALL_RETURN(-1);
@@ -360,6 +384,9 @@ int _syscall(int num, int a, int b, int c, int d) {
case _SYSCALL_MOUNT:
_syscall_mount(a, (userptr_t)b, c);
break;
+ case _SYSCALL_DUP:
+ _syscall_dup(a, b, c);
+ break;
case _SYSCALL_READ:
_syscall_read(a, (userptr_t)b, c, d);
break;
diff --git a/src/shared/syscalls.h b/src/shared/syscalls.h
index f19f05a..1383ebd 100644
--- a/src/shared/syscalls.h
+++ b/src/shared/syscalls.h
@@ -15,6 +15,7 @@ enum {
_SYSCALL_OPEN,
_SYSCALL_MOUNT,
+ _SYSCALL_DUP,
_SYSCALL_READ,
_SYSCALL_WRITE,
@@ -53,8 +54,9 @@ int _syscall_await(void);
int _syscall_fork(int flags, handle_t __user *fs_front);
handle_t _syscall_open(const char __user *path, int len, int flags);
-
int _syscall_mount(handle_t h, const char __user *path, int len);
+handle_t _syscall_dup(handle_t from, handle_t to, int flags);
+
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);