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