diff --git a/Makefile.am b/Makefile.am index 443c04d..b107b1c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -84,6 +84,10 @@ if ENABLE_ZLIB libkmod_libkmod_la_SOURCES += libkmod/libkmod-file-zlib.c endif +if ENABLE_ZSTD +libkmod_libkmod_la_SOURCES += libkmod/libkmod-file-zstd.c +endif + EXTRA_DIST += libkmod/libkmod.sym EXTRA_DIST += libkmod/README \ libkmod/COPYING testsuite/COPYING tools/COPYING COPYING diff --git a/configure.ac b/configure.ac index 85fefe3..eb24ed0 100644 --- a/configure.ac +++ b/configure.ac @@ -125,6 +125,7 @@ AS_IF([test "x$with_zstd" != "xno"], [ AC_MSG_NOTICE([Zstandard support not requested]) ]) CC_FEATURE_APPEND([with_features], [with_zstd], [ZSTD]) +AM_CONDITIONAL([ENABLE_ZSTD], [test "x$with_zstd" != "xno"]) AC_ARG_WITH([xz], AS_HELP_STRING([--with-xz], [handle Xz-compressed modules @<:@default=disabled@:>@]), diff --git a/libkmod/libkmod-file-zstd.c b/libkmod/libkmod-file-zstd.c new file mode 100644 index 0000000..1aec638 --- /dev/null +++ b/libkmod/libkmod-file-zstd.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +/* + * Copyright © 2024 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "libkmod.h" +#include "libkmod-internal.h" +#include "libkmod-internal-file.h" + +static int zstd_read_block(struct kmod_file *file, size_t block_size, + ZSTD_inBuffer *input, size_t *input_capacity) +{ + ssize_t rdret; + int ret; + + if (*input_capacity < block_size) { + free((void *)input->src); + input->src = malloc(block_size); + if (input->src == NULL) { + ret = -errno; + ERR(file->ctx, "zstd: %m\n"); + return ret; + } + *input_capacity = block_size; + } + + rdret = read(file->fd, (void *)input->src, block_size); + if (rdret < 0) { + ret = -errno; + ERR(file->ctx, "zstd: %m\n"); + return ret; + } + + input->pos = 0; + input->size = rdret; + return 0; +} + +static int zstd_ensure_outbuffer_space(ZSTD_outBuffer *buffer, size_t min_free) +{ + uint8_t *old_buffer = buffer->dst; + int ret = 0; + + if (buffer->size - buffer->pos >= min_free) + return 0; + + if (buffer->size < min_free) + buffer->size = min_free; + else + buffer->size *= 2; + + buffer->size += min_free; + buffer->dst = realloc(buffer->dst, buffer->size); + if (buffer->dst == NULL) { + ret = -errno; + free(old_buffer); + } + + return ret; +} + +static int zstd_decompress_block(struct kmod_file *file, ZSTD_DStream *dstr, + ZSTD_inBuffer *input, ZSTD_outBuffer *output, + size_t *next_block_size) +{ + size_t out_buf_min_size = ZSTD_DStreamOutSize(); + int ret = 0; + + do { + ssize_t dsret; + + ret = zstd_ensure_outbuffer_space(output, out_buf_min_size); + if (ret) { + ERR(file->ctx, "zstd: %s\n", strerror(-ret)); + break; + } + + dsret = ZSTD_decompressStream(dstr, output, input); + if (ZSTD_isError(dsret)) { + ret = -EINVAL; + ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(dsret)); + break; + } + if (dsret > 0) + *next_block_size = (size_t)dsret; + } while (input->pos < input->size + || output->pos > output->size + || output->size - output->pos < out_buf_min_size); + + return ret; +} + +int kmod_file_load_zstd(struct kmod_file *file) +{ + ZSTD_DStream *dstr; + size_t next_block_size; + size_t zst_inb_capacity = 0; + ZSTD_inBuffer zst_inb = { 0 }; + ZSTD_outBuffer zst_outb = { 0 }; + int ret; + + dstr = ZSTD_createDStream(); + if (dstr == NULL) { + ret = -EINVAL; + ERR(file->ctx, "zstd: Failed to create decompression stream\n"); + goto out; + } + + next_block_size = ZSTD_initDStream(dstr); + + while (true) { + ret = zstd_read_block(file, next_block_size, &zst_inb, + &zst_inb_capacity); + if (ret != 0) + goto out; + if (zst_inb.size == 0) /* EOF */ + break; + + ret = zstd_decompress_block(file, dstr, &zst_inb, &zst_outb, + &next_block_size); + if (ret != 0) + goto out; + } + + ZSTD_freeDStream(dstr); + free((void *)zst_inb.src); + file->memory = zst_outb.dst; + file->size = zst_outb.pos; + return 0; +out: + if (dstr != NULL) + ZSTD_freeDStream(dstr); + free((void *)zst_inb.src); + free((void *)zst_outb.dst); + return ret; +} diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c index 140e9ca..4842f13 100644 --- a/libkmod/libkmod-file.c +++ b/libkmod/libkmod-file.c @@ -26,9 +26,6 @@ #include #include #include -#ifdef ENABLE_ZSTD -#include -#endif #include @@ -36,140 +33,6 @@ #include "libkmod-internal.h" #include "libkmod-internal-file.h" -#ifdef ENABLE_ZSTD -static int zstd_read_block(struct kmod_file *file, size_t block_size, - ZSTD_inBuffer *input, size_t *input_capacity) -{ - ssize_t rdret; - int ret; - - if (*input_capacity < block_size) { - free((void *)input->src); - input->src = malloc(block_size); - if (input->src == NULL) { - ret = -errno; - ERR(file->ctx, "zstd: %m\n"); - return ret; - } - *input_capacity = block_size; - } - - rdret = read(file->fd, (void *)input->src, block_size); - if (rdret < 0) { - ret = -errno; - ERR(file->ctx, "zstd: %m\n"); - return ret; - } - - input->pos = 0; - input->size = rdret; - return 0; -} - -static int zstd_ensure_outbuffer_space(ZSTD_outBuffer *buffer, size_t min_free) -{ - uint8_t *old_buffer = buffer->dst; - int ret = 0; - - if (buffer->size - buffer->pos >= min_free) - return 0; - - if (buffer->size < min_free) - buffer->size = min_free; - else - buffer->size *= 2; - - buffer->dst = realloc(buffer->dst, buffer->size); - if (buffer->dst == NULL) { - ret = -errno; - free(old_buffer); - } - - return ret; -} - -static int zstd_decompress_block(struct kmod_file *file, ZSTD_DStream *dstr, - ZSTD_inBuffer *input, ZSTD_outBuffer *output, - size_t *next_block_size) -{ - size_t out_buf_min_size = ZSTD_DStreamOutSize(); - int ret = 0; - - do { - ssize_t dsret; - - ret = zstd_ensure_outbuffer_space(output, out_buf_min_size); - if (ret) { - ERR(file->ctx, "zstd: %s\n", strerror(-ret)); - break; - } - - dsret = ZSTD_decompressStream(dstr, output, input); - if (ZSTD_isError(dsret)) { - ret = -EINVAL; - ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(dsret)); - break; - } - if (dsret > 0) - *next_block_size = (size_t)dsret; - } while (input->pos < input->size - || output->pos > output->size - || output->size - output->pos < out_buf_min_size); - - return ret; -} - -static int load_zstd(struct kmod_file *file) -{ - ZSTD_DStream *dstr; - size_t next_block_size; - size_t zst_inb_capacity = 0; - ZSTD_inBuffer zst_inb = { 0 }; - ZSTD_outBuffer zst_outb = { 0 }; - int ret; - - dstr = ZSTD_createDStream(); - if (dstr == NULL) { - ret = -EINVAL; - ERR(file->ctx, "zstd: Failed to create decompression stream\n"); - goto out; - } - - next_block_size = ZSTD_initDStream(dstr); - - while (true) { - ret = zstd_read_block(file, next_block_size, &zst_inb, - &zst_inb_capacity); - if (ret != 0) - goto out; - if (zst_inb.size == 0) /* EOF */ - break; - - ret = zstd_decompress_block(file, dstr, &zst_inb, &zst_outb, - &next_block_size); - if (ret != 0) - goto out; - } - - ZSTD_freeDStream(dstr); - free((void *)zst_inb.src); - file->memory = zst_outb.dst; - file->size = zst_outb.pos; - return 0; -out: - if (dstr != NULL) - ZSTD_freeDStream(dstr); - free((void *)zst_inb.src); - free((void *)zst_outb.dst); - return ret; -} -#else -static int load_zstd(struct kmod_file *file) -{ - return -ENOSYS; -} -#endif - static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD}; static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0}; static const char magic_zlib[] = {0x1f, 0x8b}; @@ -198,7 +61,7 @@ static const struct comp_type { const char *magic_bytes; int (*load)(struct kmod_file *file); } comp_types[] = { - {sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, load_zstd}, + {sizeof(magic_zstd), KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, kmod_file_load_zstd}, {sizeof(magic_xz), KMOD_FILE_COMPRESSION_XZ, magic_xz, kmod_file_load_xz}, {sizeof(magic_zlib), KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, kmod_file_load_zlib}, {0, KMOD_FILE_COMPRESSION_NONE, NULL, load_reg} diff --git a/libkmod/libkmod-internal-file.h b/libkmod/libkmod-internal-file.h index ca9f868..36cd327 100644 --- a/libkmod/libkmod-internal-file.h +++ b/libkmod/libkmod-internal-file.h @@ -32,3 +32,8 @@ int kmod_file_load_zlib(struct kmod_file *file); static inline int kmod_file_load_zlib(struct kmod_file *file) { return -ENOSYS; } #endif +#ifdef ENABLE_ZSTD +int kmod_file_load_zstd(struct kmod_file *file); +#else +static inline int kmod_file_load_zstd(struct kmod_file *file) { return -ENOSYS; } +#endif