dedup

deduplicating backup program
git clone git://git.2f30.org/dedup
Log | Files | Refs | README | LICENSE

bcompress.c (6415B)


      1 /* Compression layer implementation */
      2 #include <sys/types.h>
      3 #include <sys/stat.h>
      4 
      5 #include <assert.h>
      6 #include <fcntl.h>
      7 #include <stdint.h>
      8 #include <stdio.h>
      9 #include <stdlib.h>
     10 #include <string.h>
     11 #include <strings.h>
     12 #include <unistd.h>
     13 
     14 #include <lz4.h>
     15 #include <snappy-c.h>
     16 
     17 #include "block.h"
     18 #include "config.h"
     19 #include "misc.h"
     20 #include "state.h"
     21 
     22 #define CDNONETYPE	0x200
     23 #define CDSNAPPYTYPE	0x201
     24 #define CDLZ4TYPE	0x202
     25 #define CDSIZE		(8 + 8)
     26 
     27 extern struct param param;
     28 
     29 extern int pack(unsigned char *, char *, ...);
     30 extern int unpack(unsigned char *, char *, ...);
     31 
     32 static int bccreat(struct bctx *bctx, char *path, int mode);
     33 static int bcopen(struct bctx *bctx, char *path, int flags, int mode);
     34 static int bcput(struct bctx *bctx, void *buf, size_t n, unsigned char *md);
     35 static int bcget(struct bctx *bctx, unsigned char *md, void *buf, size_t *n);
     36 static int bcrm(struct bctx *bctx, unsigned char *md);
     37 static int bcgc(struct bctx *bctx);
     38 static int bcsync(struct bctx *bctx);
     39 static int bcclose(struct bctx *bctx);
     40 
     41 static struct bops bops = {
     42 	.creat = bccreat,
     43 	.open = bcopen,
     44 	.put = bcput,
     45 	.get = bcget,
     46 	.rm = bcrm,
     47 	.gc = bcgc,
     48 	.sync = bcsync,
     49 	.close = bcclose,
     50 };
     51 
     52 /* Compression layer context */
     53 struct cctx {
     54 	int type;	/* compression algorithm type for new blocks */
     55 };
     56 
     57 /* Compression descriptor */
     58 struct cd {
     59 	uint16_t type;			/* compression algorithm type */
     60 	unsigned char reserved[6];	/* should be set to 0 when writing */
     61 	uint64_t size;			/* size of compressed block */
     62 };
     63 
     64 /* Unpack compression descriptor */
     65 static int
     66 unpackcd(void *buf, struct cd *cd)
     67 {
     68 	int n;
     69 
     70 	n = unpack(buf, "s'6q",
     71 	           &cd->type,
     72 	           cd->reserved,
     73 	           &cd->size);
     74 
     75 	assert(n == CDSIZE);
     76 	return n;
     77 }
     78 
     79 /* Pack compression descriptor */
     80 static int
     81 packcd(void *buf, struct cd *cd)
     82 {
     83 	int n;
     84 
     85 	n = pack(buf, "s'6q",
     86 	         cd->type,
     87 	         cd->reserved,
     88 	         cd->size);
     89 
     90 	assert(n == CDSIZE);
     91 	return n;
     92 }
     93 
     94 static int
     95 bccreat(struct bctx *bctx, char *path, int mode)
     96 {
     97 	struct cctx *cctx;
     98 	int type;
     99 
    100 	if (strcasecmp(param.calgo, "none") == 0) {
    101 		type = CDNONETYPE;
    102 	} else if (strcasecmp(param.calgo, "snappy") == 0) {
    103 		type = CDSNAPPYTYPE;
    104 	} else if (strcasecmp(param.calgo, "lz4") == 0) {
    105 		type = CDLZ4TYPE;
    106 	} else {
    107 		seterr("invalid compression type: %s", param.calgo);
    108 		return -1;
    109 	}
    110 
    111 	bctx->cctx = calloc(1, sizeof(struct cctx));
    112 	if (bctx->cctx == NULL) {
    113 		seterr("calloc: out of memory");
    114 		return -1;
    115 	}
    116 	cctx = bctx->cctx;
    117 	cctx->type = type;
    118 
    119 	if (bencryptops()->creat(bctx, path, mode) < 0) {
    120 		free(cctx);
    121 		return -1;
    122 	}
    123 	return 0;
    124 }
    125 
    126 static int
    127 bcopen(struct bctx *bctx, char *path, int flags, int mode)
    128 {
    129 	struct cctx *cctx;
    130 	int type;
    131 
    132 	if (strcasecmp(param.calgo, "none") == 0) {
    133 		type = CDNONETYPE;
    134 	} else if (strcasecmp(param.calgo, "snappy") == 0) {
    135 		type = CDSNAPPYTYPE;
    136 	} else if (strcasecmp(param.calgo, "lz4") == 0) {
    137 		type = CDLZ4TYPE;
    138 	} else {
    139 		seterr("invalid compression type: %s", param.calgo);
    140 		return -1;
    141 	}
    142 
    143 	bctx->cctx = calloc(1, sizeof(struct cctx));
    144 	if (bctx->cctx == NULL) {
    145 		seterr("calloc: out of memory");
    146 		return -1;
    147 	}
    148 	cctx = bctx->cctx;
    149 	cctx->type = type;
    150 
    151 	if (bencryptops()->open(bctx, path, flags, mode) < 0) {
    152 		free(cctx);
    153 		return -1;
    154 	}
    155 	return 0;
    156 }
    157 
    158 static int
    159 bcput(struct bctx *bctx, void *buf, size_t n, unsigned char *md)
    160 {
    161 	struct cctx *cctx;
    162 	struct cd cd;
    163 	char *cbuf;
    164 	size_t cn;
    165 	int r;
    166 
    167 	/* Calculate compressed block size */
    168 	cctx = bctx->cctx;
    169 	switch (cctx->type) {
    170 	case CDNONETYPE:
    171 		cn = n;
    172 		break;
    173 	case CDSNAPPYTYPE:
    174 		cn = snappy_max_compressed_length(n);
    175 		break;
    176 	case CDLZ4TYPE:
    177 		cn = LZ4_compressBound(n);
    178 		break;
    179 	}
    180 
    181 	cbuf = malloc(CDSIZE + cn);
    182 	if (cbuf == NULL) {
    183 		seterr("malloc: out of memory");
    184 		return -1;
    185 	}
    186 
    187 	/* Compress block */
    188 	switch (cctx->type) {
    189 	case CDNONETYPE:
    190 		memcpy(&cbuf[CDSIZE], buf, cn);
    191 		break;
    192 	case CDSNAPPYTYPE:
    193 		if (snappy_compress(buf, n, &cbuf[CDSIZE], &cn) != SNAPPY_OK) {
    194 			free(cbuf);
    195 			seterr("snappy_compress: failed");
    196 			return -1;
    197 		}
    198 		break;
    199 	case CDLZ4TYPE:
    200 		r = LZ4_compress_default(buf, &cbuf[CDSIZE], n, cn);
    201 		if (r < 0) {
    202 			free(cbuf);
    203 			seterr("LZ4_compress_default: failed");
    204 			return -1;
    205 		}
    206 		cn = r;
    207 		break;
    208 	}
    209 
    210 	/* Prepare compression descriptor */
    211 	cd.type = cctx->type;
    212 	memset(cd.reserved, 0, sizeof(cd.reserved));
    213 	cd.size = cn;
    214 	/* Prepend compression descriptor */
    215 	packcd(cbuf, &cd);
    216 
    217 	if (bencryptops()->put(bctx, cbuf, CDSIZE + cn, md) < 0) {
    218 		free(cbuf);
    219 		return -1;
    220 	}
    221 
    222 	free(cbuf);
    223 	return cd.size;
    224 }
    225 
    226 static int
    227 bcget(struct bctx *bctx, unsigned char *md, void *buf, size_t *n)
    228 {
    229 	struct cd cd;
    230 	char *cbuf;
    231 	size_t cn, un, size;
    232 	int r;
    233 
    234 	/* Calculate maximum compressed block size */
    235 	size = *n;
    236 	cn = snappy_max_compressed_length(*n);
    237 	if (cn > size)
    238 		size = cn;
    239 	cn = LZ4_compressBound(*n);
    240 	if (cn > size)
    241 		size = cn;
    242 	size += CDSIZE;
    243 
    244 	cbuf = malloc(size);
    245 	if (cbuf == NULL) {
    246 		seterr("malloc: out of memory");
    247 		return -1;
    248 	}
    249 
    250 	if (bencryptops()->get(bctx, md, cbuf, &size) < 0) {
    251 		free(cbuf);
    252 		return -1;
    253 	}
    254 
    255 	unpackcd(cbuf, &cd);
    256 
    257 	/* Decompress block */
    258 	switch (cd.type) {
    259 	case CDNONETYPE:
    260 		un = cd.size;
    261 		if (*n < un) {
    262 			free(cbuf);
    263 			seterr("buffer too small");
    264 			return -1;
    265 		}
    266 		memcpy(buf, &cbuf[CDSIZE], un);
    267 		break;
    268 	case CDSNAPPYTYPE:
    269 		if (snappy_uncompressed_length(&cbuf[CDSIZE], cd.size,
    270 		                               &un) != SNAPPY_OK) {
    271 			free(cbuf);
    272 			seterr("snappy_uncompressed_length: failed");
    273 			return -1;
    274 		}
    275 
    276 		if (*n < un) {
    277 			free(cbuf);
    278 			seterr("buffer too small");
    279 			return -1;
    280 		}
    281 
    282 		if (snappy_uncompress(&cbuf[CDSIZE], cd.size, buf,
    283 		                      &un) != SNAPPY_OK) {
    284 			free(cbuf);
    285 			seterr("snappy_uncompress: failed");
    286 			return -1;
    287 		}
    288 		break;
    289 	case CDLZ4TYPE:
    290 		r = LZ4_decompress_safe(&cbuf[CDSIZE], buf, cd.size, *n);
    291 		if (r < 0) {
    292 			free(cbuf);
    293 			seterr("LZ4_decompress_safe: failed");
    294 			return -1;
    295 		}
    296 		un = r;
    297 		break;
    298 	}
    299 
    300 	free(cbuf);
    301 	*n = un;
    302 	return 0;
    303 }
    304 
    305 static int
    306 bcrm(struct bctx *bctx, unsigned char *md)
    307 {
    308 	return bencryptops()->rm(bctx, md);
    309 }
    310 
    311 static int
    312 bcgc(struct bctx *bctx)
    313 {
    314 	return bencryptops()->gc(bctx);
    315 }
    316 
    317 static int
    318 bcsync(struct bctx *bctx)
    319 {
    320 	return bencryptops()->sync(bctx);
    321 }
    322 
    323 static int
    324 bcclose(struct bctx *bctx)
    325 {
    326 	struct cctx *cctx = bctx->cctx;
    327 
    328 	free(cctx);
    329 	return bencryptops()->close(bctx);
    330 }
    331 
    332 struct bops *
    333 bcompressops(void)
    334 {
    335 	return &bops;
    336 }