tar: optional support for --to-command
No binary (build failed)
—
(log yok)
| @@ -280,6 +280,15 @@ config FEATURE_TAR_LONG_OPTIONS | |
| help | |
| Enable use of long options, increases size by about 400 Bytes | |
| + | config FEATURE_TAR_TO_COMMAND |
| + | bool "Support for writing to an external program" |
| + | default y |
| + | depends on TAR && FEATURE_TAR_LONG_OPTIONS |
| + | help |
| + | If you enable this option you'll be able to instruct tar to send |
| + | the contents of each extracted file to the standard input of an |
| + | external program. |
| + | |
| config FEATURE_TAR_UNAME_GNAME | |
| bool "Enable use of user and group names" | |
| default y | |
| @@ -53,6 +53,7 @@ lib-$(CONFIG_FEATURE_SEAMLESS_BZ2) += open_transformer.o decompress_bunzip2 | |
| lib-$(CONFIG_FEATURE_SEAMLESS_LZMA) += open_transformer.o decompress_unlzma.o get_header_tar_lzma.o | |
| lib-$(CONFIG_FEATURE_SEAMLESS_XZ) += open_transformer.o decompress_unxz.o | |
| lib-$(CONFIG_FEATURE_COMPRESS_USAGE) += decompress_bunzip2.o | |
| + | lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o |
| ifneq ($(lib-y),) | |
| lib-y += $(COMMON_FILES) | |
| @@ -0,0 +1,137 @@ | |
| + | /* vi: set sw=4 ts=4: */ |
| + | /* |
| + | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
| + | */ |
| + | |
| + | #include "libbb.h" |
| + | #include "unarchive.h" |
| + | |
| + | enum { |
| + | //TAR_FILETYPE, |
| + | TAR_MODE, |
| + | TAR_FILENAME, |
| + | TAR_REALNAME, |
| + | #if ENABLE_FEATURE_TAR_UNAME_GNAME |
| + | TAR_UNAME, |
| + | TAR_GNAME, |
| + | #endif |
| + | TAR_SIZE, |
| + | TAR_UID, |
| + | TAR_GID, |
| + | TAR_MAX, |
| + | }; |
| + | |
| + | static const char *const tar_var[] = { |
| + | // "FILETYPE", |
| + | "MODE", |
| + | "FILENAME", |
| + | "REALNAME", |
| + | #if ENABLE_FEATURE_TAR_UNAME_GNAME |
| + | "UNAME", |
| + | "GNAME", |
| + | #endif |
| + | "SIZE", |
| + | "UID", |
| + | "GID", |
| + | }; |
| + | |
| + | static void xputenv(char *str) |
| + | { |
| + | if (putenv(str)) |
| + | bb_error_msg_and_die(bb_msg_memory_exhausted); |
| + | } |
| + | |
| + | static void str2env(char *env[], int idx, const char *str) |
| + | { |
| + | env[idx] = xasprintf("TAR_%s=%s", tar_var[idx], str); |
| + | xputenv(env[idx]); |
| + | } |
| + | |
| + | static void dec2env(char *env[], int idx, unsigned long long val) |
| + | { |
| + | env[idx] = xasprintf("TAR_%s=%llu", tar_var[idx], val); |
| + | xputenv(env[idx]); |
| + | } |
| + | |
| + | static void oct2env(char *env[], int idx, unsigned long val) |
| + | { |
| + | env[idx] = xasprintf("TAR_%s=%lo", tar_var[idx], val); |
| + | xputenv(env[idx]); |
| + | } |
| + | |
| + | void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle) |
| + | { |
| + | file_header_t *file_header = archive_handle->file_header; |
| + | |
| + | #if 0 /* do we need this? ENABLE_FEATURE_TAR_SELINUX */ |
| + | char *sctx = archive_handle->tar__next_file_sctx; |
| + | if (!sctx) |
| + | sctx = archive_handle->tar__global_sctx; |
| + | if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */ |
| + | setfscreatecon(sctx); |
| + | free(archive_handle->tar__next_file_sctx); |
| + | archive_handle->tar__next_file_sctx = NULL; |
| + | } |
| + | #endif |
| + | |
| + | if ((file_header->mode & S_IFMT) == S_IFREG) { |
| + | pid_t pid; |
| + | int p[2], status; |
| + | char *tar_env[TAR_MAX]; |
| + | |
| + | memset(tar_env, 0, sizeof(tar_env)); |
| + | |
| + | xpipe(p); |
| + | pid = BB_MMU ? fork() : vfork(); |
| + | switch (pid) { |
| + | case -1: |
| + | bb_perror_msg_and_die(BB_MMU ? "fork" : "vfork"); |
| + | case 0: |
| + | /* Child */ |
| + | /* str2env(tar_env, TAR_FILETYPE, "f"); - parent should do it once */ |
| + | oct2env(tar_env, TAR_MODE, file_header->mode); |
| + | str2env(tar_env, TAR_FILENAME, file_header->name); |
| + | str2env(tar_env, TAR_REALNAME, file_header->name); |
| + | #if ENABLE_FEATURE_TAR_UNAME_GNAME |
| + | str2env(tar_env, TAR_UNAME, file_header->tar__uname); |
| + | str2env(tar_env, TAR_GNAME, file_header->tar__gname); |
| + | #endif |
| + | dec2env(tar_env, TAR_SIZE, file_header->size); |
| + | dec2env(tar_env, TAR_UID, file_header->uid); |
| + | dec2env(tar_env, TAR_GID, file_header->gid); |
| + | close(p[1]); |
| + | xdup2(p[0], STDIN_FILENO); |
| + | signal(SIGPIPE, SIG_DFL); |
| + | execl("/bin/sh", "/bin/sh" + 5, "-c", archive_handle->tar__to_command, NULL); |
| + | bb_perror_msg_and_die("can't execute '%s'", "/bin/sh"); |
| + | } |
| + | close(p[0]); |
| + | /* Our caller is expected to do signal(SIGPIPE, SIG_IGN) |
| + | * so that we don't die if child don't read all the input: */ |
| + | bb_copyfd_exact_size(archive_handle->src_fd, p[1], file_header->size); |
| + | close(p[1]); |
| + | |
| + | if (safe_waitpid(pid, &status, 0) == -1) |
| + | bb_perror_msg_and_die("waitpid"); |
| + | if (WIFEXITED(status) && WEXITSTATUS(status)) |
| + | bb_error_msg_and_die("'%s' returned status %d", |
| + | archive_handle->tar__to_command, WEXITSTATUS(status)); |
| + | if (WIFSIGNALED(status)) |
| + | bb_error_msg_and_die("'%s' terminated on signal %d", |
| + | archive_handle->tar__to_command, WTERMSIG(status)); |
| + | |
| + | if (!BB_MMU) { |
| + | int i; |
| + | for (i = 0; i < TAR_MAX; i++) { |
| + | if (tar_env[i]) |
| + | bb_unsetenv_and_free(tar_env[i]); |
| + | } |
| + | } |
| + | } |
| + | |
| + | #if 0 /* ENABLE_FEATURE_TAR_SELINUX */ |
| + | if (sctx) |
| + | /* reset the context after creating an entry */ |
| + | setfscreatecon(NULL); |
| + | #endif |
| + | } |
| @@ -750,6 +750,7 @@ enum { | |
| IF_FEATURE_SEAMLESS_Z( OPTBIT_COMPRESS ,) // 16th bit | |
| IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,) | |
| #if ENABLE_FEATURE_TAR_LONG_OPTIONS | |
| + | IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,) |
| OPTBIT_NUMERIC_OWNER, | |
| OPTBIT_NOPRESERVE_PERM, | |
| OPTBIT_OVERWRITE, | |
| @@ -772,6 +773,7 @@ enum { | |
| OPT_GZIP = IF_FEATURE_SEAMLESS_GZ( (1 << OPTBIT_GZIP )) + 0, // z | |
| OPT_COMPRESS = IF_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z | |
| OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m | |
| + | OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command |
| OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner | |
| OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions | |
| OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite | |
| @@ -812,6 +814,9 @@ static const char tar_longopts[] ALIGN1 = | |
| # endif | |
| # if ENABLE_FEATURE_TAR_NOPRESERVE_TIME | |
| "touch\0" No_argument "m" | |
| + | # endif |
| + | # if ENABLE_FEATURE_TAR_TO_COMMAND |
| + | "to-command\0" Required_argument "\xfb" |
| # endif | |
| /* use numeric uid/gid from tar header, not textual */ | |
| "numeric-owner\0" No_argument "\xfc" | |
| @@ -904,6 +909,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv) | |
| , &tar_filename // -f filename | |
| IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T | |
| IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X | |
| + | IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command |
| #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM | |
| , &excludes // --exclude | |
| #endif | |
| @@ -922,6 +928,12 @@ int tar_main(int argc UNUSED_PARAM, char **argv) | |
| if (opt & OPT_2STDOUT) | |
| tar_handle->action_data = data_extract_to_stdout; | |
| + | if (opt & OPT_2COMMAND) { |
| + | putenv((char*)"TAR_FILETYPE=f"); |
| + | signal(SIGPIPE, SIG_IGN); |
| + | tar_handle->action_data = data_extract_to_command; |
| + | } |
| + | |
| if (opt & OPT_KEEP_OLD) | |
| tar_handle->ah_flags &= ~ARCHIVE_UNLINK_OLD; | |
| @@ -75,6 +75,9 @@ typedef struct archive_handle_t { | |
| char* tar__longname; | |
| char* tar__linkname; | |
| # endif | |
| + | #if ENABLE_FEATURE_TAR_TO_COMMAND |
| + | char* tar__to_command; |
| + | #endif |
| # if ENABLE_FEATURE_TAR_SELINUX | |
| char* tar__global_sctx; | |
| char* tar__next_file_sctx; | |
| @@ -128,6 +131,7 @@ extern void unpack_ar_archive(archive_handle_t *ar_archive) FAST_FUNC; | |
| extern void data_skip(archive_handle_t *archive_handle) FAST_FUNC; | |
| extern void data_extract_all(archive_handle_t *archive_handle) FAST_FUNC; | |
| extern void data_extract_to_stdout(archive_handle_t *archive_handle) FAST_FUNC; | |
| + | extern void data_extract_to_command(archive_handle_t *archive_handle) FAST_FUNC; |
| extern void header_skip(const file_header_t *file_header) FAST_FUNC; | |
| extern void header_list(const file_header_t *file_header) FAST_FUNC; | |