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