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>
36 #include "journal-def.h"
38 #include "sparse-endian.h"
39 #include "string-util.h"
44 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t
, LZ4F_freeCompressionContext
);
45 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t
, LZ4F_freeDecompressionContext
);
48 #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
50 static const char* const object_compressed_table
[_OBJECT_COMPRESSED_MAX
] = {
51 [OBJECT_COMPRESSED_XZ
] = "XZ",
52 [OBJECT_COMPRESSED_LZ4
] = "LZ4",
55 DEFINE_STRING_TABLE_LOOKUP(object_compressed
, int);
57 int compress_blob_xz(const void *src
, uint64_t src_size
, void *dst
, size_t *dst_size
) {
59 static const lzma_options_lzma opt
= {
60 1u << 20u, NULL
, 0, LZMA_LC_DEFAULT
, LZMA_LP_DEFAULT
,
61 LZMA_PB_DEFAULT
, LZMA_MODE_FAST
, 128, LZMA_MF_HC3
, 4};
62 static const lzma_filter filters
[2] = {
63 {LZMA_FILTER_LZMA2
, (lzma_options_lzma
*) &opt
},
64 {LZMA_VLI_UNKNOWN
, NULL
}
74 /* Returns < 0 if we couldn't compress the data or the
75 * compressed result is longer than the original */
80 ret
= lzma_stream_buffer_encode((lzma_filter
*) filters
, LZMA_CHECK_NONE
, NULL
,
81 src
, src_size
, dst
, &out_pos
, src_size
- 1);
88 return -EPROTONOSUPPORT
;
92 int compress_blob_lz4(const void *src
, uint64_t src_size
, void *dst
, size_t *dst_size
) {
101 /* Returns < 0 if we couldn't compress the data or the
102 * compressed result is longer than the original */
107 r
= LZ4_compress_limitedOutput(src
, dst
+ 8, src_size
, src_size
- 8 - 1);
111 *(le64_t
*) dst
= htole64(src_size
);
116 return -EPROTONOSUPPORT
;
121 int decompress_blob_xz(const void *src
, uint64_t src_size
,
122 void **dst
, size_t *dst_alloc_size
, size_t* dst_size
, size_t dst_max
) {
125 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
130 assert(src_size
> 0);
132 assert(dst_alloc_size
);
134 assert(*dst_alloc_size
== 0 || *dst
);
136 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
140 space
= MIN(src_size
* 2, dst_max
?: (size_t) -1);
141 if (!greedy_realloc(dst
, dst_alloc_size
, space
, 1))
145 s
.avail_in
= src_size
;
153 ret
= lzma_code(&s
, LZMA_FINISH
);
155 if (ret
== LZMA_STREAM_END
)
157 else if (ret
!= LZMA_OK
)
160 if (dst_max
> 0 && (space
- s
.avail_out
) >= dst_max
)
162 else if (dst_max
> 0 && space
== dst_max
)
165 used
= space
- s
.avail_out
;
166 space
= MIN(2 * space
, dst_max
?: (size_t) -1);
167 if (!greedy_realloc(dst
, dst_alloc_size
, space
, 1))
170 s
.avail_out
= space
- used
;
171 s
.next_out
= *dst
+ used
;
174 *dst_size
= space
- s
.avail_out
;
177 return -EPROTONOSUPPORT
;
181 int decompress_blob_lz4(const void *src
, uint64_t src_size
,
182 void **dst
, size_t *dst_alloc_size
, size_t* dst_size
, size_t dst_max
) {
186 int r
, size
; /* LZ4 uses int for size */
189 assert(src_size
> 0);
191 assert(dst_alloc_size
);
193 assert(*dst_alloc_size
== 0 || *dst
);
198 size
= le64toh( *(le64_t
*)src
);
199 if (size
< 0 || (le64_t
) size
!= *(le64_t
*)src
)
201 if ((size_t) size
> *dst_alloc_size
) {
202 out
= realloc(*dst
, size
);
206 *dst_alloc_size
= size
;
210 r
= LZ4_decompress_safe(src
+ 8, out
, src_size
- 8, size
);
211 if (r
< 0 || r
!= size
)
217 return -EPROTONOSUPPORT
;
221 int decompress_blob(int compression
,
222 const void *src
, uint64_t src_size
,
223 void **dst
, size_t *dst_alloc_size
, size_t* dst_size
, size_t dst_max
) {
224 if (compression
== OBJECT_COMPRESSED_XZ
)
225 return decompress_blob_xz(src
, src_size
,
226 dst
, dst_alloc_size
, dst_size
, dst_max
);
227 else if (compression
== OBJECT_COMPRESSED_LZ4
)
228 return decompress_blob_lz4(src
, src_size
,
229 dst
, dst_alloc_size
, dst_size
, dst_max
);
235 int decompress_startswith_xz(const void *src
, uint64_t src_size
,
236 void **buffer
, size_t *buffer_size
,
237 const void *prefix
, size_t prefix_len
,
241 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
244 /* Checks whether the decompressed blob starts with the
245 * mentioned prefix. The byte extra needs to follow the
249 assert(src_size
> 0);
253 assert(*buffer_size
== 0 || *buffer
);
255 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
259 if (!(greedy_realloc(buffer
, buffer_size
, ALIGN_8(prefix_len
+ 1), 1)))
263 s
.avail_in
= src_size
;
265 s
.next_out
= *buffer
;
266 s
.avail_out
= *buffer_size
;
269 ret
= lzma_code(&s
, LZMA_FINISH
);
271 if (ret
!= LZMA_STREAM_END
&& ret
!= LZMA_OK
)
274 if (*buffer_size
- s
.avail_out
>= prefix_len
+ 1)
275 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
276 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
278 if (ret
== LZMA_STREAM_END
)
281 s
.avail_out
+= *buffer_size
;
283 if (!(greedy_realloc(buffer
, buffer_size
, *buffer_size
* 2, 1)))
286 s
.next_out
= *buffer
+ *buffer_size
- s
.avail_out
;
290 return -EPROTONOSUPPORT
;
294 int decompress_startswith_lz4(const void *src
, uint64_t src_size
,
295 void **buffer
, size_t *buffer_size
,
296 const void *prefix
, size_t prefix_len
,
299 /* Checks whether the decompressed blob starts with the
300 * mentioned prefix. The byte extra needs to follow the
306 assert(src_size
> 0);
310 assert(*buffer_size
== 0 || *buffer
);
315 if (!(greedy_realloc(buffer
, buffer_size
, ALIGN_8(prefix_len
+ 1), 1)))
318 r
= LZ4_decompress_safe_partial(src
+ 8, *buffer
, src_size
- 8,
319 prefix_len
+ 1, *buffer_size
);
323 if ((unsigned) r
>= prefix_len
+ 1)
324 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
325 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
330 return -EPROTONOSUPPORT
;
334 int decompress_startswith(int compression
,
335 const void *src
, uint64_t src_size
,
336 void **buffer
, size_t *buffer_size
,
337 const void *prefix
, size_t prefix_len
,
339 if (compression
== OBJECT_COMPRESSED_XZ
)
340 return decompress_startswith_xz(src
, src_size
,
344 else if (compression
== OBJECT_COMPRESSED_LZ4
)
345 return decompress_startswith_lz4(src
, src_size
,
353 int compress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
) {
355 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
357 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
358 lzma_action action
= LZMA_RUN
;
363 ret
= lzma_easy_encoder(&s
, LZMA_PRESET_DEFAULT
, LZMA_CHECK_CRC64
);
364 if (ret
!= LZMA_OK
) {
365 log_error("Failed to initialize XZ encoder: code %u", ret
);
370 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
371 size_t m
= sizeof(buf
);
374 if (max_bytes
!= (uint64_t) -1 && (uint64_t) m
> max_bytes
)
375 m
= (size_t) max_bytes
;
377 n
= read(fdf
, buf
, m
);
381 action
= LZMA_FINISH
;
386 if (max_bytes
!= (uint64_t) -1) {
387 assert(max_bytes
>= (uint64_t) n
);
393 if (s
.avail_out
== 0) {
395 s
.avail_out
= sizeof(out
);
398 ret
= lzma_code(&s
, action
);
399 if (ret
!= LZMA_OK
&& ret
!= LZMA_STREAM_END
) {
400 log_error("Compression failed: code %u", ret
);
404 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
407 n
= sizeof(out
) - s
.avail_out
;
409 k
= loop_write(fdt
, out
, n
, false);
413 if (ret
== LZMA_STREAM_END
) {
414 log_debug("XZ compression finished (%"PRIu64
" -> %"PRIu64
" bytes, %.1f%%)",
415 s
.total_in
, s
.total_out
,
416 (double) s
.total_out
/ s
.total_in
* 100);
423 return -EPROTONOSUPPORT
;
427 #define LZ4_BUFSIZE (512*1024u)
429 int compress_stream_lz4(int fdf
, int fdt
, uint64_t max_bytes
) {
433 _cleanup_(LZ4F_freeCompressionContextp
) LZ4F_compressionContext_t ctx
= NULL
;
434 _cleanup_free_
char *buf
= NULL
;
436 size_t size
, n
, total_in
= 0, total_out
= 0, offset
= 0, frame_size
;
439 static const LZ4F_compressOptions_t options
= {
442 static const LZ4F_preferences_t preferences
= {
443 .frameInfo
.blockSizeID
= 5,
446 c
= LZ4F_createCompressionContext(&ctx
, LZ4F_VERSION
);
450 if (fstat(fdf
, &st
) < 0)
451 return log_debug_errno(errno
, "fstat() failed: %m");
453 frame_size
= LZ4F_compressBound(LZ4_BUFSIZE
, &preferences
);
454 size
= frame_size
+ 64*1024; /* add some space for header and trailer */
459 n
= offset
= LZ4F_compressBegin(ctx
, buf
, size
, &preferences
);
463 src
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, fdf
, 0);
464 if (src
== MAP_FAILED
)
467 log_debug("Buffer size is %zu bytes, header size %zu bytes.", size
, n
);
469 while (total_in
< (size_t) st
.st_size
) {
472 k
= MIN(LZ4_BUFSIZE
, st
.st_size
- total_in
);
473 n
= LZ4F_compressUpdate(ctx
, buf
+ offset
, size
- offset
,
474 src
+ total_in
, k
, &options
);
475 if (LZ4F_isError(n
)) {
476 r
= -ENOTRECOVERABLE
;
484 if (max_bytes
!= (uint64_t) -1 && total_out
> (size_t) max_bytes
) {
485 log_debug("Compressed stream longer than %zd bytes", max_bytes
);
489 if (size
- offset
< frame_size
+ 4) {
490 k
= loop_write(fdt
, buf
, offset
, false);
499 n
= LZ4F_compressEnd(ctx
, buf
+ offset
, size
- offset
, &options
);
500 if (LZ4F_isError(n
)) {
501 r
= -ENOTRECOVERABLE
;
507 r
= loop_write(fdt
, buf
, offset
, false);
511 log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
513 (double) total_out
/ total_in
* 100);
515 munmap(src
, st
.st_size
);
518 return -EPROTONOSUPPORT
;
522 int decompress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
) {
525 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
528 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
529 lzma_action action
= LZMA_RUN
;
534 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
535 if (ret
!= LZMA_OK
) {
536 log_debug("Failed to initialize XZ decoder: code %u", ret
);
541 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
544 n
= read(fdf
, buf
, sizeof(buf
));
548 action
= LZMA_FINISH
;
555 if (s
.avail_out
== 0) {
557 s
.avail_out
= sizeof(out
);
560 ret
= lzma_code(&s
, action
);
561 if (ret
!= LZMA_OK
&& ret
!= LZMA_STREAM_END
) {
562 log_debug("Decompression failed: code %u", ret
);
566 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
569 n
= sizeof(out
) - s
.avail_out
;
571 if (max_bytes
!= (uint64_t) -1) {
572 if (max_bytes
< (uint64_t) n
)
578 k
= loop_write(fdt
, out
, n
, false);
582 if (ret
== LZMA_STREAM_END
) {
583 log_debug("XZ decompression finished (%"PRIu64
" -> %"PRIu64
" bytes, %.1f%%)",
584 s
.total_in
, s
.total_out
,
585 (double) s
.total_out
/ s
.total_in
* 100);
592 log_debug("Cannot decompress file. Compiled without XZ support.");
593 return -EPROTONOSUPPORT
;
598 static int decompress_stream_lz4_v1(int fdf
, int fdt
, uint64_t max_bytes
) {
600 _cleanup_free_
char *buf
= NULL
, *out
= NULL
;
602 LZ4_streamDecode_t lz4_data
= {};
604 size_t total_in
= sizeof(header
), total_out
= 0;
609 out
= malloc(4*LZ4_BUFSIZE
);
617 r
= loop_read_exact(fdf
, &header
, sizeof(header
), false);
625 /* We refuse to use a bigger decompression buffer than
626 * the one used for compression by 4 times. This means
627 * that compression buffer size can be enlarged 4
628 * times. This can be changed, but old binaries might
629 * not accept buffers compressed by newer binaries then.
631 if (m
> LZ4_COMPRESSBOUND(LZ4_BUFSIZE
* 4)) {
632 log_debug("Compressed stream block too big: %zd bytes", m
);
636 total_in
+= sizeof(header
) + m
;
638 if (!GREEDY_REALLOC(buf
, buf_size
, m
))
641 r
= loop_read_exact(fdf
, buf
, m
, false);
645 r
= LZ4_decompress_safe_continue(&lz4_data
, buf
, out
, m
, 4*LZ4_BUFSIZE
);
647 log_debug("LZ4 decompression failed (legacy format).");
653 if (max_bytes
!= (uint64_t) -1 && (uint64_t) total_out
> max_bytes
) {
654 log_debug("Decompressed stream longer than %" PRIu64
" bytes", max_bytes
);
658 r
= loop_write(fdt
, out
, r
, false);
663 log_debug("LZ4 decompression finished (legacy format, %zu -> %zu bytes, %.1f%%)",
665 (double) total_out
/ total_in
* 100);
670 static int decompress_stream_lz4_v2(int in
, int out
, uint64_t max_bytes
) {
672 _cleanup_(LZ4F_freeDecompressionContextp
) LZ4F_decompressionContext_t ctx
= NULL
;
673 _cleanup_free_
char *buf
= NULL
;
677 size_t total_in
= 0, total_out
= 0;
679 c
= LZ4F_createDecompressionContext(&ctx
, LZ4F_VERSION
);
683 if (fstat(in
, &st
) < 0)
684 return log_debug_errno(errno
, "fstat() failed: %m");
686 buf
= malloc(LZ4_BUFSIZE
);
690 src
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, in
, 0);
691 if (src
== MAP_FAILED
)
694 while (total_in
< (size_t) st
.st_size
) {
695 size_t produced
= LZ4_BUFSIZE
;
696 size_t used
= st
.st_size
- total_in
;
698 c
= LZ4F_decompress(ctx
, buf
, &produced
, src
+ total_in
, &used
, NULL
);
699 if (LZ4F_isError(c
)) {
705 total_out
+= produced
;
707 if (max_bytes
!= (uint64_t) -1 && total_out
> (size_t) max_bytes
) {
708 log_debug("Decompressed stream longer than %zd bytes", max_bytes
);
713 r
= loop_write(out
, buf
, produced
, false);
718 log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
720 (double) total_out
/ total_in
* 100);
722 munmap(src
, st
.st_size
);
727 int decompress_stream_lz4(int fdf
, int fdt
, uint64_t max_bytes
) {
731 r
= decompress_stream_lz4_v2(fdf
, fdt
, max_bytes
);
733 r
= decompress_stream_lz4_v1(fdf
, fdt
, max_bytes
);
736 log_debug("Cannot decompress file. Compiled without LZ4 support.");
737 return -EPROTONOSUPPORT
;
741 int decompress_stream(const char *filename
, int fdf
, int fdt
, uint64_t max_bytes
) {
743 if (endswith(filename
, ".lz4"))
744 return decompress_stream_lz4(fdf
, fdt
, max_bytes
);
745 else if (endswith(filename
, ".xz"))
746 return decompress_stream_xz(fdf
, fdt
, max_bytes
);
748 return -EPROTONOSUPPORT
;