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 = 1;     /* 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 = 1;
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 "LzmaDecode.h"
02789 
02790 #define kInBufferSize (1 << 15)
02791 typedef struct _CBuffer
02792 {
02793   ILzmaInCallback InCallback;
02794   FILE *File;
02795   unsigned char Buffer[kInBufferSize];
02796 } CBuffer;
02797 
02798 typedef struct lzfile {
02799     CBuffer g_InBuffer;
02800     CLzmaDecoderState state;  /* it's about 24-80 bytes structure, if int is 32-bit */
02801     unsigned char properties[LZMA_PROPERTIES_SIZE];
02802 
02803 //    FILE *file;
02804     int pid;
02805 } LZFILE;
02806 
02807 static size_t MyReadFile(FILE *file, void *data, size_t size)
02808 { 
02809     if (size == 0) return 0;
02810     return fread(data, 1, size, file); 
02811 }
02812 
02813 static int MyReadFileAndCheck(FILE *file, void *data, size_t size)
02814 {
02815     return (MyReadFile(file, data, size) == size);
02816 }
02817 
02818 static int LzmaReadCompressed(void *object, const unsigned char **buffer, SizeT *size)
02819 {
02820     CBuffer *b = (CBuffer *)object;
02821     *buffer = b->Buffer;
02822     *size = (SizeT)MyReadFile(b->File, b->Buffer, kInBufferSize);
02823     return LZMA_RESULT_OK;
02824 }
02825 
02826 static inline /*@dependent@*/ void * lzdFileno(FD_t fd)
02827         /*@*/
02828 {
02829     void * rc = NULL;
02830     int i;
02831 
02832     FDSANE(fd);
02833     for (i = fd->nfps; i >= 0; i--) {
02834 /*@-boundsread@*/
02835             FDSTACK_t * fps = &fd->fps[i];
02836 /*@=boundsread@*/
02837             if (fps->io != lzdio)
02838                 continue;
02839             rc = fps->fp;
02840         break;
02841     }
02842     
02843     return rc;
02844 }
02845 
02846 /*@-mods@*/   /* XXX hide rpmGlobalMacroContext mods for now. */
02847 static FD_t lzdWriteOpen(int fdno, int fopen, const char * mode)
02848         /*@globals fileSystem, internalState @*/
02849         /*@modifies fileSystem, internalState @*/
02850 {
02851     int pid;
02852     int p[2];
02853     int xx;
02854     const char *lzma;
02855     char l[3];
02856     const char *level;
02857 
02858     if (isdigit(mode[1])) /* "w5" */
02859     {
02860         sprintf(l, "-%c", mode[1]);
02861         level = l;
02862     }
02863     else
02864          level = NULL;
02865 
02866     if (fdno < 0) return NULL;
02867     if (pipe(p) < 0) {
02868         close(fdno);
02869         return NULL;
02870     }
02871     pid = fork();
02872     if (pid < 0) {
02873         close(fdno);
02874         return NULL;
02875     }
02876     if (pid) {
02877         FD_t fd;
02878         LZFILE *lzfile;
02879 
02880         close(fdno);
02881         close(p[0]);
02882         lzfile = calloc(1, sizeof(*lzfile));
02883         if (lzfile == NULL) return NULL;
02884         lzfile->g_InBuffer.File = fdopen(p[1], "wb");
02885         lzfile->pid = pid;
02886         if (lzfile->g_InBuffer.File == NULL) {
02887             close(p[1]);
02888             free(lzfile);
02889             return NULL;
02890         }
02891         fd = fdNew("open (lzdOpen write)");
02892         if (fopen) fdPop(fd);
02893         fdPush(fd, lzdio, lzfile, -1);
02894         return fdLink(fd, "lzdOpen");
02895     } else {
02896         int i;
02897         /* lzma */
02898         close(p[1]);
02899         dup2(p[0], 0);
02900         dup2(fdno, 1);
02901         for (i = 3; i < 1024; i++) close(i);
02902           xx = close(i);
02903         lzma = rpmGetPath("%{?__lzma}%{!?__lzma:/usr/bin/lzma}", NULL);
02904         if (execl(lzma, "lzma", level, NULL))
02905                 _exit(1);
02906         lzma = _free(lzma);
02907     }
02908     return NULL; /* warning */
02909 }
02910 
02911 static FD_t lzdReadOpen(int fdno, int fopen)
02912 {
02913     LZFILE *lzfile;
02914     unsigned char ff[8];
02915     FD_t fd;
02916 
02917     if (fdno < 0) return NULL;
02918     lzfile = calloc(1, sizeof(*lzfile));
02919     if (lzfile == NULL) return NULL;
02920     lzfile->g_InBuffer.File = fdopen(fdno, "rb");
02921     if (lzfile->g_InBuffer.File == NULL) goto error2;
02922 
02923     if (!MyReadFileAndCheck(lzfile->g_InBuffer.File, lzfile->properties, sizeof(lzfile->properties))) {
02924 error:
02925         fclose(lzfile->g_InBuffer.File);
02926 error2:
02927         free(lzfile);
02928         return NULL;
02929     }
02930     if (!MyReadFileAndCheck(lzfile->g_InBuffer.File, ff, 8)) goto error;
02931     if (LzmaDecodeProperties(&lzfile->state.Properties, lzfile->properties, LZMA_PROPERTIES_SIZE) != LZMA_RESULT_OK)
02932         goto error;
02933     lzfile->state.Probs = (CProb *)malloc(LzmaGetNumProbs(&lzfile->state.Properties) * sizeof(CProb));
02934     if (lzfile->state.Probs == NULL) goto error;
02935 
02936     if (lzfile->state.Properties.DictionarySize == 0)
02937         lzfile->state.Dictionary = 0;
02938     else {
02939         lzfile->state.Dictionary = (unsigned char *)malloc(lzfile->state.Properties.DictionarySize);
02940         if (lzfile->state.Dictionary == NULL) {
02941             free(lzfile->state.Probs);
02942             goto error;
02943         }
02944     }
02945     lzfile->g_InBuffer.InCallback.Read = LzmaReadCompressed;
02946     LzmaDecoderInit(&lzfile->state);
02947 
02948     fd = fdNew("open (lzdOpen read)");
02949     if (fopen) fdPop(fd);
02950     fdPush(fd, lzdio, lzfile, -1);
02951     return fdLink(fd, "lzdOpen");
02952 }
02953 
02954 /*@-globuse@*/
02955 static /*@null@*/ FD_t lzdOpen(const char * path, const char * mode)
02956         /*@globals fileSystem @*/
02957         /*@modifies fileSystem @*/
02958 {
02959     if (mode == NULL)
02960         return NULL;
02961     if (mode[0] == 'w') {
02962         int fdno = open(path, O_WRONLY);
02963 
02964         if (fdno < 0) return NULL;
02965         return lzdWriteOpen(fdno, 1, mode);
02966     } else {
02967         int fdno = open(path, O_RDONLY);
02968 
02969         if (fdno < 0) return NULL;
02970         return lzdReadOpen(fdno, 1);
02971     }
02972 }
02973 /*@=globuse@*/
02974 
02975 /*@-globuse@*/
02976 static /*@null@*/ FD_t lzdFdopen(void * cookie, const char * fmode)
02977         /*@globals fileSystem, internalState @*/
02978         /*@modifies fileSystem, internalState @*/
02979 {
02980     FD_t fd = c2f(cookie);
02981     int fdno;
02982 
02983     if (fmode == NULL) return NULL;
02984     fdno = fdFileno(fd);
02985     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
02986     if (fdno < 0) return NULL;
02987     if (fmode[0] == 'w') {
02988         return lzdWriteOpen(fdno, 0, fmode);
02989     } else {
02990         return lzdReadOpen(fdno, 0);
02991     }
02992 }
02993 /*@=globuse@*/
02994 
02995 /*@-globuse@*/
02996 static int lzdFlush(FD_t fd)
02997         /*@globals fileSystem @*/
02998         /*@modifies fileSystem @*/
02999 {
03000     LZFILE *lzfile = lzdFileno(fd);
03001 
03002     if (lzfile == NULL || lzfile->g_InBuffer.File == NULL) return -2;
03003     return fflush(lzfile->g_InBuffer.File);
03004 }
03005 /*@=globuse@*/
03006 
03007 /* =============================================================== */
03008 /*@-globuse@*/
03009 /*@-mustmod@*/          /* LCL: *buf is modified */
03010 static ssize_t lzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
03011         /*@globals fileSystem, internalState @*/
03012         /*@modifies *buf, fileSystem, internalState @*/
03013 {
03014     FD_t fd = c2f(cookie);
03015     LZFILE *lzfile;
03016     ssize_t rc = 0;
03017     int res = 0;
03018 
03019     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
03020     lzfile = lzdFileno(fd);
03021     fdstat_enter(fd, FDSTAT_READ);
03022     if (lzfile->g_InBuffer.File)
03023         /*@-compdef@*/
03024         res = LzmaDecode(&lzfile->state, &lzfile->g_InBuffer.InCallback, buf, count, &rc);
03025         /*@=compdef@*/
03026     if (res) {
03027         if (lzfile)
03028             fd->errcookie = "Lzma: decoding error";
03029     } else if (rc >= 0) {
03030         fdstat_exit(fd, FDSTAT_READ, rc);
03031         /*@-compdef@*/
03032         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
03033         /*@=compdef@*/
03034     }
03035     return rc;
03036 }
03037 /*@=mustmod@*/
03038 /*@=globuse@*/
03039 
03040 /*@-globuse@*/
03041 static ssize_t lzdWrite(void * cookie, const char * buf, size_t count)
03042         /*@globals fileSystem, internalState @*/
03043         /*@modifies fileSystem, internalState @*/
03044 {
03045     FD_t fd = c2f(cookie);
03046     LZFILE *lzfile;
03047     ssize_t rc;
03048 
03049     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
03050 
03051     if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
03052 
03053     lzfile = lzdFileno(fd);
03054     fdstat_enter(fd, FDSTAT_WRITE);
03055     rc = fwrite((void *)buf, 1, count, lzfile->g_InBuffer.File);
03056     if (rc == -1) {
03057         fd->errcookie = strerror(ferror(lzfile->g_InBuffer.File));
03058     } else if (rc > 0) {
03059         fdstat_exit(fd, FDSTAT_WRITE, rc);
03060     }
03061     return rc;
03062 }
03063 /*@=globuse@*/
03064 
03065 static inline int lzdSeek(void * cookie, /*@unused@*/ _libio_pos_t pos,
03066                         /*@unused@*/ int whence)
03067         /*@*/
03068 {
03069     FD_t fd = c2f(cookie);
03070 
03071     LZDONLY(fd);
03072     return -2;
03073 }
03074 
03075 static int lzdClose( /*@only@*/ void * cookie)
03076         /*@globals fileSystem, internalState @*/
03077         /*@modifies fileSystem, internalState @*/
03078 {
03079     FD_t fd = c2f(cookie);
03080     LZFILE *lzfile;
03081     int rc;
03082 
03083     lzfile = lzdFileno(fd);
03084 
03085     if (lzfile == NULL) return -2;
03086     fdstat_enter(fd, FDSTAT_CLOSE);
03087     /*@-noeffectuncon@*/ /* FIX: check rc */
03088     fclose(lzfile->g_InBuffer.File);
03089     if (lzfile->pid) wait4(lzfile->pid, NULL, 0, NULL);
03090     else { /* reading */
03091         free(lzfile->state.Probs);
03092         if (lzfile->state.Dictionary) free(lzfile->state.Dictionary);
03093     }
03094     free(lzfile);
03095     /*@=noeffectuncon@*/
03096     rc = 0;     /* XXX FIXME */
03097 
03098     /* XXX TODO: preserve fd if errors */
03099 
03100     if (fd) {
03101         if (rc == -1) {
03102             fd->errcookie = strerror(ferror(lzfile->g_InBuffer.File));
03103         } else if (rc >= 0) {
03104             fdstat_exit(fd, FDSTAT_CLOSE, rc);
03105         }
03106     }
03107 
03108 DBGIO(fd, (stderr, "==>\tlzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
03109 
03110     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "LZDIO", stderr);
03111     /*@-branchstate@*/
03112     if (rc == 0)
03113         fd = fdFree(fd, "open (lzdClose)");
03114     /*@=branchstate@*/
03115     return rc;
03116 }
03117 
03118 /*@-type@*/ /* LCL: function typedefs */
03119 static struct FDIO_s lzdio_s = {
03120   lzdRead, lzdWrite, lzdSeek, lzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
03121   NULL, lzdOpen, lzdFileno, lzdFlush,   NULL, NULL, NULL, NULL, NULL
03122 };
03123 /*@=type@*/
03124 FDIO_t lzdio = /*@-compmempass@*/ &lzdio_s /*@=compmempass@*/ ;
03125 
03126 /* =============================================================== */
03127 /*@observer@*/
03128 static const char * getFdErrstr (FD_t fd)
03129         /*@*/
03130 {
03131     const char *errstr = NULL;
03132 
03133 #ifdef  HAVE_ZLIB_H
03134     if (fdGetIo(fd) == gzdio) {
03135         errstr = fd->errcookie;
03136     } else
03137 #endif  /* HAVE_ZLIB_H */
03138 
03139 #ifdef  HAVE_BZLIB_H
03140     if (fdGetIo(fd) == bzdio) {
03141         errstr = fd->errcookie;
03142     } else
03143 #endif  /* HAVE_BZLIB_H */
03144     if (fdGetIo(fd) == lzdio) {
03145     errstr = fd->errcookie;
03146     } else 
03147     {
03148         errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
03149     }
03150 
03151     return errstr;
03152 }
03153 
03154 /* =============================================================== */
03155 
03156 const char *Fstrerror(FD_t fd)
03157 {
03158     if (fd == NULL)
03159         return (errno ? strerror(errno) : "");
03160     FDSANE(fd);
03161     return getFdErrstr(fd);
03162 }
03163 
03164 #define FDIOVEC(_fd, _vec)      \
03165   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
03166 
03167 size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
03168     fdio_read_function_t _read;
03169     int rc;
03170 
03171     FDSANE(fd);
03172 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
03173 
03174     if (fdGetIo(fd) == fpio) {
03175         /*@+voidabstract -nullpass@*/
03176         rc = fread(buf, size, nmemb, fdGetFILE(fd));
03177         /*@=voidabstract =nullpass@*/
03178         return rc;
03179     }
03180 
03181     /*@-nullderef@*/
03182     _read = FDIOVEC(fd, read);
03183     /*@=nullderef@*/
03184 
03185     rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
03186     return rc;
03187 }
03188 
03189 size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
03190 {
03191     fdio_write_function_t _write;
03192     int rc;
03193 
03194     FDSANE(fd);
03195 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
03196 
03197     if (fdGetIo(fd) == fpio) {
03198 /*@-boundsread@*/
03199         /*@+voidabstract -nullpass@*/
03200         rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
03201         /*@=voidabstract =nullpass@*/
03202 /*@=boundsread@*/
03203         return rc;
03204     }
03205 
03206     /*@-nullderef@*/
03207     _write = FDIOVEC(fd, write);
03208     /*@=nullderef@*/
03209 
03210     rc = (_write ? _write(fd, buf, size * nmemb) : -2);
03211     return rc;
03212 }
03213 
03214 int Fseek(FD_t fd, _libio_off_t offset, int whence) {
03215     fdio_seek_function_t _seek;
03216 #ifdef USE_COOKIE_SEEK_POINTER
03217     _IO_off64_t o64 = offset;
03218     _libio_pos_t pos = &o64;
03219 #else
03220     _libio_pos_t pos = offset;
03221 #endif
03222 
03223     long int rc;
03224 
03225     FDSANE(fd);
03226 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
03227 
03228     if (fdGetIo(fd) == fpio) {
03229         FILE *fp;
03230 
03231         /*@+voidabstract -nullpass@*/
03232         fp = fdGetFILE(fd);
03233         rc = fseek(fp, offset, whence);
03234         /*@=voidabstract =nullpass@*/
03235         return rc;
03236     }
03237 
03238     /*@-nullderef@*/
03239     _seek = FDIOVEC(fd, seek);
03240     /*@=nullderef@*/
03241 
03242     rc = (_seek ? _seek(fd, pos, whence) : -2);
03243     return rc;
03244 }
03245 
03246 int Fclose(FD_t fd)
03247 {
03248     int rc = 0, ec = 0;
03249 
03250     FDSANE(fd);
03251 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
03252 
03253     fd = fdLink(fd, "Fclose");
03254     /*@-branchstate@*/
03255     while (fd->nfps >= 0) {
03256 /*@-boundsread@*/
03257         FDSTACK_t * fps = &fd->fps[fd->nfps];
03258 /*@=boundsread@*/
03259         
03260         if (fps->io == fpio) {
03261             FILE *fp;
03262             int fpno;
03263 
03264             /*@+voidabstract -nullpass@*/
03265             fp = fdGetFILE(fd);
03266             fpno = fileno(fp);
03267             /*@=voidabstract =nullpass@*/
03268         /* XXX persistent HTTP/1.1 returns the previously opened fp */
03269             if (fd->nfps > 0 && fpno == -1 &&
03270                 fd->fps[fd->nfps-1].io == ufdio &&
03271                 fd->fps[fd->nfps-1].fp == fp &&
03272                 (fd->fps[fd->nfps-1].fdno >= 0 || fd->req != NULL))
03273             {
03274                 int hadreqpersist = (fd->req != NULL);
03275 
03276                 if (fp)
03277                     rc = fflush(fp);
03278                 fd->nfps--;
03279                 /*@-refcounttrans@*/
03280                 rc = ufdClose(fd);
03281                 /*@=refcounttrans@*/
03282 /*@-usereleased@*/
03283                 if (fdGetFdno(fd) >= 0)
03284                     break;
03285                 if (!fd->persist)
03286                     hadreqpersist = 0;
03287                 fdSetFp(fd, NULL);
03288                 fd->nfps++;
03289                 if (fp) {
03290                     /* HACK: flimsy Keepalive wiring. */
03291                     if (hadreqpersist) {
03292                         fd->nfps--;
03293 /*@-exposetrans@*/
03294                         fdSetFp(fd, fp);
03295 /*@=exposetrans@*/
03296 /*@-refcounttrans@*/
03297                         (void) fdClose(fd);
03298 /*@=refcounttrans@*/
03299                         fdSetFp(fd, NULL);
03300                         fd->nfps++;
03301 /*@-refcounttrans@*/
03302                         (void) fdClose(fd);
03303 /*@=refcounttrans@*/
03304                     } else
03305                         rc = fclose(fp);
03306                 }
03307                 fdPop(fd);
03308                 if (noLibio)
03309                     fdSetFp(fd, NULL);
03310             } else {
03311                 if (fp)
03312                     rc = fclose(fp);
03313                 if (fpno == -1) {
03314                     fd = fdFree(fd, "fopencookie (Fclose)");
03315                     fdPop(fd);
03316                 }
03317             }
03318         } else {
03319             /*@-nullderef@*/
03320             fdio_close_function_t _close = FDIOVEC(fd, close);
03321             /*@=nullderef@*/
03322             rc = _close(fd);
03323         }
03324         if (fd->nfps == 0)
03325             break;
03326         if (ec == 0 && rc)
03327             ec = rc;
03328         fdPop(fd);
03329     }
03330     /*@=branchstate@*/
03331     fd = fdFree(fd, "Fclose");
03332     return ec;
03333 /*@=usereleased@*/
03334 }
03335 
03347 /*@-boundswrite@*/
03348 static inline void cvtfmode (const char *m,
03349                                 /*@out@*/ char *stdio, size_t nstdio,
03350                                 /*@out@*/ char *other, size_t nother,
03351                                 /*@out@*/ const char **end, /*@out@*/ int * f)
03352         /*@modifies *stdio, *other, *end, *f @*/
03353 {
03354     int flags = 0;
03355     char c;
03356 
03357     switch (*m) {
03358     case 'a':
03359         flags |= O_WRONLY | O_CREAT | O_APPEND;
03360         if (--nstdio > 0) *stdio++ = *m;
03361         break;
03362     case 'w':
03363         flags |= O_WRONLY | O_CREAT | O_TRUNC;
03364         if (--nstdio > 0) *stdio++ = *m;
03365         break;
03366     case 'r':
03367         flags |= O_RDONLY;
03368         if (--nstdio > 0) *stdio++ = *m;
03369         break;
03370     default:
03371         *stdio = '\0';
03372         return;
03373         /*@notreached@*/ break;
03374     }
03375     m++;
03376 
03377     while ((c = *m++) != '\0') {
03378         switch (c) {
03379         case '.':
03380             /*@switchbreak@*/ break;
03381         case '+':
03382             flags &= ~(O_RDONLY|O_WRONLY);
03383             flags |= O_RDWR;
03384             if (--nstdio > 0) *stdio++ = c;
03385             continue;
03386             /*@notreached@*/ /*@switchbreak@*/ break;
03387         case 'b':
03388             if (--nstdio > 0) *stdio++ = c;
03389             continue;
03390             /*@notreached@*/ /*@switchbreak@*/ break;
03391         case 'x':
03392             flags |= O_EXCL;
03393             if (--nstdio > 0) *stdio++ = c;
03394             continue;
03395             /*@notreached@*/ /*@switchbreak@*/ break;
03396         default:
03397             if (--nother > 0) *other++ = c;
03398             continue;
03399             /*@notreached@*/ /*@switchbreak@*/ break;
03400         }
03401         break;
03402     }
03403 
03404     *stdio = *other = '\0';
03405     if (end != NULL)
03406         *end = (*m != '\0' ? m : NULL);
03407     if (f != NULL)
03408         *f = flags;
03409 }
03410 /*@=boundswrite@*/
03411 
03412 #if _USE_LIBIO
03413 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
03414 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
03415 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
03416 #endif
03417 #endif
03418 
03419 /*@-boundswrite@*/
03420 FD_t Fdopen(FD_t ofd, const char *fmode)
03421 {
03422     char stdio[20], other[20], zstdio[20];
03423     const char *end = NULL;
03424     FDIO_t iof = NULL;
03425     FD_t fd = ofd;
03426 
03427 if (_rpmio_debug)
03428 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
03429     FDSANE(fd);
03430 
03431     if (fmode == NULL)
03432         return NULL;
03433 
03434     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
03435     if (stdio[0] == '\0')
03436         return NULL;
03437     zstdio[0] = '\0';
03438     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
03439     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
03440 
03441     if (end == NULL && other[0] == '\0')
03442         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
03443 
03444     /*@-branchstate@*/
03445     if (end && *end) {
03446         if (!strcmp(end, "fdio")) {
03447             iof = fdio;
03448         } else if (!strcmp(end, "gzdio")) {
03449             iof = gzdio;
03450             /*@-internalglobs@*/
03451             fd = gzdFdopen(fd, zstdio);
03452             /*@=internalglobs@*/
03453 #if HAVE_BZLIB_H
03454         } else if (!strcmp(end, "bzdio")) {
03455             iof = bzdio;
03456             /*@-internalglobs@*/
03457             fd = bzdFdopen(fd, zstdio);
03458             /*@=internalglobs@*/
03459 #endif
03460     } else if (!strcmp(end, "lzdio")) {
03461         iof = lzdio;
03462         fd = lzdFdopen(fd, zstdio);
03463         } else if (!strcmp(end, "ufdio")) {
03464             iof = ufdio;
03465         } else if (!strcmp(end, "fpio")) {
03466             iof = fpio;
03467             if (noLibio) {
03468                 int fdno = Fileno(fd);
03469                 FILE * fp = fdopen(fdno, stdio);
03470 /*@+voidabstract -nullpass@*/
03471 if (_rpmio_debug)
03472 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
03473 /*@=voidabstract =nullpass@*/
03474                 if (fp == NULL)
03475                     return NULL;
03476                 /* XXX gzdio/bzdio use fp for private data */
03477                 /*@+voidabstract@*/
03478                 if (fdGetFp(fd) == NULL)
03479                     fdSetFp(fd, fp);
03480                 fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
03481                 /*@=voidabstract@*/
03482             }
03483         }
03484     } else if (other[0] != '\0') {
03485         for (end = other; *end && strchr("0123456789fh", *end); end++)
03486             {};
03487         if (*end == '\0') {
03488             iof = gzdio;
03489             /*@-internalglobs@*/
03490             fd = gzdFdopen(fd, zstdio);
03491             /*@=internalglobs@*/
03492         }
03493     }
03494     /*@=branchstate@*/
03495     if (iof == NULL)
03496         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
03497 
03498     if (!noLibio) {
03499         FILE * fp = NULL;
03500 
03501 #if _USE_LIBIO
03502         {   cookie_io_functions_t ciof;
03503             ciof.read = iof->read;
03504             ciof.write = iof->write;
03505             ciof.seek = iof->seek;
03506             ciof.close = iof->close;
03507             fp = fopencookie(fd, stdio, ciof);
03508 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
03509         }
03510 #endif
03511 
03512         /*@-branchstate@*/
03513         if (fp) {
03514             /* XXX gzdio/bzdio use fp for private data */
03515             /*@+voidabstract -nullpass@*/
03516             if (fdGetFp(fd) == NULL)
03517                 fdSetFp(fd, fp);
03518             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
03519             /*@=voidabstract =nullpass@*/
03520             fd = fdLink(fd, "fopencookie");
03521         }
03522         /*@=branchstate@*/
03523     }
03524 
03525 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
03526     /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
03527 }
03528 /*@=boundswrite@*/
03529 
03530 FD_t Fopen(const char *path, const char *fmode)
03531 {
03532     char stdio[20], other[20];
03533     const char *end = NULL;
03534     mode_t perms = 0666;
03535     int flags;
03536     FD_t fd;
03537 
03538     if (path == NULL || fmode == NULL)
03539         return NULL;
03540 
03541     stdio[0] = '\0';
03542     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
03543     if (stdio[0] == '\0')
03544         return NULL;
03545 
03546     /*@-branchstate@*/
03547     if (end == NULL || !strcmp(end, "fdio")) {
03548 if (_rpmio_debug)
03549 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
03550         fd = fdOpen(path, flags, perms);
03551         if (fdFileno(fd) < 0) {
03552             if (fd) (void) fdClose(fd);
03553             return NULL;
03554         }
03555     } else {
03556         FILE *fp;
03557         int fdno;
03558         int isHTTP = 0;
03559 
03560         /* XXX gzdio and bzdio here too */
03561 
03562         switch (urlIsURL(path)) {
03563         case URL_IS_HTTPS:
03564         case URL_IS_HTTP:
03565         case URL_IS_HKP:
03566             isHTTP = 1;
03567             /*@fallthrough@*/
03568         case URL_IS_PATH:
03569         case URL_IS_DASH:
03570         case URL_IS_FTP:
03571         case URL_IS_UNKNOWN:
03572 if (_rpmio_debug)
03573 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
03574             fd = ufdOpen(path, flags, perms);
03575             if (fd == NULL || !(fdFileno(fd) >= 0 || fd->req != NULL))
03576                 return fd;
03577             break;
03578         default:
03579 if (_rpmio_debug)
03580 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
03581             return NULL;
03582             /*@notreached@*/ break;
03583         }
03584 
03585         /* XXX persistent HTTP/1.1 returns the previously opened fp */
03586         if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0 || fd->req != NULL))
03587         {
03588             /*@+voidabstract@*/
03589             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
03590             /*@=voidabstract@*/
03591             return fd;
03592         }
03593     }
03594     /*@=branchstate@*/
03595 
03596     /*@-branchstate@*/
03597     if (fd)
03598         fd = Fdopen(fd, fmode);
03599     /*@=branchstate@*/
03600     return fd;
03601 }
03602 
03603 int Fflush(FD_t fd)
03604 {
03605     void * vh;
03606     if (fd == NULL) return -1;
03607     if (fdGetIo(fd) == fpio)
03608         /*@+voidabstract -nullpass@*/
03609         return fflush(fdGetFILE(fd));
03610         /*@=voidabstract =nullpass@*/
03611 
03612     vh = fdGetFp(fd);
03613     if (vh && fdGetIo(fd) == gzdio)
03614         return gzdFlush(vh);
03615 #if HAVE_BZLIB_H
03616     if (vh && fdGetIo(fd) == bzdio)
03617         return bzdFlush(vh);
03618 #endif
03619 
03620     return 0;
03621 }
03622 
03623 int Ferror(FD_t fd)
03624 {
03625     int i, rc = 0;
03626 
03627     if (fd == NULL) return -1;
03628     if (fd->req != NULL) {
03629         /* HACK: flimsy wiring for neon errors. */
03630         rc = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
03631     } else
03632     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
03633 /*@-boundsread@*/
03634         FDSTACK_t * fps = &fd->fps[i];
03635 /*@=boundsread@*/
03636         int ec;
03637         
03638         if (fps->io == fpio) {
03639             /*@+voidabstract -nullpass@*/
03640             ec = ferror(fdGetFILE(fd));
03641             /*@=voidabstract =nullpass@*/
03642         } else if (fps->io == gzdio) {
03643             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
03644             i--;        /* XXX fdio under gzdio always has fdno == -1 */
03645 #if HAVE_BZLIB_H
03646         } else if (fps->io == bzdio) {
03647             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
03648             i--;        /* XXX fdio under bzdio always has fdno == -1 */
03649 #endif
03650     } else if (fps->io == lzdio) {
03651             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
03652             i--;        /* XXX fdio under lzdio always has fdno == -1 */
03653         } else {
03654         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
03655             ec = (fdFileno(fd) < 0 ? -1 : 0);
03656         }
03657 
03658         if (rc == 0 && ec)
03659             rc = ec;
03660     }
03661 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
03662     return rc;
03663 }
03664 
03665 int Fileno(FD_t fd)
03666 {
03667     int i, rc = -1;
03668 
03669     if (fd == NULL) return -1;
03670     if (fd->req != NULL)
03671         rc = 123456789; /* HACK: https has no steenkin fileno. */
03672     else
03673     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
03674 /*@-boundsread@*/
03675         rc = fd->fps[i].fdno;
03676 /*@=boundsread@*/
03677     }
03678     
03679 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
03680     return rc;
03681 }
03682 
03683 /* XXX this is naive */
03684 int Fcntl(FD_t fd, int op, void *lip)
03685 {
03686     return fcntl(Fileno(fd), op, lip);
03687 }
03688 
03689 /* =============================================================== */
03690 /* Helper routines that may be generally useful.
03691  */
03692 /*@-bounds@*/
03693 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
03694 {
03695     char * d, * de;
03696     int created = 0;
03697     int rc;
03698 
03699     if (path == NULL)
03700         return -1;
03701     d = alloca(strlen(path)+2);
03702     de = stpcpy(d, path);
03703     de[1] = '\0';
03704     for (de = d; *de != '\0'; de++) {
03705         struct stat st;
03706         char savec;
03707 
03708         while (*de && *de != '/') de++;
03709         savec = de[1];
03710         de[1] = '\0';
03711 
03712         rc = Stat(d, &st);
03713         if (rc) {
03714             switch(errno) {
03715             default:
03716                 return errno;
03717                 /*@notreached@*/ /*@switchbreak@*/ break;
03718             case ENOENT:
03719                 /*@switchbreak@*/ break;
03720             }
03721             rc = Mkdir(d, mode);
03722             if (rc)
03723                 return errno;
03724             created = 1;
03725             if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
03726                 rc = chown(d, uid, gid);
03727                 if (rc)
03728                     return errno;
03729             }
03730         } else if (!S_ISDIR(st.st_mode)) {
03731             return ENOTDIR;
03732         }
03733         de[1] = savec;
03734     }
03735     rc = 0;
03736     if (created)
03737         rpmMessage(RPMMESS_DEBUG, "created directory(s) %s mode 0%o\n",
03738                         path, mode);
03739     return rc;
03740 }
03741 /*@=bounds@*/
03742 
03743 /*@-boundswrite@*/
03744 int rpmioSlurp(const char * fn, const byte ** bp, ssize_t * blenp)
03745 {
03746     static ssize_t blenmax = (32 * BUFSIZ);
03747     ssize_t blen = 0;
03748     byte * b = NULL;
03749     ssize_t size;
03750     FD_t fd;
03751     int rc = 0;
03752 
03753     fd = Fopen(fn, "r.ufdio");
03754     if (fd == NULL || Ferror(fd)) {
03755         rc = 2;
03756         goto exit;
03757     }
03758 
03759     size = fdSize(fd);
03760     blen = (size >= 0 ? size : blenmax);
03761     /*@-branchstate@*/
03762     if (blen) {
03763         int nb;
03764         b = xmalloc(blen+1);
03765         b[0] = '\0';
03766         nb = Fread(b, sizeof(*b), blen, fd);
03767         if (Ferror(fd) || (size > 0 && nb != blen)) {
03768             rc = 1;
03769             goto exit;
03770         }
03771         if (blen == blenmax && nb < blen) {
03772             blen = nb;
03773             b = xrealloc(b, blen+1);
03774         }
03775         b[blen] = '\0';
03776     }
03777     /*@=branchstate@*/
03778 
03779 exit:
03780     if (fd) (void) Fclose(fd);
03781         
03782     if (rc) {
03783         if (b) free(b);
03784         b = NULL;
03785         blen = 0;
03786     }
03787 
03788     if (bp) *bp = b;
03789     else if (b) free(b);
03790 
03791     if (blenp) *blenp = blen;
03792 
03793     return rc;
03794 }
03795 /*@=boundswrite@*/
03796 
03797 /*@-type@*/ /* LCL: function typedefs */
03798 static struct FDIO_s fpio_s = {
03799   ufdRead, ufdWrite, fdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
03800   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
03801 };
03802 /*@=type@*/
03803 FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;

Generated on Sat Feb 23 05:14:48 2008 for rpm by  doxygen 1.5.2