diff options
author | dzwdz | 2023-01-11 19:35:44 +0100 |
---|---|---|
committer | dzwdz | 2023-01-11 19:37:46 +0100 |
commit | ec81fa16d837f430add92b4d2ee4bd3727ca6c6d (patch) | |
tree | 43c91abe8d6a76fcec006fe67c1b585573d3662f /src | |
parent | c178ab5d5ca328d5b0072d54e3dc66d1b198df7b (diff) |
kernel: return EPIPE when fs_waiting on a dead filesystem
Diffstat (limited to 'src')
-rw-r--r-- | src/kernel/handle.c | 2 | ||||
-rwxr-xr-x[-rw-r--r--] | src/kernel/proc.c | 20 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 13 | ||||
-rw-r--r-- | src/kernel/vfs/mount.c | 16 | ||||
-rw-r--r-- | src/kernel/vfs/procfs.c | 4 | ||||
-rw-r--r-- | src/kernel/vfs/request.c | 48 | ||||
-rw-r--r-- | src/kernel/vfs/request.h | 22 | ||||
-rw-r--r-- | src/shared/include/camellia/errno.h | 1 | ||||
-rw-r--r-- | src/user/app/ext2fs/main.c | 1 | ||||
-rw-r--r-- | src/user/app/init/init.c | 1 | ||||
-rw-r--r-- | src/user/app/tests/kernel/fs.c | 40 | ||||
-rw-r--r-- | src/user/lib/include/__errno.h | 1 |
12 files changed, 119 insertions, 50 deletions
diff --git a/src/kernel/handle.c b/src/kernel/handle.c index b21ebdc..fabe559 100644 --- a/src/kernel/handle.c +++ b/src/kernel/handle.c @@ -36,7 +36,7 @@ void handle_close(struct handle *h) { } if (h->backend) - vfs_backend_refdown(h->backend); + vfs_backend_refdown(h->backend, true); // TODO sanity check to check if refcount is true. handle_sanity? diff --git a/src/kernel/proc.c b/src/kernel/proc.c index 36e6004..408e3d0 100644..100755 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -79,8 +79,8 @@ struct process *process_fork(struct process *parent, int flags) { if ((flags & FORK_NEWFS) == 0 && parent->controlled) { child->controlled = parent->controlled; - child->controlled->potential_handlers++; - child->controlled->refcount++; + assert(child->controlled->provhcnt); + child->controlled->provhcnt++; } child->mount = parent->mount; @@ -123,24 +123,12 @@ void process_kill(struct process *p, int ret) { if (proc_alive(p)) { if (p->controlled) { // TODO vfs_backend_user_handlerdown - assert(p->controlled->potential_handlers > 0); - p->controlled->potential_handlers--; - if (p->controlled->potential_handlers == 0) { - // orphaned - struct vfs_request *q = p->controlled->queue; - while (q) { - struct vfs_request *q2 = q->queue_next; - vfsreq_finish_short(q, -1); - q = q2; - } - p->controlled->queue = NULL; - } + assert(p->controlled->provhcnt > 0); if (p->controlled->user.handler == p) { assert(p->state == PS_WAITS4REQUEST); p->controlled->user.handler = NULL; } - - vfs_backend_refdown(p->controlled); + vfs_backend_refdown(p->controlled, false); p->controlled = NULL; } diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index 8bd7de3..3ebda61 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -62,8 +62,8 @@ long _syscall_fork(int flags, handle_t __user *fs_front) { h->backend = kzalloc(sizeof *h->backend); h->backend->is_user = true; - h->backend->potential_handlers = 1; - h->backend->refcount = 2; // child + handle + h->backend->provhcnt = 1; /* child */ + h->backend->usehcnt = 1; /* handle */ h->backend->user.handler = NULL; h->backend->queue = NULL; child->controlled = h->backend; @@ -149,7 +149,10 @@ long _syscall_mount(handle_t hid, const char __user *path, long len) { if (!handle || handle->type != HANDLE_FS_FRONT) goto fail; backend = handle->backend; - if (backend) backend->refcount++; + if (backend) { + assert(backend->usehcnt); + backend->usehcnt++; + } // append to mount list // TODO move to kernel/vfs/mount.c @@ -269,6 +272,10 @@ handle_t _syscall_fs_wait(char __user *buf, long max_len, struct ufs_request __u struct vfs_backend *backend = process_current->controlled; // TODO can be used to tell if you're init if (!backend) SYSCALL_RETURN(-1); + if (backend->usehcnt == 0) { + /* nothing on the other end. EPIPE seems fitting */ + SYSCALL_RETURN(-EPIPE); + } process_transition(process_current, PS_WAITS4REQUEST); if (backend->user.handler) diff --git a/src/kernel/vfs/mount.c b/src/kernel/vfs/mount.c index aa73acb..2815bb9 100644 --- a/src/kernel/vfs/mount.c +++ b/src/kernel/vfs/mount.c @@ -14,8 +14,8 @@ void vfs_root_register(const char *path, void (*accept)(struct vfs_request *)) { struct vfs_mount *mount = kmalloc(sizeof *mount); *backend = (struct vfs_backend) { .is_user = false, - .potential_handlers = 1, - .refcount = 1, + .usehcnt = 1, + .provhcnt = 1, .kern.accept = accept, }; *mount = (struct vfs_mount){ @@ -55,11 +55,15 @@ void vfs_mount_remref(struct vfs_mount *mnt) { if (--(mnt->refs) > 0) return; struct vfs_mount *prev = mnt->prev; - if (mnt->backend) - vfs_backend_refdown(mnt->backend); - if (mnt->prefix_owned) + if (mnt->backend) { + vfs_backend_refdown(mnt->backend, true); + } + if (mnt->prefix_owned) { kfree((void*)mnt->prefix); + } kfree(mnt); - if (prev) vfs_mount_remref(prev); + if (prev) { + vfs_mount_remref(prev); + } } diff --git a/src/kernel/vfs/procfs.c b/src/kernel/vfs/procfs.c index e434c45..0f65f93 100644 --- a/src/kernel/vfs/procfs.c +++ b/src/kernel/vfs/procfs.c @@ -110,8 +110,8 @@ 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, + .provhcnt = 1, + .usehcnt = 1, .kern.accept = procfs_accept, .kern.data = proc, .kern.cleanup = procfs_cleanup, diff --git a/src/kernel/vfs/request.c b/src/kernel/vfs/request.c index 2288de5..1cd2787 100644 --- a/src/kernel/vfs/request.c +++ b/src/kernel/vfs/request.c @@ -21,7 +21,10 @@ void vfsreq_create(struct vfs_request req_) { req = kmalloc(sizeof *req); } memcpy(req, &req_, sizeof *req); - if (req->backend) req->backend->refcount++; + if (req->backend) { + assert(req->backend->usehcnt); + req->backend->usehcnt++; + } if (req->type == VFSOP_OPEN && !(req->flags & OPEN_WRITE) && (req->flags & OPEN_CREATE)) { vfsreq_finish_short(req, -EINVAL); @@ -30,7 +33,7 @@ void vfsreq_create(struct vfs_request req_) { // TODO if i add a handle field to vfs_request, check ->readable ->writeable here - if (req->backend && req->backend->potential_handlers) { + if (req->backend && req->backend->provhcnt) { struct vfs_request **iter = &req->backend->queue; while (*iter != NULL) // find free spot in queue iter = &(*iter)->queue_next; @@ -49,7 +52,8 @@ void vfsreq_finish(struct vfs_request *req, char __user *stored, long ret, if (!(flags & FSR_DELEGATE)) { /* default behavior - create a new handle for the file, wrap the id */ h = handle_init(HANDLE_FILE); - h->backend = req->backend; req->backend->refcount++; + h->backend = req->backend; + req->backend->usehcnt++; h->file_id = stored; h->readable = OPEN_READABLE(req->flags); h->writeable = OPEN_WRITEABLE(req->flags); @@ -74,7 +78,7 @@ void vfsreq_finish(struct vfs_request *req, char __user *stored, long ret, kfree(req->input.buf_kern); if (req->backend) - vfs_backend_refdown(req->backend); + vfs_backend_refdown(req->backend, true); if (req->caller) { assert(req->caller->state == PS_WAITS4FS); @@ -151,13 +155,35 @@ static void vfs_backend_user_accept(struct vfs_request *req) { return; } -void vfs_backend_refdown(struct vfs_backend *b) { +void vfs_backend_refdown(struct vfs_backend *b, bool use) { + size_t *field = use ? &b->usehcnt : &b->provhcnt; 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); + assert(0 < *field); + *field -= 1; + + if (b->provhcnt == 0 && use == false) { + struct vfs_request *q = b->queue; + while (q) { + struct vfs_request *q2 = q->queue_next; + vfsreq_finish_short(q, -1); + q = q2; + } + b->queue = NULL; + } + if (b->usehcnt == 0 && use == true) { + if (!b->is_user && b->kern.cleanup) { + b->kern.cleanup(b); + } + if (b->is_user && b->user.handler) { + struct process *p = b->user.handler; + b->user.handler = NULL; + assert(p->state == PS_WAITS4REQUEST); + regs_savereturn(&p->regs, -EPIPE); + process_transition(p, PS_RUNNING); + } + } + if (b->usehcnt == 0 && b->provhcnt == 0) { + assert(!b->queue); + kfree(b); } - kfree(b); } diff --git a/src/kernel/vfs/request.h b/src/kernel/vfs/request.h index ef7c83a..4d7fa01 100644 --- a/src/kernel/vfs/request.h +++ b/src/kernel/vfs/request.h @@ -5,15 +5,17 @@ // describes something which can act as an access function struct vfs_backend { - /* references: - * struct vfs_mount - * struct vfs_request - * struct process - * struct handle - */ - size_t refcount; + /* amount of using references + * struct vfs_mount + * struct vfs_request + * struct handle + * once it reaches 0, it'll never increase */ + size_t usehcnt; /* struct vfs_mount */ + /* amount of providing references + * struct process + * 0 - orphaned, will never increase */ + size_t provhcnt; - size_t potential_handlers; // 0 - orphaned struct vfs_request *queue; bool is_user; union { @@ -72,4 +74,6 @@ static inline void vfsreq_finish_short(struct vfs_request *req, long ret) { /** Try to accept an enqueued request */ void vfs_backend_tryaccept(struct vfs_backend *); -void vfs_backend_refdown(struct vfs_backend *); +// TODO the bool arg is confusing. maybe this should just be a function +// that verified the refcount and potentially frees the backend +void vfs_backend_refdown(struct vfs_backend *, bool use); diff --git a/src/shared/include/camellia/errno.h b/src/shared/include/camellia/errno.h index c572f88..002980c 100644 --- a/src/shared/include/camellia/errno.h +++ b/src/shared/include/camellia/errno.h @@ -14,6 +14,7 @@ #define EACCES 10 #define EMFILE 11 /* all file descriptors taken */ #define ECONNRESET 12 +#define EPIPE 13 #define EISDIR 200 #define ENAMETOOLONG 201 diff --git a/src/user/app/ext2fs/main.c b/src/user/app/ext2fs/main.c index b4a4702..f6d65cc 100644 --- a/src/user/app/ext2fs/main.c +++ b/src/user/app/ext2fs/main.c @@ -230,6 +230,7 @@ main(int argc, char **argv) break; } } + warnx("cleaning up"); return 1; } diff --git a/src/user/app/init/init.c b/src/user/app/init/init.c index 0af0150..350d9a0 100644 --- a/src/user/app/init/init.c +++ b/src/user/app/init/init.c @@ -29,6 +29,7 @@ void redirect(const char *exe, const char *out, const char *in) { exit(1); } _syscall_await(); + _syscall_filicide(); } } } diff --git a/src/user/app/tests/kernel/fs.c b/src/user/app/tests/kernel/fs.c index ea8a827..6669c41 100644 --- a/src/user/app/tests/kernel/fs.c +++ b/src/user/app/tests/kernel/fs.c @@ -1,8 +1,8 @@ -// TODO test it working too... - #include "../tests.h" #include <camellia/flags.h> #include <camellia/syscalls.h> +#include <stdbool.h> +#include <string.h> static void test_unfinished_req(void) { handle_t h = -1; @@ -37,7 +37,43 @@ static void test_orphaned_fs(void) { } } +static void test_fs_cleanup(void) { + const char *msg = "success"; + int msglen = 8; + char buf[16]; + handle_t ends[2]; + test(_syscall_pipe(ends, 0) >= 0); + if (!_syscall_fork(0, NULL)) { + handle_t h = -1; + if (_syscall_fork(FORK_NEWFS, &h) == 0) { + for (;;) { + struct ufs_request req; + handle_t reqh = _syscall_fs_wait(buf, sizeof buf, &req); + if (reqh < 0) break; + _syscall_fs_respond(reqh, NULL, 0, 0); /* success */ + } + /* this is the test: does it break out of the loop + * when it should cleanup */ + _syscall_write(ends[1], msg, msglen, -1, 0); + exit(0); + } else { + test(_syscall_mount(h, "/", 1) == 0); + h = _syscall_open("/", 1, 0); + test(h >= 0); + _syscall_close(h); + // TODO another test without the delay + _syscall_sleep(0); + exit(0); + } + } else { + test(_syscall_read(ends[0], buf, sizeof buf, 0) == msglen); + test(memcmp(buf, msg, msglen) == 0); + } +} + void r_k_fs(void) { run_test(test_unfinished_req); run_test(test_orphaned_fs); + run_test(test_fs_cleanup); + run_test(test_fs_cleanup); } diff --git a/src/user/lib/include/__errno.h b/src/user/lib/include/__errno.h index 69113cb..559e588 100644 --- a/src/user/lib/include/__errno.h +++ b/src/user/lib/include/__errno.h @@ -12,6 +12,7 @@ E( 9, "ENOTEMPTY") E( 10, "EACCES") E( 11, "EMFILE all file descriptors taken") E( 12, "ECONNRESET") +E( 13, "EPIPE") E(200, "EISDIR") E(201, "ENAMETOOLONG") #endif |