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

rpmio/rpmio.c

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

Generated on Wed Dec 28 16:55:18 2016 for rpm by  doxygen 1.4.4