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

systemd / systemd / 29132483780

10 Jul 2026 08:43PM UTC coverage: 72.912% (+0.2%) from 72.702%
29132483780

push

github

bluca
man: run forgotten 'update-man-rules'

344600 of 472622 relevant lines covered (72.91%)

1365091.83 hits per line

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

93.43
/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
#ifndef SYSTEMD_CFLAGS_MARKER_LIBXZ
10
#  error "missing libxz_cflags in meson dependency."
11
#endif
12
#include <lzma.h>
13
#endif
14

15
#if HAVE_LZ4
16
#ifndef SYSTEMD_CFLAGS_MARKER_LIBLZ4
17
#  error "missing liblz4_cflags in meson dependency."
18
#endif
19
#include <lz4.h>
20
#include <lz4frame.h>
21
#include <lz4hc.h>
22
#endif
23

24
#if HAVE_ZSTD
25
#ifndef SYSTEMD_CFLAGS_MARKER_LIBZSTD
26
#  error "missing libzstd_cflags in meson dependency."
27
#endif
28
#include <zstd.h>
29
#include <zstd_errors.h>
30
#endif
31

32
#if HAVE_ZLIB
33
#ifndef SYSTEMD_CFLAGS_MARKER_LIBZ
34
#  error "missing libz_cflags in meson dependency."
35
#endif
36
#include <zlib.h>
37
#endif
38

39
#if HAVE_BZIP2
40
#ifndef SYSTEMD_CFLAGS_MARKER_LIBBZIP2
41
#  error "missing libbzip2_cflags in meson dependency."
42
#endif
43
#include <bzlib.h>
44
#endif
45

46
#include "alloc-util.h"
47
#include "bitfield.h"
48
#include "compress.h"
49
#include "dlfcn-util.h"
50
#include "io-util.h"
51
#include "log.h"
52
#include "string-table.h"
53
#include "unaligned.h"
54

55
#if HAVE_XZ
56
static DLSYM_PROTOTYPE(lzma_code) = NULL;
57
static DLSYM_PROTOTYPE(lzma_easy_encoder) = NULL;
58
static DLSYM_PROTOTYPE(lzma_end) = NULL;
59
static DLSYM_PROTOTYPE(lzma_stream_buffer_encode) = NULL;
60
static DLSYM_PROTOTYPE(lzma_stream_decoder) = NULL;
61
static DLSYM_PROTOTYPE(lzma_lzma_preset) = NULL;
62

63
/* We can’t just do _cleanup_(sym_lzma_end) because a compiler bug makes
64
 * this fail with:
65
 * ../src/basic/compress.c: In function ‘decompress_blob_xz’:
66
 * ../src/basic/compress.c:304:9: error: cleanup argument not a function
67
 *   304 |         _cleanup_(sym_lzma_end) lzma_stream s = LZMA_STREAM_INIT;
68
 *       |         ^~~~~~~~~
69
 */
70
static inline void lzma_end_wrapper(lzma_stream *ls) {
301✔
71
        sym_lzma_end(ls);
301✔
72
}
301✔
73
#endif
74

75
#if HAVE_LZ4
76
static DLSYM_PROTOTYPE(LZ4F_compressBegin) = NULL;
77
static DLSYM_PROTOTYPE(LZ4F_compressBound) = NULL;
78
static DLSYM_PROTOTYPE(LZ4F_compressEnd) = NULL;
79
static DLSYM_PROTOTYPE(LZ4F_compressUpdate) = NULL;
80
static DLSYM_PROTOTYPE(LZ4F_createCompressionContext) = NULL;
81
static DLSYM_PROTOTYPE(LZ4F_createDecompressionContext) = NULL;
82
static DLSYM_PROTOTYPE(LZ4F_decompress) = NULL;
83
static DLSYM_PROTOTYPE(LZ4F_freeCompressionContext) = NULL;
84
static DLSYM_PROTOTYPE(LZ4F_freeDecompressionContext) = NULL;
85
static DLSYM_PROTOTYPE(LZ4F_isError) = NULL;
86
static DLSYM_PROTOTYPE(LZ4_compress_HC) = NULL;
87
static DLSYM_PROTOTYPE(LZ4_compress_default) = NULL;
88
static DLSYM_PROTOTYPE(LZ4_decompress_safe) = NULL;
89
static DLSYM_PROTOTYPE(LZ4_decompress_safe_partial) = NULL;
90
static DLSYM_PROTOTYPE(LZ4_versionNumber) = NULL;
91

92
static const LZ4F_preferences_t lz4_preferences = {
93
        .frameInfo.blockSizeID = 5,
94
};
95
#endif
96

97
#if HAVE_ZSTD
98
static DLSYM_PROTOTYPE(ZSTD_CCtx_setParameter) = NULL;
99
static DLSYM_PROTOTYPE(ZSTD_compress) = NULL;
100
static DLSYM_PROTOTYPE(ZSTD_compressStream2) = NULL;
101
static DLSYM_PROTOTYPE(ZSTD_createCCtx) = NULL;
102
static DLSYM_PROTOTYPE(ZSTD_createDCtx) = NULL;
103
static DLSYM_PROTOTYPE(ZSTD_CStreamInSize) = NULL;
104
static DLSYM_PROTOTYPE(ZSTD_CStreamOutSize) = NULL;
105
static DLSYM_PROTOTYPE(ZSTD_decompressStream) = NULL;
106
static DLSYM_PROTOTYPE(ZSTD_DStreamInSize) = NULL;
107
static DLSYM_PROTOTYPE(ZSTD_DStreamOutSize) = NULL;
108
static DLSYM_PROTOTYPE(ZSTD_freeCCtx) = NULL;
109
static DLSYM_PROTOTYPE(ZSTD_freeDCtx) = NULL;
110
static DLSYM_PROTOTYPE(ZSTD_getErrorCode) = NULL;
111
static DLSYM_PROTOTYPE(ZSTD_getErrorName) = NULL;
112
static DLSYM_PROTOTYPE(ZSTD_getFrameContentSize) = NULL;
113
static DLSYM_PROTOTYPE(ZSTD_isError) = NULL;
114

115
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ZSTD_DCtx*, sym_ZSTD_freeDCtx, ZSTD_freeDCtxp, NULL);
835,561✔
116

117
static int zstd_ret_to_errno(size_t ret) {
8✔
118
        switch (sym_ZSTD_getErrorCode(ret)) {
8✔
119
        case ZSTD_error_dstSize_tooSmall:
120
                return -ENOBUFS;
121
        case ZSTD_error_memory_allocation:
×
122
                return -ENOMEM;
×
123
        default:
×
124
                return -EBADMSG;
×
125
        }
126
}
127
#endif
128

129
#if HAVE_ZLIB
130
static DLSYM_PROTOTYPE(deflateInit2_) = NULL;
131
static DLSYM_PROTOTYPE(deflate) = NULL;
132
static DLSYM_PROTOTYPE(deflateEnd) = NULL;
133
static DLSYM_PROTOTYPE(inflateInit2_) = NULL;
134
static DLSYM_PROTOTYPE(inflate) = NULL;
135
static DLSYM_PROTOTYPE(inflateEnd) = NULL;
136

137
static inline void deflateEnd_wrapper(z_stream *s) {
31✔
138
        sym_deflateEnd(s);
31✔
139
}
31✔
140

141
static inline void inflateEnd_wrapper(z_stream *s) {
290✔
142
        sym_inflateEnd(s);
290✔
143
}
290✔
144
#endif
145

146
#if HAVE_BZIP2
147
static DLSYM_PROTOTYPE(BZ2_bzCompressInit) = NULL;
148
static DLSYM_PROTOTYPE(BZ2_bzCompress) = NULL;
149
static DLSYM_PROTOTYPE(BZ2_bzCompressEnd) = NULL;
150
static DLSYM_PROTOTYPE(BZ2_bzDecompressInit) = NULL;
151
static DLSYM_PROTOTYPE(BZ2_bzDecompress) = NULL;
152
static DLSYM_PROTOTYPE(BZ2_bzDecompressEnd) = NULL;
153

154
static inline void BZ2_bzCompressEnd_wrapper(bz_stream *s) {
19✔
155
        sym_BZ2_bzCompressEnd(s);
19✔
156
}
19✔
157

158
static inline void BZ2_bzDecompressEnd_wrapper(bz_stream *s) {
278✔
159
        sym_BZ2_bzDecompressEnd(s);
278✔
160
}
278✔
161
#endif
162

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

192
#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
193

194
/* zlib windowBits value for gzip format: MAX_WBITS (15) + 16 to enable gzip header detection/generation */
195
#define ZLIB_WBITS_GZIP (15 + 16)
196

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

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

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

224
DEFINE_STRING_TABLE_LOOKUP(compression, Compression);
2,497✔
225
DEFINE_STRING_TABLE_LOOKUP(compression_uppercase, Compression);
21✔
226
DEFINE_STRING_TABLE_LOOKUP(compression_extension, Compression);
36✔
227

228
Compression compression_from_string_harder(const char *s) {
70✔
229
        Compression c;
70✔
230

231
        assert(s);
70✔
232

233
        c = compression_from_string(s);
70✔
234
        if (c >= 0)
70✔
235
                return c;
236

237
        return compression_uppercase_from_string(s);
21✔
238
}
239

240
Compression compression_from_filename(const char *filename) {
10✔
241
        Compression c;
10✔
242
        const char *e;
10✔
243

244
        assert(filename);
10✔
245

246
        e = strrchr(filename, '.');
10✔
247
        if (!e)
10✔
248
                return COMPRESSION_NONE;
249

250
        c = compression_extension_from_string(e);
10✔
251
        if (c < 0)
10✔
252
                return COMPRESSION_NONE;
×
253

254
        return c;
255
}
256

257
bool compression_supported_journal(Compression c) {
20✔
258
        static const unsigned supported =
20✔
259
                (1U << COMPRESSION_NONE) |
260
                (1U << COMPRESSION_XZ) * HAVE_XZ |
261
                (1U << COMPRESSION_LZ4) * HAVE_LZ4 |
262
                (1U << COMPRESSION_ZSTD) * HAVE_ZSTD;
263

264
        assert(c >= 0);
20✔
265
        assert(c < _COMPRESSION_MAX);
20✔
266

267
        return BIT_SET(supported, c);
20✔
268
}
269

270
bool compression_supported(Compression c) {
4,032✔
271
        static const unsigned supported =
4,032✔
272
                (1U << COMPRESSION_NONE) |
273
                (1U << COMPRESSION_XZ) * HAVE_XZ |
274
                (1U << COMPRESSION_LZ4) * HAVE_LZ4 |
275
                (1U << COMPRESSION_ZSTD) * HAVE_ZSTD |
276
                (1U << COMPRESSION_GZIP) * HAVE_ZLIB |
277
                (1U << COMPRESSION_BZIP2) * HAVE_BZIP2;
278

279
        assert(c >= 0);
4,032✔
280
        assert(c < _COMPRESSION_MAX);
4,032✔
281

282
        return BIT_SET(supported, c);
4,032✔
283
}
284

285
Compression compression_detect_from_magic(const uint8_t data[static COMPRESSION_MAGIC_BYTES_MAX]) {
1,058✔
286
        /* Magic signatures per RFC 1952 (gzip), tukaani.org/xz/xz-file-format.txt (xz),
287
         * RFC 8878 (zstd), lz4/doc/lz4_Frame_format.md (lz4), and the bzip2 file format.
288
         * Make sure to update COMPRESSION_MAGIC_BYTES_MAX if needed when adding a new magic. */
289
        if (memcmp(data, (const uint8_t[]) { 0x1f, 0x8b }, 2) == 0)
1,058✔
290
                return COMPRESSION_GZIP;
209✔
291
        if (memcmp(data, (const uint8_t[]) { 0xfd, '7', 'z', 'X', 'Z', 0x00 }, 6) == 0)
849✔
292
                return COMPRESSION_XZ;
1✔
293
        if (memcmp(data, (const uint8_t[]) { 0x28, 0xb5, 0x2f, 0xfd }, 4) == 0)
848✔
294
                return COMPRESSION_ZSTD;
2✔
295
        if (memcmp(data, (const uint8_t[]) { 0x04, 0x22, 0x4d, 0x18 }, 4) == 0)
846✔
296
                return COMPRESSION_LZ4;
1✔
297
        if (memcmp(data, (const uint8_t[]) { 'B', 'Z', 'h' }, 3) == 0)
845✔
298
                return COMPRESSION_BZIP2;
1✔
299

300
        return _COMPRESSION_INVALID;
844✔
301
}
302

303
int dlopen_xz(int log_level) {
343✔
304
#if HAVE_XZ
305
        static void *lzma_dl = NULL;
343✔
306

307
        LIBLZMA_NOTE(COMPRESSION_PRIORITY_XZ);
343✔
308

309
        return dlopen_many_sym_or_warn(
343✔
310
                        &lzma_dl,
311
                        "liblzma.so.5", log_level,
312
                        DLSYM_ARG(lzma_code),
313
                        DLSYM_ARG(lzma_easy_encoder),
314
                        DLSYM_ARG(lzma_end),
315
                        DLSYM_ARG(lzma_stream_buffer_encode),
316
                        DLSYM_ARG(lzma_lzma_preset),
317
                        DLSYM_ARG(lzma_stream_decoder));
318
#else
319
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
320
                              "lzma support is not compiled in.");
321
#endif
322
}
323

324
int dlopen_lz4(int log_level) {
709✔
325
#if HAVE_LZ4
326
        static void *lz4_dl = NULL;
709✔
327

328
        LIBLZ4_NOTE(COMPRESSION_PRIORITY_LZ4);
709✔
329

330
        return dlopen_many_sym_or_warn(
709✔
331
                        &lz4_dl,
332
                        "liblz4.so.1", log_level,
333
                        DLSYM_ARG(LZ4F_compressBegin),
334
                        DLSYM_ARG(LZ4F_compressBound),
335
                        DLSYM_ARG(LZ4F_compressEnd),
336
                        DLSYM_ARG(LZ4F_compressUpdate),
337
                        DLSYM_ARG(LZ4F_createCompressionContext),
338
                        DLSYM_ARG(LZ4F_createDecompressionContext),
339
                        DLSYM_ARG(LZ4F_decompress),
340
                        DLSYM_ARG(LZ4F_freeCompressionContext),
341
                        DLSYM_ARG(LZ4F_freeDecompressionContext),
342
                        DLSYM_ARG(LZ4F_isError),
343
                        DLSYM_ARG(LZ4_compress_default),
344
                        DLSYM_ARG(LZ4_compress_HC),
345
                        DLSYM_ARG(LZ4_decompress_safe),
346
                        DLSYM_ARG(LZ4_decompress_safe_partial),
347
                        DLSYM_ARG(LZ4_versionNumber));
348
#else
349
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
350
                              "lz4 support is not compiled in.");
351
#endif
352
}
353

354
int dlopen_zstd(int log_level) {
836,528✔
355
#if HAVE_ZSTD
356
        static void *zstd_dl = NULL;
836,528✔
357

358
        LIBZSTD_NOTE(COMPRESSION_PRIORITY_ZSTD);
836,528✔
359

360
        return dlopen_many_sym_or_warn(
836,528✔
361
                        &zstd_dl,
362
                        "libzstd.so.1", log_level,
363
                        DLSYM_ARG(ZSTD_getErrorCode),
364
                        DLSYM_ARG(ZSTD_compress),
365
                        DLSYM_ARG(ZSTD_getFrameContentSize),
366
                        DLSYM_ARG(ZSTD_decompressStream),
367
                        DLSYM_ARG(ZSTD_getErrorName),
368
                        DLSYM_ARG(ZSTD_DStreamOutSize),
369
                        DLSYM_ARG(ZSTD_CStreamInSize),
370
                        DLSYM_ARG(ZSTD_CStreamOutSize),
371
                        DLSYM_ARG(ZSTD_CCtx_setParameter),
372
                        DLSYM_ARG(ZSTD_compressStream2),
373
                        DLSYM_ARG(ZSTD_DStreamInSize),
374
                        DLSYM_ARG(ZSTD_freeCCtx),
375
                        DLSYM_ARG(ZSTD_freeDCtx),
376
                        DLSYM_ARG(ZSTD_isError),
377
                        DLSYM_ARG(ZSTD_createDCtx),
378
                        DLSYM_ARG(ZSTD_createCCtx));
379
#else
380
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
381
                              "zstd support is not compiled in.");
382
#endif
383
}
384

385
int dlopen_zlib(int log_level) {
553✔
386
#if HAVE_ZLIB
387
        static void *zlib_dl = NULL;
553✔
388

389
        LIBZ_NOTE(suggested);
553✔
390

391
        return dlopen_many_sym_or_warn(
553✔
392
                        &zlib_dl,
393
                        "libz.so.1", log_level,
394
                        DLSYM_ARG(deflateInit2_),
395
                        DLSYM_ARG(deflate),
396
                        DLSYM_ARG(deflateEnd),
397
                        DLSYM_ARG(inflateInit2_),
398
                        DLSYM_ARG(inflate),
399
                        DLSYM_ARG(inflateEnd));
400
#else
401
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
402
                              "zlib support is not compiled in.");
403
#endif
404
}
405

406
int dlopen_bzip2(int log_level) {
310✔
407
#if HAVE_BZIP2
408
        static void *bzip2_dl = NULL;
310✔
409

410
        LIBBZ2_NOTE(suggested);
310✔
411

412
        return dlopen_many_sym_or_warn(
310✔
413
                        &bzip2_dl,
414
                        "libbz2.so.1", log_level,
415
                        DLSYM_ARG(BZ2_bzCompressInit),
416
                        DLSYM_ARG(BZ2_bzCompress),
417
                        DLSYM_ARG(BZ2_bzCompressEnd),
418
                        DLSYM_ARG(BZ2_bzDecompressInit),
419
                        DLSYM_ARG(BZ2_bzDecompress),
420
                        DLSYM_ARG(BZ2_bzDecompressEnd));
421
#else
422
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
423
                              "bzip2 support is not compiled in.");
424
#endif
425
}
426

427
static int compress_blob_xz(
29✔
428
                const void *src,
429
                uint64_t src_size,
430
                void *dst,
431
                size_t dst_alloc_size,
432
                size_t *dst_size,
433
                int level) {
434

435
        assert(src);
29✔
436
        assert(src_size > 0);
29✔
437
        assert(dst);
29✔
438
        assert(dst_alloc_size > 0);
29✔
439
        assert(dst_size);
29✔
440

441
#if HAVE_XZ
442
        lzma_options_lzma opt = {
29✔
443
                1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
444
                LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4
445
        };
446
        lzma_filter filters[] = {
29✔
447
                { LZMA_FILTER_LZMA2, &opt },
448
                { LZMA_VLI_UNKNOWN, NULL }
449
        };
450
        lzma_ret ret;
29✔
451
        size_t out_pos = 0;
29✔
452
        int r;
29✔
453

454
        r = dlopen_xz(LOG_DEBUG);
29✔
455
        if (r < 0)
29✔
456
                return r;
29✔
457

458
        if (level >= 0) {
29✔
459
                r = sym_lzma_lzma_preset(&opt, (uint32_t) level);
×
460
                if (r < 0)
461
                        return r;
462
        }
463

464
        /* Returns < 0 if we couldn't compress the data or the
465
         * compressed result is longer than the original */
466

467
        if (src_size < 80)
29✔
468
                return -ENOBUFS;
469

470
        ret = sym_lzma_stream_buffer_encode(filters, LZMA_CHECK_NONE, NULL,
29✔
471
                                        src, src_size, dst, &out_pos, dst_alloc_size);
472
        if (ret != LZMA_OK)
29✔
473
                return -ENOBUFS;
474

475
        *dst_size = out_pos;
24✔
476
        return 0;
24✔
477
#else
478
        return -EPROTONOSUPPORT;
479
#endif
480
}
481

482
static int compress_blob_lz4(
215✔
483
                const void *src,
484
                uint64_t src_size,
485
                void *dst,
486
                size_t dst_alloc_size,
487
                size_t *dst_size,
488
                int level) {
489

490
        assert(src);
215✔
491
        assert(src_size > 0);
215✔
492
        assert(dst);
215✔
493
        assert(dst_alloc_size > 0);
215✔
494
        assert(dst_size);
215✔
495

496
#if HAVE_LZ4
497
        int r;
215✔
498

499
        r = dlopen_lz4(LOG_DEBUG);
215✔
500
        if (r < 0)
215✔
501
                return r;
502
        /* Returns < 0 if we couldn't compress the data or the
503
         * compressed result is longer than the original */
504

505
        if (src_size < 9)
215✔
506
                return -ENOBUFS;
507

508
        if (src_size > INT_MAX)
215✔
509
                return -EFBIG;
510
        if (dst_alloc_size > INT_MAX)
215✔
511
                dst_alloc_size = INT_MAX;
×
512

513
        if (level <= 0)
215✔
514
                r = sym_LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
215✔
515
        else
516
                r = sym_LZ4_compress_HC(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8, level);
×
517
        if (r <= 0)
215✔
518
                return -ENOBUFS;
519

520
        unaligned_write_le64(dst, src_size);
204✔
521
        *dst_size = r + 8;
204✔
522

523
        return 0;
204✔
524
#else
525
        return -EPROTONOSUPPORT;
526
#endif
527
}
528

529
static int compress_blob_zstd(
910✔
530
                const void *src,
531
                uint64_t src_size,
532
                void *dst,
533
                size_t dst_alloc_size,
534
                size_t *dst_size,
535
                int level) {
536

537
        assert(src);
910✔
538
        assert(src_size > 0);
910✔
539
        assert(dst);
910✔
540
        assert(dst_alloc_size > 0);
910✔
541
        assert(dst_size);
910✔
542

543
#if HAVE_ZSTD
544
        size_t k;
910✔
545
        int r;
910✔
546

547
        r = dlopen_zstd(LOG_DEBUG);
910✔
548
        if (r < 0)
910✔
549
                return r;
550

551
        k = sym_ZSTD_compress(dst, dst_alloc_size, src, src_size, level < 0 ? 0 : level);
910✔
552
        if (sym_ZSTD_isError(k))
910✔
553
                return zstd_ret_to_errno(k);
8✔
554

555
        *dst_size = k;
902✔
556
        return 0;
902✔
557
#else
558
        return -EPROTONOSUPPORT;
559
#endif
560
}
561

562
static int compress_blob_gzip(const void *src, uint64_t src_size,
31✔
563
                       void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
564

565
        assert(src);
31✔
566
        assert(src_size > 0);
31✔
567
        assert(dst);
31✔
568
        assert(dst_alloc_size > 0);
31✔
569
        assert(dst_size);
31✔
570

571
#if HAVE_ZLIB
572
        int r;
31✔
573

574
        r = dlopen_zlib(LOG_DEBUG);
31✔
575
        if (r < 0)
31✔
576
                return r;
31✔
577

578
        if (src_size > UINT_MAX)
31✔
579
                return -EFBIG;
580
        if (dst_alloc_size > UINT_MAX)
31✔
581
                dst_alloc_size = UINT_MAX;
×
582

583
        _cleanup_(deflateEnd_wrapper) z_stream s = {};
31✔
584

585
        r = sym_deflateInit2_(&s, level < 0 ? Z_DEFAULT_COMPRESSION : level,
31✔
586
                              /* method= */ Z_DEFLATED,
587
                              /* windowBits= */ ZLIB_WBITS_GZIP,
588
                              /* memLevel= */ 8,
589
                              /* strategy= */ Z_DEFAULT_STRATEGY,
590
                              ZLIB_VERSION, (int) sizeof(s));
591
        if (r != Z_OK)
31✔
592
                return -ENOMEM;
593

594
        s.next_in = (void*) src;
31✔
595
        s.avail_in = src_size;
31✔
596
        s.next_out = dst;
31✔
597
        s.avail_out = dst_alloc_size;
31✔
598

599
        r = sym_deflate(&s, Z_FINISH);
31✔
600
        if (r != Z_STREAM_END)
31✔
601
                return -ENOBUFS;
602

603
        *dst_size = dst_alloc_size - s.avail_out;
26✔
604
        return 0;
26✔
605
#else
606
        return -EPROTONOSUPPORT;
607
#endif
608
}
609

610
static int compress_blob_bzip2(
19✔
611
                const void *src, uint64_t src_size,
612
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
613

614
        assert(src);
19✔
615
        assert(src_size > 0);
19✔
616
        assert(dst);
19✔
617
        assert(dst_alloc_size > 0);
19✔
618
        assert(dst_size);
19✔
619

620
#if HAVE_BZIP2
621
        int r;
19✔
622

623
        r = dlopen_bzip2(LOG_DEBUG);
19✔
624
        if (r < 0)
19✔
625
                return r;
19✔
626

627
        if (src_size > UINT_MAX)
19✔
628
                return -EFBIG;
629
        if (dst_alloc_size > UINT_MAX)
19✔
630
                dst_alloc_size = UINT_MAX;
×
631

632
        _cleanup_(BZ2_bzCompressEnd_wrapper) bz_stream s = {};
19✔
633

634
        r = sym_BZ2_bzCompressInit(&s, level < 0 ? 9 : level, /* verbosity= */ 0, /* workFactor= */ 0);
38✔
635
        if (r != BZ_OK)
19✔
636
                return -ENOMEM;
637

638
        s.next_in = (char*) src;
19✔
639
        s.avail_in = src_size;
19✔
640
        s.next_out = (char*) dst;
19✔
641
        s.avail_out = dst_alloc_size;
19✔
642

643
        r = sym_BZ2_bzCompress(&s, BZ_FINISH);
19✔
644

645
        if (r != BZ_STREAM_END)
19✔
646
                return -ENOBUFS;
647

648
        *dst_size = dst_alloc_size - s.avail_out;
15✔
649
        return 0;
15✔
650
#else
651
        return -EPROTONOSUPPORT;
652
#endif
653
}
654

655
int compress_blob_journal(
1,154✔
656
                Compression compression,
657
                const void *src, uint64_t src_size,
658
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
659

660
        switch (compression) {
1,154✔
661
        case COMPRESSION_XZ:
29✔
662
                return compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size, level);
29✔
663
        case COMPRESSION_LZ4:
215✔
664
                return compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size, level);
215✔
665
        case COMPRESSION_ZSTD:
910✔
666
                return compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size, level);
910✔
667
        default:
668
                return -EOPNOTSUPP;
669
        }
670
}
671

672
int compress_blob(
439✔
673
                Compression compression,
674
                const void *src, uint64_t src_size,
675
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
676

677
        switch (compression) {
439✔
678
        case COMPRESSION_GZIP:
31✔
679
                return compress_blob_gzip(src, src_size, dst, dst_alloc_size, dst_size, level);
31✔
680
        case COMPRESSION_BZIP2:
19✔
681
                return compress_blob_bzip2(src, src_size, dst, dst_alloc_size, dst_size, level);
19✔
682
        default:
389✔
683
                return compress_blob_journal(compression, src, src_size, dst, dst_alloc_size, dst_size, level);
389✔
684
        }
685
}
686

687
static int decompress_blob_xz(
31✔
688
                const void *src,
689
                uint64_t src_size,
690
                void **dst,
691
                size_t *dst_size,
692
                size_t dst_max) {
693

694
        assert(src);
31✔
695
        assert(src_size > 0);
31✔
696
        assert(dst);
31✔
697
        assert(dst_size);
31✔
698

699
#if HAVE_XZ
700
        int r;
31✔
701

702
        r = dlopen_xz(LOG_DEBUG);
31✔
703
        if (r < 0)
31✔
704
                return r;
31✔
705

706
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
31✔
707
        lzma_ret ret = sym_lzma_stream_decoder(&s, UINT64_MAX, /* flags= */ 0);
31✔
708
        if (ret != LZMA_OK)
31✔
709
                return -ENOMEM;
710

711
        size_t space = MIN(src_size * 2, dst_max ?: SIZE_MAX);
31✔
712
        if (!greedy_realloc(dst, space, 1))
31✔
713
                return -ENOMEM;
714

715
        s.next_in = src;
31✔
716
        s.avail_in = src_size;
31✔
717

718
        s.next_out = *dst;
31✔
719
        s.avail_out = space;
31✔
720

721
        for (;;) {
331✔
722
                size_t used;
181✔
723

724
                ret = sym_lzma_code(&s, LZMA_FINISH);
181✔
725
                if (ret == LZMA_STREAM_END)
181✔
726
                        break;
727
                if (ret != LZMA_OK)
152✔
728
                        return -ENOMEM;
729

730
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
150✔
731
                        break;
732
                if (dst_max > 0 && space == dst_max)
150✔
733
                        return -ENOBUFS;
734

735
                used = space - s.avail_out;
150✔
736
                /* Silence static analyzers, space is bounded by allocation size */
737
                assert(space <= SIZE_MAX / 2);
150✔
738
                space = MIN(2 * space, dst_max ?: SIZE_MAX);
150✔
739
                if (!greedy_realloc(dst, space, 1))
150✔
740
                        return -ENOMEM;
741

742
                s.avail_out = space - used;
150✔
743
                s.next_out = *(uint8_t**)dst + used;
150✔
744
        }
745

746
        *dst_size = space - s.avail_out;
29✔
747
        return 0;
29✔
748
#else
749
        return -EPROTONOSUPPORT;
750
#endif
751
}
752

753
static int decompress_blob_lz4(
211✔
754
                const void *src,
755
                uint64_t src_size,
756
                void **dst,
757
                size_t *dst_size,
758
                size_t dst_max) {
759

760
        assert(src);
211✔
761
        assert(src_size > 0);
211✔
762
        assert(dst);
211✔
763
        assert(dst_size);
211✔
764

765
#if HAVE_LZ4
766
        char* out;
211✔
767
        int r, size; /* LZ4 uses int for size */
211✔
768

769
        r = dlopen_lz4(LOG_DEBUG);
211✔
770
        if (r < 0)
211✔
771
                return r;
772

773
        if (src_size <= 8)
211✔
774
                return -EBADMSG;
775

776
        if (src_size - 8 > INT_MAX)
209✔
777
                return -EFBIG;
778

779
        size = unaligned_read_le64(src);
209✔
780
        if (size < 0 || (unsigned) size != unaligned_read_le64(src))
209✔
781
                return -EFBIG;
782
        if (dst_max > 0 && (size_t) size > dst_max)
209✔
783
                return -ENOBUFS;
784
        out = greedy_realloc(dst, size, 1);
209✔
785
        if (!out)
209✔
786
                return -ENOMEM;
787

788
        r = sym_LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
209✔
789
        if (r < 0 || r != size)
209✔
790
                return -EBADMSG;
791

792
        *dst_size = size;
209✔
793
        return 0;
209✔
794
#else
795
        return -EPROTONOSUPPORT;
796
#endif
797
}
798

799
size_t zstd_dstream_out_size(void) {
1✔
800
#if HAVE_ZSTD
801
        if (dlopen_zstd(LOG_DEBUG) < 0)
1✔
802
                return 0;
803

804
        return sym_ZSTD_DStreamOutSize();
1✔
805
#else
806
        return 0;
807
#endif
808
}
809

810
static int decompress_blob_zstd(
43,763✔
811
                const void *src,
812
                uint64_t src_size,
813
                void **dst,
814
                size_t *dst_size,
815
                size_t dst_max) {
816

817
        assert(src);
43,763✔
818
        assert(src_size > 0);
43,763✔
819
        assert(dst);
43,763✔
820
        assert(dst_size);
43,763✔
821

822
#if HAVE_ZSTD
823
        int r;
43,763✔
824

825
        r = dlopen_zstd(LOG_DEBUG);
43,763✔
826
        if (r < 0)
43,763✔
827
                return r;
43,763✔
828

829
        /* ZSTD_getFrameContentSize() returns unsigned long long, which is not necessarily size_t, so
830
         * keep the value in that type until we've clamped it down to something that fits size_t. */
831
        unsigned long long size = sym_ZSTD_getFrameContentSize(src, src_size);
43,763✔
832
        if (size == ZSTD_CONTENTSIZE_ERROR)
43,763✔
833
                return -EBADMSG;
834

835
        /* ZSTD_CONTENTSIZE_UNKNOWN is returned when the frame header doesn't record the decompressed
836
         * size, which is the case e.g. for kernel images compressed with 'zstd'. Per the zstd
837
         * documentation this is not an error, it just means we don't know the output size upfront and
838
         * have to grow the output buffer as we decompress. */
839

840
        /* dst_max == 0 means "no limit"; fold that into a single ceiling up front so the rest of the
841
         * function can treat the unlimited case like any other rather than repeating the sentinel. */
842
        size_t cap = dst_max ?: SIZE_MAX;
43,761✔
843

844
        size_t space;
43,761✔
845
        if (size != ZSTD_CONTENTSIZE_UNKNOWN)
43,761✔
846
                /* The MIN clamps the (possibly 64-bit) content size to the ceiling, which is <= SIZE_MAX,
847
                 * so the result always fits size_t. */
848
                space = MAX(sym_ZSTD_DStreamOutSize(), (size_t) MIN(size, (unsigned long long) cap));
43,756✔
849
        else {
850
                /* Start from twice the compressed size, guarding the doubling against overflow, then
851
                 * clamp to the ceiling. */
852
                uint64_t twice = src_size <= UINT64_MAX / 2 ? src_size * 2 : UINT64_MAX;
5✔
853
                space = MAX(sym_ZSTD_DStreamOutSize(), (size_t) MIN(twice, (uint64_t) cap));
5✔
854
        }
855

856
        if (!greedy_realloc(dst, space, 1))
43,761✔
857
                return -ENOMEM;
858
        space = MALLOC_SIZEOF_SAFE(*dst);
43,761✔
859

860
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
87,522✔
861
        if (!dctx)
43,761✔
862
                return -ENOMEM;
863

864
        ZSTD_inBuffer input = {
43,761✔
865
                .src = src,
866
                .size = src_size,
867
        };
868
        ZSTD_outBuffer output = {
43,761✔
869
                .dst = *dst,
43,761✔
870
                /* Never offer the decoder more room than the cap: greedy_realloc() over-allocates, so
871
                 * without this clamp a single ZSTD_decompressStream() call could write past dst_max
872
                 * before the output.pos >= cap check below fires. */
873
                .size = MIN(space, cap),
43,761✔
874
        };
875

876
        for (;;) {
43,766✔
877
                size_t k = sym_ZSTD_decompressStream(dctx, &output, &input);
43,766✔
878
                if (sym_ZSTD_isError(k))
43,766✔
879
                        return log_debug_errno(zstd_ret_to_errno(k), "ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
880

881
                /* output.size is clamped to MIN(space, cap) here and after every grow, and zstd keeps
882
                 * output.pos <= output.size, so output.pos never exceeds cap: this fires exactly when the
883
                 * caller's limit has been filled. */
884
                if (output.pos >= cap)
43,766✔
885
                        break;
886

887
                /* k == 0 means the current frame was fully decoded and flushed. If no input is left
888
                 * either, the whole stream is done. This must be checked before the grow branch below:
889
                 * the frame can finish exactly as the output buffer fills, and growing then would call
890
                 * the decoder again on already-exhausted input, which it'd misread as a truncated next
891
                 * frame. */
892
                if (k == 0 && input.pos >= input.size)
43,765✔
893
                        break;
894

895
                /* Otherwise there is still work to do: either decoded data buffered inside the decoder
896
                 * that didn't fit (k != 0, e.g. zstd's buffered-flush mode), or another concatenated
897
                 * frame to decode (k == 0 with input left). If the output buffer is full, grow it and
898
                 * call again. Guard against the doubling overflowing size_t (only reachable with an
899
                 * uncapped dst_max once the allocation has already grown past SIZE_MAX/2). */
900
                if (output.pos == output.size) {
11✔
901
                        if (space > SIZE_MAX / 2)
4✔
902
                                return -E2BIG;
903
                        space = MIN(2 * space, cap);
4✔
904
                        if (!greedy_realloc(dst, space, 1))
4✔
905
                                return -ENOMEM;
906
                        space = MALLOC_SIZEOF_SAFE(*dst);
4✔
907
                        output.dst = *dst;
4✔
908
                        output.size = MIN(space, cap);
4✔
909
                        continue;
4✔
910
                }
911

912
                /* The output buffer still has room but the decoder stopped. If input is exhausted the
913
                 * decoder wanted more (k != 0, since k == 0 + no input broke above), i.e. the stream was
914
                 * truncated mid-frame. Otherwise input remains (a concatenated frame) and we loop. */
915
                if (input.pos >= input.size)
7✔
916
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD stream ended unexpectedly, probably corrupted.");
6✔
917
        }
918

919
        *dst_size = output.pos;
43,755✔
920
        return 0;
43,755✔
921
#else
922
        return -EPROTONOSUPPORT;
923
#endif
924
}
925

926
static int decompress_blob_gzip(
24✔
927
                const void *src,
928
                uint64_t src_size,
929
                void **dst,
930
                size_t *dst_size,
931
                size_t dst_max) {
932

933
        assert(src);
24✔
934
        assert(src_size > 0);
24✔
935
        assert(dst);
24✔
936
        assert(dst_size);
24✔
937

938
#if HAVE_ZLIB
939
        int r;
24✔
940

941
        r = dlopen_zlib(LOG_DEBUG);
24✔
942
        if (r < 0)
24✔
943
                return r;
24✔
944

945
        if (src_size > UINT_MAX)
24✔
946
                return -EFBIG;
947

948
        _cleanup_(inflateEnd_wrapper) z_stream s = {};
24✔
949

950
        r = sym_inflateInit2_(&s, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(s));
24✔
951
        if (r != Z_OK)
24✔
952
                return -ENOMEM;
953

954
        size_t space = MIN3(src_size * 2, dst_max ?: SIZE_MAX, (size_t) UINT_MAX);
24✔
955
        if (!greedy_realloc(dst, space, 1))
24✔
956
                return -ENOMEM;
957

958
        s.next_in = (void*) src;
24✔
959
        s.avail_in = src_size;
24✔
960
        s.next_out = *dst;
24✔
961
        s.avail_out = space;
24✔
962

963
        for (;;) {
338✔
964
                size_t used;
181✔
965

966
                r = sym_inflate(&s, Z_NO_FLUSH);
181✔
967
                if (r == Z_STREAM_END)
181✔
968
                        break;
969
                if (!IN_SET(r, Z_OK, Z_BUF_ERROR))
159✔
970
                        return -EBADMSG;
971

972
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
157✔
973
                        break;
974
                if (dst_max > 0 && space == dst_max)
157✔
975
                        return -ENOBUFS;
976

977
                used = space - s.avail_out;
157✔
978
                space = MIN3(2 * space, dst_max ?: SIZE_MAX, UINT_MAX);
157✔
979
                if (!greedy_realloc(dst, space, 1))
157✔
980
                        return -ENOMEM;
981

982
                s.avail_out = space - used;
157✔
983
                s.next_out = *(uint8_t**)dst + used;
157✔
984
        }
985

986
        *dst_size = space - s.avail_out;
22✔
987
        return 0;
22✔
988
#else
989
        return -EPROTONOSUPPORT;
990
#endif
991
}
992

993
static int decompress_blob_bzip2(
12✔
994
                const void *src,
995
                uint64_t src_size,
996
                void **dst,
997
                size_t *dst_size,
998
                size_t dst_max) {
999

1000
        assert(src);
12✔
1001
        assert(src_size > 0);
12✔
1002
        assert(dst);
12✔
1003
        assert(dst_size);
12✔
1004

1005
#if HAVE_BZIP2
1006
        int r;
12✔
1007

1008
        r = dlopen_bzip2(LOG_DEBUG);
12✔
1009
        if (r < 0)
12✔
1010
                return r;
12✔
1011

1012
        if (src_size > UINT_MAX)
12✔
1013
                return -EFBIG;
1014

1015
        _cleanup_(BZ2_bzDecompressEnd_wrapper) bz_stream s = {};
12✔
1016

1017
        r = sym_BZ2_bzDecompressInit(&s, /* verbosity= */ 0, /* small= */ 0);
12✔
1018
        if (r != BZ_OK)
12✔
1019
                return -ENOMEM;
1020

1021
        size_t space = MIN3(src_size * 2, dst_max ?: SIZE_MAX, (size_t) UINT_MAX);
12✔
1022
        if (!greedy_realloc(dst, space, 1))
12✔
1023
                return -ENOMEM;
1024

1025
        s.next_in = (char*) src;
12✔
1026
        s.avail_in = src_size;
12✔
1027
        s.next_out = (char*) *dst;
12✔
1028
        s.avail_out = space;
12✔
1029

1030
        for (;;) {
158✔
1031
                size_t used;
85✔
1032

1033
                r = sym_BZ2_bzDecompress(&s);
85✔
1034
                if (r == BZ_STREAM_END)
85✔
1035
                        break;
1036
                if (r != BZ_OK)
75✔
1037
                        return -EBADMSG;
1038

1039
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
73✔
1040
                        break;
1041
                if (dst_max > 0 && space == dst_max)
73✔
1042
                        return -ENOBUFS;
1043

1044
                used = space - s.avail_out;
73✔
1045
                space = MIN3(2 * space, dst_max ?: SIZE_MAX, (size_t) UINT_MAX);
73✔
1046
                if (!greedy_realloc(dst, space, 1))
73✔
1047
                        return -ENOMEM;
1048

1049
                s.avail_out = space - used;
73✔
1050
                s.next_out = (char*) *dst + used;
73✔
1051
        }
1052

1053
        *dst_size = space - s.avail_out;
10✔
1054
        return 0;
10✔
1055
#else
1056
        return -EPROTONOSUPPORT;
1057
#endif
1058
}
1059

1060
int decompress_blob_journal(
44,005✔
1061
                Compression compression,
1062
                const void *src,
1063
                uint64_t src_size,
1064
                void **dst,
1065
                size_t *dst_size,
1066
                size_t dst_max) {
1067

1068
        switch (compression) {
44,005✔
1069
        case COMPRESSION_XZ:
31✔
1070
                return decompress_blob_xz(
31✔
1071
                                src, src_size,
1072
                                dst, dst_size, dst_max);
1073
        case COMPRESSION_LZ4:
211✔
1074
                return decompress_blob_lz4(
211✔
1075
                                src, src_size,
1076
                                dst, dst_size, dst_max);
1077
        case COMPRESSION_ZSTD:
43,763✔
1078
                return decompress_blob_zstd(
43,763✔
1079
                                src, src_size,
1080
                                dst, dst_size, dst_max);
1081
        default:
1082
                return -EPROTONOSUPPORT;
1083
        }
1084
}
1085

1086
int decompress_blob(
408✔
1087
                Compression compression,
1088
                const void *src,
1089
                uint64_t src_size,
1090
                void **dst,
1091
                size_t *dst_size,
1092
                size_t dst_max) {
1093

1094
        switch (compression) {
408✔
1095
        case COMPRESSION_GZIP:
24✔
1096
                return decompress_blob_gzip(
24✔
1097
                                src, src_size,
1098
                                dst, dst_size, dst_max);
1099
        case COMPRESSION_BZIP2:
12✔
1100
                return decompress_blob_bzip2(
12✔
1101
                                src, src_size,
1102
                                dst, dst_size, dst_max);
1103
        default:
372✔
1104
                return decompress_blob_journal(
372✔
1105
                                compression,
1106
                                src, src_size,
1107
                                dst, dst_size, dst_max);
1108
        }
1109
}
1110

1111
int decompress_zlib_raw(
×
1112
                const void *src,
1113
                uint64_t src_size,
1114
                void *dst,
1115
                size_t dst_size,
1116
                int wbits) {
1117

1118
#if HAVE_ZLIB
1119
        int r;
×
1120

1121
        r = dlopen_zlib(LOG_DEBUG);
×
1122
        if (r < 0)
×
1123
                return r;
×
1124

1125
        if (src_size > UINT_MAX)
×
1126
                return -EFBIG;
1127
        if (dst_size > UINT_MAX)
×
1128
                return -EFBIG;
1129

1130
        _cleanup_(inflateEnd_wrapper) z_stream s = {
×
1131
                .next_in = (void*) src,
1132
                .avail_in = src_size,
1133
                .next_out = dst,
1134
                .avail_out = dst_size,
1135
        };
1136

1137
        r = sym_inflateInit2_(&s, /* windowBits= */ wbits, ZLIB_VERSION, (int) sizeof(s));
×
1138
        if (r != Z_OK)
×
1139
                return -EIO;
1140

1141
        r = sym_inflate(&s, Z_FINISH);
×
1142
        size_t produced = (uint8_t*) s.next_out - (uint8_t*) dst;
×
1143

1144
        if (r != Z_STREAM_END || produced != dst_size)
×
1145
                return -EBADMSG;
×
1146

1147
        return 0;
1148
#else
1149
        return -EPROTONOSUPPORT;
1150
#endif
1151
}
1152

1153
static int decompress_startswith_xz(
270✔
1154
                const void *src,
1155
                uint64_t src_size,
1156
                void **buffer,
1157
                const void *prefix,
1158
                size_t prefix_len,
1159
                uint8_t extra) {
1160

1161
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
1162
         * follow the prefix */
1163

1164
        assert(src);
270✔
1165
        assert(src_size > 0);
270✔
1166
        assert(buffer);
270✔
1167
        assert(prefix);
270✔
1168

1169
#if HAVE_XZ
1170
        int r;
270✔
1171

1172
        r = dlopen_xz(LOG_DEBUG);
270✔
1173
        if (r < 0)
270✔
1174
                return r;
270✔
1175

1176
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
270✔
1177
        lzma_ret ret = sym_lzma_stream_decoder(&s, UINT64_MAX, /* flags= */ 0);
270✔
1178
        if (ret != LZMA_OK)
270✔
1179
                return -EBADMSG;
1180

1181
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
1182
                return -ENOMEM;
1183

1184
        size_t allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
1185

1186
        s.next_in = src;
270✔
1187
        s.avail_in = src_size;
270✔
1188

1189
        s.next_out = *buffer;
270✔
1190
        s.avail_out = allocated;
270✔
1191

1192
        for (;;) {
270✔
1193
                ret = sym_lzma_code(&s, LZMA_FINISH);
270✔
1194

1195
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
270✔
1196
                        return -EBADMSG;
1197

1198
                if (allocated - s.avail_out >= prefix_len + 1)
270✔
1199
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
270✔
1200
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
270✔
1201

1202
                if (ret == LZMA_STREAM_END)
×
1203
                        return 0;
1204

1205
                s.avail_out += allocated;
×
1206

1207
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
1208
                        return -ENOMEM;
1209

1210
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
1211
                s.next_out = *(uint8_t**)buffer + allocated - s.avail_out;
×
1212
        }
1213

1214
#else
1215
        return -EPROTONOSUPPORT;
1216
#endif
1217
}
1218

1219
static int decompress_startswith_lz4(
270✔
1220
                const void *src,
1221
                uint64_t src_size,
1222
                void **buffer,
1223
                const void *prefix,
1224
                size_t prefix_len,
1225
                uint8_t extra) {
1226

1227
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
1228
         * follow the prefix */
1229

1230
        assert(src);
270✔
1231
        assert(src_size > 0);
270✔
1232
        assert(buffer);
270✔
1233
        assert(prefix);
270✔
1234

1235
#if HAVE_LZ4
1236
        size_t allocated;
270✔
1237
        int r;
270✔
1238

1239
        r = dlopen_lz4(LOG_DEBUG);
270✔
1240
        if (r < 0)
270✔
1241
                return r;
1242

1243
        if (src_size <= 8)
270✔
1244
                return -EBADMSG;
1245

1246
        if (src_size - 8 > INT_MAX)
270✔
1247
                return -EFBIG;
1248

1249
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
1250
                return -ENOMEM;
1251
        allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
1252

1253
        r = sym_LZ4_decompress_safe_partial(
540✔
1254
                        (char*)src + 8,
1255
                        *buffer,
1256
                        src_size - 8,
270✔
1257
                        prefix_len + 1,
270✔
1258
                        allocated);
1259

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

1267
                if (sym_LZ4_versionNumber() >= 10803)
×
1268
                        /* We trust that the newer lz4 decompresses the number of bytes we
1269
                         * requested if available in the compressed string. */
1270
                        return 0;
×
1271

1272
                if (r > 0)
×
1273
                        /* Compare what we have first, in case of mismatch we can
1274
                         * shortcut the full comparison. */
1275
                        if (memcmp(*buffer, prefix, r) != 0)
×
1276
                                return 0;
1277

1278
                /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
1279
                 * so in pathological cases might need to decompress the full field. */
1280
                r = decompress_blob_lz4(src, src_size, buffer, &size, 0);
×
1281
                if (r < 0)
×
1282
                        return r;
1283

1284
                if (size < prefix_len + 1)
×
1285
                        return 0;
1286
        }
1287

1288
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
270✔
1289
                ((const uint8_t*) *buffer)[prefix_len] == extra;
270✔
1290
#else
1291
        return -EPROTONOSUPPORT;
1292
#endif
1293
}
1294

1295
static int decompress_startswith_zstd(
791,800✔
1296
                const void *src,
1297
                uint64_t src_size,
1298
                void **buffer,
1299
                const void *prefix,
1300
                size_t prefix_len,
1301
                uint8_t extra) {
1302

1303
        assert(src);
791,800✔
1304
        assert(src_size > 0);
791,800✔
1305
        assert(buffer);
791,800✔
1306
        assert(prefix);
791,800✔
1307

1308
#if HAVE_ZSTD
1309
        int r;
791,800✔
1310

1311
        r = dlopen_zstd(LOG_DEBUG);
791,800✔
1312
        if (r < 0)
791,800✔
1313
                return r;
791,800✔
1314

1315
        /* ZSTD_getFrameContentSize() returns unsigned long long, which is not necessarily size_t. */
1316
        unsigned long long size = sym_ZSTD_getFrameContentSize(src, src_size);
791,800✔
1317
        if (size == ZSTD_CONTENTSIZE_ERROR)
791,800✔
1318
                return -EBADMSG;
1319

1320
        /* ZSTD_CONTENTSIZE_UNKNOWN means the frame doesn't record the decompressed size, so we can't
1321
         * shortcut here. The single decompression below only needs prefix_len + 1 bytes anyway. */
1322
        if (size != ZSTD_CONTENTSIZE_UNKNOWN && size < prefix_len + 1)
791,800✔
1323
                return 0; /* Decompressed text too short to match the prefix and extra */
1324

1325
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
1,583,600✔
1326
        if (!dctx)
791,800✔
1327
                return -ENOMEM;
1328

1329
        if (!(greedy_realloc(buffer, MAX(sym_ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
791,800✔
1330
                return -ENOMEM;
1331

1332
        ZSTD_inBuffer input = {
791,800✔
1333
                .src = src,
1334
                .size = src_size,
1335
        };
1336
        ZSTD_outBuffer output = {
1,583,600✔
1337
                .dst = *buffer,
791,800✔
1338
                .size = MALLOC_SIZEOF_SAFE(*buffer),
791,800✔
1339
        };
1340
        size_t k;
791,800✔
1341

1342
        k = sym_ZSTD_decompressStream(dctx, &output, &input);
791,800✔
1343
        if (sym_ZSTD_isError(k))
791,800✔
1344
                return log_debug_errno(zstd_ret_to_errno(k), "ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
1345
        if (output.pos < prefix_len + 1) {
791,800✔
1346
                /* The output buffer is at least prefix_len + 1 bytes, so if it isn't full the decoder
1347
                 * stopped for another reason: either the frame ended (k == 0), in which case the stream
1348
                 * is simply too short to match the prefix, or it's still expecting input (k != 0), which
1349
                 * means the stream was truncated mid-frame. */
1350
                if (k != 0)
3✔
1351
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD stream ended unexpectedly, probably corrupted.");
2✔
1352
                return 0; /* Decompressed text too short to match the prefix and extra */
1353
        }
1354

1355
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
791,797✔
1356
                ((const uint8_t*) *buffer)[prefix_len] == extra;
32,501✔
1357
#else
1358
        return -EPROTONOSUPPORT;
1359
#endif
1360
}
1361

1362
static int decompress_startswith_gzip(
266✔
1363
                const void *src,
1364
                uint64_t src_size,
1365
                void **buffer,
1366
                const void *prefix,
1367
                size_t prefix_len,
1368
                uint8_t extra) {
1369

1370
        assert(src);
266✔
1371
        assert(src_size > 0);
266✔
1372
        assert(buffer);
266✔
1373
        assert(prefix);
266✔
1374

1375
#if HAVE_ZLIB
1376
        int r;
266✔
1377

1378
        r = dlopen_zlib(LOG_DEBUG);
266✔
1379
        if (r < 0)
266✔
1380
                return r;
266✔
1381

1382
        if (src_size > UINT_MAX)
266✔
1383
                return -EFBIG;
1384

1385
        _cleanup_(inflateEnd_wrapper) z_stream s = {};
266✔
1386

1387
        r = sym_inflateInit2_(&s, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(s));
266✔
1388
        if (r != Z_OK)
266✔
1389
                return -EBADMSG;
1390

1391
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
266✔
1392
                return -ENOMEM;
1393

1394
        size_t allocated = MALLOC_SIZEOF_SAFE(*buffer);
266✔
1395

1396
        s.next_in = (void*) src;
266✔
1397
        s.avail_in = src_size;
266✔
1398

1399
        s.next_out = *buffer;
266✔
1400
        s.avail_out = MIN(allocated, (size_t) UINT_MAX);
266✔
1401

1402
        for (;;) {
266✔
1403
                r = sym_inflate(&s, Z_FINISH);
266✔
1404

1405
                if (!IN_SET(r, Z_OK, Z_STREAM_END, Z_BUF_ERROR))
266✔
1406
                        return -EBADMSG;
1407

1408
                if (allocated - s.avail_out >= prefix_len + 1)
266✔
1409
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
266✔
1410
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
266✔
1411

1412
                if (r == Z_STREAM_END)
×
1413
                        return 0;
1414

1415
                size_t used = allocated - s.avail_out;
×
1416

1417
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
1418
                        return -ENOMEM;
1419

1420
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
1421
                s.avail_out = MIN(allocated - used, (size_t) UINT_MAX);
×
1422
                s.next_out = *(uint8_t**)buffer + used;
×
1423
        }
1424
#else
1425
        return -EPROTONOSUPPORT;
1426
#endif
1427
}
1428

1429
static int decompress_startswith_bzip2(
266✔
1430
                const void *src,
1431
                uint64_t src_size,
1432
                void **buffer,
1433
                const void *prefix,
1434
                size_t prefix_len,
1435
                uint8_t extra) {
1436

1437
        assert(src);
266✔
1438
        assert(src_size > 0);
266✔
1439
        assert(buffer);
266✔
1440
        assert(prefix);
266✔
1441

1442
#if HAVE_BZIP2
1443
        int r;
266✔
1444

1445
        r = dlopen_bzip2(LOG_DEBUG);
266✔
1446
        if (r < 0)
266✔
1447
                return r;
266✔
1448

1449
        if (src_size > UINT_MAX)
266✔
1450
                return -EFBIG;
1451

1452
        _cleanup_(BZ2_bzDecompressEnd_wrapper) bz_stream s = {};
266✔
1453

1454
        r = sym_BZ2_bzDecompressInit(&s, /* verbosity= */ 0, /* small= */ 0);
266✔
1455
        if (r != BZ_OK)
266✔
1456
                return -EBADMSG;
1457

1458
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
266✔
1459
                return -ENOMEM;
1460

1461
        size_t allocated = MALLOC_SIZEOF_SAFE(*buffer);
266✔
1462

1463
        s.next_in = (char*) src;
266✔
1464
        s.avail_in = src_size;
266✔
1465

1466
        s.next_out = *buffer;
266✔
1467
        s.avail_out = MIN(allocated, (size_t) UINT_MAX);
266✔
1468

1469
        for (;;) {
266✔
1470
                r = sym_BZ2_bzDecompress(&s);
266✔
1471

1472
                if (!IN_SET(r, BZ_OK, BZ_STREAM_END))
266✔
1473
                        return -EBADMSG;
1474

1475
                if (allocated - s.avail_out >= prefix_len + 1)
266✔
1476
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
266✔
1477
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
266✔
1478

1479
                if (r == BZ_STREAM_END)
×
1480
                        return 0;
1481

1482
                size_t used = allocated - s.avail_out;
×
1483

1484
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
1485
                        return -ENOMEM;
1486

1487
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
1488
                s.avail_out = MIN(allocated - used, (size_t) UINT_MAX);
×
1489
                s.next_out = (char*) *buffer + used;
×
1490
        }
1491
#else
1492
        return -EPROTONOSUPPORT;
1493
#endif
1494
}
1495

1496
int decompress_startswith_journal(
792,340✔
1497
                Compression compression,
1498
                const void *src, uint64_t src_size,
1499
                void **buffer,
1500
                const void *prefix, size_t prefix_len,
1501
                uint8_t extra) {
1502

1503
        switch (compression) {
792,340✔
1504
        case COMPRESSION_XZ:
270✔
1505
                return decompress_startswith_xz(src, src_size, buffer, prefix, prefix_len, extra);
270✔
1506
        case COMPRESSION_LZ4:
270✔
1507
                return decompress_startswith_lz4(src, src_size, buffer, prefix, prefix_len, extra);
270✔
1508
        case COMPRESSION_ZSTD:
791,800✔
1509
                return decompress_startswith_zstd(src, src_size, buffer, prefix, prefix_len, extra);
791,800✔
1510
        default:
1511
                return -EOPNOTSUPP;
1512
        }
1513
}
1514

1515
int decompress_startswith(
1,333✔
1516
                Compression compression,
1517
                const void *src, uint64_t src_size,
1518
                void **buffer,
1519
                const void *prefix, size_t prefix_len,
1520
                uint8_t extra) {
1521

1522
        switch (compression) {
1,333✔
1523
        case COMPRESSION_GZIP:
266✔
1524
                return decompress_startswith_gzip(src, src_size, buffer, prefix, prefix_len, extra);
266✔
1525
        case COMPRESSION_BZIP2:
266✔
1526
                return decompress_startswith_bzip2(src, src_size, buffer, prefix, prefix_len, extra);
266✔
1527
        default:
801✔
1528
                return decompress_startswith_journal(compression, src, src_size, buffer, prefix, prefix_len, extra);
801✔
1529
        }
1530
}
1531

1532
int compress_stream(
51✔
1533
                Compression type,
1534
                int fdf, int fdt,
1535
                uint64_t max_bytes,
1536
                uint64_t *ret_uncompressed_size) {
1537

1538
        _cleanup_(compressor_freep) Compressor *c = NULL;
×
1539
        _cleanup_free_ void *buf = NULL;
×
1540
        _cleanup_free_ uint8_t *input = NULL;
51✔
1541
        size_t buf_size = 0, buf_alloc = 0;
51✔
1542
        uint64_t total_in = 0, total_out = 0;
51✔
1543
        int r;
51✔
1544

1545
        assert(fdf >= 0);
51✔
1546
        assert(fdt >= 0);
51✔
1547

1548
        r = compressor_new(&c, type);
51✔
1549
        if (r < 0)
51✔
1550
                return r;
1551

1552
        input = new(uint8_t, COMPRESS_PIPE_BUFFER_SIZE);
51✔
1553
        if (!input)
51✔
1554
                return -ENOMEM;
1555

1556
        for (;;) {
832✔
1557
                size_t m = COMPRESS_PIPE_BUFFER_SIZE;
832✔
1558
                ssize_t n;
832✔
1559

1560
                if (max_bytes != UINT64_MAX && (uint64_t) m > max_bytes)
832✔
1561
                        m = (size_t) max_bytes;
×
1562

1563
                n = read(fdf, input, m);
832✔
1564
                if (n < 0)
832✔
1565
                        return -errno;
×
1566

1567
                if (n == 0) {
832✔
1568
                        r = compressor_finish(c, &buf, &buf_size, &buf_alloc);
51✔
1569
                        if (r < 0)
51✔
1570
                                return r;
1571

1572
                        if (buf_size > 0) {
51✔
1573
                                r = loop_write(fdt, buf, buf_size);
51✔
1574
                                if (r < 0)
51✔
1575
                                        return r;
1576
                                total_out += buf_size;
51✔
1577
                        }
1578
                        break;
51✔
1579
                }
1580

1581
                total_in += n;
781✔
1582
                if (max_bytes != UINT64_MAX) {
781✔
1583
                        assert(max_bytes >= (uint64_t) n);
742✔
1584
                        max_bytes -= n;
742✔
1585
                }
1586

1587
                r = compressor_start(c, input, n, &buf, &buf_size, &buf_alloc);
781✔
1588
                if (r < 0)
781✔
1589
                        return r;
1590

1591
                if (buf_size > 0) {
781✔
1592
                        r = loop_write(fdt, buf, buf_size);
741✔
1593
                        if (r < 0)
741✔
1594
                                return r;
1595
                        total_out += buf_size;
741✔
1596
                }
1597
        }
1598

1599
        if (ret_uncompressed_size)
51✔
1600
                *ret_uncompressed_size = total_in;
46✔
1601

1602
        if (total_in == 0)
51✔
1603
                log_debug("%s compression finished (no input data)", compression_to_string(type));
×
1604
        else
1605
                log_debug("%s compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
51✔
1606
                          compression_to_string(type), total_in, total_out, (double) total_out / total_in * 100);
1607

1608
        return 0;
1609
}
1610

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

1616
        assert(fd >= 0);
41✔
1617

1618
        if (fstat(fd, &st) < 0)
41✔
1619
                return -errno;
×
1620

1621
        int flags = fcntl(fd, F_GETFL);
41✔
1622
        if (flags < 0)
41✔
1623
                return -errno;
×
1624

1625
        return S_ISREG(st.st_mode) && !FLAGS_SET(flags, O_APPEND);
41✔
1626
}
1627

1628
/* After sparse decompression, set the file size to the current position to account for
1629
 * trailing holes that sparse_write() created via lseek but never extended the file size for. */
1630
static int finalize_sparse(int fd) {
30✔
1631
        off_t pos;
30✔
1632

1633
        assert(fd >= 0);
30✔
1634

1635
        pos = lseek(fd, 0, SEEK_CUR);
30✔
1636
        if (pos < 0)
30✔
1637
                return -errno;
×
1638

1639
        if (ftruncate(fd, pos) < 0)
30✔
1640
                return -errno;
×
1641

1642
        return 0;
1643
}
1644

1645
/* Common helper for decompress_stream_*() wrappers */
1646

1647
struct decompress_stream_userdata {
1648
        int fd;
1649
        uint64_t max_bytes;
1650
        uint64_t total_out;
1651
        bool sparse;
1652
};
1653

1654
static int decompress_stream_write_callback(const void *data, size_t size, void *userdata) {
248✔
1655
        struct decompress_stream_userdata *u = ASSERT_PTR(userdata);
248✔
1656

1657
        if (u->max_bytes != UINT64_MAX) {
248✔
1658
                if (u->max_bytes < size)
30✔
1659
                        return -EFBIG;
1660
                u->max_bytes -= size;
25✔
1661
        }
1662

1663
        u->total_out += size;
243✔
1664

1665
        if (u->sparse) {
243✔
1666
                /* Note: sparse_write() does not retry on EINTR and converts short writes to -EIO.
1667
                 * This is fine here since sparse mode is only used on regular files, where short
1668
                 * writes and EINTR are not expected in practice. */
1669
                ssize_t k = sparse_write(u->fd, data, size, 64);
242✔
1670
                if (k < 0)
242✔
1671
                        return (int) k;
×
1672
                return 0;
1673
        }
1674

1675
        return loop_write(u->fd, data, size);
1✔
1676
}
1677

1678
static int decompressor_new(Decompressor **ret, Compression type) {
41✔
1679
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1680
        int r;
41✔
1681
#endif
1682

1683
        assert(ret);
41✔
1684

1685
        _cleanup_(compressor_freep) Decompressor *c = new0(Decompressor, 1);
41✔
1686
        if (!c)
41✔
1687
                return -ENOMEM;
1688

1689
        c->type = _COMPRESSION_INVALID;
41✔
1690

1691
        switch (type) {
41✔
1692

1693
#if HAVE_XZ
1694
        case COMPRESSION_XZ:
6✔
1695
                r = dlopen_xz(LOG_DEBUG);
6✔
1696
                if (r < 0)
6✔
1697
                        return r;
1698

1699
                if (sym_lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED) != LZMA_OK)
6✔
1700
                        return -EIO;
1701
                break;
1702
#endif
1703

1704
#if HAVE_LZ4
1705
        case COMPRESSION_LZ4: {
6✔
1706
                r = dlopen_lz4(LOG_DEBUG);
6✔
1707
                if (r < 0)
6✔
1708
                        return r;
1709

1710
                size_t rc = sym_LZ4F_createDecompressionContext(&c->d_lz4, LZ4F_VERSION);
6✔
1711
                if (sym_LZ4F_isError(rc))
6✔
1712
                        return -ENOMEM;
1713

1714
                break;
1715
        }
1716
#endif
1717

1718
#if HAVE_ZSTD
1719
        case COMPRESSION_ZSTD:
16✔
1720
                r = dlopen_zstd(LOG_DEBUG);
16✔
1721
                if (r < 0)
16✔
1722
                        return r;
1723

1724
                c->d_zstd = sym_ZSTD_createDCtx();
16✔
1725
                if (!c->d_zstd)
16✔
1726
                        return -ENOMEM;
1727
                break;
1728
#endif
1729

1730
#if HAVE_ZLIB
1731
        case COMPRESSION_GZIP:
7✔
1732
                r = dlopen_zlib(LOG_DEBUG);
7✔
1733
                if (r < 0)
7✔
1734
                        return r;
1735

1736
                r = sym_inflateInit2_(&c->gzip, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(c->gzip));
7✔
1737
                if (r != Z_OK)
7✔
1738
                        return -EIO;
1739
                break;
1740
#endif
1741

1742
#if HAVE_BZIP2
1743
        case COMPRESSION_BZIP2:
6✔
1744
                r = dlopen_bzip2(LOG_DEBUG);
6✔
1745
                if (r < 0)
6✔
1746
                        return r;
1747

1748
                r = sym_BZ2_bzDecompressInit(&c->bzip2, /* verbosity= */ 0, /* small= */ 0);
6✔
1749
                if (r != BZ_OK)
6✔
1750
                        return -EIO;
1751
                break;
1752
#endif
1753

1754
        default:
1755
                return -EOPNOTSUPP;
1756
        }
1757

1758
        c->type = type;
41✔
1759
        c->encoding = false;
41✔
1760
        *ret = TAKE_PTR(c);
41✔
1761
        return 0;
41✔
1762
}
1763

1764
int decompress_stream(
41✔
1765
                Compression type,
1766
                int fdf, int fdt,
1767
                uint64_t max_bytes) {
1768

1769
        _cleanup_(compressor_freep) Decompressor *c = NULL;
×
1770
        _cleanup_free_ uint8_t *buf = NULL;
41✔
1771
        uint64_t total_in = 0;
41✔
1772
        int r;
41✔
1773

1774
        assert(fdf >= 0);
41✔
1775
        assert(fdt >= 0);
41✔
1776

1777
        r = decompressor_new(&c, type);
41✔
1778
        if (r < 0)
41✔
1779
                return r;
1780

1781
        struct decompress_stream_userdata userdata = {
82✔
1782
                .fd = fdt,
1783
                .max_bytes = max_bytes,
1784
                .sparse = should_sparse(fdt) > 0,
41✔
1785
        };
1786

1787
        buf = new(uint8_t, COMPRESS_PIPE_BUFFER_SIZE);
41✔
1788
        if (!buf)
41✔
1789
                return -ENOMEM;
1790

1791
        for (;;) {
89✔
1792
                ssize_t n;
89✔
1793

1794
                n = read(fdf, buf, COMPRESS_PIPE_BUFFER_SIZE);
89✔
1795
                if (n < 0)
89✔
1796
                        return -errno;
×
1797
                if (n == 0)
89✔
1798
                        break;
1799

1800
                total_in += n;
59✔
1801

1802
                r = decompressor_push(c, buf, n, decompress_stream_write_callback, &userdata);
59✔
1803
                if (r < 0)
59✔
1804
                        return r;
1805
        }
1806

1807
        if (total_in == 0)
30✔
1808
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%s decompression failed: no data read",
×
1809
                                       compression_to_string(type));
1810

1811
        if (userdata.sparse) {
30✔
1812
                r = finalize_sparse(fdt);
30✔
1813
                if (r < 0)
30✔
1814
                        return r;
1815
        }
1816

1817
        log_debug("%s decompression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
30✔
1818
                  compression_to_string(type), total_in, userdata.total_out,
1819
                  (double) userdata.total_out / total_in * 100);
1820

1821
        return 0;
1822
}
1823

1824
int decompress_stream_by_filename(const char *filename, int fdf, int fdt, uint64_t max_bytes) {
10✔
1825
        Compression c = compression_from_filename(filename);
10✔
1826
        if (c == COMPRESSION_NONE)
10✔
1827
                return -EPROTONOSUPPORT;
1828

1829
        return decompress_stream(c, fdf, fdt, max_bytes);
10✔
1830
}
1831

1832
/* Push-based streaming compression/decompression context API */
1833

1834
Compressor* compressor_free(Compressor *c) {
1,533✔
1835
        if (!c)
1,533✔
1836
                return NULL;
1837

1838
        switch (c->type) {
1,342✔
1839

1840
#if HAVE_XZ
1841
        case COMPRESSION_XZ:
12✔
1842
                sym_lzma_end(&c->xz);
12✔
1843
                break;
12✔
1844
#endif
1845

1846
#if HAVE_LZ4
1847
        case COMPRESSION_LZ4:
12✔
1848
                if (c->encoding) {
12✔
1849
                        sym_LZ4F_freeCompressionContext(c->c_lz4);
5✔
1850
                        c->c_lz4 = NULL;
5✔
1851
                        c->lz4_header = mfree(c->lz4_header);
5✔
1852
                } else {
1853
                        sym_LZ4F_freeDecompressionContext(c->d_lz4);
7✔
1854
                        c->d_lz4 = NULL;
7✔
1855
                }
1856
                break;
1857
#endif
1858

1859
#if HAVE_ZSTD
1860
        case COMPRESSION_ZSTD:
53✔
1861
                if (c->encoding) {
53✔
1862
                        sym_ZSTD_freeCCtx(c->c_zstd);
35✔
1863
                        c->c_zstd = NULL;
35✔
1864
                } else {
1865
                        sym_ZSTD_freeDCtx(c->d_zstd);
18✔
1866
                        c->d_zstd = NULL;
18✔
1867
                }
1868
                break;
1869
#endif
1870

1871
#if HAVE_ZLIB
1872
        case COMPRESSION_GZIP:
229✔
1873
                if (c->encoding)
229✔
1874
                        sym_deflateEnd(&c->gzip);
14✔
1875
                else
1876
                        sym_inflateEnd(&c->gzip);
215✔
1877
                break;
1878
#endif
1879

1880
#if HAVE_BZIP2
1881
        case COMPRESSION_BZIP2:
12✔
1882
                if (c->encoding)
12✔
1883
                        sym_BZ2_bzCompressEnd(&c->bzip2);
5✔
1884
                else
1885
                        sym_BZ2_bzDecompressEnd(&c->bzip2);
7✔
1886
                break;
1887
#endif
1888

1889
        default:
1890
                break;
1891
        }
1892

1893
        return mfree(c);
1,342✔
1894
}
1895

1896
Compression compressor_type(const Compressor *c) {
4,416✔
1897
        return c ? c->type : _COMPRESSION_INVALID;
4,416✔
1898
}
1899

1900
int decompressor_detect(Decompressor **ret, const void *data, size_t size) {
1,230✔
1901
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1902
        int r;
1,230✔
1903
#endif
1904

1905
        assert(ret);
1,230✔
1906

1907
        if (*ret)
1,230✔
1908
                return 1;
1,230✔
1909

1910
        if (size < COMPRESSION_MAGIC_BYTES_MAX)
1,230✔
1911
                return 0;
1912

1913
        assert(data);
1,056✔
1914

1915
        Compression type = compression_detect_from_magic(data);
1,056✔
1916

1917
        _cleanup_(compressor_freep) Decompressor *c = new0(Decompressor, 1);
1,056✔
1918
        if (!c)
1,056✔
1919
                return -ENOMEM;
1920

1921
        switch (type) {
1,056✔
1922

1923
#if HAVE_XZ
1924
        case COMPRESSION_XZ: {
1✔
1925
                r = dlopen_xz(LOG_DEBUG);
1✔
1926
                if (r < 0)
1✔
1927
                        return r;
1928

1929
                lzma_ret xzr = sym_lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED);
1✔
1930
                if (xzr != LZMA_OK)
1✔
1931
                        return -EIO;
1932

1933
                break;
1934
        }
1935
#endif
1936

1937
#if HAVE_LZ4
1938
        case COMPRESSION_LZ4: {
1✔
1939
                r = dlopen_lz4(LOG_DEBUG);
1✔
1940
                if (r < 0)
1✔
1941
                        return r;
1942

1943
                size_t rc = sym_LZ4F_createDecompressionContext(&c->d_lz4, LZ4F_VERSION);
1✔
1944
                if (sym_LZ4F_isError(rc))
1✔
1945
                        return -ENOMEM;
1946

1947
                break;
1948
        }
1949
#endif
1950

1951
#if HAVE_ZSTD
1952
        case COMPRESSION_ZSTD: {
2✔
1953
                r = dlopen_zstd(LOG_DEBUG);
2✔
1954
                if (r < 0)
2✔
1955
                        return r;
1956

1957
                c->d_zstd = sym_ZSTD_createDCtx();
2✔
1958
                if (!c->d_zstd)
2✔
1959
                        return -ENOMEM;
1960

1961
                break;
1962
        }
1963
#endif
1964

1965
#if HAVE_ZLIB
1966
        case COMPRESSION_GZIP: {
208✔
1967
                r = dlopen_zlib(LOG_DEBUG);
208✔
1968
                if (r < 0)
208✔
1969
                        return r;
1970

1971
                r = sym_inflateInit2_(&c->gzip, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(c->gzip));
208✔
1972
                if (r != Z_OK)
208✔
1973
                        return -EIO;
1974

1975
                break;
1976
        }
1977
#endif
1978

1979
#if HAVE_BZIP2
1980
        case COMPRESSION_BZIP2: {
1✔
1981
                r = dlopen_bzip2(LOG_DEBUG);
1✔
1982
                if (r < 0)
1✔
1983
                        return r;
1984

1985
                r = sym_BZ2_bzDecompressInit(&c->bzip2, /* verbosity= */ 0, /* small= */ 0);
1✔
1986
                if (r != BZ_OK)
1✔
1987
                        return -EIO;
1988

1989
                break;
1990
        }
1991
#endif
1992

1993
        default:
843✔
1994
                if (type != _COMPRESSION_INVALID)
843✔
1995
                        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
1996
                                               "Detected %s compression, but support is not compiled in.",
1997
                                               compression_to_string(type));
1998
                type = COMPRESSION_NONE;
1999
                break;
2000
        }
2001

2002
        c->type = type;
1,056✔
2003
        c->encoding = false;
1,056✔
2004

2005
        log_debug("Detected compression type: %s", compression_to_string(c->type));
1,056✔
2006
        *ret = TAKE_PTR(c);
1,056✔
2007
        return 1;
1,056✔
2008
}
2009

2010
int decompressor_force_off(Decompressor **ret) {
174✔
2011
        assert(ret);
174✔
2012

2013
        *ret = compressor_free(*ret);
174✔
2014

2015
        Decompressor *c = new0(Decompressor, 1);
174✔
2016
        if (!c)
174✔
2017
                return -ENOMEM;
2018

2019
        c->type = COMPRESSION_NONE;
174✔
2020
        c->encoding = false;
174✔
2021
        *ret = c;
174✔
2022
        return 0;
174✔
2023
}
2024

2025
int decompressor_push(Decompressor *c, const void *data, size_t size, DecompressorCallback callback, void *userdata) {
43,636✔
2026
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2027
        _cleanup_free_ uint8_t *buffer = NULL;
43,636✔
2028
#endif
2029
        int r;
43,636✔
2030

2031
        assert(c);
43,636✔
2032
        assert(callback);
43,636✔
2033

2034
        if (c->encoding)
43,636✔
2035
                return -EINVAL;
2036

2037
        if (size == 0)
43,636✔
2038
                return 1;
2039

2040
        assert(data);
43,014✔
2041

2042
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2043
        if (c->type != COMPRESSION_NONE) {
43,014✔
2044
                buffer = new(uint8_t, COMPRESS_PIPE_BUFFER_SIZE);
30,774✔
2045
                if (!buffer)
30,774✔
2046
                        return -ENOMEM;
2047
        }
2048
#endif
2049

2050
        switch (c->type) {
43,014✔
2051

2052
        case COMPRESSION_NONE:
12,240✔
2053
                r = callback(data, size, userdata);
12,240✔
2054
                if (r < 0)
12,240✔
2055
                        return r;
×
2056

2057
                break;
2058

2059
#if HAVE_XZ
2060
        case COMPRESSION_XZ:
7✔
2061
                c->xz.next_in = data;
7✔
2062
                c->xz.avail_in = size;
7✔
2063

2064
                while (c->xz.avail_in > 0) {
20✔
2065
                        c->xz.next_out = buffer;
8✔
2066
                        c->xz.avail_out = COMPRESS_PIPE_BUFFER_SIZE;
8✔
2067

2068
                        lzma_ret lzr = sym_lzma_code(&c->xz, LZMA_RUN);
8✔
2069
                        if (!IN_SET(lzr, LZMA_OK, LZMA_STREAM_END))
8✔
2070
                                return -EBADMSG;
2071

2072
                        if (c->xz.avail_out < COMPRESS_PIPE_BUFFER_SIZE) {
7✔
2073
                                r = callback(buffer, COMPRESS_PIPE_BUFFER_SIZE - c->xz.avail_out, userdata);
7✔
2074
                                if (r < 0)
7✔
2075
                                        return r;
2076
                        }
2077
                }
2078

2079
                break;
2080
#endif
2081

2082
#if HAVE_LZ4
2083
        case COMPRESSION_LZ4: {
2084
                const uint8_t *src = data;
2085
                size_t src_remaining = size;
2086

2087
                while (src_remaining > 0) {
13✔
2088
                        size_t produced = COMPRESS_PIPE_BUFFER_SIZE;
8✔
2089
                        size_t consumed = src_remaining;
8✔
2090

2091
                        size_t rc = sym_LZ4F_decompress(c->d_lz4, buffer, &produced, src, &consumed, NULL);
8✔
2092
                        if (sym_LZ4F_isError(rc))
8✔
2093
                                return -EBADMSG;
2✔
2094

2095
                        if (consumed == 0 && produced == 0)
7✔
2096
                                break; /* No progress possible with current input */
2097

2098
                        src += consumed;
7✔
2099
                        src_remaining -= consumed;
7✔
2100

2101
                        if (produced > 0) {
7✔
2102
                                r = callback(buffer, produced, userdata);
7✔
2103
                                if (r < 0)
7✔
2104
                                        return r;
2105
                        }
2106
                }
2107

2108
                break;
2109
        }
2110
#endif
2111

2112
#if HAVE_ZSTD
2113
        case COMPRESSION_ZSTD: {
36✔
2114
                ZSTD_inBuffer input = {
36✔
2115
                        .src =  (void*) data,
2116
                        .size = size,
2117
                };
2118

2119
                while (input.pos < input.size) {
259✔
2120
                        ZSTD_outBuffer output = {
226✔
2121
                                .dst = buffer,
2122
                                .size = COMPRESS_PIPE_BUFFER_SIZE,
2123
                        };
2124

2125
                        size_t res = sym_ZSTD_decompressStream(c->d_zstd, &output, &input);
226✔
2126
                        if (sym_ZSTD_isError(res))
226✔
2127
                                return -EBADMSG;
3✔
2128

2129
                        if (output.pos > 0) {
225✔
2130
                                r = callback(output.dst, output.pos, userdata);
225✔
2131
                                if (r < 0)
225✔
2132
                                        return r;
2133
                        }
2134
                }
2135

2136
                break;
33✔
2137
        }
2138
#endif
2139

2140
#if HAVE_ZLIB
2141
        case COMPRESSION_GZIP:
30,717✔
2142
                if (size > UINT_MAX)
30,717✔
2143
                        return -EFBIG;
2144

2145
                c->gzip.next_in = (void*) data;
30,717✔
2146
                c->gzip.avail_in = size;
30,717✔
2147

2148
                while (c->gzip.avail_in > 0) {
64,858✔
2149
                        c->gzip.next_out = buffer;
34,356✔
2150
                        c->gzip.avail_out = COMPRESS_PIPE_BUFFER_SIZE;
34,356✔
2151

2152
                        int zr = sym_inflate(&c->gzip, Z_NO_FLUSH);
34,356✔
2153
                        if (!IN_SET(zr, Z_OK, Z_STREAM_END))
34,356✔
2154
                                return -EBADMSG;
2155

2156
                        if (c->gzip.avail_out < COMPRESS_PIPE_BUFFER_SIZE) {
34,355✔
2157
                                r = callback(buffer, COMPRESS_PIPE_BUFFER_SIZE - c->gzip.avail_out, userdata);
34,355✔
2158
                                if (r < 0)
34,355✔
2159
                                        return r;
2160
                        }
2161

2162
                        if (zr == Z_STREAM_END)
34,354✔
2163
                                break;
2164
                }
2165

2166
                break;
2167
#endif
2168

2169
#if HAVE_BZIP2
2170
        case COMPRESSION_BZIP2:
7✔
2171
                if (size > UINT_MAX)
7✔
2172
                        return -EFBIG;
2173

2174
                c->bzip2.next_in = (char*) data;
7✔
2175
                c->bzip2.avail_in = size;
7✔
2176

2177
                while (c->bzip2.avail_in > 0) {
8✔
2178
                        c->bzip2.next_out = (char*) buffer;
8✔
2179
                        c->bzip2.avail_out = COMPRESS_PIPE_BUFFER_SIZE;
8✔
2180

2181
                        int bzr = sym_BZ2_bzDecompress(&c->bzip2);
8✔
2182
                        if (!IN_SET(bzr, BZ_OK, BZ_STREAM_END))
8✔
2183
                                return -EBADMSG;
2184

2185
                        if (c->bzip2.avail_out < COMPRESS_PIPE_BUFFER_SIZE) {
7✔
2186
                                r = callback(buffer, COMPRESS_PIPE_BUFFER_SIZE - c->bzip2.avail_out, userdata);
7✔
2187
                                if (r < 0)
7✔
2188
                                        return r;
2189
                        }
2190

2191
                        if (bzr == BZ_STREAM_END)
6✔
2192
                                break;
2193
                }
2194

2195
                break;
2196
#endif
2197

2198
        default:
×
2199
                assert_not_reached();
×
2200
        }
2201

2202
        return 1;
2203
}
2204

2205
int compressor_new(Compressor **ret, Compression type) {
71✔
2206
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2207
        int r;
71✔
2208
#endif
2209

2210
        assert(ret);
71✔
2211

2212
        _cleanup_(compressor_freep) Compressor *c = new0(Compressor, 1);
71✔
2213
        if (!c)
71✔
2214
                return -ENOMEM;
2215

2216
        c->type = _COMPRESSION_INVALID;
71✔
2217
        /* Set encoding early so that compressor_freep calls the correct cleanup (compression vs
2218
         * decompression) if any operation in the switch fails after setting c->type. This is safe
2219
         * because _COMPRESSION_INVALID hits the default: break case regardless of the encoding flag. */
2220
        c->encoding = true;
71✔
2221

2222
        switch (type) {
71✔
2223

2224
#if HAVE_XZ
2225
        case COMPRESSION_XZ: {
5✔
2226
                r = dlopen_xz(LOG_DEBUG);
5✔
2227
                if (r < 0)
5✔
2228
                        return r;
2229

2230
                lzma_ret xzr = sym_lzma_easy_encoder(&c->xz, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
5✔
2231
                if (xzr != LZMA_OK)
5✔
2232
                        return -EIO;
2233

2234
                c->type = COMPRESSION_XZ;
5✔
2235
                break;
5✔
2236
        }
2237
#endif
2238

2239
#if HAVE_LZ4
2240
        case COMPRESSION_LZ4: {
5✔
2241
                r = dlopen_lz4(LOG_DEBUG);
5✔
2242
                if (r < 0)
5✔
2243
                        return r;
2244

2245
                size_t rc = sym_LZ4F_createCompressionContext(&c->c_lz4, LZ4F_VERSION);
5✔
2246
                if (sym_LZ4F_isError(rc))
5✔
2247
                        return -ENOMEM;
2248

2249
                c->type = COMPRESSION_LZ4;
5✔
2250

2251
                /* Generate the frame header and stash it for the first compressor_start call */
2252
                size_t header_bound = sym_LZ4F_compressBound(0, &lz4_preferences);
5✔
2253
                c->lz4_header = malloc(header_bound);
5✔
2254
                if (!c->lz4_header)
5✔
2255
                        return -ENOMEM;
2256

2257
                c->lz4_header_size = sym_LZ4F_compressBegin(c->c_lz4, c->lz4_header, header_bound, &lz4_preferences);
5✔
2258
                if (sym_LZ4F_isError(c->lz4_header_size))
5✔
2259
                        return -EINVAL;
2260

2261
                break;
2262
        }
2263
#endif
2264

2265
#if HAVE_ZSTD
2266
        case COMPRESSION_ZSTD:
35✔
2267
                r = dlopen_zstd(LOG_DEBUG);
35✔
2268
                if (r < 0)
35✔
2269
                        return r;
2270

2271
                c->c_zstd = sym_ZSTD_createCCtx();
35✔
2272
                if (!c->c_zstd)
35✔
2273
                        return -ENOMEM;
2274

2275
                c->type = COMPRESSION_ZSTD;
35✔
2276

2277
                size_t z = sym_ZSTD_CCtx_setParameter(c->c_zstd, ZSTD_c_compressionLevel, ZSTD_CLEVEL_DEFAULT);
35✔
2278
                if (sym_ZSTD_isError(z))
35✔
2279
                        return -EIO;
2280

2281
                z = sym_ZSTD_CCtx_setParameter(c->c_zstd, ZSTD_c_checksumFlag, /* enable= */ 1);
35✔
2282
                if (sym_ZSTD_isError(z))
35✔
2283
                        log_debug("Failed to enable ZSTD checksum, ignoring: %s", sym_ZSTD_getErrorName(z));
×
2284

2285
                break;
2286
#endif
2287

2288
#if HAVE_ZLIB
2289
        case COMPRESSION_GZIP:
14✔
2290
                r = dlopen_zlib(LOG_DEBUG);
14✔
2291
                if (r < 0)
14✔
2292
                        return r;
2293

2294
                r = sym_deflateInit2_(&c->gzip,
14✔
2295
                                      Z_DEFAULT_COMPRESSION,
2296
                                      /* method= */ Z_DEFLATED,
2297
                                      /* windowBits= */ ZLIB_WBITS_GZIP,
2298
                                      /* memLevel= */ 8,
2299
                                      /* strategy= */ Z_DEFAULT_STRATEGY,
2300
                                      ZLIB_VERSION, (int) sizeof(c->gzip));
2301
                if (r != Z_OK)
14✔
2302
                        return -EIO;
2303

2304
                c->type = COMPRESSION_GZIP;
14✔
2305
                break;
14✔
2306
#endif
2307

2308
#if HAVE_BZIP2
2309
        case COMPRESSION_BZIP2:
5✔
2310
                r = dlopen_bzip2(LOG_DEBUG);
5✔
2311
                if (r < 0)
5✔
2312
                        return r;
2313

2314
                r = sym_BZ2_bzCompressInit(&c->bzip2, /* blockSize100k= */ 9, /* verbosity= */ 0, /* workFactor= */ 0);
5✔
2315
                if (r != BZ_OK)
5✔
2316
                        return -EIO;
2317

2318
                c->type = COMPRESSION_BZIP2;
5✔
2319
                break;
5✔
2320
#endif
2321

2322
        case COMPRESSION_NONE:
7✔
2323
                c->type = COMPRESSION_NONE;
7✔
2324
                break;
7✔
2325

2326
        default:
2327
                return -EOPNOTSUPP;
2328
        }
2329

2330
        *ret = TAKE_PTR(c);
71✔
2331
        return 0;
71✔
2332
}
2333

2334
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2335
static int enlarge_buffer(void **buffer, size_t *buffer_size, size_t *buffer_allocated, size_t need) {
5,031✔
2336
        assert(buffer);
5,031✔
2337
        assert(buffer_size);
5,031✔
2338
        assert(buffer_allocated);
5,031✔
2339

2340
        need = MAX3(need, *buffer_size + 1, (size_t) COMPRESS_PIPE_BUFFER_SIZE);
5,031✔
2341
        if (*buffer_allocated >= need)
5,031✔
2342
                return 0;
2343

2344
        if (!greedy_realloc(buffer, need, 1))
74✔
2345
                return -ENOMEM;
2346

2347
        *buffer_allocated = MALLOC_SIZEOF_SAFE(*buffer);
74✔
2348
        return 1;
74✔
2349
}
2350
#endif
2351

2352
int compressor_start(
5,004✔
2353
                Compressor *c,
2354
                const void *data,
2355
                size_t size,
2356
                void **buffer,
2357
                size_t *buffer_size,
2358
                size_t *buffer_allocated) {
2359

2360
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2361
        int r;
5,004✔
2362
#endif
2363

2364
        assert(c);
5,004✔
2365
        assert(buffer);
5,004✔
2366
        assert(buffer_size);
5,004✔
2367
        assert(buffer_allocated);
5,004✔
2368

2369
        if (!c->encoding)
5,004✔
2370
                return -EINVAL;
2371

2372
        if (size == 0)
5,004✔
2373
                return 0;
2374

2375
        assert(data);
5,004✔
2376

2377
        *buffer_size = 0;
5,004✔
2378

2379
        switch (c->type) {
5,004✔
2380

2381
#if HAVE_XZ
2382
        case COMPRESSION_XZ:
6✔
2383

2384
                c->xz.next_in = data;
6✔
2385
                c->xz.avail_in = size;
6✔
2386

2387
                while (c->xz.avail_in > 0) {
12✔
2388
                        lzma_ret lzr;
6✔
2389

2390
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
6✔
2391
                        if (r < 0)
6✔
2392
                                return r;
2393

2394
                        c->xz.next_out = (uint8_t*) *buffer + *buffer_size;
6✔
2395
                        c->xz.avail_out = *buffer_allocated - *buffer_size;
6✔
2396

2397
                        lzr = sym_lzma_code(&c->xz, LZMA_RUN);
6✔
2398
                        if (lzr != LZMA_OK)
6✔
2399
                                return -EIO;
2400

2401
                        *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out;
6✔
2402
                }
2403

2404
                break;
2405
#endif
2406

2407
#if HAVE_LZ4
2408
        case COMPRESSION_LZ4: {
6✔
2409
                /* Prepend any stashed frame header from compressor_new */
2410
                if (c->lz4_header_size > 0) {
6✔
2411
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, c->lz4_header_size);
5✔
2412
                        if (r < 0)
5✔
2413
                                return r;
2414

2415
                        memcpy(*buffer, c->lz4_header, c->lz4_header_size);
5✔
2416
                        *buffer_size = c->lz4_header_size;
5✔
2417
                        c->lz4_header = mfree(c->lz4_header);
5✔
2418
                        c->lz4_header_size = 0;
5✔
2419
                }
2420

2421
                size_t bound = sym_LZ4F_compressBound(size, &lz4_preferences);
6✔
2422
                r = enlarge_buffer(buffer, buffer_size, buffer_allocated, *buffer_size + bound);
6✔
2423
                if (r < 0)
6✔
2424
                        return r;
2425

2426
                size_t n = sym_LZ4F_compressUpdate(c->c_lz4,
12✔
2427
                                                   (uint8_t*) *buffer + *buffer_size,
6✔
2428
                                                   *buffer_allocated - *buffer_size,
6✔
2429
                                                   data, size, NULL);
2430
                if (sym_LZ4F_isError(n))
6✔
2431
                        return -EIO;
2432

2433
                *buffer_size += n;
6✔
2434
                break;
6✔
2435
        }
2436
#endif
2437

2438
#if HAVE_ZSTD
2439
        case COMPRESSION_ZSTD: {
761✔
2440
                ZSTD_inBuffer input = {
761✔
2441
                        .src = data,
2442
                        .size = size,
2443
                };
2444

2445
                while (input.pos < input.size) {
1,522✔
2446
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
761✔
2447
                        if (r < 0)
761✔
2448
                                return r;
×
2449

2450
                        ZSTD_outBuffer output = {
761✔
2451
                                .dst = ((uint8_t *) *buffer + *buffer_size),
761✔
2452
                                .size = *buffer_allocated - *buffer_size,
761✔
2453
                        };
2454

2455
                        size_t res = sym_ZSTD_compressStream2(c->c_zstd, &output, &input, ZSTD_e_continue);
761✔
2456
                        if (sym_ZSTD_isError(res))
761✔
2457
                                return -EIO;
2458

2459
                        *buffer_size += output.pos;
761✔
2460
                }
2461

2462
                break;
761✔
2463
        }
2464
#endif
2465

2466
#if HAVE_ZLIB
2467
        case COMPRESSION_GZIP:
4,183✔
2468
                if (size > UINT_MAX)
4,183✔
2469
                        return -EFBIG;
2470

2471
                c->gzip.next_in = (void*) data;
4,183✔
2472
                c->gzip.avail_in = size;
4,183✔
2473

2474
                while (c->gzip.avail_in > 0) {
8,366✔
2475
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
4,183✔
2476
                        if (r < 0)
4,183✔
2477
                                return r;
2478

2479
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
4,183✔
2480
                        c->gzip.next_out = (uint8_t*) *buffer + *buffer_size;
4,183✔
2481
                        c->gzip.avail_out = avail;
4,183✔
2482

2483
                        r = sym_deflate(&c->gzip, Z_NO_FLUSH);
4,183✔
2484
                        if (r != Z_OK)
4,183✔
2485
                                return -EIO;
2486

2487
                        *buffer_size += avail - c->gzip.avail_out;
4,183✔
2488
                }
2489

2490
                break;
2491
#endif
2492

2493
#if HAVE_BZIP2
2494
        case COMPRESSION_BZIP2:
6✔
2495
                if (size > UINT_MAX)
6✔
2496
                        return -EFBIG;
2497

2498
                c->bzip2.next_in = (void*) data;
6✔
2499
                c->bzip2.avail_in = size;
6✔
2500

2501
                while (c->bzip2.avail_in > 0) {
12✔
2502
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
6✔
2503
                        if (r < 0)
6✔
2504
                                return r;
2505

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

2510
                        r = sym_BZ2_bzCompress(&c->bzip2, BZ_RUN);
6✔
2511
                        if (r != BZ_RUN_OK)
6✔
2512
                                return -EIO;
2513

2514
                        *buffer_size += avail - c->bzip2.avail_out;
6✔
2515
                }
2516

2517
                break;
2518
#endif
2519

2520
        case COMPRESSION_NONE:
42✔
2521

2522
                if (*buffer_allocated < size) {
42✔
2523
                        void *p;
3✔
2524

2525
                        p = realloc(*buffer, size);
3✔
2526
                        if (!p)
3✔
2527
                                return -ENOMEM;
2528

2529
                        *buffer = p;
3✔
2530
                        *buffer_allocated = size;
3✔
2531
                }
2532

2533
                memcpy(*buffer, data, size);
42✔
2534
                *buffer_size = size;
42✔
2535
                break;
42✔
2536

2537
        default:
2538
                return -EOPNOTSUPP;
2539
        }
2540

2541
        return 0;
2542
}
2543

2544
int compressor_finish(Compressor *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated) {
66✔
2545
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2546
        int r;
66✔
2547
#endif
2548

2549
        assert(c);
66✔
2550
        assert(buffer);
66✔
2551
        assert(buffer_size);
66✔
2552
        assert(buffer_allocated);
66✔
2553

2554
        if (!c->encoding)
66✔
2555
                return -EINVAL;
2556

2557
        *buffer_size = 0;
66✔
2558

2559
        switch (c->type) {
66✔
2560

2561
#if HAVE_XZ
2562
        case COMPRESSION_XZ: {
5✔
2563
                lzma_ret lzr;
5✔
2564

2565
                c->xz.avail_in = 0;
5✔
2566

2567
                do {
5✔
2568
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
5✔
2569
                        if (r < 0)
5✔
2570
                                return r;
2571

2572
                        c->xz.next_out = (uint8_t*) *buffer + *buffer_size;
5✔
2573
                        c->xz.avail_out = *buffer_allocated - *buffer_size;
5✔
2574

2575
                        lzr = sym_lzma_code(&c->xz, LZMA_FINISH);
5✔
2576
                        if (!IN_SET(lzr, LZMA_OK, LZMA_STREAM_END))
5✔
2577
                                return -EIO;
2578

2579
                        *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out;
5✔
2580
                } while (lzr != LZMA_STREAM_END);
5✔
2581

2582
                break;
2583
        }
2584
#endif
2585

2586
#if HAVE_LZ4
2587
        case COMPRESSION_LZ4: {
5✔
2588
                size_t bound = sym_LZ4F_compressBound(0, &lz4_preferences);
5✔
2589
                r = enlarge_buffer(buffer, buffer_size, buffer_allocated, bound);
5✔
2590
                if (r < 0)
5✔
2591
                        return r;
2592

2593
                size_t n = sym_LZ4F_compressEnd(c->c_lz4, *buffer, *buffer_allocated, NULL);
5✔
2594
                if (sym_LZ4F_isError(n))
5✔
2595
                        return -EIO;
2596

2597
                *buffer_size = n;
5✔
2598
                break;
5✔
2599
        }
2600
#endif
2601

2602
#if HAVE_ZSTD
2603
        case COMPRESSION_ZSTD: {
35✔
2604
                ZSTD_inBuffer input = {};
35✔
2605
                size_t res;
35✔
2606

2607
                do {
35✔
2608
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
35✔
2609
                        if (r < 0)
35✔
2610
                                return r;
×
2611

2612
                        ZSTD_outBuffer output = {
35✔
2613
                                .dst = ((uint8_t *) *buffer + *buffer_size),
35✔
2614
                                .size = *buffer_allocated - *buffer_size,
35✔
2615
                        };
2616

2617
                        res = sym_ZSTD_compressStream2(c->c_zstd, &output, &input, ZSTD_e_end);
35✔
2618
                        if (sym_ZSTD_isError(res))
35✔
2619
                                return -EIO;
2620

2621
                        *buffer_size += output.pos;
35✔
2622
                } while (res != 0);
35✔
2623

2624
                break;
35✔
2625
        }
2626
#endif
2627

2628
#if HAVE_ZLIB
2629
        case COMPRESSION_GZIP:
14✔
2630
                c->gzip.avail_in = 0;
14✔
2631

2632
                do {
14✔
2633
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
14✔
2634
                        if (r < 0)
14✔
2635
                                return r;
2636

2637
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
14✔
2638
                        c->gzip.next_out = (uint8_t*) *buffer + *buffer_size;
14✔
2639
                        c->gzip.avail_out = avail;
14✔
2640

2641
                        r = sym_deflate(&c->gzip, Z_FINISH);
14✔
2642
                        if (!IN_SET(r, Z_OK, Z_STREAM_END))
14✔
2643
                                return -EIO;
2644

2645
                        *buffer_size += avail - c->gzip.avail_out;
14✔
2646
                } while (r != Z_STREAM_END);
14✔
2647

2648
                break;
2649
#endif
2650

2651
#if HAVE_BZIP2
2652
        case COMPRESSION_BZIP2:
5✔
2653
                c->bzip2.avail_in = 0;
5✔
2654

2655
                do {
5✔
2656
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
5✔
2657
                        if (r < 0)
5✔
2658
                                return r;
2659

2660
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
5✔
2661
                        c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size);
5✔
2662
                        c->bzip2.avail_out = avail;
5✔
2663

2664
                        r = sym_BZ2_bzCompress(&c->bzip2, BZ_FINISH);
5✔
2665
                        if (!IN_SET(r, BZ_FINISH_OK, BZ_STREAM_END))
5✔
2666
                                return -EIO;
2667

2668
                        *buffer_size += avail - c->bzip2.avail_out;
5✔
2669
                } while (r != BZ_STREAM_END);
5✔
2670

2671
                break;
2672
#endif
2673

2674
        case COMPRESSION_NONE:
2675
                break;
2676

2677
        default:
2678
                return -EOPNOTSUPP;
2679
        }
2680

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

© 2026 Coveralls, Inc