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