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