diff options
author | Russ Cox <rsc@swtch.com> | 2008-06-15 01:19:37 -0400 |
---|---|---|
committer | Russ Cox <rsc@swtch.com> | 2008-06-15 01:19:37 -0400 |
commit | 003c13aa185cebc8797b8b04e8bd0722643467aa (patch) | |
tree | 248ed1d3e36efaf6e4bff072e460800137b8dfc2 /src/cmd/vac | |
parent | ada3d479a7d132a0da51a2cca3e9354bf2e7661c (diff) | |
download | plan9port-003c13aa185cebc8797b8b04e8bd0722643467aa.tar.gz plan9port-003c13aa185cebc8797b8b04e8bd0722643467aa.tar.bz2 plan9port-003c13aa185cebc8797b8b04e8bd0722643467aa.zip |
vac: make qids unique
Diffstat (limited to 'src/cmd/vac')
-rw-r--r-- | src/cmd/vac/file.c | 49 | ||||
-rw-r--r-- | src/cmd/vac/fns.h | 1 | ||||
-rw-r--r-- | src/cmd/vac/mkfile | 4 | ||||
-rw-r--r-- | src/cmd/vac/pack.c | 2 | ||||
-rw-r--r-- | src/cmd/vac/vac.c | 1684 | ||||
-rw-r--r-- | src/cmd/vac/vac.h | 9 | ||||
-rw-r--r-- | src/cmd/vac/vtdump.c | 372 |
7 files changed, 557 insertions, 1564 deletions
diff --git a/src/cmd/vac/file.c b/src/cmd/vac/file.c index 53cc95e3..fac419b1 100644 --- a/src/cmd/vac/file.c +++ b/src/cmd/vac/file.c @@ -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* @@ -140,7 +142,7 @@ uvlong vacfilegetid(VacFile *f) { /* immutable */ - return f->dir.qid; + return f->qidoffset + f->dir.qid; } ulong @@ -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; @@ -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); @@ -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); @@ -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; @@ -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); @@ -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) @@ -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) diff --git a/src/cmd/vac/fns.h b/src/cmd/vac/fns.h index 27cd479f..3cf87840 100644 --- a/src/cmd/vac/fns.h +++ b/src/cmd/vac/fns.h @@ -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); diff --git a/src/cmd/vac/mkfile b/src/cmd/vac/mkfile index 224ab882..98e122b8 100644 --- a/src/cmd/vac/mkfile +++ b/src/cmd/vac/mkfile @@ -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 diff --git a/src/cmd/vac/pack.c b/src/cmd/vac/pack.c index cb92fad8..9777464f 100644 --- a/src/cmd/vac/pack.c +++ b/src/cmd/vac/pack.c @@ -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); diff --git a/src/cmd/vac/vac.c b/src/cmd/vac/vac.c index 71244f43..c1508549 100644 --- a/src/cmd/vac/vac.c +++ b/src/cmd/vac/vac.c @@ -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 */ -}; +// TODO: qids -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); - -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) @@ -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++; @@ -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); + 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; } - n = dirreadall(fd, &d); - close(fd); - if(n < 0){ - warn("could not read %s: %r", *argv); - continue; + 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); } - 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); + close(fd); + continue; + } + if((d = dirstat(argv[i])) == nil){ + warn("stat %s: %r", argv[i]); continue; } - nargv = vtrealloc(nargv, (nargc+1)*sizeof nargv[0]); - nargv[nargc++] = *argv; + 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 @@ -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); - - fd = open(sname, OREAD); - if(fd < 0) { - warn("could not open file: %s: %r", lname); - return; - } + vd->plan9 = 1; + vd->p9path = dir->qid.path; + vd->p9version = dir->qid.vers; +} - if(verbose) - fprint(2, "%s\n", lname); +#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]; + + 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; +} - dir = dirfstat(fd); - if(dir == nil) { - warn("can't stat %s: %r", lname); - close(fd); +/* + * 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; } - dir->name = lastelem(sname); - - entry = dsink->nentry; - if(dir->mode & DMDIR) - vacdir(dsink, fd, lname, sname, vf); + if(d->mode&DMDIR) + stats.ndir++; 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; + stats.nfile++; + if(merge && vacmerge(fp, name) >= 0) + return; + if(verbose) - fprint(2, "%s\n", "<stdio>"); + fprint(2, "%s%s\n", name, (d->mode&DMDIR) ? "/" : ""); - dir = dirfstat(0); - if(dir == nil) { - warn("can't stat <stdio>: %r"); +#ifdef PLAN9PORT + if(d->mode&Special) + fd = -1; + else +#endif + if((fd = open(name, OREAD)) < 0){ + warn("open %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; -} - -static ulong -vacdataskip(Sink *sink, VacFile *vf, int fd, ulong blocks, uchar *buf, char *lname) -{ - int n; - ulong i; - uchar score[VtScoreSize]; + elem = strrchr(name, '/'); + if(elem) + elem++; + else + elem = name; - /* skip blocks for append only files */ - if(seek(fd, (blocks-1)*bsize, 0) != (blocks-1)*bsize) { - warn("error seeking: %s", lname); - goto Err; - } - n = readn(fd, buf, bsize); - if(n < bsize) { - warn("error checking append only file: %s", lname); - goto Err; - } - 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; + 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; - 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; + 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.skip++; - sinkwritescore(sink, score, bsize); - } - - return i; -Err: - seek(fd, 0, 0); - return 0; + 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 -vacdata(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir) +void +vacstdin(VacFile *fp, char *name) { - uchar *buf; - Sink *sink; + vlong off; + VacFile *f; + static char buf[8192]; int n; - uchar score[VtScoreSize]; - ulong block, same; - VacDir vd; - ulong vfblocks; - - 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++; + if((f = vacfilecreate(fp, name, 0666)) == nil){ + warn("vacfilecreate %s: %r", name); + return; } - 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); + + 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; } - free(dirs); + off += n; } - dirsinkclose(ds); - dirsinkwritesink(dsink, ds->sink); - dirsinkwritesink(dsink, ds->msink->sink); - dirsinkfree(ds); + vacfileflush(f, 1); + vacfiledecref(f); } -static int -vacmergefile(DirSink *dsink, VacFile *vf, VacDir *dir, uvlong offset, uvlong *max) +/* + * 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) { - 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); + 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; } - 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; + if(vacfilegetentries(mf, &ed, &em) < 0){ + warn("could not get entries for %s in %s", d->elem, vacfile); + vacfiledecref(mf); + return -1; } - - 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((f = vacfilecreate(fp, d->elem, d->mode)) == nil){ + warn("vacfilecreate %s: %r", d->elem); + vacfiledecref(mf); return -1; - - d = nil; - fs = vacfsopen(dsink->sink->z, sname, VtOREAD, 100); - if(fs == nil) + } + 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; - - 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); + + vacfiledecref(mf); + vacfiledecref(f); return 0; } -static int -vacspecial(DirSink *dsink, Dir* dir, char *lname, char *sname, VacFile *vf) +int +vacmerge(VacFile *fp, char *name) { - char *btmp, *buf; - int buflen, dtype, major, minor, n; - ulong entry; - Sink *sink; + VacFs *mfs; VacDir vd; + VacDirEnum *de; + VacFile *mp; + uvlong maxqid, offset; - n = 0; - buflen = 128; - buf = malloc(buflen); - if(buf == nil) + if(strlen(name) < 4 || strcmp(name+strlen(name)-4, ".vac") != 0) + return -1; + if((mfs = vacfsopen(z, name, VtOREAD, 100)) == 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); - } - - 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); - - 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; + vdeclose(de); + vacfiledecref(mp); } - - /* 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); + vacfsclose(mfs); 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 @@ -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); +} + diff --git a/src/cmd/vac/vac.h b/src/cmd/vac/vac.h index 5ce69734..610028f4 100644 --- a/src/cmd/vac/vac.h +++ b/src/cmd/vac/vac.h @@ -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 */ @@ -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); @@ -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 *); diff --git a/src/cmd/vac/vtdump.c b/src/cmd/vac/vtdump.c deleted file mode 100644 index 2daaf1bb..00000000 --- a/src/cmd/vac/vtdump.c +++ /dev/null @@ -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); -} |