root/ext/fileinfo/libmagic/compress.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. file_zmagic
  2. swrite
  3. sread
  4. file_pipe2file
  5. uncompressgzipped
  6. uncompressbuf

   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 "config.h"
  36 #include "file.h"
  37 
  38 #ifndef lint
  39 FILE_RCSID("@(#)$File: compress.c,v 1.77 2014/12/12 16:33:01 christos Exp $")
  40 #endif
  41 
  42 #include "magic.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 #ifdef HAVE_SIGNAL_H
  50 #include <signal.h>
  51 # ifndef HAVE_SIG_T
  52 typedef void (*sig_t)(int);
  53 # endif /* HAVE_SIG_T */
  54 #endif 
  55 #ifndef PHP_WIN32
  56 #include <sys/ioctl.h>
  57 #endif
  58 #ifdef HAVE_SYS_WAIT_H
  59 #include <sys/wait.h>
  60 #endif
  61 #if defined(HAVE_SYS_TIME_H)
  62 #include <sys/time.h>
  63 #endif
  64 #if defined(HAVE_ZLIB_H) && defined(HAVE_LIBZ)
  65 #define BUILTIN_DECOMPRESS
  66 #include <zlib.h>
  67 #endif
  68 
  69 #undef FIONREAD
  70 
  71 
  72 private const struct {
  73         const char magic[8];
  74         size_t maglen;
  75         const char *argv[3];
  76         int silent;
  77 } compr[] = {
  78         { "\037\235", 2, { "gzip", "-cdq", NULL }, 1 },         /* compressed */
  79         /* Uncompress can get stuck; so use gzip first if we have it
  80          * Idea from Damien Clark, thanks! */
  81         { "\037\235", 2, { "uncompress", "-c", NULL }, 1 },     /* compressed */
  82         { "\037\213", 2, { "gzip", "-cdq", NULL }, 1 },         /* gzipped */
  83         { "\037\236", 2, { "gzip", "-cdq", NULL }, 1 },         /* frozen */
  84         { "\037\240", 2, { "gzip", "-cdq", NULL }, 1 },         /* SCO LZH */
  85         /* the standard pack utilities do not accept standard input */
  86         { "\037\036", 2, { "gzip", "-cdq", NULL }, 0 },         /* packed */
  87         { "PK\3\4",   4, { "gzip", "-cdq", NULL }, 1 },         /* pkzipped, */
  88                                             /* ...only first file examined */
  89         { "BZh",      3, { "bzip2", "-cd", NULL }, 1 },         /* bzip2-ed */
  90         { "LZIP",     4, { "lzip", "-cdq", NULL }, 1 },
  91         { "\3757zXZ\0",6,{ "xz", "-cd", NULL }, 1 },            /* XZ Utils */
  92         { "LRZI",     4, { "lrzip", "-dqo-", NULL }, 1 },       /* LRZIP */
  93         { "\004\"M\030", 4, { "lz4", "-cd", NULL }, 1 },        /* LZ4 */
  94 };
  95 
  96 #define NODATA ((size_t)~0)
  97 
  98 private ssize_t swrite(int, const void *, size_t);
  99 #ifdef PHP_FILEINFO_UNCOMPRESS
 100 private size_t uncompressbuf(struct magic_set *, int, size_t,
 101     const unsigned char *, unsigned char **, size_t);
 102 #ifdef BUILTIN_DECOMPRESS
 103 private size_t uncompressgzipped(struct magic_set *, const unsigned char *,
 104     unsigned char **, size_t);
 105 #endif
 106 
 107 protected int
 108 file_zmagic(struct magic_set *ms, int fd, const char *name,
 109     const unsigned char *buf, size_t nbytes)
 110 {
 111         unsigned char *newbuf = NULL;
 112         size_t i, nsz;
 113         int rv = 0;
 114         int mime = ms->flags & MAGIC_MIME;
 115 #ifdef HAVE_SIGNAL_H
 116         sig_t osigpipe;
 117 #endif
 118 
 119         if ((ms->flags & MAGIC_COMPRESS) == 0)
 120                 return 0;
 121 
 122 #ifdef HAVE_SIGNAL_H
 123         osigpipe = signal(SIGPIPE, SIG_IGN);
 124 #endif
 125         for (i = 0; i < ncompr; i++) {
 126                 if (nbytes < compr[i].maglen)
 127                         continue;
 128                 if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 &&
 129                     (nsz = uncompressbuf(ms, fd, i, buf, &newbuf,
 130                     nbytes)) != NODATA) {
 131                         ms->flags &= ~MAGIC_COMPRESS;
 132                         rv = -1;
 133                         if (file_buffer(ms, -1, name, newbuf, nsz) == -1)
 134                                 goto error;
 135 
 136                         if (mime == MAGIC_MIME || mime == 0) {
 137                                 if (file_printf(ms, mime ?
 138                                     " compressed-encoding=" : " (") == -1)
 139                                         goto error;
 140                                 if (file_buffer(ms, -1, NULL, buf, nbytes) == -1)
 141                                         goto error;
 142                                 if (!mime && file_printf(ms, ")") == -1)
 143                                         goto error;
 144                         }
 145 
 146                         rv = 1;
 147                         break;
 148                 }
 149         }
 150 error:
 151 #ifdef HAVE_SIGNAL_H
 152         (void)signal(SIGPIPE, osigpipe);
 153 #endif
 154         if (newbuf)
 155                 efree(newbuf);
 156         ms->flags |= MAGIC_COMPRESS;
 157         return rv;
 158 }
 159 #endif
 160 /*
 161  * `safe' write for sockets and pipes.
 162  */
 163 private ssize_t
 164 swrite(int fd, const void *buf, size_t n)
 165 {
 166         ssize_t rv;
 167         size_t rn = n;
 168 
 169         do
 170                 switch (rv = write(fd, buf, n)) {
 171                 case -1:
 172                         if (errno == EINTR)
 173                                 continue;
 174                         return -1;
 175                 default:
 176                         n -= rv;
 177                         buf = CAST(const char *, buf) + rv;
 178                         break;
 179                 }
 180         while (n > 0);
 181         return rn;
 182 }
 183 
 184 
 185 /*
 186  * `safe' read for sockets and pipes.
 187  */
 188 protected ssize_t
 189 sread(int fd, void *buf, size_t n, int canbepipe)
 190 {
 191         ssize_t rv;
 192 #ifdef FIONREAD
 193         int t = 0;
 194 #endif
 195         size_t rn = n;
 196 
 197         if (fd == STDIN_FILENO)
 198                 goto nocheck;
 199 
 200 #ifdef FIONREAD
 201         if (canbepipe && (ioctl(fd, FIONREAD, &t) == -1 || t == 0)) {
 202 #ifdef FD_ZERO
 203                 ssize_t cnt;
 204                 for (cnt = 0;; cnt++) {
 205                         fd_set check;
 206                         struct timeval tout = {0, 100 * 1000};
 207                         int selrv;
 208 
 209                         FD_ZERO(&check);
 210                         FD_SET(fd, &check);
 211 
 212                         /*
 213                          * Avoid soft deadlock: do not read if there
 214                          * is nothing to read from sockets and pipes.
 215                          */
 216                         selrv = select(fd + 1, &check, NULL, NULL, &tout);
 217                         if (selrv == -1) {
 218                                 if (errno == EINTR || errno == EAGAIN)
 219                                         continue;
 220                         } else if (selrv == 0 && cnt >= 5) {
 221                                 return 0;
 222                         } else
 223                                 break;
 224                 }
 225 #endif
 226                 (void)ioctl(fd, FIONREAD, &t);
 227         }
 228 
 229         if (t > 0 && (size_t)t < n) {
 230                 n = t;
 231                 rn = n;
 232         }
 233 #endif
 234 
 235 nocheck:
 236         do
 237                 switch ((rv = FINFO_READ_FUNC(fd, buf, n))) {
 238                 case -1:
 239                         if (errno == EINTR)
 240                                 continue;
 241                         return -1;
 242                 case 0:
 243                         return rn - n;
 244                 default:
 245                         n -= rv;
 246                         buf = ((char *)buf) + rv;
 247                         break;
 248                 }
 249         while (n > 0);
 250         return rn;
 251 }
 252 
 253 protected int
 254 file_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
 255     size_t nbytes)
 256 {
 257         char buf[4096];
 258         ssize_t r;
 259         int tfd;
 260 
 261         (void)strlcpy(buf, "/tmp/file.XXXXXX", sizeof buf);
 262 #ifndef HAVE_MKSTEMP
 263         {
 264                 char *ptr = mktemp(buf);
 265                 tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
 266                 r = errno;
 267                 (void)unlink(ptr);
 268                 errno = r;
 269         }
 270 #else
 271         {
 272                 int te;
 273                 tfd = mkstemp(buf);
 274                 te = errno;
 275                 (void)unlink(buf);
 276                 errno = te;
 277         }
 278 #endif
 279         if (tfd == -1) {
 280                 file_error(ms, errno,
 281                     "cannot create temporary file for pipe copy");
 282                 return -1;
 283         }
 284 
 285         if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes)
 286                 r = 1;
 287         else {
 288                 while ((r = sread(fd, buf, sizeof(buf), 1)) > 0)
 289                         if (swrite(tfd, buf, (size_t)r) != r)
 290                                 break;
 291         }
 292 
 293         switch (r) {
 294         case -1:
 295                 file_error(ms, errno, "error copying from pipe to temp file");
 296                 return -1;
 297         case 0:
 298                 break;
 299         default:
 300                 file_error(ms, errno, "error while writing to temp file");
 301                 return -1;
 302         }
 303 
 304         /*
 305          * We duplicate the file descriptor, because fclose on a
 306          * tmpfile will delete the file, but any open descriptors
 307          * can still access the phantom inode.
 308          */
 309         if ((fd = dup2(tfd, fd)) == -1) {
 310                 file_error(ms, errno, "could not dup descriptor for temp file");
 311                 return -1;
 312         }
 313         (void)close(tfd);
 314         if (FINFO_LSEEK_FUNC(fd, (zend_off_t)0, SEEK_SET) == (zend_off_t)-1) {
 315                 file_badseek(ms);
 316                 return -1;
 317         }
 318         return fd;
 319 }
 320 
 321 #ifdef PHP_FILEINFO_UNCOMPRESS
 322 #ifdef BUILTIN_DECOMPRESS
 323 
 324 #define FHCRC           (1 << 1)
 325 #define FEXTRA          (1 << 2)
 326 #define FNAME           (1 << 3)
 327 #define FCOMMENT        (1 << 4)
 328 
 329 private size_t
 330 uncompressgzipped(struct magic_set *ms, const unsigned char *old,
 331     unsigned char **newch, size_t n)
 332 {
 333         unsigned char flg = old[3];
 334         size_t data_start = 10;
 335         z_stream z;
 336         int rc;
 337 
 338         if (flg & FEXTRA) {
 339                 if (data_start+1 >= n)
 340                         return 0;
 341                 data_start += 2 + old[data_start] + old[data_start + 1] * 256;
 342         }
 343         if (flg & FNAME) {
 344                 while(data_start < n && old[data_start])
 345                         data_start++;
 346                 data_start++;
 347         }
 348         if(flg & FCOMMENT) {
 349                 while(data_start < n && old[data_start])
 350                         data_start++;
 351                 data_start++;
 352         }
 353         if(flg & FHCRC)
 354                 data_start += 2;
 355 
 356         if (data_start >= n)
 357                 return 0;
 358         if ((*newch = CAST(unsigned char *, emalloc(HOWMANY + 1))) == NULL) {
 359                 return 0;
 360         }
 361         
 362         /* XXX: const castaway, via strchr */
 363         z.next_in = (Bytef *)strchr((const char *)old + data_start,
 364             old[data_start]);
 365         z.avail_in = CAST(uint32_t, (n - data_start));
 366         z.next_out = *newch;
 367         z.avail_out = HOWMANY;
 368         z.zalloc = Z_NULL;
 369         z.zfree = Z_NULL;
 370         z.opaque = Z_NULL;
 371 
 372         /* LINTED bug in header macro */
 373         rc = inflateInit2(&z, -15);
 374         if (rc != Z_OK) {
 375                 file_error(ms, 0, "zlib: %s", z.msg);
 376                 return 0;
 377         }
 378 
 379         rc = inflate(&z, Z_SYNC_FLUSH);
 380         if (rc != Z_OK && rc != Z_STREAM_END) {
 381                 file_error(ms, 0, "zlib: %s", z.msg);
 382                 return 0;
 383         }
 384 
 385         n = (size_t)z.total_out;
 386         (void)inflateEnd(&z);
 387         
 388         /* let's keep the nul-terminate tradition */
 389         (*newch)[n] = '\0';
 390 
 391         return n;
 392 }
 393 #endif
 394 
 395 private size_t
 396 uncompressbuf(struct magic_set *ms, int fd, size_t method,
 397     const unsigned char *old, unsigned char **newch, size_t n)
 398 {
 399         int fdin[2], fdout[2];
 400         int status;
 401         ssize_t r;
 402 
 403 #ifdef BUILTIN_DECOMPRESS
 404         /* FIXME: This doesn't cope with bzip2 */
 405         if (method == 2)
 406                 return uncompressgzipped(ms, old, newch, n);
 407 #endif
 408         (void)fflush(stdout);
 409         (void)fflush(stderr);
 410 
 411         if ((fd != -1 && pipe(fdin) == -1) || pipe(fdout) == -1) {
 412                 file_error(ms, errno, "cannot create pipe");    
 413                 return NODATA;
 414         }
 415         switch (fork()) {
 416         case 0: /* child */
 417                 (void) close(0);
 418                 if (fd != -1) {
 419                     (void) dup(fd);
 420                     (void) FINFO_LSEEK_FUNC(0, (zend_off_t)0, SEEK_SET);
 421                 } else {
 422                     (void) dup(fdin[0]);
 423                     (void) close(fdin[0]);
 424                     (void) close(fdin[1]);
 425                 }
 426 
 427                 (void) close(1);
 428                 (void) dup(fdout[1]);
 429                 (void) close(fdout[0]);
 430                 (void) close(fdout[1]);
 431 #ifndef DEBUG
 432                 if (compr[method].silent)
 433                         (void)close(2);
 434 #endif
 435 
 436                 (void)execvp(compr[method].argv[0],
 437                     (char *const *)(intptr_t)compr[method].argv);
 438 #ifdef DEBUG
 439                 (void)fprintf(stderr, "exec `%s' failed (%s)\n",
 440                     compr[method].argv[0], strerror(errno));
 441 #endif
 442                 exit(1);
 443                 /*NOTREACHED*/
 444         case -1:
 445                 file_error(ms, errno, "could not fork");
 446                 return NODATA;
 447 
 448         default: /* parent */
 449                 (void) close(fdout[1]);
 450                 if (fd == -1) {
 451                         (void) close(fdin[0]);
 452                         /* 
 453                          * fork again, to avoid blocking because both
 454                          * pipes filled
 455                          */
 456                         switch (fork()) {
 457                         case 0: /* child */
 458                                 (void)close(fdout[0]);
 459                                 if (swrite(fdin[1], old, n) != (ssize_t)n) {
 460 #ifdef DEBUG
 461                                         (void)fprintf(stderr,
 462                                             "Write failed (%s)\n",
 463                                             strerror(errno));
 464 #endif
 465                                         exit(1);
 466                                 }
 467                                 exit(0);
 468                                 /*NOTREACHED*/
 469 
 470                         case -1:
 471 #ifdef DEBUG
 472                                 (void)fprintf(stderr, "Fork failed (%s)\n",
 473                                     strerror(errno));
 474 #endif
 475                                 exit(1);
 476                                 /*NOTREACHED*/
 477 
 478                         default:  /* parent */
 479                                 break;
 480                         }
 481                         (void) close(fdin[1]);
 482                         fdin[1] = -1;
 483                 }
 484 
 485                 *newch = (unsigned char *) emalloc(HOWMANY + 1);
 486 
 487                 if ((r = sread(fdout[0], *newch, HOWMANY, 0)) <= 0) {
 488 #ifdef DEBUG
 489                         (void)fprintf(stderr, "Read failed (%s)\n",
 490                             strerror(errno));
 491 #endif
 492                         efree(*newch);
 493                         n = NODATA-;
 494                         *newch = NULL;
 495                         goto err;
 496                 } else {
 497                         n = r;
 498                 }
 499                 /* NUL terminate, as every buffer is handled here. */
 500                 (*newch)[n] = '\0';
 501 err:
 502                 if (fdin[1] != -1)
 503                         (void) close(fdin[1]);
 504                 (void) close(fdout[0]);
 505 #ifdef WNOHANG
 506                 while (waitpid(pid, NULL, WNOHANG) != -1)
 507                         continue;
 508 #else
 509                 (void)wait(NULL);
 510 #endif
 511 
 512                 (void) close(fdin[0]);
 513             
 514                 return n;
 515         }
 516 }
 517 #endif /* if PHP_FILEINFO_UNCOMPRESS */

/* [<][>][^][v][top][bottom][index][help] */