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

systemd / systemd / 14815796853

02 May 2025 11:41AM UTC coverage: 72.24% (-0.003%) from 72.243%
14815796853

push

github

web-flow
Various changes to prepare for running IWYU on the repository (#37319)

These are various commits that were required to get things compiling
after running IWYU. I think all of them make sense on their own, hence
this split PR to merge them ahead of time.

81 of 96 new or added lines in 48 files covered. (84.38%)

209 existing lines in 39 files now uncovered.

297219 of 411432 relevant lines covered (72.24%)

693693.2 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 <inttypes.h>
4
#include <malloc.h>
5
#include <stdlib.h>
6
#include <sys/mman.h>
7
#include <sys/stat.h>
8
#include <sys/types.h>
9
#include <unistd.h>
10

11
#if HAVE_LZ4
12
#include <lz4hc.h>
13
#endif
14

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

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

24
#include "alloc-util.h"
25
#include "bitfield.h"
26
#include "compress.h"
27
#include "fd-util.h"
28
#include "fileio.h"
29
#include "io-util.h"
30
#include "log.h"
31
#include "macro.h"
32
#include "sparse-endian.h"
33
#include "string-table.h"
34
#include "string-util.h"
35
#include "unaligned.h"
36

37
#if HAVE_LZ4
38
static void *lz4_dl = NULL;
39

40
static DLSYM_PROTOTYPE(LZ4F_compressBegin) = NULL;
41
static DLSYM_PROTOTYPE(LZ4F_compressBound) = NULL;
42
static DLSYM_PROTOTYPE(LZ4F_compressEnd) = NULL;
43
static DLSYM_PROTOTYPE(LZ4F_compressUpdate) = NULL;
44
static DLSYM_PROTOTYPE(LZ4F_createCompressionContext) = NULL;
45
static DLSYM_PROTOTYPE(LZ4F_createDecompressionContext) = NULL;
46
static DLSYM_PROTOTYPE(LZ4F_decompress) = NULL;
47
static DLSYM_PROTOTYPE(LZ4F_freeCompressionContext) = NULL;
48
static DLSYM_PROTOTYPE(LZ4F_freeDecompressionContext) = NULL;
49
static DLSYM_PROTOTYPE(LZ4F_isError) = NULL;
50
DLSYM_PROTOTYPE(LZ4_compress_default) = NULL;
51
DLSYM_PROTOTYPE(LZ4_compress_HC) = NULL;
52
DLSYM_PROTOTYPE(LZ4_decompress_safe) = NULL;
53
DLSYM_PROTOTYPE(LZ4_decompress_safe_partial) = NULL;
54
DLSYM_PROTOTYPE(LZ4_versionNumber) = NULL;
55

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

60
#if HAVE_ZSTD
61
static void *zstd_dl = NULL;
62

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

80
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_CCtx*, sym_ZSTD_freeCCtx, NULL);
21✔
81
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_DCtx*, sym_ZSTD_freeDCtx, NULL);
249,856✔
82

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

95
#if HAVE_XZ
96
static void *lzma_dl = NULL;
97

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

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

117
#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
118

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

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

133
DEFINE_STRING_TABLE_LOOKUP(compression, Compression);
535✔
134
DEFINE_STRING_TABLE_LOOKUP(compression_lowercase, Compression);
91✔
135

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

143
        assert(c >= 0);
2,692✔
144
        assert(c < _COMPRESSION_MAX);
2,692✔
145

146
        return BIT_SET(supported, c);
2,692✔
147
}
148

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

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

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

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

177
#if HAVE_XZ
178
        lzma_options_lzma opt = {
20✔
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[] = {
20✔
183
                { LZMA_FILTER_LZMA2, &opt },
184
                { LZMA_VLI_UNKNOWN, NULL }
185
        };
186
        lzma_ret ret;
20✔
187
        size_t out_pos = 0;
20✔
188
        int r;
20✔
189

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

194
        if (level >= 0) {
20✔
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)
20✔
204
                return -ENOBUFS;
205

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

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

218
#if HAVE_LZ4
219
int dlopen_lz4(void) {
3,156✔
220
        ELF_NOTE_DLOPEN("lz4",
3,156✔
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,156✔
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
}
244
#endif
245

246
int compress_blob_lz4(const void *src, uint64_t src_size,
182✔
247
                      void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
248

249
        assert(src);
182✔
250
        assert(src_size > 0);
182✔
251
        assert(dst);
182✔
252
        assert(dst_alloc_size > 0);
182✔
253
        assert(dst_size);
182✔
254

255
#if HAVE_LZ4
256
        int r;
182✔
257

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

264
        if (src_size < 9)
182✔
265
                return -ENOBUFS;
266

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

274
        unaligned_write_le64(dst, src_size);
176✔
275
        *dst_size = r + 8;
176✔
276

277
        return 0;
176✔
278
#else
279
        return -EPROTONOSUPPORT;
280
#endif
281
}
282

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

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

312
int compress_blob_zstd(
265✔
313
                const void *src, uint64_t src_size,
314
                void *dst, size_t dst_alloc_size, size_t *dst_size, int level) {
315

316
        assert(src);
265✔
317
        assert(src_size > 0);
265✔
318
        assert(dst);
265✔
319
        assert(dst_alloc_size > 0);
265✔
320
        assert(dst_size);
265✔
321

322
#if HAVE_ZSTD
323
        size_t k;
265✔
324
        int r;
265✔
325

326
        r = dlopen_zstd();
265✔
327
        if (r < 0)
265✔
328
                return r;
329

330
        k = sym_ZSTD_compress(dst, dst_alloc_size, src, src_size, level < 0 ? 0 : level);
265✔
331
        if (sym_ZSTD_isError(k))
265✔
332
                return zstd_ret_to_errno(k);
5✔
333

334
        *dst_size = k;
260✔
335
        return 0;
260✔
336
#else
337
        return -EPROTONOSUPPORT;
338
#endif
339
}
340

341
int decompress_blob_xz(
2,541✔
342
                const void *src,
343
                uint64_t src_size,
344
                void **dst,
345
                size_t* dst_size,
346
                size_t dst_max) {
347

348
        assert(src);
2,541✔
349
        assert(src_size > 0);
2,541✔
350
        assert(dst);
2,541✔
351
        assert(dst_size);
2,541✔
352

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

359
        r = dlopen_lzma();
2,541✔
360
        if (r < 0)
2,541✔
361
                return r;
362

363
        ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
2,541✔
364
        if (ret != LZMA_OK)
2,541✔
365
                return -ENOMEM;
366

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

371
        s.next_in = src;
2,541✔
372
        s.avail_in = src_size;
2,541✔
373

374
        s.next_out = *dst;
2,541✔
375
        s.avail_out = space;
2,541✔
376

377
        for (;;) {
2,771✔
378
                size_t used;
2,656✔
379

380
                ret = sym_lzma_code(&s, LZMA_FINISH);
2,656✔
381

382
                if (ret == LZMA_STREAM_END)
2,656✔
383
                        break;
384
                else if (ret != LZMA_OK)
121✔
385
                        return -ENOMEM;
386

387
                if (dst_max > 0 && (space - s.avail_out) >= dst_max)
115✔
388
                        break;
389
                else if (dst_max > 0 && space == dst_max)
115✔
390
                        return -ENOBUFS;
391

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

397
                s.avail_out = space - used;
115✔
398
                s.next_out = *(uint8_t**)dst + used;
115✔
399
        }
400

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

408
int decompress_blob_lz4(
2,698✔
409
                const void *src,
410
                uint64_t src_size,
411
                void **dst,
412
                size_t* dst_size,
413
                size_t dst_max) {
414

415
        assert(src);
2,698✔
416
        assert(src_size > 0);
2,698✔
417
        assert(dst);
2,698✔
418
        assert(dst_size);
2,698✔
419

420
#if HAVE_LZ4
421
        char* out;
2,698✔
422
        int r, size; /* LZ4 uses int for size */
2,698✔
423

424
        r = dlopen_lz4();
2,698✔
425
        if (r < 0)
2,698✔
426
                return r;
427

428
        if (src_size <= 8)
2,698✔
429
                return -EBADMSG;
430

431
        size = unaligned_read_le64(src);
2,696✔
432
        if (size < 0 || (unsigned) size != unaligned_read_le64(src))
2,696✔
433
                return -EFBIG;
434
        out = greedy_realloc(dst, size, 1);
2,692✔
435
        if (!out)
2,692✔
436
                return -ENOMEM;
437

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

442
        *dst_size = size;
2,692✔
443
        return 0;
2,692✔
444
#else
445
        return -EPROTONOSUPPORT;
446
#endif
447
}
448

449
int decompress_blob_zstd(
200,454✔
450
                const void *src,
451
                uint64_t src_size,
452
                void **dst,
453
                size_t *dst_size,
454
                size_t dst_max) {
455

456
        assert(src);
200,454✔
457
        assert(src_size > 0);
200,454✔
458
        assert(dst);
200,454✔
459
        assert(dst_size);
200,454✔
460

461
#if HAVE_ZSTD
462
        uint64_t size;
200,454✔
463
        int r;
200,454✔
464

465
        r = dlopen_zstd();
200,454✔
466
        if (r < 0)
200,454✔
467
                return r;
200,454✔
468

469
        size = sym_ZSTD_getFrameContentSize(src, src_size);
200,454✔
470
        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
200,454✔
471
                return -EBADMSG;
472

473
        if (dst_max > 0 && size > dst_max)
200,448✔
474
                size = dst_max;
×
475
        if (size > SIZE_MAX)
200,448✔
476
                return -E2BIG;
477

478
        if (!(greedy_realloc(dst, MAX(sym_ZSTD_DStreamOutSize(), size), 1)))
200,448✔
479
                return -ENOMEM;
480

481
        _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
400,896✔
482
        if (!dctx)
200,448✔
483
                return -ENOMEM;
484

485
        ZSTD_inBuffer input = {
200,448✔
486
                .src = src,
487
                .size = src_size,
488
        };
489
        ZSTD_outBuffer output = {
400,896✔
490
                .dst = *dst,
200,448✔
491
                .size = MALLOC_SIZEOF_SAFE(*dst),
200,448✔
492
        };
493

494
        size_t k = sym_ZSTD_decompressStream(dctx, &output, &input);
200,448✔
495
        if (sym_ZSTD_isError(k)) {
200,448✔
496
                log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
497
                return zstd_ret_to_errno(k);
×
498
        }
499
        assert(output.pos >= size);
200,448✔
500

501
        *dst_size = size;
200,448✔
502
        return 0;
200,448✔
503
#else
504
        return -EPROTONOSUPPORT;
505
#endif
506
}
507

508
int decompress_blob(
205,388✔
509
                Compression compression,
510
                const void *src,
511
                uint64_t src_size,
512
                void **dst,
513
                size_t* dst_size,
514
                size_t dst_max) {
515

516
        if (compression == COMPRESSION_XZ)
205,388✔
517
                return decompress_blob_xz(
2,528✔
518
                                src, src_size,
519
                                dst, dst_size, dst_max);
520
        else if (compression == COMPRESSION_LZ4)
202,860✔
521
                return decompress_blob_lz4(
2,526✔
522
                                src, src_size,
523
                                dst, dst_size, dst_max);
524
        else if (compression == COMPRESSION_ZSTD)
200,334✔
525
                return decompress_blob_zstd(
200,334✔
526
                                src, src_size,
527
                                dst, dst_size, dst_max);
528
        else
529
                return -EPROTONOSUPPORT;
530
}
531

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

540
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
541
         * follow the prefix */
542

543
        assert(src);
270✔
544
        assert(src_size > 0);
270✔
545
        assert(buffer);
270✔
546
        assert(prefix);
270✔
547

548
#if HAVE_XZ
549
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
270✔
550
        size_t allocated;
270✔
551
        lzma_ret ret;
270✔
552
        int r;
270✔
553

554
        r = dlopen_lzma();
270✔
555
        if (r < 0)
270✔
556
                return r;
557

558
        ret = sym_lzma_stream_decoder(&s, UINT64_MAX, 0);
270✔
559
        if (ret != LZMA_OK)
270✔
560
                return -EBADMSG;
561

562
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
563
                return -ENOMEM;
564

565
        allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
566

567
        s.next_in = src;
270✔
568
        s.avail_in = src_size;
270✔
569

570
        s.next_out = *buffer;
270✔
571
        s.avail_out = allocated;
270✔
572

573
        for (;;) {
270✔
574
                ret = sym_lzma_code(&s, LZMA_FINISH);
270✔
575

576
                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
270✔
577
                        return -EBADMSG;
578

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

583
                if (ret == LZMA_STREAM_END)
×
584
                        return 0;
585

586
                s.avail_out += allocated;
×
587

588
                if (!(greedy_realloc(buffer, allocated * 2, 1)))
×
589
                        return -ENOMEM;
590

591
                allocated = MALLOC_SIZEOF_SAFE(*buffer);
×
592
                s.next_out = *(uint8_t**)buffer + allocated - s.avail_out;
×
593
        }
594

595
#else
596
        return -EPROTONOSUPPORT;
597
#endif
598
}
599

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

608
        /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
609
         * follow the prefix */
610

611
        assert(src);
270✔
612
        assert(src_size > 0);
270✔
613
        assert(buffer);
270✔
614
        assert(prefix);
270✔
615

616
#if HAVE_LZ4
617
        size_t allocated;
270✔
618
        int r;
270✔
619

620
        r = dlopen_lz4();
270✔
621
        if (r < 0)
270✔
622
                return r;
623

624
        if (src_size <= 8)
270✔
625
                return -EBADMSG;
626

627
        if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
270✔
628
                return -ENOMEM;
629
        allocated = MALLOC_SIZEOF_SAFE(*buffer);
270✔
630

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

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

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

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

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

662
                if (size < prefix_len + 1)
×
663
                        return 0;
664
        }
665

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

673
int decompress_startswith_zstd(
49,395✔
674
                const void *src,
675
                uint64_t src_size,
676
                void **buffer,
677
                const void *prefix,
678
                size_t prefix_len,
679
                uint8_t extra) {
680

681
        assert(src);
49,395✔
682
        assert(src_size > 0);
49,395✔
683
        assert(buffer);
49,395✔
684
        assert(prefix);
49,395✔
685

686
#if HAVE_ZSTD
687
        int r;
49,395✔
688

689
        r = dlopen_zstd();
49,395✔
690
        if (r < 0)
49,395✔
691
                return r;
49,395✔
692

693
        uint64_t size = sym_ZSTD_getFrameContentSize(src, src_size);
49,395✔
694
        if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
49,395✔
695
                return -EBADMSG;
696

697
        if (size < prefix_len + 1)
49,395✔
698
                return 0; /* Decompressed text too short to match the prefix and extra */
699

700
        _cleanup_(sym_ZSTD_freeDCtxp) ZSTD_DCtx *dctx = sym_ZSTD_createDCtx();
98,790✔
701
        if (!dctx)
49,395✔
702
                return -ENOMEM;
703

704
        if (!(greedy_realloc(buffer, MAX(sym_ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
49,395✔
705
                return -ENOMEM;
706

707
        ZSTD_inBuffer input = {
49,395✔
708
                .src = src,
709
                .size = src_size,
710
        };
711
        ZSTD_outBuffer output = {
98,790✔
712
                .dst = *buffer,
49,395✔
713
                .size = MALLOC_SIZEOF_SAFE(*buffer),
49,395✔
714
        };
715
        size_t k;
49,395✔
716

717
        k = sym_ZSTD_decompressStream(dctx, &output, &input);
49,395✔
718
        if (sym_ZSTD_isError(k)) {
49,395✔
719
                log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k));
×
720
                return zstd_ret_to_errno(k);
×
721
        }
722
        assert(output.pos >= prefix_len + 1);
49,395✔
723

724
        return memcmp(*buffer, prefix, prefix_len) == 0 &&
49,395✔
725
                ((const uint8_t*) *buffer)[prefix_len] == extra;
412✔
726
#else
727
        return -EPROTONOSUPPORT;
728
#endif
729
}
730

731
int decompress_startswith(
49,137✔
732
                Compression compression,
733
                const void *src,
734
                uint64_t src_size,
735
                void **buffer,
736
                const void *prefix,
737
                size_t prefix_len,
738
                uint8_t extra) {
739

740
        if (compression == COMPRESSION_XZ)
49,137✔
741
                return decompress_startswith_xz(
4✔
742
                                src, src_size,
743
                                buffer,
744
                                prefix, prefix_len,
745
                                extra);
746

747
        else if (compression == COMPRESSION_LZ4)
49,133✔
748
                return decompress_startswith_lz4(
4✔
749
                                src, src_size,
750
                                buffer,
751
                                prefix, prefix_len,
752
                                extra);
753
        else if (compression == COMPRESSION_ZSTD)
49,129✔
754
                return decompress_startswith_zstd(
49,129✔
755
                                src, src_size,
756
                                buffer,
757
                                prefix, prefix_len,
758
                                extra);
759
        else
760
                return -EBADMSG;
761
}
762

763
int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
1✔
764
        assert(fdf >= 0);
1✔
765
        assert(fdt >= 0);
1✔
766

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

774
        r = dlopen_lzma();
1✔
775
        if (r < 0)
1✔
776
                return r;
777

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

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

789
                        if (max_bytes != UINT64_MAX && (uint64_t) m > max_bytes)
7✔
790
                                m = (size_t) max_bytes;
×
791

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

801
                                if (max_bytes != UINT64_MAX) {
6✔
802
                                        assert(max_bytes >= (uint64_t) n);
×
803
                                        max_bytes -= n;
×
804
                                }
805
                        }
806
                }
807

808
                if (s.avail_out == 0) {
8✔
809
                        s.next_out = out;
2✔
810
                        s.avail_out = sizeof(out);
2✔
811
                }
812

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

819
                if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
8✔
820
                        ssize_t n, k;
2✔
821

822
                        n = sizeof(out) - s.avail_out;
2✔
823

824
                        k = loop_write(fdt, out, n);
2✔
825
                        if (k < 0)
2✔
826
                                return k;
827

828
                        if (ret == LZMA_STREAM_END) {
2✔
829
                                if (ret_uncompressed_size)
1✔
830
                                        *ret_uncompressed_size = s.total_in;
1✔
831

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

836
                                return 0;
1✔
837
                        }
838
                }
839
        }
840
#else
841
        return -EPROTONOSUPPORT;
842
#endif
843
}
844

845
#define LZ4_BUFSIZE (512*1024u)
846

847
int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
1✔
848

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

861
        r = dlopen_lz4();
1✔
862
        if (r < 0)
1✔
863
                return r;
864

865
        c = sym_LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
1✔
866
        if (sym_LZ4F_isError(c))
1✔
867
                return -ENOMEM;
868

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

875
        in_buff = malloc(LZ4_BUFSIZE);
1✔
876
        if (!in_buff)
1✔
877
                return -ENOMEM;
878

879
        n = offset = total_out = sym_LZ4F_compressBegin(ctx, out_buff, out_allocsize, &preferences);
1✔
880
        if (sym_LZ4F_isError(n))
1✔
881
                return -EINVAL;
882

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

885
        for (;;) {
2✔
886
                ssize_t k;
2✔
887

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

898
                total_in += k;
1✔
899
                offset += n;
1✔
900
                total_out += n;
1✔
901

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

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

914
        n = sym_LZ4F_compressEnd(ctx, out_buff + offset, out_allocsize - offset, NULL);
1✔
915
        if (sym_LZ4F_isError(n))
1✔
916
                return -ENOTRECOVERABLE;
917

918
        offset += n;
1✔
919
        total_out += n;
1✔
920
        r = loop_write(fdt, out_buff, offset);
1✔
921
        if (r < 0)
1✔
922
                return r;
923

924
        if (ret_uncompressed_size)
1✔
925
                *ret_uncompressed_size = total_in;
1✔
926

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

931
        return 0;
932
#else
933
        return -EPROTONOSUPPORT;
934
#endif
935
}
936

937
int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
3✔
938
        assert(fdf >= 0);
3✔
939
        assert(fdt >= 0);
3✔
940

941
#if HAVE_XZ
942
        _cleanup_(lzma_end_wrapper) lzma_stream s = LZMA_STREAM_INIT;
3✔
943
        lzma_ret ret;
3✔
944

945
        uint8_t buf[BUFSIZ], out[BUFSIZ];
3✔
946
        lzma_action action = LZMA_RUN;
3✔
947
        int r;
3✔
948

949
        r = dlopen_lzma();
3✔
950
        if (r < 0)
3✔
951
                return r;
952

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

959
        for (;;) {
15✔
960
                if (s.avail_in == 0 && action == LZMA_RUN) {
15✔
961
                        ssize_t n;
5✔
962

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

974
                if (s.avail_out == 0) {
15✔
975
                        s.next_out = out;
13✔
976
                        s.avail_out = sizeof(out);
13✔
977
                }
978

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

985
                if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
14✔
986
                        ssize_t n, k;
12✔
987

988
                        n = sizeof(out) - s.avail_out;
12✔
989

990
                        if (max_bytes != UINT64_MAX) {
12✔
991
                                if (max_bytes < (uint64_t) n)
12✔
992
                                        return -EFBIG;
993

994
                                max_bytes -= n;
11✔
995
                        }
996

997
                        k = loop_write(fdt, out, n);
11✔
998
                        if (k < 0)
11✔
999
                                return k;
1000

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

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

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

1026
        r = dlopen_lz4();
3✔
1027
        if (r < 0)
3✔
1028
                return r;
1029

1030
        c = sym_LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
3✔
1031
        if (sym_LZ4F_isError(c))
3✔
1032
                return -ENOMEM;
1033

1034
        if (fstat(in, &st) < 0)
3✔
1035
                return log_debug_errno(errno, "fstat() failed: %m");
×
1036

1037
        if (file_offset_beyond_memory_size(st.st_size))
3✔
1038
                return -EFBIG;
1039

1040
        buf = malloc(LZ4_BUFSIZE);
3✔
1041
        if (!buf)
3✔
1042
                return -ENOMEM;
1043

1044
        src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0);
3✔
1045
        if (src == MAP_FAILED)
3✔
1046
                return -errno;
×
1047

1048
        while (total_in < (size_t) st.st_size) {
5✔
1049
                size_t produced = LZ4_BUFSIZE;
3✔
1050
                size_t used = st.st_size - total_in;
3✔
1051

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

1058
                total_in += used;
3✔
1059
                total_out += produced;
3✔
1060

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

1067
                r = loop_write(out, buf, produced);
2✔
1068
                if (r < 0)
2✔
1069
                        goto cleanup;
×
1070
        }
1071

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

1085
int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
21✔
1086
        assert(fdf >= 0);
21✔
1087
        assert(fdt >= 0);
21✔
1088

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

1097
        r = dlopen_zstd();
21✔
1098
        if (r < 0)
21✔
1099
                return r;
1100

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

1110
        z = sym_ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1);
21✔
1111
        if (sym_ZSTD_isError(z))
21✔
1112
                log_debug("Failed to enable ZSTD checksum, ignoring: %s", sym_ZSTD_getErrorName(z));
×
1113

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

1126
                red = loop_read(fdf, in_buff, in_allocsize, true);
210✔
1127
                if (red < 0)
210✔
1128
                        return red;
×
1129
                is_last_chunk = red == 0;
210✔
1130

1131
                in_bytes += (size_t) red;
210✔
1132
                input.size = (size_t) red;
210✔
1133

1134
                for (bool finished = false; !finished;) {
420✔
1135
                        ZSTD_outBuffer output = {
210✔
1136
                                .dst = out_buff,
1137
                                .size = out_allocsize,
1138
                                .pos = 0
1139
                        };
1140
                        size_t remaining;
210✔
1141
                        ssize_t wrote;
210✔
1142

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

1151
                        if (sym_ZSTD_isError(remaining)) {
210✔
1152
                                log_debug("ZSTD encoder failed: %s", sym_ZSTD_getErrorName(remaining));
×
1153
                                return zstd_ret_to_errno(remaining);
×
1154
                        }
1155

1156
                        if (left < output.pos)
210✔
1157
                                return -EFBIG;
1158

1159
                        wrote = loop_write_full(fdt, output.dst, output.pos, USEC_INFINITY);
210✔
1160
                        if (wrote < 0)
210✔
1161
                                return wrote;
1162

1163
                        left -= output.pos;
210✔
1164

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

1173
                /* zstd only returns 0 when the input is completely consumed */
1174
                assert(input.pos == input.size);
210✔
1175
                if (is_last_chunk)
210✔
1176
                        break;
1177
        }
1178

1179
        if (ret_uncompressed_size)
21✔
1180
                *ret_uncompressed_size = in_bytes;
21✔
1181

1182
        if (in_bytes > 0)
21✔
1183
                log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
21✔
1184
                          in_bytes, max_bytes - left, (double) (max_bytes - left) / in_bytes * 100);
1185
        else
1186
                log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes)",
×
1187
                          in_bytes, max_bytes - left);
1188

1189
        return 0;
1190
#else
1191
        return -EPROTONOSUPPORT;
1192
#endif
1193
}
1194

1195
int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
13✔
1196
        assert(fdf >= 0);
13✔
1197
        assert(fdt >= 0);
13✔
1198

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

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

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

1234
                red = loop_read(fdf, in_buff, in_allocsize, true);
23✔
1235
                if (red < 0)
23✔
1236
                        return red;
2✔
1237
                if (red == 0)
23✔
1238
                        break;
1239

1240
                in_bytes += (size_t) red;
13✔
1241
                input.size = (size_t) red;
13✔
1242
                input.pos = 0;
13✔
1243

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

1270
                        if (left < output.pos)
48✔
1271
                                return -EFBIG;
2✔
1272

1273
                        wrote = loop_write_full(fdt, output.dst, output.pos, USEC_INFINITY);
47✔
1274
                        if (wrote < 0)
47✔
1275
                                return wrote;
1276

1277
                        left -= output.pos;
46✔
1278
                }
1279
                if (has_error)
11✔
1280
                        break;
1281
        }
1282

1283
        if (in_bytes == 0)
11✔
1284
                return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoder failed: no data read");
×
1285

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

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

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

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