shithub: gefs

Download patch

ref: 5b0200652a0fccf9b4eeeb518dbdb698f9bda71f
parent: ef7604325ba923d67906222b2130f76e067b7fbb
author: Ori Bernstein <ori@eigenstate.org>
date: Wed Nov 22 01:13:43 EST 2023

fs: do the rest of the error handling conversion

--- a/blk.c
+++ b/blk.c
@@ -1076,6 +1076,7 @@
 				syncblk(qe.b);
 			dropblk(qe.b);
 		}
+		assert(estacksz() == 0);
 	}
 }
 
--- a/check.c
+++ b/check.c
@@ -181,7 +181,7 @@
 checkfs(int fd)
 {
 	int ok, height;
-	char *e, pfx[1], name[Keymax+1];
+	char pfx[1], name[Keymax+1];
 	Tree *t;
 	Scan s;
 	Blk *b;
@@ -198,17 +198,9 @@
 	}
 	pfx[0] = Klabel;
 	btnewscan(&s, pfx, 1);
-	if((e = btenter(&fs->snap, &s)) != nil){
-		fprint(fd, "scanning snapshots: %s\n", e);
-		return 0;
-	}
+	btenter(&fs->snap, &s);
 	while(1){
-		if((e = btnext(&s, &s.kv)) != nil){
-			fprint(fd, "invalid snapshot tree: %s\n", e);
-			ok = 0;
-			break;
-		}
-		if(s.done)
+		if(!btnext(&s, &s.kv))
 			break;
 		memcpy(name, s.kv.k+1, s.kv.nk-1);
 		name[s.kv.nk-1] = 0;
--- a/cons.c
+++ b/cons.c
@@ -103,7 +103,6 @@
 static void
 refreshusers(int fd, char **, int)
 {
-	char *e;
 	Mount *mnt;
 
 	if((mnt = getmount("adm")) == nil){
@@ -110,11 +109,13 @@
 		fprint(fd, "load users: missing 'adm'\n");
 		return;
 	}
-	e = loadusers(fd, mnt->root);
-	if(e != nil)
-		fprint(fd, "load users: %s\n", e);
-	else
-		fprint(fd, "refreshed users\n");
+	if(waserror()){
+		fprint(fd, "load users: %s\n", errmsg());
+		clunkmount(mnt);
+		return;
+	}
+	loadusers(fd, mnt->root);
+	fprint(fd, "refreshed users\n");
 	clunkmount(mnt);
 }
 
@@ -213,7 +214,7 @@
 static void
 showent(int fd, char **ap, int na)
 {
-	char *e, *p, *name, kbuf[Keymax], kvbuf[Kvmax];
+	char *p, *name, kbuf[Keymax], kvbuf[Kvmax];
 	Tree *t;
 	Kvp kv;
 	Key k;
@@ -226,37 +227,26 @@
 	}
 	pqid = strtoll(ap[0], nil, 16);
 	name = na == 2 ? ap[1] : nil;
-	if((p = packdkey(kbuf, sizeof(kbuf), pqid, name)) == nil){
-		fprint(fd, "could not pack key\n");
-		goto Out;
-	}
+	p = packdkey(kbuf, sizeof(kbuf), pqid, name);
 	k.k = kbuf;
 	k.nk = p - kbuf;
 	if(name != nil){
-		if((e = btlookup(t, &k, &kv, kvbuf, sizeof(kvbuf))) != nil){
-			fprint(fd, "lookup failed: %s\n", e);
-			goto Out;
+		if(!btlookup(t, &k, &kv, kvbuf, sizeof(kvbuf))){
+			fprint(fd, "lookup failed: %s\n", name);
+			closesnap(t);
+			return;
 		}
 		fprint(fd, "%P\n", &kv);
 	}else{
 		btnewscan(&s, k.k, k.nk);
-		if((e = btenter(t, &s)) != nil){
-			fprint(fd, "scan failed: %s\n", e);
-			goto Out;
-		}
+		btenter(t, &s);
 		while(1){
-			if((e = btnext(&s, &kv)) != nil){
-				fprint(fd, "scan failed: %s\n", e);
-				btexit(&s);
-				goto Out;
-			}
-			if(s.done)
+			if(!btnext(&s, &kv))
 				break;
 			fprint(fd, "%P\n", &kv);
 		}
 		btexit(&s);
 	}
-Out:
 	closesnap(t);
 }
 
--- a/dat.h
+++ b/dat.h
@@ -156,6 +156,7 @@
 extern char Edir[];
 extern char Esyntax[];
 extern char Enouser[];
+extern char Enogrp[];
 extern char Efsize[];
 extern char Ebadu[];
 extern char Erdonly[];
@@ -682,7 +683,7 @@
 	vlong	offset;	/* last read offset */
 
 	char	first;
-	char	done;
+	char	donescan;
 	char	overflow;
 	char	present;
 	int	ht;
--- a/dump.c
+++ b/dump.c
@@ -375,7 +375,7 @@
 void
 showsnap(int fd, char **ap, int na)
 {
-	char *e, pfx[Snapsz];
+	char pfx[Snapsz];
 	vlong id;
 	Scan s;
 	uint flg;
@@ -387,18 +387,10 @@
 		pfx[0] = Klabel;
 		sz = 1;
 		btnewscan(&s, pfx, sz);
-		if((e = btenter(&fs->snap, &s)) != nil){
-			fprint(fd, "scan: %s\n", e);
-			btexit(&s);
-			return;
-		}
+		btenter(&fs->snap, &s);
 		while(1){
-			if((e = btnext(&s, &s.kv)) != nil){
-				fprint(fd, "scan: %s\n", e);
+			if(!btnext(&s, &s.kv))
 				break;
-			}
-			if(s.done)
-				break;
 			flg = UNPACK32(s.kv.v+1+8);
 			fprint(fd, "label: %P 0x%x\n", &s.kv, flg);
 		}
@@ -414,18 +406,10 @@
 		PACK64(pfx+1, id);
 	}
 	btnewscan(&s, pfx, sz);
-	if((e = btenter(&fs->snap, &s)) != nil){
-		fprint(fd, "scan: %s\n", e);
-		btexit(&s);
-		return;
-	}
+	btenter(&fs->snap, &s);
 	while(1){
-		if((e = btnext(&s, &s.kv)) != nil){
-			fprint(fd, "scan: %s\n", e);
+		if(!btnext(&s, &s.kv))
 			break;
-		}
-		if(s.done)
-			break;
 		fprint(fd, "snap: %P\n", &s.kv);
 		if(unpacktree(&t, s.kv.v, s.kv.nv) == nil){
 			fprint(fd, "unpack: garbled tree\n");
@@ -439,7 +423,7 @@
 void
 showdlist(int fd, char** ap, int na)
 {
-	char *p, *e, *err, pfx[Kvmax];
+	char *p, *e, pfx[Kvmax];
 	Dlist dl;
 	Bptr hd;
 	Scan s;
@@ -455,17 +439,9 @@
 		PACK64(pfx+1, id);
 	}
 	btnewscan(&s, pfx, sz);
-	if((err = btenter(&fs->snap, &s)) != nil){
-		fprint(fd, "scan: %s\n", err);
-		btexit(&s);
-		return;
-	}
+	btenter(&fs->snap, &s);
 	while(1){
-		if((e = btnext(&s, &s.kv)) != nil){
-			fprint(fd, "scan: %s\n", e);
-			break;
-		}
-		if(s.done)
+		if(!btnext(&s, &s.kv))
 			break;
 		fprint(fd, "dlist: %P\n", &s.kv);
 		kv2dlist(&s.kv, &dl);
--- a/error.c
+++ b/error.c
@@ -27,6 +27,7 @@
 char Edir[]	= "invalid directory";
 char Esyntax[]	= "syntax error";
 char Enouser[]	= "user does not exist";
+char Enogrp[]	= "group does not exist";
 char Efsize[]	= "file too big";
 char Ebadu[]	= "attach -- unknown user or failed authentication";
 char Erdonly[]	= "file system read only";
--- a/fns.h
+++ b/fns.h
@@ -68,10 +68,10 @@
 Mount*	getmount(char*);
 void	clunkmount(Mount*);
 
-char*	updatesnap(Tree**, Tree*, char*);
-char*	tagsnap(Tree*, char*, int);
-char*	delsnap(Tree*, vlong, char*);
-char*	freedl(Dlist*, int);
+void	updatesnap(Tree**, Tree*, char*);
+void	tagsnap(Tree*, char*, int);
+void	delsnap(Tree*, vlong, char*);
+void	freedl(Dlist*, int);
 Tree*	opensnap(char*, int*);
 
 void	closesnap(Tree*);
@@ -84,21 +84,21 @@
 int	scandead(Dlist*, int, void(*)(Bptr, void*), void*);
 int	endfs(void);
 int	compresslog(Arena*);
-char*	flushdlcache(int);
+void	flushdlcache(int);
 void	setval(Blk*, Kvp*);
 
 Conn*	newconn(int, int);
 
 int	walk1(Tree*, vlong, char*, Qid*, vlong*);
-char*	loadusers(int, Tree*);
+void	loadusers(int, Tree*);
 User*	uid2user(int);
 User*	name2user(char*);
 
-char*	btupsert(Tree*, Msg*, int);
-char*	btlookup(Tree*, Key*, Kvp*, char*, int);
+void	btupsert(Tree*, Msg*, int);
+int	btlookup(Tree*, Key*, Kvp*, char*, int);
 void	btnewscan(Scan*, char*, int);
-char*	btenter(Tree*, Scan*);
-char*	btnext(Scan*, Kvp*);
+void	btenter(Tree*, Scan*);
+int	btnext(Scan*, Kvp*);
 void	btexit(Scan*);
 
 int	checkflag(Blk *b, int);
@@ -146,13 +146,10 @@
 _Noreturn void	nexterror(void);
 #define waserror()	(setjmp(*_waserror()))
 #define errmsg()	(((Errctx*)*errctx)->err)
-#define	poperror()	(((Errctx*)*errctx)->nerrlab--)
+#define	poperror()	assert(((Errctx*)*errctx)->nerrlab-- > 0)
+#define estacksz()	(((Errctx*)*errctx)->nerrlab)
 
-char*	pack8(int*, char*, char*, uchar);
-char*	pack16(int*, char*, char*, ushort);
-char*	pack32(int*, char*, char*, uint);
-char*	pack64(int*, char*, char*, uvlong);
-char*	packstr(int*, char*, char*, char*);
+char*	packstr(char*, char*, char*);
 
 int	dir2kv(vlong, Xdir*, Kvp*, char*, int);
 int	dir2statbuf(Xdir*, char*, int);
@@ -183,13 +180,7 @@
 char*	unpackdkey(char*, int, vlong*);
 Tree*	unpacktree(Tree*, char*, int);
 char*	unpacksb(Gefs*, char*, int);
-
-/* void* is a bit hacky, but we want both signed and unsigned to work */
-char*	unpack8(int*, char*, char*, void*);
-char*	unpack16(int*, char*, char*, void*);
-char*	unpack32(int*, char*, char*, void*);
-char*	unpack64(int*, char*, char*, void*);
-char*	unpackstr(int*, char*, char*, char**);
+char*	unpackstr(char*, char*, char**);
 
 /* fmt */
 int	Bconv(Fmt*);
--- a/fs.c
+++ b/fs.c
@@ -18,13 +18,12 @@
 	Key k;
 
 	err = 0;
-	if((p = packdkey(kbuf, sizeof(kbuf), up, name)) == nil)
-		return -1;
+	p = packdkey(kbuf, sizeof(kbuf), up, name);
 	k.k = kbuf;
 	k.nk = p - kbuf;
 	if(err)
 		return -1;
-	if(btlookup(t, &k, &kv, rbuf, sizeof(rbuf)) != nil)
+	if(!btlookup(t, &k, &kv, rbuf, sizeof(rbuf)))
 		return -1;
 	if(kv2dir(&kv, &d) == -1)
 		return -1;
@@ -38,9 +37,12 @@
 {
 	Tree *t, *s;
 	Mount *mnt;
-	char *e;
 
 	lock(&fs->mountlk);
+	if(waserror()){
+		unlock(&fs->mountlk);
+		nexterror();
+	}
 	t = nil;
 	*pred = -1;
 	*bp = (Bptr){-1, -1, -1};
@@ -55,6 +57,7 @@
 	if(t == nil && (t = opensnap(a->old, nil)) == nil){
 		fprint(a->fd, "snap: open '%s': does not exist\n", a->old);
 		unlock(&fs->mountlk);
+		poperror();
 		return;
 	}
 	if(a->delete){
@@ -69,11 +72,7 @@
 			*bp = t->bp;
 			unlock(&t->lk);
 		}
-		if((e = delsnap(t, t->succ, a->old)) != nil){
-			fprint(a->fd, "snap: error deleting '%s': %s\n", a->new, e);
-			unlock(&fs->mountlk);
-			return;
-		}
+		delsnap(t, t->succ, a->old);
 	}else{
 		if((s = opensnap(a->new, nil)) != nil){
 			fprint(a->fd, "snap: already exists '%s'\n", a->new);
@@ -81,14 +80,11 @@
 			unlock(&fs->mountlk);
 			return;
 		}
-		if((e = tagsnap(t, a->new, a->mutable)) != nil){
-			fprint(a->fd, "snap: error creating '%s': %s\n", a->new, e);
-			unlock(&fs->mountlk);
-			return;
-		}
+		tagsnap(t, a->new, a->mutable);
 	}
 	closesnap(t);
 	unlock(&fs->mountlk);
+	poperror();
 	/* we probably want explicit snapshots to get synced */
 	if(a->delete)
 		fprint(a->fd, "deleted: %s\n", a->old);
@@ -240,31 +236,25 @@
 }
 
 
-static char*
+static int
 lookup(Mount *mnt, Key *k, Kvp *kv, char *buf, int nbuf)
 {
-	char *e;
 	Tree *r;
 
 	if(mnt == nil)
-		return Eattach;
-
+		error(Eattach);
 	r = agetp(&mnt->root);
-	e = btlookup(r, k, kv, buf, nbuf);
-	return e;
+	return btlookup(r, k, kv, buf, nbuf);
 }
 
-static char*
+static void
 upsert(Mount *mnt, Msg *m, int nm)
 {
-	char *e;
-
 	if(!mnt->mutable)
-		return Erdonly;
+		error(Erdonly);
 	if(mnt->root->nlbl != 1 || mnt->root->nref != 0)
-		if((e = updatesnap(&mnt->root, mnt->root, mnt->name)) != nil)
-			return e;
-	return btupsert(mnt->root, m, nm);
+		updatesnap(&mnt->root, mnt->root, mnt->name);
+	btupsert(mnt->root, m, nm);
 }
 
 /*
@@ -290,7 +280,7 @@
 static int
 readb(Fid *f, char *d, vlong o, vlong n, vlong sz)
 {
-	char *e, buf[17], kvbuf[17+32];
+	char buf[17], kvbuf[17+32];
 	vlong fb, fo;
 	Bptr bp;
 	Blk *b;
@@ -311,19 +301,13 @@
 	PACK64(k.k+1, f->qpath);
 	PACK64(k.k+9, fb);
 
-	e = lookup(f->mnt, &k, &kv, kvbuf, sizeof(kvbuf));
-	if(e != nil){
-		if(e != Esrch){
-			werrstr(e);
-			return -1;
-		}
+	if(!lookup(f->mnt, &k, &kv, kvbuf, sizeof(kvbuf))){
 		memset(d, 0, n);
 		return n;
 	}
 
 	bp = unpackbp(kv.v, kv.nv);
-	if((b = getblk(bp, GBraw)) == nil)
-		return -1;
+	b = getblk(bp, GBraw);
 	memcpy(d, b->buf+fo, n);
 	dropblk(b);
 	return n;
@@ -332,7 +316,7 @@
 static int
 writeb(Fid *f, Msg *m, Bptr *ret, char *s, vlong o, vlong n, vlong sz)
 {
-	char *e, buf[Kvmax];
+	char buf[Kvmax];
 	vlong fb, fo;
 	Blk *b, *t;
 	Bptr bp;
@@ -346,22 +330,15 @@
 	PACK64(m->k+9, fb);
 
 	b = newdblk(f->mnt->root, Tdat, f->qpath);
-	if(b == nil)
-		return -1;
 	t = nil;
-	e = lookup(f->mnt, m, &kv, buf, sizeof(buf));
-	if(e == nil){
+	if(lookup(f->mnt, m, &kv, buf, sizeof(buf))){
 		bp = unpackbp(kv.v, kv.nv);
 		if(fb < sz && (fo != 0 || n != Blksz)){
-			if((t = getblk(bp, GBraw)) == nil)
-				return -1;
+			t = getblk(bp, GBraw);
 			memcpy(b->buf, t->buf, Blksz);
 			dropblk(t);
 		}
 		freeblk(f->mnt->root, nil, bp);
-	}else if(e != Esrch){
-		werrstr("%s", e);
-		return -1;
 	}
 	if(fo+n > Blksz)
 		n = Blksz-fo;
@@ -445,7 +422,6 @@
 		goto Out;
 	}
 	if((t = opensnap(name, &mnt->mutable)) == nil){
-		werrstr("%s", Enosnap);
 		free(mnt->name);
 		free(mnt);
 		mnt = nil;
@@ -776,6 +752,10 @@
 		rerror(m, Eauth);
 		return;
 	}
+	if(strcmp(m->uname, "none") == 0){
+		rerror(m, Enone);
+		return;
+	}
 	if((de = mallocz(sizeof(Dent), 1)) == nil){
 		rerror(m, Enomem);
 		return;
@@ -910,12 +890,15 @@
 	int uid;
 
 	de = nil;
+	mnt = nil;
+	if(waserror()){
+		rerror(m, errmsg());
+		goto Err;
+	}
 	if(m->aname[0] == '\0')
 		m->aname = "main";
-	if((mnt = getmount(m->aname)) == nil){
-		rerror(m, "%r");
-		goto Out;
-	}
+	if((mnt = getmount(m->aname)) == nil)
+		error(Enosnap);
 
 	rlock(&fs->userlk);
 	n = m->uname;
@@ -927,8 +910,7 @@
 		n = "adm";
 	if((u = name2user(n)) == nil){
 		runlock(&fs->userlk);
-		rerror(m, Enouser);
-		goto Out;
+		error(Enouser);
 	}
 	uid = u->id;
 	runlock(&fs->userlk);
@@ -936,50 +918,36 @@
 	if(m->afid != NOFID){
 		r.data = nil;
 		r.count = 0;
-		if((af = getfid(m->conn, m->afid)) == nil){
-			rerror(m, Enofid);
-			goto Out;
-		}
-		if((e = authread(af, &r, nil, 0)) != nil){
-			rerror(m, e);
+		if((af = getfid(m->conn, m->afid)) == nil)
+			error(Enofid);
+		if(waserror()){
 			putfid(af);
-			goto Out;
+			nexterror();
 		}
-		if(af->uid != uid){
-			rerror(m, Ebadu);
-			putfid(af);
-			goto Out;
-		}
+		if((e = authread(af, &r, nil, 0)) != nil)
+			error(e);
+		if(af->uid != uid)
+			error(Ebadu);
+		poperror();
 		putfid(af);
-	}else if(!fs->noauth){
-		rerror(m, Ebadu);
-		goto Out;
-	}
+	}else if(!fs->noauth && strcmp(m->uname, "none") != 0)
+		error(Ebadu);
 
 	if(strcmp(m->aname, "dump") == 0){
 		memset(&d, 0, sizeof(d));
 		filldumpdir(&d);
 	}else{
-		if((p = packdkey(dbuf, sizeof(dbuf), -1ULL, "")) == nil){
-			rerror(m, Elength);
-			goto Out;
-		}
+		if((p = packdkey(dbuf, sizeof(dbuf), -1ULL, "")) == nil)
+			error(Elength);
 		dk.k = dbuf;
 		dk.nk = p - dbuf;
 		t = agetp(&mnt->root);
-		if((e = btlookup(t, &dk, &kv, kvbuf, sizeof(kvbuf))) != nil){
-			rerror(m, e);
-			goto Out;
-		}
-		if(kv2dir(&kv, &d) == -1){
-			rerror(m, Efs);
-			goto Out;
-		}
+		if(!btlookup(t, &dk, &kv, kvbuf, sizeof(kvbuf)))
+			error(Enosnap);
+		kv2dir(&kv, &d);
 	}
-	if((de = getdent(-1, &d)) == nil){
-		rerror(m, Efs);
-		goto Out;
-	}
+	if((de = getdent(-1, &d)) == nil)
+		error(Efs);
 
 	memset(&f, 0, sizeof(Fid));
 	f.fid = NOFID;
@@ -993,42 +961,38 @@
 	f.duid = d.uid;
 	f.dgid = d.gid;
 	f.dmode = d.mode;
-	if(dupfid(m->conn, m->fid, &f) == nil){
-		rerror(m, Efid);
-		goto Out;
-	}
+	if(dupfid(m->conn, m->fid, &f) == nil)
+		error(Efid);
 
 	r.type = Rattach;
 	r.qid = d.qid;
 	respond(m, &r);
+	poperror();
 
-Out:
-	clunkdent(de);
+Err:	clunkdent(de);
 	clunkmount(mnt);
 }
 
-static char*
+static int
 findparent(Fid *f, vlong *qpath, char **name, char *buf, int nbuf)
 {
-	char *p, *e, kbuf[Keymax];
+	char *p, kbuf[Keymax];
 	Kvp kv;
 	Key k;
 
-	if((p = packsuper(kbuf, sizeof(kbuf), f->pqpath)) == nil)
-		return Elength;
+	p = packsuper(kbuf, sizeof(kbuf), f->pqpath);
 	k.k = kbuf;
 	k.nk = p - kbuf;
-	if((e = lookup(f->mnt, &k, &kv, buf, nbuf)) != nil)
-		return e;
-	if((*name = unpackdkey(kv.v, kv.nv, qpath)) == nil)
-		return Efs;
-	return nil;
+	if(!lookup(f->mnt, &k, &kv, buf, nbuf))
+		return 0;
+	*name = unpackdkey(kv.v, kv.nv, qpath);
+	return 1;
 }
 
 static void
 fswalk(Fmsg *m)
 {
-	char *p, *e, *name, kbuf[Maxent], kvbuf[Kvmax];
+	char *p, *name, kbuf[Maxent], kvbuf[Kvmax];
 	int duid, dgid, dmode;
 	vlong up, prev;
 	Fid *o, *f;
@@ -1044,12 +1008,13 @@
 		rerror(m, Enofid);
 		return;
 	}
-	if(o->mode != -1){
-		rerror(m, Einuse);
+	if(waserror()){
+		rerror(m, errmsg());
 		putfid(o);
 		return;
 	}
-	e = nil;
+	if(o->mode != -1)
+		error(Einuse);
 	mnt = o->mnt;
 	up = o->qpath;
 	prev = o->qpath;
@@ -1061,24 +1026,18 @@
 	dmode = d.mode;
 	r.type = Rwalk;
 	for(i = 0; i < m->nwname; i++){
-		if(fsaccess(o, d.mode, d.uid, d.gid, DMEXEC) != 0){
-			rerror(m, Eperm);
-			putfid(o);
-			return;
-		}
+		if(fsaccess(o, d.mode, d.uid, d.gid, DMEXEC) != 0)
+			error(Eperm);
 		name = m->wname[i];
 		if(d.qid.path == Qdump){
-			if((mnt = getmount(m->wname[i])) == nil){
-				rerror(m, Esrch);
-				putfid(o);
-				return;
-			}
-			if((p = packdkey(kbuf, sizeof(kbuf), -1ULL, "")) == nil){
-				rerror(m, Elength);
+			if((mnt = getmount(m->wname[i])) == nil)
+				error(Esrch);
+			if(waserror()){
 				clunkmount(mnt);
-				putfid(o);
-				return;
+				nexterror();
 			}
+			p = packdkey(kbuf, sizeof(kbuf), -1ULL, "");
+			poperror();
 		}else{
 			if(strcmp(m->wname[i], "..") == 0){
 				if(o->pqpath == Qdump){
@@ -1088,17 +1047,11 @@
 					dgid = d.gid;
 					dmode = d.mode;
 					goto Found;
-				}else if((e = findparent(o, &prev, &name, kbuf, sizeof(kbuf))) != nil){
-					rerror(m, e);
-					putfid(o);
-					return;
 				}
+				if(!findparent(o, &prev, &name, kbuf, sizeof(kbuf)))
+					error(Esrch);
 			}
-			if((p = packdkey(kbuf, sizeof(kbuf), prev, name)) == nil){
-				rerror(m, Elength);
-				putfid(o);
-				return;
-			}
+			p = packdkey(kbuf, sizeof(kbuf), prev, name);
 		}
 		duid = d.uid;
 		dgid = d.gid;
@@ -1105,13 +1058,9 @@
 		dmode = d.mode;
 		k.k = kbuf;
 		k.nk = p - kbuf;
-		if((e = lookup(mnt, &k, &kv, kvbuf, sizeof(kvbuf))) != nil)
+		if(!lookup(mnt, &k, &kv, kvbuf, sizeof(kvbuf)))
 			break;
-		if(kv2dir(&kv, &d) == -1){
-			rerror(m, Efs);
-			putfid(o);
-			return;
-		}
+		kv2dir(&kv, &d);
 Found:
 		up = prev;
 		prev = d.qid.path;
@@ -1118,22 +1067,20 @@
 		r.wqid[i] = d.qid;
 	}
 	r.nwqid = i;
-	if(i == 0 && m->nwname != 0){
-		rerror(m, e);
-		putfid(o);
-		return;
-	}
+	if(i == 0 && m->nwname != 0)
+		error(Esrch);
 	f = o;
 	if(m->fid != m->newfid && i == m->nwname){
-		if((f = dupfid(m->conn, m->newfid, o)) == nil){
-			rerror(m, Efid);
-			putfid(o);
-			return;
-		}
+		if((f = dupfid(m->conn, m->newfid, o)) == nil)
+			error(Efid);
 		putfid(o);
 	}
 	if(i > 0 && i == m->nwname){
 		lock(f);
+		if(waserror()){
+			unlock(f);
+			nexterror();
+		}
 		if(up == Qdump)
 			dent = getdent(-1ULL, &d);
 		else
@@ -1141,10 +1088,7 @@
 		if(dent == nil){
 			if(f != o)
 				clunkfid(m->conn, f);
-			rerror(m, Enomem);
-			unlock(f);
-			putfid(f);
-			return;
+			error(Enomem);
 		}
 		if(mnt != f->mnt){
 			clunkmount(f->mnt);
@@ -1158,9 +1102,11 @@
 		f->duid = duid;
 		f->dgid = dgid;
 		f->dmode = dmode;
+		poperror();
 		unlock(f);
 	}
 	respond(m, &r);
+	poperror();
 	putfid(f);
 }
 
@@ -1176,18 +1122,19 @@
 		rerror(m, Enofid);
 		return;
 	}
-	rlock(f->dent);
-	n = dir2statbuf(f->dent, buf, sizeof(buf));
-	runlock(f->dent);
-	if(n == -1){
-		rerror(m, "stat: %r");
+	if(waserror()){
+		rerror(m, errmsg());
 		putfid(f);
 		return;
 	}
+	rlock(f->dent);
+	n = dir2statbuf(f->dent, buf, sizeof(buf));
+	runlock(f->dent);
 	r.type = Rstat;
 	r.stat = (uchar*)buf;
 	r.nstat = n;
 	respond(m, &r);
+	poperror();
 	putfid(f);
 }
 
@@ -1195,7 +1142,7 @@
 fswstat(Fmsg *m, int id, Amsg **ao)
 {
 	char rnbuf[Kvmax], opbuf[Kvmax], upbuf[Upksz];
-	char *p, *e, strs[65535];
+	char *p, strs[65535];
 	int op, nm, rename, truncate;
 	vlong oldlen;
 	Qid old;
@@ -1209,6 +1156,7 @@
 	Key k;
 	User *u;
 
+	*ao = nil;
 	rename = 0;
 	truncate = 0;
 	if((f = getfid(m->conn, m->fid)) == nil){
@@ -1218,18 +1166,18 @@
 	de = f->dent;
 	truncwait(de, id);
 	wlock(de);
-	if(de->gone){
-		rerror(m, Ephase);
-		goto Out;
+	if(waserror()){
+		rerror(m, errmsg());
+		free(*ao);
+		*ao = nil;
+		goto Err;
 	}
-	if((de->qid.type & QTAUTH) || (de->qid.path & Qdump)){
-		rerror(m, Emode);
-		goto Out;
-	}
-	if(convM2D(m->stat, m->nstat, &d, strs) <= BIT16SZ){
-		rerror(m, Edir);
-		goto Out;
-	}
+	if(de->gone)
+		error(Ephase);
+	if((de->qid.type & QTAUTH) || (de->qid.path & Qdump))
+		error(Emode);
+	if(convM2D(m->stat, m->nstat, &d, strs) <= BIT16SZ)
+		error(Edir);
 
 	t = agetp(&f->mnt->root);
 	n = de->Xdir;
@@ -1239,40 +1187,28 @@
 
 	/* check validity of updated fields and construct Owstat message */
 	if(d.qid.path != ~0 || d.qid.vers != ~0){
-		if(d.qid.path != de->qid.path){
-			rerror(m, Ewstatp);
-			goto Out;
-		}
-		if(d.qid.vers != de->qid.vers){
-			rerror(m, Ewstatv);
-			goto Out;
-		}
+		if(d.qid.path != de->qid.path)
+			error(Ewstatp);
+		if(d.qid.vers != de->qid.vers)
+			error(Ewstatv);
 	}
 	if(*d.name != '\0'){
 		if(strcmp(d.name, de->name) != 0){
 			rename = 1;
-			if(okname(d.name) == -1){
-				rerror(m, Ename);
-				goto Out;
-			}
-			if(walk1(t, f->dent->up, d.name, &old, &oldlen) == 0){
-				rerror(m, Eexist);
-				goto Out;
-			}
+			if(okname(d.name) == -1)
+				error(Ename);
+			if(walk1(t, f->dent->up, d.name, &old, &oldlen) == 0)
+				error(Eexist);
 			n.name = d.name;
 		}
 	}
 	if(d.length != ~0){
-		if(d.length < 0){
-			rerror(m, Ewstatl);
-			goto Out;
-		}
+		if(d.length < 0)
+			error(Ewstatl);
 		if(d.length != de->length){
 			if(d.length < de->length){
-				if((*ao = malloc(sizeof(Amsg))) == nil){
-					rerror(m, Enomem);
-					goto Out;
-				}
+				if((*ao = malloc(sizeof(Amsg))) == nil)
+					error(Enomem);
 				aincl(&de->ref, 1);
 				aincl(&f->mnt->ref, 1);
 				(*ao)->op = AOclear;
@@ -1291,14 +1227,10 @@
 		}
 	}
 	if(d.mode != ~0){
-		if((d.mode^de->mode) & DMDIR){
-			rerror(m, Ewstatd);
-			goto Out;
-		}
-		if(d.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777)){
-			rerror(m, Ewstatb);
-			goto Out;
-		}
+		if((d.mode^de->mode) & DMDIR)
+			error(Ewstatd);
+		if(d.mode & ~(DMDIR|DMAPPEND|DMEXCL|DMTMP|0777))
+			error(Ewstatb);
 		if(d.mode != de->mode){
 			n.mode = d.mode;
 			n.qid.type = d.mode>>24;
@@ -1320,8 +1252,7 @@
 		u = name2user(d.uid);
 		if(u == nil){
 			runlock(&fs->userlk);
-			rerror(m, Enouser);
-			goto Out;
+			error(Enouser);
 		}
 		n.uid = u->id;
 		runlock(&fs->userlk);
@@ -1336,8 +1267,7 @@
 		u = name2user(d.gid);
 		if(u == nil){
 			runlock(&fs->userlk);
-			rerror(m, Enouser);
-			goto Out;
+			error(Enogrp);
 		}
 		n.gid = u->id;
 		runlock(&fs->userlk);
@@ -1353,38 +1283,23 @@
 	p += 4;
 
 	/* check permissions */
-	if(rename){
-		if(fsaccess(f, f->dmode, f->duid, f->dgid, DMWRITE) == -1){
-			rerror(m, Eperm);
-			goto Out;
-		}
-	}
-	if(op & Owsize){
-		if(fsaccess(f, de->mode, de->uid, de->gid, DMWRITE) == -1){
-			rerror(m, Eperm);
-			goto Out;
-		}
-	}
-	if(op & (Owmode|Owmtime)){
-		if(!permissive && f->uid != de->uid && !groupleader(f->uid, de->gid)){
-			rerror(m, Ewstato);
-			goto Out;
-		}
-	}
-	if(op & Owuid){
-		if(!permissive){
-			rerror(m, Ewstatu);
-			goto Out;
-		}
-	}
-	if(op & Owgid){
+	if(rename)
+		if(fsaccess(f, f->dmode, f->duid, f->dgid, DMWRITE) == -1)
+			error(Eperm);
+	if(op & Owsize)
+		if(fsaccess(f, de->mode, de->uid, de->gid, DMWRITE) == -1)
+			error(Eperm);
+	if(op & (Owmode|Owmtime))
+		if(!permissive && f->uid != de->uid && !groupleader(f->uid, de->gid))
+			error(Ewstato);
+	if(op & Owuid)
+		if(!permissive)
+			error(Ewstatu);
+	if(op & Owgid)
 		if(!permissive
 		&& !(f->uid == de->uid && ingroup(f->uid, n.gid))
-		&& !(groupleader(f->uid, de->gid) && groupleader(f->uid, n.gid))){
-			rerror(m, Ewstatg);
-			goto Out;
-		}
-	}
+		&& !(groupleader(f->uid, de->gid) && groupleader(f->uid, n.gid)))
+			error(Ewstatg);
 
 	/* update directory entry */
 	nm = 0;
@@ -1396,10 +1311,7 @@
 		nm++;
 	
 		mb[nm].op = Oinsert;
-		if(dir2kv(f->pqpath, &n, &mb[nm], rnbuf, sizeof(rnbuf)) == -1){
-			rerror(m, Efs);
-			goto Out;
-		}
+		dir2kv(f->pqpath, &n, &mb[nm], rnbuf, sizeof(rnbuf));
 		k = mb[nm].Key;
 		nm++;
 
@@ -1421,12 +1333,7 @@
 		nm++;
 	}
 	assert(nm <= nelem(mb));
-	if((e = upsert(f->mnt, mb, nm)) != nil){
-		qunlock(&de->trunclk);
-		wunlock(de);
-		rerror(m, e);
-		goto Out;
-	}
+	upsert(f->mnt, mb, nm);
 
 	de->Xdir = n;
 	if(rename)
@@ -1434,9 +1341,9 @@
 
 	r.type = Rwstat;
 	respond(m, &r);
+	poperror();
 
-Out:
-	if(!truncate)
+Err:	if(!truncate)
 		qunlock(&de->trunclk);
 	wunlock(de);
 	putfid(f);
@@ -1468,7 +1375,7 @@
 static void
 fscreate(Fmsg *m)
 {
-	char *p, *e, buf[Kvmax], upkbuf[Keymax], upvbuf[Inlmax];
+	char *p, buf[Kvmax], upkbuf[Keymax], upvbuf[Inlmax];
 	Dent *de;
 	vlong oldlen;
 	Qid old;
@@ -1491,6 +1398,11 @@
 		return;
 	}
 	lock(f);
+	if(waserror()){
+		rerror(m, errmsg());
+		goto Err;
+		
+	}
 	if(f->mode != -1){
 		rerror(m, Einuse);
 		goto Out;
@@ -1554,10 +1466,7 @@
 		mb[nm].nv = p - upvbuf;
 		nm++;
 	}
-	if((e = upsert(f->mnt, mb, nm)) != nil){
-		rerror(m, e);
-		goto Out;
-	}
+	upsert(f->mnt, mb, nm);
 
 	de = getdent(f->qpath, &d);
 	if(de == nil){
@@ -1568,17 +1477,13 @@
 	f->pqpath = f->qpath;
 	f->qpath = d.qid.path;
 	f->dent = de;
-	unlock(f);
-	putfid(f);
 
 	r.type = Rcreate;
 	r.qid = d.qid;
 	r.iounit = f->iounit;
 	respond(m, &r);
-	return;
-
-Out:
-	unlock(f);
+Out:	poperror();
+Err:	unlock(f);
 	putfid(f);
 	return;
 }
@@ -1592,16 +1497,15 @@
 
 	if(f->dent->qid.type == QTFILE)
 		return nil;
+
 	t = agetp(&f->mnt->root);
 	packdkey(pfx, sizeof(pfx), f->qpath, nil);
 	btnewscan(&s, pfx, sizeof(pfx));
-	if((e = btenter(t, &s)) != nil)
-		goto Out;
-	if((e = btnext(&s, &s.kv)) != nil)
-		goto Out;
-	if(!s.done)
+	btenter(t, &s);
+	if(btnext(&s, &s.kv))
 		e = Enempty;
-Out:
+	else
+		e = nil;
 	btexit(&s);
 	return e;
 }
@@ -1609,11 +1513,10 @@
 static void
 fsremove(Fmsg *m, int id, Amsg **ao)
 {
-	char upbuf[Upksz];
+	char *e, upbuf[Upksz];
 	Fcall r;
 	Msg mb[2];
 	Fid *f;
-	char *e;
 
 	if((f = getfid(m->conn, m->fid)) == nil){
 		rerror(m, Enofid);
@@ -1623,16 +1526,19 @@
 
 	truncwait(f->dent, id);
 	wlock(f->dent);
-	if(f->dent->gone){
-		e = Ephase;
-		goto Error;
+	*ao = nil;
+	if(waserror()){
+		rerror(m, errmsg());
+		free(*ao);
+		*ao = nil;
+		goto Err;
 	}
+	if(f->dent->gone)
+		error(Ephase);
 	if((e = candelete(f)) != nil)
-		goto Error;
-	if(fsaccess(f, f->dmode, f->duid, f->dgid, DMWRITE) == -1){
-		e = Eperm;
-		goto Error;
-	}
+		error(e);
+	if(fsaccess(f, f->dmode, f->duid, f->dgid, DMWRITE) == -1)
+		error(Eperm);
 	mb[0].op = Odelete;
 	mb[0].k = f->dent->k;
 	mb[0].nk = f->dent->nk;
@@ -1644,11 +1550,10 @@
 	mb[1].nk = Upksz;
 	mb[1].nv = 0;
 
-	if((e = upsert(f->mnt, mb, 1)) != nil)
-		goto Error;
+	upsert(f->mnt, mb, 1);
 	if(f->dent->qid.type == QTFILE){
 		if((*ao = malloc(sizeof(Amsg))) == nil)
-			goto Error;
+			error(Enomem);
 		aincl(&f->mnt->ref, 1);
 		(*ao)->op = AOclear;
 		(*ao)->mnt = f->mnt;
@@ -1658,25 +1563,19 @@
 		(*ao)->dent = nil;
 	}
 	f->dent->gone = 1;
-	qunlock(&f->dent->trunclk);
-	wunlock(f->dent);
-
 	r.type = Rremove;
 	respond(m, &r);
-	putfid(f);
-	return;
-
-Error:
-	qunlock(&f->dent->trunclk);
+	poperror();
+Err:	qunlock(&f->dent->trunclk);
 	wunlock(f->dent);
-	rerror(m, e);
 	putfid(f);
+	return;
 }
 
 static void
 fsopen(Fmsg *m, int id, Amsg **ao)
 {
-	char *p, *e, buf[Kvmax];
+	char *p, buf[Kvmax];
 	int mbits;
 	Fcall r;
 	Xdir d;
@@ -1689,38 +1588,32 @@
 		rerror(m, Enofid);
 		return;
 	}
-
+	if(waserror()){
+		rerror(m, errmsg());
+		putfid(f);
+		return;
+	}
 	if((f->qpath & Qdump) != 0){
 		filldumpdir(&d);
 	}else{
-		if((e = lookup(f->mnt, f->dent, &kv, buf, sizeof(buf))) != nil){
-			rerror(m, e);
-			putfid(f);
-			return;
-		}
-		if(kv2dir(&kv, &d) == -1){
-			rerror(m, Efs);
-			putfid(f);
-			return;
-		}
+		if(!lookup(f->mnt, f->dent, &kv, buf, sizeof(buf)))
+			error(Esrch);
+		kv2dir(&kv, &d);
 	}
 	wlock(f->dent);
-	if(f->dent->gone){
-		rerror(m, Ephase);
-Disallow:	wunlock(f->dent);
-		putfid(f);
-		return;
+	if(waserror()){
+		wunlock(f->dent);
+		nexterror();
 	}
+	if(f->dent->gone)
+		error(Ephase);
 	if(f->dent->qid.type & QTEXCL)
-	if(f->dent->ref != 1){
-		rerror(m, Elocked);
-		goto Disallow;
-	}
-	if(fsaccess(f, d.mode, d.uid, d.gid, mbits) == -1){
-		rerror(m, Eperm);
-		goto Disallow;
-	}
+	if(f->dent->ref != 1)
+		error(Elocked);
+	if(fsaccess(f, d.mode, d.uid, d.gid, mbits) == -1)
+		error(Eperm);
 	f->dent->length = d.length;
+	poperror();
 	wunlock(f->dent);
 	r.type = Ropen;
 	r.qid = d.qid;
@@ -1728,17 +1621,10 @@
 
 	lock(f);
 	if(f->mode != -1){
-		rerror(m, Einuse);
 		unlock(f);
-		putfid(f);
-		return;
+		error(Einuse);
 	}
 	f->mode = mode2bits(m->mode);
-//	if(!fs->rdonly && (m->mode == OEXEC)){
-//		lock(&fs->root.lk);
-//		f->root = fs->root;
-//		unlock(&fs->root.lk);
-//	}
 	if(m->mode & OTRUNC){
 		truncwait(f->dent, id);
 		wlock(f->dent);
@@ -1755,10 +1641,13 @@
 		mb.nk = f->dent->nk;
 		mb.v = buf;
 		mb.nv = p - buf;
-		if((*ao = malloc(sizeof(Amsg))) == nil){
-			e = Enomem;
-			goto Error;
+		if(waserror()){
+			qunlock(&f->dent->trunclk);
+			wunlock(f->dent);
+			nexterror();
 		}
+		if((*ao = malloc(sizeof(Amsg))) == nil)
+			error(Enomem);
 		aincl(&f->mnt->ref, 1);
 		(*ao)->op = AOclear;
 		(*ao)->mnt = f->mnt;
@@ -1766,17 +1655,13 @@
 		(*ao)->off = 0;
 		(*ao)->length = f->dent->length;
 		(*ao)->dent = nil;
-		if((e = upsert(f->mnt, &mb, 1)) != nil){
-Error:			qunlock(&f->dent->trunclk);
-			wunlock(f->dent);
-			rerror(m, e);
-			putfid(f);
-			return;
-		}
+		upsert(f->mnt, &mb, 1);
 		qunlock(&f->dent->trunclk);
 		wunlock(f->dent);
+		poperror();
 	}
 	unlock(f);
+	poperror();
 	respond(m, &r);
 	putfid(f);
 }
@@ -1784,7 +1669,7 @@
 static char*
 readsnap(Fmsg *m, Fid *f, Fcall *r)
 {
-	char pfx[1], *p, *e;
+	char pfx[1], *p;
 	int n, ns;
 	Scan *s;
 	Xdir d;
@@ -1804,7 +1689,7 @@
 		f->scan = s;
 		unlock(f);
 	}
-	if(s->done){
+	if(s->donescan){
 		r->count = 0;
 		return nil;
 	}
@@ -1823,12 +1708,9 @@
 		p += ns;
 		n -= ns;
 	}
-	if((e = btenter(&fs->snap, s)) != nil)
-		return e;
+	btenter(&fs->snap, s);
 	while(1){
-		if((e = btnext(s, &s->kv)) != nil)
-			return e;
-		if(s->done)
+		if(!btnext(s, &s->kv))
 			break;
 		memcpy(d.name, s->kv.k+1, s->kv.nk-1);
 		d.name[s->kv.nk-1] = 0;
@@ -1848,7 +1730,7 @@
 static char*
 readdir(Fmsg *m, Fid *f, Fcall *r)
 {
-	char pfx[Dpfxsz], *p, *e;
+	char pfx[Dpfxsz], *p;
 	int n, ns;
 	Tree *t;
 	Scan *s;
@@ -1869,7 +1751,7 @@
 		f->scan = s;
 		unlock(f);
 	}
-	if(s->done){
+	if(s->donescan){
 		r->count = 0;
 		return nil;
 	}
@@ -1884,12 +1766,9 @@
 		p += ns;
 		n -= ns;
 	}
-	if((e = btenter(t, s)) != nil)
-		return e;
+	btenter(t, s);
 	while(1){
-		if((e = btnext(s, &s->kv)) != nil)
-			return e;
-		if(s->done)
+		if(!btnext(s, &s->kv))
 			break;
 		if((ns = kv2statbuf(&s->kv, p, n)) == -1){
 			s->overflow = 1;
@@ -1997,23 +1876,20 @@
 	}
 	truncwait(f->dent, id);
 	wlock(f->dent);
-	if(f->dent->gone){
-		rerror(m, Ephase);
+	if(waserror()){
+		rerror(m, errmsg());
 		qunlock(&f->dent->trunclk);
 		wunlock(f->dent);
 		putfid(f);
 		return;
 	}
+	if(f->dent->gone)
+		error(Ephase);
 	if(f->dent->qid.type == QTAUTH){
 		e = authwrite(f, &r, m->data, m->count);
 		if(e != nil)
-			rerror(m, e);
-		else
-			respond(m, &r);
-		qunlock(&f->dent->trunclk);
-		wunlock(f->dent);
-		putfid(f);
-		return;
+			error(e);
+		goto Out;
 	}		
 
 	w = 0;
@@ -2028,17 +1904,14 @@
 		kv[i].nk = sizeof(kbuf[i]);
 		kv[i].v = vbuf[i];
 		kv[i].nv = sizeof(vbuf[i]);
-		n = writeb(f, &kv[i], &bp[i], p, o, c, f->dent->length);
-		if(n == -1){
-			for(j = 0; j < i; j++)
-				freeblk(t, nil, bp[i]);
-			qunlock(&f->dent->trunclk);
-			wunlock(f->dent);
-			fprint(2, "%r");
-			putfid(f);
-			abort();
-			return;
+		if(waserror()){
+			if(!fs->rdonly)
+				for(j = 0; j < i; j++)
+					freeblk(t, nil, bp[i]);
+			nexterror();
 		}
+		n = writeb(f, &kv[i], &bp[i], p, o, c, f->dent->length);
+		poperror();
 		w += n;
 		p += n;
 		o += n;
@@ -2067,21 +1940,16 @@
 
 	kv[i].v = sbuf;
 	kv[i].nv = p - sbuf;
-	if((e = upsert(f->mnt, kv, i+1)) != nil){
-		rerror(m, e);
-		qunlock(&f->dent->trunclk);
-		wunlock(f->dent);
-		putfid(f);
-		abort();
-		return;
-	}
-	qunlock(&f->dent->trunclk);
-	wunlock(f->dent);
+	upsert(f->mnt, kv, i+1);
 
 	r.type = Rwrite;
 	r.count = w;
+Out:
+	poperror();
  	respond(m, &r);
-	putfid(f);
+	qunlock(&f->dent->trunclk);
+	wunlock(f->dent);
+	putfid(f);	
 }
 
 void
@@ -2182,6 +2050,7 @@
 			respond(m, &r);
 			break;
 		}
+		assert(estacksz() == 0);
 	}
 }
 
@@ -2209,7 +2078,8 @@
 		case Topen:	fsopen(m, id, &a);	break;
 		default:	abort();		break;
 		}
- 		epochend(id);
+		assert(estacksz() == 0);
+		epochend(id);
  		qunlock(&fs->mutlk);
 		epochclean();
 
@@ -2233,6 +2103,7 @@
 		case Tstat:	fsstat(m);		break;
 		case Topen:	fsopen(m, id, nil);	break;
 		}
+		assert(estacksz() == 0);
 		epochend(id);
 	}
 }
@@ -2298,7 +2169,7 @@
 void
 runsweep(int id, void*)
 {
-	char *e, buf[Offksz];
+	char buf[Offksz];
 	Bptr bp, nb, *oldhd;
 	vlong off, pred;
 	Mount *mnt;
@@ -2362,16 +2233,26 @@
 			break;
 
 		case AOsnap:
+			if(waserror()){
+				fprint(2, "taking snap: %s\n", errmsg());
+				ainc(&fs->rdonly);
+			}
 			qlock(&fs->mutlk);
+			if(waserror()){
+				qunlock(&fs->mutlk);
+				nexterror();
+			}
 			epochstart(id);
 			snapfs(am, &bp, &pred);
 			sync();
 			epochend(id);
+			poperror();
 			qunlock(&fs->mutlk);
 			if(pred != -1){
 				epochwait();
 				sweeptree(bp, pred);
 			}
+			poperror();
 			break;
 
 		case AOclear:
@@ -2386,8 +2267,7 @@
 				PACK64(m.k+9, off);
 				m.v = nil;
 				m.nv = 0;
-				if((e = upsert(am->mnt, &m, 1)) != nil)
-					broke("reading %B: %s", bp, e);
+				upsert(am->mnt, &m, 1);
 				epochend(id);
 				qunlock(&fs->mutlk);
 				epochclean();
@@ -2399,6 +2279,7 @@
 			clunkmount(am->mnt);
 			break;
 		}
+		assert(estacksz() == 0);
 		free(am);
 	}
 }
--- a/load.c
+++ b/load.c
@@ -57,7 +57,6 @@
 	Mount *dump;
 	Arena *a;
 	Bptr bp;
-	char *e;
 	Tree *t;
 	int i, k;
 
@@ -110,6 +109,13 @@
 			sysfatal("load log %B: %r", a->loghd);
 	}
 
+	if(waserror())
+		sysfatal("load fs: %s\n", errmsg());
+	if((t = opensnap("adm", nil)) == nil)
+		sysfatal("load users: no adm label");
+	loadusers(2, t);
+	poperror();
+
 	fprint(2, "load %s:\n", dev);
 	fprint(2, "\tsnaptree:\t%B\n", fs->snap.bp);
 	fprint(2, "\tnarenas:\t%d\n", fs->narena);
@@ -118,9 +124,5 @@
 	fprint(2, "\tnextgen:\t%lld\n", fs->nextgen);
 	fprint(2, "\tblocksize:\t%lld\n", Blksz);
 	fprint(2, "\tcachesz:\t%lld MiB\n", fs->cmax*Blksz/MiB);
-	if((t = opensnap("adm", nil)) == nil)
-		sysfatal("load users: no adm label");
-	if((e = loadusers(2, t)) != nil)
-		sysfatal("load users: %s\n", e);
 	closesnap(t);
 }
--- a/pack.c
+++ b/pack.c
@@ -6,124 +6,30 @@
 #include "dat.h"
 #include "fns.h"
 
-char*
-unpack8(int *err, char *p, char *e, void *v)
-{
-	if (e - p < 1 || *err){
-		*err = 1;
-		return p;
-	}
-	*(uchar*)v = p[0];
-	return p+1;
-}
-
-char*
-unpack16(int *err, char *p, char *e, void *v)
-{
-	if (e - p < 2 || *err){
-		*err = 1;
-		return p;
-	}
-	*(ushort*)v = UNPACK16(p);
-	return p+2;
-}
-
-char*
-unpack32(int *err, char *p, char *e, void *v)
-{
-	if (e - p < 4 || *err){
-		*err = 1;
-		return p;
-	}
-	*(uint*)v = UNPACK32(p);
-	return p+4;
-}
-
-char*
-unpack64(int *err, char *p, char *e, void *v)
-{
-	if (e - p < 8 || *err){
-		*err = 1;
-		return p;
-	}
-	*(uvlong*)v = UNPACK64(p);
-	return p+8;
-}
-
 /* Terminated so we can use them directly in C */
 char*
-unpackstr(int *err, char *p, char *e, char **s)
+unpackstr(char *p, char *e, char **s)
 {
 	int n;
 
-	if (e - p < 3 || *err){
-		*err = 1;
-		return p;
-	}
+	if (e - p < 3)
+		error(Elength);
 	n = UNPACK16(p);
-	if(e - p < n + 3 || p[n+2] != 0){
-		*err = 1;
-		return p;
-	}
+	if(e - p < n + 3 || p[n+2] != 0)
+		error(Efs);
 	*s = p+2;
 	return p+3+n;
 }
 
-char*
-pack8(int *err, char *p, char *e, uchar v)
-{
-	if (e - p < 1 || *err){
-		*err = 1;
-		return p;
-	}
-	p[0] = v;
-	return p+1;
-}
-
-char*
-pack16(int *err, char *p, char *e, ushort v)
-{
-	if (e - p < 2 || *err){
-		*err = 1;
-		return p;
-	}
-	PACK16(p, v);
-	return p+2;
-}
-
-char*
-pack32(int *err, char *p, char *e, uint v)
-{
-	if (e - p < 4 || *err){
-		*err = 1;
-		return p;
-	}
-	PACK32(p, v);
-	return p+4;
-}
-
-char*
-pack64(int *err, char *p, char *e, uvlong v)
-{
-	if (e - p < 8 || *err){
-		*err = 1;
-		return p;
-	}
-	PACK64(p, v);
-	return p+8;
-}
-
 /* Terminated so we can use them directly in C */
 char*
-packstr(int *err, char *p, char *e, char *s)
+packstr(char *p, char *e, char *s)
 {
 	int n;
 
 	n = strlen(s);
-	if (e - p < n+3 || *err){
-		*err = 1;
-		return p;
-	}
+	if (e - p < n+3)
+		error(Elength);
 	PACK16(p, n);		p += 2;
 	memmove(p, s, n);	p += n;
 	*p = 0;			p += 1;
@@ -135,13 +41,11 @@
 {
 	char *ek, *ev, *eb;
 
-	if((ek = packdkey(buf, nbuf, up, d->name)) == nil)
-		return -1;
+	ek = packdkey(buf, nbuf, up, d->name);
 	kv->k = buf;
 	kv->nk = ek - buf;
 	eb = buf + nbuf;
-	if((ev = packdval(ek, eb - ek, d)) == nil)
-		return -1;
+	ev = packdval(ek, eb - ek, d);
 	kv->v = ek;
 	kv->nv = ev - ek;
 	return 0;
@@ -151,16 +55,12 @@
 packdkey(char *p, int sz, vlong up, char *name)
 {
 	char *ep;
-	int err;
 
-	err = 0;
 	ep = p + sz;
-	p = pack8(&err, p, ep, Kent);
-	p = pack64(&err, p, ep, up);
+	PACK8(p, Kent);	p += 1;
+	PACK64(p, up);	p += 8;
 	if(name != nil)
-		p = packstr(&err, p, ep, name);
-	if(err)
-		return nil;
+		p = packstr(p, ep, name);
 	return p;
 }
 
@@ -167,16 +67,15 @@
 char*
 unpackdkey(char *p, int sz, vlong *up)
 {
-	char t, *ep, *name;
-	int err;
+	char key, *ep, *name;
 
-	err = 0;
 	ep = p + sz;
-	p = unpack8(&err, p, ep, &t);
-	p = unpack64(&err, p, ep, up);
-	p = unpackstr(&err, p, ep, &name);
-	if(err || t != Kent || p != ep)
-		return nil;
+	assert(sz > 9);
+	key = UNPACK8(p);	p += 1;
+	*up = UNPACK64(p);	p += 8;
+	assert(key == Kent);
+	p = unpackstr(p, ep, &name);
+	assert(p <= ep);
 	return name;
 }
 
@@ -184,14 +83,11 @@
 packsuper(char *p, int sz, vlong up)
 {
 	char *ep;
-	int err;
 
-	err = 0;
-	ep = p + sz;
-	p = pack8(&err, p, ep, Kup);
-	p = pack64(&err, p, ep, up);
-	if(err)
-		return nil;
+	ep = p+sz;
+	PACK8(p, Kup);	p += 1;
+	PACK64(p, up);	p += 8;
+	assert(p <= ep);
 	return p;
 }
 
@@ -199,23 +95,20 @@
 packdval(char *p, int sz, Xdir *d)
 {
 	char *e;
-	int err;
 
-	err = 0;
 	e = p + sz;
-	p = pack64(&err, p, e, d->flag);
-	p = pack64(&err, p, e, d->qid.path);
-	p = pack32(&err, p, e, d->qid.vers);
-	p = pack8(&err, p, e, d->qid.type);
-	p = pack32(&err, p, e, d->mode);
-	p = pack64(&err, p, e, d->atime);
-	p = pack64(&err, p, e, d->mtime);
-	p = pack64(&err, p, e, d->length);
-	p = pack32(&err, p, e, d->uid);
-	p = pack32(&err, p, e, d->gid);
-	p = pack32(&err, p, e, d->muid);
-	if(err)
-		return nil;
+	PACK64(p, d->flag);	p += 8;
+	PACK64(p, d->qid.path);	p += 8;
+	PACK32(p, d->qid.vers);	p += 4;
+	PACK8(p, d->qid.type);	p += 1;
+	PACK32(p, d->mode);	p += 4;
+	PACK64(p, d->atime);	p += 8;
+	PACK64(p, d->mtime);	p += 8;
+	PACK64(p, d->length);	p += 8;
+	PACK32(p, d->uid);	p += 4;
+	PACK32(p, d->gid);	p += 4;
+	PACK32(p, d->muid);	p += 4;
+	assert(p <= e);
 	return p;
 }
 
@@ -223,43 +116,30 @@
 kv2dir(Kvp *kv, Xdir *d)
 {
 	char *k, *ek, *v, *ev;
-	int err;
 
 	memset(d, 0, sizeof(Xdir));
-	err = 0;
 	k = kv->k + 9;
 	ek = kv->k + kv->nk;
-	k = unpackstr(&err, k, ek, &d->name);
-	if(err){
-		werrstr("key too small [%d]", kv->nk);
-		return -1;
-	}
+	k = unpackstr(k, ek, &d->name);
 
 	v = kv->v;
 	ev = v + kv->nv;
-	v = unpack64(&err, v, ev, &d->flag);
-	v = unpack64(&err, v, ev, &d->qid.path);
-	v = unpack32(&err, v, ev, &d->qid.vers);
-	v = unpack8(&err, v, ev, &d->qid.type);
-	v = unpack32(&err, v, ev, &d->mode);
-	v = unpack64(&err, v, ev, &d->atime);
-	v = unpack64(&err, v, ev, &d->mtime);
-	v = unpack64(&err, v, ev, &d->length);
-	v = unpack32(&err, v, ev, &d->uid);
-	v = unpack32(&err, v, ev, &d->gid);
-	v = unpack32(&err, v, ev, &d->muid);
-	if(err){
-		werrstr("val too small [%s]", d->name);
-		return -1;
-	}
-	if(k != ek){
-		werrstr("invalid path");
-		return -1;
-	}
-	if(v != ev){
-		werrstr("stat full of fuck");
-		return -1;
-	}
+	d->flag 	= UNPACK64(v);	v += 8;
+	d->qid.path	= UNPACK64(v);	v += 8;
+	d->qid.vers	= UNPACK32(v);	v += 4;
+	d->qid.type	= UNPACK8(v);	v += 1;
+	d->mode		= UNPACK32(v);	v += 4;
+	d->atime	= UNPACK64(v);	v += 8;
+	d->mtime	= UNPACK64(v);	v += 8;
+	d->length	= UNPACK64(v);	v += 8;
+	d->uid		= UNPACK32(v);	v += 4;
+	d->gid		= UNPACK32(v);	v += 4;
+	d->muid		= UNPACK32(v);	v += 4;
+	assert(v <= ev);
+	if(k != ek)
+		error(Efs);
+	if(v != ev)
+		error(Efs);
 	return 0;
 }
 
--- a/snap.c
+++ b/snap.c
@@ -7,7 +7,7 @@
 #include "fns.h"
 #include "atomic.h"
 
-static char*
+static void
 dlsync(Dlist *dl)
 {
 	char kvbuf[512];
@@ -14,14 +14,14 @@
 	Msg m;
 
 	if(dl->ins == nil)
-		return nil;
+		return;
 	if(!checkflag(dl->ins, Bdirty))
-		return nil;
+		return;
 
 	enqueue(dl->ins);
 	m.op = Oinsert;
 	dlist2kv(dl, &m, kvbuf, sizeof(kvbuf));
-	return btupsert(&fs->snap, &m, 1);
+	btupsert(&fs->snap, &m, 1);
 }
 
 static void
@@ -81,7 +81,11 @@
 	if((dl = dlcacheget(gen, bgen)) != nil)
 		return dl;
 	if((dl = mallocz(sizeof(Dlist), 1)) == nil)
-		return nil;
+		error(Enomem);
+	if(waserror()){
+		free(dl);
+		nexterror();
+	}
 	kbuf[0] = Kdlist;
 	PACK64(kbuf+1, gen);
 	PACK64(kbuf+9, bgen);
@@ -89,12 +93,10 @@
 	k.nk = sizeof(kbuf);
 
 	/* load up existing dlist */
-	if(btlookup(&fs->snap, &k, &kv, kvbuf, sizeof(kvbuf)) == nil){
+	if(btlookup(&fs->snap, &k, &kv, kvbuf, sizeof(kvbuf))){
 		kv2dlist(&kv, dl);
-		if(dl->hd.addr != -1 && (dl->ins = getblk(dl->hd, 0)) == nil){
-			free(dl);
-			return nil;
-		}
+		if(dl->hd.addr != -1)
+			dl->ins = getblk(dl->hd, 0);
 		goto Found;
 	}
 
@@ -107,10 +109,8 @@
 
 	m.op = Oinsert;
 	dlist2kv(dl, &m, kvbuf, sizeof(kvbuf));
-	if(btupsert(&fs->snap, &m, 1) != nil){
-		free(dl);
-		return nil;
-	}
+	btupsert(&fs->snap, &m, 1);
+	poperror();
 Found:
 	h = ihash(gen) ^ ihash(bgen);
 	p = &fs->dlcache[h % fs->dlcmax];
@@ -143,10 +143,10 @@
 	fs->dlhead = dl;
 }
 
-char*
+void
 freedl(Dlist *dl, int docontents)
 {
-	char *e, buf[Kvmax];
+	char buf[Kvmax];
 	Bptr bp, fb;
 	Msg m;
 	Blk *b;
@@ -156,12 +156,10 @@
 	if(dl != &fs->snapdl){
 		m.op = Odelete;
 		dlist2kv(dl, &m, buf, sizeof(buf));
-		if((e = btupsert(&fs->snap, &m, 1)) != nil)
-			return e;
+		btupsert(&fs->snap, &m, 1);
 	}
 	while(bp.addr != -1){
-		if((b = getblk(bp, 0)) == nil)
-			return Efs;
+		b = getblk(bp, 0);
 		if(docontents){
 			for(p = b->data; p != b->data+b->logsz; p += 8){
 				fb.addr = UNPACK64(p);
@@ -174,21 +172,25 @@
 		freeblk(&fs->snap, b, b->bp);
 		dropblk(b);
 	}
-	return nil;
 }
 
-static char*
+static void
 mergedl(vlong merge, vlong gen, vlong bgen)
 {
-	char *e, buf[2][Kvmax];
+	char buf[2][Kvmax];
 	Dlist *d, *m;
 	Msg msg[2];
 	Blk *b;
 
-	if((d = getdl(merge, bgen)) == nil)
-		return Enomem;
-	if((m = getdl(gen, bgen)) == nil)
-		return Enomem;
+	d = nil;
+	m = nil;
+	if(waserror()){
+		putdl(m);
+		putdl(d);
+		nexterror();
+	}
+	d = getdl(merge, bgen);
+	m = getdl(gen, bgen);
 	/*
 	 * If the dest dlist didn't exist,
 	 * just move the merge dlist over
@@ -210,8 +212,6 @@
 			b = holdblk(d->ins);
 		else
 			b = getblk(d->tl, 0);
-		if(b == nil)
-			return Efs;
 		b->logp = m->hd;
 		setflag(b, Bdirty);
 		enqueue(b);
@@ -221,16 +221,16 @@
 	dlist2kv(m, &msg[0], buf[0], sizeof(buf[0]));
 	msg[1].op = Oinsert;
 	dlist2kv(d, &msg[1], buf[1], sizeof(buf[1]));
-	e = btupsert(&fs->snap, msg, 2);
+	btupsert(&fs->snap, msg, 2);
 	putdl(m);
 	putdl(d);
-	return e;
+	poperror();
 }
 
-static char*
+static void
 reclaimblocks(vlong gen, vlong succ, vlong prev)
 {
-	char *e, pfx[9];
+	char pfx[9];
 	Dlist dl;
 	Scan s;
 
@@ -237,26 +237,24 @@
 	pfx[0] = Kdlist;
 	PACK64(pfx+1, gen);
 	btnewscan(&s, pfx, sizeof(pfx));
-	if((e = btenter(&fs->snap, &s)) != nil)
-		return e;
+	btenter(&fs->snap, &s);
+	if(waserror()){
+		btexit(&s);
+		nexterror();
+	}
 	while(1){
-		if((e = btnext(&s, &s.kv)) != nil)
+		if(!btnext(&s, &s.kv))
 			break;
-		if(s.done)
-			break;
 		kv2dlist(&s.kv, &dl);
 
 		if(succ != -1 && dl.bgen <= prev)
-			e = mergedl(succ, dl.gen, dl.bgen);
+			mergedl(succ, dl.gen, dl.bgen);
 		else if(dl.bgen <= prev)
-			e = mergedl(prev, dl.gen, dl.bgen);
+			mergedl(prev, dl.gen, dl.bgen);
 		else
-			e = freedl(&dl, 1);
-		if(e != nil)
-			break;
+			freedl(&dl, 1);
 	}
 	btexit(&s);
-	return e;
 }
 
 /*
@@ -267,10 +265,10 @@
  * If it has one successor and no label, then
  * it will be merged with that successor.
  */
-char*
+void
 delsnap(Tree *t, vlong succ, char *name)
 {
-	char buf[4][Kvmax], *e;
+	char *p, buf[4][Kvmax];
 	int nm, deltree;
 	Mount *mnt;
 	Msg m[4];
@@ -281,13 +279,12 @@
 		if(strcmp(name, "dump") == 0
 		|| strcmp(name, "empty") == 0
 		|| strcmp(name, "adm") == 0)
-			return Ename;
+			error(Ename);
 
 		m[nm].op = Odelete;
 		m[nm].k = buf[nm];
-		if((e = packlbl(buf[nm], sizeof(buf[nm]), name)) == nil)
-			return Ename;
-		m[nm].nk = e - m[nm].k;
+		p = packlbl(buf[nm], sizeof(buf[nm]), name);
+		m[nm].nk = p - m[nm].k;
 		m[nm].v = nil;
 		m[nm].nv = 0;
 		t->nlbl--;
@@ -306,20 +303,16 @@
 		}
 		m[nm].op = Odelete;
 		m[nm].k = buf[nm];
-		if((e = packsnap(buf[nm], sizeof(buf[nm]), t->gen)) == nil)
-			return Ename;
-		m[nm].nk = e - m[nm].k;
+		p = packsnap(buf[nm], sizeof(buf[nm]), t->gen);
+		m[nm].nk = p - m[nm].k;
 		m[nm].v = nil;
 		m[nm].nv = 0;
 		nm++;
 	}
 	assert(nm <= nelem(m));
-	if((e = flushdlcache(1)) != nil)
-		return e;
-	if((e = btupsert(&fs->snap, m, nm)) != nil)
-		return e;
-	if((e = reclaimblocks(t->gen, succ, t->pred)) != nil)
-		return e;
+	flushdlcache(1);
+	btupsert(&fs->snap, m, nm);
+	reclaimblocks(t->gen, succ, t->pred);
 	if(deltree){
 		for(mnt = fs->mounts; mnt != nil; mnt = mnt->next){
 			if(mnt->root->gen == t->succ)
@@ -328,7 +321,6 @@
 				mnt->root->succ = t->succ;
 		}
 	}
-	return nil;
 }
 
 /*
@@ -336,7 +328,7 @@
  * its reference count. This labelled snapshot
  * will show up in the dump.
  */
-char*
+void
 tagsnap(Tree *t, char *name, int mutable)
 {
 	char buf[3][Kvmax];
@@ -347,12 +339,17 @@
 	if(strcmp(name, "dump") == 0
 	|| strcmp(name, "empty") == 0
 	|| strcmp(name, "adm") == 0)
-		return Ename;
+		error(Ename);
 
 	i = 0;
+	n = nil;
+	if(waserror()){
+		free(n);
+		nexterror();
+	}
 	if(mutable){
 		if((n = mallocz(sizeof(Tree), 1)) == nil)
-			return Enomem;
+			error(Enomem);
 		n->memref = 1;
 		n->dirty = 0;
 		n->nlbl = 1;
@@ -387,7 +384,9 @@
 		lbl2kv(name, t->gen, 0, &m[i], buf[i], sizeof(buf[i]));
 		i++;
 	}
-	return btupsert(&fs->snap, m, i);
+	btupsert(&fs->snap, m, i);
+	poperror();
+	free(n);
 }
 
 /*
@@ -397,16 +396,16 @@
  * list; once it's observable by a derived snapshot it must be
  * immutable.
  */
-char*
+void
 updatesnap(Tree **r, Tree *o, char *lbl)
 {
-	char buf[4][Kvmax], *e;
+	char buf[4][Kvmax];
 	Msg m[4];
 	Tree *t;
 	int i;
 
 	if(!o->dirty)
-		return nil;
+		return;
 
 	/* update the old kvp */
 	o->nlbl--;
@@ -415,7 +414,11 @@
 	/* create the new one */
 
 	if((t = mallocz(sizeof(Tree), 1)) == nil)
-		return Enomem;
+		error(Enomem);
+	if(waserror()){
+		free(t);
+		nexterror();
+	}
 	t->memref = 1;
 	t->dirty = 0;
 
@@ -445,10 +448,7 @@
 	m[i].op = Oinsert;
 	lbl2kv(lbl, t->gen, Lmut, &m[i], buf[i], sizeof(buf[i]));
 	i++;
-	if((e = btupsert(&fs->snap, m, i)) != nil){
-		free(t);
-		return e;
-	}
+	btupsert(&fs->snap, m, i);
 
 	/* only update the dirty status after we sync */
 	o->dirty = 0;
@@ -458,7 +458,7 @@
 		delsnap(o, t->gen, nil);
 	closesnap(o);
 	asetp(r, t);
-	return nil;
+	poperror();
 }
 
 /*
@@ -479,7 +479,7 @@
 		return nil;
 	k.k = buf;
 	k.nk = p - buf;
-	if(btlookup(&fs->snap, &k, &kv, buf, sizeof(buf)) != nil)
+	if(!btlookup(&fs->snap, &k, &kv, buf, sizeof(buf)))
 		return nil;
 	assert(kv.nv == 1+8+4);
 	gen = UNPACK64(kv.v + 1);
@@ -488,21 +488,21 @@
 		*mut = !!(flg&Lmut);
 
 	if((t = mallocz(sizeof(Tree), 1)) == nil)
-		goto Error;
-	if((p = packsnap(buf, sizeof(buf), gen)) == nil)
-		goto Error;
+		error(Enomem);
+	if(waserror()){
+		free(t);
+		nexterror();
+	}
+	p = packsnap(buf, sizeof(buf), gen);
 	k.k = buf;
 	k.nk = p - buf;
-	if(btlookup(&fs->snap, &k, &kv, buf, sizeof(buf)) != nil)
-		abort();
-	if(unpacktree(t, kv.v, kv.nv) == nil)
-		abort();
+	if(!btlookup(&fs->snap, &k, &kv, buf, sizeof(buf)))
+		broke(Efs);
+	unpacktree(t, kv.v, kv.nv);
 	t->memref = 1;
 	t->memgen = aincv(&fs->nextgen, 1);
+	poperror();
 	return t;
-Error:
-	free(t);
-	return nil;
 }
 
 /*
@@ -523,16 +523,14 @@
 	limbo(f);
 }
 
-char*
+void
 flushdlcache(int clear)
 {
 	Dlist *dl, *n;
-	char *e;
 
 	for(dl = fs->dlhead; dl != nil; dl = n){
 		n = dl->cnext;
-		if((e = dlsync(dl)) != nil)
-			return e;
+		dlsync(dl);
 		if(clear){
 			if(dl->ins != nil)
 				dropblk(dl->ins);
@@ -544,7 +542,6 @@
 		fs->dltail = nil;
 		memset(fs->dlcache, 0, fs->dlcmax*sizeof(Dlist*));
 	}
-	return nil;
 }
 
 /*
--- a/tree.c
+++ b/tree.c
@@ -654,7 +654,7 @@
 	if(waserror()){
 		efreeblk(t, l);
 		efreeblk(t, r);
-		return;
+		nexterror();
 	}
 	l = newblk(t, b->type);
 	r = newblk(t, b->type);
@@ -752,7 +752,7 @@
 	if(waserror()){
 		efreeblk(t, l);
 		efreeblk(t, r);
-		return;
+		nexterror();
 	}
 	l = newblk(t, b->type);
 	r = newblk(t, b->type);
@@ -794,6 +794,7 @@
 	p->op = POsplit;
 	p->nl = l;
 	p->nr = r;
+	poperror();
 }
 
 static void
@@ -885,7 +886,7 @@
 	if(waserror()){
 		efreeblk(t, l);
 		efreeblk(t, r);
-		return;
+		nexterror();
 	}
 	l = newblk(t, a->type);
 	r = newblk(t, a->type);
@@ -1201,7 +1202,7 @@
 }
 	
 
-char*
+void
 btupsert(Tree *t, Msg *msg, int nmsg)
 {
 	int i, npath, npull, dh, sz, height;
@@ -1219,8 +1220,9 @@
 	npath = 0;
 	if(waserror()){
 		freepath(t, path, npath);
-		return errmsg();
+		nexterror();
 	}
+int eb;
 
 Again:
 	b = getroot(t, &height);
@@ -1227,7 +1229,7 @@
 	if(b->type == Tpivot && !filledbuf(b, nmsg, sz)){
 		fastupsert(t, b, msg, nmsg);
 		poperror();
-		return nil;
+		return;
 	}
 
 	/*
@@ -1237,7 +1239,7 @@
 	 */
 	npath = 0;
 	if((path = calloc((height + 2), sizeof(Path))) == nil)
-		return Enomem;
+		error(Enomem);
 	path[npath].b = nil;
 	path[npath].idx = -1;
 	path[npath].midx = -1;
@@ -1292,7 +1294,6 @@
 	if(npull != nmsg)
 		goto Again;
 	poperror();
-	return nil;
 }
 
 Blk*
@@ -1309,7 +1310,7 @@
 	return getblk(bp, 0);
 }
 
-char*
+int
 btlookup(Tree *t, Key *k, Kvp *r, char *buf, int nbuf)
 {
 	int i, j, h, ok, same;
@@ -1316,15 +1317,12 @@
 	Blk *b, **p;
 	Bptr bp;
 	Msg m;
-	char *err;
 
-	if((b = getroot(t, &h)) == nil)
-		return Efs;
+	b = getroot(t, &h);
 	if((p = calloc(h, sizeof(Blk*))) == nil){
 		dropblk(b);
-		return Enomem;
+		error(Enomem);
 	}
-	err = Esrch;
 	ok = 0;
 	p[0] = holdblk(b);
 	for(i = 1; i < h; i++){
@@ -1331,10 +1329,7 @@
 		if(blksearch(p[i-1], k, r, &same) == -1)
 			break;
 		bp = unpackbp(r->v, r->nv);
-		if((p[i] = getblk(bp, 0)) == nil){
-			err = Efs;
-			goto Out;
-		}
+		p[i] = getblk(bp, 0);
 	}
 	if(p[h-1] != nil)
 		blksearch(p[h-1], k, r, &ok);
@@ -1356,22 +1351,20 @@
 			ok = apply(r, &m, buf, nbuf);
 		}
 	}
-	if(ok)
-		err = nil;
-Out:
 	for(i = 0; i < h; i++)
 		if(p[i] != nil)
 			dropblk(p[i]);
 	dropblk(b);
 	free(p);
-	return err;
+	return ok;
 }
 
 void
 btnewscan(Scan *s, char *pfx, int npfx)
 {
+	memset(s, 0, sizeof(*s));
 	s->first = 1;
-	s->done = 0;
+	s->donescan = 0;
 	s->offset = 0;
 	s->pfx.k = s->pfxbuf;
 	s->pfx.nk = npfx;
@@ -1382,7 +1375,7 @@
 	cpkey(&s->kv, &s->pfx, s->kvbuf, sizeof(s->kvbuf));
 }
 
-char*
+void
 btenter(Tree *t, Scan *s)
 {
 	int i, same;
@@ -1392,13 +1385,12 @@
 	Blk *b;
 	Kvp v;
 
-	if(s->done)
-		return nil;
-	if((b = getroot(t, &s->ht)) == nil)
-		return Eio;
+	if(s->donescan)
+		return;
+	b = getroot(t, &s->ht);
 	if((s->path = calloc(s->ht, sizeof(Scanp))) == nil){
 		dropblk(b);
-		return Enomem;
+		error(Enomem);
 	}
 	p = s->path;
 	p[0].b = b;
@@ -1420,17 +1412,15 @@
 				}
 			}
 			bp = unpackbp(v.v, v.nv);
-			if((b = getblk(bp, 0)) == nil)
-				return Eio;
+			b = getblk(bp, 0);
 			p[i+1].b = b;
 		}else if(p[i].vi == -1 || !same || !s->first)
 			p[i].vi++;
 	}
 	s->first = 0;
-	return nil;
 }
 
-char *
+int
 btnext(Scan *s, Kvp *r)
 {
 	int i, j, h, ok, start, bufsrc;
@@ -1444,8 +1434,12 @@
 	h = s->ht;
 	start = h;
 	bufsrc = -1;
-	if(s->done)
-		return nil;
+	if(s->donescan)
+		return 0;
+	if(waserror()){
+		btexit(s);
+		nexterror();
+	}
 	/* load up the correct blocks for the scan */
 	for(i = h-1; i >= 0; i--){
 		if(p[i].b != nil
@@ -1452,8 +1446,9 @@
 		&&(p[i].vi < p[i].b->nval || p[i].bi < p[i].b->nbuf))
 			break;
 		if(i == 0){
-			s->done = 1;
-			return nil;
+			s->donescan = 1;
+			poperror();
+			return 0;
 		}
 		if(p[i].b != nil)
 			dropblk(p[i].b);
@@ -1468,8 +1463,7 @@
 		for(i = start; i < h; i++){
 			getval(p[i-1].b, p[i-1].vi, &kv);
 			bp = unpackbp(kv.v, kv.nv);
-			if((p[i].b = getblk(bp, 0)) == nil)
-				return "error reading block";
+			p[i].b = getblk(bp, 0);
 		}
 	
 		/* find the minimum key along the path up */
@@ -1491,8 +1485,9 @@
 		}
 	}
 	if(m.nk < s->pfx.nk || memcmp(m.k, s->pfx.k, s->pfx.nk) != 0){
-		s->done = 1;
-		return nil;
+		s->donescan = 1;
+		poperror();
+		return 0;
 	}
 
 	/* scan all messages applying to the message */
@@ -1511,9 +1506,10 @@
 			p[i].bi++;
 		}
 	}
+	poperror();
 	if(!ok)
 		goto Again;
-	return nil;
+	return 1;
 }
 
 void
--- a/user.c
+++ b/user.c
@@ -9,7 +9,7 @@
 static char*
 slurp(Tree *t, vlong path, vlong len)
 {
-	char *e, *ret, buf[Offksz], kvbuf[Offksz + Ptrsz];
+	char *ret, buf[Offksz], kvbuf[Offksz + Ptrsz];
 	vlong o;
 	Blk *b;
 	Bptr bp;
@@ -17,7 +17,7 @@
 	Kvp kv;
 
 	if((ret = malloc(len + 1)) == nil)
-		return Enomem;
+		error(Enomem);
 	k.k = buf;
 	k.nk = Offksz;
 	for(o = 0; o < len; o += Blksz){
@@ -24,11 +24,10 @@
 		k.k[0] = Kdat;
 		PACK64(k.k+1, path);
 		PACK64(k.k+9, o);
-		if((e = btlookup(t, &k, &kv, kvbuf, sizeof(kvbuf))) != nil)
-			return e;
+		if(!btlookup(t, &k, &kv, kvbuf, sizeof(kvbuf)))
+			error(Esrch);
 		bp = unpackbp(kv.v, kv.nv);
-		if((b = getblk(bp, GBraw)) == nil)
-			return Eio;
+		b = getblk(bp, GBraw);
 		if(len - o >= Blksz)
 			memcpy(ret + o, b->buf, Blksz);
 		else
@@ -216,7 +215,7 @@
 		
 }
 
-char*
+void
 loadusers(int fd, Tree *t)
 {
 	char *s, *e;
@@ -224,16 +223,16 @@
 	Qid q;
 
 	if(walk1(t, -1, "", &q, &len) == -1)
-		return Efs;
+		error(Efs);
 	if(walk1(t, q.path, "users", &q, &len) == -1)
-		return Eio;
+		error(Esrch);
 	if(q.type != QTFILE)
-		return Etype;
+		error(Etype);
 	if(len >= 1*MiB)
-		return Efsize;
-	if((s = slurp(t, q.path, len)) == nil)
-		return Eio;
+		error(Efsize);
+	s = slurp(t, q.path, len);
 	e = parseusers(fd, s);
+	if(e != nil)
+		error(e);
 	free(s);
-	return e;
 }