From b182f8fb5e5765dd5c935aa7b3775e382662b318 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Sat, 24 Dec 2011 14:58:30 +0100 Subject: [PATCH] Support for loading Xz-compressed modules --- Makefile.am | 4 +- configure.ac | 12 ++++- libkmod/libkmod-file.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++ libkmod/libkmod.pc.in | 4 +- 4 files changed, 148 insertions(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index 109638c..9cee395 100644 --- a/Makefile.am +++ b/Makefile.am @@ -27,6 +27,8 @@ SED_PROCESS = \ -e 's,@exec_prefix\@,$(exec_prefix),g' \ -e 's,@libdir\@,$(libdir),g' \ -e 's,@includedir\@,$(includedir),g' \ + -e 's,@liblzma_CFLAGS\@,${liblzma_CFLAGS},g' \ + -e 's,@liblzma_LIBS\@,${liblzma_LIBS},g' \ -e 's,@zlib_CFLAGS\@,${zlib_CFLAGS},g' \ -e 's,@zlib_LIBS\@,${zlib_LIBS},g' \ < $< > $@ || rm $@ @@ -63,7 +65,7 @@ libkmod_libkmod_la_LDFLAGS = $(AM_LDFLAGS) \ -version-info $(LIBKMOD_CURRENT):$(LIBKMOD_REVISION):$(LIBKMOD_AGE) \ -Wl,--version-script=$(top_srcdir)/libkmod/libkmod.sym libkmod_libkmod_la_DEPENDENCIES = ${top_srcdir}/libkmod/libkmod.sym -libkmod_libkmod_la_LIBADD = ${zlib_LIBS} +libkmod_libkmod_la_LIBADD = ${liblzma_LIBS} ${zlib_LIBS} pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libkmod/libkmod.pc diff --git a/configure.ac b/configure.ac index 46b535d..ae1a636 100644 --- a/configure.ac +++ b/configure.ac @@ -51,6 +51,16 @@ AS_IF([test "x$enable_logging" = "xyes"], [ AC_DEFINE(ENABLE_LOGGING, [1], [System logging.]) ]) +AC_ARG_WITH([xz], + AS_HELP_STRING([--with-xz], [handle Xz-compressed modules @<:@default=disabled@:>@]), + [], [with_xz=no]) +AS_IF([test "x$with_xz" != "xno"], [ + PKG_CHECK_MODULES([liblzma], [liblzma >= 4.99]) + AC_DEFINE([ENABLE_XZ], [1], [Enable Xz for modules.]) +], [ + AC_MSG_NOTICE([Xz support not requested]) +]) + AC_ARG_WITH([zlib], AS_HELP_STRING([--with-zlib], [handle gzipped modules @<:@default=disabled@:>@]), [], [with_zlib=no]) @@ -139,6 +149,6 @@ AC_MSG_RESULT([ tools: ${enable_tools} logging: ${enable_logging} - zlib: ${with_zlib} + compression: xz=${with_xz} zlib=${with_zlib} debug: ${enable_debug} ]) diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c index 99ff3f2..1f5d35d 100644 --- a/libkmod/libkmod-file.c +++ b/libkmod/libkmod-file.c @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #include @@ -30,11 +31,17 @@ #include "libkmod.h" #include "libkmod-private.h" +#ifdef ENABLE_XZ +#include +#endif #ifdef ENABLE_ZLIB #include #endif struct kmod_file { +#ifdef ENABLE_XZ + bool xz_used; +#endif #ifdef ENABLE_ZLIB gzFile gzf; #endif @@ -43,6 +50,120 @@ struct kmod_file { void *memory; }; +#ifdef ENABLE_XZ +static bool xz_detect(struct kmod_file *file) +{ + ssize_t ret; + char buf[6]; + + ret = read(file->fd, buf, sizeof(buf)); + if (ret < 0 || lseek(file->fd, 0, SEEK_SET)) + return -errno; + return ret == sizeof(buf) && + memcmp(buf, "\xFD""7zXZ\x00", sizeof(buf)) == 0; +} + +static void xz_uncompress_belch(lzma_ret ret) +{ + switch (ret) { + case LZMA_MEM_ERROR: + fprintf(stderr, "xz: %s\n", strerror(ENOMEM)); + break; + case LZMA_FORMAT_ERROR: + fprintf(stderr, "xz: File format not recognized\n"); + break; + case LZMA_OPTIONS_ERROR: + fprintf(stderr, "xz: Unsupported compression options\n"); + break; + case LZMA_DATA_ERROR: + fprintf(stderr, "xz: File is corrupt\n"); + break; + case LZMA_BUF_ERROR: + fprintf(stderr, "xz: Unexpected end of input\n"); + break; + default: + fprintf(stderr, "xz: Internal error (bug)\n"); + break; + } +} + +static int xz_uncompress(lzma_stream *strm, struct kmod_file *file) +{ + uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ]; + lzma_action action = LZMA_RUN; + lzma_ret ret; + void *p = NULL; + size_t total = 0; + + strm->avail_in = 0; + strm->next_out = out_buf; + strm->avail_out = sizeof(out_buf); + + while (true) { + if (strm->avail_in == 0) { + ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf)); + if (rdret < 0) { + ret = -errno; + goto out; + } + strm->next_in = in_buf; + strm->avail_in = rdret; + if (rdret == 0) + action = LZMA_FINISH; + } + ret = lzma_code(strm, action); + if (strm->avail_out == 0 || ret != LZMA_OK) { + size_t write_size = BUFSIZ - strm->avail_out; + char *tmp = realloc(p, total + write_size); + if (tmp == NULL) { + ret = -errno; + goto out; + } + memcpy(tmp + total, out_buf, write_size); + total += write_size; + p = tmp; + strm->next_out = out_buf; + strm->avail_out = BUFSIZ; + } + if (ret == LZMA_STREAM_END) + break; + if (ret != LZMA_OK) { + xz_uncompress_belch(ret); + ret = -EINVAL; + goto out; + } + } + file->memory = p; + file->size = total; + return 0; + out: + free(p); + close(file->fd); + return ret; +} + +static int xz_file_open(struct kmod_file *file) +{ + lzma_stream strm = LZMA_STREAM_INIT; + lzma_ret lzret; + int ret; + + lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED); + if (lzret == LZMA_MEM_ERROR) { + fprintf(stderr, "xz: %s\n", strerror(ENOMEM)); + close(file->fd); + return -ENOMEM; + } else if (lzret != LZMA_OK) { + fprintf(stderr, "xz: Internal error (bug)\n"); + close(file->fd); + return -EINVAL; + } + ret = xz_uncompress(&strm, file); + lzma_end(&strm); + return ret; +} +#endif + #ifdef ENABLE_ZLIB #define READ_STEP (4 * 1024 * 1024) @@ -152,6 +273,11 @@ struct kmod_file *kmod_file_open(const char *filename) goto error; } +#ifdef ENABLE_XZ + if (xz_detect(file)) + err = xz_file_open(file); + else +#endif #ifdef ENABLE_ZLIB if (check_zlib(file)) err = zlib_file_open(file); @@ -181,6 +307,12 @@ off_t kmod_file_get_size(const struct kmod_file *file) void kmod_file_unref(struct kmod_file *file) { +#ifdef ENABLE_XZ + if (file->xz_used) { + free(file->memory); + close(file->fd); + } else +#endif #ifdef ENABLE_ZLIB if (file->gzf != NULL) { free(file->memory); diff --git a/libkmod/libkmod.pc.in b/libkmod/libkmod.pc.in index 202ebe9..d5d3081 100644 --- a/libkmod/libkmod.pc.in +++ b/libkmod/libkmod.pc.in @@ -7,5 +7,5 @@ Name: libkmod Description: Library to deal with kernel modules Version: @VERSION@ Libs: -L${libdir} -lkmod -Libs.private: @zlib_LIBS@ -Cflags: -I${includedir} @zlib_CFLAGS@ +Libs.private: @liblzma_LIBS@ @zlib_LIBS@ +Cflags: -I${includedir} @liblzma_CFLAGS@ @zlib_CFLAGS@ -- 2.7.4