Commit Diff


commit - 333c1dccc2f9af67b9c3d8513cca492d022fab4f
commit + 3d77c87e81bf16aeaf52ba0f523af6708c5c4964
blob - fc34d6886d088e7a66cf8aab7e2b621fefc99519 (mode 644)
blob + /dev/null
--- src/cmd/vac/cache.c
+++ /dev/null
@@ -1,876 +0,0 @@
-#include "stdinc.h"
-#include "vac.h"
-#include "dat.h"
-#include "fns.h"
-
-typedef struct Label Label;
-
-enum {
-	BadHeap = ~0,
-};
-
-/*
- * the plan is to store data to the cache in c->size blocks
- * with the block zero extended to fill it out.  When writing to
- * venti, the block will be zero truncated.  The walker will also check
- * that the block fits within psize or dsize as the case may be.
- */
-
-struct Cache
-{
-	VtLock	*lk;
-	VtSession *z;
-	u32int	now;			/* ticks for usage timestamps */
-	int	size;			/* max. size of any block; allocated to each block */
-	Lump	**heads;		/* hash table for finding address */
-	int	nheap;			/* number of available victims */
-	Lump	**heap;			/* heap for locating victims */
-	long	nblocks;		/* number of blocks allocated */
-	Lump	*blocks;		/* array of block descriptors */
-	u8int	*mem;			/* memory for all block descriptors */
-	Lump	*free;			/* free list of lumps */
-
-	long hashSize;
-};
-
-/*
- * the tag for a block is hash(index, parent tag)
- */
-
-struct Label {
-	uchar gen[4];
-	uchar state;
-	uchar type;		/* top bit indicates it is part of a directory */
-	uchar tag[4];		/* tag of file it is in */
-};
-
-
-static char ENoDir[] = "directory entry is not allocated";
-
-static void fixHeap(int si, Lump *b);
-static int upHeap(int i, Lump *b);
-static int downHeap(int i, Lump *b);
-static char	*lumpState(int);
-static void	lumpSetState(Lump *u, int state);
-
-Cache *
-cacheAlloc(VtSession *z, int blockSize, long nblocks)
-{
-	int i;
-	Cache *c;
-	Lump *b;
-
-	c = vtMemAllocZ(sizeof(Cache));
-	
-	c->lk = vtLockAlloc();
-	c->z = z;
-	c->size = blockSize;
-	c->nblocks = nblocks;
-	c->hashSize = nblocks;
-	c->heads = vtMemAllocZ(c->hashSize*sizeof(Lump*));
-	c->heap = vtMemAllocZ(nblocks*sizeof(Lump*));
-	c->blocks = vtMemAllocZ(nblocks*sizeof(Lump));
-	c->mem = vtMemAllocZ(nblocks * blockSize);
-	for(i = 0; i < nblocks; i++){
-		b = &c->blocks[i];
-		b->lk = vtLockAlloc();
-		b->c = c;
-		b->data = &c->mem[i * blockSize];
-		b->addr = i+1;
-		b->state = LumpFree;
-		b->heap = BadHeap;
-		b->next = c->free;
-		c->free = b;
-	}
-	c->nheap = 0;
-
-	return c;
-}
-
-long
-cacheGetSize(Cache *c)
-{
-	return c->nblocks;
-}
-
-int
-cacheGetBlockSize(Cache *c)
-{
-	return c->size;
-}
-
-int
-cacheSetSize(Cache *c, long nblocks)
-{
-	USED(c);
-	USED(nblocks);
-	return 0;
-}
-
-void
-cacheFree(Cache *c)
-{
-	int i;
-
-	for(i = 0; i < c->nblocks; i++){
-		assert(c->blocks[i].ref == 0);
-		vtLockFree(c->blocks[i].lk);
-	}
-	vtMemFree(c->heads);
-	vtMemFree(c->blocks);
-	vtMemFree(c->mem);
-	vtMemFree(c);
-}
-
-static u32int
-hash(Cache *c, uchar score[VtScoreSize], int type)
-{
-	u32int h;
-	uchar *p = score + VtScoreSize-4;
-
-	h = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
-	h += type;
-	return h % c->hashSize;
-}
-
-static void
-findLump(Cache *c, Lump *bb)
-{
-	Lump *b, *last;
-	int h;
-
-	last = nil;
-	h = hash(c, bb->score, bb->type);
-	for(b = c->heads[h]; b != nil; b = b->next){
-		if(last != b->prev)
-			vtFatal("bad prev link");
-		if(b == bb)
-			return;
-		last = b;
-	}
-	vtFatal("block missing from hash table");
-}
-
-void
-cacheCheck(Cache *c)
-{
-	u32int size, now;
-	int i, k, refed, free;
-	static uchar zero[VtScoreSize];
-	Lump *p;
-
-	size = c->size;
-	now = c->now;
-
-	free = 0;
-	for(p=c->free; p; p=p->next)
-		free++;
-	for(i = 0; i < c->nheap; i++){
-		if(c->heap[i]->heap != i)
-			vtFatal("mis-heaped at %d: %d", i, c->heap[i]->heap);
-		if(i > 0 && c->heap[(i - 1) >> 1]->used2 - now > c->heap[i]->used2 - now)
-			vtFatal("bad heap ordering");
-		k = (i << 1) + 1;
-		if(k < c->nheap && c->heap[i]->used2 - now > c->heap[k]->used2 - now)
-			vtFatal("bad heap ordering");
-		k++;
-		if(k < c->nheap && c->heap[i]->used2 - now > c->heap[k]->used2 - now)
-			vtFatal("bad heap ordering");
-	}
-
-	refed = 0;
-	for(i = 0; i < c->nblocks; i++){
-		if(c->blocks[i].data != &c->mem[i * size])
-			vtFatal("mis-blocked at %d", i);
-		if(c->blocks[i].ref && c->blocks[i].heap == BadHeap){
-			refed++;
-		}
-		if(memcmp(zero, c->blocks[i].score, VtScoreSize))
-			findLump(c, &c->blocks[i]);
-	}
-if(refed > 0)fprint(2, "cacheCheck: nheap %d refed %d free %d\n", c->nheap, refed, free);
-	assert(c->nheap + refed + free == c->nblocks);
-	refed = 0;
-	for(i = 0; i < c->nblocks; i++){
-		if(c->blocks[i].ref) {
-if(1)fprint(2, "%d %V %d %s\n", c->blocks[i].type, c->blocks[i].score, c->blocks[i].ref, lumpState(c->blocks[i].state));
-			refed++;
-		}
-	}
-if(refed > 0)fprint(2, "cacheCheck: in used %d\n", refed);
-}
-
-/*
- * delete an arbitrary block from the heap
- */
-static void
-delHeap(Lump *db)
-{
-	fixHeap(db->heap, db->c->heap[--db->c->nheap]);
-	db->heap = BadHeap;
-}
-
-static void
-fixHeap(int si, Lump *b)
-{
-	int i;
-
-	i = upHeap(si, b);
-	if(i == si)
-		downHeap(i, b);
-}
-
-static int
-upHeap(int i, Lump *b)
-{
-	Lump *bb;
-	u32int now;
-	int p;
-	Cache *c;
-	
-	c = b->c;
-	now = c->now;
-	for(; i != 0; i = p){
-		p = (i - 1) >> 1;
-		bb = c->heap[p];
-		if(b->used2 - now >= bb->used2 - now)
-			break;
-		c->heap[i] = bb;
-		bb->heap = i;
-	}
-	c->heap[i] = b;
-	b->heap = i;
-
-	return i;
-}
-
-static int
-downHeap(int i, Lump *b)
-{
-	Lump *bb;
-	u32int now;
-	int k;
-	Cache *c;
-	
-	c = b->c;
-	now = c->now;
-	for(; ; i = k){
-		k = (i << 1) + 1;
-		if(k >= c->nheap)
-			break;
-		if(k + 1 < c->nheap && c->heap[k]->used2 - now > c->heap[k + 1]->used2 - now)
-			k++;
-		bb = c->heap[k];
-		if(b->used2 - now <= bb->used2 - now)
-			break;
-		c->heap[i] = bb;
-		bb->heap = i;
-	}
-	c->heap[i] = b;
-	b->heap = i;
-	return i;
-}
-
-
-/* called with c->lk held */
-Lump *
-cacheBumpLump(Cache *c)
-{
-	Lump *b;
-
-	/*
-	 * missed: locate the block with the oldest second to last use.
-	 * remove it from the heap, and fix up the heap.
-	 */
-	if(c->free) {
-		b = c->free;
-		c->free = b->next;
-	} else {
-		for(;;){
-			if(c->nheap == 0) {
-				cacheCheck(c);
-				assert(0);
-				return nil;
-			}
-			b = c->heap[0];
-			delHeap(b);
-			if(b->ref == 0)
-				break;
-		}
-
-		/*
-		 * unchain the block from hash chain
-		 */
-		if(b->prev == nil)
-			c->heads[hash(c, b->score, b->type)] = b->next;
-		else
-			b->prev->next = b->next;
-		if(b->next != nil)
-			b->next->prev = b->prev;
-
-	}
-
-	/*
-	 * the new block has no last use, so assume it happens sometime in the middle
-	 */
-	b->used = (b->used2 + c->now) / 2;
-	b->asize = 0;
-
-	return b;
-}
-
-Lump *
-cacheAllocLump(Cache *c, int type, int size, int dir)
-{
-	Lump *b;
-	ulong h;
-
-	assert(size <= c->size);
-
-again:
-	vtLock(c->lk);
-	b = cacheBumpLump(c);
-	if(b == nil) {
-		vtUnlock(c->lk);
-fprint(2, "cache is full\n");
-		/* XXX should be better */
-		sleep(100);
-		goto again;
-	}
-
-	vtLock(b->lk);
-
-	assert(b->ref == 0);
-	b->ref++;
-	b->used2 = b->used;
-	b->used = c->now++;
-
-	/* convert addr into score */
-	memset(b->score, 0, VtScoreSize-4);
-	b->score[VtScoreSize-4] = b->addr>>24;
-	b->score[VtScoreSize-3] = b->addr>>16;
-	b->score[VtScoreSize-2] = b->addr>>8;
-	b->score[VtScoreSize-1] = b->addr;
-	
-	b->dir = dir;
-	b->type = type;
-	b->gen = 0;
-	b->asize = size;
-	b->state = LumpFree;
-
-	h = hash(c, b->score, b->type);
-
-	/* chain onto correct hash */
-	b->next = c->heads[h];
-	c->heads[h] = b;
-	if(b->next != nil)
-		b->next->prev = b;
-	b->prev = nil;
-
-	vtUnlock(c->lk);
-
-	vtZeroExtend(type, b->data, 0, size);
-	lumpSetState(b, LumpActive);
-
-	return b;
-}
-
-int
-scoreIsLocal(uchar score[VtScoreSize])
-{
-	static uchar zero[VtScoreSize];
-	
-	return memcmp(score, zero, VtScoreSize-4) == 0;
-}
-
-Lump *
-cacheGetLump(Cache *c, uchar score[VtScoreSize], int type, int size)
-{
-	Lump *b;
-	ulong h;
-	int n;
-	static uchar zero[VtScoreSize];
-
-	assert(size <= c->size);
-
-	h = hash(c, score, type);
-
-again:
-	/*
-	 * look for the block in the cache
-	 */
-	vtLock(c->lk);
-	for(b = c->heads[h]; b != nil; b = b->next){
-		if(memcmp(b->score, score, VtScoreSize) == 0 && b->type == type)
-			goto found;
-	}
-
-	/* should not be looking for a temp block */
-	if(scoreIsLocal(score)) {
-		if(memcmp(score, zero, VtScoreSize) == 0)
-			vtSetError("looking for zero score");
-		else
-			vtSetError("missing local block");
-		vtUnlock(c->lk);
-		return nil;
-	}
-
-	b = cacheBumpLump(c);
-	if(b == nil) {
-		vtUnlock(c->lk);
-		sleep(100);
-		goto again;
-	}
-
-	/* chain onto correct hash */
-	b->next = c->heads[h];
-	c->heads[h] = b;
-	if(b->next != nil)
-		b->next->prev = b;
-	b->prev = nil;
-
-	memmove(b->score, score, VtScoreSize);	
-	b->type = type;
-	b->state = LumpFree;
-
-found:
-	b->ref++;
-	b->used2 = b->used;
-	b->used = c->now++;
-	if(b->heap != BadHeap)
-		fixHeap(b->heap, b);
-
-	vtUnlock(c->lk);
-
-	vtLock(b->lk);
-	if(b->state != LumpFree)
-		return b;
-	
-	n = vtRead(c->z, score, type, b->data, size);
-	if(n < 0) {
-		lumpDecRef(b, 1);
-		return nil;
-	}
-	if(!vtSha1Check(score, b->data, n)) {
-		vtSetError("vtSha1Check failed");
-		lumpDecRef(b, 1);
-		return nil;
-	}
-	vtZeroExtend(type, b->data, n, size);
-	b->asize = size;
-	lumpSetState(b, LumpVenti);
-
-	return b;
-}
-
-static char *
-lumpState(int state)
-{
-	switch(state) {
-	default:
-		return "Unknown!!";
-	case LumpFree:
-		return "Free";
-	case LumpActive:
-		return "Active";
-	case LumpSnap:
-		return "Snap";
-	case LumpZombie:
-		return "Zombie";
-	case LumpVenti:
-		return "Venti";
-	}
-}
-
-static void
-lumpSetState(Lump *u, int state)
-{
-//	if(u->state != LumpFree)
-//		fprint(2, "%V: %s -> %s\n", u->score, lumpState(u->state), lumpState(state));
-	u->state = state;
-}
-	
-int
-lumpGetScore(Lump *u, int offset, uchar score[VtScoreSize])
-{
-	uchar *sp;
-	VtRoot root;
-	VtEntry dir;
-
-	vtLock(u->lk);
-
-	switch(u->type) {
-	default:
-		vtSetError("bad type");
-		goto Err;
-	case VtPointerType0:
-	case VtPointerType1:
-	case VtPointerType2:
-	case VtPointerType3:
-	case VtPointerType4:
-	case VtPointerType5:
-	case VtPointerType6:
-		if((offset+1)*VtScoreSize > u->asize)
-			sp = nil;
-		else
-			sp = u->data + offset*VtScoreSize;
-		break;
-	case VtRootType:
-		if(u->asize < VtRootSize) {
-			vtSetError("runt root block");
-			goto Err;
-		}
-		if(!vtRootUnpack(&root, u->data))
-			goto Err;
-		sp = root.score;
-		break;
-	case VtDirType:
-		if((offset+1)*VtEntrySize > u->asize) {
-			vtSetError(ENoDir);
-			goto Err;
-		}
-		if(!vtEntryUnpack(&dir, u->data, offset))
-			goto Err;
-		if(!dir.flags & VtEntryActive) {
-			vtSetError(ENoDir);
-			goto Err;
-		}
-		sp = dir.score;
-		break;
-	}
-
-	if(sp == nil)
-		memmove(score, vtZeroScore, VtScoreSize);
-	else
-		memmove(score, sp, VtScoreSize);
-
-	vtUnlock(u->lk);
-	return !scoreIsLocal(score);
-Err:
-	vtUnlock(u->lk);
-	return 0;
-}
-
-Lump *
-lumpWalk(Lump *u, int offset, int type, int size, int readOnly, int lock)
-{
-	Lump *v, *vv;
-	Cache *c;
-	uchar score[VtScoreSize], *sp;
-	VtRoot root;
-	VtEntry dir;
-	int split, isdir;
-
-	c = u->c;
-	vtLock(u->lk);
-
-Again:
-	v = nil;
-	vv = nil;
-
-	isdir = u->dir;
-	switch(u->type) {
-	default:
-		vtSetError("bad type");
-		goto Err;
-	case VtPointerType0:
-	case VtPointerType1:
-	case VtPointerType2:
-	case VtPointerType3:
-	case VtPointerType4:
-	case VtPointerType5:
-	case VtPointerType6:
-		if((offset+1)*VtScoreSize > u->asize)
-			sp = nil;
-		else
-			sp = u->data + offset*VtScoreSize;
-		break;
-	case VtRootType:
-		if(u->asize < VtRootSize) {
-			vtSetError("runt root block");
-			goto Err;
-		}
-		if(!vtRootUnpack(&root, u->data))
-			goto Err;
-		sp = root.score;
-		break;
-	case VtDirType:
-		if((offset+1)*VtEntrySize > u->asize) {
-			vtSetError(ENoDir);
-			goto Err;
-		}
-		if(!vtEntryUnpack(&dir, u->data, offset))
-			goto Err;
-		if(!(dir.flags & VtEntryActive)) {
-			vtSetError(ENoDir);
-			goto Err;
-		}
-		isdir = (dir.flags & VtEntryDir) != 0;
-//		sp = dir.score;
-		sp = u->data + offset*VtEntrySize + 20;
-		break;
-	}
-
-	if(sp == nil)
-		memmove(score, vtZeroScore, VtScoreSize);
-	else
-		memmove(score, sp, VtScoreSize);
-	
-	vtUnlock(u->lk);
-
-
-if(0)fprint(2, "lumpWalk: %V:%s %d:%d-> %V:%d\n", u->score, lumpState(u->state), u->type, offset, score, type);
-	v = cacheGetLump(c, score, type, size);
-	if(v == nil)
-		return nil;
-
-	split = 1;
-	if(readOnly)
-		split = 0;
-
-	switch(v->state) {
-	default:
-		assert(0);
-	case LumpFree:
-fprint(2, "block is free %V!\n", v->score);
-		vtSetError("phase error");
-		goto Err2;
-	case LumpActive:	
-		if(v->gen < u->gen) {
-print("LumpActive gen\n");
-			lumpSetState(v, LumpSnap);
-			v->gen = u->gen;
-		} else
-			split = 0;
-		break;
-	case LumpSnap:
-	case LumpVenti:
-		break;
-	}
-	
-	/* easy case */
-	if(!split) {
-		if(!lock)
-			vtUnlock(v->lk);
-		return v;
-	}
-
-	if(sp == nil) {
-		vtSetError("bad offset");
-		goto Err2;
-	}
-
-	vv = cacheAllocLump(c, v->type, size, isdir);
-	/* vv is locked */
-	vv->gen = u->gen;
-	memmove(vv->data, v->data, v->asize);
-if(0)fprint(2, "split %V into %V\n", v->score, vv->score);
-
-	lumpDecRef(v, 1);
-	v = nil;
-
-	vtLock(u->lk);
-	if(u->state != LumpActive) {
-		vtSetError("bad parent state: can not happen");
-		goto Err;
-	}
-
-	/* check that nothing changed underfoot */
-	if(memcmp(sp, score, VtScoreSize) != 0) {
-		lumpDecRef(vv, 1);
-fprint(2, "lumpWalk: parent changed under foot\n");
-		goto Again;
-	}
-
-	/* XXX - hold Active blocks up - will go eventually */
-	lumpIncRef(vv);
-
-	/* change the parent */
-	memmove(sp, vv->score, VtScoreSize);
-	
-	vtUnlock(u->lk);
-	
-	if(!lock)
-		vtUnlock(vv->lk);
-	return vv;
-Err:
-	vtUnlock(u->lk);
-	lumpDecRef(v, 0);
-	lumpDecRef(vv, 1);
-	return nil;
-Err2:
-	lumpDecRef(v, 1);
-	return nil;
-	
-}
-
-void
-lumpFreeEntry(Lump *u, int entry)
-{
-	uchar score[VtScoreSize];
-	int type;
-	ulong gen;
-	VtEntry dir;
-	Cache *c;
-
-	c = u->c;
-	vtLock(u->lk);
-	if(u->state == LumpVenti)
-		goto Exit;
-
-	switch(u->type) {
-	default:
-		fprint(2, "freeing bad lump type: %d\n", u->type);
-		return;
-	case VtPointerType0:
-		if((entry+1)*VtScoreSize > u->asize)
-			goto Exit;
-		memmove(score, u->data + entry*VtScoreSize, VtScoreSize);
-		memmove(u->data + entry*VtScoreSize, vtZeroScore, VtScoreSize);
-		type = u->dir?VtDirType:VtDataType;
-		break;
-	case VtPointerType1:
-	case VtPointerType2:
-	case VtPointerType3:
-	case VtPointerType4:
-	case VtPointerType5:
-	case VtPointerType6:
-		if((entry+1)*VtScoreSize > u->asize)
-			goto Exit;
-		memmove(score, u->data + entry*VtScoreSize, VtScoreSize);
-		memmove(u->data + entry*VtScoreSize, vtZeroScore, VtScoreSize);
-		type = u->type-1;
-		break;
-	case VtDirType:
-		if((entry+1)*VtEntrySize > u->asize)
-			goto Exit;
-		if(!vtEntryUnpack(&dir, u->data, entry))
-			goto Exit;
-		if(!dir.flags & VtEntryActive)
-			goto Exit;
-		gen = dir.gen;
-		if(gen != ~0)
-			gen++;
-		if(dir.depth == 0)
-			type = (dir.flags&VtEntryDir)?VtDirType:VtDataType;
-		else
-			type = VtPointerType0 + dir.depth - 1;
-		memmove(score, dir.score, VtScoreSize);
-		memset(&dir, 0, sizeof(dir));
-		dir.gen = gen;
-		vtEntryPack(&dir, u->data, entry);
-		break;
-	case VtDataType:
-		type = VtErrType;
-		break;
-	}
-	vtUnlock(u->lk);
-	if(type == VtErrType || !scoreIsLocal(score))
-		return;
-
-	u = cacheGetLump(c, score, type, c->size);
-	if(u == nil)
-		return;
-	lumpDecRef(u, 1);
-	/* XXX remove extra reference */
-	lumpDecRef(u, 0);
-	return;
-Exit:
-	vtUnlock(u->lk);
-	return;
-
-}
-
-void
-lumpCleanup(Lump *u)
-{
-	int i, n;
-
-	switch(u->type) {
-	default:
-		return;
-	case VtPointerType0:
-	case VtPointerType1:
-	case VtPointerType2:
-	case VtPointerType3:
-	case VtPointerType4:
-	case VtPointerType5:
-	case VtPointerType6:
-		n = u->asize/VtScoreSize;
-		break;	
-	case VtDirType:
-		n = u->asize/VtEntrySize;
-		break;
-	}
-
-	for(i=0; i<n; i++)
-		lumpFreeEntry(u, i);
-}
-
-
-void
-lumpDecRef(Lump *b, int unlock)
-{
-	int i;
-	Cache *c;
-
-	if(b == nil)
-		return;
-
-	if(unlock)
-		vtUnlock(b->lk);
-
-	c = b->c;
-	vtLock(c->lk);
-	if(--b->ref > 0) {
-		vtUnlock(c->lk);
-		return;
-	}
-	assert(b->ref == 0);
-
-	switch(b->state) {
-	default:
-		fprint(2, "bad state: %s\n", lumpState(b->state));
-		assert(0);
-	case LumpActive:
-		/* hack - but will do for now */
-		b->ref++;
-		vtUnlock(c->lk);
-		lumpCleanup(b);
-		vtLock(c->lk);
-		b->ref--;
-		lumpSetState(b, LumpFree);
-		break;
-	case LumpZombie:
-		lumpSetState(b, LumpFree);
-		break;
-	case LumpFree:
-	case LumpVenti:
-		break;
-	}
-
-	/*
-	 * reinsert in the free heap
-	 */
-	if(b->heap == BadHeap) {
-		i = upHeap(c->nheap++, b);
-		c->heap[i] = b;
-		b->heap = i;
-	}
-
-	vtUnlock(c->lk);
-}
-
-Lump *
-lumpIncRef(Lump *b)
-{
-	Cache *c;
-
-	c = b->c;
-
-	vtLock(c->lk);
-	assert(b->ref > 0);
-	b->ref++;
-	vtUnlock(c->lk);
-	return b;
-}
blob - a468668d612e95e0aa9d9754c31d82457f7d34db
blob + ec15db4a9fc0236c1a6c5c3dbe0b0e6bfa242c93
--- src/cmd/vac/dat.h
+++ src/cmd/vac/dat.h
@@ -1,60 +1,27 @@
-typedef struct Source Source;
-typedef struct VacFile VacFile;
 typedef struct MetaBlock MetaBlock;
 typedef struct MetaEntry MetaEntry;
-typedef struct Lump Lump;
-typedef struct Cache Cache;
-typedef struct Super Super;
 
-enum {
-	NilBlock	= (~0UL),
+enum
+{
 	MaxBlock	= (1UL<<31),
 };
 
-
-struct VacFS {
-	int ref;
-	
-	/* need a read write lock? */
-
-	uchar score[VtScoreSize];
-	VacFile *root;
-	
-	VtSession *z;
-	int readOnly;
-	int bsize;		/* maximum block size */
-	uvlong qid;		/* next qid */
-	Cache *cache;
+enum {
+	BytesPerEntry = 100,	/* estimate of bytes per dir entries - determines number of index entries in the block */
+	FullPercentage = 80,	/* don't allocate in block if more than this percentage full */
+	FlushSize = 200,	/* number of blocks to flush */
+	DirtyPercentage = 50,	/* maximum percentage of dirty blocks */
 };
 
 
-struct Source {
-	VtLock *lk;
-
-	Cache *cache;	/* immutable */
-	int readOnly;	/* immutable */
-
-	Lump *lump;	/* lump containing venti dir entry */
-	ulong block;	/* block number within parent: immutable */
-	int entry;	/* which entry in the block: immutable */
-
-	/* most of a VtEntry, except the score */
-	ulong gen;	/* generation: immutable */
-	int dir;	/* dir flags: immutable */
-	int depth;	/* number of levels of pointer blocks */
-	int psize;	/* pointer block size: immutable */
-	int dsize;	/* data block size: immutable */
-	uvlong size;	/* size in bytes of file */
-
-	int epb;	/* dir entries per block = dize/VtEntrySize: immutable */
-};
-
-struct MetaEntry {
+struct MetaEntry
+{
 	uchar *p;
 	ushort size;
 };
 
-struct MetaBlock {
+struct MetaBlock
+{
 	int maxsize;		/* size of block */
 	int size;		/* size used */
 	int free;		/* free space within used size */
@@ -64,93 +31,13 @@ struct MetaBlock {
 	uchar *buf;
 };
 
-/*
- * contains a one block buffer
- * to avoid problems of the block changing underfoot
- * and to enable an interface that supports unget.
- */
-struct VacDirEnum {
+struct VacDirEnum
+{
 	VacFile *file;
-	
-	ulong block;	/* current block */
-	MetaBlock mb;	/* parsed version of block */
-	int index;	/* index in block */
+	u32int boff;
+	int i, n;
+	VacDir *buf;
 };
 
-/* Lump states */
-enum {
-	LumpFree,
-	LumpVenti,	/* on venti server: score > 2^32: just a cached copy */
-	LumpActive,	/* active */
-	LumpActiveRO,	/* active: read only block */
-	LumpActiveA,	/* active: achrived */
-	LumpSnap,	/* snapshot: */
-	LumpSnapRO,	/* snapshot: read only */
-	LumpSnapA,	/* snapshot: achived */
-	LumpZombie,	/* block with no pointer to it: waiting to be freed */
-	
-	LumpMax
-};
-
-/*
- * Each lump has a state and generation
- * The following invariants are maintained
- * 	Each lump has no more than than one parent per generation
- * 	For Active*, no child has a parent of a greater generation
- *	For Snap*, there is a snap parent of given generation and there are
- *		no parents of greater gen - implies no children of a greater gen
- *	For *RO, the lump is fixed - no change ca be made - all pointers
- *		are valid venti addresses
- *	For *A, the lump is on the venti server
- *	There are no pointers to Zombie lumps
- *
- * Transitions
- *	Archiver at generation g
- *	Mutator at generation h
- *	
- *	Want to modify a lump
- *		Venti: create new Active(h)
- *		Active(x): x == h: do nothing
- *		Acitve(x): x < h: change to Snap(h-1) + add Active(h)
- *		ActiveRO(x): change to SnapRO(h-1) + add Active(h)
- *		ActiveA(x): add Active(h)
- *		Snap*(x): should not occur
- *		Zombie(x): should not occur
- *	Want to archive
- *		Active(x): x != g: should never happen
- *		Active(x): x == g fix children and free them: move to ActoveRO(g);
- *		ActiveRO(x): x != g: should never happen
- *		ActiveRO(x): x == g: wait until it hits ActiveA or SnapA
- *		ActiveA(x): done
- *		Active(x): x < g: should never happen
- *		Snap(x): x >= g: fix children, freeing all SnapA(y) x == y;
- *		SnapRO(x): wait until it hits SnapA
- *
- */
-
-
-struct Lump {
-	int ref;
-
-	Cache *c;
-
-	VtLock *lk;
-	
-	int state;
-	ulong gen;
-
-	uchar 	*data;
-	uchar	score[VtScoreSize];	/* score of packet */
-	uchar	vscore[VtScoreSize];	/* venti score - when archived */
-	u8int	type;			/* type of packet */
-	int	dir;			/* part of a directory - extension of type */
-	u16int	asize;			/* allocated size of block */
-	Lump	*next;			/* doubly linked hash chains */
-	Lump	*prev;
-	u32int	heap;			/* index in heap table */
-	u32int	used;			/* last reference times */
-	u32int	used2;
-
-	u32int	addr;			/* mutable block address */
-};
-
+void _mbinit(MetaBlock*, u8int*, uint, uint);
+int _mbsearch(MetaBlock*, char*, int*, MetaEntry*);
blob - 5c8ff29d8e95e6214892cb88d31739ba4c2774e9
blob + f28f93760b0f6ddfafa72b904dac4b2462d169f9
--- src/cmd/vac/error.c
+++ src/cmd/vac/error.c
@@ -5,6 +5,8 @@
 #include "error.h"
 
 char ENoDir[] = "directory entry is not allocated";
+char ENoFile[] = "no such file or directory";
+char EBadPath[] = "bad path";
 char EBadDir[] = "corrupted directory entry";
 char EBadMeta[] = "corrupted meta data";
 char ENotDir[] = "not a directory";
blob - 742228cfb3c31957a9b1b80d11f3396853be1ea9
blob + bbb97373e3d80e86df15f31a86662f1518b48881
--- src/cmd/vac/error.h
+++ src/cmd/vac/error.h
@@ -12,3 +12,5 @@ extern char ERemoved[];
 extern char ENotEmpty[];
 extern char EExists[];
 extern char ERoot[];
+extern char ENoFile[];
+extern char EBadPath[];
blob - 900422d2a633c09d782df7343c662528809ead05
blob + cdcfd50aaf7f60945b2bc80aac90f65c34f79a11
--- src/cmd/vac/file.c
+++ src/cmd/vac/file.c
@@ -8,382 +8,150 @@
  * locking order is upwards.  A thread can hold the lock for a VacFile
  * and then acquire the lock of its parent
  */
+struct VacFile
+{
+	VacFs	*fs;	/* immutable */
 
-struct VacFile {
 	/* meta data for file: protected by the lk in the parent */
-	int ref;		/* holds this data structure up */
-	VacFS *fs;		/* immutable */
+	int		ref;	/* holds this data structure up */
 
-	int	removed;	/* file has been removed */
-	int	dirty;		/* dir is dirty with respect to meta data in block */
-	ulong	block;		/* block offset withing msource for this file's meta data */
+	int		partial;	/* file was never really open */
+	int		removed;	/* file has been removed */
+	int		dirty;	/* dir is dirty with respect to meta data in block */
+	u32int	boff;		/* block offset within msource for this file's metadata */
+	VacDir	dir;		/* metadata for this file */
+	VacFile	*up;		/* parent file */
+	VacFile	*next;	/* sibling */
 
-	VacDir dir;		/* meta data for this file */
-
-	VacFile *up;	/* parent file */
-	VacFile *next;	/* sibling */
-
-	/* data for file */
-	VtLock *lk;		/* lock for source and msource */
-	Source *source;
-	Source *msource;	/* for directories: meta data for children */
-	VacFile *down;		/* children */
+	RWLock	lk;		/* lock for the following */
+	VtFile	*source;	/* actual data */
+	VtFile	*msource;	/* metadata for children in a directory */
+	VacFile	*down;	/* children */
+	int		mode;
 };
 
-static int vfMetaFlush(VacFile*);
-static ulong msAlloc(Source *ms, ulong, int n);
+static int		filelock(VacFile*);
+static u32int	filemetaalloc(VacFile*, VacDir*, u32int);
+static int		filemetaflush2(VacFile*, char*);
+static void		filemetalock(VacFile*);
+static void		filemetaunlock(VacFile*);
+static void		fileraccess(VacFile*);
+static int		filerlock(VacFile*);
+static void		filerunlock(VacFile*);
+static void		fileunlock(VacFile*);
+static void		filewaccess(VacFile*, char*);
 
-static void
-vfRUnlock(VacFile *vf)
-{
-	vtRUnlock(vf->lk);
-}
-	
+void mbinit(MetaBlock*, u8int*, uint, uint);
+int mbsearch(MetaBlock*, char*, int*, MetaEntry*);
+int mbresize(MetaBlock*, MetaEntry*, int);
+VacFile *vdlookup(VacFile*, char*);
 
-static int
-vfRLock(VacFile *vf)
+static VacFile*
+filealloc(VacFs *fs)
 {
-	vtRLock(vf->lk);
-	if(vf->source == nil) {
-		vfRUnlock(vf);
-		vtSetError(ERemoved);
-		return 0;
-	}
-	return 1;
-}
+	VacFile *f;
 
-static void
-vfUnlock(VacFile *vf)
-{
-	vtUnlock(vf->lk);
+	f = vtmallocz(sizeof(VacFile));
+	f->ref = 1;
+	f->fs = fs;
+	f->boff = NilBlock;
+	f->mode = fs->mode;
+	return f;
 }
 
-static int
-vfLock(VacFile *vf)
-{
-	vtLock(vf->lk);
-	if(vf->source == nil) {
-		vfUnlock(vf);
-		vtSetError(ERemoved);
-		return 0;
-	}
-	return 1;
-}
-
 static void
-vfMetaLock(VacFile *vf)
+filefree(VacFile *f)
 {
-	assert(vf->up->msource != nil);
-	vtLock(vf->up->lk);
+	vtfileclose(f->source);
+	vtfileclose(f->msource);
+	vdcleanup(&f->dir);
+	memset(f, ~0, sizeof *f);	/* paranoia */
+	vtfree(f);
 }
 
-static void
-vfMetaUnlock(VacFile *vf)
+/*
+ * the file is locked already
+ * f->msource is unlocked
+ */
+static VacFile*
+dirlookup(VacFile *f, char *elem)
 {
-	vtUnlock(vf->up->lk);
-}
-
-
-static void
-vfRAccess(VacFile* vf)
-{
-	vfMetaLock(vf);
-	vf->dir.atime = time(0L);
-	vf->dirty = 1;
-	vfMetaUnlock(vf);
-	vfMetaFlush(vf);
-}
-
-static void
-vfWAccess(VacFile* vf, char *mid)
-{
-	vfMetaLock(vf);
-	vf->dir.atime = vf->dir.mtime = time(0L);
-	if(strcmp(vf->dir.mid, mid) != 0) {
-		vtMemFree(vf->dir.mid);
-		vf->dir.mid = vtStrDup(mid);
-	}
-	vf->dir.mcount++;
-	vf->dirty = 1;
-	vfMetaUnlock(vf);
-	vfMetaFlush(vf);
-}
-
-void
-vdCleanup(VacDir *dir)
-{
-	vtMemFree(dir->elem);
-	dir->elem = nil;
-	vtMemFree(dir->uid);
-	dir->uid = nil;
-	vtMemFree(dir->gid);
-	dir->gid = nil;
-	vtMemFree(dir->mid);
-	dir->mid = nil;
-}
-
-void
-vdCopy(VacDir *dst, VacDir *src)
-{
-	*dst = *src;
-	dst->elem = vtStrDup(src->elem);
-	dst->uid = vtStrDup(src->uid);
-	dst->gid = vtStrDup(src->gid);
-	dst->mid = vtStrDup(src->mid);
-}
-
-static int
-mbSearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me)
-{
 	int i;
-	int b, t, x;
-
-	/* binary search within block */
-	b = 0;
-	t = mb->nindex;
-	while(b < t) {
-		i = (b+t)>>1;
-		if(!meUnpack(me, mb, i))
-			return 0;
-		if(mb->unbotch)
-			x = meCmpNew(me, elem);
-		else
-			x = meCmp(me, elem);
-
-		if(x == 0) {
-			*ri = i;
-			return 1;
-		}
-	
-		if(x < 0)
-			b = i+1;
-		else /* x > 0 */
-			t = i;
-	}
-
-	assert(b == t);
-	
-	*ri = b;	/* b is the index to insert this entry */
-	memset(me, 0, sizeof(*me));
-
-	return 1;
-}
-
-static void
-mbInit(MetaBlock *mb, uchar *p, int n)
-{
-	memset(mb, 0, sizeof(MetaBlock));
-	mb->maxsize = n;
-	mb->buf = p;
-	mb->maxindex = n/100;
-	mb->size = MetaHeaderSize + mb->maxindex*MetaIndexSize;
-}
-
-static int
-vfMetaFlush(VacFile *vf)
-{
-	VacFile *vfp;
-	Lump *u;
 	MetaBlock mb;
-	MetaEntry me, nme;
-	uchar *p;
-	int i, n, moved;
+	MetaEntry me;
+	VtBlock *b;
+	VtFile *meta;
+	VacFile *ff;
+	u32int bo, nb;
 
-//print("vfMetaFlush %s\n", vf->dir.elem);
-
-	/* assume name has not changed for the moment */
-
-	vfMetaLock(vf);
-
-	vfp = vf->up;
-	moved = 0;
-
-	u = sourceGetLump(vfp->msource, vf->block, 0, 1);
-	if(u == nil)
-		goto Err;
-
-	if(!mbUnpack(&mb, u->data, u->asize))
-		goto Err;
-	if(!mbSearch(&mb, vf->dir.elem, &i, &me) || me.p == nil)
-		goto Err;
-
-	nme = me;
-	n = vdSize(&vf->dir);
-//print("old size %d new size %d\n", me.size, n);
-	if(n <= nme.size) {
-		nme.size = n;
-	} else {
-		/* try expand entry? */
-		p = mbAlloc(&mb, n);
-//print("alloced %ld\n", p - mb.buf);
-		if(p == nil) {
-assert(0);
-			/* much more work */
+	meta = f->msource;
+	b = nil;
+	if(vtfilelock(meta, -1) < 0)
+		return nil;
+	nb = (vtfilegetsize(meta)+meta->dsize-1)/meta->dsize;
+	for(bo=0; bo<nb; bo++){
+		b = vtfileblock(meta, bo, VtOREAD);
+		if(b == nil)
+			goto Err;
+		if(mbunpack(&mb, b->data, meta->dsize) < 0)
+			goto Err;
+		if(mbsearch(&mb, elem, &i, &me) < 0){
+			ff = filealloc(f->fs);
+			if(vdunpack(&ff->dir, &me) < 0){
+				filefree(ff);
+				goto Err;
+			}
+			vtfileunlock(meta);
+			vtblockput(b);
+			ff->boff = bo;
+			ff->mode = f->mode;
+			return ff;
 		}
-		nme.p = p;
-		nme.size = n;
-	}
 
-	mbDelete(&mb, i, &me);
-	memset(me.p, 0, me.size);
-	if(!moved) {
-		vdPack(&vf->dir, &nme);
-		mbInsert(&mb, i, &nme);
+		vtblockput(b);
+		b = nil;
 	}
-
-	mbPack(&mb);
-	lumpDecRef(u, 1);
-
-	vf->dirty = 0;
-
-	vfMetaUnlock(vf);
-	return 1;
-
-Err:
-	lumpDecRef(u, 1);
-	vfMetaUnlock(vf);
-	return 0;
-}
-
-static VacFile *
-vfAlloc(VacFS *fs)
-{
-	VacFile *vf;
-
-	vf = vtMemAllocZ(sizeof(VacFile));
-	vf->lk = vtLockAlloc();
-	vf->ref = 1;
-	vf->fs = fs;
-	return vf;
-}
-
-static void
-vfFree(VacFile *vf)
-{
-	sourceFree(vf->source);
-	vtLockFree(vf->lk);	
-	sourceFree(vf->msource);
-	vdCleanup(&vf->dir);
-	
-	vtMemFree(vf);
-}
-
-/* the file is locked already */
-static VacFile *
-dirLookup(VacFile *vf, char *elem)
-{
-	int i, j, nb;
-	MetaBlock mb;
-	MetaEntry me;
-	Lump *u;
-	Source *meta;
-	VacFile *nvf;
-
-	meta = vf->msource;
-	u = nil;
-	nb = sourceGetNumBlocks(meta);
-	for(i=0; i<nb; i++) {
-		u = sourceGetLump(meta, i, 1, 1);
-		if(u == nil)
-			goto Err;
-		if(!mbUnpack(&mb, u->data, u->asize))
-			goto Err;
-		if(!mbSearch(&mb, elem, &j, &me))
-			goto Err;
-		if(me.p != nil) {
-			nvf = vfAlloc(vf->fs);
-			if(!vdUnpack(&nvf->dir, &me)) {
-				vfFree(nvf);
-				goto Err;
-			}
-			lumpDecRef(u, 1);
-			nvf->block = i;
-			return nvf;
-		}
-		
-		lumpDecRef(u, 1);
-		u = nil;
-	}
-	vtSetError("file does not exist");
+	werrstr(ENoFile);
 	/* fall through */
 Err:
-	lumpDecRef(u, 1);
+	vtfileunlock(meta);
+	vtblockput(b);
 	return nil;
 }
 
-VacFile *
-vfRoot(VacFS *fs, uchar *score)
+VacFile*
+_vacfileroot(VacFs *fs, VtFile *r)
 {
-	VtEntry e;
-	Lump *u, *v;
-	Source *r, *r0, *r1, *r2;
+	VtBlock *b;
+	VtFile *r0, *r1, *r2;
 	MetaBlock mb;
 	MetaEntry me;
 	VacFile *root, *mr;
 
+	b = nil;
 	root = nil;
 	mr = nil;
-	r0 = nil;
 	r1 = nil;
 	r2 = nil;
-	v = nil;
-	r = nil;
 
-	u = cacheGetLump(fs->cache, score, VtDirType, fs->bsize);
-	if(u == nil)
+	if(vtfilelock(r, -1) < 0)
+		return nil;
+	r0 = vtfileopen(r, 0, fs->mode);
+	if(r0 == nil)
 		goto Err;
-	if(!fs->readOnly) {
-		v = cacheAllocLump(fs->cache, VtDirType, fs->bsize, 1);
-		if(v == nil) {
-			vtUnlock(u->lk);
-			goto Err;
-		}
-		v->gen = u->gen;
-		v->asize = u->asize;
-		v->state = LumpActive;
-		memmove(v->data, u->data, v->asize);
-		lumpDecRef(u, 1);
-		u = v;
-		v = nil;
-	}
-	vtUnlock(u->lk);
-	vtEntryUnpack(&e, u->data, 2);
-	if(e.flags == 0){		/* just one entry */
-		r = sourceAlloc(fs->cache, u, 0, 0, fs->readOnly);
-		if(r == nil)
-			goto Err;
-		r0 = sourceOpen(r, 0, fs->readOnly);
-		if(r0 == nil)
-			goto Err;
-		r1 = sourceOpen(r, 1, fs->readOnly);
-		if(r1 == nil)
-			goto Err;
-		r2 = sourceOpen(r, 2, fs->readOnly);
-		if(r2 == nil)
-			goto Err;
-		sourceFree(r);
-		r = nil;
-	}else{
-		r0 = sourceAlloc(fs->cache, u, 0, 0, fs->readOnly);
-		if(r0 == nil)
-			goto Err;
-		r1 = sourceAlloc(fs->cache, u, 0, 1, fs->readOnly);
-		if(r1 == nil)
-			goto Err;
-		r2 = sourceAlloc(fs->cache, u, 0, 2, fs->readOnly);
-		if(r2 == nil)
-			goto Err;
-	}
-	lumpDecRef(u, 0);
-	u = sourceGetLump(r2, 0, 1, 0);
-	if(u == nil)
+	r1 = vtfileopen(r, 1, fs->mode);
+	if(r1 == nil)
 		goto Err;
+	r2 = vtfileopen(r, 2, fs->mode);
+	if(r2 == nil)
+		goto Err;
 
-	mr = vfAlloc(fs);
+	mr = filealloc(fs);
 	mr->msource = r2;
 	r2 = nil;
 
-	root = vfAlloc(fs);
+	root = filealloc(fs);
+	root->boff = 0;
 	root->up = mr;
 	root->source = r0;
 	r0 = nil;
@@ -392,280 +160,404 @@ vfRoot(VacFS *fs, uchar *score)
 
 	mr->down = root;
 
-	if(!mbUnpack(&mb, u->data, u->asize))
+	if(vtfilelock(mr->msource, -1) < 0)
 		goto Err;
+	b = vtfileblock(mr->msource, 0, VtOREAD);
+	vtfileunlock(mr->msource);
+	if(b == nil)
+		goto Err;
 
-	if(!meUnpack(&me, &mb, 0))
+	if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
 		goto Err;
-	if(!vdUnpack(&root->dir, &me))
+
+	meunpack(&me, &mb, 0);
+	if(vdunpack(&root->dir, &me) < 0)
 		goto Err;
+	vtblockput(b);
+	vtfileunlock(r);
+	fileraccess(root);
 
-	vfRAccess(root);
-	lumpDecRef(u, 0);
-	sourceFree(r2);
-
 	return root;
 Err:
-	lumpDecRef(u, 0);
-	lumpDecRef(v, 0);
+	vtblockput(b);
 	if(r0)
-		sourceFree(r0);
+		vtfileclose(r0);
 	if(r1)
-		sourceFree(r1);
+		vtfileclose(r1);
 	if(r2)
-		sourceFree(r2);
-	if(r)
-		sourceFree(r);
+		vtfileclose(r2);
 	if(mr)
-		vfFree(mr);
+		filefree(mr);
 	if(root)
-		vfFree(root);
+		filefree(root);
+	vtfileunlock(r);
 
 	return nil;
 }
 
-VacFile *
-vfWalk(VacFile *vf, char *elem)
+static VtFile *
+fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
 {
-	VacFile *nvf;
+	VtFile *r;
 
-	vfRAccess(vf);
+	if(vtfilelock(f->source, mode) < 0)
+		return nil;
+	r = vtfileopen(f->source, offset, mode);
+	vtfileunlock(f->source);
+	if(r == nil)
+		return nil;
+	if(r->gen != gen){
+		werrstr(ERemoved);
+		goto Err;
+	}
+	if(r->dir != dir && r->mode != -1){
+fprint(2, "fileopensource: dir mismatch %d %d\n", r->dir, dir);
+		werrstr(EBadMeta);
+		goto Err;
+	}
+	return r;
+Err:
+	vtfileclose(r);
+	return nil;
+}
 
-	if(elem[0] == 0) {
-		vtSetError("illegal path element");
+VacFile*
+_filewalk(VacFile *f, char *elem, int partial)
+{
+	VacFile *ff;
+
+	fileraccess(f);
+
+	if(elem[0] == 0){
+		werrstr(EBadPath);
 		return nil;
 	}
-	if(!vfIsDir(vf)) {
-		vtSetError("not a directory");
+
+	if(!vacfileisdir(f)){
+		werrstr(ENotDir);
 		return nil;
 	}
 
-	if(strcmp(elem, ".") == 0) {
-		return vfIncRef(vf);
+	if(strcmp(elem, ".") == 0){
+		return vacfileincref(f);
 	}
 
-	if(strcmp(elem, "..") == 0) {
-		if(vfIsRoot(vf))
-			return vfIncRef(vf);
-		return vfIncRef(vf->up);
+	if(strcmp(elem, "..") == 0){
+		if(vacfileisroot(f))
+			return vacfileincref(f);
+		return vacfileincref(f->up);
 	}
 
-	if(!vfLock(vf))
+	if(filelock(f) < 0)
 		return nil;
 
-	for(nvf = vf->down; nvf; nvf=nvf->next) {
-		if(strcmp(elem, nvf->dir.elem) == 0 && !nvf->removed) {
-			nvf->ref++;
+	for(ff = f->down; ff; ff=ff->next){
+		if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
+			ff->ref++;
 			goto Exit;
 		}
 	}
 
-	nvf = dirLookup(vf, elem);
-	if(nvf == nil)
+	ff = dirlookup(f, elem);
+	if(ff == nil)
 		goto Err;
-	nvf->source = sourceOpen(vf->source, nvf->dir.entry, vf->fs->readOnly);
-	if(nvf->source == nil)
-		goto Err;
-	if(nvf->dir.mode & ModeDir) {
-		nvf->msource = sourceOpen(vf->source, nvf->dir.mentry, vf->fs->readOnly);
-		if(nvf->msource == nil)
+
+	if(ff->dir.mode & ModeSnapshot)
+		ff->mode = VtOREAD;
+
+	if(partial){
+		/*
+		 * Do nothing.  We're opening this file only so we can clri it.
+		 * Usually the sources can't be opened, hence we won't even bother.
+		 * Be VERY careful with the returned file.  If you hand it to a routine
+		 * expecting ff->source and/or ff->msource to be non-nil, we're
+		 * likely to dereference nil.  FileClri should be the only routine
+		 * setting partial.
+		 */
+		ff->partial = 1;
+	}else if(ff->dir.mode & ModeDir){
+		ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 1, ff->mode);
+		ff->msource = fileopensource(f, ff->dir.mentry, ff->dir.mgen, 0, ff->mode);
+		if(ff->source == nil || ff->msource == nil)
 			goto Err;
+	}else{
+		ff->source = fileopensource(f, ff->dir.entry, ff->dir.gen, 0, ff->mode);
+		if(ff->source == nil)
+			goto Err;
 	}
 
 	/* link in and up parent ref count */
-	nvf->next = vf->down;
-	vf->down = nvf;
-	nvf->up = vf;
-	vfIncRef(vf);
+	ff->next = f->down;
+	f->down = ff;
+	ff->up = f;
+	vacfileincref(f);
 Exit:
-	vfUnlock(vf);
-	return nvf;
+	fileunlock(f);
+	return ff;
 Err:
-	vfUnlock(vf);
-	if(nvf != nil)
-		vfFree(nvf);
+	fileunlock(f);
+	if(ff != nil)
+		vacfiledecref(ff);
 	return nil;
 }
 
-VacFile *
-vfOpen(VacFS *fs, char *path)
+VacFile*
+vacfilewalk(VacFile *f, char *elem)
 {
-	VacFile *vf, *nvf;
-	char *p, elem[VtMaxStringSize];
+	return _filewalk(f, elem, 0);
+}
+
+VacFile*
+_fileopen(VacFs *fs, char *path, int partial)
+{
+	VacFile *f, *ff;
+	char *p, elem[VtMaxStringSize], *opath;
 	int n;
 
-	vf = fs->root;
-	vfIncRef(vf);
-	while(*path != 0) {
+	f = fs->root;
+	vacfileincref(f);
+	opath = path;
+	while(*path != 0){
 		for(p = path; *p && *p != '/'; p++)
 			;
 		n = p - path;
-		if(n > 0) {
-			if(n > VtMaxStringSize) {
-				vtSetError("path element too long");
+		if(n > 0){
+			if(n > VtMaxStringSize){
+				werrstr("%s: element too long", EBadPath);
 				goto Err;
 			}
 			memmove(elem, path, n);
 			elem[n] = 0;
-			nvf = vfWalk(vf, elem);
-			if(nvf == nil)
+			ff = _filewalk(f, elem, partial && *p=='\0');
+			if(ff == nil){
+				werrstr("%.*s: %R", utfnlen(opath, p-opath), opath);
 				goto Err;
-			vfDecRef(vf);
-			vf = nvf;
+			}
+			vacfiledecref(f);
+			f = ff;
 		}
 		if(*p == '/')
 			p++;
 		path = p;
 	}
-	return vf;
+	return f;
 Err:
-	vfDecRef(vf);
+	vacfiledecref(f);
 	return nil;
 }
 
-VacFile *
-vfCreate(VacFile *vf, char *elem, ulong mode, char *user)
+VacFile*
+vacfileopen(VacFs *fs, char *path)
 {
-	VacFile *nvf;
+	return _fileopen(fs, path, 0);
+}
+
+#if 0
+static void
+filesettmp(VacFile *f, int istmp)
+{
+	int i;
+	VtEntry e;
+	VtFile *r;
+
+	for(i=0; i<2; i++){
+		if(i==0)
+			r = f->source;
+		else
+			r = f->msource;
+		if(r == nil)
+			continue;
+		if(vtfilegetentry(r, &e) < 0){
+			fprint(2, "vtfilegetentry failed (cannot happen): %r\n");
+			continue;
+		}
+		if(istmp)
+			e.flags |= VtEntryNoArchive;
+		else
+			e.flags &= ~VtEntryNoArchive;
+		if(vtfilesetentry(r, &e) < 0){
+			fprint(2, "vtfilesetentry failed (cannot happen): %r\n");
+			continue;
+		}
+	}
+}
+#endif
+
+VacFile*
+vacfilecreate(VacFile *f, char *elem, ulong mode, char *uid)
+{
+	VacFile *ff;
 	VacDir *dir;
-	int n, i;
-	uchar *p;
-	Source *pr, *r, *mr;
+	VtFile *pr, *r, *mr;
 	int isdir;
-	MetaBlock mb;
-	MetaEntry me;
-	Lump *u;
 
-	if(!vfLock(vf))
+	if(filelock(f) < 0)
 		return nil;
 
 	r = nil;
 	mr = nil;
-	u = nil;
-
-	for(nvf = vf->down; nvf; nvf=nvf->next) {
-		if(strcmp(elem, nvf->dir.elem) == 0 && !nvf->removed) {
-			nvf = nil;
-			vtSetError(EExists);
-			goto Err;
+	for(ff = f->down; ff; ff=ff->next){
+		if(strcmp(elem, ff->dir.elem) == 0 && !ff->removed){
+			ff = nil;
+			werrstr(EExists);
+			goto Err1;
 		}
 	}
 
-	nvf = dirLookup(vf, elem);
-	if(nvf != nil) {
-		vtSetError(EExists);
-		goto Err;
+	ff = dirlookup(f, elem);
+	if(ff != nil){
+		werrstr(EExists);
+		goto Err1;
 	}
 
-	nvf = vfAlloc(vf->fs);
+	pr = f->source;
+	if(pr->mode != VtORDWR){
+		werrstr(EReadOnly);
+		goto Err1;
+	}
+
+	if(vtfilelock2(f->source, f->msource, -1) < 0)
+		goto Err1;
+
+	ff = filealloc(f->fs);
 	isdir = mode & ModeDir;
 
-	pr = vf->source;
-	r = sourceCreate(pr, pr->psize, pr->dsize, isdir, 0);
+	r = vtfilecreate(pr, pr->dsize, isdir, 0);
 	if(r == nil)
 		goto Err;
-	if(isdir) {
-		mr = sourceCreate(pr, pr->psize, pr->dsize, 0, r->block*pr->epb + r->entry);
+	if(isdir){
+		mr = vtfilecreate(pr, pr->dsize, 0, r->offset);
 		if(mr == nil)
 			goto Err;
 	}
-	
-	dir = &nvf->dir;
-	dir->elem = vtStrDup(elem);
-	dir->entry = r->block*pr->epb + r->entry;
+
+	dir = &ff->dir;
+	dir->elem = vtstrdup(elem);
+	dir->entry = r->offset;
 	dir->gen = r->gen;
-	if(isdir) {
-		dir->mentry = mr->block*pr->epb + mr->entry;
+	if(isdir){
+		dir->mentry = mr->offset;
 		dir->mgen = mr->gen;
 	}
 	dir->size = 0;
-	dir->qid = vf->fs->qid++;
-	dir->uid = vtStrDup(user);
-	dir->gid = vtStrDup(vf->dir.gid);
-	dir->mid = vtStrDup(user);
+	if(_vacfsnextqid(f->fs, &dir->qid) < 0)
+		goto Err;
+	dir->uid = vtstrdup(uid);
+	dir->gid = vtstrdup(f->dir.gid);
+	dir->mid = vtstrdup(uid);
 	dir->mtime = time(0L);
 	dir->mcount = 0;
 	dir->ctime = dir->mtime;
 	dir->atime = dir->mtime;
 	dir->mode = mode;
 
-	n = vdSize(dir);
-	nvf->block = msAlloc(vf->msource, 0, n);
-	if(nvf->block == NilBlock)
+	ff->boff = filemetaalloc(f, dir, 0);
+	if(ff->boff == NilBlock)
 		goto Err;
-	u = sourceGetLump(vf->msource, nvf->block, 0, 1);
-	if(u == nil)
-		goto Err;
-	if(!mbUnpack(&mb, u->data, u->asize))
-		goto Err;
-	p = mbAlloc(&mb, n);
-	if(p == nil)
-		goto Err;
-		
-	if(!mbSearch(&mb, elem, &i, &me))
-		goto Err;
-	assert(me.p == nil);
-	me.p = p;
-	me.size = n;
 
-	vdPack(dir, &me);
-	mbInsert(&mb, i, &me);
-	mbPack(&mb);
-	lumpDecRef(u, 1);
+	vtfileunlock(f->source);
+	vtfileunlock(f->msource);
 
-	nvf->source = r;
-	nvf->msource = mr;
+	ff->source = r;
+	ff->msource = mr;
 
+#if 0
+	if(mode&ModeTemporary){
+		if(vtfilelock2(r, mr, -1) < 0)
+			goto Err1;
+		filesettmp(ff, 1);
+		vtfileunlock(r);
+		if(mr)
+			vtfileunlock(mr);
+	}
+#endif
+
+	/* committed */
+
 	/* link in and up parent ref count */
-	nvf->next = vf->down;
-	vf->down = nvf;
-	nvf->up = vf;
-	vfIncRef(vf);
+	ff->next = f->down;
+	f->down = ff;
+	ff->up = f;
+	vacfileincref(f);
 
-	vfWAccess(vf, user);
+	filewaccess(f, uid);
 
-	vfUnlock(vf);
-	return nvf;
+	fileunlock(f);
+	return ff;
 
 Err:
-	lumpDecRef(u, 1);
-	if(r)
-		sourceRemove(r);
-	if(mr)
-		sourceRemove(mr);
-	if(nvf)
-		vfFree(nvf);
-	vfUnlock(vf);
-	return 0;
+	vtfileunlock(f->source);
+	vtfileunlock(f->msource);
+Err1:
+	if(r){
+		vtfilelock(r, -1);
+		vtfileremove(r);
+	}
+	if(mr){
+		vtfilelock(mr, -1);
+		vtfileremove(mr);
+	}
+	if(ff)
+		vacfiledecref(ff);
+	fileunlock(f);
+	return nil;
 }
 
-
 int
-vfRead(VacFile *vf, void *buf, int cnt, vlong offset)
+vacfileblockscore(VacFile *f, u32int bn, u8int *score)
 {
-	Source *s;
+	VtFile *s;
 	uvlong size;
-	ulong bn;
+	int dsize, ret;
+
+	ret = -1;
+	if(filerlock(f) < 0)
+		return -1;
+	fileraccess(f);
+	if(vtfilelock(f->source, VtOREAD) < 0)
+		goto out;
+
+	s = f->source;
+	dsize = s->dsize;
+	size = vtfilegetsize(s);
+	if((uvlong)bn*dsize >= size)
+		goto out;
+	ret = vtfileblockscore(f->source, bn, score);
+
+out:
+	vtfileunlock(f->source);
+	filerunlock(f);
+	return ret;
+}
+
+int
+vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
+{
+	VtFile *s;
+	uvlong size;
+	u32int bn;
 	int off, dsize, n, nn;
-	Lump *u;
-	uchar *b;
+	VtBlock *b;
+	uchar *p;
 
-if(0)fprint(2, "vfRead: %s %d, %lld\n", vf->dir.elem, cnt, offset);
+if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
 
-	if(!vfRLock(vf))
+	if(filerlock(f) < 0)
 		return -1;
 
-	s = vf->source;
+	if(offset < 0){
+		werrstr(EBadOffset);
+		goto Err1;
+	}
 
-	dsize = s->dsize;
-	size = sourceGetSize(s);
+	fileraccess(f);
 
-	if(offset < 0) {
-		vtSetError(EBadOffset);
-		goto Err;
-	}
+	if(vtfilelock(f->source, VtOREAD) < 0)
+		goto Err1;
 
-	vfRAccess(vf);
+	s = f->source;
+	dsize = s->dsize;
+	size = vtfilegetsize(s);
 
 	if(offset >= size)
 		offset = size;
@@ -674,541 +566,1269 @@ if(0)fprint(2, "vfRead: %s %d, %lld\n", vf->dir.elem, 
 		cnt = size-offset;
 	bn = offset/dsize;
 	off = offset%dsize;
-	b = buf;
-	while(cnt > 0) {
-		u = sourceGetLump(s, bn, 1, 0);
-		if(u == nil)
+	p = buf;
+	while(cnt > 0){
+		b = vtfileblock(s, bn, OREAD);
+		if(b == nil)
 			goto Err;
-		if(u->asize <= off) {
-			lumpDecRef(u, 0);
-			goto Err;
-		}
 		n = cnt;
 		if(n > dsize-off)
 			n = dsize-off;
-		nn = u->asize-off;
+		nn = dsize-off;
 		if(nn > n)
 			nn = n;
-		memmove(b, u->data+off, nn);
-		memset(b+nn, 0, n-nn);
+		memmove(p, b->data+off, nn);
+		memset(p+nn, 0, nn-n);
 		off = 0;
 		bn++;
 		cnt -= n;
-		b += n;
-		lumpDecRef(u, 0);
+		p += n;
+		vtblockput(b);
 	}
-	vfRUnlock(vf);
-	return b-(uchar*)buf;
+	vtfileunlock(s);
+	filerunlock(f);
+	return p-(uchar*)buf;
+
 Err:
-	vfRUnlock(vf);
+	vtfileunlock(s);
+Err1:
+	filerunlock(f);
 	return -1;
 }
 
+#if 0
+/* 
+ * Changes the file block bn to be the given block score.
+ * Very sneaky.  Only used by flfmt.
+ */
 int
-vfWrite(VacFile *vf, void *buf, int cnt, vlong offset, char *user)
+filemapblock(VacFile *f, ulong bn, uchar score[VtScoreSize], ulong tag)
 {
-	Source *s;
-	ulong bn;
-	int off, dsize, n;
-	Lump *u;
-	uchar *b;
+	VtBlock *b;
+	VtEntry e;
+	VtFile *s;
 
-	USED(user);
-
-	if(!vfLock(vf))
+	if(filelock(f) < 0)
 		return -1;
 
-	if(vf->fs->readOnly) {
-		vtSetError(EReadOnly);
+	s = nil;
+	if(f->dir.mode & ModeDir){
+		werrstr(ENotFile);
 		goto Err;
 	}
 
-	if(vf->dir.mode & ModeDir) {
-		vtSetError(ENotFile);
+	if(f->source->mode != VtORDWR){
+		werrstr(EReadOnly);
 		goto Err;
 	}
-if(0)fprint(2, "vfWrite: %s %d, %lld\n", vf->dir.elem, cnt, offset);
 
-	s = vf->source;
-	dsize = s->dsize;
+	if(vtfilelock(f->source, -1) < 0)
+		goto Err;
 
-	if(offset < 0) {
-		vtSetError(EBadOffset);
+	s = f->source;
+	b = _vtfileblock(s, bn, VtORDWR, 1, tag);
+	if(b == nil)
 		goto Err;
-	}
 
-	vfWAccess(vf, user);
+	if(vtfilegetentry(s, &e) < 0)
+		goto Err;
+	if(b->l.type == BtDir){
+		memmove(e.score, score, VtScoreSize);
+		assert(e.tag == tag || e.tag == 0);
+		e.tag = tag;
+		e.flags |= VtEntryLocal;
+		vtentrypack(&e, b->data, f->source->offset % f->source->epb);
+	}else
+		memmove(b->data + (bn%(e.psize/VtScoreSize))*VtScoreSize, score, VtScoreSize);
+	vtblockdirty(b);
+	vtblockput(b);
+	vtfileunlock(s);
+	fileunlock(f);
+	return 0;
 
-	bn = offset/dsize;
-	off = offset%dsize;
-	b = buf;
-	while(cnt > 0) {
-		n = cnt;
-		if(n > dsize-off)
-			n = dsize-off;
-		if(!sourceSetDepth(s, offset+n))
-			goto Err;
-		u = sourceGetLump(s, bn, 0, 0);
-		if(u == nil)
-			goto Err;
-		if(u->asize < dsize) {
-			vtSetError("runt block");
-			lumpDecRef(u, 0);
-			goto Err;
-		}
-		memmove(u->data+off, b, n);
-		off = 0;
-		cnt -= n;
-		b += n;
-		offset += n;
-		bn++;
-		lumpDecRef(u, 0);
-		if(!sourceSetSize(s, offset))
-			goto Err;
-	}
-	vfLock(vf);
-	return b-(uchar*)buf;
 Err:
-	vfLock(vf);
+	if(s)
+		vtfileunlock(s);
+	fileunlock(f);
 	return -1;
 }
+#endif
 
 int
-vfGetDir(VacFile *vf, VacDir *dir)
+vacfilesetsize(VacFile *f, uvlong size)
 {
-	if(!vfRLock(vf))
-		return 0;
+	int r;
 
-	vfMetaLock(vf);
-	vdCopy(dir, &vf->dir);
-	vfMetaUnlock(vf);
-
-	if(!vfIsDir(vf))
-		dir->size = sourceGetSize(vf->source);
-	vfRUnlock(vf);
-
-	return 1;
+	if(filelock(f) < 0)
+		return -1;
+	r = 0;
+	if(f->dir.mode & ModeDir){
+		werrstr(ENotFile);
+		goto Err;
+	}
+	if(f->source->mode != VtORDWR){
+		werrstr(EReadOnly);
+		goto Err;
+	}
+	if(vtfilelock(f->source, -1) < 0)
+		goto Err;
+	r = vtfilesetsize(f->source, size);
+	vtfileunlock(f->source);
+Err:
+	fileunlock(f);
+	return r;
 }
 
-uvlong
-vfGetId(VacFile *vf)
+int
+filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
 {
-	/* immutable */
-	return vf->dir.qid;
-}
-
-ulong
-vfGetMcount(VacFile *vf)
-{
-	ulong mcount;
-	
-	vfMetaLock(vf);
-	mcount = vf->dir.mcount;
-	vfMetaUnlock(vf);
-	return mcount;
-}
+	VtFile *s;
+	ulong bn;
+	int off, dsize, n;
+	VtBlock *b;
+	uchar *p;
+	vlong eof;
 
+if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
 
-int
-vfIsDir(VacFile *vf)
-{
-	/* immutable */
-	return (vf->dir.mode & ModeDir) != 0;
+	if(filelock(f) < 0)
+		return -1;
+
+	s = nil;
+	if(f->dir.mode & ModeDir){
+		werrstr(ENotFile);
+		goto Err;
+	}
+
+	if(f->source->mode != VtORDWR){
+		werrstr(EReadOnly);
+		goto Err;
+	}
+	if(offset < 0){
+		werrstr(EBadOffset);
+		goto Err;
+	}
+
+	filewaccess(f, uid);
+
+	if(vtfilelock(f->source, -1) < 0)
+		goto Err;
+	s = f->source;
+	dsize = s->dsize;
+
+	eof = vtfilegetsize(s);
+	if(f->dir.mode & ModeAppend)
+		offset = eof;
+	bn = offset/dsize;
+	off = offset%dsize;
+	p = buf;
+	while(cnt > 0){
+		n = cnt;
+		if(n > dsize-off)
+			n = dsize-off;
+		b = vtfileblock(s, bn, n<dsize?VtORDWR:VtOWRITE);
+		if(b == nil){
+			if(offset > eof)
+				vtfilesetsize(s, offset);
+			goto Err;
+		}
+		memmove(b->data+off, p, n);
+		off = 0;
+		cnt -= n;
+		p += n;
+		offset += n;
+		bn++;
+		vtblockdirty(b);
+		vtblockput(b);
+	}
+	if(offset > eof && vtfilesetsize(s, offset) < 0)
+		goto Err;
+	vtfileunlock(s);
+	fileunlock(f);
+	return p-(uchar*)buf;
+Err:
+	if(s)
+		vtfileunlock(s);
+	fileunlock(f);
+	return -1;
 }
 
 int
-vfIsRoot(VacFile *vf)
+vacfilegetdir(VacFile *f, VacDir *dir)
 {
-	return vf == vf->fs->root;
+	if(filerlock(f) < 0)
+		return -1;
+
+	filemetalock(f);
+	vdcopy(dir, &f->dir);
+	filemetaunlock(f);
+
+	if(vacfileisdir(f) < 0){
+		if(vtfilelock(f->source, VtOREAD) < 0){
+			filerunlock(f);
+			return -1;
+		}
+		dir->size = vtfilegetsize(f->source);
+		vtfileunlock(f->source);
+	}
+	filerunlock(f);
+
+	return 0;
 }
 
 int
-vfGetSize(VacFile *vf, uvlong *size)
+vacfiletruncate(VacFile *f, char *uid)
 {
-	if(!vfRLock(vf))
-		return 0;
-	*size = sourceGetSize(vf->source);
-	vfRUnlock(vf);
+	if(vacfileisdir(f)){
+		werrstr(ENotFile);
+		return -1;
+	}
 
-	return 1;
+	if(filelock(f) < 0)
+		return -1;
+
+	if(f->source->mode != VtORDWR){
+		werrstr(EReadOnly);
+		fileunlock(f);
+		return -1;
+	}
+	if(vtfilelock(f->source, -1) < 0){
+		fileunlock(f);
+		return -1;
+	}
+	if(vtfiletruncate(f->source) < 0){
+		vtfileunlock(f->source);
+		fileunlock(f);
+		return -1;
+	}
+	vtfileunlock(f->source);
+	fileunlock(f);
+
+	filewaccess(f, uid);
+
+	return 0;
 }
 
-static int
-vfMetaRemove(VacFile *vf, char *user)
+int
+vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
 {
-	Lump *u;
-	MetaBlock mb;
-	MetaEntry me;
-	int i;
-	VacFile *vfp;
+	VacFile *ff;
+	char *oelem;
+	u32int mask;
+	u64int size;
 
-	vfp = vf->up;
+	/* can not set permissions for the root */
+	if(vacfileisroot(f)){
+		werrstr(ERoot);
+		return -1;
+	}
 
-	vfWAccess(vfp, user);
+	if(filelock(f) < 0)
+		return -1;
 
-	vfMetaLock(vf);
+	if(f->source->mode != VtORDWR){
+		werrstr(EReadOnly);
+		fileunlock(f);
+		return -1;
+	}
 
-	u = sourceGetLump(vfp->msource, vf->block, 0, 1);
-	if(u == nil)
-		goto Err;
+	filemetalock(f);
 
-	if(!mbUnpack(&mb, u->data, u->asize))
+	/* check new name does not already exist */
+	if(strcmp(f->dir.elem, dir->elem) != 0){
+		for(ff = f->up->down; ff; ff=ff->next){
+			if(strcmp(dir->elem, ff->dir.elem) == 0 && !ff->removed){
+				werrstr(EExists);
+				goto Err;
+			}
+		}
+
+		ff = dirlookup(f->up, dir->elem);
+		if(ff != nil){
+			vacfiledecref(ff);
+			werrstr(EExists);
+			goto Err;
+		}
+	}
+
+	if(vtfilelock2(f->source, f->msource, -1) < 0)
 		goto Err;
-	if(!mbSearch(&mb, vf->dir.elem, &i, &me) || me.p == nil)
-		goto Err;
-print("deleting %d entry\n", i);
-	mbDelete(&mb, i, &me);
-	memset(me.p, 0, me.size);
-	mbPack(&mb);
-	
-	lumpDecRef(u, 1);
-	
-	vf->removed = 1;
-	vf->block = NilBlock;
+	if(!vacfileisdir(f)){
+		size = vtfilegetsize(f->source);
+		if(size != dir->size){
+			if(vtfilesetsize(f->source, dir->size) < 0){
+				vtfileunlock(f->source);
+				if(f->msource)
+					vtfileunlock(f->msource);
+				goto Err;
+			}
+			/* commited to changing it now */
+		}
+	}
+	/* commited to changing it now */
+#if 0
+	if((f->dir.mode&ModeTemporary) != (dir->mode&ModeTemporary))
+		filesettmp(f, dir->mode&ModeTemporary);
+#endif
+	vtfileunlock(f->source);
+	if(f->msource)
+		vtfileunlock(f->msource);
 
-	vfMetaUnlock(vf);
-	return 1;
+	oelem = nil;
+	if(strcmp(f->dir.elem, dir->elem) != 0){
+		oelem = f->dir.elem;
+		f->dir.elem = vtstrdup(dir->elem);
+	}
 
-Err:
-	lumpDecRef(u, 1);
-	vfMetaUnlock(vf);
+	if(strcmp(f->dir.uid, dir->uid) != 0){
+		vtfree(f->dir.uid);
+		f->dir.uid = vtstrdup(dir->uid);
+	}
+
+	if(strcmp(f->dir.gid, dir->gid) != 0){
+		vtfree(f->dir.gid);
+		f->dir.gid = vtstrdup(dir->gid);
+	}
+
+	f->dir.mtime = dir->mtime;
+	f->dir.atime = dir->atime;
+
+//fprint(2, "mode %x %x ", f->dir.mode, dir->mode);
+	mask = ~(ModeDir|ModeSnapshot);
+	f->dir.mode &= ~mask;
+	f->dir.mode |= mask & dir->mode;
+	f->dirty = 1;
+//fprint(2, "->%x\n", f->dir.mode);
+
+	filemetaflush2(f, oelem);
+	vtfree(oelem);
+
+	filemetaunlock(f);
+	fileunlock(f);
+
+	filewaccess(f->up, uid);
+
 	return 0;
+Err:
+	filemetaunlock(f);
+	fileunlock(f);
+	return -1;
 }
 
+int
+vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
+{
+	int ret;
 
-static int
-vfCheckEmpty(VacFile *vf)
+	if(filelock(f) < 0)
+		return -1;
+	filemetalock(f);
+	f->dir.qidspace = 1;
+	f->dir.qidoffset = offset;
+	f->dir.qidmax = max;
+	ret = filemetaflush2(f, nil);
+	filemetaunlock(f);
+	fileunlock(f);
+	return ret;
+}
+
+uvlong
+vacfilegetid(VacFile *f)
 {
-	int i, n;
-	Lump *u;
-	MetaBlock mb;
-	Source *r;
+	/* immutable */
+	return f->dir.qid;
+}
 
-	r = vf->msource;
-	n = sourceGetNumBlocks(r);
-	for(i=0; i<n; i++) {
-		u = sourceGetLump(r, i, 1, 1);
-		if(u == nil)
-			goto Err;
-		if(!mbUnpack(&mb, u->data, u->asize))
-			goto Err;
-		if(mb.nindex > 0) {
-			vtSetError(ENotEmpty);
-			goto Err;
-		}
-		lumpDecRef(u, 1);
+ulong
+vacfilegetmcount(VacFile *f)
+{
+	ulong mcount;
+
+	filemetalock(f);
+	mcount = f->dir.mcount;
+	filemetaunlock(f);
+	return mcount;
+}
+
+ulong
+vacfilegetmode(VacFile *f)
+{
+	ulong mode;
+
+	filemetalock(f);
+	mode = f->dir.mode;
+	filemetaunlock(f);
+	return mode;
+}
+
+int
+vacfileisdir(VacFile *f)
+{
+	/* immutable */
+	return (f->dir.mode & ModeDir) != 0;
+}
+
+int
+vacfileisroot(VacFile *f)
+{
+	return f == f->fs->root;
+}
+
+int
+vacfilegetsize(VacFile *f, uvlong *size)
+{
+	if(filerlock(f) < 0)
+		return 0;
+	if(vtfilelock(f->source, VtOREAD) < 0){
+		filerunlock(f);
+		return -1;
 	}
-	return 1;
-Err:
-	lumpDecRef(u, 1);
+	*size = vtfilegetsize(f->source);
+	vtfileunlock(f->source);
+	filerunlock(f);
+
 	return 0;
 }
 
 int
-vfRemove(VacFile *vf, char *user)
-{	
-	/* can not remove the root */
-	if(vfIsRoot(vf)) {
-		vtSetError(ERoot);
+vacfilegetvtentry(VacFile *f, VtEntry *e)
+{
+	if(filerlock(f) < 0)
 		return 0;
+	if(vtfilelock(f->source, VtOREAD) < 0){
+		filerunlock(f);
+		return -1;
 	}
+	vtfilegetentry(f->source, e);
+	vtfileunlock(f->source);
+	filerunlock(f);
 
-	if(!vfLock(vf))
+	return 0;
+}
+
+void
+vacfilemetaflush(VacFile *f, int rec)
+{
+	VacFile **kids, *p;
+	int nkids;
+	int i;
+
+	filemetalock(f);
+	filemetaflush2(f, nil);
+	filemetaunlock(f);
+
+	if(!rec || !vacfileisdir(f))
+		return;
+
+	if(filelock(f) < 0)
+		return;
+	nkids = 0;
+	for(p=f->down; p; p=p->next)
+		nkids++;
+	kids = vtmalloc(nkids*sizeof(VacFile*));
+	i = 0;
+	for(p=f->down; p; p=p->next){
+		kids[i++] = p;
+		p->ref++;
+	}
+	fileunlock(f);
+
+	for(i=0; i<nkids; i++){
+		vacfilemetaflush(kids[i], 1);
+		vacfiledecref(kids[i]);
+	}
+	vtfree(kids);
+}
+
+/* assumes metaLock is held */
+static int
+filemetaflush2(VacFile *f, char *oelem)
+{
+	VacFile *fp;
+	VtBlock *b, *bb;
+	MetaBlock mb;
+	MetaEntry me, me2;
+	int i, n;
+	u32int boff;
+
+	if(!f->dirty)
 		return 0;
 
-	if(vfIsDir(vf) && !vfCheckEmpty(vf))
+	if(oelem == nil)
+		oelem = f->dir.elem;
+
+	fp = f->up;
+
+	if(vtfilelock(fp->msource, -1) < 0)
+		return 0;
+	/* can happen if source is clri'ed out from under us */
+	if(f->boff == NilBlock)
+		goto Err1;
+	b = vtfileblock(fp->msource, f->boff, VtORDWR);
+	if(b == nil)
+		goto Err1;
+
+	if(mbunpack(&mb, b->data, fp->msource->dsize) < 0)
 		goto Err;
-			
-	assert(vf->down == nil);
+	if(mbsearch(&mb, oelem, &i, &me) < 0)
+		goto Err;
 
-	sourceRemove(vf->source);
-	vf->source = nil;
-	if(vf->msource) {
-		sourceRemove(vf->msource);
-		vf->msource = nil;
+	n = vdsize(&f->dir);
+if(0)fprint(2, "old size %d new size %d\n", me.size, n);
+
+	if(mbresize(&mb, &me, n) >= 0){
+		/* fits in the block */
+		mbdelete(&mb, i, &me);
+		if(strcmp(f->dir.elem, oelem) != 0)
+			mbsearch(&mb, f->dir.elem, &i, &me2);
+		vdpack(&f->dir, &me);
+		mbinsert(&mb, i, &me);
+		mbpack(&mb);
+		vtblockdirty(b);
+		vtblockput(b);
+		vtfileunlock(fp->msource);
+		f->dirty = 0;
+		return -1;
 	}
-	
-	vfUnlock(vf);
-	
-	if(!vfMetaRemove(vf, user))
-		return 0;
-	
-	return 1;
-		
+
+	/*
+	 * moving entry to another block
+	 * it is feasible for the fs to crash leaving two copies
+	 * of the directory entry.  This is just too much work to
+	 * fix.  Given that entries are only allocated in a block that
+	 * is less than PercentageFull, most modifications of meta data
+	 * will fit within the block.  i.e. this code should almost
+	 * never be executed.
+	 */
+	boff = filemetaalloc(fp, &f->dir, f->boff+1);
+	if(boff == NilBlock){
+		/* mbResize might have modified block */
+		mbpack(&mb);
+		vtblockdirty(b);
+		goto Err;
+	}
+fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
+	f->boff = boff;
+
+	/* make sure deletion goes to disk after new entry */
+	bb = vtfileblock(fp->msource, f->boff, VtORDWR);
+	mbdelete(&mb, i, &me);
+	mbpack(&mb);
+//	blockDependency(b, bb, -1, nil, nil);
+	vtblockput(bb);
+	vtblockdirty(b);
+	vtblockput(b);
+	vtfileunlock(fp->msource);
+
+	f->dirty = 0;
+
+	return 0;
+
 Err:
-	vfUnlock(vf);
+	vtblockput(b);
+Err1:
+	vtfileunlock(fp->msource);
+	return -1;
+}
+
+static int
+filemetaremove(VacFile *f, char *uid)
+{
+	VtBlock *b;
+	MetaBlock mb;
+	MetaEntry me;
+	int i;
+	VacFile *up;
+
+	b = nil;
+	up = f->up;
+	filewaccess(up, uid);
+	filemetalock(f);
+
+	if(vtfilelock(up->msource, VtORDWR) < 0)
+		goto Err;
+	b = vtfileblock(up->msource, f->boff, VtORDWR);
+	if(b == nil)
+		goto Err;
+
+	if(mbunpack(&mb, b->data, up->msource->dsize) < 0)
+		goto Err;
+	if(mbsearch(&mb, f->dir.elem, &i, &me) < 0)
+		goto Err;
+	mbdelete(&mb, i, &me);
+	mbpack(&mb);
+	vtfileunlock(up->msource);
+
+	vtblockdirty(b);
+	vtblockput(b);
+
+	f->removed = 1;
+	f->boff = NilBlock;
+	f->dirty = 0;
+
+	filemetaunlock(f);
 	return 0;
+
+Err:
+	vtfileunlock(up->msource);
+	vtblockput(b);
+	filemetaunlock(f);
+	return -1;
 }
 
-VacFile *
-vfIncRef(VacFile *vf)
+/* assume file is locked, assume f->msource is locked */
+static int
+filecheckempty(VacFile *f)
 {
-	vfMetaLock(vf);
-	assert(vf->ref > 0);
-	vf->ref++;
-	vfMetaUnlock(vf);
-	return vf;
+	u32int i, n;
+	VtBlock *b;
+	MetaBlock mb;
+	VtFile *r;
+
+	r = f->msource;
+	n = (vtfilegetsize(r)+r->dsize-1)/r->dsize;
+	for(i=0; i<n; i++){
+		b = vtfileblock(r, i, VtORDWR);
+		if(b == nil)
+			goto Err;
+		if(mbunpack(&mb, b->data, r->dsize) < 0)
+			goto Err;
+		if(mb.nindex > 0){
+			werrstr(ENotEmpty);
+			goto Err;
+		}
+		vtblockput(b);
+	}
+	return 0;
+Err:
+	vtblockput(b);
+	return -1;
 }
 
-void
-vfDecRef(VacFile *vf)
+int
+vacfileremove(VacFile *f, char *muid)
 {
-	VacFile *p, *q, **qq;
+	VacFile *ff;
 
-	if(vf->up == nil) {
-		vfFree(vf);
-		return;
+	/* can not remove the root */
+	if(vacfileisroot(f)){
+		werrstr(ERoot);
+		return -1;
 	}
 
-	vfMetaLock(vf);
-	vf->ref--;
-	if(vf->ref > 0) {
-		vfMetaUnlock(vf);
-		return;
+	if(filelock(f) < 0)
+		return -1;
+
+	if(f->source->mode != VtORDWR){
+		werrstr(EReadOnly);
+		goto Err1;
 	}
-	assert(vf->ref == 0);
-	assert(vf->down == nil);
+	if(vtfilelock2(f->source, f->msource, -1) < 0)
+		goto Err1;
+	if(vacfileisdir(f) && filecheckempty(f)<0)
+		goto Err;
 
-	p = vf->up;
-	qq = &p->down;
-	for(q = *qq; q; qq=&q->next,q=*qq)
-		if(q == vf)
-			break;
-	assert(q != nil);
-	*qq = vf->next;
+	for(ff=f->down; ff; ff=ff->next)
+		assert(ff->removed);
 
-	vfMetaUnlock(vf);
-	vfFree(vf);
+	vtfileremove(f->source);
+	f->source = nil;
+	if(f->msource){
+		vtfileremove(f->msource);
+		f->msource = nil;
+	}
 
-	vfDecRef(p);
+	fileunlock(f);
+
+	if(filemetaremove(f, muid) < 0)
+		return -1;
+
+	return 0;
+
+Err:
+	vtfileunlock(f->source);
+	if(f->msource)
+		vtfileunlock(f->msource);
+Err1:
+	fileunlock(f);
+	return -1;
 }
 
+static int
+clri(VacFile *f, char *uid)
+{
+	int r;
+
+	if(f == nil)
+		return -1;
+	if(f->up->source->mode != VtORDWR){
+		werrstr(EReadOnly);
+		vacfiledecref(f);
+		return -1;
+	}
+	r = filemetaremove(f, uid);
+	vacfiledecref(f);
+	return r;
+}
+
 int
-vfGetVtEntry(VacFile *vf, VtEntry *e)
+vacfileclripath(VacFs *fs, char *path, char *uid)
 {
-	int res;
+	return clri(_fileopen(fs, path, 1), uid);
+}
 
-	if(!vfRLock(vf))
-		return 0;
-	res = sourceGetVtEntry(vf->source, e);
-	vfRUnlock(vf);
-	return res;
+int
+vacfileclri(VacFile *dir, char *elem, char *uid)
+{
+	return clri(_filewalk(dir, elem, 1), uid);
 }
 
+VacFile*
+vacfileincref(VacFile *vf)
+{
+	filemetalock(vf);
+	assert(vf->ref > 0);
+	vf->ref++;
+	filemetaunlock(vf);
+	return vf;
+}
+
 int
-vfGetBlockScore(VacFile *vf, ulong bn, uchar score[VtScoreSize])
+vacfiledecref(VacFile *f)
 {
-	Lump *u;
-	int ret, off;
-	Source *r;
+	VacFile *p, *q, **qq;
 
-	if(!vfRLock(vf))
+	if(f->up == nil){
+		/* never linked in */
+		assert(f->ref == 1);
+		filefree(f);
 		return 0;
+	}
 
-	r = vf->source;
-
-	u = sourceWalk(r, bn, 1, &off);
-	if(u == nil){
-		vfRUnlock(vf);
-		return 0;
+	filemetalock(f);
+	f->ref--;
+	if(f->ref > 0){
+		filemetaunlock(f);
+		return -1;
 	}
+	assert(f->ref == 0);
+	assert(f->down == nil);
 
-	ret = lumpGetScore(u, off, score);
-	lumpDecRef(u, 0);
-	vfRUnlock(vf);
+	filemetaflush2(f, nil);
 
-	return ret;
+	p = f->up;
+	qq = &p->down;
+	for(q = *qq; q; q = *qq){
+		if(q == f)
+			break;
+		qq = &q->next;
+	}
+	assert(q != nil);
+	*qq = f->next;
+
+	filemetaunlock(f);
+	filefree(f);
+	vacfiledecref(p);
+	return 0;
 }
 
-VacFile *
-vfGetParent(VacFile *vf)
+VacFile*
+filegetparent(VacFile *f)
 {
-	if(vfIsRoot(vf))
-		return vfIncRef(vf);
-	return vfIncRef(vf->up);
+	if(vacfileisroot(f))
+		return vacfileincref(f);
+	return vacfileincref(f->up);
 }
 
-static VacDirEnum *
-vdeAlloc(VacFile *vf)
+VacDirEnum*
+vdeopen(VacFile *f)
 {
-	VacDirEnum *ds;
+	VacDirEnum *vde;
+	VacFile *p;
 
-	if(!(vf->dir.mode & ModeDir)) {
-		vtSetError(ENotDir);
-		vfDecRef(vf);
+	if(!vacfileisdir(f)){
+		werrstr(ENotDir);
 		return nil;
 	}
 
-	ds = vtMemAllocZ(sizeof(VacDirEnum));
-	ds->file = vf;
-	
-	return ds;
-}
-
-VacDirEnum *
-vdeOpen(VacFS *fs, char *path)
-{
-	VacFile *vf;
-
-	vf = vfOpen(fs, path);
-	if(vf == nil)
+	/* flush out meta data */
+	if(filelock(f) < 0)
 		return nil;
+	for(p=f->down; p; p=p->next)
+		filemetaflush2(p, nil);
+	fileunlock(f);
 
-	return vdeAlloc(vf);
-}
+	vde = vtmallocz(sizeof(VacDirEnum));
+	vde->file = vacfileincref(f);
 
-VacDirEnum *
-vfDirEnum(VacFile *vf)
-{
-	return vdeAlloc(vfIncRef(vf));
+	return vde;
 }
 
 static int
-dirEntrySize(Source *s, ulong elem, ulong gen, uvlong *size)
+direntrysize(VtFile *s, ulong elem, ulong gen, uvlong *size)
 {
-	Lump *u;
+	VtBlock *b;
 	ulong bn;
 	VtEntry e;
+	int epb;
 
-	bn = elem/s->epb;
-	elem -= bn*s->epb;
+	epb = s->dsize/VtEntrySize;
+	bn = elem/epb;
+	elem -= bn*epb;
 
-	u = sourceGetLump(s, bn, 1, 1);
-	if(u == nil)
+	b = vtfileblock(s, bn, VtOREAD);
+	if(b == nil)
 		goto Err;
-	if(u->asize < (elem+1)*VtEntrySize) {
-		vtSetError(ENoDir);
+	if(vtentryunpack(&e, b->data, elem) < 0)
 		goto Err;
-	}
-	vtEntryUnpack(&e, u->data, elem);
-	if(!(e.flags & VtEntryActive) || e.gen != gen) {
-fprint(2, "gen mismatch\n");
-		vtSetError(ENoDir);
-		goto Err;
-	}
 
-	*size = e.size;
-	lumpDecRef(u, 1);
-	return 1;	
+	/* hanging entries are returned as zero size */
+	if(!(e.flags & VtEntryActive) || e.gen != gen)
+		*size = 0;
+	else
+		*size = e.size;
+	vtblockput(b);
+	return 0;
 
 Err:
-	lumpDecRef(u, 1);
-	return 0;
+	vtblockput(b);
+	return -1;
 }
 
-int
-vdeRead(VacDirEnum *ds, VacDir *dir, int n)
+static int
+vdefill(VacDirEnum *vde)
 {
-	ulong nb;
-	int i;
-	Source *meta, *source;
+	int i, n;
+	VtFile *meta, *source;
 	MetaBlock mb;
 	MetaEntry me;
-	Lump *u;
+	VacFile *f;
+	VtBlock *b;
+	VacDir *de;
 
-	vfRAccess(ds->file);
+	/* clean up first */
+	for(i=vde->i; i<vde->n; i++)
+		vdcleanup(vde->buf+i);
+	vtfree(vde->buf);
+	vde->buf = nil;
+	vde->i = 0;
+	vde->n = 0;
 
-	if(!vfRLock(ds->file))
-		return -1;
+	f = vde->file;
 
-	i = 0;
-	u = nil;
-	source = ds->file->source;
-	meta = ds->file->msource;
-	nb = (sourceGetSize(meta) + meta->dsize - 1)/meta->dsize;
+	source = f->source;
+	meta = f->msource;
 
-	if(ds->block >= nb)
-		goto Exit;
-	u = sourceGetLump(meta, ds->block, 1, 1);
-	if(u == nil)
+	b = vtfileblock(meta, vde->boff, VtOREAD);
+	if(b == nil)
 		goto Err;
-	if(!mbUnpack(&mb, u->data, u->asize))
+	if(mbunpack(&mb, b->data, meta->dsize) < 0)
 		goto Err;
 
-	for(i=0; i<n; i++) {
-		while(ds->index >= mb.nindex) {
-			lumpDecRef(u, 1);
-			u = nil;
-			ds->index = 0;
-			ds->block++;
-			if(ds->block >= nb)
-				goto Exit;
-			u = sourceGetLump(meta, ds->block, 1, 1);
-			if(u == nil)
-				goto Err;
-			if(!mbUnpack(&mb, u->data, u->asize))
-				goto Err;
-		}
-		if(!meUnpack(&me, &mb, ds->index))
+	n = mb.nindex;
+	vde->buf = vtmalloc(n * sizeof(VacDir));
+
+	for(i=0; i<n; i++){
+		de = vde->buf + i;
+		meunpack(&me, &mb, i);
+		if(vdunpack(de, &me) < 0)
 			goto Err;
-		if(dir != nil) {
-			if(!vdUnpack(&dir[i], &me))
-				goto Err;
-			if(!(dir[i].mode & ModeDir))
-			if(!dirEntrySize(source, dir[i].entry, dir[i].gen, &dir[i].size))
-				goto Err;
-		}
-		ds->index++;
+		vde->n++;
+		if(!(de->mode & ModeDir))
+		if(direntrysize(source, de->entry, de->gen, &de->size) < 0)
+			goto Err;
 	}
-Exit:
-	lumpDecRef(u, 1);
-	vfRUnlock(ds->file);
-	return i;
+	vde->boff++;
+	vtblockput(b);
+	return 0;
 Err:
-	lumpDecRef(u, 1);
-	vfRUnlock(ds->file);
-	n = i;
-	for(i=0; i<n ; i++)
-		vdCleanup(&dir[i]);
+	vtblockput(b);
 	return -1;
 }
 
+int
+vderead(VacDirEnum *vde, VacDir *de)
+{
+	int ret, didread;
+	VacFile *f;
+	u32int nb;
+
+	f = vde->file;
+	if(filerlock(f) < 0)
+		return -1;
+
+	if(vtfilelock2(f->source, f->msource, VtOREAD) < 0){
+		filerunlock(f);
+		return -1;
+	}
+
+	nb = (vtfilegetsize(f->msource)+f->msource->dsize-1)/f->msource->dsize;
+
+	didread = 0;
+	while(vde->i >= vde->n){
+		if(vde->boff >= nb){
+			ret = 0;
+			goto Return;
+		}
+		didread = 1;
+		if(vdefill(vde) < 0){
+			ret = -1;
+			goto Return;
+		}
+	}
+
+	memmove(de, vde->buf + vde->i, sizeof(VacDir));
+	vde->i++;
+	ret = 1;
+
+Return:
+	vtfileunlock(f->source);
+	vtfileunlock(f->msource);
+	filerunlock(f);
+
+	if(didread)
+		fileraccess(f);
+	return ret;
+}
+
 void
-vdeFree(VacDirEnum *ds)
+vdeclose(VacDirEnum *vde)
 {
-	if(ds == nil)
+	int i;
+	if(vde == nil)
 		return;
-	vfDecRef(ds->file);
-	vtMemFree(ds);
+	for(i=vde->i; i<vde->n; i++)
+		vdcleanup(vde->buf+i);
+	vtfree(vde->buf);
+	vacfiledecref(vde->file);
+	vtfree(vde);
 }
 
-static ulong
-msAlloc(Source *ms, ulong start, int n)
+/*
+ * caller must lock f->source and f->msource
+ * caller must NOT lock the source and msource
+ * referenced by dir.
+ */
+static u32int
+filemetaalloc(VacFile *f, VacDir *dir, u32int start)
 {
-	ulong nb, i;
-	Lump *u;
+	u32int nb, bo;
+	VtBlock *b, *bb;
 	MetaBlock mb;
+	int nn;
+	uchar *p;
+	int i, n, epb;
+	MetaEntry me;
+	VtFile *s, *ms;
 
-	nb = sourceGetNumBlocks(ms);
-	u = nil;
+	s = f->source;
+	ms = f->msource;
+
+	n = vdsize(dir);
+	nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
+	b = nil;
 	if(start > nb)
 		start = nb;
-	for(i=start; i<nb; i++) {
-		u = sourceGetLump(ms, i, 1, 1);
-		if(u == nil)
+	for(bo=start; bo<nb; bo++){
+		b = vtfileblock(ms, bo, VtORDWR);
+		if(b == nil)
 			goto Err;
-		if(!mbUnpack(&mb, u->data, ms->dsize))
+		if(mbunpack(&mb, b->data, ms->dsize) < 0)
 			goto Err;
-		if(mb.maxsize - mb.size + mb.free >= n && mb.nindex < mb.maxindex)
+		nn = (mb.maxsize*FullPercentage/100) - mb.size + mb.free;
+		if(n <= nn && mb.nindex < mb.maxindex)
 			break;
-		lumpDecRef(u, 1);
-		u = nil;
+		vtblockput(b);
+		b = nil;
 	}
+
 	/* add block to meta file */
-	if(i == nb) {
-		if(!sourceSetDepth(ms, (i+1)*ms->dsize))
+	if(b == nil){
+		b = vtfileblock(ms, bo, VtORDWR);
+		if(b == nil)
 			goto Err;
-		u = sourceGetLump(ms, i, 0, 1);
-		if(u == nil)
-			goto Err;
-		sourceSetSize(ms, (nb+1)*ms->dsize);
-		mbInit(&mb, u->data, u->asize);
-		mbPack(&mb);
+		vtfilesetsize(ms, (nb+1)*ms->dsize);
+		mbinit(&mb, b->data, ms->dsize, ms->dsize/BytesPerEntry);
 	}
-	lumpDecRef(u, 1);
-	return i;
+
+	p = mballoc(&mb, n);
+	if(p == nil){
+		/* mbAlloc might have changed block */
+		mbpack(&mb);
+		vtblockdirty(b);
+		werrstr(EBadMeta);
+		goto Err;
+	}
+
+	mbsearch(&mb, dir->elem, &i, &me);
+	assert(me.p == nil);
+	me.p = p;
+	me.size = n;
+	vdpack(dir, &me);
+	mbinsert(&mb, i, &me);
+	mbpack(&mb);
+
+#ifdef notdef /* XXX */
+	/* meta block depends on super block for qid ... */
+	bb = cacheLocal(b->c, PartSuper, 0, VtOREAD);
+	blockDependency(b, bb, -1, nil, nil);
+	vtblockput(bb);
+
+	/* ... and one or two dir entries */
+	epb = s->dsize/VtEntrySize;
+	bb = vtfileblock(s, dir->entry/epb, VtOREAD);
+	blockDependency(b, bb, -1, nil, nil);
+	vtblockput(bb);
+	if(dir->mode & ModeDir){
+		bb = sourceBlock(s, dir->mentry/epb, VtOREAD);
+		blockDependency(b, bb, -1, nil, nil);
+		vtblockput(bb);
+	}
+#endif
+
+	vtblockdirty(b);
+	vtblockput(b);
+	return bo;
 Err:
-	lumpDecRef(u, 1);
+	vtblockput(b);
 	return NilBlock;
 }
 
+static int
+chksource(VacFile *f)
+{
+	if(f->partial)
+		return 0;
+
+	if(f->source == nil || (f->dir.mode & ModeDir) && f->msource == nil){
+		werrstr(ERemoved);
+		return -1;
+	}
+	return 0;
+}
+
+static int
+filerlock(VacFile *f)
+{
+//	assert(!canwlock(&f->fs->elk));
+	rlock(&f->lk);
+	if(chksource(f) < 0){
+		runlock(&f->lk);
+		return -1;
+	}
+	return 0;
+}
+
+static void
+filerunlock(VacFile *f)
+{
+	runlock(&f->lk);
+}
+
+static int
+filelock(VacFile *f)
+{
+//	assert(!canwlock(&f->fs->elk));
+	wlock(&f->lk);
+	if(chksource(f) < 0){
+		wunlock(&f->lk);
+		return -1;
+	}
+	return 0;
+}
+
+static void
+fileunlock(VacFile *f)
+{
+	wunlock(&f->lk);
+}
+
+/*
+ * f->source and f->msource must NOT be locked.
+ * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
+ * We have to respect that ordering.
+ */
+static void
+filemetalock(VacFile *f)
+{
+	assert(f->up != nil);
+//	assert(!canwlock(&f->fs->elk));
+	wlock(&f->up->lk);
+}
+
+static void
+filemetaunlock(VacFile *f)
+{
+	wunlock(&f->up->lk);
+}
+
+/*
+ * f->source and f->msource must NOT be locked.
+ * see filemetalock.
+ */
+static void
+fileraccess(VacFile* f)
+{
+	if(f->mode == VtOREAD)
+		return;
+
+	filemetalock(f);
+	f->dir.atime = time(0L);
+	f->dirty = 1;
+	filemetaunlock(f);
+}
+
+/*
+ * f->source and f->msource must NOT be locked.
+ * see filemetalock.
+ */
+static void
+filewaccess(VacFile* f, char *mid)
+{
+	if(f->mode == VtOREAD)
+		return;
+
+	filemetalock(f);
+	f->dir.atime = f->dir.mtime = time(0L);
+	if(strcmp(f->dir.mid, mid) != 0){
+		vtfree(f->dir.mid);
+		f->dir.mid = vtstrdup(mid);
+	}
+	f->dir.mcount++;
+	f->dirty = 1;
+	filemetaunlock(f);
+
+/*RSC: let's try this */
+/*presotto - lets not
+	if(f->up)
+		filewaccess(f->up, mid);
+*/
+}
+
+#if 0
+static void
+markCopied(Block *b)
+{
+	VtBlock *lb;
+	Label l;
+
+	if(globalToLocal(b->score) == NilBlock)
+		return;
+
+	if(!(b->l.state & BsCopied)){
+		/*
+		 * We need to record that there are now pointers in
+		 * b that are not unique to b.  We do this by marking
+		 * b as copied.  Since we don't return the label block,
+		 * the caller can't get the dependencies right.  So we have
+		 * to flush the block ourselves.  This is a rare occurrence.
+		 */
+		l = b->l;
+		l.state |= BsCopied;
+		lb = _blockSetLabel(b, &l);
+	WriteAgain:
+		while(!blockWrite(lb)){
+			fprint(2, "getEntry: could not write label block\n");
+			sleep(10*1000);
+		}
+		while(lb->iostate != BioClean && lb->iostate != BioDirty){
+			assert(lb->iostate == BioWriting);
+			vtSleep(lb->ioready);
+		}
+		if(lb->iostate == BioDirty)
+			goto WriteAgain;
+		vtblockput(lb);
+	}
+}
+
+static int
+getEntry(VtFile *r, Entry *e, int mark)
+{
+	Block *b;
+
+	if(r == nil){
+		memset(&e, 0, sizeof e);
+		return 1;
+	}
+
+	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, VtOREAD);
+	if(b == nil)
+		return 0;
+	if(!entryUnpack(e, b->data, r->offset % r->epb)){
+		vtblockput(b);
+		return 0;
+	}
+
+	if(mark)
+		markCopied(b);
+	vtblockput(b);
+	return 1;
+}
+
+static int
+setEntry(Source *r, Entry *e)
+{
+	Block *b;
+	Entry oe;
+
+	b = cacheGlobal(r->fs->cache, r->score, BtDir, r->tag, VtORDWR);
+	if(0) fprint(2, "setEntry: b %#ux %d score=%V\n", b->addr, r->offset % r->epb, e->score);
+	if(b == nil)
+		return 0;
+	if(!entryUnpack(&oe, b->data, r->offset % r->epb)){
+		vtblockput(b);
+		return 0;
+	}
+	e->gen = oe.gen;
+	entryPack(e, b->data, r->offset % r->epb);
+
+	/* BUG b should depend on the entry pointer */
+
+	markCopied(b);
+	vtblockdirty(b);
+	vtblockput(b);
+	return 1;
+}
+
+/* assumes hold elk */
+int
+fileSnapshot(VacFile *dst, VacFile *src, u32int epoch, int doarchive)
+{
+	Entry e, ee;
+
+	/* add link to snapshot */
+	if(!getEntry(src->source, &e, 1) || !getEntry(src->msource, &ee, 1))
+		return 0;
+
+	e.snap = epoch;
+	e.archive = doarchive;
+	ee.snap = epoch;
+	ee.archive = doarchive;
+
+	if(!setEntry(dst->source, &e) || !setEntry(dst->msource, &ee))
+		return 0;
+	return 1;
+}
+
+int
+fileGetSources(VacFile *f, Entry *e, Entry *ee, int mark)
+{
+	if(!getEntry(f->source, e, mark)
+	|| !getEntry(f->msource, ee, mark))
+		return 0;
+	return 1;
+}	
+
+int
+fileWalkSources(VacFile *f)
+{
+	if(f->mode == VtOREAD)
+		return 1;
+	if(!sourceLock2(f->source, f->msource, VtORDWR))
+		return 0;
+	vtfileunlock(f->source);
+	vtfileunlock(f->msource);
+	return 1;
+}
+
+#endif
blob - f013f126dd9ee86e9508a5fa4befbb786a86de9b
blob + 335d85c96322a06dc0e0c17b04bb4b5e6f9e15a4
--- src/cmd/vac/fns.h
+++ src/cmd/vac/fns.h
@@ -1,46 +1,17 @@
-Source	*sourceAlloc(Cache*, Lump *u, ulong block, int elem, int readonly);
-Source 	*sourceOpen(Source*, ulong entry, int readOnly);
-Source 	*sourceCreate(Source*, int psize, int dsize, int isdir, ulong entry);
-Lump	*sourceGetLump(Source*, ulong block, int readOnly, int lock);
-Lump	*sourceWalk(Source *r, ulong block, int readOnly, int *);
-int	sourceSetDepth(Source *r, uvlong size);
-int	sourceSetSize(Source *r, uvlong size);
-uvlong	sourceGetSize(Source *r);
-int	sourceSetDirSize(Source *r, ulong size);
-ulong	sourceGetDirSize(Source *r);
-void	sourceRemove(Source*);
-void	sourceFree(Source*);
-int	sourceGetVtEntry(Source *r, VtEntry *dir);
-ulong	sourceGetNumBlocks(Source *r);
+int	mbunpack(MetaBlock *mb, uchar *p, int n);
+void	mbinsert(MetaBlock *mb, int i, MetaEntry*);
+void	mbdelete(MetaBlock *mb, int i, MetaEntry*);
+void	mbpack(MetaBlock *mb);
+uchar	*mballoc(MetaBlock *mb, int n);
 
-Lump	*lumpWalk(Lump *u, int offset, int type, int size, int readOnly, int lock);
-int	lumpGetScore(Lump *u, int offset, uchar score[VtScoreSize]);
-void	lumpDecRef(Lump*, int unlock);
-Lump	*lumpIncRef(Lump*);
-void	lumpFreeEntry(Lump *u, int entry);
+int	meunpack(MetaEntry*, MetaBlock *mb, int i);
+int	mecmp(MetaEntry*, char *s);
+int	mecmpnew(MetaEntry*, char *s);
 
-Cache 	*cacheAlloc(VtSession *z, int blockSize, long nblocks);
-Lump 	*cacheAllocLump(Cache *c, int type, int size, int dir);
-void	cacheFree(Cache *c);
-long	cacheGetSize(Cache*);
-int	cacheSetSize(Cache*, long);
-int	cacheGetBlockSize(Cache *c);
-Lump 	*cacheGetLump(Cache *c, uchar score[VtScoreSize], int type, int size);
-void	cacheCheck(Cache*);
+int	vdsize(VacDir *dir);
+int	vdunpack(VacDir *dir, MetaEntry*);
+void	vdpack(VacDir *dir, MetaEntry*);
 
-int	mbUnpack(MetaBlock *mb, uchar *p, int n);
-void	mbInsert(MetaBlock *mb, int i, MetaEntry*);
-void	mbDelete(MetaBlock *mb, int i, MetaEntry*);
-void	mbPack(MetaBlock *mb);
-uchar	*mbAlloc(MetaBlock *mb, int n);
+VacFile *_vacfileroot(VacFs *fs, VtFile *file);
 
-int	meUnpack(MetaEntry*, MetaBlock *mb, int i);
-int	meCmp(MetaEntry*, char *s);
-int	meCmpNew(MetaEntry*, char *s);
-
-int	vdSize(VacDir *dir);
-int	vdUnpack(VacDir *dir, MetaEntry*);
-void	vdPack(VacDir *dir, MetaEntry*);
-
-VacFile *vfRoot(VacFS *fs, uchar *score);
-
+int	_vacfsnextqid(VacFs *fs, uvlong *qid);
blob - 568e2ece36988c8c76c20342540d7871e522396c
blob + e6902d596f7ea5f3eaff69a34cf14004db5ca60d
--- src/cmd/vac/fs.c
+++ src/cmd/vac/fs.c
@@ -5,100 +5,107 @@
 
 static char EBadVacFormat[] = "bad format for vac file";
 
-static VacFS *
-vfsAlloc(VtSession *z, int bsize, long ncache)
+static VacFs *
+vacfsalloc(VtConn *z, int bsize, int ncache, int mode)
 {
-	VacFS *fs;
+	VacFs *fs;
 
-	fs = vtMemAllocZ(sizeof(VacFS));
+	fs = vtmallocz(sizeof(VacFs));
 	fs->ref = 1;
 	fs->z = z;
 	fs->bsize = bsize;
-	fs->cache = cacheAlloc(z, bsize, ncache);
+	fs->cache = vtcachealloc(z, bsize, ncache, mode);
 	return fs;
 }
 
 static int
-readScore(int fd, uchar score[VtScoreSize])
+readscore(int fd, uchar score[VtScoreSize])
 {
-	char buf[44];
-	int i, n, c;
+	char buf[45], *pref;
+	int n;
 
-	n = readn(fd, buf, sizeof(buf));
+	n = readn(fd, buf, sizeof(buf)-1);
 	if(n < sizeof(buf)) {
-		vtSetError("short read");
-		return 0;
+		werrstr("short read");
+		return -1;
 	}
-	if(strncmp(buf, "vac:", 4) != 0) {
-		vtSetError("not a vac file");
-		return 0;
+	buf[n] = 0;
+
+	if(vtparsescore(buf, &pref, score) < 0){
+		werrstr(EBadVacFormat);
+		return -1;
 	}
-	memset(score, 0, VtScoreSize);
-	for(i=4; i<sizeof(buf); i++) {
-		if(buf[i] >= '0' && buf[i] <= '9')
-			c = buf[i] - '0';
-		else if(buf[i] >= 'a' && buf[i] <= 'f')
-			c = buf[i] - 'a' + 10;
-		else if(buf[i] >= 'A' && buf[i] <= 'F')
-			c = buf[i] - 'A' + 10;
-		else {
-			vtSetError("bad format for venti score");
-			return 0;
-		}
-		if((i & 1) == 0)
-			c <<= 4;
-	
-		score[(i>>1)-2] |= c;
+	if(pref==nil || strcmp(pref, "vac") != 0) {
+		werrstr("not a vac file");
+		return -1;
 	}
-	return 1;
+	return 0;
 }
 
-VacFS *
-vfsOpen(VtSession *z, char *file, int readOnly, long ncache)
+VacFs*
+vacfsopen(VtConn *z, char *file, int mode, int ncache)
 {
-	VacFS *fs;
-	int n, fd;
-	VtRoot rt;
-	uchar score[VtScoreSize], buf[VtRootSize];
-	VacFile *root;
+	int fd;
+	uchar score[VtScoreSize];
 
 	fd = open(file, OREAD);
-	if(fd < 0) {
-		vtOSError();
+	if(fd < 0)
 		return nil;
-	}
 
-	if(!readScore(fd, score)) {
+	if(readscore(fd, score) < 0){
 		close(fd);
 		return nil;
 	}
 	close(fd);
 
-	n = vtRead(z, score, VtRootType, buf, VtRootSize);
+	return vacfsopenscore(z, score, mode, ncache);
+}
+
+VacFs*
+vacfsopenscore(VtConn *z, u8int *score, int mode, int ncache)
+{
+	VacFs *fs;
+	int n;
+	VtRoot rt;
+	uchar buf[VtRootSize];
+	VacFile *root;
+	VtFile *r;
+	VtEntry e;
+
+	n = vtread(z, score, VtRootType, buf, VtRootSize);
 	if(n < 0)
 		return nil;
-	if(n != VtRootSize) {
-		vtSetError("vtRead on root too short");
+	if(n != VtRootSize){
+		werrstr("vtread on root too short");
 		return nil;
 	}
 
-	if(!vtSha1Check(score, buf, VtRootSize)) {
-		vtSetError("vtSha1Check failed on root block");	
+	if(vtrootunpack(&rt, buf) < 0)
 		return nil;
-	}
 
-	if(!vtRootUnpack(&rt, buf))
-		return nil;
-
 	if(strcmp(rt.type, "vac") != 0) {
-		vtSetError("not a vac root");
+		werrstr("not a vac root");
 		return nil;
 	}
 
-	fs = vfsAlloc(z, rt.blockSize, ncache);
+	fs = vacfsalloc(z, rt.blocksize, ncache, mode);
 	memmove(fs->score, score, VtScoreSize);
-	fs->readOnly = readOnly;
-	root = vfRoot(fs, rt.score);
+	fs->mode = mode;
+
+	memmove(e.score, score, VtScoreSize);
+	e.gen = 0;
+	e.psize = (rt.blocksize/VtEntrySize)*VtEntrySize;
+	e.dsize = rt.blocksize;
+	e.type = VtDirType;
+	e.flags = VtEntryActive;
+	e.size = 3*VtEntrySize;
+
+	root = nil;
+	if((r = vtfileopenroot(fs->cache, &e)) == nil)
+		goto Err;
+
+	root = _vacfileroot(fs, r);
+	vtfileclose(r);
 	if(root == nil)
 		goto Err;
 	fs->root = root;
@@ -106,83 +113,63 @@ vfsOpen(VtSession *z, char *file, int readOnly, long n
 	return fs;
 Err:
 	if(root)
-		vfDecRef(root);
-	vfsClose(fs);
+		vacfiledecref(root);
+	vacfsclose(fs);
 	return nil;
 }
 
-VacFS *
-vacFsCreate(VtSession *z, int bsize, long ncache)
+VacFs *
+vacfscreate(VtConn *z, int bsize, int ncache)
 {
-	VacFS *fs;
-
-	fs = vfsAlloc(z, bsize, ncache);
-	return fs;
+	return vacfsalloc(z, bsize, ncache, VtORDWR);
 }
 
 int
-vfsIsReadOnly(VacFS *fs)
+vacfsmode(VacFs *fs)
 {
-	return fs->readOnly != 0;
+	return fs->mode;
 }
 
-VacFile *
-vfsGetRoot(VacFS *fs)
+VacFile*
+vacfsgetroot(VacFs *fs)
 {
-	return vfIncRef(fs->root);
+	return vacfileincref(fs->root);
 }
 
 int
-vfsGetBlockSize(VacFS *fs)
+vacfsgetblocksize(VacFs *fs)
 {
 	return fs->bsize;
 }
 
 int
-vfsGetScore(VacFS *fs, uchar score[VtScoreSize])
+vacfsgetscore(VacFs *fs, u8int *score)
 {
-	memmove(fs, score, VtScoreSize);
-	return 1;
+	memmove(score, fs->score, VtScoreSize);
+	return 0;
 }
 
-long
-vfsGetCacheSize(VacFS *fs)
-{
-	return cacheGetSize(fs->cache);
-}
-
 int
-vfsSetCacheSize(VacFS *fs, long size)
+_vacfsnextqid(VacFs *fs, uvlong *qid)
 {
-	return cacheSetSize(fs->cache, size);
+	++fs->qid;
+	*qid = fs->qid;
+	return 0;
 }
 
 int
-vfsSnapshot(VacFS *fs, char *src, char *dst)
+vacfssync(VacFs *fs)
 {
-	USED(fs);
-	USED(src);
-	USED(dst);
-	return 1;
+	return 0;
 }
 
-int
-vfsSync(VacFS*)
+void
+vacfsclose(VacFs *fs)
 {
-	return 1;
-}
-
-int
-vfsClose(VacFS *fs)
-{
 	if(fs->root)
-		vfDecRef(fs->root);
+		vacfiledecref(fs->root);
 	fs->root = nil;
-	cacheCheck(fs->cache);
-	cacheFree(fs->cache);
-	memset(fs, 0, sizeof(VacFS));
-	vtMemFree(fs);
-	return 1;
+	vtcachefree(fs->cache);
+	vtfree(fs);
 }
 
-
blob - ac33ab3207d4189829e20cb19f39b88067bcf7ab
blob + a4b7a29824e2142bf703a05c9bfe2fd9a42742a4
--- src/cmd/vac/mkfile
+++ src/cmd/vac/mkfile
@@ -2,13 +2,12 @@ PLAN9=../../..
 <$PLAN9/src/mkhdr
 
 LIBFILES=\
-	cache\
 	error\
 	file\
 	fs\
-	source\
 	pack\
 
+SHORTLIB=venti sec thread mux bio 9
 LIB=${LIBFILES:%=%.$O}
 
 HFILES=\
blob - b16834d9cc27ac4eea0552dd3b9e3686b9722fdf
blob + 074152ea431e68a55f1501ced0ddc248d4ac3c48
--- src/cmd/vac/pack.c
+++ src/cmd/vac/pack.c
@@ -12,7 +12,7 @@ struct MetaChunk {
 	ushort index;
 };
 
-static int	stringUnpack(char **s, uchar **p, int *n);
+static int	stringunpack(char **s, uchar **p, int *n);
 
 /*
  * integer conversion routines
@@ -23,35 +23,35 @@ static int	stringUnpack(char **s, uchar **p, int *n);
 #define	U48GET(p)	(((uvlong)U16GET(p)<<32)|(uvlong)U32GET((p)+2))
 #define	U64GET(p)	(((uvlong)U32GET(p)<<32)|(uvlong)U32GET((p)+4))
 
-#define	U8PUT(p,v)	(p)[0]=(v)
-#define	U16PUT(p,v)	(p)[0]=(v)>>8;(p)[1]=(v)
-#define	U32PUT(p,v)	(p)[0]=(v)>>24;(p)[1]=(v)>>16;(p)[2]=(v)>>8;(p)[3]=(v)
+#define	U8PUT(p,v)	(p)[0]=(v)&0xFF
+#define	U16PUT(p,v)	(p)[0]=((v)>>8)&0xFF;(p)[1]=(v)&0xFF
+#define	U32PUT(p,v)	(p)[0]=((v)>>24)&0xFF;(p)[1]=((v)>>16)&0xFF;(p)[2]=((v)>>8)&0xFF;(p)[3]=(v)&0xFF
 #define	U48PUT(p,v,t32)	t32=(v)>>32;U16PUT(p,t32);t32=(v);U32PUT((p)+2,t32)
 #define	U64PUT(p,v,t32)	t32=(v)>>32;U32PUT(p,t32);t32=(v);U32PUT((p)+4,t32)
 
 static int
-stringUnpack(char **s, uchar **p, int *n)
+stringunpack(char **s, uchar **p, int *n)
 {
 	int nn;
 
 	if(*n < 2)
-		return 0;
+		return -1;
 	
 	nn = U16GET(*p);
 	*p += 2;
 	*n -= 2;
 	if(nn > *n)
-		return 0;
-	*s = vtMemAlloc(nn+1);
+		return -1;
+	*s = vtmalloc(nn+1);
 	memmove(*s, *p, nn);
 	(*s)[nn] = 0;
 	*p += nn;
 	*n -= nn;
-	return 1;
+	return 0;
 }
 
 static int
-stringPack(char *s, uchar *p)
+stringpack(char *s, uchar *p)
 {
 	int n;
 
@@ -63,7 +63,7 @@ stringPack(char *s, uchar *p)
 
 
 int
-mbUnpack(MetaBlock *mb, uchar *p, int n)
+mbunpack(MetaBlock *mb, uchar *p, int n)
 {
 	u32int magic;
 
@@ -72,13 +72,13 @@ mbUnpack(MetaBlock *mb, uchar *p, int n)
 
 	if(n == 0) {
 		memset(mb, 0, sizeof(MetaBlock));
-		return 1;
+		return 0;
 	}
 
 	magic = U32GET(p);
 	if(magic != MetaMagic && magic != MetaMagic+1) {
-		vtSetError("bad meta block magic");
-		return 0;
+		werrstr("bad meta block magic");
+		return -1;
 	}
 	mb->size = U16GET(p+4);
 	mb->free = U16GET(p+6);
@@ -87,22 +87,22 @@ mbUnpack(MetaBlock *mb, uchar *p, int n)
 	mb->unbotch = (magic == MetaMagic+1);
 
 	if(mb->size > n) {
-		vtSetError("bad meta block size");
-		return 0;
+		werrstr("bad meta block size");
+		return -1;
 	}
 	p += MetaHeaderSize;
 	n -= MetaHeaderSize;
 
 	USED(p);
 	if(n < mb->maxindex*MetaIndexSize) {
- 		vtSetError("truncated meta block 2");
-		return 0;
+ 		werrstr("truncated meta block 2");
+		return -1;
 	}
-	return 1;
+	return 0;
 }
 
 void
-mbPack(MetaBlock *mb)
+mbpack(MetaBlock *mb)
 {
 	uchar *p;
 
@@ -117,7 +117,7 @@ mbPack(MetaBlock *mb)
 
 
 void
-mbDelete(MetaBlock *mb, int i, MetaEntry *me)
+mbdelete(MetaBlock *mb, int i, MetaEntry *me)
 {
 	uchar *p;
 	int n;
@@ -137,7 +137,7 @@ mbDelete(MetaBlock *mb, int i, MetaEntry *me)
 }
 
 void
-mbInsert(MetaBlock *mb, int i, MetaEntry *me)
+mbinsert(MetaBlock *mb, int i, MetaEntry *me)
 {
 	uchar *p;
 	int o, n;
@@ -161,14 +161,14 @@ mbInsert(MetaBlock *mb, int i, MetaEntry *me)
 }
 
 int
-meUnpack(MetaEntry *me, MetaBlock *mb, int i)
+meunpack(MetaEntry *me, MetaBlock *mb, int i)
 {
 	uchar *p;
 	int eo, en;
 
 	if(i < 0 || i >= mb->nindex) {
-		vtSetError("bad meta entry index");
-		return 0;
+		werrstr("bad meta entry index");
+		return -1;
 	}
 
 	p = mb->buf + MetaHeaderSize + i*MetaIndexSize;
@@ -177,32 +177,32 @@ meUnpack(MetaEntry *me, MetaBlock *mb, int i)
 
 if(0)print("eo = %d en = %d\n", eo, en);
 	if(eo < MetaHeaderSize + mb->maxindex*MetaIndexSize) {
-		vtSetError("corrupted entry in meta block");
-		return 0;
+		werrstr("corrupted entry in meta block");
+		return -1;
 	}
 
 	if(eo+en > mb->size) {
- 		vtSetError("truncated meta block");
-		return 0;
+ 		werrstr("truncated meta block");
+		return -1;
 	}
 
 	p = mb->buf + eo;
 	
 	/* make sure entry looks ok and includes an elem name */
 	if(en < 8 || U32GET(p) != DirMagic || en < 8 + U16GET(p+6)) {
-		vtSetError("corrupted meta block entry");
-		return 0;
+		werrstr("corrupted meta block entry");
+		return -1;
 	}
 
 	me->p = p;
 	me->size = en;
 
-	return 1;
+	return 0;
 }
 
 /* assumes a small amount of checking has been done in mbEntry */
 int
-meCmp(MetaEntry *me, char *s)
+mecmp(MetaEntry *me, char *s)
 {
 	int n;
 	uchar *p;
@@ -230,7 +230,7 @@ meCmp(MetaEntry *me, char *s)
 }
 
 int
-meCmpNew(MetaEntry *me, char *s)
+mecmpnew(MetaEntry *me, char *s)
 {
 	int n;
 	uchar *p;
@@ -258,9 +258,9 @@ meCmpNew(MetaEntry *me, char *s)
 }
 
 static int
-offsetCmp(void *s0, void *s1)
+offsetcmp(const void *s0, const void *s1)
 {
-	MetaChunk *mc0, *mc1;
+	const MetaChunk *mc0, *mc1;
 
 	mc0 = s0;
 	mc1 = s1;
@@ -272,13 +272,13 @@ offsetCmp(void *s0, void *s1)
 }
 
 static MetaChunk *
-metaChunks(MetaBlock *mb)
+metachunks(MetaBlock *mb)
 {
 	MetaChunk *mc;
 	int oo, o, n, i;
 	uchar *p;
 
-	mc = vtMemAlloc(mb->nindex*sizeof(MetaChunk));
+	mc = vtmalloc(mb->nindex*sizeof(MetaChunk));
 	p = mb->buf + MetaHeaderSize;
 	for(i = 0; i<mb->nindex; i++) {
 		mc[i].offset = U16GET(p);
@@ -287,7 +287,7 @@ metaChunks(MetaBlock *mb)
 		p += MetaIndexSize;
 	}
 
-	qsort(mc, mb->nindex, sizeof(MetaChunk), offsetCmp);
+	qsort(mc, mb->nindex, sizeof(MetaChunk), offsetcmp);
 
 	/* check block looks ok */
 	oo = MetaHeaderSize + mb->maxindex*MetaIndexSize;
@@ -307,12 +307,12 @@ metaChunks(MetaBlock *mb)
 
 	return mc;
 Err:
-	vtMemFree(mc);
+	vtfree(mc);
 	return nil;
 }
 
 static void
-mbCompact(MetaBlock *mb, MetaChunk *mc)
+mbcompact(MetaBlock *mb, MetaChunk *mc)
 {
 	int oo, o, n, i;
 
@@ -333,7 +333,7 @@ mbCompact(MetaBlock *mb, MetaChunk *mc)
 }
 
 uchar *
-mbAlloc(MetaBlock *mb, int n)
+mballoc(MetaBlock *mb, int n)
 {
 	int i, o;
 	MetaChunk *mc;
@@ -346,33 +346,33 @@ mbAlloc(MetaBlock *mb, int n)
 	if(mb->maxsize - mb->size + mb->free < n)
 		return nil;
 
-	mc = metaChunks(mb);
+	mc = metachunks(mb);
 
 	/* look for hole */
 	o = MetaHeaderSize + mb->maxindex*MetaIndexSize;
 	for(i=0; i<mb->nindex; i++) {
 		if(mc[i].offset - o >= n) {
-			vtMemFree(mc);
+			vtfree(mc);
 			return mb->buf + o;
 		}
 		o = mc[i].offset + mc[i].size;
 	}
 
 	if(mb->maxsize - o >= n) {
-		vtMemFree(mc);
+		vtfree(mc);
 		return mb->buf + o;
 	}
 
 	/* compact and return off the end */
-	mbCompact(mb, mc);
-	vtMemFree(mc);
+	mbcompact(mb, mc);
+	vtfree(mc);
 
 	assert(mb->maxsize - mb->size >= n);
 	return mb->buf + mb->size;
 }
 
 int
-vdSize(VacDir *dir)
+vdsize(VacDir *dir)
 {
 	int n;
 	
@@ -399,17 +399,17 @@ vdSize(VacDir *dir)
 	n += 2 + strlen(dir->mid);
 
 	/* optional sections */
-	if(dir->qidSpace) {
+	if(dir->qidspace) {
 		n += 	3 + 	/* option header */
-			8 + 	/* qidOffset */
-			8;	/* qid Max */
+			8 + 	/* qid offset */
+			8;	/* qid max */
 	}
 
 	return n;
 }
 
 void
-vdPack(VacDir *dir, MetaEntry *me)
+vdpack(VacDir *dir, MetaEntry *me)
 {
 	uchar *p;
 	ulong t32;
@@ -420,7 +420,7 @@ vdPack(VacDir *dir, MetaEntry *me)
 	U16PUT(p+4, 9);		/* version */
 	p += 6;
 
-	p += stringPack(dir->elem, p);
+	p += stringpack(dir->elem, p);
 
 	U32PUT(p, dir->entry);
 	U32PUT(p+4, dir->gen);
@@ -429,9 +429,9 @@ vdPack(VacDir *dir, MetaEntry *me)
 	U64PUT(p+16, dir->qid, t32);
 	p += 24;
 
-	p += stringPack(dir->uid, p);
-	p += stringPack(dir->gid, p);
-	p += stringPack(dir->mid, p);
+	p += stringpack(dir->uid, p);
+	p += stringpack(dir->gid, p);
+	p += stringpack(dir->mid, p);
 	
 	U32PUT(p, dir->mtime);
 	U32PUT(p+4, dir->mcount);
@@ -440,12 +440,12 @@ vdPack(VacDir *dir, MetaEntry *me)
 	U32PUT(p+16, dir->mode);
 	p += 5*4;
 
-	if(dir->qidSpace) {
+	if(dir->qidspace) {
 		U8PUT(p, DirQidSpaceEntry);
 		U16PUT(p+1, 2*8);
 		p += 3;
-		U64PUT(p, dir->qidOffset, t32);
-		U64PUT(p+8, dir->qidMax, t32);
+		U64PUT(p, dir->qidoffset, t32);
+		U64PUT(p+8, dir->qidmax, t32);
 	}
 
 	assert(p == me->p + me->size);
@@ -453,7 +453,7 @@ vdPack(VacDir *dir, MetaEntry *me)
 
 
 int
-vdUnpack(VacDir *dir, MetaEntry *me)
+vdunpack(VacDir *dir, MetaEntry *me)
 {
 	int t, nn, n, version;
 	uchar *p;
@@ -483,7 +483,7 @@ if(0)print("vdUnpack: got magic\n");
 if(0)print("vdUnpack: got version\n");
 
 	/* elem */
-	if(!stringUnpack(&dir->elem, &p, &n))
+	if(stringunpack(&dir->elem, &p, &n) < 0)
 		goto Err;
 
 if(0)print("vdUnpack: got elem\n");
@@ -532,15 +532,15 @@ if(0)print("vdUnpack: got qid\n");
 	}
 	
 	/* uid */
-	if(!stringUnpack(&dir->uid, &p, &n))
+	if(stringunpack(&dir->uid, &p, &n) < 0)
 		goto Err;
 
 	/* gid */
-	if(!stringUnpack(&dir->gid, &p, &n))
+	if(stringunpack(&dir->gid, &p, &n) < 0)
 		goto Err;
 
 	/* mid */
-	if(!stringUnpack(&dir->mid, &p, &n))
+	if(stringunpack(&dir->mid, &p, &n) < 0)
 		goto Err;
 
 if(0)print("vdUnpack: got ids\n");
@@ -584,11 +584,11 @@ if(0)print("vdUnpack: got times\n");
 				break;
 			break;
 		case DirQidSpaceEntry:
-			if(dir->qidSpace || nn != 16)
+			if(dir->qidspace || nn != 16)
 				goto Err;
-			dir->qidSpace = 1;
-			dir->qidOffset = U64GET(p);
-			dir->qidMax = U64GET(p+8);
+			dir->qidspace = 1;
+			dir->qidoffset = U64GET(p);
+			dir->qidmax = U64GET(p+8);
 			break;
 		}
 		p += nn;
@@ -600,10 +600,112 @@ if(0)print("vdUnpack: got options\n");
 		goto Err;
 
 if(0)print("vdUnpack: correct size\n");
-	return 1;
+	return 0;
 Err:
 if(0)print("vdUnpack: XXXXXXXXXXXX EbadMeta\n");
-	vtSetError(EBadMeta);
-	vdCleanup(dir);
-	return 0;
+	werrstr(EBadMeta);
+	vdcleanup(dir);
+	return -1;
 }
+
+void
+vdcleanup(VacDir *dir)
+{
+	vtfree(dir->elem);
+	dir->elem = nil;
+	vtfree(dir->uid);
+	dir->uid = nil;
+	vtfree(dir->gid);
+	dir->gid = nil;
+	vtfree(dir->mid);
+	dir->mid = nil;
+}
+
+void
+vdcopy(VacDir *dst, VacDir *src)
+{
+	*dst = *src;
+	dst->elem = vtstrdup(dst->elem);
+	dst->uid = vtstrdup(dst->uid);
+	dst->gid = vtstrdup(dst->gid);
+	dst->mid = vtstrdup(dst->mid);
+}
+
+int
+mbsearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me)
+{
+	int i;
+	int b, t, x;
+
+	/* binary search within block */
+	b = 0;
+	t = mb->nindex;
+	while(b < t) {
+		i = (b+t)>>1;
+		if(meunpack(me, mb, i) < 0)
+			return 0;
+		if(mb->unbotch)
+			x = mecmpnew(me, elem);
+		else
+			x = mecmp(me, elem);
+
+		if(x == 0) {
+			*ri = i;
+			return 1;
+		}
+	
+		if(x < 0)
+			b = i+1;
+		else /* x > 0 */
+			t = i;
+	}
+
+	assert(b == t);
+	
+	*ri = b;	/* b is the index to insert this entry */
+	memset(me, 0, sizeof(*me));
+
+	return 1;
+}
+
+void
+mbinit(MetaBlock *mb, uchar *p, int n)
+{
+	memset(mb, 0, sizeof(MetaBlock));
+	mb->maxsize = n;
+	mb->buf = p;
+	mb->maxindex = n/100;
+	mb->size = MetaHeaderSize + mb->maxindex*MetaIndexSize;
+}
+
+int
+mbresize(MetaBlock *mb, MetaEntry *me, int n)
+{
+	uchar *p, *ep;
+
+	/* easy case */
+	if(n <= me->size){
+		me->size = n;
+		return 0;
+	}
+
+	/* try and expand entry */
+
+	p = me->p + me->size;
+	ep = mb->buf + mb->maxsize;
+	while(p < ep && *p == 0)
+		p++;
+	if(n <= p - me->p){
+		me->size = n;
+		return 0;
+	}
+
+	p = mballoc(mb, n);
+	if(p != nil){
+		me->p = p;
+		me->size = n;
+		return 0;
+	}
+
+	return -1;
+}
blob - e7245a2fffdb8e195e752fbc9642dfd820b8fb24 (mode 644)
blob + /dev/null
--- src/cmd/vac/source.c
+++ /dev/null
@@ -1,390 +0,0 @@
-#include "stdinc.h"
-#include "vac.h"
-#include "dat.h"
-#include "fns.h"
-#include "error.h"
-
-static int	sizeToDepth(uvlong s, int psize, int dsize);
-
-static int
-sizeToDepth(uvlong s, int psize, int dsize)
-{
-	int np;
-	int d;
-	
-	/* determine pointer depth */
-	np = psize/VtScoreSize;
-	s = (s + dsize - 1)/dsize;
-	for(d = 0; s > 1; d++)
-		s = (s + np - 1)/np;
-	return d;
-}
-
-/* assumes u is lock? */
-Source *
-sourceAlloc(Cache *c, Lump *u, ulong block, int entry, int readOnly)
-{
-	Source *r;
-	VtEntry d;
-
-	if(u->asize < (entry+1)*VtEntrySize) {
-		vtSetError(ENoDir);
-		return nil;
-	}
-
-	if(!vtEntryUnpack(&d, u->data, entry))
-		return nil;
-	
-	if(!(d.flags & VtEntryActive)) {
-fprint(2, "bad flags %#ux %V\n", d.flags, d.score);
-		vtSetError(ENoDir);
-		return nil;
-	}
-	
-	/* HACK for backwards compatiblity - should go away at some point */
-	if(d.depth == 0) {
-if(d.size > d.dsize) fprint(2, "depth == 0! size = %ulld\n", d.size);
-		d.depth = sizeToDepth(d.size, d.psize, d.dsize);
-	}
-
-	if(d.depth < sizeToDepth(d.size, d.psize, d.dsize)) {
-		vtSetError(EBadDir);
-		return nil;
-	}
-
-	r = vtMemAllocZ(sizeof(Source));
-	r->lk = vtLockAlloc();
-	r->cache = c;
-	r->readOnly = readOnly;
-	r->lump = lumpIncRef(u);
-	r->block = block;
-	r->entry = entry;
-	r->gen = d.gen;
-	r->dir = (d.flags & VtEntryDir) != 0;
-	r->depth = d.depth;
-	r->psize = d.psize;
-	r->dsize = d.dsize;
-	r->size = d.size;
-
-	r->epb = r->dsize/VtEntrySize;
-
-	return r;
-}
-
-Source *
-sourceOpen(Source *r, ulong entry, int readOnly)
-{
-	ulong bn;
-	Lump *u;
-
-if(0)fprint(2, "sourceOpen: %V:%d: %lud\n", r->lump->score, r->entry, entry);
-	if(r->readOnly && !readOnly) {
-		vtSetError(EReadOnly);
-		return nil;
-	}
-
-	bn = entry/r->epb;
-
-	u = sourceGetLump(r, bn, readOnly, 1);
-	if(u == nil)
-		return nil;
-
-	r = sourceAlloc(r->cache, u, bn, entry%r->epb, readOnly);
-	lumpDecRef(u, 1);
-	return r;
-}
-
-Source *
-sourceCreate(Source *r, int psize, int dsize, int isdir, ulong entry)
-{
-	Source *rr;
-	int i;
-	Lump *u;
-	ulong bn;
-	VtEntry dir;
-
-	if(r->readOnly) {
-		vtSetError(EReadOnly);
-		return nil;
-	}
-
-	if(entry == 0) {
-		/*
-		 * look at a random block to see if we can find an empty entry
-		 */
-		entry = sourceGetDirSize(r);
-		entry = r->epb*lnrand(entry/r->epb+1);
-	}
-
-	/*
-	 * need to loop since multiple threads could be trying to allocate
-	 */
-	for(;;) {
-		bn = entry/r->epb;
-		sourceSetDepth(r, (uvlong)(bn+1)*r->dsize);
-		u = sourceGetLump(r, bn, 0, 1);
-		if(u == nil)
-			return nil;
-		for(i=entry%r->epb; i<r->epb; i++) {
-			vtEntryUnpack(&dir, u->data, i);
-			if((dir.flags&VtEntryActive) == 0 && dir.gen != ~0)
-				goto Found;
-		}
-		lumpDecRef(u, 1);
-		entry = sourceGetDirSize(r);
-	}
-Found:
-	/* found an entry */
-	dir.psize = psize;
-	dir.dsize = dsize;
-	dir.flags = VtEntryActive;
-	if(isdir)
-		dir.flags |= VtEntryDir;
-	dir.depth = 0;
-	dir.size = 0;
-	memmove(dir.score, vtZeroScore, VtScoreSize);
-	vtEntryPack(&dir, u->data, i);
-
-	sourceSetDirSize(r, bn*r->epb + i + 1);
-	rr = sourceAlloc(r->cache, u, bn, i, 0);
-	
-	lumpDecRef(u, 1);
-	return rr;
-}
-
-void
-sourceRemove(Source *r)
-{
-	lumpFreeEntry(r->lump, r->entry);
-	sourceFree(r);
-}
-
-int
-sourceSetDepth(Source *r, uvlong size)
-{
-	Lump *u, *v;
-	VtEntry dir;
-	int depth;
-
-	if(r->readOnly){
-		vtSetError(EReadOnly);
-		return 0;
-	}
-
-	depth = sizeToDepth(size, r->psize, r->dsize);
-
-	assert(depth >= 0);
-
-	if(depth > VtPointerDepth) {
-		vtSetError(ETooBig);
-		return 0;
-	}
-
-	vtLock(r->lk);
-
-	if(r->depth >= depth) {
-		vtUnlock(r->lk);
-		return 1;
-	}
-	
-	u = r->lump;
-	vtLock(u->lk);
-	if(!vtEntryUnpack(&dir, u->data, r->entry)) {
-		vtUnlock(u->lk);
-		vtUnlock(r->lk);
-		return 0;
-	}
-	while(dir.depth < depth) {
-		v = cacheAllocLump(r->cache, VtPointerType0+r->depth, r->psize, r->dir);
-		if(v == nil)
-			break;
-		memmove(v->data, dir.score, VtScoreSize);
-		memmove(dir.score, v->score, VtScoreSize);
-		dir.depth++;
-		vtUnlock(v->lk);
-	}
-	vtEntryPack(&dir, u->data, r->entry);
-	vtUnlock(u->lk);
-
-	r->depth = dir.depth;
-	vtUnlock(r->lk);
-
-	return dir.depth == depth;
-}
-
-int
-sourceGetVtEntry(Source *r, VtEntry *dir)
-{
-	Lump *u;
-
-	u = r->lump;
-	vtLock(u->lk);
-	if(!vtEntryUnpack(dir, u->data, r->entry)) {
-		vtUnlock(u->lk);
-		return 0;
-	}
-	vtUnlock(u->lk);
-	return 1;
-}
-
-uvlong
-sourceGetSize(Source *r)
-{
-	uvlong size;
-
-	vtLock(r->lk);
-	size = r->size;
-	vtUnlock(r->lk);
-
-	return size;
-}
-
-
-int
-sourceSetSize(Source *r, uvlong size)
-{
-	Lump *u;
-	VtEntry dir;
-	int depth;
-
-	if(r->readOnly) {
-		vtSetError(EReadOnly);
-		return 0;
-	}
-
-	if(size > VtMaxFileSize || size > ((uvlong)MaxBlock)*r->dsize) {
-		vtSetError(ETooBig);
-		return 0;
-	}
-
-	vtLock(r->lk);
-	depth = sizeToDepth(size, r->psize, r->dsize);
-	if(size < r->size) {
-		vtUnlock(r->lk);
-		return 1;
-	}
-	if(depth > r->depth) {
-		vtSetError(EBadDir);
-		vtUnlock(r->lk);
-		return 0;
-	}
-	
-	u = r->lump;
-	vtLock(u->lk);
-	vtEntryUnpack(&dir, u->data, r->entry);
-	dir.size = size;
-	vtEntryPack(&dir, u->data, r->entry);
-	vtUnlock(u->lk);
-	r->size = size;
-	vtUnlock(r->lk);
-	return 1;
-}
-
-int
-sourceSetDirSize(Source *r, ulong ds)
-{
-	uvlong size;
-
-	size = (uvlong)r->dsize*(ds/r->epb);
-	size += VtEntrySize*(ds%r->epb);
-	return sourceSetSize(r, size);
-}
-
-ulong
-sourceGetDirSize(Source *r)
-{
-	ulong ds;
-	uvlong size;
-
-	size = sourceGetSize(r);
-	ds = r->epb*(size/r->dsize);
-	ds += (size%r->dsize)/VtEntrySize;
-	return ds;
-}
-
-ulong
-sourceGetNumBlocks(Source *r)
-{
-	return (sourceGetSize(r)+r->dsize-1)/r->dsize;
-}
-
-Lump *
-sourceWalk(Source *r, ulong block, int readOnly, int *off)
-{
-	int depth;
-	int i, np;
-	Lump *u, *v;
-	int elem[VtPointerDepth+1];
-	ulong b;
-
-	if(r->readOnly && !readOnly) {
-		vtSetError(EReadOnly);
-		return nil;
-	}
-
-	vtLock(r->lk);
-	np = r->psize/VtScoreSize;
-	b = block;
-	for(i=0; i<r->depth; i++) {
-		elem[i] = b % np;
-		b /= np;
-	}
-	if(b != 0) {
-		vtUnlock(r->lk);
-		vtSetError(EBadOffset);
-		return nil;
-	}
-	elem[i] = r->entry;
-	u = lumpIncRef(r->lump);
-	depth = r->depth;
-	*off = elem[0];
-	vtUnlock(r->lk);
-
-	for(i=depth; i>0; i--) {
-		v = lumpWalk(u, elem[i], VtPointerType0+i-1, r->psize, readOnly, 0);
-		lumpDecRef(u, 0);
-		if(v == nil)
-			return nil;
-		u = v;
-	}
-
-	return u;
-}
-
-Lump *
-sourceGetLump(Source *r, ulong block, int readOnly, int lock)
-{
-	int type, off;
-	Lump *u, *v;
-
-	if(r->readOnly && !readOnly) {
-		vtSetError(EReadOnly);
-		return nil;
-	}
-	if(block == NilBlock) {
-		vtSetError(ENilBlock);
-		return nil;
-	}
-if(0)fprint(2, "sourceGetLump: %V:%d %lud\n", r->lump->score, r->entry, block);
-	u = sourceWalk(r, block, readOnly, &off);
-	if(u == nil)
-		return nil;
-	if(r->dir)
-		type = VtDirType;
-	else
-		type = VtDataType;
-	v = lumpWalk(u, off, type, r->dsize, readOnly, lock);
-	lumpDecRef(u, 0);
-	return v;
-}
-
-void
-sourceFree(Source *k)
-{
-	if(k == nil)
-		return;
-	lumpDecRef(k->lump, 0);
-	vtLockFree(k->lk);
-	memset(k, ~0, sizeof(*k));
-	vtMemFree(k);
-}
blob - aca405fc52c8bc4559fe989552538f534b9153ef
blob + bf72d3fde9902ff355846e27dbf8b7f6af6d0d75
--- src/cmd/vac/stdinc.h
+++ src/cmd/vac/stdinc.h
@@ -1,8 +1,5 @@
 #include <u.h>
 #include <libc.h>
-
-#include "venti.h"
-
-typedef uvlong	u64int;
-typedef	uchar	u8int;
-typedef ushort	u16int;
+#include <thread.h>
+#include <venti.h>
+#include <libsec.h>
blob - 8153c37b3f20919b76fdaddb379c6380db339aea
blob + f6efb917114a5736bdfed1cd41b029a823559a18
--- src/cmd/vac/vac.c
+++ src/cmd/vac/vac.c
@@ -1,30 +1,174 @@
-#include <u.h>
-#include <libc.h>
-#include <venti.h>
+#include "stdinc.h"
+#include "vac.h"
+#include "dat.h"
+#include "fns.h"
 
-int bsize;
-char *host;
-VtConn *z;
+typedef struct Sink Sink;
+typedef struct MetaSink MetaSink;
+typedef struct DirSink DirSink;
 
-void
+struct Sink {
+	VtConn *z;
+	VtEntry dir;
+	uchar *buf;
+	uchar *pbuf[VtPointerDepth+1];
+};
+
+struct DirSink {
+	Sink *sink;
+	MetaSink *msink;
+	ulong nentry;
+	uchar *buf;
+	uchar *p;	/* current pointer */
+	uchar *ep;	/* end pointer */
+};
+
+struct MetaSink {
+	Sink *sink;
+	uchar *buf;
+	int maxindex;
+	int nindex;
+	uchar *rp;	/* start of current record */
+	uchar *p;	/* current pointer */
+	uchar *ep;	/* end pointer */
+};
+
+static void usage(void);
+static int strpcmp(const void*, const void*);
+static void warn(char *fmt, ...);
+static void cleanup(void);
+static u64int unittoull(char *s);
+static void vac(VtConn *z, char *argv[]);
+static void vacfile(DirSink *dsink, char *lname, char *sname, VacFile*);
+static void vacstdin(DirSink *dsink, char *name, VacFile *vf);
+static void vacdata(DirSink *dsink, int fd, char *lname, VacFile*, Dir*);
+static void vacdir(DirSink *dsink, int fd, char *lname, char *sname, VacFile*);
+static int vacmerge(DirSink *dsink, char *lname, char *sname);
+
+Sink *sinkalloc(VtConn *z, int psize, int dsize);
+void sinkwrite(Sink *k, uchar *data, int n);
+void sinkwritescore(Sink *k, uchar *score, int n);
+void sinkclose(Sink *k);
+void sinkfree(Sink *k);
+
+DirSink *dirsinkalloc(VtConn *z, int psize, int dsize);
+void dirsinkwrite(DirSink *k, VtEntry*);
+void dirsinkwritesink(DirSink *k, Sink*);
+int dirsinkwritefile(DirSink *k, VacFile *vf);
+void dirsinkclose(DirSink *k);
+void dirsinkfree(DirSink *k);
+
+MetaSink *metasinkalloc(VtConn *z, int psize, int dsize);
+void metasinkputc(MetaSink *k, int c);
+void metasinkputstring(MetaSink *k, char *s);
+void metasinkputuint32(MetaSink *k, ulong x);
+void metasinkputuint64(MetaSink *k, uvlong x);
+void metasinkwrite(MetaSink *k, uchar *data, int n);
+void metasinkwritedir(MetaSink *ms, VacDir *vd);
+void metasinkeor(MetaSink *k);
+void metasinkclose(MetaSink *k);
+void metasinkfree(MetaSink *k);
+void plan9tovacdir(VacDir*, Dir*, ulong entry, uvlong qid);
+
+enum {
+	Version = 8,
+	BlockSize = 8*1024,
+	MaxExclude = 1000,
+};
+
+struct {
+	ulong	file;
+	ulong	sfile;
+	ulong	data;
+	ulong	sdata;
+	ulong	skip;
+	ulong	meta;
+} stats;
+
+int bsize = BlockSize;
+int maxbsize;
+char *oname, *dfile;
+int verbose;
+uvlong fileid = 1;
+int qdiff;
+char *exclude[MaxExclude];
+int nexclude;
+int nowrite;
+int merge;
+char *isi;
+
+static void
 usage(void)
 {
-	fprint(2, "usage: vac [-b blocksize] [-h host] file\n");
+	fprint(2, "usage: %s [-amqsv] [-h host] [-d vacfile] [-b blocksize] [-i name] [-e exclude] [-f vacfile] file ... \n", argv0);
+	exits("usage");
 }
 
 void
-main(int argc, char *argv[])
+threadmain(int argc, char *argv[])
 {
+	VtConn *z;
+	char *p;
+	char *host = nil;
+	int statsflag = 0;
+
+	atexit(cleanup);
+
 	ARGBEGIN{
 	default:
 		usage();
 	case 'b':
-		bsize = unittoull(EARGF(usage()));
+		p = ARGF();
+		if(p == 0)
+			usage();
+		bsize = unittoull(p);
+		if(bsize == ~0)
+			usage();
 		break;
+	case 'd':
+		dfile = ARGF();
+		if(dfile == nil)
+			usage();
+		break;
+	case 'e':
+		if(nexclude >= MaxExclude)
+			sysfatal("too many exclusions\n");
+		exclude[nexclude] = ARGF();
+		if(exclude[nexclude] == nil)
+			usage();
+		nexclude++;
+		break;
+	case 'f':
+		oname = ARGF();
+		if(oname == 0)
+			usage();
+		break;
 	case 'h':
-		host = EARGF(usage());
+		host = ARGF();
+		if(host == nil)
+			usage();
 		break;
-	}ARGEND
+	case 'i':
+		isi = ARGF();
+		if(isi == nil)
+			usage();
+		break;
+	case 'n':
+		nowrite++;
+		break;
+	case 'm':
+		merge++;
+		break;
+	case 'q':
+		qdiff++;
+		break;
+	case 's':
+		statsflag++;
+		break;
+	case 'v':
+		verbose++;
+		break;
+	}ARGEND;
 
 	if(bsize < 512)
 		bsize = 512;
@@ -32,45 +176,73 @@ main(int argc, char *argv[])
 		bsize = VtMaxLumpSize;
 	maxbsize = bsize;
 
-	vtAttach();
+	fmtinstall('V', vtscorefmt);
 
-	fmtinstall('V', vtScoreFmt);
-	fmtinstall('R', vtErrFmt);
-
-	z = vtDial(host, 0);
+	z = vtdial(host);
 	if(z == nil)
-		vtFatal("could not connect to server: %R");
+		sysfatal("could not connect to server: %r");
 
-	if(!vtConnect(z, 0))
-		vtFatal("vtConnect: %R");
+	if(vtconnect(z) < 0)
+		sysfatal("vtconnect: %r");
 
-	qsort(exclude, nexclude, sizeof(char*), strpCmp);
+	qsort(exclude, nexclude, sizeof(char*), strpcmp);
 
 	vac(z, argv);
-	if(!vtSync(z))
+
+	if(vtsync(z) < 0)
 		fprint(2, "warning: could not ask server to flush pending writes: %R\n");
 
-	if(statsFlag)
+	if(statsflag)
 		fprint(2, "files %ld:%ld data %ld:%ld:%ld meta %ld\n", stats.file, stats.sfile,
 			stats.data, stats.skip, stats.sdata, stats.meta);
 //packetStats();
-	vtClose(z);
-	vtDetach();
+	vthangup(z);
 
-	exits(0);
+	threadexitsall(0);
 }
 
 static int
-vac(VtSession *z, char *argv[])
+strpcmp(const void *p0, const void *p1)
+{
+	return strcmp(*(char**)p0, *(char**)p1);
+}
+
+int
+vacwrite(VtConn *z, uchar score[VtScoreSize], int type, uchar *buf, int n)
+{
+	assert(n > 0);
+	if(nowrite){
+		sha1(buf, n, score, nil);
+		return 0;
+	}
+sha1(buf, n, score, nil);
+fprint(2, "write %V %d\n", score, type);
+	return vtwrite(z, score, type, buf, n);
+}
+
+static char*
+lastelem(char *oname)
+{
+	char *p;
+
+	if(oname == nil)
+		abort();
+	if((p = strrchr(oname, '/')) == nil)
+		return oname;
+	return p+1;
+}
+
+static void
+vac(VtConn *z, char *argv[])
 {
 	DirSink *dsink, *ds;
 	MetaSink *ms;
 	VtRoot root;
 	uchar score[VtScoreSize], buf[VtRootSize];
 	char cwd[2048];
-	int cd, i;
+	int cd;
 	char *cp2, *cp;
-	VacFS *fs;
+	VacFs *fs;
 	VacFile *vff;
 	int fd;
 	Dir *dir;
@@ -79,13 +251,13 @@ vac(VtSession *z, char *argv[])
 	if(getwd(cwd, sizeof(cwd)) == 0)
 		sysfatal("can't find current directory: %r\n");
 
-	dsink = dirSinkAlloc(z, bsize, bsize);
+	dsink = dirsinkalloc(z, bsize, bsize);
 
 	fs = nil;
 	if(dfile != nil) {
-		fs = vfsOpen(z, dfile, 1, 10000);
+		fs = vacfsopen(z, dfile, VtOREAD, 1000);
 		if(fs == nil)
-			fprint(2, "could not open diff: %s: %s\n", dfile, vtGetError());
+			fprint(2, "could not open diff: %s: %r\n", dfile);
 	}
 		
 
@@ -99,6 +271,10 @@ vac(VtSession *z, char *argv[])
 	dir = dirfstat(fd);
 	if(dir == nil)
 		sysfatal("dirfstat failed: %r");
+	if(oname)
+		dir->name = lastelem(oname);
+	else
+		dir->name = "stdin";
 
 	for(; *argv; argv++) {
 		cp2 = *argv;
@@ -115,10 +291,10 @@ vac(VtSession *z, char *argv[])
 		}
 		vff = nil;
 		if(fs)
-			vff = vfOpen(fs, cp2);
-		vacFile(dsink, argv[0], cp2, vff);
+			vff = vacfileopen(fs, cp2);
+		vacfile(dsink, argv[0], cp2, vff);
 		if(vff)
-			vfDecRef(vff);
+			vacfiledecref(vff);
 		if(cd && chdir(cwd) < 0)
 			sysfatal("can't cd back to %s: %r\n", cwd);
 	}
@@ -126,68 +302,63 @@ vac(VtSession *z, char *argv[])
 	if(isi) {
 		vff = nil;
 		if(fs)
-			vff = vfOpen(fs, isi);
-		vacStdin(dsink, isi, vff);
+			vff = vacfileopen(fs, isi);
+		vacstdin(dsink, isi, vff);
 		if(vff)
-			vfDecRef(vff);
+			vacfiledecref(vff);
 	}
 
-	dirSinkClose(dsink);
+	dirsinkclose(dsink);
 
 	/* build meta information for the root */
-	ms = metaSinkAlloc(z, bsize, bsize);
+	ms = metasinkalloc(z, bsize, bsize);
 	/* fake into a directory */
 	dir->mode |= (dir->mode&0444)>>2;
 	dir->qid.type |= QTDIR;
 	dir->mode |= DMDIR;
-	plan9ToVacDir(&vd, dir, 0, fileid++);
+	plan9tovacdir(&vd, dir, 0, fileid++);
 	if(strcmp(vd.elem, "/") == 0){
-		vtMemFree(vd.elem);
-		vd.elem = vtStrDup("root");
+		vtfree(vd.elem);
+		vd.elem = vtstrdup("root");
 	}
-	metaSinkWriteDir(ms, &vd);
-	vdCleanup(&vd);
-	metaSinkClose(ms);
+	metasinkwritedir(ms, &vd);
+	vdcleanup(&vd);
+	metasinkclose(ms);
 	
-	ds = dirSinkAlloc(z, bsize, bsize);
-	dirSinkWriteSink(ds, dsink->sink);
-	dirSinkWriteSink(ds, dsink->msink->sink);
-	dirSinkWriteSink(ds, ms->sink);
-	dirSinkClose(ds);
+	ds = dirsinkalloc(z, bsize, bsize);
+	dirsinkwritesink(ds, dsink->sink);
+	dirsinkwritesink(ds, dsink->msink->sink);
+	dirsinkwritesink(ds, ms->sink);
+	dirsinkclose(ds);
 
 	memset(&root, 0, sizeof(root));		
-	root.version = VtRootVersion;
 	strncpy(root.name, dir->name, sizeof(root.name));
 	root.name[sizeof(root.name)-1] = 0;
 	free(dir);
 	sprint(root.type, "vac");
 	memmove(root.score, ds->sink->dir.score, VtScoreSize);
-	root.blockSize = maxbsize;
+	root.blocksize = maxbsize;
 	if(fs != nil)
-		vfsGetScore(fs, root.prev);
+		vacfsgetscore(fs, root.prev);
 
-	metaSinkFree(ms);
-	dirSinkFree(ds);
-	dirSinkFree(dsink);
+	metasinkfree(ms);
+	dirsinkfree(ds);
+	dirsinkfree(dsink);
 	if(fs != nil)
-		vfsClose(fs);
+		vacfsclose(fs);
 	
-	vtRootPack(&root, buf);
-	if(!vacWrite(z, score, VtRootType, buf, VtRootSize))
-		vtFatal("vacWrite failed: %s", vtGetError());
+	vtrootpack(&root, buf);
+	if(vacwrite(z, score, VtRootType, buf, VtRootSize) < 0)
+		sysfatal("vacWrite failed: %r");
 
-	fprint(fd, "vac:");
-	for(i=0; i<VtScoreSize; i++)
-		fprint(fd, "%.2x", score[i]);
-	fprint(fd, "\n");
-	
+	fprint(fd, "vac:%V\n", score);
+
 	/* avoid remove at cleanup */
 	oname = nil;
-	return 1;
 }
 
 static int
-isExcluded(char *name)
+isexcluded(char *name)
 {
 	int bot, top, i, x;
 
@@ -207,24 +378,24 @@ isExcluded(char *name)
 }
 
 static void
-vacFile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
+vacfile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
 {
 	int fd;
 	Dir *dir;
 	VacDir vd;
 	ulong entry;
 
-	if(isExcluded(lname)) {
+	if(isexcluded(lname)) {
 		warn("excluding: %s", lname);
 		return;
 	}
 
-	if(merge && vacMerge(dsink, lname, sname))
+	if(merge && vacmerge(dsink, lname, sname) >= 0)
 		return;
 
 	fd = open(sname, OREAD);
 	if(fd < 0) {
-		warn("could not open file: %s: %s", lname, vtOSError());
+		warn("could not open file: %s: %r", lname);
 		return;
 	}
 
@@ -237,24 +408,25 @@ vacFile(DirSink *dsink, char *lname, char *sname, VacF
 		close(fd);
 		return;
 	}
+	dir->name = lastelem(sname);
 
 	entry = dsink->nentry;
 
 	if(dir->mode & DMDIR) 
-		vacDir(dsink, fd, lname, sname, vf);
+		vacdir(dsink, fd, lname, sname, vf);
 	else
-		vacData(dsink, fd, lname, vf, dir);
+		vacdata(dsink, fd, lname, vf, dir);
 
-	plan9ToVacDir(&vd, dir, entry, fileid++);
-	metaSinkWriteDir(dsink->msink, &vd);
-	vdCleanup(&vd);
+	plan9tovacdir(&vd, dir, entry, fileid++);
+	metasinkwritedir(dsink->msink, &vd);
+	vdcleanup(&vd);
 
 	free(dir);
 	close(fd);
 }
 
 static void
-vacStdin(DirSink *dsink, char *name, VacFile *vf)
+vacstdin(DirSink *dsink, char *name, VacFile *vf)
 {
 	Dir *dir;
 	VacDir vd;
@@ -268,21 +440,33 @@ vacStdin(DirSink *dsink, char *name, VacFile *vf)
 		warn("can't stat <stdio>: %r");
 		return;
 	}
+	dir->name = "stdin";
 
 	entry = dsink->nentry;
 
-	vacData(dsink, 0, "<stdin>", vf, dir);
+	vacdata(dsink, 0, "<stdin>", vf, dir);
 
-	plan9ToVacDir(&vd, dir, entry, fileid++);
-	vd.elem = vtStrDup(name);
-	metaSinkWriteDir(dsink->msink, &vd);
-	vdCleanup(&vd);
+	plan9tovacdir(&vd, dir, entry, fileid++);
+	vd.elem = vtstrdup(name);
+	metasinkwritedir(dsink->msink, &vd);
+	vdcleanup(&vd);
 
 	free(dir);
 }
 
+static int
+sha1check(u8int *score, uchar *buf, int n)
+{
+	char score2[VtScoreSize];
+
+	sha1(buf, n, score, nil);
+	if(memcmp(score, score2, VtScoreSize) == 0)
+		return 0;
+	return -1;
+}
+
 static ulong
-vacDataSkip(Sink *sink, VacFile *vf, int fd, ulong blocks, uchar *buf, char *lname)
+vacdataskip(Sink *sink, VacFile *vf, int fd, ulong blocks, uchar *buf, char *lname)
 {
 	int n;
 	ulong i;
@@ -293,24 +477,24 @@ vacDataSkip(Sink *sink, VacFile *vf, int fd, ulong blo
 		warn("error seeking: %s", lname);
 		goto Err;
 	}
-	n = readBlock(fd, buf, bsize);
+	n = readn(fd, buf, bsize);
 	if(n < bsize) {
 		warn("error checking append only file: %s", lname);
 		goto Err;
 	}
-	if(!vfGetBlockScore(vf, blocks-1, score) || !vtSha1Check(score, buf, n)) {
+	if(vacfileblockscore(vf, blocks-1, score)<0 || sha1check(score, buf, n)<0) {
 		warn("last block of append file did not match: %s", lname);
 		goto Err;
 	}
 
 	for(i=0; i<blocks; i++) {
-		if(!vfGetBlockScore(vf, i, score)) {
+		if(vacfileblockscore(vf, i, score) < 0) {
 			warn("could not get score: %s: %lud", lname, i);
 			seek(fd, i*bsize, 0);
 			return i;
 		}
 		stats.skip++;
-		sinkWriteScore(sink, score, bsize);
+		sinkwritescore(sink, score, bsize);
 	}
 
 	return i;
@@ -320,7 +504,7 @@ Err:
 }
 
 static void
-vacData(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
+vacdata(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
 {
 	uchar *buf;
 	Sink *sink;
@@ -332,13 +516,13 @@ vacData(DirSink *dsink, int fd, char *lname, VacFile *
 
 	vfblocks = 0;
 	if(vf != nil && qdiff) {
-		vfGetDir(vf, &vd);
+		vacfilegetdir(vf, &vd);
 		if(vd.mtime == dir->mtime)
 		if(vd.size == dir->length)
 		if(!vd.plan9 || /* vd.p9path == dir->qid.path && */ vd.p9version == dir->qid.vers)
-		if(dirSinkWriteFile(dsink, vf)) {
+		if(dirsinkwritefile(dsink, vf)) {
 			stats.sfile++;
-			vdCleanup(&vd);
+			vdcleanup(&vd);
 			return;
 		}
 
@@ -349,30 +533,30 @@ vacData(DirSink *dsink, int fd, char *lname, VacFile *
 		if(vd.p9path == dir->qid.path)
 			vfblocks = vd.size/bsize;
 
-		vdCleanup(&vd);
+		vdcleanup(&vd);
 	}
 	stats.file++;
 
-	buf = vtMemAlloc(bsize);
-	sink = sinkAlloc(dsink->sink->z, bsize, bsize);
+	buf = vtmalloc(bsize);
+	sink = sinkalloc(dsink->sink->z, bsize, bsize);
 	block = 0;
 	same = stats.sdata+stats.skip;
 
 	if(vfblocks > 1)
-		block += vacDataSkip(sink, vf, fd, vfblocks, buf, lname);
+		block += vacdataskip(sink, vf, fd, vfblocks, buf, lname);
 
 if(0) fprint(2, "vacData: %s: %ld\n", lname, block);
 	for(;;) {
-		n = readBlock(fd, buf, bsize);
+		n = readn(fd, buf, bsize);
 		if(0 && n < 0)
-			warn("file truncated due to read error: %s: %s", lname, vtOSError());
+			warn("file truncated due to read error: %s: %r", lname);
 		if(n <= 0)
 			break;
-		if(vf != nil && vfGetBlockScore(vf, block, score) && vtSha1Check(score, buf, n)) {
+		if(vf != nil && vacfileblockscore(vf, block, score) && sha1check(score, buf, n)>=0) {
 			stats.sdata++;
-			sinkWriteScore(sink, score, n);
+			sinkwritescore(sink, score, n);
 		} else
-			sinkWrite(sink, buf, n);
+			sinkwrite(sink, buf, n);
 		block++;
 	}
 	same = stats.sdata+stats.skip - same;
@@ -381,15 +565,15 @@ if(0) fprint(2, "vacData: %s: %ld\n", lname, block);
 		if(0)fprint(2, "%s: total %lud same %lud:%lud diff %lud\n",
 			lname, block, same, vfblocks, block-same);
 
-	sinkClose(sink);
-	dirSinkWriteSink(dsink, sink);
-	sinkFree(sink);
+	sinkclose(sink);
+	dirsinkwritesink(dsink, sink);
+	sinkfree(sink);
 	free(buf);
 }
 
 
 static void
-vacDir(DirSink *dsink, int fd, char *lname, char *sname, VacFile *vf)
+vacdir(DirSink *dsink, int fd, char *lname, char *sname, VacFile *vf)
 {
 	Dir *dirs;
 	char *ln, *sn;
@@ -398,58 +582,58 @@ vacDir(DirSink *dsink, int fd, char *lname, char *snam
 	VacFile *vvf;
 	char *name;
 
-	ds = dirSinkAlloc(dsink->sink->z, bsize, bsize);
+	ds = dirsinkalloc(dsink->sink->z, bsize, bsize);
 	while((nd = dirread(fd, &dirs)) > 0){
 		for(i = 0; i < nd; i++){
 			name = dirs[i].name;
 			/* check for bad file names */
 			if(name[0] == 0 || strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
 				continue;
-			ln = vtMemAlloc(strlen(lname) + strlen(name) + 2);
-			sn = vtMemAlloc(strlen(sname) + strlen(name) + 2);
+			ln = vtmalloc(strlen(lname) + strlen(name) + 2);
+			sn = vtmalloc(strlen(sname) + strlen(name) + 2);
 			sprint(ln, "%s/%s", lname, name);
 			sprint(sn, "%s/%s", sname, name);
 			if(vf != nil)
-				vvf = vfWalk(vf, name);
+				vvf = vacfilewalk(vf, name);
 			else
 				vvf = nil;
-			vacFile(ds, ln, sn, vvf);
+			vacfile(ds, ln, sn, vvf);
 			if(vvf != nil)
-				vfDecRef(vvf);
-			vtMemFree(ln);
-			vtMemFree(sn);
+				vacfiledecref(vvf);
+			vtfree(ln);
+			vtfree(sn);
 		}
 		free(dirs);
 	}
-	dirSinkClose(ds);
-	dirSinkWriteSink(dsink, ds->sink);
-	dirSinkWriteSink(dsink, ds->msink->sink);
-	dirSinkFree(ds);
+	dirsinkclose(ds);
+	dirsinkwritesink(dsink, ds->sink);
+	dirsinkwritesink(dsink, ds->msink->sink);
+	dirsinkfree(ds);
 }
 
 static int
-vacMergeFile(DirSink *dsink, VacFile *vf, VacDir *dir, uvlong offset, uvlong *max)
+vacmergefile(DirSink *dsink, VacFile *vf, VacDir *dir, uvlong offset, uvlong *max)
 {
 	uchar buf[VtEntrySize];
 	VtEntry dd, md;
 	int e;
 
-	if(vfRead(vf, buf, VtEntrySize, (uvlong)dir->entry*VtEntrySize) != VtEntrySize) {
+	if(vacfileread(vf, buf, VtEntrySize, (uvlong)dir->entry*VtEntrySize) != VtEntrySize) {
 		warn("could not read venti dir entry: %s\n", dir->elem);
-		return 0;
+		return -1;
 	}
-	vtEntryUnpack(&dd, buf, 0);
+	vtentryunpack(&dd, buf, 0);
 
 	if(dir->mode & ModeDir)	{
 		e = dir->mentry;
 		if(e == 0)
 			e = dir->entry + 1;
 		
-		if(vfRead(vf, buf, VtEntrySize, e*VtEntrySize) != VtEntrySize) {
+		if(vacfileread(vf, buf, VtEntrySize, e*VtEntrySize) != VtEntrySize) {
 			warn("could not read venti dir entry: %s\n", dir->elem);
 			return 0;
 		}
-		vtEntryUnpack(&md, buf, 0);
+		vtentryunpack(&md, buf, 0);
 	}
 
 	/* max might incorrect in some old dumps */
@@ -461,27 +645,27 @@ vacMergeFile(DirSink *dsink, VacFile *vf, VacDir *dir,
 	dir->qid += offset;
 	dir->entry = dsink->nentry;
 
-	if(dir->qidSpace) {
-		dir->qidOffset += offset;
+	if(dir->qidspace) {
+		dir->qidoffset += offset;
 	} else {
-		dir->qidSpace = 1;
-		dir->qidOffset = offset;
-		dir->qidMax = *max;
+		dir->qidspace = 1;
+		dir->qidoffset = offset;
+		dir->qidmax = *max;
 	}
 
-	dirSinkWrite(dsink, &dd);
+	dirsinkwrite(dsink, &dd);
 	if(dir->mode & ModeDir)	
-		dirSinkWrite(dsink, &md);
-	metaSinkWriteDir(dsink->msink, dir);
+		dirsinkwrite(dsink, &md);
+	metasinkwritedir(dsink->msink, dir);
 	
-	return 1;
+	return 0;
 }
 
 static int
-vacMerge(DirSink *dsink, char *lname, char *sname)
+vacmerge(DirSink *dsink, char *lname, char *sname)
 {
 	char *p;
-	VacFS *fs;
+	VacFs *fs;
 	VacFile *vf;
 	VacDirEnum *d;
 	VacDir dir;
@@ -492,67 +676,67 @@ vacMerge(DirSink *dsink, char *lname, char *sname)
 		return 0;
 
 	d = nil;
-	fs = vfsOpen(dsink->sink->z, sname, 1, 100);
+	fs = vacfsopen(dsink->sink->z, sname, VtOREAD, 100);
 	if(fs == nil)
-		return 0;
+		return -1;
 
-	vf = vfOpen(fs, "/");
+	vf = vacfileopen(fs, "/");
 	if(vf == nil)
 		goto Done;
-	max = vfGetId(vf);
-	d = vdeOpen(fs, "/");
+	max = vacfilegetid(vf);
+	d = vdeopen(vf);
 	if(d == nil)
 		goto Done;
 
 	if(verbose)
 		fprint(2, "merging: %s\n", lname);
 
-	if(maxbsize < vfsGetBlockSize(fs))
-		maxbsize = vfsGetBlockSize(fs);
+	if(maxbsize < fs->bsize)
+		maxbsize = fs->bsize;
 
 	for(;;) {
-		if(vdeRead(d, &dir, 1) < 1)
+		if(vderead(d, &dir) < 1)
 			break;
-		vacMergeFile(dsink, vf, &dir, fileid, &max);
-		vdCleanup(&dir);	
+		vacmergefile(dsink, vf, &dir, fileid, &max);
+		vdcleanup(&dir);	
 	}
 	fileid += max;
 
 Done:
 	if(d != nil)
-		vdeFree(d);
+		vdeclose(d);
 	if(vf != nil)
-		vfDecRef(vf);
-	vfsClose(fs);
-	return 1;
+		vacfiledecref(vf);
+	vacfsclose(fs);
+	return 0;
 }
 
 Sink *
-sinkAlloc(VtSession *z, int psize, int dsize)
+sinkalloc(VtConn *z, int psize, int dsize)
 {
 	Sink *k;
 	int i;
 
 	if(psize < 512 || psize > VtMaxLumpSize)
-		vtFatal("sinkAlloc: bad psize");
+		sysfatal("sinkalloc: bad psize");
 	if(dsize < 512 || dsize > VtMaxLumpSize)
-		vtFatal("sinkAlloc: bad psize");
+		sysfatal("sinkalloc: bad psize");
 
 	psize = VtScoreSize*(psize/VtScoreSize);
 
-	k = vtMemAllocZ(sizeof(Sink));
+	k = vtmallocz(sizeof(Sink));
 	k->z = z;
 	k->dir.flags = VtEntryActive;
 	k->dir.psize = psize;
 	k->dir.dsize = dsize;
-	k->buf = vtMemAllocZ(VtPointerDepth*k->dir.psize + VtScoreSize);
+	k->buf = vtmallocz(VtPointerDepth*k->dir.psize + VtScoreSize);
 	for(i=0; i<=VtPointerDepth; i++)
 		k->pbuf[i] = k->buf + i*k->dir.psize;
 	return k;
 }
 
 void
-sinkWriteScore(Sink *k, uchar score[VtScoreSize], int n)
+sinkwritescore(Sink *k, uchar score[VtScoreSize], int n)
 {
 	int i;
 	uchar *p;
@@ -567,11 +751,11 @@ sinkWriteScore(Sink *k, uchar score[VtScoreSize], int 
 		if(k->pbuf[i] < k->buf + d->psize*(i+1))
 			break;
 		if(i == VtPointerDepth-1)
-			vtFatal("file too big");
+			sysfatal("file too big");
 		p = k->buf+i*d->psize;
 		stats.meta++;
-		if(!vacWrite(k->z, k->pbuf[i+1], VtPointerType0+i, p, d->psize))
-			vtFatal("vacWrite failed: %s", vtGetError());
+		if(vacwrite(k->z, k->pbuf[i+1], VtDataType+1+i, p, d->psize) < 0)
+			sysfatal("vacwrite failed: %r");
 		k->pbuf[i] = p;
 	}
 
@@ -582,29 +766,29 @@ sinkWriteScore(Sink *k, uchar score[VtScoreSize], int 
 }
 
 void
-sinkWrite(Sink *k, uchar *p, int n)
+sinkwrite(Sink *k, uchar *p, int n)
 {
 	int type;
 	uchar score[VtScoreSize];
 
 	if(n > k->dir.dsize)
-		vtFatal("sinkWrite: size too big");
+		sysfatal("sinkWrite: size too big");
 
-	if(k->dir.flags & VtEntryDir) {
+	if((k->dir.type&~VtTypeDepthMask) == VtDirType){
 		type = VtDirType;
 		stats.meta++;
 	} else {
 		type = VtDataType;
 		stats.data++;
 	}
-	if(!vacWrite(k->z, score, type, p, n))
-		vtFatal("vacWrite failed: %s", vtGetError());
+	if(vacwrite(k->z, score, type, p, n) < 0)
+		sysfatal("vacWrite failed: %r");
 
-	sinkWriteScore(k, score, n);
+	sinkwritescore(k, score, n);
 }
 
 static int
-sizeToDepth(uvlong s, int psize, int dsize)
+sizetodepth(uvlong s, int psize, int dsize)
 {
 	int np;
 	int d;
@@ -618,9 +802,9 @@ sizeToDepth(uvlong s, int psize, int dsize)
 }
 
 void
-sinkClose(Sink *k)
+sinkclose(Sink *k)
 {
-	int i, n;
+	int i, n, base;
 	uchar *p;
 	VtEntry *kd;
 
@@ -628,7 +812,7 @@ sinkClose(Sink *k)
 
 	/* empty */
 	if(kd->size == 0) {
-		memmove(kd->score, vtZeroScore, VtScoreSize);
+		memmove(kd->score, vtzeroscore, VtScoreSize);
 		return;
 	}
 
@@ -636,7 +820,10 @@ sinkClose(Sink *k)
 		if(k->pbuf[n] > k->buf + kd->psize*n)
 			break;
 
-	kd->depth = sizeToDepth(kd->size, kd->psize, kd->dsize);
+fprint(2, "type %d -> ", kd->type);
+	base = kd->type&~VtTypeDepthMask;
+	kd->type = base + sizetodepth(kd->size, kd->psize, kd->dsize);
+fprint(2, "%d ", kd->type);
 
 	/* skip full part of tree */
 	for(i=0; i<n && k->pbuf[i] == k->buf + kd->psize*i; i++)
@@ -644,6 +831,7 @@ sinkClose(Sink *k)
 
 	/* is the tree completely full */
 	if(i == n && k->pbuf[n] == k->buf + kd->psize*n + VtScoreSize) {
+fprint(2, "full\n");
 		memmove(kd->score, k->pbuf[n] - VtScoreSize, VtScoreSize);
 		return;
 	}
@@ -653,93 +841,95 @@ sinkClose(Sink *k)
 	for(; i<n; i++) {
 		p = k->buf+i*kd->psize;
 		stats.meta++;
-		if(!vacWrite(k->z, k->pbuf[i+1], VtPointerType0+i, p, k->pbuf[i]-p))
-			vtFatal("vacWrite failed: %s", vtGetError());
+		if(vacwrite(k->z, k->pbuf[i+1], base+1+i, p, k->pbuf[i]-p) < 0)
+			sysfatal("vacWrite failed: %r");
 		k->pbuf[i+1] += VtScoreSize;
 	}
 	memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize);
+fprint(2, "%V\n", kd->score);
 }
 
 void
-sinkFree(Sink *k)
+sinkfree(Sink *k)
 {
-	vtMemFree(k->buf);
-	vtMemFree(k);
+	vtfree(k->buf);
+	vtfree(k);
 }
 
 DirSink *
-dirSinkAlloc(VtSession *z, int psize, int dsize)
+dirsinkalloc(VtConn *z, int psize, int dsize)
 {
 	DirSink *k;
 	int ds;
 
 	ds = VtEntrySize*(dsize/VtEntrySize);
 
-	k = vtMemAllocZ(sizeof(DirSink));
-	k->sink = sinkAlloc(z, psize, ds);
-	k->sink->dir.flags |= VtEntryDir;
-	k->msink = metaSinkAlloc(z, psize, dsize);
-	k->buf = vtMemAlloc(ds);
+	k = vtmallocz(sizeof(DirSink));
+	k->sink = sinkalloc(z, psize, ds);
+	k->sink->dir.type = VtDirType;
+	k->msink = metasinkalloc(z, psize, dsize);
+	k->buf = vtmalloc(ds);
 	k->p = k->buf;
 	k->ep = k->buf + ds;
 	return k;
 }
 
 void
-dirSinkWrite(DirSink *k, VtEntry *dir)
+dirsinkwrite(DirSink *k, VtEntry *dir)
 {
 	if(k->p + VtEntrySize > k->ep) {
-		sinkWrite(k->sink, k->buf, k->p - k->buf);
+		sinkwrite(k->sink, k->buf, k->p - k->buf);
 		k->p = k->buf;
 	}
-	vtEntryPack(dir, k->p, 0);
+fprint(2, "write entry %V %d\n", dir->score, dir->type);
+	vtentrypack(dir, k->p, 0);
 	k->nentry++;
 	k->p += VtEntrySize;
 }
 
 void
-dirSinkWriteSink(DirSink *k, Sink *sink)
+dirsinkwritesink(DirSink *k, Sink *sink)
 {
-	dirSinkWrite(k, &sink->dir);
+	dirsinkwrite(k, &sink->dir);
 }
 
 int
-dirSinkWriteFile(DirSink *k, VacFile *vf)
+dirsinkwritefile(DirSink *k, VacFile *vf)
 {
 	VtEntry dir;
 
-	if(!vfGetVtEntry(vf, &dir))
-		return 0;
-	dirSinkWrite(k, &dir);
-	return 1;
+	if(vacfilegetvtentry(vf, &dir) < 0)
+		return -1;
+	dirsinkwrite(k, &dir);
+	return 0;
 }
 
 void
-dirSinkClose(DirSink *k)
+dirsinkclose(DirSink *k)
 {
-	metaSinkClose(k->msink);
+	metasinkclose(k->msink);
 	if(k->p != k->buf)
-		sinkWrite(k->sink, k->buf, k->p - k->buf);
-	sinkClose(k->sink);
+		sinkwrite(k->sink, k->buf, k->p - k->buf);
+	sinkclose(k->sink);
 }
 
 void
-dirSinkFree(DirSink *k)
+dirsinkfree(DirSink *k)
 {
-	sinkFree(k->sink);
-	metaSinkFree(k->msink);
-	vtMemFree(k->buf);
-	vtMemFree(k);
+	sinkfree(k->sink);
+	metasinkfree(k->msink);
+	vtfree(k->buf);
+	vtfree(k);
 }
 
-MetaSink *
-metaSinkAlloc(VtSession *z, int psize, int dsize)
+MetaSink*
+metasinkalloc(VtConn *z, int psize, int dsize)
 {
 	MetaSink *k;
 
-	k = vtMemAllocZ(sizeof(MetaSink));
-	k->sink = sinkAlloc(z, psize, dsize);
-	k->buf = vtMemAlloc(dsize);
+	k = vtmallocz(sizeof(MetaSink));
+	k->sink = sinkalloc(z, psize, dsize);
+	k->buf = vtmalloc(dsize);
 	k->maxindex = dsize/100;	/* 100 byte entries seems reasonable */
 	if(k->maxindex < 1)
 		k->maxindex = 1;
@@ -749,22 +939,22 @@ metaSinkAlloc(VtSession *z, int psize, int dsize)
 }
 
 /* hack to get base to compare routine - not reentrant */
-uchar *blockBase;
+uchar *blockbase;
 
 int
-dirCmp(void *p0, void *p1)
+dircmp(const void *p0, const void *p1)
 {
 	uchar *q0, *q1;
 	int n0, n1, r;
 
 	/* name is first element of entry */
-	q0 = p0;
-	q0 = blockBase + (q0[0]<<8) + q0[1];
+	q0 = (uchar*)p0;
+	q0 = blockbase + (q0[0]<<8) + q0[1];
 	n0 = (q0[6]<<8) + q0[7];
 	q0 += 8;
 
-	q1 = p1;
-	q1 = blockBase + (q1[0]<<8) + q1[1];
+	q1 = (uchar*)p1;
+	q1 = blockbase + (q1[0]<<8) + q1[1];
 	n1 = (q1[6]<<8) + q1[7];
 	q1 += 8;
 
@@ -780,7 +970,7 @@ dirCmp(void *p0, void *p1)
 }
 
 void
-metaSinkFlush(MetaSink *k)
+metasinkflush(MetaSink *k)
 {
 	uchar *p;
 	int n;
@@ -798,19 +988,19 @@ metaSinkFlush(MetaSink *k)
 	mb.nindex = k->nindex;
 	mb.maxindex = k->maxindex;
 	mb.buf = p;
-	mbPack(&mb);
+	mbpack(&mb);
 	
 	p += MetaHeaderSize;
 
 	/* XXX this is not reentrant! */
-	blockBase = k->buf;
-	qsort(p, k->nindex, MetaIndexSize, dirCmp);
+	blockbase = k->buf;
+	qsort(p, k->nindex, MetaIndexSize, dircmp);
 	p += k->nindex*MetaIndexSize;
 	
 	memset(p, 0, (k->maxindex-k->nindex)*MetaIndexSize);
 	p += (k->maxindex-k->nindex)*MetaIndexSize;
 
-	sinkWrite(k->sink, k->buf, n);
+	sinkwrite(k->sink, k->buf, n);
 
 	/* move down partial entry */
 	n = k->p - k->rp;
@@ -821,109 +1011,109 @@ metaSinkFlush(MetaSink *k)
 }
 
 void
-metaSinkPutc(MetaSink *k, int c)
+metasinkputc(MetaSink *k, int c)
 {
 	if(k->p+1 > k->ep)
-		metaSinkFlush(k);
+		metasinkflush(k);
 	if(k->p+1 > k->ep)
-		vtFatal("directory entry too large");
+		sysfatal("directory entry too large");
 	k->p[0] = c;
 	k->p++;
 }
 
 void
-metaSinkPutString(MetaSink *k, char *s)
+metasinkputstring(MetaSink *k, char *s)
 {
 	int n = strlen(s);
-	metaSinkPutc(k, n>>8);
-	metaSinkPutc(k, n);
-	metaSinkWrite(k, (uchar*)s, n);
+	metasinkputc(k, n>>8);
+	metasinkputc(k, n);
+	metasinkwrite(k, (uchar*)s, n);
 }
 
 void
-metaSinkPutUint32(MetaSink *k, ulong x)
+metasinkputuint32(MetaSink *k, ulong x)
 {
-	metaSinkPutc(k, x>>24);
-	metaSinkPutc(k, x>>16);
-	metaSinkPutc(k, x>>8);
-	metaSinkPutc(k, x);
+	metasinkputc(k, x>>24);
+	metasinkputc(k, x>>16);
+	metasinkputc(k, x>>8);
+	metasinkputc(k, x);
 }
 
 void
-metaSinkPutUint64(MetaSink *k, uvlong x)
+metasinkputuint64(MetaSink *k, uvlong x)
 {
-	metaSinkPutUint32(k, x>>32);
-	metaSinkPutUint32(k, x);
+	metasinkputuint32(k, x>>32);
+	metasinkputuint32(k, x);
 }
 
 void
-metaSinkWrite(MetaSink *k, uchar *data, int n)
+metasinkwrite(MetaSink *k, uchar *data, int n)
 {
 	if(k->p + n > k->ep)
-		metaSinkFlush(k);
+		metasinkflush(k);
 	if(k->p + n > k->ep)
-		vtFatal("directory entry too large");
+		sysfatal("directory entry too large");
 	
 	memmove(k->p, data, n);
 	k->p += n;
 }
 
 void
-metaSinkWriteDir(MetaSink *ms, VacDir *dir)
+metasinkwritedir(MetaSink *ms, VacDir *dir)
 {
-	metaSinkPutUint32(ms, DirMagic);
-	metaSinkPutc(ms, Version>>8);
-	metaSinkPutc(ms, Version);		
-	metaSinkPutString(ms, dir->elem);
-	metaSinkPutUint32(ms, dir->entry);
-	metaSinkPutUint64(ms, dir->qid);
-	metaSinkPutString(ms, dir->uid);
-	metaSinkPutString(ms, dir->gid);
-	metaSinkPutString(ms, dir->mid);
-	metaSinkPutUint32(ms, dir->mtime);
-	metaSinkPutUint32(ms, dir->mcount);
-	metaSinkPutUint32(ms, dir->ctime);
-	metaSinkPutUint32(ms, dir->atime);
-	metaSinkPutUint32(ms, dir->mode);
+	metasinkputuint32(ms, DirMagic);
+	metasinkputc(ms, Version>>8);
+	metasinkputc(ms, Version);		
+	metasinkputstring(ms, dir->elem);
+	metasinkputuint32(ms, dir->entry);
+	metasinkputuint64(ms, dir->qid);
+	metasinkputstring(ms, dir->uid);
+	metasinkputstring(ms, dir->gid);
+	metasinkputstring(ms, dir->mid);
+	metasinkputuint32(ms, dir->mtime);
+	metasinkputuint32(ms, dir->mcount);
+	metasinkputuint32(ms, dir->ctime);
+	metasinkputuint32(ms, dir->atime);
+	metasinkputuint32(ms, dir->mode);
 
 	if(dir->plan9) {
-		metaSinkPutc(ms, DirPlan9Entry);	/* plan9 extra info */
-		metaSinkPutc(ms, 0);			/* plan9 extra size */
-		metaSinkPutc(ms, 12);			/* plan9 extra size */
-		metaSinkPutUint64(ms, dir->p9path);
-		metaSinkPutUint32(ms, dir->p9version);
+		metasinkputc(ms, DirPlan9Entry);	/* plan9 extra info */
+		metasinkputc(ms, 0);			/* plan9 extra size */
+		metasinkputc(ms, 12);			/* plan9 extra size */
+		metasinkputuint64(ms, dir->p9path);
+		metasinkputuint32(ms, dir->p9version);
 	}
 
-	if(dir->qidSpace != 0) {
-		metaSinkPutc(ms, DirQidSpaceEntry);
-		metaSinkPutc(ms, 0);
-		metaSinkPutc(ms, 16);
-		metaSinkPutUint64(ms, dir->qidOffset);
-		metaSinkPutUint64(ms, dir->qidMax);
+	if(dir->qidspace != 0) {
+		metasinkputc(ms, DirQidSpaceEntry);
+		metasinkputc(ms, 0);
+		metasinkputc(ms, 16);
+		metasinkputuint64(ms, dir->qidoffset);
+		metasinkputuint64(ms, dir->qidmax);
 	}
 
 	if(dir->gen != 0) {
-		metaSinkPutc(ms, DirGenEntry);
-		metaSinkPutc(ms, 0);
-		metaSinkPutc(ms, 4);
-		metaSinkPutUint32(ms, dir->gen);
+		metasinkputc(ms, DirGenEntry);
+		metasinkputc(ms, 0);
+		metasinkputc(ms, 4);
+		metasinkputuint32(ms, dir->gen);
 	}
 
-	metaSinkEOR(ms);
+	metasinkeor(ms);
 }
 
 
 void
-plan9ToVacDir(VacDir *vd, Dir *dir, ulong entry, uvlong qid)
+plan9tovacdir(VacDir *vd, Dir *dir, ulong entry, uvlong qid)
 {
 	memset(vd, 0, sizeof(VacDir));
 
-	vd->elem = vtStrDup(dir->name);
+	vd->elem = vtstrdup(dir->name);
 	vd->entry = entry;
 	vd->qid = qid;
-	vd->uid = vtStrDup(dir->uid);
-	vd->gid = vtStrDup(dir->gid);
-	vd->mid = vtStrDup(dir->muid);
+	vd->uid = vtstrdup(dir->uid);
+	vd->gid = vtstrdup(dir->gid);
+	vd->mid = vtstrdup(dir->muid);
 	vd->mtime = dir->mtime;
 	vd->mcount = 0;
 	vd->ctime = dir->mtime;		/* ctime: not available on plan 9 */
@@ -944,7 +1134,7 @@ plan9ToVacDir(VacDir *vd, Dir *dir, ulong entry, uvlon
 
 
 void
-metaSinkEOR(MetaSink *k)
+metasinkeor(MetaSink *k)
 {
 	uchar *p;
 	int o, n;
@@ -960,22 +1150,22 @@ metaSinkEOR(MetaSink *k)
 	k->rp = k->p;
 	k->nindex++;
 	if(k->nindex == k->maxindex)
-		metaSinkFlush(k);
+		metasinkflush(k);
 }
 
 void
-metaSinkClose(MetaSink *k)
+metasinkclose(MetaSink *k)
 {
-	metaSinkFlush(k);
-	sinkClose(k->sink);
+	metasinkflush(k);
+	sinkclose(k->sink);
 }
 
 void
-metaSinkFree(MetaSink *k)
+metasinkfree(MetaSink *k)
 {
-	sinkFree(k->sink);
-	vtMemFree(k->buf);
-	vtMemFree(k);
+	sinkfree(k->sink);
+	vtfree(k->buf);
+	vtfree(k);
 }
 
 static void
blob - 549c441c8e45901a00e5d338f628bd6a94da821a
blob + 28e4706de9e61668f6693d65800472c88510006f
--- src/cmd/vac/vac.h
+++ src/cmd/vac/vac.h
@@ -1,4 +1,4 @@
-typedef struct VacFS VacFS;
+typedef struct VacFs VacFs;
 typedef struct VacDir VacDir;
 typedef struct VacFile VacFile;
 typedef struct VacDirEnum VacDirEnum;
@@ -6,7 +6,8 @@ typedef struct VacDirEnum VacDirEnum;
 /*
  * Mode bits
  */
-enum {
+enum
+{
 	ModeOtherExec = (1<<0),		
 	ModeOtherWrite = (1<<1),
 	ModeOtherRead = (1<<2),
@@ -30,7 +31,8 @@ enum {
 	ModeSnapshot = (1<<20),		/* read only snapshot */
 };
 
-enum {
+enum
+{
 	MetaMagic = 0x5656fc79,
 	MetaHeaderSize = 12,
 	MetaIndexSize = 4,
@@ -38,14 +40,16 @@ enum {
 	DirMagic = 0x1c4d9072,
 };
 
-enum {
+enum
+{
 	DirPlan9Entry = 1,	/* not valid in version >= 9 */
 	DirNTEntry,		/* not valid in version >= 9 */
 	DirQidSpaceEntry,
 	DirGenEntry,		/* not valid in version >= 9 */
 };
 
-struct VacDir {
+struct VacDir
+{
 	char *elem;		/* path element */
 	ulong entry;		/* entry in directory for data */
 	ulong gen;		/* generation of data entry */
@@ -69,58 +73,67 @@ struct VacDir {
 	ulong p9version;
 
 	/* sub space of qid */
-	int qidSpace;
-	uvlong qidOffset;	/* qid offset */
-	uvlong qidMax;		/* qid maximum */
+	int qidspace;
+	uvlong qidoffset;	/* qid offset */
+	uvlong qidmax;		/* qid maximum */
 };
 
-VacFS *vfsOpen(VtSession *z, char *file, int readOnly, long ncache);
-VacFS *vfsCreate(VtSession *z, int bsize, long ncache);
-int vfsGetBlockSize(VacFS*);
-int vfsIsReadOnly(VacFS*);
-VacFile *vfsGetRoot(VacFS*);
 
-long vfsGetCacheSize(VacFS*);
-int vfsSetCacheSize(VacFS*, long);
-int vfsSnapshot(VacFS*, char *src, char *dst);
-int vfsSync(VacFS*);
-int vfsClose(VacFS*);
-int vfsGetScore(VacFS*, uchar score[VtScoreSize]);
+struct VacFs
+{
+	int		ref;
+	uchar	score[VtScoreSize];
+	VacFile	*root;
+	VtConn	*z;
+	int		mode;
+	int		bsize;
+	uvlong	qid;
+	VtCache	*cache;
+};
 
+VacFs	*vacfsopen(VtConn *z, char *file, int mode, int ncache);
+VacFs	*vacfsopenscore(VtConn *z, u8int *score, int mode, int ncache);
+VacFs	*vacfscreate(VtConn *z, int bsize, int ncache);
+void		vacfsclose(VacFs *fs);
+int		vacfssync(VacFs *fs);
+int		vacfssnapshot(VacFs *fs, char *src, char *dst);
+int		vacfsgetscore(VacFs *fs, u8int *score);
+
 /* 
  * other ideas
  *
- * VacFS *vfsSnapshot(VacFS*, char *src);
- * int vfsGraft(VacFS*, char *name, VacFS*);
+ * VacFs *vfsSnapshot(VacFs*, char *src);
+ * int vfsGraft(VacFs*, char *name, VacFs*);
  */
 
-VacFile *vfOpen(VacFS*, char *path);
-VacFile *vfCreate(VacFile*, char *elem, ulong perm, char *user);
-VacFile *vfWalk(VacFile*, char *elem);
-int vfRemove(VacFile*, char*);
-int vfRead(VacFile*, void *, int n, vlong offset);
-int vfWrite(VacFile*, void *, int n, vlong offset, char *user);
-int vfReadPacket(VacFile*, Packet**, vlong offset);
-int vfWritePacket(VacFile*, Packet*, vlong offset, char *user);
-uvlong vfGetId(VacFile*);
-ulong vfGetMcount(VacFile*);
-int vfIsDir(VacFile*);
-int vfGetBlockScore(VacFile*, ulong bn, uchar score[VtScoreSize]);
-int vfGetSize(VacFile*, uvlong *size);
-int vfGetDir(VacFile*, VacDir*);
-int vfSetDir(VacFile*, VacDir*);
-int vfGetVtEntry(VacFile*, VtEntry*);
-VacFile *vfGetParent(VacFile*);
-int vfSync(VacFile*);
-VacFile *vfIncRef(VacFile*);
-void vfDecRef(VacFile*);
-VacDirEnum *vfDirEnum(VacFile*);
-int vfIsRoot(VacFile *vf);
+VacFile	*vacfileopen(VacFs *fs, char *path);
+VacFile	*vacfilecreate(VacFile *file, char *elem, ulong perm, char *muid);
+VacFile	*vacfilewalk(VacFile *file, char *elem);
+int		vacfileremove(VacFile *file, char *muid);
+int		vacfileread(VacFile *file, void *buf, int n, vlong offset);
+int		vacfileblockscore(VacFile *file, u32int, u8int*);
+int		vacfilewrite(VacFile *file, void *buf, int n, vlong offset, char *muid);
+int		vacfilereadpacket(VacFile *file, Packet **pp, vlong offset);
+int		vacfilewritepacket(VacFile *file, Packet *p, vlong offset, char *muid);
+uvlong	vacfilegetid(VacFile *file);
+ulong	vacfilegetmcount(VacFile *file);
+int		vacfileisdir(VacFile *file);
+int		vacfileisroot(VacFile *file);
+int		vacfilegetblocksize(VacFile *file, u32int bn, u8int *score);
+int		vacfilegetsize(VacFile *file, uvlong *size);
+int		vacfilegetdir(VacFile *file, VacDir *dir);
+int		vacfilesetdir(VacFile *file, VacDir *dir, char *muid);
+int		vacfilegetvtentry(VacFile *file, VtEntry *entry);
+VacFile	*vacfilegetparent(VacFile *file);
+int		vacfilesync(VacFile*);
+VacFile	*vacfileincref(VacFile*);
+int		vacfiledecref(VacFile*);
 
-void	vdCleanup(VacDir *dir);
-void	vdCopy(VacDir *dst, VacDir *src);
+void		vdcleanup(VacDir *dir);
+void		vdcopy(VacDir *dst, VacDir *src);
 
-VacDirEnum *vdeOpen(VacFS*, char *path);
-int vdeRead(VacDirEnum*, VacDir *, int n);
-void vdeFree(VacDirEnum*);
 
+VacDirEnum	*vdeopen(VacFile*);
+int			vderead(VacDirEnum*, VacDir *);
+void			vdeclose(VacDirEnum*);
+
blob - 963e46890295a02bad46533c7738ff26a1b3d07a
blob + daabedc625c96634728a136d068e336d2116a71e
--- src/cmd/vac/vtdump.c
+++ src/cmd/vac/vtdump.c
@@ -24,20 +24,19 @@ int cmp;
 int all;
 int find;
 uchar fscore[VtScoreSize];
-VtSession *z;
+VtConn *z;
 
-int vtGetUint16(uchar *p);
-ulong vtGetUint32(uchar *p);
-uvlong vtGetUint48(uchar *p);
+int vtgetuint16(uchar *p);
+ulong vtgetuint32(uchar *p);
+uvlong vtgetuint48(uchar *p);
 void usage(void);
-int parseScore(uchar *score, char *buf, int n);
-void readRoot(VtRoot*, uchar *score, char *file);
-int dumpDir(Source*, int indent);
+void readroot(VtRoot*, uchar *score, char *file);
+int dumpdir(Source*, int indent);
 
 void
-main(int argc, char *argv[])
+threadmain(int argc, char *argv[])
 {
-	char *host = nil;
+	char *host = nil, *pref;
 	uchar score[VtScoreSize];
 	Source source;
 	uchar buf[VtMaxLumpSize];
@@ -54,7 +53,7 @@ main(int argc, char *argv[])
 	case 'f':
 		find++;
 		p = ARGF();
-		if(p == nil || !parseScore(fscore, p, strlen(p)))
+		if(p == nil || vtparsescore(p, &pref, fscore) < 0 || !pref || strcmp(pref, "vac") != 0)
 			usage();
 		break;
 	case 'a':
@@ -62,46 +61,33 @@ main(int argc, char *argv[])
 		break;
 	}ARGEND
 
-	vtAttach();
-
-	bout = vtMemAllocZ(sizeof(Biobuf));
+	bout = vtmallocz(sizeof(Biobuf));
 	Binit(bout, 1, OWRITE);
 
 	if(argc > 1)
 		usage();
 
-	vtAttach();
+	fmtinstall('V', vtscorefmt);
+	fmtinstall('H', encodefmt);
 
-	fmtinstall('V', vtScoreFmt);
-	fmtinstall('R', vtErrFmt);
-
-	z = vtDial(host, 0);
+	z = vtdial(host);
 	if(z == nil)
-		vtFatal("could not connect to server: %s", vtGetError());
+		sysfatal("could not connect to server: %r");
 
-	if(!vtConnect(z, 0))
-		sysfatal("vtConnect: %r");
+	if(vtconnect(z) < 0)
+		sysfatal("vtconnect: %r");
 
-	readRoot(&root, score, argv[0]);
-	ver = root.version;
-	bsize = root.blockSize;
+	readroot(&root, score, argv[0]);
+	bsize = root.blocksize;
 	if(!find) {
 		Bprint(bout, "score: %V\n", score);
-		Bprint(bout, "version: %d\n", ver);
 		Bprint(bout, "name: %s\n", root.name);
 		Bprint(bout, "type: %s\n", root.type);
 		Bprint(bout, "bsize: %d\n", bsize);
 		Bprint(bout, "prev: %V\n", root.prev);
 	}
 
-	switch(ver) {
-	default:
-		sysfatal("unknown version");
-	case VtRootVersion:
-		break;
-	}
-
-	n = vtRead(z, root.score, VtDirType, buf, bsize);
+	n = vtread(z, root.score, VtDirType, buf, bsize);
 	if(n < 0)
 		sysfatal("could not read root dir");
 
@@ -115,17 +101,16 @@ main(int argc, char *argv[])
 	source.depth = 0;
 	source.size = n;
 
-	dumpDir(&source, 0);
+	dumpdir(&source, 0);
 
 	Bterm(bout);
 
-	vtClose(z);
-	vtDetach();
-	exits(0);
+	vthangup(z);
+	threadexitsall(0);
 }
 
 void
-sourcePrint(Source *s, int indent, int entry)
+sourceprint(Source *s, int indent, int entry)
 {
 	int i;
 	uvlong size;
@@ -165,11 +150,11 @@ parse(Source *s, uchar *p)
 	VtEntry dir;
 
 	memset(s, 0, sizeof(*s));
-	if(!vtEntryUnpack(&dir, p, 0))
-		return 0;
+	if(vtentryunpack(&dir, p, 0) < 0)
+		return -1;
 
 	if(!(dir.flags & VtEntryActive))
-		return 1;
+		return 0;
 
 	s->active = 1;
 	s->gen = dir.gen;
@@ -177,21 +162,23 @@ parse(Source *s, uchar *p)
 	s->dsize = dir.size;
 	s->size = dir.size;
 	memmove(s->score, dir.score, VtScoreSize);
-	if(dir.flags & VtEntryDir)
+	if((dir.type&~VtTypeDepthMask) == VtDirType)
 		s->dir = 1;
-	s->depth = dir.depth;
-	return 1;
-
+fprint(2, "sdir %d type %d %.*H\n", s->dir, dir.type, VtEntrySize, p);
+	s->depth = dir.type&VtTypeDepthMask;
+	return 0;
 }
 
 int
-sourceRead(Source *s, ulong block, uchar *p, int n)
+sourceread(Source *s, ulong block, uchar *p, int n)
 {
-	uchar buf[VtMaxLumpSize];
+	uchar *buf;
 	uchar score[VtScoreSize];
 	int i, nn, np, type;
 	int elem[VtPointerDepth];
 
+	buf = vtmalloc(VtMaxLumpSize);
+
 	memmove(score, s->score, VtScoreSize);
 
 	np = s->psize/VtScoreSize;
@@ -202,17 +189,17 @@ sourceRead(Source *s, ulong block, uchar *p, int n)
 	assert(block == 0);
 
 	for(i=s->depth-1; i>=0; i--) {
-		nn = vtRead(z, score, VtPointerType0+i, buf, s->psize);
-		if(nn < 0)
+		nn = vtread(z, score, (s->dir ? VtDirType : VtDataType)+1+i, buf, s->psize);
+		if(nn < 0){
+fprint(2, "vtread %V %d: %r\n", score, (s->dir ? VtDirType : VtDataType)+1+i);
+			free(buf);
 			return -1;
-
-		if(!vtSha1Check(score, buf, nn)) {
-			vtSetError("vtSha1Check failed on root block");
-			return -1;
 		}
 
-		if((elem[i]+1)*VtScoreSize > nn)
+		if((elem[i]+1)*VtScoreSize > nn){
+			free(buf);
 			return 0;
+		}
 		memmove(score, buf + elem[i]*VtScoreSize, VtScoreSize);
 	}
 
@@ -221,20 +208,20 @@ sourceRead(Source *s, ulong block, uchar *p, int n)
 	else
 		type = VtDataType;
 
-	nn = vtRead(z, score, type, p, n);
-	if(nn < 0)
+	nn = vtread(z, score, type, p, n);
+	if(nn < 0){
+fprint(2, "vtread %V %d: %r\n", score, type);
+abort();
+		free(buf);
 		return -1;
-
-	if(!vtSha1Check(score, p, nn)) {
-		vtSetError("vtSha1Check failed on root block");
-		return -1;
 	}
 	
+	free(buf);
 	return nn;
 }
 
 void
-dumpFileContents(Source *s)
+dumpfilecontents(Source *s)
 {
 	int nb, lb, i, n;
 	uchar buf[VtMaxLumpSize];
@@ -243,9 +230,9 @@ dumpFileContents(Source *s)
 	lb = s->size%s->dsize;
 	for(i=0; i<nb; i++) {
 		memset(buf, 0, s->dsize);
-		n = sourceRead(s, i, buf, s->dsize);
+		n = sourceread(s, i, buf, s->dsize);
 		if(n < 0) {	
-			fprint(2, "could not read block: %d: %s\n", i, vtGetError());
+			fprint(2, "could not read block: %d: %r\n", i);
 			continue;
 		}
 		if(i < nb-1)
@@ -256,42 +243,45 @@ dumpFileContents(Source *s)
 }
 
 void
-dumpFile(Source *s, int indent)
+dumpfile(Source *s, int indent)
 {
 	int nb, i, j, n;
-	uchar buf[VtMaxLumpSize];
+	uchar *buf;
 	uchar score[VtScoreSize];
 
+	buf = vtmalloc(VtMaxLumpSize);
 	nb = (s->size + s->dsize - 1)/s->dsize;
 	for(i=0; i<nb; i++) {
 		memset(buf, 0, s->dsize);
-		n = sourceRead(s, i, buf, s->dsize);
+		n = sourceread(s, i, buf, s->dsize);
 		if(n < 0) {	
-			fprint(2, "could not read block: %d: %s\n", i, vtGetError());
+			fprint(2, "could not read block: %d: %r\n", i);
 			continue;
 		}
 		for(j=0; j<indent; j++)
 			Bprint(bout, " ");
-		vtSha1(score, buf, n);		
+		sha1(buf, n, score, nil);		
 		Bprint(bout, "%4d: size: %ud: %V\n", i, n, score);
 	}
+	vtfree(buf);
 }
 
 int
-dumpDir(Source *s, int indent)
+dumpdir(Source *s, int indent)
 {
 	int pb, ne, nb, i, j, n, entry;
-	uchar buf[VtMaxLumpSize];
 	Source ss;
+	uchar *buf;
 
+	buf = vtmalloc(VtMaxLumpSize);
 	pb = s->dsize/VtEntrySize;
 	ne = pb*(s->size/s->dsize) + (s->size%s->dsize)/VtEntrySize;
 	nb = (s->size + s->dsize - 1)/s->dsize;
 	for(i=0; i<nb; i++) {
 		memset(buf, 0, s->dsize);
-		n = sourceRead(s, i, buf, s->dsize);
+		n = sourceread(s, i, buf, s->dsize);
 		if(n < 0) {	
-			fprint(2, "could not read block: %d: %s\n", i, vtGetError());
+			fprint(2, "could not read block: %d: %r\n", i);
 			continue;
 		}
 		for(j=0; j<pb; j++) {
@@ -301,63 +291,40 @@ dumpDir(Source *s, int indent)
 			parse(&ss, buf + j * VtEntrySize);
 
 			if(!find)
-				sourcePrint(&ss, indent, entry);
+				sourceprint(&ss, indent, entry);
 			else if(memcmp(ss.score, fscore, VtScoreSize) == 0) {
-				dumpFileContents(&ss);
-				return 0;
+				dumpfilecontents(&ss);
+				free(buf);
+				return -1;
 			}
 
 			if(ss.dir) {
-				if(!dumpDir(&ss, indent+1))
-					return 0;
+				if(dumpdir(&ss, indent+1) < 0){
+					free(buf);
+					return -1;
+				}
 			} else if(all)
-				dumpFile(&ss, indent+1);
+				dumpfile(&ss, indent+1);
 		}
 	}
-	return 1;
+	free(buf);
+	return 0;
 }
 
 void
 usage(void)
 {
 	fprint(2, "%s: [file]\n", argv0);
-	exits("usage");
+	threadexits("usage");
 }
 
-int
-parseScore(uchar *score, char *buf, int n)
-{
-	int i, c;
-
-	memset(score, 0, VtScoreSize);
-
-	if(n < VtScoreSize*2)
-		return 0;
-	for(i=0; i<VtScoreSize*2; i++) {
-		if(buf[i] >= '0' && buf[i] <= '9')
-			c = buf[i] - '0';
-		else if(buf[i] >= 'a' && buf[i] <= 'f')
-			c = buf[i] - 'a' + 10;
-		else if(buf[i] >= 'A' && buf[i] <= 'F')
-			c = buf[i] - 'A' + 10;
-		else {
-			return 0;
-		}
-
-		if((i & 1) == 0)
-			c <<= 4;
-	
-		score[i>>1] |= c;
-	}
-	return 1;
-}
-
 void
-readRoot(VtRoot *root, uchar *score, char *file)
+readroot(VtRoot *root, uchar *score, char *file)
 {
 	int fd;
-	uchar buf[VtRootSize];
-	int i, n, nn;
+	char *pref;
+	char buf[VtRootSize];
+	int n, nn;
 
 	if(file == 0)
 		fd = 0;
@@ -369,23 +336,17 @@ readRoot(VtRoot *root, uchar *score, char *file)
 	n = readn(fd, buf, sizeof(buf)-1);
 	if(n < 0)
 		sysfatal("read failed: %r\n");
-	buf[n] = 0;
+	if(n==0 || buf[n-1] != '\n')
+		sysfatal("not a root file");
+	buf[n-1] = 0;
 	close(fd);
 
-	for(i=0; i<n; i++) {
-		if(!parseScore(score, (char*)(buf+i), n-i))
-			continue;
-		nn = vtRead(z, score, VtRootType, buf, VtRootSize);
-		if(nn >= 0) {
-			if(nn != VtRootSize)
-				sysfatal("vtRead on root too short");
-			if(!vtSha1Check(score, buf, VtRootSize))
-				sysfatal("vtSha1Check failed on root block");
-			if(!vtRootUnpack(root, buf))
-				sysfatal("could not parse root: %r");
-			return;
-		}
+	if(vtparsescore(buf, &pref, score) < 0){
+		sysfatal("not a root file");
 	}
-
-	sysfatal("could not find root");
+	nn = vtread(z, score, VtRootType, buf, VtRootSize);
+	if(nn < 0)
+		sysfatal("cannot read root %V", score);
+	if(vtrootunpack(root, buf) < 0)
+		sysfatal("cannot parse root: %r");
 }