• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

rpmio/rpmio.c

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

Generated on Fri Dec 3 2010 20:54:30 for rpm by  doxygen 1.7.2