# rpmio/rpmio.c

Go to the documentation of this file.

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