summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordzwdz2023-01-11 19:35:44 +0100
committerdzwdz2023-01-11 19:37:46 +0100
commitec81fa16d837f430add92b4d2ee4bd3727ca6c6d (patch)
tree43c91abe8d6a76fcec006fe67c1b585573d3662f
parentc178ab5d5ca328d5b0072d54e3dc66d1b198df7b (diff)
kernel: return EPIPE when fs_waiting on a dead filesystem
-rw-r--r--src/kernel/handle.c2
-rwxr-xr-x[-rw-r--r--]src/kernel/proc.c20
-rw-r--r--src/kernel/syscalls.c13
-rw-r--r--src/kernel/vfs/mount.c16
-rw-r--r--src/kernel/vfs/procfs.c4
-rw-r--r--src/kernel/vfs/request.c48
-rw-r--r--src/kernel/vfs/request.h22
-rw-r--r--src/shared/include/camellia/errno.h1
-rw-r--r--src/user/app/ext2fs/main.c1
-rw-r--r--src/user/app/init/init.c1
-rw-r--r--src/user/app/tests/kernel/fs.c40
-rw-r--r--src/user/lib/include/__errno.h1
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