Blob


1 #include <u.h>
2 #include <sys/stat.h>
3 #include "stdinc.h"
4 #include "vac.h"
5 #include "dat.h"
6 #include "fns.h"
8 int mainstacksize = 128*1024;
10 typedef struct Sink Sink;
11 typedef struct MetaSink MetaSink;
12 typedef struct DirSink DirSink;
14 struct Sink {
15 VtConn *z;
16 VtEntry dir;
17 uchar *buf;
18 uchar *pbuf[VtPointerDepth+1];
19 };
21 struct DirSink {
22 Sink *sink;
23 MetaSink *msink;
24 ulong nentry;
25 uchar *buf;
26 uchar *p; /* current pointer */
27 uchar *ep; /* end pointer */
28 };
30 struct MetaSink {
31 Sink *sink;
32 uchar *buf;
33 int maxindex;
34 int nindex;
35 uchar *rp; /* start of current record */
36 uchar *p; /* current pointer */
37 uchar *ep; /* end pointer */
38 };
40 static void usage(void);
41 static int strpcmp(const void*, const void*);
42 static void warn(char *fmt, ...);
43 static void cleanup(void);
44 static u64int unittoull(char *s);
45 static void vac(VtConn *z, char *argv[]);
46 static void vacfile(DirSink *dsink, char *lname, char *sname, VacFile*);
47 static void vacstdin(DirSink *dsink, char *name, VacFile *vf);
48 static void vacdata(DirSink *dsink, int fd, char *lname, VacFile*, Dir*);
49 static void vacdir(DirSink *dsink, int fd, char *lname, char *sname, VacFile*);
50 static int vacmerge(DirSink *dsink, char *lname, char *sname);
51 static int vacspecial(DirSink *dsink, Dir *dir, char *lname, char *sname, VacFile *vf);
52 Sink *sinkalloc(VtConn *z, int psize, int dsize);
53 void sinkwrite(Sink *k, uchar *data, int n);
54 void sinkwritescore(Sink *k, uchar *score, int n);
55 void sinkclose(Sink *k);
56 void sinkfree(Sink *k);
58 DirSink *dirsinkalloc(VtConn *z, int psize, int dsize);
59 void dirsinkwrite(DirSink *k, VtEntry*);
60 void dirsinkwritesink(DirSink *k, Sink*);
61 int dirsinkwritefile(DirSink *k, VacFile *vf);
62 void dirsinkclose(DirSink *k);
63 void dirsinkfree(DirSink *k);
65 MetaSink *metasinkalloc(VtConn *z, int psize, int dsize);
66 void metasinkputc(MetaSink *k, int c);
67 void metasinkputstring(MetaSink *k, char *s);
68 void metasinkputuint32(MetaSink *k, ulong x);
69 void metasinkputuint64(MetaSink *k, uvlong x);
70 void metasinkwrite(MetaSink *k, uchar *data, int n);
71 void metasinkwritedir(MetaSink *ms, VacDir *vd);
72 void metasinkeor(MetaSink *k);
73 void metasinkclose(MetaSink *k);
74 void metasinkfree(MetaSink *k);
75 void plan9tovacdir(VacDir*, Dir*, ulong entry, uvlong qid);
77 enum {
78 Version = 8,
79 BlockSize = 8*1024,
80 MaxExclude = 1000
81 };
83 struct {
84 ulong file;
85 ulong sfile;
86 ulong data;
87 ulong sdata;
88 ulong skip;
89 ulong meta;
90 } stats;
92 int bsize = BlockSize;
93 int maxbsize;
94 char *oname, *dfile;
95 int verbose;
96 uvlong fileid = 1;
97 int qdiff;
98 char *exclude[MaxExclude];
99 int nexclude;
100 int nowrite;
101 int merge;
102 char *isi;
104 static void
105 usage(void)
107 fprint(2, "usage: %s [-amqsv] [-h host] [-d vacfile] [-b blocksize] [-i name] [-e exclude] [-f vacfile] file ... \n", argv0);
108 threadexitsall("usage");
111 void
112 threadmain(int argc, char *argv[])
114 VtConn *z;
115 char *p;
116 char *host = nil;
117 int statsflag = 0;
119 atexit(cleanup);
121 ARGBEGIN{
122 default:
123 usage();
124 case 'b':
125 p = ARGF();
126 if(p == 0)
127 usage();
128 bsize = unittoull(p);
129 if(bsize == ~0)
130 usage();
131 break;
132 case 'd':
133 dfile = ARGF();
134 if(dfile == nil)
135 usage();
136 break;
137 case 'e':
138 if(nexclude >= MaxExclude)
139 sysfatal("too many exclusions\n");
140 exclude[nexclude] = ARGF();
141 if(exclude[nexclude] == nil)
142 usage();
143 nexclude++;
144 break;
145 case 'f':
146 oname = ARGF();
147 if(oname == 0)
148 usage();
149 break;
150 case 'h':
151 host = ARGF();
152 if(host == nil)
153 usage();
154 break;
155 case 'i':
156 isi = ARGF();
157 if(isi == nil)
158 usage();
159 break;
160 case 'n':
161 nowrite++;
162 break;
163 case 'm':
164 merge++;
165 break;
166 case 'q':
167 qdiff++;
168 break;
169 case 's':
170 statsflag++;
171 break;
172 case 'v':
173 verbose++;
174 break;
175 }ARGEND;
177 if(argc == 0)
178 usage();
180 if(bsize < 512)
181 bsize = 512;
182 if(bsize > VtMaxLumpSize)
183 bsize = VtMaxLumpSize;
184 maxbsize = bsize;
186 fmtinstall('V', vtscorefmt);
188 z = vtdial(host);
189 if(z == nil)
190 sysfatal("could not connect to server: %r");
192 if(vtconnect(z) < 0)
193 sysfatal("vtconnect: %r");
195 qsort(exclude, nexclude, sizeof(char*), strpcmp);
197 vac(z, argv);
199 if(vtsync(z) < 0)
200 fprint(2, "warning: could not ask server to flush pending writes: %r\n");
202 if(statsflag)
203 fprint(2, "files %ld:%ld data %ld:%ld:%ld meta %ld\n", stats.file, stats.sfile,
204 stats.data, stats.skip, stats.sdata, stats.meta);
205 /*packetStats(); */
206 vthangup(z);
208 threadexitsall(0);
211 static int
212 strpcmp(const void *p0, const void *p1)
214 return strcmp(*(char**)p0, *(char**)p1);
217 int
218 vacwrite(VtConn *z, uchar score[VtScoreSize], int type, uchar *buf, int n)
220 assert(n > 0);
221 if(nowrite){
222 sha1(buf, n, score, nil);
223 return 0;
225 return vtwrite(z, score, type, buf, n);
228 static char*
229 lastelem(char *oname)
231 char *p;
233 if(oname == nil)
234 abort();
235 if((p = strrchr(oname, '/')) == nil)
236 return oname;
237 return p+1;
240 static void
241 vac(VtConn *z, char *argv[])
243 DirSink *dsink, *ds;
244 MetaSink *ms;
245 VtRoot root;
246 uchar score[VtScoreSize], buf[VtRootSize];
247 char cwd[2048];
248 int cd;
249 char *cp2, *cp;
250 VacFs *fs;
251 VacFile *vff;
252 int fd;
253 Dir *dir;
254 VacDir vd;
256 if(getwd(cwd, sizeof(cwd)) == 0)
257 sysfatal("can't find current directory: %r\n");
259 dsink = dirsinkalloc(z, bsize, bsize);
261 fs = nil;
262 if(dfile != nil) {
263 fs = vacfsopen(z, dfile, VtOREAD, 1000);
264 if(fs == nil)
265 fprint(2, "could not open diff: %s: %r\n", dfile);
269 if(oname != nil) {
270 fd = create(oname, OWRITE, 0666);
271 if(fd < 0)
272 sysfatal("could not create file: %s: %r", oname);
273 } else
274 fd = 1;
276 dir = dirfstat(fd);
277 if(dir == nil)
278 sysfatal("dirfstat failed: %r");
279 if(oname)
280 dir->name = lastelem(oname);
281 else
282 dir->name = "stdin";
284 for(; *argv; argv++) {
285 cp2 = *argv;
286 cd = 0;
287 for (cp = *argv; *cp; cp++)
288 if (*cp == '/')
289 cp2 = cp;
290 if (cp2 != *argv) {
291 *cp2 = '\0';
292 chdir(*argv);
293 *cp2 = '/';
294 cp2++;
295 cd = 1;
297 vff = nil;
298 if(fs)
299 vff = vacfileopen(fs, cp2);
300 vacfile(dsink, argv[0], cp2, vff);
301 if(vff)
302 vacfiledecref(vff);
303 if(cd && chdir(cwd) < 0)
304 sysfatal("can't cd back to %s: %r\n", cwd);
307 if(isi) {
308 vff = nil;
309 if(fs)
310 vff = vacfileopen(fs, isi);
311 vacstdin(dsink, isi, vff);
312 if(vff)
313 vacfiledecref(vff);
316 dirsinkclose(dsink);
318 /* build meta information for the root */
319 ms = metasinkalloc(z, bsize, bsize);
320 /* fake into a directory */
321 dir->mode = DMDIR|0555;
322 dir->qid.type |= QTDIR;
323 plan9tovacdir(&vd, dir, 0, fileid++);
324 if(strcmp(vd.elem, "/") == 0){
325 vtfree(vd.elem);
326 vd.elem = vtstrdup("root");
328 metasinkwritedir(ms, &vd);
329 vdcleanup(&vd);
330 metasinkclose(ms);
332 ds = dirsinkalloc(z, bsize, bsize);
333 dirsinkwritesink(ds, dsink->sink);
334 dirsinkwritesink(ds, dsink->msink->sink);
335 dirsinkwritesink(ds, ms->sink);
336 dirsinkclose(ds);
338 memset(&root, 0, sizeof(root));
339 strncpy(root.name, dir->name, sizeof(root.name));
340 root.name[sizeof(root.name)-1] = 0;
341 free(dir);
342 sprint(root.type, "vac");
343 memmove(root.score, ds->sink->dir.score, VtScoreSize);
344 root.blocksize = maxbsize;
345 if(fs != nil)
346 vacfsgetscore(fs, root.prev);
348 metasinkfree(ms);
349 dirsinkfree(ds);
350 dirsinkfree(dsink);
351 if(fs != nil)
352 vacfsclose(fs);
354 vtrootpack(&root, buf);
355 if(vacwrite(z, score, VtRootType, buf, VtRootSize) < 0)
356 sysfatal("vacWrite failed: %r");
358 fprint(fd, "vac:%V\n", score);
360 /* avoid remove at cleanup */
361 oname = nil;
364 static int
365 isexcluded(char *name)
367 int bot, top, i, x;
369 bot = 0;
370 top = nexclude;
371 while(bot < top) {
372 i = (bot+top)>>1;
373 x = strcmp(exclude[i], name);
374 if(x == 0)
375 return 1;
376 if(x < 0)
377 bot = i + 1;
378 else /* x > 0 */
379 top = i;
381 return 0;
384 static void
385 vacfile(DirSink *dsink, char *lname, char *sname, VacFile *vf)
387 int fd;
388 Dir *dir;
389 VacDir vd;
390 ulong entry;
392 if(isexcluded(lname)) {
393 warn("excluding: %s", lname);
394 return;
397 if(merge && vacmerge(dsink, lname, sname) >= 0)
398 return;
400 if((dir = dirstat(sname)) == nil){
401 warn("could not stat file %s: %r", lname);
402 return;
404 if(dir->mode&(DMSYMLINK|DMDEVICE|DMNAMEDPIPE)){
405 vacspecial(dsink, dir, lname, sname, vf);
406 free(dir);
407 return;
408 }else if(dir->mode&DMSOCKET){
409 free(dir);
410 return;
412 free(dir);
414 fd = open(sname, OREAD);
415 if(fd < 0) {
416 warn("could not open file: %s: %r", lname);
417 return;
420 if(verbose)
421 fprint(2, "%s\n", lname);
423 dir = dirfstat(fd);
424 if(dir == nil) {
425 warn("can't stat %s: %r", lname);
426 close(fd);
427 return;
429 dir->name = lastelem(sname);
431 entry = dsink->nentry;
433 if(dir->mode & DMDIR)
434 vacdir(dsink, fd, lname, sname, vf);
435 else
436 vacdata(dsink, fd, lname, vf, dir);
438 plan9tovacdir(&vd, dir, entry, fileid++);
439 metasinkwritedir(dsink->msink, &vd);
440 vdcleanup(&vd);
442 free(dir);
443 close(fd);
446 static void
447 vacstdin(DirSink *dsink, char *name, VacFile *vf)
449 Dir *dir;
450 VacDir vd;
451 ulong entry;
453 if(verbose)
454 fprint(2, "%s\n", "<stdio>");
456 dir = dirfstat(0);
457 if(dir == nil) {
458 warn("can't stat <stdio>: %r");
459 return;
461 dir->name = "stdin";
463 entry = dsink->nentry;
465 vacdata(dsink, 0, "<stdin>", vf, dir);
467 plan9tovacdir(&vd, dir, entry, fileid++);
468 vd.elem = vtstrdup(name);
469 metasinkwritedir(dsink->msink, &vd);
470 vdcleanup(&vd);
472 free(dir);
475 static int
476 sha1check(u8int *score, uchar *buf, int n)
478 uchar score2[VtScoreSize];
480 sha1(buf, n, score2, nil);
481 if(memcmp(score, score2, VtScoreSize) == 0)
482 return 0;
483 return -1;
486 static ulong
487 vacdataskip(Sink *sink, VacFile *vf, int fd, ulong blocks, uchar *buf, char *lname)
489 int n;
490 ulong i;
491 uchar score[VtScoreSize];
493 /* skip blocks for append only files */
494 if(seek(fd, (blocks-1)*bsize, 0) != (blocks-1)*bsize) {
495 warn("error seeking: %s", lname);
496 goto Err;
498 n = readn(fd, buf, bsize);
499 if(n < bsize) {
500 warn("error checking append only file: %s", lname);
501 goto Err;
503 if(vacfileblockscore(vf, blocks-1, score)<0 || sha1check(score, buf, n)<0) {
504 warn("last block of append file did not match: %s", lname);
505 goto Err;
508 for(i=0; i<blocks; i++) {
509 if(vacfileblockscore(vf, i, score) < 0) {
510 warn("could not get score: %s: %lud", lname, i);
511 seek(fd, i*bsize, 0);
512 return i;
514 stats.skip++;
515 sinkwritescore(sink, score, bsize);
518 return i;
519 Err:
520 seek(fd, 0, 0);
521 return 0;
524 static void
525 vacdata(DirSink *dsink, int fd, char *lname, VacFile *vf, Dir *dir)
527 uchar *buf;
528 Sink *sink;
529 int n;
530 uchar score[VtScoreSize];
531 ulong block, same;
532 VacDir vd;
533 ulong vfblocks;
535 vfblocks = 0;
536 if(vf != nil && qdiff) {
537 vacfilegetdir(vf, &vd);
538 if(vd.mtime == dir->mtime)
539 if(vd.size == dir->length)
540 if(!vd.plan9 || /* vd.p9path == dir->qid.path && */ vd.p9version == dir->qid.vers)
541 if(dirsinkwritefile(dsink, vf) >= 0) {
542 stats.sfile++;
543 vdcleanup(&vd);
544 return;
547 if(verbose)
548 fprint(2, "+ %s\n", lname);
550 /* look for an append only file */
551 if((dir->mode&DMAPPEND) != 0)
552 if(vd.size < dir->length)
553 if(vd.plan9)
554 if(vd.p9path == dir->qid.path)
555 vfblocks = vd.size/bsize;
557 vdcleanup(&vd);
559 stats.file++;
561 buf = vtmalloc(bsize);
562 sink = sinkalloc(dsink->sink->z, bsize, bsize);
563 block = 0;
564 same = stats.sdata+stats.skip;
566 if(vfblocks > 1)
567 block += vacdataskip(sink, vf, fd, vfblocks, buf, lname);
569 if(0) fprint(2, "vacData: %s: %ld\n", lname, block);
570 for(;;) {
571 n = readn(fd, buf, bsize);
572 if(0 && n < 0)
573 warn("file truncated due to read error: %s: %r", lname);
574 if(n <= 0)
575 break;
576 if(vf != nil && vacfileblockscore(vf, block, score)>=0 && sha1check(score, buf, n)>=0) {
577 stats.sdata++;
578 sinkwritescore(sink, score, n);
579 } else
580 sinkwrite(sink, buf, n);
581 block++;
583 same = stats.sdata+stats.skip - same;
585 if(same && (dir->mode&DMAPPEND) != 0)
586 if(0)fprint(2, "%s: total %lud same %lud:%lud diff %lud\n",
587 lname, block, same, vfblocks, block-same);
589 sinkclose(sink);
590 dirsinkwritesink(dsink, sink);
591 sinkfree(sink);
592 free(buf);
596 static void
597 vacdir(DirSink *dsink, int fd, char *lname, char *sname, VacFile *vf)
599 Dir *dirs;
600 char *ln, *sn;
601 int i, nd;
602 DirSink *ds;
603 VacFile *vvf;
604 char *name;
606 ds = dirsinkalloc(dsink->sink->z, bsize, bsize);
607 while((nd = dirread(fd, &dirs)) > 0){
608 for(i = 0; i < nd; i++){
609 name = dirs[i].name;
610 /* check for bad file names */
611 if(name[0] == 0 || strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
612 continue;
613 ln = vtmalloc(strlen(lname) + strlen(name) + 2);
614 sn = vtmalloc(strlen(sname) + strlen(name) + 2);
615 strcpy(ln, lname);
616 strcat(ln, "/");
617 strcat(ln, name);
618 strcpy(sn, sname);
619 strcat(sn, "/");
620 strcat(sn, name);
621 if(vf != nil)
622 vvf = vacfilewalk(vf, name);
623 else
624 vvf = nil;
625 vacfile(ds, ln, sn, vvf);
626 if(vvf != nil)
627 vacfiledecref(vvf);
628 vtfree(ln);
629 vtfree(sn);
631 free(dirs);
633 dirsinkclose(ds);
634 dirsinkwritesink(dsink, ds->sink);
635 dirsinkwritesink(dsink, ds->msink->sink);
636 dirsinkfree(ds);
639 static int
640 vacmergefile(DirSink *dsink, VacFile *vf, VacDir *dir, uvlong offset, uvlong *max)
642 uchar buf[VtEntrySize];
643 VtEntry dd, md;
644 int e;
646 if(vacfileread(vf, buf, VtEntrySize, (uvlong)dir->entry*VtEntrySize) != VtEntrySize) {
647 warn("could not read venti dir entry: %s\n", dir->elem);
648 return -1;
650 vtentryunpack(&dd, buf, 0);
652 if(dir->mode & ModeDir) {
653 e = dir->mentry;
654 if(e == 0)
655 e = dir->entry + 1;
657 if(vacfileread(vf, buf, VtEntrySize, e*VtEntrySize) != VtEntrySize) {
658 warn("could not read venti dir entry: %s\n", dir->elem);
659 return 0;
661 vtentryunpack(&md, buf, 0);
664 /* max might incorrect in some old dumps */
665 if(dir->qid >= *max) {
666 warn("qid out of range: %s", dir->elem);
667 *max = dir->qid;
670 dir->qid += offset;
671 dir->entry = dsink->nentry;
673 if(dir->qidspace) {
674 dir->qidoffset += offset;
675 } else {
676 dir->qidspace = 1;
677 dir->qidoffset = offset;
678 dir->qidmax = *max;
681 dirsinkwrite(dsink, &dd);
682 if(dir->mode & ModeDir)
683 dirsinkwrite(dsink, &md);
684 metasinkwritedir(dsink->msink, dir);
686 return 0;
689 static int
690 vacmerge(DirSink *dsink, char *lname, char *sname)
692 char *p;
693 VacFs *fs;
694 VacFile *vf;
695 VacDirEnum *d;
696 VacDir dir;
697 uvlong max;
699 if((p=strrchr(sname, '.')) == nil || strcmp(p, ".vac") != 0)
700 return -1;
702 d = nil;
703 fs = vacfsopen(dsink->sink->z, sname, VtOREAD, 100);
704 if(fs == nil)
705 return -1;
707 vf = vacfileopen(fs, "/");
708 if(vf == nil)
709 goto Done;
710 max = vacfilegetid(vf);
711 d = vdeopen(vf);
712 if(d == nil)
713 goto Done;
715 if(verbose)
716 fprint(2, "merging: %s\n", lname);
718 if(maxbsize < fs->bsize)
719 maxbsize = fs->bsize;
721 for(;;) {
722 if(vderead(d, &dir) < 1)
723 break;
724 vacmergefile(dsink, vf, &dir, fileid, &max);
725 vdcleanup(&dir);
727 fileid += max;
729 Done:
730 if(d != nil)
731 vdeclose(d);
732 if(vf != nil)
733 vacfiledecref(vf);
734 vacfsclose(fs);
735 return 0;
738 static int
739 vacspecial(DirSink *dsink, Dir* dir, char *lname, char *sname, VacFile *vf)
741 char *btmp, *buf;
742 int buflen, dtype, major, minor, n;
743 ulong entry;
744 Sink *sink;
745 VacDir vd;
747 n = 0;
748 buflen = 128;
749 buf = malloc(buflen);
750 if(buf == nil)
751 return -1;
753 if(verbose)
754 fprint(2, "%s\n", lname);
756 dir->name = lastelem(sname);
758 if(dir->mode & DMSYMLINK){
759 while((n = readlink(sname, buf, buflen)) == buflen){
760 buflen *= 2;
761 btmp = vtrealloc(buf, buflen);
762 if(btmp == nil){
763 free(buf);
764 return -1;
766 buf = btmp;
768 dir->mode &= ~DMDIR;
769 dir->mode |= DMSYMLINK;
770 }else if(dir->mode & DMDEVICE){
771 dtype = (dir->qid.path >> 16) & 0xFF;
772 minor = dir->qid.path & 0xff;
773 major = (dir->qid.path >> 8) & 0xFF;
774 n = snprint(buf, buflen, "%c %d %d", dtype, major, minor);
777 entry = dsink->nentry;
779 sink = sinkalloc(dsink->sink->z, bsize, bsize);
780 sinkwrite(sink, (uchar*)buf, n);
781 sinkclose(sink);
782 dirsinkwritesink(dsink, sink);
783 sinkfree(sink);
784 free(buf);
786 dir->name = lastelem(sname);
787 dir->length = n;
788 plan9tovacdir(&vd, dir, entry, fileid++);
789 metasinkwritedir(dsink->msink, &vd);
790 vdcleanup(&vd);
792 return 0;
796 Sink *
797 sinkalloc(VtConn *z, int psize, int dsize)
799 Sink *k;
800 int i;
802 if(psize < 512 || psize > VtMaxLumpSize)
803 sysfatal("sinkalloc: bad psize");
804 if(dsize < 512 || dsize > VtMaxLumpSize)
805 sysfatal("sinkalloc: bad psize");
807 psize = VtScoreSize*(psize/VtScoreSize);
809 k = vtmallocz(sizeof(Sink));
810 k->z = z;
811 k->dir.flags = VtEntryActive;
812 k->dir.psize = psize;
813 k->dir.dsize = dsize;
814 k->buf = vtmallocz(VtPointerDepth*k->dir.psize + VtScoreSize);
815 for(i=0; i<=VtPointerDepth; i++)
816 k->pbuf[i] = k->buf + i*k->dir.psize;
817 return k;
820 void
821 sinkwritescore(Sink *k, uchar score[VtScoreSize], int n)
823 int i;
824 uchar *p;
825 VtEntry *d;
827 memmove(k->pbuf[0], score, VtScoreSize);
829 d = &k->dir;
831 for(i=0; i<VtPointerDepth; i++) {
832 k->pbuf[i] += VtScoreSize;
833 if(k->pbuf[i] < k->buf + d->psize*(i+1))
834 break;
835 if(i == VtPointerDepth-1)
836 sysfatal("file too big");
837 p = k->buf+i*d->psize;
838 stats.meta++;
839 if(vacwrite(k->z, k->pbuf[i+1], VtDataType+1+i, p, d->psize) < 0)
840 sysfatal("vacwrite failed: %r");
841 k->pbuf[i] = p;
844 /* round size up to multiple of dsize */
845 d->size = d->dsize * ((d->size + d->dsize-1)/d->dsize);
847 d->size += n;
850 void
851 sinkwrite(Sink *k, uchar *p, int n)
853 int type;
854 uchar score[VtScoreSize];
856 if(n == 0)
857 return;
859 if(n > k->dir.dsize)
860 sysfatal("sinkWrite: size too big");
862 if((k->dir.type&~VtTypeDepthMask) == VtDirType){
863 type = VtDirType;
864 stats.meta++;
865 } else {
866 type = VtDataType;
867 stats.data++;
869 if(vacwrite(k->z, score, type, p, n) < 0)
870 sysfatal("vacWrite failed: %r");
872 sinkwritescore(k, score, n);
875 static int
876 sizetodepth(uvlong s, int psize, int dsize)
878 int np;
879 int d;
881 /* determine pointer depth */
882 np = psize/VtScoreSize;
883 s = (s + dsize - 1)/dsize;
884 for(d = 0; s > 1; d++)
885 s = (s + np - 1)/np;
886 return d;
889 void
890 sinkclose(Sink *k)
892 int i, n, base;
893 uchar *p;
894 VtEntry *kd;
896 kd = &k->dir;
898 /* empty */
899 if(kd->size == 0) {
900 memmove(kd->score, vtzeroscore, VtScoreSize);
901 return;
904 for(n=VtPointerDepth-1; n>0; n--)
905 if(k->pbuf[n] > k->buf + kd->psize*n)
906 break;
908 base = kd->type&~VtTypeDepthMask;
909 kd->type = base + sizetodepth(kd->size, kd->psize, kd->dsize);
911 /* skip full part of tree */
912 for(i=0; i<n && k->pbuf[i] == k->buf + kd->psize*i; i++)
915 /* is the tree completely full */
916 if(i == n && k->pbuf[n] == k->buf + kd->psize*n + VtScoreSize) {
917 memmove(kd->score, k->pbuf[n] - VtScoreSize, VtScoreSize);
918 return;
920 n++;
922 /* clean up the edge */
923 for(; i<n; i++) {
924 p = k->buf+i*kd->psize;
925 stats.meta++;
926 if(vacwrite(k->z, k->pbuf[i+1], base+1+i, p, k->pbuf[i]-p) < 0)
927 sysfatal("vacWrite failed: %r");
928 k->pbuf[i+1] += VtScoreSize;
930 memmove(kd->score, k->pbuf[i] - VtScoreSize, VtScoreSize);
933 void
934 sinkfree(Sink *k)
936 vtfree(k->buf);
937 vtfree(k);
940 DirSink *
941 dirsinkalloc(VtConn *z, int psize, int dsize)
943 DirSink *k;
944 int ds;
946 ds = VtEntrySize*(dsize/VtEntrySize);
948 k = vtmallocz(sizeof(DirSink));
949 k->sink = sinkalloc(z, psize, ds);
950 k->sink->dir.type = VtDirType;
951 k->msink = metasinkalloc(z, psize, dsize);
952 k->buf = vtmalloc(ds);
953 k->p = k->buf;
954 k->ep = k->buf + ds;
955 return k;
958 void
959 dirsinkwrite(DirSink *k, VtEntry *dir)
961 if(k->p + VtEntrySize > k->ep) {
962 sinkwrite(k->sink, k->buf, k->p - k->buf);
963 k->p = k->buf;
965 vtentrypack(dir, k->p, 0);
966 k->nentry++;
967 k->p += VtEntrySize;
970 void
971 dirsinkwritesink(DirSink *k, Sink *sink)
973 dirsinkwrite(k, &sink->dir);
976 int
977 dirsinkwritefile(DirSink *k, VacFile *vf)
979 VtEntry dir;
981 if(vacfilegetvtentry(vf, &dir) < 0)
982 return -1;
983 dirsinkwrite(k, &dir);
984 return 0;
987 void
988 dirsinkclose(DirSink *k)
990 metasinkclose(k->msink);
991 if(k->p != k->buf)
992 sinkwrite(k->sink, k->buf, k->p - k->buf);
993 sinkclose(k->sink);
996 void
997 dirsinkfree(DirSink *k)
999 sinkfree(k->sink);
1000 metasinkfree(k->msink);
1001 vtfree(k->buf);
1002 vtfree(k);
1005 MetaSink*
1006 metasinkalloc(VtConn *z, int psize, int dsize)
1008 MetaSink *k;
1010 k = vtmallocz(sizeof(MetaSink));
1011 k->sink = sinkalloc(z, psize, dsize);
1012 k->buf = vtmalloc(dsize);
1013 k->maxindex = dsize/100; /* 100 byte entries seems reasonable */
1014 if(k->maxindex < 1)
1015 k->maxindex = 1;
1016 k->rp = k->p = k->buf + MetaHeaderSize + k->maxindex*MetaIndexSize;
1017 k->ep = k->buf + dsize;
1018 return k;
1021 /* hack to get base to compare routine - not reentrant */
1022 uchar *blockbase;
1024 int
1025 dircmp(const void *p0, const void *p1)
1027 uchar *q0, *q1;
1028 int n0, n1, r;
1030 /* name is first element of entry */
1031 q0 = (uchar*)p0;
1032 q0 = blockbase + (q0[0]<<8) + q0[1];
1033 n0 = (q0[6]<<8) + q0[7];
1034 q0 += 8;
1036 q1 = (uchar*)p1;
1037 q1 = blockbase + (q1[0]<<8) + q1[1];
1038 n1 = (q1[6]<<8) + q1[7];
1039 q1 += 8;
1041 if(n0 == n1)
1042 return memcmp(q0, q1, n0);
1043 else if (n0 < n1) {
1044 r = memcmp(q0, q1, n0);
1045 return (r==0)?1:r;
1046 } else {
1047 r = memcmp(q0, q1, n1);
1048 return (r==0)?-1:r;
1052 void
1053 metasinkflush(MetaSink *k)
1055 uchar *p;
1056 int n;
1057 MetaBlock mb;
1059 if(k->nindex == 0)
1060 return;
1061 assert(k->nindex <= k->maxindex);
1063 p = k->buf;
1064 n = k->rp - p;
1066 mb.size = n;
1067 mb.free = 0;
1068 mb.nindex = k->nindex;
1069 mb.maxindex = k->maxindex;
1070 mb.buf = p;
1071 mbpack(&mb);
1073 p += MetaHeaderSize;
1075 /* XXX this is not reentrant! */
1076 blockbase = k->buf;
1077 qsort(p, k->nindex, MetaIndexSize, dircmp);
1078 p += k->nindex*MetaIndexSize;
1080 memset(p, 0, (k->maxindex-k->nindex)*MetaIndexSize);
1081 p += (k->maxindex-k->nindex)*MetaIndexSize;
1083 sinkwrite(k->sink, k->buf, n);
1085 /* move down partial entry */
1086 n = k->p - k->rp;
1087 memmove(p, k->rp, n);
1088 k->rp = p;
1089 k->p = p + n;
1090 k->nindex = 0;
1093 void
1094 metasinkputc(MetaSink *k, int c)
1096 if(k->p+1 > k->ep)
1097 metasinkflush(k);
1098 if(k->p+1 > k->ep)
1099 sysfatal("directory entry too large");
1100 k->p[0] = c;
1101 k->p++;
1104 void
1105 metasinkputstring(MetaSink *k, char *s)
1107 int n = strlen(s);
1108 metasinkputc(k, n>>8);
1109 metasinkputc(k, n);
1110 metasinkwrite(k, (uchar*)s, n);
1113 void
1114 metasinkputuint32(MetaSink *k, ulong x)
1116 metasinkputc(k, x>>24);
1117 metasinkputc(k, x>>16);
1118 metasinkputc(k, x>>8);
1119 metasinkputc(k, x);
1122 void
1123 metasinkputuint64(MetaSink *k, uvlong x)
1125 metasinkputuint32(k, x>>32);
1126 metasinkputuint32(k, x);
1129 void
1130 metasinkwrite(MetaSink *k, uchar *data, int n)
1132 if(k->p + n > k->ep)
1133 metasinkflush(k);
1134 if(k->p + n > k->ep)
1135 sysfatal("directory entry too large");
1137 memmove(k->p, data, n);
1138 k->p += n;
1141 void
1142 metasinkwritedir(MetaSink *ms, VacDir *dir)
1144 metasinkputuint32(ms, DirMagic);
1145 metasinkputc(ms, Version>>8);
1146 metasinkputc(ms, Version);
1147 metasinkputstring(ms, dir->elem);
1148 metasinkputuint32(ms, dir->entry);
1149 metasinkputuint64(ms, dir->qid);
1150 metasinkputstring(ms, dir->uid);
1151 metasinkputstring(ms, dir->gid);
1152 metasinkputstring(ms, dir->mid);
1153 metasinkputuint32(ms, dir->mtime);
1154 metasinkputuint32(ms, dir->mcount);
1155 metasinkputuint32(ms, dir->ctime);
1156 metasinkputuint32(ms, dir->atime);
1157 metasinkputuint32(ms, dir->mode);
1159 if(dir->plan9) {
1160 metasinkputc(ms, DirPlan9Entry); /* plan9 extra info */
1161 metasinkputc(ms, 0); /* plan9 extra size */
1162 metasinkputc(ms, 12); /* plan9 extra size */
1163 metasinkputuint64(ms, dir->p9path);
1164 metasinkputuint32(ms, dir->p9version);
1167 if(dir->qidspace != 0) {
1168 metasinkputc(ms, DirQidSpaceEntry);
1169 metasinkputc(ms, 0);
1170 metasinkputc(ms, 16);
1171 metasinkputuint64(ms, dir->qidoffset);
1172 metasinkputuint64(ms, dir->qidmax);
1175 if(dir->gen != 0) {
1176 metasinkputc(ms, DirGenEntry);
1177 metasinkputc(ms, 0);
1178 metasinkputc(ms, 4);
1179 metasinkputuint32(ms, dir->gen);
1182 metasinkeor(ms);
1186 void
1187 plan9tovacdir(VacDir *vd, Dir *dir, ulong entry, uvlong qid)
1189 memset(vd, 0, sizeof(VacDir));
1191 vd->elem = vtstrdup(dir->name);
1192 vd->entry = entry;
1193 vd->qid = qid;
1194 vd->uid = vtstrdup(dir->uid);
1195 vd->gid = vtstrdup(dir->gid);
1196 vd->mid = vtstrdup(dir->muid);
1197 vd->mtime = dir->mtime;
1198 vd->mcount = 0;
1199 vd->ctime = dir->mtime; /* ctime: not available on plan 9 */
1200 vd->atime = dir->atime;
1202 vd->mode = dir->mode & 0777;
1203 if(dir->mode & DMDIR)
1204 vd->mode |= ModeDir;
1205 if(dir->mode & DMAPPEND)
1206 vd->mode |= ModeAppend;
1207 if(dir->mode & DMEXCL)
1208 vd->mode |= ModeExclusive;
1209 if(dir->mode & DMDEVICE)
1210 vd->mode |= ModeDevice;
1211 if(dir->mode & DMNAMEDPIPE)
1212 vd->mode |= ModeNamedPipe;
1213 if(dir->mode & DMSYMLINK)
1214 vd->mode |= ModeLink;
1216 vd->plan9 = 1;
1217 vd->p9path = dir->qid.path;
1218 vd->p9version = dir->qid.vers;
1222 void
1223 metasinkeor(MetaSink *k)
1225 uchar *p;
1226 int o, n;
1228 p = k->buf + MetaHeaderSize;
1229 p += k->nindex * MetaIndexSize;
1230 o = k->rp-k->buf; /* offset from start of block */
1231 n = k->p-k->rp; /* size of entry */
1232 p[0] = o >> 8;
1233 p[1] = o;
1234 p[2] = n >> 8;
1235 p[3] = n;
1236 k->rp = k->p;
1237 k->nindex++;
1238 if(k->nindex == k->maxindex)
1239 metasinkflush(k);
1242 void
1243 metasinkclose(MetaSink *k)
1245 metasinkflush(k);
1246 sinkclose(k->sink);
1249 void
1250 metasinkfree(MetaSink *k)
1252 sinkfree(k->sink);
1253 vtfree(k->buf);
1254 vtfree(k);
1257 static void
1258 warn(char *fmt, ...)
1260 va_list arg;
1262 va_start(arg, fmt);
1263 fprint(2, "%s: ", argv0);
1264 vfprint(2, fmt, arg);
1265 fprint(2, "\n");
1266 va_end(arg);
1269 static void
1270 cleanup(void)
1272 if(oname != nil)
1273 remove(oname);
1276 #define TWID64 ((u64int)~(u64int)0)
1278 static u64int
1279 unittoull(char *s)
1281 char *es;
1282 u64int n;
1284 if(s == nil)
1285 return TWID64;
1286 n = strtoul(s, &es, 0);
1287 if(*es == 'k' || *es == 'K'){
1288 n *= 1024;
1289 es++;
1290 }else if(*es == 'm' || *es == 'M'){
1291 n *= 1024*1024;
1292 es++;
1293 }else if(*es == 'g' || *es == 'G'){
1294 n *= 1024*1024*1024;
1295 es++;
1297 if(*es != '\0')
1298 return TWID64;
1299 return n;