Line data Source code
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 15 : DEFINE_STRING_TABLE_LOOKUP(object_compressed, int);
48 :
49 6874 : 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 6874 : size_t out_pos = 0;
60 :
61 6874 : assert(src);
62 6874 : assert(src_size > 0);
63 6874 : assert(dst);
64 6874 : 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 6874 : if (src_size < 80)
70 79 : return -ENOBUFS;
71 :
72 6795 : ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
73 : src, src_size, dst, &out_pos, src_size - 1);
74 6795 : if (ret != LZMA_OK)
75 7 : return -ENOBUFS;
76 :
77 6788 : *dst_size = out_pos;
78 6788 : return 0;
79 : #else
80 : return -EPROTONOSUPPORT;
81 : #endif
82 : }
83 :
84 0 : 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 0 : return -EPROTONOSUPPORT;
109 : #endif
110 : }
111 :
112 :
113 6794 : 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 13588 : _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
118 : lzma_ret ret;
119 : size_t space;
120 :
121 6794 : assert(src);
122 6794 : assert(src_size > 0);
123 6794 : assert(dst);
124 6794 : assert(dst_alloc_size);
125 6794 : assert(dst_size);
126 6794 : assert(*dst_alloc_size == 0 || *dst);
127 :
128 6794 : ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
129 6794 : if (ret != LZMA_OK)
130 0 : return -ENOMEM;
131 :
132 6794 : space = MIN(src_size * 2, dst_max ?: (size_t) -1);
133 6794 : if (!greedy_realloc(dst, dst_alloc_size, space, 1))
134 0 : return -ENOMEM;
135 :
136 6794 : s.next_in = src;
137 6794 : s.avail_in = src_size;
138 :
139 6794 : s.next_out = *dst;
140 6794 : s.avail_out = space;
141 :
142 : for (;;) {
143 : size_t used;
144 :
145 61520 : ret = lzma_code(&s, LZMA_FINISH);
146 :
147 61520 : if (ret == LZMA_STREAM_END)
148 6788 : break;
149 54732 : else if (ret != LZMA_OK)
150 6 : return -ENOMEM;
151 :
152 54726 : if (dst_max > 0 && (space - s.avail_out) >= dst_max)
153 : break;
154 54726 : else if (dst_max > 0 && space == dst_max)
155 0 : return -ENOBUFS;
156 :
157 54726 : used = space - s.avail_out;
158 54726 : space = MIN(2 * space, dst_max ?: (size_t) -1);
159 54726 : if (!greedy_realloc(dst, dst_alloc_size, space, 1))
160 0 : return -ENOMEM;
161 :
162 54726 : s.avail_out = space - used;
163 54726 : s.next_out = *dst + used;
164 54726 : }
165 :
166 6788 : *dst_size = space - s.avail_out;
167 6788 : return 0;
168 : #else
169 : return -EPROTONOSUPPORT;
170 : #endif
171 : }
172 :
173 0 : 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 0 : return -EPROTONOSUPPORT;
210 : #endif
211 : }
212 :
213 1 : 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 1 : if (compression == OBJECT_COMPRESSED_XZ)
217 1 : return decompress_blob_xz(src, src_size,
218 : dst, dst_alloc_size, dst_size, dst_max);
219 0 : else if (compression == OBJECT_COMPRESSED_LZ4)
220 0 : return decompress_blob_lz4(src, src_size,
221 : dst, dst_alloc_size, dst_size, dst_max);
222 : else
223 0 : return -EBADMSG;
224 : }
225 :
226 :
227 4 : 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 8 : _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 4 : assert(src);
241 4 : assert(src_size > 0);
242 4 : assert(buffer);
243 4 : assert(buffer_size);
244 4 : assert(prefix);
245 4 : assert(*buffer_size == 0 || *buffer);
246 :
247 4 : ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
248 4 : if (ret != LZMA_OK)
249 0 : return -EBADMSG;
250 :
251 4 : if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
252 0 : return -ENOMEM;
253 :
254 4 : s.next_in = src;
255 4 : s.avail_in = src_size;
256 :
257 4 : s.next_out = *buffer;
258 4 : s.avail_out = *buffer_size;
259 :
260 : for (;;) {
261 4 : ret = lzma_code(&s, LZMA_FINISH);
262 :
263 4 : if (ret != LZMA_STREAM_END && ret != LZMA_OK)
264 0 : return -EBADMSG;
265 :
266 4 : if (*buffer_size - s.avail_out >= prefix_len + 1)
267 7 : return memcmp(*buffer, prefix, prefix_len) == 0 &&
268 3 : ((const uint8_t*) *buffer)[prefix_len] == extra;
269 :
270 0 : if (ret == LZMA_STREAM_END)
271 0 : return 0;
272 :
273 0 : s.avail_out += *buffer_size;
274 :
275 0 : if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1)))
276 0 : return -ENOMEM;
277 :
278 0 : s.next_out = *buffer + *buffer_size - s.avail_out;
279 0 : }
280 :
281 : #else
282 : return -EPROTONOSUPPORT;
283 : #endif
284 : }
285 :
286 0 : 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 0 : return -EPROTONOSUPPORT;
323 : #endif
324 : }
325 :
326 0 : 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 0 : if (compression == OBJECT_COMPRESSED_XZ)
332 0 : return decompress_startswith_xz(src, src_size,
333 : buffer, buffer_size,
334 : prefix, prefix_len,
335 : extra);
336 0 : else if (compression == OBJECT_COMPRESSED_LZ4)
337 0 : return decompress_startswith_lz4(src, src_size,
338 : buffer, buffer_size,
339 : prefix, prefix_len,
340 : extra);
341 : else
342 0 : return -EBADMSG;
343 : }
344 :
345 1 : int compress_stream_xz(int fdf, int fdt, off_t max_bytes) {
346 : #ifdef HAVE_XZ
347 2 : _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
348 : lzma_ret ret;
349 :
350 : uint8_t buf[BUFSIZ], out[BUFSIZ];
351 1 : lzma_action action = LZMA_RUN;
352 :
353 1 : assert(fdf >= 0);
354 1 : assert(fdt >= 0);
355 :
356 1 : ret = lzma_easy_encoder(&s, LZMA_PRESET_DEFAULT, LZMA_CHECK_CRC64);
357 1 : if (ret != LZMA_OK) {
358 0 : log_error("Failed to initialize XZ encoder: code %u", ret);
359 0 : return -EINVAL;
360 : }
361 :
362 : for (;;) {
363 142 : if (s.avail_in == 0 && action == LZMA_RUN) {
364 116 : size_t m = sizeof(buf);
365 : ssize_t n;
366 :
367 116 : if (max_bytes != -1 && m > (size_t) max_bytes)
368 0 : m = max_bytes;
369 :
370 116 : n = read(fdf, buf, m);
371 116 : if (n < 0)
372 0 : return -errno;
373 116 : if (n == 0)
374 1 : action = LZMA_FINISH;
375 : else {
376 115 : s.next_in = buf;
377 115 : s.avail_in = n;
378 :
379 115 : if (max_bytes != -1) {
380 0 : assert(max_bytes >= n);
381 0 : max_bytes -= n;
382 : }
383 : }
384 : }
385 :
386 142 : if (s.avail_out == 0) {
387 31 : s.next_out = out;
388 31 : s.avail_out = sizeof(out);
389 : }
390 :
391 142 : ret = lzma_code(&s, action);
392 142 : if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
393 0 : log_error("Compression failed: code %u", ret);
394 0 : return -EBADMSG;
395 : }
396 :
397 142 : if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
398 : ssize_t n, k;
399 :
400 31 : n = sizeof(out) - s.avail_out;
401 :
402 31 : k = loop_write(fdt, out, n, false);
403 31 : if (k < 0)
404 0 : return k;
405 :
406 31 : if (ret == LZMA_STREAM_END) {
407 1 : 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 1 : return 0;
412 : }
413 : }
414 141 : }
415 : #else
416 : return -EPROTONOSUPPORT;
417 : #endif
418 : }
419 :
420 : #define LZ4_BUFSIZE (512*1024)
421 :
422 0 : 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 0 : return -EPROTONOSUPPORT;
497 : #endif
498 : }
499 :
500 3 : int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) {
501 :
502 : #ifdef HAVE_XZ
503 6 : _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
504 : lzma_ret ret;
505 :
506 : uint8_t buf[BUFSIZ], out[BUFSIZ];
507 3 : lzma_action action = LZMA_RUN;
508 :
509 3 : assert(fdf >= 0);
510 3 : assert(fdt >= 0);
511 :
512 3 : ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
513 3 : if (ret != LZMA_OK) {
514 0 : log_error("Failed to initialize XZ decoder: code %u", ret);
515 0 : return -ENOMEM;
516 : }
517 :
518 : for (;;) {
519 291 : if (s.avail_in == 0 && action == LZMA_RUN) {
520 : ssize_t n;
521 :
522 63 : n = read(fdf, buf, sizeof(buf));
523 63 : if (n < 0)
524 0 : return -errno;
525 63 : if (n == 0)
526 0 : action = LZMA_FINISH;
527 : else {
528 63 : s.next_in = buf;
529 63 : s.avail_in = n;
530 : }
531 : }
532 :
533 291 : if (s.avail_out == 0) {
534 231 : s.next_out = out;
535 231 : s.avail_out = sizeof(out);
536 : }
537 :
538 291 : ret = lzma_code(&s, action);
539 291 : if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
540 1 : log_error("Decompression failed: code %u", ret);
541 1 : return -EBADMSG;
542 : }
543 :
544 290 : if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
545 : ssize_t n, k;
546 :
547 230 : n = sizeof(out) - s.avail_out;
548 :
549 230 : if (max_bytes != -1) {
550 230 : if (max_bytes < n)
551 1 : return -EFBIG;
552 :
553 229 : max_bytes -= n;
554 : }
555 :
556 229 : k = loop_write(fdt, out, n, false);
557 229 : if (k < 0)
558 0 : return k;
559 :
560 229 : if (ret == LZMA_STREAM_END) {
561 1 : 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 1 : return 0;
566 : }
567 : }
568 288 : }
569 : #else
570 : log_error("Cannot decompress file. Compiled without XZ support.");
571 : return -EPROTONOSUPPORT;
572 : #endif
573 : }
574 :
575 0 : 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 0 : log_error("Cannot decompress file. Compiled without LZ4 support.");
646 0 : return -EPROTONOSUPPORT;
647 : #endif
648 : }
649 :
650 0 : int decompress_stream(const char *filename, int fdf, int fdt, off_t max_bytes) {
651 :
652 0 : if (endswith(filename, ".lz4"))
653 0 : return decompress_stream_lz4(fdf, fdt, max_bytes);
654 0 : else if (endswith(filename, ".xz"))
655 0 : return decompress_stream_xz(fdf, fdt, max_bytes);
656 : else
657 0 : return -EPROTONOSUPPORT;
658 : }
|