compress.c revision 169962
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 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28/* 29 * compress routines: 30 * zmagic() - returns 0 if not recognized, uncompresses and prints 31 * information if recognized 32 * uncompress(method, old, n, newch) - uncompress old into new, 33 * using method, return sizeof new 34 */ 35#include "file.h" 36#include "magic.h" 37#include <stdio.h> 38#include <stdlib.h> 39#ifdef HAVE_UNISTD_H 40#include <unistd.h> 41#endif 42#include <string.h> 43#include <errno.h> 44#include <sys/types.h> 45#include <sys/ioctl.h> 46#ifdef HAVE_SYS_WAIT_H 47#include <sys/wait.h> 48#endif 49#if defined(HAVE_SYS_TIME_H) 50#include <sys/time.h> 51#endif 52#ifdef HAVE_LIBZ 53#include <zlib.h> 54#endif 55 56 57#ifndef lint 58FILE_RCSID("@(#)$File: compress.c,v 1.51 2007/03/05 02:41:29 christos Exp $") 59#endif 60 61private struct { 62 const char *magic; 63 size_t maglen; 64 const char *const argv[3]; 65 int silent; 66} compr[] = { 67 { "\037\235", 2, { "gzip", "-cdq", NULL }, 1 }, /* compressed */ 68 /* Uncompress can get stuck; so use gzip first if we have it 69 * Idea from Damien Clark, thanks! */ 70 { "\037\235", 2, { "uncompress", "-c", NULL }, 1 }, /* compressed */ 71 { "\037\213", 2, { "gzip", "-cdq", NULL }, 1 }, /* gzipped */ 72 { "\037\236", 2, { "gzip", "-cdq", NULL }, 1 }, /* frozen */ 73 { "\037\240", 2, { "gzip", "-cdq", NULL }, 1 }, /* SCO LZH */ 74 /* the standard pack utilities do not accept standard input */ 75 { "\037\036", 2, { "gzip", "-cdq", NULL }, 0 }, /* packed */ 76 { "PK\3\4", 4, { "gzip", "-cdq", NULL }, 1 }, /* pkzipped, */ 77 /* ...only first file examined */ 78 { "BZh", 3, { "bzip2", "-cd", NULL }, 1 }, /* bzip2-ed */ 79}; 80 81private size_t ncompr = sizeof(compr) / sizeof(compr[0]); 82 83#define NODATA ((size_t)~0) 84 85 86private ssize_t swrite(int, const void *, size_t); 87private size_t uncompressbuf(struct magic_set *, int, size_t, 88 const unsigned char *, unsigned char **, size_t); 89#ifdef HAVE_LIBZ 90private size_t uncompressgzipped(struct magic_set *, const unsigned char *, 91 unsigned char **, size_t); 92#endif 93 94protected int 95file_zmagic(struct magic_set *ms, int fd, const char *name, 96 const unsigned char *buf, size_t nbytes) 97{ 98 unsigned char *newbuf = NULL; 99 size_t i, nsz; 100 int rv = 0; 101 102 if ((ms->flags & MAGIC_COMPRESS) == 0) 103 return 0; 104 105 for (i = 0; i < ncompr; i++) { 106 if (nbytes < compr[i].maglen) 107 continue; 108 if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 && 109 (nsz = uncompressbuf(ms, fd, i, buf, &newbuf, 110 nbytes)) != NODATA) { 111 ms->flags &= ~MAGIC_COMPRESS; 112 rv = -1; 113 if (file_buffer(ms, -1, name, newbuf, nsz) == -1) 114 goto error; 115 if (file_printf(ms, " (") == -1) 116 goto error; 117 if (file_buffer(ms, -1, NULL, buf, nbytes) == -1) 118 goto error; 119 if (file_printf(ms, ")") == -1) 120 goto error; 121 rv = 1; 122 break; 123 } 124 } 125error: 126 if (newbuf) 127 free(newbuf); 128 ms->flags |= MAGIC_COMPRESS; 129 return rv; 130} 131 132/* 133 * `safe' write for sockets and pipes. 134 */ 135private ssize_t 136swrite(int fd, const void *buf, size_t n) 137{ 138 int rv; 139 size_t rn = n; 140 141 do 142 switch (rv = write(fd, buf, n)) { 143 case -1: 144 if (errno == EINTR) 145 continue; 146 return -1; 147 default: 148 n -= rv; 149 buf = ((const char *)buf) + rv; 150 break; 151 } 152 while (n > 0); 153 return rn; 154} 155 156 157/* 158 * `safe' read for sockets and pipes. 159 */ 160protected ssize_t 161sread(int fd, void *buf, size_t n, int canbepipe) 162{ 163 int rv, cnt; 164#ifdef FIONREAD 165 int t = 0; 166#endif 167 size_t rn = n; 168 169 if (fd == STDIN_FILENO) 170 goto nocheck; 171 172#ifdef FIONREAD 173 if ((canbepipe && (ioctl(fd, FIONREAD, &t) == -1)) || (t == 0)) { 174#ifdef FD_ZERO 175 for (cnt = 0;; cnt++) { 176 fd_set check; 177 struct timeval tout = {0, 100 * 1000}; 178 int selrv; 179 180 FD_ZERO(&check); 181 FD_SET(fd, &check); 182 183 /* 184 * Avoid soft deadlock: do not read if there 185 * is nothing to read from sockets and pipes. 186 */ 187 selrv = select(fd + 1, &check, NULL, NULL, &tout); 188 if (selrv == -1) { 189 if (errno == EINTR || errno == EAGAIN) 190 continue; 191 } else if (selrv == 0 && cnt >= 5) { 192 return 0; 193 } else 194 break; 195 } 196#endif 197 (void)ioctl(fd, FIONREAD, &t); 198 } 199 200 if (t > 0 && (size_t)t < n) { 201 n = t; 202 rn = n; 203 } 204#endif 205 206nocheck: 207 do 208 switch ((rv = read(fd, buf, n))) { 209 case -1: 210 if (errno == EINTR) 211 continue; 212 return -1; 213 case 0: 214 return rn - n; 215 default: 216 n -= rv; 217 buf = ((char *)buf) + rv; 218 break; 219 } 220 while (n > 0); 221 return rn; 222} 223 224protected int 225file_pipe2file(struct magic_set *ms, int fd, const void *startbuf, 226 size_t nbytes) 227{ 228 char buf[4096]; 229 int r, tfd; 230 231 (void)strcpy(buf, "/tmp/file.XXXXXX"); 232#ifndef HAVE_MKSTEMP 233 { 234 char *ptr = mktemp(buf); 235 tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600); 236 r = errno; 237 (void)unlink(ptr); 238 errno = r; 239 } 240#else 241 tfd = mkstemp(buf); 242 r = errno; 243 (void)unlink(buf); 244 errno = r; 245#endif 246 if (tfd == -1) { 247 file_error(ms, errno, 248 "cannot create temporary file for pipe copy"); 249 return -1; 250 } 251 252 if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes) 253 r = 1; 254 else { 255 while ((r = sread(fd, buf, sizeof(buf), 1)) > 0) 256 if (swrite(tfd, buf, (size_t)r) != r) 257 break; 258 } 259 260 switch (r) { 261 case -1: 262 file_error(ms, errno, "error copying from pipe to temp file"); 263 return -1; 264 case 0: 265 break; 266 default: 267 file_error(ms, errno, "error while writing to temp file"); 268 return -1; 269 } 270 271 /* 272 * We duplicate the file descriptor, because fclose on a 273 * tmpfile will delete the file, but any open descriptors 274 * can still access the phantom inode. 275 */ 276 if ((fd = dup2(tfd, fd)) == -1) { 277 file_error(ms, errno, "could not dup descriptor for temp file"); 278 return -1; 279 } 280 (void)close(tfd); 281 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { 282 file_badseek(ms); 283 return -1; 284 } 285 return fd; 286} 287 288#ifdef HAVE_LIBZ 289 290#define FHCRC (1 << 1) 291#define FEXTRA (1 << 2) 292#define FNAME (1 << 3) 293#define FCOMMENT (1 << 4) 294 295private size_t 296uncompressgzipped(struct magic_set *ms, const unsigned char *old, 297 unsigned char **newch, size_t n) 298{ 299 unsigned char flg = old[3]; 300 size_t data_start = 10; 301 z_stream z; 302 int rc; 303 304 if (flg & FEXTRA) { 305 if (data_start+1 >= n) 306 return 0; 307 data_start += 2 + old[data_start] + old[data_start + 1] * 256; 308 } 309 if (flg & FNAME) { 310 while(data_start < n && old[data_start]) 311 data_start++; 312 data_start++; 313 } 314 if(flg & FCOMMENT) { 315 while(data_start < n && old[data_start]) 316 data_start++; 317 data_start++; 318 } 319 if(flg & FHCRC) 320 data_start += 2; 321 322 if (data_start >= n) 323 return 0; 324 if ((*newch = (unsigned char *)malloc(HOWMANY + 1)) == NULL) { 325 return 0; 326 } 327 328 /* XXX: const castaway, via strchr */ 329 z.next_in = (Bytef *)strchr((const char *)old + data_start, 330 old[data_start]); 331 z.avail_in = n - data_start; 332 z.next_out = *newch; 333 z.avail_out = HOWMANY; 334 z.zalloc = Z_NULL; 335 z.zfree = Z_NULL; 336 z.opaque = Z_NULL; 337 338 rc = inflateInit2(&z, -15); 339 if (rc != Z_OK) { 340 file_error(ms, 0, "zlib: %s", z.msg); 341 return 0; 342 } 343 344 rc = inflate(&z, Z_SYNC_FLUSH); 345 if (rc != Z_OK && rc != Z_STREAM_END) { 346 file_error(ms, 0, "zlib: %s", z.msg); 347 return 0; 348 } 349 350 n = (size_t)z.total_out; 351 (void)inflateEnd(&z); 352 353 /* let's keep the nul-terminate tradition */ 354 (*newch)[n] = '\0'; 355 356 return n; 357} 358#endif 359 360private size_t 361uncompressbuf(struct magic_set *ms, int fd, size_t method, 362 const unsigned char *old, unsigned char **newch, size_t n) 363{ 364 int fdin[2], fdout[2]; 365 int r; 366 367#ifdef HAVE_LIBZ 368 if (method == 2) 369 return uncompressgzipped(ms, old, newch, n); 370#endif 371 (void)fflush(stdout); 372 (void)fflush(stderr); 373 374 if ((fd != -1 && pipe(fdin) == -1) || pipe(fdout) == -1) { 375 file_error(ms, errno, "cannot create pipe"); 376 return NODATA; 377 } 378 switch (fork()) { 379 case 0: /* child */ 380 (void) close(0); 381 if (fd != -1) { 382 (void) dup(fd); 383 (void) lseek(0, (off_t)0, SEEK_SET); 384 } else { 385 (void) dup(fdin[0]); 386 (void) close(fdin[0]); 387 (void) close(fdin[1]); 388 } 389 390 (void) close(1); 391 (void) dup(fdout[1]); 392 (void) close(fdout[0]); 393 (void) close(fdout[1]); 394#ifndef DEBUG 395 if (compr[method].silent) 396 (void)close(2); 397#endif 398 399 (void)execvp(compr[method].argv[0], 400 (char *const *)(intptr_t)compr[method].argv); 401#ifdef DEBUG 402 (void)fprintf(stderr, "exec `%s' failed (%s)\n", 403 compr[method].argv[0], strerror(errno)); 404#endif 405 exit(1); 406 /*NOTREACHED*/ 407 case -1: 408 file_error(ms, errno, "could not fork"); 409 return NODATA; 410 411 default: /* parent */ 412 (void) close(fdout[1]); 413 if (fd == -1) { 414 (void) close(fdin[0]); 415 /* 416 * fork again, to avoid blocking because both 417 * pipes filled 418 */ 419 switch (fork()) { 420 case 0: /* child */ 421 (void)close(fdout[0]); 422 if (swrite(fdin[1], old, n) != (ssize_t)n) { 423#ifdef DEBUG 424 (void)fprintf(stderr, 425 "Write failed (%s)\n", 426 strerror(errno)); 427#endif 428 exit(1); 429 } 430 exit(0); 431 /*NOTREACHED*/ 432 433 case -1: 434#ifdef DEBUG 435 (void)fprintf(stderr, "Fork failed (%s)\n", 436 strerror(errno)); 437#endif 438 exit(1); 439 /*NOTREACHED*/ 440 441 default: /* parent */ 442 break; 443 } 444 (void) close(fdin[1]); 445 fdin[1] = -1; 446 } 447 448 if ((*newch = (unsigned char *) malloc(HOWMANY + 1)) == NULL) { 449#ifdef DEBUG 450 (void)fprintf(stderr, "Malloc failed (%s)\n", 451 strerror(errno)); 452#endif 453 n = 0; 454 goto err; 455 } 456 if ((r = sread(fdout[0], *newch, HOWMANY, 0)) <= 0) { 457#ifdef DEBUG 458 (void)fprintf(stderr, "Read failed (%s)\n", 459 strerror(errno)); 460#endif 461 free(*newch); 462 n = 0; 463 newch[0] = '\0'; 464 goto err; 465 } else { 466 n = r; 467 } 468 /* NUL terminate, as every buffer is handled here. */ 469 (*newch)[n] = '\0'; 470err: 471 if (fdin[1] != -1) 472 (void) close(fdin[1]); 473 (void) close(fdout[0]); 474#ifdef WNOHANG 475 while (waitpid(-1, NULL, WNOHANG) != -1) 476 continue; 477#else 478 (void)wait(NULL); 479#endif 480 return n; 481 } 482} 483