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

Generated on Fri Oct 12 08:44:54 2007 for rpm by  doxygen 1.5.2