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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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

--
libvir-list mailing list
libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list



[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]