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