1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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.
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.
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/>.
33 # include <lz4frame.h>
39 #include "sparse-endian.h"
40 #include "journal-def.h"
43 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t
, LZ4F_freeCompressionContext
);
44 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t
, LZ4F_freeDecompressionContext
);
47 #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
49 static const char* const object_compressed_table
[_OBJECT_COMPRESSED_MAX
] = {
50 [OBJECT_COMPRESSED_XZ
] = "XZ",
51 [OBJECT_COMPRESSED_LZ4
] = "LZ4",
54 DEFINE_STRING_TABLE_LOOKUP(object_compressed
, int);
56 int compress_blob_xz(const void *src
, uint64_t src_size
, void *dst
, size_t *dst_size
) {
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
}
73 /* Returns < 0 if we couldn't compress the data or the
74 * compressed result is longer than the original */
79 ret
= lzma_stream_buffer_encode((lzma_filter
*) filters
, LZMA_CHECK_NONE
, NULL
,
80 src
, src_size
, dst
, &out_pos
, src_size
- 1);
87 return -EPROTONOSUPPORT
;
91 int compress_blob_lz4(const void *src
, uint64_t src_size
, void *dst
, size_t *dst_size
) {
100 /* Returns < 0 if we couldn't compress the data or the
101 * compressed result is longer than the original */
106 r
= LZ4_compress_limitedOutput(src
, dst
+ 8, src_size
, src_size
- 8 - 1);
110 *(le64_t
*) dst
= htole64(src_size
);
115 return -EPROTONOSUPPORT
;
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
) {
124 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
129 assert(src_size
> 0);
131 assert(dst_alloc_size
);
133 assert(*dst_alloc_size
== 0 || *dst
);
135 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
139 space
= MIN(src_size
* 2, dst_max
?: (size_t) -1);
140 if (!greedy_realloc(dst
, dst_alloc_size
, space
, 1))
144 s
.avail_in
= src_size
;
152 ret
= lzma_code(&s
, LZMA_FINISH
);
154 if (ret
== LZMA_STREAM_END
)
156 else if (ret
!= LZMA_OK
)
159 if (dst_max
> 0 && (space
- s
.avail_out
) >= dst_max
)
161 else if (dst_max
> 0 && space
== dst_max
)
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))
169 s
.avail_out
= space
- used
;
170 s
.next_out
= *dst
+ used
;
173 *dst_size
= space
- s
.avail_out
;
176 return -EPROTONOSUPPORT
;
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
) {
185 int r
, size
; /* LZ4 uses int for size */
188 assert(src_size
> 0);
190 assert(dst_alloc_size
);
192 assert(*dst_alloc_size
== 0 || *dst
);
197 size
= le64toh( *(le64_t
*)src
);
198 if (size
< 0 || (le64_t
) size
!= *(le64_t
*)src
)
200 if ((size_t) size
> *dst_alloc_size
) {
201 out
= realloc(*dst
, size
);
205 *dst_alloc_size
= size
;
209 r
= LZ4_decompress_safe(src
+ 8, out
, src_size
- 8, size
);
210 if (r
< 0 || r
!= size
)
216 return -EPROTONOSUPPORT
;
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
);
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
,
240 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
243 /* Checks whether the decompressed blob starts with the
244 * mentioned prefix. The byte extra needs to follow the
248 assert(src_size
> 0);
252 assert(*buffer_size
== 0 || *buffer
);
254 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
258 if (!(greedy_realloc(buffer
, buffer_size
, ALIGN_8(prefix_len
+ 1), 1)))
262 s
.avail_in
= src_size
;
264 s
.next_out
= *buffer
;
265 s
.avail_out
= *buffer_size
;
268 ret
= lzma_code(&s
, LZMA_FINISH
);
270 if (ret
!= LZMA_STREAM_END
&& ret
!= LZMA_OK
)
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
;
277 if (ret
== LZMA_STREAM_END
)
280 s
.avail_out
+= *buffer_size
;
282 if (!(greedy_realloc(buffer
, buffer_size
, *buffer_size
* 2, 1)))
285 s
.next_out
= *buffer
+ *buffer_size
- s
.avail_out
;
289 return -EPROTONOSUPPORT
;
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
,
298 /* Checks whether the decompressed blob starts with the
299 * mentioned prefix. The byte extra needs to follow the
305 assert(src_size
> 0);
309 assert(*buffer_size
== 0 || *buffer
);
314 if (!(greedy_realloc(buffer
, buffer_size
, ALIGN_8(prefix_len
+ 1), 1)))
317 r
= LZ4_decompress_safe_partial(src
+ 8, *buffer
, src_size
- 8,
318 prefix_len
+ 1, *buffer_size
);
322 if ((unsigned) r
>= prefix_len
+ 1)
323 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
324 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
329 return -EPROTONOSUPPORT
;
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
,
338 if (compression
== OBJECT_COMPRESSED_XZ
)
339 return decompress_startswith_xz(src
, src_size
,
343 else if (compression
== OBJECT_COMPRESSED_LZ4
)
344 return decompress_startswith_lz4(src
, src_size
,
352 int compress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
) {
354 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
356 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
357 lzma_action action
= LZMA_RUN
;
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
);
369 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
370 size_t m
= sizeof(buf
);
373 if (max_bytes
!= (uint64_t) -1 && (uint64_t) m
> max_bytes
)
374 m
= (size_t) max_bytes
;
376 n
= read(fdf
, buf
, m
);
380 action
= LZMA_FINISH
;
385 if (max_bytes
!= (uint64_t) -1) {
386 assert(max_bytes
>= (uint64_t) n
);
392 if (s
.avail_out
== 0) {
394 s
.avail_out
= sizeof(out
);
397 ret
= lzma_code(&s
, action
);
398 if (ret
!= LZMA_OK
&& ret
!= LZMA_STREAM_END
) {
399 log_error("Compression failed: code %u", ret
);
403 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
406 n
= sizeof(out
) - s
.avail_out
;
408 k
= loop_write(fdt
, out
, n
, false);
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);
422 return -EPROTONOSUPPORT
;
426 #define LZ4_BUFSIZE (512*1024u)
428 int compress_stream_lz4(int fdf
, int fdt
, uint64_t max_bytes
) {
432 _cleanup_(LZ4F_freeCompressionContextp
) LZ4F_compressionContext_t ctx
= NULL
;
433 _cleanup_free_
char *buf
= NULL
;
435 size_t size
, n
, total_in
= 0, total_out
= 0, offset
= 0, frame_size
;
438 static const LZ4F_compressOptions_t options
= {
441 static const LZ4F_preferences_t preferences
= {
442 .frameInfo
.blockSizeID
= 5,
445 c
= LZ4F_createCompressionContext(&ctx
, LZ4F_VERSION
);
449 if (fstat(fdf
, &st
) < 0)
450 return log_debug_errno(errno
, "fstat() failed: %m");
452 frame_size
= LZ4F_compressBound(LZ4_BUFSIZE
, &preferences
);
453 size
= frame_size
+ 64*1024; /* add some space for header and trailer */
458 n
= offset
= LZ4F_compressBegin(ctx
, buf
, size
, &preferences
);
462 src
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, fdf
, 0);
463 if (src
== MAP_FAILED
)
466 log_debug("Buffer size is %zu bytes, header size %zu bytes.", size
, n
);
468 while (total_in
< (size_t) st
.st_size
) {
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
;
483 if (max_bytes
!= (uint64_t) -1 && total_out
> (size_t) max_bytes
) {
484 log_debug("Compressed stream longer than %zd bytes", max_bytes
);
488 if (size
- offset
< frame_size
+ 4) {
489 k
= loop_write(fdt
, buf
, offset
, false);
498 n
= LZ4F_compressEnd(ctx
, buf
+ offset
, size
- offset
, &options
);
499 if (LZ4F_isError(n
)) {
500 r
= -ENOTRECOVERABLE
;
506 r
= loop_write(fdt
, buf
, offset
, false);
510 log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
512 (double) total_out
/ total_in
* 100);
514 munmap(src
, st
.st_size
);
517 return -EPROTONOSUPPORT
;
521 int decompress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
) {
524 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
527 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
528 lzma_action action
= LZMA_RUN
;
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
);
540 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
543 n
= read(fdf
, buf
, sizeof(buf
));
547 action
= LZMA_FINISH
;
554 if (s
.avail_out
== 0) {
556 s
.avail_out
= sizeof(out
);
559 ret
= lzma_code(&s
, action
);
560 if (ret
!= LZMA_OK
&& ret
!= LZMA_STREAM_END
) {
561 log_debug("Decompression failed: code %u", ret
);
565 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
568 n
= sizeof(out
) - s
.avail_out
;
570 if (max_bytes
!= (uint64_t) -1) {
571 if (max_bytes
< (uint64_t) n
)
577 k
= loop_write(fdt
, out
, n
, false);
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);
591 log_debug("Cannot decompress file. Compiled without XZ support.");
592 return -EPROTONOSUPPORT
;
597 static int decompress_stream_lz4_v1(int fdf
, int fdt
, uint64_t max_bytes
) {
599 _cleanup_free_
char *buf
= NULL
, *out
= NULL
;
601 LZ4_streamDecode_t lz4_data
= {};
603 size_t total_in
= sizeof(header
), total_out
= 0;
608 out
= malloc(4*LZ4_BUFSIZE
);
616 r
= loop_read_exact(fdf
, &header
, sizeof(header
), false);
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.
630 if (m
> LZ4_COMPRESSBOUND(LZ4_BUFSIZE
* 4)) {
631 log_debug("Compressed stream block too big: %zd bytes", m
);
635 total_in
+= sizeof(header
) + m
;
637 if (!GREEDY_REALLOC(buf
, buf_size
, m
))
640 r
= loop_read_exact(fdf
, buf
, m
, false);
644 r
= LZ4_decompress_safe_continue(&lz4_data
, buf
, out
, m
, 4*LZ4_BUFSIZE
);
646 log_debug("LZ4 decompression failed (legacy format).");
652 if (max_bytes
!= (uint64_t) -1 && (uint64_t) total_out
> max_bytes
) {
653 log_debug("Decompressed stream longer than %" PRIu64
" bytes", max_bytes
);
657 r
= loop_write(fdt
, out
, r
, false);
662 log_debug("LZ4 decompression finished (legacy format, %zu -> %zu bytes, %.1f%%)",
664 (double) total_out
/ total_in
* 100);
669 static int decompress_stream_lz4_v2(int in
, int out
, uint64_t max_bytes
) {
671 _cleanup_(LZ4F_freeDecompressionContextp
) LZ4F_decompressionContext_t ctx
= NULL
;
672 _cleanup_free_
char *buf
= NULL
;
676 size_t total_in
= 0, total_out
= 0;
678 c
= LZ4F_createDecompressionContext(&ctx
, LZ4F_VERSION
);
682 if (fstat(in
, &st
) < 0)
683 return log_debug_errno(errno
, "fstat() failed: %m");
685 buf
= malloc(LZ4_BUFSIZE
);
689 src
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, in
, 0);
690 if (src
== MAP_FAILED
)
693 while (total_in
< (size_t) st
.st_size
) {
694 size_t produced
= LZ4_BUFSIZE
;
695 size_t used
= st
.st_size
- total_in
;
697 c
= LZ4F_decompress(ctx
, buf
, &produced
, src
+ total_in
, &used
, NULL
);
698 if (LZ4F_isError(c
)) {
704 total_out
+= produced
;
706 if (max_bytes
!= (uint64_t) -1 && total_out
> (size_t) max_bytes
) {
707 log_debug("Decompressed stream longer than %zd bytes", max_bytes
);
712 r
= loop_write(out
, buf
, produced
, false);
717 log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
719 (double) total_out
/ total_in
* 100);
721 munmap(src
, st
.st_size
);
726 int decompress_stream_lz4(int fdf
, int fdt
, uint64_t max_bytes
) {
730 r
= decompress_stream_lz4_v2(fdf
, fdt
, max_bytes
);
732 r
= decompress_stream_lz4_v1(fdf
, fdt
, max_bytes
);
735 log_debug("Cannot decompress file. Compiled without LZ4 support.");
736 return -EPROTONOSUPPORT
;
740 int decompress_stream(const char *filename
, int fdf
, int fdt
, uint64_t max_bytes
) {
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
);
747 return -EPROTONOSUPPORT
;