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