recrypt.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. #include "platform.h"
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <assert.h>
  5. #include <errno.h>
  6. #include <stdint.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <unistd.h>
  11. #include "asprintf.h"
  12. #include "crypto.h"
  13. #include "dirutil.h"
  14. #include "getopt.h"
  15. #include "imalloc.h"
  16. #include "keyfile.h"
  17. #include "multitape_internal.h"
  18. #include "passphrase_entry.h"
  19. #include "storage.h"
  20. #include "tarsnap_opt.h"
  21. #include "tsnetwork.h"
  22. #include "warnp.h"
  23. /* Copy batches of 16384 blocks; print a . per 512 blocks. */
  24. #define BATCHLEN 16384
  25. #define BATCHDOT 512
  26. /* Read blocks using 8 connections. */
  27. #define NCONNS 8
  28. /* Keymasks required. */
  29. static const int KEYMASK_NEW_KEYFILE = (CRYPTO_KEYMASK_SIGN_PRIV
  30. | CRYPTO_KEYMASK_ENCR_PUB | CRYPTO_KEYMASK_AUTH_GET
  31. | CRYPTO_KEYMASK_AUTH_PUT | CRYPTO_KEYMASK_HMAC_FILE_WRITE);
  32. static const int KEYMASK_OLD_KEYFILE = (CRYPTO_KEYMASK_SIGN_PUB
  33. | CRYPTO_KEYMASK_ENCR_PRIV | CRYPTO_KEYMASK_AUTH_GET
  34. | CRYPTO_KEYMASK_AUTH_DELETE | CRYPTO_KEYMASK_HMAC_FILE);
  35. /* Global tarsnap options declared in tarsnap_opt.h. */
  36. int tarsnap_opt_aggressive_networking = 1;
  37. int tarsnap_opt_noisy_warnings = 0;
  38. int tarsnap_opt_humanize_numbers = 0;
  39. uint64_t tarsnap_opt_checkpointbytes = (uint64_t)(-1);
  40. uint64_t tarsnap_opt_maxbytesout = (uint64_t)(-1);
  41. struct block {
  42. char class;
  43. uint8_t name[32];
  44. };
  45. struct reader {
  46. STORAGE_R * SR;
  47. int status;
  48. struct block * b;
  49. uint8_t * buf;
  50. size_t buflen;
  51. };
  52. static void
  53. usage(void)
  54. {
  55. fprintf(stderr, "usage: tarsnap-recrypt %s %s %s %s\n",
  56. "--oldkey old-key-file", "--oldcachedir old-cache-dir",
  57. "--newkey new-key-file", "--newcachedir new-cache-dir");
  58. fprintf(stderr, " tarsnap-recrypt --version\n");
  59. exit(1);
  60. /* NOTREACHED */
  61. }
  62. static void
  63. lockdirs(const char * odir, const char * ndir, int * odirlock, int * ndirlock)
  64. {
  65. struct stat sbo, sbn;
  66. /* PEBKAC check: make sure the two paths are different. */
  67. if (strcmp(odir, ndir) == 0) {
  68. warn0("Old and new cache directories must be different");
  69. exit(1);
  70. }
  71. /* Lock the two cache directories. */
  72. if ((*odirlock = multitape_lock(odir)) == -1) {
  73. warnp("Cannot lock old cache directory: %s", odir);
  74. exit(1);
  75. }
  76. if ((*ndirlock = multitape_lock(ndir)) == -1) {
  77. warnp("Cannot lock new cache directory: %s", ndir);
  78. exit(1);
  79. }
  80. /* Make sure we didn't lock the same file twice. */
  81. if (fstat(*odirlock, &sbo) || fstat(*ndirlock, &sbn)) {
  82. warnp("Cannot stat lockfile");
  83. exit(1);
  84. }
  85. if ((sbn.st_dev == sbo.st_dev) && (sbn.st_ino == sbo.st_ino)) {
  86. warn0("Old and new cache directories must be different");
  87. exit(1);
  88. }
  89. }
  90. static void
  91. getblist(uint64_t mnum, struct block ** blist, size_t * blistlen)
  92. {
  93. uint8_t * flist_m;
  94. size_t nfiles_m;
  95. uint8_t * flist_i;
  96. size_t nfiles_i;
  97. uint8_t * flist_c;
  98. size_t nfiles_c;
  99. size_t i, j;
  100. /* Get lists of {metadata, metaindex, chunk} files. */
  101. if (storage_directory_read(mnum, 'm', 0, &flist_m, &nfiles_m)) {
  102. warnp("Error reading metadata file list");
  103. exit(1);
  104. }
  105. if (storage_directory_read(mnum, 'i', 0, &flist_i, &nfiles_i)) {
  106. warnp("Error reading metaindex file list");
  107. exit(1);
  108. }
  109. if (storage_directory_read(mnum, 'c', 0, &flist_c, &nfiles_c)) {
  110. warnp("Error reading chunk file list");
  111. exit(1);
  112. }
  113. /* If there are no blocks, we have nothing to do. */
  114. if (nfiles_m + nfiles_i + nfiles_c == 0) {
  115. *blistlen = 0;
  116. *blist = NULL;
  117. return;
  118. }
  119. /* Allocate array of blocks. */
  120. *blistlen = nfiles_m + nfiles_i + nfiles_c;
  121. if (IMALLOC(*blist, *blistlen, struct block)) {
  122. warnp("Cannot allocate memory");
  123. exit(1);
  124. }
  125. /* Copy block names into the array in lexicographical order. */
  126. for (i = 0, j = 0; i < nfiles_c; i++, j++) {
  127. (*blist)[j].class = 'c';
  128. memcpy((*blist)[j].name, &flist_c[i * 32], 32);
  129. }
  130. for (i = 0; i < nfiles_i; i++, j++) {
  131. (*blist)[j].class = 'i';
  132. memcpy((*blist)[j].name, &flist_i[i * 32], 32);
  133. }
  134. for (i = 0; i < nfiles_m; i++, j++) {
  135. (*blist)[j].class = 'm';
  136. memcpy((*blist)[j].name, &flist_m[i * 32], 32);
  137. }
  138. /* Free the individual arrays. */
  139. free(flist_c);
  140. free(flist_i);
  141. free(flist_m);
  142. }
  143. static int
  144. cmpblock(const struct block * x, const struct block * y)
  145. {
  146. if (x->class < y->class)
  147. return (-1);
  148. else if (x->class > y->class)
  149. return (1);
  150. else
  151. return (memcmp(x->name, y->name, 32));
  152. }
  153. static void
  154. compareblists(const struct block * oblist, size_t oblistlen,
  155. const struct block * nblist, size_t nblistlen,
  156. struct block ** cblist, size_t * cblistlen)
  157. {
  158. size_t i, j, k;
  159. int rc;
  160. /* Make sure that nblist is a subset of oblist. */
  161. for (i = j = 0; j < nblistlen; i++) {
  162. /*
  163. * If we've hit the end of oblist or the next block in oblist
  164. * is greater than the next block in nblist, there's at least
  165. * one block in nblist which is not in oblist.
  166. */
  167. if ((i == oblistlen) ||
  168. ((rc = cmpblock(&oblist[i], &nblist[j])) > 0)) {
  169. warn0("New machine has data not in old machine!"
  170. " Cannot continue.");
  171. exit(1);
  172. }
  173. /* If we've matched this new block, advance the pointer. */
  174. if (rc == 0)
  175. j++;
  176. }
  177. /* If the lists are identical, we have nothing to do. */
  178. if (oblistlen == nblistlen) {
  179. *cblistlen = 0;
  180. *cblist = NULL;
  181. return;
  182. }
  183. /* Allocate space for the blocks-to-copy list. */
  184. *cblistlen = oblistlen - nblistlen;
  185. if (IMALLOC(*cblist, *cblistlen, struct block)) {
  186. warnp("Cannot allocate memory");
  187. exit(1);
  188. }
  189. /* Copy names of blocks which need copying. */
  190. for (i = j = k = 0; i < oblistlen; i++) {
  191. /* Do we need to copy this block? */
  192. if ((j == nblistlen) ||
  193. (cmpblock(&oblist[i], &nblist[j]) < 0)) {
  194. (*cblist)[k].class = oblist[i].class;
  195. memcpy((*cblist)[k].name, oblist[i].name, 32);
  196. k++;
  197. } else
  198. j++;
  199. }
  200. /* Sanity check. */
  201. if (j != nblistlen) {
  202. warn0("Programmer error: Didn't get to end of new block list");
  203. exit(1);
  204. }
  205. if (k != *cblistlen) {
  206. warn0("Programmer error: Didn't fill blocks-to-copy list");
  207. exit(1);
  208. }
  209. }
  210. static int
  211. callback_read(void * cookie, int status, uint8_t * buf, size_t buflen)
  212. {
  213. struct reader * R = cookie;
  214. /* Make sure we succeeded. */
  215. if (status != 0) {
  216. warn0("Block read returned failure: %d", status);
  217. return (-1);
  218. }
  219. /* Store the returned buffer. */
  220. R->buf = buf;
  221. R->buflen = buflen;
  222. /* This read is done. */
  223. R->status = 1;
  224. /* Success! */
  225. return (0);
  226. }
  227. static void
  228. copyblocks(struct block * blist, size_t bnum, uint64_t omnum,
  229. STORAGE_W * SW)
  230. {
  231. struct reader R[NCONNS];
  232. size_t i;
  233. size_t wleft;
  234. uint8_t * buf;
  235. size_t buflen;
  236. /* Initialize NCONNS readers. */
  237. for (i = 0; i < NCONNS; i++) {
  238. if ((R[i].SR = storage_read_init(omnum)) == NULL) {
  239. warnp("Cannot initialize reader");
  240. exit(1);
  241. }
  242. R[i].status = 1;
  243. R[i].b = NULL;
  244. }
  245. /* No blocks have been writ yet. */
  246. wleft = bnum;
  247. /* Cycle through readers handling blocks. */
  248. for (i = 0; ; i = (i + 1) % NCONNS) {
  249. /* If all the blocks have been writ, stop. */
  250. if (wleft == 0)
  251. break;
  252. /* Spin until this reader is not reading. */
  253. if (network_spin(&R[i].status)) {
  254. warnp("Error in network layer");
  255. exit(1);
  256. }
  257. /* If we have a block, write it. */
  258. if (R[i].b != NULL) {
  259. /* If this is a metadata file, recrypt it. */
  260. if (R[i].b->class == 'm') {
  261. /* Perform the re-cryption. */
  262. if (multitape_metadata_recrypt(R[i].buf,
  263. R[i].buflen, &buf, &buflen)) {
  264. warnp("Error re-encrypting metadata");
  265. exit(1);
  266. }
  267. /* Throw out the old buffer. */
  268. free(R[i].buf);
  269. R[i].buf = buf;
  270. R[i].buflen = buflen;
  271. }
  272. /* Write the block. */
  273. if (storage_write_file(SW, R[i].buf, R[i].buflen,
  274. R[i].b->class, R[i].b->name)) {
  275. warnp("Error writing block");
  276. exit(1);
  277. }
  278. wleft--;
  279. /* Report progress. */
  280. if (wleft % BATCHDOT == 0)
  281. printf(".");
  282. /* We don't have a block any more. */
  283. R[i].b = NULL;
  284. free(R[i].buf);
  285. }
  286. /* If we have blocks to read, read one. */
  287. if (bnum > 0) {
  288. /* Assign this block and read it. */
  289. R[i].status = 0;
  290. R[i].b = blist;
  291. if (storage_read_file_callback(R[i].SR, NULL, 0,
  292. R[i].b->class, R[i].b->name,
  293. callback_read, &R[i])) {
  294. warnp("Error reading block");
  295. exit(1);
  296. }
  297. /* We've assigned this block. */
  298. blist++;
  299. bnum--;
  300. }
  301. }
  302. /* Shut down the readers. */
  303. for (i = 0; i < NCONNS; i++)
  304. storage_read_free(R[i].SR);
  305. }
  306. static void
  307. copydirectory(FILE * src, FILE * dst)
  308. {
  309. uint8_t buf[65536];
  310. size_t len;
  311. while ((len = fread(buf, 1, 65536, src)) > 0) {
  312. if (fwrite(buf, 1, len, dst) != len) {
  313. warnp("Error writing chunk directory");
  314. exit(1);
  315. }
  316. }
  317. if (ferror(src)) {
  318. warnp("Error reading chunk directory");
  319. exit(1);
  320. }
  321. }
  322. int
  323. main(int argc, char **argv)
  324. {
  325. struct stat sb;
  326. const char *ocachedir, *ncachedir;
  327. const char *okeyfile, *nkeyfile;
  328. uint64_t omachinenum, nmachinenum;
  329. int odirlock, ndirlock;
  330. struct block *oblist, *nblist, *cblist;
  331. size_t oblistlen, nblistlen, cblistlen;
  332. uint8_t olastseq[32], oseqnum[32];
  333. uint8_t nlastseq[32], nseqnum[32];
  334. STORAGE_D * SD;
  335. STORAGE_W * SW;
  336. size_t bpos, copynum;
  337. char *odirpath, *ndirpath;
  338. FILE *odir, *ndir;
  339. const char * ch;
  340. const char * missingkey = NULL;
  341. int storage_modified = 0;
  342. WARNP_INIT;
  343. /* Attempt to avoid buffering stdout since we print progress msgs. */
  344. setvbuf(stdout, NULL, _IONBF, 0);
  345. /* Initialize key cache. */
  346. if (crypto_keys_init()) {
  347. warnp("Key cache initialization failed");
  348. exit(1);
  349. }
  350. /* No options yet. */
  351. ocachedir = ncachedir = NULL;
  352. okeyfile = nkeyfile = NULL;
  353. /* Parse arguments. */
  354. while ((ch = GETOPT(argc, argv)) != NULL) {
  355. GETOPT_SWITCH(ch) {
  356. GETOPT_OPTARG("--oldkey"):
  357. if (okeyfile != NULL)
  358. usage();
  359. okeyfile = optarg;
  360. break;
  361. GETOPT_OPTARG("--oldcachedir"):
  362. if (ocachedir != NULL)
  363. usage();
  364. ocachedir = optarg;
  365. break;
  366. GETOPT_OPTARG("--newkey"):
  367. if (nkeyfile != NULL)
  368. usage();
  369. nkeyfile = optarg;
  370. break;
  371. GETOPT_OPTARG("--newcachedir"):
  372. if ((ncachedir != NULL) || (argc < 2))
  373. usage();
  374. ncachedir = optarg;
  375. break;
  376. GETOPT_OPT("--version"):
  377. fprintf(stderr, "tarsnap-recrypt %s\n",
  378. PACKAGE_VERSION);
  379. exit(0);
  380. GETOPT_MISSING_ARG:
  381. warn0("Missing argument to %s", ch);
  382. /* FALLTHROUGH */
  383. GETOPT_DEFAULT:
  384. usage();
  385. }
  386. }
  387. argc -= optind;
  388. argv += optind;
  389. /* We should have processed all the arguments. */
  390. if (argc != 0)
  391. usage();
  392. (void)argv; /* argv is not used beyond this point. */
  393. /* Make sure we have the necessary options. */
  394. if ((ocachedir == NULL) || (ncachedir == NULL) ||
  395. (okeyfile == NULL) || (nkeyfile == NULL))
  396. usage();
  397. /* Make sure the cache directories exist. */
  398. if (build_dir(ncachedir, "--newcachdir"))
  399. exit(1);
  400. if (build_dir(ocachedir, "--oldcachdir"))
  401. exit(1);
  402. /* Lock the cache directories. */
  403. lockdirs(ocachedir, ncachedir, &odirlock, &ndirlock);
  404. /* Read keys from the new key file. */
  405. if (keyfile_read(nkeyfile, &nmachinenum, KEYMASK_NEW_KEYFILE, 0,
  406. PASSPHRASE_TTY_STDIN, NULL)) {
  407. warnp("Cannot read key file: %s", nkeyfile);
  408. exit(1);
  409. }
  410. if ((missingkey = crypto_keys_missing(KEYMASK_NEW_KEYFILE)) != NULL) {
  411. warn0("The %s key is required in the new key", missingkey);
  412. exit(1);
  413. }
  414. /* Get a list of blocks in the new machine. */
  415. printf("Reading list of blocks for new machine...");
  416. getblist(nmachinenum, &nblist, &nblistlen);
  417. printf(" done.\n");
  418. /*
  419. * Make sure any pending checkpoint or commit on the new machine is
  420. * completed, and get the current sequence # for future reference.
  421. */
  422. printf("Validating new machine state...");
  423. if (multitape_cleanstate(ncachedir, nmachinenum, 0,
  424. &storage_modified)) {
  425. warnp("Cannot complete pending checkpoint or commit");
  426. exit(1);
  427. }
  428. if (multitape_sequence(ncachedir, nlastseq)) {
  429. warnp("Cannot get sequence number for new machine");
  430. exit(1);
  431. }
  432. printf(" done.\n");
  433. /*
  434. * Read keys from the old key file. The AUTH_GET key replaces the
  435. * key we read from the new key file; this is fine since (now that
  436. * we've read the list of blocks) the only thing we'll be doing to
  437. * the new machine is writing blocks.
  438. */
  439. if (keyfile_read(okeyfile, &omachinenum, KEYMASK_OLD_KEYFILE, 0,
  440. PASSPHRASE_TTY_STDIN, NULL)) {
  441. warnp("Cannot read key file: %s", okeyfile);
  442. exit(1);
  443. }
  444. if ((missingkey = crypto_keys_missing(KEYMASK_OLD_KEYFILE)) != NULL) {
  445. warn0("The %s key is required in the old key", missingkey);
  446. exit(1);
  447. }
  448. /*
  449. * Make sure any pending checkpoint or commit is completed, and start
  450. * a storage-layer delete transaction on the old machine. Doing this
  451. * now serves two purposes: First, it ensures that our cached state is
  452. * synced with the server (via the sequence # check) and thus that the
  453. * directory file we're going to copy across is valid; and second, it
  454. * guarantees that if we lose a race against another system accessing
  455. * the same machine's data, we won't delete anything (such a scenario
  456. * implies PEBKAC, but safety belts are good anyway).
  457. */
  458. printf("Validating old machine state...");
  459. if (multitape_cleanstate(ocachedir, omachinenum, 1,
  460. &storage_modified)) {
  461. warnp("Cannot complete pending checkpoint or commit");
  462. exit(1);
  463. }
  464. if (multitape_sequence(ocachedir, olastseq)) {
  465. warnp("Cannot get sequence number for old machine");
  466. exit(1);
  467. }
  468. if ((SD =
  469. storage_delete_start(omachinenum, olastseq, oseqnum)) == NULL) {
  470. warnp("Cannot start delete transaction");
  471. exit(1);
  472. }
  473. printf(" done.\n");
  474. /* Get a list of blocks in the old machine. */
  475. printf("Reading list of blocks for old machine...");
  476. getblist(omachinenum, &oblist, &oblistlen);
  477. printf(" done.\n");
  478. /* Compare lists of blocks. */
  479. compareblists(oblist, oblistlen, nblist, nblistlen,
  480. &cblist, &cblistlen);
  481. /* Don't need the list of blocks owned by the new machine any more. */
  482. free(nblist);
  483. /* Construct paths to chunk directories. */
  484. if (asprintf(&ndirpath, "%s/directory", ncachedir) == -1) {
  485. warnp("asprintf");
  486. exit(1);
  487. }
  488. if (asprintf(&odirpath, "%s/directory", ocachedir) == -1) {
  489. warnp("asprintf");
  490. exit(1);
  491. }
  492. /*
  493. * If the old chunk directory file does not exist, the old machine
  494. * must have no blocks; confirm this, and exit (since copying and
  495. * deleting zero files is a no-op).
  496. */
  497. if (stat(odirpath, &sb)) {
  498. /* Errors other than ENOENT are bad. */
  499. if (errno != ENOENT) {
  500. warnp("stat(%s)", odirpath);
  501. exit(1);
  502. }
  503. /* Having blocks without a cache directory is bad. */
  504. if (oblistlen != 0) {
  505. warn0("Chunk directory is missing: %s", odirpath);
  506. exit(1);
  507. }
  508. /* Nothing to do. */
  509. exit(0);
  510. }
  511. /*
  512. * Create an empty directory file for the new machine. An empty file
  513. * is not a valid directory, so if we crash, tarsnap will fail until
  514. * the cache directory is reconstructed via --fsck or we are re-run
  515. * and allowed to finish the recrypting process.
  516. */
  517. if ((ndir = fopen(ndirpath, "w")) == NULL) {
  518. warnp("Cannot create chunk directory for new machine");
  519. exit(1);
  520. }
  521. /* Copy blocks to new machine. */
  522. for (bpos = 0; bpos < cblistlen; bpos += BATCHLEN) {
  523. /* Report progress. */
  524. printf("Copying blocks [%zu/%zu]..", bpos / BATCHLEN + 1,
  525. (cblistlen + BATCHLEN - 1) / BATCHLEN);
  526. /* Start a write transaction. */
  527. if ((SW = storage_write_start(nmachinenum, nlastseq,
  528. nseqnum)) == NULL) {
  529. warnp("Cannot start write transaction");
  530. exit(1);
  531. }
  532. /* Figure out how many blocks we need to copy. */
  533. if ((copynum = cblistlen - bpos) > BATCHLEN)
  534. copynum = BATCHLEN;
  535. /* Copy the blocks. */
  536. copyblocks(&cblist[bpos], copynum, omachinenum, SW);
  537. /* End the write transaction. */
  538. if (storage_write_end(SW)) {
  539. warnp("Cannot complete write transaction");
  540. exit(1);
  541. }
  542. /* Commit the write transaction. */
  543. if (multitape_commit(ncachedir, nmachinenum, nseqnum, 0,
  544. &storage_modified)) {
  545. warnp("Cannot commit write transaction");
  546. exit(1);
  547. }
  548. /* We have a new last sequence number. */
  549. memcpy(nlastseq, nseqnum, 32);
  550. /* We've done this group. */
  551. printf(". done.\n");
  552. }
  553. /* Open the old cache directory... */
  554. if ((odir = fopen(odirpath, "r")) == NULL) {
  555. warnp("Cannot read chunk directory for old machine");
  556. exit(1);
  557. }
  558. /* ... and copy its contents into the new cache directory. */
  559. printf("Updating cache directory...");
  560. copydirectory(odir, ndir);
  561. printf(" done.\n");
  562. /* Close the old and new chunk directories. */
  563. if (fclose(ndir) || fclose(odir)) {
  564. warnp("Error closing chunk directory");
  565. exit(1);
  566. }
  567. /* Delete blocks from old machine. */
  568. for (bpos = 0; bpos < oblistlen; bpos++) {
  569. /* Report progress. */
  570. if (bpos % BATCHLEN == 0)
  571. printf("Deleting blocks [%zu/%zu]..",
  572. bpos / BATCHLEN + 1,
  573. (oblistlen + BATCHLEN - 1) / BATCHLEN);
  574. /* Delete a file. */
  575. if (storage_delete_file(SD, oblist[bpos].class,
  576. oblist[bpos].name)) {
  577. warnp("Error deleting blocks");
  578. exit(1);
  579. }
  580. /* Report any completed progress. */
  581. if ((bpos == oblistlen - 1) ||
  582. (bpos % BATCHLEN == BATCHLEN - 1)) {
  583. if (storage_delete_flush(SD)) {
  584. warnp("Error deleting blocks");
  585. exit(1);
  586. }
  587. printf(". done.\n");
  588. } else if (bpos % BATCHDOT == BATCHDOT - 1)
  589. printf(".");
  590. }
  591. /* We've issued all our deletes. */
  592. if (storage_delete_end(SD)) {
  593. warnp("Error deleting blocks");
  594. exit(1);
  595. }
  596. /* Commit the delete transaction and delete the old chunk dir. */
  597. printf("Committing block deletes...");
  598. if (multitape_commit(ocachedir, omachinenum, oseqnum, 1,
  599. &storage_modified)) {
  600. warnp("Cannot commit delete transaction");
  601. exit(1);
  602. }
  603. if (unlink(odirpath)) {
  604. warnp("Cannot delete old chunk directory: %s", odirpath);
  605. exit(1);
  606. }
  607. printf(" done.\n");
  608. /* Free strings allocated by asprintf. */
  609. free(odirpath);
  610. free(ndirpath);
  611. /* Close lock files (not really needed as they autoclose on exit). */
  612. if (close(odirlock))
  613. warnp("close");
  614. if (close(ndirlock))
  615. warnp("close");
  616. (void)storage_modified; /* UNUSED */
  617. /* Success! */
  618. return (0);
  619. }