1 /* vi: set sw=4 ts=4: */
2 /*
3  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
4  */
5 #include "libbb.h"
6 #include "bb_archive.h"
7 
init_transformer_state(transformer_state_t * xstate)8 void FAST_FUNC init_transformer_state(transformer_state_t *xstate)
9 {
10 	memset(xstate, 0, sizeof(*xstate));
11 }
12 
check_signature16(transformer_state_t * xstate,unsigned magic16)13 int FAST_FUNC check_signature16(transformer_state_t *xstate, unsigned magic16)
14 {
15 	if (!xstate->signature_skipped) {
16 		uint16_t magic2;
17 		if (full_read(xstate->src_fd, &magic2, 2) != 2 || magic2 != magic16) {
18 			bb_simple_error_msg("invalid magic");
19 			return -1;
20 		}
21 		xstate->signature_skipped = 2;
22 	}
23 	return 0;
24 }
25 
transformer_write(transformer_state_t * xstate,const void * buf,size_t bufsize)26 ssize_t FAST_FUNC transformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize)
27 {
28 	ssize_t nwrote;
29 
30 	if (xstate->mem_output_size_max != 0) {
31 		size_t pos = xstate->mem_output_size;
32 		size_t size;
33 
34 		size = (xstate->mem_output_size += bufsize);
35 		if (size > xstate->mem_output_size_max) {
36 			free(xstate->mem_output_buf);
37 			xstate->mem_output_buf = NULL;
38 			bb_perror_msg("buffer %u too small", (unsigned)xstate->mem_output_size_max);
39 			nwrote = -1;
40 			goto ret;
41 		}
42 		xstate->mem_output_buf = xrealloc(xstate->mem_output_buf, size + 1);
43 		memcpy(xstate->mem_output_buf + pos, buf, bufsize);
44 		xstate->mem_output_buf[size] = '\0';
45 		nwrote = bufsize;
46 	} else {
47 		nwrote = full_write(xstate->dst_fd, buf, bufsize);
48 		if (nwrote != (ssize_t)bufsize) {
49 			bb_simple_perror_msg("write");
50 			nwrote = -1;
51 			goto ret;
52 		}
53 	}
54  ret:
55 	return nwrote;
56 }
57 
xtransformer_write(transformer_state_t * xstate,const void * buf,size_t bufsize)58 ssize_t FAST_FUNC xtransformer_write(transformer_state_t *xstate, const void *buf, size_t bufsize)
59 {
60 	ssize_t nwrote = transformer_write(xstate, buf, bufsize);
61 	if (nwrote != (ssize_t)bufsize) {
62 		xfunc_die();
63 	}
64 	return nwrote;
65 }
66 
check_errors_in_children(int signo)67 void check_errors_in_children(int signo)
68 {
69 	int status;
70 
71 	if (!signo) {
72 		/* block waiting for any child */
73 		if (wait(&status) < 0)
74 //FIXME: check EINTR?
75 			return; /* probably there are no children */
76 		goto check_status;
77 	}
78 
79 	/* Wait for any child without blocking */
80 	for (;;) {
81 		if (wait_any_nohang(&status) < 0)
82 //FIXME: check EINTR?
83 			/* wait failed?! I'm confused... */
84 			return;
85  check_status:
86 		/*if (WIFEXITED(status) && WEXITSTATUS(status) == 0)*/
87 		/* On Linux, the above can be checked simply as: */
88 		if (status == 0)
89 			/* this child exited with 0 */
90 			continue;
91 		/* Cannot happen:
92 		if (!WIFSIGNALED(status) && !WIFEXITED(status)) ???;
93 		 */
94 		bb_got_signal = 1;
95 	}
96 }
97 
98 /* transformer(), more than meets the eye */
99 #if BB_MMU
fork_transformer(int fd,int signature_skipped,IF_DESKTOP (long long)int FAST_FUNC (* transformer)(transformer_state_t * xstate))100 void FAST_FUNC fork_transformer(int fd,
101 	int signature_skipped,
102 	IF_DESKTOP(long long) int FAST_FUNC (*transformer)(transformer_state_t *xstate)
103 )
104 #else
105 void FAST_FUNC fork_transformer(int fd, const char *transform_prog)
106 #endif
107 {
108 	struct fd_pair fd_pipe;
109 	int pid;
110 
111 	xpiped_pair(fd_pipe);
112 	pid = BB_MMU ? xfork() : xvfork();
113 	if (pid == 0) {
114 		/* Child */
115 		close(fd_pipe.rd); /* we don't want to read from the parent */
116 		// FIXME: error check?
117 #if BB_MMU
118 		{
119 			IF_DESKTOP(long long) int r;
120 			transformer_state_t xstate;
121 			init_transformer_state(&xstate);
122 			xstate.signature_skipped = signature_skipped;
123 			xstate.src_fd = fd;
124 			xstate.dst_fd = fd_pipe.wr;
125 			r = transformer(&xstate);
126 			if (ENABLE_FEATURE_CLEAN_UP) {
127 				close(fd_pipe.wr); /* send EOF */
128 				close(fd);
129 			}
130 			/* must be _exit! bug was actually seen here */
131 			_exit(/*error if:*/ r < 0);
132 		}
133 #else
134 		{
135 			char *argv[4];
136 			xmove_fd(fd, 0);
137 			xmove_fd(fd_pipe.wr, 1);
138 			argv[0] = (char*)transform_prog;
139 			argv[1] = (char*)"-cf";
140 			argv[2] = (char*)"-";
141 			argv[3] = NULL;
142 			BB_EXECVP(transform_prog, argv);
143 			bb_perror_msg_and_die("can't execute '%s'", transform_prog);
144 		}
145 #endif
146 		/* notreached */
147 	}
148 
149 	/* parent process */
150 	close(fd_pipe.wr); /* don't want to write to the child */
151 	xmove_fd(fd_pipe.rd, fd);
152 }
153 
154 
155 #if SEAMLESS_COMPRESSION
156 
157 /* Used by e.g. rpm which gives us a fd without filename,
158  * thus we can't guess the format from filename's extension.
159  */
setup_transformer_on_fd(int fd,int fail_if_not_compressed)160 static transformer_state_t *setup_transformer_on_fd(int fd, int fail_if_not_compressed)
161 {
162 	transformer_state_t *xstate;
163 
164 	xstate = xzalloc(sizeof(*xstate));
165 	xstate->src_fd = fd;
166 
167 	/* .gz and .bz2 both have 2-byte signature, and their
168 	 * unpack_XXX_stream wants this header skipped. */
169 	xstate->signature_skipped = 2;
170 	xread(fd, xstate->magic.b16, 2);
171 	if (ENABLE_FEATURE_SEAMLESS_GZ
172 	 && xstate->magic.b16[0] == GZIP_MAGIC
173 	) {
174 		xstate->xformer = unpack_gz_stream;
175 		USE_FOR_NOMMU(xstate->xformer_prog = "gunzip";)
176 		goto found_magic;
177 	}
178 	if (ENABLE_FEATURE_SEAMLESS_Z
179 	 && xstate->magic.b16[0] == COMPRESS_MAGIC
180 	) {
181 		xstate->xformer = unpack_Z_stream;
182 		USE_FOR_NOMMU(xstate->xformer_prog = "uncompress";)
183 		goto found_magic;
184 	}
185 	if (ENABLE_FEATURE_SEAMLESS_BZ2
186 	 && xstate->magic.b16[0] == BZIP2_MAGIC
187 	) {
188 		xstate->xformer = unpack_bz2_stream;
189 		USE_FOR_NOMMU(xstate->xformer_prog = "bunzip2";)
190 		goto found_magic;
191 	}
192 	if (ENABLE_FEATURE_SEAMLESS_XZ
193 	 && xstate->magic.b16[0] == XZ_MAGIC1
194 	) {
195 		uint32_t v32;
196 		xstate->signature_skipped = 6;
197 		xread(fd, &xstate->magic.b16[1], 4);
198 		move_from_unaligned32(v32, &xstate->magic.b16[1]);
199 		if (v32 == XZ_MAGIC2) {
200 			xstate->xformer = unpack_xz_stream;
201 			USE_FOR_NOMMU(xstate->xformer_prog = "unxz";)
202 			goto found_magic;
203 		}
204 	}
205 
206 	/* No known magic seen */
207 	if (fail_if_not_compressed)
208 		bb_simple_error_msg_and_die("no gzip"
209 			IF_FEATURE_SEAMLESS_BZ2("/bzip2")
210 			IF_FEATURE_SEAMLESS_XZ("/xz")
211 			" magic");
212 
213 	/* Some callers expect this function to "consume" fd
214 	 * even if data is not compressed. In this case,
215 	 * we return a state with trivial transformer.
216 	 */
217 //	USE_FOR_MMU(xstate->xformer = copy_stream;)
218 //	USE_FOR_NOMMU(xstate->xformer_prog = "cat";)
219 
220  found_magic:
221 	return xstate;
222 }
223 
fork_transformer_and_free(transformer_state_t * xstate)224 static void fork_transformer_and_free(transformer_state_t *xstate)
225 {
226 # if BB_MMU
227 	fork_transformer_with_no_sig(xstate->src_fd, xstate->xformer);
228 # else
229 	/* NOMMU version of fork_transformer execs
230 	 * an external unzipper that wants
231 	 * file position at the start of the file.
232 	 */
233 	xlseek(xstate->src_fd, - xstate->signature_skipped, SEEK_CUR);
234 	xstate->signature_skipped = 0;
235 	fork_transformer_with_sig(xstate->src_fd, xstate->xformer, xstate->xformer_prog);
236 # endif
237 	free(xstate);
238 }
239 
240 /* Used by e.g. rpm which gives us a fd without filename,
241  * thus we can't guess the format from filename's extension.
242  */
setup_unzip_on_fd(int fd,int fail_if_not_compressed)243 int FAST_FUNC setup_unzip_on_fd(int fd, int fail_if_not_compressed)
244 {
245 	transformer_state_t *xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
246 
247 	if (!xstate->xformer) {
248 		free(xstate);
249 		return 1;
250 	}
251 
252 	fork_transformer_and_free(xstate);
253 	return 0;
254 }
255 #if ENABLE_FEATURE_SEAMLESS_LZMA
256 /* ...and custom version for LZMA */
setup_lzma_on_fd(int fd)257 void FAST_FUNC setup_lzma_on_fd(int fd)
258 {
259 	transformer_state_t *xstate = xzalloc(sizeof(*xstate));
260 	xstate->src_fd = fd;
261 	xstate->xformer = unpack_lzma_stream;
262 	USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";)
263 	fork_transformer_and_free(xstate);
264 }
265 #endif
266 
open_transformer(const char * fname,int fail_if_not_compressed)267 static transformer_state_t *open_transformer(const char *fname, int fail_if_not_compressed)
268 {
269 	transformer_state_t *xstate;
270 	int fd;
271 
272 	fd = open(fname, O_RDONLY);
273 	if (fd < 0)
274 		return NULL;
275 
276 	if (ENABLE_FEATURE_SEAMLESS_LZMA) {
277 		/* .lzma has no header/signature, can only detect it by extension */
278 		if (is_suffixed_with(fname, ".lzma")) {
279 			xstate = xzalloc(sizeof(*xstate));
280 			xstate->src_fd = fd;
281 			xstate->xformer = unpack_lzma_stream;
282 			USE_FOR_NOMMU(xstate->xformer_prog = "unlzma";)
283 			return xstate;
284 		}
285 	}
286 
287 	xstate = setup_transformer_on_fd(fd, fail_if_not_compressed);
288 
289 	return xstate;
290 }
291 
open_zipped(const char * fname,int fail_if_not_compressed)292 int FAST_FUNC open_zipped(const char *fname, int fail_if_not_compressed)
293 {
294 	int fd;
295 	transformer_state_t *xstate;
296 
297 	xstate = open_transformer(fname, fail_if_not_compressed);
298 	if (!xstate)
299 		return -1;
300 
301 	fd = xstate->src_fd;
302 # if BB_MMU
303 	if (xstate->xformer) {
304 		fork_transformer_with_no_sig(fd, xstate->xformer);
305 	} else {
306 		/* the file is not compressed */
307 		xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
308 		xstate->signature_skipped = 0;
309 	}
310 # else
311 	/* NOMMU can't avoid the seek :( */
312 	xlseek(fd, - xstate->signature_skipped, SEEK_CUR);
313 	xstate->signature_skipped = 0;
314 	if (xstate->xformer) {
315 		fork_transformer_with_sig(fd, xstate->xformer, xstate->xformer_prog);
316 	} /* else: the file is not compressed */
317 # endif
318 
319 	free(xstate);
320 	return fd;
321 }
322 
xmalloc_open_zipped_read_close(const char * fname,size_t * maxsz_p)323 void* FAST_FUNC xmalloc_open_zipped_read_close(const char *fname, size_t *maxsz_p)
324 {
325 # if 1
326 	transformer_state_t *xstate;
327 	char *image;
328 
329 	xstate = open_transformer(fname, /*fail_if_not_compressed:*/ 0);
330 	if (!xstate) /* file open error */
331 		return NULL;
332 
333 	image = NULL;
334 	if (xstate->xformer) {
335 		/* In-memory decompression */
336 		xstate->mem_output_size_max = maxsz_p ? *maxsz_p : (size_t)(INT_MAX - 4095);
337 		xstate->xformer(xstate);
338 		if (xstate->mem_output_buf) {
339 			image = xstate->mem_output_buf;
340 			if (maxsz_p)
341 				*maxsz_p = xstate->mem_output_size;
342 		}
343 	} else {
344 		/* File is not compressed.
345 		 * We already read first few bytes, account for that.
346 		 * Example where it happens:
347 		 * "modinfo MODULE.ko" (not compressed)
348 		 *   open("MODULE.ko", O_RDONLY|O_LARGEFILE) = 4
349 		 *   read(4, "\177E", 2)                     = 2
350 		 *   fstat64(4, ...)
351 		 *   mmap(...)
352 		 *   read(4, "LF\2\1\1\0\0\0\0"...
353 		 * ...and we avoided seeking on the fd! :)
354 		 */
355 		image = xmalloc_read_with_initial_buf(
356 			xstate->src_fd,
357 			maxsz_p,
358 			xmemdup(&xstate->magic, xstate->signature_skipped),
359 			xstate->signature_skipped
360 		);
361 		xstate->signature_skipped = 0;
362 	}
363 
364 	if (!image)
365 		bb_perror_msg("read error from '%s'", fname);
366 	close(xstate->src_fd);
367 	free(xstate);
368 	return image;
369 # else
370 	/* This version forks a subprocess - much more expensive */
371 	int fd;
372 	char *image;
373 
374 	fd = open_zipped(fname, /*fail_if_not_compressed:*/ 0);
375 	if (fd < 0)
376 		return NULL;
377 
378 	image = xmalloc_read(fd, maxsz_p);
379 	if (!image)
380 		bb_perror_msg("read error from '%s'", fname);
381 	close(fd);
382 	return image;
383 # endif
384 }
385 
386 #endif /* SEAMLESS_COMPRESSION */
387