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