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

Generated on Wed Feb 6 22:32:15 2008 for rpm by  doxygen 1.5.1