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

Generated on Fri Aug 31 11:16:08 2007 for rpm by  doxygen 1.4.6