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

systemd / systemd / 28630269468

02 Jul 2026 10:23PM UTC coverage: 72.618% (-0.06%) from 72.68%
28630269468

push

github

yuwata
test: ignore fails when the formatted timezone differs from the current one

When formatting a timestamp the C API takes into account historical data
from tzdata, so it returns a date strings with a historically-correct
timezone abbreviation. However, tzname[] doesn't do this and it returns
the most recent abbreviation for the given zone.

For example, according to tzdata America/Cancun switched from EST/EDT to
CST/CDT on 1998-08-02:

Zone America/Cancun     -5:47:04 -      LMT     1922 Jan  1  6:00u
                        -6:00   -       CST     1981 Dec 26  2:00
                        -5:00   -       EST     1983 Jan  4  0:00
                        -6:00   Mexico  C%sT    1997 Oct 26  2:00
                        -5:00   Mexico  E%sT    1998 Aug  2  2:00
                        -6:00   Mexico  C%sT    2015 Feb  1  2:00
                        -5:00   -       EST

So, formatting a timestamp from this time will yield a string with the
EDT timezone:

$ TZ=America/Cancun date -d "@902035565"
Sun Aug  2 01:26:05 EDT 1998

But using tzname[] (or strptime %z) shows the most recent data, where
America/Cancun uses EST (and doesn't use DST anymore, hence
tzname[1]=CDT that glibc remembers from the previous zone epoch):

$ TZ=America/Cancun ./tz
{EST, CDT}

This means that when we parse the formatted timestamp back we don't use
the historical timezone data, so we might end up with a different
offset:

TZ=America/Cancun, tzname[0]=EST, tzname[1]=CDT
@902035565603993 → Sun 1998-08-02 01:26:05 EDT → @902039165000000 → Sun 1998-08-02 01:26:05 CDT
src/test/test-time-util.c:452: Assertion failed: Expected "ignore" to be true
Aborted                    (core dumped) build-local/test-time-util

Instead of adding exceptions for every single timezone that switched
between different offsets in the past, let's address this a bit more
generally and skip the check if the parsed timezone doesn't match any of
the current timezones - this still keeps the check that the time... (continued)

1 of 5 new or added lines in 1 file covered. (20.0%)

9391 existing lines in 86 files now uncovered.

341680 of 470518 relevant lines covered (72.62%)

1343034.26 hits per line

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

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

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

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

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

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

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

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

31
#include "sd-dlopen.h"
32

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

42
#if HAVE_XZ
43
static DLSYM_PROTOTYPE(lzma_code) = NULL;
44
static DLSYM_PROTOTYPE(lzma_easy_encoder) = NULL;
45
static DLSYM_PROTOTYPE(lzma_end) = NULL;
46
static DLSYM_PROTOTYPE(lzma_stream_buffer_encode) = NULL;
47
static DLSYM_PROTOTYPE(lzma_stream_decoder) = NULL;
48
static DLSYM_PROTOTYPE(lzma_lzma_preset) = NULL;
49

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

62
#if HAVE_LZ4
63
static DLSYM_PROTOTYPE(LZ4F_compressBegin) = NULL;
64
static DLSYM_PROTOTYPE(LZ4F_compressBound) = NULL;
65
static DLSYM_PROTOTYPE(LZ4F_compressEnd) = NULL;
66
static DLSYM_PROTOTYPE(LZ4F_compressUpdate) = NULL;
67
static DLSYM_PROTOTYPE(LZ4F_createCompressionContext) = NULL;
68
static DLSYM_PROTOTYPE(LZ4F_createDecompressionContext) = NULL;
69
static DLSYM_PROTOTYPE(LZ4F_decompress) = NULL;
70
static DLSYM_PROTOTYPE(LZ4F_freeCompressionContext) = NULL;
71
static DLSYM_PROTOTYPE(LZ4F_freeDecompressionContext) = NULL;
72
static DLSYM_PROTOTYPE(LZ4F_isError) = NULL;
73
static DLSYM_PROTOTYPE(LZ4_compress_HC) = NULL;
74
static DLSYM_PROTOTYPE(LZ4_compress_default) = NULL;
75
static DLSYM_PROTOTYPE(LZ4_decompress_safe) = NULL;
76
static DLSYM_PROTOTYPE(LZ4_decompress_safe_partial) = NULL;
77
static DLSYM_PROTOTYPE(LZ4_versionNumber) = NULL;
78

79
static const LZ4F_preferences_t lz4_preferences = {
80
        .frameInfo.blockSizeID = 5,
81
};
82
#endif
83

84
#if HAVE_ZSTD
85
static DLSYM_PROTOTYPE(ZSTD_CCtx_setParameter) = NULL;
86
static DLSYM_PROTOTYPE(ZSTD_compress) = NULL;
87
static DLSYM_PROTOTYPE(ZSTD_compressStream2) = NULL;
88
static DLSYM_PROTOTYPE(ZSTD_createCCtx) = NULL;
89
static DLSYM_PROTOTYPE(ZSTD_createDCtx) = NULL;
90
static DLSYM_PROTOTYPE(ZSTD_CStreamInSize) = NULL;
91
static DLSYM_PROTOTYPE(ZSTD_CStreamOutSize) = NULL;
92
static DLSYM_PROTOTYPE(ZSTD_decompressStream) = NULL;
93
static DLSYM_PROTOTYPE(ZSTD_DStreamInSize) = NULL;
94
static DLSYM_PROTOTYPE(ZSTD_DStreamOutSize) = NULL;
95
static DLSYM_PROTOTYPE(ZSTD_freeCCtx) = NULL;
96
static DLSYM_PROTOTYPE(ZSTD_freeDCtx) = NULL;
97
static DLSYM_PROTOTYPE(ZSTD_getErrorCode) = NULL;
98
static DLSYM_PROTOTYPE(ZSTD_getErrorName) = NULL;
99
static DLSYM_PROTOTYPE(ZSTD_getFrameContentSize) = NULL;
100
static DLSYM_PROTOTYPE(ZSTD_isError) = NULL;
101

102
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ZSTD_DCtx*, sym_ZSTD_freeDCtx, ZSTD_freeDCtxp, NULL);
837,590✔
103

104
static int zstd_ret_to_errno(size_t ret) {
6✔
105
        switch (sym_ZSTD_getErrorCode(ret)) {
6✔
106
        case ZSTD_error_dstSize_tooSmall:
107
                return -ENOBUFS;
108
        case ZSTD_error_memory_allocation:
×
109
                return -ENOMEM;
×
110
        default:
×
111
                return -EBADMSG;
×
112
        }
113
}
114
#endif
115

116
#if HAVE_ZLIB
117
static DLSYM_PROTOTYPE(deflateInit2_) = NULL;
118
static DLSYM_PROTOTYPE(deflate) = NULL;
119
static DLSYM_PROTOTYPE(deflateEnd) = NULL;
120
static DLSYM_PROTOTYPE(inflateInit2_) = NULL;
121
static DLSYM_PROTOTYPE(inflate) = NULL;
122
static DLSYM_PROTOTYPE(inflateEnd) = NULL;
123

124
static inline void deflateEnd_wrapper(z_stream *s) {
15✔
125
        sym_deflateEnd(s);
15✔
126
}
15✔
127

128
static inline void inflateEnd_wrapper(z_stream *s) {
276✔
129
        sym_inflateEnd(s);
276✔
130
}
276✔
131
#endif
132

133
#if HAVE_BZIP2
134
static DLSYM_PROTOTYPE(BZ2_bzCompressInit) = NULL;
135
static DLSYM_PROTOTYPE(BZ2_bzCompress) = NULL;
136
static DLSYM_PROTOTYPE(BZ2_bzCompressEnd) = NULL;
137
static DLSYM_PROTOTYPE(BZ2_bzDecompressInit) = NULL;
138
static DLSYM_PROTOTYPE(BZ2_bzDecompress) = NULL;
139
static DLSYM_PROTOTYPE(BZ2_bzDecompressEnd) = NULL;
140

141
static inline void BZ2_bzCompressEnd_wrapper(bz_stream *s) {
11✔
142
        sym_BZ2_bzCompressEnd(s);
11✔
143
}
11✔
144

145
static inline void BZ2_bzDecompressEnd_wrapper(bz_stream *s) {
272✔
146
        sym_BZ2_bzDecompressEnd(s);
272✔
147
}
272✔
148
#endif
149

150
/* Opaque Compressor/Decompressor struct definition */
151
struct Compressor {
152
        Compression type;
153
        bool encoding;
154
        union {
155
#if HAVE_XZ
156
                lzma_stream xz;
157
#endif
158
#if HAVE_LZ4
159
                struct {
160
                        LZ4F_compressionContext_t c_lz4;
161
                        void *lz4_header;        /* stashed frame header from LZ4F_compressBegin */
162
                        size_t lz4_header_size;
163
                };
164
                LZ4F_decompressionContext_t d_lz4;
165
#endif
166
#if HAVE_ZSTD
167
                ZSTD_CCtx *c_zstd;
168
                ZSTD_DCtx *d_zstd;
169
#endif
170
#if HAVE_ZLIB
171
                z_stream gzip;
172
#endif
173
#if HAVE_BZIP2
174
                bz_stream bzip2;
175
#endif
176
        };
177
};
178

179
#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
180

181
/* zlib windowBits value for gzip format: MAX_WBITS (15) + 16 to enable gzip header detection/generation */
182
#define ZLIB_WBITS_GZIP (15 + 16)
183

184
static const char* const compression_table[_COMPRESSION_MAX] = {
185
        [COMPRESSION_NONE]  = "uncompressed", /* backwards compatibility with importd */
186
        [COMPRESSION_XZ]    = "xz",
187
        [COMPRESSION_LZ4]   = "lz4",
188
        [COMPRESSION_ZSTD]  = "zstd",
189
        [COMPRESSION_GZIP]  = "gzip",
190
        [COMPRESSION_BZIP2] = "bzip2",
191
};
192

193
static const char* const compression_uppercase_table[_COMPRESSION_MAX] = {
194
        [COMPRESSION_NONE]  = "NONE", /* backwards compatibility with SYSTEMD_JOURNAL_COMPRESS=NONE */
195
        [COMPRESSION_XZ]    = "XZ",
196
        [COMPRESSION_LZ4]   = "LZ4",
197
        [COMPRESSION_ZSTD]  = "ZSTD",
198
        [COMPRESSION_GZIP]  = "GZIP",
199
        [COMPRESSION_BZIP2] = "BZIP2",
200
};
201

202
static const char* const compression_extension_table[_COMPRESSION_MAX] = {
203
        [COMPRESSION_NONE]  = "",
204
        [COMPRESSION_XZ]    = ".xz",
205
        [COMPRESSION_LZ4]   = ".lz4",
206
        [COMPRESSION_ZSTD]  = ".zst",
207
        [COMPRESSION_GZIP]  = ".gz",
208
        [COMPRESSION_BZIP2] = ".bz2",
209
};
210

211
DEFINE_STRING_TABLE_LOOKUP(compression, Compression);
2,321✔
212
DEFINE_STRING_TABLE_LOOKUP(compression_uppercase, Compression);
21✔
213
DEFINE_STRING_TABLE_LOOKUP(compression_extension, Compression);
32✔
214

215
Compression compression_from_string_harder(const char *s) {
58✔
216
        Compression c;
58✔
217

218
        assert(s);
58✔
219

220
        c = compression_from_string(s);
58✔
221
        if (c >= 0)
58✔
222
                return c;
223

224
        return compression_uppercase_from_string(s);
21✔
225
}
226

227
Compression compression_from_filename(const char *filename) {
10✔
228
        Compression c;
10✔
229
        const char *e;
10✔
230

231
        assert(filename);
10✔
232

233
        e = strrchr(filename, '.');
10✔
234
        if (!e)
10✔
235
                return COMPRESSION_NONE;
236

237
        c = compression_extension_from_string(e);
10✔
238
        if (c < 0)
10✔
239
                return COMPRESSION_NONE;
×
240

241
        return c;
242
}
243

244
bool compression_supported(Compression c) {
4,040✔
245
        static const unsigned supported =
4,040✔
246
                (1U << COMPRESSION_NONE) |
247
                (1U << COMPRESSION_XZ) * HAVE_XZ |
248
                (1U << COMPRESSION_LZ4) * HAVE_LZ4 |
249
                (1U << COMPRESSION_ZSTD) * HAVE_ZSTD |
250
                (1U << COMPRESSION_GZIP) * HAVE_ZLIB |
251
                (1U << COMPRESSION_BZIP2) * HAVE_BZIP2;
252

253
        assert(c >= 0);
4,040✔
254
        assert(c < _COMPRESSION_MAX);
4,040✔
255

256
        return BIT_SET(supported, c);
4,040✔
257
}
258

259
Compression compression_detect_from_magic(const uint8_t data[static COMPRESSION_MAGIC_BYTES_MAX]) {
1,041✔
260
        /* Magic signatures per RFC 1952 (gzip), tukaani.org/xz/xz-file-format.txt (xz),
261
         * RFC 8878 (zstd), lz4/doc/lz4_Frame_format.md (lz4), and the bzip2 file format.
262
         * Make sure to update COMPRESSION_MAGIC_BYTES_MAX if needed when adding a new magic. */
263
        if (memcmp(data, (const uint8_t[]) { 0x1f, 0x8b }, 2) == 0)
1,041✔
264
                return COMPRESSION_GZIP;
209✔
265
        if (memcmp(data, (const uint8_t[]) { 0xfd, '7', 'z', 'X', 'Z', 0x00 }, 6) == 0)
832✔
266
                return COMPRESSION_XZ;
1✔
267
        if (memcmp(data, (const uint8_t[]) { 0x28, 0xb5, 0x2f, 0xfd }, 4) == 0)
831✔
268
                return COMPRESSION_ZSTD;
2✔
269
        if (memcmp(data, (const uint8_t[]) { 0x04, 0x22, 0x4d, 0x18 }, 4) == 0)
829✔
270
                return COMPRESSION_LZ4;
1✔
271
        if (memcmp(data, (const uint8_t[]) { 'B', 'Z', 'h' }, 3) == 0)
828✔
272
                return COMPRESSION_BZIP2;
1✔
273

274
        return _COMPRESSION_INVALID;
827✔
275
}
276

277
int dlopen_xz(int log_level) {
327✔
278
#if HAVE_XZ
279
        static void *lzma_dl = NULL;
327✔
280

281
        SD_ELF_NOTE_DLOPEN(
327✔
282
                        "lzma",
283
                        "Support lzma compression in journal and coredump files",
284
                        COMPRESSION_PRIORITY_XZ,
285
                        "liblzma.so.5");
286

287
        return dlopen_many_sym_or_warn(
327✔
288
                        &lzma_dl,
289
                        "liblzma.so.5", log_level,
290
                        DLSYM_ARG(lzma_code),
291
                        DLSYM_ARG(lzma_easy_encoder),
292
                        DLSYM_ARG(lzma_end),
293
                        DLSYM_ARG(lzma_stream_buffer_encode),
294
                        DLSYM_ARG(lzma_lzma_preset),
295
                        DLSYM_ARG(lzma_stream_decoder));
296
#else
297
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
298
                              "lzma support is not compiled in.");
299
#endif
300
}
301

302
int dlopen_lz4(int log_level) {
641✔
303
#if HAVE_LZ4
304
        static void *lz4_dl = NULL;
641✔
305

306
        SD_ELF_NOTE_DLOPEN(
641✔
307
                        "lz4",
308
                        "Support lz4 compression in journal and coredump files",
309
                        COMPRESSION_PRIORITY_LZ4,
310
                        "liblz4.so.1");
311

312
        return dlopen_many_sym_or_warn(
641✔
313
                        &lz4_dl,
314
                        "liblz4.so.1", log_level,
315
                        DLSYM_ARG(LZ4F_compressBegin),
316
                        DLSYM_ARG(LZ4F_compressBound),
317
                        DLSYM_ARG(LZ4F_compressEnd),
318
                        DLSYM_ARG(LZ4F_compressUpdate),
319
                        DLSYM_ARG(LZ4F_createCompressionContext),
320
                        DLSYM_ARG(LZ4F_createDecompressionContext),
321
                        DLSYM_ARG(LZ4F_decompress),
322
                        DLSYM_ARG(LZ4F_freeCompressionContext),
323
                        DLSYM_ARG(LZ4F_freeDecompressionContext),
324
                        DLSYM_ARG(LZ4F_isError),
325
                        DLSYM_ARG(LZ4_compress_default),
326
                        DLSYM_ARG(LZ4_compress_HC),
327
                        DLSYM_ARG(LZ4_decompress_safe),
328
                        DLSYM_ARG(LZ4_decompress_safe_partial),
329
                        DLSYM_ARG(LZ4_versionNumber));
330
#else
331
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
332
                              "lz4 support is not compiled in.");
333
#endif
334
}
335

336
int dlopen_zstd(int log_level) {
838,394✔
337
#if HAVE_ZSTD
338
        static void *zstd_dl = NULL;
838,394✔
339

340
        SD_ELF_NOTE_DLOPEN(
838,394✔
341
                        "zstd",
342
                        "Support zstd compression in journal and coredump files",
343
                        COMPRESSION_PRIORITY_ZSTD,
344
                        "libzstd.so.1");
345

346
        return dlopen_many_sym_or_warn(
838,394✔
347
                        &zstd_dl,
348
                        "libzstd.so.1", log_level,
349
                        DLSYM_ARG(ZSTD_getErrorCode),
350
                        DLSYM_ARG(ZSTD_compress),
351
                        DLSYM_ARG(ZSTD_getFrameContentSize),
352
                        DLSYM_ARG(ZSTD_decompressStream),
353
                        DLSYM_ARG(ZSTD_getErrorName),
354
                        DLSYM_ARG(ZSTD_DStreamOutSize),
355
                        DLSYM_ARG(ZSTD_CStreamInSize),
356
                        DLSYM_ARG(ZSTD_CStreamOutSize),
357
                        DLSYM_ARG(ZSTD_CCtx_setParameter),
358
                        DLSYM_ARG(ZSTD_compressStream2),
359
                        DLSYM_ARG(ZSTD_DStreamInSize),
360
                        DLSYM_ARG(ZSTD_freeCCtx),
361
                        DLSYM_ARG(ZSTD_freeDCtx),
362
                        DLSYM_ARG(ZSTD_isError),
363
                        DLSYM_ARG(ZSTD_createDCtx),
364
                        DLSYM_ARG(ZSTD_createCCtx));
365
#else
366
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
367
                              "zstd support is not compiled in.");
368
#endif
369
}
370

371
int dlopen_zlib(int log_level) {
523✔
372
#if HAVE_ZLIB
373
        static void *zlib_dl = NULL;
523✔
374

375
        SD_ELF_NOTE_DLOPEN(
523✔
376
                        "zlib",
377
                        "Support gzip compression and decompression",
378
                        SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
379
                        "libz.so.1");
380

381
        return dlopen_many_sym_or_warn(
523✔
382
                        &zlib_dl,
383
                        "libz.so.1", log_level,
384
                        DLSYM_ARG(deflateInit2_),
385
                        DLSYM_ARG(deflate),
386
                        DLSYM_ARG(deflateEnd),
387
                        DLSYM_ARG(inflateInit2_),
388
                        DLSYM_ARG(inflate),
389
                        DLSYM_ARG(inflateEnd));
390
#else
391
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
392
                              "zlib support is not compiled in.");
393
#endif
394
}
395

396
int dlopen_bzip2(int log_level) {
296✔
397
#if HAVE_BZIP2
398
        static void *bzip2_dl = NULL;
296✔
399

400
        SD_ELF_NOTE_DLOPEN(
296✔
401
                        "bzip2",
402
                        "Support bzip2 compression and decompression",
403
                        SD_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED,
404
                        "libbz2.so.1");
405

406
        return dlopen_many_sym_or_warn(
296✔
407
                        &bzip2_dl,
408
                        "libbz2.so.1", log_level,
409
                        DLSYM_ARG(BZ2_bzCompressInit),
410
                        DLSYM_ARG(BZ2_bzCompress),
411
                        DLSYM_ARG(BZ2_bzCompressEnd),
412
                        DLSYM_ARG(BZ2_bzDecompressInit),
413
                        DLSYM_ARG(BZ2_bzDecompress),
414
                        DLSYM_ARG(BZ2_bzDecompressEnd));
415
#else
416
        return log_full_errno(log_level, SYNTHETIC_ERRNO(EOPNOTSUPP),
417
                              "bzip2 support is not compiled in.");
418
#endif
419
}
420

421
static int compress_blob_xz(
20✔
422
                const void *src,
423
                uint64_t src_size,
424
                void *dst,
425
                size_t dst_alloc_size,
426
                size_t *dst_size,
427
                int level) {
428

429
        assert(src);
20✔
430
        assert(src_size > 0);
20✔
431
        assert(dst);
20✔
432
        assert(dst_alloc_size > 0);
20✔
433
        assert(dst_size);
20✔
434

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

448
        r = dlopen_xz(LOG_DEBUG);
20✔
449
        if (r < 0)
20✔
450
                return r;
20✔
451

452
        if (level >= 0) {
20✔
453
                r = sym_lzma_lzma_preset(&opt, (uint32_t) level);
1✔
454
                if (r < 0)
455
                        return r;
456
        }
457

458
        /* Returns < 0 if we couldn't compress the data or the
459
         * compressed result is longer than the original */
460

461
        if (src_size < 80)
20✔
462
                return -ENOBUFS;
463

464
        ret = sym_lzma_stream_buffer_encode(filters, LZMA_CHECK_NONE, NULL,
20✔
465
                                        src, src_size, dst, &out_pos, dst_alloc_size);
466
        if (ret != LZMA_OK)
20✔
467
                return -ENOBUFS;
468

469
        *dst_size = out_pos;
17✔
470
        return 0;
17✔
471
#else
472
        return -EPROTONOSUPPORT;
473
#endif
474
}
475

476
static int compress_blob_lz4(
180✔
477
                const void *src,
478
                uint64_t src_size,
479
                void *dst,
480
                size_t dst_alloc_size,
481
                size_t *dst_size,
482
                int level) {
483

484
        assert(src);
180✔
485
        assert(src_size > 0);
180✔
486
        assert(dst);
180✔
487
        assert(dst_alloc_size > 0);
180✔
488
        assert(dst_size);
180✔
489

490
#if HAVE_LZ4
491
        int r;
180✔
492

493
        r = dlopen_lz4(LOG_DEBUG);
180✔
494
        if (r < 0)
180✔
495
                return r;
496
        /* Returns < 0 if we couldn't compress the data or the
497
         * compressed result is longer than the original */
498

499
        if (src_size < 9)
180✔
500
                return -ENOBUFS;
501

502
        if (src_size > INT_MAX)
180✔
503
                return -EFBIG;
504
        if (dst_alloc_size > INT_MAX)
180✔
505
                dst_alloc_size = INT_MAX;
×
506

507
        if (level <= 0)
180✔
508
                r = sym_LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
180✔
509
        else
510
                r = sym_LZ4_compress_HC(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8, level);
×
511
        if (r <= 0)
180✔
512
                return -ENOBUFS;
513

514
        unaligned_write_le64(dst, src_size);
171✔
515
        *dst_size = r + 8;
171✔
516

517
        return 0;
171✔
518
#else
519
        return -EPROTONOSUPPORT;
520
#endif
521
}
522

523
static int compress_blob_zstd(
751✔
524
                const void *src,
525
                uint64_t src_size,
526
                void *dst,
527
                size_t dst_alloc_size,
528
                size_t *dst_size,
529
                int level) {
530

531
        assert(src);
751✔
532
        assert(src_size > 0);
751✔
533
        assert(dst);
751✔
534
        assert(dst_alloc_size > 0);
751✔
535
        assert(dst_size);
751✔
536

537
#if HAVE_ZSTD
538
        size_t k;
751✔
539
        int r;
751✔
540

541
        r = dlopen_zstd(LOG_DEBUG);
751✔
542
        if (r < 0)
751✔
543
                return r;
544

545
        k = sym_ZSTD_compress(dst, dst_alloc_size, src, src_size, level < 0 ? 0 : level);
751✔
546
        if (sym_ZSTD_isError(k))
751✔
547
                return zstd_ret_to_errno(k);
6✔
548

549
        *dst_size = k;
745✔
550
        return 0;
745✔
551
#else
552
        return -EPROTONOSUPPORT;
553
#endif
554
}
555

556
static int compress_blob_gzip(const void *src, uint64_t src_size,
15✔
557
                       void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
558

559
        assert(src);
15✔
560
        assert(src_size > 0);
15✔
561
        assert(dst);
15✔
562
        assert(dst_alloc_size > 0);
15✔
563
        assert(dst_size);
15✔
564

565
#if HAVE_ZLIB
566
        int r;
15✔
567

568
        r = dlopen_zlib(LOG_DEBUG);
15✔
569
        if (r < 0)
15✔
570
                return r;
15✔
571

572
        if (src_size > UINT_MAX)
15✔
573
                return -EFBIG;
574
        if (dst_alloc_size > UINT_MAX)
15✔
575
                dst_alloc_size = UINT_MAX;
×
576

577
        _cleanup_(deflateEnd_wrapper) z_stream s = {};
15✔
578

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

588
        s.next_in = (void*) src;
15✔
589
        s.avail_in = src_size;
15✔
590
        s.next_out = dst;
15✔
591
        s.avail_out = dst_alloc_size;
15✔
592

593
        r = sym_deflate(&s, Z_FINISH);
15✔
594
        if (r != Z_STREAM_END)
15✔
595
                return -ENOBUFS;
596

597
        *dst_size = dst_alloc_size - s.avail_out;
12✔
598
        return 0;
12✔
599
#else
600
        return -EPROTONOSUPPORT;
601
#endif
602
}
603

604
static int compress_blob_bzip2(
11✔
605
                const void *src, uint64_t src_size,
606
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
607

608
        assert(src);
11✔
609
        assert(src_size > 0);
11✔
610
        assert(dst);
11✔
611
        assert(dst_alloc_size > 0);
11✔
612
        assert(dst_size);
11✔
613

614
#if HAVE_BZIP2
615
        int r;
11✔
616

617
        r = dlopen_bzip2(LOG_DEBUG);
11✔
618
        if (r < 0)
11✔
619
                return r;
11✔
620

621
        if (src_size > UINT_MAX)
11✔
622
                return -EFBIG;
623
        if (dst_alloc_size > UINT_MAX)
11✔
624
                dst_alloc_size = UINT_MAX;
×
625

626
        _cleanup_(BZ2_bzCompressEnd_wrapper) bz_stream s = {};
11✔
627

628
        r = sym_BZ2_bzCompressInit(&s, level < 0 ? 9 : level, /* verbosity= */ 0, /* workFactor= */ 0);
22✔
629
        if (r != BZ_OK)
11✔
630
                return -ENOMEM;
631

632
        s.next_in = (char*) src;
11✔
633
        s.avail_in = src_size;
11✔
634
        s.next_out = (char*) dst;
11✔
635
        s.avail_out = dst_alloc_size;
11✔
636

637
        r = sym_BZ2_bzCompress(&s, BZ_FINISH);
11✔
638

639
        if (r != BZ_STREAM_END)
11✔
640
                return -ENOBUFS;
641

642
        *dst_size = dst_alloc_size - s.avail_out;
9✔
643
        return 0;
9✔
644
#else
645
        return -EPROTONOSUPPORT;
646
#endif
647
}
648

649
int compress_blob(
977✔
650
                Compression compression,
651
                const void *src, uint64_t src_size,
652
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
653

654
        switch (compression) {
977✔
655
        case COMPRESSION_XZ:
20✔
656
                return compress_blob_xz(src, src_size, dst, dst_alloc_size, dst_size, level);
20✔
657
        case COMPRESSION_LZ4:
180✔
658
                return compress_blob_lz4(src, src_size, dst, dst_alloc_size, dst_size, level);
180✔
659
        case COMPRESSION_ZSTD:
751✔
660
                return compress_blob_zstd(src, src_size, dst, dst_alloc_size, dst_size, level);
751✔
661
        case COMPRESSION_GZIP:
15✔
662
                return compress_blob_gzip(src, src_size, dst, dst_alloc_size, dst_size, level);
15✔
663
        case COMPRESSION_BZIP2:
11✔
664
                return compress_blob_bzip2(src, src_size, dst, dst_alloc_size, dst_size, level);
11✔
665
        default:
666
                return -EOPNOTSUPP;
667
        }
668
}
669

670
static int decompress_blob_xz(
24✔
671
                const void *src,
672
                uint64_t src_size,
673
                void **dst,
674
                size_t *dst_size,
675
                size_t dst_max) {
676

677
        assert(src);
24✔
678
        assert(src_size > 0);
24✔
679
        assert(dst);
24✔
680
        assert(dst_size);
24✔
681

682
#if HAVE_XZ
683
        int r;
24✔
684

685
        r = dlopen_xz(LOG_DEBUG);
24✔
686
        if (r < 0)
24✔
687
                return r;
24✔
688

689
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
24✔
690
        lzma_ret ret = sym_lzma_stream_decoder(&s, UINT64_MAX, /* flags= */ 0);
24✔
691
        if (ret != LZMA_OK)
24✔
692
                return -ENOMEM;
693

694
        size_t space = MIN(src_size * 2, dst_max ?: SIZE_MAX);
24✔
695
        if (!greedy_realloc(dst, space, 1))
24✔
696
                return -ENOMEM;
697

698
        s.next_in = src;
24✔
699
        s.avail_in = src_size;
24✔
700

701
        s.next_out = *dst;
24✔
702
        s.avail_out = space;
24✔
703

704
        for (;;) {
216✔
705
                size_t used;
120✔
706

707
                ret = sym_lzma_code(&s, LZMA_FINISH);
120✔
708
                if (ret == LZMA_STREAM_END)
120✔
709
                        break;
710
                if (ret != LZMA_OK)
98✔
711
                        return -ENOMEM;
712

713
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
96✔
714
                        break;
715
                if (dst_max > 0 && space == dst_max)
96✔
716
                        return -ENOBUFS;
717

718
                used = space - s.avail_out;
96✔
719
                /* Silence static analyzers, space is bounded by allocation size */
720
                assert(space <= SIZE_MAX / 2);
96✔
721
                space = MIN(2 * space, dst_max ?: SIZE_MAX);
96✔
722
                if (!greedy_realloc(dst, space, 1))
96✔
723
                        return -ENOMEM;
724

725
                s.avail_out = space - used;
96✔
726
                s.next_out = *(uint8_t**)dst + used;
96✔
727
        }
728

729
        *dst_size = space - s.avail_out;
22✔
730
        return 0;
22✔
731
#else
732
        return -EPROTONOSUPPORT;
733
#endif
734
}
735

736
static int decompress_blob_lz4(
178✔
737
                const void *src,
738
                uint64_t src_size,
739
                void **dst,
740
                size_t *dst_size,
741
                size_t dst_max) {
742

743
        assert(src);
178✔
744
        assert(src_size > 0);
178✔
745
        assert(dst);
178✔
746
        assert(dst_size);
178✔
747

748
#if HAVE_LZ4
749
        char* out;
178✔
750
        int r, size; /* LZ4 uses int for size */
178✔
751

752
        r = dlopen_lz4(LOG_DEBUG);
178✔
753
        if (r < 0)
178✔
754
                return r;
755

756
        if (src_size <= 8)
178✔
757
                return -EBADMSG;
758

759
        if (src_size - 8 > INT_MAX)
176✔
760
                return -EFBIG;
761

762
        size = unaligned_read_le64(src);
176✔
763
        if (size < 0 || (unsigned) size != unaligned_read_le64(src))
176✔
764
                return -EFBIG;
765
        if (dst_max > 0 && (size_t) size > dst_max)
176✔
766
                return -ENOBUFS;
767
        out = greedy_realloc(dst, size, 1);
176✔
768
        if (!out)
176✔
769
                return -ENOMEM;
770

771
        r = sym_LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
176✔
772
        if (r < 0 || r != size)
176✔
773
                return -EBADMSG;
774

775
        *dst_size = size;
176✔
776
        return 0;
176✔
777
#else
778
        return -EPROTONOSUPPORT;
779
#endif
780
}
781

782
size_t zstd_dstream_out_size(void) {
1✔
783
#if HAVE_ZSTD
784
        if (dlopen_zstd(LOG_DEBUG) < 0)
1✔
785
                return 0;
786

787
        return sym_ZSTD_DStreamOutSize();
1✔
788
#else
789
        return 0;
790
#endif
791
}
792

793
static int decompress_blob_zstd(
47,118✔
794
                const void *src,
795
                uint64_t src_size,
796
                void **dst,
797
                size_t *dst_size,
798
                size_t dst_max) {
799

800
        assert(src);
47,118✔
801
        assert(src_size > 0);
47,118✔
802
        assert(dst);
47,118✔
803
        assert(dst_size);
47,118✔
804

805
#if HAVE_ZSTD
806
        int r;
47,118✔
807

808
        r = dlopen_zstd(LOG_DEBUG);
47,118✔
809
        if (r < 0)
47,118✔
810
                return r;
47,118✔
811

812
        /* ZSTD_getFrameContentSize() returns unsigned long long, which is not necessarily size_t, so
813
         * keep the value in that type until we've clamped it down to something that fits size_t. */
814
        unsigned long long size = sym_ZSTD_getFrameContentSize(src, src_size);
47,118✔
815
        if (size == ZSTD_CONTENTSIZE_ERROR)
47,118✔
816
                return -EBADMSG;
817

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

823
        /* dst_max == 0 means "no limit"; fold that into a single ceiling up front so the rest of the
824
         * function can treat the unlimited case like any other rather than repeating the sentinel. */
825
        size_t cap = dst_max ?: SIZE_MAX;
47,116✔
826

827
        size_t space;
47,116✔
828
        if (size != ZSTD_CONTENTSIZE_UNKNOWN)
47,116✔
829
                /* The MIN clamps the (possibly 64-bit) content size to the ceiling, which is <= SIZE_MAX,
830
                 * so the result always fits size_t. */
831
                space = MAX(sym_ZSTD_DStreamOutSize(), (size_t) MIN(size, (unsigned long long) cap));
47,111✔
832
        else {
833
                /* Start from twice the compressed size, guarding the doubling against overflow, then
834
                 * clamp to the ceiling. */
835
                uint64_t twice = src_size <= UINT64_MAX / 2 ? src_size * 2 : UINT64_MAX;
5✔
836
                space = MAX(sym_ZSTD_DStreamOutSize(), (size_t) MIN(twice, (uint64_t) cap));
5✔
837
        }
838

839
        if (!greedy_realloc(dst, space, 1))
47,116✔
840
                return -ENOMEM;
841
        space = MALLOC_SIZEOF_SAFE(*dst);
47,116✔
842

843
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
94,232✔
844
        if (!dctx)
47,116✔
845
                return -ENOMEM;
846

847
        ZSTD_inBuffer input = {
47,116✔
848
                .src = src,
849
                .size = src_size,
850
        };
851
        ZSTD_outBuffer output = {
47,116✔
852
                .dst = *dst,
47,116✔
853
                /* Never offer the decoder more room than the cap: greedy_realloc() over-allocates, so
854
                 * without this clamp a single ZSTD_decompressStream() call could write past dst_max
855
                 * before the output.pos >= cap check below fires. */
856
                .size = MIN(space, cap),
47,116✔
857
        };
858

859
        for (;;) {
47,121✔
860
                size_t k = sym_ZSTD_decompressStream(dctx, &output, &input);
47,121✔
861
                if (sym_ZSTD_isError(k))
47,121✔
UNCOV
862
                        return log_debug_errno(zstd_ret_to_errno(k), "ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
863

864
                /* output.size is clamped to MIN(space, cap) here and after every grow, and zstd keeps
865
                 * output.pos <= output.size, so output.pos never exceeds cap: this fires exactly when the
866
                 * caller's limit has been filled. */
867
                if (output.pos >= cap)
47,121✔
868
                        break;
869

870
                /* k == 0 means the current frame was fully decoded and flushed. If no input is left
871
                 * either, the whole stream is done. This must be checked before the grow branch below:
872
                 * the frame can finish exactly as the output buffer fills, and growing then would call
873
                 * the decoder again on already-exhausted input, which it'd misread as a truncated next
874
                 * frame. */
875
                if (k == 0 && input.pos >= input.size)
47,120✔
876
                        break;
877

878
                /* Otherwise there is still work to do: either decoded data buffered inside the decoder
879
                 * that didn't fit (k != 0, e.g. zstd's buffered-flush mode), or another concatenated
880
                 * frame to decode (k == 0 with input left). If the output buffer is full, grow it and
881
                 * call again. Guard against the doubling overflowing size_t (only reachable with an
882
                 * uncapped dst_max once the allocation has already grown past SIZE_MAX/2). */
883
                if (output.pos == output.size) {
11✔
884
                        if (space > SIZE_MAX / 2)
4✔
885
                                return -E2BIG;
886
                        space = MIN(2 * space, cap);
4✔
887
                        if (!greedy_realloc(dst, space, 1))
4✔
888
                                return -ENOMEM;
889
                        space = MALLOC_SIZEOF_SAFE(*dst);
4✔
890
                        output.dst = *dst;
4✔
891
                        output.size = MIN(space, cap);
4✔
892
                        continue;
4✔
893
                }
894

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

902
        *dst_size = output.pos;
47,110✔
903
        return 0;
47,110✔
904
#else
905
        return -EPROTONOSUPPORT;
906
#endif
907
}
908

909
static int decompress_blob_gzip(
10✔
910
                const void *src,
911
                uint64_t src_size,
912
                void **dst,
913
                size_t *dst_size,
914
                size_t dst_max) {
915

916
        assert(src);
10✔
917
        assert(src_size > 0);
10✔
918
        assert(dst);
10✔
919
        assert(dst_size);
10✔
920

921
#if HAVE_ZLIB
922
        int r;
10✔
923

924
        r = dlopen_zlib(LOG_DEBUG);
10✔
925
        if (r < 0)
10✔
926
                return r;
10✔
927

928
        if (src_size > UINT_MAX)
10✔
929
                return -EFBIG;
930

931
        _cleanup_(inflateEnd_wrapper) z_stream s = {};
10✔
932

933
        r = sym_inflateInit2_(&s, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(s));
10✔
934
        if (r != Z_OK)
10✔
935
                return -ENOMEM;
936

937
        size_t space = MIN3(src_size * 2, dst_max ?: SIZE_MAX, (size_t) UINT_MAX);
10✔
938
        if (!greedy_realloc(dst, space, 1))
10✔
939
                return -ENOMEM;
940

941
        s.next_in = (void*) src;
10✔
942
        s.avail_in = src_size;
10✔
943
        s.next_out = *dst;
10✔
944
        s.avail_out = space;
10✔
945

946
        for (;;) {
102✔
947
                size_t used;
56✔
948

949
                r = sym_inflate(&s, Z_NO_FLUSH);
56✔
950
                if (r == Z_STREAM_END)
56✔
951
                        break;
952
                if (!IN_SET(r, Z_OK, Z_BUF_ERROR))
48✔
953
                        return -EBADMSG;
954

955
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
46✔
956
                        break;
957
                if (dst_max > 0 && space == dst_max)
46✔
958
                        return -ENOBUFS;
959

960
                used = space - s.avail_out;
46✔
961
                space = MIN3(2 * space, dst_max ?: SIZE_MAX, UINT_MAX);
46✔
962
                if (!greedy_realloc(dst, space, 1))
46✔
963
                        return -ENOMEM;
964

965
                s.avail_out = space - used;
46✔
966
                s.next_out = *(uint8_t**)dst + used;
46✔
967
        }
968

969
        *dst_size = space - s.avail_out;
8✔
970
        return 0;
8✔
971
#else
972
        return -EPROTONOSUPPORT;
973
#endif
974
}
975

976
static int decompress_blob_bzip2(
6✔
977
                const void *src,
978
                uint64_t src_size,
979
                void **dst,
980
                size_t *dst_size,
981
                size_t dst_max) {
982

983
        assert(src);
6✔
984
        assert(src_size > 0);
6✔
985
        assert(dst);
6✔
986
        assert(dst_size);
6✔
987

988
#if HAVE_BZIP2
989
        int r;
6✔
990

991
        r = dlopen_bzip2(LOG_DEBUG);
6✔
992
        if (r < 0)
6✔
993
                return r;
6✔
994

995
        if (src_size > UINT_MAX)
6✔
996
                return -EFBIG;
997

998
        _cleanup_(BZ2_bzDecompressEnd_wrapper) bz_stream s = {};
6✔
999

1000
        r = sym_BZ2_bzDecompressInit(&s, /* verbosity= */ 0, /* small= */ 0);
6✔
1001
        if (r != BZ_OK)
6✔
1002
                return -ENOMEM;
1003

1004
        size_t space = MIN3(src_size * 2, dst_max ?: SIZE_MAX, (size_t) UINT_MAX);
6✔
1005
        if (!greedy_realloc(dst, space, 1))
6✔
1006
                return -ENOMEM;
1007

1008
        s.next_in = (char*) src;
6✔
1009
        s.avail_in = src_size;
6✔
1010
        s.next_out = (char*) *dst;
6✔
1011
        s.avail_out = space;
6✔
1012

1013
        for (;;) {
56✔
1014
                size_t used;
31✔
1015

1016
                r = sym_BZ2_bzDecompress(&s);
31✔
1017
                if (r == BZ_STREAM_END)
31✔
1018
                        break;
1019
                if (r != BZ_OK)
27✔
1020
                        return -EBADMSG;
1021

1022
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
25✔
1023
                        break;
1024
                if (dst_max > 0 && space == dst_max)
25✔
1025
                        return -ENOBUFS;
1026

1027
                used = space - s.avail_out;
25✔
1028
                space = MIN3(2 * space, dst_max ?: SIZE_MAX, (size_t) UINT_MAX);
25✔
1029
                if (!greedy_realloc(dst, space, 1))
25✔
1030
                        return -ENOMEM;
1031

1032
                s.avail_out = space - used;
25✔
1033
                s.next_out = (char*) *dst + used;
25✔
1034
        }
1035

1036
        *dst_size = space - s.avail_out;
4✔
1037
        return 0;
4✔
1038
#else
1039
        return -EPROTONOSUPPORT;
1040
#endif
1041
}
1042

1043
int decompress_blob(
47,336✔
1044
                Compression compression,
1045
                const void *src,
1046
                uint64_t src_size,
1047
                void **dst,
1048
                size_t *dst_size,
1049
                size_t dst_max) {
1050

1051
        switch (compression) {
47,336✔
1052
        case COMPRESSION_XZ:
24✔
1053
                return decompress_blob_xz(
24✔
1054
                                src, src_size,
1055
                                dst, dst_size, dst_max);
1056
        case COMPRESSION_LZ4:
178✔
1057
                return decompress_blob_lz4(
178✔
1058
                                src, src_size,
1059
                                dst, dst_size, dst_max);
1060
        case COMPRESSION_ZSTD:
47,118✔
1061
                return decompress_blob_zstd(
47,118✔
1062
                                src, src_size,
1063
                                dst, dst_size, dst_max);
1064
        case COMPRESSION_GZIP:
10✔
1065
                return decompress_blob_gzip(
10✔
1066
                                src, src_size,
1067
                                dst, dst_size, dst_max);
1068
        case COMPRESSION_BZIP2:
6✔
1069
                return decompress_blob_bzip2(
6✔
1070
                                src, src_size,
1071
                                dst, dst_size, dst_max);
1072
        default:
1073
                return -EPROTONOSUPPORT;
1074
        }
1075
}
1076

UNCOV
1077
int decompress_zlib_raw(
×
1078
                const void *src,
1079
                uint64_t src_size,
1080
                void *dst,
1081
                size_t dst_size,
1082
                int wbits) {
1083

1084
#if HAVE_ZLIB
UNCOV
1085
        int r;
×
1086

UNCOV
1087
        r = dlopen_zlib(LOG_DEBUG);
×
UNCOV
1088
        if (r < 0)
×
UNCOV
1089
                return r;
×
1090

UNCOV
1091
        if (src_size > UINT_MAX)
×
1092
                return -EFBIG;
UNCOV
1093
        if (dst_size > UINT_MAX)
×
1094
                return -EFBIG;
1095

UNCOV
1096
        _cleanup_(inflateEnd_wrapper) z_stream s = {
×
1097
                .next_in = (void*) src,
1098
                .avail_in = src_size,
1099
                .next_out = dst,
1100
                .avail_out = dst_size,
1101
        };
1102

UNCOV
1103
        r = sym_inflateInit2_(&s, /* windowBits= */ wbits, ZLIB_VERSION, (int) sizeof(s));
×
1104
        if (r != Z_OK)
×
1105
                return -EIO;
1106

1107
        r = sym_inflate(&s, Z_FINISH);
×
1108
        size_t produced = (uint8_t*) s.next_out - (uint8_t*) dst;
×
1109

UNCOV
1110
        if (r != Z_STREAM_END || produced != dst_size)
×
UNCOV
1111
                return -EBADMSG;
×
1112

1113
        return 0;
1114
#else
1115
        return -EPROTONOSUPPORT;
1116
#endif
1117
}
1118

1119
static int decompress_startswith_xz(
270✔
1120
                const void *src,
1121
                uint64_t src_size,
1122
                void **buffer,
1123
                const void *prefix,
1124
                size_t prefix_len,
1125
                uint8_t extra) {
1126

1127
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
1128
         * follow the prefix */
1129

1130
        assert(src);
270✔
1131
        assert(src_size > 0);
270✔
1132
        assert(buffer);
270✔
1133
        assert(prefix);
270✔
1134

1135
#if HAVE_XZ
1136
        int r;
270✔
1137

1138
        r = dlopen_xz(LOG_DEBUG);
270✔
1139
        if (r < 0)
270✔
1140
                return r;
270✔
1141

1142
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
270✔
1143
        lzma_ret ret = sym_lzma_stream_decoder(&s, UINT64_MAX, /* flags= */ 0);
270✔
1144
        if (ret != LZMA_OK)
270✔
1145
                return -EBADMSG;
1146

1147
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
1148
                return -ENOMEM;
1149

1150
        size_t allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
1151

1152
        s.next_in = src;
270✔
1153
        s.avail_in = src_size;
270✔
1154

1155
        s.next_out = *buffer;
270✔
1156
        s.avail_out = allocated;
270✔
1157

1158
        for (;;) {
270✔
1159
                ret = sym_lzma_code(&s, LZMA_FINISH);
270✔
1160

1161
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
270✔
1162
                        return -EBADMSG;
1163

1164
                if (allocated - s.avail_out >= prefix_len + 1)
270✔
1165
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
270✔
1166
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
270✔
1167

UNCOV
1168
                if (ret == LZMA_STREAM_END)
×
1169
                        return 0;
1170

UNCOV
1171
                s.avail_out += allocated;
×
1172

UNCOV
1173
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
1174
                        return -ENOMEM;
1175

UNCOV
1176
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
1177
                s.next_out = *(uint8_t**)buffer + allocated - s.avail_out;
×
1178
        }
1179

1180
#else
1181
        return -EPROTONOSUPPORT;
1182
#endif
1183
}
1184

1185
static int decompress_startswith_lz4(
270✔
1186
                const void *src,
1187
                uint64_t src_size,
1188
                void **buffer,
1189
                const void *prefix,
1190
                size_t prefix_len,
1191
                uint8_t extra) {
1192

1193
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
1194
         * follow the prefix */
1195

1196
        assert(src);
270✔
1197
        assert(src_size > 0);
270✔
1198
        assert(buffer);
270✔
1199
        assert(prefix);
270✔
1200

1201
#if HAVE_LZ4
1202
        size_t allocated;
270✔
1203
        int r;
270✔
1204

1205
        r = dlopen_lz4(LOG_DEBUG);
270✔
1206
        if (r < 0)
270✔
1207
                return r;
1208

1209
        if (src_size <= 8)
270✔
1210
                return -EBADMSG;
1211

1212
        if (src_size - 8 > INT_MAX)
270✔
1213
                return -EFBIG;
1214

1215
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
1216
                return -ENOMEM;
1217
        allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
1218

1219
        r = sym_LZ4_decompress_safe_partial(
540✔
1220
                        (char*)src + 8,
1221
                        *buffer,
1222
                        src_size - 8,
270✔
1223
                        prefix_len + 1,
270✔
1224
                        allocated);
1225

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

UNCOV
1233
                if (sym_LZ4_versionNumber() >= 10803)
×
1234
                        /* We trust that the newer lz4 decompresses the number of bytes we
1235
                         * requested if available in the compressed string. */
UNCOV
1236
                        return 0;
×
1237

1238
                if (r > 0)
×
1239
                        /* Compare what we have first, in case of mismatch we can
1240
                         * shortcut the full comparison. */
UNCOV
1241
                        if (memcmp(*buffer, prefix, r) != 0)
×
1242
                                return 0;
1243

1244
                /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
1245
                 * so in pathological cases might need to decompress the full field. */
UNCOV
1246
                r = decompress_blob_lz4(src, src_size, buffer, &size, 0);
×
UNCOV
1247
                if (r < 0)
×
1248
                        return r;
1249

UNCOV
1250
                if (size < prefix_len + 1)
×
1251
                        return 0;
1252
        }
1253

1254
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
270✔
1255
                ((const uint8_t*) *buffer)[prefix_len] == extra;
270✔
1256
#else
1257
        return -EPROTONOSUPPORT;
1258
#endif
1259
}
1260

1261
static int decompress_startswith_zstd(
790,474✔
1262
                const void *src,
1263
                uint64_t src_size,
1264
                void **buffer,
1265
                const void *prefix,
1266
                size_t prefix_len,
1267
                uint8_t extra) {
1268

1269
        assert(src);
790,474✔
1270
        assert(src_size > 0);
790,474✔
1271
        assert(buffer);
790,474✔
1272
        assert(prefix);
790,474✔
1273

1274
#if HAVE_ZSTD
1275
        int r;
790,474✔
1276

1277
        r = dlopen_zstd(LOG_DEBUG);
790,474✔
1278
        if (r < 0)
790,474✔
1279
                return r;
790,474✔
1280

1281
        /* ZSTD_getFrameContentSize() returns unsigned long long, which is not necessarily size_t. */
1282
        unsigned long long size = sym_ZSTD_getFrameContentSize(src, src_size);
790,474✔
1283
        if (size == ZSTD_CONTENTSIZE_ERROR)
790,474✔
1284
                return -EBADMSG;
1285

1286
        /* ZSTD_CONTENTSIZE_UNKNOWN means the frame doesn't record the decompressed size, so we can't
1287
         * shortcut here. The single decompression below only needs prefix_len + 1 bytes anyway. */
1288
        if (size != ZSTD_CONTENTSIZE_UNKNOWN && size < prefix_len + 1)
790,474✔
1289
                return 0; /* Decompressed text too short to match the prefix and extra */
1290

1291
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
1,580,948✔
1292
        if (!dctx)
790,474✔
1293
                return -ENOMEM;
1294

1295
        if (!(greedy_realloc(buffer, MAX(sym_ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
790,474✔
1296
                return -ENOMEM;
1297

1298
        ZSTD_inBuffer input = {
790,474✔
1299
                .src = src,
1300
                .size = src_size,
1301
        };
1302
        ZSTD_outBuffer output = {
1,580,948✔
1303
                .dst = *buffer,
790,474✔
1304
                .size = MALLOC_SIZEOF_SAFE(*buffer),
790,474✔
1305
        };
1306
        size_t k;
790,474✔
1307

1308
        k = sym_ZSTD_decompressStream(dctx, &output, &input);
790,474✔
1309
        if (sym_ZSTD_isError(k))
790,474✔
UNCOV
1310
                return log_debug_errno(zstd_ret_to_errno(k), "ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
1311
        if (output.pos < prefix_len + 1) {
790,474✔
1312
                /* The output buffer is at least prefix_len + 1 bytes, so if it isn't full the decoder
1313
                 * stopped for another reason: either the frame ended (k == 0), in which case the stream
1314
                 * is simply too short to match the prefix, or it's still expecting input (k != 0), which
1315
                 * means the stream was truncated mid-frame. */
1316
                if (k != 0)
3✔
1317
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD stream ended unexpectedly, probably corrupted.");
2✔
1318
                return 0; /* Decompressed text too short to match the prefix and extra */
1319
        }
1320

1321
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
790,471✔
1322
                ((const uint8_t*) *buffer)[prefix_len] == extra;
31,709✔
1323
#else
1324
        return -EPROTONOSUPPORT;
1325
#endif
1326
}
1327

1328
static int decompress_startswith_gzip(
266✔
1329
                const void *src,
1330
                uint64_t src_size,
1331
                void **buffer,
1332
                const void *prefix,
1333
                size_t prefix_len,
1334
                uint8_t extra) {
1335

1336
        assert(src);
266✔
1337
        assert(src_size > 0);
266✔
1338
        assert(buffer);
266✔
1339
        assert(prefix);
266✔
1340

1341
#if HAVE_ZLIB
1342
        int r;
266✔
1343

1344
        r = dlopen_zlib(LOG_DEBUG);
266✔
1345
        if (r < 0)
266✔
1346
                return r;
266✔
1347

1348
        if (src_size > UINT_MAX)
266✔
1349
                return -EFBIG;
1350

1351
        _cleanup_(inflateEnd_wrapper) z_stream s = {};
266✔
1352

1353
        r = sym_inflateInit2_(&s, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(s));
266✔
1354
        if (r != Z_OK)
266✔
1355
                return -EBADMSG;
1356

1357
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
266✔
1358
                return -ENOMEM;
1359

1360
        size_t allocated = MALLOC_SIZEOF_SAFE(*buffer);
266✔
1361

1362
        s.next_in = (void*) src;
266✔
1363
        s.avail_in = src_size;
266✔
1364

1365
        s.next_out = *buffer;
266✔
1366
        s.avail_out = MIN(allocated, (size_t) UINT_MAX);
266✔
1367

1368
        for (;;) {
266✔
1369
                r = sym_inflate(&s, Z_FINISH);
266✔
1370

1371
                if (!IN_SET(r, Z_OK, Z_STREAM_END, Z_BUF_ERROR))
266✔
1372
                        return -EBADMSG;
1373

1374
                if (allocated - s.avail_out >= prefix_len + 1)
266✔
1375
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
266✔
1376
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
266✔
1377

UNCOV
1378
                if (r == Z_STREAM_END)
×
1379
                        return 0;
1380

UNCOV
1381
                size_t used = allocated - s.avail_out;
×
1382

UNCOV
1383
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
1384
                        return -ENOMEM;
1385

UNCOV
1386
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
UNCOV
1387
                s.avail_out = MIN(allocated - used, (size_t) UINT_MAX);
×
UNCOV
1388
                s.next_out = *(uint8_t**)buffer + used;
×
1389
        }
1390
#else
1391
        return -EPROTONOSUPPORT;
1392
#endif
1393
}
1394

1395
static int decompress_startswith_bzip2(
266✔
1396
                const void *src,
1397
                uint64_t src_size,
1398
                void **buffer,
1399
                const void *prefix,
1400
                size_t prefix_len,
1401
                uint8_t extra) {
1402

1403
        assert(src);
266✔
1404
        assert(src_size > 0);
266✔
1405
        assert(buffer);
266✔
1406
        assert(prefix);
266✔
1407

1408
#if HAVE_BZIP2
1409
        int r;
266✔
1410

1411
        r = dlopen_bzip2(LOG_DEBUG);
266✔
1412
        if (r < 0)
266✔
1413
                return r;
266✔
1414

1415
        if (src_size > UINT_MAX)
266✔
1416
                return -EFBIG;
1417

1418
        _cleanup_(BZ2_bzDecompressEnd_wrapper) bz_stream s = {};
266✔
1419

1420
        r = sym_BZ2_bzDecompressInit(&s, /* verbosity= */ 0, /* small= */ 0);
266✔
1421
        if (r != BZ_OK)
266✔
1422
                return -EBADMSG;
1423

1424
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
266✔
1425
                return -ENOMEM;
1426

1427
        size_t allocated = MALLOC_SIZEOF_SAFE(*buffer);
266✔
1428

1429
        s.next_in = (char*) src;
266✔
1430
        s.avail_in = src_size;
266✔
1431

1432
        s.next_out = *buffer;
266✔
1433
        s.avail_out = MIN(allocated, (size_t) UINT_MAX);
266✔
1434

1435
        for (;;) {
266✔
1436
                r = sym_BZ2_bzDecompress(&s);
266✔
1437

1438
                if (!IN_SET(r, BZ_OK, BZ_STREAM_END))
266✔
1439
                        return -EBADMSG;
1440

1441
                if (allocated - s.avail_out >= prefix_len + 1)
266✔
1442
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
266✔
1443
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
266✔
1444

UNCOV
1445
                if (r == BZ_STREAM_END)
×
1446
                        return 0;
1447

UNCOV
1448
                size_t used = allocated - s.avail_out;
×
1449

UNCOV
1450
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
1451
                        return -ENOMEM;
1452

UNCOV
1453
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
UNCOV
1454
                s.avail_out = MIN(allocated - used, (size_t) UINT_MAX);
×
UNCOV
1455
                s.next_out = (char*) *buffer + used;
×
1456
        }
1457
#else
1458
        return -EPROTONOSUPPORT;
1459
#endif
1460
}
1461

1462
int decompress_startswith(
791,546✔
1463
                Compression compression,
1464
                const void *src, uint64_t src_size,
1465
                void **buffer,
1466
                const void *prefix, size_t prefix_len,
1467
                uint8_t extra) {
1468

1469
        switch (compression) {
791,546✔
1470
        case COMPRESSION_XZ:
270✔
1471
                return decompress_startswith_xz(src, src_size, buffer, prefix, prefix_len, extra);
270✔
1472
        case COMPRESSION_LZ4:
270✔
1473
                return decompress_startswith_lz4(src, src_size, buffer, prefix, prefix_len, extra);
270✔
1474
        case COMPRESSION_ZSTD:
790,474✔
1475
                return decompress_startswith_zstd(src, src_size, buffer, prefix, prefix_len, extra);
790,474✔
1476
        case COMPRESSION_GZIP:
266✔
1477
                return decompress_startswith_gzip(src, src_size, buffer, prefix, prefix_len, extra);
266✔
1478
        case COMPRESSION_BZIP2:
266✔
1479
                return decompress_startswith_bzip2(src, src_size, buffer, prefix, prefix_len, extra);
266✔
1480
        default:
1481
                return -EOPNOTSUPP;
1482
        }
1483
}
1484

1485
int compress_stream(
47✔
1486
                Compression type,
1487
                int fdf, int fdt,
1488
                uint64_t max_bytes,
1489
                uint64_t *ret_uncompressed_size) {
1490

UNCOV
1491
        _cleanup_(compressor_freep) Compressor *c = NULL;
×
UNCOV
1492
        _cleanup_free_ void *buf = NULL;
×
1493
        _cleanup_free_ uint8_t *input = NULL;
47✔
1494
        size_t buf_size = 0, buf_alloc = 0;
47✔
1495
        uint64_t total_in = 0, total_out = 0;
47✔
1496
        int r;
47✔
1497

1498
        assert(fdf >= 0);
47✔
1499
        assert(fdt >= 0);
47✔
1500

1501
        r = compressor_new(&c, type);
47✔
1502
        if (r < 0)
47✔
1503
                return r;
1504

1505
        input = new(uint8_t, COMPRESS_PIPE_BUFFER_SIZE);
47✔
1506
        if (!input)
47✔
1507
                return -ENOMEM;
1508

1509
        for (;;) {
768✔
1510
                size_t m = COMPRESS_PIPE_BUFFER_SIZE;
768✔
1511
                ssize_t n;
768✔
1512

1513
                if (max_bytes != UINT64_MAX && (uint64_t) m > max_bytes)
768✔
1514
                        m = (size_t) max_bytes;
×
1515

1516
                n = read(fdf, input, m);
768✔
1517
                if (n < 0)
768✔
UNCOV
1518
                        return -errno;
×
1519

1520
                if (n == 0) {
768✔
1521
                        r = compressor_finish(c, &buf, &buf_size, &buf_alloc);
47✔
1522
                        if (r < 0)
47✔
1523
                                return r;
1524

1525
                        if (buf_size > 0) {
47✔
1526
                                r = loop_write(fdt, buf, buf_size);
47✔
1527
                                if (r < 0)
47✔
1528
                                        return r;
1529
                                total_out += buf_size;
47✔
1530
                        }
1531
                        break;
47✔
1532
                }
1533

1534
                total_in += n;
721✔
1535
                if (max_bytes != UINT64_MAX) {
721✔
1536
                        assert(max_bytes >= (uint64_t) n);
682✔
1537
                        max_bytes -= n;
682✔
1538
                }
1539

1540
                r = compressor_start(c, input, n, &buf, &buf_size, &buf_alloc);
721✔
1541
                if (r < 0)
721✔
1542
                        return r;
1543

1544
                if (buf_size > 0) {
721✔
1545
                        r = loop_write(fdt, buf, buf_size);
685✔
1546
                        if (r < 0)
685✔
1547
                                return r;
1548
                        total_out += buf_size;
685✔
1549
                }
1550
        }
1551

1552
        if (ret_uncompressed_size)
47✔
1553
                *ret_uncompressed_size = total_in;
42✔
1554

1555
        if (total_in == 0)
47✔
UNCOV
1556
                log_debug("%s compression finished (no input data)", compression_to_string(type));
×
1557
        else
1558
                log_debug("%s compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
47✔
1559
                          compression_to_string(type), total_in, total_out, (double) total_out / total_in * 100);
1560

1561
        return 0;
1562
}
1563

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

1569
        assert(fd >= 0);
41✔
1570

1571
        if (fstat(fd, &st) < 0)
41✔
UNCOV
1572
                return -errno;
×
1573

1574
        int flags = fcntl(fd, F_GETFL);
41✔
1575
        if (flags < 0)
41✔
UNCOV
1576
                return -errno;
×
1577

1578
        return S_ISREG(st.st_mode) && !FLAGS_SET(flags, O_APPEND);
41✔
1579
}
1580

1581
/* After sparse decompression, set the file size to the current position to account for
1582
 * trailing holes that sparse_write() created via lseek but never extended the file size for. */
1583
static int finalize_sparse(int fd) {
30✔
1584
        off_t pos;
30✔
1585

1586
        assert(fd >= 0);
30✔
1587

1588
        pos = lseek(fd, 0, SEEK_CUR);
30✔
1589
        if (pos < 0)
30✔
UNCOV
1590
                return -errno;
×
1591

1592
        if (ftruncate(fd, pos) < 0)
30✔
UNCOV
1593
                return -errno;
×
1594

1595
        return 0;
1596
}
1597

1598
/* Common helper for decompress_stream_*() wrappers */
1599

1600
struct decompress_stream_userdata {
1601
        int fd;
1602
        uint64_t max_bytes;
1603
        uint64_t total_out;
1604
        bool sparse;
1605
};
1606

1607
static int decompress_stream_write_callback(const void *data, size_t size, void *userdata) {
248✔
1608
        struct decompress_stream_userdata *u = ASSERT_PTR(userdata);
248✔
1609

1610
        if (u->max_bytes != UINT64_MAX) {
248✔
1611
                if (u->max_bytes < size)
30✔
1612
                        return -EFBIG;
1613
                u->max_bytes -= size;
25✔
1614
        }
1615

1616
        u->total_out += size;
243✔
1617

1618
        if (u->sparse) {
243✔
1619
                /* Note: sparse_write() does not retry on EINTR and converts short writes to -EIO.
1620
                 * This is fine here since sparse mode is only used on regular files, where short
1621
                 * writes and EINTR are not expected in practice. */
1622
                ssize_t k = sparse_write(u->fd, data, size, 64);
242✔
1623
                if (k < 0)
242✔
UNCOV
1624
                        return (int) k;
×
1625
                return 0;
1626
        }
1627

1628
        return loop_write(u->fd, data, size);
1✔
1629
}
1630

1631
static int decompressor_new(Decompressor **ret, Compression type) {
41✔
1632
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1633
        int r;
41✔
1634
#endif
1635

1636
        assert(ret);
41✔
1637

1638
        _cleanup_(compressor_freep) Decompressor *c = new0(Decompressor, 1);
41✔
1639
        if (!c)
41✔
1640
                return -ENOMEM;
1641

1642
        c->type = _COMPRESSION_INVALID;
41✔
1643

1644
        switch (type) {
41✔
1645

1646
#if HAVE_XZ
1647
        case COMPRESSION_XZ:
6✔
1648
                r = dlopen_xz(LOG_DEBUG);
6✔
1649
                if (r < 0)
6✔
1650
                        return r;
1651

1652
                if (sym_lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED) != LZMA_OK)
6✔
1653
                        return -EIO;
1654
                break;
1655
#endif
1656

1657
#if HAVE_LZ4
1658
        case COMPRESSION_LZ4: {
6✔
1659
                r = dlopen_lz4(LOG_DEBUG);
6✔
1660
                if (r < 0)
6✔
1661
                        return r;
1662

1663
                size_t rc = sym_LZ4F_createDecompressionContext(&c->d_lz4, LZ4F_VERSION);
6✔
1664
                if (sym_LZ4F_isError(rc))
6✔
1665
                        return -ENOMEM;
1666

1667
                break;
1668
        }
1669
#endif
1670

1671
#if HAVE_ZSTD
1672
        case COMPRESSION_ZSTD:
16✔
1673
                r = dlopen_zstd(LOG_DEBUG);
16✔
1674
                if (r < 0)
16✔
1675
                        return r;
1676

1677
                c->d_zstd = sym_ZSTD_createDCtx();
16✔
1678
                if (!c->d_zstd)
16✔
1679
                        return -ENOMEM;
1680
                break;
1681
#endif
1682

1683
#if HAVE_ZLIB
1684
        case COMPRESSION_GZIP:
7✔
1685
                r = dlopen_zlib(LOG_DEBUG);
7✔
1686
                if (r < 0)
7✔
1687
                        return r;
1688

1689
                r = sym_inflateInit2_(&c->gzip, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(c->gzip));
7✔
1690
                if (r != Z_OK)
7✔
1691
                        return -EIO;
1692
                break;
1693
#endif
1694

1695
#if HAVE_BZIP2
1696
        case COMPRESSION_BZIP2:
6✔
1697
                r = dlopen_bzip2(LOG_DEBUG);
6✔
1698
                if (r < 0)
6✔
1699
                        return r;
1700

1701
                r = sym_BZ2_bzDecompressInit(&c->bzip2, /* verbosity= */ 0, /* small= */ 0);
6✔
1702
                if (r != BZ_OK)
6✔
1703
                        return -EIO;
1704
                break;
1705
#endif
1706

1707
        default:
1708
                return -EOPNOTSUPP;
1709
        }
1710

1711
        c->type = type;
41✔
1712
        c->encoding = false;
41✔
1713
        *ret = TAKE_PTR(c);
41✔
1714
        return 0;
41✔
1715
}
1716

1717
int decompress_stream(
41✔
1718
                Compression type,
1719
                int fdf, int fdt,
1720
                uint64_t max_bytes) {
1721

UNCOV
1722
        _cleanup_(compressor_freep) Decompressor *c = NULL;
×
1723
        _cleanup_free_ uint8_t *buf = NULL;
41✔
1724
        uint64_t total_in = 0;
41✔
1725
        int r;
41✔
1726

1727
        assert(fdf >= 0);
41✔
1728
        assert(fdt >= 0);
41✔
1729

1730
        r = decompressor_new(&c, type);
41✔
1731
        if (r < 0)
41✔
1732
                return r;
1733

1734
        struct decompress_stream_userdata userdata = {
82✔
1735
                .fd = fdt,
1736
                .max_bytes = max_bytes,
1737
                .sparse = should_sparse(fdt) > 0,
41✔
1738
        };
1739

1740
        buf = new(uint8_t, COMPRESS_PIPE_BUFFER_SIZE);
41✔
1741
        if (!buf)
41✔
1742
                return -ENOMEM;
1743

1744
        for (;;) {
89✔
1745
                ssize_t n;
89✔
1746

1747
                n = read(fdf, buf, COMPRESS_PIPE_BUFFER_SIZE);
89✔
1748
                if (n < 0)
89✔
UNCOV
1749
                        return -errno;
×
1750
                if (n == 0)
89✔
1751
                        break;
1752

1753
                total_in += n;
59✔
1754

1755
                r = decompressor_push(c, buf, n, decompress_stream_write_callback, &userdata);
59✔
1756
                if (r < 0)
59✔
1757
                        return r;
1758
        }
1759

1760
        if (total_in == 0)
30✔
UNCOV
1761
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "%s decompression failed: no data read",
×
1762
                                       compression_to_string(type));
1763

1764
        if (userdata.sparse) {
30✔
1765
                r = finalize_sparse(fdt);
30✔
1766
                if (r < 0)
30✔
1767
                        return r;
1768
        }
1769

1770
        log_debug("%s decompression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
30✔
1771
                  compression_to_string(type), total_in, userdata.total_out,
1772
                  (double) userdata.total_out / total_in * 100);
1773

1774
        return 0;
1775
}
1776

1777
int decompress_stream_by_filename(const char *filename, int fdf, int fdt, uint64_t max_bytes) {
10✔
1778
        Compression c = compression_from_filename(filename);
10✔
1779
        if (c == COMPRESSION_NONE)
10✔
1780
                return -EPROTONOSUPPORT;
1781

1782
        return decompress_stream(c, fdf, fdt, max_bytes);
10✔
1783
}
1784

1785
/* Push-based streaming compression/decompression context API */
1786

1787
Compressor* compressor_free(Compressor *c) {
1,518✔
1788
        if (!c)
1,518✔
1789
                return NULL;
1790

1791
        switch (c->type) {
1,324✔
1792

1793
#if HAVE_XZ
1794
        case COMPRESSION_XZ:
12✔
1795
                sym_lzma_end(&c->xz);
12✔
1796
                break;
12✔
1797
#endif
1798

1799
#if HAVE_LZ4
1800
        case COMPRESSION_LZ4:
12✔
1801
                if (c->encoding) {
12✔
1802
                        sym_LZ4F_freeCompressionContext(c->c_lz4);
5✔
1803
                        c->c_lz4 = NULL;
5✔
1804
                        c->lz4_header = mfree(c->lz4_header);
5✔
1805
                } else {
1806
                        sym_LZ4F_freeDecompressionContext(c->d_lz4);
7✔
1807
                        c->d_lz4 = NULL;
7✔
1808
                }
1809
                break;
1810
#endif
1811

1812
#if HAVE_ZSTD
1813
        case COMPRESSION_ZSTD:
49✔
1814
                if (c->encoding) {
49✔
1815
                        sym_ZSTD_freeCCtx(c->c_zstd);
31✔
1816
                        c->c_zstd = NULL;
31✔
1817
                } else {
1818
                        sym_ZSTD_freeDCtx(c->d_zstd);
18✔
1819
                        c->d_zstd = NULL;
18✔
1820
                }
1821
                break;
1822
#endif
1823

1824
#if HAVE_ZLIB
1825
        case COMPRESSION_GZIP:
229✔
1826
                if (c->encoding)
229✔
1827
                        sym_deflateEnd(&c->gzip);
14✔
1828
                else
1829
                        sym_inflateEnd(&c->gzip);
215✔
1830
                break;
1831
#endif
1832

1833
#if HAVE_BZIP2
1834
        case COMPRESSION_BZIP2:
12✔
1835
                if (c->encoding)
12✔
1836
                        sym_BZ2_bzCompressEnd(&c->bzip2);
5✔
1837
                else
1838
                        sym_BZ2_bzDecompressEnd(&c->bzip2);
7✔
1839
                break;
1840
#endif
1841

1842
        default:
1843
                break;
1844
        }
1845

1846
        return mfree(c);
1,324✔
1847
}
1848

1849
Compression compressor_type(const Compressor *c) {
4,402✔
1850
        return c ? c->type : _COMPRESSION_INVALID;
4,402✔
1851
}
1852

1853
int decompressor_detect(Decompressor **ret, const void *data, size_t size) {
1,216✔
1854
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1855
        int r;
1,216✔
1856
#endif
1857

1858
        assert(ret);
1,216✔
1859

1860
        if (*ret)
1,216✔
1861
                return 1;
1,216✔
1862

1863
        if (size < COMPRESSION_MAGIC_BYTES_MAX)
1,216✔
1864
                return 0;
1865

1866
        assert(data);
1,039✔
1867

1868
        Compression type = compression_detect_from_magic(data);
1,039✔
1869

1870
        _cleanup_(compressor_freep) Decompressor *c = new0(Decompressor, 1);
1,039✔
1871
        if (!c)
1,039✔
1872
                return -ENOMEM;
1873

1874
        switch (type) {
1,039✔
1875

1876
#if HAVE_XZ
1877
        case COMPRESSION_XZ: {
1✔
1878
                r = dlopen_xz(LOG_DEBUG);
1✔
1879
                if (r < 0)
1✔
1880
                        return r;
1881

1882
                lzma_ret xzr = sym_lzma_stream_decoder(&c->xz, UINT64_MAX, LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED);
1✔
1883
                if (xzr != LZMA_OK)
1✔
1884
                        return -EIO;
1885

1886
                break;
1887
        }
1888
#endif
1889

1890
#if HAVE_LZ4
1891
        case COMPRESSION_LZ4: {
1✔
1892
                r = dlopen_lz4(LOG_DEBUG);
1✔
1893
                if (r < 0)
1✔
1894
                        return r;
1895

1896
                size_t rc = sym_LZ4F_createDecompressionContext(&c->d_lz4, LZ4F_VERSION);
1✔
1897
                if (sym_LZ4F_isError(rc))
1✔
1898
                        return -ENOMEM;
1899

1900
                break;
1901
        }
1902
#endif
1903

1904
#if HAVE_ZSTD
1905
        case COMPRESSION_ZSTD: {
2✔
1906
                r = dlopen_zstd(LOG_DEBUG);
2✔
1907
                if (r < 0)
2✔
1908
                        return r;
1909

1910
                c->d_zstd = sym_ZSTD_createDCtx();
2✔
1911
                if (!c->d_zstd)
2✔
1912
                        return -ENOMEM;
1913

1914
                break;
1915
        }
1916
#endif
1917

1918
#if HAVE_ZLIB
1919
        case COMPRESSION_GZIP: {
208✔
1920
                r = dlopen_zlib(LOG_DEBUG);
208✔
1921
                if (r < 0)
208✔
1922
                        return r;
1923

1924
                r = sym_inflateInit2_(&c->gzip, /* windowBits= */ ZLIB_WBITS_GZIP, ZLIB_VERSION, (int) sizeof(c->gzip));
208✔
1925
                if (r != Z_OK)
208✔
1926
                        return -EIO;
1927

1928
                break;
1929
        }
1930
#endif
1931

1932
#if HAVE_BZIP2
1933
        case COMPRESSION_BZIP2: {
1✔
1934
                r = dlopen_bzip2(LOG_DEBUG);
1✔
1935
                if (r < 0)
1✔
1936
                        return r;
1937

1938
                r = sym_BZ2_bzDecompressInit(&c->bzip2, /* verbosity= */ 0, /* small= */ 0);
1✔
1939
                if (r != BZ_OK)
1✔
1940
                        return -EIO;
1941

1942
                break;
1943
        }
1944
#endif
1945

1946
        default:
826✔
1947
                if (type != _COMPRESSION_INVALID)
826✔
UNCOV
1948
                        return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
×
1949
                                               "Detected %s compression, but support is not compiled in.",
1950
                                               compression_to_string(type));
1951
                type = COMPRESSION_NONE;
1952
                break;
1953
        }
1954

1955
        c->type = type;
1,039✔
1956
        c->encoding = false;
1,039✔
1957

1958
        log_debug("Detected compression type: %s", compression_to_string(c->type));
1,039✔
1959
        *ret = TAKE_PTR(c);
1,039✔
1960
        return 1;
1,039✔
1961
}
1962

1963
int decompressor_force_off(Decompressor **ret) {
177✔
1964
        assert(ret);
177✔
1965

1966
        *ret = compressor_free(*ret);
177✔
1967

1968
        Decompressor *c = new0(Decompressor, 1);
177✔
1969
        if (!c)
177✔
1970
                return -ENOMEM;
1971

1972
        c->type = COMPRESSION_NONE;
177✔
1973
        c->encoding = false;
177✔
1974
        *ret = c;
177✔
1975
        return 0;
177✔
1976
}
1977

1978
int decompressor_push(Decompressor *c, const void *data, size_t size, DecompressorCallback callback, void *userdata) {
44,473✔
1979
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1980
        _cleanup_free_ uint8_t *buffer = NULL;
44,473✔
1981
#endif
1982
        int r;
44,473✔
1983

1984
        assert(c);
44,473✔
1985
        assert(callback);
44,473✔
1986

1987
        if (c->encoding)
44,473✔
1988
                return -EINVAL;
1989

1990
        if (size == 0)
44,473✔
1991
                return 1;
1992

1993
        assert(data);
43,860✔
1994

1995
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
1996
        if (c->type != COMPRESSION_NONE) {
43,860✔
1997
                buffer = new(uint8_t, COMPRESS_PIPE_BUFFER_SIZE);
31,634✔
1998
                if (!buffer)
31,634✔
1999
                        return -ENOMEM;
2000
        }
2001
#endif
2002

2003
        switch (c->type) {
43,860✔
2004

2005
        case COMPRESSION_NONE:
12,226✔
2006
                r = callback(data, size, userdata);
12,226✔
2007
                if (r < 0)
12,226✔
UNCOV
2008
                        return r;
×
2009

2010
                break;
2011

2012
#if HAVE_XZ
2013
        case COMPRESSION_XZ:
7✔
2014
                c->xz.next_in = data;
7✔
2015
                c->xz.avail_in = size;
7✔
2016

2017
                while (c->xz.avail_in > 0) {
20✔
2018
                        c->xz.next_out = buffer;
8✔
2019
                        c->xz.avail_out = COMPRESS_PIPE_BUFFER_SIZE;
8✔
2020

2021
                        lzma_ret lzr = sym_lzma_code(&c->xz, LZMA_RUN);
8✔
2022
                        if (!IN_SET(lzr, LZMA_OK, LZMA_STREAM_END))
8✔
2023
                                return -EBADMSG;
2024

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

2032
                break;
2033
#endif
2034

2035
#if HAVE_LZ4
2036
        case COMPRESSION_LZ4: {
2037
                const uint8_t *src = data;
2038
                size_t src_remaining = size;
2039

2040
                while (src_remaining > 0) {
13✔
2041
                        size_t produced = COMPRESS_PIPE_BUFFER_SIZE;
8✔
2042
                        size_t consumed = src_remaining;
8✔
2043

2044
                        size_t rc = sym_LZ4F_decompress(c->d_lz4, buffer, &produced, src, &consumed, NULL);
8✔
2045
                        if (sym_LZ4F_isError(rc))
8✔
2046
                                return -EBADMSG;
2✔
2047

2048
                        if (consumed == 0 && produced == 0)
7✔
2049
                                break; /* No progress possible with current input */
2050

2051
                        src += consumed;
7✔
2052
                        src_remaining -= consumed;
7✔
2053

2054
                        if (produced > 0) {
7✔
2055
                                r = callback(buffer, produced, userdata);
7✔
2056
                                if (r < 0)
7✔
2057
                                        return r;
2058
                        }
2059
                }
2060

2061
                break;
2062
        }
2063
#endif
2064

2065
#if HAVE_ZSTD
2066
        case COMPRESSION_ZSTD: {
36✔
2067
                ZSTD_inBuffer input = {
36✔
2068
                        .src =  (void*) data,
2069
                        .size = size,
2070
                };
2071

2072
                while (input.pos < input.size) {
259✔
2073
                        ZSTD_outBuffer output = {
226✔
2074
                                .dst = buffer,
2075
                                .size = COMPRESS_PIPE_BUFFER_SIZE,
2076
                        };
2077

2078
                        size_t res = sym_ZSTD_decompressStream(c->d_zstd, &output, &input);
226✔
2079
                        if (sym_ZSTD_isError(res))
226✔
2080
                                return -EBADMSG;
3✔
2081

2082
                        if (output.pos > 0) {
225✔
2083
                                r = callback(output.dst, output.pos, userdata);
225✔
2084
                                if (r < 0)
225✔
2085
                                        return r;
2086
                        }
2087
                }
2088

2089
                break;
33✔
2090
        }
2091
#endif
2092

2093
#if HAVE_ZLIB
2094
        case COMPRESSION_GZIP:
31,577✔
2095
                if (size > UINT_MAX)
31,577✔
2096
                        return -EFBIG;
2097

2098
                c->gzip.next_in = (void*) data;
31,577✔
2099
                c->gzip.avail_in = size;
31,577✔
2100

2101
                while (c->gzip.avail_in > 0) {
66,859✔
2102
                        c->gzip.next_out = buffer;
35,497✔
2103
                        c->gzip.avail_out = COMPRESS_PIPE_BUFFER_SIZE;
35,497✔
2104

2105
                        int zr = sym_inflate(&c->gzip, Z_NO_FLUSH);
35,497✔
2106
                        if (!IN_SET(zr, Z_OK, Z_STREAM_END))
35,497✔
2107
                                return -EBADMSG;
2108

2109
                        if (c->gzip.avail_out < COMPRESS_PIPE_BUFFER_SIZE) {
35,496✔
2110
                                r = callback(buffer, COMPRESS_PIPE_BUFFER_SIZE - c->gzip.avail_out, userdata);
35,496✔
2111
                                if (r < 0)
35,496✔
2112
                                        return r;
2113
                        }
2114

2115
                        if (zr == Z_STREAM_END)
35,495✔
2116
                                break;
2117
                }
2118

2119
                break;
2120
#endif
2121

2122
#if HAVE_BZIP2
2123
        case COMPRESSION_BZIP2:
7✔
2124
                if (size > UINT_MAX)
7✔
2125
                        return -EFBIG;
2126

2127
                c->bzip2.next_in = (char*) data;
7✔
2128
                c->bzip2.avail_in = size;
7✔
2129

2130
                while (c->bzip2.avail_in > 0) {
8✔
2131
                        c->bzip2.next_out = (char*) buffer;
8✔
2132
                        c->bzip2.avail_out = COMPRESS_PIPE_BUFFER_SIZE;
8✔
2133

2134
                        int bzr = sym_BZ2_bzDecompress(&c->bzip2);
8✔
2135
                        if (!IN_SET(bzr, BZ_OK, BZ_STREAM_END))
8✔
2136
                                return -EBADMSG;
2137

2138
                        if (c->bzip2.avail_out < COMPRESS_PIPE_BUFFER_SIZE) {
7✔
2139
                                r = callback(buffer, COMPRESS_PIPE_BUFFER_SIZE - c->bzip2.avail_out, userdata);
7✔
2140
                                if (r < 0)
7✔
2141
                                        return r;
2142
                        }
2143

2144
                        if (bzr == BZ_STREAM_END)
6✔
2145
                                break;
2146
                }
2147

2148
                break;
2149
#endif
2150

UNCOV
2151
        default:
×
UNCOV
2152
                assert_not_reached();
×
2153
        }
2154

2155
        return 1;
2156
}
2157

2158
int compressor_new(Compressor **ret, Compression type) {
67✔
2159
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2160
        int r;
67✔
2161
#endif
2162

2163
        assert(ret);
67✔
2164

2165
        _cleanup_(compressor_freep) Compressor *c = new0(Compressor, 1);
67✔
2166
        if (!c)
67✔
2167
                return -ENOMEM;
2168

2169
        c->type = _COMPRESSION_INVALID;
67✔
2170
        /* Set encoding early so that compressor_freep calls the correct cleanup (compression vs
2171
         * decompression) if any operation in the switch fails after setting c->type. This is safe
2172
         * because _COMPRESSION_INVALID hits the default: break case regardless of the encoding flag. */
2173
        c->encoding = true;
67✔
2174

2175
        switch (type) {
67✔
2176

2177
#if HAVE_XZ
2178
        case COMPRESSION_XZ: {
5✔
2179
                r = dlopen_xz(LOG_DEBUG);
5✔
2180
                if (r < 0)
5✔
2181
                        return r;
2182

2183
                lzma_ret xzr = sym_lzma_easy_encoder(&c->xz, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
5✔
2184
                if (xzr != LZMA_OK)
5✔
2185
                        return -EIO;
2186

2187
                c->type = COMPRESSION_XZ;
5✔
2188
                break;
5✔
2189
        }
2190
#endif
2191

2192
#if HAVE_LZ4
2193
        case COMPRESSION_LZ4: {
5✔
2194
                r = dlopen_lz4(LOG_DEBUG);
5✔
2195
                if (r < 0)
5✔
2196
                        return r;
2197

2198
                size_t rc = sym_LZ4F_createCompressionContext(&c->c_lz4, LZ4F_VERSION);
5✔
2199
                if (sym_LZ4F_isError(rc))
5✔
2200
                        return -ENOMEM;
2201

2202
                c->type = COMPRESSION_LZ4;
5✔
2203

2204
                /* Generate the frame header and stash it for the first compressor_start call */
2205
                size_t header_bound = sym_LZ4F_compressBound(0, &lz4_preferences);
5✔
2206
                c->lz4_header = malloc(header_bound);
5✔
2207
                if (!c->lz4_header)
5✔
2208
                        return -ENOMEM;
2209

2210
                c->lz4_header_size = sym_LZ4F_compressBegin(c->c_lz4, c->lz4_header, header_bound, &lz4_preferences);
5✔
2211
                if (sym_LZ4F_isError(c->lz4_header_size))
5✔
2212
                        return -EINVAL;
2213

2214
                break;
2215
        }
2216
#endif
2217

2218
#if HAVE_ZSTD
2219
        case COMPRESSION_ZSTD:
31✔
2220
                r = dlopen_zstd(LOG_DEBUG);
31✔
2221
                if (r < 0)
31✔
2222
                        return r;
2223

2224
                c->c_zstd = sym_ZSTD_createCCtx();
31✔
2225
                if (!c->c_zstd)
31✔
2226
                        return -ENOMEM;
2227

2228
                c->type = COMPRESSION_ZSTD;
31✔
2229

2230
                size_t z = sym_ZSTD_CCtx_setParameter(c->c_zstd, ZSTD_c_compressionLevel, ZSTD_CLEVEL_DEFAULT);
31✔
2231
                if (sym_ZSTD_isError(z))
31✔
2232
                        return -EIO;
2233

2234
                z = sym_ZSTD_CCtx_setParameter(c->c_zstd, ZSTD_c_checksumFlag, /* enable= */ 1);
31✔
2235
                if (sym_ZSTD_isError(z))
31✔
UNCOV
2236
                        log_debug("Failed to enable ZSTD checksum, ignoring: %s", sym_ZSTD_getErrorName(z));
×
2237

2238
                break;
2239
#endif
2240

2241
#if HAVE_ZLIB
2242
        case COMPRESSION_GZIP:
14✔
2243
                r = dlopen_zlib(LOG_DEBUG);
14✔
2244
                if (r < 0)
14✔
2245
                        return r;
2246

2247
                r = sym_deflateInit2_(&c->gzip,
14✔
2248
                                      Z_DEFAULT_COMPRESSION,
2249
                                      /* method= */ Z_DEFLATED,
2250
                                      /* windowBits= */ ZLIB_WBITS_GZIP,
2251
                                      /* memLevel= */ 8,
2252
                                      /* strategy= */ Z_DEFAULT_STRATEGY,
2253
                                      ZLIB_VERSION, (int) sizeof(c->gzip));
2254
                if (r != Z_OK)
14✔
2255
                        return -EIO;
2256

2257
                c->type = COMPRESSION_GZIP;
14✔
2258
                break;
14✔
2259
#endif
2260

2261
#if HAVE_BZIP2
2262
        case COMPRESSION_BZIP2:
5✔
2263
                r = dlopen_bzip2(LOG_DEBUG);
5✔
2264
                if (r < 0)
5✔
2265
                        return r;
2266

2267
                r = sym_BZ2_bzCompressInit(&c->bzip2, /* blockSize100k= */ 9, /* verbosity= */ 0, /* workFactor= */ 0);
5✔
2268
                if (r != BZ_OK)
5✔
2269
                        return -EIO;
2270

2271
                c->type = COMPRESSION_BZIP2;
5✔
2272
                break;
5✔
2273
#endif
2274

2275
        case COMPRESSION_NONE:
7✔
2276
                c->type = COMPRESSION_NONE;
7✔
2277
                break;
7✔
2278

2279
        default:
2280
                return -EOPNOTSUPP;
2281
        }
2282

2283
        *ret = TAKE_PTR(c);
67✔
2284
        return 0;
67✔
2285
}
2286

2287
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2288
static int enlarge_buffer(void **buffer, size_t *buffer_size, size_t *buffer_allocated, size_t need) {
5,291✔
2289
        assert(buffer);
5,291✔
2290
        assert(buffer_size);
5,291✔
2291
        assert(buffer_allocated);
5,291✔
2292

2293
        need = MAX3(need, *buffer_size + 1, (size_t) COMPRESS_PIPE_BUFFER_SIZE);
5,291✔
2294
        if (*buffer_allocated >= need)
5,291✔
2295
                return 0;
2296

2297
        if (!greedy_realloc(buffer, need, 1))
70✔
2298
                return -ENOMEM;
2299

2300
        *buffer_allocated = MALLOC_SIZEOF_SAFE(*buffer);
70✔
2301
        return 1;
70✔
2302
}
2303
#endif
2304

2305
int compressor_start(
5,268✔
2306
                Compressor *c,
2307
                const void *data,
2308
                size_t size,
2309
                void **buffer,
2310
                size_t *buffer_size,
2311
                size_t *buffer_allocated) {
2312

2313
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2314
        int r;
5,268✔
2315
#endif
2316

2317
        assert(c);
5,268✔
2318
        assert(buffer);
5,268✔
2319
        assert(buffer_size);
5,268✔
2320
        assert(buffer_allocated);
5,268✔
2321

2322
        if (!c->encoding)
5,268✔
2323
                return -EINVAL;
2324

2325
        if (size == 0)
5,268✔
2326
                return 0;
2327

2328
        assert(data);
5,268✔
2329

2330
        *buffer_size = 0;
5,268✔
2331

2332
        switch (c->type) {
5,268✔
2333

2334
#if HAVE_XZ
2335
        case COMPRESSION_XZ:
6✔
2336

2337
                c->xz.next_in = data;
6✔
2338
                c->xz.avail_in = size;
6✔
2339

2340
                while (c->xz.avail_in > 0) {
12✔
2341
                        lzma_ret lzr;
6✔
2342

2343
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
6✔
2344
                        if (r < 0)
6✔
2345
                                return r;
2346

2347
                        c->xz.next_out = (uint8_t*) *buffer + *buffer_size;
6✔
2348
                        c->xz.avail_out = *buffer_allocated - *buffer_size;
6✔
2349

2350
                        lzr = sym_lzma_code(&c->xz, LZMA_RUN);
6✔
2351
                        if (lzr != LZMA_OK)
6✔
2352
                                return -EIO;
2353

2354
                        *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out;
6✔
2355
                }
2356

2357
                break;
2358
#endif
2359

2360
#if HAVE_LZ4
2361
        case COMPRESSION_LZ4: {
6✔
2362
                /* Prepend any stashed frame header from compressor_new */
2363
                if (c->lz4_header_size > 0) {
6✔
2364
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, c->lz4_header_size);
5✔
2365
                        if (r < 0)
5✔
2366
                                return r;
2367

2368
                        memcpy(*buffer, c->lz4_header, c->lz4_header_size);
5✔
2369
                        *buffer_size = c->lz4_header_size;
5✔
2370
                        c->lz4_header = mfree(c->lz4_header);
5✔
2371
                        c->lz4_header_size = 0;
5✔
2372
                }
2373

2374
                size_t bound = sym_LZ4F_compressBound(size, &lz4_preferences);
6✔
2375
                r = enlarge_buffer(buffer, buffer_size, buffer_allocated, *buffer_size + bound);
6✔
2376
                if (r < 0)
6✔
2377
                        return r;
2378

2379
                size_t n = sym_LZ4F_compressUpdate(c->c_lz4,
12✔
2380
                                                   (uint8_t*) *buffer + *buffer_size,
6✔
2381
                                                   *buffer_allocated - *buffer_size,
6✔
2382
                                                   data, size, NULL);
2383
                if (sym_LZ4F_isError(n))
6✔
2384
                        return -EIO;
2385

2386
                *buffer_size += n;
6✔
2387
                break;
6✔
2388
        }
2389
#endif
2390

2391
#if HAVE_ZSTD
2392
        case COMPRESSION_ZSTD: {
701✔
2393
                ZSTD_inBuffer input = {
701✔
2394
                        .src = data,
2395
                        .size = size,
2396
                };
2397

2398
                while (input.pos < input.size) {
1,402✔
2399
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
701✔
2400
                        if (r < 0)
701✔
UNCOV
2401
                                return r;
×
2402

2403
                        ZSTD_outBuffer output = {
701✔
2404
                                .dst = ((uint8_t *) *buffer + *buffer_size),
701✔
2405
                                .size = *buffer_allocated - *buffer_size,
701✔
2406
                        };
2407

2408
                        size_t res = sym_ZSTD_compressStream2(c->c_zstd, &output, &input, ZSTD_e_continue);
701✔
2409
                        if (sym_ZSTD_isError(res))
701✔
2410
                                return -EIO;
2411

2412
                        *buffer_size += output.pos;
701✔
2413
                }
2414

2415
                break;
701✔
2416
        }
2417
#endif
2418

2419
#if HAVE_ZLIB
2420
        case COMPRESSION_GZIP:
4,507✔
2421
                if (size > UINT_MAX)
4,507✔
2422
                        return -EFBIG;
2423

2424
                c->gzip.next_in = (void*) data;
4,507✔
2425
                c->gzip.avail_in = size;
4,507✔
2426

2427
                while (c->gzip.avail_in > 0) {
9,014✔
2428
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
4,507✔
2429
                        if (r < 0)
4,507✔
2430
                                return r;
2431

2432
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
4,507✔
2433
                        c->gzip.next_out = (uint8_t*) *buffer + *buffer_size;
4,507✔
2434
                        c->gzip.avail_out = avail;
4,507✔
2435

2436
                        r = sym_deflate(&c->gzip, Z_NO_FLUSH);
4,507✔
2437
                        if (r != Z_OK)
4,507✔
2438
                                return -EIO;
2439

2440
                        *buffer_size += avail - c->gzip.avail_out;
4,507✔
2441
                }
2442

2443
                break;
2444
#endif
2445

2446
#if HAVE_BZIP2
2447
        case COMPRESSION_BZIP2:
6✔
2448
                if (size > UINT_MAX)
6✔
2449
                        return -EFBIG;
2450

2451
                c->bzip2.next_in = (void*) data;
6✔
2452
                c->bzip2.avail_in = size;
6✔
2453

2454
                while (c->bzip2.avail_in > 0) {
12✔
2455
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
6✔
2456
                        if (r < 0)
6✔
2457
                                return r;
2458

2459
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
6✔
2460
                        c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size);
6✔
2461
                        c->bzip2.avail_out = avail;
6✔
2462

2463
                        r = sym_BZ2_bzCompress(&c->bzip2, BZ_RUN);
6✔
2464
                        if (r != BZ_RUN_OK)
6✔
2465
                                return -EIO;
2466

2467
                        *buffer_size += avail - c->bzip2.avail_out;
6✔
2468
                }
2469

2470
                break;
2471
#endif
2472

2473
        case COMPRESSION_NONE:
42✔
2474

2475
                if (*buffer_allocated < size) {
42✔
2476
                        void *p;
2✔
2477

2478
                        p = realloc(*buffer, size);
2✔
2479
                        if (!p)
2✔
2480
                                return -ENOMEM;
2481

2482
                        *buffer = p;
2✔
2483
                        *buffer_allocated = size;
2✔
2484
                }
2485

2486
                memcpy(*buffer, data, size);
42✔
2487
                *buffer_size = size;
42✔
2488
                break;
42✔
2489

2490
        default:
2491
                return -EOPNOTSUPP;
2492
        }
2493

2494
        return 0;
2495
}
2496

2497
int compressor_finish(Compressor *c, void **buffer, size_t *buffer_size, size_t *buffer_allocated) {
62✔
2498
#if HAVE_XZ || HAVE_LZ4 || HAVE_ZSTD || HAVE_ZLIB || HAVE_BZIP2
2499
        int r;
62✔
2500
#endif
2501

2502
        assert(c);
62✔
2503
        assert(buffer);
62✔
2504
        assert(buffer_size);
62✔
2505
        assert(buffer_allocated);
62✔
2506

2507
        if (!c->encoding)
62✔
2508
                return -EINVAL;
2509

2510
        *buffer_size = 0;
62✔
2511

2512
        switch (c->type) {
62✔
2513

2514
#if HAVE_XZ
2515
        case COMPRESSION_XZ: {
5✔
2516
                lzma_ret lzr;
5✔
2517

2518
                c->xz.avail_in = 0;
5✔
2519

2520
                do {
5✔
2521
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
5✔
2522
                        if (r < 0)
5✔
2523
                                return r;
2524

2525
                        c->xz.next_out = (uint8_t*) *buffer + *buffer_size;
5✔
2526
                        c->xz.avail_out = *buffer_allocated - *buffer_size;
5✔
2527

2528
                        lzr = sym_lzma_code(&c->xz, LZMA_FINISH);
5✔
2529
                        if (!IN_SET(lzr, LZMA_OK, LZMA_STREAM_END))
5✔
2530
                                return -EIO;
2531

2532
                        *buffer_size += (*buffer_allocated - *buffer_size) - c->xz.avail_out;
5✔
2533
                } while (lzr != LZMA_STREAM_END);
5✔
2534

2535
                break;
2536
        }
2537
#endif
2538

2539
#if HAVE_LZ4
2540
        case COMPRESSION_LZ4: {
5✔
2541
                size_t bound = sym_LZ4F_compressBound(0, &lz4_preferences);
5✔
2542
                r = enlarge_buffer(buffer, buffer_size, buffer_allocated, bound);
5✔
2543
                if (r < 0)
5✔
2544
                        return r;
2545

2546
                size_t n = sym_LZ4F_compressEnd(c->c_lz4, *buffer, *buffer_allocated, NULL);
5✔
2547
                if (sym_LZ4F_isError(n))
5✔
2548
                        return -EIO;
2549

2550
                *buffer_size = n;
5✔
2551
                break;
5✔
2552
        }
2553
#endif
2554

2555
#if HAVE_ZSTD
2556
        case COMPRESSION_ZSTD: {
31✔
2557
                ZSTD_inBuffer input = {};
31✔
2558
                size_t res;
31✔
2559

2560
                do {
31✔
2561
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
31✔
2562
                        if (r < 0)
31✔
UNCOV
2563
                                return r;
×
2564

2565
                        ZSTD_outBuffer output = {
31✔
2566
                                .dst = ((uint8_t *) *buffer + *buffer_size),
31✔
2567
                                .size = *buffer_allocated - *buffer_size,
31✔
2568
                        };
2569

2570
                        res = sym_ZSTD_compressStream2(c->c_zstd, &output, &input, ZSTD_e_end);
31✔
2571
                        if (sym_ZSTD_isError(res))
31✔
2572
                                return -EIO;
2573

2574
                        *buffer_size += output.pos;
31✔
2575
                } while (res != 0);
31✔
2576

2577
                break;
31✔
2578
        }
2579
#endif
2580

2581
#if HAVE_ZLIB
2582
        case COMPRESSION_GZIP:
14✔
2583
                c->gzip.avail_in = 0;
14✔
2584

2585
                do {
14✔
2586
                        r = enlarge_buffer(buffer, buffer_size, buffer_allocated, /* need= */ 0);
14✔
2587
                        if (r < 0)
14✔
2588
                                return r;
2589

2590
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
14✔
2591
                        c->gzip.next_out = (uint8_t*) *buffer + *buffer_size;
14✔
2592
                        c->gzip.avail_out = avail;
14✔
2593

2594
                        r = sym_deflate(&c->gzip, Z_FINISH);
14✔
2595
                        if (!IN_SET(r, Z_OK, Z_STREAM_END))
14✔
2596
                                return -EIO;
2597

2598
                        *buffer_size += avail - c->gzip.avail_out;
14✔
2599
                } while (r != Z_STREAM_END);
14✔
2600

2601
                break;
2602
#endif
2603

2604
#if HAVE_BZIP2
2605
        case COMPRESSION_BZIP2:
5✔
2606
                c->bzip2.avail_in = 0;
5✔
2607

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

2613
                        size_t avail = MIN(*buffer_allocated - *buffer_size, (size_t) UINT_MAX);
5✔
2614
                        c->bzip2.next_out = (void*) ((uint8_t*) *buffer + *buffer_size);
5✔
2615
                        c->bzip2.avail_out = avail;
5✔
2616

2617
                        r = sym_BZ2_bzCompress(&c->bzip2, BZ_FINISH);
5✔
2618
                        if (!IN_SET(r, BZ_FINISH_OK, BZ_STREAM_END))
5✔
2619
                                return -EIO;
2620

2621
                        *buffer_size += avail - c->bzip2.avail_out;
5✔
2622
                } while (r != BZ_STREAM_END);
5✔
2623

2624
                break;
2625
#endif
2626

2627
        case COMPRESSION_NONE:
2628
                break;
2629

2630
        default:
2631
                return -EOPNOTSUPP;
2632
        }
2633

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

© 2026 Coveralls, Inc