diff options
Diffstat (limited to 'src/kernel/handleset.c')
-rw-r--r-- | src/kernel/handleset.c | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/src/kernel/handleset.c b/src/kernel/handleset.c new file mode 100644 index 0000000..b52eaa0 --- /dev/null +++ b/src/kernel/handleset.c @@ -0,0 +1,128 @@ +#include <camellia/errno.h> +#include <camellia/flags.h> +#include <kernel/handleset.h> +#include <kernel/malloc.h> +#include <kernel/panic.h> + +HandleSet * +hs_init(void) +{ + HandleSet *hs = kzalloc(sizeof *hs, "handles"); + hs->refcount = 1; + return hs; +} + +HandleSet * +hs_copy(HandleSet *from) +{ + HandleSet *hs = hs_init(); + assert(from); + for (hid_t i = 0; i < HANDLE_MAX; i++) { + hs->h[i] = from->h[i]; + if (hs->h[i]) { + hs->h[i]->refcount++; + } + } + return hs; +} + +void +hs_unref(HandleSet *hs) +{ + assert(hs); + assert(hs->refcount != 0); + hs->refcount--; + if (hs->refcount == 0) { + for (hid_t i = 0; i < HANDLE_MAX; i++) { + hs_close(hs, i); + } + kfree(hs); + } +} + +hid_t +hs_findfree(HandleSet *hs, hid_t start) +{ + if (start < 0) start = 0; + for (hid_t i = start; i < HANDLE_MAX; i++) { + if (hs->h[i] == NULL) { + return i; + } + } + return -1; +} + +Handle * +hs_get(HandleSet *hs, hid_t id) +{ + if (id == HANDLE_NULLFS) { + // TODO get rid of this stupid hack + static Handle h = (Handle){ + .type = HANDLE_FS_FRONT, + .backend = NULL, + .refcount = 2, /* never free */ + }; + return &h; + } else if (0 <= id && id < HANDLE_MAX) { + return hs->h[id]; + } else { + return NULL; + } +} + +hid_t +hs_hinit(HandleSet *hs, enum handle_type type, Handle **hp) +{ + hid_t hid = hs_findfree(hs, 1); + if (hid < 0) return -1; + hs->h[hid] = handle_init(type); + if (hp) *hp = hs->h[hid]; + return hid; +} + +hid_t +hs_dup(HandleSet *hs, hid_t from, hid_t to, int flags) +{ + Handle *fromh, **toh; + + if (to < 0 || (flags & DUP_SEARCH)) { + to = hs_findfree(hs, to); + if (to < 0) return -EMFILE; + } else if (to >= HANDLE_MAX) { + return -EBADF; + } + + if (to == from) return to; + toh = &hs->h[to]; + fromh = hs_get(hs, from); + + if (*toh) handle_close(*toh); + *toh = fromh; + if (fromh) fromh->refcount++; + + return to; +} + +Handle * +hs_take(HandleSet *hs, hid_t hid) +{ + if (hid < 0 || hid >= HANDLE_MAX) { + return hs_get(hs, hid); // TODO can't this cause HANDLE_NULLFS to be freed? + } + Handle *h = hs->h[hid]; + hs->h[hid] = NULL; + return h; +} + +hid_t +hs_put(HandleSet *hs, Handle *h) +{ + assert(h); + hid_t hid = hs_findfree(hs, 1); + if (hid < 0) { + handle_close(h); + return hid; + } + hs->h[hid] = h; + return hid; +} |