xref: /freebsd/contrib/file/src/compress.c (revision 5f0216bd883edee71bf81051e3c20505e4820903)
1b6cee71dSXin LI /*
2b6cee71dSXin LI  * Copyright (c) Ian F. Darwin 1986-1995.
3b6cee71dSXin LI  * Software written by Ian F. Darwin and others;
4b6cee71dSXin LI  * maintained 1995-present by Christos Zoulas and others.
5b6cee71dSXin LI  *
6b6cee71dSXin LI  * Redistribution and use in source and binary forms, with or without
7b6cee71dSXin LI  * modification, are permitted provided that the following conditions
8b6cee71dSXin LI  * are met:
9b6cee71dSXin LI  * 1. Redistributions of source code must retain the above copyright
10b6cee71dSXin LI  *    notice immediately at the beginning of the file, without modification,
11b6cee71dSXin LI  *    this list of conditions, and the following disclaimer.
12b6cee71dSXin LI  * 2. Redistributions in binary form must reproduce the above copyright
13b6cee71dSXin LI  *    notice, this list of conditions and the following disclaimer in the
14b6cee71dSXin LI  *    documentation and/or other materials provided with the distribution.
15b6cee71dSXin LI  *
16b6cee71dSXin LI  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17b6cee71dSXin LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18b6cee71dSXin LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19b6cee71dSXin LI  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20b6cee71dSXin LI  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21b6cee71dSXin LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22b6cee71dSXin LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23b6cee71dSXin LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24b6cee71dSXin LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25b6cee71dSXin LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26b6cee71dSXin LI  * SUCH DAMAGE.
27b6cee71dSXin LI  */
28b6cee71dSXin LI /*
29b6cee71dSXin LI  * compress routines:
30b6cee71dSXin LI  *	zmagic() - returns 0 if not recognized, uncompresses and prints
31b6cee71dSXin LI  *		   information if recognized
32b6cee71dSXin LI  *	uncompress(method, old, n, newch) - uncompress old into new,
33b6cee71dSXin LI  *					    using method, return sizeof new
34b6cee71dSXin LI  */
35b6cee71dSXin LI #include "file.h"
36b6cee71dSXin LI 
37b6cee71dSXin LI #ifndef lint
38*5f0216bdSXin LI FILE_RCSID("@(#)$File: compress.c,v 1.80 2015/06/03 18:21:24 christos Exp $")
39b6cee71dSXin LI #endif
40b6cee71dSXin LI 
41b6cee71dSXin LI #include "magic.h"
42b6cee71dSXin LI #include <stdlib.h>
43b6cee71dSXin LI #ifdef HAVE_UNISTD_H
44b6cee71dSXin LI #include <unistd.h>
45b6cee71dSXin LI #endif
46b6cee71dSXin LI #include <string.h>
47b6cee71dSXin LI #include <errno.h>
48*5f0216bdSXin LI #ifdef HAVE_SIGNAL_H
494460e5b0SXin LI #include <signal.h>
50*5f0216bdSXin LI # ifndef HAVE_SIG_T
51*5f0216bdSXin LI typedef void (*sig_t)(int);
52*5f0216bdSXin LI # endif /* HAVE_SIG_T */
53*5f0216bdSXin LI #endif
54c2931133SXin LI #if !defined(__MINGW32__) && !defined(WIN32)
55b6cee71dSXin LI #include <sys/ioctl.h>
56b6cee71dSXin LI #endif
57b6cee71dSXin LI #ifdef HAVE_SYS_WAIT_H
58b6cee71dSXin LI #include <sys/wait.h>
59b6cee71dSXin LI #endif
60b6cee71dSXin LI #if defined(HAVE_SYS_TIME_H)
61b6cee71dSXin LI #include <sys/time.h>
62b6cee71dSXin LI #endif
63b6cee71dSXin LI #if defined(HAVE_ZLIB_H) && defined(HAVE_LIBZ)
64b6cee71dSXin LI #define BUILTIN_DECOMPRESS
65b6cee71dSXin LI #include <zlib.h>
66b6cee71dSXin LI #endif
67b6cee71dSXin LI 
68b6cee71dSXin LI private const struct {
69b6cee71dSXin LI 	const char magic[8];
70b6cee71dSXin LI 	size_t maglen;
71b6cee71dSXin LI 	const char *argv[3];
72b6cee71dSXin LI 	int silent;
73b6cee71dSXin LI } compr[] = {
74b6cee71dSXin LI 	{ "\037\235", 2, { "gzip", "-cdq", NULL }, 1 },		/* compressed */
75b6cee71dSXin LI 	/* Uncompress can get stuck; so use gzip first if we have it
76b6cee71dSXin LI 	 * Idea from Damien Clark, thanks! */
77b6cee71dSXin LI 	{ "\037\235", 2, { "uncompress", "-c", NULL }, 1 },	/* compressed */
78b6cee71dSXin LI 	{ "\037\213", 2, { "gzip", "-cdq", NULL }, 1 },		/* gzipped */
79b6cee71dSXin LI 	{ "\037\236", 2, { "gzip", "-cdq", NULL }, 1 },		/* frozen */
80b6cee71dSXin LI 	{ "\037\240", 2, { "gzip", "-cdq", NULL }, 1 },		/* SCO LZH */
81b6cee71dSXin LI 	/* the standard pack utilities do not accept standard input */
82b6cee71dSXin LI 	{ "\037\036", 2, { "gzip", "-cdq", NULL }, 0 },		/* packed */
83b6cee71dSXin LI 	{ "PK\3\4",   4, { "gzip", "-cdq", NULL }, 1 },		/* pkzipped, */
84b6cee71dSXin LI 					    /* ...only first file examined */
85b6cee71dSXin LI 	{ "BZh",      3, { "bzip2", "-cd", NULL }, 1 },		/* bzip2-ed */
86b6cee71dSXin LI 	{ "LZIP",     4, { "lzip", "-cdq", NULL }, 1 },
87b6cee71dSXin LI  	{ "\3757zXZ\0",6,{ "xz", "-cd", NULL }, 1 },		/* XZ Utils */
88b6cee71dSXin LI  	{ "LRZI",     4, { "lrzip", "-dqo-", NULL }, 1 },	/* LRZIP */
89b6cee71dSXin LI  	{ "\004\"M\030", 4, { "lz4", "-cd", NULL }, 1 },	/* LZ4 */
90b6cee71dSXin LI };
91b6cee71dSXin LI 
92b6cee71dSXin LI #define NODATA ((size_t)~0)
93b6cee71dSXin LI 
94b6cee71dSXin LI private ssize_t swrite(int, const void *, size_t);
95b6cee71dSXin LI #if HAVE_FORK
96b6cee71dSXin LI private size_t ncompr = sizeof(compr) / sizeof(compr[0]);
97b6cee71dSXin LI private size_t uncompressbuf(struct magic_set *, int, size_t,
98b6cee71dSXin LI     const unsigned char *, unsigned char **, size_t);
99b6cee71dSXin LI #ifdef BUILTIN_DECOMPRESS
100b6cee71dSXin LI private size_t uncompressgzipped(struct magic_set *, const unsigned char *,
101b6cee71dSXin LI     unsigned char **, size_t);
102b6cee71dSXin LI #endif
103b6cee71dSXin LI 
104b6cee71dSXin LI protected int
105b6cee71dSXin LI file_zmagic(struct magic_set *ms, int fd, const char *name,
106b6cee71dSXin LI     const unsigned char *buf, size_t nbytes)
107b6cee71dSXin LI {
108b6cee71dSXin LI 	unsigned char *newbuf = NULL;
109b6cee71dSXin LI 	size_t i, nsz;
110b6cee71dSXin LI 	int rv = 0;
111b6cee71dSXin LI 	int mime = ms->flags & MAGIC_MIME;
112*5f0216bdSXin LI #ifdef HAVE_SIGNAL_H
1134460e5b0SXin LI 	sig_t osigpipe;
114*5f0216bdSXin LI #endif
115b6cee71dSXin LI 
116b6cee71dSXin LI 	if ((ms->flags & MAGIC_COMPRESS) == 0)
117b6cee71dSXin LI 		return 0;
118b6cee71dSXin LI 
119*5f0216bdSXin LI #ifdef HAVE_SIGNAL_H
1204460e5b0SXin LI 	osigpipe = signal(SIGPIPE, SIG_IGN);
121*5f0216bdSXin LI #endif
122b6cee71dSXin LI 	for (i = 0; i < ncompr; i++) {
123b6cee71dSXin LI 		if (nbytes < compr[i].maglen)
124b6cee71dSXin LI 			continue;
125b6cee71dSXin LI 		if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 &&
126b6cee71dSXin LI 		    (nsz = uncompressbuf(ms, fd, i, buf, &newbuf,
127b6cee71dSXin LI 		    nbytes)) != NODATA) {
128b6cee71dSXin LI 			ms->flags &= ~MAGIC_COMPRESS;
129b6cee71dSXin LI 			rv = -1;
130b6cee71dSXin LI 			if (file_buffer(ms, -1, name, newbuf, nsz) == -1)
131b6cee71dSXin LI 				goto error;
132b6cee71dSXin LI 
133*5f0216bdSXin LI 			if ((ms->flags & MAGIC_COMPRESS_TRANSP) == 0 &&
134*5f0216bdSXin LI 			    (mime == MAGIC_MIME || mime == 0)) {
135b6cee71dSXin LI 				if (file_printf(ms, mime ?
136b6cee71dSXin LI 				    " compressed-encoding=" : " (") == -1)
137b6cee71dSXin LI 					goto error;
138b6cee71dSXin LI 				if (file_buffer(ms, -1, NULL, buf, nbytes) == -1)
139b6cee71dSXin LI 					goto error;
140b6cee71dSXin LI 				if (!mime && file_printf(ms, ")") == -1)
141b6cee71dSXin LI 					goto error;
142b6cee71dSXin LI 			}
143b6cee71dSXin LI 
144b6cee71dSXin LI 			rv = 1;
145b6cee71dSXin LI 			break;
146b6cee71dSXin LI 		}
147b6cee71dSXin LI 	}
148b6cee71dSXin LI error:
149*5f0216bdSXin LI #ifdef HAVE_SIGNAL_H
1504460e5b0SXin LI 	(void)signal(SIGPIPE, osigpipe);
151*5f0216bdSXin LI #endif
152b6cee71dSXin LI 	free(newbuf);
153b6cee71dSXin LI 	ms->flags |= MAGIC_COMPRESS;
154b6cee71dSXin LI 	return rv;
155b6cee71dSXin LI }
156b6cee71dSXin LI #endif
157b6cee71dSXin LI /*
158b6cee71dSXin LI  * `safe' write for sockets and pipes.
159b6cee71dSXin LI  */
160b6cee71dSXin LI private ssize_t
161b6cee71dSXin LI swrite(int fd, const void *buf, size_t n)
162b6cee71dSXin LI {
163b6cee71dSXin LI 	ssize_t rv;
164b6cee71dSXin LI 	size_t rn = n;
165b6cee71dSXin LI 
166b6cee71dSXin LI 	do
167b6cee71dSXin LI 		switch (rv = write(fd, buf, n)) {
168b6cee71dSXin LI 		case -1:
169b6cee71dSXin LI 			if (errno == EINTR)
170b6cee71dSXin LI 				continue;
171b6cee71dSXin LI 			return -1;
172b6cee71dSXin LI 		default:
173b6cee71dSXin LI 			n -= rv;
174b6cee71dSXin LI 			buf = CAST(const char *, buf) + rv;
175b6cee71dSXin LI 			break;
176b6cee71dSXin LI 		}
177b6cee71dSXin LI 	while (n > 0);
178b6cee71dSXin LI 	return rn;
179b6cee71dSXin LI }
180b6cee71dSXin LI 
181b6cee71dSXin LI 
182b6cee71dSXin LI /*
183b6cee71dSXin LI  * `safe' read for sockets and pipes.
184b6cee71dSXin LI  */
185b6cee71dSXin LI protected ssize_t
186b6cee71dSXin LI sread(int fd, void *buf, size_t n, int canbepipe __attribute__((__unused__)))
187b6cee71dSXin LI {
188b6cee71dSXin LI 	ssize_t rv;
189b6cee71dSXin LI #ifdef FIONREAD
190b6cee71dSXin LI 	int t = 0;
191b6cee71dSXin LI #endif
192b6cee71dSXin LI 	size_t rn = n;
193b6cee71dSXin LI 
194b6cee71dSXin LI 	if (fd == STDIN_FILENO)
195b6cee71dSXin LI 		goto nocheck;
196b6cee71dSXin LI 
197b6cee71dSXin LI #ifdef FIONREAD
198b6cee71dSXin LI 	if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) {
199b6cee71dSXin LI #ifdef FD_ZERO
200b6cee71dSXin LI 		ssize_t cnt;
201b6cee71dSXin LI 		for (cnt = 0;; cnt++) {
202b6cee71dSXin LI 			fd_set check;
203b6cee71dSXin LI 			struct timeval tout = {0, 100 * 1000};
204b6cee71dSXin LI 			int selrv;
205b6cee71dSXin LI 
206b6cee71dSXin LI 			FD_ZERO(&check);
207b6cee71dSXin LI 			FD_SET(fd, &check);
208b6cee71dSXin LI 
209b6cee71dSXin LI 			/*
210b6cee71dSXin LI 			 * Avoid soft deadlock: do not read if there
211b6cee71dSXin LI 			 * is nothing to read from sockets and pipes.
212b6cee71dSXin LI 			 */
213b6cee71dSXin LI 			selrv = select(fd + 1, &check, NULL, NULL, &tout);
214b6cee71dSXin LI 			if (selrv == -1) {
215b6cee71dSXin LI 				if (errno == EINTR || errno == EAGAIN)
216b6cee71dSXin LI 					continue;
217b6cee71dSXin LI 			} else if (selrv == 0 && cnt >= 5) {
218b6cee71dSXin LI 				return 0;
219b6cee71dSXin LI 			} else
220b6cee71dSXin LI 				break;
221b6cee71dSXin LI 		}
222b6cee71dSXin LI #endif
223b6cee71dSXin LI 		(void)ioctl(fd, FIONREAD, &t);
224b6cee71dSXin LI 	}
225b6cee71dSXin LI 
226b6cee71dSXin LI 	if (t > 0 && (size_t)t < n) {
227b6cee71dSXin LI 		n = t;
228b6cee71dSXin LI 		rn = n;
229b6cee71dSXin LI 	}
230b6cee71dSXin LI #endif
231b6cee71dSXin LI 
232b6cee71dSXin LI nocheck:
233b6cee71dSXin LI 	do
234b6cee71dSXin LI 		switch ((rv = read(fd, buf, n))) {
235b6cee71dSXin LI 		case -1:
236b6cee71dSXin LI 			if (errno == EINTR)
237b6cee71dSXin LI 				continue;
238b6cee71dSXin LI 			return -1;
239b6cee71dSXin LI 		case 0:
240b6cee71dSXin LI 			return rn - n;
241b6cee71dSXin LI 		default:
242b6cee71dSXin LI 			n -= rv;
243b6cee71dSXin LI 			buf = ((char *)buf) + rv;
244b6cee71dSXin LI 			break;
245b6cee71dSXin LI 		}
246b6cee71dSXin LI 	while (n > 0);
247b6cee71dSXin LI 	return rn;
248b6cee71dSXin LI }
249b6cee71dSXin LI 
250b6cee71dSXin LI protected int
251b6cee71dSXin LI file_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
252b6cee71dSXin LI     size_t nbytes)
253b6cee71dSXin LI {
254b6cee71dSXin LI 	char buf[4096];
255b6cee71dSXin LI 	ssize_t r;
256b6cee71dSXin LI 	int tfd;
257b6cee71dSXin LI 
258b6cee71dSXin LI 	(void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof buf);
259b6cee71dSXin LI #ifndef HAVE_MKSTEMP
260b6cee71dSXin LI 	{
261b6cee71dSXin LI 		char *ptr = mktemp(buf);
262b6cee71dSXin LI 		tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
263b6cee71dSXin LI 		r = errno;
264b6cee71dSXin LI 		(void)unlink(ptr);
265b6cee71dSXin LI 		errno = r;
266b6cee71dSXin LI 	}
267b6cee71dSXin LI #else
268b6cee71dSXin LI 	{
269b6cee71dSXin LI 		int te;
270b6cee71dSXin LI 		tfd = mkstemp(buf);
271b6cee71dSXin LI 		te = errno;
272b6cee71dSXin LI 		(void)unlink(buf);
273b6cee71dSXin LI 		errno = te;
274b6cee71dSXin LI 	}
275b6cee71dSXin LI #endif
276b6cee71dSXin LI 	if (tfd == -1) {
277b6cee71dSXin LI 		file_error(ms, errno,
278b6cee71dSXin LI 		    "cannot create temporary file for pipe copy");
279b6cee71dSXin LI 		return -1;
280b6cee71dSXin LI 	}
281b6cee71dSXin LI 
282b6cee71dSXin LI 	if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes)
283b6cee71dSXin LI 		r = 1;
284b6cee71dSXin LI 	else {
285b6cee71dSXin LI 		while ((r = sread(fd, buf, sizeof(buf), 1)) > 0)
286b6cee71dSXin LI 			if (swrite(tfd, buf, (size_t)r) != r)
287b6cee71dSXin LI 				break;
288b6cee71dSXin LI 	}
289b6cee71dSXin LI 
290b6cee71dSXin LI 	switch (r) {
291b6cee71dSXin LI 	case -1:
292b6cee71dSXin LI 		file_error(ms, errno, "error copying from pipe to temp file");
293b6cee71dSXin LI 		return -1;
294b6cee71dSXin LI 	case 0:
295b6cee71dSXin LI 		break;
296b6cee71dSXin LI 	default:
297b6cee71dSXin LI 		file_error(ms, errno, "error while writing to temp file");
298b6cee71dSXin LI 		return -1;
299b6cee71dSXin LI 	}
300b6cee71dSXin LI 
301b6cee71dSXin LI 	/*
302b6cee71dSXin LI 	 * We duplicate the file descriptor, because fclose on a
303b6cee71dSXin LI 	 * tmpfile will delete the file, but any open descriptors
304b6cee71dSXin LI 	 * can still access the phantom inode.
305b6cee71dSXin LI 	 */
306b6cee71dSXin LI 	if ((fd = dup2(tfd, fd)) == -1) {
307b6cee71dSXin LI 		file_error(ms, errno, "could not dup descriptor for temp file");
308b6cee71dSXin LI 		return -1;
309b6cee71dSXin LI 	}
310b6cee71dSXin LI 	(void)close(tfd);
311b6cee71dSXin LI 	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
312b6cee71dSXin LI 		file_badseek(ms);
313b6cee71dSXin LI 		return -1;
314b6cee71dSXin LI 	}
315b6cee71dSXin LI 	return fd;
316b6cee71dSXin LI }
317b6cee71dSXin LI #if HAVE_FORK
318b6cee71dSXin LI #ifdef BUILTIN_DECOMPRESS
319b6cee71dSXin LI 
320b6cee71dSXin LI #define FHCRC		(1 << 1)
321b6cee71dSXin LI #define FEXTRA		(1 << 2)
322b6cee71dSXin LI #define FNAME		(1 << 3)
323b6cee71dSXin LI #define FCOMMENT	(1 << 4)
324b6cee71dSXin LI 
325b6cee71dSXin LI private size_t
326b6cee71dSXin LI uncompressgzipped(struct magic_set *ms, const unsigned char *old,
327b6cee71dSXin LI     unsigned char **newch, size_t n)
328b6cee71dSXin LI {
329b6cee71dSXin LI 	unsigned char flg = old[3];
330b6cee71dSXin LI 	size_t data_start = 10;
331b6cee71dSXin LI 	z_stream z;
332b6cee71dSXin LI 	int rc;
333b6cee71dSXin LI 
334b6cee71dSXin LI 	if (flg & FEXTRA) {
335b6cee71dSXin LI 		if (data_start+1 >= n)
336b6cee71dSXin LI 			return 0;
337b6cee71dSXin LI 		data_start += 2 + old[data_start] + old[data_start + 1] * 256;
338b6cee71dSXin LI 	}
339b6cee71dSXin LI 	if (flg & FNAME) {
340b6cee71dSXin LI 		while(data_start < n && old[data_start])
341b6cee71dSXin LI 			data_start++;
342b6cee71dSXin LI 		data_start++;
343b6cee71dSXin LI 	}
344b6cee71dSXin LI 	if(flg & FCOMMENT) {
345b6cee71dSXin LI 		while(data_start < n && old[data_start])
346b6cee71dSXin LI 			data_start++;
347b6cee71dSXin LI 		data_start++;
348b6cee71dSXin LI 	}
349b6cee71dSXin LI 	if(flg & FHCRC)
350b6cee71dSXin LI 		data_start += 2;
351b6cee71dSXin LI 
352b6cee71dSXin LI 	if (data_start >= n)
353b6cee71dSXin LI 		return 0;
354b6cee71dSXin LI 	if ((*newch = CAST(unsigned char *, malloc(HOWMANY + 1))) == NULL) {
355b6cee71dSXin LI 		return 0;
356b6cee71dSXin LI 	}
357b6cee71dSXin LI 
358b6cee71dSXin LI 	/* XXX: const castaway, via strchr */
359b6cee71dSXin LI 	z.next_in = (Bytef *)strchr((const char *)old + data_start,
360b6cee71dSXin LI 	    old[data_start]);
361b6cee71dSXin LI 	z.avail_in = CAST(uint32_t, (n - data_start));
362b6cee71dSXin LI 	z.next_out = *newch;
363b6cee71dSXin LI 	z.avail_out = HOWMANY;
364b6cee71dSXin LI 	z.zalloc = Z_NULL;
365b6cee71dSXin LI 	z.zfree = Z_NULL;
366b6cee71dSXin LI 	z.opaque = Z_NULL;
367b6cee71dSXin LI 
368b6cee71dSXin LI 	/* LINTED bug in header macro */
369b6cee71dSXin LI 	rc = inflateInit2(&z, -15);
370b6cee71dSXin LI 	if (rc != Z_OK) {
371b6cee71dSXin LI 		file_error(ms, 0, "zlib: %s", z.msg);
372b6cee71dSXin LI 		return 0;
373b6cee71dSXin LI 	}
374b6cee71dSXin LI 
375b6cee71dSXin LI 	rc = inflate(&z, Z_SYNC_FLUSH);
376b6cee71dSXin LI 	if (rc != Z_OK && rc != Z_STREAM_END) {
377b6cee71dSXin LI 		file_error(ms, 0, "zlib: %s", z.msg);
378b6cee71dSXin LI 		return 0;
379b6cee71dSXin LI 	}
380b6cee71dSXin LI 
381b6cee71dSXin LI 	n = (size_t)z.total_out;
382b6cee71dSXin LI 	(void)inflateEnd(&z);
383b6cee71dSXin LI 
384b6cee71dSXin LI 	/* let's keep the nul-terminate tradition */
385b6cee71dSXin LI 	(*newch)[n] = '\0';
386b6cee71dSXin LI 
387b6cee71dSXin LI 	return n;
388b6cee71dSXin LI }
389b6cee71dSXin LI #endif
390b6cee71dSXin LI 
391b6cee71dSXin LI private size_t
392b6cee71dSXin LI uncompressbuf(struct magic_set *ms, int fd, size_t method,
393b6cee71dSXin LI     const unsigned char *old, unsigned char **newch, size_t n)
394b6cee71dSXin LI {
395b6cee71dSXin LI 	int fdin[2], fdout[2];
396c2931133SXin LI 	int status;
397b6cee71dSXin LI 	ssize_t r;
398b6cee71dSXin LI 
399b6cee71dSXin LI #ifdef BUILTIN_DECOMPRESS
400b6cee71dSXin LI         /* FIXME: This doesn't cope with bzip2 */
401b6cee71dSXin LI 	if (method == 2)
402b6cee71dSXin LI 		return uncompressgzipped(ms, old, newch, n);
403b6cee71dSXin LI #endif
404b6cee71dSXin LI 	(void)fflush(stdout);
405b6cee71dSXin LI 	(void)fflush(stderr);
406b6cee71dSXin LI 
407b6cee71dSXin LI 	if ((fd != -1 && pipe(fdin) == -1) || pipe(fdout) == -1) {
408b6cee71dSXin LI 		file_error(ms, errno, "cannot create pipe");
409b6cee71dSXin LI 		return NODATA;
410b6cee71dSXin LI 	}
411*5f0216bdSXin LI 	switch (fork()) {
412b6cee71dSXin LI 	case 0:	/* child */
413b6cee71dSXin LI 		(void) close(0);
414b6cee71dSXin LI 		if (fd != -1) {
415b6cee71dSXin LI 		    if (dup(fd) == -1)
416b6cee71dSXin LI 			_exit(1);
417b6cee71dSXin LI 		    (void) lseek(0, (off_t)0, SEEK_SET);
418b6cee71dSXin LI 		} else {
419b6cee71dSXin LI 		    if (dup(fdin[0]) == -1)
420b6cee71dSXin LI 			_exit(1);
421b6cee71dSXin LI 		    (void) close(fdin[0]);
422b6cee71dSXin LI 		    (void) close(fdin[1]);
423b6cee71dSXin LI 		}
424b6cee71dSXin LI 
425b6cee71dSXin LI 		(void) close(1);
426b6cee71dSXin LI 		if (dup(fdout[1]) == -1)
427b6cee71dSXin LI 			_exit(1);
428b6cee71dSXin LI 		(void) close(fdout[0]);
429b6cee71dSXin LI 		(void) close(fdout[1]);
430b6cee71dSXin LI #ifndef DEBUG
431b6cee71dSXin LI 		if (compr[method].silent)
432b6cee71dSXin LI 			(void)close(2);
433b6cee71dSXin LI #endif
434b6cee71dSXin LI 
435b6cee71dSXin LI 		(void)execvp(compr[method].argv[0],
436b6cee71dSXin LI 		    (char *const *)(intptr_t)compr[method].argv);
437b6cee71dSXin LI #ifdef DEBUG
438b6cee71dSXin LI 		(void)fprintf(stderr, "exec `%s' failed (%s)\n",
439b6cee71dSXin LI 		    compr[method].argv[0], strerror(errno));
440b6cee71dSXin LI #endif
441b6cee71dSXin LI 		exit(1);
442b6cee71dSXin LI 		/*NOTREACHED*/
443b6cee71dSXin LI 	case -1:
444b6cee71dSXin LI 		file_error(ms, errno, "could not fork");
445b6cee71dSXin LI 		return NODATA;
446b6cee71dSXin LI 
447b6cee71dSXin LI 	default: /* parent */
448b6cee71dSXin LI 		(void) close(fdout[1]);
449b6cee71dSXin LI 		if (fd == -1) {
450b6cee71dSXin LI 			(void) close(fdin[0]);
451b6cee71dSXin LI 			/*
452b6cee71dSXin LI 			 * fork again, to avoid blocking because both
453b6cee71dSXin LI 			 * pipes filled
454b6cee71dSXin LI 			 */
455b6cee71dSXin LI 			switch (fork()) {
456b6cee71dSXin LI 			case 0: /* child */
457b6cee71dSXin LI 				(void)close(fdout[0]);
458b6cee71dSXin LI 				if (swrite(fdin[1], old, n) != (ssize_t)n) {
459b6cee71dSXin LI #ifdef DEBUG
460b6cee71dSXin LI 					(void)fprintf(stderr,
461b6cee71dSXin LI 					    "Write failed (%s)\n",
462b6cee71dSXin LI 					    strerror(errno));
463b6cee71dSXin LI #endif
464b6cee71dSXin LI 					exit(1);
465b6cee71dSXin LI 				}
466b6cee71dSXin LI 				exit(0);
467b6cee71dSXin LI 				/*NOTREACHED*/
468b6cee71dSXin LI 
469b6cee71dSXin LI 			case -1:
470b6cee71dSXin LI #ifdef DEBUG
471b6cee71dSXin LI 				(void)fprintf(stderr, "Fork failed (%s)\n",
472b6cee71dSXin LI 				    strerror(errno));
473b6cee71dSXin LI #endif
474b6cee71dSXin LI 				exit(1);
475b6cee71dSXin LI 				/*NOTREACHED*/
476b6cee71dSXin LI 
477b6cee71dSXin LI 			default:  /* parent */
478c2931133SXin LI 				if (wait(&status) == -1) {
479c2931133SXin LI #ifdef DEBUG
480c2931133SXin LI 					(void)fprintf(stderr,
481c2931133SXin LI 					    "Wait failed (%s)\n",
482c2931133SXin LI 					    strerror(errno));
483c2931133SXin LI #endif
484c2931133SXin LI 					exit(1);
485c2931133SXin LI 				}
486c2931133SXin LI 				exit(WIFEXITED(status) ?
487c2931133SXin LI 				    WEXITSTATUS(status) : 1);
488c2931133SXin LI 				/*NOTREACHED*/
489b6cee71dSXin LI 			}
490b6cee71dSXin LI 			(void) close(fdin[1]);
491b6cee71dSXin LI 			fdin[1] = -1;
492b6cee71dSXin LI 		}
493b6cee71dSXin LI 
494b6cee71dSXin LI 		if ((*newch = (unsigned char *) malloc(HOWMANY + 1)) == NULL) {
495b6cee71dSXin LI #ifdef DEBUG
496b6cee71dSXin LI 			(void)fprintf(stderr, "Malloc failed (%s)\n",
497b6cee71dSXin LI 			    strerror(errno));
498b6cee71dSXin LI #endif
499c2931133SXin LI 			n = NODATA;
500b6cee71dSXin LI 			goto err;
501b6cee71dSXin LI 		}
502b6cee71dSXin LI 		if ((r = sread(fdout[0], *newch, HOWMANY, 0)) <= 0) {
503b6cee71dSXin LI #ifdef DEBUG
504b6cee71dSXin LI 			(void)fprintf(stderr, "Read failed (%s)\n",
505b6cee71dSXin LI 			    strerror(errno));
506b6cee71dSXin LI #endif
507b6cee71dSXin LI 			free(*newch);
508c2931133SXin LI 			n = NODATA;
509b6cee71dSXin LI 			*newch = NULL;
510b6cee71dSXin LI 			goto err;
511b6cee71dSXin LI 		} else {
512b6cee71dSXin LI 			n = r;
513b6cee71dSXin LI 		}
514b6cee71dSXin LI  		/* NUL terminate, as every buffer is handled here. */
515b6cee71dSXin LI  		(*newch)[n] = '\0';
516b6cee71dSXin LI err:
517b6cee71dSXin LI 		if (fdin[1] != -1)
518b6cee71dSXin LI 			(void) close(fdin[1]);
519b6cee71dSXin LI 		(void) close(fdout[0]);
520c2931133SXin LI 		if (wait(&status) == -1) {
521c2931133SXin LI #ifdef DEBUG
522c2931133SXin LI 			(void)fprintf(stderr, "Wait failed (%s)\n",
523c2931133SXin LI 			    strerror(errno));
524b6cee71dSXin LI #endif
525c2931133SXin LI 			n = NODATA;
5264460e5b0SXin LI 		} else if (!WIFEXITED(status)) {
527c2931133SXin LI #ifdef DEBUG
5284460e5b0SXin LI 			(void)fprintf(stderr, "Child not exited (0x%x)\n",
5294460e5b0SXin LI 			    status);
530c2931133SXin LI #endif
5314460e5b0SXin LI 		} else if (WEXITSTATUS(status) != 0) {
5324460e5b0SXin LI #ifdef DEBUG
5334460e5b0SXin LI 			(void)fprintf(stderr, "Child exited (0x%d)\n",
5344460e5b0SXin LI 			    WEXITSTATUS(status));
5354460e5b0SXin LI #endif
536c2931133SXin LI 		}
537c2931133SXin LI 
538b6cee71dSXin LI 		(void) close(fdin[0]);
539b6cee71dSXin LI 
540b6cee71dSXin LI 		return n;
541b6cee71dSXin LI 	}
542b6cee71dSXin LI }
543b6cee71dSXin LI #endif
544