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