multitape_fsck.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. #include "platform.h"
  2. #include <stdint.h>
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #include "chunks.h"
  8. #include "crypto.h"
  9. #include "hexify.h"
  10. #include "imalloc.h"
  11. #include "multitape_internal.h"
  12. #include "storage.h"
  13. #include "sysendian.h"
  14. #include "warnp.h"
  15. #include "multitape.h"
  16. static size_t findinlist(const uint8_t[32], const uint8_t *, size_t);
  17. static int callback_chunkref(void *, struct chunkheader *);
  18. static int deletearchive(STORAGE_D *, struct tapemetadata *);
  19. static int phase1(uint64_t, STORAGE_D *, STORAGE_R *,
  20. struct tapemetadata ***, size_t *);
  21. static int phase2(uint64_t, STORAGE_D *,
  22. struct tapemetadata **, size_t);
  23. static CHUNKS_S * phase3(uint64_t, const char *);
  24. static int phase4(STORAGE_D *, STORAGE_R *, CHUNKS_S *,
  25. struct tapemetadata **, size_t);
  26. static int phase5(STORAGE_D *, CHUNKS_S *);
  27. /**
  28. * findinlist(file, flist, nfiles):
  29. * Find ${file} in the sorted list ${flist} of length ${nfiles}. Return its
  30. * location; or if it is not present, return ${nfiles}.
  31. */
  32. static size_t
  33. findinlist(const uint8_t file[32], const uint8_t * flist, size_t nfiles)
  34. {
  35. size_t midpoint;
  36. int cmp;
  37. size_t rc;
  38. /* Handle 0 files and 1 file specially. */
  39. if (nfiles == 0) {
  40. /* Not found. */
  41. return (nfiles);
  42. }
  43. if (nfiles == 1) {
  44. if (memcmp(file, &flist[0], 32)) {
  45. /* Not found. */
  46. return (nfiles);
  47. } else {
  48. /* Got it! */
  49. return (0);
  50. }
  51. }
  52. /* Binary search. */
  53. midpoint = nfiles / 2;
  54. cmp = memcmp(file, &flist[midpoint * 32], 32);
  55. if (cmp < 0) {
  56. /* The file must be in the first half. */
  57. rc = findinlist(file, flist, midpoint);
  58. if (rc == midpoint) {
  59. /* Not found. */
  60. return (nfiles);
  61. } else {
  62. /* Got it! */
  63. return (rc);
  64. }
  65. } else if (cmp == 0) {
  66. /* Got it! */
  67. return (midpoint);
  68. } else {
  69. /* The file must be in the second half. */
  70. rc = findinlist(file, &flist[(midpoint + 1) * 32],
  71. nfiles - (midpoint + 1));
  72. if (rc == nfiles - (midpoint + 1)) {
  73. /* Not found. */
  74. return (nfiles);
  75. } else {
  76. /* Got it! */
  77. return (rc + (midpoint + 1));
  78. }
  79. }
  80. }
  81. /**
  82. * callback_chunkref(cookie, ch):
  83. * Call chunks_stats_addchunk on the chunk stats cookie ${cookie} and the
  84. * chunk header ${ch}.
  85. */
  86. static int
  87. callback_chunkref(void * cookie, struct chunkheader * ch)
  88. {
  89. CHUNKS_S * C = cookie;
  90. size_t len, zlen;
  91. /* Decode chunk header. */
  92. len = le32dec(ch->len);
  93. zlen = le32dec(ch->zlen);
  94. /* Notify the chunk layer that the current archive uses this chunk. */
  95. return (chunks_stats_addchunk(C, ch->hash, len, zlen));
  96. }
  97. /**
  98. * deletearchive(SD, tmd):
  99. * Delete the metadata and index for the specified archive.
  100. */
  101. static int
  102. deletearchive(STORAGE_D * SD, struct tapemetadata * tmd)
  103. {
  104. uint8_t hbuf[32];
  105. size_t fragnum;
  106. uint8_t fraghash[32];
  107. /* Compute hash of tape name. */
  108. if (crypto_hash_data(CRYPTO_KEY_HMAC_NAME,
  109. (uint8_t *)tmd->name, strlen(tmd->name), hbuf))
  110. goto err0;
  111. /* Delete index fragments. */
  112. for (fragnum = 0; fragnum * MAXIFRAG < tmd->indexlen; fragnum++) {
  113. multitape_metaindex_fragname(hbuf, (uint32_t)fragnum, fraghash);
  114. if (storage_delete_file(SD, 'i', fraghash))
  115. goto err0;
  116. }
  117. /* Delete metadata file. */
  118. if (storage_delete_file(SD, 'm', hbuf))
  119. goto err0;
  120. /* Success! */
  121. return (0);
  122. err0:
  123. /* Failure! */
  124. return (-1);
  125. }
  126. /**
  127. * phase1(machinenum, SD, SR, mdatlist, nmdat):
  128. * Read the list of tape metadata files from the server, and parse each of
  129. * them, deleting any which are corrupt. Set ${mdatlist} to point to a list
  130. * of pointers to metadata structures, and ${nmdat} to the number of (valid)
  131. * metadata files.
  132. */
  133. static int
  134. phase1(uint64_t machinenum, STORAGE_D * SD, STORAGE_R * SR,
  135. struct tapemetadata *** mdatlist, size_t * nmdat)
  136. {
  137. uint8_t * flist;
  138. size_t nfiles;
  139. struct tapemetadata ** mdats;
  140. size_t nvalids;
  141. size_t file;
  142. struct tapemetadata * mdat;
  143. char fname[65];
  144. /* Report status. */
  145. fprintf(stdout, "Phase 1: Verifying metadata validity\n");
  146. /* Obtain a list of metadata files. */
  147. if (storage_directory_read(machinenum, 'm', 0, &flist, &nfiles))
  148. goto err0;
  149. /* Allocate space for nfiles tapemetadata structures. */
  150. if (IMALLOC(mdats, nfiles, struct tapemetadata *))
  151. goto err1;
  152. nvalids = 0;
  153. /* Scan through the list of metadata files, parsing each in turn. */
  154. for (file = 0; file < nfiles; file++) {
  155. if ((mdat = malloc(sizeof(struct tapemetadata))) == NULL)
  156. goto err2;
  157. switch (multitape_metadata_get_byhash(SR, NULL, mdat,
  158. &flist[file * 32], 1)) {
  159. case -1:
  160. /* Internal error. */
  161. goto err3;
  162. case 0:
  163. /* Success. */
  164. mdats[nvalids] = mdat;
  165. nvalids += 1;
  166. break;
  167. case 1:
  168. /* That's weird, the file was there a moment ago. */
  169. warn0("Metadata file has vanished!");
  170. goto err3;
  171. case 2:
  172. /* Corrupt file -- delete it. */
  173. hexify(&flist[file * 32], fname, 32);
  174. fprintf(stdout,
  175. " Deleting corrupt metadata file: %s\n", fname);
  176. if (storage_delete_file(SD, 'm', &flist[file * 32]))
  177. goto err3;
  178. free(mdat);
  179. break;
  180. }
  181. }
  182. /* Free file list. */
  183. free(flist);
  184. /* Return list of tapemetadata structures. */
  185. *mdatlist = mdats;
  186. *nmdat = nvalids;
  187. /* Success! */
  188. return (0);
  189. err3:
  190. free(mdat);
  191. err2:
  192. for (file = 0; file < nvalids; file++) {
  193. multitape_metadata_free(mdats[file]);
  194. free(mdats[file]);
  195. }
  196. free(mdats);
  197. err1:
  198. free(flist);
  199. err0:
  200. /* Failure! */
  201. return (-1);
  202. }
  203. /**
  204. * phase2(machinenum, SD, mdatlist, nmdat):
  205. * Read the list of metaindex files from the server, and delete any metadata
  206. * or metaindex files for which there aren't corresponding metaindex or
  207. * metadata files.
  208. */
  209. static int
  210. phase2(uint64_t machinenum, STORAGE_D * SD,
  211. struct tapemetadata ** mdatlist, size_t nmdat)
  212. {
  213. uint8_t * flist;
  214. size_t nfiles;
  215. size_t file;
  216. uint8_t hbuf[32];
  217. size_t fragnum;
  218. uint8_t fraghash[32];
  219. uint8_t * neededvec;
  220. char fname[65];
  221. /* Report status. */
  222. fprintf(stdout,
  223. "Phase 2: Verifying metadata/metaindex consistency\n");
  224. /* Obtain a list of metaindex files. */
  225. if (storage_directory_read(machinenum, 'i', 0, &flist, &nfiles))
  226. goto err0;
  227. /*
  228. * We make two passes through the metadata list: First we make sure
  229. * that all the needed metaindex files exist, and remove any metadata
  230. * files for which metaindex file(s) are missing; second, we record
  231. * which metaindex files are needed and remove those which aren't.
  232. */
  233. for (file = 0; file < nmdat; file++) {
  234. /* Compute hash of tape name. */
  235. if (crypto_hash_data(CRYPTO_KEY_HMAC_NAME,
  236. (uint8_t *)mdatlist[file]->name,
  237. strlen(mdatlist[file]->name), hbuf))
  238. goto err1;
  239. for (fragnum = 0;
  240. fragnum * MAXIFRAG < mdatlist[file]->indexlen;
  241. fragnum++) {
  242. multitape_metaindex_fragname(hbuf, (uint32_t)fragnum,
  243. fraghash);
  244. if (findinlist(fraghash, flist, nfiles) == nfiles) {
  245. fprintf(stdout,
  246. " Deleting archive"
  247. " with corrupt index: %s\n",
  248. mdatlist[file]->name);
  249. if (storage_delete_file(SD, 'm', hbuf))
  250. goto err1;
  251. /* This metadata doesn't exist any more. */
  252. multitape_metadata_free(mdatlist[file]);
  253. free(mdatlist[file]);
  254. mdatlist[file] = NULL;
  255. goto nextfile;
  256. }
  257. }
  258. nextfile: ;
  259. }
  260. /* Allocate an array for keeping track of which files are needed. */
  261. if ((neededvec = malloc(nfiles)) == NULL)
  262. goto err1;
  263. memset(neededvec, 0, nfiles);
  264. /* Mark files as needed. */
  265. for (file = 0; file < nmdat; file++) {
  266. /* Skip deleted metadata. */
  267. if (mdatlist[file] == NULL)
  268. continue;
  269. /* Compute hash of tape name. */
  270. if (crypto_hash_data(CRYPTO_KEY_HMAC_NAME,
  271. (uint8_t *)mdatlist[file]->name,
  272. strlen(mdatlist[file]->name), hbuf))
  273. goto err2;
  274. for (fragnum = 0;
  275. fragnum * MAXIFRAG < mdatlist[file]->indexlen;
  276. fragnum++) {
  277. multitape_metaindex_fragname(hbuf, (uint32_t)fragnum,
  278. fraghash);
  279. neededvec[findinlist(fraghash, flist, nfiles)] = 1;
  280. }
  281. }
  282. /* Delete any unneeded metaindex files. */
  283. for (file = 0; file < nfiles; file++) {
  284. if (neededvec[file] == 0) {
  285. hexify(&flist[file * 32], fname, 32);
  286. fprintf(stdout,
  287. " Deleting orphaned index fragment: %s\n",
  288. fname);
  289. if (storage_delete_file(SD, 'i', &flist[file * 32]))
  290. goto err2;
  291. }
  292. }
  293. /* Free needed files vector. */
  294. free(neededvec);
  295. /* Free file list. */
  296. free(flist);
  297. /* Success! */
  298. return (0);
  299. err2:
  300. free(neededvec);
  301. err1:
  302. free(flist);
  303. err0:
  304. /* Failure! */
  305. return (-1);
  306. }
  307. /**
  308. * phase3(machinenum, cachedir):
  309. * Read the list of chunks and prepare the chunks layer for fscking.
  310. */
  311. CHUNKS_S *
  312. phase3(uint64_t machinenum, const char * cachedir)
  313. {
  314. /* Report status. */
  315. fprintf(stdout, "Phase 3: Reading chunk list\n");
  316. return (chunks_fsck_start(machinenum, cachedir));
  317. }
  318. /**
  319. * phase4(SD, SR, C, mdatlist, nmdat):
  320. * Verify that the index is not corrupt and that all needed chunks exist; and
  321. * reference-count the chunks (i.e., regenerate the chunk directory).
  322. */
  323. static int
  324. phase4(STORAGE_D * SD, STORAGE_R * SR, CHUNKS_S * C,
  325. struct tapemetadata ** mdatlist, size_t nmdat)
  326. {
  327. size_t file;
  328. /* Report status. */
  329. fprintf(stdout, "Phase 4: Verifying archive completeness\n");
  330. /* Cache up to 100 bytes of blocks per chunk in the directory. */
  331. storage_read_set_cache_limit(SR, 100 * chunks_stats_getdirsz(C));
  332. /* Iterate through the archives. */
  333. for (file = 0; file < nmdat; file++) {
  334. /* Print progress. */
  335. fprintf(stdout, " Archive %zu/%zu...\n", file + 1, nmdat);
  336. /* Skip deleted metadata. */
  337. if (mdatlist[file] == NULL)
  338. continue;
  339. /* The current archive hasn't referenced any chunks yet. */
  340. chunks_stats_zeroarchive(C);
  341. /* ... but one extra file (the metadata) has been used. */
  342. chunks_stats_extrastats(C, mdatlist[file]->metadatalen);
  343. /*
  344. * Determine if all referenced chunks exist, and inform the
  345. * chunk layer about said references.
  346. */
  347. switch (multitape_chunkiter_tmd(SR, C, mdatlist[file],
  348. callback_chunkref, C, 1)) {
  349. case -1:
  350. /* Internal error. */
  351. goto err0;
  352. case 0:
  353. /* Add "current archive" reference stats to total. */
  354. if (chunks_fsck_archive_add(C))
  355. goto err0;
  356. break;
  357. case 1:
  358. /* A non-existent file is referenced. */
  359. fprintf(stdout, " Deleting broken archive: %s\n",
  360. mdatlist[file]->name);
  361. if (deletearchive(SD, mdatlist[file]))
  362. goto err0;
  363. break;
  364. case 2:
  365. /* A file was corrupt. */
  366. fprintf(stdout, " Deleting corrupt archive: %s\n",
  367. mdatlist[file]->name);
  368. if (deletearchive(SD, mdatlist[file]))
  369. goto err0;
  370. break;
  371. }
  372. }
  373. /* Success! */
  374. return (0);
  375. err0:
  376. /* Failure! */
  377. return (-1);
  378. }
  379. /**
  380. * phase5(SD, C):
  381. * Delete any chunks which aren't referenced by any archives.
  382. */
  383. static int
  384. phase5(STORAGE_D * SD, CHUNKS_S * C)
  385. {
  386. /* Report status. */
  387. fprintf(stdout, "Phase 5: Identifying unreferenced chunks\n");
  388. return (chunks_fsck_deletechunks(C, SD));
  389. }
  390. /**
  391. * fscktape(machinenum, cachedir, prune, whichkey, storage_modified):
  392. * Correct any inconsistencies in the archive set (by removing orphaned or
  393. * corrupt files) and reconstruct the chunk directory in ${cachedir}. If
  394. * ${prune} is zero, don't correct inconsistencies; instead, exit with an
  395. * error. If ${whichkey} is zero, use the write key (for non-pruning fsck
  396. * only); otherwise, use the delete key. If the data on the server has been
  397. * modified, set ${*storage_modified} to 1.
  398. */
  399. int
  400. fscktape(uint64_t machinenum, const char * cachedir, int prune, int whichkey,
  401. int * storage_modified)
  402. {
  403. STORAGE_D * SD;
  404. STORAGE_R * SR;
  405. CHUNKS_S * C;
  406. int lockfd;
  407. uint8_t seqnum[32];
  408. struct tapemetadata ** mdatlist;
  409. size_t nmdat;
  410. size_t file;
  411. uint8_t key = (whichkey == 0) ? 0 : 1;
  412. /* Lock the cache directory. */
  413. if ((lockfd = multitape_lock(cachedir)) == -1)
  414. goto err0;
  415. /* Make sure the lower layers are in a clean state. */
  416. if (multitape_cleanstate(cachedir, machinenum, key, storage_modified))
  417. goto err1;
  418. /*
  419. * If a checkpointed archive creation was in progress on a different
  420. * machine, we might as well commit it -- we're going to regenerate
  421. * all of our local state anyway.
  422. */
  423. if (storage_transaction_commitfromcheckpoint(machinenum, key,
  424. storage_modified))
  425. goto err1;
  426. /* Start a storage-layer fsck transaction. */
  427. if ((SD = storage_fsck_start(machinenum, seqnum,
  428. prune ? 0 : 1, key)) == NULL)
  429. goto err1;
  430. /* Obtain a storage-layer read cookie. */
  431. if ((SR = storage_read_init(machinenum)) == NULL)
  432. goto err2;
  433. /*
  434. * Phase 1: Read and parse all the metadata files, and delete any
  435. * metadata files which are corrupt.
  436. */
  437. if (phase1(machinenum, SD, SR, &mdatlist, &nmdat))
  438. goto err3;
  439. /*
  440. * Phase 2: Verify that all the expected metaindex files exist; if
  441. * any are missing, remove the associated metadata file, and if any
  442. * extra metaindex files exist, remove them.
  443. */
  444. if (phase2(machinenum, SD, mdatlist, nmdat))
  445. goto err4;
  446. /*
  447. * Phase 3: Enumerate chunks.
  448. */
  449. if ((C = phase3(machinenum, cachedir)) == NULL)
  450. goto err4;
  451. /*
  452. * Phase 4: Make sure that all the chunks needed for the archives
  453. * are in fact present; and in the process, reconstruct the chunk
  454. * directory and extra statistics.
  455. */
  456. if (phase4(SD, SR, C, mdatlist, nmdat))
  457. goto err5;
  458. /*
  459. * Phase 5: Delete any unreferenced chunks.
  460. */
  461. if (phase5(SD, C))
  462. goto err5;
  463. /* Free metadata structures. */
  464. for (file = 0; file < nmdat; file++) {
  465. multitape_metadata_free(mdatlist[file]);
  466. free(mdatlist[file]);
  467. }
  468. free(mdatlist);
  469. /* Finish the chunk layer fsck operation. */
  470. if (chunks_fsck_end(C))
  471. goto err3;
  472. /* Free the storage-layer read cookie. */
  473. storage_read_free(SR);
  474. /* Finish the storage layer fsck transaction. */
  475. if (storage_delete_end(SD))
  476. goto err1;
  477. /* Commit the transaction. */
  478. if (multitape_commit(cachedir, machinenum, seqnum, key,
  479. storage_modified))
  480. goto err1;
  481. /* Unlock the cache directory. */
  482. if (close(lockfd))
  483. warnp("close");
  484. /* Success! */
  485. return (0);
  486. err5:
  487. chunks_stats_free(C);
  488. err4:
  489. for (file = 0; file < nmdat; file++) {
  490. multitape_metadata_free(mdatlist[file]);
  491. free(mdatlist[file]);
  492. }
  493. free(mdatlist);
  494. err3:
  495. storage_read_free(SR);
  496. err2:
  497. storage_delete_free(SD);
  498. err1:
  499. if (close(lockfd))
  500. warnp("close");
  501. err0:
  502. /* Failure! */
  503. return (-1);
  504. }
  505. /**
  506. * statstape_initialize(machinenum, cachedir):
  507. * Initialize an empty chunk directory in ${cachedir} so that --print-stats
  508. * works. This requires the "directory" file, but no other files. Return 0
  509. * on success, -1 on error, and 1 if the cachedir is already initialized.
  510. */
  511. int
  512. statstape_initialize(uint64_t machinenum, const char * cachedir)
  513. {
  514. int rc;
  515. (void) machinenum; /* UNUSED */
  516. /* Initialize the "directory" file. */
  517. if ((rc = chunks_initialize(cachedir)) != 0)
  518. goto err0;
  519. /* Success! */
  520. return (0);
  521. err0:
  522. /* Failure! */
  523. return (rc);
  524. }