changelog shortlog tags branches files raw gz bz2 help

Mercurial > hg > plan9front / changeset: upas/fs: rework the refcounting, use 64bit qid path, add checks

changeset 6900: fd0c1315d6ef
parent 6899: 0cedaa205af6
child 6901: 41b94427c45d
author: cinap_lenrek@felloff.net
date: Wed, 21 Nov 2018 00:37:35 +0100
files: sys/src/cmd/upas/fs/cache.c sys/src/cmd/upas/fs/dat.h sys/src/cmd/upas/fs/fs.c sys/src/cmd/upas/fs/idx.c sys/src/cmd/upas/fs/mbox.c sys/src/cmd/upas/fs/mkfile
description: upas/fs: rework the refcounting, use 64bit qid path, add checks

add function to check the refcounts for Mailbox and Message on a fid

use the full 64 bit of the qid.path, so we can use the full 32 bit for the id

instead of only maintaining refcount for the top message, msgincref() now
adds a reference to all its parent messages including self and top message.
then we can check in recursive delmessage() that all the parts have a zero
refcount.

remove the Fid.mtop field, it was never used.

make sure deletion and flag changes only affect the top message.

cachefree(): only look for top message in lru. sub-parts are never
added to the cache.

use the nparts field when reading sub-part of existing message, so
that we parse the index right in case the number of parts somehow
changed.

messages marked as Deleted but still in inbox should be written
to the index.
     1.1--- a/sys/src/cmd/upas/fs/cache.c
     1.2+++ b/sys/src/cmd/upas/fs/cache.c
     1.3@@ -31,17 +31,29 @@ notecache(Mailbox *mb, Message *m, long 
     1.4 	addlru(mb, m);
     1.5 }
     1.6 
     1.7-static void
     1.8-cachefree0(Mailbox *mb, Message *m, int force)
     1.9+void
    1.10+cachefree(Mailbox *mb, Message *m, int force)
    1.11 {
    1.12-	long sz, i;
    1.13-	Message *s;
    1.14+	long i;
    1.15+	Message *s, **ll;
    1.16 
    1.17 	if(!force && mb->fetch == nil)
    1.18 		return;
    1.19+	if(Topmsg(mb, m)){
    1.20+		for(ll = &mb->lru; *ll != nil; ll = &((*ll)->lru)){
    1.21+			if(*ll == m){
    1.22+				mb->nlru--;
    1.23+				*ll = m->lru;
    1.24+				m->lru = nil;
    1.25+				break;
    1.26+			}
    1.27+		}
    1.28+		if(mb->decache)
    1.29+			mb->decache(mb, m);
    1.30+		mb->cached -= m->csize;
    1.31+	}
    1.32 	for(s = m->part; s; s = s->next)
    1.33 		cachefree(mb, s, force);
    1.34-	dprint("cachefree: %D	%p,	%p\n", m->fileid, m, m->start);
    1.35 	if(m->mallocd){
    1.36 		free(m->start);
    1.37 		m->mallocd = 0;
    1.38@@ -58,7 +70,6 @@ cachefree0(Mailbox *mb, Message *m, int 
    1.39 		free(m->references[i]);
    1.40 		m->references[i] = 0;
    1.41 	}
    1.42-	sz = m->csize;
    1.43 	m->csize = 0;
    1.44 	m->start = 0;
    1.45 	m->end = 0;
    1.46@@ -69,30 +80,10 @@ cachefree0(Mailbox *mb, Message *m, int 
    1.47 	m->bend = 0;
    1.48 	m->mheader = 0;
    1.49 	m->mhend = 0;
    1.50-	if(mb->decache)
    1.51-		mb->decache(mb, m);
    1.52 	m->decoded = 0;
    1.53 	m->converted = 0;
    1.54 	m->badchars = 0;
    1.55 	m->cstate &= ~(Cheader|Cbody);
    1.56-	if(Topmsg(mb, m))
    1.57-		mb->cached -= sz;
    1.58-}
    1.59-
    1.60-void
    1.61-cachefree(Mailbox *mb, Message *m, int force)
    1.62-{
    1.63-	Message **ll;
    1.64-
    1.65-	for(ll = &mb->lru; *ll != nil; ll = &((*ll)->lru)){
    1.66-		if(*ll == m){
    1.67-			mb->nlru--;
    1.68-			*ll = m->lru;
    1.69-			m->lru = nil;
    1.70-			break;
    1.71-		}
    1.72-	}
    1.73-	cachefree0(mb, m, force);
    1.74 }
    1.75 
    1.76 void
    1.77@@ -277,7 +268,8 @@ middlecache(Mailbox *mb, Message *m)
    1.78 	}
    1.79 	if(y == 0)
    1.80 		return 0;
    1.81-	dprint("middlecache %d [%D] %lud %lud\n", m->id, m->fileid, (ulong)(m->end - m->start), m->size);
    1.82+	dprint("middlecache %lud [%D] %lud %lud\n",
    1.83+		m->id, m->fileid, (ulong)(m->end - m->start), m->size);
    1.84 	return cachebody(mb, m);
    1.85 }
    1.86 
    1.87@@ -292,7 +284,7 @@ cacheheaders(Mailbox *mb, Message *m)
    1.88 		return 0;
    1.89 	if(!Topmsg(mb, m))
    1.90 		return middlecache(mb, m);
    1.91-	dprint("cacheheaders %d %D\n", m->id, m->fileid);
    1.92+	dprint("cacheheaders %lud %D\n", m->id, m->fileid);
    1.93 	if(m->size < 10000)
    1.94 		r = fetch(mb, m, 0, m->size);
    1.95 	else for(r = 0; (o = m->end - m->start) < m->size; ){
    1.96@@ -327,7 +319,7 @@ digestmessage(Mailbox *mb, Message *m)
    1.97 		m->deleted = Dup;	/* no dups allowed */
    1.98 	}else
    1.99 		mtreeadd(mb, m);
   1.100-	dprint("%d %#A\n", m->id, m->digest);
   1.101+	dprint("%lud %#A\n", m->id, m->digest);
   1.102 }
   1.103 
   1.104 int
   1.105@@ -337,10 +329,10 @@ cachebody(Mailbox *mb, Message *m)
   1.106 
   1.107 	while(!Topmsg(mb, m))
   1.108 		m = m->whole;
   1.109-	if(!mb->fetch || m->cstate&Cbody)
   1.110+	if(mb->fetch == nil || m->cstate&Cbody)
   1.111 		return 0;
   1.112 	o = m->end - m->start;
   1.113-	dprint("cachebody %d [%D] %lud %lud %s", m->id, m->fileid, o, m->size, cstate(m));
   1.114+	dprint("cachebody %lud [%D] %lud %lud %s", m->id, m->fileid, o, m->size, cstate(m));
   1.115 	if(o < m->size)
   1.116 	if(fetch(mb, m, o, m->size - o) < 0)
   1.117 		return -1;
   1.118@@ -389,7 +381,7 @@ insurecache(Mailbox *mb, Message *m)
   1.119 {
   1.120 	if(m->deleted || !m->inmbox)
   1.121 		return -1;
   1.122-	msgincref(m);
   1.123+	msgincref(mb, m);
   1.124 	cacheidx(mb, m);
   1.125 	if((m->cstate & Cidx) == 0){
   1.126 		logmsg(m, "%s: can't cache: %s: %r", mb->path, m->name);
     2.1--- a/sys/src/cmd/upas/fs/dat.h
     2.2+++ b/sys/src/cmd/upas/fs/dat.h
     2.3@@ -74,7 +74,7 @@ struct Idx {
     2.4 
     2.5 typedef struct Message Message;
     2.6 struct Message {
     2.7-	int	id;
     2.8+	ulong	id;
     2.9 	int	refs;
    2.10 	int	subname;
    2.11 	char	name[12];
    2.12@@ -152,7 +152,7 @@ typedef struct Mailbox Mailbox;
    2.13 struct Mailbox {
    2.14 	int	refs;
    2.15 	Mailbox	*next;
    2.16-	int	id;
    2.17+	ulong	id;
    2.18 	int	flags;
    2.19 	char	rmflags;
    2.20 	char	dolock;		/* lock when syncing? */
    2.21@@ -209,7 +209,6 @@ int		insurecache(Mailbox*, Message*);
    2.22 void		putcache(Mailbox*, Message*);		/* asymmetricial */
    2.23 void		cachefree(Mailbox*, Message*, int);
    2.24 
    2.25-Message*	gettopmsg(Mailbox*, Message*);
    2.26 char*		syncmbox(Mailbox*, int);
    2.27 void*		emalloc(ulong);
    2.28 void*		erealloc(void*, ulong);
    2.29@@ -223,13 +222,12 @@ int		wraptls(int, char*);
    2.30 
    2.31 void		eprint(char*, ...);
    2.32 void		iprint(char *, ...);
    2.33-int		newid(void);
    2.34 char*		newmbox(char*, char*, int, Mailbox**);
    2.35 void		freembox(char*);
    2.36 char*		removembox(char*, int);
    2.37 void		syncallmboxes(void);
    2.38 void		logmsg(Message*, char*, ...);
    2.39-void		msgincref(Message*);
    2.40+void		msgincref(Mailbox*, Message*);
    2.41 void		msgdecref(Mailbox*, Message*);
    2.42 void		mboxincref(Mailbox*);
    2.43 void		mboxdecref(Mailbox*);
    2.44@@ -306,7 +304,7 @@ enum {
    2.45 	Qmboxctl,
    2.46 };
    2.47 
    2.48-#define PATH(id, f)	((((id) & 0xfffff)<<10) | (f))
    2.49+#define PATH(id, f)	(((uvlong)(id)<<10) | (f))
    2.50 #define FILE(p)		((p) & 0x3ff)
    2.51 
    2.52 /* hash table to aid in name lookup, all files have an entry */
    2.53@@ -314,16 +312,16 @@ typedef struct Hash Hash;
    2.54 struct Hash {
    2.55 	Hash	*next;
    2.56 	char	*name;
    2.57-	ulong	ppath;
    2.58+	uvlong	ppath;
    2.59 	Qid	qid;
    2.60 	Mailbox	*mb;
    2.61 	Message	*m;
    2.62 };
    2.63 
    2.64-uint	hash(char*);
    2.65-Hash	*hlook(ulong, char*);
    2.66-void	henter(ulong, char*, Qid, Message*, Mailbox*);
    2.67-void	hfree(ulong, char*);
    2.68+ulong	hash(char*);
    2.69+Hash	*hlook(uvlong, char*);
    2.70+void	henter(uvlong, char*, Qid, Message*, Mailbox*);
    2.71+void	hfree(uvlong, char*);
    2.72 
    2.73 char	*intern(char*);
    2.74 void	idxfree(Idx*);
     3.1--- a/sys/src/cmd/upas/fs/fs.c
     3.2+++ b/sys/src/cmd/upas/fs/fs.c
     3.3@@ -14,7 +14,6 @@ struct Fid
     3.4 	Fid	*next;
     3.5 	Mailbox	*mb;
     3.6 	Message	*m;
     3.7-	Message *mtop;		/* top level message */
     3.8 
     3.9 	long	foff;		/* offset/DIRLEN of finger */
    3.10 	Message	*fptr;		/* pointer to message at off */
    3.11@@ -132,8 +131,6 @@ static QLock	synclock;
    3.12 void
    3.13 sanemsg(Message *m)
    3.14 {
    3.15-	assert(m->refs < 100);
    3.16-	assert(m->next != m);
    3.17 	if(m->end < m->start)
    3.18 		abort();
    3.19 	if(m->ballocd && (m->start <= m->body && m->end >= m->body))
    3.20@@ -769,13 +766,10 @@ doclone(Fid *f, int nfid)
    3.21 		return nil;
    3.22 	nf->busy = 1;
    3.23 	nf->open = 0;
    3.24-	nf->mb = f->mb;
    3.25-	if(nf->mb)
    3.26+	if(nf->mb = f->mb)
    3.27 		mboxincref(nf->mb);
    3.28 	if(nf->m = f->m)
    3.29-		msgincref(gettopmsg(nf->mb, nf->m));
    3.30-	if(nf->mtop = f->mtop)
    3.31-		msgincref(nf->mtop);
    3.32+		msgincref(nf->mb, nf->m);
    3.33 	nf->qid = f->qid;
    3.34 	return nf;
    3.35 }
    3.36@@ -798,9 +792,10 @@ dowalk(Fid *f, char *name)
    3.37 {
    3.38 	char *rv, *p;
    3.39 	int t, t1;
    3.40-	Mailbox *mb;
    3.41 	Hash *h;
    3.42 
    3.43+	if(f->qid.type != QTDIR)
    3.44+		return Enotdir;
    3.45 	t = FILE(f->qid.path);
    3.46 	rv = Enotexist;
    3.47 
    3.48@@ -815,26 +810,16 @@ retry:
    3.49 	}else
    3.50 		h = hlook(f->qid.path, name);
    3.51 	if(h != nil){
    3.52-		if(f->m)
    3.53-			msgdecref(f->mb, gettopmsg(f->mb, f->m));
    3.54-		if(f->mb && f->mb != h->mb)
    3.55-			mboxdecref(f->mb);
    3.56-		f->mb = h->mb;
    3.57-		f->m = h->m;
    3.58+		if(h->mb)
    3.59+			mboxincref(h->mb);
    3.60+		if(h->m)
    3.61+			msgincref(h->mb, h->m);
    3.62 		if(f->m)
    3.63-			msgincref(gettopmsg(f->mb, f->m));
    3.64-		switch(t){
    3.65-		case Qtop:
    3.66-			if(f->mb)
    3.67-				mboxincref(f->mb);
    3.68-			break;
    3.69-		case Qmbox:
    3.70-			if(f->m){
    3.71-				msgincref(f->m);
    3.72-				f->mtop = f->m;
    3.73-			}
    3.74-			break;
    3.75-		}
    3.76+			msgdecref(f->mb, f->m);
    3.77+		if(f->mb)
    3.78+			mboxdecref(f->mb);
    3.79+		f->m = h->m;
    3.80+		f->mb = h->mb;
    3.81 		f->qid = h->qid;
    3.82 		if(t1 < Qmax)
    3.83 			f->qid.path = PATH(f->m->id, t1);	/* sleezy speedup */
    3.84@@ -842,18 +827,9 @@ retry:
    3.85 	}else if((p = strchr(name, '.')) != nil && *name != '.'){
    3.86 		*p = 0;
    3.87 		goto retry;
    3.88-	}
    3.89-
    3.90-	if(rv == nil)
    3.91-		return rv;
    3.92-
    3.93-	if(strcmp(name, ".") == 0)
    3.94-		return nil;
    3.95-
    3.96-	if(f->qid.type != QTDIR)
    3.97-		return Enotdir;
    3.98-
    3.99-	if(strcmp(name, "..") == 0){
   3.100+	} else if(strcmp(name, ".") == 0){
   3.101+		rv = nil;
   3.102+	} else if(strcmp(name, "..") == 0){
   3.103 		switch(t){
   3.104 		case Qtop:
   3.105 			f->qid.path = PATH(0, Qtop);
   3.106@@ -864,20 +840,19 @@ retry:
   3.107 			f->qid.path = PATH(0, Qtop);
   3.108 			f->qid.type = QTDIR;
   3.109 			f->qid.vers = 0;
   3.110-			mb = f->mb;
   3.111+			mboxdecref(f->mb);
   3.112 			f->mb = nil;
   3.113-			mboxdecref(mb);
   3.114 			break;
   3.115 		case Qdir:
   3.116 			if(Topmsg(f->mb, f->m)){
   3.117 				f->qid.path = PATH(f->mb->id, Qmbox);
   3.118 				f->qid.type = QTDIR;
   3.119 				f->qid.vers = f->mb->vers;
   3.120-				msgdecref(f->mb, f->mtop);
   3.121 				msgdecref(f->mb, f->m);
   3.122-				f->m = f->mtop = nil;
   3.123+				f->m = nil;
   3.124 			} else {
   3.125-				/* refs don't change; still the same message */
   3.126+				msgincref(f->mb, f->m->whole);
   3.127+				msgdecref(f->mb, f->m);
   3.128 				f->m = f->m->whole;
   3.129 				f->qid.path = PATH(f->m->id, Qdir);
   3.130 				f->qid.type = QTDIR;
   3.131@@ -987,6 +962,8 @@ readtopdir(Fid*, uchar *buf, long off, i
   3.132 	pos += m;
   3.133 		
   3.134 	for(mb = mbl; mb != nil; mb = mb->next){
   3.135+		assert(mb->refs > 0);
   3.136+
   3.137 		mkstat(&d, mb, nil, Qmbox);
   3.138 		m = convD2M(&d, &buf[n], blen - n);
   3.139 		if(off <= pos){
   3.140@@ -1008,6 +985,8 @@ readmboxdir(Fid *f, uchar *buf, long off
   3.141 	long pos;
   3.142 	Message *msg;
   3.143 
   3.144+	assert(f->mb->refs > 0);
   3.145+
   3.146 	if(off == 0)
   3.147 		syncmbox(f->mb, 1);
   3.148 
   3.149@@ -1198,7 +1177,6 @@ rwrite(Fid *f)
   3.150 {
   3.151 	char *argvbuf[1024], **argv, file[Pathlen], *err, *v0;
   3.152 	int i, t, argc, flags;
   3.153-	Message *m;
   3.154 
   3.155 	t = FILE(f->qid.path);
   3.156 	rhdr.count = thdr.count;
   3.157@@ -1288,21 +1266,19 @@ rwrite(Fid *f)
   3.158 		}
   3.159 		return Ebadctl;
   3.160 	case Qmboxctl:
   3.161-		if(f->mb && f->mb->ctl){
   3.162-			argc = tokenize(thdr.data, argv, nelem(argvbuf));
   3.163-			if(argc == 0)
   3.164-				return Ebadctl;
   3.165-			return f->mb->ctl(f->mb, argc, argv);
   3.166-		}
   3.167-		break;
   3.168+		if(f->mb->ctl == nil)
   3.169+			break;
   3.170+		argc = tokenize(thdr.data, argv, nelem(argvbuf));
   3.171+		if(argc == 0)
   3.172+			return Ebadctl;
   3.173+		return f->mb->ctl(f->mb, argc, argv);
   3.174 	case Qflags:
   3.175 		/*
   3.176 		 * modifying flags on subparts is a little strange.
   3.177 		 */
   3.178-		if(!f->mb || !f->m)
   3.179+		if(!Topmsg(f->mb, f->m))
   3.180 			break;
   3.181-		m = gettopmsg(f->mb, f->m);
   3.182-		return modflags(f->mb, m, thdr.data);
   3.183+		return modflags(f->mb, f->m, thdr.data);
   3.184 	}
   3.185 	return Eperm;
   3.186 }
   3.187@@ -1310,21 +1286,17 @@ rwrite(Fid *f)
   3.188 char*
   3.189 rclunk(Fid *f)
   3.190 {
   3.191-	Mailbox *mb;
   3.192-
   3.193 	f->busy = 1;
   3.194 	/* coherence(); */
   3.195 	f->fid = -1;
   3.196 	f->open = 0;
   3.197-	mb = f->mb;
   3.198-	if(f->mtop)
   3.199-		msgdecref(mb, f->mtop);
   3.200-	if(f->m)
   3.201-		msgdecref(mb, gettopmsg(mb, f->m));
   3.202-	f->m = f->mtop = nil;
   3.203-	if(mb){
   3.204+	if(f->m != nil){
   3.205+		msgdecref(f->mb, f->m);
   3.206+		f->m = nil;
   3.207+	}
   3.208+	if(f->mb != nil){
   3.209+		mboxdecref(f->mb);
   3.210 		f->mb = nil;
   3.211-		mboxdecref(mb);
   3.212 	}
   3.213 	f->busy = 0;
   3.214 	return 0;
   3.215@@ -1333,7 +1305,7 @@ rclunk(Fid *f)
   3.216 char *
   3.217 rremove(Fid *f)
   3.218 {
   3.219-	if(f->m != nil && f->m->deleted == 0)
   3.220+	if(f->mb != nil && f->m != nil && Topmsg(f->mb, f->m) && f->m->deleted == 0)
   3.221 		f->m->deleted = Deleted;
   3.222 	return rclunk(f);
   3.223 }
   3.224@@ -1357,6 +1329,28 @@ rwstat(Fid*)
   3.225 	return Eperm;
   3.226 }
   3.227 
   3.228+static Fid*
   3.229+checkfid(Fid *f)
   3.230+{
   3.231+	switch(FILE(f->qid.path)){
   3.232+	case Qtop:
   3.233+	case Qctl:
   3.234+		assert(f->mb == nil);
   3.235+		assert(f->m == nil);
   3.236+		break;
   3.237+	case Qmbox:
   3.238+	case Qmboxctl:
   3.239+		assert(f->mb != nil && f->mb->refs > 0);
   3.240+		assert(f->m == nil);
   3.241+		break;
   3.242+	default:
   3.243+		assert(f->mb != nil && f->mb->refs > 0);
   3.244+		assert(f->m != nil && f->m->refs > 0);
   3.245+		break;
   3.246+	}
   3.247+	return f;
   3.248+}
   3.249+
   3.250 Fid*
   3.251 newfid(int fid)
   3.252 {
   3.253@@ -1365,7 +1359,7 @@ newfid(int fid)
   3.254 	ff = 0;
   3.255 	for(f = fids; f; f = f->next)
   3.256 		if(f->fid == fid)
   3.257-			return f;
   3.258+			return checkfid(f);
   3.259 		else if(!ff && !f->busy)
   3.260 			ff = f;
   3.261 	if(ff){
   3.262@@ -1475,20 +1469,6 @@ reader(void)
   3.263 	}
   3.264 }
   3.265 
   3.266-int
   3.267-newid(void)
   3.268-{
   3.269-	int rv;
   3.270-	static int id;
   3.271-	static Lock idlock;
   3.272-
   3.273-	lock(&idlock);
   3.274-	rv = ++id;
   3.275-	unlock(&idlock);
   3.276-
   3.277-	return rv;
   3.278-}
   3.279-
   3.280 void
   3.281 error(char *s)
   3.282 {
   3.283@@ -1600,10 +1580,10 @@ readheader(Message *m, char *buf, int of
   3.284 	return to - buf;
   3.285 }
   3.286 
   3.287-uint
   3.288+ulong
   3.289 hash(char *s)
   3.290 {
   3.291-	uint c, h;
   3.292+	ulong c, h;
   3.293 
   3.294 	h = 0;
   3.295 	while(c = *s++)
   3.296@@ -1613,9 +1593,9 @@ hash(char *s)
   3.297 }
   3.298 
   3.299 Hash*
   3.300-hlook(ulong ppath, char *name)
   3.301+hlook(uvlong ppath, char *name)
   3.302 {
   3.303-	int h;
   3.304+	ulong h;
   3.305 	Hash *hp;
   3.306 
   3.307 	h = (hash(name)+ppath) % nelem(htab);
   3.308@@ -1626,9 +1606,9 @@ hlook(ulong ppath, char *name)
   3.309 }
   3.310 
   3.311 void
   3.312-henter(ulong ppath, char *name, Qid qid, Message *m, Mailbox *mb)
   3.313+henter(uvlong ppath, char *name, Qid qid, Message *m, Mailbox *mb)
   3.314 {
   3.315-	int h;
   3.316+	ulong h;
   3.317 	Hash *hp, **l;
   3.318 
   3.319 	h = (hash(name)+ppath) % nelem(htab);
   3.320@@ -1650,9 +1630,9 @@ henter(ulong ppath, char *name, Qid qid,
   3.321 }
   3.322 
   3.323 void
   3.324-hfree(ulong ppath, char *name)
   3.325+hfree(uvlong ppath, char *name)
   3.326 {
   3.327-	int h;
   3.328+	ulong h;
   3.329 	Hash *hp, **l;
   3.330 
   3.331 	h = (hash(name)+ppath) % nelem(htab);
     4.1--- a/sys/src/cmd/upas/fs/idx.c
     4.2+++ b/sys/src/cmd/upas/fs/idx.c
     4.3@@ -366,18 +366,20 @@ dead:
     4.4 			/*
     4.5 			 * read in mutable information.
     4.6 			 * currently this is only flags
     4.7+			 * and nparts.
     4.8 			 */
     4.9 			redux++;
    4.10 			if(level == 0)
    4.11 				m->deleted &= ~Dmark;
    4.12-			if(m->nparts)
    4.13+			n = m->nparts;
    4.14+			m->nparts = strtoul(f[21], 0, 0);
    4.15 			if(rdidx(b, mb, m, m->nparts, level + 1) == -1)
    4.16 				goto dead;
    4.17 			ll = &m->next;
    4.18-			idprint("%d seen before %d... %.2ux", level, m->id, m->cstate);
    4.19+			idprint("%d seen before %lud... %.2ux", level, m->id, m->cstate);
    4.20 			flags = m->flags;
    4.21 			m->flags |= strtoul(f[1], 0, 16);
    4.22-			if(flags != m->flags)
    4.23+			if(flags != m->flags || n != m->nparts)
    4.24 				m->cstate |= Cidxstale;
    4.25 			m->cstate |= Cidx;
    4.26 			idprint("→%.2ux\n", m->cstate);
    4.27@@ -385,7 +387,7 @@ dead:
    4.28 			continue;
    4.29 		}
    4.30 		m = newmessage(parent);
    4.31-		idprint("%d new %d %#A\n", level, m->id, digest);
    4.32+		idprint("%d new %lud %#A\n", level, m->id, digest);
    4.33 		m->digest = digest;
    4.34 		m->flags = strtoul(f[1], 0, 16);
    4.35 		m->fileid = rdfileid(f[2], level);
     5.1--- a/sys/src/cmd/upas/fs/mbox.c
     5.2+++ b/sys/src/cmd/upas/fs/mbox.c
     5.3@@ -191,6 +191,20 @@ initheaders(void)
     5.4 		head[i].len = strlen(head[i].type);
     5.5 }
     5.6 
     5.7+static ulong
     5.8+newid(void)
     5.9+{
    5.10+	ulong rv;
    5.11+	static ulong id;
    5.12+	static Lock idlock;
    5.13+
    5.14+	lock(&idlock);
    5.15+	rv = ++id;
    5.16+	unlock(&idlock);
    5.17+
    5.18+	return rv;
    5.19+}
    5.20+
    5.21 char*
    5.22 newmbox(char *path, char *name, int flags, Mailbox **r)
    5.23 {
    5.24@@ -271,50 +285,42 @@ freembox(char *name)
    5.25 {
    5.26 	Mailbox **l, *mb;
    5.27 
    5.28-	for(l=&mbl; *l != nil; l=&(*l)->next)
    5.29-		if(strcmp(name, (*l)->name) == 0){
    5.30-			mb = *l;
    5.31+	for(l = &mbl; (mb = *l) != nil; l = &mb->next)
    5.32+		if(strcmp(name, mb->name) == 0){
    5.33 			*l = mb->next;
    5.34+			mb->next = nil;
    5.35+			hfree(PATH(0, Qtop), mb->name);
    5.36+			if(mb->ctl)
    5.37+				hfree(PATH(mb->id, Qmbox), "ctl");
    5.38 			mboxdecref(mb);
    5.39 			break;
    5.40 		}
    5.41-	hfree(PATH(0, Qtop), name);
    5.42+}
    5.43+
    5.44+char*
    5.45+removembox(char *name, int flags)
    5.46+{
    5.47+	Mailbox *mb;
    5.48+
    5.49+	for(mb = mbl; mb != nil; mb = mb->next)
    5.50+		if(strcmp(name, mb->path) == 0){
    5.51+			mb->flags |= ORCLOSE;
    5.52+			mb->rmflags = flags;
    5.53+			freembox(mb->name);
    5.54+			return 0;
    5.55+		}
    5.56+	return "maibox not found";
    5.57 }
    5.58 
    5.59 void
    5.60 syncallmboxes(void)
    5.61 {
    5.62+	Mailbox *mb;
    5.63 	char *err;
    5.64-	Mailbox *m;
    5.65-
    5.66-	for(m = mbl; m != nil; m = m->next)
    5.67-		if(err = syncmbox(m, 0))
    5.68-			eprint("syncmbox: %s\n", err);
    5.69-}
    5.70-
    5.71-
    5.72-char*
    5.73-removembox(char *name, int flags)
    5.74-{
    5.75-	int found;
    5.76-	Mailbox **l, *mb;
    5.77 
    5.78-	found = 0;
    5.79-	for(l=&mbl; *l != nil; l=&(*l)->next)
    5.80-		if(strcmp(name, (*l)->path) == 0){
    5.81-			mb = *l;
    5.82-			*l = mb->next;
    5.83-			mb->flags |= ORCLOSE;
    5.84-			mb->rmflags = flags;
    5.85-			mboxdecref(mb);
    5.86-			found = 1;
    5.87-			break;
    5.88-		}
    5.89-	hfree(PATH(0, Qtop), name);
    5.90-
    5.91-	if(found == 0)
    5.92-		return "maibox not found";
    5.93-	return 0;
    5.94+	for(mb = mbl; mb != nil; mb = mb->next)
    5.95+		if(err = syncmbox(mb, 0))
    5.96+			eprint("syncmbox: %s\n", err);
    5.97 }
    5.98 
    5.99 /*
   5.100@@ -347,7 +353,7 @@ rxtotm(Message *m, Tm *tm)
   5.101 	return r;
   5.102 }
   5.103 
   5.104-Message*
   5.105+static Message*
   5.106 gettopmsg(Mailbox *mb, Message *m)
   5.107 {
   5.108 	while(!Topmsg(mb, m))
   5.109@@ -355,7 +361,7 @@ gettopmsg(Mailbox *mb, Message *m)
   5.110 	return m;
   5.111 }
   5.112 
   5.113-void
   5.114+static void
   5.115 datesec(Mailbox *mb, Message *m)
   5.116 {
   5.117 	vlong v;
   5.118@@ -1011,31 +1017,32 @@ delmessage(Mailbox *mb, Message *m)
   5.119 {
   5.120 	Message **l;
   5.121 
   5.122+	assert(m->refs == 0);
   5.123+	while(m->part)
   5.124+		delmessage(mb, m->part);
   5.125+
   5.126 	mb->vers++;
   5.127 	msgfreed++;
   5.128 
   5.129-	if(m->whole != m){
   5.130+	if(m != m->whole){
   5.131 		/* unchain from parent */
   5.132 		for(l = &m->whole->part; *l && *l != m; l = &(*l)->next)
   5.133 			;
   5.134 		if(*l != nil)
   5.135 			*l = m->next;
   5.136-
   5.137+		m->next = nil;
   5.138 		/* clear out of name lookup hash table */
   5.139 		if(m->whole->whole == m->whole)
   5.140 			hfree(PATH(mb->id, Qmbox), m->name);
   5.141 		else
   5.142 			hfree(PATH(m->whole->id, Qdir), m->name);
   5.143 		hfree(PATH(m->id, Qdir), "xxx");		/* sleezy speedup */
   5.144-	}
   5.145-	if(Topmsg(mb, m)){
   5.146-		if(m != mb->root)
   5.147+
   5.148+		if(Topmsg(mb, m))
   5.149 			mtreedelete(mb, m);
   5.150 		cachefree(mb, m, 1);
   5.151+		idxfree(m);
   5.152 	}
   5.153-	idxfree(m);
   5.154-	while(m->part)
   5.155-		delmessage(mb, m->part);
   5.156 	free(m->unixfrom);
   5.157 	free(m->unixheader);
   5.158 	free(m->date822);
   5.159@@ -1093,7 +1100,7 @@ flagmessages(int argc, char **argv)
   5.160 
   5.161 	if(argc%2)
   5.162 		return "bad flags";
   5.163-	for(mb = mbl; mb; mb = mb->next)
   5.164+	for(mb = mbl; mb != nil; mb = mb->next)
   5.165 		if(strcmp(*argv, mb->name) == 0)
   5.166 			break;
   5.167 	if(mb == nil)
   5.168@@ -1114,21 +1121,33 @@ flagmessages(int argc, char **argv)
   5.169 }
   5.170 
   5.171 void
   5.172-msgincref(Message *m)
   5.173+msgincref(Mailbox *mb, Message *m)
   5.174 {
   5.175-	m->refs++;
   5.176+	assert(mb->refs > 0);
   5.177+	for(;; m = m->whole){
   5.178+		assert(m->refs >= 0);
   5.179+		m->refs++;
   5.180+		if(Topmsg(mb, m))
   5.181+			break;
   5.182+	}
   5.183 }
   5.184 
   5.185 void
   5.186 msgdecref(Mailbox *mb, Message *m)
   5.187 {
   5.188-	assert(m->refs > 0);
   5.189-	m->refs--;
   5.190-	if(m->refs == 0){
   5.191-		if(m->deleted)
   5.192-			syncmbox(mb, 1);
   5.193-		else
   5.194-			putcache(mb, m);
   5.195+	assert(mb->refs >= 0);
   5.196+	for(;; m = m->whole){
   5.197+		assert(m->refs > 0);
   5.198+		m->refs--;
   5.199+		if(Topmsg(mb, m)){
   5.200+			if(m->refs == 0){
   5.201+				if(m->deleted)
   5.202+					syncmbox(mb, 1);
   5.203+				else
   5.204+					putcache(mb, m);
   5.205+			}
   5.206+			break;
   5.207+		}
   5.208 	}
   5.209 }
   5.210 
   5.211@@ -1156,21 +1175,18 @@ void
   5.212 mboxdecref(Mailbox *mb)
   5.213 {
   5.214 	assert(mb->refs > 0);
   5.215-	mb->refs--;
   5.216-	if(mb->refs == 0){
   5.217-		syncmbox(mb, 1);
   5.218-		delmessage(mb, mb->root);
   5.219-		if(mb->ctl)
   5.220-			hfree(PATH(mb->id, Qmbox), "ctl");
   5.221-		if(mb->close)
   5.222-			mb->close(mb);
   5.223-		if(mb->flags & ORCLOSE && mb->remove)
   5.224-		if(mb->remove(mb, mb->rmflags))
   5.225-			rmidx(mb->path, mb->rmflags);
   5.226-		free(mb->mtree);
   5.227-		free(mb->d);
   5.228-		free(mb);
   5.229-	}
   5.230+	if(--mb->refs)
   5.231+		return;
   5.232+	syncmbox(mb, 1);
   5.233+	delmessage(mb, mb->root);
   5.234+	if(mb->close)
   5.235+		mb->close(mb);
   5.236+	if(mb->flags & ORCLOSE && mb->remove)
   5.237+	if(mb->remove(mb, mb->rmflags))
   5.238+		rmidx(mb->path, mb->rmflags);
   5.239+	free(mb->mtree);
   5.240+	free(mb->d);
   5.241+	free(mb);
   5.242 }
   5.243 
   5.244 
     6.1--- a/sys/src/cmd/upas/fs/mkfile
     6.2+++ b/sys/src/cmd/upas/fs/mkfile
     6.3@@ -1,7 +1,7 @@
     6.4 </$objtype/mkfile
     6.5 <../mkupas
     6.6 
     6.7-TARG=	fs\
     6.8+TARG=fs
     6.9 
    6.10 OFILES=\
    6.11 	cache.$O\