multitape_transaction.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. #include "platform.h"
  2. #include <sys/file.h>
  3. #include <sys/stat.h>
  4. #include <assert.h>
  5. #include <errno.h>
  6. #include <fcntl.h>
  7. #include <inttypes.h>
  8. #include <stdint.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include "asprintf.h"
  14. #include "chunks.h"
  15. #include "crypto_entropy.h"
  16. #include "dirutil.h"
  17. #include "hexify.h"
  18. #include "hexlink.h"
  19. #include "storage.h"
  20. #include "warnp.h"
  21. #include "multitape_internal.h"
  22. static int multitape_docheckpoint(const char *, uint64_t, uint8_t);
  23. static int multitape_docommit(const char *, uint64_t, uint8_t, int *);
  24. /**
  25. * multitape_cleanstate(cachedir, machinenum, key, storage_modified):
  26. * Complete any pending checkpoint or commit. The value ${key} should be 0
  27. * if the write access key should be used to sign a commit request, or 1 if
  28. * the delete access key should be used. If the data on the server has been
  29. * modified, set ${*storage_modified} to 1.
  30. */
  31. int
  32. multitape_cleanstate(const char * cachedir, uint64_t machinenum, uint8_t key,
  33. int * storage_modified)
  34. {
  35. /* Complete any pending checkpoint. */
  36. if (multitape_docheckpoint(cachedir, machinenum, key))
  37. goto err0;
  38. /* Complete any pending commit. */
  39. if (multitape_docommit(cachedir, machinenum, key, storage_modified))
  40. goto err0;
  41. /* Success! */
  42. return (0);
  43. err0:
  44. /* Failure! */
  45. return (-1);
  46. }
  47. /**
  48. * multitape_docheckpoint(cachedir, machinenum, key):
  49. * Complete any pending checkpoint.
  50. */
  51. static int
  52. multitape_docheckpoint(const char * cachedir, uint64_t machinenum,
  53. uint8_t key)
  54. {
  55. char * s, * t;
  56. uint8_t seqnum[32];
  57. uint8_t ckptnonce[32];
  58. uint8_t seqnum_ckptnonce[64];
  59. /* We need a cachedir. */
  60. assert(cachedir != NULL);
  61. /* Make sure ${cachedir} is flushed to disk. */
  62. if (dirutil_fsyncdir(cachedir))
  63. goto err0;
  64. /* Read ${cachedir}/ckpt_m if it exists. */
  65. if (asprintf(&s, "%s/ckpt_m", cachedir) == -1) {
  66. warnp("asprintf");
  67. goto err0;
  68. }
  69. if (hexlink_read(s, seqnum_ckptnonce, 64)) {
  70. if (errno != ENOENT)
  71. goto err1;
  72. /*
  73. * ${cachedir}/ckpt_m doesn't exist; we don't need to do
  74. * anything.
  75. */
  76. goto done;
  77. }
  78. /* Split symlink data into separate seqnum and ckptnonce. */
  79. memcpy(seqnum, seqnum_ckptnonce, 32);
  80. memcpy(ckptnonce, &seqnum_ckptnonce[32], 32);
  81. /* Ask the chunk layer to complete the checkpoint. */
  82. if (chunks_transaction_checkpoint(cachedir))
  83. goto err1;
  84. /* Ask the storage layer to create the checkpoint. */
  85. if (storage_transaction_checkpoint(machinenum, seqnum, ckptnonce,
  86. key))
  87. goto err1;
  88. /* Remove ${cachedir}/commit_m if it exists. */
  89. if (asprintf(&t, "%s/commit_m", cachedir) == -1) {
  90. warnp("asprintf");
  91. goto err1;
  92. }
  93. if (unlink(t)) {
  94. /* ENOENT isn't a problem. */
  95. if (errno != ENOENT) {
  96. warnp("unlink(%s)", t);
  97. goto err2;
  98. }
  99. }
  100. /* This checkpoint is commitable -- create a new commit marker. */
  101. if (hexlink_write(t, seqnum, 32))
  102. goto err2;
  103. free(t);
  104. /* Make sure ${cachedir} is flushed to disk. */
  105. if (dirutil_fsyncdir(cachedir))
  106. goto err1;
  107. /* Delete ${cachedir}/ckpt_m. */
  108. if (unlink(s)) {
  109. warnp("unlink(%s)", s);
  110. goto err1;
  111. }
  112. /* Make sure ${cachedir} is flushed to disk. */
  113. if (dirutil_fsyncdir(cachedir))
  114. goto err1;
  115. done:
  116. free(s);
  117. /* Success! */
  118. return (0);
  119. err2:
  120. free(t);
  121. err1:
  122. free(s);
  123. err0:
  124. /* Failure! */
  125. return (-1);
  126. }
  127. /**
  128. * multitape_checkpoint(cachedir, machinenum, seqnum):
  129. * Create a checkpoint in the current write transaction.
  130. */
  131. int
  132. multitape_checkpoint(const char * cachedir, uint64_t machinenum,
  133. const uint8_t seqnum[32])
  134. {
  135. char * s;
  136. uint8_t ckptnonce[32];
  137. uint8_t seqnum_ckptnonce[64];
  138. /* We need a cachedir. */
  139. assert(cachedir != NULL);
  140. /* Generate random checkpoint nonce. */
  141. if (crypto_entropy_read(ckptnonce, 32))
  142. goto err0;
  143. /* Create symlink from ckpt_m to [seqnum][ckptnonce]. */
  144. memcpy(seqnum_ckptnonce, seqnum, 32);
  145. memcpy(&seqnum_ckptnonce[32], ckptnonce, 32);
  146. if (asprintf(&s, "%s/ckpt_m", cachedir) == -1) {
  147. warnp("asprintf");
  148. goto err0;
  149. }
  150. if (hexlink_write(s, seqnum_ckptnonce, 64))
  151. goto err1;
  152. free(s);
  153. /*
  154. * Complete the checkpoint creation (using the write key, since in
  155. * this code path we know that we always have the write key).
  156. */
  157. if (multitape_docheckpoint(cachedir, machinenum, 0))
  158. goto err0;
  159. /* Success! */
  160. return (0);
  161. err1:
  162. free(s);
  163. err0:
  164. /* Failure! */
  165. return (-1);
  166. }
  167. /**
  168. * multitape_docommit(cachedir, machinenum, key, storage_modified):
  169. * Complete any pending commit.
  170. */
  171. static int
  172. multitape_docommit(const char * cachedir, uint64_t machinenum, uint8_t key,
  173. int * storage_modified)
  174. {
  175. char * s, * t;
  176. uint8_t seqnum[32];
  177. /* We need a cachedir. */
  178. assert(cachedir != NULL);
  179. /* Make sure ${cachedir} is flushed to disk. */
  180. if (dirutil_fsyncdir(cachedir))
  181. goto err0;
  182. /* Read ${cachedir}/commit_m if it exists. */
  183. if (asprintf(&s, "%s/commit_m", cachedir) == -1) {
  184. warnp("asprintf");
  185. goto err0;
  186. }
  187. if (hexlink_read(s, seqnum, 32)) {
  188. if (errno != ENOENT)
  189. goto err1;
  190. /*
  191. * ${cachedir}/commit_m doesn't exist; we don't need to do
  192. * anything.
  193. */
  194. goto done;
  195. }
  196. /* Ask the chunk layer to commit the transaction. */
  197. if (chunks_transaction_commit(cachedir))
  198. goto err1;
  199. /* Ask the storage layer to commit the transaction. */
  200. if (storage_transaction_commit(machinenum, seqnum, key,
  201. storage_modified))
  202. goto err1;
  203. /* Remove ${cachedir}/cseq if it exists. */
  204. if (asprintf(&t, "%s/cseq", cachedir) == -1) {
  205. warnp("asprintf");
  206. goto err1;
  207. }
  208. if (unlink(t)) {
  209. /* ENOENT isn't a problem. */
  210. if (errno != ENOENT) {
  211. warnp("unlink(%s)", t);
  212. goto err2;
  213. }
  214. }
  215. /* Store the new sequence number. */
  216. if (hexlink_write(t, seqnum, 32))
  217. goto err2;
  218. free(t);
  219. /* Make sure ${cachedir} is flushed to disk. */
  220. if (dirutil_fsyncdir(cachedir))
  221. goto err1;
  222. /* Delete ${cachedir}/commit_m. */
  223. if (unlink(s)) {
  224. warnp("unlink(%s)", s);
  225. goto err1;
  226. }
  227. /* Make sure ${cachedir} is flushed to disk. */
  228. if (dirutil_fsyncdir(cachedir))
  229. goto err1;
  230. done:
  231. free(s);
  232. /* Success! */
  233. return (0);
  234. err2:
  235. free(t);
  236. err1:
  237. free(s);
  238. err0:
  239. /* Failure! */
  240. return (-1);
  241. }
  242. /**
  243. * multitape_commit(cachedir, machinenum, seqnum, key, storage_modified):
  244. * Commit the most recent transaction. The value ${key} is defined as in
  245. * multitape_cleanstate. If the data on the server has been modified, set
  246. * ${*storage_modified} to 1.
  247. */
  248. int
  249. multitape_commit(const char * cachedir, uint64_t machinenum,
  250. const uint8_t seqnum[32], uint8_t key, int * storage_modified)
  251. {
  252. char * s;
  253. /* We need a cachedir. */
  254. assert(cachedir != NULL);
  255. /* Make ${cachedir}/commit_m point to ${seqnum}. */
  256. if (asprintf(&s, "%s/commit_m", cachedir) == -1) {
  257. warnp("asprintf");
  258. goto err0;
  259. }
  260. if (hexlink_write(s, seqnum, 32))
  261. goto err1;
  262. free(s);
  263. /* Complete the commit. */
  264. if (multitape_docommit(cachedir, machinenum, key, storage_modified))
  265. goto err0;
  266. /* Success! */
  267. return (0);
  268. err1:
  269. free(s);
  270. err0:
  271. /* Failure! */
  272. return (-1);
  273. }
  274. /**
  275. * multitape_lock(cachedir):
  276. * Lock the given cache directory using lockf(3) or flock(2); return the file
  277. * descriptor of the lock file, or -1 on error.
  278. */
  279. int
  280. multitape_lock(const char * cachedir)
  281. {
  282. char * s;
  283. int fd;
  284. /* We need a cachedir. */
  285. assert(cachedir != NULL);
  286. /* Open ${cachedir}/lockf. */
  287. if (asprintf(&s, "%s/lockf", cachedir) == -1) {
  288. warnp("asprintf");
  289. goto err0;
  290. }
  291. if ((fd = open(s, O_CREAT | O_RDWR, 0666)) == -1) {
  292. warnp("open(%s)", s);
  293. goto err1;
  294. }
  295. /* Lock the file. */
  296. #ifdef HAVE_LOCKF
  297. while (lockf(fd, F_TLOCK, 0)) {
  298. /* Retry on EINTR. */
  299. if (errno == EINTR)
  300. continue;
  301. /* Already locked? */
  302. if (errno == EACCES || errno == EAGAIN) {
  303. warn0("Transaction already in progress");
  304. goto err2;
  305. }
  306. /* Something went wrong. */
  307. warnp("lockf(%s)", s);
  308. goto err2;
  309. }
  310. #else
  311. if (flock(fd, LOCK_EX | LOCK_NB)) {
  312. /* Already locked? */
  313. if (errno == EWOULDBLOCK) {
  314. warn0("Transaction already in progress");
  315. goto err2;
  316. }
  317. /* Something went wrong. */
  318. warnp("flock(%s)", s);
  319. goto err2;
  320. }
  321. #endif
  322. /* Free string allocated by asprintf. */
  323. free(s);
  324. /* Success! */
  325. return (fd);
  326. err2:
  327. if (close(fd))
  328. warnp("close");
  329. err1:
  330. free(s);
  331. err0:
  332. /* Failure! */
  333. return (-1);
  334. }
  335. /**
  336. * multitape_sequence(cachedir, seqnum):
  337. * Set ${seqnum} to the sequence number of the last committed transaction in
  338. * the cache directory ${cachedir}, or 0 if no transactions have ever been
  339. * committed.
  340. */
  341. int
  342. multitape_sequence(const char * cachedir, uint8_t seqnum[32])
  343. {
  344. char * s;
  345. /* We need a cachedir. */
  346. assert(cachedir != NULL);
  347. /* Read the link ${cachedir}/cseq. */
  348. if (asprintf(&s, "%s/cseq", cachedir) == -1) {
  349. warnp("asprintf");
  350. goto err0;
  351. }
  352. if (hexlink_read(s, seqnum, 32)) {
  353. if (errno != ENOENT)
  354. goto err1;
  355. /* ENOENT means the sequence number is zero. */
  356. memset(seqnum, 0, 32);
  357. }
  358. free(s);
  359. /* Success! */
  360. return (0);
  361. err1:
  362. free(s);
  363. err0:
  364. /* Failure! */
  365. return (-1);
  366. }