/* 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" 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__sctx[PAX_NEXT_FILE]; if (!sctx) sctx = archive_handle->tar__sctx[PAX_GLOBAL]; if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */ setfscreatecon(sctx); free(archive_handle->tar__sctx[PAX_NEXT_FILE]); archive_handle->tar__sctx[PAX_NEXT_FILE] = 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 ? xfork() : xvfork(); if (pid == 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(archive_handle->tar__to_command_shell, archive_handle->tar__to_command_shell, "-c", archive_handle->tar__to_command, NULL); bb_perror_msg_and_die("can't execute '%s'", archive_handle->tar__to_command_shell); } 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 }