summaryrefslogtreecommitdiff
path: root/archival/libunarchive/data_extract_to_command.c
blob: 983c5301d0e57ed3c1b76d9235e3e49671058e28 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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
}