Skip to content

Commit ffa66ac

Browse files
committed
archival: add support for zstd compressed data
Status: not yet peer-reviewed, patch did not applied cleanly zstd compression of tarballs and distribution packages is more common. BusyBox doesn't include zstd code so use external zstd and unzstd programs to support it. Due to the requirement for external utilities the feature isn't enabled by default. With this change BusyBox tar, rpm and dpkg applets can handle zstd compressed data. When the feature is enabled: function old new delta setup_transformer_on_fd 126 192 +66 tar_main 1114 1176 +62 .rodata 102483 102544 +61 get_header_tar_zstd - 53 +53 unpack_zstd_stream - 47 +47 filter_accept_list_reassign 217 246 +29 packed_usage 35215 35239 +24 dpkg_deb_main 395 415 +20 unpack_package 641 654 +13 init_archive_deb_control 75 88 +13 tar_longopts 314 321 +7 ------------------------------------------------------------------------------ (add/remove: 4/0 grow/shrink: 9/0 up/down: 395/0) Total: 395 bytes Signed-off-by: Ron Yorston <rmy@pobox.com> Signed-off-by: Roberto A. Foglietta <roberto.foglietta@gmail.com>
1 parent d07b625 commit ffa66ac

11 files changed

Lines changed: 124 additions & 6 deletions

File tree

archival/Config.src

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ config FEATURE_SEAMLESS_Z
2525
bool "Make tar, rpm, modprobe etc understand .Z data"
2626
default n # it is ancient
2727

28+
config FEATURE_SEAMLESS_ZSTD
29+
bool "Make tar, rpm, modprobe etc understand .zst data"
30+
default n
31+
help
32+
This requires external zstd and unzstd binaries.
33+
2834
INSERT
2935

3036
config FEATURE_LZMA_FAST

archival/dpkg.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,6 +1505,9 @@ static void init_archive_deb_control(archive_handle_t *ar_handle)
15051505
#if ENABLE_FEATURE_SEAMLESS_XZ
15061506
llist_add_to(&(ar_handle->accept), (char*)"control.tar.xz");
15071507
#endif
1508+
#if ENABLE_FEATURE_SEAMLESS_ZSTD
1509+
llist_add_to(&(ar_handle->accept), (char*)"control.tar.zst");
1510+
#endif
15081511

15091512
/* Assign the tar handle as a subarchive of the ar handle */
15101513
ar_handle->dpkg__sub_archive = tar_handle;
@@ -1532,6 +1535,9 @@ static void init_archive_deb_data(archive_handle_t *ar_handle)
15321535
#if ENABLE_FEATURE_SEAMLESS_XZ
15331536
llist_add_to(&(ar_handle->accept), (char*)"data.tar.xz");
15341537
#endif
1538+
#if ENABLE_FEATURE_SEAMLESS_ZSTD
1539+
llist_add_to(&(ar_handle->accept), (char*)"data.tar.zst");
1540+
#endif
15351541

15361542
/* Assign the tar handle as a subarchive of the ar handle */
15371543
ar_handle->dpkg__sub_archive = tar_handle;

archival/dpkg_deb.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ int dpkg_deb_main(int argc UNUSED_PARAM, char **argv)
7777
llist_add_to(&ar_archive->accept, (char*)"data.tar.xz");
7878
llist_add_to(&control_tar_llist, (char*)"control.tar.xz");
7979
#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
8084

8185
/* Must have 1 or 2 args */
8286
opt = getopt32(argv, "^" "cefXx"

archival/libarchive/Kbuild.src

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ DPKG_FILES:= \
3939
get_header_tar_bz2.o \
4040
get_header_tar_lzma.o \
4141
get_header_tar_xz.o \
42+
get_header_tar_zstd.o
4243

4344
INSERT
4445

@@ -89,6 +90,7 @@ lib-$(CONFIG_FEATURE_SEAMLESS_GZ) += open_transformer.o decompress_gunzip.
8990
lib-$(CONFIG_FEATURE_SEAMLESS_BZ2) += open_transformer.o decompress_bunzip2.o
9091
lib-$(CONFIG_FEATURE_SEAMLESS_LZMA) += open_transformer.o decompress_unlzma.o
9192
lib-$(CONFIG_FEATURE_SEAMLESS_XZ) += open_transformer.o decompress_unxz.o
93+
lib-$(CONFIG_FEATURE_SEAMLESS_ZSTD) += open_transformer.o decompress_unzstd.o
9294
lib-$(CONFIG_FEATURE_COMPRESS_USAGE) += open_transformer.o decompress_bunzip2.o
9395
lib-$(CONFIG_FEATURE_COMPRESS_BBCONFIG) += open_transformer.o decompress_bunzip2.o
9496
lib-$(CONFIG_FEATURE_SH_EMBEDDED_SCRIPTS) += open_transformer.o decompress_bunzip2.o
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "libbb.h"
2+
#include "bb_archive.h"
3+
4+
IF_DESKTOP(long long) int FAST_FUNC
5+
unpack_zstd_stream(transformer_state_t *xstate)
6+
{
7+
// FIXME Provide an internal implementation of zstd decompression.
8+
char *argv[] = {(char *)"unzstd", (char *)"-cf", (char *)"-", NULL};
9+
10+
xmove_fd(xstate->src_fd, STDIN_FILENO);
11+
xmove_fd(xstate->dst_fd, STDOUT_FILENO);
12+
BB_EXECVP_or_die(argv);
13+
}

archival/libarchive/filter_accept_list_reassign.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle)
5555
archive_handle->dpkg__action_data_subarchive = get_header_tar_xz;
5656
return EXIT_SUCCESS;
5757
}
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+
}
5864
}
5965
return EXIT_FAILURE;
6066
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
8+
char 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+
}

archival/libarchive/open_transformer.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,11 +202,24 @@ static transformer_state_t *setup_transformer_on_fd(int fd, int die_if_not_compr
202202
goto found_magic;
203203
}
204204
}
205+
if (ENABLE_FEATURE_SEAMLESS_ZSTD && xstate->magic.b16[0] == ZSTD_MAGIC1) {
206+
xread(fd, &xstate->magic.b16[1], 2);
207+
if (xstate->magic.b16[1] == ZSTD_MAGIC2) {
208+
xstate->xformer = unpack_zstd_stream;
209+
USE_FOR_NOMMU(xstate->xformer_prog = "unzstd";)
210+
// FIXME We execute an external decompressor even on MMU.
211+
// Force a seek back to the signature.
212+
xstate->signature_skipped = 0;
213+
xlseek(xstate->src_fd, -4, SEEK_CUR);
214+
goto found_magic;
215+
}
216+
}
205217

206218
/* No known magic seen */
207219
if (die_if_not_compressed)
208220
bb_simple_error_msg_and_die("no gzip"
209221
IF_FEATURE_SEAMLESS_BZ2("/bzip2")
222+
IF_FEATURE_SEAMLESS_ZSTD("/zstd")
210223
IF_FEATURE_SEAMLESS_XZ("/xz")
211224
" magic");
212225

archival/tar.c

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
//config:config FEATURE_TAR_AUTODETECT
4444
//config: bool "Autodetect compressed tarballs"
4545
//config: default y
46-
//config: depends on TAR && (FEATURE_SEAMLESS_Z || FEATURE_SEAMLESS_GZ || FEATURE_SEAMLESS_BZ2 || FEATURE_SEAMLESS_LZMA || FEATURE_SEAMLESS_XZ)
46+
//config: depends on TAR && (FEATURE_SEAMLESS_Z || FEATURE_SEAMLESS_GZ || FEATURE_SEAMLESS_BZ2 || FEATURE_SEAMLESS_LZMA || FEATURE_SEAMLESS_XZ || FEATURE_SEAMLESS_ZSTD)
4747
//config: help
4848
//config: With this option tar can automatically detect compressed
4949
//config: tarballs. Currently it works only on files (not pipes etc).
@@ -787,6 +787,11 @@ static llist_t *append_file_list_to_list(llist_t *list)
787787
//usage: "\n --lzma (De)compress using lzma"
788788
//usage: )
789789
//usage: )
790+
//usage: IF_FEATURE_SEAMLESS_ZSTD(
791+
//usage: IF_FEATURE_TAR_LONG_OPTIONS(
792+
//usage: "\n --zstd (De)compress using zstd"
793+
//usage: )
794+
//usage: )
790795
//usage: "\n -a (De)compress based on extension"
791796
//usage: IF_FEATURE_TAR_CREATE(
792797
//usage: "\n -h Follow symlinks"
@@ -827,6 +832,7 @@ enum {
827832
IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,)
828833
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
829834
OPTBIT_STRIP_COMPONENTS,
835+
IF_FEATURE_SEAMLESS_ZSTD(OPTBIT_ZSTD ,)
830836
IF_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA ,)
831837
OPTBIT_NORECURSION,
832838
IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,)
@@ -854,14 +860,15 @@ enum {
854860
OPT_AUTOCOMPRESS_BY_EXT = 1 << OPTBIT_AUTOCOMPRESS_BY_EXT, // a
855861
OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m
856862
OPT_STRIP_COMPONENTS = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_STRIP_COMPONENTS)) + 0, // strip-components
863+
OPT_ZSTD = IF_FEATURE_TAR_LONG_OPTIONS(IF_FEATURE_SEAMLESS_ZSTD((1 << OPTBIT_ZSTD))) + 0, // zstd
857864
OPT_LZMA = IF_FEATURE_TAR_LONG_OPTIONS(IF_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA))) + 0, // lzma
858865
OPT_NORECURSION = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NORECURSION )) + 0, // no-recursion
859866
OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command
860867
OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner
861868
OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions
862869
OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite
863870

864-
OPT_ANY_COMPRESS = (OPT_BZIP2 | OPT_LZMA | OPT_GZIP | OPT_XZ | OPT_COMPRESS),
871+
OPT_ANY_COMPRESS = (OPT_BZIP2 | OPT_LZMA | OPT_GZIP | OPT_XZ | OPT_COMPRESS | OPT_ZSTD),
865872
};
866873
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
867874
static const char tar_longopts[] ALIGN1 =
@@ -901,7 +908,10 @@ static const char tar_longopts[] ALIGN1 =
901908
# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME
902909
"touch\0" No_argument "m"
903910
# endif
904-
"strip-components\0" Required_argument "\xf8"
911+
"strip-components\0" Required_argument "\xf7"
912+
# if ENABLE_FEATURE_SEAMLESS_ZSTD
913+
"zstd\0" No_argument "\xf8"
914+
# endif
905915
# if ENABLE_FEATURE_SEAMLESS_LZMA
906916
"lzma\0" No_argument "\xf9"
907917
# endif
@@ -999,7 +1009,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
9991009
IF_FEATURE_SEAMLESS_Z( "Z" )
10001010
"a"
10011011
IF_FEATURE_TAR_NOPRESERVE_TIME("m")
1002-
IF_FEATURE_TAR_LONG_OPTIONS("\xf8:") // --strip-components
1012+
IF_FEATURE_TAR_LONG_OPTIONS("\xf7:") // --strip-components
10031013
"\0"
10041014
"tt:vv:" // count -t,-v
10051015
#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
@@ -1009,7 +1019,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
10091019
IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
10101020
IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive
10111021
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
1012-
":\xf8+" // --strip-components=NUM
1022+
":\xf7+" // --strip-components=NUM
10131023
#endif
10141024
LONGOPTS
10151025
, &base_dir // -C dir
@@ -1049,6 +1059,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
10491059
showopt(OPT_AUTOCOMPRESS_BY_EXT);
10501060
showopt(OPT_NOPRESERVE_TIME );
10511061
showopt(OPT_STRIP_COMPONENTS);
1062+
showopt(OPT_ZSTD );
10521063
showopt(OPT_LZMA );
10531064
showopt(OPT_NORECURSION );
10541065
showopt(OPT_2COMMAND );
@@ -1170,7 +1181,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
11701181
} else {
11711182
tar_handle->src_fd = xopen(tar_filename, flags);
11721183
#if ENABLE_FEATURE_TAR_CREATE
1173-
if ((OPT_GZIP | OPT_BZIP2 | OPT_XZ | OPT_LZMA) != 0 /* at least one is config-enabled */
1184+
if ((OPT_GZIP | OPT_BZIP2 | OPT_XZ | OPT_LZMA | OPT_ZSTD) != 0 /* at least one is config-enabled */
11741185
&& (opt & OPT_AUTOCOMPRESS_BY_EXT)
11751186
&& flags != O_RDONLY
11761187
) {
@@ -1182,6 +1193,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
11821193
opt |= OPT_XZ;
11831194
if (OPT_LZMA != 0 && is_suffixed_with(tar_filename, "lzma"))
11841195
opt |= OPT_LZMA;
1196+
if (OPT_ZSTD != 0 && is_suffixed_with(tar_filename, "zst"))
1197+
opt |= OPT_ZSTD;
11851198
}
11861199
#endif
11871200
}
@@ -1206,6 +1219,8 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
12061219
zipMode = "lzma";
12071220
if (opt & OPT_XZ)
12081221
zipMode = "xz";
1222+
if (opt & OPT_ZSTD)
1223+
zipMode = "zstd";
12091224
# endif
12101225
tbInfo = xzalloc(sizeof(*tbInfo));
12111226
tbInfo->tarFd = tar_handle->src_fd;
@@ -1246,6 +1261,10 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
12461261
USE_FOR_MMU(IF_FEATURE_SEAMLESS_XZ(xformer = unpack_xz_stream;))
12471262
USE_FOR_NOMMU(xformer_prog = "unxz";)
12481263
}
1264+
if (opt & OPT_ZSTD) {
1265+
USE_FOR_MMU(xformer = unpack_zstd_stream;)
1266+
USE_FOR_NOMMU(xformer_prog = "unzstd";)
1267+
}
12491268

12501269
fork_transformer_with_sig(tar_handle->src_fd, xformer, xformer_prog);
12511270
/* Can't lseek over pipes */

include/bb_archive.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ enum {
1717
/* (unsigned) cast suppresses "integer overflow in expression" warning */
1818
XZ_MAGIC1a = 256 * (unsigned)(256 * (256 * 0xfd + '7') + 'z') + 'X',
1919
XZ_MAGIC2a = 256 * 'Z' + 0,
20+
/* .zst signature: 0x28, 0xb5, 0x2f, 0xfd */
21+
ZSTD_MAGIC1 = 256 * 0x28 + 0xb5,
22+
ZSTD_MAGIC2 = 256 * 0x2f + 0xfd,
2023
#else
2124
COMPRESS_MAGIC = 0x9d1f,
2225
GZIP_MAGIC = 0x8b1f,
@@ -25,6 +28,8 @@ enum {
2528
XZ_MAGIC2 = 'z' + ('X' + ('Z' + 0 * 256) * 256) * 256,
2629
XZ_MAGIC1a = 0xfd + ('7' + ('z' + 'X' * 256) * 256) * 256,
2730
XZ_MAGIC2a = 'Z' + 0 * 256,
31+
ZSTD_MAGIC1 = 0x28 + 0xb5 * 256,
32+
ZSTD_MAGIC2 = 0x2f + 0xfd * 256,
2833
#endif
2934
};
3035

@@ -198,6 +203,7 @@ char get_header_tar_gz(archive_handle_t *archive_handle) FAST_FUNC;
198203
char get_header_tar_bz2(archive_handle_t *archive_handle) FAST_FUNC;
199204
char get_header_tar_lzma(archive_handle_t *archive_handle) FAST_FUNC;
200205
char get_header_tar_xz(archive_handle_t *archive_handle) FAST_FUNC;
206+
char get_header_tar_zstd(archive_handle_t *archive_handle) FAST_FUNC;
201207

202208
void seek_by_jump(int fd, off_t amount) FAST_FUNC;
203209
void seek_by_read(int fd, off_t amount) FAST_FUNC;
@@ -255,6 +261,7 @@ IF_DESKTOP(long long) int unpack_gz_stream(transformer_state_t *xstate) FAST_FUN
255261
IF_DESKTOP(long long) int unpack_bz2_stream(transformer_state_t *xstate) FAST_FUNC;
256262
IF_DESKTOP(long long) int unpack_lzma_stream(transformer_state_t *xstate) FAST_FUNC;
257263
IF_DESKTOP(long long) int unpack_xz_stream(transformer_state_t *xstate) FAST_FUNC;
264+
IF_DESKTOP(long long) int unpack_zstd_stream(transformer_state_t *xstate) FAST_FUNC;
258265

259266
char* append_ext(char *filename, const char *expected_ext) FAST_FUNC;
260267
int bbunpack(char **argv,

0 commit comments

Comments
 (0)