chiark / gitweb /
remove unused includes
[elogind.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 n, m;
593                 int r;
594
595                 n = read(fdf, &header, sizeof(header));
596                 if (n < 0)
597                         return -errno;
598                 if (n != sizeof(header))
599                         return errno ? -errno : -EIO;
600
601                 m = le32toh(header);
602                 if (m == 0)
603                         break;
604
605                 /* We refuse to use a bigger decompression buffer than
606                  * the one used for compression by 4 times. This means
607                  * that compression buffer size can be enlarged 4
608                  * times. This can be changed, but old binaries might
609                  * not accept buffers compressed by newer binaries then.
610                  */
611                 if (m > LZ4_COMPRESSBOUND(LZ4_BUFSIZE * 4)) {
612                         log_error("Compressed stream block too big: %zd bytes", m);
613                         return -EBADMSG;
614                 }
615
616                 total_in += sizeof(header) + m;
617
618                 if (!GREEDY_REALLOC(buf, buf_size, m))
619                         return log_oom();
620
621                 errno = 0;
622                 n = loop_read(fdf, buf, m, false);
623                 if (n < 0)
624                         return n;
625                 if (n != m)
626                         return errno ? -errno : -EIO;
627
628                 r = LZ4_decompress_safe_continue(&lz4_data, buf, out, m, 4*LZ4_BUFSIZE);
629                 if (r <= 0)
630                         log_error("LZ4 decompression failed.");
631
632                 total_out += r;
633
634                 if (max_bytes != -1 && total_out > (size_t) max_bytes) {
635                         log_debug("Decompressed stream longer than %zd bytes", max_bytes);
636                         return -EFBIG;
637                 }
638
639                 n = loop_write(fdt, out, r, false);
640                 if (n < 0)
641                         return n;
642         }
643
644         log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
645                   total_in, total_out,
646                   (double) total_out / total_in * 100);
647
648         return 0;
649 #else
650         log_error("Cannot decompress file. Compiled without LZ4 support.");
651         return -EPROTONOSUPPORT;
652 #endif
653 }
654
655 int decompress_stream(const char *filename, int fdf, int fdt, off_t max_bytes) {
656
657         if (endswith(filename, ".lz4"))
658                 return decompress_stream_lz4(fdf, fdt, max_bytes);
659         else if (endswith(filename, ".xz"))
660                 return decompress_stream_xz(fdf, fdt, max_bytes);
661         else
662                 return -EPROTONOSUPPORT;
663 }