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