code: mafs

Download patch

ref: 0325bdc87b12a33c16b792c6ed50400b8ffce325
parent: d75fdae03c215393b0c965940684803d29c46996
author: 9ferno <[email protected]>
date: Sat Jan 7 20:02:34 EST 2023

experimenting with the names as a system file

--- a/9p.c
+++ b/9p.c
@@ -2,23 +2,23 @@
 
 Tlock	*tlockhead = nil, *tlocktail = nil;
 QLock	tlock;
+
 extern u32 mpsrvpid;
 extern u8 noauth;
 static u64 lastflushtime = 0;
 static RWLock flushlck;
 
-s32 readfile(u64 dblkno, u64 qpath, char *rbuf, s32 rbufsize, u64 offset);
-s32 writefile(u64 dblkno, u64 qpath, s16 uid, char *wbuf, s32 wbufsize, u64 offset);
-s32 writeallappend(Iobuf *dbuf, u64 dblkno, Iobuf **oldbufp);
-
 Aux*
-newaux(u64 addr, u16 uid)
+newaux(u64 dblkno, u16 uid, u64 pdblkno, u64 pqpath, u64 preli)
 {
 	Aux *a;
 
 	a = emalloc9p(sizeof(Aux));
-	a->dblkno = addr;
+	a->dblkno = dblkno;
 	a->uid = uid;
+	a->pdblkno = pdblkno;
+	a->pqpath = pqpath;
+	a->preli = preli;
 	a->ctlmsg = nil;
 	a->nctlmsg = 0;
 	return a;
@@ -38,7 +38,7 @@
 fsauth(Req *req)
 {
 	if(noauth)
-		respond(req, "no authentication required");
+		respond(req, errstring[Eauthmsg]);
 	else
 		auth9p(req);
 }
@@ -49,27 +49,138 @@
 	short uid;
 	Iobuf *dbuf;
 
-	if(noauth == 0 && authattach(req) < 0){
+	if(noauth == 0 && authattach(req) < 0)
 		return;
-	}
 
-	if((uid = lookupid(req->ifcall.uname)) <= 0){
-		respond(req, "no such user");
+	if(waserror()){
+		responderror(req);
 		return;
 	}
+	if((uid = lookupid(req->ifcall.uname)) <= 0)
+		error(errstring[Enouser]);
 
-	dbuf = getmetachk(Bdroot, Breadonly, Tdentry, Qproot);
-	if(dbuf == nil){
-		respond(req, "phase error");
-		return;
+	dbuf = egetmetachk(Bdroot, Breadonly, Tdentry, Qproot);
+	if(waserror()){
+		putbuf(dbuf, 0);
+		nexterror();
 	}
-	req->fid->aux = newaux(Bdroot, uid);
+
+	req->fid->aux = newaux(Bdroot, uid, 0, 0, 0);
 	req->fid->qid = (Qid){Qproot, dbuf->d->version, QTDIR};
+	poperror();
 	putbuf(dbuf, 0);
 	req->ofcall.qid = req->fid->qid;
+	poperror();
 	respond(req, nil);
 }
 
+static char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+	Aux *aux;
+	Dentry *d, *chd, *p;
+	Iobuf *dbuf, *cbuf, *pbuf;
+	u64 chreli, blkno;
+	s8 v;
+	char err[ERRMAX];
+
+	if(shuttingdown)
+		return nil;
+	if((fid->qid.type&QTDIR) == 0)
+		return errstring[Enotdir];
+
+	if(waserror()){
+		rerrstr(err, ERRMAX);
+		return err;
+	}
+
+	aux = fid->aux;
+	if(strcmp(name, "..") == 0){
+		switch(fid->qid.path){
+		case Qproot:
+			return nil;
+		default:
+			if(chatty9p > 1)
+				dprint("fswalk1 .. fid->qid.path %llud aux->dblkno %llud aux->pdblkno %llud\n",
+						fid->qid.path, aux->dblkno, aux->pdblkno);
+			pbuf = egetmetachk(aux->pdblkno, Breadonly, Tdentry, aux->pqpath);
+
+			p = pbuf->d;
+			*qid = (Qid){p->path, p->version, (p->mode&DMDIR) ? QTDIR : QTFILE};
+			if(aux != nil){
+				aux->dblkno = pbuf->blkno; /* preli in aux will be wrong, issue? */
+				aux->pdblkno = p->pdblkno;
+				aux->pqpath = p->pqpath;
+				aux->preli = p->preli;
+			}
+			putbuf(pbuf, 0);
+			fid->qid = *qid;
+			poperror();
+			return nil;
+		}
+	}
+
+	/* assuming that it will ever be here only for directories */
+		/* some directory, find the child with name or idx */
+	dbuf = egetmetachk(aux->dblkno, Breadonly, Tdentry, fid->qid.path);
+	if(waserror()){
+		putbuf(dbuf, 0);
+		nexterror();
+	}
+	d = dbuf->d;
+
+	if(canaccess(aux->uid, d, DMEXEC) == 0)
+		error(errstring[Eperm]);
+
+	v = searchnames(d, name, &chreli);
+	if(v < 0)
+		error(errstring[Einvread]);
+	if(v == 0)
+		error(errstring[Enotfound]);
+
+	if((blkno = rel2abs(d, chreli)) == 0)
+		error(errstring[Ephase]);
+
+	cbuf = egetmeta(blkno, Breadonly, Bused);
+	if(waserror()){
+		putbuf(cbuf, 0);
+		nexterror();
+	}
+	chd = cbuf->d;
+	checktag(cbuf, 1, Tdentry, chd->qpath);
+	if((chd->mode&DMDIR) && canaccess(aux->uid, chd, DMEXEC) == 0)
+		error(errstring[Eperm]);
+
+	*qid = (Qid){chd->path, chd->version, (chd->mode&DMDIR) ? QTDIR : QTFILE};
+	if(aux != nil){
+		aux->dblkno = blkno;
+		aux->pdblkno = chd->pdblkno;/* or, aux->dblkno */
+		aux->pqpath = chd->pqpath;	/* or, d->pqpath */
+		aux->preli = chreli;		/* or, chd->preli */
+	}
+	poperror();
+	putbuf(cbuf, 0);
+	poperror();
+	putbuf(dbuf, 0);
+	fid->qid = *qid;
+	poperror();
+	return nil;
+}
+
+static char*
+fsclone(Fid *oldfid, Fid *newfid)
+{
+	Aux *o;
+
+	if(shuttingdown)
+		return nil;
+	o = oldfid->aux;
+	if(o == nil)
+		return "bad fid";
+	newfid->aux = newaux(o->dblkno, o->uid, o->pdblkno, o->pqpath, o->preli);
+	return nil;
+}
+
 static void
 fsdestroyfid(Fid *fid)
 {
@@ -125,7 +236,7 @@
 }
 
 static void
-fsmkdir(Dentry *d, Dir *dir, char *buf, u64 appendsize)
+fsmkdir(Dentry *d, Dir *dir, char *buf, u64 appendsize, s8 *name, u16 namelen)
 {
 	memset(dir, 0, sizeof(*dir));
 	dir->qid = (Qid){d->path, d->version, (d->mode&DMDIR)? QTDIR : QTFILE};
@@ -136,17 +247,18 @@
 	if(dir->qid.type & QTDIR)
 		dir->length = 0;
 	if(buf == nil){
-		dir->name = estrdup9p(d->name);
+		dir->name = emalloc9p((long)namelen+1);	/* TODO is this a leak? */
+		strncpy(dir->name, name, (long)namelen);
 		dir->uid = username(d->uid, dir->uid);
 		dir->gid = username(d->gid, dir->gid);
 		dir->muid = username(d->muid, dir->muid);
 	}else{
-		memset(buf, 0, Namelen + 3 * Userlen);
-		strncpy(buf, d->name, Namelen - 1);
+		memset(buf, 0,  (long)namelen+1 + 3 * (Userlen+1));
+		strncpy(buf, name, (long)namelen);
 		dir->name = buf;
-		dir->uid = username(d->uid, buf + Namelen);
-		dir->gid = username(d->gid, buf + Namelen + Userlen);
-		dir->muid = username(d->muid, buf + Namelen + 2 * Userlen);
+		dir->uid = username(d->uid, buf + namelen+1);
+		dir->gid = username(d->gid, buf + namelen+1 + Userlen+1);
+		dir->muid = username(d->muid, buf + namelen+1 + 2 * (Userlen+1));
 	}
 }
 
@@ -153,27 +265,166 @@
 static void
 fsstat(Req *req)
 {
-	Dentry *d;
-	Iobuf *dbuf;
+	Dentry *d, *pd;
+	Iobuf *dbuf, *pdbuf;
+	Aux *aux;
+	s8 *name;
+	u16 namelen;
 
-	dbuf = getmetachk(((Aux*)req->fid->aux)->dblkno, Breadonly,
-						Tdentry, req->fid->qid.path);
-	if(dbuf == nil){
-		dprint("fsstat dbuf == nil dblkno %llud qpath %llud\n",
-				((Aux*)req->fid->aux)->dblkno, req->fid->qid.path);
-		respond(req, errstring[Ephase]);
+	name = nil;
+	pdbuf = dbuf = nil;
+	pd = nil;
+	if(waserror()){
+		responderror(req);
 		return;
 	}
+	aux = (Aux*)req->fid->aux;
+	if(req->fid->qid.path != Qproot){
+		pdbuf = egetmetachk(aux->pdblkno, Breadonly, Tdentry, aux->pqpath);
+		if(waserror()){
+			putbuf(dbuf, 0);
+			nexterror();
+		}
+		pd = pdbuf->d;
+	}
 
+	dbuf = egetmetachk(aux->dblkno, Breadonly,
+						Tdentry, req->fid->qid.path);
+	if(waserror()){
+		putbuf(dbuf, 0);
+		nexterror();
+	}
 	d = dbuf->d;
+
 	/* nothing to do for already zero'ed out slots */
-	if(d->path != Qpnone)
-		fsmkdir(d, &req->d, nil, dbuf->appendsize);
+	if(d->path == Qpnone){
+		error(errstring[Ephase]);
+/*	}else if(d->flags&Dsys){
+		error(errstring[Esys]); */
+	}else{
+		if(req->fid->qid.path == Qproot){
+			name = emalloc9p(2);
+			name[0] = '/';
+			name[1] = '\0';
+			namelen = (u16)1;
+		}else{
+			readname(pd, d->preli, &name, &namelen);
+		}
+		fsmkdir(d, &req->d, nil, dbuf->appendsize, name, namelen);
+	}
 
+	if(name)
+		free(name);
+	poperror();
 	putbuf(dbuf, 0);
+	if(pdbuf){
+		poperror();
+		putbuf(pdbuf, 0);
+	}
+	poperror();
 	respond(req, nil);
 }
 
+static void
+fsread(Req *req)
+{
+	s32 n;
+	Iobuf *dbuf, *cbuf;
+	Dentry *d, *ch;
+	s8 *sbuf, *name;
+	Dir dir;
+	Aux *aux;
+	u64 blkno;
+	u16 namelen;
+
+	if(waserror()){
+		responderror(req);
+		return;
+	}
+
+	if(shuttingdown)
+		error(errstring[Eshutdown]);
+
+	if(req->fid->qid.type == QTAUTH){
+		authread(req);
+		return;
+	}else if(req->fid->qid.path == Qpctl){
+		ctlread(req);
+		return;
+	}
+	aux = (Aux*)req->fid->aux;
+	if((req->fid->qid.type & QTDIR) != 0){
+		/* using this loop to skip over zero'ed out blocks or system files */
+
+		dbuf = egetmetachk(aux->dblkno, Breadonly, Tdentry, req->fid->qid.path);
+		if(waserror()){
+			putbuf(dbuf, 0);
+			nexterror();
+		}
+		d = dbuf->d;
+
+		if(canaccess(aux->uid, d, DMEXEC) == 0)
+			error(errstring[Eaccess]);
+
+		cbuf = nil;
+		if(aux->dri < Nsys)
+			aux->dri = Nsys;
+		do{
+			if(cbuf){
+				poperror();
+				putbuf(cbuf, 0);
+			}
+			if((blkno = rel2abs(d, aux->dri++)) == 0){
+				poperror();
+				putbuf(dbuf, 0);
+				req->ofcall.offset = req->ifcall.offset;
+				req->ofcall.count = 0;
+				aux->dri = 0;
+				poperror();
+				respond(req, nil);
+				return;
+			}
+
+			cbuf = egetmeta(blkno, Breadonly, Bused);
+			if(waserror()){
+				putbuf(cbuf, 0);
+				nexterror();
+			}
+			ch = cbuf->d;
+			checktag(cbuf, 1, Tdentry, ch->path);
+
+		}while(ch->flags&Dsys || ch->tag == Tdentry && ch->path == Qpnone);
+
+		readname(d, ch->preli, &name, &namelen);
+		sbuf = emalloc9p(namelen + 1+ 3*(Userlen+1));
+		fsmkdir(ch, &dir, sbuf, cbuf->appendsize, name, namelen);
+		req->ofcall.count = n = convD2M(&dir, (u8*)req->ofcall.data, req->ifcall.count);
+		free(sbuf);
+		req->ofcall.offset = req->ifcall.offset+n;
+		if(n == 0)
+			aux->dri = 0;
+		if(cbuf){
+			poperror();
+			putbuf(cbuf, 0);
+		}
+		if(dbuf){
+			poperror();
+			putbuf(dbuf, 0);
+		}
+		poperror();
+		respond(req, nil);
+		return;
+	}
+
+	n = readfile(aux->dblkno, req->fid->qid.path,
+				 req->ofcall.data, req->ifcall.count,
+				 req->ifcall.offset);
+	req->ofcall.count = n;
+	req->ofcall.offset = req->ifcall.offset+n;
+	poperror();
+	respond(req, nil);
+}
+
 int
 emptystr(char *s)
 {
@@ -187,70 +438,76 @@
 static void
 fswstat(Req *req)
 {
-	Dentry *d;
-	Iobuf *dbuf;
+	Dentry *d, *pd;
+	Iobuf *dbuf, *pdbuf;
 	s16 gid;
+	Aux *aux;
+	u8 namechange;
 
-	if(shuttingdown){
-		respond(req, errstring[Eshutdown]);
+	if(waserror()){
+		responderror(req);
 		return;
 	}
-	if(readonly){
-		respond(req, errstring[Eronly]);
-		return;
-	}
-	if(req->fid->qid.path == Qpctl){
-		respond(req, errstring[Einval]);
-		return;
-	}
-	if((req->d.type & QTDIR) > 0 && req->d.length != ~0){
-		respond(req, errstring[Einval]);
-		return;
-	}
-	if(((Aux*)req->fid->aux)->uid == None)
-		goto noperm;
 
+	if(shuttingdown)
+		error(errstring[Eshutdown]);
+	if(readonly)
+		error(errstring[Eronly]);
+	if(req->fid->qid.path == Qpctl)
+		error(errstring[Einval]);
+	if((req->d.type & QTDIR) > 0 && req->d.length != ~0)
+		error(errstring[Einval]);
+
+	aux = (Aux*)req->fid->aux;
+	if(aux->uid == None)
+		error(errstring[Eperm]);
+
 	/* TODO allow changing the length as per stat(5) */
 	if(req->d.atime != ~0 ||  req->d.length  != ~0)
-		goto noperm;
+		error(errstring[Eperm]);
 
 	/* stat(5) says that this is illegal */
-	if(emptystr(req->d.uid) == 0){
-		respond(req, errstring[Einval]);
-		return;
-	}
+	if(emptystr(req->d.uid) == 0)
+		error(errstring[Einval]);
 
 	/* TODO cwfs/9p2.c, hjfs/fs2.c and kfs64.b do more here. Get that stuff in. */
+	pdbuf = nil; pd = nil;
+	if(emptystr(req->d.name) == 0){
+		namechange = 1;
+		pdbuf = egetmetachk(aux->pdblkno, Bwritable,
+							Tdentry, aux->pqpath);
+		if(waserror()){
+			putbuf(pdbuf, 0);
+			nexterror();
+		}
+		pd = pdbuf->d;
+	}else
+		namechange = 0;
 
-	dbuf = getmetachk(((Aux*)req->fid->aux)->dblkno, Bwritable,
+	dbuf = egetmetachk(aux->dblkno, Bwritable,
 						Tdentry, req->fid->qid.path);
-	if(dbuf == nil){
-		respond(req, errstring[Eperm]);
-		return;
-	}
-	d = dbuf->d;
-	if(canaccess(((Aux*)req->fid->aux)->uid, d, DMWRITE) == 0){
+	if(waserror()){
 		putbuf(dbuf, 0);
-		respond(req, errstring[Eaccess]);
-		return;
+		nexterror();
 	}
+	d = dbuf->d;
+	if(canaccess(aux->uid, d, DMWRITE) == 0)
+		error(errstring[Eaccess]);
+
 	/* invalid to change the directory bit stat(5) */
 	if(req->d.mode != ~0 &&
-			(req->d.mode&DMDIR) != (d->mode&DMDIR)){
-		putbuf(dbuf, 0);
-		respond(req, errstring[Einval]);
-		return;
-	}
-	if(emptystr(req->d.name) == 0){
-		strncpy(d->name, req->d.name, Namelen);
-		d->mtime = nsec();
-		d->muid = ((Aux*)req->fid->aux)->uid;
-	}
-	if(d->uid == ((Aux*)req->fid->aux)->uid ||
-		leadgroup(((Aux*)req->fid->aux)->uid, d->gid) == 1){
+			(req->d.mode&DMDIR) != (d->mode&DMDIR))
+		error(errstring[Einval]);
+	if(d->uid == aux->uid ||
+		leadgroup(aux->uid, d->gid) == 1){
+		if(namechange){
+			d->muid = aux->uid;
+			updatename(pd, aux->uid, aux->preli, req->d.name); /* TODO check status */
+			d->mtime = nsec();
+		}
 		if(req->d.mtime != ~0){
 			d->mtime = req->d.mtime*Nsec;
-			d->muid = ((Aux*)req->fid->aux)->uid;
+			d->muid = aux->uid;
 		}
 		if(req->d.mode != ~0 &&
 			(req->d.mode&DMDIR) == (d->mode&DMDIR) &&
@@ -264,110 +521,57 @@
 		}
 	}
 
+	poperror();
 	putbuf(dbuf, 1);
+	if(pdbuf){
+		poperror();
+		putbuf(pdbuf, 0);	/* no changes to the parent directory entry per-se */
+	}
+	poperror();
 	respond(req, nil);
-	return;
-noperm:
-	respond(req, errstring[Eperm]);
 	return;	
 }
 
 static void
-fsread(Req *req)
-{
-	s32 n;
-	Iobuf *dbuf, *cbuf;
-	Dentry *d;
-	char nbuf[Namelen + 3 * Userlen];
-	Dir dir;
-
-	if(shuttingdown){
-		respond(req, errstring[Eshutdown]);
-		return;
-	}
-	if(req->fid->qid.type == QTAUTH){
-		authread(req);
-		return;
-	}else if(req->fid->qid.path == Qpctl){
-		ctlread(req);
-		return;
-	}
-	if((req->fid->qid.type & QTDIR) != 0){
-		/* using this loop to skip over zero'ed out blocks */
-		dbuf = cbuf = nil;
-		do{
-			if(dbuf != nil)
-				freesearchstate(&dbuf, &cbuf);
-			/* some directory, find the child at offset */
-			d = searchdir(((Aux*)req->fid->aux)->dblkno, req->fid->qid.path,
-							((Aux*)req->fid->aux)->uid, nil,
-							((Aux*)req->fid->aux)->dri, &dbuf, &cbuf);
-			if(d == nil){
-				req->ofcall.offset = req->ifcall.offset;
-				req->ofcall.count = 0;
-				((Aux*)req->fid->aux)->dri = 0;
-				respond(req, nil);
-				return;
-			}
-			((Aux*)req->fid->aux)->dri++;
-		}while(d->path == 0);
-
-		fsmkdir(d, &dir, nbuf, cbuf->appendsize);
-		req->ofcall.count = n = convD2M(&dir, (u8*)req->ofcall.data, req->ifcall.count);
-		req->ofcall.offset = req->ifcall.offset+n;
-		if(n == 0)
-			((Aux*)req->fid->aux)->dri = 0;
-		freesearchstate(&dbuf, &cbuf);
-		respond(req, nil);
-		return;
-	}
-
-	n = readfile(((Aux*)req->fid->aux)->dblkno, req->fid->qid.path,
-				 req->ofcall.data, req->ifcall.count,
-				 req->ifcall.offset);
-	req->ofcall.count = n;
-	req->ofcall.offset = req->ifcall.offset+n;
-	respond(req, nil);
-}
-
-static void
 fswrite(Req *req)
 {
 	s32 rv;
 
-	if(shuttingdown){
-		respond(req, errstring[Eshutdown]);
+	if(waserror()){
+		responderror(req);
 		return;
 	}
-	if(readonly){
-		respond(req, errstring[Eronly]);
-		return;
-	}
+
+	if(shuttingdown)
+		error(errstring[Eshutdown]);
+	if(readonly)
+		error(errstring[Eronly]);
 	if(req->fid->qid.type == QTAUTH){
 		authwrite(req);
+		poperror();
 		return;
 	}else if(req->fid->qid.path == Qpctl){
 		ctlwrite(req);
+		poperror();
 		return;
-	}else if(req->fid->qid.path < Qproot){
-		respond(req, errstring[Eaccess]);
-		return;
-	}
-	if((req->fid->qid.type & QTDIR) != 0){
-		respond(req, errstring[Einval]);
-		return;
-	}
+	}else if(req->fid->qid.path < Qproot)
+		error(errstring[Eaccess]);
+
+	if((req->fid->qid.type & QTDIR) != 0)
+		error(errstring[Einval]);
+
 	rv = writefile(((Aux*)req->fid->aux)->dblkno, req->fid->qid.path,
 					((Aux*)req->fid->aux)->uid,
 					req->ifcall.data, req->ifcall.count,
 					req->ifcall.offset);
 	if(rv == -3)
-		respond(req, errstring[Efull]);
+		error(errstring[Efull]);
 	else if(rv == -1 || rv == -2)
-		respond(req, errstring[Ephase]);
+		error(errstring[Ephase]);
 	else{
 		req->ofcall.count = rv;
 		req->ofcall.offset = req->ifcall.offset+rv;
+		poperror();
 		respond(req, nil);
 	}
 	if(shuttingdown == 0 &&
@@ -382,16 +586,19 @@
 {
 	Fid *fid;
 	Aux *aux;
+	Iobuf *pdbuf;
+	Dentry *pd;
 
-	if(shuttingdown){
-		respond(req, errstring[Eshutdown]);
+	if(waserror()){
+		responderror(req);
 		return;
 	}
-	if(readonly){
-		respond(req, errstring[Eronly]);
-		return;
-	}
 
+	if(shuttingdown)
+		error(errstring[Eshutdown]);
+	if(readonly)
+		error(errstring[Eronly]);
+
 	fid = req->fid;
 	aux = fid->aux;
 	if(aux == nil ||
@@ -398,93 +605,27 @@
 		aux->uid == None ||
 		fid->qid.path < Qproot ||
 		aux->dblkno == 0){
-		respond(req, errstring[Eperm]);
-		return;
+		error(errstring[Eperm]);
 	}
 
+	pdbuf = egetmetachk(aux->pdblkno, Bwritable, Tdentry, aux->pqpath);
+	if(waserror()){
+		putbuf(pdbuf, 0);
+		nexterror();
+	}
+	pd = pdbuf->d;
+	rmname(pd, aux->uid, aux->preli);
+
 	if(fid->qid.type & QTDIR)
 		rmdirectory(fid->qid.path, aux->dblkno);
 	else
 		rmfile(fid->qid.path, aux->dblkno);
+	poperror();
+	putbuf(pdbuf, 0);
+	poperror();
 	respond(req, nil);
 }
 
-static char*
-fswalk1(Fid *fid, char *name, Qid *qid)
-{
-	Aux *aux;
-	Dentry *d, *p;
-	Iobuf *dbuf, *pbuf, *cbuf;
-
-	if(shuttingdown){
-		return nil;
-	}
-	if((fid->qid.type&QTDIR) == 0)
-		return errstring[Enotdir];
-
-	aux = fid->aux;
-	if(strcmp(name, "..") == 0){
-		switch(fid->qid.path){
-		case Qproot:
-			return nil;
-		default:
-			if(chatty9p > 1)
-				dprint("fswalk1 .. fid->qid.path %llud aux->dblkno %llud\n",
-						fid->qid.path, aux->dblkno);
-			dbuf = getmetachk(aux->dblkno, Breadonly, Tdentry, fid->qid.path);
-			if(dbuf == nil)
-				return errstring[Ephase];
-			d = dbuf->d;
-			if(chatty9p > 1)
-				dprint("fswalk1 .. d->name %s d->qid.path %llud d->pdblkno %llud\n",
-						d->name, d->path, d->pdblkno);
-			pbuf = getmetachk(d->pdblkno, Breadonly, Tdentry, d->pqpath);
-			if(pbuf == nil)
-				return errstring[Ephase];
-			putbuf(dbuf, 0);
-			p = pbuf->d;
-			*qid = (Qid){p->path, p->version, (p->mode&DMDIR) ? QTDIR : QTFILE};
-			if(aux != nil)
-				aux->dblkno = pbuf->blkno;
-			putbuf(pbuf, 0);
-			fid->qid = *qid;
-			return nil;
-		}
-	}
-
-	/* assuming that it will ever be here only for directories */
-		/* some directory, find the child with name or idx */
-		d = searchdir(aux->dblkno, fid->qid.path,
-						aux->uid, name, 0, &dbuf, &cbuf);
-		if(d == nil)
-			return "directory entry not found";
-		if((d->mode&DMDIR) && canaccess(d->uid, d, DMEXEC) == 0){
-			freesearchstate(&dbuf, &cbuf);
-			return errstring[Eperm];
-		}
-		*qid = (Qid){d->path, d->version, (d->mode&DMDIR) ? QTDIR : QTFILE};
-		if(aux != nil)
-			aux->dblkno = cbuf->blkno;
-		freesearchstate(&dbuf, &cbuf);
-		fid->qid = *qid;
-		return nil;
-}
-
-static char*
-fsclone(Fid *oldfid, Fid *newfid)
-{
-	Aux *o;
-
-	if(shuttingdown){
-		return nil;
-	}
-	o = oldfid->aux;
-	if(o == nil)
-		return "bad fid";
-	newfid->aux = newaux(o->dblkno, o->uid);
-	return nil;
-}
-
 /*
  * error(Eperm) if open permission not granted for up->newr.
  */
@@ -512,20 +653,23 @@
 static void
 fscreate(Req *req)
 {
-	Iobuf *dbuf, *cbuf;
+	Iobuf *dbuf, *cbuf, *iobufs[3];
 	u64 reli, blkno;
 	Dentry *dchild, *dparent;
-	u64 zblkno;	/* zero'ed dentry that can be reused */
+	u64 qpath;	/* zero'ed dentry that can be reused */
 	Aux *aux;
 	u32 perm;
 	Fid *fid;
 	Tlock *t;
 
-	/* no create's when shutting down */
-	if(shuttingdown){
-		respond(req, errstring[Eshutdown]);
+	if(waserror()){
+		responderror(req);
 		return;
 	}
+
+	if(shuttingdown)
+		error(errstring[Eshutdown]);
+
 	fid = req->fid;
 	aux = fid->aux;
 	if(chatty9p >2)
@@ -534,51 +678,33 @@
 	if(aux == nil ||
 		aux->uid == None ||
 		fid->qid.path < Qproot ||
-		aux->dblkno == 0){
-		respond(req, errstring[Eaccess]);
-		return;
-	}
+		aux->dblkno == 0)
+		error(errstring[Eaccess]);
 
-	if(strlen(req->ifcall.name) > Namelen){
-		respond(req, errstring[Etoolong]);
-		return;
-	}
-	if((fid->qid.type & QTDIR) == 0){
-		respond(req, errstring[Enotdir]);
-		return;
-	}
+	if((fid->qid.type & QTDIR) == 0)
+		error(errstring[Enotdir]);
+
 	if(req->ifcall.name == nil ||
 		strlen(req->ifcall.name) == 0 ||
 		req->ifcall.name[0] == '/' ||
-		checkname9p2(req->ifcall.name) == 0){
-		respond(req, errstring[Ebadname]);
-		return;
-	}
-	if(readonly){
-		respond(req, errstring[Eaccess]);
-		return;
-	}
+		checkname9p2(req->ifcall.name) == 0)
+		error(errstring[Ebadname]);
 
+	if(readonly)
+		error(errstring[Eronly]);
+
 	perm = req->ifcall.perm;
-	cbuf = nil;
-	USED(cbuf);
-	zblkno = 0;
-	dbuf = getmetachk(aux->dblkno, Bwritable, Tdentry, fid->qid.path);
-	if(dbuf == nil){
-		respond(req, errstring[Ephase]);
-		return;
+	dbuf = egetmetachk(aux->dblkno, Bwritable, Tdentry, fid->qid.path);
+	if(waserror()){
+		putbuf(dbuf, 0);
+		nexterror();
 	}
 	dparent = dbuf->d;
-	if(canaccess(((Aux*)req->fid->aux)->uid, dparent, DMWRITE) == 0){
-		respond(req, errstring[Eperm]);
-		return;
-	}
+	if(canaccess(((Aux*)req->fid->aux)->uid, dparent, DMWRITE) == 0)
+		error(errstring[Eperm]);
 
-	if(canaccess(aux->uid, dparent, DMWRITE) == 0){
-		putbuf(dbuf, 0);
-		respond(req, errstring[Eaccess]);
-		return;
-	}
+	if(canaccess(aux->uid, dparent, DMWRITE) == 0)
+		error(errstring[Eaccess]);
 
 	fid->omode = OREAD;
 	switch(req->ifcall.mode & 7) {
@@ -593,156 +719,145 @@
 			fid->omode = OREAD+OWRITE;
 			break;
 		default:
-			putbuf(dbuf, 0);
-			respond(req, errstring[Emode]);
-			return;
+			error(errstring[Emode]);
 	}
-	for(reli = 0, blkno = 1; blkno > 0; reli++){
-		blkno = rel2abs(dparent, reli);
-		/* if(reli >= Max)
-			error(Etoobig); */
+	if(searchnames(dparent, req->ifcall.name, &reli)){
+		/* found with the same name */
+		blkno = rel2abs(dparent, reli);	
 
-		if(blkno == 0){
-			/* end reached, nothing found, create */
-			if(zblkno != 0){
-				cbuf = getmetachk(zblkno, Bwritable, Tdentry, Qpnone);
-				if(cbuf == nil){
-					putbuf(dbuf, 0);
-					respond(req, errstring[Ephase]);
-					return;
-				}
-			}else{
-				cbuf = allocmeta(Tdentry, fid->qid.path);
-				if(cbuf == nil){
-					putbuf(dbuf, 0);
-					respond(req, errstring[Efull]);
-					return;
-				}
-			}
-			dchild = cbuf->d;
-			dchild->size = 0;
-			dchild->pdblkno = dbuf->blkno;
-			dchild->pqpath = dparent->path;
-			dchild->mtime = nsec();
-			dchild->uid = dchild->muid = aux->uid;
-			dchild->gid = dparent->gid;
-			/* TODO DMAPPEND attributes */
-			if(perm&DMDIR){
-				if((req->ifcall.mode & OTRUNC) || (perm & DMAPPEND) ||
-					(fid->omode & OWRITE)){
-					putbuf(cbuf, 1);
-					putbuf(dbuf, 0);
-					respond(req, errstring[Eaccess]);
-					return;
-				}
-			}
-			if(perm&DMDIR)
-				dchild->mode = DMDIR | (perm & (~0777 | (dparent->mode & 0777)));
-			else
-				dchild->mode = perm & (~0666 | (dparent->mode & 0666));
-			if(perm&DMAPPEND)
-				dchild->mode |= DMAPPEND;
-			if(perm&DMEXCL)
-				dchild->mode |= DMEXCL;
-			if(canaccess(aux->uid, dchild, DMWRITE) == 0){
-				putbuf(cbuf, 0);
-				putbuf(dbuf, 0);
-				respond(req, errstring[Eaccess]);
-				return;
-			}
-			dchild->qpath = dchild->path = newqpath();
-			dchild->version = 0;
-			dchild->namelen = strlen(req->ifcall.name);
-			strncpy(dchild->name, req->ifcall.name, Namelen);
-			fid->qid = (Qid){dchild->path, 0, (perm&DMDIR) ? QTDIR : QTFILE};
-			aux->dblkno = cbuf->blkno;
-			aux->dri = 0;
-			if(perm&DMEXCL){
-				t = emalloc9p(sizeof(Tlock));
-				t->time = nsec();
-				t->qpath = dchild->path;
-				t->dblkno = aux->dblkno;
-				qlock(&tlock);
-				if(tlocktail == nil){
-					tlocktail = tlockhead = t;
-				}else{
-					tlocktail->next = t;
-					t->prev = tlocktail;
-					tlocktail = t;
-				}
-				qunlock(&tlock);
-				aux->tlocked = 1;
-			}else
-				aux->tlocked = 0;
-			req->ofcall.qid = fid->qid;
-			req->ofcall.iounit = Iounit;
-			putbuf(cbuf, 1);	/* save Iobuf of the content */
-			/*
-				only add it to the directory dentry if we are adding a new dentry block
-				if we are reusing a zero'ed out slot, it already exists in the
-				directory dentry
-			 */
-			if(zblkno == 0 &&
-				addrelative(dparent, dbuf->blkno, reli, aux->dblkno) == 0){
-				putbuf(dbuf, 1);
-				respond(req, errstring[Ephase]);
-				return;
-			}
-			putbuf(dbuf, 1);
-			respond(req, nil);
-			return;
-		}else{
-			cbuf = getmeta(blkno, Breadonly, Bused);
-			if(cbuf == nil){
-				putbuf(dbuf, 0);
-				respond(req, errstring[Ephase]);
-				return;
-			}
-			dchild = cbuf->d;
+		cbuf = egetmeta(blkno, Breadonly, Bused);
+		if(waserror()){
+			putbuf(cbuf, 0);
+			nexterror();
+		}
+		dchild = cbuf->d;
 
-			/* nothing to do for already zero'ed out slots */
-			if(dchild->qpath == Qpnone){
-				if(zblkno == 0)
-					zblkno = cbuf->blkno;
-				goto Nextdentry;
-			}
+		if(dchild->qpath == Qpnone || dchild->tag != Tdentry)
+			error(errstring[Ephase]);
 
-			if(dchild->tag != Tdentry){
-				putbuf(cbuf, 0);
-				putbuf(dbuf, 0);
-				respond(req, errstring[Ephase]);
-				return;
-			}
+		/* req->ifcall.name matched, truncate file and use it */
+		if(permcheck(dchild->uid, dchild->gid, aux->uid, perm, ORDWR) == 0)
+			error(errstring[Eperm]);
+		dchild->muid = aux->uid;
+		fid->qid = (Qid){dchild->qpath, dchild->version,
+							(req->ifcall.mode&DMDIR) ? QTDIR : QTFILE};
+		aux->dblkno = blkno;
+		aux->pdblkno = dchild->pdblkno;
+		aux->pqpath = dchild->pqpath;
+		aux->preli = reli;
+		aux->dri = 0;
+		req->ofcall.qid = fid->qid;
+		req->ofcall.iounit = Iounit;
+		/*
+			save Iobuf of the child so truncate can open
+			it with a wlock(), if needed
+		 */
+		poperror();
+		putbuf(cbuf, 0);
+		if(req->ofcall.qid.path >= Qpusers &&
+			req->ofcall.qid.type == QTFILE)
+			truncatefile(req->ofcall.qid.path, blkno, aux->uid);
+		poperror();
+		putbuf(dbuf, 0);
+		poperror();
+		respond(req, nil);
+		return;
+	}
 
-			if(strcmp(req->ifcall.name, dchild->name) == 0){
-				/* req->ifcall.name matched, truncate file and use it */
-				if(permcheck(dchild->uid, dchild->gid, aux->uid, perm, ORDWR) == 0){
-					respond(req, errstring[Eperm]);
-					return;
-				}
-				dchild->muid = aux->uid;
-				fid->qid = (Qid){dchild->qpath, dchild->version,
-									(req->ifcall.mode&DMDIR) ? QTDIR : QTFILE};
-				aux->dblkno = blkno;
-				aux->dri = 0;
-				req->ofcall.qid = fid->qid;
-				req->ofcall.iounit = Iounit;
-				/*
-					save Iobuf of the child so truncate can open
-					it with a wlock(), if needed
-				 */
-				putbuf(cbuf, 0);
-				if(req->ofcall.qid.path >= Qpusers &&
-					req->ofcall.qid.type == QTFILE)
-					truncatefile(req->ofcall.qid.path, blkno, aux->uid);
-				putbuf(dbuf, 0);
-				respond(req, nil);
-				return;
-			}
-			Nextdentry:
-				putbuf(cbuf, 0);
+	/* add a new one at reli */
+	/* nothing found, create */
+	/* TODO if(reli >= Maxreli)
+			error(errstring[Etoobig]); */
+
+	if(perm&DMDIR){
+		if((req->ifcall.mode & OTRUNC) ||
+			(perm & DMAPPEND) ||
+			(fid->omode & OWRITE))
+			error(errstring[Einval]);
+	}
+
+	blkno = rel2abs(dparent, reli);	
+	qpath = newqpaths((perm&DMDIR)?4:1);
+	if(blkno == 0){
+		cbuf = allocmeta(Tdentry, qpath);
+		if(waserror()){
+			freeblockbuf(cbuf);
+			nexterror();
 		}
+		blkno = cbuf->blkno;
+		/* only add it to the directory dentry if we are adding a new dentry block
+			if we are reusing a zero'ed out slot, it already exists in the
+			directory dentry */
+		addrelative(dparent, dbuf->blkno, reli, cbuf->blkno);
+	}else{
+		cbuf = egetmetachk(blkno, Bwritable, Tdentry, Qpnone);
+		if(waserror()){
+			putbuf(cbuf, 0);
+			nexterror();
+		}
+		if(canaccess(aux->uid, cbuf->d, DMWRITE) == 0)
+			error(errstring[Eaccess]);
 	}
+	if(addname(dparent, aux->uid, reli, req->ifcall.name) == 0)
+		error(errstring[Ephase]);
+
+	if(perm&DMDIR &&
+		allocdentries(3, iobufs, qpath+1) < 3)
+		error(errstring[Efull]);
+	cbuf->d->qpath = cbuf->d->path = qpath;
+
+	dchild = cbuf->d;
+	dchild->size = 0;
+	dchild->pdblkno = dbuf->blkno;
+	dchild->pqpath = dparent->path;
+	dchild->preli = reli;
+	dchild->mtime = nsec();
+	dchild->uid = dchild->muid = aux->uid;
+	dchild->gid = dparent->gid;
+	dchild->version = 0;
+	if(perm&DMDIR){
+		dchild->mode = DMDIR | (perm & (~0777 | (dparent->mode & 0777)));
+		newnames(dchild, aux->uid, blkno, iobufs);
+	}else
+		dchild->mode = perm & (~0666 | (dparent->mode & 0666));
+
+	/* TODO DMAPPEND attributes? */
+	if(perm&DMAPPEND)
+		dchild->mode |= DMAPPEND;
+	if(perm&DMEXCL)
+		dchild->mode |= DMEXCL;
+	fid->qid = (Qid){dchild->path, 0, (perm&DMDIR) ? QTDIR : QTFILE};
+	aux->dblkno = cbuf->blkno;
+	aux->pdblkno = dchild->pdblkno;
+	aux->pqpath = dchild->pqpath;
+	aux->preli = reli;
+	aux->dri = 0;
+	if(perm&DMEXCL){
+		t = emalloc9p(sizeof(Tlock));
+		t->time = nsec();
+		t->qpath = dchild->path;
+		t->dblkno = aux->dblkno;
+		qlock(&tlock);
+		if(tlocktail == nil){
+			tlocktail = tlockhead = t;
+		}else{
+			tlocktail->next = t;
+			t->prev = tlocktail;
+			tlocktail = t;
+		}
+		qunlock(&tlock);
+		aux->tlocked = 1;
+	}else
+		aux->tlocked = 0;
+	req->ofcall.qid = fid->qid;
+	req->ofcall.iounit = Iounit;
+	poperror();
+	putbuf(cbuf, 1);	/* save Iobuf of the content */
+	poperror();
+	putbuf(dbuf, 1);
+	poperror();
+	respond(req, nil);
+	return;
 }
 
 /* read the Req.ifcall.mode and build the Fid.omode based on the dentry */
@@ -756,7 +871,6 @@
 	Dentry *d;
 	Tlock *t;
 
-	dbuf = nil;
 	mode = req->ifcall.mode;
 	fid = req->fid;
 	aux = req->fid->aux;
@@ -764,29 +878,34 @@
 	if(chatty9p > 1)
 		dprint("fsopen fid->qid.path %d mode %d readonly %d\n",
 				 fid->qid.path, mode, readonly);
-	if(readonly && (mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0)
-		goto inval;
 
-	dbuf = getmetachk(aux->dblkno, Breadonly, Tdentry, fid->qid.path);
-	if(dbuf == nil){
-		respond(req, errstring[Ephase]);
+	if(waserror()){
+		responderror(req);
 		return;
 	}
+	if(readonly && (mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0)
+		error(errstring[Einval]);
+
+	dbuf = egetmetachk(aux->dblkno, Breadonly, Tdentry, fid->qid.path);
+	if(waserror()){
+		putbuf(dbuf, 0);
+		nexterror();
+	}
 	d = dbuf->d;
 
 	if((mode & OTRUNC) != 0 &&
 		canaccess(aux->uid, d, DMWRITE) == 0)
-		goto perm;
+		error(errstring[Eperm]);
 	if((mode & ORCLOSE) != 0)
 		if(canaccess(aux->uid, d, DMWRITE) == 0)
-			goto perm;
+		error(errstring[Eperm]);
 	if((d->mode & DMAPPEND) != 0)
 		mode &= ~OTRUNC;
 	if((d->mode & DMDIR) != 0){
 		if((mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0)
-			goto inval;
+			error(errstring[Einval]);
 		if(canaccess(aux->uid, d, DMEXEC) == 0)
-			goto perm;
+			error(errstring[Eperm]);
 	}
 	if((d->mode & DMEXCL) != 0){
 		qlock(&tlock);
@@ -834,25 +953,18 @@
 	req->ofcall.iounit = Iounit;
 	req->ofcall.qid = fid->qid;
 
+	poperror();
 	putbuf(dbuf, 0);
 	if((mode & OTRUNC) == 0){
+		poperror();
 		respond(req, nil);
 		return;
 	}
 	/* truncate file */
 	truncatefile(fid->qid.path, aux->dblkno, aux->uid);
+	poperror();
 	respond(req, nil);
 	return;
-inval:
-	if(dbuf != nil)
-		putbuf(dbuf, 0);
-	respond(req, errstring[Einval]);
-	return;
-perm:
-	if(dbuf != nil)
-		putbuf(dbuf, 0);
-	respond(req, errstring[Eaccess]);
-	return;
 }
 
 /* below is from nemo's Pg 252 */
@@ -939,9 +1051,25 @@
 } worker[Nworkers];	/* keeps track of running procs to flush */
 
 void
+work(Buffer *b, int id)
+{
+	Work w;
+
+	worker[id].pid = getpid();
+	w = get(b);
+	while(w.f != nil){
+		worker[id].w = w;
+		w.f(w.r);
+		worker[id].w = (Work){nil, nil};
+		w = get(b);
+	}
+	worker[id].pid = 0;
+}
+
+void
 initworks(Buffer *b)
 {
-	// release all locks, set everything to null values
+	// ufree all locks, set everything to null values
 	memset(b, 0, sizeof(*b));
 	// set the locks used by the Rendezes
 	b->isempty.l = &b->lck;
@@ -1064,22 +1192,6 @@
 }
 
 void
-work(Buffer *b, int id)
-{
-	Work w;
-
-	worker[id].pid = getpid();
-	w = get(b);
-	while(w.f != nil){
-		worker[id].w = w;
-		w.f(w.r);
-		worker[id].w = (Work){nil, nil};
-		w = get(b);
-	}
-	worker[id].pid = 0;
-}
-
-void
 fsflush(void)
 {
 	u64 now;
@@ -1099,11 +1211,18 @@
 startproc(Buffer *b, int id, u8 syncer)
 {
 	char name[128];
+	Errenv env;
 
 	switch(rfork(RFPROC|RFMEM|RFFDG)){
 	case -1:
 		panic("can't fork");
 	case 0:
+		envpp = privalloc();
+		if(_nprivates != envpidx)
+			panic("_nprivates != envpidx\n");
+		envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+		*envpp = &env;
+
 		if(chatty9p > 1)
 			dprint("child %d pid: %d\n", id, getpid());
 		break;
@@ -1153,51 +1272,43 @@
 }
 
 void
-bfsopen(Req *r)
+mfsopen(Req *r)
 {
 	put(&buf, fsopen, r);
 }
 
 void
-bfscreate(Req *r)
+mfscreate(Req *r)
 {
 	put(&buf, fscreate, r);
 }
 
 void
-bfsread(Req *r)
+mfsread(Req *r)
 {
-	if(r->fid->qid.type == QTAUTH){
-		authread(r);
-		return;
-	}else
-		put(&buf, fsread, r);
+	put(&buf, fsread, r);
 }
 
 void
-bfswrite(Req *r)
+mfswrite(Req *r)
 {
-	if(r->fid->qid.type == QTAUTH){
-		authwrite(r);
-		return;
-	}else
-		put(&buf, fswrite, r);
+	put(&buf, fswrite, r);
 }
 
 void
-bfsstat(Req *r)
+mfsstat(Req *r)
 {
 	put(&buf, fsstat, r);
 }
 
 void
-bfswstat(Req *r)
+mfswstat(Req *r)
 {
 	put(&buf, fswstat, r);
 }
 
 void
-bfsremove(Req *r)
+mfsremove(Req *r)
 {
 	put(&buf, fsremove, r);
 }
@@ -1212,13 +1323,13 @@
 	.walk1 = fswalk1,
 	.clone = fsclone,
 
-	.open = bfsopen,
-	.create = bfscreate,
-	.read = bfsread,
-	.write = bfswrite,
-	.stat = bfsstat,
-	.wstat = bfswstat,
-	.remove = bfsremove,
+	.open = mfsopen,
+	.create = mfscreate,
+	.read = mfsread,
+	.write = mfswrite,
+	.stat = mfsstat,
+	.wstat = mfswstat,
+	.remove = mfsremove,
 
 	.start = fsstart,
 	.end = fsend,
@@ -1308,9 +1419,7 @@
 	u64 filesize;
 	Iobuf *dbuf;
 
-	dbuf = getmetachk(dblkno, Breadonly, Tdentry, qpath);
-	if(dbuf == nil)
-		return 0;
+	dbuf = egetmetachk(dblkno, Breadonly, Tdentry, qpath);
 	filesize = dbuf->d->size;
 	putbuf(dbuf, 0);
 	return filesize;
@@ -1325,9 +1434,11 @@
 	u64 datablocksize;
 
 	sent = 0;
-	dbuf = getmetachk(dblkno, Breadonly, Tdentry, qpath);
-	if(dbuf == nil)
-		return 0;
+	dbuf = egetmetachk(dblkno, Breadonly, Tdentry, qpath);
+	if(waserror()){
+		putbuf(dbuf, 0);
+		nexterror();
+	}
 	d = dbuf->d;
 	filesize = d->size+dbuf->appendsize;
 
@@ -1345,10 +1456,6 @@
 		tosend = filesize - offset;
 	for(sent = 0; sent < tosend && offset+sent < d->size; ){
 		buf = getdatablkat(d, (offset+sent)/Maxdatablocksize);
-		if(buf == nil){
-			putbuf(dbuf, 0);
-			return -1;
-		}
 		if(buf->len == Maxdatablockunits)
 			datablocksize = Maxdatablocksize;
 		else
@@ -1365,6 +1472,7 @@
 		sent += n;
 	}
 readend:
+	poperror();
 	putbuf(dbuf, 0);
 	return sent;
 }
@@ -1393,9 +1501,7 @@
 		nblocks = nlastdatablocks(d->size);
 		nblockssize = nblocks*Blocksize -Ddataidssize;
 	}
-	oldbuf = getbufchk(oldblkno, nblocks, Bwritable, Tdata, d->path, getcallerpc(&d));
-	if(oldbuf == nil)
-		return -1;
+	oldbuf = egetbufchk(oldblkno, nblocks, Bwritable, Tdata, d->path, getcallerpc(&d));
 
 	/* update with the new contents */
 	to = offset%Maxdatablocksize;
@@ -1404,9 +1510,11 @@
 
 	/* allocate new blocks to copy on write */
 	newbuf = allocblocks(nblocks,Tdata, d->path);
-	if(newbuf == nil)
-		return -1;
 	newblkno = newbuf->blkno;
+	if(waserror()){
+		freeblocks(newblkno, nblocks, Tdata, d->path);
+		nexterror();
+	}
 
 	/* put the old contents in these new blocks */
 	memcpy(newbuf->io->buf, oldbuf->io->buf, nblocks*Blocksize);
@@ -1413,11 +1521,8 @@
 	putbuf(newbuf, 1);
 
 	/* add the newly allocated blocks to the Dentry */
-	if(addrelative(d, dblkno, offset/Maxdatablocksize, newblkno) == 0){
-		panic("could not write Tdata block\n");
-		freeblocks(newblkno, nblocks, Tdata, d->path);
-		return -2;
-	}
+	addrelative(d, dblkno, offset/Maxdatablocksize, newblkno);
+	poperror();
 	*oldbufp = oldbuf; /* freeblockbuf(oldbuf); */
 	return howmuch;
 }
@@ -1442,10 +1547,12 @@
 
 		/* allocate new blocks */
 		newbuf = allocblocks(Maxdatablockunits,Tdata, d->path);
-		if(newbuf == nil)
-			return -1;
 		newblkno = newbuf->blkno;
 		newbuf->io->dblkno = dblkno;
+		if(waserror()){
+			freeblocks(newblkno, Maxdatablockunits, Tdata, d->path);
+			nexterror();
+		}
 
 		/* add the contents of append to those new blocks */
 		memcpy(newbuf->io->buf, dbuf->append, Maxdatablocksize);
@@ -1453,11 +1560,8 @@
 		putbuf(newbuf, 1);
 
 		/* add this newly allocated blocks to the Dentry */
-		if(addrelative(d, dblkno, d->size/Maxdatablocksize, newblkno) == 0){
-			panic("could not write Tdata block\n");
-			freeblocks(newblkno, Maxdatablockunits, Tdata, d->path);
-			return -1;
-		}
+		addrelative(d, dblkno, d->size/Maxdatablocksize, newblkno);
+		poperror();
 		d->size += Maxdatablocksize;
 		dbuf->appendsize -= Maxdatablocksize;
 		return 1;
@@ -1471,11 +1575,13 @@
 
 		/* allocate new blocks */
 		newbuf = allocblocks(Maxdatablockunits, Tdata, d->path);
-		if(newbuf == nil)
-			return -1;
 		newblkno = newbuf->blkno;
 		newbuf->io->dblkno = dblkno;
 		newbufsize = 0;
+		if(waserror()){
+			freeblocks(newblkno, Maxdatablockunits, Tdata, d->path);
+			nexterror();
+		}
 
 		/* read from the last block */
 		/* copy that to the new allocated blocks */
@@ -1483,12 +1589,8 @@
 		if(lastdatablksize=d->size%Maxdatablocksize){
 			/* partial block, above is = not == */
 			oldblkno = rel2abs(d, d->size/Maxdatablocksize);
-			oldbuf = getbufchk(oldblkno, nlastdatablocks(lastdatablksize),
+			oldbuf = egetbufchk(oldblkno, nlastdatablocks(lastdatablksize),
 								Bwritable, Tdata, d->path, getcallerpc(&dbuf));
-			if(oldbuf == nil){
-				freeblocks(newblkno, Maxdatablockunits, Tdata, d->path);
-				return -1;
-			}
 			memcpy(newbuf->io->buf, oldbuf->io->buf, lastdatablksize);
 			newbufsize = lastdatablksize;
 		}
@@ -1504,11 +1606,8 @@
 		putbuf(newbuf, 1);
 
 		/* add this newly allocated blocks to the Dentry */
-		if(addrelative(d, dblkno, d->size/Maxdatablocksize, newblkno) == 0){
-			panic("could not write Tdata block\n");
-			freeblocks(newblkno, Maxdatablockunits, Tdata, d->path);
-			return -2;
-		}
+		addrelative(d, dblkno, d->size/Maxdatablocksize, newblkno);
+		poperror();
 
 		/* free the old last blocks after the dentry is written */
 		if(oldbuf)
@@ -1556,10 +1655,12 @@
 		/* allocate new blocks */
 		newblocks = nlastdatablocks(dbuf->appendsize);
 		newbuf = allocblocks(newblocks,Tdata, d->path);
-		if(newbuf == nil)
-			return -1;
 		newblkno = newbuf->blkno;
 		newbuf->io->dblkno = dblkno;
+		if(waserror()){
+			freeblocks(newblkno, newblocks, Tdata, d->path);
+			nexterror();
+		}
 
 		/* add the contents of append to those new blocks */
 		memcpy(newbuf->io->buf, dbuf->append, dbuf->appendsize);
@@ -1567,11 +1668,8 @@
 		putbuf(newbuf, 1);
 
 		/* add this newly allocated blocks to the Dentry */
-		if(addrelative(d, dblkno, d->size/Maxdatablocksize, newblkno) == 0){
-			panic("could not write Tdata block\n");
-			freeblocks(newblkno, newblocks, Tdata, d->path);
-			return -1;
-		}
+		addrelative(d, dblkno, d->size/Maxdatablocksize, newblkno);
+		poperror();
 		d->size += dbuf->appendsize;
 		dbuf->appendsize = 0;
 		return 1;
@@ -1582,11 +1680,13 @@
 	/* allocate new blocks */
 	newblocks = nlastdatablocks((d->size%Maxdatablocksize) +dbuf->appendsize);
 	newbuf = allocblocks(newblocks, Tdata, d->path);
-	if(newbuf == nil)
-		return -1;
 	newblkno = newbuf->blkno;
 	newbufsize = 0;
 	newbuf->io->dblkno = dblkno;
+	if(waserror()){
+		freeblocks(newblkno, newblocks, Tdata, d->path);
+		nexterror();
+	}
 
 	/* read from the last block and copy that to the newly allocated blocks */
 	if(lastdatablksize=d->size%Maxdatablocksize){
@@ -1612,11 +1712,8 @@
 	putbuf(newbuf, 1);
 
 	/* add this newly allocated blocks to the Dentry */
-	if(addrelative(d, dblkno, d->size/Maxdatablocksize, newblkno) == 0){
-		panic("could not write Tdata block\n");
-		freeblocks(newblkno, newblocks, Tdata, d->path);
-		return -2;
-	}
+	addrelative(d, dblkno, d->size/Maxdatablocksize, newblkno);
+	poperror();
 
 	/* free the old last blocks after the dentry is written */
 	if(oldbuf)
@@ -1643,7 +1740,7 @@
 {
 	Dentry *d;
 	s64 written;
-	s32 n, rv;
+	s32 n;
 	Iobuf *dbuf, *oldbuf;
 	u8 dowrite;
 
@@ -1650,12 +1747,14 @@
 	written = 0;
 Writefileagain:
 	oldbuf = nil;
-	dbuf = getmetachk(dblkno, Bwritable, Tdentry, qpath);
-	if(dbuf == nil)
-		return 0;
+	dbuf = egetmetachk(dblkno, Bwritable, Tdentry, qpath);
+	if(waserror()){
+		putbuf(dbuf, 0);
+		nexterror();
+	}
 	d = dbuf->d;
 	d->muid = uid;
-	rv = dowrite = 0;
+	dowrite = 0;
 	// odentry(d);
 
 	// little data, stuff it in the Dentry
@@ -1673,10 +1772,6 @@
 		offset+wbufsize > Ddatasize){
 		if(dbuf->append == nil){
 			dbuf->append = allocmemunits(Maxdatablockunits);
-			if(dbuf->append == nil){
-				rv = -1;
-				goto writeend;
-			}
 			dbuf->appendsize = 0;
 		}
 		memcpy(dbuf->append, d->buf, d->size);
@@ -1688,11 +1783,10 @@
 	while(oldbuf == nil && written < wbufsize){
 
 		if(chatty9p > 1)
-			dprint("writefile(): d->name %s d->size %llud dbuf->appendsize %llud"
+			dprint("writefile(): d->size %llud dbuf->appendsize %llud"
 					" offset %llud written %d offset+written %llud wbufsize %ld\n",
-				d->name, d->size, dbuf->appendsize,
-				offset, written, offset+written, wbufsize
-				);
+					d->size, dbuf->appendsize,
+					offset, written, offset+written, wbufsize);
 
 		/* all the below functions only write upto the end of an extent.
 		   Hence, the need for a loop to keep repeating.
@@ -1701,8 +1795,7 @@
 			dowrite = 1;
 			if(chatty9p > 1)
 				dprint("writefile(): writefullappend\n");
-			if((rv=writefullappend(dbuf, d, dblkno, &oldbuf)) < 0)
-				goto writeend;
+			writefullappend(dbuf, d, dblkno, &oldbuf);
 
 		}else if(offset > d->size+dbuf->appendsize){
 			/* new blank blocks until offset
@@ -1713,10 +1806,6 @@
 
 			if(dbuf->append == nil){
 				dbuf->append = allocmemunits(Maxdatablockunits);
-				if(dbuf->append == nil){
-					rv = -1;
-					goto writeend;
-				}
 				dbuf->appendsize = 0;
 			}
 			n = min(Maxdatablocksize, offset-d->size);
@@ -1729,7 +1818,7 @@
 			if(chatty9p > 1)
 				dprint("writefile(): replace existing data\n");
 
-			rv = n = update(d, dblkno,
+			n = update(d, dblkno,
 							wbuf+written, /* from where */
 							/* how much */
 							min(wbufsize-written, d->size-(offset+written)),
@@ -1737,13 +1826,8 @@
 							&oldbuf);
 			if(chatty9p > 1)
 				dprint("writefile(): update returned %d\n", n);
-			if(n<0){
-				dprint("update has an issue %d\n", n);
-				goto writeend;
-			}else{
-				written += n;
-				dowrite = 1;
-			}
+			written += n;
+			dowrite = 1;
 
 		}else if(offset+written >= d->size &&
 					offset+written < d->size+dbuf->appendsize){
@@ -1762,10 +1846,6 @@
 				dprint("writefile(): append\n");
 			if(dbuf->append == nil){
 				dbuf->append = allocmemunits(Maxdatablockunits);
-				if(dbuf->append == nil){
-					rv = -1;
-					goto writeend;
-				}
 				dbuf->appendsize = 0;
 			}
 			n = min(Maxdatablocksize-dbuf->appendsize, wbufsize-written);
@@ -1782,6 +1862,7 @@
 	d->mtime = nsec();
 	if(dowrite)
 		d->version++;
+	poperror();
 	putbuf(dbuf, dowrite);
 	if(oldbuf){
 		freeblockbuf(oldbuf);
@@ -1789,9 +1870,13 @@
 	}
 
 	/* How will this work for partial writes? */
-	if(rv < 0)
-		return rv;
 	if(chatty9p > 1)
 		dprint("writefile(): written %d\n", written);
 	return written;
+}
+
+void *
+malloc9p(u32 sz)
+{
+	return emalloc9p((ulong)sz);
 }
--- a/all.h
+++ b/all.h
@@ -107,6 +107,9 @@
 void	freememunits(u8 *m, u16 len);
 void	fsflush(void);
 int		checktag(Iobuf *p, u16 len, u8 tag, u64 qpath);
+Iobuf *egetbufchk(u64 blkno, u16 len, u8 readonly, int tag, u64 qpath, u64 userpc);
+Iobuf *egetmetachk(u64 blkno, u8 readonly, int tag, u64 qpath);
+Iobuf *egetmeta(u64 blkno, u8 readonly, u8 freshalloc);
 Iobuf*	getbuf(u64 blkno, u16 len, u8 readonly, u8 freshalloc, u64 userpc);
 Iobuf*	getbufchk(u64 blkno, u16 len, u8 readonly, int tag, u64 qpath, u64 userpc);
 Iobuf*	getmeta(u64 blkno, u8 readonly, u8 freshalloc);
@@ -127,11 +130,13 @@
 /* routines to manipulate the contents */
 Iobuf*	allocblocks(u64 len, int tag, u64 qpath);
 Iobuf*	allocmeta(int tag, u64 qpath);
+u8		allocdentries(u16 n, Iobuf **iobufs, u64 qpath);
 void	freeblockbuf(Iobuf *buf);
 void	freeblocks(u64 blkno, u64 len, u16 tag, u64 qpath);
 void	fsok(int ok);
 void	init(int doream, u64 size);
 u64		newqpath(void);
+u64		newqpaths(u8 n);
 u64		nperiblock(u16 tag);
 u64		nperindunit(u16 tag);
 u64		power( u64 base, int n);
@@ -138,7 +143,7 @@
 void	ream(u64 size);
 u64	rel2abs(Dentry *d, u64 reli);
 void	rmfile(u64 qpath, u64 dblkno);
-void	rmdirectory(u64 qpath, u64 dblkno);
+void	rmdirectory(u64 qpath,  u64 dblkno);
 void	rootream(void);
 Dentry	*searchdir(u64 dblkno, u64 qpath, u16 uid, char *searchname, u64 searchidx, Iobuf **dbuf, Iobuf **buf);
 void	shutdown(void);
@@ -151,10 +156,11 @@
 void	clearfrees(void);
 void	freeaux(Aux *a);
 Iobuf*	getdatablkat(Dentry *d, u64 reli);
+u64		loadextentsfile(u64 blkno, u64 qpath, Extents *es);
 void	loadfrees(void);
-Aux*	newaux(u64 addr, u16 uid);
 s32		readfile(u64 dblkno, u64 qpath, char *rbuf, s32 rbufsize, u64 offset);
 s32		readfilesize(u64 dblkno, u64 qpath);
+void	saveextentstofile(u64 blkno, u64 qpath, u16 uid, Extents *es);
 void	savefrees(void);
 void	truncatefile(u64 qpath, u64 dblkno, s16 uid);
 s32		writeallappend(Iobuf *dbuf, u64 dblkno, Iobuf **oldbufp);
@@ -163,6 +169,14 @@
 u64		sync(void);
 void	flushold(void);
 
+/* names routines */
+s8	searchnames(Dentry *d, char *searchname, u64 *relip);
+u16	readname(Dentry *d, u64 noffset, s8 **namep, u16 *namelen);
+void	rmname(Dentry *pd, s16 uid, u64 noffset);
+u16	addname(Dentry *pd, u16 uid, u64 preli, s8 *name);
+u16	updatename(Dentry *pd, u16 uid, u64 preli, s8 *name);
+void	newnames(Dentry *pd, u16 uid, u64 pdblkno, Iobuf **iobufs);
+
 /* user access routines */
 int	byname(void*, void*);
 int	byuid(void*, void*);
@@ -199,7 +213,10 @@
 void	ctlwrite(Req *req);
 
 int	dprint(char *fmt, ...);
+int	dprintfd(int fd, char *fmt, ...);
 int	prime(long);
 u64	min(u64 a, u64 b);
+void	*malloc9p(u32 sz);
+void	*emalloc(u32 sz);
 
 #pragma varargck	argpos	panic	1
--- a/blk.c
+++ b/blk.c
@@ -9,7 +9,7 @@
 	Dentry *d;
 
 	d = (Dentry*)buf;
-	fprint(fd, "name %s\n", d->name);
+	// fprint(fd, "name %s\n", d->name);
 	fprint(fd, "uid %d\n", d->uid);
 	fprint(fd, "gid %d\n", d->gid);
 	fprint(fd, "muid %d\n", d->muid);
--- a/block.c
+++ b/block.c
@@ -26,7 +26,19 @@
 	u64 size, blkno;
 	u8 buf[Maxdatablocksize];
 	Data *da;
+	Errenv env;
 
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+	}
+
 	ARGBEGIN{
 	default:	usage();
 	case 'D':	debug++; break;
@@ -49,8 +61,8 @@
 
 	if(debug){
 		print("Dentry size %d\n", sizeof(Dentry));
-		print("Dentryhdr size %d Ddatasize %llud Namelen %d\n",
-				sizeof(Dentryhdr), Ddatasize, Namelen);
+		print("Dentryhdr size %d Ddatasize %llud Servicelen %d\n",
+				sizeof(Dentryhdr), Ddatasize, Servicelen);
 	}
 
 	// nunits = size/Blocksize;
--- a/config.c
+++ b/config.c
@@ -7,10 +7,11 @@
 	Iobuf *buf;
 	char cfg[Ddatasize];
 
-	buf = getmetachk(dblkno, Breadonly, Tdentry, Qpconfig);
-	if(buf == nil)
+	if(waserror())
 		panic("cannot find config file in %llud block\n", dblkno);
+	buf = egetmetachk(dblkno, Breadonly, Tdentry, Qpconfig);
 	memcpy(cfg, buf->d->buf, buf->d->size);
+	poperror();
 	putbuf(buf, 0);
 
 	parseconfig(cfg, &config);
@@ -48,10 +49,11 @@
 	Iobuf *buf;
 	s32 n;
 
-	buf = getmetachk(bno, Bwritable, Tdentry, Qpconfig);
-	if(buf == nil)
+	if(waserror())
 		panic("cannot write config");
+	buf = egetmetachk(bno, Bwritable, Tdentry, Qpconfig);
 	n = configstr(buf->d->buf, Ddatasize);
 	buf->d->size = n;
+	poperror();
 	putbuf(buf, 1);
 }
--- a/console.c
+++ b/console.c
@@ -174,7 +174,7 @@
 static int
 walkpath(Chan *ch, char *path, char **cr)
 {
-	char buf[Namelen], *p, *fp;
+	char buf[Servicelen], *p, *fp;
 
 	fp = path;
 	if(*path != '/'){
@@ -202,7 +202,7 @@
 		}
 		if(*path == 0)
 			break;
-		if(p - path >= Namelen)
+		if(p - path >= Servicelen)
 			goto noent;
 		memset(buf, 0, sizeof buf);
 		memcpy(buf, path, p - path);
--- a/ctl.c
+++ b/ctl.c
@@ -165,7 +165,7 @@
 {
 	Cmdbuf *cb;
 	Cmdtab *ct;
-	char srvfilename[Namelen];
+	char srvfilename[Servicelen];
 	int n;
 
 	if(chatty9p)
@@ -186,7 +186,7 @@
 			if(ct->index == Chalt){
 				/* /srv/mafs_service file will not exist when mounted with -s */
 				if(mpsrvpid &&
-					snprint(srvfilename, Namelen, "/srv/%s", service) > 5){
+					snprint(srvfilename, Servicelen, "/srv/%s", service) > 5){
 					/*
 						this stuff is only needed when the worker is doing
 						the shutdown()
--- a/dat.c
+++ b/dat.c
@@ -16,12 +16,12 @@
 	/* add more Tind tags here ... */
 };
 
-char	*errstring[MAXERR] =
+s8	*errstring[MAXERR] =
 {
 	[Eaccess]	"access permission denied",
 	[Ealloc]	"phase error -- directory entry not allocated",
 	[Eauth]		"authentication failed",
-	[Eauthmsg]	"kfs: authentication not required",
+	[Eauthmsg]	"mafs: authentication not required",
 	[Ebadname]	"bad name",
 	[Ebadspc]	"attach -- bad specifier",
 	[Ebadu]		"attach -- privileged user",
@@ -40,6 +40,9 @@
 	[Efidinuse]	"fid already in use",
 	[Efull]		"file system full",
 	[Einval]	"invalid operation",
+	[Einvread]	"invalid read operation",
+	[Einvwrite]	"invalid write operation",
+	[Einvusers]	"invalid users contents",
 	[Elocked]	"open/create -- file is locked",
 	[Emode]		"open/create -- unknown mode",
 	[Ename]		"create/wstat -- bad character in file name",
@@ -46,10 +49,13 @@
 	[Enomem]	"no memory",
 	[Enotd]		"wstat -- attempt to change directory",
 	[Enotdir]	"not a directory",
+	[Enotfound]	"not found",
 	[Enotg]		"wstat -- not in group",
 	[Enotl]		"wstat -- attempt to change length",
 	[Enotm]		"wstat -- unknown type/mode",
 	[Enotu]		"wstat -- not owner",
+	[Enouser]	"no such user",
+	[Enousers]	"no users",
 	[Eoffset]	"read/write -- offset negative",
 	[Eopen]		"read/write -- on non open fid",
 	[Eperm]		"no permission",
@@ -57,11 +63,8 @@
 	[Eqid]		"phase error -- qid does not match",
 	[Eqidmode]	"wstat -- qid.type/dir.mode mismatch",
 	[Eronly]	"file system read only",
-	[Ersc]	"it's russ's fault.  bug him.",
-	[Eshutdown]	"m[]afs shutting down",
-	[Esystem]	"m[a]fs system error",
+	[Eshutdown]	"mafs shutting down",
+	[Esystem]	"mafs system error",
 	[Etoolong]	"name too long",
-	[Einvusers]	"invalid users contents",
-	[Enousers]	"no users",
 	[Ewalk]		"walk -- too many (system wide)",
 };
--- a/dat.h
+++ b/dat.h
@@ -30,6 +30,7 @@
 	None	= 0,	/* user ID for "none" */
 	Noworld	= 9999,	/* conventional id for "noworld" group */
 	Userlen = 32,
+	Servicelen = 128,
 
 	Nsec	= 1000ULL*1000*1000,
 	Usec	= 1000ULL*1000,
@@ -36,6 +37,13 @@
 	Msec	= 1000ULL,
 	Nbkp	= 1,
 	Nrefresh = 3*Nsec,
+
+	Dsys = 1,	/* for flagging .n, .nl and .nle files. Dentry.flags */
+
+	In	= 0,	/* reli of .n file */
+	Inl	= 1,	/* reli of .nl file */
+	Inle= 2,	/* reli of .nle file */
+	Nsys,
 };
 
 /*
@@ -57,27 +65,42 @@
 	Nu64perblock= (Blocksize/sizeof(u64)),		/* number of u64's in a block */
 	Dpathidx	= (Blocksize/sizeof(u64) -1),	/* index of path in the last data block, last u64 */
 
-	Namelen = 128,	/* maximum length of a file name, calculated anually */
-	Ndblock	= 32,	/* number of direct blocks in a Dentry */
-	Niblock	= 5,	/* maximum depth of indirect blocks, can increase it to 8 without issues */
+	Ndblock	= 46,	/* number of direct blocks in a Dentry */
+	Niblock	= 5,	/* maximum depth of indirect blocks, can increase it to 9 without issues */
 
 	/* global block numbers. The bkp contents locations are calculated by ream() */
 	Bdmagic	= 0,	/* block number of first block. Bmagic conflicts with bio.h */
 	Bdconfig= 1,	/* block number of /a/config dentry and contents */
 	Bdsuper	= 2,	/* block number of /a/super dentry and contents */
+
 	Bda		= 3,	/* block number of /a directory */
-	Bdusers	= 4,	/* block number of /a/users/ dentry */
+	Bdanames= 4,	/* block number of /a dir names directory */
+	Bdalnames= 5,	/* block number of /a dir long names directory */
+	Bdalnamesextents= 6,	/* block number of /a dir long names extents directory */
 
+	Bdusers	= 7,	/* block number of /a/users/ directory entry */
+	Bdusersnames= 8,/* block number of /a/users/ directory names */
+	Bduserslnames= 9,/* block number of /a/users/ directory long names */
+	Bduserslnamesextents= 10,/* block number of /a/users/ directory long names extents */
+
 					/* contents of blocks below change on use */ 
-	Bdbkp	= 5,	/* block number of /a/bkp directory */
-	Bdusersinuse = 6,	/* block number of /a/users/inuse dentry and contents */
-	Bdfrees = 7,	/* block number of /a/frees dentry, text file of free extents */
+	Bdbkp	= 11,	/* block number of /a/bkp directory */
+	Bdbkpnames= 12,	/* block number of /a/bkp directory names */
+	Bdbkplnames= 13,/* block number of /a/bkp directory long names */
+	Bdbkplnamesextents= 14,	/* block number of /a/bkp directory long names extents */
 
+	Bdusersinuse = 15,	/* block number of /a/users/inuse dentry and contents */
+	Bdfrees = 16,	/* block number of /a/frees dentry, text file of free extents */
+
 					/* no user writes allowed on blocks below Bdctl */
-	Bdctl	= 8,	/* block number of /a/ctl dentry, empty contents, virtual file */
-	Bdusersstaging = 9,/* block number of /a/users/staging dentry */
-	Bdroot	= 10,	/* block number of root directory */
+	Bdctl	= 17,	/* block number of /a/ctl dentry, empty contents, virtual file */
+	Bdusersstaging = 18,/* block number of /a/users/staging dentry */
 
+	Bdroot	= 19,	/* block number of root directory */
+	Bdrootnames	= 20,	/* block number of root directory names */
+	Bdrootlnames	= 21,	/* block number of root directory long names */
+	Bdrootlnamesextents	= 22,	/* block number of root directory long names extents */
+
 	Nbused,	/* blocks used up by default */
 
 	/* number of blocks used by the above and the backup blocks */
@@ -88,11 +111,23 @@
 	Qpnone		= 0,
 	Qpconfig	= Bdconfig,	/* /a/config block Tag.qpath */
 	Qpsuper		= Bdsuper,	/* /a/super block Tag.qpath */
+
 	Qpa			= Bda,	/* /a */
+	Qpanames	= Bdanames,	/* /a */
+	Qpalnames	= Bdalnames,	/* /a */
+	Qpalnamesextents= Bdalnamesextents,	/* /a */
+
 	Qpusers		= Bdusers,	/* /a/users block Tag.qpath */
+	Qpusersnames= Bdusersnames,	/* /a/users block Tag.qpath */
+	Qpuserslnames= Bduserslnames,	/* /a/users block Tag.qpath */
+	Qpuserslnamesextents= Bduserslnamesextents,	/* /a/users block Tag.qpath */
 
 	Qpbkp		= Bdbkp,	/* /a/bkp block Tag.qpath */
-	Qpusersinuse	= Bdusersinuse,	/* /a/users/inuse block Tag.qpath */
+	Qpbkpnames	= Bdbkpnames,	/* /a/bkp block Tag.qpath */
+	Qpbkplnames	= Bdbkplnames,	/* /a/bkp block Tag.qpath */
+	Qpbkplnamesextents= Bdbkplnamesextents,	/* /a/bkp block Tag.qpath */
+
+	Qpusersinuse= Bdusersinuse,	/* /a/users/inuse block Tag.qpath */
 	Qpfrees		= Bdfrees,	/* /a/frees block Tag.qpath */
 
 				/* system qpaths */
@@ -102,6 +137,10 @@
 	Qproot0,	/* /a/bkp/root.0 block Tag.qpath */
 
 	Qproot	= 32,	/* /, so fscreate() and fswrite() can disallow any create's below */
+	Qprootnames,
+	Qprootlnames,
+	Qprootlnamesextents,
+
 	Qpctl,			/* /a/ctl */
 	Qpusersstaging,	/* /a/users/staging block Tag.qpath */
 	Nqidgen	= 64,
@@ -118,18 +157,18 @@
 struct Dentryhdr
 {
 	u8 tag;
-	u8 namelen;
+	u8 flags;		/* attributes (names extents, names - hide system files)  */
 	s16 uid;
 	s16 gid;
 	s16 muid;		/* 8 */
 	u64 size;		/* 0 for directories. For files, size in bytes of the content - 16 */
-	u64 pdblkno; 	/* block number of the parent directory entry. Will be 0 for root. - 24 */
+	u64 pdblkno; 	/* block number of the parent directory entry. 0 for root. - 24 */
 	u64 pqpath; 	/* parent qid.path - 32 */
-	u64 mtime;		/* modified time in nano seconds from epoch - 40 */
-	u64 qpath;		/* unique identifier Qid.path 48 */
-	u32 version;	/* Qid.version 52 */
-	u32 mode;		/* same bits as defined in /sys/include/libc.h:/Dir\.mode/ - 56 */
-	s8 name[Namelen]; /* Namelen = 128 - 184*/
+	u64 preli; 		/* my reli in my parent's directory entry - 40 */
+	u64 mtime;		/* modified time in nano seconds from epoch - 48 */
+	u64 qpath;		/* unique identifier Qid.path 56 */
+	u32 version;	/* Qid.version 60 */
+	u32 mode;		/* same bits as defined in /sys/include/libc.h:/Dir\.mode/ - 64 */
 };
 struct Datahdr
 {
@@ -169,7 +208,7 @@
 		};
 		Super;
 
-		/* when size <= Dentrysize-184-sizeof(Tag), store the data here itself */
+		/* when size <= Ddatasize, store the data here itself */
 		s8 buf[Ddatasize];
 	};
 	u64 path;	/* same as qid.path */
@@ -203,7 +242,10 @@
 
 struct Aux
 {
-	u64 dblkno; /* dentry absolute block number */
+	u64 dblkno; /* directory entry absolute block number */
+	u64 pdblkno;/* block number of my parent's directory entry */
+	u64 pqpath;/* block number of my parent's directory entry */
+	u64 preli;	/* my relative index in the parent's directory entry */
 	u16 uid;
 	u8 tlocked;	/* for exclusive use files, flag to indicate lock */
 	u64 dri;	/* directory index while reading a directory */
@@ -239,7 +281,7 @@
 	struct Bkp config;
 	struct Bkp super;
 	struct Bkp root;
-	char service[Namelen];
+	char service[Servicelen];
 };
 
 enum
@@ -276,6 +318,9 @@
 	Efidinuse,
 	Efull,
 	Einval,
+	Einvread,
+	Einvwrite,
+	Einvusers,
 	Elocked,
 	Emode,
 	Ename,
@@ -282,11 +327,14 @@
 	Enomem,
 	Enotd,
 	Enotdir,
+	Enotfound,
 	Enotg,
 	Enotl,
 	Enotm,
 	Enotu,
 	Enotw,
+	Enouser,
+	Enousers,
 	Eoffset,
 	Eopen,
 	Eperm,
@@ -298,10 +346,7 @@
 	Eshutdown,
 	Esystem,
 	Etoolong,
-	Einvusers,
-	Enousers,
 	Ewalk,
-
 	MAXERR
 };
 
@@ -353,7 +398,7 @@
 #define	TLOCK		MINUTE(5)
 
 extern	char*	errstring[MAXERR];
-extern	char	service[Namelen];
+extern	char	service[Servicelen];
 extern	char	*tagnames[];
 extern	char	*devfile;	/* device file path */
 extern	int		devfd ;		/* device fd */
@@ -371,3 +416,18 @@
 extern Config config;
 extern char userserrmsg[Nuserserrmsg];
 Config *parseconfig(s8 *cfg, Config *c);
+
+typedef	struct	Errenv	Errenv;
+struct Errenv		/* Error environment */
+{
+	jmp_buf label[16];
+	u8 nlabel;
+};
+extern s8 envpidx;	/* the index in _privates holding the Errenv location */
+extern Errenv **envpp;
+extern void	**_privates;	/* defined in libc */
+extern int	_nprivates;
+int waserror(void);
+void poperror(void);
+void nexterror(void);
+void error(s8 *fmt, ...);
--- a/dentry.c
+++ b/dentry.c
@@ -42,10 +42,7 @@
 	if(blkno == 0){
 		return 0;
 	}
-	if((buf = getmetachk(blkno, Breadonly, tag, path)) == nil){
-		dprint("%s",errstring[Ephase]);
-		return 0;
-	}
+	buf = egetmetachk(blkno, Breadonly, tag, path);
 	if(tag > Tind0){
 		n = nperindunit(tag);
 		b = getindblk(buf->i->bufa[reli/n],
@@ -94,17 +91,11 @@
 		return 0;
 	}
 	if(indblkno == 0){
-		if((buf = allocmeta(tag, path)) == nil){
-			dprint("%s",errstring[Efull]);
-			return 0;
-		}
+		buf = allocmeta(tag, path);
 		buf->i->dblkno = dblkno;
 		indblkno = buf->blkno;
 	}else{
-		if((buf = getmetachk(indblkno, Bwritable, tag, path)) == nil){
-			dprint("updateindblock: %s",errstring[Ephase]);
-			return 0;
-		}
+		buf = egetmetachk(indblkno, Bwritable, tag, path);
 		if(buf->blkno != indblkno){
 			dprint("updateindblock: buf->blkno != indblkno"
 					" buf->blkno %llud indblkno %llud\n",
@@ -158,8 +149,8 @@
 	u8 tag;
 
 	if(chatty9p > 2)
-	dprint("addrelative %llud:%s reli %llud blkno %llud\n",
-			dblkno, d->name, reli, blkno);
+	dprint("addrelative %llud: reli %llud blkno %llud\n",
+			dblkno, reli, blkno);
 
 	path = d->path;
 	if(reli < Ndblock){
@@ -171,10 +162,7 @@
 	if(chatty9p > 2)
 	dprint("addrelative(): reli %llud tag %s d->iblocks[%d] %llud blkno %llud\n",
 			reli, tagnames[tag], tag-Tind0, d->iblocks[tag-Tind0], blkno);
-	if((nblkno = updateindblock(dblkno, d->iblocks[tag-Tind0], reli-tagstartreli(tag), tag, path, blkno)) == 0){
-			dprint("%s",errstring[Ephase]);
-			return 0;
-		}
+	nblkno = updateindblock(dblkno, d->iblocks[tag-Tind0], reli-tagstartreli(tag), tag, path, blkno);
 	d->iblocks[tag-Tind0] = nblkno;
 	return nblkno;
 }
@@ -182,7 +170,7 @@
 /*
 	directtag == Tdata for files and Tdentry for directories
 
-	Could copy all the block numbers into memory, release the block
+	Could copy all the block numbers into memory, ufree the block
 	and work on free'ing each block. But, this does not help
 	as this would all be dangling stuff identifiable by the fsck
 	and removed by it on a crash.
@@ -196,10 +184,10 @@
 
 	if(iblkno == 0)
 		return reli;
-	ibuf = getmetachk(iblkno, Bwritable, tag, qpath);
-	if(ibuf == nil){
-		panic("%s",errstring[Ephase]);
-		return reli;
+	ibuf = egetmetachk(iblkno, Bwritable, tag, qpath);
+	if(waserror()){
+		putbuf(ibuf, 0);
+		nexterror();
 	}
 
 	if(tag == Tind0)
@@ -224,6 +212,7 @@
 								reli, lastreli, lastunits);
 			ibuf->i->bufa[i] = 0;
 		}
+	poperror();
 	freeblockbuf(ibuf);
 	return reli;
 }
@@ -283,13 +272,12 @@
 
 	if(qpath < Qpusers || dblkno == 0)
 		return;
-	dbuf = getmetachk(dblkno, Bwritable, Tdentry, qpath);
-	if(dbuf == nil)
-			dprint("%s",errstring[Ephase]);
+	dbuf = egetmetachk(dblkno, Bwritable, Tdentry, qpath);
 
 	truncatefilebuf(dbuf, uid);
 }
 
+/* name is removed by the caller */
 /*
 	Removes the file contents and zero's the dentry
 
@@ -312,9 +300,7 @@
 
 	if(qpath < Qpusers || dblkno == 0)
 		return;
-	dbuf = getmetachk(dblkno, Bwritable, Tdentry, qpath);
-	if(dbuf == nil)
-		dprint("%s",errstring[Ephase]);
+	dbuf = egetmetachk(dblkno, Bwritable, Tdentry, qpath);
 	size = dbuf->d->size;
 	memcpy(&d, dbuf->d, sizeof(Dentry));
 	memset(dbuf->d, 0, Blocksize);
@@ -347,6 +333,7 @@
 	}
 }
 
+/* name is removed by the caller */
 void
 rmdirectory(u64 qpath, u64 dblkno)
 {
@@ -361,12 +348,11 @@
 	if(qpath < Qpusers || dblkno == 0)
 		return;
 	/* clear the dentry to avoid links to removed content */
-	dbuf = getmetachk(dblkno, Bwritable, Tdentry, qpath);
-	if(dbuf == nil)
-			dprint("%s",errstring[Ephase]);
+	dbuf = egetmetachk(dblkno, Bwritable, Tdentry, qpath);
 	memcpy(&d, dbuf->d, sizeof(Dentry));
 	memset(dbuf->d, 0, Blocksize);
 	settag(dbuf, Tdentry, Qpnone);
+
 	putbuf(dbuf, 1);
 
 	/*
@@ -378,7 +364,7 @@
 		if((blkno = rel2abs(&d, reli)) == 0)
 			break;
 
-		buf = getmeta(blkno, Breadonly, Bused);
+		buf = egetmeta(blkno, Breadonly, Bused);
 		child = buf->d;
 		cqpath = child->path;
 		mode = child->mode;
@@ -455,95 +441,9 @@
 		return 1;
 
 	/* not doing the du read access or allowing god that cwfs does for now */
-
 	return 0;
 }
 
-/* relative block numbers and the index are the same as we have 1 direntry per block */
-Dentry *
-searchdir(u64 dblkno, u64 qpath, u16 uid, char *searchname, u64 searchidx, Iobuf **dbuf, Iobuf **buf)
-{
-	u64 reli, idx;
-	Dentry *d, *ch;
-	u64 blkno;
-
-	*dbuf = getmetachk(dblkno, Breadonly, Tdentry, qpath);
-	if(*dbuf == nil)
-		return nil;
-	d = (*dbuf)->d;
-
-	if(canaccess(uid, d, DMEXEC) == 0){
-		putbuf(*dbuf, 0);
-		*dbuf = nil;
-		dprint("%s",errstring[Ephase]);
-		return nil;
-	}
-
-	/* using idx to not include zero'ed out slots in our search */
-	for(reli = 0, idx = 0; ; reli++){
-
-		
-		if((blkno = rel2abs(d, reli)) == 0){
-			putbuf(*dbuf, 0);
-			*dbuf = nil;
-			return nil;
-		}
-		if(chatty9p>2)
-			dprint("searchdir reli %d s.blkno %llud s.len %d\n",
-					reli, blkno);
-
-		*buf = getmeta(blkno, Breadonly, Bused);
-		if(*buf == nil){
-			putbuf(*dbuf, 0);
-			*dbuf = nil;
-			dprint("%s",errstring[Ephase]);
-			return nil;
-		}
-
-		ch = (*buf)->d;
-		if(chatty9p > 2)
-			dprint("searchdir: dblkno %llud qpath %llud searchname %s searchidx %d"
-					" reli %llud blkno %llud ch->path %llud\n",
-					dblkno, qpath, searchname, searchidx, reli, blkno,
-					ch->path);
-		if(checktag(*buf, 1, Tdentry, ch->path) == 0){
-			putbuf(*buf, 0);
-			putbuf(*dbuf, 0);
-			*dbuf = *buf = nil;
-			dprint("%s",errstring[Ephase]);
-			return nil;
-		}
-
-		/* nothing to do for already zero'ed out slots */
-		if(ch->tag == Tdentry && ch->path == Qpnone)
-			goto Nextdentry;
-
-		if(searchname != nil){
-			if(strcmp(searchname, (char*)ch->name) == 0){
-				if(chatty9p > 2)
-				dprint("searchdir: found name %s\n", searchname);
-				return ch;
-			}
-		}else if(idx == searchidx){
-			if(chatty9p > 2)
-			dprint("searchdir: found index %d\n", searchidx);
-			return ch;
-		}
-
-		idx++; /* so that zero'ed slots do not match */
-Nextdentry:
-		putbuf(*buf, 0);
-	}
-	/* should never be here */
-}
-void
-freesearchstate(Iobuf **dbuf, Iobuf **buf)
-{
-	putbuf(*buf, 0);
-	putbuf(*dbuf, 0);
-	*dbuf = *buf = nil;
-}
-
 /*
 	get the contents in the reli'th block number of the Dir, d.
 	The buffer holding the d should be locked by the caller.
@@ -562,49 +462,48 @@
 		return nil;
 
 	if(reli < d->size/Maxdatablocksize)
-		buf = getbufchk(blkno, Maxdatablockunits, Breadonly, Tdata, d->path, getcallerpc(&d));
+		return egetbufchk(blkno, Maxdatablockunits, Breadonly, Tdata, d->path, getcallerpc(&d));
 	else
-		buf = getbufchk(blkno, nlastdatablocks(d->size), Breadonly, Tdata, d->path, getcallerpc(&d));
-	if(buf == nil){
-		putbuf(buf, 0);
-		dprint("%s",errstring[Ephase]);
-		return nil;
-	}
-	return buf;
+		return egetbufchk(blkno, nlastdatablocks(d->size), Breadonly, Tdata, d->path, getcallerpc(&d));
 }
 
-/* the frees list of free blocks will include the blocks
-	used to store the frees */
 void
-savefrees(void)
+saveextentstofile(u64 blkno, u64 qpath, u16 uid, Extents *es)
 {
 	s32 nbuf;
 	s8 *buf;
-	Iobuf *dbuf, *oldbuf;
 
-	/* should not be necessary as we clear out the file
-		in loadfrees() */
-	clearfrees();
-
-	/* will not be accurate as we are allocating blocks below.
+	/* will not be accurate for frees as we are allocating blocks below.
 		But, will be more than what we will actually end up using.
 	 */
-	nbuf = sizeofextents(&frees);
+	nbuf = sizeofextents(es);
 	/* +1 as snprint() always places a terminating NUL byte */
 	buf = emalloc9p(nbuf+1);
 
 	/* get the extents into buf */
-	if(saveextents(&frees, buf, nbuf+1) == -1)
-		panic("savefrees nbuf not enough");
+	if(saveextents(es, buf, nbuf+1) == -1)
+		error("savefrees nbuf not enough");
 
 	/* writing the actual extents now */
-	writefile(Bdfrees, Qpfrees, -1, buf, nbuf, 0);
+	writefile(blkno, qpath, uid, buf, nbuf, 0);
 	free(buf);
+}
 
+/* the frees list of free blocks will include the blocks
+	used to store the frees */
+void
+savefrees(void)
+{
+	Iobuf *dbuf, *oldbuf;
+
+	/* should not be necessary as we clear out the file
+		in loadfrees() */
+	clearfrees();
+
+	saveextentstofile(Bdfrees, Qpfrees, -1, &frees);
+
 	/* flush to the disk if append has stuff */
-	dbuf = getmetachk(Bdfrees, Bwritable, Tdentry, Qpfrees);
-	if(dbuf == nil)
-		return;
+	dbuf = egetmetachk(Bdfrees, Bwritable, Tdentry, Qpfrees);
 	if(dbuf->append == nil)
 		putbuf(dbuf, 0);
 	else{
@@ -616,27 +515,36 @@
 	}
 }
 
+u64
+loadextentsfile(u64 blkno, u64 qpath, Extents *es)
+{
+	u64 size;
+	s8 *buf;
+
+	size = readfilesize(blkno, qpath);
+	if(size == 0)
+		return size;
+
+	buf = emalloc9p(size);
+	if(buf == nil)
+		error("loadfrees: nil emalloc of %llud bytes", size);
+	if(readfile(blkno, qpath, buf, size, 0) != size)
+		error("loadextentsfile: could not load extents");
+	loadextents(es, buf, size);
+	free(buf);
+	return size;
+}
+
 void
 loadfrees(void)
 {
-	u64 size, n;
-	s8 *buf;
+	u64 size;
 
-	size = readfilesize(Bdfrees, Qpfrees);
-	if(size == 0){
+	size = loadextentsfile(Bdfrees, Qpfrees, &frees);
+	if(size == 0)
 		panic("There are no free blocks.\n"
 				"If there was an unsafe shutdown,"
 				" use \'disk/fsck %s\' to correct the disk state\n", devfile);
-	}
-	buf = emalloc9p(size);
-	if(buf == nil)
-		panic("loadfrees: nil emalloc of %llud bytes", size);
-	if(readfile(Bdfrees, Qpfrees, buf, size, 0) != size)
-		panic("loadfrees: could not load frees");
-	n = loadextents(&frees, buf, size);
-	if(chatty9p > 2)
-	dprint("loadfrees: loaded free extents %llud\n", n);
-	free(buf);
 }
 
 /* clear out /a/frees data contents after starting up
@@ -649,9 +557,7 @@
 	Iobuf *dbuf;
 	Dentry *d;
 
-	dbuf = getmetachk(Bdfrees, Bwritable, Tdentry, Qpfrees);
-	if(dbuf == nil)
-			dprint("%s",errstring[Ephase]);
+	dbuf = egetmetachk(Bdfrees, Bwritable, Tdentry, Qpfrees);
 	d = dbuf->d;
 	d->size = 0;
 	memset(d->buf, 0, Ddatasize);
--- a/dev.c
+++ b/dev.c
@@ -12,7 +12,7 @@
 	u64 size;
 
 	if((dir = dirfstat(devfd)) == nil)
-		panic("can't stat device\n");
+		error("can't stat device: %r\n");
 	size = dir->length;
 	free(dir);
 	return size;
@@ -24,7 +24,7 @@
 	s32 n;
 
 	if((n= pread(devfd, b, len*Blocksize, blkno*Blocksize)) != len*Blocksize)
-		panic("devread failed: %r\n");
+		error("devread failed: %r\n");
 	return n;
 }
 
@@ -34,7 +34,7 @@
 	s32 wn;
 
 	if((wn = pwrite(devfd, b, len*Blocksize, blkno*Blocksize)) != len*Blocksize)
-		panic("devwrite failed: %r\n");
+		error("devwrite failed: %r\n");
 	return wn;
 }
 
@@ -43,7 +43,7 @@
 {
 	devfd = open(devfile, ORDWR);
 	if(devfd < 0)
-		panic("can't open %s", devfile);
+		error("can't open %s", devfile);
 	seek(devfd, 0, 0);
 	return devsize();
 }
--- a/find.c
+++ b/find.c
@@ -19,7 +19,7 @@
 struct Parent
 {
 	u64 blkno;
-	s8 name[Namelen];
+	s8 name[Servicelen];
 };
 
 int debug = 0;
@@ -125,11 +125,11 @@
 
 	for(i =Ndir-1; i>depth; i--){
 		parents[i].blkno = 0;
-		memset(parents[i].name, 0, Namelen);
+		memset(parents[i].name, 0, Servicelen);
 	}
 	parents[depth].blkno = dblkno;
 	if(namelen == 0)
-		strncpy(parents[depth].name, name, Namelen);
+		strncpy(parents[depth].name, name, Servicelen);
 	else
 		strncpy(parents[depth].name, name, namelen);
 
@@ -147,8 +147,8 @@
 	devread(blkno, buf, 1);
 	d = (Dentry*)buf;
 	if(debug)
-		print("walkdirectory %llud tag %s name %s d->qpath %llud\n",
-				blkno, tagnames[d->tag], d->name, d->qpath);
+		print("walkdirectory %llud tag %s d->qpath %llud\n",
+				blkno, tagnames[d->tag], d->qpath);
 	if(debug)
 		showdepth(depth, blkno, d->name);
 	if(d->tag != Tdentry || d->path != d->qpath){
--- a/fns.h
+++ b/fns.h
@@ -27,4 +27,3 @@
 u16		blklen(u16 tag);
 
 void	panic(char*, ...);
-int	fprint(int, char*, ...);
--- a/free.c
+++ b/free.c
@@ -12,9 +12,13 @@
 Extents frees;
 int chatty9p = 0;
 char *devfile = nil;
-void walkdentry(u64 blkno);
+
 int checkdentry(u64 blkno, u8 tag, u64 qpath);
+void *emalloc(u32);
+s8 *estrdup(s8 *);
 void getfrees(u64 dblkno);
+void walkdentry(u64 blkno);
+void panic(char *fmt, ...);
 
 static void
 usage(void)
@@ -27,7 +31,19 @@
 main(int argc, char *argv[])
 {
 	u64 size;
+	Errenv env;
 
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+	}
+
 	ARGBEGIN{
 	default:	usage();
 	case 'D':	chatty9p++; break;
@@ -50,7 +66,7 @@
 		print("%s %llud bytes %llud blocks\n", devfile, size, size/Blocksize);
 
 	checkdentry(Bdfrees, Tdentry, Qpfrees);
-	initextents(&frees, "frees", nil);
+	initextents(&frees, "frees", 0, 0, 2, nil, fprint, panic, emalloc);
 	getfrees(Bdfrees);
 	showextents(1, "", &frees);
 	// showblocknos(1, &frees);
--- a/fsck
+++ b/fsck
@@ -10,10 +10,8 @@
 # get the total number of blocks from the config block
 nblocks=`{disk/block $disk 1 | awk '$1 == "nblocks"{ print $2 }'}
 
-# write the free extents to /adm/frees
-disk/used $disk > /tmp/used.blocks
-disk/updatefrees $disk <{disk/unused $nblocks /tmp/used.blocks}
+# write the free extents to /a/frees
+disk/updatefrees $disk <{disk/unused $nblocks <{disk/used $disk}}
 
 # change fsok from 0 to 1
 disk/fsok $disk
-rm /tmp/used.blocks
--- a/fsok.c
+++ b/fsok.c
@@ -24,6 +24,18 @@
 	u64 size;
 	s8 buf[Blocksize];
 	Dentry *d;
+	Errenv env;
+
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+	}
 
 	ARGBEGIN{
 	default:	usage();
--- a/iobuf.c
+++ b/iobuf.c
@@ -19,12 +19,12 @@
 {
 	memunitstart = sbrk((nunits+1) * Blocksize);
 	memunitpool = memunitstart+Blocksize- ((u64)memunitstart%Blocksize);
-	initextents(&memunits, "memunits", fsflush);
+	initextents(&memunits, "memunits", 1, 0, 2, fsflush, dprintfd, panic, malloc9p);
 	if(chatty9p > 4)
 		dprint("initmemunitpool: memunitpool %p nunits*Blocksize %p\n",
 				memunitpool, nunits*Blocksize);
 	if(nunits > 0)
-		bfree(&memunits, 0, nunits);
+		ufree(&memunits, 0, nunits);
 	else
 		panic("invalid nunits %llud\n", nunits);
 }
@@ -35,7 +35,7 @@
 	u64 m;
 	u8 *a;
 
-	m = balloc(&memunits, len);
+	ualloc(&memunits, len, &m);
 	if(chatty9p > 4)
 		dprint("allocmemunit: memunitpool %p m %p\n",
 				memunitpool, m);
@@ -52,7 +52,7 @@
 	if((m-memunitpool)%Blocksize)
 		panic("freememunit: (m-memunitpool)%%Blocksize %llud\n",
 				(u64)(m-memunitpool)%Blocksize);
-	bfree(&memunits, (m-memunitpool)/Blocksize, len);
+	ufree(&memunits, (m-memunitpool)/Blocksize, len);
 }
 
 s32
@@ -62,6 +62,7 @@
 	char locked[32];
 	u8 tag;
 	char *name;
+	u16 namelen;
 
 	if(p == nil)
 		return 0;
@@ -75,18 +76,18 @@
 		n = snprint(buf, nbuf, "%s %llud %s\n",
 					tagnames[p->tag], p->blkno, locked);
 	else{
-		if(p->d->tag > Tdata && p->d->tag < MAXTAG){
-			tag = p->d->tag;
-			name = p->d->name;
-		}else{
-			tag = p->d->tag;
-			name = p->d->name;
-		}
-		if(tag == Tdentry)
+		tag = p->d->tag;
+		if(tag == Tdentry && p->blkno == Bdroot){
+			n = snprint(buf, nbuf, "%s %llud / %lluds %s %s %llud\n",
+					tagnames[tag], p->blkno, (nsec()-p->atime)/Nsec,
+					locked, p->append!=nil?"data":"nil", p->appendsize);
+		}else if(tag == Tdentry){
+			readname(p->d, p->d->preli, &name, &namelen);
 			n = snprint(buf, nbuf, "%s %llud %s %lluds %s %s %llud\n",
 					tagnames[tag], p->blkno, name, (nsec()-p->atime)/Nsec,
 					locked, p->append!=nil?"data":"nil", p->appendsize);
-		else
+			free(name);
+		}else
 			n = snprint(buf, nbuf, "%s %llud %lluds %s\n",
 					tagnames[tag], p->blkno, (nsec()-p->atime)/Nsec, locked);
 	}
@@ -435,14 +436,53 @@
 }
 
 Iobuf *
-getmetachk(u64 blkno, u8 readonly, int tag, u64 qpath)
+egetbufchk(u64 blkno, u16 len, u8 readonly, int tag, u64 qpath, u64 userpc)
 {
-	return getbufchk(blkno, 1, readonly,
+	Iobuf *b;
+
+	b = getbuf(blkno, len, readonly, Bused, userpc);
+	if(b == nil){
+		error(errstring[Ephase]);
+	}else{
+		if(waserror()){
+			putbuf(b, 0);
+			nexterror();
+		}
+		b->tag = b->xiobuf[0];
+		checktag(b, len, tag, qpath);
+		poperror();
+	}
+	if(b->io == nil)
+		panic("b->io == nil blkno %llud readonly %d tag %d"
+				" qpath %llud b->blkno %llud caller %#p\n",
+				blkno, readonly, tag, qpath, b->blkno,
+				getcallerpc(&blkno));
+	return b;
+}
+
+Iobuf *
+egetmetachk(u64 blkno, u8 readonly, int tag, u64 qpath)
+{
+	return egetbufchk(blkno, 1, readonly,
 					 tag, qpath,
 					 getcallerpc(&blkno));
 }
 
 Iobuf *
+egetmeta(u64 blkno, u8 readonly, u8 freshalloc)
+{
+	Iobuf *b;
+
+	b = getbuf(blkno, 1, readonly,
+				freshalloc, getcallerpc(&blkno));
+	if(b == nil)
+		error(errstring[Ephase]);
+	else
+		b->tag = b->xiobuf[0];
+	return b;
+}
+
+Iobuf *
 getmeta(u64 blkno, u8 readonly, u8 freshalloc)
 {
 	Iobuf *b;
@@ -449,7 +489,9 @@
 
 	b = getbuf(blkno, 1, readonly,
 				freshalloc, getcallerpc(&blkno));
-	if(b != nil)
+	if(b == nil)
+		panic("getmeta blkno %llud failed\n", blkno);
+	else
 		b->tag = b->xiobuf[0];
 	return b;
 }
@@ -478,10 +520,7 @@
 		return;
 	}
 
-	buf = getmetachk(bno, Bwritable, Tdentry, qpath);
-	if(buf == nil){
-		panic("bkp: buf == nil\n");
-	}
+	buf = egetmetachk(bno, Bwritable, Tdentry, qpath);
 	memcpy(buf->d->buf, contents, Ddatasize);
 	buf->d->mtime = nsec();
 //	if(qpath == Qproot0 || qpath == Qproot1){
@@ -544,41 +583,31 @@
 checktag(Iobuf *p, u16 len, u8 tag, u64 qpath)
 {
 	uintptr pc;
-	u16 ptag;
-	u64 pqpath;
+	u16 buftag;
+	u64 bufqpath;
 
 	if(tag == Tdata){
-		ptag = p->io->tag;
-		pqpath = p->xiobuf64[p->len*Nu64perblock -1];
+		buftag = p->io->tag;
+		bufqpath = p->xiobuf64[p->len*Nu64perblock -1];
 	}else{
-		ptag = p->d->tag;
-		pqpath = p->d->path;
+		buftag = p->d->tag;
+		bufqpath = p->d->path;
 	}
 
 	if(len != p->len ||
-		tag != ptag ||
-		(qpath != Qpnone && pqpath != qpath)){
+		tag != buftag ||
+		qpath != bufqpath){
 		pc = getcallerpc(&p);
 
-		dprint("	tag = %G; expected %G; blkno = %llud\n",
-				(uint)ptag, (uint)tag, p->blkno);
-		if(qpath == Qpnone){
-			dprint("checktag pc=%p disk %s(block %llud) tag/path=%s/%llud;"
-					" expected %s len %llud p->len %llud\n",
-					pc, devfile, p->blkno,
-					tagnames[ptag], pqpath,
-					tagnames[tag],
-					len, p->len);
-		} else {
-				dprint("	tag/path = %G/%llux; expected %G/%llux\n",
-						(uint)ptag, pqpath, tag, qpath);
-				dprint("checktag pc=%p disk %s(block %llud) tag/path=%s/%llud;"
-						" expected %s/%llud\n",
-						pc, devfile, p->blkno,
-						tagnames[ptag], pqpath,
-						tagnames[tag], qpath);
-				panic("checktag failed\n");
-		}
+		dprint("checktag blkno %llud tag/path = %G/%llud; expected %G/%llud\n",
+				(uint)buftag, bufqpath, tag, qpath);
+		dprint("checktag pc=%p disk %s(block %llud) tag/path=%s/%llud;"
+				" expected %s/%llud\n",
+				pc, devfile, p->blkno,
+				tagnames[buftag], bufqpath,
+				tagnames[tag], qpath);
+		panic("checktag failed\n");
+		error(errstring[Ephase]);
 		return 0;
 	}
 	return 1;
--- a/mafs.c
+++ b/mafs.c
@@ -2,7 +2,7 @@
 #include	"pool.h"
 
 char *devfile = nil;	/* device file path */
-char service[Namelen] = "\0";
+char service[Servicelen] = "\0";
 
 /* use a byte for all these flags as hjfs? */
 u8	noauth = 0;
@@ -28,10 +28,22 @@
 main(int argc, char **argv)
 {
 	int doream, stdio;
-	char buf[Namelen];
+	char buf[Servicelen];
 	int pid, ctl;
 	u64 nmemunits, size;
+	Errenv env;
 
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+	}
+
 	/* mainmem->flags |= POOL_PARANOIA|POOL_LOGGING; */
 
 	/*
@@ -59,7 +71,7 @@
 		doream = 1;
 		/* fall through */
 	case 'n':
-		snprint(service, Namelen, "%s", EARGF(usage()));
+		snprint(service, Servicelen, "%s", EARGF(usage()));
 		break;
 	case 's':	stdio++;    break;
 	}ARGEND
@@ -103,7 +115,7 @@
 
 	formatinit();
 	initmemunitpool(nmemunits);
-	initextents(&frees, "blocks", nil);
+	initextents(&frees, "blocks", 1, 0, 2, nil, dprintfd, panic, malloc9p);
 	iobufinit();
 
 	/*
@@ -112,7 +124,7 @@
 	 */
 	init(doream, size);
 	if(service[0] == '\0')
-		snprint(service, Namelen, "%s", config.service);
+		snprint(service, Servicelen, "%s", config.service);
 
 	start9p(stdio);
 	/*
--- a/mfs.c
+++ b/mfs.c
@@ -2,7 +2,7 @@
 #include	"pool.h"
 
 char *devfile = nil;	/* device file path */
-char service[Namelen] = "\0";
+char service[Servicelen] = "\0";
 u8	noauth = 0;
 u8	readonly = 0;
 u8	shuttingdown = 0;
@@ -27,7 +27,7 @@
 main(int argc, char **argv)
 {
 	int doream, stdio;
-	char buf[Namelen];
+	char buf[Servicelen];
 	int pid, ctl;
 	u64 nmemunits, size;
 
@@ -60,7 +60,7 @@
 		doream = 1;
 		/* fall through */
 	case 'n':
-		snprint(service, Namelen, "%s", EARGF(usage()));
+		snprint(service, Servicelen, "%s", EARGF(usage()));
 		break;
 	case 's':	stdio++;    break;
 	}ARGEND
@@ -106,7 +106,7 @@
 	 */
 	init(doream, size);
 	if(service[0] == '\0')
-		snprint(service, Namelen, "%s", config.service);
+		snprint(service, Servicelen, "%s", config.service);
 
 	start9p(stdio);
 	/*
--- a/misc.c
+++ b/misc.c
@@ -3,6 +3,9 @@
 #include "dat.h"
 #include "fns.h"
 
+s8 envpidx = -1;		/* the index in _privates holding the Errenv location. hack: hoping that this will be the same across all processes */
+Errenv **envpp = nil;
+
 void
 chkrunlock(RWLock *q)
 {
@@ -36,20 +39,37 @@
 }
 
 int
+dprintfd(int fd, char *fmt, ...)
+{
+	static char buf[2048];
+	static QLock lk;
+	va_list va;
+	int n;
+
+	qlock(&lk);
+	va_start(va, fmt);
+	snprint(buf, 2048, "%d %llud: %s", getpid(), nsec(), fmt);
+	n = vfprint(fd, buf, va);
+	va_end(va);
+	qunlock(&lk);
+	return n;
+}
+
+int
 dprint(char *fmt, ...)
 {
 	static char buf[2048];
 	static QLock lk;
 	va_list va;
-	int rc;
+	int n;
 
 	qlock(&lk);
 	va_start(va, fmt);
 	snprint(buf, 2048, "%d %llud: %s", getpid(), nsec(), fmt);
-	rc = vfprint(2, buf, va);
+	n = vfprint(2, buf, va);
 	va_end(va);
 	qunlock(&lk);
-	return rc;
+	return n;
 }
 
 void
@@ -68,4 +88,46 @@
 	write(2, buf, s - buf);
 	abort();
 	exits(buf);
+}
+
+void *
+emalloc(u32 sz)
+{
+	return mallocz((ulong)sz, 1);
+}
+
+int
+waserror(void)
+{
+	Errenv *env = _privates[envpidx];
+	++env->nlabel;
+	return setjmp(env->label[env->nlabel-1]);
+}
+
+void
+poperror(void)
+{
+	Errenv *env = *envpp;
+	--env->nlabel;
+}
+
+void
+nexterror(void)
+{
+	Errenv *env = _privates[envpidx];
+	longjmp(env->label[--env->nlabel], 1);
+}
+
+void
+error(s8 *fmt, ...)
+{
+	Errenv *env = _privates[envpidx];
+	va_list arg;
+	char buf[ERRMAX];
+
+	va_start(arg, fmt);
+	vseprint(buf, buf+ERRMAX, fmt, arg);
+	va_end(arg);
+	errstr(buf, ERRMAX);
+	nexterror();
 }
--- a/mkfile
+++ b/mkfile
@@ -1,6 +1,6 @@
 </$objtype/mkfile
 
-TARG=mafs block find free fsok reconcile unused updatefrees used # mfs
+TARG=mafs block  free fsok reconcile unused updatefrees used # mfs find
 
 OFILES=\
 	9p.$O\
@@ -10,9 +10,9 @@
 	dat.$O\
 	dentry.$O\
 	dev.$O\
-	extents.$O\
 	iobuf.$O\
 	misc.$O\
+	names.$O\
 	parseconfig.$O\
 	sub.$O\
 	tag.$O\
@@ -23,7 +23,6 @@
 	all.h\
 	dat.h\
 	errno.h\
-	extents.h\
 	fns.h\
 
 BIN=/$objtype/bin/disk
@@ -45,16 +44,16 @@
 $O.fsok:	blk.$O dat.$O dev.$O misc.$O tag.$O fsok.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.updatefrees:	blk.$O dat.$O dev.$O misc.$O tag.$O extents.$O updatefrees.$O
+$O.updatefrees:	blk.$O dat.$O dev.$O misc.$O tag.$O updatefrees.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.unused:	blk.$O dat.$O dev.$O extents.$O unused.$O misc.$O
+$O.unused:	blk.$O dat.$O dev.$O unused.$O misc.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.used:	blk.$O dat.$O dev.$O extents.$O misc.$O tag.$O used.$O
+$O.used:	blk.$O dat.$O dev.$O misc.$O tag.$O used.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.free:	blk.$O dat.$O dev.$O extents.$O free.$O misc.$O tag.$O
+$O.free:	blk.$O dat.$O dev.$O free.$O misc.$O tag.$O
 	$LD $LDFLAGS -o $target $prereq
 
 $O.block:	blk.$O dat.$O dev.$O misc.$O tag.$O block.$O
@@ -63,7 +62,7 @@
 $O.find:	blk.$O dat.$O dev.$O misc.$O tag.$O find.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.reconcile:	dat.$O extents.$O misc.$O reconcile.$O
+$O.reconcile:	dat.$O misc.$O reconcile.$O
 	$LD $LDFLAGS -o $target $prereq
 
 $O.loader:	$OFILES
--- /dev/null
+++ b/names.c
@@ -1,0 +1,238 @@
+#include	"all.h"
+
+enum {
+	Nquantum = 32, /* size of a Name is a multiple of 32 bytes */
+	Nfirst = 6,		/* length of name stored in Name */
+};
+
+typedef	struct	Name	Name;
+#pragma pack on
+struct Name
+{
+	u16	namelen;
+	s8 name[Nfirst];	/* first 6 bytes. If longer, use noffset */
+	u64 noffset;		/* offset where the name is stored in .nl - long names file */
+};
+#pragma pack off
+
+s8
+searchnames(Dentry *d, char *searchname, u64 *relip)
+{
+	u16 searchnamelen, slen;
+	s8 *namebuf;
+	u64 n, reli;
+	Name nm;
+
+	/* is this valid? */
+	if(d == nil || searchname == nil || relip == nil)
+		panic("searchnames: should not be happening");
+
+	searchnamelen = strlen(searchname);
+	if(searchnamelen == 0)
+		error(errstring[Ebadname]);
+
+	reli = *relip = 0;
+	slen = min(searchnamelen,Nfirst);
+	if(searchnamelen > Nfirst){
+		namebuf = emalloc9p(searchnamelen);
+		if(waserror()){
+			free(namebuf);
+			nexterror();
+		}
+	}else
+		namebuf = nil;
+	while(readfile(d->dblocks[In], d->qpath+1+In,
+					(s8*)&nm, sizeof(Name), reli*sizeof(Name)) == sizeof(Name)){
+		if(searchnamelen == nm.namelen &&
+			strncmp(nm.name, searchname, slen) == 0){
+			if(searchnamelen <= Nfirst){
+				*relip = reli;
+				return 1;
+			}else{
+				n = readfile(d->dblocks[Inl], d->qpath+1+Inl,
+								namebuf, searchnamelen, nm.noffset);
+				if(n != searchnamelen)
+					error(errstring[Einvread]);
+				if(n == searchnamelen &&
+					strncmp(searchname, namebuf, searchnamelen) == 0){
+					*relip = reli;
+					poperror();
+					free(namebuf);
+					return 1;
+				}
+			}
+		}
+		if(*relip == 0 && nm.namelen == 0)
+			*relip = reli; /* a zeroed out reli, reuse if none found */
+		reli++;
+	}
+
+	if(namebuf){
+		poperror();
+		free(namebuf);
+	}
+
+	/* nothing found */
+	if(*relip == 0)
+		*relip = reli;	/* use a new reli at the end */
+	return 0;
+}
+
+void
+newnames(Dentry *pd, u16 uid, u64 pdblkno, Iobuf **iobufs)
+{
+	Iobuf *iobuf;
+	Dentry *d;
+	u8 i;
+
+	for(i = 0; i < 3; i++){
+		iobuf = iobufs[i];
+		pd->dblocks[i] = iobuf->blkno;
+		d = iobuf->d;
+		d->flags = Dsys;
+		d->pdblkno = pdblkno;
+		d->pqpath = pd->qpath;
+		d->preli = i;
+		d->uid = pd->uid;
+		d->gid = pd->gid;
+		d->muid = pd->muid;
+		d->mode = pd->mode & ~DMDIR;
+		d->mtime = nsec();
+		d->version = 1;
+		putbuf(iobuf, 1);
+	}
+	addname(pd, uid, 0, ".n");
+	addname(pd, uid, 1, ".nl");
+	addname(pd, uid, 2, ".nle");
+}
+
+/* returns namelen */
+u16
+readname(Dentry *pd, u64 preli, s8 **namep, u16 *namelen)
+{
+	u16 n;
+	Name nm;
+
+	n = readfile(pd->dblocks[In], pd->qpath+1+In, (s8*)&nm, sizeof(Name), preli*sizeof(Name));
+	if(n != sizeof(Name))
+		error(errstring[Einvread]);
+
+	*namelen = nm.namelen;
+	*namep = emalloc9p(nm.namelen+1);
+	if(nm.namelen <= Nfirst){
+		strncpy(*namep, nm.name, nm.namelen);
+		return nm.namelen;
+	}
+	return readfile(pd->dblocks[Inl], pd->qpath+1+Inl, *namep, nm.namelen, nm.noffset);
+}
+
+void
+rmname(Dentry *pd, s16 uid, u64 preli)
+{
+	u64 n;
+	Name nm;
+	Extents *nes;
+	s8 esname[32];
+
+	n = readfile(pd->dblocks[In], pd->qpath+1+In, (s8*)&nm, sizeof(Name), preli*sizeof(Name));
+	if(n != sizeof(Name))
+		error(errstring[Einvread]);
+
+	if(nm.namelen > Nfirst){
+		nes = emalloc9p(sizeof(Extents));
+		if(waserror()){
+			freeextents(nes);
+			nexterror();
+		}
+		snprint(esname, 32, "%llud", pd->qpath);
+		initextents(nes, esname, 0, 0, 2, nil, dprintfd, panic, malloc9p);
+		loadextentsfile(pd->dblocks[Inle], pd->qpath+1+Inle, nes);
+		ufree(nes, nm.noffset, nm.namelen);
+		saveextentstofile(pd->dblocks[Inle], pd->qpath+1+Inle, uid, nes);
+		poperror();
+		freeextents(nes);
+	}
+	memset(&nm, 0, sizeof(Name));
+	n = writefile(pd->dblocks[In], pd->qpath+1+In, uid, (s8*)&nm, sizeof(Name), preli*sizeof(Name));
+	if(n != sizeof(Name))
+		error(errstring[Einvwrite]);
+}
+
+u16
+addnamelen(Dentry *pd, u16 uid, u64 preli, s8 *name, u16 namelen)
+{
+	s8 *buf;
+	Extents *nes;
+	s8 esname[32];
+	u64 start, size;
+	Name nm;
+
+	nm.namelen = namelen;
+	strncpy(nm.name, name, Nfirst);
+	if(nm.namelen <= Nfirst){
+		nm.noffset = 0;
+		if(writefile(pd->dblocks[In], pd->qpath+1+In, uid,
+						(s8*)&nm, sizeof(Name), preli*sizeof(Name)) == sizeof(Name))
+			return nm.namelen;
+		return 0;
+	}
+
+	nes = emalloc9p(sizeof(Extents));
+	if(waserror()){
+		freeextents(nes);
+		nexterror();
+	}
+	snprint(esname, 32, "%llud", pd->qpath);
+	initextents(nes, esname, 0, 0, 2, nil, dprintfd, panic, malloc9p);
+	loadextentsfile(pd->dblocks[Inle], pd->qpath+1+Inle, nes);
+
+	/* reusing */
+	buf = emalloc9p(nm.namelen);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
+	strncpy(buf, name, nm.namelen);
+	if(ualloc(nes, namelen, &start)){
+		if(writefile(pd->dblocks[Inl], pd->qpath+1+Inl, uid, buf, nm.namelen, start) != namelen)
+			error(errstring[Einvwrite]);
+		nm.noffset = start;
+		saveextentstofile(pd->dblocks[Inle], pd->qpath+1+Inle, uid, nes);
+	}else{
+		nm.noffset = size = readfilesize(pd->dblocks[Inl], pd->qpath+1+Inl);
+		if(writefile(pd->dblocks[Inl], pd->qpath+1+Inl, uid, buf, nm.namelen, size) != nm.namelen)
+			error(errstring[Einvwrite]);
+	}
+	poperror();
+	free(buf);
+	poperror();
+	freeextents(nes);
+	if(writefile(pd->dblocks[In], pd->qpath+1+In, uid,
+					(s8*)&nm, sizeof(Name), preli*sizeof(Name)) != sizeof(Name))
+		error(errstring[Einvwrite]);
+
+	return nm.namelen;
+}
+
+u16
+addname(Dentry *pd, u16 uid, u64 preli, s8 *name)
+{
+	return addnamelen(pd, uid, preli, name, strlen(name));
+}
+
+u16
+updatename(Dentry *pd, u16 uid, u64 preli, s8 *name)
+{
+	u16 newlen, n;
+	Name nm;
+
+	/* if the namelen matches with the existing, change in-place */
+	newlen = strlen(name);
+	n = readfile(pd->dblocks[In], pd->qpath+1+In, (s8*)&nm, sizeof(Name), preli*sizeof(Name));
+	if(n != sizeof(Name))
+		error(errstring[Einvread]);
+	if(nm.namelen > Nfirst)
+		rmname(pd, uid, preli);
+
+	return addnamelen(pd, uid, preli, name, newlen);
+}
--- a/parseconfig.c
+++ b/parseconfig.c
@@ -53,7 +53,7 @@
 //				if((c->root.dest[1] = atoll(tokens[++i])) == 0)
 //					panic("mafs: %s initconfig(): invalid destination root\n", service);
 		}else if(strcmp(tokens[i], "service") == 0){
-			snprint(c->service, Namelen,"%s", tokens[++i]);
+			snprint(c->service, Servicelen,"%s", tokens[++i]);
 			if((nl=strchr(c->service, '\n')) != nil)
 				*nl = '\0';
 		}
--- a/reconcile.c
+++ b/reconcile.c
@@ -43,14 +43,14 @@
 };
 typedef struct Stream Stream;
 
-static void init(Stream *s);
 void collect(Stream * s);
+void common(u64 nblocks, Stream *u, Stream *f);
 void *emalloc(u32);
-s8 *estrdup(s8 *);
-void show(Stream * s);
 s8 find(Extents *es, u64 bno);
-void common(u64 nblocks, Stream *u, Stream *f);
+static void init(Stream *s);
 void missing(u64 nblocks, Stream *u, Stream *f);
+void panic(char *fmt, ...);
+void show(Stream * s);
 
 static void
 usage(void)
@@ -65,14 +65,26 @@
 {
 	Stream u, f;	/* u = used, f = free */
 	u64 nblocks;
+	Errenv env;
 
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+	}
+
 	memset(&u, 0, sizeof(Stream));
 	memset(&f, 0, sizeof(Stream));
 	ARGBEGIN{
 	default:	usage();
 	case 'D':	chatty9p = ++debug;					break;
-	case 'u':	u.file = estrdup(EARGF(usage()));	break;
-	case 'F':	f.file = estrdup(EARGF(usage()));	break;
+	case 'u':	u.file = strdup(EARGF(usage()));	break;
+	case 'F':	f.file = strdup(EARGF(usage()));	break;
 	}ARGEND
 
 	if(argc != 1)
@@ -187,7 +199,7 @@
 	if(debug)
 		print("collect %s ", s->name);
 	s->es = emalloc(sizeof(Extents));
-	initextents(s->es, s->name, nil);
+	initextents(s->es, s->name, 0, 0, 2, nil, fprint, panic, emalloc);
 	while((s->buf = Brdstr(&s->bp, '\n', 1)) != nil) {
 		p = s->buf;
 		start = strtoull(p, &ep, 10);
@@ -209,7 +221,7 @@
 			panic("loadextents does not match up: start %llud end %llud nblocks %llud",
 					start, end, nblocks);
 
-		add(s->es, start, nblocks);
+		ufree(s->es, start, nblocks);
 // show(s);
 	}
 	if(debug)
--- a/sub.c
+++ b/sub.c
@@ -3,22 +3,26 @@
 extern Srv mysrv;
 
 u64
-newqpath()
+newqpaths(u8 n)
 {
 	u64 qpath;
 	Iobuf *sb;
 	Dentry *s;
 
-	sb = getmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
-	if(sb == nil){
-		panic("newqpath: sb == nil\n");
-	}
+	sb = egetmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
 	s = sb->d;
-	qpath = s->qidgen++;
+	qpath = s->qidgen;
+	s->qidgen += n;
 	putbuf(sb, 1);
 	return qpath;
 }
 
+u64
+newqpath()
+{
+	return newqpaths(1);
+}
+
 /*
  * what are legal characters in a name?
  * only disallow control characters.
@@ -30,12 +34,11 @@
 {
 	int i, c;
 
-	for(i=0; i<Namelen; i++) {
+	for(i=0; i<strlen(n); i++) {
 		c = *n & 0xff;
 		if(c == 0) {
 			if(i == 0)
 				return 1;
-			memset(n, 0, Namelen-i);
 			return 0;
 		}
 		if(c <= 040)
@@ -76,7 +79,7 @@
 	if(len > Maxdatablockunits)
 		panic("allocblocks(): invalid len %llud tag %s qpath %llud\n",
 				len, tagnames[tag], qpath);
-	blkno = balloc(&frees, len);
+	ualloc(&frees, len, &blkno);
 	if(chatty9p > 1)
 		dprint("alloc %llud\n", blkno);
 
@@ -96,12 +99,24 @@
 Iobuf *
 allocmeta(int tag, u64 qpath)
 {
-	Iobuf *b;
+	return allocblocks(1, tag, qpath);
+}
 
-	b = allocblocks(1, tag, qpath);
-	if(b == nil)
-		panic("out of free blocks\n");
-	return b;
+u8
+allocdentries(u16 n, Iobuf **iobufs, u64 qpath)
+{
+	u8 i, j;
+
+	for(i = 0; i < n; i++){
+		iobufs[i] = allocblocks(1, Tdentry, qpath+i);
+		if(iobufs[i] == nil){
+			for(j = 0; j < i; j++)
+				freeblockbuf(iobufs[j]);
+			return i;
+		}
+		iobufs[i]->d->qpath = qpath+i;
+	}
+	return i;
 }
 
 /* the buf should have been wlock()'ed */
@@ -114,7 +129,7 @@
 	buf->tag = Tblank;
 	/* clear the buf to avoid leaks on reuse */
 	memset(buf->xiobuf, 0, buf->len*Blocksize);
-	bfree(&frees, buf->blkno, buf->len);
+	ufree(&frees, buf->blkno, buf->len);
 	putbuf(buf, 0);
 }
 
@@ -130,9 +145,7 @@
 		return;
 	}
 
-	buf = getbufchk(blkno, len, Bwritable, tag, qpath, getcallerpc(&blkno));
-	if(buf == nil)
-		dprint("%s",errstring[Ephase]);
+	buf = egetbufchk(blkno, len, Bwritable, tag, qpath, getcallerpc(&blkno));
 	freeblockbuf(buf);
 }
 
@@ -161,10 +174,7 @@
 	Iobuf *sb;
 	Dentry *s;
 
-	sb = getmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
-	if(sb == nil){
-		panic("fsok: sb == nil\n");
-	}
+	sb = egetmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
 	s = sb->d;
 	s->fsok = ok;
 	if(chatty9p > 1){
@@ -182,18 +192,17 @@
 }
 
 void
-reamfile(u64 dblkno, u64 qpath, char *name, u64 size, u64 pdblkno, u64 pqpath)
+reamfile(u64 dblkno, u64 qpath, char *name, u64 size, u64 pdblkno, u64 pqpath, u64 preli)
 {
 	Iobuf *b;
 	Dentry *d;
 
-	b = getmeta(dblkno, Bwritable, Bfreshalloc);
-	if(b == nil)
-		panic("reamfile dblkno %llud b == nil\n", dblkno);
+	b = egetmeta(dblkno, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, qpath);
 	d = b->d;
-	d->namelen = strlen(name);
-	strcpy(d->name, name);
+	USED(name);
+/*	d->namelen = strlen(name);
+	strcpy(d->name, name);*/
 	d->uid = d->muid = d->gid = -1;
 	d->mode =
 		((DMREAD) << 6) |
@@ -200,11 +209,12 @@
 		((DMREAD) << 3) |
 		((DMREAD) << 0);
 	d->qpath = qpath;
-	d->version = 0;
+	d->version = 1;
 	d->mtime = nsec();
 	d->size = size;
 	d->pdblkno = pdblkno;
 	d->pqpath = pqpath;
+	d->preli = preli;
 	putbuf(b, 1);
 }
 
@@ -219,6 +229,7 @@
 	Iobuf *b;
 	Dentry *d;
 	char users[128+Userlen*3], cbuf[Ddatasize];
+	char inle[] = "0 439 440\n\0";
 	char *user;
 	int userslen, n;
 
@@ -231,31 +242,152 @@
 					"10006:%s:%s:\n", user, user, user);
 
 	/* cannot show this in /a though as the block number is 0 */
-	b = getmeta(Bdmagic, Bwritable, Bfreshalloc);
+	b = egetmeta(Bdmagic, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpmagic);
 	d = b->d;
-	d->namelen = 5;
-	strncpy(d->name, "magic", 6);
+	// d->namelen = 5;
+	// strncpy(d->name, "magic", 6);
 	d->uid = d->muid = d->gid = -1;
-	d->mode = DMDIR |
-		((DMREAD|DMWRITE|DMEXEC) << 6) |
-		((DMREAD|DMWRITE|DMEXEC) << 3) |
-		((DMREAD|DMWRITE|DMEXEC) << 0);
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
 	d->qpath = Qpmagic;
-	d->version = 0;
+	d->version = 1;
 	d->mtime = nsec();
 	d->pdblkno = Bda;
 	d->pqpath = Qpa;
+	d->preli = 0;
 	n = snprint((s8*)d->buf, Ddatasize, "%s%llud\n",
 			magic, Blocksize);
 	d->size = n;
 	putbuf(b, 1);
 
-	b = getmeta(Bda, Bwritable, Bfreshalloc);
+	b = egetmeta(Bdrootnames, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qprootnames);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qprootnames;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bdroot;
+	d->pqpath = Qproot;
+	d->preli = In;
+	putbuf(b, 1);
+
+	b = egetmeta(Bdrootlnames, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qprootlnames);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qprootlnames;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bdroot;
+	d->pqpath = Qproot;
+	d->preli = Inl;
+	putbuf(b, 1);
+
+	b = egetmeta(Bdrootlnamesextents, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qprootlnamesextents);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qprootlnamesextents;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bdroot;
+	d->pqpath = Qproot;
+	d->preli = Inle;
+	d->size = sizeof inle;
+	strcpy((s8*)d->buf, inle);
+	putbuf(b, 1);
+
+	b = egetmeta(Bdroot, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qproot);
+	d = b->d;
+	d->uid = d->muid = -1;
+	d->gid = -1;
+	d->mode = DMDIR |
+		((DMREAD|DMWRITE|DMEXEC) << 6) |
+		((DMREAD|DMWRITE|DMEXEC) << 3) |
+		((DMREAD|DMWRITE|DMEXEC) << 0);
+	d->qpath = Qproot;
+	d->version = 1;
+	d->mtime = nsec();
+	d->dblocks[0] = Bdrootnames;
+	d->dblocks[1] = Bdrootlnames;
+	d->dblocks[2] = Bdrootlnamesextents;
+	d->dblocks[3] = Bda;
+	addname(d, -1, 0, ".n");
+	addname(d, -1, 1, ".nl");
+	addname(d, -1, 2, ".nle");
+	addname(d, -1, 3, "a");
+	putbuf(b, 1);
+
+	b = egetmeta(Bdanames, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qpanames);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qpanames;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bda;
+	d->pqpath = Qpa;
+	d->preli = In;
+	putbuf(b, 1);
+
+	b = egetmeta(Bdalnames, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qpalnames);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qpalnames;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bda;
+	d->pqpath = Qpa;
+	d->preli = Inl;
+	putbuf(b, 1);
+
+	b = egetmeta(Bdalnamesextents, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qpalnamesextents);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qpalnamesextents;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bda;
+	d->pqpath = Qpa;
+	d->preli = Inle;
+	d->size = sizeof inle;
+	strcpy((s8*)d->buf, inle);
+	putbuf(b, 1);
+
+	b = egetmeta(Bda, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpa);
 	d = b->d;
-	d->namelen = 3;
-	strncpy(d->name, "a", 4);
 	d->uid = d->muid = d->gid = -1;
 	d->mode = DMDIR |
 		((DMREAD|DMWRITE|DMEXEC) << 6) |
@@ -266,19 +398,80 @@
 	d->mtime = nsec();
 	d->pdblkno = Bdroot;
 	d->pqpath = Qproot;
-	d->dblocks[0] = Bdconfig;
-	d->dblocks[1] = Bdsuper;
-	d->dblocks[2] = Bdusers;
+	d->preli = 3;
+	d->dblocks[0] = Bdanames;
+	d->dblocks[1] = Bdalnames;
+	d->dblocks[2] = Bdalnamesextents;
 	d->dblocks[3] = Bdbkp;
-	d->dblocks[4] = Bdfrees;
+	d->dblocks[4] = Bdconfig;
 	d->dblocks[5] = Bdctl;
+	d->dblocks[6] = Bdfrees;
+	d->dblocks[7] = Bdsuper;
+	d->dblocks[8] = Bdusers;
+	addname(d, -1, 0, ".n");
+	addname(d, -1, 1, ".nl");
+	addname(d, -1, 2, ".nle");
+	addname(d, -1, 3, "bkp");
+	addname(d, -1, 4, "config");
+	addname(d, -1, 5, "ctl");
+	addname(d, -1, 6, "frees");
+	addname(d, -1, 7, "super");
+	addname(d, -1, 8, "users");
 	putbuf(b, 1);
 
-	b = getmeta(Bdbkp, Bwritable, Bfreshalloc);
+	b = egetmeta(Bdbkpnames, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qpbkpnames);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qpbkpnames;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bdbkp;
+	d->pqpath = Qpbkp;
+	d->preli = In;
+	putbuf(b, 1);
+
+	b = egetmeta(Bdbkplnames, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qpbkplnames);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qpbkplnames;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bdbkp;
+	d->pqpath = Qpbkp;
+	d->preli = Inl;
+	putbuf(b, 1);
+
+	b = egetmeta(Bdbkplnamesextents, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qpbkplnamesextents);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qpbkplnamesextents;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bdbkp;
+	d->pqpath = Qpbkp;
+	d->preli = Inle;
+	d->size = sizeof inle;
+	strcpy((s8*)d->buf, inle);
+	putbuf(b, 1);
+
+	b = egetmeta(Bdbkp, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpbkp);
 	d = b->d;
-	d->namelen = 3;
-	strncpy(d->name, "bkp", 4);
 	d->uid = d->muid = d->gid = -1;
 	d->mode = DMDIR |
 		((DMREAD|DMWRITE|DMEXEC) << 6) |
@@ -290,16 +483,74 @@
 	d->size = strlen(users)+1;
 	d->pdblkno = Bda;
 	d->pqpath = Qpa;
-	d->dblocks[0] = bdconfig0;
-	d->dblocks[1] = bdsuper0;
-	d->dblocks[2] = bdroot0;
+	d->preli = 3;
+	d->dblocks[0] = Bdbkpnames;
+	d->dblocks[1] = Bdbkplnames;
+	d->dblocks[2] = Bdbkplnamesextents;
+	d->dblocks[3] = bdconfig0;
+	d->dblocks[4] = bdroot0;
+	d->dblocks[5] = bdsuper0;
+	addname(d, -1, 0, ".n");
+	addname(d, -1, 1, ".nl");
+	addname(d, -1, 2, ".nle");
+	addname(d, -1, 3, "config.0");
+	addname(d, -1, 4, "root.0");
+	addname(d, -1, 5, "super.0");
 	putbuf(b, 1);
 
-	b = getmeta(Bdusers, Bwritable, Bfreshalloc);
+	b = egetmeta(Bdusersnames, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qpusersnames);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qpusersnames;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bdusers;
+	d->pqpath = Qpusers;
+	d->preli = In;
+	putbuf(b, 1);
+
+	b = egetmeta(Bduserslnames, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qpuserslnames);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qpuserslnames;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bdusers;
+	d->pqpath = Qpusers;
+	d->preli = Inl;
+	putbuf(b, 1);
+
+	b = egetmeta(Bduserslnamesextents, Bwritable, Bfreshalloc);
+	settag(b, Tdentry, Qpuserslnamesextents);
+	d = b->d;
+	d->uid = d->muid = d->gid = -1;
+	d->mode =
+		((DMREAD) << 6) |
+		((DMREAD) << 3) |
+		((DMREAD) << 0);
+	d->qpath = Qpuserslnamesextents;
+	d->version = 1;
+	d->mtime = nsec();
+	d->pdblkno = Bdusers;
+	d->pqpath = Qpusers;
+	d->preli = Inle;
+	d->size = sizeof inle;
+	strcpy((s8*)d->buf, inle);
+	putbuf(b, 1);
+
+	b = egetmeta(Bdusers, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpusers);
 	d = b->d;
-	d->namelen = 5;
-	strncpy(d->name, "users", 6);
 	d->uid = d->muid = d->gid = -1;
 	d->mode = DMDIR |
 		((DMREAD|DMWRITE|DMEXEC) << 6) |
@@ -306,20 +557,26 @@
 		((DMREAD|DMWRITE|DMEXEC) << 3) |
 		((DMREAD|DMWRITE|DMEXEC) << 0);
 	d->qpath = Qpusers;
-	d->version = 0;
+	d->version = 1;
 	d->mtime = nsec();
-	d->size = strlen(users)+1;
 	d->pdblkno = Bda;
 	d->pqpath = Qpa;
-	d->dblocks[0] = Bdusersinuse;
-	d->dblocks[1] = Bdusersstaging;
+	d->preli = 8;
+	d->dblocks[0] = Bdusersnames;
+	d->dblocks[1] = Bduserslnames;
+	d->dblocks[2] = Bduserslnamesextents;
+	d->dblocks[3] = Bdusersinuse;
+	d->dblocks[4] = Bdusersstaging;
+	addname(d, -1, 0, ".n");
+	addname(d, -1, 1, ".nl");
+	addname(d, -1, 2, ".nle");
+	addname(d, -1, 3, "inuse");
+	addname(d, -1, 4, "staging");
 	putbuf(b, 1);
 
-	b = getmeta(Bdusersinuse, Bwritable, Bfreshalloc);
+	b = egetmeta(Bdusersinuse, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpusersinuse);
 	d = b->d;
-	d->namelen = 5;
-	strncpy(d->name, "inuse", 6);
 	d->uid = d->muid = d->gid = -1;
 	d->mode =
 		((DMREAD) << 6) |
@@ -326,36 +583,18 @@
 		((DMREAD) << 3) |
 		((DMREAD) << 0);
 	d->qpath = Qpusersinuse;
-	d->version = 0;
+	d->version = 1;
 	d->mtime = nsec();
 	d->size = userslen;
 	d->pdblkno = Bdusers;
 	d->pqpath = Qpusers;
+	d->preli = 3;
 	strcpy((s8*)d->buf, users);
 	putbuf(b, 1);
 
-	b = getmeta(Bdroot, Bwritable, Bfreshalloc);
-	settag(b, Tdentry, Qproot);
-	d = b->d;
-	d->namelen = 1;
-	strncpy(d->name, "/", 5);
-	d->uid = d->muid = -1;
-	d->gid = -1;
-	d->mode = DMDIR |
-		((DMREAD|DMWRITE|DMEXEC) << 6) |
-		((DMREAD|DMWRITE|DMEXEC) << 3) |
-		((DMREAD|DMWRITE|DMEXEC) << 0);
-	d->qpath = Qproot;
-	d->version = 0;
-	d->mtime = nsec();
-	d->dblocks[0] = Bda;
-	putbuf(b, 1);
-
-	b = getmeta(Bdconfig, Bwritable, Bfreshalloc);
+	b = egetmeta(Bdconfig, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpconfig);
 	d = b->d;
-	d->namelen = 6;
-	strncpy(d->name, "config", 6);
 	d->uid = d->muid = d->gid = -1;
 	d->mode =
 		((DMREAD) << 6) |
@@ -362,20 +601,21 @@
 		((DMREAD) << 3) |
 		((DMREAD) << 0);
 	d->qpath = Qpconfig;
-	d->version = 0;
+	d->version = 1;
 	d->mtime = nsec();
 	memset(cbuf, 0, Ddatasize);
 	d->size = configstr(cbuf, Ddatasize);
 	d->pdblkno = Bda;
 	d->pqpath = Qpa;
+	d->preli = 4;
 	strncpy((s8*)d->buf, cbuf, d->size);
 	putbuf(b, 1);
 
-	reamfile(Bdctl, Qpctl, "ctl", 0, Bda, Qpa);
-	reamfile(Bdfrees, Qpfrees, "frees", 0, Bda, Qpa);
+	reamfile(Bdctl, Qpctl, "ctl", 0, Bda, Qpa, 5);
+	reamfile(Bdfrees, Qpfrees, "frees", 0, Bda, Qpa, 6);
 
 	reamfile(Bdusersstaging, Qpusersstaging, "staging",
-			 0, Bdusers, Qpusers);
+			 0, Bdusers, Qpusers, 4);
 }
 
 void
@@ -385,12 +625,8 @@
 	Dentry *s;
 
 	sbuf = getbuf(Bdsuper, 1, Bwritable, Bfreshalloc, getcallerpc(&size));
-	if(sbuf == nil)
-		panic("superream: sbuf == nil");
 	settag(sbuf, Tdentry, Qpsuper);
 	s = sbuf->d;
-	s->namelen = 5;
-	strncpy(s->name, "super", 6);
 	s->uid = s->muid = s->gid = -1;
 	s->mode =
 		((DMREAD) << 6) |
@@ -397,11 +633,12 @@
 		((DMREAD) << 3) |
 		((DMREAD) << 0);
 	s->qpath = Qpsuper;
-	s->version = 0;
+	s->version = 1;
 	s->mtime = nsec();
 	s->size = sizeof(Super);
 	s->pdblkno = Bda;
 	s->pqpath = Qpa;
+	s->preli = 7;
 	s->qidgen = Nqidgen;
 	s->fsok = 1;
 
@@ -417,14 +654,14 @@
 	config.config.srcbno = Bdconfig;
 	config.super.srcbno = Bdsuper;
 	config.root.srcbno = Bdroot;
-	strncpy(config.service, service, Namelen);
+	strncpy(config.service, service, Servicelen);
 
 	reamfile(config.config.dest[0], Qpconfig0, "config.0",
-			 0, Bdbkp, Qpbkp);
-	reamfile(config.super.dest[0], Qpsuper0, "super.0",
-			 0, Bdbkp, Qpbkp);
+			0, Bdbkp, Qpbkp, 3);
 	reamfile(config.root.dest[0], Qproot0, "root.0",
-			 0, Bdbkp, Qpbkp);
+			 0, Bdbkp, Qpbkp, 4);
+	reamfile(config.super.dest[0], Qpsuper0, "super.0",
+			 0, Bdbkp, Qpbkp, 5);
 	putbuf(sbuf, 1);
 
 	if(chatty9p > 1)
@@ -438,7 +675,7 @@
 	reamdefaults(config.config.dest[0], config.super.dest[0], config.root.dest[0]);
 
 	if(config.root.dest[0]-Nbused > 0)
-		bfree(&frees, Nbused, config.root.dest[0]-Nbused);
+		ufree(&frees, Nbused, config.root.dest[0]-Nbused);
 	else if (config.root.dest[0] == Nbused)
 		dprint("mafs: %s no free blocks: config.root.dest[0] %llud Nbused %ud Nminblocks %llud\n",
 				service, config.root.dest[0], Nbused, Nminblocks);
@@ -474,7 +711,7 @@
 				service, devfile, size, nblocks, Blocksize);
 		dprint("	(data block size: usable %d bytes)\n",
 				Maxdatablocksize);
-		dprint("	(name length: %d bytes)\n", Namelen);
+		dprint("	(name length: %d bytes)\n", Servicelen);
 	}
 
 	memset(buf, 0, Blocksize);
@@ -506,10 +743,10 @@
 	}
 
 	/* check magic */
-	b = getmetachk(Bdmagic, Breadonly, Tdentry, Qpmagic);
-	if(b == nil){
-		panic("Invalid magic: %s",errstring[Ephase]);
-		return;
+	b = egetmetachk(Bdmagic, Breadonly, Tdentry, Qpmagic);
+	if(waserror()){
+		putbuf(b, 0);
+		nexterror();
 	}
 	if(strncmp((s8*)b->d->buf,magic,strlen(magic)) != 0){
 		print("init: bad magic -%s-", (s8*)b->io+256);
@@ -521,14 +758,11 @@
 				Blocksize, unitsize);
 		panic("bad Blocksize != unitsize");
 	}
+	poperror();
 	putbuf(b, 0);
 
 	/* check super */
-	sb = getmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
-	if(sb == nil){
-		panic("Invalid super: %s",errstring[Ephase]);
-		return;
-	}
+	sb = egetmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
 	s = sb->d;
 	if(config.size != size){
 		dprint("corrupted disk or disk size changed:"
--- a/tag.c
+++ b/tag.c
@@ -150,8 +150,8 @@
 		return Maxdatablockunits;
 
 	tot = lastblksize +Ddataidssize;
-	if(tot%Blocksize == 0)
-		return tot/Blocksize; /* aligns to the Blocksize */
-	else
+	if(tot%Blocksize)
 		return 1+(tot/Blocksize);
+	else
+		return tot/Blocksize; /* aligns to the Blocksize */
 }
--- a/tests/mkfile
+++ b/tests/mkfile
@@ -24,6 +24,15 @@
 $O.testconfig:	../dat.$O ../parseconfig.$O ../misc.$O testconfig.$O
 	$LD $LDFLAGS -o $target $prereq
 
+$O.testcblock:	testcblock.$O
+	$LD $LDFLAGS -o $target $prereq
+
+$O.pwrites:	pwrites.$O
+	$LD $LDFLAGS -o $target $prereq
+
+$O.offsets:	offsets.$O
+	$LD $LDFLAGS -o $target $prereq
+
 t3:VQ:
 	./regress.rc test.3
 	echo regress done
--- a/tests/sizes.c
+++ b/tests/sizes.c
@@ -8,16 +8,28 @@
 {
 	int t;
 	u64 n;
+	Errenv env;
 
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		print(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+		exits("error");
+	}
+
 	print("Blocksize %llud Maxdatablockunits %d\n",
 			Blocksize, Maxdatablockunits);
+	print("Dentry size %d\n", sizeof(Dentry));
 	print("Dentryhdr size %d Ddatasize %llud\n",
 			sizeof(Dentryhdr), Ddatasize);
-	print("Dentry size %d Namelen %d\n",
-			sizeof(Dentry), Namelen);
 	print("Datahdr size %d Ddataidssize %d Maxdatablocksize %llud\n",
 			sizeof(Datahdr), Ddataidssize, Maxdatablocksize);
-	print("Namelen %d Ndblock %d Niblock %d\n", Namelen, Ndblock, Niblock);
+	print("Ndblock %d Niblock %d\n", Ndblock, Niblock);
 	print("Nindperblock %llud Maxdatablocksize %llud\n",
 			Nindperblock, Maxdatablocksize);
 
--- a/tests/test.0/notes
+++ b/tests/test.0/notes
@@ -1,18 +1,35 @@
 Creates a disk of 32 blocks. Unmarked blocks are blank.
 
 block	- description
-0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
-20		- / direntry
+0		- /a/magic dir entry and data
+1		- /a/config dir entry
+2		- /a/super dir entry
 
-26	- /adm/bkp/root.0 dir entry
-28	- /adm/bkp/super.0 dir entry
-30	- /adm/bkp/config.0 dir entry
+3		- /a/ dir entry
+4		- /a/ dir names entry
+5		- /a/ dir long names entry
+6		- /a/ dir long names extents entry
+
+7		- /a/users/ dir entry
+8		- /a/users/ dir names entry
+9		- /a/users/ dir long names entry
+10		- /a/users/ dir long names extents entry
+
+11		- /a/bkp/ dir entry
+12		- /a/bkp/ dir names entry
+13		- /a/bkp/ dir long names entry
+14		- /a/bkp/ dir long names extents entry
+
+15		- /a/users/inuse dir entry
+16		- /a/frees dir entry
+17		- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
+
+19		- / dir entry
+20		- / dir names entry
+21		- / dir names entry
+22		- / dir names extents entry
+
+26	- /a/bkp/root.0 dir entry
+28	- /a/bkp/super.0 dir entry
+30	- /a/bkp/config.0 dir entry
--- a/tests/test.1/action.rc
+++ b/tests/test.1/action.rc
@@ -7,4 +7,4 @@
 touch /n/^$service^/dir1/file1
 echo test >> /n/^$service^/dir1/file1
 
-cat /n/^$service^/adm/ctl
+cat /n/^$service^/a/ctl
--- a/tests/test.1/notes
+++ b/tests/test.1/notes
@@ -5,20 +5,20 @@
 
 block	- description
 0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
+2		- /a/config dir entry
+4		- /a/super dir entry
+6		- /a/ dir entry
+8		- /a/users/ dir entry
+10		- /a/bkp/ dir entry
+12		- /a/users/inuse dir entry
+14	- /a/frees dir entry
+16	- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
 20		- / direntry
 
 22	- /dir1 directory entry
 24	- /dir1/file1 directory entry and contents
 
-26	- /adm/bkp/root.0 dir entry
-28	- /adm/bkp/super.0 dir entry
-30	- /adm/bkp/config.0 dir entry
+26	- /a/bkp/root.0 dir entry
+28	- /a/bkp/super.0 dir entry
+30	- /a/bkp/config.0 dir entry
--- a/tests/test.2/notes
+++ b/tests/test.2/notes
@@ -7,19 +7,19 @@
 
 block	- description
 0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
+2		- /a/config dir entry
+4		- /a/super dir entry
+6		- /a/ dir entry
+8		- /a/users/ dir entry
+10		- /a/bkp/ dir entry
+12		- /a/users/inuse dir entry
+14	- /a/frees dir entry
+16	- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
 20		- / direntry
 
 22	- /file1 blank dentry as /file1 was deleted but / still holds onto the dentry
 
-34	- /adm/bkp/root.0 dir entry
-36	- /adm/bkp/super.0 dir entry
-38	- /adm/bkp/config.0 dir entry
+34	- /a/bkp/root.0 dir entry
+36	- /a/bkp/super.0 dir entry
+38	- /a/bkp/config.0 dir entry
--- a/tests/test.3/action.rc
+++ b/tests/test.3/action.rc
@@ -28,4 +28,4 @@
 dofile 16384_blocks.file
 # dofile big.file # cannot do this, not enough space for this.
 
-cat /n/^$service^/adm/ctl
+cat /n/^$service^/a/ctl
--- a/tests/test.3/notes
+++ b/tests/test.3/notes
@@ -6,19 +6,19 @@
 
 block	- description
 0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
+2		- /a/config dir entry
+4		- /a/super dir entry
+6		- /a/ dir entry
+8		- /a/users/ dir entry
+10		- /a/bkp/ dir entry
+12		- /a/users/inuse dir entry
+14	- /a/frees dir entry
+16	- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
 20		- / direntry
 
 20	- big file blank dentry as the big file was deleted but / still holds onto the dentry
 
-40954	- /adm/bkp/root.0 dir entry
-40956	- /adm/bkp/super.0 dir entry
-40958	- /adm/bkp/config.0 dir entry
+40954	- /a/bkp/root.0 dir entry
+40956	- /a/bkp/super.0 dir entry
+40958	- /a/bkp/config.0 dir entry
--- a/tests/test.4/notes
+++ b/tests/test.4/notes
@@ -3,19 +3,19 @@
 
 block	- description
 0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
+2		- /a/config dir entry
+4		- /a/super dir entry
+6		- /a/ dir entry
+8		- /a/users/ dir entry
+10		- /a/bkp/ dir entry
+12		- /a/users/inuse dir entry
+14	- /a/frees dir entry
+16	- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
 20		- / direntry
 
 20	- big file blank dentry as the big file was deleted but / still holds onto the dentry
 
-58	- /adm/bkp/root.0 dir entry
-60	- /adm/bkp/super.0 dir entry
-62	- /adm/bkp/config.0 dir entry
+58	- /a/bkp/root.0 dir entry
+60	- /a/bkp/super.0 dir entry
+62	- /a/bkp/config.0 dir entry
--- a/tests/test.6/action.rc
+++ b/tests/test.6/action.rc
@@ -7,5 +7,5 @@
 touch /n/^$service^/dir1/file1
 echo test >> /n/^$service^/dir1/file1
 
-cat /n/^$service^/adm/ctl
+cat /n/^$service^/a/ctl
 sleep 1
--- a/tests/test.7/action.rc
+++ b/tests/test.7/action.rc
@@ -12,4 +12,4 @@
 sleep 1
 ls -ltr test.7/16384_blocks.file /n/^$service^/16384_blocks.file.*
 
-cat /n/^$service^/adm/ctl
+cat /n/^$service^/a/ctl
--- a/tests/test.8/notes
+++ b/tests/test.8/notes
@@ -3,18 +3,18 @@
 
 block	- description
 0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
+2		- /a/config dir entry
+4		- /a/super dir entry
+6		- /a/ dir entry
+8		- /a/users/ dir entry
+10		- /a/bkp/ dir entry
+12		- /a/users/inuse dir entry
+14	- /a/frees dir entry
+16	- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
 20		- / direntry
 
 
-22	- /adm/bkp/root.0 dir entry
-24	- /adm/bkp/super.0 dir entry
-26	- /adm/bkp/config.0 dir entry
+22	- /a/bkp/root.0 dir entry
+24	- /a/bkp/super.0 dir entry
+26	- /a/bkp/config.0 dir entry
--- a/tests/test.9/action.rc
+++ b/tests/test.9/action.rc
@@ -23,4 +23,4 @@
 time fcp -x test.9/25MB.file /n/^$service^/dir4/
 
 du -a /n/^$service^/
-cat /n/^$service^/adm/ctl
+cat /n/^$service^/a/ctl
--- a/tests/test.9/notes
+++ b/tests/test.9/notes
@@ -2,15 +2,15 @@
 
 block	- description
 0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
+2		- /a/config dir entry
+4		- /a/super dir entry
+6		- /a/ dir entry
+8		- /a/users/ dir entry
+10		- /a/bkp/ dir entry
+12		- /a/users/inuse dir entry
+14	- /a/frees dir entry
+16	- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
 20		- / direntry
 
 22	- /dir1 directory entry
@@ -23,6 +23,6 @@
 34	- /dir2/file2 directory entry
 36	- /dir1/file2 contents
 
-131066	- /adm/bkp/root.0 dir entry
-131068	- /adm/bkp/super.0 dir entry
-131070	- /adm/bkp/config.0 dir entry
+131066	- /a/bkp/root.0 dir entry
+131068	- /a/bkp/super.0 dir entry
+131070	- /a/bkp/config.0 dir entry
--- a/tests/test.a/action.rc
+++ b/tests/test.a/action.rc
@@ -8,4 +8,4 @@
 # tail /mnt/term/tmp/test.out
 # rm /n/^$service^/big.file
 
-cat /n/^$service^/adm/ctl
+cat /n/^$service^/a/ctl
--- a/tests/test.b/action.rc
+++ b/tests/test.b/action.rc
@@ -7,4 +7,4 @@
 touch /n/^$service^/dir1/file1
 echo test >> /n/^$service^/dir1/file1
 
-cat /n/^$service^/adm/ctl
+cat /n/^$service^/a/ctl
--- a/tests/test.b/notes
+++ b/tests/test.b/notes
@@ -7,15 +7,15 @@
 
 block	- description
 0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
+2		- /a/config dir entry
+4		- /a/super dir entry
+6		- /a/ dir entry
+8		- /a/users/ dir entry
+10		- /a/bkp/ dir entry
+12		- /a/users/inuse dir entry
+14	- /a/frees dir entry
+16	- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
 20		- / direntry
 
 22	- /dir1 directory entry
@@ -22,6 +22,6 @@
 24	- /dir1/file1 directory entry
 26	- /dir1/file1 contents
 
-26	- /adm/bkp/root.0 dir entry
-28	- /adm/bkp/super.0 dir entry
-30	- /adm/bkp/config.0 dir entry
+26	- /a/bkp/root.0 dir entry
+28	- /a/bkp/super.0 dir entry
+30	- /a/bkp/config.0 dir entry
--- a/tests/test.d/action.rc
+++ b/tests/test.d/action.rc
@@ -4,4 +4,4 @@
 
 mkdir -p /n/^$service^/a/b/c/d/e
 du -a /n/^$service^/
-cat /n/^$service^/adm/ctl
+cat /n/^$service^/a/ctl
--- a/tests/test.e/action.rc
+++ b/tests/test.e/action.rc
@@ -8,4 +8,4 @@
 rm -rf /n/^$service^/a/b
 rm /n/^$service^/a/1.txt
 du -a /n/^$service^/
-cat /n/^$service^/adm/ctl
+cat /n/^$service^/a/ctl
--- a/tests/test.e/notes
+++ b/tests/test.e/notes
@@ -4,21 +4,21 @@
 
 block	- description
 0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
+2		- /a/config dir entry
+4		- /a/super dir entry
+6		- /a/ dir entry
+8		- /a/users/ dir entry
+10		- /a/bkp/ dir entry
+12		- /a/users/inuse dir entry
+14	- /a/frees dir entry
+16	- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
 20		- / direntry
 
 22	- zeroed out directory entry of /a/1.txt
 24	- /a/2.txt directory entry
-26	- contents of /adm/frees
+26	- contents of /a/frees
 
-32	- /adm/bkp/root.0 dir entry
-34	- /adm/bkp/super.0 dir entry
-36	- /adm/bkp/config.0 dir entry
+32	- /a/bkp/root.0 dir entry
+34	- /a/bkp/super.0 dir entry
+36	- /a/bkp/config.0 dir entry
--- a/tests/test.f/action.rc
+++ b/tests/test.f/action.rc
@@ -6,7 +6,7 @@
 # du -a /n/^$service^/
 echo test > /n/^$service^/file1
 
-echo halt >> /n/^$service^/adm/ctl
+echo halt >> /n/^$service^/a/ctl
 
 disk/mafs $disk
 sleep 1
--- a/tests/test.f/notes
+++ b/tests/test.f/notes
@@ -6,20 +6,20 @@
 
 block	- description
 0		- magic dir entry and data
-2		- /adm/config dir entry
-4		- /adm/super dir entry
-6		- /adm/ dir entry
-8		- /adm/users/ dir entry
-10		- /adm/bkp/ dir entry
-12		- /adm/users/inuse dir entry
-14	- /adm/frees dir entry
-16	- /adm/ctl dir entry -- virtual file, empty contents
-18		- /adm/users/staging dir entry
+2		- /a/config dir entry
+4		- /a/super dir entry
+6		- /a/ dir entry
+8		- /a/users/ dir entry
+10		- /a/bkp/ dir entry
+12		- /a/users/inuse dir entry
+14	- /a/frees dir entry
+16	- /a/ctl dir entry -- virtual file, empty contents
+18		- /a/users/staging dir entry
 20		- / direntry
 
 22	- /dir1 directory entry
 24	- /dir1/file1 directory entry and contents
 
-26	- /adm/bkp/root.0 dir entry
-28	- /adm/bkp/super.0 dir entry
-30	- /adm/bkp/config.0 dir entry
+26	- /a/bkp/root.0 dir entry
+28	- /a/bkp/super.0 dir entry
+30	- /a/bkp/config.0 dir entry
--- a/tests/testconfig.c
+++ b/tests/testconfig.c
@@ -10,7 +10,7 @@
 
 int chatty9p = 0;
 Config config = {0};
-char	service[Namelen] = "testcofig";
+char	service[Servicelen] = "testcofig";
 
 static void
 usage(void)
@@ -23,6 +23,19 @@
 main(int argc, char *argv[])
 {
 	s8 buf[Ddatasize];
+	Errenv env;
+
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		print(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+		exits("error");
+	}
 
 	ARGBEGIN{
 	default:	usage();
--- a/unused.c
+++ b/unused.c
@@ -48,10 +48,10 @@
 };
 typedef struct Stream Stream;
 
-static void init(Stream *s);
 void collect(Stream * s);
 void *emalloc(u32);
-s8 *estrdup(s8 *);
+static void init(Stream *s);
+void panic(char *fmt, ...);
 void show(Stream * s);
 
 static void
@@ -62,6 +62,16 @@
 }
 
 void
+printfd(int fd, char *fmt, ...)
+{
+	va_list va;
+
+	va_start(va, fmt);
+	vfprint(fd, fmt, va);
+	va_end(va);
+}
+
+void
 main(int argc, char *argv[])
 {
 	Stream u;	/* u = used */
@@ -68,7 +78,19 @@
 	struct Extents unused;
 	u64 nblocks;
 	int listblocks = 0;
+	Errenv env;
 
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+	}
+
 	ARGBEGIN{
 	default:	usage();
 	case 'D':	chatty9p = ++debug;	break;
@@ -79,7 +101,7 @@
 		usage();
 
 	nblocks = atoll(argv[0]);
-	u.file = estrdup(argv[1]);
+	u.file = strdup(argv[1]);
 
 	if(u.file == nil)
 		sysfatal("no used file");
@@ -94,7 +116,7 @@
 		show(&u);
 
 	/* identify unused blocks */
-	initextents(&unused, "unused", nil);
+	initextents(&unused, "unused", 0, 0, 2, nil, fprint, panic, emalloc);
 	holes(u.es, &unused);
 
 	if(listblocks)
@@ -130,7 +152,7 @@
 	if(debug)
 		print("collect %s ", s->name);
 	s->es = emalloc(sizeof(Extents));
-	initextents(s->es, s->name, nil);
+	initextents(s->es, s->name, 0, 0, 2, nil, fprint, panic, emalloc);
 	while((s->buf = Brdstr(&s->bp, '\n', 1)) != nil) {
 		p = s->buf;
 		start = strtoull(p, &ep, 10);
@@ -152,7 +174,7 @@
 			panic("loadextents does not match up: start %llud end %llud nblocks %llud",
 					start, end, nblocks);
 
-		add(s->es, start, nblocks);
+		ufree(s->es, start, nblocks);
 // show(s);
 	}
 	if(debug)
--- a/updatefrees.c
+++ b/updatefrees.c
@@ -18,7 +18,10 @@
 int chatty9p;
 int debug = 0;
 char *devfile = nil, *freesfile = nil;
+
 Extents frees = {0};
+void *emalloc(u32);
+void panic(char *fmt, ...);
 
 static void
 usage(void)
@@ -37,7 +40,19 @@
 	Data *f;
 	Dentry *d;
 	u64 *f64;
+	Errenv env;
 
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+	}
+
 	ARGBEGIN{
 	default:	usage();
 	case 'D':	debug++; break;
@@ -82,7 +97,7 @@
 		panic("null size %s", devfile);
 
 	if(debug){
-		print("Namelen %d\n", Namelen);
+		print("Servicelen %d\n", Servicelen);
 		print("Dentry size %d\n", sizeof(Dentry));
 	}
 
@@ -98,10 +113,10 @@
 		strncpy(d->buf, (s8*)f->buf, nfreesize);
 		devwrite(Bdfrees, buf, 1);
 	}else{
-		initextents(&frees, "frees", nil);
+		initextents(&frees, "frees", 0, 0, 2, nil, fprint, panic, emalloc);
 		loadextents(&frees, (s8*)f->buf, nfreesize);
 		nblocks = nlastdatablocks(nfreesize);
-		freeblkno = balloc(&frees, nblocks);
+		 ualloc(&frees, nblocks, &freeblkno);
 
 		d->dblocks[0] = freeblkno;
 		f->tag = Tdata;
--- a/used.c
+++ b/used.c
@@ -16,6 +16,9 @@
 Extents useds = {0};
 int chatty9p = 0;
 char *devfile = nil;
+
+void *emalloc(u32);
+void panic(char *fmt, ...);
 void walkdirectory(u64 blkno);
 void walkfile(u64 blkno);
 int checkdentry(u64 blkno, u8 tag, u64 qpath);
@@ -27,11 +30,29 @@
 	exits("usage");
 }
 
+void *
+mallocu32(u32 sz)
+{
+	return mallocz((ulong)sz, 1);
+}
+
 void
 main(int argc, char *argv[])
 {
 	u64 size;
+	Errenv env;
 
+	envpp = privalloc();
+	envpidx = _nprivates;	/* the index in _privates holding the Errenv location */
+	*envpp = &env;
+	if(waserror()){
+		panic(0, "err stack %d: %lux %lux %lux %lux %lux %lux",
+			env.nlabel,
+			env.label[0][JMPBUFPC], env.label[1][JMPBUFPC],
+			env.label[2][JMPBUFPC], env.label[3][JMPBUFPC],
+			env.label[4][JMPBUFPC], env.label[5][JMPBUFPC]);
+	}
+
 	ARGBEGIN{
 	default:	usage();
 	case 'D':	chatty9p= 8; break;
@@ -53,7 +74,7 @@
 	if(chatty9p)
 		print("%s %llud bytes %llud blocks\n", devfile, size, size/Blocksize);
 
-	initextents(&useds, "useds", nil);
+	initextents(&useds, "useds", 0, 0, 2, nil, fprint, panic, emalloc);
 	checkdentry(Bdmagic, Tdentry, Qpmagic);
 	walkdirectory(Bdroot);
 	close(devfd);
@@ -74,7 +95,7 @@
 	if(chatty9p)
 		print("blkno %llud tag %s\n", blkno, tagnames[dtag]);
 	// print("%llud\n", blkno);
-	add(&useds, blkno, len);
+	ufree(&useds, blkno, len);
 	return 1;
 }
 
@@ -149,8 +170,8 @@
 	devread(blkno, buf, 1);
 	d = (Dentry*)buf;
 	if(chatty9p)
-		print("walkdirectory %llud tag %s name %s d->path %llud\n",
-				blkno, tagnames[d->tag], d->name, d->path);
+		print("walkdirectory %llud tag %s d->path %llud\n",
+				blkno, tagnames[d->tag], d->path);
 	if(d->tag != Tdentry || d->path != d->qpath){
 		if(chatty9p)
 			print("walkdirectory invalid %llud tag/path expected %s/%llud actual %s/%llud\n",
@@ -158,7 +179,7 @@
 		fprint(2, "%llud\n", blkno);
 	}else{
 		// print("%llud\n", blkno);
-		add(&useds, blkno, 1);
+		ufree(&useds, blkno, 1);
 	}
 	/* do not list the data blocks used by /a/frees
 		as they are considered to be free blocks */
@@ -205,8 +226,8 @@
 	devread(blkno, buf, 1);
 	d = (Dentry*)buf;
 	if(chatty9p)
-		print("walkfile %llud tag %s name %s d->qid.path %llud\n",
-				blkno, tagnames[d->tag], d->name, d->qpath);
+		print("walkfile %llud tag %s d->qid.path %llud\n",
+				blkno, tagnames[d->tag], d->qpath);
 	if(d->tag != Tdentry || d->path != d->qpath){
 		if(chatty9p)
 			print("walkfile invalid %llud tag/path expected %s/%llud actual %s/%llud\n",
@@ -214,7 +235,7 @@
 		fprint(2, "%llud\n", blkno);
 	}else{
 		// print("%llud\n", blkno);
-		add(&useds, blkno, 1);
+		ufree(&useds, blkno, 1);
 	}
 	/* do not list the data blocks used by /a/frees
 		as they are considered to be free blocks */
@@ -251,7 +272,7 @@
 	}
 	if(d->size/Maxdatablocksize != nblocks/Maxdatablockunits ||
 		nlastdatablocks(d->size%Maxdatablocksize) != nblocks%Maxdatablockunits)
-		fprint(2, "file name %s size %llud is using %llud blocks\n",
-				d->name, d->size, nblocks);
+		fprint(2, "file size %llud is using %llud blocks\n",
+				d->size, nblocks);
 	return;
 }
--- a/user.c
+++ b/user.c
@@ -423,14 +423,19 @@
 	User *us;
 	int rv;
 
-	ubuf = getmetachk(Bdusersinuse, Breadonly, Tdentry, Qpusersinuse);
+	ubuf = egetmetachk(Bdusersinuse, Breadonly, Tdentry, Qpusersinuse);
 	usize = ubuf->d->size+1;
 	buf = emalloc9p(usize);
 	putbuf(ubuf, 0);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
 
 	readfile(Bdusersinuse, Qpusersinuse, buf, usize, 0);
 	if((rv=parseusers(&us, buf, usize)) != 0)
-		panic("could not parse /a/users/inuse rv %d\n", rv);
+		error("could not parse /a/users/inuse rv %d\n", rv);
+	poperror();
 	free(buf);
 	lruuser = us;
 }
@@ -451,22 +456,32 @@
 	u64 usize;
 	User *us, *old;
 
-	ubuf = getmetachk(Bdusersstaging, Breadonly, Tdentry, Qpusersstaging);
+	ubuf = egetmetachk(Bdusersstaging, Breadonly, Tdentry, Qpusersstaging);
 	usize = ubuf->d->size+1;
 	buf = emalloc9p(usize);
 	putbuf(ubuf, 0);
+	if(waserror()){
+		free(buf);
+		nexterror();
+	}
 
 	readfile(Bdusersstaging, Qpusersstaging, buf, usize, 0);
 	if(parseusers(&us, buf, usize) != 0)
-		panic("could not parse /a/users/staging\n");
+		error("could not parse /a/users/staging\n");
 
 	wlock(&userslock);
 	old = lruuser;
+	if(waserror()){
+		wunlock(&userslock);
+		nexterror();
+	}
 	writefile(Bdusersinuse, Qpusersinuse, -1, buf, usize, 0);
 	lruuser = us;
+	poperror();
 	wunlock(&userslock);
 
 	freeusers(old);
+	poperror();
 	free(buf);
 }
 
--- a/writer.c
+++ b/writer.c
@@ -179,7 +179,7 @@
 void
 initwriter(void)
 {
-	char name[Namelen];
+	char name[Servicelen];
 
 	// set the locks used by the Rendezes
 	drts.isempty.l = &drts.lck;
@@ -195,7 +195,7 @@
 	default:
 		return;
 	}
-	snprint(name, Namelen, "%s writer", service);
+	snprint(name, Servicelen, "%s writer", service);
 	procsetname(name);
 	while(stopwrites == 0 || drts.n > 0){
 		dowrite();