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);