chunks_directory.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566
  1. #include "platform.h"
  2. #include <sys/types.h>
  3. #include <sys/stat.h>
  4. #include <assert.h>
  5. #include <errno.h>
  6. #include <inttypes.h>
  7. #include <limits.h>
  8. #include <stddef.h>
  9. #include <stdint.h>
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <unistd.h>
  14. #include "asprintf.h"
  15. #include "ctassert.h"
  16. #include "dirutil.h"
  17. #include "rwhashtab.h"
  18. #include "sysendian.h"
  19. #include "warnp.h"
  20. #include "chunks.h"
  21. #include "chunks_internal.h"
  22. /* On-disk extra data statistics structure; integers are little-endian. */
  23. struct chunkstats_external {
  24. uint8_t nchunks[8]; /* Number of files. */
  25. uint8_t s_len[8]; /* Sum of file lengths. */
  26. uint8_t s_zlen[8]; /* Sum of compressed lengths. */
  27. };
  28. CTASSERT(sizeof(struct chunkstats_external) == 24);
  29. /* On-disk chunk metadata structure; integers are little-endian. */
  30. struct chunkdata_external {
  31. uint8_t hash[32]; /* HMAC of chunk. */
  32. uint8_t len[4]; /* Length of chunk. */
  33. uint8_t zlen[4]; /* Compressed length of chunk. */
  34. uint8_t nrefs[4]; /* Number of existing tapes using this. */
  35. uint8_t ncopies[4]; /* Number of copies of this chunk. */
  36. };
  37. CTASSERT(sizeof(struct chunkdata_external) == 48);
  38. static int callback_write(void * rec, void * cookie);
  39. static int callback_free(void * rec, void * cookie);
  40. /**
  41. * callback_write(rec, cookie):
  42. * Convert chunkdata record ${rec} into a struct chunkdata_external and
  43. * write it to the FILE * ${cookie}; but don't write entries with nrefs == 0.
  44. */
  45. static int
  46. callback_write(void * rec, void * cookie)
  47. {
  48. struct chunkdata_external che;
  49. struct chunkdata * ch = rec;
  50. FILE * f = cookie;
  51. /* If nrefs == 0, return without writing anything. */
  52. if (ch->nrefs == 0)
  53. return (0);
  54. /* Convert to on-disk format. */
  55. memcpy(che.hash, ch->hash, 32);
  56. le32enc(che.len, ch->len);
  57. le32enc(che.zlen, ch->zlen_flags & CHDATA_ZLEN);
  58. le32enc(che.nrefs, ch->nrefs);
  59. le32enc(che.ncopies, ch->ncopies);
  60. /* Write. */
  61. if (fwrite(&che, sizeof(che), 1, f) != 1) {
  62. warnp("Error writing to chunk directory");
  63. return (-1);
  64. }
  65. /* Success! */
  66. return (0);
  67. }
  68. /**
  69. * callback_free(rec, cookie):
  70. * If the chunkdata record ${rec} was allocated via malloc(3), free it.
  71. */
  72. static int
  73. callback_free(void * rec, void * cookie)
  74. {
  75. struct chunkdata * ch = rec;
  76. (void)cookie; /* UNUSED */
  77. if (ch->zlen_flags & CHDATA_MALLOC)
  78. free(rec);
  79. /* Success! */
  80. return (0);
  81. }
  82. /**
  83. * chunks_directory_read(cachepath, dir, stats_unique, stats_all, stats_extra,
  84. * mustexist, statstape):
  85. * Read stats_extra statistics (statistics on non-chunks which are stored)
  86. * and the chunk directory (if present) from "${cachepath}/directory" into
  87. * memory allocated and assigned to ${*dir}; and return a hash table
  88. * populated with struct chunkdata records. Populate stats_all with
  89. * statistics for all the chunks listed in the directory (counting
  90. * multiplicity) and populate stats_unique with statistics reflecting the
  91. * unique chunks. If ${mustexist}, error out if the directory does not exist.
  92. * If ${statstape}, allocate struct chunkdata_statstape records instead.
  93. */
  94. RWHASHTAB *
  95. chunks_directory_read(const char * cachepath, void ** dir,
  96. struct chunkstats * stats_unique, struct chunkstats * stats_all,
  97. struct chunkstats * stats_extra, int mustexist, int statstape)
  98. {
  99. struct chunkdata_external che;
  100. struct chunkstats_external cse;
  101. struct stat sb;
  102. RWHASHTAB * HT;
  103. char * s;
  104. struct chunkdata * p = NULL;
  105. struct chunkdata_statstape * ps = NULL;
  106. FILE * f;
  107. size_t numchunks;
  108. /* Zero statistics. */
  109. chunks_stats_zero(stats_unique);
  110. chunks_stats_zero(stats_all);
  111. chunks_stats_zero(stats_extra);
  112. /* Create a hash table to hold the chunkdata structures. */
  113. HT = rwhashtab_init(offsetof(struct chunkdata, hash), 32);
  114. if (HT == NULL)
  115. goto err0;
  116. /* Bail if we're not using a cache directory. */
  117. if (cachepath == NULL) {
  118. *dir = NULL;
  119. return (HT);
  120. }
  121. /* Construct the string "${cachepath}/directory". */
  122. if (asprintf(&s, "%s/directory", cachepath) == -1) {
  123. warnp("asprintf");
  124. goto err1;
  125. }
  126. if (stat(s, &sb)) {
  127. /* Could not stat ${cachepath}/directory. Error? */
  128. if (errno != ENOENT) {
  129. warnp("stat(%s)", s);
  130. goto err2;
  131. }
  132. /* The directory doesn't exist; complain if mustexist != 0. */
  133. if (mustexist) {
  134. warn0("Error reading cache directory from %s",
  135. cachepath);
  136. goto err2;
  137. }
  138. /*
  139. * ${cachepath}/directory does not exist; set ${*dir} to NULL
  140. * and return the empty hash table.
  141. */
  142. free(s);
  143. *dir = NULL;
  144. return (HT);
  145. }
  146. /*
  147. * Make sure the directory file isn't too large or too small, in
  148. * order to avoid any possibility of integer overflows.
  149. */
  150. if ((sb.st_size < 0) ||
  151. ((sizeof(off_t) > sizeof(size_t)) && (sb.st_size > SIZE_MAX))) {
  152. warn0("on-disk directory has insane size (%jd bytes): %s",
  153. (intmax_t)(sb.st_size), s);
  154. goto err2;
  155. }
  156. /* Make sure the directory file isn't too small (different message). */
  157. if ((size_t)sb.st_size < sizeof(struct chunkstats_external)) {
  158. warn0("on-disk directory is too small (%jd bytes): %s",
  159. (intmax_t)(sb.st_size), s);
  160. goto err2;
  161. }
  162. /* Make sure the number of chunks is an integer. */
  163. if (((size_t)sb.st_size - sizeof(struct chunkstats_external)) %
  164. (sizeof(struct chunkdata_external))) {
  165. warn0("on-disk directory is corrupt: %s", s);
  166. goto err2;
  167. }
  168. /* Compute the number of on-disk chunks. */
  169. numchunks =
  170. ((size_t)sb.st_size - sizeof(struct chunkstats_external)) /
  171. sizeof(struct chunkdata_external);
  172. /* Make sure we don't get an integer overflow. */
  173. if (numchunks >= SIZE_MAX / sizeof(struct chunkdata_statstape)) {
  174. warn0("on-disk directory is too large: %s", s);
  175. goto err2;
  176. }
  177. /*
  178. * Allocate memory to ${*dir} large enough to store a struct
  179. * chunkdata or struct chunkdata_statstape for each struct
  180. * chunkdata_external in ${cachepath}/directory.
  181. */
  182. if (statstape) {
  183. ps = malloc(numchunks * sizeof(struct chunkdata_statstape));
  184. *dir = ps;
  185. } else {
  186. p = malloc(numchunks * sizeof(struct chunkdata));
  187. *dir = p;
  188. }
  189. if (*dir == NULL)
  190. goto err2;
  191. /* Open the directory file. */
  192. if ((f = fopen(s, "r")) == NULL) {
  193. warnp("fopen(%s)", s);
  194. goto err3;
  195. }
  196. /* Read the extra files statistics. */
  197. if (fread(&cse, sizeof(cse), 1, f) != 1) {
  198. warnp("fread(%s)", s);
  199. goto err4;
  200. }
  201. stats_extra->nchunks = le64dec(cse.nchunks);
  202. stats_extra->s_len = le64dec(cse.s_len);
  203. stats_extra->s_zlen = le64dec(cse.s_zlen);
  204. /* Read the chunk structures. */
  205. for (; numchunks != 0; numchunks--) {
  206. /* Set p to point at the struct chunkdata. */
  207. if (statstape)
  208. p = &ps->d;
  209. /* Read the file one record at a time... */
  210. if (fread(&che, sizeof(che), 1, f) != 1) {
  211. warnp("fread(%s)", s);
  212. goto err4;
  213. }
  214. /* ... creating struct chunkdata records... */
  215. memcpy(p->hash, che.hash, 32);
  216. p->len = le32dec(che.len);
  217. p->zlen_flags = le32dec(che.zlen);
  218. p->nrefs = le32dec(che.nrefs);
  219. p->ncopies = le32dec(che.ncopies);
  220. /* ... inserting them into the hash table... */
  221. if (rwhashtab_insert(HT, p))
  222. goto err4;
  223. #if UINT32_MAX > SSIZE_MAX
  224. /* ... paranoid check for number of copies... */
  225. if (p->ncopies > SSIZE_MAX)
  226. warn0("More than %zd copies of a chunk; "
  227. "data is ok but stats may be inaccurate",
  228. SSIZE_MAX);
  229. #endif
  230. /* ... and updating the statistics. */
  231. chunks_stats_add(stats_unique, p->len, p->zlen_flags, 1);
  232. chunks_stats_add(stats_all, p->len, p->zlen_flags,
  233. (ssize_t)p->ncopies);
  234. /* Sanity check. */
  235. if ((p->len == 0) || (p->zlen_flags == 0) || (p->nrefs == 0)) {
  236. warn0("on-disk directory is corrupt: %s", s);
  237. goto err4;
  238. }
  239. /* Move to next record. */
  240. if (statstape)
  241. ps++;
  242. else
  243. p++;
  244. }
  245. if (fclose(f)) {
  246. warnp("fclose(%s)", s);
  247. goto err3;
  248. }
  249. /* Free string allocated by asprintf. */
  250. free(s);
  251. /* Success! */
  252. return (HT);
  253. err4:
  254. if (fclose(f))
  255. warnp("fclose");
  256. err3:
  257. free(*dir);
  258. err2:
  259. free(s);
  260. err1:
  261. rwhashtab_free(HT);
  262. err0:
  263. /* Failure! */
  264. return (NULL);
  265. }
  266. /**
  267. * chunks_directory_write(cachepath, HT, stats_extra, suff):
  268. * Write stats_extra statistics and the contents of the hash table ${HT} of
  269. * struct chunkdata records to a new chunk directory in
  270. * "${cachepath}/directory${suff}".
  271. */
  272. int
  273. chunks_directory_write(const char * cachepath, RWHASHTAB * HT,
  274. struct chunkstats * stats_extra, const char * suff)
  275. {
  276. struct chunkstats_external cse;
  277. FILE * f;
  278. char * s;
  279. /* The caller must pass the cachepath, and a suffix to use. */
  280. assert(cachepath != NULL);
  281. assert(suff != NULL);
  282. /* Construct the path to the new chunk directory. */
  283. if (asprintf(&s, "%s/directory%s", cachepath, suff) == -1) {
  284. warnp("asprintf");
  285. goto err0;
  286. }
  287. /* Create the new chunk directory. */
  288. if ((f = fopen(s, "w")) == NULL) {
  289. warnp("fopen(%s)", s);
  290. goto err1;
  291. }
  292. /* Write the extra files statistics. */
  293. le64enc(cse.nchunks, stats_extra->nchunks);
  294. le64enc(cse.s_len, stats_extra->s_len);
  295. le64enc(cse.s_zlen, stats_extra->s_zlen);
  296. if (fwrite(&cse, sizeof(cse), 1, f) != 1) {
  297. warnp("Error writing to chunk directory");
  298. goto err2;
  299. }
  300. /* Write the hash table entries to the new chunk directory. */
  301. if (rwhashtab_foreach(HT, callback_write, f))
  302. goto err2;
  303. /* Call fsync on the new chunk directory and close it. */
  304. if (dirutil_fsync(f, s))
  305. goto err2;
  306. if (fclose(f)) {
  307. warnp("fclose(%s)", s);
  308. goto err1;
  309. }
  310. /* Free string allocated by asprintf. */
  311. free(s);
  312. /* Success! */
  313. return (0);
  314. err2:
  315. if (fclose(f))
  316. warnp("fclose");
  317. err1:
  318. free(s);
  319. err0:
  320. /* Failure! */
  321. return (-1);
  322. }
  323. /**
  324. * chunks_directory_exists(cachepath):
  325. * Return 1 if the /directory file exists within ${cachepath}, 0 if it does
  326. * not, or -1 if there is an error.
  327. */
  328. int
  329. chunks_directory_exists(const char * cachepath)
  330. {
  331. char * directory_filename;
  332. struct stat sb;
  333. int rc;
  334. /* Prepare filename. */
  335. if (asprintf(&directory_filename, "%s/directory", cachepath) == -1) {
  336. rc = -1;
  337. goto done;
  338. }
  339. /* Check if file exists. */
  340. if (stat(directory_filename, &sb) == 0) {
  341. /* File exists. */
  342. rc = 1;
  343. } else {
  344. if (errno == ENOENT) {
  345. /* File does not exist. */
  346. rc = 0;
  347. } else {
  348. /* Other error. */
  349. warnp("stat(%s)", directory_filename);
  350. rc = -1;
  351. }
  352. }
  353. /* Clean up memory. */
  354. free(directory_filename);
  355. done:
  356. /* Return result code. */
  357. return (rc);
  358. }
  359. /**
  360. * chunks_directory_free(HT, dir):
  361. * Free the hash table ${HT} of struct chunkdata records, all of its
  362. * elements, and ${dir}.
  363. */
  364. void
  365. chunks_directory_free(RWHASHTAB * HT, void * dir)
  366. {
  367. /* Free records in the hash table. */
  368. rwhashtab_foreach(HT, callback_free, NULL);
  369. /* Free the hash table itself. */
  370. rwhashtab_free(HT);
  371. /* Free the records which were allocated en masse. */
  372. free(dir);
  373. }
  374. /**
  375. * chunks_directory_commit(cachepath, osuff, nsuff):
  376. * If ${cachepath}/directory${osuff} exists, move it to
  377. * ${cachepath}/directory${nsuff} (replacing anything already there).
  378. */
  379. int
  380. chunks_directory_commit(const char * cachepath, const char * osuff,
  381. const char * nsuff)
  382. {
  383. struct stat sbs;
  384. struct stat sbt;
  385. char * s;
  386. char * t;
  387. /* The caller must pass the cachepath, and suffices to use. */
  388. assert(cachepath != NULL);
  389. assert(nsuff != NULL);
  390. assert(osuff != NULL);
  391. /* Construct file names. */
  392. if (asprintf(&s, "%s/directory%s", cachepath, nsuff) == -1) {
  393. warnp("asprintf");
  394. goto err0;
  395. }
  396. if (asprintf(&t, "%s/directory%s", cachepath, osuff) == -1) {
  397. warnp("asprintf");
  398. goto err1;
  399. }
  400. /*
  401. * If ${cachedir}/directory.tmp does not exist, the transaction was
  402. * already committed from the perspective of the chunk layer; so we
  403. * can free memory and return.
  404. */
  405. if (lstat(t, &sbt)) {
  406. if (errno == ENOENT)
  407. goto done;
  408. warnp("lstat(%s)", t);
  409. goto err2;
  410. }
  411. /*
  412. * If ${cachedir}/directory exists and is not the same file as
  413. * ${cachedir}/directory.tmp, remove ${cachedir}/directory and
  414. * create a hard link from ${cachedir}/directory.tmp.
  415. */
  416. if (lstat(s, &sbs)) {
  417. if (errno != ENOENT) {
  418. warnp("lstat(%s)", s);
  419. goto err2;
  420. }
  421. } else {
  422. if (sbs.st_ino != sbt.st_ino) {
  423. /* Remove ${cachedir}/directory. */
  424. if (unlink(s)) {
  425. warnp("unlink(%s)", s);
  426. goto err2;
  427. }
  428. } else {
  429. /*
  430. * We're replaying and we've already linked the two
  431. * paths; skip ahead to unlinking the .tmp file, as
  432. * otherwise link(2) will fail with EEXIST.
  433. */
  434. goto linkdone;
  435. }
  436. }
  437. /*-
  438. * We want to move ${t} to ${s} in a crash-proof way. Unfortunately
  439. * the POSIX rename(2) syscall merely guarantees that if ${s} already
  440. * exists then ${s} will always exist -- not that the file being
  441. * renamed will always exist. Depending on how crash-proof the
  442. * filesystem is, that second requirement might not be satisfied.
  443. *
  444. * Ideally we would like to solve this problem by creating a hard
  445. * link, syncing the directory, then unlinking the old file; but we
  446. * might be running on a filesystem/OS which doesn't support hard
  447. * links (e.g., FAT32).
  448. *
  449. * If the link(2) call fails with ENOSYS (sensible failure code for
  450. * not supporting hard links) or EPERM (Linux's idea of a joke?), we
  451. * fall back to using rename(2) instead of link/sync/unlink.
  452. */
  453. /* Create a link from ${cachedir}/directory.tmp. */
  454. if (link(t, s)) {
  455. if ((errno != ENOSYS) && (errno != EPERM)) {
  456. warnp("link(%s, %s)", t, s);
  457. goto err2;
  458. }
  459. /* Use rename(2) instead. */
  460. if (rename(t, s)) {
  461. warnp("rename(%s, %s)", t, s);
  462. goto err2;
  463. }
  464. } else {
  465. linkdone:
  466. /* Make sure ${cachedir} is flushed to disk. */
  467. if (dirutil_fsyncdir(cachepath))
  468. goto err2;
  469. /* Remove ${cachedir}/directory.tmp. */
  470. if (unlink(t)) {
  471. warnp("unlink(%s)", t);
  472. goto err2;
  473. }
  474. }
  475. /* Finally, sync the directory one last time. */
  476. if (dirutil_fsyncdir(cachepath))
  477. goto err2;
  478. done:
  479. free(t);
  480. free(s);
  481. /* Success! */
  482. return (0);
  483. err2:
  484. free(t);
  485. err1:
  486. free(s);
  487. err0:
  488. /* Failure! */
  489. return (-1);
  490. }