tvac: clean up, add unvac - 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 64f9764ea9f958a1abc7a32424f43019723e9e53
 (DIR) parent 01cea2ecb8c0b101a22e7883d87fbf1d28a03043
 (HTM) Author: Russ Cox <rsc@swtch.com>
       Date:   Sat, 14 Jun 2008 13:28:49 -0400
       
       vac: clean up, add unvac
       
       Diffstat:
         D src/cmd/vac/README                  |       2 --
         M src/cmd/vac/file.c                  |     238 +++----------------------------
         M src/cmd/vac/fns.h                   |       8 ++++++--
         M src/cmd/vac/mkfile                  |       2 +-
         M src/cmd/vac/pack.c                  |      85 ++++++++++++++++++++-----------
         D src/cmd/vac/rtest.c                 |      71 -------------------------------
         D src/cmd/vac/srcload.c               |     302 -------------------------------
         A src/cmd/vac/unvac.c                 |     293 ++++++++++++++++++++++++++++++
         D src/cmd/vac/util.c                  |      71 -------------------------------
         D src/cmd/vac/vac-orig.c              |    1213 -------------------------------
         M src/cmd/vac/vac.c                   |      84 +++++++++++++++++++++++++++----
         M src/cmd/vac/vac.h                   |       9 +--------
         M src/cmd/vac/vacfs.c                 |     176 +++++++------------------------
         D src/cmd/vac/vactest.c               |     182 -------------------------------
         D src/cmd/vac/vtread.c                |     126 -------------------------------
         D src/cmd/vac/wtest.c                 |      47 -------------------------------
       
       16 files changed, 488 insertions(+), 2421 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/vac/README b/src/cmd/vac/README
       t@@ -1,2 +0,0 @@
       -This is somewhat untested but is believed to work.
       -
 (DIR) diff --git a/src/cmd/vac/file.c b/src/cmd/vac/file.c
       t@@ -564,8 +564,6 @@ vacfileread(VacFile *f, void *buf, int cnt, vlong offset)
                VtBlock *b;
                uchar *p;
        
       -if(0)fprint(2, "fileRead: %s %d, %lld\n", f->dir.elem, cnt, offset);
       -
                if(filerlock(f) < 0)
                        return -1;
        
       t@@ -620,64 +618,6 @@ Err1:
                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;
       -        }
       -
       -        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:
       -        if(s)
       -                vtfileunlock(s);
       -        fileunlock(f);
       -        return -1;
       -}
       -#endif
       -
        int
        vacfilesetsize(VacFile *f, uvlong size)
        {
       t@@ -713,8 +653,6 @@ filewrite(VacFile *f, void *buf, int cnt, vlong offset, char *uid)
                uchar *p;
                vlong eof;
        
       -if(0)fprint(2, "fileWrite: %s %d, %lld\n", f->dir.elem, cnt, offset);
       -
                if(filelock(f) < 0)
                        return -1;
        
       t@@ -1064,7 +1002,7 @@ vacfilemetaflush(VacFile *f, int rec)
                vtfree(kids);
        }
        
       -/* assumes metaLock is held */
       +/* assumes metalock is held */
        static int
        filemetaflush2(VacFile *f, char *oelem)
        {
       t@@ -1097,15 +1035,14 @@ filemetaflush2(VacFile *f, char *oelem)
                if(mbsearch(&mb, oelem, &i, &me) < 0)
                        goto Err;
        
       -        n = vdsize(&f->dir);
       -if(0)fprint(2, "old size %d new size %d\n", me.size, n);
       +        n = vdsize(&f->dir, VacDirVersion);
        
                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);
       +                vdpack(&f->dir, &me, VacDirVersion);
                        mbinsert(&mb, i, &me);
                        mbpack(&mb);
                        /* vtblockdirty(b); */
       t@@ -1126,19 +1063,17 @@ if(0)fprint(2, "old size %d new size %d\n", me.size, n);
                 */
                boff = filemetaalloc(fp, &f->dir, f->boff+1);
                if(boff == NilBlock){
       -                /* mbResize might have modified block */
       +                /* mbresize might have modified block */
                        mbpack(&mb);
                        /* vtblockdirty(b); */
                        goto Err;
                }
       -fprint(2, "fileMetaFlush moving entry from %ud -> %ud\n", f->boff, boff);
                f->boff = boff;
        
                /* make sure deletion goes to disk after new entry */
                bb = vtfileblock(fp->msource, f->boff, VtORDWR);
                mbdelete(&mb, i, &me);
                mbpack(&mb);
       -/*        blockDependency(b, bb, -1, nil, nil); */
                vtblockput(bb);
                /* vtblockdirty(b); */
                vtblockput(b);
       t@@ -1522,6 +1457,16 @@ Return:
                return ret;
        }
        
       +int
       +vdeunread(VacDirEnum *vde)
       +{
       +        if(vde->i > 0){
       +                vde->i--;
       +                return 0;
       +        }
       +        return -1;
       +}
       +
        void
        vdeclose(VacDirEnum *vde)
        {
       t@@ -1555,7 +1500,7 @@ filemetaalloc(VacFile *f, VacDir *dir, u32int start)
                s = f->source;
                ms = f->msource;
        
       -        n = vdsize(dir);
       +        n = vdsize(dir, VacDirVersion);
                nb = (vtfilegetsize(ms)+ms->dsize-1)/ms->dsize;
                b = nil;
                if(start > nb)
       t@@ -1584,9 +1529,8 @@ filemetaalloc(VacFile *f, VacDir *dir, u32int start)
        
                p = mballoc(&mb, n);
                if(p == nil){
       -                /* mbAlloc might have changed block */
       +                /* mballoc might have changed block */
                        mbpack(&mb);
       -                /* vtblockdirty(b); */
                        werrstr(EBadMeta);
                        goto Err;
                }
       t@@ -1595,29 +1539,10 @@ filemetaalloc(VacFile *f, VacDir *dir, u32int start)
                assert(me.p == nil);
                me.p = p;
                me.size = n;
       -        vdpack(dir, &me);
       +        vdpack(dir, &me, VacDirVersion);
                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:
       t@@ -1676,7 +1601,7 @@ fileunlock(VacFile *f)
        
        /*
         * f->source and f->msource must NOT be locked.
       - * fileMetaFlush locks the fileMeta and then the source (in fileMetaFlush2).
       + * vacfilemetaflush locks the filemeta and then the source (in filemetaflush2).
         * We have to respect that ordering.
         */
        static void
       t@@ -1736,130 +1661,3 @@ filewaccess(VacFile* f, char *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@@ -8,9 +8,13 @@ int        meunpack(MetaEntry*, MetaBlock *mb, int i);
        int        mecmp(MetaEntry*, char *s);
        int        mecmpnew(MetaEntry*, char *s);
        
       -int        vdsize(VacDir *dir);
       +enum {
       +        VacDirVersion = 8,
       +        FossilDirVersion = 9,
       +};
       +int        vdsize(VacDir *dir, int);
        int        vdunpack(VacDir *dir, MetaEntry*);
       -void        vdpack(VacDir *dir, MetaEntry*);
       +void        vdpack(VacDir *dir, MetaEntry*, int);
        
        VacFile *_vacfileroot(VacFs *fs, VtFile *file);
        
 (DIR) diff --git a/src/cmd/vac/mkfile b/src/cmd/vac/mkfile
       t@@ -16,7 +16,7 @@ HFILES=\
                dat.h\
                fns.h\
        
       -TARG=vac vacfs # vtdump
       +TARG=vac vacfs unvac # vtdump
        
        default:V: all
        
 (DIR) diff --git a/src/cmd/vac/pack.c b/src/cmd/vac/pack.c
       t@@ -200,7 +200,7 @@ if(0)print("eo = %d en = %d\n", eo, en);
                return 0;
        }
        
       -/* assumes a small amount of checking has been done in mbEntry */
       +/* assumes a small amount of checking has been done in mbentry */
        int
        mecmp(MetaEntry *me, char *s)
        {
       t@@ -372,18 +372,17 @@ mballoc(MetaBlock *mb, int n)
        }
        
        int
       -vdsize(VacDir *dir)
       +vdsize(VacDir *dir, int version)
        {
                int n;
                
       -        /* constant part */
       +        if(version < 8 || version > 9)
       +                sysfatal("bad version %d in vdpack", version);
        
       +        /* constant part */
                n =         4 +        /* magic */
                        2 +         /* version */
                        4 +        /* entry */
       -                4 +         /* guid */
       -                4 +         /* mentry */
       -                4 +         /* mgen */
                        8 +        /* qid */
                        4 +         /* mtime */
                        4 +         /* mcount */
       t@@ -392,6 +391,13 @@ vdsize(VacDir *dir)
                        4 +        /* mode */
                        0;
        
       +        if(version == 9){
       +                n +=         4 +        /* gen */
       +                        4 +         /* mentry */
       +                        4 +         /* mgen */
       +                        0;
       +        }
       +
                /* strings */
                n += 2 + strlen(dir->elem);
                n += 2 + strlen(dir->uid);
       t@@ -399,35 +405,53 @@ vdsize(VacDir *dir)
                n += 2 + strlen(dir->mid);
        
                /* optional sections */
       +        if(version < 9 && dir->plan9) {
       +                n +=         3 +         /* option header */
       +                        8 +         /* path */
       +                        4;        /* version */
       +        }
                if(dir->qidspace) {
                        n +=         3 +         /* option header */
                                8 +         /* qid offset */
                                8;        /* qid max */
                }
       +        if(version < 9 && dir->gen) {
       +                n +=         3 +         /* option header */
       +                        4;        /* gen */
       +        }
        
                return n;
        }
        
        void
       -vdpack(VacDir *dir, MetaEntry *me)
       +vdpack(VacDir *dir, MetaEntry *me, int version)
        {
                uchar *p;
                ulong t32;
        
       +        if(version < 8 || version > 9)
       +                sysfatal("bad version %d in vdpack", version);
       +
                p = me->p;
                
                U32PUT(p, DirMagic);
       -        U16PUT(p+4, 9);                /* version */
       +        U16PUT(p+4, version);                /* version */
                p += 6;
        
                p += stringpack(dir->elem, p);
        
                U32PUT(p, dir->entry);
       -        U32PUT(p+4, dir->gen);
       -        U32PUT(p+8, dir->mentry);
       -        U32PUT(p+12, dir->mgen);
       -        U64PUT(p+16, dir->qid, t32);
       -        p += 24;
       +        p += 4;
       +        
       +        if(version == 9){
       +                U32PUT(p, dir->gen);
       +                U32PUT(p+4, dir->mentry);
       +                U32PUT(p+8, dir->mgen);
       +                p += 12;
       +        }
       +
       +        U64PUT(p, dir->qid, t32);
       +        p += 8;
        
                p += stringpack(dir->uid, p);
                p += stringpack(dir->gid, p);
       t@@ -439,6 +463,15 @@ vdpack(VacDir *dir, MetaEntry *me)
                U32PUT(p+12, dir->atime);
                U32PUT(p+16, dir->mode);
                p += 5*4;
       +        
       +        if(dir->plan9 && version < 9) {
       +                U8PUT(p, DirPlan9Entry);
       +                U16PUT(p+1, 8+4);
       +                p += 3;
       +                U64PUT(p, dir->p9path, t32);
       +                U32PUT(p+8, dir->p9version);
       +                p += 12;
       +        }
        
                if(dir->qidspace) {
                        U8PUT(p, DirQidSpaceEntry);
       t@@ -446,12 +479,20 @@ vdpack(VacDir *dir, MetaEntry *me)
                        p += 3;
                        U64PUT(p, dir->qidoffset, t32);
                        U64PUT(p+8, dir->qidmax, t32);
       +                p += 16;
       +        }
       +        
       +        if(dir->gen && version < 9) {
       +                U8PUT(p, DirGenEntry);
       +                U16PUT(p+1, 4);
       +                p += 3;
       +                U32PUT(p, dir->gen);
       +                p += 4;
                }
        
                assert(p == me->p + me->size);
        }
        
       -
        int
        vdunpack(VacDir *dir, MetaEntry *me)
        {
       t@@ -463,14 +504,12 @@ vdunpack(VacDir *dir, MetaEntry *me)
        
                memset(dir, 0, sizeof(VacDir));
        
       -if(0)print("vdUnpack\n");
                /* magic */
                if(n < 4 || U32GET(p) != DirMagic)
                        goto Err;
                p += 4;
                n -= 4;
        
       -if(0)print("vdUnpack: got magic\n");
                /* version */
                if(n < 2)
                        goto Err;
       t@@ -480,14 +519,10 @@ if(0)print("vdUnpack: got magic\n");
                p += 2;
                n -= 2;        
        
       -if(0)print("vdUnpack: got version\n");
       -
                /* elem */
                if(stringunpack(&dir->elem, &p, &n) < 0)
                        goto Err;
        
       -if(0)print("vdUnpack: got elem\n");
       -
                /* entry  */
                if(n < 4)
                        goto Err;
       t@@ -495,8 +530,6 @@ if(0)print("vdUnpack: got elem\n");
                p += 4;
                n -= 4;
        
       -if(0)print("vdUnpack: got entry\n");
       -
                if(version < 9) {
                        dir->gen = 0;
                        dir->mentry = dir->entry+1;
       t@@ -511,8 +544,6 @@ if(0)print("vdUnpack: got entry\n");
                        n -= 3*4;
                }
        
       -if(0)print("vdUnpack: got gen etc\n");
       -
                /* size is gotten from DirEntry */
        
                /* qid */
       t@@ -522,7 +553,6 @@ if(0)print("vdUnpack: got gen etc\n");
                p += 8;
                n -= 8;
        
       -if(0)print("vdUnpack: got qid\n");
                /* skip replacement */
                if(version == 7) {
                        if(n < VtScoreSize)
       t@@ -543,7 +573,6 @@ if(0)print("vdUnpack: got qid\n");
                if(stringunpack(&dir->mid, &p, &n) < 0)
                        goto Err;
        
       -if(0)print("vdUnpack: got ids\n");
                if(n < 5*4)
                        goto Err;
                dir->mtime = U32GET(p);
       t@@ -554,7 +583,6 @@ if(0)print("vdUnpack: got ids\n");
                p += 5*4;
                n -= 5*4;
        
       -if(0)print("vdUnpack: got times\n");
                /* optional meta data */
                while(n > 0) {
                        if(n < 3)
       t@@ -594,15 +622,12 @@ if(0)print("vdUnpack: got times\n");
                        p += nn;
                        n -= nn;
                }
       -if(0)print("vdUnpack: got options\n");
        
                if(p != me->p + me->size)
                        goto Err;
        
       -if(0)print("vdUnpack: correct size\n");
                return 0;
        Err:
       -if(0)print("vdUnpack: XXXXXXXXXXXX EbadMeta\n");
                werrstr(EBadMeta);
                vdcleanup(dir);
                return -1;
 (DIR) diff --git a/src/cmd/vac/rtest.c b/src/cmd/vac/rtest.c
       t@@ -1,71 +0,0 @@
       -#include "stdinc.h"
       -
       -enum {
       -        Nblock = 300000,
       -        BlockSize = 8*1024
       -};
       -
       -uchar data[Nblock*VtScoreSize];
       -int rflag;
       -int nblock = 10000;
       -int perm[Nblock];
       -
       -void
       -main(int argc, char *argv[])
       -{
       -        VtSession *z;
       -        int i, j, t;
       -        int start;
       -        uchar buf[BlockSize];
       -
       -        srand(time(0));
       -
       -        ARGBEGIN{
       -        case 'r':
       -                rflag++;
       -                break;
       -        case 'n':
       -                nblock = atoi(ARGF());
       -                break;
       -        }ARGEND
       -
       -        for(i=0; i<nblock; i++)
       -                perm[i] = i;
       -
       -        if(rflag) {
       -                for(i=0; i<nblock; i++) {
       -                        j = nrand(nblock);
       -                        t = perm[j];
       -                        perm[j] = perm[i];
       -                        perm[i] = t;
       -                }
       -        }
       -
       -        if(readn(0, data, VtScoreSize*nblock) < VtScoreSize*nblock)
       -                sysfatal("read failed: %r");
       -
       -        vtAttach();
       -
       -        z = vtDial("iolaire2");
       -        if(z == nil)
       -                sysfatal("cound not connect to venti");
       -        if(!vtConnect(z, 0))
       -                vtFatal("vtConnect: %s", vtGetError());
       -
       -        print("starting\n");
       -
       -        start = times(0);
       -
       -        if(rflag && nblock > 10000)
       -                nblock = 10000;
       -
       -        for(i=0; i<nblock; i++) {
       -                if(vtRead(z, data+perm[i]*VtScoreSize, VtDataType, buf, BlockSize) < 0)
       -                        vtFatal("vtRead failed: %d: %s", i, vtGetError());
       -        }
       -
       -        print("time = %f\n", (times(0) - start)*0.001);
       -
       -        vtClose(z);
       -        vtDetach();
       -}
 (DIR) diff --git a/src/cmd/vac/srcload.c b/src/cmd/vac/srcload.c
       t@@ -1,302 +0,0 @@
       -#include "stdinc.h"
       -#include <bio.h>
       -#include "vac.h"
       -#include "dat.h"
       -#include "fns.h"
       -#include "error.h"
       -
       -int num = 1000;
       -int length = 20*1024;
       -int block= 1024;
       -int bush = 4;
       -int iter = 10000;
       -Biobuf *bout;
       -int maxdepth;
       -
       -Source *mkroot(Cache*);
       -void new(Source*, int trace, int);
       -int delete(Source*);
       -void dump(Source*, int indent, ulong nentry);
       -void dumpone(Source *s);
       -int count(Source *s, int);
       -void stats(Source *s);
       -
       -void
       -main(int argc, char *argv[])
       -{
       -        int i;
       -        Cache *c;
       -        char *host = nil;
       -        VtSession *z;
       -        int csize = 10000;
       -        Source *r;
       -        ulong t;
       -
       -        t = time(0);
       -        fprint(1, "time = %lud\n", t);
       -
       -        srand(t);
       -
       -        ARGBEGIN{
       -        case 'i':
       -                iter = atoi(ARGF());
       -                break;
       -        case 'n':
       -                num = atoi(ARGF());
       -                break;
       -        case 'l':
       -                length = atoi(ARGF());
       -                break;
       -        case 'b':        
       -                block = atoi(ARGF());
       -                break;
       -        case 'h':
       -                host = ARGF();
       -                break;
       -        case 'u':
       -                bush = atoi(ARGF());
       -                break;
       -        case 'c':
       -                csize = atoi(ARGF());
       -                break;
       -        }ARGEND;
       -
       -        vtAttach();
       -
       -        bout = vtMemAllocZ(sizeof(Biobuf));
       -        Binit(bout, 1, OWRITE);
       -
       -        fmtinstall('V', vtScoreFmt);
       -        fmtinstall('R', vtErrFmt);
       -
       -        z = vtDial(host);
       -        if(z == nil)
       -                vtFatal("could not connect to server: %s", vtGetError());
       -
       -        if(!vtConnect(z, 0))
       -                sysfatal("vtConnect: %r");
       -
       -        c = cacheAlloc(z, block, csize);
       -        r = mkroot(c);
       -        for(i=0; i<num; i++)
       -                new(r, 0, 0);
       -
       -        for(i=0; i<iter; i++) {
       -if(i % 10000 == 0)
       -stats(r);
       -                new(r, 0, 0);
       -                delete(r);
       -        }
       -
       -        fprint(2, "count = %d top = %lud\n", count(r, 0), sourceGetDirSize(r));
       -/*        cacheCheck(c); */
       -fprint(2, "deleting\n");
       -        for(i=0; i<num; i++)
       -                delete(r);
       -
       -/*        dump(r, 0, 0); */
       -        
       -        lumpDecRef(r->lump, 0);
       -        sourceRemove(r);
       -        cacheCheck(c);
       -
       -        vtClose(z);
       -        vtDetach();
       -
       -        exits(0);
       -}
       -
       -
       -Source *
       -mkroot(Cache *c)
       -{
       -        Lump *u;
       -        VtEntry *dir;
       -        Source *r;
       -
       -        u = cacheAllocLump(c, VtDirType, cacheGetBlockSize(c), 1);
       -        dir = (VtEntry*)u->data;
       -        vtPutUint16(dir->psize, cacheGetBlockSize(c));
       -        vtPutUint16(dir->dsize, cacheGetBlockSize(c));
       -        dir->flag = VtEntryActive|VtEntryDir;
       -        memmove(dir->score, vtZeroScore, VtScoreSize);
       -        
       -        r = sourceAlloc(c, u, 0, 0);
       -        vtUnlock(u->lk);
       -        if(r == nil)
       -                sysfatal("could not create root source: %r");
       -        return r;
       -}
       -
       -void
       -new(Source *s, int trace, int depth)
       -{
       -        int i, n;
       -        Source *ss;
       -        
       -        if(depth > maxdepth)
       -                maxdepth = depth;
       -
       -        n = sourceGetDirSize(s);
       -        for(i=0; i<n; i++) {
       -                ss = sourceOpen(s, nrand(n), 0);
       -                if(ss == nil)
       -                        continue;
       -                if(ss->dir && frand() < 1./bush) {
       -                        if(trace) {
       -                                int j;
       -                                for(j=0; j<trace; j++)
       -                                        Bprint(bout, " ");
       -                                Bprint(bout, "decend %d\n", i);
       -                        }
       -                        new(ss, trace?trace+1:0, depth+1);
       -                        sourceFree(ss);
       -                        return;
       -                }
       -                sourceFree(ss);
       -        }
       -        ss = sourceCreate(s, s->psize, s->dsize, 1+frand()>.5, 0);
       -        if(ss == nil)
       -                fprint(2, "could not create directory: %r\n");
       -        if(trace) {
       -                int j;
       -                for(j=1; j<trace; j++)
       -                        Bprint(bout, " ");
       -                Bprint(bout, "create %d %V\n", ss->entry, ss->lump->score);
       -        }
       -        sourceFree(ss);
       -}
       -
       -int
       -delete(Source *s)
       -{
       -        int i, n;
       -        Source *ss;
       -        
       -        assert(s->dir);
       -
       -        n = sourceGetDirSize(s);
       -        /* check if empty */
       -        for(i=0; i<n; i++) {
       -                ss = sourceOpen(s, i, 1);
       -                if(ss != nil) {
       -                        sourceFree(ss);
       -                        break;
       -                }
       -        }
       -        if(i == n)
       -                return 0;
       -                
       -        for(;;) {
       -                ss = sourceOpen(s, nrand(n), 0);
       -                if(ss == nil)
       -                        continue;
       -                if(ss->dir && delete(ss)) {
       -                        sourceFree(ss);
       -                        return 1;
       -                }
       -                if(1)
       -                        break;
       -                sourceFree(ss);
       -        }
       -
       -
       -        sourceRemove(ss);
       -        return 1;
       -}
       -
       -void
       -dumpone(Source *s)
       -{
       -        ulong i, n;
       -        Source *ss;
       -
       -        Bprint(bout, "gen %4lud depth %d %V", s->gen, s->depth, s->lump->score);
       -        if(!s->dir) {
       -                Bprint(bout, " data size: %llud\n", s->size);
       -                return;
       -        }
       -        n = sourceGetDirSize(s);
       -        Bprint(bout, " dir size: %lud\n", n);
       -        for(i=0; i<n; i++) {
       -                ss = sourceOpen(s, i, 1);
       -                if(ss == nil) {
       -fprint(2, "%lud: %r\n", i);
       -                        continue;
       -                }
       -                Bprint(bout, "\t%lud %d %llud %V\n", i, ss->dir, ss->size, ss->lump->score);
       -                sourceFree(ss);
       -        }
       -        return;
       -}
       -
       -
       -void
       -dump(Source *s, int ident, ulong entry)
       -{
       -        ulong i, n;
       -        Source *ss;
       -
       -        for(i=0; i<ident; i++)
       -                Bprint(bout, " ");
       -        Bprint(bout, "%4lud: gen %4lud depth %d", entry, s->gen, s->depth);
       -        if(!s->dir) {
       -                Bprint(bout, " data size: %llud\n", s->size);
       -                return;
       -        }
       -        n = sourceGetDirSize(s);
       -        Bprint(bout, " dir size: %lud\n", n);
       -        for(i=0; i<n; i++) {
       -                ss = sourceOpen(s, i, 1);
       -                if(ss == nil)
       -                        continue;
       -                dump(ss, ident+1, i);
       -                sourceFree(ss);
       -        }
       -        return;
       -}
       -
       -int
       -count(Source *s, int rec)
       -{
       -        ulong i, n;
       -        int c;
       -        Source *ss;
       -
       -        if(!s->dir)
       -                return 0;
       -        n = sourceGetDirSize(s);
       -        c = 0;
       -        for(i=0; i<n; i++) {
       -                ss = sourceOpen(s, i, 1);
       -                if(ss == nil)
       -                        continue;
       -                if(rec)
       -                        c += count(ss, rec);
       -                c++;
       -                sourceFree(ss);
       -        }
       -        return c;
       -}
       -
       -void
       -stats(Source *s)
       -{
       -        int n, i, c, cc, max;
       -        Source *ss;
       -
       -        cc = 0;
       -        max = 0;
       -        n = sourceGetDirSize(s);
       -        for(i=0; i<n; i++) {
       -                ss = sourceOpen(s, i, 1);
       -                if(ss == nil)
       -                        continue;
       -                cc++;
       -                c = count(ss, 1);
       -                if(c > max)
       -                        max = c;
       -                sourceFree(ss);
       -        }
       -fprint(2, "count = %d top = %d depth=%d maxcount %d\n", cc, n, maxdepth, max);
       -}
 (DIR) diff --git a/src/cmd/vac/unvac.c b/src/cmd/vac/unvac.c
       t@@ -0,0 +1,293 @@
       +#include "stdinc.h"
       +#include <auth.h>
       +#include <fcall.h>
       +#include <thread.h>
       +#include "vac.h"
       +
       +VacFs *fs;
       +int tostdout;
       +int nwant;
       +char **want;
       +int *found;
       +int chatty;
       +VtConn *conn;
       +int errors;
       +int settimes;
       +int table;
       +
       +int mtimefmt(Fmt*);
       +void unvac(VacFile*, char*, VacDir*);
       +
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: unvac [-TVctv] [-h host] file.vac [file ...]\n");
       +        threadexitsall("usage");
       +}
       +
       +void
       +threadmain(int argc, char *argv[])
       +{
       +        int i;
       +        char *host;
       +        VacFile *f;
       +
       +        fmtinstall('H', encodefmt);
       +        fmtinstall('V', vtscorefmt);
       +        fmtinstall('F', vtfcallfmt);
       +        fmtinstall('T', mtimefmt);
       +        fmtinstall('M', dirmodefmt);
       +        
       +        host = nil;
       +        ARGBEGIN{
       +        case 'T':
       +                settimes = 1;
       +                break;
       +        case 'V':
       +                chattyventi = 1;
       +                break;
       +        case 'c':
       +                tostdout++;
       +                break;
       +        case 'h':
       +                host = EARGF(usage());
       +                break;
       +        case 't':
       +                table++;
       +                break;
       +        case 'v':
       +                chatty++;
       +                break;
       +        default:
       +                usage();
       +        }ARGEND
       +
       +        if(argc < 1)
       +                usage();
       +
       +        conn = vtdial(host);
       +        if(conn == nil)
       +                sysfatal("could not connect to server: %r");
       +
       +        if(vtconnect(conn) < 0)
       +                sysfatal("vtconnect: %r");
       +
       +        fs = vacfsopen(conn, argv[0], VtOREAD, 128);
       +        if(fs == nil)
       +                sysfatal("vacfsopen: %r");
       +
       +        nwant = argc-1;
       +        want = argv+1;
       +        found = vtmallocz(nwant*sizeof found[0]);
       +
       +        if((f = vacfsgetroot(fs)) == nil)
       +                sysfatal("vacfsgetroot: %r");
       +        
       +        unvac(f, nil, nil);
       +        for(i=0; i<nwant; i++){
       +                if(want[i] && !found[i]){
       +                        fprint(2, "warning: didn't find %s\n", want[i]);
       +                        errors++;
       +                }
       +        }
       +        if(errors)
       +                threadexitsall("errors");
       +        threadexitsall(0);
       +}
       +
       +int
       +writen(int fd, char *buf, int n)
       +{
       +        int m;
       +        int oldn;
       +        
       +        oldn = n;
       +        while(n > 0){
       +                m = write(fd, buf, n);
       +                if(m <= 0)
       +                        return -1;
       +                buf += m;
       +                n -= m;
       +        }
       +        return oldn;
       +}
       +
       +int
       +wantfile(char *name)
       +{
       +        int i, namelen, n;
       +        
       +        if(nwant == 0)
       +                return 1;
       +
       +        namelen = strlen(name);
       +        for(i=0; i<nwant; i++){
       +                if(want[i] == nil)
       +                        continue;
       +                n = strlen(want[i]);
       +                if(n < namelen && name[n] == '/' && memcmp(name, want[i], n) == 0)
       +                        return 1;
       +                if(namelen < n && want[i][namelen] == '/' && memcmp(want[i], name, n) == 0)
       +                        return 1;
       +                if(n == namelen && memcmp(name, want[i], n) == 0){
       +                        found[i] = 1;
       +                        return 1;
       +                }
       +        }
       +        return 0;
       +}
       +
       +void
       +unvac(VacFile *f, char *name, VacDir *vdir)
       +{
       +        static char buf[65536];
       +        int fd, mode, n, mode9;
       +        char *newname;
       +        char *what;
       +        vlong off;
       +        Dir d, *dp;
       +        VacDirEnum *vde;
       +        VacDir newvdir;
       +        VacFile *newf;
       +
       +        if(vdir)
       +                mode = vdir->mode;
       +        else
       +                mode = vacfilegetmode(f);
       +
       +        if(vdir){
       +                if(table){
       +                        if(chatty){
       +                                mode9 = vdir->mode&0777;
       +                                if(mode&ModeDir)
       +                                        mode9 |= DMDIR;
       +                                if(mode&ModeLink)
       +                                        mode9 |= DMSYMLINK;
       +                                if(mode&ModeAppend)
       +                                        mode9 |= DMAPPEND;
       +                                if(mode&ModeExclusive)
       +                                        mode9 |= DMEXCL;
       +                                if(mode&ModeNamedPipe)
       +                                        mode9 |= DMNAMEDPIPE;
       +                                if(mode&ModeSetUid)
       +                                        mode9 |= DMSETUID;
       +                                if(mode&ModeSetGid)
       +                                        mode9 |= DMSETGID;
       +                                if(mode&ModeDevice)
       +                                        mode9 |= DMDEVICE;
       +                                print("%M %-10s %-10s %11lld %T %s\n",
       +                                        mode9, vdir->uid, vdir->gid, vdir->size,
       +                                        vdir->mtime, name);
       +                        }else
       +                                print("%s%s\n", name, (mode&ModeDir) ? "/" : "");
       +                }
       +                else if(chatty)
       +                        fprint(2, "%s%s\n", name, (mode&ModeDir) ? "/" : "");
       +        }
       +
       +        if(mode&(ModeDevice|ModeLink|ModeNamedPipe|ModeExclusive)){
       +                if(table)
       +                        return;
       +                if(mode&ModeDevice)
       +                        what = "device";
       +                else if(mode&ModeLink)
       +                        what = "link";
       +                else if(mode&ModeNamedPipe)
       +                        what = "named pipe";
       +                else if(mode&ModeExclusive)
       +                        what = "lock";
       +                else
       +                        what = "unknown type of file";
       +                fprint(2, "warning: ignoring %s %s\n", what, name);
       +                return;
       +        }
       +        
       +        if(mode&ModeDir){
       +                if((vde = vdeopen(f)) == nil){
       +                        fprint(2, "vdeopen %s: %r", name);
       +                        errors++;
       +                        return;
       +                }
       +                if(!table && !tostdout && vdir){
       +                        // create directory
       +                        if((dp = dirstat(name)) == nil){
       +                                if((fd = create(name, OREAD, DMDIR|(mode&0777))) < 0){
       +                                        fprint(2, "mkdir %s: %r\n", name);
       +                                        vdeclose(vde);
       +                                }
       +                                close(fd);
       +                        }else{
       +                                if(!(dp->mode&DMDIR)){
       +                                        fprint(2, "%s already exists and is not a directory\n", name);
       +                                        errors++;
       +                                        free(dp);
       +                                        vdeclose(vde);
       +                                        return;
       +                                }
       +                                free(dp);
       +                        }
       +                }
       +                while(vderead(vde, &newvdir) > 0){
       +                        if(name == nil)
       +                                newname = newvdir.elem;
       +                        else
       +                                newname = smprint("%s/%s", name, newvdir.elem);
       +                        if(wantfile(newname)){
       +                                if((newf = vacfilewalk(f, newvdir.elem)) == nil){
       +                                        fprint(2, "walk %s: %r\n", name);
       +                                        errors++;
       +                                }else if(newf == f){
       +                                        fprint(2, "walk loop: %s\n", newname);
       +                                        vacfiledecref(newf);
       +                                }else{
       +                                        unvac(newf, newname, &newvdir);
       +                                        vacfiledecref(newf);
       +                                }
       +                        }
       +                        if(newname != newvdir.elem)
       +                                free(newname);
       +                        vdcleanup(&newvdir);
       +                }
       +                vdeclose(vde);
       +        }else{
       +                if(!table){
       +                        if(tostdout)
       +                                fd = dup(1, -1);
       +                        else if((fd = create(name, OWRITE, mode&0777)) < 0){
       +                                fprint(2, "create %s: %r\n", name);
       +                                errors++;
       +                                return;
       +                        }
       +                        off = 0;
       +                        while((n = vacfileread(f, buf, sizeof buf, off)) > 0){
       +                                if(writen(fd, buf, n) != n){
       +                                        fprint(2, "write %s: %r\n", name);
       +                                        errors++;
       +                                        close(fd);
       +                                        remove(name);
       +                                        return;
       +                                }
       +                                off += n;
       +                        }
       +                        close(fd);
       +                }
       +        }
       +        if(vdir && settimes && !tostdout){
       +                nulldir(&d);
       +                d.mtime = vdir->mtime;
       +                if(dirwstat(name, &d) < 0)
       +                        fprint(2, "warning: setting mtime on %s: %r", name);
       +        }
       +}
       +
       +int
       +mtimefmt(Fmt *f)
       +{
       +        Tm *tm;
       +        
       +        tm = localtime(va_arg(f->args, ulong));
       +        fmtprint(f, "%04d-%02d-%02d %02d:%02d",
       +                tm->year+1900, tm->mon+1, tm->mday,
       +                tm->hour, tm->min);
       +        return 0;
       +}
 (DIR) diff --git a/src/cmd/vac/util.c b/src/cmd/vac/util.c
       t@@ -1,71 +0,0 @@
       -#include "stdinc.h"
       -#include "vac.h"
       -#include "dat.h"
       -#include "fns.h"
       -
       -int
       -vtGetUint16(uchar *p)
       -{
       -        return (p[0]<<8)|p[1];
       -}
       -
       -ulong
       -vtGetUint32(uchar *p)
       -{
       -        return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
       -}
       -
       -uvlong
       -vtGetUint48(uchar *p)
       -{
       -        return ((uvlong)p[0]<<40)|((uvlong)p[1]<<32)|
       -                (p[2]<<24)|(p[3]<<16)|(p[4]<<8)|p[5];
       -}
       -
       -uvlong
       -vtGetUint64(uchar *p)
       -{
       -        return ((uvlong)p[0]<<56)|((uvlong)p[1]<<48)|((uvlong)p[2]<<40)|
       -                ((uvlong)p[3]<<32)|(p[4]<<24)|(p[5]<<16)|(p[6]<<8)|p[7];
       -}
       -
       -
       -void
       -vtPutUint16(uchar *p, int x)
       -{
       -        p[0] = x>>8;
       -        p[1] = x;
       -}
       -
       -void
       -vtPutUint32(uchar *p, ulong x)
       -{
       -        p[0] = x>>24;
       -        p[1] = x>>16;
       -        p[2] = x>>8;
       -        p[3] = x;
       -}
       -
       -void
       -vtPutUint48(uchar *p, uvlong x)
       -{
       -        p[0] = x>>40;
       -        p[1] = x>>32;
       -        p[2] = x>>24;
       -        p[3] = x>>16;
       -        p[4] = x>>8;
       -        p[5] = x;
       -}
       -
       -void
       -vtPutUint64(uchar *p, uvlong x)
       -{
       -        p[0] = x>>56;
       -        p[1] = x>>48;
       -        p[2] = x>>40;
       -        p[3] = x>>32;
       -        p[4] = x>>24;
       -        p[5] = x>>16;
       -        p[6] = x>>8;
       -        p[7] = x;
       -}
 (DIR) diff --git a/src/cmd/vac/vac-orig.c b/src/cmd/vac/vac-orig.c
       t@@ -1,1213 +0,0 @@
       -#include "stdinc.h"
       -#include "vac.h"
       -#include "dat.h"
       -#include "fns.h"
       -
       -typedef struct Sink Sink;
       -typedef struct MetaSink MetaSink;
       -typedef struct DirSink DirSink;
       -
       -struct Sink {
       -        VtSession *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(void*, void*);
       -static void warn(char *fmt, ...);
       -static void cleanup(void);
       -static u64int unittoull(char *s);
       -static int vac(VtSession *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(VtSession *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(VtSession *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(VtSession *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;
       -
       -void
       -main(int argc, char *argv[])
       -{
       -        VtSession *z;
       -        char *p;
       -        char *host = nil;
       -        int statsFlag = 0;
       -
       -        atexit(cleanup);
       -
       -        ARGBEGIN{
       -        default:
       -                usage();
       -        case 'b':
       -                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 = 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;
       -        case 's':
       -                statsFlag++;
       -                break;
       -        case 'v':
       -                verbose++;
       -                break;
       -        }ARGEND;
       -
       -        if(bsize < 512)
       -                bsize = 512;
       -        if(bsize > VtMaxLumpSize)
       -                bsize = VtMaxLumpSize;
       -        maxbsize = bsize;
       -
       -        vtAttach();
       -
       -        fmtinstall('V', vtScoreFmt);
       -        fmtinstall('R', vtErrFmt);
       -
       -        z = vtDial(host, 0);
       -        if(z == nil)
       -                vtFatal("could not connect to server: %R");
       -
       -        if(!vtConnect(z, 0))
       -                vtFatal("vtConnect: %R");
       -
       -        qsort(exclude, nexclude, sizeof(char*), strpCmp);
       -
       -        vac(z, argv);
       -        if(!vtSync(z))
       -                fprint(2, "warning: could not ask server to flush pending writes: %R\n");
       -
       -        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();
       -
       -        exits(0);
       -}
       -
       -void
       -static usage(void)
       -{
       -        fprint(2, "usage: %s [-amqsv] [-h host] [-d vacfile] [-b blocksize] [-i name] [-e exclude] [-f vacfile] file ... \n", argv0);
       -        exits("usage");
       -}
       -
       -static
       -int strpCmp(void *p0, void *p1)
       -{
       -        return strcmp(*(char**)p0, *(char**)p1);
       -}
       -
       -
       -int
       -readBlock(int fd, uchar *buf, int n)
       -{
       -        int m, t = 0;
       -
       -        while(t < n){
       -                m = read(fd, buf+t, n-t);
       -                if(m < 0)
       -                        return -1;
       -                if(m == 0)
       -                        break;
       -                t += m;
       -        }
       -        return t;
       -}
       -
       -int
       -vacWrite(VtSession *z, uchar score[VtScoreSize], int type, uchar *buf, int n)
       -{
       -assert(n > 0);
       -        if(nowrite) {
       -                vtSha1(score, buf, n);
       -                return 1;
       -        }
       -        if(!vtWrite(z, score, type, buf, n))
       -                return 0;
       -        if(!vtSha1Check(score, buf, n)) {
       -                uchar score2[VtScoreSize];
       -                
       -                vtSha1(score2, buf, n);
       -fprint(2, "vtSha1Check: n = %d %V %V\n", n, score, score2);
       -                vtSetError("vtSha1Check failed");
       -                return 0;
       -        }
       -        return 1;
       -}
       -
       -
       -static int
       -vac(VtSession *z, char *argv[])
       -{
       -        DirSink *dsink, *ds;
       -        MetaSink *ms;
       -        VtRoot root;
       -        uchar score[VtScoreSize], buf[VtRootSize];
       -        char cwd[2048];
       -        int cd, i;
       -        char *cp2, *cp;
       -        VacFS *fs;
       -        VacFile *vff;
       -        int fd;
       -        Dir *dir;
       -        VacDir vd;
       -
       -        if(getwd(cwd, sizeof(cwd)) == 0)
       -                sysfatal("can't find current directory: %r\n");
       -
       -        dsink = dirSinkAlloc(z, bsize, bsize);
       -
       -        fs = nil;
       -        if(dfile != nil) {
       -                fs = vfsOpen(z, dfile, 1, 10000);
       -                if(fs == nil)
       -                        fprint(2, "could not open diff: %s: %s\n", dfile, vtGetError());
       -        }
       -                
       -
       -        if(oname != nil) {
       -                fd = create(oname, OWRITE, 0666);
       -                if(fd < 0)
       -                        sysfatal("could not create file: %s: %r", oname);
       -        } else 
       -                fd = 1;
       -
       -        dir = dirfstat(fd);
       -        if(dir == nil)
       -                sysfatal("dirfstat failed: %r");
       -
       -        for(; *argv; argv++) {
       -                cp2 = *argv;
       -                cd = 0;
       -                for (cp = *argv; *cp; cp++)
       -                        if (*cp == '/')
       -                                cp2 = cp;
       -                if (cp2 != *argv) {
       -                        *cp2 = '\0';
       -                        chdir(*argv);
       -                        *cp2 = '/';
       -                        cp2++;
       -                        cd = 1;
       -                }
       -                vff = nil;
       -                if(fs)
       -                        vff = vfOpen(fs, cp2);
       -                vacFile(dsink, argv[0], cp2, vff);
       -                if(vff)
       -                        vfDecRef(vff);
       -                if(cd && chdir(cwd) < 0)
       -                        sysfatal("can't cd back to %s: %r\n", cwd);
       -        }
       -        
       -        if(isi) {
       -                vff = nil;
       -                if(fs)
       -                        vff = vfOpen(fs, isi);
       -                vacStdin(dsink, isi, vff);
       -                if(vff)
       -                        vfDecRef(vff);
       -        }
       -
       -        dirSinkClose(dsink);
       -
       -        /* build meta information for the root */
       -        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++);
       -        if(strcmp(vd.elem, "/") == 0){
       -                vtMemFree(vd.elem);
       -                vd.elem = vtStrDup("root");
       -        }
       -        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);
       -
       -        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;
       -        if(fs != nil)
       -                vfsGetScore(fs, root.prev);
       -
       -        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");
       -        
       -        /* avoid remove at cleanup */
       -        oname = nil;
       -        return 1;
       -}
       -
       -static int
       -isExcluded(char *name)
       -{
       -        int bot, top, i, x;
       -
       -        bot = 0;        
       -        top = nexclude;
       -        while(bot < top) {
       -                i = (bot+top)>>1;
       -                x = strcmp(exclude[i], name);
       -                if(x == 0)
       -                        return 1;
       -                if(x < 0)
       -                        bot = i + 1;
       -                else /* x > 0 */
       -                        top = i;
       -        }
       -        return 0;
       -}
       -
       -static void
       -vacFile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
       -{
       -        int fd;
       -        Dir *dir;
       -        VacDir vd;
       -        ulong entry;
       -
       -        if(isExcluded(lname)) {
       -                warn("excluding: %s", lname);
       -                return;
       -        }
       -
       -        if(merge && vacMerge(dsink, lname, sname))
       -                return;
       -
       -        fd = open(sname, OREAD);
       -        if(fd < 0) {
       -                warn("could not open file: %s: %s", lname, vtOSError());
       -                return;
       -        }
       -
       -        if(verbose)
       -                fprint(2, "%s\n", lname);
       -
       -        dir = dirfstat(fd);
       -        if(dir == nil) {
       -                warn("can't stat %s: %r", lname);
       -                close(fd);
       -                return;
       -        }
       -
       -        entry = dsink->nentry;
       -
       -        if(dir->mode & DMDIR) 
       -                vacDir(dsink, fd, lname, sname, vf);
       -        else
       -                vacData(dsink, fd, lname, vf, dir);
       -
       -        plan9ToVacDir(&vd, dir, entry, fileid++);
       -        metaSinkWriteDir(dsink->msink, &vd);
       -        vdCleanup(&vd);
       -
       -        free(dir);
       -        close(fd);
       -}
       -
       -static void
       -vacStdin(DirSink *dsink, char *name, VacFile *vf)
       -{
       -        Dir *dir;
       -        VacDir vd;
       -        ulong entry;
       -
       -        if(verbose)
       -                fprint(2, "%s\n", "<stdio>");
       -
       -        dir = dirfstat(0);
       -        if(dir == nil) {
       -                warn("can't stat <stdio>: %r");
       -                return;
       -        }
       -
       -        entry = dsink->nentry;
       -
       -        vacData(dsink, 0, "<stdin>", vf, dir);
       -
       -        plan9ToVacDir(&vd, dir, entry, fileid++);
       -        vd.elem = vtStrDup(name);
       -        metaSinkWriteDir(dsink->msink, &vd);
       -        vdCleanup(&vd);
       -
       -        free(dir);
       -}
       -
       -static ulong
       -vacDataSkip(Sink *sink, VacFile *vf, int fd, ulong blocks, uchar *buf, char *lname)
       -{
       -        int n;
       -        ulong i;
       -        uchar score[VtScoreSize];
       -
       -        /* skip blocks for append only files */
       -        if(seek(fd, (blocks-1)*bsize, 0) != (blocks-1)*bsize) {
       -                warn("error seeking: %s", lname);
       -                goto Err;
       -        }
       -        n = readBlock(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)) {
       -                warn("last block of append file did not match: %s", lname);
       -                goto Err;
       -        }
       -
       -        for(i=0; i<blocks; i++) {
       -                if(!vfGetBlockScore(vf, i, score)) {
       -                        warn("could not get score: %s: %lud", lname, i);
       -                        seek(fd, i*bsize, 0);
       -                        return i;
       -                }
       -                stats.skip++;
       -                sinkWriteScore(sink, score, bsize);
       -        }
       -
       -        return i;
       -Err:
       -        seek(fd, 0, 0);
       -        return 0;
       -}
       -
       -static void
       -vacData(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
       -{
       -        uchar *buf;
       -        Sink *sink;
       -        int n;
       -        uchar score[VtScoreSize];
       -        ulong block, same;
       -        VacDir vd;
       -        ulong vfblocks;
       -
       -        vfblocks = 0;
       -        if(vf != nil && qdiff) {
       -                vfGetDir(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)) {
       -                        stats.sfile++;
       -                        vdCleanup(&vd);
       -                        return;
       -                }
       -
       -                /* look for an append only file */
       -                if((dir->mode&DMAPPEND) != 0)
       -                if(vd.size < dir->length)
       -                if(vd.plan9)
       -                if(vd.p9path == dir->qid.path)
       -                        vfblocks = vd.size/bsize;
       -
       -                vdCleanup(&vd);
       -        }
       -        stats.file++;
       -
       -        buf = vtMemAlloc(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);
       -
       -if(0) fprint(2, "vacData: %s: %ld\n", lname, block);
       -        for(;;) {
       -                n = readBlock(fd, buf, bsize);
       -                if(0 && n < 0)
       -                        warn("file truncated due to read error: %s: %s", lname, vtOSError());
       -                if(n <= 0)
       -                        break;
       -                if(vf != nil && vfGetBlockScore(vf, block, score) && vtSha1Check(score, buf, n)) {
       -                        stats.sdata++;
       -                        sinkWriteScore(sink, score, n);
       -                } else
       -                        sinkWrite(sink, buf, n);
       -                block++;
       -        }
       -        same = stats.sdata+stats.skip - same;
       -
       -        if(same && (dir->mode&DMAPPEND) != 0)
       -                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);
       -        free(buf);
       -}
       -
       -
       -static void
       -vacDir(DirSink *dsink, int fd, char *lname, char *sname, VacFile *vf)
       -{
       -        Dir *dirs;
       -        char *ln, *sn;
       -        int i, nd;
       -        DirSink *ds;
       -        VacFile *vvf;
       -        char *name;
       -
       -        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);
       -                        sprint(ln, "%s/%s", lname, name);
       -                        sprint(sn, "%s/%s", sname, name);
       -                        if(vf != nil)
       -                                vvf = vfWalk(vf, name);
       -                        else
       -                                vvf = nil;
       -                        vacFile(ds, ln, sn, vvf);
       -                        if(vvf != nil)
       -                                vfDecRef(vvf);
       -                        vtMemFree(ln);
       -                        vtMemFree(sn);
       -                }
       -                free(dirs);
       -        }
       -        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)
       -{
       -        uchar buf[VtEntrySize];
       -        VtEntry dd, md;
       -        int e;
       -
       -        if(vfRead(vf, buf, VtEntrySize, (uvlong)dir->entry*VtEntrySize) != VtEntrySize) {
       -                warn("could not read venti dir entry: %s\n", dir->elem);
       -                return 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) {
       -                        warn("could not read venti dir entry: %s\n", dir->elem);
       -                        return 0;
       -                }
       -                vtEntryUnpack(&md, buf, 0);
       -        }
       -
       -        /* max might incorrect in some old dumps */
       -        if(dir->qid >= *max) {
       -                warn("qid out of range: %s", dir->elem);
       -                *max = dir->qid;
       -        }
       -
       -        dir->qid += offset;
       -        dir->entry = dsink->nentry;
       -
       -        if(dir->qidSpace) {
       -                dir->qidOffset += offset;
       -        } else {
       -                dir->qidSpace = 1;
       -                dir->qidOffset = offset;
       -                dir->qidMax = *max;
       -        }
       -
       -        dirSinkWrite(dsink, &dd);
       -        if(dir->mode & ModeDir)        
       -                dirSinkWrite(dsink, &md);
       -        metaSinkWriteDir(dsink->msink, dir);
       -        
       -        return 1;
       -}
       -
       -static int
       -vacMerge(DirSink *dsink, char *lname, char *sname)
       -{
       -        char *p;
       -        VacFS *fs;
       -        VacFile *vf;
       -        VacDirEnum *d;
       -        VacDir dir;
       -        uvlong max;
       -
       -        p = strrchr(sname, '.');
       -        if(p == 0 || strcmp(p, ".vac"))
       -                return 0;
       -
       -        d = nil;
       -        fs = vfsOpen(dsink->sink->z, sname, 1, 100);
       -        if(fs == nil)
       -                return 0;
       -
       -        vf = vfOpen(fs, "/");
       -        if(vf == nil)
       -                goto Done;
       -        max = vfGetId(vf);
       -        d = vdeOpen(fs, "/");
       -        if(d == nil)
       -                goto Done;
       -
       -        if(verbose)
       -                fprint(2, "merging: %s\n", lname);
       -
       -        if(maxbsize < vfsGetBlockSize(fs))
       -                maxbsize = vfsGetBlockSize(fs);
       -
       -        for(;;) {
       -                if(vdeRead(d, &dir, 1) < 1)
       -                        break;
       -                vacMergeFile(dsink, vf, &dir, fileid, &max);
       -                vdCleanup(&dir);        
       -        }
       -        fileid += max;
       -
       -Done:
       -        if(d != nil)
       -                vdeFree(d);
       -        if(vf != nil)
       -                vfDecRef(vf);
       -        vfsClose(fs);
       -        return 1;
       -}
       -
       -Sink *
       -sinkAlloc(VtSession *z, int psize, int dsize)
       -{
       -        Sink *k;
       -        int i;
       -
       -        if(psize < 512 || psize > VtMaxLumpSize)
       -                vtFatal("sinkAlloc: bad psize");
       -        if(dsize < 512 || dsize > VtMaxLumpSize)
       -                vtFatal("sinkAlloc: bad psize");
       -
       -        psize = VtScoreSize*(psize/VtScoreSize);
       -
       -        k = vtMemAllocZ(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);
       -        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)
       -{
       -        int i;
       -        uchar *p;
       -        VtEntry *d;
       -
       -        memmove(k->pbuf[0], score, VtScoreSize);
       -
       -        d = &k->dir;
       -
       -        for(i=0; i<VtPointerDepth; i++) {
       -                k->pbuf[i] += VtScoreSize;
       -                if(k->pbuf[i] < k->buf + d->psize*(i+1))
       -                        break;
       -                if(i == VtPointerDepth-1)
       -                        vtFatal("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());
       -                k->pbuf[i] = p;
       -        }
       -
       -        /* round size up to multiple of dsize */
       -        d->size = d->dsize * ((d->size + d->dsize-1)/d->dsize);
       -        
       -        d->size += n;
       -}
       -
       -void
       -sinkWrite(Sink *k, uchar *p, int n)
       -{
       -        int type;
       -        uchar score[VtScoreSize];
       -
       -        if(n > k->dir.dsize)
       -                vtFatal("sinkWrite: size too big");
       -
       -        if(k->dir.flags & VtEntryDir) {
       -                type = VtDirType;
       -                stats.meta++;
       -        } else {
       -                type = VtDataType;
       -                stats.data++;
       -        }
       -        if(!vacWrite(k->z, score, type, p, n))
       -                vtFatal("vacWrite failed: %s", vtGetError());
       -
       -        sinkWriteScore(k, score, n);
       -}
       -
       -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;
       -}
       -
       -void
       -sinkClose(Sink *k)
       -{
       -        int i, n;
       -        uchar *p;
       -        VtEntry *kd;
       -
       -        kd = &k->dir;
       -
       -        /* empty */
       -        if(kd->size == 0) {
       -                memmove(kd->score, vtZeroScore, VtScoreSize);
       -                return;
       -        }
       -
       -        for(n=VtPointerDepth-1; n>0; n--)
       -                if(k->pbuf[n] > k->buf + kd->psize*n)
       -                        break;
       -
       -        kd->depth = sizeToDepth(kd->size, kd->psize, kd->dsize);
       -
       -        /* skip full part of tree */
       -        for(i=0; i<n && k->pbuf[i] == k->buf + kd->psize*i; i++)
       -                ;
       -
       -        /* is the tree completely full */
       -        if(i == n && k->pbuf[n] == k->buf + kd->psize*n + VtScoreSize) {
       -                memmove(kd->score, k->pbuf[n] - VtScoreSize, VtScoreSize);
       -                return;
       -        }
       -        n++;
       -
       -        /* clean up the edge */
       -        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());
       -                k->pbuf[i+1] += VtScoreSize;
       -        }
       -        memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize);
       -}
       -
       -void
       -sinkFree(Sink *k)
       -{
       -        vtMemFree(k->buf);
       -        vtMemFree(k);
       -}
       -
       -DirSink *
       -dirSinkAlloc(VtSession *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->p = k->buf;
       -        k->ep = k->buf + ds;
       -        return k;
       -}
       -
       -void
       -dirSinkWrite(DirSink *k, VtEntry *dir)
       -{
       -        if(k->p + VtEntrySize > k->ep) {
       -                sinkWrite(k->sink, k->buf, k->p - k->buf);
       -                k->p = k->buf;
       -        }
       -        vtEntryPack(dir, k->p, 0);
       -        k->nentry++;
       -        k->p += VtEntrySize;
       -}
       -
       -void
       -dirSinkWriteSink(DirSink *k, Sink *sink)
       -{
       -        dirSinkWrite(k, &sink->dir);
       -}
       -
       -int
       -dirSinkWriteFile(DirSink *k, VacFile *vf)
       -{
       -        VtEntry dir;
       -
       -        if(!vfGetVtEntry(vf, &dir))
       -                return 0;
       -        dirSinkWrite(k, &dir);
       -        return 1;
       -}
       -
       -void
       -dirSinkClose(DirSink *k)
       -{
       -        metaSinkClose(k->msink);
       -        if(k->p != k->buf)
       -                sinkWrite(k->sink, k->buf, k->p - k->buf);
       -        sinkClose(k->sink);
       -}
       -
       -void
       -dirSinkFree(DirSink *k)
       -{
       -        sinkFree(k->sink);
       -        metaSinkFree(k->msink);
       -        vtMemFree(k->buf);
       -        vtMemFree(k);
       -}
       -
       -MetaSink *
       -metaSinkAlloc(VtSession *z, int psize, int dsize)
       -{
       -        MetaSink *k;
       -
       -        k = vtMemAllocZ(sizeof(MetaSink));
       -        k->sink = sinkAlloc(z, psize, dsize);
       -        k->buf = vtMemAlloc(dsize);
       -        k->maxindex = dsize/100;        /* 100 byte entries seems reasonable */
       -        if(k->maxindex < 1)
       -                k->maxindex = 1;
       -        k->rp = k->p = k->buf + MetaHeaderSize + k->maxindex*MetaIndexSize;
       -        k->ep = k->buf + dsize;
       -        return k;
       -}
       -
       -/* hack to get base to compare routine - not reentrant */
       -uchar *blockBase;
       -
       -int
       -dirCmp(void *p0, void *p1)
       -{
       -        uchar *q0, *q1;
       -        int n0, n1, r;
       -
       -        /* name is first element of entry */
       -        q0 = 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];
       -        n1 = (q1[6]<<8) + q1[7];
       -        q1 += 8;
       -
       -        if(n0 == n1)
       -                return memcmp(q0, q1, n0);
       -        else if (n0 < n1) {
       -                r = memcmp(q0, q1, n0);
       -                return (r==0)?1:r;
       -        } else  {
       -                r = memcmp(q0, q1, n1);
       -                return (r==0)?-1:r;
       -        }
       -}
       -
       -void
       -metaSinkFlush(MetaSink *k)
       -{
       -        uchar *p;
       -        int n;
       -        MetaBlock mb;
       -
       -        if(k->nindex == 0)
       -                return;
       -        assert(k->nindex <= k->maxindex);
       -
       -        p = k->buf;
       -        n = k->rp - p;
       -
       -        mb.size = n;
       -        mb.free = 0;
       -        mb.nindex = k->nindex;
       -        mb.maxindex = k->maxindex;
       -        mb.buf = p;
       -        mbPack(&mb);
       -        
       -        p += MetaHeaderSize;
       -
       -        /* XXX this is not reentrant! */
       -        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);
       -
       -        /* move down partial entry */
       -        n = k->p - k->rp;
       -        memmove(p, k->rp, n);
       -        k->rp = p;
       -        k->p = p + n;
       -        k->nindex = 0;
       -}
       -
       -void
       -metaSinkPutc(MetaSink *k, int c)
       -{
       -        if(k->p+1 > k->ep)
       -                metaSinkFlush(k);
       -        if(k->p+1 > k->ep)
       -                vtFatal("directory entry too large");
       -        k->p[0] = c;
       -        k->p++;
       -}
       -
       -void
       -metaSinkPutString(MetaSink *k, char *s)
       -{
       -        int n = strlen(s);
       -        metaSinkPutc(k, n>>8);
       -        metaSinkPutc(k, n);
       -        metaSinkWrite(k, (uchar*)s, n);
       -}
       -
       -void
       -metaSinkPutUint32(MetaSink *k, ulong x)
       -{
       -        metaSinkPutc(k, x>>24);
       -        metaSinkPutc(k, x>>16);
       -        metaSinkPutc(k, x>>8);
       -        metaSinkPutc(k, x);
       -}
       -
       -void
       -metaSinkPutUint64(MetaSink *k, uvlong x)
       -{
       -        metaSinkPutUint32(k, x>>32);
       -        metaSinkPutUint32(k, x);
       -}
       -
       -void
       -metaSinkWrite(MetaSink *k, uchar *data, int n)
       -{
       -        if(k->p + n > k->ep)
       -                metaSinkFlush(k);
       -        if(k->p + n > k->ep)
       -                vtFatal("directory entry too large");
       -        
       -        memmove(k->p, data, n);
       -        k->p += n;
       -}
       -
       -void
       -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);
       -
       -        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);
       -        }
       -
       -        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);
       -        }
       -
       -        metaSinkEOR(ms);
       -}
       -
       -
       -void
       -plan9ToVacDir(VacDir *vd, Dir *dir, ulong entry, uvlong qid)
       -{
       -        memset(vd, 0, sizeof(VacDir));
       -
       -        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->mtime = dir->mtime;
       -        vd->mcount = 0;
       -        vd->ctime = dir->mtime;                /* ctime: not available on plan 9 */
       -        vd->atime = dir->atime;
       -
       -        vd->mode = dir->mode & 0777;
       -        if(dir->mode & DMDIR)
       -                vd->mode |= ModeDir;
       -        if(dir->mode & DMAPPEND)
       -                vd->mode |= ModeAppend;
       -        if(dir->mode & DMEXCL)
       -                vd->mode |= ModeExclusive;
       -
       -        vd->plan9 = 1;
       -        vd->p9path = dir->qid.path;
       -        vd->p9version = dir->qid.vers;
       -}
       -
       -
       -void
       -metaSinkEOR(MetaSink *k)
       -{
       -        uchar *p;
       -        int o, n;
       -
       -        p = k->buf + MetaHeaderSize;
       -        p += k->nindex * MetaIndexSize;
       -        o = k->rp-k->buf;         /* offset from start of block */
       -        n = k->p-k->rp;                /* size of entry */
       -        p[0] = o >> 8;
       -        p[1] = o;
       -        p[2] = n >> 8;
       -        p[3] = n;
       -        k->rp = k->p;
       -        k->nindex++;
       -        if(k->nindex == k->maxindex)
       -                metaSinkFlush(k);
       -}
       -
       -void
       -metaSinkClose(MetaSink *k)
       -{
       -        metaSinkFlush(k);
       -        sinkClose(k->sink);
       -}
       -
       -void
       -metaSinkFree(MetaSink *k)
       -{
       -        sinkFree(k->sink);
       -        vtMemFree(k->buf);
       -        vtMemFree(k);
       -}
       -
       -static void
       -warn(char *fmt, ...)
       -{
       -        va_list arg;
       -
       -        va_start(arg, fmt);
       -        fprint(2, "%s: ", argv0);
       -        vfprint(2, fmt, arg);
       -        fprint(2, "\n");
       -        va_end(arg);
       -}
       -
       -static void
       -cleanup(void)
       -{
       -        if(oname != nil)
       -                remove(oname);
       -}
       -
       -#define TWID64        ((u64int)~(u64int)0)
       -
       -static u64int
       -unittoull(char *s)
       -{
       -        char *es;
       -        u64int n;
       -
       -        if(s == nil)
       -                return TWID64;
       -        n = strtoul(s, &es, 0);
       -        if(*es == 'k' || *es == 'K'){
       -                n *= 1024;
       -                es++;
       -        }else if(*es == 'm' || *es == 'M'){
       -                n *= 1024*1024;
       -                es++;
       -        }else if(*es == 'g' || *es == 'G'){
       -                n *= 1024*1024*1024;
       -                es++;
       -        }
       -        if(*es != '\0')
       -                return TWID64;
       -        return n;
       -}
 (DIR) diff --git a/src/cmd/vac/vac.c b/src/cmd/vac/vac.c
       t@@ -5,7 +5,18 @@
        #include "dat.h"
        #include "fns.h"
        
       -int mainstacksize = 128*1024;
       +/*
       + * We're between a rock and a hard place here.
       + * The pw library (getpwnam, etc.) reads the 
       + * password and group files into an on-stack buffer,
       + * so if you have some huge groups, you overflow
       + * the stack.  Because of this, the thread library turns
       + * it off by default, so that dirstat returns "14571" instead of "rsc".
       + * But for vac we want names.  So cautiously turn the pwlibrary
       + * back on (see threadmain) and make the main thread stack huge.
       + */
       +extern int _p9usepwlibrary;
       +int mainstacksize = 4*1024*1024;
        
        typedef struct Sink Sink;
        typedef struct MetaSink MetaSink;
       t@@ -101,6 +112,8 @@ int nowrite;
        int merge;
        char *isi;
        
       +char **expandargv(char**);
       +
        static void
        usage(void)
        {
       t@@ -116,6 +129,9 @@ threadmain(int argc, char *argv[])
                char *host = nil;
                int statsflag = 0;
        
       +        /* see comment above */
       +        _p9usepwlibrary = 1;
       +
                atexit(cleanup);
        
                ARGBEGIN{
       t@@ -194,20 +210,71 @@ threadmain(int argc, char *argv[])
        
                qsort(exclude, nexclude, sizeof(char*), strpcmp);
        
       +        argv = expandargv(argv);
       +
                vac(z, argv);
        
                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(); */
       +                dup(2, 1);
       +                packetstats();
       +        }
                vthangup(z);
        
                threadexitsall(0);
        }
        
       +// Expand special directory names like / and . and .. into a list of their files.
       +char**
       +expandargv(char **argv)
       +{
       +        char **nargv;
       +        int nargc;
       +        int i, n;
       +        Dir *d;
       +        int fd;
       +        char *s;
       +
       +        nargc = 0;
       +        nargv = nil;
       +        for(; *argv; argv++){
       +                cleanname(*argv);
       +                if(strcmp(*argv, "/") == 0 || strcmp(*argv, ".") == 0 || strcmp(*argv, "..") == 0
       +                                || (strlen(*argv) > 3 && strcmp(*argv+strlen(*argv)-3, "/..") == 0)){
       +                        if((fd = open(*argv, OREAD)) < 0){
       +                                warn("could not open %s: %r", *argv);
       +                                continue;
       +                        }
       +                        n = dirreadall(fd, &d);
       +                        close(fd);
       +                        if(n < 0){
       +                                warn("could not read %s: %r", *argv);
       +                                continue;
       +                        }
       +                        nargv = vtrealloc(nargv, (nargc+n)*sizeof nargv[0]);
       +                        for(i=0; i<n; i++){
       +                                s = vtmalloc(strlen(*argv)+1+strlen(d[i].name)+1);
       +                                strcpy(s, *argv);
       +                                strcat(s, "/");
       +                                strcat(s, d[i].name);
       +                                cleanname(s);
       +                                nargv[nargc++] = s;
       +                        }
       +                        free(d);
       +                        continue;
       +                }
       +                nargv = vtrealloc(nargv, (nargc+1)*sizeof nargv[0]);
       +                nargv[nargc++] = *argv;
       +        }
       +        nargv = vtrealloc(nargv, (nargc+1)*sizeof nargv[0]);
       +        nargv[nargc] = nil;
       +        return nargv;
       +}
       +
        static int
        strpcmp(const void *p0, const void *p1)
        {
       t@@ -353,7 +420,7 @@ vac(VtConn *z, char *argv[])
                
                vtrootpack(&root, buf);
                if(vacwrite(z, score, VtRootType, buf, VtRootSize) < 0)
       -                sysfatal("vacWrite failed: %r");
       +                sysfatal("vacwrite: %r");
        
                fprint(fd, "vac:%V\n", score);
        
       t@@ -566,7 +633,6 @@ vacdata(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
                if(vfblocks > 1)
                        block += vacdataskip(sink, vf, fd, vfblocks, buf, lname);
        
       -if(0) fprint(2, "vacData: %s: %ld\n", lname, block);
                for(;;) {
                        n = readn(fd, buf, bsize);
                        if(0 && n < 0)
       t@@ -857,7 +923,7 @@ sinkwrite(Sink *k, uchar *p, int n)
                        return;
        
                if(n > k->dir.dsize)
       -                sysfatal("sinkWrite: size too big");
       +                sysfatal("sinkwrite: size too big");
        
                if((k->dir.type&~VtTypeDepthMask) == VtDirType){
                        type = VtDirType;
       t@@ -867,7 +933,7 @@ sinkwrite(Sink *k, uchar *p, int n)
                        stats.data++;
                }
                if(vacwrite(k->z, score, type, p, n) < 0)
       -                sysfatal("vacWrite failed: %r");
       +                sysfatal("vacwrite: %r");
        
                sinkwritescore(k, score, n);
        }
       t@@ -924,7 +990,7 @@ sinkclose(Sink *k)
                        p = k->buf+i*kd->psize;
                        stats.meta++;
                        if(vacwrite(k->z, k->pbuf[i+1], base+1+i, p, k->pbuf[i]-p) < 0)
       -                        sysfatal("vacWrite failed: %r");
       +                        sysfatal("vacwrite: %r");
                        k->pbuf[i+1] += VtScoreSize;
                }
                memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize);
       t@@ -1141,6 +1207,7 @@ metasinkwrite(MetaSink *k, uchar *data, int n)
        void
        metasinkwritedir(MetaSink *ms, VacDir *dir)
        {
       +        
                metasinkputuint32(ms, DirMagic);
                metasinkputc(ms, Version>>8);
                metasinkputc(ms, Version);                
       t@@ -1218,7 +1285,6 @@ plan9tovacdir(VacDir *vd, Dir *dir, ulong entry, uvlong qid)
                vd->p9version = dir->qid.vers;
        }
        
       -
        void
        metasinkeor(MetaSink *k)
        {
 (DIR) diff --git a/src/cmd/vac/vac.h b/src/cmd/vac/vac.h
       t@@ -101,13 +101,6 @@ 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*);
       - */
       -
        VacFile *vacfsgetroot(VacFs *fs);
        VacFile        *vacfileopen(VacFs *fs, char *path);
        VacFile        *vacfilecreate(VacFile *file, char *elem, ulong perm, char *muid);
       t@@ -140,4 +133,4 @@ void                vdcopy(VacDir *dst, VacDir *src);
        VacDirEnum        *vdeopen(VacFile*);
        int                        vderead(VacDirEnum*, VacDir *);
        void                        vdeclose(VacDirEnum*);
       -
       +int        vdeunread(VacDirEnum*);
 (DIR) diff --git a/src/cmd/vac/vacfs.c b/src/cmd/vac/vacfs.c
       t@@ -5,18 +5,12 @@
        #include "vac.h"
        
        typedef struct Fid Fid;
       -typedef struct DirBuf DirBuf;
        
        enum
        {
                OPERM        = 0x3                /* mask of all permission types in open mode */
        };
        
       -enum
       -{
       -        DirBufSize = 20
       -};
       -
        struct Fid
        {
                short busy;
       t@@ -25,18 +19,8 @@ struct Fid
                char *user;
                Qid qid;
                VacFile *file;
       -
       -        DirBuf *db;
       -
       -        Fid        *next;
       -};
       -
       -struct DirBuf
       -{
                VacDirEnum *vde;
       -        VacDir buf[DirBufSize];
       -        int i, n;
       -        int eof;
       +        Fid        *next;
        };
        
        enum
       t@@ -73,12 +57,8 @@ int        perm(Fid*, int);
        int        permf(VacFile*, char*, int);
        ulong        getl(void *p);
        void        init(char*, char*, long, int);
       -DirBuf        *dirBufAlloc(VacFile*);
       -VacDir        *dirBufGet(DirBuf*);
       -int        dirBufUnget(DirBuf*);
       -void        dirBufFree(DirBuf*);
        int        vacdirread(Fid *f, char *p, long off, long cnt);
       -int        vdStat(VacFile *parent, VacDir *vd, uchar *p, int np);
       +int        vacstat(VacFile *parent, VacDir *vd, uchar *p, int np);
        void         srv(void* a);
        
        
       t@@ -141,7 +121,6 @@ threadmain(int argc, char *argv[])
                int stdio = 0;
                char *host = nil;
                long ncache = 1000;
       -        int readOnly = 1;
        
                fmtinstall('H', encodefmt);
                fmtinstall('V', vtscorefmt);
       t@@ -184,7 +163,20 @@ threadmain(int argc, char *argv[])
                        usage();
        
                initfcalls();
       -        init(argv[0], host, ncache, readOnly);
       +
       +        notify(notifyf);
       +        user = getuser();
       +
       +        conn = vtdial(host);
       +        if(conn == nil)
       +                sysfatal("could not connect to server: %r");
       +
       +        if(vtconnect(conn) < 0)
       +                sysfatal("vtconnect: %r");
       +
       +        fs = vacfsopen(conn, argv[0], VtOREAD, ncache);
       +        if(fs == nil)
       +                sysfatal("vacfsopen: %r");
        
                if(pipe(p) < 0)
                        sysfatal("pipe failed: %r");
       t@@ -294,19 +286,6 @@ rattach(Fid *f)
                return 0;
        }
        
       -VacFile*
       -_vfWalk(VacFile *file, char *name)
       -{
       -        VacFile *n;
       -
       -        n = vacfilewalk(file, name);
       -        if(n)
       -                return n;
       -        if(strcmp(name, "SLASH") == 0)
       -                return vacfilewalk(file, "/");
       -        return nil;
       -}
       -
        char*
        rwalk(Fid *f)
        {
       t@@ -356,7 +335,7 @@ rwalk(Fid *f)
                                err = Eperm;
                                break;
                        }
       -                nfile = _vfWalk(file, rhdr.wname[nqid]);
       +                nfile = vacfilewalk(file, rhdr.wname[nqid]);
                        if(nfile == nil)
                                break;
                        vacfiledecref(file);
       t@@ -410,7 +389,7 @@ ropen(Fid *f)
                        if(!perm(f, Pread))
                                return vtstrdup(Eperm);
                        thdr.qid = f->qid;
       -                f->db = nil;
       +                f->vde = nil;
                        f->open = 1;
                        return 0;
                }
       t@@ -564,8 +543,8 @@ rclunk(Fid *f)
                if(f->file)
                        vacfiledecref(f->file);
                f->file = nil;
       -        dirBufFree(f->db);
       -        f->db = nil;
       +        vdeclose(f->vde);
       +        f->vde = nil;
                return 0;
        }
        
       t@@ -588,8 +567,6 @@ rremove(Fid *f)
        
                if(!vacfileremove(vf, "none")) {
                        rerrstr(errbuf, sizeof errbuf);
       -print("vfRemove failed: %s\n", errbuf);
       -
                        err = errbuf;
                }
        
       t@@ -611,7 +588,7 @@ rstat(Fid *f)
                parent = vacfilegetparent(f->file);
                vacfilegetdir(f->file, &dir);
                thdr.stat = statbuf;
       -        thdr.nstat = vdStat(parent, &dir, thdr.stat, sizeof statbuf);
       +        thdr.nstat = vacstat(parent, &dir, thdr.stat, sizeof statbuf);
                vdcleanup(&dir);
                vacfiledecref(parent);
                return 0;
       t@@ -626,7 +603,7 @@ rwstat(Fid *f)
        }
        
        int
       -vdStat(VacFile *parent, VacDir *vd, uchar *p, int np)
       +vacstat(VacFile *parent, VacDir *vd, uchar *p, int np)
        {
                char *ext;
                int n, ret;
       t@@ -694,96 +671,39 @@ vdStat(VacFile *parent, VacDir *vd, uchar *p, int np)
                return ret;
        }
        
       -DirBuf*
       -dirBufAlloc(VacFile *vf)
       -{
       -        DirBuf *db;
       -
       -        db = vtmallocz(sizeof(DirBuf));
       -        db->vde = vdeopen(vf);
       -        return db;
       -}
       -
       -VacDir *
       -dirBufGet(DirBuf *db)
       -{
       -        VacDir *vd;
       -        int n;
       -
       -        if(db->eof)
       -                return nil;
       -
       -        if(db->i >= db->n) {
       -                n = vderead(db->vde, db->buf);
       -                if(n < 0)
       -                        return nil;
       -                db->i = 0;
       -                db->n = n;
       -                if(n == 0) {
       -                        db->eof = 1;
       -                        return nil;
       -                }
       -        }
       -
       -        vd = db->buf + db->i;
       -        db->i++;
       -
       -        return vd;
       -}
       -
       -int
       -dirBufUnget(DirBuf *db)
       -{
       -        assert(db->i > 0);
       -        db->i--;
       -        return 1;
       -}
       -
       -void
       -dirBufFree(DirBuf *db)
       -{
       -        int i;
       -
       -        if(db == nil)
       -                return;
       -
       -        for(i=db->i; i<db->n; i++)
       -                vdcleanup(db->buf + i);
       -        vdeclose(db->vde);
       -        vtfree(db);
       -}
       -
        int
        vacdirread(Fid *f, char *p, long off, long cnt)
        {
       -        int n, nb;
       -        VacDir *vd;
       +        int i, n, nb;
       +        VacDir vd;
        
                /*
                 * special case of rewinding a directory
                 * otherwise ignore the offset
                 */
       -        if(off == 0 && f->db) {
       -                dirBufFree(f->db);
       -                f->db = nil;
       +        if(off == 0 && f->vde){
       +                vdeclose(f->vde);
       +                f->vde = nil;
                }
        
       -        if(f->db == nil)
       -                f->db = dirBufAlloc(f->file);
       +        if(f->vde == nil){
       +                f->vde = vdeopen(f->file);
       +                if(f->vde == nil)
       +                        return -1;
       +        }
        
                for(nb = 0; nb < cnt; nb += n) {
       -                vd = dirBufGet(f->db);
       -                if(vd == nil) {
       -                        if(!f->db->eof)
       -                                return -1;
       +                i = vderead(f->vde, &vd);
       +                if(i < 0)
       +                        return -1;
       +                if(i == 0)
                                break;
       -                }
       -                n = vdStat(f->file, vd, (uchar*)p, cnt-nb);
       +                n = vacstat(f->file, &vd, (uchar*)p, cnt-nb);
                        if(n <= BIT16SZ) {
       -                        dirBufUnget(f->db);
       +                        vdeunread(f->vde);
                                break;
                        }
       -                vdcleanup(vd);
       +                vdcleanup(&vd);
                        p += n;
                }
                return nb;
       t@@ -886,24 +806,6 @@ perm(Fid *f, int p)
        }
        
        void
       -init(char *file, char *host, long ncache, int readOnly)
       -{
       -        notify(notifyf);
       -        user = getuser();
       -
       -        conn = vtdial(host);
       -        if(conn == nil)
       -                sysfatal("could not connect to server: %r");
       -
       -        if(vtconnect(conn) < 0)
       -                sysfatal("vtconnect: %r");
       -
       -        fs = vacfsopen(conn, file, /*readOnly ? ModeSnapshot :*/ VtOREAD, ncache);
       -        if(fs == nil)
       -                sysfatal("vfsOpen: %r");
       -}
       -
       -void
        vacshutdown(void)
        {
                Fid *f;
 (DIR) diff --git a/src/cmd/vac/vactest.c b/src/cmd/vac/vactest.c
       t@@ -1,182 +0,0 @@
       -#include "stdinc.h"
       -#include "vac.h"
       -#include "dat.h"
       -#include "fns.h"
       -
       -void usage(void);
       -int unvac(VacFS *fs);
       -int readScore(int fd, uchar score[VtScoreSize]);
       -static void warn(char *fmt, ...);
       -void dirlist(VacFS *fs, char *path);
       -
       -static        int        nwant;
       -static        char        **want;
       -static        int        dflag = 1;
       -static        int        cflag;
       -static        int        lower;
       -static        int        verbose;
       -static        int        settimes;
       -
       -void
       -main(int argc, char *argv[])
       -{
       -        char *zfile;
       -        int ok, table;
       -        VtSession *z;
       -        char *vsrv = nil;
       -        char *host = nil;
       -        char *p;
       -        int ncache = 1000;
       -        VacFS *fs;
       -
       -        table = 0;
       -        zfile = nil;
       -        ARGBEGIN{
       -        case 'D':
       -                dflag++;
       -                break;
       -        case 'c':
       -                cflag++;
       -                break;
       -        case 'C':
       -                p = ARGF();
       -                if(p == nil)
       -                        usage();
       -                ncache = atoi(p);
       -                if(ncache < 10)
       -                        ncache = 10;
       -                if(ncache > 1000000)
       -                        ncache = 1000000;
       -                break;
       -        case 'i':
       -                lower++;
       -                break;
       -        case 'f':
       -                zfile = ARGF();
       -                if(zfile == nil)
       -                        usage();
       -                break;
       -        case 'h':
       -                host = ARGF();
       -                break;
       -        case 't':
       -                table++;
       -                break;
       -        case 'T':
       -                settimes++;
       -                break;
       -        case 's':
       -                vsrv = ARGF();
       -                break;
       -        case 'v':
       -                verbose++;
       -                break;
       -        default:
       -                usage();
       -                break;
       -        }ARGEND
       -
       -        nwant = argc;
       -        want = argv;
       -
       -        vtAttach();
       -
       -        if(zfile == nil)
       -                usage();
       -
       -        if(vsrv != nil)
       -                z = vtStdioServer(vsrv);
       -        else
       -                z = vtDial(host);
       -        if(z == nil)
       -                vtFatal("could not connect to server: %s", vtGetError());
       -        vtSetDebug(z, 0);
       -        if(!vtConnect(z, 0))
       -                vtFatal("vtConnect: %s", vtGetError());
       -        fs = vfsOpen(z, zfile, 1, ncache);
       -        if(fs == nil)
       -                vtFatal("vfsOpen: %s", vtGetError());
       -        ok = unvac(fs);
       -        vtClose(z);
       -        vtDetach();
       -        
       -        exits(ok? 0 : "error");
       -}
       -
       -void
       -usage(void)
       -{
       -        fprint(2, "usage: %s [-tTcDv] -f zipfile [-s ventid] [-h host] [file ...]\n", argv0);
       -        exits("usage");
       -}
       -
       -void
       -suck(VacFile *f)
       -{
       -        USED(f);
       -}
       -
       -
       -void
       -vacfile(VacFS *fs, char *path, VacDir *vd)
       -{
       -        char *path2;
       -
       -        path2 = vtMemAlloc(strlen(path) + 1 + strlen(vd->elem) + 1);
       -        if(path[1] == 0)
       -                sprintf(path2, "/%s", vd->elem);
       -        else
       -                sprintf(path2, "%s/%s", path, vd->elem);
       -fprint(2, "vac file: %s\n", path2);
       -        if(vd->mode & ModeDir)
       -                dirlist(fs, path2);
       -        vtMemFree(path2);
       -}
       -
       -void
       -dirlist(VacFS *fs, char *path)
       -{
       -        VacDir vd[50];
       -        VacDirEnum *ds;
       -        int i, n;
       -
       -        ds = vdeOpen(fs, path);
       -        if(ds == nil) {
       -                fprint(2, "could not open: %s: %s\n", path, vtGetError());
       -                return;
       -        }
       -        for(;;) {
       -                n = vdeRead(ds, vd, sizeof(vd)/sizeof(VacDir));
       -                if(n < 0) {
       -                        warn("vdRead failed: %s: %s", path, vtGetError());
       -                        return;
       -                }
       -                if(n == 0)
       -                        break;
       -                for(i=0; i<n; i++) {
       -                        vacfile(fs, path, &vd[i]);
       -                        vdCleanup(&vd[i]);
       -                }
       -        }
       -        vdeFree(ds);
       -}
       -
       -int
       -unvac(VacFS *fs)
       -{
       -        dirlist(fs, "/");
       -
       -        return 1;
       -}
       -
       -static void
       -warn(char *fmt, ...)
       -{
       -        va_list arg;
       -
       -        va_start(arg, fmt);
       -        fprint(2, "%s: ", argv0);
       -        vfprint(2, fmt, arg);
       -        fprint(2, "\n");
       -        va_end(arg);
       -}
 (DIR) diff --git a/src/cmd/vac/vtread.c b/src/cmd/vac/vtread.c
       t@@ -1,126 +0,0 @@
       -#include "stdinc.h"
       -#include <bio.h>
       -
       -typedef struct Source Source;
       -
       -struct Source
       -{
       -        ulong gen;
       -        int psize;
       -        int dsize;
       -        int dir;
       -        int active;
       -        int depth;
       -        uvlong size;
       -        uchar score[VtScoreSize];
       -        int reserved;
       -};
       -
       -int bsize;
       -Biobuf *bout;
       -VtRootLump root;
       -int ver;
       -int cmp;
       -int all;
       -int find;
       -uchar fscore[VtScoreSize];
       -int dirSize;
       -void (*parse)(Source*, uchar*);
       -VtSession *z;
       -
       -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(VtRootLump*, uchar *score, char *file);
       -void parse1(Source*, uchar*);
       -void parse2(Source*, uchar*);
       -int dumpDir(Source*, int indent);
       -
       -void
       -main(int argc, char *argv[])
       -{
       -        char *host = nil;
       -        uchar score[VtScoreSize];
       -        uchar buf[VtMaxLumpSize];
       -        int type;
       -        int n;
       -        
       -        type = VtDataType;
       -
       -        ARGBEGIN{
       -        case 't':
       -                type = atoi(ARGF());
       -                break;
       -        }ARGEND
       -
       -        vtAttach();
       -
       -        bout = vtMemAllocZ(sizeof(Biobuf));
       -        Binit(bout, 1, OWRITE);
       -
       -        if(argc != 1)
       -                usage();
       -
       -        vtAttach();
       -
       -        fmtinstall('V', vtScoreFmt);
       -        fmtinstall('R', vtErrFmt);
       -
       -        z = vtDial(host);
       -        if(z == nil)
       -                vtFatal("could not connect to server: %s", vtGetError());
       -
       -        if(!vtConnect(z, 0))
       -                sysfatal("vtConnect: %r");
       -
       -        if(!parseScore(score, argv[0], strlen(argv[0])))
       -                vtFatal("could not parse score: %s", vtGetError());
       -
       -        n = vtRead(z, score, type, buf, VtMaxLumpSize);
       -        if(n < 0)
       -                vtFatal("could not read block: %s", vtGetError());
       -        Bwrite(bout, buf, n);
       -
       -        Bterm(bout);
       -
       -        vtClose(z);
       -        vtDetach();
       -        exits(0);
       -}
       -
       -void
       -usage(void)
       -{
       -        fprint(2, "%s: -t type score\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;
       -}
 (DIR) diff --git a/src/cmd/vac/wtest.c b/src/cmd/vac/wtest.c
       t@@ -1,47 +0,0 @@
       -#include "stdinc.h"
       -
       -enum {
       -        Nblock = 10000,
       -        BlockSize = 8*1024
       -};
       -
       -uchar data[Nblock*BlockSize];
       -
       -void
       -main(int argc, char *argv[])
       -{
       -        VtSession *z;
       -        int i;
       -        uchar score[VtScoreSize];
       -        int start;
       -
       -        ARGBEGIN{
       -        }ARGEND
       -
       -        for(i=0; i<Nblock; i++) {
       -                if(readn(0, data+i*BlockSize, BlockSize) < BlockSize)
       -                        sysfatal("read failed: %r");
       -        }
       -
       -        vtAttach();
       -
       -        z = vtDial("iolaire2");
       -        if(z == nil)
       -                sysfatal("cound not connect to venti");
       -        if(!vtConnect(z, 0))
       -                vtFatal("vtConnect: %s", vtGetError());
       -
       -        print("starting\n");
       -
       -        start = times(0);
       -
       -        for(i=0; i<Nblock; i++) {
       -                if(!vtWrite(z, score, VtDataType, data+i*BlockSize, BlockSize))
       -                        vtFatal("vtWrite failed: %s", vtGetError());
       -        }
       -
       -        print("time = %f\n", (times(0) - start)*0.001);
       -
       -        vtClose(z);
       -        vtDetach();
       -}