From e9161cdcda9e5170f3ea5f18a8275395004ffce4 Mon Sep 17 00:00:00 2001 From: dzwdz Date: Fri, 19 Aug 2022 17:50:55 +0200 Subject: kernel/proc: abstract away managing handles --- src/kernel/proc.c | 62 ++++++++++++++++++--- src/kernel/proc.h | 15 +++++- src/kernel/syscalls.c | 105 +++++++++++++----------------------- src/kernel/vfs/request.c | 35 +++++------- src/shared/include/camellia/errno.h | 1 + 5 files changed, 121 insertions(+), 97 deletions(-) (limited to 'src') 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 #include #include #include @@ -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. */ -- cgit v1.2.3