rpm 5.2.1

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

- File List
- Globals

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