1 /* SPDX-License-Identifier: LGPL-2.1+ */
19 #include "alloc-util.h"
23 #include "journal-def.h"
25 #include "sparse-endian.h"
26 #include "string-table.h"
27 #include "string-util.h"
28 #include "unaligned.h"
32 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t
, LZ4F_freeCompressionContext
);
33 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t
, LZ4F_freeDecompressionContext
);
36 #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
38 static const char* const object_compressed_table
[_OBJECT_COMPRESSED_MAX
] = {
39 [OBJECT_COMPRESSED_XZ
] = "XZ",
40 [OBJECT_COMPRESSED_LZ4
] = "LZ4",
43 DEFINE_STRING_TABLE_LOOKUP(object_compressed
, int);
45 int compress_blob_xz(const void *src
, uint64_t src_size
,
46 void *dst
, size_t dst_alloc_size
, size_t *dst_size
) {
48 static const lzma_options_lzma opt
= {
49 1u << 20u, NULL
, 0, LZMA_LC_DEFAULT
, LZMA_LP_DEFAULT
,
50 LZMA_PB_DEFAULT
, LZMA_MODE_FAST
, 128, LZMA_MF_HC3
, 4
52 static const lzma_filter filters
[] = {
53 { LZMA_FILTER_LZMA2
, (lzma_options_lzma
*) &opt
},
54 { LZMA_VLI_UNKNOWN
, NULL
}
62 assert(dst_alloc_size
> 0);
65 /* Returns < 0 if we couldn't compress the data or the
66 * compressed result is longer than the original */
71 ret
= lzma_stream_buffer_encode((lzma_filter
*) filters
, LZMA_CHECK_NONE
, NULL
,
72 src
, src_size
, dst
, &out_pos
, dst_alloc_size
);
79 return -EPROTONOSUPPORT
;
83 int compress_blob_lz4(const void *src
, uint64_t src_size
,
84 void *dst
, size_t dst_alloc_size
, size_t *dst_size
) {
91 assert(dst_alloc_size
> 0);
94 /* Returns < 0 if we couldn't compress the data or the
95 * compressed result is longer than the original */
100 r
= LZ4_compress_default(src
, (char*)dst
+ 8, src_size
, (int) dst_alloc_size
- 8);
104 unaligned_write_le64(dst
, src_size
);
109 return -EPROTONOSUPPORT
;
113 int decompress_blob_xz(const void *src
, uint64_t src_size
,
114 void **dst
, size_t *dst_alloc_size
, size_t* dst_size
, size_t dst_max
) {
117 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
122 assert(src_size
> 0);
124 assert(dst_alloc_size
);
126 assert(*dst_alloc_size
== 0 || *dst
);
128 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
132 space
= MIN(src_size
* 2, dst_max
?: (size_t) -1);
133 if (!greedy_realloc(dst
, dst_alloc_size
, space
, 1))
137 s
.avail_in
= src_size
;
145 ret
= lzma_code(&s
, LZMA_FINISH
);
147 if (ret
== LZMA_STREAM_END
)
149 else if (ret
!= LZMA_OK
)
152 if (dst_max
> 0 && (space
- s
.avail_out
) >= dst_max
)
154 else if (dst_max
> 0 && space
== dst_max
)
157 used
= space
- s
.avail_out
;
158 space
= MIN(2 * space
, dst_max
?: (size_t) -1);
159 if (!greedy_realloc(dst
, dst_alloc_size
, space
, 1))
162 s
.avail_out
= space
- used
;
163 s
.next_out
= *(uint8_t**)dst
+ used
;
166 *dst_size
= space
- s
.avail_out
;
169 return -EPROTONOSUPPORT
;
173 int decompress_blob_lz4(const void *src
, uint64_t src_size
,
174 void **dst
, size_t *dst_alloc_size
, size_t* dst_size
, size_t dst_max
) {
178 int r
, size
; /* LZ4 uses int for size */
181 assert(src_size
> 0);
183 assert(dst_alloc_size
);
185 assert(*dst_alloc_size
== 0 || *dst
);
190 size
= unaligned_read_le64(src
);
191 if (size
< 0 || (unsigned) size
!= unaligned_read_le64(src
))
193 if ((size_t) size
> *dst_alloc_size
) {
194 out
= realloc(*dst
, size
);
198 *dst_alloc_size
= size
;
202 r
= LZ4_decompress_safe((char*)src
+ 8, out
, src_size
- 8, size
);
203 if (r
< 0 || r
!= size
)
209 return -EPROTONOSUPPORT
;
213 int decompress_blob(int compression
,
214 const void *src
, uint64_t src_size
,
215 void **dst
, size_t *dst_alloc_size
, size_t* dst_size
, size_t dst_max
) {
216 if (compression
== OBJECT_COMPRESSED_XZ
)
217 return decompress_blob_xz(src
, src_size
,
218 dst
, dst_alloc_size
, dst_size
, dst_max
);
219 else if (compression
== OBJECT_COMPRESSED_LZ4
)
220 return decompress_blob_lz4(src
, src_size
,
221 dst
, dst_alloc_size
, dst_size
, dst_max
);
226 int decompress_startswith_xz(const void *src
, uint64_t src_size
,
227 void **buffer
, size_t *buffer_size
,
228 const void *prefix
, size_t prefix_len
,
232 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
235 /* Checks whether the decompressed blob starts with the
236 * mentioned prefix. The byte extra needs to follow the
240 assert(src_size
> 0);
244 assert(*buffer_size
== 0 || *buffer
);
246 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
250 if (!(greedy_realloc(buffer
, buffer_size
, ALIGN_8(prefix_len
+ 1), 1)))
254 s
.avail_in
= src_size
;
256 s
.next_out
= *buffer
;
257 s
.avail_out
= *buffer_size
;
260 ret
= lzma_code(&s
, LZMA_FINISH
);
262 if (!IN_SET(ret
, LZMA_OK
, LZMA_STREAM_END
))
265 if (*buffer_size
- s
.avail_out
>= prefix_len
+ 1)
266 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
267 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
269 if (ret
== LZMA_STREAM_END
)
272 s
.avail_out
+= *buffer_size
;
274 if (!(greedy_realloc(buffer
, buffer_size
, *buffer_size
* 2, 1)))
277 s
.next_out
= *(uint8_t**)buffer
+ *buffer_size
- s
.avail_out
;
281 return -EPROTONOSUPPORT
;
285 int decompress_startswith_lz4(const void *src
, uint64_t src_size
,
286 void **buffer
, size_t *buffer_size
,
287 const void *prefix
, size_t prefix_len
,
290 /* Checks whether the decompressed blob starts with the
291 * mentioned prefix. The byte extra needs to follow the
297 assert(src_size
> 0);
301 assert(*buffer_size
== 0 || *buffer
);
306 if (!(greedy_realloc(buffer
, buffer_size
, ALIGN_8(prefix_len
+ 1), 1)))
309 r
= LZ4_decompress_safe_partial((char*)src
+ 8, *buffer
, src_size
- 8,
310 prefix_len
+ 1, *buffer_size
);
311 /* One lz4 < 1.8.3, we might get "failure" (r < 0), or "success" where
312 * just a part of the buffer is decompressed. But if we get a smaller
313 * amount of bytes than requested, we don't know whether there isn't enough
314 * data to fill the requested size or whether we just got a partial answer.
316 if (r
< 0 || (size_t) r
< prefix_len
+ 1) {
319 if (LZ4_versionNumber() >= 10803)
320 /* We trust that the newer lz4 decompresses the number of bytes we
321 * requested if available in the compressed string. */
325 /* Compare what we have first, in case of mismatch we can
326 * shortcut the full comparison. */
327 if (memcmp(*buffer
, prefix
, r
) != 0)
330 /* Before version 1.8.3, lz4 always tries to decode full a "sequence",
331 * so in pathological cases might need to decompress the full field. */
332 r
= decompress_blob_lz4(src
, src_size
, buffer
, buffer_size
, &size
, 0);
336 if (size
< prefix_len
+ 1)
340 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
341 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
343 return -EPROTONOSUPPORT
;
347 int decompress_startswith(int compression
,
348 const void *src
, uint64_t src_size
,
349 void **buffer
, size_t *buffer_size
,
350 const void *prefix
, size_t prefix_len
,
352 if (compression
== OBJECT_COMPRESSED_XZ
)
353 return decompress_startswith_xz(src
, src_size
,
357 else if (compression
== OBJECT_COMPRESSED_LZ4
)
358 return decompress_startswith_lz4(src
, src_size
,
366 int compress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
) {
368 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
370 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
371 lzma_action action
= LZMA_RUN
;
376 ret
= lzma_easy_encoder(&s
, LZMA_PRESET_DEFAULT
, LZMA_CHECK_CRC64
);
377 if (ret
!= LZMA_OK
) {
378 log_error("Failed to initialize XZ encoder: code %u", ret
);
383 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
384 size_t m
= sizeof(buf
);
387 if (max_bytes
!= (uint64_t) -1 && (uint64_t) m
> max_bytes
)
388 m
= (size_t) max_bytes
;
390 n
= read(fdf
, buf
, m
);
394 action
= LZMA_FINISH
;
399 if (max_bytes
!= (uint64_t) -1) {
400 assert(max_bytes
>= (uint64_t) n
);
406 if (s
.avail_out
== 0) {
408 s
.avail_out
= sizeof(out
);
411 ret
= lzma_code(&s
, action
);
412 if (!IN_SET(ret
, LZMA_OK
, LZMA_STREAM_END
)) {
413 log_error("Compression failed: code %u", ret
);
417 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
420 n
= sizeof(out
) - s
.avail_out
;
422 k
= loop_write(fdt
, out
, n
, false);
426 if (ret
== LZMA_STREAM_END
) {
427 log_debug("XZ compression finished (%"PRIu64
" -> %"PRIu64
" bytes, %.1f%%)",
428 s
.total_in
, s
.total_out
,
429 (double) s
.total_out
/ s
.total_in
* 100);
436 return -EPROTONOSUPPORT
;
440 #define LZ4_BUFSIZE (512*1024u)
442 int compress_stream_lz4(int fdf
, int fdt
, uint64_t max_bytes
) {
446 _cleanup_(LZ4F_freeCompressionContextp
) LZ4F_compressionContext_t ctx
= NULL
;
447 _cleanup_free_
char *buf
= NULL
;
449 size_t size
, n
, total_in
= 0, total_out
, offset
= 0, frame_size
;
452 static const LZ4F_compressOptions_t options
= {
455 static const LZ4F_preferences_t preferences
= {
456 .frameInfo
.blockSizeID
= 5,
459 c
= LZ4F_createCompressionContext(&ctx
, LZ4F_VERSION
);
463 if (fstat(fdf
, &st
) < 0)
464 return log_debug_errno(errno
, "fstat() failed: %m");
466 frame_size
= LZ4F_compressBound(LZ4_BUFSIZE
, &preferences
);
467 size
= frame_size
+ 64*1024; /* add some space for header and trailer */
472 n
= offset
= total_out
= LZ4F_compressBegin(ctx
, buf
, size
, &preferences
);
476 src
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, fdf
, 0);
477 if (src
== MAP_FAILED
)
480 log_debug("Buffer size is %zu bytes, header size %zu bytes.", size
, n
);
482 while (total_in
< (size_t) st
.st_size
) {
485 k
= MIN(LZ4_BUFSIZE
, st
.st_size
- total_in
);
486 n
= LZ4F_compressUpdate(ctx
, buf
+ offset
, size
- offset
,
487 src
+ total_in
, k
, &options
);
488 if (LZ4F_isError(n
)) {
489 r
= -ENOTRECOVERABLE
;
497 if (max_bytes
!= (uint64_t) -1 && total_out
> (size_t) max_bytes
) {
498 log_debug("Compressed stream longer than %"PRIu64
" bytes", max_bytes
);
502 if (size
- offset
< frame_size
+ 4) {
503 k
= loop_write(fdt
, buf
, offset
, false);
512 n
= LZ4F_compressEnd(ctx
, buf
+ offset
, size
- offset
, &options
);
513 if (LZ4F_isError(n
)) {
514 r
= -ENOTRECOVERABLE
;
520 r
= loop_write(fdt
, buf
, offset
, false);
524 log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
526 (double) total_out
/ total_in
* 100);
528 munmap(src
, st
.st_size
);
531 return -EPROTONOSUPPORT
;
535 int decompress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
) {
538 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
541 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
542 lzma_action action
= LZMA_RUN
;
547 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
548 if (ret
!= LZMA_OK
) {
549 log_debug("Failed to initialize XZ decoder: code %u", ret
);
554 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
557 n
= read(fdf
, buf
, sizeof(buf
));
561 action
= LZMA_FINISH
;
568 if (s
.avail_out
== 0) {
570 s
.avail_out
= sizeof(out
);
573 ret
= lzma_code(&s
, action
);
574 if (!IN_SET(ret
, LZMA_OK
, LZMA_STREAM_END
)) {
575 log_debug("Decompression failed: code %u", ret
);
579 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
582 n
= sizeof(out
) - s
.avail_out
;
584 if (max_bytes
!= (uint64_t) -1) {
585 if (max_bytes
< (uint64_t) n
)
591 k
= loop_write(fdt
, out
, n
, false);
595 if (ret
== LZMA_STREAM_END
) {
596 log_debug("XZ decompression finished (%"PRIu64
" -> %"PRIu64
" bytes, %.1f%%)",
597 s
.total_in
, s
.total_out
,
598 (double) s
.total_out
/ s
.total_in
* 100);
605 log_debug("Cannot decompress file. Compiled without XZ support.");
606 return -EPROTONOSUPPORT
;
610 int decompress_stream_lz4(int in
, int out
, uint64_t max_bytes
) {
613 _cleanup_(LZ4F_freeDecompressionContextp
) LZ4F_decompressionContext_t ctx
= NULL
;
614 _cleanup_free_
char *buf
= NULL
;
618 size_t total_in
= 0, total_out
= 0;
620 c
= LZ4F_createDecompressionContext(&ctx
, LZ4F_VERSION
);
624 if (fstat(in
, &st
) < 0)
625 return log_debug_errno(errno
, "fstat() failed: %m");
627 buf
= malloc(LZ4_BUFSIZE
);
631 src
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, in
, 0);
632 if (src
== MAP_FAILED
)
635 while (total_in
< (size_t) st
.st_size
) {
636 size_t produced
= LZ4_BUFSIZE
;
637 size_t used
= st
.st_size
- total_in
;
639 c
= LZ4F_decompress(ctx
, buf
, &produced
, src
+ total_in
, &used
, NULL
);
640 if (LZ4F_isError(c
)) {
646 total_out
+= produced
;
648 if (max_bytes
!= (uint64_t) -1 && total_out
> (size_t) max_bytes
) {
649 log_debug("Decompressed stream longer than %"PRIu64
" bytes", max_bytes
);
654 r
= loop_write(out
, buf
, produced
, false);
659 log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
661 total_in
> 0 ? (double) total_out
/ total_in
* 100 : 0.0);
663 munmap(src
, st
.st_size
);
666 log_debug("Cannot decompress file. Compiled without LZ4 support.");
667 return -EPROTONOSUPPORT
;
671 int decompress_stream(const char *filename
, int fdf
, int fdt
, uint64_t max_bytes
) {
673 if (endswith(filename
, ".lz4"))
674 return decompress_stream_lz4(fdf
, fdt
, max_bytes
);
675 else if (endswith(filename
, ".xz"))
676 return decompress_stream_xz(fdf
, fdt
, max_bytes
);
678 return -EPROTONOSUPPORT
;