]> git.ipfire.org Git - thirdparty/kmod.git/commitdiff
Support for loading Xz-compressed modules
authorJan Engelhardt <jengelh@medozas.de>
Sat, 24 Dec 2011 13:58:30 +0000 (14:58 +0100)
committerJan Engelhardt <jengelh@medozas.de>
Sat, 24 Dec 2011 19:26:22 +0000 (20:26 +0100)
Makefile.am
configure.ac
libkmod/libkmod-file.c
libkmod/libkmod.pc.in

index 109638c5733a72c56fd35b20419ee10539bd7f8b..9cee395624e03b1ed8b15784a1ff7cb82581b3bc 100644 (file)
@@ -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
index 46b535ddcd755497158d5e28353c706236ac74be..ae1a6362e4c8c21b90d369acfbc6eb707f4d64ba 100644 (file)
@@ -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}
 ])
index 99ff3f2abe9c10e428de60bfb09c5ea9e46c3aab..1f5d35d63487492b7535de6e4420149947d3804d 100644 (file)
@@ -18,6 +18,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <errno.h>
 #include "libkmod.h"
 #include "libkmod-private.h"
 
+#ifdef ENABLE_XZ
+#include <lzma.h>
+#endif
 #ifdef ENABLE_ZLIB
 #include <zlib.h>
 #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);
index 202ebe93df89579a12219cddf02bcd4dc12a041a..d5d308141d1d236df2fb6b0906d3cab6f9b151f9 100644 (file)
@@ -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@