Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

rpmio.c

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

Generated on Mon Apr 4 20:06:31 2005 for rpm by  doxygen 1.4.1