Coverage Report

Created: 2025-01-09 06:02

/src/casync/src/compressor.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1+ */
2
3
#include "compressor.h"
4
#include "util.h"
5
6
/* #undef EIO */
7
/* #define EIO __LINE__ */
8
9
/* #undef EBADMSG */
10
/* #define EBADMSG __LINE__ */
11
12
0
int detect_compression(const void *buffer, size_t size) {
13
0
        static const uint8_t xz_signature[] = {
14
0
                0xfd, '7', 'z', 'X', 'Z', 0x00
15
0
        };
16
17
0
        static const uint8_t gzip_signature[] = {
18
0
                0x1f, 0x8b
19
0
        };
20
21
0
        static const uint8_t zstd_signature[] = {
22
0
                0x28, 0xb5, 0x2f, 0xfd
23
0
        };
24
25
0
        if (size >= sizeof(xz_signature) &&
26
0
            memcmp(buffer, xz_signature, sizeof(xz_signature)) == 0)
27
0
                return CA_COMPRESSION_XZ;
28
29
0
        if (size >= sizeof(gzip_signature) &&
30
0
            memcmp(buffer, gzip_signature, sizeof(gzip_signature)) == 0)
31
0
                return CA_COMPRESSION_GZIP;
32
33
0
        if (size >= sizeof(zstd_signature) &&
34
0
            memcmp(buffer, zstd_signature, sizeof(zstd_signature)) == 0)
35
0
                return CA_COMPRESSION_ZSTD;
36
37
0
        if (size < MAX3(sizeof(xz_signature), sizeof(gzip_signature), sizeof(zstd_signature)))
38
0
                return -EAGAIN; /* Not ready to decide yet */
39
40
0
        return -EBADMSG;
41
0
}
42
43
0
bool compressor_is_supported(CaCompressionType compressor) {
44
0
        switch (compressor) {
45
0
        case CA_COMPRESSION_XZ:
46
0
                return HAVE_LIBLZMA;
47
0
        case CA_COMPRESSION_GZIP:
48
0
                return HAVE_LIBZ;
49
0
        case CA_COMPRESSION_ZSTD:
50
0
                return HAVE_LIBZSTD;
51
0
        default:
52
0
                assert("Unknown compression type");
53
0
                return false;
54
0
        }
55
0
}
56
57
211
int compressor_start_decode(CompressorContext *c, CaCompressionType compressor) {
58
211
        int r;
59
60
211
        if (!c)
61
0
                return -EINVAL;
62
211
        if (compressor < 0)
63
37
                return -EINVAL;
64
174
        if (compressor >= _CA_COMPRESSION_TYPE_MAX)
65
33
                return -EOPNOTSUPP;
66
67
141
        switch (compressor) {
68
69
33
        case CA_COMPRESSION_XZ: {
70
33
#if HAVE_LIBLZMA
71
33
                lzma_ret xzr;
72
73
33
                xzr = lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK);
74
33
                if (xzr != LZMA_OK)
75
0
                        return -EIO;
76
33
                break;
77
#else
78
                return -ENOSYS;
79
#endif
80
33
        }
81
82
107
        case CA_COMPRESSION_GZIP:
83
107
#if HAVE_LIBZ
84
107
                r = inflateInit2(&c->gzip, 15 | 16);
85
107
                if (r != Z_OK)
86
0
                        return -EIO;
87
107
                break;
88
#else
89
                return -ENOSYS;
90
#endif
91
92
107
        case CA_COMPRESSION_ZSTD:
93
#if HAVE_LIBZSTD
94
                c->zstd.dstream = ZSTD_createDStream();
95
                if (!c->zstd.dstream)
96
                        return -ENOMEM;
97
98
                ZSTD_initDStream(c->zstd.dstream);
99
                break;
100
#else
101
1
                return -ENOSYS;
102
0
#endif
103
104
0
        default:
105
0
                assert_not_reached("Unknown decompressor.");
106
141
        }
107
108
140
        c->operation = COMPRESSOR_DECODE;
109
140
        c->compressor = compressor;
110
111
140
        return 0;
112
141
}
113
114
0
int compressor_start_encode(CompressorContext *c, CaCompressionType compressor) {
115
0
        int r;
116
117
0
        if (!c)
118
0
                return -EINVAL;
119
0
        if (compressor < 0)
120
0
                return -EINVAL;
121
0
        if (compressor >= _CA_COMPRESSION_TYPE_MAX)
122
0
                return -EOPNOTSUPP;
123
124
0
        switch (compressor) {
125
126
0
        case CA_COMPRESSION_XZ: {
127
0
#if HAVE_LIBLZMA
128
0
                lzma_ret xzr;
129
130
0
                xzr = lzma_easy_encoder(&c->xz, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
131
0
                if (xzr != LZMA_OK)
132
0
                        return -EIO;
133
0
                break;
134
#else
135
                return -ENOSYS;
136
#endif
137
0
        }
138
139
0
        case CA_COMPRESSION_GZIP:
140
0
#if HAVE_LIBZ
141
0
                r = deflateInit2(&c->gzip, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | 16, 8, Z_DEFAULT_STRATEGY);
142
0
                if (r != Z_OK)
143
0
                        return -EIO;
144
0
                break;
145
#else
146
                return -ENOSYS;
147
#endif
148
149
0
        case CA_COMPRESSION_ZSTD:
150
#if HAVE_LIBZSTD
151
                c->zstd.cstream = ZSTD_createCStream();
152
                if (!c->zstd.cstream)
153
                        return -ENOMEM;
154
155
                ZSTD_initCStream(c->zstd.cstream, 3);
156
                break;
157
#else
158
0
                return -ENOSYS;
159
0
#endif
160
161
0
        default:
162
0
                assert_not_reached("Unknown compressor.");
163
0
        }
164
165
0
        c->operation = COMPRESSOR_ENCODE;
166
0
        c->compressor = compressor;
167
168
0
        return 0;
169
0
}
170
171
211
void compressor_finish(CompressorContext *c) {
172
211
        if (!c)
173
0
                return;
174
211
        if (c->operation == COMPRESSOR_UNINITIALIZED)
175
71
                return;
176
177
140
        switch (c->compressor) {
178
179
33
        case CA_COMPRESSION_XZ:
180
33
#if HAVE_LIBLZMA
181
33
                lzma_end(&c->xz);
182
33
#endif
183
33
                break;
184
185
107
        case CA_COMPRESSION_GZIP:
186
107
#if HAVE_LIBZ
187
107
                if (c->operation == COMPRESSOR_DECODE)
188
107
                        inflateEnd(&c->gzip);
189
0
                else if (c->operation == COMPRESSOR_ENCODE)
190
0
                        deflateEnd(&c->gzip);
191
0
                else
192
0
                        assert_not_reached("Unknown operation.");
193
107
#endif
194
107
                break;
195
196
107
        case CA_COMPRESSION_ZSTD:
197
#if HAVE_LIBZSTD
198
                if (c->operation == COMPRESSOR_ENCODE)
199
                        ZSTD_freeCStream(c->zstd.cstream);
200
                else if (c->operation == COMPRESSOR_DECODE)
201
                        ZSTD_freeDStream(c->zstd.dstream);
202
                else
203
                        assert_not_reached("Unknown operation.");
204
#endif
205
0
                break;
206
207
0
        default:
208
0
                assert_not_reached("Unknown compressor.");
209
140
        }
210
140
}
211
212
140
int compressor_input(CompressorContext *c, const void *p, size_t sz) {
213
214
140
        if (!c)
215
0
                return -EINVAL;
216
140
        if (sz > 0 && !p)
217
0
                return -EINVAL;
218
219
140
        switch (c->compressor) {
220
221
33
        case CA_COMPRESSION_XZ:
222
33
#if HAVE_LIBLZMA
223
33
                c->xz.next_in = p;
224
33
                c->xz.avail_in = sz;
225
33
                break;
226
#else
227
                return -ENOSYS;
228
#endif
229
230
107
        case CA_COMPRESSION_GZIP:
231
107
#if HAVE_LIBZ
232
107
                c->gzip.next_in = (void*) p;
233
107
                c->gzip.avail_in = sz;
234
107
                break;
235
#else
236
                return -ENOSYS;
237
#endif
238
239
0
        case CA_COMPRESSION_ZSTD:
240
#if HAVE_LIBZSTD
241
                c->zstd.input = (ZSTD_inBuffer) {
242
                        .src = p,
243
                        .size = sz,
244
                };
245
                break;
246
#else
247
0
                return -ENOSYS;
248
0
#endif
249
250
0
        default:
251
0
                assert_not_reached("Unknown compressor.");
252
140
        }
253
254
140
        return 0;
255
140
}
256
257
140
int compressor_decode(CompressorContext *c, void *p, size_t sz, size_t *ret_done) {
258
140
        int r;
259
260
140
        if (!c)
261
0
                return -EINVAL;
262
140
        if (sz > 0 && !p)
263
0
                return -EINVAL;
264
140
        if (!ret_done)
265
0
                return -EINVAL;
266
267
140
        if (c->operation != COMPRESSOR_DECODE)
268
0
                return -ENOTTY;
269
270
140
        switch (c->compressor) {
271
272
33
        case CA_COMPRESSION_XZ: {
273
33
#if HAVE_LIBLZMA
274
33
                lzma_ret xzr;
275
276
33
                c->xz.next_out = p;
277
33
                c->xz.avail_out = sz;
278
279
33
                assert(c->xz.avail_out > 0);
280
33
                assert(c->xz.avail_in > 0);
281
282
33
                xzr = lzma_code(&c->xz, LZMA_RUN);
283
33
                if (xzr == LZMA_STREAM_END) {
284
285
0
                        if (c->xz.avail_in > 0)
286
0
                                return -EBADMSG;
287
288
0
                        *ret_done = sz - c->xz.avail_out;
289
0
                        return COMPRESSOR_EOF;
290
291
33
                } else if (xzr != LZMA_OK)
292
30
                        return -EIO;
293
294
3
                *ret_done = sz - c->xz.avail_out;
295
296
3
                if (c->xz.avail_in > 0)
297
0
                        return COMPRESSOR_MORE;
298
299
3
                return COMPRESSOR_GOOD;
300
#else
301
                return -ENOSYS;
302
#endif
303
3
        }
304
305
107
        case CA_COMPRESSION_GZIP:
306
107
#if HAVE_LIBZ
307
107
                c->gzip.next_out = p;
308
107
                c->gzip.avail_out = sz;
309
310
107
                assert(c->gzip.avail_out > 0);
311
107
                assert(c->gzip.avail_in > 0);
312
313
107
                r = inflate(&c->gzip, Z_NO_FLUSH);
314
107
                if (r == Z_STREAM_END) {
315
316
35
                        if (c->gzip.avail_in > 0)
317
34
                                return -EBADMSG;
318
319
1
                        *ret_done = sz - c->gzip.avail_out;
320
1
                        return COMPRESSOR_EOF;
321
322
72
                } else if (r != Z_OK)
323
46
                        return -EIO;
324
325
26
                *ret_done = sz - c->gzip.avail_out;
326
327
26
                if (c->gzip.avail_in > 0)
328
25
                        return COMPRESSOR_MORE;
329
330
1
                return COMPRESSOR_GOOD;
331
#else
332
                return -ENOSYS;
333
#endif
334
335
0
        case CA_COMPRESSION_ZSTD: {
336
#if HAVE_LIBZSTD
337
                size_t k;
338
339
                c->zstd.output = (ZSTD_outBuffer) {
340
                        .dst = p,
341
                        .size = sz,
342
                };
343
344
                assert(c->zstd.output.size > c->zstd.output.pos);
345
                assert(c->zstd.input.size > c->zstd.input.pos);
346
347
                k = ZSTD_decompressStream(c->zstd.dstream, &c->zstd.output, &c->zstd.input);
348
                if (ZSTD_isError(k))
349
                        return -EIO;
350
351
                if (k == 0) {
352
                        if (c->zstd.input.size > c->zstd.input.pos)
353
                                return -EBADMSG;
354
355
                        r = COMPRESSOR_EOF;
356
                } else if (c->zstd.input.pos < c->zstd.input.size)
357
                        r = COMPRESSOR_MORE;
358
                else
359
                        r = COMPRESSOR_GOOD;
360
361
                *ret_done = c->zstd.output.pos;
362
                return r;
363
#else
364
0
                return -ENOSYS;
365
26
#endif
366
26
        }
367
368
0
        default:
369
0
                assert_not_reached("Unknown compressor.");
370
140
        }
371
140
}
372
373
0
int compressor_encode(CompressorContext *c, bool finalize, void *p, size_t sz, size_t *ret_done) {
374
0
        _unused_ int r;
375
376
0
        if (!c)
377
0
                return -EINVAL;
378
0
        if (sz > 0 && !p)
379
0
                return -EINVAL;
380
0
        if (!ret_done)
381
0
                return -EINVAL;
382
383
0
        if (c->operation != COMPRESSOR_ENCODE)
384
0
                return -ENOTTY;
385
386
0
        switch (c->compressor) {
387
388
0
        case CA_COMPRESSION_XZ: {
389
0
#if HAVE_LIBLZMA
390
0
                lzma_ret xzr;
391
392
0
                c->xz.next_out = p;
393
0
                c->xz.avail_out = sz;
394
395
0
                xzr = lzma_code(&c->xz, finalize ? LZMA_FINISH : LZMA_RUN);
396
0
                if (xzr == LZMA_STREAM_END) {
397
0
                        assert(c->xz.avail_in == 0);
398
0
                        *ret_done = sz - c->xz.avail_out;
399
0
                        return COMPRESSOR_EOF;
400
0
                } else if (xzr != LZMA_OK)
401
0
                        return -EIO;
402
403
0
                *ret_done = sz - c->xz.avail_out;
404
405
0
                if (c->xz.avail_in > 0)
406
0
                        return COMPRESSOR_MORE;
407
408
0
                return COMPRESSOR_GOOD;
409
#else
410
                return -ENOSYS;
411
#endif
412
0
        }
413
414
0
        case CA_COMPRESSION_GZIP:
415
0
#if HAVE_LIBZ
416
0
                c->gzip.next_out = p;
417
0
                c->gzip.avail_out = sz;
418
419
0
                r = deflate(&c->gzip, finalize ? Z_FINISH : Z_NO_FLUSH);
420
0
                if (r == Z_STREAM_END) {
421
0
                        assert(c->gzip.avail_in == 0);
422
0
                        *ret_done = sz - c->gzip.avail_out;
423
0
                        return COMPRESSOR_EOF;
424
0
                } else if (r != Z_OK)
425
0
                        return -EIO;
426
427
0
                *ret_done = sz - c->gzip.avail_out;
428
429
0
                if (c->gzip.avail_in > 0)
430
0
                        return COMPRESSOR_MORE;
431
432
0
                return COMPRESSOR_GOOD;
433
#else
434
                return -ENOSYS;
435
#endif
436
437
0
        case CA_COMPRESSION_ZSTD: {
438
#if HAVE_LIBZSTD
439
                size_t k;
440
441
                c->zstd.output = (ZSTD_outBuffer) {
442
                        .dst = p,
443
                        .size = sz,
444
                };
445
446
                assert(c->zstd.output.size > c->zstd.output.pos);
447
448
                if (c->zstd.input.size > c->zstd.input.pos)
449
                        k = ZSTD_compressStream(c->zstd.cstream, &c->zstd.output, &c->zstd.input);
450
                else {
451
                        assert(finalize);
452
                        k = ZSTD_endStream(c->zstd.cstream, &c->zstd.output);
453
                }
454
                if (ZSTD_isError(k))
455
                        return -EIO;
456
457
                *ret_done = c->zstd.output.pos;
458
459
                if (c->zstd.input.pos < c->zstd.input.size)
460
                        return COMPRESSOR_MORE;
461
                if (k == 0)
462
                        return COMPRESSOR_EOF;
463
464
                return COMPRESSOR_GOOD;
465
#else
466
0
                return -ENOSYS;
467
0
#endif
468
0
        }
469
470
0
        default:
471
0
                assert_not_reached("Unknown compressor.");
472
0
        }
473
0
}