This is an automated email from the git hooks/post-receive script.

guillem pushed a commit to branch main
in repository dpkg.

View the commit online:
https://git.dpkg.org/cgit/dpkg/dpkg.git/commit/?id=2c2f7066bd8c3209762762fa6905fa567b08ca5a

commit 2c2f7066bd8c3209762762fa6905fa567b08ca5a (HEAD -> main)
Author: Guillem Jover <[email protected]>
AuthorDate: Mon Jan 9 02:32:42 2023 +0100

    libdpkg: Add zstd support for .deb archives
    
    This adds support for .deb ZStandard compression and decompression.
    The main reason for this addition is due to Ubuntu having forked the
    .deb ecosystem when they added support for this unilaterally, so now
    there are many .deb in the wild using this compression format, which
    cannot be handled by the upstream dpkg-deb tool.
    
    Although at least now the ZStandard format is widely used on many
    projects, has been specified within the IETF as RFC8878, so backwards
    compatibility and format stability are no longer a concern, and it has
    good trade offs between size and speed.
    
    This has been implemented from scratch, based on the initial prototype
    code used during the early request evaluation. Using the new advanced
    API, with support for multi-threading, and using an I/O loop resembling
    the one used with liblzma, as the plan is to eventually switch all
    compressors to use a single I/O loop implementation.
    
    Closes: #892664
---
 README                 |   1 +
 configure.ac           |   2 +
 debian/control         |   6 +
 debian/rules           |   1 +
 lib/dpkg/Makefile.am   |   1 +
 lib/dpkg/compress.c    | 342 ++++++++++++++++++++++++++++++++++++++++++++++++-
 lib/dpkg/compress.h    |   1 +
 lib/dpkg/libdpkg.pc.in |   2 +-
 m4/dpkg-libs.m4        |   7 +
 man/deb.pod            |   2 +
 man/dpkg-deb.pod       |  12 +-
 src/Makefile.am        |   1 +
 src/at/deb-format.at   |  38 ++++++
 src/deb/extract.c      |   1 +
 src/deb/main.c         |   3 +-
 15 files changed, 411 insertions(+), 9 deletions(-)

diff --git a/README b/README
index eca08c25a..e791ef5a6 100644
--- a/README
+++ b/README
@@ -86,6 +86,7 @@ To enable optional functionality or programs, this software 
might be needed:
   libmd (used by libdpkg, required if libc is missing digest functions)
   libz (from zlib, used instead of gzip command-line tool)
   liblzma (from xz utils, used instead of xz command-line tool)
+  libzstd (from libzstd, used instead of zstd command-line tool)
   libbz2 (from bzip2, used instead of bzip2 command-line tool)
   libselinux
   curses compatible library (needed on --enable-dselect)
diff --git a/configure.ac b/configure.ac
index 0c15c8b90..9a2412920 100644
--- a/configure.ac
+++ b/configure.ac
@@ -97,6 +97,7 @@ DPKG_LIB_MD
 DPKG_LIB_Z
 DPKG_LIB_BZ2
 DPKG_LIB_LZMA
+DPKG_LIB_ZSTD
 DPKG_LIB_SELINUX
 AS_IF([test "x$build_dselect" = "xyes"], [
   DPKG_LIB_CURSES
@@ -287,6 +288,7 @@ Configuration:
     libmd . . . . . . . . . . . . : $have_libmd
     libz  . . . . . . . . . . . . : $have_libz_impl
     liblzma . . . . . . . . . . . : $have_liblzma
+    libzstd . . . . . . . . . . . : $have_libzstd
     libbz2  . . . . . . . . . . . : $have_libbz2
     libcurses . . . . . . . . . . : ${have_libcurses:-no}
 CONFIG
diff --git a/debian/control b/debian/control
index 57d418a3f..f7f5e1068 100644
--- a/debian/control
+++ b/debian/control
@@ -21,12 +21,16 @@ Build-Depends:
  libbz2-dev,
 # Version needed for multi-threaded decompressor support.
  liblzma-dev (>= 5.4.0),
+# Version needed for the new streaming API.
+ libzstd-dev (>= 1.4.0),
  libselinux1-dev [linux-any],
  libncurses-dev (>= 6.1+20180210),
 # Needed for the functional test.
  bzip2 <!nocheck>,
 # Version needed for multi-threaded decompressor support.
  xz-utils (>= 5.4.0) <!nocheck>,
+# Needed for the functional test.
+ zstd <!nocheck>,
 # Needed for the author release process.
  git <pkg.dpkg.author-release>,
  ca-certificates <pkg.dpkg.author-release>,
@@ -86,6 +90,8 @@ Depends:
  zlib1g-dev,
 # Version needed for multi-threaded decompressor support.
  liblzma-dev (>= 5.4.0),
+# Version needed for the new streaming API.
+ libzstd-dev (>= 1.4.0),
  libbz2-dev,
 Description: Debian package management static library
  This package provides the header files and static library necessary to
diff --git a/debian/rules b/debian/rules
index a37fd4de4..eec726690 100755
--- a/debian/rules
+++ b/debian/rules
@@ -52,6 +52,7 @@ override_dh_auto_configure:
                --with-devlibdir=\$${prefix}/lib/$(DEB_HOST_MULTIARCH) \
                --with-libz \
                --with-liblzma \
+               --with-libzstd \
                --with-libbz2 \
                # EOL
 
diff --git a/lib/dpkg/Makefile.am b/lib/dpkg/Makefile.am
index 892a11278..97a8dbfa6 100644
--- a/lib/dpkg/Makefile.am
+++ b/lib/dpkg/Makefile.am
@@ -45,6 +45,7 @@ libdpkg_la_LIBADD += \
        $(LIBINTL) \
        $(Z_LIBS) \
        $(LZMA_LIBS) \
+       $(ZSTD_LIBS) \
        $(BZ2_LIBS) \
        # EOL
 endif
diff --git a/lib/dpkg/compress.c b/lib/dpkg/compress.c
index 760f93b34..e56f6c1a4 100644
--- a/lib/dpkg/compress.c
+++ b/lib/dpkg/compress.c
@@ -4,7 +4,7 @@
  *
  * Copyright © 2000 Wichert Akkerman <[email protected]>
  * Copyright © 2004 Scott James Remnant <[email protected]>
- * Copyright © 2006-2015 Guillem Jover <[email protected]>
+ * Copyright © 2006-2023 Guillem Jover <[email protected]>
  *
  * This is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -35,6 +35,13 @@
 #ifdef WITH_LIBLZMA
 #include <lzma.h>
 #endif
+#ifdef WITH_LIBZSTD
+#include <zstd.h>
+#define DPKG_ZSTD_MAX_LEVEL ZSTD_maxCLevel()
+#else
+#define DPKG_ZSTD_MAX_LEVEL 22
+#define ZSTD_CLEVEL_DEFAULT 3
+#endif
 #ifdef WITH_LIBBZ2
 #include <bzlib.h>
 #endif
@@ -50,6 +57,7 @@
 #include <dpkg/compress.h>
 #if USE_LIBZ_IMPL == USE_LIBZ_IMPL_NONE || \
     !defined(WITH_LIBLZMA) || \
+    !defined(WITH_LIBZSTD) || \
     !defined(WITH_LIBBZ2)
 #include <dpkg/subproc.h>
 
@@ -100,7 +108,7 @@ command_decompress_init(struct command *cmd, const char 
*name, const char *desc)
 }
 #endif
 
-#if defined(WITH_LIBLZMA)
+#if defined(WITH_LIBLZMA) || defined(WITH_LIBZSTD)
 enum dpkg_stream_filter {
        DPKG_STREAM_COMPRESS    = 1,
        DPKG_STREAM_DECOMPRESS  = 2,
@@ -961,6 +969,330 @@ static const struct compressor compressor_lzma = {
        .decompress = decompress_lzma,
 };
 
+/*
+ * ZStandard compressor.
+ */
+
+#define ZSTD           "zstd"
+
+#ifdef WITH_LIBZSTD
+struct io_zstd_stream {
+       enum dpkg_stream_filter filter;
+       enum dpkg_stream_action action;
+       enum dpkg_stream_status status;
+
+       union {
+               ZSTD_CCtx *c;
+               ZSTD_DCtx *d;
+       } ctx;
+
+       const uint8_t *next_in;
+       size_t avail_in;
+       uint8_t *next_out;
+       size_t avail_out;
+};
+
+struct io_zstd {
+       const char *desc;
+
+       struct compress_params *params;
+
+       void (*init)(struct io_zstd *io, struct io_zstd_stream *s);
+       void (*code)(struct io_zstd *io, struct io_zstd_stream *s);
+       void (*done)(struct io_zstd *io, struct io_zstd_stream *s);
+};
+
+static void DPKG_ATTR_NORET
+filter_zstd_error(struct io_zstd *io, size_t ret)
+{
+       ohshit(_("%s: zstd error: %s"), io->desc, ZSTD_getErrorName(ret));
+}
+
+static uint32_t
+filter_zstd_get_cputhreads(struct compress_params *params)
+{
+       ZSTD_bounds workers;
+       long threads_max = 1;
+
+       /* The shared library has not been built with multi-threading. */
+       workers = ZSTD_cParam_getBounds(ZSTD_c_nbWorkers);
+       if (workers.upperBound == 0)
+               return 1;
+
+#ifdef _SC_NPROCESSORS_ONLN
+       threads_max = sysconf(_SC_NPROCESSORS_ONLN);
+       if (threads_max < 0)
+               return 1;
+#endif
+
+       if (params->threads_max >= 0)
+               return clamp(params->threads_max, 1, threads_max);
+
+       return threads_max;
+}
+
+static size_t
+filter_zstd_get_buf_in_size(struct io_zstd_stream *s)
+{
+       if (s->filter == DPKG_STREAM_DECOMPRESS)
+               return ZSTD_DStreamInSize();
+       else
+               return ZSTD_CStreamInSize();
+}
+
+static size_t
+filter_zstd_get_buf_out_size(struct io_zstd_stream *s)
+{
+       if (s->filter == DPKG_STREAM_DECOMPRESS)
+               return ZSTD_DStreamOutSize();
+       else
+               return ZSTD_CStreamOutSize();
+}
+
+static void
+filter_unzstd_init(struct io_zstd *io, struct io_zstd_stream *s)
+{
+       s->filter = DPKG_STREAM_DECOMPRESS;
+       s->action = DPKG_STREAM_RUN;
+       s->status = DPKG_STREAM_OK;
+
+       s->ctx.d = ZSTD_createDCtx();
+       if (s->ctx.d == NULL)
+               ohshit(_("%s: cannot create zstd decompression context"),
+                      io->desc);
+}
+
+static void
+filter_unzstd_code(struct io_zstd *io, struct io_zstd_stream *s)
+{
+       ZSTD_inBuffer buf_in = { s->next_in, s->avail_in, 0 };
+       ZSTD_outBuffer buf_out = { s->next_out, s->avail_out, 0 };
+       size_t ret;
+
+       ret = ZSTD_decompressStream(s->ctx.d, &buf_out, &buf_in);
+       if (ZSTD_isError(ret))
+               filter_zstd_error(io, ret);
+
+       s->next_in += buf_in.pos;
+       s->avail_in -= buf_in.pos;
+       s->next_out += buf_out.pos;
+       s->avail_out -= buf_out.pos;
+
+       if (ret == 0)
+               s->status = DPKG_STREAM_END;
+}
+
+static void
+filter_unzstd_done(struct io_zstd *io, struct io_zstd_stream *s)
+{
+       ZSTD_freeDCtx(s->ctx.d);
+}
+
+static void
+filter_zstd_init(struct io_zstd *io, struct io_zstd_stream *s)
+{
+       int clevel = io->params->level;
+       uint32_t nthreads;
+       size_t ret;
+
+       s->filter = DPKG_STREAM_COMPRESS;
+       s->action = DPKG_STREAM_RUN;
+       s->status = DPKG_STREAM_OK;
+
+       s->ctx.c = ZSTD_createCCtx();
+       if (s->ctx.c == NULL)
+               ohshit(_("%s: cannot create zstd compression context"),
+                      io->desc);
+
+       ret = ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_compressionLevel, clevel);
+       if (ZSTD_isError(ret))
+               filter_zstd_error(io, ret);
+       ret = ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_checksumFlag, 1);
+       if (ZSTD_isError(ret))
+               filter_zstd_error(io, ret);
+
+       nthreads = filter_zstd_get_cputhreads(io->params);
+       if (nthreads > 1)
+               ZSTD_CCtx_setParameter(s->ctx.c, ZSTD_c_nbWorkers, nthreads);
+}
+
+static void
+filter_zstd_code(struct io_zstd *io, struct io_zstd_stream *s)
+{
+       ZSTD_inBuffer buf_in = { s->next_in, s->avail_in, 0 };
+       ZSTD_outBuffer buf_out = { s->next_out, s->avail_out, 0 };
+       ZSTD_EndDirective action;
+       size_t ret;
+
+       if (s->action == DPKG_STREAM_FINISH)
+               action = ZSTD_e_end;
+       else
+               action = ZSTD_e_continue;
+
+       ret = ZSTD_compressStream2(s->ctx.c, &buf_out, &buf_in, action);
+       if (ZSTD_isError(ret))
+               filter_zstd_error(io, ret);
+
+       s->next_in += buf_in.pos;
+       s->avail_in -= buf_in.pos;
+       s->next_out += buf_out.pos;
+       s->avail_out -= buf_out.pos;
+
+       if (s->action == DPKG_STREAM_FINISH && ret == 0)
+               s->status = DPKG_STREAM_END;
+}
+
+static void
+filter_zstd_done(struct io_zstd *io, struct io_zstd_stream *s)
+{
+       ZSTD_freeCCtx(s->ctx.c);
+}
+
+static void
+filter_zstd(struct io_zstd *io, int fd_in, int fd_out)
+{
+       ssize_t buf_in_size;
+       ssize_t buf_out_size;
+       uint8_t *buf_in;
+       uint8_t *buf_out;
+       struct io_zstd_stream s = {
+               .action = DPKG_STREAM_INIT,
+       };
+
+       io->init(io, &s);
+
+       buf_in_size = filter_zstd_get_buf_in_size(&s);
+       buf_in = m_malloc(buf_in_size);
+       buf_out_size = filter_zstd_get_buf_out_size(&s);
+       buf_out = m_malloc(buf_out_size);
+
+       s.next_out = buf_out;
+       s.avail_out = buf_out_size;
+
+       do {
+               ssize_t len;
+
+               if (s.avail_in == 0 && s.action != DPKG_STREAM_FINISH) {
+                       len = fd_read(fd_in, buf_in, buf_in_size);
+                       if (len < 0)
+                               ohshite(_("%s: zstd read error"), io->desc);
+                       if (len < buf_in_size)
+                               s.action = DPKG_STREAM_FINISH;
+                       s.next_in = buf_in;
+                       s.avail_in = len;
+               }
+
+               io->code(io, &s);
+
+               if (s.avail_out == 0 || s.status == DPKG_STREAM_END) {
+                       len = fd_write(fd_out, buf_out, s.next_out - buf_out);
+                       if (len < 0)
+                               ohshite(_("%s: zstd write error"), io->desc);
+                       s.next_out = buf_out;
+                       s.avail_out = buf_out_size;
+               }
+       } while (s.status != DPKG_STREAM_END);
+
+       io->done(io, &s);
+
+       free(buf_in);
+       free(buf_out);
+
+       if (close(fd_out))
+               ohshite(_("%s: zstd close error"), io->desc);
+}
+
+static void
+decompress_zstd(struct compress_params *params, int fd_in, int fd_out,
+                const char *desc)
+{
+       struct io_zstd io;
+
+       io.init = filter_unzstd_init;
+       io.code = filter_unzstd_code;
+       io.done = filter_unzstd_done;
+       io.desc = desc;
+       io.params = params;
+
+       filter_zstd(&io, fd_in, fd_out);
+}
+
+static void
+compress_zstd(struct compress_params *params, int fd_in, int fd_out,
+              const char *desc)
+{
+       struct io_zstd io;
+
+       io.init = filter_zstd_init;
+       io.code = filter_zstd_code;
+       io.done = filter_zstd_done;
+       io.desc = desc;
+       io.params = params;
+
+       filter_zstd(&io, fd_in, fd_out);
+}
+#else
+static const char *env_zstd[] = {
+       "ZSTD_CLEVEL",
+       "ZSTD_NBTHREADS",
+       NULL,
+};
+
+static void
+decompress_zstd(struct compress_params *params, int fd_in, int fd_out,
+                const char *desc)
+{
+       struct command cmd;
+       char *threads_opt = NULL;
+
+       command_decompress_init(&cmd, ZSTD, desc);
+       command_add_arg(&cmd, "-q");
+
+       if (params->threads_max > 0) {
+               threads_opt = str_fmt("-T%d", params->threads_max);
+               command_add_arg(&cmd, threads_opt);
+       }
+
+       fd_fd_filter(&cmd, fd_in, fd_out, env_zstd);
+
+       command_destroy(&cmd);
+       free(threads_opt);
+}
+
+static void
+compress_zstd(struct compress_params *params, int fd_in, int fd_out,
+              const char *desc)
+{
+       struct command cmd;
+       char *threads_opt = NULL;
+
+       command_compress_init(&cmd, ZSTD, desc, params->level);
+       command_add_arg(&cmd, "-q");
+
+       if (params->level > 19)
+               command_add_arg(&cmd, "--ultra");
+
+       if (params->threads_max > 0) {
+               threads_opt = str_fmt("-T%d", params->threads_max);
+               command_add_arg(&cmd, threads_opt);
+       }
+
+       fd_fd_filter(&cmd, fd_in, fd_out, env_zstd);
+
+       command_destroy(&cmd);
+       free(threads_opt);
+}
+#endif
+
+static const struct compressor compressor_zstd = {
+       .name = "zstd",
+       .extension = ".zst",
+       .default_level = ZSTD_CLEVEL_DEFAULT,
+       .fixup_params = fixup_none_params,
+       .compress = compress_zstd,
+       .decompress = decompress_zstd,
+};
+
 /*
  * Generic compressor filter.
  */
@@ -969,6 +1301,7 @@ static const struct compressor *compressor_array[] = {
        [COMPRESSOR_TYPE_NONE] = &compressor_none,
        [COMPRESSOR_TYPE_GZIP] = &compressor_gzip,
        [COMPRESSOR_TYPE_XZ] = &compressor_xz,
+       [COMPRESSOR_TYPE_ZSTD] = &compressor_zstd,
        [COMPRESSOR_TYPE_BZIP2] = &compressor_bzip2,
        [COMPRESSOR_TYPE_LZMA] = &compressor_lzma,
 };
@@ -1053,7 +1386,10 @@ compressor_check_params(struct compress_params *params, 
struct dpkg_error *err)
 {
        compressor_fixup_params(params);
 
-       if (params->level > 9) {
+       if ((params->type == COMPRESSOR_TYPE_ZSTD &&
+            params->level > DPKG_ZSTD_MAX_LEVEL) ||
+           (params->type != COMPRESSOR_TYPE_ZSTD &&
+            params->level > 9)) {
                dpkg_put_error(err, _("invalid compression level %d"),
                               params->level);
                return false;
diff --git a/lib/dpkg/compress.h b/lib/dpkg/compress.h
index f9b66ba0e..7458e174b 100644
--- a/lib/dpkg/compress.h
+++ b/lib/dpkg/compress.h
@@ -40,6 +40,7 @@ enum compressor_type {
        COMPRESSOR_TYPE_NONE,
        COMPRESSOR_TYPE_GZIP,
        COMPRESSOR_TYPE_XZ,
+       COMPRESSOR_TYPE_ZSTD,
        COMPRESSOR_TYPE_BZIP2,
        COMPRESSOR_TYPE_LZMA,
 };
diff --git a/lib/dpkg/libdpkg.pc.in b/lib/dpkg/libdpkg.pc.in
index 4bf1b72be..1a9159498 100644
--- a/lib/dpkg/libdpkg.pc.in
+++ b/lib/dpkg/libdpkg.pc.in
@@ -7,5 +7,5 @@ Name: libdpkg
 Description: Debian package management system library
 Version: @VERSION@
 Libs: -L${libdir} -ldpkg
-Libs.private: @MD_LIBS@ @Z_LIBS@ @LZMA_LIBS@ @BZ2_LIBS@
+Libs.private: @MD_LIBS@ @Z_LIBS@ @LZMA_LIBS@ @ZSTD_LIBS@ @BZ2_LIBS@
 Cflags: -I${includedir}
diff --git a/m4/dpkg-libs.m4 b/m4/dpkg-libs.m4
index 2badcd74d..556446a54 100644
--- a/m4/dpkg-libs.m4
+++ b/m4/dpkg-libs.m4
@@ -108,6 +108,13 @@ AC_DEFUN([DPKG_LIB_LZMA], [
   ])
 ])# DPKG_LIB_LZMA
 
+# DPKG_LIB_ZSTD
+# ------------
+# Check for zstd library.
+AC_DEFUN([DPKG_LIB_ZSTD], [
+  DPKG_WITH_COMPRESS_LIB([zstd], [zstd.h], [ZSTD_compressStream2])
+])# DPKG_LIB_ZSTD
+
 # DPKG_LIB_BZ2
 # ------------
 # Check for bz2 library.
diff --git a/man/deb.pod b/man/deb.pod
index 6e3964507..812b2bcfb 100644
--- a/man/deb.pod
+++ b/man/deb.pod
@@ -84,6 +84,7 @@ It is a tar archive containing the package control 
information, either
 not compressed (supported since dpkg 1.17.6), or compressed with
 gzip (with B<.gz> extension) or
 xz (with B<.xz> extension, supported since 1.17.6),
+zstd (with B<.zst> extension, supported since dpkg 1.21.18),
 as a series of plain files, of which the file
 B<control>
 is mandatory and contains the core control information, the
@@ -106,6 +107,7 @@ It contains the filesystem as a tar archive, either
 not compressed (supported since dpkg 1.10.24), or compressed with
 gzip (with B<.gz> extension),
 xz (with B<.xz> extension, supported since dpkg 1.15.6),
+zstd (with B<.zst> extension, supported since dpkg 1.21.18),
 bzip2 (with B<.bz2> extension, supported since dpkg 1.10.24) or
 lzma (with B<.lzma> extension, supported since dpkg 1.13.25).
 
diff --git a/man/dpkg-deb.pod b/man/dpkg-deb.pod
index 42ac708c3..50076c1f0 100644
--- a/man/dpkg-deb.pod
+++ b/man/dpkg-deb.pod
@@ -254,8 +254,11 @@ The default for this field is “${Package}\t${Version}\n”.
 =item B<-z>I<compress-level>
 
 Specify which compression level to use on the compressor backend, when
-building a package (default is 9 for gzip, 6 for xz).
-The accepted values are 0-9 with: 0 being mapped to compressor none for gzip.
+building a package (default is 9 for gzip, 6 for xz, 3 for zstd).
+The accepted values are compressor specific.
+For gzip, from 0-9 with 0 being mapped to compressor none.
+For xz from 0-9.
+For zstd from 0-22, with levels from 20 to 22 enabling its ultra mode.
 Before dpkg 1.16.2 level 0 was equivalent to compressor none for all
 compressors.
 
@@ -270,6 +273,7 @@ gzip (since dpkg 1.17.0) and B<extreme> for xz.
 
 Specify which compression type to use when building a package.
 Allowed values are B<gzip>, B<xz> (since dpkg 1.15.6),
+B<zstd> (since dpkg 1.21.18)
 and B<none> (default is B<xz>).
 
 =item B<--[no-]uniform-compression>
@@ -278,8 +282,8 @@ Specify that the same compression parameters should be used 
for all archive
 members (i.e. B<control.tar> and B<data.tar>; since dpkg 1.17.6).
 Otherwise only the
 B<data.tar> member will use those parameters. The only supported
-compression types allowed to be uniformly used are B<none>, B<gzip>
-and B<xz>.
+compression types allowed to be uniformly used are B<none>, B<gzip>,
+B<xz> and B<zstd>.
 The B<--no-uniform-compression> option disables uniform compression
 (since dpkg 1.19.0).
 Uniform compression is the default (since dpkg 1.19.0).
diff --git a/src/Makefile.am b/src/Makefile.am
index 95cc94353..435816558 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -100,6 +100,7 @@ dpkg_deb_LDADD = \
        $(LDADD) \
        $(Z_LIBS) \
        $(LZMA_LIBS) \
+       $(ZSTD_LIBS) \
        $(BZ2_LIBS) \
        # EOL
 
diff --git a/src/at/deb-format.at b/src/at/deb-format.at
index b706b1228..b06beeda1 100644
--- a/src/at/deb-format.at
+++ b/src/at/deb-format.at
@@ -169,6 +169,7 @@ AT_KEYWORDS([dpkg-deb deb])
 
 AT_SKIP_IF([! command -v xz >/dev/null])
 AT_SKIP_IF([! command -v gzip >/dev/null])
+AT_SKIP_IF([! command -v zstd >/dev/null])
 AT_SKIP_IF([! command -v bzip2 >/dev/null])
 AT_SKIP_IF([! command -v lzma >/dev/null])
 
@@ -198,6 +199,8 @@ gzip -c control.tar >control.tar.gz
 gzip -c data.tar >data.tar.gz
 xz -c control.tar >control.tar.xz
 xz -c data.tar >data.tar.xz
+zstd -c control.tar >control.tar.zst
+zstd -c data.tar >data.tar.zst
 bzip2 -c data.tar >data.tar.bz2
 lzma -c data.tar >data.tar.lzma
 touch _ignore
@@ -441,6 +444,19 @@ drwxr-xr-x root/root         0 1970-01-01 00:00 ./
 -rw-r--r-- root/root         5 1970-01-01 00:00 ./file-templ
 ])
 
+AT_CHECK([
+# Test control.tar.zst member
+ar rc pkg-control-zst.deb debian-binary control.tar.zst data.tar.zst
+ar t pkg-control-zst.deb
+dpkg-deb -c pkg-control-zst.deb
+], [], [debian-binary
+control.tar.zst
+data.tar.zst
+drwxr-xr-x root/root         0 1970-01-01 00:00 ./
+-rw-r--r-- root/root    641345 1970-01-01 00:00 ./ChangeLog.old
+-rw-r--r-- root/root         5 1970-01-01 00:00 ./file-templ
+])
+
 AT_CHECK([
 # Test data.tar member
 ar rc pkg-data-none.deb debian-binary control.tar.gz data.tar
@@ -480,6 +496,19 @@ drwxr-xr-x root/root         0 1970-01-01 00:00 ./
 -rw-r--r-- root/root         5 1970-01-01 00:00 ./file-templ
 ])
 
+AT_CHECK([
+# Test data.tar.zst member
+ar rc pkg-data-zst.deb debian-binary control.tar.gz data.tar.zst
+ar t pkg-data-zst.deb
+dpkg-deb -c pkg-data-zst.deb
+], [], [debian-binary
+control.tar.gz
+data.tar.zst
+drwxr-xr-x root/root         0 1970-01-01 00:00 ./
+-rw-r--r-- root/root    641345 1970-01-01 00:00 ./ChangeLog.old
+-rw-r--r-- root/root         5 1970-01-01 00:00 ./file-templ
+])
+
 AT_CHECK([
 # Test data.tar.bz2 member
 ar rc pkg-data-bz2.deb debian-binary control.tar.gz data.tar.bz2
@@ -558,4 +587,13 @@ cmp ctrl-xz.tar control.tar
 cmp fsys-xz.tar data.tar
 ])
 
+AT_CHECK([
+# Test building and extracting zstd compressed archive
+dpkg-deb --uniform-compression --root-owner-group -Zzstd -b pkg-templ 
pkg-comp-zstd.deb >/dev/null
+dpkg-deb --ctrl-tarfile pkg-comp-zstd.deb >ctrl-zstd.tar
+dpkg-deb --fsys-tarfile pkg-comp-zstd.deb >fsys-zstd.tar
+cmp ctrl-zstd.tar control.tar
+cmp fsys-zstd.tar data.tar
+])
+
 AT_CLEANUP
diff --git a/src/deb/extract.c b/src/deb/extract.c
index 6466fa6f2..73612a454 100644
--- a/src/deb/extract.c
+++ b/src/deb/extract.c
@@ -185,6 +185,7 @@ extracthalf(const char *debar, const char *dir,
           decompress_params.type = compressor_find_by_extension(extension);
           if (decompress_params.type != COMPRESSOR_TYPE_NONE &&
               decompress_params.type != COMPRESSOR_TYPE_GZIP &&
+              decompress_params.type != COMPRESSOR_TYPE_ZSTD &&
               decompress_params.type != COMPRESSOR_TYPE_XZ)
             ohshit(_("archive '%s' uses unknown compression for member '%.*s', 
"
                      "giving up"),
diff --git a/src/deb/main.c b/src/deb/main.c
index 416eba30f..4a1673835 100644
--- a/src/deb/main.c
+++ b/src/deb/main.c
@@ -109,7 +109,7 @@ usage(const char *const *argv)
 "      --[no-]uniform-compression   Use the compression params on all 
members.\n"
 "  -z#                              Set the compression level when building.\n"
 "  -Z<type>                         Set the compression type used when 
building.\n"
-"                                     Allowed types: gzip, xz, none.\n"
+"                                     Allowed types: gzip, xz, zstd, none.\n"
 "  -S<strategy>                     Set the compression strategy when 
building.\n"
 "                                     Allowed values: none; extreme (xz);\n"
 "                                     filtered, huffman, rle, fixed (gzip).\n"
@@ -297,6 +297,7 @@ int main(int argc, const char *const *argv) {
   if (opt_uniform_compression &&
       (compress_params.type != COMPRESSOR_TYPE_NONE &&
        compress_params.type != COMPRESSOR_TYPE_GZIP &&
+       compress_params.type != COMPRESSOR_TYPE_ZSTD &&
        compress_params.type != COMPRESSOR_TYPE_XZ))
     badusage(_("unsupported compression type '%s' with uniform compression"),
              compressor_get_name(compress_params.type));

-- 
Dpkg.Org's dpkg

Reply via email to