compress.c revision 133359
1/*
2 * Copyright (c) Ian F. Darwin 1986-1995.
3 * Software written by Ian F. Darwin and others;
4 * maintained 1995-present by Christos Zoulas and others.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice immediately at the beginning of the file, without modification,
11 *    this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *    This product includes software developed by Ian F. Darwin and others.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
25 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33/*
34 * compress routines:
35 *	zmagic() - returns 0 if not recognized, uncompresses and prints
36 *		   information if recognized
37 *	uncompress(method, old, n, newch) - uncompress old into new,
38 *					    using method, return sizeof new
39 */
40#include "file.h"
41#include "magic.h"
42#include <stdio.h>
43#include <stdlib.h>
44#ifdef HAVE_UNISTD_H
45#include <unistd.h>
46#endif
47#include <string.h>
48#include <errno.h>
49#include <sys/types.h>
50#ifdef HAVE_SYS_WAIT_H
51#include <sys/wait.h>
52#endif
53#ifdef HAVE_LIBZ
54#include <zlib.h>
55#endif
56
57#ifndef lint
58FILE_RCSID("@(#)$Id: compress.c,v 1.37 2004/07/24 21:00:56 christos Exp $")
59#endif
60
61
62private struct {
63	const char *magic;
64	size_t maglen;
65	const char *const argv[3];
66	int silent;
67} compr[] = {
68	{ "\037\235", 2, { "gzip", "-cdq", NULL }, 1 },		/* compressed */
69	/* Uncompress can get stuck; so use gzip first if we have it
70	 * Idea from Damien Clark, thanks! */
71	{ "\037\235", 2, { "uncompress", "-c", NULL }, 1 },	/* compressed */
72	{ "\037\213", 2, { "gzip", "-cdq", NULL }, 1 },		/* gzipped */
73	{ "\037\236", 2, { "gzip", "-cdq", NULL }, 1 },		/* frozen */
74	{ "\037\240", 2, { "gzip", "-cdq", NULL }, 1 },		/* SCO LZH */
75	/* the standard pack utilities do not accept standard input */
76	{ "\037\036", 2, { "gzip", "-cdq", NULL }, 0 },		/* packed */
77	{ "BZh",      3, { "bzip2", "-cd", NULL }, 1 },		/* bzip2-ed */
78};
79
80private int ncompr = sizeof(compr) / sizeof(compr[0]);
81
82
83private ssize_t swrite(int, const void *, size_t);
84private ssize_t sread(int, void *, size_t);
85private size_t uncompressbuf(struct magic_set *, size_t, const unsigned char *,
86    unsigned char **, size_t);
87#ifdef HAVE_LIBZ
88private size_t uncompressgzipped(struct magic_set *, const unsigned char *,
89    unsigned char **, size_t);
90#endif
91
92protected int
93file_zmagic(struct magic_set *ms, const unsigned char *buf, size_t nbytes)
94{
95	unsigned char *newbuf = NULL;
96	size_t i, nsz;
97	int rv = 0;
98
99	if ((ms->flags & MAGIC_COMPRESS) == 0)
100		return 0;
101
102	for (i = 0; i < ncompr; i++) {
103		if (nbytes < compr[i].maglen)
104			continue;
105		if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 &&
106		    (nsz = uncompressbuf(ms, i, buf, &newbuf, nbytes)) != 0) {
107			ms->flags &= ~MAGIC_COMPRESS;
108			rv = -1;
109			if (file_buffer(ms, newbuf, nsz) == -1)
110				goto error;
111			if (file_printf(ms, " (") == -1)
112				goto error;
113			if (file_buffer(ms, buf, nbytes) == -1)
114				goto error;
115			if (file_printf(ms, ")") == -1)
116				goto error;
117			rv = 1;
118			break;
119		}
120	}
121error:
122	if (newbuf)
123		free(newbuf);
124	ms->flags |= MAGIC_COMPRESS;
125	return rv;
126}
127
128/*
129 * `safe' write for sockets and pipes.
130 */
131private ssize_t
132swrite(int fd, const void *buf, size_t n)
133{
134	int rv;
135	size_t rn = n;
136
137	do
138		switch (rv = write(fd, buf, n)) {
139		case -1:
140			if (errno == EINTR)
141				continue;
142			return -1;
143		default:
144			n -= rv;
145			buf = ((const char *)buf) + rv;
146			break;
147		}
148	while (n > 0);
149	return rn;
150}
151
152
153/*
154 * `safe' read for sockets and pipes.
155 */
156private ssize_t
157sread(int fd, void *buf, size_t n)
158{
159	int rv;
160	size_t rn = n;
161
162	do
163		switch (rv = read(fd, buf, n)) {
164		case -1:
165			if (errno == EINTR)
166				continue;
167			return -1;
168		case 0:
169			return rn - n;
170		default:
171			n -= rv;
172			buf = ((char *)buf) + rv;
173			break;
174		}
175	while (n > 0);
176	return rn;
177}
178
179protected int
180file_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
181    size_t nbytes)
182{
183	char buf[4096];
184	int r, tfd;
185
186	(void)strcpy(buf, "/tmp/file.XXXXXX");
187#ifndef HAVE_MKSTEMP
188	{
189		char *ptr = mktemp(buf);
190		tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
191		r = errno;
192		(void)unlink(ptr);
193		errno = r;
194	}
195#else
196	tfd = mkstemp(buf);
197	r = errno;
198	(void)unlink(buf);
199	errno = r;
200#endif
201	if (tfd == -1) {
202		file_error(ms, errno,
203		    "cannot create temporary file for pipe copy");
204		return -1;
205	}
206
207	if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes)
208		r = 1;
209	else {
210		while ((r = sread(fd, buf, sizeof(buf))) > 0)
211			if (swrite(tfd, buf, (size_t)r) != r)
212				break;
213	}
214
215	switch (r) {
216	case -1:
217		file_error(ms, errno, "error copying from pipe to temp file");
218		return -1;
219	case 0:
220		break;
221	default:
222		file_error(ms, errno, "error while writing to temp file");
223		return -1;
224	}
225
226	/*
227	 * We duplicate the file descriptor, because fclose on a
228	 * tmpfile will delete the file, but any open descriptors
229	 * can still access the phantom inode.
230	 */
231	if ((fd = dup2(tfd, fd)) == -1) {
232		file_error(ms, errno, "could not dup descriptor for temp file");
233		return -1;
234	}
235	(void)close(tfd);
236	if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
237		file_badseek(ms);
238		return -1;
239	}
240	return fd;
241}
242
243#ifdef HAVE_LIBZ
244
245#define FHCRC		(1 << 1)
246#define FEXTRA		(1 << 2)
247#define FNAME		(1 << 3)
248#define FCOMMENT	(1 << 4)
249
250private size_t
251uncompressgzipped(struct magic_set *ms, const unsigned char *old,
252    unsigned char **newch, size_t n)
253{
254	unsigned char flg = old[3];
255	size_t data_start = 10;
256	z_stream z;
257	int rc;
258
259	if (flg & FEXTRA) {
260		if (data_start+1 >= n)
261			return 0;
262		data_start += 2 + old[data_start] + old[data_start + 1] * 256;
263	}
264	if (flg & FNAME) {
265		while(data_start < n && old[data_start])
266			data_start++;
267		data_start++;
268	}
269	if(flg & FCOMMENT) {
270		while(data_start < n && old[data_start])
271			data_start++;
272		data_start++;
273	}
274	if(flg & FHCRC)
275		data_start += 2;
276
277	if (data_start >= n)
278		return 0;
279	if ((*newch = (unsigned char *)malloc(HOWMANY + 1)) == NULL) {
280		return 0;
281	}
282
283	/* XXX: const castaway, via strchr */
284	z.next_in = (Bytef *)strchr((const char *)old + data_start,
285	    old[data_start]);
286	z.avail_in = n - data_start;
287	z.next_out = *newch;
288	z.avail_out = HOWMANY;
289	z.zalloc = Z_NULL;
290	z.zfree = Z_NULL;
291	z.opaque = Z_NULL;
292
293	rc = inflateInit2(&z, -15);
294	if (rc != Z_OK) {
295		file_error(ms, 0, "zlib: %s", z.msg);
296		return 0;
297	}
298
299	rc = inflate(&z, Z_SYNC_FLUSH);
300	if (rc != Z_OK && rc != Z_STREAM_END) {
301		file_error(ms, 0, "zlib: %s", z.msg);
302		return 0;
303	}
304
305	n = (size_t)z.total_out;
306	inflateEnd(&z);
307
308	/* let's keep the nul-terminate tradition */
309	(*newch)[n++] = '\0';
310
311	return n;
312}
313#endif
314
315private size_t
316uncompressbuf(struct magic_set *ms, size_t method, const unsigned char *old,
317    unsigned char **newch, size_t n)
318{
319	int fdin[2], fdout[2];
320	int r;
321
322	/* The buffer is NUL terminated, and we don't need that. */
323	n--;
324
325#ifdef HAVE_LIBZ
326	if (method == 2)
327		return uncompressgzipped(ms, old, newch, n);
328#endif
329
330	if (pipe(fdin) == -1 || pipe(fdout) == -1) {
331		file_error(ms, errno, "cannot create pipe");
332		return 0;
333	}
334	switch (fork()) {
335	case 0:	/* child */
336		(void) close(0);
337		(void) dup(fdin[0]);
338		(void) close(fdin[0]);
339		(void) close(fdin[1]);
340
341		(void) close(1);
342		(void) dup(fdout[1]);
343		(void) close(fdout[0]);
344		(void) close(fdout[1]);
345		if (compr[method].silent)
346			(void) close(2);
347
348		execvp(compr[method].argv[0],
349		       (char *const *)compr[method].argv);
350		exit(1);
351		/*NOTREACHED*/
352	case -1:
353		file_error(ms, errno, "could not fork");
354		return 0;
355
356	default: /* parent */
357		(void) close(fdin[0]);
358		(void) close(fdout[1]);
359		/* fork again, to avoid blocking because both pipes filled */
360		switch (fork()) {
361		case 0: /* child */
362			(void)close(fdout[0]);
363			if (swrite(fdin[1], old, n) != n)
364				exit(1);
365			exit(0);
366			/*NOTREACHED*/
367
368		case -1:
369			exit(1);
370			/*NOTREACHED*/
371
372		default:  /* parent */
373			break;
374		}
375		(void) close(fdin[1]);
376		fdin[1] = -1;
377		if ((*newch = (unsigned char *) malloc(HOWMANY + 1)) == NULL) {
378			n = 0;
379			goto err;
380		}
381		if ((r = sread(fdout[0], *newch, HOWMANY)) <= 0) {
382			free(*newch);
383			n = 0;
384			newch[0] = '\0';
385			goto err;
386		} else {
387			n = r;
388		}
389 		/* NUL terminate, as every buffer is handled here. */
390 		(*newch)[n++] = '\0';
391err:
392		if (fdin[1] != -1)
393			(void) close(fdin[1]);
394		(void) close(fdout[0]);
395#ifdef WNOHANG
396		while (waitpid(-1, NULL, WNOHANG) != -1)
397			continue;
398#else
399		(void)wait(NULL);
400#endif
401		return n;
402	}
403}
404