This patch adds DEFLATE compression algorithm support to erofsfuse
by using zlib (by default) and libdeflate.  libdeflate will be used
instead of zlib if libdeflate is enabled.

Signed-off-by: Gao Xiang <[email protected]>
---
 configure.ac       |  45 ++++++++++++++
 dump/Makefile.am   |   3 +-
 fsck/Makefile.am   |   6 +-
 fuse/Makefile.am   |   2 +-
 include/erofs_fs.h |   7 +++
 lib/decompress.c   | 145 +++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 204 insertions(+), 4 deletions(-)

diff --git a/configure.ac b/configure.ac
index cd6be4a..d41f4a3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -122,6 +122,15 @@ AC_ARG_ENABLE(lzma,
    [AS_HELP_STRING([--enable-lzma], [enable LZMA compression support 
@<:@default=no@:>@])],
    [enable_lzma="$enableval"], [enable_lzma="no"])
 
+AC_ARG_WITH(zlib,
+   [AS_HELP_STRING([--without-zlib],
+      [Ignore presence of zlib inflate support @<:@default=enabled@:>@])])
+
+AC_ARG_WITH(libdeflate,
+   [AS_HELP_STRING([--with-libdeflate],
+      [Enable and build with libdeflate inflate support 
@<:@default=disabled@:>@])], [],
+      [with_libdeflate="no"])
+
 AC_ARG_ENABLE(fuse,
    [AS_HELP_STRING([--enable-fuse], [enable erofsfuse @<:@default=no@:>@])],
    [enable_fuse="$enableval"], [enable_fuse="no"])
@@ -394,6 +403,34 @@ if test "x$enable_lzma" = "xyes"; then
   CPPFLAGS="${saved_CPPFLAGS}"
 fi
 
+# Configure zlib
+AS_IF([test "x$with_zlib" != "xno"], [
+  PKG_CHECK_MODULES([zlib], [zlib])
+  # Paranoia: don't trust the result reported by pkgconfig before trying out
+  saved_LIBS="$LIBS"
+  saved_CPPFLAGS=${CPPFLAGS}
+  CPPFLAGS="${zlib_CFLAGS} ${CPPFLAGS}"
+  LIBS="${zlib_LIBS} $LIBS"
+  AC_CHECK_LIB(z, inflate, [
+    have_zlib="yes" ], [
+    AC_MSG_ERROR([zlib doesn't work properly])])
+  LIBS="${saved_LIBS}"
+  CPPFLAGS="${saved_CPPFLAGS}"], [have_zlib="no"])
+
+# Configure libdeflate
+AS_IF([test "x$with_libdeflate" != "xno"], [
+  PKG_CHECK_MODULES([libdeflate], [libdeflate])
+  # Paranoia: don't trust the result reported by pkgconfig before trying out
+  saved_LIBS="$LIBS"
+  saved_CPPFLAGS=${CPPFLAGS}
+  CPPFLAGS="${libdeflate_CFLAGS} ${CPPFLAGS}"
+  LIBS="${libdeflate_LIBS} $LIBS"
+  AC_CHECK_LIB(deflate, libdeflate_deflate_decompress, [
+    have_libdeflate="yes" ], [
+    AC_MSG_ERROR([libdeflate doesn't work properly])])
+  LIBS="${saved_LIBS}"
+  CPPFLAGS="${saved_CPPFLAGS}"], [have_libdeflate="no"])
+
 # Enable 64-bit off_t
 CFLAGS+=" -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64"
 
@@ -449,6 +486,14 @@ if test "x${have_liblzma}" = "xyes"; then
   AC_SUBST([liblzma_CFLAGS])
 fi
 
+if test "x$have_zlib" = "xyes"; then
+  AC_DEFINE([HAVE_ZLIB], 1, [Define to 1 if zlib is found])
+fi
+
+if test "x$have_libdeflate" = "xyes"; then
+  AC_DEFINE([HAVE_LIBDEFLATE], 1, [Define to 1 if libdeflate is found])
+fi
+
 # Dump maximum block size
 AS_IF([test "x$erofs_cv_max_block_size" = "x"],
       [$erofs_cv_max_block_size = 4096], [])
diff --git a/dump/Makefile.am b/dump/Makefile.am
index c2bef6d..63f301e 100644
--- a/dump/Makefile.am
+++ b/dump/Makefile.am
@@ -7,4 +7,5 @@ AM_CPPFLAGS = ${libuuid_CFLAGS}
 dump_erofs_SOURCES = main.c
 dump_erofs_CFLAGS = -Wall -I$(top_srcdir)/include
 dump_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} \
-       ${libuuid_LIBS} ${liblz4_LIBS} ${liblzma_LIBS}
+       ${libuuid_LIBS} ${liblz4_LIBS} ${liblzma_LIBS} ${zlib_LIBS} \
+       ${libdeflate_LIBS}
diff --git a/fsck/Makefile.am b/fsck/Makefile.am
index 9366a9d..ffdd0be 100644
--- a/fsck/Makefile.am
+++ b/fsck/Makefile.am
@@ -7,7 +7,8 @@ AM_CPPFLAGS = ${libuuid_CFLAGS}
 fsck_erofs_SOURCES = main.c
 fsck_erofs_CFLAGS = -Wall -I$(top_srcdir)/include
 fsck_erofs_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} \
-       ${libuuid_LIBS} ${liblz4_LIBS} ${liblzma_LIBS}
+       ${libuuid_LIBS} ${liblz4_LIBS} ${liblzma_LIBS} ${zlib_LIBS} \
+       ${libdeflate_LIBS}
 
 if ENABLE_FUZZING
 noinst_PROGRAMS   = fuzz_erofsfsck
@@ -15,5 +16,6 @@ fuzz_erofsfsck_SOURCES = main.c
 fuzz_erofsfsck_CFLAGS = -Wall -I$(top_srcdir)/include -DFUZZING
 fuzz_erofsfsck_LDFLAGS = -fsanitize=address,fuzzer
 fuzz_erofsfsck_LDADD = $(top_builddir)/lib/liberofs.la ${libselinux_LIBS} \
-       ${libuuid_LIBS} ${liblz4_LIBS} ${liblzma_LIBS}
+       ${libuuid_LIBS} ${liblz4_LIBS} ${liblzma_LIBS} ${zlib_LIBS} \
+       ${libdeflate_LIBS}
 endif
diff --git a/fuse/Makefile.am b/fuse/Makefile.am
index 3179a2b..50be783 100644
--- a/fuse/Makefile.am
+++ b/fuse/Makefile.am
@@ -7,4 +7,4 @@ erofsfuse_SOURCES = main.c
 erofsfuse_CFLAGS = -Wall -I$(top_srcdir)/include
 erofsfuse_CFLAGS += -DFUSE_USE_VERSION=26 ${libfuse_CFLAGS} 
${libselinux_CFLAGS}
 erofsfuse_LDADD = $(top_builddir)/lib/liberofs.la ${libfuse_LIBS} 
${liblz4_LIBS} \
-       ${libselinux_LIBS} ${liblzma_LIBS}
+       ${libselinux_LIBS} ${liblzma_LIBS} ${zlib_LIBS} ${libdeflate_LIBS}
diff --git a/include/erofs_fs.h b/include/erofs_fs.h
index 3697882..6b7b34f 100644
--- a/include/erofs_fs.h
+++ b/include/erofs_fs.h
@@ -297,6 +297,7 @@ enum {
 enum {
        Z_EROFS_COMPRESSION_LZ4         = 0,
        Z_EROFS_COMPRESSION_LZMA        = 1,
+       Z_EROFS_COMPRESSION_DEFLATE     = 1,
        Z_EROFS_COMPRESSION_MAX
 };
 #define Z_EROFS_ALL_COMPR_ALGS         ((1 << Z_EROFS_COMPRESSION_MAX) - 1)
@@ -317,6 +318,12 @@ struct z_erofs_lzma_cfgs {
 
 #define Z_EROFS_LZMA_MAX_DICT_SIZE     (8 * Z_EROFS_PCLUSTER_MAX_SIZE)
 
+/* 6 bytes (+ length field = 8 bytes) */
+struct z_erofs_deflate_cfgs {
+       u8 windowbits;                  /* 8..15 for DEFLATE */
+       u8 reserved[5];
+} __packed;
+
 /*
  * bit 0 : COMPACTED_2B indexes (0 - off; 1 - on)
  *  e.g. for 4k logical cluster size,      4B        if compacted 2B is off;
diff --git a/lib/decompress.c b/lib/decompress.c
index 59a9ca0..5bc55c5 100644
--- a/lib/decompress.c
+++ b/lib/decompress.c
@@ -9,6 +9,147 @@
 #include "erofs/err.h"
 #include "erofs/print.h"
 
+#ifdef HAVE_LIBDEFLATE
+/* if libdeflate is available, use libdeflate instead. */
+#include <libdeflate.h>
+
+static int z_erofs_decompress_deflate(struct z_erofs_decompress_req *rq)
+{
+       u8 *dest = (u8 *)rq->out;
+       u8 *src = (u8 *)rq->in;
+       u8 *buff = NULL;
+       size_t actual_out;
+       unsigned int inputmargin = 0;
+       struct libdeflate_decompressor *inf;
+       enum libdeflate_result ret;
+
+       while (!src[inputmargin & (erofs_blksiz() - 1)])
+               if (!(++inputmargin & (erofs_blksiz() - 1)))
+                       break;
+
+       if (inputmargin >= rq->inputsize)
+               return -EFSCORRUPTED;
+
+       if (rq->decodedskip) {
+               buff = malloc(rq->decodedlength);
+               if (!buff)
+                       return -ENOMEM;
+               dest = buff;
+       }
+
+       inf = libdeflate_alloc_decompressor();
+       if (!inf)
+               return -ENOMEM;
+
+       if (rq->partial_decoding) {
+               ret = libdeflate_deflate_decompress(inf, src + inputmargin,
+                               rq->inputsize - inputmargin, dest,
+                               rq->decodedlength, &actual_out);
+               if (ret && ret != LIBDEFLATE_INSUFFICIENT_SPACE) {
+                       ret = -EIO;
+                       goto out_inflate_end;
+               }
+
+               if (actual_out != rq->decodedlength) {
+                       ret = -EIO;
+                       goto out_inflate_end;
+               }
+       } else {
+               ret = libdeflate_deflate_decompress(inf, src + inputmargin,
+                               rq->inputsize - inputmargin, dest,
+                               rq->decodedlength, NULL);
+               if (ret) {
+                       ret = -EIO;
+                       goto out_inflate_end;
+               }
+       }
+
+       if (rq->decodedskip)
+               memcpy(rq->out, dest + rq->decodedskip,
+                      rq->decodedlength - rq->decodedskip);
+
+out_inflate_end:
+       libdeflate_free_decompressor(inf);
+       if (buff)
+               free(buff);
+       return ret;
+}
+#elif defined(HAVE_ZLIB)
+#include <zlib.h>
+
+/* report a zlib or i/o error */
+static int zerr(int ret)
+{
+       switch (ret) {
+       case Z_STREAM_ERROR:
+               return -EINVAL;
+       case Z_DATA_ERROR:
+               return -EIO;
+       case Z_MEM_ERROR:
+               return -ENOMEM;
+       case Z_ERRNO:
+       case Z_VERSION_ERROR:
+       default:
+               return -EFAULT;
+       }
+}
+
+static int z_erofs_decompress_deflate(struct z_erofs_decompress_req *rq)
+{
+       int ret = 0;
+       u8 *dest = (u8 *)rq->out;
+       u8 *src = (u8 *)rq->in;
+       u8 *buff = NULL;
+       unsigned int inputmargin = 0;
+       z_stream strm;
+
+       while (!src[inputmargin & (erofs_blksiz() - 1)])
+               if (!(++inputmargin & (erofs_blksiz() - 1)))
+                       break;
+
+       if (inputmargin >= rq->inputsize)
+               return -EFSCORRUPTED;
+
+       if (rq->decodedskip) {
+               buff = malloc(rq->decodedlength);
+               if (!buff)
+                       return -ENOMEM;
+               dest = buff;
+       }
+
+       /* allocate inflate state */
+       strm.zalloc = Z_NULL;
+       strm.zfree = Z_NULL;
+       strm.opaque = Z_NULL;
+       strm.avail_in = 0;
+       strm.next_in = Z_NULL;
+       ret = inflateInit2(&strm, -15);
+       if (ret != Z_OK)
+               return zerr(ret);
+
+       strm.next_in = src + inputmargin;
+       strm.avail_in = rq->inputsize - inputmargin;
+       strm.next_out = dest;
+       strm.avail_out = rq->decodedlength;
+
+       ret = inflate(&strm, rq->partial_decoding ? Z_SYNC_FLUSH : Z_FINISH);
+       if (ret != Z_STREAM_END) {
+               ret = zerr(ret);
+               goto out_inflate_end;
+       }
+
+       if (rq->decodedskip)
+               memcpy(rq->out, dest + rq->decodedskip,
+                      rq->decodedlength - rq->decodedskip);
+
+out_inflate_end:
+       inflateEnd(&strm);
+       if (buff)
+               free(buff);
+       return ret;
+}
+#endif
+
 #ifdef HAVE_LIBLZMA
 #include <lzma.h>
 
@@ -167,6 +308,10 @@ int z_erofs_decompress(struct z_erofs_decompress_req *rq)
 #ifdef HAVE_LIBLZMA
        if (rq->alg == Z_EROFS_COMPRESSION_LZMA)
                return z_erofs_decompress_lzma(rq);
+#endif
+#ifdef HAVE_ZLIB
+       if (rq->alg == Z_EROFS_COMPRESSION_DEFLATE)
+               return z_erofs_decompress_deflate(rq);
 #endif
        return -EOPNOTSUPP;
 }
-- 
2.24.4

Reply via email to