From 1cdea92b11ae260a8223f08e1e71e7487906f2f2 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Mon, 6 May 2019 12:02:47 +0200 Subject: Add lz4 compressor implementation Signed-off-by: David Oberhollenzer --- README | 2 +- configure.ac | 17 ++++++++ lib/Makemodule.am | 7 ++++ lib/comp/compressor.c | 56 ++++++++++++++++++++++++++ lib/comp/internal.h | 6 +++ lib/comp/lz4.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++ mkfs/Makemodule.am | 4 ++ unpack/Makemodule.am | 4 ++ 8 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 lib/comp/lz4.c diff --git a/README b/README index a953acf..c81fe46 100644 --- a/README +++ b/README @@ -83,5 +83,5 @@ At the moment, the following things are still missing: - hard links - NFS export tables - compressor options - - compressors other than XZ and GZIP (i.e. lz4, zstd, *maybe* lzma1?) + - zstd and *maybe* lzma1 compressors - support for extracting SquashFS < 4.0 diff --git a/configure.ac b/configure.ac index 422a113..b138c6f 100644 --- a/configure.ac +++ b/configure.ac @@ -71,15 +71,27 @@ AC_ARG_WITH([lzo], esac], [AM_CONDITIONAL([WITH_LZO], [true])]) +AC_ARG_WITH([lz4], + [AS_HELP_STRING([--without-lz4], + [Build without lz4 compression support])], + [case "${withval}" in + yes) AM_CONDITIONAL([WITH_LZ4], [true]) ;; + no) AM_CONDITIONAL([WITH_LZ4], [false]) ;; + *) AC_MSG_ERROR([bad value ${withval} for --without-lz4]) ;; + esac], + [AM_CONDITIONAL([WITH_LZ4], [true])]) + ##### search for dependencies ##### need_zlib="no" need_xz="no" need_lzo="no" +need_lz4="no" AM_COND_IF([WITH_GZIP], [need_zlib="yes"]) AM_COND_IF([WITH_XZ], [need_xz="yes"]) AM_COND_IF([WITH_LZO], [need_lzo="yes"]) +AM_COND_IF([WITH_LZ4], [need_lz4="yes"]) if test "x$need_zlib" = "xyes"; then PKG_CHECK_MODULES(ZLIB, [zlib], [], [AC_MSG_ERROR([cannot find zlib])]) @@ -100,6 +112,11 @@ if test "x$need_lzo" = "xyes"; then ) fi +if test "x$need_lz4" = "xyes"; then + PKG_CHECK_MODULES(LZ4, [liblz4], [], + [AC_MSG_ERROR([cannot lz4 library])]) +fi + ##### generate output ##### AC_CONFIG_HEADERS([config.h]) diff --git a/lib/Makemodule.am b/lib/Makemodule.am index aacec34..17004bc 100644 --- a/lib/Makemodule.am +++ b/lib/Makemodule.am @@ -42,4 +42,11 @@ libcompress_a_CFLAGS += $(LZO_CFLAGS) libcompress_a_CPPFLAGS += -DWITH_LZO endif +if WITH_LZ4 +libcompress_a_SOURCES += lib/comp/lz4.c + +libcompress_a_CFLAGS += $(LZ4_CFLAGS) +libcompress_a_CPPFLAGS += -DWITH_LZ4 +endif + noinst_LIBRARIES += libfstree.a libcompress.a libutil.a libsquashfs.a diff --git a/lib/comp/compressor.c b/lib/comp/compressor.c index 42ebd8b..3b473a3 100644 --- a/lib/comp/compressor.c +++ b/lib/comp/compressor.c @@ -1,7 +1,9 @@ /* SPDX-License-Identifier: GPL-3.0-or-later */ #include +#include #include "internal.h" +#include "util.h" typedef compressor_t *(*compressor_fun_t)(bool compress, size_t block_size); @@ -15,8 +17,62 @@ static compressor_fun_t compressors[SQFS_COMP_MAX + 1] = { #ifdef WITH_LZO [SQFS_COMP_LZO] = create_lzo_compressor, #endif +#ifdef WITH_LZ4 + [SQFS_COMP_LZ4] = create_lz4_compressor, +#endif }; +int generic_write_options(int fd, const void *data, size_t size) +{ + uint8_t buffer[size + 2]; + ssize_t ret; + + *((uint16_t *)buffer) = htole16(0x8000 | size); + memcpy(buffer + 2, data, size); + + ret = write_retry(fd, buffer, sizeof(buffer)); + + if (ret < 0) { + perror("writing compressor options"); + return -1; + } + + if ((size_t)ret < sizeof(buffer)) { + fputs("writing compressor options: truncated write\n", stderr); + return -1; + } + + return ret; +} + +int generic_read_options(int fd, void *data, size_t size) +{ + uint8_t buffer[size + 2]; + ssize_t ret; + + ret = read_retry(fd, buffer, sizeof(buffer)); + + if (ret < 0) { + perror("reading compressor options"); + return -1; + } + + if ((size_t)ret < sizeof(buffer)) { + fputs("reading compressor options: unexpected end of file\n", + stderr); + return -1; + } + + if (le16toh(*((uint16_t *)buffer)) != (0x8000 | size)) { + fputs("reading compressor options: invalid meta data header\n", + stderr); + return -1; + } + + memcpy(data, buffer + 2, size); + return 0; +} + bool compressor_exists(E_SQFS_COMPRESSOR id) { if (id < SQFS_COMP_MIN || id > SQFS_COMP_MAX) diff --git a/lib/comp/internal.h b/lib/comp/internal.h index 3b24af0..863f82b 100644 --- a/lib/comp/internal.h +++ b/lib/comp/internal.h @@ -4,10 +4,16 @@ #include "compress.h" +int generic_write_options(int fd, const void *data, size_t size); + +int generic_read_options(int fd, void *data, size_t size); + compressor_t *create_xz_compressor(bool compress, size_t block_size); compressor_t *create_gzip_compressor(bool compress, size_t block_size); compressor_t *create_lzo_compressor(bool compress, size_t block_size); +compressor_t *create_lz4_compressor(bool compress, size_t block_size); + #endif /* INTERNAL_H */ diff --git a/lib/comp/lz4.c b/lib/comp/lz4.c new file mode 100644 index 0000000..f9c753d --- /dev/null +++ b/lib/comp/lz4.c @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +#include +#include +#include +#include + +#include +#include + +#include "internal.h" + +typedef struct { + compressor_t base; +} lz4_compressor_t; + +typedef struct { + uint32_t version; + uint32_t flags; +} lz4_options; + +#define LZ4LEGACY 1 + +static int lz4_write_options(compressor_t *base, int fd) +{ + lz4_options opt = { + .version = htole32(LZ4LEGACY), + .flags = htole32(0), + }; + (void)base; + + return generic_write_options(fd, &opt, sizeof(opt)); +} + +static int lz4_read_options(compressor_t *base, int fd) +{ + lz4_options opt; + (void)base; + + if (generic_read_options(fd, &opt, sizeof(opt))) + return -1; + + opt.version = le32toh(opt.version); + opt.flags = le32toh(opt.flags); + + if (opt.version != LZ4LEGACY) { + fprintf(stderr, "unsupported lz4 version '%d'\n", opt.version); + return -1; + } + + return 0; +} + +static ssize_t lz4_comp_block(compressor_t *base, const uint8_t *in, + size_t size, uint8_t *out, size_t outsize) +{ + int ret; + (void)base; + + ret = LZ4_compress_default((void *)in, (void *)out, size, outsize); + + if (ret < 0) { + fputs("internal error in lz4 compressor\n", stderr); + return -1; + } + + return ret; +} + +static ssize_t lz4_uncomp_block(compressor_t *base, const uint8_t *in, + size_t size, uint8_t *out, size_t outsize) +{ + int ret; + (void)base; + + ret = LZ4_decompress_safe((void *)in, (void *)out, size, outsize); + + if (ret < 0) { + fputs("internal error in lz4 decompressor\n", stderr); + return -1; + } + + return ret; +} + +static void lz4_destroy(compressor_t *base) +{ + free(base); +} + +compressor_t *create_lz4_compressor(bool compress, size_t block_size) +{ + lz4_compressor_t *lz4 = calloc(1, sizeof(*lz4)); + compressor_t *base = (compressor_t *)lz4; + (void)block_size; + + if (lz4 == NULL) { + perror("creating lz4 compressor"); + return NULL; + } + + base->destroy = lz4_destroy; + base->do_block = compress ? lz4_comp_block : lz4_uncomp_block; + base->write_options = lz4_write_options; + base->read_options = lz4_read_options; + return base; +} diff --git a/mkfs/Makemodule.am b/mkfs/Makemodule.am index 35e937c..dc105a0 100644 --- a/mkfs/Makemodule.am +++ b/mkfs/Makemodule.am @@ -14,4 +14,8 @@ if WITH_LZO gensquashfs_LDADD += $(LZO_LIBS) endif +if WITH_LZ4 +gensquashfs_LDADD += $(LZ4_LIBS) +endif + bin_PROGRAMS += gensquashfs diff --git a/unpack/Makemodule.am b/unpack/Makemodule.am index c3e61aa..c68a005 100644 --- a/unpack/Makemodule.am +++ b/unpack/Makemodule.am @@ -16,4 +16,8 @@ if WITH_LZO rdsquashfs_LDADD += $(LZO_LIBS) endif +if WITH_LZ4 +rdsquashfs_LDADD += $(LZ4_LIBS) +endif + bin_PROGRAMS += rdsquashfs -- cgit v1.2.3