vac.c 14 KB

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