chiark / gitweb /
journald: fix some xsprrintf() buffer size fallout
[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 %u", 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 %u", 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                         k = loop_write(fdt, out, n, false);
404                         if (k < 0)
405                                 return k;
406
407                         if (ret == LZMA_STREAM_END) {
408                                 log_debug("XZ compression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
409                                           s.total_in, s.total_out,
410                                           (double) s.total_out / s.total_in * 100);
411
412                                 return 0;
413                         }
414                 }
415         }
416 #else
417         return -EPROTONOSUPPORT;
418 #endif
419 }
420
421 #define LZ4_BUFSIZE (512*1024)
422
423 int compress_stream_lz4(int fdf, int fdt, off_t max_bytes) {
424
425 #ifdef HAVE_LZ4
426
427         _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *out = NULL;
428         char *buf;
429         LZ4_stream_t lz4_data = {};
430         le32_t header;
431         size_t total_in = 0, total_out = sizeof(header);
432         ssize_t n;
433
434         assert(fdf >= 0);
435         assert(fdt >= 0);
436
437         buf1 = malloc(LZ4_BUFSIZE);
438         buf2 = malloc(LZ4_BUFSIZE);
439         out = malloc(LZ4_COMPRESSBOUND(LZ4_BUFSIZE));
440         if (!buf1 || !buf2 || !out)
441                 return log_oom();
442
443         buf = buf1;
444         for (;;) {
445                 size_t m;
446                 int r;
447
448                 m = LZ4_BUFSIZE;
449                 if (max_bytes != -1 && m > (size_t) max_bytes - total_in)
450                         m = max_bytes - total_in;
451
452                 n = read(fdf, buf, m);
453                 if (n < 0)
454                         return -errno;
455                 if (n == 0)
456                         break;
457
458                 total_in += n;
459
460                 r = LZ4_compress_continue(&lz4_data, buf, out, n);
461                 if (r == 0) {
462                         log_error("LZ4 compression failed.");
463                         return -EBADMSG;
464                 }
465
466                 header = htole32(r);
467                 errno = 0;
468
469                 n = write(fdt, &header, sizeof(header));
470                 if (n < 0)
471                         return -errno;
472                 if (n != sizeof(header))
473                         return errno ? -errno : -EIO;
474
475                 n = loop_write(fdt, out, r, false);
476                 if (n < 0)
477                         return n;
478
479                 total_out += sizeof(header) + r;
480
481                 buf = buf == buf1 ? buf2 : buf1;
482         }
483
484         header = htole32(0);
485         n = write(fdt, &header, sizeof(header));
486         if (n < 0)
487                 return -errno;
488         if (n != sizeof(header))
489                 return errno ? -errno : -EIO;
490
491         log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)",
492                   total_in, total_out,
493                   (double) total_out / total_in * 100);
494
495         return 0;
496 #else
497         return -EPROTONOSUPPORT;
498 #endif
499 }
500
501 int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) {
502
503 #ifdef HAVE_XZ
504         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
505         lzma_ret ret;
506
507         uint8_t buf[BUFSIZ], out[BUFSIZ];
508         lzma_action action = LZMA_RUN;
509
510         assert(fdf >= 0);
511         assert(fdt >= 0);
512
513         ret = lzma_stream_decoder(&s, UINT64_MAX, 0);
514         if (ret != LZMA_OK) {
515                 log_error("Failed to initialize XZ decoder: code %u", ret);
516                 return -ENOMEM;
517         }
518
519         for (;;) {
520                 if (s.avail_in == 0 && action == LZMA_RUN) {
521                         ssize_t n;
522
523                         n = read(fdf, buf, sizeof(buf));
524                         if (n < 0)
525                                 return -errno;
526                         if (n == 0)
527                                 action = LZMA_FINISH;
528                         else {
529                                 s.next_in = buf;
530                                 s.avail_in = n;
531                         }
532                 }
533
534                 if (s.avail_out == 0) {
535                         s.next_out = out;
536                         s.avail_out = sizeof(out);
537                 }
538
539                 ret = lzma_code(&s, action);
540                 if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
541                         log_error("Decompression failed: code %u", ret);
542                         return -EBADMSG;
543                 }
544
545                 if (s.avail_out == 0 || ret == LZMA_STREAM_END) {
546                         ssize_t n, k;
547
548                         n = sizeof(out) - s.avail_out;
549
550                         if (max_bytes != -1) {
551                                 if (max_bytes < n)
552                                         return -EFBIG;
553
554                                 max_bytes -= n;
555                         }
556
557                         k = loop_write(fdt, out, n, false);
558                         if (k < 0)
559                                 return k;
560
561                         if (ret == LZMA_STREAM_END) {
562                                 log_debug("XZ decompression finished (%"PRIu64" -> %"PRIu64" bytes, %.1f%%)",
563                                           s.total_in, s.total_out,
564                                           (double) s.total_out / s.total_in * 100);
565
566                                 return 0;
567                         }
568                 }
569         }
570 #else
571         log_error("Cannot decompress file. Compiled without XZ support.");
572         return -EPROTONOSUPPORT;
573 #endif
574 }
575
576 int decompress_stream_lz4(int fdf, int fdt, off_t max_bytes) {
577
578 #ifdef HAVE_LZ4
579         _cleanup_free_ char *buf = NULL, *out = NULL;
580         size_t buf_size = 0;
581         LZ4_streamDecode_t lz4_data = {};
582         le32_t header;
583         size_t total_in = sizeof(header), total_out = 0;
584
585         assert(fdf >= 0);
586         assert(fdt >= 0);
587
588         out = malloc(4*LZ4_BUFSIZE);
589         if (!out)
590                 return log_oom();
591
592         for (;;) {
593                 ssize_t n, m;
594                 int r;
595
596                 n = read(fdf, &header, sizeof(header));
597                 if (n < 0)
598                         return -errno;
599                 if (n != sizeof(header))
600                         return errno ? -errno : -EIO;
601
602                 m = le32toh(header);
603                 if (m == 0)
604                         break;
605
606                 /* We refuse to use a bigger decompression buffer than
607                  * the one used for compression by 4 times. This means
608                  * that compression buffer size can be enlarged 4
609                  * times. This can be changed, but old binaries might
610                  * not accept buffers compressed by newer binaries then.
611                  */
612                 if (m > LZ4_COMPRESSBOUND(LZ4_BUFSIZE * 4)) {
613                         log_error("Compressed stream block too big: %zd bytes", m);
614                         return -EBADMSG;
615                 }
616
617                 total_in += sizeof(header) + m;
618
619                 if (!GREEDY_REALLOC(buf, buf_size, m))
620                         return log_oom();
621
622                 errno = 0;
623                 n = loop_read(fdf, buf, m, false);
624                 if (n < 0)
625                         return n;
626                 if (n != m)
627                         return errno ? -errno : -EIO;
628
629                 r = LZ4_decompress_safe_continue(&lz4_data, buf, out, m, 4*LZ4_BUFSIZE);
630                 if (r <= 0)
631                         log_error("LZ4 decompression failed.");
632
633                 total_out += r;
634
635                 if (max_bytes != -1 && total_out > (size_t) max_bytes) {
636                         log_debug("Decompressed stream longer than %zd bytes", max_bytes);
637                         return -EFBIG;
638                 }
639
640                 n = loop_write(fdt, out, r, false);
641                 if (n < 0)
642                         return n;
643         }
644
645         log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)",
646                   total_in, total_out,
647                   (double) total_out / total_in * 100);
648
649         return 0;
650 #else
651         log_error("Cannot decompress file. Compiled without LZ4 support.");
652         return -EPROTONOSUPPORT;
653 #endif
654 }
655
656 int decompress_stream(const char *filename, int fdf, int fdt, off_t max_bytes) {
657
658         if (endswith(filename, ".lz4"))
659                 return decompress_stream_lz4(fdf, fdt, max_bytes);
660         else if (endswith(filename, ".xz"))
661                 return decompress_stream_xz(fdf, fdt, max_bytes);
662         else
663                 return -EPROTONOSUPPORT;
664 }