xref: /netbsd-src/external/bsd/file/dist/src/compress.c (revision 76c7fc5f6b13ed0b1508e6b313e88e59977ed78e)
1 /*	$NetBSD: compress.c,v 1.18 2019/05/22 17:26:05 christos Exp $	*/
2 
3 /*
4  * Copyright (c) Ian F. Darwin 1986-1995.
5  * Software written by Ian F. Darwin and others;
6  * maintained 1995-present by Christos Zoulas and others.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice immediately at the beginning of the file, without modification,
13  *    this list of conditions, and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
22  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 /*
31  * compress routines:
32  *	zmagic() - returns 0 if not recognized, uncompresses and prints
33  *		   information if recognized
34  *	uncompress(method, old, n, newch) - uncompress old into new,
35  *					    using method, return sizeof new
36  */
37 #include "file.h"
38 
39 #ifndef lint
40 #if 0
41 FILE_RCSID("@(#)$File: compress.c,v 1.121 2019/05/07 02:27:11 christos Exp $")
42 #else
43 __RCSID("$NetBSD: compress.c,v 1.18 2019/05/22 17:26:05 christos Exp $");
44 #endif
45 #endif
46 
47 #include "magic.h"
48 #include <stdlib.h>
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 #include <string.h>
53 #include <errno.h>
54 #include <ctype.h>
55 #include <stdarg.h>
56 #include <signal.h>
57 #ifndef HAVE_SIG_T
58 typedef void (*sig_t)(int);
59 #endif /* HAVE_SIG_T */
60 #if !defined(__MINGW32__) && !defined(WIN32)
61 #include <sys/ioctl.h>
62 #endif
63 #ifdef HAVE_SYS_WAIT_H
64 #include <sys/wait.h>
65 #endif
66 #if defined(HAVE_SYS_TIME_H)
67 #include <sys/time.h>
68 #endif
69 
70 #if defined(HAVE_ZLIB_H) && defined(ZLIBSUPPORT)
71 #define BUILTIN_DECOMPRESS
72 #include <zlib.h>
73 #endif
74 
75 #if defined(HAVE_BZLIB_H)
76 #define BUILTIN_BZLIB
77 #include <bzlib.h>
78 #endif
79 
80 #ifdef DEBUG
81 int tty = -1;
82 #define DPRINTF(...)	do { \
83 	if (tty == -1) \
84 		tty = open("/dev/tty", O_RDWR); \
85 	if (tty == -1) \
86 		abort(); \
87 	dprintf(tty, __VA_ARGS__); \
88 } while (/*CONSTCOND*/0)
89 #else
90 #define DPRINTF(...)
91 #endif
92 
93 #ifdef ZLIBSUPPORT
94 /*
95  * The following python code is not really used because ZLIBSUPPORT is only
96  * defined if we have a built-in zlib, and the built-in zlib handles that.
97  * That is not true for android where we have zlib.h and not -lz.
98  */
99 static const char zlibcode[] =
100     "import sys, zlib; sys.stdout.write(zlib.decompress(sys.stdin.read()))";
101 
102 static const char *zlib_args[] = { "python", "-c", zlibcode, NULL };
103 
104 static int
105 zlibcmp(const unsigned char *buf)
106 {
107 	unsigned short x = 1;
108 	unsigned char *s = CAST(unsigned char *, CAST(void *, &x));
109 
110 	if ((buf[0] & 0xf) != 8 || (buf[0] & 0x80) != 0)
111 		return 0;
112 	if (s[0] != 1)	/* endianness test */
113 		x = buf[0] | (buf[1] << 8);
114 	else
115 		x = buf[1] | (buf[0] << 8);
116 	if (x % 31)
117 		return 0;
118 	return 1;
119 }
120 #endif
121 
122 #define gzip_flags "-cd"
123 #define lrzip_flags "-do"
124 #define lzip_flags gzip_flags
125 
126 static const char *gzip_args[] = {
127 	"gzip", gzip_flags, NULL
128 };
129 static const char *uncompress_args[] = {
130 	"uncompress", "-c", NULL
131 };
132 static const char *bzip2_args[] = {
133 	"bzip2", "-cd", NULL
134 };
135 static const char *lzip_args[] = {
136 	"lzip", lzip_flags, NULL
137 };
138 static const char *xz_args[] = {
139 	"xz", "-cd", NULL
140 };
141 static const char *lrzip_args[] = {
142 	"lrzip", lrzip_flags, NULL
143 };
144 static const char *lz4_args[] = {
145 	"lz4", "-cd", NULL
146 };
147 static const char *zstd_args[] = {
148 	"zstd", "-cd", NULL
149 };
150 
151 #define	do_zlib		NULL
152 #define	do_bzlib	NULL
153 
154 private const struct {
155 	const void *magic;
156 	size_t maglen;
157 	const char **argv;
158 	void *unused;
159 } compr[] = {
160 	{ "\037\235",	2, gzip_args, NULL },		/* compressed */
161 	/* Uncompress can get stuck; so use gzip first if we have it
162 	 * Idea from Damien Clark, thanks! */
163 	{ "\037\235",	2, uncompress_args, NULL },	/* compressed */
164 	{ "\037\213",	2, gzip_args, do_zlib },	/* gzipped */
165 	{ "\037\236",	2, gzip_args, NULL },		/* frozen */
166 	{ "\037\240",	2, gzip_args, NULL },		/* SCO LZH */
167 	/* the standard pack utilities do not accept standard input */
168 	{ "\037\036",	2, gzip_args, NULL },		/* packed */
169 	{ "PK\3\4",	4, gzip_args, NULL },		/* pkzipped, */
170 	/* ...only first file examined */
171 	{ "BZh",	3, bzip2_args, do_bzlib },	/* bzip2-ed */
172 	{ "LZIP",	4, lzip_args, NULL },		/* lzip-ed */
173  	{ "\3757zXZ\0",	6, xz_args, NULL },		/* XZ Utils */
174  	{ "LRZI",	4, lrzip_args, NULL },	/* LRZIP */
175  	{ "\004\"M\030",4, lz4_args, NULL },		/* LZ4 */
176  	{ "\x28\xB5\x2F\xFD", 4, zstd_args, NULL },	/* zstd */
177 #ifdef ZLIBSUPPORT
178 	{ RCAST(const void *, zlibcmp),	0, zlib_args, NULL },	/* zlib */
179 #endif
180 };
181 
182 #define OKDATA 	0
183 #define NODATA	1
184 #define ERRDATA	2
185 
186 private ssize_t swrite(int, const void *, size_t);
187 #if HAVE_FORK
188 private size_t ncompr = __arraycount(compr);
189 private int uncompressbuf(int, size_t, size_t, const unsigned char *,
190     unsigned char **, size_t *);
191 #ifdef BUILTIN_DECOMPRESS
192 private int uncompresszlib(const unsigned char *, unsigned char **, size_t,
193     size_t *, int);
194 private int uncompressgzipped(const unsigned char *, unsigned char **, size_t,
195     size_t *);
196 #endif
197 #ifdef BUILTIN_BZLIB
198 private int uncompressbzlib(const unsigned char *, unsigned char **, size_t,
199     size_t *, int);
200 #endif
201 
202 static int makeerror(unsigned char **, size_t *, const char *, ...)
203     __attribute__((__format__(__printf__, 3, 4)));
204 private const char *methodname(size_t);
205 
206 private int
207 format_decompression_error(struct magic_set *ms, size_t i, unsigned char *buf)
208 {
209 	unsigned char *p;
210 	int mime = ms->flags & MAGIC_MIME;
211 
212 	if (!mime)
213 		return file_printf(ms, "ERROR:[%s: %s]", methodname(i), buf);
214 
215 	for (p = buf; *p; p++)
216 		if (!isalnum(*p))
217 			*p = '-';
218 
219 	return file_printf(ms, "application/x-decompression-error-%s-%s",
220 	    methodname(i), buf);
221 }
222 
223 protected int
224 file_zmagic(struct magic_set *ms, const struct buffer *b, const char *name)
225 {
226 	unsigned char *newbuf = NULL;
227 	size_t i, nsz;
228 	char *rbuf;
229 	file_pushbuf_t *pb;
230 	int urv, prv, rv = 0;
231 	int mime = ms->flags & MAGIC_MIME;
232 	int fd = b->fd;
233 	const unsigned char *buf = CAST(const unsigned char *, b->fbuf);
234 	size_t nbytes = b->flen;
235 	int sa_saved = 0;
236 	struct sigaction sig_act;
237 
238 	if ((ms->flags & MAGIC_COMPRESS) == 0)
239 		return 0;
240 
241 	for (i = 0; i < ncompr; i++) {
242 		int zm;
243 		if (nbytes < compr[i].maglen)
244 			continue;
245 #ifdef ZLIBSUPPORT
246 		if (compr[i].maglen == 0)
247 			zm = (RCAST(int (*)(const unsigned char *),
248 			    CCAST(void *, compr[i].magic)))(buf);
249 		else
250 #endif
251 			zm = memcmp(buf, compr[i].magic, compr[i].maglen) == 0;
252 
253 		if (!zm)
254 			continue;
255 
256 		/* Prevent SIGPIPE death if child dies unexpectedly */
257 		if (!sa_saved) {
258 			//We can use sig_act for both new and old, but
259 			struct sigaction new_act;
260 			memset(&new_act, 0, sizeof(new_act));
261 			new_act.sa_handler = SIG_IGN;
262 			sa_saved = sigaction(SIGPIPE, &new_act, &sig_act) != -1;
263 		}
264 
265 		nsz = nbytes;
266 		urv = uncompressbuf(fd, ms->bytes_max, i, buf, &newbuf, &nsz);
267 		DPRINTF("uncompressbuf = %d, %s, %" SIZE_T_FORMAT "u\n", urv,
268 		    (char *)newbuf, nsz);
269 		switch (urv) {
270 		case OKDATA:
271 		case ERRDATA:
272 			ms->flags &= ~MAGIC_COMPRESS;
273 			if (urv == ERRDATA)
274 				prv = format_decompression_error(ms, i, newbuf);
275 			else
276 				prv = file_buffer(ms, -1, NULL, name, newbuf, nsz);
277 			if (prv == -1)
278 				goto error;
279 			rv = 1;
280 			if ((ms->flags & MAGIC_COMPRESS_TRANSP) != 0)
281 				goto out;
282 			if (mime != MAGIC_MIME && mime != 0)
283 				goto out;
284 			if ((file_printf(ms,
285 			    mime ? " compressed-encoding=" : " (")) == -1)
286 				goto error;
287 			if ((pb = file_push_buffer(ms)) == NULL)
288 				goto error;
289 			/*
290 			 * XXX: If file_buffer fails here, we overwrite
291 			 * the compressed text. FIXME.
292 			 */
293 			if (file_buffer(ms, -1, NULL, NULL, buf, nbytes) == -1) {
294 				if (file_pop_buffer(ms, pb) != NULL)
295 					abort();
296 				goto error;
297 			}
298 			if ((rbuf = file_pop_buffer(ms, pb)) != NULL) {
299 				if (file_printf(ms, "%s", rbuf) == -1) {
300 					free(rbuf);
301 					goto error;
302 				}
303 				free(rbuf);
304 			}
305 			if (!mime && file_printf(ms, ")") == -1)
306 				goto error;
307 			/*FALLTHROUGH*/
308 		case NODATA:
309 			break;
310 		default:
311 			abort();
312 			/*NOTREACHED*/
313 		error:
314 			rv = -1;
315 			break;
316 		}
317 	}
318 out:
319 	DPRINTF("rv = %d\n", rv);
320 
321 	if (sa_saved && sig_act.sa_handler != SIG_IGN)
322 		(void)sigaction(SIGPIPE, &sig_act, NULL);
323 
324 	free(newbuf);
325 	ms->flags |= MAGIC_COMPRESS;
326 	DPRINTF("Zmagic returns %d\n", rv);
327 	return rv;
328 }
329 #endif
330 /*
331  * `safe' write for sockets and pipes.
332  */
333 private ssize_t
334 swrite(int fd, const void *buf, size_t n)
335 {
336 	ssize_t rv;
337 	size_t rn = n;
338 
339 	do
340 		switch (rv = write(fd, buf, n)) {
341 		case -1:
342 			if (errno == EINTR)
343 				continue;
344 			return -1;
345 		default:
346 			n -= rv;
347 			buf = CAST(const char *, buf) + rv;
348 			break;
349 		}
350 	while (n > 0);
351 	return rn;
352 }
353 
354 
355 /*
356  * `safe' read for sockets and pipes.
357  */
358 protected ssize_t
359 sread(int fd, void *buf, size_t n, int canbepipe __attribute__((__unused__)))
360 {
361 	ssize_t rv;
362 #ifdef FIONREAD
363 	int t = 0;
364 #endif
365 	size_t rn = n;
366 
367 	if (fd == STDIN_FILENO)
368 		goto nocheck;
369 
370 #ifdef FIONREAD
371 	if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) {
372 #ifdef FD_ZERO
373 		ssize_t cnt;
374 		for (cnt = 0;; cnt++) {
375 			fd_set check;
376 			struct timeval tout = {0, 100 * 1000};
377 			int selrv;
378 
379 			FD_ZERO(&check);
380 			FD_SET(fd, &check);
381 
382 			/*
383 			 * Avoid soft deadlock: do not read if there
384 			 * is nothing to read from sockets and pipes.
385 			 */
386 			selrv = select(fd + 1, &check, NULL, NULL, &tout);
387 			if (selrv == -1) {
388 				if (errno == EINTR || errno == EAGAIN)
389 					continue;
390 			} else if (selrv == 0 && cnt >= 5) {
391 				return 0;
392 			} else
393 				break;
394 		}
395 #endif
396 		(void)ioctl(fd, FIONREAD, &t);
397 	}
398 
399 	if (t > 0 && CAST(size_t, t) < n) {
400 		n = t;
401 		rn = n;
402 	}
403 #endif
404 
405 nocheck:
406 	do
407 		switch ((rv = read(fd, buf, n))) {
408 		case -1:
409 			if (errno == EINTR)
410 				continue;
411 			return -1;
412 		case 0:
413 			return rn - n;
414 		default:
415 			n -= rv;
416 			buf = CAST(char *, CCAST(void *, buf)) + rv;
417 			break;
418 		}
419 	while (n > 0);
420 	return rn;
421 }
422 
423 protected int
424 file_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
425     size_t nbytes)
426 {
427 	char buf[4096];
428 	ssize_t r;
429 	int tfd;
430 
431 	(void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof buf);
432 #ifndef HAVE_MKSTEMP
433 	{
434 		char *ptr = mktemp(buf);
435 		tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
436 		r = errno;
437 		(void)unlink(ptr);
438 		errno = r;
439 	}
440 #else
441 	{
442 		int te;
443 		mode_t ou = umask(0);
444 		tfd = mkstemp(buf);
445 		(void)umask(ou);
446 		te = errno;
447 		(void)unlink(buf);
448 		errno = te;
449 	}
450 #endif
451 	if (tfd == -1) {
452 		file_error(ms, errno,
453 		    "cannot create temporary file for pipe copy");
454 		return -1;
455 	}
456 
457 	if (swrite(tfd, startbuf, nbytes) != CAST(ssize_t, nbytes))
458 		r = 1;
459 	else {
460 		while ((r = sread(fd, buf, sizeof(buf), 1)) > 0)
461 			if (swrite(tfd, buf, CAST(size_t, r)) != r)
462 				break;
463 	}
464 
465 	switch (r) {
466 	case -1:
467 		file_error(ms, errno, "error copying from pipe to temp file");
468 		return -1;
469 	case 0:
470 		break;
471 	default:
472 		file_error(ms, errno, "error while writing to temp file");
473 		return -1;
474 	}
475 
476 	/*
477 	 * We duplicate the file descriptor, because fclose on a
478 	 * tmpfile will delete the file, but any open descriptors
479 	 * can still access the phantom inode.
480 	 */
481 	if ((fd = dup2(tfd, fd)) == -1) {
482 		file_error(ms, errno, "could not dup descriptor for temp file");
483 		return -1;
484 	}
485 	(void)close(tfd);
486 	if (lseek(fd, CAST(off_t, 0), SEEK_SET) == CAST(off_t, -1)) {
487 		file_badseek(ms);
488 		return -1;
489 	}
490 	return fd;
491 }
492 #if HAVE_FORK
493 #ifdef BUILTIN_DECOMPRESS
494 
495 #define FHCRC		(1 << 1)
496 #define FEXTRA		(1 << 2)
497 #define FNAME		(1 << 3)
498 #define FCOMMENT	(1 << 4)
499 
500 
501 private int
502 uncompressgzipped(const unsigned char *old, unsigned char **newch,
503     size_t bytes_max, size_t *n)
504 {
505 	unsigned char flg = old[3];
506 	size_t data_start = 10;
507 
508 	if (flg & FEXTRA) {
509 		if (data_start + 1 >= *n)
510 			goto err;
511 		data_start += 2 + old[data_start] + old[data_start + 1] * 256;
512 	}
513 	if (flg & FNAME) {
514 		while(data_start < *n && old[data_start])
515 			data_start++;
516 		data_start++;
517 	}
518 	if (flg & FCOMMENT) {
519 		while(data_start < *n && old[data_start])
520 			data_start++;
521 		data_start++;
522 	}
523 	if (flg & FHCRC)
524 		data_start += 2;
525 
526 	if (data_start >= *n)
527 		goto err;
528 
529 	*n -= data_start;
530 	old += data_start;
531 	return uncompresszlib(old, newch, bytes_max, n, 0);
532 err:
533 	return makeerror(newch, n, "File too short");
534 }
535 
536 private int
537 uncompresszlib(const unsigned char *old, unsigned char **newch,
538     size_t bytes_max, size_t *n, int zlib)
539 {
540 	int rc;
541 	z_stream z;
542 
543 	if ((*newch = CAST(unsigned char *, malloc(bytes_max + 1))) == NULL)
544 		return makeerror(newch, n, "No buffer, %s", strerror(errno));
545 
546 	z.next_in = CCAST(Bytef *, old);
547 	z.avail_in = CAST(uint32_t, *n);
548 	z.next_out = *newch;
549 	z.avail_out = CAST(unsigned int, bytes_max);
550 	z.zalloc = Z_NULL;
551 	z.zfree = Z_NULL;
552 	z.opaque = Z_NULL;
553 
554 	/* LINTED bug in header macro */
555 	rc = zlib ? inflateInit(&z) : inflateInit2(&z, -15);
556 	if (rc != Z_OK)
557 		goto err;
558 
559 	rc = inflate(&z, Z_SYNC_FLUSH);
560 	if (rc != Z_OK && rc != Z_STREAM_END)
561 		goto err;
562 
563 	*n = CAST(size_t, z.total_out);
564 	rc = inflateEnd(&z);
565 	if (rc != Z_OK)
566 		goto err;
567 
568 	/* let's keep the nul-terminate tradition */
569 	(*newch)[*n] = '\0';
570 
571 	return OKDATA;
572 err:
573 	strlcpy(RCAST(char *, *newch), z.msg ? z.msg : zError(rc), bytes_max);
574 	*n = strlen(RCAST(char *, *newch));
575 	return ERRDATA;
576 }
577 #endif
578 
579 static int
580 makeerror(unsigned char **buf, size_t *len, const char *fmt, ...)
581 {
582 	char *msg;
583 	va_list ap;
584 	int rv;
585 
586 	va_start(ap, fmt);
587 	rv = vasprintf(&msg, fmt, ap);
588 	va_end(ap);
589 	if (rv < 0) {
590 		*buf = NULL;
591 		*len = 0;
592 		return NODATA;
593 	}
594 	*buf = RCAST(unsigned char *, msg);
595 	*len = strlen(msg);
596 	return ERRDATA;
597 }
598 
599 static void
600 closefd(int *fd, size_t i)
601 {
602 	if (fd[i] == -1)
603 		return;
604 	(void) close(fd[i]);
605 	fd[i] = -1;
606 }
607 
608 static void
609 closep(int *fd)
610 {
611 	size_t i;
612 	for (i = 0; i < 2; i++)
613 		closefd(fd, i);
614 }
615 
616 static int
617 copydesc(int i, int fd)
618 {
619 	if (fd == i)
620 		return 0; /* "no dup was necessary" */
621 	if (dup2(fd, i) == -1) {
622 		DPRINTF("dup(%d, %d) failed (%s)\n", fd, i, strerror(errno));
623 		exit(1);
624 	}
625 	return 1;
626 }
627 
628 static pid_t
629 writechild(int fd, const void *old, size_t n)
630 {
631 	pid_t pid;
632 
633 	/*
634 	 * fork again, to avoid blocking because both
635 	 * pipes filled
636 	 */
637 	pid = fork();
638 	if (pid == -1) {
639 		DPRINTF("Fork failed (%s)\n", strerror(errno));
640 		exit(1);
641 	}
642 	if (pid == 0) {
643 		/* child */
644 		if (swrite(fd, old, n) != CAST(ssize_t, n)) {
645 			DPRINTF("Write failed (%s)\n", strerror(errno));
646 			exit(1);
647 		}
648 		exit(0);
649 	}
650 	/* parent */
651 	return pid;
652 }
653 
654 static ssize_t
655 filter_error(unsigned char *ubuf, ssize_t n)
656 {
657 	char *p;
658 	char *buf;
659 
660 	ubuf[n] = '\0';
661 	buf = RCAST(char *, ubuf);
662 	while (isspace(CAST(unsigned char, *buf)))
663 		buf++;
664 	DPRINTF("Filter error[[[%s]]]\n", buf);
665 	if ((p = strchr(CAST(char *, buf), '\n')) != NULL)
666 		*p = '\0';
667 	if ((p = strchr(CAST(char *, buf), ';')) != NULL)
668 		*p = '\0';
669 	if ((p = strrchr(CAST(char *, buf), ':')) != NULL) {
670 		++p;
671 		while (isspace(CAST(unsigned char, *p)))
672 			p++;
673 		n = strlen(p);
674 		memmove(ubuf, p, CAST(size_t, n + 1));
675 	}
676 	DPRINTF("Filter error after[[[%s]]]\n", (char *)ubuf);
677 	if (islower(*ubuf))
678 		*ubuf = toupper(*ubuf);
679 	return n;
680 }
681 
682 private const char *
683 methodname(size_t method)
684 {
685 #ifdef BUILTIN_DECOMPRESS
686         /* FIXME: This doesn't cope with bzip2 */
687 	if (method == 2 || compr[method].maglen == 0)
688 	    return "zlib";
689 #endif
690 	return compr[method].argv[0];
691 }
692 
693 private int
694 uncompressbuf(int fd, size_t bytes_max, size_t method, const unsigned char *old,
695     unsigned char **newch, size_t* n)
696 {
697 	int fdp[3][2];
698 	int status, rv, w;
699 	pid_t pid;
700 	pid_t writepid = -1;
701 	size_t i;
702 	ssize_t r;
703 
704 #ifdef BUILTIN_DECOMPRESS
705         /* FIXME: This doesn't cope with bzip2 */
706 	if (method == 2)
707 		return uncompressgzipped(old, newch, bytes_max, n);
708 	if (compr[method].maglen == 0)
709 		return uncompresszlib(old, newch, bytes_max, n, 1);
710 #endif
711 	(void)fflush(stdout);
712 	(void)fflush(stderr);
713 
714 	for (i = 0; i < __arraycount(fdp); i++)
715 		fdp[i][0] = fdp[i][1] = -1;
716 
717 	if ((fd == -1 && pipe(fdp[STDIN_FILENO]) == -1) ||
718 	    pipe(fdp[STDOUT_FILENO]) == -1 || pipe(fdp[STDERR_FILENO]) == -1) {
719 		closep(fdp[STDIN_FILENO]);
720 		closep(fdp[STDOUT_FILENO]);
721 		return makeerror(newch, n, "Cannot create pipe, %s",
722 		    strerror(errno));
723 	}
724 
725 	/* For processes with large mapped virtual sizes, vfork
726 	 * may be _much_ faster (10-100 times) than fork.
727 	 */
728 	pid = vfork();
729 	if (pid == -1) {
730 		return makeerror(newch, n, "Cannot vfork, %s",
731 		    strerror(errno));
732 	}
733 	if (pid == 0) {
734 		/* child */
735 		/* Note: we are after vfork, do not modify memory
736 		 * in a way which confuses parent. In particular,
737 		 * do not modify fdp[i][j].
738 		 */
739 		if (fd != -1) {
740 			(void) lseek(fd, CAST(off_t, 0), SEEK_SET);
741 			if (copydesc(STDIN_FILENO, fd))
742 				(void) close(fd);
743 		} else {
744 			if (copydesc(STDIN_FILENO, fdp[STDIN_FILENO][0]))
745 				(void) close(fdp[STDIN_FILENO][0]);
746 			if (fdp[STDIN_FILENO][1] > 2)
747 				(void) close(fdp[STDIN_FILENO][1]);
748 		}
749 ///FIXME: if one of the fdp[i][j] is 0 or 1, this can bomb spectacularly
750 		if (copydesc(STDOUT_FILENO, fdp[STDOUT_FILENO][1]))
751 			(void) close(fdp[STDOUT_FILENO][1]);
752 		if (fdp[STDOUT_FILENO][0] > 2)
753 			(void) close(fdp[STDOUT_FILENO][0]);
754 
755 		if (copydesc(STDERR_FILENO, fdp[STDERR_FILENO][1]))
756 			(void) close(fdp[STDERR_FILENO][1]);
757 		if (fdp[STDERR_FILENO][0] > 2)
758 			(void) close(fdp[STDERR_FILENO][0]);
759 
760 		(void)execvp(compr[method].argv[0],
761 		    RCAST(char *const *, RCAST(intptr_t, compr[method].argv)));
762 		dprintf(STDERR_FILENO, "exec `%s' failed, %s",
763 		    compr[method].argv[0], strerror(errno));
764 		_exit(1); /* _exit(), not exit(), because of vfork */
765 	}
766 	/* parent */
767 	/* Close write sides of child stdout/err pipes */
768 	for (i = 1; i < __arraycount(fdp); i++)
769 		closefd(fdp[i], 1);
770 	/* Write the buffer data to child stdin, if we don't have fd */
771 	if (fd == -1) {
772 		closefd(fdp[STDIN_FILENO], 0);
773 		writepid = writechild(fdp[STDIN_FILENO][1], old, *n);
774 		closefd(fdp[STDIN_FILENO], 1);
775 	}
776 
777 	*newch = CAST(unsigned char *, malloc(bytes_max + 1));
778 	if (*newch == NULL) {
779 		rv = makeerror(newch, n, "No buffer, %s",
780 		    strerror(errno));
781 		goto err;
782 	}
783 	rv = OKDATA;
784 	r = sread(fdp[STDOUT_FILENO][0], *newch, bytes_max, 0);
785 	if (r <= 0) {
786 		DPRINTF("Read stdout failed %d (%s)\n", fdp[STDOUT_FILENO][0],
787 		    r != -1 ? strerror(errno) : "no data");
788 
789 		rv = ERRDATA;
790 		if (r == 0 &&
791 		    (r = sread(fdp[STDERR_FILENO][0], *newch, bytes_max, 0)) > 0)
792 		{
793 			r = filter_error(*newch, r);
794 			goto ok;
795 		}
796 		free(*newch);
797 		if  (r == 0)
798 			rv = makeerror(newch, n, "Read failed, %s",
799 			    strerror(errno));
800 		else
801 			rv = makeerror(newch, n, "No data");
802 		goto err;
803 	}
804 ok:
805 	*n = r;
806 	/* NUL terminate, as every buffer is handled here. */
807 	(*newch)[*n] = '\0';
808 err:
809 	closefd(fdp[STDIN_FILENO], 1);
810 	closefd(fdp[STDOUT_FILENO], 0);
811 	closefd(fdp[STDERR_FILENO], 0);
812 
813 	w = waitpid(pid, &status, 0);
814 wait_err:
815 	if (w == -1) {
816 		free(*newch);
817 		rv = makeerror(newch, n, "Wait failed, %s", strerror(errno));
818 		DPRINTF("Child wait return %#x\n", status);
819 	} else if (!WIFEXITED(status)) {
820 		DPRINTF("Child not exited (%#x)\n", status);
821 	} else if (WEXITSTATUS(status) != 0) {
822 		DPRINTF("Child exited (%#x)\n", WEXITSTATUS(status));
823 	}
824 	if (writepid > 0) {
825 		/* _After_ we know decompressor has exited, our input writer
826 		 * definitely will exit now (at worst, writing fails in it,
827 		 * since output fd is closed now on the reading size).
828 		 */
829 		w = waitpid(writepid, &status, 0);
830 		writepid = -1;
831 		goto wait_err;
832 	}
833 
834 	closefd(fdp[STDIN_FILENO], 0); //why? it is already closed here!
835 	DPRINTF("Returning %p n=%" SIZE_T_FORMAT "u rv=%d\n", *newch, *n, rv);
836 
837 	return rv;
838 }
839 #endif
840