tar: optional support for --to-command
authorLadislav Michl <Ladislav.Michl@seznam.cz>
Thu, 24 Jun 2010 23:33:00 +0000 (01:33 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Thu, 24 Jun 2010 23:33:00 +0000 (01:33 +0200)
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 <Ladislav.Michl@seznam.cz>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
archival/Config.src
archival/libunarchive/Kbuild.src
archival/libunarchive/data_extract_to_command.c [new file with mode: 0644]
archival/tar.c
include/unarchive.h

index 3dbd3aea16dfcbdc45740b8465528b4af49b1856..f64b3347b25b3ee37f8fdf81bab21ce6af0e77c7 100644 (file)
@@ -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
index 81854558ba9e4b22ae6e1750388101eee27e97e7..6ad1d7a6574bba3790a0b72f6eed4714c65d507b 100644 (file)
@@ -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 (file)
index 0000000..983c530
--- /dev/null
@@ -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
+}
index 3a940128bbe7718c2e735dfdf4518bf7ad064396..344c9dea452c64468a216e767b297208357ece21 100644 (file)
@@ -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;
 
index 8009de2821b3f3d29cec774f962ea51889be7e36..f3aa05d09baaa545e098bdebdbd75baca79b559c 100644 (file)
@@ -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;