Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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

Generated on Tue Oct 4 09:47:34 2011 for rpm by  doxygen 1.3.9.1