diff --git a/README b/README index 3461d9d..61eb953 100644 --- a/README +++ b/README @@ -70,6 +70,8 @@ are building: zlib (statically linked) - if your kernel uses gzipped modules + xz (statically linked) - if your kernel uses xz-compressed modules + Building and installing ----------------------- diff --git a/configure.ac b/configure.ac index b3144a4..8e3d64d 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,39 @@ if test "x$zlib_static" = "xyes"; then AC_SUBST([ZLIB_STATIC_LIBS]) fi +dnl Support for xzed kernel modules. +AC_CHECK_HEADER([lzma.h],[ + AC_CHECK_LIB([lzma],[lzma_code],[ + AC_MSG_CHECKING([for xz static library]) + old_CFLAGS="$CFLAGS" + old_LDFLAGS="$LDFLAGS" + old_LIBS="$LIBS" + CFLAGS="$CFLAGS -static" + LDFLAGS="$LDFLAGS -static" + LIBS="$LIBS -llzma" + AC_LINK_IFELSE([ + #include + #include + #include + int main () { lzma_stream s = LZMA_STREAM_INIT; + exit (&s == NULL ? 1 : 0); } + ],[ + lzma_static=yes + LZMA_STATIC_LIBS="-llzma" + AC_MSG_RESULT([yes]) + ],[ + AC_MSG_RESULT([no]) + ]) + CFLAGS="$old_CFLAGS" + LDFLAGS="$old_LDFLAGS" + LIBS="$old_LIBS" + ]) +]) +if test "x$lzma_static" = "xyes"; then + AC_DEFINE([HAVE_LZMA],[1],[Define if you have static lzma]) + AC_SUBST([LZMA_STATIC_LIBS]) +fi + dnl mke2fs. AC_PATH_PROG([MKE2FS],[mke2fs],[no], [$PATH$PATH_SEPARATOR/sbin$PATH_SEPARATOR]) diff --git a/helper/init.c b/helper/init.c index 4c10bff..7f096ec 100644 --- a/helper/init.c +++ b/helper/init.c @@ -45,6 +45,10 @@ #include #endif +#ifdef HAVE_LZMA +#include +#endif + /* Maximum time to wait for the root device to appear (seconds). * * On slow machines with lots of disks (Koji running the 255 disk test @@ -100,6 +104,9 @@ main () #ifdef HAVE_ZLIB " zlib" #endif +#ifdef HAVE_LZMA + " xz" +#endif "\n"); read_cmdline (); @@ -260,6 +267,20 @@ main () exit (EXIT_FAILURE); } +#if HAVE_LZMA +static int +ends_with (const char *str, const char *suffix) +{ + if (!str || !suffix) + return 0; + size_t lenstr = strlen (str); + size_t lensuffix = strlen (suffix); + if (lensuffix > lenstr) + return 0; + return strncmp (str + lenstr - lensuffix, suffix, lensuffix) == 0; +} +#endif + static void insmod (const char *filename) { @@ -269,15 +290,81 @@ insmod (const char *filename) fprintf (stderr, "supermin: internal insmod %s\n", filename); #ifdef HAVE_ZLIB - gzFile gzfp = gzopen (filename, "rb"); int capacity = 64*1024; char *buf = (char *) malloc (capacity); int tmpsize = 8 * 1024; char tmp[tmpsize]; int num; + errno = 0; size = 0; +#ifdef HAVE_LZMA + if (ends_with(filename, ".xz")) { + lzma_stream strm = LZMA_STREAM_INIT; + lzma_ret ret = lzma_stream_decoder(&strm, UINT64_MAX, + LZMA_CONCATENATED); + if (verbose) + fprintf (stderr, "supermin: running xz\n"); + FILE *fd = fopen (filename, "r"); + if (!fd) { + perror("popen failed"); + exit (EXIT_FAILURE); + } + char tmp_out[tmpsize]; + strm.avail_in = 0; + strm.next_out = tmp_out; + strm.avail_out = tmpsize; + + lzma_action action = LZMA_RUN; + + while (1) { + if (strm.avail_in == 0) { + strm.next_in = tmp; + strm.avail_in = fread(tmp, 1, tmpsize, fd); + + if (ferror(fd)) { + // POSIX says that fread() sets errno if + // an error occurred. ferror() doesn't + // touch errno. + perror("Error reading input file"); + exit (EXIT_FAILURE); + } + if (feof(fd)) action = LZMA_FINISH; + } + + ret = lzma_code(&strm, action); + + // Write and check write error before checking decoder error. + // This way as much data as possible gets written to output + // even if decoder detected an error. + if (strm.avail_out == 0 || ret != LZMA_OK) { + const size_t num = tmpsize - strm.avail_out; + if (num > capacity) { + buf = (char*) realloc (buf, size*2); + capacity = size; + } + memcpy (buf+size, tmp_out, num); + capacity -= num; + size += num; + strm.next_out = tmp_out; + strm.avail_out = tmpsize; + } + if (ret != LZMA_OK) { + if (ret == LZMA_STREAM_END) { + break; + } else { + perror("internal error"); + exit(EXIT_FAILURE); + } + } + } + fclose (fd); + if (verbose) + fprintf (stderr, "done with xz %d read\n", size); + } else { +#endif + gzFile gzfp = gzopen (filename, "rb"); if (gzfp == NULL) { fprintf (stderr, "insmod: gzopen failed: %s", filename); exit (EXIT_FAILURE); @@ -296,6 +383,10 @@ insmod (const char *filename) exit (EXIT_FAILURE); } gzclose (gzfp); +#ifdef HAVE_LZMA +} +#endif + #else int fd = open (filename, O_RDONLY); if (fd == -1) { --- a/helper/Makefile.am 2013-07-29 18:46:23.000000000 +0800 +++ b/helper/Makefile.am 2013-08-21 18:32:37.250303040 +0800 @@ -43,7 +43,7 @@ init_SOURCES = init.c init_CFLAGS = -static init_LDFLAGS = -static -init_LDADD = $(ZLIB_STATIC_LIBS) +init_LDADD = $(ZLIB_STATIC_LIBS) $(LZMA_STATIC_LIBS) CLEANFILES = ext2init.S