rpmio/rpmio.c

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

Generated on Fri Aug 11 12:48:15 2006 for rpm by  doxygen 1.4.7