[libvirt] [PATCH sandbox 1/3] Support lzma and gzip compressed kernel modules

Daniel P. Berrange berrange at redhat.com
Tue Jun 16 11:25:42 UTC 2015


Modern distros like Fedora have started to compress their
kernel module files, so we can't simply read the file contents
and load the module. We have to first do a decompression step,
as the kernel won't do that itself.  While Fedora uses lzma,
upstream kernels are also capable of using gzip.

This links in the lzma and gzip libraries to handle
decompression. NB the static versions of lzma/gzip
are required since libvirt-sandbox-init-qemu must be
statically linked.
---
 configure.ac                                     |   4 +
 libvirt-sandbox.spec.in                          |   2 +
 libvirt-sandbox/Makefile.am                      |   7 ++
 libvirt-sandbox/libvirt-sandbox-builder-initrd.c |  35 +++---
 libvirt-sandbox/libvirt-sandbox-init-qemu.c      | 143 ++++++++++++++++++++++-
 5 files changed, 173 insertions(+), 18 deletions(-)

diff --git a/configure.ac b/configure.ac
index cd3745a..4f53c94 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,6 +17,8 @@ LIBVIRT_GCONFIG_REQUIRED=0.1.8
 LIBVIRT_GLIB_REQUIRED=0.1.7
 LIBVIRT_GOBJECT_REQUIRED=0.1.7
 GOBJECT_INTROSPECTION_REQUIRED=0.10.8
+LZMA_REQUIRED=5.0.0
+ZLIB_REQUIRED=1.2.0
 
 LIBVIRT_SANDBOX_MAJOR_VERSION=`echo $VERSION | awk -F. '{print $1}'`
 LIBVIRT_SANDBOX_MINOR_VERSION=`echo $VERSION | awk -F. '{print $2}'`
@@ -78,6 +80,8 @@ PKG_CHECK_MODULES(LIBVIRT, libvirt >= $LIBVIRT_REQUIRED)
 PKG_CHECK_MODULES(LIBVIRT_GLIB, libvirt-glib-1.0 >= $LIBVIRT_GOBJECT_REQUIRED)
 PKG_CHECK_MODULES(LIBVIRT_GOBJECT, libvirt-gobject-1.0 >= $LIBVIRT_GOBJECT_REQUIRED)
 PKG_CHECK_MODULES(LIBVIRT_GCONFIG, libvirt-gconfig-1.0 >= $LIBVIRT_GCONFIG_REQUIRED)
+PKG_CHECK_MODULES(ZLIB, zlib >= $ZLIB_REQUIRED)
+PKG_CHECK_MODULES(LZMA, liblzma >= $LZMA_REQUIRED)
 
 LIBVIRT_SANDBOX_CAPNG
 LIBVIRT_SANDBOX_GETTEXT
diff --git a/libvirt-sandbox.spec.in b/libvirt-sandbox.spec.in
index 7deadb2..1ec6e27 100644
--- a/libvirt-sandbox.spec.in
+++ b/libvirt-sandbox.spec.in
@@ -27,6 +27,8 @@ BuildRequires: /usr/bin/pod2man
 BuildRequires: intltool
 BuildRequires: libselinux-devel
 BuildRequires: glib2-devel >= 2.32.0
+BuildRequires: xz-devel >= 5.0.0, xz-static
+BuildRequires: zlib-devel >= 1.2.0, zlib-static
 Requires: rpm-python
 # For virsh lxc-enter-namespace command
 Requires: libvirt-client >= %{libvirt_version}
diff --git a/libvirt-sandbox/Makefile.am b/libvirt-sandbox/Makefile.am
index 96302cb..30c9ebf 100644
--- a/libvirt-sandbox/Makefile.am
+++ b/libvirt-sandbox/Makefile.am
@@ -139,6 +139,7 @@ libvirt_sandbox_1_0_la_CFLAGS = \
 			-DLOCALEDIR="\"$(datadir)/locale"\" \
 			$(COVERAGE_CFLAGS) \
 			-I$(top_srcdir) \
+			-I$(top_builddir) \
 			$(GIO_UNIX_CFLAGS) \
 			$(LIBVIRT_GLIB_CFLAGS) \
 			$(LIBVIRT_GOBJECT_CFLAGS) \
@@ -172,6 +173,7 @@ libvirt_sandbox_init_common_CFLAGS = \
 			-DLOCALEDIR="\"$(datadir)/locale"\" \
 			$(COVERAGE_CFLAGS) \
 			-I$(top_srcdir) \
+			-I$(top_builddir) \
 			$(GIO_UNIX_CFLAGS) \
 			$(LIBVIRT_GLIB_CFLAGS) \
 			$(LIBVIRT_GOBJECT_CFLAGS) \
@@ -196,6 +198,7 @@ libvirt_sandbox_init_lxc_CFLAGS = \
 			-DLIBEXECDIR="\"$(libexecdir)\"" \
 			-DSANDBOXCONFIGDIR="\"$(sandboxconfigdir)\"" \
 			-I$(top_srcdir) \
+			-I$(top_builddir) \
 			$(GIO_UNIX_CFLAGS) \
 			$(LIBVIRT_GLIB_CFLAGS) \
 			$(LIBVIRT_GOBJECT_CFLAGS) \
@@ -217,11 +220,15 @@ libvirt_sandbox_init_qemu_SOURCES = libvirt-sandbox-init-qemu.c
 libvirt_sandbox_init_qemu_CFLAGS = \
 			-DLIBEXECDIR="\"$(libexecdir)\"" \
 			-DSANDBOXCONFIGDIR="\"$(sandboxconfigdir)\"" \
+			$(ZLIB_CFLAGS) \
+			$(LZMA_CFLAGS) \
 			$(WARN_CFLAGS) \
 			$(NULL)
 libvirt_sandbox_init_qemu_LDFLAGS = \
 			-all-static \
 			$(COVERAGE_CFLAGS:-f%=-Wc,f%) \
+			$(ZLIB_LIBS) \
+			$(LZMA_LIBS) \
 			$(WARN_CFLAGS) \
 			$(NULL)
 
diff --git a/libvirt-sandbox/libvirt-sandbox-builder-initrd.c b/libvirt-sandbox/libvirt-sandbox-builder-initrd.c
index 95f05e2..59a03e6 100644
--- a/libvirt-sandbox/libvirt-sandbox-builder-initrd.c
+++ b/libvirt-sandbox/libvirt-sandbox-builder-initrd.c
@@ -232,7 +232,7 @@ static GList *gvir_sandbox_builder_initrd_find_files(GList *modnames,
         if (strstr(thisname, ".ko")) {
             GList *tmp = modnames;
             while (tmp) {
-                if (g_str_equal(thisname, tmp->data)) {
+                if (g_str_has_prefix(thisname, tmp->data)) {
                     modfiles = g_list_append(modfiles, child);
                     child = NULL;
                 }
@@ -293,7 +293,7 @@ static GList *gvir_sandbox_builder_initrd_find_files(GList *modnames,
         if (strstr(de->d_name, ".ko")) {
             GList *tmp = modnames;
             while (tmp) {
-                if (g_str_equal(de->d_name, tmp->data)) {
+                if (g_str_has_prefix(de->d_name, tmp->data)) {
                     modfiles = g_list_append(modfiles, child);
                     child = NULL;
                 }
@@ -366,7 +366,6 @@ static gboolean gvir_sandbox_builder_initrd_populate_tmpdir(const gchar *tmpdir,
     GFile *modlist = NULL;
     gchar *modlistpath = NULL;
     GOutputStream *modlistos = NULL;
-    GError *e = NULL;
 
     if (!gvir_sandbox_builder_initrd_copy_file(
                                                gvir_sandbox_config_initrd_get_init(config),
@@ -374,11 +373,9 @@ static gboolean gvir_sandbox_builder_initrd_populate_tmpdir(const gchar *tmpdir,
         return FALSE;
 
     modnames = gvir_sandbox_config_initrd_get_modules(config);
-    modfiles = gvir_sandbox_builder_initrd_find_modules(modnames, config, &e);
-    if (e) {
-        g_propagate_error(error, e);
+    modfiles = gvir_sandbox_builder_initrd_find_modules(modnames, config, error);
+    if (*error)
         goto cleanup;
-    }
 
     tmp = modfiles;
     while (tmp) {
@@ -404,14 +401,22 @@ static gboolean gvir_sandbox_builder_initrd_populate_tmpdir(const gchar *tmpdir,
 
     tmp = modnames;
     while (tmp) {
-        if (!g_output_stream_write_all(modlistos,
-                                       tmp->data, strlen(tmp->data),
-                                       NULL, NULL, error))
-            goto cleanup;
-        if (!g_output_stream_write_all(modlistos,
-                                       "\n", 1,
-                                       NULL, NULL, error))
-            goto cleanup;
+        GList *files = modfiles;
+        while (files) {
+            const gchar *basename = g_file_get_basename(files->data);
+            if (g_str_has_prefix(basename, tmp->data)) {
+                if (!g_output_stream_write_all(modlistos,
+                                               basename, strlen(basename),
+                                               NULL, NULL, error))
+                    goto cleanup;
+                if (!g_output_stream_write_all(modlistos,
+                                               "\n", 1,
+                                               NULL, NULL, error))
+                    goto cleanup;
+                break;
+            }
+            files = files->next;
+        }
         tmp = tmp->next;
     }
 
diff --git a/libvirt-sandbox/libvirt-sandbox-init-qemu.c b/libvirt-sandbox/libvirt-sandbox-init-qemu.c
index 750c9de..2c2c803 100644
--- a/libvirt-sandbox/libvirt-sandbox-init-qemu.c
+++ b/libvirt-sandbox/libvirt-sandbox-init-qemu.c
@@ -42,6 +42,8 @@
 #include <fcntl.h>
 #include <sys/reboot.h>
 #include <termios.h>
+#include <lzma.h>
+#include <zlib.h>
 
 #define ATTR_UNUSED __attribute__((__unused__))
 
@@ -481,15 +483,150 @@ static char *readall(const char *filename, size_t *len)
     return data;
 }
 
+
+static int
+has_suffix(const char *filename, const char *ext)
+{
+    char *offset = strstr(filename, ext);
+    return  (offset &&
+             offset[strlen(ext)] == '\0');
+}
+
+static char *
+load_module_file_lzma(const char *filename, size_t *len)
+{
+    lzma_stream st = LZMA_STREAM_INIT;
+    char *xzdata;
+    size_t xzlen;
+    char *data;
+    lzma_ret ret;
+
+    *len = 0;
+
+    if (debug)
+        fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s\n", __func__, filename);
+
+    if ((ret = lzma_stream_decoder(&st, UINT64_MAX,
+                                   LZMA_CONCATENATED)) != LZMA_OK) {
+        fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s: lzma init failure: %d\n",
+                __func__, filename, ret);
+        exit_poweroff();
+    }
+    xzdata = readall(filename, &xzlen);
+
+    st.next_in = (unsigned char *)xzdata;
+    st.avail_in = xzlen;
+
+    *len = 32 * 1024;
+    if (!(data = malloc(*len))) {
+        fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s\n", __func__, strerror(errno));
+        exit_poweroff();
+    }
+
+    st.next_out = (unsigned char *)data;
+    st.avail_out = *len;
+
+    do {
+        ret = lzma_code(&st, LZMA_FINISH);
+        if (st.avail_out == 0) {
+            size_t used = *len;
+            *len += (32 * 1024);
+            if (!(data = realloc(data, *len))) {
+                fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s\n", __func__, strerror(errno));
+                exit_poweroff();
+            }
+            st.next_out = (unsigned char *)data + used;
+            st.avail_out = *len - used;
+        }
+        if (ret != LZMA_OK && ret != LZMA_STREAM_END) {
+            fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s: lzma decode failure: %d\n",
+                    __func__, filename, ret);
+            exit_poweroff();
+        }
+    } while (ret != LZMA_STREAM_END);
+    lzma_end(&st);
+    free(xzdata);
+    return data;
+}
+
+static char *
+load_module_file_zlib(const char *filename, size_t *len)
+{
+    gzFile fp;
+    char *data;
+    unsigned int avail;
+    size_t total;
+    int got;
+
+    if (debug)
+        fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s\n", __func__, filename);
+
+    if (!(fp = gzopen(filename, "rb"))) {
+        fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s: gzopen failure\n",
+                __func__, filename);
+        exit_poweroff();
+    }
+
+    *len = 32 * 1024;
+    if (!(data = malloc(*len))) {
+        fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s\n", __func__, strerror(errno));
+        exit_poweroff();
+    }
+
+    avail = *len;
+    total = 0;
+
+    do {
+        got = gzread(fp, data + total, avail);
+
+        if (got < 0) {
+            fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s: gzread failure\n",
+                __func__, filename);
+            exit_poweroff();
+        }
+
+        total += got;
+        if (total >= *len) {
+            *len += (32 * 1024);
+            if (!(data = realloc(data, *len))) {
+                fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s\n", __func__, strerror(errno));
+                exit_poweroff();
+            }
+            avail = *len - total;
+        }
+    } while (got > 0);
+
+    gzclose(fp);
+    return data;
+}
+
+static char *
+load_module_file_raw(const char *filename, size_t *len)
+{
+    if (debug)
+        fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s\n", __func__, filename);
+    return readall(filename, len);
+}
+
+static char *
+load_module_file(const char *filename, size_t *len)
+{
+    if (has_suffix(filename, ".ko.xz"))
+        return load_module_file_lzma(filename, len);
+    else if (has_suffix(filename, ".ko.gz"))
+        return load_module_file_zlib(filename, len);
+    else
+        return load_module_file_raw(filename, len);
+}
+
+
 static void
 insmod(const char *filename)
 {
     char *data;
     size_t len;
-    if (debug)
-        fprintf(stderr, "libvirt-sandbox-init-qemu: %s: %s\n", __func__, filename);
 
-    data = readall(filename, &len);
+    data = load_module_file(filename, &len);
 
     if (init_module(data, (unsigned long)len, "") < 0) {
         const char *msg;
-- 
2.4.2




More information about the libvir-list mailing list