tvac: make qids unique - 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 003c13aa185cebc8797b8b04e8bd0722643467aa
 (DIR) parent ada3d479a7d132a0da51a2cca3e9354bf2e7661c
 (HTM) Author: Russ Cox <rsc@swtch.com>
       Date:   Sun, 15 Jun 2008 01:19:37 -0400
       
       vac: make qids unique
       
       Diffstat:
         M src/cmd/vac/file.c                  |      49 ++++++++++++++++++++++++++++++-
         M src/cmd/vac/fns.h                   |       1 +
         M src/cmd/vac/mkfile                  |       4 +---
         M src/cmd/vac/pack.c                  |       2 +-
         M src/cmd/vac/vac.c                   |    1698 +++++++++----------------------
         M src/cmd/vac/vac.h                   |       9 ++++++++-
         D src/cmd/vac/vtdump.c                |     372 -------------------------------
       
       7 files changed, 564 insertions(+), 1571 deletions(-)
       ---
 (DIR) diff --git a/src/cmd/vac/file.c b/src/cmd/vac/file.c
       t@@ -45,6 +45,8 @@ struct VacFile
                VtFile        *msource;        /* metadata for children in a directory */
                VacFile        *down;        /* children */
                int                mode;
       +        
       +        uvlong        qidoffset;        /* qid offset */
        };
        
        static VacFile*
       t@@ -140,7 +142,7 @@ uvlong
        vacfilegetid(VacFile *f)
        {
                /* immutable */
       -        return f->dir.qid;
       +        return f->qidoffset + f->dir.qid;
        }
        
        ulong
       t@@ -400,6 +402,7 @@ dirlookup(VacFile *f, char *elem)
                                        filefree(ff);
                                        goto Err;
                                }
       +                        ff->qidoffset = f->qidoffset + ff->dir.qidoffset;
                                vtfileunlock(meta);
                                vtblockput(b);
                                ff->boff = bo;
       t@@ -993,6 +996,7 @@ Found:
                me.p = p;
                me.size = n;
                vdpack(dir, &me, VacDirVersion);
       +vdunpack(dir, &me);
                mbinsert(&mb, i, &me);
                mbpack(&mb);
                vtblockput(b);
       t@@ -1062,6 +1066,7 @@ filemetaflush(VacFile *f, char *oelem)
        
                        /* Pack new data into new location. */
                        vdpack(&f->dir, &me, VacDirVersion);
       +vdunpack(&f->dir, &me);
                        mbinsert(&mb, i, &me);
                        mbpack(&mb);
                        
       t@@ -1269,6 +1274,7 @@ vacfilecreate(VacFile *fp, char *elem, ulong mode)
                if(vtfilelock2(fp->source, fp->msource, -1) < 0)
                        goto Err1;
                ff = filealloc(fp->fs);
       +        ff->qidoffset = fp->qidoffset;        /* hopefully fp->qidoffset == 0 */
                type = VtDataType;
                if(mode & ModeDir)
                        type = VtDirType;
       t@@ -1601,10 +1607,16 @@ vacfilesetqidspace(VacFile *f, u64int offset, u64int max)
        
                if(filelock(f) < 0)
                        return -1;
       +        if(f->source->mode != VtORDWR){
       +                fileunlock(f);
       +                werrstr(EReadOnly);
       +                return -1;
       +        }
                filemetalock(f);
                f->dir.qidspace = 1;
                f->dir.qidoffset = offset;
                f->dir.qidmax = max;
       +        f->dirty = 1;
                ret = filemetaflush(f, nil);
                filemetaunlock(f);
                fileunlock(f);
       t@@ -1853,6 +1865,34 @@ _vacfsnextqid(VacFs *fs, uvlong *qid)
        }
        
        void
       +vacfsjumpqid(VacFs *fs, uvlong step)
       +{
       +        fs->qid += step;
       +}
       +
       +/*
       + * Set *maxqid to the maximum qid expected in this file system.
       + * In newer vac archives, the maximum qid is stored in the
       + * qidspace VacDir annotation.  In older vac archives, the root
       + * got created last, so it had the maximum qid.
       + */
       +int
       +vacfsgetmaxqid(VacFs *fs, uvlong *maxqid)
       +{
       +        VacDir vd;
       +        
       +        if(vacfilegetdir(fs->root, &vd) < 0)
       +                return -1;
       +        if(vd.qidspace)
       +                *maxqid = vd.qidmax;
       +        else
       +                *maxqid = vd.qid;
       +        vdcleanup(&vd);
       +        return 0;
       +}
       +
       +
       +void
        vacfsclose(VacFs *fs)
        {
                if(fs->root)
       t@@ -1952,6 +1992,13 @@ vacfssync(VacFs *fs)
                /* Sync the entire vacfs to disk. */
                if(vacfileflush(fs->root, 1) < 0)
                        return -1;
       +        if(vtfilelock(fs->root->up->msource, -1) < 0)
       +                return -1;
       +        if(vtfileflush(fs->root->up->msource) < 0){
       +                vtfileunlock(fs->root->up->msource);
       +                return -1;
       +        }
       +        vtfileunlock(fs->root->up->msource);
        
                /* Prepare the dir stream for the root block. */
                if(getentry(fs->root->source, &e) < 0)
 (DIR) diff --git a/src/cmd/vac/fns.h b/src/cmd/vac/fns.h
       t@@ -22,3 +22,4 @@ void        vdpack(VacDir *dir, MetaEntry*, int);
        VacFile *_vacfileroot(VacFs *fs, VtFile *file);
        
        int        _vacfsnextqid(VacFs *fs, uvlong *qid);
       +void        vacfsjumpqid(VacFs*, uvlong step);
 (DIR) diff --git a/src/cmd/vac/mkfile b/src/cmd/vac/mkfile
       t@@ -15,10 +15,8 @@ HFILES=\
                dat.h\
                fns.h\
        
       -TARG=vac vacfs unvac # vtdump
       +TARG=vac vacfs unvac
        
        default:V: all
        
       -test:V: $O.srcload $O.wtest $O.rtest $O.vtdump $O.vtread
       -
        <$PLAN9/src/mkmany
 (DIR) diff --git a/src/cmd/vac/pack.c b/src/cmd/vac/pack.c
       t@@ -463,7 +463,7 @@ vdpack(VacDir *dir, MetaEntry *me, int version)
                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);
 (DIR) diff --git a/src/cmd/vac/vac.c b/src/cmd/vac/vac.c
       t@@ -1,154 +1,103 @@
       -#include <u.h>
       -#include <sys/stat.h>
        #include "stdinc.h"
        #include "vac.h"
        #include "dat.h"
        #include "fns.h"
        
       -/*
       - * 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;
       -typedef struct DirSink DirSink;
       -
       -struct Sink {
       -        VtConn *z;
       -        VtEntry dir;
       -        uchar *buf;
       -        uchar *pbuf[VtPointerDepth+1];
       -};
       -
       -struct DirSink {
       -        Sink *sink;
       -        MetaSink *msink;
       -        ulong nentry;
       -        uchar *buf;
       -        uchar *p;        /* current pointer */
       -        uchar *ep;        /* end pointer */
       -};
       -
       -struct MetaSink {
       -        Sink *sink;
       -        uchar *buf;
       -        int maxindex;
       -        int nindex;
       -        uchar *rp;        /* start of current record */
       -        uchar *p;        /* current pointer */
       -        uchar *ep;        /* end pointer */
       -};
       -
       -static void usage(void);
       -static int strpcmp(const void*, const void*);
       -static void warn(char *fmt, ...);
       -static void cleanup(void);
       -static u64int unittoull(char *s);
       -static void vac(VtConn *z, char *argv[]);
       -static void vacfile(DirSink *dsink, char *lname, char *sname, VacFile*);
       -static void vacstdin(DirSink *dsink, char *name, VacFile *vf);
       -static void vacdata(DirSink *dsink, int fd, char *lname, VacFile*, Dir*);
       -static void vacdir(DirSink *dsink, int fd, char *lname, char *sname, VacFile*);
       -static int vacmerge(DirSink *dsink, char *lname, char *sname);
       -static int vacspecial(DirSink *dsink, Dir *dir, char *lname, char *sname, VacFile *vf);
       -Sink *sinkalloc(VtConn *z, int psize, int dsize);
       -void sinkwrite(Sink *k, uchar *data, int n);
       -void sinkwritescore(Sink *k, uchar *score, int n);
       -void sinkclose(Sink *k);
       -void sinkfree(Sink *k);
       +// TODO: qids
        
       -DirSink *dirsinkalloc(VtConn *z, int psize, int dsize);
       -void dirsinkwrite(DirSink *k, VtEntry*);
       -void dirsinkwritesink(DirSink *k, Sink*);
       -int dirsinkwritefile(DirSink *k, VacFile *vf);
       -void dirsinkclose(DirSink *k);
       -void dirsinkfree(DirSink *k);
       -
       -MetaSink *metasinkalloc(VtConn *z, int psize, int dsize);
       -void metasinkputc(MetaSink *k, int c);
       -void metasinkputstring(MetaSink *k, char *s);
       -void metasinkputuint32(MetaSink *k, ulong x);
       -void metasinkputuint64(MetaSink *k, uvlong x);
       -void metasinkwrite(MetaSink *k, uchar *data, int n);
       -void metasinkwritedir(MetaSink *ms, VacDir *vd);
       -void metasinkeor(MetaSink *k);
       -void metasinkclose(MetaSink *k);
       -void metasinkfree(MetaSink *k);
       -void plan9tovacdir(VacDir*, Dir*, ulong entry, uvlong qid);
       +void
       +usage(void)
       +{
       +        fprint(2, "usage: vac [-b blocksize] [-h host] file...\n");
       +        threadexitsall("usage");
       +}
        
       -enum {
       -        Version = 8,
       +enum
       +{
                BlockSize = 8*1024,
                MaxExclude = 1000
        };
        
       -struct {
       -        ulong        file;
       -        ulong        sfile;
       -        ulong        data;
       -        ulong        sdata;
       -        ulong        skip;
       -        ulong        meta;
       +struct
       +{
       +        int nfile;
       +        int ndir;
       +        vlong data;
       +        vlong skipdata;
       +        int skipfiles;
        } stats;
        
       -int bsize = BlockSize;
       -int maxbsize;
       -char *oname, *dfile;
       -int verbose;
       -uvlong fileid = 1;
        int qdiff;
       +int merge;
       +int verbose;
       +char *host;
       +VtConn *z;
       +VacFs *fs;
        char *exclude[MaxExclude];
        int nexclude;
       -int nowrite;
       -int merge;
       -char *isi;
       +char *vacfile;
        
       -char **expandargv(char**);
       +int vacmerge(VacFile*, char*);
       +void vac(VacFile*, VacFile*, char*, Dir*);
       +void vacstdin(VacFile*, char*);
        
       -static void
       -usage(void)
       -{
       -        fprint(2, "usage: %s [-amqsv] [-h host] [-d vacfile] [-b blocksize] [-i name] [-e exclude] [-f vacfile] file ... \n", argv0);
       -        threadexitsall("usage");
       -}
       +static u64int unittoull(char*);
       +static void warn(char *fmt, ...);
       +static int strpcmp(const void*, const void*);
       +static void removevacfile(void);
       +
       +/*
       + * 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;
        
        void
       -threadmain(int argc, char *argv[])
       +threadmain(int argc, char **argv)
        {
       -        VtConn *z;
       -        char *p;
       -        char *host = nil;
       -        int statsflag = 0;
       -
       +        int i, j, fd, n, printstats;
       +        Dir *d;
       +        char *s;
       +        uvlong u;
       +        VacFile *f, *fdiff;
       +        VacFs *fsdiff;
       +        int blocksize;
       +        int outfd;
       +        char *stdinname;
       +        char *diffvac;
       +        uvlong qid;
       +
       +#ifdef PLAN9PORT
                /* see comment above */
                _p9usepwlibrary = 1;
       +#endif
        
       -        atexit(cleanup);
       +        fmtinstall('H', encodefmt);
       +        fmtinstall('V', vtscorefmt);
        
       +        blocksize = BlockSize;
       +        stdinname = nil;
       +        printstats = 0;
       +        fsdiff = nil;
       +        diffvac = nil;
                ARGBEGIN{
       -        default:
       -                usage();
                case 'b':
       -                p = ARGF();
       -                if(p == 0)
       -                        usage();
       -                bsize = unittoull(p);
       -                if(bsize == ~0)
       -                        usage();
       +                u = unittoull(EARGF(usage()));
       +                if(u < 512)
       +                        u = 512;
       +                if(u > VtMaxLumpSize)
       +                        u = VtMaxLumpSize;
       +                blocksize = u;
                        break;
                case 'd':
       -                dfile = ARGF();
       -                if(dfile == nil)
       -                        usage();
       +                diffvac = EARGF(usage());
                        break;
                case 'e':
                        if(nexclude >= MaxExclude)
       t@@ -159,22 +108,13 @@ threadmain(int argc, char *argv[])
                        nexclude++;
                        break;
                case 'f':
       -                oname = ARGF();
       -                if(oname == 0)
       -                        usage();
       +                vacfile = EARGF(usage());
                        break;
                case 'h':
       -                host = ARGF();
       -                if(host == nil)
       -                        usage();
       +                host = EARGF(usage());
                        break;
                case 'i':
       -                isi = ARGF();
       -                if(isi == nil)
       -                        usage();
       -                break;
       -        case 'n':
       -                nowrite++;
       +                stdinname = EARGF(usage());
                        break;
                case 'm':
                        merge++;
       t@@ -183,249 +123,135 @@ threadmain(int argc, char *argv[])
                        qdiff++;
                        break;
                case 's':
       -                statsflag++;
       +                printstats++;
                        break;
                case 'v':
                        verbose++;
                        break;
       -        }ARGEND;
       -
       -        if(argc == 0)
       +        default:
       +                usage();
       +        }ARGEND
       +        
       +        if(argc == 0 && !stdinname)
                        usage();
        
       -        if(bsize < 512)
       -                bsize = 512;
       -        if(bsize > VtMaxLumpSize)
       -                bsize = VtMaxLumpSize;
       -        maxbsize = bsize;
       +        if(vacfile == nil)
       +                outfd = 1;
       +        else if((outfd = create(vacfile, OWRITE, 0666)) < 0)
       +                sysfatal("create %s: %r", vacfile);
       +        atexit(removevacfile);
        
       -        fmtinstall('V', vtscorefmt);
       +        qsort(exclude, nexclude, sizeof(char*), strpcmp);
        
                z = vtdial(host);
                if(z == nil)
                        sysfatal("could not connect to server: %r");
       -
                if(vtconnect(z) < 0)
                        sysfatal("vtconnect: %r");
       -
       -        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){
       -                fprint(2, "files %ld:%ld data %ld:%ld:%ld meta %ld\n", stats.file, stats.sfile,
       -                        stats.data, stats.skip, stats.sdata, stats.meta);
       -                dup(2, 1);
       -                packetstats();
       +        
       +        if(diffvac){
       +                if((fsdiff = vacfsopen(z, diffvac, VtOREAD, 128)) == nil)
       +                        warn("vacfsopen %s: %r", diffvac);
                }
       -        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;
       +        if((fs = vacfscreate(z, blocksize, 512)) == nil)
       +                sysfatal("vacfscreate: %r");
        
       -        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);
       +        f = vacfsgetroot(fs);
       +        if(fsdiff)
       +                fdiff = vacfsgetroot(fsdiff);
       +        else
       +                fdiff = nil;
       +        if(stdinname)
       +                vacstdin(f, stdinname);
       +        for(i=0; i<argc; i++){
       +                // We can't use / and . and .. and ../.. as valid archive
       +                // names, so expand to the list of files in the directory.
       +                if(argv[i][0] == 0){
       +                        warn("empty string given as command-line argument");
       +                        continue;
       +                }
       +                cleanname(argv[i]);
       +                if(strcmp(argv[i], "/") == 0
       +                || strcmp(argv[i], ".") == 0
       +                || strcmp(argv[i], "..") == 0
       +                || (strlen(argv[i]) > 3 && strcmp(argv[i]+strlen(argv[i])-3, "/..") == 0)){
       +                        if((fd = open(argv[i], OREAD)) < 0){
       +                                warn("open %s: %r", argv[i]);
                                        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;
       +                        while((n = dirread(fd, &d)) > 0){
       +                                for(j=0; j<n; j++){
       +                                        s = vtmalloc(strlen(argv[i])+1+strlen(d[j].name)+1);
       +                                        strcpy(s, argv[i]);
       +                                        strcat(s, "/");
       +                                        strcat(s, d[j].name);
       +                                        cleanname(s);
       +                                        vac(f, fdiff, s, &d[j]);
       +                                }
       +                                free(d);
                                }
       -                        free(d);
       +                        close(fd);
                                continue;
                        }
       -                nargv = vtrealloc(nargv, (nargc+1)*sizeof nargv[0]);
       -                nargv[nargc++] = *argv;
       +                if((d = dirstat(argv[i])) == nil){
       +                        warn("stat %s: %r", argv[i]);
       +                        continue;
       +                }
       +                vac(f, fdiff, argv[i], d);
       +                free(d);
                }
       -        nargv = vtrealloc(nargv, (nargc+1)*sizeof nargv[0]);
       -        nargv[nargc] = nil;
       -        return nargv;
       -}
       -
       -static int
       -strpcmp(const void *p0, const void *p1)
       -{
       -        return strcmp(*(char**)p0, *(char**)p1);
       -}
       +        if(fdiff)
       +                vacfiledecref(fdiff);
       +        
       +        /*
       +         * Record the maximum qid so that vacs can be merged
       +         * without introducing overlapping qids.  Older versions
       +         * of vac arranged that the root would have the largest
       +         * qid in the file system, but we can't do that anymore
       +         * (the root gets created first!).
       +         */
       +        if(_vacfsnextqid(fs, &qid) >= 0)
       +                vacfilesetqidspace(f, 0, qid);
       +        vacfiledecref(f);
       +
       +        /*
       +         * Copy fsdiff's root block score into fs's slot for that,
       +         * so that vacfssync will copy it into root.prev for us.
       +         * Just nice documentation, no effect.
       +         */
       +        if(fsdiff)
       +                memmove(fs->score, fsdiff->score, VtScoreSize);
       +        if(vacfssync(fs) < 0)
       +                fprint(2, "vacfssync: %r\n");
       +
       +        fprint(outfd, "vac:%V\n", fs->score);
       +        vacfsclose(fs);
       +        atexitdont(removevacfile);
       +        vthangup(z);
        
       -int
       -vacwrite(VtConn *z, uchar score[VtScoreSize], int type, uchar *buf, int n)
       -{
       -        assert(n > 0);
       -        if(nowrite){
       -                sha1(buf, n, score, nil);
       -                return 0;
       +        if(printstats){
       +                fprint(2,
       +                        "%d files, %d files skipped, %d directories\n"
       +                        "%lld data bytes written, %lld data bytes skipped\n",
       +                        stats.nfile, stats.skipfiles, stats.ndir, stats.data, stats.skipdata);
       +                dup(2, 1);
       +                packetstats();
                }
       -        return vtwrite(z, score, type, buf, n);
       +        threadexitsall(0);
        }
        
       -static char*
       -lastelem(char *oname)
       +static void
       +removevacfile(void)
        {
       -        char *p;
       -
       -        if(oname == nil)
       -                abort();
       -        if((p = strrchr(oname, '/')) == nil)
       -                return oname;
       -        return p+1;
       +        if(vacfile)
       +                remove(vacfile);
        }
        
       -static void
       -vac(VtConn *z, char *argv[])
       +static int
       +strpcmp(const void *p0, const void *p1)
        {
       -        DirSink *dsink, *ds;
       -        MetaSink *ms;
       -        VtRoot root;
       -        uchar score[VtScoreSize], buf[VtRootSize];
       -        char cwd[2048];
       -        int cd;
       -        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 = vacfsopen(z, dfile, VtOREAD, 1000);
       -                if(fs == nil)
       -                        fprint(2, "could not open diff: %s: %r\n", dfile);
       -        }
       -                
       -
       -        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");
       -        if(oname)
       -                dir->name = lastelem(oname);
       -        else
       -                dir->name = "stdin";
       -
       -        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 = vacfileopen(fs, cp2);
       -                vacfile(dsink, argv[0], cp2, vff);
       -                if(vff)
       -                        vacfiledecref(vff);
       -                if(cd && chdir(cwd) < 0)
       -                        sysfatal("can't cd back to %s: %r\n", cwd);
       -        }
       -        
       -        if(isi) {
       -                vff = nil;
       -                if(fs)
       -                        vff = vacfileopen(fs, isi);
       -                vacstdin(dsink, isi, vff);
       -                if(vff)
       -                        vacfiledecref(vff);
       -        }
       -
       -        dirsinkclose(dsink);
       -
       -        /* build meta information for the root */
       -        ms = metasinkalloc(z, bsize, bsize);
       -        /* fake into a directory */
       -        dir->mode = DMDIR|0555;
       -        dir->qid.type |= QTDIR;
       -        plan9tovacdir(&vd, dir, 0, fileid++);
       -        if(strcmp(vd.elem, "/") == 0){
       -                vtfree(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));                
       -        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)
       -                vacfsgetscore(fs, root.prev);
       -
       -        metasinkfree(ms);
       -        dirsinkfree(ds);
       -        dirsinkfree(dsink);
       -        if(fs != nil)
       -                vacfsclose(fs);
       -        
       -        vtrootpack(&root, buf);
       -        if(vacwrite(z, score, VtRootType, buf, VtRootSize) < 0)
       -                sysfatal("vacwrite: %r");
       -
       -        fprint(fd, "vac:%V\n", score);
       -
       -        /* avoid remove at cleanup */
       -        oname = nil;
       +        return strcmp(*(char**)p0, *(char**)p1);
        }
        
        static int
       t@@ -448,897 +274,370 @@ isexcluded(char *name)
                return 0;
        }
        
       -static void
       -vacfile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
       +void
       +plan9tovacdir(VacDir *vd, Dir *dir)
        {
       -        int fd;
       -        Dir *dir;
       -        VacDir vd;
       -        ulong entry;
       +        memset(vd, 0, sizeof *vd);
        
       -        if(isexcluded(lname)) {
       -                warn("excluding: %s", lname);
       -                return;
       -        }
       +        vd->elem = dir->name;
       +        vd->uid = dir->uid;
       +        vd->gid = dir->gid;
       +        vd->mid = dir->muid;
       +        if(vd->mid == nil)
       +                vd->mid = "";
       +        vd->mtime = dir->mtime;
       +        vd->mcount = 0;
       +        vd->ctime = dir->mtime;                /* ctime: not available on plan 9 */
       +        vd->atime = dir->atime;
       +        vd->size = dir->length;
        
       -        if(merge && vacmerge(dsink, lname, sname) >= 0)
       -                return;
       +        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;
       +#ifdef PLAN9PORT
       +        if(dir->mode & DMDEVICE)
       +                vd->mode |= ModeDevice;
       +        if(dir->mode & DMNAMEDPIPE)
       +                vd->mode |= ModeNamedPipe;
       +        if(dir->mode & DMSYMLINK)
       +                vd->mode |= ModeLink;
       +#endif
        
       -        if((dir = dirstat(sname)) == nil){
       -                warn("could not stat file %s: %r", lname);
       -                return;
       -        }
       -        if(dir->mode&(DMSYMLINK|DMDEVICE|DMNAMEDPIPE)){
       -                vacspecial(dsink, dir, lname, sname, vf);
       -                free(dir);
       -                return;
       -        }else if(dir->mode&DMSOCKET){
       -                free(dir);
       -                return;
       -        }
       -        free(dir);
       +        vd->plan9 = 1;
       +        vd->p9path = dir->qid.path;
       +        vd->p9version = dir->qid.vers;
       +}
       +
       +#ifdef PLAN9PORT
       +enum {
       +        Special =
       +                DMSOCKET |
       +                DMSYMLINK |
       +                DMNAMEDPIPE |
       +                DMDEVICE
       +};
       +#endif
       +
       +/*
       + * Does block b of f have the same SHA1 hash as the n bytes at buf?
       + */
       +static int
       +sha1matches(VacFile *f, ulong b, uchar *buf, int n)
       +{
       +        uchar fscore[VtScoreSize];
       +        uchar bufscore[VtScoreSize];
                
       -        fd = open(sname, OREAD);
       -        if(fd < 0) {
       -                warn("could not open file: %s: %r", lname);
       +        if(vacfileblockscore(f, b, fscore) < 0)
       +                return 0;
       +        n = vtzerotruncate(VtDataType, buf, n);
       +        sha1(buf, n, bufscore, nil);
       +        if(memcmp(bufscore, fscore, VtScoreSize) == 0)
       +                return 1;
       +        return 0;
       +}
       +
       +/*
       + * Archive the file named name, which has stat info d,
       + * into the vac directory fp (p = parent).  
       + *
       + * If we're doing a vac -d against another archive, the
       + * equivalent directory to fp in that archive is diffp.
       + */
       +void
       +vac(VacFile *fp, VacFile *diffp, char *name, Dir *d)
       +{
       +        char *elem, *s;
       +        static char buf[65536];
       +        int fd, i, n, bsize;
       +        vlong off;
       +        Dir *dk;        // kids
       +        VacDir vd, vddiff;
       +        VacFile *f, *fdiff;
       +        VtEntry e;
       +
       +        if(isexcluded(name)){
       +                warn("excluding %s%s", name, (d->mode&DMDIR) ? "/" : "");
                        return;
                }
        
       +        if(d->mode&DMDIR)
       +                stats.ndir++;
       +        else
       +                stats.nfile++;
       +
       +        if(merge && vacmerge(fp, name) >= 0)
       +                return;
       +        
                if(verbose)
       -                fprint(2, "%s\n", lname);
       +                fprint(2, "%s%s\n", name, (d->mode&DMDIR) ? "/" : "");
        
       -        dir = dirfstat(fd);
       -        if(dir == nil) {
       -                warn("can't stat %s: %r", lname);
       -                close(fd);
       +#ifdef PLAN9PORT
       +        if(d->mode&Special)
       +                fd = -1;
       +        else
       +#endif
       +        if((fd = open(name, OREAD)) < 0){
       +                warn("open %s: %r", name);
                        return;
                }
       -        dir->name = lastelem(sname);
        
       -        entry = dsink->nentry;
       -
       -        if(dir->mode & DMDIR) 
       -                vacdir(dsink, fd, lname, sname, vf);
       +        elem = strrchr(name, '/');
       +        if(elem)
       +                elem++;
                else
       -                vacdata(dsink, fd, lname, vf, dir);
       +                elem = name;
        
       -        plan9tovacdir(&vd, dir, entry, fileid++);
       -        metasinkwritedir(dsink->msink, &vd);
       -        vdcleanup(&vd);
       +        plan9tovacdir(&vd, d);
       +        if((f = vacfilecreate(fp, elem, vd.mode)) == nil){
       +                warn("vacfilecreate %s: %r", name);
       +                return;
       +        }
       +        if(diffp)
       +                fdiff = vacfilewalk(diffp, elem);
       +        else
       +                fdiff = nil;
        
       -        free(dir);
       +        if(vacfilesetdir(f, &vd) < 0)
       +                warn("vacfilesetdir %s: %r", name);
       +        
       +#ifdef PLAN9PORT
       +        if(d->mode&(DMSOCKET|DMNAMEDPIPE)){
       +                /* don't write anything */
       +        }
       +        else if(d->mode&DMSYMLINK){
       +                n = readlink(name, buf, sizeof buf);
       +                if(n > 0 && vacfilewrite(f, buf, n, 0) < 0){
       +                        warn("venti write %s: %r", name);
       +                        goto Out;
       +                }
       +                stats.data += n;
       +        }else if(d->mode&DMDEVICE){
       +                snprint(buf, sizeof buf, "%c %d %d",
       +                        (char)((d->qid.path >> 16) & 0xFF),
       +                        (int)(d->qid.path & 0xFF),
       +                        (int)((d->qid.path >> 8) & 0xFF));
       +                if(vacfilewrite(f, buf, strlen(buf), 0) < 0){
       +                        warn("venti write %s: %r", name);
       +                        goto Out;
       +                }
       +                stats.data += strlen(buf);
       +        }else
       +#endif
       +        if(d->mode&DMDIR){
       +                while((n = dirread(fd, &dk)) > 0){
       +                        for(i=0; i<n; i++){
       +                                s = vtmalloc(strlen(name)+1+strlen(dk[i].name)+1);
       +                                strcpy(s, name);
       +                                strcat(s, "/");
       +                                strcat(s, dk[i].name);
       +                                vac(f, fdiff, s, &dk[i]);
       +                                free(s);
       +                        }
       +                        free(dk);
       +                }
       +        }else{
       +                off = 0;
       +                bsize = fs->bsize;
       +                if(fdiff){
       +                        /*
       +                         * Copy fdiff's contents into f by moving the score.
       +                         * We'll diff and update below.
       +                         */
       +                        if(vacfilegetentries(fdiff, &e, nil) >= 0)
       +                        if(vacfilesetentries(f, &e, nil) >= 0){
       +                                bsize = e.dsize;
       +                        
       +                                /*
       +                                 * Or if -q is set, and the metadata looks the same,
       +                                 * don't even bother reading the file.
       +                                 */
       +                                if(qdiff && vacfilegetdir(fdiff, &vddiff) >= 0){
       +                                        if(vddiff.mtime == vd.mtime)
       +                                        if(vddiff.size == vd.size)
       +                                        if(!vddiff.plan9 || (/* vddiff.p9path == vd.p9path && */ vddiff.p9version == vd.p9version)){
       +                                                stats.skipfiles++;
       +                                                stats.nfile--;
       +                                                vdcleanup(&vddiff);
       +                                                goto Out;
       +                                        }
       +                                        
       +                                        /*
       +                                         * Skip over presumably-unchanged prefix
       +                                         * of an append-only file.
       +                                         */
       +                                        if(vd.mode&ModeAppend)
       +                                        if(vddiff.size < vd.size)
       +                                        if(vddiff.plan9 && vd.plan9)
       +                                        if(vddiff.p9path == vd.p9path){
       +                                                off = vd.size/bsize*bsize;
       +                                                if(seek(fd, off, 0) >= 0)
       +                                                        stats.skipdata += off;
       +                                                else{
       +                                                        seek(fd, 0, 0);        // paranoia
       +                                                        off = 0;
       +                                                }
       +                                        }
       +
       +                                        vdcleanup(&vddiff);
       +                                        // XXX different verbose chatty prints for kaminsky?
       +                                }
       +                        }
       +                }
       +                if(qdiff && verbose)
       +                        fprint(2, "+%s\n", name);
       +                while((n = readn(fd, buf, bsize)) > 0){
       +                        if(fdiff && sha1matches(f, off/bsize, (uchar*)buf, n)){
       +                                off += n;
       +                                stats.skipdata += n;
       +                                continue;
       +                        }
       +                        if(vacfilewrite(f, buf, n, off) < 0){
       +                                warn("venti write %s: %r", name);
       +                                goto Out;
       +                        }
       +                        stats.data += n;
       +                        off += n;
       +                }
       +                /*
       +                 * Since we started with fdiff's contents,
       +                 * set the size in case fdiff was bigger.
       +                 */
       +                if(fdiff && vacfilesetsize(f, off) < 0)
       +                        warn("vtfilesetsize %s: %r", name);
       +        }
       +
       +Out:
       +        vacfileflush(f, 1);
       +        vacfiledecref(f);
       +        if(fdiff)
       +                vacfiledecref(fdiff);
                close(fd);
        }
        
       -static void
       -vacstdin(DirSink *dsink, char *name, VacFile *vf)
       +void
       +vacstdin(VacFile *fp, char *name)
        {
       -        Dir *dir;
       -        VacDir vd;
       -        ulong entry;
       -
       -        if(verbose)
       -                fprint(2, "%s\n", "<stdio>");
       +        vlong off;
       +        VacFile *f;
       +        static char buf[8192];
       +        int n;
        
       -        dir = dirfstat(0);
       -        if(dir == nil) {
       -                warn("can't stat <stdio>: %r");
       +        if((f = vacfilecreate(fp, name, 0666)) == nil){
       +                warn("vacfilecreate %s: %r", name);
                        return;
                }
       -        dir->name = "stdin";
       -
       -        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 int
       -sha1check(u8int *score, uchar *buf, int n)
       -{
       -        uchar score2[VtScoreSize];
       -
       -        sha1(buf, n, score2, nil);
       -        if(memcmp(score, score2, VtScoreSize) == 0)
       -                return 0;
       -        return -1;
       +        
       +        off = 0;
       +        while((n = read(0, buf, sizeof buf)) > 0){
       +                if(vacfilewrite(f, buf, n, off) < 0){
       +                        warn("venti write %s: %r", name);
       +                        vacfiledecref(f);
       +                        return;
       +                }
       +                off += n;
       +        }
       +        vacfileflush(f, 1);
       +        vacfiledecref(f);
        }
        
       -static ulong
       -vacdataskip(Sink *sink, VacFile *vf, int fd, ulong blocks, uchar *buf, char *lname)
       +/*
       + * fp is the directory we're writing.
       + * mp is the directory whose contents we're merging in.
       + * d is the directory entry of the file from mp that we want to add to fp.
       + * vacfile is the name of the .vac file, for error messages.
       + * offset is the qid that qid==0 in mp should correspond to.
       + * max is the maximum qid we expect to see (not really needed).
       + */
       +int
       +vacmergefile(VacFile *fp, VacFile *mp, VacDir *d, char *vacfile,
       +        vlong offset, vlong max)
        {
       -        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;
       +        VtEntry ed, em;
       +        VacFile *mf;
       +        VacFile *f;
       +        
       +        mf = vacfilewalk(mp, d->elem);
       +        if(mf == nil){
       +                warn("could not walk %s in %s", d->elem, vacfile);
       +                return -1;
                }
       -        n = readn(fd, buf, bsize);
       -        if(n < bsize) {
       -                warn("error checking append only file: %s", lname);
       -                goto Err;
       +        if(vacfilegetentries(mf, &ed, &em) < 0){
       +                warn("could not get entries for %s in %s", d->elem, vacfile);
       +                vacfiledecref(mf);
       +                return -1;
                }
       -        if(vacfileblockscore(vf, blocks-1, score)<0 || sha1check(score, buf, n)<0) {
       -                warn("last block of append file did not match: %s", lname);
       -                goto Err;
       +        
       +        if((f = vacfilecreate(fp, d->elem, d->mode)) == nil){
       +                warn("vacfilecreate %s: %r", d->elem);
       +                vacfiledecref(mf);
       +                return -1;
                }
       -
       -        for(i=0; i<blocks; i++) {
       -                if(vacfileblockscore(vf, i, score) < 0) {
       -                        warn("could not get score: %s: %lud", lname, i);
       -                        seek(fd, i*bsize, 0);
       -                        return i;
       -                }
       -                stats.skip++;
       -                sinkwritescore(sink, score, bsize);
       +        if(d->qidspace){
       +                d->qidoffset += offset;
       +                d->qidmax += offset;
       +        }else{
       +                d->qidspace = 1;
       +                d->qidoffset = offset;
       +                d->qidmax = max;
       +        }
       +        if(vacfilesetdir(f, d) < 0 || vacfilesetentries(f, &ed, &em) < 0){
       +                warn("vacmergefile %s: %r", d->elem);
       +                vacfiledecref(mf);
       +                vacfiledecref(f);
       +                return -1;
                }
       -
       -        return i;
       -Err:
       -        seek(fd, 0, 0);
       +        
       +        vacfiledecref(mf);
       +        vacfiledecref(f);
                return 0;
        }
        
       -static void
       -vacdata(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
       +int
       +vacmerge(VacFile *fp, char *name)
        {
       -        uchar *buf;
       -        Sink *sink;
       -        int n;
       -        uchar score[VtScoreSize];
       -        ulong block, same;
       +        VacFs *mfs;
                VacDir vd;
       -        ulong vfblocks;
       +        VacDirEnum *de;
       +        VacFile *mp;
       +        uvlong maxqid, offset;
        
       -        vfblocks = 0;
       -        if(vf != nil && qdiff) {
       -                vacfilegetdir(vf, &vd);
       -                if(vd.mtime == dir->mtime)
       -                if(vd.size == dir->length)
       -                if(!vd.plan9 || /* vd.p9path == dir->qid.path && */ vd.p9version == dir->qid.vers)
       -                if(dirsinkwritefile(dsink, vf) >= 0) {
       -                        stats.sfile++;
       -                        vdcleanup(&vd);
       -                        return;
       -                }
       -
       -                if(verbose)
       -                        fprint(2, "+ %s\n", lname);
       -
       -                /* 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 = vtmalloc(bsize);
       -        sink = sinkalloc(dsink->sink->z, bsize, bsize);
       -        block = 0;
       -        same = stats.sdata+stats.skip;
       -
       -        if(vfblocks > 1)
       -                block += vacdataskip(sink, vf, fd, vfblocks, buf, lname);
       -
       -        for(;;) {
       -                n = readn(fd, buf, bsize);
       -                if(0 && n < 0)
       -                        warn("file truncated due to read error: %s: %r", lname);
       -                if(n <= 0)
       -                        break;
       -                if(vf != nil && vacfileblockscore(vf, block, score)>=0 && sha1check(score, buf, n)>=0) {
       -                        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 = vtmalloc(strlen(lname) + strlen(name) + 2);
       -                        sn = vtmalloc(strlen(sname) + strlen(name) + 2);
       -                        strcpy(ln, lname);
       -                        strcat(ln, "/");
       -                        strcat(ln, name);
       -                        strcpy(sn, sname);
       -                        strcat(sn, "/");
       -                        strcat(sn, name);
       -                        if(vf != nil)
       -                                vvf = vacfilewalk(vf, name);
       -                        else
       -                                vvf = nil;
       -                        vacfile(ds, ln, sn, vvf);
       -                        if(vvf != nil)
       -                                vacfiledecref(vvf);
       -                        vtfree(ln);
       -                        vtfree(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(vacfileread(vf, buf, VtEntrySize, (uvlong)dir->entry*VtEntrySize) != VtEntrySize) {
       -                warn("could not read venti dir entry: %s\n", dir->elem);
       -                return -1;
       -        }
       -        vtentryunpack(&dd, buf, 0);
       -
       -        if(dir->mode & ModeDir)        {
       -                e = dir->mentry;
       -                if(e == 0)
       -                        e = dir->entry + 1;
       -                
       -                if(vacfileread(vf, buf, VtEntrySize, e*VtEntrySize) != VtEntrySize) {
       -                        warn("could not read venti dir entry: %s\n", dir->elem);
       -                        return 0;
       -                }
       -                vtentryunpack(&md, buf, 0);
       -        }
       -
       -        /* 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 0;
       -}
       -
       -static int
       -vacmerge(DirSink *dsink, char *lname, char *sname)
       -{
       -        char *p;
       -        VacFs *fs;
       -        VacFile *vf;
       -        VacDirEnum *d;
       -        VacDir dir;
       -        uvlong max;
       -
       -        if((p=strrchr(sname, '.')) == nil || strcmp(p, ".vac") != 0)
       +        if(strlen(name) < 4 || strcmp(name+strlen(name)-4, ".vac") != 0)
                        return -1;
       -
       -        d = nil;
       -        fs = vacfsopen(dsink->sink->z, sname, VtOREAD, 100);
       -        if(fs == nil)
       +        if((mfs = vacfsopen(z, name, VtOREAD, 100)) == nil)
                        return -1;
       -
       -        vf = vacfileopen(fs, "/");
       -        if(vf == nil)
       -                goto Done;
       -        max = vacfilegetid(vf);
       -        d = vdeopen(vf);
       -        if(d == nil)
       -                goto Done;
       -
                if(verbose)
       -                fprint(2, "merging: %s\n", lname);
       -
       -        if(maxbsize < fs->bsize)
       -                maxbsize = fs->bsize;
       -
       -        for(;;) {
       -                if(vderead(d, &dir) < 1)
       -                        break;
       -                vacmergefile(dsink, vf, &dir, fileid, &max);
       -                vdcleanup(&dir);        
       -        }
       -        fileid += max;
       -
       -Done:
       -        if(d != nil)
       -                vdeclose(d);
       -        if(vf != nil)
       -                vacfiledecref(vf);
       -        vacfsclose(fs);
       -        return 0;
       -}
       -
       -static int
       -vacspecial(DirSink *dsink, Dir* dir, char *lname, char *sname, VacFile *vf)
       -{
       -        char *btmp, *buf;
       -        int buflen, dtype, major, minor, n;
       -        ulong entry;
       -        Sink *sink;
       -        VacDir vd;
       -
       -        n = 0;
       -        buflen = 128;
       -        buf = malloc(buflen);
       -        if(buf == nil)
       -                return -1;
       -
       -        if(verbose)
       -                fprint(2, "%s\n", lname);
       -
       -        dir->name = lastelem(sname);
       -
       -        if(dir->mode & DMSYMLINK){
       -                while((n = readlink(sname, buf, buflen)) == buflen){
       -                        buflen *= 2;
       -                        btmp = vtrealloc(buf, buflen);
       -                        if(btmp == nil){
       -                                free(buf);
       -                                return -1;
       +                fprint(2, "merging %s\n", name);
       +
       +        de = vdeopen(fs->root);
       +        if(de){
       +                mp = vacfsgetroot(mfs);
       +                offset = 0;
       +                if(vacfsgetmaxqid(mfs, &maxqid) >= 0){
       +                        _vacfsnextqid(fs, &offset);
       +                        vacfsjumpqid(fs, maxqid+1);
       +                }
       +                while(vderead(de, &vd) > 0){
       +                        if(vd.qid > maxqid){
       +                                warn("vacmerge %s: maxqid=%lld but %s has %lld",
       +                                        name, maxqid, vd.elem, vd.qid);
       +                                vacfsjumpqid(fs, vd.qid - maxqid);
       +                                maxqid = vd.qid;
                                }
       -                        buf = btmp;
       +                        vacmergefile(fp, mp, &vd, name,
       +                                offset, maxqid);
       +                        vdcleanup(&vd);
                        }
       -                dir->mode &= ~DMDIR;
       -                dir->mode |= DMSYMLINK;
       -        }else if(dir->mode & DMDEVICE){
       -                dtype = (dir->qid.path >> 16) & 0xFF;
       -                minor = dir->qid.path & 0xff;
       -                major = (dir->qid.path >> 8) & 0xFF;
       -                n = snprint(buf, buflen, "%c %d %d", dtype, major, minor);
       +                vdeclose(de);
       +                vacfiledecref(mp);
                }
       -
       -        entry = dsink->nentry;
       -
       -        sink = sinkalloc(dsink->sink->z, bsize, bsize);
       -        sinkwrite(sink, (uchar*)buf, n);
       -        sinkclose(sink);
       -        dirsinkwritesink(dsink, sink);
       -        sinkfree(sink);
       -        free(buf);
       -        
       -        dir->name = lastelem(sname);
       -        dir->length = n;
       -        plan9tovacdir(&vd, dir, entry, fileid++);
       -        metasinkwritedir(dsink->msink, &vd);
       -        vdcleanup(&vd);
       -
       +        vacfsclose(mfs);
                return 0;
        }
        
       -
       -Sink *
       -sinkalloc(VtConn *z, int psize, int dsize)
       -{
       -        Sink *k;
       -        int i;
       -
       -        if(psize < 512 || psize > VtMaxLumpSize)
       -                sysfatal("sinkalloc: bad psize");
       -        if(dsize < 512 || dsize > VtMaxLumpSize)
       -                sysfatal("sinkalloc: bad psize");
       -
       -        psize = VtScoreSize*(psize/VtScoreSize);
       -
       -        k = vtmallocz(sizeof(Sink));
       -        k->z = z;
       -        k->dir.flags = VtEntryActive;
       -        k->dir.psize = psize;
       -        k->dir.dsize = dsize;
       -        k->buf = vtmallocz(VtPointerDepth*k->dir.psize + VtScoreSize);
       -        for(i=0; i<=VtPointerDepth; i++)
       -                k->pbuf[i] = k->buf + i*k->dir.psize;
       -        return k;
       -}
       -
       -void
       -sinkwritescore(Sink *k, uchar score[VtScoreSize], int n)
       -{
       -        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)
       -                        sysfatal("file too big");
       -                p = k->buf+i*d->psize;
       -                stats.meta++;
       -                if(vacwrite(k->z, k->pbuf[i+1], VtDataType+1+i, p, d->psize) < 0)
       -                        sysfatal("vacwrite failed: %r");
       -                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 == 0)
       -                return;
       -
       -        if(n > k->dir.dsize)
       -                sysfatal("sinkwrite: size too big");
       -
       -        if((k->dir.type&~VtTypeDepthMask) == VtDirType){
       -                type = VtDirType;
       -                stats.meta++;
       -        } else {
       -                type = VtDataType;
       -                stats.data++;
       -        }
       -        if(vacwrite(k->z, score, type, p, n) < 0)
       -                sysfatal("vacwrite: %r");
       -
       -        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, base;
       -        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;
       -
       -        base = kd->type&~VtTypeDepthMask;
       -        kd->type = base + 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], base+1+i, p, k->pbuf[i]-p) < 0)
       -                        sysfatal("vacwrite: %r");
       -                k->pbuf[i+1] += VtScoreSize;
       -        }
       -        memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize);
       -}
       -
       -void
       -sinkfree(Sink *k)
       -{
       -        vtfree(k->buf);
       -        vtfree(k);
       -}
       -
       -DirSink *
       -dirsinkalloc(VtConn *z, int psize, int dsize)
       -{
       -        DirSink *k;
       -        int ds;
       -
       -        ds = VtEntrySize*(dsize/VtEntrySize);
       -
       -        k = vtmallocz(sizeof(DirSink));
       -        k->sink = sinkalloc(z, psize, ds);
       -        k->sink->dir.type = VtDirType;
       -        k->msink = metasinkalloc(z, psize, dsize);
       -        k->buf = vtmalloc(ds);
       -        k->p = k->buf;
       -        k->ep = k->buf + ds;
       -        return k;
       -}
       -
       -void
       -dirsinkwrite(DirSink *k, VtEntry *dir)
       -{
       -        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(vacfilegetentries(vf, &dir, nil) < 0)
       -                return -1;
       -        dirsinkwrite(k, &dir);
       -        return 0;
       -}
       -
       -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);
       -        vtfree(k->buf);
       -        vtfree(k);
       -}
       -
       -MetaSink*
       -metasinkalloc(VtConn *z, int psize, int dsize)
       -{
       -        MetaSink *k;
       -
       -        k = vtmallocz(sizeof(MetaSink));
       -        k->sink = sinkalloc(z, psize, dsize);
       -        k->buf = vtmalloc(dsize);
       -        k->maxindex = dsize/100;        /* 100 byte entries seems reasonable */
       -        if(k->maxindex < 1)
       -                k->maxindex = 1;
       -        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(const void *p0, const void *p1)
       -{
       -        uchar *q0, *q1;
       -        int n0, n1, r;
       -
       -        /* name is first element of entry */
       -        q0 = (uchar*)p0;
       -        q0 = blockbase + (q0[0]<<8) + q0[1];
       -        n0 = (q0[6]<<8) + q0[7];
       -        q0 += 8;
       -
       -        q1 = (uchar*)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)
       -                sysfatal("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)
       -                sysfatal("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;
       -        if(dir->mode & DMDEVICE)
       -                vd->mode |= ModeDevice;
       -        if(dir->mode & DMNAMEDPIPE)
       -                vd->mode |= ModeNamedPipe;
       -        if(dir->mode & DMSYMLINK)
       -                vd->mode |= ModeLink;
       -
       -        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);
       -        vtfree(k->buf);
       -        vtfree(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
       t@@ -1364,3 +663,16 @@ unittoull(char *s)
                        return TWID64;
                return n;
        }
       +
       +static void
       +warn(char *fmt, ...)
       +{
       +        va_list arg;
       +
       +        va_start(arg, fmt);
       +        fprint(2, "vac: ");
       +        vfprint(2, fmt, arg);
       +        fprint(2, "\n");
       +        va_end(arg);
       +}
       +
 (DIR) diff --git a/src/cmd/vac/vac.h b/src/cmd/vac/vac.h
       t@@ -3,6 +3,11 @@ typedef struct VacDir VacDir;
        typedef struct VacFile VacFile;
        typedef struct VacDirEnum VacDirEnum;
        
       +#ifndef PLAN9PORT
       +#pragma incomplete VacFile
       +#pragma incomplete VacDirEnum
       +#endif
       +
        /*
         * Mode bits
         */
       t@@ -99,6 +104,8 @@ void                vacfsclose(VacFs *fs);
        int                vacfssync(VacFs *fs);
        int                vacfssnapshot(VacFs *fs, char *src, char *dst);
        int                vacfsgetscore(VacFs *fs, u8int *score);
       +int                vacfsgetmaxqid(VacFs*, uvlong*);
       +void                vacfsjumpqid(VacFs*, uvlong);
        
        VacFile *vacfsgetroot(VacFs *fs);
        VacFile        *vacfileopen(VacFs *fs, char *path);
       t@@ -127,7 +134,7 @@ int                vacfilesetentries(VacFile *f, VtEntry *e, VtEntry *me);
        
        void                vdcleanup(VacDir *dir);
        void                vdcopy(VacDir *dst, VacDir *src);
       -
       +int                vacfilesetqidspace(VacFile*, u64int, u64int);
        
        VacDirEnum        *vdeopen(VacFile*);
        int                        vderead(VacDirEnum*, VacDir *);
 (DIR) diff --git a/src/cmd/vac/vtdump.c b/src/cmd/vac/vtdump.c
       t@@ -1,372 +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;
       -VtRoot root;
       -int ver;
       -int cmp;
       -int all;
       -int find;
       -uchar fscore[VtScoreSize];
       -VtConn *z;
       -
       -int vtgetuint16(uchar *p);
       -ulong vtgetuint32(uchar *p);
       -uvlong vtgetuint48(uchar *p);
       -void usage(void);
       -void readroot(VtRoot*, uchar *score, char *file);
       -int dumpdir(Source*, int indent);
       -int timevtread(VtConn*, uchar*, int, void*, int);
       -
       -int mainstacksize = 512*1024;
       -
       -void
       -threadmain(int argc, char *argv[])
       -{
       -        char *host = nil, *pref;
       -        uchar score[VtScoreSize];
       -        Source source;
       -        char *p;
       -        int n;
       -        uchar buf[VtMaxLumpSize];
       -
       -        ARGBEGIN{
       -        case 'h':
       -                host = ARGF();
       -                break;
       -        case 'c':
       -                cmp++;
       -                break;
       -        case 'f':
       -                find++;
       -                p = ARGF();
       -                if(p == nil || vtparsescore(p, &pref, fscore) < 0 || !pref || strcmp(pref, "vac") != 0)
       -                        usage();
       -                break;
       -        case 'a':
       -                all = 1;
       -                break;
       -        }ARGEND
       -
       -        bout = vtmallocz(sizeof(Biobuf));
       -        Binit(bout, 1, OWRITE);
       -
       -        if(argc > 1)
       -                usage();
       -
       -        fmtinstall('V', vtscorefmt);
       -        fmtinstall('H', encodefmt);
       -
       -        z = vtdial(host);
       -        if(z == nil)
       -                sysfatal("could not connect to server: %r");
       -
       -        if(vtconnect(z) < 0)
       -                sysfatal("vtconnect: %r");
       -
       -        readroot(&root, score, argv[0]);
       -        bsize = root.blocksize;
       -        if(!find) {
       -                Bprint(bout, "score: %V\n", score);
       -                Bprint(bout, "name: %s\n", root.name);
       -                Bprint(bout, "type: %s\n", root.type);
       -                Bprint(bout, "bsize: %d\n", bsize);
       -                Bprint(bout, "prev: %V\n", root.prev);
       -        }
       -
       -fprint(2, "read...\n");
       -        n = timevtread(z, root.score, VtDirType, buf, bsize);
       -        if(n < 0)
       -                sysfatal("could not read root dir");
       -
       -fprint(2, "...\n");
       -        /* fake up top level source */
       -        memset(&source, 0, sizeof(source));
       -        memmove(source.score, root.score, VtScoreSize);
       -        source.psize = bsize;
       -        source.dsize = bsize;
       -        source.dir = 1;
       -        source.active = 1;
       -        source.depth = 0;
       -        source.size = n;
       -
       -        dumpdir(&source, 0);
       -
       -        Bterm(bout);
       -
       -        vthangup(z);
       -        threadexitsall(0);
       -}
       -
       -void
       -sourceprint(Source *s, int indent, int entry)
       -{
       -        int i;
       -        uvlong size;
       -        int ne;
       -
       -        for(i=0; i<indent; i++)
       -                Bprint(bout, " ");
       -        Bprint(bout, "%4d", entry);
       -        if(s->active) {
       -                /* dir size in directory entries */
       -                if(s->dir) {
       -                        ne = s->dsize/VtEntrySize;
       -                        size = ne*(s->size/s->dsize) + (s->size%s->dsize)/VtEntrySize;
       -                } else 
       -                        size = s->size;
       -                if(cmp) {
       -                        Bprint(bout, ": gen: %lud size: %llud",
       -                                s->gen, size);
       -                        if(!s->dir)
       -                                Bprint(bout, ": %V", s->score);
       -                } else {
       -                        Bprint(bout, ": gen: %lud psize: %d dsize: %d",
       -                                s->gen, s->psize, s->dsize);
       -                        Bprint(bout, " depth: %d size: %llud: %V",
       -                                s->depth, size, s->score);
       -                }
       -                
       -                if(s->reserved)
       -                        Bprint(bout, ": reserved not emtpy");
       -        }
       -        Bprint(bout, "\n");
       -}
       -
       -int
       -parse(Source *s, uchar *p)
       -{
       -        VtEntry dir;
       -
       -        memset(s, 0, sizeof(*s));
       -        if(vtentryunpack(&dir, p, 0) < 0)
       -                return -1;
       -
       -        if(!(dir.flags & VtEntryActive))
       -                return 0;
       -
       -        s->active = 1;
       -        s->gen = dir.gen;
       -        s->psize = dir.psize;
       -        s->dsize = dir.dsize;
       -        s->size = dir.size;
       -        memmove(s->score, dir.score, VtScoreSize);
       -        if((dir.type&~VtTypeDepthMask) == VtDirType)
       -                s->dir = 1;
       -        s->depth = dir.type&VtTypeDepthMask;
       -        return 0;
       -}
       -
       -int
       -sourceread(Source *s, ulong block, uchar *p, int n)
       -{
       -        uchar *buf;
       -        uchar score[VtScoreSize];
       -        int i, nn, np, type;
       -        int elem[VtPointerDepth];
       -
       -        buf = vtmalloc(VtMaxLumpSize);
       -
       -        memmove(score, s->score, VtScoreSize);
       -
       -        np = s->psize/VtScoreSize;
       -        for(i=0; i<s->depth; i++) {
       -                elem[i] = block % np;
       -                block /= np;
       -        }
       -        assert(block == 0);
       -
       -        for(i=s->depth-1; i>=0; i--) {
       -                nn = timevtread(z, score, (s->dir ? VtDirType : VtDataType)+1+i, buf, s->psize);
       -                if(nn < 0){
       -fprint(2, "vtread %V %d: %r\n", score, (s->dir ? VtDirType : VtDataType)+1+i);
       -                        free(buf);
       -                        return -1;
       -                }
       -
       -                if((elem[i]+1)*VtScoreSize > nn){
       -                        free(buf);
       -                        return 0;
       -                }
       -                memmove(score, buf + elem[i]*VtScoreSize, VtScoreSize);
       -        }
       -
       -        if(s->dir)
       -                type = VtDirType;
       -        else
       -                type = VtDataType;
       -
       -        nn = timevtread(z, score, type, p, n);
       -        if(nn < 0){
       -fprint(2, "vtread %V %d: %r\n", score, type);
       -abort();
       -                free(buf);
       -                return -1;
       -        }
       -        
       -        free(buf);
       -        return nn;
       -}
       -
       -void
       -dumpfilecontents(Source *s)
       -{
       -        int nb, lb, i, n;
       -        uchar buf[VtMaxLumpSize];
       -
       -        nb = (s->size + s->dsize - 1)/s->dsize;
       -        lb = s->size%s->dsize;
       -        for(i=0; i<nb; i++) {
       -                memset(buf, 0, s->dsize);
       -                n = sourceread(s, i, buf, s->dsize);
       -                if(n < 0) {        
       -                        fprint(2, "could not read block: %d: %r\n", i);
       -                        continue;
       -                }
       -                if(i < nb-1)
       -                        Bwrite(bout, buf, s->dsize);
       -                else
       -                        Bwrite(bout, buf, lb);
       -        }
       -}
       -
       -void
       -dumpfile(Source *s, int indent)
       -{
       -        int nb, i, j, n;
       -        uchar *buf;
       -        uchar score[VtScoreSize];
       -
       -        buf = vtmalloc(VtMaxLumpSize);
       -        nb = (s->size + s->dsize - 1)/s->dsize;
       -        for(i=0; i<nb; i++) {
       -                memset(buf, 0, s->dsize);
       -                n = sourceread(s, i, buf, s->dsize);
       -                if(n < 0) {        
       -                        fprint(2, "could not read block: %d: %r\n", i);
       -                        continue;
       -                }
       -                for(j=0; j<indent; j++)
       -                        Bprint(bout, " ");
       -                sha1(buf, n, score, nil);                
       -                Bprint(bout, "%4d: size: %ud: %V\n", i, n, score);
       -        }
       -        vtfree(buf);
       -}
       -
       -int
       -dumpdir(Source *s, int indent)
       -{
       -        int pb, ne, nb, i, j, n, entry;
       -        Source ss;
       -        uchar *buf;
       -
       -        buf = vtmalloc(VtMaxLumpSize);
       -        pb = s->dsize/VtEntrySize;
       -        ne = pb*(s->size/s->dsize) + (s->size%s->dsize)/VtEntrySize;
       -        nb = (s->size + s->dsize - 1)/s->dsize;
       -        for(i=0; i<nb; i++) {
       -                memset(buf, 0, s->dsize);
       -                n = sourceread(s, i, buf, s->dsize);
       -                if(n < 0) {        
       -                        fprint(2, "could not read block: %d: %r\n", i);
       -                        continue;
       -                }
       -                for(j=0; j<pb; j++) {
       -                        entry = i*pb + j;
       -                        if(entry >= ne)
       -                                break;
       -                        parse(&ss, buf + j * VtEntrySize);
       -
       -                        if(!find)
       -                                sourceprint(&ss, indent, entry);
       -                        else if(memcmp(ss.score, fscore, VtScoreSize) == 0) {
       -                                dumpfilecontents(&ss);
       -                                free(buf);
       -                                return -1;
       -                        }
       -
       -                        if(ss.dir) {
       -                                if(dumpdir(&ss, indent+1) < 0){
       -                                        free(buf);
       -                                        return -1;
       -                                }
       -                        } else if(all)
       -                                dumpfile(&ss, indent+1);
       -                }
       -        }
       -        free(buf);
       -        return 0;
       -}
       -
       -void
       -usage(void)
       -{
       -        fprint(2, "%s: [file]\n", argv0);
       -        threadexits("usage");
       -}
       -
       -void
       -readroot(VtRoot *root, uchar *score, char *file)
       -{
       -        int fd;
       -        char *pref;
       -        char buf[VtRootSize];
       -        int n, nn;
       -
       -        if(file == 0)
       -                fd = 0;
       -        else {
       -                fd = open(file, OREAD);
       -                if(fd < 0)
       -                        sysfatal("could not open file: %s: %r\n", file);
       -        }
       -        n = readn(fd, buf, sizeof(buf)-1);
       -        if(n < 0)
       -                sysfatal("read failed: %r\n");
       -        if(n==0 || buf[n-1] != '\n')
       -                sysfatal("not a root file");
       -        buf[n-1] = 0;
       -        close(fd);
       -
       -        if(vtparsescore(buf, &pref, score) < 0){
       -                sysfatal("not a root file");
       -        }
       -        nn = timevtread(z, score, VtRootType, buf, VtRootSize);
       -        if(nn < 0)
       -                sysfatal("cannot read root %V", score);
       -        if(vtrootunpack(root, buf) < 0)
       -                sysfatal("cannot parse root: %r");
       -}
       -
       -int
       -timevtread(VtConn *z, uchar *score, int type, void *buf, int nbuf)
       -{
       -/*
       -        ulong t0, t1;
       -        int n;
       -
       -        t0 = nsec();
       -        n = vtread(z, score, type, buf, nbuf);
       -        t1 = nsec();
       -        fprint(2, "read %V: %.6f seconds\n", score, (t1-t0)/1.e9);
       -        return n;
       -*/
       -        return vtread(z, score, type, buf, nbuf);
       -}