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