Support for loading Xz-compressed modules

This commit is contained in:
Jan Engelhardt 2011-12-24 14:58:30 +01:00
parent 5a51a357a8
commit b182f8fb5e
4 changed files with 148 additions and 4 deletions

View 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

View 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}
])

View 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>
@ -30,11 +31,17 @@
#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);

View 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@