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
00021 #include <zlib.h>
00022
00023
00024 #include "debug.h"
00025
00026
00027
00028 #define GZDONLY(fd) assert(fdGetIo(fd) == gzdio)
00029
00030 typedef struct cpio_state_s {
00031 rpmuint32_t n;
00032 rpmuint32_t mode;
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;
00041 rpmuint32_t sum;
00042 unsigned char win[RSYNC_WIN];
00043 } * rsync_state;
00044
00045 typedef struct rpmGZFILE_s {
00046 gzFile gz;
00047 struct rsync_state_s rs;
00048 struct cpio_state_s cs;
00049 rpmuint32_t nb;
00050 } * rpmGZFILE;
00051
00052
00053
00054 static int enable_rsync = 1;
00055
00056
00057
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
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){}
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
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
00132 {
00133 rpmuint32_t i;
00134
00135 if (s->n < RSYNC_WIN) {
00136 s->sum += (rpmuint32_t)c;
00137 s->win[s->n++] = c;
00138 return false;
00139 }
00140 i = s->n++ % RSYNC_WIN;
00141 s->sum -= (rpmuint32_t)s->win[i];
00142 s->sum += (rpmuint32_t)c;
00143 s->win[i] = c;
00144 if (s->sum % RSYNC_WIN == 0) {
00145 s->n = 0;
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
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
00165 rpmgz->rs.n = rpmgz->rs.sum = 0;
00166 if (rpmgz->nb >= 2*CHUNK)
00167
00168 goto cpio_sync;
00169 if (rpmgz->cs.size < CHUNK)
00170
00171 return false;
00172 if (rpmgz->nb < CHUNK/2)
00173
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
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
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
00226
00227 static inline 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
00246 FD_t gzdOpen(const char * path, const char * fmode)
00247
00248
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 FD_t gzdFdopen(void * cookie, const char *fmode)
00269
00270
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);
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);
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);
00300 }
00301
00302
00303
00304 static ssize_t gzdRead(void * cookie, char * buf, size_t count)
00305
00306
00307 {
00308 FD_t fd = c2f(cookie);
00309 rpmGZFILE rpmgz;
00310 ssize_t rc;
00311
00312 if (fd == NULL || fd->bytesRemain == 0) return 0;
00313
00314 rpmgz = gzdFileno(fd);
00315 if (rpmgz == NULL) return -2;
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 && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
00330 }
00331 return rc;
00332 }
00333
00334
00335 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
00336
00337
00338 {
00339 FD_t fd = c2f(cookie);
00340 rpmGZFILE rpmgz;
00341 ssize_t rc;
00342
00343 if (fd == NULL || fd->bytesRemain == 0) return 0;
00344
00345 if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
00346
00347 rpmgz = gzdFileno(fd);
00348 if (rpmgz == NULL) return -2;
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
00370 #define HAVE_GZSEEK
00371 static inline int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
00372
00373
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);
00387
00388 rpmgz = gzdFileno(fd);
00389 if (rpmgz == NULL) return -2;
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( void * cookie)
00411
00412
00413 {
00414 FD_t fd = c2f(cookie);
00415 rpmGZFILE rpmgz;
00416 int rc;
00417
00418 rpmgz = gzdFileno(fd);
00419 if (rpmgz == NULL) return -2;
00420
00421 fdstat_enter(fd, FDSTAT_CLOSE);
00422
00423 rc = gzclose(rpmgz->gz);
00424
00425 rpmgz->gz = NULL;
00426
00427 rpmgz = _free(rpmgz);
00428
00429
00430
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
00454 static struct FDIO_s gzdio_s = {
00455 gzdRead, gzdWrite, gzdSeek, gzdClose, gzdOpen, gzdFdopen, gzdFlush,
00456 };
00457
00458
00459 FDIO_t gzdio = &gzdio_s ;
00460
00461
00462 #endif