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