shithub: gefs

Download patch

ref: 2a05dca22fba164e7549e359771aa26d88a7018a
parent: 6ce7fa270782c91c5f829ed1cae2f6aaef1f9d34
author: Ori Bernstein <ori@eigenstate.org>
date: Sun May 12 11:09:46 EDT 2024

fs: fix race between walk and remove

we need a double check that the file is in the tree
because the walk to the fid is done in a reader proc
that can look it up in a stale version of the tree,
while we clunk the dent in the mutator proc.

--- a/fs.c
+++ b/fs.c
@@ -1668,9 +1668,11 @@
 static void
 fsremove(Fmsg *m, int id, Amsg **ao)
 {
-	char *e, upbuf[Upksz];
+	char *e, buf[Kvmax];
 	Fcall r;
 	Msg mb[2];
+	Tree *t;
+	Kvp kv;
 	Fid *f;
 
 	if((f = getfid(m->conn, m->fid)) == nil){
@@ -1677,6 +1679,7 @@
 		rerror(m, Enofid);
 		return;
 	}
+	t = f->mnt->root;
 	clunkfid(m->conn, f);
 
 	truncwait(f->dent, id);
@@ -1690,6 +1693,17 @@
 	}
 	if(f->dent->gone)
 		error(Ephase);
+	/*
+	 * we need a double check that the file is in the tree
+	 * here, because the walk to the fid is done in a reader
+	 * proc that can look it up in a stale version of the
+	 * tree, while we clunk the dent in the mutator proc.
+	 *
+	 * this means we can theoretically get some deletions
+	 * of files that are already gone.
+	 */
+	if(!btlookup(t, &f->dent->Key, &kv, buf, sizeof(buf)))
+		error(Ephase);
 	if((e = candelete(f)) != nil)
 		error(e);
 	if(fsaccess(f, f->dmode, f->duid, f->dgid, DMWRITE) == -1)
@@ -1700,9 +1714,9 @@
 	mb[0].nv = 0;
 
 	if(f->dent->qid.type & QTDIR){
-		packsuper(upbuf, sizeof(upbuf), f->qpath);
+		packsuper(buf, sizeof(buf), f->qpath);
 		mb[1].op = Oclobber;
-		mb[1].k = upbuf;
+		mb[1].k = buf;
 		mb[1].nk = Upksz;
 		mb[1].nv = 0;
 		upsert(f->mnt, mb, 2);