summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--contrib/android/Android.mk1
-rw-r--r--cpio/bsdcpio.15
-rw-r--r--cpio/cmdline.c1
-rw-r--r--cpio/cpio.c4
-rw-r--r--cpio/cpio.h1
-rw-r--r--libarchive/archive_read_support_filter_zstd.c11
-rw-r--r--libarchive/archive_write_add_filter.c1
-rw-r--r--libarchive/archive_write_add_filter_by_name.c1
-rw-r--r--libarchive/archive_write_add_filter_zstd.c334
-rw-r--r--libarchive/archive_write_filter.34
-rw-r--r--tar/bsdtar.16
-rw-r--r--tar/bsdtar.c8
-rw-r--r--tar/bsdtar.h1
-rw-r--r--tar/cmdline.c1
-rw-r--r--tar/creation_set.c3
16 files changed, 375 insertions, 8 deletions
diff --git a/Makefile.am b/Makefile.am
index ff613d93..a3762e64 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -209,6 +209,7 @@ libarchive_la_SOURCES= \
libarchive/archive_write_add_filter_gzip.c \
libarchive/archive_write_add_filter_lrzip.c \
libarchive/archive_write_add_filter_lz4.c \
+ libarchive/archive_write_add_filter_zstd.c \
libarchive/archive_write_add_filter_lzop.c \
libarchive/archive_write_add_filter_none.c \
libarchive/archive_write_add_filter_program.c \
diff --git a/contrib/android/Android.mk b/contrib/android/Android.mk
index 87c06b0f..0d67f7ac 100644
--- a/contrib/android/Android.mk
+++ b/contrib/android/Android.mk
@@ -112,6 +112,7 @@ libarchive_src_files := libarchive/archive_acl.c \
libarchive/archive_write_add_filter_gzip.c \
libarchive/archive_write_add_filter_lrzip.c \
libarchive/archive_write_add_filter_lz4.c \
+ libarchive/archive_write_add_filter_zstd.c \
libarchive/archive_write_add_filter_lzop.c \
libarchive/archive_write_add_filter_none.c \
libarchive/archive_write_add_filter_program.c \
diff --git a/cpio/bsdcpio.1 b/cpio/bsdcpio.1
index e52546e6..786a7170 100644
--- a/cpio/bsdcpio.1
+++ b/cpio/bsdcpio.1
@@ -187,6 +187,11 @@ In input mode, this option is ignored.
Compress the archive with lz4-compatible compression before writing it.
In input mode, this option is ignored; lz4 compression is recognized
automatically on input.
+.It Fl Fl zstd
+(o mode only)
+Compress the archive with zstd-compatible compression before writing it.
+In input mode, this option is ignored; zstd compression is recognized
+automatically on input.
.It Fl Fl lzma
(o mode only)
Compress the file with lzma-compatible compression before writing it.
diff --git a/cpio/cmdline.c b/cpio/cmdline.c
index 0c10b2cd..4f371cee 100644
--- a/cpio/cmdline.c
+++ b/cpio/cmdline.c
@@ -75,6 +75,7 @@ static const struct option {
{ "list", 0, 't' },
{ "lrzip", 0, OPTION_LRZIP },
{ "lz4", 0, OPTION_LZ4 },
+ { "zstd", 0, OPTION_ZSTD },
{ "lzma", 0, OPTION_LZMA },
{ "lzop", 0, OPTION_LZOP },
{ "make-directories", 0, 'd' },
diff --git a/cpio/cpio.c b/cpio/cpio.c
index 5beedd0d..17d12ff6 100644
--- a/cpio/cpio.c
+++ b/cpio/cpio.c
@@ -267,6 +267,7 @@ main(int argc, char *argv[])
break;
case OPTION_LRZIP:
case OPTION_LZ4:
+ case OPTION_ZSTD:
case OPTION_LZMA: /* GNU tar, others */
case OPTION_LZOP: /* GNU tar, others */
cpio->compress = opt;
@@ -540,6 +541,9 @@ mode_out(struct cpio *cpio)
case OPTION_LZ4:
r = archive_write_add_filter_lz4(cpio->archive);
break;
+ case OPTION_ZSTD:
+ r = archive_write_add_filter_zstd(cpio->archive);
+ break;
case OPTION_LZMA:
r = archive_write_add_filter_lzma(cpio->archive);
break;
diff --git a/cpio/cpio.h b/cpio/cpio.h
index 1036dece..14a77c11 100644
--- a/cpio/cpio.h
+++ b/cpio/cpio.h
@@ -104,6 +104,7 @@ enum {
OPTION_INSECURE,
OPTION_LRZIP,
OPTION_LZ4,
+ OPTION_ZSTD,
OPTION_LZMA,
OPTION_LZOP,
OPTION_PASSPHRASE,
diff --git a/libarchive/archive_read_support_filter_zstd.c b/libarchive/archive_read_support_filter_zstd.c
index f9bf2e54..3a8c5405 100644
--- a/libarchive/archive_read_support_filter_zstd.c
+++ b/libarchive/archive_read_support_filter_zstd.c
@@ -221,12 +221,14 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
/* Try to fill the output buffer. */
while (out.pos < out.size && !state->eof) {
if (!state->in_stream) {
- if (ZSTD_isError(ZSTD_initDStream(state->dstream))) {
+ ret = ZSTD_initDStream(state->dstream);
+ if (ZSTD_isError(ret)) {
free(state->out_block);
free(state);
archive_set_error(&self->archive->archive,
ARCHIVE_ERRNO_MISC,
- "Error initializing zstd decompressor");
+ "Error initializing zstd decompressor: %s",
+ ZSTD_getErrorName(ret));
return (ARCHIVE_FATAL);
}
state->in_stream = 1;
@@ -236,7 +238,7 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
if (in.src == NULL && avail_in <= 0) {
archive_set_error(&self->archive->archive,
ARCHIVE_ERRNO_MISC,
- "truncated zstd input");
+ "Truncated zstd input");
return (ARCHIVE_FATAL);
}
in.size = avail_in;
@@ -247,7 +249,8 @@ zstd_filter_read(struct archive_read_filter *self, const void **p)
if (ZSTD_isError(ret)) {
archive_set_error(&self->archive->archive,
ARCHIVE_ERRNO_MISC,
- "zstd decompression failed");
+ "Zstd decompression failed: %s",
+ ZSTD_getErrorName(ret));
return (ARCHIVE_FATAL);
}
diff --git a/libarchive/archive_write_add_filter.c b/libarchive/archive_write_add_filter.c
index 08f518ad..acefc69e 100644
--- a/libarchive/archive_write_add_filter.c
+++ b/libarchive/archive_write_add_filter.c
@@ -48,6 +48,7 @@ struct { int code; int (*setter)(struct archive *); } codes[] =
{ ARCHIVE_FILTER_GRZIP, archive_write_add_filter_grzip },
{ ARCHIVE_FILTER_LRZIP, archive_write_add_filter_lrzip },
{ ARCHIVE_FILTER_LZ4, archive_write_add_filter_lz4 },
+ { ARCHIVE_FILTER_ZSTD, archive_write_add_filter_zstd },
{ ARCHIVE_FILTER_LZIP, archive_write_add_filter_lzip },
{ ARCHIVE_FILTER_LZMA, archive_write_add_filter_lzma },
{ ARCHIVE_FILTER_LZOP, archive_write_add_filter_lzip },
diff --git a/libarchive/archive_write_add_filter_by_name.c b/libarchive/archive_write_add_filter_by_name.c
index 85a8d475..f3971bf5 100644
--- a/libarchive/archive_write_add_filter_by_name.c
+++ b/libarchive/archive_write_add_filter_by_name.c
@@ -52,6 +52,7 @@ struct { const char *name; int (*setter)(struct archive *); } names[] =
{ "gzip", archive_write_add_filter_gzip },
{ "lrzip", archive_write_add_filter_lrzip },
{ "lz4", archive_write_add_filter_lz4 },
+ { "zstd", archive_write_add_filter_zstd },
{ "lzip", archive_write_add_filter_lzip },
{ "lzma", archive_write_add_filter_lzma },
{ "lzop", archive_write_add_filter_lzop },
diff --git a/libarchive/archive_write_add_filter_zstd.c b/libarchive/archive_write_add_filter_zstd.c
new file mode 100644
index 00000000..c47b2de0
--- /dev/null
+++ b/libarchive/archive_write_add_filter_zstd.c
@@ -0,0 +1,334 @@
+/*-
+ * Copyright (c) 2017 Sean Purcell
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "archive_platform.h"
+
+__FBSDID("$FreeBSD$");
+
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_ZSTD_H
+#include <zstd.h>
+#endif
+
+#include "archive.h"
+#include "archive_private.h"
+#include "archive_string.h"
+#include "archive_write_private.h"
+
+/* Don't compile this if we don't have zstd.h */
+
+struct private_data {
+ int compression_level;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ ZSTD_CStream *cstream;
+ int64_t total_in;
+ ZSTD_outBuffer out;
+#else
+ struct archive_write_program_data *pdata;
+#endif
+};
+
+static int archive_compressor_zstd_options(struct archive_write_filter *,
+ const char *, const char *);
+static int archive_compressor_zstd_open(struct archive_write_filter *);
+static int archive_compressor_zstd_write(struct archive_write_filter *,
+ const void *, size_t);
+static int archive_compressor_zstd_close(struct archive_write_filter *);
+static int archive_compressor_zstd_free(struct archive_write_filter *);
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+static int drive_compressor(struct archive_write_filter *,
+ struct private_data *, int, const void *, size_t);
+#endif
+
+
+/*
+ * Add a zstd compression filter to this write handle.
+ */
+int
+archive_write_add_filter_zstd(struct archive *_a)
+{
+ struct archive_write *a = (struct archive_write *)_a;
+ struct archive_write_filter *f = __archive_write_allocate_filter(_a);
+ struct private_data *data;
+ archive_check_magic(&a->archive, ARCHIVE_WRITE_MAGIC,
+ ARCHIVE_STATE_NEW, "archive_write_add_filter_zstd");
+
+ data = calloc(1, sizeof(*data));
+ if (data == NULL) {
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ f->data = data;
+ f->open = &archive_compressor_zstd_open;
+ f->options = &archive_compressor_zstd_options;
+ f->close = &archive_compressor_zstd_close;
+ f->free = &archive_compressor_zstd_free;
+ f->code = ARCHIVE_FILTER_ZSTD;
+ f->name = "zstd";
+ data->compression_level = 3; /* Default level used by the zstd CLI */
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ data->cstream = ZSTD_createCStream();
+ if (data->cstream == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM,
+ "Failed to allocate zstd compressor object");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+#else
+ data->pdata = __archive_write_program_allocate("zstd");
+ if (data->pdata == NULL) {
+ free(data);
+ archive_set_error(&a->archive, ENOMEM, "Out of memory");
+ return (ARCHIVE_FATAL);
+ }
+ data->compression_level = 0;
+ archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
+ "Using external zstd program");
+ return (ARCHIVE_WARN);
+#endif
+}
+
+static int
+archive_compressor_zstd_free(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+ ZSTD_freeCStream(data->cstream);
+ free(data->out.dst);
+#else
+ __archive_write_program_free(data->pdata);
+#endif
+ free(data);
+ f->data = NULL;
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Set write options.
+ */
+static int
+archive_compressor_zstd_options(struct archive_write_filter *f, const char *key,
+ const char *value)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ if (strcmp(key, "compression-level") == 0) {
+ int level = atoi(value);
+ if (level < 1 || level > ZSTD_maxCLevel()) {
+ return (ARCHIVE_WARN);
+ }
+ data->compression_level = level;
+ return (ARCHIVE_OK);
+ }
+
+ /* Note: The "warn" return is just to inform the options
+ * supervisor that we didn't handle it. It will generate
+ * a suitable error if no one used this option. */
+ return (ARCHIVE_WARN);
+}
+
+#if HAVE_ZSTD_H && HAVE_LIBZSTD
+/*
+ * Setup callback.
+ */
+static int
+archive_compressor_zstd_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ ret = __archive_write_open_filter(f->next_filter);
+ if (ret != ARCHIVE_OK)
+ return (ret);
+
+ if (data->out.dst == NULL) {
+ size_t bs = ZSTD_CStreamOutSize(), bpb;
+ if (f->archive->magic == ARCHIVE_WRITE_MAGIC) {
+ /* Buffer size should be a multiple number of
+ * the of bytes per block for performance. */
+ bpb = archive_write_get_bytes_per_block(f->archive);
+ if (bpb > bs)
+ bs = bpb;
+ else if (bpb != 0)
+ bs -= bs % bpb;
+ }
+ data->out.size = bs;
+ data->out.pos = 0;
+ data->out.dst
+ = (unsigned char *)malloc(data->out.size);
+ if (data->out.dst == NULL) {
+ archive_set_error(f->archive, ENOMEM,
+ "Can't allocate data for compression buffer");
+ return (ARCHIVE_FATAL);
+ }
+ }
+
+ f->write = archive_compressor_zstd_write;
+
+ if (ZSTD_isError(ZSTD_initCStream(data->cstream,
+ data->compression_level))) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Internal error initializing zstd compressor object");
+ return (ARCHIVE_FATAL);
+ }
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Write data to the compressed stream.
+ */
+static int
+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int ret;
+
+ /* Update statistics */
+ data->total_in += length;
+
+ if ((ret = drive_compressor(f, data, 0, buff, length)) != ARCHIVE_OK)
+ return (ret);
+
+ return (ARCHIVE_OK);
+}
+
+/*
+ * Finish the compression...
+ */
+static int
+archive_compressor_zstd_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ int r1, r2;
+
+ /* Finish zstd frame */
+ r1 = drive_compressor(f, data, 1, NULL, 0);
+
+ r2 = __archive_write_close_filter(f->next_filter);
+
+ return r1 < r2 ? r1 : r2;
+}
+
+/*
+ * Utility function to push input data through compressor,
+ * writing full output blocks as necessary.
+ *
+ * Note that this handles both the regular write case (finishing ==
+ * false) and the end-of-archive case (finishing == true).
+ */
+static int
+drive_compressor(struct archive_write_filter *f,
+ struct private_data *data, int finishing, const void *src, size_t length)
+{
+ int ret;
+ size_t zstdret;
+ ZSTD_inBuffer in = (ZSTD_inBuffer) { src, length, 0 };
+
+ for (;;) {
+ if (data->out.pos == data->out.size) {
+ ret = __archive_write_filter(f->next_filter,
+ data->out.dst, data->out.size);
+ if (ret != ARCHIVE_OK)
+ return (ARCHIVE_FATAL);
+ data->out.pos = 0;
+ }
+
+ /* If there's nothing to do, we're done. */
+ if (!finishing && in.pos == in.size)
+ return (ARCHIVE_OK);
+
+ if (!finishing) {
+ zstdret = ZSTD_compressStream(data->cstream,
+ &data->out, &in);
+ } else {
+ zstdret = ZSTD_endStream(data->cstream,
+ &data->out);
+ }
+
+ if (ZSTD_isError(zstdret)) {
+ archive_set_error(f->archive, ARCHIVE_ERRNO_MISC,
+ "Zstd compression failed: %s",
+ ZSTD_getErrorName(zstdret));
+ return (ARCHIVE_FATAL);
+ }
+
+ /* If we're finishing, 0 means nothing left to flush */
+ if (finishing && zstdret == 0) {
+ ret = __archive_write_filter(f->next_filter,
+ data->out.dst, data->out.pos);
+ return (ret);
+ }
+ }
+}
+
+#else /* HAVE_ZSTD_H && HAVE_LIBZSTD */
+
+static int
+archive_compressor_zstd_open(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+ struct archive_string as;
+ int r;
+
+ archive_string_init(&as);
+ archive_string_sprintf(&as, "zstd -%d", data->compression_level);
+
+ f->write = archive_compressor_zstd_write;
+ r = __archive_write_program_open(f, data->pdata, as.s);
+ archive_string_free(&as);
+ return (r);
+}
+
+static int
+archive_compressor_zstd_write(struct archive_write_filter *f, const void *buff,
+ size_t length)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_write(f, data->pdata, buff, length);
+}
+
+static int
+archive_compressor_zstd_close(struct archive_write_filter *f)
+{
+ struct private_data *data = (struct private_data *)f->data;
+
+ return __archive_write_program_close(f, data->pdata);
+}
+
+#endif /* HAVE_ZSTD_H && HAVE_LIBZSTD */
diff --git a/libarchive/archive_write_filter.3 b/libarchive/archive_write_filter.3
index e1d18915..896cd242 100644
--- a/libarchive/archive_write_filter.3
+++ b/libarchive/archive_write_filter.3
@@ -36,6 +36,7 @@
.Nm archive_write_add_filter_gzip ,
.Nm archive_write_add_filter_lrzip ,
.Nm archive_write_add_filter_lz4 ,
+.Nm archive_write_add_filter_zstd ,
.Nm archive_write_add_filter_lzip ,
.Nm archive_write_add_filter_lzma ,
.Nm archive_write_add_filter_lzop ,
@@ -63,6 +64,8 @@ Streaming Archive Library (libarchive, -larchive)
.Ft int
.Fn archive_write_add_filter_lz4 "struct archive *"
.Ft int
+.Fn archive_write_add_filter_zstd "struct archive *"
+.Ft int
.Fn archive_write_add_filter_lzip "struct archive *"
.Ft int
.Fn archive_write_add_filter_lzma "struct archive *"
@@ -85,6 +88,7 @@ Streaming Archive Library (libarchive, -larchive)
.Fn archive_write_add_filter_gzip ,
.Fn archive_write_add_filter_lrzip ,
.Fn archive_write_add_filter_lz4 ,
+.Fn archive_write_add_filter_zstd ,
.Fn archive_write_add_filter_lzip ,
.Fn archive_write_add_filter_lzma ,
.Fn archive_write_add_filter_lzop ,
diff --git a/tar/bsdtar.1 b/tar/bsdtar.1
index cdc317b6..89b50779 100644
--- a/tar/bsdtar.1
+++ b/tar/bsdtar.1
@@ -342,6 +342,10 @@ In extract or list modes, this option is ignored.
Compress the archive with lz4-compatible compression before writing it.
In input mode, this option is ignored; lz4 compression is recognized
automatically on input.
+.It Fl Fl zstd
+Compress the archive with zstd-compatible compression before writing it.
+In input mode, this option is ignored; lz4 compression is recognized
+automatically on input.
.It Fl Fl lzma
(c mode only) Compress the resulting archive with the original LZMA algorithm.
Use of this option is discouraged and new archives should be created with
@@ -577,6 +581,8 @@ A decimal integer from 4 to 7 specifying the lz4 compression block size
.It Cm lz4:block-dependence
Use the previous block of the block being compressed for
a compression dictionary to improve compression ratio.
+.It Cm zstd:compression-level
+A decimal integer from 1 to 22 specifying the zstd compression level.
.It Cm lzop:compression-level
A decimal integer from 1 to 9 specifying the lzop compression level.
.It Cm xz:compression-level
diff --git a/tar/bsdtar.c b/tar/bsdtar.c
index 9fc68332..1b9851f9 100644
--- a/tar/bsdtar.c
+++ b/tar/bsdtar.c
@@ -416,6 +416,7 @@ main(int argc, char **argv)
break;
case OPTION_LRZIP:
case OPTION_LZ4:
+ case OPTION_ZSTD:
case OPTION_LZIP: /* GNU tar beginning with 1.23 */
case OPTION_LZMA: /* GNU tar beginning with 1.20 */
case OPTION_LZOP: /* GNU tar beginning with 1.21 */
@@ -427,9 +428,10 @@ main(int argc, char **argv)
switch (opt) {
case OPTION_LRZIP: compression_name = "lrzip"; break;
case OPTION_LZ4: compression_name = "lz4"; break;
- case OPTION_LZIP: compression_name = "lzip"; break;
- case OPTION_LZMA: compression_name = "lzma"; break;
- case OPTION_LZOP: compression_name = "lzop"; break;
+ case OPTION_ZSTD: compression_name = "zstd"; break;
+ case OPTION_LZIP: compression_name = "lzip"; break;
+ case OPTION_LZMA: compression_name = "lzma"; break;
+ case OPTION_LZOP: compression_name = "lzop"; break;
}
break;
case 'm': /* SUSv2 */
diff --git a/tar/bsdtar.h b/tar/bsdtar.h
index 10a2cf2f..304fb568 100644
--- a/tar/bsdtar.h
+++ b/tar/bsdtar.h
@@ -147,6 +147,7 @@ enum {
OPTION_KEEP_NEWER_FILES,
OPTION_LRZIP,
OPTION_LZ4,
+ OPTION_ZSTD,
OPTION_LZIP,
OPTION_LZMA,
OPTION_LZOP,
diff --git a/tar/cmdline.c b/tar/cmdline.c
index e36c545b..23388db0 100644
--- a/tar/cmdline.c
+++ b/tar/cmdline.c
@@ -107,6 +107,7 @@ static const struct bsdtar_option {
{ "list", 0, 't' },
{ "lrzip", 0, OPTION_LRZIP },
{ "lz4", 0, OPTION_LZ4 },
+ { "zstd", 0, OPTION_ZSTD },
{ "lzip", 0, OPTION_LZIP },
{ "lzma", 0, OPTION_LZMA },
{ "lzop", 0, OPTION_LZOP },
diff --git a/tar/creation_set.c b/tar/creation_set.c
index 24cf3fcd..e246c0a5 100644
--- a/tar/creation_set.c
+++ b/tar/creation_set.c
@@ -76,13 +76,14 @@ get_filter_code(const char *suffix)
{ ".lrz", "lrzip" },
{ ".lz", "lzip" },
{ ".lz4", "lz4" },
+ { ".zstd", "zstd"},
{ ".lzo", "lzop" },
{ ".lzma", "lzma" },
{ ".uu", "uuencode" },
{ ".xz", "xz" },
{ NULL, NULL }
};
-
+
return get_suffix_code(filters, suffix);
}