PageRenderTime 24ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/src/cmd/vbackup/vbackup.c

https://bitbucket.org/rsc/plan9port/
C | 620 lines | 486 code | 62 blank | 72 comment | 124 complexity | bef73ef554f39e6db63046b891912ced MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, Unlicense
  1. /*
  2. * vbackup [-Dnv] fspartition [score]
  3. *
  4. * Copy a file system to a disk image stored on Venti.
  5. * Prints a vnfs config line for the copied image.
  6. *
  7. * -D print debugging
  8. * -f 'fast' writes - skip write if block exists on server
  9. * -i read old scores incrementally
  10. * -m set mount name
  11. * -M set mount place
  12. * -n nop -- don't actually write blocks
  13. * -s print status updates
  14. * -v print debugging trace
  15. * -w write parallelism
  16. *
  17. * If score is given on the command line, it should be the
  18. * score from a previous vbackup on this fspartition.
  19. * In this mode, only the new blocks are stored to Venti.
  20. * The result is still a complete image, but requires many
  21. * fewer Venti writes in the common case.
  22. *
  23. * This program is structured as three processes connected
  24. * by buffered queues:
  25. *
  26. * fsysproc | cmpproc | ventiproc
  27. *
  28. * Fsysproc reads the disk and queues the blocks.
  29. * Cmpproc compares the blocks against the SHA1 hashes
  30. * in the old image, if any. It discards the unchanged blocks
  31. * and queues the changed ones. Ventiproc writes blocks to Venti.
  32. *
  33. * There is a fourth proc, statusproc, which prints status
  34. * updates about how the various procs are progressing.
  35. */
  36. #include <u.h>
  37. #include <libc.h>
  38. #include <bio.h>
  39. #include <thread.h>
  40. #include <libsec.h>
  41. #include <venti.h>
  42. #include <diskfs.h>
  43. #include "queue.h"
  44. enum
  45. {
  46. STACK = 32768
  47. };
  48. typedef struct WriteReq WriteReq;
  49. struct WriteReq
  50. {
  51. Packet *p;
  52. uint type;
  53. uchar score[VtScoreSize];
  54. };
  55. Biobuf bscores; /* biobuf filled with block scores */
  56. int debug; /* debugging flag (not used) */
  57. Disk* disk; /* disk being backed up */
  58. RWLock endlk; /* silly synchonization */
  59. int errors; /* are we exiting with an error status? */
  60. int fastwrites; /* do not write blocks already on server */
  61. int fsscanblock; /* last block scanned */
  62. Fsys* fsys; /* file system being backed up */
  63. int incremental; /* use vscores rather than bscores */
  64. int nchange; /* number of changed blocks */
  65. int nop; /* don't actually send blocks to venti */
  66. int nskip; /* number of blocks skipped (already on server) */
  67. int nwritethread; /* number of write-behind threads */
  68. Queue* qcmp; /* queue fsys->cmp */
  69. Queue* qventi; /* queue cmp->venti */
  70. int statustime; /* print status every _ seconds */
  71. int verbose; /* print extra stuff */
  72. VtFile* vfile; /* venti file being written */
  73. VtFile* vscores; /* venti file with block scores */
  74. Channel* writechan; /* chan(WriteReq) */
  75. VtConn* z; /* connection to venti */
  76. VtCache* zcache; /* cache of venti blocks */
  77. uchar* zero; /* blocksize zero bytes */
  78. int nsend, nrecv;
  79. void cmpproc(void*);
  80. void fsysproc(void*);
  81. void statusproc(void*);
  82. void ventiproc(void*);
  83. int timefmt(Fmt*);
  84. char* guessmountplace(char *dev);
  85. void
  86. usage(void)
  87. {
  88. fprint(2, "usage: vbackup [-DVnv] [-m mtpt] [-M mtpl] [-s secs] [-w n] disk [score]\n");
  89. threadexitsall("usage");
  90. }
  91. void
  92. threadmain(int argc, char **argv)
  93. {
  94. char *pref, *mountname, *mountplace;
  95. uchar score[VtScoreSize], prev[VtScoreSize];
  96. int i, fd, csize;
  97. vlong bsize;
  98. Tm tm;
  99. VtEntry e;
  100. VtBlock *b;
  101. VtCache *c;
  102. VtRoot root;
  103. char *tmp, *tmpnam;
  104. fmtinstall('F', vtfcallfmt);
  105. fmtinstall('H', encodefmt);
  106. fmtinstall('T', timefmt);
  107. fmtinstall('V', vtscorefmt);
  108. mountname = sysname();
  109. mountplace = nil;
  110. ARGBEGIN{
  111. default:
  112. usage();
  113. break;
  114. case 'D':
  115. debug++;
  116. break;
  117. case 'V':
  118. chattyventi = 1;
  119. break;
  120. case 'f':
  121. fastwrites = 1;
  122. break;
  123. case 'i':
  124. incremental = 1;
  125. break;
  126. case 'm':
  127. mountname = EARGF(usage());
  128. break;
  129. case 'M':
  130. mountplace = EARGF(usage());
  131. i = strlen(mountplace);
  132. if(i > 0 && mountplace[i-1] == '/')
  133. mountplace[i-1] = 0;
  134. break;
  135. case 'n':
  136. nop = 1;
  137. break;
  138. case 's':
  139. statustime = atoi(EARGF(usage()));
  140. break;
  141. case 'v':
  142. verbose = 1;
  143. break;
  144. case 'w':
  145. nwritethread = atoi(EARGF(usage()));
  146. break;
  147. }ARGEND
  148. if(argc != 1 && argc != 2)
  149. usage();
  150. if(statustime)
  151. print("# %T vbackup %s %s\n", argv[0], argc>=2 ? argv[1] : "");
  152. /*
  153. * open fs
  154. */
  155. if((disk = diskopenfile(argv[0])) == nil)
  156. sysfatal("diskopen: %r");
  157. if((disk = diskcache(disk, 32768, 2*MAXQ+16)) == nil)
  158. sysfatal("diskcache: %r");
  159. if((fsys = fsysopen(disk)) == nil)
  160. sysfatal("fsysopen: %r");
  161. /*
  162. * connect to venti
  163. */
  164. if((z = vtdial(nil)) == nil)
  165. sysfatal("vtdial: %r");
  166. if(vtconnect(z) < 0)
  167. sysfatal("vtconnect: %r");
  168. /*
  169. * set up venti block cache
  170. */
  171. zero = vtmallocz(fsys->blocksize);
  172. bsize = fsys->blocksize;
  173. csize = 50; /* plenty; could probably do with 5 */
  174. if(verbose)
  175. fprint(2, "cache %d blocks\n", csize);
  176. c = vtcachealloc(z, bsize*csize);
  177. zcache = c;
  178. /*
  179. * parse starting score
  180. */
  181. memset(prev, 0, sizeof prev);
  182. if(argc == 1){
  183. vfile = vtfilecreateroot(c, (fsys->blocksize/VtScoreSize)*VtScoreSize,
  184. fsys->blocksize, VtDataType);
  185. if(vfile == nil)
  186. sysfatal("vtfilecreateroot: %r");
  187. vtfilelock(vfile, VtORDWR);
  188. if(vtfilewrite(vfile, zero, 1, bsize*fsys->nblock-1) != 1)
  189. sysfatal("vtfilewrite: %r");
  190. if(vtfileflush(vfile) < 0)
  191. sysfatal("vtfileflush: %r");
  192. }else{
  193. if(vtparsescore(argv[1], &pref, score) < 0)
  194. sysfatal("bad score: %r");
  195. if(pref!=nil && strcmp(pref, fsys->type) != 0)
  196. sysfatal("score is %s but fsys is %s", pref, fsys->type);
  197. b = vtcacheglobal(c, score, VtRootType, VtRootSize);
  198. if(b){
  199. if(vtrootunpack(&root, b->data) < 0)
  200. sysfatal("bad root: %r");
  201. if(strcmp(root.type, fsys->type) != 0)
  202. sysfatal("root is %s but fsys is %s", root.type, fsys->type);
  203. memmove(prev, score, VtScoreSize);
  204. memmove(score, root.score, VtScoreSize);
  205. vtblockput(b);
  206. }
  207. b = vtcacheglobal(c, score, VtDirType, VtEntrySize);
  208. if(b == nil)
  209. sysfatal("vtcacheglobal %V: %r", score);
  210. if(vtentryunpack(&e, b->data, 0) < 0)
  211. sysfatal("%V: vtentryunpack failed", score);
  212. if(verbose)
  213. fprint(2, "entry: size %llud psize %d dsize %d\n",
  214. e.size, e.psize, e.dsize);
  215. vtblockput(b);
  216. if((vfile = vtfileopenroot(c, &e)) == nil)
  217. sysfatal("vtfileopenroot: %r");
  218. vtfilelock(vfile, VtORDWR);
  219. if(e.dsize != bsize)
  220. sysfatal("file system block sizes don't match %d %lld", e.dsize, bsize);
  221. if(e.size != fsys->nblock*bsize)
  222. sysfatal("file system block counts don't match %lld %lld", e.size, fsys->nblock*bsize);
  223. }
  224. tmpnam = nil;
  225. if(incremental){
  226. if(vtfilegetentry(vfile, &e) < 0)
  227. sysfatal("vtfilegetentry: %r");
  228. if((vscores = vtfileopenroot(c, &e)) == nil)
  229. sysfatal("vtfileopenroot: %r");
  230. vtfileunlock(vfile);
  231. }else{
  232. /*
  233. * write scores of blocks into temporary file
  234. */
  235. if((tmp = getenv("TMP")) != nil){
  236. /* okay, good */
  237. }else if(access("/var/tmp", 0) >= 0)
  238. tmp = "/var/tmp";
  239. else
  240. tmp = "/tmp";
  241. tmpnam = smprint("%s/vbackup.XXXXXX", tmp);
  242. if(tmpnam == nil)
  243. sysfatal("smprint: %r");
  244. if((fd = opentemp(tmpnam, ORDWR|ORCLOSE)) < 0)
  245. sysfatal("opentemp %s: %r", tmpnam);
  246. if(statustime)
  247. print("# %T reading scores into %s\n", tmpnam);
  248. if(verbose)
  249. fprint(2, "read scores into %s...\n", tmpnam);
  250. Binit(&bscores, fd, OWRITE);
  251. for(i=0; i<fsys->nblock; i++){
  252. if(vtfileblockscore(vfile, i, score) < 0)
  253. sysfatal("vtfileblockhash %d: %r", i);
  254. if(Bwrite(&bscores, score, VtScoreSize) != VtScoreSize)
  255. sysfatal("Bwrite: %r");
  256. }
  257. Bterm(&bscores);
  258. vtfileunlock(vfile);
  259. /*
  260. * prep scores for rereading
  261. */
  262. seek(fd, 0, 0);
  263. Binit(&bscores, fd, OREAD);
  264. }
  265. /*
  266. * start the main processes
  267. */
  268. if(statustime)
  269. print("# %T starting procs\n");
  270. qcmp = qalloc();
  271. qventi = qalloc();
  272. rlock(&endlk);
  273. proccreate(fsysproc, nil, STACK);
  274. rlock(&endlk);
  275. proccreate(ventiproc, nil, STACK);
  276. rlock(&endlk);
  277. proccreate(cmpproc, nil, STACK);
  278. if(statustime){
  279. rlock(&endlk);
  280. proccreate(statusproc, nil, STACK);
  281. }
  282. /*
  283. * wait for processes to finish
  284. */
  285. wlock(&endlk);
  286. qfree(qcmp);
  287. qfree(qventi);
  288. if(statustime)
  289. print("# %T procs exited: %d of %d %d-byte blocks changed, "
  290. "%d read, %d written, %d skipped, %d copied\n",
  291. nchange, fsys->nblock, fsys->blocksize,
  292. vtcachenread, vtcachenwrite, nskip, vtcachencopy);
  293. /*
  294. * prepare root block
  295. */
  296. if(incremental)
  297. vtfileclose(vscores);
  298. vtfilelock(vfile, -1);
  299. if(vtfileflush(vfile) < 0)
  300. sysfatal("vtfileflush: %r");
  301. if(vtfilegetentry(vfile, &e) < 0)
  302. sysfatal("vtfilegetentry: %r");
  303. vtfileunlock(vfile);
  304. vtfileclose(vfile);
  305. b = vtcacheallocblock(c, VtDirType, VtEntrySize);
  306. if(b == nil)
  307. sysfatal("vtcacheallocblock: %r");
  308. vtentrypack(&e, b->data, 0);
  309. if(vtblockwrite(b) < 0)
  310. sysfatal("vtblockwrite: %r");
  311. memset(&root, 0, sizeof root);
  312. strecpy(root.name, root.name+sizeof root.name, argv[0]);
  313. strecpy(root.type, root.type+sizeof root.type, fsys->type);
  314. memmove(root.score, b->score, VtScoreSize);
  315. root.blocksize = fsys->blocksize;
  316. memmove(root.prev, prev, VtScoreSize);
  317. vtblockput(b);
  318. b = vtcacheallocblock(c, VtRootType, VtRootSize);
  319. if(b == nil)
  320. sysfatal("vtcacheallocblock: %r");
  321. vtrootpack(&root, b->data);
  322. if(vtblockwrite(b) < 0)
  323. sysfatal("vtblockwrite: %r");
  324. tm = *localtime(time(0));
  325. tm.year += 1900;
  326. tm.mon++;
  327. if(mountplace == nil)
  328. mountplace = guessmountplace(argv[0]);
  329. print("mount /%s/%d/%02d%02d%s %s:%V %d/%02d%02d/%02d%02d\n",
  330. mountname, tm.year, tm.mon, tm.mday,
  331. mountplace,
  332. root.type, b->score,
  333. tm.year, tm.mon, tm.mday, tm.hour, tm.min);
  334. print("# %T %s %s:%V\n", argv[0], root.type, b->score);
  335. if(statustime)
  336. print("# %T venti sync\n");
  337. vtblockput(b);
  338. if(vtsync(z) < 0)
  339. sysfatal("vtsync: %r");
  340. if(statustime)
  341. print("# %T synced\n");
  342. fsysclose(fsys);
  343. diskclose(disk);
  344. vtcachefree(zcache);
  345. // Vtgoodbye hangs on Linux - not sure why.
  346. // Probably vtfcallrpc doesn't quite get the
  347. // remote hangup right. Also probably related
  348. // to the vtrecvproc problem below.
  349. // vtgoodbye(z);
  350. // Leak here, because I can't seem to make
  351. // the vtrecvproc exit.
  352. // vtfreeconn(z);
  353. free(tmpnam);
  354. z = nil;
  355. zcache = nil;
  356. fsys = nil;
  357. disk = nil;
  358. threadexitsall(nil);
  359. }
  360. void
  361. fsysproc(void *dummy)
  362. {
  363. u32int i;
  364. Block *db;
  365. USED(dummy);
  366. for(i=0; i<fsys->nblock; i++){
  367. fsscanblock = i;
  368. if((db = fsysreadblock(fsys, i)) != nil)
  369. qwrite(qcmp, db, i);
  370. }
  371. fsscanblock = i;
  372. qclose(qcmp);
  373. if(statustime)
  374. print("# %T fsys proc exiting\n");
  375. runlock(&endlk);
  376. }
  377. void
  378. cmpproc(void *dummy)
  379. {
  380. uchar *data;
  381. Block *db;
  382. u32int bno, bsize;
  383. uchar score[VtScoreSize];
  384. uchar score1[VtScoreSize];
  385. USED(dummy);
  386. if(incremental)
  387. vtfilelock(vscores, VtOREAD);
  388. bsize = fsys->blocksize;
  389. while((db = qread(qcmp, &bno)) != nil){
  390. data = db->data;
  391. sha1(data, vtzerotruncate(VtDataType, data, bsize), score, nil);
  392. if(incremental){
  393. if(vtfileblockscore(vscores, bno, score1) < 0)
  394. sysfatal("cmpproc vtfileblockscore %d: %r", bno);
  395. }else{
  396. if(Bseek(&bscores, (vlong)bno*VtScoreSize, 0) < 0)
  397. sysfatal("cmpproc Bseek: %r");
  398. if(Bread(&bscores, score1, VtScoreSize) != VtScoreSize)
  399. sysfatal("cmpproc Bread: %r");
  400. }
  401. if(memcmp(score, score1, VtScoreSize) != 0){
  402. nchange++;
  403. if(verbose)
  404. print("# block %ud: old %V new %V\n", bno, score1, score);
  405. qwrite(qventi, db, bno);
  406. }else
  407. blockput(db);
  408. }
  409. qclose(qventi);
  410. if(incremental)
  411. vtfileunlock(vscores);
  412. if(statustime)
  413. print("# %T cmp proc exiting\n");
  414. runlock(&endlk);
  415. }
  416. void
  417. writethread(void *v)
  418. {
  419. WriteReq wr;
  420. char err[ERRMAX];
  421. USED(v);
  422. while(recv(writechan, &wr) == 1){
  423. nrecv++;
  424. if(wr.p == nil)
  425. break;
  426. if(fastwrites && vtread(z, wr.score, wr.type, nil, 0) < 0){
  427. rerrstr(err, sizeof err);
  428. if(strstr(err, "read too small")){ /* already exists */
  429. nskip++;
  430. packetfree(wr.p);
  431. continue;
  432. }
  433. }
  434. if(vtwritepacket(z, wr.score, wr.type, wr.p) < 0)
  435. sysfatal("vtwritepacket: %r");
  436. packetfree(wr.p);
  437. }
  438. }
  439. int
  440. myvtwrite(VtConn *z, uchar score[VtScoreSize], uint type, uchar *buf, int n)
  441. {
  442. WriteReq wr;
  443. if(nwritethread == 0){
  444. n = vtwrite(z, score, type, buf, n);
  445. if(n < 0)
  446. sysfatal("vtwrite: %r");
  447. return n;
  448. }
  449. wr.p = packetalloc();
  450. packetappend(wr.p, buf, n);
  451. packetsha1(wr.p, score);
  452. memmove(wr.score, score, VtScoreSize);
  453. wr.type = type;
  454. nsend++;
  455. send(writechan, &wr);
  456. return 0;
  457. }
  458. void
  459. ventiproc(void *dummy)
  460. {
  461. int i;
  462. Block *db;
  463. u32int bno;
  464. u64int bsize;
  465. USED(dummy);
  466. proccreate(vtsendproc, z, STACK);
  467. proccreate(vtrecvproc, z, STACK);
  468. writechan = chancreate(sizeof(WriteReq), 0);
  469. for(i=0; i<nwritethread; i++)
  470. threadcreate(writethread, nil, STACK);
  471. vtcachesetwrite(zcache, myvtwrite);
  472. bsize = fsys->blocksize;
  473. vtfilelock(vfile, -1);
  474. while((db = qread(qventi, &bno)) != nil){
  475. if(nop){
  476. blockput(db);
  477. continue;
  478. }
  479. if(vtfilewrite(vfile, db->data, bsize, bno*bsize) != bsize)
  480. sysfatal("ventiproc vtfilewrite: %r");
  481. if(vtfileflushbefore(vfile, (bno+1)*bsize) < 0)
  482. sysfatal("ventiproc vtfileflushbefore: %r");
  483. blockput(db);
  484. }
  485. vtfileunlock(vfile);
  486. vtcachesetwrite(zcache, nil);
  487. for(i=0; i<nwritethread; i++)
  488. send(writechan, nil);
  489. chanfree(writechan);
  490. if(statustime)
  491. print("# %T venti proc exiting - nsend %d nrecv %d\n", nsend, nrecv);
  492. runlock(&endlk);
  493. }
  494. static int
  495. percent(u32int a, u32int b)
  496. {
  497. return (vlong)a*100/b;
  498. }
  499. void
  500. statusproc(void *dummy)
  501. {
  502. int n;
  503. USED(dummy);
  504. for(n=0;;n++){
  505. sleep(1000);
  506. if(qcmp->closed && qcmp->nel==0 && qventi->closed && qventi->nel==0)
  507. break;
  508. if(n < statustime)
  509. continue;
  510. n = 0;
  511. print("# %T fsscan=%d%% cmpq=%d%% ventiq=%d%%\n",
  512. percent(fsscanblock, fsys->nblock),
  513. percent(qcmp->nel, MAXQ),
  514. percent(qventi->nel, MAXQ));
  515. }
  516. print("# %T status proc exiting\n");
  517. runlock(&endlk);
  518. }
  519. int
  520. timefmt(Fmt *fmt)
  521. {
  522. vlong ns;
  523. Tm tm;
  524. ns = nsec();
  525. tm = *localtime(time(0));
  526. return fmtprint(fmt, "%04d/%02d%02d %02d:%02d:%02d.%03d",
  527. tm.year+1900, tm.mon+1, tm.mday, tm.hour, tm.min, tm.sec,
  528. (int)(ns%1000000000)/1000000);
  529. }
  530. char*
  531. guessmountplace(char *dev)
  532. {
  533. char *cmd, *q;
  534. int p[2], fd[3], n;
  535. char buf[100];
  536. if(pipe(p) < 0)
  537. sysfatal("pipe: %r");
  538. fd[0] = -1;
  539. fd[1] = p[1];
  540. fd[2] = -1;
  541. cmd = smprint("mount | awk 'BEGIN{v=\"%s\"; u=v; sub(/rdisk/, \"disk\", u);} ($1==v||$1==u) && $2 == \"on\" {print $3}'", dev);
  542. if(threadspawnl(fd, "sh", "sh", "-c", cmd, nil) < 0)
  543. sysfatal("exec mount|awk (to find mtpt of %s): %r", dev);
  544. /* threadspawnl closed p[1] */
  545. free(cmd);
  546. n = readn(p[0], buf, sizeof buf-1);
  547. close(p[0]);
  548. if(n <= 0)
  549. return dev;
  550. buf[n] = 0;
  551. if((q = strchr(buf, '\n')) == nil)
  552. return dev;
  553. *q = 0;
  554. q = buf+strlen(buf);
  555. if(q>buf && *(q-1) == '/')
  556. *--q = 0;
  557. return strdup(buf);
  558. }