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

systemd / systemd / 29622915321

17 Jul 2026 08:13PM UTC coverage: 72.84% (-0.2%) from 73.034%
29622915321

push

github

bluca
docs: clarify scope of portable services

Removed note clarifying that portable services are only for system services and not user services, and changed comparisons to "system services" with just "services". With newer systemd versions, `systemd-portabled` can be run as a user service.

345602 of 474467 relevant lines covered (72.84%)

1512984.3 hits per line

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

93.52
/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) {
294✔
71
        sym_lzma_end(ls);
294✔
72
}
294✔
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);
834,594✔
116

117
static int zstd_ret_to_errno(size_t ret) {
7✔
118
        switch (sym_ZSTD_getErrorCode(ret)) {
7✔
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) {
16✔
138
        sym_deflateEnd(s);
16✔
139
}
16✔
140

141
static inline void inflateEnd_wrapper(z_stream *s) {
276✔
142
        sym_inflateEnd(s);
276✔
143
}
276✔
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) {
14✔
155
        sym_BZ2_bzCompressEnd(s);
14✔
156
}
14✔
157

158
static inline void BZ2_bzDecompressEnd_wrapper(bz_stream *s) {
274✔
159
        sym_BZ2_bzDecompressEnd(s);
274✔
160
}
274✔
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,705✔
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) {
73✔
229
        Compression c;
73✔
230

231
        assert(s);
73✔
232

233
        c = compression_from_string(s);
73✔
234
        if (c >= 0)
73✔
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,038✔
271
        static const unsigned supported =
4,038✔
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,038✔
280
        assert(c < _COMPRESSION_MAX);
4,038✔
281

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

285
Compression compression_detect_from_magic(const uint8_t data[static COMPRESSION_MAGIC_BYTES_MAX]) {
1,102✔
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,102✔
290
                return COMPRESSION_GZIP;
209✔
291
        if (memcmp(data, (const uint8_t[]) { 0xfd, '7', 'z', 'X', 'Z', 0x00 }, 6) == 0)
893✔
292
                return COMPRESSION_XZ;
1✔
293
        if (memcmp(data, (const uint8_t[]) { 0x28, 0xb5, 0x2f, 0xfd }, 4) == 0)
892✔
294
                return COMPRESSION_ZSTD;
2✔
295
        if (memcmp(data, (const uint8_t[]) { 0x04, 0x22, 0x4d, 0x18 }, 4) == 0)
890✔
296
                return COMPRESSION_LZ4;
1✔
297
        if (memcmp(data, (const uint8_t[]) { 'B', 'Z', 'h' }, 3) == 0)
889✔
298
                return COMPRESSION_BZIP2;
1✔
299

300
        return _COMPRESSION_INVALID;
888✔
301
}
302

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

307
        LIBLZMA_NOTE(COMPRESSION_PRIORITY_XZ);
328✔
308

309
        return dlopen_many_sym_or_warn(
328✔
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) {
535✔
325
#if HAVE_LZ4
326
        static void *lz4_dl = NULL;
535✔
327

328
        LIBLZ4_NOTE(COMPRESSION_PRIORITY_LZ4);
535✔
329

330
        return dlopen_many_sym_or_warn(
535✔
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) {
835,686✔
355
#if HAVE_ZSTD
356
        static void *zstd_dl = NULL;
835,686✔
357

358
        LIBZSTD_NOTE(COMPRESSION_PRIORITY_ZSTD);
835,686✔
359

360
        return dlopen_many_sym_or_warn(
835,686✔
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) {
524✔
386
#if HAVE_ZLIB
387
        static void *zlib_dl = NULL;
524✔
388

389
        LIBZ_NOTE(suggested);
524✔
390

391
        return dlopen_many_sym_or_warn(
524✔
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) {
301✔
407
#if HAVE_BZIP2
408
        static void *bzip2_dl = NULL;
301✔
409

410
        LIBBZ2_NOTE(suggested);
301✔
411

412
        return dlopen_many_sym_or_warn(
301✔
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(
21✔
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);
21✔
436
        assert(src_size > 0);
21✔
437
        assert(dst);
21✔
438
        assert(dst_alloc_size > 0);
21✔
439
        assert(dst_size);
21✔
440

441
#if HAVE_XZ
442
        lzma_options_lzma opt = {
21✔
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[] = {
21✔
447
                { LZMA_FILTER_LZMA2, &opt },
448
                { LZMA_VLI_UNKNOWN, NULL }
449
        };
450
        lzma_ret ret;
21✔
451
        size_t out_pos = 0;
21✔
452
        int r;
21✔
453

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

458
        if (level >= 0) {
21✔
459
                r = sym_lzma_lzma_preset(&opt, (uint32_t) level);
1✔
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)
21✔
468
                return -ENOBUFS;
469

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

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

482
static int compress_blob_lz4(
127✔
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);
127✔
491
        assert(src_size > 0);
127✔
492
        assert(dst);
127✔
493
        assert(dst_alloc_size > 0);
127✔
494
        assert(dst_size);
127✔
495

496
#if HAVE_LZ4
497
        int r;
127✔
498

499
        r = dlopen_lz4(LOG_DEBUG);
127✔
500
        if (r < 0)
127✔
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)
127✔
506
                return -ENOBUFS;
507

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

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

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

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

529
static int compress_blob_zstd(
1,035✔
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);
1,035✔
538
        assert(src_size > 0);
1,035✔
539
        assert(dst);
1,035✔
540
        assert(dst_alloc_size > 0);
1,035✔
541
        assert(dst_size);
1,035✔
542

543
#if HAVE_ZSTD
544
        size_t k;
1,035✔
545
        int r;
1,035✔
546

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

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

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

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

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

571
#if HAVE_ZLIB
572
        int r;
16✔
573

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

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

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

585
        r = sym_deflateInit2_(&s, level < 0 ? Z_DEFAULT_COMPRESSION : level,
16✔
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)
16✔
592
                return -ENOMEM;
593

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

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

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

610
static int compress_blob_bzip2(
14✔
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);
14✔
615
        assert(src_size > 0);
14✔
616
        assert(dst);
14✔
617
        assert(dst_alloc_size > 0);
14✔
618
        assert(dst_size);
14✔
619

620
#if HAVE_BZIP2
621
        int r;
14✔
622

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

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

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

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

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

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

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

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

655
int compress_blob_journal(
1,183✔
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,183✔
661
        case COMPRESSION_XZ:
21✔
662
                return compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size, level);
21✔
663
        case COMPRESSION_LZ4:
127✔
664
                return compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size, level);
127✔
665
        case COMPRESSION_ZSTD:
1,035✔
666
                return compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size, level);
1,035✔
667
        default:
668
                return -EOPNOTSUPP;
669
        }
670
}
671

672
int compress_blob(
273✔
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) {
273✔
678
        case COMPRESSION_GZIP:
16✔
679
                return compress_blob_gzip(src, src_size, dst, dst_alloc_size, dst_size, level);
16✔
680
        case COMPRESSION_BZIP2:
14✔
681
                return compress_blob_bzip2(src, src_size, dst, dst_alloc_size, dst_size, level);
14✔
682
        default:
243✔
683
                return compress_blob_journal(compression, src, src_size, dst, dst_alloc_size, dst_size, level);
243✔
684
        }
685
}
686

687
static int decompress_blob_xz(
24✔
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);
24✔
695
        assert(src_size > 0);
24✔
696
        assert(dst);
24✔
697
        assert(dst_size);
24✔
698

699
#if HAVE_XZ
700
        int r;
24✔
701

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

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

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

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

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

721
        for (;;) {
194✔
722
                size_t used;
109✔
723

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

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

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

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

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

753
static int decompress_blob_lz4(
125✔
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);
125✔
761
        assert(src_size > 0);
125✔
762
        assert(dst);
125✔
763
        assert(dst_size);
125✔
764

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

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

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

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

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

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

792
        *dst_size = size;
123✔
793
        return 0;
123✔
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(
46,042✔
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);
46,042✔
818
        assert(src_size > 0);
46,042✔
819
        assert(dst);
46,042✔
820
        assert(dst_size);
46,042✔
821

822
#if HAVE_ZSTD
823
        int r;
46,042✔
824

825
        r = dlopen_zstd(LOG_DEBUG);
46,042✔
826
        if (r < 0)
46,042✔
827
                return r;
46,042✔
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);
46,042✔
832
        if (size == ZSTD_CONTENTSIZE_ERROR)
46,042✔
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;
46,040✔
843

844
        size_t space;
46,040✔
845
        if (size != ZSTD_CONTENTSIZE_UNKNOWN)
46,040✔
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));
46,035✔
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))
46,040✔
857
                return -ENOMEM;
858
        space = MALLOC_SIZEOF_SAFE(*dst);
46,040✔
859

860
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
92,080✔
861
        if (!dctx)
46,040✔
862
                return -ENOMEM;
863

864
        ZSTD_inBuffer input = {
46,040✔
865
                .src = src,
866
                .size = src_size,
867
        };
868
        ZSTD_outBuffer output = {
46,040✔
869
                .dst = *dst,
46,040✔
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),
46,040✔
874
        };
875

876
        for (;;) {
46,045✔
877
                size_t k = sym_ZSTD_decompressStream(dctx, &output, &input);
46,045✔
878
                if (sym_ZSTD_isError(k))
46,045✔
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)
46,045✔
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)
46,044✔
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;
46,034✔
920
        return 0;
46,034✔
921
#else
922
        return -EPROTONOSUPPORT;
923
#endif
924
}
925

926
static int decompress_blob_gzip(
10✔
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);
10✔
934
        assert(src_size > 0);
10✔
935
        assert(dst);
10✔
936
        assert(dst_size);
10✔
937

938
#if HAVE_ZLIB
939
        int r;
10✔
940

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

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

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

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

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

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

963
        for (;;) {
98✔
964
                size_t used;
54✔
965

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

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

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

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

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

993
static int decompress_blob_bzip2(
8✔
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);
8✔
1001
        assert(src_size > 0);
8✔
1002
        assert(dst);
8✔
1003
        assert(dst_size);
8✔
1004

1005
#if HAVE_BZIP2
1006
        int r;
8✔
1007

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

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

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

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

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

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

1030
        for (;;) {
88✔
1031
                size_t used;
48✔
1032

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

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

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

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

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

1060
int decompress_blob_journal(
46,191✔
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) {
46,191✔
1069
        case COMPRESSION_XZ:
24✔
1070
                return decompress_blob_xz(
24✔
1071
                                src, src_size,
1072
                                dst, dst_size, dst_max);
1073
        case COMPRESSION_LZ4:
125✔
1074
                return decompress_blob_lz4(
125✔
1075
                                src, src_size,
1076
                                dst, dst_size, dst_max);
1077
        case COMPRESSION_ZSTD:
46,042✔
1078
                return decompress_blob_zstd(
46,042✔
1079
                                src, src_size,
1080
                                dst, dst_size, dst_max);
1081
        default:
1082
                return -EPROTONOSUPPORT;
1083
        }
1084
}
1085

1086
int decompress_blob(
248✔
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) {
248✔
1095
        case COMPRESSION_GZIP:
10✔
1096
                return decompress_blob_gzip(
10✔
1097
                                src, src_size,
1098
                                dst, dst_size, dst_max);
1099
        case COMPRESSION_BZIP2:
8✔
1100
                return decompress_blob_bzip2(
8✔
1101
                                src, src_size,
1102
                                dst, dst_size, dst_max);
1103
        default:
230✔
1104
                return decompress_blob_journal(
230✔
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(
788,554✔
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);
788,554✔
1304
        assert(src_size > 0);
788,554✔
1305
        assert(buffer);
788,554✔
1306
        assert(prefix);
788,554✔
1307

1308
#if HAVE_ZSTD
1309
        int r;
788,554✔
1310

1311
        r = dlopen_zstd(LOG_DEBUG);
788,554✔
1312
        if (r < 0)
788,554✔
1313
                return r;
788,554✔
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);
788,554✔
1317
        if (size == ZSTD_CONTENTSIZE_ERROR)
788,554✔
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)
788,554✔
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,577,108✔
1326
        if (!dctx)
788,554✔
1327
                return -ENOMEM;
1328

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

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

1342
        k = sym_ZSTD_decompressStream(dctx, &output, &input);
788,554✔
1343
        if (sym_ZSTD_isError(k))
788,554✔
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) {
788,554✔
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 &&
788,551✔
1356
                ((const uint8_t*) *buffer)[prefix_len] == extra;
32,462✔
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(
789,094✔
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) {
789,094✔
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:
788,554✔
1509
                return decompress_startswith_zstd(src, src_size, buffer, prefix, prefix_len, extra);
788,554✔
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 (;;) {
794✔
1557
                size_t m = COMPRESS_PIPE_BUFFER_SIZE;
794✔
1558
                ssize_t n;
794✔
1559

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

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

1567
                if (n == 0) {
794✔
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;
743✔
1582
                if (max_bytes != UINT64_MAX) {
743✔
1583
                        assert(max_bytes >= (uint64_t) n);
704✔
1584
                        max_bytes -= n;
704✔
1585
                }
1586

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

1591
                if (buf_size > 0) {
743✔
1592
                        r = loop_write(fdt, buf, buf_size);
703✔
1593
                        if (r < 0)
703✔
1594
                                return r;
1595
                        total_out += buf_size;
703✔
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,511✔
1835
        if (!c)
1,511✔
1836
                return NULL;
1837

1838
        switch (c->type) {
1,353✔
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,353✔
1894
}
1895

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

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

1905
        assert(ret);
1,241✔
1906

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

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

1913
        assert(data);
1,100✔
1914

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

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

1921
        switch (type) {
1,100✔
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:
887✔
1994
                if (type != _COMPRESSION_INVALID)
887✔
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,100✔
2003
        c->encoding = false;
1,100✔
2004

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

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

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

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

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

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

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

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

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

2040
        assert(data);
43,055✔
2041

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

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

2052
        case COMPRESSION_NONE:
12,251✔
2053
                r = callback(data, size, userdata);
12,251✔
2054
                if (r < 0)
12,251✔
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,747✔
2142
                if (size > UINT_MAX)
30,747✔
2143
                        return -EFBIG;
2144

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

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

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

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

2162
                        if (zr == Z_STREAM_END)
34,388✔
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,001✔
2336
        assert(buffer);
5,001✔
2337
        assert(buffer_size);
5,001✔
2338
        assert(buffer_allocated);
5,001✔
2339

2340
        need = MAX3(need, *buffer_size + 1, (size_t) COMPRESS_PIPE_BUFFER_SIZE);
5,001✔
2341
        if (*buffer_allocated >= need)
5,001✔
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(
4,974✔
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;
4,974✔
2362
#endif
2363

2364
        assert(c);
4,974✔
2365
        assert(buffer);
4,974✔
2366
        assert(buffer_size);
4,974✔
2367
        assert(buffer_allocated);
4,974✔
2368

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

2372
        if (size == 0)
4,974✔
2373
                return 0;
2374

2375
        assert(data);
4,974✔
2376

2377
        *buffer_size = 0;
4,974✔
2378

2379
        switch (c->type) {
4,974✔
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: {
723✔
2440
                ZSTD_inBuffer input = {
723✔
2441
                        .src = data,
2442
                        .size = size,
2443
                };
2444

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

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

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

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

2462
                break;
723✔
2463
        }
2464
#endif
2465

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

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

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

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

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

2487
                        *buffer_size += avail - c->gzip.avail_out;
4,191✔
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;
2✔
2524

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

2529
                        *buffer = p;
2✔
2530
                        *buffer_allocated = size;
2✔
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