From a1a4ab33cd75f0bc1d5e71989b01ba3446c998ae Mon Sep 17 00:00:00 2001 From: dzwdz Date: Fri, 6 Jan 2023 21:11:44 +0100 Subject: kernel: basic procfs --- src/kernel/proc.c | 26 ++++++++-- src/kernel/proc.h | 6 ++- src/kernel/syscalls.c | 2 +- src/kernel/vfs/procfs.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++ src/kernel/vfs/procfs.h | 4 ++ src/kernel/vfs/request.c | 4 +- src/kernel/vfs/request.h | 2 + 7 files changed, 158 insertions(+), 8 deletions(-) create mode 100644 src/kernel/vfs/procfs.c create mode 100644 src/kernel/vfs/procfs.h (limited to 'src/kernel') diff --git a/src/kernel/proc.c b/src/kernel/proc.c index 6bc7754..3f3aba5 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -1,18 +1,19 @@ -#include #include +#include #include #include #include #include #include #include +#include #include #include static struct process *process_first = NULL; struct process *process_current; -static uint32_t next_pid = 0; +static uint32_t next_pid = 1; /** Removes a process from the process tree. */ @@ -211,6 +212,8 @@ void process_try2collect(struct process *dead) { process_transition(parent, PS_RUNNING); } + handle_close(dead->specialh.procfs); + assert(dead->refcount == 0); if (dead != process_first) { process_forget(dead); kfree(dead); @@ -291,12 +294,25 @@ struct handle *process_handle_get(struct process *p, handle_t id) { static struct handle h = (struct handle){ .type = HANDLE_FS_FRONT, .backend = NULL, - .refcount = 2, + .refcount = 2, /* never free */ }; return &h; + } else if (id == HANDLE_PROCFS) { + if (!p->specialh.procfs) { + struct handle *h = kmalloc(sizeof *h); + *h = (struct handle){ + .type = HANDLE_FS_FRONT, + .backend = procfs_backend(p), + .refcount = 1, + }; + p->specialh.procfs = h; + } + return p->specialh.procfs; + } else if (0 <= id && id < HANDLE_MAX) { + return p->_handles[id]; + } else { + return NULL; } - if (id < 0 || id >= HANDLE_MAX) return NULL; - return p->_handles[id]; } handle_t process_handle_init(struct process *p, enum handle_type type, struct handle **hs) { diff --git a/src/kernel/proc.h b/src/kernel/proc.h index eb39b51..07cba28 100644 --- a/src/kernel/proc.h +++ b/src/kernel/proc.h @@ -50,10 +50,14 @@ struct process { struct vfs_mount *mount; struct handle **_handles; /* points to struct handle *[HANDLE_MAX] */ uint64_t *handles_refcount; /* works just like pages_refcount */ + struct { + struct handle *procfs; + } specialh; uint32_t cid; /* child id. unique amongst all of this process' siblings */ uint32_t nextcid; /* the child id to assign to the next spawned child */ - uint32_t globalid; /* only for debugging, don't expose to userland */ + uint32_t globalid; /* only for internal use, don't expose to userland */ + uint32_t refcount; /* non-owning. should always be 0 on kill */ bool noreap; /* allocated once, the requests from WAITS4FS get stored here */ diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 2ace0f4..f1abe30 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -60,7 +60,7 @@ long _syscall_fork(int flags, handle_t __user *fs_front) { SYSCALL_RETURN(-EMFILE); } - h->backend = kmalloc(sizeof *h->backend); + h->backend = kzalloc(sizeof *h->backend); h->backend->is_user = true; h->backend->potential_handlers = 1; h->backend->refcount = 2; // child + handle diff --git a/src/kernel/vfs/procfs.c b/src/kernel/vfs/procfs.c new file mode 100644 index 0000000..e434c45 --- /dev/null +++ b/src/kernel/vfs/procfs.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include +#include + +static uint32_t openpath(const char *path, size_t len, struct process *root); +static struct process *findgid(uint32_t gid, struct process *root); +static void procfs_accept(struct vfs_request *req); +static void procfs_cleanup(struct vfs_backend *be); + +static uint32_t +openpath(const char *path, size_t len, struct process *p) +{ + if (len == 0) return 0; + path++, len--; + + while (len) { + uint32_t cid = 0; + for (; 0 < len && *path != '/'; path++, len--) { + char c = *path; + if (!('0' <= c && c <= '9')) { + return 0; + } + cid = cid * 10 + *path - '0'; + } + if (len == 0) { + return 0; + } + assert(*path == '/'); + path++, len--; + + p = p->child; + if (!p) return 0; + while (p->cid != cid) { + p = p->sibling; + if (!p) return 0; + } + } + return p->globalid; +} + +static struct process * +findgid(uint32_t gid, struct process *root) +{ + for (struct process *p = root; p; p = process_next(p)) { + if (p->globalid == gid) return p; + } + return NULL; +} + +static void +procfs_accept(struct vfs_request *req) +{ + struct process *root = req->backend->kern.data; + struct process *p; + char buf[512]; + assert(root); + if (req->type == VFSOP_OPEN) { + int gid; + assert(req->input.kern); + gid = openpath(req->input.buf_kern, req->input.len, root); + vfsreq_finish_short(req, gid == 0 ? -ENOENT : gid); + return; + } + p = findgid((uintptr_t)req->id, root); + if (!p) { + vfsreq_finish_short(req, -EGENERIC); + return; + } + + if (req->type == VFSOP_READ) { + // TODO port dirbuild to kernel + int pos = 0; + if (req->offset != 0) { + vfsreq_finish_short(req, -ENOSYS); + return; + } + for (struct process *iter = p->child; iter; iter = iter->sibling) { + assert(pos < 512); + // processes could possibly be identified by unique identifiers instead + // e.g. an encrypted gid, or just a randomly generated one + // con: would require bringing in a crypto library + pos += snprintf(buf + pos, 512 - pos, "%d/", iter->cid) + 1; + if (512 <= pos) { + vfsreq_finish_short(req, -1); + } + } + assert(0 <= pos && (size_t)pos <= sizeof buf); + virt_cpy_to(req->caller->pages, req->output.buf, buf, pos); + vfsreq_finish_short(req, pos); + } else { + vfsreq_finish_short(req, -ENOSYS); + } +} + +static void +procfs_cleanup(struct vfs_backend *be) +{ + struct process *p = be->kern.data; + assert(p); + p->refcount--; +} + +struct vfs_backend * +procfs_backend(struct process *proc) +{ + struct vfs_backend *be = kzalloc(sizeof(struct vfs_backend)); + *be = (struct vfs_backend) { + .is_user = false, + .potential_handlers = 1, + .refcount = 1, + .kern.accept = procfs_accept, + .kern.data = proc, + .kern.cleanup = procfs_cleanup, + }; + proc->refcount++; + assert(proc->refcount); /* overflow */ + return be; +} diff --git a/src/kernel/vfs/procfs.h b/src/kernel/vfs/procfs.h new file mode 100644 index 0000000..5ee4e96 --- /dev/null +++ b/src/kernel/vfs/procfs.h @@ -0,0 +1,4 @@ +#pragma once +#include + +struct vfs_backend *procfs_backend(struct process *proc); diff --git a/src/kernel/vfs/request.c b/src/kernel/vfs/request.c index a129f2e..2288de5 100644 --- a/src/kernel/vfs/request.c +++ b/src/kernel/vfs/request.c @@ -155,7 +155,9 @@ void vfs_backend_refdown(struct vfs_backend *b) { assert(b); assert(b->refcount > 0); if (--(b->refcount) > 0) return; - assert(!b->queue); + if (!b->is_user && b->kern.cleanup) { + b->kern.cleanup(b); + } kfree(b); } diff --git a/src/kernel/vfs/request.h b/src/kernel/vfs/request.h index db707d0..ef7c83a 100644 --- a/src/kernel/vfs/request.h +++ b/src/kernel/vfs/request.h @@ -22,6 +22,8 @@ struct vfs_backend { } user; struct { void (*accept)(struct vfs_request *); + void (*cleanup)(struct vfs_backend *); + void *data; } kern; }; }; -- cgit v1.2.3