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

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

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

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 hits per line

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

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

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

8
#if HAVE_LZ4
9
#include <lz4.h>
10
#include <lz4hc.h>
11
#include <lz4frame.h>
12
#endif
13

14
#if HAVE_XZ
15
#include <lzma.h>
16
#endif
17

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

23
#include "alloc-util.h"
24
#include "bitfield.h"
25
#include "compress.h"
26
#include "dlfcn-util.h"
27
#include "fileio.h"
28
#include "io-util.h"
29
#include "log.h"
30
#include "string-table.h"
31
#include "string-util.h"
32
#include "unaligned.h"
33

34
#if HAVE_LZ4
35
static void *lz4_dl = NULL;
36

37
static DLSYM_PROTOTYPE(LZ4F_compressBegin) = NULL;
38
static DLSYM_PROTOTYPE(LZ4F_compressBound) = NULL;
39
static DLSYM_PROTOTYPE(LZ4F_compressEnd) = NULL;
40
static DLSYM_PROTOTYPE(LZ4F_compressUpdate) = NULL;
41
static DLSYM_PROTOTYPE(LZ4F_createCompressionContext) = NULL;
42
static DLSYM_PROTOTYPE(LZ4F_createDecompressionContext) = NULL;
43
static DLSYM_PROTOTYPE(LZ4F_decompress) = NULL;
44
static DLSYM_PROTOTYPE(LZ4F_freeCompressionContext) = NULL;
45
static DLSYM_PROTOTYPE(LZ4F_freeDecompressionContext) = NULL;
46
static DLSYM_PROTOTYPE(LZ4F_isError) = NULL;
47
static DLSYM_PROTOTYPE(LZ4_compress_HC) = NULL;
48
/* These are used in test-compress.c so we don't make them static. */
49
DLSYM_PROTOTYPE(LZ4_compress_default) = NULL;
50
DLSYM_PROTOTYPE(LZ4_decompress_safe) = NULL;
51
DLSYM_PROTOTYPE(LZ4_decompress_safe_partial) = NULL;
52
DLSYM_PROTOTYPE(LZ4_versionNumber) = NULL;
53

54
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_compressionContext_t, sym_LZ4F_freeCompressionContext, NULL);
1✔
55
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_decompressionContext_t, sym_LZ4F_freeDecompressionContext, NULL);
3✔
56
#endif
57

58
#if HAVE_ZSTD
59
static void *zstd_dl = NULL;
60

61
static DLSYM_PROTOTYPE(ZSTD_CCtx_setParameter) = NULL;
62
static DLSYM_PROTOTYPE(ZSTD_compress) = NULL;
63
static DLSYM_PROTOTYPE(ZSTD_compressStream2) = NULL;
64
static DLSYM_PROTOTYPE(ZSTD_createCCtx) = NULL;
65
static DLSYM_PROTOTYPE(ZSTD_createDCtx) = NULL;
66
static DLSYM_PROTOTYPE(ZSTD_CStreamInSize) = NULL;
67
static DLSYM_PROTOTYPE(ZSTD_CStreamOutSize) = NULL;
68
static DLSYM_PROTOTYPE(ZSTD_decompressStream) = NULL;
69
static DLSYM_PROTOTYPE(ZSTD_DStreamInSize) = NULL;
70
static DLSYM_PROTOTYPE(ZSTD_DStreamOutSize) = NULL;
71
static DLSYM_PROTOTYPE(ZSTD_freeCCtx) = NULL;
72
static DLSYM_PROTOTYPE(ZSTD_freeDCtx) = NULL;
73
static DLSYM_PROTOTYPE(ZSTD_getErrorCode) = NULL;
74
static DLSYM_PROTOTYPE(ZSTD_getErrorName) = NULL;
75
static DLSYM_PROTOTYPE(ZSTD_getFrameContentSize) = NULL;
76
static DLSYM_PROTOTYPE(ZSTD_isError) = NULL;
77

78
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_CCtx*, sym_ZSTD_freeCCtx, NULL);
25✔
79
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_DCtx*, sym_ZSTD_freeDCtx, NULL);
248,489✔
80

81
static int zstd_ret_to_errno(size_t ret) {
4✔
82
        switch (sym_ZSTD_getErrorCode(ret)) {
4✔
83
        case ZSTD_error_dstSize_tooSmall:
84
                return -ENOBUFS;
UNCOV
85
        case ZSTD_error_memory_allocation:
×
UNCOV
86
                return -ENOMEM;
×
87
        default:
1✔
88
                return -EBADMSG;
1✔
89
        }
90
}
91
#endif
92

93
#if HAVE_XZ
94
static void *lzma_dl = NULL;
95

96
static DLSYM_PROTOTYPE(lzma_code) = NULL;
97
static DLSYM_PROTOTYPE(lzma_easy_encoder) = NULL;
98
static DLSYM_PROTOTYPE(lzma_end) = NULL;
99
static DLSYM_PROTOTYPE(lzma_stream_buffer_encode) = NULL;
100
static DLSYM_PROTOTYPE(lzma_stream_decoder) = NULL;
101
static DLSYM_PROTOTYPE(lzma_lzma_preset) = NULL;
102

103
/* We can't just do _cleanup_(sym_lzma_end) because a compiler bug makes
104
 * this fail with:
105
 * ../src/basic/compress.c: In function ‘decompress_blob_xz’:
106
 * ../src/basic/compress.c:304:9: error: cleanup argument not a function
107
 *   304 |         _cleanup_(sym_lzma_end) lzma_stream s = LZMA_STREAM_INIT;
108
 *       |         ^~~~~~~~~
109
 */
110
static inline void lzma_end_wrapper(lzma_stream *ls) {
2,832✔
111
        sym_lzma_end(ls);
2,832✔
112
}
113
#endif
114

115
#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
116

117
static const char* const compression_table[_COMPRESSION_MAX] = {
118
        [COMPRESSION_NONE] = "NONE",
119
        [COMPRESSION_XZ]   = "XZ",
120
        [COMPRESSION_LZ4]  = "LZ4",
121
        [COMPRESSION_ZSTD] = "ZSTD",
122
};
123

124
static const char* const compression_lowercase_table[_COMPRESSION_MAX] = {
125
        [COMPRESSION_NONE] = "none",
126
        [COMPRESSION_XZ]   = "xz",
127
        [COMPRESSION_LZ4]  = "lz4",
128
        [COMPRESSION_ZSTD] = "zstd",
129
};
130

131
DEFINE_STRING_TABLE_LOOKUP(compression, Compression);
537✔
132
DEFINE_STRING_TABLE_LOOKUP(compression_lowercase, Compression);
54✔
133

134
bool compression_supported(Compression c) {
2,664✔
135
        static const unsigned supported =
2,664✔
136
                (1U << COMPRESSION_NONE) |
137
                (1U << COMPRESSION_XZ) * HAVE_XZ |
138
                (1U << COMPRESSION_LZ4) * HAVE_LZ4 |
139
                (1U << COMPRESSION_ZSTD) * HAVE_ZSTD;
140

141
        assert(c >= 0);
2,664✔
142
        assert(c < _COMPRESSION_MAX);
2,664✔
143

144
        return BIT_SET(supported, c);
2,664✔
145
}
146

147
#if HAVE_XZ
148
int dlopen_lzma(void) {
2,851✔
149
        ELF_NOTE_DLOPEN("lzma",
2,851✔
150
                        "Support lzma compression in journal and coredump files",
151
                        COMPRESSION_PRIORITY_XZ,
152
                        "liblzma.so.5");
153

154
        return dlopen_many_sym_or_warn(
2,851✔
155
                        &lzma_dl,
156
                        "liblzma.so.5", LOG_DEBUG,
157
                        DLSYM_ARG(lzma_code),
158
                        DLSYM_ARG(lzma_easy_encoder),
159
                        DLSYM_ARG(lzma_end),
160
                        DLSYM_ARG(lzma_stream_buffer_encode),
161
                        DLSYM_ARG(lzma_lzma_preset),
162
                        DLSYM_ARG(lzma_stream_decoder));
163
}
164
#endif
165

166
int compress_blob_xz(const void *src, uint64_t src_size,
18✔
167
                     void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
168

169
        assert(src);
18✔
170
        assert(src_size > 0);
18✔
171
        assert(dst);
18✔
172
        assert(dst_alloc_size > 0);
18✔
173
        assert(dst_size);
18✔
174

175
#if HAVE_XZ
176
        lzma_options_lzma opt = {
18✔
177
                1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
178
                LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4
179
        };
180
        lzma_filter filters[] = {
18✔
181
                { LZMA_FILTER_LZMA2, &opt },
182
                { LZMA_VLI_UNKNOWN, NULL }
183
        };
184
        lzma_ret ret;
18✔
185
        size_t out_pos = 0;
18✔
186
        int r;
18✔
187

188
        r = dlopen_lzma();
18✔
189
        if (r < 0)
18✔
190
                return r;
18✔
191

192
        if (level >= 0) {
18✔
UNCOV
193
                r = sym_lzma_lzma_preset(&opt, (uint32_t) level);
×
194
                if (r < 0)
195
                        return r;
196
        }
197

198
        /* Returns < 0 if we couldn't compress the data or the
199
         * compressed result is longer than the original */
200

201
        if (src_size < 80)
18✔
202
                return -ENOBUFS;
203

204
        ret = sym_lzma_stream_buffer_encode(filters, LZMA_CHECK_NONE, NULL,
18✔
205
                                        src, src_size, dst, &out_pos, dst_alloc_size);
206
        if (ret != LZMA_OK)
18✔
207
                return -ENOBUFS;
208

209
        *dst_size = out_pos;
15✔
210
        return 0;
15✔
211
#else
212
        return -EPROTONOSUPPORT;
213
#endif
214
}
215

216
#if HAVE_LZ4
217
int dlopen_lz4(void) {
3,026✔
218
        ELF_NOTE_DLOPEN("lz4",
3,026✔
219
                        "Support lz4 compression in journal and coredump files",
220
                        COMPRESSION_PRIORITY_LZ4,
221
                        "liblz4.so.1");
222

223
        return dlopen_many_sym_or_warn(
3,026✔
224
                        &lz4_dl,
225
                        "liblz4.so.1", LOG_DEBUG,
226
                        DLSYM_ARG(LZ4F_compressBegin),
227
                        DLSYM_ARG(LZ4F_compressBound),
228
                        DLSYM_ARG(LZ4F_compressEnd),
229
                        DLSYM_ARG(LZ4F_compressUpdate),
230
                        DLSYM_ARG(LZ4F_createCompressionContext),
231
                        DLSYM_ARG(LZ4F_createDecompressionContext),
232
                        DLSYM_ARG(LZ4F_decompress),
233
                        DLSYM_ARG(LZ4F_freeCompressionContext),
234
                        DLSYM_ARG(LZ4F_freeDecompressionContext),
235
                        DLSYM_ARG(LZ4F_isError),
236
                        DLSYM_ARG(LZ4_compress_default),
237
                        DLSYM_ARG(LZ4_compress_HC),
238
                        DLSYM_ARG(LZ4_decompress_safe),
239
                        DLSYM_ARG(LZ4_decompress_safe_partial),
240
                        DLSYM_ARG(LZ4_versionNumber));
241
}
242
#endif
243

244
int compress_blob_lz4(const void *src, uint64_t src_size,
121✔
245
                      void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
246

247
        assert(src);
121✔
248
        assert(src_size > 0);
121✔
249
        assert(dst);
121✔
250
        assert(dst_alloc_size > 0);
121✔
251
        assert(dst_size);
121✔
252

253
#if HAVE_LZ4
254
        int r;
121✔
255

256
        r = dlopen_lz4();
121✔
257
        if (r < 0)
121✔
258
                return r;
259
        /* Returns < 0 if we couldn't compress the data or the
260
         * compressed result is longer than the original */
261

262
        if (src_size < 9)
121✔
263
                return -ENOBUFS;
264

265
        if (level <= 0)
121✔
266
                r = sym_LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
121✔
267
        else
UNCOV
268
                r = sym_LZ4_compress_HC(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8, level);
×
269
        if (r <= 0)
121✔
270
                return -ENOBUFS;
271

272
        unaligned_write_le64(dst, src_size);
114✔
273
        *dst_size = r + 8;
114✔
274

275
        return 0;
114✔
276
#else
277
        return -EPROTONOSUPPORT;
278
#endif
279
}
280

281
#if HAVE_ZSTD
282
int dlopen_zstd(void) {
248,756✔
283
        ELF_NOTE_DLOPEN("zstd",
248,756✔
284
                        "Support zstd compression in journal and coredump files",
285
                        COMPRESSION_PRIORITY_ZSTD,
286
                        "libzstd.so.1");
287

288
        return dlopen_many_sym_or_warn(
248,756✔
289
                        &zstd_dl,
290
                        "libzstd.so.1", LOG_DEBUG,
291
                        DLSYM_ARG(ZSTD_getErrorCode),
292
                        DLSYM_ARG(ZSTD_compress),
293
                        DLSYM_ARG(ZSTD_getFrameContentSize),
294
                        DLSYM_ARG(ZSTD_decompressStream),
295
                        DLSYM_ARG(ZSTD_getErrorName),
296
                        DLSYM_ARG(ZSTD_DStreamOutSize),
297
                        DLSYM_ARG(ZSTD_CStreamInSize),
298
                        DLSYM_ARG(ZSTD_CStreamOutSize),
299
                        DLSYM_ARG(ZSTD_CCtx_setParameter),
300
                        DLSYM_ARG(ZSTD_compressStream2),
301
                        DLSYM_ARG(ZSTD_DStreamInSize),
302
                        DLSYM_ARG(ZSTD_freeCCtx),
303
                        DLSYM_ARG(ZSTD_freeDCtx),
304
                        DLSYM_ARG(ZSTD_isError),
305
                        DLSYM_ARG(ZSTD_createDCtx),
306
                        DLSYM_ARG(ZSTD_createCCtx));
307
}
308
#endif
309

310
int compress_blob_zstd(
235✔
311
                const void *src, uint64_t src_size,
312
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
313

314
        assert(src);
235✔
315
        assert(src_size > 0);
235✔
316
        assert(dst);
235✔
317
        assert(dst_alloc_size > 0);
235✔
318
        assert(dst_size);
235✔
319

320
#if HAVE_ZSTD
321
        size_t k;
235✔
322
        int r;
235✔
323

324
        r = dlopen_zstd();
235✔
325
        if (r < 0)
235✔
326
                return r;
327

328
        k = sym_ZSTD_compress(dst, dst_alloc_size, src, src_size, level < 0 ? 0 : level);
235✔
329
        if (sym_ZSTD_isError(k))
235✔
330
                return zstd_ret_to_errno(k);
3✔
331

332
        *dst_size = k;
232✔
333
        return 0;
232✔
334
#else
335
        return -EPROTONOSUPPORT;
336
#endif
337
}
338

339
int decompress_blob_xz(
2,558✔
340
                const void *src,
341
                uint64_t src_size,
342
                void **dst,
343
                size_t* dst_size,
344
                size_t dst_max) {
345

346
        assert(src);
2,558✔
347
        assert(src_size > 0);
2,558✔
348
        assert(dst);
2,558✔
349
        assert(dst_size);
2,558✔
350

351
#if HAVE_XZ
352
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
2,558✔
353
        lzma_ret ret;
2,558✔
354
        size_t space;
2,558✔
355
        int r;
2,558✔
356

357
        r = dlopen_lzma();
2,558✔
358
        if (r < 0)
2,558✔
359
                return r;
360

361
        ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
2,558✔
362
        if (ret != LZMA_OK)
2,558✔
363
                return -ENOMEM;
364

365
        space = MIN(src_size * 2, dst_max ?: SIZE_MAX);
2,558✔
366
        if (!greedy_realloc(dst, space, 1))
2,558✔
367
                return -ENOMEM;
368

369
        s.next_in = src;
2,558✔
370
        s.avail_in = src_size;
2,558✔
371

372
        s.next_out = *dst;
2,558✔
373
        s.avail_out = space;
2,558✔
374

375
        for (;;) {
2,744✔
376
                size_t used;
2,651✔
377

378
                ret = sym_lzma_code(&s, LZMA_FINISH);
2,651✔
379

380
                if (ret == LZMA_STREAM_END)
2,651✔
381
                        break;
382
                else if (ret != LZMA_OK)
99✔
383
                        return -ENOMEM;
384

385
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
93✔
386
                        break;
387
                else if (dst_max > 0 && space == dst_max)
93✔
388
                        return -ENOBUFS;
389

390
                used = space - s.avail_out;
93✔
391
                space = MIN(2 * space, dst_max ?: SIZE_MAX);
93✔
392
                if (!greedy_realloc(dst, space, 1))
93✔
393
                        return -ENOMEM;
394

395
                s.avail_out = space - used;
93✔
396
                s.next_out = *(uint8_t**)dst + used;
93✔
397
        }
398

399
        *dst_size = space - s.avail_out;
2,552✔
400
        return 0;
2,552✔
401
#else
402
        return -EPROTONOSUPPORT;
403
#endif
404
}
405

406
int decompress_blob_lz4(
2,629✔
407
                const void *src,
408
                uint64_t src_size,
409
                void **dst,
410
                size_t* dst_size,
411
                size_t dst_max) {
412

413
        assert(src);
2,629✔
414
        assert(src_size > 0);
2,629✔
415
        assert(dst);
2,629✔
416
        assert(dst_size);
2,629✔
417

418
#if HAVE_LZ4
419
        char* out;
2,629✔
420
        int r, size; /* LZ4 uses int for size */
2,629✔
421

422
        r = dlopen_lz4();
2,629✔
423
        if (r < 0)
2,629✔
424
                return r;
425

426
        if (src_size <= 8)
2,629✔
427
                return -EBADMSG;
428

429
        size = unaligned_read_le64(src);
2,627✔
430
        if (size < 0 || (unsigned) size != unaligned_read_le64(src))
2,627✔
431
                return -EFBIG;
432
        out = greedy_realloc(dst, size, 1);
2,623✔
433
        if (!out)
2,623✔
434
                return -ENOMEM;
435

436
        r = sym_LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
2,623✔
437
        if (r < 0 || r != size)
2,623✔
438
                return -EBADMSG;
439

440
        *dst_size = size;
2,623✔
441
        return 0;
2,623✔
442
#else
443
        return -EPROTONOSUPPORT;
444
#endif
445
}
446

447
int decompress_blob_zstd(
199,767✔
448
                const void *src,
449
                uint64_t src_size,
450
                void **dst,
451
                size_t *dst_size,
452
                size_t dst_max) {
453

454
        assert(src);
199,767✔
455
        assert(src_size > 0);
199,767✔
456
        assert(dst);
199,767✔
457
        assert(dst_size);
199,767✔
458

459
#if HAVE_ZSTD
460
        uint64_t size;
199,767✔
461
        int r;
199,767✔
462

463
        r = dlopen_zstd();
199,767✔
464
        if (r < 0)
199,767✔
465
                return r;
199,767✔
466

467
        size = sym_ZSTD_getFrameContentSize(src, src_size);
199,767✔
468
        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
199,767✔
469
                return -EBADMSG;
470

471
        if (dst_max > 0 && size > dst_max)
199,761✔
UNCOV
472
                size = dst_max;
×
473
        if (size > SIZE_MAX)
199,761✔
474
                return -E2BIG;
475

476
        if (!(greedy_realloc(dst, MAX(sym_ZSTD_DStreamOutSize(), size), 1)))
199,761✔
477
                return -ENOMEM;
478

479
        _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
399,522✔
480
        if (!dctx)
199,761✔
481
                return -ENOMEM;
482

483
        ZSTD_inBuffer input = {
199,761✔
484
                .src = src,
485
                .size = src_size,
486
        };
487
        ZSTD_outBuffer output = {
399,522✔
488
                .dst = *dst,
199,761✔
489
                .size = MALLOC_SIZEOF_SAFE(*dst),
199,761✔
490
        };
491

492
        size_t k = sym_ZSTD_decompressStream(dctx, &output, &input);
199,761✔
493
        if (sym_ZSTD_isError(k))
199,761✔
UNCOV
494
                return log_debug_errno(zstd_ret_to_errno(k), "ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
495
        if (output.pos < size)
199,761✔
UNCOV
496
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoded less data than indicated, probably corrupted stream.");
×
497

498
        *dst_size = size;
199,761✔
499
        return 0;
199,761✔
500
#else
501
        return -EPROTONOSUPPORT;
502
#endif
503
}
504

505
int decompress_blob(
204,722✔
506
                Compression compression,
507
                const void *src,
508
                uint64_t src_size,
509
                void **dst,
510
                size_t* dst_size,
511
                size_t dst_max) {
512

513
        if (compression == COMPRESSION_XZ)
204,722✔
514
                return decompress_blob_xz(
2,547✔
515
                                src, src_size,
516
                                dst, dst_size, dst_max);
517
        else if (compression == COMPRESSION_LZ4)
202,175✔
518
                return decompress_blob_lz4(
2,519✔
519
                                src, src_size,
520
                                dst, dst_size, dst_max);
521
        else if (compression == COMPRESSION_ZSTD)
199,656✔
522
                return decompress_blob_zstd(
199,656✔
523
                                src, src_size,
524
                                dst, dst_size, dst_max);
525
        else
526
                return -EPROTONOSUPPORT;
527
}
528

529
int decompress_startswith_xz(
270✔
530
                const void *src,
531
                uint64_t src_size,
532
                void **buffer,
533
                const void *prefix,
534
                size_t prefix_len,
535
                uint8_t extra) {
536

537
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
538
         * follow the prefix */
539

540
        assert(src);
270✔
541
        assert(src_size > 0);
270✔
542
        assert(buffer);
270✔
543
        assert(prefix);
270✔
544

545
#if HAVE_XZ
546
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
270✔
547
        size_t allocated;
270✔
548
        lzma_ret ret;
270✔
549
        int r;
270✔
550

551
        r = dlopen_lzma();
270✔
552
        if (r < 0)
270✔
553
                return r;
554

555
        ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
270✔
556
        if (ret != LZMA_OK)
270✔
557
                return -EBADMSG;
558

559
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
560
                return -ENOMEM;
561

562
        allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
563

564
        s.next_in = src;
270✔
565
        s.avail_in = src_size;
270✔
566

567
        s.next_out = *buffer;
270✔
568
        s.avail_out = allocated;
270✔
569

570
        for (;;) {
270✔
571
                ret = sym_lzma_code(&s, LZMA_FINISH);
270✔
572

573
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
270✔
574
                        return -EBADMSG;
575

576
                if (allocated - s.avail_out >= prefix_len + 1)
270✔
577
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
540✔
578
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
267✔
579

UNCOV
580
                if (ret == LZMA_STREAM_END)
×
581
                        return 0;
582

UNCOV
583
                s.avail_out += allocated;
×
584

585
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
586
                        return -ENOMEM;
587

588
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
UNCOV
589
                s.next_out = *(uint8_t**)buffer + allocated - s.avail_out;
×
590
        }
591

592
#else
593
        return -EPROTONOSUPPORT;
594
#endif
595
}
596

597
int decompress_startswith_lz4(
270✔
598
                const void *src,
599
                uint64_t src_size,
600
                void **buffer,
601
                const void *prefix,
602
                size_t prefix_len,
603
                uint8_t extra) {
604

605
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
606
         * follow the prefix */
607

608
        assert(src);
270✔
609
        assert(src_size > 0);
270✔
610
        assert(buffer);
270✔
611
        assert(prefix);
270✔
612

613
#if HAVE_LZ4
614
        size_t allocated;
270✔
615
        int r;
270✔
616

617
        r = dlopen_lz4();
270✔
618
        if (r < 0)
270✔
619
                return r;
620

621
        if (src_size <= 8)
270✔
622
                return -EBADMSG;
623

624
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
625
                return -ENOMEM;
626
        allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
627

628
        r = sym_LZ4_decompress_safe_partial(
540✔
629
                        (char*)src + 8,
630
                        *buffer,
631
                        src_size - 8,
270✔
632
                        prefix_len + 1,
270✔
633
                        allocated);
634

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

UNCOV
642
                if (sym_LZ4_versionNumber() >= 10803)
×
643
                        /* We trust that the newer lz4 decompresses the number of bytes we
644
                         * requested if available in the compressed string. */
645
                        return 0;
×
646

647
                if (r > 0)
×
648
                        /* Compare what we have first, in case of mismatch we can
649
                         * shortcut the full comparison. */
650
                        if (memcmp(*buffer, prefix, r) != 0)
×
651
                                return 0;
652

653
                /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
654
                 * so in pathological cases might need to decompress the full field. */
655
                r = decompress_blob_lz4(src, src_size, buffer, &size, 0);
×
UNCOV
656
                if (r < 0)
×
657
                        return r;
658

UNCOV
659
                if (size < prefix_len + 1)
×
660
                        return 0;
661
        }
662

663
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
270✔
664
                ((const uint8_t*) *buffer)[prefix_len] == extra;
267✔
665
#else
666
        return -EPROTONOSUPPORT;
667
#endif
668
}
669

670
int decompress_startswith_zstd(
48,715✔
671
                const void *src,
672
                uint64_t src_size,
673
                void **buffer,
674
                const void *prefix,
675
                size_t prefix_len,
676
                uint8_t extra) {
677

678
        assert(src);
48,715✔
679
        assert(src_size > 0);
48,715✔
680
        assert(buffer);
48,715✔
681
        assert(prefix);
48,715✔
682

683
#if HAVE_ZSTD
684
        int r;
48,715✔
685

686
        r = dlopen_zstd();
48,715✔
687
        if (r < 0)
48,715✔
688
                return r;
48,715✔
689

690
        uint64_t size = sym_ZSTD_getFrameContentSize(src, src_size);
48,715✔
691
        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
48,715✔
692
                return -EBADMSG;
693

694
        if (size < prefix_len + 1)
48,715✔
695
                return 0; /* Decompressed text too short to match the prefix and extra */
696

697
        _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
97,430✔
698
        if (!dctx)
48,715✔
699
                return -ENOMEM;
700

701
        if (!(greedy_realloc(buffer, MAX(sym_ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
48,715✔
702
                return -ENOMEM;
703

704
        ZSTD_inBuffer input = {
48,715✔
705
                .src = src,
706
                .size = src_size,
707
        };
708
        ZSTD_outBuffer output = {
97,430✔
709
                .dst = *buffer,
48,715✔
710
                .size = MALLOC_SIZEOF_SAFE(*buffer),
48,715✔
711
        };
712
        size_t k;
48,715✔
713

714
        k = sym_ZSTD_decompressStream(dctx, &output, &input);
48,715✔
715
        if (sym_ZSTD_isError(k)) {
48,715✔
UNCOV
716
                log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
UNCOV
717
                return zstd_ret_to_errno(k);
×
718
        }
719
        assert(output.pos >= prefix_len + 1);
48,715✔
720

721
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
48,715✔
722
                ((const uint8_t*) *buffer)[prefix_len] == extra;
375✔
723
#else
724
        return -EPROTONOSUPPORT;
725
#endif
726
}
727

728
int decompress_startswith(
48,457✔
729
                Compression compression,
730
                const void *src,
731
                uint64_t src_size,
732
                void **buffer,
733
                const void *prefix,
734
                size_t prefix_len,
735
                uint8_t extra) {
736

737
        if (compression == COMPRESSION_XZ)
48,457✔
738
                return decompress_startswith_xz(
4✔
739
                                src, src_size,
740
                                buffer,
741
                                prefix, prefix_len,
742
                                extra);
743

744
        else if (compression == COMPRESSION_LZ4)
48,453✔
745
                return decompress_startswith_lz4(
4✔
746
                                src, src_size,
747
                                buffer,
748
                                prefix, prefix_len,
749
                                extra);
750
        else if (compression == COMPRESSION_ZSTD)
48,449✔
751
                return decompress_startswith_zstd(
48,449✔
752
                                src, src_size,
753
                                buffer,
754
                                prefix, prefix_len,
755
                                extra);
756
        else
757
                return -EBADMSG;
758
}
759

760
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
1✔
761
        assert(fdf >= 0);
1✔
762
        assert(fdt >= 0);
1✔
763

764
#if HAVE_XZ
765
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
1✔
766
        lzma_ret ret;
1✔
767
        uint8_t buf[BUFSIZ], out[BUFSIZ];
1✔
768
        lzma_action action = LZMA_RUN;
1✔
769
        int r;
1✔
770

771
        r = dlopen_lzma();
1✔
772
        if (r < 0)
1✔
773
                return r;
774

775
        ret = sym_lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
1✔
776
        if (ret != LZMA_OK)
1✔
UNCOV
777
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
778
                                       "Failed to initialize XZ encoder: code %u",
779
                                       ret);
780

781
        for (;;) {
8✔
782
                if (s.avail_in == 0 && action == LZMA_RUN) {
8✔
783
                        size_t m = sizeof(buf);
7✔
784
                        ssize_t n;
7✔
785

786
                        if (max_bytes != UINT64_MAX && (uint64_t) m > max_bytes)
7✔
UNCOV
787
                                m = (size_t) max_bytes;
×
788

789
                        n = read(fdf, buf, m);
7✔
790
                        if (n < 0)
7✔
UNCOV
791
                                return -errno;
×
792
                        if (n == 0)
7✔
793
                                action = LZMA_FINISH;
794
                        else {
795
                                s.next_in = buf;
6✔
796
                                s.avail_in = n;
6✔
797

798
                                if (max_bytes != UINT64_MAX) {
6✔
UNCOV
799
                                        assert(max_bytes >= (uint64_t) n);
×
UNCOV
800
                                        max_bytes -= n;
×
801
                                }
802
                        }
803
                }
804

805
                if (s.avail_out == 0) {
8✔
806
                        s.next_out = out;
2✔
807
                        s.avail_out = sizeof(out);
2✔
808
                }
809

810
                ret = sym_lzma_code(&s, action);
8✔
811
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
8✔
UNCOV
812
                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
813
                                               "Compression failed: code %u",
814
                                               ret);
815

816
                if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
8✔
817
                        ssize_t n, k;
2✔
818

819
                        n = sizeof(out) - s.avail_out;
2✔
820

821
                        k = loop_write(fdt, out, n);
2✔
822
                        if (k < 0)
2✔
823
                                return k;
824

825
                        if (ret == LZMA_STREAM_END) {
2✔
826
                                if (ret_uncompressed_size)
1✔
827
                                        *ret_uncompressed_size = s.total_in;
1✔
828

829
                                log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
1✔
830
                                          s.total_in, s.total_out,
831
                                          (double) s.total_out / s.total_in * 100);
832

833
                                return 0;
1✔
834
                        }
835
                }
836
        }
837
#else
838
        return -EPROTONOSUPPORT;
839
#endif
840
}
841

842
#define LZ4_BUFSIZE (512*1024u)
843

844
int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
1✔
845

846
#if HAVE_LZ4
847
        LZ4F_errorCode_t c;
1✔
UNCOV
848
        _cleanup_(sym_LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
×
UNCOV
849
        _cleanup_free_ void *in_buff = NULL;
×
850
        _cleanup_free_ char *out_buff = NULL;
1✔
851
        size_t out_allocsize, n, offset = 0, frame_size;
1✔
852
        uint64_t total_in = 0, total_out;
1✔
853
        int r;
1✔
854
        static const LZ4F_preferences_t preferences = {
1✔
855
                .frameInfo.blockSizeID = 5,
856
        };
857

858
        r = dlopen_lz4();
1✔
859
        if (r < 0)
1✔
860
                return r;
861

862
        c = sym_LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
1✔
863
        if (sym_LZ4F_isError(c))
1✔
864
                return -ENOMEM;
865

866
        frame_size = sym_LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
1✔
867
        out_allocsize = frame_size + 64*1024; /* add some space for header and trailer */
1✔
868
        out_buff = malloc(out_allocsize);
1✔
869
        if (!out_buff)
1✔
870
                return -ENOMEM;
871

872
        in_buff = malloc(LZ4_BUFSIZE);
1✔
873
        if (!in_buff)
1✔
874
                return -ENOMEM;
875

876
        n = offset = total_out = sym_LZ4F_compressBegin(ctx, out_buff, out_allocsize, &preferences);
1✔
877
        if (sym_LZ4F_isError(n))
1✔
878
                return -EINVAL;
879

880
        log_debug("Buffer size is %zu bytes, header size %zu bytes.", out_allocsize, n);
1✔
881

882
        for (;;) {
2✔
883
                ssize_t k;
2✔
884

885
                k = loop_read(fdf, in_buff, LZ4_BUFSIZE, true);
2✔
886
                if (k < 0)
2✔
UNCOV
887
                        return k;
×
888
                if (k == 0)
2✔
889
                        break;
890
                n = sym_LZ4F_compressUpdate(ctx, out_buff + offset, out_allocsize - offset,
1✔
891
                                        in_buff, k, NULL);
892
                if (sym_LZ4F_isError(n))
1✔
893
                        return -ENOTRECOVERABLE;
894

895
                total_in += k;
1✔
896
                offset += n;
1✔
897
                total_out += n;
1✔
898

899
                if (max_bytes != UINT64_MAX && total_out > (size_t) max_bytes)
1✔
UNCOV
900
                        return log_debug_errno(SYNTHETIC_ERRNO(EFBIG),
×
901
                                               "Compressed stream longer than %" PRIu64 " bytes", max_bytes);
902

903
                if (out_allocsize - offset < frame_size + 4) {
1✔
UNCOV
904
                        k = loop_write(fdt, out_buff, offset);
×
905
                        if (k < 0)
×
906
                                return k;
907
                        offset = 0;
908
                }
909
        }
910

911
        n = sym_LZ4F_compressEnd(ctx, out_buff + offset, out_allocsize - offset, NULL);
1✔
912
        if (sym_LZ4F_isError(n))
1✔
913
                return -ENOTRECOVERABLE;
914

915
        offset += n;
1✔
916
        total_out += n;
1✔
917
        r = loop_write(fdt, out_buff, offset);
1✔
918
        if (r < 0)
1✔
919
                return r;
920

921
        if (ret_uncompressed_size)
1✔
922
                *ret_uncompressed_size = total_in;
1✔
923

924
        log_debug("LZ4 compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
1✔
925
                  total_in, total_out,
926
                  (double) total_out / total_in * 100);
927

928
        return 0;
929
#else
930
        return -EPROTONOSUPPORT;
931
#endif
932
}
933

934
int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
3✔
935
        assert(fdf >= 0);
3✔
936
        assert(fdt >= 0);
3✔
937

938
#if HAVE_XZ
939
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
3✔
940
        lzma_ret ret;
3✔
941

942
        uint8_t buf[BUFSIZ], out[BUFSIZ];
3✔
943
        lzma_action action = LZMA_RUN;
3✔
944
        int r;
3✔
945

946
        r = dlopen_lzma();
3✔
947
        if (r < 0)
3✔
948
                return r;
949

950
        ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
3✔
951
        if (ret != LZMA_OK)
3✔
UNCOV
952
                return log_debug_errno(SYNTHETIC_ERRNO(ENOMEM),
×
953
                                       "Failed to initialize XZ decoder: code %u",
954
                                       ret);
955

956
        for (;;) {
15✔
957
                if (s.avail_in == 0 && action == LZMA_RUN) {
15✔
958
                        ssize_t n;
5✔
959

960
                        n = read(fdf, buf, sizeof(buf));
5✔
961
                        if (n < 0)
5✔
UNCOV
962
                                return -errno;
×
963
                        if (n == 0)
5✔
964
                                action = LZMA_FINISH;
965
                        else {
966
                                s.next_in = buf;
5✔
967
                                s.avail_in = n;
5✔
968
                        }
969
                }
970

971
                if (s.avail_out == 0) {
15✔
972
                        s.next_out = out;
13✔
973
                        s.avail_out = sizeof(out);
13✔
974
                }
975

976
                ret = sym_lzma_code(&s, action);
15✔
977
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
15✔
978
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
1✔
979
                                               "Decompression failed: code %u",
980
                                               ret);
981

982
                if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
14✔
983
                        ssize_t n, k;
12✔
984

985
                        n = sizeof(out) - s.avail_out;
12✔
986

987
                        if (max_bytes != UINT64_MAX) {
12✔
988
                                if (max_bytes < (uint64_t) n)
12✔
989
                                        return -EFBIG;
990

991
                                max_bytes -= n;
11✔
992
                        }
993

994
                        k = loop_write(fdt, out, n);
11✔
995
                        if (k < 0)
11✔
996
                                return k;
997

998
                        if (ret == LZMA_STREAM_END) {
11✔
999
                                log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
1✔
1000
                                          s.total_in, s.total_out,
1001
                                          (double) s.total_out / s.total_in * 100);
1002

1003
                                return 0;
1✔
1004
                        }
1005
                }
1006
        }
1007
#else
1008
        return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
1009
                               "Cannot decompress file. Compiled without XZ support.");
1010
#endif
1011
}
1012

1013
int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
3✔
1014
#if HAVE_LZ4
1015
        size_t c;
3✔
UNCOV
1016
        _cleanup_(sym_LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
×
1017
        _cleanup_free_ char *buf = NULL;
3✔
1018
        char *src;
3✔
1019
        struct stat st;
3✔
1020
        int r;
3✔
1021
        size_t total_in = 0, total_out = 0;
3✔
1022

1023
        r = dlopen_lz4();
3✔
1024
        if (r < 0)
3✔
1025
                return r;
1026

1027
        c = sym_LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
3✔
1028
        if (sym_LZ4F_isError(c))
3✔
1029
                return -ENOMEM;
1030

1031
        if (fstat(in, &st) < 0)
3✔
UNCOV
1032
                return log_debug_errno(errno, "fstat() failed: %m");
×
1033

1034
        if (file_offset_beyond_memory_size(st.st_size))
3✔
1035
                return -EFBIG;
1036

1037
        buf = malloc(LZ4_BUFSIZE);
3✔
1038
        if (!buf)
3✔
1039
                return -ENOMEM;
1040

1041
        src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0);
3✔
1042
        if (src == MAP_FAILED)
3✔
UNCOV
1043
                return -errno;
×
1044

1045
        while (total_in < (size_t) st.st_size) {
5✔
1046
                size_t produced = LZ4_BUFSIZE;
3✔
1047
                size_t used = st.st_size - total_in;
3✔
1048

1049
                c = sym_LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
3✔
1050
                if (sym_LZ4F_isError(c)) {
3✔
UNCOV
1051
                        r = -EBADMSG;
×
1052
                        goto cleanup;
1✔
1053
                }
1054

1055
                total_in += used;
3✔
1056
                total_out += produced;
3✔
1057

1058
                if (max_bytes != UINT64_MAX && total_out > (size_t) max_bytes) {
3✔
1059
                        log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes);
1✔
1060
                        r = -EFBIG;
1✔
1061
                        goto cleanup;
1✔
1062
                }
1063

1064
                r = loop_write(out, buf, produced);
2✔
1065
                if (r < 0)
2✔
UNCOV
1066
                        goto cleanup;
×
1067
        }
1068

1069
        log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
2✔
1070
                  total_in, total_out,
1071
                  total_in > 0 ? (double) total_out / total_in * 100 : 0.0);
1072
        r = 0;
1073
 cleanup:
3✔
1074
        munmap(src, st.st_size);
3✔
1075
        return r;
3✔
1076
#else
1077
        return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
1078
                               "Cannot decompress file. Compiled without LZ4 support.");
1079
#endif
1080
}
1081

1082
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
25✔
1083
        assert(fdf >= 0);
25✔
1084
        assert(fdt >= 0);
25✔
1085

1086
#if HAVE_ZSTD
UNCOV
1087
        _cleanup_(sym_ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
×
1088
        _cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
25✔
1089
        size_t in_allocsize, out_allocsize;
25✔
1090
        size_t z;
25✔
1091
        uint64_t left = max_bytes, in_bytes = 0;
25✔
1092
        int r;
25✔
1093

1094
        r = dlopen_zstd();
25✔
1095
        if (r < 0)
25✔
1096
                return r;
1097

1098
        /* Create the context and buffers */
1099
        in_allocsize = sym_ZSTD_CStreamInSize();
25✔
1100
        out_allocsize = sym_ZSTD_CStreamOutSize();
25✔
1101
        in_buff = malloc(in_allocsize);
25✔
1102
        out_buff = malloc(out_allocsize);
25✔
1103
        cctx = sym_ZSTD_createCCtx();
25✔
1104
        if (!cctx || !out_buff || !in_buff)
25✔
1105
                return -ENOMEM;
1106

1107
        z = sym_ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1);
25✔
1108
        if (sym_ZSTD_isError(z))
25✔
UNCOV
1109
                log_debug("Failed to enable ZSTD checksum, ignoring: %s", sym_ZSTD_getErrorName(z));
×
1110

1111
        /* This loop read from the input file, compresses that entire chunk,
1112
         * and writes all output produced to the output file.
1113
         */
1114
        for (;;) {
239✔
1115
                bool is_last_chunk;
264✔
1116
                ZSTD_inBuffer input = {
264✔
1117
                        .src = in_buff,
1118
                        .size = 0,
1119
                        .pos = 0
1120
                };
1121
                ssize_t red;
264✔
1122

1123
                red = loop_read(fdf, in_buff, in_allocsize, true);
264✔
1124
                if (red < 0)
264✔
UNCOV
1125
                        return red;
×
1126
                is_last_chunk = red == 0;
264✔
1127

1128
                in_bytes += (size_t) red;
264✔
1129
                input.size = (size_t) red;
264✔
1130

1131
                for (bool finished = false; !finished;) {
528✔
1132
                        ZSTD_outBuffer output = {
264✔
1133
                                .dst = out_buff,
1134
                                .size = out_allocsize,
1135
                                .pos = 0
1136
                        };
1137
                        size_t remaining;
264✔
1138
                        ssize_t wrote;
264✔
1139

1140
                        /* Compress into the output buffer and write all of the
1141
                         * output to the file so we can reuse the buffer next
1142
                         * iteration.
1143
                         */
1144
                        remaining = sym_ZSTD_compressStream2(
503✔
1145
                                cctx, &output, &input,
1146
                                is_last_chunk ? ZSTD_e_end : ZSTD_e_continue);
1147

1148
                        if (sym_ZSTD_isError(remaining)) {
264✔
UNCOV
1149
                                log_debug("ZSTD encoder failed: %s", sym_ZSTD_getErrorName(remaining));
×
UNCOV
1150
                                return zstd_ret_to_errno(remaining);
×
1151
                        }
1152

1153
                        if (left < output.pos)
264✔
1154
                                return -EFBIG;
1155

1156
                        wrote = loop_write_full(fdt, output.dst, output.pos, USEC_INFINITY);
264✔
1157
                        if (wrote < 0)
264✔
1158
                                return wrote;
1159

1160
                        left -= output.pos;
264✔
1161

1162
                        /* If we're on the last chunk we're finished when zstd
1163
                         * returns 0, which means its consumed all the input AND
1164
                         * finished the frame. Otherwise, we're finished when
1165
                         * we've consumed all the input.
1166
                         */
1167
                        finished = is_last_chunk ? (remaining == 0) : (input.pos == input.size);
264✔
1168
                }
1169

1170
                /* zstd only returns 0 when the input is completely consumed */
1171
                assert(input.pos == input.size);
264✔
1172
                if (is_last_chunk)
264✔
1173
                        break;
1174
        }
1175

1176
        if (ret_uncompressed_size)
25✔
1177
                *ret_uncompressed_size = in_bytes;
25✔
1178

1179
        if (in_bytes > 0)
25✔
1180
                log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
25✔
1181
                          in_bytes, max_bytes - left, (double) (max_bytes - left) / in_bytes * 100);
1182
        else
UNCOV
1183
                log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes)",
×
1184
                          in_bytes, max_bytes - left);
1185

1186
        return 0;
1187
#else
1188
        return -EPROTONOSUPPORT;
1189
#endif
1190
}
1191

1192
int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
13✔
1193
        assert(fdf >= 0);
13✔
1194
        assert(fdt >= 0);
13✔
1195

1196
#if HAVE_ZSTD
UNCOV
1197
        _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = NULL;
×
1198
        _cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
13✔
1199
        size_t in_allocsize, out_allocsize;
13✔
1200
        size_t last_result = 0;
13✔
1201
        uint64_t left = max_bytes, in_bytes = 0;
13✔
1202
        int r;
13✔
1203

1204
        r = dlopen_zstd();
13✔
1205
        if (r < 0)
13✔
1206
                return r;
1207
        /* Create the context and buffers */
1208
        in_allocsize = sym_ZSTD_DStreamInSize();
13✔
1209
        out_allocsize = sym_ZSTD_DStreamOutSize();
13✔
1210
        in_buff = malloc(in_allocsize);
13✔
1211
        out_buff = malloc(out_allocsize);
13✔
1212
        dctx = sym_ZSTD_createDCtx();
13✔
1213
        if (!dctx || !out_buff || !in_buff)
13✔
1214
                return -ENOMEM;
1215

1216
        /* This loop assumes that the input file is one or more concatenated
1217
         * zstd streams. This example won't work if there is trailing non-zstd
1218
         * data at the end, but streaming decompression in general handles this
1219
         * case. ZSTD_decompressStream() returns 0 exactly when the frame is
1220
         * completed, and doesn't consume input after the frame.
1221
         */
1222
        for (;;) {
33✔
1223
                bool has_error = false;
23✔
1224
                ZSTD_inBuffer input = {
23✔
1225
                        .src = in_buff,
1226
                        .size = 0,
1227
                        .pos = 0
1228
                };
1229
                ssize_t red;
23✔
1230

1231
                red = loop_read(fdf, in_buff, in_allocsize, true);
23✔
1232
                if (red < 0)
23✔
1233
                        return red;
2✔
1234
                if (red == 0)
23✔
1235
                        break;
1236

1237
                in_bytes += (size_t) red;
13✔
1238
                input.size = (size_t) red;
13✔
1239
                input.pos = 0;
13✔
1240

1241
                /* Given a valid frame, zstd won't consume the last byte of the
1242
                 * frame until it has flushed all of the decompressed data of
1243
                 * the frame. So input.pos < input.size means frame is not done
1244
                 * or there is still output available.
1245
                 */
1246
                while (input.pos < input.size) {
59✔
1247
                        ZSTD_outBuffer output = {
49✔
1248
                                .dst = out_buff,
1249
                                .size = out_allocsize,
1250
                                .pos = 0
1251
                        };
1252
                        ssize_t wrote;
49✔
1253
                        /* The return code is zero if the frame is complete, but
1254
                         * there may be multiple frames concatenated together.
1255
                         * Zstd will automatically reset the context when a
1256
                         * frame is complete. Still, calling ZSTD_DCtx_reset()
1257
                         * can be useful to reset the context to a clean state,
1258
                         * for instance if the last decompression call returned
1259
                         * an error.
1260
                         */
1261
                        last_result = sym_ZSTD_decompressStream(dctx, &output, &input);
49✔
1262
                        if (sym_ZSTD_isError(last_result)) {
49✔
1263
                                has_error = true;
1✔
1264
                                break;
1✔
1265
                        }
1266

1267
                        if (left < output.pos)
48✔
1268
                                return -EFBIG;
2✔
1269

1270
                        wrote = loop_write_full(fdt, output.dst, output.pos, USEC_INFINITY);
47✔
1271
                        if (wrote < 0)
47✔
1272
                                return wrote;
1273

1274
                        left -= output.pos;
46✔
1275
                }
1276
                if (has_error)
11✔
1277
                        break;
1278
        }
1279

1280
        if (in_bytes == 0)
11✔
UNCOV
1281
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoder failed: no data read");
×
1282

1283
        if (last_result != 0) {
11✔
1284
                /* The last return value from ZSTD_decompressStream did not end
1285
                 * on a frame, but we reached the end of the file! We assume
1286
                 * this is an error, and the input was truncated.
1287
                 */
1288
                log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(last_result));
1✔
1289
                return zstd_ret_to_errno(last_result);
1✔
1290
        }
1291

1292
        log_debug(
10✔
1293
                "ZSTD decompression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
1294
                in_bytes,
1295
                max_bytes - left,
1296
                (double) (max_bytes - left) / in_bytes * 100);
1297
        return 0;
1298
#else
1299
        return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
1300
                               "Cannot decompress file. Compiled without ZSTD support.");
1301
#endif
1302
}
1303

1304
int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) {
10✔
1305

1306
        if (endswith(filename, ".lz4"))
10✔
UNCOV
1307
                return decompress_stream_lz4(fdf, fdt, max_bytes);
×
1308
        else if (endswith(filename, ".xz"))
10✔
UNCOV
1309
                return decompress_stream_xz(fdf, fdt, max_bytes);
×
1310
        else if (endswith(filename, ".zst"))
10✔
1311
                return decompress_stream_zstd(fdf, fdt, max_bytes);
10✔
1312
        else
1313
                return -EPROTONOSUPPORT;
1314
}
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