aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2026-03-24 12:15:33 +0000
committerRon Yorston <rmy@pobox.com>2026-03-24 12:15:33 +0000
commitaaae1865727bcd53da581340456de34dfb182d5c (patch)
treeb0be881225fad8bcb14977854b248539b7d81ea2
parent052807cc3330b3c5c1fb60b93ad0d2039d6142df (diff)
downloadbusybox-w32-zstd.tar.gz
busybox-w32-zstd.tar.bz2
busybox-w32-zstd.zip
archival: support zstd in rpm and dpkgzstd
-rw-r--r--archival/Config.src4
-rw-r--r--archival/dpkg.c6
-rw-r--r--archival/dpkg_deb.c4
-rw-r--r--archival/libarchive/Kbuild.src1
-rw-r--r--archival/libarchive/decompress_unzstd.c11
-rw-r--r--archival/libarchive/filter_accept_list_reassign.c6
-rw-r--r--archival/libarchive/get_header_tar_zstd.c20
-rw-r--r--archival/libarchive/open_transformer.c5
-rw-r--r--archival/tar.c18
-rw-r--r--include/bb_archive.h1
-rwxr-xr-xtestsuite/tar.tests22
11 files changed, 78 insertions, 20 deletions
diff --git a/archival/Config.src b/archival/Config.src
index 1dbb0f3fa..bd75f0f65 100644
--- a/archival/Config.src
+++ b/archival/Config.src
@@ -27,7 +27,9 @@ config FEATURE_SEAMLESS_Z
27 27
28config FEATURE_SEAMLESS_ZSTD 28config FEATURE_SEAMLESS_ZSTD
29 bool "Make tar, rpm, modprobe etc understand .zst data" 29 bool "Make tar, rpm, modprobe etc understand .zst data"
30 default y 30 default n
31 help
32 This requires external zstd and unzstd binaries.
31 33
32INSERT 34INSERT
33 35
diff --git a/archival/dpkg.c b/archival/dpkg.c
index a53da8df1..c007633dd 100644
--- a/archival/dpkg.c
+++ b/archival/dpkg.c
@@ -1510,6 +1510,9 @@ static void init_archive_deb_control(archive_handle_t *ar_handle)
1510#if ENABLE_FEATURE_SEAMLESS_XZ 1510#if ENABLE_FEATURE_SEAMLESS_XZ
1511 llist_add_to(&(ar_handle->accept), (char*)"control.tar.xz"); 1511 llist_add_to(&(ar_handle->accept), (char*)"control.tar.xz");
1512#endif 1512#endif
1513#if ENABLE_FEATURE_SEAMLESS_ZSTD
1514 llist_add_to(&(ar_handle->accept), (char*)"control.tar.zst");
1515#endif
1513 1516
1514 /* Assign the tar handle as a subarchive of the ar handle */ 1517 /* Assign the tar handle as a subarchive of the ar handle */
1515 ar_handle->dpkg__sub_archive = tar_handle; 1518 ar_handle->dpkg__sub_archive = tar_handle;
@@ -1537,6 +1540,9 @@ static void init_archive_deb_data(archive_handle_t *ar_handle)
1537#if ENABLE_FEATURE_SEAMLESS_XZ 1540#if ENABLE_FEATURE_SEAMLESS_XZ
1538 llist_add_to(&(ar_handle->accept), (char*)"data.tar.xz"); 1541 llist_add_to(&(ar_handle->accept), (char*)"data.tar.xz");
1539#endif 1542#endif
1543#if ENABLE_FEATURE_SEAMLESS_ZSTD
1544 llist_add_to(&(ar_handle->accept), (char*)"data.tar.zst");
1545#endif
1540 1546
1541 /* Assign the tar handle as a subarchive of the ar handle */ 1547 /* Assign the tar handle as a subarchive of the ar handle */
1542 ar_handle->dpkg__sub_archive = tar_handle; 1548 ar_handle->dpkg__sub_archive = tar_handle;
diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c
index dda931169..0c60ae41a 100644
--- a/archival/dpkg_deb.c
+++ b/archival/dpkg_deb.c
@@ -77,6 +77,10 @@ int dpkg_deb_main(int argc UNUSED_PARAM, char **argv)
77 llist_add_to(&ar_archive->accept, (char*)"data.tar.xz"); 77 llist_add_to(&ar_archive->accept, (char*)"data.tar.xz");
78 llist_add_to(&control_tar_llist, (char*)"control.tar.xz"); 78 llist_add_to(&control_tar_llist, (char*)"control.tar.xz");
79#endif 79#endif
80#if ENABLE_FEATURE_SEAMLESS_ZSTD
81 llist_add_to(&ar_archive->accept, (char*)"data.tar.zst");
82 llist_add_to(&control_tar_llist, (char*)"control.tar.zst");
83#endif
80 84
81 /* Must have 1 or 2 args */ 85 /* Must have 1 or 2 args */
82 opt = getopt32(argv, "^" "cefXx" 86 opt = getopt32(argv, "^" "cefXx"
diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src
index 70c7fae5b..1af3cf61d 100644
--- a/archival/libarchive/Kbuild.src
+++ b/archival/libarchive/Kbuild.src
@@ -39,6 +39,7 @@ DPKG_FILES:= \
39 get_header_tar_bz2.o \ 39 get_header_tar_bz2.o \
40 get_header_tar_lzma.o \ 40 get_header_tar_lzma.o \
41 get_header_tar_xz.o \ 41 get_header_tar_xz.o \
42 get_header_tar_zstd.o \
42 43
43INSERT 44INSERT
44 45
diff --git a/archival/libarchive/decompress_unzstd.c b/archival/libarchive/decompress_unzstd.c
index 08930a3e5..c55ab8575 100644
--- a/archival/libarchive/decompress_unzstd.c
+++ b/archival/libarchive/decompress_unzstd.c
@@ -4,16 +4,9 @@
4IF_DESKTOP(long long) int FAST_FUNC 4IF_DESKTOP(long long) int FAST_FUNC
5unpack_zstd_stream(transformer_state_t *xstate) 5unpack_zstd_stream(transformer_state_t *xstate)
6{ 6{
7 char *argv[] = {(char *)"unzstd", NULL}; 7 // FIXME Provide an internal implementation of zstd decompression.
8 char *argv[] = {(char *)"unzstd", (char *)"-cf", (char *)"-", NULL};
8 9
9 /* Exec external unzstd. */
10#if BB_MMU
11 if (xstate->signature_skipped) {
12 /* If called from fork_transformer_with_no_sig() signature_skipped
13 * is just a flag, not the actual offset. But we know it's 4. */
14 xlseek(xstate->src_fd, -4, SEEK_CUR);
15 }
16#endif
17 xmove_fd(xstate->src_fd, STDIN_FILENO); 10 xmove_fd(xstate->src_fd, STDIN_FILENO);
18 xmove_fd(xstate->dst_fd, STDOUT_FILENO); 11 xmove_fd(xstate->dst_fd, STDOUT_FILENO);
19 BB_EXECVP_or_die(argv); 12 BB_EXECVP_or_die(argv);
diff --git a/archival/libarchive/filter_accept_list_reassign.c b/archival/libarchive/filter_accept_list_reassign.c
index 826c5c29d..a78186a6e 100644
--- a/archival/libarchive/filter_accept_list_reassign.c
+++ b/archival/libarchive/filter_accept_list_reassign.c
@@ -55,6 +55,12 @@ char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle)
55 archive_handle->dpkg__action_data_subarchive = get_header_tar_xz; 55 archive_handle->dpkg__action_data_subarchive = get_header_tar_xz;
56 return EXIT_SUCCESS; 56 return EXIT_SUCCESS;
57 } 57 }
58 if (ENABLE_FEATURE_SEAMLESS_ZSTD
59 && strcmp(name_ptr, "zst") == 0
60 ) {
61 archive_handle->dpkg__action_data_subarchive = get_header_tar_zstd;
62 return EXIT_SUCCESS;
63 }
58 } 64 }
59 return EXIT_FAILURE; 65 return EXIT_FAILURE;
60} 66}
diff --git a/archival/libarchive/get_header_tar_zstd.c b/archival/libarchive/get_header_tar_zstd.c
new file mode 100644
index 000000000..6f1b4b977
--- /dev/null
+++ b/archival/libarchive/get_header_tar_zstd.c
@@ -0,0 +1,20 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4 */
5#include "libbb.h"
6#include "bb_archive.h"
7
8char FAST_FUNC get_header_tar_zstd(archive_handle_t *archive_handle)
9{
10 /* Can't lseek over pipes */
11 archive_handle->seek = seek_by_read;
12
13 fork_transformer_with_sig(archive_handle->src_fd, unpack_zstd_stream, "unzstd");
14 archive_handle->offset = 0;
15 while (get_header_tar(archive_handle) == EXIT_SUCCESS)
16 continue;
17
18 /* Can only do one file at a time */
19 return EXIT_FAILURE;
20}
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c
index 9f41c7b4f..375e7b249 100644
--- a/archival/libarchive/open_transformer.c
+++ b/archival/libarchive/open_transformer.c
@@ -211,11 +211,14 @@ static transformer_state_t *setup_transformer_on_fd(int fd, int die_if_not_compr
211 } 211 }
212 212
213 if (ENABLE_FEATURE_SEAMLESS_ZSTD && xstate->magic.b16[0] == ZSTD_MAGIC1) { 213 if (ENABLE_FEATURE_SEAMLESS_ZSTD && xstate->magic.b16[0] == ZSTD_MAGIC1) {
214 xstate->signature_skipped = 4;
215 xread(fd, &xstate->magic.b16[1], 2); 214 xread(fd, &xstate->magic.b16[1], 2);
216 if (xstate->magic.b16[1] == ZSTD_MAGIC2) { 215 if (xstate->magic.b16[1] == ZSTD_MAGIC2) {
217 xstate->xformer = unpack_zstd_stream; 216 xstate->xformer = unpack_zstd_stream;
218 USE_FOR_NOMMU(xstate->xformer_prog = "unzstd";) 217 USE_FOR_NOMMU(xstate->xformer_prog = "unzstd";)
218 // FIXME We execute an external decompressor even on MMU.
219 // Force a seek back to the signature.
220 xstate->signature_skipped = 0;
221 xlseek(xstate->src_fd, -4, SEEK_CUR);
219 goto found_magic; 222 goto found_magic;
220 } 223 }
221 } 224 }
diff --git a/archival/tar.c b/archival/tar.c
index c3e9f0e0c..033e08105 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -856,8 +856,8 @@ enum {
856 IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,) 856 IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,)
857#if ENABLE_FEATURE_TAR_LONG_OPTIONS 857#if ENABLE_FEATURE_TAR_LONG_OPTIONS
858 OPTBIT_STRIP_COMPONENTS, 858 OPTBIT_STRIP_COMPONENTS,
859 IF_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA ,)
860 IF_FEATURE_SEAMLESS_ZSTD(OPTBIT_ZSTD ,) 859 IF_FEATURE_SEAMLESS_ZSTD(OPTBIT_ZSTD ,)
860 IF_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA ,)
861 OPTBIT_NORECURSION, 861 OPTBIT_NORECURSION,
862 IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,) 862 IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,)
863 OPTBIT_NUMERIC_OWNER, 863 OPTBIT_NUMERIC_OWNER,
@@ -884,8 +884,8 @@ enum {
884 OPT_AUTOCOMPRESS_BY_EXT = 1 << OPTBIT_AUTOCOMPRESS_BY_EXT, // a 884 OPT_AUTOCOMPRESS_BY_EXT = 1 << OPTBIT_AUTOCOMPRESS_BY_EXT, // a
885 OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m 885 OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m
886 OPT_STRIP_COMPONENTS = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_STRIP_COMPONENTS)) + 0, // strip-components 886 OPT_STRIP_COMPONENTS = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_STRIP_COMPONENTS)) + 0, // strip-components
887 OPT_LZMA = IF_FEATURE_TAR_LONG_OPTIONS(IF_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA))) + 0, // lzma
888 OPT_ZSTD = IF_FEATURE_TAR_LONG_OPTIONS(IF_FEATURE_SEAMLESS_ZSTD((1 << OPTBIT_ZSTD))) + 0, // zstd 887 OPT_ZSTD = IF_FEATURE_TAR_LONG_OPTIONS(IF_FEATURE_SEAMLESS_ZSTD((1 << OPTBIT_ZSTD))) + 0, // zstd
888 OPT_LZMA = IF_FEATURE_TAR_LONG_OPTIONS(IF_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA))) + 0, // lzma
889 OPT_NORECURSION = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NORECURSION )) + 0, // no-recursion 889 OPT_NORECURSION = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NORECURSION )) + 0, // no-recursion
890 OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command 890 OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command
891 OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner 891 OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner
@@ -932,13 +932,13 @@ static const char tar_longopts[] ALIGN1 =
932# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME 932# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME
933 "touch\0" No_argument "m" 933 "touch\0" No_argument "m"
934# endif 934# endif
935 "strip-components\0" Required_argument "\xf8" 935 "strip-components\0" Required_argument "\xf7"
936# if ENABLE_FEATURE_SEAMLESS_ZSTD
937 "zstd\0" No_argument "\xf8"
938# endif
936# if ENABLE_FEATURE_SEAMLESS_LZMA 939# if ENABLE_FEATURE_SEAMLESS_LZMA
937 "lzma\0" No_argument "\xf9" 940 "lzma\0" No_argument "\xf9"
938# endif 941# endif
939# if ENABLE_FEATURE_SEAMLESS_ZSTD
940 "zstd\0" No_argument "\xf7"
941# endif
942 "no-recursion\0" No_argument "\xfa" 942 "no-recursion\0" No_argument "\xfa"
943# if ENABLE_FEATURE_TAR_TO_COMMAND 943# if ENABLE_FEATURE_TAR_TO_COMMAND
944 "to-command\0" Required_argument "\xfb" 944 "to-command\0" Required_argument "\xfb"
@@ -1033,7 +1033,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1033 IF_FEATURE_SEAMLESS_Z( "Z" ) 1033 IF_FEATURE_SEAMLESS_Z( "Z" )
1034 "a" 1034 "a"
1035 IF_FEATURE_TAR_NOPRESERVE_TIME("m") 1035 IF_FEATURE_TAR_NOPRESERVE_TIME("m")
1036 IF_FEATURE_TAR_LONG_OPTIONS("\xf8:") // --strip-components 1036 IF_FEATURE_TAR_LONG_OPTIONS("\xf7:") // --strip-components
1037 "\0" 1037 "\0"
1038 "tt:vv:" // count -t,-v 1038 "tt:vv:" // count -t,-v
1039#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM 1039#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
@@ -1043,7 +1043,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1043 IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive 1043 IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
1044 IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive 1044 IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive
1045#if ENABLE_FEATURE_TAR_LONG_OPTIONS 1045#if ENABLE_FEATURE_TAR_LONG_OPTIONS
1046 ":\xf8+" // --strip-components=NUM 1046 ":\xf7+" // --strip-components=NUM
1047#endif 1047#endif
1048 LONGOPTS 1048 LONGOPTS
1049 , &base_dir // -C dir 1049 , &base_dir // -C dir
@@ -1083,8 +1083,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
1083 showopt(OPT_AUTOCOMPRESS_BY_EXT); 1083 showopt(OPT_AUTOCOMPRESS_BY_EXT);
1084 showopt(OPT_NOPRESERVE_TIME ); 1084 showopt(OPT_NOPRESERVE_TIME );
1085 showopt(OPT_STRIP_COMPONENTS); 1085 showopt(OPT_STRIP_COMPONENTS);
1086 showopt(OPT_LZMA );
1087 showopt(OPT_ZSTD ); 1086 showopt(OPT_ZSTD );
1087 showopt(OPT_LZMA );
1088 showopt(OPT_NORECURSION ); 1088 showopt(OPT_NORECURSION );
1089 showopt(OPT_2COMMAND ); 1089 showopt(OPT_2COMMAND );
1090 showopt(OPT_NUMERIC_OWNER ); 1090 showopt(OPT_NUMERIC_OWNER );
diff --git a/include/bb_archive.h b/include/bb_archive.h
index eb784d464..916c51bab 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -213,6 +213,7 @@ char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC;
213char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC; 213char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC;
214char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC; 214char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC;
215char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC; 215char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC;
216char get_header_tar_zstd(archive_handle_t *archive_handle) FAST_FUNC;
216 217
217void seek_by_jump(int fd, off_t amount) FAST_FUNC; 218void seek_by_jump(int fd, off_t amount) FAST_FUNC;
218void seek_by_read(int fd, off_t amount) FAST_FUNC; 219void seek_by_read(int fd, off_t amount) FAST_FUNC;
diff --git a/testsuite/tar.tests b/testsuite/tar.tests
index 0f2e89112..d2155add7 100755
--- a/testsuite/tar.tests
+++ b/testsuite/tar.tests
@@ -245,6 +245,28 @@ SKIP=
245cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null 245cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
246 246
247mkdir tar.tempdir && cd tar.tempdir || exit 1 247mkdir tar.tempdir && cd tar.tempdir || exit 1
248# Do we detect ZSTD-compressed data (even w/o .tar.zst extension)?
249# (uuencoded hello_world.tar.zst contains one empty file named "hello_world")
250# Currently we require an external unzstd program to be available.
251optional UUDECODE FEATURE_TAR_AUTODETECT FEATURE_SEAMLESS_ZSTD
252command -v unzstd >/dev/null || SKIP=1
253testing "tar extract tar.zst" "\
254uudecode -o input && tar tf input && echo Ok
255" "\
256hello_world
257Ok
258" \
259"" "\
260begin-base64 644 hello_world.tar.zst
261KLUv/QRYpQIA5ANoZWxsb193b3JsZAAwMDAwNjQ0ADAwMDE3NTAAMTUxNjA0
262NTQwMzAAMDExNDc3ACAwAHVzdGFyICAAcm15AAcAtAbwMBQOAJMCkBq8wApX
263awJ0ak0T/A==
264====
265"
266SKIP=
267cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null
268
269mkdir tar.tempdir && cd tar.tempdir || exit 1
248# On extract, everything up to and including last ".." component is stripped 270# On extract, everything up to and including last ".." component is stripped
249optional FEATURE_TAR_CREATE 271optional FEATURE_TAR_CREATE
250testing "tar strips /../ on extract" "\ 272testing "tar strips /../ on extract" "\