rpm  5.4.10
rpmio.c
Go to the documentation of this file.
1 
5 #include "system.h"
6 #include <stdarg.h>
7 
8 #if defined(HAVE_MACHINE_TYPES_H)
9 # include <machine/types.h>
10 #endif
11 
12 #if defined(HAVE_SYS_SOCKET_H)
13 # include <sys/socket.h>
14 #endif
15 
16 #ifndef NI_MAXHOST
17 #define NI_MAXHOST 1025
18 #endif
19 
20 #if defined(__LCLINT__)
21 struct addrinfo
22 {
23  int ai_flags; /* Input flags. */
24  int ai_family; /* Protocol family for socket. */
25  int ai_socktype; /* Socket type. */
26  int ai_protocol; /* Protocol for socket. */
27  socklen_t ai_addrlen; /* Length of socket address. */
28  struct sockaddr *ai_addr; /* Socket address for socket. */
29  char *ai_canonname; /* Canonical name for service location. */
30  struct addrinfo *ai_next; /* Pointer to next in list. */
31 };
32 
33 /*@-exportheader -incondefs @*/
34 extern int getaddrinfo (__const char *__restrict __name,
35  __const char *__restrict __service,
36  __const struct addrinfo *__restrict __req,
37  /*@out@*/ struct addrinfo **__restrict __pai)
38  /*@modifies *__pai @*/;
39 
40 extern int getnameinfo (__const struct sockaddr *__restrict __sa,
41  socklen_t __salen, /*@out@*/ char *__restrict __host,
42  socklen_t __hostlen, /*@out@*/ char *__restrict __serv,
43  socklen_t __servlen, unsigned int __flags)
44  /*@modifies __host, __serv @*/;
45 
46 extern void freeaddrinfo (/*@only@*/ struct addrinfo *__ai)
47  /*@modifies __ai @*/;
48 /*@=exportheader =incondefs @*/
49 #else
50 #include <netdb.h> /* XXX getaddrinfo et al */
51 #endif
52 
53 #include <netinet/in.h>
54 #include <arpa/inet.h> /* XXX for inet_aton and HP-UX */
55 
56 #if defined(HAVE_NETINET_IN_SYSTM_H)
57 # include <sys/types.h>
58 # include <netinet/in_systm.h>
59 #endif
60 
61 #if defined(WITH_XZ)
62 #include <lzma.h>
63 #endif
64 
65 #include <rpmiotypes.h>
66 #include <rpmmacro.h> /* XXX rpmioAccess needs rpmCleanPath() */
67 
68 #include <mongo.h>
69 
70 #include <rpmaug.h>
71 #include <rpmficl.h>
72 #include <rpmgit.h>
73 #include <rpmjs.h>
74 #include <rpmlua.h> /* XXX rpmioClean() calls rpmluaFree() */
75 #include <rpmnix.h>
76 #include <rpmodbc.h>
77 #include <rpmperl.h>
78 #include <rpmpython.h>
79 #include <rpmruby.h>
80 #include <rpmsql.h>
81 #include <rpmsquirrel.h>
82 #include <rpmtcl.h>
83 
84 #define _RPMHKP_INTERNAL /* XXX awol/crl bloom filters */
85 #include <rpmhkp.h>
86 
87 #include <rpmsm.h>
88 #include <rpmsp.h>
89 #include <rpmsx.h>
90 
91 #if defined(HAVE_LIBIO_H) && defined(_G_IO_IO_FILE_VERSION)
92 #define _USE_LIBIO 1
93 #endif
94 
95 /* XXX HP-UX w/o -D_XOPEN_SOURCE needs */
96 #if !defined(HAVE_HERRNO) && (defined(hpux) || defined(__hpux) || defined(__LCLINT__))
97 /*@unchecked@*/
98 extern int h_errno;
99 #endif
100 
101 #ifndef IPPORT_FTP
102 #define IPPORT_FTP 21
103 #endif
104 #ifndef IPPORT_HTTP
105 #define IPPORT_HTTP 80
106 #endif
107 
108 #if !defined(HAVE_INET_ATON)
109 #define inet_aton(cp,inp) rpm_inet_aton(cp,inp)
110 static int rpm_inet_aton(const char *cp, struct in_addr *inp)
111  /*@modifies *inp @*/
112 {
113  long addr;
114 
115  addr = inet_addr(cp);
116  if (addr == ((long) -1)) return 0;
117 
118  memcpy(inp, &addr, sizeof(addr));
119  return 1;
120 }
121 #endif
122 
123 #if defined(USE_ALT_DNS) && USE_ALT_DNS
124 #include "dns.h"
125 #endif
126 
127 #include <rpmio_internal.h>
128 #undef fdFileno
129 #undef fdOpen
130 #define fdOpen __fdOpen
131 #undef fdRead
132 #define fdRead __fdRead
133 #undef fdWrite
134 #define fdWrite __fdWrite
135 #undef fdClose
136 #define fdClose __fdClose
137 
138 #include <ugid.h>
139 #include <rpmcb.h>
140 #include <rpmdav.h>
141 
142 #include "debug.h"
143 
144 /*@access FILE @*/ /* XXX to permit comparison/conversion with void *. */
145 /*@access urlinfo @*/
146 /*@access FDSTAT_t @*/
147 /*@access rpmxar @*/
148 /*@access pgpDig @*/
149 
150 #define FDTO(fd) (fd ? ((FD_t)fd)->rd_timeoutsecs : -99)
151 #define FDCPIOPOS(fd) (fd ? ((FD_t)fd)->fd_cpioPos : -99)
152 
153 #define FDONLY(fd) assert(fdGetIo(fd) == fdio)
154 
155 #define UFDONLY(fd) /* assert(fdGetIo(fd) == ufdio) */
156 
157 #define fdGetFILE(_fd) ((FILE *)fdGetFp(_fd))
158 
161 /*@unchecked@*/
162 #if _USE_LIBIO
163 int noLibio = 0;
164 #else
165 int noLibio = 1;
166 #endif
167 
168 #define TIMEOUT_SECS 60
169 
172 /*@unchecked@*/
174 
177 /*@unchecked@*/
178 int _rpmio_debug = 0;
179 
182 /*@unchecked@*/
183 int _av_debug = 0;
184 
187 /*@unchecked@*/
188 int _ftp_debug = 0;
189 
192 /*@unchecked@*/
193 int _dav_debug = 0;
194 
195 /* =============================================================== */
196 
197 const char * fdbg(FD_t fd)
198 {
199  static char buf[BUFSIZ];
200  char *be = buf;
201  int i;
202 
203  buf[0] = '\0';
204  if (fd == NULL)
205  return buf;
206 
207 #ifdef DYING
208  sprintf(be, "fd %p", fd); be += strlen(be);
209  if (fd->rd_timeoutsecs >= 0) {
210  sprintf(be, " secs %d", fd->rd_timeoutsecs);
211  be += strlen(be);
212  }
213 #endif
214  if (fd->bytesRemain != -1) {
215  sprintf(be, " clen %d", (int)fd->bytesRemain);
216  be += strlen(be);
217  }
218  if (fd->wr_chunked) {
219  strcpy(be, " chunked");
220  be += strlen(be);
221  }
222  *be++ = '\t';
223  for (i = fd->nfps; i >= 0; i--) {
224  FDSTACK_t * fps = &fd->fps[i];
225  if (i != fd->nfps)
226  *be++ = ' ';
227  *be++ = '|';
228  *be++ = ' ';
229  if (fps->io == fdio) {
230  sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
231  } else if (fps->io == ufdio) {
232  sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
233 #if defined(WITH_ZLIB)
234  } else if (fps->io == gzdio) {
235  sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
236 #endif
237 #if defined(WITH_BZIP2)
238  } else if (fps->io == bzdio) {
239  sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
240 #endif
241 #if defined(WITH_XZ)
242  } else if (fps->io == lzdio) {
243  sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno);
244  } else if (fps->io == xzdio) {
245  sprintf(be, "XZD %p fdno %d", fps->fp, fps->fdno);
246 #endif
247  } else if (fps->io == fpio) {
248  /*@+voidabstract@*/
249  sprintf(be, "%s %p(%d) fdno %d",
250  (fps->fdno < 0 ? "LIBIO" : "FP"),
251  fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
252  /*@=voidabstract@*/
253  } else {
254  sprintf(be, "??? io %p fp %p fdno %d ???",
255  fps->io, fps->fp, fps->fdno);
256  }
257  be += strlen(be);
258  *be = '\0';
259  }
260  return buf;
261 }
262 
263 /* =============================================================== */
264 FD_t fdDup(int fdno)
265 {
266  FD_t fd;
267  int nfdno;
268 
269  if ((nfdno = dup(fdno)) < 0)
270  return NULL;
271  if (fcntl(nfdno, F_SETFD, FD_CLOEXEC)) {
272  (void) close(nfdno);
273  return NULL;
274  }
275  fd = fdNew("open (fdDup)");
276  fdSetOpen(fd, "fdDup", nfdno, 0); /* XXX bogus */
277  fdSetFdno(fd, nfdno);
278 DBGIO(fd, (stderr, "<-- fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
279  /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
280 }
281 
282 static inline /*@unused@*/
283 int fdSeekNot(void * cookie,
284  /*@unused@*/ _libio_pos_t pos,
285  /*@unused@*/ int whence)
286  /*@*/
287 {
288  FD_t fd = c2f(cookie);
289  FDSANE(fd); /* XXX keep gcc quiet */
290  return -2;
291 }
292 
293 /* =============================================================== */
294 
295 static void fdFini(void * _fd)
296  /*@globals fileSystem @*/
297  /*@modifies _fd, fileSystem @*/
298 {
299  FD_t fd = (FD_t) _fd;
300  int i;
301 
302 assert(fd != NULL);
303  fd->opath = _free(fd->opath);
304  if (fd->stats) free(fd->stats); fd->stats = NULL;
305  if (fd->ndigests > 0)
306  for (i = fd->ndigests - 1; i >= 0; i--) {
307  DIGEST_CTX ctx = fd->digests[i];
308  if (ctx == NULL)
309  continue;
310  (void) rpmDigestFinal(ctx, NULL, NULL, 0);
311  fd->digests[i] = NULL;
312  }
313  fd->digests = _free(fd->digests);
314  fd->ndigests = 0;
315  fd->contentType = _free(fd->contentType);
317 /*@-onlytrans@*/
318 #ifdef WITH_XAR
319  fd->xar = rpmxarFree(fd->xar, "fdFini");
320 #endif
321 #ifdef WITH_NEON
322 #ifndef NOTYET
323 if (fd->req != NULL)
324 fprintf(stderr, "*** %s: fd->req %p\n", __FUNCTION__, fd->req);
325 fd->req = NULL;
326 #else
327 assert(fd->req == NULL);
328 #endif
329 #endif
330  fd->dig = pgpDigFree(fd->dig);
331 /*@=onlytrans@*/
332 }
333 
334 /*@unchecked@*/ /*@only@*/ /*@null@*/
336 
337 static FD_t fdGetPool(/*@null@*/ rpmioPool pool)
338  /*@globals _fdPool, fileSystem @*/
339  /*@modifies pool, _fdPool, fileSystem @*/
340 {
341  FD_t fd;
342 
343  if (_fdPool == NULL) {
344  _fdPool = rpmioNewPool("fd", sizeof(*fd), -1, _rpmio_debug,
345  (char * (*)(void *))fdbg, NULL, fdFini);
346  pool = _fdPool;
347  }
348  fd = (FD_t) rpmioGetPool(pool, sizeof(*fd));
349  memset(((char *)fd)+sizeof(fd->_item), 0, sizeof(*fd)-sizeof(fd->_item));
350  return fd;
351 }
352 
353 /*@-incondefs@*/
354 /*@null@*/
355 FD_t XfdNew(const char * msg, const char * fn, unsigned ln)
356 {
357  FD_t fd = fdGetPool(_fdPool);
358  if (fd == NULL) /* XXX xmalloc never returns NULL */
359  return NULL;
360  fd->flags = 0;
361  fd->magic = FDMAGIC;
362 
363  fd->nfps = 0;
364  memset(fd->fps, 0, sizeof(fd->fps));
365 
366  fd->fps[0].io = ufdio;
367  fd->fps[0].fp = NULL;
368  fd->fps[0].fdno = -1;
369 
370  fd->u = NULL;
371  fd->req = NULL;
372  fd->rd_timeoutsecs = 1; /* XXX default value used to be -1 */
373  fd->bytesRemain = -1;
374  fd->contentLength = -1;
375  fd->persist = 0;
376  fd->wr_chunked = 0;
377 
378  fd->syserrno = 0;
379  fd->errcookie = NULL;
380 
381  fd->opath = NULL;
382  fd->oflags = 0;
383  fd->omode = 0;
384 
385  fd->xar = NULL;
386  fd->dig = NULL;
387  fd->stats = (FDSTAT_t) xcalloc(1, sizeof(*fd->stats));
388  fd->ndigests = 0;
389  fd->digests = NULL;
390 
391  fd->contentType = NULL;
392  fd->contentDisposition = NULL;
393  fd->lastModified = 0;
394  fd->ftpFileDoneNeeded = 0;
395  fd->fd_cpioPos = 0;
396 
397  return (FD_t)rpmioLinkPoolItem((rpmioItem)fd, msg, fn, ln);
398 }
399 /*@=incondefs@*/
400 
401 static ssize_t fdRead(void * cookie, /*@out@*/ char * buf, size_t count)
402  /*@globals errno, fileSystem, internalState @*/
403  /*@modifies buf, errno, fileSystem, internalState @*/
404  /*@requires maxSet(buf) >= (count - 1) @*/
405 {
406  FD_t fd = c2f(cookie);
407  ssize_t rc;
408 
409  if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
410 
412  /* HACK: flimsy wiring for davRead */
413  if (fd->req != NULL) {
414 #ifdef WITH_NEON
415  if (fd->req != (void *)-1)
416  rc = davRead(fd, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
417  else
418  rc = -1;
419 #else
420  rc = -1;
421 #endif
422  /* XXX Chunked davRead EOF. */
423  if (rc == 0)
424  fd->bytesRemain = 0;
425  } else
426  if (fd->xar != NULL) {
427 #ifdef WITH_XAR
428  rc = xarRead(fd, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
429 #else
430  rc = -1;
431 #endif
432  } else
433  rc = read(fdFileno(fd), buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
434  fdstat_exit(fd, FDSTAT_READ, rc);
435 
436  if (fd->ndigests > 0 && rc > 0) fdUpdateDigests(fd, (const unsigned char *)buf, rc);
437 
438 DBGIO(fd, (stderr, "<--\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
439 
440  return rc;
441 }
442 
443 static ssize_t fdWrite(void * cookie, const char * buf, size_t count)
444  /*@globals errno, fileSystem, internalState @*/
445  /*@modifies errno, fileSystem, internalState @*/
446 {
447  FD_t fd = c2f(cookie);
448  int fdno = fdFileno(fd);
449  ssize_t rc;
450 
451  if (fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
452 
453  if (fd->ndigests > 0 && count > 0) fdUpdateDigests(fd, (const unsigned char *)buf, count);
454 
455  if (count == 0) return 0;
456 
458  /* HACK: flimsy wiring for davWrite */
459  if (fd->req != NULL)
460 #ifdef WITH_NEON
461  if (fd->req != (void *)-1)
462  rc = davWrite(fd, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
463  else
464  rc = -1;
465 #else
466  rc = -1;
467 #endif
468  else
469  rc = write(fdno, buf, (count > (size_t)fd->bytesRemain ? (size_t)fd->bytesRemain : count));
470  fdstat_exit(fd, FDSTAT_WRITE, rc);
471 
472 DBGIO(fd, (stderr, "<--\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
473 
474  return rc;
475 }
476 
477 static int fdSeek(void * cookie, _libio_pos_t pos, int whence)
478  /*@globals fileSystem, internalState @*/
479  /*@modifies fileSystem, internalState @*/
480 {
481 #ifdef USE_COOKIE_SEEK_POINTER
482  _IO_off64_t p = *pos;
483 #else
484  off_t p = pos;
485 #endif
486  FD_t fd = c2f(cookie);
487  off_t rc;
488 
489  assert(fd->bytesRemain == -1); /* XXX FIXME fadio only for now */
491  rc = lseek(fdFileno(fd), p, whence);
492  fdstat_exit(fd, FDSTAT_SEEK, rc);
493 
494 DBGIO(fd, (stderr, "<--\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
495 
496  return (int) rc;
497 }
498 
499 static int fdClose( /*@only@*/ void * cookie)
500  /*@globals errno, fileSystem, systemState, internalState @*/
501  /*@modifies errno, fileSystem, systemState, internalState @*/
502 {
503  FD_t fd;
504  int fdno;
505  int rc;
506 
507  if (cookie == NULL) return -2;
508  fd = c2f(cookie);
509  fdno = fdFileno(fd);
510 
511  fdSetFdno(fd, -1);
512 
514  /* HACK: flimsy wiring for davClose */
515  if (fd->req != NULL)
516 #ifdef WITH_NEON
517  rc = davClose(fd);
518 #else
519  rc = -1;
520 #endif
521  else
522  rc = ((fdno >= 0) ? close(fdno) : -2);
523  fdstat_exit(fd, FDSTAT_CLOSE, rc);
524 
525 DBGIO(fd, (stderr, "<--\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
526 
527  fd = fdFree(fd, "open (fdClose)");
528  return rc;
529 }
530 
531 static /*@null@*/ FD_t fdOpen(const char *path, int flags, mode_t mode)
532  /*@globals errno, fileSystem, internalState @*/
533  /*@modifies errno, fileSystem, internalState @*/
534 {
535  FD_t fd;
536  int fdno;
537 
538  fdno = open(path, flags, mode);
539  if (fdno < 0) return NULL;
540  if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
541  (void) close(fdno);
542  return NULL;
543  }
544  fd = fdNew("open (fdOpen)");
545  fdSetOpen(fd, path, flags, mode);
546  fdSetFdno(fd, fdno);
547 assert(fd != NULL);
548  fd->flags = flags;
549 DBGIO(fd, (stderr, "<--\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
550  /*@-refcounttrans@*/ return fd; /*@=refcounttrans@*/
551 }
552 
553 #ifdef NOTUSED
554 FILE *fdFdopen(void * cookie, const char *fmode)
555 {
556  FD_t fd = c2f(cookie);
557  int fdno;
558  FILE * fp;
559 
560  if (fmode == NULL) return NULL;
561  fdno = fdFileno(fd);
562  if (fdno < 0) return NULL;
563  fp = fdopen(fdno, fmode);
564 DBGIO(fd, (stderr, "<-- fdFdopen(%p,\"%s\") fdno %d -> fp %p fdno %d\n", cookie, fmode, fdno, fp, fileno(fp)));
565  fd = fdFree(fd, "open (fdFdopen)");
566  return fp;
567 }
568 #endif
569 
570 /*@-type@*/ /* LCL: function typedefs */
571 static struct FDIO_s fdio_s = {
572  fdRead, fdWrite, fdSeek, fdClose, NULL, NULL, NULL,
573 };
574 /*@=type@*/
575 
576 FDIO_t fdio = /*@-compmempass@*/ &fdio_s /*@=compmempass@*/ ;
577 
578 int fdWritable(FD_t fd, int secs)
579 {
580  int fdno;
581  int rc;
582 #if defined(HAVE_POLL_H)
583  int msecs = (secs >= 0 ? (1000 * secs) : -1);
584  struct pollfd wrfds;
585 #else
586  struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
587  fd_set wrfds;
588  FD_ZERO(&wrfds);
589 #endif
590 
591  /* HACK: flimsy wiring for davWrite */
592  if (fd->req != NULL)
593  return (fd->req == (void *)-1 ? -1 : 1);
594 
595  if ((fdno = fdFileno(fd)) < 0)
596  return -1; /* XXX W2DO? */
597 
598  do {
599 #if defined(HAVE_POLL_H)
600  wrfds.fd = fdno;
601  wrfds.events = POLLOUT;
602  wrfds.revents = 0;
603  rc = poll(&wrfds, 1, msecs);
604 #else
605  if (tvp) {
606  tvp->tv_sec = secs;
607  tvp->tv_usec = 0;
608  }
609  FD_SET(fdno, &wrfds);
610 /*@-compdef -nullpass@*/
611  rc = select(fdno + 1, NULL, &wrfds, NULL, tvp);
612 /*@=compdef =nullpass@*/
613 #endif
614 
615  /* HACK: EBADF on PUT chunked termination from ufdClose. */
616 if (_rpmio_debug && !(rc == 1 && errno == 0))
617 fprintf(stderr, "*** fdWritable fdno %d rc %d %s\n", fdno, rc, strerror(errno));
618  if (rc < 0) {
619  switch (errno) {
620  case EINTR:
621  continue;
622  /*@notreached@*/ /*@switchbreak@*/ break;
623  default:
624  return rc;
625  /*@notreached@*/ /*@switchbreak@*/ break;
626  }
627  }
628  return rc;
629  } while (1);
630  /*@notreached@*/
631 }
632 
633 int fdReadable(FD_t fd, int secs)
634 {
635  int fdno;
636  int rc;
637 #if defined(HAVE_POLL_H)
638  int msecs = (secs >= 0 ? (1000 * secs) : -1);
639  struct pollfd rdfds;
640 #else
641  struct timeval timeout, *tvp = (secs >= 0 ? &timeout : NULL);
642  fd_set rdfds;
643  FD_ZERO(&rdfds);
644 #endif
645 
646  /* HACK: flimsy wiring for davRead */
647  if (fd->req != NULL)
648  return (fd->req == (void *)-1 ? -1 : 1);
649 
650  if ((fdno = fdFileno(fd)) < 0)
651  return -1; /* XXX W2DO? */
652 
653  do {
654 #if defined(HAVE_POLL_H)
655  rdfds.fd = fdno;
656  rdfds.events = POLLIN;
657  rdfds.revents = 0;
658  rc = poll(&rdfds, 1, msecs);
659 #else
660  if (tvp) {
661  tvp->tv_sec = secs;
662  tvp->tv_usec = 0;
663  }
664  FD_SET(fdno, &rdfds);
665  /*@-compdef -nullpass@*/
666  rc = select(fdno + 1, &rdfds, NULL, NULL, tvp);
667  /*@=compdef =nullpass@*/
668 #endif
669 
670  if (rc < 0) {
671  switch (errno) {
672  case EINTR:
673  continue;
674  /*@notreached@*/ /*@switchbreak@*/ break;
675  default:
676  return rc;
677  /*@notreached@*/ /*@switchbreak@*/ break;
678  }
679  }
680  return rc;
681  } while (1);
682  /*@notreached@*/
683 }
684 
685 int fdFgets(FD_t fd, char * buf, size_t len)
686 {
687  int fdno;
688  int secs = fd->rd_timeoutsecs;
689  size_t nb = 0;
690  int ec = 0;
691  char lastchar = '\0';
692 
693  if ((fdno = fdFileno(fd)) < 0)
694  return 0; /* XXX W2DO? */
695 
696  do {
697  int rc;
698 
699  /* Is there data to read? */
700  rc = fdReadable(fd, secs);
701 
702  switch (rc) {
703  case -1: /* error */
704  ec = -1;
705  continue;
706  /*@notreached@*/ /*@switchbreak@*/ break;
707  case 0: /* timeout */
708  ec = -1;
709  continue;
710  /*@notreached@*/ /*@switchbreak@*/ break;
711  default: /* data to read */
712  /*@switchbreak@*/ break;
713  }
714 
715  errno = 0;
716 #ifdef NOISY
717  rc = fdRead(fd, buf + nb, 1);
718 #else
719  rc = (int)read(fdFileno(fd), buf + nb, 1);
720 #endif
721  if (rc < 0) {
722  fd->syserrno = errno;
723  switch (errno) {
724  case EWOULDBLOCK:
725  continue;
726  /*@notreached@*/ /*@switchbreak@*/ break;
727  default:
728  /*@switchbreak@*/ break;
729  }
730 if (_rpmio_debug)
731 fprintf(stderr, "*** read: fd %p rc %d errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
732  ec = -1;
733  break;
734  } else if (rc == 0) {
735 if (_rpmio_debug)
736 fprintf(stderr, "*** read: fd %p rc %d EOF errno %d %s \"%s\"\n", fd, rc, errno, strerror(errno), buf);
737  break;
738  } else {
739  nb += rc;
740  buf[nb] = '\0';
741  lastchar = buf[nb - 1];
742  }
743  } while (ec == 0 && nb < len && lastchar != '\n');
744 
745  return (ec >= 0 ? (int)nb : ec);
746 }
747 
748 /* =============================================================== */
749 /* Support for FTP/HTTP I/O.
750  */
751 const char * ftpStrerror(int errorNumber)
752 {
753  switch (errorNumber) {
754  case 0:
755  return _("Success");
756 
757  /* HACK error impediance match, coalesce and rename. */
758  case FTPERR_NE_ERROR:
759  return ("NE_ERROR: Generic error.");
760  case FTPERR_NE_LOOKUP:
761  return ("NE_LOOKUP: Hostname lookup failed.");
762  case FTPERR_NE_AUTH:
763  return ("NE_AUTH: Server authentication failed.");
764  case FTPERR_NE_PROXYAUTH:
765  return ("NE_PROXYAUTH: Proxy authentication failed.");
766  case FTPERR_NE_CONNECT:
767  return ("NE_CONNECT: Could not connect to server.");
768  case FTPERR_NE_TIMEOUT:
769  return ("NE_TIMEOUT: Connection timed out.");
770  case FTPERR_NE_FAILED:
771  return ("NE_FAILED: The precondition failed.");
772  case FTPERR_NE_RETRY:
773  return ("NE_RETRY: Retry request.");
774  case FTPERR_NE_REDIRECT:
775  return ("NE_REDIRECT: Redirect received.");
776 
778  return _("Bad server response");
780  return _("Server I/O error");
782  return _("Server timeout");
784  return _("Unable to lookup server host address");
785  case FTPERR_BAD_HOSTNAME:
786  return _("Unable to lookup server host name");
788  return _("Failed to connect to server");
790  return _("Failed to establish data connection to server");
792  return _("I/O error to local file");
794  return _("Error setting remote server to passive mode");
796  return _("File not found on server");
798  return _("Abort in progress");
799 
800  case FTPERR_UNKNOWN:
801  default:
802  return _("Unknown or unexpected error");
803  }
804 }
805 
806 const char *urlStrerror(const char *url)
807 {
808  const char *retstr;
809  switch (urlIsURL(url)) {
810  case URL_IS_HKP:
811  case URL_IS_FTP:
812  case URL_IS_HTTP:
813  case URL_IS_HTTPS:
814  { urlinfo u;
815 /* XXX This only works for httpReq/ftpLogin/ftpReq failures */
816  if (urlSplit(url, &u) == 0)
817  retstr = ftpStrerror(u->openError);
818  else
819  retstr = _("Malformed URL");
820  } break;
821  case URL_IS_MONGO: /* XXX FIXME */
822  default:
823  retstr = strerror(errno);
824  break;
825  }
826  return retstr;
827 }
828 
829 #if !defined(HAVE_GETADDRINFO)
830 #if !defined(USE_ALT_DNS) || !USE_ALT_DNS
831 static int mygethostbyname(const char * host,
832  /*@out@*/ struct in_addr * address)
833  /*@globals h_errno @*/
834  /*@modifies *address @*/
835 {
836  struct hostent * hostinfo;
837 
838  /*@-multithreaded @*/
839  hostinfo = gethostbyname(host);
840  /*@=multithreaded @*/
841  if (!hostinfo) return 1;
842 
843  memcpy(address, hostinfo->h_addr_list[0], sizeof(*address));
844  return 0;
845 }
846 #endif
847 
848 /*@-compdef@*/ /* FIX: address->s_addr undefined. */
849 static int getHostAddress(const char * host, /*@out@*/ struct in_addr * address)
850  /*@globals errno, h_errno @*/
851  /*@modifies *address, errno @*/
852 {
853 #if 0 /* XXX workaround nss_foo module hand-off using valgrind. */
854  if (!strcmp(host, "localhost")) {
855  /*@-moduncon @*/
856  if (!inet_aton("127.0.0.1", address))
857  return FTPERR_BAD_HOST_ADDR;
858  /*@=moduncon @*/
859  } else
860 #endif
861  if (xisdigit(host[0])) {
862  /*@-moduncon @*/
863  if (!inet_aton(host, address))
864  return FTPERR_BAD_HOST_ADDR;
865  /*@=moduncon @*/
866  } else {
867  if (mygethostbyname(host, address)) {
868  errno = h_errno;
869  return FTPERR_BAD_HOSTNAME;
870  }
871  }
872 
873  return 0;
874 }
875 /*@=compdef@*/
876 #endif /* HAVE_GETADDRINFO */
877 
878 static int tcpConnect(FD_t ctrl, const char * host, int port)
879  /*@globals fileSystem, internalState @*/
880  /*@modifies ctrl, fileSystem, internalState @*/
881 {
882  int fdno = -1;
883  int rc;
884 #ifdef HAVE_GETADDRINFO
885 /*@-unrecog@*/
886  struct addrinfo hints, *res, *res0;
887 #ifndef NI_MAXSERV
888 #define NI_MAXSERV 32
889 #endif
890  char pbuf[NI_MAXSERV];
891  int xx;
892 
893  memset(&hints, 0, sizeof(hints));
894  hints.ai_family = AF_UNSPEC;
895  hints.ai_socktype = SOCK_STREAM;
896  sprintf(pbuf, "%d", port);
897  pbuf[sizeof(pbuf)-1] = '\0';
899  if (getaddrinfo(host, pbuf, &hints, &res0) == 0) {
900  for (res = res0; res != NULL; res = res->ai_next) {
901  if ((fdno = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
902  continue;
903  if (connect(fdno, res->ai_addr, (int)res->ai_addrlen) < 0) {
904  xx = close(fdno);
905  continue;
906  }
907  /* success */
908  rc = 0;
909  if (_ftp_debug) {
910  char hbuf[NI_MAXHOST];
911  hbuf[0] = '\0';
912  xx = getnameinfo(res->ai_addr, res->ai_addrlen, hbuf, sizeof(hbuf),
913  NULL, 0, NI_NUMERICHOST);
914  fprintf(stderr,"++ connect [%s]:%d on fdno %d\n",
915  /*@-unrecog@*/ hbuf /*@=unrecog@*/, port, fdno);
916  }
917  break;
918  }
919  freeaddrinfo(res0);
920  }
921  if (rc < 0)
922  goto errxit;
923 /*@=unrecog@*/
924 #else /* HAVE_GETADDRINFO */
925  struct sockaddr_in sin;
926 
927  memset(&sin, 0, sizeof(sin));
928  sin.sin_family = AF_INET;
929  sin.sin_port = htons(port);
930  sin.sin_addr.s_addr = INADDR_ANY;
931 
932  do {
933  if ((rc = getHostAddress(host, &sin.sin_addr)) < 0)
934  break;
935 
936  if ((fdno = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) {
938  break;
939  }
940 
941  /*@-internalglobs@*/
942  if (connect(fdno, (struct sockaddr *) &sin, sizeof(sin))) {
944  break;
945  }
946  /*@=internalglobs@*/
947  } while (0);
948 
949  if (rc < 0)
950  goto errxit;
951 
952 if (_ftp_debug)
953 fprintf(stderr,"++ connect %s:%d on fdno %d\n",
954 /*@-unrecog -moduncon -evalorderuncon @*/
955 inet_ntoa(sin.sin_addr)
956 /*@=unrecog =moduncon =evalorderuncon @*/ ,
957 (int)ntohs(sin.sin_port), fdno);
958 #endif /* HAVE_GETADDRINFO */
959 
960  fdSetFdno(ctrl, (fdno >= 0 ? fdno : -1));
961  return 0;
962 
963 errxit:
964  /*@-observertrans@*/
965  fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
966  /*@=observertrans@*/
967  if (fdno >= 0)
968  (void) close(fdno);
969  return rc;
970 }
971 
972 static int checkResponse(void * _u, FD_t ctrl,
973  /*@out@*/ int *ecp, /*@out@*/ char ** str)
974  /*@globals fileSystem @*/
975  /*@modifies ctrl, *ecp, *str, fileSystem @*/
976 {
977  urlinfo u = (urlinfo) _u;
978  char *buf;
979  size_t bufAlloced;
980  int bufLength = 0;
981  const char *s;
982  char *se;
983  int ec = 0;
984  int moretodo = 1;
985  char errorCode[4];
986 
987  URLSANE(u);
988  if (u->bufAlloced == 0 || u->buf == NULL) {
990  u->buf = (char *) xcalloc(u->bufAlloced, sizeof(u->buf[0]));
991  }
992  buf = u->buf;
993  bufAlloced = u->bufAlloced;
994  *buf = '\0';
995 
996  errorCode[0] = '\0';
997 
998  do {
999  int rc;
1000 
1001  /*
1002  * Read next line from server.
1003  */
1004  se = buf + bufLength;
1005  *se = '\0';
1006  rc = fdFgets(ctrl, se, (bufAlloced - bufLength));
1007  if (rc < 0) {
1009  continue;
1010  } else if (rc == 0 || fdWritable(ctrl, 0) < 1)
1011  moretodo = 0;
1012 
1013  /*
1014  * Process next line from server.
1015  */
1016  for (s = se; *s != '\0'; s = se) {
1017  const char *e;
1018 
1019  while (*se && *se != '\n') se++;
1020 
1021  if (se > s && se[-1] == '\r')
1022  se[-1] = '\0';
1023  if (*se == '\0')
1024  /*@innerbreak@*/ break;
1025 
1026 if (_ftp_debug)
1027 fprintf(stderr, "<- %s\n", s);
1028 
1029  /* HTTP: header termination on empty line */
1030  if (*s == '\0') {
1031  moretodo = 0;
1032  /*@innerbreak@*/ break;
1033  }
1034  *se++ = '\0';
1035 
1036  /* HTTP: look for "HTTP/1.1 123 ..." */
1037  if (!strncmp(s, "HTTP", sizeof("HTTP")-1)) {
1038  ctrl->contentLength = -1;
1039  if ((e = strchr(s, '.')) != NULL) {
1040  e++;
1041  u->httpVersion = (int)(*e - '0');
1042  if (u->httpVersion < 1 || u->httpVersion > 2)
1043  ctrl->persist = u->httpVersion = 0;
1044  else
1045  ctrl->persist = 1;
1046  }
1047  if ((e = strchr(s, ' ')) != NULL) {
1048  e++;
1049  if (strchr("0123456789", *e))
1050  strncpy(errorCode, e, 3);
1051  errorCode[3] = '\0';
1052  }
1053  /*@innercontinue@*/ continue;
1054  }
1055 
1056  /* HTTP: look for "token: ..." */
1057  for (e = s; *e && !(*e == ' ' || *e == ':'); e++)
1058  {};
1059  if (e > s && *e++ == ':') {
1060  size_t ne = (e - s);
1061  while (*e && *e == ' ') e++;
1062 #if 0
1063  if (!strncmp(s, "Date:", ne)) {
1064  } else
1065  if (!strncmp(s, "Server:", ne)) {
1066  } else
1067  if (!strncmp(s, "Last-Modified:", ne)) {
1068  } else
1069  if (!strncmp(s, "ETag:", ne)) {
1070  } else
1071 #endif
1072  if (!strncmp(s, "Accept-Ranges:", ne)) {
1073  if (!strcmp(e, "bytes"))
1075  if (!strcmp(e, "none"))
1077  } else
1078  if (!strncmp(s, "Content-Length:", ne)) {
1079  if (strchr("0123456789", *e))
1080  ctrl->contentLength = atol(e);
1081  } else
1082  if (!strncmp(s, "Connection:", ne)) {
1083  if (!strcmp(e, "close"))
1084  ctrl->persist = 0;
1085  }
1086 #if 0
1087  else
1088  if (!strncmp(s, "Content-Type:", ne)) {
1089  } else
1090  if (!strncmp(s, "Transfer-Encoding:", ne)) {
1091  if (!strcmp(e, "chunked"))
1092  ctrl->wr_chunked = 1;
1093  else
1094  ctrl->wr_chunked = 0;
1095  } else
1096  if (!strncmp(s, "Allow:", ne)) {
1097  }
1098 #endif
1099  /*@innercontinue@*/ continue;
1100  }
1101 
1102  /* HTTP: look for "<TITLE>501 ... </TITLE>" */
1103  if (!strncmp(s, "<TITLE>", sizeof("<TITLE>")-1))
1104  s += sizeof("<TITLE>") - 1;
1105 
1106  /* FTP: look for "123-" and/or "123 " */
1107  if (strchr("0123456789", *s)) {
1108  if (errorCode[0] != '\0') {
1109  if (!strncmp(s, errorCode, sizeof("123")-1) && s[3] == ' ')
1110  moretodo = 0;
1111  } else {
1112  strncpy(errorCode, s, sizeof("123")-1);
1113  errorCode[3] = '\0';
1114  if (s[3] != '-')
1115  moretodo = 0;
1116  }
1117  }
1118  }
1119 
1120  if (moretodo && se > s) {
1121  bufLength = se - s - 1;
1122  if (s != buf)
1123  memmove(buf, s, bufLength);
1124  } else {
1125  bufLength = 0;
1126  }
1127  } while (moretodo && ec == 0);
1128 
1129  if (str) *str = buf;
1130  if (ecp) *ecp = atoi(errorCode);
1131 
1132  return ec;
1133 }
1134 
1135 static int ftpCheckResponse(urlinfo u, /*@out@*/ char ** str)
1136  /*@globals fileSystem @*/
1137  /*@modifies u, *str, fileSystem @*/
1138 {
1139  int ec = 0;
1140  int rc;
1141 
1142  URLSANE(u);
1143  rc = checkResponse(u, u->ctrl, &ec, str);
1144 
1145  switch (ec) {
1146  case 550:
1147  return FTPERR_FILE_NOT_FOUND;
1148  /*@notreached@*/ break;
1149  case 552:
1151  /*@notreached@*/ break;
1152  default:
1153  if (ec >= 400 && ec <= 599) {
1155  }
1156  break;
1157  }
1158  return rc;
1159 }
1160 
1161 static int ftpCommand(urlinfo u, char ** str, ...)
1162  /*@globals fileSystem, internalState @*/
1163  /*@modifies u, *str, fileSystem, internalState @*/
1164 {
1165  va_list ap;
1166  int len = 0;
1167  const char * s, * t;
1168  char * te;
1169  int rc;
1170 
1171  URLSANE(u);
1172  va_start(ap, str);
1173  while ((s = va_arg(ap, const char *)) != NULL) {
1174  if (len) len++;
1175  len += strlen(s);
1176  }
1177  len += sizeof("\r\n")-1;
1178  va_end(ap);
1179 
1180  t = te = (char *) alloca(len + 1);
1181 
1182  va_start(ap, str);
1183  while ((s = va_arg(ap, const char *)) != NULL) {
1184  if (te > t) *te++ = ' ';
1185  te = stpcpy(te, s);
1186  }
1187  te = stpcpy(te, "\r\n");
1188  va_end(ap);
1189 
1190 if (_ftp_debug)
1191 fprintf(stderr, "-> %s", t);
1192  if (fdWrite(u->ctrl, t, (te-t)) != (te-t))
1193  return FTPERR_SERVER_IO_ERROR;
1194 
1195  rc = ftpCheckResponse(u, str);
1196  return rc;
1197 }
1198 
1199 static int ftpLogin(urlinfo u)
1200  /*@globals fileSystem, internalState @*/
1201  /*@modifies u, fileSystem, internalState @*/
1202 {
1203  const char * host;
1204  const char * user;
1205  const char * password;
1206  int port;
1207  int rc;
1208 
1209  URLSANE(u);
1210  u->ctrl = fdLink(u->ctrl, "open ctrl");
1211 
1212  if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL)) {
1213  rc = FTPERR_BAD_HOSTNAME;
1214  goto errxit;
1215  }
1216 
1217  if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = IPPORT_FTP;
1218 
1219  if ((user = (u->proxyu ? u->proxyu : u->user)) == NULL)
1220  user = "anonymous";
1221 
1222  if ((password = u->password) == NULL) {
1223  uid_t uid = getuid();
1224  struct passwd * pw;
1225  if (uid && (pw = getpwuid(uid)) != NULL) {
1226  char *myp = (char *) alloca(strlen(pw->pw_name) + sizeof("@"));
1227  strcpy(myp, pw->pw_name);
1228  strcat(myp, "@");
1229  password = myp;
1230  } else {
1231  password = "root@";
1232  }
1233  }
1234 
1235  if (fdFileno(u->ctrl) >= 0 && fdWritable(u->ctrl, 0) < 1)
1236  /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
1237 
1238 /*@-usereleased@*/
1239  if (fdFileno(u->ctrl) < 0) {
1240  rc = tcpConnect(u->ctrl, host, port);
1241  if (rc < 0)
1242  goto errxit2;
1243  }
1244 
1245  if ((rc = ftpCheckResponse(u, NULL)))
1246  goto errxit;
1247 
1248  if ((rc = ftpCommand(u, NULL, "USER", user, NULL)))
1249  goto errxit;
1250 
1251  if ((rc = ftpCommand(u, NULL, "PASS", password, NULL)))
1252  goto errxit;
1253 
1254  if ((rc = ftpCommand(u, NULL, "TYPE", "I", NULL)))
1255  goto errxit;
1256 
1257  /*@-compdef@*/
1258  return 0;
1259  /*@=compdef@*/
1260 
1261 errxit:
1262  /*@-observertrans@*/
1263  fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
1264  /*@=observertrans@*/
1265 errxit2:
1266  if (fdFileno(u->ctrl) >= 0)
1267  /*@-refcounttrans@*/ (void) fdClose(u->ctrl); /*@=refcounttrans@*/
1268  /*@-compdef@*/
1269  return rc;
1270  /*@=compdef@*/
1271 /*@=usereleased@*/
1272 }
1273 
1274 int ftpReq(FD_t data, const char * ftpCmd, const char * ftpArg)
1275 {
1276  urlinfo u = (urlinfo) data->u;
1277 #if !defined(HAVE_GETADDRINFO)
1278  struct sockaddr_in dataAddress;
1279 #endif /* HAVE_GETADDRINFO */
1280  char remoteIP[NI_MAXHOST];
1281  char * cmd;
1282  size_t cmdlen;
1283  char * passReply;
1284  char * chptr;
1285  int rc;
1286  int epsv;
1287  int port;
1288 
1289  remoteIP[0] = '\0';
1290  URLSANE(u);
1291  if (ftpCmd == NULL)
1292  return FTPERR_UNKNOWN; /* XXX W2DO? */
1293 
1294  cmdlen = strlen(ftpCmd) + (ftpArg ? 1+strlen(ftpArg) : 0) + sizeof("\r\n");
1295  chptr = cmd = (char *) alloca(cmdlen);
1296  chptr = stpcpy(chptr, ftpCmd);
1297  if (ftpArg) {
1298  *chptr++ = ' ';
1299  chptr = stpcpy(chptr, ftpArg);
1300  }
1301  chptr = stpcpy(chptr, "\r\n");
1302  cmdlen = chptr - cmd;
1303 
1304 /*
1305  * Get the ftp version of the Content-Length.
1306  */
1307  if (!strncmp(cmd, "RETR", 4)) {
1308  unsigned cl;
1309 
1310  passReply = NULL;
1311  rc = ftpCommand(u, &passReply, "SIZE", ftpArg, NULL);
1312  if (rc)
1313  goto errxit;
1314  if (sscanf(passReply, "%d %u", &rc, &cl) != 2) {
1316  goto errxit;
1317  }
1318  rc = 0;
1319  data->contentLength = cl;
1320  }
1321 
1322  epsv = 0;
1323  passReply = NULL;
1324 #ifdef HAVE_GETNAMEINFO
1325  rc = ftpCommand(u, &passReply, "EPSV", NULL);
1326  if (rc == 0) {
1327 #ifdef HAVE_GETADDRINFO
1328  struct sockaddr_storage ss;
1329 #else /* HAVE_GETADDRINFO */
1330  struct sockaddr_in ss;
1331 #endif /* HAVE_GETADDRINFO */
1332  socklen_t sslen = sizeof(ss);
1333 
1334  /* we need to know IP of remote host */
1335  if ((getpeername(fdFileno(c2f(u->ctrl)), (struct sockaddr *)&ss, &sslen) == 0)
1336  && (getnameinfo((struct sockaddr *)&ss, sslen,
1337  remoteIP, sizeof(remoteIP),
1338  NULL, 0, NI_NUMERICHOST) == 0))
1339  {
1340  epsv++;
1341  } else {
1342  /* abort EPSV and fall back to PASV */
1343  rc = ftpCommand(u, &passReply, "ABOR", NULL);
1344  if (rc) {
1345  rc = FTPERR_PASSIVE_ERROR;
1346  goto errxit;
1347  }
1348  }
1349  }
1350  if (epsv == 0)
1351 #endif /* HAVE_GETNAMEINFO */
1352  rc = ftpCommand(u, &passReply, "PASV", NULL);
1353  if (rc) {
1354  rc = FTPERR_PASSIVE_ERROR;
1355  goto errxit;
1356  }
1357 
1358  chptr = passReply;
1359 assert(chptr != NULL);
1360  while (*chptr && *chptr != '(') chptr++;
1361  if (*chptr != '(') return FTPERR_PASSIVE_ERROR;
1362  chptr++;
1363  passReply = chptr;
1364  while (*chptr && *chptr != ')') chptr++;
1365  if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
1366  *chptr-- = '\0';
1367 
1368  if (epsv) {
1369  int i;
1370  if(sscanf(passReply,"%*c%*c%*c%d%*c",&i) != 1) {
1371  rc = FTPERR_PASSIVE_ERROR;
1372  goto errxit;
1373  }
1374  port = i;
1375  } else {
1376 
1377  while (*chptr && *chptr != ',') chptr--;
1378  if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
1379  chptr--;
1380  while (*chptr && *chptr != ',') chptr--;
1381  if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
1382  *chptr++ = '\0';
1383 
1384  /* now passReply points to the IP portion, and chptr points to the
1385  port number portion */
1386 
1387  { int i, j;
1388  if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
1389  rc = FTPERR_PASSIVE_ERROR;
1390  goto errxit;
1391  }
1392  port = (((unsigned)i) << 8) + j;
1393  }
1394 
1395  chptr = passReply;
1396  while (*chptr++ != '\0') {
1397  if (*chptr == ',') *chptr = '.';
1398  }
1399  sprintf(remoteIP, "%s", passReply);
1400  } /* if (epsv) */
1401 
1402 #ifdef HAVE_GETADDRINFO
1403 /*@-unrecog@*/
1404  {
1405  struct addrinfo hints, *res, *res0;
1406  char pbuf[NI_MAXSERV];
1407  int xx;
1408 
1409  memset(&hints, 0, sizeof(hints));
1410  hints.ai_family = AF_UNSPEC;
1411  hints.ai_socktype = SOCK_STREAM;
1412  hints.ai_flags = AI_NUMERICHOST;
1413 #if defined(AI_IDN)
1414  hints.ai_flags |= AI_IDN;
1415 #endif
1416  sprintf(pbuf, "%d", port);
1417  pbuf[sizeof(pbuf)-1] = '\0';
1418  if (getaddrinfo(remoteIP, pbuf, &hints, &res0)) {
1419  rc = FTPERR_PASSIVE_ERROR;
1420  goto errxit;
1421  }
1422 
1423  for (res = res0; res != NULL; res = res->ai_next) {
1424  rc = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
1425  fdSetFdno(data, (rc >= 0 ? rc : -1));
1426  if (rc < 0) {
1427  if (res->ai_next)
1428  continue;
1429  else {
1430  rc = FTPERR_FAILED_CONNECT;
1431  freeaddrinfo(res0);
1432  goto errxit;
1433  }
1434  }
1435  data = fdLink(data, "open data (ftpReq)");
1436 
1437  /* XXX setsockopt SO_LINGER */
1438  /* XXX setsockopt SO_KEEPALIVE */
1439  /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
1440 
1441  {
1442  int criterr = 0;
1443  while (connect(fdFileno(data), res->ai_addr, (int)res->ai_addrlen) < 0) {
1444  if (errno == EINTR)
1445  /*@innercontinue@*/ continue;
1446  criterr++;
1447  }
1448  if (criterr) {
1449  if (res->ai_addr) {
1450 /*@-refcounttrans@*/
1451  xx = fdClose(data);
1452 /*@=refcounttrans@*/
1453  continue;
1454  } else {
1455  rc = FTPERR_PASSIVE_ERROR;
1456  freeaddrinfo(res0);
1457  goto errxit;
1458  }
1459  }
1460  }
1461  /* success */
1462  rc = 0;
1463  break;
1464  }
1465  freeaddrinfo(res0);
1466  }
1467 /*@=unrecog@*/
1468 #else /* HAVE_GETADDRINFO */
1469  memset(&dataAddress, 0, sizeof(dataAddress));
1470  dataAddress.sin_family = AF_INET;
1471  dataAddress.sin_port = htons(port);
1472 
1473  /*@-moduncon@*/
1474  if (!inet_aton(remoteIP, &dataAddress.sin_addr)) {
1475  rc = FTPERR_PASSIVE_ERROR;
1476  goto errxit;
1477  }
1478  /*@=moduncon@*/
1479 
1480  rc = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
1481  fdSetFdno(data, (rc >= 0 ? rc : -1));
1482  if (rc < 0) {
1483  rc = FTPERR_FAILED_CONNECT;
1484  goto errxit;
1485  }
1486  data = fdLink(data, "open data (ftpReq)");
1487 
1488  /* XXX setsockopt SO_LINGER */
1489  /* XXX setsockopt SO_KEEPALIVE */
1490  /* XXX setsockopt SO_TOS IPTOS_THROUGHPUT */
1491 
1492  /*@-internalglobs@*/
1493  while (connect(fdFileno(data), (struct sockaddr *) &dataAddress,
1494  sizeof(dataAddress)) < 0)
1495  {
1496  if (errno == EINTR)
1497  continue;
1499  goto errxit;
1500  }
1501  /*@=internalglobs@*/
1502 #endif /* HAVE_GETADDRINFO */
1503 
1504 if (_ftp_debug)
1505 fprintf(stderr, "-> %s", cmd);
1506  if ((size_t)fdWrite(u->ctrl, cmd, cmdlen) != cmdlen) {
1508  goto errxit;
1509  }
1510 
1511  if ((rc = ftpCheckResponse(u, NULL))) {
1512  goto errxit;
1513  }
1514 
1515  data->ftpFileDoneNeeded = 1;
1516  u->ctrl = fdLink(u->ctrl, "grab data (ftpReq)");
1517  u->ctrl = fdLink(u->ctrl, "open data (ftpReq)");
1518  return 0;
1519 
1520 errxit:
1521  /*@-observertrans@*/
1522  fdSetSyserrno(u->ctrl, errno, ftpStrerror(rc));
1523  /*@=observertrans@*/
1524  if (fdFileno(data) >= 0)
1525  /*@-refcounttrans@*/ (void) fdClose(data); /*@=refcounttrans@*/
1526  return rc;
1527 }
1528 
1529 #ifdef DYING
1530 /*@unchecked@*/ /*@null@*/
1531 static rpmCallbackFunction _urlNotify = NULL;
1532 
1533 /*@unchecked@*/ /*@null@*/
1534 static void * _urlNotifyData = NULL;
1535 
1536 /*@unchecked@*/
1537 static int _urlNotifyCount = -1;
1538 
1539 static void urlSetCallback(rpmCallbackFunction notify, void *notifyData, int notifyCount) {
1540  _urlNotify = notify;
1541  _urlNotifyData = notifyData;
1542  _urlNotifyCount = (notifyCount >= 0) ? notifyCount : 4096;
1543 }
1544 #endif
1545 
1546 int ufdCopy(FD_t sfd, FD_t tfd)
1547 {
1548  char buf[BUFSIZ];
1549  int itemsRead;
1550  int itemsCopied = 0;
1551  int rc = 0;
1552 #ifdef DYING
1553  int notifier = -1;
1554 
1555  if (_urlNotify) {
1556  /*@-noeffectuncon @*/ /* FIX: check rc */
1557  (void)(*_urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
1558  0, 0, NULL, _urlNotifyData);
1559  /*@=noeffectuncon @*/
1560  }
1561 #endif
1562 
1563  while (1) {
1564  rc = (int) Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
1565  if (rc < 0) /* XXX never happens Fread returns size_t */
1566  break;
1567  else if (rc == 0) {
1568  rc = itemsCopied;
1569  break;
1570  }
1571  itemsRead = rc;
1572  rc = (int) Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
1573  if (rc < 0) /* XXX never happens Fwrite returns size_t */
1574  break;
1575  if (rc != itemsRead) {
1576  rc = FTPERR_FILE_IO_ERROR;
1577  break;
1578  }
1579 
1580  itemsCopied += itemsRead;
1581 #ifdef DYING
1582  if (_urlNotify && _urlNotifyCount > 0) {
1583  int n = itemsCopied/_urlNotifyCount;
1584  if (n != notifier) {
1585  /*@-noeffectuncon @*/ /* FIX: check rc */
1586  (void)(*_urlNotify) (NULL, RPMCALLBACK_INST_PROGRESS,
1587  itemsCopied, 0, NULL, _urlNotifyData);
1588  /*@=noeffectuncon @*/
1589  notifier = n;
1590  }
1591  }
1592 #endif
1593  }
1594 
1595  DBGIO(sfd, (stderr, "++ copied %d bytes: %s\n", itemsCopied,
1596  ftpStrerror(rc)));
1597 
1598 #ifdef DYING
1599  if (_urlNotify) {
1600  /*@-noeffectuncon @*/ /* FIX: check rc */
1601  (void)(*_urlNotify) (NULL, RPMCALLBACK_INST_OPEN_FILE,
1602  itemsCopied, itemsCopied, NULL, _urlNotifyData);
1603  /*@=noeffectuncon @*/
1604  }
1605 #endif
1606 
1607  return rc;
1608 }
1609 
1610 static int urlConnect(const char * url, /*@out@*/ urlinfo * uret)
1611  /*@globals h_errno, fileSystem, internalState @*/
1612  /*@modifies *uret, fileSystem, internalState @*/
1613 {
1614  urlinfo u;
1615  int rc = 0;
1616 
1617  if (urlSplit(url, &u) < 0)
1618  return -1;
1619 
1620  if (urlType(u) == URL_IS_FTP) {
1621  FD_t fd;
1622 
1623  if ((fd = u->ctrl) == NULL) {
1624  fd = u->ctrl = fdNew("persist ctrl (urlConnect FTP)");
1625 /*@-usereleased@*/
1626  fdSetOpen(u->ctrl, url, 0, 0);
1627  fdSetIo(u->ctrl, ufdio);
1628 /*@=usereleased@*/
1629  }
1630 
1631 assert(fd != NULL);
1633  fd->contentLength = fd->bytesRemain = -1;
1634  fd->u = NULL; /* XXX FTP ctrl has not */
1635  fd->ftpFileDoneNeeded = 0;
1636  fd = fdLink(fd, "grab ctrl (urlConnect FTP)");
1637 
1638  if (fdFileno(u->ctrl) < 0) {
1639  rpmlog(RPMLOG_DEBUG, D_("logging into %s as %s, pw %s\n"),
1640  u->host ? u->host : "???",
1641  u->user ? u->user : "ftp",
1642  u->password ? u->password : "(username)");
1643 
1644  if ((rc = ftpLogin(u)) < 0) { /* XXX save ftpLogin error */
1645  u->ctrl = fdFree(fd, "grab ctrl (urlConnect FTP)");
1646  u->openError = rc;
1647  }
1648  }
1649  }
1650 
1651  if (uret != NULL)
1652  *uret = urlLink(u, "urlConnect");
1653  u = urlFree(u, "urlSplit (urlConnect)");
1654 
1655  return rc;
1656 }
1657 
1658 int ufdGetFile(FD_t sfd, FD_t tfd)
1659 {
1660  int rc;
1661 
1662  FDSANE(sfd);
1663  FDSANE(tfd);
1664  rc = ufdCopy(sfd, tfd);
1665  (void) Fclose(sfd);
1666  if (rc > 0) /* XXX ufdCopy now returns no. bytes copied */
1667  rc = 0;
1668  return rc;
1669 }
1670 
1671 int ftpCmd(const char * cmd, const char * url, const char * arg2)
1672 {
1673  urlinfo u;
1674  int rc;
1675  const char * path;
1676 
1677  if (urlConnect(url, &u) < 0)
1678  return -1;
1679 
1680  (void) urlPath(url, &path);
1681 
1682  rc = ftpCommand(u, NULL, cmd, path, arg2, NULL);
1683  u->ctrl = fdFree(u->ctrl, "grab ctrl (ftpCmd)");
1684  return rc;
1685 }
1686 
1687 /* XXX these aren't worth the pain of including correctly */
1688 #if !defined(IAC)
1689 #define IAC ((unsigned char)255) /* interpret as command: */
1690 #endif
1691 #if !defined(IP)
1692 #define IP ((unsigned char)244) /* interrupt process--permanently */
1693 #endif
1694 #if !defined(DM)
1695 #define DM ((unsigned char)242) /* data mark--for connect. cleaning */
1696 #endif
1697 #if !defined(SHUT_RDWR)
1698 #define SHUT_RDWR 1+1
1699 #endif
1700 
1701 static int ftpAbort(urlinfo u, FD_t data)
1702  /*@globals fileSystem, internalState @*/
1703  /*@modifies u, data, fileSystem, internalState @*/
1704 {
1705  static unsigned char ipbuf[3] = { IAC, IP, IAC };
1706  FD_t ctrl;
1707  int rc;
1708  int tosecs;
1709 
1710  URLSANE(u);
1711 
1712  if (data != NULL) {
1713  data->ftpFileDoneNeeded = 0;
1714  if (fdFileno(data) >= 0)
1715  u->ctrl = fdFree(u->ctrl, "open data (ftpAbort)");
1716  u->ctrl = fdFree(u->ctrl, "grab data (ftpAbort)");
1717  }
1718  ctrl = u->ctrl;
1719 
1720  DBGIO(0, (stderr, "-> ABOR\n"));
1721 
1722 /*@-usereleased -compdef@*/
1723  if (send(fdFileno(ctrl), ipbuf, sizeof(ipbuf), MSG_OOB) != sizeof(ipbuf)) {
1724  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1725  return FTPERR_SERVER_IO_ERROR;
1726  }
1727 
1728  sprintf(u->buf, "%cABOR\r\n",(char) DM);
1729  if (fdWrite(ctrl, u->buf, 7) != 7) {
1730  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1731  return FTPERR_SERVER_IO_ERROR;
1732  }
1733 
1734  if (data && fdFileno(data) >= 0) {
1735  /* XXX shorten data drain time wait */
1736  tosecs = data->rd_timeoutsecs;
1737  data->rd_timeoutsecs = 10;
1738  if (fdReadable(data, data->rd_timeoutsecs) > 0) {
1739 /*@-infloopsuncon@*/
1740  while ((ufdio->read)(data, u->buf, u->bufAlloced) > 0)
1741  u->buf[0] = '\0';
1742 /*@=infloopsuncon@*/
1743  }
1744  data->rd_timeoutsecs = tosecs;
1745  /* XXX ftp abort needs to close the data channel to receive status */
1746  (void) shutdown(fdFileno(data), SHUT_RDWR);
1747  (void) close(fdFileno(data));
1748  data->fps[0].fdno = -1; /* XXX WRONG but expedient */
1749  }
1750 
1751  /* XXX shorten ctrl drain time wait */
1752 assert(u->ctrl != NULL);
1753  tosecs = u->ctrl->rd_timeoutsecs;
1754  u->ctrl->rd_timeoutsecs = 10;
1755  if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) {
1756  rc = ftpCheckResponse(u, NULL);
1757  }
1758  rc = ftpCheckResponse(u, NULL);
1759  u->ctrl->rd_timeoutsecs = tosecs;
1760 
1761  return rc;
1762 /*@=usereleased =compdef@*/
1763 }
1764 
1765 static int ftpFileDone(urlinfo u, FD_t data)
1766  /*@globals fileSystem @*/
1767  /*@modifies u, data, fileSystem @*/
1768 {
1769  int rc = 0;
1770 
1771  URLSANE(u);
1772  assert(data->ftpFileDoneNeeded);
1773 
1774  if (data->ftpFileDoneNeeded) {
1775  data->ftpFileDoneNeeded = 0;
1776  u->ctrl = fdFree(u->ctrl, "open data (ftpFileDone)");
1777  u->ctrl = fdFree(u->ctrl, "grab data (ftpFileDone)");
1778  rc = ftpCheckResponse(u, NULL);
1779  }
1780  return rc;
1781 }
1782 
1783 #ifndef WITH_NEON
1784 static int httpResp(urlinfo u, FD_t ctrl, /*@out@*/ char ** str)
1785  /*@globals fileSystem @*/
1786  /*@modifies ctrl, *str, fileSystem @*/
1787 {
1788  int ec = 0;
1789  int rc;
1790 
1791  URLSANE(u);
1792  rc = checkResponse(u, ctrl, &ec, str);
1793 
1794 if (_ftp_debug && !(rc == 0 && (ec == 200 || ec == 201)))
1795 fprintf(stderr, "*** httpResp: rc %d ec %d\n", rc, ec);
1796 
1797  switch (ec) {
1798  case 200:
1799  case 201: /* 201 Created. */
1800  break;
1801  case 204: /* HACK: if overwriting, 204 No Content. */
1802  case 403: /* 403 Forbidden. */
1803  ctrl->syserrno = EACCES; /* HACK */
1804  rc = FTPERR_UNKNOWN;
1805  break;
1806  default:
1807  rc = FTPERR_FILE_NOT_FOUND;
1808  break;
1809  }
1810  return rc;
1811 }
1812 
1813 static int httpReq(FD_t ctrl, const char * httpCmd, const char * httpArg)
1814  /*@globals h_errno, fileSystem, internalState @*/
1815  /*@modifies ctrl, fileSystem, internalState @*/
1816 {
1817  urlinfo u;
1818  const char * host;
1819  const char * path;
1820  char hthost[NI_MAXHOST];
1821  int port;
1822  int rc;
1823  char * req;
1824  size_t len;
1825  int retrying = 0;
1826 
1827 assert(ctrl != NULL);
1828  u = ctrl->u;
1829  URLSANE(u);
1830 
1831  if (((host = (u->proxyh ? u->proxyh : u->host)) == NULL))
1832  return FTPERR_BAD_HOSTNAME;
1833  if (strchr(host, ':'))
1834  sprintf(hthost, "[%s]", host);
1835  else
1836  strcpy(hthost, host);
1837 
1838  if ((port = (u->proxyp > 0 ? u->proxyp : u->port)) < 0) port = 80;
1839  path = (u->proxyh || u->proxyp > 0) ? u->url : httpArg;
1840  if (path == NULL) path = "";
1841 
1842 reopen:
1843  if (fdFileno(ctrl) >= 0 && (rc = fdWritable(ctrl, 0)) < 1) {
1844  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1845  }
1846 
1847 /*@-usereleased@*/
1848  if (fdFileno(ctrl) < 0) {
1849  rc = tcpConnect(ctrl, host, port);
1850  if (rc < 0)
1851  goto errxit2;
1852  ctrl = fdLink(ctrl, "open ctrl (httpReq)");
1853  }
1854 
1855  len = sizeof("\
1856 req x HTTP/1.0\r\n\
1857 User-Agent: rpm/3.0.4\r\n\
1858 Host: y:z\r\n\
1859 Accept: text/plain\r\n\
1860 Transfer-Encoding: chunked\r\n\
1861 \r\n\
1862 ") + strlen(httpCmd) + strlen(path) + sizeof(VERSION) + strlen(hthost) + 20;
1863 
1864  req = alloca(len);
1865  *req = '\0';
1866 
1867  if (!strcmp(httpCmd, "PUT")) {
1868  sprintf(req, "\
1869 %s %s HTTP/1.%d\r\n\
1870 User-Agent: rpm/%s\r\n\
1871 Host: %s:%d\r\n\
1872 Accept: text/plain\r\n\
1873 Transfer-Encoding: chunked\r\n\
1874 \r\n\
1875 ", httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
1876 } else {
1877  sprintf(req, "\
1878 %s %s HTTP/1.%d\r\n\
1879 User-Agent: rpm/%s\r\n\
1880 Host: %s:%d\r\n\
1881 Accept: text/plain\r\n\
1882 \r\n\
1883 ", httpCmd, path, (u->httpVersion ? 1 : 0), VERSION, hthost, port);
1884 }
1885 
1886 if (_ftp_debug)
1887 fprintf(stderr, "-> %s", req);
1888 
1889  len = strlen(req);
1890  if (fdWrite(ctrl, req, len) != (ssize_t)len) {
1892  goto errxit;
1893  }
1894 
1895  if (!strcmp(httpCmd, "PUT")) {
1896  ctrl->wr_chunked = 1;
1897  } else {
1898 
1899  rc = httpResp(u, ctrl, NULL);
1900 
1901  if (rc) {
1902  if (!retrying) { /* not HTTP_OK */
1903  retrying = 1;
1904  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1905  goto reopen;
1906  }
1907  goto errxit;
1908  }
1909  }
1910 
1911  ctrl = fdLink(ctrl, "open data (httpReq)");
1912  return 0;
1913 
1914 errxit:
1915  /*@-observertrans@*/
1916  fdSetSyserrno(ctrl, errno, ftpStrerror(rc));
1917  /*@=observertrans@*/
1918 errxit2:
1919  if (fdFileno(ctrl) >= 0)
1920  /*@-refcounttrans@*/ (void) fdClose(ctrl); /*@=refcounttrans@*/
1921  return rc;
1922 /*@=usereleased@*/
1923 }
1924 #endif /* WITH_NEON */
1925 
1926 /* XXX DYING: unused */
1928 {
1929  FDSANE(fd);
1930  if (fd->u == NULL)
1931  return NULL;
1932 /*@-retexpose@*/
1933  return urlLink(fd->u, "ufdGetUrlinfo");
1934 /*@=retexpose@*/
1935 }
1936 
1937 /* =============================================================== */
1938 static ssize_t ufdRead(void * cookie, /*@out@*/ char * buf, size_t count)
1939  /*@globals fileSystem, internalState @*/
1940  /*@modifies buf, fileSystem, internalState @*/
1941  /*@requires maxSet(buf) >= (count - 1) @*/
1942 {
1943  FD_t fd = c2f(cookie);
1944  size_t bytesRead;
1945  size_t total;
1946 
1947  if (fdGetIo(fd) == fdio) {
1948  struct stat sb;
1949  int fdno = fdFileno(fd);
1950  (void) fstat(fdno, &sb);
1951  if (S_ISREG(sb.st_mode))
1952  return fdRead(fd, buf, count);
1953  }
1954 
1955  UFDONLY(fd);
1956  assert(fd->rd_timeoutsecs >= 0);
1957 
1958  for (total = 0; total < count; total += bytesRead) {
1959 
1960  int rc;
1961 
1962  bytesRead = 0;
1963 
1964  /* Is there data to read? */
1965  if (fd->bytesRemain == 0) return (ssize_t) total; /* XXX simulate EOF */
1966  rc = fdReadable(fd, fd->rd_timeoutsecs);
1967 
1968  switch (rc) {
1969  case -1: /* error */
1970  case 0: /* timeout */
1971  return (ssize_t) total;
1972  /*@notreached@*/ /*@switchbreak@*/ break;
1973  default: /* data to read */
1974  /*@switchbreak@*/ break;
1975  }
1976 
1977  rc = (int) fdRead(fd, buf + total, count - total);
1978 
1979  if (rc < 0) {
1980  switch (errno) {
1981  case EWOULDBLOCK:
1982  continue;
1983  /*@notreached@*/ /*@switchbreak@*/ break;
1984  default:
1985  /*@switchbreak@*/ break;
1986  }
1987 if (_rpmio_debug)
1988 fprintf(stderr, "*** read: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
1989  return rc;
1990  /*@notreached@*/ break;
1991  } else if (rc == 0) {
1992  return (ssize_t) total;
1993  /*@notreached@*/ break;
1994  }
1995  bytesRead = (size_t) rc;
1996  }
1997 
1998  return (ssize_t) count;
1999 }
2000 
2001 static ssize_t ufdWrite(void * cookie, const char * buf, size_t count)
2002  /*@globals fileSystem, internalState @*/
2003  /*@modifies fileSystem, internalState @*/
2004 {
2005  FD_t fd = c2f(cookie);
2006  size_t bytesWritten;
2007  size_t total = 0;
2008 
2009 #ifdef NOTYET
2010  if (fdGetIo(fd) == fdio) {
2011  struct stat sb;
2012  (void) fstat(fdGetFdno(fd), &sb);
2013  if (S_ISREG(sb.st_mode))
2014  return fdWrite(fd, buf, count);
2015  }
2016 #endif
2017 
2018  UFDONLY(fd);
2019 
2020  for (total = 0; total < count; total += bytesWritten) {
2021 
2022  int rc;
2023 
2024  bytesWritten = 0;
2025 
2026  /* Is there room to write data? */
2027  if (fd->bytesRemain == 0) {
2028 fprintf(stderr, "*** ufdWrite fd %p WRITE PAST END OF CONTENT\n", fd);
2029  return (ssize_t) total; /* XXX simulate EOF */
2030  }
2031  rc = fdWritable(fd, 2); /* XXX configurable? */
2032 
2033  switch (rc) {
2034  case -1: /* error */
2035  case 0: /* timeout */
2036  return (ssize_t) total;
2037  /*@notreached@*/ /*@switchbreak@*/ break;
2038  default: /* data to write */
2039  /*@switchbreak@*/ break;
2040  }
2041 
2042  rc = (int) fdWrite(fd, buf + total, count - total);
2043 
2044  if (rc < 0) {
2045  switch (errno) {
2046  case EWOULDBLOCK:
2047  continue;
2048  /*@notreached@*/ /*@switchbreak@*/ break;
2049  default:
2050  /*@switchbreak@*/ break;
2051  }
2052 if (_rpmio_debug)
2053 fprintf(stderr, "*** write: rc %d errno %d %s \"%s\"\n", rc, errno, strerror(errno), buf);
2054  return rc;
2055  /*@notreached@*/ break;
2056  } else if (rc == 0) {
2057  return (ssize_t) total;
2058  /*@notreached@*/ break;
2059  }
2060  bytesWritten = (size_t) rc;
2061  }
2062 
2063  return (ssize_t) count;
2064 }
2065 
2066 static int ufdSeek(void * cookie, _libio_pos_t pos, int whence)
2067  /*@globals fileSystem, internalState @*/
2068  /*@modifies fileSystem, internalState @*/
2069 {
2070  FD_t fd = c2f(cookie);
2071 
2072  switch (urlType(fd->u)) {
2073  case URL_IS_UNKNOWN:
2074  case URL_IS_PATH:
2075  break;
2076  case URL_IS_DASH:
2077  case URL_IS_HKP:
2078  case URL_IS_FTP:
2079  case URL_IS_HTTP:
2080  case URL_IS_HTTPS:
2081  case URL_IS_MONGO: /* XXX FIXME */
2082  default:
2083  return -2;
2084  /*@notreached@*/ break;
2085  }
2086  return fdSeek(cookie, pos, whence);
2087 }
2088 
2089 /*@-usereleased@*/ /* LCL: fd handling is tricky here. */
2090 int ufdClose( /*@only@*/ void * cookie)
2091 {
2092  FD_t fd = c2f(cookie);
2093 
2094  UFDONLY(fd);
2095 
2096  if (fd->u) {
2097  urlinfo u = (urlinfo) fd->u;
2098 
2099 /*@-evalorder @*/
2100  if (fd == u->data)
2101  fd = u->data = fdFree(fd, "grab data (ufdClose persist)");
2102  else
2103  fd = fdFree(fd, "grab data (ufdClose)");
2104 assert(fd != NULL);
2105  (void) urlFree(fd->u, "url (ufdClose)");
2106  fd->u = NULL;
2107  u->ctrl = fdFree(u->ctrl, "grab ctrl (ufdClose)");
2108 /*@=evalorder @*/
2109 
2110  if (urlType(u) == URL_IS_FTP) {
2111 
2112  /* XXX if not using libio, lose the fp from fpio */
2113  { FILE * fp;
2114  /*@+voidabstract -nullpass@*/
2115  fp = fdGetFILE(fd);
2116  if (noLibio && fp)
2117  fdSetFp(fd, NULL);
2118  /*@=voidabstract =nullpass@*/
2119  }
2120 
2121  /*
2122  * Non-error FTP has 4 refs on the data fd:
2123  * "persist data (ufdOpen FTP)" rpmio.c:888
2124  * "grab data (ufdOpen FTP)" rpmio.c:892
2125  * "open data (ftpReq)" ftp.c:633
2126  * "fopencookie" rpmio.c:1507
2127  *
2128  * Non-error FTP has 5 refs on the ctrl fd:
2129  * "persist ctrl" url.c:176
2130  * "grab ctrl (urlConnect FTP)" rpmio.c:404
2131  * "open ctrl" ftp.c:504
2132  * "grab data (ftpReq)" ftp.c:661
2133  * "open data (ftpReq)" ftp.c:662
2134  */
2135  if (fd->bytesRemain > 0) {
2136  if (fd->ftpFileDoneNeeded) {
2137  if (fdReadable(u->ctrl, 0) > 0)
2138  (void) ftpFileDone(u, fd);
2139  else
2140  (void) ftpAbort(u, fd);
2141  }
2142  } else {
2143  int rc;
2144  /* XXX STOR et al require close before ftpFileDone */
2145  /*@-refcounttrans@*/
2146  rc = fdClose(fd);
2147  /*@=refcounttrans@*/
2148 #if 0 /* XXX error exit from ufdOpen does not have this set */
2149  assert(fd->ftpFileDoneNeeded != 0);
2150 #endif
2151  /*@-compdef@*/ /* FIX: u->data undefined */
2152  if (fd->ftpFileDoneNeeded)
2153  (void) ftpFileDone(u, fd);
2154  /*@=compdef@*/
2155  return rc;
2156  }
2157  }
2158 
2159  /* XXX Why not (u->urltype == URL_IS_HTTP) ??? */
2160  /* XXX Why not (u->urltype == URL_IS_HTTPS) ??? */
2161  /* XXX Why not (u->urltype == URL_IS_HKP) ??? */
2162  if (u->scheme != NULL
2163  && (!strncmp(u->scheme, "http", sizeof("http")-1) || !strncmp(u->scheme, "hkp", sizeof("hkp")-1)))
2164  {
2165  /*
2166  * HTTP has 4 (or 5 if persistent malloc) refs on the fd:
2167  * "persist ctrl" url.c:177
2168  * "grab ctrl (ufdOpen HTTP)" rpmio.c:924
2169  * "grab data (ufdOpen HTTP)" rpmio.c:928
2170  * "open ctrl (httpReq)" ftp.c:382
2171  * "open data (httpReq)" ftp.c:435
2172  */
2173 
2174 /*@-evalorder @*/
2175  if (fd == u->ctrl)
2176  fd = u->ctrl = fdFree(fd, "open data (ufdClose HTTP persist ctrl)");
2177  else if (fd == u->data)
2178  fd = u->data = fdFree(fd, "open data (ufdClose HTTP persist data)");
2179  else
2180  fd = fdFree(fd, "open data (ufdClose HTTP)");
2181 /*@=evalorder @*/
2182 
2183  /* XXX if not using libio, lose the fp from fpio */
2184  { FILE * fp;
2185  /*@+voidabstract -nullpass@*/
2186  fp = fdGetFILE(fd);
2187  if (noLibio && fp)
2188  fdSetFp(fd, NULL);
2189  /*@=voidabstract =nullpass@*/
2190  }
2191 
2192  /* If content remains, then don't persist. */
2193 assert(fd != NULL);
2194  if (fd->bytesRemain > 0) {
2195  fd->persist = 0;
2196  }
2197  fd->contentLength = fd->bytesRemain = -1;
2198 
2199  /* If persisting, then Fclose will juggle refcounts. */
2200  if (fd->persist && (fd == u->ctrl || fd == u->data))
2201  return 0;
2202  }
2203  }
2204  return fdClose(fd);
2205 }
2206 /*@=usereleased@*/
2207 
2208 /*@-nullstate@*/ /* FIX: u->{ctrl,data}->u undef after XurlLink. */
2209 /*@null@*/ FD_t ftpOpen(const char *url, /*@unused@*/ int flags,
2210  /*@unused@*/ mode_t mode, /*@out@*/ urlinfo *uret)
2211  /*@modifies *uret @*/
2212 {
2213  urlinfo u = NULL;
2214  FD_t fd = NULL;
2215 
2216 #if 0 /* XXX makeTempFile() heartburn */
2217  assert(!(flags & O_RDWR));
2218 #endif
2219  if (urlConnect(url, &u) < 0)
2220  goto exit;
2221 
2222  if (u->data == NULL)
2223  u->data = fdNew("persist data (ftpOpen)");
2224 
2225 assert(u->data != NULL);
2226 /*@-unqualifiedtrans@*/
2227  if (u->data->u == NULL)
2228  fd = u->data = fdLink(u->data, "grab data (ftpOpen persist data)");
2229  else
2230  fd = fdNew("grab data (ftpOpen)");
2231 /*@=unqualifiedtrans@*/
2232 
2233  if (fd != NULL) {
2234  fdSetOpen(fd, url, flags, mode);
2235  fdSetIo(fd, ufdio);
2236  fd->ftpFileDoneNeeded = 0;
2238  fd->contentLength = fd->bytesRemain = -1;
2239 /*@-usereleased@*/
2240  fd->u = urlLink(u, "url (ufdOpen FTP)");
2241 /*@=usereleased@*/
2242  }
2243 
2244 exit:
2245  if (uret)
2246  *uret = u;
2247  /*@-refcounttrans@*/
2248  return fd;
2249  /*@=refcounttrans@*/
2250 }
2251 /*@=nullstate@*/
2252 
2253 static /*@null@*/ FD_t ufdOpen(const char * url, int flags, mode_t mode)
2254  /*@globals h_errno, fileSystem, internalState @*/
2255  /*@modifies fileSystem, internalState @*/
2256 {
2257  FD_t fd = NULL;
2258  const char * cmd;
2259  urlinfo u;
2260  const char * path;
2261  urltype ut = urlPath(url, &path);
2262 
2263 if (_rpmio_debug)
2264 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
2265 
2266 /*@-usereleased@*/
2267  switch (ut) {
2268  case URL_IS_FTP:
2269  fd = ftpOpen(url, flags, mode, &u);
2270  if (fd == NULL || u == NULL)
2271  break;
2272 
2273  /* XXX W2DO? use STOU rather than STOR to prevent clobbering */
2274  cmd = ((flags & O_WRONLY)
2275  ? ((flags & O_APPEND) ? "APPE" :
2276  ((flags & O_CREAT) ? "STOR" : "STOR"))
2277  : ((flags & O_CREAT) ? "STOR" : "RETR"));
2278  u->openError = ftpReq(fd, cmd, path);
2279  if (u->openError < 0) {
2280  /* XXX make sure that we can exit through ufdClose */
2281  fd = fdLink(fd, "error data (ufdOpen FTP)");
2282  } else {
2283  fd->bytesRemain = ((!strcmp(cmd, "RETR"))
2284  ? fd->contentLength : -1);
2285  fd->wr_chunked = 0;
2286  }
2287  break;
2288  case URL_IS_HKP:
2289  case URL_IS_HTTP:
2290  case URL_IS_HTTPS:
2291 #ifdef WITH_NEON
2292  fd = davOpen(url, flags, mode, &u);
2293 #else
2294  fd = httpOpen(url, flags, mode, &u);
2295 #endif
2296  if (fd == NULL || u == NULL)
2297  break;
2298 
2299  cmd = ((flags & O_WRONLY)
2300  ? ((flags & O_APPEND) ? "PUT" :
2301  ((flags & O_CREAT) ? "PUT" : "PUT"))
2302  : "GET");
2303 #ifdef WITH_NEON
2304  u->openError = davReq(fd, cmd, path);
2305 #else
2306  u->openError = httpReq(fd, cmd, path);
2307 #endif
2308  if (u->openError < 0) {
2309  /* XXX rpmdav doesn't behave consistently with the rest...*/
2310 #ifndef WITH_NEON
2311  /* XXX make sure that we can exit through ufdClose */
2312  fd = u->ctrl = fdLink(fd, "error ctrl (ufdOpen HTTP)");
2313  fd = u->data fdLink(fd, "error data (ufdOpen HTTP)");
2314 #endif
2315  } else {
2316  fd->bytesRemain = ((!strcmp(cmd, "GET"))
2317  ? fd->contentLength : -1);
2318  fd->wr_chunked = ((!strcmp(cmd, "PUT"))
2319  ? fd->wr_chunked : 0);
2320  }
2321  break;
2322  case URL_IS_MONGO: /* XXX FIXME */
2323  break;
2324  case URL_IS_DASH:
2325  assert(!(flags & O_RDWR));
2326  fd = fdDup( ((flags & O_WRONLY) ? STDOUT_FILENO : STDIN_FILENO) );
2327  if (fd) {
2328  fdSetOpen(fd, url, flags, mode);
2329  fdSetIo(fd, ufdio);
2330  fd->rd_timeoutsecs = 600; /* XXX W2DO? 10 mins? */
2331  fd->contentLength = fd->bytesRemain = -1;
2332  }
2333  break;
2334  case URL_IS_PATH:
2335  case URL_IS_UNKNOWN:
2336  default:
2337  fd = fdOpen(path, flags, mode);
2338  if (fd) {
2339  fdSetIo(fd, ufdio);
2340  fd->rd_timeoutsecs = 1;
2341  fd->contentLength = fd->bytesRemain = -1;
2342  }
2343  break;
2344  }
2345 
2346  if (fd == NULL) return NULL;
2347  if (Fileno(fd) < 0) {
2348  (void) ufdClose(fd);
2349  return NULL;
2350  }
2351 /*@=usereleased@*/
2352 DBGIO(fd, (stderr, "<--\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
2353  return fd;
2354 }
2355 
2356 /*@-type@*/ /* LCL: function typedefs */
2357 static struct FDIO_s ufdio_s = {
2358  ufdRead, ufdWrite, ufdSeek, ufdClose, NULL, NULL, NULL,
2359 };
2360 /*@=type@*/
2361 
2362 FDIO_t ufdio = /*@-compmempass@*/ &ufdio_s /*@=compmempass@*/ ;
2363 
2364 /* =============================================================== */
2365 /*@observer@*/
2366 static const char * getFdErrstr (FD_t fd)
2367  /*@*/
2368 {
2369  const char *errstr = NULL;
2370 
2371 #if defined(WITH_ZLIB)
2372  if (fdGetIo(fd) == gzdio) {
2373  errstr = (const char *)fd->errcookie;
2374  } else
2375 #endif /* WITH_ZLIB */
2376 
2377 #if defined(WITH_BZIP2)
2378  if (fdGetIo(fd) == bzdio) {
2379  errstr = (const char *)fd->errcookie;
2380  } else
2381 #endif
2382 
2383 #if defined(WITH_XZ)
2384  if (fdGetIo(fd) == lzdio) {
2385  errstr = (const char *)fd->errcookie;
2386  } else
2387  if (fdGetIo(fd) == xzdio) {
2388  errstr = (const char *)fd->errcookie;
2389  } else
2390 #endif
2391 
2392  {
2393  errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
2394  }
2395 
2396  return errstr;
2397 }
2398 
2399 /* =============================================================== */
2400 
2401 const char *Fstrerror(FD_t fd)
2402 {
2403  if (fd == NULL)
2404  return (errno ? strerror(errno) : "");
2405  FDSANE(fd);
2406  return getFdErrstr(fd);
2407 }
2408 
2409 #define FDIOVEC(_fd, _vec) \
2410  ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
2411 
2412 size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
2413  fdio_read_function_t _read;
2414  int rc;
2415 
2416  FDSANE(fd);
2417 DBGIO(fd, (stderr, "==> Fread(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
2418 
2419  if (fdGetIo(fd) == fpio) {
2420  /*@+voidabstract -nullpass@*/
2421  rc = (int) fread(buf, size, nmemb, fdGetFILE(fd));
2422  /*@=voidabstract =nullpass@*/
2423  return (size_t) rc;
2424  }
2425 
2426  /*@-nullderef@*/
2427  _read = FDIOVEC(fd, read);
2428  /*@=nullderef@*/
2429 
2430  rc = (int) (_read ? (*_read) (fd, (char *)buf, size * nmemb) : -2);
2431  return (size_t) rc;
2432 }
2433 
2434 size_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
2435 {
2436  fdio_write_function_t _write;
2437  int rc;
2438 
2439  FDSANE(fd);
2440 DBGIO(fd, (stderr, "==> Fwrite(%p,%u,%u,%p) %s\n", buf, (unsigned)size, (unsigned)nmemb, (fd ? fd : NULL), fdbg(fd)));
2441 
2442  if (fdGetIo(fd) == fpio) {
2443  /*@+voidabstract -nullpass@*/
2444  rc = (int) fwrite(buf, size, nmemb, fdGetFILE(fd));
2445  /*@=voidabstract =nullpass@*/
2446  return (size_t) rc;
2447  }
2448 
2449  /*@-nullderef@*/
2450  _write = FDIOVEC(fd, write);
2451  /*@=nullderef@*/
2452 
2453  rc = (int) (_write ? _write(fd, (const char *)buf, size * nmemb) : -2);
2454  return (size_t) rc;
2455 }
2456 
2457 int Fseek(FD_t fd, _libio_off_t offset, int whence)
2458 {
2459  fdio_seek_function_t _seek;
2460 #ifdef USE_COOKIE_SEEK_POINTER
2461  _IO_off64_t o64 = offset;
2462  _libio_pos_t pos = &o64;
2463 #else
2464  _libio_pos_t pos = offset;
2465 #endif
2466 
2467  long int rc;
2468 
2469  FDSANE(fd);
2470 DBGIO(fd, (stderr, "==> Fseek(%p,%ld,%d) %s\n", fd, (long)offset, whence, fdbg(fd)));
2471 
2472  if (fdGetIo(fd) == fpio)
2473  return fseek(fdGetFILE(fd), (long)offset, whence);
2474 
2475  /*@-nullderef@*/
2476  _seek = FDIOVEC(fd, seek);
2477  /*@=nullderef@*/
2478 
2479  rc = (_seek ? _seek(fd, pos, whence) : -2);
2480  return rc;
2481 }
2482 
2483 long Ftell(FD_t fd)
2484 {
2485  long int rc = -2;
2486 
2487  FDSANE(fd);
2488 
2489  if (fdGetIo(fd) == fpio)
2490  rc = ftell(fdGetFILE(fd));
2491  else
2492  errno = EBADF;
2493 DBGIO(fd, (stderr, "<== Ftell(%p) rc %ld %s\n", fd, rc, fdbg(fd)));
2494  return rc;
2495 }
2496 
2497 void Rewind(FD_t fd)
2498 {
2499  FDSANE(fd);
2500 DBGIO(fd, (stderr, "==> Rewind(%p) %s\n", fd, fdbg(fd)));
2501 
2502  if (fdGetIo(fd) == fpio)
2503  rewind(fdGetFILE(fd));
2504 }
2505 
2506 int Fgetpos(FD_t fd, fpos_t *pos)
2507 {
2508  int rc = -2;
2509 
2510  FDSANE(fd);
2511 
2512  if (fdGetIo(fd) == fpio)
2513  rc = fgetpos(fdGetFILE(fd), pos);
2514  else
2515  errno = EBADF;
2516 DBGIO(fd, (stderr, "<== Fgetpos(%p,%p) rc %d %s\n", fd, pos, rc, fdbg(fd)));
2517  return rc;
2518 }
2519 
2520 int Fsetpos(FD_t fd, fpos_t *pos)
2521 {
2522  int rc = -2;
2523 
2524  FDSANE(fd);
2525 
2526  if (fdGetIo(fd) == fpio)
2527  return fgetpos(fdGetFILE(fd), pos);
2528 
2529  errno = EBADF;
2530 DBGIO(fd, (stderr, "<== Fsetpos(%p,%p) rc %d %s\n", fd, pos, rc, fdbg(fd)));
2531  return rc;
2532 }
2533 
2534 int Fclose(FD_t fd)
2535 {
2536  int rc = 0, ec = 0;
2537 
2538  FDSANE(fd);
2539 DBGIO(fd, (stderr, "==> Fclose(%p) %s\n", (fd ? fd : NULL), fdbg(fd)));
2540 
2541 /*@-usereleased@*/
2542  fd = fdLink(fd, "Fclose");
2543  if (fd != NULL)
2544  while (fd->nfps >= 0) {
2545  FDSTACK_t * fps = &fd->fps[fd->nfps];
2546 
2547  if (fps->io == fpio) {
2548  FILE *fp;
2549  int fpno;
2550 
2551  /*@+voidabstract -nullpass@*/
2552  fp = fdGetFILE(fd);
2553  fpno = fileno(fp);
2554  /*@=voidabstract =nullpass@*/
2555  /* XXX persistent HTTP/1.1 returns the previously opened fp */
2556  if (fd->nfps > 0 && fpno == -1 &&
2557  fd->fps[fd->nfps-1].io == ufdio &&
2558  fd->fps[fd->nfps-1].fp == fp &&
2559  (fd->fps[fd->nfps-1].fdno >= 0 || fd->req != NULL))
2560  {
2561  int hadreqpersist = (fd->req != NULL);
2562 
2563  if (fp)
2564  rc = fflush(fp);
2565  fd->nfps--;
2566  /*@-refcounttrans@*/
2567  rc = ufdClose(fd);
2568  /*@=refcounttrans@*/
2569  if (fdGetFdno(fd) >= 0)
2570  break;
2571  if (!fd->persist)
2572  hadreqpersist = 0;
2573  fdSetFp(fd, NULL);
2574  fd->nfps++;
2575  if (fp) {
2576  /* HACK: flimsy Keepalive wiring. */
2577  if (hadreqpersist) {
2578 #ifdef NOTYET /* XXX not quite right yet. */
2579  (void) davDisconnect(fd);
2580  fd->req = NULL;
2581 #endif
2582  fd->nfps--;
2583 /*@-exposetrans@*/
2584  fdSetFp(fd, fp);
2585 /*@=exposetrans@*/
2586 /*@-refcounttrans@*/
2587  (void) fdClose(fd);
2588 /*@=refcounttrans@*/
2589  fdSetFp(fd, NULL);
2590  fd->nfps++;
2591 /*@-refcounttrans@*/
2592  (void) fdClose(fd);
2593 /*@=refcounttrans@*/
2594  } else
2595  rc = fclose(fp);
2596  }
2597  fdPop(fd);
2598  if (noLibio)
2599  fdSetFp(fd, NULL);
2600  } else {
2601  if (fp)
2602  rc = fclose(fp);
2603  if (fpno == -1) {
2604  fd = fdFree(fd, "fopencookie (Fclose)");
2605  fdPop(fd);
2606  }
2607  }
2608  } else {
2609  /*@-nullderef@*/
2610  fdio_close_function_t _close = FDIOVEC(fd, close);
2611  /*@=nullderef@*/
2612  rc = _close(fd);
2613  }
2614  if (fd == NULL || fd->nfps == 0) /* XXX fd != NULL ever */
2615  break;
2616  if (ec == 0 && rc)
2617  ec = rc;
2618  fdPop(fd);
2619  }
2620  fd = fdFree(fd, "Fclose");
2621  return ec;
2622 /*@=usereleased@*/
2623 }
2624 
2642 static inline void cvtfmode (const char *m,
2643  /*@out@*/ char *stdio, size_t nstdio,
2644  /*@out@*/ char *other, size_t nother,
2645  /*@out@*/ const char **end, /*@out@*/ int * f)
2646  /*@modifies *stdio, *other, *end, *f @*/
2647 {
2648  int flags = 0;
2649  char c;
2650 
2651  switch (*m) {
2652  case 'a':
2653  flags |= O_WRONLY | O_CREAT | O_APPEND;
2654  if (--nstdio > 0) *stdio++ = *m;
2655  break;
2656  case 'w':
2657  flags |= O_WRONLY | O_CREAT | O_TRUNC;
2658  if (--nstdio > 0) *stdio++ = *m;
2659  break;
2660  case 'r':
2661  flags |= O_RDONLY;
2662  if (--nstdio > 0) *stdio++ = *m;
2663  break;
2664  default:
2665  *stdio = '\0';
2666  return;
2667  /*@notreached@*/ break;
2668  }
2669  m++;
2670 
2671  while ((c = *m++) != '\0') {
2672  switch (c) {
2673  case '.':
2674  /*@switchbreak@*/ break;
2675  case '+':
2676  flags &= ~(O_RDONLY|O_WRONLY);
2677  flags |= O_RDWR;
2678  if (--nstdio > 0) *stdio++ = c;
2679  continue;
2680  /*@notreached@*/ /*@switchbreak@*/ break;
2681  case 'x': /* glibc: open file exclusively. */
2682  flags |= O_EXCL;
2683  /*@fallthrough@*/
2684  case 'm': /* glibc: mmap'd reads */
2685  case 'c': /* glibc: no cancel */
2686 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ >= 3
2687  if (--nstdio > 0) *stdio++ = c;
2688 #endif
2689  continue;
2690  /*@notreached@*/ /*@switchbreak@*/ break;
2691  case 'b':
2692  if (--nstdio > 0) *stdio++ = c;
2693  continue;
2694  /*@notreached@*/ /*@switchbreak@*/ break;
2695  default:
2696  if (--nother > 0) *other++ = c;
2697  continue;
2698  /*@notreached@*/ /*@switchbreak@*/ break;
2699  }
2700  break;
2701  }
2702  if (c == '\0') m--; /* one too far */
2703 
2704  *stdio = *other = '\0';
2705  if (end != NULL)
2706  *end = (*m != '\0' ? m : NULL);
2707  if (f != NULL)
2708  *f = flags;
2709 }
2710 
2711 #if _USE_LIBIO
2712 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
2713 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
2714 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
2715 #endif
2716 #endif
2717 
2718 FD_t Fdopen(FD_t ofd, const char *fmode)
2719 {
2720  char stdio[20], other[20], zstdio[40+1];
2721  const char *end = NULL;
2722  FDIO_t iof = NULL;
2723  FD_t fd = ofd;
2724 
2725 if (_rpmio_debug)
2726 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
2727  FDSANE(fd);
2728 
2729  if (fmode == NULL)
2730  return NULL;
2731 
2732  cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
2733  if (stdio[0] == '\0')
2734  return NULL;
2735  zstdio[0] = '\0';
2736  (void) stpcpy( stpcpy(zstdio, stdio), other);
2737 
2738  if (end == NULL && other[0] == '\0')
2739  /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
2740 
2741  if (end && *end) {
2742  if (!strcmp(end, "fdio")) {
2743  iof = fdio;
2744 #if defined(WITH_ZLIB)
2745  } else if (!strcmp(end, "gzdio")) {
2746  iof = gzdio;
2747  /*@-internalglobs@*/
2748  fd = iof->_fdopen(fd, zstdio);
2749  /*@=internalglobs@*/
2750 #endif
2751 #if defined(WITH_BZIP2)
2752  } else if (!strcmp(end, "bzdio")) {
2753  iof = bzdio;
2754  /*@-internalglobs@*/
2755  fd = iof->_fdopen(fd, zstdio);
2756  /*@=internalglobs@*/
2757 #endif
2758 #if defined(WITH_XZ)
2759  } else if (!strcmp(end, "lzdio")) {
2760  iof = lzdio;
2761  fd = iof->_fdopen(fd, zstdio);
2762  } else if (!strcmp(end, "xzdio")) {
2763  iof = xzdio;
2764  fd = iof->_fdopen(fd, zstdio);
2765 #endif
2766  } else if (!strcmp(end, "ufdio")) {
2767  iof = ufdio;
2768  } else if (!strcmp(end, "fpio")) {
2769  iof = fpio;
2770  if (noLibio) {
2771  int fdno = Fileno(fd);
2772  FILE * fp = fdopen(fdno, stdio);
2773 /*@+voidabstract -nullpass@*/
2774 if (_rpmio_debug)
2775 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
2776 /*@=voidabstract =nullpass@*/
2777  if (fp == NULL)
2778  return NULL;
2779  /* XXX gzdio/bzdio use fp for private data */
2780  /*@+voidabstract@*/
2781  if (fdGetFp(fd) == NULL)
2782  fdSetFp(fd, fp);
2783  fdPush(fd, fpio, fp, fdno); /* Push fpio onto stack */
2784  /*@=voidabstract@*/
2785  }
2786  }
2787  } else if (other[0] != '\0') {
2788  for (end = other; *end && strchr("0123456789fh", *end); end++)
2789  {};
2790  if (*end == '\0') {
2791 #if defined(WITH_ZLIB)
2792  iof = gzdio;
2793  /*@-internalglobs@*/
2794  fd = iof->_fdopen(fd, zstdio);
2795  /*@=internalglobs@*/
2796 #endif
2797  }
2798  }
2799  if (iof == NULL)
2800  /*@-refcounttrans -retalias@*/ return fd; /*@=refcounttrans =retalias@*/
2801 
2802  if (!noLibio) {
2803  FILE * fp = NULL;
2804 
2805 #if _USE_LIBIO
2806  { cookie_io_functions_t ciof;
2807  ciof.read = iof->read;
2808  ciof.write = iof->write;
2809  ciof.seek = iof->seek;
2810  ciof.close = iof->close;
2811  fp = fopencookie(fd, stdio, ciof);
2812 DBGIO(fd, (stderr, "<-- fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
2813  }
2814 #endif
2815 
2816  if (fp) {
2817  /* XXX gzdio/bzdio use fp for private data */
2818  /*@+voidabstract -nullpass@*/
2819  if (fdGetFp(fd) == NULL)
2820  fdSetFp(fd, fp);
2821  fdPush(fd, fpio, fp, fileno(fp)); /* Push fpio onto stack */
2822  /*@=voidabstract =nullpass@*/
2823  fd = fdLink(fd, "fopencookie");
2824  }
2825  }
2826 
2827 /*@-refcounttrans -retalias -usereleased @*/
2828 DBGIO(fd, (stderr, "<== Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
2829  return fd;
2830 /*@=refcounttrans =retalias =usereleased @*/
2831 }
2832 
2833 FD_t Fopen(const char *path, const char *_fmode)
2834 {
2835  const char * fmode = NULL;
2836  char stdio[20], other[20];
2837  const char *end = NULL;
2838  mode_t perms = 0666;
2839  int flags = 0;
2840  FD_t fd = NULL;
2841 
2842  if (path == NULL || _fmode == NULL)
2843  goto exit;
2844 /*@-globs -mods@*/
2845  fmode = rpmExpand(_fmode, NULL);
2846 /*@=globs =mods@*/
2847 
2848 if (_rpmio_debug)
2849 fprintf(stderr, "==> Fopen(%s, %s)\n", path, fmode);
2850 
2851  stdio[0] = '\0';
2852  cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
2853  if (stdio[0] == '\0')
2854  goto exit;
2855 
2856  if (end == NULL || !strcmp(end, "fdio")) {
2857  fd = fdOpen(path, flags, perms);
2858  if (fdFileno(fd) < 0) {
2859  if (fd) (void) fdClose(fd);
2860  fd = NULL;
2861  goto exit;
2862  }
2863  } else {
2864  FILE *fp;
2865  int fdno;
2866  int isHTTP = 0;
2867 
2868  /* XXX gzdio/bzdio/lzdio through here too */
2869 
2870  switch (urlIsURL(path)) {
2871  case URL_IS_HKP:
2872  case URL_IS_HTTP:
2873  case URL_IS_HTTPS:
2874  isHTTP = 1;
2875  /*@fallthrough@*/
2876  case URL_IS_PATH:
2877  case URL_IS_DASH:
2878  case URL_IS_FTP:
2879  case URL_IS_UNKNOWN:
2880  fd = ufdOpen(path, flags, perms);
2881  if (fd == NULL || !(fdFileno(fd) >= 0 || fd->req != NULL)) {
2882  if (fd) (void) fdClose(fd);
2883  fd = NULL;
2884  goto exit;
2885  }
2886  break;
2887  case URL_IS_MONGO: /* XXX FIXME */
2888  default:
2889  if (fd) (void) fdClose(fd);
2890  fd = NULL;
2891  goto exit;
2892  /*@notreached@*/ break;
2893  }
2894 
2895  /* XXX persistent HTTP/1.1 returns the previously opened fp */
2896  if (isHTTP && ((fp = (FILE *) fdGetFp(fd)) != NULL)
2897  && ((fdno = fdGetFdno(fd)) >= 0 || fd->req != NULL))
2898  {
2899  /*@+voidabstract@*/
2900  fdPush(fd, fpio, fp, fileno(fp)); /* Push fpio onto stack */
2901  /*@=voidabstract@*/
2902  goto exit;
2903  }
2904  }
2905 
2906  if (fd)
2907  fd = Fdopen(fd, fmode);
2908 exit:
2909 
2910 if (_rpmio_debug)
2911 fprintf(stderr, "<== Fopen(%s, %s) fd %p\n", path, fmode, fd);
2912  fmode = _free(fmode);
2913  return fd;
2914 }
2915 
2916 int Fflush(FD_t fd)
2917 {
2918  void * vh;
2919  if (fd == NULL) return -1;
2920  if (fdGetIo(fd) == fpio)
2921  /*@+voidabstract -nullpass@*/
2922  return fflush(fdGetFILE(fd));
2923  /*@=voidabstract =nullpass@*/
2924 
2925  vh = fdGetFp(fd);
2926 #if defined(WITH_ZLIB)
2927  if (vh && fdGetIo(fd) == gzdio && gzdio->_flush != NULL)
2928  return (*gzdio->_flush) ((void *)fd);
2929 #endif
2930 #if defined(WITH_BZIP2)
2931  if (vh && fdGetIo(fd) == bzdio && bzdio->_flush != NULL)
2932  return (*bzdio->_flush) ((void *)fd);
2933 #endif
2934 #if defined(WITH_XZ)
2935  if (vh && fdGetIo(fd) == lzdio && lzdio->_flush != NULL)
2936  return (*lzdio->_flush) ((void *)fd);
2937  if (vh && fdGetIo(fd) == xzdio && xzdio->_flush != NULL)
2938  return (*xzdio->_flush) ((void *)fd);
2939 #endif
2940 
2941  return 0;
2942 }
2943 
2944 int Ferror(FD_t fd)
2945 {
2946  int i, rc = 0;
2947 
2948  if (fd == NULL) return -1;
2949  if (fd->req != NULL) {
2950  /* HACK: flimsy wiring for neon errors. */
2951  rc = (fd->req == (void *)-1 || fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2952  } else
2953  for (i = fd->nfps; rc == 0 && i >= 0; i--) {
2954  FDSTACK_t * fps = &fd->fps[i];
2955  int ec;
2956 
2957  if (fps->io == fpio) {
2958  /*@+voidabstract -nullpass@*/
2959  ec = ferror(fdGetFILE(fd));
2960  /*@=voidabstract =nullpass@*/
2961 #if defined(WITH_ZLIB)
2962  } else if (fps->io == gzdio) {
2963  ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2964  i--; /* XXX fdio under gzdio always has fdno == -1 */
2965 #endif
2966 #if defined(WITH_BZIP2)
2967  } else if (fps->io == bzdio) {
2968  ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2969  i--; /* XXX fdio under bzdio always has fdno == -1 */
2970 #endif
2971 #if defined(WITH_XZ)
2972  } else if (fps->io == lzdio) {
2973  ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2974  i--; /* XXX fdio under lzdio always has fdno == -1 */
2975  } else if (fps->io == xzdio) {
2976  ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
2977  i--; /* XXX fdio under xzdio always has fdno == -1 */
2978 #endif
2979  } else {
2980  /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
2981  ec = (fdFileno(fd) < 0 ? -1 : 0);
2982  }
2983 
2984  if (rc == 0 && ec)
2985  rc = ec;
2986  }
2987 DBGIO(fd, (stderr, "<== Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
2988  return rc;
2989 }
2990 
2991 int Fileno(FD_t fd)
2992 {
2993  int i, rc = -1;
2994 
2995  if (fd == NULL)
2996  return -1;
2997  if (fd->req != NULL)
2998  rc = 123456789; /* HACK: https has no steenkin fileno. */
2999  else
3000  for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
3001  rc = fd->fps[i].fdno;
3002  }
3003 
3004 DBGIO(fd, (stderr, "<== Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
3005  return rc;
3006 }
3007 
3008 /* XXX this is naive */
3009 int Fcntl(FD_t fd, int op, void *lip)
3010 {
3011  return fcntl(Fileno(fd), op, lip);
3012 }
3013 
3014 /* =============================================================== */
3015 /* Helper routines that may be generally useful.
3016  */
3017 int rpmioMkpath(const char * path, mode_t mode, uid_t uid, gid_t gid)
3018 {
3019  char * d, * de;
3020  int created = 0;
3021  int rc;
3022 
3023  if (path == NULL || *path == '\0')
3024  return -1;
3025  d = (char *) alloca(strlen(path)+2);
3026  de = stpcpy(d, path);
3027  de[1] = '\0';
3028  for (de = d; *de != '\0'; de++) {
3029  struct stat st;
3030  char savec;
3031 
3032  while (*de && *de != '/') de++;
3033  savec = de[1];
3034  de[1] = '\0';
3035 
3036  rc = Stat(d, &st);
3037  if (rc) {
3038  switch(errno) {
3039  default:
3040  return errno;
3041  /*@notreached@*/ /*@switchbreak@*/ break;
3042  case ENOENT:
3043  /*@switchbreak@*/ break;
3044  }
3045  rc = Mkdir(d, mode);
3046  if (rc)
3047  return errno;
3048  created = 1;
3049  if (!(uid == (uid_t) -1 && gid == (gid_t) -1)) {
3050  rc = Chown(d, uid, gid);
3051  if (rc)
3052  return errno;
3053  }
3054  } else if (!S_ISDIR(st.st_mode)) {
3055  return ENOTDIR;
3056  }
3057  de[1] = savec;
3058  }
3059  rc = 0;
3060  if (created)
3061  rpmlog(RPMLOG_DEBUG, D_("created directory(s) %s mode 0%o\n"),
3062  path, (unsigned)mode);
3063  return rc;
3064 }
3065 
3066 #define _PATH "/bin:/usr/bin:/sbin:/usr/sbin"
3067 /*@unchecked@*/ /*@observer@*/
3068 static const char *_path = _PATH;
3069 
3070 #define alloca_strdup(_s) strcpy((char *)alloca(strlen(_s)+1), (_s))
3071 
3072 int rpmioAccess(const char * FN, const char * path, int mode)
3073 {
3074  char fn[4096];
3075  char * bn;
3076  char * r, * re;
3077  char * t, * te;
3078  int negate = 0;
3079  int rc = 0;
3080 
3081  /* Empty paths are always accessible. */
3082  if (FN == NULL || *FN == '\0')
3083  return 0;
3084 
3085  if (mode == 0)
3086  mode = X_OK;
3087 
3088  /* Strip filename out of its name space wrapper. */
3089  bn = (char *) alloca_strdup(FN);
3090  for (t = bn; t && *t; t++) {
3091  if (*t != '(')
3092  continue;
3093  *t++ = '\0';
3094 
3095  /* Permit negation on name space tests. */
3096  if (*bn == '!') {
3097  negate = 1;
3098  bn++;
3099  }
3100 
3101  /* Set access flags from name space marker. */
3102  if (strlen(bn) == 3
3103  && strchr("Rr_", bn[0]) != NULL
3104  && strchr("Ww_", bn[1]) != NULL
3105  && strchr("Xx_", bn[2]) != NULL) {
3106  mode = 0;
3107  if (strchr("Rr", bn[0]) != NULL)
3108  mode |= R_OK;
3109  if (strchr("Ww", bn[1]) != NULL)
3110  mode |= W_OK;
3111  if (strchr("Xx", bn[2]) != NULL)
3112  mode |= X_OK;
3113  if (mode == 0)
3114  mode = F_OK;
3115  } else if (!strcmp(bn, "exists"))
3116  mode = F_OK;
3117  else if (!strcmp(bn, "executable"))
3118  mode = X_OK;
3119  else if (!strcmp(bn, "readable"))
3120  mode = R_OK;
3121  else if (!strcmp(bn, "writable"))
3122  mode = W_OK;
3123 
3124  bn = t;
3125  te = bn + strlen(t) - 1;
3126  if (*te != ')') /* XXX syntax error, never exists */
3127  return 1;
3128  *te = '\0';
3129  break;
3130  }
3131 
3132  /* Empty paths are always accessible. */
3133  if (*bn == '\0')
3134  goto exit;
3135 
3136  /* Check absolute path for access. */
3137  if (*bn == '/') {
3138  rc = (Access(bn, mode) != 0 ? 1 : 0);
3139 if (_rpmio_debug)
3140 fprintf(stderr, "*** rpmioAccess(\"%s\", 0x%x) rc %d\n", bn, mode, rc);
3141  goto exit;
3142  }
3143 
3144  /* Find path to search. */
3145  if (path == NULL)
3146  path = getenv("PATH");
3147  if (path == NULL)
3148  path = _path;
3149  if (path == NULL) {
3150  rc = 1;
3151  goto exit;
3152  }
3153 
3154  /* Look for relative basename on PATH. */
3155  for (r = alloca_strdup(path); r != NULL && *r != '\0'; r = re) {
3156 
3157  /* Find next element, terminate current element. */
3158  for (re = r; (re = strchr(re, ':')) != NULL; re++) {
3159  if (!(re[1] == '/' && re[2] == '/'))
3160  /*@innerbreak@*/ break;
3161  }
3162  if (re && *re == ':')
3163  *re++ = '\0';
3164  else
3165  re = r + strlen(r);
3166 
3167  /* Expand ~/ to $HOME/ */
3168  fn[0] = '\0';
3169  t = fn;
3170  *t = '\0'; /* XXX redundant. */
3171  if (r[0] == '~' && r[1] == '/') {
3172  const char * home = getenv("HOME");
3173  if (home == NULL) /* XXX No HOME? */
3174  continue;
3175  if (strlen(home) > (sizeof(fn) - strlen(r))) /* XXX too big */
3176  continue;
3177  t = stpcpy(t, home);
3178  r++; /* skip ~ */
3179  }
3180  t = stpcpy(t, r);
3181  if (t[-1] != '/' && *bn != '/')
3182  *t++ = '/';
3183  t = stpcpy(t, bn);
3184  t = rpmCleanPath(fn);
3185  if (t == NULL) /* XXX can't happen */
3186  continue;
3187 
3188  /* Check absolute path for access. */
3189  rc = (Access(t, mode) != 0 ? 1 : 0);
3190 if (_rpmio_debug)
3191 fprintf(stderr, "*** rpmioAccess(\"%s\", 0x%x) rc %d\n", t, mode, rc);
3192  if (rc == 0)
3193  goto exit;
3194  }
3195 
3196  rc = 1;
3197 
3198 exit:
3199  if (negate)
3200  rc ^= 1;
3201  return rc;
3202 }
3203 
3204 #if defined(WITH_NSS) && !defined(__LCLINT__) /* XXX TODO: add nssDestroy */
3205 #ifdef __cplusplus
3206 extern "C" {
3207 #endif
3208 /*@-exportheader@*/
3209 extern void NSS_Shutdown(void);
3210 /*@=exportheader@*/
3211 #ifdef __cplusplus
3212 }
3213 #endif
3214 
3215 /*@unchecked@*/
3216 int _rpmnss_init = 0;
3217 #endif
3218 
3219 void rpmioClean(void)
3220 {
3221 /*@-nestedextern@*/
3222  extern rpmioPool _avxPool;
3223  extern rpmioPool _urlPool;
3224  extern rpmioPool _xarPool;
3225  extern rpmioPool _digPool;
3226  extern rpmioPool _rpmiobPool;
3227  extern rpmioPool _rpmvcPool;
3228  extern rpmioPool _rpmvtPool;
3229 /*@-shadow@*/
3230  extern rpmioPool _mirePool;
3231  extern rpmioPool _rpmbfPool;
3232  extern rpmioPool _rpmhkpPool;
3233  extern rpmioPool _htmlPool;
3234  extern rpmioPool _htPool;
3235  extern rpmioPool _ctxPool;
3236  extern rpmioPool _rpmsmPool;
3237  extern rpmioPool _rpmspPool;
3238  extern rpmioPool _rpmsxPool;
3239  extern rpmioPool _rpmsyckPool;
3240 /*@=shadow@*/
3241 
3242  extern rpmioPool _rpmasnPool;
3243  extern rpmioPool _rpmbagPool;
3244  extern rpmioPool _rpmcvsPool;
3245  extern rpmioPool _rpmgitPool;
3246  extern rpmioPool _rpmsetPool;
3247  extern rpmioPool _rpmsvnPool;
3248  extern rpmioPool _rpmtpmPool;
3249 
3250  extern rpmioPool _rpmaugPool;
3251  extern rpmioPool _rpmcudfPool;
3252  extern rpmioPool _rpmficlPool;
3253  extern rpmioPool _rpmjsPool;
3254  extern rpmioPool _rpmluavPool;
3255  extern rpmioPool _rpmluaPool;
3256  extern rpmioPool _rpmmgPool;
3257  extern rpmioPool _rpmmgoPool;
3258 #ifdef NOTYET
3259  extern rpmioPool _rpmnixPool;
3260 #endif
3261  extern rpmioPool _odbcPool;
3262  extern rpmioPool _rpmperlPool;
3263  extern rpmioPool _rpmpythonPool;
3264  extern rpmioPool _rpmrubyPool;
3265  extern rpmioPool _rpmsqlPool;
3266  extern rpmioPool _rpmsquirrelPool;
3267  extern rpmioPool _rpmtclPool;
3268 /*@=nestedextern@*/
3269 
3270 #if defined(WITH_LUA)
3271  (void) rpmluaFree(NULL);
3272 #endif
3273 #if defined(WITH_NEON)
3274  davDestroy();
3275 #endif
3276 #if defined(WITH_NSS) && !defined(__LCLINT__)
3277  if (_rpmnss_init) {
3278  (void) NSS_Shutdown();
3279  _rpmnss_init = 0;
3280  }
3281 #endif
3282  urlFreeCache();
3283 
3285  _rpmtclPool = rpmioFreePool(_rpmtclPool);
3287  _rpmsquirrelPool = rpmioFreePool(_rpmsquirrelPool);
3289  _rpmsqlPool = rpmioFreePool(_rpmsqlPool);
3291  _rpmrubyPool = rpmioFreePool(_rpmrubyPool);
3293  _rpmpythonPool = rpmioFreePool(_rpmpythonPool);
3295  _rpmperlPool = rpmioFreePool(_rpmperlPool);
3297  _rpmjsPool = rpmioFreePool(_rpmjsPool);
3298  _rpmficlI = rpmficlFree(_rpmficlI);
3299  _rpmficlPool = rpmioFreePool(_rpmficlPool);
3300 
3301  _rpmgitI = rpmgitFree(_rpmgitI);
3302  _rpmgitPool = rpmioFreePool(_rpmgitPool);
3303 
3304  _rpmaugI = rpmaugFree(_rpmaugI);
3305  _rpmaugPool = rpmioFreePool(_rpmaugPool);
3306  _rpmmgoI = rpmmgoFree(_rpmmgoI);
3307  _rpmmgoPool = rpmioFreePool(_rpmmgoPool);
3308 
3309  _rpmasnPool = rpmioFreePool(_rpmasnPool);
3310  _rpmbagPool = rpmioFreePool(_rpmbagPool);
3311  _rpmcvsPool = rpmioFreePool(_rpmcvsPool);
3312  _odbcPool = rpmioFreePool(_odbcPool);
3313  _rpmsetPool = rpmioFreePool(_rpmsetPool);
3314  _rpmsvnPool = rpmioFreePool(_rpmsvnPool);
3315  _rpmtpmPool = rpmioFreePool(_rpmtpmPool);
3316 
3317 #ifdef NOTYET /* XXX FIXME: dig out the recursion deadlock. */
3318  _rpmnixI = rpmnixFree(_rpmnixI);
3319  _rpmnixPool = rpmioFreePool(_rpmnixPool);
3320 #endif
3321 
3322  _rpmcudfPool = rpmioFreePool(_rpmcudfPool);
3323  _rpmluavPool = rpmioFreePool(_rpmluavPool);
3324  _rpmluaPool = rpmioFreePool(_rpmluaPool);
3325 
3326  _rpmhkpI = rpmhkpFree(_rpmhkpI);
3327  _rpmhkpPool = rpmioFreePool(_rpmhkpPool);
3328  _rpmhkp_awol.bf = rpmbfFree(_rpmhkp_awol.bf);
3329  _rpmhkp_crl.bf = rpmbfFree(_rpmhkp_crl.bf);
3330 
3331  _rpmvcPool = rpmioFreePool(_rpmvcPool);
3332  _rpmvtPool = rpmioFreePool(_rpmvtPool);
3333 
3334  _rpmsmI = rpmsmFree(_rpmsmI);
3335  _rpmsmPool = rpmioFreePool(_rpmsmPool);
3336  _rpmspPool = rpmioFreePool(_rpmspPool);
3338  _rpmsxPool = rpmioFreePool(_rpmsxPool);
3339 
3340  _htmlPool = rpmioFreePool(_htmlPool);
3341  _mirePool = rpmioFreePool(_mirePool);
3342  _rpmmgPool = rpmioFreePool(_rpmmgPool);
3343  _rpmbfPool = rpmioFreePool(_rpmbfPool);
3344  _htPool = rpmioFreePool(_htPool);
3345  _ctxPool = rpmioFreePool(_ctxPool);
3346  _rpmsyckPool = rpmioFreePool(_rpmsyckPool);
3347  _rpmiobPool = rpmioFreePool(_rpmiobPool);
3348  _digPool = rpmioFreePool(_digPool);
3349  _xarPool = rpmioFreePool(_xarPool);
3350  _avxPool = rpmioFreePool(_avxPool);
3351  _urlPool = rpmioFreePool(_urlPool);
3352  _fdPool = rpmioFreePool(_fdPool);
3353 
3354  rpmlogClose();
3355 }
3356 
3357 /*@-type@*/ /* LCL: function typedefs */
3358 static struct FDIO_s fpio_s = {
3359  ufdRead, ufdWrite, fdSeek, ufdClose, NULL, NULL, NULL,
3360 };
3361 /*@=type@*/
3362 
3363 FDIO_t fpio = /*@-compmempass@*/ &fpio_s /*@=compmempass@*/ ;