From ec81fa16d837f430add92b4d2ee4bd3727ca6c6d Mon Sep 17 00:00:00 2001
From: dzwdz
Date: Wed, 11 Jan 2023 19:35:44 +0100
Subject: kernel: return EPIPE when fs_waiting on a dead filesystem

---
 src/kernel/vfs/mount.c   | 16 ++++++++++------
 src/kernel/vfs/procfs.c  |  4 ++--
 src/kernel/vfs/request.c | 48 +++++++++++++++++++++++++++++++++++++-----------
 src/kernel/vfs/request.h | 22 +++++++++++++---------
 4 files changed, 62 insertions(+), 28 deletions(-)

(limited to 'src/kernel/vfs')

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);
-- 
cgit v1.2.3