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