summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordzwdz2022-08-19 17:50:55 +0200
committerdzwdz2022-08-19 17:50:55 +0200
commite9161cdcda9e5170f3ea5f18a8275395004ffce4 (patch)
tree104d2a290c6431012d7ceedbdd4208688e8d1387
parent05f93a814a9b5fa6b0f3223fc51566c84b92d158 (diff)
kernel/proc: abstract away managing handles
-rw-r--r--src/kernel/proc.c62
-rw-r--r--src/kernel/proc.h15
-rw-r--r--src/kernel/syscalls.c105
-rw-r--r--src/kernel/vfs/request.c35
-rw-r--r--src/shared/include/camellia/errno.h1
5 files changed, 121 insertions, 97 deletions
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index 906fc29..a9ad37a 100644
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -1,3 +1,4 @@
+#include <camellia/errno.h>
#include <kernel/arch/generic.h>
#include <kernel/execbuf.h>
#include <kernel/mem/alloc.h>
@@ -89,9 +90,9 @@ struct process *process_fork(struct process *parent, int flags) {
child->mount->refs++;
for (handle_t h = 0; h < HANDLE_MAX; h++) {
- child->handles[h] = parent->handles[h];
- if (child->handles[h])
- child->handles[h]->refcount++;
+ child->_handles[h] = parent->_handles[h];
+ if (child->_handles[h])
+ child->_handles[h]->refcount++;
}
return child;
@@ -148,8 +149,8 @@ void process_kill(struct process *p, int ret) {
if (p->state == PS_WAITS4TIMER)
timer_deschedule(p);
- for (handle_t h = 0; h < HANDLE_MAX; h++)
- handle_close(p->handles[h]);
+ for (handle_t hid = 0; hid < HANDLE_MAX; hid++)
+ process_handle_close(p, hid);
vfs_mount_remref(p->mount);
p->mount = NULL;
@@ -268,7 +269,7 @@ struct process *process_next(struct process *p) {
handle_t process_find_free_handle(struct process *proc, handle_t start_at) {
for (handle_t hid = start_at; hid < HANDLE_MAX; hid++) {
- if (proc->handles[hid] == NULL)
+ if (proc->_handles[hid] == NULL)
return hid;
}
return -1;
@@ -276,7 +277,54 @@ handle_t process_find_free_handle(struct process *proc, handle_t start_at) {
struct handle *process_handle_get(struct process *p, handle_t id) {
if (id < 0 || id >= HANDLE_MAX) return NULL;
- return p->handles[id];
+ return p->_handles[id];
+}
+
+handle_t process_handle_init(struct process *p, enum handle_type type, struct handle **hs) {
+ handle_t hid = process_find_free_handle(p, 1);
+ if (hid < 0) return -1;
+ p->_handles[hid] = handle_init(type);
+ if (hs) *hs = p->_handles[hid];
+ return hid;
+}
+
+handle_t process_handle_dup(struct process *p, handle_t from, handle_t to) {
+ struct handle *fromh, **toh;
+
+ if (to < 0) {
+ to = process_find_free_handle(p, 0);
+ if (to < 0) return -EMFILE;
+ } else if (to >= HANDLE_MAX) {
+ return -EBADF;
+ }
+
+ if (to == from) return to;
+ toh = &p->_handles[to];
+ fromh = (from >= 0 && from < HANDLE_MAX) ? p->_handles[from] : NULL;
+
+ if (*toh) handle_close(*toh);
+ *toh = fromh;
+ if (fromh) fromh->refcount++;
+
+ return to;
+}
+
+struct handle *process_handle_take(struct process *p, handle_t hid) {
+ if (hid < 0 || hid >= HANDLE_MAX) return NULL;
+ struct handle *h = p->_handles[hid];
+ p->_handles[hid] = NULL;
+ return h;
+}
+
+handle_t process_handle_put(struct process *p, struct handle *h) {
+ assert(h);
+ handle_t hid = process_find_free_handle(p, 1);
+ if (hid < 0) {
+ handle_close(h);
+ return hid;
+ }
+ p->_handles[hid] = h;
+ return hid;
}
void process_transition(struct process *p, enum process_state state) {
diff --git a/src/kernel/proc.h b/src/kernel/proc.h
index e7946b2..4b256bd 100644
--- a/src/kernel/proc.h
+++ b/src/kernel/proc.h
@@ -48,7 +48,7 @@ struct process {
};
struct vfs_mount *mount;
- struct handle *handles[HANDLE_MAX];
+ struct handle *_handles[HANDLE_MAX];
uint32_t id; /* only for debugging, don't expose to userland */
bool noreap;
@@ -84,5 +84,18 @@ struct process *process_next(struct process *);
handle_t process_find_free_handle(struct process *proc, handle_t start_at);
struct handle *process_handle_get(struct process *, handle_t);
+handle_t process_handle_init(struct process *, enum handle_type, struct handle **);
+handle_t process_handle_dup(struct process *p, handle_t from, handle_t to);
+static inline void process_handle_close(struct process *p, handle_t hid) {
+ // TODO test
+ process_handle_dup(p, -1, hid);
+}
+
+/* Gets a handle and removes the process' reference to it, without decreasing the refcount.
+ * Meant to be used together with process_handle_put. */
+struct handle *process_handle_take(struct process *, handle_t);
+/* Put a handle in a process, taking the ownership away from the caller.
+ * Doesn't increase the refcount on success, decreases it on failure. */
+handle_t process_handle_put(struct process *, struct handle *);
void process_transition(struct process *, enum process_state);
diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c
index 6bdbf5f..5f380b6 100644
--- a/src/kernel/syscalls.c
+++ b/src/kernel/syscalls.c
@@ -47,38 +47,34 @@ long _syscall_await(void) {
long _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;
+ struct handle *h;
+ handle_t hid = process_handle_init(process_current, HANDLE_FS_FRONT, &h);
+ if (hid < 0) {
+ // TODO test
+ child->noreap = true;
+ process_kill(child, -EMFILE);
+ SYSCALL_RETURN(-EMFILE);
+ }
- process_current->handles[front]->backend = backend;
+ h->backend = kmalloc(sizeof *h->backend);
+ h->backend->heap = true;
+ h->backend->is_user = true;
+ h->backend->potential_handlers = 1;
+ h->backend->refcount = 2; // child + handle
+ h->backend->user.handler = NULL;
+ h->backend->queue = NULL;
+ child->controlled = h->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);
+ virt_cpy_to(process_current->pages, fs_front, &hid, sizeof hid);
}
}
SYSCALL_RETURN(1);
@@ -181,27 +177,8 @@ fail:
}
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);
+ if (flags != 0) SYSCALL_RETURN(-ENOSYS);
+ SYSCALL_RETURN(process_handle_dup(process_current, from, to));
}
static long simple_vfsop(
@@ -257,34 +234,28 @@ long _syscall_getsize(handle_t hid) {
}
long _syscall_remove(handle_t hid) {
- if (hid < 0 || hid >= HANDLE_MAX) return -1;
- struct handle **hslot = &process_current->handles[hid];
- struct handle *h = *hslot;
- if (!h) SYSCALL_RETURN(-1);
- if (h->ro) SYSCALL_RETURN(-EACCES);
- if (h->type == HANDLE_FILE) {
+ struct handle *h = process_handle_get(process_current, hid);
+ if (!h) SYSCALL_RETURN(-EBADF);
+ if (!h->ro && h->type == HANDLE_FILE) {
vfsreq_create((struct vfs_request) {
.type = VFSOP_REMOVE,
.id = h->file_id,
.caller = process_current,
.backend = h->backend,
});
- handle_close(*hslot);
- *hslot = NULL;
+ process_handle_close(process_current, hid);
return -1; // dummy
} else {
- handle_close(*hslot);
- *hslot = NULL;
- SYSCALL_RETURN(-ENOSYS);
+ process_handle_close(process_current, hid);
+ SYSCALL_RETURN(h->ro ? -EACCES : -ENOSYS);
}
}
long _syscall_close(handle_t hid) {
- if (hid < 0 || hid >= HANDLE_MAX) return -1;
- struct handle **h = &process_current->handles[hid];
- if (!*h) SYSCALL_RETURN(-1);
- handle_close(*h);
- *h = NULL;
+ if (!process_handle_get(process_current, hid)) {
+ SYSCALL_RETURN(-EBADF);
+ }
+ process_handle_close(process_current, hid);
SYSCALL_RETURN(0);
}
@@ -361,17 +332,17 @@ void __user *_syscall_memflag(void __user *addr, size_t len, int flags) {
}
long _syscall_pipe(handle_t __user user_ends[2], int flags) {
- if (flags) SYSCALL_RETURN(-1);
+ if (flags) SYSCALL_RETURN(-ENOSYS);
+
handle_t ends[2];
struct handle *rend, *wend;
-
- ends[0] = process_find_free_handle(process_current, 0);
- if (ends[0] < 0) SYSCALL_RETURN(-1);
- ends[1] = process_find_free_handle(process_current, ends[0]+1);
- if (ends[1] < 0) SYSCALL_RETURN(-1);
-
- rend = process_current->handles[ends[0]] = handle_init(HANDLE_PIPE);
- wend = process_current->handles[ends[1]] = handle_init(HANDLE_PIPE);
+ ends[0] = process_handle_init(process_current, HANDLE_PIPE, &rend);
+ ends[1] = process_handle_init(process_current, HANDLE_PIPE, &wend);
+ if (ends[0] < 0 || ends[1] < 0) {
+ process_handle_close(process_current, ends[0]);
+ process_handle_close(process_current, ends[1]);
+ SYSCALL_RETURN(-EMFILE);
+ }
wend->pipe.write_end = true;
wend->pipe.sister = rend;
rend->pipe.sister = wend;
diff --git a/src/kernel/vfs/request.c b/src/kernel/vfs/request.c
index f4db0db..a3be057 100644
--- a/src/kernel/vfs/request.c
+++ b/src/kernel/vfs/request.c
@@ -39,35 +39,26 @@ void vfsreq_finish(struct vfs_request *req, char __user *stored, long ret,
int flags, struct process *handler)
{
if (req->type == VFSOP_OPEN && ret >= 0) {
- // TODO write tests for caller getting killed while opening a file
- if (!req->caller) panic_unimplemented();
-
- handle_t handle = process_find_free_handle(req->caller, 0);
- if (handle < 0)
- panic_invalid_state(); // we check for free handles before the open() call
-
+ struct handle *h;
if (!(flags & FSR_DELEGATE)) {
/* default behavior - create a new handle for the file, wrap the id */
- struct handle *backing = handle_init(HANDLE_FILE);
- backing->backend = req->backend;
- req->backend->refcount++;
- backing->file_id = stored;
- backing->ro = req->flags & OPEN_RO;
- req->caller->handles[handle] = backing;
+ h = handle_init(HANDLE_FILE);
+ h->backend = req->backend; req->backend->refcount++;
+ h->file_id = stored;
+ h->ro = req->flags & OPEN_RO;
} else {
/* delegating - moving a handle to the caller */
assert(handler);
+ h = process_handle_take(handler, ret);
+ }
- struct handle *h = handler->handles[ret];
- if (!h) {
- kprintf("tried delegating an invalid handle\n");
- handle = -1; // return error
- } else {
- req->caller->handles[handle] = h;
- handler->handles[ret] = NULL;
- }
+ if (h) {
+ // TODO write tests for caller getting killed while opening a file
+ if (!req->caller) panic_unimplemented();
+ ret = process_handle_put(req->caller, h);
+ } else {
+ ret = -1;
}
- ret = handle;
}
if (req->input.kern)
diff --git a/src/shared/include/camellia/errno.h b/src/shared/include/camellia/errno.h
index 9d3eddc..e3f4d34 100644
--- a/src/shared/include/camellia/errno.h
+++ b/src/shared/include/camellia/errno.h
@@ -9,3 +9,4 @@
#define ENOENT 8
#define ENOTEMPTY 9
#define EACCES 10
+#define EMFILE 11 /* All file descriptors taken. */