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