compress.c revision 328874
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.
5133359Sobrien *
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.
15133359Sobrien *
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
3268349Sobrien *	uncompress(method, old, n, newch) - uncompress old into new,
3368349Sobrien *					    using method, return sizeof new
3468349Sobrien */
3568349Sobrien#include "file.h"
36191736Sobrien
37191736Sobrien#ifndef lint
38328874SeadlerFILE_RCSID("@(#)$File: compress.c,v 1.105 2017/05/25 00:13:03 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>
50284237Sdelphij#ifdef HAVE_SIGNAL_H
51276577Sdelphij#include <signal.h>
52284237Sdelphij# ifndef HAVE_SIG_T
53284237Sdelphijtypedef void (*sig_t)(int);
54284237Sdelphij# endif /* HAVE_SIG_T */
55284237Sdelphij#endif
56275698Sdelphij#if !defined(__MINGW32__) && !defined(WIN32)
57169942Sobrien#include <sys/ioctl.h>
58226048Sobrien#endif
5968349Sobrien#ifdef HAVE_SYS_WAIT_H
6068349Sobrien#include <sys/wait.h>
6168349Sobrien#endif
62169962Sobrien#if defined(HAVE_SYS_TIME_H)
63169962Sobrien#include <sys/time.h>
64169962Sobrien#endif
65328874Seadler#if defined(HAVE_ZLIB_H) && defined(ZLIBSUPPORT)
66175296Sobrien#define BUILTIN_DECOMPRESS
67103373Sobrien#include <zlib.h>
68103373Sobrien#endif
69298192Sdelphij#ifdef DEBUG
70298192Sdelphijint tty = -1;
71298192Sdelphij#define DPRINTF(...)	do { \
72298192Sdelphij	if (tty == -1) \
73298192Sdelphij		tty = open("/dev/tty", O_RDWR); \
74298192Sdelphij	if (tty == -1) \
75298192Sdelphij		abort(); \
76298192Sdelphij	dprintf(tty, __VA_ARGS__); \
77298192Sdelphij} while (/*CONSTCOND*/0)
78298192Sdelphij#else
79298192Sdelphij#define DPRINTF(...)
80298192Sdelphij#endif
81103373Sobrien
82298192Sdelphij#ifdef ZLIBSUPPORT
83298192Sdelphij/*
84298192Sdelphij * The following python code is not really used because ZLIBSUPPORT is only
85298192Sdelphij * defined if we have a built-in zlib, and the built-in zlib handles that.
86328874Seadler * That is not true for android where we have zlib.h and not -lz.
87298192Sdelphij */
88298192Sdelphijstatic const char zlibcode[] =
89298192Sdelphij    "import sys, zlib; sys.stdout.write(zlib.decompress(sys.stdin.read()))";
90298192Sdelphij
91298192Sdelphijstatic const char *zlib_args[] = { "python", "-c", zlibcode, NULL };
92298192Sdelphij
93298192Sdelphijstatic int
94298192Sdelphijzlibcmp(const unsigned char *buf)
95298192Sdelphij{
96298192Sdelphij	unsigned short x = 1;
97328874Seadler	unsigned char *s = CAST(unsigned char *, CAST(void *, &x));
98298192Sdelphij
99298192Sdelphij	if ((buf[0] & 0xf) != 8 || (buf[0] & 0x80) != 0)
100298192Sdelphij		return 0;
101298192Sdelphij	if (s[0] != 1)	/* endianness test */
102298192Sdelphij		x = buf[0] | (buf[1] << 8);
103298192Sdelphij	else
104298192Sdelphij		x = buf[1] | (buf[0] << 8);
105298192Sdelphij	if (x % 31)
106298192Sdelphij		return 0;
107298192Sdelphij	return 1;
108298192Sdelphij}
109298192Sdelphij#endif
110298192Sdelphij
111298192Sdelphij#define gzip_flags "-cd"
112298192Sdelphij#define lrzip_flags "-do"
113298192Sdelphij#define lzip_flags gzip_flags
114298192Sdelphij
115298192Sdelphijstatic const char *gzip_args[] = {
116298192Sdelphij	"gzip", gzip_flags, NULL
117298192Sdelphij};
118298192Sdelphijstatic const char *uncompress_args[] = {
119298192Sdelphij	"uncompress", "-c", NULL
120298192Sdelphij};
121298192Sdelphijstatic const char *bzip2_args[] = {
122298192Sdelphij	"bzip2", "-cd", NULL
123298192Sdelphij};
124298192Sdelphijstatic const char *lzip_args[] = {
125298192Sdelphij	"lzip", lzip_flags, NULL
126298192Sdelphij};
127298192Sdelphijstatic const char *xz_args[] = {
128298192Sdelphij	"xz", "-cd", NULL
129298192Sdelphij};
130298192Sdelphijstatic const char *lrzip_args[] = {
131298192Sdelphij	"lrzip", lrzip_flags, NULL
132298192Sdelphij};
133298192Sdelphijstatic const char *lz4_args[] = {
134298192Sdelphij	"lz4", "-cd", NULL
135298192Sdelphij};
136309847Sdelphijstatic const char *zstd_args[] = {
137309847Sdelphij	"zstd", "-cd", NULL
138309847Sdelphij};
139298192Sdelphij
140186690Sobrienprivate const struct {
141298192Sdelphij	const void *magic;
142133359Sobrien	size_t maglen;
143298192Sdelphij	const char **argv;
14468349Sobrien} compr[] = {
145298192Sdelphij	{ "\037\235",	2, gzip_args },		/* compressed */
14675937Sobrien	/* Uncompress can get stuck; so use gzip first if we have it
14775937Sobrien	 * Idea from Damien Clark, thanks! */
148298192Sdelphij	{ "\037\235",	2, uncompress_args },	/* compressed */
149298192Sdelphij	{ "\037\213",	2, gzip_args },		/* gzipped */
150298192Sdelphij	{ "\037\236",	2, gzip_args },		/* frozen */
151298192Sdelphij	{ "\037\240",	2, gzip_args },		/* SCO LZH */
15268349Sobrien	/* the standard pack utilities do not accept standard input */
153298192Sdelphij	{ "\037\036",	2, gzip_args },		/* packed */
154298192Sdelphij	{ "PK\3\4",	4, gzip_args },		/* pkzipped, */
155298192Sdelphij	/* ...only first file examined */
156298192Sdelphij	{ "BZh",	3, bzip2_args },	/* bzip2-ed */
157298192Sdelphij	{ "LZIP",	4, lzip_args },		/* lzip-ed */
158298192Sdelphij 	{ "\3757zXZ\0",	6, xz_args },		/* XZ Utils */
159298192Sdelphij 	{ "LRZI",	4, lrzip_args },	/* LRZIP */
160298192Sdelphij 	{ "\004\"M\030",4, lz4_args },		/* LZ4 */
161309847Sdelphij 	{ "\x28\xB5\x2F\xFD", 4, zstd_args },	/* zstd */
162298192Sdelphij#ifdef ZLIBSUPPORT
163309847Sdelphij	{ RCAST(const void *, zlibcmp),	0, zlib_args },		/* zlib */
164298192Sdelphij#endif
16568349Sobrien};
16668349Sobrien
167298192Sdelphij#define OKDATA 	0
168298192Sdelphij#define NODATA	1
169298192Sdelphij#define ERRDATA	2
17068349Sobrien
171133359Sobrienprivate ssize_t swrite(int, const void *, size_t);
172226048Sobrien#if HAVE_FORK
173226048Sobrienprivate size_t ncompr = sizeof(compr) / sizeof(compr[0]);
174298192Sdelphijprivate int uncompressbuf(int, size_t, size_t, const unsigned char *,
175298192Sdelphij    unsigned char **, size_t *);
176175296Sobrien#ifdef BUILTIN_DECOMPRESS
177298192Sdelphijprivate int uncompresszlib(const unsigned char *, unsigned char **, size_t,
178298192Sdelphij    size_t *, int);
179298192Sdelphijprivate int uncompressgzipped(const unsigned char *, unsigned char **, size_t,
180298192Sdelphij    size_t *);
181103373Sobrien#endif
182298192Sdelphijstatic int makeerror(unsigned char **, size_t *, const char *, ...)
183298192Sdelphij    __attribute__((__format__(__printf__, 3, 4)));
184298192Sdelphijprivate const char *methodname(size_t);
18568349Sobrien
186133359Sobrienprotected int
187169962Sobrienfile_zmagic(struct magic_set *ms, int fd, const char *name,
188169962Sobrien    const unsigned char *buf, size_t nbytes)
18968349Sobrien{
190133359Sobrien	unsigned char *newbuf = NULL;
191133359Sobrien	size_t i, nsz;
192298192Sdelphij	char *rbuf;
193298192Sdelphij	file_pushbuf_t *pb;
194299736Sdelphij	int urv, prv, rv = 0;
195175296Sobrien	int mime = ms->flags & MAGIC_MIME;
196284237Sdelphij#ifdef HAVE_SIGNAL_H
197276577Sdelphij	sig_t osigpipe;
198284237Sdelphij#endif
19968349Sobrien
200133359Sobrien	if ((ms->flags & MAGIC_COMPRESS) == 0)
201133359Sobrien		return 0;
202133359Sobrien
203284237Sdelphij#ifdef HAVE_SIGNAL_H
204276577Sdelphij	osigpipe = signal(SIGPIPE, SIG_IGN);
205284237Sdelphij#endif
20668349Sobrien	for (i = 0; i < ncompr; i++) {
207298192Sdelphij		int zm;
20868349Sobrien		if (nbytes < compr[i].maglen)
20968349Sobrien			continue;
210298192Sdelphij#ifdef ZLIBSUPPORT
211298192Sdelphij		if (compr[i].maglen == 0)
212309847Sdelphij			zm = (RCAST(int (*)(const unsigned char *),
213298192Sdelphij			    CCAST(void *, compr[i].magic)))(buf);
214298192Sdelphij		else
215298192Sdelphij#endif
216298192Sdelphij			zm = memcmp(buf, compr[i].magic, compr[i].maglen) == 0;
217298192Sdelphij
218298192Sdelphij		if (!zm)
219298192Sdelphij			continue;
220298192Sdelphij		nsz = nbytes;
221299736Sdelphij		urv = uncompressbuf(fd, ms->bytes_max, i, buf, &newbuf, &nsz);
222299736Sdelphij		DPRINTF("uncompressbuf = %d, %s, %zu\n", urv, (char *)newbuf,
223298192Sdelphij		    nsz);
224299736Sdelphij		switch (urv) {
225298192Sdelphij		case OKDATA:
226298192Sdelphij		case ERRDATA:
227298192Sdelphij
228133359Sobrien			ms->flags &= ~MAGIC_COMPRESS;
229299736Sdelphij			if (urv == ERRDATA)
230299736Sdelphij				prv = file_printf(ms, "%s ERROR: %s",
231298192Sdelphij				    methodname(i), newbuf);
232298192Sdelphij			else
233299736Sdelphij				prv = file_buffer(ms, -1, name, newbuf, nsz);
234299736Sdelphij			if (prv == -1)
235133359Sobrien				goto error;
236299736Sdelphij			rv = 1;
237298192Sdelphij			if ((ms->flags & MAGIC_COMPRESS_TRANSP) != 0)
238298192Sdelphij				goto out;
239298192Sdelphij			if (mime != MAGIC_MIME && mime != 0)
240298192Sdelphij				goto out;
241298192Sdelphij			if ((file_printf(ms,
242298192Sdelphij			    mime ? " compressed-encoding=" : " (")) == -1)
243298192Sdelphij				goto error;
244298192Sdelphij			if ((pb = file_push_buffer(ms)) == NULL)
245298192Sdelphij				goto error;
246299736Sdelphij			/*
247299736Sdelphij			 * XXX: If file_buffer fails here, we overwrite
248299736Sdelphij			 * the compressed text. FIXME.
249299736Sdelphij			 */
250298192Sdelphij			if (file_buffer(ms, -1, NULL, buf, nbytes) == -1)
251298192Sdelphij				goto error;
252298192Sdelphij			if ((rbuf = file_pop_buffer(ms, pb)) != NULL) {
253298192Sdelphij				if (file_printf(ms, "%s", rbuf) == -1) {
254298192Sdelphij					free(rbuf);
255175296Sobrien					goto error;
256298192Sdelphij				}
257298192Sdelphij				free(rbuf);
258175296Sobrien			}
259298192Sdelphij			if (!mime && file_printf(ms, ")") == -1)
260298192Sdelphij				goto error;
261299736Sdelphij			/*FALLTHROUGH*/
262298192Sdelphij		case NODATA:
263299736Sdelphij			break;
264298192Sdelphij		default:
265298192Sdelphij			abort();
266299736Sdelphij			/*NOTREACHED*/
267299736Sdelphij		error:
268299736Sdelphij			rv = -1;
269299736Sdelphij			break;
27068349Sobrien		}
27168349Sobrien	}
272298192Sdelphijout:
273299736Sdelphij	DPRINTF("rv = %d\n", rv);
274299736Sdelphij
275284237Sdelphij#ifdef HAVE_SIGNAL_H
276276577Sdelphij	(void)signal(SIGPIPE, osigpipe);
277284237Sdelphij#endif
278234250Sobrien	free(newbuf);
279133359Sobrien	ms->flags |= MAGIC_COMPRESS;
280298192Sdelphij	DPRINTF("Zmagic returns %d\n", rv);
281133359Sobrien	return rv;
28268349Sobrien}
283226048Sobrien#endif
28475937Sobrien/*
28575937Sobrien * `safe' write for sockets and pipes.
28675937Sobrien */
287133359Sobrienprivate ssize_t
288103373Sobrienswrite(int fd, const void *buf, size_t n)
28975937Sobrien{
290226048Sobrien	ssize_t rv;
29175937Sobrien	size_t rn = n;
29268349Sobrien
29375937Sobrien	do
29475937Sobrien		switch (rv = write(fd, buf, n)) {
29575937Sobrien		case -1:
29675937Sobrien			if (errno == EINTR)
29775937Sobrien				continue;
29875937Sobrien			return -1;
29975937Sobrien		default:
30075937Sobrien			n -= rv;
301226048Sobrien			buf = CAST(const char *, buf) + rv;
30275937Sobrien			break;
30375937Sobrien		}
30475937Sobrien	while (n > 0);
30575937Sobrien	return rn;
30675937Sobrien}
30775937Sobrien
30875937Sobrien
30975937Sobrien/*
31075937Sobrien * `safe' read for sockets and pipes.
31175937Sobrien */
312169942Sobrienprotected ssize_t
313267843Sdelphijsread(int fd, void *buf, size_t n, int canbepipe __attribute__((__unused__)))
31475937Sobrien{
315226048Sobrien	ssize_t rv;
316169942Sobrien#ifdef FIONREAD
317169942Sobrien	int t = 0;
318169942Sobrien#endif
31975937Sobrien	size_t rn = n;
32075937Sobrien
321169942Sobrien	if (fd == STDIN_FILENO)
322169942Sobrien		goto nocheck;
323169942Sobrien
324169942Sobrien#ifdef FIONREAD
325267843Sdelphij	if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) {
326169942Sobrien#ifdef FD_ZERO
327267843Sdelphij		ssize_t cnt;
328169962Sobrien		for (cnt = 0;; cnt++) {
329169942Sobrien			fd_set check;
330169942Sobrien			struct timeval tout = {0, 100 * 1000};
331169962Sobrien			int selrv;
332169942Sobrien
333169942Sobrien			FD_ZERO(&check);
334169942Sobrien			FD_SET(fd, &check);
335169942Sobrien
336169942Sobrien			/*
337169942Sobrien			 * Avoid soft deadlock: do not read if there
338169942Sobrien			 * is nothing to read from sockets and pipes.
339169942Sobrien			 */
340169962Sobrien			selrv = select(fd + 1, &check, NULL, NULL, &tout);
341169962Sobrien			if (selrv == -1) {
342169942Sobrien				if (errno == EINTR || errno == EAGAIN)
343169942Sobrien					continue;
344169962Sobrien			} else if (selrv == 0 && cnt >= 5) {
345169942Sobrien				return 0;
346169962Sobrien			} else
347169962Sobrien				break;
348169942Sobrien		}
349169942Sobrien#endif
350169942Sobrien		(void)ioctl(fd, FIONREAD, &t);
351169942Sobrien	}
352169942Sobrien
353169942Sobrien	if (t > 0 && (size_t)t < n) {
354169942Sobrien		n = t;
355169942Sobrien		rn = n;
356169942Sobrien	}
357169942Sobrien#endif
358169942Sobrien
359169942Sobriennocheck:
36075937Sobrien	do
361169942Sobrien		switch ((rv = read(fd, buf, n))) {
36275937Sobrien		case -1:
36375937Sobrien			if (errno == EINTR)
36475937Sobrien				continue;
36575937Sobrien			return -1;
366103373Sobrien		case 0:
367103373Sobrien			return rn - n;
36875937Sobrien		default:
36975937Sobrien			n -= rv;
370309847Sdelphij			buf = CAST(char *, CCAST(void *, buf)) + rv;
37175937Sobrien			break;
37275937Sobrien		}
37375937Sobrien	while (n > 0);
37475937Sobrien	return rn;
37575937Sobrien}
37675937Sobrien
377133359Sobrienprotected int
378133359Sobrienfile_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
379133359Sobrien    size_t nbytes)
380103373Sobrien{
381103373Sobrien	char buf[4096];
382226048Sobrien	ssize_t r;
383226048Sobrien	int tfd;
384103373Sobrien
385191736Sobrien	(void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof buf);
386103373Sobrien#ifndef HAVE_MKSTEMP
387103373Sobrien	{
388103373Sobrien		char *ptr = mktemp(buf);
389103373Sobrien		tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
390103373Sobrien		r = errno;
391103373Sobrien		(void)unlink(ptr);
392103373Sobrien		errno = r;
393103373Sobrien	}
394103373Sobrien#else
395267843Sdelphij	{
396267843Sdelphij		int te;
397267843Sdelphij		tfd = mkstemp(buf);
398267843Sdelphij		te = errno;
399267843Sdelphij		(void)unlink(buf);
400267843Sdelphij		errno = te;
401267843Sdelphij	}
402103373Sobrien#endif
403103373Sobrien	if (tfd == -1) {
404133359Sobrien		file_error(ms, errno,
405133359Sobrien		    "cannot create temporary file for pipe copy");
406133359Sobrien		return -1;
407103373Sobrien	}
408103373Sobrien
409133359Sobrien	if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes)
410103373Sobrien		r = 1;
411103373Sobrien	else {
412169962Sobrien		while ((r = sread(fd, buf, sizeof(buf), 1)) > 0)
413133359Sobrien			if (swrite(tfd, buf, (size_t)r) != r)
414103373Sobrien				break;
415103373Sobrien	}
416103373Sobrien
417103373Sobrien	switch (r) {
418103373Sobrien	case -1:
419133359Sobrien		file_error(ms, errno, "error copying from pipe to temp file");
420133359Sobrien		return -1;
421103373Sobrien	case 0:
422103373Sobrien		break;
423103373Sobrien	default:
424133359Sobrien		file_error(ms, errno, "error while writing to temp file");
425133359Sobrien		return -1;
426103373Sobrien	}
427103373Sobrien
428103373Sobrien	/*
429103373Sobrien	 * We duplicate the file descriptor, because fclose on a
430103373Sobrien	 * tmpfile will delete the file, but any open descriptors
431103373Sobrien	 * can still access the phantom inode.
432103373Sobrien	 */
433103373Sobrien	if ((fd = dup2(tfd, fd)) == -1) {
434133359Sobrien		file_error(ms, errno, "could not dup descriptor for temp file");
435133359Sobrien		return -1;
436103373Sobrien	}
437103373Sobrien	(void)close(tfd);
438103373Sobrien	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
439133359Sobrien		file_badseek(ms);
440133359Sobrien		return -1;
441103373Sobrien	}
442103373Sobrien	return fd;
443103373Sobrien}
444226048Sobrien#if HAVE_FORK
445175296Sobrien#ifdef BUILTIN_DECOMPRESS
446103373Sobrien
447103373Sobrien#define FHCRC		(1 << 1)
448103373Sobrien#define FEXTRA		(1 << 2)
449103373Sobrien#define FNAME		(1 << 3)
450103373Sobrien#define FCOMMENT	(1 << 4)
451103373Sobrien
452298192Sdelphij
453298192Sdelphijprivate int
454298192Sdelphijuncompressgzipped(const unsigned char *old, unsigned char **newch,
455298192Sdelphij    size_t bytes_max, size_t *n)
45668349Sobrien{
457103373Sobrien	unsigned char flg = old[3];
458133359Sobrien	size_t data_start = 10;
459103373Sobrien
460133359Sobrien	if (flg & FEXTRA) {
461298192Sdelphij		if (data_start + 1 >= *n)
462298192Sdelphij			goto err;
463103373Sobrien		data_start += 2 + old[data_start] + old[data_start + 1] * 256;
464133359Sobrien	}
465103373Sobrien	if (flg & FNAME) {
466298192Sdelphij		while(data_start < *n && old[data_start])
467103373Sobrien			data_start++;
468103373Sobrien		data_start++;
469103373Sobrien	}
470298192Sdelphij	if (flg & FCOMMENT) {
471298192Sdelphij		while(data_start < *n && old[data_start])
472103373Sobrien			data_start++;
473103373Sobrien		data_start++;
474103373Sobrien	}
475298192Sdelphij	if (flg & FHCRC)
476103373Sobrien		data_start += 2;
477103373Sobrien
478298192Sdelphij	if (data_start >= *n)
479298192Sdelphij		goto err;
480298192Sdelphij
481298192Sdelphij	*n -= data_start;
482298192Sdelphij	old += data_start;
483298192Sdelphij	return uncompresszlib(old, newch, bytes_max, n, 0);
484298192Sdelphijerr:
485298192Sdelphij	return makeerror(newch, n, "File too short");
486298192Sdelphij}
487298192Sdelphij
488298192Sdelphijprivate int
489298192Sdelphijuncompresszlib(const unsigned char *old, unsigned char **newch,
490298192Sdelphij    size_t bytes_max, size_t *n, int zlib)
491298192Sdelphij{
492298192Sdelphij	int rc;
493298192Sdelphij	z_stream z;
494298192Sdelphij
495298192Sdelphij	if ((*newch = CAST(unsigned char *, malloc(bytes_max + 1))) == NULL)
496298192Sdelphij		return makeerror(newch, n, "No buffer, %s", strerror(errno));
497298192Sdelphij
498298192Sdelphij	z.next_in = CCAST(Bytef *, old);
499298192Sdelphij	z.avail_in = CAST(uint32_t, *n);
500103373Sobrien	z.next_out = *newch;
501328874Seadler	z.avail_out = CAST(unsigned int, bytes_max);
502103373Sobrien	z.zalloc = Z_NULL;
503103373Sobrien	z.zfree = Z_NULL;
504103373Sobrien	z.opaque = Z_NULL;
505103373Sobrien
506226048Sobrien	/* LINTED bug in header macro */
507298192Sdelphij	rc = zlib ? inflateInit(&z) : inflateInit2(&z, -15);
508298192Sdelphij	if (rc != Z_OK)
509298192Sdelphij		goto err;
510103373Sobrien
511103373Sobrien	rc = inflate(&z, Z_SYNC_FLUSH);
512298192Sdelphij	if (rc != Z_OK && rc != Z_STREAM_END)
513298192Sdelphij		goto err;
514103373Sobrien
515298192Sdelphij	*n = (size_t)z.total_out;
516298192Sdelphij	rc = inflateEnd(&z);
517298192Sdelphij	if (rc != Z_OK)
518298192Sdelphij		goto err;
519103373Sobrien
520103373Sobrien	/* let's keep the nul-terminate tradition */
521298192Sdelphij	(*newch)[*n] = '\0';
522103373Sobrien
523298192Sdelphij	return OKDATA;
524298192Sdelphijerr:
525309847Sdelphij	strlcpy((char *)*newch, z.msg ? z.msg : zError(rc), bytes_max);
526298192Sdelphij	*n = strlen((char *)*newch);
527298192Sdelphij	return ERRDATA;
528298192Sdelphij}
529298192Sdelphij#endif
530298192Sdelphij
531298192Sdelphijstatic int
532298192Sdelphijmakeerror(unsigned char **buf, size_t *len, const char *fmt, ...)
533298192Sdelphij{
534298192Sdelphij	char *msg;
535298192Sdelphij	va_list ap;
536298192Sdelphij	int rv;
537298192Sdelphij
538298192Sdelphij	va_start(ap, fmt);
539298192Sdelphij	rv = vasprintf(&msg, fmt, ap);
540298192Sdelphij	va_end(ap);
541298192Sdelphij	if (rv < 0) {
542298192Sdelphij		*buf = NULL;
543298192Sdelphij		*len = 0;
544298192Sdelphij		return NODATA;
545298192Sdelphij	}
546298192Sdelphij	*buf = (unsigned char *)msg;
547298192Sdelphij	*len = strlen(msg);
548298192Sdelphij	return ERRDATA;
549298192Sdelphij}
550298192Sdelphij
551298192Sdelphijstatic void
552298192Sdelphijclosefd(int *fd, size_t i)
553298192Sdelphij{
554298192Sdelphij	if (fd[i] == -1)
555298192Sdelphij		return;
556298192Sdelphij	(void) close(fd[i]);
557298192Sdelphij	fd[i] = -1;
558298192Sdelphij}
559298192Sdelphij
560298192Sdelphijstatic void
561298192Sdelphijclosep(int *fd)
562298192Sdelphij{
563298192Sdelphij	size_t i;
564298192Sdelphij	for (i = 0; i < 2; i++)
565298192Sdelphij		closefd(fd, i);
566298192Sdelphij}
567298192Sdelphij
568298192Sdelphijstatic void
569298192Sdelphijcopydesc(int i, int *fd)
570298192Sdelphij{
571298192Sdelphij	int j = fd[i == STDIN_FILENO ? 0 : 1];
572298192Sdelphij	if (j == i)
573298192Sdelphij		return;
574298192Sdelphij	if (dup2(j, i) == -1) {
575298192Sdelphij		DPRINTF("dup(%d, %d) failed (%s)\n", j, i, strerror(errno));
576298192Sdelphij		exit(1);
577298192Sdelphij	}
578298192Sdelphij	closep(fd);
579298192Sdelphij}
580298192Sdelphij
581298192Sdelphijstatic void
582298192Sdelphijwritechild(int fdp[3][2], const void *old, size_t n)
583298192Sdelphij{
584298192Sdelphij	int status;
585298192Sdelphij
586298192Sdelphij	closefd(fdp[STDIN_FILENO], 0);
587298192Sdelphij	/*
588298192Sdelphij	 * fork again, to avoid blocking because both
589298192Sdelphij	 * pipes filled
590298192Sdelphij	 */
591298192Sdelphij	switch (fork()) {
592298192Sdelphij	case 0: /* child */
593298192Sdelphij		closefd(fdp[STDOUT_FILENO], 0);
594298192Sdelphij		if (swrite(fdp[STDIN_FILENO][1], old, n) != (ssize_t)n) {
595298192Sdelphij			DPRINTF("Write failed (%s)\n", strerror(errno));
596298192Sdelphij			exit(1);
597298192Sdelphij		}
598298192Sdelphij		exit(0);
599298192Sdelphij		/*NOTREACHED*/
600298192Sdelphij
601298192Sdelphij	case -1:
602298192Sdelphij		DPRINTF("Fork failed (%s)\n", strerror(errno));
603298192Sdelphij		exit(1);
604298192Sdelphij		/*NOTREACHED*/
605298192Sdelphij
606298192Sdelphij	default:  /* parent */
607298192Sdelphij		if (wait(&status) == -1) {
608298192Sdelphij			DPRINTF("Wait failed (%s)\n", strerror(errno));
609298192Sdelphij			exit(1);
610298192Sdelphij		}
611298192Sdelphij		DPRINTF("Grandchild wait return %#x\n", status);
612298192Sdelphij	}
613298192Sdelphij	closefd(fdp[STDIN_FILENO], 1);
614298192Sdelphij}
615298192Sdelphij
616298192Sdelphijstatic ssize_t
617298192Sdelphijfilter_error(unsigned char *ubuf, ssize_t n)
618298192Sdelphij{
619298192Sdelphij	char *p;
620298192Sdelphij	char *buf;
621298192Sdelphij
622298192Sdelphij	ubuf[n] = '\0';
623298192Sdelphij	buf = (char *)ubuf;
624298192Sdelphij	while (isspace((unsigned char)*buf))
625298192Sdelphij		buf++;
626298192Sdelphij	DPRINTF("Filter error[[[%s]]]\n", buf);
627298192Sdelphij	if ((p = strchr((char *)buf, '\n')) != NULL)
628298192Sdelphij		*p = '\0';
629298192Sdelphij	if ((p = strchr((char *)buf, ';')) != NULL)
630298192Sdelphij		*p = '\0';
631298192Sdelphij	if ((p = strrchr((char *)buf, ':')) != NULL) {
632298192Sdelphij		++p;
633298192Sdelphij		while (isspace((unsigned char)*p))
634298192Sdelphij			p++;
635298192Sdelphij		n = strlen(p);
636328874Seadler		memmove(ubuf, p, CAST(size_t, n + 1));
637298192Sdelphij	}
638298192Sdelphij	DPRINTF("Filter error after[[[%s]]]\n", (char *)ubuf);
639298192Sdelphij	if (islower(*ubuf))
640298192Sdelphij		*ubuf = toupper(*ubuf);
641103373Sobrien	return n;
642103373Sobrien}
643298192Sdelphij
644298192Sdelphijprivate const char *
645298192Sdelphijmethodname(size_t method)
646298192Sdelphij{
647298192Sdelphij#ifdef BUILTIN_DECOMPRESS
648298192Sdelphij        /* FIXME: This doesn't cope with bzip2 */
649298192Sdelphij	if (method == 2 || compr[method].maglen == 0)
650298192Sdelphij	    return "zlib";
651103373Sobrien#endif
652298192Sdelphij	return compr[method].argv[0];
653298192Sdelphij}
654103373Sobrien
655298192Sdelphijprivate int
656298192Sdelphijuncompressbuf(int fd, size_t bytes_max, size_t method, const unsigned char *old,
657298192Sdelphij    unsigned char **newch, size_t* n)
658103373Sobrien{
659298192Sdelphij	int fdp[3][2];
660298192Sdelphij	int status, rv;
661298192Sdelphij	size_t i;
662226048Sobrien	ssize_t r;
66368349Sobrien
664175296Sobrien#ifdef BUILTIN_DECOMPRESS
665186690Sobrien        /* FIXME: This doesn't cope with bzip2 */
666103373Sobrien	if (method == 2)
667298192Sdelphij		return uncompressgzipped(old, newch, bytes_max, n);
668298192Sdelphij	if (compr[method].maglen == 0)
669298192Sdelphij		return uncompresszlib(old, newch, bytes_max, n, 1);
670103373Sobrien#endif
671159764Sobrien	(void)fflush(stdout);
672159764Sobrien	(void)fflush(stderr);
673103373Sobrien
674298192Sdelphij	for (i = 0; i < __arraycount(fdp); i++)
675298192Sdelphij		fdp[i][0] = fdp[i][1] = -1;
676298192Sdelphij
677298192Sdelphij	if ((fd == -1 && pipe(fdp[STDIN_FILENO]) == -1) ||
678298192Sdelphij	    pipe(fdp[STDOUT_FILENO]) == -1 || pipe(fdp[STDERR_FILENO]) == -1) {
679298192Sdelphij		closep(fdp[STDIN_FILENO]);
680298192Sdelphij		closep(fdp[STDOUT_FILENO]);
681298192Sdelphij		return makeerror(newch, n, "Cannot create pipe, %s",
682298192Sdelphij		    strerror(errno));
68368349Sobrien	}
684284237Sdelphij	switch (fork()) {
68568349Sobrien	case 0:	/* child */
686159764Sobrien		if (fd != -1) {
687298192Sdelphij			fdp[STDIN_FILENO][0] = fd;
688298192Sdelphij			(void) lseek(fd, (off_t)0, SEEK_SET);
689159764Sobrien		}
690298192Sdelphij
691298192Sdelphij		for (i = 0; i < __arraycount(fdp); i++)
692328874Seadler			copydesc(CAST(int, i), fdp[i]);
69368349Sobrien
694169962Sobrien		(void)execvp(compr[method].argv[0],
695169962Sobrien		    (char *const *)(intptr_t)compr[method].argv);
696298192Sdelphij		dprintf(STDERR_FILENO, "exec `%s' failed, %s",
697159764Sobrien		    compr[method].argv[0], strerror(errno));
69868349Sobrien		exit(1);
69968349Sobrien		/*NOTREACHED*/
70068349Sobrien	case -1:
701298192Sdelphij		return makeerror(newch, n, "Cannot fork, %s",
702298192Sdelphij		    strerror(errno));
70368349Sobrien
70468349Sobrien	default: /* parent */
705298192Sdelphij		for (i = 1; i < __arraycount(fdp); i++)
706298192Sdelphij			closefd(fdp[i], 1);
707159764Sobrien
708298192Sdelphij		/* Write the buffer data to the child, if we don't have fd */
709298192Sdelphij		if (fd == -1)
710298192Sdelphij			writechild(fdp, old, *n);
711133359Sobrien
712298192Sdelphij		*newch = CAST(unsigned char *, malloc(bytes_max + 1));
713298192Sdelphij		if (*newch == NULL) {
714298192Sdelphij			rv = makeerror(newch, n, "No buffer, %s",
715159764Sobrien			    strerror(errno));
71675937Sobrien			goto err;
71775937Sobrien		}
718298192Sdelphij		rv = OKDATA;
719298192Sdelphij		if ((r = sread(fdp[STDOUT_FILENO][0], *newch, bytes_max, 0)) > 0)
720298192Sdelphij			break;
721298192Sdelphij		DPRINTF("Read stdout failed %d (%s)\n", fdp[STDOUT_FILENO][0],
722298192Sdelphij		    r != -1 ? strerror(errno) : "no data");
723298192Sdelphij
724298192Sdelphij		rv = ERRDATA;
725298192Sdelphij		if (r == 0 &&
726298192Sdelphij		    (r = sread(fdp[STDERR_FILENO][0], *newch, bytes_max, 0)) > 0)
727298192Sdelphij		{
728298192Sdelphij			r = filter_error(*newch, r);
729298192Sdelphij			break;
73068349Sobrien		}
731298192Sdelphij		free(*newch);
732298192Sdelphij		if  (r == 0)
733298192Sdelphij			rv = makeerror(newch, n, "Read failed, %s",
734275698Sdelphij			    strerror(errno));
735298192Sdelphij		else
736298192Sdelphij			rv = makeerror(newch, n, "No data");
737298192Sdelphij		goto err;
738298192Sdelphij	}
739275698Sdelphij
740298192Sdelphij	*n = r;
741298192Sdelphij	/* NUL terminate, as every buffer is handled here. */
742298192Sdelphij	(*newch)[*n] = '\0';
743298192Sdelphijerr:
744298192Sdelphij	closefd(fdp[STDIN_FILENO], 1);
745298192Sdelphij	closefd(fdp[STDOUT_FILENO], 0);
746298192Sdelphij	closefd(fdp[STDERR_FILENO], 0);
747298192Sdelphij	if (wait(&status) == -1) {
748298192Sdelphij		free(*newch);
749298192Sdelphij		rv = makeerror(newch, n, "Wait failed, %s", strerror(errno));
750298192Sdelphij		DPRINTF("Child wait return %#x\n", status);
751298192Sdelphij	} else if (!WIFEXITED(status)) {
752328874Seadler		DPRINTF("Child not exited (%#x)\n", status);
753298192Sdelphij	} else if (WEXITSTATUS(status) != 0) {
754328874Seadler		DPRINTF("Child exited (%#x)\n", WEXITSTATUS(status));
75568349Sobrien	}
756298192Sdelphij
757298192Sdelphij	closefd(fdp[STDIN_FILENO], 0);
758298192Sdelphij	DPRINTF("Returning %p n=%zu rv=%d\n", *newch, *n, rv);
759298192Sdelphij
760298192Sdelphij	return rv;
76168349Sobrien}
762226048Sobrien#endif
763