- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

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