URI: 
       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 }