rpmio/rpmio.c

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

Generated on Fri Aug 31 10:38:35 2007 for rpm by  doxygen 1.5.1