tVac works. - plan9port - [fork] Plan 9 from user space
 (HTM) git clone git://src.adamsgaard.dk/plan9port
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 3d77c87e81bf16aeaf52ba0f523af6708c5c4964
 (DIR) parent 333c1dccc2f9af67b9c3d8513cca492d022fab4f
 (HTM) Author: rsc <devnull@localhost>
       Date:   Mon, 15 Mar 2004 01:56:49 +0000
       
       Vac works.
       
       Diffstat:
         D src/cmd/vac/cache.c                 |     876 -------------------------------
         M src/cmd/vac/dat.h                   |     149 ++++---------------------------
         M src/cmd/vac/error.c                 |       2 ++
         M src/cmd/vac/error.h                 |       2 ++
         M src/cmd/vac/file.c                  |    2278 ++++++++++++++++++++-----------
         M src/cmd/vac/fns.h                   |      55 ++++++++-----------------------
         M src/cmd/vac/fs.c                    |     187 ++++++++++++++-----------------
         M src/cmd/vac/mkfile                  |       3 +--
         M src/cmd/vac/pack.c                  |     246 ++++++++++++++++++++++---------
         D src/cmd/vac/source.c                |     390 -------------------------------
         M src/cmd/vac/stdinc.h                |       9 +++------
         M src/cmd/vac/vac.c                   |     746 +++++++++++++++++++------------
         M src/cmd/vac/vac.h                   |     109 +++++++++++++++++--------------
         M src/cmd/vac/vtdump.c                |     215 +++++++++++++------------------
       
       14 files changed, 2366 insertions(+), 2901 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/vac/cache.c b/src/cmd/vac/cache.c
       t@@ -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;
       -}
 (DIR) diff --git a/src/cmd/vac/dat.h b/src/cmd/vac/dat.h
       t@@ -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 */
       t@@ -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 */
       -};
       -
       -/* 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 */
       +        u32int boff;
       +        int i, n;
       +        VacDir *buf;
        };
        
       +void _mbinit(MetaBlock*, u8int*, uint, uint);
       +int _mbsearch(MetaBlock*, char*, int*, MetaEntry*);
 (DIR) diff --git a/src/cmd/vac/error.c b/src/cmd/vac/error.c
       t@@ -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";
 (DIR) diff --git a/src/cmd/vac/error.h b/src/cmd/vac/error.h
       t@@ -12,3 +12,5 @@ extern char ERemoved[];
        extern char ENotEmpty[];
        extern char EExists[];
        extern char ERoot[];
       +extern char ENoFile[];
       +extern char EBadPath[];
 (DIR) diff --git a/src/cmd/vac/file.c b/src/cmd/vac/file.c
       t@@ -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        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 */
       -
       -        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 */
       +        int                ref;        /* holds this data structure up */
       +
       +        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 */
       +
       +        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 void
       -vfRUnlock(VacFile *vf)
       -{
       -        vtRUnlock(vf->lk);
       -}
       -        
       -
       -static int
       -vfRLock(VacFile *vf)
       -{
       -        vtRLock(vf->lk);
       -        if(vf->source == nil) {
       -                vfRUnlock(vf);
       -                vtSetError(ERemoved);
       -                return 0;
       -        }
       -        return 1;
       -}
       -
       -static void
       -vfUnlock(VacFile *vf)
       -{
       -        vtUnlock(vf->lk);
       -}
       -
       -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)
       -{
       -        assert(vf->up->msource != nil);
       -        vtLock(vf->up->lk);
       -}
       -
       -static void
       -vfMetaUnlock(VacFile *vf)
       -{
       -        vtUnlock(vf->up->lk);
       -}
       -
       -
       -static void
       -vfRAccess(VacFile* vf)
       +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*);
       +
       +void mbinit(MetaBlock*, u8int*, uint, uint);
       +int mbsearch(MetaBlock*, char*, int*, MetaEntry*);
       +int mbresize(MetaBlock*, MetaEntry*, int);
       +VacFile *vdlookup(VacFile*, char*);
       +
       +static VacFile*
       +filealloc(VacFs *fs)
        {
       -        vfMetaLock(vf);
       -        vf->dir.atime = time(0L);
       -        vf->dirty = 1;
       -        vfMetaUnlock(vf);
       -        vfMetaFlush(vf);
       +        VacFile *f;
       +
       +        f = vtmallocz(sizeof(VacFile));
       +        f->ref = 1;
       +        f->fs = fs;
       +        f->boff = NilBlock;
       +        f->mode = fs->mode;
       +        return f;
        }
        
        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)
       +filefree(VacFile *f)
        {
       -        *dst = *src;
       -        dst->elem = vtStrDup(src->elem);
       -        dst->uid = vtStrDup(src->uid);
       -        dst->gid = vtStrDup(src->gid);
       -        dst->mid = vtStrDup(src->mid);
       +        vtfileclose(f->source);
       +        vtfileclose(f->msource);
       +        vdcleanup(&f->dir);
       +        memset(f, ~0, sizeof *f);        /* paranoia */
       +        vtfree(f);
        }
        
       -static int
       -mbSearch(MetaBlock *mb, char *elem, int *ri, MetaEntry *me)
       +/*
       + * the file is locked already
       + * f->msource is unlocked
       + */
       +static VacFile*
       +dirlookup(VacFile *f, char *elem)
        {
                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;
       -
       -//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 */
       -                }
       -                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);
       -        }
       -
       -        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))
       +        VtBlock *b;
       +        VtFile *meta;
       +        VacFile *ff;
       +        u32int bo, nb;
       +
       +        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(!mbSearch(&mb, elem, &j, &me))
       +                if(mbunpack(&mb, b->data, meta->dsize) < 0)
                                goto Err;
       -                if(me.p != nil) {
       -                        nvf = vfAlloc(vf->fs);
       -                        if(!vdUnpack(&nvf->dir, &me)) {
       -                                vfFree(nvf);
       +                if(mbsearch(&mb, elem, &i, &me) < 0){
       +                        ff = filealloc(f->fs);
       +                        if(vdunpack(&ff->dir, &me) < 0){
       +                                filefree(ff);
                                        goto Err;
                                }
       -                        lumpDecRef(u, 1);
       -                        nvf->block = i;
       -                        return nvf;
       +                        vtfileunlock(meta);
       +                        vtblockput(b);
       +                        ff->boff = bo;
       +                        ff->mode = f->mode;
       +                        return ff;
                        }
       -                
       -                lumpDecRef(u, 1);
       -                u = nil;
       +
       +                vtblockput(b);
       +                b = 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;
       t@@ -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;
       -
       -        if(!meUnpack(&me, &mb, 0))
       +        b = vtfileblock(mr->msource, 0, VtOREAD);
       +        vtfileunlock(mr->msource);
       +        if(b == nil)
                        goto Err;
       -        if(!vdUnpack(&root->dir, &me))
       +
       +        if(mbunpack(&mb, b->data, mr->msource->dsize) < 0)
                        goto Err;
        
       -        vfRAccess(root);
       -        lumpDecRef(u, 0);
       -        sourceFree(r2);
       +        meunpack(&me, &mb, 0);
       +        if(vdunpack(&root->dir, &me) < 0)
       +                goto Err;
       +        vtblockput(b);
       +        vtfileunlock(r);
       +        fileraccess(root);
        
                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;
       +}
       +
       +static VtFile *
       +fileopensource(VacFile *f, u32int offset, u32int gen, int dir, uint mode)
       +{
       +        VtFile *r;
        
       +        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;
        }
        
       -VacFile *
       -vfWalk(VacFile *vf, char *elem)
       +VacFile*
       +_filewalk(VacFile *f, char *elem, int partial)
        {
       -        VacFile *nvf;
       +        VacFile *ff;
        
       -        vfRAccess(vf);
       +        fileraccess(f);
        
       -        if(elem[0] == 0) {
       -                vtSetError("illegal path element");
       +        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)
       +{
       +        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 *nvf;
       +        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;
       +        }
       +
       +        pr = f->source;
       +        if(pr->mode != VtORDWR){
       +                werrstr(EReadOnly);
       +                goto Err1;
                }
        
       -        nvf = vfAlloc(vf->fs);
       +        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)
       -                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))
       +        ff->boff = filemetaalloc(f, dir, 0);
       +        if(ff->boff == NilBlock)
                        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);
       +
       +        ff->source = r;
       +        ff->msource = mr;
        
       -        nvf->source = r;
       -        nvf->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
       +vacfileblockscore(VacFile *f, u32int bn, u8int *score)
       +{
       +        VtFile *s;
       +        uvlong size;
       +        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
       -vfRead(VacFile *vf, void *buf, int cnt, vlong offset)
       +vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
        {
       -        Source *s;
       +        VtFile *s;
                uvlong size;
       -        ulong bn;
       +        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;
       t@@ -674,541 +566,1269 @@ if(0)fprint(2, "vfRead: %s %d, %lld\n", vf->dir.elem, cnt, offset);
                        cnt = size-offset;
                bn = offset/dsize;
                off = offset%dsize;
       -        b = buf;
       -        while(cnt > 0) {
       -                u = sourceGetLump(s, bn, 1, 0);
       -                if(u == nil)
       -                        goto Err;
       -                if(u->asize <= off) {
       -                        lumpDecRef(u, 0);
       +        p = buf;
       +        while(cnt > 0){
       +                b = vtfileblock(s, bn, OREAD);
       +                if(b == nil)
                                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);
       +        }
       +        vtfileunlock(s);
       +        filerunlock(f);
       +        return p-(uchar*)buf;
       +
       +Err:
       +        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
       +filemapblock(VacFile *f, ulong bn, uchar score[VtScoreSize], ulong tag)
       +{
       +        VtBlock *b;
       +        VtEntry e;
       +        VtFile *s;
       +
       +        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;
                }
       -        vfRUnlock(vf);
       -        return b-(uchar*)buf;
       +
       +        if(vtfilelock(f->source, -1) < 0)
       +                goto Err;
       +
       +        s = f->source;
       +        b = _vtfileblock(s, bn, VtORDWR, 1, tag);
       +        if(b == nil)
       +                goto Err;
       +
       +        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;
       +
        Err:
       -        vfRUnlock(vf);
       +        if(s)
       +                vtfileunlock(s);
       +        fileunlock(f);
                return -1;
        }
       +#endif
       +
       +int
       +vacfilesetsize(VacFile *f, uvlong size)
       +{
       +        int r;
       +
       +        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;
       +}
        
        int
       -vfWrite(VacFile *vf, void *buf, int cnt, vlong offset, char *user)
       +filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
        {
       -        Source *s;
       +        VtFile *s;
                ulong bn;
                int off, dsize, n;
       -        Lump *u;
       -        uchar *b;
       +        VtBlock *b;
       +        uchar *p;
       +        vlong eof;
        
       -        USED(user);
       +if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
        
       -        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(offset < 0) {
       -                vtSetError(EBadOffset);
       +        if(offset < 0){
       +                werrstr(EBadOffset);
                        goto Err;
                }
        
       -        vfWAccess(vf, user);
       +        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;
       -        b = buf;
       -        while(cnt > 0) {
       +        p = 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);
       +                b = vtfileblock(s, bn, n<dsize?VtORDWR:VtOWRITE);
       +                if(b == nil){
       +                        if(offset > eof)
       +                                vtfilesetsize(s, offset);
                                goto Err;
                        }
       -                memmove(u->data+off, b, n);
       +                memmove(b->data+off, p, n);
                        off = 0;
                        cnt -= n;
       -                b += n;
       +                p += n;
                        offset += n;
                        bn++;
       -                lumpDecRef(u, 0);
       -                if(!sourceSetSize(s, offset))
       -                        goto Err;
       +                vtblockdirty(b);
       +                vtblockput(b);
                }
       -        vfLock(vf);
       -        return b-(uchar*)buf;
       +        if(offset > eof && vtfilesetsize(s, offset) < 0)
       +                goto Err;
       +        vtfileunlock(s);
       +        fileunlock(f);
       +        return p-(uchar*)buf;
        Err:
       -        vfLock(vf);
       +        if(s)
       +                vtfileunlock(s);
       +        fileunlock(f);
                return -1;
        }
        
        int
       -vfGetDir(VacFile *vf, VacDir *dir)
       +vacfilegetdir(VacFile *f, VacDir *dir)
        {
       -        if(!vfRLock(vf))
       -                return 0;
       -
       -        vfMetaLock(vf);
       -        vdCopy(dir, &vf->dir);
       -        vfMetaUnlock(vf);
       +        if(filerlock(f) < 0)
       +                return -1;
        
       -        if(!vfIsDir(vf))
       -                dir->size = sourceGetSize(vf->source);
       -        vfRUnlock(vf);
       +        filemetalock(f);
       +        vdcopy(dir, &f->dir);
       +        filemetaunlock(f);
        
       -        return 1;
       +        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
       +vacfiletruncate(VacFile *f, char *uid)
       +{
       +        if(vacfileisdir(f)){
       +                werrstr(ENotFile);
       +                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;
       +}
       +
       +int
       +vacfilesetdir(VacFile *f, VacDir *dir, char *uid)
       +{
       +        VacFile *ff;
       +        char *oelem;
       +        u32int mask;
       +        u64int size;
       +
       +        /* can not set permissions for the root */
       +        if(vacfileisroot(f)){
       +                werrstr(ERoot);
       +                return -1;
       +        }
       +
       +        if(filelock(f) < 0)
       +                return -1;
       +
       +        if(f->source->mode != VtORDWR){
       +                werrstr(EReadOnly);
       +                fileunlock(f);
       +                return -1;
       +        }
       +
       +        filemetalock(f);
       +
       +        /* 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(!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);
       +
       +        oelem = nil;
       +        if(strcmp(f->dir.elem, dir->elem) != 0){
       +                oelem = f->dir.elem;
       +                f->dir.elem = vtstrdup(dir->elem);
       +        }
       +
       +        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;
       +
       +        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
       -vfGetId(VacFile *vf)
       +vacfilegetid(VacFile *f)
        {
                /* immutable */
       -        return vf->dir.qid;
       +        return f->dir.qid;
        }
        
        ulong
       -vfGetMcount(VacFile *vf)
       +vacfilegetmcount(VacFile *f)
        {
                ulong mcount;
       -        
       -        vfMetaLock(vf);
       -        mcount = vf->dir.mcount;
       -        vfMetaUnlock(vf);
       +
       +        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
       -vfIsDir(VacFile *vf)
       +vacfileisdir(VacFile *f)
        {
                /* immutable */
       -        return (vf->dir.mode & ModeDir) != 0;
       +        return (f->dir.mode & ModeDir) != 0;
        }
        
        int
       -vfIsRoot(VacFile *vf)
       +vacfileisroot(VacFile *f)
        {
       -        return vf == vf->fs->root;
       +        return f == f->fs->root;
        }
        
        int
       -vfGetSize(VacFile *vf, uvlong *size)
       +vacfilegetsize(VacFile *f, uvlong *size)
        {
       -        if(!vfRLock(vf))
       +        if(filerlock(f) < 0)
                        return 0;
       -        *size = sourceGetSize(vf->source);
       -        vfRUnlock(vf);
       +        if(vtfilelock(f->source, VtOREAD) < 0){
       +                filerunlock(f);
       +                return -1;
       +        }
       +        *size = vtfilegetsize(f->source);
       +        vtfileunlock(f->source);
       +        filerunlock(f);
        
       -        return 1;
       +        return 0;
       +}
       +
       +int
       +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);
       +
       +        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
       -vfMetaRemove(VacFile *vf, char *user)
       +filemetaflush2(VacFile *f, char *oelem)
        {
       -        Lump *u;
       +        VacFile *fp;
       +        VtBlock *b, *bb;
                MetaBlock mb;
       -        MetaEntry me;
       -        int i;
       -        VacFile *vfp;
       +        MetaEntry me, me2;
       +        int i, n;
       +        u32int boff;
        
       -        vfp = vf->up;
       +        if(!f->dirty)
       +                return 0;
        
       -        vfWAccess(vfp, user);
       +        if(oelem == nil)
       +                oelem = f->dir.elem;
        
       -        vfMetaLock(vf);
       +        fp = f->up;
        
       -        u = sourceGetLump(vfp->msource, vf->block, 0, 1);
       -        if(u == nil)
       +        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;
       -
       -        if(!mbUnpack(&mb, u->data, u->asize))
       +        if(mbsearch(&mb, oelem, &i, &me) < 0)
                        goto Err;
       -        if(!mbSearch(&mb, vf->dir.elem, &i, &me) || me.p == 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;
       +        }
       +
       +        /*
       +         * 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;
       -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;
       -
       -        vfMetaUnlock(vf);
       -        return 1;
       +        }
       +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;
        
       -Err:
       -        lumpDecRef(u, 1);
       -        vfMetaUnlock(vf);
                return 0;
       +
       +Err:
       +        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;
       +}
        
       +/* assume file is locked, assume f->msource is locked */
        static int
       -vfCheckEmpty(VacFile *vf)
       +filecheckempty(VacFile *f)
        {
       -        int i, n;
       -        Lump *u;
       +        u32int i, n;
       +        VtBlock *b;
                MetaBlock mb;
       -        Source *r;
       +        VtFile *r;
        
       -        r = vf->msource;
       -        n = sourceGetNumBlocks(r);
       -        for(i=0; i<n; i++) {
       -                u = sourceGetLump(r, i, 1, 1);
       -                if(u == nil)
       +        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, u->data, u->asize))
       +                if(mbunpack(&mb, b->data, r->dsize) < 0)
                                goto Err;
       -                if(mb.nindex > 0) {
       -                        vtSetError(ENotEmpty);
       +                if(mb.nindex > 0){
       +                        werrstr(ENotEmpty);
                                goto Err;
                        }
       -                lumpDecRef(u, 1);
       +                vtblockput(b);
                }
       -        return 1;
       -Err:
       -        lumpDecRef(u, 1);
                return 0;
       +Err:
       +        vtblockput(b);
       +        return -1;
        }
        
        int
       -vfRemove(VacFile *vf, char *user)
       -{        
       +vacfileremove(VacFile *f, char *muid)
       +{
       +        VacFile *ff;
       +
                /* can not remove the root */
       -        if(vfIsRoot(vf)) {
       -                vtSetError(ERoot);
       -                return 0;
       +        if(vacfileisroot(f)){
       +                werrstr(ERoot);
       +                return -1;
                }
        
       -        if(!vfLock(vf))
       -                return 0;
       +        if(filelock(f) < 0)
       +                return -1;
        
       -        if(vfIsDir(vf) && !vfCheckEmpty(vf))
       +        if(f->source->mode != VtORDWR){
       +                werrstr(EReadOnly);
       +                goto Err1;
       +        }
       +        if(vtfilelock2(f->source, f->msource, -1) < 0)
       +                goto Err1;
       +        if(vacfileisdir(f) && filecheckempty(f)<0)
                        goto Err;
       -                        
       -        assert(vf->down == nil);
       -
       -        sourceRemove(vf->source);
       -        vf->source = nil;
       -        if(vf->msource) {
       -                sourceRemove(vf->msource);
       -                vf->msource = nil;
       -        }
       -        
       -        vfUnlock(vf);
       -        
       -        if(!vfMetaRemove(vf, user))
       -                return 0;
       -        
       -        return 1;
       -                
       -Err:
       -        vfUnlock(vf);
       +
       +        for(ff=f->down; ff; ff=ff->next)
       +                assert(ff->removed);
       +
       +        vtfileremove(f->source);
       +        f->source = nil;
       +        if(f->msource){
       +                vtfileremove(f->msource);
       +                f->msource = nil;
       +        }
       +
       +        fileunlock(f);
       +
       +        if(filemetaremove(f, muid) < 0)
       +                return -1;
       +
                return 0;
       -}
        
       -VacFile *
       -vfIncRef(VacFile *vf)
       -{
       -        vfMetaLock(vf);
       -        assert(vf->ref > 0);
       -        vf->ref++;
       -        vfMetaUnlock(vf);
       -        return vf;
       +Err:
       +        vtfileunlock(f->source);
       +        if(f->msource)
       +                vtfileunlock(f->msource);
       +Err1:
       +        fileunlock(f);
       +        return -1;
        }
        
       -void
       -vfDecRef(VacFile *vf)
       +static int
       +clri(VacFile *f, char *uid)
        {
       -        VacFile *p, *q, **qq;
       -
       -        if(vf->up == nil) {
       -                vfFree(vf);
       -                return;
       -        }
       +        int r;
        
       -        vfMetaLock(vf);
       -        vf->ref--;
       -        if(vf->ref > 0) {
       -                vfMetaUnlock(vf);
       -                return;
       +        if(f == nil)
       +                return -1;
       +        if(f->up->source->mode != VtORDWR){
       +                werrstr(EReadOnly);
       +                vacfiledecref(f);
       +                return -1;
                }
       -        assert(vf->ref == 0);
       -        assert(vf->down == nil);
       -
       -        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;
       -
       -        vfMetaUnlock(vf);
       -        vfFree(vf);
       +        r = filemetaremove(f, uid);
       +        vacfiledecref(f);
       +        return r;
       +}
        
       -        vfDecRef(p);
       +int
       +vacfileclripath(VacFs *fs, char *path, char *uid)
       +{
       +        return clri(_fileopen(fs, path, 1), uid);
        }
        
        int
       -vfGetVtEntry(VacFile *vf, VtEntry *e)
       +vacfileclri(VacFile *dir, char *elem, char *uid)
        {
       -        int res;
       +        return clri(_filewalk(dir, elem, 1), uid);
       +}
        
       -        if(!vfRLock(vf))
       -                return 0;
       -        res = sourceGetVtEntry(vf->source, e);
       -        vfRUnlock(vf);
       -        return res;
       +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)
       -                goto Err;
       -        if(u->asize < (elem+1)*VtEntrySize) {
       -                vtSetError(ENoDir);
       +        b = vtfileblock(s, bn, VtOREAD);
       +        if(b == nil)
                        goto Err;
       -        }
       -        vtEntryUnpack(&e, u->data, elem);
       -        if(!(e.flags & VtEntryActive) || e.gen != gen) {
       -fprint(2, "gen mismatch\n");
       -                vtSetError(ENoDir);
       +        if(vtentryunpack(&e, b->data, elem) < 0)
                        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;
       -
       -        if(ds->block >= nb)
       -                goto Exit;
       -        u = sourceGetLump(meta, ds->block, 1, 1);
       -        if(u == nil)
       +        source = f->source;
       +        meta = f->msource;
       +
       +        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;
       +                vde->n++;
       +                if(!(de->mode & ModeDir))
       +                if(direntrysize(source, de->entry, de->gen, &de->size) < 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++;
                }
       -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))
       -                        goto Err;
       -                u = sourceGetLump(ms, i, 0, 1);
       -                if(u == nil)
       +        if(b == nil){
       +                b = vtfileblock(ms, bo, VtORDWR);
       +                if(b == 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);
       +        }
       +
       +        p = mballoc(&mb, n);
       +        if(p == nil){
       +                /* mbAlloc might have changed block */
       +                mbpack(&mb);
       +                vtblockdirty(b);
       +                werrstr(EBadMeta);
       +                goto Err;
                }
       -        lumpDecRef(u, 1);
       -        return i;
       +
       +        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
 (DIR) diff --git a/src/cmd/vac/fns.h b/src/cmd/vac/fns.h
       t@@ -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);
       -
       -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);
       +VacFile *_vacfileroot(VacFs *fs, VtFile *file);
        
       +int        _vacfsnextqid(VacFs *fs, uvlong *qid);
 (DIR) diff --git a/src/cmd/vac/fs.c b/src/cmd/vac/fs.c
       t@@ -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");
       -                return nil;
       -        }
       -
       -        if(!vtSha1Check(score, buf, VtRootSize)) {
       -                vtSetError("vtSha1Check failed on root block");        
       +        if(n != VtRootSize){
       +                werrstr("vtread on root too short");
                        return nil;
                }
        
       -        if(!vtRootUnpack(&rt, buf))
       +        if(vtrootunpack(&rt, buf) < 0)
                        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;
       t@@ -106,83 +113,63 @@ vfsOpen(VtSession *z, char *file, int readOnly, long ncache)
                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])
       -{
       -        memmove(fs, score, VtScoreSize);
       -        return 1;
       -}
       -
       -long
       -vfsGetCacheSize(VacFS *fs)
       +vacfsgetscore(VacFs *fs, u8int *score)
        {
       -        return cacheGetSize(fs->cache);
       +        memmove(score, fs->score, VtScoreSize);
       +        return 0;
        }
        
        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*)
       -{
       -        return 1;
       -}
       -
       -int
       -vfsClose(VacFS *fs)
       +void
       +vacfsclose(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);
        }
        
       -
 (DIR) diff --git a/src/cmd/vac/mkfile b/src/cmd/vac/mkfile
       t@@ -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=\
 (DIR) diff --git a/src/cmd/vac/pack.c b/src/cmd/vac/pack.c
       t@@ -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
       t@@ -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;
        
       t@@ -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;
        
       t@@ -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);
       t@@ -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;
        
       t@@ -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;
       t@@ -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;
       t@@ -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;
       t@@ -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;
       t@@ -230,7 +230,7 @@ meCmp(MetaEntry *me, char *s)
        }
        
        int
       -meCmpNew(MetaEntry *me, char *s)
       +mecmpnew(MetaEntry *me, char *s)
        {
                int n;
                uchar *p;
       t@@ -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;
       t@@ -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);
       t@@ -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;
       t@@ -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;
        
       t@@ -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;
       t@@ -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;
                
       t@@ -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;
       t@@ -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);
       t@@ -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);
       t@@ -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);
       t@@ -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;
       t@@ -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");
       t@@ -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");
       t@@ -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;
       t@@ -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;
        }
 (DIR) diff --git a/src/cmd/vac/source.c b/src/cmd/vac/source.c
       t@@ -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);
       -}
 (DIR) diff --git a/src/cmd/vac/stdinc.h b/src/cmd/vac/stdinc.h
       t@@ -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>
 (DIR) diff --git a/src/cmd/vac/vac.c b/src/cmd/vac/vac.c
       t@@ -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;
       +        case 'i':
       +                isi = ARGF();
       +                if(isi == nil)
       +                        usage();
       +                break;
       +        case 'n':
       +                nowrite++;
       +                break;
       +        case 'm':
       +                merge++;
       +                break;
       +        case 'q':
       +                qdiff++;
                        break;
       -        }ARGEND
       +        case 's':
       +                statsflag++;
       +                break;
       +        case 'v':
       +                verbose++;
       +                break;
       +        }ARGEND;
        
                if(bsize < 512)
                        bsize = 512;
       t@@ -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;
       t@@ -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);
                }
                        
        
       t@@ -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;
       t@@ -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);
                }
       t@@ -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);
       -        
       -        vtRootPack(&root, buf);
       -        if(!vacWrite(z, score, VtRootType, buf, VtRootSize))
       -                vtFatal("vacWrite failed: %s", vtGetError());
       -
       -        fprint(fd, "vac:");
       -        for(i=0; i<VtScoreSize; i++)
       -                fprint(fd, "%.2x", score[i]);
       -        fprint(fd, "\n");
       +                vacfsclose(fs);
                
       +        vtrootpack(&root, buf);
       +        if(vacwrite(z, score, VtRootType, buf, VtRootSize) < 0)
       +                sysfatal("vacWrite failed: %r");
       +
       +        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;
        
       t@@ -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;
                }
        
       t@@ -237,24 +408,25 @@ vacFile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
                        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;
       t@@ -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;
       t@@ -293,24 +477,24 @@ vacDataSkip(Sink *sink, VacFile *vf, int fd, ulong blocks, uchar *buf, char *lna
                        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;
       t@@ -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;
       t@@ -332,13 +516,13 @@ vacData(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
        
                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;
                        }
        
       t@@ -349,30 +533,30 @@ vacData(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
                        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;
       t@@ -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;
       t@@ -398,58 +582,58 @@ vacDir(DirSink *dsink, int fd, char *lname, char *sname, VacFile *vf)
                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 */
       t@@ -461,27 +645,27 @@ vacMergeFile(DirSink *dsink, VacFile *vf, VacDir *dir, uvlong offset, uvlong *ma
                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;
       t@@ -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;
       t@@ -567,11 +751,11 @@ sinkWriteScore(Sink *k, uchar score[VtScoreSize], int n)
                        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;
                }
        
       t@@ -582,29 +766,29 @@ sinkWriteScore(Sink *k, uchar score[VtScoreSize], int n)
        }
        
        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;
       t@@ -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;
        
       t@@ -628,7 +812,7 @@ sinkClose(Sink *k)
        
                /* empty */
                if(kd->size == 0) {
       -                memmove(kd->score, vtZeroScore, VtScoreSize);
       +                memmove(kd->score, vtzeroscore, VtScoreSize);
                        return;
                }
        
       t@@ -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++)
       t@@ -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;
                }
       t@@ -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;
       t@@ -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;
        
       t@@ -780,7 +970,7 @@ dirCmp(void *p0, void *p1)
        }
        
        void
       -metaSinkFlush(MetaSink *k)
       +metasinkflush(MetaSink *k)
        {
                uchar *p;
                int n;
       t@@ -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;
       t@@ -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 */
       t@@ -944,7 +1134,7 @@ plan9ToVacDir(VacDir *vd, Dir *dir, ulong entry, uvlong qid)
        
        
        void
       -metaSinkEOR(MetaSink *k)
       +metasinkeor(MetaSink *k)
        {
                uchar *p;
                int o, n;
       t@@ -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
 (DIR) diff --git a/src/cmd/vac/vac.h b/src/cmd/vac/vac.h
       t@@ -1,4 +1,4 @@
       -typedef struct VacFS VacFS;
       +typedef struct VacFs VacFs;
        typedef struct VacDir VacDir;
        typedef struct VacFile VacFile;
        typedef struct VacDirEnum VacDirEnum;
       t@@ -6,7 +6,8 @@ typedef struct VacDirEnum VacDirEnum;
        /*
         * Mode bits
         */
       -enum {
       +enum
       +{
                ModeOtherExec = (1<<0),                
                ModeOtherWrite = (1<<1),
                ModeOtherRead = (1<<2),
       t@@ -30,7 +31,8 @@ enum {
                ModeSnapshot = (1<<20),                /* read only snapshot */
        };
        
       -enum {
       +enum
       +{
                MetaMagic = 0x5656fc79,
                MetaHeaderSize = 12,
                MetaIndexSize = 4,
       t@@ -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 */
       t@@ -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*);
        
 (DIR) diff --git a/src/cmd/vac/vtdump.c b/src/cmd/vac/vtdump.c
       t@@ -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];
       t@@ -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':
       t@@ -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('R', vtErrFmt);
       +        fmtinstall('V', vtscorefmt);
       +        fmtinstall('H', encodefmt);
        
       -        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");
        
       t@@ -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;
       t@@ -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;
       t@@ -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;
       t@@ -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)
       -                        return -1;
       -
       -                if(!vtSha1Check(score, buf, nn)) {
       -                        vtSetError("vtSha1Check failed on root block");
       +                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((elem[i]+1)*VtScoreSize > nn)
       +                if((elem[i]+1)*VtScoreSize > nn){
       +                        free(buf);
                                return 0;
       +                }
                        memmove(score, buf + elem[i]*VtScoreSize, VtScoreSize);
                }
        
       t@@ -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)
       -                return -1;
       -
       -        if(!vtSha1Check(score, p, nn)) {
       -                vtSetError("vtSha1Check failed on root block");
       +        nn = vtread(z, score, type, p, n);
       +        if(nn < 0){
       +fprint(2, "vtread %V %d: %r\n", score, type);
       +abort();
       +                free(buf);
                        return -1;
                }
                
       +        free(buf);
                return nn;
        }
        
        void
       -dumpFileContents(Source *s)
       +dumpfilecontents(Source *s)
        {
                int nb, lb, i, n;
                uchar buf[VtMaxLumpSize];
       t@@ -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)
       t@@ -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++) {
       t@@ -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");
       -}
       -
       -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;
       +        threadexits("usage");
        }
        
        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;
       t@@ -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");
        }