1/* vi: set sw=4 ts=4: */
2/*
3 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
4 */
5
6#include "libbb.h"
7#include "unarchive.h"
8
9enum {
10	//TAR_FILETYPE,
11	TAR_MODE,
12	TAR_FILENAME,
13	TAR_REALNAME,
14#if ENABLE_FEATURE_TAR_UNAME_GNAME
15	TAR_UNAME,
16	TAR_GNAME,
17#endif
18	TAR_SIZE,
19	TAR_UID,
20	TAR_GID,
21	TAR_MAX,
22};
23
24static const char *const tar_var[] = {
25	// "FILETYPE",
26	"MODE",
27	"FILENAME",
28	"REALNAME",
29#if ENABLE_FEATURE_TAR_UNAME_GNAME
30	"UNAME",
31	"GNAME",
32#endif
33	"SIZE",
34	"UID",
35	"GID",
36};
37
38static void xputenv(char *str)
39{
40	if (putenv(str))
41		bb_error_msg_and_die(bb_msg_memory_exhausted);
42}
43
44static void str2env(char *env[], int idx, const char *str)
45{
46	env[idx] = xasprintf("TAR_%s=%s", tar_var[idx], str);
47	xputenv(env[idx]);
48}
49
50static void dec2env(char *env[], int idx, unsigned long long val)
51{
52	env[idx] = xasprintf("TAR_%s=%llu", tar_var[idx], val);
53	xputenv(env[idx]);
54}
55
56static void oct2env(char *env[], int idx, unsigned long val)
57{
58	env[idx] = xasprintf("TAR_%s=%lo", tar_var[idx], val);
59	xputenv(env[idx]);
60}
61
62void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle)
63{
64	file_header_t *file_header = archive_handle->file_header;
65
66#if 0 /* do we need this? ENABLE_FEATURE_TAR_SELINUX */
67	char *sctx = archive_handle->tar__next_file_sctx;
68	if (!sctx)
69		sctx = archive_handle->tar__global_sctx;
70	if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */
71		setfscreatecon(sctx);
72		free(archive_handle->tar__next_file_sctx);
73		archive_handle->tar__next_file_sctx = NULL;
74	}
75#endif
76
77	if ((file_header->mode & S_IFMT) == S_IFREG) {
78		pid_t pid;
79		int p[2], status;
80		char *tar_env[TAR_MAX];
81
82		memset(tar_env, 0, sizeof(tar_env));
83
84		xpipe(p);
85		pid = BB_MMU ? xfork() : xvfork();
86		if (pid == 0) {
87			/* Child */
88			/* str2env(tar_env, TAR_FILETYPE, "f"); - parent should do it once */
89			oct2env(tar_env, TAR_MODE, file_header->mode);
90			str2env(tar_env, TAR_FILENAME, file_header->name);
91			str2env(tar_env, TAR_REALNAME, file_header->name);
92#if ENABLE_FEATURE_TAR_UNAME_GNAME
93			str2env(tar_env, TAR_UNAME, file_header->tar__uname);
94			str2env(tar_env, TAR_GNAME, file_header->tar__gname);
95#endif
96			dec2env(tar_env, TAR_SIZE, file_header->size);
97			dec2env(tar_env, TAR_UID, file_header->uid);
98			dec2env(tar_env, TAR_GID, file_header->gid);
99			close(p[1]);
100			xdup2(p[0], STDIN_FILENO);
101			signal(SIGPIPE, SIG_DFL);
102			execl(DEFAULT_SHELL, DEFAULT_SHELL_SHORT_NAME, "-c", archive_handle->tar__to_command, NULL);
103			bb_perror_msg_and_die("can't execute '%s'", DEFAULT_SHELL);
104		}
105		close(p[0]);
106		/* Our caller is expected to do signal(SIGPIPE, SIG_IGN)
107		 * so that we don't die if child don't read all the input: */
108		bb_copyfd_exact_size(archive_handle->src_fd, p[1], -file_header->size);
109		close(p[1]);
110
111		if (safe_waitpid(pid, &status, 0) == -1)
112			bb_perror_msg_and_die("waitpid");
113		if (WIFEXITED(status) && WEXITSTATUS(status))
114			bb_error_msg_and_die("'%s' returned status %d",
115				archive_handle->tar__to_command, WEXITSTATUS(status));
116		if (WIFSIGNALED(status))
117			bb_error_msg_and_die("'%s' terminated on signal %d",
118				archive_handle->tar__to_command, WTERMSIG(status));
119
120		if (!BB_MMU) {
121			int i;
122			for (i = 0; i < TAR_MAX; i++) {
123				if (tar_env[i])
124					bb_unsetenv_and_free(tar_env[i]);
125			}
126		}
127	}
128
129#if 0 /* ENABLE_FEATURE_TAR_SELINUX */
130	if (sctx)
131		/* reset the context after creating an entry */
132		setfscreatecon(NULL);
133#endif
134}
135