code: mafs

Download patch

ref: 88a739c07b9bbf452a5cbfa1a33cbe7730aaae37
parent: 334cd4d87fc9fd8e48183128e9d60de669ab29fe
author: 9ferno <gophone2015@gmail.com>
date: Tue Nov 22 09:45:40 EST 2022

copy on write for all blocks

--- a/9p.c
+++ b/9p.c
@@ -7,6 +7,7 @@
 
 s32 readfile(u64 dblkno, u64 qpath, char *rbuf, s32 rbufsize, u64 offset, Req *req);
 s32 writefile(u64 dblkno, u64 qpath, s16 uid, char *wbuf, s32 wbufsize, u64 offset, Req *req);
+s32 writeallappend(Iobuf *dbuf, Dentry *d, u64 dblkno);
 
 Aux*
 newaux(u64 addr, u16 uid)
@@ -32,7 +33,6 @@
 {
 	short uid;
 	Iobuf *dbuf;
-	Dentry *d;
 
 	if(authattach(req) < 0){
 		return;
@@ -43,15 +43,14 @@
 		return;
 	}
 
-	dbuf = getbufchk(Bdroot, Breadonly, Tdentry, Qproot);
+	dbuf = getmetachk(Bdroot, Breadonly, Tdentry, Qproot);
 	if(dbuf == nil){
 		respond(req, "phase error");
 		return;
 	}
-	d = dbuf->d;
 	req->fid->aux = newaux(Bdroot, uid);
-	req->fid->qid = (Qid){Qproot, d->qid.version, QTDIR};
-	putbuf(dbuf);
+	req->fid->qid = (Qid){Qproot, dbuf->cur->version, QTDIR};
+	putbuf(dbuf, 0);
 	req->ofcall.qid = req->fid->qid;
 	respond(req, nil);
 }
@@ -60,6 +59,7 @@
 fsdestroyfid(Fid *fid)
 {
 	Tlock *t;
+	Iobuf *dbuf;
 
 	if((fid->qid.type & QTAUTH) != 0){
 		authdestroy(fid);
@@ -91,6 +91,12 @@
 		}
 		qunlock(&tlock);
 	}
+	dbuf = getmetachk(((Aux*)fid->aux)->dblkno,
+						Bwritable, Tdentry, fid->qid.path);
+	if(dbuf != nil){
+		writeallappend(dbuf, (Dentry*)dbuf->new, ((Aux*)fid->aux)->dblkno);
+		putbuf(dbuf, 1);
+	}
 	freeaux(fid->aux);
 }
 
@@ -98,7 +104,7 @@
 fsmkdir(Dentry *d, Dir *dir, char *buf)
 {
 	memset(dir, 0, sizeof(*dir));
-	dir->qid = (Qid){d->qid.path, d->qid.version, (d->mode&DMDIR)? QTDIR : QTFILE};
+	dir->qid = (Qid){d->path, d->version, (d->mode&DMDIR)? QTDIR : QTFILE};
 	dir->mode = (d->mode & 0777) | (dir->qid.type << 24);
 	dir->atime = time(nil);
 	dir->mtime = d->mtime/Nsec; /* ns to seconds */
@@ -126,7 +132,7 @@
 	Dentry *d;
 	Iobuf *dbuf;
 
-	dbuf = getbufchk(((Aux*)req->fid->aux)->dblkno, Breadonly,
+	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",
@@ -135,12 +141,12 @@
 		return;
 	}
 
-	d = dbuf->d;
+	d = (Dentry*)dbuf->cur;
 	/* nothing to do for already zero'ed out slots */
 	if(d->path != Qpnone)
 		fsmkdir(d, &req->d, nil);
 
-	putbuf(dbuf);
+	putbuf(dbuf, 0);
 	respond(req, nil);
 }
 
@@ -192,15 +198,15 @@
 
 	/* TODO cwfs/9p2.c, hjfs/fs2.c and kfs64.b do more here. Get that stuff in. */
 
-	dbuf = getbufchk(((Aux*)req->fid->aux)->dblkno, Bwritable,
+	dbuf = getmetachk(((Aux*)req->fid->aux)->dblkno, Bwritable,
 						Tdentry, req->fid->qid.path);
 	if(dbuf == nil){
 		respond(req, errstring[Eperm]);
 		return;
 	}
-	d = dbuf->d;
+	d = (Dentry*)dbuf->new;
 	if(canaccess(((Aux*)req->fid->aux)->uid, d, DMWRITE) == 0){
-		putbuf(dbuf);
+		putbuf(dbuf, 0);
 		respond(req, errstring[Eaccess]);
 		return;
 	}
@@ -207,7 +213,7 @@
 	/* invalid to change the directory bit stat(5) */
 	if(req->d.mode != ~0 &&
 			(req->d.mode&DMDIR) != (d->mode&DMDIR)){
-		putbuf(dbuf);
+		putbuf(dbuf, 0);
 		respond(req, errstring[Einval]);
 		return;
 	}
@@ -234,8 +240,8 @@
 		}
 	}
 
-	putbuf(dbuf);
 	respond(req, nil);
+	putbuf(dbuf, 1);
 	return;
 noperm:
 	respond(req, errstring[Eperm]);
@@ -280,7 +286,7 @@
 				return;
 			}
 			((Aux*)req->fid->aux)->dri++;
-		}while(d->qid.path == 0);
+		}while(d->path == 0);
 
 		fsmkdir(d, &dir, nbuf);
 		req->ofcall.count = n = convD2M(&dir, (u8*)req->ofcall.data, req->ifcall.count);
@@ -293,8 +299,8 @@
 	}
 
 	readfile(((Aux*)req->fid->aux)->dblkno, req->fid->qid.path,
-				 req->ofcall.data, req->ifcall.count, req->ifcall.offset,
-				 req);
+				 req->ofcall.data, req->ifcall.count,
+				 req->ifcall.offset, req);
 }
 
 
@@ -323,7 +329,8 @@
 
 	writefile(((Aux*)req->fid->aux)->dblkno, req->fid->qid.path,
 					((Aux*)req->fid->aux)->uid,
-					req->ifcall.data, req->ifcall.count, req->ifcall.offset, req);
+					req->ifcall.data, req->ifcall.count,
+					req->ifcall.offset, req);
 }
 
 static void
@@ -380,22 +387,22 @@
 			if(chatty9p > 1)
 				dprint("fswalk1 .. fid->qid.path %llud aux->dblkno %llud\n",
 						fid->qid.path, aux->dblkno);
-			dbuf = getbufchk(aux->dblkno, Breadonly, Tdentry, fid->qid.path);
+			dbuf = getmetachk(aux->dblkno, Breadonly, Tdentry, fid->qid.path);
 			if(dbuf == nil)
 				return errstring[Ephase];
-			d = dbuf->d;
+			d = (Dentry*)dbuf->cur;
 			if(chatty9p > 1)
 				dprint("fswalk1 .. d->name %s d->qid.path %llud d->pdblkno %llud\n",
-						d->name, d->qid.path, d->pdblkno);
-			pbuf = getbufchk(d->pdblkno, Breadonly, Tdentry, d->pqpath);
+						d->name, d->path, d->pdblkno);
+			pbuf = getmetachk(d->pdblkno, Breadonly, Tdentry, d->pqpath);
 			if(pbuf == nil)
 				return errstring[Ephase];
-			putbuf(dbuf);
-			p = pbuf->d;
-			*qid = (Qid){p->qid.path, p->qid.version, (p->mode&DMDIR) ? QTDIR : QTFILE};
+			putbuf(dbuf, 0);
+			p = (Dentry*)pbuf->cur;
+			*qid = (Qid){p->path, p->version, (p->mode&DMDIR) ? QTDIR : QTFILE};
 			if(aux != nil)
 				aux->dblkno = pbuf->blkno;
-			putbuf(pbuf);
+			putbuf(pbuf, 0);
 			fid->qid = *qid;
 			return nil;
 		}
@@ -411,7 +418,7 @@
 			freesearchstate(&dbuf, &cbuf);
 			return errstring[Eperm];
 		}
-		*qid = (Qid){d->qid.path, d->qid.version, (d->mode&DMDIR) ? QTDIR : QTFILE};
+		*qid = (Qid){d->path, d->version, (d->mode&DMDIR) ? QTDIR : QTFILE};
 		if(aux != nil)
 			aux->dblkno = cbuf->blkno;
 		freesearchstate(&dbuf, &cbuf);
@@ -435,7 +442,7 @@
 }
 
 /*
- * error(Eperm) if open permission not granted for up->user.
+ * error(Eperm) if open permission not granted for up->newr.
  */
 int
 permcheck(u16 fileuid, u16 filegid, u16 uid, ulong perm, int omode)
@@ -512,12 +519,12 @@
 	cbuf = nil;
 	USED(cbuf);
 	zblkno = 0;
-	dbuf = getbufchk(aux->dblkno, Bwritable, Tdentry, fid->qid.path);
+	dbuf = getmetachk(aux->dblkno, Bwritable, Tdentry, fid->qid.path);
 	if(dbuf == nil){
 		respond(req, errstring[Ephase]);
 		return;
 	}
-	dparent = dbuf->d;
+	dparent = (Dentry*)dbuf->cur;
 	if(canaccess(((Aux*)req->fid->aux)->uid, dparent, DMWRITE) == 0){
 		respond(req, errstring[Eperm]);
 		return;
@@ -524,7 +531,7 @@
 	}
 
 	if(canaccess(aux->uid, dparent, DMWRITE) == 0){
-		putbuf(dbuf);
+		putbuf(dbuf, 0);
 		respond(req, errstring[Eaccess]);
 		return;
 	}
@@ -542,7 +549,7 @@
 			fid->omode = OREAD+OWRITE;
 			break;
 		default:
-			putbuf(dbuf);
+			putbuf(dbuf, 0);
 			respond(req, errstring[Emode]);
 			return;
 	}
@@ -554,24 +561,24 @@
 		if(blkno == 0){
 			/* end reached, nothing found, create */
 			if(zblkno != 0){
-				cbuf = getbufchk(zblkno, Bwritable, Tdentry, Qpnone);
+				cbuf = getmetachk(zblkno, Bwritable, Tdentry, Qpnone);
 				if(cbuf == nil){
-					putbuf(dbuf);
+					putbuf(dbuf, 0);
 					respond(req, errstring[Ephase]);
 					return;
 				}
 			}else{
-				cbuf = allocblock(Tdentry, fid->qid.path);
+				cbuf = allocmeta(Tdentry, fid->qid.path);
 				if(cbuf == nil){
-					putbuf(dbuf);
+					putbuf(dbuf, 0);
 					respond(req, errstring[Efull]);
 					return;
 				}
 			}
-			dchild = cbuf->d;
+			dchild = (Dentry*)cbuf->new;
 			dchild->size = 0;
 			dchild->pdblkno = dbuf->blkno;
-			dchild->pqpath = dparent->qid.path;
+			dchild->pqpath = dparent->path;
 			dchild->mtime = nsec();
 			dchild->uid = dchild->muid = aux->uid;
 			dchild->gid = dparent->gid;
@@ -579,8 +586,8 @@
 			if(perm&DMDIR){
 				if((req->ifcall.mode & OTRUNC) || (perm & DMAPPEND) ||
 					(fid->omode & OWRITE)){
-					putbuf(cbuf);
-					putbuf(dbuf);
+					putbuf(cbuf, 1);
+					putbuf(dbuf, 0);
 					respond(req, errstring[Eaccess]);
 					return;
 				}
@@ -594,21 +601,21 @@
 			if(perm&DMEXCL)
 				dchild->mode |= DMEXCL;
 			if(canaccess(aux->uid, dchild, DMWRITE) == 0){
-				putbuf(cbuf);
-				putbuf(dbuf);
+				putbuf(cbuf, 0);
+				putbuf(dbuf, 0);
 				respond(req, errstring[Eaccess]);
 				return;
 			}
-			dchild->qid.path = newqpath();
-			dchild->qid.version = 0;
+			dchild->path = newqpath();
+			dchild->version = 0;
 			strncpy(dchild->name, req->ifcall.name, Namelen);
-			fid->qid = (Qid){dchild->qid.path, 0, (perm&DMDIR) ? QTDIR : QTFILE};
+			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->qid.path;
+				t->qpath = dchild->path;
 				t->dblkno = aux->dblkno;
 				qlock(&tlock);
 				if(tlocktail == nil){
@@ -624,8 +631,8 @@
 				aux->tlocked = 0;
 			req->ofcall.qid = fid->qid;
 			req->ofcall.iounit = Iounit;
-			settag(cbuf, Tdentry, dchild->qid.path);
-			putbuf(cbuf);	/* save Iobuf of the content */
+			settag(cbuf, Tdentry, dchild->path);
+			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
@@ -633,24 +640,24 @@
 			 */
 			if(zblkno == 0 &&
 				addrelative(dparent, dbuf->blkno, reli, aux->dblkno) == 0){
-				putbuf(dbuf);
+				putbuf(dbuf, 1);
 				respond(req, errstring[Ephase]);
 				return;
 			}
-			putbuf(dbuf);
+			putbuf(dbuf, 1);
 			respond(req, nil);
 			return;
 		}else{
-			cbuf = getbuf(blkno, Dentryunits, Breadonly, Bused);
+			cbuf = getmeta(blkno, Breadonly, Bused);
 			if(cbuf == nil){
-				putbuf(dbuf);
+				putbuf(dbuf, 0);
 				respond(req, errstring[Ephase]);
 				return;
 			}
-			dchild = cbuf->d;
+			dchild = (Dentry*)cbuf->cur;
 
 			/* nothing to do for already zero'ed out slots */
-			if(dchild->path == Qpnone){
+			if(dchild->qpath == Qpnone){
 				if(zblkno == 0)
 					zblkno = cbuf->blkno;
 				goto Nextdentry;
@@ -657,8 +664,8 @@
 			}
 
 			if(dchild->tag != Tdentry){
-				putbuf(cbuf);
-				putbuf(dbuf);
+				putbuf(cbuf, 0);
+				putbuf(dbuf, 0);
 				respond(req, errstring[Ephase]);
 				return;
 			}
@@ -670,7 +677,7 @@
 					return;
 				}
 				dchild->muid = aux->uid;
-				fid->qid = (Qid){dchild->qid.path, dchild->qid.version,
+				fid->qid = (Qid){dchild->qpath, dchild->version,
 									(req->ifcall.mode&DMDIR) ? QTDIR : QTFILE};
 				aux->dblkno = blkno;
 				aux->dri = 0;
@@ -680,22 +687,16 @@
 					save Iobuf of the child so truncate can open
 					it with a wlock(), if needed
 				 */
-				putbuf(cbuf);
+				putbuf(cbuf, 0);
 				if(req->ofcall.qid.path >= Qpusers &&
 					req->ofcall.qid.type == QTFILE)
 					truncatefile(req->ofcall.qid.path, blkno, aux->uid);
-				putbuf(dbuf);
-		/*		DBG("> mafscreate c->path %s mode 0x%ux omode0 0x%ux\n"
-					"	c->qid.path 0x%zux c->qid.vers %lud c->qid.type %ud 0x%ux\n"
-					"	c->aux 0x%p\n",
-					chanpath(c), c->mode, omode, c->qid.path,
-					c->qid.vers, c->qid.type, c->qid.type,
-					c->aux);*/
+				putbuf(dbuf, 0);
 				respond(req, nil);
 				return;
 			}
 			Nextdentry:
-				putbuf(cbuf);
+				putbuf(cbuf, 0);
 		}
 	}
 }
@@ -722,12 +723,12 @@
 	if(readonly && (mode & (ORCLOSE | OTRUNC | OWRITE | ORDWR)) != 0)
 		goto inval;
 
-	dbuf = getbufchk(aux->dblkno, Breadonly, Tdentry, fid->qid.path);
+	dbuf = getmetachk(aux->dblkno, Breadonly, Tdentry, fid->qid.path);
 	if(dbuf == nil){
 		respond(req, errstring[Ephase]);
 		return;
 	}
-	d = dbuf->d;
+	d = (Dentry*)dbuf->cur;
 
 	if((mode & OTRUNC) != 0 &&
 		canaccess(aux->uid, d, DMWRITE) == 0)
@@ -784,12 +785,12 @@
 	if((mode & ORCLOSE) != 0)
 		omode |= ORCLOSE;
 
-	fid->qid = (Qid){d->qid.path, d->qid.version, (d->mode&DMDIR) ? QTDIR : QTFILE};
+	fid->qid = (Qid){d->path, d->version, (d->mode&DMDIR) ? QTDIR : QTFILE};
 	fid->omode = omode;
 	req->ofcall.iounit = Iounit;
 	req->ofcall.qid = fid->qid;
 
-	putbuf(dbuf);
+	putbuf(dbuf, 0);
 	if((mode & OTRUNC) == 0){
 		respond(req, nil);
 		return;
@@ -800,12 +801,12 @@
 	return;
 inval:
 	if(dbuf != nil)
-		putbuf(dbuf);
+		putbuf(dbuf, 0);
 	respond(req, errstring[Einval]);
 	return;
 perm:
 	if(dbuf != nil)
-		putbuf(dbuf);
+		putbuf(dbuf, 0);
 	respond(req, errstring[Eaccess]);
 	return;
 }
@@ -961,10 +962,11 @@
 		all the clients that mounted it. Just shutdown and let the respective clients
 		deal with their mess.
  */
+/* TODO need to track open fids and close them */
 void
 shutdown(void)
 {
-	u64 n;
+//	u64 n;
 /*	User *u, *v; */
 
 	if(chatty9p > 1)
@@ -978,7 +980,7 @@
 		rwakeupall(&buf.isempty);
 		qunlock(&buf.lck);
 	}
-	if(synchronouswrites == 0){
+/*	if(synchronouswrites == 0){
 		while((n=pendingwrites())>0){
 			if(chatty9p > 1)
 			dprint("shutdown: pendingwrites %llud of write queue\n", n);
@@ -985,8 +987,9 @@
 			sleep(1000);
 		}
 	}
+*/
 	savefrees(Bdfrees);
-	if(synchronouswrites == 0){
+/*	if(synchronouswrites == 0){
 		while((n=pendingwrites())>0){
 			if(chatty9p > 1)
 			dprint("shutdown: pendingwrites %llud of frees\n", n);
@@ -994,15 +997,15 @@
 		}
 		stopwriter();
 	}
-
+*/
 	/* free users, why bother? leave it alone */
-/*	u = t->users;
+/*	u = t->newrs;
 	while(u != nil){
 		v = u->next;
 		free(u);
 		u = v;
 	}
-	t->users = nil;*/
+	t->newrs = nil;*/
 
 	if(chatty9p > 1)
 		dprint("shutdown: exiting\n");
@@ -1230,11 +1233,11 @@
 	u64 filesize;
 	Iobuf *dbuf;
 
-	dbuf = getbufchk(dblkno, Breadonly, Tdentry, qpath);
+	dbuf = getmetachk(dblkno, Breadonly, Tdentry, qpath);
 	if(dbuf == nil)
 		return 0;
-	filesize = dbuf->d->size;
-	putbuf(dbuf);
+	filesize = dbuf->cur->size;
+	putbuf(dbuf, 0);
 	return filesize;
 }
 s32
@@ -1244,21 +1247,22 @@
 	s64 tosend, sent, filesize;
 	s32 n;
 	Iobuf *dbuf, *buf;
+	u64 datablocksize;
 
-	dbuf = getbufchk(dblkno, Breadonly, Tdentry, qpath);
+	dbuf = getmetachk(dblkno, Breadonly, Tdentry, qpath);
 	if(dbuf == nil)
 		return 0;
-	d = dbuf->d;
-	filesize = d->size;
+	d = (Dentry*)dbuf->cur;
+	filesize = d->size+dbuf->appendsize;
 
 	if(offset >= filesize){
-		putbuf(dbuf);
+		putbuf(dbuf, 0);
 		return 0;
 	}
 	if(filesize < Ddatasize){
 		n = min(filesize-offset, rbufsize);
 		memcpy(rbuf, d->buf+offset, n);
-		putbuf(dbuf);
+		putbuf(dbuf, 0);
 		return n;
 	}
 	if(filesize - offset > rbufsize)
@@ -1267,32 +1271,43 @@
 		tosend = filesize - offset;
 
 	for(sent = 0; sent < tosend && offset+sent < d->size; ){
-		buf = getdatablkat(d, (offset+sent)/Datablocksize, Breadonly);
+		buf = getdatablkat(d, (offset+sent)/Maxdatablocksize);
 		if(buf == nil){
-			putbuf(dbuf);
+			putbuf(dbuf, 0);
 			return -1;
 		}
-		n = min(Datablocksize-((offset+sent)%Datablocksize), tosend-sent);
-		memcpy(rbuf+sent, buf->io->buf+((offset+sent)%Datablocksize), n);
+		if(buf->len == Maxdatablockunits)
+			datablocksize = Maxdatablocksize;
+		else
+			datablocksize = buf->len*Blocksize -sizeof(Datahdr) -sizeof(u64 /* trailing path */);
+		n = min(datablocksize-((offset+sent)%Maxdatablocksize), tosend-sent);
+		memcpy(rbuf+sent, buf->io->buf+((offset+sent)%Maxdatablocksize), n);
 		sent += n;
-		putbuf(buf);
+		putbuf(buf, 0);
 	}
+	if(dbuf->append && sent < tosend && offset+sent < d->size+dbuf->appendsize){
+		/* assuming that rbufsize < Maxdatablocksize */
+		n = min(d->size+dbuf->appendsize-(offset+sent), tosend-sent);
+		memcpy(rbuf+sent, dbuf->append+(offset+sent-d->size), n);
+		sent += n;
+	}
 	if(req != nil){
 		req->ofcall.count = sent;
 		req->ofcall.offset = req->ifcall.offset+sent;
 		respond(req, nil);
 	}
-	putbuf(dbuf);
+	putbuf(dbuf, 0);
 	return sent;
 }
 
 /* only for updating existing data */
+/* TODO copy on write */
 s32
 update(Dentry *d, u64 /* dblkno */, char *wbuf, s32 wbufsize, u64 offset)
 {
 	Iobuf *buf;
 	s32 howmuch;
-	u64 blkno, to;
+	u64 blkno, to, nblocks;
 
 	if(d == nil || wbuf == nil || wbufsize == 0)
 		return 0;
@@ -1304,19 +1319,22 @@
 	/*	get the extent
 		overlay data
 	 */
-
-	blkno = rel2abs(d, offset/Datablocksize);
+	blkno = rel2abs(d, offset/Maxdatablocksize);
+	if(offset/Maxdatablocksize < d->size/Maxdatablocksize)
+		nblocks = Maxdatablockunits;
+	else
+		nblocks = nlastdatablocks(d->size);
 	if(chatty9p > 1)
 	dprint("update d->name %s d->size %llud offset %llud"
 				" rel2abs(offset/Datablocksize %llud) = blkno %llud\n",
-				d->name, d->size, offset, offset/Datablocksize, blkno);
-	buf = getbufchk(blkno, Bwritable, Tdata, d->qid.path);
+				d->name, d->size, offset, offset/Maxdatablocksize, blkno);
+	buf = getbufchk(blkno, nblocks, Bwritable, Tdata, d->path);
 	if(buf == nil)
 		return -1;
 
 	/* overlay the new contents */
-	to = offset%Datablocksize;
-	howmuch = min(Datablocksize-to, wbufsize);
+	to = offset%Maxdatablocksize;
+	howmuch = min((nblocks*Blocksize)-to, wbufsize);
 	if(chatty9p > 1){
 		dprint("updating buf->blkno %llud offset %llud size %llud\n",
 				buf->blkno, to, howmuch);
@@ -1327,87 +1345,212 @@
 		dprint("update after\n");
 		showbuf(buf);
 	}
-	putbuf(buf);
+	putbuf(buf, 1);
 	return howmuch;
 }
 
+/*
+	Scenarios:
+	1. last block is full or no last block
+	2. last block is partially full
+ */
 s32
-append(Dentry *d, u64 dblkno, char *wbuf, s32 wbufsize)
+writefullappend(Iobuf *dbuf, Dentry *d, u64 dblkno)
 {
-	Iobuf *buf;
+	Iobuf *oldbuf, *newbuf;
 	s32 howmuch;
-	u64 blkno, lastblksize;
+	u64 newblkno, newbufsize, lastdatablksize, oldblkno;
 
-	if(d == nil || wbuf == nil || wbufsize == 0)
-		return 0;
-	if(chatty9p > 1)
-		dprint("append wbufsize %d\n", wbufsize);
+	/* last data block is full or there is no last block.
+		write out the append
+	 */
+	if(d->size%Maxdatablocksize == 0 &&
+		dbuf->appendsize == Maxdatablocksize){
 
-	if((lastblksize=d->size%Datablocksize) == 0){
-		/* last block is full, use a new block */
-		if(chatty9p > 1)
-		dprint("append new data block rel2abs d->name %s reli d->size %llud"
-				" d->size/Datablocksize %llud\n",
-				d->name, d->size, d->size/Datablocksize);
-
-		/* write single block */
-		howmuch = min(Datablocksize, wbufsize);
-		buf = allocblock(Tdata, d->qid.path);
-		if(buf == nil)
+		/* allocate new blocks */
+		newbuf = allocblocks(Maxdatablockunits,Tdata, d->path);
+		if(newbuf == nil)
 			return -1;
-		blkno = buf->blkno;
-		memcpy(buf->io->buf, wbuf, howmuch);
-		putbuf(buf);
+		newblkno = newbuf->blkno;
 
-		if(addrelative(d, dblkno, d->size/Datablocksize, blkno) == 0){
+		/* add the contents of append to those new blocks */
+		memcpy(newbuf->io->buf, dbuf->append, dbuf->appendsize);
+
+		/* add this newly allocated blocks to the Dentry */
+		if(addrelative(d, dblkno, d->size/Maxdatablocksize, newblkno) == 0){
 			panic("could not write Tdata block\n");
-			freeblock(blkno, Tdata, d->qid.path);
-			return -2;
+			freeblocks(newblkno, Maxdatablockunits, Tdata, d->path);
+			return -1;
 		}
-		return howmuch;
-	}else{
-		/* last block is partially full, fill it up */
+		d->size += dbuf->appendsize;
+		dbuf->appendsize = 0;
+		putbuf(newbuf, 1);
+		return 1;
 
-		blkno = rel2abs(d, d->size/Datablocksize);
-		if(blkno == 0)
-			panic("append update rel2abs blkno == 0"
-					" d->name %s reli d->size %llud"
-					" d->size/Datablocksize %llud s.blkno %llud\n",
-					d->name, d->size, d->size/Datablocksize, blkno);
-		if(chatty9p > 1)
-		dprint("append update rel2abs d->name %s reli d->size %llud"
-				" d->size/Datablocksize %llud blkno %llud\n",
-				d->name, d->size, d->size/Datablocksize, blkno);
-		buf = getbufchk(blkno, Bwritable, Tdata, d->qid.path);
-		if(buf == nil)
+	}else if(d->size%Maxdatablocksize > 0 &&
+			(d->size%Maxdatablocksize)+dbuf->appendsize >= Maxdatablocksize){
+
+		/* last data block is not full.
+			write a full last data block and leave the rest in append
+		 */
+
+		/* allocate new blocks */
+		newbuf = allocblocks(Maxdatablockunits, Tdata, d->path);
+		if(newbuf == nil)
 			return -1;
+		newblkno = newbuf->blkno;
+		newbufsize = 0;
 
-		howmuch = min(Datablocksize-lastblksize, wbufsize);
-		if(chatty9p > 1)
-		dprint("fill lastblksize %llud howmuch %llud\n",
-				lastblksize, howmuch);
-		memcpy(buf->io->buf+lastblksize, wbuf, howmuch);
-		putbuf(buf);
-		return howmuch;
+		/* read from the last block */
+		/* copy that to the new allocated blocks */
+		oldbuf = nil;
+		if(lastdatablksize=d->size%Maxdatablocksize){
+			/* partial block, above is = not == */
+			oldblkno = rel2abs(d, d->size/Maxdatablocksize);
+			oldbuf = getbufchk(oldblkno, nlastdatablocks(lastdatablksize),
+								Bwritable, Tdata, d->path);
+			if(oldbuf == nil){
+				freeblocks(newblkno, Maxdatablockunits, Tdata, d->path);
+				return -1;
+			}
+			memcpy(newbuf->io->buf, oldbuf->io->buf, lastdatablksize);
+			newbufsize = lastdatablksize;
+		}
+
+		/* add the contents of append to those new blocks */
+		howmuch = Maxdatablocksize-newbufsize;
+		memcpy(newbuf->io->buf+newbufsize, dbuf->append, howmuch);
+
+		/* move the left over append stuff to the front */
+		memmove(dbuf->append, dbuf->append+howmuch, dbuf->appendsize-howmuch);
+
+		/* 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;
+		}
+		d->size += howmuch;
+		dbuf->appendsize -= howmuch;
+		putbuf(newbuf, 1);
+
+		/* free the old last blocks */
+		if(oldbuf)
+			putbuffree(oldbuf);
+		return 1;
 	}
+	return 0;
 }
 
-/* pad blanks one span at a time */
-static s32
-padblanks(Dentry *d, u64 dblkno, s32 size)
+/*
+	Scenarios:
+	1. last block is full or no last block
+	2. last block is partially full
+ */
+s32
+writeallappend(Iobuf *dbuf, Dentry *d, u64 dblkno)
 {
-	s8 *buf;
-	int n;
+	Iobuf *oldbuf, *newbuf;
+	u64 newblkno, newbufsize, lastdatablksize, oldblkno, newblocks;
+	s32 rv;
 
-	if(d == nil || size <= 0)
-		return 0;
-	n = min(size, Datablocksize - d->size%Datablocksize);
-	buf = emalloc9p(n);
-	n = append(d, dblkno, buf, n);
-	free(buf);
-	return n;
+	if(dbuf->appendsize == 0)
+		return 1;
+
+	rv = 0;
+	if((d->size%Maxdatablocksize)+dbuf->appendsize >= Maxdatablocksize)
+		rv = writefullappend(dbuf, d, dblkno);
+
+	if(rv < 0)
+		return rv;
+	if(dbuf->appendsize == 0)
+		return 1;
+
+	/* last data block is full or there is no last block. */
+	if(d->size%Maxdatablocksize == 0){
+
+		/* allocate new blocks */
+		newbuf = allocblocks(nlastdatablocks(dbuf->appendsize),Tdata, d->path);
+		if(newbuf == nil)
+			return -1;
+		newblkno = newbuf->blkno;
+
+		/* add the contents of append to those new blocks */
+		memcpy(newbuf->io->buf, dbuf->append, dbuf->appendsize);
+		newbuf->io->len = nlastdatablocks(dbuf->appendsize);
+
+		/* 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, nlastdatablocks(dbuf->appendsize), Tdata, d->path);
+			return -1;
+		}
+		d->size += dbuf->appendsize;
+		dbuf->appendsize = 0;
+		putbuf(newbuf, 1);
+		return 1;
+	}
+
+	/* 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;
+
+	/* read from the last block */
+	/* copy that to the new allocated blocks */
+	oldbuf = nil;
+	if(lastdatablksize=d->size%Maxdatablocksize){
+		/* partial block, above is = not == */
+		oldblkno = rel2abs(d, d->size/Maxdatablocksize);
+		oldbuf = getbufchk(oldblkno, nlastdatablocks(lastdatablksize),
+							Bwritable, Tdata, d->path);
+		if(oldbuf == nil){
+			freeblocks(newblkno, newblocks, Tdata, d->path);
+			return -1;
+		}
+		memcpy(newbuf->io->buf, oldbuf->io->buf, lastdatablksize);
+		newbufsize = lastdatablksize;
+	}
+
+	/* add the contents of append to those new blocks */
+	/* writefullappend() takes care of bigger sizes */
+	memcpy(newbuf->io->buf+newbufsize, dbuf->append, dbuf->appendsize);
+	newbuf->io->len = newblocks;
+
+	/* nothing left in append to move to the front */
+
+	/* 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;
+	}
+
+	/* free the old last blocks */
+	if(oldbuf)
+		putbuffree(oldbuf);
+	d->size += dbuf->appendsize;
+	dbuf->appendsize = 0;
+	putbuf(newbuf, 1);
+	return 1;
 }
 
+s32
+newappend(Iobuf *dbuf, u64 dblkno, u64 path)
+{
+	dbuf->append = (Data*)allocmemunits(Maxdatablockunits);
+	if(dbuf->append == nil)
+		return -1;
+	dbuf->appendsize = 0;
+	dbuf->append->tag = Tdata;
+	dbuf->append->len = Maxdatablockunits;
+	dbuf->append->dblkno = dblkno;
+	dbuf->append->path = path;
+	return 1;
+}
 /*
 	3 scenarios
 		offset < filesize && offset+wbufsize <= filesize
@@ -1424,15 +1567,16 @@
 {
 	Dentry *d;
 	s64 written;
-	s32 n;
-	Iobuf *dbuf, *buf;
-	u64 blkno;
+	s32 n, rv;
+	Iobuf *dbuf;
+	u8 dowrite;
 
-	dbuf = getbufchk(dblkno, Bwritable, Tdentry, qpath);
+	dbuf = getmetachk(dblkno, Bwritable, Tdentry, qpath);
 	if(dbuf == nil)
 		return 0;
-	d = dbuf->d;
+	d = dbuf->new;
 	d->muid = uid;
+	rv = dowrite = 0;
 	// odentry(d);
 
 	// little data, stuff it in the Dentry
@@ -1441,20 +1585,16 @@
 		if(offset+wbufsize > d->size)
 			d->size = offset+wbufsize;
 		written = wbufsize;
+		dowrite = 1;
 		goto writeend;
 	}
 	// more data, relocate it to a proper data block
 	if(d->size > 0 && d->size <= Ddatasize && offset+wbufsize > Ddatasize){
-		buf = allocblock(Tdata, d->qid.path);
-		if(buf == nil){
-			written = 0;
-			goto writeend;
-		}
-		blkno = buf->blkno;
-		memcpy(buf->io->buf, d->buf, d->size);
-		putbuf(buf);
-		memset(d->buf, 0, d->size);
-		d->dblocks[0] = blkno;
+		newappend(dbuf, dblkno, d->path);
+		memcpy(dbuf->append, d->buf, d->size);
+		dbuf->appendsize = d->size;
+		d->size = 0;
+		dowrite = 1;
 	}
 
 	for(written = 0; written < wbufsize; ){
@@ -1466,7 +1606,7 @@
 		/* all the below functions only write upto the end of an extent.
 		   Hence, the need for a loop to keep repeating.
 		 */
-		if(offset > d->size){
+		if(offset > d->size+dbuf->appendsize){
 			/* new blank blocks until offset
 				fill blank data upto offset */
 
@@ -1473,14 +1613,11 @@
 			if(chatty9p > 1)
 				dprint("writefile(): blank blocks until offset\n");
 
-			n = padblanks(d, dblkno, offset+wbufsize-d->size);
-			if(chatty9p > 1)
-				dprint("writefile(): padblanks returned %d\n", n);
-			if(n<0){
-				dprint("padblanks has an issue %d\n", n);
-				goto writeend;
-			}else
-				d->size += n;
+			if(dbuf->append == nil)
+				if((rv = newappend(dbuf, dblkno, d->path)) < 0)
+					goto writeend;
+			n = min(Maxdatablocksize, offset-d->size);
+			dbuf->appendsize = n;
 
 		}else if(offset+written < d->size){
 			/* replacing existing data
@@ -1489,7 +1626,7 @@
 			if(chatty9p > 1)
 				dprint("writefile(): replace existing data\n");
 
-			n = update(d, dblkno,
+			rv = n = update(d, dblkno,
 							wbuf+written, /* from where */
 							/* how much */
 							min(wbufsize-written, d->size-(offset+written)),
@@ -1502,32 +1639,41 @@
 			}else
 				written += n;
 
-		}else if(offset+written >= d->size){
+		}else if(offset+written >= d->size &&
+					offset+written < d->size+dbuf->appendsize){
+			/* changing append contents */
+			n = min(dbuf->appendsize-(offset+written),wbufsize-written);
+			memcpy(dbuf->append+dbuf->appendsize, wbuf+written, n);
+			dbuf->appendsize += n;
+			written += n;
+
+		}else if(offset+written >= d->size+dbuf->appendsize){
+			/* assuming that wbufsize <= append */
 			/* append data, changes file size
 				data blocks from offset until offset+wbufsize */
-			if(chatty9p > 1)
-				dprint("writefile(): append\n");
-			n = append(d, dblkno, wbuf+written, /* from where */
-							wbufsize-written	/* how much */);
-			if(chatty9p > 1)
-				dprint("writefile(): append returned %d\n", n);
-			if(n<0){
-				dprint("append has an issue %d\n", n);
-				goto writeend;
-			}
+			if(dbuf->append == nil)
+				if((rv = newappend(dbuf, dblkno, d->path)) < 0)
+					goto writeend;
+			n = min(Maxdatablocksize-dbuf->appendsize, wbufsize-written);
+			memcpy(dbuf->append+dbuf->appendsize, wbuf+written, n);
+			dbuf->appendsize += n;
 			written += n;
-			d->size += n;
 		}
 		else
 			panic("writefile: should not be here");
+
+		if(dbuf->appendsize == Maxdatablocksize)
+			dowrite = 1;
+			if((rv=writefullappend(dbuf, d, dblkno)) < 0)
+				goto writeend;
 	}
 
 writeend:
 	d->mtime = nsec();
 	if(req != nil){
-		if(written == -3)
+		if(rv == -3)
 			respond(req, errstring[Efull]);
-		else if(written == -1 || written == -2)
+		else if(rv == -1 || rv == -2)
 			respond(req, errstring[Ephase]);
 		else{
 			req->ofcall.count = written;
@@ -1535,6 +1681,6 @@
 			respond(req, nil);
 		}
 	}
-	putbuf(dbuf);
+	putbuf(dbuf, dowrite);
 	return written;
 }
--- a/all.h
+++ b/all.h
@@ -82,31 +82,11 @@
 		Data *io;
 		Metadata *m;
 	};
-	Metadata *use;	/* use this unit for changes */
+	Metadataunit *cur;	/* this has the current Indirect or Dentry values */
+	Metadataunit *new;	/* use this unit for Indirect or Dentry changes */
 
-	/* head of a linked list of children with changes yet to be written */
-	Iobuf *changes;
-
-	/* linked list of sibling changes not yet written */
-	u8 change;	/* Add or Remove. Am I being added or removed? */
-	Iobuf *prev, *next;
-
-	u8 *append;		/* appended data added not yet written to disk */
-
-	/* obsolete
-		This field is used by mafs to ensure that Iobufs are not reused
-		while there are pending writes.
-
-		dowrite() uses a Ref instead of a wlock() to mark Iobuf's
-		with pending writes.
-		Using a wlock() in dowrite() causes a deadlock with putwrite()
-		especially when the writer queue is full.
-		getbuf() guarantees that even a free'ed block cannot be
-		stolen until the dirties == 0. This avoids dirty blocks
-		being stolen for other block numbers.
-		incref(dirties) only happens while holding a wlock() in putwrite().
-	 */
-	Ref	dirties;	/* number of versions of this block yet to be written by the writer */
+	Data *append;		/* appended data added not yet written to disk */
+	u64 appendsize;
 };
 
 extern	u64	nbuckets;		/* n hash buckets for i/o */
@@ -118,11 +98,13 @@
 void	initmemunitpool(u64 nunits);
 u8 *allocmemunits(u16 len);
 void	freememunits(u8 *m, u16 len);
-int		checktag(Iobuf *p, u8 tag, u64 qpath);
-Iobuf*	getbuf(u64 blkno, u64 len, u8 readonly, u8 freshalloc);
-Iobuf*	getbufchk(u64 blkno, u64 len, u8 readonly, int tag, u64 qpath);
+int		checktag(Iobuf *p, u16 len, u8 tag, u64 qpath);
+Iobuf*	getbuf(u64 blkno, u16 len, u8 readonly, u8 freshalloc);
+Iobuf*	getbufchk(u64 blkno, u16 len, u8 readonly, int tag, u64 qpath);
+Iobuf*	getmeta(u64 blkno, u8 readonly, u8 freshalloc);
+Iobuf*	getmetachk(u64 blkno, u8 readonly, int tag, u64 qpath);
 void	iobufinit(void);
-void	putbuf(Iobuf *p);
+void	putbuf(Iobuf *p, u8 dowrite);
 void	putbuffree(Iobuf *p);
 void	settag(Iobuf *p, u8 tag, u64 qpath);
 void	showbuf(Iobuf *p);
@@ -135,12 +117,15 @@
 
 /* routines to manipulate the contents */
 Iobuf*	allocblocks(u64 len, int tag, u64 qpath);
+Iobuf*	allocmeta(int tag, 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();
+u64		newqpath(void);
 u64		nperiblock(u16 tag);
+u64		nperindunit(u16 tag);
+u64		power( u64 base, int n);
 void	ream(u64 size);
 u64	rel2abs(Dentry *d, u64 reli);
 void	rmfile(u64 qpath, u64 dblkno);
@@ -154,12 +139,15 @@
 
 /* dentry routines */
 u64 	addrelative(Dentry *d, u64 dblkno, u64 reli, u64 blkno);
-Iobuf*	getdatablkat(Dentry *d, u64 reli, int flags);
+void	freeaux(Aux *a);
+Iobuf*	getdatablkat(Dentry *d, u64 reli);
 void	loadfrees(u64 dblkno);
+Aux*	newaux(u64 addr, u16 uid);
 s32		readfile(u64 dblkno, u64 qpath, char *rbuf, s32 rbufsize, u64 offset, Req *req);
 s32		readfilesize(u64 dblkno, u64 qpath);
 void	savefrees(u64 dblkno);
 void	truncatefile(u64 qpath, u64 dblkno, s16 uid);
+s32		writeallappend(Iobuf *dbuf, Dentry *d, u64 dblkno);
 s32		writefile(u64 dblkno, u64 qpath, s16 uid, char *wbuf, s32 wbufsize, u64 offset, Req *req);
 
 /* user access routines */
@@ -173,13 +161,9 @@
 u8	chkwunlock(RWLock *q);
 void 	cmd_user(void);
 char*	cname(char*);
-void	ctlread(Req *req);
-void	ctlwrite(Req *req);
-int		dprint(char *fmt, ...);
 void	sublockinit(void);
 int	fname(char*);
 void	formatinit(void);
-void	freeaux(Aux *a);
 void   freesearchstate(Iobuf **dbuf, Iobuf **buf);
 int	iaccess(short uid, Dentry *d, int m);
 int	ingroup(s16 uid, s16 gid, int locked);
@@ -186,23 +170,23 @@
 void	initconfig(u64 bno);
 int	leadgroup(s16 uid, s16 gid);
 s16	lookupid(char *name);
-u64	min(u64 a, u64 b);
-Aux	*newaux(u64 addr, u16 uid);
-u64	newqpath(void);
 void	newstart(void);
-u64	nperindunit(u16 tag);
 int	oconvD2M(Dentry*, void*);
 int	oconvM2D(void*, Dentry*);
 int	ofcallfmt(Fmt*);
-u64	power( u64 base, int n);
-int	prime(long);
-void	putsuper(int locked);
 int	strtouid(char*);
 int	strtouid1(char*);
-void	sync(void);
 void	syncusers(void);
 char	*username(s16 uid, char *username);
 void	usersinit(void);
 void	writeconfig(u64 bno);
+
+void	ctlread(Req *req);
+void	ctlwrite(Req *req);
+
+int	dprint(char *fmt, ...);
+int	prime(long);
+u64	min(u64 a, u64 b);
+void	sync(void);
 
 #pragma varargck	argpos	panic	1
--- a/blk.c
+++ b/blk.c
@@ -88,20 +88,20 @@
 void
 showblock(int fd, u8 *buf)
 {
+	u8 tag;
 	Data *t;
 	Dentry *d;
-	u8 tag;
 	Indirect *r;
 
 	tag = buf[0];
-	d = (Dentry*)buf;
 	t = (Data*)buf;
 	r = (Indirect*)buf;
+	d = (Dentry*)buf;
 	if(tag == Tblank){
 		fprint(fd, "%s\n", tagnames[tag]);
 		return;
 	}else if(tag == Tdentry){
-		fprint(fd, "%s %llud %d\n", tagnames[tag], d->path, d->ver);
+		fprint(fd, "%s %llud %d\n", tagnames[tag], d->path, d->verd);
 		showdentry(fd, buf);
 		return;
 	}else if(tag == Tdata){
@@ -109,10 +109,35 @@
 		showdata(fd, buf);
 		return;
 	}else if(tag < Maxtind)
-		fprint(fd, "%s %d %llud %llud\n", tagnames[tag], r->ver, r->dblkno, r->path);
+		fprint(fd, "%s %d %llud %llud\n", tagnames[tag], r->veri, r->dblkno, r->path);
 
-	if(t->tag >= Tind0 && t->tag < Maxtind)
+	if(tag >= Tind0 && tag < Maxtind)
 		showind(fd, buf);
-	else if(t->tag != 0 || t->path != 0)
-		fprint(fd, "unknown tag type %d path %llud\n", t->tag, t->path);
+	else
+		fprint(fd, "unknown tag type %d\n", tag);
 }
+
+u8
+recentmetadata(void *buf, void **cur, void **new)
+{
+	Metadataunit *ms;
+
+	if(buf == nil)
+		panic("recentmetadata buf == nil\n");
+
+	ms = buf;
+	if((ms[0].verd == 0 && ms[1].verd == 255) ||
+		(ms[0].verd >= ms[1].verd)){
+		if(new != nil)
+			*new = ms+1;
+		if(cur != nil)
+			*cur = ms;
+		return 0;
+	}
+
+	if(new != nil)
+		*new = ms;
+	if(cur != nil)
+		*cur = ms+1;
+	return 1;
+}
\ No newline at end of file
--- a/block.c
+++ b/block.c
@@ -19,7 +19,7 @@
 main(int argc, char *argv[])
 {
 	u64 size, blkno, nunits;
-	u8 buf[Rawblocksize];
+	u8 buf[Blocksize];
 
 	ARGBEGIN{
 	default:	usage();
@@ -47,11 +47,8 @@
 				sizeof(Dentryhdr), Ddatasize, Namelen);
 	}
 
-	nunits = size/Unit;
-	if(blkno+Rawblockunits > nunits)
-		devread(blkno, buf, Dentryunits);
-	else
-		devread(blkno, buf, Rawblockunits);
+	// nunits = size/Blocksize;
+	devread(blkno, buf, 1);
 	showblock(1, buf);
 	close(devfd);
 	exits(0);
--- a/config.c
+++ b/config.c
@@ -5,14 +5,14 @@
 initconfig(u64 dblkno)
 {
 	Iobuf *buf;
-	char *tokens[128], cfg[Datablocksize], *nl;
+	char *tokens[128], cfg[Ddatasize], *nl;
 	s32 n, i;
 
-	buf = getbufchk(dblkno, Breadonly, Tdentry, Qpconfig);
+	buf = getmetachk(dblkno, Breadonly, Tdentry, Qpconfig);
 	if(buf == nil)
 		panic("cannot find config file in %llud block\n", dblkno);
-	memcpy(cfg, buf->d->buf, buf->d->size);
-	putbuf(buf);
+	memcpy(cfg, buf->cur->buf, buf->cur->size);
+	putbuf(buf, 0);
 
 	n = tokenize(cfg, tokens, 128);
 	if(n > 128)
@@ -67,21 +67,18 @@
 	dprint("config:\n"
 			"size %llud\n"
 			"nblocks %llud\n"
-			"backup config %llud to %llud %llud\n"
-			"backup super %llud to %llud %llud\n"
-			"backup root %llud to %llud %llud\n",
+			"backup config %llud to %llud\n"
+			"backup super %llud to %llud\n"
+			"backup root %llud to %llud\n",
 			"service %s\n",
 			config.size,
 			config.nblocks,
 			config.config.srcbno,
 			config.config.dest[0],
-			config.config.dest[1],
 			config.super.srcbno,
 			config.super.dest[0],
-			config.super.dest[1],
 			config.root.srcbno,
 			config.root.dest[0],
-			config.root.dest[1],
 			config.service);
 }
 
@@ -91,32 +88,29 @@
 	Iobuf *buf;
 	s32 n;
 
-	buf = getbufchk(bno, Bwritable, Tdentry, Qpconfig);
+	buf = getmetachk(bno, Bwritable, Tdentry, Qpconfig);
 	if(buf == nil)
 		panic("cannot write config");
-	n = snprint((s8*)buf->d->buf, Ddatasize,
+	n = snprint((s8*)buf->new->buf, Ddatasize,
 			"size %llud\n"
 			"nblocks %llud\n"
-			"backup config %llud to %llud %llud\n"
-			"backup super %llud to %llud %llud\n"
-			"backup root %llud to %llud %llud\n"
+			"backup config %llud to %llud\n"
+			"backup super %llud to %llud\n"
+			"backup root %llud to %llud\n"
 			"service %s\n",
 			config.size,
 			config.nblocks,
 			config.config.srcbno,
 			config.config.dest[0],
-			config.config.dest[1],
 			config.super.srcbno,
 			config.super.dest[0],
-			config.super.dest[1],
 			config.root.srcbno,
 			config.root.dest[0],
-			config.root.dest[1],
 			config.service);
 	if(n == Ddatasize)
 		panic("config needs more blocks");
-	buf->d->size = n;
+	buf->new->size = n;
 	if(chatty9p > 1)
-		dprint("config:\n%s", buf->d->buf);
-	putbuf(buf);
+		dprint("config:\n%s", buf->cur->buf);
+	putbuf(buf, 1);
 }
--- a/ctl.c
+++ b/ctl.c
@@ -18,8 +18,8 @@
 int
 cmdsync(Cmdbuf *)
 {
-	sync();
-	dprint("sync\n");
+//	sync();
+	dprint("sync TODO\n");
 	return 0;
 }
 
@@ -93,7 +93,8 @@
 	free = nfrees(&frees);
 	used = config.nblocks - free;
 
-	n = snprint(buf, 1024, "pending writes %llud blocks\n", pendingwrites());
+/*	n = snprint(buf, 1024, "pending writes %llud blocks\n", pendingwrites());*/
+	n = 0;
 	if(config.size > TiB)
 	n += snprint(buf+n, 1024-n, "(blocks) free %ulld, used %ulld, total %ulld\n"
 						"(MiB) free %ulld, used %ulld, total %ulld\n"
@@ -153,8 +154,8 @@
 {
 	Cmdbuf *cb;
 	Cmdtab *ct;
-	int n;
 	char srvfilename[Namelen];
+	int n;
 
 	if(chatty9p)
 		dprint("consproc: >%s\n", req->ifcall.data);
@@ -173,7 +174,7 @@
 						this stuff is only needed when the worker is doing
 						the shutdown()
 					 */
-					n = remove(srvfilename);
+					remove(srvfilename);
 					close(req->srv->infd);
 					close(req->srv->outfd);
 					if(mpsrvpid > 0){
--- a/dat.h
+++ b/dat.h
@@ -11,6 +11,7 @@
 typedef	struct	Config	Config;
 typedef	struct	Data	Data;
 typedef struct	Metadata Metadata;
+typedef struct	Metadataunit Metadataunit;
 typedef	struct	Dentryhdr	Dentryhdr;
 typedef	struct	Dentry	Dentry;
 typedef	struct	Datahdr	Datahdr;
@@ -78,9 +79,12 @@
 	Bdctl	= 16,	/* block number of /adm/ctl dentry, empty contents, virtual file */
 	Bdusersstaging = 18,/* block number of /adm/users/staging dentry */
 	Bdroot	= 20,	/* block number of root directory */
-	Nbused,			/* blocks used up by default */
-	Nminblocks = Nbused+(Nbkp*3), /* number of blocks used by the above and the backup blocks */
 
+	Nbused	= Bdroot+2,	/* blocks used up by default */
+
+	/* number of blocks used by the above and the backup blocks */
+	Nminblocks = Nbused+(Metadataunits*Nbkp*3),
+
 	/* Qpnone is the Tag.path of free blocks/extents(Tfree),
 		and zero'ed out dentry blocks */
 	Qpnone		= 0,
@@ -117,7 +121,7 @@
 struct Dentryhdr
 {
 	u8 tag;
-	u8 ver;			/* a counter to do copy on write */
+	u8 verd;
 	s16 uid;
 	s16 gid;
 	s16 muid;		/* 8 */
@@ -125,7 +129,7 @@
 	u64 pdblkno; 	/* block number of the parent directory entry. Will be 0 for root. - 24 */
 	u64 pqpath; 	/* parent qid.path - 32 */
 	u64 mtime;		/* modified time in nano seconds from epoch - 40 */
-	u64 path;		/* unique identifier Qid.path 48 */
+	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 */
 	u8 namelen;		/* store name as a counted string 57 */
@@ -173,12 +177,12 @@
 		/* when size <= Dentrysize-184-sizeof(Tag), store the data here itself */
 		s8 buf[Ddatasize];
 	};
-	u64 path;
+	u64 path;	/* same as qid.path */
 };
 struct Indirect
 {
 	u8 tag;
-	u8 ver;
+	u8 veri;
 	u8 pad[6];	/* unused, to align to a multiple of 8 */
 	u64 dblkno;	/* block number of the directory entry */
 	u64 bufa[Nindperblock];
@@ -194,7 +198,11 @@
 };
 struct Metadata
 {
-	struct Metadataunit m[2];
+	union
+	{
+		Indirect i[2];
+		Dentry d[2];
+	};
 };
 struct Data		/* used to unmarshall the disk contents */
 {
--- a/dentry.c
+++ b/dentry.c
@@ -17,18 +17,18 @@
 	if(blkno == 0){
 		return 0;
 	}
-	if((buf = getbufchk(blkno, Breadonly, tag, path)) == nil){
+	if((buf = getmetachk(blkno, Breadonly, tag, path)) == nil){
 		dprint("%s",errstring[Ephase]);
 		return 0;
 	}
 	if(tag > Tind0){
 		n = nperindunit(tag);
-		b = getindblk(buf->io->bufa[reli/n],
+		b = getindblk(buf->cur->bufa[reli/n],
 						reli%n, tag-1, path);
 	}else{
-		b = buf->io->bufa[reli];
+		b = buf->cur->bufa[reli];
 	}
-	putbuf(buf);
+	putbuf(buf, 0);
 	return b;
 }
 
@@ -55,7 +55,7 @@
 		return d->dblocks[reli];
 
 	tag = rel2tind(reli);
-	return getindblk(d->iblocks[tag-Tind0], reli-tagstartreli(tag), tag, d->qid.path);
+	return getindblk(d->iblocks[tag-Tind0], reli-tagstartreli(tag), tag, d->path);
 }
 
 u64
@@ -69,13 +69,13 @@
 		return 0;
 	}
 	if(indblkno == 0){
-		if((buf = allocblock(tag, path)) == nil){
+		if((buf = allocmeta(tag, path)) == nil){
 			dprint("%s",errstring[Efull]);
 			return 0;
 		}
 		indblkno = buf->blkno;
 	}else{
-		if((buf = getbufchk(indblkno, Bwritable, tag, path)) == nil){
+		if((buf = getmetachk(indblkno, Bwritable, tag, path)) == nil){
 			dprint("updateindblock: %s",errstring[Ephase]);
 			return 0;
 		}
@@ -99,10 +99,10 @@
 			dprint("%s",errstring[Ephase]);
 			return 0;
 		}
-		childindblkno = updateindblock(buf->io->bufa[reli/n],
+		childindblkno = updateindblock(buf->new->bufa[reli/n],
 								reli%n, tag-1, path,
 								blkno);
-		buf->io->bufa[reli/n] = childindblkno;
+		buf->new->bufa[reli/n] = childindblkno;
 	}else{
 		if(reli >= Nindperblock){
 			panic("updateindblock invalid reli: indblkno %llud reli %llud tag %s"
@@ -111,9 +111,9 @@
 			dprint("%s",errstring[Ephase]);
 			return 0;
 		}
-		buf->io->bufa[reli] = blkno;
+		buf->new->bufa[reli] = blkno;
 	}
-	putbuf(buf);
+	putbuf(buf, 1);
 	return indblkno;
 }
 
@@ -135,7 +135,7 @@
 	dprint("addrelative %llud:%s reli %llud blkno %llud\n",
 			dblkno, d->name, reli, blkno);
 
-	path = d->qid.path;
+	path = d->path;
 	if(reli < Ndblock){
 		d->dblocks[reli] = blkno;
 		return blkno;
@@ -161,8 +161,8 @@
 	as this would all be dangling stuff identifiable by the fsck
 	and removed by it on a crash.
  */
-void
-freeindblock(u64 iblkno, u16 tag, u64 qpath, u16 directtag)
+u64
+freeindblock(u64 iblkno, u16 tag, u64 qpath, u16 directtag, u64 reli, u64 lastreli, u64 lastunits)
 {
 	Iobuf *ibuf;
 	int i;
@@ -169,27 +169,37 @@
 	u64 blkno;
 
 	if(iblkno == 0)
-		return;
-	ibuf = getbufchk(iblkno, Bwritable, tag, qpath);
-	if(ibuf == nil)
-			dprint("%s",errstring[Ephase]);
+		return reli;
+	ibuf = getmetachk(iblkno, Bwritable, tag, qpath);
+	if(ibuf == nil){
+		panic("%s",errstring[Ephase]);
+		return reli;
+	}
 
 	if(tag == Tind0)
 		for(i=0; i < Nindperblock; i++){
-			blkno = ibuf->io->bufa[i];
+			blkno = ibuf->cur->bufa[i];
 			if(blkno == 0)
 				break;
-			freeblock(blkno, directtag, qpath);
-			ibuf->io->bufa[i] = 0;
+			if(directtag == Tdentry)
+				freeblocks(blkno, Metadataunits, directtag, qpath);
+			else if(reli < lastreli)
+				freeblocks(blkno, Maxdatablockunits, directtag, qpath);
+			else
+				freeblocks(blkno, lastunits, directtag, qpath);
+			ibuf->new->bufa[i] = 0;
+			reli++;
 		}
 	else
 		for(i=0; i < Nindperblock; i++){
-			if(ibuf->io->bufa[i] == 0)
+			if(ibuf->cur->bufa[i] == 0)
 				break;
-			freeindblock(ibuf->io->bufa[i], tag-1, qpath, directtag);
-			ibuf->io->bufa[i] = 0;
+			reli = freeindblock(ibuf->cur->bufa[i], tag-1, qpath, directtag,
+								reli, lastreli, lastunits);
+			ibuf->new->bufa[i] = 0;
 		}
 	freeblockbuf(ibuf);
+	return reli;
 }
 
 /* dbuf should be wlock'ed */
@@ -198,8 +208,9 @@
 {
 	Dentry *d, d1;
 	int i;
+	u64 reli, lastreli, lastblks;
 
-	d = dbuf->d;
+	d = (Dentry*)dbuf->cur;
 	memcpy(&d1, d, sizeof(Dentry));
 	for(i=0; i<Ndblock; i++)
 		d->dblocks[i] = 0;
@@ -208,17 +219,23 @@
 	d->size = 0;
 	d->mtime = nsec();
 	d->muid = uid;
-	putbuf(dbuf);
+	putbuf(dbuf, 1);
 
+	lastreli = d1.size/Maxdatablocksize;
+	lastblks = nlastdatablocks(d1.size);
 	for(i=0; i<Ndblock; i++){
 		if(d1.dblocks[i] == 0)
 			return;
-		freeblock(d1.dblocks[i], Tdata, d1.qid.path);
+		if(i < lastreli)
+			freeblocks(d1.dblocks[i], Maxdatablockunits, Tdata, d1.path);
+		else
+			freeblocks(d1.dblocks[i], lastblks, Tdata, d1.path);
 	}
-	for(i=0;i<Niblock; i++){
+	for(i=0, reli=Ndblock;i<Niblock; i++){
 		if(d1.iblocks[i] == 0)
 			return;
-		freeindblock(d1.iblocks[i], Tind0+i, d1.qid.path, Tdata);
+		reli = freeindblock(d1.iblocks[i], Tind0+i, d1.path, Tdata,
+						reli, lastreli, lastblks);
 	}
 }
 
@@ -237,7 +254,7 @@
 
 	if(qpath < Qpusers || dblkno == 0)
 		return;
-	dbuf = getbufchk(dblkno, Bwritable, Tdentry, qpath);
+	dbuf = getmetachk(dblkno, Bwritable, Tdentry, qpath);
 	if(dbuf == nil)
 			dprint("%s",errstring[Ephase]);
 
@@ -262,31 +279,44 @@
 	Iobuf *dbuf;
 	Dentry d;
 	int i;
-	u64 size;
+	u64 size, reli, lastreli, lastblks;
 
 	if(qpath < Qpusers || dblkno == 0)
 		return;
-	dbuf = getbufchk(dblkno, Bwritable, Tdentry, qpath);
+	dbuf = getmetachk(dblkno, Bwritable, Tdentry, qpath);
 	if(dbuf == nil)
 			dprint("%s",errstring[Ephase]);
-	size = dbuf->d->size;
-	memcpy(&d, dbuf->d, sizeof(Dentry));
-	memset(dbuf->d, 0, Dentrysize);
+	size = dbuf->cur->size;
+	memcpy(&d, dbuf->cur, sizeof(Dentry));
+	memset(dbuf->new, 0, Blocksize);
 	settag(dbuf, Tdentry, Qpnone);
-	putbuf(dbuf);
+	dbuf->new->verd = dbuf->cur->verd+1;
+	devwrite(dblkno, dbuf->xiobuf, Metadataunits);
+	if(dbuf->append){ /* data not yet written */
+		freememunits((u8*)dbuf->append, Maxdatablockunits);
+		dbuf->append = nil;
+		dbuf->appendsize = 0;
+	}
+	putbuf(dbuf, 1);
 
 	if(size < Ddatasize)
 		return;
 
+	lastreli = d.size/Maxdatablocksize;
+	lastblks = nlastdatablocks(d.size);
 	for(i=0; i<Ndblock; i++){
 		if(d.dblocks[i] == 0)
 			return;
-		freeblock(d.dblocks[i], Tdata, qpath);
+		if(i < lastreli)
+			freeblocks(d.dblocks[i], Maxdatablockunits, Tdata, qpath);
+		else
+			freeblocks(d.dblocks[i], lastblks, Tdata, qpath);
 	}
-	for(i=0;i<Niblock; i++){
+	for(i=0, reli=Ndblock;i<Niblock; i++){
 		if(d.iblocks[i] == 0)
 			return;
-		freeindblock(d.iblocks[i], Tind0+i, qpath, Tdata);
+		reli = freeindblock(d.iblocks[i], Tind0+i, qpath, Tdata,
+							reli, lastreli, lastblks);
 	}
 }
 
@@ -304,13 +334,14 @@
 	if(qpath < Qpusers || dblkno == 0)
 		return;
 	/* clear the dentry to avoid links to removed content */
-	dbuf = getbufchk(dblkno, Bwritable, Tdentry, qpath);
+	dbuf = getmetachk(dblkno, Bwritable, Tdentry, qpath);
 	if(dbuf == nil)
 			dprint("%s",errstring[Ephase]);
-	memcpy(&d, dbuf->d, sizeof(Dentry));
-	memset(dbuf->d, 0, Dentrysize);
+	memcpy(&d, dbuf->cur, sizeof(Dentry));
+	memset(dbuf->new, 0, Blocksize);
 	settag(dbuf, Tdentry, Qpnone);
-	putbuf(dbuf);
+	dbuf->new->verd = dbuf->cur->verd+1;
+	putbuf(dbuf, 1);
 
 	/*
 		remove the children
@@ -321,12 +352,12 @@
 		if((blkno = rel2abs(&d, reli)) == 0)
 			break;
 
-		buf = getbuf(blkno, Dentryunits, Breadonly, Bused);
-		child = buf->d;
-		cqpath = child->qid.path;
+		buf = getmeta(blkno, Breadonly, Bused);
+		child = (Dentry*)buf->cur;
+		cqpath = child->path;
 		mode = child->mode;
-		ct = buf->d->tag;
-		putbuf(buf);
+		ct = child->tag;
+		putbuf(buf, 0);
 
 		/* nothing to do for already zero'ed out slots */
 		if(cqpath == Qpnone || ct == Tnone)
@@ -345,12 +376,13 @@
 	for(i=0; i<Ndblock; i++){
 		if(d.dblocks[i] == 0)
 			return;
-		freeblock(d.dblocks[i], Tdentry, Qpnone);
+		freeblocks(d.dblocks[i], Metadataunits, Tdentry, Qpnone);
 	}
-	for(i=0;i<Niblock; i++){
+	for(i=0, reli=Ndblock;i<Niblock; i++){
 		if(d.iblocks[i] == 0)
 			return;
-		freeindblock(d.iblocks[i], Tind0+i, qpath, Tdentry);
+		reli = freeindblock(d.iblocks[i], Tind0+i, qpath, Tdentry,
+							reli, 0, Metadataunits);
 	}
 }
 
@@ -409,13 +441,13 @@
 	Dentry *d, *ch;
 	u64 blkno;
 
-	*dbuf = getbufchk(dblkno, Breadonly, Tdentry, qpath);
+	*dbuf = getmetachk(dblkno, Breadonly, Tdentry, qpath);
 	if(*dbuf == nil)
 		return nil;
-	d = (*dbuf)->d;
+	d = (Dentry*)((*dbuf)->cur);
 
 	if(canaccess(uid, d, DMEXEC) == 0){
-		putbuf(*dbuf);
+		putbuf(*dbuf, 0);
 		*dbuf = nil;
 		dprint("%s",errstring[Ephase]);
 		return nil;
@@ -426,7 +458,7 @@
 
 		
 		if((blkno = rel2abs(d, reli)) == 0){
-			putbuf(*dbuf);
+			putbuf(*dbuf, 0);
 			*dbuf = nil;
 			return nil;
 		}
@@ -434,23 +466,23 @@
 			dprint("searchdir reli %d s.blkno %llud s.len %d\n",
 					reli, blkno);
 
-		*buf = getbuf(blkno, Dentryunits, Breadonly, Bused);
+		*buf = getmeta(blkno, Breadonly, Bused);
 		if(*buf == nil){
-			putbuf(*dbuf);
+			putbuf(*dbuf, 0);
 			*dbuf = nil;
 			dprint("%s",errstring[Ephase]);
 			return nil;
 		}
 
-		ch = (*buf)->d;
+		ch = (Dentry*)(*buf)->cur;
 		if(chatty9p > 2)
 			dprint("searchdir: dblkno %llud qpath %llud searchname %s searchidx %d"
-					" reli %llud blkno %llud ch->qid.path %llud\n",
+					" reli %llud blkno %llud ch->path %llud\n",
 					dblkno, qpath, searchname, searchidx, reli, blkno,
-					ch->qid.path);
-		if(checktag(*buf, Tdentry, ch->qid.path) == 0){
-			putbuf(*buf);
-			putbuf(*dbuf);
+					ch->path);
+		if(checktag(*buf, Metadataunits, Tdentry, ch->path) == 0){
+			putbuf(*buf, 0);
+			putbuf(*dbuf, 0);
 			*dbuf = *buf = nil;
 			dprint("%s",errstring[Ephase]);
 			return nil;
@@ -474,7 +506,7 @@
 
 		idx++; /* so that zero'ed slots do not match */
 Nextdentry:
-		putbuf(*buf);
+		putbuf(*buf, 0);
 	}
 	/* should never be here */
 }
@@ -481,8 +513,8 @@
 void
 freesearchstate(Iobuf **dbuf, Iobuf **buf)
 {
-	putbuf(*buf);
-	putbuf(*dbuf);
+	putbuf(*buf, 0);
+	putbuf(*dbuf, 0);
 	*dbuf = *buf = nil;
 }
 
@@ -492,7 +524,7 @@
 	only for file contents. not for directory contents.
  */
 Iobuf *
-getdatablkat(Dentry *d, u64 reli, int flags)
+getdatablkat(Dentry *d, u64 reli)
 {
 	Iobuf *buf;
 	u64 blkno;
@@ -502,9 +534,13 @@
 	 */
 	if((blkno = rel2abs(d, reli)) == 0)
 		return nil;
-	buf = getbufchk(blkno, flags, Tdata, d->qid.path);
+
+	if(reli < d->size/Maxdatablocksize)
+		buf = getbufchk(blkno, Maxdatablockunits, Breadonly, Tdata, d->path);
+	else
+		buf = getbufchk(blkno, nlastdatablocks(d->size), Breadonly, Tdata, d->path);
 	if(buf == nil){
-		putbuf(buf);
+		putbuf(buf, 0);
 		dprint("%s",errstring[Ephase]);
 		return nil;
 	}
@@ -518,6 +554,7 @@
 {
 	s32 nbuf;
 	s8 *buf;
+	Iobuf *dbuf;
 
 	/* should not be necessary as we clear out the file
 		in loadfrees() */
@@ -536,6 +573,16 @@
 	/* writing the actual extents now */
 	writefile(dblkno, Qpfrees, -1, buf, nbuf, 0, nil);
 	free(buf);
+
+	/* flush to the disk if append has stuff */
+	dbuf = getmetachk(dblkno, Bwritable, Tdentry, Qpfrees);
+	if(dbuf == nil)
+		return;
+	if(dbuf->append != nil){
+		writeallappend(dbuf, dbuf->cur, dblkno);
+		putbuf(dbuf, 1);
+	}else
+		putbuf(dbuf, 0);
 }
 
 void
@@ -564,10 +611,10 @@
 	/* I cannot use truncatefile() below as it would
 		bfree() the blocks (which are already in the Extents frees)
 		and that would cause an inconsistency/panic */
-	dbuf = getbufchk(dblkno, Bwritable, Tdentry, Qpfrees);
+	dbuf = getmetachk(dblkno, Bwritable, Tdentry, Qpfrees);
 	if(dbuf == nil)
 			dprint("%s",errstring[Ephase]);
-	d = dbuf->d;
+	d = (Dentry*)dbuf->new;
 	d->size = 0;
 	for(i=0; i<Ndblock; i++)
 		d->dblocks[i] = 0;
@@ -575,5 +622,5 @@
 		d->iblocks[i] = 0;
 	d->mtime = nsec();
 	d->muid = -1;
-	putbuf(dbuf);
+	putbuf(dbuf, 1);
 }
--- a/find.c
+++ b/find.c
@@ -62,7 +62,7 @@
 	if(size == 0)
 		panic("null size %s", devfile);
 	if(debug)
-		print("%s %llud bytes %llud blocks\n", devfile, size, size/Unit);
+		print("%s %llud bytes %llud blocks\n", devfile, size, size/Blocksize);
 
 	walkdirectory(Bdroot, 0);
 	exits(0);
@@ -79,7 +79,7 @@
 }
 
 int
-checkvalid(u64 blkno, u8 dtag, u64 dpath, u8 tag, u64 qpath)
+checkvalid(u64 blkno, u8 dtag, u64 dpath, u8 tag, u64 qpath, u16 len)
 {
 	int ret = 1;
 
@@ -91,7 +91,7 @@
 		ret=0;
 	}/*else
 		print("%llud\n", blkno);*/
-	if(blkno == findblkno){
+	if(blkno <= findblkno || findblkno < blkno+len){
 		showparents();
 		exits(nil);
 	}
@@ -101,14 +101,15 @@
 void
 walkindir(u64 blkno, u8 tag, u8 bottomtag, u64 qpath, s8 depth)
 {
-	u8 buf[Rawblocksize], *cbuf;
-	Content *t, *ct;
+	u8 buf[Metadatablocksize], *cbuf;
+	Indirect *t;
+	Data *ct;
 	u64 cblkno, *bufa;
 	int i;
 
-	devread(blkno, buf, Rawblockunits);
-	t = (Content*)buf;
-	if(checkvalid(blkno, t->tag, t->path, tag, qpath)){
+	devread(blkno, buf, Metadataunits);
+	t = (Indirect*)buf;
+	if(checkvalid(blkno, t->tag, t->path, tag, qpath, Metadataunits)){
 		if(t->tag == Tind0){
 			bufa = (u64*)buf;
 			for(i = 0; i<Nindperblock; i++){
@@ -115,28 +116,25 @@
 				cblkno = bufa[i];
 				if(cblkno == 0)
 					return;
-				cbuf = malloc(Rawblocksize);
-				devread(cblkno, cbuf, Rawblockunits);
-				ct = (Content*)cbuf;
 				if(Tdentry == bottomtag)
 					/* another directory */
 					walkdirectory(cblkno, depth);
-				else
-					checkvalid(cblkno, ct->tag, ct->path, bottomtag, qpath);
-				free(cbuf);
+				else{
+					cbuf = malloc(Blocksize);
+					devread(cblkno, cbuf, 1);
+					ct = (Data*)cbuf;
+					checkvalid(cblkno, ct->tag, ct->path, bottomtag, qpath, ct->len);
+					free(cbuf);
+				}
 			}
 		}else{
-			bufa = (u64*)buf;
-			cbuf = malloc(Rawblocksize);
 			for(i = 0; i<Nindperblock; i++){
-				cblkno = bufa[i];
+				cblkno = t->bufa[i];
 				if(cblkno == 0)
 					return;
-				devread(cblkno, cbuf, Rawblockunits);
 				/* check tag */
 				walkindir(cblkno, tag-1,  bottomtag, qpath, depth);
 			}
-			free(cbuf);
 		}
 	}
 	return;
@@ -165,23 +163,23 @@
 void
 walkdirectory(u64 blkno, s8 depth)
 {
-	u8 buf[Dentrysize], *cbuf;
+	u8 buf[Metadatablocksize], *cbuf;
 	Dentry *d, *cd;
-	Content *ct;
+	Indirect *ct;
 	u64 cblkno;
 	int i;
 
-	devread(blkno, buf, Dentryunits);
+	devread(blkno, buf, Metadataunits);
 	d = (Dentry*)buf;
 	showdepth(depth);
 	print("%llud:%s\n", blkno, d->name);
 	if(debug)
-		print("walkdentry %llud tag %s name %s d->qid.path %llud\n",
-				blkno, tagnames[d->tag], d->name, d->qid.path);
-	if(d->tag != Tdentry || d->path != d->qid.path){
+		print("walkdentry %llud tag %s name %s d->qpath %llud\n",
+				blkno, tagnames[d->tag], d->name, d->qpath);
+	if(d->tag != Tdentry || d->path != d->qpath){
 		if(debug)
 			print("walkdentry invalid %llud tag/path expected %s/%llud actual %s/%llud\n",
-					blkno, tagnames[Tdentry], d->qid.path, tagnames[d->tag], d->path);
+					blkno, tagnames[Tdentry], d->qpath, tagnames[d->tag], d->path);
 		fprint(2, "%llud\n", blkno);
 	}/*else
 		print("%llud\n", blkno);*/
@@ -190,8 +188,8 @@
 		cblkno = d->dblocks[i];
 		if(cblkno == 0)
 			return;
-		cbuf = malloc(Dentrysize);
-		devread(cblkno, cbuf, Dentryunits);
+		cbuf = malloc(Metadatablocksize);
+		devread(cblkno, cbuf, Metadataunits);
 		cd = (Dentry*)cbuf;
 		if((cd->mode & DMDIR) > 0)
 			walkdirectory(cblkno, depth+1);
@@ -199,15 +197,15 @@
 			walkfile(cblkno, depth+1);
 		free(cbuf);
 	}
-	cbuf = malloc(Rawblocksize);
+	cbuf = malloc(Metadatablocksize);
 	for(i = 0; i<Niblock; i++){
 		cblkno = d->iblocks[i];
 		if(cblkno == 0)
 			return;
-		devread(cblkno, cbuf, Rawblockunits);
-		ct = (Content*)cbuf;
+		devread(cblkno, cbuf, Metadataunits);
+		ct = (Indirect*)cbuf;
 		if(ct->tag == Tind0+i){
-			walkindir(cblkno, Tind0+i, Tdentry, d->qid.path, depth);
+			walkindir(cblkno, Tind0+i, Tdentry, d->qpath, depth);
 		}else{
 			fprint(2, "invalid indir tag %llud\n", cblkno);
 			fprint(2, "%llud\n", cblkno);
@@ -220,23 +218,24 @@
 void
 walkfile(u64 blkno, s8 depth)
 {
-	u8 buf[Rawblocksize], *cbuf;
+	u8 buf[Metadatablocksize], *cbuf;
 	Dentry *d;
-	Content *ct;
+	Data *ct;
+	Indirect *it;
 	u64 cblkno;
 	int i;
 
-	devread(blkno, buf, Dentryunits);
+	devread(blkno, buf, Metadataunits);
 	d = (Dentry*)buf;
 	showdepth(depth);
 	print("%llud:%s\n", blkno, d->name);
 	if(debug)
 		print("walkfile %llud tag %s name %s d->qid.path %llud\n",
-				blkno, tagnames[d->tag], d->name, d->qid.path);
-	if(d->tag != Tdentry || d->path != d->qid.path){
+				blkno, tagnames[d->tag], d->name, d->qpath);
+	if(d->tag != Tdentry || d->path != d->qpath){
 		if(debug)
 			print("walkdentry invalid %llud tag/path expected %s/%llud actual %s/%llud\n",
-					blkno, tagnames[Tdentry], d->qid.path, tagnames[d->tag], d->path);
+					blkno, tagnames[Tdentry], d->qpath, tagnames[d->tag], d->path);
 		fprint(2, "%llud\n", blkno);
 	}/*else
 		print("%llud\n", blkno);*/
@@ -247,21 +246,21 @@
 		cblkno = d->dblocks[i];
 		if(cblkno == 0)
 			return;
-		cbuf = malloc(Rawblocksize);
-		devread(cblkno, cbuf, Rawblockunits);
-		ct = (Content*)cbuf;
-		checkvalid(cblkno, ct->tag, ct->path, Tdata, d->qid.path);
+		cbuf = malloc(Metadatablocksize);
+		devread(cblkno, cbuf, Metadataunits);
+		ct = (Data*)cbuf;
+		checkvalid(cblkno, ct->tag, ct->path, Tdata, d->qpath, ct->len);
 		free(cbuf);
 	}
-	cbuf = malloc(Rawblocksize);
+	cbuf = malloc(Metadatablocksize);
 	for(i = 0; i<Niblock; i++){
 		cblkno = d->iblocks[i];
 		if(cblkno == 0)
 			return;
-		devread(cblkno, cbuf, Rawblockunits);
-		ct = (Content*)cbuf;
-		if(ct->tag == Tind0+i){
-			walkindir(cblkno, Tind0+i, Tdata, d->qid.path, depth);
+		devread(cblkno, cbuf, Metadataunits);
+		it = (Indirect*)cbuf;
+		if(it->tag == Tind0+i){
+			walkindir(cblkno, Tind0+i, Tdata, d->qpath, depth);
 		}else{
 			fprint(2, "invalid indir tag %llud\n", cblkno);
 			fprint(2, "%llud\n", cblkno);
--- a/fns.h
+++ b/fns.h
@@ -1,4 +1,5 @@
 
+u64 nlastdatablocks(u64 size);
 u64 nperiblock(u16 tag);
 u64	nperindunit(u16 tag);
 u8	rel2tind(u64 reli);
@@ -5,6 +6,8 @@
 u64	tagstartreli(u8 tag);
 u64	maxreli(u8 tag);
 u64	maxblocks(u8 tag);
+u64	nlastdatablocks(u64 size);
+u64 size2datareli(u64 size);
 
 /* actual device i/o routines */
 u64	devinit(char *devfile);
@@ -22,6 +25,7 @@
 void	showind0(int fd, u8 *buf);
 void	showind(int fd, u8 *buf);
 u16		blklen(u16 tag);
+u8		recentmetadata(void *buf, void **old, void **new);
 
 void	panic(char*, ...);
 int	fprint(int, char*, ...);
--- a/free.c
+++ b/free.c
@@ -47,7 +47,7 @@
 	if(size == 0)
 		panic("null size %s", devfile);
 	if(chatty9p)
-		print("%s %llud bytes %llud blocks\n", devfile, size, size/Unit);
+		print("%s %llud bytes %llud blocks\n", devfile, size, size/Blocksize);
 
 	checkdentry(Bdfrees, Tdentry, Qpfrees);
 	initextents(&frees);
@@ -59,7 +59,7 @@
 }
 
 int
-checkvalid(u64 blkno, u8 dtag, u64 dpath, u8 tag, u64 qpath)
+checkvalid(u64 blkno, u8 dtag, u64 dpath, u8 tag, u64 qpath, u16 len)
 {
 	if(dtag != tag || dpath != qpath){
 		/* if(chatty9p) */
@@ -69,7 +69,7 @@
 		return 0;
 	}
 	if(chatty9p)
-	print("%llud\n", blkno);
+		print("%llud %d\n", blkno, len);
 	return 1;
 }
 
@@ -76,12 +76,12 @@
 int
 checkdentry(u64 blkno, u8 tag, u64 qpath)
 {
-	u8 buf[Dentrysize];
+	u8 buf[Metadatablocksize];
 	Dentry *t;
 
-	devread(blkno, buf, Dentryunits);
+	devread(blkno, buf, Metadataunits);
 	t = (Dentry*)buf;
-	return checkvalid(blkno, t->tag, t->path, tag, qpath);
+	return checkvalid(blkno, t->tag, t->path, tag, qpath, Metadataunits);
 }
 
 void
@@ -89,9 +89,9 @@
 {
 	s8 *buf;
 
-	buf = emalloc(Rawblocksize);
-	devread(blkno, buf, Rawblockunits);
-	loadextents(&frees, buf, Datablocksize);
+	buf = emalloc(Metadatablocksize);
+	devread(blkno, buf, Metadataunits);
+	loadextents(&frees, buf, Maxdatablocksize);
 	free(buf);
 }
 
@@ -98,26 +98,24 @@
 void
 walkindir(u64 blkno, u16 tag, u16 bottomtag, u64 qpath)
 {
-	u8 buf[Rawblocksize];
-	Content *t;
-	u64 cblkno, *bufa;
+	u8 buf[Metadatablocksize];
+	Indirect *t;
+	u64 cblkno;
 	int i;
 
-	devread(blkno, buf, Rawblockunits);
-	t = (Content*)buf;
-	if(checkvalid(blkno, t->tag, t->path, tag, qpath)){
+	devread(blkno, buf, Metadataunits);
+	t = (Indirect*)buf;
+	if(checkvalid(blkno, t->tag, t->path, tag, qpath, Metadataunits)){
 		if(t->tag == Tind0){
-			bufa = (u64*)buf;
 			for(i = 0; i<Nindperblock; i++){
-				cblkno = bufa[i];
+				cblkno = t->bufa[i];
 				if(cblkno == 0)
 					return;
 				loadfreeextents(cblkno);
 			}
 		}else{
-			bufa = (u64*)buf;
 			for(i = 0; i<Nindperblock; i++){
-				cblkno = bufa[i];
+				cblkno = t->bufa[i];
 				if(cblkno == 0)
 					return;
 				/* check tag */
@@ -131,13 +129,13 @@
 getfrees(u64 dblkno)
 {
 	u64 size;
-	u8 buf[Dentrysize];
+	u8 buf[Metadatablocksize];
 	Dentry *d;
 	u64 cblkno;
 	int i;
 
-	devread(dblkno, buf, Dentryunits);
-	d = (Dentry*)buf;
+	devread(dblkno, buf, Metadataunits);
+	recentmetadata(buf, &d, nil);
 	size = d->size;
 	if(size == 0)
 		panic("loadfreeextents size == 0");
@@ -146,7 +144,7 @@
 		loadextents(&frees, d->buf, d->size);
 		return;
 	}
-	if(checkvalid(dblkno, d->tag, d->path, Tdentry, Qpfrees)){
+	if(checkvalid(dblkno, d->tag, d->path, Tdentry, Qpfrees, Metadataunits)){
 		for(i = 0; i<Ndblock; i++){
 			cblkno = d->dblocks[i];
 			if(cblkno == 0)
--- a/iobuf.c
+++ b/iobuf.c
@@ -6,7 +6,7 @@
 extern u8 synchronouswrites;
 
 /*
-	extents of Unit units of memory used to store
+	extents of Blocksize units of memory used to store
 	the disk block contents in memory for the buffer cache
 	and write queue
  */
@@ -18,12 +18,12 @@
 void
 initmemunitpool(u64 nunits)
 {
-	memunitstart = sbrk((nunits+1) * Unit);
-	memunitpool = memunitstart+Unit- ((u64)memunitstart%Unit);
+	memunitstart = sbrk((nunits+1) * Blocksize);
+	memunitpool = memunitstart+Blocksize- ((u64)memunitstart%Blocksize);
 	initextents(&memunits);
 	if(chatty9p > 4)
-		dprint("initmemunitpool: memunitpool %p nunits*Unit %p\n",
-				memunitpool, nunits*Unit);
+		dprint("initmemunitpool: memunitpool %p nunits*Blocksize %p\n",
+				memunitpool, nunits*Blocksize);
 	bfree(&memunits, 0, nunits);
 }
 
@@ -36,7 +36,7 @@
 	if(chatty9p > 4)
 		dprint("allocmemunit: memunitpool %p m %p\n",
 				memunitpool, m);
-	return memunitpool+(m*Unit);
+	return memunitpool+(m*Blocksize);
 }
 
 void
@@ -44,10 +44,10 @@
 {
 	if(m == 0)
 		panic("freememunit: m == 0\n");
-	if((m-memunitpool)%Unit)
-		panic("freememunit: (m-memunitpool)%%Unit %llud\n",
-				(u64)(m-memunitpool)%Unit);
-	bfree(&memunits, (m-memunitpool)/Unit, len);
+	if((m-memunitpool)%Blocksize)
+		panic("freememunit: (m-memunitpool)%%Blocksize %llud\n",
+				(u64)(m-memunitpool)%Blocksize);
+	bfree(&memunits, (m-memunitpool)/Blocksize, len);
 }
 
 /*
@@ -146,7 +146,9 @@
 				freememunits(p->xiobuf, p->len);
 				p->xiobuf = allocmemunits(len);
 				p->len = len;
-				if(freshalloc == 0)
+				if(freshalloc)
+					memset(p->xiobuf, 0, len*Blocksize);
+				else
 					devread(blkno, p->xiobuf, len);
 				if(readonly){
 					if(chkwunlock(p) == 0){
@@ -192,8 +194,8 @@
 Another:
 		do{
 			p = s->back;
-			if(p->ref == 0 && p->dirties.ref == 0 && canwlock(p)){
-				if(p->dirties.ref > 0 || p->ref > 0){
+			if(p->ref == 0 && p->append == nil && canwlock(p)){
+				if(p->ref > 0){
 					wunlock(p);
 					goto Another;
 				}
@@ -227,7 +229,9 @@
 	if(chatty9p > 4)
 		dprint("	after qunlock(hp) hp 0x%p blkno %llud\n",
 				hp, blkno);
-	if(freshalloc == 0)
+	if(freshalloc)
+		memset(p->xiobuf, 0, len*Blocksize);
+	else
 		devread(blkno, p->xiobuf, len);
 	if(readonly){
 		if(chatty9p > 4)
@@ -241,21 +245,23 @@
 }
 
 Iobuf *
-getbufchk(u64 blkno, u8 readonly, int tag, u64 qpath)
+getbufchk(u64 blkno, u16 len, u8 readonly, int tag, u64 qpath)
 {
 	Iobuf *b;
-	u16 len;
 
 	if(chatty9p > 4)
 		dprint("getbufchk caller pc 0x%p\n", getcallerpc(&blkno));
-	if(tag == Tdentry)
-		len = Dentryunits;
-	else
-		len = Rawblockunits;
 	b = getbuf(blkno, len, readonly, Bused);
 	if(b != nil)
-		if(checktag(b, tag, qpath) == 0){
-			putbuf(b);
+		if(tag != Tdata){
+			recentmetadata(b->m, &b->cur, &b->new);
+			if(readonly == 0){ /* writable */
+				memcpy(b->new, b->cur, Blocksize);
+				b->new->verd++;
+			}
+		}
+		if(checktag(b, len, tag, qpath) == 0){
+			putbuf(b, 0);
 			panic("checktag on %llud failed %s\n", blkno, errstring[Ephase]);
 		}
 	if(b->io == nil)
@@ -266,6 +272,28 @@
 	return b;
 }
 
+Iobuf *
+getmetachk(u64 blkno, u8 readonly, int tag, u64 qpath)
+{
+	return getbufchk(blkno, Metadataunits, readonly, tag, qpath);
+}
+
+Iobuf *
+getmeta(u64 blkno, u8 readonly, u8 freshalloc)
+{
+	Iobuf *b;
+
+	b = getbuf(blkno, Metadataunits, readonly, freshalloc);
+	if(b == nil)
+		return nil;
+	recentmetadata(b->m, &b->cur, &b->new);
+	if(readonly == 0){ /* writable */
+		memcpy(b->new, b->cur, Blocksize);
+		b->new->verd++;
+	}
+	return b;
+}
+
 /*
 	put the Iobuf of the disk block at addr back into
 		the buffer cache for others to use.
@@ -290,22 +318,21 @@
 		return;
 	}
 
-	buf = getbufchk(bno, Bwritable, Tdentry, qpath);
+	buf = getmetachk(bno, Bwritable, Tdentry, qpath);
 	if(buf == nil){
 		panic("bkp: buf == nil\n");
 	}
-	memcpy(buf->d->buf, contents, Ddatasize);
-	buf->d->mtime = nsec();
+	memcpy(buf->new->buf, contents, Ddatasize);
+	buf->new->mtime = nsec();
 //	if(qpath == Qproot0 || qpath == Qproot1){
 //		buf->d->mode &= ~DMDIR; /* to avoid recursive du -a */
 //	}
-	putbuf(buf);
+	putbuf(buf, 1);
 }
 
 void
-putbuf(Iobuf *p)
+putbuf(Iobuf *p, u8 dowrite)
 {
-	s8 i;
 	u8 buf[Ddatasize];
 	u64 srcbno;
 
@@ -329,34 +356,31 @@
 		if(chatty9p > 4)
 		dprint(" .. runlock()'ed\n");
 	}else{
-		if(canwlock(p)){
-			panic("putbuf: buffer not locked %llud\n", p->blkno);
-		}
 		srcbno = p->blkno;
-		if(p->blkno == config.config.srcbno ||
-			p->blkno == config.super.srcbno ||
-			p->blkno == config.root.srcbno){
-			memcpy(buf, p->d->buf, Ddatasize);
-		}
-		if(synchronouswrites){
-			if(p->blkno < 10)
+		if(dowrite){
+			if(p->xiobuf[0] == Tdata){
 				devwrite(p->blkno, p->xiobuf, p->len);
-			if(chkwunlock(p) == 0){
-				showbuf(p);
-				panic("putbuf: chkwunlock(p) == 0 called by %#p\n", getcallerpc(&p));
+			}else{
+				if(p->blkno == config.config.srcbno ||
+					p->blkno == config.super.srcbno ||
+					p->blkno == config.root.srcbno)
+					memcpy(buf, p->new->buf, Ddatasize);
+				devwrite(p->blkno+(p->new>p->cur?1:0), p->new, 1);
 			}
-		}else
-			putwrite(p);
-		if(srcbno == config.config.srcbno){
-			for(i=0; i<Nbkp; i++)
-				bkp(srcbno, buf, config.config.dest[i], Qpconfig0+i*3);
-		}else if(srcbno == config.super.srcbno){
-			for(i=0; i<Nbkp; i++)
-				bkp(srcbno, buf, config.super.dest[i], Qpsuper0+i*3);
-		}else if(srcbno == config.root.srcbno){
-			for(i=0; i<Nbkp; i++)
-				bkp(srcbno, buf, config.root.dest[i], Qproot0+i*3);
 		}
+		if(chkwunlock(p) == 0){
+			showbuf(p);
+			panic("putbuf: chkwunlock(p) == 0 called by %#p\n", getcallerpc(&p));
+		}
+		if(dowrite){
+			if(srcbno == config.config.srcbno){
+				bkp(srcbno, buf, config.config.dest[0], Qpconfig0);
+			}else if(srcbno == config.super.srcbno){
+				bkp(srcbno, buf, config.super.dest[0], Qpsuper0);
+			}else if(srcbno == config.root.srcbno){
+				bkp(srcbno, buf, config.root.dest[0], Qproot0);
+			}
+		}
 	}
 }
 
@@ -398,22 +422,20 @@
 }
 
 int
-checktag(Iobuf *p, u8 tag, u64 qpath)
+checktag(Iobuf *p, u16 len, u8 tag, u64 qpath)
 {
 	uintptr pc;
-	u16 len;
 	u16 ptag;
 	u64 pqpath;
 
-	if(tag == Tdentry){
-		ptag = p->d->tag;
-		pqpath = p->d->path;
-	}else{
+	if(tag == Tdata){
 		ptag = p->io->tag;
 		pqpath = p->io->path;
+	}else{
+		ptag = ((Dentry*)p->cur)->tag;
+		pqpath = ((Dentry*)p->cur)->path;
 	}
 
-	len = blklen(tag);
 	if(len != p->len ||
 		tag != ptag ||
 		(qpath != Qpnone && qpath != pqpath)){
@@ -423,10 +445,11 @@
 				(uint)ptag, (uint)tag, p->blkno);
 		if(qpath == Qpnone){
 			dprint("checktag pc=%p disk %s(block %llud) tag/path=%s/%llud;"
-					" expected %s\n",
+					" expected %s len %llud p->len %llud\n",
 					pc, devfile, (u64)p->blkno,
 					tagnames[ptag], (u64)pqpath,
-					tagnames[tag]);
+					tagnames[tag],
+					len, p->len);
 		} else {
 				dprint("	tag/path = %G/%llux; expected %G/%llux\n",
 						(uint)ptag, pqpath, tag, qpath);
@@ -450,12 +473,12 @@
 	if(p->io == nil)
 		panic("settag %s(%llux) tag/path=%s/%llud: p->io == nil\n",
 				devfile, (u64)p->blkno, tagnames[tag], qpath);
-	if(tag == Tdentry){
-		p->d->tag = Tdentry;
-		p->d->path = qpath;
-	}else{
-		p->io->tag = tag;
+	if(tag == Tdata){
+		p->io->tag = Tdentry;
 		p->io->path = qpath;
+	}else{
+		((Dentry*)p->new)->tag = tag;
+		((Dentry*)p->new)->path = qpath;
 	}
 }
 
@@ -506,7 +529,7 @@
 		return;
 	}
 	dprint("showbuf p 0x%p ref %d readers %d writer %d"
-			" blkno %llud len %llud"
+			" blkno %llud len %d"
 			" fore 0x%p back 0x%p"
 			" xiobuf 0x%p"
 			" caller %#p\n",
--- a/mafs.c
+++ b/mafs.c
@@ -6,8 +6,6 @@
 u8	noauth = 0;
 u8	readonly = 0;
 u8	shuttingdown = 0;
-u8	synchronouswrites = 0;
-extern u64 npendingwrites;		/* write throttling */
 
 int	writeallow;	/* never on; for compatibility with fs */
 int	wstatallow;
@@ -40,7 +38,6 @@
 	rfork(RFNAMEG|RFNOTEG|RFREND);
 
 	nbuckets = 0;
-	npendingwrites = 0;
 	nmemunits = 0;
 	doream = stdio = 0;
 
@@ -55,7 +52,6 @@
 	case 'D':	chatty9p++; break;
 	case 'f':	devfile = ARGF(); break;
 	case 'h':	nbuckets = atoll(EARGF(usage())); break;
-	case 'w':	npendingwrites = atoll(EARGF(usage())); break;
 	case 'm':	nmemunits = atoll(EARGF(usage())); break;
 	case 'r':
 		doream = 1;
@@ -95,8 +91,8 @@
 	if(chatty9p){
 		dprint("\nPlan 9 %d-bit file server with %d-deep indirect blocks\n",
 			sizeof(u64)*8, Niblock);
-		dprint("nmemunits %llud npendingwrites %llud nbuckets %llud\n",
-				nmemunits, npendingwrites, nbuckets);
+		dprint("nmemunits %llud nbuckets %llud\n",
+				nmemunits, nbuckets);
 	}
 
 	formatinit();
@@ -103,7 +99,6 @@
 
 	initmemunitpool(nmemunits);
 	initextents(&frees);
-	initwriter();
 	iobufinit();
 
 	/*
--- a/misc.c
+++ b/misc.c
@@ -17,7 +17,7 @@
 chkwunlock(RWLock *q)
 {
 	if(q->writer <= 0){
-		print("wunlock(%#p): not locked: pc %#p\n",
+		panic("wunlock(%#p): not locked: pc %#p\n",
 			q, getcallerpc(&q));
 		return 0;
 	}
--- a/mkfile
+++ b/mkfile
@@ -16,8 +16,8 @@
 	sub.$O\
 	tag.$O\
 	user.$O\
-	writer.$O\
 
+
 HFILES=\
 	all.h\
 	dat.h\
@@ -34,22 +34,22 @@
 </sys/src/cmd/mkmany
 # LDFLAGS=-p
 
-$O.updatefrees:	blk.$O dat.$O dev.$O misc.$O updatefrees.$O
+$O.updatefrees:	blk.$O dat.$O dev.$O misc.$O tag.$O updatefrees.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.unused:	dat.$O dev.$O extents.$O unused.$O misc.$O
+$O.unused:	blk.$O dat.$O dev.$O extents.$O unused.$O misc.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.used:	blk.$O dat.$O dev.$O extents.$O misc.$O used.$O
+$O.used:	blk.$O dat.$O dev.$O extents.$O misc.$O tag.$O used.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.free:	dat.$O dev.$O extents.$O free.$O misc.$O
+$O.free:	blk.$O dat.$O dev.$O extents.$O free.$O misc.$O tag.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.block:	blk.$O dat.$O dev.$O misc.$O block.$O
+$O.block:	blk.$O dat.$O dev.$O misc.$O tag.$O block.$O
 	$LD $LDFLAGS -o $target $prereq
 
-$O.find:	blk.$O dat.$O dev.$O misc.$O find.$O
+$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
--- a/sub.c
+++ b/sub.c
@@ -11,17 +11,17 @@
 	s32 on, nn;
 	s8 buf[Ddatasize-16];
 
-	sb = getbufchk(Bdsuper, Metadataunits, Bwritable, Tdentry, Qpsuper);
+	sb = getmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
 	if(sb == nil){
 		panic("newqpath: sb == nil\n");
 	}
-	s = (Dentry*)sb->use;
+	s = (Dentry*)sb->new;
 	on = snprint(buf, Ddatasize-16, "%llud", s->qidgen);
 	qpath = s->qidgen++;
 	nn = snprint(buf, Ddatasize-16, "%llud", s->qidgen);
 	if(on != nn)
 		s->size += nn-on;
-	putbuf(sb);
+	putbuf(sb, 1);
 	return qpath;
 }
 
@@ -91,10 +91,22 @@
 
 	/* clear the buf to avoid leaks on reuse */
 	memset(buf->xiobuf, 0, len*Blocksize);
+	if(tag != Tdata)
+		recentmetadata(buf->m, &buf->cur, &buf->new);
 	settag(buf, tag, qpath);
 	return buf;
 }
 
+Iobuf *
+allocmeta(int tag, u64 qpath)
+{
+	Iobuf *b;
+
+	b = allocblocks(Metadataunits, tag, qpath);
+	b->new->verd++;
+	return b;
+}
+
 /* the buf should have been wlock()'ed */
 void
 freeblockbuf(Iobuf *buf)
@@ -151,17 +163,17 @@
 	Iobuf *sb;
 	Dentry *s;
 
-	sb = getbufchk(Bdsuper, Metadataunits, Bwritable, Tdentry, Qpsuper);
+	sb = getmetachk(Bdsuper, Bwritable, Tdentry, Qpsuper);
 	if(sb == nil){
 		panic("fsok: sb == nil\n");
 	}
-	s = (Dentry*)sb->use;
+	s = (Dentry*)sb->new;
 	s->fsok = ok;
 	if(chatty9p > 1){
 		dprint("fsok ok %d\n", ok);
 		showsuper(2, (u8*)s);
 	}
-	putbuf(sb);
+	putbuf(sb, 1);
 }
 
 void
@@ -172,16 +184,16 @@
 }
 
 void
-reamfile(u64 dblkno, u64 qpath, char *name, u64 size, u64 pdblkno, u64 pqpath, u64 contentblkno)
+reamfile(u64 dblkno, u64 qpath, char *name, u64 size, u64 pdblkno, u64 pqpath)
 {
 	Iobuf *b;
 	Dentry *d;
 
-	b = getbuf(dblkno, Metadataunits, Bwritable, Bfreshalloc);
-	memset(b->m, 0, Metadatablocksize);
+	b = getmeta(dblkno, Bwritable, Bfreshalloc);
+	if(b == nil)
+		panic("reamfile dblkno %llud b == nil\n", dblkno);
 	settag(b, Tdentry, qpath);
-	d = (Dentry*)b->use;
-	d->ver++;
+	d = (Dentry*)b->new;
 	d->namelen = strlen(name);
 	strcpy(d->name, name);
 	d->uid = d->muid = d->gid = -1;
@@ -189,15 +201,13 @@
 		((DMREAD) << 6) |
 		((DMREAD) << 3) |
 		((DMREAD) << 0);
-	d->path = qpath;
+	d->qpath = qpath;
 	d->version = 0;
 	d->mtime = nsec();
 	d->size = size;
 	d->pdblkno = pdblkno;
 	d->pqpath = pqpath;
-	if(contentblkno > 0)
-		d->dblocks[0] = contentblkno;
-	putbuf(b);
+	putbuf(b, 1);
 }
 
 /*
@@ -223,11 +233,9 @@
 					"10006:%s:%s:\n", user, user, user);
 
 	/* cannot show this in /adm though as the block number is 0 */
-	b = getbuf(Bdmagic, Metadataunits, Bwritable, Bfreshalloc);
-	memset(b->m, 0, Metadatablocksize);
+	b = getmeta(Bdmagic, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpmagic);
-	d = (Dentry*)b->use;
-	d->ver++;
+	d = (Dentry*)b->new;
 	d->namelen = 5;
 	strncpy(d->name, "magic", 6);
 	d->uid = d->muid = d->gid = -1;
@@ -235,7 +243,7 @@
 		((DMREAD|DMWRITE|DMEXEC) << 6) |
 		((DMREAD|DMWRITE|DMEXEC) << 3) |
 		((DMREAD|DMWRITE|DMEXEC) << 0);
-	d->path = Qpmagic;
+	d->qpath = Qpmagic;
 	d->version = 0;
 	d->mtime = nsec();
 	d->pdblkno = Bdadm;
@@ -243,13 +251,11 @@
 	n = snprint((s8*)d->buf, Ddatasize, "%s%llud\n",
 			magic, Blocksize);
 	d->size = n;
-	putbuf(b);
+	putbuf(b, 1);
 
-	b = getbuf(Bdadm, Metadataunits, Bwritable, Bfreshalloc);
-	memset(b->m, 0, Metadatablocksize);
+	b = getmeta(Bdadm, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpadm);
-	d = (Dentry*)b->use;
-	d->ver++;
+	d = (Dentry*)b->new;
 	d->namelen = 3;
 	strncpy(d->name, "adm", 4);
 	d->uid = d->muid = d->gid = -1;
@@ -257,7 +263,7 @@
 		((DMREAD|DMWRITE|DMEXEC) << 6) |
 		((DMREAD|DMWRITE|DMEXEC) << 3) |
 		((DMREAD|DMWRITE|DMEXEC) << 0);
-	d->path = Qpadm;
+	d->qpath = Qpadm;
 	d->version = 0;
 	d->mtime = nsec();
 	d->pdblkno = Bdroot;
@@ -268,13 +274,11 @@
 	d->dblocks[3] = Bdbkp;
 	d->dblocks[4] = Bdfrees;
 	d->dblocks[5] = Bdctl;
-	putbuf(b);
+	putbuf(b, 1);
 
-	b = getbuf(Bdbkp, Metadataunits, Bwritable, Bfreshalloc);
-	memset(b->m, 0, Metadatablocksize);
+	b = getmeta(Bdbkp, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpbkp);
-	d = (Dentry*)b->use;
-	d->ver++;
+	d = (Dentry*)b->new;
 	d->namelen = 3;
 	strncpy(d->name, "bkp", 4);
 	d->uid = d->muid = d->gid = -1;
@@ -282,7 +286,7 @@
 		((DMREAD|DMWRITE|DMEXEC) << 6) |
 		((DMREAD|DMWRITE|DMEXEC) << 3) |
 		((DMREAD|DMWRITE|DMEXEC) << 0);
-	d->path = Qpbkp;
+	d->qpath = Qpbkp;
 	d->version = 0;
 	d->mtime = nsec();
 	d->size = strlen(users)+1;
@@ -291,13 +295,11 @@
 	d->dblocks[0] = bdconfig0;
 	d->dblocks[1] = bdsuper0;
 	d->dblocks[2] = bdroot0;
-	putbuf(b);
+	putbuf(b, 1);
 
-	b = getbuf(Bdusers, Metadataunits, Bwritable, Bfreshalloc);
-	memset(b->m, 0, Metadatablocksize);
+	b = getmeta(Bdusers, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpusers);
-	d = (Dentry*)b->use;
-	d->ver++;
+	d = (Dentry*)b->new;
 	d->namelen = 5;
 	strncpy(d->name, "users", 6);
 	d->uid = d->muid = d->gid = -1;
@@ -305,7 +307,7 @@
 		((DMREAD|DMWRITE|DMEXEC) << 6) |
 		((DMREAD|DMWRITE|DMEXEC) << 3) |
 		((DMREAD|DMWRITE|DMEXEC) << 0);
-	d->path = Qpusers;
+	d->qpath = Qpusers;
 	d->version = 0;
 	d->mtime = nsec();
 	d->size = strlen(users)+1;
@@ -313,13 +315,11 @@
 	d->pqpath = Qpadm;
 	d->dblocks[0] = Bdusersinuse;
 	d->dblocks[1] = Bdusersstaging;
-	putbuf(b);
+	putbuf(b, 1);
 
-	b = getbuf(Bdusersinuse, Metadataunits, Bwritable, Bfreshalloc);
-	memset(b->m, 0, Metadatablocksize);
+	b = getmeta(Bdusersinuse, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qpusersinuse);
-	d = (Dentry*)b->use;
-	d->ver++;
+	d = (Dentry*)b->new;
 	d->namelen = 5;
 	strncpy(d->name, "inuse", 6);
 	d->uid = d->muid = d->gid = -1;
@@ -327,7 +327,7 @@
 		((DMREAD) << 6) |
 		((DMREAD) << 3) |
 		((DMREAD) << 0);
-	d->path = Qpusersinuse;
+	d->qpath = Qpusersinuse;
 	d->version = 0;
 	d->mtime = nsec();
 	d->size = userslen;
@@ -334,13 +334,11 @@
 	d->pdblkno = Bdusers;
 	d->pqpath = Qpusers;
 	strcpy((s8*)d->buf, users);
-	putbuf(b);
+	putbuf(b, 1);
 
-	b = getbuf(Bdroot, Metadataunits, Bwritable, Bfreshalloc);
-	memset(b->m, 0, Metadatablocksize);
+	b = getmeta(Bdroot, Bwritable, Bfreshalloc);
 	settag(b, Tdentry, Qproot);
-	d = (Dentry*)b->use;
-	d->ver++;
+	d = (Dentry*)b->new;
 	d->namelen = 1;
 	strncpy(d->name, "/", 5);
 	d->uid = d->muid = -1;
@@ -349,18 +347,18 @@
 		((DMREAD|DMWRITE|DMEXEC) << 6) |
 		((DMREAD|DMWRITE|DMEXEC) << 3) |
 		((DMREAD|DMWRITE|DMEXEC) << 0);
-	d->path = Qproot;
+	d->qpath = Qproot;
 	d->version = 0;
 	d->mtime = nsec();
 	d->dblocks[0] = Bdadm;
-	putbuf(b);
+	putbuf(b, 1);
 
-	reamfile(Bdconfig, Qpconfig, "config", 0, Bdadm, Qpadm, 0);
-	reamfile(Bdctl, Qpctl, "ctl", 0, Bdadm, Qpadm, 0);
-	reamfile(Bdfrees, Qpfrees, "frees", 0, Bdadm, Qpadm, 0);
+	reamfile(Bdconfig, Qpconfig, "config", 0, Bdadm, Qpadm);
+	reamfile(Bdctl, Qpctl, "ctl", 0, Bdadm, Qpadm);
+	reamfile(Bdfrees, Qpfrees, "frees", 0, Bdadm, Qpadm);
 
 	reamfile(Bdusersstaging, Qpusersstaging, "staging",
-			 0, Bdusers, Qpusers, 0);
+			 0, Bdusers, Qpusers);
 }
 
 void
@@ -374,10 +372,10 @@
 	sbuf = getbuf(Bdsuper, Metadataunits, Bwritable, Bfreshalloc);
 	if(sbuf == nil)
 		panic("superream: sbuf == nil");
-	memset(sbuf->m, 0, Metadatablocksize);
+	sbuf->cur = sbuf->new = (Metadataunit*)sbuf->m;
 	settag(sbuf, Tdentry, Qpsuper);
-	s = (Dentry*)sbuf->use;
-	s->ver++;
+	s = (Dentry*)sbuf->new;
+	s->verd++;
 	s->namelen = 5;
 	strncpy(s->name, "super", 6);
 	s->uid = s->muid = s->gid = -1;
@@ -385,7 +383,7 @@
 		((DMREAD) << 6) |
 		((DMREAD) << 3) |
 		((DMREAD) << 0);
-	s->path = Qpsuper;
+	s->qpath = Qpsuper;
 	s->version = 0;
 	s->mtime = nsec();
 	s->size = 18;
@@ -400,15 +398,21 @@
 	config.nblocks = nblocks;
 	config.config.dest[0] = nblocks-Bdconfig;
 	config.super.dest[0] = nblocks-Bdsuper;
-	config.root.dest[0] = nblocks-3;
+	config.root.dest[0] = nblocks-6;
 
+	/* this will enable backups */
+	config.config.srcbno = Bdconfig;
+	config.super.srcbno = Bdsuper;
+	config.root.srcbno = Bdroot;
+	strncpy(config.service, service, Namelen);
+
 	reamfile(config.config.dest[0], Qpconfig0, "config.0",
-			 0, Bdbkp, Qpbkp, config.config.dest[0]);
+			 0, Bdbkp, Qpbkp);
 	reamfile(config.super.dest[0], Qpsuper0, "super.0",
-			 0, Bdbkp, Qpbkp, config.super.dest[0]);
+			 0, Bdbkp, Qpbkp);
 	reamfile(config.root.dest[0], Qproot0, "root.0",
-			 0, Bdbkp, Qpbkp, config.root.dest[0]);
-	putbuf(sbuf);
+			 0, Bdbkp, Qpbkp);
+	putbuf(sbuf, 1);
 
 	if(chatty9p > 1)
 	dprint("backup config %llud"
@@ -426,12 +430,6 @@
 	if(chatty9p > 1)
 		dprint("done\n");
 
-	/* this will enable backups */
-	config.config.srcbno = Bdconfig;
-	config.super.srcbno = Bdsuper;
-	config.root.srcbno = Bdroot;
-	strncpy(config.service, service, Namelen);
-
 	fsok(0);
 }
 
@@ -490,35 +488,35 @@
 	}
 
 	/* check magic */
-	b = getbufchk(Bdmagic, Metadataunits, Breadonly, Tdentry, Qpmagic);
+	b = getmetachk(Bdmagic, Breadonly, Tdentry, Qpmagic);
 	if(b == nil){
 		panic("Invalid magic: %s",errstring[Ephase]);
 		return;
 	}
-	if(strncmp((s8*)((Dentry*)b->use)->buf,magic,strlen(magic)) != 0){
+	if(strncmp((s8*)((Dentry*)b->cur)->buf,magic,strlen(magic)) != 0){
 		print("init: bad magic -%s-", (s8*)b->io+256);
 		panic("bad magic");
 	}
-	unitsize = strtoul((s8*)((Dentry*)b->use)->buf+strlen(magic), &rptr, 10);
+	unitsize = strtoul((s8*)((Dentry*)b->cur)->buf+strlen(magic), &rptr, 10);
 	if(unitsize != Blocksize){
 		print("init incorrect block size Blocksize %llud unitsize %d\n",
 				Blocksize, unitsize);
 		panic("bad Blocksize != unitsize");
 	}
-	putbuf(b);
+	putbuf(b, 0);
 
 	/* check super */
-	sb = getbufchk(Bdsuper, Metadataunits, Breadonly, Tdentry, Qpsuper);
+	sb = getmetachk(Bdsuper, Breadonly, Tdentry, Qpsuper);
 	if(sb == nil){
 		panic("Invalid super: %s",errstring[Ephase]);
 		return;
 	}
-	s = (Dentry*)sb->use;
+	s = (Dentry*)sb->cur;
 	if(s->fsok != 1 || config.size != size)
 		panic(errstring[Edirty]);
 
 	s->fsok = 0;
-	putbuf(sb);
+	putbuf(sb, 1);
 	shuttingdown = 0;
 	usersinit();
 }
--- a/tag.c
+++ b/tag.c
@@ -112,3 +112,39 @@
 {
 	return maxreli(tag)+1;
 }
+
+/* size to Tdata reli */
+u64
+size2datareli(u64 size)
+{
+	return size/Maxdatablocksize;
+}
+
+/* number of Tdata blocks for this size */
+u64
+size2ndata(u64 size)
+{
+	if(size == 0)
+		return 0;
+	if(size%Maxdatablocksize == 0)
+		return size/Maxdatablocksize;
+	else
+		return 1+ size/Maxdatablocksize;
+}
+
+/*
+	for allocate on flush.
+	number of blocks of the last Tdata block
+ */
+u64
+nlastdatablocks(u64 size)
+{
+	u64 lastblkholds;
+
+	if((lastblkholds = size%Maxdatablocksize) == 0)
+		return Maxdatablockunits;
+	if(lastblkholds%(Blocksize-sizeof(Datahdr)-sizeof(u64)) == 0)
+		return lastblkholds/(Blocksize-sizeof(Datahdr)-sizeof(u64));
+	else
+		return 1+(lastblkholds/(Blocksize-sizeof(Datahdr)-sizeof(u64)));
+}
--- a/tests/mkfile
+++ b/tests/mkfile
@@ -2,7 +2,7 @@
 
 BIN=/$objtype/bin/disk
 
-TARG=sizes offsets testextents pwrites reli # mfsck
+TARG=sizes offsets testextents pwrites reli nlastdatablocks # mfsck
 
 OFILES=\
 	../dat.$O\
--- a/tests/regress.rc
+++ b/tests/regress.rc
@@ -93,10 +93,10 @@
 	chkreli.rc
 	chkextents.rc
 	for (t in test.*){
-		rununi mfs $t
+#		rununi mfs $t
 		rununi mafs $t
-		runmp mfs $t
-		runmp mafs $t
+#		runmp mfs $t
+#		runmp mafs $t
 	}
 }
 if not {
@@ -107,9 +107,9 @@
 		chkextents.rc
 	}
 	if not {
-		rununi mfs $*
+#		rununi mfs $*
 		rununi mafs $*
-		runmp mfs $*
-		runmp mafs $*
+#		runmp mfs $*
+#		runmp mafs $*
 	}
 }
--- a/updatefrees.c
+++ b/updatefrees.c
@@ -26,9 +26,9 @@
 {
 	u64 size, freeblkno;
 	s32 nfreesize;
-	s8 buf[Dentrysize], frees[Rawblocksize];
+	s8 buf[Metadatablocksize], frees[Maxdatablocksize];
 	int fd;
-	Content *c;
+	Data *c;
 	Dentry *d;
 
 	ARGBEGIN{
@@ -47,19 +47,19 @@
 	if(freesfile == nil)
 		sysfatal("no frees file");
 
-	memset(buf,0,Dentrysize);
-	memset(frees, 0, Rawblocksize);
+	memset(buf,0,Metadatablocksize);
+	memset(frees, 0, Maxdatablocksize);
 
 	fd = open(freesfile, OREAD);
 	if(fd < 0)
 		sysfatal("updatefrees: cannot open freesfile %s\n", freesfile);
 
-	nfreesize = read(fd, frees, Rawblocksize);
+	nfreesize = read(fd, frees, Maxdatablocksize);
 	if(nfreesize <= 0)
 		sysfatal("updatefrees: nfreesize %d <= 0\n", nfreesize);
-	if(nfreesize > Datablocksize)
+	if(nfreesize > Maxdatablocksize)
 		sysfatal("updatefrees: unsupported nfreesize %d > Datablocksize %llud\n",
-					nfreesize, Datablocksize);
+					nfreesize, Maxdatablocksize);
 	close(fd);
 
 	freeblkno = atoll((s8*)frees);
@@ -76,7 +76,7 @@
 		print("Dentry size %d\n", sizeof(Dentry));
 	}
 
-	devread(Bdfrees, buf, Dentryunits);
+	devread(Bdfrees, buf, Metadataunits);
 	// showblock(1, buf);
 
 	d = (Dentry*)buf;
@@ -85,18 +85,18 @@
 	d->size = nfreesize;
 	if(nfreesize < Ddatasize){
 		strncpy(d->buf, frees, nfreesize);
-		devwrite(Bdfrees, buf, Dentryunits);
+		devwrite(Bdfrees, buf, Metadataunits);
 	}else{
 		if(d->dblocks[0] != freeblkno)
 			print("changed dblocks[0]: %llud to %llud\n", d->dblocks[0], freeblkno);
 		d->dblocks[0] = freeblkno;
 
-		c = (Content*)frees;
+		c = (Data*)frees;
 		c->tag = Tdata;
 		c->path = Qpfrees;
 
-		devwrite(Bdfrees, buf, Dentryunits);
-		devwrite(freeblkno, frees, Rawblockunits);
+		devwrite(Bdfrees, buf, Metadataunits);
+		devwrite(freeblkno, frees, nlastdatablocks(nfreesize));
 	}
 	close(devfd);
 	exits(0);
--- a/used.c
+++ b/used.c
@@ -49,7 +49,7 @@
 	if(size == 0)
 		panic("null size %s", devfile);
 	if(chatty9p)
-		print("%s %llud bytes %llud blocks\n", devfile, size, size/Unit);
+		print("%s %llud bytes %llud blocks\n", devfile, size, size/Blocksize);
 
 	initextents(&useds);
 	checkdentry(Bdmagic, Tdentry, Qpmagic);
@@ -60,7 +60,7 @@
 }
 
 int
-checkvalid(u64 blkno, u8 dtag, u64 dpath, u8 tag, u64 qpath)
+checkvalid(u64 blkno, u8 dtag, u64 dpath, u8 tag, u64 qpath, u16 len)
 {
 	if(dtag != tag || dpath != qpath){
 		/* if(chatty9p) */
@@ -72,7 +72,7 @@
 	if(chatty9p)
 		print("blkno %llud tag %s\n", blkno, tagnames[dtag]);
 	// print("%llud\n", blkno);
-	add(&useds, blkno, blklen(tag));
+	add(&useds, blkno, len);
 	return 1;
 }
 
@@ -79,38 +79,48 @@
 int
 checkdentry(u64 blkno, u8 tag, u64 qpath)
 {
-	u8 buf[Rawblocksize];
+	u8 buf[Metadatablocksize];
 	Dentry *d;
 
-	devread(blkno, buf, Dentryunits);
-	d = (Dentry*)buf;
-	return checkvalid(blkno, d->tag, d->path, tag, qpath);
+	devread(blkno, buf, Metadataunits);
+	recentmetadata(buf, &d, nil);
+	return checkvalid(blkno, d->tag, d->path, tag, qpath, Metadataunits);
 }
 
 void
 walkindir(u64 blkno, u16 tag, u16 bottomtag, u64 qpath)
 {
-	u8 buf[Rawblocksize], *cbuf;
-	Content *t, *ct;
+	u8 buf[Metadatablocksize], *cbuf;
+	Indirect *t;
+	Data *da;
 	u64 cblkno;
 	int i;
+	Dentry *cd;
 
-	devread(blkno, buf, blklen(tag));
-	t = (Content*)buf;
-	if(checkvalid(blkno, t->tag, t->path, tag, qpath)){
+	devread(blkno, buf, Metadataunits);
+	recentmetadata(buf, &t, nil);
+	if(checkvalid(blkno, t->tag, t->path, tag, qpath, Metadataunits)){
 		if(tag == Tind0){
 			for(i = 0; i<Nindperblock; i++){
 				cblkno = t->bufa[i];
 				if(cblkno == 0)
 					return;
-				cbuf = malloc(blklen(bottomtag)*Unit);
-				devread(cblkno, cbuf, blklen(bottomtag));
-				ct = (Content*)cbuf;
-				if(ct->tag == Tdentry && ct->tag == bottomtag)
-					walkdirectory(cblkno);
-				else
-					checkvalid(cblkno, ct->tag, ct->path, bottomtag, qpath);
-				free(cbuf);
+				if(bottomtag == Tdentry){
+					cbuf = malloc(Metadatablocksize);
+					devread(cblkno, cbuf, Metadataunits);
+					recentmetadata(cbuf, &cd, nil);
+					if((cd->mode & DMDIR) > 0)
+						walkdirectory(cblkno);
+					else
+						walkfile(cblkno);
+					free(cbuf);
+				}else{
+					cbuf = malloc(Maxdatablockunits*Blocksize);
+					devread(cblkno, cbuf, 1);
+					da = (Data*)cbuf;
+					checkvalid(cblkno, da->tag, da->path, Tdata, da->path, da->len);
+					free(cbuf);
+				}
 			}
 		}else{
 			for(i = 0; i<Nindperblock; i++){
@@ -127,25 +137,25 @@
 void
 walkdirectory(u64 blkno)
 {
-	u8 buf[Rawblocksize], *cbuf;
+	u8 buf[Metadatablocksize], *cbuf;
 	Dentry *d, *cd;
-	Content *ct;
+	Indirect *it;
 	u64 cblkno;
 	int i;
 
-	devread(blkno, buf, Dentryunits);
-	d = (Dentry*)buf;
+	devread(blkno, buf, Metadataunits);
+	recentmetadata(buf, &d, nil);
 	if(chatty9p)
-		print("walkdirectory %llud tag %s name %s d->qid.path %llud\n",
-				blkno, tagnames[d->tag], d->name, d->qid.path);
-	if(d->tag != Tdentry || d->path != d->qid.path){
+		print("walkdirectory %llud tag %s name %s d->path %llud\n",
+				blkno, tagnames[d->tag], d->name, 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",
-					blkno, tagnames[Tdentry], d->qid.path, tagnames[d->tag], d->path);
+					blkno, tagnames[Tdentry], d->qpath, tagnames[d->tag], d->path);
 		fprint(2, "%llud\n", blkno);
 	}else{
 		// print("%llud\n", blkno);
-		add(&useds, blkno, Dentryunits);
+		add(&useds, blkno, Metadataunits);
 	}
 	/* do not list the data blocks used by /adm/frees
 		as they are considered to be free blocks */
@@ -155,9 +165,9 @@
 		cblkno = d->dblocks[i];
 		if(cblkno == 0)
 			return;
-		cbuf = malloc(Dentrysize);
-		devread(cblkno, cbuf, Dentryunits);
-		cd = (Dentry*)cbuf;
+		cbuf = malloc(Metadatablocksize);
+		devread(cblkno, cbuf, Metadataunits);
+		recentmetadata(cbuf, &cd, nil);
 		if((cd->mode & DMDIR) > 0)
 			walkdirectory(cblkno);
 		else
@@ -164,15 +174,15 @@
 			walkfile(cblkno);
 		free(cbuf);
 	}
-	cbuf = malloc(Rawblocksize);
+	cbuf = malloc(Metadatablocksize);
 	for(i = 0; i<Niblock; i++){
 		cblkno = d->iblocks[i];
 		if(cblkno == 0)
 			return;
-		devread(cblkno, cbuf, Dentryunits);
-		ct = (Content*)cbuf;
-		if(ct->tag == Tind0+i){
-			walkindir(cblkno, Tind0+i, Tdentry, d->qid.path);
+		devread(cblkno, cbuf, Metadataunits);
+		recentmetadata(cbuf, &it, nil);
+		if(it->tag == Tind0+i){
+			walkindir(cblkno, Tind0+i, Tdentry, d->qpath);
 		}else{
 			fprint(2, "invalid indir tag %llud\n", cblkno);
 			fprint(2, "%llud\n", cblkno);
@@ -185,25 +195,26 @@
 void
 walkfile(u64 blkno)
 {
-	u8 buf[Rawblocksize], *cbuf;
+	u8 buf[Metadatablocksize], *cbuf;
 	Dentry *d;
-	Content *ct;
+	Data *ct;
+	Indirect *it;
 	u64 cblkno;
 	int i;
 
-	devread(blkno, buf, Dentryunits);
-	d = (Dentry*)buf;
+	devread(blkno, buf, Metadataunits);
+	recentmetadata(buf, &d, nil);
 	if(chatty9p)
 		print("walkfile %llud tag %s name %s d->qid.path %llud\n",
-				blkno, tagnames[d->tag], d->name, d->qid.path);
-	if(d->tag != Tdentry || d->path != d->qid.path){
+				blkno, tagnames[d->tag], d->name, 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",
-					blkno, tagnames[Tdentry], d->qid.path, tagnames[d->tag], d->path);
+					blkno, tagnames[Tdentry], d->qpath, tagnames[d->tag], d->path);
 		fprint(2, "%llud\n", blkno);
 	}else{
 		// print("%llud\n", blkno);
-		add(&useds, blkno, Dentryunits);
+		add(&useds, blkno, Metadataunits);
 	}
 	/* do not list the data blocks used by /adm/frees
 		as they are considered to be free blocks */
@@ -211,25 +222,28 @@
 		return;
 	if(d->size <= Ddatasize)
 		return;
+
+	cbuf = malloc(Maxdatablockunits*Blocksize);
 	for(i = 0; i<Ndblock; i++){
 		cblkno = d->dblocks[i];
-		if(cblkno == 0)
+		if(cblkno == 0){
+			free(cbuf);
 			return;
-		cbuf = malloc(Rawblocksize);
-		devread(cblkno, cbuf, Rawblockunits);
-		ct = (Content*)cbuf;
-		checkvalid(cblkno, ct->tag, ct->path, Tdata, d->qid.path);
-		free(cbuf);
+		}
+		devread(cblkno, cbuf, 1);
+		ct = (Data*)cbuf;
+		checkvalid(cblkno, ct->tag, ct->path, Tdata, d->qpath, ct->len);
 	}
-	cbuf = malloc(Rawblocksize);
 	for(i = 0; i<Niblock; i++){
 		cblkno = d->iblocks[i];
-		if(cblkno == 0)
+		if(cblkno == 0){
+			free(cbuf);
 			return;
-		devread(cblkno, cbuf, Rawblockunits);
-		ct = (Content*)cbuf;
-		if(ct->tag == Tind0+i){
-			walkindir(cblkno, Tind0+i, Tdata, d->qid.path);
+		}
+		devread(cblkno, cbuf, Metadataunits);
+		recentmetadata(buf, &it, nil);
+		if(it->tag == Tind0+i){
+			walkindir(cblkno, Tind0+i, Tdata, d->qpath);
 		}else{
 			fprint(2, "invalid indir tag %llud\n", cblkno);
 			fprint(2, "%llud\n", cblkno);
--- a/user.c
+++ b/user.c
@@ -423,10 +423,10 @@
 	User *us;
 	int rv;
 
-	ubuf = getbufchk(Bdusersinuse, Breadonly, Tdentry, Qpusersinuse);
-	usize = ubuf->d->size+1;
+	ubuf = getmetachk(Bdusersinuse, Breadonly, Tdentry, Qpusersinuse);
+	usize = ubuf->cur->size+1;
 	buf = emalloc9p(usize);
-	putbuf(ubuf);
+	putbuf(ubuf, 0);
 
 	readfile(Bdusersinuse, Qpusersinuse, buf, usize, 0, nil);
 	if((rv=parseusers(&us, buf, usize)) != 0)
@@ -451,10 +451,10 @@
 	u64 usize;
 	User *us, *old;
 
-	ubuf = getbufchk(Bdusersstaging, Breadonly, Tdentry, Qpusersstaging);
-	usize = ubuf->d->size+1;
+	ubuf = getmetachk(Bdusersstaging, Breadonly, Tdentry, Qpusersstaging);
+	usize = ubuf->cur->size+1;
 	buf = emalloc9p(usize);
-	putbuf(ubuf);
+	putbuf(ubuf, 0);
 
 	readfile(Bdusersstaging, Qpusersstaging, buf, usize, 0, nil);
 	if(parseusers(&us, buf, usize) != 0)