]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/compress.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / journal / compress.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
e4e61fdb
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
e4e61fdb
LP
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 15 Lesser General Public License for more details.
e4e61fdb 16
5430f7f2 17 You should have received a copy of the GNU Lesser General Public License
e4e61fdb
LP
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
82e24b00 21#include <inttypes.h>
e4e61fdb
LP
22#include <stdlib.h>
23#include <string.h>
4b5bc539 24#include <sys/mman.h>
07630cea 25#include <unistd.h>
d89c8fdf 26
349cc4a5 27#if HAVE_XZ
3ffd4af2 28#include <lzma.h>
d89c8fdf
ZJS
29#endif
30
349cc4a5 31#if HAVE_LZ4
3ffd4af2
LP
32#include <lz4.h>
33#include <lz4frame.h>
d89c8fdf 34#endif
e4e61fdb 35
b5efdb8a 36#include "alloc-util.h"
e4e61fdb 37#include "compress.h"
3ffd4af2 38#include "fd-util.h"
c004493c 39#include "io-util.h"
07630cea 40#include "journal-def.h"
355b59e2 41#include "macro.h"
d89c8fdf 42#include "sparse-endian.h"
8b43440b 43#include "string-table.h"
07630cea
LP
44#include "string-util.h"
45#include "util.h"
d89c8fdf 46
349cc4a5 47#if HAVE_LZ4
4b5bc539
ZJS
48DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionContext);
49DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext);
50#endif
51
d89c8fdf
ZJS
52#define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t))
53
54static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = {
55 [OBJECT_COMPRESSED_XZ] = "XZ",
56 [OBJECT_COMPRESSED_LZ4] = "LZ4",
57};
e4e61fdb 58
d89c8fdf
ZJS
59DEFINE_STRING_TABLE_LOOKUP(object_compressed, int);
60
5d6f46b6
ZJS
61int compress_blob_xz(const void *src, uint64_t src_size,
62 void *dst, size_t dst_alloc_size, size_t *dst_size) {
349cc4a5 63#if HAVE_XZ
1930eed2
JS
64 static const lzma_options_lzma opt = {
65 1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
0240c603
LP
66 LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4
67 };
68 static const lzma_filter filters[] = {
69 { LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt },
70 { LZMA_VLI_UNKNOWN, NULL }
1930eed2 71 };
e4e61fdb 72 lzma_ret ret;
76cc0bf6 73 size_t out_pos = 0;
e4e61fdb
LP
74
75 assert(src);
76 assert(src_size > 0);
77 assert(dst);
5d6f46b6 78 assert(dst_alloc_size > 0);
e4e61fdb
LP
79 assert(dst_size);
80
d89c8fdf 81 /* Returns < 0 if we couldn't compress the data or the
e4e61fdb
LP
82 * compressed result is longer than the original */
83
1930eed2
JS
84 if (src_size < 80)
85 return -ENOBUFS;
86
87 ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
5d6f46b6 88 src, src_size, dst, &out_pos, dst_alloc_size);
e4e61fdb 89 if (ret != LZMA_OK)
d89c8fdf 90 return -ENOBUFS;
e4e61fdb 91
76cc0bf6 92 *dst_size = out_pos;
d89c8fdf
ZJS
93 return 0;
94#else
95 return -EPROTONOSUPPORT;
96#endif
e4e61fdb
LP
97}
98
5d6f46b6
ZJS
99int compress_blob_lz4(const void *src, uint64_t src_size,
100 void *dst, size_t dst_alloc_size, size_t *dst_size) {
349cc4a5 101#if HAVE_LZ4
d89c8fdf
ZJS
102 int r;
103
104 assert(src);
105 assert(src_size > 0);
106 assert(dst);
5d6f46b6 107 assert(dst_alloc_size > 0);
d89c8fdf
ZJS
108 assert(dst_size);
109
110 /* Returns < 0 if we couldn't compress the data or the
111 * compressed result is longer than the original */
112
113 if (src_size < 9)
114 return -ENOBUFS;
e4e61fdb 115
691b90d4
ZJS
116#if LZ4_VERSION_NUMBER >= 10700
117 r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
118#else
8e170d29 119 r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
691b90d4 120#endif
d89c8fdf
ZJS
121 if (r <= 0)
122 return -ENOBUFS;
123
124 *(le64_t*) dst = htole64(src_size);
125 *dst_size = r + 8;
126
127 return 0;
128#else
129 return -EPROTONOSUPPORT;
130#endif
131}
132
133
134int decompress_blob_xz(const void *src, uint64_t src_size,
fa1c4b51 135 void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
d89c8fdf 136
349cc4a5 137#if HAVE_XZ
5e592c66 138 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
e4e61fdb 139 lzma_ret ret;
fa1c4b51 140 size_t space;
e4e61fdb
LP
141
142 assert(src);
143 assert(src_size > 0);
144 assert(dst);
145 assert(dst_alloc_size);
146 assert(dst_size);
147 assert(*dst_alloc_size == 0 || *dst);
148
149 ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
150 if (ret != LZMA_OK)
d89c8fdf 151 return -ENOMEM;
e4e61fdb 152
fa1c4b51 153 space = MIN(src_size * 2, dst_max ?: (size_t) -1);
5e592c66 154 if (!greedy_realloc(dst, dst_alloc_size, space, 1))
01c3322e 155 return -ENOMEM;
e4e61fdb
LP
156
157 s.next_in = src;
158 s.avail_in = src_size;
159
160 s.next_out = *dst;
93b73b06 161 s.avail_out = space;
e4e61fdb
LP
162
163 for (;;) {
fa1c4b51 164 size_t used;
e4e61fdb
LP
165
166 ret = lzma_code(&s, LZMA_FINISH);
167
168 if (ret == LZMA_STREAM_END)
169 break;
d89c8fdf
ZJS
170 else if (ret != LZMA_OK)
171 return -ENOMEM;
e4e61fdb 172
93b73b06
LP
173 if (dst_max > 0 && (space - s.avail_out) >= dst_max)
174 break;
d89c8fdf
ZJS
175 else if (dst_max > 0 && space == dst_max)
176 return -ENOBUFS;
93b73b06 177
5e592c66 178 used = space - s.avail_out;
fa1c4b51 179 space = MIN(2 * space, dst_max ?: (size_t) -1);
5e592c66 180 if (!greedy_realloc(dst, dst_alloc_size, space, 1))
01c3322e 181 return -ENOMEM;
e4e61fdb 182
5e592c66 183 s.avail_out = space - used;
8e170d29 184 s.next_out = *(uint8_t**)dst + used;
e4e61fdb
LP
185 }
186
93b73b06 187 *dst_size = space - s.avail_out;
d89c8fdf
ZJS
188 return 0;
189#else
190 return -EPROTONOSUPPORT;
191#endif
e4e61fdb
LP
192}
193
d89c8fdf 194int decompress_blob_lz4(const void *src, uint64_t src_size,
fa1c4b51 195 void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
d89c8fdf 196
349cc4a5 197#if HAVE_LZ4
d89c8fdf 198 char* out;
fa1c4b51 199 int r, size; /* LZ4 uses int for size */
e4e61fdb 200
d89c8fdf
ZJS
201 assert(src);
202 assert(src_size > 0);
203 assert(dst);
204 assert(dst_alloc_size);
205 assert(dst_size);
206 assert(*dst_alloc_size == 0 || *dst);
207
208 if (src_size <= 8)
209 return -EBADMSG;
210
211 size = le64toh( *(le64_t*)src );
b3aa6229 212 if (size < 0 || (unsigned) size != le64toh(*(le64_t*)src))
fa1c4b51
ZJS
213 return -EFBIG;
214 if ((size_t) size > *dst_alloc_size) {
d89c8fdf
ZJS
215 out = realloc(*dst, size);
216 if (!out)
217 return -ENOMEM;
218 *dst = out;
219 *dst_alloc_size = size;
220 } else
221 out = *dst;
222
8e170d29 223 r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
fa1c4b51 224 if (r < 0 || r != size)
d89c8fdf
ZJS
225 return -EBADMSG;
226
227 *dst_size = size;
228 return 0;
229#else
230 return -EPROTONOSUPPORT;
231#endif
232}
233
234int decompress_blob(int compression,
235 const void *src, uint64_t src_size,
fa1c4b51 236 void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
d89c8fdf
ZJS
237 if (compression == OBJECT_COMPRESSED_XZ)
238 return decompress_blob_xz(src, src_size,
239 dst, dst_alloc_size, dst_size, dst_max);
240 else if (compression == OBJECT_COMPRESSED_LZ4)
241 return decompress_blob_lz4(src, src_size,
242 dst, dst_alloc_size, dst_size, dst_max);
243 else
244 return -EBADMSG;
245}
246
247
248int decompress_startswith_xz(const void *src, uint64_t src_size,
fa1c4b51
ZJS
249 void **buffer, size_t *buffer_size,
250 const void *prefix, size_t prefix_len,
d89c8fdf
ZJS
251 uint8_t extra) {
252
349cc4a5 253#if HAVE_XZ
5e592c66 254 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
e4e61fdb 255 lzma_ret ret;
e4e61fdb 256
d89c8fdf 257 /* Checks whether the decompressed blob starts with the
e4e61fdb
LP
258 * mentioned prefix. The byte extra needs to follow the
259 * prefix */
260
261 assert(src);
262 assert(src_size > 0);
263 assert(buffer);
264 assert(buffer_size);
265 assert(prefix);
266 assert(*buffer_size == 0 || *buffer);
267
268 ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
269 if (ret != LZMA_OK)
d89c8fdf 270 return -EBADMSG;
e4e61fdb 271
d89c8fdf
ZJS
272 if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
273 return -ENOMEM;
e4e61fdb
LP
274
275 s.next_in = src;
276 s.avail_in = src_size;
277
278 s.next_out = *buffer;
279 s.avail_out = *buffer_size;
280
281 for (;;) {
e4e61fdb
LP
282 ret = lzma_code(&s, LZMA_FINISH);
283
4c701096 284 if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
d89c8fdf 285 return -EBADMSG;
e4e61fdb 286
5e592c66
ZJS
287 if (*buffer_size - s.avail_out >= prefix_len + 1)
288 return memcmp(*buffer, prefix, prefix_len) == 0 &&
289 ((const uint8_t*) *buffer)[prefix_len] == extra;
e4e61fdb
LP
290
291 if (ret == LZMA_STREAM_END)
d89c8fdf 292 return 0;
e4e61fdb 293
e4e61fdb
LP
294 s.avail_out += *buffer_size;
295
5e592c66 296 if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1)))
d89c8fdf 297 return -ENOMEM;
e4e61fdb 298
8e170d29 299 s.next_out = *(uint8_t**)buffer + *buffer_size - s.avail_out;
5e592c66 300 }
d89c8fdf
ZJS
301
302#else
303 return -EPROTONOSUPPORT;
304#endif
305}
306
307int decompress_startswith_lz4(const void *src, uint64_t src_size,
fa1c4b51
ZJS
308 void **buffer, size_t *buffer_size,
309 const void *prefix, size_t prefix_len,
d89c8fdf 310 uint8_t extra) {
349cc4a5 311#if HAVE_LZ4
d89c8fdf
ZJS
312 /* Checks whether the decompressed blob starts with the
313 * mentioned prefix. The byte extra needs to follow the
314 * prefix */
315
316 int r;
1f4b467d 317 size_t size;
d89c8fdf
ZJS
318
319 assert(src);
320 assert(src_size > 0);
321 assert(buffer);
322 assert(buffer_size);
323 assert(prefix);
324 assert(*buffer_size == 0 || *buffer);
325
326 if (src_size <= 8)
327 return -EBADMSG;
328
329 if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
330 return -ENOMEM;
331
8e170d29 332 r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8,
d89c8fdf 333 prefix_len + 1, *buffer_size);
1f4b467d
ZJS
334 if (r >= 0)
335 size = (unsigned) r;
336 else {
337 /* lz4 always tries to decode full "sequence", so in
338 * pathological cases might need to decompress the
339 * full field. */
340 r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0);
341 if (r < 0)
342 return r;
343 }
d89c8fdf 344
1f4b467d 345 if (size >= prefix_len + 1)
d89c8fdf
ZJS
346 return memcmp(*buffer, prefix, prefix_len) == 0 &&
347 ((const uint8_t*) *buffer)[prefix_len] == extra;
348 else
349 return 0;
350
351#else
352 return -EPROTONOSUPPORT;
353#endif
e4e61fdb 354}
355b59e2 355
d89c8fdf
ZJS
356int decompress_startswith(int compression,
357 const void *src, uint64_t src_size,
fa1c4b51
ZJS
358 void **buffer, size_t *buffer_size,
359 const void *prefix, size_t prefix_len,
d89c8fdf
ZJS
360 uint8_t extra) {
361 if (compression == OBJECT_COMPRESSED_XZ)
362 return decompress_startswith_xz(src, src_size,
363 buffer, buffer_size,
364 prefix, prefix_len,
365 extra);
366 else if (compression == OBJECT_COMPRESSED_LZ4)
367 return decompress_startswith_lz4(src, src_size,
368 buffer, buffer_size,
369 prefix, prefix_len,
370 extra);
371 else
372 return -EBADMSG;
373}
374
59f448cf 375int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
349cc4a5 376#if HAVE_XZ
355b59e2
ZJS
377 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
378 lzma_ret ret;
355b59e2
ZJS
379 uint8_t buf[BUFSIZ], out[BUFSIZ];
380 lzma_action action = LZMA_RUN;
381
382 assert(fdf >= 0);
383 assert(fdt >= 0);
384
d89c8fdf 385 ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
355b59e2 386 if (ret != LZMA_OK) {
1fa2f38f 387 log_error("Failed to initialize XZ encoder: code %u", ret);
355b59e2
ZJS
388 return -EINVAL;
389 }
390
391 for (;;) {
392 if (s.avail_in == 0 && action == LZMA_RUN) {
393 size_t m = sizeof(buf);
394 ssize_t n;
395
59f448cf
LP
396 if (max_bytes != (uint64_t) -1 && (uint64_t) m > max_bytes)
397 m = (size_t) max_bytes;
355b59e2
ZJS
398
399 n = read(fdf, buf, m);
400 if (n < 0)
401 return -errno;
402 if (n == 0)
403 action = LZMA_FINISH;
404 else {
405 s.next_in = buf;
406 s.avail_in = n;
407
59f448cf
LP
408 if (max_bytes != (uint64_t) -1) {
409 assert(max_bytes >= (uint64_t) n);
355b59e2
ZJS
410 max_bytes -= n;
411 }
412 }
413 }
414
415 if (s.avail_out == 0) {
416 s.next_out = out;
417 s.avail_out = sizeof(out);
418 }
419
420 ret = lzma_code(&s, action);
4c701096 421 if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) {
1fa2f38f 422 log_error("Compression failed: code %u", ret);
355b59e2
ZJS
423 return -EBADMSG;
424 }
425
426 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
427 ssize_t n, k;
428
429 n = sizeof(out) - s.avail_out;
430
355b59e2
ZJS
431 k = loop_write(fdt, out, n, false);
432 if (k < 0)
433 return k;
355b59e2
ZJS
434
435 if (ret == LZMA_STREAM_END) {
fa1c4b51 436 log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
355b59e2
ZJS
437 s.total_in, s.total_out,
438 (double) s.total_out / s.total_in * 100);
439
440 return 0;
441 }
442 }
443 }
3b1a55e1
ZJS
444#else
445 return -EPROTONOSUPPORT;
446#endif
355b59e2
ZJS
447}
448
4b5bc539 449#define LZ4_BUFSIZE (512*1024u)
d89c8fdf 450
59f448cf 451int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
d89c8fdf 452
349cc4a5 453#if HAVE_LZ4
4b5bc539
ZJS
454 LZ4F_errorCode_t c;
455 _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
456 _cleanup_free_ char *buf = NULL;
457 char *src = NULL;
d487b815 458 size_t size, n, total_in = 0, total_out, offset = 0, frame_size;
4b5bc539
ZJS
459 struct stat st;
460 int r;
461 static const LZ4F_compressOptions_t options = {
462 .stableSrc = 1,
463 };
464 static const LZ4F_preferences_t preferences = {
465 .frameInfo.blockSizeID = 5,
466 };
d89c8fdf 467
4b5bc539
ZJS
468 c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION);
469 if (LZ4F_isError(c))
470 return -ENOMEM;
d89c8fdf 471
4b5bc539 472 if (fstat(fdf, &st) < 0)
5146f9f0 473 return log_debug_errno(errno, "fstat() failed: %m");
d89c8fdf 474
4b5bc539
ZJS
475 frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences);
476 size = frame_size + 64*1024; /* add some space for header and trailer */
477 buf = malloc(size);
478 if (!buf)
479 return -ENOMEM;
d89c8fdf 480
d487b815 481 n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences);
4b5bc539
ZJS
482 if (LZ4F_isError(n))
483 return -EINVAL;
d89c8fdf 484
4b5bc539 485 src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0);
e0685172 486 if (src == MAP_FAILED)
4b5bc539 487 return -errno;
d89c8fdf 488
4b5bc539 489 log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n);
d89c8fdf 490
4b5bc539
ZJS
491 while (total_in < (size_t) st.st_size) {
492 ssize_t k;
d89c8fdf 493
4b5bc539
ZJS
494 k = MIN(LZ4_BUFSIZE, st.st_size - total_in);
495 n = LZ4F_compressUpdate(ctx, buf + offset, size - offset,
496 src + total_in, k, &options);
497 if (LZ4F_isError(n)) {
498 r = -ENOTRECOVERABLE;
499 goto cleanup;
d89c8fdf
ZJS
500 }
501
4b5bc539
ZJS
502 total_in += k;
503 offset += n;
504 total_out += n;
d89c8fdf 505
4b5bc539 506 if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
82e24b00 507 log_debug("Compressed stream longer than %"PRIu64" bytes", max_bytes);
4b5bc539
ZJS
508 return -EFBIG;
509 }
d89c8fdf 510
4b5bc539 511 if (size - offset < frame_size + 4) {
4b5bc539
ZJS
512 k = loop_write(fdt, buf, offset, false);
513 if (k < 0) {
514 r = k;
515 goto cleanup;
516 }
517 offset = 0;
518 }
519 }
d89c8fdf 520
4b5bc539
ZJS
521 n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options);
522 if (LZ4F_isError(n)) {
523 r = -ENOTRECOVERABLE;
524 goto cleanup;
d89c8fdf
ZJS
525 }
526
4b5bc539
ZJS
527 offset += n;
528 total_out += n;
4b5bc539
ZJS
529 r = loop_write(fdt, buf, offset, false);
530 if (r < 0)
531 goto cleanup;
d89c8fdf
ZJS
532
533 log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
534 total_in, total_out,
535 (double) total_out / total_in * 100);
4b5bc539
ZJS
536 cleanup:
537 munmap(src, st.st_size);
538 return r;
d89c8fdf
ZJS
539#else
540 return -EPROTONOSUPPORT;
541#endif
542}
543
59f448cf 544int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
d89c8fdf 545
349cc4a5 546#if HAVE_XZ
355b59e2
ZJS
547 _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
548 lzma_ret ret;
549
550 uint8_t buf[BUFSIZ], out[BUFSIZ];
551 lzma_action action = LZMA_RUN;
552
553 assert(fdf >= 0);
554 assert(fdt >= 0);
555
556 ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
557 if (ret != LZMA_OK) {
5146f9f0 558 log_debug("Failed to initialize XZ decoder: code %u", ret);
d89c8fdf 559 return -ENOMEM;
355b59e2
ZJS
560 }
561
562 for (;;) {
563 if (s.avail_in == 0 && action == LZMA_RUN) {
564 ssize_t n;
565
566 n = read(fdf, buf, sizeof(buf));
567 if (n < 0)
568 return -errno;
569 if (n == 0)
570 action = LZMA_FINISH;
571 else {
572 s.next_in = buf;
573 s.avail_in = n;
574 }
575 }
576
577 if (s.avail_out == 0) {
578 s.next_out = out;
579 s.avail_out = sizeof(out);
580 }
581
582 ret = lzma_code(&s, action);
4c701096 583 if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) {
5146f9f0 584 log_debug("Decompression failed: code %u", ret);
355b59e2
ZJS
585 return -EBADMSG;
586 }
587
588 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
589 ssize_t n, k;
590
591 n = sizeof(out) - s.avail_out;
592
59f448cf
LP
593 if (max_bytes != (uint64_t) -1) {
594 if (max_bytes < (uint64_t) n)
d89c8fdf 595 return -EFBIG;
355b59e2
ZJS
596
597 max_bytes -= n;
598 }
599
355b59e2
ZJS
600 k = loop_write(fdt, out, n, false);
601 if (k < 0)
602 return k;
355b59e2
ZJS
603
604 if (ret == LZMA_STREAM_END) {
fa1c4b51 605 log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
355b59e2
ZJS
606 s.total_in, s.total_out,
607 (double) s.total_out / s.total_in * 100);
608
609 return 0;
610 }
611 }
612 }
d89c8fdf 613#else
5146f9f0 614 log_debug("Cannot decompress file. Compiled without XZ support.");
d89c8fdf
ZJS
615 return -EPROTONOSUPPORT;
616#endif
617}
618
8e64dd1e 619int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
349cc4a5 620#if HAVE_LZ4
4b5bc539
ZJS
621 size_t c;
622 _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
623 _cleanup_free_ char *buf = NULL;
624 char *src;
625 struct stat st;
626 int r = 0;
627 size_t total_in = 0, total_out = 0;
628
629 c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION);
630 if (LZ4F_isError(c))
631 return -ENOMEM;
632
633 if (fstat(in, &st) < 0)
5146f9f0 634 return log_debug_errno(errno, "fstat() failed: %m");
4b5bc539
ZJS
635
636 buf = malloc(LZ4_BUFSIZE);
637 if (!buf)
638 return -ENOMEM;
639
640 src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0);
e0685172 641 if (src == MAP_FAILED)
4b5bc539
ZJS
642 return -errno;
643
644 while (total_in < (size_t) st.st_size) {
645 size_t produced = LZ4_BUFSIZE;
646 size_t used = st.st_size - total_in;
647
648 c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL);
649 if (LZ4F_isError(c)) {
650 r = -EBADMSG;
651 goto cleanup;
652 }
653
654 total_in += used;
655 total_out += produced;
656
657 if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
82e24b00 658 log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes);
5146f9f0 659 r = -EFBIG;
4b5bc539
ZJS
660 goto cleanup;
661 }
662
663 r = loop_write(out, buf, produced, false);
664 if (r < 0)
665 goto cleanup;
666 }
667
668 log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
669 total_in, total_out,
670 (double) total_out / total_in * 100);
671 cleanup:
672 munmap(src, st.st_size);
673 return r;
d89c8fdf 674#else
5146f9f0 675 log_debug("Cannot decompress file. Compiled without LZ4 support.");
d89c8fdf
ZJS
676 return -EPROTONOSUPPORT;
677#endif
678}
679
59f448cf 680int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) {
d89c8fdf
ZJS
681
682 if (endswith(filename, ".lz4"))
683 return decompress_stream_lz4(fdf, fdt, max_bytes);
684 else if (endswith(filename, ".xz"))
685 return decompress_stream_xz(fdf, fdt, max_bytes);
686 else
687 return -EPROTONOSUPPORT;
355b59e2 688}