From 2b46fd49b14b2ac30e0c767c65ac2b29f6922a45 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 25 Jun 2010 01:33:00 +0200 Subject: tar: optional support for --to-command function old new delta data_extract_to_command - 430 +430 dec2env - 44 +44 tar_main 778 819 +41 str2env - 37 +37 tar_var - 32 +32 xputenv - 22 +22 tar_longopts 257 270 +13 ------------------------------------------------------------------------------ (add/remove: 6/0 grow/shrink: 2/0 up/down: 619/0) Total: 619 bytes Signed-off-by: Ladislav Michl Signed-off-by: Denys Vlasenko --- archival/Config.src | 9 ++ archival/libunarchive/Kbuild.src | 1 + archival/libunarchive/data_extract_to_command.c | 137 ++++++++++++++++++++++++ archival/tar.c | 12 +++ 4 files changed, 159 insertions(+) create mode 100644 archival/libunarchive/data_extract_to_command.c (limited to 'archival') diff --git a/archival/Config.src b/archival/Config.src index 3dbd3aea1..f64b3347b 100644 --- a/archival/Config.src +++ b/archival/Config.src @@ -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 diff --git a/archival/libunarchive/Kbuild.src b/archival/libunarchive/Kbuild.src index 81854558b..6ad1d7a65 100644 --- a/archival/libunarchive/Kbuild.src +++ b/archival/libunarchive/Kbuild.src @@ -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) diff --git a/archival/libunarchive/data_extract_to_command.c b/archival/libunarchive/data_extract_to_command.c new file mode 100644 index 000000000..983c5301d --- /dev/null +++ b/archival/libunarchive/data_extract_to_command.c @@ -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 +} diff --git a/archival/tar.c b/archival/tar.c index 3a940128b..344c9dea4 100644 --- a/archival/tar.c +++ b/archival/tar.c @@ -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; -- cgit v1.2.3