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