• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 24697298032

20 Apr 2026 09:22PM UTC coverage: 72.23% (+1.6%) from 70.661%
24697298032

push

github

bluca
sysupdate: Emit READY=1 status when installing

`READY=1` is already correctly emitted when acquiring an update, but was
forgotten to be emitted when subsequently installing that update.

That meant that the state tracking code in `sysupdated` and hence
`updatectl` could not properly report the progress of the install
operation, and hence printed “Already up-to-date” after a successful
update installation, rather than “Done”.

Add a test to catch this in future.

Signed-off-by: Philip Withnall <pwithnall@gnome.org>

Fixes: https://github.com/systemd/systemd/issues/41502

322667 of 446720 relevant lines covered (72.23%)

1191594.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.32
/src/basic/compress.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <stdio.h>
5
#include <sys/stat.h>
6
#include <unistd.h>
7

8
#if HAVE_XZ
9
#include <lzma.h>
10
#endif
11

12
#if HAVE_LZ4
13
#include <lz4.h>
14
#include <lz4frame.h>
15
#include <lz4hc.h>
16
#endif
17

18
#if HAVE_ZSTD
19
#include <zstd.h>
20
#include <zstd_errors.h>
21
#endif
22

23
#if HAVE_ZLIB
24
#include <zlib.h>
25
#endif
26

27
#if HAVE_BZIP2
28
#include <bzlib.h>
29
#endif
30

31
#include "sd-dlopen.h"
32

33
#include "alloc-util.h"
34
#include "bitfield.h"
35
#include "compress.h"
36
#include "dlfcn-util.h"
37
#include "io-util.h"
38
#include "log.h"
39
#include "string-table.h"
40
#include "unaligned.h"
41

42
#if HAVE_XZ
43
static void *lzma_dl = NULL;
44

45
static DLSYM_PROTOTYPE(lzma_code) = NULL;
46
static DLSYM_PROTOTYPE(lzma_easy_encoder) = NULL;
47
static DLSYM_PROTOTYPE(lzma_end) = NULL;
48
static DLSYM_PROTOTYPE(lzma_stream_buffer_encode) = NULL;
49
static DLSYM_PROTOTYPE(lzma_stream_decoder) = NULL;
50
static DLSYM_PROTOTYPE(lzma_lzma_preset) = NULL;
51

52
/* We can’t just do _cleanup_(sym_lzma_end) because a compiler bug makes
53
 * this fail with:
54
 * ../src/basic/compress.c: In function ‘decompress_blob_xz’:
55
 * ../src/basic/compress.c:304:9: error: cleanup argument not a function
56
 *   304 |         _cleanup_(sym_lzma_end) lzma_stream s = LZMA_STREAM_INIT;
57
 *       |         ^~~~~~~~~
58
 */
59
static inline void lzma_end_wrapper(lzma_stream *ls) {
2,957✔
60
        sym_lzma_end(ls);
2,957✔
61
}
2,957✔
62
#endif
63

64
#if HAVE_LZ4
65
static void *lz4_dl = NULL;
66

67
static DLSYM_PROTOTYPE(LZ4F_compressBegin) = NULL;
68
static DLSYM_PROTOTYPE(LZ4F_compressBound) = NULL;
69
static DLSYM_PROTOTYPE(LZ4F_compressEnd) = NULL;
70
static DLSYM_PROTOTYPE(LZ4F_compressUpdate) = NULL;
71
static DLSYM_PROTOTYPE(LZ4F_createCompressionContext) = NULL;
72
static DLSYM_PROTOTYPE(LZ4F_createDecompressionContext) = NULL;
73
static DLSYM_PROTOTYPE(LZ4F_decompress) = NULL;
74
static DLSYM_PROTOTYPE(LZ4F_freeCompressionContext) = NULL;
75
static DLSYM_PROTOTYPE(LZ4F_freeDecompressionContext) = NULL;
76
static DLSYM_PROTOTYPE(LZ4F_isError) = NULL;
77
static DLSYM_PROTOTYPE(LZ4_compress_HC) = NULL;
78
static DLSYM_PROTOTYPE(LZ4_compress_default) = NULL;
79
static DLSYM_PROTOTYPE(LZ4_decompress_safe) = NULL;
80
static DLSYM_PROTOTYPE(LZ4_decompress_safe_partial) = NULL;
81
static DLSYM_PROTOTYPE(LZ4_versionNumber) = NULL;
82

83
static const LZ4F_preferences_t lz4_preferences = {
84
        .frameInfo.blockSizeID = 5,
85
};
86
#endif
87

88
#if HAVE_ZSTD
89
static void *zstd_dl = NULL;
90

91
static DLSYM_PROTOTYPE(ZSTD_CCtx_setParameter) = NULL;
92
static DLSYM_PROTOTYPE(ZSTD_compress) = NULL;
93
static DLSYM_PROTOTYPE(ZSTD_compressStream2) = NULL;
94
static DLSYM_PROTOTYPE(ZSTD_createCCtx) = NULL;
95
static DLSYM_PROTOTYPE(ZSTD_createDCtx) = NULL;
96
static DLSYM_PROTOTYPE(ZSTD_CStreamInSize) = NULL;
97
static DLSYM_PROTOTYPE(ZSTD_CStreamOutSize) = NULL;
98
static DLSYM_PROTOTYPE(ZSTD_decompressStream) = NULL;
99
static DLSYM_PROTOTYPE(ZSTD_DStreamInSize) = NULL;
100
static DLSYM_PROTOTYPE(ZSTD_DStreamOutSize) = NULL;
101
static DLSYM_PROTOTYPE(ZSTD_freeCCtx) = NULL;
102
static DLSYM_PROTOTYPE(ZSTD_freeDCtx) = NULL;
103
static DLSYM_PROTOTYPE(ZSTD_getErrorCode) = NULL;
104
static DLSYM_PROTOTYPE(ZSTD_getErrorName) = NULL;
105
static DLSYM_PROTOTYPE(ZSTD_getFrameContentSize) = NULL;
106
static DLSYM_PROTOTYPE(ZSTD_isError) = NULL;
107

108
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ZSTD_DCtx*, sym_ZSTD_freeDCtx, ZSTD_freeDCtxp, NULL);
5,210,183✔
109

110
static int zstd_ret_to_errno(size_t ret) {
4✔
111
        switch (sym_ZSTD_getErrorCode(ret)) {
4✔
112
        case ZSTD_error_dstSize_tooSmall:
113
                return -ENOBUFS;
114
        case ZSTD_error_memory_allocation:
×
115
                return -ENOMEM;
×
116
        default:
×
117
                return -EBADMSG;
×
118
        }
119
}
120
#endif
121

122
#if HAVE_ZLIB
123
static void *zlib_dl = NULL;
124

125
static DLSYM_PROTOTYPE(deflateInit2_) = NULL;
126
static DLSYM_PROTOTYPE(deflate) = NULL;
127
static DLSYM_PROTOTYPE(deflateEnd) = NULL;
128
static DLSYM_PROTOTYPE(inflateInit2_) = NULL;
129
static DLSYM_PROTOTYPE(inflate) = NULL;
130
static DLSYM_PROTOTYPE(inflateEnd) = NULL;
131

132
static inline void deflateEnd_wrapper(z_stream *s) {
15✔
133
        sym_deflateEnd(s);
15✔
134
}
15✔
135

136
static inline void inflateEnd_wrapper(z_stream *s) {
276✔
137
        sym_inflateEnd(s);
276✔
138
}
276✔
139
#endif
140

141
#if HAVE_BZIP2
142
static void *bzip2_dl = NULL;
143

144
static DLSYM_PROTOTYPE(BZ2_bzCompressInit) = NULL;
145
static DLSYM_PROTOTYPE(BZ2_bzCompress) = NULL;
146
static DLSYM_PROTOTYPE(BZ2_bzCompressEnd) = NULL;
147
static DLSYM_PROTOTYPE(BZ2_bzDecompressInit) = NULL;
148
static DLSYM_PROTOTYPE(BZ2_bzDecompress) = NULL;
149
static DLSYM_PROTOTYPE(BZ2_bzDecompressEnd) = NULL;
150

151
static inline void BZ2_bzCompressEnd_wrapper(bz_stream *s) {
12✔
152
        sym_BZ2_bzCompressEnd(s);
12✔
153
}
12✔
154

155
static inline void BZ2_bzDecompressEnd_wrapper(bz_stream *s) {
273✔
156
        sym_BZ2_bzDecompressEnd(s);
273✔
157
}
273✔
158
#endif
159

160
/* Opaque Compressor/Decompressor struct definition */
161
struct Compressor {
162
        Compression type;
163
        bool encoding;
164
        union {
165
#if HAVE_XZ
166
                lzma_stream xz;
167
#endif
168
#if HAVE_LZ4
169
                struct {
170
                        LZ4F_compressionContext_t c_lz4;
171
                        void *lz4_header;        /* stashed frame header from LZ4F_compressBegin */
172
                        size_t lz4_header_size;
173
                };
174
                LZ4F_decompressionContext_t d_lz4;
175
#endif
176
#if HAVE_ZSTD
177
                ZSTD_CCtx *c_zstd;
178
                ZSTD_DCtx *d_zstd;
179
#endif
180
#if HAVE_ZLIB
181
                z_stream gzip;
182
#endif
183
#if HAVE_BZIP2
184
                bz_stream bzip2;
185
#endif
186
        };
187
};
188

189
#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
190

191
/* zlib windowBits value for gzip format: MAX_WBITS (15) + 16 to enable gzip header detection/generation */
192
#define ZLIB_WBITS_GZIP (15 + 16)
193

194
static const char* const compression_table[_COMPRESSION_MAX] = {
195
        [COMPRESSION_NONE]  = "uncompressed", /* backwards compatibility with importd */
196
        [COMPRESSION_XZ]    = "xz",
197
        [COMPRESSION_LZ4]   = "lz4",
198
        [COMPRESSION_ZSTD]  = "zstd",
199
        [COMPRESSION_GZIP]  = "gzip",
200
        [COMPRESSION_BZIP2] = "bzip2",
201
};
202

203
static const char* const compression_uppercase_table[_COMPRESSION_MAX] = {
204
        [COMPRESSION_NONE]  = "NONE", /* backwards compatibility with SYSTEMD_JOURNAL_COMPRESS=NONE */
205
        [COMPRESSION_XZ]    = "XZ",
206
        [COMPRESSION_LZ4]   = "LZ4",
207
        [COMPRESSION_ZSTD]  = "ZSTD",
208
        [COMPRESSION_GZIP]  = "GZIP",
209
        [COMPRESSION_BZIP2] = "BZIP2",
210
};
211

212
static const char* const compression_extension_table[_COMPRESSION_MAX] = {
213
        [COMPRESSION_NONE]  = "",
214
        [COMPRESSION_XZ]    = ".xz",
215
        [COMPRESSION_LZ4]   = ".lz4",
216
        [COMPRESSION_ZSTD]  = ".zst",
217
        [COMPRESSION_GZIP]  = ".gz",
218
        [COMPRESSION_BZIP2] = ".bz2",
219
};
220

221
DEFINE_STRING_TABLE_LOOKUP(compression, Compression);
1,654✔
222
DEFINE_STRING_TABLE_LOOKUP(compression_uppercase, Compression);
21✔
223
DEFINE_STRING_TABLE_LOOKUP(compression_extension, Compression);
34✔
224

225
Compression compression_from_string_harder(const char *s) {
71✔
226
        Compression c;
71✔
227

228
        assert(s);
71✔
229

230
        c = compression_from_string(s);
71✔
231
        if (c >= 0)
71✔
232
                return c;
233

234
        return compression_uppercase_from_string(s);
21✔
235
}
236

237
Compression compression_from_filename(const char *filename) {
10✔
238
        Compression c;
10✔
239
        const char *e;
10✔
240

241
        assert(filename);
10✔
242

243
        e = strrchr(filename, '.');
10✔
244
        if (!e)
10✔
245
                return COMPRESSION_NONE;
246

247
        c = compression_extension_from_string(e);
10✔
248
        if (c < 0)
10✔
249
                return COMPRESSION_NONE;
×
250

251
        return c;
252
}
253

254
bool compression_supported(Compression c) {
4,039✔
255
        static const unsigned supported =
4,039✔
256
                (1U << COMPRESSION_NONE) |
257
                (1U << COMPRESSION_XZ) * HAVE_XZ |
258
                (1U << COMPRESSION_LZ4) * HAVE_LZ4 |
259
                (1U << COMPRESSION_ZSTD) * HAVE_ZSTD |
260
                (1U << COMPRESSION_GZIP) * HAVE_ZLIB |
261
                (1U << COMPRESSION_BZIP2) * HAVE_BZIP2;
262

263
        assert(c >= 0);
4,039✔
264
        assert(c < _COMPRESSION_MAX);
4,039✔
265

266
        return BIT_SET(supported, c);
4,039✔
267
}
268

269
Compression compression_detect_from_magic(const uint8_t data[static COMPRESSION_MAGIC_BYTES_MAX]) {
335✔
270
        /* Magic signatures per RFC 1952 (gzip), tukaani.org/xz/xz-file-format.txt (xz),
271
         * RFC 8878 (zstd), lz4/doc/lz4_Frame_format.md (lz4), and the bzip2 file format.
272
         * Make sure to update COMPRESSION_MAGIC_BYTES_MAX if needed when adding a new magic. */
273
        if (memcmp(data, (const uint8_t[]) { 0x1f, 0x8b }, 2) == 0)
335✔
274
                return COMPRESSION_GZIP;
89✔
275
        if (memcmp(data, (const uint8_t[]) { 0xfd, '7', 'z', 'X', 'Z', 0x00 }, 6) == 0)
246✔
276
                return COMPRESSION_XZ;
1✔
277
        if (memcmp(data, (const uint8_t[]) { 0x28, 0xb5, 0x2f, 0xfd }, 4) == 0)
245✔
278
                return COMPRESSION_ZSTD;
2✔
279
        if (memcmp(data, (const uint8_t[]) { 0x04, 0x22, 0x4d, 0x18 }, 4) == 0)
243✔
280
                return COMPRESSION_LZ4;
1✔
281
        if (memcmp(data, (const uint8_t[]) { 'B', 'Z', 'h' }, 3) == 0)
242✔
282
                return COMPRESSION_BZIP2;
1✔
283

284
        return _COMPRESSION_INVALID;
241✔
285
}
286

287
int dlopen_xz(void) {
2,995✔
288
#if HAVE_XZ
289
        SD_ELF_NOTE_DLOPEN(
2,995✔
290
                        "lzma",
291
                        "Support lzma compression in journal and coredump files",
292
                        COMPRESSION_PRIORITY_XZ,
293
                        "liblzma.so.5");
294

295
        return dlopen_many_sym_or_warn(
2,995✔
296
                        &lzma_dl,
297
                        "liblzma.so.5", LOG_DEBUG,
298
                        DLSYM_ARG(lzma_code),
299
                        DLSYM_ARG(lzma_easy_encoder),
300
                        DLSYM_ARG(lzma_end),
301
                        DLSYM_ARG(lzma_stream_buffer_encode),
302
                        DLSYM_ARG(lzma_lzma_preset),
303
                        DLSYM_ARG(lzma_stream_decoder));
304
#else
305
        return -EOPNOTSUPP;
306
#endif
307
}
308

309
int dlopen_lz4(void) {
3,270✔
310
#if HAVE_LZ4
311
        SD_ELF_NOTE_DLOPEN(
3,270✔
312
                        "lz4",
313
                        "Support lz4 compression in journal and coredump files",
314
                        COMPRESSION_PRIORITY_LZ4,
315
                        "liblz4.so.1");
316

317
        return dlopen_many_sym_or_warn(
3,270✔
318
                        &lz4_dl,
319
                        "liblz4.so.1", LOG_DEBUG,
320
                        DLSYM_ARG(LZ4F_compressBegin),
321
                        DLSYM_ARG(LZ4F_compressBound),
322
                        DLSYM_ARG(LZ4F_compressEnd),
323
                        DLSYM_ARG(LZ4F_compressUpdate),
324
                        DLSYM_ARG(LZ4F_createCompressionContext),
325
                        DLSYM_ARG(LZ4F_createDecompressionContext),
326
                        DLSYM_ARG(LZ4F_decompress),
327
                        DLSYM_ARG(LZ4F_freeCompressionContext),
328
                        DLSYM_ARG(LZ4F_freeDecompressionContext),
329
                        DLSYM_ARG(LZ4F_isError),
330
                        DLSYM_ARG(LZ4_compress_default),
331
                        DLSYM_ARG(LZ4_compress_HC),
332
                        DLSYM_ARG(LZ4_decompress_safe),
333
                        DLSYM_ARG(LZ4_decompress_safe_partial),
334
                        DLSYM_ARG(LZ4_versionNumber));
335
#else
336
        return -EOPNOTSUPP;
337
#endif
338
}
339

340
int dlopen_zstd(void) {
5,210,994✔
341
#if HAVE_ZSTD
342
        SD_ELF_NOTE_DLOPEN(
5,210,994✔
343
                        "zstd",
344
                        "Support zstd compression in journal and coredump files",
345
                        COMPRESSION_PRIORITY_ZSTD,
346
                        "libzstd.so.1");
347

348
        return dlopen_many_sym_or_warn(
5,210,994✔
349
                        &zstd_dl,
350
                        "libzstd.so.1", LOG_DEBUG,
351
                        DLSYM_ARG(ZSTD_getErrorCode),
352
                        DLSYM_ARG(ZSTD_compress),
353
                        DLSYM_ARG(ZSTD_getFrameContentSize),
354
                        DLSYM_ARG(ZSTD_decompressStream),
355
                        DLSYM_ARG(ZSTD_getErrorName),
356
                        DLSYM_ARG(ZSTD_DStreamOutSize),
357
                        DLSYM_ARG(ZSTD_CStreamInSize),
358
                        DLSYM_ARG(ZSTD_CStreamOutSize),
359
                        DLSYM_ARG(ZSTD_CCtx_setParameter),
360
                        DLSYM_ARG(ZSTD_compressStream2),
361
                        DLSYM_ARG(ZSTD_DStreamInSize),
362
                        DLSYM_ARG(ZSTD_freeCCtx),
363
                        DLSYM_ARG(ZSTD_freeDCtx),
364
                        DLSYM_ARG(ZSTD_isError),
365
                        DLSYM_ARG(ZSTD_createDCtx),
366
                        DLSYM_ARG(ZSTD_createCCtx));
367
#else
368
        return -EOPNOTSUPP;
369
#endif
370
}
371

372
int dlopen_zlib(void) {
403✔
373
#if HAVE_ZLIB
374
        SD_ELF_NOTE_DLOPEN(
403✔
375
                        "zlib",
376
                        "Support gzip compression and decompression",
377
                        SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
378
                        "libz.so.1");
379

380
        return dlopen_many_sym_or_warn(
403✔
381
                        &zlib_dl,
382
                        "libz.so.1", LOG_DEBUG,
383
                        DLSYM_ARG(deflateInit2_),
384
                        DLSYM_ARG(deflate),
385
                        DLSYM_ARG(deflateEnd),
386
                        DLSYM_ARG(inflateInit2_),
387
                        DLSYM_ARG(inflate),
388
                        DLSYM_ARG(inflateEnd));
389
#else
390
        return -EOPNOTSUPP;
391
#endif
392
}
393

394
int dlopen_bzip2(void) {
298✔
395
#if HAVE_BZIP2
396
        SD_ELF_NOTE_DLOPEN(
298✔
397
                        "bzip2",
398
                        "Support bzip2 compression and decompression",
399
                        SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
400
                        "libbz2.so.1");
401

402
        return dlopen_many_sym_or_warn(
298✔
403
                        &bzip2_dl,
404
                        "libbz2.so.1", LOG_DEBUG,
405
                        DLSYM_ARG(BZ2_bzCompressInit),
406
                        DLSYM_ARG(BZ2_bzCompress),
407
                        DLSYM_ARG(BZ2_bzCompressEnd),
408
                        DLSYM_ARG(BZ2_bzDecompressInit),
409
                        DLSYM_ARG(BZ2_bzDecompress),
410
                        DLSYM_ARG(BZ2_bzDecompressEnd));
411
#else
412
        return -EOPNOTSUPP;
413
#endif
414
}
415

416
static int compress_blob_xz(
25✔
417
                const void *src,
418
                uint64_t src_size,
419
                void *dst,
420
                size_t dst_alloc_size,
421
                size_t *dst_size,
422
                int level) {
423

424
        assert(src);
25✔
425
        assert(src_size > 0);
25✔
426
        assert(dst);
25✔
427
        assert(dst_alloc_size > 0);
25✔
428
        assert(dst_size);
25✔
429

430
#if HAVE_XZ
431
        lzma_options_lzma opt = {
25✔
432
                1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
433
                LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4
434
        };
435
        lzma_filter filters[] = {
25✔
436
                { LZMA_FILTER_LZMA2, &opt },
437
                { LZMA_VLI_UNKNOWN, NULL }
438
        };
439
        lzma_ret ret;
25✔
440
        size_t out_pos = 0;
25✔
441
        int r;
25✔
442

443
        r = dlopen_xz();
25✔
444
        if (r < 0)
25✔
445
                return r;
25✔
446

447
        if (level >= 0) {
25✔
448
                r = sym_lzma_lzma_preset(&opt, (uint32_t) level);
4✔
449
                if (r < 0)
450
                        return r;
451
        }
452

453
        /* Returns < 0 if we couldn't compress the data or the
454
         * compressed result is longer than the original */
455

456
        if (src_size < 80)
25✔
457
                return -ENOBUFS;
458

459
        ret = sym_lzma_stream_buffer_encode(filters, LZMA_CHECK_NONE, NULL,
25✔
460
                                        src, src_size, dst, &out_pos, dst_alloc_size);
461
        if (ret != LZMA_OK)
25✔
462
                return -ENOBUFS;
463

464
        *dst_size = out_pos;
22✔
465
        return 0;
22✔
466
#else
467
        return -EPROTONOSUPPORT;
468
#endif
469
}
470

471
static int compress_blob_lz4(
173✔
472
                const void *src,
473
                uint64_t src_size,
474
                void *dst,
475
                size_t dst_alloc_size,
476
                size_t *dst_size,
477
                int level) {
478

479
        assert(src);
173✔
480
        assert(src_size > 0);
173✔
481
        assert(dst);
173✔
482
        assert(dst_alloc_size > 0);
173✔
483
        assert(dst_size);
173✔
484

485
#if HAVE_LZ4
486
        int r;
173✔
487

488
        r = dlopen_lz4();
173✔
489
        if (r < 0)
173✔
490
                return r;
491
        /* Returns < 0 if we couldn't compress the data or the
492
         * compressed result is longer than the original */
493

494
        if (src_size < 9)
173✔
495
                return -ENOBUFS;
496

497
        if (src_size > INT_MAX)
173✔
498
                return -EFBIG;
499
        if (dst_alloc_size > INT_MAX)
173✔
500
                dst_alloc_size = INT_MAX;
×
501

502
        if (level <= 0)
173✔
503
                r = sym_LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
170✔
504
        else
505
                r = sym_LZ4_compress_HC(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8, level);
3✔
506
        if (r <= 0)
173✔
507
                return -ENOBUFS;
508

509
        unaligned_write_le64(dst, src_size);
166✔
510
        *dst_size = r + 8;
166✔
511

512
        return 0;
166✔
513
#else
514
        return -EPROTONOSUPPORT;
515
#endif
516
}
517

518
static int compress_blob_zstd(
760✔
519
                const void *src,
520
                uint64_t src_size,
521
                void *dst,
522
                size_t dst_alloc_size,
523
                size_t *dst_size,
524
                int level) {
525

526
        assert(src);
760✔
527
        assert(src_size > 0);
760✔
528
        assert(dst);
760✔
529
        assert(dst_alloc_size > 0);
760✔
530
        assert(dst_size);
760✔
531

532
#if HAVE_ZSTD
533
        size_t k;
760✔
534
        int r;
760✔
535

536
        r = dlopen_zstd();
760✔
537
        if (r < 0)
760✔
538
                return r;
539

540
        k = sym_ZSTD_compress(dst, dst_alloc_size, src, src_size, level < 0 ? 0 : level);
760✔
541
        if (sym_ZSTD_isError(k))
760✔
542
                return zstd_ret_to_errno(k);
4✔
543

544
        *dst_size = k;
756✔
545
        return 0;
756✔
546
#else
547
        return -EPROTONOSUPPORT;
548
#endif
549
}
550

551
static int compress_blob_gzip(const void *src, uint64_t src_size,
15✔
552
                       void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
553

554
        assert(src);
15✔
555
        assert(src_size > 0);
15✔
556
        assert(dst);
15✔
557
        assert(dst_alloc_size > 0);
15✔
558
        assert(dst_size);
15✔
559

560
#if HAVE_ZLIB
561
        int r;
15✔
562

563
        r = dlopen_zlib();
15✔
564
        if (r < 0)
15✔
565
                return r;
15✔
566

567
        if (src_size > UINT_MAX)
15✔
568
                return -EFBIG;
569
        if (dst_alloc_size > UINT_MAX)
15✔
570
                dst_alloc_size = UINT_MAX;
×
571

572
        _cleanup_(deflateEnd_wrapper) z_stream s = {};
15✔
573

574
        r = sym_deflateInit2_(&s, level < 0 ? Z_DEFAULT_COMPRESSION : level,
15✔
575
                              /* method= */ Z_DEFLATED,
576
                              /* windowBits= */ ZLIB_WBITS_GZIP,
577
                              /* memLevel= */ 8,
578
                              /* strategy= */ Z_DEFAULT_STRATEGY,
579
                              ZLIB_VERSION, (int) sizeof(s));
580
        if (r != Z_OK)
15✔
581
                return -ENOMEM;
582

583
        s.next_in = (void*) src;
15✔
584
        s.avail_in = src_size;
15✔
585
        s.next_out = dst;
15✔
586
        s.avail_out = dst_alloc_size;
15✔
587

588
        r = sym_deflate(&s, Z_FINISH);
15✔
589
        if (r != Z_STREAM_END)
15✔
590
                return -ENOBUFS;
591

592
        *dst_size = dst_alloc_size - s.avail_out;
12✔
593
        return 0;
12✔
594
#else
595
        return -EPROTONOSUPPORT;
596
#endif
597
}
598

599
static int compress_blob_bzip2(
12✔
600
                const void *src, uint64_t src_size,
601
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
602

603
        assert(src);
12✔
604
        assert(src_size > 0);
12✔
605
        assert(dst);
12✔
606
        assert(dst_alloc_size > 0);
12✔
607
        assert(dst_size);
12✔
608

609
#if HAVE_BZIP2
610
        int r;
12✔
611

612
        r = dlopen_bzip2();
12✔
613
        if (r < 0)
12✔
614
                return r;
12✔
615

616
        if (src_size > UINT_MAX)
12✔
617
                return -EFBIG;
618
        if (dst_alloc_size > UINT_MAX)
12✔
619
                dst_alloc_size = UINT_MAX;
×
620

621
        _cleanup_(BZ2_bzCompressEnd_wrapper) bz_stream s = {};
12✔
622

623
        r = sym_BZ2_bzCompressInit(&s, level < 0 ? 9 : level, /* verbosity= */ 0, /* workFactor= */ 0);
24✔
624
        if (r != BZ_OK)
12✔
625
                return -ENOMEM;
626

627
        s.next_in = (char*) src;
12✔
628
        s.avail_in = src_size;
12✔
629
        s.next_out = (char*) dst;
12✔
630
        s.avail_out = dst_alloc_size;
12✔
631

632
        r = sym_BZ2_bzCompress(&s, BZ_FINISH);
12✔
633

634
        if (r != BZ_STREAM_END)
12✔
635
                return -ENOBUFS;
636

637
        *dst_size = dst_alloc_size - s.avail_out;
10✔
638
        return 0;
10✔
639
#else
640
        return -EPROTONOSUPPORT;
641
#endif
642
}
643

644
int compress_blob(
985✔
645
                Compression compression,
646
                const void *src, uint64_t src_size,
647
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
648

649
        switch (compression) {
985✔
650
        case COMPRESSION_XZ:
25✔
651
                return compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size, level);
25✔
652
        case COMPRESSION_LZ4:
173✔
653
                return compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size, level);
173✔
654
        case COMPRESSION_ZSTD:
760✔
655
                return compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size, level);
760✔
656
        case COMPRESSION_GZIP:
15✔
657
                return compress_blob_gzip(src, src_size, dst, dst_alloc_size, dst_size, level);
15✔
658
        case COMPRESSION_BZIP2:
12✔
659
                return compress_blob_bzip2(src, src_size, dst, dst_alloc_size, dst_size, level);
12✔
660
        default:
661
                return -EOPNOTSUPP;
662
        }
663
}
664

665
static int decompress_blob_xz(
2,687✔
666
                const void *src,
667
                uint64_t src_size,
668
                void **dst,
669
                size_t *dst_size,
670
                size_t dst_max) {
671

672
        assert(src);
2,687✔
673
        assert(src_size > 0);
2,687✔
674
        assert(dst);
2,687✔
675
        assert(dst_size);
2,687✔
676

677
#if HAVE_XZ
678
        int r;
2,687✔
679

680
        r = dlopen_xz();
2,687✔
681
        if (r < 0)
2,687✔
682
                return r;
2,687✔
683

684
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
2,687✔
685
        lzma_ret ret = sym_lzma_stream_decoder(&s, UINT64_MAX, /* flags= */ 0);
2,687✔
686
        if (ret != LZMA_OK)
2,687✔
687
                return -ENOMEM;
688

689
        size_t space = MIN(src_size * 2, dst_max ?: SIZE_MAX);
2,687✔
690
        if (!greedy_realloc(dst, space, 1))
2,687✔
691
                return -ENOMEM;
692

693
        s.next_in = src;
2,687✔
694
        s.avail_in = src_size;
2,687✔
695

696
        s.next_out = *dst;
2,687✔
697
        s.avail_out = space;
2,687✔
698

699
        for (;;) {
2,931✔
700
                size_t used;
2,809✔
701

702
                ret = sym_lzma_code(&s, LZMA_FINISH);
2,809✔
703
                if (ret == LZMA_STREAM_END)
2,809✔
704
                        break;
705
                if (ret != LZMA_OK)
124✔
706
                        return -ENOMEM;
707

708
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
122✔
709
                        break;
710
                if (dst_max > 0 && space == dst_max)
122✔
711
                        return -ENOBUFS;
712

713
                used = space - s.avail_out;
122✔
714
                /* Silence static analyzers, space is bounded by allocation size */
715
                assert(space <= SIZE_MAX / 2);
122✔
716
                space = MIN(2 * space, dst_max ?: SIZE_MAX);
122✔
717
                if (!greedy_realloc(dst, space, 1))
122✔
718
                        return -ENOMEM;
719

720
                s.avail_out = space - used;
122✔
721
                s.next_out = *(uint8_t**)dst + used;
122✔
722
        }
723

724
        *dst_size = space - s.avail_out;
2,685✔
725
        return 0;
2,685✔
726
#else
727
        return -EPROTONOSUPPORT;
728
#endif
729
}
730

731
static int decompress_blob_lz4(
2,814✔
732
                const void *src,
733
                uint64_t src_size,
734
                void **dst,
735
                size_t *dst_size,
736
                size_t dst_max) {
737

738
        assert(src);
2,814✔
739
        assert(src_size > 0);
2,814✔
740
        assert(dst);
2,814✔
741
        assert(dst_size);
2,814✔
742

743
#if HAVE_LZ4
744
        char* out;
2,814✔
745
        int r, size; /* LZ4 uses int for size */
2,814✔
746

747
        r = dlopen_lz4();
2,814✔
748
        if (r < 0)
2,814✔
749
                return r;
750

751
        if (src_size <= 8)
2,814✔
752
                return -EBADMSG;
753

754
        if (src_size - 8 > INT_MAX)
2,812✔
755
                return -EFBIG;
756

757
        size = unaligned_read_le64(src);
2,812✔
758
        if (size < 0 || (unsigned) size != unaligned_read_le64(src))
2,812✔
759
                return -EFBIG;
760
        if (dst_max > 0 && (size_t) size > dst_max)
2,812✔
761
                return -ENOBUFS;
762
        out = greedy_realloc(dst, size, 1);
2,812✔
763
        if (!out)
2,812✔
764
                return -ENOMEM;
765

766
        r = sym_LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
2,812✔
767
        if (r < 0 || r != size)
2,812✔
768
                return -EBADMSG;
769

770
        *dst_size = size;
2,812✔
771
        return 0;
2,812✔
772
#else
773
        return -EPROTONOSUPPORT;
774
#endif
775
}
776

777
static int decompress_blob_zstd(
258,677✔
778
                const void *src,
779
                uint64_t src_size,
780
                void **dst,
781
                size_t *dst_size,
782
                size_t dst_max) {
783

784
        assert(src);
258,677✔
785
        assert(src_size > 0);
258,677✔
786
        assert(dst);
258,677✔
787
        assert(dst_size);
258,677✔
788

789
#if HAVE_ZSTD
790
        uint64_t size;
258,677✔
791
        int r;
258,677✔
792

793
        r = dlopen_zstd();
258,677✔
794
        if (r < 0)
258,677✔
795
                return r;
258,677✔
796

797
        size = sym_ZSTD_getFrameContentSize(src, src_size);
258,677✔
798
        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
258,677✔
799
                return -EBADMSG;
800

801
        if (dst_max > 0 && size > dst_max)
258,675✔
802
                size = dst_max;
×
803
        if (size > SIZE_MAX)
258,675✔
804
                return -E2BIG;
805

806
        if (!(greedy_realloc(dst, MAX(sym_ZSTD_DStreamOutSize(), size), 1)))
258,675✔
807
                return -ENOMEM;
808

809
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
517,350✔
810
        if (!dctx)
258,675✔
811
                return -ENOMEM;
812

813
        ZSTD_inBuffer input = {
258,675✔
814
                .src = src,
815
                .size = src_size,
816
        };
817
        ZSTD_outBuffer output = {
517,350✔
818
                .dst = *dst,
258,675✔
819
                .size = MALLOC_SIZEOF_SAFE(*dst),
258,675✔
820
        };
821

822
        size_t k = sym_ZSTD_decompressStream(dctx, &output, &input);
258,675✔
823
        if (sym_ZSTD_isError(k))
258,675✔
824
                return log_debug_errno(zstd_ret_to_errno(k), "ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
825
        if (output.pos < size)
258,675✔
826
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoded less data than indicated, probably corrupted stream.");
6✔
827

828
        *dst_size = size;
258,669✔
829
        return 0;
258,669✔
830
#else
831
        return -EPROTONOSUPPORT;
832
#endif
833
}
834

835
static int decompress_blob_gzip(
10✔
836
                const void *src,
837
                uint64_t src_size,
838
                void **dst,
839
                size_t *dst_size,
840
                size_t dst_max) {
841

842
        assert(src);
10✔
843
        assert(src_size > 0);
10✔
844
        assert(dst);
10✔
845
        assert(dst_size);
10✔
846

847
#if HAVE_ZLIB
848
        int r;
10✔
849

850
        r = dlopen_zlib();
10✔
851
        if (r < 0)
10✔
852
                return r;
10✔
853

854
        if (src_size > UINT_MAX)
10✔
855
                return -EFBIG;
856

857
        _cleanup_(inflateEnd_wrapper) z_stream s = {};
10✔
858

859
        r = sym_inflateInit2_(&s, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(s));
10✔
860
        if (r != Z_OK)
10✔
861
                return -ENOMEM;
862

863
        size_t space = MIN3(src_size * 2, dst_max ?: SIZE_MAX, (size_t) UINT_MAX);
10✔
864
        if (!greedy_realloc(dst, space, 1))
10✔
865
                return -ENOMEM;
866

867
        s.next_in = (void*) src;
10✔
868
        s.avail_in = src_size;
10✔
869
        s.next_out = *dst;
10✔
870
        s.avail_out = space;
10✔
871

872
        for (;;) {
102✔
873
                size_t used;
56✔
874

875
                r = sym_inflate(&s, Z_NO_FLUSH);
56✔
876
                if (r == Z_STREAM_END)
56✔
877
                        break;
878
                if (!IN_SET(r, Z_OK, Z_BUF_ERROR))
48✔
879
                        return -EBADMSG;
880

881
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
46✔
882
                        break;
883
                if (dst_max > 0 && space == dst_max)
46✔
884
                        return -ENOBUFS;
885

886
                used = space - s.avail_out;
46✔
887
                space = MIN3(2 * space, dst_max ?: SIZE_MAX, UINT_MAX);
46✔
888
                if (!greedy_realloc(dst, space, 1))
46✔
889
                        return -ENOMEM;
890

891
                s.avail_out = space - used;
46✔
892
                s.next_out = *(uint8_t**)dst + used;
46✔
893
        }
894

895
        *dst_size = space - s.avail_out;
8✔
896
        return 0;
8✔
897
#else
898
        return -EPROTONOSUPPORT;
899
#endif
900
}
901

902
static int decompress_blob_bzip2(
7✔
903
                const void *src,
904
                uint64_t src_size,
905
                void **dst,
906
                size_t *dst_size,
907
                size_t dst_max) {
908

909
        assert(src);
7✔
910
        assert(src_size > 0);
7✔
911
        assert(dst);
7✔
912
        assert(dst_size);
7✔
913

914
#if HAVE_BZIP2
915
        int r;
7✔
916

917
        r = dlopen_bzip2();
7✔
918
        if (r < 0)
7✔
919
                return r;
7✔
920

921
        if (src_size > UINT_MAX)
7✔
922
                return -EFBIG;
923

924
        _cleanup_(BZ2_bzDecompressEnd_wrapper) bz_stream s = {};
7✔
925

926
        r = sym_BZ2_bzDecompressInit(&s, /* verbosity= */ 0, /* small= */ 0);
7✔
927
        if (r != BZ_OK)
7✔
928
                return -ENOMEM;
929

930
        size_t space = MIN3(src_size * 2, dst_max ?: SIZE_MAX, (size_t) UINT_MAX);
7✔
931
        if (!greedy_realloc(dst, space, 1))
7✔
932
                return -ENOMEM;
933

934
        s.next_in = (char*) src;
7✔
935
        s.avail_in = src_size;
7✔
936
        s.next_out = (char*) *dst;
7✔
937
        s.avail_out = space;
7✔
938

939
        for (;;) {
85✔
940
                size_t used;
46✔
941

942
                r = sym_BZ2_bzDecompress(&s);
46✔
943
                if (r == BZ_STREAM_END)
46✔
944
                        break;
945
                if (r != BZ_OK)
41✔
946
                        return -EBADMSG;
947

948
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
39✔
949
                        break;
950
                if (dst_max > 0 && space == dst_max)
39✔
951
                        return -ENOBUFS;
952

953
                used = space - s.avail_out;
39✔
954
                space = MIN3(2 * space, dst_max ?: SIZE_MAX, (size_t) UINT_MAX);
39✔
955
                if (!greedy_realloc(dst, space, 1))
39✔
956
                        return -ENOMEM;
957

958
                s.avail_out = space - used;
39✔
959
                s.next_out = (char*) *dst + used;
39✔
960
        }
961

962
        *dst_size = space - s.avail_out;
5✔
963
        return 0;
5✔
964
#else
965
        return -EPROTONOSUPPORT;
966
#endif
967
}
968

969
int decompress_blob(
264,195✔
970
                Compression compression,
971
                const void *src,
972
                uint64_t src_size,
973
                void **dst,
974
                size_t *dst_size,
975
                size_t dst_max) {
976

977
        switch (compression) {
264,195✔
978
        case COMPRESSION_XZ:
2,687✔
979
                return decompress_blob_xz(
2,687✔
980
                                src, src_size,
981
                                dst, dst_size, dst_max);
982
        case COMPRESSION_LZ4:
2,814✔
983
                return decompress_blob_lz4(
2,814✔
984
                                src, src_size,
985
                                dst, dst_size, dst_max);
986
        case COMPRESSION_ZSTD:
258,677✔
987
                return decompress_blob_zstd(
258,677✔
988
                                src, src_size,
989
                                dst, dst_size, dst_max);
990
        case COMPRESSION_GZIP:
10✔
991
                return decompress_blob_gzip(
10✔
992
                                src, src_size,
993
                                dst, dst_size, dst_max);
994
        case COMPRESSION_BZIP2:
7✔
995
                return decompress_blob_bzip2(
7✔
996
                                src, src_size,
997
                                dst, dst_size, dst_max);
998
        default:
999
                return -EPROTONOSUPPORT;
1000
        }
1001
}
1002

1003
int decompress_zlib_raw(
×
1004
                const void *src,
1005
                uint64_t src_size,
1006
                void *dst,
1007
                size_t dst_size,
1008
                int wbits) {
1009

1010
#if HAVE_ZLIB
1011
        int r;
×
1012

1013
        r = dlopen_zlib();
×
1014
        if (r < 0)
×
1015
                return r;
×
1016

1017
        if (src_size > UINT_MAX)
×
1018
                return -EFBIG;
1019
        if (dst_size > UINT_MAX)
×
1020
                return -EFBIG;
1021

1022
        _cleanup_(inflateEnd_wrapper) z_stream s = {
×
1023
                .next_in = (void*) src,
1024
                .avail_in = src_size,
1025
                .next_out = dst,
1026
                .avail_out = dst_size,
1027
        };
1028

1029
        r = sym_inflateInit2_(&s, /* windowBits= */ wbits, ZLIB_VERSION, (int) sizeof(s));
×
1030
        if (r != Z_OK)
×
1031
                return -EIO;
1032

1033
        r = sym_inflate(&s, Z_FINISH);
×
1034
        size_t produced = (uint8_t*) s.next_out - (uint8_t*) dst;
×
1035

1036
        if (r != Z_STREAM_END || produced != dst_size)
×
1037
                return -EBADMSG;
×
1038

1039
        return 0;
1040
#else
1041
        return -EPROTONOSUPPORT;
1042
#endif
1043
}
1044

1045
static int decompress_startswith_xz(
270✔
1046
                const void *src,
1047
                uint64_t src_size,
1048
                void **buffer,
1049
                const void *prefix,
1050
                size_t prefix_len,
1051
                uint8_t extra) {
1052

1053
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
1054
         * follow the prefix */
1055

1056
        assert(src);
270✔
1057
        assert(src_size > 0);
270✔
1058
        assert(buffer);
270✔
1059
        assert(prefix);
270✔
1060

1061
#if HAVE_XZ
1062
        int r;
270✔
1063

1064
        r = dlopen_xz();
270✔
1065
        if (r < 0)
270✔
1066
                return r;
270✔
1067

1068
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
270✔
1069
        lzma_ret ret = sym_lzma_stream_decoder(&s, UINT64_MAX, /* flags= */ 0);
270✔
1070
        if (ret != LZMA_OK)
270✔
1071
                return -EBADMSG;
1072

1073
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
1074
                return -ENOMEM;
1075

1076
        size_t allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
1077

1078
        s.next_in = src;
270✔
1079
        s.avail_in = src_size;
270✔
1080

1081
        s.next_out = *buffer;
270✔
1082
        s.avail_out = allocated;
270✔
1083

1084
        for (;;) {
270✔
1085
                ret = sym_lzma_code(&s, LZMA_FINISH);
270✔
1086

1087
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
270✔
1088
                        return -EBADMSG;
1089

1090
                if (allocated - s.avail_out >= prefix_len + 1)
270✔
1091
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
540✔
1092
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
270✔
1093

1094
                if (ret == LZMA_STREAM_END)
×
1095
                        return 0;
1096

1097
                s.avail_out += allocated;
×
1098

1099
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
1100
                        return -ENOMEM;
1101

1102
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
1103
                s.next_out = *(uint8_t**)buffer + allocated - s.avail_out;
×
1104
        }
1105

1106
#else
1107
        return -EPROTONOSUPPORT;
1108
#endif
1109
}
1110

1111
static int decompress_startswith_lz4(
270✔
1112
                const void *src,
1113
                uint64_t src_size,
1114
                void **buffer,
1115
                const void *prefix,
1116
                size_t prefix_len,
1117
                uint8_t extra) {
1118

1119
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
1120
         * follow the prefix */
1121

1122
        assert(src);
270✔
1123
        assert(src_size > 0);
270✔
1124
        assert(buffer);
270✔
1125
        assert(prefix);
270✔
1126

1127
#if HAVE_LZ4
1128
        size_t allocated;
270✔
1129
        int r;
270✔
1130

1131
        r = dlopen_lz4();
270✔
1132
        if (r < 0)
270✔
1133
                return r;
1134

1135
        if (src_size <= 8)
270✔
1136
                return -EBADMSG;
1137

1138
        if (src_size - 8 > INT_MAX)
270✔
1139
                return -EFBIG;
1140

1141
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
1142
                return -ENOMEM;
1143
        allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
1144

1145
        r = sym_LZ4_decompress_safe_partial(
540✔
1146
                        (char*)src + 8,
1147
                        *buffer,
1148
                        src_size - 8,
270✔
1149
                        prefix_len + 1,
270✔
1150
                        allocated);
1151

1152
        /* One lz4 < 1.8.3, we might get "failure" (r < 0), or "success" where just a part of the buffer is
1153
         * decompressed. But if we get a smaller amount of bytes than requested, we don't know whether there
1154
         * isn't enough data to fill the requested size or whether we just got a partial answer.
1155
         */
1156
        if (r < 0 || (size_t) r < prefix_len + 1) {
270✔
1157
                size_t size;
×
1158

1159
                if (sym_LZ4_versionNumber() >= 10803)
×
1160
                        /* We trust that the newer lz4 decompresses the number of bytes we
1161
                         * requested if available in the compressed string. */
1162
                        return 0;
×
1163

1164
                if (r > 0)
×
1165
                        /* Compare what we have first, in case of mismatch we can
1166
                         * shortcut the full comparison. */
1167
                        if (memcmp(*buffer, prefix, r) != 0)
×
1168
                                return 0;
1169

1170
                /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
1171
                 * so in pathological cases might need to decompress the full field. */
1172
                r = decompress_blob_lz4(src, src_size, buffer, &size, 0);
×
1173
                if (r < 0)
×
1174
                        return r;
1175

1176
                if (size < prefix_len + 1)
×
1177
                        return 0;
1178
        }
1179

1180
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
270✔
1181
                ((const uint8_t*) *buffer)[prefix_len] == extra;
270✔
1182
#else
1183
        return -EPROTONOSUPPORT;
1184
#endif
1185
}
1186

1187
static int decompress_startswith_zstd(
4,951,508✔
1188
                const void *src,
1189
                uint64_t src_size,
1190
                void **buffer,
1191
                const void *prefix,
1192
                size_t prefix_len,
1193
                uint8_t extra) {
1194

1195
        assert(src);
4,951,508✔
1196
        assert(src_size > 0);
4,951,508✔
1197
        assert(buffer);
4,951,508✔
1198
        assert(prefix);
4,951,508✔
1199

1200
#if HAVE_ZSTD
1201
        int r;
4,951,508✔
1202

1203
        r = dlopen_zstd();
4,951,508✔
1204
        if (r < 0)
4,951,508✔
1205
                return r;
4,951,508✔
1206

1207
        uint64_t size = sym_ZSTD_getFrameContentSize(src, src_size);
4,951,508✔
1208
        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
4,951,508✔
1209
                return -EBADMSG;
1210

1211
        if (size < prefix_len + 1)
4,951,508✔
1212
                return 0; /* Decompressed text too short to match the prefix and extra */
1213

1214
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
9,903,016✔
1215
        if (!dctx)
4,951,508✔
1216
                return -ENOMEM;
1217

1218
        if (!(greedy_realloc(buffer, MAX(sym_ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
4,951,508✔
1219
                return -ENOMEM;
1220

1221
        ZSTD_inBuffer input = {
4,951,508✔
1222
                .src = src,
1223
                .size = src_size,
1224
        };
1225
        ZSTD_outBuffer output = {
9,903,016✔
1226
                .dst = *buffer,
4,951,508✔
1227
                .size = MALLOC_SIZEOF_SAFE(*buffer),
4,951,508✔
1228
        };
1229
        size_t k;
4,951,508✔
1230

1231
        k = sym_ZSTD_decompressStream(dctx, &output, &input);
4,951,508✔
1232
        if (sym_ZSTD_isError(k))
4,951,508✔
1233
                return log_debug_errno(zstd_ret_to_errno(k), "ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
1234
        if (output.pos < prefix_len + 1)
4,951,508✔
1235
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoded less data than indicated, probably corrupted stream.");
2✔
1236

1237
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
4,951,506✔
1238
                ((const uint8_t*) *buffer)[prefix_len] == extra;
21,495✔
1239
#else
1240
        return -EPROTONOSUPPORT;
1241
#endif
1242
}
1243

1244
static int decompress_startswith_gzip(
266✔
1245
                const void *src,
1246
                uint64_t src_size,
1247
                void **buffer,
1248
                const void *prefix,
1249
                size_t prefix_len,
1250
                uint8_t extra) {
1251

1252
        assert(src);
266✔
1253
        assert(src_size > 0);
266✔
1254
        assert(buffer);
266✔
1255
        assert(prefix);
266✔
1256

1257
#if HAVE_ZLIB
1258
        int r;
266✔
1259

1260
        r = dlopen_zlib();
266✔
1261
        if (r < 0)
266✔
1262
                return r;
266✔
1263

1264
        if (src_size > UINT_MAX)
266✔
1265
                return -EFBIG;
1266

1267
        _cleanup_(inflateEnd_wrapper) z_stream s = {};
266✔
1268

1269
        r = sym_inflateInit2_(&s, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(s));
266✔
1270
        if (r != Z_OK)
266✔
1271
                return -EBADMSG;
1272

1273
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
266✔
1274
                return -ENOMEM;
1275

1276
        size_t allocated = MALLOC_SIZEOF_SAFE(*buffer);
266✔
1277

1278
        s.next_in = (void*) src;
266✔
1279
        s.avail_in = src_size;
266✔
1280

1281
        s.next_out = *buffer;
266✔
1282
        s.avail_out = MIN(allocated, (size_t) UINT_MAX);
266✔
1283

1284
        for (;;) {
266✔
1285
                r = sym_inflate(&s, Z_FINISH);
266✔
1286

1287
                if (!IN_SET(r, Z_OK, Z_STREAM_END, Z_BUF_ERROR))
266✔
1288
                        return -EBADMSG;
1289

1290
                if (allocated - s.avail_out >= prefix_len + 1)
266✔
1291
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
532✔
1292
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
266✔
1293

1294
                if (r == Z_STREAM_END)
×
1295
                        return 0;
1296

1297
                size_t used = allocated - s.avail_out;
×
1298

1299
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
1300
                        return -ENOMEM;
1301

1302
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
1303
                s.avail_out = MIN(allocated - used, (size_t) UINT_MAX);
×
1304
                s.next_out = *(uint8_t**)buffer + used;
×
1305
        }
1306
#else
1307
        return -EPROTONOSUPPORT;
1308
#endif
1309
}
1310

1311
static int decompress_startswith_bzip2(
266✔
1312
                const void *src,
1313
                uint64_t src_size,
1314
                void **buffer,
1315
                const void *prefix,
1316
                size_t prefix_len,
1317
                uint8_t extra) {
1318

1319
        assert(src);
266✔
1320
        assert(src_size > 0);
266✔
1321
        assert(buffer);
266✔
1322
        assert(prefix);
266✔
1323

1324
#if HAVE_BZIP2
1325
        int r;
266✔
1326

1327
        r = dlopen_bzip2();
266✔
1328
        if (r < 0)
266✔
1329
                return r;
266✔
1330

1331
        if (src_size > UINT_MAX)
266✔
1332
                return -EFBIG;
1333

1334
        _cleanup_(BZ2_bzDecompressEnd_wrapper) bz_stream s = {};
266✔
1335

1336
        r = sym_BZ2_bzDecompressInit(&s, /* verbosity= */ 0, /* small= */ 0);
266✔
1337
        if (r != BZ_OK)
266✔
1338
                return -EBADMSG;
1339

1340
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
266✔
1341
                return -ENOMEM;
1342

1343
        size_t allocated = MALLOC_SIZEOF_SAFE(*buffer);
266✔
1344

1345
        s.next_in = (char*) src;
266✔
1346
        s.avail_in = src_size;
266✔
1347

1348
        s.next_out = *buffer;
266✔
1349
        s.avail_out = MIN(allocated, (size_t) UINT_MAX);
266✔
1350

1351
        for (;;) {
266✔
1352
                r = sym_BZ2_bzDecompress(&s);
266✔
1353

1354
                if (!IN_SET(r, BZ_OK, BZ_STREAM_END))
266✔
1355
                        return -EBADMSG;
1356

1357
                if (allocated - s.avail_out >= prefix_len + 1)
266✔
1358
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
532✔
1359
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
266✔
1360

1361
                if (r == BZ_STREAM_END)
×
1362
                        return 0;
1363

1364
                size_t used = allocated - s.avail_out;
×
1365

1366
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
1367
                        return -ENOMEM;
1368

1369
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
1370
                s.avail_out = MIN(allocated - used, (size_t) UINT_MAX);
×
1371
                s.next_out = (char*) *buffer + used;
×
1372
        }
1373
#else
1374
        return -EPROTONOSUPPORT;
1375
#endif
1376
}
1377

1378
int decompress_startswith(
4,952,580✔
1379
                Compression compression,
1380
                const void *src, uint64_t src_size,
1381
                void **buffer,
1382
                const void *prefix, size_t prefix_len,
1383
                uint8_t extra) {
1384

1385
        switch (compression) {
4,952,580✔
1386
        case COMPRESSION_XZ:
270✔
1387
                return decompress_startswith_xz(src, src_size, buffer, prefix, prefix_len, extra);
270✔
1388
        case COMPRESSION_LZ4:
270✔
1389
                return decompress_startswith_lz4(src, src_size, buffer, prefix, prefix_len, extra);
270✔
1390
        case COMPRESSION_ZSTD:
4,951,508✔
1391
                return decompress_startswith_zstd(src, src_size, buffer, prefix, prefix_len, extra);
4,951,508✔
1392
        case COMPRESSION_GZIP:
266✔
1393
                return decompress_startswith_gzip(src, src_size, buffer, prefix, prefix_len, extra);
266✔
1394
        case COMPRESSION_BZIP2:
266✔
1395
                return decompress_startswith_bzip2(src, src_size, buffer, prefix, prefix_len, extra);
266✔
1396
        default:
1397
                return -EOPNOTSUPP;
1398
        }
1399
}
1400

1401
int compress_stream(
46✔
1402
                Compression type,
1403
                int fdf, int fdt,
1404
                uint64_t max_bytes,
1405
                uint64_t *ret_uncompressed_size) {
1406

1407
        _cleanup_(compressor_freep) Compressor *c = NULL;
×
1408
        _cleanup_free_ void *buf = NULL;
×
1409
        _cleanup_free_ uint8_t *input = NULL;
46✔
1410
        size_t buf_size = 0, buf_alloc = 0;
46✔
1411
        uint64_t total_in = 0, total_out = 0;
46✔
1412
        int r;
46✔
1413

1414
        assert(fdf >= 0);
46✔
1415
        assert(fdt >= 0);
46✔
1416

1417
        r = compressor_new(&c, type);
46✔
1418
        if (r < 0)
46✔
1419
                return r;
1420

1421
        input = new(uint8_t, COMPRESS_PIPE_BUFFER_SIZE);
46✔
1422
        if (!input)
46✔
1423
                return -ENOMEM;
1424

1425
        for (;;) {
633✔
1426
                size_t m = COMPRESS_PIPE_BUFFER_SIZE;
633✔
1427
                ssize_t n;
633✔
1428

1429
                if (max_bytes != UINT64_MAX && (uint64_t) m > max_bytes)
633✔
1430
                        m = (size_t) max_bytes;
×
1431

1432
                n = read(fdf, input, m);
633✔
1433
                if (n < 0)
633✔
1434
                        return -errno;
×
1435

1436
                if (n == 0) {
633✔
1437
                        r = compressor_finish(c, &buf, &buf_size, &buf_alloc);
46✔
1438
                        if (r < 0)
46✔
1439
                                return r;
1440

1441
                        if (buf_size > 0) {
46✔
1442
                                r = loop_write(fdt, buf, buf_size);
46✔
1443
                                if (r < 0)
46✔
1444
                                        return r;
1445
                                total_out += buf_size;
46✔
1446
                        }
1447
                        break;
46✔
1448
                }
1449

1450
                total_in += n;
587✔
1451
                if (max_bytes != UINT64_MAX) {
587✔
1452
                        assert(max_bytes >= (uint64_t) n);
560✔
1453
                        max_bytes -= n;
560✔
1454
                }
1455

1456
                r = compressor_start(c, input, n, &buf, &buf_size, &buf_alloc);
587✔
1457
                if (r < 0)
587✔
1458
                        return r;
1459

1460
                if (buf_size > 0) {
587✔
1461
                        r = loop_write(fdt, buf, buf_size);
551✔
1462
                        if (r < 0)
551✔
1463
                                return r;
1464
                        total_out += buf_size;
551✔
1465
                }
1466
        }
1467

1468
        if (ret_uncompressed_size)
46✔
1469
                *ret_uncompressed_size = total_in;
44✔
1470

1471
        if (total_in == 0)
46✔
1472
                log_debug("%s compression finished (no input data)", compression_to_string(type));
×
1473
        else
1474
                log_debug("%s compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
46✔
1475
                          compression_to_string(type), total_in, total_out, (double) total_out / total_in * 100);
1476

1477
        return 0;
1478
}
1479

1480
/* Determine whether sparse writes should be used for this fd. Sparse writes are only safe on
1481
 * regular files without O_APPEND (O_APPEND ignores lseek position, which would collapse holes). */
1482
static int should_sparse(int fd) {
41✔
1483
        struct stat st;
41✔
1484

1485
        assert(fd >= 0);
41✔
1486

1487
        if (fstat(fd, &st) < 0)
41✔
1488
                return -errno;
×
1489

1490
        int flags = fcntl(fd, F_GETFL);
41✔
1491
        if (flags < 0)
41✔
1492
                return -errno;
×
1493

1494
        return S_ISREG(st.st_mode) && !FLAGS_SET(flags, O_APPEND);
41✔
1495
}
1496

1497
/* After sparse decompression, set the file size to the current position to account for
1498
 * trailing holes that sparse_write() created via lseek but never extended the file size for. */
1499
static int finalize_sparse(int fd) {
30✔
1500
        off_t pos;
30✔
1501

1502
        assert(fd >= 0);
30✔
1503

1504
        pos = lseek(fd, 0, SEEK_CUR);
30✔
1505
        if (pos < 0)
30✔
1506
                return -errno;
×
1507

1508
        if (ftruncate(fd, pos) < 0)
30✔
1509
                return -errno;
×
1510

1511
        return 0;
1512
}
1513

1514
/* Common helper for decompress_stream_*() wrappers */
1515

1516
struct decompress_stream_userdata {
1517
        int fd;
1518
        uint64_t max_bytes;
1519
        uint64_t total_out;
1520
        bool sparse;
1521
};
1522

1523
static int decompress_stream_write_callback(const void *data, size_t size, void *userdata) {
275✔
1524
        struct decompress_stream_userdata *u = ASSERT_PTR(userdata);
275✔
1525

1526
        if (u->max_bytes != UINT64_MAX) {
275✔
1527
                if (u->max_bytes < size)
30✔
1528
                        return -EFBIG;
1529
                u->max_bytes -= size;
25✔
1530
        }
1531

1532
        u->total_out += size;
270✔
1533

1534
        if (u->sparse) {
270✔
1535
                /* Note: sparse_write() does not retry on EINTR and converts short writes to -EIO.
1536
                 * This is fine here since sparse mode is only used on regular files, where short
1537
                 * writes and EINTR are not expected in practice. */
1538
                ssize_t k = sparse_write(u->fd, data, size, 64);
269✔
1539
                if (k < 0)
269✔
1540
                        return (int) k;
×
1541
                return 0;
1542
        }
1543

1544
        return loop_write(u->fd, data, size);
1✔
1545
}
1546

1547
static int decompressor_new(Decompressor **ret, Compression type) {
41✔
1548
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1549
        int r;
41✔
1550
#endif
1551

1552
        assert(ret);
41✔
1553

1554
        _cleanup_(compressor_freep) Decompressor *c = new0(Decompressor, 1);
41✔
1555
        if (!c)
41✔
1556
                return -ENOMEM;
1557

1558
        c->type = _COMPRESSION_INVALID;
41✔
1559

1560
        switch (type) {
41✔
1561

1562
#if HAVE_XZ
1563
        case COMPRESSION_XZ:
6✔
1564
                r = dlopen_xz();
6✔
1565
                if (r < 0)
6✔
1566
                        return r;
1567

1568
                if (sym_lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED) != LZMA_OK)
6✔
1569
                        return -EIO;
1570
                break;
1571
#endif
1572

1573
#if HAVE_LZ4
1574
        case COMPRESSION_LZ4: {
6✔
1575
                r = dlopen_lz4();
6✔
1576
                if (r < 0)
6✔
1577
                        return r;
1578

1579
                size_t rc = sym_LZ4F_createDecompressionContext(&c->d_lz4, LZ4F_VERSION);
6✔
1580
                if (sym_LZ4F_isError(rc))
6✔
1581
                        return -ENOMEM;
1582

1583
                break;
1584
        }
1585
#endif
1586

1587
#if HAVE_ZSTD
1588
        case COMPRESSION_ZSTD:
16✔
1589
                r = dlopen_zstd();
16✔
1590
                if (r < 0)
16✔
1591
                        return r;
1592

1593
                c->d_zstd = sym_ZSTD_createDCtx();
16✔
1594
                if (!c->d_zstd)
16✔
1595
                        return -ENOMEM;
1596
                break;
1597
#endif
1598

1599
#if HAVE_ZLIB
1600
        case COMPRESSION_GZIP:
7✔
1601
                r = dlopen_zlib();
7✔
1602
                if (r < 0)
7✔
1603
                        return r;
1604

1605
                r = sym_inflateInit2_(&c->gzip, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(c->gzip));
7✔
1606
                if (r != Z_OK)
7✔
1607
                        return -EIO;
1608
                break;
1609
#endif
1610

1611
#if HAVE_BZIP2
1612
        case COMPRESSION_BZIP2:
6✔
1613
                r = dlopen_bzip2();
6✔
1614
                if (r < 0)
6✔
1615
                        return r;
1616

1617
                r = sym_BZ2_bzDecompressInit(&c->bzip2, /* verbosity= */ 0, /* small= */ 0);
6✔
1618
                if (r != BZ_OK)
6✔
1619
                        return -EIO;
1620
                break;
1621
#endif
1622

1623
        default:
1624
                return -EOPNOTSUPP;
1625
        }
1626

1627
        c->type = type;
41✔
1628
        c->encoding = false;
41✔
1629
        *ret = TAKE_PTR(c);
41✔
1630
        return 0;
41✔
1631
}
1632

1633
int decompress_stream(
41✔
1634
                Compression type,
1635
                int fdf, int fdt,
1636
                uint64_t max_bytes) {
1637

1638
        _cleanup_(compressor_freep) Decompressor *c = NULL;
×
1639
        _cleanup_free_ uint8_t *buf = NULL;
41✔
1640
        uint64_t total_in = 0;
41✔
1641
        int r;
41✔
1642

1643
        assert(fdf >= 0);
41✔
1644
        assert(fdt >= 0);
41✔
1645

1646
        r = decompressor_new(&c, type);
41✔
1647
        if (r < 0)
41✔
1648
                return r;
1649

1650
        struct decompress_stream_userdata userdata = {
82✔
1651
                .fd = fdt,
1652
                .max_bytes = max_bytes,
1653
                .sparse = should_sparse(fdt) > 0,
41✔
1654
        };
1655

1656
        buf = new(uint8_t, COMPRESS_PIPE_BUFFER_SIZE);
41✔
1657
        if (!buf)
41✔
1658
                return -ENOMEM;
1659

1660
        for (;;) {
89✔
1661
                ssize_t n;
89✔
1662

1663
                n = read(fdf, buf, COMPRESS_PIPE_BUFFER_SIZE);
89✔
1664
                if (n < 0)
89✔
1665
                        return -errno;
×
1666
                if (n == 0)
89✔
1667
                        break;
1668

1669
                total_in += n;
59✔
1670

1671
                r = decompressor_push(c, buf, n, decompress_stream_write_callback, &userdata);
59✔
1672
                if (r < 0)
59✔
1673
                        return r;
1674
        }
1675

1676
        if (total_in == 0)
30✔
1677
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%s decompression failed: no data read",
×
1678
                                       compression_to_string(type));
1679

1680
        if (userdata.sparse) {
30✔
1681
                r = finalize_sparse(fdt);
30✔
1682
                if (r < 0)
30✔
1683
                        return r;
1684
        }
1685

1686
        log_debug("%s decompression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
30✔
1687
                  compression_to_string(type), total_in, userdata.total_out,
1688
                  (double) userdata.total_out / total_in * 100);
1689

1690
        return 0;
1691
}
1692

1693
int decompress_stream_by_filename(const char *filename, int fdf, int fdt, uint64_t max_bytes) {
10✔
1694
        Compression c = compression_from_filename(filename);
10✔
1695
        if (c == COMPRESSION_NONE)
10✔
1696
                return -EPROTONOSUPPORT;
1697

1698
        return decompress_stream(c, fdf, fdt, max_bytes);
10✔
1699
}
1700

1701
/* Push-based streaming compression/decompression context API */
1702

1703
Compressor* compressor_free(Compressor *c) {
523✔
1704
        if (!c)
523✔
1705
                return NULL;
1706

1707
        switch (c->type) {
477✔
1708

1709
#if HAVE_XZ
1710
        case COMPRESSION_XZ:
12✔
1711
                sym_lzma_end(&c->xz);
12✔
1712
                break;
12✔
1713
#endif
1714

1715
#if HAVE_LZ4
1716
        case COMPRESSION_LZ4:
12✔
1717
                if (c->encoding) {
12✔
1718
                        sym_LZ4F_freeCompressionContext(c->c_lz4);
5✔
1719
                        c->c_lz4 = NULL;
5✔
1720
                        c->lz4_header = mfree(c->lz4_header);
5✔
1721
                } else {
1722
                        sym_LZ4F_freeDecompressionContext(c->d_lz4);
7✔
1723
                        c->d_lz4 = NULL;
7✔
1724
                }
1725
                break;
1726
#endif
1727

1728
#if HAVE_ZSTD
1729
        case COMPRESSION_ZSTD:
48✔
1730
                if (c->encoding) {
48✔
1731
                        sym_ZSTD_freeCCtx(c->c_zstd);
30✔
1732
                        c->c_zstd = NULL;
30✔
1733
                } else {
1734
                        sym_ZSTD_freeDCtx(c->d_zstd);
18✔
1735
                        c->d_zstd = NULL;
18✔
1736
                }
1737
                break;
1738
#endif
1739

1740
#if HAVE_ZLIB
1741
        case COMPRESSION_GZIP:
109✔
1742
                if (c->encoding)
109✔
1743
                        sym_deflateEnd(&c->gzip);
14✔
1744
                else
1745
                        sym_inflateEnd(&c->gzip);
95✔
1746
                break;
1747
#endif
1748

1749
#if HAVE_BZIP2
1750
        case COMPRESSION_BZIP2:
12✔
1751
                if (c->encoding)
12✔
1752
                        sym_BZ2_bzCompressEnd(&c->bzip2);
5✔
1753
                else
1754
                        sym_BZ2_bzDecompressEnd(&c->bzip2);
7✔
1755
                break;
1756
#endif
1757

1758
        default:
1759
                break;
1760
        }
1761

1762
        return mfree(c);
477✔
1763
}
1764

1765
Compression compressor_type(const Compressor *c) {
15,229✔
1766
        return c ? c->type : _COMPRESSION_INVALID;
15,229✔
1767
}
1768

1769
int decompressor_detect(Decompressor **ret, const void *data, size_t size) {
370✔
1770
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1771
        int r;
370✔
1772
#endif
1773

1774
        assert(ret);
370✔
1775

1776
        if (*ret)
370✔
1777
                return 1;
370✔
1778

1779
        if (size < COMPRESSION_MAGIC_BYTES_MAX)
370✔
1780
                return 0;
1781

1782
        assert(data);
333✔
1783

1784
        Compression type = compression_detect_from_magic(data);
333✔
1785

1786
        _cleanup_(compressor_freep) Decompressor *c = new0(Decompressor, 1);
333✔
1787
        if (!c)
333✔
1788
                return -ENOMEM;
1789

1790
        switch (type) {
333✔
1791

1792
#if HAVE_XZ
1793
        case COMPRESSION_XZ: {
1✔
1794
                r = dlopen_xz();
1✔
1795
                if (r < 0)
1✔
1796
                        return r;
1797

1798
                lzma_ret xzr = sym_lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED);
1✔
1799
                if (xzr != LZMA_OK)
1✔
1800
                        return -EIO;
1801

1802
                break;
1803
        }
1804
#endif
1805

1806
#if HAVE_LZ4
1807
        case COMPRESSION_LZ4: {
1✔
1808
                r = dlopen_lz4();
1✔
1809
                if (r < 0)
1✔
1810
                        return r;
1811

1812
                size_t rc = sym_LZ4F_createDecompressionContext(&c->d_lz4, LZ4F_VERSION);
1✔
1813
                if (sym_LZ4F_isError(rc))
1✔
1814
                        return -ENOMEM;
1815

1816
                break;
1817
        }
1818
#endif
1819

1820
#if HAVE_ZSTD
1821
        case COMPRESSION_ZSTD: {
2✔
1822
                r = dlopen_zstd();
2✔
1823
                if (r < 0)
2✔
1824
                        return r;
1825

1826
                c->d_zstd = sym_ZSTD_createDCtx();
2✔
1827
                if (!c->d_zstd)
2✔
1828
                        return -ENOMEM;
1829

1830
                break;
1831
        }
1832
#endif
1833

1834
#if HAVE_ZLIB
1835
        case COMPRESSION_GZIP: {
88✔
1836
                r = dlopen_zlib();
88✔
1837
                if (r < 0)
88✔
1838
                        return r;
1839

1840
                r = sym_inflateInit2_(&c->gzip, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(c->gzip));
88✔
1841
                if (r != Z_OK)
88✔
1842
                        return -EIO;
1843

1844
                break;
1845
        }
1846
#endif
1847

1848
#if HAVE_BZIP2
1849
        case COMPRESSION_BZIP2: {
1✔
1850
                r = dlopen_bzip2();
1✔
1851
                if (r < 0)
1✔
1852
                        return r;
1853

1854
                r = sym_BZ2_bzDecompressInit(&c->bzip2, /* verbosity= */ 0, /* small= */ 0);
1✔
1855
                if (r != BZ_OK)
1✔
1856
                        return -EIO;
1857

1858
                break;
1859
        }
1860
#endif
1861

1862
        default:
240✔
1863
                if (type != _COMPRESSION_INVALID)
240✔
1864
                        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
1865
                                               "Detected %s compression, but support is not compiled in.",
1866
                                               compression_to_string(type));
1867
                type = COMPRESSION_NONE;
1868
                break;
1869
        }
1870

1871
        c->type = type;
333✔
1872
        c->encoding = false;
333✔
1873

1874
        log_debug("Detected compression type: %s", compression_to_string(c->type));
333✔
1875
        *ret = TAKE_PTR(c);
333✔
1876
        return 1;
333✔
1877
}
1878

1879
int decompressor_force_off(Decompressor **ret) {
37✔
1880
        assert(ret);
37✔
1881

1882
        *ret = compressor_free(*ret);
37✔
1883

1884
        Decompressor *c = new0(Decompressor, 1);
37✔
1885
        if (!c)
37✔
1886
                return -ENOMEM;
1887

1888
        c->type = COMPRESSION_NONE;
37✔
1889
        c->encoding = false;
37✔
1890
        *ret = c;
37✔
1891
        return 0;
37✔
1892
}
1893

1894
int decompressor_push(Decompressor *c, const void *data, size_t size, DecompressorCallback callback, void *userdata) {
24,468✔
1895
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1896
        _cleanup_free_ uint8_t *buffer = NULL;
24,468✔
1897
#endif
1898
        int r;
24,468✔
1899

1900
        assert(c);
24,468✔
1901
        assert(callback);
24,468✔
1902

1903
        if (c->encoding)
24,468✔
1904
                return -EINVAL;
1905

1906
        if (size == 0)
24,468✔
1907
                return 1;
1908

1909
        assert(data);
24,266✔
1910

1911
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1912
        if (c->type != COMPRESSION_NONE) {
24,266✔
1913
                buffer = new(uint8_t, COMPRESS_PIPE_BUFFER_SIZE);
15,846✔
1914
                if (!buffer)
15,846✔
1915
                        return -ENOMEM;
1916
        }
1917
#endif
1918

1919
        switch (c->type) {
24,266✔
1920

1921
        case COMPRESSION_NONE:
8,420✔
1922
                r = callback(data, size, userdata);
8,420✔
1923
                if (r < 0)
8,420✔
1924
                        return r;
×
1925

1926
                break;
1927

1928
#if HAVE_XZ
1929
        case COMPRESSION_XZ:
7✔
1930
                c->xz.next_in = data;
7✔
1931
                c->xz.avail_in = size;
7✔
1932

1933
                while (c->xz.avail_in > 0) {
20✔
1934
                        c->xz.next_out = buffer;
8✔
1935
                        c->xz.avail_out = COMPRESS_PIPE_BUFFER_SIZE;
8✔
1936

1937
                        lzma_ret lzr = sym_lzma_code(&c->xz, LZMA_RUN);
8✔
1938
                        if (!IN_SET(lzr, LZMA_OK, LZMA_STREAM_END))
8✔
1939
                                return -EBADMSG;
1940

1941
                        if (c->xz.avail_out < COMPRESS_PIPE_BUFFER_SIZE) {
7✔
1942
                                r = callback(buffer, COMPRESS_PIPE_BUFFER_SIZE - c->xz.avail_out, userdata);
7✔
1943
                                if (r < 0)
7✔
1944
                                        return r;
1945
                        }
1946
                }
1947

1948
                break;
1949
#endif
1950

1951
#if HAVE_LZ4
1952
        case COMPRESSION_LZ4: {
1953
                const uint8_t *src = data;
1954
                size_t src_remaining = size;
1955

1956
                while (src_remaining > 0) {
13✔
1957
                        size_t produced = COMPRESS_PIPE_BUFFER_SIZE;
8✔
1958
                        size_t consumed = src_remaining;
8✔
1959

1960
                        size_t rc = sym_LZ4F_decompress(c->d_lz4, buffer, &produced, src, &consumed, NULL);
8✔
1961
                        if (sym_LZ4F_isError(rc))
8✔
1962
                                return -EBADMSG;
2✔
1963

1964
                        if (consumed == 0 && produced == 0)
7✔
1965
                                break; /* No progress possible with current input */
1966

1967
                        src += consumed;
7✔
1968
                        src_remaining -= consumed;
7✔
1969

1970
                        if (produced > 0) {
7✔
1971
                                r = callback(buffer, produced, userdata);
7✔
1972
                                if (r < 0)
7✔
1973
                                        return r;
1974
                        }
1975
                }
1976

1977
                break;
1978
        }
1979
#endif
1980

1981
#if HAVE_ZSTD
1982
        case COMPRESSION_ZSTD: {
36✔
1983
                ZSTD_inBuffer input = {
36✔
1984
                        .src =  (void*) data,
1985
                        .size = size,
1986
                };
1987

1988
                while (input.pos < input.size) {
286✔
1989
                        ZSTD_outBuffer output = {
253✔
1990
                                .dst = buffer,
1991
                                .size = COMPRESS_PIPE_BUFFER_SIZE,
1992
                        };
1993

1994
                        size_t res = sym_ZSTD_decompressStream(c->d_zstd, &output, &input);
253✔
1995
                        if (sym_ZSTD_isError(res))
253✔
1996
                                return -EBADMSG;
3✔
1997

1998
                        if (output.pos > 0) {
252✔
1999
                                r = callback(output.dst, output.pos, userdata);
252✔
2000
                                if (r < 0)
252✔
2001
                                        return r;
2002
                        }
2003
                }
2004

2005
                break;
33✔
2006
        }
2007
#endif
2008

2009
#if HAVE_ZLIB
2010
        case COMPRESSION_GZIP:
15,789✔
2011
                if (size > UINT_MAX)
15,789✔
2012
                        return -EFBIG;
2013

2014
                c->gzip.next_in = (void*) data;
15,789✔
2015
                c->gzip.avail_in = size;
15,789✔
2016

2017
                while (c->gzip.avail_in > 0) {
35,248✔
2018
                        c->gzip.next_out = buffer;
19,554✔
2019
                        c->gzip.avail_out = COMPRESS_PIPE_BUFFER_SIZE;
19,554✔
2020

2021
                        int zr = sym_inflate(&c->gzip, Z_NO_FLUSH);
19,554✔
2022
                        if (!IN_SET(zr, Z_OK, Z_STREAM_END))
19,554✔
2023
                                return -EBADMSG;
2024

2025
                        if (c->gzip.avail_out < COMPRESS_PIPE_BUFFER_SIZE) {
19,553✔
2026
                                r = callback(buffer, COMPRESS_PIPE_BUFFER_SIZE - c->gzip.avail_out, userdata);
19,553✔
2027
                                if (r < 0)
19,553✔
2028
                                        return r;
2029
                        }
2030

2031
                        if (zr == Z_STREAM_END)
19,552✔
2032
                                break;
2033
                }
2034

2035
                break;
2036
#endif
2037

2038
#if HAVE_BZIP2
2039
        case COMPRESSION_BZIP2:
7✔
2040
                if (size > UINT_MAX)
7✔
2041
                        return -EFBIG;
2042

2043
                c->bzip2.next_in = (char*) data;
7✔
2044
                c->bzip2.avail_in = size;
7✔
2045

2046
                while (c->bzip2.avail_in > 0) {
8✔
2047
                        c->bzip2.next_out = (char*) buffer;
8✔
2048
                        c->bzip2.avail_out = COMPRESS_PIPE_BUFFER_SIZE;
8✔
2049

2050
                        int bzr = sym_BZ2_bzDecompress(&c->bzip2);
8✔
2051
                        if (!IN_SET(bzr, BZ_OK, BZ_STREAM_END))
8✔
2052
                                return -EBADMSG;
2053

2054
                        if (c->bzip2.avail_out < COMPRESS_PIPE_BUFFER_SIZE) {
7✔
2055
                                r = callback(buffer, COMPRESS_PIPE_BUFFER_SIZE - c->bzip2.avail_out, userdata);
7✔
2056
                                if (r < 0)
7✔
2057
                                        return r;
2058
                        }
2059

2060
                        if (bzr == BZ_STREAM_END)
6✔
2061
                                break;
2062
                }
2063

2064
                break;
2065
#endif
2066

2067
        default:
×
2068
                assert_not_reached();
×
2069
        }
2070

2071
        return 1;
2072
}
2073

2074
int compressor_new(Compressor **ret, Compression type) {
66✔
2075
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2076
        int r;
66✔
2077
#endif
2078

2079
        assert(ret);
66✔
2080

2081
        _cleanup_(compressor_freep) Compressor *c = new0(Compressor, 1);
66✔
2082
        if (!c)
66✔
2083
                return -ENOMEM;
2084

2085
        c->type = _COMPRESSION_INVALID;
66✔
2086
        /* Set encoding early so that compressor_freep calls the correct cleanup (compression vs
2087
         * decompression) if any operation in the switch fails after setting c->type. This is safe
2088
         * because _COMPRESSION_INVALID hits the default: break case regardless of the encoding flag. */
2089
        c->encoding = true;
66✔
2090

2091
        switch (type) {
66✔
2092

2093
#if HAVE_XZ
2094
        case COMPRESSION_XZ: {
5✔
2095
                r = dlopen_xz();
5✔
2096
                if (r < 0)
5✔
2097
                        return r;
2098

2099
                lzma_ret xzr = sym_lzma_easy_encoder(&c->xz, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
5✔
2100
                if (xzr != LZMA_OK)
5✔
2101
                        return -EIO;
2102

2103
                c->type = COMPRESSION_XZ;
5✔
2104
                break;
5✔
2105
        }
2106
#endif
2107

2108
#if HAVE_LZ4
2109
        case COMPRESSION_LZ4: {
5✔
2110
                r = dlopen_lz4();
5✔
2111
                if (r < 0)
5✔
2112
                        return r;
2113

2114
                size_t rc = sym_LZ4F_createCompressionContext(&c->c_lz4, LZ4F_VERSION);
5✔
2115
                if (sym_LZ4F_isError(rc))
5✔
2116
                        return -ENOMEM;
2117

2118
                c->type = COMPRESSION_LZ4;
5✔
2119

2120
                /* Generate the frame header and stash it for the first compressor_start call */
2121
                size_t header_bound = sym_LZ4F_compressBound(0, &lz4_preferences);
5✔
2122
                c->lz4_header = malloc(header_bound);
5✔
2123
                if (!c->lz4_header)
5✔
2124
                        return -ENOMEM;
2125

2126
                c->lz4_header_size = sym_LZ4F_compressBegin(c->c_lz4, c->lz4_header, header_bound, &lz4_preferences);
5✔
2127
                if (sym_LZ4F_isError(c->lz4_header_size))
5✔
2128
                        return -EINVAL;
2129

2130
                break;
2131
        }
2132
#endif
2133

2134
#if HAVE_ZSTD
2135
        case COMPRESSION_ZSTD:
30✔
2136
                r = dlopen_zstd();
30✔
2137
                if (r < 0)
30✔
2138
                        return r;
2139

2140
                c->c_zstd = sym_ZSTD_createCCtx();
30✔
2141
                if (!c->c_zstd)
30✔
2142
                        return -ENOMEM;
2143

2144
                c->type = COMPRESSION_ZSTD;
30✔
2145

2146
                size_t z = sym_ZSTD_CCtx_setParameter(c->c_zstd, ZSTD_c_compressionLevel, ZSTD_CLEVEL_DEFAULT);
30✔
2147
                if (sym_ZSTD_isError(z))
30✔
2148
                        return -EIO;
2149

2150
                z = sym_ZSTD_CCtx_setParameter(c->c_zstd, ZSTD_c_checksumFlag, /* enable= */ 1);
30✔
2151
                if (sym_ZSTD_isError(z))
30✔
2152
                        log_debug("Failed to enable ZSTD checksum, ignoring: %s", sym_ZSTD_getErrorName(z));
×
2153

2154
                break;
2155
#endif
2156

2157
#if HAVE_ZLIB
2158
        case COMPRESSION_GZIP:
14✔
2159
                r = dlopen_zlib();
14✔
2160
                if (r < 0)
14✔
2161
                        return r;
2162

2163
                r = sym_deflateInit2_(&c->gzip,
14✔
2164
                                      Z_DEFAULT_COMPRESSION,
2165
                                      /* method= */ Z_DEFLATED,
2166
                                      /* windowBits= */ ZLIB_WBITS_GZIP,
2167
                                      /* memLevel= */ 8,
2168
                                      /* strategy= */ Z_DEFAULT_STRATEGY,
2169
                                      ZLIB_VERSION, (int) sizeof(c->gzip));
2170
                if (r != Z_OK)
14✔
2171
                        return -EIO;
2172

2173
                c->type = COMPRESSION_GZIP;
14✔
2174
                break;
14✔
2175
#endif
2176

2177
#if HAVE_BZIP2
2178
        case COMPRESSION_BZIP2:
5✔
2179
                r = dlopen_bzip2();
5✔
2180
                if (r < 0)
5✔
2181
                        return r;
2182

2183
                r = sym_BZ2_bzCompressInit(&c->bzip2, /* blockSize100k= */ 9, /* verbosity= */ 0, /* workFactor= */ 0);
5✔
2184
                if (r != BZ_OK)
5✔
2185
                        return -EIO;
2186

2187
                c->type = COMPRESSION_BZIP2;
5✔
2188
                break;
5✔
2189
#endif
2190

2191
        case COMPRESSION_NONE:
7✔
2192
                c->type = COMPRESSION_NONE;
7✔
2193
                break;
7✔
2194

2195
        default:
2196
                return -EOPNOTSUPP;
2197
        }
2198

2199
        *ret = TAKE_PTR(c);
66✔
2200
        return 0;
66✔
2201
}
2202

2203
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2204
static int enlarge_buffer(void **buffer, size_t *buffer_size, size_t *buffer_allocated, size_t need) {
4,991✔
2205
        assert(buffer);
4,991✔
2206
        assert(buffer_size);
4,991✔
2207
        assert(buffer_allocated);
4,991✔
2208

2209
        need = MAX3(need, *buffer_size + 1, (size_t) COMPRESS_PIPE_BUFFER_SIZE);
4,991✔
2210
        if (*buffer_allocated >= need)
4,991✔
2211
                return 0;
2212

2213
        if (!greedy_realloc(buffer, need, 1))
69✔
2214
                return -ENOMEM;
2215

2216
        *buffer_allocated = MALLOC_SIZEOF_SAFE(*buffer);
69✔
2217
        return 1;
69✔
2218
}
2219
#endif
2220

2221
int compressor_start(
4,927✔
2222
                Compressor *c,
2223
                const void *data,
2224
                size_t size,
2225
                void **buffer,
2226
                size_t *buffer_size,
2227
                size_t *buffer_allocated) {
2228

2229
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2230
        int r;
4,927✔
2231
#endif
2232

2233
        assert(c);
4,927✔
2234
        assert(buffer);
4,927✔
2235
        assert(buffer_size);
4,927✔
2236
        assert(buffer_allocated);
4,927✔
2237

2238
        if (!c->encoding)
4,927✔
2239
                return -EINVAL;
2240

2241
        if (size == 0)
4,927✔
2242
                return 0;
2243

2244
        assert(data);
4,927✔
2245

2246
        *buffer_size = 0;
4,927✔
2247

2248
        switch (c->type) {
4,927✔
2249

2250
#if HAVE_XZ
2251
        case COMPRESSION_XZ:
6✔
2252

2253
                c->xz.next_in = data;
6✔
2254
                c->xz.avail_in = size;
6✔
2255

2256
                while (c->xz.avail_in > 0) {
12✔
2257
                        lzma_ret lzr;
6✔
2258

2259
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
6✔
2260
                        if (r < 0)
6✔
2261
                                return r;
2262

2263
                        c->xz.next_out = (uint8_t*) *buffer + *buffer_size;
6✔
2264
                        c->xz.avail_out = *buffer_allocated - *buffer_size;
6✔
2265

2266
                        lzr = sym_lzma_code(&c->xz, LZMA_RUN);
6✔
2267
                        if (lzr != LZMA_OK)
6✔
2268
                                return -EIO;
2269

2270
                        *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out;
6✔
2271
                }
2272

2273
                break;
2274
#endif
2275

2276
#if HAVE_LZ4
2277
        case COMPRESSION_LZ4: {
6✔
2278
                /* Prepend any stashed frame header from compressor_new */
2279
                if (c->lz4_header_size > 0) {
6✔
2280
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, c->lz4_header_size);
5✔
2281
                        if (r < 0)
5✔
2282
                                return r;
2283

2284
                        memcpy(*buffer, c->lz4_header, c->lz4_header_size);
5✔
2285
                        *buffer_size = c->lz4_header_size;
5✔
2286
                        c->lz4_header = mfree(c->lz4_header);
5✔
2287
                        c->lz4_header_size = 0;
5✔
2288
                }
2289

2290
                size_t bound = sym_LZ4F_compressBound(size, &lz4_preferences);
6✔
2291
                r = enlarge_buffer(buffer, buffer_size, buffer_allocated, *buffer_size + bound);
6✔
2292
                if (r < 0)
6✔
2293
                        return r;
2294

2295
                size_t n = sym_LZ4F_compressUpdate(c->c_lz4,
12✔
2296
                                                   (uint8_t*) *buffer + *buffer_size,
6✔
2297
                                                   *buffer_allocated - *buffer_size,
6✔
2298
                                                   data, size, NULL);
2299
                if (sym_LZ4F_isError(n))
6✔
2300
                        return -EIO;
2301

2302
                *buffer_size += n;
6✔
2303
                break;
6✔
2304
        }
2305
#endif
2306

2307
#if HAVE_ZSTD
2308
        case COMPRESSION_ZSTD: {
567✔
2309
                ZSTD_inBuffer input = {
567✔
2310
                        .src = data,
2311
                        .size = size,
2312
                };
2313

2314
                while (input.pos < input.size) {
1,134✔
2315
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
567✔
2316
                        if (r < 0)
567✔
2317
                                return r;
×
2318

2319
                        ZSTD_outBuffer output = {
567✔
2320
                                .dst = ((uint8_t *) *buffer + *buffer_size),
567✔
2321
                                .size = *buffer_allocated - *buffer_size,
567✔
2322
                        };
2323

2324
                        size_t res = sym_ZSTD_compressStream2(c->c_zstd, &output, &input, ZSTD_e_continue);
567✔
2325
                        if (sym_ZSTD_isError(res))
567✔
2326
                                return -EIO;
2327

2328
                        *buffer_size += output.pos;
567✔
2329
                }
2330

2331
                break;
567✔
2332
        }
2333
#endif
2334

2335
#if HAVE_ZLIB
2336
        case COMPRESSION_GZIP:
4,342✔
2337
                if (size > UINT_MAX)
4,342✔
2338
                        return -EFBIG;
2339

2340
                c->gzip.next_in = (void*) data;
4,342✔
2341
                c->gzip.avail_in = size;
4,342✔
2342

2343
                while (c->gzip.avail_in > 0) {
8,684✔
2344
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
4,342✔
2345
                        if (r < 0)
4,342✔
2346
                                return r;
2347

2348
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
4,342✔
2349
                        c->gzip.next_out = (uint8_t*) *buffer + *buffer_size;
4,342✔
2350
                        c->gzip.avail_out = avail;
4,342✔
2351

2352
                        r = sym_deflate(&c->gzip, Z_NO_FLUSH);
4,342✔
2353
                        if (r != Z_OK)
4,342✔
2354
                                return -EIO;
2355

2356
                        *buffer_size += avail - c->gzip.avail_out;
4,342✔
2357
                }
2358

2359
                break;
2360
#endif
2361

2362
#if HAVE_BZIP2
2363
        case COMPRESSION_BZIP2:
6✔
2364
                if (size > UINT_MAX)
6✔
2365
                        return -EFBIG;
2366

2367
                c->bzip2.next_in = (void*) data;
6✔
2368
                c->bzip2.avail_in = size;
6✔
2369

2370
                while (c->bzip2.avail_in > 0) {
12✔
2371
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
6✔
2372
                        if (r < 0)
6✔
2373
                                return r;
2374

2375
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
6✔
2376
                        c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size);
6✔
2377
                        c->bzip2.avail_out = avail;
6✔
2378

2379
                        r = sym_BZ2_bzCompress(&c->bzip2, BZ_RUN);
6✔
2380
                        if (r != BZ_RUN_OK)
6✔
2381
                                return -EIO;
2382

2383
                        *buffer_size += avail - c->bzip2.avail_out;
6✔
2384
                }
2385

2386
                break;
2387
#endif
2388

2389
        case COMPRESSION_NONE:
×
2390

2391
                if (*buffer_allocated < size) {
×
2392
                        void *p;
×
2393

2394
                        p = realloc(*buffer, size);
×
2395
                        if (!p)
×
2396
                                return -ENOMEM;
2397

2398
                        *buffer = p;
×
2399
                        *buffer_allocated = size;
×
2400
                }
2401

2402
                memcpy(*buffer, data, size);
×
2403
                *buffer_size = size;
×
2404
                break;
×
2405

2406
        default:
2407
                return -EOPNOTSUPP;
2408
        }
2409

2410
        return 0;
2411
}
2412

2413
int compressor_finish(Compressor *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated) {
60✔
2414
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2415
        int r;
60✔
2416
#endif
2417

2418
        assert(c);
60✔
2419
        assert(buffer);
60✔
2420
        assert(buffer_size);
60✔
2421
        assert(buffer_allocated);
60✔
2422

2423
        if (!c->encoding)
60✔
2424
                return -EINVAL;
2425

2426
        *buffer_size = 0;
60✔
2427

2428
        switch (c->type) {
60✔
2429

2430
#if HAVE_XZ
2431
        case COMPRESSION_XZ: {
5✔
2432
                lzma_ret lzr;
5✔
2433

2434
                c->xz.avail_in = 0;
5✔
2435

2436
                do {
5✔
2437
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
5✔
2438
                        if (r < 0)
5✔
2439
                                return r;
2440

2441
                        c->xz.next_out = (uint8_t*) *buffer + *buffer_size;
5✔
2442
                        c->xz.avail_out = *buffer_allocated - *buffer_size;
5✔
2443

2444
                        lzr = sym_lzma_code(&c->xz, LZMA_FINISH);
5✔
2445
                        if (!IN_SET(lzr, LZMA_OK, LZMA_STREAM_END))
5✔
2446
                                return -EIO;
2447

2448
                        *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out;
5✔
2449
                } while (lzr != LZMA_STREAM_END);
5✔
2450

2451
                break;
2452
        }
2453
#endif
2454

2455
#if HAVE_LZ4
2456
        case COMPRESSION_LZ4: {
5✔
2457
                size_t bound = sym_LZ4F_compressBound(0, &lz4_preferences);
5✔
2458
                r = enlarge_buffer(buffer, buffer_size, buffer_allocated, bound);
5✔
2459
                if (r < 0)
5✔
2460
                        return r;
2461

2462
                size_t n = sym_LZ4F_compressEnd(c->c_lz4, *buffer, *buffer_allocated, NULL);
5✔
2463
                if (sym_LZ4F_isError(n))
5✔
2464
                        return -EIO;
2465

2466
                *buffer_size = n;
5✔
2467
                break;
5✔
2468
        }
2469
#endif
2470

2471
#if HAVE_ZSTD
2472
        case COMPRESSION_ZSTD: {
30✔
2473
                ZSTD_inBuffer input = {};
30✔
2474
                size_t res;
30✔
2475

2476
                do {
30✔
2477
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
30✔
2478
                        if (r < 0)
30✔
2479
                                return r;
×
2480

2481
                        ZSTD_outBuffer output = {
30✔
2482
                                .dst = ((uint8_t *) *buffer + *buffer_size),
30✔
2483
                                .size = *buffer_allocated - *buffer_size,
30✔
2484
                        };
2485

2486
                        res = sym_ZSTD_compressStream2(c->c_zstd, &output, &input, ZSTD_e_end);
30✔
2487
                        if (sym_ZSTD_isError(res))
30✔
2488
                                return -EIO;
2489

2490
                        *buffer_size += output.pos;
30✔
2491
                } while (res != 0);
30✔
2492

2493
                break;
30✔
2494
        }
2495
#endif
2496

2497
#if HAVE_ZLIB
2498
        case COMPRESSION_GZIP:
14✔
2499
                c->gzip.avail_in = 0;
14✔
2500

2501
                do {
14✔
2502
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
14✔
2503
                        if (r < 0)
14✔
2504
                                return r;
2505

2506
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
14✔
2507
                        c->gzip.next_out = (uint8_t*) *buffer + *buffer_size;
14✔
2508
                        c->gzip.avail_out = avail;
14✔
2509

2510
                        r = sym_deflate(&c->gzip, Z_FINISH);
14✔
2511
                        if (!IN_SET(r, Z_OK, Z_STREAM_END))
14✔
2512
                                return -EIO;
2513

2514
                        *buffer_size += avail - c->gzip.avail_out;
14✔
2515
                } while (r != Z_STREAM_END);
14✔
2516

2517
                break;
2518
#endif
2519

2520
#if HAVE_BZIP2
2521
        case COMPRESSION_BZIP2:
5✔
2522
                c->bzip2.avail_in = 0;
5✔
2523

2524
                do {
5✔
2525
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
5✔
2526
                        if (r < 0)
5✔
2527
                                return r;
2528

2529
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
5✔
2530
                        c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size);
5✔
2531
                        c->bzip2.avail_out = avail;
5✔
2532

2533
                        r = sym_BZ2_bzCompress(&c->bzip2, BZ_FINISH);
5✔
2534
                        if (!IN_SET(r, BZ_FINISH_OK, BZ_STREAM_END))
5✔
2535
                                return -EIO;
2536

2537
                        *buffer_size += avail - c->bzip2.avail_out;
5✔
2538
                } while (r != BZ_STREAM_END);
5✔
2539

2540
                break;
2541
#endif
2542

2543
        case COMPRESSION_NONE:
2544
                break;
2545

2546
        default:
2547
                return -EOPNOTSUPP;
2548
        }
2549

2550
        return 0;
2551
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc