snap.c - dedup - deduplicating backup program
HTML git clone git://bitreich.org/dedup/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/dedup/
DIR Log
DIR Files
DIR Refs
DIR Tags
DIR README
DIR LICENSE
---
snap.c (10918B)
---
1 /* Snapshot archive implementation */
2 #include <sys/types.h>
3 #include <sys/stat.h>
4
5 #include <assert.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <limits.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <strings.h>
14 #include <unistd.h>
15
16 #include <sodium.h>
17
18 #include "config.h"
19 #include "misc.h"
20 #include "queue.h"
21 #include "snap.h"
22 #include "state.h"
23
24 /* snapshot encryption algorithms */
25 #define SNONETYPE 0x400
26 #define SCHACHATYPE 0x401
27
28 /* snapshot header constants */
29 #define SHDRMAGIC "SNAPSNAPPYSNOOP"
30 #define NSHDRMAGIC 16
31
32 #define VMIN 0
33 #define VMAJ 1
34 #define VMINMASK 0xff
35 #define VMAJSHIFT 8
36 #define VMAJMASK 0xff
37
38 #define CRYPTOHDRSIZE crypto_secretstream_xchacha20poly1305_HEADERBYTES
39 #define SHDRSIZE (NSHDRMAGIC + CRYPTOHDRSIZE + 8 + 8)
40
41 extern struct param param;
42
43 /* misc helpers */
44 extern int pack(unsigned char *, char *, ...);
45 extern int unpack(unsigned char *, char *, ...);
46
47 /* Snapshot header structure */
48 struct shdr {
49 char magic[NSHDRMAGIC]; /* magic number for file(1) */
50 unsigned char header[CRYPTOHDRSIZE]; /* xchacha20-poly1305 crypto header */
51 uint64_t flags; /* version number */
52 uint64_t nbd; /* number of block hashes */
53 };
54
55 struct mdnode {
56 unsigned char md[MDSIZE]; /* hash of block */
57 TAILQ_ENTRY(mdnode) e; /* mdhead link node */
58 };
59
60 struct sctx {
61 TAILQ_HEAD(mdhead, mdnode) mdhead; /* list of hashes contained in snapshot */
62 struct mdnode *mdnext; /* next hash to be returned via sget() */
63 int type; /* encryption algorithm */
64 int fd; /* underlying snapshot file descriptor */
65 int rdonly; /* when set, ssync() is a no-op */
66 struct shdr shdr; /* snapshot header */
67 };
68
69 /* Unpack snapshot header */
70 static int
71 unpackshdr(unsigned char *buf, struct shdr *shdr)
72 {
73 char fmt[BUFSIZ];
74 int n;
75
76 snprintf(fmt, sizeof(fmt), "'%d'%dqq", NSHDRMAGIC, CRYPTOHDRSIZE);
77 n = unpack(buf, fmt,
78 shdr->magic,
79 shdr->header,
80 &shdr->flags,
81 &shdr->nbd);
82
83 assert(n == SHDRSIZE);
84 return n;
85 }
86
87 /* Pack snapshot header */
88 static int
89 packshdr(unsigned char *buf, struct shdr *shdr)
90 {
91 char fmt[BUFSIZ];
92 int n;
93
94 snprintf(fmt, sizeof(fmt), "'%d'%dqq", NSHDRMAGIC, CRYPTOHDRSIZE);
95 n = pack(buf, fmt,
96 shdr->magic,
97 shdr->header,
98 shdr->flags,
99 shdr->nbd);
100
101 assert(n == SHDRSIZE);
102 return n;
103 }
104
105 static int
106 loadmdnone(struct sctx *sctx, int first)
107 {
108 struct mdnode *mdnode;
109
110 mdnode = calloc(1, sizeof(*mdnode));
111 if (mdnode == NULL) {
112 seterr("calloc: out of memory");
113 return -1;
114 }
115
116 if (xread(sctx->fd, mdnode->md, MDSIZE) != MDSIZE) {
117 seterr("failed to read block hash: %s", strerror(errno));
118 return -1;
119 }
120
121 TAILQ_INSERT_TAIL(&sctx->mdhead, mdnode, e);
122 return 0;
123 }
124
125 static int
126 loadmdchacha(struct sctx *sctx, int first)
127 {
128 unsigned char buf[MDSIZE + crypto_secretstream_xchacha20poly1305_ABYTES];
129 unsigned char hdr[SHDRSIZE];
130 static crypto_secretstream_xchacha20poly1305_state state;
131 struct mdnode *mdnode;
132 struct shdr *shdr;
133
134 shdr = &sctx->shdr;
135 packshdr(hdr, shdr);
136 if (first && crypto_secretstream_xchacha20poly1305_init_pull(&state,
137 shdr->header,
138 param.key) < 0) {
139 seterr("invalid crypto header");
140 return -1;
141 }
142
143 if (xread(sctx->fd, buf, sizeof(buf)) != sizeof(buf)) {
144 seterr("failed to read block hash: %s", strerror(errno));
145 return -1;
146 }
147
148 mdnode = calloc(1, sizeof(*mdnode));
149 if (mdnode == NULL) {
150 seterr("calloc: out of memory");
151 return -1;
152 }
153
154 if (crypto_secretstream_xchacha20poly1305_pull(&state, mdnode->md, NULL,
155 NULL, buf, sizeof(buf),
156 hdr, sizeof(hdr)) < 0) {
157 free(mdnode);
158 seterr("authentication failed");
159 return -1;
160 }
161
162 TAILQ_INSERT_TAIL(&sctx->mdhead, mdnode, e);
163 return 0;
164 }
165
166 static int
167 initmdhead(struct sctx *sctx)
168 {
169 struct shdr *shdr;
170 int (*loadmd)(struct sctx *, int);
171 uint64_t i;
172
173 if (sctx->type == SNONETYPE)
174 loadmd = loadmdnone;
175 else
176 loadmd = loadmdchacha;
177
178 shdr = &sctx->shdr;
179 for (i = 0; i < shdr->nbd; i++) {
180 if ((*loadmd)(sctx, i == 0) == 0)
181 continue;
182
183 while (!TAILQ_EMPTY(&sctx->mdhead)) {
184 struct mdnode *mdnode;
185
186 mdnode = TAILQ_FIRST(&sctx->mdhead);
187 TAILQ_REMOVE(&sctx->mdhead, mdnode, e);
188 free(mdnode);
189 }
190 return -1;
191 }
192 return 0;
193 }
194
195 int
196 screat(char *path, int mode, struct sctx **sctx)
197 {
198 unsigned char buf[SHDRSIZE];
199 struct shdr *shdr;
200 int type;
201 int fd;
202
203 if (path == NULL || sctx == NULL) {
204 seterr("invalid params");
205 return -1;
206 }
207
208 /* Determine algorithm type */
209 if (strcasecmp(param.ealgo, "none") == 0) {
210 type = SNONETYPE;
211 } else if (strcasecmp(param.ealgo, "XChaCha20-Poly1305") == 0) {
212 type = SCHACHATYPE;
213 } else {
214 seterr("invalid encryption type: %s", param.ealgo);
215 return -1;
216 }
217
218 /* Ensure a key has been provided if caller requested encryption */
219 if (type != SNONETYPE && !param.keyloaded) {
220 seterr("expected encryption key");
221 return -1;
222 }
223
224 if (sodium_init() < 0) {
225 seterr("sodium_init: failed");
226 return -1;
227 }
228
229 fd = open(path, O_RDWR | O_CREAT | O_EXCL, mode);
230 if (fd < 0) {
231 seterr("open: %s", strerror(errno));
232 return -1;
233 }
234
235 *sctx = calloc(1, sizeof(**sctx));
236 if (*sctx == NULL) {
237 close(fd);
238 seterr("calloc: out of memory");
239 return -1;
240 }
241
242 TAILQ_INIT(&(*sctx)->mdhead);
243 (*sctx)->mdnext = NULL;
244 (*sctx)->type = type;
245 (*sctx)->fd = fd;
246
247 shdr = &(*sctx)->shdr;
248 memcpy(shdr->magic, SHDRMAGIC, NSHDRMAGIC);
249 shdr->flags = (VMAJ << VMAJSHIFT) | VMIN;
250 shdr->nbd = 0;
251
252 packshdr(buf, shdr);
253 if (xwrite(fd, buf, SHDRSIZE) != SHDRSIZE) {
254 free(*sctx);
255 close(fd);
256 seterr("failed to write snapshot header: %s", strerror(errno));
257 return -1;
258 }
259 return 0;
260 }
261
262 int
263 sopen(char *path, int flags, int mode, struct sctx **sctx)
264 {
265 unsigned char buf[SHDRSIZE];
266 struct shdr *shdr;
267 int type;
268 int fd;
269
270 if (path == NULL || sctx == NULL) {
271 seterr("invalid params");
272 return -1;
273 }
274
275 /* Existing snapshots are immutable */
276 if (flags != S_READ) {
277 seterr("invalid params");
278 return -1;
279 }
280
281 /* Determine algorithm type */
282 if (strcasecmp(param.ealgo, "none") == 0) {
283 type = SNONETYPE;
284 } else if (strcasecmp(param.ealgo, "XChaCha20-Poly1305") == 0) {
285 type = SCHACHATYPE;
286 } else {
287 seterr("invalid encryption type: %s", param.ealgo);
288 return -1;
289 }
290
291 /* Ensure a key has been provided if caller requested encryption */
292 if (type != SNONETYPE && !param.keyloaded) {
293 seterr("expected encryption key");
294 return -1;
295 }
296
297 if (sodium_init() < 0) {
298 seterr("sodium_init: failed");
299 return -1;
300 }
301
302 fd = open(path, O_RDONLY, mode);
303 if (fd < 0) {
304 seterr("open: %s", strerror(errno));
305 return -1;
306 }
307
308 *sctx = calloc(1, sizeof(**sctx));
309 if (*sctx == NULL) {
310 close(fd);
311 seterr("calloc: out of memory");
312 return -1;
313 }
314
315 TAILQ_INIT(&(*sctx)->mdhead);
316 (*sctx)->mdnext = NULL;
317 (*sctx)->type = type;
318 (*sctx)->fd = fd;
319 (*sctx)->rdonly = 1;
320
321 shdr = &(*sctx)->shdr;
322
323 if (xread(fd, buf, SHDRSIZE) != SHDRSIZE) {
324 free(sctx);
325 close(fd);
326 seterr("failed to read snapshot header: %s", strerror(errno));
327 return -1;
328 }
329 unpackshdr(buf, shdr);
330
331 if (memcmp(shdr->magic, SHDRMAGIC, NSHDRMAGIC) != 0) {
332 free(sctx);
333 close(fd);
334 seterr("unknown snapshot header magic");
335 return -1;
336 }
337
338 /* If the major version is different, the format is incompatible */
339 if (((shdr->flags >> VMAJSHIFT) & VMAJMASK) != VMAJ) {
340 free(sctx);
341 close(fd);
342 seterr("snapshot header version mismatch");
343 return -1;
344 }
345
346 if (initmdhead(*sctx) < 0) {
347 free(*sctx);
348 close(fd);
349 return -1;
350 }
351 return 0;
352 }
353
354 int
355 sput(struct sctx *sctx, unsigned char *md)
356 {
357 struct shdr *shdr;
358 struct mdnode *mdnode;
359
360 if (sctx == NULL || md == NULL) {
361 seterr("invalid params");
362 return -1;
363 }
364
365 mdnode = calloc(1, sizeof(*mdnode));
366 if (mdnode == NULL) {
367 seterr("calloc: out of memory");
368 return -1;
369 }
370 shdr = &sctx->shdr;
371 shdr->nbd++;
372 memcpy(mdnode->md, md, MDSIZE);
373 TAILQ_INSERT_TAIL(&sctx->mdhead, mdnode, e);
374 return 0;
375 }
376
377 int
378 sget(struct sctx *sctx, unsigned char *md)
379 {
380 struct mdnode *mdnode;
381
382 if (sctx == NULL || md == NULL) {
383 seterr("invalid params");
384 return -1;
385 }
386
387 mdnode = sctx->mdnext;
388 if (mdnode == NULL)
389 mdnode = TAILQ_FIRST(&sctx->mdhead);
390 else
391 mdnode = TAILQ_NEXT(mdnode, e);
392 sctx->mdnext = mdnode;
393 if (mdnode != NULL) {
394 memcpy(md, mdnode->md, MDSIZE);
395 return MDSIZE;
396 }
397 return 0;
398 }
399
400 static int
401 syncnone(struct sctx *sctx)
402 {
403 unsigned char hdr[SHDRSIZE];
404 struct mdnode *mdnode;
405 struct shdr *shdr;
406
407 shdr = &sctx->shdr;
408 packshdr(hdr, shdr);
409 if (xwrite(sctx->fd, hdr, SHDRSIZE) != SHDRSIZE) {
410 seterr("failed to write snapshot header: %s", strerror(errno));
411 return -1;
412 }
413
414 TAILQ_FOREACH(mdnode, &sctx->mdhead, e) {
415 if (xwrite(sctx->fd, mdnode->md, MDSIZE) != MDSIZE) {
416 seterr("failed to write block hash: %s",
417 strerror(errno));
418 return -1;
419 }
420 }
421 return 0;
422 }
423
424 static int
425 syncchacha(struct sctx *sctx)
426 {
427 unsigned char hdr[SHDRSIZE];
428 crypto_secretstream_xchacha20poly1305_state state;
429 struct mdnode *mdnode;
430 struct shdr *shdr;
431
432 shdr = &sctx->shdr;
433 crypto_secretstream_xchacha20poly1305_init_push(&state,
434 shdr->header,
435 param.key);
436
437 packshdr(hdr, shdr);
438 if (xwrite(sctx->fd, hdr, SHDRSIZE) != SHDRSIZE) {
439 seterr("failed to write snapshot header: %s", strerror(errno));
440 return -1;
441 }
442
443 TAILQ_FOREACH(mdnode, &sctx->mdhead, e) {
444 unsigned char buf[MDSIZE + crypto_secretstream_xchacha20poly1305_ABYTES];
445 unsigned char tag;
446
447 if (TAILQ_LAST(&sctx->mdhead, mdhead) == mdnode)
448 tag = crypto_secretstream_xchacha20poly1305_TAG_FINAL;
449 else
450 tag = 0;
451
452 crypto_secretstream_xchacha20poly1305_push(&state,
453 buf, NULL,
454 mdnode->md, MDSIZE,
455 hdr, SHDRSIZE, tag);
456 if (xwrite(sctx->fd, buf, sizeof(buf)) != sizeof(buf)) {
457 seterr("failed to write block hash: %s",
458 strerror(errno));
459 return -1;
460 }
461 }
462 return 0;
463 }
464
465 int
466 ssync(struct sctx *sctx)
467 {
468 if (sctx == NULL) {
469 seterr("invalid params");
470 return -1;
471 }
472
473 if (sctx->rdonly)
474 return 0;
475
476 if (lseek(sctx->fd, 0, SEEK_SET) < 0) {
477 seterr("lseek: %s", strerror(errno));
478 return -1;
479 }
480
481 if (sctx->type == SNONETYPE)
482 syncnone(sctx);
483 else
484 syncchacha(sctx);
485
486 fsync(sctx->fd);
487 return 0;
488 }
489
490 int
491 sclose(struct sctx *sctx)
492 {
493 int r;
494
495 if (sctx == NULL)
496 return -1;
497
498 if (ssync(sctx) < 0)
499 return -1;
500
501 /* Free block hash list */
502 while (!TAILQ_EMPTY(&sctx->mdhead)) {
503 struct mdnode *mdnode;
504
505 mdnode = TAILQ_FIRST(&sctx->mdhead);
506 TAILQ_REMOVE(&sctx->mdhead, mdnode, e);
507 free(mdnode);
508 }
509
510 r = close(sctx->fd);
511 free(sctx);
512 if (r < 0)
513 seterr("close: %s", strerror(errno));
514 return r;
515 }