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

rpmio/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("@"));
            strcpy(myp, pw->pw_name);
            strcat(myp, "@");
            password = myp;
        } else {
            password = "root@";
01091         }
01092     }
01093     /*@=branchstate@*/
01094 
01095     /*@-branchstate@*/
01096     if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
01097         /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
01098     /*@=branchstate@*/
01099 
01100 /*@-usereleased@*/
01101     if (fdFileno(u->ctrl) < 0) {
01102         rc = tcpConnect(u->ctrl, host, port);
01103         if (rc < 0)
01104             goto errxit2;
01105     }
01106 
01107     if ((rc = ftpCheckResponse(u, NULL)))
01108         goto errxit;
01109 
01110     if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
01111         goto errxit;
01112 
01113     if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
01114         goto errxit;
01115 
01116     if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
01117         goto errxit;
01118 
01119     /*@-compdef@*/
01120     return 0;
01121     /*@=compdef@*/
01122 
01123 errxit:
01124     /*@-observertrans@*/
01125     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
01126     /*@=observertrans@*/
01127 errxit2:
01128     /*@-branchstate@*/
01129     if (fdFileno(u->ctrl) >= 0)
01130         /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
01131     /*@=branchstate@*/
01132     /*@-compdef@*/
01133     return rc;
01134     /*@=compdef@*/
01135 /*@=usereleased@*/
01136 }
01137 
01138 int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
01139 {
01140     urlinfo u = data->url;
01141     struct sockaddr_in dataAddress;
01142     char * cmd;
01143     int cmdlen;
01144     char * passReply;
01145     char * chptr;
01146     int rc;
01147 
01148     URLSANE(u);
01149     if (ftpCmd == NULL)
01150         return FTPERR_UNKNOWN;  /* XXX W2DO? */
01151 
01152     cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
01153     chptr = cmd = alloca(cmdlen);
01154     chptr = stpcpy(chptr, ftpCmd);
01155     if (ftpArg) {
01156         *chptr++ = ' ';
01157         chptr = stpcpy(chptr, ftpArg);
01158     }
01159     chptr = stpcpy(chptr, "\r\n");
01160     cmdlen = chptr - cmd;
01161 
01162 /*
01163  * Get the ftp version of the Content-Length.
01164  */
01165     if (!strncmp(cmd, "RETR", 4)) {
01166         unsigned cl;
01167 
01168         passReply = NULL;
01169         rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
01170         if (rc)
01171             goto errxit;
01172         if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
01173             rc = FTPERR_BAD_SERVER_RESPONSE;
01174             goto errxit;
01175         }
01176         rc = 0;
01177         data->contentLength = cl;
01178     }
01179 
01180     passReply = NULL;
01181     rc = ftpCommand(u, &passReply, "PASV", NULL);
01182     if (rc) {
01183         rc = FTPERR_PASSIVE_ERROR;
01184         goto errxit;
01185     }
01186 
01187     chptr = passReply;
01188     while (*chptr && *chptr != '(') chptr++;
01189     if (*chptr != '(') return FTPERR_PASSIVE_ERROR; 
01190     chptr++;
01191     passReply = chptr;
01192     while (*chptr && *chptr != ')') chptr++;
01193     if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
01194     *chptr-- = '\0';
01195 
01196     while (*chptr && *chptr != ',') chptr--;
01197     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01198     chptr--;
01199     while (*chptr && *chptr != ',') chptr--;
01200     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
01201     *chptr++ = '\0';
01202     
01203     /* now passReply points to the IP portion, and chptr points to the
01204        port number portion */
01205 
01206     {   int i, j;
01207         memset(&dataAddress, 0, sizeof(dataAddress));
01208         dataAddress.sin_family = AF_INET;
01209         if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
01210             rc = FTPERR_PASSIVE_ERROR;
01211             goto errxit;
01212         }
01213         dataAddress.sin_port = htons((((unsigned)i) << 8) + j);
01214     }
01215 
01216     chptr = passReply;
01217     while (*chptr++ != '\0') {
01218         if (*chptr == ',') *chptr = '.';
01219     }
01220 
01221     /*@-moduncon@*/
01222     if (!inet_aton(passReply, &dataAddress.sin_addr)) {
01223         rc = FTPERR_PASSIVE_ERROR;
01224         goto errxit;
01225     }
01226     /*@=moduncon@*/
01227 
01228     rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
01229     fdSetFdno(data, (rc >= 0 ? rc : -1));
01230     if (rc < 0) {
01231         rc = FTPERR_FAILED_CONNECT;
01232         goto errxit;
01233     }
01234     data = fdLink(data, "open data (ftpReq)");
01235 
01236     /* XXX setsockopt SO_LINGER */
01237     /* XXX setsockopt SO_KEEPALIVE */
01238     /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
01239 
01240     /*@-internalglobs@*/
01241     while (connect(fdFileno(data), (struct sockaddr *) &dataAddress, 
01242                 sizeof(dataAddress)) < 0)
01243     {
01244         if (errno == EINTR)
01245             continue;
01246         rc = FTPERR_FAILED_DATA_CONNECT;
01247         goto errxit;
01248     }
01249     /*@=internalglobs@*/
01250 
01251 if (_ftp_debug)
01252 fprintf(stderr, "-> %s", cmd);
01253     if (fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
01254         rc = FTPERR_SERVER_IO_ERROR;
01255         goto errxit;
01256     }
01257 
01258     if ((rc = ftpCheckResponse(u, NULL))) {
01259         goto errxit;
01260     }
01261 
01262     data->ftpFileDoneNeeded = 1;
01263     u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
01264     u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
01265     return 0;
01266 
01267 errxit:
01268     /*@-observertrans@*/
01269     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
01270     /*@=observertrans@*/
01271     /*@-branchstate@*/
01272     if (fdFileno(data) >= 0)
01273         /*@-refcounttrans@*/ (void) fdClose(data); /*@=refcounttrans@*/
01274     /*@=branchstate@*/
01275     return rc;
01276 }
01277 
01278 /*@unchecked@*/ /*@null@*/
01279 static rpmCallbackFunction      urlNotify = NULL;
01280 
01281 /*@unchecked@*/ /*@null@*/
01282 static void *                   urlNotifyData = NULL;
01283 
01284 /*@unchecked@*/
01285 static int                      urlNotifyCount = -1;
01286 
01287 void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
01288     urlNotify = notify;
01289     urlNotifyData = notifyData;
01290     urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
01291 }
01292 
01293 int ufdCopy(FD_t sfd, FD_t tfd)
01294 {
01295     char buf[BUFSIZ];
01296     int itemsRead;
01297     int itemsCopied = 0;
01298     int rc = 0;
01299     int notifier = -1;
01300 
01301     if (urlNotify) {
01302         /*@-noeffectuncon @*/ /* FIX: check rc */
01303         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01304                 0, 0, NULL, urlNotifyData);
01305         /*@=noeffectuncon @*/
01306     }
01307     
01308     while (1) {
01309         rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
01310         if (rc < 0)
01311             break;
01312         else if (rc == 0) {
01313             rc = itemsCopied;
01314             break;
01315         }
01316         itemsRead = rc;
01317         rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
01318         if (rc < 0)
01319             break;
01320         if (rc != itemsRead) {
01321             rc = FTPERR_FILE_IO_ERROR;
01322             break;
01323         }
01324 
01325         itemsCopied += itemsRead;
01326         if (urlNotify && urlNotifyCount > 0) {
01327             int n = itemsCopied/urlNotifyCount;
01328             if (n != notifier) {
01329                 /*@-noeffectuncon @*/ /* FIX: check rc */
01330                 (void)(*urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
01331                         itemsCopied, 0, NULL, urlNotifyData);
01332                 /*@=noeffectuncon @*/
01333                 notifier = n;
01334             }
01335         }
01336     }
01337 
01338 /*@-modfilesys@*/
01339     DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
01340         ftpStrerror(rc)));
01341 /*@=modfilesys@*/
01342 
01343     if (urlNotify) {
01344         /*@-noeffectuncon @*/ /* FIX: check rc */
01345         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
01346                 itemsCopied, itemsCopied, NULL, urlNotifyData);
01347         /*@=noeffectuncon @*/
01348     }
01349     
01350     return rc;
01351 }
01352 
01353 static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
01354         /*@globals fileSystem @*/
01355         /*@modifies *uret, fileSystem @*/
01356 {
01357     urlinfo u;
01358     int rc = 0;
01359 
01360     if (urlSplit(url, &u) < 0)
01361         return -1;
01362 
01363     if (u->urltype == URL_IS_FTP) {
01364         FD_t fd;
01365 
01366         if ((fd = u->ctrl) == NULL) {
01367             fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
01368             fdSetIo(u->ctrl, ufdio);
01369         }
01370         
01371         fd->rd_timeoutsecs = ftpTimeoutSecs;
01372         fd->contentLength = fd->bytesRemain = -1;
01373         fd->url = NULL;         /* XXX FTP ctrl has not */
01374         fd->ftpFileDoneNeeded = 0;
01375         fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
01376 
01377         if (fdFileno(u->ctrl) < 0) {
01378             rpmMessage(RPMMESS_DEBUG, _("logging into %s as %s, pw %s\n"),
01379                         u->host ? u->host : "???",
01380                         u->user ? u->user : "ftp",
01381                         u->password ? u->password : "(username)");
01382 
01383             if ((rc = ftpLogin(u)) < 0) {       /* XXX save ftpLogin error */
01384                 u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
01385                 u->openError = rc;
01386             }
01387         }
01388     }
01389 
01390     if (uret != NULL)
01391         *uret = urlLink(u, "urlConnect");
01392     u = urlFree(u, "urlSplit (urlConnect)");    
01393 
01394     return rc;
01395 }
01396 
01397 int ufdGetFile(FD_t sfd, FD_t tfd)
01398 {
01399     int rc;
01400 
01401     FDSANE(sfd);
01402     FDSANE(tfd);
01403     rc = ufdCopy(sfd, tfd);
01404     (void) Fclose(sfd);
01405     if (rc > 0)         /* XXX ufdCopy now returns no. bytes copied */
01406         rc = 0;
01407     return rc;
01408 }
01409 
01410 int ftpCmd(const char * cmd, const char * url, const char * arg2)
01411 {
01412     urlinfo u;
01413     int rc;
01414     const char * path;
01415 
01416     if (urlConnect(url, &u) < 0)
01417         return -1;
01418 
01419     (void) urlPath(url, &path);
01420 
01421     rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
01422     u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
01423     return rc;
01424 }
01425 
01426 /* XXX these aren't worth the pain of including correctly */
01427 #if !defined(IAC)
01428 #define IAC     255             /* interpret as command: */
01429 #endif
01430 #if !defined(IP)
01431 #define IP      244             /* interrupt process--permanently */
01432 #endif
01433 #if !defined(DM)
01434 #define DM      242             /* data mark--for connect. cleaning */
01435 #endif
01436 #if !defined(SHUT_RDWR)
01437 #define SHUT_RDWR       1+1
01438 #endif
01439 
01440 static int ftpAbort(urlinfo u, FD_t data)
01441         /*@globals fileSystem @*/
01442         /*@modifies u, data, fileSystem @*/
01443 {
01444     static unsigned char ipbuf[3] = { IAC, IP, IAC };
01445     FD_t ctrl;
01446     int rc;
01447     int tosecs;
01448 
01449     URLSANE(u);
01450 
01451     if (data != NULL) {
01452         data->ftpFileDoneNeeded = 0;
01453         if (fdFileno(data) >= 0)
01454             u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
01455         u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
01456     }
01457     ctrl = u->ctrl;
01458 
01459 /*@-modfilesys@*/
01460     DBGIO(0, (stderr, "-> ABOR\n"));
01461 /*@=modfilesys@*/
01462 
01463 /*@-usereleased -compdef@*/
01464     if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
01465         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01466         return FTPERR_SERVER_IO_ERROR;
01467     }
01468 
01469     sprintf(u->buf, "%cABOR\r\n",(char) DM);
01470     if (fdWrite(ctrl, u->buf, 7) != 7) {
01471         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01472         return FTPERR_SERVER_IO_ERROR;
01473     }
01474 
01475     if (data && fdFileno(data) >= 0) {
01476         /* XXX shorten data drain time wait */
01477         tosecs = data->rd_timeoutsecs;
01478         data->rd_timeoutsecs = 10;
01479         if (fdReadable(data, data->rd_timeoutsecs) > 0) {
01480             while (timedRead(data, u->buf, u->bufAlloced) > 0)
01481                 u->buf[0] = '\0';
01482         }
01483         data->rd_timeoutsecs = tosecs;
01484         /* XXX ftp abort needs to close the data channel to receive status */
01485         (void) shutdown(fdFileno(data), SHUT_RDWR);
01486         (void) close(fdFileno(data));
01487         data->fps[0].fdno = -1; /* XXX WRONG but expedient */
01488     }
01489 
01490     /* XXX shorten ctrl drain time wait */
01491     tosecs = u->ctrl->rd_timeoutsecs;
01492     u->ctrl->rd_timeoutsecs = 10;
01493     if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
01494         rc = ftpCheckResponse(u, NULL);
01495     }
01496     rc = ftpCheckResponse(u, NULL);
01497     u->ctrl->rd_timeoutsecs = tosecs;
01498 
01499     return rc;
01500 /*@=usereleased =compdef@*/
01501 }
01502 
01503 static int ftpFileDone(urlinfo u, FD_t data)
01504         /*@globals fileSystem @*/
01505         /*@modifies u, data, fileSystem @*/
01506 {
01507     int rc = 0;
01508 
01509     URLSANE(u);
01510     assert(data->ftpFileDoneNeeded);
01511 
01512     if (data->ftpFileDoneNeeded) {
01513         data->ftpFileDoneNeeded = 0;
01514         u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
01515         u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
01516         rc = ftpCheckResponse(u, NULL);
01517     }
01518     return rc;
01519 }
01520 
01521 static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
01522         /*@globals fileSystem @*/
01523         /*@modifies ctrl, *str, fileSystem @*/
01524 {
01525     int ec = 0;
01526     int rc;
01527 
01528     URLSANE(u);
01529     rc = checkResponse(u, ctrl, &ec, str);
01530 
01531 if (_ftp_debug && !(rc == 0 && ec == 200))
01532 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
01533 
01534     switch (ec) {
01535     case 200:
01536         break;
01537     default:
01538         rc = FTPERR_FILE_NOT_FOUND;
01539         break;
01540     }
01541 
01542     return rc;
01543 }
01544 
01545 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
01546         /*@globals fileSystem @*/
01547         /*@modifies ctrl, fileSystem @*/
01548 {
01549     urlinfo u = ctrl->url;
01550     const char * host;
01551     const char * path;
01552     int port;
01553     int rc;
01554     char * req;
01555     size_t len;
01556     int retrying = 0;
01557 
01558     URLSANE(u);
01559     assert(ctrl != NULL);
01560 
01561     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
01562         return FTPERR_BAD_HOSTNAME;
01563 
01564     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
01565     path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
01566     /*@-branchstate@*/
01567     if (path == NULL) path = "";
01568     /*@=branchstate@*/
01569 
01570 reopen:
01571     /*@-branchstate@*/
01572     if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
01573         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01574     }
01575     /*@=branchstate@*/
01576 
01577 /*@-usereleased@*/
01578     if (fdFileno(ctrl) < 0) {
01579         rc = tcpConnect(ctrl, host, port);
01580         if (rc < 0)
01581             goto errxit2;
01582         ctrl = fdLink(ctrl, "open ctrl (httpReq)");
01583     }
01584 
01585     len = sizeof("\
01586 req x HTTP/1.0\r\n\
01587 User-Agent: rpm/3.0.4\r\n\
01588 Host: y:z\r\n\
01589 Accept: text/plain\r\n\
01590 Transfer-Encoding: chunked\r\n\
01591 \r\n\
01592 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(host) + 20;
01593 
01594     req = alloca(len);
01595     *req = '\0';
01596 
01597   if (!strcmp(httpCmd, "PUT")) {
01598     sprintf(req, "\
01599 %s %s HTTP/1.%d\r\n\
01600 User-Agent: rpm/%s\r\n\
01601 Host: %s:%d\r\n\
01602 Accept: text/plain\r\n\
01603 Transfer-Encoding: chunked\r\n\
01604 \r\n\
01605 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
01606 } else {
01607     sprintf(req, "\
01608 %s %s HTTP/1.%d\r\n\
01609 User-Agent: rpm/%s\r\n\
01610 Host: %s:%d\r\n\
01611 Accept: text/plain\r\n\
01612 \r\n\
01613 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, host, port);
01614 }
01615 
01616 if (_ftp_debug)
01617 fprintf(stderr, "-> %s", req);
01618 
01619     len = strlen(req);
01620     if (fdWrite(ctrl, req, len) != len) {
01621         rc = FTPERR_SERVER_IO_ERROR;
01622         goto errxit;
01623     }
01624 
01625     /*@-branchstate@*/
01626     if (!strcmp(httpCmd, "PUT")) {
01627         ctrl->wr_chunked = 1;
01628     } else {
01629 
01630         rc = httpResp(u, ctrl, NULL);
01631 
01632         if (rc) {
01633             if (!retrying) {    /* not HTTP_OK */
01634                 retrying = 1;
01635                 /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01636                 goto reopen;
01637             }
01638             goto errxit;
01639         }
01640     }
01641     /*@=branchstate@*/
01642 
01643     ctrl = fdLink(ctrl, "open data (httpReq)");
01644     return 0;
01645 
01646 errxit:
01647     /*@-observertrans@*/
01648     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
01649     /*@=observertrans@*/
01650 errxit2:
01651     /*@-branchstate@*/
01652     if (fdFileno(ctrl) >= 0)
01653         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
01654     /*@=branchstate@*/
01655     return rc;
01656 /*@=usereleased@*/
01657 }
01658 
01659 /* XXX DYING: unused */
01660 void * ufdGetUrlinfo(FD_t fd)
01661 {
01662     FDSANE(fd);
01663     if (fd->url == NULL)
01664         return NULL;
01665     return urlLink(fd->url, "ufdGetUrlinfo");
01666 }
01667 
01668 /* =============================================================== */
01669 static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count)
01670         /*@globals fileSystem, internalState @*/
01671         /*@modifies *buf, fileSystem, internalState @*/
01672 {
01673     FD_t fd = c2f(cookie);
01674     int bytesRead;
01675     int total;
01676 
01677     *buf = '\0';        /* LCL: insistent bugger. */
01678     /* XXX preserve timedRead() behavior */
01679     if (fdGetIo(fd) == fdio) {
01680         struct stat sb;
01681         int fdno = fdFileno(fd);
01682         (void) fstat(fdno, &sb);
01683         if (S_ISREG(sb.st_mode))
01684             return fdRead(fd, buf, count);
01685     }
01686 
01687     UFDONLY(fd);
01688     assert(fd->rd_timeoutsecs >= 0);
01689 
01690     for (total = 0; total < count; total += bytesRead) {
01691 
01692         int rc;
01693 
01694         bytesRead = 0;
01695 
01696         /* Is there data to read? */
01697         if (fd->bytesRemain == 0) return total; /* XXX simulate EOF */
01698         rc = fdReadable(fd, fd->rd_timeoutsecs);
01699 
01700         switch (rc) {
01701         case -1:        /* error */
01702         case  0:        /* timeout */
01703             return total;
01704             /*@notreached@*/ /*@switchbreak@*/ break;
01705         default:        /* data to read */
01706             /*@switchbreak@*/ break;
01707         }
01708 
01709         rc = fdRead(fd, buf + total, count - total);
01710 
01711         if (rc < 0) {
01712             switch (errno) {
01713             case EWOULDBLOCK:
01714                 continue;
01715                 /*@notreached@*/ /*@switchbreak@*/ break;
01716             default:
01717                 /*@switchbreak@*/ break;
01718             }
01719 if (_rpmio_debug)
01720 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
01721             return rc;
01722             /*@notreached@*/ break;
01723         } else if (rc == 0) {
01724             return total;
01725             /*@notreached@*/ break;
01726         }
01727         bytesRead = rc;
01728     }
01729 
01730     return count;
01731 }
01732 
01733 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
01734         /*@globals fileSystem, internalState @*/
01735         /*@modifies fileSystem, internalState @*/
01736 {
01737     FD_t fd = c2f(cookie);
01738     int bytesWritten;
01739     int total = 0;
01740 
01741 #ifdef  NOTYET
01742     if (fdGetIo(fd) == fdio) {
01743         struct stat sb;
01744         (void) fstat(fdGetFdno(fd), &sb);
01745         if (S_ISREG(sb.st_mode))
01746             return fdWrite(fd, buf, count);
01747     }
01748 #endif
01749 
01750     UFDONLY(fd);
01751 
01752     for (total = 0; total < count; total += bytesWritten) {
01753 
01754         int rc;
01755 
01756         bytesWritten = 0;
01757 
01758         /* Is there room to write data? */
01759         if (fd->bytesRemain == 0) {
01760 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
01761             return total;       /* XXX simulate EOF */
01762         }
01763         rc = fdWritable(fd, 2);         /* XXX configurable? */
01764 
01765         switch (rc) {
01766         case -1:        /* error */
01767         case  0:        /* timeout */
01768             return total;
01769             /*@notreached@*/ /*@switchbreak@*/ break;
01770         default:        /* data to write */
01771             /*@switchbreak@*/ break;
01772         }
01773 
01774         rc = fdWrite(fd, buf + total, count - total);
01775 
01776         if (rc < 0) {
01777             switch (errno) {
01778             case EWOULDBLOCK:
01779                 continue;
01780                 /*@notreached@*/ /*@switchbreak@*/ break;
01781             default:
01782                 /*@switchbreak@*/ break;
01783             }
01784 if (_rpmio_debug)
01785 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
01786             return rc;
01787             /*@notreached@*/ break;
01788         } else if (rc == 0) {
01789             return total;
01790             /*@notreached@*/ break;
01791         }
01792         bytesWritten = rc;
01793     }
01794 
01795     return count;
01796 }
01797 
01798 static inline int ufdSeek(void * cookie, _libio_pos_t pos, int whence)
01799         /*@globals fileSystem, internalState @*/
01800         /*@modifies fileSystem, internalState @*/
01801 {
01802     FD_t fd = c2f(cookie);
01803 
01804     switch (fd->urlType) {
01805     case URL_IS_UNKNOWN:
01806     case URL_IS_PATH:
01807         break;
01808     case URL_IS_DASH:
01809     case URL_IS_FTP:
01810     case URL_IS_HTTP:
01811     default:
01812         return -2;
01813         /*@notreached@*/ break;
01814     }
01815     return fdSeek(cookie, pos, whence);
01816 }
01817 
01818 /*@-branchstate@*/
01819 /*@-usereleased@*/      /* LCL: fd handling is tricky here. */
01820 int ufdClose( /*@only@*/ void * cookie)
01821 {
01822     FD_t fd = c2f(cookie);
01823 
01824     UFDONLY(fd);
01825 
01826     /*@-branchstate@*/
01827     if (fd->url) {
01828         urlinfo u = fd->url;
01829 
01830         if (fd == u->data)
01831                 fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
01832         else
01833                 fd = fdFree(fd, "grab data (ufdClose)");
01834         (void) urlFree(fd->url, "url (ufdClose)");
01835         fd->url = NULL;
01836         u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
01837 
01838         if (u->urltype == URL_IS_FTP) {
01839 
01840             /* XXX if not using libio, lose the fp from fpio */
01841             {   FILE * fp;
01842                 /*@+voidabstract -nullpass@*/
01843                 fp = fdGetFILE(fd);
01844                 if (noLibio && fp)
01845                     fdSetFp(fd, NULL);
01846                 /*@=voidabstract =nullpass@*/
01847             }
01848 
01849             /*
01850              * Normal FTP has 4 refs on the data fd:
01851              *  "persist data (ufdOpen FTP)"            rpmio.c:888
01852              *  "grab data (ufdOpen FTP)"               rpmio.c:892
01853              *  "open data (ftpReq)"                    ftp.c:633
01854              *  "fopencookie"                           rpmio.c:1507
01855              *
01856              * Normal FTP has 5 refs on the ctrl fd:
01857              *  "persist ctrl"                          url.c:176
01858              *  "grab ctrl (urlConnect FTP)"            rpmio.c:404
01859              *  "open ctrl"                             ftp.c:504
01860              *  "grab data (ftpReq)"                    ftp.c:661
01861              *  "open data (ftpReq)"                    ftp.c:662
01862              */
01863             if (fd->bytesRemain > 0) {
01864                 if (fd->ftpFileDoneNeeded) {
01865                     if (fdReadable(u->ctrl, 0) > 0)
01866                         (void) ftpFileDone(u, fd);
01867                     else
01868                         (void) ftpAbort(u, fd);
01869                 }
01870             } else {
01871                 int rc;
01872                 /* XXX STOR et al require close before ftpFileDone */
01873                 /*@-refcounttrans@*/
01874                 rc = fdClose(fd);
01875                 /*@=refcounttrans@*/
01876 #if 0   /* XXX error exit from ufdOpen does not have this set */
01877                 assert(fd->ftpFileDoneNeeded != 0);
01878 #endif
01879                 /*@-compdef@*/ /* FIX: u->data undefined */
01880                 if (fd->ftpFileDoneNeeded)
01881                     (void) ftpFileDone(u, fd);
01882                 /*@=compdef@*/
01883                 return rc;
01884             }
01885         }
01886 
01887         /* XXX Why not (u->urltype == URL_IS_HTTP) ??? */
01888         if (u->service != NULL && !strcmp(u->service, "http")) {
01889             if (fd->wr_chunked) {
01890                 int rc;
01891             /* XXX HTTP PUT requires terminating 0 length chunk. */
01892                 (void) fdWrite(fd, NULL, 0);
01893                 fd->wr_chunked = 0;
01894             /* XXX HTTP PUT requires terminating entity-header. */
01895 if (_ftp_debug)
01896 fprintf(stderr, "-> \r\n");
01897                 (void) fdWrite(fd, "\r\n", sizeof("\r\n")-1);
01898                 rc = httpResp(u, fd, NULL);
01899             }
01900 
01901             if (fd == u->ctrl)
01902                 fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
01903             else if (fd == u->data)
01904                 fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
01905             else
01906                 fd = fdFree(fd, "open data (ufdClose HTTP)");
01907 
01908             /*
01909              * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
01910              *  "persist ctrl"                          url.c:177
01911              *  "grab ctrl (ufdOpen HTTP)"              rpmio.c:924
01912              *  "grab data (ufdOpen HTTP)"              rpmio.c:928
01913              *  "open ctrl (httpReq)"                   ftp.c:382
01914              *  "open data (httpReq)"                   ftp.c:435
01915              */
01916 
01917             /* XXX if not using libio, lose the fp from fpio */
01918             {   FILE * fp;
01919                 /*@+voidabstract -nullpass@*/
01920                 fp = fdGetFILE(fd);
01921                 if (noLibio && fp)
01922                     fdSetFp(fd, NULL);
01923                 /*@=voidabstract =nullpass@*/
01924             }
01925 
01926             if (fd->persist && u->httpVersion &&
01927                 (fd == u->ctrl || fd == u->data) && fd->bytesRemain == 0) {
01928                 fd->contentLength = fd->bytesRemain = -1;
01929                 return 0;
01930             } else {
01931                 fd->contentLength = fd->bytesRemain = -1;
01932             }
01933         }
01934     }
01935     return fdClose(fd);
01936 }
01937 /*@=usereleased@*/
01938 /*@=branchstate@*/
01939 
01940 /*@-nullstate@*/        /* FIX: u->{ctrl,data}->url undef after XurlLink. */
01941 /*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
01942                 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
01943         /*@modifies *uret @*/
01944 {
01945     urlinfo u = NULL;
01946     FD_t fd = NULL;
01947 
01948 #if 0   /* XXX makeTempFile() heartburn */
01949     assert(!(flags & O_RDWR));
01950 #endif
01951     if (urlConnect(url, &u) < 0)
01952         goto exit;
01953 
01954     if (u->data == NULL)
01955         u->data = fdNew("persist data (ftpOpen)");
01956 
01957     if (u->data->url == NULL)
01958         fd = fdLink(u->data, "grab data (ftpOpen persist data)");
01959     else
01960         fd = fdNew("grab data (ftpOpen)");
01961 
01962     if (fd) {
01963         fdSetIo(fd, ufdio);
01964         fd->ftpFileDoneNeeded = 0;
01965         fd->rd_timeoutsecs = ftpTimeoutSecs;
01966         fd->contentLength = fd->bytesRemain = -1;
01967         fd->url = urlLink(u, "url (ufdOpen FTP)");
01968         fd->urlType = URL_IS_FTP;
01969     }
01970 
01971 exit:
01972     if (uret)
01973         *uret = u;
01974     /*@-refcounttrans@*/
01975     return fd;
01976     /*@=refcounttrans@*/
01977 }
01978 /*@=nullstate@*/
01979 
01980 /*@-nullstate@*/        /* FIX: u->{ctrl,data}->url undef after XurlLink. */
01981 static /*@null@*/ FD_t httpOpen(const char * url, /*@unused@*/ int flags,
01982                 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo * uret)
01983         /*@modifies *uret @*/
01984 {
01985     urlinfo u = NULL;
01986     FD_t fd = NULL;
01987 
01988 #if 0   /* XXX makeTempFile() heartburn */
01989     assert(!(flags & O_RDWR));
01990 #endif
01991     if (urlSplit(url, &u))
01992         goto exit;
01993 
01994     if (u->ctrl == NULL)
01995         u->ctrl = fdNew("persist ctrl (httpOpen)");
01996     if (u->ctrl->nrefs > 2 && u->data == NULL)
01997         u->data = fdNew("persist data (httpOpen)");
01998 
01999     if (u->ctrl->url == NULL)
02000         fd = fdLink(u->ctrl, "grab ctrl (httpOpen persist ctrl)");
02001     else if (u->data->url == NULL)
02002         fd = fdLink(u->data, "grab ctrl (httpOpen persist data)");
02003     else
02004         fd = fdNew("grab ctrl (httpOpen)");
02005 
02006     if (fd) {
02007         fdSetIo(fd, ufdio);
02008         fd->ftpFileDoneNeeded = 0;
02009         fd->rd_timeoutsecs = httpTimeoutSecs;
02010         fd->contentLength = fd->bytesRemain = -1;
02011         fd->url = urlLink(u, "url (httpOpen)");
02012         fd = fdLink(fd, "grab data (httpOpen)");
02013         fd->urlType = URL_IS_HTTP;
02014     }
02015 
02016 exit:
02017     if (uret)
02018         *uret = u;
02019     /*@-refcounttrans@*/
02020     return fd;
02021     /*@=refcounttrans@*/
02022 }
02023 /*@=nullstate@*/
02024 
02025 static /*@null@*/ FD_t ufdOpen(const char * url, int flags, mode_t mode)
02026         /*@globals fileSystem @*/
02027         /*@modifies fileSystem @*/
02028 {
02029     FD_t fd = NULL;
02030     const char * cmd;
02031     urlinfo u;
02032     const char * path;
02033     urltype urlType = urlPath(url, &path);
02034 
02035 if (_rpmio_debug)
02036 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
02037 
02038     /*@-branchstate@*/
02039     switch (urlType) {
02040     case URL_IS_FTP:
02041         fd = ftpOpen(url, flags, mode, &u);
02042         if (fd == NULL || u == NULL)
02043             break;
02044 
02045         /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
02046         cmd = ((flags & O_WRONLY) 
02047                 ?  ((flags & O_APPEND) ? "APPE" :
02048                    ((flags & O_CREAT) ? "STOR" : "STOR"))
02049                 :  ((flags & O_CREAT) ? "STOR" : "RETR"));
02050         u->openError = ftpReq(fd, cmd, path);
02051         if (u->openError < 0) {
02052             /* XXX make sure that we can exit through ufdClose */
02053             fd = fdLink(fd, "error data (ufdOpen FTP)");
02054         } else {
02055             fd->bytesRemain = ((!strcmp(cmd, "RETR"))
02056                 ?  fd->contentLength : -1);
02057             fd->wr_chunked = 0;
02058         }
02059         break;
02060     case URL_IS_HTTP:
02061         fd = httpOpen(url, flags, mode, &u);
02062         if (fd == NULL || u == NULL)
02063             break;
02064 
02065         cmd = ((flags & O_WRONLY)
02066                 ?  ((flags & O_APPEND) ? "PUT" :
02067                    ((flags & O_CREAT) ? "PUT" : "PUT"))
02068                 : "GET");
02069         u->openError = httpReq(fd, cmd, path);
02070         if (u->openError < 0) {
02071             /* XXX make sure that we can exit through ufdClose */
02072             fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
02073             fd = fdLink(fd, "error data (ufdOpen HTTP)");
02074         } else {
02075             fd->bytesRemain = ((!strcmp(cmd, "GET"))
02076                 ?  fd->contentLength : -1);
02077             fd->wr_chunked = ((!strcmp(cmd, "PUT"))
02078                 ?  fd->wr_chunked : 0);
02079         }
02080         break;
02081     case URL_IS_DASH:
02082         assert(!(flags & O_RDWR));
02083         fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
02084         if (fd) {
02085             fdSetIo(fd, ufdio);
02086             fd->rd_timeoutsecs = 600;   /* XXX W2DO? 10 mins? */
02087             fd->contentLength = fd->bytesRemain = -1;
02088         }
02089         break;
02090     case URL_IS_PATH:
02091     case URL_IS_UNKNOWN:
02092     default:
02093         fd = fdOpen(path, flags, mode);
02094         if (fd) {
02095             fdSetIo(fd, ufdio);
02096             fd->rd_timeoutsecs = 1;
02097             fd->contentLength = fd->bytesRemain = -1;
02098         }
02099         break;
02100     }
02101     /*@=branchstate@*/
02102 
02103     if (fd == NULL) return NULL;
02104     fd->urlType = urlType;
02105     if (Fileno(fd) < 0) {
02106         (void) ufdClose(fd);
02107         return NULL;
02108     }
02109 /*@-modfilesys@*/
02110 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
02111 /*@=modfilesys@*/
02112     return fd;
02113 }
02114 
02115 static struct FDIO_s ufdio_s = {
02116   ufdRead, ufdWrite, ufdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
02117   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
02118 };
02119 FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
02120 
02121 /* =============================================================== */
02122 /* Support for GZIP library.
02123  */
02124 #ifdef  HAVE_ZLIB_H
02125 /*@-moduncon@*/
02126 
02127 /*@-noparams@*/
02128 #include <zlib.h>
02129 /*@=noparams@*/
02130 
02131 static inline /*@dependent@*/ /*@null@*/ void * gzdFileno(FD_t fd)
02132         /*@*/
02133 {
02134     void * rc = NULL;
02135     int i;
02136 
02137     FDSANE(fd);
02138     for (i = fd->nfps; i >= 0; i--) {
02139         FDSTACK_t * fps = &fd->fps[i];
02140         if (fps->io != gzdio)
02141             continue;
02142         rc = fps->fp;
02143         break;
02144     }
02145     
02146     return rc;
02147 }
02148 
02149 static /*@null@*/ FD_t gzdOpen(const char * path, const char * fmode)
02150         /*@globals fileSystem @*/
02151         /*@modifies fileSystem @*/
02152 {
02153     FD_t fd;
02154     gzFile *gzfile;
02155     if ((gzfile = gzopen(path, fmode)) == NULL)
02156         return NULL;
02157     fd = fdNew("open (gzdOpen)");
02158     fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
02159     
02160 /*@-modfilesys@*/
02161 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
02162 /*@=modfilesys@*/
02163     return fdLink(fd, "gzdOpen");
02164 }
02165 
02166 /*@-globuse@*/
02167 static /*@null@*/ FD_t gzdFdopen(void * cookie, const char *fmode)
02168         /*@globals fileSystem, internalState @*/
02169         /*@modifies fileSystem, internalState @*/
02170 {
02171     FD_t fd = c2f(cookie);
02172     int fdno;
02173     gzFile *gzfile;
02174 
02175     if (fmode == NULL) return NULL;
02176     fdno = fdFileno(fd);
02177     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
02178     if (fdno < 0) return NULL;
02179     gzfile = gzdopen(fdno, fmode);
02180     if (gzfile == NULL) return NULL;
02181 
02182     fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
02183 
02184     return fdLink(fd, "gzdFdopen");
02185 }
02186 /*@=globuse@*/
02187 
02188 /*@-globuse@*/
02189 static int gzdFlush(FD_t fd)
02190         /*@globals fileSystem @*/
02191         /*@modifies fileSystem @*/
02192 {
02193     gzFile *gzfile;
02194     gzfile = gzdFileno(fd);
02195     if (gzfile == NULL) return -2;
02196     return gzflush(gzfile, Z_SYNC_FLUSH);       /* XXX W2DO? */
02197 }
02198 /*@=globuse@*/
02199 
02200 /* =============================================================== */
02201 /*@-mustmod@*/          /* LCL: *buf is modified */
02202 static ssize_t gzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
02203         /*@globals fileSystem, internalState @*/
02204         /*@modifies *buf, fileSystem, internalState @*/
02205 {
02206     FD_t fd = c2f(cookie);
02207     gzFile *gzfile;
02208     ssize_t rc;
02209 
02210     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
02211 
02212     gzfile = gzdFileno(fd);
02213     if (gzfile == NULL) return -2;      /* XXX can't happen */
02214 
02215     fdstat_enter(fd, FDSTAT_READ);
02216     /*@-compdef@*/ /* LCL: *buf is undefined */
02217     rc = gzread(gzfile, buf, count);
02218 /*@-modfilesys@*/
02219 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
02220 /*@=modfilesys@*/
02221     /*@=compdef@*/
02222     if (rc < 0) {
02223         int zerror = 0;
02224         fd->errcookie = gzerror(gzfile, &zerror);
02225         if (zerror == Z_ERRNO) {
02226             fd->syserrno = errno;
02227             fd->errcookie = strerror(fd->syserrno);
02228         }
02229     } else if (rc >= 0) {
02230         fdstat_exit(fd, FDSTAT_READ, rc);
02231         /*@-compdef@*/
02232         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, buf, rc);
02233         /*@=compdef@*/
02234     }
02235     return rc;
02236 }
02237 /*@=mustmod@*/
02238 
02239 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
02240         /*@globals fileSystem, internalState @*/
02241         /*@modifies fileSystem, internalState @*/
02242 {
02243     FD_t fd = c2f(cookie);
02244     gzFile *gzfile;
02245     ssize_t rc;
02246 
02247     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
02248 
02249     if (fd->ndigests && count > 0) fdUpdateDigests(fd, buf, count);
02250 
02251     gzfile = gzdFileno(fd);
02252     if (gzfile == NULL) return -2;      /* XXX can't happen */
02253 
02254     fdstat_enter(fd, FDSTAT_WRITE);
02255     rc = gzwrite(gzfile, (void *)buf, count);
02256 /*@-modfilesys@*/
02257 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
02258 /*@=modfilesys@*/
02259     if (rc < 0) {
02260         int zerror = 0;
02261         fd->errcookie = gzerror(gzfile, &zerror);
02262         if (zerror == Z_ERRNO) {
02263             fd->syserrno = errno;
02264             fd->errcookie = strerror(fd->syserrno);
02265         }
02266     } else if (rc > 0) {
02267         fdstat_exit(fd, FDSTAT_WRITE, rc);
02268     }
02269     return rc;
02270 }
02271 
02272 /* XXX zlib-1.0.4 has not */
02273 static inline int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
02274         /*@globals fileSystem, internalState @*/
02275         /*@modifies fileSystem, internalState @*/
02276 {
02277 #ifdef USE_COOKIE_SEEK_POINTER
02278     _IO_off64_t p = *pos;
02279 #else
02280     off_t p = pos;
02281 #endif
02282     int rc;
02283 #if HAVE_GZSEEK
02284     FD_t fd = c2f(cookie);
02285     gzFile *gzfile;
02286 
02287     if (fd == NULL) return -2;
02288     assert(fd->bytesRemain == -1);      /* XXX FIXME */
02289 
02290     gzfile = gzdFileno(fd);
02291     if (gzfile == NULL) return -2;      /* XXX can't happen */
02292 
02293     fdstat_enter(fd, FDSTAT_SEEK);
02294     rc = gzseek(gzfile, p, whence);
02295 /*@-modfilesys@*/
02296 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
02297 /*@=modfilesys@*/
02298     if (rc < 0) {
02299         int zerror = 0;
02300         fd->errcookie = gzerror(gzfile, &zerror);
02301         if (zerror == Z_ERRNO) {
02302             fd->syserrno = errno;
02303             fd->errcookie = strerror(fd->syserrno);
02304         }
02305     } else if (rc >= 0) {
02306         fdstat_exit(fd, FDSTAT_SEEK, rc);
02307     }
02308 #else
02309     rc = -2;
02310 #endif
02311     return rc;
02312 }
02313 
02314 static int gzdClose( /*@only@*/ void * cookie)
02315         /*@globals fileSystem, internalState @*/
02316         /*@modifies fileSystem, internalState @*/
02317 {
02318     FD_t fd = c2f(cookie);
02319     gzFile *gzfile;
02320     int rc;
02321 
02322     gzfile = gzdFileno(fd);
02323     if (gzfile == NULL) return -2;      /* XXX can't happen */
02324 
02325     fdstat_enter(fd, FDSTAT_CLOSE);
02326     /*@-dependenttrans@*/
02327     rc = gzclose(gzfile);
02328     /*@=dependenttrans@*/
02329 
02330     /* XXX TODO: preserve fd if errors */
02331 
02332     if (fd) {
02333 /*@-modfilesys@*/
02334 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
02335 /*@=modfilesys@*/
02336         if (rc < 0) {
02337             /*@-usereleased@*/
02338             fd->errcookie = gzerror(gzfile, &rc);
02339             /*@=usereleased@*/
02340             if (rc == Z_ERRNO) {
02341                 fd->syserrno = errno;
02342                 fd->errcookie = strerror(fd->syserrno);
02343             }
02344         } else if (rc >= 0) {
02345             fdstat_exit(fd, FDSTAT_CLOSE, rc);
02346         }
02347     }
02348 
02349 /*@-modfilesys@*/
02350 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
02351 /*@=modfilesys@*/
02352 
02353     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
02354     /*@-branchstate@*/
02355     if (rc == 0)
02356         fd = fdFree(fd, "open (gzdClose)");
02357     /*@=branchstate@*/
02358     return rc;
02359 }
02360 
02361 static struct FDIO_s gzdio_s = {
02362   gzdRead, gzdWrite, gzdSeek, gzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
02363   NULL, gzdOpen, gzdFileno, gzdFlush,   NULL, NULL, NULL, NULL, NULL
02364 };
02365 FDIO_t gzdio = /*@-compmempass@*/ &gzdio_s /*@=compmempass@*/ ;
02366 
02367 /*@=moduncon@*/
02368 #endif  /* HAVE_ZLIB_H */
02369 
02370 /* =============================================================== */
02371 /* Support for BZIP2 library.
02372  */
02373 #if HAVE_BZLIB_H
02374 /*@-moduncon@*/
02375 
02376 #include <bzlib.h>
02377 
02378 #ifdef HAVE_BZ2_1_0
02379 # define bzopen  BZ2_bzopen
02380 # define bzclose BZ2_bzclose
02381 # define bzdopen BZ2_bzdopen
02382 # define bzerror BZ2_bzerror
02383 # define bzflush BZ2_bzflush
02384 # define bzread  BZ2_bzread
02385 # define bzwrite BZ2_bzwrite
02386 #endif /* HAVE_BZ2_1_0 */
02387 
02388 static inline /*@dependent@*/ void * bzdFileno(FD_t fd)
02389         /*@*/
02390 {
02391     void * rc = NULL;
02392     int i;
02393 
02394     FDSANE(fd);
02395     for (i = fd->nfps; i >= 0; i--) {
02396         FDSTACK_t * fps = &fd->fps[i];
02397         if (fps->io != bzdio)
02398             continue;
02399         rc = fps->fp;
02400         break;
02401     }
02402     
02403     return rc;
02404 }
02405 
02406 /*@-globuse@*/
02407 static /*@null@*/ FD_t bzdOpen(const char * path, const char * mode)
02408         /*@globals fileSystem @*/
02409         /*@modifies fileSystem @*/
02410 {
02411     FD_t fd;
02412     BZFILE *bzfile;;
02413     if ((bzfile = bzopen(path, mode)) == NULL)
02414         return NULL;
02415     fd = fdNew("open (bzdOpen)");
02416     fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
02417     return fdLink(fd, "bzdOpen");
02418 }
02419 /*@=globuse@*/
02420 
02421 /*@-globuse@*/
02422 static /*@null@*/ FD_t bzdFdopen(void * cookie, const char * fmode)
02423         /*@globals fileSystem, internalState @*/
02424         /*@modifies fileSystem, internalState @*/
02425 {
02426     FD_t fd = c2f(cookie);
02427     int fdno;
02428     BZFILE *bzfile;
02429 
02430     if (fmode == NULL) return NULL;
02431     fdno = fdFileno(fd);
02432     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
02433     if (fdno < 0) return NULL;
02434     bzfile = bzdopen(fdno, fmode);
02435     if (bzfile == NULL) return NULL;
02436 
02437     fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
02438 
02439     return fdLink(fd, "bzdFdopen");
02440 }
02441 /*@=globuse@*/
02442 
02443 /*@-globuse@*/
02444 static int bzdFlush(FD_t fd)
02445         /*@globals fileSystem @*/
02446         /*@modifies fileSystem @*/
02447 {
02448     return bzflush(bzdFileno(fd));
02449 }
02450 /*@=globuse@*/
02451 
02452 /* =============================================================== */
02453 /*@-globuse@*/
02454 /*@-mustmod@*/          /* LCL: *buf is modified */
02455 static ssize_t bzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
02456         /*@globals fileSystem, internalState @*/
02457         /*@modifies *buf, fileSystem, internalState @*/
02458 {
02459     FD_t fd = c2f(cookie);
02460     BZFILE *bzfile;
02461     ssize_t rc = 0;
02462 
02463     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
02464     bzfile = bzdFileno(fd);
02465     fdstat_enter(fd, FDSTAT_READ);
02466     if (bzfile)
02467         /*@-compdef@*/
02468         rc = bzread(bzfile, buf, count);
02469         /*@=compdef@*/
02470     if (rc == -1) {
02471         int zerror = 0;
02472         if (bzfile)
02473             fd->errcookie = bzerror(bzfile, &zerror);
02474     } else if (rc >= 0) {
02475         fdstat_exit(fd, FDSTAT_READ, rc);
02476         /*@-compdef@*/
02477         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, buf, rc);
02478         /*@=compdef@*/
02479     }
02480     return rc;
02481 }
02482 /*@=mustmod@*/
02483 /*@=globuse@*/
02484 
02485 /*@-globuse@*/
02486 static ssize_t bzdWrite(void * cookie, const char * buf, size_t count)
02487         /*@globals fileSystem, internalState @*/
02488         /*@modifies fileSystem, internalState @*/
02489 {
02490     FD_t fd = c2f(cookie);
02491     BZFILE *bzfile;
02492     ssize_t rc;
02493 
02494     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
02495 
02496     if (fd->ndigests && count > 0) fdUpdateDigests(fd, buf, count);
02497 
02498     bzfile = bzdFileno(fd);
02499     fdstat_enter(fd, FDSTAT_WRITE);
02500     rc = bzwrite(bzfile, (void *)buf, count);
02501     if (rc == -1) {
02502         int zerror = 0;
02503         fd->errcookie = bzerror(bzfile, &zerror);
02504     } else if (rc > 0) {
02505         fdstat_exit(fd, FDSTAT_WRITE, rc);
02506     }
02507     return rc;
02508 }
02509 /*@=globuse@*/
02510 
02511 static inline int bzdSeek(void * cookie, /*@unused@*/ _libio_pos_t pos,
02512                         /*@unused@*/ int whence)
02513         /*@*/
02514 {
02515     FD_t fd = c2f(cookie);
02516 
02517     BZDONLY(fd);
02518     return -2;
02519 }
02520 
02521 static int bzdClose( /*@only@*/ void * cookie)
02522         /*@globals fileSystem, internalState @*/
02523         /*@modifies fileSystem, internalState @*/
02524 {
02525     FD_t fd = c2f(cookie);
02526     BZFILE *bzfile;
02527     int rc;
02528 
02529     bzfile = bzdFileno(fd);
02530 
02531     if (bzfile == NULL) return -2;
02532     fdstat_enter(fd, FDSTAT_CLOSE);
02533     /*@-noeffectuncon@*/ /* FIX: check rc */
02534     bzclose(bzfile);
02535     /*@=noeffectuncon@*/
02536     rc = 0;     /* XXX FIXME */
02537 
02538     /* XXX TODO: preserve fd if errors */
02539 
02540     if (fd) {
02541         if (rc == -1) {
02542             int zerror = 0;
02543             fd->errcookie = bzerror(bzfile, &zerror);
02544         } else if (rc >= 0) {
02545             fdstat_exit(fd, FDSTAT_CLOSE, rc);
02546         }
02547     }
02548 
02549 /*@-modfilesys@*/
02550 DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
02551 /*@=modfilesys@*/
02552 
02553     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
02554     /*@-branchstate@*/
02555     if (rc == 0)
02556         fd = fdFree(fd, "open (bzdClose)");
02557     /*@=branchstate@*/
02558     return rc;
02559 }
02560 
02561 static struct FDIO_s bzdio_s = {
02562   bzdRead, bzdWrite, bzdSeek, bzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
02563   NULL, bzdOpen, bzdFileno, bzdFlush,   NULL, NULL, NULL, NULL, NULL
02564 };
02565 FDIO_t bzdio = /*@-compmempass@*/ &bzdio_s /*@=compmempass@*/ ;
02566 
02567 /*@=moduncon@*/
02568 #endif  /* HAVE_BZLIB_H */
02569 
02570 /* =============================================================== */
02571 /*@observer@*/ static const char * getFdErrstr (FD_t fd)
02572         /*@*/
02573 {
02574     const char *errstr = NULL;
02575 
02576 #ifdef  HAVE_ZLIB_H
02577     if (fdGetIo(fd) == gzdio) {
02578         errstr = fd->errcookie;
02579     } else
02580 #endif  /* HAVE_ZLIB_H */
02581 
02582 #ifdef  HAVE_BZLIB_H
02583     if (fdGetIo(fd) == bzdio) {
02584         errstr = fd->errcookie;
02585     } else
02586 #endif  /* HAVE_BZLIB_H */
02587 
02588     {
02589         errstr = strerror(fd->syserrno);
02590     }
02591 
02592     return errstr;
02593 }
02594 
02595 /* =============================================================== */
02596 
02597 const char *Fstrerror(FD_t fd)
02598 {
02599     if (fd == NULL)
02600         return strerror(errno);
02601     FDSANE(fd);
02602     return getFdErrstr(fd);
02603 }
02604 
02605 #define FDIOVEC(_fd, _vec)      \
02606   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
02607 
02608 size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
02609     fdio_read_function_t _read;
02610     int rc;
02611 
02612     FDSANE(fd);
02613 #ifdef __LCLINT__
02614     *(char *)buf = '\0';
02615 #endif
02616 /*@-modfilesys@*/
02617 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
02618 /*@=modfilesys@*/
02619 
02620     if (fdGetIo(fd) == fpio) {
02621         /*@+voidabstract -nullpass@*/
02622         rc = fread(buf, size, nmemb, fdGetFILE(fd));
02623         /*@=voidabstract =nullpass@*/
02624         return rc;
02625     }
02626 
02627     /*@-nullderef@*/
02628     _read = FDIOVEC(fd, read);
02629     /*@=nullderef@*/
02630 
02631     rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
02632     return rc;
02633 }
02634 
02635 size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
02636 {
02637     fdio_write_function_t _write;
02638     int rc;
02639 
02640     FDSANE(fd);
02641 /*@-modfilesys@*/
02642 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
02643 /*@=modfilesys@*/
02644 
02645     if (fdGetIo(fd) == fpio) {
02646         /*@+voidabstract -nullpass@*/
02647         rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
02648         /*@=voidabstract =nullpass@*/
02649         return rc;
02650     }
02651 
02652     /*@-nullderef@*/
02653     _write = FDIOVEC(fd, write);
02654     /*@=nullderef@*/
02655 
02656     rc = (_write ? _write(fd, buf, size * nmemb) : -2);
02657     return rc;
02658 }
02659 
02660 int Fseek(FD_t fd, _libio_off_t offset, int whence) {
02661     fdio_seek_function_t _seek;
02662 #ifdef USE_COOKIE_SEEK_POINTER
02663     _IO_off64_t o64 = offset;
02664     _libio_pos_t pos = &o64;
02665 #else
02666     _libio_pos_t pos = offset;
02667 #endif
02668 
02669     long int rc;
02670 
02671     FDSANE(fd);
02672 /*@-modfilesys@*/
02673 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
02674 /*@=modfilesys@*/
02675 
02676     if (fdGetIo(fd) == fpio) {
02677         FILE *fp;
02678 
02679         /*@+voidabstract -nullpass@*/
02680         fp = fdGetFILE(fd);
02681         rc = fseek(fp, offset, whence);
02682         /*@=voidabstract =nullpass@*/
02683         return rc;
02684     }
02685 
02686     /*@-nullderef@*/
02687     _seek = FDIOVEC(fd, seek);
02688     /*@=nullderef@*/
02689 
02690     rc = (_seek ? _seek(fd, pos, whence) : -2);
02691     return rc;
02692 }
02693 
02694 int Fclose(FD_t fd)
02695 {
02696     int rc = 0, ec = 0;
02697 
02698     FDSANE(fd);
02699 /*@-modfilesys@*/
02700 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
02701 /*@=modfilesys@*/
02702 
02703     fd = fdLink(fd, "Fclose");
02704     /*@-branchstate@*/
02705     while (fd->nfps >= 0) {
02706         FDSTACK_t * fps = &fd->fps[fd->nfps];
02707         
02708         if (fps->io == fpio) {
02709             FILE *fp;
02710             int fpno;
02711 
02712             /*@+voidabstract -nullpass@*/
02713             fp = fdGetFILE(fd);
02714             fpno = fileno(fp);
02715             /*@=voidabstract =nullpass@*/
02716         /* XXX persistent HTTP/1.1 returns the previously opened fp */
02717             if (fd->nfps > 0 && fpno == -1 &&
02718                 fd->fps[fd->nfps-1].io == ufdio &&
02719                 fd->fps[fd->nfps-1].fp == fp &&
02720                 fd->fps[fd->nfps-1].fdno >= 0)
02721             {
02722                 if (fp)
02723                     rc = fflush(fp);
02724                 fd->nfps--;
02725                 /*@-refcounttrans@*/
02726                 rc = ufdClose(fd);
02727                 /*@=refcounttrans@*/
02728 /*@-usereleased@*/
02729                 if (fdGetFdno(fd) >= 0)
02730                     break;
02731                 fdSetFp(fd, NULL);
02732                 fd->nfps++;
02733                 if (fp)
02734                     rc = fclose(fp);
02735                 fdPop(fd);
02736                 if (noLibio)
02737                     fdSetFp(fd, NULL);
02738             } else {
02739                 if (fp)
02740                     rc = fclose(fp);
02741                 if (fpno == -1) {
02742                     fd = fdFree(fd, "fopencookie (Fclose)");
02743                     fdPop(fd);
02744                 }
02745             }
02746         } else {
02747             /*@-nullderef@*/
02748             fdio_close_function_t _close = FDIOVEC(fd, close);
02749             /*@=nullderef@*/
02750             rc = _close(fd);
02751         }
02752         if (fd->nfps == 0)
02753             break;
02754         if (ec == 0 && rc)
02755             ec = rc;
02756         fdPop(fd);
02757     }
02758     /*@=branchstate@*/
02759     fd = fdFree(fd, "Fclose");
02760     return ec;
02761 /*@=usereleased@*/
02762 }
02763 
02775 static inline void cvtfmode (const char *m,
02776                                 /*@out@*/ char *stdio, size_t nstdio,
02777                                 /*@out@*/ char *other, size_t nother,
02778                                 /*@out@*/ const char **end, /*@out@*/ int * f)
02779         /*@modifies *stdio, *other, *end, *f @*/
02780 {
02781     int flags = 0;
02782     char c;
02783 
02784     switch (*m) {
02785     case 'a':
02786         flags |= O_WRONLY | O_CREAT | O_APPEND;
02787         if (--nstdio > 0) *stdio++ = *m;
02788         break;
02789     case 'w':
02790         flags |= O_WRONLY | O_CREAT | O_TRUNC;
02791         if (--nstdio > 0) *stdio++ = *m;
02792         break;
02793     case 'r':
02794         flags |= O_RDONLY;
02795         if (--nstdio > 0) *stdio++ = *m;
02796         break;
02797     default:
02798         *stdio = '\0';
02799         return;
02800         /*@notreached@*/ break;
02801     }
02802     m++;
02803 
02804     while ((c = *m++) != '\0') {
02805         switch (c) {
02806         case '.':
02807             /*@switchbreak@*/ break;
02808         case '+':
02809             flags &= ~(O_RDONLY|O_WRONLY);
02810             flags |= O_RDWR;
02811             if (--nstdio > 0) *stdio++ = c;
02812             continue;
02813             /*@notreached@*/ /*@switchbreak@*/ break;
02814         case 'b':
02815             if (--nstdio > 0) *stdio++ = c;
02816             continue;
02817             /*@notreached@*/ /*@switchbreak@*/ break;
02818         case 'x':
02819             flags |= O_EXCL;
02820             if (--nstdio > 0) *stdio++ = c;
02821             continue;
02822             /*@notreached@*/ /*@switchbreak@*/ break;
02823         default:
02824             if (--nother > 0) *other++ = c;
02825             continue;
02826             /*@notreached@*/ /*@switchbreak@*/ break;
02827         }
02828         break;
02829     }
02830 
02831     *stdio = *other = '\0';
02832     if (end != NULL)
02833         *end = (*m != '\0' ? m : NULL);
02834     if (f != NULL)
02835         *f = flags;
02836 }
02837 
02838 #if _USE_LIBIO
02839 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
02840 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
02841 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
02842 #endif
02843 #endif
02844 
02845 FD_t Fdopen(FD_t ofd, const char *fmode)
02846 {
02847     char stdio[20], other[20], zstdio[20];
02848     const char *end = NULL;
02849     FDIO_t iof = NULL;
02850     FD_t fd = ofd;
02851 
02852 if (_rpmio_debug)
02853 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
02854     FDSANE(fd);
02855 
02856     if (fmode == NULL)
02857         return NULL;
02858 
02859     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
02860     if (stdio[0] == '\0')
02861         return NULL;
02862     zstdio[0] = '\0';
02863     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
02864     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
02865 
02866     if (end == NULL && other[0] == '\0')
02867         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
02868 
02869     /*@-branchstate@*/
02870     if (end && *end) {
02871         if (!strcmp(end, "fdio")) {
02872             iof = fdio;
02873         } else if (!strcmp(end, "gzdio")) {
02874             iof = gzdio;
02875             /*@-internalglobs@*/
02876             fd = gzdFdopen(fd, zstdio);
02877             /*@=internalglobs@*/
02878 #if HAVE_BZLIB_H
02879         } else if (!strcmp(end, "bzdio")) {
02880             iof = bzdio;
02881             /*@-internalglobs@*/
02882             fd = bzdFdopen(fd, zstdio);
02883             /*@=internalglobs@*/
02884 #endif
02885         } else if (!strcmp(end, "ufdio")) {
02886             iof = ufdio;
02887         } else if (!strcmp(end, "fadio")) {
02888             iof = fadio;
02889         } else if (!strcmp(end, "fpio")) {
02890             iof = fpio;
02891             if (noLibio) {
02892                 int fdno = Fileno(fd);
02893                 FILE * fp = fdopen(fdno, stdio);
02894 /*@+voidabstract -nullpass@*/
02895 if (_rpmio_debug)
02896 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
02897 /*@=voidabstract =nullpass@*/
02898                 if (fp == NULL)
02899                     return NULL;
02900                 /* XXX gzdio/bzdio use fp for private data */
02901                 /*@+voidabstract@*/
02902                 if (fdGetFp(fd) == NULL)
02903                     fdSetFp(fd, fp);
02904                 fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
02905                 /*@=voidabstract@*/
02906             }
02907         }
02908     } else if (other[0] != '\0') {
02909         for (end = other; *end && strchr("0123456789fh", *end); end++)
02910             {};
02911         if (*end == '\0') {
02912             iof = gzdio;
02913             /*@-internalglobs@*/
02914             fd = gzdFdopen(fd, zstdio);
02915             /*@=internalglobs@*/
02916         }
02917     }
02918     /*@=branchstate@*/
02919     if (iof == NULL)
02920         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
02921 
02922     if (!noLibio) {
02923         FILE * fp = NULL;
02924 
02925 #if _USE_LIBIO
02926         {   cookie_io_functions_t ciof;
02927             ciof.read = iof->read;
02928             ciof.write = iof->write;
02929             ciof.seek = iof->seek;
02930             ciof.close = iof->close;
02931             fp = fopencookie(fd, stdio, ciof);
02932 /*@-modfilesys@*/
02933 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
02934 /*@=modfilesys@*/
02935         }
02936 #endif
02937 
02938         /*@-branchstate@*/
02939         if (fp) {
02940             /* XXX gzdio/bzdio use fp for private data */
02941             /*@+voidabstract -nullpass@*/
02942             if (fdGetFp(fd) == NULL)
02943                 fdSetFp(fd, fp);
02944             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
02945             /*@=voidabstract =nullpass@*/
02946             fd = fdLink(fd, "fopencookie");
02947         }
02948         /*@=branchstate@*/
02949     }
02950 
02951 /*@-modfilesys@*/
02952 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
02953 /*@=modfilesys@*/
02954     /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
02955 }
02956 
02957 FD_t Fopen(const char *path, const char *fmode)
02958 {
02959     char stdio[20], other[20];
02960     const char *end = NULL;
02961     mode_t perms = 0666;
02962     int flags;
02963     FD_t fd;
02964 
02965     if (path == NULL || fmode == NULL)
02966         return NULL;
02967 
02968     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
02969     if (stdio[0] == '\0')
02970         return NULL;
02971 
02972     /*@-branchstate@*/
02973     if (end == NULL || !strcmp(end, "fdio")) {
02974 if (_rpmio_debug)
02975 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
02976         fd = fdOpen(path, flags, perms);
02977         if (fdFileno(fd) < 0) {
02978             if (fd) (void) fdClose(fd);
02979             return NULL;
02980         }
02981     } else if (!strcmp(end, "fadio")) {
02982 if (_rpmio_debug)
02983 fprintf(stderr, "*** Fopen fadio path %s fmode %s\n", path, fmode);
02984         fd = fadio->_open(path, flags, perms);
02985         if (fdFileno(fd) < 0) {
02986             /*@-refcounttrans@*/ (void) fdClose(fd); /*@=refcounttrans@*/
02987             return NULL;
02988         }
02989     } else {
02990         FILE *fp;
02991         int fdno;
02992         int isHTTP = 0;
02993 
02994         /* XXX gzdio and bzdio here too */
02995 
02996         switch (urlIsURL(path)) {
02997         case URL_IS_HTTP:
02998             isHTTP = 1;
02999             /*@fallthrough@*/
03000         case URL_IS_PATH:
03001         case URL_IS_DASH:
03002         case URL_IS_FTP:
03003         case URL_IS_UNKNOWN:
03004 if (_rpmio_debug)
03005 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
03006             fd = ufdOpen(path, flags, perms);
03007             if (fd == NULL || fdFileno(fd) < 0)
03008                 return fd;
03009             break;
03010         default:
03011 if (_rpmio_debug)
03012 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
03013             return NULL;
03014             /*@notreached@*/ break;
03015         }
03016 
03017         /* XXX persistent HTTP/1.1 returns the previously opened fp */
03018         if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0)) {
03019             /*@+voidabstract@*/
03020             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
03021             /*@=voidabstract@*/
03022             return fd;
03023         }
03024     }
03025     /*@=branchstate@*/
03026 
03027     /*@-branchstate@*/
03028     if (fd)
03029         fd = Fdopen(fd, fmode);
03030     /*@=branchstate@*/
03031     return fd;
03032 }
03033 
03034 int Fflush(FD_t fd)
03035 {
03036     void * vh;
03037     if (fd == NULL) return -1;
03038     if (fdGetIo(fd) == fpio)
03039         /*@+voidabstract -nullpass@*/
03040         return fflush(fdGetFILE(fd));
03041         /*@=voidabstract =nullpass@*/
03042 
03043     vh = fdGetFp(fd);
03044     if (vh && fdGetIo(fd) == gzdio)
03045         return gzdFlush(vh);
03046 #if HAVE_BZLIB_H
03047     if (vh && fdGetIo(fd) == bzdio)
03048         return bzdFlush(vh);
03049 #endif
03050 
03051     return 0;
03052 }
03053 
03054 int Ferror(FD_t fd)
03055 {
03056     int i, rc = 0;
03057 
03058     if (fd == NULL) return -1;
03059     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
03060         FDSTACK_t * fps = &fd->fps[i];
03061         int ec;
03062         
03063         if (fps->io == fpio) {
03064             /*@+voidabstract -nullpass@*/
03065             ec = ferror(fdGetFILE(fd));
03066             /*@=voidabstract =nullpass@*/
03067         } else if (fps->io == gzdio) {
03068             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
03069             i--;        /* XXX fdio under gzdio always has fdno == -1 */
03070 #if HAVE_BZLIB_H
03071         } else if (fps->io == bzdio) {
03072             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
03073             i--;        /* XXX fdio under bzdio always has fdno == -1 */
03074 #endif
03075         } else {
03076         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
03077             ec = (fdFileno(fd) < 0 ? -1 : 0);
03078         }
03079 
03080         if (rc == 0 && ec)
03081             rc = ec;
03082     }
03083 /*@-modfilesys@*/
03084 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
03085 /*@=modfilesys@*/
03086     return rc;
03087 }
03088 
03089 int Fileno(FD_t fd)
03090 {
03091     int i, rc = -1;
03092 
03093     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
03094         rc = fd->fps[i].fdno;
03095     }
03096 /*@-modfilesys@*/
03097 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
03098 /*@=modfilesys@*/
03099     return rc;
03100 }
03101 
03102 /* XXX this is naive */
03103 int Fcntl(FD_t fd, int op, void *lip)
03104 {
03105     return fcntl(Fileno(fd), op, lip);
03106 }
03107 
03108 /* =============================================================== */
03109 /* Helper routines that may be generally useful.
03110  */
03111 
03112 int rpmioSlurp(const char * fn, const byte ** bp, ssize_t * blenp)
03113 {
03114     static ssize_t blenmax = (8 * BUFSIZ);
03115     ssize_t blen = 0;
03116     byte * b = NULL;
03117     ssize_t size;
03118     FD_t fd;
03119     int rc = 0;
03120 
03121     fd = Fopen(fn, "r.ufdio");
03122     if (fd == NULL || Ferror(fd)) {
03123         rc = 2;
03124         goto exit;
03125     }
03126 
03127     size = fdSize(fd);
03128     blen = (size >= 0 ? size : blenmax);
03129     /*@-branchstate@*/
03130     if (blen) {
03131         int nb;
03132         b = xmalloc(blen+1);
03133         b[0] = '\0';
03134         nb = Fread(b, sizeof(*b), blen, fd);
03135         if (Ferror(fd) || (size > 0 && nb != blen)) {
03136             rc = 1;
03137             goto exit;
03138         }
03139         if (blen == blenmax && nb < blen) {
03140             blen = nb;
03141             b = xrealloc(b, blen+1);
03142         }
03143         b[blen] = '\0';
03144     }
03145     /*@=branchstate@*/
03146 
03147 exit:
03148     if (fd) (void) Fclose(fd);
03149         
03150     if (rc) {
03151         if (b) free(b);
03152         b = NULL;
03153         blen = 0;
03154     }
03155 
03156     if (bp) *bp = b;
03157     else if (b) free(b);
03158 
03159     if (blenp) *blenp = blen;
03160 
03161     return rc;
03162 }
03163 
03164 static struct FDIO_s fpio_s = {
03165   ufdRead, ufdWrite, fdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
03166   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
03167 };
03168 FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;
03169 /*@=type@*/
03170 

Generated on Sat Oct 23 07:55:47 2004 for rpm by doxygen 1.3.6