URI: 
       tvac.c - plan9port - [fork] Plan 9 from user space
  HTML git clone git://src.adamsgaard.dk/plan9port
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR 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] [-f new.vac] [-e exclude]... [-h host] file...\n");
           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)) < 0)
          170                                 sysfatal("create %s: %r", archivefile);
          171                         atexit(removevacfile);        // because it is new
          172                         if((fs = vacfscreate(z, blocksize, CacheSize)) == nil)
          173                                 sysfatal("vacfscreate: %r");
          174                 }else{
          175                         if((fs = vacfsopen(z, archivefile, VtORDWR, CacheSize)) == nil)
          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 diff against\n");
          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, CacheSize)) == nil)
          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 directory.
          233                 if(argv[i][0] == 0){
          234                         warn("empty string given as command-line argument");
          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])-3, "/..") == 0)){
          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+strlen(d[j].name)+1);
          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 skipped\n",
          300                         stats.nfile, stats.skipfiles, stats.ndir, stats.data, stats.skipdata);
          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 != 0)
          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 > 1231 || p != vd.elem+4)
          351                                 continue;
          352                         if(*p == '.'){
          353                                 if(p[1] == '0' || (n1 = strtol(p+1, &p, 10)) == 0 || *p != 0)
          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 on plan 9 */
          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].name)+1);
          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 score.
          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 looks the same,
          557                                  * don't even bother reading the file.
          558                                  */
          559                                 if(qdiff && vacfilegetdir(fdiff, &vddiff) >= 0){
          560                                         if(vddiff.mtime == vd.mtime)
          561                                         if(vddiff.size == vd.size)
          562                                         if(!vddiff.plan9 || (/* vddiff.p9path == vd.p9path && */ vddiff.p9version == vd.p9version)){
          563                                                 stats.skipfiles++;
          564                                                 stats.nfile--;
          565                                                 vdcleanup(&vddiff);
          566                                                 goto Out;
          567                                         }
          568 
          569                                         /*
          570                                          * Skip over presumably-unchanged prefix
          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*bsize;
          578                                                 if(seek(fd, off, 0) >= 0)
          579                                                         stats.skipdata += off;
          580                                                 else{
          581                                                         seek(fd, 0, 0);        // paranoia
          582                                                         off = 0;
          583                                                 }
          584                                         }
          585 
          586                                         vdcleanup(&vddiff);
          587                                         // XXX different verbose chatty prints for kaminsky?
          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*)buf, n)){
          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 fp.
          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, vacfile);
          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 has %lld",
          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 }