rpm 5.3.12
rpmio/gzdio.c
Go to the documentation of this file.
00001 
00006 #include "system.h"
00007 
00008 #if     defined(NOTYET) || defined(__LCLINT__)
00009 #include <stdbool.h>
00010 #else
00011 typedef enum { true = 1, false = 0 } bool;
00012 #endif
00013 
00014 #include "rpmio_internal.h"
00015 #include <rpmmacro.h>
00016 #include <rpmcb.h>
00017 
00018 #if defined(WITH_ZLIB)
00019 
00020 /*@-noparams@*/
00021 #include <zlib.h>
00022 /*@=noparams@*/
00023 
00024 #include "debug.h"
00025 
00026 /*@access FD_t @*/
00027 
00028 #define GZDONLY(fd)     assert(fdGetIo(fd) == gzdio)
00029 
00030 typedef struct cpio_state_s {
00031     rpmuint32_t n;                      /* byte progress in cpio header */
00032     rpmuint32_t mode;                   /* file attributes */
00033     rpmuint32_t nlnk;
00034     rpmuint32_t size;
00035 } * cpio_state;
00036 
00037 #define RSYNC_WIN 4096
00038 
00039 typedef struct rsync_state_s {
00040     rpmuint32_t n;                      /* number of elements in the window */
00041     rpmuint32_t sum;                    /* current sum */
00042     unsigned char win[RSYNC_WIN];       /* window elements */
00043 } * rsync_state;
00044 
00045 typedef struct rpmGZFILE_s {
00046     gzFile gz;                          /* gzFile is a pointer */
00047     struct rsync_state_s rs;
00048     struct cpio_state_s cs;
00049     rpmuint32_t nb;                     /* bytes pending for sync */
00050 } * rpmGZFILE;                          /* like FILE, to use with star */
00051 
00052 /* Should gzflush be called only after RSYNC_WIN boundaries? */
00053 /*@unchecked@*/
00054 static int enable_rsync = 1;
00055 
00056 /* =============================================================== */
00057 /* from ../lib/cpio.h */
00058 #define CPIO_NEWC_MAGIC "070701"
00059 #define PHYS_HDR_SIZE 110
00060 
00061 #define OFFSET_MODE (sizeof(CPIO_NEWC_MAGIC)-1 + 1*8)
00062 #define OFFSET_NLNK (sizeof(CPIO_NEWC_MAGIC)-1 + 4*8)
00063 #define OFFSET_SIZE (sizeof(CPIO_NEWC_MAGIC)-1 + 6*8)
00064 
00065 static inline
00066 int hex(char c)
00067         /*@*/
00068 {
00069     if (c >= '0' && c <= '9')
00070         return (int)(c - '0');
00071     else if (c >= 'a' && c <= 'f')
00072         return (int)(c - 'a') + 10;
00073     else if (c >= 'A' && c <= 'F')
00074         return (int)(c - 'A') + 10;
00075     return -1;
00076 }
00077 
00078 static inline
00079 bool cpio_next(cpio_state s, unsigned char c)
00080         /*@modifies s @*/
00081 {
00082     if (s->n >= sizeof(CPIO_NEWC_MAGIC)-1) {
00083         int d = hex(c);
00084         if (d < 0) {
00085             s->n = 0;
00086             return false;
00087         }
00088         if (0){} /* indent */
00089         else if (s->n >= OFFSET_MODE && s->n < OFFSET_MODE+8) {
00090             if (s->n == OFFSET_MODE)
00091                 s->mode = 0;
00092             else
00093                 s->mode <<= 4;
00094             s->mode |= d;
00095         }
00096         else if (s->n >= OFFSET_NLNK && s->n < OFFSET_NLNK+8) {
00097             if (s->n == OFFSET_NLNK)
00098                 s->nlnk = 0;
00099             else
00100                 s->nlnk <<= 4;
00101             s->nlnk |= d;
00102         }
00103         else if (s->n >= OFFSET_SIZE && s->n < OFFSET_SIZE+8) {
00104             if (s->n == OFFSET_SIZE)
00105                 s->size = 0;
00106             else
00107                 s->size <<= 4;
00108             s->size |= d;
00109         }
00110         s->n++;
00111         if (s->n >= PHYS_HDR_SIZE) {
00112             s->n = 0;
00113             if (!S_ISREG(s->mode) || s->nlnk != 1)
00114                 /* no file data */
00115                 s->size = 0;
00116             return true;
00117         }
00118     }
00119     else if (CPIO_NEWC_MAGIC[s->n] == c) {
00120         s->n++;
00121     }
00122     else {
00123         s->n = 0;
00124     }
00125     return false;
00126 }
00127 
00128 /* =============================================================== */
00129 static inline
00130 bool rsync_next(rsync_state s, unsigned char c)
00131         /*@modifies s @*/
00132 {
00133     rpmuint32_t i;
00134 
00135     if (s->n < RSYNC_WIN) {             /* not enough elements */
00136         s->sum += (rpmuint32_t)c;       /* update the sum */
00137         s->win[s->n++] = c;             /* remember the element */
00138         return false;                   /* no match */
00139     }
00140     i = s->n++ % RSYNC_WIN;             /* wrap up */
00141     s->sum -= (rpmuint32_t)s->win[i];   /* move the window on */
00142     s->sum += (rpmuint32_t)c;
00143     s->win[i] = c;
00144     if (s->sum % RSYNC_WIN == 0) {      /* match */
00145         s->n = 0;                       /* reset */
00146         s->sum = 0;
00147         return true;
00148     }
00149     return false;
00150 }
00151 
00152 #define CHUNK 4096
00153 
00154 static inline
00155 bool sync_hint(rpmGZFILE rpmgz, unsigned char c)
00156         /*@modifies rpmgz @*/
00157 {
00158     bool cpio_hint;
00159     bool rsync_hint;
00160 
00161     rpmgz->nb++;
00162     cpio_hint = cpio_next(&rpmgz->cs, c);
00163     if (cpio_hint) {
00164         /* cpio header/data boundary */
00165         rpmgz->rs.n = rpmgz->rs.sum = 0;
00166         if (rpmgz->nb >= 2*CHUNK)
00167             /* better sync here */
00168             goto cpio_sync;
00169         if (rpmgz->cs.size < CHUNK)
00170             /* file is too small */
00171             return false;
00172         if (rpmgz->nb < CHUNK/2)
00173             /* not enough pending bytes */
00174             return false;
00175     cpio_sync:
00176         rpmgz->nb = 0;
00177         return true;
00178     }
00179     rsync_hint = rsync_next(&rpmgz->rs, c);
00180     if (rsync_hint) {
00181         /* rolling checksum match */
00182         assert(rpmgz->nb >= RSYNC_WIN);
00183         rpmgz->nb = 0;
00184         return true;
00185     }
00186     return false;
00187 }
00188 
00189 static ssize_t
00190 rsyncable_gzwrite(rpmGZFILE rpmgz, const unsigned char *const buf, const size_t len)
00191         /*@modifies rpmgz @*/
00192 {
00193     ssize_t rc;
00194     size_t n;
00195     ssize_t n_written = 0;
00196     const unsigned char *begin = buf;
00197     size_t i;
00198 
00199     for (i = 0; i < len; i++) {
00200         if (!sync_hint(rpmgz, buf[i]))
00201             continue;
00202         n = i + 1 - (begin - buf);
00203         rc = gzwrite(rpmgz->gz, begin, (unsigned)n);
00204         if (rc < 0)
00205             return (n_written ? n_written : rc);
00206         n_written += rc;
00207         if (rc < (ssize_t)n)
00208             return n_written;
00209         begin += n;
00210         rc = gzflush(rpmgz->gz, Z_SYNC_FLUSH);
00211         if (rc < 0)
00212             return (n_written ? n_written : rc);
00213     }
00214     if (begin < buf + len) {
00215         n = len - (begin - buf);
00216         rc = gzwrite(rpmgz->gz, begin, (unsigned)n);
00217         if (rc < 0)
00218             return (n_written ? n_written : rc);
00219         n_written += rc;
00220     }
00221     return n_written;
00222 }
00223 
00224 /* =============================================================== */
00225 /*@-moduncon@*/
00226 
00227 static inline /*@dependent@*/ /*@null@*/ void * gzdFileno(FD_t fd)
00228         /*@*/
00229 {
00230     void * rc = NULL;
00231     int i;
00232 
00233     FDSANE(fd);
00234     for (i = fd->nfps; i >= 0; i--) {
00235         FDSTACK_t * fps = &fd->fps[i];
00236         if (fps->io != gzdio)
00237             continue;
00238         rc = fps->fp;
00239         break;
00240     }
00241 
00242     return rc;
00243 }
00244 
00245 static /*@null@*/
00246 FD_t gzdOpen(const char * path, const char * fmode)
00247         /*@globals fileSystem, internalState @*/
00248         /*@modifies fileSystem, internalState @*/
00249 {
00250     FD_t fd;
00251     rpmGZFILE rpmgz;
00252     mode_t mode = (fmode && fmode[0] == 'w' ? O_WRONLY : O_RDONLY);
00253 
00254     rpmgz = xcalloc(1, sizeof(*rpmgz));
00255     rpmgz->gz = gzopen(path, fmode);
00256     if (rpmgz->gz == NULL) {
00257         rpmgz = _free(rpmgz);
00258         return NULL;
00259     }
00260     fd = fdNew("open (gzdOpen)");
00261     fdPop(fd); fdPush(fd, gzdio, rpmgz, -1);
00262     fdSetOpen(fd, path, -1, mode);
00263 
00264 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
00265     return fdLink(fd, "gzdOpen");
00266 }
00267 
00268 static /*@null@*/ FD_t gzdFdopen(void * cookie, const char *fmode)
00269         /*@globals fileSystem, internalState @*/
00270         /*@modifies fileSystem, internalState @*/
00271 {
00272     FD_t fd = c2f(cookie);
00273     int fdno;
00274     rpmGZFILE rpmgz;
00275 
00276     if (fmode == NULL) return NULL;
00277     fdno = fdFileno(fd);
00278     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
00279     if (fdno < 0) return NULL;
00280     rpmgz = xcalloc(1, sizeof(*rpmgz));
00281     rpmgz->gz = gzdopen(fdno, fmode);
00282     if (rpmgz->gz == NULL) {
00283         rpmgz = _free(rpmgz);
00284         return NULL;
00285     }
00286 
00287     fdPush(fd, gzdio, rpmgz, fdno);             /* Push gzdio onto stack */
00288 
00289     return fdLink(fd, "gzdFdopen");
00290 }
00291 
00292 static int gzdFlush(void * cookie)
00293         /*@*/
00294 {
00295     FD_t fd = c2f(cookie);
00296     rpmGZFILE rpmgz;
00297     rpmgz = gzdFileno(fd);
00298     if (rpmgz == NULL) return -2;
00299     return gzflush(rpmgz->gz, Z_SYNC_FLUSH);    /* XXX W2DO? */
00300 }
00301 
00302 /* =============================================================== */
00303 /*@-mustmod@*/
00304 static ssize_t gzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
00305         /*@globals fileSystem, internalState @*/
00306         /*@modifies buf, fileSystem, internalState @*/
00307 {
00308     FD_t fd = c2f(cookie);
00309     rpmGZFILE rpmgz;
00310     ssize_t rc;
00311 
00312     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
00313 
00314     rpmgz = gzdFileno(fd);
00315     if (rpmgz == NULL) return -2;       /* XXX can't happen */
00316 
00317     fdstat_enter(fd, FDSTAT_READ);
00318     rc = gzread(rpmgz->gz, buf, (unsigned)count);
00319 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
00320     if (rc < 0) {
00321         int zerror = 0;
00322         fd->errcookie = gzerror(rpmgz->gz, &zerror);
00323         if (zerror == Z_ERRNO) {
00324             fd->syserrno = errno;
00325             fd->errcookie = strerror(fd->syserrno);
00326         }
00327     } else {
00328         fdstat_exit(fd, FDSTAT_READ, (rc > 0 ? rc : 0));
00329         if (fd->ndigests > 0 && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
00330     }
00331     return rc;
00332 }
00333 /*@=mustmod@*/
00334 
00335 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
00336         /*@globals fileSystem, internalState @*/
00337         /*@modifies fileSystem, internalState @*/
00338 {
00339     FD_t fd = c2f(cookie);
00340     rpmGZFILE rpmgz;
00341     ssize_t rc;
00342 
00343     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
00344 
00345     if (fd->ndigests > 0 && count > 0) fdUpdateDigests(fd, (void *)buf, count);
00346 
00347     rpmgz = gzdFileno(fd);
00348     if (rpmgz == NULL) return -2;       /* XXX can't happen */
00349 
00350     fdstat_enter(fd, FDSTAT_WRITE);
00351     if (enable_rsync)
00352         rc = rsyncable_gzwrite(rpmgz, (void *)buf, (unsigned)count);
00353     else
00354         rc = gzwrite(rpmgz->gz, (void *)buf, (unsigned)count);
00355 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
00356     if (rc < (ssize_t)count) {
00357         int zerror = 0;
00358         fd->errcookie = gzerror(rpmgz->gz, &zerror);
00359         if (zerror == Z_ERRNO) {
00360             fd->syserrno = errno;
00361             fd->errcookie = strerror(fd->syserrno);
00362         }
00363     }
00364     if (rc > 0)
00365         fdstat_exit(fd, FDSTAT_WRITE, rc);
00366     return rc;
00367 }
00368 
00369 /* XXX zlib-1.0.4 has not */
00370 #define HAVE_GZSEEK     /* XXX autoFu doesn't set this anymore. */
00371 static int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
00372         /*@globals fileSystem, internalState @*/
00373         /*@modifies fileSystem, internalState @*/
00374 {
00375     int rc;
00376 #if defined(HAVE_GZSEEK)
00377 #ifdef USE_COOKIE_SEEK_POINTER
00378     _IO_off64_t p = *pos;
00379 #else
00380     off_t p = pos;
00381 #endif
00382     FD_t fd = c2f(cookie);
00383     rpmGZFILE rpmgz;
00384 
00385     if (fd == NULL) return -2;
00386     assert(fd->bytesRemain == -1);      /* XXX FIXME */
00387 
00388     rpmgz = gzdFileno(fd);
00389     if (rpmgz == NULL) return -2;       /* XXX can't happen */
00390 
00391     fdstat_enter(fd, FDSTAT_SEEK);
00392     rc = gzseek(rpmgz->gz, (long)p, whence);
00393 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
00394     if (rc < 0) {
00395         int zerror = 0;
00396         fd->errcookie = gzerror(rpmgz->gz, &zerror);
00397         if (zerror == Z_ERRNO) {
00398             fd->syserrno = errno;
00399             fd->errcookie = strerror(fd->syserrno);
00400         }
00401     }
00402     if (rc > 0)
00403         fdstat_exit(fd, FDSTAT_SEEK, rc);
00404 #else
00405     rc = -2;
00406 #endif
00407     return rc;
00408 }
00409 
00410 static int gzdClose( /*@only@*/ void * cookie)
00411         /*@globals fileSystem, internalState @*/
00412         /*@modifies fileSystem, internalState @*/
00413 {
00414     FD_t fd = c2f(cookie);
00415     rpmGZFILE rpmgz;
00416     int rc;
00417 
00418     rpmgz = gzdFileno(fd);
00419     if (rpmgz == NULL) return -2;       /* XXX can't happen */
00420 
00421     fdstat_enter(fd, FDSTAT_CLOSE);
00422     /*@-dependenttrans@*/
00423     rc = gzclose(rpmgz->gz);
00424     /*@=dependenttrans@*/
00425     rpmgz->gz = NULL;
00426 /*@-dependenttrans@*/
00427     rpmgz = _free(rpmgz);
00428 /*@=dependenttrans@*/
00429 
00430     /* XXX TODO: preserve fd if errors */
00431 
00432     if (fd) {
00433 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
00434         if (rc < 0) {
00435             fd->errcookie = "gzclose error";
00436             if (rc == Z_ERRNO) {
00437                 fd->syserrno = errno;
00438                 fd->errcookie = strerror(fd->syserrno);
00439             }
00440         } else if (rc >= 0) {
00441             fdstat_exit(fd, FDSTAT_CLOSE, rc);
00442         }
00443     }
00444 
00445 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
00446 
00447     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
00448     if (rc == 0)
00449         fd = fdFree(fd, "open (gzdClose)");
00450     return rc;
00451 }
00452 
00453 /*@-type@*/ /* LCL: function typedefs */
00454 static struct FDIO_s gzdio_s = {
00455   gzdRead, gzdWrite, gzdSeek, gzdClose, gzdOpen, gzdFdopen, gzdFlush,
00456 };
00457 /*@=type@*/
00458 
00459 FDIO_t gzdio = /*@-compmempass@*/ &gzdio_s /*@=compmempass@*/ ;
00460 
00461 /*@=moduncon@*/
00462 #endif  /* WITH_ZLIB */