[PATCH v1] tar: add --occurrence[=NUM] option
Gavin Li
gavinli at thegavinli.com
Mon Aug 21 19:17:29 UTC 2023
Enable GNU tar's --occurrence option when FEATURE_TAR_LONG_OPTIONS is enabled.
Signed-off-by: Gavin Li <gavinli at thegavinli.com>
---
archival/ar.c | 6 +-
archival/cpio.c | 2 +-
archival/dpkg.c | 38 ++++++------
archival/dpkg_deb.c | 24 ++++----
archival/libarchive/Kbuild.src | 2 +
archival/libarchive/accept_add_to.c | 15 +++++
archival/libarchive/accept_add_to_end.c | 15 +++++
archival/libarchive/filter_accept_list.c | 2 +-
.../libarchive/filter_accept_list_reassign.c | 2 +-
.../libarchive/filter_accept_reject_list.c | 18 +++++-
archival/libarchive/get_header_tar.c | 7 +++
archival/tar.c | 59 ++++++++++++++-----
include/bb_archive.h | 14 ++++-
testsuite/tar/tar-handles-occurrence | 18 ++++++
14 files changed, 168 insertions(+), 54 deletions(-)
create mode 100644 archival/libarchive/accept_add_to.c
create mode 100644 archival/libarchive/accept_add_to_end.c
create mode 100644 testsuite/tar/tar-handles-occurrence
diff --git a/archival/ar.c b/archival/ar.c
index 320cbae72..f1df21e95 100644
--- a/archival/ar.c
+++ b/archival/ar.c
@@ -56,7 +56,7 @@
/* filter out entries with same names as specified on the command line */
static char FAST_FUNC filter_replaceable(archive_handle_t *handle)
{
- if (find_list_entry(handle->accept, handle->file_header->name))
+ if (find_list_entry((llist_t *)handle->accept, handle->file_header->name))
return EXIT_FAILURE;
return EXIT_SUCCESS;
@@ -124,7 +124,7 @@ static int write_ar_header(archive_handle_t *handle)
struct stat st;
int fd;
- fn = llist_pop(&handle->accept);
+ fn = llist_pop((llist_t **)&handle->accept);
if (!fn)
return -1;
@@ -287,7 +287,7 @@ int ar_main(int argc UNUSED_PARAM, char **argv)
if (*argv)
archive_handle->filter = filter_accept_list;
while (*argv) {
- llist_add_to_end(&archive_handle->accept, *argv++);
+ accept_add_to_end(&archive_handle->accept, *argv++);
}
#if ENABLE_FEATURE_AR_CREATE
diff --git a/archival/cpio.c b/archival/cpio.c
index f0d990048..4950d6ead 100644
--- a/archival/cpio.c
+++ b/archival/cpio.c
@@ -559,7 +559,7 @@ int cpio_main(int argc UNUSED_PARAM, char **argv)
while (*argv) {
archive_handle->filter = filter_accept_list;
- llist_add_to(&archive_handle->accept, *argv);
+ accept_add_to(&archive_handle->accept, *argv);
argv++;
}
diff --git a/archival/dpkg.c b/archival/dpkg.c
index 8031956e9..23e46e062 100644
--- a/archival/dpkg.c
+++ b/archival/dpkg.c
@@ -1495,15 +1495,15 @@ static void init_archive_deb_control(archive_handle_t *ar_handle)
tar_handle->src_fd = ar_handle->src_fd;
/* We don't care about data.tar.* or debian-binary, just control.tar.* */
- llist_add_to(&(ar_handle->accept), (char*)"control.tar");
+ accept_add_to(&(ar_handle->accept), (char*)"control.tar");
#if ENABLE_FEATURE_SEAMLESS_GZ
- llist_add_to(&(ar_handle->accept), (char*)"control.tar.gz");
+ accept_add_to(&(ar_handle->accept), (char*)"control.tar.gz");
#endif
#if ENABLE_FEATURE_SEAMLESS_BZ2
- llist_add_to(&(ar_handle->accept), (char*)"control.tar.bz2");
+ accept_add_to(&(ar_handle->accept), (char*)"control.tar.bz2");
#endif
#if ENABLE_FEATURE_SEAMLESS_XZ
- llist_add_to(&(ar_handle->accept), (char*)"control.tar.xz");
+ accept_add_to(&(ar_handle->accept), (char*)"control.tar.xz");
#endif
/* Assign the tar handle as a subarchive of the ar handle */
@@ -1519,18 +1519,18 @@ static void init_archive_deb_data(archive_handle_t *ar_handle)
tar_handle->src_fd = ar_handle->src_fd;
/* We don't care about control.tar.* or debian-binary, just data.tar.* */
- llist_add_to(&(ar_handle->accept), (char*)"data.tar");
+ accept_add_to(&(ar_handle->accept), (char*)"data.tar");
#if ENABLE_FEATURE_SEAMLESS_GZ
- llist_add_to(&(ar_handle->accept), (char*)"data.tar.gz");
+ accept_add_to(&(ar_handle->accept), (char*)"data.tar.gz");
#endif
#if ENABLE_FEATURE_SEAMLESS_BZ2
- llist_add_to(&(ar_handle->accept), (char*)"data.tar.bz2");
+ accept_add_to(&(ar_handle->accept), (char*)"data.tar.bz2");
#endif
#if ENABLE_FEATURE_SEAMLESS_LZMA
- llist_add_to(&(ar_handle->accept), (char*)"data.tar.lzma");
+ accept_add_to(&(ar_handle->accept), (char*)"data.tar.lzma");
#endif
#if ENABLE_FEATURE_SEAMLESS_XZ
- llist_add_to(&(ar_handle->accept), (char*)"data.tar.xz");
+ accept_add_to(&(ar_handle->accept), (char*)"data.tar.xz");
#endif
/* Assign the tar handle as a subarchive of the ar handle */
@@ -1545,7 +1545,7 @@ static void FAST_FUNC data_extract_to_buffer(archive_handle_t *archive_handle)
xread(archive_handle->src_fd, archive_handle->dpkg__buffer, size);
}
-static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, llist_t *myaccept)
+static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, accept_llist_t *myaccept)
{
ar_handle->dpkg__sub_archive->action_data = data_extract_to_buffer;
ar_handle->dpkg__sub_archive->accept = myaccept;
@@ -1557,7 +1557,7 @@ static char *deb_extract_control_file_to_buffer(archive_handle_t *ar_handle, lli
return ar_handle->dpkg__sub_archive->dpkg__buffer;
}
-static void append_control_file_to_llist(const char *package_name, const char *control_name, llist_t **ll)
+static void append_control_file_to_llist(const char *package_name, const char *control_name, accept_llist_t **ll)
{
FILE *fp;
char *filename, *line;
@@ -1567,7 +1567,7 @@ static void append_control_file_to_llist(const char *package_name, const char *c
free(filename);
if (fp != NULL) {
while ((line = xmalloc_fgetline(fp)) != NULL)
- llist_add_to(ll, line);
+ accept_add_to(ll, line);
fclose(fp);
}
}
@@ -1578,7 +1578,7 @@ static char FAST_FUNC filter_rename_config(archive_handle_t *archive_handle)
char *name_ptr = archive_handle->file_header->name + 1;
/* Is this file marked as config file? */
- if (!find_list_entry(archive_handle->accept, name_ptr))
+ if (!find_list_entry((llist_t *)archive_handle->accept, name_ptr))
return EXIT_SUCCESS; /* no */
fd = open(name_ptr, O_RDONLY);
@@ -1600,7 +1600,7 @@ static char FAST_FUNC filter_rename_config(archive_handle_t *archive_handle)
free(buf);
/* Is it changed after install? */
- if (find_list_entry(archive_handle->accept, md5line) == NULL) {
+ if (find_list_entry((llist_t *)archive_handle->accept, md5line) == NULL) {
printf("Warning: Creating %s as %s.dpkg-new\n", name_ptr, name_ptr);
archive_handle->file_header->name = xasprintf("%s.dpkg-new", archive_handle->file_header->name);
}
@@ -1660,8 +1660,8 @@ static void unpack_package(deb_file_t *deb_file)
char *list_filename;
archive_handle_t *archive_handle;
FILE *out_stream;
- llist_t *accept_list;
- llist_t *conffile_list;
+ accept_llist_t *accept_list;
+ accept_llist_t *conffile_list;
int i;
/* If existing version, remove it first */
@@ -1690,7 +1690,7 @@ static void unpack_package(deb_file_t *deb_file)
i = 0;
while (i < ARRAY_SIZE(all_control_files)) {
char *c = xasprintf("./%s", all_control_files[i]);
- llist_add_to(&accept_list, c);
+ accept_add_to(&accept_list, c);
i++;
}
archive_handle->dpkg__sub_archive->accept = accept_list;
@@ -1831,10 +1831,10 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv)
if (opt & (OPT_install | OPT_unpack)) {
/* -i/-u: require filename */
archive_handle_t *archive_handle;
- llist_t *control_list = NULL;
+ accept_llist_t *control_list = NULL;
/* Extract the control file */
- llist_add_to(&control_list, (char*)"./control");
+ accept_add_to(&control_list, (char*)"./control");
archive_handle = init_archive_deb_ar(argv[0]);
init_archive_deb_control(archive_handle);
deb_file[deb_count]->control_file = deb_extract_control_file_to_buffer(archive_handle, control_list);
diff --git a/archival/dpkg_deb.c b/archival/dpkg_deb.c
index dda931169..988c6bcad 100644
--- a/archival/dpkg_deb.c
+++ b/archival/dpkg_deb.c
@@ -47,7 +47,7 @@ int dpkg_deb_main(int argc UNUSED_PARAM, char **argv)
{
archive_handle_t *ar_archive;
archive_handle_t *tar_archive;
- llist_t *control_tar_llist = NULL;
+ accept_llist_t *control_tar_llist = NULL;
unsigned opt;
const char *extract_dir;
@@ -59,23 +59,23 @@ int dpkg_deb_main(int argc UNUSED_PARAM, char **argv)
ar_archive->dpkg__sub_archive = tar_archive;
ar_archive->filter = filter_accept_list_reassign;
- llist_add_to(&ar_archive->accept, (char*)"data.tar");
- llist_add_to(&control_tar_llist, (char*)"control.tar");
+ accept_add_to(&ar_archive->accept, (char*)"data.tar");
+ accept_add_to(&control_tar_llist, (char*)"control.tar");
#if ENABLE_FEATURE_SEAMLESS_GZ
- llist_add_to(&ar_archive->accept, (char*)"data.tar.gz");
- llist_add_to(&control_tar_llist, (char*)"control.tar.gz");
+ accept_add_to(&ar_archive->accept, (char*)"data.tar.gz");
+ accept_add_to(&control_tar_llist, (char*)"control.tar.gz");
#endif
#if ENABLE_FEATURE_SEAMLESS_BZ2
- llist_add_to(&ar_archive->accept, (char*)"data.tar.bz2");
- llist_add_to(&control_tar_llist, (char*)"control.tar.bz2");
+ accept_add_to(&ar_archive->accept, (char*)"data.tar.bz2");
+ accept_add_to(&control_tar_llist, (char*)"control.tar.bz2");
#endif
#if ENABLE_FEATURE_SEAMLESS_LZMA
- llist_add_to(&ar_archive->accept, (char*)"data.tar.lzma");
- llist_add_to(&control_tar_llist, (char*)"control.tar.lzma");
+ accept_add_to(&ar_archive->accept, (char*)"data.tar.lzma");
+ accept_add_to(&control_tar_llist, (char*)"control.tar.lzma");
#endif
#if ENABLE_FEATURE_SEAMLESS_XZ
- llist_add_to(&ar_archive->accept, (char*)"data.tar.xz");
- llist_add_to(&control_tar_llist, (char*)"control.tar.xz");
+ accept_add_to(&ar_archive->accept, (char*)"data.tar.xz");
+ accept_add_to(&control_tar_llist, (char*)"control.tar.xz");
#endif
/* Must have 1 or 2 args */
@@ -95,7 +95,7 @@ int dpkg_deb_main(int argc UNUSED_PARAM, char **argv)
/* Print the entire control file */
//TODO: standard tool accepts an optional list of fields to print
ar_archive->accept = control_tar_llist;
- llist_add_to(&(tar_archive->accept), (char*)"./control");
+ accept_add_to(&(tar_archive->accept), (char*)"./control");
tar_archive->filter = filter_accept_list;
tar_archive->action_data = data_extract_to_stdout;
if (extract_dir)
diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src
index d2f284b08..ccc83a9f0 100644
--- a/archival/libarchive/Kbuild.src
+++ b/archival/libarchive/Kbuild.src
@@ -7,6 +7,8 @@
lib-y:= common.o
COMMON_FILES:= \
+ accept_add_to.o \
+ accept_add_to_end.o \
\
data_skip.o \
data_extract_all.o \
diff --git a/archival/libarchive/accept_add_to.c b/archival/libarchive/accept_add_to.c
new file mode 100644
index 000000000..36310ba9d
--- /dev/null
+++ b/archival/libarchive/accept_add_to.c
@@ -0,0 +1,15 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "bb_archive.h"
+
+void FAST_FUNC accept_add_to(accept_llist_t **old_head, char *data)
+{
+ accept_llist_t *new_head = xzalloc(sizeof(accept_llist_t));
+
+ new_head->data = data;
+ new_head->link = *old_head;
+ *old_head = new_head;
+}
diff --git a/archival/libarchive/accept_add_to_end.c b/archival/libarchive/accept_add_to_end.c
new file mode 100644
index 000000000..8260ea23e
--- /dev/null
+++ b/archival/libarchive/accept_add_to_end.c
@@ -0,0 +1,15 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+#include "bb_archive.h"
+
+void FAST_FUNC accept_add_to_end(accept_llist_t **list_head, char *data)
+{
+ while (*list_head)
+ list_head = &(*list_head)->link;
+ *list_head = xzalloc(sizeof(accept_llist_t));
+ (*list_head)->data = data;
+ /*(*list_head)->link = NULL;*/
+}
diff --git a/archival/libarchive/filter_accept_list.c b/archival/libarchive/filter_accept_list.c
index 32f806574..0319ff927 100644
--- a/archival/libarchive/filter_accept_list.c
+++ b/archival/libarchive/filter_accept_list.c
@@ -12,7 +12,7 @@
*/
char FAST_FUNC filter_accept_list(archive_handle_t *archive_handle)
{
- if (find_list_entry(archive_handle->accept, archive_handle->file_header->name))
+ if (find_list_entry((llist_t *)archive_handle->accept, archive_handle->file_header->name))
return EXIT_SUCCESS;
return EXIT_FAILURE;
}
diff --git a/archival/libarchive/filter_accept_list_reassign.c b/archival/libarchive/filter_accept_list_reassign.c
index 826c5c29d..c6428f9c3 100644
--- a/archival/libarchive/filter_accept_list_reassign.c
+++ b/archival/libarchive/filter_accept_list_reassign.c
@@ -17,7 +17,7 @@
char FAST_FUNC filter_accept_list_reassign(archive_handle_t *archive_handle)
{
/* Check the file entry is in the accept list */
- if (find_list_entry(archive_handle->accept, archive_handle->file_header->name)) {
+ if (find_list_entry((llist_t *)archive_handle->accept, archive_handle->file_header->name)) {
const char *name_ptr;
/* Find extension */
diff --git a/archival/libarchive/filter_accept_reject_list.c b/archival/libarchive/filter_accept_reject_list.c
index 939e626fa..935e60c29 100644
--- a/archival/libarchive/filter_accept_reject_list.c
+++ b/archival/libarchive/filter_accept_reject_list.c
@@ -14,7 +14,7 @@ char FAST_FUNC filter_accept_reject_list(archive_handle_t *archive_handle)
{
const char *key;
const llist_t *reject_entry;
- const llist_t *accept_entry;
+ accept_llist_t *accept_entry;
key = archive_handle->file_header->name;
@@ -26,10 +26,24 @@ char FAST_FUNC filter_accept_reject_list(archive_handle_t *archive_handle)
/* Fail if an accept list was specified and the key wasnt in there */
if (archive_handle->accept) {
- accept_entry = find_list_entry2(archive_handle->accept, key);
+ accept_entry = (accept_llist_t *)find_list_entry2((llist_t *)archive_handle->accept, key);
if (!accept_entry) {
return EXIT_FAILURE;
}
+
+ /* Mark the file as seen */
+ accept_entry->tar__seen_count++;
+
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ /* Support tar --occurrence */
+ if (archive_handle->tar__occurrence) {
+ if (accept_entry->tar__seen_count == archive_handle->tar__occurrence) {
+ archive_handle->tar__occurrence_remaining--;
+ } else {
+ return EXIT_FAILURE;
+ }
+ }
+#endif
}
/* Accepted */
diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c
index cc6f3f0ad..9ae190609 100644
--- a/archival/libarchive/get_header_tar.c
+++ b/archival/libarchive/get_header_tar.c
@@ -176,6 +176,13 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
# define p_linkname 0
#endif
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ if (archive_handle->tar__occurrence && archive_handle->tar__occurrence_remaining == 0) {
+ /* We've found all of the occurrences we were looking for, signal end of archive */
+ return EXIT_FAILURE;
+ }
+#endif
+
#if ENABLE_FEATURE_TAR_GNU_EXTENSIONS || ENABLE_FEATURE_TAR_SELINUX
again:
#endif
diff --git a/archival/tar.c b/archival/tar.c
index d6ca6c1e0..7aa393dc2 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -655,7 +655,7 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
static NOINLINE int writeTarFile(
struct TarBallInfo *tbInfo,
int recurseFlags,
- const llist_t *filelist,
+ const accept_llist_t *filelist,
const char *gzip)
{
int errorFlag = FALSE;
@@ -715,9 +715,9 @@ static NOINLINE int writeTarFile(
#endif /* FEATURE_TAR_CREATE */
#if ENABLE_FEATURE_TAR_FROM
-static llist_t *append_file_list_to_list(llist_t *list)
+static accept_llist_t *append_file_list_to_list(llist_t *list)
{
- llist_t *newlist = NULL;
+ accept_llist_t *newlist = NULL;
while (list) {
FILE *src_stream;
@@ -729,7 +729,7 @@ static llist_t *append_file_list_to_list(llist_t *list)
char *cp = last_char_is(line, '/');
if (cp > line)
*cp = '\0';
- llist_add_to_end(&newlist, line);
+ accept_add_to_end(&newlist, line);
}
fclose(src_stream);
}
@@ -799,6 +799,7 @@ static llist_t *append_file_list_to_list(llist_t *list)
//usage: )
//usage: )
//usage: IF_FEATURE_TAR_LONG_OPTIONS(
+//usage: "\n --occurrence [NUM] Exit after NUM (default 1) occurrences"
//usage: "\n --overwrite Replace existing files"
//usage: "\n --strip-components NUM NUM of leading components to strip"
//usage: "\n --no-recursion Don't descend in directories"
@@ -826,6 +827,7 @@ enum {
OPTBIT_AUTOCOMPRESS_BY_EXT,
IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,)
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ OPTBIT_OCCURRENCE,
OPTBIT_STRIP_COMPONENTS,
IF_FEATURE_SEAMLESS_LZMA(OPTBIT_LZMA ,)
OPTBIT_NORECURSION,
@@ -853,6 +855,7 @@ enum {
OPT_COMPRESS = IF_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z
OPT_AUTOCOMPRESS_BY_EXT = 1 << OPTBIT_AUTOCOMPRESS_BY_EXT, // a
OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m
+ OPT_OCCURRENCE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OCCURRENCE )) + 0, // occurrence
OPT_STRIP_COMPONENTS = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_STRIP_COMPONENTS)) + 0, // strip-components
OPT_LZMA = IF_FEATURE_TAR_LONG_OPTIONS(IF_FEATURE_SEAMLESS_LZMA((1 << OPTBIT_LZMA))) + 0, // lzma
OPT_NORECURSION = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NORECURSION )) + 0, // no-recursion
@@ -901,6 +904,7 @@ static const char tar_longopts[] ALIGN1 =
# if ENABLE_FEATURE_TAR_NOPRESERVE_TIME
"touch\0" No_argument "m"
# endif
+ "occurrence\0" Optional_argument "\xf7"
"strip-components\0" Required_argument "\xf8"
# if ENABLE_FEATURE_SEAMLESS_LZMA
"lzma\0" No_argument "\xf9"
@@ -936,6 +940,9 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
const char *tar_filename = "-";
unsigned opt;
int verboseFlag = 0;
+#if ENABLE_FEATURE_TAR_FROM
+ llist_t *accept = NULL, *reject = NULL;
+#endif
#if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM
llist_t *excludes = NULL;
#endif
@@ -999,6 +1006,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
IF_FEATURE_SEAMLESS_Z( "Z" )
"a"
IF_FEATURE_TAR_NOPRESERVE_TIME("m")
+ IF_FEATURE_TAR_LONG_OPTIONS("\xf7:") // --occurrence
IF_FEATURE_TAR_LONG_OPTIONS("\xf8:") // --strip-components
"\0"
"tt:vv:" // count -t,-v
@@ -1009,14 +1017,16 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive
IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ ":\xf7+" // --occurrence[=NUM]
":\xf8+" // --strip-components=NUM
#endif
LONGOPTS
, &base_dir // -C dir
, &tar_filename // -f filename
- IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T
- IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X
+ IF_FEATURE_TAR_FROM(, &accept) // T
+ IF_FEATURE_TAR_FROM(, &reject) // X
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ , &tar_handle->tar__occurrence // --occurrence
, &tar_handle->tar__strip_components // --strip-components
#endif
IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command
@@ -1061,6 +1071,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
bb_error_msg("verboseFlag:%d", verboseFlag);
bb_error_msg("tar_handle->tar__to_command:'%s'", tar_handle->tar__to_command);
bb_error_msg("tar_handle->tar__strip_components:%u", tar_handle->tar__strip_components);
+ bb_error_msg("tar_handle->tar__occurrence:%u", tar_handle->tar__occurrence);
return 0;
# undef showopt
#endif
@@ -1106,7 +1117,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
#if ENABLE_FEATURE_TAR_FROM
/* Convert each -X EXCLFILE to list of to-be-rejected glob patterns */
- tar_handle->reject = append_file_list_to_list(tar_handle->reject);
+ tar_handle->reject = (llist_t *)append_file_list_to_list(reject);
# if ENABLE_FEATURE_TAR_LONG_OPTIONS
/* Append --exclude=GLOBPATTERNs to reject */
if (excludes) {
@@ -1116,7 +1127,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
*p2next = excludes;
}
# endif
- tar_handle->accept = append_file_list_to_list(tar_handle->accept);
+ tar_handle->accept = append_file_list_to_list(accept);
#endif
/* Setup an array of filenames to work with */
@@ -1126,13 +1137,26 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
char *cp = last_char_is(*argv, '/');
if (cp > *argv)
*cp = '\0';
- llist_add_to_end(&tar_handle->accept, *argv);
+ accept_add_to_end(&tar_handle->accept, *argv);
argv++;
}
if (tar_handle->accept || tar_handle->reject)
tar_handle->filter = filter_accept_reject_list;
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ if (tar_handle->tar__occurrence) {
+ accept_llist_t *a = tar_handle->accept;
+
+ if (opt & OPT_CREATE)
+ bb_simple_error_msg_and_die("--occurrence cannot be used with -c");
+ if (!a)
+ bb_simple_error_msg_and_die("--occurrence requires a file list");
+ for (; a; a = a->link)
+ tar_handle->tar__occurrence_remaining++;
+ }
+#endif
+
/* Open the tar file */
{
int tar_fd = STDIN_FILENO;
@@ -1265,14 +1289,21 @@ int tar_main(int argc UNUSED_PARAM, char **argv)
create_links_from_list(tar_handle->link_placeholders);
/* Check that every file that should have been extracted was */
- while (tar_handle->accept) {
- if (!find_list_entry(tar_handle->reject, tar_handle->accept->data)
- && !find_list_entry(tar_handle->passed, tar_handle->accept->data)
- ) {
+ for (; tar_handle->accept; tar_handle->accept = tar_handle->accept->link) {
+ if (find_list_entry(tar_handle->reject, tar_handle->accept->data)) {
+ continue;
+ }
+ if (tar_handle->accept->tar__seen_count == 0) {
bb_error_msg_and_die("%s: not found in archive",
tar_handle->accept->data);
}
- tar_handle->accept = tar_handle->accept->link;
+#if ENABLE_FEATURE_TAR_LONG_OPTIONS
+ if (tar_handle->tar__occurrence
+ && tar_handle->accept->tar__seen_count < tar_handle->tar__occurrence) {
+ bb_error_msg_and_die("%s: required occurrence not found in archive",
+ tar_handle->accept->data);
+ }
+#endif
}
if (ENABLE_FEATURE_CLEAN_UP /* && tar_handle->src_fd != STDIN_FILENO */)
close(tar_handle->src_fd);
diff --git a/include/bb_archive.h b/include/bb_archive.h
index e0ef8fc4e..74b55236f 100644
--- a/include/bb_archive.h
+++ b/include/bb_archive.h
@@ -43,6 +43,13 @@ typedef struct file_header_t {
dev_t device;
} file_header_t;
+typedef struct accept_llist_t {
+ /* link/data must be first: this struct needs to be llist-compatible */
+ struct accept_llist_t *link;
+ char *data;
+ unsigned tar__seen_count;
+} accept_llist_t;
+
struct hardlinks_t;
typedef struct archive_handle_t {
@@ -55,7 +62,7 @@ typedef struct archive_handle_t {
/* Define if the header and data component should be processed */
char FAST_FUNC (*filter)(struct archive_handle_t *);
/* List of files that have been accepted */
- llist_t *accept;
+ accept_llist_t *accept;
/* List of files that have been rejected */
llist_t *reject;
/* List of files that have successfully been worked on */
@@ -82,6 +89,8 @@ typedef struct archive_handle_t {
/* Archiver specific. Can make it a union if it ever gets big */
#if ENABLE_FEATURE_TAR_LONG_OPTIONS
unsigned tar__strip_components;
+ unsigned tar__occurrence;
+ unsigned tar__occurrence_remaining;
#endif
#define PAX_NEXT_FILE 0
#define PAX_GLOBAL 1
@@ -175,6 +184,9 @@ extern const char cpio_TRAILER[];
archive_handle_t *init_handle(void) FAST_FUNC;
+void accept_add_to(accept_llist_t **old_head, char *data) FAST_FUNC;
+void accept_add_to_end(accept_llist_t **list_head, char *data) FAST_FUNC;
+
char filter_accept_all(archive_handle_t *archive_handle) FAST_FUNC;
char filter_accept_list(archive_handle_t *archive_handle) FAST_FUNC;
char filter_accept_list_reassign(archive_handle_t *archive_handle) FAST_FUNC;
diff --git a/testsuite/tar/tar-handles-occurrence b/testsuite/tar/tar-handles-occurrence
new file mode 100644
index 000000000..4d7a8c5db
--- /dev/null
+++ b/testsuite/tar/tar-handles-occurrence
@@ -0,0 +1,18 @@
+# FEATURE: CONFIG_FEATURE_TAR_LONG_OPTIONS
+
+echo one > test.txt
+busybox tar -cf one.tar test.txt
+
+echo two > test.txt
+busybox tar -cf two.tar test.txt
+
+(head -c 1024 one.tar; head -c 1024 two.tar) > combined.tar
+
+data=$(busybox tar -xO --occurrence=1 test.txt < combined.tar)
+test "$data" = "one"
+
+data=$(busybox tar -xO --occurrence=2 test.txt < combined.tar)
+test "$data" = "two"
+
+data=$(busybox tar -xO --occurrence=3 test.txt < combined.tar) && exit 1
+test "$data" = ""
--
2.39.2
More information about the busybox
mailing list