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