1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
20 #include <zstd_errors.h>
23 #include "alloc-util.h"
26 #include "dlfcn-util.h"
30 #include "string-table.h"
31 #include "string-util.h"
32 #include "unaligned.h"
35 static void *lz4_dl
= NULL
;
37 static DLSYM_PROTOTYPE(LZ4F_compressBegin
) = NULL
;
38 static DLSYM_PROTOTYPE(LZ4F_compressBound
) = NULL
;
39 static DLSYM_PROTOTYPE(LZ4F_compressEnd
) = NULL
;
40 static DLSYM_PROTOTYPE(LZ4F_compressUpdate
) = NULL
;
41 static DLSYM_PROTOTYPE(LZ4F_createCompressionContext
) = NULL
;
42 static DLSYM_PROTOTYPE(LZ4F_createDecompressionContext
) = NULL
;
43 static DLSYM_PROTOTYPE(LZ4F_decompress
) = NULL
;
44 static DLSYM_PROTOTYPE(LZ4F_freeCompressionContext
) = NULL
;
45 static DLSYM_PROTOTYPE(LZ4F_freeDecompressionContext
) = NULL
;
46 static DLSYM_PROTOTYPE(LZ4F_isError
) = NULL
;
47 static DLSYM_PROTOTYPE(LZ4_compress_HC
) = NULL
;
48 /* These are used in test-compress.c so we don't make them static. */
49 DLSYM_PROTOTYPE(LZ4_compress_default
) = NULL
;
50 DLSYM_PROTOTYPE(LZ4_decompress_safe
) = NULL
;
51 DLSYM_PROTOTYPE(LZ4_decompress_safe_partial
) = NULL
;
52 DLSYM_PROTOTYPE(LZ4_versionNumber
) = NULL
;
54 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_compressionContext_t
, sym_LZ4F_freeCompressionContext
, NULL
);
55 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(LZ4F_decompressionContext_t
, sym_LZ4F_freeDecompressionContext
, NULL
);
59 static void *zstd_dl
= NULL
;
61 static DLSYM_PROTOTYPE(ZSTD_CCtx_setParameter
) = NULL
;
62 static DLSYM_PROTOTYPE(ZSTD_compress
) = NULL
;
63 static DLSYM_PROTOTYPE(ZSTD_compressStream2
) = NULL
;
64 static DLSYM_PROTOTYPE(ZSTD_createCCtx
) = NULL
;
65 static DLSYM_PROTOTYPE(ZSTD_createDCtx
) = NULL
;
66 static DLSYM_PROTOTYPE(ZSTD_CStreamInSize
) = NULL
;
67 static DLSYM_PROTOTYPE(ZSTD_CStreamOutSize
) = NULL
;
68 static DLSYM_PROTOTYPE(ZSTD_decompressStream
) = NULL
;
69 static DLSYM_PROTOTYPE(ZSTD_DStreamInSize
) = NULL
;
70 static DLSYM_PROTOTYPE(ZSTD_DStreamOutSize
) = NULL
;
71 static DLSYM_PROTOTYPE(ZSTD_freeCCtx
) = NULL
;
72 static DLSYM_PROTOTYPE(ZSTD_freeDCtx
) = NULL
;
73 static DLSYM_PROTOTYPE(ZSTD_getErrorCode
) = NULL
;
74 static DLSYM_PROTOTYPE(ZSTD_getErrorName
) = NULL
;
75 static DLSYM_PROTOTYPE(ZSTD_getFrameContentSize
) = NULL
;
76 static DLSYM_PROTOTYPE(ZSTD_isError
) = NULL
;
78 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_CCtx
*, sym_ZSTD_freeCCtx
, NULL
);
79 DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(ZSTD_DCtx
*, sym_ZSTD_freeDCtx
, NULL
);
81 static int zstd_ret_to_errno(size_t ret
) {
82 switch (sym_ZSTD_getErrorCode(ret
)) {
83 case ZSTD_error_dstSize_tooSmall
:
85 case ZSTD_error_memory_allocation
:
94 static void *lzma_dl
= NULL
;
96 static DLSYM_PROTOTYPE(lzma_code
) = NULL
;
97 static DLSYM_PROTOTYPE(lzma_easy_encoder
) = NULL
;
98 static DLSYM_PROTOTYPE(lzma_end
) = NULL
;
99 static DLSYM_PROTOTYPE(lzma_stream_buffer_encode
) = NULL
;
100 static DLSYM_PROTOTYPE(lzma_stream_decoder
) = NULL
;
101 static DLSYM_PROTOTYPE(lzma_lzma_preset
) = NULL
;
103 /* We can't just do _cleanup_(sym_lzma_end) because a compiler bug makes
105 * ../src/basic/compress.c: In function ‘decompress_blob_xz’:
106 * ../src/basic/compress.c:304:9: error: cleanup argument not a function
107 * 304 | _cleanup_(sym_lzma_end) lzma_stream s = LZMA_STREAM_INIT;
110 static inline void lzma_end_wrapper(lzma_stream
*ls
) {
115 #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
117 static const char* const compression_table
[_COMPRESSION_MAX
] = {
118 [COMPRESSION_NONE
] = "NONE",
119 [COMPRESSION_XZ
] = "XZ",
120 [COMPRESSION_LZ4
] = "LZ4",
121 [COMPRESSION_ZSTD
] = "ZSTD",
124 static const char* const compression_lowercase_table
[_COMPRESSION_MAX
] = {
125 [COMPRESSION_NONE
] = "none",
126 [COMPRESSION_XZ
] = "xz",
127 [COMPRESSION_LZ4
] = "lz4",
128 [COMPRESSION_ZSTD
] = "zstd",
131 DEFINE_STRING_TABLE_LOOKUP(compression
, Compression
);
132 DEFINE_STRING_TABLE_LOOKUP(compression_lowercase
, Compression
);
134 bool compression_supported(Compression c
) {
135 static const unsigned supported
=
136 (1U << COMPRESSION_NONE
) |
137 (1U << COMPRESSION_XZ
) * HAVE_XZ
|
138 (1U << COMPRESSION_LZ4
) * HAVE_LZ4
|
139 (1U << COMPRESSION_ZSTD
) * HAVE_ZSTD
;
142 assert(c
< _COMPRESSION_MAX
);
144 return BIT_SET(supported
, c
);
148 int dlopen_lzma(void) {
149 ELF_NOTE_DLOPEN("lzma",
150 "Support lzma compression in journal and coredump files",
151 COMPRESSION_PRIORITY_XZ
,
154 return dlopen_many_sym_or_warn(
156 "liblzma.so.5", LOG_DEBUG
,
157 DLSYM_ARG(lzma_code
),
158 DLSYM_ARG(lzma_easy_encoder
),
160 DLSYM_ARG(lzma_stream_buffer_encode
),
161 DLSYM_ARG(lzma_lzma_preset
),
162 DLSYM_ARG(lzma_stream_decoder
));
166 int compress_blob_xz(const void *src
, uint64_t src_size
,
167 void *dst
, size_t dst_alloc_size
, size_t *dst_size
, int level
) {
170 assert(src_size
> 0);
172 assert(dst_alloc_size
> 0);
176 lzma_options_lzma opt
= {
177 1u << 20u, NULL
, 0, LZMA_LC_DEFAULT
, LZMA_LP_DEFAULT
,
178 LZMA_PB_DEFAULT
, LZMA_MODE_FAST
, 128, LZMA_MF_HC3
, 4
180 lzma_filter filters
[] = {
181 { LZMA_FILTER_LZMA2
, &opt
},
182 { LZMA_VLI_UNKNOWN
, NULL
}
193 r
= sym_lzma_lzma_preset(&opt
, (uint32_t) level
);
198 /* Returns < 0 if we couldn't compress the data or the
199 * compressed result is longer than the original */
204 ret
= sym_lzma_stream_buffer_encode(filters
, LZMA_CHECK_NONE
, NULL
,
205 src
, src_size
, dst
, &out_pos
, dst_alloc_size
);
212 return -EPROTONOSUPPORT
;
217 int dlopen_lz4(void) {
218 ELF_NOTE_DLOPEN("lz4",
219 "Support lz4 compression in journal and coredump files",
220 COMPRESSION_PRIORITY_LZ4
,
223 return dlopen_many_sym_or_warn(
225 "liblz4.so.1", LOG_DEBUG
,
226 DLSYM_ARG(LZ4F_compressBegin
),
227 DLSYM_ARG(LZ4F_compressBound
),
228 DLSYM_ARG(LZ4F_compressEnd
),
229 DLSYM_ARG(LZ4F_compressUpdate
),
230 DLSYM_ARG(LZ4F_createCompressionContext
),
231 DLSYM_ARG(LZ4F_createDecompressionContext
),
232 DLSYM_ARG(LZ4F_decompress
),
233 DLSYM_ARG(LZ4F_freeCompressionContext
),
234 DLSYM_ARG(LZ4F_freeDecompressionContext
),
235 DLSYM_ARG(LZ4F_isError
),
236 DLSYM_ARG(LZ4_compress_default
),
237 DLSYM_ARG(LZ4_compress_HC
),
238 DLSYM_ARG(LZ4_decompress_safe
),
239 DLSYM_ARG(LZ4_decompress_safe_partial
),
240 DLSYM_ARG(LZ4_versionNumber
));
244 int compress_blob_lz4(const void *src
, uint64_t src_size
,
245 void *dst
, size_t dst_alloc_size
, size_t *dst_size
, int level
) {
248 assert(src_size
> 0);
250 assert(dst_alloc_size
> 0);
259 /* Returns < 0 if we couldn't compress the data or the
260 * compressed result is longer than the original */
266 r
= sym_LZ4_compress_default(src
, (char*)dst
+ 8, src_size
, (int) dst_alloc_size
- 8);
268 r
= sym_LZ4_compress_HC(src
, (char*)dst
+ 8, src_size
, (int) dst_alloc_size
- 8, level
);
272 unaligned_write_le64(dst
, src_size
);
277 return -EPROTONOSUPPORT
;
282 int dlopen_zstd(void) {
283 ELF_NOTE_DLOPEN("zstd",
284 "Support zstd compression in journal and coredump files",
285 COMPRESSION_PRIORITY_ZSTD
,
288 return dlopen_many_sym_or_warn(
290 "libzstd.so.1", LOG_DEBUG
,
291 DLSYM_ARG(ZSTD_getErrorCode
),
292 DLSYM_ARG(ZSTD_compress
),
293 DLSYM_ARG(ZSTD_getFrameContentSize
),
294 DLSYM_ARG(ZSTD_decompressStream
),
295 DLSYM_ARG(ZSTD_getErrorName
),
296 DLSYM_ARG(ZSTD_DStreamOutSize
),
297 DLSYM_ARG(ZSTD_CStreamInSize
),
298 DLSYM_ARG(ZSTD_CStreamOutSize
),
299 DLSYM_ARG(ZSTD_CCtx_setParameter
),
300 DLSYM_ARG(ZSTD_compressStream2
),
301 DLSYM_ARG(ZSTD_DStreamInSize
),
302 DLSYM_ARG(ZSTD_freeCCtx
),
303 DLSYM_ARG(ZSTD_freeDCtx
),
304 DLSYM_ARG(ZSTD_isError
),
305 DLSYM_ARG(ZSTD_createDCtx
),
306 DLSYM_ARG(ZSTD_createCCtx
));
310 int compress_blob_zstd(
311 const void *src
, uint64_t src_size
,
312 void *dst
, size_t dst_alloc_size
, size_t *dst_size
, int level
) {
315 assert(src_size
> 0);
317 assert(dst_alloc_size
> 0);
328 k
= sym_ZSTD_compress(dst
, dst_alloc_size
, src
, src_size
, level
< 0 ? 0 : level
);
329 if (sym_ZSTD_isError(k
))
330 return zstd_ret_to_errno(k
);
335 return -EPROTONOSUPPORT
;
339 int decompress_blob_xz(
347 assert(src_size
> 0);
352 _cleanup_(lzma_end_wrapper
) lzma_stream s
= LZMA_STREAM_INIT
;
361 ret
= sym_lzma_stream_decoder(&s
, UINT64_MAX
, 0);
365 space
= MIN(src_size
* 2, dst_max
?: SIZE_MAX
);
366 if (!greedy_realloc(dst
, space
, 1))
370 s
.avail_in
= src_size
;
378 ret
= sym_lzma_code(&s
, LZMA_FINISH
);
379 if (ret
== LZMA_STREAM_END
)
384 if (dst_max
> 0 && (space
- s
.avail_out
) >= dst_max
)
386 if (dst_max
> 0 && space
== dst_max
)
389 used
= space
- s
.avail_out
;
390 space
= MIN(2 * space
, dst_max
?: SIZE_MAX
);
391 if (!greedy_realloc(dst
, space
, 1))
394 s
.avail_out
= space
- used
;
395 s
.next_out
= *(uint8_t**)dst
+ used
;
398 *dst_size
= space
- s
.avail_out
;
401 return -EPROTONOSUPPORT
;
405 int decompress_blob_lz4(
413 assert(src_size
> 0);
419 int r
, size
; /* LZ4 uses int for size */
428 size
= unaligned_read_le64(src
);
429 if (size
< 0 || (unsigned) size
!= unaligned_read_le64(src
))
431 out
= greedy_realloc(dst
, size
, 1);
435 r
= sym_LZ4_decompress_safe((char*)src
+ 8, out
, src_size
- 8, size
);
436 if (r
< 0 || r
!= size
)
442 return -EPROTONOSUPPORT
;
446 int decompress_blob_zstd(
454 assert(src_size
> 0);
466 size
= sym_ZSTD_getFrameContentSize(src
, src_size
);
467 if (IN_SET(size
, ZSTD_CONTENTSIZE_ERROR
, ZSTD_CONTENTSIZE_UNKNOWN
))
470 if (dst_max
> 0 && size
> dst_max
)
475 if (!(greedy_realloc(dst
, MAX(sym_ZSTD_DStreamOutSize(), size
), 1)))
478 _cleanup_(sym_ZSTD_freeDCtxp
) ZSTD_DCtx
*dctx
= sym_ZSTD_createDCtx();
482 ZSTD_inBuffer input
= {
486 ZSTD_outBuffer output
= {
488 .size
= MALLOC_SIZEOF_SAFE(*dst
),
491 size_t k
= sym_ZSTD_decompressStream(dctx
, &output
, &input
);
492 if (sym_ZSTD_isError(k
))
493 return log_debug_errno(zstd_ret_to_errno(k
), "ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k
));
494 if (output
.pos
< size
)
495 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "ZSTD decoded less data than indicated, probably corrupted stream.");
500 return -EPROTONOSUPPORT
;
505 Compression compression
,
512 switch (compression
) {
514 return decompress_blob_xz(
516 dst
, dst_size
, dst_max
);
517 case COMPRESSION_LZ4
:
518 return decompress_blob_lz4(
520 dst
, dst_size
, dst_max
);
521 case COMPRESSION_ZSTD
:
522 return decompress_blob_zstd(
524 dst
, dst_size
, dst_max
);
526 return -EPROTONOSUPPORT
;
530 int decompress_startswith_xz(
538 /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
539 * follow the prefix */
542 assert(src_size
> 0);
547 _cleanup_(lzma_end_wrapper
) lzma_stream s
= LZMA_STREAM_INIT
;
556 ret
= sym_lzma_stream_decoder(&s
, UINT64_MAX
, 0);
560 if (!(greedy_realloc(buffer
, ALIGN_8(prefix_len
+ 1), 1)))
563 allocated
= MALLOC_SIZEOF_SAFE(*buffer
);
566 s
.avail_in
= src_size
;
568 s
.next_out
= *buffer
;
569 s
.avail_out
= allocated
;
572 ret
= sym_lzma_code(&s
, LZMA_FINISH
);
574 if (!IN_SET(ret
, LZMA_OK
, LZMA_STREAM_END
))
577 if (allocated
- s
.avail_out
>= prefix_len
+ 1)
578 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
579 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
581 if (ret
== LZMA_STREAM_END
)
584 s
.avail_out
+= allocated
;
586 if (!(greedy_realloc(buffer
, allocated
* 2, 1)))
589 allocated
= MALLOC_SIZEOF_SAFE(*buffer
);
590 s
.next_out
= *(uint8_t**)buffer
+ allocated
- s
.avail_out
;
594 return -EPROTONOSUPPORT
;
598 int decompress_startswith_lz4(
606 /* Checks whether the decompressed blob starts with the mentioned prefix. The byte extra needs to
607 * follow the prefix */
610 assert(src_size
> 0);
625 if (!(greedy_realloc(buffer
, ALIGN_8(prefix_len
+ 1), 1)))
627 allocated
= MALLOC_SIZEOF_SAFE(*buffer
);
629 r
= sym_LZ4_decompress_safe_partial(
636 /* One lz4 < 1.8.3, we might get "failure" (r < 0), or "success" where just a part of the buffer is
637 * decompressed. But if we get a smaller amount of bytes than requested, we don't know whether there
638 * isn't enough data to fill the requested size or whether we just got a partial answer.
640 if (r
< 0 || (size_t) r
< prefix_len
+ 1) {
643 if (sym_LZ4_versionNumber() >= 10803)
644 /* We trust that the newer lz4 decompresses the number of bytes we
645 * requested if available in the compressed string. */
649 /* Compare what we have first, in case of mismatch we can
650 * shortcut the full comparison. */
651 if (memcmp(*buffer
, prefix
, r
) != 0)
654 /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
655 * so in pathological cases might need to decompress the full field. */
656 r
= decompress_blob_lz4(src
, src_size
, buffer
, &size
, 0);
660 if (size
< prefix_len
+ 1)
664 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
665 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
667 return -EPROTONOSUPPORT
;
671 int decompress_startswith_zstd(
680 assert(src_size
> 0);
691 uint64_t size
= sym_ZSTD_getFrameContentSize(src
, src_size
);
692 if (IN_SET(size
, ZSTD_CONTENTSIZE_ERROR
, ZSTD_CONTENTSIZE_UNKNOWN
))
695 if (size
< prefix_len
+ 1)
696 return 0; /* Decompressed text too short to match the prefix and extra */
698 _cleanup_(sym_ZSTD_freeDCtxp
) ZSTD_DCtx
*dctx
= sym_ZSTD_createDCtx();
702 if (!(greedy_realloc(buffer
, MAX(sym_ZSTD_DStreamOutSize(), prefix_len
+ 1), 1)))
705 ZSTD_inBuffer input
= {
709 ZSTD_outBuffer output
= {
711 .size
= MALLOC_SIZEOF_SAFE(*buffer
),
715 k
= sym_ZSTD_decompressStream(dctx
, &output
, &input
);
716 if (sym_ZSTD_isError(k
)) {
717 log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(k
));
718 return zstd_ret_to_errno(k
);
720 assert(output
.pos
>= prefix_len
+ 1);
722 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
723 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
725 return -EPROTONOSUPPORT
;
729 int decompress_startswith(
730 Compression compression
,
738 switch (compression
) {
741 return decompress_startswith_xz(
747 case COMPRESSION_LZ4
:
748 return decompress_startswith_lz4(
753 case COMPRESSION_ZSTD
:
754 return decompress_startswith_zstd(
764 int compress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
, uint64_t *ret_uncompressed_size
) {
769 _cleanup_(lzma_end_wrapper
) lzma_stream s
= LZMA_STREAM_INIT
;
771 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
772 lzma_action action
= LZMA_RUN
;
779 ret
= sym_lzma_easy_encoder(&s
, LZMA_PRESET_DEFAULT
, LZMA_CHECK_CRC64
);
781 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
782 "Failed to initialize XZ encoder: code %u",
786 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
787 size_t m
= sizeof(buf
);
790 if (max_bytes
!= UINT64_MAX
&& (uint64_t) m
> max_bytes
)
791 m
= (size_t) max_bytes
;
793 n
= read(fdf
, buf
, m
);
797 action
= LZMA_FINISH
;
802 if (max_bytes
!= UINT64_MAX
) {
803 assert(max_bytes
>= (uint64_t) n
);
809 if (s
.avail_out
== 0) {
811 s
.avail_out
= sizeof(out
);
814 ret
= sym_lzma_code(&s
, action
);
815 if (!IN_SET(ret
, LZMA_OK
, LZMA_STREAM_END
))
816 return log_error_errno(SYNTHETIC_ERRNO(EBADMSG
),
817 "Compression failed: code %u",
820 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
823 n
= sizeof(out
) - s
.avail_out
;
825 k
= loop_write(fdt
, out
, n
);
829 if (ret
== LZMA_STREAM_END
) {
830 if (ret_uncompressed_size
)
831 *ret_uncompressed_size
= s
.total_in
;
834 log_debug("XZ compression finished (no input data)");
836 log_debug("XZ compression finished (%"PRIu64
" -> %"PRIu64
" bytes, %.1f%%)",
837 s
.total_in
, s
.total_out
,
838 (double) s
.total_out
/ s
.total_in
* 100);
845 return -EPROTONOSUPPORT
;
849 #define LZ4_BUFSIZE (512*1024u)
851 int compress_stream_lz4(int fdf
, int fdt
, uint64_t max_bytes
, uint64_t *ret_uncompressed_size
) {
855 _cleanup_(sym_LZ4F_freeCompressionContextp
) LZ4F_compressionContext_t ctx
= NULL
;
856 _cleanup_free_
void *in_buff
= NULL
;
857 _cleanup_free_
char *out_buff
= NULL
;
858 size_t out_allocsize
, n
, offset
= 0, frame_size
;
859 uint64_t total_in
= 0, total_out
;
861 static const LZ4F_preferences_t preferences
= {
862 .frameInfo
.blockSizeID
= 5,
869 c
= sym_LZ4F_createCompressionContext(&ctx
, LZ4F_VERSION
);
870 if (sym_LZ4F_isError(c
))
873 frame_size
= sym_LZ4F_compressBound(LZ4_BUFSIZE
, &preferences
);
874 out_allocsize
= frame_size
+ 64*1024; /* add some space for header and trailer */
875 out_buff
= malloc(out_allocsize
);
879 in_buff
= malloc(LZ4_BUFSIZE
);
883 n
= offset
= total_out
= sym_LZ4F_compressBegin(ctx
, out_buff
, out_allocsize
, &preferences
);
884 if (sym_LZ4F_isError(n
))
887 log_debug("Buffer size is %zu bytes, header size %zu bytes.", out_allocsize
, n
);
892 k
= loop_read(fdf
, in_buff
, LZ4_BUFSIZE
, true);
897 n
= sym_LZ4F_compressUpdate(ctx
, out_buff
+ offset
, out_allocsize
- offset
,
899 if (sym_LZ4F_isError(n
))
900 return -ENOTRECOVERABLE
;
906 if (max_bytes
!= UINT64_MAX
&& total_out
> (size_t) max_bytes
)
907 return log_debug_errno(SYNTHETIC_ERRNO(EFBIG
),
908 "Compressed stream longer than %" PRIu64
" bytes", max_bytes
);
910 if (out_allocsize
- offset
< frame_size
+ 4) {
911 k
= loop_write(fdt
, out_buff
, offset
);
918 n
= sym_LZ4F_compressEnd(ctx
, out_buff
+ offset
, out_allocsize
- offset
, NULL
);
919 if (sym_LZ4F_isError(n
))
920 return -ENOTRECOVERABLE
;
924 r
= loop_write(fdt
, out_buff
, offset
);
928 if (ret_uncompressed_size
)
929 *ret_uncompressed_size
= total_in
;
932 log_debug("LZ4 compression finished (no input data)");
934 log_debug("LZ4 compression finished (%" PRIu64
" -> %" PRIu64
" bytes, %.1f%%)",
936 (double) total_out
/ total_in
* 100);
940 return -EPROTONOSUPPORT
;
944 int decompress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
) {
949 _cleanup_(lzma_end_wrapper
) lzma_stream s
= LZMA_STREAM_INIT
;
952 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
953 lzma_action action
= LZMA_RUN
;
960 ret
= sym_lzma_stream_decoder(&s
, UINT64_MAX
, 0);
962 return log_debug_errno(SYNTHETIC_ERRNO(ENOMEM
),
963 "Failed to initialize XZ decoder: code %u",
967 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
970 n
= read(fdf
, buf
, sizeof(buf
));
974 action
= LZMA_FINISH
;
981 if (s
.avail_out
== 0) {
983 s
.avail_out
= sizeof(out
);
986 ret
= sym_lzma_code(&s
, action
);
987 if (!IN_SET(ret
, LZMA_OK
, LZMA_STREAM_END
))
988 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
),
989 "Decompression failed: code %u",
992 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
995 n
= sizeof(out
) - s
.avail_out
;
997 if (max_bytes
!= UINT64_MAX
) {
998 if (max_bytes
< (uint64_t) n
)
1004 k
= loop_write(fdt
, out
, n
);
1008 if (ret
== LZMA_STREAM_END
) {
1009 if (s
.total_in
== 0)
1010 log_debug("XZ decompression finished (no input data)");
1012 log_debug("XZ decompression finished (%"PRIu64
" -> %"PRIu64
" bytes, %.1f%%)",
1013 s
.total_in
, s
.total_out
,
1014 (double) s
.total_out
/ s
.total_in
* 100);
1021 return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT
),
1022 "Cannot decompress file. Compiled without XZ support.");
1026 int decompress_stream_lz4(int in
, int out
, uint64_t max_bytes
) {
1029 _cleanup_(sym_LZ4F_freeDecompressionContextp
) LZ4F_decompressionContext_t ctx
= NULL
;
1030 _cleanup_free_
char *buf
= NULL
;
1034 size_t total_in
= 0, total_out
= 0;
1040 c
= sym_LZ4F_createDecompressionContext(&ctx
, LZ4F_VERSION
);
1041 if (sym_LZ4F_isError(c
))
1044 if (fstat(in
, &st
) < 0)
1045 return log_debug_errno(errno
, "fstat() failed: %m");
1047 if (file_offset_beyond_memory_size(st
.st_size
))
1050 buf
= malloc(LZ4_BUFSIZE
);
1054 src
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, in
, 0);
1055 if (src
== MAP_FAILED
)
1058 while (total_in
< (size_t) st
.st_size
) {
1059 size_t produced
= LZ4_BUFSIZE
;
1060 size_t used
= st
.st_size
- total_in
;
1062 c
= sym_LZ4F_decompress(ctx
, buf
, &produced
, src
+ total_in
, &used
, NULL
);
1063 if (sym_LZ4F_isError(c
)) {
1069 total_out
+= produced
;
1071 if (max_bytes
!= UINT64_MAX
&& total_out
> (size_t) max_bytes
) {
1072 log_debug("Decompressed stream longer than %"PRIu64
" bytes", max_bytes
);
1077 r
= loop_write(out
, buf
, produced
);
1083 log_debug("LZ4 decompression finished (no input data)");
1085 log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
1086 total_in
, total_out
,
1087 (double) total_out
/ total_in
* 100);
1090 munmap(src
, st
.st_size
);
1093 return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT
),
1094 "Cannot decompress file. Compiled without LZ4 support.");
1098 int compress_stream_zstd(int fdf
, int fdt
, uint64_t max_bytes
, uint64_t *ret_uncompressed_size
) {
1103 _cleanup_(sym_ZSTD_freeCCtxp
) ZSTD_CCtx
*cctx
= NULL
;
1104 _cleanup_free_
void *in_buff
= NULL
, *out_buff
= NULL
;
1105 size_t in_allocsize
, out_allocsize
;
1107 uint64_t left
= max_bytes
, in_bytes
= 0;
1114 /* Create the context and buffers */
1115 in_allocsize
= sym_ZSTD_CStreamInSize();
1116 out_allocsize
= sym_ZSTD_CStreamOutSize();
1117 in_buff
= malloc(in_allocsize
);
1118 out_buff
= malloc(out_allocsize
);
1119 cctx
= sym_ZSTD_createCCtx();
1120 if (!cctx
|| !out_buff
|| !in_buff
)
1123 z
= sym_ZSTD_CCtx_setParameter(cctx
, ZSTD_c_checksumFlag
, 1);
1124 if (sym_ZSTD_isError(z
))
1125 log_debug("Failed to enable ZSTD checksum, ignoring: %s", sym_ZSTD_getErrorName(z
));
1127 /* This loop read from the input file, compresses that entire chunk,
1128 * and writes all output produced to the output file.
1132 ZSTD_inBuffer input
= {
1139 red
= loop_read(fdf
, in_buff
, in_allocsize
, true);
1142 is_last_chunk
= red
== 0;
1144 in_bytes
+= (size_t) red
;
1145 input
.size
= (size_t) red
;
1147 for (bool finished
= false; !finished
;) {
1148 ZSTD_outBuffer output
= {
1150 .size
= out_allocsize
,
1156 /* Compress into the output buffer and write all of the
1157 * output to the file so we can reuse the buffer next
1160 remaining
= sym_ZSTD_compressStream2(
1161 cctx
, &output
, &input
,
1162 is_last_chunk
? ZSTD_e_end
: ZSTD_e_continue
);
1164 if (sym_ZSTD_isError(remaining
)) {
1165 log_debug("ZSTD encoder failed: %s", sym_ZSTD_getErrorName(remaining
));
1166 return zstd_ret_to_errno(remaining
);
1169 if (left
< output
.pos
)
1172 wrote
= loop_write_full(fdt
, output
.dst
, output
.pos
, USEC_INFINITY
);
1178 /* If we're on the last chunk we're finished when zstd
1179 * returns 0, which means its consumed all the input AND
1180 * finished the frame. Otherwise, we're finished when
1181 * we've consumed all the input.
1183 finished
= is_last_chunk
? (remaining
== 0) : (input
.pos
== input
.size
);
1186 /* zstd only returns 0 when the input is completely consumed */
1187 assert(input
.pos
== input
.size
);
1192 if (ret_uncompressed_size
)
1193 *ret_uncompressed_size
= in_bytes
;
1196 log_debug("ZSTD compression finished (no input data)");
1198 log_debug("ZSTD compression finished (%" PRIu64
" -> %" PRIu64
" bytes, %.1f%%)",
1199 in_bytes
, max_bytes
- left
, (double) (max_bytes
- left
) / in_bytes
* 100);
1203 return -EPROTONOSUPPORT
;
1207 int decompress_stream_zstd(int fdf
, int fdt
, uint64_t max_bytes
) {
1212 _cleanup_(sym_ZSTD_freeDCtxp
) ZSTD_DCtx
*dctx
= NULL
;
1213 _cleanup_free_
void *in_buff
= NULL
, *out_buff
= NULL
;
1214 size_t in_allocsize
, out_allocsize
;
1215 size_t last_result
= 0;
1216 uint64_t left
= max_bytes
, in_bytes
= 0;
1222 /* Create the context and buffers */
1223 in_allocsize
= sym_ZSTD_DStreamInSize();
1224 out_allocsize
= sym_ZSTD_DStreamOutSize();
1225 in_buff
= malloc(in_allocsize
);
1226 out_buff
= malloc(out_allocsize
);
1227 dctx
= sym_ZSTD_createDCtx();
1228 if (!dctx
|| !out_buff
|| !in_buff
)
1231 /* This loop assumes that the input file is one or more concatenated
1232 * zstd streams. This example won't work if there is trailing non-zstd
1233 * data at the end, but streaming decompression in general handles this
1234 * case. ZSTD_decompressStream() returns 0 exactly when the frame is
1235 * completed, and doesn't consume input after the frame.
1238 bool has_error
= false;
1239 ZSTD_inBuffer input
= {
1246 red
= loop_read(fdf
, in_buff
, in_allocsize
, true);
1252 in_bytes
+= (size_t) red
;
1253 input
.size
= (size_t) red
;
1256 /* Given a valid frame, zstd won't consume the last byte of the
1257 * frame until it has flushed all of the decompressed data of
1258 * the frame. So input.pos < input.size means frame is not done
1259 * or there is still output available.
1261 while (input
.pos
< input
.size
) {
1262 ZSTD_outBuffer output
= {
1264 .size
= out_allocsize
,
1268 /* The return code is zero if the frame is complete, but
1269 * there may be multiple frames concatenated together.
1270 * Zstd will automatically reset the context when a
1271 * frame is complete. Still, calling ZSTD_DCtx_reset()
1272 * can be useful to reset the context to a clean state,
1273 * for instance if the last decompression call returned
1276 last_result
= sym_ZSTD_decompressStream(dctx
, &output
, &input
);
1277 if (sym_ZSTD_isError(last_result
)) {
1282 if (left
< output
.pos
)
1285 wrote
= loop_write_full(fdt
, output
.dst
, output
.pos
, USEC_INFINITY
);
1296 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG
), "ZSTD decoder failed: no data read");
1298 if (last_result
!= 0) {
1299 /* The last return value from ZSTD_decompressStream did not end
1300 * on a frame, but we reached the end of the file! We assume
1301 * this is an error, and the input was truncated.
1303 log_debug("ZSTD decoder failed: %s", sym_ZSTD_getErrorName(last_result
));
1304 return zstd_ret_to_errno(last_result
);
1308 log_debug("ZSTD decompression finished (no input data)");
1310 log_debug("ZSTD decompression finished (%" PRIu64
" -> %" PRIu64
" bytes, %.1f%%)",
1313 (double) (max_bytes
- left
) / in_bytes
* 100);
1316 return log_debug_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT
),
1317 "Cannot decompress file. Compiled without ZSTD support.");
1321 int decompress_stream(const char *filename
, int fdf
, int fdt
, uint64_t max_bytes
) {
1323 if (endswith(filename
, ".lz4"))
1324 return decompress_stream_lz4(fdf
, fdt
, max_bytes
);
1325 if (endswith(filename
, ".xz"))
1326 return decompress_stream_xz(fdf
, fdt
, max_bytes
);
1327 if (endswith(filename
, ".zst"))
1328 return decompress_stream_zstd(fdf
, fdt
, max_bytes
);
1330 return -EPROTONOSUPPORT
;