rpmio/rpmio.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 #include <stdarg.h>
00007 
00008 #if HAVE_MACHINE_TYPES_H
00009 # include <machine/types.h>
00010 #endif
00011 
00012 #if HAVE_SYS_SOCKET_H
00013 # include <sys/socket.h>
00014 #endif
00015 
00016 #include <netinet/in.h>
00017 #include <arpa/inet.h>          /* XXX for inet_aton and HP-UX */
00018 
00019 #if HAVE_NETINET_IN_SYSTM_H
00020 # include <sys/types.h>
00021 # include <netinet/in_systm.h>
00022 #endif
00023 
00024 #if HAVE_LIBIO_H && defined(_G_IO_IO_FILE_VERSION)
00025 #define _USE_LIBIO      1
00026 #endif
00027 
00028 /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
00029 #if !defined(HAVE_HERRNO) && (defined(__hpux) || defined(__LCLINT__))
00030 /*@unchecked@*/
00031 extern int h_errno;
00032 #endif
00033 
00034 #ifndef IPPORT_FTP
00035 #define IPPORT_FTP      21
00036 #endif
00037 #ifndef IPPORT_HTTP
00038 #define IPPORT_HTTP     80
00039 #endif
00040 
00041 #if !defined(HAVE_INET_ATON)
00042 static int inet_aton(const char *cp, struct in_addr *inp)
00043         /*@modifies *inp @*/
00044 {
00045     long addr;
00046 
00047     addr = inet_addr(cp);
00048     if (addr == ((long) -1)) return 0;
00049 
00050     memcpy(inp, &addr, sizeof(addr));
00051     return 1;
00052 }
00053 #endif
00054 
00055 #if defined(USE_ALT_DNS) && USE_ALT_DNS
00056 #include "dns.h"
00057 #endif
00058 
00059 #include <rpmio_internal.h>
00060 #undef  fdFileno
00061 #undef  fdOpen
00062 #define fdOpen  __fdOpen
00063 #undef  fdRead
00064 #define fdRead  __fdRead
00065 #undef  fdWrite
00066 #define fdWrite __fdWrite
00067 #undef  fdClose
00068 #define fdClose __fdClose
00069 
00070 #include <rpmdav.h>
00071 #include "ugid.h"
00072 #include "rpmmessages.h"
00073 
00074 #include "debug.h"
00075 
00076 /*@access FILE @*/      /* XXX to permit comparison/conversion with void *. */
00077 /*@access urlinfo @*/
00078 /*@access FDSTAT_t @*/
00079 
00080 #define FDNREFS(fd)     (fd ? ((FD_t)fd)->nrefs : -9)
00081 #define FDTO(fd)        (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
00082 #define FDCPIOPOS(fd)   (fd ? ((FD_t)fd)->fd_cpioPos : -99)
00083 
00084 #define FDONLY(fd)      assert(fdGetIo(fd) == fdio)
00085 #define GZDONLY(fd)     assert(fdGetIo(fd) == gzdio)
00086 #define BZDONLY(fd)     assert(fdGetIo(fd) == bzdio)
00087 #define LZDONLY(fd)     assert(fdGetIo(fd) == lzdio)
00088 
00089 #define UFDONLY(fd)     /* assert(fdGetIo(fd) == ufdio) */
00090 
00091 #define fdGetFILE(_fd)  ((FILE *)fdGetFp(_fd))
00092 
00095 /*@unchecked@*/
00096 #if _USE_LIBIO
00097 int noLibio = 0;
00098 #else
00099 int noLibio = 1;
00100 #endif
00101 
00102 #define TIMEOUT_SECS 60
00103 
00106 /*@unchecked@*/
00107 static int ftpTimeoutSecs = TIMEOUT_SECS;
00108 
00111 /*@unchecked@*/
00112 static int httpTimeoutSecs = TIMEOUT_SECS;
00113 
00116 /*@unchecked@*/
00117 int _rpmio_debug = 0;
00118 
00121 /*@unchecked@*/
00122 int _av_debug = 0;
00123 
00126 /*@unchecked@*/
00127 int _ftp_debug = 0;
00128 
00131 /*@unchecked@*/
00132 int _dav_debug = 0;
00133 
00139 /*@unused@*/ static inline /*@null@*/ void *
00140 _free(/*@only@*/ /*@null@*/ /*@out@*/ const void * p)
00141         /*@modifies p@*/
00142 {
00143     if (p != NULL)      free((void *)p);
00144     return NULL;
00145 }
00146 
00147 /* =============================================================== */
00148 
00149 /*@-boundswrite@*/
00150 static /*@observer@*/ const char * fdbg(/*@null@*/ FD_t fd)
00151         /*@*/
00152 {
00153     static char buf[BUFSIZ];
00154     char *be = buf;
00155     int i;
00156 
00157     buf[0] = '\0';
00158     if (fd == NULL)
00159         return buf;
00160 
00161 #ifdef DYING
00162     sprintf(be, "fd %p", fd);   be += strlen(be);
00163     if (fd->rd_timeoutsecs >= 0) {
00164         sprintf(be, " secs %d", fd->rd_timeoutsecs);
00165         be += strlen(be);
00166     }
00167 #endif
00168     if (fd->bytesRemain != -1) {
00169         sprintf(be, " clen %d", (int)fd->bytesRemain);
00170         be += strlen(be);
00171      }
00172     if (fd->wr_chunked) {
00173         strcpy(be, " chunked");
00174         be += strlen(be);
00175      }
00176     *be++ = '\t';
00177     for (i = fd->nfps; i >= 0; i--) {
00178         FDSTACK_t * fps = &fd->fps[i];
00179         if (i != fd->nfps)
00180             *be++ = ' ';
00181         *be++ = '|';
00182         *be++ = ' ';
00183         if (fps->io == fdio) {
00184             sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
00185         } else if (fps->io == ufdio) {
00186             sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
00187         } else if (fps->io == gzdio) {
00188             sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
00189 #if HAVE_BZLIB_H
00190         } else if (fps->io == bzdio) {
00191             sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
00192 #endif
00193         } else if (fps->io == lzdio) {
00194             sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno);
00195         } else if (fps->io == fpio) {
00196             /*@+voidabstract@*/
00197             sprintf(be, "%s %p(%d) fdno %d",
00198                 (fps->fdno < 0 ? "LIBIO" : "FP"),
00199                 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
00200             /*@=voidabstract@*/
00201         } else {
00202             sprintf(be, "??? io %p fp %p fdno %d ???",
00203                 fps->io, fps->fp, fps->fdno);
00204         }
00205         be += strlen(be);
00206         *be = '\0';
00207     }
00208     return buf;
00209 }
00210 /*@=boundswrite@*/
00211 
00212 /* =============================================================== */
00213 off_t fdSize(FD_t fd)
00214 {
00215     struct stat sb;
00216     off_t rc = -1; 
00217 
00218 #ifdef  NOISY
00219 DBGIO(0, (stderr, "==>\tfdSize(%p) rc %ld\n", fd, (long)rc));
00220 #endif
00221     FDSANE(fd);
00222     if (fd->contentLength >= 0)
00223         rc = fd->contentLength;
00224     else switch (fd->urlType) {
00225     case URL_IS_PATH:
00226     case URL_IS_UNKNOWN:
00227         if (fstat(Fileno(fd), &sb) == 0)
00228             rc = sb.st_size;
00229         /*@fallthrough@*/
00230     case URL_IS_HTTPS:
00231     case URL_IS_HTTP:
00232     case URL_IS_HKP:
00233     case URL_IS_FTP:
00234     case URL_IS_DASH:
00235         break;
00236     }
00237     return rc;
00238 }
00239 
00240 FD_t fdDup(int fdno)
00241 {
00242     FD_t fd;
00243     int nfdno;
00244 
00245     if ((nfdno = dup(fdno)) < 0)
00246         return NULL;
00247     fd = fdNew("open (fdDup)");
00248     fdSetFdno(fd, nfdno);
00249 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
00250     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00251 }
00252 
00253 static inline /*@unused@*/ int fdSeekNot(void * cookie,
00254                 /*@unused@*/ _libio_pos_t pos,  /*@unused@*/ int whence)
00255         /*@*/
00256 {
00257     FD_t fd = c2f(cookie);
00258     FDSANE(fd);         /* XXX keep gcc quiet */
00259     return -2;
00260 }
00261 
00262 #ifdef UNUSED
00263 FILE *fdFdopen(void * cookie, const char *fmode)
00264 {
00265     FD_t fd = c2f(cookie);
00266     int fdno;
00267     FILE * fp;
00268 
00269     if (fmode == NULL) return NULL;
00270     fdno = fdFileno(fd);
00271     if (fdno < 0) return NULL;
00272     fp = fdopen(fdno, fmode);
00273 DBGIO(fd, (stderr, "==> fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
00274     fd = fdFree(fd, "open (fdFdopen)");
00275     return fp;
00276 }
00277 #endif
00278 
00279 /* =============================================================== */
00280 /*@-mustmod@*/ /* FIX: cookie is modified */
00281 static inline /*@null@*/ FD_t XfdLink(void * cookie, const char * msg,
00282                 const char * file, unsigned line)
00283         /*@modifies *cookie @*/
00284 {
00285     FD_t fd;
00286 if (cookie == NULL)
00287     /*@-castexpose@*/
00288 DBGREFS(0, (stderr, "--> fd  %p ++ %d %s at %s:%u\n", cookie, FDNREFS(cookie)+1, msg, file, line));
00289     /*@=castexpose@*/
00290     fd = c2f(cookie);
00291     if (fd) {
00292         fd->nrefs++;
00293 DBGREFS(fd, (stderr, "--> fd  %p ++ %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
00294     }
00295     return fd;
00296 }
00297 /*@=mustmod@*/
00298 
00299 static inline /*@null@*/
00300 FD_t XfdFree( /*@killref@*/ FD_t fd, const char *msg,
00301                 const char *file, unsigned line)
00302         /*@modifies fd @*/
00303 {
00304         int i;
00305 
00306 if (fd == NULL)
00307 DBGREFS(0, (stderr, "--> fd  %p -- %d %s at %s:%u\n", fd, FDNREFS(fd), msg, file, line));
00308     FDSANE(fd);
00309     if (fd) {
00310 DBGREFS(fd, (stderr, "--> fd  %p -- %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
00311         if (--fd->nrefs > 0)
00312             /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
00313         fd->stats = _free(fd->stats);
00314         for (i = fd->ndigests - 1; i >= 0; i--) {
00315             FDDIGEST_t fddig = fd->digests + i;
00316             if (fddig->hashctx == NULL)
00317                 continue;
00318             (void) rpmDigestFinal(fddig->hashctx, NULL, NULL, 0);
00319             fddig->hashctx = NULL;
00320         }
00321         fd->ndigests = 0;
00322         /*@-refcounttrans@*/ free(fd); /*@=refcounttrans@*/
00323     }
00324     return NULL;
00325 }
00326 
00327 static inline /*@null@*/
00328 FD_t XfdNew(const char * msg, const char * file, unsigned line)
00329         /*@globals internalState @*/
00330         /*@modifies internalState @*/
00331 {
00332     FD_t fd = xcalloc(1, sizeof(*fd));
00333     if (fd == NULL) /* XXX xmalloc never returns NULL */
00334         return NULL;
00335     fd->nrefs = 0;
00336     fd->flags = 0;
00337     fd->magic = FDMAGIC;
00338     fd->urlType = URL_IS_UNKNOWN;
00339 
00340     fd->nfps = 0;
00341     memset(fd->fps, 0, sizeof(fd->fps));
00342 
00343     fd->fps[0].io = fdio;
00344     fd->fps[0].fp = NULL;
00345     fd->fps[0].fdno = -1;
00346 
00347     fd->url = NULL;
00348     fd->rd_timeoutsecs = 60;    /* XXX default value used to be -1 */
00349     fd->contentLength = fd->bytesRemain = -1;
00350     fd->wr_chunked = 0;
00351     fd->syserrno = 0;
00352     fd->errcookie = NULL;
00353     fd->stats = xcalloc(1, sizeof(*fd->stats));
00354 
00355     fd->ndigests = 0;
00356     memset(fd->digests, 0, sizeof(fd->digests));
00357 
00358     fd->ftpFileDoneNeeded = 0;
00359     fd->firstFree = 0;
00360     fd->fileSize = 0;
00361     fd->fd_cpioPos = 0;
00362 
00363     return XfdLink(fd, msg, file, line);
00364 }
00365 
00366 static ssize_t fdRead(void * cookie, /*@out@*/ char * buf, size_t count)
00367         /*@globals errno, fileSystem, internalState @*/
00368         /*@modifies buf, errno, fileSystem, internalState @*/
00369         /*@requires maxSet(buf) >= (count - 1) @*/
00370         /*@ensures maxRead(buf) == result @*/
00371 {
00372     FD_t fd = c2f(cookie);
00373     ssize_t rc;
00374 
00375     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
00376 
00377     fdstat_enter(fd, FDSTAT_READ);
00378 /*@-boundswrite@*/
00379     /* HACK: flimsy wiring for davRead */
00380     if (fd->req != NULL) {
00381 #ifdef WITH_NEON
00382         rc = davRead(fd, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
00383 #else
00384         rc = -1;
00385 #endif
00386         /* XXX Chunked davRead EOF. */
00387         if (rc == 0)
00388             fd->bytesRemain = 0;
00389     } else
00390         rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
00391 /*@=boundswrite@*/
00392     fdstat_exit(fd, FDSTAT_READ, rc);
00393 
00394     if (fd->ndigests && rc > 0) fdUpdateDigests(fd, buf, rc);
00395 
00396 DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
00397 
00398     return rc;
00399 }
00400 
00401 static ssize_t fdWrite(void * cookie, const char * buf, size_t count)
00402         /*@globals errno, fileSystem, internalState @*/
00403         /*@modifies errno, fileSystem, internalState @*/
00404 {
00405     FD_t fd = c2f(cookie);
00406     int fdno = fdFileno(fd);
00407     ssize_t rc;
00408 
00409     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
00410 
00411     if (fd->ndigests && count > 0) fdUpdateDigests(fd, buf, count);
00412 
00413     if (count == 0) return 0;
00414 
00415     fdstat_enter(fd, FDSTAT_WRITE);
00416 /*@-boundsread@*/
00417     /* HACK: flimsy wiring for davWrite */
00418     if (fd->req != NULL) {
00419 #ifdef WITH_NEON
00420         rc = davWrite(fd, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
00421 #else
00422         return -1;
00423 #endif
00424     } else
00425         rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
00426 /*@=boundsread@*/
00427     fdstat_exit(fd, FDSTAT_WRITE, rc);
00428 
00429 DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
00430 
00431     return rc;
00432 }
00433 
00434 static inline int fdSeek(void * cookie, _libio_pos_t pos, int whence)
00435         /*@globals fileSystem, internalState @*/
00436         /*@modifies fileSystem, internalState @*/
00437 {
00438 #ifdef USE_COOKIE_SEEK_POINTER
00439     _IO_off64_t p = *pos;
00440 #else
00441     off_t p = pos;
00442 #endif
00443     FD_t fd = c2f(cookie);
00444     off_t rc;
00445 
00446     assert(fd->bytesRemain == -1);      /* XXX FIXME fadio only for now */
00447     fdstat_enter(fd, FDSTAT_SEEK);
00448     rc = lseek(fdFileno(fd), p, whence);
00449     fdstat_exit(fd, FDSTAT_SEEK, rc);
00450 
00451 DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
00452 
00453     return rc;
00454 }
00455 
00456 static int fdClose( /*@only@*/ void * cookie)
00457         /*@globals errno, fileSystem, systemState, internalState @*/
00458         /*@modifies errno, fileSystem, systemState, internalState @*/
00459 {
00460     FD_t fd;
00461     int fdno;
00462     int rc;
00463 
00464     if (cookie == NULL) return -2;
00465     fd = c2f(cookie);
00466     fdno = fdFileno(fd);
00467 
00468     fdSetFdno(fd, -1);
00469 
00470     fdstat_enter(fd, FDSTAT_CLOSE);
00471     /* HACK: flimsy wiring for davClose */
00472 /*@-branchstate@*/
00473     if (fd->req != NULL) {
00474 #ifdef WITH_NEON
00475         rc = davClose(fd);
00476 #else
00477         return -1;
00478 #endif
00479     } else
00480         rc = ((fdno >= 0) ? close(fdno) : -2);
00481 /*@=branchstate@*/
00482     fdstat_exit(fd, FDSTAT_CLOSE, rc);
00483 
00484 DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
00485 
00486     fd = fdFree(fd, "open (fdClose)");
00487     return rc;
00488 }
00489 
00490 static /*@null@*/ FD_t fdOpen(const char *path, int flags, mode_t mode)
00491         /*@globals errno, fileSystem, internalState @*/
00492         /*@modifies errno, fileSystem, internalState @*/
00493 {
00494     FD_t fd;
00495     int fdno;
00496 
00497     fdno = open(path, flags, mode);
00498     if (fdno < 0) return NULL;
00499     if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
00500         (void) close(fdno);
00501         return NULL;
00502     }
00503     fd = fdNew("open (fdOpen)");
00504     fdSetFdno(fd, fdno);
00505     fd->flags = flags;
00506 DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
00507     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
00508 }
00509 
00510 /*@-type@*/ /* LCL: function typedefs */
00511 static struct FDIO_s fdio_s = {
00512   fdRead, fdWrite, fdSeek, fdClose, XfdLink, XfdFree, XfdNew, fdFileno,
00513   fdOpen, NULL, fdGetFp, NULL,  mkdir, chdir, rmdir, rename, unlink
00514 };
00515 /*@=type@*/
00516 FDIO_t fdio = /*@-compmempass@*/ &fdio_s /*@=compmempass@*/ ;
00517 
00518 int fdWritable(FD_t fd, int secs)
00519 {
00520     int fdno;
00521     int rc;
00522 #if HAVE_POLL_H
00523     int msecs = (secs >= 0 ? (1000 * secs) : -1);
00524     struct pollfd wrfds;
00525 #else
00526     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
00527     fd_set wrfds;
00528     FD_ZERO(&wrfds);
00529 #endif
00530         
00531     /* HACK: flimsy wiring for davWrite */
00532     if (fd->req != NULL)
00533         return 1;
00534 
00535     if ((fdno = fdFileno(fd)) < 0)
00536         return -1;      /* XXX W2DO? */
00537         
00538     do {
00539 #if HAVE_POLL_H
00540         wrfds.fd = fdno;
00541         wrfds.events = POLLOUT;
00542         wrfds.revents = 0;
00543         rc = poll(&wrfds, 1, msecs);
00544 #else
00545         if (tvp) {
00546             tvp->tv_sec = secs;
00547             tvp->tv_usec = 0;
00548         }
00549         FD_SET(fdno, &wrfds);
00550 /*@-compdef -nullpass@*/
00551         rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
00552 /*@=compdef =nullpass@*/
00553 #endif
00554 
00555         /* HACK: EBADF on PUT chunked termination from ufdClose. */
00556 if (_rpmio_debug && !(rc == 1 && errno == 0))
00557 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
00558         if (rc < 0) {
00559             switch (errno) {
00560             case EINTR:
00561                 continue;
00562                 /*@notreached@*/ /*@switchbreak@*/ break;
00563             default:
00564                 return rc;
00565                 /*@notreached@*/ /*@switchbreak@*/ break;
00566             }
00567         }
00568         return rc;
00569     } while (1);
00570     /*@notreached@*/
00571 }
00572 
00573 int fdReadable(FD_t fd, int secs)
00574 {
00575     int fdno;
00576     int rc;
00577 #if HAVE_POLL_H
00578     int msecs = (secs >= 0 ? (1000 * secs) : -1);
00579     struct pollfd rdfds;
00580 #else
00581     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
00582     fd_set rdfds;
00583     FD_ZERO(&rdfds);
00584 #endif
00585 
00586     /* HACK: flimsy wiring for davRead */
00587     if (fd->req != NULL)
00588         return 1;
00589 
00590     if ((fdno = fdFileno(fd)) < 0)
00591         return -1;      /* XXX W2DO? */
00592         
00593     do {
00594 #if HAVE_POLL_H
00595         rdfds.fd = fdno;
00596         rdfds.events = POLLIN;
00597         rdfds.revents = 0;
00598         rc = poll(&rdfds, 1, msecs);
00599 #else
00600         if (tvp) {
00601             tvp->tv_sec = secs;
00602             tvp->tv_usec = 0;
00603         }
00604         FD_SET(fdno, &rdfds);
00605         /*@-compdef -nullpass@*/
00606         rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
00607         /*@=compdef =nullpass@*/
00608 #endif
00609 
00610         if (rc < 0) {
00611             switch (errno) {
00612             case EINTR:
00613                 continue;
00614                 /*@notreached@*/ /*@switchbreak@*/ break;
00615             default:
00616                 return rc;
00617                 /*@notreached@*/ /*@switchbreak@*/ break;
00618             }
00619         }
00620         return rc;
00621     } while (1);
00622     /*@notreached@*/
00623 }
00624 
00625 /*@-boundswrite@*/
00626 int fdFgets(FD_t fd, char * buf, size_t len)
00627 {
00628     int fdno;
00629     int secs = fd->rd_timeoutsecs;
00630     size_t nb = 0;
00631     int ec = 0;
00632     char lastchar = '\0';
00633 
00634     if ((fdno = fdFileno(fd)) < 0)
00635         return 0;       /* XXX W2DO? */
00636         
00637     do {
00638         int rc;
00639 
00640         /* Is there data to read? */
00641         rc = fdReadable(fd, secs);
00642 
00643         switch (rc) {
00644         case -1:        /* error */
00645             ec = -1;
00646             continue;
00647             /*@notreached@*/ /*@switchbreak@*/ break;
00648         case  0:        /* timeout */
00649             ec = -1;
00650             continue;
00651             /*@notreached@*/ /*@switchbreak@*/ break;
00652         default:        /* data to read */
00653             /*@switchbreak@*/ break;
00654         }
00655 
00656         errno = 0;
00657 #ifdef  NOISY
00658         rc = fdRead(fd, buf + nb, 1);
00659 #else
00660         rc = read(fdFileno(fd), buf + nb, 1);
00661 #endif
00662         if (rc < 0) {
00663             fd->syserrno = errno;
00664             switch (errno) {
00665             case EWOULDBLOCK:
00666                 continue;
00667                 /*@notreached@*/ /*@switchbreak@*/ break;
00668             default:
00669                 /*@switchbreak@*/ break;
00670             }
00671 if (_rpmio_debug)
00672 fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
00673             ec = -1;
00674             break;
00675         } else if (rc == 0) {
00676 if (_rpmio_debug)
00677 fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
00678             break;
00679         } else {
00680             nb += rc;
00681             buf[nb] = '\0';
00682             lastchar = buf[nb - 1];
00683         }
00684     } while (ec == 0 && nb < len && lastchar != '\n');
00685 
00686     return (ec >= 0 ? nb : ec);
00687 }
00688 /*@=boundswrite@*/
00689 
00690 /* =============================================================== */
00691 /* Support for FTP/HTTP I/O.
00692  */
00693 const char *const ftpStrerror(int errorNumber)
00694 {
00695     switch (errorNumber) {
00696     case 0:
00697         return _("Success");
00698 
00699     /* HACK error impediance match, coalesce and rename. */
00700     case FTPERR_NE_ERROR:
00701         return ("NE_ERROR: Generic error.");
00702     case FTPERR_NE_LOOKUP:
00703         return ("NE_LOOKUP: Hostname lookup failed.");
00704     case FTPERR_NE_AUTH:
00705         return ("NE_AUTH: Server authentication failed.");
00706     case FTPERR_NE_PROXYAUTH:
00707         return ("NE_PROXYAUTH: Proxy authentication failed.");
00708     case FTPERR_NE_CONNECT:
00709         return ("NE_CONNECT: Could not connect to server.");
00710     case FTPERR_NE_TIMEOUT:
00711         return ("NE_TIMEOUT: Connection timed out.");
00712     case FTPERR_NE_FAILED:
00713         return ("NE_FAILED: The precondition failed.");
00714     case FTPERR_NE_RETRY:
00715         return ("NE_RETRY: Retry request.");
00716     case FTPERR_NE_REDIRECT:
00717         return ("NE_REDIRECT: Redirect received.");
00718 
00719     case FTPERR_BAD_SERVER_RESPONSE:
00720         return _("Bad server response");
00721     case FTPERR_SERVER_IO_ERROR:
00722         return _("Server I/O error");
00723     case FTPERR_SERVER_TIMEOUT:
00724         return _("Server timeout");
00725     case FTPERR_BAD_HOST_ADDR:
00726         return _("Unable to lookup server host address");
00727     case FTPERR_BAD_HOSTNAME:
00728         return _("Unable to lookup server host name");
00729     case FTPERR_FAILED_CONNECT:
00730         return _("Failed to connect to server");
00731     case FTPERR_FAILED_DATA_CONNECT:
00732         return _("Failed to establish data connection to server");
00733     case FTPERR_FILE_IO_ERROR:
00734         return _("I/O error to local file");
00735     case FTPERR_PASSIVE_ERROR:
00736         return _("Error setting remote server to passive mode");
00737     case FTPERR_FILE_NOT_FOUND:
00738         return _("File not found on server");
00739     case FTPERR_NIC_ABORT_IN_PROGRESS:
00740         return _("Abort in progress");
00741 
00742     case FTPERR_UNKNOWN:
00743     default:
00744         return _("Unknown or unexpected error");
00745     }
00746 }
00747 
00748 const char *urlStrerror(const char *url)
00749 {
00750     const char *retstr;
00751     /*@-branchstate@*/
00752     switch (urlIsURL(url)) {
00753     case URL_IS_HTTPS:
00754     case URL_IS_HTTP:
00755     case URL_IS_HKP:
00756     case URL_IS_FTP:
00757     {   urlinfo u;
00758 /* XXX This only works for httpReq/ftpLogin/ftpReq failures */
00759         if (urlSplit(url, &u) == 0) {
00760             retstr = ftpStrerror(u->openError);
00761         } else
00762             retstr = "Malformed URL";
00763     }   break;
00764     default:
00765         retstr = strerror(errno);
00766         break;
00767     }
00768     /*@=branchstate@*/
00769     return retstr;
00770 }
00771 
00772 #if !defined(HAVE_GETADDRINFO)
00773 #if !defined(USE_ALT_DNS) || !USE_ALT_DNS 
00774 static int mygethostbyname(const char * host,
00775                 /*@out@*/ struct in_addr * address)
00776         /*@globals h_errno @*/
00777         /*@modifies *address @*/
00778 {
00779     struct hostent * hostinfo;
00780 
00781     /*@-multithreaded @*/
00782     hostinfo = gethostbyname(host);
00783     /*@=multithreaded @*/
00784     if (!hostinfo) return 1;
00785 
00786 /*@-boundswrite@*/
00787     memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
00788 /*@=boundswrite@*/
00789     return 0;
00790 }
00791 #endif
00792 
00793 /*@-boundsread@*/
00794 /*@-compdef@*/  /* FIX: address->s_addr undefined. */
00795 static int getHostAddress(const char * host, /*@out@*/ struct in_addr * address)
00796         /*@globals errno, h_errno @*/
00797         /*@modifies *address, errno @*/
00798 {
00799 #if 0   /* XXX workaround nss_foo module hand-off using valgrind. */
00800     if (!strcmp(host, "localhost")) {
00801         /*@-moduncon @*/
00802         if (!inet_aton("127.0.0.1", address))
00803             return FTPERR_BAD_HOST_ADDR;
00804         /*@=moduncon @*/
00805     } else
00806 #endif
00807     if (xisdigit(host[0])) {
00808         /*@-moduncon @*/
00809         if (!inet_aton(host, address))
00810             return FTPERR_BAD_HOST_ADDR;
00811         /*@=moduncon @*/
00812     } else {
00813         if (mygethostbyname(host, address)) {
00814             errno = h_errno;
00815             return FTPERR_BAD_HOSTNAME;
00816         }
00817     }
00818     
00819     return 0;
00820 }
00821 /*@=compdef@*/
00822 /*@=boundsread@*/
00823 #endif
00824 
00825 static int tcpConnect(FD_t ctrl, const char * host, int port)
00826         /*@globals h_errno, fileSystem, internalState @*/
00827         /*@modifies ctrl, fileSystem, internalState @*/
00828 {
00829     int fdno = -1;
00830     int rc;
00831 #ifdef  HAVE_GETADDRINFO
00832     struct addrinfo hints, *res, *res0;
00833     char pbuf[NI_MAXSERV];
00834 
00835     memset(&hints, 0, sizeof(hints));
00836     hints.ai_family = AF_UNSPEC;
00837     hints.ai_socktype = SOCK_STREAM;
00838     sprintf(pbuf, "%d", port);
00839     pbuf[sizeof(pbuf)-1] = '\0';
00840     rc = FTPERR_FAILED_CONNECT;
00841     if (getaddrinfo(host, pbuf, &hints, &res0) == 0) {
00842         for (res = res0; res != NULL; res= res->ai_next) {
00843             if ((fdno = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
00844                 continue;
00845             if (connect(fdno, res->ai_addr, res->ai_addrlen) < 0) {
00846                 close(fdno);
00847                 continue;
00848             }
00849             /* success */
00850             rc = 0;
00851             if (_ftp_debug) {
00852                 char hbuf[NI_MAXHOST];
00853                 getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
00854                                 NULL, 0, NI_NUMERICHOST);
00855                 fprintf(stderr,"++ connect [%s]:%d on fdno %d\n",
00856                                 /*@-unrecog@*/ hbuf /*@=unrecog@*/, port, fdno);
00857             }
00858             break;
00859         }
00860         freeaddrinfo(res0);
00861     }
00862     if (rc < 0)
00863         goto errxit;
00864 
00865 #else   /* HAVE_GETADDRINFO */                      
00866     struct sockaddr_in sin;
00867 
00868 /*@-boundswrite@*/
00869     memset(&sin, 0, sizeof(sin));
00870 /*@=boundswrite@*/
00871     sin.sin_family = AF_INET;
00872     sin.sin_port = htons(port);
00873     sin.sin_addr.s_addr = INADDR_ANY;
00874     
00875   do {
00876     if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
00877         break;
00878 
00879     if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
00880         rc = FTPERR_FAILED_CONNECT;
00881         break;
00882     }
00883 
00884     /*@-internalglobs@*/
00885     if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
00886         rc = FTPERR_FAILED_CONNECT;
00887         break;
00888     }
00889     /*@=internalglobs@*/
00890   } while (0);
00891 
00892     if (rc < 0)
00893         goto errxit;
00894 
00895 if (_ftp_debug)
00896 fprintf(stderr,"++ connect %s:%d on fdno %d\n",
00897 /*@-unrecog -moduncon -evalorderuncon @*/
00898 inet_ntoa(sin.sin_addr)
00899 /*@=unrecog =moduncon =evalorderuncon @*/ ,
00900 (int)ntohs(sin.sin_port), fdno);
00901 #endif  /* HAVE_GETADDRINFO */
00902 
00903     fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
00904     return 0;
00905 
00906 errxit:
00907     /*@-observertrans@*/
00908     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
00909     /*@=observertrans@*/
00910     if (fdno >= 0)
00911         (void) close(fdno);
00912     return rc;
00913 }
00914 
00915 /*@-boundswrite@*/
00916 static int checkResponse(void * uu, FD_t ctrl,
00917                 /*@out@*/ int *ecp, /*@out@*/ char ** str)
00918         /*@globals fileSystem @*/
00919         /*@modifies ctrl, *ecp, *str, fileSystem @*/
00920 {
00921     urlinfo u = uu;
00922     char *buf;
00923     size_t bufAlloced;
00924     int bufLength = 0; 
00925     const char *s;
00926     char *se;
00927     int ec = 0;
00928     int moretodo = 1;
00929     char errorCode[4];
00930  
00931     URLSANE(u);
00932     if (u->bufAlloced == 0 || u->buf == NULL) {
00933         u->bufAlloced = _url_iobuf_size;
00934         u->buf = xcalloc(u->bufAlloced, sizeof(u->buf[0]));
00935     }
00936     buf = u->buf;
00937     bufAlloced = u->bufAlloced;
00938     *buf = '\0';
00939 
00940     errorCode[0] = '\0';
00941     
00942     do {
00943         int rc;
00944 
00945         /*
00946          * Read next line from server.
00947          */
00948         se = buf + bufLength;
00949         *se = '\0';
00950         rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
00951         if (rc < 0) {
00952             ec = FTPERR_BAD_SERVER_RESPONSE;
00953             continue;
00954         } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
00955             moretodo = 0;
00956 
00957         /*
00958          * Process next line from server.
00959          */
00960         for (s = se; *s != '\0'; s = se) {
00961                 const char *e;
00962 
00963                 while (*se && *se != '\n') se++;
00964 
00965                 if (se > s && se[-1] == '\r')
00966                    se[-1] = '\0';
00967                 if (*se == '\0')
00968                     /*@innerbreak@*/ break;
00969 
00970 if (_ftp_debug)
00971 fprintf(stderr, "<- %s\n", s);
00972 
00973                 /* HTTP: header termination on empty line */
00974                 if (*s == '\0') {
00975                     moretodo = 0;
00976                     /*@innerbreak@*/ break;
00977                 }
00978                 *se++ = '\0';
00979 
00980                 /* HTTP: look for "HTTP/1.1 123 ..." */
00981                 if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
00982                     ctrl->contentLength = -1;
00983                     if ((e = strchr(s, '.')) != NULL) {
00984                         e++;
00985                         u->httpVersion = *e - '0';
00986                         if (u->httpVersion < 1 || u->httpVersion > 2)
00987                             ctrl->persist = u->httpVersion = 0;
00988                         else
00989                             ctrl->persist = 1;
00990                     }
00991                     if ((e = strchr(s, ' ')) != NULL) {
00992                         e++;
00993                         if (strchr("0123456789", *e))
00994                             strncpy(errorCode, e, 3);
00995                         errorCode[3] = '\0';
00996                     }
00997                     /*@innercontinue@*/ continue;
00998                 }
00999 
01000                 /* HTTP: look for "token: ..." */
01001                 for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
01002                     {};
01003                 if (e > s && *e++ == ':') {
01004                     size_t ne = (e - s);
01005                     while (*e && *e == ' ') e++;
01006 #if 0
01007                     if (!strncmp(s, "Date:", ne)) {
01008                     } else
01009                     if (!strncmp(s, "Server:", ne)) {
01010                     } else
01011                     if (!strncmp(s, "Last-Modified:", ne)) {
01012                     } else
01013                     if (!strncmp(s, "ETag:", ne)) {
01014                     } else
01015 #endif
01016                     if (!strncmp(s, "Accept-Ranges:", ne)) {
01017                         if (!strcmp(e, "bytes"))
01018                             u->httpHasRange = 1;
01019                         if (!strcmp(e, "none"))
01020                             u->httpHasRange = 0;
01021                     } else
01022                     if (!strncmp(s, "Content-Length:", ne)) {
01023                         if (strchr("0123456789", *e))
01024                             ctrl->contentLength = atoi(e);
01025                     } else
01026                     if (!strncmp(s, "Connection:", ne)) {
01027                         if (!strcmp(e, "close"))
01028                             ctrl->persist = 0;
01029                     }
01030 #if 0
01031                     else
01032                     if (!strncmp(s, "Content-Type:", ne)) {
01033                     } else
01034                     if (!strncmp(s, "Transfer-Encoding:", ne)) {
01035                         if (!strcmp(e, "chunked"))
01036                             ctrl->wr_chunked = 1;
01037                         else
01038                             ctrl->wr_chunked = 0;
01039                     } else
01040                     if (!strncmp(s, "Allow:", ne)) {
01041                     }
01042 #endif
01043                     /*@innercontinue@*/ continue;
01044                 }
01045 
01046                 /* HTTP: look for "<TITLE>501 ... </TITLE>" */
01047                 if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
01048                     s += sizeof("<TITLE>") - 1;
01049 
01050                 /* FTP: look for "123-" and/or "123 " */
01051                 if (strchr("0123456789", *s)) {
01052                     if (errorCode[0] != '\0') {
01053                         if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
01054                             moretodo = 0;
01055                     } else {
01056                         strncpy(errorCode, s, sizeof("123")-1);
01057                         errorCode[3] = '\0';
01058                         if (s[3] != '-')
01059                             moretodo = 0;
01060                     }
01061                 }
01062         }
01063 
01064         if (moretodo && se > s) {
01065             bufLength = se - s - 1;
01066             if (s != buf)
01067                 memmove(buf, s, bufLength);
01068         } else {
01069             bufLength = 0;
01070         }
01071     } while (moretodo && ec == 0);
01072 
01073     if (str)    *str = buf;
01074     if (ecp)    *ecp = atoi(errorCode);
01075 
01076     return ec;
01077 }
01078 /*@=boundswrite@*/
01079 
01080 static int ftpCheckResponse(urlinfo u, /*@out@*/ char ** str)
01081         /*@globals fileSystem @*/
01082         /*@modifies u, *str, fileSystem @*/
01083 {
01084     int ec = 0;
01085     int rc;
01086 
01087     URLSANE(u);
01088     rc = checkResponse(u, u->ctrl, &ec, str);
01089 
01090     switch (ec) {
01091     case 550:
01092         return FTPERR_FILE_NOT_FOUND;
01093         /*@notreached@*/ break;
01094     case 552:
01095         return FTPERR_NIC_ABORT_IN_PROGRESS;
01096         /*@notreached@*/ break;
01097     default:
01098         if (ec >= 400 && ec <= 599) {
01099             return FTPERR_BAD_SERVER_RESPONSE;
01100         }
01101         break;
01102     }
01103     return rc;
01104 }
01105 
01106 static int ftpCommand(urlinfo u, char ** str, ...)
01107         /*@globals fileSystem, internalState @*/
01108         /*@modifies u, *str, fileSystem, internalState @*/
01109 {
01110     va_list ap;
01111     int len = 0;
01112     const char * s, * t;
01113     char * te;
01114     int rc;
01115 
01116     URLSANE(u);
01117     va_start(ap, str);
01118     while ((s = va_arg(ap, const char *)) != NULL) {
01119         if (len) len++;
01120         len += strlen(s);
01121     }
01122     len += sizeof("\r\n")-1;
01123     va_end(ap);
01124 
01125 /*@-boundswrite@*/
01126     t = te = alloca(len + 1);
01127 
01128     va_start(ap, str);
01129     while ((s = va_arg(ap, const char *)) != NULL) {
01130         if (te > t) *te++ = ' ';
01131         te = stpcpy(te, s);
01132     }
01133     te = stpcpy(te, "\r\n");
01134     va_end(ap);
01135 /*@=boundswrite@*/
01136 
01137 if (_ftp_debug)
01138 fprintf(stderr, "-> %s", t);
01139     if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
01140         return FTPERR_SERVER_IO_ERROR;
01141 
01142     rc = ftpCheckResponse(u, str);
01143     return rc;
01144 }
01145 
01146 static int ftpLogin(urlinfo u)
01147         /*@globals h_errno, fileSystem, internalState @*/
01148         /*@modifies u, fileSystem, internalState @*/
01149 {
01150     const char * host;
01151     const char * user;
01152     const char * password;
01153     int port;
01154     int rc;
01155 
01156     URLSANE(u);
01157     u->ctrl = fdLink(u->ctrl, "open ctrl");
01158 
01159     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
01160         rc = FTPERR_BAD_HOSTNAME;
01161         goto errxit;
01162     }
01163 
01164     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
01165 
01166     /*@-branchstate@*/
01167     if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
01168         user = "anonymous";
01169     /*@=branchstate@*/
01170 
01171     /*@-branchstate@*/
01172     if ((password = u->password) == NULL) {
01173         uid_t uid = getuid();
01174         struct passwd * pw;
01175         if (uid && (pw = getpwuid(uid)) != NULL) {
01176 /*@-boundswrite@*/
01177             char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
01178             strcpy(myp, pw->pw_name);
01179             strcat(myp, "@");
01180 /*@=boundswrite@*/
01181             password = myp;
01182         } else {
01183             password = "root@";
01184         }
01185     }
01186     /*@=branchstate@*/
01187 
01188     /*@-branchstate@*/
01189     if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
01190         /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
01191     /*@=branchstate@*/
01192 
01193 /*@-usereleased@*/
01194     if (fdFileno(u->ctrl) < 0) {
01195         rc = tcpConnect(u->ctrl, host, port);
01196         if (rc < 0)
01197             goto errxit2;
01198     }
01199 
01200     if ((rc = ftpCheckResponse(u, NULL)))
01201         goto errxit;
01202 
01203     if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
01204         goto errxit;
01205 
01206     if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
01207         goto errxit;
01208 
01209     if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
01210         goto errxit;
01211 
01212     /*@-compdef@*/
01213     return 0;
01214     /*@=compdef@*/
01215 
01216 errxit:
01217     /*@-observertrans@*/
01218     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
01219     /*@=observertrans@*/
01220 errxit2:
01221     /*@-branchstate@*/
01222     if (fdFileno(u->ctrl) >= 0)
01223         /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
01224     /*@=branchstate@*/
01225     /*@-compdef@*/
01226     return rc;
01227     /*@=compdef@*/
01228 /*@=usereleased@*/
01229 }
01230 
01231 int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
01232 {
01233     urlinfo u = data->url;
01234 #if !defined(HAVE_GETADDRINFO)
01235     struct sockaddr_in dataAddress;
01236 #endif  /* HAVE_GETADDRINFO */
01237     char remoteIP[NI_MAXHOST];
01238     char * cmd;
01239     int cmdlen;
01240     char * passReply;
01241     char * chptr;
01242     int rc;
01243     int epsv;
01244     int port;
01245 
01246 /*@-boundswrite@*/
01247     URLSANE(u);
01248     if (ftpCmd == NULL)
01249         return FTPERR_UNKNOWN;  /* XXX W2DO? */
01250 
01251     cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
01252     chptr = cmd = alloca(cmdlen);
01253     chptr = stpcpy(chptr, ftpCmd);
01254     if (ftpArg) {
01255         *chptr++ = ' ';
01256         chptr = stpcpy(chptr, ftpArg);
01257     }
01258     chptr = stpcpy(chptr, "\r\n");
01259     cmdlen = chptr - cmd;
01260 
01261 /*
01262  * Get the ftp version of the Content-Length.
01263  */
01264     if (!strncmp(cmd, "RETR", 4)) {
01265         unsigned cl;
01266 
01267         passReply = NULL;
01268         rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
01269         if (rc)
01270             goto errxit;
01271         if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
01272             rc = FTPERR_BAD_SERVER_RESPONSE;
01273             goto errxit;
01274         }
01275         rc = 0;
01276         data->contentLength = cl;
01277     }
01278 
01279     epsv = 0;
01280     passReply = NULL;
01281 #ifdef HAVE_GETNAMEINFO
01282     rc = ftpCommand(u, &passReply, "EPSV", NULL);
01283     if (rc==0) {
01284 #ifdef HAVE_GETADDRINFO
01285         struct sockaddr_storage ss;
01286 #else /* HAVE_GETADDRINFO */
01287         struct sockaddr_in ss;
01288 #endif /* HAVE_GETADDRINFO */
01289         int size;
01290         /* we need to know IP of remote host */
01291         size=sizeof(ss);
01292         if ((getpeername(fdFileno(c2f(u->ctrl)), (struct sockaddr *)&ss, &size) == 0) &&
01293                         (getnameinfo((struct sockaddr *)&ss, size, remoteIP, sizeof(remoteIP),
01294                                 NULL, 0, NI_NUMERICHOST) == 0))
01295                 epsv++;
01296         else {
01297                 /* abort EPSV and fall back to PASV */
01298                 rc = ftpCommand(u, &passReply, "ABOR", NULL);
01299                 if (rc) {
01300                     rc = FTPERR_PASSIVE_ERROR;
01301                     goto errxit;
01302                 }
01303         }
01304     }
01305     if (epsv==0)
01306 #endif /* HAVE_GETNAMEINFO */
01307         rc = ftpCommand(u, &passReply, "PASV", NULL);
01308     if (rc) {
01309         rc = FTPERR_PASSIVE_ERROR;
01310         goto errxit;
01311     }
01312 
01313     chptr = passReply;
01314     while (*chptr && *chptr != '(') chptr++;
01315     if (*chptr != '(') return FTPERR_PASSIVE_ERROR; 
01316     chptr++;
01317     passReply = chptr;
01318     while (*chptr && *chptr != ')') chptr++;
01319     if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
01320     *chptr-- = '\0';
01321 
01322     if (epsv) {
01323         int i;
01324         if(sscanf(passReply,"%*c%*c%*c%d%*c",&i) != 1) {
01325            rc = FTPERR_PASSIVE_ERROR;
01326            goto errxit;
01327         }
01328         port = i;
01329     } else {
01330  
01331     while (*chptr && *chptr != ',') chptr--;
01332     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01333     chptr--;
01334     while (*chptr && *chptr != ',') chptr--;
01335     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01336     *chptr++ = '\0';
01337     
01338     /* now passReply points to the IP portion, and chptr points to the
01339        port number portion */
01340 
01341     {   int i, j;
01342         if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
01343             rc = FTPERR_PASSIVE_ERROR;
01344             goto errxit;
01345         }
01346         port = (((unsigned)i) << 8) + j;
01347     }
01348 
01349     chptr = passReply;
01350     while (*chptr++ != '\0') {
01351         if (*chptr == ',') *chptr = '.';
01352     }
01353 /*@=boundswrite@*/
01354     sprintf(remoteIP, "%s", passReply);
01355     } /* if (epsv) */
01356 
01357 #ifdef HAVE_GETADDRINFO
01358     {
01359         struct addrinfo hints, *res, *res0;
01360         char pbuf[NI_MAXSERV];
01361 
01362         memset(&hints, 0, sizeof(hints));
01363         hints.ai_family = AF_UNSPEC;
01364         hints.ai_socktype = SOCK_STREAM;
01365         hints.ai_flags = AI_NUMERICHOST;
01366         sprintf(pbuf, "%d", port);
01367         pbuf[sizeof(pbuf)-1] = '\0';
01368         if (getaddrinfo(remoteIP, pbuf, &hints, &res0)) {
01369             rc = FTPERR_PASSIVE_ERROR;
01370             goto errxit;
01371         }
01372 
01373         for (res = res0; res != NULL; res = res->ai_next) {
01374             rc = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
01375             fdSetFdno(data, (rc >= 0 ? rc : -1));
01376             if (rc < 0) {
01377                 if (res->ai_next)
01378                     continue;
01379                 else {
01380                     rc = FTPERR_FAILED_CONNECT;
01381                     freeaddrinfo(res0);
01382                     goto errxit;
01383                 }
01384             }
01385             data = fdLink(data, "open data (ftpReq)");
01386 
01387             /* XXX setsockopt SO_LINGER */
01388             /* XXX setsockopt SO_KEEPALIVE */
01389             /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
01390             
01391             {
01392                 int criterr = 0;
01393                 while (connect(fdFileno(data), res->ai_addr, res->ai_addrlen) < 0) {
01394                     if (errno == EINTR)
01395                         continue;
01396                     criterr++;
01397                 }
01398                 if (criterr) {
01399                     if (res->ai_addr) {
01400                         fdClose(data);
01401                         continue;
01402                     } else {
01403                         rc = FTPERR_PASSIVE_ERROR;
01404                         freeaddrinfo(res0);
01405                         goto errxit;
01406                     }
01407                 }
01408             }
01409             /* success */
01410             rc = 0;
01411             break;
01412         }
01413         freeaddrinfo(res0);
01414     }
01415             
01416 #else /* HAVE_GETADDRINFO */
01417     memset(&dataAddress, 0, sizeof(dataAddress));
01418     dataAddress.sin_family = AF_INET;
01419     dataAddress.sin_port = htons(port);
01420 
01421     /*@-moduncon@*/
01422     if (!inet_aton(remoteIP, &dataAddress.sin_addr)) {
01423         rc = FTPERR_PASSIVE_ERROR;
01424         goto errxit;
01425     }
01426     /*@=moduncon@*/
01427 
01428     rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
01429     fdSetFdno(data, (rc >= 0 ? rc : -1));
01430     if (rc < 0) {
01431         rc = FTPERR_FAILED_CONNECT;
01432         goto errxit;
01433     }
01434     data = fdLink(data, "open data (ftpReq)");
01435 
01436     /* XXX setsockopt SO_LINGER */
01437     /* XXX setsockopt SO_KEEPALIVE */
01438     /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
01439 
01440     /*@-internalglobs@*/
01441     while (connect(fdFileno(data), (struct sockaddr *) &dataAddress, 
01442                 sizeof(dataAddress)) < 0)
01443     {
01444         if (errno == EINTR)
01445             continue;
01446         rc = FTPERR_FAILED_DATA_CONNECT;
01447         goto errxit;
01448     }
01449     /*@=internalglobs@*/
01450 #endif /* HAVE_GETADDRINFO */
01451 
01452 if (_ftp_debug)
01453 fprintf(stderr, "-> %s", cmd);
01454     if (fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
01455         rc = FTPERR_SERVER_IO_ERROR;
01456         goto errxit;
01457     }
01458 
01459     if ((rc = ftpCheckResponse(u, NULL))) {
01460         goto errxit;
01461     }
01462 
01463     data->ftpFileDoneNeeded = 1;
01464     u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
01465     u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
01466     return 0;
01467 
01468 errxit:
01469     /*@-observertrans@*/
01470     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
01471     /*@=observertrans@*/
01472     /*@-branchstate@*/
01473     if (fdFileno(data) >= 0)
01474         /*@-refcounttrans@*/ (void) fdClose(data); /*@=refcounttrans@*/
01475     /*@=branchstate@*/
01476     return rc;
01477 }
01478 
01479 /*@unchecked@*/ /*@null@*/
01480 static rpmCallbackFunction      urlNotify = NULL;
01481 
01482 /*@unchecked@*/ /*@null@*/
01483 static void *                   urlNotifyData = NULL;
01484 
01485 /*@unchecked@*/
01486 static int                      urlNotifyCount = -1;
01487 
01488 void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
01489     urlNotify = notify;
01490     urlNotifyData = notifyData;
01491     urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
01492 }
01493 
01494 int ufdCopy(FD_t sfd, FD_t tfd)
01495 {
01496     char buf[BUFSIZ];
01497     int itemsRead;
01498     int itemsCopied = 0;
01499     int rc = 0;
01500     int notifier = -1;
01501 
01502     if (urlNotify) {
01503 /*@-boundsread@*/
01504         /*@-noeffectuncon @*/ /* FIX: check rc */
01505         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01506                 0, 0, NULL, urlNotifyData);
01507         /*@=noeffectuncon @*/
01508 /*@=boundsread@*/
01509     }
01510     
01511     while (1) {
01512         rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
01513         if (rc < 0)
01514             break;
01515         else if (rc == 0) {
01516             rc = itemsCopied;
01517             break;
01518         }
01519         itemsRead = rc;
01520         rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
01521         if (rc < 0)
01522             break;
01523         if (rc != itemsRead) {
01524             rc = FTPERR_FILE_IO_ERROR;
01525             break;
01526         }
01527 
01528         itemsCopied += itemsRead;
01529         if (urlNotify && urlNotifyCount > 0) {
01530             int n = itemsCopied/urlNotifyCount;
01531             if (n != notifier) {
01532 /*@-boundsread@*/
01533                 /*@-noeffectuncon @*/ /* FIX: check rc */
01534                 (void)(*urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
01535                         itemsCopied, 0, NULL, urlNotifyData);
01536                 /*@=noeffectuncon @*/
01537 /*@=boundsread@*/
01538                 notifier = n;
01539             }
01540         }
01541     }
01542 
01543     DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
01544         ftpStrerror(rc)));
01545 
01546     if (urlNotify) {
01547 /*@-boundsread@*/
01548         /*@-noeffectuncon @*/ /* FIX: check rc */
01549         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01550                 itemsCopied, itemsCopied, NULL, urlNotifyData);
01551         /*@=noeffectuncon @*/
01552 /*@=boundsread@*/
01553     }
01554     
01555     return rc;
01556 }
01557 
01558 static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
01559         /*@globals h_errno, fileSystem, internalState @*/
01560         /*@modifies *uret, fileSystem, internalState @*/
01561 {
01562     urlinfo u;
01563     int rc = 0;
01564 
01565     if (urlSplit(url, &u) < 0)
01566         return -1;
01567 
01568     if (u->urltype == URL_IS_FTP) {
01569         FD_t fd;
01570 
01571         if ((fd = u->ctrl) == NULL) {
01572             fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
01573             fdSetIo(u->ctrl, ufdio);
01574         }
01575         
01576         fd->rd_timeoutsecs = ftpTimeoutSecs;
01577         fd->contentLength = fd->bytesRemain = -1;
01578         fd->url = NULL;         /* XXX FTP ctrl has not */
01579         fd->ftpFileDoneNeeded = 0;
01580         fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
01581 
01582         if (fdFileno(u->ctrl) < 0) {
01583             rpmMessage(RPMMESS_DEBUG, _("logging into %s as %s, pw %s\n"),
01584                         u->host ? u->host : "???",
01585                         u->user ? u->user : "ftp",
01586                         u->password ? u->password : "(username)");
01587 
01588             if ((rc = ftpLogin(u)) < 0) {       /* XXX save ftpLogin error */
01589                 u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
01590                 u->openError = rc;
01591             }
01592         }
01593     }
01594 
01595 /*@-boundswrite@*/
01596     if (uret != NULL)
01597         *uret = urlLink(u, "urlConnect");
01598 /*@=boundswrite@*/
01599     u = urlFree(u, "urlSplit (urlConnect)");    
01600 
01601     return rc;
01602 }
01603 
01604 int ufdGetFile(FD_t sfd, FD_t tfd)
01605 {
01606     int rc;
01607 
01608     FDSANE(sfd);
01609     FDSANE(tfd);
01610     rc = ufdCopy(sfd, tfd);
01611     (void) Fclose(sfd);
01612     if (rc > 0)         /* XXX ufdCopy now returns no. bytes copied */
01613         rc = 0;
01614     return rc;
01615 }
01616 
01617 int ftpCmd(const char * cmd, const char * url, const char * arg2)
01618 {
01619     urlinfo u;
01620     int rc;
01621     const char * path;
01622 
01623     if (urlConnect(url, &u) < 0)
01624         return -1;
01625 
01626     (void) urlPath(url, &path);
01627 
01628     rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
01629     u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
01630     return rc;
01631 }
01632 
01633 /* XXX these aren't worth the pain of including correctly */
01634 #if !defined(IAC)
01635 #define IAC     255             /* interpret as command: */
01636 #endif
01637 #if !defined(IP)
01638 #define IP      244             /* interrupt process--permanently */
01639 #endif
01640 #if !defined(DM)
01641 #define DM      242             /* data mark--for connect. cleaning */
01642 #endif
01643 #if !defined(SHUT_RDWR)
01644 #define SHUT_RDWR       1+1
01645 #endif
01646 
01647 static int ftpAbort(urlinfo u, FD_t data)
01648         /*@globals fileSystem, internalState @*/
01649         /*@modifies u, data, fileSystem, internalState @*/
01650 {
01651     static unsigned char ipbuf[3] = { IAC, IP, IAC };
01652     FD_t ctrl;
01653     int rc;
01654     int tosecs;
01655 
01656     URLSANE(u);
01657 
01658     if (data != NULL) {
01659         data->ftpFileDoneNeeded = 0;
01660         if (fdFileno(data) >= 0)
01661             u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
01662         u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
01663     }
01664     ctrl = u->ctrl;
01665 
01666     DBGIO(0, (stderr, "-> ABOR\n"));
01667 
01668 /*@-usereleased -compdef@*/
01669     if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
01670         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01671         return FTPERR_SERVER_IO_ERROR;
01672     }
01673 
01674     sprintf(u->buf, "%cABOR\r\n",(char) DM);
01675     if (fdWrite(ctrl, u->buf, 7) != 7) {
01676         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01677         return FTPERR_SERVER_IO_ERROR;
01678     }
01679 
01680     if (data && fdFileno(data) >= 0) {
01681         /* XXX shorten data drain time wait */
01682         tosecs = data->rd_timeoutsecs;
01683         data->rd_timeoutsecs = 10;
01684         if (fdReadable(data, data->rd_timeoutsecs) > 0) {
01685 /*@-boundswrite@*/
01686             while (timedRead(data, u->buf, u->bufAlloced) > 0)
01687                 u->buf[0] = '\0';
01688 /*@=boundswrite@*/
01689         }
01690         data->rd_timeoutsecs = tosecs;
01691         /* XXX ftp abort needs to close the data channel to receive status */
01692         (void) shutdown(fdFileno(data), SHUT_RDWR);
01693         (void) close(fdFileno(data));
01694         data->fps[0].fdno = -1; /* XXX WRONG but expedient */
01695     }
01696 
01697     /* XXX shorten ctrl drain time wait */
01698     tosecs = u->ctrl->rd_timeoutsecs;
01699     u->ctrl->rd_timeoutsecs = 10;
01700     if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
01701         rc = ftpCheckResponse(u, NULL);
01702     }
01703     rc = ftpCheckResponse(u, NULL);
01704     u->ctrl->rd_timeoutsecs = tosecs;
01705 
01706     return rc;
01707 /*@=usereleased =compdef@*/
01708 }
01709 
01710 static int ftpFileDone(urlinfo u, FD_t data)
01711         /*@globals fileSystem @*/
01712         /*@modifies u, data, fileSystem @*/
01713 {
01714     int rc = 0;
01715 
01716     URLSANE(u);
01717     assert(data->ftpFileDoneNeeded);
01718 
01719     if (data->ftpFileDoneNeeded) {
01720         data->ftpFileDoneNeeded = 0;
01721         u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
01722         u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
01723         rc = ftpCheckResponse(u, NULL);
01724     }
01725     return rc;
01726 }
01727 
01728 static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
01729         /*@globals fileSystem @*/
01730         /*@modifies ctrl, *str, fileSystem @*/
01731 {
01732     int ec = 0;
01733     int rc;
01734 
01735     URLSANE(u);
01736     rc = checkResponse(u, ctrl, &ec, str);
01737 
01738 if (_ftp_debug && !(rc == 0 && (ec == 200 || ec == 201)))
01739 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
01740 
01741     switch (ec) {
01742     case 200:
01743     case 201:                   /* 201 Created. */
01744         break;
01745     case 204:                   /* HACK: if overwriting, 204 No Content. */
01746     case 403:                   /* 403 Forbidden. */
01747         ctrl->syserrno = EACCES;        /* HACK */
01748         rc = FTPERR_UNKNOWN;
01749         break;
01750     default:
01751         rc = FTPERR_FILE_NOT_FOUND;
01752         break;
01753     }
01754     return rc;
01755 }
01756 
01757 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
01758         /*@globals h_errno, fileSystem, internalState @*/
01759         /*@modifies ctrl, fileSystem, internalState @*/
01760 {
01761     urlinfo u;
01762     const char * host;
01763     const char * path;
01764     char hthost[NI_MAXHOST];
01765     int port;
01766     int rc;
01767     char * req;
01768     size_t len;
01769     int retrying = 0;
01770 
01771 assert(ctrl != NULL);
01772     u = ctrl->url;
01773     URLSANE(u);
01774 
01775     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
01776         return FTPERR_BAD_HOSTNAME;
01777     if (strchr(host, ':'))
01778         sprintf(hthost, "[%s]", host);
01779     else
01780         strcpy(hthost, host);
01781 
01782     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
01783     path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
01784     /*@-branchstate@*/
01785     if (path == NULL) path = "";
01786     /*@=branchstate@*/
01787 
01788 reopen:
01789     /*@-branchstate@*/
01790     if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
01791         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01792     }
01793     /*@=branchstate@*/
01794 
01795 /*@-usereleased@*/
01796     if (fdFileno(ctrl) < 0) {
01797         rc = tcpConnect(ctrl, host, port);
01798         if (rc < 0)
01799             goto errxit2;
01800         ctrl = fdLink(ctrl, "open ctrl (httpReq)");
01801     }
01802 
01803     len = sizeof("\
01804 req x HTTP/1.0\r\n\
01805 User-Agent: rpm/3.0.4\r\n\
01806 Host: y:z\r\n\
01807 Accept: text/plain\r\n\
01808 Transfer-Encoding: chunked\r\n\
01809 \r\n\
01810 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(hthost) + 20;
01811 
01812 /*@-boundswrite@*/
01813     req = alloca(len);
01814     *req = '\0';
01815 
01816   if (!strcmp(httpCmd, "PUT")) {
01817     sprintf(req, "\
01818 %s %s HTTP/1.%d\r\n\
01819 User-Agent: rpm/%s\r\n\
01820 Host: %s:%d\r\n\
01821 Accept: text/plain\r\n\
01822 Transfer-Encoding: chunked\r\n\
01823 \r\n\
01824 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
01825 } else {
01826     sprintf(req, "\
01827 %s %s HTTP/1.%d\r\n\
01828 User-Agent: rpm/%s\r\n\
01829 Host: %s:%d\r\n\
01830 Accept: text/plain\r\n\
01831 \r\n\
01832 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
01833 }
01834 /*@=boundswrite@*/
01835 
01836 if (_ftp_debug)
01837 fprintf(stderr, "-> %s", req);
01838 
01839     len = strlen(req);
01840     if (fdWrite(ctrl, req, len) != len) {
01841         rc = FTPERR_SERVER_IO_ERROR;
01842         goto errxit;
01843     }
01844 
01845     /*@-branchstate@*/
01846     if (!strcmp(httpCmd, "PUT")) {
01847         ctrl->wr_chunked = 1;
01848     } else {
01849 
01850         rc = httpResp(u, ctrl, NULL);
01851 
01852         if (rc) {
01853             if (!retrying) {    /* not HTTP_OK */
01854                 retrying = 1;
01855                 /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01856                 goto reopen;
01857             }
01858             goto errxit;
01859         }
01860     }
01861     /*@=branchstate@*/
01862 
01863     ctrl = fdLink(ctrl, "open data (httpReq)");
01864     return 0;
01865 
01866 errxit:
01867     /*@-observertrans@*/
01868     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
01869     /*@=observertrans@*/
01870 errxit2:
01871     /*@-branchstate@*/
01872     if (fdFileno(ctrl) >= 0)
01873         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01874     /*@=branchstate@*/
01875     return rc;
01876 /*@=usereleased@*/
01877 }
01878 
01879 /* XXX DYING: unused */
01880 void * ufdGetUrlinfo(FD_t fd)
01881 {
01882     FDSANE(fd);
01883     if (fd->url == NULL)
01884         return NULL;
01885     return urlLink(fd->url, "ufdGetUrlinfo");
01886 }
01887 
01888 /* =============================================================== */
01889 static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count)
01890         /*@globals fileSystem, internalState @*/
01891         /*@modifies buf, fileSystem, internalState @*/
01892         /*@requires maxSet(buf) >= (count - 1) @*/
01893         /*@ensures maxRead(buf) == result @*/
01894 {
01895     FD_t fd = c2f(cookie);
01896     int bytesRead;
01897     int total;
01898 
01899     /* XXX preserve timedRead() behavior */
01900     if (fdGetIo(fd) == fdio) {
01901         struct stat sb;
01902         int fdno = fdFileno(fd);
01903         (void) fstat(fdno, &sb);
01904         if (S_ISREG(sb.st_mode))
01905             return fdRead(fd, buf, count);
01906     }
01907 
01908     UFDONLY(fd);
01909     assert(fd->rd_timeoutsecs >= 0);
01910 
01911     for (total = 0; total < count; total += bytesRead) {
01912 
01913         int rc;
01914 
01915         bytesRead = 0;
01916 
01917         /* Is there data to read? */
01918         if (fd->bytesRemain == 0) return total; /* XXX simulate EOF */
01919         rc = fdReadable(fd, fd->rd_timeoutsecs);
01920 
01921         switch (rc) {
01922         case -1:        /* error */
01923         case  0:        /* timeout */
01924             return total;
01925             /*@notreached@*/ /*@switchbreak@*/ break;
01926         default:        /* data to read */
01927             /*@switchbreak@*/ break;
01928         }
01929 
01930 /*@-boundswrite@*/
01931         rc = fdRead(fd, buf + total, count - total);
01932 /*@=boundswrite@*/
01933 
01934         if (rc < 0) {
01935             switch (errno) {
01936             case EWOULDBLOCK:
01937                 continue;
01938                 /*@notreached@*/ /*@switchbreak@*/ break;
01939             default:
01940                 /*@switchbreak@*/ break;
01941             }
01942 if (_rpmio_debug)
01943 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
01944             return rc;
01945             /*@notreached@*/ break;
01946         } else if (rc == 0) {
01947             return total;
01948             /*@notreached@*/ break;
01949         }
01950         bytesRead = rc;
01951     }
01952 
01953     return count;
01954 }
01955 
01956 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
01957         /*@globals fileSystem, internalState @*/
01958         /*@modifies fileSystem, internalState @*/
01959 {
01960     FD_t fd = c2f(cookie);
01961     int bytesWritten;
01962     int total = 0;
01963 
01964 #ifdef  NOTYET
01965     if (fdGetIo(fd) == fdio) {
01966         struct stat sb;
01967         (void) fstat(fdGetFdno(fd), &sb);
01968         if (S_ISREG(sb.st_mode))
01969             return fdWrite(fd, buf, count);
01970     }
01971 #endif
01972 
01973     UFDONLY(fd);
01974 
01975     for (total = 0; total < count; total += bytesWritten) {
01976 
01977         int rc;
01978 
01979         bytesWritten = 0;
01980 
01981         /* Is there room to write data? */
01982         if (fd->bytesRemain == 0) {
01983 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
01984             return total;       /* XXX simulate EOF */
01985         }
01986         rc = fdWritable(fd, 2);         /* XXX configurable? */
01987 
01988         switch (rc) {
01989         case -1:        /* error */
01990         case  0:        /* timeout */
01991             return total;
01992             /*@notreached@*/ /*@switchbreak@*/ break;
01993         default:        /* data to write */
01994             /*@switchbreak@*/ break;
01995         }
01996 
01997         rc = fdWrite(fd, buf + total, count - total);
01998 
01999         if (rc < 0) {
02000             switch (errno) {
02001             case EWOULDBLOCK:
02002                 continue;
02003                 /*@notreached@*/ /*@switchbreak@*/ break;
02004             default:
02005                 /*@switchbreak@*/ break;
02006             }
02007 if (_rpmio_debug)
02008 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
02009             return rc;
02010             /*@notreached@*/ break;
02011         } else if (rc == 0) {
02012             return total;
02013             /*@notreached@*/ break;
02014         }
02015         bytesWritten = rc;
02016     }
02017 
02018     return count;
02019 }
02020 
02021 static inline int ufdSeek(void * cookie, _libio_pos_t pos, int whence)
02022         /*@globals fileSystem, internalState @*/
02023         /*@modifies fileSystem, internalState @*/
02024 {
02025     FD_t fd = c2f(cookie);
02026 
02027     switch (fd->urlType) {
02028     case URL_IS_UNKNOWN:
02029     case URL_IS_PATH:
02030         break;
02031     case URL_IS_HTTPS:
02032     case URL_IS_HTTP:
02033     case URL_IS_HKP:
02034     case URL_IS_FTP:
02035     case URL_IS_DASH:
02036     default:
02037         return -2;
02038         /*@notreached@*/ break;
02039     }
02040     return fdSeek(cookie, pos, whence);
02041 }
02042 
02043 /*@-branchstate@*/
02044 /*@-usereleased@*/      /* LCL: fd handling is tricky here. */
02045 int ufdClose( /*@only@*/ void * cookie)
02046 {
02047     FD_t fd = c2f(cookie);
02048 
02049     UFDONLY(fd);
02050 
02051     /*@-branchstate@*/
02052     if (fd->url) {
02053         urlinfo u = fd->url;
02054 
02055         if (fd == u->data)
02056                 fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
02057         else
02058                 fd = fdFree(fd, "grab data (ufdClose)");
02059         (void) urlFree(fd->url, "url (ufdClose)");
02060         fd->url = NULL;
02061         u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
02062 
02063         if (u->urltype == URL_IS_FTP) {
02064 
02065             /* XXX if not using libio, lose the fp from fpio */
02066             {   FILE * fp;
02067                 /*@+voidabstract -nullpass@*/
02068                 fp = fdGetFILE(fd);
02069                 if (noLibio && fp)
02070                     fdSetFp(fd, NULL);
02071                 /*@=voidabstract =nullpass@*/
02072             }
02073 
02074             /*
02075              * Non-error FTP has 4 refs on the data fd:
02076              *  "persist data (ufdOpen FTP)"            rpmio.c:888
02077              *  "grab data (ufdOpen FTP)"               rpmio.c:892
02078              *  "open data (ftpReq)"                    ftp.c:633
02079              *  "fopencookie"                           rpmio.c:1507
02080              *
02081              * Non-error FTP has 5 refs on the ctrl fd:
02082              *  "persist ctrl"                          url.c:176
02083              *  "grab ctrl (urlConnect FTP)"            rpmio.c:404
02084              *  "open ctrl"                             ftp.c:504
02085              *  "grab data (ftpReq)"                    ftp.c:661
02086              *  "open data (ftpReq)"                    ftp.c:662
02087              */
02088             if (fd->bytesRemain > 0) {
02089                 if (fd->ftpFileDoneNeeded) {
02090                     if (fdReadable(u->ctrl, 0) > 0)
02091                         (void) ftpFileDone(u, fd);
02092                     else
02093                         (void) ftpAbort(u, fd);
02094                 }
02095             } else {
02096                 int rc;
02097                 /* XXX STOR et al require close before ftpFileDone */
02098                 /*@-refcounttrans@*/
02099                 rc = fdClose(fd);
02100                 /*@=refcounttrans@*/
02101 #if 0   /* XXX error exit from ufdOpen does not have this set */
02102                 assert(fd->ftpFileDoneNeeded != 0);
02103 #endif
02104                 /*@-compdef@*/ /* FIX: u->data undefined */
02105                 if (fd->ftpFileDoneNeeded)
02106                     (void) ftpFileDone(u, fd);
02107                 /*@=compdef@*/
02108                 return rc;
02109             }
02110         }
02111 
02112         /* XXX Why not (u->urltype == URL_IS_HTTP) ??? */
02113         /* XXX Why not (u->urltype == URL_IS_HTTPS) ??? */
02114         /* XXX Why not (u->urltype == URL_IS_HKP) ??? */
02115         if (u->scheme != NULL
02116          && (!strncmp(u->scheme, "http", sizeof("http")-1) || !strncmp(u->scheme, "hkp", sizeof("hkp")-1)))
02117         {
02118             /*
02119              * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
02120              *  "persist ctrl"                          url.c:177
02121              *  "grab ctrl (ufdOpen HTTP)"              rpmio.c:924
02122              *  "grab data (ufdOpen HTTP)"              rpmio.c:928
02123              *  "open ctrl (httpReq)"                   ftp.c:382
02124              *  "open data (httpReq)"                   ftp.c:435
02125              */
02126 
02127             if (fd == u->ctrl)
02128                 fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
02129             else if (fd == u->data)
02130                 fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
02131             else
02132                 fd = fdFree(fd, "open data (ufdClose HTTP)");
02133 
02134             /* XXX if not using libio, lose the fp from fpio */
02135             {   FILE * fp;
02136                 /*@+voidabstract -nullpass@*/
02137                 fp = fdGetFILE(fd);
02138                 if (noLibio && fp)
02139                     fdSetFp(fd, NULL);
02140                 /*@=voidabstract =nullpass@*/
02141             }
02142 
02143             /* If content remains, then don't persist. */
02144             if (fd->bytesRemain > 0)
02145                 fd->persist = 0;
02146             fd->contentLength = fd->bytesRemain = -1;
02147 
02148             /* If persisting, then Fclose will juggle refcounts. */
02149             if (fd->persist && (fd == u->ctrl || fd == u->data))
02150                 return 0;
02151         }
02152     }
02153     return fdClose(fd);
02154 }
02155 /*@=usereleased@*/
02156 /*@=branchstate@*/
02157 
02158 /*@-nullstate@*/        /* FIX: u->{ctrl,data}->url undef after XurlLink. */
02159 /*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
02160                 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
02161         /*@modifies *uret @*/
02162 {
02163     urlinfo u = NULL;
02164     FD_t fd = NULL;
02165 
02166 #if 0   /* XXX makeTempFile() heartburn */
02167     assert(!(flags & O_RDWR));
02168 #endif
02169     if (urlConnect(url, &u) < 0)
02170         goto exit;
02171 
02172     if (u->data == NULL)
02173         u->data = fdNew("persist data (ftpOpen)");
02174 
02175     if (u->data->url == NULL)
02176         fd = fdLink(u->data, "grab data (ftpOpen persist data)");
02177     else
02178         fd = fdNew("grab data (ftpOpen)");
02179 
02180     if (fd) {
02181         fdSetIo(fd, ufdio);
02182         fd->ftpFileDoneNeeded = 0;
02183         fd->rd_timeoutsecs = ftpTimeoutSecs;
02184         fd->contentLength = fd->bytesRemain = -1;
02185         fd->url = urlLink(u, "url (ufdOpen FTP)");
02186         fd->urlType = URL_IS_FTP;
02187     }
02188 
02189 exit:
02190 /*@-boundswrite@*/
02191     if (uret)
02192         *uret = u;
02193 /*@=boundswrite@*/
02194     /*@-refcounttrans@*/
02195     return fd;
02196     /*@=refcounttrans@*/
02197 }
02198 /*@=nullstate@*/
02199 
02200 #ifndef WITH_NEON
02201 /*@-nullstate@*/        /* FIX: u->{ctrl,data}->url undef after XurlLink. */
02202 static /*@null@*/ FD_t httpOpen(const char * url, /*@unused@*/ int flags,
02203                 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo * uret)
02204         /*@globals internalState @*/
02205         /*@modifies *uret, internalState @*/
02206 {
02207     urlinfo u = NULL;
02208     FD_t fd = NULL;
02209 
02210 #if 0   /* XXX makeTempFile() heartburn */
02211     assert(!(flags & O_RDWR));
02212 #endif
02213     if (urlSplit(url, &u))
02214         goto exit;
02215 
02216     if (u->ctrl == NULL)
02217         u->ctrl = fdNew("persist ctrl (httpOpen)");
02218     if (u->ctrl->nrefs > 2 && u->data == NULL)
02219         u->data = fdNew("persist data (httpOpen)");
02220 
02221     if (u->ctrl->url == NULL)
02222         fd = fdLink(u->ctrl, "grab ctrl (httpOpen persist ctrl)");
02223     else if (u->data->url == NULL)
02224         fd = fdLink(u->data, "grab ctrl (httpOpen persist data)");
02225     else
02226         fd = fdNew("grab ctrl (httpOpen)");
02227 
02228     if (fd) {
02229         fdSetIo(fd, ufdio);
02230         fd->ftpFileDoneNeeded = 0;
02231         fd->rd_timeoutsecs = httpTimeoutSecs;
02232         fd->contentLength = fd->bytesRemain = -1;
02233         fd->url = urlLink(u, "url (httpOpen)");
02234         fd = fdLink(fd, "grab data (httpOpen)");
02235         fd->urlType = URL_IS_HTTP;
02236     }
02237 
02238 exit:
02239 /*@-boundswrite@*/
02240     if (uret)
02241         *uret = u;
02242 /*@=boundswrite@*/
02243     /*@-refcounttrans@*/
02244     return fd;
02245     /*@=refcounttrans@*/
02246 }
02247 /*@=nullstate@*/
02248 #endif
02249 
02250 static /*@null@*/ FD_t ufdOpen(const char * url, int flags, mode_t mode)
02251         /*@globals h_errno, fileSystem, internalState @*/
02252         /*@modifies fileSystem, internalState @*/
02253 {
02254     FD_t fd = NULL;
02255     const char * cmd;
02256     urlinfo u;
02257     const char * path;
02258     urltype urlType = urlPath(url, &path);
02259 
02260 if (_rpmio_debug)
02261 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
02262 
02263     /*@-branchstate@*/
02264     switch (urlType) {
02265     case URL_IS_FTP:
02266         fd = ftpOpen(url, flags, mode, &u);
02267         if (fd == NULL || u == NULL)
02268             break;
02269 
02270         /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
02271         cmd = ((flags & O_WRONLY) 
02272                 ?  ((flags & O_APPEND) ? "APPE" :
02273                    ((flags & O_CREAT) ? "STOR" : "STOR"))
02274                 :  ((flags & O_CREAT) ? "STOR" : "RETR"));
02275         u->openError = ftpReq(fd, cmd, path);
02276         if (u->openError < 0) {
02277             /* XXX make sure that we can exit through ufdClose */
02278             fd = fdLink(fd, "error data (ufdOpen FTP)");
02279         } else {
02280             fd->bytesRemain = ((!strcmp(cmd, "RETR"))
02281                 ?  fd->contentLength : -1);
02282             fd->wr_chunked = 0;
02283         }
02284         break;
02285     case URL_IS_HTTPS:
02286     case URL_IS_HTTP:
02287     case URL_IS_HKP:
02288 #ifdef WITH_NEON
02289         fd = davOpen(url, flags, mode, &u);
02290 #else
02291         fd = httpOpen(url, flags, mode, &u);
02292 #endif
02293         if (fd == NULL || u == NULL)
02294             break;
02295 
02296         cmd = ((flags & O_WRONLY)
02297                 ?  ((flags & O_APPEND) ? "PUT" :
02298                    ((flags & O_CREAT) ? "PUT" : "PUT"))
02299                 : "GET");
02300 #ifdef WITH_NEON
02301         u->openError = davReq(fd, cmd, path);
02302 #else
02303         u->openError = httpReq(fd, cmd, path);
02304 #endif
02305         if (u->openError < 0) {
02306             /* XXX make sure that we can exit through ufdClose */
02307             fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
02308             fd = fdLink(fd, "error data (ufdOpen HTTP)");
02309         } else {
02310             fd->bytesRemain = ((!strcmp(cmd, "GET"))
02311                 ?  fd->contentLength : -1);
02312             fd->wr_chunked = ((!strcmp(cmd, "PUT"))
02313                 ?  fd->wr_chunked : 0);
02314         }
02315         break;
02316     case URL_IS_DASH:
02317         assert(!(flags & O_RDWR));
02318         fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
02319         if (fd) {
02320             fdSetIo(fd, ufdio);
02321             fd->rd_timeoutsecs = 600;   /* XXX W2DO? 10 mins? */
02322             fd->contentLength = fd->bytesRemain = -1;
02323         }
02324         break;
02325     case URL_IS_PATH:
02326     case URL_IS_UNKNOWN:
02327     default:
02328         fd = fdOpen(path, flags, mode);
02329         if (fd) {
02330             fdSetIo(fd, ufdio);
02331             fd->rd_timeoutsecs = 60;
02332             fd->contentLength = fd->bytesRemain = -1;
02333         }
02334         break;
02335     }
02336     /*@=branchstate@*/
02337 
02338     if (fd == NULL) return NULL;
02339     fd->urlType = urlType;
02340     if (Fileno(fd) < 0) {
02341         (void) ufdClose(fd);
02342         return NULL;
02343     }
02344 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
02345     return fd;
02346 }
02347 
02348 /*@-type@*/ /* LCL: function typedefs */
02349 static struct FDIO_s ufdio_s = {
02350   ufdRead, ufdWrite, ufdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
02351   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
02352 };
02353 /*@=type@*/
02354 FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
02355 
02356 /* =============================================================== */
02357 /* Support for GZIP library.
02358  */
02359 #ifdef  HAVE_ZLIB_H
02360 /*@-moduncon@*/
02361 
02362 /*@-noparams@*/
02363 #include <zlib.h>
02364 /*@=noparams@*/
02365 
02366 static inline /*@dependent@*/ /*@null@*/ void * gzdFileno(FD_t fd)
02367         /*@*/
02368 {
02369     void * rc = NULL;
02370     int i;
02371 
02372     FDSANE(fd);
02373     for (i = fd->nfps; i >= 0; i--) {
02374 /*@-boundsread@*/
02375         FDSTACK_t * fps = &fd->fps[i];
02376 /*@=boundsread@*/
02377         if (fps->io != gzdio)
02378             continue;
02379         rc = fps->fp;
02380         break;
02381     }
02382     
02383     return rc;
02384 }
02385 
02386 static /*@null@*/
02387 FD_t gzdOpen(const char * path, const char * fmode)
02388         /*@globals fileSystem, internalState @*/
02389         /*@modifies fileSystem, internalState @*/
02390 {
02391     FD_t fd;
02392     gzFile gzfile;
02393     if ((gzfile = gzopen(path, fmode)) == NULL)
02394         return NULL;
02395     fd = fdNew("open (gzdOpen)");
02396     fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
02397     
02398 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
02399     return fdLink(fd, "gzdOpen");
02400 }
02401 
02402 static /*@null@*/ FD_t gzdFdopen(void * cookie, const char *fmode)
02403         /*@globals fileSystem, internalState @*/
02404         /*@modifies fileSystem, internalState @*/
02405 {
02406     FD_t fd = c2f(cookie);
02407     int fdno;
02408     gzFile gzfile;
02409 
02410     if (fmode == NULL) return NULL;
02411     fdno = fdFileno(fd);
02412     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
02413     if (fdno < 0) return NULL;
02414     gzfile = gzdopen(fdno, fmode);
02415     if (gzfile == NULL) return NULL;
02416 
02417     fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
02418 
02419     return fdLink(fd, "gzdFdopen");
02420 }
02421 
02422 static int gzdFlush(FD_t fd)
02423         /*@globals fileSystem @*/
02424         /*@modifies fileSystem @*/
02425 {
02426     gzFile gzfile;
02427     gzfile = gzdFileno(fd);
02428     if (gzfile == NULL) return -2;
02429     return gzflush(gzfile, Z_SYNC_FLUSH);       /* XXX W2DO? */
02430 }
02431 
02432 /* =============================================================== */
02433 static ssize_t gzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
02434         /*@globals fileSystem, internalState @*/
02435         /*@modifies buf, fileSystem, internalState @*/
02436 {
02437     FD_t fd = c2f(cookie);
02438     gzFile gzfile;
02439     ssize_t rc;
02440 
02441     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
02442 
02443     gzfile = gzdFileno(fd);
02444     if (gzfile == NULL) return -2;      /* XXX can't happen */
02445 
02446     fdstat_enter(fd, FDSTAT_READ);
02447     rc = gzread(gzfile, buf, count);
02448 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
02449     if (rc < 0) {
02450         int zerror = 0;
02451         fd->errcookie = gzerror(gzfile, &zerror);
02452         if (zerror == Z_ERRNO) {
02453             fd->syserrno = errno;
02454             fd->errcookie = strerror(fd->syserrno);
02455         }
02456     } else if (rc >= 0) {
02457         fdstat_exit(fd, FDSTAT_READ, rc);
02458         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, buf, rc);
02459     }
02460     return rc;
02461 }
02462 
02463 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
02464         /*@globals fileSystem, internalState @*/
02465         /*@modifies fileSystem, internalState @*/
02466 {
02467     FD_t fd = c2f(cookie);
02468     gzFile gzfile;
02469     ssize_t rc;
02470 
02471     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
02472 
02473     if (fd->ndigests && count > 0) fdUpdateDigests(fd, buf, count);
02474 
02475     gzfile = gzdFileno(fd);
02476     if (gzfile == NULL) return -2;      /* XXX can't happen */
02477 
02478     fdstat_enter(fd, FDSTAT_WRITE);
02479     rc = gzwrite(gzfile, (void *)buf, count);
02480 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
02481     if (rc < 0) {
02482         int zerror = 0;
02483         fd->errcookie = gzerror(gzfile, &zerror);
02484         if (zerror == Z_ERRNO) {
02485             fd->syserrno = errno;
02486             fd->errcookie = strerror(fd->syserrno);
02487         }
02488     } else if (rc > 0) {
02489         fdstat_exit(fd, FDSTAT_WRITE, rc);
02490     }
02491     return rc;
02492 }
02493 
02494 /* XXX zlib-1.0.4 has not */
02495 static inline int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
02496         /*@globals fileSystem, internalState @*/
02497         /*@modifies fileSystem, internalState @*/
02498 {
02499 #ifdef USE_COOKIE_SEEK_POINTER
02500     _IO_off64_t p = *pos;
02501 #else
02502     off_t p = pos;
02503 #endif
02504     int rc;
02505 #if HAVE_GZSEEK
02506     FD_t fd = c2f(cookie);
02507     gzFile gzfile;
02508 
02509     if (fd == NULL) return -2;
02510     assert(fd->bytesRemain == -1);      /* XXX FIXME */
02511 
02512     gzfile = gzdFileno(fd);
02513     if (gzfile == NULL) return -2;      /* XXX can't happen */
02514 
02515     fdstat_enter(fd, FDSTAT_SEEK);
02516     rc = gzseek(gzfile, p, whence);
02517 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
02518     if (rc < 0) {
02519         int zerror = 0;
02520         fd->errcookie = gzerror(gzfile, &zerror);
02521         if (zerror == Z_ERRNO) {
02522             fd->syserrno = errno;
02523             fd->errcookie = strerror(fd->syserrno);
02524         }
02525     } else if (rc >= 0) {
02526         fdstat_exit(fd, FDSTAT_SEEK, rc);
02527     }
02528 #else
02529     rc = -2;
02530 #endif
02531     return rc;
02532 }
02533 
02534 static int gzdClose( /*@only@*/ void * cookie)
02535         /*@globals fileSystem, internalState @*/
02536         /*@modifies fileSystem, internalState @*/
02537 {
02538     FD_t fd = c2f(cookie);
02539     gzFile gzfile;
02540     int rc;
02541 
02542     gzfile = gzdFileno(fd);
02543     if (gzfile == NULL) return -2;      /* XXX can't happen */
02544 
02545     fdstat_enter(fd, FDSTAT_CLOSE);
02546     /*@-dependenttrans@*/
02547     rc = gzclose(gzfile);
02548     /*@=dependenttrans@*/
02549 
02550     /* XXX TODO: preserve fd if errors */
02551 
02552     if (fd) {
02553 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
02554         if (rc < 0) {
02555             fd->errcookie = "gzclose error";
02556             if (rc == Z_ERRNO) {
02557                 fd->syserrno = errno;
02558                 fd->errcookie = strerror(fd->syserrno);
02559             }
02560         } else if (rc >= 0) {
02561             fdstat_exit(fd, FDSTAT_CLOSE, rc);
02562         }
02563     }
02564 
02565 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
02566 
02567     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
02568     /*@-branchstate@*/
02569     if (rc == 0)
02570         fd = fdFree(fd, "open (gzdClose)");
02571     /*@=branchstate@*/
02572     return rc;
02573 }
02574 
02575 /*@-type@*/ /* LCL: function typedefs */
02576 static struct FDIO_s gzdio_s = {
02577   gzdRead, gzdWrite, gzdSeek, gzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
02578   NULL, gzdOpen, gzdFileno, gzdFlush,   NULL, NULL, NULL, NULL, NULL
02579 };
02580 /*@=type@*/
02581 FDIO_t gzdio = /*@-compmempass@*/ &gzdio_s /*@=compmempass@*/ ;
02582 
02583 /*@=moduncon@*/
02584 #endif  /* HAVE_ZLIB_H */
02585 
02586 /* =============================================================== */
02587 /* Support for BZIP2 library.
02588  */
02589 #if HAVE_BZLIB_H
02590 /*@-moduncon@*/
02591 
02592 #include <bzlib.h>
02593 
02594 #ifdef HAVE_BZ2_1_0
02595 # define bzopen  BZ2_bzopen
02596 # define bzclose BZ2_bzclose
02597 # define bzdopen BZ2_bzdopen
02598 # define bzerror BZ2_bzerror
02599 # define bzflush BZ2_bzflush
02600 # define bzread  BZ2_bzread
02601 # define bzwrite BZ2_bzwrite
02602 #endif /* HAVE_BZ2_1_0 */
02603 
02604 static inline /*@dependent@*/ void * bzdFileno(FD_t fd)
02605         /*@*/
02606 {
02607     void * rc = NULL;
02608     int i;
02609 
02610     FDSANE(fd);
02611     for (i = fd->nfps; i >= 0; i--) {
02612 /*@-boundsread@*/
02613         FDSTACK_t * fps = &fd->fps[i];
02614 /*@=boundsread@*/
02615         if (fps->io != bzdio)
02616             continue;
02617         rc = fps->fp;
02618         break;
02619     }
02620     
02621     return rc;
02622 }
02623 
02624 /*@-globuse@*/
02625 static /*@null@*/ FD_t bzdOpen(const char * path, const char * mode)
02626         /*@globals fileSystem @*/
02627         /*@modifies fileSystem @*/
02628 {
02629     FD_t fd;
02630     BZFILE *bzfile;;
02631     if ((bzfile = bzopen(path, mode)) == NULL)
02632         return NULL;
02633     fd = fdNew("open (bzdOpen)");
02634     fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
02635     return fdLink(fd, "bzdOpen");
02636 }
02637 /*@=globuse@*/
02638 
02639 /*@-globuse@*/
02640 static /*@null@*/ FD_t bzdFdopen(void * cookie, const char * fmode)
02641         /*@globals fileSystem, internalState @*/
02642         /*@modifies fileSystem, internalState @*/
02643 {
02644     FD_t fd = c2f(cookie);
02645     int fdno;
02646     BZFILE *bzfile;
02647 
02648     if (fmode == NULL) return NULL;
02649     fdno = fdFileno(fd);
02650     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
02651     if (fdno < 0) return NULL;
02652     bzfile = bzdopen(fdno, fmode);
02653     if (bzfile == NULL) return NULL;
02654 
02655     fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
02656 
02657     return fdLink(fd, "bzdFdopen");
02658 }
02659 /*@=globuse@*/
02660 
02661 /*@-globuse@*/
02662 static int bzdFlush(FD_t fd)
02663         /*@globals fileSystem @*/
02664         /*@modifies fileSystem @*/
02665 {
02666     return bzflush(bzdFileno(fd));
02667 }
02668 /*@=globuse@*/
02669 
02670 /* =============================================================== */
02671 /*@-globuse@*/
02672 /*@-mustmod@*/          /* LCL: *buf is modified */
02673 static ssize_t bzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
02674         /*@globals fileSystem, internalState @*/
02675         /*@modifies *buf, fileSystem, internalState @*/
02676 {
02677     FD_t fd = c2f(cookie);
02678     BZFILE *bzfile;
02679     ssize_t rc = 0;
02680 
02681     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
02682     bzfile = bzdFileno(fd);
02683     fdstat_enter(fd, FDSTAT_READ);
02684     if (bzfile)
02685         /*@-compdef@*/
02686         rc = bzread(bzfile, buf, count);
02687         /*@=compdef@*/
02688     if (rc == -1) {
02689         int zerror = 0;
02690         if (bzfile)
02691             fd->errcookie = bzerror(bzfile, &zerror);
02692     } else if (rc >= 0) {
02693         fdstat_exit(fd, FDSTAT_READ, rc);
02694         /*@-compdef@*/
02695         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, buf, rc);
02696         /*@=compdef@*/
02697     }
02698     return rc;
02699 }
02700 /*@=mustmod@*/
02701 /*@=globuse@*/
02702 
02703 /*@-globuse@*/
02704 static ssize_t bzdWrite(void * cookie, const char * buf, size_t count)
02705         /*@globals fileSystem, internalState @*/
02706         /*@modifies fileSystem, internalState @*/
02707 {
02708     FD_t fd = c2f(cookie);
02709     BZFILE *bzfile;
02710     ssize_t rc;
02711 
02712     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
02713 
02714     if (fd->ndigests && count > 0) fdUpdateDigests(fd, buf, count);
02715 
02716     bzfile = bzdFileno(fd);
02717     fdstat_enter(fd, FDSTAT_WRITE);
02718     rc = bzwrite(bzfile, (void *)buf, count);
02719     if (rc == -1) {
02720         int zerror = 0;
02721         fd->errcookie = bzerror(bzfile, &zerror);
02722     } else if (rc > 0) {
02723         fdstat_exit(fd, FDSTAT_WRITE, rc);
02724     }
02725     return rc;
02726 }
02727 /*@=globuse@*/
02728 
02729 static inline int bzdSeek(void * cookie, /*@unused@*/ _libio_pos_t pos,
02730                         /*@unused@*/ int whence)
02731         /*@*/
02732 {
02733     FD_t fd = c2f(cookie);
02734 
02735     BZDONLY(fd);
02736     return -2;
02737 }
02738 
02739 static int bzdClose( /*@only@*/ void * cookie)
02740         /*@globals fileSystem, internalState @*/
02741         /*@modifies fileSystem, internalState @*/
02742 {
02743     FD_t fd = c2f(cookie);
02744     BZFILE *bzfile;
02745     int rc;
02746 
02747     bzfile = bzdFileno(fd);
02748 
02749     if (bzfile == NULL) return -2;
02750     fdstat_enter(fd, FDSTAT_CLOSE);
02751     /*@-noeffectuncon@*/ /* FIX: check rc */
02752     bzclose(bzfile);
02753     /*@=noeffectuncon@*/
02754     rc = 0;     /* XXX FIXME */
02755 
02756     /* XXX TODO: preserve fd if errors */
02757 
02758     if (fd) {
02759         if (rc == -1) {
02760             int zerror = 0;
02761             fd->errcookie = bzerror(bzfile, &zerror);
02762         } else if (rc >= 0) {
02763             fdstat_exit(fd, FDSTAT_CLOSE, rc);
02764         }
02765     }
02766 
02767 DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
02768 
02769     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
02770     /*@-branchstate@*/
02771     if (rc == 0)
02772         fd = fdFree(fd, "open (bzdClose)");
02773     /*@=branchstate@*/
02774     return rc;
02775 }
02776 
02777 /*@-type@*/ /* LCL: function typedefs */
02778 static struct FDIO_s bzdio_s = {
02779   bzdRead, bzdWrite, bzdSeek, bzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
02780   NULL, bzdOpen, bzdFileno, bzdFlush,   NULL, NULL, NULL, NULL, NULL
02781 };
02782 /*@=type@*/
02783 FDIO_t bzdio = /*@-compmempass@*/ &bzdio_s /*@=compmempass@*/ ;
02784 
02785 /*@=moduncon@*/
02786 #endif  /* HAVE_BZLIB_H */
02787 
02788 #include <sys/types.h>
02789 #include <inttypes.h>
02790 #include <lzma.h>
02791 
02792 #define kBufferSize (1 << 15)
02793 
02794 typedef struct lzfile {
02795   /* IO buffer */
02796     uint8_t buf[kBufferSize];
02797 
02798     lzma_stream strm;
02799 
02800     FILE *file;
02801 
02802     int encoding;
02803     int eof;
02804 
02805 } LZFILE;
02806 
02807 static LZFILE *lzopen_internal(const char *path, const char *mode, int fd)
02808 {
02809     int level = 5;
02810     int encoding = 0;
02811     FILE *fp;
02812     LZFILE *lzfile;
02813     lzma_ret ret;
02814 
02815     for (; *mode; mode++) {
02816         if (*mode == 'w')
02817             encoding = 1;
02818         else if (*mode == 'r')
02819             encoding = 0;
02820         else if (*mode >= '1' && *mode <= '9')
02821             level = *mode - '0';
02822     }
02823     if (fd != -1)
02824         fp = fdopen(fd, encoding ? "w" : "r");
02825     else
02826         fp = fopen(path, encoding ? "w" : "r");
02827     if (!fp)
02828         return 0;
02829     lzfile = calloc(1, sizeof(*lzfile));
02830     if (!lzfile) {
02831         fclose(fp);
02832         return 0;
02833     }
02834     lzfile->file = fp;
02835     lzfile->encoding = encoding;
02836     lzfile->eof = 0;
02837     lzfile->strm = LZMA_STREAM_INIT_VAR;
02838     if (encoding) {
02839         lzma_options_alone alone;
02840         alone.uncompressed_size = LZMA_VLI_VALUE_UNKNOWN;
02841         memcpy(&alone.lzma, &lzma_preset_lzma[level - 1], sizeof(alone.lzma));
02842         ret = lzma_alone_encoder(&lzfile->strm, &alone);
02843     } else {
02844         ret = lzma_auto_decoder(&lzfile->strm, 0, 0);
02845     }
02846     if (ret != LZMA_OK) {
02847         fclose(fp);
02848         free(lzfile);
02849         return 0;
02850     }
02851     return lzfile;
02852 }
02853 
02854 static LZFILE *lzopen(const char *path, const char *mode)
02855 {
02856     return lzopen_internal(path, mode, -1);
02857 }
02858 
02859 static LZFILE *lzdopen(int fd, const char *mode)
02860 {
02861     if (fd < 0)
02862         return 0;
02863     return lzopen_internal(0, mode, fd);
02864 }
02865 
02866 static int lzflush(LZFILE *lzfile)
02867 {
02868     return fflush(lzfile->file);
02869 }
02870 
02871 static int lzclose(LZFILE *lzfile)
02872 {
02873     lzma_ret ret;
02874     int n;
02875 
02876     if (!lzfile)
02877         return -1;
02878     if (lzfile->encoding) {
02879         for (;;) {
02880             lzfile->strm.avail_out = kBufferSize;
02881             lzfile->strm.next_out = lzfile->buf;
02882             ret = lzma_code(&lzfile->strm, LZMA_FINISH);
02883             if (ret != LZMA_OK && ret != LZMA_STREAM_END)
02884                 return -1;
02885             n = kBufferSize - lzfile->strm.avail_out;
02886             if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
02887                 return -1;
02888             if (ret == LZMA_STREAM_END)
02889                 break;
02890         }
02891     }
02892     lzma_end(&lzfile->strm);
02893     return fclose(lzfile->file);
02894     free(lzfile);
02895 }
02896 
02897 static ssize_t lzread(LZFILE *lzfile, void *buf, size_t len)
02898 {
02899     lzma_ret ret;
02900     int eof = 0;
02901 
02902     if (!lzfile || lzfile->encoding)
02903       return -1;
02904     if (lzfile->eof)
02905       return 0;
02906     lzfile->strm.next_out = buf;
02907     lzfile->strm.avail_out = len;
02908     for (;;) {
02909         if (!lzfile->strm.avail_in) {
02910             lzfile->strm.next_in = lzfile->buf;
02911             lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file);
02912             if (!lzfile->strm.avail_in)
02913                 eof = 1;
02914         }
02915         ret = lzma_code(&lzfile->strm, LZMA_RUN);
02916         if (ret == LZMA_STREAM_END) {
02917             lzfile->eof = 1;
02918             return len - lzfile->strm.avail_out;
02919         }
02920         if (ret != LZMA_OK)
02921             return -1;
02922         if (!lzfile->strm.avail_out)
02923             return len;
02924         if (eof)
02925             return -1;
02926       }
02927 }
02928 
02929 static ssize_t lzwrite(LZFILE *lzfile, void *buf, size_t len)
02930 {
02931     lzma_ret ret;
02932     int n;
02933     if (!lzfile || !lzfile->encoding)
02934         return -1;
02935     if (!len)
02936         return 0;
02937     lzfile->strm.next_in = buf;
02938     lzfile->strm.avail_in = len;
02939     for (;;) {
02940         lzfile->strm.next_out = lzfile->buf;
02941         lzfile->strm.avail_out = kBufferSize;
02942         ret = lzma_code(&lzfile->strm, LZMA_RUN);
02943         if (ret != LZMA_OK)
02944             return -1;
02945         n = kBufferSize - lzfile->strm.avail_out;
02946         if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
02947             return -1;
02948         if (!lzfile->strm.avail_in)
02949             return len;
02950     }
02951 }
02952 
02953 /* =============================================================== */
02954 
02955 static inline /*@dependent@*/ void * lzdFileno(FD_t fd)
02956         /*@*/
02957 {
02958     void * rc = NULL;
02959     int i;
02960 
02961     FDSANE(fd);
02962     for (i = fd->nfps; i >= 0; i--) {
02963 /*@-boundsread@*/
02964             FDSTACK_t * fps = &fd->fps[i];
02965 /*@=boundsread@*/
02966             if (fps->io != lzdio)
02967                 continue;
02968             rc = fps->fp;
02969         break;
02970     }
02971     
02972     return rc;
02973 }
02974 
02975 /*@-globuse@*/
02976 static /*@null@*/ FD_t lzdOpen(const char * path, const char * mode)
02977         /*@globals fileSystem @*/
02978         /*@modifies fileSystem @*/
02979 {
02980     FD_t fd;
02981     LZFILE *lzfile;
02982     if ((lzfile = lzopen(path, mode)) == NULL)
02983         return NULL;
02984     fd = fdNew("open (lzdOpen)");
02985     fdPop(fd); fdPush(fd, lzdio, lzfile, -1);
02986     return fdLink(fd, "lzdOpen");
02987 }
02988 /*@=globuse@*/
02989 
02990 
02991 /*@-globuse@*/
02992 static /*@null@*/ FD_t lzdFdopen(void * cookie, const char * fmode)
02993         /*@globals fileSystem, internalState @*/
02994         /*@modifies fileSystem, internalState @*/
02995 {
02996     FD_t fd = c2f(cookie);
02997     int fdno;
02998     LZFILE *lzfile;
02999 
03000     if (fmode == NULL) return NULL;
03001     fdno = fdFileno(fd);
03002     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
03003     if (fdno < 0) return NULL;
03004     lzfile = lzdopen(fdno, fmode);
03005     if (lzfile == NULL) return NULL;
03006     fdPush(fd, lzdio, lzfile, fdno);
03007     return fdLink(fd, "lzdFdopen");
03008 }
03009 /*@=globuse@*/
03010 
03011 /*@-globuse@*/
03012 static int lzdFlush(FD_t fd)
03013         /*@globals fileSystem @*/
03014         /*@modifies fileSystem @*/
03015 {
03016     return lzflush(lzdFileno(fd));
03017 }
03018 /*@=globuse@*/
03019 
03020 /* =============================================================== */
03021 /*@-globuse@*/
03022 /*@-mustmod@*/          /* LCL: *buf is modified */
03023 static ssize_t lzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
03024         /*@globals fileSystem, internalState @*/
03025         /*@modifies *buf, fileSystem, internalState @*/
03026 {
03027     FD_t fd = c2f(cookie);
03028     LZFILE *lzfile;
03029     ssize_t rc = 0;
03030 
03031     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
03032     lzfile = lzdFileno(fd);
03033     fdstat_enter(fd, FDSTAT_READ);
03034     if (lzfile)
03035         /*@-compdef@*/
03036         rc = lzread(lzfile, buf, count);
03037         /*@=compdef@*/
03038     if (rc == -1) {
03039         fd->errcookie = "Lzma: decoding error";
03040     } else if (rc >= 0) {
03041         fdstat_exit(fd, FDSTAT_READ, rc);
03042         /*@-compdef@*/
03043         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
03044         /*@=compdef@*/
03045     }
03046     return rc;
03047 }
03048 /*@=mustmod@*/
03049 /*@=globuse@*/
03050 
03051 /*@-globuse@*/
03052 static ssize_t lzdWrite(void * cookie, const char * buf, size_t count)
03053 /*@globals fileSystem, internalState @*/
03054 /*@modifies fileSystem, internalState @*/
03055 {
03056     FD_t fd = c2f(cookie);
03057     LZFILE *lzfile;
03058     ssize_t rc = 0;
03059 
03060     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
03061 
03062     if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
03063 
03064     lzfile = lzdFileno(fd);
03065 
03066     fdstat_enter(fd, FDSTAT_WRITE);
03067     rc = lzwrite(lzfile, (void *)buf, count);
03068     if (rc < 0) {
03069         fd->errcookie = "Lzma: encoding error";
03070     } else if (rc > 0) {
03071         fdstat_exit(fd, FDSTAT_WRITE, rc);
03072     }
03073     return rc;
03074 }
03075 
03076 static inline int lzdSeek(void * cookie, /*@unused@*/ _libio_pos_t pos,
03077                         /*@unused@*/ int whence)
03078         /*@*/
03079 {
03080     FD_t fd = c2f(cookie);
03081 
03082     LZDONLY(fd);
03083     return -2;
03084 }
03085 
03086 static int lzdClose( /*@only@*/ void * cookie)
03087         /*@globals fileSystem, internalState @*/
03088         /*@modifies fileSystem, internalState @*/
03089 {
03090     FD_t fd = c2f(cookie);
03091     LZFILE *lzfile;
03092     int rc;
03093 
03094     lzfile = lzdFileno(fd);
03095 
03096     if (lzfile == NULL) return -2;
03097     fdstat_enter(fd, FDSTAT_CLOSE);
03098     /*@-dependenttrans@*/
03099     rc = lzclose(lzfile);
03100     /*@=dependenttrans@*/
03101 
03102     /* XXX TODO: preserve fd if errors */
03103 
03104     if (fd) {
03105         if (rc == -1) {
03106             fd->errcookie = strerror(ferror(lzfile->file));
03107         } else if (rc >= 0) {
03108             fdstat_exit(fd, FDSTAT_CLOSE, rc);
03109         }
03110     }
03111 
03112 DBGIO(fd, (stderr, "==>\tlzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
03113 
03114     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "LZDIO", stderr);
03115     /*@-branchstate@*/
03116     if (rc == 0)
03117         fd = fdFree(fd, "open (lzdClose)");
03118     /*@=branchstate@*/
03119     return rc;
03120 }
03121 
03122 /*@-type@*/ /* LCL: function typedefs */
03123 static struct FDIO_s lzdio_s = {
03124   lzdRead, lzdWrite, lzdSeek, lzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
03125   NULL, lzdOpen, lzdFileno, lzdFlush,   NULL, NULL, NULL, NULL, NULL
03126 };
03127 /*@=type@*/
03128 FDIO_t lzdio = /*@-compmempass@*/ &lzdio_s /*@=compmempass@*/ ;
03129 
03130 /* =============================================================== */
03131 /*@observer@*/
03132 static const char * getFdErrstr (FD_t fd)
03133         /*@*/
03134 {
03135     const char *errstr = NULL;
03136 
03137 #ifdef  HAVE_ZLIB_H
03138     if (fdGetIo(fd) == gzdio) {
03139         errstr = fd->errcookie;
03140     } else
03141 #endif  /* HAVE_ZLIB_H */
03142 
03143 #ifdef  HAVE_BZLIB_H
03144     if (fdGetIo(fd) == bzdio) {
03145         errstr = fd->errcookie;
03146     } else
03147 #endif  /* HAVE_BZLIB_H */
03148     if (fdGetIo(fd) == lzdio) {
03149     errstr = fd->errcookie;
03150     } else 
03151     {
03152         errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
03153     }
03154 
03155     return errstr;
03156 }
03157 
03158 /* =============================================================== */
03159 
03160 const char *Fstrerror(FD_t fd)
03161 {
03162     if (fd == NULL)
03163         return (errno ? strerror(errno) : "");
03164     FDSANE(fd);
03165     return getFdErrstr(fd);
03166 }
03167 
03168 #define FDIOVEC(_fd, _vec)      \
03169   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
03170 
03171 size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
03172     fdio_read_function_t _read;
03173     int rc;
03174 
03175     FDSANE(fd);
03176 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
03177 
03178     if (fdGetIo(fd) == fpio) {
03179         /*@+voidabstract -nullpass@*/
03180         rc = fread(buf, size, nmemb, fdGetFILE(fd));
03181         /*@=voidabstract =nullpass@*/
03182         return rc;
03183     }
03184 
03185     /*@-nullderef@*/
03186     _read = FDIOVEC(fd, read);
03187     /*@=nullderef@*/
03188 
03189     rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
03190     return rc;
03191 }
03192 
03193 size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
03194 {
03195     fdio_write_function_t _write;
03196     int rc;
03197 
03198     FDSANE(fd);
03199 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
03200 
03201     if (fdGetIo(fd) == fpio) {
03202 /*@-boundsread@*/
03203         /*@+voidabstract -nullpass@*/
03204         rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
03205         /*@=voidabstract =nullpass@*/
03206 /*@=boundsread@*/
03207         return rc;
03208     }
03209 
03210     /*@-nullderef@*/
03211     _write = FDIOVEC(fd, write);
03212     /*@=nullderef@*/
03213 
03214     rc = (_write ? _write(fd, buf, size * nmemb) : -2);
03215     return rc;
03216 }
03217 
03218 int Fseek(FD_t fd, _libio_off_t offset, int whence) {
03219     fdio_seek_function_t _seek;
03220 #ifdef USE_COOKIE_SEEK_POINTER
03221     _IO_off64_t o64 = offset;
03222     _libio_pos_t pos = &o64;
03223 #else
03224     _libio_pos_t pos = offset;
03225 #endif
03226 
03227     long int rc;
03228 
03229     FDSANE(fd);
03230 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
03231 
03232     if (fdGetIo(fd) == fpio) {
03233         FILE *fp;
03234 
03235         /*@+voidabstract -nullpass@*/
03236         fp = fdGetFILE(fd);
03237         rc = fseek(fp, offset, whence);
03238         /*@=voidabstract =nullpass@*/
03239         return rc;
03240     }
03241 
03242     /*@-nullderef@*/
03243     _seek = FDIOVEC(fd, seek);
03244     /*@=nullderef@*/
03245 
03246     rc = (_seek ? _seek(fd, pos, whence) : -2);
03247     return rc;
03248 }
03249 
03250 int Fclose(FD_t fd)
03251 {
03252     int rc = 0, ec = 0;
03253 
03254     FDSANE(fd);
03255 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
03256 
03257     fd = fdLink(fd, "Fclose");
03258     /*@-branchstate@*/
03259     while (fd->nfps >= 0) {
03260 /*@-boundsread@*/
03261         FDSTACK_t * fps = &fd->fps[fd->nfps];
03262 /*@=boundsread@*/
03263         
03264         if (fps->io == fpio) {
03265             FILE *fp;
03266             int fpno;
03267 
03268             /*@+voidabstract -nullpass@*/
03269             fp = fdGetFILE(fd);
03270             fpno = fileno(fp);
03271             /*@=voidabstract =nullpass@*/
03272         /* XXX persistent HTTP/1.1 returns the previously opened fp */
03273             if (fd->nfps > 0 && fpno == -1 &&
03274                 fd->fps[fd->nfps-1].io == ufdio &&
03275                 fd->fps[fd->nfps-1].fp == fp &&
03276                 (fd->fps[fd->nfps-1].fdno >= 0 || fd->req != NULL))
03277             {
03278                 int hadreqpersist = (fd->req != NULL);
03279 
03280                 if (fp)
03281                     rc = fflush(fp);
03282                 fd->nfps--;
03283                 /*@-refcounttrans@*/
03284                 rc = ufdClose(fd);
03285                 /*@=refcounttrans@*/
03286 /*@-usereleased@*/
03287                 if (fdGetFdno(fd) >= 0)
03288                     break;
03289                 if (!fd->persist)
03290                     hadreqpersist = 0;
03291                 fdSetFp(fd, NULL);
03292                 fd->nfps++;
03293                 if (fp) {
03294                     /* HACK: flimsy Keepalive wiring. */
03295                     if (hadreqpersist) {
03296                         fd->nfps--;
03297 /*@-exposetrans@*/
03298                         fdSetFp(fd, fp);
03299 /*@=exposetrans@*/
03300 /*@-refcounttrans@*/
03301                         (void) fdClose(fd);
03302 /*@=refcounttrans@*/
03303                         fdSetFp(fd, NULL);
03304                         fd->nfps++;
03305 /*@-refcounttrans@*/
03306                         (void) fdClose(fd);
03307 /*@=refcounttrans@*/
03308                     } else
03309                         rc = fclose(fp);
03310                 }
03311                 fdPop(fd);
03312                 if (noLibio)
03313                     fdSetFp(fd, NULL);
03314             } else {
03315                 if (fp)
03316                     rc = fclose(fp);
03317                 if (fpno == -1) {
03318                     fd = fdFree(fd, "fopencookie (Fclose)");
03319                     fdPop(fd);
03320                 }
03321             }
03322         } else {
03323             /*@-nullderef@*/
03324             fdio_close_function_t _close = FDIOVEC(fd, close);
03325             /*@=nullderef@*/
03326             rc = _close(fd);
03327         }
03328         if (fd->nfps == 0)
03329             break;
03330         if (ec == 0 && rc)
03331             ec = rc;
03332         fdPop(fd);
03333     }
03334     /*@=branchstate@*/
03335     fd = fdFree(fd, "Fclose");
03336     return ec;
03337 /*@=usereleased@*/
03338 }
03339 
03351 /*@-boundswrite@*/
03352 static inline void cvtfmode (const char *m,
03353                                 /*@out@*/ char *stdio, size_t nstdio,
03354                                 /*@out@*/ char *other, size_t nother,
03355                                 /*@out@*/ const char **end, /*@out@*/ int * f)
03356         /*@modifies *stdio, *other, *end, *f @*/
03357 {
03358     int flags = 0;
03359     char c;
03360 
03361     switch (*m) {
03362     case 'a':
03363         flags |= O_WRONLY | O_CREAT | O_APPEND;
03364         if (--nstdio > 0) *stdio++ = *m;
03365         break;
03366     case 'w':
03367         flags |= O_WRONLY | O_CREAT | O_TRUNC;
03368         if (--nstdio > 0) *stdio++ = *m;
03369         break;
03370     case 'r':
03371         flags |= O_RDONLY;
03372         if (--nstdio > 0) *stdio++ = *m;
03373         break;
03374     default:
03375         *stdio = '\0';
03376         return;
03377         /*@notreached@*/ break;
03378     }
03379     m++;
03380 
03381     while ((c = *m++) != '\0') {
03382         switch (c) {
03383         case '.':
03384             /*@switchbreak@*/ break;
03385         case '+':
03386             flags &= ~(O_RDONLY|O_WRONLY);
03387             flags |= O_RDWR;
03388             if (--nstdio > 0) *stdio++ = c;
03389             continue;
03390             /*@notreached@*/ /*@switchbreak@*/ break;
03391         case 'b':
03392             if (--nstdio > 0) *stdio++ = c;
03393             continue;
03394             /*@notreached@*/ /*@switchbreak@*/ break;
03395         case 'x':
03396             flags |= O_EXCL;
03397             if (--nstdio > 0) *stdio++ = c;
03398             continue;
03399             /*@notreached@*/ /*@switchbreak@*/ break;
03400         default:
03401             if (--nother > 0) *other++ = c;
03402             continue;
03403             /*@notreached@*/ /*@switchbreak@*/ break;
03404         }
03405         break;
03406     }
03407 
03408     *stdio = *other = '\0';
03409     if (end != NULL)
03410         *end = (*m != '\0' ? m : NULL);
03411     if (f != NULL)
03412         *f = flags;
03413 }
03414 /*@=boundswrite@*/
03415 
03416 #if _USE_LIBIO
03417 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
03418 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
03419 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
03420 #endif
03421 #endif
03422 
03423 /*@-boundswrite@*/
03424 FD_t Fdopen(FD_t ofd, const char *fmode)
03425 {
03426     char stdio[20], other[20], zstdio[20];
03427     const char *end = NULL;
03428     FDIO_t iof = NULL;
03429     FD_t fd = ofd;
03430 
03431 if (_rpmio_debug)
03432 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
03433     FDSANE(fd);
03434 
03435     if (fmode == NULL)
03436         return NULL;
03437 
03438     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
03439     if (stdio[0] == '\0')
03440         return NULL;
03441     zstdio[0] = '\0';
03442     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
03443     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
03444 
03445     if (end == NULL && other[0] == '\0')
03446         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
03447 
03448     /*@-branchstate@*/
03449     if (end && *end) {
03450         if (!strcmp(end, "fdio")) {
03451             iof = fdio;
03452         } else if (!strcmp(end, "gzdio")) {
03453             iof = gzdio;
03454             /*@-internalglobs@*/
03455             fd = gzdFdopen(fd, zstdio);
03456             /*@=internalglobs@*/
03457 #if HAVE_BZLIB_H
03458         } else if (!strcmp(end, "bzdio")) {
03459             iof = bzdio;
03460             /*@-internalglobs@*/
03461             fd = bzdFdopen(fd, zstdio);
03462             /*@=internalglobs@*/
03463 #endif
03464     } else if (!strcmp(end, "lzdio")) {
03465         iof = lzdio;
03466         fd = lzdFdopen(fd, zstdio);
03467         } else if (!strcmp(end, "ufdio")) {
03468             iof = ufdio;
03469         } else if (!strcmp(end, "fpio")) {
03470             iof = fpio;
03471             if (noLibio) {
03472                 int fdno = Fileno(fd);
03473                 FILE * fp = fdopen(fdno, stdio);
03474 /*@+voidabstract -nullpass@*/
03475 if (_rpmio_debug)
03476 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
03477 /*@=voidabstract =nullpass@*/
03478                 if (fp == NULL)
03479                     return NULL;
03480                 /* XXX gzdio/bzdio use fp for private data */
03481                 /*@+voidabstract@*/
03482                 if (fdGetFp(fd) == NULL)
03483                     fdSetFp(fd, fp);
03484                 fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
03485                 /*@=voidabstract@*/
03486             }
03487         }
03488     } else if (other[0] != '\0') {
03489         for (end = other; *end && strchr("0123456789fh", *end); end++)
03490             {};
03491         if (*end == '\0') {
03492             iof = gzdio;
03493             /*@-internalglobs@*/
03494             fd = gzdFdopen(fd, zstdio);
03495             /*@=internalglobs@*/
03496         }
03497     }
03498     /*@=branchstate@*/
03499     if (iof == NULL)
03500         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
03501 
03502     if (!noLibio) {
03503         FILE * fp = NULL;
03504 
03505 #if _USE_LIBIO
03506         {   cookie_io_functions_t ciof;
03507             ciof.read = iof->read;
03508             ciof.write = iof->write;
03509             ciof.seek = iof->seek;
03510             ciof.close = iof->close;
03511             fp = fopencookie(fd, stdio, ciof);
03512 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
03513         }
03514 #endif
03515 
03516         /*@-branchstate@*/
03517         if (fp) {
03518             /* XXX gzdio/bzdio use fp for private data */
03519             /*@+voidabstract -nullpass@*/
03520             if (fdGetFp(fd) == NULL)
03521                 fdSetFp(fd, fp);
03522             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
03523             /*@=voidabstract =nullpass@*/
03524             fd = fdLink(fd, "fopencookie");
03525         }
03526         /*@=branchstate@*/
03527     }
03528 
03529 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
03530     /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
03531 }
03532 /*@=boundswrite@*/
03533 
03534 FD_t Fopen(const char *path, const char *fmode)
03535 {
03536     char stdio[20], other[20];
03537     const char *end = NULL;
03538     mode_t perms = 0666;
03539     int flags;
03540     FD_t fd;
03541 
03542     if (path == NULL || fmode == NULL)
03543         return NULL;
03544 
03545     stdio[0] = '\0';
03546     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
03547     if (stdio[0] == '\0')
03548         return NULL;
03549 
03550     /*@-branchstate@*/
03551     if (end == NULL || !strcmp(end, "fdio")) {
03552 if (_rpmio_debug)
03553 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
03554         fd = fdOpen(path, flags, perms);
03555         if (fdFileno(fd) < 0) {
03556             if (fd) (void) fdClose(fd);
03557             return NULL;
03558         }
03559     } else {
03560         FILE *fp;
03561         int fdno;
03562         int isHTTP = 0;
03563 
03564         /* XXX gzdio and bzdio here too */
03565 
03566         switch (urlIsURL(path)) {
03567         case URL_IS_HTTPS:
03568         case URL_IS_HTTP:
03569         case URL_IS_HKP:
03570             isHTTP = 1;
03571             /*@fallthrough@*/
03572         case URL_IS_PATH:
03573         case URL_IS_DASH:
03574         case URL_IS_FTP:
03575         case URL_IS_UNKNOWN:
03576 if (_rpmio_debug)
03577 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
03578             fd = ufdOpen(path, flags, perms);
03579             if (fd == NULL || !(fdFileno(fd) >= 0 || fd->req != NULL))
03580                 return fd;
03581             break;
03582         default:
03583 if (_rpmio_debug)
03584 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
03585             return NULL;
03586             /*@notreached@*/ break;
03587         }
03588 
03589         /* XXX persistent HTTP/1.1 returns the previously opened fp */
03590         if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0 || fd->req != NULL))
03591         {
03592             /*@+voidabstract@*/
03593             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
03594             /*@=voidabstract@*/
03595             return fd;
03596         }
03597     }
03598     /*@=branchstate@*/
03599 
03600     /*@-branchstate@*/
03601     if (fd)
03602         fd = Fdopen(fd, fmode);
03603     /*@=branchstate@*/
03604     return fd;
03605 }
03606 
03607 int Fflush(FD_t fd)
03608 {
03609     void * vh;
03610     if (fd == NULL) return -1;
03611     if (fdGetIo(fd) == fpio)
03612         /*@+voidabstract -nullpass@*/
03613         return fflush(fdGetFILE(fd));
03614         /*@=voidabstract =nullpass@*/
03615 
03616     vh = fdGetFp(fd);
03617     if (vh && fdGetIo(fd) == gzdio)
03618         return gzdFlush(vh);
03619 #if HAVE_BZLIB_H
03620     if (vh && fdGetIo(fd) == bzdio)
03621         return bzdFlush(vh);
03622 #endif
03623 
03624     return 0;
03625 }
03626 
03627 int Ferror(FD_t fd)
03628 {
03629     int i, rc = 0;
03630 
03631     if (fd == NULL) return -1;
03632     if (fd->req != NULL) {
03633         /* HACK: flimsy wiring for neon errors. */
03634         rc = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
03635     } else
03636     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
03637 /*@-boundsread@*/
03638         FDSTACK_t * fps = &fd->fps[i];
03639 /*@=boundsread@*/
03640         int ec;
03641         
03642         if (fps->io == fpio) {
03643             /*@+voidabstract -nullpass@*/
03644             ec = ferror(fdGetFILE(fd));
03645             /*@=voidabstract =nullpass@*/
03646         } else if (fps->io == gzdio) {
03647             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
03648             i--;        /* XXX fdio under gzdio always has fdno == -1 */
03649 #if HAVE_BZLIB_H
03650         } else if (fps->io == bzdio) {
03651             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
03652             i--;        /* XXX fdio under bzdio always has fdno == -1 */
03653 #endif
03654     } else if (fps->io == lzdio) {
03655             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
03656             i--;        /* XXX fdio under lzdio always has fdno == -1 */
03657         } else {
03658         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
03659             ec = (fdFileno(fd) < 0 ? -1 : 0);
03660         }
03661 
03662         if (rc == 0 && ec)
03663             rc = ec;
03664     }
03665 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
03666     return rc;
03667 }
03668 
03669 int Fileno(FD_t fd)
03670 {
03671     int i, rc = -1;
03672 
03673     if (fd == NULL) return -1;
03674     if (fd->req != NULL)
03675         rc = 123456789; /* HACK: https has no steenkin fileno. */
03676     else
03677     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
03678 /*@-boundsread@*/
03679         rc = fd->fps[i].fdno;
03680 /*@=boundsread@*/
03681     }
03682     
03683 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
03684     return rc;
03685 }
03686 
03687 /* XXX this is naive */
03688 int Fcntl(FD_t fd, int op, void *lip)
03689 {
03690     return fcntl(Fileno(fd), op, lip);
03691 }
03692 
03693 /* =============================================================== */
03694 /* Helper routines that may be generally useful.
03695  */
03696 
03697 char * rpmEscapeSpaces(const char * s)
03698 {
03699     const char * se;
03700     char * t;
03701     char * te;
03702     size_t nb = 0;
03703 
03704     for (se = s; *se; se++) {
03705         if (isspace(*se))
03706             nb++;
03707         nb++;
03708     }
03709     nb++;
03710 
03711     t = te = xmalloc(nb);
03712     for (se = s; *se; se++) {
03713         if (isspace(*se))
03714             *te++ = '\\';
03715         *te++ = *se;
03716     }
03717     *te = '\0';
03718     return t;
03719 }
03720 
03721 /*@-bounds@*/
03722 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
03723 {
03724     char * d, * de;
03725     int created = 0;
03726     int rc;
03727 
03728     if (path == NULL)
03729         return -1;
03730     d = alloca(strlen(path)+2);
03731     de = stpcpy(d, path);
03732     de[1] = '\0';
03733     for (de = d; *de != '\0'; de++) {
03734         struct stat st;
03735         char savec;
03736 
03737         while (*de && *de != '/') de++;
03738         savec = de[1];
03739         de[1] = '\0';
03740 
03741         rc = Stat(d, &st);
03742         if (rc) {
03743             switch(errno) {
03744             default:
03745                 return errno;
03746                 /*@notreached@*/ /*@switchbreak@*/ break;
03747             case ENOENT:
03748                 /*@switchbreak@*/ break;
03749             }
03750             rc = Mkdir(d, mode);
03751             if (rc)
03752                 return errno;
03753             created = 1;
03754             if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
03755                 rc = chown(d, uid, gid);
03756                 if (rc)
03757                     return errno;
03758             }
03759         } else if (!S_ISDIR(st.st_mode)) {
03760             return ENOTDIR;
03761         }
03762         de[1] = savec;
03763     }
03764     rc = 0;
03765     if (created)
03766         rpmMessage(RPMMESS_DEBUG, "created directory(s) %s mode 0%o\n",
03767                         path, mode);
03768     return rc;
03769 }
03770 /*@=bounds@*/
03771 
03772 /*@-boundswrite@*/
03773 int rpmioSlurp(const char * fn, const byte ** bp, ssize_t * blenp)
03774 {
03775     static ssize_t blenmax = (32 * BUFSIZ);
03776     ssize_t blen = 0;
03777     byte * b = NULL;
03778     ssize_t size;
03779     FD_t fd;
03780     int rc = 0;
03781 
03782     fd = Fopen(fn, "r.ufdio");
03783     if (fd == NULL || Ferror(fd)) {
03784         rc = 2;
03785         goto exit;
03786     }
03787 
03788     size = fdSize(fd);
03789     blen = (size >= 0 ? size : blenmax);
03790     /*@-branchstate@*/
03791     if (blen) {
03792         int nb;
03793         b = xmalloc(blen+1);
03794         b[0] = '\0';
03795         nb = Fread(b, sizeof(*b), blen, fd);
03796         if (Ferror(fd) || (size > 0 && nb != blen)) {
03797             rc = 1;
03798             goto exit;
03799         }
03800         if (blen == blenmax && nb < blen) {
03801             blen = nb;
03802             b = xrealloc(b, blen+1);
03803         }
03804         b[blen] = '\0';
03805     }
03806     /*@=branchstate@*/
03807 
03808 exit:
03809     if (fd) (void) Fclose(fd);
03810         
03811     if (rc) {
03812         if (b) free(b);
03813         b = NULL;
03814         blen = 0;
03815     }
03816 
03817     if (bp) *bp = b;
03818     else if (b) free(b);
03819 
03820     if (blenp) *blenp = blen;
03821 
03822     return rc;
03823 }
03824 /*@=boundswrite@*/
03825 
03826 /*@-type@*/ /* LCL: function typedefs */
03827 static struct FDIO_s fpio_s = {
03828   ufdRead, ufdWrite, fdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
03829   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
03830 };
03831 /*@=type@*/
03832 FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;

Generated on Sun Jun 1 21:56:22 2008 for rpm by  doxygen 1.5.4