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

systemd / systemd / 20321298054

17 Dec 2025 11:19PM UTC coverage: 72.709% (-0.03%) from 72.736%
20321298054

push

github

YHNdnzj
core: set Result=start-limit-hit when a unit is rate limited

There is currently no way to figure out a rate limit was hit on a unit,
as the last result is stripped in order to keep reporting the first
result, which is useful in case of a watchdog failure, which is the
reason why it was changed as such.

But rate limiting is also an important information to provide to
users, so allow the Result property to reflect it when it
happens.

6 of 7 new or added lines in 7 files covered. (85.71%)

1358 existing lines in 49 files now uncovered.

309654 of 425883 relevant lines covered (72.71%)

1138050.05 hits per line

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

89.87
/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_RENAME(LZ4F_compressionContext_t, sym_LZ4F_freeCompressionContext, LZ4F_freeCompressionContextp, NULL);
1✔
55
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(LZ4F_decompressionContext_t, sym_LZ4F_freeDecompressionContext, LZ4F_freeDecompressionContextp, 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_RENAME(ZSTD_CCtx*, sym_ZSTD_freeCCtx, ZSTD_freeCCtxp, NULL);
21✔
79
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(ZSTD_DCtx*, sym_ZSTD_freeDCtx, ZSTD_freeDCtxp, NULL);
4,716,509✔
80

81
static int zstd_ret_to_errno(size_t ret) {
8✔
82
        switch (sym_ZSTD_getErrorCode(ret)) {
8✔
83
        case ZSTD_error_dstSize_tooSmall:
84
                return -ENOBUFS;
85
        case ZSTD_error_memory_allocation:
×
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,858✔
111
        sym_lzma_end(ls);
2,858✔
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);
492✔
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
int dlopen_lzma(void) {
2,877✔
148
#if HAVE_XZ
149
        ELF_NOTE_DLOPEN("lzma",
2,877✔
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,877✔
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
#else
164
        return -EOPNOTSUPP;
165
#endif
166
}
167

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

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

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

190
        r = dlopen_lzma();
18✔
191
        if (r < 0)
18✔
192
                return r;
18✔
193

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

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

203
        if (src_size < 80)
18✔
204
                return -ENOBUFS;
205

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

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

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

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

248
int compress_blob_lz4(const void *src, uint64_t src_size,
167✔
249
                      void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
250

251
        assert(src);
167✔
252
        assert(src_size > 0);
167✔
253
        assert(dst);
167✔
254
        assert(dst_alloc_size > 0);
167✔
255
        assert(dst_size);
167✔
256

257
#if HAVE_LZ4
258
        int r;
167✔
259

260
        r = dlopen_lz4();
167✔
261
        if (r < 0)
167✔
262
                return r;
263
        /* Returns < 0 if we couldn't compress the data or the
264
         * compressed result is longer than the original */
265

266
        if (src_size < 9)
167✔
267
                return -ENOBUFS;
268

269
        if (level <= 0)
167✔
270
                r = sym_LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
167✔
271
        else
UNCOV
272
                r = sym_LZ4_compress_HC(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8, level);
×
273
        if (r <= 0)
167✔
274
                return -ENOBUFS;
275

276
        unaligned_write_le64(dst, src_size);
157✔
277
        *dst_size = r + 8;
157✔
278

279
        return 0;
157✔
280
#else
281
        return -EPROTONOSUPPORT;
282
#endif
283
}
284

285
int dlopen_zstd(void) {
4,716,823✔
286
#if HAVE_ZSTD
287
        ELF_NOTE_DLOPEN("zstd",
4,716,823✔
288
                        "Support zstd compression in journal and coredump files",
289
                        COMPRESSION_PRIORITY_ZSTD,
290
                        "libzstd.so.1");
291

292
        return dlopen_many_sym_or_warn(
4,716,823✔
293
                        &zstd_dl,
294
                        "libzstd.so.1", LOG_DEBUG,
295
                        DLSYM_ARG(ZSTD_getErrorCode),
296
                        DLSYM_ARG(ZSTD_compress),
297
                        DLSYM_ARG(ZSTD_getFrameContentSize),
298
                        DLSYM_ARG(ZSTD_decompressStream),
299
                        DLSYM_ARG(ZSTD_getErrorName),
300
                        DLSYM_ARG(ZSTD_DStreamOutSize),
301
                        DLSYM_ARG(ZSTD_CStreamInSize),
302
                        DLSYM_ARG(ZSTD_CStreamOutSize),
303
                        DLSYM_ARG(ZSTD_CCtx_setParameter),
304
                        DLSYM_ARG(ZSTD_compressStream2),
305
                        DLSYM_ARG(ZSTD_DStreamInSize),
306
                        DLSYM_ARG(ZSTD_freeCCtx),
307
                        DLSYM_ARG(ZSTD_freeDCtx),
308
                        DLSYM_ARG(ZSTD_isError),
309
                        DLSYM_ARG(ZSTD_createDCtx),
310
                        DLSYM_ARG(ZSTD_createCCtx));
311
#else
312
        return -EOPNOTSUPP;
313
#endif
314
}
315

316
int compress_blob_zstd(
286✔
317
                const void *src, uint64_t src_size,
318
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
319

320
        assert(src);
286✔
321
        assert(src_size > 0);
286✔
322
        assert(dst);
286✔
323
        assert(dst_alloc_size > 0);
286✔
324
        assert(dst_size);
286✔
325

326
#if HAVE_ZSTD
327
        size_t k;
286✔
328
        int r;
286✔
329

330
        r = dlopen_zstd();
286✔
331
        if (r < 0)
286✔
332
                return r;
333

334
        k = sym_ZSTD_compress(dst, dst_alloc_size, src, src_size, level < 0 ? 0 : level);
286✔
335
        if (sym_ZSTD_isError(k))
286✔
336
                return zstd_ret_to_errno(k);
7✔
337

338
        *dst_size = k;
279✔
339
        return 0;
279✔
340
#else
341
        return -EPROTONOSUPPORT;
342
#endif
343
}
344

345
int decompress_blob_xz(
2,584✔
346
                const void *src,
347
                uint64_t src_size,
348
                void **dst,
349
                size_t* dst_size,
350
                size_t dst_max) {
351

352
        assert(src);
2,584✔
353
        assert(src_size > 0);
2,584✔
354
        assert(dst);
2,584✔
355
        assert(dst_size);
2,584✔
356

357
#if HAVE_XZ
358
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
2,584✔
359
        lzma_ret ret;
2,584✔
360
        size_t space;
2,584✔
361
        int r;
2,584✔
362

363
        r = dlopen_lzma();
2,584✔
364
        if (r < 0)
2,584✔
365
                return r;
366

367
        ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
2,584✔
368
        if (ret != LZMA_OK)
2,584✔
369
                return -ENOMEM;
370

371
        space = MIN(src_size * 2, dst_max ?: SIZE_MAX);
2,584✔
372
        if (!greedy_realloc(dst, space, 1))
2,584✔
373
                return -ENOMEM;
374

375
        s.next_in = src;
2,584✔
376
        s.avail_in = src_size;
2,584✔
377

378
        s.next_out = *dst;
2,584✔
379
        s.avail_out = space;
2,584✔
380

381
        for (;;) {
2,770✔
382
                size_t used;
2,677✔
383

384
                ret = sym_lzma_code(&s, LZMA_FINISH);
2,677✔
385
                if (ret == LZMA_STREAM_END)
2,677✔
386
                        break;
387
                if (ret != LZMA_OK)
99✔
388
                        return -ENOMEM;
389

390
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
93✔
391
                        break;
392
                if (dst_max > 0 && space == dst_max)
93✔
393
                        return -ENOBUFS;
394

395
                used = space - s.avail_out;
93✔
396
                space = MIN(2 * space, dst_max ?: SIZE_MAX);
93✔
397
                if (!greedy_realloc(dst, space, 1))
93✔
398
                        return -ENOMEM;
399

400
                s.avail_out = space - used;
93✔
401
                s.next_out = *(uint8_t**)dst + used;
93✔
402
        }
403

404
        *dst_size = space - s.avail_out;
2,578✔
405
        return 0;
2,578✔
406
#else
407
        return -EPROTONOSUPPORT;
408
#endif
409
}
410

411
int decompress_blob_lz4(
2,725✔
412
                const void *src,
413
                uint64_t src_size,
414
                void **dst,
415
                size_t* dst_size,
416
                size_t dst_max) {
417

418
        assert(src);
2,725✔
419
        assert(src_size > 0);
2,725✔
420
        assert(dst);
2,725✔
421
        assert(dst_size);
2,725✔
422

423
#if HAVE_LZ4
424
        char* out;
2,725✔
425
        int r, size; /* LZ4 uses int for size */
2,725✔
426

427
        r = dlopen_lz4();
2,725✔
428
        if (r < 0)
2,725✔
429
                return r;
430

431
        if (src_size <= 8)
2,725✔
432
                return -EBADMSG;
433

434
        size = unaligned_read_le64(src);
2,723✔
435
        if (size < 0 || (unsigned) size != unaligned_read_le64(src))
2,723✔
436
                return -EFBIG;
437
        out = greedy_realloc(dst, size, 1);
2,719✔
438
        if (!out)
2,719✔
439
                return -ENOMEM;
440

441
        r = sym_LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
2,719✔
442
        if (r < 0 || r != size)
2,719✔
443
                return -EBADMSG;
444

445
        *dst_size = size;
2,719✔
446
        return 0;
2,719✔
447
#else
448
        return -EPROTONOSUPPORT;
449
#endif
450
}
451

452
int decompress_blob_zstd(
239,376✔
453
                const void *src,
454
                uint64_t src_size,
455
                void **dst,
456
                size_t *dst_size,
457
                size_t dst_max) {
458

459
        assert(src);
239,376✔
460
        assert(src_size > 0);
239,376✔
461
        assert(dst);
239,376✔
462
        assert(dst_size);
239,376✔
463

464
#if HAVE_ZSTD
465
        uint64_t size;
239,376✔
466
        int r;
239,376✔
467

468
        r = dlopen_zstd();
239,376✔
469
        if (r < 0)
239,376✔
470
                return r;
239,376✔
471

472
        size = sym_ZSTD_getFrameContentSize(src, src_size);
239,376✔
473
        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
239,376✔
474
                return -EBADMSG;
475

476
        if (dst_max > 0 && size > dst_max)
239,370✔
477
                size = dst_max;
×
478
        if (size > SIZE_MAX)
239,370✔
479
                return -E2BIG;
480

481
        if (!(greedy_realloc(dst, MAX(sym_ZSTD_DStreamOutSize(), size), 1)))
239,370✔
482
                return -ENOMEM;
483

484
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
478,740✔
485
        if (!dctx)
239,370✔
486
                return -ENOMEM;
487

488
        ZSTD_inBuffer input = {
239,370✔
489
                .src = src,
490
                .size = src_size,
491
        };
492
        ZSTD_outBuffer output = {
478,740✔
493
                .dst = *dst,
239,370✔
494
                .size = MALLOC_SIZEOF_SAFE(*dst),
239,370✔
495
        };
496

497
        size_t k = sym_ZSTD_decompressStream(dctx, &output, &input);
239,370✔
498
        if (sym_ZSTD_isError(k))
239,370✔
499
                return log_debug_errno(zstd_ret_to_errno(k), "ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
500
        if (output.pos < size)
239,370✔
501
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoded less data than indicated, probably corrupted stream.");
×
502

503
        *dst_size = size;
239,370✔
504
        return 0;
239,370✔
505
#else
506
        return -EPROTONOSUPPORT;
507
#endif
508
}
509

510
int decompress_blob(
244,369✔
511
                Compression compression,
512
                const void *src,
513
                uint64_t src_size,
514
                void **dst,
515
                size_t* dst_size,
516
                size_t dst_max) {
517

518
        switch (compression) {
244,369✔
519
        case COMPRESSION_XZ:
2,573✔
520
                return decompress_blob_xz(
2,573✔
521
                                src, src_size,
522
                                dst, dst_size, dst_max);
523
        case COMPRESSION_LZ4:
2,572✔
524
                return decompress_blob_lz4(
2,572✔
525
                                src, src_size,
526
                                dst, dst_size, dst_max);
527
        case COMPRESSION_ZSTD:
239,224✔
528
                return decompress_blob_zstd(
239,224✔
529
                                src, src_size,
530
                                dst, dst_size, dst_max);
531
        default:
532
                return -EPROTONOSUPPORT;
533
        }
534
}
535

536
int decompress_startswith_xz(
270✔
537
                const void *src,
538
                uint64_t src_size,
539
                void **buffer,
540
                const void *prefix,
541
                size_t prefix_len,
542
                uint8_t extra) {
543

544
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
545
         * follow the prefix */
546

547
        assert(src);
270✔
548
        assert(src_size > 0);
270✔
549
        assert(buffer);
270✔
550
        assert(prefix);
270✔
551

552
#if HAVE_XZ
553
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
270✔
554
        size_t allocated;
270✔
555
        lzma_ret ret;
270✔
556
        int r;
270✔
557

558
        r = dlopen_lzma();
270✔
559
        if (r < 0)
270✔
560
                return r;
561

562
        ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
270✔
563
        if (ret != LZMA_OK)
270✔
564
                return -EBADMSG;
565

566
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
567
                return -ENOMEM;
568

569
        allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
570

571
        s.next_in = src;
270✔
572
        s.avail_in = src_size;
270✔
573

574
        s.next_out = *buffer;
270✔
575
        s.avail_out = allocated;
270✔
576

577
        for (;;) {
270✔
578
                ret = sym_lzma_code(&s, LZMA_FINISH);
270✔
579

580
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
270✔
581
                        return -EBADMSG;
582

583
                if (allocated - s.avail_out >= prefix_len + 1)
270✔
584
                        return memcmp(*buffer, prefix, prefix_len) == 0 &&
540✔
585
                                ((const uint8_t*) *buffer)[prefix_len] == extra;
267✔
586

587
                if (ret == LZMA_STREAM_END)
×
588
                        return 0;
589

590
                s.avail_out += allocated;
×
591

592
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
593
                        return -ENOMEM;
594

595
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
596
                s.next_out = *(uint8_t**)buffer + allocated - s.avail_out;
×
597
        }
598

599
#else
600
        return -EPROTONOSUPPORT;
601
#endif
602
}
603

604
int decompress_startswith_lz4(
270✔
605
                const void *src,
606
                uint64_t src_size,
607
                void **buffer,
608
                const void *prefix,
609
                size_t prefix_len,
610
                uint8_t extra) {
611

612
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
613
         * follow the prefix */
614

615
        assert(src);
270✔
616
        assert(src_size > 0);
270✔
617
        assert(buffer);
270✔
618
        assert(prefix);
270✔
619

620
#if HAVE_LZ4
621
        size_t allocated;
270✔
622
        int r;
270✔
623

624
        r = dlopen_lz4();
270✔
625
        if (r < 0)
270✔
626
                return r;
627

628
        if (src_size <= 8)
270✔
629
                return -EBADMSG;
630

631
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
632
                return -ENOMEM;
633
        allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
634

635
        r = sym_LZ4_decompress_safe_partial(
540✔
636
                        (char*)src + 8,
637
                        *buffer,
638
                        src_size - 8,
270✔
639
                        prefix_len + 1,
270✔
640
                        allocated);
641

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

649
                if (sym_LZ4_versionNumber() >= 10803)
×
650
                        /* We trust that the newer lz4 decompresses the number of bytes we
651
                         * requested if available in the compressed string. */
652
                        return 0;
×
653

654
                if (r > 0)
×
655
                        /* Compare what we have first, in case of mismatch we can
656
                         * shortcut the full comparison. */
657
                        if (memcmp(*buffer, prefix, r) != 0)
×
658
                                return 0;
659

660
                /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
661
                 * so in pathological cases might need to decompress the full field. */
662
                r = decompress_blob_lz4(src, src_size, buffer, &size, 0);
×
663
                if (r < 0)
×
664
                        return r;
665

666
                if (size < prefix_len + 1)
×
667
                        return 0;
668
        }
669

670
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
270✔
671
                ((const uint8_t*) *buffer)[prefix_len] == extra;
267✔
672
#else
673
        return -EPROTONOSUPPORT;
674
#endif
675
}
676

677
int decompress_startswith_zstd(
4,477,126✔
678
                const void *src,
679
                uint64_t src_size,
680
                void **buffer,
681
                const void *prefix,
682
                size_t prefix_len,
683
                uint8_t extra) {
684

685
        assert(src);
4,477,126✔
686
        assert(src_size > 0);
4,477,126✔
687
        assert(buffer);
4,477,126✔
688
        assert(prefix);
4,477,126✔
689

690
#if HAVE_ZSTD
691
        int r;
4,477,126✔
692

693
        r = dlopen_zstd();
4,477,126✔
694
        if (r < 0)
4,477,126✔
695
                return r;
4,477,126✔
696

697
        uint64_t size = sym_ZSTD_getFrameContentSize(src, src_size);
4,477,126✔
698
        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
4,477,126✔
699
                return -EBADMSG;
700

701
        if (size < prefix_len + 1)
4,477,126✔
702
                return 0; /* Decompressed text too short to match the prefix and extra */
703

704
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
8,954,252✔
705
        if (!dctx)
4,477,126✔
706
                return -ENOMEM;
707

708
        if (!(greedy_realloc(buffer, MAX(sym_ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
4,477,126✔
709
                return -ENOMEM;
710

711
        ZSTD_inBuffer input = {
4,477,126✔
712
                .src = src,
713
                .size = src_size,
714
        };
715
        ZSTD_outBuffer output = {
8,954,252✔
716
                .dst = *buffer,
4,477,126✔
717
                .size = MALLOC_SIZEOF_SAFE(*buffer),
4,477,126✔
718
        };
719
        size_t k;
4,477,126✔
720

721
        k = sym_ZSTD_decompressStream(dctx, &output, &input);
4,477,126✔
722
        if (sym_ZSTD_isError(k)) {
4,477,126✔
723
                log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
724
                return zstd_ret_to_errno(k);
×
725
        }
726
        assert(output.pos >= prefix_len + 1);
4,477,126✔
727

728
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
4,477,126✔
729
                ((const uint8_t*) *buffer)[prefix_len] == extra;
15,206✔
730
#else
731
        return -EPROTONOSUPPORT;
732
#endif
733
}
734

735
int decompress_startswith(
4,476,868✔
736
                Compression compression,
737
                const void *src,
738
                uint64_t src_size,
739
                void **buffer,
740
                const void *prefix,
741
                size_t prefix_len,
742
                uint8_t extra) {
743

744
        switch (compression) {
4,476,868✔
745

746
        case COMPRESSION_XZ:
4✔
747
                return decompress_startswith_xz(
4✔
748
                                src, src_size,
749
                                buffer,
750
                                prefix, prefix_len,
751
                                extra);
752

753
        case COMPRESSION_LZ4:
4✔
754
                return decompress_startswith_lz4(
4✔
755
                                src, src_size,
756
                                buffer,
757
                                prefix, prefix_len,
758
                                extra);
759
        case COMPRESSION_ZSTD:
4,476,860✔
760
                return decompress_startswith_zstd(
4,476,860✔
761
                                src, src_size,
762
                                buffer,
763
                                prefix, prefix_len,
764
                                extra);
765
        default:
766
                return -EBADMSG;
767
        }
768
}
769

770
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
1✔
771
        assert(fdf >= 0);
1✔
772
        assert(fdt >= 0);
1✔
773

774
#if HAVE_XZ
775
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
1✔
776
        lzma_ret ret;
1✔
777
        uint8_t buf[BUFSIZ], out[BUFSIZ];
1✔
778
        lzma_action action = LZMA_RUN;
1✔
779
        int r;
1✔
780

781
        r = dlopen_lzma();
1✔
782
        if (r < 0)
1✔
783
                return r;
784

785
        ret = sym_lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
1✔
786
        if (ret != LZMA_OK)
1✔
787
                return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
×
788
                                       "Failed to initialize XZ encoder: code %u",
789
                                       ret);
790

791
        for (;;) {
8✔
792
                if (s.avail_in == 0 && action == LZMA_RUN) {
8✔
793
                        size_t m = sizeof(buf);
7✔
794
                        ssize_t n;
7✔
795

796
                        if (max_bytes != UINT64_MAX && (uint64_t) m > max_bytes)
7✔
797
                                m = (size_t) max_bytes;
×
798

799
                        n = read(fdf, buf, m);
7✔
800
                        if (n < 0)
7✔
801
                                return -errno;
×
802
                        if (n == 0)
7✔
803
                                action = LZMA_FINISH;
804
                        else {
805
                                s.next_in = buf;
6✔
806
                                s.avail_in = n;
6✔
807

808
                                if (max_bytes != UINT64_MAX) {
6✔
809
                                        assert(max_bytes >= (uint64_t) n);
×
810
                                        max_bytes -= n;
×
811
                                }
812
                        }
813
                }
814

815
                if (s.avail_out == 0) {
8✔
816
                        s.next_out = out;
2✔
817
                        s.avail_out = sizeof(out);
2✔
818
                }
819

820
                ret = sym_lzma_code(&s, action);
8✔
821
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
8✔
822
                        return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
×
823
                                               "Compression failed: code %u",
824
                                               ret);
825

826
                if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
8✔
827
                        ssize_t n, k;
2✔
828

829
                        n = sizeof(out) - s.avail_out;
2✔
830

831
                        k = loop_write(fdt, out, n);
2✔
832
                        if (k < 0)
2✔
833
                                return k;
834

835
                        if (ret == LZMA_STREAM_END) {
2✔
836
                                if (ret_uncompressed_size)
1✔
837
                                        *ret_uncompressed_size = s.total_in;
1✔
838

839
                                if (s.total_in == 0)
1✔
840
                                        log_debug("XZ compression finished (no input data)");
×
841
                                else
842
                                        log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
1✔
843
                                                  s.total_in, s.total_out,
844
                                                  (double) s.total_out / s.total_in * 100);
845

846
                                return 0;
1✔
847
                        }
848
                }
849
        }
850
#else
851
        return -EPROTONOSUPPORT;
852
#endif
853
}
854

855
#define LZ4_BUFSIZE (512*1024u)
856

857
int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
1✔
858

859
#if HAVE_LZ4
860
        LZ4F_errorCode_t c;
1✔
861
        _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
×
862
        _cleanup_free_ void *in_buff = NULL;
×
863
        _cleanup_free_ char *out_buff = NULL;
1✔
864
        size_t out_allocsize, n, offset = 0, frame_size;
1✔
865
        uint64_t total_in = 0, total_out;
1✔
866
        int r;
1✔
867
        static const LZ4F_preferences_t preferences = {
1✔
868
                .frameInfo.blockSizeID = 5,
869
        };
870

871
        r = dlopen_lz4();
1✔
872
        if (r < 0)
1✔
873
                return r;
874

875
        c = sym_LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
1✔
876
        if (sym_LZ4F_isError(c))
1✔
877
                return -ENOMEM;
878

879
        frame_size = sym_LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
1✔
880
        out_allocsize = frame_size + 64*1024; /* add some space for header and trailer */
1✔
881
        out_buff = malloc(out_allocsize);
1✔
882
        if (!out_buff)
1✔
883
                return -ENOMEM;
884

885
        in_buff = malloc(LZ4_BUFSIZE);
1✔
886
        if (!in_buff)
1✔
887
                return -ENOMEM;
888

889
        n = offset = total_out = sym_LZ4F_compressBegin(ctx, out_buff, out_allocsize, &preferences);
1✔
890
        if (sym_LZ4F_isError(n))
1✔
891
                return -EINVAL;
892

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

895
        for (;;) {
2✔
896
                ssize_t k;
2✔
897

898
                k = loop_read(fdf, in_buff, LZ4_BUFSIZE, true);
2✔
899
                if (k < 0)
2✔
900
                        return k;
×
901
                if (k == 0)
2✔
902
                        break;
903
                n = sym_LZ4F_compressUpdate(ctx, out_buff + offset, out_allocsize - offset,
1✔
904
                                        in_buff, k, NULL);
905
                if (sym_LZ4F_isError(n))
1✔
906
                        return -ENOTRECOVERABLE;
907

908
                total_in += k;
1✔
909
                offset += n;
1✔
910
                total_out += n;
1✔
911

912
                if (max_bytes != UINT64_MAX && total_out > (size_t) max_bytes)
1✔
913
                        return log_debug_errno(SYNTHETIC_ERRNO(EFBIG),
×
914
                                               "Compressed stream longer than %" PRIu64 " bytes", max_bytes);
915

916
                if (out_allocsize - offset < frame_size + 4) {
1✔
917
                        k = loop_write(fdt, out_buff, offset);
×
918
                        if (k < 0)
×
919
                                return k;
920
                        offset = 0;
921
                }
922
        }
923

924
        n = sym_LZ4F_compressEnd(ctx, out_buff + offset, out_allocsize - offset, NULL);
1✔
925
        if (sym_LZ4F_isError(n))
1✔
926
                return -ENOTRECOVERABLE;
927

928
        offset += n;
1✔
929
        total_out += n;
1✔
930
        r = loop_write(fdt, out_buff, offset);
1✔
931
        if (r < 0)
1✔
932
                return r;
933

934
        if (ret_uncompressed_size)
1✔
935
                *ret_uncompressed_size = total_in;
1✔
936

937
        if (total_in == 0)
1✔
938
                log_debug("LZ4 compression finished (no input data)");
×
939
        else
940
                log_debug("LZ4 compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
1✔
941
                          total_in, total_out,
942
                          (double) total_out / total_in * 100);
943

944
        return 0;
945
#else
946
        return -EPROTONOSUPPORT;
947
#endif
948
}
949

950
int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
3✔
951
        assert(fdf >= 0);
3✔
952
        assert(fdt >= 0);
3✔
953

954
#if HAVE_XZ
955
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
3✔
956
        lzma_ret ret;
3✔
957

958
        uint8_t buf[BUFSIZ], out[BUFSIZ];
3✔
959
        lzma_action action = LZMA_RUN;
3✔
960
        int r;
3✔
961

962
        r = dlopen_lzma();
3✔
963
        if (r < 0)
3✔
964
                return r;
965

966
        ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
3✔
967
        if (ret != LZMA_OK)
3✔
968
                return log_debug_errno(SYNTHETIC_ERRNO(ENOMEM),
×
969
                                       "Failed to initialize XZ decoder: code %u",
970
                                       ret);
971

972
        for (;;) {
15✔
973
                if (s.avail_in == 0 && action == LZMA_RUN) {
15✔
974
                        ssize_t n;
5✔
975

976
                        n = read(fdf, buf, sizeof(buf));
5✔
977
                        if (n < 0)
5✔
978
                                return -errno;
×
979
                        if (n == 0)
5✔
980
                                action = LZMA_FINISH;
981
                        else {
982
                                s.next_in = buf;
5✔
983
                                s.avail_in = n;
5✔
984
                        }
985
                }
986

987
                if (s.avail_out == 0) {
15✔
988
                        s.next_out = out;
13✔
989
                        s.avail_out = sizeof(out);
13✔
990
                }
991

992
                ret = sym_lzma_code(&s, action);
15✔
993
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
15✔
994
                        return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
1✔
995
                                               "Decompression failed: code %u",
996
                                               ret);
997

998
                if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
14✔
999
                        ssize_t n, k;
12✔
1000

1001
                        n = sizeof(out) - s.avail_out;
12✔
1002

1003
                        if (max_bytes != UINT64_MAX) {
12✔
1004
                                if (max_bytes < (uint64_t) n)
12✔
1005
                                        return -EFBIG;
1006

1007
                                max_bytes -= n;
11✔
1008
                        }
1009

1010
                        k = loop_write(fdt, out, n);
11✔
1011
                        if (k < 0)
11✔
1012
                                return k;
1013

1014
                        if (ret == LZMA_STREAM_END) {
11✔
1015
                                if (s.total_in == 0)
1✔
1016
                                        log_debug("XZ decompression finished (no input data)");
×
1017
                                else
1018
                                        log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
1✔
1019
                                                  s.total_in, s.total_out,
1020
                                                  (double) s.total_out / s.total_in * 100);
1021

1022
                                return 0;
1✔
1023
                        }
1024
                }
1025
        }
1026
#else
1027
        return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
1028
                               "Cannot decompress file. Compiled without XZ support.");
1029
#endif
1030
}
1031

1032
int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
3✔
1033
#if HAVE_LZ4
1034
        size_t c;
3✔
1035
        _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
×
1036
        _cleanup_free_ char *buf = NULL;
3✔
1037
        char *src;
3✔
1038
        struct stat st;
3✔
1039
        int r;
3✔
1040
        size_t total_in = 0, total_out = 0;
3✔
1041

1042
        r = dlopen_lz4();
3✔
1043
        if (r < 0)
3✔
1044
                return r;
1045

1046
        c = sym_LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
3✔
1047
        if (sym_LZ4F_isError(c))
3✔
1048
                return -ENOMEM;
1049

1050
        if (fstat(fdf, &st) < 0)
3✔
1051
                return log_debug_errno(errno, "fstat() failed: %m");
×
1052

1053
        if (file_offset_beyond_memory_size(st.st_size))
3✔
1054
                return -EFBIG;
1055

1056
        buf = malloc(LZ4_BUFSIZE);
3✔
1057
        if (!buf)
3✔
1058
                return -ENOMEM;
1059

1060
        src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0);
3✔
1061
        if (src == MAP_FAILED)
3✔
1062
                return -errno;
×
1063

1064
        while (total_in < (size_t) st.st_size) {
5✔
1065
                size_t produced = LZ4_BUFSIZE;
3✔
1066
                size_t used = st.st_size - total_in;
3✔
1067

1068
                c = sym_LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
3✔
1069
                if (sym_LZ4F_isError(c)) {
3✔
1070
                        r = -EBADMSG;
×
1071
                        goto cleanup;
1✔
1072
                }
1073

1074
                total_in += used;
3✔
1075
                total_out += produced;
3✔
1076

1077
                if (max_bytes != UINT64_MAX && total_out > (size_t) max_bytes) {
3✔
1078
                        log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes);
1✔
1079
                        r = -EFBIG;
1✔
1080
                        goto cleanup;
1✔
1081
                }
1082

1083
                r = loop_write(fdt, buf, produced);
2✔
1084
                if (r < 0)
2✔
1085
                        goto cleanup;
×
1086
        }
1087

1088
        if (total_in == 0)
2✔
1089
                log_debug("LZ4 decompression finished (no input data)");
×
1090
        else
1091
                log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
2✔
1092
                          total_in, total_out,
1093
                          (double) total_out / total_in * 100);
1094
        r = 0;
1095
 cleanup:
3✔
1096
        munmap(src, st.st_size);
3✔
1097
        return r;
3✔
1098
#else
1099
        return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
1100
                               "Cannot decompress file. Compiled without LZ4 support.");
1101
#endif
1102
}
1103

1104
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
21✔
1105
        assert(fdf >= 0);
21✔
1106
        assert(fdt >= 0);
21✔
1107

1108
#if HAVE_ZSTD
1109
        _cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
×
1110
        _cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
21✔
1111
        size_t in_allocsize, out_allocsize;
21✔
1112
        size_t z;
21✔
1113
        uint64_t left = max_bytes, in_bytes = 0;
21✔
1114
        int r;
21✔
1115

1116
        r = dlopen_zstd();
21✔
1117
        if (r < 0)
21✔
1118
                return r;
1119

1120
        /* Create the context and buffers */
1121
        in_allocsize = sym_ZSTD_CStreamInSize();
21✔
1122
        out_allocsize = sym_ZSTD_CStreamOutSize();
21✔
1123
        in_buff = malloc(in_allocsize);
21✔
1124
        out_buff = malloc(out_allocsize);
21✔
1125
        cctx = sym_ZSTD_createCCtx();
21✔
1126
        if (!cctx || !out_buff || !in_buff)
21✔
1127
                return -ENOMEM;
1128

1129
        z = sym_ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1);
21✔
1130
        if (sym_ZSTD_isError(z))
21✔
1131
                log_debug("Failed to enable ZSTD checksum, ignoring: %s", sym_ZSTD_getErrorName(z));
×
1132

1133
        /* This loop read from the input file, compresses that entire chunk,
1134
         * and writes all output produced to the output file.
1135
         */
1136
        for (;;) {
181✔
1137
                bool is_last_chunk;
202✔
1138
                ZSTD_inBuffer input = {
202✔
1139
                        .src = in_buff,
1140
                        .size = 0,
1141
                        .pos = 0
1142
                };
1143
                ssize_t red;
202✔
1144

1145
                red = loop_read(fdf, in_buff, in_allocsize, true);
202✔
1146
                if (red < 0)
202✔
1147
                        return red;
×
1148
                is_last_chunk = red == 0;
202✔
1149

1150
                in_bytes += (size_t) red;
202✔
1151
                input.size = (size_t) red;
202✔
1152

1153
                for (bool finished = false; !finished;) {
404✔
1154
                        ZSTD_outBuffer output = {
202✔
1155
                                .dst = out_buff,
1156
                                .size = out_allocsize,
1157
                                .pos = 0
1158
                        };
1159
                        size_t remaining;
202✔
1160
                        ssize_t wrote;
202✔
1161

1162
                        /* Compress into the output buffer and write all of the
1163
                         * output to the file so we can reuse the buffer next
1164
                         * iteration.
1165
                         */
1166
                        remaining = sym_ZSTD_compressStream2(
383✔
1167
                                cctx, &output, &input,
1168
                                is_last_chunk ? ZSTD_e_end : ZSTD_e_continue);
1169

1170
                        if (sym_ZSTD_isError(remaining)) {
202✔
1171
                                log_debug("ZSTD encoder failed: %s", sym_ZSTD_getErrorName(remaining));
×
1172
                                return zstd_ret_to_errno(remaining);
×
1173
                        }
1174

1175
                        if (left < output.pos)
202✔
1176
                                return -EFBIG;
1177

1178
                        wrote = loop_write_full(fdt, output.dst, output.pos, USEC_INFINITY);
202✔
1179
                        if (wrote < 0)
202✔
1180
                                return wrote;
1181

1182
                        left -= output.pos;
202✔
1183

1184
                        /* If we're on the last chunk we're finished when zstd
1185
                         * returns 0, which means its consumed all the input AND
1186
                         * finished the frame. Otherwise, we're finished when
1187
                         * we've consumed all the input.
1188
                         */
1189
                        finished = is_last_chunk ? (remaining == 0) : (input.pos == input.size);
202✔
1190
                }
1191

1192
                /* zstd only returns 0 when the input is completely consumed */
1193
                assert(input.pos == input.size);
202✔
1194
                if (is_last_chunk)
202✔
1195
                        break;
1196
        }
1197

1198
        if (ret_uncompressed_size)
21✔
1199
                *ret_uncompressed_size = in_bytes;
21✔
1200

1201
        if (in_bytes == 0)
21✔
1202
                log_debug("ZSTD compression finished (no input data)");
×
1203
        else
1204
                log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
21✔
1205
                          in_bytes, max_bytes - left, (double) (max_bytes - left) / in_bytes * 100);
1206

1207
        return 0;
1208
#else
1209
        return -EPROTONOSUPPORT;
1210
#endif
1211
}
1212

1213
int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
13✔
1214
        assert(fdf >= 0);
13✔
1215
        assert(fdt >= 0);
13✔
1216

1217
#if HAVE_ZSTD
1218
        _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = NULL;
×
1219
        _cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
13✔
1220
        size_t in_allocsize, out_allocsize;
13✔
1221
        size_t last_result = 0;
13✔
1222
        uint64_t left = max_bytes, in_bytes = 0;
13✔
1223
        int r;
13✔
1224

1225
        r = dlopen_zstd();
13✔
1226
        if (r < 0)
13✔
1227
                return r;
1228
        /* Create the context and buffers */
1229
        in_allocsize = sym_ZSTD_DStreamInSize();
13✔
1230
        out_allocsize = sym_ZSTD_DStreamOutSize();
13✔
1231
        in_buff = malloc(in_allocsize);
13✔
1232
        out_buff = malloc(out_allocsize);
13✔
1233
        dctx = sym_ZSTD_createDCtx();
13✔
1234
        if (!dctx || !out_buff || !in_buff)
13✔
1235
                return -ENOMEM;
1236

1237
        /* This loop assumes that the input file is one or more concatenated
1238
         * zstd streams. This example won't work if there is trailing non-zstd
1239
         * data at the end, but streaming decompression in general handles this
1240
         * case. ZSTD_decompressStream() returns 0 exactly when the frame is
1241
         * completed, and doesn't consume input after the frame.
1242
         */
1243
        for (;;) {
33✔
1244
                bool has_error = false;
23✔
1245
                ZSTD_inBuffer input = {
23✔
1246
                        .src = in_buff,
1247
                        .size = 0,
1248
                        .pos = 0
1249
                };
1250
                ssize_t red;
23✔
1251

1252
                red = loop_read(fdf, in_buff, in_allocsize, true);
23✔
1253
                if (red < 0)
23✔
1254
                        return red;
2✔
1255
                if (red == 0)
23✔
1256
                        break;
1257

1258
                in_bytes += (size_t) red;
13✔
1259
                input.size = (size_t) red;
13✔
1260
                input.pos = 0;
13✔
1261

1262
                /* Given a valid frame, zstd won't consume the last byte of the
1263
                 * frame until it has flushed all of the decompressed data of
1264
                 * the frame. So input.pos < input.size means frame is not done
1265
                 * or there is still output available.
1266
                 */
1267
                while (input.pos < input.size) {
59✔
1268
                        ZSTD_outBuffer output = {
49✔
1269
                                .dst = out_buff,
1270
                                .size = out_allocsize,
1271
                                .pos = 0
1272
                        };
1273
                        ssize_t wrote;
49✔
1274
                        /* The return code is zero if the frame is complete, but
1275
                         * there may be multiple frames concatenated together.
1276
                         * Zstd will automatically reset the context when a
1277
                         * frame is complete. Still, calling ZSTD_DCtx_reset()
1278
                         * can be useful to reset the context to a clean state,
1279
                         * for instance if the last decompression call returned
1280
                         * an error.
1281
                         */
1282
                        last_result = sym_ZSTD_decompressStream(dctx, &output, &input);
49✔
1283
                        if (sym_ZSTD_isError(last_result)) {
49✔
1284
                                has_error = true;
1✔
1285
                                break;
1✔
1286
                        }
1287

1288
                        if (left < output.pos)
48✔
1289
                                return -EFBIG;
2✔
1290

1291
                        wrote = loop_write_full(fdt, output.dst, output.pos, USEC_INFINITY);
47✔
1292
                        if (wrote < 0)
47✔
1293
                                return wrote;
1294

1295
                        left -= output.pos;
46✔
1296
                }
1297
                if (has_error)
11✔
1298
                        break;
1299
        }
1300

1301
        if (in_bytes == 0)
11✔
1302
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoder failed: no data read");
×
1303

1304
        if (last_result != 0) {
11✔
1305
                /* The last return value from ZSTD_decompressStream did not end
1306
                 * on a frame, but we reached the end of the file! We assume
1307
                 * this is an error, and the input was truncated.
1308
                 */
1309
                log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(last_result));
1✔
1310
                return zstd_ret_to_errno(last_result);
1✔
1311
        }
1312

1313
        if (in_bytes == 0)
10✔
1314
                log_debug("ZSTD decompression finished (no input data)");
1315
        else
1316
                log_debug("ZSTD decompression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
10✔
1317
                          in_bytes,
1318
                          max_bytes - left,
1319
                          (double) (max_bytes - left) / in_bytes * 100);
1320
        return 0;
1321
#else
1322
        return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
1323
                               "Cannot decompress file. Compiled without ZSTD support.");
1324
#endif
1325
}
1326

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

1329
        if (endswith(filename, ".lz4"))
10✔
1330
                return decompress_stream_lz4(fdf, fdt, max_bytes);
×
1331
        if (endswith(filename, ".xz"))
10✔
1332
                return decompress_stream_xz(fdf, fdt, max_bytes);
×
1333
        if (endswith(filename, ".zst"))
10✔
1334
                return decompress_stream_zstd(fdf, fdt, max_bytes);
10✔
1335

1336
        return -EPROTONOSUPPORT;
1337
}
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