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