]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/compress.c
basic: rename util.h to logarithm.h
[thirdparty/systemd.git] / src / basic / compress.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
e4e61fdb 2
82e24b00 3#include <inttypes.h>
319a4f4b 4#include <malloc.h>
e4e61fdb 5#include <stdlib.h>
4b5bc539 6#include <sys/mman.h>
ca78ad1d 7#include <sys/stat.h>
319a4f4b 8#include <sys/types.h>
07630cea 9#include <unistd.h>
d89c8fdf 10
349cc4a5 11#if HAVE_XZ
3ffd4af2 12#include <lzma.h>
d89c8fdf
ZJS
13#endif
14
349cc4a5 15#if HAVE_LZ4
3ffd4af2
LP
16#include <lz4.h>
17#include <lz4frame.h>
d89c8fdf 18#endif
e4e61fdb 19
ef5924aa
NL
20#if HAVE_ZSTD
21#include <zstd.h>
22#include <zstd_errors.h>
23#endif
24
b5efdb8a 25#include "alloc-util.h"
e4e61fdb 26#include "compress.h"
3ffd4af2 27#include "fd-util.h"
1a823cde 28#include "fileio.h"
c004493c 29#include "io-util.h"
355b59e2 30#include "macro.h"
d89c8fdf 31#include "sparse-endian.h"
8b43440b 32#include "string-table.h"
07630cea 33#include "string-util.h"
4094c4bf 34#include "unaligned.h"
d89c8fdf 35
349cc4a5 36#if HAVE_LZ4
fd421c4a
ZJS
37DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_compressionContext_t, LZ4F_freeCompressionContext, NULL);
38DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext, NULL);
4b5bc539
ZJS
39#endif
40
ef5924aa 41#if HAVE_ZSTD
fd421c4a
ZJS
42DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_CCtx*, ZSTD_freeCCtx, NULL);
43DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_DCtx*, ZSTD_freeDCtx, NULL);
ef5924aa
NL
44
45static int zstd_ret_to_errno(size_t ret) {
46 switch (ZSTD_getErrorCode(ret)) {
47 case ZSTD_error_dstSize_tooSmall:
48 return -ENOBUFS;
49 case ZSTD_error_memory_allocation:
50 return -ENOMEM;
51 default:
52 return -EBADMSG;
53 }
54}
55#endif
56
d89c8fdf
ZJS
57#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
58
acc50c92 59static const char* const compression_table[_COMPRESSION_MAX] = {
4d698d12 60 [COMPRESSION_NONE] = "NONE",
acc50c92
LP
61 [COMPRESSION_XZ] = "XZ",
62 [COMPRESSION_LZ4] = "LZ4",
63 [COMPRESSION_ZSTD] = "ZSTD",
d89c8fdf 64};
e4e61fdb 65
acc50c92 66DEFINE_STRING_TABLE_LOOKUP(compression, Compression);
d89c8fdf 67
5d6f46b6
ZJS
68int compress_blob_xz(const void *src, uint64_t src_size,
69 void *dst, size_t dst_alloc_size, size_t *dst_size) {
349cc4a5 70#if HAVE_XZ
1930eed2
JS
71 static const lzma_options_lzma opt = {
72 1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
0240c603
LP
73 LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4
74 };
75 static const lzma_filter filters[] = {
76 { LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt },
77 { LZMA_VLI_UNKNOWN, NULL }
1930eed2 78 };
e4e61fdb 79 lzma_ret ret;
76cc0bf6 80 size_t out_pos = 0;
e4e61fdb
LP
81
82 assert(src);
83 assert(src_size > 0);
84 assert(dst);
5d6f46b6 85 assert(dst_alloc_size > 0);
e4e61fdb
LP
86 assert(dst_size);
87
d89c8fdf 88 /* Returns < 0 if we couldn't compress the data or the
e4e61fdb
LP
89 * compressed result is longer than the original */
90
1930eed2
JS
91 if (src_size < 80)
92 return -ENOBUFS;
93
94 ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
5d6f46b6 95 src, src_size, dst, &out_pos, dst_alloc_size);
e4e61fdb 96 if (ret != LZMA_OK)
d89c8fdf 97 return -ENOBUFS;
e4e61fdb 98
76cc0bf6 99 *dst_size = out_pos;
acc50c92 100 return COMPRESSION_XZ;
d89c8fdf
ZJS
101#else
102 return -EPROTONOSUPPORT;
103#endif
e4e61fdb
LP
104}
105
5d6f46b6
ZJS
106int compress_blob_lz4(const void *src, uint64_t src_size,
107 void *dst, size_t dst_alloc_size, size_t *dst_size) {
349cc4a5 108#if HAVE_LZ4
d89c8fdf
ZJS
109 int r;
110
111 assert(src);
112 assert(src_size > 0);
113 assert(dst);
5d6f46b6 114 assert(dst_alloc_size > 0);
d89c8fdf
ZJS
115 assert(dst_size);
116
117 /* Returns < 0 if we couldn't compress the data or the
118 * compressed result is longer than the original */
119
120 if (src_size < 9)
121 return -ENOBUFS;
e4e61fdb 122
691b90d4 123 r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
d89c8fdf
ZJS
124 if (r <= 0)
125 return -ENOBUFS;
126
4094c4bf 127 unaligned_write_le64(dst, src_size);
d89c8fdf
ZJS
128 *dst_size = r + 8;
129
acc50c92 130 return COMPRESSION_LZ4;
d89c8fdf
ZJS
131#else
132 return -EPROTONOSUPPORT;
133#endif
134}
135
8653185a
LP
136int compress_blob_zstd(
137 const void *src, uint64_t src_size,
138 void *dst, size_t dst_alloc_size, size_t *dst_size) {
139#if HAVE_ZSTD
140 size_t k;
141
142 assert(src);
143 assert(src_size > 0);
144 assert(dst);
145 assert(dst_alloc_size > 0);
146 assert(dst_size);
147
148 k = ZSTD_compress(dst, dst_alloc_size, src, src_size, 0);
149 if (ZSTD_isError(k))
150 return zstd_ret_to_errno(k);
151
152 *dst_size = k;
acc50c92 153 return COMPRESSION_ZSTD;
8653185a
LP
154#else
155 return -EPROTONOSUPPORT;
156#endif
157}
158
319a4f4b
LP
159int decompress_blob_xz(
160 const void *src,
161 uint64_t src_size,
162 void **dst,
163 size_t* dst_size,
164 size_t dst_max) {
d89c8fdf 165
349cc4a5 166#if HAVE_XZ
5e592c66 167 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
e4e61fdb 168 lzma_ret ret;
fa1c4b51 169 size_t space;
e4e61fdb
LP
170
171 assert(src);
172 assert(src_size > 0);
173 assert(dst);
e4e61fdb 174 assert(dst_size);
e4e61fdb
LP
175
176 ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
177 if (ret != LZMA_OK)
d89c8fdf 178 return -ENOMEM;
e4e61fdb 179
f5fbe71d 180 space = MIN(src_size * 2, dst_max ?: SIZE_MAX);
319a4f4b 181 if (!greedy_realloc(dst, space, 1))
01c3322e 182 return -ENOMEM;
e4e61fdb
LP
183
184 s.next_in = src;
185 s.avail_in = src_size;
186
187 s.next_out = *dst;
93b73b06 188 s.avail_out = space;
e4e61fdb
LP
189
190 for (;;) {
fa1c4b51 191 size_t used;
e4e61fdb
LP
192
193 ret = lzma_code(&s, LZMA_FINISH);
194
195 if (ret == LZMA_STREAM_END)
196 break;
d89c8fdf
ZJS
197 else if (ret != LZMA_OK)
198 return -ENOMEM;
e4e61fdb 199
93b73b06
LP
200 if (dst_max > 0 && (space - s.avail_out) >= dst_max)
201 break;
d89c8fdf
ZJS
202 else if (dst_max > 0 && space == dst_max)
203 return -ENOBUFS;
93b73b06 204
5e592c66 205 used = space - s.avail_out;
f5fbe71d 206 space = MIN(2 * space, dst_max ?: SIZE_MAX);
319a4f4b 207 if (!greedy_realloc(dst, space, 1))
01c3322e 208 return -ENOMEM;
e4e61fdb 209
5e592c66 210 s.avail_out = space - used;
8e170d29 211 s.next_out = *(uint8_t**)dst + used;
e4e61fdb
LP
212 }
213
93b73b06 214 *dst_size = space - s.avail_out;
d89c8fdf
ZJS
215 return 0;
216#else
217 return -EPROTONOSUPPORT;
218#endif
e4e61fdb
LP
219}
220
319a4f4b
LP
221int decompress_blob_lz4(
222 const void *src,
223 uint64_t src_size,
224 void **dst,
225 size_t* dst_size,
226 size_t dst_max) {
d89c8fdf 227
349cc4a5 228#if HAVE_LZ4
d89c8fdf 229 char* out;
fa1c4b51 230 int r, size; /* LZ4 uses int for size */
e4e61fdb 231
d89c8fdf
ZJS
232 assert(src);
233 assert(src_size > 0);
234 assert(dst);
d89c8fdf 235 assert(dst_size);
d89c8fdf
ZJS
236
237 if (src_size <= 8)
238 return -EBADMSG;
239
4094c4bf
LP
240 size = unaligned_read_le64(src);
241 if (size < 0 || (unsigned) size != unaligned_read_le64(src))
fa1c4b51 242 return -EFBIG;
319a4f4b
LP
243 out = greedy_realloc(dst, size, 1);
244 if (!out)
245 return -ENOMEM;
d89c8fdf 246
8e170d29 247 r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
fa1c4b51 248 if (r < 0 || r != size)
d89c8fdf
ZJS
249 return -EBADMSG;
250
251 *dst_size = size;
252 return 0;
253#else
254 return -EPROTONOSUPPORT;
255#endif
256}
257
8653185a 258int decompress_blob_zstd(
319a4f4b
LP
259 const void *src,
260 uint64_t src_size,
261 void **dst,
262 size_t *dst_size,
263 size_t dst_max) {
8653185a
LP
264
265#if HAVE_ZSTD
a2415327 266 uint64_t size;
8653185a
LP
267
268 assert(src);
269 assert(src_size > 0);
270 assert(dst);
8653185a 271 assert(dst_size);
8653185a 272
a2415327
ZJS
273 size = ZSTD_getFrameContentSize(src, src_size);
274 if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
275 return -EBADMSG;
8653185a 276
a2415327
ZJS
277 if (dst_max > 0 && size > dst_max)
278 size = dst_max;
279 if (size > SIZE_MAX)
280 return -E2BIG;
8653185a 281
319a4f4b 282 if (!(greedy_realloc(dst, MAX(ZSTD_DStreamOutSize(), size), 1)))
a2415327 283 return -ENOMEM;
8653185a 284
a2415327
ZJS
285 _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx();
286 if (!dctx)
287 return -ENOMEM;
8653185a 288
a2415327
ZJS
289 ZSTD_inBuffer input = {
290 .src = src,
291 .size = src_size,
292 };
293 ZSTD_outBuffer output = {
294 .dst = *dst,
319a4f4b 295 .size = MALLOC_SIZEOF_SAFE(*dst),
a2415327 296 };
8653185a 297
a2415327
ZJS
298 size_t k = ZSTD_decompressStream(dctx, &output, &input);
299 if (ZSTD_isError(k)) {
300 log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k));
301 return zstd_ret_to_errno(k);
8653185a 302 }
a2415327
ZJS
303 assert(output.pos >= size);
304
305 *dst_size = size;
306 return 0;
8653185a
LP
307#else
308 return -EPROTONOSUPPORT;
309#endif
310}
311
312int decompress_blob(
acc50c92 313 Compression compression,
319a4f4b
LP
314 const void *src,
315 uint64_t src_size,
316 void **dst,
317 size_t* dst_size,
318 size_t dst_max) {
8653185a 319
acc50c92 320 if (compression == COMPRESSION_XZ)
8653185a
LP
321 return decompress_blob_xz(
322 src, src_size,
319a4f4b 323 dst, dst_size, dst_max);
acc50c92 324 else if (compression == COMPRESSION_LZ4)
8653185a
LP
325 return decompress_blob_lz4(
326 src, src_size,
319a4f4b 327 dst, dst_size, dst_max);
acc50c92 328 else if (compression == COMPRESSION_ZSTD)
8653185a
LP
329 return decompress_blob_zstd(
330 src, src_size,
319a4f4b 331 dst, dst_size, dst_max);
d89c8fdf 332 else
b4a11ca3 333 return -EPROTONOSUPPORT;
d89c8fdf
ZJS
334}
335
319a4f4b
LP
336int decompress_startswith_xz(
337 const void *src,
338 uint64_t src_size,
339 void **buffer,
340 const void *prefix,
341 size_t prefix_len,
342 uint8_t extra) {
d89c8fdf 343
349cc4a5 344#if HAVE_XZ
5e592c66 345 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
319a4f4b 346 size_t allocated;
e4e61fdb 347 lzma_ret ret;
e4e61fdb 348
319a4f4b
LP
349 /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
350 * follow the prefix */
e4e61fdb
LP
351
352 assert(src);
353 assert(src_size > 0);
354 assert(buffer);
e4e61fdb 355 assert(prefix);
e4e61fdb
LP
356
357 ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
358 if (ret != LZMA_OK)
d89c8fdf 359 return -EBADMSG;
e4e61fdb 360
319a4f4b 361 if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
d89c8fdf 362 return -ENOMEM;
e4e61fdb 363
319a4f4b
LP
364 allocated = MALLOC_SIZEOF_SAFE(*buffer);
365
e4e61fdb
LP
366 s.next_in = src;
367 s.avail_in = src_size;
368
369 s.next_out = *buffer;
319a4f4b 370 s.avail_out = allocated;
e4e61fdb
LP
371
372 for (;;) {
e4e61fdb
LP
373 ret = lzma_code(&s, LZMA_FINISH);
374
4c701096 375 if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
d89c8fdf 376 return -EBADMSG;
e4e61fdb 377
319a4f4b 378 if (allocated - s.avail_out >= prefix_len + 1)
5e592c66
ZJS
379 return memcmp(*buffer, prefix, prefix_len) == 0 &&
380 ((const uint8_t*) *buffer)[prefix_len] == extra;
e4e61fdb
LP
381
382 if (ret == LZMA_STREAM_END)
d89c8fdf 383 return 0;
e4e61fdb 384
319a4f4b 385 s.avail_out += allocated;
e4e61fdb 386
319a4f4b 387 if (!(greedy_realloc(buffer, allocated * 2, 1)))
d89c8fdf 388 return -ENOMEM;
e4e61fdb 389
319a4f4b
LP
390 allocated = MALLOC_SIZEOF_SAFE(*buffer);
391 s.next_out = *(uint8_t**)buffer + allocated - s.avail_out;
5e592c66 392 }
d89c8fdf
ZJS
393
394#else
395 return -EPROTONOSUPPORT;
396#endif
397}
398
319a4f4b
LP
399int decompress_startswith_lz4(
400 const void *src,
401 uint64_t src_size,
402 void **buffer,
403 const void *prefix,
404 size_t prefix_len,
405 uint8_t extra) {
406
349cc4a5 407#if HAVE_LZ4
319a4f4b
LP
408 /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
409 * follow the prefix */
d89c8fdf 410
319a4f4b 411 size_t allocated;
d89c8fdf
ZJS
412 int r;
413
414 assert(src);
415 assert(src_size > 0);
416 assert(buffer);
d89c8fdf 417 assert(prefix);
d89c8fdf
ZJS
418
419 if (src_size <= 8)
420 return -EBADMSG;
421
319a4f4b 422 if (!(greedy_realloc(buffer, ALIGN_8(prefix_len + 1), 1)))
d89c8fdf 423 return -ENOMEM;
319a4f4b
LP
424 allocated = MALLOC_SIZEOF_SAFE(*buffer);
425
426 r = LZ4_decompress_safe_partial(
427 (char*)src + 8,
428 *buffer,
429 src_size - 8,
430 prefix_len + 1,
431 allocated);
432
433 /* One lz4 < 1.8.3, we might get "failure" (r < 0), or "success" where just a part of the buffer is
434 * decompressed. But if we get a smaller amount of bytes than requested, we don't know whether there
435 * isn't enough data to fill the requested size or whether we just got a partial answer.
e41ef6fd
ZJS
436 */
437 if (r < 0 || (size_t) r < prefix_len + 1) {
438 size_t size;
439
440 if (LZ4_versionNumber() >= 10803)
441 /* We trust that the newer lz4 decompresses the number of bytes we
442 * requested if available in the compressed string. */
443 return 0;
444
445 if (r > 0)
446 /* Compare what we have first, in case of mismatch we can
447 * shortcut the full comparison. */
448 if (memcmp(*buffer, prefix, r) != 0)
449 return 0;
450
451 /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
452 * so in pathological cases might need to decompress the full field. */
319a4f4b 453 r = decompress_blob_lz4(src, src_size, buffer, &size, 0);
1f4b467d
ZJS
454 if (r < 0)
455 return r;
d89c8fdf 456
e41ef6fd
ZJS
457 if (size < prefix_len + 1)
458 return 0;
459 }
d89c8fdf 460
e41ef6fd
ZJS
461 return memcmp(*buffer, prefix, prefix_len) == 0 &&
462 ((const uint8_t*) *buffer)[prefix_len] == extra;
d89c8fdf
ZJS
463#else
464 return -EPROTONOSUPPORT;
465#endif
e4e61fdb 466}
355b59e2 467
8653185a 468int decompress_startswith_zstd(
319a4f4b
LP
469 const void *src,
470 uint64_t src_size,
471 void **buffer,
472 const void *prefix,
473 size_t prefix_len,
8653185a
LP
474 uint8_t extra) {
475#if HAVE_ZSTD
8653185a
LP
476 assert(src);
477 assert(src_size > 0);
478 assert(buffer);
8653185a 479 assert(prefix);
8653185a 480
e4a321fc
ZJS
481 uint64_t size = ZSTD_getFrameContentSize(src, src_size);
482 if (IN_SET(size, ZSTD_CONTENTSIZE_ERROR, ZSTD_CONTENTSIZE_UNKNOWN))
483 return -EBADMSG;
484
485 if (size < prefix_len + 1)
486 return 0; /* Decompressed text too short to match the prefix and extra */
487
488 _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = ZSTD_createDCtx();
8653185a
LP
489 if (!dctx)
490 return -ENOMEM;
491
319a4f4b 492 if (!(greedy_realloc(buffer, MAX(ZSTD_DStreamOutSize(), prefix_len + 1), 1)))
8653185a
LP
493 return -ENOMEM;
494
495 ZSTD_inBuffer input = {
496 .src = src,
497 .size = src_size,
498 };
499 ZSTD_outBuffer output = {
500 .dst = *buffer,
319a4f4b 501 .size = MALLOC_SIZEOF_SAFE(*buffer),
8653185a 502 };
e4a321fc 503 size_t k;
8653185a 504
e4a321fc
ZJS
505 k = ZSTD_decompressStream(dctx, &output, &input);
506 if (ZSTD_isError(k)) {
507 log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(k));
508 return zstd_ret_to_errno(k);
8653185a 509 }
e4a321fc
ZJS
510 assert(output.pos >= prefix_len + 1);
511
512 return memcmp(*buffer, prefix, prefix_len) == 0 &&
513 ((const uint8_t*) *buffer)[prefix_len] == extra;
8653185a
LP
514#else
515 return -EPROTONOSUPPORT;
516#endif
517}
518
519int decompress_startswith(
acc50c92 520 Compression compression,
319a4f4b
LP
521 const void *src,
522 uint64_t src_size,
523 void **buffer,
524 const void *prefix,
525 size_t prefix_len,
8653185a
LP
526 uint8_t extra) {
527
acc50c92 528 if (compression == COMPRESSION_XZ)
8653185a
LP
529 return decompress_startswith_xz(
530 src, src_size,
319a4f4b 531 buffer,
8653185a
LP
532 prefix, prefix_len,
533 extra);
534
acc50c92 535 else if (compression == COMPRESSION_LZ4)
8653185a
LP
536 return decompress_startswith_lz4(
537 src, src_size,
319a4f4b 538 buffer,
8653185a
LP
539 prefix, prefix_len,
540 extra);
acc50c92 541 else if (compression == COMPRESSION_ZSTD)
8653185a
LP
542 return decompress_startswith_zstd(
543 src, src_size,
319a4f4b 544 buffer,
8653185a
LP
545 prefix, prefix_len,
546 extra);
d89c8fdf
ZJS
547 else
548 return -EBADMSG;
549}
550
5b6f8e13 551int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
349cc4a5 552#if HAVE_XZ
355b59e2
ZJS
553 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
554 lzma_ret ret;
355b59e2
ZJS
555 uint8_t buf[BUFSIZ], out[BUFSIZ];
556 lzma_action action = LZMA_RUN;
557
558 assert(fdf >= 0);
559 assert(fdt >= 0);
560
d89c8fdf 561 ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
d7a0f1f4
FS
562 if (ret != LZMA_OK)
563 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
564 "Failed to initialize XZ encoder: code %u",
565 ret);
355b59e2
ZJS
566
567 for (;;) {
568 if (s.avail_in == 0 && action == LZMA_RUN) {
569 size_t m = sizeof(buf);
570 ssize_t n;
571
f5fbe71d 572 if (max_bytes != UINT64_MAX && (uint64_t) m > max_bytes)
59f448cf 573 m = (size_t) max_bytes;
355b59e2
ZJS
574
575 n = read(fdf, buf, m);
576 if (n < 0)
577 return -errno;
578 if (n == 0)
579 action = LZMA_FINISH;
580 else {
581 s.next_in = buf;
582 s.avail_in = n;
583
f5fbe71d 584 if (max_bytes != UINT64_MAX) {
59f448cf 585 assert(max_bytes >= (uint64_t) n);
355b59e2
ZJS
586 max_bytes -= n;
587 }
588 }
589 }
590
591 if (s.avail_out == 0) {
592 s.next_out = out;
593 s.avail_out = sizeof(out);
594 }
595
596 ret = lzma_code(&s, action);
d7a0f1f4
FS
597 if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
598 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG),
599 "Compression failed: code %u",
600 ret);
355b59e2
ZJS
601
602 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
603 ssize_t n, k;
604
605 n = sizeof(out) - s.avail_out;
606
355b59e2
ZJS
607 k = loop_write(fdt, out, n, false);
608 if (k < 0)
609 return k;
355b59e2
ZJS
610
611 if (ret == LZMA_STREAM_END) {
5b6f8e13
LB
612 if (ret_uncompressed_size)
613 *ret_uncompressed_size = s.total_in;
614
fa1c4b51 615 log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
355b59e2
ZJS
616 s.total_in, s.total_out,
617 (double) s.total_out / s.total_in * 100);
618
acc50c92 619 return COMPRESSION_XZ;
355b59e2
ZJS
620 }
621 }
622 }
3b1a55e1
ZJS
623#else
624 return -EPROTONOSUPPORT;
625#endif
355b59e2
ZJS
626}
627
4b5bc539 628#define LZ4_BUFSIZE (512*1024u)
d89c8fdf 629
5b6f8e13 630int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
d89c8fdf 631
349cc4a5 632#if HAVE_LZ4
4b5bc539
ZJS
633 LZ4F_errorCode_t c;
634 _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
834bab01
LB
635 _cleanup_free_ void *in_buff = NULL;
636 _cleanup_free_ char *out_buff = NULL;
5b6f8e13
LB
637 size_t out_allocsize, n, offset = 0, frame_size;
638 uint64_t total_in = 0, total_out;
4b5bc539 639 int r;
4b5bc539
ZJS
640 static const LZ4F_preferences_t preferences = {
641 .frameInfo.blockSizeID = 5,
642 };
d89c8fdf 643
4b5bc539
ZJS
644 c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
645 if (LZ4F_isError(c))
646 return -ENOMEM;
d89c8fdf 647
4b5bc539 648 frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
834bab01
LB
649 out_allocsize = frame_size + 64*1024; /* add some space for header and trailer */
650 out_buff = malloc(out_allocsize);
651 if (!out_buff)
652 return -ENOMEM;
653
654 in_buff = malloc(LZ4_BUFSIZE);
655 if (!in_buff)
4b5bc539 656 return -ENOMEM;
d89c8fdf 657
834bab01 658 n = offset = total_out = LZ4F_compressBegin(ctx, out_buff, out_allocsize, &preferences);
4b5bc539
ZJS
659 if (LZ4F_isError(n))
660 return -EINVAL;
d89c8fdf 661
834bab01 662 log_debug("Buffer size is %zu bytes, header size %zu bytes.", out_allocsize, n);
d89c8fdf 663
834bab01 664 for (;;) {
4b5bc539 665 ssize_t k;
d89c8fdf 666
834bab01
LB
667 k = loop_read(fdf, in_buff, LZ4_BUFSIZE, true);
668 if (k < 0)
669 return k;
670 if (k == 0)
671 break;
672 n = LZ4F_compressUpdate(ctx, out_buff + offset, out_allocsize - offset,
673 in_buff, k, NULL);
674 if (LZ4F_isError(n))
675 return -ENOTRECOVERABLE;
d89c8fdf 676
4b5bc539
ZJS
677 total_in += k;
678 offset += n;
679 total_out += n;
d89c8fdf 680
834bab01
LB
681 if (max_bytes != UINT64_MAX && total_out > (size_t) max_bytes)
682 return log_debug_errno(SYNTHETIC_ERRNO(EFBIG),
683 "Compressed stream longer than %" PRIu64 " bytes", max_bytes);
d89c8fdf 684
834bab01
LB
685 if (out_allocsize - offset < frame_size + 4) {
686 k = loop_write(fdt, out_buff, offset, false);
687 if (k < 0)
688 return k;
4b5bc539
ZJS
689 offset = 0;
690 }
691 }
d89c8fdf 692
834bab01
LB
693 n = LZ4F_compressEnd(ctx, out_buff + offset, out_allocsize - offset, NULL);
694 if (LZ4F_isError(n))
695 return -ENOTRECOVERABLE;
d89c8fdf 696
4b5bc539
ZJS
697 offset += n;
698 total_out += n;
834bab01 699 r = loop_write(fdt, out_buff, offset, false);
4b5bc539 700 if (r < 0)
834bab01 701 return r;
d89c8fdf 702
5b6f8e13
LB
703 if (ret_uncompressed_size)
704 *ret_uncompressed_size = total_in;
705
706 log_debug("LZ4 compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
d89c8fdf
ZJS
707 total_in, total_out,
708 (double) total_out / total_in * 100);
834bab01 709
acc50c92 710 return COMPRESSION_LZ4;
d89c8fdf
ZJS
711#else
712 return -EPROTONOSUPPORT;
713#endif
714}
715
59f448cf 716int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
d89c8fdf 717
349cc4a5 718#if HAVE_XZ
355b59e2
ZJS
719 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
720 lzma_ret ret;
721
722 uint8_t buf[BUFSIZ], out[BUFSIZ];
723 lzma_action action = LZMA_RUN;
724
725 assert(fdf >= 0);
726 assert(fdt >= 0);
727
728 ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
d7a0f1f4
FS
729 if (ret != LZMA_OK)
730 return log_debug_errno(SYNTHETIC_ERRNO(ENOMEM),
731 "Failed to initialize XZ decoder: code %u",
732 ret);
355b59e2
ZJS
733
734 for (;;) {
735 if (s.avail_in == 0 && action == LZMA_RUN) {
736 ssize_t n;
737
738 n = read(fdf, buf, sizeof(buf));
739 if (n < 0)
740 return -errno;
741 if (n == 0)
742 action = LZMA_FINISH;
743 else {
744 s.next_in = buf;
745 s.avail_in = n;
746 }
747 }
748
749 if (s.avail_out == 0) {
750 s.next_out = out;
751 s.avail_out = sizeof(out);
752 }
753
754 ret = lzma_code(&s, action);
d7a0f1f4
FS
755 if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
756 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG),
757 "Decompression failed: code %u",
758 ret);
355b59e2
ZJS
759
760 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
761 ssize_t n, k;
762
763 n = sizeof(out) - s.avail_out;
764
f5fbe71d 765 if (max_bytes != UINT64_MAX) {
59f448cf 766 if (max_bytes < (uint64_t) n)
d89c8fdf 767 return -EFBIG;
355b59e2
ZJS
768
769 max_bytes -= n;
770 }
771
355b59e2
ZJS
772 k = loop_write(fdt, out, n, false);
773 if (k < 0)
774 return k;
355b59e2
ZJS
775
776 if (ret == LZMA_STREAM_END) {
fa1c4b51 777 log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
355b59e2
ZJS
778 s.total_in, s.total_out,
779 (double) s.total_out / s.total_in * 100);
780
781 return 0;
782 }
783 }
784 }
d89c8fdf 785#else
d7a0f1f4
FS
786 return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
787 "Cannot decompress file. Compiled without XZ support.");
d89c8fdf
ZJS
788#endif
789}
790
8e64dd1e 791int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
349cc4a5 792#if HAVE_LZ4
4b5bc539
ZJS
793 size_t c;
794 _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
795 _cleanup_free_ char *buf = NULL;
796 char *src;
797 struct stat st;
798 int r = 0;
799 size_t total_in = 0, total_out = 0;
800
801 c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
802 if (LZ4F_isError(c))
803 return -ENOMEM;
804
805 if (fstat(in, &st) < 0)
5146f9f0 806 return log_debug_errno(errno, "fstat() failed: %m");
4b5bc539 807
1a823cde
TS
808 if (file_offset_beyond_memory_size(st.st_size))
809 return -EFBIG;
810
4b5bc539
ZJS
811 buf = malloc(LZ4_BUFSIZE);
812 if (!buf)
813 return -ENOMEM;
814
815 src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0);
e0685172 816 if (src == MAP_FAILED)
4b5bc539
ZJS
817 return -errno;
818
819 while (total_in < (size_t) st.st_size) {
820 size_t produced = LZ4_BUFSIZE;
821 size_t used = st.st_size - total_in;
822
823 c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
824 if (LZ4F_isError(c)) {
825 r = -EBADMSG;
826 goto cleanup;
827 }
828
829 total_in += used;
830 total_out += produced;
831
f5fbe71d 832 if (max_bytes != UINT64_MAX && total_out > (size_t) max_bytes) {
82e24b00 833 log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes);
5146f9f0 834 r = -EFBIG;
4b5bc539
ZJS
835 goto cleanup;
836 }
837
838 r = loop_write(out, buf, produced, false);
839 if (r < 0)
840 goto cleanup;
841 }
842
843 log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
844 total_in, total_out,
25048348 845 total_in > 0 ? (double) total_out / total_in * 100 : 0.0);
4b5bc539
ZJS
846 cleanup:
847 munmap(src, st.st_size);
848 return r;
d89c8fdf 849#else
d7a0f1f4
FS
850 return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
851 "Cannot decompress file. Compiled without LZ4 support.");
d89c8fdf
ZJS
852#endif
853}
854
5b6f8e13 855int compress_stream_zstd(int fdf, int fdt, uint64_t max_bytes, uint64_t *ret_uncompressed_size) {
ef5924aa
NL
856#if HAVE_ZSTD
857 _cleanup_(ZSTD_freeCCtxp) ZSTD_CCtx *cctx = NULL;
858 _cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
859 size_t in_allocsize, out_allocsize;
860 size_t z;
861 uint64_t left = max_bytes, in_bytes = 0;
ef5924aa
NL
862
863 assert(fdf >= 0);
864 assert(fdt >= 0);
865
866 /* Create the context and buffers */
867 in_allocsize = ZSTD_CStreamInSize();
868 out_allocsize = ZSTD_CStreamOutSize();
869 in_buff = malloc(in_allocsize);
870 out_buff = malloc(out_allocsize);
871 cctx = ZSTD_createCCtx();
872 if (!cctx || !out_buff || !in_buff)
873 return -ENOMEM;
874
ef5924aa
NL
875 z = ZSTD_CCtx_setParameter(cctx, ZSTD_c_checksumFlag, 1);
876 if (ZSTD_isError(z))
877 log_debug("Failed to enable ZSTD checksum, ignoring: %s", ZSTD_getErrorName(z));
878
879 /* This loop read from the input file, compresses that entire chunk,
880 * and writes all output produced to the output file.
881 */
882 for (;;) {
883 bool is_last_chunk;
884 ZSTD_inBuffer input = {
885 .src = in_buff,
886 .size = 0,
887 .pos = 0
888 };
889 ssize_t red;
890
891 red = loop_read(fdf, in_buff, in_allocsize, true);
892 if (red < 0)
893 return red;
894 is_last_chunk = red == 0;
895
896 in_bytes += (size_t) red;
897 input.size = (size_t) red;
898
899 for (bool finished = false; !finished;) {
900 ZSTD_outBuffer output = {
901 .dst = out_buff,
902 .size = out_allocsize,
903 .pos = 0
904 };
905 size_t remaining;
906 ssize_t wrote;
907
908 /* Compress into the output buffer and write all of the
909 * output to the file so we can reuse the buffer next
910 * iteration.
911 */
912 remaining = ZSTD_compressStream2(
913 cctx, &output, &input,
914 is_last_chunk ? ZSTD_e_end : ZSTD_e_continue);
915
916 if (ZSTD_isError(remaining)) {
917 log_debug("ZSTD encoder failed: %s", ZSTD_getErrorName(remaining));
918 return zstd_ret_to_errno(remaining);
919 }
920
921 if (left < output.pos)
922 return -EFBIG;
923
924 wrote = loop_write(fdt, output.dst, output.pos, 1);
925 if (wrote < 0)
926 return wrote;
927
928 left -= output.pos;
929
930 /* If we're on the last chunk we're finished when zstd
931 * returns 0, which means its consumed all the input AND
932 * finished the frame. Otherwise, we're finished when
933 * we've consumed all the input.
934 */
935 finished = is_last_chunk ? (remaining == 0) : (input.pos == input.size);
936 }
937
938 /* zstd only returns 0 when the input is completely consumed */
939 assert(input.pos == input.size);
940 if (is_last_chunk)
941 break;
942 }
943
5b6f8e13
LB
944 if (ret_uncompressed_size)
945 *ret_uncompressed_size = in_bytes;
946
89d36ce8
YW
947 if (in_bytes > 0)
948 log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
949 in_bytes, max_bytes - left, (double) (max_bytes - left) / in_bytes * 100);
950 else
951 log_debug("ZSTD compression finished (%" PRIu64 " -> %" PRIu64 " bytes)",
952 in_bytes, max_bytes - left);
ef5924aa 953
acc50c92 954 return COMPRESSION_ZSTD;
ef5924aa
NL
955#else
956 return -EPROTONOSUPPORT;
957#endif
958}
959
960int decompress_stream_zstd(int fdf, int fdt, uint64_t max_bytes) {
961#if HAVE_ZSTD
962 _cleanup_(ZSTD_freeDCtxp) ZSTD_DCtx *dctx = NULL;
963 _cleanup_free_ void *in_buff = NULL, *out_buff = NULL;
964 size_t in_allocsize, out_allocsize;
965 size_t last_result = 0;
966 uint64_t left = max_bytes, in_bytes = 0;
967
968 assert(fdf >= 0);
969 assert(fdt >= 0);
970
971 /* Create the context and buffers */
972 in_allocsize = ZSTD_DStreamInSize();
973 out_allocsize = ZSTD_DStreamOutSize();
974 in_buff = malloc(in_allocsize);
975 out_buff = malloc(out_allocsize);
976 dctx = ZSTD_createDCtx();
977 if (!dctx || !out_buff || !in_buff)
978 return -ENOMEM;
979
980 /* This loop assumes that the input file is one or more concatenated
981 * zstd streams. This example won't work if there is trailing non-zstd
982 * data at the end, but streaming decompression in general handles this
983 * case. ZSTD_decompressStream() returns 0 exactly when the frame is
984 * completed, and doesn't consume input after the frame.
985 */
986 for (;;) {
987 bool has_error = false;
988 ZSTD_inBuffer input = {
989 .src = in_buff,
990 .size = 0,
991 .pos = 0
992 };
993 ssize_t red;
994
995 red = loop_read(fdf, in_buff, in_allocsize, true);
996 if (red < 0)
997 return red;
998 if (red == 0)
999 break;
1000
1001 in_bytes += (size_t) red;
1002 input.size = (size_t) red;
1003 input.pos = 0;
1004
1005 /* Given a valid frame, zstd won't consume the last byte of the
1006 * frame until it has flushed all of the decompressed data of
1007 * the frame. So input.pos < input.size means frame is not done
1008 * or there is still output available.
1009 */
1010 while (input.pos < input.size) {
1011 ZSTD_outBuffer output = {
1012 .dst = out_buff,
1013 .size = out_allocsize,
1014 .pos = 0
1015 };
1016 ssize_t wrote;
1017 /* The return code is zero if the frame is complete, but
1018 * there may be multiple frames concatenated together.
1019 * Zstd will automatically reset the context when a
1020 * frame is complete. Still, calling ZSTD_DCtx_reset()
1021 * can be useful to reset the context to a clean state,
1022 * for instance if the last decompression call returned
1023 * an error.
1024 */
1025 last_result = ZSTD_decompressStream(dctx, &output, &input);
1026 if (ZSTD_isError(last_result)) {
1027 has_error = true;
1028 break;
1029 }
1030
1031 if (left < output.pos)
1032 return -EFBIG;
1033
1034 wrote = loop_write(fdt, output.dst, output.pos, 1);
1035 if (wrote < 0)
1036 return wrote;
1037
1038 left -= output.pos;
1039 }
1040 if (has_error)
1041 break;
1042 }
1043
1044 if (in_bytes == 0)
1045 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "ZSTD decoder failed: no data read");
1046
1047 if (last_result != 0) {
1048 /* The last return value from ZSTD_decompressStream did not end
1049 * on a frame, but we reached the end of the file! We assume
1050 * this is an error, and the input was truncated.
1051 */
1052 log_debug("ZSTD decoder failed: %s", ZSTD_getErrorName(last_result));
1053 return zstd_ret_to_errno(last_result);
1054 }
1055
1056 log_debug(
1057 "ZSTD decompression finished (%" PRIu64 " -> %" PRIu64 " bytes, %.1f%%)",
1058 in_bytes,
1059 max_bytes - left,
1060 (double) (max_bytes - left) / in_bytes * 100);
1061 return 0;
1062#else
d7a0f1f4
FS
1063 return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT),
1064 "Cannot decompress file. Compiled without ZSTD support.");
ef5924aa
NL
1065#endif
1066}
1067
59f448cf 1068int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) {
d89c8fdf
ZJS
1069
1070 if (endswith(filename, ".lz4"))
1071 return decompress_stream_lz4(fdf, fdt, max_bytes);
1072 else if (endswith(filename, ".xz"))
1073 return decompress_stream_xz(fdf, fdt, max_bytes);
ef5924aa
NL
1074 else if (endswith(filename, ".zst"))
1075 return decompress_stream_zstd(fdf, fdt, max_bytes);
d89c8fdf
ZJS
1076 else
1077 return -EPROTONOSUPPORT;
355b59e2 1078}