Enable GNU tar's --occurrence option when FEATURE_TAR_LONG_OPTIONS is enabled.
Signed-off-by: Gavin Li <gavi...@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 _______________________________________________ busybox mailing list busybox@busybox.net http://lists.busybox.net/mailman/listinfo/busybox