tvac.c - plan9port - [fork] Plan 9 from user space | |
git clone git://src.adamsgaard.dk/plan9port | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
tvac.c (16320B) | |
--- | |
1 #include "stdinc.h" | |
2 #include "vac.h" | |
3 #include "dat.h" | |
4 #include "fns.h" | |
5 | |
6 // TODO: qids | |
7 | |
8 void | |
9 usage(void) | |
10 { | |
11 fprint(2, "vac [-imqsv] [-a archive.vac] [-b bsize] [-d old.vac]… | |
12 threadexitsall("usage"); | |
13 } | |
14 | |
15 enum | |
16 { | |
17 BlockSize = 8*1024, | |
18 CacheSize = 128<<20, | |
19 }; | |
20 | |
21 struct | |
22 { | |
23 int nfile; | |
24 int ndir; | |
25 vlong data; | |
26 vlong skipdata; | |
27 int skipfiles; | |
28 } stats; | |
29 | |
30 int qdiff; | |
31 int merge; | |
32 int verbose; | |
33 char *host; | |
34 VtConn *z; | |
35 VacFs *fs; | |
36 char *archivefile; | |
37 char *vacfile; | |
38 | |
39 int vacmerge(VacFile*, char*); | |
40 void vac(VacFile*, VacFile*, char*, Dir*); | |
41 void vacstdin(VacFile*, char*); | |
42 VacFile *recentarchive(VacFs*, char*); | |
43 | |
44 static u64int unittoull(char*); | |
45 static void warn(char *fmt, ...); | |
46 static void removevacfile(void); | |
47 | |
48 #ifdef PLAN9PORT | |
49 /* | |
50 * We're between a rock and a hard place here. | |
51 * The pw library (getpwnam, etc.) reads the | |
52 * password and group files into an on-stack buffer, | |
53 * so if you have some huge groups, you overflow | |
54 * the stack. Because of this, the thread library turns | |
55 * it off by default, so that dirstat returns "14571" instead of "rsc". | |
56 * But for vac we want names. So cautiously turn the pwlibrary | |
57 * back on (see threadmain) and make the main thread stack huge. | |
58 */ | |
59 extern int _p9usepwlibrary; | |
60 int mainstacksize = 4*1024*1024; | |
61 | |
62 #endif | |
63 void | |
64 threadmain(int argc, char **argv) | |
65 { | |
66 int i, j, fd, n, printstats; | |
67 Dir *d; | |
68 char *s; | |
69 uvlong u; | |
70 VacFile *f, *fdiff; | |
71 VacFs *fsdiff; | |
72 int blocksize; | |
73 int outfd; | |
74 char *stdinname; | |
75 char *diffvac; | |
76 uvlong qid; | |
77 | |
78 #ifdef PLAN9PORT | |
79 /* see comment above */ | |
80 _p9usepwlibrary = 1; | |
81 #endif | |
82 | |
83 fmtinstall('F', vtfcallfmt); | |
84 fmtinstall('H', encodefmt); | |
85 fmtinstall('V', vtscorefmt); | |
86 | |
87 blocksize = BlockSize; | |
88 stdinname = nil; | |
89 printstats = 0; | |
90 fsdiff = nil; | |
91 diffvac = nil; | |
92 | |
93 ARGBEGIN{ | |
94 case 'V': | |
95 chattyventi++; | |
96 break; | |
97 case 'a': | |
98 archivefile = EARGF(usage()); | |
99 break; | |
100 case 'b': | |
101 u = unittoull(EARGF(usage())); | |
102 if(u < 512) | |
103 u = 512; | |
104 blocksize = u; | |
105 break; | |
106 case 'd': | |
107 diffvac = EARGF(usage()); | |
108 break; | |
109 case 'e': | |
110 excludepattern(EARGF(usage())); | |
111 break; | |
112 case 'f': | |
113 vacfile = EARGF(usage()); | |
114 break; | |
115 case 'h': | |
116 host = EARGF(usage()); | |
117 break; | |
118 case 'i': | |
119 stdinname = EARGF(usage()); | |
120 break; | |
121 case 'm': | |
122 merge++; | |
123 break; | |
124 case 'q': | |
125 qdiff++; | |
126 break; | |
127 case 's': | |
128 printstats++; | |
129 break; | |
130 case 'v': | |
131 verbose++; | |
132 break; | |
133 case 'x': | |
134 loadexcludefile(EARGF(usage())); | |
135 break; | |
136 default: | |
137 usage(); | |
138 }ARGEND | |
139 | |
140 if(argc == 0 && !stdinname) | |
141 usage(); | |
142 | |
143 if(archivefile && (vacfile || diffvac)){ | |
144 fprint(2, "cannot use -a with -f, -d\n"); | |
145 usage(); | |
146 } | |
147 | |
148 z = vtdial(host); | |
149 if(z == nil) | |
150 sysfatal("could not connect to server: %r"); | |
151 if(vtconnect(z) < 0) | |
152 sysfatal("vtconnect: %r"); | |
153 | |
154 // Setup: | |
155 // fs is the output vac file system | |
156 // f is directory in output vac to write new files | |
157 // fdiff is corresponding directory in existing vac | |
158 if(archivefile){ | |
159 VacFile *fp; | |
160 char yyyy[5]; | |
161 char mmdd[10]; | |
162 char oldpath[40]; | |
163 Tm tm; | |
164 | |
165 fdiff = nil; | |
166 if((outfd = open(archivefile, ORDWR)) < 0){ | |
167 if(access(archivefile, 0) >= 0) | |
168 sysfatal("open %s: %r", archivefile); | |
169 if((outfd = create(archivefile, OWRITE, 0666)) <… | |
170 sysfatal("create %s: %r", archivefile); | |
171 atexit(removevacfile); // because it is n… | |
172 if((fs = vacfscreate(z, blocksize, CacheSize)) =… | |
173 sysfatal("vacfscreate: %r"); | |
174 }else{ | |
175 if((fs = vacfsopen(z, archivefile, VtORDWR, Cach… | |
176 sysfatal("vacfsopen %s: %r", archivefile… | |
177 if((fdiff = recentarchive(fs, oldpath)) != nil){ | |
178 if(verbose) | |
179 fprint(2, "diff %s\n", oldpath); | |
180 }else | |
181 if(verbose) | |
182 fprint(2, "no recent archive to … | |
183 } | |
184 | |
185 // Create yyyy/mmdd. | |
186 tm = *localtime(time(0)); | |
187 snprint(yyyy, sizeof yyyy, "%04d", tm.year+1900); | |
188 fp = vacfsgetroot(fs); | |
189 if((f = vacfilewalk(fp, yyyy)) == nil | |
190 && (f = vacfilecreate(fp, yyyy, ModeDir|0555)) == nil) | |
191 sysfatal("vacfscreate %s: %r", yyyy); | |
192 vacfiledecref(fp); | |
193 fp = f; | |
194 | |
195 snprint(mmdd, sizeof mmdd, "%02d%02d", tm.mon+1, tm.mday… | |
196 n = 0; | |
197 while((f = vacfilewalk(fp, mmdd)) != nil){ | |
198 vacfiledecref(f); | |
199 n++; | |
200 snprint(mmdd+4, sizeof mmdd-4, ".%d", n); | |
201 } | |
202 f = vacfilecreate(fp, mmdd, ModeDir|0555); | |
203 if(f == nil) | |
204 sysfatal("vacfscreate %s/%s: %r", yyyy, mmdd); | |
205 vacfiledecref(fp); | |
206 | |
207 if(verbose) | |
208 fprint(2, "archive %s/%s\n", yyyy, mmdd); | |
209 }else{ | |
210 if(vacfile == nil) | |
211 outfd = 1; | |
212 else if((outfd = create(vacfile, OWRITE, 0666)) < 0) | |
213 sysfatal("create %s: %r", vacfile); | |
214 atexit(removevacfile); | |
215 if((fs = vacfscreate(z, blocksize, CacheSize)) == nil) | |
216 sysfatal("vacfscreate: %r"); | |
217 f = vacfsgetroot(fs); | |
218 | |
219 fdiff = nil; | |
220 if(diffvac){ | |
221 if((fsdiff = vacfsopen(z, diffvac, VtOREAD, Cach… | |
222 warn("vacfsopen %s: %r", diffvac); | |
223 else | |
224 fdiff = vacfsgetroot(fsdiff); | |
225 } | |
226 } | |
227 | |
228 if(stdinname) | |
229 vacstdin(f, stdinname); | |
230 for(i=0; i<argc; i++){ | |
231 // We can't use / and . and .. and ../.. as valid archive | |
232 // names, so expand to the list of files in the director… | |
233 if(argv[i][0] == 0){ | |
234 warn("empty string given as command-line argumen… | |
235 continue; | |
236 } | |
237 cleanname(argv[i]); | |
238 if(strcmp(argv[i], "/") == 0 | |
239 || strcmp(argv[i], ".") == 0 | |
240 || strcmp(argv[i], "..") == 0 | |
241 || (strlen(argv[i]) > 3 && strcmp(argv[i]+strlen(argv[i]… | |
242 if((fd = open(argv[i], OREAD)) < 0){ | |
243 warn("open %s: %r", argv[i]); | |
244 continue; | |
245 } | |
246 while((n = dirread(fd, &d)) > 0){ | |
247 for(j=0; j<n; j++){ | |
248 s = vtmalloc(strlen(argv[i])+1+s… | |
249 strcpy(s, argv[i]); | |
250 strcat(s, "/"); | |
251 strcat(s, d[j].name); | |
252 cleanname(s); | |
253 vac(f, fdiff, s, &d[j]); | |
254 } | |
255 free(d); | |
256 } | |
257 close(fd); | |
258 continue; | |
259 } | |
260 if((d = dirstat(argv[i])) == nil){ | |
261 warn("stat %s: %r", argv[i]); | |
262 continue; | |
263 } | |
264 vac(f, fdiff, argv[i], d); | |
265 free(d); | |
266 } | |
267 if(fdiff) | |
268 vacfiledecref(fdiff); | |
269 | |
270 /* | |
271 * Record the maximum qid so that vacs can be merged | |
272 * without introducing overlapping qids. Older versions | |
273 * of vac arranged that the root would have the largest | |
274 * qid in the file system, but we can't do that anymore | |
275 * (the root gets created first!). | |
276 */ | |
277 if(_vacfsnextqid(fs, &qid) >= 0) | |
278 vacfilesetqidspace(f, 0, qid); | |
279 vacfiledecref(f); | |
280 | |
281 /* | |
282 * Copy fsdiff's root block score into fs's slot for that, | |
283 * so that vacfssync will copy it into root.prev for us. | |
284 * Just nice documentation, no effect. | |
285 */ | |
286 if(fsdiff) | |
287 memmove(fs->score, fsdiff->score, VtScoreSize); | |
288 if(vacfssync(fs) < 0) | |
289 fprint(2, "vacfssync: %r\n"); | |
290 | |
291 fprint(outfd, "vac:%V\n", fs->score); | |
292 atexitdont(removevacfile); | |
293 vacfsclose(fs); | |
294 vthangup(z); | |
295 | |
296 if(printstats){ | |
297 fprint(2, | |
298 "%d files, %d files skipped, %d directories\n" | |
299 "%lld data bytes written, %lld data bytes skippe… | |
300 stats.nfile, stats.skipfiles, stats.ndir, stats.… | |
301 dup(2, 1); | |
302 packetstats(); | |
303 } | |
304 threadexitsall(0); | |
305 } | |
306 | |
307 VacFile* | |
308 recentarchive(VacFs *fs, char *path) | |
309 { | |
310 VacFile *fp, *f; | |
311 VacDirEnum *de; | |
312 VacDir vd; | |
313 char buf[10]; | |
314 int year, mmdd, nn, n, n1; | |
315 char *p; | |
316 | |
317 fp = vacfsgetroot(fs); | |
318 de = vdeopen(fp); | |
319 year = 0; | |
320 if(de){ | |
321 for(; vderead(de, &vd) > 0; vdcleanup(&vd)){ | |
322 if(strlen(vd.elem) != 4) | |
323 continue; | |
324 if((n = strtol(vd.elem, &p, 10)) < 1900 || *p !=… | |
325 continue; | |
326 if(year < n) | |
327 year = n; | |
328 } | |
329 } | |
330 vdeclose(de); | |
331 if(year == 0){ | |
332 vacfiledecref(fp); | |
333 return nil; | |
334 } | |
335 snprint(buf, sizeof buf, "%04d", year); | |
336 if((f = vacfilewalk(fp, buf)) == nil){ | |
337 fprint(2, "warning: dirread %s but cannot walk", buf); | |
338 vacfiledecref(fp); | |
339 return nil; | |
340 } | |
341 fp = f; | |
342 | |
343 de = vdeopen(fp); | |
344 mmdd = 0; | |
345 nn = 0; | |
346 if(de){ | |
347 for(; vderead(de, &vd) > 0; vdcleanup(&vd)){ | |
348 if(strlen(vd.elem) < 4) | |
349 continue; | |
350 if((n = strtol(vd.elem, &p, 10)) < 100 || n > 12… | |
351 continue; | |
352 if(*p == '.'){ | |
353 if(p[1] == '0' || (n1 = strtol(p+1, &p, … | |
354 continue; | |
355 }else{ | |
356 if(*p != 0) | |
357 continue; | |
358 n1 = 0; | |
359 } | |
360 if(n < mmdd || (n == mmdd && n1 < nn)) | |
361 continue; | |
362 mmdd = n; | |
363 nn = n1; | |
364 } | |
365 } | |
366 vdeclose(de); | |
367 if(mmdd == 0){ | |
368 vacfiledecref(fp); | |
369 return nil; | |
370 } | |
371 if(nn == 0) | |
372 snprint(buf, sizeof buf, "%04d", mmdd); | |
373 else | |
374 snprint(buf, sizeof buf, "%04d.%d", mmdd, nn); | |
375 if((f = vacfilewalk(fp, buf)) == nil){ | |
376 fprint(2, "warning: dirread %s but cannot walk", buf); | |
377 vacfiledecref(fp); | |
378 return nil; | |
379 } | |
380 vacfiledecref(fp); | |
381 | |
382 sprint(path, "%04d/%s", year, buf); | |
383 return f; | |
384 } | |
385 | |
386 static void | |
387 removevacfile(void) | |
388 { | |
389 if(vacfile) | |
390 remove(vacfile); | |
391 } | |
392 | |
393 void | |
394 plan9tovacdir(VacDir *vd, Dir *dir) | |
395 { | |
396 memset(vd, 0, sizeof *vd); | |
397 | |
398 vd->elem = dir->name; | |
399 vd->uid = dir->uid; | |
400 vd->gid = dir->gid; | |
401 vd->mid = dir->muid; | |
402 if(vd->mid == nil) | |
403 vd->mid = ""; | |
404 vd->mtime = dir->mtime; | |
405 vd->mcount = 0; | |
406 vd->ctime = dir->mtime; /* ctime: not available o… | |
407 vd->atime = dir->atime; | |
408 vd->size = dir->length; | |
409 | |
410 vd->mode = dir->mode & 0777; | |
411 if(dir->mode & DMDIR) | |
412 vd->mode |= ModeDir; | |
413 if(dir->mode & DMAPPEND) | |
414 vd->mode |= ModeAppend; | |
415 if(dir->mode & DMEXCL) | |
416 vd->mode |= ModeExclusive; | |
417 #ifdef PLAN9PORT | |
418 if(dir->mode & DMDEVICE) | |
419 vd->mode |= ModeDevice; | |
420 if(dir->mode & DMNAMEDPIPE) | |
421 vd->mode |= ModeNamedPipe; | |
422 if(dir->mode & DMSYMLINK) | |
423 vd->mode |= ModeLink; | |
424 #endif | |
425 | |
426 vd->plan9 = 1; | |
427 vd->p9path = dir->qid.path; | |
428 vd->p9version = dir->qid.vers; | |
429 } | |
430 | |
431 #ifdef PLAN9PORT | |
432 enum { | |
433 Special = | |
434 DMSOCKET | | |
435 DMSYMLINK | | |
436 DMNAMEDPIPE | | |
437 DMDEVICE | |
438 }; | |
439 #endif | |
440 | |
441 /* | |
442 * Archive the file named name, which has stat info d, | |
443 * into the vac directory fp (p = parent). | |
444 * | |
445 * If we're doing a vac -d against another archive, the | |
446 * equivalent directory to fp in that archive is diffp. | |
447 */ | |
448 void | |
449 vac(VacFile *fp, VacFile *diffp, char *name, Dir *d) | |
450 { | |
451 char *elem, *s; | |
452 static char *buf; | |
453 int fd, i, n, bsize; | |
454 vlong off; | |
455 Dir *dk; // kids | |
456 VacDir vd, vddiff; | |
457 VacFile *f, *fdiff; | |
458 VtEntry e; | |
459 | |
460 if(!includefile(name)){ | |
461 warn("excluding %s%s", name, (d->mode&DMDIR) ? "/" : ""); | |
462 return; | |
463 } | |
464 | |
465 if(d->mode&DMDIR) | |
466 stats.ndir++; | |
467 else | |
468 stats.nfile++; | |
469 | |
470 if(merge && vacmerge(fp, name) >= 0) | |
471 return; | |
472 | |
473 if(verbose) | |
474 fprint(2, "%s%s\n", name, (d->mode&DMDIR) ? "/" : ""); | |
475 | |
476 #ifdef PLAN9PORT | |
477 if(d->mode&Special) | |
478 fd = -1; | |
479 else | |
480 #endif | |
481 if((fd = open(name, OREAD)) < 0){ | |
482 warn("open %s: %r", name); | |
483 return; | |
484 } | |
485 | |
486 elem = strrchr(name, '/'); | |
487 if(elem) | |
488 elem++; | |
489 else | |
490 elem = name; | |
491 | |
492 plan9tovacdir(&vd, d); | |
493 if((f = vacfilecreate(fp, elem, vd.mode)) == nil){ | |
494 warn("vacfilecreate %s: %r", name); | |
495 return; | |
496 } | |
497 if(diffp) | |
498 fdiff = vacfilewalk(diffp, elem); | |
499 else | |
500 fdiff = nil; | |
501 | |
502 if(vacfilesetdir(f, &vd) < 0) | |
503 warn("vacfilesetdir %s: %r", name); | |
504 | |
505 bsize = fs->bsize; | |
506 if(buf == nil) | |
507 buf = vtmallocz(bsize); | |
508 | |
509 #ifdef PLAN9PORT | |
510 if(d->mode&(DMSOCKET|DMNAMEDPIPE)){ | |
511 /* don't write anything */ | |
512 } | |
513 else if(d->mode&DMSYMLINK){ | |
514 n = readlink(name, buf, sizeof buf); | |
515 if(n > 0 && vacfilewrite(f, buf, n, 0) < 0){ | |
516 warn("venti write %s: %r", name); | |
517 goto Out; | |
518 } | |
519 stats.data += n; | |
520 }else if(d->mode&DMDEVICE){ | |
521 snprint(buf, sizeof buf, "%c %d %d", | |
522 (char)((d->qid.path >> 16) & 0xFF), | |
523 (int)(d->qid.path & 0xFF), | |
524 (int)((d->qid.path >> 8) & 0xFF)); | |
525 if(vacfilewrite(f, buf, strlen(buf), 0) < 0){ | |
526 warn("venti write %s: %r", name); | |
527 goto Out; | |
528 } | |
529 stats.data += strlen(buf); | |
530 }else | |
531 #endif | |
532 if(d->mode&DMDIR){ | |
533 while((n = dirread(fd, &dk)) > 0){ | |
534 for(i=0; i<n; i++){ | |
535 s = vtmalloc(strlen(name)+1+strlen(dk[i]… | |
536 strcpy(s, name); | |
537 strcat(s, "/"); | |
538 strcat(s, dk[i].name); | |
539 vac(f, fdiff, s, &dk[i]); | |
540 free(s); | |
541 } | |
542 free(dk); | |
543 } | |
544 }else{ | |
545 off = 0; | |
546 if(fdiff){ | |
547 /* | |
548 * Copy fdiff's contents into f by moving the sc… | |
549 * We'll diff and update below. | |
550 */ | |
551 if(vacfilegetentries(fdiff, &e, nil) >= 0) | |
552 if(vacfilesetentries(f, &e, nil) >= 0){ | |
553 bsize = e.dsize; | |
554 | |
555 /* | |
556 * Or if -q is set, and the metadata loo… | |
557 * don't even bother reading the file. | |
558 */ | |
559 if(qdiff && vacfilegetdir(fdiff, &vddiff… | |
560 if(vddiff.mtime == vd.mtime) | |
561 if(vddiff.size == vd.size) | |
562 if(!vddiff.plan9 || (/* vddiff.p… | |
563 stats.skipfiles++; | |
564 stats.nfile--; | |
565 vdcleanup(&vddiff); | |
566 goto Out; | |
567 } | |
568 | |
569 /* | |
570 * Skip over presumably-unchange… | |
571 * of an append-only file. | |
572 */ | |
573 if(vd.mode&ModeAppend) | |
574 if(vddiff.size < vd.size) | |
575 if(vddiff.plan9 && vd.plan9) | |
576 if(vddiff.p9path == vd.p9path){ | |
577 off = vd.size/bsize*bsiz… | |
578 if(seek(fd, off, 0) >= 0) | |
579 stats.skipdata +… | |
580 else{ | |
581 seek(fd, 0, 0); … | |
582 off = 0; | |
583 } | |
584 } | |
585 | |
586 vdcleanup(&vddiff); | |
587 // XXX different verbose chatty … | |
588 } | |
589 } | |
590 } | |
591 if(qdiff && verbose) | |
592 fprint(2, "+%s\n", name); | |
593 while((n = readn(fd, buf, bsize)) > 0){ | |
594 if(fdiff && sha1matches(f, off/bsize, (uchar*)bu… | |
595 off += n; | |
596 stats.skipdata += n; | |
597 continue; | |
598 } | |
599 if(vacfilewrite(f, buf, n, off) < 0){ | |
600 warn("venti write %s: %r", name); | |
601 goto Out; | |
602 } | |
603 stats.data += n; | |
604 off += n; | |
605 } | |
606 /* | |
607 * Since we started with fdiff's contents, | |
608 * set the size in case fdiff was bigger. | |
609 */ | |
610 if(fdiff && vacfilesetsize(f, off) < 0) | |
611 warn("vtfilesetsize %s: %r", name); | |
612 } | |
613 | |
614 Out: | |
615 vacfileflush(f, 1); | |
616 vacfiledecref(f); | |
617 if(fdiff) | |
618 vacfiledecref(fdiff); | |
619 close(fd); | |
620 } | |
621 | |
622 void | |
623 vacstdin(VacFile *fp, char *name) | |
624 { | |
625 vlong off; | |
626 VacFile *f; | |
627 static char buf[8192]; | |
628 int n; | |
629 | |
630 if((f = vacfilecreate(fp, name, 0666)) == nil){ | |
631 warn("vacfilecreate %s: %r", name); | |
632 return; | |
633 } | |
634 | |
635 off = 0; | |
636 while((n = read(0, buf, sizeof buf)) > 0){ | |
637 if(vacfilewrite(f, buf, n, off) < 0){ | |
638 warn("venti write %s: %r", name); | |
639 vacfiledecref(f); | |
640 return; | |
641 } | |
642 off += n; | |
643 } | |
644 vacfileflush(f, 1); | |
645 vacfiledecref(f); | |
646 } | |
647 | |
648 /* | |
649 * fp is the directory we're writing. | |
650 * mp is the directory whose contents we're merging in. | |
651 * d is the directory entry of the file from mp that we want to add to f… | |
652 * vacfile is the name of the .vac file, for error messages. | |
653 * offset is the qid that qid==0 in mp should correspond to. | |
654 * max is the maximum qid we expect to see (not really needed). | |
655 */ | |
656 int | |
657 vacmergefile(VacFile *fp, VacFile *mp, VacDir *d, char *vacfile, | |
658 vlong offset, vlong max) | |
659 { | |
660 VtEntry ed, em; | |
661 VacFile *mf; | |
662 VacFile *f; | |
663 | |
664 mf = vacfilewalk(mp, d->elem); | |
665 if(mf == nil){ | |
666 warn("could not walk %s in %s", d->elem, vacfile); | |
667 return -1; | |
668 } | |
669 if(vacfilegetentries(mf, &ed, &em) < 0){ | |
670 warn("could not get entries for %s in %s", d->elem, vacf… | |
671 vacfiledecref(mf); | |
672 return -1; | |
673 } | |
674 | |
675 if((f = vacfilecreate(fp, d->elem, d->mode)) == nil){ | |
676 warn("vacfilecreate %s: %r", d->elem); | |
677 vacfiledecref(mf); | |
678 return -1; | |
679 } | |
680 if(d->qidspace){ | |
681 d->qidoffset += offset; | |
682 d->qidmax += offset; | |
683 }else{ | |
684 d->qidspace = 1; | |
685 d->qidoffset = offset; | |
686 d->qidmax = max; | |
687 } | |
688 if(vacfilesetdir(f, d) < 0 | |
689 || vacfilesetentries(f, &ed, &em) < 0 | |
690 || vacfilesetqidspace(f, d->qidoffset, d->qidmax) < 0){ | |
691 warn("vacmergefile %s: %r", d->elem); | |
692 vacfiledecref(mf); | |
693 vacfiledecref(f); | |
694 return -1; | |
695 } | |
696 | |
697 vacfiledecref(mf); | |
698 vacfiledecref(f); | |
699 return 0; | |
700 } | |
701 | |
702 int | |
703 vacmerge(VacFile *fp, char *name) | |
704 { | |
705 VacFs *mfs; | |
706 VacDir vd; | |
707 VacDirEnum *de; | |
708 VacFile *mp; | |
709 uvlong maxqid, offset; | |
710 | |
711 if(strlen(name) < 4 || strcmp(name+strlen(name)-4, ".vac") != 0) | |
712 return -1; | |
713 if((mfs = vacfsopen(z, name, VtOREAD, CacheSize)) == nil) | |
714 return -1; | |
715 if(verbose) | |
716 fprint(2, "merging %s\n", name); | |
717 | |
718 mp = vacfsgetroot(mfs); | |
719 de = vdeopen(mp); | |
720 if(de){ | |
721 offset = 0; | |
722 if(vacfsgetmaxqid(mfs, &maxqid) >= 0){ | |
723 _vacfsnextqid(fs, &offset); | |
724 vacfsjumpqid(fs, maxqid+1); | |
725 } | |
726 while(vderead(de, &vd) > 0){ | |
727 if(vd.qid > maxqid){ | |
728 warn("vacmerge %s: maxqid=%lld but %s ha… | |
729 name, maxqid, vd.elem, vd.qid); | |
730 vacfsjumpqid(fs, vd.qid - maxqid); | |
731 maxqid = vd.qid; | |
732 } | |
733 vacmergefile(fp, mp, &vd, name, | |
734 offset, maxqid); | |
735 vdcleanup(&vd); | |
736 } | |
737 vdeclose(de); | |
738 } | |
739 vacfiledecref(mp); | |
740 vacfsclose(mfs); | |
741 return 0; | |
742 } | |
743 | |
744 #define TWID64 ((u64int)~(u64int)0) | |
745 | |
746 static u64int | |
747 unittoull(char *s) | |
748 { | |
749 char *es; | |
750 u64int n; | |
751 | |
752 if(s == nil) | |
753 return TWID64; | |
754 n = strtoul(s, &es, 0); | |
755 if(*es == 'k' || *es == 'K'){ | |
756 n *= 1024; | |
757 es++; | |
758 }else if(*es == 'm' || *es == 'M'){ | |
759 n *= 1024*1024; | |
760 es++; | |
761 }else if(*es == 'g' || *es == 'G'){ | |
762 n *= 1024*1024*1024; | |
763 es++; | |
764 } | |
765 if(*es != '\0') | |
766 return TWID64; | |
767 return n; | |
768 } | |
769 | |
770 static void | |
771 warn(char *fmt, ...) | |
772 { | |
773 va_list arg; | |
774 | |
775 va_start(arg, fmt); | |
776 fprint(2, "vac: "); | |
777 vfprint(2, fmt, arg); | |
778 fprint(2, "\n"); | |
779 va_end(arg); | |
780 } |