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