diff options
author | dzwdz | 2023-01-06 21:11:44 +0100 |
---|---|---|
committer | dzwdz | 2023-01-06 21:11:44 +0100 |
commit | a1a4ab33cd75f0bc1d5e71989b01ba3446c998ae (patch) | |
tree | 4796f9ffc780dcb0336ed7c66c0cb42584320d61 | |
parent | e0c7bad47a54d865ef6194643e2cd20f6094e507 (diff) |
kernel: basic procfs
-rw-r--r-- | src/kernel/proc.c | 26 | ||||
-rw-r--r-- | src/kernel/proc.h | 6 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 2 | ||||
-rw-r--r-- | src/kernel/vfs/procfs.c | 122 | ||||
-rw-r--r-- | src/kernel/vfs/procfs.h | 4 | ||||
-rw-r--r-- | src/kernel/vfs/request.c | 4 | ||||
-rw-r--r-- | src/kernel/vfs/request.h | 2 | ||||
-rw-r--r-- | src/shared/include/camellia/flags.h | 1 | ||||
-rw-r--r-- | src/shared/include/camellia/syscalls.h | 4 | ||||
-rw-r--r-- | src/user/app/shell/shell.c | 10 | ||||
-rw-r--r-- | src/user/bootstrap/main.c | 3 | ||||
-rw-r--r-- | src/user/lib/syscall.c | 4 |
12 files changed, 180 insertions, 8 deletions
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 <camellia/flags.h> #include <camellia/errno.h> +#include <camellia/flags.h> #include <kernel/arch/generic.h> #include <kernel/execbuf.h> #include <kernel/mem/alloc.h> #include <kernel/panic.h> #include <kernel/proc.h> #include <kernel/vfs/mount.h> +#include <kernel/vfs/procfs.h> #include <shared/mem.h> #include <stdint.h> 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 <camellia/errno.h> +#include <kernel/mem/virt.h> +#include <kernel/panic.h> +#include <kernel/proc.h> +#include <kernel/vfs/procfs.h> +#include <kernel/vfs/request.h> +#include <shared/mem.h> + +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 <kernel/vfs/request.h> + +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; }; }; diff --git a/src/shared/include/camellia/flags.h b/src/shared/include/camellia/flags.h index 632ac3e..5ff9f10 100644 --- a/src/shared/include/camellia/flags.h +++ b/src/shared/include/camellia/flags.h @@ -31,3 +31,4 @@ /* special handles */ #define HANDLE_NULLFS -2 +#define HANDLE_PROCFS -3 diff --git a/src/shared/include/camellia/syscalls.h b/src/shared/include/camellia/syscalls.h index f9e3832..01b1b5d 100644 --- a/src/shared/include/camellia/syscalls.h +++ b/src/shared/include/camellia/syscalls.h @@ -22,6 +22,8 @@ #define _SYSCALL_SLEEP 15 +#define _SYSCALL_PROCFS 16 + #define _SYSCALL_EXECBUF 100 #define _SYSCALL_DEBUG_KLOG 101 @@ -78,6 +80,8 @@ long _syscall_pipe(handle_t __user user_ends[2], int flags); void _syscall_sleep(long ms); +handle_t _syscall_procfs(void); + /* see shared/execbuf.h */ long _syscall_execbuf(void __user *buf, size_t len); diff --git a/src/user/app/shell/shell.c b/src/user/app/shell/shell.c index 8aa640b..3dad1bd 100644 --- a/src/user/app/shell/shell.c +++ b/src/user/app/shell/shell.c @@ -52,6 +52,16 @@ void run_args(int argc, char **argv, struct redir *redir) { } else { _syscall_mount(HANDLE_NULLFS, argv[1], strlen(argv[1])); } + } else if (!strcmp(argv[0], "procmnt")) { + if (argc < 2) { + fprintf(stderr, "procmnt: missing mountpoint\n"); + return; + } + _syscall_mount(HANDLE_PROCFS, argv[1], strlen(argv[1])); + if (!fork2_n_mount("/")) { + fs_dir_inject(argv[1]); + exit(1); + } return; } else if (!strcmp(argv[0], "cd")) { if (chdir(argc > 1 ? argv[1] : "/") < 0) diff --git a/src/user/bootstrap/main.c b/src/user/bootstrap/main.c index 97b980b..f145e74 100644 --- a/src/user/bootstrap/main.c +++ b/src/user/bootstrap/main.c @@ -20,6 +20,9 @@ _Noreturn void main(void) { fs_whitelist(l); } + _syscall_mount(HANDLE_PROCFS, "/proc/", strlen("/proc/")); + MOUNT_AT("/") { fs_dir_inject("/proc/"); } + MOUNT_AT("/init/") { tar_driver(&_initrd); } const char *initpath = "bin/amd64/init"; diff --git a/src/user/lib/syscall.c b/src/user/lib/syscall.c index 50c71a8..09b8e49 100644 --- a/src/user/lib/syscall.c +++ b/src/user/lib/syscall.c @@ -70,6 +70,10 @@ void _syscall_sleep(long ms) { return (void)_syscall(_SYSCALL_SLEEP, ms, 0, 0, 0, 0); } +handle_t _syscall_procfs(void) { + return (handle_t)_syscall(_SYSCALL_PROCFS, 0, 0, 0, 0, 0); +} + long _syscall_execbuf(void __user *buf, size_t len) { return _syscall(_SYSCALL_EXECBUF, (long)buf, (long)len, 0, 0, 0); } |