diff options
Diffstat (limited to 'src/journal/compress.c')
-rw-r--r-- | src/journal/compress.c | 307 |
1 files changed, 166 insertions, 141 deletions
diff --git a/src/journal/compress.c b/src/journal/compress.c index 383f6a6e96..ba734b5561 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -1,5 +1,3 @@ -/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ - /*** This file is part of systemd. @@ -19,23 +17,36 @@ 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> +#include <lzma.h> #endif #ifdef HAVE_LZ4 -# include <lz4.h> +#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 "util.h" #include "sparse-endian.h" -#include "journal-def.h" +#include "string-table.h" +#include "string-util.h" +#include "util.h" + +#ifdef HAVE_LZ4 +DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_compressionContext_t, LZ4F_freeCompressionContext); +DEFINE_TRIVIAL_CLEANUP_FUNC(LZ4F_decompressionContext_t, LZ4F_freeDecompressionContext); +#endif #define ALIGN_8(l) ALIGN_TO(l, sizeof(size_t)) @@ -46,14 +57,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) { +int compress_blob_xz(const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size) { #ifdef 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; @@ -61,6 +74,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 @@ -70,7 +84,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; @@ -81,13 +95,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) { +int compress_blob_lz4(const void *src, uint64_t src_size, + void *dst, size_t dst_alloc_size, size_t *dst_size) { #ifdef 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 @@ -96,7 +112,7 @@ 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); + r = LZ4_compress_limitedOutput(src, (char*)dst + 8, src_size, (int) dst_alloc_size - 8); if (r <= 0) return -ENOBUFS; @@ -160,7 +176,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; @@ -188,7 +204,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); @@ -199,7 +215,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; @@ -275,7 +291,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 @@ -293,6 +309,7 @@ int decompress_startswith_lz4(const void *src, uint64_t src_size, * prefix */ int r; + size_t size; assert(src); assert(src_size > 0); @@ -307,12 +324,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 @@ -342,11 +367,10 @@ int decompress_startswith(int compression, return -EBADMSG; } -int compress_stream_xz(int fdf, int fdt, off_t max_bytes) { +int compress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { #ifdef HAVE_XZ _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; lzma_ret ret; - uint8_t buf[BUFSIZ], out[BUFSIZ]; lzma_action action = LZMA_RUN; @@ -364,8 +388,8 @@ int compress_stream_xz(int fdf, int fdt, off_t max_bytes) { size_t m = sizeof(buf); ssize_t n; - if (max_bytes != -1 && m > (size_t) max_bytes) - m = max_bytes; + if (max_bytes != (uint64_t) -1 && (uint64_t) m > max_bytes) + m = (size_t) max_bytes; n = read(fdf, buf, m); if (n < 0) @@ -376,8 +400,8 @@ int compress_stream_xz(int fdf, int fdt, off_t max_bytes) { s.next_in = buf; s.avail_in = n; - if (max_bytes != -1) { - assert(max_bytes >= n); + if (max_bytes != (uint64_t) -1) { + assert(max_bytes >= (uint64_t) n); max_bytes -= n; } } @@ -417,87 +441,102 @@ int compress_stream_xz(int fdf, int fdt, off_t max_bytes) { #endif } -#define LZ4_BUFSIZE (512*1024) +#define LZ4_BUFSIZE (512*1024u) -int compress_stream_lz4(int fdf, int fdt, off_t max_bytes) { +int compress_stream_lz4(int fdf, int fdt, uint64_t max_bytes) { #ifdef 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, offset = 0, frame_size; + struct stat st; + int r; + static const LZ4F_compressOptions_t options = { + .stableSrc = 1, + }; + static const LZ4F_preferences_t preferences = { + .frameInfo.blockSizeID = 5, + }; - _cleanup_free_ char *buf1 = NULL, *buf2 = NULL, *out = NULL; - char *buf; - LZ4_stream_t lz4_data = {}; - le32_t header; - size_t total_in = 0, total_out = sizeof(header); - ssize_t n; + c = LZ4F_createCompressionContext(&ctx, LZ4F_VERSION); + if (LZ4F_isError(c)) + return -ENOMEM; - assert(fdf >= 0); - assert(fdt >= 0); + if (fstat(fdf, &st) < 0) + return log_debug_errno(errno, "fstat() failed: %m"); - buf1 = malloc(LZ4_BUFSIZE); - buf2 = malloc(LZ4_BUFSIZE); - out = malloc(LZ4_COMPRESSBOUND(LZ4_BUFSIZE)); - if (!buf1 || !buf2 || !out) - return log_oom(); + frame_size = LZ4F_compressBound(LZ4_BUFSIZE, &preferences); + size = frame_size + 64*1024; /* add some space for header and trailer */ + buf = malloc(size); + if (!buf) + return -ENOMEM; - buf = buf1; - for (;;) { - size_t m; - int r; + n = offset = total_out = LZ4F_compressBegin(ctx, buf, size, &preferences); + if (LZ4F_isError(n)) + return -EINVAL; - m = LZ4_BUFSIZE; - if (max_bytes != -1 && m > (size_t) max_bytes - total_in) - m = max_bytes - total_in; + src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdf, 0); + if (src == MAP_FAILED) + return -errno; - n = read(fdf, buf, m); - if (n < 0) - return -errno; - if (n == 0) - break; + log_debug("Buffer size is %zu bytes, header size %zu bytes.", size, n); - total_in += n; + while (total_in < (size_t) st.st_size) { + ssize_t k; - r = LZ4_compress_continue(&lz4_data, buf, out, n); - if (r == 0) { - log_error("LZ4 compression failed."); - return -EBADMSG; + k = MIN(LZ4_BUFSIZE, st.st_size - total_in); + n = LZ4F_compressUpdate(ctx, buf + offset, size - offset, + src + total_in, k, &options); + if (LZ4F_isError(n)) { + r = -ENOTRECOVERABLE; + goto cleanup; } - header = htole32(r); - errno = 0; + total_in += k; + offset += n; + total_out += n; - n = write(fdt, &header, sizeof(header)); - if (n < 0) - return -errno; - if (n != sizeof(header)) - return errno ? -errno : -EIO; - - n = loop_write(fdt, out, r, false); - if (n < 0) - return n; + if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { + log_debug("Compressed stream longer than %"PRIu64" bytes", max_bytes); + return -EFBIG; + } - total_out += sizeof(header) + r; + if (size - offset < frame_size + 4) { + k = loop_write(fdt, buf, offset, false); + if (k < 0) { + r = k; + goto cleanup; + } + offset = 0; + } + } - buf = buf == buf1 ? buf2 : buf1; + n = LZ4F_compressEnd(ctx, buf + offset, size - offset, &options); + if (LZ4F_isError(n)) { + r = -ENOTRECOVERABLE; + goto cleanup; } - header = htole32(0); - n = write(fdt, &header, sizeof(header)); - if (n < 0) - return -errno; - if (n != sizeof(header)) - return errno ? -errno : -EIO; + offset += n; + total_out += n; + r = loop_write(fdt, buf, offset, false); + if (r < 0) + goto cleanup; log_debug("LZ4 compression finished (%zu -> %zu bytes, %.1f%%)", total_in, total_out, (double) total_out / total_in * 100); - - return 0; + cleanup: + munmap(src, st.st_size); + return r; #else return -EPROTONOSUPPORT; #endif } -int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) { +int decompress_stream_xz(int fdf, int fdt, uint64_t max_bytes) { #ifdef HAVE_XZ _cleanup_(lzma_end) lzma_stream s = LZMA_STREAM_INIT; @@ -511,7 +550,7 @@ int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) { ret = lzma_stream_decoder(&s, UINT64_MAX, 0); if (ret != LZMA_OK) { - log_error("Failed to initialize XZ decoder: code %u", ret); + log_debug("Failed to initialize XZ decoder: code %u", ret); return -ENOMEM; } @@ -537,7 +576,7 @@ int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) { ret = lzma_code(&s, action); if (ret != LZMA_OK && ret != LZMA_STREAM_END) { - log_error("Decompression failed: code %u", ret); + log_debug("Decompression failed: code %u", ret); return -EBADMSG; } @@ -546,8 +585,8 @@ int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) { n = sizeof(out) - s.avail_out; - if (max_bytes != -1) { - if (max_bytes < n) + if (max_bytes != (uint64_t) -1) { + if (max_bytes < (uint64_t) n) return -EFBIG; max_bytes -= n; @@ -567,87 +606,73 @@ int decompress_stream_xz(int fdf, int fdt, off_t max_bytes) { } } #else - log_error("Cannot decompress file. Compiled without XZ support."); + log_debug("Cannot decompress file. Compiled without XZ support."); return -EPROTONOSUPPORT; #endif } -int decompress_stream_lz4(int fdf, int fdt, off_t max_bytes) { - +int decompress_stream_lz4(int in, int out, uint64_t max_bytes) { #ifdef HAVE_LZ4 - _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); + size_t c; + _cleanup_(LZ4F_freeDecompressionContextp) LZ4F_decompressionContext_t ctx = NULL; + _cleanup_free_ char *buf = NULL; + char *src; + struct stat st; + int r = 0; + size_t total_in = 0, total_out = 0; + + c = LZ4F_createDecompressionContext(&ctx, LZ4F_VERSION); + if (LZ4F_isError(c)) + return -ENOMEM; - out = malloc(4*LZ4_BUFSIZE); - if (!out) - return log_oom(); + if (fstat(in, &st) < 0) + return log_debug_errno(errno, "fstat() failed: %m"); - for (;;) { - ssize_t m; - int r; + buf = malloc(LZ4_BUFSIZE); + if (!buf) + return -ENOMEM; - r = loop_read_exact(fdf, &header, sizeof(header), false); - if (r < 0) - return r; + src = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, in, 0); + if (src == MAP_FAILED) + return -errno; - m = le32toh(header); - if (m == 0) - break; + while (total_in < (size_t) st.st_size) { + size_t produced = LZ4_BUFSIZE; + size_t used = st.st_size - total_in; - /* 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_error("Compressed stream block too big: %zd bytes", m); - return -EBADMSG; + c = LZ4F_decompress(ctx, buf, &produced, src + total_in, &used, NULL); + if (LZ4F_isError(c)) { + r = -EBADMSG; + goto cleanup; } - total_in += sizeof(header) + m; - - if (!GREEDY_REALLOC(buf, buf_size, m)) - return log_oom(); - - 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_error("LZ4 decompression failed."); + total_in += used; + total_out += produced; - total_out += r; - - if (max_bytes != -1 && total_out > (size_t) max_bytes) { - log_debug("Decompressed stream longer than %zd bytes", max_bytes); - return -EFBIG; + if (max_bytes != (uint64_t) -1 && total_out > (size_t) max_bytes) { + log_debug("Decompressed stream longer than %"PRIu64" bytes", max_bytes); + r = -EFBIG; + goto cleanup; } - r = loop_write(fdt, out, r, false); + r = loop_write(out, buf, produced, false); if (r < 0) - return r; + goto cleanup; } log_debug("LZ4 decompression finished (%zu -> %zu bytes, %.1f%%)", total_in, total_out, (double) total_out / total_in * 100); - - return 0; + cleanup: + munmap(src, st.st_size); + return r; #else - log_error("Cannot decompress file. Compiled without LZ4 support."); + log_debug("Cannot decompress file. Compiled without LZ4 support."); return -EPROTONOSUPPORT; #endif } -int decompress_stream(const char *filename, int fdf, int fdt, off_t max_bytes) { +int decompress_stream(const char *filename, int fdf, int fdt, uint64_t max_bytes) { if (endswith(filename, ".lz4")) return decompress_stream_lz4(fdf, fdt, max_bytes); |