]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/compress.c
Merge pull request #1690 from evverx/run-runtime-directory
[thirdparty/systemd.git] / src / journal / compress.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/mman.h>
25 #include <unistd.h>
26
27 #ifdef HAVE_XZ
28 #include <lzma.h>
29 #endif
30
31 #ifdef HAVE_LZ4
32 #include <lz4.h>
33 #include <lz4frame.h>
34 #endif
35
36 #include "alloc-util.h"
37 #include "compress.h"
38 #include "fd-util.h"
39 #include "io-util.h"
40 #include "journal-def.h"
41 #include "macro.h"
42 #include "sparse-endian.h"
43 #include "string-table.h"
44 #include "string-util.h"
45 #include "util.h"
46
47 #ifdef HAVE_LZ4
48 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionContext);
49 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext);
50 #endif
51
52 #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
53
54 static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = {
55 [OBJECT_COMPRESSED_XZ] = "XZ",
56 [OBJECT_COMPRESSED_LZ4] = "LZ4",
57 };
58
59 DEFINE_STRING_TABLE_LOOKUP(object_compressed, int);
60
61 int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
62 #ifdef HAVE_XZ
63 static const lzma_options_lzma opt = {
64 1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
65 LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4
66 };
67 static const lzma_filter filters[] = {
68 { LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt },
69 { LZMA_VLI_UNKNOWN, NULL }
70 };
71 lzma_ret ret;
72 size_t out_pos = 0;
73
74 assert(src);
75 assert(src_size > 0);
76 assert(dst);
77 assert(dst_size);
78
79 /* Returns < 0 if we couldn't compress the data or the
80 * compressed result is longer than the original */
81
82 if (src_size < 80)
83 return -ENOBUFS;
84
85 ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
86 src, src_size, dst, &out_pos, src_size - 1);
87 if (ret != LZMA_OK)
88 return -ENOBUFS;
89
90 *dst_size = out_pos;
91 return 0;
92 #else
93 return -EPROTONOSUPPORT;
94 #endif
95 }
96
97 int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
98 #ifdef HAVE_LZ4
99 int r;
100
101 assert(src);
102 assert(src_size > 0);
103 assert(dst);
104 assert(dst_size);
105
106 /* Returns < 0 if we couldn't compress the data or the
107 * compressed result is longer than the original */
108
109 if (src_size < 9)
110 return -ENOBUFS;
111
112 r = LZ4_compress_limitedOutput(src, dst + 8, src_size, src_size - 8 - 1);
113 if (r <= 0)
114 return -ENOBUFS;
115
116 *(le64_t*) dst = htole64(src_size);
117 *dst_size = r + 8;
118
119 return 0;
120 #else
121 return -EPROTONOSUPPORT;
122 #endif
123 }
124
125
126 int decompress_blob_xz(const void *src, uint64_t src_size,
127 void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
128
129 #ifdef HAVE_XZ
130 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
131 lzma_ret ret;
132 size_t space;
133
134 assert(src);
135 assert(src_size > 0);
136 assert(dst);
137 assert(dst_alloc_size);
138 assert(dst_size);
139 assert(*dst_alloc_size == 0 || *dst);
140
141 ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
142 if (ret != LZMA_OK)
143 return -ENOMEM;
144
145 space = MIN(src_size * 2, dst_max ?: (size_t) -1);
146 if (!greedy_realloc(dst, dst_alloc_size, space, 1))
147 return -ENOMEM;
148
149 s.next_in = src;
150 s.avail_in = src_size;
151
152 s.next_out = *dst;
153 s.avail_out = space;
154
155 for (;;) {
156 size_t used;
157
158 ret = lzma_code(&s, LZMA_FINISH);
159
160 if (ret == LZMA_STREAM_END)
161 break;
162 else if (ret != LZMA_OK)
163 return -ENOMEM;
164
165 if (dst_max > 0 && (space - s.avail_out) >= dst_max)
166 break;
167 else if (dst_max > 0 && space == dst_max)
168 return -ENOBUFS;
169
170 used = space - s.avail_out;
171 space = MIN(2 * space, dst_max ?: (size_t) -1);
172 if (!greedy_realloc(dst, dst_alloc_size, space, 1))
173 return -ENOMEM;
174
175 s.avail_out = space - used;
176 s.next_out = *dst + used;
177 }
178
179 *dst_size = space - s.avail_out;
180 return 0;
181 #else
182 return -EPROTONOSUPPORT;
183 #endif
184 }
185
186 int decompress_blob_lz4(const void *src, uint64_t src_size,
187 void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
188
189 #ifdef HAVE_LZ4
190 char* out;
191 int r, size; /* LZ4 uses int for size */
192
193 assert(src);
194 assert(src_size > 0);
195 assert(dst);
196 assert(dst_alloc_size);
197 assert(dst_size);
198 assert(*dst_alloc_size == 0 || *dst);
199
200 if (src_size <= 8)
201 return -EBADMSG;
202
203 size = le64toh( *(le64_t*)src );
204 if (size < 0 || (le64_t) size != *(le64_t*)src)
205 return -EFBIG;
206 if ((size_t) size > *dst_alloc_size) {
207 out = realloc(*dst, size);
208 if (!out)
209 return -ENOMEM;
210 *dst = out;
211 *dst_alloc_size = size;
212 } else
213 out = *dst;
214
215 r = LZ4_decompress_safe(src + 8, out, src_size - 8, size);
216 if (r < 0 || r != size)
217 return -EBADMSG;
218
219 *dst_size = size;
220 return 0;
221 #else
222 return -EPROTONOSUPPORT;
223 #endif
224 }
225
226 int decompress_blob(int compression,
227 const void *src, uint64_t src_size,
228 void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
229 if (compression == OBJECT_COMPRESSED_XZ)
230 return decompress_blob_xz(src, src_size,
231 dst, dst_alloc_size, dst_size, dst_max);
232 else if (compression == OBJECT_COMPRESSED_LZ4)
233 return decompress_blob_lz4(src, src_size,
234 dst, dst_alloc_size, dst_size, dst_max);
235 else
236 return -EBADMSG;
237 }
238
239
240 int decompress_startswith_xz(const void *src, uint64_t src_size,
241 void **buffer, size_t *buffer_size,
242 const void *prefix, size_t prefix_len,
243 uint8_t extra) {
244
245 #ifdef HAVE_XZ
246 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
247 lzma_ret ret;
248
249 /* Checks whether the decompressed blob starts with the
250 * mentioned prefix. The byte extra needs to follow the
251 * prefix */
252
253 assert(src);
254 assert(src_size > 0);
255 assert(buffer);
256 assert(buffer_size);
257 assert(prefix);
258 assert(*buffer_size == 0 || *buffer);
259
260 ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
261 if (ret != LZMA_OK)
262 return -EBADMSG;
263
264 if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
265 return -ENOMEM;
266
267 s.next_in = src;
268 s.avail_in = src_size;
269
270 s.next_out = *buffer;
271 s.avail_out = *buffer_size;
272
273 for (;;) {
274 ret = lzma_code(&s, LZMA_FINISH);
275
276 if (ret != LZMA_STREAM_END && ret != LZMA_OK)
277 return -EBADMSG;
278
279 if (*buffer_size - s.avail_out >= prefix_len + 1)
280 return memcmp(*buffer, prefix, prefix_len) == 0 &&
281 ((const uint8_t*) *buffer)[prefix_len] == extra;
282
283 if (ret == LZMA_STREAM_END)
284 return 0;
285
286 s.avail_out += *buffer_size;
287
288 if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1)))
289 return -ENOMEM;
290
291 s.next_out = *buffer + *buffer_size - s.avail_out;
292 }
293
294 #else
295 return -EPROTONOSUPPORT;
296 #endif
297 }
298
299 int decompress_startswith_lz4(const void *src, uint64_t src_size,
300 void **buffer, size_t *buffer_size,
301 const void *prefix, size_t prefix_len,
302 uint8_t extra) {
303 #ifdef HAVE_LZ4
304 /* Checks whether the decompressed blob starts with the
305 * mentioned prefix. The byte extra needs to follow the
306 * prefix */
307
308 int r;
309
310 assert(src);
311 assert(src_size > 0);
312 assert(buffer);
313 assert(buffer_size);
314 assert(prefix);
315 assert(*buffer_size == 0 || *buffer);
316
317 if (src_size <= 8)
318 return -EBADMSG;
319
320 if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
321 return -ENOMEM;
322
323 r = LZ4_decompress_safe_partial(src + 8, *buffer, src_size - 8,
324 prefix_len + 1, *buffer_size);
325
326 if (r < 0)
327 return -EBADMSG;
328 if ((unsigned) r >= prefix_len + 1)
329 return memcmp(*buffer, prefix, prefix_len) == 0 &&
330 ((const uint8_t*) *buffer)[prefix_len] == extra;
331 else
332 return 0;
333
334 #else
335 return -EPROTONOSUPPORT;
336 #endif
337 }
338
339 int decompress_startswith(int compression,
340 const void *src, uint64_t src_size,
341 void **buffer, size_t *buffer_size,
342 const void *prefix, size_t prefix_len,
343 uint8_t extra) {
344 if (compression == OBJECT_COMPRESSED_XZ)
345 return decompress_startswith_xz(src, src_size,
346 buffer, buffer_size,
347 prefix, prefix_len,
348 extra);
349 else if (compression == OBJECT_COMPRESSED_LZ4)
350 return decompress_startswith_lz4(src, src_size,
351 buffer, buffer_size,
352 prefix, prefix_len,
353 extra);
354 else
355 return -EBADMSG;
356 }
357
358 int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
359 #ifdef HAVE_XZ
360 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
361 lzma_ret ret;
362 uint8_t buf[BUFSIZ], out[BUFSIZ];
363 lzma_action action = LZMA_RUN;
364
365 assert(fdf >= 0);
366 assert(fdt >= 0);
367
368 ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
369 if (ret != LZMA_OK) {
370 log_error("Failed to initialize XZ encoder: code %u", ret);
371 return -EINVAL;
372 }
373
374 for (;;) {
375 if (s.avail_in == 0 && action == LZMA_RUN) {
376 size_t m = sizeof(buf);
377 ssize_t n;
378
379 if (max_bytes != (uint64_t) -1 && (uint64_t) m > max_bytes)
380 m = (size_t) max_bytes;
381
382 n = read(fdf, buf, m);
383 if (n < 0)
384 return -errno;
385 if (n == 0)
386 action = LZMA_FINISH;
387 else {
388 s.next_in = buf;
389 s.avail_in = n;
390
391 if (max_bytes != (uint64_t) -1) {
392 assert(max_bytes >= (uint64_t) n);
393 max_bytes -= n;
394 }
395 }
396 }
397
398 if (s.avail_out == 0) {
399 s.next_out = out;
400 s.avail_out = sizeof(out);
401 }
402
403 ret = lzma_code(&s, action);
404 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
405 log_error("Compression failed: code %u", ret);
406 return -EBADMSG;
407 }
408
409 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
410 ssize_t n, k;
411
412 n = sizeof(out) - s.avail_out;
413
414 k = loop_write(fdt, out, n, false);
415 if (k < 0)
416 return k;
417
418 if (ret == LZMA_STREAM_END) {
419 log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
420 s.total_in, s.total_out,
421 (double) s.total_out / s.total_in * 100);
422
423 return 0;
424 }
425 }
426 }
427 #else
428 return -EPROTONOSUPPORT;
429 #endif
430 }
431
432 #define LZ4_BUFSIZE (512*1024u)
433
434 int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
435
436 #ifdef HAVE_LZ4
437 LZ4F_errorCode_t c;
438 _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
439 _cleanup_free_ char *buf = NULL;
440 char *src = NULL;
441 size_t size, n, total_in = 0, total_out = 0, offset = 0, frame_size;
442 struct stat st;
443 int r;
444 static const LZ4F_compressOptions_t options = {
445 .stableSrc = 1,
446 };
447 static const LZ4F_preferences_t preferences = {
448 .frameInfo.blockSizeID = 5,
449 };
450
451 c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
452 if (LZ4F_isError(c))
453 return -ENOMEM;
454
455 if (fstat(fdf, &st) < 0)
456 return log_debug_errno(errno, "fstat() failed: %m");
457
458 frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
459 size = frame_size + 64*1024; /* add some space for header and trailer */
460 buf = malloc(size);
461 if (!buf)
462 return -ENOMEM;
463
464 n = offset = LZ4F_compressBegin(ctx, buf, size, &preferences);
465 if (LZ4F_isError(n))
466 return -EINVAL;
467
468 src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0);
469 if (src == MAP_FAILED)
470 return -errno;
471
472 log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n);
473
474 while (total_in < (size_t) st.st_size) {
475 ssize_t k;
476
477 k = MIN(LZ4_BUFSIZE, st.st_size - total_in);
478 n = LZ4F_compressUpdate(ctx, buf + offset, size - offset,
479 src + total_in, k, &options);
480 if (LZ4F_isError(n)) {
481 r = -ENOTRECOVERABLE;
482 goto cleanup;
483 }
484
485 total_in += k;
486 offset += n;
487 total_out += n;
488
489 if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
490 log_debug("Compressed stream longer than %zd bytes", max_bytes);
491 return -EFBIG;
492 }
493
494 if (size - offset < frame_size + 4) {
495 k = loop_write(fdt, buf, offset, false);
496 if (k < 0) {
497 r = k;
498 goto cleanup;
499 }
500 offset = 0;
501 }
502 }
503
504 n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options);
505 if (LZ4F_isError(n)) {
506 r = -ENOTRECOVERABLE;
507 goto cleanup;
508 }
509
510 offset += n;
511 total_out += n;
512 r = loop_write(fdt, buf, offset, false);
513 if (r < 0)
514 goto cleanup;
515
516 log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
517 total_in, total_out,
518 (double) total_out / total_in * 100);
519 cleanup:
520 munmap(src, st.st_size);
521 return r;
522 #else
523 return -EPROTONOSUPPORT;
524 #endif
525 }
526
527 int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
528
529 #ifdef HAVE_XZ
530 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
531 lzma_ret ret;
532
533 uint8_t buf[BUFSIZ], out[BUFSIZ];
534 lzma_action action = LZMA_RUN;
535
536 assert(fdf >= 0);
537 assert(fdt >= 0);
538
539 ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
540 if (ret != LZMA_OK) {
541 log_debug("Failed to initialize XZ decoder: code %u", ret);
542 return -ENOMEM;
543 }
544
545 for (;;) {
546 if (s.avail_in == 0 && action == LZMA_RUN) {
547 ssize_t n;
548
549 n = read(fdf, buf, sizeof(buf));
550 if (n < 0)
551 return -errno;
552 if (n == 0)
553 action = LZMA_FINISH;
554 else {
555 s.next_in = buf;
556 s.avail_in = n;
557 }
558 }
559
560 if (s.avail_out == 0) {
561 s.next_out = out;
562 s.avail_out = sizeof(out);
563 }
564
565 ret = lzma_code(&s, action);
566 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
567 log_debug("Decompression failed: code %u", ret);
568 return -EBADMSG;
569 }
570
571 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
572 ssize_t n, k;
573
574 n = sizeof(out) - s.avail_out;
575
576 if (max_bytes != (uint64_t) -1) {
577 if (max_bytes < (uint64_t) n)
578 return -EFBIG;
579
580 max_bytes -= n;
581 }
582
583 k = loop_write(fdt, out, n, false);
584 if (k < 0)
585 return k;
586
587 if (ret == LZMA_STREAM_END) {
588 log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
589 s.total_in, s.total_out,
590 (double) s.total_out / s.total_in * 100);
591
592 return 0;
593 }
594 }
595 }
596 #else
597 log_debug("Cannot decompress file. Compiled without XZ support.");
598 return -EPROTONOSUPPORT;
599 #endif
600 }
601
602 #ifdef HAVE_LZ4
603 static int decompress_stream_lz4_v1(int fdf, int fdt, uint64_t max_bytes) {
604
605 _cleanup_free_ char *buf = NULL, *out = NULL;
606 size_t buf_size = 0;
607 LZ4_streamDecode_t lz4_data = {};
608 le32_t header;
609 size_t total_in = sizeof(header), total_out = 0;
610
611 assert(fdf >= 0);
612 assert(fdt >= 0);
613
614 out = malloc(4*LZ4_BUFSIZE);
615 if (!out)
616 return -ENOMEM;
617
618 for (;;) {
619 ssize_t m;
620 int r;
621
622 r = loop_read_exact(fdf, &header, sizeof(header), false);
623 if (r < 0)
624 return r;
625
626 m = le32toh(header);
627 if (m == 0)
628 break;
629
630 /* We refuse to use a bigger decompression buffer than
631 * the one used for compression by 4 times. This means
632 * that compression buffer size can be enlarged 4
633 * times. This can be changed, but old binaries might
634 * not accept buffers compressed by newer binaries then.
635 */
636 if (m > LZ4_COMPRESSBOUND(LZ4_BUFSIZE * 4)) {
637 log_debug("Compressed stream block too big: %zd bytes", m);
638 return -ENOBUFS;
639 }
640
641 total_in += sizeof(header) + m;
642
643 if (!GREEDY_REALLOC(buf, buf_size, m))
644 return -ENOMEM;
645
646 r = loop_read_exact(fdf, buf, m, false);
647 if (r < 0)
648 return r;
649
650 r = LZ4_decompress_safe_continue(&lz4_data, buf, out, m, 4*LZ4_BUFSIZE);
651 if (r <= 0) {
652 log_debug("LZ4 decompression failed (legacy format).");
653 return -EBADMSG;
654 }
655
656 total_out += r;
657
658 if (max_bytes != (uint64_t) -1 && (uint64_t) total_out > max_bytes) {
659 log_debug("Decompressed stream longer than %" PRIu64 " bytes", max_bytes);
660 return -EFBIG;
661 }
662
663 r = loop_write(fdt, out, r, false);
664 if (r < 0)
665 return r;
666 }
667
668 log_debug("LZ4 decompression finished (legacy format, %zu -> %zu bytes, %.1f%%)",
669 total_in, total_out,
670 (double) total_out / total_in * 100);
671
672 return 0;
673 }
674
675 static int decompress_stream_lz4_v2(int in, int out, uint64_t max_bytes) {
676 size_t c;
677 _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
678 _cleanup_free_ char *buf = NULL;
679 char *src;
680 struct stat st;
681 int r = 0;
682 size_t total_in = 0, total_out = 0;
683
684 c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
685 if (LZ4F_isError(c))
686 return -ENOMEM;
687
688 if (fstat(in, &st) < 0)
689 return log_debug_errno(errno, "fstat() failed: %m");
690
691 buf = malloc(LZ4_BUFSIZE);
692 if (!buf)
693 return -ENOMEM;
694
695 src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0);
696 if (src == MAP_FAILED)
697 return -errno;
698
699 while (total_in < (size_t) st.st_size) {
700 size_t produced = LZ4_BUFSIZE;
701 size_t used = st.st_size - total_in;
702
703 c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
704 if (LZ4F_isError(c)) {
705 r = -EBADMSG;
706 goto cleanup;
707 }
708
709 total_in += used;
710 total_out += produced;
711
712 if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
713 log_debug("Decompressed stream longer than %zd bytes", max_bytes);
714 r = -EFBIG;
715 goto cleanup;
716 }
717
718 r = loop_write(out, buf, produced, false);
719 if (r < 0)
720 goto cleanup;
721 }
722
723 log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
724 total_in, total_out,
725 (double) total_out / total_in * 100);
726 cleanup:
727 munmap(src, st.st_size);
728 return r;
729 }
730 #endif
731
732 int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
733 #ifdef HAVE_LZ4
734 int r;
735
736 r = decompress_stream_lz4_v2(fdf, fdt, max_bytes);
737 if (r == -EBADMSG)
738 r = decompress_stream_lz4_v1(fdf, fdt, max_bytes);
739 return r;
740 #else
741 log_debug("Cannot decompress file. Compiled without LZ4 support.");
742 return -EPROTONOSUPPORT;
743 #endif
744 }
745
746 int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) {
747
748 if (endswith(filename, ".lz4"))
749 return decompress_stream_lz4(fdf, fdt, max_bytes);
750 else if (endswith(filename, ".xz"))
751 return decompress_stream_xz(fdf, fdt, max_bytes);
752 else
753 return -EPROTONOSUPPORT;
754 }