compress.c revision 354939
168349Sobrien/*
2133359Sobrien * Copyright (c) Ian F. Darwin 1986-1995.
3133359Sobrien * Software written by Ian F. Darwin and others;
4133359Sobrien * maintained 1995-present by Christos Zoulas and others.
5354939Sdelphij *
6133359Sobrien * Redistribution and use in source and binary forms, with or without
7133359Sobrien * modification, are permitted provided that the following conditions
8133359Sobrien * are met:
9133359Sobrien * 1. Redistributions of source code must retain the above copyright
10133359Sobrien *    notice immediately at the beginning of the file, without modification,
11133359Sobrien *    this list of conditions, and the following disclaimer.
12133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright
13133359Sobrien *    notice, this list of conditions and the following disclaimer in the
14133359Sobrien *    documentation and/or other materials provided with the distribution.
15354939Sdelphij *
16133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26133359Sobrien * SUCH DAMAGE.
27133359Sobrien */
28133359Sobrien/*
2968349Sobrien * compress routines:
3068349Sobrien *	zmagic() - returns 0 if not recognized, uncompresses and prints
3168349Sobrien *		   information if recognized
32354939Sdelphij *	uncompress(method, old, n, newch) - uncompress old into new,
3368349Sobrien *					    using method, return sizeof new
3468349Sobrien */
3568349Sobrien#include "file.h"
36191736Sobrien
37191736Sobrien#ifndef lint
38354939SdelphijFILE_RCSID("@(#)$File: compress.c,v 1.121 2019/05/07 02:27:11 christos Exp $")
39191736Sobrien#endif
40191736Sobrien
41133359Sobrien#include "magic.h"
4268349Sobrien#include <stdlib.h>
4368349Sobrien#ifdef HAVE_UNISTD_H
4468349Sobrien#include <unistd.h>
4568349Sobrien#endif
4668349Sobrien#include <string.h>
47133359Sobrien#include <errno.h>
48298192Sdelphij#include <ctype.h>
49298192Sdelphij#include <stdarg.h>
50276577Sdelphij#include <signal.h>
51354939Sdelphij#ifndef HAVE_SIG_T
52284237Sdelphijtypedef void (*sig_t)(int);
53354939Sdelphij#endif /* HAVE_SIG_T */
54275698Sdelphij#if !defined(__MINGW32__) && !defined(WIN32)
55169942Sobrien#include <sys/ioctl.h>
56226048Sobrien#endif
5768349Sobrien#ifdef HAVE_SYS_WAIT_H
5868349Sobrien#include <sys/wait.h>
5968349Sobrien#endif
60169962Sobrien#if defined(HAVE_SYS_TIME_H)
61169962Sobrien#include <sys/time.h>
62169962Sobrien#endif
63354939Sdelphij
64328874Seadler#if defined(HAVE_ZLIB_H) && defined(ZLIBSUPPORT)
65175296Sobrien#define BUILTIN_DECOMPRESS
66103373Sobrien#include <zlib.h>
67103373Sobrien#endif
68354939Sdelphij
69354939Sdelphij#if defined(HAVE_BZLIB_H)
70354939Sdelphij#define BUILTIN_BZLIB
71354939Sdelphij#include <bzlib.h>
72354939Sdelphij#endif
73354939Sdelphij
74298192Sdelphij#ifdef DEBUG
75298192Sdelphijint tty = -1;
76298192Sdelphij#define DPRINTF(...)	do { \
77298192Sdelphij	if (tty == -1) \
78298192Sdelphij		tty = open("/dev/tty", O_RDWR); \
79298192Sdelphij	if (tty == -1) \
80298192Sdelphij		abort(); \
81298192Sdelphij	dprintf(tty, __VA_ARGS__); \
82298192Sdelphij} while (/*CONSTCOND*/0)
83298192Sdelphij#else
84298192Sdelphij#define DPRINTF(...)
85298192Sdelphij#endif
86103373Sobrien
87298192Sdelphij#ifdef ZLIBSUPPORT
88298192Sdelphij/*
89298192Sdelphij * The following python code is not really used because ZLIBSUPPORT is only
90298192Sdelphij * defined if we have a built-in zlib, and the built-in zlib handles that.
91328874Seadler * That is not true for android where we have zlib.h and not -lz.
92298192Sdelphij */
93298192Sdelphijstatic const char zlibcode[] =
94298192Sdelphij    "import sys, zlib; sys.stdout.write(zlib.decompress(sys.stdin.read()))";
95298192Sdelphij
96298192Sdelphijstatic const char *zlib_args[] = { "python", "-c", zlibcode, NULL };
97298192Sdelphij
98298192Sdelphijstatic int
99298192Sdelphijzlibcmp(const unsigned char *buf)
100298192Sdelphij{
101298192Sdelphij	unsigned short x = 1;
102328874Seadler	unsigned char *s = CAST(unsigned char *, CAST(void *, &x));
103298192Sdelphij
104298192Sdelphij	if ((buf[0] & 0xf) != 8 || (buf[0] & 0x80) != 0)
105298192Sdelphij		return 0;
106298192Sdelphij	if (s[0] != 1)	/* endianness test */
107298192Sdelphij		x = buf[0] | (buf[1] << 8);
108298192Sdelphij	else
109298192Sdelphij		x = buf[1] | (buf[0] << 8);
110298192Sdelphij	if (x % 31)
111298192Sdelphij		return 0;
112298192Sdelphij	return 1;
113298192Sdelphij}
114298192Sdelphij#endif
115298192Sdelphij
116298192Sdelphij#define gzip_flags "-cd"
117298192Sdelphij#define lrzip_flags "-do"
118298192Sdelphij#define lzip_flags gzip_flags
119298192Sdelphij
120298192Sdelphijstatic const char *gzip_args[] = {
121298192Sdelphij	"gzip", gzip_flags, NULL
122298192Sdelphij};
123298192Sdelphijstatic const char *uncompress_args[] = {
124298192Sdelphij	"uncompress", "-c", NULL
125298192Sdelphij};
126298192Sdelphijstatic const char *bzip2_args[] = {
127298192Sdelphij	"bzip2", "-cd", NULL
128298192Sdelphij};
129298192Sdelphijstatic const char *lzip_args[] = {
130298192Sdelphij	"lzip", lzip_flags, NULL
131298192Sdelphij};
132298192Sdelphijstatic const char *xz_args[] = {
133298192Sdelphij	"xz", "-cd", NULL
134298192Sdelphij};
135298192Sdelphijstatic const char *lrzip_args[] = {
136298192Sdelphij	"lrzip", lrzip_flags, NULL
137298192Sdelphij};
138298192Sdelphijstatic const char *lz4_args[] = {
139298192Sdelphij	"lz4", "-cd", NULL
140298192Sdelphij};
141309847Sdelphijstatic const char *zstd_args[] = {
142309847Sdelphij	"zstd", "-cd", NULL
143309847Sdelphij};
144298192Sdelphij
145354939Sdelphij#define	do_zlib		NULL
146354939Sdelphij#define	do_bzlib	NULL
147354939Sdelphij
148186690Sobrienprivate const struct {
149298192Sdelphij	const void *magic;
150133359Sobrien	size_t maglen;
151298192Sdelphij	const char **argv;
152354939Sdelphij	void *unused;
15368349Sobrien} compr[] = {
154354939Sdelphij	{ "\037\235",	2, gzip_args, NULL },		/* compressed */
15575937Sobrien	/* Uncompress can get stuck; so use gzip first if we have it
15675937Sobrien	 * Idea from Damien Clark, thanks! */
157354939Sdelphij	{ "\037\235",	2, uncompress_args, NULL },	/* compressed */
158354939Sdelphij	{ "\037\213",	2, gzip_args, do_zlib },	/* gzipped */
159354939Sdelphij	{ "\037\236",	2, gzip_args, NULL },		/* frozen */
160354939Sdelphij	{ "\037\240",	2, gzip_args, NULL },		/* SCO LZH */
16168349Sobrien	/* the standard pack utilities do not accept standard input */
162354939Sdelphij	{ "\037\036",	2, gzip_args, NULL },		/* packed */
163354939Sdelphij	{ "PK\3\4",	4, gzip_args, NULL },		/* pkzipped, */
164298192Sdelphij	/* ...only first file examined */
165354939Sdelphij	{ "BZh",	3, bzip2_args, do_bzlib },	/* bzip2-ed */
166354939Sdelphij	{ "LZIP",	4, lzip_args, NULL },		/* lzip-ed */
167354939Sdelphij 	{ "\3757zXZ\0",	6, xz_args, NULL },		/* XZ Utils */
168354939Sdelphij 	{ "LRZI",	4, lrzip_args, NULL },	/* LRZIP */
169354939Sdelphij 	{ "\004\"M\030",4, lz4_args, NULL },		/* LZ4 */
170354939Sdelphij 	{ "\x28\xB5\x2F\xFD", 4, zstd_args, NULL },	/* zstd */
171298192Sdelphij#ifdef ZLIBSUPPORT
172354939Sdelphij	{ RCAST(const void *, zlibcmp),	0, zlib_args, NULL },	/* zlib */
173298192Sdelphij#endif
17468349Sobrien};
17568349Sobrien
176298192Sdelphij#define OKDATA 	0
177298192Sdelphij#define NODATA	1
178298192Sdelphij#define ERRDATA	2
17968349Sobrien
180133359Sobrienprivate ssize_t swrite(int, const void *, size_t);
181226048Sobrien#if HAVE_FORK
182354939Sdelphijprivate size_t ncompr = __arraycount(compr);
183298192Sdelphijprivate int uncompressbuf(int, size_t, size_t, const unsigned char *,
184298192Sdelphij    unsigned char **, size_t *);
185175296Sobrien#ifdef BUILTIN_DECOMPRESS
186298192Sdelphijprivate int uncompresszlib(const unsigned char *, unsigned char **, size_t,
187298192Sdelphij    size_t *, int);
188298192Sdelphijprivate int uncompressgzipped(const unsigned char *, unsigned char **, size_t,
189298192Sdelphij    size_t *);
190103373Sobrien#endif
191354939Sdelphij#ifdef BUILTIN_BZLIB
192354939Sdelphijprivate int uncompressbzlib(const unsigned char *, unsigned char **, size_t,
193354939Sdelphij    size_t *, int);
194354939Sdelphij#endif
195354939Sdelphij
196298192Sdelphijstatic int makeerror(unsigned char **, size_t *, const char *, ...)
197298192Sdelphij    __attribute__((__format__(__printf__, 3, 4)));
198298192Sdelphijprivate const char *methodname(size_t);
19968349Sobrien
200337827Seadlerprivate int
201337827Seadlerformat_decompression_error(struct magic_set *ms, size_t i, unsigned char *buf)
202337827Seadler{
203337827Seadler	unsigned char *p;
204337827Seadler	int mime = ms->flags & MAGIC_MIME;
205337827Seadler
206337827Seadler	if (!mime)
207337827Seadler		return file_printf(ms, "ERROR:[%s: %s]", methodname(i), buf);
208337827Seadler
209337827Seadler	for (p = buf; *p; p++)
210337827Seadler		if (!isalnum(*p))
211337827Seadler			*p = '-';
212337827Seadler
213337827Seadler	return file_printf(ms, "application/x-decompression-error-%s-%s",
214337827Seadler	    methodname(i), buf);
215337827Seadler}
216337827Seadler
217133359Sobrienprotected int
218337827Seadlerfile_zmagic(struct magic_set *ms, const struct buffer *b, const char *name)
21968349Sobrien{
220133359Sobrien	unsigned char *newbuf = NULL;
221133359Sobrien	size_t i, nsz;
222298192Sdelphij	char *rbuf;
223298192Sdelphij	file_pushbuf_t *pb;
224299736Sdelphij	int urv, prv, rv = 0;
225175296Sobrien	int mime = ms->flags & MAGIC_MIME;
226337827Seadler	int fd = b->fd;
227354939Sdelphij	const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
228337827Seadler	size_t nbytes = b->flen;
229354939Sdelphij	int sa_saved = 0;
230354939Sdelphij	struct sigaction sig_act;
23168349Sobrien
232133359Sobrien	if ((ms->flags & MAGIC_COMPRESS) == 0)
233133359Sobrien		return 0;
234133359Sobrien
23568349Sobrien	for (i = 0; i < ncompr; i++) {
236298192Sdelphij		int zm;
23768349Sobrien		if (nbytes < compr[i].maglen)
23868349Sobrien			continue;
239298192Sdelphij#ifdef ZLIBSUPPORT
240298192Sdelphij		if (compr[i].maglen == 0)
241309847Sdelphij			zm = (RCAST(int (*)(const unsigned char *),
242298192Sdelphij			    CCAST(void *, compr[i].magic)))(buf);
243298192Sdelphij		else
244298192Sdelphij#endif
245298192Sdelphij			zm = memcmp(buf, compr[i].magic, compr[i].maglen) == 0;
246298192Sdelphij
247298192Sdelphij		if (!zm)
248298192Sdelphij			continue;
249354939Sdelphij
250354939Sdelphij		/* Prevent SIGPIPE death if child dies unexpectedly */
251354939Sdelphij		if (!sa_saved) {
252354939Sdelphij			//We can use sig_act for both new and old, but
253354939Sdelphij			struct sigaction new_act;
254354939Sdelphij			memset(&new_act, 0, sizeof(new_act));
255354939Sdelphij			new_act.sa_handler = SIG_IGN;
256354939Sdelphij			sa_saved = sigaction(SIGPIPE, &new_act, &sig_act) != -1;
257354939Sdelphij		}
258354939Sdelphij
259298192Sdelphij		nsz = nbytes;
260299736Sdelphij		urv = uncompressbuf(fd, ms->bytes_max, i, buf, &newbuf, &nsz);
261354939Sdelphij		DPRINTF("uncompressbuf = %d, %s, %" SIZE_T_FORMAT "u\n", urv,
262354939Sdelphij		    (char *)newbuf, nsz);
263299736Sdelphij		switch (urv) {
264298192Sdelphij		case OKDATA:
265298192Sdelphij		case ERRDATA:
266133359Sobrien			ms->flags &= ~MAGIC_COMPRESS;
267299736Sdelphij			if (urv == ERRDATA)
268337827Seadler				prv = format_decompression_error(ms, i, newbuf);
269298192Sdelphij			else
270354939Sdelphij				prv = file_buffer(ms, -1, NULL, name, newbuf, nsz);
271299736Sdelphij			if (prv == -1)
272133359Sobrien				goto error;
273299736Sdelphij			rv = 1;
274298192Sdelphij			if ((ms->flags & MAGIC_COMPRESS_TRANSP) != 0)
275298192Sdelphij				goto out;
276298192Sdelphij			if (mime != MAGIC_MIME && mime != 0)
277298192Sdelphij				goto out;
278298192Sdelphij			if ((file_printf(ms,
279298192Sdelphij			    mime ? " compressed-encoding=" : " (")) == -1)
280298192Sdelphij				goto error;
281298192Sdelphij			if ((pb = file_push_buffer(ms)) == NULL)
282298192Sdelphij				goto error;
283299736Sdelphij			/*
284299736Sdelphij			 * XXX: If file_buffer fails here, we overwrite
285299736Sdelphij			 * the compressed text. FIXME.
286299736Sdelphij			 */
287354939Sdelphij			if (file_buffer(ms, -1, NULL, NULL, buf, nbytes) == -1) {
288354939Sdelphij				if (file_pop_buffer(ms, pb) != NULL)
289354939Sdelphij					abort();
290298192Sdelphij				goto error;
291354939Sdelphij			}
292298192Sdelphij			if ((rbuf = file_pop_buffer(ms, pb)) != NULL) {
293298192Sdelphij				if (file_printf(ms, "%s", rbuf) == -1) {
294298192Sdelphij					free(rbuf);
295175296Sobrien					goto error;
296298192Sdelphij				}
297298192Sdelphij				free(rbuf);
298175296Sobrien			}
299298192Sdelphij			if (!mime && file_printf(ms, ")") == -1)
300298192Sdelphij				goto error;
301299736Sdelphij			/*FALLTHROUGH*/
302298192Sdelphij		case NODATA:
303299736Sdelphij			break;
304298192Sdelphij		default:
305298192Sdelphij			abort();
306299736Sdelphij			/*NOTREACHED*/
307299736Sdelphij		error:
308299736Sdelphij			rv = -1;
309299736Sdelphij			break;
31068349Sobrien		}
31168349Sobrien	}
312298192Sdelphijout:
313299736Sdelphij	DPRINTF("rv = %d\n", rv);
314299736Sdelphij
315354939Sdelphij	if (sa_saved && sig_act.sa_handler != SIG_IGN)
316354939Sdelphij		(void)sigaction(SIGPIPE, &sig_act, NULL);
317354939Sdelphij
318234250Sobrien	free(newbuf);
319133359Sobrien	ms->flags |= MAGIC_COMPRESS;
320298192Sdelphij	DPRINTF("Zmagic returns %d\n", rv);
321133359Sobrien	return rv;
32268349Sobrien}
323226048Sobrien#endif
32475937Sobrien/*
32575937Sobrien * `safe' write for sockets and pipes.
32675937Sobrien */
327133359Sobrienprivate ssize_t
328103373Sobrienswrite(int fd, const void *buf, size_t n)
32975937Sobrien{
330226048Sobrien	ssize_t rv;
33175937Sobrien	size_t rn = n;
33268349Sobrien
33375937Sobrien	do
33475937Sobrien		switch (rv = write(fd, buf, n)) {
33575937Sobrien		case -1:
33675937Sobrien			if (errno == EINTR)
33775937Sobrien				continue;
33875937Sobrien			return -1;
33975937Sobrien		default:
34075937Sobrien			n -= rv;
341226048Sobrien			buf = CAST(const char *, buf) + rv;
34275937Sobrien			break;
34375937Sobrien		}
34475937Sobrien	while (n > 0);
34575937Sobrien	return rn;
34675937Sobrien}
34775937Sobrien
34875937Sobrien
34975937Sobrien/*
35075937Sobrien * `safe' read for sockets and pipes.
35175937Sobrien */
352169942Sobrienprotected ssize_t
353267843Sdelphijsread(int fd, void *buf, size_t n, int canbepipe __attribute__((__unused__)))
35475937Sobrien{
355226048Sobrien	ssize_t rv;
356169942Sobrien#ifdef FIONREAD
357169942Sobrien	int t = 0;
358169942Sobrien#endif
35975937Sobrien	size_t rn = n;
36075937Sobrien
361169942Sobrien	if (fd == STDIN_FILENO)
362169942Sobrien		goto nocheck;
363169942Sobrien
364169942Sobrien#ifdef FIONREAD
365267843Sdelphij	if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) {
366169942Sobrien#ifdef FD_ZERO
367267843Sdelphij		ssize_t cnt;
368169962Sobrien		for (cnt = 0;; cnt++) {
369169942Sobrien			fd_set check;
370169942Sobrien			struct timeval tout = {0, 100 * 1000};
371169962Sobrien			int selrv;
372169942Sobrien
373169942Sobrien			FD_ZERO(&check);
374169942Sobrien			FD_SET(fd, &check);
375169942Sobrien
376169942Sobrien			/*
377169942Sobrien			 * Avoid soft deadlock: do not read if there
378169942Sobrien			 * is nothing to read from sockets and pipes.
379169942Sobrien			 */
380169962Sobrien			selrv = select(fd + 1, &check, NULL, NULL, &tout);
381169962Sobrien			if (selrv == -1) {
382169942Sobrien				if (errno == EINTR || errno == EAGAIN)
383169942Sobrien					continue;
384169962Sobrien			} else if (selrv == 0 && cnt >= 5) {
385169942Sobrien				return 0;
386169962Sobrien			} else
387169962Sobrien				break;
388169942Sobrien		}
389169942Sobrien#endif
390169942Sobrien		(void)ioctl(fd, FIONREAD, &t);
391169942Sobrien	}
392169942Sobrien
393354939Sdelphij	if (t > 0 && CAST(size_t, t) < n) {
394169942Sobrien		n = t;
395169942Sobrien		rn = n;
396169942Sobrien	}
397169942Sobrien#endif
398169942Sobrien
399169942Sobriennocheck:
40075937Sobrien	do
401169942Sobrien		switch ((rv = read(fd, buf, n))) {
40275937Sobrien		case -1:
40375937Sobrien			if (errno == EINTR)
40475937Sobrien				continue;
40575937Sobrien			return -1;
406103373Sobrien		case 0:
407103373Sobrien			return rn - n;
40875937Sobrien		default:
40975937Sobrien			n -= rv;
410309847Sdelphij			buf = CAST(char *, CCAST(void *, buf)) + rv;
41175937Sobrien			break;
41275937Sobrien		}
41375937Sobrien	while (n > 0);
41475937Sobrien	return rn;
41575937Sobrien}
41675937Sobrien
417133359Sobrienprotected int
418133359Sobrienfile_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
419133359Sobrien    size_t nbytes)
420103373Sobrien{
421103373Sobrien	char buf[4096];
422226048Sobrien	ssize_t r;
423226048Sobrien	int tfd;
424103373Sobrien
425191736Sobrien	(void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof buf);
426103373Sobrien#ifndef HAVE_MKSTEMP
427103373Sobrien	{
428103373Sobrien		char *ptr = mktemp(buf);
429103373Sobrien		tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
430103373Sobrien		r = errno;
431103373Sobrien		(void)unlink(ptr);
432103373Sobrien		errno = r;
433103373Sobrien	}
434103373Sobrien#else
435267843Sdelphij	{
436267843Sdelphij		int te;
437354939Sdelphij		mode_t ou = umask(0);
438267843Sdelphij		tfd = mkstemp(buf);
439354939Sdelphij		(void)umask(ou);
440267843Sdelphij		te = errno;
441267843Sdelphij		(void)unlink(buf);
442267843Sdelphij		errno = te;
443267843Sdelphij	}
444103373Sobrien#endif
445103373Sobrien	if (tfd == -1) {
446133359Sobrien		file_error(ms, errno,
447133359Sobrien		    "cannot create temporary file for pipe copy");
448133359Sobrien		return -1;
449103373Sobrien	}
450103373Sobrien
451354939Sdelphij	if (swrite(tfd, startbuf, nbytes) != CAST(ssize_t, nbytes))
452103373Sobrien		r = 1;
453103373Sobrien	else {
454169962Sobrien		while ((r = sread(fd, buf, sizeof(buf), 1)) > 0)
455354939Sdelphij			if (swrite(tfd, buf, CAST(size_t, r)) != r)
456103373Sobrien				break;
457103373Sobrien	}
458103373Sobrien
459103373Sobrien	switch (r) {
460103373Sobrien	case -1:
461133359Sobrien		file_error(ms, errno, "error copying from pipe to temp file");
462133359Sobrien		return -1;
463103373Sobrien	case 0:
464103373Sobrien		break;
465103373Sobrien	default:
466133359Sobrien		file_error(ms, errno, "error while writing to temp file");
467133359Sobrien		return -1;
468103373Sobrien	}
469103373Sobrien
470103373Sobrien	/*
471103373Sobrien	 * We duplicate the file descriptor, because fclose on a
472103373Sobrien	 * tmpfile will delete the file, but any open descriptors
473103373Sobrien	 * can still access the phantom inode.
474103373Sobrien	 */
475103373Sobrien	if ((fd = dup2(tfd, fd)) == -1) {
476133359Sobrien		file_error(ms, errno, "could not dup descriptor for temp file");
477133359Sobrien		return -1;
478103373Sobrien	}
479103373Sobrien	(void)close(tfd);
480354939Sdelphij	if (lseek(fd, CAST(off_t, 0), SEEK_SET) == CAST(off_t, -1)) {
481133359Sobrien		file_badseek(ms);
482133359Sobrien		return -1;
483103373Sobrien	}
484103373Sobrien	return fd;
485103373Sobrien}
486226048Sobrien#if HAVE_FORK
487175296Sobrien#ifdef BUILTIN_DECOMPRESS
488103373Sobrien
489103373Sobrien#define FHCRC		(1 << 1)
490103373Sobrien#define FEXTRA		(1 << 2)
491103373Sobrien#define FNAME		(1 << 3)
492103373Sobrien#define FCOMMENT	(1 << 4)
493103373Sobrien
494298192Sdelphij
495298192Sdelphijprivate int
496298192Sdelphijuncompressgzipped(const unsigned char *old, unsigned char **newch,
497298192Sdelphij    size_t bytes_max, size_t *n)
49868349Sobrien{
499103373Sobrien	unsigned char flg = old[3];
500133359Sobrien	size_t data_start = 10;
501103373Sobrien
502133359Sobrien	if (flg & FEXTRA) {
503298192Sdelphij		if (data_start + 1 >= *n)
504298192Sdelphij			goto err;
505103373Sobrien		data_start += 2 + old[data_start] + old[data_start + 1] * 256;
506133359Sobrien	}
507103373Sobrien	if (flg & FNAME) {
508298192Sdelphij		while(data_start < *n && old[data_start])
509103373Sobrien			data_start++;
510103373Sobrien		data_start++;
511103373Sobrien	}
512298192Sdelphij	if (flg & FCOMMENT) {
513298192Sdelphij		while(data_start < *n && old[data_start])
514103373Sobrien			data_start++;
515103373Sobrien		data_start++;
516103373Sobrien	}
517298192Sdelphij	if (flg & FHCRC)
518103373Sobrien		data_start += 2;
519103373Sobrien
520298192Sdelphij	if (data_start >= *n)
521298192Sdelphij		goto err;
522298192Sdelphij
523298192Sdelphij	*n -= data_start;
524298192Sdelphij	old += data_start;
525298192Sdelphij	return uncompresszlib(old, newch, bytes_max, n, 0);
526298192Sdelphijerr:
527298192Sdelphij	return makeerror(newch, n, "File too short");
528298192Sdelphij}
529298192Sdelphij
530298192Sdelphijprivate int
531298192Sdelphijuncompresszlib(const unsigned char *old, unsigned char **newch,
532298192Sdelphij    size_t bytes_max, size_t *n, int zlib)
533298192Sdelphij{
534298192Sdelphij	int rc;
535298192Sdelphij	z_stream z;
536298192Sdelphij
537354939Sdelphij	if ((*newch = CAST(unsigned char *, malloc(bytes_max + 1))) == NULL)
538298192Sdelphij		return makeerror(newch, n, "No buffer, %s", strerror(errno));
539298192Sdelphij
540298192Sdelphij	z.next_in = CCAST(Bytef *, old);
541298192Sdelphij	z.avail_in = CAST(uint32_t, *n);
542103373Sobrien	z.next_out = *newch;
543328874Seadler	z.avail_out = CAST(unsigned int, bytes_max);
544103373Sobrien	z.zalloc = Z_NULL;
545103373Sobrien	z.zfree = Z_NULL;
546103373Sobrien	z.opaque = Z_NULL;
547103373Sobrien
548226048Sobrien	/* LINTED bug in header macro */
549298192Sdelphij	rc = zlib ? inflateInit(&z) : inflateInit2(&z, -15);
550298192Sdelphij	if (rc != Z_OK)
551298192Sdelphij		goto err;
552103373Sobrien
553103373Sobrien	rc = inflate(&z, Z_SYNC_FLUSH);
554298192Sdelphij	if (rc != Z_OK && rc != Z_STREAM_END)
555298192Sdelphij		goto err;
556103373Sobrien
557354939Sdelphij	*n = CAST(size_t, z.total_out);
558298192Sdelphij	rc = inflateEnd(&z);
559298192Sdelphij	if (rc != Z_OK)
560298192Sdelphij		goto err;
561354939Sdelphij
562103373Sobrien	/* let's keep the nul-terminate tradition */
563298192Sdelphij	(*newch)[*n] = '\0';
564103373Sobrien
565298192Sdelphij	return OKDATA;
566298192Sdelphijerr:
567354939Sdelphij	strlcpy(RCAST(char *, *newch), z.msg ? z.msg : zError(rc), bytes_max);
568354939Sdelphij	*n = strlen(RCAST(char *, *newch));
569298192Sdelphij	return ERRDATA;
570298192Sdelphij}
571298192Sdelphij#endif
572298192Sdelphij
573298192Sdelphijstatic int
574298192Sdelphijmakeerror(unsigned char **buf, size_t *len, const char *fmt, ...)
575298192Sdelphij{
576298192Sdelphij	char *msg;
577298192Sdelphij	va_list ap;
578298192Sdelphij	int rv;
579298192Sdelphij
580298192Sdelphij	va_start(ap, fmt);
581298192Sdelphij	rv = vasprintf(&msg, fmt, ap);
582298192Sdelphij	va_end(ap);
583298192Sdelphij	if (rv < 0) {
584298192Sdelphij		*buf = NULL;
585298192Sdelphij		*len = 0;
586298192Sdelphij		return NODATA;
587298192Sdelphij	}
588354939Sdelphij	*buf = RCAST(unsigned char *, msg);
589298192Sdelphij	*len = strlen(msg);
590298192Sdelphij	return ERRDATA;
591298192Sdelphij}
592298192Sdelphij
593298192Sdelphijstatic void
594298192Sdelphijclosefd(int *fd, size_t i)
595298192Sdelphij{
596298192Sdelphij	if (fd[i] == -1)
597298192Sdelphij		return;
598298192Sdelphij	(void) close(fd[i]);
599298192Sdelphij	fd[i] = -1;
600298192Sdelphij}
601298192Sdelphij
602298192Sdelphijstatic void
603298192Sdelphijclosep(int *fd)
604298192Sdelphij{
605298192Sdelphij	size_t i;
606298192Sdelphij	for (i = 0; i < 2; i++)
607298192Sdelphij		closefd(fd, i);
608298192Sdelphij}
609298192Sdelphij
610354939Sdelphijstatic int
611354939Sdelphijcopydesc(int i, int fd)
612298192Sdelphij{
613354939Sdelphij	if (fd == i)
614354939Sdelphij		return 0; /* "no dup was necessary" */
615354939Sdelphij	if (dup2(fd, i) == -1) {
616354939Sdelphij		DPRINTF("dup(%d, %d) failed (%s)\n", fd, i, strerror(errno));
617298192Sdelphij		exit(1);
618298192Sdelphij	}
619354939Sdelphij	return 1;
620298192Sdelphij}
621298192Sdelphij
622354939Sdelphijstatic pid_t
623354939Sdelphijwritechild(int fd, const void *old, size_t n)
624298192Sdelphij{
625354939Sdelphij	pid_t pid;
626298192Sdelphij
627354939Sdelphij	/*
628298192Sdelphij	 * fork again, to avoid blocking because both
629298192Sdelphij	 * pipes filled
630298192Sdelphij	 */
631354939Sdelphij	pid = fork();
632354939Sdelphij	if (pid == -1) {
633354939Sdelphij		DPRINTF("Fork failed (%s)\n", strerror(errno));
634354939Sdelphij		exit(1);
635354939Sdelphij	}
636354939Sdelphij	if (pid == 0) {
637354939Sdelphij		/* child */
638354939Sdelphij		if (swrite(fd, old, n) != CAST(ssize_t, n)) {
639298192Sdelphij			DPRINTF("Write failed (%s)\n", strerror(errno));
640298192Sdelphij			exit(1);
641298192Sdelphij		}
642298192Sdelphij		exit(0);
643298192Sdelphij	}
644354939Sdelphij	/* parent */
645354939Sdelphij	return pid;
646298192Sdelphij}
647298192Sdelphij
648298192Sdelphijstatic ssize_t
649298192Sdelphijfilter_error(unsigned char *ubuf, ssize_t n)
650298192Sdelphij{
651298192Sdelphij	char *p;
652298192Sdelphij	char *buf;
653298192Sdelphij
654298192Sdelphij	ubuf[n] = '\0';
655354939Sdelphij	buf = RCAST(char *, ubuf);
656354939Sdelphij	while (isspace(CAST(unsigned char, *buf)))
657298192Sdelphij		buf++;
658298192Sdelphij	DPRINTF("Filter error[[[%s]]]\n", buf);
659354939Sdelphij	if ((p = strchr(CAST(char *, buf), '\n')) != NULL)
660298192Sdelphij		*p = '\0';
661354939Sdelphij	if ((p = strchr(CAST(char *, buf), ';')) != NULL)
662298192Sdelphij		*p = '\0';
663354939Sdelphij	if ((p = strrchr(CAST(char *, buf), ':')) != NULL) {
664298192Sdelphij		++p;
665354939Sdelphij		while (isspace(CAST(unsigned char, *p)))
666298192Sdelphij			p++;
667298192Sdelphij		n = strlen(p);
668328874Seadler		memmove(ubuf, p, CAST(size_t, n + 1));
669298192Sdelphij	}
670298192Sdelphij	DPRINTF("Filter error after[[[%s]]]\n", (char *)ubuf);
671298192Sdelphij	if (islower(*ubuf))
672298192Sdelphij		*ubuf = toupper(*ubuf);
673103373Sobrien	return n;
674103373Sobrien}
675298192Sdelphij
676298192Sdelphijprivate const char *
677298192Sdelphijmethodname(size_t method)
678298192Sdelphij{
679298192Sdelphij#ifdef BUILTIN_DECOMPRESS
680298192Sdelphij        /* FIXME: This doesn't cope with bzip2 */
681298192Sdelphij	if (method == 2 || compr[method].maglen == 0)
682298192Sdelphij	    return "zlib";
683103373Sobrien#endif
684298192Sdelphij	return compr[method].argv[0];
685298192Sdelphij}
686103373Sobrien
687298192Sdelphijprivate int
688298192Sdelphijuncompressbuf(int fd, size_t bytes_max, size_t method, const unsigned char *old,
689298192Sdelphij    unsigned char **newch, size_t* n)
690103373Sobrien{
691298192Sdelphij	int fdp[3][2];
692354939Sdelphij	int status, rv, w;
693354939Sdelphij	pid_t pid;
694354939Sdelphij	pid_t writepid = -1;
695298192Sdelphij	size_t i;
696226048Sobrien	ssize_t r;
69768349Sobrien
698175296Sobrien#ifdef BUILTIN_DECOMPRESS
699186690Sobrien        /* FIXME: This doesn't cope with bzip2 */
700103373Sobrien	if (method == 2)
701298192Sdelphij		return uncompressgzipped(old, newch, bytes_max, n);
702298192Sdelphij	if (compr[method].maglen == 0)
703298192Sdelphij		return uncompresszlib(old, newch, bytes_max, n, 1);
704103373Sobrien#endif
705159764Sobrien	(void)fflush(stdout);
706159764Sobrien	(void)fflush(stderr);
707103373Sobrien
708298192Sdelphij	for (i = 0; i < __arraycount(fdp); i++)
709298192Sdelphij		fdp[i][0] = fdp[i][1] = -1;
710298192Sdelphij
711298192Sdelphij	if ((fd == -1 && pipe(fdp[STDIN_FILENO]) == -1) ||
712298192Sdelphij	    pipe(fdp[STDOUT_FILENO]) == -1 || pipe(fdp[STDERR_FILENO]) == -1) {
713298192Sdelphij		closep(fdp[STDIN_FILENO]);
714298192Sdelphij		closep(fdp[STDOUT_FILENO]);
715298192Sdelphij		return makeerror(newch, n, "Cannot create pipe, %s",
716298192Sdelphij		    strerror(errno));
71768349Sobrien	}
718354939Sdelphij
719354939Sdelphij	/* For processes with large mapped virtual sizes, vfork
720354939Sdelphij	 * may be _much_ faster (10-100 times) than fork.
721354939Sdelphij	 */
722354939Sdelphij	pid = vfork();
723354939Sdelphij	if (pid == -1) {
724354939Sdelphij		return makeerror(newch, n, "Cannot vfork, %s",
725354939Sdelphij		    strerror(errno));
726354939Sdelphij	}
727354939Sdelphij	if (pid == 0) {
728354939Sdelphij		/* child */
729354939Sdelphij		/* Note: we are after vfork, do not modify memory
730354939Sdelphij		 * in a way which confuses parent. In particular,
731354939Sdelphij		 * do not modify fdp[i][j].
732354939Sdelphij		 */
733159764Sobrien		if (fd != -1) {
734354939Sdelphij			(void) lseek(fd, CAST(off_t, 0), SEEK_SET);
735354939Sdelphij			if (copydesc(STDIN_FILENO, fd))
736354939Sdelphij				(void) close(fd);
737354939Sdelphij		} else {
738354939Sdelphij			if (copydesc(STDIN_FILENO, fdp[STDIN_FILENO][0]))
739354939Sdelphij				(void) close(fdp[STDIN_FILENO][0]);
740354939Sdelphij			if (fdp[STDIN_FILENO][1] > 2)
741354939Sdelphij				(void) close(fdp[STDIN_FILENO][1]);
742159764Sobrien		}
743354939Sdelphij///FIXME: if one of the fdp[i][j] is 0 or 1, this can bomb spectacularly
744354939Sdelphij		if (copydesc(STDOUT_FILENO, fdp[STDOUT_FILENO][1]))
745354939Sdelphij			(void) close(fdp[STDOUT_FILENO][1]);
746354939Sdelphij		if (fdp[STDOUT_FILENO][0] > 2)
747354939Sdelphij			(void) close(fdp[STDOUT_FILENO][0]);
74868349Sobrien
749354939Sdelphij		if (copydesc(STDERR_FILENO, fdp[STDERR_FILENO][1]))
750354939Sdelphij			(void) close(fdp[STDERR_FILENO][1]);
751354939Sdelphij		if (fdp[STDERR_FILENO][0] > 2)
752354939Sdelphij			(void) close(fdp[STDERR_FILENO][0]);
753354939Sdelphij
754169962Sobrien		(void)execvp(compr[method].argv[0],
755354939Sdelphij		    RCAST(char *const *, RCAST(intptr_t, compr[method].argv)));
756354939Sdelphij		dprintf(STDERR_FILENO, "exec `%s' failed, %s",
757159764Sobrien		    compr[method].argv[0], strerror(errno));
758354939Sdelphij		_exit(1); /* _exit(), not exit(), because of vfork */
759354939Sdelphij	}
760354939Sdelphij	/* parent */
761354939Sdelphij	/* Close write sides of child stdout/err pipes */
762354939Sdelphij	for (i = 1; i < __arraycount(fdp); i++)
763354939Sdelphij		closefd(fdp[i], 1);
764354939Sdelphij	/* Write the buffer data to child stdin, if we don't have fd */
765354939Sdelphij	if (fd == -1) {
766354939Sdelphij		closefd(fdp[STDIN_FILENO], 0);
767354939Sdelphij		writepid = writechild(fdp[STDIN_FILENO][1], old, *n);
768354939Sdelphij		closefd(fdp[STDIN_FILENO], 1);
769354939Sdelphij	}
770354939Sdelphij
771354939Sdelphij	*newch = CAST(unsigned char *, malloc(bytes_max + 1));
772354939Sdelphij	if (*newch == NULL) {
773354939Sdelphij		rv = makeerror(newch, n, "No buffer, %s",
774298192Sdelphij		    strerror(errno));
775354939Sdelphij		goto err;
776354939Sdelphij	}
777354939Sdelphij	rv = OKDATA;
778354939Sdelphij	r = sread(fdp[STDOUT_FILENO][0], *newch, bytes_max, 0);
779354939Sdelphij	if (r <= 0) {
780298192Sdelphij		DPRINTF("Read stdout failed %d (%s)\n", fdp[STDOUT_FILENO][0],
781298192Sdelphij		    r != -1 ? strerror(errno) : "no data");
782298192Sdelphij
783298192Sdelphij		rv = ERRDATA;
784298192Sdelphij		if (r == 0 &&
785298192Sdelphij		    (r = sread(fdp[STDERR_FILENO][0], *newch, bytes_max, 0)) > 0)
786298192Sdelphij		{
787298192Sdelphij			r = filter_error(*newch, r);
788354939Sdelphij			goto ok;
78968349Sobrien		}
790298192Sdelphij		free(*newch);
791298192Sdelphij		if  (r == 0)
792298192Sdelphij			rv = makeerror(newch, n, "Read failed, %s",
793275698Sdelphij			    strerror(errno));
794298192Sdelphij		else
795298192Sdelphij			rv = makeerror(newch, n, "No data");
796298192Sdelphij		goto err;
797298192Sdelphij	}
798354939Sdelphijok:
799298192Sdelphij	*n = r;
800298192Sdelphij	/* NUL terminate, as every buffer is handled here. */
801298192Sdelphij	(*newch)[*n] = '\0';
802298192Sdelphijerr:
803298192Sdelphij	closefd(fdp[STDIN_FILENO], 1);
804298192Sdelphij	closefd(fdp[STDOUT_FILENO], 0);
805298192Sdelphij	closefd(fdp[STDERR_FILENO], 0);
806354939Sdelphij
807354939Sdelphij	w = waitpid(pid, &status, 0);
808354939Sdelphijwait_err:
809354939Sdelphij	if (w == -1) {
810298192Sdelphij		free(*newch);
811298192Sdelphij		rv = makeerror(newch, n, "Wait failed, %s", strerror(errno));
812298192Sdelphij		DPRINTF("Child wait return %#x\n", status);
813298192Sdelphij	} else if (!WIFEXITED(status)) {
814328874Seadler		DPRINTF("Child not exited (%#x)\n", status);
815298192Sdelphij	} else if (WEXITSTATUS(status) != 0) {
816328874Seadler		DPRINTF("Child exited (%#x)\n", WEXITSTATUS(status));
81768349Sobrien	}
818354939Sdelphij	if (writepid > 0) {
819354939Sdelphij		/* _After_ we know decompressor has exited, our input writer
820354939Sdelphij		 * definitely will exit now (at worst, writing fails in it,
821354939Sdelphij		 * since output fd is closed now on the reading size).
822354939Sdelphij		 */
823354939Sdelphij		w = waitpid(writepid, &status, 0);
824354939Sdelphij		writepid = -1;
825354939Sdelphij		goto wait_err;
826354939Sdelphij	}
827298192Sdelphij
828354939Sdelphij	closefd(fdp[STDIN_FILENO], 0); //why? it is already closed here!
829354939Sdelphij	DPRINTF("Returning %p n=%" SIZE_T_FORMAT "u rv=%d\n", *newch, *n, rv);
830354939Sdelphij
831298192Sdelphij	return rv;
83268349Sobrien}
833226048Sobrien#endif
834