compress.c revision 337827
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
38337827SeadlerFILE_RCSID("@(#)$File: compress.c,v 1.107 2018/04/28 18:48:22 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
186337827Seadlerprivate int
187337827Seadlerformat_decompression_error(struct magic_set *ms, size_t i, unsigned char *buf)
188337827Seadler{
189337827Seadler	unsigned char *p;
190337827Seadler	int mime = ms->flags & MAGIC_MIME;
191337827Seadler
192337827Seadler	if (!mime)
193337827Seadler		return file_printf(ms, "ERROR:[%s: %s]", methodname(i), buf);
194337827Seadler
195337827Seadler	for (p = buf; *p; p++)
196337827Seadler		if (!isalnum(*p))
197337827Seadler			*p = '-';
198337827Seadler
199337827Seadler	return file_printf(ms, "application/x-decompression-error-%s-%s",
200337827Seadler	    methodname(i), buf);
201337827Seadler}
202337827Seadler
203133359Sobrienprotected int
204337827Seadlerfile_zmagic(struct magic_set *ms, const struct buffer *b, const char *name)
20568349Sobrien{
206133359Sobrien	unsigned char *newbuf = NULL;
207133359Sobrien	size_t i, nsz;
208298192Sdelphij	char *rbuf;
209298192Sdelphij	file_pushbuf_t *pb;
210299736Sdelphij	int urv, prv, rv = 0;
211175296Sobrien	int mime = ms->flags & MAGIC_MIME;
212337827Seadler	int fd = b->fd;
213337827Seadler	const unsigned char *buf = b->fbuf;
214337827Seadler	size_t nbytes = b->flen;
215284237Sdelphij#ifdef HAVE_SIGNAL_H
216276577Sdelphij	sig_t osigpipe;
217284237Sdelphij#endif
21868349Sobrien
219133359Sobrien	if ((ms->flags & MAGIC_COMPRESS) == 0)
220133359Sobrien		return 0;
221133359Sobrien
222284237Sdelphij#ifdef HAVE_SIGNAL_H
223276577Sdelphij	osigpipe = signal(SIGPIPE, SIG_IGN);
224284237Sdelphij#endif
22568349Sobrien	for (i = 0; i < ncompr; i++) {
226298192Sdelphij		int zm;
22768349Sobrien		if (nbytes < compr[i].maglen)
22868349Sobrien			continue;
229298192Sdelphij#ifdef ZLIBSUPPORT
230298192Sdelphij		if (compr[i].maglen == 0)
231309847Sdelphij			zm = (RCAST(int (*)(const unsigned char *),
232298192Sdelphij			    CCAST(void *, compr[i].magic)))(buf);
233298192Sdelphij		else
234298192Sdelphij#endif
235298192Sdelphij			zm = memcmp(buf, compr[i].magic, compr[i].maglen) == 0;
236298192Sdelphij
237298192Sdelphij		if (!zm)
238298192Sdelphij			continue;
239298192Sdelphij		nsz = nbytes;
240299736Sdelphij		urv = uncompressbuf(fd, ms->bytes_max, i, buf, &newbuf, &nsz);
241299736Sdelphij		DPRINTF("uncompressbuf = %d, %s, %zu\n", urv, (char *)newbuf,
242298192Sdelphij		    nsz);
243299736Sdelphij		switch (urv) {
244298192Sdelphij		case OKDATA:
245298192Sdelphij		case ERRDATA:
246133359Sobrien			ms->flags &= ~MAGIC_COMPRESS;
247299736Sdelphij			if (urv == ERRDATA)
248337827Seadler				prv = format_decompression_error(ms, i, newbuf);
249298192Sdelphij			else
250299736Sdelphij				prv = file_buffer(ms, -1, name, newbuf, nsz);
251299736Sdelphij			if (prv == -1)
252133359Sobrien				goto error;
253299736Sdelphij			rv = 1;
254298192Sdelphij			if ((ms->flags & MAGIC_COMPRESS_TRANSP) != 0)
255298192Sdelphij				goto out;
256298192Sdelphij			if (mime != MAGIC_MIME && mime != 0)
257298192Sdelphij				goto out;
258298192Sdelphij			if ((file_printf(ms,
259298192Sdelphij			    mime ? " compressed-encoding=" : " (")) == -1)
260298192Sdelphij				goto error;
261298192Sdelphij			if ((pb = file_push_buffer(ms)) == NULL)
262298192Sdelphij				goto error;
263299736Sdelphij			/*
264299736Sdelphij			 * XXX: If file_buffer fails here, we overwrite
265299736Sdelphij			 * the compressed text. FIXME.
266299736Sdelphij			 */
267298192Sdelphij			if (file_buffer(ms, -1, NULL, buf, nbytes) == -1)
268298192Sdelphij				goto error;
269298192Sdelphij			if ((rbuf = file_pop_buffer(ms, pb)) != NULL) {
270298192Sdelphij				if (file_printf(ms, "%s", rbuf) == -1) {
271298192Sdelphij					free(rbuf);
272175296Sobrien					goto error;
273298192Sdelphij				}
274298192Sdelphij				free(rbuf);
275175296Sobrien			}
276298192Sdelphij			if (!mime && file_printf(ms, ")") == -1)
277298192Sdelphij				goto error;
278299736Sdelphij			/*FALLTHROUGH*/
279298192Sdelphij		case NODATA:
280299736Sdelphij			break;
281298192Sdelphij		default:
282298192Sdelphij			abort();
283299736Sdelphij			/*NOTREACHED*/
284299736Sdelphij		error:
285299736Sdelphij			rv = -1;
286299736Sdelphij			break;
28768349Sobrien		}
28868349Sobrien	}
289298192Sdelphijout:
290299736Sdelphij	DPRINTF("rv = %d\n", rv);
291299736Sdelphij
292284237Sdelphij#ifdef HAVE_SIGNAL_H
293276577Sdelphij	(void)signal(SIGPIPE, osigpipe);
294284237Sdelphij#endif
295234250Sobrien	free(newbuf);
296133359Sobrien	ms->flags |= MAGIC_COMPRESS;
297298192Sdelphij	DPRINTF("Zmagic returns %d\n", rv);
298133359Sobrien	return rv;
29968349Sobrien}
300226048Sobrien#endif
30175937Sobrien/*
30275937Sobrien * `safe' write for sockets and pipes.
30375937Sobrien */
304133359Sobrienprivate ssize_t
305103373Sobrienswrite(int fd, const void *buf, size_t n)
30675937Sobrien{
307226048Sobrien	ssize_t rv;
30875937Sobrien	size_t rn = n;
30968349Sobrien
31075937Sobrien	do
31175937Sobrien		switch (rv = write(fd, buf, n)) {
31275937Sobrien		case -1:
31375937Sobrien			if (errno == EINTR)
31475937Sobrien				continue;
31575937Sobrien			return -1;
31675937Sobrien		default:
31775937Sobrien			n -= rv;
318226048Sobrien			buf = CAST(const char *, buf) + rv;
31975937Sobrien			break;
32075937Sobrien		}
32175937Sobrien	while (n > 0);
32275937Sobrien	return rn;
32375937Sobrien}
32475937Sobrien
32575937Sobrien
32675937Sobrien/*
32775937Sobrien * `safe' read for sockets and pipes.
32875937Sobrien */
329169942Sobrienprotected ssize_t
330267843Sdelphijsread(int fd, void *buf, size_t n, int canbepipe __attribute__((__unused__)))
33175937Sobrien{
332226048Sobrien	ssize_t rv;
333169942Sobrien#ifdef FIONREAD
334169942Sobrien	int t = 0;
335169942Sobrien#endif
33675937Sobrien	size_t rn = n;
33775937Sobrien
338169942Sobrien	if (fd == STDIN_FILENO)
339169942Sobrien		goto nocheck;
340169942Sobrien
341169942Sobrien#ifdef FIONREAD
342267843Sdelphij	if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) {
343169942Sobrien#ifdef FD_ZERO
344267843Sdelphij		ssize_t cnt;
345169962Sobrien		for (cnt = 0;; cnt++) {
346169942Sobrien			fd_set check;
347169942Sobrien			struct timeval tout = {0, 100 * 1000};
348169962Sobrien			int selrv;
349169942Sobrien
350169942Sobrien			FD_ZERO(&check);
351169942Sobrien			FD_SET(fd, &check);
352169942Sobrien
353169942Sobrien			/*
354169942Sobrien			 * Avoid soft deadlock: do not read if there
355169942Sobrien			 * is nothing to read from sockets and pipes.
356169942Sobrien			 */
357169962Sobrien			selrv = select(fd + 1, &check, NULL, NULL, &tout);
358169962Sobrien			if (selrv == -1) {
359169942Sobrien				if (errno == EINTR || errno == EAGAIN)
360169942Sobrien					continue;
361169962Sobrien			} else if (selrv == 0 && cnt >= 5) {
362169942Sobrien				return 0;
363169962Sobrien			} else
364169962Sobrien				break;
365169942Sobrien		}
366169942Sobrien#endif
367169942Sobrien		(void)ioctl(fd, FIONREAD, &t);
368169942Sobrien	}
369169942Sobrien
370169942Sobrien	if (t > 0 && (size_t)t < n) {
371169942Sobrien		n = t;
372169942Sobrien		rn = n;
373169942Sobrien	}
374169942Sobrien#endif
375169942Sobrien
376169942Sobriennocheck:
37775937Sobrien	do
378169942Sobrien		switch ((rv = read(fd, buf, n))) {
37975937Sobrien		case -1:
38075937Sobrien			if (errno == EINTR)
38175937Sobrien				continue;
38275937Sobrien			return -1;
383103373Sobrien		case 0:
384103373Sobrien			return rn - n;
38575937Sobrien		default:
38675937Sobrien			n -= rv;
387309847Sdelphij			buf = CAST(char *, CCAST(void *, buf)) + rv;
38875937Sobrien			break;
38975937Sobrien		}
39075937Sobrien	while (n > 0);
39175937Sobrien	return rn;
39275937Sobrien}
39375937Sobrien
394133359Sobrienprotected int
395133359Sobrienfile_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
396133359Sobrien    size_t nbytes)
397103373Sobrien{
398103373Sobrien	char buf[4096];
399226048Sobrien	ssize_t r;
400226048Sobrien	int tfd;
401103373Sobrien
402191736Sobrien	(void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof buf);
403103373Sobrien#ifndef HAVE_MKSTEMP
404103373Sobrien	{
405103373Sobrien		char *ptr = mktemp(buf);
406103373Sobrien		tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
407103373Sobrien		r = errno;
408103373Sobrien		(void)unlink(ptr);
409103373Sobrien		errno = r;
410103373Sobrien	}
411103373Sobrien#else
412267843Sdelphij	{
413267843Sdelphij		int te;
414267843Sdelphij		tfd = mkstemp(buf);
415267843Sdelphij		te = errno;
416267843Sdelphij		(void)unlink(buf);
417267843Sdelphij		errno = te;
418267843Sdelphij	}
419103373Sobrien#endif
420103373Sobrien	if (tfd == -1) {
421133359Sobrien		file_error(ms, errno,
422133359Sobrien		    "cannot create temporary file for pipe copy");
423133359Sobrien		return -1;
424103373Sobrien	}
425103373Sobrien
426133359Sobrien	if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes)
427103373Sobrien		r = 1;
428103373Sobrien	else {
429169962Sobrien		while ((r = sread(fd, buf, sizeof(buf), 1)) > 0)
430133359Sobrien			if (swrite(tfd, buf, (size_t)r) != r)
431103373Sobrien				break;
432103373Sobrien	}
433103373Sobrien
434103373Sobrien	switch (r) {
435103373Sobrien	case -1:
436133359Sobrien		file_error(ms, errno, "error copying from pipe to temp file");
437133359Sobrien		return -1;
438103373Sobrien	case 0:
439103373Sobrien		break;
440103373Sobrien	default:
441133359Sobrien		file_error(ms, errno, "error while writing to temp file");
442133359Sobrien		return -1;
443103373Sobrien	}
444103373Sobrien
445103373Sobrien	/*
446103373Sobrien	 * We duplicate the file descriptor, because fclose on a
447103373Sobrien	 * tmpfile will delete the file, but any open descriptors
448103373Sobrien	 * can still access the phantom inode.
449103373Sobrien	 */
450103373Sobrien	if ((fd = dup2(tfd, fd)) == -1) {
451133359Sobrien		file_error(ms, errno, "could not dup descriptor for temp file");
452133359Sobrien		return -1;
453103373Sobrien	}
454103373Sobrien	(void)close(tfd);
455103373Sobrien	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
456133359Sobrien		file_badseek(ms);
457133359Sobrien		return -1;
458103373Sobrien	}
459103373Sobrien	return fd;
460103373Sobrien}
461226048Sobrien#if HAVE_FORK
462175296Sobrien#ifdef BUILTIN_DECOMPRESS
463103373Sobrien
464103373Sobrien#define FHCRC		(1 << 1)
465103373Sobrien#define FEXTRA		(1 << 2)
466103373Sobrien#define FNAME		(1 << 3)
467103373Sobrien#define FCOMMENT	(1 << 4)
468103373Sobrien
469298192Sdelphij
470298192Sdelphijprivate int
471298192Sdelphijuncompressgzipped(const unsigned char *old, unsigned char **newch,
472298192Sdelphij    size_t bytes_max, size_t *n)
47368349Sobrien{
474103373Sobrien	unsigned char flg = old[3];
475133359Sobrien	size_t data_start = 10;
476103373Sobrien
477133359Sobrien	if (flg & FEXTRA) {
478298192Sdelphij		if (data_start + 1 >= *n)
479298192Sdelphij			goto err;
480103373Sobrien		data_start += 2 + old[data_start] + old[data_start + 1] * 256;
481133359Sobrien	}
482103373Sobrien	if (flg & FNAME) {
483298192Sdelphij		while(data_start < *n && old[data_start])
484103373Sobrien			data_start++;
485103373Sobrien		data_start++;
486103373Sobrien	}
487298192Sdelphij	if (flg & FCOMMENT) {
488298192Sdelphij		while(data_start < *n && old[data_start])
489103373Sobrien			data_start++;
490103373Sobrien		data_start++;
491103373Sobrien	}
492298192Sdelphij	if (flg & FHCRC)
493103373Sobrien		data_start += 2;
494103373Sobrien
495298192Sdelphij	if (data_start >= *n)
496298192Sdelphij		goto err;
497298192Sdelphij
498298192Sdelphij	*n -= data_start;
499298192Sdelphij	old += data_start;
500298192Sdelphij	return uncompresszlib(old, newch, bytes_max, n, 0);
501298192Sdelphijerr:
502298192Sdelphij	return makeerror(newch, n, "File too short");
503298192Sdelphij}
504298192Sdelphij
505298192Sdelphijprivate int
506298192Sdelphijuncompresszlib(const unsigned char *old, unsigned char **newch,
507298192Sdelphij    size_t bytes_max, size_t *n, int zlib)
508298192Sdelphij{
509298192Sdelphij	int rc;
510298192Sdelphij	z_stream z;
511298192Sdelphij
512298192Sdelphij	if ((*newch = CAST(unsigned char *, malloc(bytes_max + 1))) == NULL)
513298192Sdelphij		return makeerror(newch, n, "No buffer, %s", strerror(errno));
514298192Sdelphij
515298192Sdelphij	z.next_in = CCAST(Bytef *, old);
516298192Sdelphij	z.avail_in = CAST(uint32_t, *n);
517103373Sobrien	z.next_out = *newch;
518328874Seadler	z.avail_out = CAST(unsigned int, bytes_max);
519103373Sobrien	z.zalloc = Z_NULL;
520103373Sobrien	z.zfree = Z_NULL;
521103373Sobrien	z.opaque = Z_NULL;
522103373Sobrien
523226048Sobrien	/* LINTED bug in header macro */
524298192Sdelphij	rc = zlib ? inflateInit(&z) : inflateInit2(&z, -15);
525298192Sdelphij	if (rc != Z_OK)
526298192Sdelphij		goto err;
527103373Sobrien
528103373Sobrien	rc = inflate(&z, Z_SYNC_FLUSH);
529298192Sdelphij	if (rc != Z_OK && rc != Z_STREAM_END)
530298192Sdelphij		goto err;
531103373Sobrien
532298192Sdelphij	*n = (size_t)z.total_out;
533298192Sdelphij	rc = inflateEnd(&z);
534298192Sdelphij	if (rc != Z_OK)
535298192Sdelphij		goto err;
536103373Sobrien
537103373Sobrien	/* let's keep the nul-terminate tradition */
538298192Sdelphij	(*newch)[*n] = '\0';
539103373Sobrien
540298192Sdelphij	return OKDATA;
541298192Sdelphijerr:
542309847Sdelphij	strlcpy((char *)*newch, z.msg ? z.msg : zError(rc), bytes_max);
543298192Sdelphij	*n = strlen((char *)*newch);
544298192Sdelphij	return ERRDATA;
545298192Sdelphij}
546298192Sdelphij#endif
547298192Sdelphij
548298192Sdelphijstatic int
549298192Sdelphijmakeerror(unsigned char **buf, size_t *len, const char *fmt, ...)
550298192Sdelphij{
551298192Sdelphij	char *msg;
552298192Sdelphij	va_list ap;
553298192Sdelphij	int rv;
554298192Sdelphij
555298192Sdelphij	va_start(ap, fmt);
556298192Sdelphij	rv = vasprintf(&msg, fmt, ap);
557298192Sdelphij	va_end(ap);
558298192Sdelphij	if (rv < 0) {
559298192Sdelphij		*buf = NULL;
560298192Sdelphij		*len = 0;
561298192Sdelphij		return NODATA;
562298192Sdelphij	}
563298192Sdelphij	*buf = (unsigned char *)msg;
564298192Sdelphij	*len = strlen(msg);
565298192Sdelphij	return ERRDATA;
566298192Sdelphij}
567298192Sdelphij
568298192Sdelphijstatic void
569298192Sdelphijclosefd(int *fd, size_t i)
570298192Sdelphij{
571298192Sdelphij	if (fd[i] == -1)
572298192Sdelphij		return;
573298192Sdelphij	(void) close(fd[i]);
574298192Sdelphij	fd[i] = -1;
575298192Sdelphij}
576298192Sdelphij
577298192Sdelphijstatic void
578298192Sdelphijclosep(int *fd)
579298192Sdelphij{
580298192Sdelphij	size_t i;
581298192Sdelphij	for (i = 0; i < 2; i++)
582298192Sdelphij		closefd(fd, i);
583298192Sdelphij}
584298192Sdelphij
585298192Sdelphijstatic void
586298192Sdelphijcopydesc(int i, int *fd)
587298192Sdelphij{
588298192Sdelphij	int j = fd[i == STDIN_FILENO ? 0 : 1];
589298192Sdelphij	if (j == i)
590298192Sdelphij		return;
591298192Sdelphij	if (dup2(j, i) == -1) {
592298192Sdelphij		DPRINTF("dup(%d, %d) failed (%s)\n", j, i, strerror(errno));
593298192Sdelphij		exit(1);
594298192Sdelphij	}
595298192Sdelphij	closep(fd);
596298192Sdelphij}
597298192Sdelphij
598298192Sdelphijstatic void
599298192Sdelphijwritechild(int fdp[3][2], const void *old, size_t n)
600298192Sdelphij{
601298192Sdelphij	int status;
602298192Sdelphij
603298192Sdelphij	closefd(fdp[STDIN_FILENO], 0);
604298192Sdelphij	/*
605298192Sdelphij	 * fork again, to avoid blocking because both
606298192Sdelphij	 * pipes filled
607298192Sdelphij	 */
608298192Sdelphij	switch (fork()) {
609298192Sdelphij	case 0: /* child */
610298192Sdelphij		closefd(fdp[STDOUT_FILENO], 0);
611298192Sdelphij		if (swrite(fdp[STDIN_FILENO][1], old, n) != (ssize_t)n) {
612298192Sdelphij			DPRINTF("Write failed (%s)\n", strerror(errno));
613298192Sdelphij			exit(1);
614298192Sdelphij		}
615298192Sdelphij		exit(0);
616298192Sdelphij		/*NOTREACHED*/
617298192Sdelphij
618298192Sdelphij	case -1:
619298192Sdelphij		DPRINTF("Fork failed (%s)\n", strerror(errno));
620298192Sdelphij		exit(1);
621298192Sdelphij		/*NOTREACHED*/
622298192Sdelphij
623298192Sdelphij	default:  /* parent */
624298192Sdelphij		if (wait(&status) == -1) {
625298192Sdelphij			DPRINTF("Wait failed (%s)\n", strerror(errno));
626298192Sdelphij			exit(1);
627298192Sdelphij		}
628298192Sdelphij		DPRINTF("Grandchild wait return %#x\n", status);
629298192Sdelphij	}
630298192Sdelphij	closefd(fdp[STDIN_FILENO], 1);
631298192Sdelphij}
632298192Sdelphij
633298192Sdelphijstatic ssize_t
634298192Sdelphijfilter_error(unsigned char *ubuf, ssize_t n)
635298192Sdelphij{
636298192Sdelphij	char *p;
637298192Sdelphij	char *buf;
638298192Sdelphij
639298192Sdelphij	ubuf[n] = '\0';
640298192Sdelphij	buf = (char *)ubuf;
641298192Sdelphij	while (isspace((unsigned char)*buf))
642298192Sdelphij		buf++;
643298192Sdelphij	DPRINTF("Filter error[[[%s]]]\n", buf);
644298192Sdelphij	if ((p = strchr((char *)buf, '\n')) != NULL)
645298192Sdelphij		*p = '\0';
646298192Sdelphij	if ((p = strchr((char *)buf, ';')) != NULL)
647298192Sdelphij		*p = '\0';
648298192Sdelphij	if ((p = strrchr((char *)buf, ':')) != NULL) {
649298192Sdelphij		++p;
650298192Sdelphij		while (isspace((unsigned char)*p))
651298192Sdelphij			p++;
652298192Sdelphij		n = strlen(p);
653328874Seadler		memmove(ubuf, p, CAST(size_t, n + 1));
654298192Sdelphij	}
655298192Sdelphij	DPRINTF("Filter error after[[[%s]]]\n", (char *)ubuf);
656298192Sdelphij	if (islower(*ubuf))
657298192Sdelphij		*ubuf = toupper(*ubuf);
658103373Sobrien	return n;
659103373Sobrien}
660298192Sdelphij
661298192Sdelphijprivate const char *
662298192Sdelphijmethodname(size_t method)
663298192Sdelphij{
664298192Sdelphij#ifdef BUILTIN_DECOMPRESS
665298192Sdelphij        /* FIXME: This doesn't cope with bzip2 */
666298192Sdelphij	if (method == 2 || compr[method].maglen == 0)
667298192Sdelphij	    return "zlib";
668103373Sobrien#endif
669298192Sdelphij	return compr[method].argv[0];
670298192Sdelphij}
671103373Sobrien
672298192Sdelphijprivate int
673298192Sdelphijuncompressbuf(int fd, size_t bytes_max, size_t method, const unsigned char *old,
674298192Sdelphij    unsigned char **newch, size_t* n)
675103373Sobrien{
676298192Sdelphij	int fdp[3][2];
677298192Sdelphij	int status, rv;
678298192Sdelphij	size_t i;
679226048Sobrien	ssize_t r;
68068349Sobrien
681175296Sobrien#ifdef BUILTIN_DECOMPRESS
682186690Sobrien        /* FIXME: This doesn't cope with bzip2 */
683103373Sobrien	if (method == 2)
684298192Sdelphij		return uncompressgzipped(old, newch, bytes_max, n);
685298192Sdelphij	if (compr[method].maglen == 0)
686298192Sdelphij		return uncompresszlib(old, newch, bytes_max, n, 1);
687103373Sobrien#endif
688159764Sobrien	(void)fflush(stdout);
689159764Sobrien	(void)fflush(stderr);
690103373Sobrien
691298192Sdelphij	for (i = 0; i < __arraycount(fdp); i++)
692298192Sdelphij		fdp[i][0] = fdp[i][1] = -1;
693298192Sdelphij
694298192Sdelphij	if ((fd == -1 && pipe(fdp[STDIN_FILENO]) == -1) ||
695298192Sdelphij	    pipe(fdp[STDOUT_FILENO]) == -1 || pipe(fdp[STDERR_FILENO]) == -1) {
696298192Sdelphij		closep(fdp[STDIN_FILENO]);
697298192Sdelphij		closep(fdp[STDOUT_FILENO]);
698298192Sdelphij		return makeerror(newch, n, "Cannot create pipe, %s",
699298192Sdelphij		    strerror(errno));
70068349Sobrien	}
701284237Sdelphij	switch (fork()) {
70268349Sobrien	case 0:	/* child */
703159764Sobrien		if (fd != -1) {
704298192Sdelphij			fdp[STDIN_FILENO][0] = fd;
705298192Sdelphij			(void) lseek(fd, (off_t)0, SEEK_SET);
706159764Sobrien		}
707298192Sdelphij
708298192Sdelphij		for (i = 0; i < __arraycount(fdp); i++)
709328874Seadler			copydesc(CAST(int, i), fdp[i]);
71068349Sobrien
711169962Sobrien		(void)execvp(compr[method].argv[0],
712169962Sobrien		    (char *const *)(intptr_t)compr[method].argv);
713298192Sdelphij		dprintf(STDERR_FILENO, "exec `%s' failed, %s",
714159764Sobrien		    compr[method].argv[0], strerror(errno));
71568349Sobrien		exit(1);
71668349Sobrien		/*NOTREACHED*/
71768349Sobrien	case -1:
718298192Sdelphij		return makeerror(newch, n, "Cannot fork, %s",
719298192Sdelphij		    strerror(errno));
72068349Sobrien
72168349Sobrien	default: /* parent */
722298192Sdelphij		for (i = 1; i < __arraycount(fdp); i++)
723298192Sdelphij			closefd(fdp[i], 1);
724159764Sobrien
725298192Sdelphij		/* Write the buffer data to the child, if we don't have fd */
726298192Sdelphij		if (fd == -1)
727298192Sdelphij			writechild(fdp, old, *n);
728133359Sobrien
729298192Sdelphij		*newch = CAST(unsigned char *, malloc(bytes_max + 1));
730298192Sdelphij		if (*newch == NULL) {
731298192Sdelphij			rv = makeerror(newch, n, "No buffer, %s",
732159764Sobrien			    strerror(errno));
73375937Sobrien			goto err;
73475937Sobrien		}
735298192Sdelphij		rv = OKDATA;
736298192Sdelphij		if ((r = sread(fdp[STDOUT_FILENO][0], *newch, bytes_max, 0)) > 0)
737298192Sdelphij			break;
738298192Sdelphij		DPRINTF("Read stdout failed %d (%s)\n", fdp[STDOUT_FILENO][0],
739298192Sdelphij		    r != -1 ? strerror(errno) : "no data");
740298192Sdelphij
741298192Sdelphij		rv = ERRDATA;
742298192Sdelphij		if (r == 0 &&
743298192Sdelphij		    (r = sread(fdp[STDERR_FILENO][0], *newch, bytes_max, 0)) > 0)
744298192Sdelphij		{
745298192Sdelphij			r = filter_error(*newch, r);
746298192Sdelphij			break;
74768349Sobrien		}
748298192Sdelphij		free(*newch);
749298192Sdelphij		if  (r == 0)
750298192Sdelphij			rv = makeerror(newch, n, "Read failed, %s",
751275698Sdelphij			    strerror(errno));
752298192Sdelphij		else
753298192Sdelphij			rv = makeerror(newch, n, "No data");
754298192Sdelphij		goto err;
755298192Sdelphij	}
756275698Sdelphij
757298192Sdelphij	*n = r;
758298192Sdelphij	/* NUL terminate, as every buffer is handled here. */
759298192Sdelphij	(*newch)[*n] = '\0';
760298192Sdelphijerr:
761298192Sdelphij	closefd(fdp[STDIN_FILENO], 1);
762298192Sdelphij	closefd(fdp[STDOUT_FILENO], 0);
763298192Sdelphij	closefd(fdp[STDERR_FILENO], 0);
764298192Sdelphij	if (wait(&status) == -1) {
765298192Sdelphij		free(*newch);
766298192Sdelphij		rv = makeerror(newch, n, "Wait failed, %s", strerror(errno));
767298192Sdelphij		DPRINTF("Child wait return %#x\n", status);
768298192Sdelphij	} else if (!WIFEXITED(status)) {
769328874Seadler		DPRINTF("Child not exited (%#x)\n", status);
770298192Sdelphij	} else if (WEXITSTATUS(status) != 0) {
771328874Seadler		DPRINTF("Child exited (%#x)\n", WEXITSTATUS(status));
77268349Sobrien	}
773298192Sdelphij
774298192Sdelphij	closefd(fdp[STDIN_FILENO], 0);
775298192Sdelphij	DPRINTF("Returning %p n=%zu rv=%d\n", *newch, *n, rv);
776298192Sdelphij
777298192Sdelphij	return rv;
77868349Sobrien}
779226048Sobrien#endif
780