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

- File List
- Globals

# rpmio/rpmio.c

Go to the documentation of this file.

    00001
    00005 #include "system.h"
    00006 #include <stdarg.h>
    00007
    00008 #if defined(HAVE_MACHINE_TYPES_H)
    00009 # include <machine/types.h>
    00010 #endif
    00011
    00012 #if defined(HAVE_SYS_SOCKET_H)
    00013 # include <sys/socket.h>
    00014 #endif
    00015
    00016 #ifndef NI_MAXHOST
    00017 #define NI_MAXHOST      1025
    00018 #endif
    00019
    00020 #if defined(__LCLINT__)
    00021 struct addrinfo
    00022 {
    00023   int ai_flags;                 /* Input flags.  */
    00024   int ai_family;                /* Protocol family for socket.  */
    00025   int ai_socktype;              /* Socket type.  */
    00026   int ai_protocol;              /* Protocol for socket.  */
    00027   socklen_t ai_addrlen;         /* Length of socket address.  */
    00028   struct sockaddr *ai_addr;     /* Socket address for socket.  */
    00029   char *ai_canonname;           /* Canonical name for service location.  */
    00030   struct addrinfo *ai_next;     /* Pointer to next in list.  */
    00031 };
    00032
    00033 /*@-exportheader@*/
    00034 extern int getaddrinfo (__const char *__restrict __name,
    00035                         __const char *__restrict __service,
    00036                         __const struct addrinfo *__restrict __req,
    00037                         /*@out@*/ struct addrinfo **__restrict __pai)
    00038         /*@modifies *__pai @*/;
    00039
    00040 extern int getnameinfo (__const struct sockaddr *__restrict __sa,
    00041                         socklen_t __salen, /*@out@*/ char *__restrict __host,
    00042                         socklen_t __hostlen, /*@out@*/ char *__restrict __serv,
    00043                         socklen_t __servlen, unsigned int __flags)
    00044         /*@modifies __host, __serv @*/;
    00045
    00046 extern void freeaddrinfo (/*@only@*/ struct addrinfo *__ai)
    00047         /*@modifies __ai @*/;
    00048 /*@=exportheader@*/
    00049 #else
    00050 #include <netdb.h>              /* XXX getaddrinfo et al */
    00051 #endif
    00052
    00053 #include <netinet/in.h>
    00054 #include <arpa/inet.h>          /* XXX for inet_aton and HP-UX */
    00055
    00056 #if defined(HAVE_NETINET_IN_SYSTM_H)
    00057 # include <sys/types.h>
    00058 # include <netinet/in_systm.h>
    00059 #endif
    00060
    00061 #include <rpmmacro.h>           /* XXX rpmioAccess needs rpmCleanPath() */
    00062 #include <rpmlua.h>             /* XXX rpmioClean() calls rpmluaFree() */
    00063
    00064 #if defined(HAVE_LIBIO_H) && defined(_G_IO_IO_FILE_VERSION)
    00065 #define _USE_LIBIO      1
    00066 #endif
    00067
    00068 /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
    00069 #if !defined(HAVE_HERRNO) && (defined(hpux) || defined(__hpux) || defined(__LCLINT__))
    00070 /*@unchecked@*/
    00071 extern int h_errno;
    00072 #endif
    00073
    00074 #ifndef IPPORT_FTP
    00075 #define IPPORT_FTP      21
    00076 #endif
    00077 #ifndef IPPORT_HTTP
    00078 #define IPPORT_HTTP     80
    00079 #endif
    00080
    00081 #if !defined(HAVE_INET_ATON)
    00082 #define inet_aton(cp,inp) rpm_inet_aton(cp,inp)
    00083 static int rpm_inet_aton(const char *cp, struct in_addr *inp)
    00084         /*@modifies *inp @*/
    00085 {
    00086     long addr;
    00087
    00088     addr = inet_addr(cp);
    00089     if (addr == ((long) -1)) return 0;
    00090
    00091     memcpy(inp, &addr, sizeof(addr));
    00092     return 1;
    00093 }
    00094 #endif
    00095
    00096 #if defined(USE_ALT_DNS) && USE_ALT_DNS
    00097 #include "dns.h"
    00098 #endif
    00099
    00100 #include <rpmio_internal.h>
    00101 #undef  fdFileno
    00102 #undef  fdOpen
    00103 #define fdOpen  __fdOpen
    00104 #undef  fdRead
    00105 #define fdRead  __fdRead
    00106 #undef  fdWrite
    00107 #define fdWrite __fdWrite
    00108 #undef  fdClose
    00109 #define fdClose __fdClose
    00110
    00111 #include <rpmdav.h>
    00112 #include "ugid.h"
    00113 #include "rpmcb.h"
    00114
    00115 #include "debug.h"
    00116
    00117 /*@access FILE @*/      /* XXX to permit comparison/conversion with void *. */
    00118 /*@access urlinfo @*/
    00119 /*@access FDSTAT_t @*/
    00120
    00121 #define FDNREFS(fd)     (fd ? ((FD_t)fd)->nrefs : -9)
    00122 #define FDTO(fd)        (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
    00123 #define FDCPIOPOS(fd)   (fd ? ((FD_t)fd)->fd_cpioPos : -99)
    00124
    00125 #define FDONLY(fd)      assert(fdGetIo(fd) == fdio)
    00126 #define GZDONLY(fd)     assert(fdGetIo(fd) == gzdio)
    00127 #define BZDONLY(fd)     assert(fdGetIo(fd) == bzdio)
    00128 #define LZDONLY(fd)     assert(fdGetIo(fd) == lzdio)
    00129
    00130 #define UFDONLY(fd)     /* assert(fdGetIo(fd) == ufdio) */
    00131
    00132 #define fdGetFILE(_fd)  ((FILE *)fdGetFp(_fd))
    00133
    00136 /*@unchecked@*/
    00137 #if _USE_LIBIO
    00138 int noLibio = 0;
    00139 #else
    00140 int noLibio = 1;
    00141 #endif
    00142
    00143 #define TIMEOUT_SECS 60
    00144
    00147 /*@unchecked@*/
    00148 static int ftpTimeoutSecs = TIMEOUT_SECS;
    00149
    00152 /*@unchecked@*/
    00153 int _rpmio_debug = 0;
    00154
    00157 /*@unchecked@*/
    00158 int _av_debug = 0;
    00159
    00162 /*@unchecked@*/
    00163 int _ftp_debug = 0;
    00164
    00167 /*@unchecked@*/
    00168 int _dav_debug = 0;
    00169
    00170 /* =============================================================== */
    00171
    00172 static /*@observer@*/ const char * fdbg(/*@null@*/ FD_t fd)
    00173         /*@*/
    00174 {
    00175     static char buf[BUFSIZ];
    00176     char *be = buf;
    00177     int i;
    00178
    00179     buf[0] = '\0';
    00180     if (fd == NULL)
    00181         return buf;
    00182
    00183 #ifdef DYING
    00184     sprintf(be, "fd %p", fd);   be += strlen(be);
    00185     if (fd->rd_timeoutsecs >= 0) {
    00186         sprintf(be, " secs %d", fd->rd_timeoutsecs);
    00187         be += strlen(be);
    00188     }
    00189 #endif
    00190     if (fd->bytesRemain != -1) {
    00191         sprintf(be, " clen %d", (int)fd->bytesRemain);
    00192         be += strlen(be);
    00193     }
    00194     if (fd->wr_chunked) {
    00195         strcpy(be, " chunked");
    00196         be += strlen(be);
    00197     }
    00198     *be++ = '\t';
    00199     for (i = fd->nfps; i >= 0; i--) {
    00200         FDSTACK_t * fps = &fd->fps[i];
    00201         if (i != fd->nfps)
    00202             *be++ = ' ';
    00203         *be++ = '|';
    00204         *be++ = ' ';
    00205         if (fps->io == fdio) {
    00206             sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
    00207         } else if (fps->io == ufdio) {
    00208             sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
    00209 #ifdef  HAVE_ZLIB_H
    00210         } else if (fps->io == gzdio) {
    00211             sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
    00212 #endif
    00213 #if defined(HAVE_BZLIB_H)
    00214         } else if (fps->io == bzdio) {
    00215             sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
    00216 #endif
    00217         } else if (fps->io == lzdio) {
    00218             sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno);
    00219         } else if (fps->io == fpio) {
    00220             /*@+voidabstract@*/
    00221             sprintf(be, "%s %p(%d) fdno %d",
    00222                 (fps->fdno < 0 ? "LIBIO" : "FP"),
    00223                 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
    00224             /*@=voidabstract@*/
    00225         } else {
    00226             sprintf(be, "??? io %p fp %p fdno %d ???",
    00227                 fps->io, fps->fp, fps->fdno);
    00228         }
    00229         be += strlen(be);
    00230         *be = '\0';
    00231     }
    00232     return buf;
    00233 }
    00234
    00235 /* =============================================================== */
    00236 off_t fdSize(FD_t fd)
    00237 {
    00238     struct stat sb;
    00239     off_t rc = -1;
    00240
    00241 #ifdef  NOISY
    00242 DBGIO(0, (stderr, "==>\tfdSize(%p) rc %ld\n", fd, (long)rc));
    00243 #endif
    00244     FDSANE(fd);
    00245     if (fd->contentLength >= 0)
    00246         rc = fd->contentLength;
    00247     else switch (fd->urlType) {
    00248     case URL_IS_PATH:
    00249     case URL_IS_UNKNOWN:
    00250         if (fstat(Fileno(fd), &sb) == 0)
    00251             rc = sb.st_size;
    00252         /*@fallthrough@*/
    00253     case URL_IS_HTTPS:
    00254     case URL_IS_HTTP:
    00255     case URL_IS_HKP:
    00256     case URL_IS_FTP:
    00257     case URL_IS_DASH:
    00258         break;
    00259     }
    00260     return rc;
    00261 }
    00262
    00263 FD_t fdDup(int fdno)
    00264 {
    00265     FD_t fd;
    00266     int nfdno;
    00267
    00268     if ((nfdno = dup(fdno)) < 0)
    00269         return NULL;
    00270     if (fcntl(nfdno, F_SETFD, FD_CLOEXEC)) {
    00271         (void) close(nfdno);
    00272         return NULL;
    00273     }
    00274     fd = fdNew("open (fdDup)");
    00275     fdSetOpen(fd, "fdDup", nfdno, 0);   /* XXX bogus */
    00276     fdSetFdno(fd, nfdno);
    00277 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
    00278     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
    00279 }
    00280
    00281 static inline /*@unused@*/ int fdSeekNot(void * cookie,
    00282                 /*@unused@*/ _libio_pos_t pos,  /*@unused@*/ int whence)
    00283         /*@*/
    00284 {
    00285     FD_t fd = c2f(cookie);
    00286     FDSANE(fd);         /* XXX keep gcc quiet */
    00287     return -2;
    00288 }
    00289
    00290 #ifdef UNUSED
    00291 FILE *fdFdopen(void * cookie, const char *fmode)
    00292 {
    00293     FD_t fd = c2f(cookie);
    00294     int fdno;
    00295     FILE * fp;
    00296
    00297     if (fmode == NULL) return NULL;
    00298     fdno = fdFileno(fd);
    00299     if (fdno < 0) return NULL;
    00300     fp = fdopen(fdno, fmode);
    00301 DBGIO(fd, (stderr, "==> fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
    00302     fd = fdFree(fd, "open (fdFdopen)");
    00303     return fp;
    00304 }
    00305 #endif
    00306
    00307 /* =============================================================== */
    00308 /*@-mustmod@*/ /* FIX: cookie is modified */
    00309 static inline /*@null@*/ FD_t XfdLink(void * cookie, const char * msg,
    00310                 const char * file, unsigned line)
    00311         /*@modifies *cookie @*/
    00312 {
    00313     FD_t fd;
    00314 if (cookie == NULL)
    00315     /*@-castexpose@*/
    00316 DBGREFS(0, (stderr, "--> fd  %p ++ %d %s at %s:%u\n", cookie, FDNREFS(cookie)+1, msg, file, line));
    00317     /*@=castexpose@*/
    00318     fd = c2f(cookie);
    00319     if (fd) {
    00320         fd->nrefs++;
    00321 DBGREFS(fd, (stderr, "--> fd  %p ++ %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
    00322     }
    00323     return fd;
    00324 }
    00325 /*@=mustmod@*/
    00326
    00327 static inline /*@null@*/
    00328 FD_t XfdFree( /*@killref@*/ FD_t fd, const char *msg,
    00329                 const char *file, unsigned line)
    00330         /*@modifies fd @*/
    00331 {
    00332         int i;
    00333
    00334 if (fd == NULL)
    00335 DBGREFS(0, (stderr, "--> fd  %p -- %d %s at %s:%u\n", fd, FDNREFS(fd), msg, file, line));
    00336     FDSANE(fd);
    00337     if (fd) {
    00338 DBGREFS(fd, (stderr, "--> fd  %p -- %d %s at %s:%u %s\n", fd, fd->nrefs, msg, file, line, fdbg(fd)));
    00339         if (--fd->nrefs > 0)
    00340             /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
    00341         fd->opath = _free(fd->opath);
    00342         fd->stats = _free(fd->stats);
    00343         for (i = fd->ndigests - 1; i >= 0; i--) {
    00344             FDDIGEST_t fddig = fd->digests + i;
    00345             if (fddig->hashctx == NULL)
    00346                 continue;
    00347             (void) rpmDigestFinal(fddig->hashctx, NULL, NULL, 0);
    00348             fddig->hashctx = NULL;
    00349         }
    00350         fd->ndigests = 0;
    00351 /*@-onlytrans@*/
    00352 #ifdef WITH_XAR
    00353         fd->xar = rpmxarFree(fd->xar);
    00354 #endif
    00355         fd->dig = pgpDigFree(fd->dig);
    00356 /*@=onlytrans@*/
    00357         memset(fd, 0, sizeof(*fd));     /* XXX trash and burn */
    00358         /*@-refcounttrans@*/ free(fd); /*@=refcounttrans@*/
    00359     }
    00360     return NULL;
    00361 }
    00362
    00363 static inline /*@null@*/
    00364 FD_t XfdNew(const char * msg, const char * file, unsigned line)
    00365         /*@globals internalState @*/
    00366         /*@modifies internalState @*/
    00367 {
    00368     FD_t fd = xcalloc(1, sizeof(*fd));
    00369     if (fd == NULL) /* XXX xmalloc never returns NULL */
    00370         return NULL;
    00371     fd->nrefs = 0;
    00372     fd->flags = 0;
    00373     fd->magic = FDMAGIC;
    00374     fd->urlType = URL_IS_UNKNOWN;
    00375
    00376     fd->nfps = 0;
    00377     memset(fd->fps, 0, sizeof(fd->fps));
    00378
    00379     fd->fps[0].io = ufdio;
    00380     fd->fps[0].fp = NULL;
    00381     fd->fps[0].fdno = -1;
    00382
    00383     fd->opath = NULL;
    00384     fd->oflags = 0;
    00385     fd->omode = 0;
    00386     fd->url = NULL;
    00387     fd->rd_timeoutsecs = 1;     /* XXX default value used to be -1 */
    00388     fd->contentLength = fd->bytesRemain = -1;
    00389     fd->wr_chunked = 0;
    00390     fd->syserrno = 0;
    00391     fd->errcookie = NULL;
    00392     fd->stats = xcalloc(1, sizeof(*fd->stats));
    00393     fd->xar = NULL;
    00394     fd->dig = NULL;
    00395
    00396     fd->ndigests = 0;
    00397     memset(fd->digests, 0, sizeof(fd->digests));
    00398
    00399     fd->ftpFileDoneNeeded = 0;
    00400     fd->fd_cpioPos = 0;
    00401
    00402     return XfdLink(fd, msg, file, line);
    00403 }
    00404
    00405 static ssize_t fdRead(void * cookie, /*@out@*/ char * buf, size_t count)
    00406         /*@globals errno, fileSystem, internalState @*/
    00407         /*@modifies buf, errno, fileSystem, internalState @*/
    00408         /*@requires maxSet(buf) >= (count - 1) @*/
    00409         /*@ensures maxRead(buf) == result @*/
    00410 {
    00411     FD_t fd = c2f(cookie);
    00412     ssize_t rc;
    00413
    00414     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
    00415
    00416     fdstat_enter(fd, FDSTAT_READ);
    00417     /* HACK: flimsy wiring for davRead */
    00418     if (fd->req != NULL) {
    00419 #ifdef WITH_NEON
    00420         rc = davRead(fd, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
    00421 #else
    00422         rc = -1;
    00423 #endif
    00424         /* XXX Chunked davRead EOF. */
    00425         if (rc == 0)
    00426             fd->bytesRemain = 0;
    00427     } else
    00428     if (fd->xar != NULL) {
    00429 #ifdef WITH_XAR
    00430         rc = xarRead(fd, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
    00431 #else
    00432         rc = -1;
    00433 #endif
    00434     } else
    00435         rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
    00436     fdstat_exit(fd, FDSTAT_READ, rc);
    00437
    00438     if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
    00439
    00440 DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
    00441
    00442     return rc;
    00443 }
    00444
    00445 static ssize_t fdWrite(void * cookie, const char * buf, size_t count)
    00446         /*@globals errno, fileSystem, internalState @*/
    00447         /*@modifies errno, fileSystem, internalState @*/
    00448 {
    00449     FD_t fd = c2f(cookie);
    00450     int fdno = fdFileno(fd);
    00451     ssize_t rc;
    00452
    00453     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
    00454
    00455     if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
    00456
    00457     if (count == 0) return 0;
    00458
    00459     fdstat_enter(fd, FDSTAT_WRITE);
    00460     /* HACK: flimsy wiring for davWrite */
    00461     if (fd->req != NULL)
    00462 #ifdef WITH_NEON
    00463         rc = davWrite(fd, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
    00464 #else
    00465         rc = -1;
    00466 #endif
    00467     else
    00468         rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
    00469     fdstat_exit(fd, FDSTAT_WRITE, rc);
    00470
    00471 DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
    00472
    00473     return rc;
    00474 }
    00475
    00476 static inline int fdSeek(void * cookie, _libio_pos_t pos, int whence)
    00477         /*@globals fileSystem, internalState @*/
    00478         /*@modifies fileSystem, internalState @*/
    00479 {
    00480 #ifdef USE_COOKIE_SEEK_POINTER
    00481     _IO_off64_t p = *pos;
    00482 #else
    00483     off_t p = pos;
    00484 #endif
    00485     FD_t fd = c2f(cookie);
    00486     off_t rc;
    00487
    00488     assert(fd->bytesRemain == -1);      /* XXX FIXME fadio only for now */
    00489     fdstat_enter(fd, FDSTAT_SEEK);
    00490     rc = lseek(fdFileno(fd), p, whence);
    00491     fdstat_exit(fd, FDSTAT_SEEK, rc);
    00492
    00493 DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
    00494
    00495     return (int) rc;
    00496 }
    00497
    00498 static int fdClose( /*@only@*/ void * cookie)
    00499         /*@globals errno, fileSystem, systemState, internalState @*/
    00500         /*@modifies errno, fileSystem, systemState, internalState @*/
    00501 {
    00502     FD_t fd;
    00503     int fdno;
    00504     int rc;
    00505
    00506     if (cookie == NULL) return -2;
    00507     fd = c2f(cookie);
    00508     fdno = fdFileno(fd);
    00509
    00510     fdSetFdno(fd, -1);
    00511
    00512     fdstat_enter(fd, FDSTAT_CLOSE);
    00513     /* HACK: flimsy wiring for davClose */
    00514     if (fd->req != NULL)
    00515 #ifdef WITH_NEON
    00516         rc = davClose(fd);
    00517 #else
    00518         rc = -1;
    00519 #endif
    00520     else
    00521         rc = ((fdno >= 0) ? close(fdno) : -2);
    00522     fdstat_exit(fd, FDSTAT_CLOSE, rc);
    00523
    00524 DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
    00525
    00526     fd = fdFree(fd, "open (fdClose)");
    00527     return rc;
    00528 }
    00529
    00530 static /*@null@*/ FD_t fdOpen(const char *path, int flags, mode_t mode)
    00531         /*@globals errno, fileSystem, internalState @*/
    00532         /*@modifies errno, fileSystem, internalState @*/
    00533 {
    00534     FD_t fd;
    00535     int fdno;
    00536
    00537     fdno = open(path, flags, mode);
    00538     if (fdno < 0) return NULL;
    00539     if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
    00540         (void) close(fdno);
    00541         return NULL;
    00542     }
    00543     fd = fdNew("open (fdOpen)");
    00544     fdSetOpen(fd, path, flags, mode);
    00545     fdSetFdno(fd, fdno);
    00546     fd->flags = flags;
    00547 DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
    00548     /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
    00549 }
    00550
    00551 /*@-type@*/ /* LCL: function typedefs */
    00552 static struct FDIO_s fdio_s = {
    00553   fdRead, fdWrite, fdSeek, fdClose, XfdLink, XfdFree, XfdNew, fdFileno,
    00554   fdOpen, NULL, fdGetFp, NULL,  mkdir, chdir, rmdir, rename, unlink
    00555 };
    00556 /*@=type@*/
    00557 FDIO_t fdio = /*@-compmempass@*/ &fdio_s /*@=compmempass@*/ ;
    00558
    00559 int fdWritable(FD_t fd, int secs)
    00560 {
    00561     int fdno;
    00562     int rc;
    00563 #if defined(HAVE_POLL_H)
    00564     int msecs = (secs >= 0 ? (1000 * secs) : -1);
    00565     struct pollfd wrfds;
    00566 #else
    00567     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
    00568     fd_set wrfds;
    00569     FD_ZERO(&wrfds);
    00570 #endif
    00571
    00572     /* HACK: flimsy wiring for davWrite */
    00573     if (fd->req != NULL)
    00574         return 1;
    00575
    00576     if ((fdno = fdFileno(fd)) < 0)
    00577         return -1;      /* XXX W2DO? */
    00578
    00579     do {
    00580 #if defined(HAVE_POLL_H)
    00581         wrfds.fd = fdno;
    00582         wrfds.events = POLLOUT;
    00583         wrfds.revents = 0;
    00584         rc = poll(&wrfds, 1, msecs);
    00585 #else
    00586         if (tvp) {
    00587             tvp->tv_sec = secs;
    00588             tvp->tv_usec = 0;
    00589         }
    00590         FD_SET(fdno, &wrfds);
    00591 /*@-compdef -nullpass@*/
    00592         rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
    00593 /*@=compdef =nullpass@*/
    00594 #endif
    00595
    00596         /* HACK: EBADF on PUT chunked termination from ufdClose. */
    00597 if (_rpmio_debug && !(rc == 1 && errno == 0))
    00598 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
    00599         if (rc < 0) {
    00600             switch (errno) {
    00601             case EINTR:
    00602                 continue;
    00603                 /*@notreached@*/ /*@switchbreak@*/ break;
    00604             default:
    00605                 return rc;
    00606                 /*@notreached@*/ /*@switchbreak@*/ break;
    00607             }
    00608         }
    00609         return rc;
    00610     } while (1);
    00611     /*@notreached@*/
    00612 }
    00613
    00614 int fdReadable(FD_t fd, int secs)
    00615 {
    00616     int fdno;
    00617     int rc;
    00618 #if defined(HAVE_POLL_H)
    00619     int msecs = (secs >= 0 ? (1000 * secs) : -1);
    00620     struct pollfd rdfds;
    00621 #else
    00622     struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
    00623     fd_set rdfds;
    00624     FD_ZERO(&rdfds);
    00625 #endif
    00626
    00627     /* HACK: flimsy wiring for davRead */
    00628     if (fd->req != NULL)
    00629         return 1;
    00630
    00631     if ((fdno = fdFileno(fd)) < 0)
    00632         return -1;      /* XXX W2DO? */
    00633
    00634     do {
    00635 #if defined(HAVE_POLL_H)
    00636         rdfds.fd = fdno;
    00637         rdfds.events = POLLIN;
    00638         rdfds.revents = 0;
    00639         rc = poll(&rdfds, 1, msecs);
    00640 #else
    00641         if (tvp) {
    00642             tvp->tv_sec = secs;
    00643             tvp->tv_usec = 0;
    00644         }
    00645         FD_SET(fdno, &rdfds);
    00646         /*@-compdef -nullpass@*/
    00647         rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
    00648         /*@=compdef =nullpass@*/
    00649 #endif
    00650
    00651         if (rc < 0) {
    00652             switch (errno) {
    00653             case EINTR:
    00654                 continue;
    00655                 /*@notreached@*/ /*@switchbreak@*/ break;
    00656             default:
    00657                 return rc;
    00658                 /*@notreached@*/ /*@switchbreak@*/ break;
    00659             }
    00660         }
    00661         return rc;
    00662     } while (1);
    00663     /*@notreached@*/
    00664 }
    00665
    00666 int fdFgets(FD_t fd, char * buf, size_t len)
    00667 {
    00668     int fdno;
    00669     int secs = fd->rd_timeoutsecs;
    00670     size_t nb = 0;
    00671     int ec = 0;
    00672     char lastchar = '\0';
    00673
    00674     if ((fdno = fdFileno(fd)) < 0)
    00675         return 0;       /* XXX W2DO? */
    00676
    00677     do {
    00678         int rc;
    00679
    00680         /* Is there data to read? */
    00681         rc = fdReadable(fd, secs);
    00682
    00683         switch (rc) {
    00684         case -1:        /* error */
    00685             ec = -1;
    00686             continue;
    00687             /*@notreached@*/ /*@switchbreak@*/ break;
    00688         case  0:        /* timeout */
    00689             ec = -1;
    00690             continue;
    00691             /*@notreached@*/ /*@switchbreak@*/ break;
    00692         default:        /* data to read */
    00693             /*@switchbreak@*/ break;
    00694         }
    00695
    00696         errno = 0;
    00697 #ifdef  NOISY
    00698         rc = fdRead(fd, buf + nb, 1);
    00699 #else
    00700         rc = (int)read(fdFileno(fd), buf + nb, 1);
    00701 #endif
    00702         if (rc < 0) {
    00703             fd->syserrno = errno;
    00704             switch (errno) {
    00705             case EWOULDBLOCK:
    00706                 continue;
    00707                 /*@notreached@*/ /*@switchbreak@*/ break;
    00708             default:
    00709                 /*@switchbreak@*/ break;
    00710             }
    00711 if (_rpmio_debug)
    00712 fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
    00713             ec = -1;
    00714             break;
    00715         } else if (rc == 0) {
    00716 if (_rpmio_debug)
    00717 fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
    00718             break;
    00719         } else {
    00720             nb += rc;
    00721             buf[nb] = '\0';
    00722             lastchar = buf[nb - 1];
    00723         }
    00724     } while (ec == 0 && nb < len && lastchar != '\n');
    00725
    00726     return (ec >= 0 ? (int)nb : ec);
    00727 }
    00728
    00729 /* =============================================================== */
    00730 /* Support for FTP/HTTP I/O.
    00731  */
    00732 const char * ftpStrerror(int errorNumber)
    00733 {
    00734     switch (errorNumber) {
    00735     case 0:
    00736         return _("Success");
    00737
    00738     /* HACK error impediance match, coalesce and rename. */
    00739     case FTPERR_NE_ERROR:
    00740         return ("NE_ERROR: Generic error.");
    00741     case FTPERR_NE_LOOKUP:
    00742         return ("NE_LOOKUP: Hostname lookup failed.");
    00743     case FTPERR_NE_AUTH:
    00744         return ("NE_AUTH: Server authentication failed.");
    00745     case FTPERR_NE_PROXYAUTH:
    00746         return ("NE_PROXYAUTH: Proxy authentication failed.");
    00747     case FTPERR_NE_CONNECT:
    00748         return ("NE_CONNECT: Could not connect to server.");
    00749     case FTPERR_NE_TIMEOUT:
    00750         return ("NE_TIMEOUT: Connection timed out.");
    00751     case FTPERR_NE_FAILED:
    00752         return ("NE_FAILED: The precondition failed.");
    00753     case FTPERR_NE_RETRY:
    00754         return ("NE_RETRY: Retry request.");
    00755     case FTPERR_NE_REDIRECT:
    00756         return ("NE_REDIRECT: Redirect received.");
    00757
    00758     case FTPERR_BAD_SERVER_RESPONSE:
    00759         return _("Bad server response");
    00760     case FTPERR_SERVER_IO_ERROR:
    00761         return _("Server I/O error");
    00762     case FTPERR_SERVER_TIMEOUT:
    00763         return _("Server timeout");
    00764     case FTPERR_BAD_HOST_ADDR:
    00765         return _("Unable to lookup server host address");
    00766     case FTPERR_BAD_HOSTNAME:
    00767         return _("Unable to lookup server host name");
    00768     case FTPERR_FAILED_CONNECT:
    00769         return _("Failed to connect to server");
    00770     case FTPERR_FAILED_DATA_CONNECT:
    00771         return _("Failed to establish data connection to server");
    00772     case FTPERR_FILE_IO_ERROR:
    00773         return _("I/O error to local file");
    00774     case FTPERR_PASSIVE_ERROR:
    00775         return _("Error setting remote server to passive mode");
    00776     case FTPERR_FILE_NOT_FOUND:
    00777         return _("File not found on server");
    00778     case FTPERR_NIC_ABORT_IN_PROGRESS:
    00779         return _("Abort in progress");
    00780
    00781     case FTPERR_UNKNOWN:
    00782     default:
    00783         return _("Unknown or unexpected error");
    00784     }
    00785 }
    00786
    00787 const char *urlStrerror(const char *url)
    00788 {
    00789     const char *retstr;
    00790     switch (urlIsURL(url)) {
    00791     case URL_IS_HTTPS:
    00792     case URL_IS_HTTP:
    00793     case URL_IS_HKP:
    00794     case URL_IS_FTP:
    00795     {   urlinfo u;
    00796 /* XXX This only works for httpReq/ftpLogin/ftpReq failures */
    00797         if (urlSplit(url, &u) == 0)
    00798             retstr = ftpStrerror(u->openError);
    00799         else
    00800             retstr = _("Malformed URL");
    00801     }   break;
    00802     default:
    00803         retstr = strerror(errno);
    00804         break;
    00805     }
    00806     return retstr;
    00807 }
    00808
    00809 #if !defined(HAVE_GETADDRINFO)
    00810 #if !defined(USE_ALT_DNS) || !USE_ALT_DNS
    00811 static int mygethostbyname(const char * host,
    00812                 /*@out@*/ struct in_addr * address)
    00813         /*@globals h_errno @*/
    00814         /*@modifies *address @*/
    00815 {
    00816     struct hostent * hostinfo;
    00817
    00818     /*@-multithreaded @*/
    00819     hostinfo = gethostbyname(host);
    00820     /*@=multithreaded @*/
    00821     if (!hostinfo) return 1;
    00822
    00823     memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
    00824     return 0;
    00825 }
    00826 #endif
    00827
    00828 /*@-compdef@*/  /* FIX: address->s_addr undefined. */
    00829 static int getHostAddress(const char * host, /*@out@*/ struct in_addr * address)
    00830         /*@globals errno, h_errno @*/
    00831         /*@modifies *address, errno @*/
    00832 {
    00833 #if 0   /* XXX workaround nss_foo module hand-off using valgrind. */
    00834     if (!strcmp(host, "localhost")) {
    00835         /*@-moduncon @*/
    00836         if (!inet_aton("127.0.0.1", address))
    00837             return FTPERR_BAD_HOST_ADDR;
    00838         /*@=moduncon @*/
    00839     } else
    00840 #endif
    00841     if (xisdigit(host[0])) {
    00842         /*@-moduncon @*/
    00843         if (!inet_aton(host, address))
    00844             return FTPERR_BAD_HOST_ADDR;
    00845         /*@=moduncon @*/
    00846     } else {
    00847         if (mygethostbyname(host, address)) {
    00848             errno = h_errno;
    00849             return FTPERR_BAD_HOSTNAME;
    00850         }
    00851     }
    00852
    00853     return 0;
    00854 }
    00855 /*@=compdef@*/
    00856 #endif  /* HAVE_GETADDRINFO */
    00857
    00858 static int tcpConnect(FD_t ctrl, const char * host, int port)
    00859         /*@globals fileSystem, internalState @*/
    00860         /*@modifies ctrl, fileSystem, internalState @*/
    00861 {
    00862     int fdno = -1;
    00863     int rc;
    00864 #ifdef  HAVE_GETADDRINFO
    00865 /*@-unrecog@*/
    00866     struct addrinfo hints, *res, *res0;
    00867     char pbuf[NI_MAXSERV];
    00868     int xx;
    00869
    00870     memset(&hints, 0, sizeof(hints));
    00871     hints.ai_family = AF_UNSPEC;
    00872     hints.ai_socktype = SOCK_STREAM;
    00873     sprintf(pbuf, "%d", port);
    00874     pbuf[sizeof(pbuf)-1] = '\0';
    00875     rc = FTPERR_FAILED_CONNECT;
    00876     if (getaddrinfo(host, pbuf, &hints, &res0) == 0) {
    00877         for (res = res0; res != NULL; res = res->ai_next) {
    00878             if ((fdno = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
    00879                 continue;
    00880             if (connect(fdno, res->ai_addr, (int)res->ai_addrlen) < 0) {
    00881                 xx = close(fdno);
    00882                 continue;
    00883             }
    00884             /* success */
    00885             rc = 0;
    00886             if (_ftp_debug) {
    00887                 char hbuf[NI_MAXHOST];
    00888                 hbuf[0] = '\0';
    00889                 xx = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
    00890                                 NULL, 0, NI_NUMERICHOST);
    00891                 fprintf(stderr,"++ connect [%s]:%d on fdno %d\n",
    00892                                 /*@-unrecog@*/ hbuf /*@=unrecog@*/, port, fdno);
    00893             }
    00894             break;
    00895         }
    00896         freeaddrinfo(res0);
    00897     }
    00898     if (rc < 0)
    00899         goto errxit;
    00900 /*@=unrecog@*/
    00901 #else   /* HAVE_GETADDRINFO */
    00902     struct sockaddr_in sin;
    00903
    00904     memset(&sin, 0, sizeof(sin));
    00905     sin.sin_family = AF_INET;
    00906     sin.sin_port = htons(port);
    00907     sin.sin_addr.s_addr = INADDR_ANY;
    00908
    00909   do {
    00910     if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
    00911         break;
    00912
    00913     if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
    00914         rc = FTPERR_FAILED_CONNECT;
    00915         break;
    00916     }
    00917
    00918     /*@-internalglobs@*/
    00919     if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
    00920         rc = FTPERR_FAILED_CONNECT;
    00921         break;
    00922     }
    00923     /*@=internalglobs@*/
    00924   } while (0);
    00925
    00926     if (rc < 0)
    00927         goto errxit;
    00928
    00929 if (_ftp_debug)
    00930 fprintf(stderr,"++ connect %s:%d on fdno %d\n",
    00931 /*@-unrecog -moduncon -evalorderuncon @*/
    00932 inet_ntoa(sin.sin_addr)
    00933 /*@=unrecog =moduncon =evalorderuncon @*/ ,
    00934 (int)ntohs(sin.sin_port), fdno);
    00935 #endif  /* HAVE_GETADDRINFO */
    00936
    00937     fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
    00938     return 0;
    00939
    00940 errxit:
    00941     /*@-observertrans@*/
    00942     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
    00943     /*@=observertrans@*/
    00944     if (fdno >= 0)
    00945         (void) close(fdno);
    00946     return rc;
    00947 }
    00948
    00949 static int checkResponse(void * uu, FD_t ctrl,
    00950                 /*@out@*/ int *ecp, /*@out@*/ char ** str)
    00951         /*@globals fileSystem @*/
    00952         /*@modifies ctrl, *ecp, *str, fileSystem @*/
    00953 {
    00954     urlinfo u = uu;
    00955     char *buf;
    00956     size_t bufAlloced;
    00957     int bufLength = 0;
    00958     const char *s;
    00959     char *se;
    00960     int ec = 0;
    00961     int moretodo = 1;
    00962     char errorCode[4];
    00963
    00964     URLSANE(u);
    00965     if (u->bufAlloced == 0 || u->buf == NULL) {
    00966         u->bufAlloced = _url_iobuf_size;
    00967         u->buf = xcalloc(u->bufAlloced, sizeof(u->buf[0]));
    00968     }
    00969     buf = u->buf;
    00970     bufAlloced = u->bufAlloced;
    00971     *buf = '\0';
    00972
    00973     errorCode[0] = '\0';
    00974
    00975     do {
    00976         int rc;
    00977
    00978         /*
    00979          * Read next line from server.
    00980          */
    00981         se = buf + bufLength;
    00982         *se = '\0';
    00983         rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
    00984         if (rc < 0) {
    00985             ec = FTPERR_BAD_SERVER_RESPONSE;
    00986             continue;
    00987         } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
    00988             moretodo = 0;
    00989
    00990         /*
    00991          * Process next line from server.
    00992          */
    00993         for (s = se; *s != '\0'; s = se) {
    00994                 const char *e;
    00995
    00996                 while (*se && *se != '\n') se++;
    00997
    00998                 if (se > s && se[-1] == '\r')
    00999                    se[-1] = '\0';
    01000                 if (*se == '\0')
    01001                     /*@innerbreak@*/ break;
    01002
    01003 if (_ftp_debug)
    01004 fprintf(stderr, "<- %s\n", s);
    01005
    01006                 /* HTTP: header termination on empty line */
    01007                 if (*s == '\0') {
    01008                     moretodo = 0;
    01009                     /*@innerbreak@*/ break;
    01010                 }
    01011                 *se++ = '\0';
    01012
    01013                 /* HTTP: look for "HTTP/1.1 123 ..." */
    01014                 if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
    01015                     ctrl->contentLength = -1;
    01016                     if ((e = strchr(s, '.')) != NULL) {
    01017                         e++;
    01018                         u->httpVersion = (int)(*e - '0');
    01019                         if (u->httpVersion < 1 || u->httpVersion > 2)
    01020                             ctrl->persist = u->httpVersion = 0;
    01021                         else
    01022                             ctrl->persist = 1;
    01023                     }
    01024                     if ((e = strchr(s, ' ')) != NULL) {
    01025                         e++;
    01026                         if (strchr("0123456789", *e))
    01027                             strncpy(errorCode, e, 3);
    01028                         errorCode[3] = '\0';
    01029                     }
    01030                     /*@innercontinue@*/ continue;
    01031                 }
    01032
    01033                 /* HTTP: look for "token: ..." */
    01034                 for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
    01035                     {};
    01036                 if (e > s && *e++ == ':') {
    01037                     size_t ne = (e - s);
    01038                     while (*e && *e == ' ') e++;
    01039 #if 0
    01040                     if (!strncmp(s, "Date:", ne)) {
    01041                     } else
    01042                     if (!strncmp(s, "Server:", ne)) {
    01043                     } else
    01044                     if (!strncmp(s, "Last-Modified:", ne)) {
    01045                     } else
    01046                     if (!strncmp(s, "ETag:", ne)) {
    01047                     } else
    01048 #endif
    01049                     if (!strncmp(s, "Accept-Ranges:", ne)) {
    01050                         if (!strcmp(e, "bytes"))
    01051                             u->allow |= RPMURL_SERVER_HASRANGE;
    01052                         if (!strcmp(e, "none"))
    01053                             u->allow &= ~RPMURL_SERVER_HASRANGE;
    01054                     } else
    01055                     if (!strncmp(s, "Content-Length:", ne)) {
    01056                         if (strchr("0123456789", *e))
    01057                             ctrl->contentLength = atol(e);
    01058                     } else
    01059                     if (!strncmp(s, "Connection:", ne)) {
    01060                         if (!strcmp(e, "close"))
    01061                             ctrl->persist = 0;
    01062                     }
    01063 #if 0
    01064                     else
    01065                     if (!strncmp(s, "Content-Type:", ne)) {
    01066                     } else
    01067                     if (!strncmp(s, "Transfer-Encoding:", ne)) {
    01068                         if (!strcmp(e, "chunked"))
    01069                             ctrl->wr_chunked = 1;
    01070                         else
    01071                             ctrl->wr_chunked = 0;
    01072                     } else
    01073                     if (!strncmp(s, "Allow:", ne)) {
    01074                     }
    01075 #endif
    01076                     /*@innercontinue@*/ continue;
    01077                 }
    01078
    01079                 /* HTTP: look for "<TITLE>501 ... </TITLE>" */
    01080                 if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
    01081                     s += sizeof("<TITLE>") - 1;
    01082
    01083                 /* FTP: look for "123-" and/or "123 " */
    01084                 if (strchr("0123456789", *s)) {
    01085                     if (errorCode[0] != '\0') {
    01086                         if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
    01087                             moretodo = 0;
    01088                     } else {
    01089                         strncpy(errorCode, s, sizeof("123")-1);
    01090                         errorCode[3] = '\0';
    01091                         if (s[3] != '-')
    01092                             moretodo = 0;
    01093                     }
    01094                 }
    01095         }
    01096
    01097         if (moretodo && se > s) {
    01098             bufLength = se - s - 1;
    01099             if (s != buf)
    01100                 memmove(buf, s, bufLength);
    01101         } else {
    01102             bufLength = 0;
    01103         }
    01104     } while (moretodo && ec == 0);
    01105
    01106     if (str)    *str = buf;
    01107     if (ecp)    *ecp = atoi(errorCode);
    01108
    01109     return ec;
    01110 }
    01111
    01112 static int ftpCheckResponse(urlinfo u, /*@out@*/ char ** str)
    01113         /*@globals fileSystem @*/
    01114         /*@modifies u, *str, fileSystem @*/
    01115 {
    01116     int ec = 0;
    01117     int rc;
    01118
    01119     URLSANE(u);
    01120     rc = checkResponse(u, u->ctrl, &ec, str);
    01121
    01122     switch (ec) {
    01123     case 550:
    01124         return FTPERR_FILE_NOT_FOUND;
    01125         /*@notreached@*/ break;
    01126     case 552:
    01127         return FTPERR_NIC_ABORT_IN_PROGRESS;
    01128         /*@notreached@*/ break;
    01129     default:
    01130         if (ec >= 400 && ec <= 599) {
    01131             return FTPERR_BAD_SERVER_RESPONSE;
    01132         }
    01133         break;
    01134     }
    01135     return rc;
    01136 }
    01137
    01138 static int ftpCommand(urlinfo u, char ** str, ...)
    01139         /*@globals fileSystem, internalState @*/
    01140         /*@modifies u, *str, fileSystem, internalState @*/
    01141 {
    01142     va_list ap;
    01143     int len = 0;
    01144     const char * s, * t;
    01145     char * te;
    01146     int rc;
    01147
    01148     URLSANE(u);
    01149     va_start(ap, str);
    01150     while ((s = va_arg(ap, const char *)) != NULL) {
    01151         if (len) len++;
    01152         len += strlen(s);
    01153     }
    01154     len += sizeof("\r\n")-1;
    01155     va_end(ap);
    01156
    01157     t = te = alloca(len + 1);
    01158
    01159     va_start(ap, str);
    01160     while ((s = va_arg(ap, const char *)) != NULL) {
    01161         if (te > t) *te++ = ' ';
    01162         te = stpcpy(te, s);
    01163     }
    01164     te = stpcpy(te, "\r\n");
    01165     va_end(ap);
    01166
    01167 if (_ftp_debug)
    01168 fprintf(stderr, "-> %s", t);
    01169     if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
    01170         return FTPERR_SERVER_IO_ERROR;
    01171
    01172     rc = ftpCheckResponse(u, str);
    01173     return rc;
    01174 }
    01175
    01176 static int ftpLogin(urlinfo u)
    01177         /*@globals fileSystem, internalState @*/
    01178         /*@modifies u, fileSystem, internalState @*/
    01179 {
    01180     const char * host;
    01181     const char * user;
    01182     const char * password;
    01183     int port;
    01184     int rc;
    01185
    01186     URLSANE(u);
    01187     u->ctrl = fdLink(u->ctrl, "open ctrl");
    01188
    01189     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
    01190         rc = FTPERR_BAD_HOSTNAME;
    01191         goto errxit;
    01192     }
    01193
    01194     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
    01195
    01196     if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
    01197         user = "anonymous";
    01198
    01199     if ((password = u->password) == NULL) {
    01200         uid_t uid = getuid();
    01201         struct passwd * pw;
    01202         if (uid && (pw = getpwuid(uid)) != NULL) {
    01203             char *myp = alloca(strlen(pw->pw_name) + sizeof("@"));
    01204             strcpy(myp, pw->pw_name);
    01205             strcat(myp, "@");
    01206             password = myp;
    01207         } else {
    01208             password = "root@";
    01209         }
    01210     }
    01211
    01212     if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
    01213         /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
    01214
    01215 /*@-usereleased@*/
    01216     if (fdFileno(u->ctrl) < 0) {
    01217         rc = tcpConnect(u->ctrl, host, port);
    01218         if (rc < 0)
    01219             goto errxit2;
    01220     }
    01221
    01222     if ((rc = ftpCheckResponse(u, NULL)))
    01223         goto errxit;
    01224
    01225     if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
    01226         goto errxit;
    01227
    01228     if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
    01229         goto errxit;
    01230
    01231     if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
    01232         goto errxit;
    01233
    01234     /*@-compdef@*/
    01235     return 0;
    01236     /*@=compdef@*/
    01237
    01238 errxit:
    01239     /*@-observertrans@*/
    01240     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
    01241     /*@=observertrans@*/
    01242 errxit2:
    01243     if (fdFileno(u->ctrl) >= 0)
    01244         /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
    01245     /*@-compdef@*/
    01246     return rc;
    01247     /*@=compdef@*/
    01248 /*@=usereleased@*/
    01249 }
    01250
    01251 int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
    01252 {
    01253     urlinfo u = data->url;
    01254 #if !defined(HAVE_GETADDRINFO)
    01255     struct sockaddr_in dataAddress;
    01256 #endif  /* HAVE_GETADDRINFO */
    01257     char remoteIP[NI_MAXHOST];
    01258     char * cmd;
    01259     size_t cmdlen;
    01260     char * passReply;
    01261     char * chptr;
    01262     int rc;
    01263     int epsv;
    01264     int port;
    01265
    01266     remoteIP[0] = '\0';
    01267     URLSANE(u);
    01268     if (ftpCmd == NULL)
    01269         return FTPERR_UNKNOWN;  /* XXX W2DO? */
    01270
    01271     cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
    01272     chptr = cmd = alloca(cmdlen);
    01273     chptr = stpcpy(chptr, ftpCmd);
    01274     if (ftpArg) {
    01275         *chptr++ = ' ';
    01276         chptr = stpcpy(chptr, ftpArg);
    01277     }
    01278     chptr = stpcpy(chptr, "\r\n");
    01279     cmdlen = chptr - cmd;
    01280
    01281 /*
    01282  * Get the ftp version of the Content-Length.
    01283  */
    01284     if (!strncmp(cmd, "RETR", 4)) {
    01285         unsigned cl;
    01286
    01287         passReply = NULL;
    01288         rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
    01289         if (rc)
    01290             goto errxit;
    01291         if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
    01292             rc = FTPERR_BAD_SERVER_RESPONSE;
    01293             goto errxit;
    01294         }
    01295         rc = 0;
    01296         data->contentLength = cl;
    01297     }
    01298
    01299     epsv = 0;
    01300     passReply = NULL;
    01301 #ifdef HAVE_GETNAMEINFO
    01302     rc = ftpCommand(u, &passReply, "EPSV", NULL);
    01303     if (rc == 0) {
    01304 #ifdef HAVE_GETADDRINFO
    01305         struct sockaddr_storage ss;
    01306 #else /* HAVE_GETADDRINFO */
    01307         struct sockaddr_in ss;
    01308 #endif /* HAVE_GETADDRINFO */
    01309         socklen_t sslen = sizeof(ss);
    01310
    01311         /* we need to know IP of remote host */
    01312         if ((getpeername(fdFileno(c2f(u->ctrl)), (struct sockaddr *)&ss, &sslen) == 0)
    01313          && (getnameinfo((struct sockaddr *)&ss, sslen,
    01314                         remoteIP, sizeof(remoteIP),
    01315                         NULL, 0, NI_NUMERICHOST) == 0))
    01316         {
    01317                 epsv++;
    01318         } else {
    01319                 /* abort EPSV and fall back to PASV */
    01320                 rc = ftpCommand(u, &passReply, "ABOR", NULL);
    01321                 if (rc) {
    01322                     rc = FTPERR_PASSIVE_ERROR;
    01323                     goto errxit;
    01324                 }
    01325         }
    01326     }
    01327    if (epsv == 0)
    01328 #endif /* HAVE_GETNAMEINFO */
    01329         rc = ftpCommand(u, &passReply, "PASV", NULL);
    01330     if (rc) {
    01331         rc = FTPERR_PASSIVE_ERROR;
    01332         goto errxit;
    01333     }
    01334
    01335     chptr = passReply;
    01336     while (*chptr && *chptr != '(') chptr++;
    01337     if (*chptr != '(') return FTPERR_PASSIVE_ERROR;
    01338     chptr++;
    01339     passReply = chptr;
    01340     while (*chptr && *chptr != ')') chptr++;
    01341     if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
    01342     *chptr-- = '\0';
    01343
    01344   if (epsv) {
    01345         int i;
    01346         if(sscanf(passReply,"%*c%*c%*c%d%*c",&i) != 1) {
    01347            rc = FTPERR_PASSIVE_ERROR;
    01348            goto errxit;
    01349         }
    01350         port = i;
    01351   } else {
    01352
    01353     while (*chptr && *chptr != ',') chptr--;
    01354     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
    01355     chptr--;
    01356     while (*chptr && *chptr != ',') chptr--;
    01357     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
    01358     *chptr++ = '\0';
    01359
    01360     /* now passReply points to the IP portion, and chptr points to the
    01361        port number portion */
    01362
    01363     {   int i, j;
    01364         if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
    01365             rc = FTPERR_PASSIVE_ERROR;
    01366             goto errxit;
    01367         }
    01368         port = (((unsigned)i) << 8) + j;
    01369     }
    01370
    01371     chptr = passReply;
    01372     while (*chptr++ != '\0') {
    01373         if (*chptr == ',') *chptr = '.';
    01374     }
    01375     sprintf(remoteIP, "%s", passReply);
    01376   } /* if (epsv) */
    01377
    01378 #ifdef HAVE_GETADDRINFO
    01379 /*@-unrecog@*/
    01380     {
    01381         struct addrinfo hints, *res, *res0;
    01382         char pbuf[NI_MAXSERV];
    01383         int xx;
    01384
    01385         memset(&hints, 0, sizeof(hints));
    01386         hints.ai_family = AF_UNSPEC;
    01387         hints.ai_socktype = SOCK_STREAM;
    01388         hints.ai_flags = AI_NUMERICHOST;
    01389 #if defined(AI_IDN)
    01390         hints.ai_flags |= AI_IDN;
    01391 #endif
    01392         sprintf(pbuf, "%d", port);
    01393         pbuf[sizeof(pbuf)-1] = '\0';
    01394         if (getaddrinfo(remoteIP, pbuf, &hints, &res0)) {
    01395             rc = FTPERR_PASSIVE_ERROR;
    01396             goto errxit;
    01397         }
    01398
    01399         for (res = res0; res != NULL; res = res->ai_next) {
    01400             rc = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    01401             fdSetFdno(data, (rc >= 0 ? rc : -1));
    01402             if (rc < 0) {
    01403                 if (res->ai_next)
    01404                     continue;
    01405                 else {
    01406                     rc = FTPERR_FAILED_CONNECT;
    01407                     freeaddrinfo(res0);
    01408                     goto errxit;
    01409                 }
    01410             }
    01411             data = fdLink(data, "open data (ftpReq)");
    01412
    01413             /* XXX setsockopt SO_LINGER */
    01414             /* XXX setsockopt SO_KEEPALIVE */
    01415             /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
    01416
    01417             {
    01418                 int criterr = 0;
    01419                 while (connect(fdFileno(data), res->ai_addr, (int)res->ai_addrlen) < 0) {
    01420                     if (errno == EINTR)
    01421                         /*@innercontinue@*/ continue;
    01422                     criterr++;
    01423                 }
    01424                 if (criterr) {
    01425                     if (res->ai_addr) {
    01426 /*@-refcounttrans@*/
    01427                         xx = fdClose(data);
    01428 /*@=refcounttrans@*/
    01429                         continue;
    01430                     } else {
    01431                         rc = FTPERR_PASSIVE_ERROR;
    01432                         freeaddrinfo(res0);
    01433                         goto errxit;
    01434                     }
    01435                 }
    01436             }
    01437             /* success */
    01438             rc = 0;
    01439             break;
    01440         }
    01441         freeaddrinfo(res0);
    01442     }
    01443 /*@=unrecog@*/
    01444 #else /* HAVE_GETADDRINFO */
    01445     memset(&dataAddress, 0, sizeof(dataAddress));
    01446     dataAddress.sin_family = AF_INET;
    01447     dataAddress.sin_port = htons(port);
    01448
    01449     /*@-moduncon@*/
    01450     if (!inet_aton(remoteIP, &dataAddress.sin_addr)) {
    01451         rc = FTPERR_PASSIVE_ERROR;
    01452         goto errxit;
    01453     }
    01454     /*@=moduncon@*/
    01455
    01456     rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
    01457     fdSetFdno(data, (rc >= 0 ? rc : -1));
    01458     if (rc < 0) {
    01459         rc = FTPERR_FAILED_CONNECT;
    01460         goto errxit;
    01461     }
    01462     data = fdLink(data, "open data (ftpReq)");
    01463
    01464     /* XXX setsockopt SO_LINGER */
    01465     /* XXX setsockopt SO_KEEPALIVE */
    01466     /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
    01467
    01468     /*@-internalglobs@*/
    01469     while (connect(fdFileno(data), (struct sockaddr *) &dataAddress,
    01470                 sizeof(dataAddress)) < 0)
    01471     {
    01472         if (errno == EINTR)
    01473             continue;
    01474         rc = FTPERR_FAILED_DATA_CONNECT;
    01475         goto errxit;
    01476     }
    01477     /*@=internalglobs@*/
    01478 #endif /* HAVE_GETADDRINFO */
    01479
    01480 if (_ftp_debug)
    01481 fprintf(stderr, "-> %s", cmd);
    01482     if (fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
    01483         rc = FTPERR_SERVER_IO_ERROR;
    01484         goto errxit;
    01485     }
    01486
    01487     if ((rc = ftpCheckResponse(u, NULL))) {
    01488         goto errxit;
    01489     }
    01490
    01491     data->ftpFileDoneNeeded = 1;
    01492     u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
    01493     u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
    01494     return 0;
    01495
    01496 errxit:
    01497     /*@-observertrans@*/
    01498     fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
    01499     /*@=observertrans@*/
    01500     if (fdFileno(data) >= 0)
    01501         /*@-refcounttrans@*/ (void) fdClose(data); /*@=refcounttrans@*/
    01502     return rc;
    01503 }
    01504
    01505 /*@unchecked@*/ /*@null@*/
    01506 static rpmCallbackFunction      urlNotify = NULL;
    01507
    01508 /*@unchecked@*/ /*@null@*/
    01509 static void *                   urlNotifyData = NULL;
    01510
    01511 /*@unchecked@*/
    01512 static int                      urlNotifyCount = -1;
    01513
    01514 void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
    01515     urlNotify = notify;
    01516     urlNotifyData = notifyData;
    01517     urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
    01518 }
    01519
    01520 int ufdCopy(FD_t sfd, FD_t tfd)
    01521 {
    01522     char buf[BUFSIZ];
    01523     int itemsRead;
    01524     int itemsCopied = 0;
    01525     int rc = 0;
    01526     int notifier = -1;
    01527
    01528     if (urlNotify) {
    01529         /*@-noeffectuncon @*/ /* FIX: check rc */
    01530         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
    01531                 0, 0, NULL, urlNotifyData);
    01532         /*@=noeffectuncon @*/
    01533     }
    01534
    01535     while (1) {
    01536         rc = (int) Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
    01537         if (rc < 0)     /* XXX never happens Fread returns size_t */
    01538             break;
    01539         else if (rc == 0) {
    01540             rc = itemsCopied;
    01541             break;
    01542         }
    01543         itemsRead = rc;
    01544         rc = (int) Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
    01545         if (rc < 0)     /* XXX never happens Fwrite returns size_t */
    01546             break;
    01547         if (rc != itemsRead) {
    01548             rc = FTPERR_FILE_IO_ERROR;
    01549             break;
    01550         }
    01551
    01552         itemsCopied += itemsRead;
    01553         if (urlNotify && urlNotifyCount > 0) {
    01554             int n = itemsCopied/urlNotifyCount;
    01555             if (n != notifier) {
    01556                 /*@-noeffectuncon @*/ /* FIX: check rc */
    01557                 (void)(*urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
    01558                         itemsCopied, 0, NULL, urlNotifyData);
    01559                 /*@=noeffectuncon @*/
    01560                 notifier = n;
    01561             }
    01562         }
    01563     }
    01564
    01565     DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
    01566         ftpStrerror(rc)));
    01567
    01568     if (urlNotify) {
    01569         /*@-noeffectuncon @*/ /* FIX: check rc */
    01570         (void)(*urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
    01571                 itemsCopied, itemsCopied, NULL, urlNotifyData);
    01572         /*@=noeffectuncon @*/
    01573     }
    01574
    01575     return rc;
    01576 }
    01577
    01578 static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
    01579         /*@globals h_errno, fileSystem, internalState @*/
    01580         /*@modifies *uret, fileSystem, internalState @*/
    01581 {
    01582     urlinfo u;
    01583     int rc = 0;
    01584
    01585     if (urlSplit(url, &u) < 0)
    01586         return -1;
    01587
    01588     if (u->urltype == URL_IS_FTP) {
    01589         FD_t fd;
    01590
    01591         if ((fd = u->ctrl) == NULL) {
    01592             fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
    01593             fdSetOpen(u->ctrl, url, 0, 0);
    01594             fdSetIo(u->ctrl, ufdio);
    01595         }
    01596
    01597         fd->rd_timeoutsecs = ftpTimeoutSecs;
    01598         fd->contentLength = fd->bytesRemain = -1;
    01599         fd->url = NULL;         /* XXX FTP ctrl has not */
    01600         fd->ftpFileDoneNeeded = 0;
    01601         fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
    01602
    01603         if (fdFileno(u->ctrl) < 0) {
    01604             rpmlog(RPMLOG_DEBUG, D_("logging into %s as %s, pw %s\n"),
    01605                         u->host ? u->host : "???",
    01606                         u->user ? u->user : "ftp",
    01607                         u->password ? u->password : "(username)");
    01608
    01609             if ((rc = ftpLogin(u)) < 0) {       /* XXX save ftpLogin error */
    01610                 u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
    01611                 u->openError = rc;
    01612             }
    01613         }
    01614     }
    01615
    01616     if (uret != NULL)
    01617         *uret = urlLink(u, "urlConnect");
    01618     u = urlFree(u, "urlSplit (urlConnect)");
    01619
    01620     return rc;
    01621 }
    01622
    01623 int ufdGetFile(FD_t sfd, FD_t tfd)
    01624 {
    01625     int rc;
    01626
    01627     FDSANE(sfd);
    01628     FDSANE(tfd);
    01629     rc = ufdCopy(sfd, tfd);
    01630     (void) Fclose(sfd);
    01631     if (rc > 0)         /* XXX ufdCopy now returns no. bytes copied */
    01632         rc = 0;
    01633     return rc;
    01634 }
    01635
    01636 int ftpCmd(const char * cmd, const char * url, const char * arg2)
    01637 {
    01638     urlinfo u;
    01639     int rc;
    01640     const char * path;
    01641
    01642     if (urlConnect(url, &u) < 0)
    01643         return -1;
    01644
    01645     (void) urlPath(url, &path);
    01646
    01647     rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
    01648     u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
    01649     return rc;
    01650 }
    01651
    01652 /* XXX these aren't worth the pain of including correctly */
    01653 #if !defined(IAC)
    01654 #define IAC     ((unsigned char)255)    /* interpret as command: */
    01655 #endif
    01656 #if !defined(IP)
    01657 #define IP      ((unsigned char)244)    /* interrupt process--permanently */
    01658 #endif
    01659 #if !defined(DM)
    01660 #define DM      ((unsigned char)242)    /* data mark--for connect. cleaning */
    01661 #endif
    01662 #if !defined(SHUT_RDWR)
    01663 #define SHUT_RDWR       1+1
    01664 #endif
    01665
    01666 static int ftpAbort(urlinfo u, FD_t data)
    01667         /*@globals fileSystem, internalState @*/
    01668         /*@modifies u, data, fileSystem, internalState @*/
    01669 {
    01670     static unsigned char ipbuf[3] = { IAC, IP, IAC };
    01671     FD_t ctrl;
    01672     int rc;
    01673     int tosecs;
    01674
    01675     URLSANE(u);
    01676
    01677     if (data != NULL) {
    01678         data->ftpFileDoneNeeded = 0;
    01679         if (fdFileno(data) >= 0)
    01680             u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
    01681         u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
    01682     }
    01683     ctrl = u->ctrl;
    01684
    01685     DBGIO(0, (stderr, "-> ABOR\n"));
    01686
    01687 /*@-usereleased -compdef@*/
    01688     if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
    01689         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
    01690         return FTPERR_SERVER_IO_ERROR;
    01691     }
    01692
    01693     sprintf(u->buf, "%cABOR\r\n",(char) DM);
    01694     if (fdWrite(ctrl, u->buf, 7) != 7) {
    01695         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
    01696         return FTPERR_SERVER_IO_ERROR;
    01697     }
    01698
    01699     if (data && fdFileno(data) >= 0) {
    01700         /* XXX shorten data drain time wait */
    01701         tosecs = data->rd_timeoutsecs;
    01702         data->rd_timeoutsecs = 10;
    01703         if (fdReadable(data, data->rd_timeoutsecs) > 0) {
    01704             while ((ufdio->read)(data, u->buf, u->bufAlloced) > 0)
    01705                 u->buf[0] = '\0';
    01706         }
    01707         data->rd_timeoutsecs = tosecs;
    01708         /* XXX ftp abort needs to close the data channel to receive status */
    01709         (void) shutdown(fdFileno(data), SHUT_RDWR);
    01710         (void) close(fdFileno(data));
    01711         data->fps[0].fdno = -1; /* XXX WRONG but expedient */
    01712     }
    01713
    01714     /* XXX shorten ctrl drain time wait */
    01715     tosecs = u->ctrl->rd_timeoutsecs;
    01716     u->ctrl->rd_timeoutsecs = 10;
    01717     if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
    01718         rc = ftpCheckResponse(u, NULL);
    01719     }
    01720     rc = ftpCheckResponse(u, NULL);
    01721     u->ctrl->rd_timeoutsecs = tosecs;
    01722
    01723     return rc;
    01724 /*@=usereleased =compdef@*/
    01725 }
    01726
    01727 static int ftpFileDone(urlinfo u, FD_t data)
    01728         /*@globals fileSystem @*/
    01729         /*@modifies u, data, fileSystem @*/
    01730 {
    01731     int rc = 0;
    01732
    01733     URLSANE(u);
    01734     assert(data->ftpFileDoneNeeded);
    01735
    01736     if (data->ftpFileDoneNeeded) {
    01737         data->ftpFileDoneNeeded = 0;
    01738         u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
    01739         u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
    01740         rc = ftpCheckResponse(u, NULL);
    01741     }
    01742     return rc;
    01743 }
    01744
    01745 #ifndef WITH_NEON
    01746 static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
    01747         /*@globals fileSystem @*/
    01748         /*@modifies ctrl, *str, fileSystem @*/
    01749 {
    01750     int ec = 0;
    01751     int rc;
    01752
    01753     URLSANE(u);
    01754     rc = checkResponse(u, ctrl, &ec, str);
    01755
    01756 if (_ftp_debug && !(rc == 0 && (ec == 200 || ec == 201)))
    01757 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
    01758
    01759     switch (ec) {
    01760     case 200:
    01761     case 201:                   /* 201 Created. */
    01762         break;
    01763     case 204:                   /* HACK: if overwriting, 204 No Content. */
    01764     case 403:                   /* 403 Forbidden. */
    01765         ctrl->syserrno = EACCES;        /* HACK */
    01766         rc = FTPERR_UNKNOWN;
    01767         break;
    01768     default:
    01769         rc = FTPERR_FILE_NOT_FOUND;
    01770         break;
    01771     }
    01772     return rc;
    01773 }
    01774
    01775 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
    01776         /*@globals h_errno, fileSystem, internalState @*/
    01777         /*@modifies ctrl, fileSystem, internalState @*/
    01778 {
    01779     urlinfo u;
    01780     const char * host;
    01781     const char * path;
    01782     char hthost[NI_MAXHOST];
    01783     int port;
    01784     int rc;
    01785     char * req;
    01786     size_t len;
    01787     int retrying = 0;
    01788
    01789 assert(ctrl != NULL);
    01790     u = ctrl->url;
    01791     URLSANE(u);
    01792
    01793     if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
    01794         return FTPERR_BAD_HOSTNAME;
    01795     if (strchr(host, ':'))
    01796         sprintf(hthost, "[%s]", host);
    01797     else
    01798         strcpy(hthost, host);
    01799
    01800     if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
    01801     path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
    01802     if (path == NULL) path = "";
    01803
    01804 reopen:
    01805     if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
    01806         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
    01807     }
    01808
    01809 /*@-usereleased@*/
    01810     if (fdFileno(ctrl) < 0) {
    01811         rc = tcpConnect(ctrl, host, port);
    01812         if (rc < 0)
    01813             goto errxit2;
    01814         ctrl = fdLink(ctrl, "open ctrl (httpReq)");
    01815     }
    01816
    01817     len = sizeof("\
    01818 req x HTTP/1.0\r\n\
    01819 User-Agent: rpm/3.0.4\r\n\
    01820 Host: y:z\r\n\
    01821 Accept: text/plain\r\n\
    01822 Transfer-Encoding: chunked\r\n\
    01823 \r\n\
    01824 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(hthost) + 20;
    01825
    01826     req = alloca(len);
    01827     *req = '\0';
    01828
    01829   if (!strcmp(httpCmd, "PUT")) {
    01830     sprintf(req, "\
    01831 %s %s HTTP/1.%d\r\n\
    01832 User-Agent: rpm/%s\r\n\
    01833 Host: %s:%d\r\n\
    01834 Accept: text/plain\r\n\
    01835 Transfer-Encoding: chunked\r\n\
    01836 \r\n\
    01837 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
    01838 } else {
    01839     sprintf(req, "\
    01840 %s %s HTTP/1.%d\r\n\
    01841 User-Agent: rpm/%s\r\n\
    01842 Host: %s:%d\r\n\
    01843 Accept: text/plain\r\n\
    01844 \r\n\
    01845 ",      httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
    01846 }
    01847
    01848 if (_ftp_debug)
    01849 fprintf(stderr, "-> %s", req);
    01850
    01851     len = strlen(req);
    01852     if (fdWrite(ctrl, req, len) != len) {
    01853         rc = FTPERR_SERVER_IO_ERROR;
    01854         goto errxit;
    01855     }
    01856
    01857     if (!strcmp(httpCmd, "PUT")) {
    01858         ctrl->wr_chunked = 1;
    01859     } else {
    01860
    01861         rc = httpResp(u, ctrl, NULL);
    01862
    01863         if (rc) {
    01864             if (!retrying) {    /* not HTTP_OK */
    01865                 retrying = 1;
    01866                 /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
    01867                 goto reopen;
    01868             }
    01869             goto errxit;
    01870         }
    01871     }
    01872
    01873     ctrl = fdLink(ctrl, "open data (httpReq)");
    01874     return 0;
    01875
    01876 errxit:
    01877     /*@-observertrans@*/
    01878     fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
    01879     /*@=observertrans@*/
    01880 errxit2:
    01881     if (fdFileno(ctrl) >= 0)
    01882         /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
    01883     return rc;
    01884 /*@=usereleased@*/
    01885 }
    01886 #endif /* WITH_NEON */
    01887
    01888 /* XXX DYING: unused */
    01889 void * ufdGetUrlinfo(FD_t fd)
    01890 {
    01891     FDSANE(fd);
    01892     if (fd->url == NULL)
    01893         return NULL;
    01894     return urlLink(fd->url, "ufdGetUrlinfo");
    01895 }
    01896
    01897 /* =============================================================== */
    01898 static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count)
    01899         /*@globals fileSystem, internalState @*/
    01900         /*@modifies buf, fileSystem, internalState @*/
    01901         /*@requires maxSet(buf) >= (count - 1) @*/
    01902         /*@ensures maxRead(buf) == result @*/
    01903 {
    01904     FD_t fd = c2f(cookie);
    01905     size_t bytesRead;
    01906     size_t total;
    01907
    01908     if (fdGetIo(fd) == fdio) {
    01909         struct stat sb;
    01910         int fdno = fdFileno(fd);
    01911         (void) fstat(fdno, &sb);
    01912         if (S_ISREG(sb.st_mode))
    01913             return fdRead(fd, buf, count);
    01914     }
    01915
    01916     UFDONLY(fd);
    01917     assert(fd->rd_timeoutsecs >= 0);
    01918
    01919     for (total = 0; total < count; total += bytesRead) {
    01920
    01921         int rc;
    01922
    01923         bytesRead = 0;
    01924
    01925         /* Is there data to read? */
    01926         if (fd->bytesRemain == 0) return (ssize_t) total; /* XXX simulate EOF */
    01927         rc = fdReadable(fd, fd->rd_timeoutsecs);
    01928
    01929         switch (rc) {
    01930         case -1:        /* error */
    01931         case  0:        /* timeout */
    01932             return (ssize_t) total;
    01933             /*@notreached@*/ /*@switchbreak@*/ break;
    01934         default:        /* data to read */
    01935             /*@switchbreak@*/ break;
    01936         }
    01937
    01938         rc = (int) fdRead(fd, buf + total, count - total);
    01939
    01940         if (rc < 0) {
    01941             switch (errno) {
    01942             case EWOULDBLOCK:
    01943                 continue;
    01944                 /*@notreached@*/ /*@switchbreak@*/ break;
    01945             default:
    01946                 /*@switchbreak@*/ break;
    01947             }
    01948 if (_rpmio_debug)
    01949 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
    01950             return rc;
    01951             /*@notreached@*/ break;
    01952         } else if (rc == 0) {
    01953             return (ssize_t) total;
    01954             /*@notreached@*/ break;
    01955         }
    01956         bytesRead = (size_t) rc;
    01957     }
    01958
    01959     return (ssize_t) count;
    01960 }
    01961
    01962 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
    01963         /*@globals fileSystem, internalState @*/
    01964         /*@modifies fileSystem, internalState @*/
    01965 {
    01966     FD_t fd = c2f(cookie);
    01967     size_t bytesWritten;
    01968     size_t total = 0;
    01969
    01970 #ifdef  NOTYET
    01971     if (fdGetIo(fd) == fdio) {
    01972         struct stat sb;
    01973         (void) fstat(fdGetFdno(fd), &sb);
    01974         if (S_ISREG(sb.st_mode))
    01975             return fdWrite(fd, buf, count);
    01976     }
    01977 #endif
    01978
    01979     UFDONLY(fd);
    01980
    01981     for (total = 0; total < count; total += bytesWritten) {
    01982
    01983         int rc;
    01984
    01985         bytesWritten = 0;
    01986
    01987         /* Is there room to write data? */
    01988         if (fd->bytesRemain == 0) {
    01989 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
    01990             return (ssize_t) total;     /* XXX simulate EOF */
    01991         }
    01992         rc = fdWritable(fd, 2);         /* XXX configurable? */
    01993
    01994         switch (rc) {
    01995         case -1:        /* error */
    01996         case  0:        /* timeout */
    01997             return (ssize_t) total;
    01998             /*@notreached@*/ /*@switchbreak@*/ break;
    01999         default:        /* data to write */
    02000             /*@switchbreak@*/ break;
    02001         }
    02002
    02003         rc = (int) fdWrite(fd, buf + total, count - total);
    02004
    02005         if (rc < 0) {
    02006             switch (errno) {
    02007             case EWOULDBLOCK:
    02008                 continue;
    02009                 /*@notreached@*/ /*@switchbreak@*/ break;
    02010             default:
    02011                 /*@switchbreak@*/ break;
    02012             }
    02013 if (_rpmio_debug)
    02014 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
    02015             return rc;
    02016             /*@notreached@*/ break;
    02017         } else if (rc == 0) {
    02018             return (ssize_t) total;
    02019             /*@notreached@*/ break;
    02020         }
    02021         bytesWritten = (size_t) rc;
    02022     }
    02023
    02024     return (ssize_t) count;
    02025 }
    02026
    02027 static inline int ufdSeek(void * cookie, _libio_pos_t pos, int whence)
    02028         /*@globals fileSystem, internalState @*/
    02029         /*@modifies fileSystem, internalState @*/
    02030 {
    02031     FD_t fd = c2f(cookie);
    02032
    02033     switch (fd->urlType) {
    02034     case URL_IS_UNKNOWN:
    02035     case URL_IS_PATH:
    02036         break;
    02037     case URL_IS_HTTPS:
    02038     case URL_IS_HTTP:
    02039     case URL_IS_HKP:
    02040     case URL_IS_FTP:
    02041     case URL_IS_DASH:
    02042     default:
    02043         return -2;
    02044         /*@notreached@*/ break;
    02045     }
    02046     return fdSeek(cookie, pos, whence);
    02047 }
    02048
    02049 /*@-usereleased@*/      /* LCL: fd handling is tricky here. */
    02050 int ufdClose( /*@only@*/ void * cookie)
    02051 {
    02052     FD_t fd = c2f(cookie);
    02053
    02054     UFDONLY(fd);
    02055
    02056     if (fd->url) {
    02057         urlinfo u = fd->url;
    02058
    02059         if (fd == u->data)
    02060                 fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
    02061         else
    02062                 fd = fdFree(fd, "grab data (ufdClose)");
    02063         (void) urlFree(fd->url, "url (ufdClose)");
    02064         fd->url = NULL;
    02065         u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
    02066
    02067         if (u->urltype == URL_IS_FTP) {
    02068
    02069             /* XXX if not using libio, lose the fp from fpio */
    02070             {   FILE * fp;
    02071                 /*@+voidabstract -nullpass@*/
    02072                 fp = fdGetFILE(fd);
    02073                 if (noLibio && fp)
    02074                     fdSetFp(fd, NULL);
    02075                 /*@=voidabstract =nullpass@*/
    02076             }
    02077
    02078             /*
    02079              * Non-error FTP has 4 refs on the data fd:
    02080              *  "persist data (ufdOpen FTP)"            rpmio.c:888
    02081              *  "grab data (ufdOpen FTP)"               rpmio.c:892
    02082              *  "open data (ftpReq)"                    ftp.c:633
    02083              *  "fopencookie"                           rpmio.c:1507
    02084              *
    02085              * Non-error FTP has 5 refs on the ctrl fd:
    02086              *  "persist ctrl"                          url.c:176
    02087              *  "grab ctrl (urlConnect FTP)"            rpmio.c:404
    02088              *  "open ctrl"                             ftp.c:504
    02089              *  "grab data (ftpReq)"                    ftp.c:661
    02090              *  "open data (ftpReq)"                    ftp.c:662
    02091              */
    02092             if (fd->bytesRemain > 0) {
    02093                 if (fd->ftpFileDoneNeeded) {
    02094                     if (fdReadable(u->ctrl, 0) > 0)
    02095                         (void) ftpFileDone(u, fd);
    02096                     else
    02097                         (void) ftpAbort(u, fd);
    02098                 }
    02099             } else {
    02100                 int rc;
    02101                 /* XXX STOR et al require close before ftpFileDone */
    02102                 /*@-refcounttrans@*/
    02103                 rc = fdClose(fd);
    02104                 /*@=refcounttrans@*/
    02105 #if 0   /* XXX error exit from ufdOpen does not have this set */
    02106                 assert(fd->ftpFileDoneNeeded != 0);
    02107 #endif
    02108                 /*@-compdef@*/ /* FIX: u->data undefined */
    02109                 if (fd->ftpFileDoneNeeded)
    02110                     (void) ftpFileDone(u, fd);
    02111                 /*@=compdef@*/
    02112                 return rc;
    02113             }
    02114         }
    02115
    02116         /* XXX Why not (u->urltype == URL_IS_HTTP) ??? */
    02117         /* XXX Why not (u->urltype == URL_IS_HTTPS) ??? */
    02118         /* XXX Why not (u->urltype == URL_IS_HKP) ??? */
    02119         if (u->scheme != NULL
    02120          && (!strncmp(u->scheme, "http", sizeof("http")-1) || !strncmp(u->scheme, "hkp", sizeof("hkp")-1)))
    02121         {
    02122             /*
    02123              * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
    02124              *  "persist ctrl"                          url.c:177
    02125              *  "grab ctrl (ufdOpen HTTP)"              rpmio.c:924
    02126              *  "grab data (ufdOpen HTTP)"              rpmio.c:928
    02127              *  "open ctrl (httpReq)"                   ftp.c:382
    02128              *  "open data (httpReq)"                   ftp.c:435
    02129              */
    02130
    02131             if (fd == u->ctrl)
    02132                 fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
    02133             else if (fd == u->data)
    02134                 fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
    02135             else
    02136                 fd = fdFree(fd, "open data (ufdClose HTTP)");
    02137
    02138             /* XXX if not using libio, lose the fp from fpio */
    02139             {   FILE * fp;
    02140                 /*@+voidabstract -nullpass@*/
    02141                 fp = fdGetFILE(fd);
    02142                 if (noLibio && fp)
    02143                     fdSetFp(fd, NULL);
    02144                 /*@=voidabstract =nullpass@*/
    02145             }
    02146
    02147             /* If content remains, then don't persist. */
    02148             if (fd->bytesRemain > 0)
    02149                 fd->persist = 0;
    02150             fd->contentLength = fd->bytesRemain = -1;
    02151
    02152             /* If persisting, then Fclose will juggle refcounts. */
    02153             if (fd->persist && (fd == u->ctrl || fd == u->data))
    02154                 return 0;
    02155         }
    02156     }
    02157     return fdClose(fd);
    02158 }
    02159 /*@=usereleased@*/
    02160
    02161 /*@-nullstate@*/        /* FIX: u->{ctrl,data}->url undef after XurlLink. */
    02162 /*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
    02163                 /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
    02164         /*@modifies *uret @*/
    02165 {
    02166     urlinfo u = NULL;
    02167     FD_t fd = NULL;
    02168
    02169 #if 0   /* XXX makeTempFile() heartburn */
    02170     assert(!(flags & O_RDWR));
    02171 #endif
    02172     if (urlConnect(url, &u) < 0)
    02173         goto exit;
    02174
    02175     if (u->data == NULL)
    02176         u->data = fdNew("persist data (ftpOpen)");
    02177
    02178     if (u->data->url == NULL)
    02179         fd = fdLink(u->data, "grab data (ftpOpen persist data)");
    02180     else
    02181         fd = fdNew("grab data (ftpOpen)");
    02182
    02183     if (fd) {
    02184         fdSetOpen(fd, url, flags, mode);
    02185         fdSetIo(fd, ufdio);
    02186         fd->ftpFileDoneNeeded = 0;
    02187         fd->rd_timeoutsecs = ftpTimeoutSecs;
    02188         fd->contentLength = fd->bytesRemain = -1;
    02189         fd->url = urlLink(u, "url (ufdOpen FTP)");
    02190         fd->urlType = URL_IS_FTP;
    02191     }
    02192
    02193 exit:
    02194     if (uret)
    02195         *uret = u;
    02196     /*@-refcounttrans@*/
    02197     return fd;
    02198     /*@=refcounttrans@*/
    02199 }
    02200 /*@=nullstate@*/
    02201
    02202 static /*@null@*/ FD_t ufdOpen(const char * url, int flags, mode_t mode)
    02203         /*@globals h_errno, fileSystem, internalState @*/
    02204         /*@modifies fileSystem, internalState @*/
    02205 {
    02206     FD_t fd = NULL;
    02207     const char * cmd;
    02208     urlinfo u;
    02209     const char * path;
    02210     urltype urlType = urlPath(url, &path);
    02211
    02212 if (_rpmio_debug)
    02213 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
    02214
    02215     switch (urlType) {
    02216     case URL_IS_FTP:
    02217         fd = ftpOpen(url, flags, mode, &u);
    02218         if (fd == NULL || u == NULL)
    02219             break;
    02220
    02221         /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
    02222         cmd = ((flags & O_WRONLY)
    02223                 ?  ((flags & O_APPEND) ? "APPE" :
    02224                    ((flags & O_CREAT) ? "STOR" : "STOR"))
    02225                 :  ((flags & O_CREAT) ? "STOR" : "RETR"));
    02226         u->openError = ftpReq(fd, cmd, path);
    02227         if (u->openError < 0) {
    02228             /* XXX make sure that we can exit through ufdClose */
    02229             fd = fdLink(fd, "error data (ufdOpen FTP)");
    02230         } else {
    02231             fd->bytesRemain = ((!strcmp(cmd, "RETR"))
    02232                 ?  fd->contentLength : -1);
    02233             fd->wr_chunked = 0;
    02234         }
    02235         break;
    02236     case URL_IS_HTTPS:
    02237     case URL_IS_HTTP:
    02238     case URL_IS_HKP:
    02239 #ifdef WITH_NEON
    02240         fd = davOpen(url, flags, mode, &u);
    02241 #else
    02242         fd = httpOpen(url, flags, mode, &u);
    02243 #endif
    02244         if (fd == NULL || u == NULL)
    02245             break;
    02246
    02247         cmd = ((flags & O_WRONLY)
    02248                 ?  ((flags & O_APPEND) ? "PUT" :
    02249                    ((flags & O_CREAT) ? "PUT" : "PUT"))
    02250                 : "GET");
    02251 #ifdef WITH_NEON
    02252         u->openError = davReq(fd, cmd, path);
    02253 #else
    02254         u->openError = httpReq(fd, cmd, path);
    02255 #endif
    02256         if (u->openError < 0) {
    02257             /* XXX make sure that we can exit through ufdClose */
    02258             fd = fdLink(fd, "error ctrl (ufdOpen HTTP)");
    02259             fd = fdLink(fd, "error data (ufdOpen HTTP)");
    02260         } else {
    02261             fd->bytesRemain = ((!strcmp(cmd, "GET"))
    02262                 ?  fd->contentLength : -1);
    02263             fd->wr_chunked = ((!strcmp(cmd, "PUT"))
    02264                 ?  fd->wr_chunked : 0);
    02265         }
    02266         break;
    02267     case URL_IS_DASH:
    02268         assert(!(flags & O_RDWR));
    02269         fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
    02270         if (fd) {
    02271             fdSetOpen(fd, url, flags, mode);
    02272             fdSetIo(fd, ufdio);
    02273             fd->rd_timeoutsecs = 600;   /* XXX W2DO? 10 mins? */
    02274             fd->contentLength = fd->bytesRemain = -1;
    02275         }
    02276         break;
    02277     case URL_IS_PATH:
    02278     case URL_IS_UNKNOWN:
    02279     default:
    02280         fd = fdOpen(path, flags, mode);
    02281         if (fd) {
    02282             fdSetIo(fd, ufdio);
    02283             fd->rd_timeoutsecs = 1;
    02284             fd->contentLength = fd->bytesRemain = -1;
    02285         }
    02286         break;
    02287     }
    02288
    02289     if (fd == NULL) return NULL;
    02290     fd->urlType = urlType;
    02291     if (Fileno(fd) < 0) {
    02292         (void) ufdClose(fd);
    02293         return NULL;
    02294     }
    02295 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
    02296     return fd;
    02297 }
    02298
    02299 /*@-type@*/ /* LCL: function typedefs */
    02300 static struct FDIO_s ufdio_s = {
    02301   ufdRead, ufdWrite, ufdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
    02302   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
    02303 };
    02304 /*@=type@*/
    02305 FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
    02306
    02307 /* =============================================================== */
    02308 /* Support for GZIP library.
    02309  */
    02310 #ifdef  HAVE_ZLIB_H
    02311 /*@-moduncon@*/
    02312
    02313 /*@-noparams@*/
    02314 #include <zlib.h>
    02315 /*@=noparams@*/
    02316
    02317 static inline /*@dependent@*/ /*@null@*/ void * gzdFileno(FD_t fd)
    02318         /*@*/
    02319 {
    02320     void * rc = NULL;
    02321     int i;
    02322
    02323     FDSANE(fd);
    02324     for (i = fd->nfps; i >= 0; i--) {
    02325         FDSTACK_t * fps = &fd->fps[i];
    02326         if (fps->io != gzdio)
    02327             continue;
    02328         rc = fps->fp;
    02329         break;
    02330     }
    02331
    02332     return rc;
    02333 }
    02334
    02335 static /*@null@*/
    02336 FD_t gzdOpen(const char * path, const char * fmode)
    02337         /*@globals fileSystem, internalState @*/
    02338         /*@modifies fileSystem, internalState @*/
    02339 {
    02340     FD_t fd;
    02341     gzFile gzfile;
    02342     if ((gzfile = gzopen(path, fmode)) == NULL)
    02343         return NULL;
    02344     fd = fdNew("open (gzdOpen)");
    02345     fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
    02346
    02347 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
    02348     return fdLink(fd, "gzdOpen");
    02349 }
    02350
    02351 static /*@null@*/ FD_t gzdFdopen(void * cookie, const char *fmode)
    02352         /*@globals fileSystem, internalState @*/
    02353         /*@modifies fileSystem, internalState @*/
    02354 {
    02355     FD_t fd = c2f(cookie);
    02356     int fdno;
    02357     gzFile gzfile;
    02358
    02359     if (fmode == NULL) return NULL;
    02360     fdno = fdFileno(fd);
    02361     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
    02362     if (fdno < 0) return NULL;
    02363     gzfile = gzdopen(fdno, fmode);
    02364     if (gzfile == NULL) return NULL;
    02365
    02366     fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
    02367
    02368     return fdLink(fd, "gzdFdopen");
    02369 }
    02370
    02371 static int gzdFlush(FD_t fd)
    02372         /*@globals fileSystem @*/
    02373         /*@modifies fileSystem @*/
    02374 {
    02375     gzFile gzfile;
    02376     gzfile = gzdFileno(fd);
    02377     if (gzfile == NULL) return -2;
    02378     return gzflush(gzfile, Z_SYNC_FLUSH);       /* XXX W2DO? */
    02379 }
    02380
    02381 /* =============================================================== */
    02382 static ssize_t gzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
    02383         /*@globals fileSystem, internalState @*/
    02384         /*@modifies buf, fileSystem, internalState @*/
    02385 {
    02386     FD_t fd = c2f(cookie);
    02387     gzFile gzfile;
    02388     ssize_t rc;
    02389
    02390     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
    02391
    02392     gzfile = gzdFileno(fd);
    02393     if (gzfile == NULL) return -2;      /* XXX can't happen */
    02394
    02395     fdstat_enter(fd, FDSTAT_READ);
    02396     rc = gzread(gzfile, buf, (unsigned)count);
    02397 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
    02398     if (rc < 0) {
    02399         int zerror = 0;
    02400         fd->errcookie = gzerror(gzfile, &zerror);
    02401         if (zerror == Z_ERRNO) {
    02402             fd->syserrno = errno;
    02403             fd->errcookie = strerror(fd->syserrno);
    02404         }
    02405     } else if (rc >= 0) {
    02406         fdstat_exit(fd, FDSTAT_READ, rc);
    02407         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
    02408     }
    02409     return rc;
    02410 }
    02411
    02412 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
    02413         /*@globals fileSystem, internalState @*/
    02414         /*@modifies fileSystem, internalState @*/
    02415 {
    02416     FD_t fd = c2f(cookie);
    02417     gzFile gzfile;
    02418     ssize_t rc;
    02419
    02420     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
    02421
    02422     if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
    02423
    02424     gzfile = gzdFileno(fd);
    02425     if (gzfile == NULL) return -2;      /* XXX can't happen */
    02426
    02427     fdstat_enter(fd, FDSTAT_WRITE);
    02428     rc = gzwrite(gzfile, (void *)buf, (unsigned)count);
    02429 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
    02430     if (rc < 0) {
    02431         int zerror = 0;
    02432         fd->errcookie = gzerror(gzfile, &zerror);
    02433         if (zerror == Z_ERRNO) {
    02434             fd->syserrno = errno;
    02435             fd->errcookie = strerror(fd->syserrno);
    02436         }
    02437     } else if (rc > 0) {
    02438         fdstat_exit(fd, FDSTAT_WRITE, rc);
    02439     }
    02440     return rc;
    02441 }
    02442
    02443 /* XXX zlib-1.0.4 has not */
    02444 #define HAVE_GZSEEK     /* XXX autoFu doesn't set this anymore. */
    02445 static inline int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
    02446         /*@globals fileSystem, internalState @*/
    02447         /*@modifies fileSystem, internalState @*/
    02448 {
    02449     int rc;
    02450 #if defined(HAVE_GZSEEK)
    02451 #ifdef USE_COOKIE_SEEK_POINTER
    02452     _IO_off64_t p = *pos;
    02453 #else
    02454     off_t p = pos;
    02455 #endif
    02456     FD_t fd = c2f(cookie);
    02457     gzFile gzfile;
    02458
    02459     if (fd == NULL) return -2;
    02460     assert(fd->bytesRemain == -1);      /* XXX FIXME */
    02461
    02462     gzfile = gzdFileno(fd);
    02463     if (gzfile == NULL) return -2;      /* XXX can't happen */
    02464
    02465     fdstat_enter(fd, FDSTAT_SEEK);
    02466     rc = gzseek(gzfile, (long)p, whence);
    02467 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
    02468     if (rc < 0) {
    02469         int zerror = 0;
    02470         fd->errcookie = gzerror(gzfile, &zerror);
    02471         if (zerror == Z_ERRNO) {
    02472             fd->syserrno = errno;
    02473             fd->errcookie = strerror(fd->syserrno);
    02474         }
    02475     } else if (rc >= 0) {
    02476         fdstat_exit(fd, FDSTAT_SEEK, rc);
    02477     }
    02478 #else
    02479     rc = -2;
    02480 #endif
    02481     return rc;
    02482 }
    02483
    02484 static int gzdClose( /*@only@*/ void * cookie)
    02485         /*@globals fileSystem, internalState @*/
    02486         /*@modifies fileSystem, internalState @*/
    02487 {
    02488     FD_t fd = c2f(cookie);
    02489     gzFile gzfile;
    02490     int rc;
    02491
    02492     gzfile = gzdFileno(fd);
    02493     if (gzfile == NULL) return -2;      /* XXX can't happen */
    02494
    02495     fdstat_enter(fd, FDSTAT_CLOSE);
    02496     /*@-dependenttrans@*/
    02497     rc = gzclose(gzfile);
    02498     /*@=dependenttrans@*/
    02499
    02500     /* XXX TODO: preserve fd if errors */
    02501
    02502     if (fd) {
    02503 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
    02504         if (rc < 0) {
    02505             fd->errcookie = "gzclose error";
    02506             if (rc == Z_ERRNO) {
    02507                 fd->syserrno = errno;
    02508                 fd->errcookie = strerror(fd->syserrno);
    02509             }
    02510         } else if (rc >= 0) {
    02511             fdstat_exit(fd, FDSTAT_CLOSE, rc);
    02512         }
    02513     }
    02514
    02515 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
    02516
    02517     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
    02518     if (rc == 0)
    02519         fd = fdFree(fd, "open (gzdClose)");
    02520     return rc;
    02521 }
    02522
    02523 /*@-type@*/ /* LCL: function typedefs */
    02524 static struct FDIO_s gzdio_s = {
    02525   gzdRead, gzdWrite, gzdSeek, gzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
    02526   NULL, gzdOpen, gzdFileno, gzdFlush,   NULL, NULL, NULL, NULL, NULL
    02527 };
    02528 /*@=type@*/
    02529 FDIO_t gzdio = /*@-compmempass@*/ &gzdio_s /*@=compmempass@*/ ;
    02530
    02531 /*@=moduncon@*/
    02532 #endif  /* HAVE_ZLIB_H */
    02533
    02534 /* =============================================================== */
    02535 /* Support for BZIP2 library.
    02536  */
    02537 #if defined(HAVE_BZLIB_H)
    02538 /*@-moduncon@*/
    02539
    02540 #include <bzlib.h>
    02541
    02542 static inline /*@dependent@*/ void * bzdFileno(FD_t fd)
    02543         /*@*/
    02544 {
    02545     void * rc = NULL;
    02546     int i;
    02547
    02548     FDSANE(fd);
    02549     for (i = fd->nfps; i >= 0; i--) {
    02550         FDSTACK_t * fps = &fd->fps[i];
    02551         if (fps->io != bzdio)
    02552             continue;
    02553         rc = fps->fp;
    02554         break;
    02555     }
    02556
    02557     return rc;
    02558 }
    02559
    02560 /*@-globuse@*/
    02561 static /*@null@*/ FD_t bzdOpen(const char * path, const char * mode)
    02562         /*@globals fileSystem @*/
    02563         /*@modifies fileSystem @*/
    02564 {
    02565     FD_t fd;
    02566     BZFILE *bzfile;;
    02567     if ((bzfile = BZ2_bzopen(path, mode)) == NULL)
    02568         return NULL;
    02569     fd = fdNew("open (bzdOpen)");
    02570     fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
    02571     return fdLink(fd, "bzdOpen");
    02572 }
    02573 /*@=globuse@*/
    02574
    02575 /*@-globuse@*/
    02576 static /*@null@*/ FD_t bzdFdopen(void * cookie, const char * fmode)
    02577         /*@globals fileSystem, internalState @*/
    02578         /*@modifies fileSystem, internalState @*/
    02579 {
    02580     FD_t fd = c2f(cookie);
    02581     int fdno;
    02582     BZFILE *bzfile;
    02583
    02584     if (fmode == NULL) return NULL;
    02585     fdno = fdFileno(fd);
    02586     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
    02587     if (fdno < 0) return NULL;
    02588     bzfile = BZ2_bzdopen(fdno, fmode);
    02589     if (bzfile == NULL) return NULL;
    02590
    02591     fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
    02592
    02593     return fdLink(fd, "bzdFdopen");
    02594 }
    02595 /*@=globuse@*/
    02596
    02597 /*@-globuse@*/
    02598 static int bzdFlush(FD_t fd)
    02599         /*@globals fileSystem @*/
    02600         /*@modifies fileSystem @*/
    02601 {
    02602     return BZ2_bzflush(bzdFileno(fd));
    02603 }
    02604 /*@=globuse@*/
    02605
    02606 /* =============================================================== */
    02607 /*@-globuse@*/
    02608 /*@-mustmod@*/          /* LCL: *buf is modified */
    02609 static ssize_t bzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
    02610         /*@globals fileSystem, internalState @*/
    02611         /*@modifies *buf, fileSystem, internalState @*/
    02612 {
    02613     FD_t fd = c2f(cookie);
    02614     BZFILE *bzfile;
    02615     ssize_t rc = 0;
    02616
    02617     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
    02618     bzfile = bzdFileno(fd);
    02619     fdstat_enter(fd, FDSTAT_READ);
    02620     if (bzfile)
    02621         /*@-compdef@*/
    02622         rc = BZ2_bzread(bzfile, buf, (int)count);
    02623         /*@=compdef@*/
    02624     if (rc == -1) {
    02625         int zerror = 0;
    02626         if (bzfile)
    02627             fd->errcookie = BZ2_bzerror(bzfile, &zerror);
    02628     } else if (rc >= 0) {
    02629         fdstat_exit(fd, FDSTAT_READ, rc);
    02630         /*@-compdef@*/
    02631         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
    02632         /*@=compdef@*/
    02633     }
    02634     return rc;
    02635 }
    02636 /*@=mustmod@*/
    02637 /*@=globuse@*/
    02638
    02639 /*@-globuse@*/
    02640 static ssize_t bzdWrite(void * cookie, const char * buf, size_t count)
    02641         /*@globals fileSystem, internalState @*/
    02642         /*@modifies fileSystem, internalState @*/
    02643 {
    02644     FD_t fd = c2f(cookie);
    02645     BZFILE *bzfile;
    02646     ssize_t rc;
    02647
    02648     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
    02649
    02650     if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
    02651
    02652     bzfile = bzdFileno(fd);
    02653     fdstat_enter(fd, FDSTAT_WRITE);
    02654     rc = BZ2_bzwrite(bzfile, (void *)buf, (int)count);
    02655     if (rc == -1) {
    02656         int zerror = 0;
    02657         fd->errcookie = BZ2_bzerror(bzfile, &zerror);
    02658     } else if (rc > 0) {
    02659         fdstat_exit(fd, FDSTAT_WRITE, rc);
    02660     }
    02661     return rc;
    02662 }
    02663 /*@=globuse@*/
    02664
    02665 static inline int bzdSeek(void * cookie, /*@unused@*/ _libio_pos_t pos,
    02666                         /*@unused@*/ int whence)
    02667         /*@*/
    02668 {
    02669     FD_t fd = c2f(cookie);
    02670
    02671     BZDONLY(fd);
    02672     return -2;
    02673 }
    02674
    02675 static int bzdClose( /*@only@*/ void * cookie)
    02676         /*@globals fileSystem, internalState @*/
    02677         /*@modifies fileSystem, internalState @*/
    02678 {
    02679     FD_t fd = c2f(cookie);
    02680     BZFILE *bzfile;
    02681     int rc;
    02682
    02683     bzfile = bzdFileno(fd);
    02684
    02685     if (bzfile == NULL) return -2;
    02686     fdstat_enter(fd, FDSTAT_CLOSE);
    02687     /*@-noeffectuncon@*/ /* FIX: check rc */
    02688     BZ2_bzclose(bzfile);
    02689     /*@=noeffectuncon@*/
    02690     rc = 0;     /* XXX FIXME */
    02691
    02692     /* XXX TODO: preserve fd if errors */
    02693
    02694     if (fd) {
    02695         if (rc == -1) {
    02696             int zerror = 0;
    02697             fd->errcookie = BZ2_bzerror(bzfile, &zerror);
    02698         } else if (rc >= 0) {
    02699             fdstat_exit(fd, FDSTAT_CLOSE, rc);
    02700         }
    02701     }
    02702
    02703 DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
    02704
    02705     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
    02706     if (rc == 0)
    02707         fd = fdFree(fd, "open (bzdClose)");
    02708     return rc;
    02709 }
    02710
    02711 /*@-type@*/ /* LCL: function typedefs */
    02712 static struct FDIO_s bzdio_s = {
    02713   bzdRead, bzdWrite, bzdSeek, bzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
    02714   NULL, bzdOpen, bzdFileno, bzdFlush,   NULL, NULL, NULL, NULL, NULL
    02715 };
    02716 /*@=type@*/
    02717 FDIO_t bzdio = /*@-compmempass@*/ &bzdio_s /*@=compmempass@*/ ;
    02718
    02719 /*@=moduncon@*/
    02720 #endif  /* HAVE_BZLIB_H */
    02721
    02722 /* =============================================================== */
    02723 /* Support for LZMA library.
    02724  */
    02725 #include "LzmaDecode.h"
    02726
    02727 #define kInBufferSize (1 << 15)
    02728 typedef struct _CBuffer {
    02729   ILzmaInCallback InCallback;
    02730 /*@dependent@*/
    02731   FILE *File;
    02732   unsigned char Buffer[kInBufferSize];
    02733 } CBuffer;
    02734
    02735 typedef struct lzfile {
    02736     CBuffer g_InBuffer;
    02737     CLzmaDecoderState state;  /* it's about 24-80 bytes structure, if int is 32-bit */
    02738     unsigned char properties[LZMA_PROPERTIES_SIZE];
    02739
    02740 #if 0
    02741     FILE *file;
    02742 #endif
    02743     pid_t pid;
    02744 } LZFILE;
    02745
    02746 static size_t MyReadFile(FILE *file, void *data, size_t size)
    02747         /*@globals fileSystem @*/
    02748         /*@modifies *file, *data, fileSystem @*/
    02749 {
    02750     if (size == 0) return 0;
    02751     return fread(data, 1, size, file);
    02752 }
    02753
    02754 static int MyReadFileAndCheck(FILE *file, void *data, size_t size)
    02755         /*@globals fileSystem @*/
    02756         /*@modifies *file, *data, fileSystem @*/
    02757 {
    02758     return (MyReadFile(file, data, size) == size);
    02759 }
    02760
    02761 static int LzmaReadCompressed(void *object, const unsigned char **buffer, SizeT *size)
    02762         /*@globals fileSystem @*/
    02763         /*@modifies *buffer, *size, fileSystem @*/
    02764 {
    02765     CBuffer *b = object;
    02766     *buffer = b->Buffer;
    02767     *size = MyReadFile(b->File, b->Buffer, kInBufferSize);
    02768     return LZMA_RESULT_OK;
    02769 }
    02770
    02771 static inline /*@dependent@*/ void * lzdFileno(FD_t fd)
    02772         /*@*/
    02773 {
    02774     void * rc = NULL;
    02775     int i;
    02776
    02777     FDSANE(fd);
    02778     for (i = fd->nfps; i >= 0; i--) {
    02779             FDSTACK_t * fps = &fd->fps[i];
    02780             if (fps->io != lzdio)
    02781                 continue;
    02782             rc = fps->fp;
    02783         break;
    02784     }
    02785
    02786     return rc;
    02787 }
    02788
    02789 /*@-mods@*/     /* XXX hide rpmGlobalMacroContext mods for now. */
    02790 static FD_t lzdWriteOpen(int fdno, int fopen, const char * mode)
    02791         /*@globals fileSystem, internalState @*/
    02792         /*@modifies fileSystem, internalState @*/
    02793 {
    02794     pid_t pid;
    02795     int p[2];
    02796     int xx;
    02797     const char *lzma;
    02798     char l[3];
    02799     const char *level;
    02800
    02801     /* revisit use of LZMA_Alone, when lzdRead supports new LZMA Utils */
    02802     char *env[] = { "LZMA_OPT=--format=alone", NULL };
    02803
    02804     if (isdigit(mode[1])) /* "w9" */
    02805     {
    02806         sprintf(l, "-%c", mode[1]);
    02807         level = l;
    02808     }
    02809     else
    02810         level = NULL;
    02811
    02812     if (fdno < 0) return NULL;
    02813     if (pipe(p) < 0) {
    02814         xx = close(fdno);
    02815         return NULL;
    02816     }
    02817     pid = fork();
    02818     if (pid < 0) {
    02819         xx = close(fdno);
    02820         return NULL;
    02821     }
    02822     if (pid) {
    02823         FD_t fd;
    02824         LZFILE * lzfile = xcalloc(1, sizeof(*lzfile));
    02825
    02826         xx = close(fdno);
    02827         xx = close(p[0]);
    02828         lzfile->pid = pid;
    02829         lzfile->g_InBuffer.File = fdopen(p[1], "wb");
    02830         if (lzfile->g_InBuffer.File == NULL) {
    02831             xx = close(p[1]);
    02832             lzfile = _free(lzfile);
    02833             return NULL;
    02834         }
    02835         fd = fdNew("open (lzdOpen write)");
    02836         if (fopen) fdPop(fd);
    02837         fdPush(fd, lzdio, lzfile, -1);
    02838         return fdLink(fd, "lzdOpen");
    02839     } else {
    02840         int i;
    02841         /* lzma */
    02842         xx = close(p[1]);
    02843         xx = dup2(p[0], 0);
    02844         xx = dup2(fdno, 1);
    02845         for (i = 3; i < 1024; i++)
    02846             xx = close(i);
    02847         lzma = rpmGetPath("%{?__lzma}%{!?__lzma:/usr/bin/lzma}", NULL);
    02848         if (execle(lzma, "lzma", level, NULL, env))
    02849             _exit(1);
    02850         lzma = _free(lzma);
    02851     }
    02852     return NULL; /* warning */
    02853 }
    02854 /*@=mods@*/
    02855
    02856 static FD_t lzdReadOpen(int fdno, int fopen)
    02857         /*@globals fileSystem @*/
    02858         /*@modifies fileSystem @*/
    02859 {
    02860     LZFILE *lzfile;
    02861     unsigned char ff[8];
    02862     FD_t fd;
    02863     size_t nb;
    02864
    02865     if (fdno < 0) return NULL;
    02866     lzfile = xcalloc(1, sizeof(*lzfile));
    02867     if (lzfile == NULL) return NULL;
    02868     lzfile->g_InBuffer.File = fdopen(fdno, "rb");
    02869     if (lzfile->g_InBuffer.File == NULL) goto error2;
    02870
    02871     if (!MyReadFileAndCheck(lzfile->g_InBuffer.File, lzfile->properties, sizeof(lzfile->properties)))
    02872             goto error;
    02873
    02874     memset(ff, 0, sizeof(ff));
    02875     if (!MyReadFileAndCheck(lzfile->g_InBuffer.File, ff, 8)) goto error;
    02876     if (LzmaDecodeProperties(&lzfile->state.Properties, lzfile->properties, LZMA_PROPERTIES_SIZE) != LZMA_RESULT_OK)
    02877         goto error;
    02878     nb = LzmaGetNumProbs(&lzfile->state.Properties) * sizeof(*lzfile->state.Probs);
    02879     lzfile->state.Probs = xmalloc(nb);
    02880     if (lzfile->state.Probs == NULL) goto error;
    02881
    02882     if (lzfile->state.Properties.DictionarySize == 0)
    02883         lzfile->state.Dictionary = 0;
    02884     else {
    02885         lzfile->state.Dictionary = xmalloc(lzfile->state.Properties.DictionarySize);
    02886         if (lzfile->state.Dictionary == NULL) {
    02887             lzfile->state.Probs = _free(lzfile->state.Probs);
    02888             goto error;
    02889         }
    02890     }
    02891     lzfile->g_InBuffer.InCallback.Read = LzmaReadCompressed;
    02892     LzmaDecoderInit(&lzfile->state);
    02893
    02894     fd = fdNew("open (lzdOpen read)");
    02895     if (fopen) fdPop(fd);
    02896     fdPush(fd, lzdio, lzfile, -1);
    02897     return fdLink(fd, "lzdOpen");
    02898
    02899 error:
    02900     (void) fclose(lzfile->g_InBuffer.File);
    02901 error2:
    02902     lzfile = _free(lzfile);
    02903     return NULL;
    02904 }
    02905
    02906 /*@-globuse@*/
    02907 static /*@null@*/ FD_t lzdOpen(const char * path, const char * mode)
    02908         /*@globals fileSystem, internalState @*/
    02909         /*@modifies fileSystem, internalState @*/
    02910 {
    02911     if (mode == NULL)
    02912         return NULL;
    02913     if (mode[0] == 'w') {
    02914         int fdno = open(path, O_WRONLY);
    02915
    02916         if (fdno < 0) return NULL;
    02917         return lzdWriteOpen(fdno, 1, mode);
    02918     } else {
    02919         int fdno = open(path, O_RDONLY);
    02920
    02921         if (fdno < 0) return NULL;
    02922         return lzdReadOpen(fdno, 1);
    02923     }
    02924 }
    02925 /*@=globuse@*/
    02926
    02927 /*@-globuse@*/
    02928 static /*@null@*/ FD_t lzdFdopen(void * cookie, const char * fmode)
    02929         /*@globals fileSystem, internalState @*/
    02930         /*@modifies fileSystem, internalState @*/
    02931 {
    02932     FD_t fd = c2f(cookie);
    02933     int fdno;
    02934
    02935     if (fmode == NULL) return NULL;
    02936     fdno = fdFileno(fd);
    02937     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
    02938     if (fdno < 0) return NULL;
    02939     if (fmode[0] == 'w') {
    02940         return lzdWriteOpen(fdno, 0, fmode);
    02941     } else {
    02942         return lzdReadOpen(fdno, 0);
    02943     }
    02944 }
    02945 /*@=globuse@*/
    02946
    02947 /*@-globuse@*/
    02948 static int lzdFlush(FD_t fd)
    02949         /*@globals fileSystem @*/
    02950         /*@modifies fileSystem @*/
    02951 {
    02952     LZFILE *lzfile = lzdFileno(fd);
    02953
    02954     if (lzfile == NULL || lzfile->g_InBuffer.File == NULL) return -2;
    02955     return fflush(lzfile->g_InBuffer.File);
    02956 }
    02957 /*@=globuse@*/
    02958
    02959 /* =============================================================== */
    02960 /*@-globuse@*/
    02961 /*@-mustmod@*/          /* LCL: *buf is modified */
    02962 static ssize_t lzdRead(void * cookie, /*@out@*/ char * buf, size_t count)
    02963         /*@globals fileSystem, internalState @*/
    02964         /*@modifies buf, fileSystem, internalState @*/
    02965 {
    02966     FD_t fd = c2f(cookie);
    02967     LZFILE *lzfile;
    02968     ssize_t rc = 0;
    02969     size_t out;
    02970     int res = 0;
    02971
    02972     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
    02973     lzfile = lzdFileno(fd);
    02974     fdstat_enter(fd, FDSTAT_READ);
    02975     if (lzfile->g_InBuffer.File) {
    02976 /*@-compdef@*/
    02977         res = LzmaDecode(&lzfile->state, &lzfile->g_InBuffer.InCallback, (unsigned char *)buf, count, &out);
    02978         rc = (ssize_t)out;
    02979     }
    02980 /*@=compdef@*/
    02981     if (res) {
    02982         if (lzfile)
    02983             fd->errcookie = "Lzma: decoding error";
    02984     } else if (rc >= 0) {
    02985         fdstat_exit(fd, FDSTAT_READ, rc);
    02986         /*@-compdef@*/
    02987         if (fd->ndigests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
    02988         /*@=compdef@*/
    02989     }
    02990     return rc;
    02991 }
    02992 /*@=mustmod@*/
    02993 /*@=globuse@*/
    02994
    02995 /*@-globuse@*/
    02996 static ssize_t lzdWrite(void * cookie, const char * buf, size_t count)
    02997         /*@globals fileSystem, internalState @*/
    02998         /*@modifies fileSystem, internalState @*/
    02999 {
    03000     FD_t fd = c2f(cookie);
    03001     LZFILE *lzfile;
    03002     ssize_t rc;
    03003
    03004     if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
    03005
    03006     if (fd->ndigests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
    03007
    03008     lzfile = lzdFileno(fd);
    03009     fdstat_enter(fd, FDSTAT_WRITE);
    03010     rc = fwrite((void *)buf, 1, count, lzfile->g_InBuffer.File);
    03011     if (rc == -1) {
    03012         fd->errcookie = strerror(ferror(lzfile->g_InBuffer.File));
    03013     } else if (rc > 0) {
    03014         fdstat_exit(fd, FDSTAT_WRITE, rc);
    03015     }
    03016     return rc;
    03017 }
    03018 /*@=globuse@*/
    03019
    03020 static inline int lzdSeek(void * cookie, /*@unused@*/ _libio_pos_t pos,
    03021                         /*@unused@*/ int whence)
    03022         /*@*/
    03023 {
    03024     FD_t fd = c2f(cookie);
    03025
    03026     LZDONLY(fd);
    03027     return -2;
    03028 }
    03029
    03030 static int lzdClose( /*@only@*/ void * cookie)
    03031         /*@globals fileSystem, internalState @*/
    03032         /*@modifies fileSystem, internalState @*/
    03033 {
    03034     FD_t fd = c2f(cookie);
    03035     LZFILE * lzfile = lzdFileno(fd);
    03036     int rc;
    03037
    03038     if (lzfile == NULL) return -2;
    03039     fdstat_enter(fd, FDSTAT_CLOSE);
    03040 /*@-noeffectuncon@*/ /* FIX: check rc */
    03041     rc = fclose(lzfile->g_InBuffer.File);
    03042     if (lzfile->pid)
    03043         rc = (int) wait4(lzfile->pid, NULL, 0, NULL);
    03044     else { /* reading */
    03045         lzfile->state.Probs = _free(lzfile->state.Probs);
    03046         lzfile->state.Dictionary = _free(lzfile->state.Dictionary);
    03047     }
    03048 /*@-dependenttrans@*/
    03049     lzfile = _free(lzfile);
    03050 /*@=dependenttrans@*/
    03051 /*@=noeffectuncon@*/
    03052     rc = 0;     /* XXX FIXME */
    03053
    03054     /* XXX TODO: preserve fd if errors */
    03055
    03056     if (fd) {
    03057         if (rc == -1) {
    03058 assert(lzfile != NULL); /* XXX FIXME, lzfile is always NULL here. */
    03059             fd->errcookie = strerror(ferror(lzfile->g_InBuffer.File));
    03060         } else if (rc >= 0) {
    03061             fdstat_exit(fd, FDSTAT_CLOSE, rc);
    03062         }
    03063     }
    03064
    03065 DBGIO(fd, (stderr, "==>\tlzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
    03066
    03067     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "LZDIO", stderr);
    03068     if (rc == 0)
    03069         fd = fdFree(fd, "open (lzdClose)");
    03070     return rc;
    03071 }
    03072
    03073 /*@-type@*/ /* LCL: function typedefs */
    03074 static struct FDIO_s lzdio_s = {
    03075   lzdRead, lzdWrite, lzdSeek, lzdClose, XfdLink, XfdFree, XfdNew, fdFileno,
    03076   NULL, lzdOpen, lzdFileno, lzdFlush,   NULL, NULL, NULL, NULL, NULL
    03077 };
    03078 /*@=type@*/
    03079 FDIO_t lzdio = /*@-compmempass@*/ &lzdio_s /*@=compmempass@*/ ;
    03080
    03081 /* =============================================================== */
    03082 /*@observer@*/
    03083 static const char * getFdErrstr (FD_t fd)
    03084         /*@*/
    03085 {
    03086     const char *errstr = NULL;
    03087
    03088 #ifdef  HAVE_ZLIB_H
    03089     if (fdGetIo(fd) == gzdio) {
    03090         errstr = fd->errcookie;
    03091     } else
    03092 #endif  /* HAVE_ZLIB_H */
    03093
    03094 #ifdef  HAVE_BZLIB_H
    03095     if (fdGetIo(fd) == bzdio) {
    03096         errstr = fd->errcookie;
    03097     } else
    03098 #endif  /* HAVE_BZLIB_H */
    03099     if (fdGetIo(fd) == lzdio) {
    03100     errstr = fd->errcookie;
    03101     } else
    03102     {
    03103         errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
    03104     }
    03105
    03106     return errstr;
    03107 }
    03108
    03109 /* =============================================================== */
    03110
    03111 const char *Fstrerror(FD_t fd)
    03112 {
    03113     if (fd == NULL)
    03114         return (errno ? strerror(errno) : "");
    03115     FDSANE(fd);
    03116     return getFdErrstr(fd);
    03117 }
    03118
    03119 #define FDIOVEC(_fd, _vec)      \
    03120   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
    03121
    03122 size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
    03123     fdio_read_function_t _read;
    03124     int rc;
    03125
    03126     FDSANE(fd);
    03127 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
    03128
    03129     if (fdGetIo(fd) == fpio) {
    03130         /*@+voidabstract -nullpass@*/
    03131         rc = (int) fread(buf, size, nmemb, fdGetFILE(fd));
    03132         /*@=voidabstract =nullpass@*/
    03133         return (size_t) rc;
    03134     }
    03135
    03136     /*@-nullderef@*/
    03137     _read = FDIOVEC(fd, read);
    03138     /*@=nullderef@*/
    03139
    03140     rc = (int) (_read ? (*_read) (fd, buf, size * nmemb) : -2);
    03141     return (size_t) rc;
    03142 }
    03143
    03144 size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
    03145 {
    03146     fdio_write_function_t _write;
    03147     int rc;
    03148
    03149     FDSANE(fd);
    03150 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
    03151
    03152     if (fdGetIo(fd) == fpio) {
    03153         /*@+voidabstract -nullpass@*/
    03154         rc = (int) fwrite(buf, size, nmemb, fdGetFILE(fd));
    03155         /*@=voidabstract =nullpass@*/
    03156         return (size_t) rc;
    03157     }
    03158
    03159     /*@-nullderef@*/
    03160     _write = FDIOVEC(fd, write);
    03161     /*@=nullderef@*/
    03162
    03163     rc = (int) (_write ? _write(fd, buf, size * nmemb) : -2);
    03164     return (size_t) rc;
    03165 }
    03166
    03167 int Fseek(FD_t fd, _libio_off_t offset, int whence) {
    03168     fdio_seek_function_t _seek;
    03169 #ifdef USE_COOKIE_SEEK_POINTER
    03170     _IO_off64_t o64 = offset;
    03171     _libio_pos_t pos = &o64;
    03172 #else
    03173     _libio_pos_t pos = offset;
    03174 #endif
    03175
    03176     long int rc;
    03177
    03178     FDSANE(fd);
    03179 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
    03180
    03181     if (fdGetIo(fd) == fpio) {
    03182         FILE *fp;
    03183
    03184         /*@+voidabstract -nullpass@*/
    03185         fp = fdGetFILE(fd);
    03186         rc = fseek(fp, (long)offset, whence);
    03187         /*@=voidabstract =nullpass@*/
    03188         return rc;
    03189     }
    03190
    03191     /*@-nullderef@*/
    03192     _seek = FDIOVEC(fd, seek);
    03193     /*@=nullderef@*/
    03194
    03195     rc = (_seek ? _seek(fd, pos, whence) : -2);
    03196     return rc;
    03197 }
    03198
    03199 int Fclose(FD_t fd)
    03200 {
    03201     int rc = 0, ec = 0;
    03202
    03203     FDSANE(fd);
    03204 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
    03205
    03206     fd = fdLink(fd, "Fclose");
    03207     while (fd->nfps >= 0) {
    03208         FDSTACK_t * fps = &fd->fps[fd->nfps];
    03209
    03210         if (fps->io == fpio) {
    03211             FILE *fp;
    03212             int fpno;
    03213
    03214             /*@+voidabstract -nullpass@*/
    03215             fp = fdGetFILE(fd);
    03216             fpno = fileno(fp);
    03217             /*@=voidabstract =nullpass@*/
    03218         /* XXX persistent HTTP/1.1 returns the previously opened fp */
    03219             if (fd->nfps > 0 && fpno == -1 &&
    03220                 fd->fps[fd->nfps-1].io == ufdio &&
    03221                 fd->fps[fd->nfps-1].fp == fp &&
    03222                 (fd->fps[fd->nfps-1].fdno >= 0 || fd->req != NULL))
    03223             {
    03224                 int hadreqpersist = (fd->req != NULL);
    03225
    03226                 if (fp)
    03227                     rc = fflush(fp);
    03228                 fd->nfps--;
    03229                 /*@-refcounttrans@*/
    03230                 rc = ufdClose(fd);
    03231                 /*@=refcounttrans@*/
    03232 /*@-usereleased@*/
    03233                 if (fdGetFdno(fd) >= 0)
    03234                     break;
    03235                 if (!fd->persist)
    03236                     hadreqpersist = 0;
    03237                 fdSetFp(fd, NULL);
    03238                 fd->nfps++;
    03239                 if (fp) {
    03240                     /* HACK: flimsy Keepalive wiring. */
    03241                     if (hadreqpersist) {
    03242                         fd->nfps--;
    03243 /*@-exposetrans@*/
    03244                         fdSetFp(fd, fp);
    03245 /*@=exposetrans@*/
    03246 /*@-refcounttrans@*/
    03247                         (void) fdClose(fd);
    03248 /*@=refcounttrans@*/
    03249                         fdSetFp(fd, NULL);
    03250                         fd->nfps++;
    03251 /*@-refcounttrans@*/
    03252                         (void) fdClose(fd);
    03253 /*@=refcounttrans@*/
    03254                     } else
    03255                         rc = fclose(fp);
    03256                 }
    03257                 fdPop(fd);
    03258                 if (noLibio)
    03259                     fdSetFp(fd, NULL);
    03260             } else {
    03261                 if (fp)
    03262                     rc = fclose(fp);
    03263                 if (fpno == -1) {
    03264                     fd = fdFree(fd, "fopencookie (Fclose)");
    03265                     fdPop(fd);
    03266                 }
    03267             }
    03268         } else {
    03269             /*@-nullderef@*/
    03270             fdio_close_function_t _close = FDIOVEC(fd, close);
    03271             /*@=nullderef@*/
    03272             rc = _close(fd);
    03273         }
    03274         if (fd->nfps == 0)
    03275             break;
    03276         if (ec == 0 && rc)
    03277             ec = rc;
    03278         fdPop(fd);
    03279     }
    03280     fd = fdFree(fd, "Fclose");
    03281     return ec;
    03282 /*@=usereleased@*/
    03283 }
    03284
    03300 static inline void cvtfmode (const char *m,
    03301                                 /*@out@*/ char *stdio, size_t nstdio,
    03302                                 /*@out@*/ char *other, size_t nother,
    03303                                 /*@out@*/ const char **end, /*@out@*/ int * f)
    03304         /*@modifies *stdio, *other, *end, *f @*/
    03305 {
    03306     int flags = 0;
    03307     char c;
    03308
    03309     switch (*m) {
    03310     case 'a':
    03311         flags |= O_WRONLY | O_CREAT | O_APPEND;
    03312         if (--nstdio > 0) *stdio++ = *m;
    03313         break;
    03314     case 'w':
    03315         flags |= O_WRONLY | O_CREAT | O_TRUNC;
    03316         if (--nstdio > 0) *stdio++ = *m;
    03317         break;
    03318     case 'r':
    03319         flags |= O_RDONLY;
    03320         if (--nstdio > 0) *stdio++ = *m;
    03321         break;
    03322     default:
    03323         *stdio = '\0';
    03324         return;
    03325         /*@notreached@*/ break;
    03326     }
    03327     m++;
    03328
    03329     while ((c = *m++) != '\0') {
    03330         switch (c) {
    03331         case '.':
    03332             /*@switchbreak@*/ break;
    03333         case '+':
    03334             flags &= ~(O_RDONLY|O_WRONLY);
    03335             flags |= O_RDWR;
    03336             if (--nstdio > 0) *stdio++ = c;
    03337             continue;
    03338             /*@notreached@*/ /*@switchbreak@*/ break;
    03339         case 'x':       /* glibc: open file exclusively. */
    03340             flags |= O_EXCL;
    03341             /*@fallthrough@*/
    03342         case 'm':       /* glibc: mmap'd reads */
    03343         case 'c':       /* glibc: no cancel */
    03344 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3
    03345             if (--nstdio > 0) *stdio++ = c;
    03346 #endif
    03347             continue;
    03348             /*@notreached@*/ /*@switchbreak@*/ break;
    03349         case 'b':
    03350             if (--nstdio > 0) *stdio++ = c;
    03351             continue;
    03352             /*@notreached@*/ /*@switchbreak@*/ break;
    03353         default:
    03354             if (--nother > 0) *other++ = c;
    03355             continue;
    03356             /*@notreached@*/ /*@switchbreak@*/ break;
    03357         }
    03358         break;
    03359     }
    03360
    03361     *stdio = *other = '\0';
    03362     if (end != NULL)
    03363         *end = (*m != '\0' ? m : NULL);
    03364     if (f != NULL)
    03365         *f = flags;
    03366 }
    03367
    03368 #if _USE_LIBIO
    03369 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
    03370 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
    03371 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
    03372 #endif
    03373 #endif
    03374
    03375 FD_t Fdopen(FD_t ofd, const char *fmode)
    03376 {
    03377     char stdio[20], other[20], zstdio[20];
    03378     const char *end = NULL;
    03379     FDIO_t iof = NULL;
    03380     FD_t fd = ofd;
    03381
    03382 if (_rpmio_debug)
    03383 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
    03384     FDSANE(fd);
    03385
    03386     if (fmode == NULL)
    03387         return NULL;
    03388
    03389     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
    03390     if (stdio[0] == '\0')
    03391         return NULL;
    03392     zstdio[0] = '\0';
    03393     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
    03394     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
    03395
    03396     if (end == NULL && other[0] == '\0')
    03397         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
    03398
    03399     if (end && *end) {
    03400         if (!strcmp(end, "fdio")) {
    03401             iof = fdio;
    03402 #if defined(HAVE_ZLIB_H)
    03403         } else if (!strcmp(end, "gzdio")) {
    03404             iof = gzdio;
    03405             /*@-internalglobs@*/
    03406             fd = gzdFdopen(fd, zstdio);
    03407             /*@=internalglobs@*/
    03408 #endif
    03409 #if defined(HAVE_BZLIB_H)
    03410         } else if (!strcmp(end, "bzdio")) {
    03411             iof = bzdio;
    03412             /*@-internalglobs@*/
    03413             fd = bzdFdopen(fd, zstdio);
    03414             /*@=internalglobs@*/
    03415 #endif
    03416     } else if (!strcmp(end, "lzdio")) {
    03417         iof = lzdio;
    03418         fd = lzdFdopen(fd, zstdio);
    03419         } else if (!strcmp(end, "ufdio")) {
    03420             iof = ufdio;
    03421         } else if (!strcmp(end, "fpio")) {
    03422             iof = fpio;
    03423             if (noLibio) {
    03424                 int fdno = Fileno(fd);
    03425                 FILE * fp = fdopen(fdno, stdio);
    03426 /*@+voidabstract -nullpass@*/
    03427 if (_rpmio_debug)
    03428 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
    03429 /*@=voidabstract =nullpass@*/
    03430                 if (fp == NULL)
    03431                     return NULL;
    03432                 /* XXX gzdio/bzdio use fp for private data */
    03433                 /*@+voidabstract@*/
    03434                 if (fdGetFp(fd) == NULL)
    03435                     fdSetFp(fd, fp);
    03436                 fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
    03437                 /*@=voidabstract@*/
    03438             }
    03439         }
    03440     } else if (other[0] != '\0') {
    03441         for (end = other; *end && strchr("0123456789fh", *end); end++)
    03442             {};
    03443         if (*end == '\0') {
    03444 #if defined(HAVE_ZLIB_H)
    03445             iof = gzdio;
    03446             /*@-internalglobs@*/
    03447             fd = gzdFdopen(fd, zstdio);
    03448             /*@=internalglobs@*/
    03449 #endif
    03450         }
    03451     }
    03452     if (iof == NULL)
    03453         /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
    03454
    03455     if (!noLibio) {
    03456         FILE * fp = NULL;
    03457
    03458 #if _USE_LIBIO
    03459         {   cookie_io_functions_t ciof;
    03460             ciof.read = iof->read;
    03461             ciof.write = iof->write;
    03462             ciof.seek = iof->seek;
    03463             ciof.close = iof->close;
    03464             fp = fopencookie(fd, stdio, ciof);
    03465 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
    03466         }
    03467 #endif
    03468
    03469         if (fp) {
    03470             /* XXX gzdio/bzdio use fp for private data */
    03471             /*@+voidabstract -nullpass@*/
    03472             if (fdGetFp(fd) == NULL)
    03473                 fdSetFp(fd, fp);
    03474             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
    03475             /*@=voidabstract =nullpass@*/
    03476             fd = fdLink(fd, "fopencookie");
    03477         }
    03478     }
    03479
    03480 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
    03481     /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
    03482 }
    03483
    03484 FD_t Fopen(const char *path, const char *fmode)
    03485 {
    03486     char stdio[20], other[20];
    03487     const char *end = NULL;
    03488     mode_t perms = 0666;
    03489     int flags = 0;
    03490     FD_t fd;
    03491
    03492     if (path == NULL || fmode == NULL)
    03493         return NULL;
    03494
    03495     stdio[0] = '\0';
    03496     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
    03497     if (stdio[0] == '\0')
    03498         return NULL;
    03499
    03500     if (end == NULL || !strcmp(end, "fdio")) {
    03501 if (_rpmio_debug)
    03502 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
    03503         fd = fdOpen(path, flags, perms);
    03504         if (fdFileno(fd) < 0) {
    03505             if (fd) (void) fdClose(fd);
    03506             return NULL;
    03507         }
    03508     } else {
    03509         FILE *fp;
    03510         int fdno;
    03511         int isHTTP = 0;
    03512
    03513         /* XXX gzdio and bzdio here too */
    03514
    03515         switch (urlIsURL(path)) {
    03516         case URL_IS_HTTPS:
    03517         case URL_IS_HTTP:
    03518         case URL_IS_HKP:
    03519             isHTTP = 1;
    03520             /*@fallthrough@*/
    03521         case URL_IS_PATH:
    03522         case URL_IS_DASH:
    03523         case URL_IS_FTP:
    03524         case URL_IS_UNKNOWN:
    03525 if (_rpmio_debug)
    03526 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
    03527             fd = ufdOpen(path, flags, perms);
    03528             if (fd == NULL || !(fdFileno(fd) >= 0 || fd->req != NULL))
    03529                 return fd;
    03530             break;
    03531         default:
    03532 if (_rpmio_debug)
    03533 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
    03534             return NULL;
    03535             /*@notreached@*/ break;
    03536         }
    03537
    03538         /* XXX persistent HTTP/1.1 returns the previously opened fp */
    03539         if (isHTTP && ((fp = fdGetFp(fd)) != NULL) && ((fdno = fdGetFdno(fd)) >= 0 || fd->req != NULL))
    03540         {
    03541             /*@+voidabstract@*/
    03542             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
    03543             /*@=voidabstract@*/
    03544             return fd;
    03545         }
    03546     }
    03547
    03548     if (fd)
    03549         fd = Fdopen(fd, fmode);
    03550     return fd;
    03551 }
    03552
    03553 int Fflush(FD_t fd)
    03554 {
    03555     void * vh;
    03556     if (fd == NULL) return -1;
    03557     if (fdGetIo(fd) == fpio)
    03558         /*@+voidabstract -nullpass@*/
    03559         return fflush(fdGetFILE(fd));
    03560         /*@=voidabstract =nullpass@*/
    03561
    03562     vh = fdGetFp(fd);
    03563 #if defined(HAVE_ZLIB_H)
    03564     if (vh && fdGetIo(fd) == gzdio)
    03565         return gzdFlush(vh);
    03566 #endif
    03567 #if defined(HAVE_BZLIB_H)
    03568     if (vh && fdGetIo(fd) == bzdio)
    03569         return bzdFlush(vh);
    03570 #endif
    03571
    03572     return 0;
    03573 }
    03574
    03575 int Ferror(FD_t fd)
    03576 {
    03577     int i, rc = 0;
    03578
    03579     if (fd == NULL) return -1;
    03580     if (fd->req != NULL) {
    03581         /* HACK: flimsy wiring for neon errors. */
    03582         rc = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
    03583     } else
    03584     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
    03585         FDSTACK_t * fps = &fd->fps[i];
    03586         int ec;
    03587
    03588         if (fps->io == fpio) {
    03589             /*@+voidabstract -nullpass@*/
    03590             ec = ferror(fdGetFILE(fd));
    03591             /*@=voidabstract =nullpass@*/
    03592 #if defined(HAVE_ZLIB_H)
    03593         } else if (fps->io == gzdio) {
    03594             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
    03595             i--;        /* XXX fdio under gzdio always has fdno == -1 */
    03596 #endif
    03597 #if defined(HAVE_BZLIB_H)
    03598         } else if (fps->io == bzdio) {
    03599             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
    03600             i--;        /* XXX fdio under bzdio always has fdno == -1 */
    03601 #endif
    03602     } else if (fps->io == lzdio) {
    03603             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
    03604             i--;        /* XXX fdio under lzdio always has fdno == -1 */
    03605         } else {
    03606         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
    03607             ec = (fdFileno(fd) < 0 ? -1 : 0);
    03608         }
    03609
    03610         if (rc == 0 && ec)
    03611             rc = ec;
    03612     }
    03613 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
    03614     return rc;
    03615 }
    03616
    03617 int Fileno(FD_t fd)
    03618 {
    03619     int i, rc = -1;
    03620
    03621     if (fd == NULL)
    03622         return -1;
    03623     if (fd->req != NULL)
    03624         rc = 123456789; /* HACK: https has no steenkin fileno. */
    03625     else
    03626     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
    03627         rc = fd->fps[i].fdno;
    03628     }
    03629
    03630 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
    03631     return rc;
    03632 }
    03633
    03634 /* XXX this is naive */
    03635 int Fcntl(FD_t fd, int op, void *lip)
    03636 {
    03637     return fcntl(Fileno(fd), op, lip);
    03638 }
    03639
    03640 /* =============================================================== */
    03641 /* Helper routines that may be generally useful.
    03642  */
    03643 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
    03644 {
    03645     char * d, * de;
    03646     int created = 0;
    03647     int rc;
    03648
    03649     if (path == NULL)
    03650         return -1;
    03651     d = alloca(strlen(path)+2);
    03652     de = stpcpy(d, path);
    03653     de[1] = '\0';
    03654     for (de = d; *de != '\0'; de++) {
    03655         struct stat st;
    03656         char savec;
    03657
    03658         while (*de && *de != '/') de++;
    03659         savec = de[1];
    03660         de[1] = '\0';
    03661
    03662         rc = Stat(d, &st);
    03663         if (rc) {
    03664             switch(errno) {
    03665             default:
    03666                 return errno;
    03667                 /*@notreached@*/ /*@switchbreak@*/ break;
    03668             case ENOENT:
    03669                 /*@switchbreak@*/ break;
    03670             }
    03671             rc = Mkdir(d, mode);
    03672             if (rc)
    03673                 return errno;
    03674             created = 1;
    03675             if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
    03676                 rc = Chown(d, uid, gid);
    03677                 if (rc)
    03678                     return errno;
    03679             }
    03680         } else if (!S_ISDIR(st.st_mode)) {
    03681             return ENOTDIR;
    03682         }
    03683         de[1] = savec;
    03684     }
    03685     rc = 0;
    03686     if (created)
    03687         rpmlog(RPMLOG_DEBUG, D_("created directory(s) %s mode 0%o\n"),
    03688                         path, (unsigned)mode);
    03689     return rc;
    03690 }
    03691
    03692 #define _PATH   "/bin:/usr/bin:/sbin:/usr/sbin"
    03693 /*@unchecked@*/ /*@observer@*/
    03694 static const char *_path = _PATH;
    03695
    03696 #define alloca_strdup(_s)       strcpy(alloca(strlen(_s)+1), (_s))
    03697
    03698 int rpmioAccess(const char * FN, const char * path, int mode)
    03699 {
    03700     char fn[4096];
    03701     char * bn;
    03702     char * r, * re;
    03703     char * t, * te;
    03704     int negate = 0;
    03705     int rc = 0;
    03706
    03707     /* Empty paths are always accessible. */
    03708     if (FN == NULL || *FN == '\0')
    03709         return 0;
    03710
    03711     if (mode == 0)
    03712         mode = X_OK;
    03713
    03714     /* Strip filename out of its name space wrapper. */
    03715     bn = alloca_strdup(FN);
    03716     for (t = bn; t && *t; t++) {
    03717         if (*t != '(')
    03718             continue;
    03719         *t++ = '\0';
    03720
    03721         /* Permit negation on name space tests. */
    03722         if (*bn == '!') {
    03723             negate = 1;
    03724             bn++;
    03725         }
    03726
    03727         /* Set access flags from name space marker. */
    03728         if (strlen(bn) == 3
    03729          && strchr("Rr_", bn[0]) != NULL
    03730          && strchr("Ww_", bn[1]) != NULL
    03731          && strchr("Xx_", bn[2]) != NULL) {
    03732             mode = 0;
    03733             if (strchr("Rr", bn[0]) != NULL)
    03734                 mode |= R_OK;
    03735             if (strchr("Ww", bn[1]) != NULL)
    03736                 mode |= W_OK;
    03737             if (strchr("Xx", bn[2]) != NULL)
    03738                 mode |= X_OK;
    03739             if (mode == 0)
    03740                 mode = F_OK;
    03741         } else if (!strcmp(bn, "exists"))
    03742             mode = F_OK;
    03743         else if (!strcmp(bn, "executable"))
    03744             mode = X_OK;
    03745         else if (!strcmp(bn, "readable"))
    03746             mode = R_OK;
    03747         else if (!strcmp(bn, "writable"))
    03748             mode = W_OK;
    03749
    03750         bn = t;
    03751         te = bn + strlen(t) - 1;
    03752         if (*te != ')')         /* XXX syntax error, never exists */
    03753             return 1;
    03754         *te = '\0';
    03755         break;
    03756     }
    03757
    03758     /* Empty paths are always accessible. */
    03759     if (*bn == '\0')
    03760         goto exit;
    03761
    03762     /* Check absolute path for access. */
    03763     if (*bn == '/') {
    03764         rc = (Access(bn, mode) != 0 ? 1 : 0);
    03765 if (_rpmio_debug)
    03766 fprintf(stderr, "*** rpmioAccess(\"%s\", 0x%x) rc %d\n", bn, mode, rc);
    03767         goto exit;
    03768     }
    03769
    03770     /* Find path to search. */
    03771     if (path == NULL)
    03772         path = getenv("PATH");
    03773     if (path == NULL)
    03774         path = _path;
    03775     if (path == NULL) {
    03776         rc = 1;
    03777         goto exit;
    03778     }
    03779
    03780     /* Look for relative basename on PATH. */
    03781     for (r = alloca_strdup(path); r != NULL && *r != '\0'; r = re) {
    03782
    03783         /* Find next element, terminate current element. */
    03784         for (re = r; (re = strchr(re, ':')) != NULL; re++) {
    03785             if (!(re[1] == '/' && re[2] == '/'))
    03786                 /*@innerbreak@*/ break;
    03787         }
    03788         if (re && *re == ':')
    03789             *re++ = '\0';
    03790         else
    03791             re = r + strlen(r);
    03792
    03793         /* Expand ~/ to $HOME/ */
    03794         fn[0] = '\0';
    03795         t = fn;
    03796         *t = '\0';      /* XXX redundant. */
    03797         if (r[0] == '~' && r[1] == '/') {
    03798             const char * home = getenv("HOME");
    03799             if (home == NULL)   /* XXX No HOME? */
    03800                 continue;
    03801             if (strlen(home) > (sizeof(fn) - strlen(r))) /* XXX too big */
    03802                 continue;
    03803             t = stpcpy(t, home);
    03804             r++;        /* skip ~ */
    03805         }
    03806         t = stpcpy(t, r);
    03807         if (t[-1] != '/' && *bn != '/')
    03808             *t++ = '/';
    03809         t = stpcpy(t, bn);
    03810         t = rpmCleanPath(fn);
    03811         if (t == NULL)  /* XXX can't happen */
    03812             continue;
    03813
    03814         /* Check absolute path for access. */
    03815         rc = (Access(t, mode) != 0 ? 1 : 0);
    03816 if (_rpmio_debug)
    03817 fprintf(stderr, "*** rpmioAccess(\"%s\", 0x%x) rc %d\n", t, mode, rc);
    03818         if (rc == 0)
    03819             goto exit;
    03820     }
    03821
    03822     rc = 1;
    03823
    03824 exit:
    03825     if (negate)
    03826         rc ^= 1;
    03827     return rc;
    03828 }
    03829
    03830 int rpmioSlurp(const char * fn, uint8_t ** bp, ssize_t * blenp)
    03831 {
    03832     static ssize_t blenmax = (32 * BUFSIZ);
    03833     ssize_t blen = 0;
    03834     uint8_t * b = NULL;
    03835     ssize_t size;
    03836     FD_t fd;
    03837     int rc = 0;
    03838
    03839     fd = Fopen(fn, "r%{?_rpmgio}");
    03840     if (fd == NULL || Ferror(fd)) {
    03841         rc = 2;
    03842         goto exit;
    03843     }
    03844
    03845     size = fdSize(fd);
    03846     blen = (size >= 0 ? size : blenmax);
    03847     if (blen) {
    03848         size_t nb;
    03849         b = xmalloc(blen+1);
    03850         b[0] = (uint8_t) '\0';
    03851         nb = Fread(b, sizeof(*b), blen, fd);
    03852         if (Ferror(fd) || (size > 0 && nb != blen)) {
    03853             rc = 1;
    03854             goto exit;
    03855         }
    03856         if (blen == blenmax && nb < blen) {
    03857             blen = nb;
    03858             b = xrealloc(b, blen+1);
    03859         }
    03860         b[blen] = (uint8_t) '\0';
    03861     }
    03862
    03863 exit:
    03864     if (fd) (void) Fclose(fd);
    03865
    03866     if (rc) {
    03867         if (b) free(b);
    03868         b = NULL;
    03869         blen = 0;
    03870     }
    03871
    03872     if (bp) *bp = b;
    03873     else if (b) free(b);
    03874
    03875     if (blenp) *blenp = blen;
    03876
    03877     return rc;
    03878 }
    03879
    03880 #if defined(WITH_NSS)
    03881 /*@-exportheader@*/
    03882 extern void NSS_Shutdown(void);
    03883 /*@=exportheader@*/
    03884 #endif
    03885
    03886 void rpmioClean(void)
    03887 {
    03888 #if defined(WITH_LUA)   /* XXX this should be done in a rpmioClean() wrapper. */
    03889     (void) rpmluaFree(NULL);
    03890 #endif
    03891 #if defined(WITH_NEON)
    03892     davDestroy();
    03893 #endif
    03894 #if defined(WITH_NSS)
    03895     (void) NSS_Shutdown();
    03896 #endif
    03897     urlFreeCache();
    03898     rpmlogClose();
    03899 }
    03900
    03901 /*@-type@*/ /* LCL: function typedefs */
    03902 static struct FDIO_s fpio_s = {
    03903   ufdRead, ufdWrite, fdSeek, ufdClose, XfdLink, XfdFree, XfdNew, fdFileno,
    03904   ufdOpen, NULL, fdGetFp, NULL, Mkdir, Chdir, Rmdir, Rename, Unlink
    03905 };
    03906 /*@=type@*/
    03907 FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;