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