file/src/compress.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) Ian F. Darwin 1986-1995.
00003  * Software written by Ian F. Darwin and others;
00004  * maintained 1995-present by Christos Zoulas and others.
00005  * 
00006  * Redistribution and use in source and binary forms, with or without
00007  * modification, are permitted provided that the following conditions
00008  * are met:
00009  * 1. Redistributions of source code must retain the above copyright
00010  *    notice immediately at the beginning of the file, without modification,
00011  *    this list of conditions, and the following disclaimer.
00012  * 2. Redistributions in binary form must reproduce the above copyright
00013  *    notice, this list of conditions and the following disclaimer in the
00014  *    documentation and/or other materials provided with the distribution.
00015  *  
00016  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
00017  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00018  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00019  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
00020  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00021  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00022  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00023  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00024  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00025  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00026  * SUCH DAMAGE.
00027  */
00028 /*
00029  * compress routines:
00030  *      zmagic() - returns 0 if not recognized, uncompresses and prints
00031  *                 information if recognized
00032  *      uncompress(method, old, n, newch) - uncompress old into new, 
00033  *                                          using method, return sizeof new
00034  */
00035 #include "file.h"
00036 #include "magic.h"
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #ifdef HAVE_UNISTD_H
00040 #include <unistd.h>
00041 #endif
00042 #include <string.h>
00043 #include <errno.h>
00044 #include <sys/types.h>
00045 #ifdef HAVE_SYS_WAIT_H
00046 #include <sys/wait.h>
00047 #endif
00048 #ifdef HAVE_LIBZ
00049 #include <zlib.h>
00050 #endif
00051 
00052 #ifndef lint
00053 FILE_RCSID("@(#)$Id: compress.c,v 1.42 2005/03/06 05:58:22 christos Exp $")
00054 #endif
00055 
00056 
00057 /*@-nullassign@*/
00058 /*@unchecked@*/ /*@observer@*/
00059 private struct {
00060 /*@observer@*/
00061         const char *magic;
00062         size_t maglen;
00063 /*@observer@*/
00064         const char *const argv[3];
00065         int silent;
00066 } compr[] = {
00067         { "\037\235", 2, { "gzip", "-cdq", NULL }, 1 },         /* compressed */
00068         /* Uncompress can get stuck; so use gzip first if we have it
00069          * Idea from Damien Clark, thanks! */
00070         { "\037\235", 2, { "uncompress", "-c", NULL }, 1 },     /* compressed */
00071         { "\037\213", 2, { "gzip", "-cdq", NULL }, 1 },         /* gzipped */
00072         { "\037\236", 2, { "gzip", "-cdq", NULL }, 1 },         /* frozen */
00073         { "\037\240", 2, { "gzip", "-cdq", NULL }, 1 },         /* SCO LZH */
00074         /* the standard pack utilities do not accept standard input */
00075         { "\037\036", 2, { "gzip", "-cdq", NULL }, 0 },         /* packed */
00076         { "PK\3\4",   4, { "gzip", "-cdq", NULL }, 1 },         /* pkzipped, */
00077                                             /* ...only first file examined */
00078         { "BZh",      3, { "bzip2", "-cd", NULL }, 1 },         /* bzip2-ed */
00079 };
00080 /*@=nullassign@*/
00081 
00082 /*@unchecked@*/
00083 private int ncompr = sizeof(compr) / sizeof(compr[0]);
00084 
00085 
00086 private ssize_t swrite(int fd, const void *buf, size_t n)
00087         /*@*/;
00088 private ssize_t sread(int fd, void *buf, size_t n)
00089         /*@modifies buf @*/;
00090 private size_t uncompressbuf(struct magic_set *ms, int fd, size_t method,
00091     const unsigned char *old, unsigned char **newch, size_t n)
00092         /*@globals fileSystem, internalState @*/
00093         /*@modifies ms, *newch, fileSystem, internalState @*/;
00094 #ifdef HAVE_LIBZ
00095 private size_t uncompressgzipped(struct magic_set *ms, const unsigned char *old,
00096     unsigned char **newch, size_t n)
00097         /*@modifies ms, *newch @*/;
00098 #endif
00099 
00100 protected int
00101 file_zmagic(struct magic_set *ms, int fd, const unsigned char *buf,
00102     size_t nbytes)
00103 {
00104         unsigned char *newbuf = NULL;
00105         size_t i, nsz;
00106         int rv = 0;
00107 
00108         if ((ms->flags & MAGIC_COMPRESS) == 0)
00109                 return 0;
00110 
00111         for (i = 0; i < ncompr; i++) {
00112                 if (nbytes < compr[i].maglen)
00113                         continue;
00114                 if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 &&
00115                     (nsz = uncompressbuf(ms, fd, i, buf, &newbuf,
00116                     nbytes)) != 0) {
00117                         ms->flags &= ~MAGIC_COMPRESS;
00118                         rv = -1;
00119                         if (file_buffer(ms, -1, newbuf, nsz) == -1)
00120                                 goto error;
00121                         if (file_printf(ms, " (") == -1)
00122                                 goto error;
00123                         if (file_buffer(ms, -1, buf, nbytes) == -1)
00124                                 goto error;
00125                         if (file_printf(ms, ")") == -1)
00126                                 goto error;
00127                         rv = 1;
00128                         break;
00129                 }
00130         }
00131 error:
00132         if (newbuf)
00133                 free(newbuf);
00134         ms->flags |= MAGIC_COMPRESS;
00135         return rv;
00136 }
00137 
00138 /*
00139  * `safe' write for sockets and pipes.
00140  */
00141 private ssize_t
00142 swrite(int fd, const void *buf, size_t n)
00143 {
00144         int rv;
00145         size_t rn = n;
00146 
00147         do
00148                 switch (rv = write(fd, buf, n)) {
00149                 case -1:
00150                         if (errno == EINTR)
00151                                 continue;
00152                         return -1;
00153                 default:
00154                         n -= rv;
00155                         buf = ((const char *)buf) + rv;
00156                         /*@switchbreak@*/ break;
00157                 }
00158         while (n > 0);
00159         return rn;
00160 }
00161 
00162 
00163 /*
00164  * `safe' read for sockets and pipes.
00165  */
00166 private ssize_t
00167 sread(int fd, void *buf, size_t n)
00168 {
00169         int rv;
00170         size_t rn = n;
00171 
00172         do
00173                 switch (rv = read(fd, buf, n)) {
00174                 case -1:
00175                         if (errno == EINTR)
00176                                 continue;
00177                         return -1;
00178                 case 0:
00179                         return rn - n;
00180                 default:
00181                         n -= rv;
00182                         buf = ((char *)buf) + rv;
00183                         /*@switchbreak@*/ break;
00184                 }
00185         while (n > 0);
00186         return rn;
00187 }
00188 
00189 protected int
00190 file_pipe2file(struct magic_set *ms, int fd, const void *startbuf,
00191     size_t nbytes)
00192 {
00193         char buf[4096];
00194         int r, tfd;
00195 
00196         (void)strcpy(buf, "/tmp/file.XXXXXX");
00197 #ifndef HAVE_MKSTEMP
00198         {
00199                 char *ptr = mktemp(buf);
00200                 tfd = open(ptr, O_RDWR|O_TRUNC|O_EXCL|O_CREAT, 0600);
00201                 r = errno;
00202                 (void)unlink(ptr);
00203                 errno = r;
00204         }
00205 #else
00206         tfd = mkstemp(buf);
00207         r = errno;
00208         (void)unlink(buf);
00209         errno = r;
00210 #endif
00211         if (tfd == -1) {
00212                 file_error(ms, errno,
00213                     "cannot create temporary file for pipe copy");
00214                 return -1;
00215         }
00216 
00217         if (swrite(tfd, startbuf, nbytes) != (ssize_t)nbytes)
00218                 r = 1;
00219         else {
00220                 while ((r = sread(fd, buf, sizeof(buf))) > 0)
00221                         if (swrite(tfd, buf, (size_t)r) != r)
00222                                 break;
00223         }
00224 
00225         switch (r) {
00226         case -1:
00227                 file_error(ms, errno, "error copying from pipe to temp file");
00228                 return -1;
00229         case 0:
00230                 break;
00231         default:
00232                 file_error(ms, errno, "error while writing to temp file");
00233                 return -1;
00234         }
00235 
00236         /*
00237          * We duplicate the file descriptor, because fclose on a
00238          * tmpfile will delete the file, but any open descriptors
00239          * can still access the phantom inode.
00240          */
00241         if ((fd = dup2(tfd, fd)) == -1) {
00242                 file_error(ms, errno, "could not dup descriptor for temp file");
00243                 return -1;
00244         }
00245         (void)close(tfd);
00246         if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
00247                 file_badseek(ms);
00248                 return -1;
00249         }
00250         return fd;
00251 }
00252 
00253 #ifdef HAVE_LIBZ
00254 
00255 #define FHCRC           (1 << 1)
00256 #define FEXTRA          (1 << 2)
00257 #define FNAME           (1 << 3)
00258 #define FCOMMENT        (1 << 4)
00259 
00260 private size_t
00261 uncompressgzipped(struct magic_set *ms, const unsigned char *old,
00262     unsigned char **newch, size_t n)
00263 {
00264         unsigned char flg = old[3];
00265         size_t data_start = 10;
00266         z_stream z;
00267         int rc;
00268 
00269         if (flg & FEXTRA) {
00270                 if (data_start+1 >= n)
00271                         return 0;
00272                 data_start += 2 + old[data_start] + old[data_start + 1] * 256;
00273         }
00274         if (flg & FNAME) {
00275                 while(data_start < n && old[data_start])
00276                         data_start++;
00277                 data_start++;
00278         }
00279         if(flg & FCOMMENT) {
00280                 while(data_start < n && old[data_start])
00281                         data_start++;
00282                 data_start++;
00283         }
00284         if(flg & FHCRC)
00285                 data_start += 2;
00286 
00287         if (data_start >= n)
00288                 return 0;
00289         if ((*newch = (unsigned char *)malloc(HOWMANY + 1)) == NULL) {
00290                 return 0;
00291         }
00292         
00293         /* XXX: const castaway, via strchr */
00294         z.next_in = (Bytef *)strchr((const char *)old + data_start,
00295             old[data_start]);
00296         z.avail_in = n - data_start;
00297         z.next_out = *newch;
00298         z.avail_out = HOWMANY;
00299         z.zalloc = Z_NULL;
00300         z.zfree = Z_NULL;
00301         z.opaque = Z_NULL;
00302 
00303         rc = inflateInit2(&z, -15);
00304         if (rc != Z_OK) {
00305                 file_error(ms, 0, "zlib: %s", z.msg);
00306                 return 0;
00307         }
00308 
00309         rc = inflate(&z, Z_SYNC_FLUSH);
00310         if (rc != Z_OK && rc != Z_STREAM_END) {
00311                 file_error(ms, 0, "zlib: %s", z.msg);
00312                 return 0;
00313         }
00314 
00315         n = (size_t)z.total_out;
00316         inflateEnd(&z);
00317         
00318         /* let's keep the nul-terminate tradition */
00319         (*newch)[n++] = '\0';
00320 
00321         return n;
00322 }
00323 #endif
00324 
00325 private size_t
00326 uncompressbuf(struct magic_set *ms, int fd, size_t method,
00327     const unsigned char *old, unsigned char **newch, size_t n)
00328 {
00329         int fdin[2], fdout[2];
00330         int r;
00331         pid_t pid1, pid2;
00332 
00333 #ifdef HAVE_LIBZ
00334         if (method == 2)
00335                 return uncompressgzipped(ms, old, newch, n);
00336 #endif
00337         (void)fflush(stdout);
00338         (void)fflush(stderr);
00339 
00340         if ((fd != -1 && pipe(fdin) == -1) || pipe(fdout) == -1) {
00341                 file_error(ms, errno, "cannot create pipe");    
00342                 return 0;
00343         }
00344         pid2 = (pid_t)-1;
00345         switch ((pid1=fork())) {
00346         case 0: /* child */
00347                 (void) close(0);
00348                 if (fd != -1) {
00349                     (void) dup(fd);
00350                     (void) lseek(0, (off_t)0, SEEK_SET);
00351                 } else {
00352                     (void) dup(fdin[0]);
00353                     (void) close(fdin[0]);
00354                     (void) close(fdin[1]);
00355                 }
00356 
00357                 (void) close(1);
00358                 (void) dup(fdout[1]);
00359                 (void) close(fdout[0]);
00360                 (void) close(fdout[1]);
00361 #ifndef DEBUG
00362                 if (compr[method].silent)
00363                         (void)close(2);
00364 #endif
00365 
00366                 execvp(compr[method].argv[0],
00367                        (char *const *)(intptr_t)compr[method].argv);
00368 #ifdef DEBUG
00369                 (void)fprintf(stderr, "exec `%s' failed (%s)\n",
00370                     compr[method].argv[0], strerror(errno));
00371 #endif
00372                 exit(EXIT_FAILURE);
00373                 /*@notreached@*/ break;
00374         case -1:
00375                 file_error(ms, errno, "could not fork");
00376                 return 0;
00377 
00378         default: /* parent */
00379                 (void) close(fdout[1]);
00380                 if (fd == -1) {
00381                         (void) close(fdin[0]);
00382                         /* 
00383                          * fork again, to avoid blocking because both
00384                          * pipes filled
00385                          */
00386                         switch ((pid2 = fork())) {
00387                         case 0: /* child */
00388                                 (void)close(fdout[0]);
00389                                 if (swrite(fdin[1], old, n) != n) {
00390 #ifdef DEBUG
00391                                         (void)fprintf(stderr,
00392                                             "Write failed (%s)\n",
00393                                             strerror(errno));
00394 #endif
00395                                         exit(EXIT_FAILURE);
00396                                         /*@notreached@*/ /*@innerbreak@*/ break;
00397                                 }
00398                                 exit(EXIT_SUCCESS);
00399                                 /*@notreached@*/ /*@innerbreak@*/ break;
00400 
00401                         case -1:
00402 #ifdef DEBUG
00403                                 (void)fprintf(stderr, "Fork failed (%s)\n",
00404                                     strerror(errno));
00405 #endif
00406                                 exit(EXIT_FAILURE);
00407                                 /*@notreached@*/ /*@innerbreak@*/ break;
00408 
00409                         default:  /* parent */
00410                                 /*@innerbreak@*/ break;
00411                         }
00412                         (void) close(fdin[1]);
00413                         fdin[1] = -1;
00414                 }
00415 
00416                 if ((*newch = (unsigned char *) malloc(HOWMANY + 1)) == NULL) {
00417 #ifdef DEBUG
00418                         (void)fprintf(stderr, "Malloc failed (%s)\n",
00419                             strerror(errno));
00420 #endif
00421                         n = 0;
00422                         goto err;
00423                 }
00424                 if ((r = sread(fdout[0], *newch, HOWMANY)) <= 0) {
00425 #ifdef DEBUG
00426                         (void)fprintf(stderr, "Read failed (%s)\n",
00427                             strerror(errno));
00428 #endif
00429                         free(*newch);
00430                         n = 0;
00431                         newch[0] = '\0';
00432                         goto err;
00433                 } else {
00434                         n = r;
00435                 }
00436                 /* NUL terminate, as every buffer is handled here. */
00437                 (*newch)[n++] = '\0';
00438 err:
00439                 if (fdin[1] != -1)
00440                         (void) close(fdin[1]);
00441                 (void) close(fdout[0]);
00442                 waitpid(pid1, NULL, 0);
00443                 if (pid2 != (pid_t)-1)
00444                         waitpid(pid2, NULL, 0);
00445                 return n;
00446         }
00447         /*@notreached@*/
00448 }

Generated on Fri Oct 12 08:44:53 2007 for rpm by  doxygen 1.5.2