12 fprint(2, "usage: vac [-b blocksize] [-h host] file\n");
16 main(int argc, char *argv[])
22 bsize = unittoull(EARGF(usage()));
25 host = EARGF(usage());
31 if(bsize > VtMaxLumpSize)
32 bsize = VtMaxLumpSize;
37 fmtinstall('V', vtScoreFmt);
38 fmtinstall('R', vtErrFmt);
42 vtFatal("could not connect to server: %R");
45 vtFatal("vtConnect: %R");
47 qsort(exclude, nexclude, sizeof(char*), strpCmp);
51 fprint(2, "warning: could not ask server to flush pending writes: %R\n");
54 fprint(2, "files %ld:%ld data %ld:%ld:%ld meta %ld\n", stats.file, stats.sfile,
55 stats.data, stats.skip, stats.sdata, stats.meta);
64 vac(VtSession *z, char *argv[])
69 uchar score[VtScoreSize], buf[VtRootSize];
79 if(getwd(cwd, sizeof(cwd)) == 0)
80 sysfatal("can't find current directory: %r\n");
82 dsink = dirSinkAlloc(z, bsize, bsize);
86 fs = vfsOpen(z, dfile, 1, 10000);
88 fprint(2, "could not open diff: %s: %s\n", dfile, vtGetError());
93 fd = create(oname, OWRITE, 0666);
95 sysfatal("could not create file: %s: %r", oname);
101 sysfatal("dirfstat failed: %r");
103 for(; *argv; argv++) {
106 for (cp = *argv; *cp; cp++)
118 vff = vfOpen(fs, cp2);
119 vacFile(dsink, argv[0], cp2, vff);
122 if(cd && chdir(cwd) < 0)
123 sysfatal("can't cd back to %s: %r\n", cwd);
129 vff = vfOpen(fs, isi);
130 vacStdin(dsink, isi, vff);
137 /* build meta information for the root */
138 ms = metaSinkAlloc(z, bsize, bsize);
139 /* fake into a directory */
140 dir->mode |= (dir->mode&0444)>>2;
141 dir->qid.type |= QTDIR;
143 plan9ToVacDir(&vd, dir, 0, fileid++);
144 if(strcmp(vd.elem, "/") == 0){
146 vd.elem = vtStrDup("root");
148 metaSinkWriteDir(ms, &vd);
152 ds = dirSinkAlloc(z, bsize, bsize);
153 dirSinkWriteSink(ds, dsink->sink);
154 dirSinkWriteSink(ds, dsink->msink->sink);
155 dirSinkWriteSink(ds, ms->sink);
158 memset(&root, 0, sizeof(root));
159 root.version = VtRootVersion;
160 strncpy(root.name, dir->name, sizeof(root.name));
161 root.name[sizeof(root.name)-1] = 0;
163 sprint(root.type, "vac");
164 memmove(root.score, ds->sink->dir.score, VtScoreSize);
165 root.blockSize = maxbsize;
167 vfsGetScore(fs, root.prev);
175 vtRootPack(&root, buf);
176 if(!vacWrite(z, score, VtRootType, buf, VtRootSize))
177 vtFatal("vacWrite failed: %s", vtGetError());
180 for(i=0; i<VtScoreSize; i++)
181 fprint(fd, "%.2x", score[i]);
184 /* avoid remove at cleanup */
190 isExcluded(char *name)
198 x = strcmp(exclude[i], name);
210 vacFile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
217 if(isExcluded(lname)) {
218 warn("excluding: %s", lname);
222 if(merge && vacMerge(dsink, lname, sname))
225 fd = open(sname, OREAD);
227 warn("could not open file: %s: %s", lname, vtOSError());
232 fprint(2, "%s\n", lname);
236 warn("can't stat %s: %r", lname);
241 entry = dsink->nentry;
243 if(dir->mode & DMDIR)
244 vacDir(dsink, fd, lname, sname, vf);
246 vacData(dsink, fd, lname, vf, dir);
248 plan9ToVacDir(&vd, dir, entry, fileid++);
249 metaSinkWriteDir(dsink->msink, &vd);
257 vacStdin(DirSink *dsink, char *name, VacFile *vf)
264 fprint(2, "%s\n", "<stdio>");
268 warn("can't stat <stdio>: %r");
272 entry = dsink->nentry;
274 vacData(dsink, 0, "<stdin>", vf, dir);
276 plan9ToVacDir(&vd, dir, entry, fileid++);
277 vd.elem = vtStrDup(name);
278 metaSinkWriteDir(dsink->msink, &vd);
285 vacDataSkip(Sink *sink, VacFile *vf, int fd, ulong blocks, uchar *buf, char *lname)
289 uchar score[VtScoreSize];
291 /* skip blocks for append only files */
292 if(seek(fd, (blocks-1)*bsize, 0) != (blocks-1)*bsize) {
293 warn("error seeking: %s", lname);
296 n = readBlock(fd, buf, bsize);
298 warn("error checking append only file: %s", lname);
301 if(!vfGetBlockScore(vf, blocks-1, score) || !vtSha1Check(score, buf, n)) {
302 warn("last block of append file did not match: %s", lname);
306 for(i=0; i<blocks; i++) {
307 if(!vfGetBlockScore(vf, i, score)) {
308 warn("could not get score: %s: %lud", lname, i);
309 seek(fd, i*bsize, 0);
313 sinkWriteScore(sink, score, bsize);
323 vacData(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
328 uchar score[VtScoreSize];
334 if(vf != nil && qdiff) {
336 if(vd.mtime == dir->mtime)
337 if(vd.size == dir->length)
338 if(!vd.plan9 || /* vd.p9path == dir->qid.path && */ vd.p9version == dir->qid.vers)
339 if(dirSinkWriteFile(dsink, vf)) {
345 /* look for an append only file */
346 if((dir->mode&DMAPPEND) != 0)
347 if(vd.size < dir->length)
349 if(vd.p9path == dir->qid.path)
350 vfblocks = vd.size/bsize;
356 buf = vtMemAlloc(bsize);
357 sink = sinkAlloc(dsink->sink->z, bsize, bsize);
359 same = stats.sdata+stats.skip;
362 block += vacDataSkip(sink, vf, fd, vfblocks, buf, lname);
364 if(0) fprint(2, "vacData: %s: %ld\n", lname, block);
366 n = readBlock(fd, buf, bsize);
368 warn("file truncated due to read error: %s: %s", lname, vtOSError());
371 if(vf != nil && vfGetBlockScore(vf, block, score) && vtSha1Check(score, buf, n)) {
373 sinkWriteScore(sink, score, n);
375 sinkWrite(sink, buf, n);
378 same = stats.sdata+stats.skip - same;
380 if(same && (dir->mode&DMAPPEND) != 0)
381 if(0)fprint(2, "%s: total %lud same %lud:%lud diff %lud\n",
382 lname, block, same, vfblocks, block-same);
385 dirSinkWriteSink(dsink, sink);
392 vacDir(DirSink *dsink, int fd, char *lname, char *sname, VacFile *vf)
401 ds = dirSinkAlloc(dsink->sink->z, bsize, bsize);
402 while((nd = dirread(fd, &dirs)) > 0){
403 for(i = 0; i < nd; i++){
405 /* check for bad file names */
406 if(name[0] == 0 || strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
408 ln = vtMemAlloc(strlen(lname) + strlen(name) + 2);
409 sn = vtMemAlloc(strlen(sname) + strlen(name) + 2);
410 sprint(ln, "%s/%s", lname, name);
411 sprint(sn, "%s/%s", sname, name);
413 vvf = vfWalk(vf, name);
416 vacFile(ds, ln, sn, vvf);
425 dirSinkWriteSink(dsink, ds->sink);
426 dirSinkWriteSink(dsink, ds->msink->sink);
431 vacMergeFile(DirSink *dsink, VacFile *vf, VacDir *dir, uvlong offset, uvlong *max)
433 uchar buf[VtEntrySize];
437 if(vfRead(vf, buf, VtEntrySize, (uvlong)dir->entry*VtEntrySize) != VtEntrySize) {
438 warn("could not read venti dir entry: %s\n", dir->elem);
441 vtEntryUnpack(&dd, buf, 0);
443 if(dir->mode & ModeDir) {
448 if(vfRead(vf, buf, VtEntrySize, e*VtEntrySize) != VtEntrySize) {
449 warn("could not read venti dir entry: %s\n", dir->elem);
452 vtEntryUnpack(&md, buf, 0);
455 /* max might incorrect in some old dumps */
456 if(dir->qid >= *max) {
457 warn("qid out of range: %s", dir->elem);
462 dir->entry = dsink->nentry;
465 dir->qidOffset += offset;
468 dir->qidOffset = offset;
472 dirSinkWrite(dsink, &dd);
473 if(dir->mode & ModeDir)
474 dirSinkWrite(dsink, &md);
475 metaSinkWriteDir(dsink->msink, dir);
481 vacMerge(DirSink *dsink, char *lname, char *sname)
490 p = strrchr(sname, '.');
491 if(p == 0 || strcmp(p, ".vac"))
495 fs = vfsOpen(dsink->sink->z, sname, 1, 100);
499 vf = vfOpen(fs, "/");
503 d = vdeOpen(fs, "/");
508 fprint(2, "merging: %s\n", lname);
510 if(maxbsize < vfsGetBlockSize(fs))
511 maxbsize = vfsGetBlockSize(fs);
514 if(vdeRead(d, &dir, 1) < 1)
516 vacMergeFile(dsink, vf, &dir, fileid, &max);
531 sinkAlloc(VtSession *z, int psize, int dsize)
536 if(psize < 512 || psize > VtMaxLumpSize)
537 vtFatal("sinkAlloc: bad psize");
538 if(dsize < 512 || dsize > VtMaxLumpSize)
539 vtFatal("sinkAlloc: bad psize");
541 psize = VtScoreSize*(psize/VtScoreSize);
543 k = vtMemAllocZ(sizeof(Sink));
545 k->dir.flags = VtEntryActive;
546 k->dir.psize = psize;
547 k->dir.dsize = dsize;
548 k->buf = vtMemAllocZ(VtPointerDepth*k->dir.psize + VtScoreSize);
549 for(i=0; i<=VtPointerDepth; i++)
550 k->pbuf[i] = k->buf + i*k->dir.psize;
555 sinkWriteScore(Sink *k, uchar score[VtScoreSize], int n)
561 memmove(k->pbuf[0], score, VtScoreSize);
565 for(i=0; i<VtPointerDepth; i++) {
566 k->pbuf[i] += VtScoreSize;
567 if(k->pbuf[i] < k->buf + d->psize*(i+1))
569 if(i == VtPointerDepth-1)
570 vtFatal("file too big");
571 p = k->buf+i*d->psize;
573 if(!vacWrite(k->z, k->pbuf[i+1], VtPointerType0+i, p, d->psize))
574 vtFatal("vacWrite failed: %s", vtGetError());
578 /* round size up to multiple of dsize */
579 d->size = d->dsize * ((d->size + d->dsize-1)/d->dsize);
585 sinkWrite(Sink *k, uchar *p, int n)
588 uchar score[VtScoreSize];
591 vtFatal("sinkWrite: size too big");
593 if(k->dir.flags & VtEntryDir) {
600 if(!vacWrite(k->z, score, type, p, n))
601 vtFatal("vacWrite failed: %s", vtGetError());
603 sinkWriteScore(k, score, n);
607 sizeToDepth(uvlong s, int psize, int dsize)
612 /* determine pointer depth */
613 np = psize/VtScoreSize;
614 s = (s + dsize - 1)/dsize;
615 for(d = 0; s > 1; d++)
631 memmove(kd->score, vtZeroScore, VtScoreSize);
635 for(n=VtPointerDepth-1; n>0; n--)
636 if(k->pbuf[n] > k->buf + kd->psize*n)
639 kd->depth = sizeToDepth(kd->size, kd->psize, kd->dsize);
641 /* skip full part of tree */
642 for(i=0; i<n && k->pbuf[i] == k->buf + kd->psize*i; i++)
645 /* is the tree completely full */
646 if(i == n && k->pbuf[n] == k->buf + kd->psize*n + VtScoreSize) {
647 memmove(kd->score, k->pbuf[n] - VtScoreSize, VtScoreSize);
652 /* clean up the edge */
654 p = k->buf+i*kd->psize;
656 if(!vacWrite(k->z, k->pbuf[i+1], VtPointerType0+i, p, k->pbuf[i]-p))
657 vtFatal("vacWrite failed: %s", vtGetError());
658 k->pbuf[i+1] += VtScoreSize;
660 memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize);
671 dirSinkAlloc(VtSession *z, int psize, int dsize)
676 ds = VtEntrySize*(dsize/VtEntrySize);
678 k = vtMemAllocZ(sizeof(DirSink));
679 k->sink = sinkAlloc(z, psize, ds);
680 k->sink->dir.flags |= VtEntryDir;
681 k->msink = metaSinkAlloc(z, psize, dsize);
682 k->buf = vtMemAlloc(ds);
689 dirSinkWrite(DirSink *k, VtEntry *dir)
691 if(k->p + VtEntrySize > k->ep) {
692 sinkWrite(k->sink, k->buf, k->p - k->buf);
695 vtEntryPack(dir, k->p, 0);
701 dirSinkWriteSink(DirSink *k, Sink *sink)
703 dirSinkWrite(k, &sink->dir);
707 dirSinkWriteFile(DirSink *k, VacFile *vf)
711 if(!vfGetVtEntry(vf, &dir))
713 dirSinkWrite(k, &dir);
718 dirSinkClose(DirSink *k)
720 metaSinkClose(k->msink);
722 sinkWrite(k->sink, k->buf, k->p - k->buf);
727 dirSinkFree(DirSink *k)
730 metaSinkFree(k->msink);
736 metaSinkAlloc(VtSession *z, int psize, int dsize)
740 k = vtMemAllocZ(sizeof(MetaSink));
741 k->sink = sinkAlloc(z, psize, dsize);
742 k->buf = vtMemAlloc(dsize);
743 k->maxindex = dsize/100; /* 100 byte entries seems reasonable */
746 k->rp = k->p = k->buf + MetaHeaderSize + k->maxindex*MetaIndexSize;
747 k->ep = k->buf + dsize;
751 /* hack to get base to compare routine - not reentrant */
755 dirCmp(void *p0, void *p1)
760 /* name is first element of entry */
762 q0 = blockBase + (q0[0]<<8) + q0[1];
763 n0 = (q0[6]<<8) + q0[7];
767 q1 = blockBase + (q1[0]<<8) + q1[1];
768 n1 = (q1[6]<<8) + q1[7];
772 return memcmp(q0, q1, n0);
774 r = memcmp(q0, q1, n0);
777 r = memcmp(q0, q1, n1);
783 metaSinkFlush(MetaSink *k)
791 assert(k->nindex <= k->maxindex);
798 mb.nindex = k->nindex;
799 mb.maxindex = k->maxindex;
805 /* XXX this is not reentrant! */
807 qsort(p, k->nindex, MetaIndexSize, dirCmp);
808 p += k->nindex*MetaIndexSize;
810 memset(p, 0, (k->maxindex-k->nindex)*MetaIndexSize);
811 p += (k->maxindex-k->nindex)*MetaIndexSize;
813 sinkWrite(k->sink, k->buf, n);
815 /* move down partial entry */
817 memmove(p, k->rp, n);
824 metaSinkPutc(MetaSink *k, int c)
829 vtFatal("directory entry too large");
835 metaSinkPutString(MetaSink *k, char *s)
838 metaSinkPutc(k, n>>8);
840 metaSinkWrite(k, (uchar*)s, n);
844 metaSinkPutUint32(MetaSink *k, ulong x)
846 metaSinkPutc(k, x>>24);
847 metaSinkPutc(k, x>>16);
848 metaSinkPutc(k, x>>8);
853 metaSinkPutUint64(MetaSink *k, uvlong x)
855 metaSinkPutUint32(k, x>>32);
856 metaSinkPutUint32(k, x);
860 metaSinkWrite(MetaSink *k, uchar *data, int n)
865 vtFatal("directory entry too large");
867 memmove(k->p, data, n);
872 metaSinkWriteDir(MetaSink *ms, VacDir *dir)
874 metaSinkPutUint32(ms, DirMagic);
875 metaSinkPutc(ms, Version>>8);
876 metaSinkPutc(ms, Version);
877 metaSinkPutString(ms, dir->elem);
878 metaSinkPutUint32(ms, dir->entry);
879 metaSinkPutUint64(ms, dir->qid);
880 metaSinkPutString(ms, dir->uid);
881 metaSinkPutString(ms, dir->gid);
882 metaSinkPutString(ms, dir->mid);
883 metaSinkPutUint32(ms, dir->mtime);
884 metaSinkPutUint32(ms, dir->mcount);
885 metaSinkPutUint32(ms, dir->ctime);
886 metaSinkPutUint32(ms, dir->atime);
887 metaSinkPutUint32(ms, dir->mode);
890 metaSinkPutc(ms, DirPlan9Entry); /* plan9 extra info */
891 metaSinkPutc(ms, 0); /* plan9 extra size */
892 metaSinkPutc(ms, 12); /* plan9 extra size */
893 metaSinkPutUint64(ms, dir->p9path);
894 metaSinkPutUint32(ms, dir->p9version);
897 if(dir->qidSpace != 0) {
898 metaSinkPutc(ms, DirQidSpaceEntry);
900 metaSinkPutc(ms, 16);
901 metaSinkPutUint64(ms, dir->qidOffset);
902 metaSinkPutUint64(ms, dir->qidMax);
906 metaSinkPutc(ms, DirGenEntry);
909 metaSinkPutUint32(ms, dir->gen);
917 plan9ToVacDir(VacDir *vd, Dir *dir, ulong entry, uvlong qid)
919 memset(vd, 0, sizeof(VacDir));
921 vd->elem = vtStrDup(dir->name);
924 vd->uid = vtStrDup(dir->uid);
925 vd->gid = vtStrDup(dir->gid);
926 vd->mid = vtStrDup(dir->muid);
927 vd->mtime = dir->mtime;
929 vd->ctime = dir->mtime; /* ctime: not available on plan 9 */
930 vd->atime = dir->atime;
932 vd->mode = dir->mode & 0777;
933 if(dir->mode & DMDIR)
935 if(dir->mode & DMAPPEND)
936 vd->mode |= ModeAppend;
937 if(dir->mode & DMEXCL)
938 vd->mode |= ModeExclusive;
941 vd->p9path = dir->qid.path;
942 vd->p9version = dir->qid.vers;
947 metaSinkEOR(MetaSink *k)
952 p = k->buf + MetaHeaderSize;
953 p += k->nindex * MetaIndexSize;
954 o = k->rp-k->buf; /* offset from start of block */
955 n = k->p-k->rp; /* size of entry */
962 if(k->nindex == k->maxindex)
967 metaSinkClose(MetaSink *k)
974 metaSinkFree(MetaSink *k)
987 fprint(2, "%s: ", argv0);
988 vfprint(2, fmt, arg);
1000 #define TWID64 ((u64int)~(u64int)0)
1010 n = strtoul(s, &es, 0);
1011 if(*es == 'k' || *es == 'K'){
1014 }else if(*es == 'm' || *es == 'M'){
1017 }else if(*es == 'g' || *es == 'G'){
1018 n *= 1024*1024*1024;