]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/journal/compress.c
Add SPDX license identifiers to source files under the LGPL
[thirdparty/systemd.git] / src / journal / compress.c
index 46a54104821eb61f9be7ed1b14faef51e43d09b1..a659459e496aff7005bfc902a4332dd10c5f9356 100644 (file)
@@ -1,5 +1,4 @@
-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
 /***
   This file is part of systemd.
 
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <inttypes.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
 #include <unistd.h>
 
-#ifdef HAVE_XZ
-#  include <lzma.h>
+#if HAVE_XZ
+#include <lzma.h>
 #endif
 
-#ifdef HAVE_LZ4
-#  include <lz4.h>
-#  include <lz4frame.h>
+#if HAVE_LZ4
+#include <lz4.h>
+#include <lz4frame.h>
 #endif
 
+#include "alloc-util.h"
+#include "compress.h"
+#include "fd-util.h"
+#include "io-util.h"
 #include "journal-def.h"
 #include "macro.h"
 #include "sparse-endian.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "util.h"
-#include "compress.h"
 
-#ifdef HAVE_LZ4
+#if HAVE_LZ4
 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionContext);
 DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext);
 #endif
@@ -54,14 +58,16 @@ static const char* const object_compressed_table[_OBJECT_COMPRESSED_MAX] = {
 
 DEFINE_STRING_TABLE_LOOKUP(object_compressed, int);
 
-int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
-#ifdef HAVE_XZ
+int compress_blob_xz(const void *src, uint64_t src_size,
+                     void *dst, size_t dst_alloc_size, size_t *dst_size) {
+#if HAVE_XZ
         static const lzma_options_lzma opt = {
                 1u << 20u, NULL, 0, LZMA_LC_DEFAULT, LZMA_LP_DEFAULT,
-                LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4};
-        static const lzma_filter filters[2] = {
-                {LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt},
-                {LZMA_VLI_UNKNOWN, NULL}
+                LZMA_PB_DEFAULT, LZMA_MODE_FAST, 128, LZMA_MF_HC3, 4
+        };
+        static const lzma_filter filters[] = {
+                { LZMA_FILTER_LZMA2, (lzma_options_lzma*) &opt },
+                { LZMA_VLI_UNKNOWN, NULL }
         };
         lzma_ret ret;
         size_t out_pos = 0;
@@ -69,6 +75,7 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_
         assert(src);
         assert(src_size > 0);
         assert(dst);
+        assert(dst_alloc_size > 0);
         assert(dst_size);
 
         /* Returns < 0 if we couldn't compress the data or the
@@ -78,7 +85,7 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_
                 return -ENOBUFS;
 
         ret = lzma_stream_buffer_encode((lzma_filter*) filters, LZMA_CHECK_NONE, NULL,
-                                        src, src_size, dst, &out_pos, src_size - 1);
+                                        src, src_size, dst, &out_pos, dst_alloc_size);
         if (ret != LZMA_OK)
                 return -ENOBUFS;
 
@@ -89,13 +96,15 @@ int compress_blob_xz(const void *src, uint64_t src_size, void *dst, size_t *dst_
 #endif
 }
 
-int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst_size) {
-#ifdef HAVE_LZ4
+int compress_blob_lz4(const void *src, uint64_t src_size,
+                      void *dst, size_t dst_alloc_size, size_t *dst_size) {
+#if HAVE_LZ4
         int r;
 
         assert(src);
         assert(src_size > 0);
         assert(dst);
+        assert(dst_alloc_size > 0);
         assert(dst_size);
 
         /* Returns < 0 if we couldn't compress the data or the
@@ -104,7 +113,11 @@ int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst
         if (src_size < 9)
                 return -ENOBUFS;
 
-        r = LZ4_compress_limitedOutput(src, dst + 8, src_size, src_size - 8 - 1);
+#if LZ4_VERSION_NUMBER >= 10700
+        r = LZ4_compress_default(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
+#else
+        r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8);
+#endif
         if (r <= 0)
                 return -ENOBUFS;
 
@@ -121,7 +134,7 @@ int compress_blob_lz4(const void *src, uint64_t src_size, void *dst, size_t *dst
 int decompress_blob_xz(const void *src, uint64_t src_size,
                        void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
 
-#ifdef HAVE_XZ
+#if HAVE_XZ
         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
         lzma_ret ret;
         size_t space;
@@ -168,7 +181,7 @@ int decompress_blob_xz(const void *src, uint64_t src_size,
                         return -ENOMEM;
 
                 s.avail_out = space - used;
-                s.next_out = *dst + used;
+                s.next_out = *(uint8_t**)dst + used;
         }
 
         *dst_size = space - s.avail_out;
@@ -181,7 +194,7 @@ int decompress_blob_xz(const void *src, uint64_t src_size,
 int decompress_blob_lz4(const void *src, uint64_t src_size,
                         void **dst, size_t *dst_alloc_size, size_t* dst_size, size_t dst_max) {
 
-#ifdef HAVE_LZ4
+#if HAVE_LZ4
         char* out;
         int r, size; /* LZ4 uses int for size */
 
@@ -196,7 +209,7 @@ int decompress_blob_lz4(const void *src, uint64_t src_size,
                 return -EBADMSG;
 
         size = le64toh( *(le64_t*)src );
-        if (size < 0 || (le64_t) size != *(le64_t*)src)
+        if (size < 0 || (unsigned) size != le64toh(*(le64_t*)src))
                 return -EFBIG;
         if ((size_t) size > *dst_alloc_size) {
                 out = realloc(*dst, size);
@@ -207,7 +220,7 @@ int decompress_blob_lz4(const void *src, uint64_t src_size,
         } else
                 out = *dst;
 
-        r = LZ4_decompress_safe(src + 8, out, src_size - 8, size);
+        r = LZ4_decompress_safe((char*)src + 8, out, src_size - 8, size);
         if (r < 0 || r != size)
                 return -EBADMSG;
 
@@ -237,7 +250,7 @@ int decompress_startswith_xz(const void *src, uint64_t src_size,
                              const void *prefix, size_t prefix_len,
                              uint8_t extra) {
 
-#ifdef HAVE_XZ
+#if HAVE_XZ
         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
         lzma_ret ret;
 
@@ -268,7 +281,7 @@ int decompress_startswith_xz(const void *src, uint64_t src_size,
         for (;;) {
                 ret = lzma_code(&s, LZMA_FINISH);
 
-                if (ret != LZMA_STREAM_END && ret != LZMA_OK)
+                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END))
                         return -EBADMSG;
 
                 if (*buffer_size - s.avail_out >= prefix_len + 1)
@@ -283,7 +296,7 @@ int decompress_startswith_xz(const void *src, uint64_t src_size,
                 if (!(greedy_realloc(buffer, buffer_size, *buffer_size * 2, 1)))
                         return -ENOMEM;
 
-                s.next_out = *buffer + *buffer_size - s.avail_out;
+                s.next_out = *(uint8_t**)buffer + *buffer_size - s.avail_out;
         }
 
 #else
@@ -295,12 +308,13 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
                               void **buffer, size_t *buffer_size,
                               const void *prefix, size_t prefix_len,
                               uint8_t extra) {
-#ifdef HAVE_LZ4
+#if HAVE_LZ4
         /* Checks whether the decompressed blob starts with the
          * mentioned prefix. The byte extra needs to follow the
          * prefix */
 
         int r;
+        size_t size;
 
         assert(src);
         assert(src_size > 0);
@@ -315,12 +329,20 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size,
         if (!(greedy_realloc(buffer, buffer_size, ALIGN_8(prefix_len + 1), 1)))
                 return -ENOMEM;
 
-        r = LZ4_decompress_safe_partial(src + 8, *buffer, src_size - 8,
+        r = LZ4_decompress_safe_partial((char*)src + 8, *buffer, src_size - 8,
                                         prefix_len + 1, *buffer_size);
+        if (r >= 0)
+                size = (unsigned) r;
+        else {
+                /* lz4 always tries to decode full "sequence", so in
+                 * pathological cases might need to decompress the
+                 * full field. */
+                r = decompress_blob_lz4(src, src_size, buffer, buffer_size, &size, 0);
+                if (r < 0)
+                        return r;
+        }
 
-        if (r < 0)
-                return -EBADMSG;
-        if ((unsigned) r >= prefix_len + 1)
+        if (size >= prefix_len + 1)
                 return memcmp(*buffer, prefix, prefix_len) == 0 &&
                         ((const uint8_t*) *buffer)[prefix_len] == extra;
         else
@@ -351,7 +373,7 @@ int decompress_startswith(int compression,
 }
 
 int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
-#ifdef HAVE_XZ
+#if HAVE_XZ
         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
         lzma_ret ret;
         uint8_t buf[BUFSIZ], out[BUFSIZ];
@@ -396,7 +418,7 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
                 }
 
                 ret = lzma_code(&s, action);
-                if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
+                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) {
                         log_error("Compression failed: code %u", ret);
                         return -EBADMSG;
                 }
@@ -428,12 +450,12 @@ int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
 
 int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
 
-#ifdef HAVE_LZ4
+#if HAVE_LZ4
         LZ4F_errorCode_t c;
         _cleanup_(LZ4F_freeCompressionContextp) LZ4F_compressionContext_t ctx = NULL;
         _cleanup_free_ char *buf = NULL;
         char *src = NULL;
-        size_t size, n, total_in = 0, total_out = 0, offset = 0, frame_size;
+        size_t size, n, total_in = 0, total_out, offset = 0, frame_size;
         struct stat st;
         int r;
         static const LZ4F_compressOptions_t options = {
@@ -456,7 +478,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
         if (!buf)
                 return -ENOMEM;
 
-        n = offset = LZ4F_compressBegin(ctx, buf, size, &preferences);
+        n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences);
         if (LZ4F_isError(n))
                 return -EINVAL;
 
@@ -482,7 +504,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
                 total_out += n;
 
                 if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
-                        log_debug("Compressed stream longer than %zd bytes", max_bytes);
+                        log_debug("Compressed stream longer than %"PRIu64" bytes", max_bytes);
                         return -EFBIG;
                 }
 
@@ -521,7 +543,7 @@ int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
 
 int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
 
-#ifdef HAVE_XZ
+#if HAVE_XZ
         _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT;
         lzma_ret ret;
 
@@ -558,7 +580,7 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
                 }
 
                 ret = lzma_code(&s, action);
-                if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
+                if (!IN_SET(ret, LZMA_OK, LZMA_STREAM_END)) {
                         log_debug("Decompression failed: code %u", ret);
                         return -EBADMSG;
                 }
@@ -594,80 +616,8 @@ int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) {
 #endif
 }
 
-#ifdef HAVE_LZ4
-static int decompress_stream_lz4_v1(int fdf, int fdt, uint64_t max_bytes) {
-
-        _cleanup_free_ char *buf = NULL, *out = NULL;
-        size_t buf_size = 0;
-        LZ4_streamDecode_t lz4_data = {};
-        le32_t header;
-        size_t total_in = sizeof(header), total_out = 0;
-
-        assert(fdf >= 0);
-        assert(fdt >= 0);
-
-        out = malloc(4*LZ4_BUFSIZE);
-        if (!out)
-                return -ENOMEM;
-
-        for (;;) {
-                ssize_t m;
-                int r;
-
-                r = loop_read_exact(fdf, &header, sizeof(header), false);
-                if (r < 0)
-                        return r;
-
-                m = le32toh(header);
-                if (m == 0)
-                        break;
-
-                /* We refuse to use a bigger decompression buffer than
-                 * the one used for compression by 4 times. This means
-                 * that compression buffer size can be enlarged 4
-                 * times. This can be changed, but old binaries might
-                 * not accept buffers compressed by newer binaries then.
-                 */
-                if (m > LZ4_COMPRESSBOUND(LZ4_BUFSIZE * 4)) {
-                        log_debug("Compressed stream block too big: %zd bytes", m);
-                        return -ENOBUFS;
-                }
-
-                total_in += sizeof(header) + m;
-
-                if (!GREEDY_REALLOC(buf, buf_size, m))
-                        return -ENOMEM;
-
-                r = loop_read_exact(fdf, buf, m, false);
-                if (r < 0)
-                        return r;
-
-                r = LZ4_decompress_safe_continue(&lz4_data, buf, out, m, 4*LZ4_BUFSIZE);
-                if (r <= 0) {
-                        log_debug("LZ4 decompression failed (legacy format).");
-                        return -EBADMSG;
-                }
-
-                total_out += r;
-
-                if (max_bytes != (uint64_t) -1 && (uint64_t) total_out > max_bytes) {
-                        log_debug("Decompressed stream longer than %" PRIu64 " bytes", max_bytes);
-                        return -EFBIG;
-                }
-
-                r = loop_write(fdt, out, r, false);
-                if (r < 0)
-                        return r;
-        }
-
-        log_debug("LZ4 decompression finished (legacy format, %zu -> %zu bytes, %.1f%%)",
-                  total_in, total_out,
-                  (double) total_out / total_in * 100);
-
-        return 0;
-}
-
-static int decompress_stream_lz4_v2(int in, int out, uint64_t max_bytes) {
+int decompress_stream_lz4(int in, int out, uint64_t max_bytes) {
+#if HAVE_LZ4
         size_t c;
         _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL;
         _cleanup_free_ char *buf = NULL;
@@ -705,7 +655,7 @@ static int decompress_stream_lz4_v2(int in, int out, uint64_t max_bytes) {
                 total_out += produced;
 
                 if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) {
-                        log_debug("Decompressed stream longer than %zd bytes", max_bytes);
+                        log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes);
                         r = -EFBIG;
                         goto cleanup;
                 }
@@ -721,17 +671,6 @@ static int decompress_stream_lz4_v2(int in, int out, uint64_t max_bytes) {
  cleanup:
         munmap(src, st.st_size);
         return r;
-}
-#endif
-
-int decompress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) {
-#ifdef HAVE_LZ4
-        int r;
-
-        r = decompress_stream_lz4_v2(fdf, fdt, max_bytes);
-        if (r == -EBADMSG)
-                r = decompress_stream_lz4_v1(fdf, fdt, max_bytes);
-        return r;
 #else
         log_debug("Cannot decompress file. Compiled without LZ4 support.");
         return -EPROTONOSUPPORT;