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/>.
38 #include "journal-def.h"
40 #include "sparse-endian.h"
41 #include "string-util.h"
45 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t
, LZ4F_freeCompressionContext
);
46 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t
, LZ4F_freeDecompressionContext
);
49 #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
51 static const char* const object_compressed_table
[_OBJECT_COMPRESSED_MAX
] = {
52 [OBJECT_COMPRESSED_XZ
] = "XZ",
53 [OBJECT_COMPRESSED_LZ4
] = "LZ4",
56 DEFINE_STRING_TABLE_LOOKUP(object_compressed
, int);
58 int compress_blob_xz(const void *src
, uint64_t src_size
, void *dst
, size_t *dst_size
) {
60 static const lzma_options_lzma opt
= {
61 1u << 20u, NULL
, 0, LZMA_LC_DEFAULT
, LZMA_LP_DEFAULT
,
62 LZMA_PB_DEFAULT
, LZMA_MODE_FAST
, 128, LZMA_MF_HC3
, 4};
63 static const lzma_filter filters
[2] = {
64 {LZMA_FILTER_LZMA2
, (lzma_options_lzma
*) &opt
},
65 {LZMA_VLI_UNKNOWN
, NULL
}
75 /* Returns < 0 if we couldn't compress the data or the
76 * compressed result is longer than the original */
81 ret
= lzma_stream_buffer_encode((lzma_filter
*) filters
, LZMA_CHECK_NONE
, NULL
,
82 src
, src_size
, dst
, &out_pos
, src_size
- 1);
89 return -EPROTONOSUPPORT
;
93 int compress_blob_lz4(const void *src
, uint64_t src_size
, void *dst
, size_t *dst_size
) {
102 /* Returns < 0 if we couldn't compress the data or the
103 * compressed result is longer than the original */
108 r
= LZ4_compress_limitedOutput(src
, dst
+ 8, src_size
, src_size
- 8 - 1);
112 *(le64_t
*) dst
= htole64(src_size
);
117 return -EPROTONOSUPPORT
;
122 int decompress_blob_xz(const void *src
, uint64_t src_size
,
123 void **dst
, size_t *dst_alloc_size
, size_t* dst_size
, size_t dst_max
) {
126 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
131 assert(src_size
> 0);
133 assert(dst_alloc_size
);
135 assert(*dst_alloc_size
== 0 || *dst
);
137 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
141 space
= MIN(src_size
* 2, dst_max
?: (size_t) -1);
142 if (!greedy_realloc(dst
, dst_alloc_size
, space
, 1))
146 s
.avail_in
= src_size
;
154 ret
= lzma_code(&s
, LZMA_FINISH
);
156 if (ret
== LZMA_STREAM_END
)
158 else if (ret
!= LZMA_OK
)
161 if (dst_max
> 0 && (space
- s
.avail_out
) >= dst_max
)
163 else if (dst_max
> 0 && space
== dst_max
)
166 used
= space
- s
.avail_out
;
167 space
= MIN(2 * space
, dst_max
?: (size_t) -1);
168 if (!greedy_realloc(dst
, dst_alloc_size
, space
, 1))
171 s
.avail_out
= space
- used
;
172 s
.next_out
= *dst
+ used
;
175 *dst_size
= space
- s
.avail_out
;
178 return -EPROTONOSUPPORT
;
182 int decompress_blob_lz4(const void *src
, uint64_t src_size
,
183 void **dst
, size_t *dst_alloc_size
, size_t* dst_size
, size_t dst_max
) {
187 int r
, size
; /* LZ4 uses int for size */
190 assert(src_size
> 0);
192 assert(dst_alloc_size
);
194 assert(*dst_alloc_size
== 0 || *dst
);
199 size
= le64toh( *(le64_t
*)src
);
200 if (size
< 0 || (le64_t
) size
!= *(le64_t
*)src
)
202 if ((size_t) size
> *dst_alloc_size
) {
203 out
= realloc(*dst
, size
);
207 *dst_alloc_size
= size
;
211 r
= LZ4_decompress_safe(src
+ 8, out
, src_size
- 8, size
);
212 if (r
< 0 || r
!= size
)
218 return -EPROTONOSUPPORT
;
222 int decompress_blob(int compression
,
223 const void *src
, uint64_t src_size
,
224 void **dst
, size_t *dst_alloc_size
, size_t* dst_size
, size_t dst_max
) {
225 if (compression
== OBJECT_COMPRESSED_XZ
)
226 return decompress_blob_xz(src
, src_size
,
227 dst
, dst_alloc_size
, dst_size
, dst_max
);
228 else if (compression
== OBJECT_COMPRESSED_LZ4
)
229 return decompress_blob_lz4(src
, src_size
,
230 dst
, dst_alloc_size
, dst_size
, dst_max
);
236 int decompress_startswith_xz(const void *src
, uint64_t src_size
,
237 void **buffer
, size_t *buffer_size
,
238 const void *prefix
, size_t prefix_len
,
242 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
245 /* Checks whether the decompressed blob starts with the
246 * mentioned prefix. The byte extra needs to follow the
250 assert(src_size
> 0);
254 assert(*buffer_size
== 0 || *buffer
);
256 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
260 if (!(greedy_realloc(buffer
, buffer_size
, ALIGN_8(prefix_len
+ 1), 1)))
264 s
.avail_in
= src_size
;
266 s
.next_out
= *buffer
;
267 s
.avail_out
= *buffer_size
;
270 ret
= lzma_code(&s
, LZMA_FINISH
);
272 if (ret
!= LZMA_STREAM_END
&& ret
!= LZMA_OK
)
275 if (*buffer_size
- s
.avail_out
>= prefix_len
+ 1)
276 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
277 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
279 if (ret
== LZMA_STREAM_END
)
282 s
.avail_out
+= *buffer_size
;
284 if (!(greedy_realloc(buffer
, buffer_size
, *buffer_size
* 2, 1)))
287 s
.next_out
= *buffer
+ *buffer_size
- s
.avail_out
;
291 return -EPROTONOSUPPORT
;
295 int decompress_startswith_lz4(const void *src
, uint64_t src_size
,
296 void **buffer
, size_t *buffer_size
,
297 const void *prefix
, size_t prefix_len
,
300 /* Checks whether the decompressed blob starts with the
301 * mentioned prefix. The byte extra needs to follow the
307 assert(src_size
> 0);
311 assert(*buffer_size
== 0 || *buffer
);
316 if (!(greedy_realloc(buffer
, buffer_size
, ALIGN_8(prefix_len
+ 1), 1)))
319 r
= LZ4_decompress_safe_partial(src
+ 8, *buffer
, src_size
- 8,
320 prefix_len
+ 1, *buffer_size
);
324 if ((unsigned) r
>= prefix_len
+ 1)
325 return memcmp(*buffer
, prefix
, prefix_len
) == 0 &&
326 ((const uint8_t*) *buffer
)[prefix_len
] == extra
;
331 return -EPROTONOSUPPORT
;
335 int decompress_startswith(int compression
,
336 const void *src
, uint64_t src_size
,
337 void **buffer
, size_t *buffer_size
,
338 const void *prefix
, size_t prefix_len
,
340 if (compression
== OBJECT_COMPRESSED_XZ
)
341 return decompress_startswith_xz(src
, src_size
,
345 else if (compression
== OBJECT_COMPRESSED_LZ4
)
346 return decompress_startswith_lz4(src
, src_size
,
354 int compress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
) {
356 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
358 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
359 lzma_action action
= LZMA_RUN
;
364 ret
= lzma_easy_encoder(&s
, LZMA_PRESET_DEFAULT
, LZMA_CHECK_CRC64
);
365 if (ret
!= LZMA_OK
) {
366 log_error("Failed to initialize XZ encoder: code %u", ret
);
371 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
372 size_t m
= sizeof(buf
);
375 if (max_bytes
!= (uint64_t) -1 && (uint64_t) m
> max_bytes
)
376 m
= (size_t) max_bytes
;
378 n
= read(fdf
, buf
, m
);
382 action
= LZMA_FINISH
;
387 if (max_bytes
!= (uint64_t) -1) {
388 assert(max_bytes
>= (uint64_t) n
);
394 if (s
.avail_out
== 0) {
396 s
.avail_out
= sizeof(out
);
399 ret
= lzma_code(&s
, action
);
400 if (ret
!= LZMA_OK
&& ret
!= LZMA_STREAM_END
) {
401 log_error("Compression failed: code %u", ret
);
405 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
408 n
= sizeof(out
) - s
.avail_out
;
410 k
= loop_write(fdt
, out
, n
, false);
414 if (ret
== LZMA_STREAM_END
) {
415 log_debug("XZ compression finished (%"PRIu64
" -> %"PRIu64
" bytes, %.1f%%)",
416 s
.total_in
, s
.total_out
,
417 (double) s
.total_out
/ s
.total_in
* 100);
424 return -EPROTONOSUPPORT
;
428 #define LZ4_BUFSIZE (512*1024u)
430 int compress_stream_lz4(int fdf
, int fdt
, uint64_t max_bytes
) {
434 _cleanup_(LZ4F_freeCompressionContextp
) LZ4F_compressionContext_t ctx
= NULL
;
435 _cleanup_free_
char *buf
= NULL
;
437 size_t size
, n
, total_in
= 0, total_out
= 0, offset
= 0, frame_size
;
440 static const LZ4F_compressOptions_t options
= {
443 static const LZ4F_preferences_t preferences
= {
444 .frameInfo
.blockSizeID
= 5,
447 c
= LZ4F_createCompressionContext(&ctx
, LZ4F_VERSION
);
451 if (fstat(fdf
, &st
) < 0)
452 return log_debug_errno(errno
, "fstat() failed: %m");
454 frame_size
= LZ4F_compressBound(LZ4_BUFSIZE
, &preferences
);
455 size
= frame_size
+ 64*1024; /* add some space for header and trailer */
460 n
= offset
= LZ4F_compressBegin(ctx
, buf
, size
, &preferences
);
464 src
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, fdf
, 0);
465 if (src
== MAP_FAILED
)
468 log_debug("Buffer size is %zu bytes, header size %zu bytes.", size
, n
);
470 while (total_in
< (size_t) st
.st_size
) {
473 k
= MIN(LZ4_BUFSIZE
, st
.st_size
- total_in
);
474 n
= LZ4F_compressUpdate(ctx
, buf
+ offset
, size
- offset
,
475 src
+ total_in
, k
, &options
);
476 if (LZ4F_isError(n
)) {
477 r
= -ENOTRECOVERABLE
;
485 if (max_bytes
!= (uint64_t) -1 && total_out
> (size_t) max_bytes
) {
486 log_debug("Compressed stream longer than %zd bytes", max_bytes
);
490 if (size
- offset
< frame_size
+ 4) {
491 k
= loop_write(fdt
, buf
, offset
, false);
500 n
= LZ4F_compressEnd(ctx
, buf
+ offset
, size
- offset
, &options
);
501 if (LZ4F_isError(n
)) {
502 r
= -ENOTRECOVERABLE
;
508 r
= loop_write(fdt
, buf
, offset
, false);
512 log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
514 (double) total_out
/ total_in
* 100);
516 munmap(src
, st
.st_size
);
519 return -EPROTONOSUPPORT
;
523 int decompress_stream_xz(int fdf
, int fdt
, uint64_t max_bytes
) {
526 _cleanup_(lzma_end
) lzma_stream s
= LZMA_STREAM_INIT
;
529 uint8_t buf
[BUFSIZ
], out
[BUFSIZ
];
530 lzma_action action
= LZMA_RUN
;
535 ret
= lzma_stream_decoder(&s
, UINT64_MAX
, 0);
536 if (ret
!= LZMA_OK
) {
537 log_debug("Failed to initialize XZ decoder: code %u", ret
);
542 if (s
.avail_in
== 0 && action
== LZMA_RUN
) {
545 n
= read(fdf
, buf
, sizeof(buf
));
549 action
= LZMA_FINISH
;
556 if (s
.avail_out
== 0) {
558 s
.avail_out
= sizeof(out
);
561 ret
= lzma_code(&s
, action
);
562 if (ret
!= LZMA_OK
&& ret
!= LZMA_STREAM_END
) {
563 log_debug("Decompression failed: code %u", ret
);
567 if (s
.avail_out
== 0 || ret
== LZMA_STREAM_END
) {
570 n
= sizeof(out
) - s
.avail_out
;
572 if (max_bytes
!= (uint64_t) -1) {
573 if (max_bytes
< (uint64_t) n
)
579 k
= loop_write(fdt
, out
, n
, false);
583 if (ret
== LZMA_STREAM_END
) {
584 log_debug("XZ decompression finished (%"PRIu64
" -> %"PRIu64
" bytes, %.1f%%)",
585 s
.total_in
, s
.total_out
,
586 (double) s
.total_out
/ s
.total_in
* 100);
593 log_debug("Cannot decompress file. Compiled without XZ support.");
594 return -EPROTONOSUPPORT
;
599 static int decompress_stream_lz4_v1(int fdf
, int fdt
, uint64_t max_bytes
) {
601 _cleanup_free_
char *buf
= NULL
, *out
= NULL
;
603 LZ4_streamDecode_t lz4_data
= {};
605 size_t total_in
= sizeof(header
), total_out
= 0;
610 out
= malloc(4*LZ4_BUFSIZE
);
618 r
= loop_read_exact(fdf
, &header
, sizeof(header
), false);
626 /* We refuse to use a bigger decompression buffer than
627 * the one used for compression by 4 times. This means
628 * that compression buffer size can be enlarged 4
629 * times. This can be changed, but old binaries might
630 * not accept buffers compressed by newer binaries then.
632 if (m
> LZ4_COMPRESSBOUND(LZ4_BUFSIZE
* 4)) {
633 log_debug("Compressed stream block too big: %zd bytes", m
);
637 total_in
+= sizeof(header
) + m
;
639 if (!GREEDY_REALLOC(buf
, buf_size
, m
))
642 r
= loop_read_exact(fdf
, buf
, m
, false);
646 r
= LZ4_decompress_safe_continue(&lz4_data
, buf
, out
, m
, 4*LZ4_BUFSIZE
);
648 log_debug("LZ4 decompression failed (legacy format).");
654 if (max_bytes
!= (uint64_t) -1 && (uint64_t) total_out
> max_bytes
) {
655 log_debug("Decompressed stream longer than %" PRIu64
" bytes", max_bytes
);
659 r
= loop_write(fdt
, out
, r
, false);
664 log_debug("LZ4 decompression finished (legacy format, %zu -> %zu bytes, %.1f%%)",
666 (double) total_out
/ total_in
* 100);
671 static int decompress_stream_lz4_v2(int in
, int out
, uint64_t max_bytes
) {
673 _cleanup_(LZ4F_freeDecompressionContextp
) LZ4F_decompressionContext_t ctx
= NULL
;
674 _cleanup_free_
char *buf
= NULL
;
678 size_t total_in
= 0, total_out
= 0;
680 c
= LZ4F_createDecompressionContext(&ctx
, LZ4F_VERSION
);
684 if (fstat(in
, &st
) < 0)
685 return log_debug_errno(errno
, "fstat() failed: %m");
687 buf
= malloc(LZ4_BUFSIZE
);
691 src
= mmap(NULL
, st
.st_size
, PROT_READ
, MAP_PRIVATE
, in
, 0);
692 if (src
== MAP_FAILED
)
695 while (total_in
< (size_t) st
.st_size
) {
696 size_t produced
= LZ4_BUFSIZE
;
697 size_t used
= st
.st_size
- total_in
;
699 c
= LZ4F_decompress(ctx
, buf
, &produced
, src
+ total_in
, &used
, NULL
);
700 if (LZ4F_isError(c
)) {
706 total_out
+= produced
;
708 if (max_bytes
!= (uint64_t) -1 && total_out
> (size_t) max_bytes
) {
709 log_debug("Decompressed stream longer than %zd bytes", max_bytes
);
714 r
= loop_write(out
, buf
, produced
, false);
719 log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
721 (double) total_out
/ total_in
* 100);
723 munmap(src
, st
.st_size
);
728 int decompress_stream_lz4(int fdf
, int fdt
, uint64_t max_bytes
) {
732 r
= decompress_stream_lz4_v2(fdf
, fdt
, max_bytes
);
734 r
= decompress_stream_lz4_v1(fdf
, fdt
, max_bytes
);
737 log_debug("Cannot decompress file. Compiled without LZ4 support.");
738 return -EPROTONOSUPPORT
;
742 int decompress_stream(const char *filename
, int fdf
, int fdt
, uint64_t max_bytes
) {
744 if (endswith(filename
, ".lz4"))
745 return decompress_stream_lz4(fdf
, fdt
, max_bytes
);
746 else if (endswith(filename
, ".xz"))
747 return decompress_stream_xz(fdf
, fdt
, max_bytes
);
749 return -EPROTONOSUPPORT
;