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

rpmio/rpmio.c

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

Generated on Mon Nov 29 2010 05:18:47 for rpm by  doxygen 1.7.2