Change fdstat functions to take fdOpx enum to fix int/enum mismatches
[platform/upstream/rpm.git] / rpmio / rpmio.c
1 /** \ingroup rpmio
2  * \file rpmio/rpmio.c
3  */
4
5 #include "system.h"
6 #include <stdarg.h>
7 #include <errno.h>
8
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmmacro.h>
11 #include <rpm/rpmfileutil.h>
12 #include <rpm/rpmsw.h>
13 #include <rpm/rpmurl.h>
14
15 #include "rpmio/rpmio_internal.h"
16
17 #include "debug.h"
18
19 #if HAVE_LIBIO_H && defined(_G_IO_IO_FILE_VERSION)
20 #define _USE_LIBIO      1
21 #endif
22
23 typedef struct _FDSTACK_s {
24     FDIO_t              io;
25     void *              fp;
26     int                 fdno;
27 } FDSTACK_t;
28
29 /** \ingroup rpmio
30  * Cumulative statistics for a descriptor.
31  */
32 typedef struct {
33     struct rpmop_s      ops[FDSTAT_MAX];        /*!< Cumulative statistics. */
34 } * FDSTAT_t;
35
36 /** \ingroup rpmio
37  * The FD_t File Handle data structure.
38  */
39 struct _FD_s {
40     int         nrefs;
41     int         flags;
42 #define RPMIO_DEBUG_IO          0x40000000
43 #define RPMIO_DEBUG_REFS        0x20000000
44     int         magic;
45 #define FDMAGIC                 0x04463138
46     int         nfps;
47     FDSTACK_t   fps[8];
48     int         urlType;        /* ufdio: */
49
50     ssize_t     bytesRemain;    /* ufdio: */
51
52     int         syserrno;       /* last system errno encountered */
53     const void *errcookie;      /* gzdio/bzdio/ufdio/xzdio: */
54
55     FDSTAT_t    stats;          /* I/O statistics */
56
57     rpmDigestBundle digests;
58 };
59
60 #define DBG(_f, _m, _x) \
61     \
62     if ((_rpmio_debug | ((_f) ? ((FD_t)(_f))->flags : 0)) & (_m)) fprintf _x \
63
64 #define DBGIO(_f, _x)   DBG((_f), RPMIO_DEBUG_IO, _x)
65
66 static FDIO_t fdGetIo(FD_t fd)
67 {
68     return (fd != NULL) ? fd->fps[fd->nfps].io : NULL;
69 }
70
71 static void fdSetIo(FD_t fd, FDIO_t io)
72 {
73     if (fd)
74         fd->fps[fd->nfps].io = io;
75 }
76
77 static void * fdGetFp(FD_t fd)
78 {
79     return (fd != NULL) ? fd->fps[fd->nfps].fp : NULL;
80 }
81
82 static void fdSetFp(FD_t fd, void * fp)
83 {
84     if (fd)
85         fd->fps[fd->nfps].fp = fp;
86 }
87
88 static void fdSetFdno(FD_t fd, int fdno)
89 {
90     if (fd) 
91         fd->fps[fd->nfps].fdno = fdno;
92 }
93
94 static void fdPush(FD_t fd, FDIO_t io, void * fp, int fdno)
95 {
96     if (fd == NULL || fd->nfps >= (sizeof(fd->fps)/sizeof(fd->fps[0]) - 1))
97         return;
98     fd->nfps++;
99     fdSetIo(fd, io);
100     fdSetFp(fd, fp);
101     fdSetFdno(fd, fdno);
102 }
103
104 static void fdPop(FD_t fd)
105 {
106     if (fd == NULL || fd->nfps < 0) return;
107     fdSetIo(fd, NULL);
108     fdSetFp(fd, NULL);
109     fdSetFdno(fd, -1);
110     fd->nfps--;
111 }
112
113 static FD_t c2f(void * cookie)
114 {
115     FD_t fd = (FD_t) cookie;
116     return (fd && fd->magic == FDMAGIC) ? fd : NULL;
117 }
118
119 void fdSetBundle(FD_t fd, rpmDigestBundle bundle)
120 {
121     if (fd)
122         fd->digests = bundle;
123 }
124
125 rpmDigestBundle fdGetBundle(FD_t fd)
126 {
127     return (fd != NULL) ? fd->digests : NULL;
128 }
129
130 static void * iotFileno(FD_t fd, FDIO_t iot)
131 {
132     void * rc = NULL;
133
134     if (fd == NULL)
135         return NULL;
136
137     for (int i = fd->nfps; i >= 0; i--) {
138         FDSTACK_t * fps = &fd->fps[i];
139         if (fps->io != iot)
140             continue;
141         rc = fps->fp;
142         break;
143     }
144     
145     return rc;
146 }
147
148 #define FDNREFS(fd)     (fd ? ((FD_t)fd)->nrefs : -9)
149
150 #define FDONLY(fd)      assert(fdGetIo(fd) == fdio)
151 #define GZDONLY(fd)     assert(fdGetIo(fd) == gzdio)
152 #define BZDONLY(fd)     assert(fdGetIo(fd) == bzdio)
153 #define LZDONLY(fd)     assert(fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio)
154
155 #define UFDONLY(fd)     /* assert(fdGetIo(fd) == ufdio) */
156
157 /**
158  */
159 #if _USE_LIBIO
160 static int noLibio = 0;
161 #else
162 static int noLibio = 1;
163 #endif
164
165 /** \ingroup rpmio
166  * \name RPMIO Vectors.
167  */
168 typedef ssize_t (*fdio_read_function_t) (void *cookie, char *buf, size_t nbytes);
169 typedef ssize_t (*fdio_write_function_t) (void *cookie, const char *buf, size_t nbytes);
170 typedef int (*fdio_seek_function_t) (void *cookie, _libio_pos_t pos, int whence);
171 typedef int (*fdio_close_function_t) (void *cookie);
172 typedef FD_t (*fdio_ref_function_t) ( void * cookie);
173 typedef FD_t (*fdio_deref_function_t) (FD_t fd);
174 typedef FD_t (*fdio_new_function_t) (void);
175 typedef int (*fdio_fileno_function_t) (void * cookie);
176 typedef FD_t (*fdio_open_function_t) (const char * path, int flags, mode_t mode);
177 typedef FD_t (*fdio_fopen_function_t) (const char * path, const char * fmode);
178 typedef void * (*fdio_ffileno_function_t) (FD_t fd);
179 typedef int (*fdio_fflush_function_t) (FD_t fd);
180
181 /** \ingroup rpmio
182  */
183 struct FDIO_s {
184   fdio_read_function_t          read;
185   fdio_write_function_t         write;
186   fdio_seek_function_t          seek;
187   fdio_close_function_t         close;
188
189   fdio_ref_function_t           _fdref;
190   fdio_deref_function_t         _fdderef;
191   fdio_new_function_t           _fdnew;
192   fdio_fileno_function_t        _fileno;
193
194   fdio_open_function_t          _open;
195   fdio_fopen_function_t         _fopen;
196   fdio_ffileno_function_t       _ffileno;
197   fdio_fflush_function_t        _fflush;
198 };
199
200 /* forward refs */
201 static const FDIO_t fdio;
202 static const FDIO_t fpio;
203 static const FDIO_t ufdio;
204 static const FDIO_t gzdio;
205 static const FDIO_t bzdio;
206 static const FDIO_t xzdio;
207 static const FDIO_t lzdio;
208
209 /** \ingroup rpmio
210  * Update digest(s) attached to fd.
211  */
212 static void fdUpdateDigests(FD_t fd, const unsigned char * buf, size_t buflen);
213
214 /**
215  */
216 int _rpmio_debug = 0;
217
218 /* =============================================================== */
219
220 static const char * fdbg(FD_t fd)
221 {
222     static char buf[BUFSIZ];
223     char *be = buf;
224     int i;
225
226     buf[0] = '\0';
227     if (fd == NULL)
228         return buf;
229
230     if (fd->bytesRemain != -1) {
231         sprintf(be, " clen %d", (int)fd->bytesRemain);
232         be += strlen(be);
233      }
234     *be++ = '\t';
235     for (i = fd->nfps; i >= 0; i--) {
236         FDSTACK_t * fps = &fd->fps[i];
237         if (i != fd->nfps)
238             *be++ = ' ';
239         *be++ = '|';
240         *be++ = ' ';
241         if (fps->io == fdio) {
242             sprintf(be, "FD %d fp %p", fps->fdno, fps->fp);
243         } else if (fps->io == ufdio) {
244             sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp);
245 #if HAVE_ZLIB_H
246         } else if (fps->io == gzdio) {
247             sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno);
248 #endif
249 #if HAVE_BZLIB_H
250         } else if (fps->io == bzdio) {
251             sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno);
252 #endif
253 #if HAVE_LZMA_H
254         } else if (fps->io == xzdio) {
255             sprintf(be, "XZD %p fdno %d", fps->fp, fps->fdno);
256         } else if (fps->io == lzdio) {
257             sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno);
258 #endif
259         } else if (fps->io == fpio) {
260             sprintf(be, "%s %p(%d) fdno %d",
261                 (fps->fdno < 0 ? "LIBIO" : "FP"),
262                 fps->fp, fileno(((FILE *)fps->fp)), fps->fdno);
263         } else {
264             sprintf(be, "??? io %p fp %p fdno %d ???",
265                 fps->io, fps->fp, fps->fdno);
266         }
267         be += strlen(be);
268         *be = '\0';
269     }
270     return buf;
271 }
272
273 static void fdstat_enter(FD_t fd, fdOpX opx)
274 {
275     if (fd == NULL) return;
276     if (fd->stats != NULL)
277         (void) rpmswEnter(fdOp(fd, opx), (ssize_t) 0);
278 }
279
280 static void fdstat_exit(FD_t fd, fdOpX opx, ssize_t rc)
281 {
282     if (fd == NULL) return;
283     if (rc == -1)
284         fd->syserrno = errno;
285     else if (rc > 0 && fd->bytesRemain > 0)
286         switch (opx) {
287         case FDSTAT_READ:
288         case FDSTAT_WRITE:
289         fd->bytesRemain -= rc;
290             break;
291         default:
292             break;
293         }
294     if (fd->stats != NULL)
295         (void) rpmswExit(fdOp(fd, opx), rc);
296 }
297
298 static void fdstat_print(FD_t fd, const char * msg, FILE * fp)
299 {
300     static const int usec_scale = (1000*1000);
301     int opx;
302
303     if (fd == NULL || fd->stats == NULL) return;
304     for (opx = 0; opx < 4; opx++) {
305         rpmop op = &fd->stats->ops[opx];
306         if (op->count <= 0) continue;
307         switch (opx) {
308         case FDSTAT_READ:
309             if (msg) fprintf(fp, "%s:", msg);
310             fprintf(fp, "%8d reads, %8ld total bytes in %d.%06d secs\n",
311                 op->count, (long)op->bytes,
312                 (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
313             break;
314         case FDSTAT_WRITE:
315             if (msg) fprintf(fp, "%s:", msg);
316             fprintf(fp, "%8d writes, %8ld total bytes in %d.%06d secs\n",
317                 op->count, (long)op->bytes,
318                 (int)(op->usecs/usec_scale), (int)(op->usecs%usec_scale));
319             break;
320         case FDSTAT_SEEK:
321             break;
322         case FDSTAT_CLOSE:
323             break;
324         }
325     }
326 }
327
328 /* =============================================================== */
329 off_t fdSize(FD_t fd)
330 {
331     struct stat sb;
332     off_t rc = -1; 
333
334     if (fd != NULL && fstat(Fileno(fd), &sb) == 0)
335         rc = sb.st_size;
336     return rc;
337 }
338
339 FD_t fdDup(int fdno)
340 {
341     FD_t fd;
342     int nfdno;
343
344     if ((nfdno = dup(fdno)) < 0)
345         return NULL;
346     fd = fdNew();
347     fdSetFdno(fd, nfdno);
348 DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd)));
349     return fd;
350 }
351
352 static int fdSeekNot(void * cookie, _libio_pos_t pos,  int whence)
353 {
354     return -2;
355 }
356
357 /** \ingroup rpmio
358  */
359 static int fdFileno(void * cookie)
360 {
361     FD_t fd = c2f(cookie);
362     return (fd != NULL) ? fd->fps[0].fdno : -2;
363 }
364
365 FILE * fdGetFILE(FD_t fd)
366 {
367     return ((FILE *)fdGetFp(fd));
368 }
369
370 /* =============================================================== */
371
372 FD_t fdLink(void * cookie)
373 {
374     FD_t fd = c2f(cookie);
375     if (fd)
376         fd->nrefs++;
377     return fd;
378 }
379
380 /**
381  */
382 FD_t fdFree( FD_t fd)
383 {
384     if (fd) {
385         if (--fd->nrefs > 0)
386             return fd;
387         fd->stats = _free(fd->stats);
388         if (fd->digests) {
389             fd->digests = rpmDigestBundleFree(fd->digests);
390         }
391         free(fd);
392     }
393     return NULL;
394 }
395
396 /**
397  */
398 FD_t fdNew(void)
399 {
400     FD_t fd = xcalloc(1, sizeof(*fd));
401     if (fd == NULL) /* XXX xmalloc never returns NULL */
402         return NULL;
403     fd->nrefs = 0;
404     fd->flags = 0;
405     fd->magic = FDMAGIC;
406     fd->urlType = URL_IS_UNKNOWN;
407
408     fd->nfps = 0;
409     memset(fd->fps, 0, sizeof(fd->fps));
410
411     fd->fps[0].io = fdio;
412     fd->fps[0].fp = NULL;
413     fd->fps[0].fdno = -1;
414
415     fd->bytesRemain = -1;
416     fd->syserrno = 0;
417     fd->errcookie = NULL;
418     fd->stats = xcalloc(1, sizeof(*fd->stats));
419     fd->digests = NULL;
420
421     return fdLink(fd);
422 }
423
424 /**
425  */
426 static ssize_t fdRead(void * cookie, char * buf, size_t count)
427 {
428     FD_t fd = c2f(cookie);
429     ssize_t rc;
430
431     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
432
433     fdstat_enter(fd, FDSTAT_READ);
434     rc = read(fdFileno(fd), buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
435     fdstat_exit(fd, FDSTAT_READ, rc);
436
437     if (fd->digests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
438
439 DBGIO(fd, (stderr, "==>\tfdRead(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
440
441     return rc;
442 }
443
444 /**
445  */
446 static ssize_t fdWrite(void * cookie, const char * buf, size_t count)
447 {
448     FD_t fd = c2f(cookie);
449     int fdno = fdFileno(fd);
450     ssize_t rc;
451
452     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
453
454     if (fd->digests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
455
456     if (count == 0) return 0;
457
458     fdstat_enter(fd, FDSTAT_WRITE);
459     rc = write(fdno, buf, (count > fd->bytesRemain ? fd->bytesRemain : count));
460     fdstat_exit(fd, FDSTAT_WRITE, rc);
461
462 DBGIO(fd, (stderr, "==>\tfdWrite(%p,%p,%ld) rc %ld %s\n", cookie, buf, (long)count, (long)rc, fdbg(fd)));
463
464     return rc;
465 }
466
467 static int fdSeek(void * cookie, _libio_pos_t pos, int whence)
468 {
469 #ifdef USE_COOKIE_SEEK_POINTER
470     _IO_off64_t p = *pos;
471 #else
472     off_t p = pos;
473 #endif
474     FD_t fd = c2f(cookie);
475     off_t rc;
476
477     if (fd == NULL)
478         return -2;
479
480     assert(fd->bytesRemain == -1);      /* XXX FIXME fadio only for now */
481     fdstat_enter(fd, FDSTAT_SEEK);
482     rc = lseek(fdFileno(fd), p, whence);
483     fdstat_exit(fd, FDSTAT_SEEK, rc);
484
485 DBGIO(fd, (stderr, "==>\tfdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
486
487     return rc;
488 }
489
490 /**
491  */
492 static int fdClose( void * cookie)
493 {
494     FD_t fd = c2f(cookie);
495     int fdno;
496     int rc;
497
498     if (fd == NULL) return -2;
499     fdno = fdFileno(fd);
500
501     fdSetFdno(fd, -1);
502
503     fdstat_enter(fd, FDSTAT_CLOSE);
504     rc = ((fdno >= 0) ? close(fdno) : -2);
505     fdstat_exit(fd, FDSTAT_CLOSE, rc);
506
507 DBGIO(fd, (stderr, "==>\tfdClose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
508
509     fd = fdFree(fd);
510     return rc;
511 }
512
513 /**
514  */
515 static FD_t fdOpen(const char *path, int flags, mode_t mode)
516 {
517     FD_t fd;
518     int fdno;
519
520     fdno = open(path, flags, mode);
521     if (fdno < 0) return NULL;
522     if (fcntl(fdno, F_SETFD, FD_CLOEXEC)) {
523         (void) close(fdno);
524         return NULL;
525     }
526     fd = fdNew();
527     fdSetFdno(fd, fdno);
528     fd->flags = flags;
529 DBGIO(fd, (stderr, "==>\tfdOpen(\"%s\",%x,0%o) %s\n", path, (unsigned)flags, (unsigned)mode, fdbg(fd)));
530     return fd;
531 }
532
533 static const struct FDIO_s fdio_s = {
534   fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
535   fdOpen, NULL, fdGetFp, NULL
536 };
537 static const FDIO_t fdio = &fdio_s ;
538
539 int ufdCopy(FD_t sfd, FD_t tfd)
540 {
541     char buf[BUFSIZ];
542     int itemsRead;
543     int itemsCopied = 0;
544     int rc = 0;
545
546     while (1) {
547         rc = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
548         if (rc < 0)
549             break;
550         else if (rc == 0) {
551             rc = itemsCopied;
552             break;
553         }
554         itemsRead = rc;
555         rc = Fwrite(buf, sizeof(buf[0]), itemsRead, tfd);
556         if (rc < 0)
557             break;
558         if (rc != itemsRead) {
559             rc = -1;
560             break;
561         }
562
563         itemsCopied += itemsRead;
564     }
565
566     DBGIO(sfd, (stderr, "++ copied %d bytes\n", itemsCopied));
567
568     return rc;
569 }
570
571 /*
572  * Deal with remote url's by fetching them with a helper application
573  * and treat as local file afterwards.
574  * TODO:
575  * - better error checking + reporting 
576  * - curl & friends don't know about hkp://, transform to http?
577  */
578
579 static FD_t urlOpen(const char * url, int flags, mode_t mode)
580 {
581     FD_t fd;
582     char *dest = NULL;
583     int rc = 1; /* assume failure */
584
585     fd = rpmMkTempFile(NULL, &dest);
586     if (fd == NULL) {
587         return NULL;
588     }
589     Fclose(fd);
590
591     rc = urlGetFile(url, dest);
592     if (rc == 0) {
593         fd = fdOpen(dest, flags, mode);
594         unlink(dest);
595     } else {
596         fd = NULL;
597     }
598     dest = _free(dest);
599
600     return fd;
601
602 }
603 static FD_t ufdOpen(const char * url, int flags, mode_t mode)
604 {
605     FD_t fd = NULL;
606     const char * path;
607     urltype urlType = urlPath(url, &path);
608
609 if (_rpmio_debug)
610 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
611
612     switch (urlType) {
613     case URL_IS_FTP:
614     case URL_IS_HTTPS:
615     case URL_IS_HTTP:
616     case URL_IS_HKP:
617         fd = urlOpen(url, flags, mode);
618         /* we're dealing with local file when urlOpen() returns */
619         urlType = URL_IS_UNKNOWN;
620         break;
621     case URL_IS_DASH:
622         if ((flags & O_ACCMODE) == O_RDWR) {
623             fd = NULL;
624         } else {
625             fd = fdDup((flags & O_ACCMODE) == O_WRONLY ?
626                         STDOUT_FILENO : STDIN_FILENO);
627         }
628         break;
629     case URL_IS_PATH:
630     case URL_IS_UNKNOWN:
631     default:
632         fd = fdOpen(path, flags, mode);
633         break;
634     }
635
636     if (fd == NULL) return NULL;
637
638     fdSetIo(fd, ufdio);
639     fd->bytesRemain = -1;
640     fd->urlType = urlType;
641
642     if (Fileno(fd) < 0) {
643         (void) fdClose(fd);
644         return NULL;
645     }
646 DBGIO(fd, (stderr, "==>\tufdOpen(\"%s\",%x,0%o) %s\n", url, (unsigned)flags, (unsigned)mode, fdbg(fd)));
647     return fd;
648 }
649
650 static const struct FDIO_s ufdio_s = {
651   fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
652   ufdOpen, NULL, fdGetFp, NULL
653 };
654 static const FDIO_t ufdio = &ufdio_s ;
655
656 ssize_t timedRead(FD_t fd, void * bufptr, size_t length)
657 {
658     return ufdio->read(fd, bufptr, length);
659 }
660
661 /* =============================================================== */
662 /* Support for GZIP library.
663  */
664 #ifdef  HAVE_ZLIB_H
665
666 #include <zlib.h>
667
668 static void * gzdFileno(FD_t fd)
669 {
670     return iotFileno(fd, gzdio);
671 }
672
673 static
674 FD_t gzdOpen(const char * path, const char * fmode)
675 {
676     FD_t fd;
677     gzFile gzfile;
678     if ((gzfile = gzopen(path, fmode)) == NULL)
679         return NULL;
680     fd = fdNew();
681     fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
682     
683 DBGIO(fd, (stderr, "==>\tgzdOpen(\"%s\", \"%s\") fd %p %s\n", path, fmode, (fd ? fd : NULL), fdbg(fd)));
684     return fdLink(fd);
685 }
686
687 static FD_t gzdFdopen(void * cookie, const char *fmode)
688 {
689     FD_t fd = c2f(cookie);
690     int fdno;
691     gzFile gzfile;
692
693     if (fd == NULL || fmode == NULL) return NULL;
694     fdno = fdFileno(fd);
695     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
696     if (fdno < 0) return NULL;
697     gzfile = gzdopen(fdno, fmode);
698     if (gzfile == NULL) return NULL;
699
700     fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
701
702     return fdLink(fd);
703 }
704
705 static int gzdFlush(FD_t fd)
706 {
707     gzFile gzfile;
708     gzfile = gzdFileno(fd);
709     if (gzfile == NULL) return -2;
710     return gzflush(gzfile, Z_SYNC_FLUSH);       /* XXX W2DO? */
711 }
712
713 /* =============================================================== */
714 static ssize_t gzdRead(void * cookie, char * buf, size_t count)
715 {
716     FD_t fd = c2f(cookie);
717     gzFile gzfile;
718     ssize_t rc;
719
720     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
721
722     gzfile = gzdFileno(fd);
723     if (gzfile == NULL) return -2;      /* XXX can't happen */
724
725     fdstat_enter(fd, FDSTAT_READ);
726     rc = gzread(gzfile, buf, count);
727 DBGIO(fd, (stderr, "==>\tgzdRead(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
728     if (rc < 0) {
729         int zerror = 0;
730         fd->errcookie = gzerror(gzfile, &zerror);
731         if (zerror == Z_ERRNO) {
732             fd->syserrno = errno;
733             fd->errcookie = strerror(fd->syserrno);
734         }
735     } else if (rc >= 0) {
736         fdstat_exit(fd, FDSTAT_READ, rc);
737         if (fd->digests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
738     }
739     return rc;
740 }
741
742 static ssize_t gzdWrite(void * cookie, const char * buf, size_t count)
743 {
744     FD_t fd = c2f(cookie);
745     gzFile gzfile;
746     ssize_t rc;
747
748     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
749
750     if (fd->digests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
751
752     gzfile = gzdFileno(fd);
753     if (gzfile == NULL) return -2;      /* XXX can't happen */
754
755     fdstat_enter(fd, FDSTAT_WRITE);
756     rc = gzwrite(gzfile, (void *)buf, count);
757 DBGIO(fd, (stderr, "==>\tgzdWrite(%p,%p,%u) rc %lx %s\n", cookie, buf, (unsigned)count, (unsigned long)rc, fdbg(fd)));
758     if (rc < 0) {
759         int zerror = 0;
760         fd->errcookie = gzerror(gzfile, &zerror);
761         if (zerror == Z_ERRNO) {
762             fd->syserrno = errno;
763             fd->errcookie = strerror(fd->syserrno);
764         }
765     } else if (rc > 0) {
766         fdstat_exit(fd, FDSTAT_WRITE, rc);
767     }
768     return rc;
769 }
770
771 /* XXX zlib-1.0.4 has not */
772 static int gzdSeek(void * cookie, _libio_pos_t pos, int whence)
773 {
774 #ifdef USE_COOKIE_SEEK_POINTER
775     _IO_off64_t p = *pos;
776 #else
777     off_t p = pos;
778 #endif
779     int rc;
780 #if HAVE_GZSEEK
781     FD_t fd = c2f(cookie);
782     gzFile gzfile;
783
784     if (fd == NULL) return -2;
785     assert(fd->bytesRemain == -1);      /* XXX FIXME */
786
787     gzfile = gzdFileno(fd);
788     if (gzfile == NULL) return -2;      /* XXX can't happen */
789
790     fdstat_enter(fd, FDSTAT_SEEK);
791     rc = gzseek(gzfile, p, whence);
792 DBGIO(fd, (stderr, "==>\tgzdSeek(%p,%ld,%d) rc %lx %s\n", cookie, (long)p, whence, (unsigned long)rc, fdbg(fd)));
793     if (rc < 0) {
794         int zerror = 0;
795         fd->errcookie = gzerror(gzfile, &zerror);
796         if (zerror == Z_ERRNO) {
797             fd->syserrno = errno;
798             fd->errcookie = strerror(fd->syserrno);
799         }
800     } else if (rc >= 0) {
801         fdstat_exit(fd, FDSTAT_SEEK, rc);
802     }
803 #else
804     rc = -2;
805 #endif
806     return rc;
807 }
808
809 static int gzdClose( void * cookie)
810 {
811     FD_t fd = c2f(cookie);
812     gzFile gzfile;
813     int rc;
814
815     gzfile = gzdFileno(fd);
816     if (gzfile == NULL) return -2;      /* XXX can't happen */
817
818     fdstat_enter(fd, FDSTAT_CLOSE);
819     rc = gzclose(gzfile);
820
821     /* XXX TODO: preserve fd if errors */
822
823     if (fd) {
824 DBGIO(fd, (stderr, "==>\tgzdClose(%p) zerror %d %s\n", cookie, rc, fdbg(fd)));
825         if (rc < 0) {
826             fd->errcookie = "gzclose error";
827             if (rc == Z_ERRNO) {
828                 fd->syserrno = errno;
829                 fd->errcookie = strerror(fd->syserrno);
830             }
831         } else if (rc >= 0) {
832             fdstat_exit(fd, FDSTAT_CLOSE, rc);
833         }
834     }
835
836 DBGIO(fd, (stderr, "==>\tgzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
837
838     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
839     if (rc == 0)
840         fd = fdFree(fd);
841     return rc;
842 }
843
844 static const struct FDIO_s gzdio_s = {
845   gzdRead, gzdWrite, gzdSeek, gzdClose, fdLink, fdFree, fdNew, fdFileno,
846   NULL, gzdOpen, gzdFileno, gzdFlush
847 };
848 static const FDIO_t gzdio = &gzdio_s ;
849
850 #endif  /* HAVE_ZLIB_H */
851
852 /* =============================================================== */
853 /* Support for BZIP2 library.
854  */
855 #if HAVE_BZLIB_H
856
857 #include <bzlib.h>
858
859 #ifdef HAVE_BZ2_1_0
860 # define bzopen  BZ2_bzopen
861 # define bzclose BZ2_bzclose
862 # define bzdopen BZ2_bzdopen
863 # define bzerror BZ2_bzerror
864 # define bzflush BZ2_bzflush
865 # define bzread  BZ2_bzread
866 # define bzwrite BZ2_bzwrite
867 #endif /* HAVE_BZ2_1_0 */
868
869 static void * bzdFileno(FD_t fd)
870 {
871     return iotFileno(fd, bzdio);
872 }
873
874 static FD_t bzdOpen(const char * path, const char * mode)
875 {
876     FD_t fd;
877     BZFILE *bzfile;;
878     if ((bzfile = bzopen(path, mode)) == NULL)
879         return NULL;
880     fd = fdNew();
881     fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
882     return fdLink(fd);
883 }
884
885 static FD_t bzdFdopen(void * cookie, const char * fmode)
886 {
887     FD_t fd = c2f(cookie);
888     int fdno;
889     BZFILE *bzfile;
890
891     if (fd == NULL || fmode == NULL) return NULL;
892     fdno = fdFileno(fd);
893     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
894     if (fdno < 0) return NULL;
895     bzfile = bzdopen(fdno, fmode);
896     if (bzfile == NULL) return NULL;
897
898     fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
899
900     return fdLink(fd);
901 }
902
903 static int bzdFlush(FD_t fd)
904 {
905     return bzflush(bzdFileno(fd));
906 }
907
908 /* =============================================================== */
909 static ssize_t bzdRead(void * cookie, char * buf, size_t count)
910 {
911     FD_t fd = c2f(cookie);
912     BZFILE *bzfile;
913     ssize_t rc = 0;
914
915     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
916     bzfile = bzdFileno(fd);
917     fdstat_enter(fd, FDSTAT_READ);
918     if (bzfile)
919         rc = bzread(bzfile, buf, count);
920     if (rc == -1) {
921         int zerror = 0;
922         if (bzfile)
923             fd->errcookie = bzerror(bzfile, &zerror);
924     } else if (rc >= 0) {
925         fdstat_exit(fd, FDSTAT_READ, rc);
926         if (fd->digests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
927     }
928     return rc;
929 }
930
931 static ssize_t bzdWrite(void * cookie, const char * buf, size_t count)
932 {
933     FD_t fd = c2f(cookie);
934     BZFILE *bzfile;
935     ssize_t rc;
936
937     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
938
939     if (fd->digests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
940
941     bzfile = bzdFileno(fd);
942     fdstat_enter(fd, FDSTAT_WRITE);
943     rc = bzwrite(bzfile, (void *)buf, count);
944     if (rc == -1) {
945         int zerror = 0;
946         fd->errcookie = bzerror(bzfile, &zerror);
947     } else if (rc > 0) {
948         fdstat_exit(fd, FDSTAT_WRITE, rc);
949     }
950     return rc;
951 }
952
953 static int bzdClose( void * cookie)
954 {
955     FD_t fd = c2f(cookie);
956     BZFILE *bzfile;
957     int rc;
958
959     bzfile = bzdFileno(fd);
960
961     if (bzfile == NULL) return -2;
962     fdstat_enter(fd, FDSTAT_CLOSE);
963     /* FIX: check rc */
964     bzclose(bzfile);
965     rc = 0;     /* XXX FIXME */
966
967     /* XXX TODO: preserve fd if errors */
968
969     if (fd) {
970         if (rc == -1) {
971             int zerror = 0;
972             fd->errcookie = bzerror(bzfile, &zerror);
973         } else if (rc >= 0) {
974             fdstat_exit(fd, FDSTAT_CLOSE, rc);
975         }
976     }
977
978 DBGIO(fd, (stderr, "==>\tbzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
979
980     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
981     if (rc == 0)
982         fd = fdFree(fd);
983     return rc;
984 }
985
986 static const struct FDIO_s bzdio_s = {
987   bzdRead, bzdWrite, fdSeekNot, bzdClose, fdLink, fdFree, fdNew, fdFileno,
988   NULL, bzdOpen, bzdFileno, bzdFlush
989 };
990 static const FDIO_t bzdio = &bzdio_s ;
991
992 #endif  /* HAVE_BZLIB_H */
993
994 /* =============================================================== */
995 static const char * getFdErrstr (FD_t fd)
996 {
997     const char *errstr = NULL;
998
999 #ifdef  HAVE_ZLIB_H
1000     if (fdGetIo(fd) == gzdio) {
1001         errstr = fd->errcookie;
1002     } else
1003 #endif  /* HAVE_ZLIB_H */
1004
1005 #ifdef  HAVE_BZLIB_H
1006     if (fdGetIo(fd) == bzdio) {
1007         errstr = fd->errcookie;
1008     } else
1009 #endif  /* HAVE_BZLIB_H */
1010 #ifdef  HAVE_LZMA_H
1011     if (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio) {
1012         errstr = fd->errcookie;
1013     } else
1014 #endif  /* HAVE_LZMA_H */
1015     {
1016         errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
1017     }
1018
1019     return errstr;
1020 }
1021
1022 /* =============================================================== */
1023 /* Support for LZMA library.
1024  */
1025
1026 #ifdef HAVE_LZMA_H
1027
1028 #include <sys/types.h>
1029 #include <inttypes.h>
1030 #include <lzma.h>
1031
1032 #define kBufferSize (1 << 15)
1033
1034 typedef struct lzfile {
1035   /* IO buffer */
1036     uint8_t buf[kBufferSize];
1037
1038     lzma_stream strm;
1039
1040     FILE *file;
1041
1042     int encoding;
1043     int eof;
1044
1045 } LZFILE;
1046
1047 static LZFILE *lzopen_internal(const char *path, const char *mode, int fd, int xz)
1048 {
1049     int level = 7;      /* Use XZ's default compression level if unspecified */
1050     int encoding = 0;
1051     FILE *fp;
1052     LZFILE *lzfile;
1053     lzma_ret ret;
1054     lzma_stream init_strm = LZMA_STREAM_INIT;
1055
1056     for (; *mode; mode++) {
1057         if (*mode == 'w')
1058             encoding = 1;
1059         else if (*mode == 'r')
1060             encoding = 0;
1061         else if (*mode >= '1' && *mode <= '9')
1062             level = *mode - '0';
1063     }
1064     if (fd != -1)
1065         fp = fdopen(fd, encoding ? "w" : "r");
1066     else
1067         fp = fopen(path, encoding ? "w" : "r");
1068     if (!fp)
1069         return 0;
1070     lzfile = calloc(1, sizeof(*lzfile));
1071     if (!lzfile) {
1072         fclose(fp);
1073         return 0;
1074     }
1075     
1076     lzfile->file = fp;
1077     lzfile->encoding = encoding;
1078     lzfile->eof = 0;
1079     lzfile->strm = init_strm;
1080     if (encoding) {
1081         if (xz) {
1082             ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
1083         } else {
1084             lzma_options_lzma options;
1085             lzma_lzma_preset(&options, level);
1086             ret = lzma_alone_encoder(&lzfile->strm, &options);
1087         }
1088     } else {    /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */
1089         ret = lzma_auto_decoder(&lzfile->strm, 100<<20, 0);
1090     }
1091     if (ret != LZMA_OK) {
1092         fclose(fp);
1093         free(lzfile);
1094         return 0;
1095     }
1096     return lzfile;
1097 }
1098
1099 static LZFILE *xzopen(const char *path, const char *mode)
1100 {
1101     return lzopen_internal(path, mode, -1, 1);
1102 }
1103
1104 static LZFILE *xzdopen(int fd, const char *mode)
1105 {
1106     if (fd < 0)
1107         return 0;
1108     return lzopen_internal(0, mode, fd, 1);
1109 }
1110
1111 static LZFILE *lzopen(const char *path, const char *mode)
1112 {
1113     return lzopen_internal(path, mode, -1, 0);
1114 }
1115
1116 static LZFILE *lzdopen(int fd, const char *mode)
1117 {
1118     if (fd < 0)
1119         return 0;
1120     return lzopen_internal(0, mode, fd, 0);
1121 }
1122
1123 static int lzflush(LZFILE *lzfile)
1124 {
1125     return fflush(lzfile->file);
1126 }
1127
1128 static int lzclose(LZFILE *lzfile)
1129 {
1130     lzma_ret ret;
1131     size_t n;
1132     int rc;
1133
1134     if (!lzfile)
1135         return -1;
1136     if (lzfile->encoding) {
1137         for (;;) {
1138             lzfile->strm.avail_out = kBufferSize;
1139             lzfile->strm.next_out = lzfile->buf;
1140             ret = lzma_code(&lzfile->strm, LZMA_FINISH);
1141             if (ret != LZMA_OK && ret != LZMA_STREAM_END)
1142                 return -1;
1143             n = kBufferSize - lzfile->strm.avail_out;
1144             if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
1145                 return -1;
1146             if (ret == LZMA_STREAM_END)
1147                 break;
1148         }
1149     }
1150     lzma_end(&lzfile->strm);
1151     rc = fclose(lzfile->file);
1152     free(lzfile);
1153     return rc;
1154 }
1155
1156 static ssize_t lzread(LZFILE *lzfile, void *buf, size_t len)
1157 {
1158     lzma_ret ret;
1159     int eof = 0;
1160
1161     if (!lzfile || lzfile->encoding)
1162       return -1;
1163     if (lzfile->eof)
1164       return 0;
1165     lzfile->strm.next_out = buf;
1166     lzfile->strm.avail_out = len;
1167     for (;;) {
1168         if (!lzfile->strm.avail_in) {
1169             lzfile->strm.next_in = lzfile->buf;
1170             lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file);
1171             if (!lzfile->strm.avail_in)
1172                 eof = 1;
1173         }
1174         ret = lzma_code(&lzfile->strm, LZMA_RUN);
1175         if (ret == LZMA_STREAM_END) {
1176             lzfile->eof = 1;
1177             return len - lzfile->strm.avail_out;
1178         }
1179         if (ret != LZMA_OK)
1180             return -1;
1181         if (!lzfile->strm.avail_out)
1182             return len;
1183         if (eof)
1184             return -1;
1185       }
1186 }
1187
1188 static ssize_t lzwrite(LZFILE *lzfile, void *buf, size_t len)
1189 {
1190     lzma_ret ret;
1191     size_t n;
1192     if (!lzfile || !lzfile->encoding)
1193         return -1;
1194     if (!len)
1195         return 0;
1196     lzfile->strm.next_in = buf;
1197     lzfile->strm.avail_in = len;
1198     for (;;) {
1199         lzfile->strm.next_out = lzfile->buf;
1200         lzfile->strm.avail_out = kBufferSize;
1201         ret = lzma_code(&lzfile->strm, LZMA_RUN);
1202         if (ret != LZMA_OK)
1203             return -1;
1204         n = kBufferSize - lzfile->strm.avail_out;
1205         if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
1206             return -1;
1207         if (!lzfile->strm.avail_in)
1208             return len;
1209     }
1210 }
1211
1212 /* =============================================================== */
1213
1214 static void * lzdFileno(FD_t fd)
1215 {
1216     void * rc = NULL;
1217     
1218     if (fd == NULL)
1219         return NULL;
1220
1221     for (int i = fd->nfps; i >= 0; i--) {
1222         FDSTACK_t * fps = &fd->fps[i];
1223         if (fps->io != xzdio && fps->io != lzdio)
1224             continue;
1225         rc = fps->fp;
1226         break;
1227     }
1228     return rc;
1229 }
1230
1231 static FD_t xzdOpen(const char * path, const char * mode)
1232 {
1233     FD_t fd;
1234     LZFILE *lzfile;
1235     if ((lzfile = xzopen(path, mode)) == NULL)
1236         return NULL;
1237     fd = fdNew();
1238     fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
1239     return fdLink(fd);
1240 }
1241
1242 static FD_t xzdFdopen(void * cookie, const char * fmode)
1243 {
1244     FD_t fd = c2f(cookie);
1245     int fdno;
1246     LZFILE *lzfile;
1247
1248     if (fd == NULL || fmode == NULL) return NULL;
1249     fdno = fdFileno(fd);
1250     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
1251     if (fdno < 0) return NULL;
1252     lzfile = xzdopen(fdno, fmode);
1253     if (lzfile == NULL) return NULL;
1254     fdPush(fd, xzdio, lzfile, fdno);
1255     return fdLink(fd);
1256 }
1257
1258 static FD_t lzdOpen(const char * path, const char * mode)
1259 {
1260     FD_t fd;
1261     LZFILE *lzfile;
1262     if ((lzfile = lzopen(path, mode)) == NULL)
1263         return NULL;
1264     fd = fdNew();
1265     fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
1266     return fdLink(fd);
1267 }
1268
1269 static FD_t lzdFdopen(void * cookie, const char * fmode)
1270 {
1271     FD_t fd = c2f(cookie);
1272     int fdno;
1273     LZFILE *lzfile;
1274
1275     if (fd == NULL || fmode == NULL) return NULL;
1276     fdno = fdFileno(fd);
1277     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
1278     if (fdno < 0) return NULL;
1279     lzfile = lzdopen(fdno, fmode);
1280     if (lzfile == NULL) return NULL;
1281     fdPush(fd, xzdio, lzfile, fdno);
1282     return fdLink(fd);
1283 }
1284
1285 static int lzdFlush(FD_t fd)
1286 {
1287     return lzflush(lzdFileno(fd));
1288 }
1289
1290 /* =============================================================== */
1291 static ssize_t lzdRead(void * cookie, char * buf, size_t count)
1292 {
1293     FD_t fd = c2f(cookie);
1294     LZFILE *lzfile;
1295     ssize_t rc = 0;
1296
1297     if (fd == NULL || fd->bytesRemain == 0) return 0; /* XXX simulate EOF */
1298     lzfile = lzdFileno(fd);
1299     fdstat_enter(fd, FDSTAT_READ);
1300     if (lzfile)
1301         rc = lzread(lzfile, buf, count);
1302     if (rc == -1) {
1303         fd->errcookie = "Lzma: decoding error";
1304     } else if (rc >= 0) {
1305         fdstat_exit(fd, FDSTAT_READ, rc);
1306         if (fd->digests && rc > 0) fdUpdateDigests(fd, (void *)buf, rc);
1307     }
1308     return rc;
1309 }
1310
1311 static ssize_t lzdWrite(void * cookie, const char * buf, size_t count)
1312 {
1313     FD_t fd = c2f(cookie);
1314     LZFILE *lzfile;
1315     ssize_t rc = 0;
1316
1317     if (fd == NULL || fd->bytesRemain == 0) return 0;   /* XXX simulate EOF */
1318
1319     if (fd->digests && count > 0) fdUpdateDigests(fd, (void *)buf, count);
1320
1321     lzfile = lzdFileno(fd);
1322
1323     fdstat_enter(fd, FDSTAT_WRITE);
1324     rc = lzwrite(lzfile, (void *)buf, count);
1325     if (rc < 0) {
1326         fd->errcookie = "Lzma: encoding error";
1327     } else if (rc > 0) {
1328         fdstat_exit(fd, FDSTAT_WRITE, rc);
1329     }
1330     return rc;
1331 }
1332
1333 static int lzdClose(void * cookie)
1334 {
1335     FD_t fd = c2f(cookie);
1336     LZFILE *lzfile;
1337     int rc;
1338
1339     lzfile = lzdFileno(fd);
1340
1341     if (lzfile == NULL) return -2;
1342     fdstat_enter(fd, FDSTAT_CLOSE);
1343     rc = lzclose(lzfile);
1344
1345     /* XXX TODO: preserve fd if errors */
1346
1347     if (fd) {
1348         if (rc == -1) {
1349             fd->errcookie = strerror(ferror(lzfile->file));
1350         } else if (rc >= 0) {
1351             fdstat_exit(fd, FDSTAT_CLOSE, rc);
1352         }
1353     }
1354
1355 DBGIO(fd, (stderr, "==>\tlzdClose(%p) rc %lx %s\n", cookie, (unsigned long)rc, fdbg(fd)));
1356
1357     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "XZDIO", stderr);
1358     if (rc == 0)
1359         fd = fdFree(fd);
1360     return rc;
1361 }
1362
1363 static struct FDIO_s xzdio_s = {
1364   lzdRead, lzdWrite, fdSeekNot, lzdClose, NULL, NULL, NULL, fdFileno,
1365   NULL, xzdOpen, lzdFileno, lzdFlush
1366 };
1367 static const FDIO_t xzdio = &xzdio_s;
1368
1369 static struct FDIO_s lzdio_s = {
1370   lzdRead, lzdWrite, fdSeekNot, lzdClose, NULL, NULL, NULL, fdFileno,
1371   NULL, lzdOpen, lzdFileno, lzdFlush
1372 };
1373 static const FDIO_t lzdio = &lzdio_s;
1374
1375 #endif  /* HAVE_LZMA_H */
1376
1377 /* =============================================================== */
1378
1379 const char *Fstrerror(FD_t fd)
1380 {
1381     if (fd == NULL)
1382         return (errno ? strerror(errno) : "");
1383     return getFdErrstr(fd);
1384 }
1385
1386 #define FDIOVEC(_fd, _vec)      \
1387   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
1388
1389 ssize_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) {
1390     fdio_read_function_t _read;
1391     int rc;
1392
1393     if (fd == NULL)
1394         return 0;
1395
1396     if (fdGetIo(fd) == fpio) {
1397         rc = fread(buf, size, nmemb, fdGetFILE(fd));
1398         return rc;
1399     }
1400
1401     _read = FDIOVEC(fd, read);
1402
1403     rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
1404     return rc;
1405 }
1406
1407 ssize_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
1408 {
1409     fdio_write_function_t _write;
1410     int rc;
1411
1412     if (fd == NULL)
1413         return 0;
1414
1415     if (fdGetIo(fd) == fpio) {
1416         rc = fwrite(buf, size, nmemb, fdGetFILE(fd));
1417         return rc;
1418     }
1419
1420     _write = FDIOVEC(fd, write);
1421
1422     rc = (_write ? _write(fd, buf, size * nmemb) : -2);
1423     return rc;
1424 }
1425
1426 int Fseek(FD_t fd, _libio_off_t offset, int whence) {
1427     fdio_seek_function_t _seek;
1428 #ifdef USE_COOKIE_SEEK_POINTER
1429     _IO_off64_t o64 = offset;
1430     _libio_pos_t pos = &o64;
1431 #else
1432     _libio_pos_t pos = offset;
1433 #endif
1434
1435     long int rc;
1436
1437     if (fd == NULL)
1438         return -1;
1439
1440     if (fdGetIo(fd) == fpio) {
1441         FILE *fp;
1442
1443         fp = fdGetFILE(fd);
1444         rc = fseek(fp, offset, whence);
1445         return rc;
1446     }
1447
1448     _seek = FDIOVEC(fd, seek);
1449
1450     rc = (_seek ? _seek(fd, pos, whence) : -2);
1451     return rc;
1452 }
1453
1454 int Fclose(FD_t fd)
1455 {
1456     int rc = 0, ec = 0;
1457
1458     if (fd == NULL)
1459         return -1;
1460
1461     fd = fdLink(fd);
1462     while (fd->nfps >= 0) {
1463         FDSTACK_t * fps = &fd->fps[fd->nfps];
1464         
1465         if (fps->io == fpio) {
1466             FILE *fp;
1467             int fpno;
1468
1469             fp = fdGetFILE(fd);
1470             fpno = fileno(fp);
1471             if (fp)
1472                 rc = fclose(fp);
1473             if (fpno == -1) {
1474                 fd = fdFree(fd);
1475                 fdPop(fd);
1476             }
1477         } else {
1478             fdio_close_function_t _close = FDIOVEC(fd, close);
1479             rc = _close ? _close(fd) : -2;
1480         }
1481         if (fd->nfps == 0)
1482             break;
1483         if (ec == 0 && rc)
1484             ec = rc;
1485         fdPop(fd);
1486     }
1487     fd = fdFree(fd);
1488     return ec;
1489 }
1490
1491 /**
1492  * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
1493  *      returns stdio[0] = NUL on error.
1494  *
1495  * - gzopen:    [0-9] is compression level
1496  * - gzopen:    'f' is filtered (Z_FILTERED)
1497  * - gzopen:    'h' is Huffman encoding (Z_HUFFMAN_ONLY)
1498  * - bzopen:    [1-9] is block size (modulo 100K)
1499  * - bzopen:    's' is smallmode
1500  * - HACK:      '.' terminates, rest is type of I/O
1501  */
1502 static void cvtfmode (const char *m,
1503                                 char *stdio, size_t nstdio,
1504                                 char *other, size_t nother,
1505                                 const char **end, int * f)
1506 {
1507     int flags = 0;
1508     char c;
1509
1510     switch (*m) {
1511     case 'a':
1512         flags |= O_WRONLY | O_CREAT | O_APPEND;
1513         if (--nstdio > 0) *stdio++ = *m;
1514         break;
1515     case 'w':
1516         flags |= O_WRONLY | O_CREAT | O_TRUNC;
1517         if (--nstdio > 0) *stdio++ = *m;
1518         break;
1519     case 'r':
1520         flags |= O_RDONLY;
1521         if (--nstdio > 0) *stdio++ = *m;
1522         break;
1523     default:
1524         *stdio = '\0';
1525         return;
1526         break;
1527     }
1528     m++;
1529
1530     while ((c = *m++) != '\0') {
1531         switch (c) {
1532         case '.':
1533             break;
1534         case '+':
1535             flags &= ~(O_RDONLY|O_WRONLY);
1536             flags |= O_RDWR;
1537             if (--nstdio > 0) *stdio++ = c;
1538             continue;
1539             break;
1540         case 'b':
1541             if (--nstdio > 0) *stdio++ = c;
1542             continue;
1543             break;
1544         case 'x':
1545             flags |= O_EXCL;
1546             if (--nstdio > 0) *stdio++ = c;
1547             continue;
1548             break;
1549         default:
1550             if (--nother > 0) *other++ = c;
1551             continue;
1552             break;
1553         }
1554         break;
1555     }
1556
1557     *stdio = *other = '\0';
1558     if (end != NULL)
1559         *end = (*m != '\0' ? m : NULL);
1560     if (f != NULL)
1561         *f = flags;
1562 }
1563
1564 #if _USE_LIBIO
1565 #if defined(__GLIBC__) && __GLIBC__ == 2 && __GLIBC_MINOR__ == 0
1566 /* XXX retrofit glibc-2.1.x typedef on glibc-2.0.x systems */
1567 typedef _IO_cookie_io_functions_t cookie_io_functions_t;
1568 #endif
1569 #endif
1570
1571 FD_t Fdopen(FD_t ofd, const char *fmode)
1572 {
1573     char stdio[20], other[20], zstdio[20];
1574     const char *end = NULL;
1575     FDIO_t iof = NULL;
1576     FD_t fd = ofd;
1577
1578 if (_rpmio_debug)
1579 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
1580
1581     if (fd == NULL || fmode == NULL)
1582         return NULL;
1583
1584     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
1585     if (stdio[0] == '\0')
1586         return NULL;
1587     zstdio[0] = '\0';
1588     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio));
1589     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio));
1590
1591     if (end == NULL && other[0] == '\0')
1592         return fd;
1593
1594     if (end && *end) {
1595         if (rstreq(end, "fdio")) {
1596             iof = fdio;
1597 #if HAVE_ZLIB_H
1598         } else if (rstreq(end, "gzdio") || rstreq(end, "gzip")) {
1599             iof = gzdio;
1600             fd = gzdFdopen(fd, zstdio);
1601 #endif
1602 #if HAVE_BZLIB_H
1603         } else if (rstreq(end, "bzdio") || rstreq(end, "bzip2")) {
1604             iof = bzdio;
1605             fd = bzdFdopen(fd, zstdio);
1606 #endif
1607 #if HAVE_LZMA_H
1608         } else if (rstreq(end, "xzdio") || rstreq(end, "xz")) {
1609             iof = xzdio;
1610             fd = xzdFdopen(fd, zstdio);
1611         } else if (rstreq(end, "lzdio") || rstreq(end, "lzma")) {
1612             iof = lzdio;
1613             fd = lzdFdopen(fd, zstdio);
1614 #endif
1615         } else if (rstreq(end, "ufdio")) {
1616             iof = ufdio;
1617         } else if (rstreq(end, "fpio")) {
1618             iof = fpio;
1619             if (noLibio) {
1620                 int fdno = Fileno(fd);
1621                 FILE * fp = fdopen(fdno, stdio);
1622 if (_rpmio_debug)
1623 fprintf(stderr, "*** Fdopen fpio fp %p\n", (void *)fp);
1624                 if (fp == NULL)
1625                     return NULL;
1626                 /* XXX gzdio/bzdio use fp for private data */
1627                 if (fdGetFp(fd) == NULL)
1628                     fdSetFp(fd, fp);
1629                 fdPush(fd, fpio, fp, fdno);     /* Push fpio onto stack */
1630             }
1631         }
1632     } else if (other[0] != '\0') {
1633         for (end = other; *end && strchr("0123456789fh", *end); end++)
1634             {};
1635         if (*end == '\0') {
1636             iof = gzdio;
1637             fd = gzdFdopen(fd, zstdio);
1638         }
1639     }
1640     if (iof == NULL)
1641         return fd;
1642
1643     if (!noLibio) {
1644         FILE * fp = NULL;
1645
1646 #if _USE_LIBIO
1647         {   cookie_io_functions_t ciof;
1648             ciof.read = iof->read;
1649             ciof.write = iof->write;
1650             ciof.seek = iof->seek;
1651             ciof.close = iof->close;
1652             fp = fopencookie(fd, stdio, ciof);
1653 DBGIO(fd, (stderr, "==> fopencookie(%p,\"%s\",*%p) returns fp %p\n", fd, stdio, iof, fp));
1654         }
1655 #endif
1656
1657         if (fp) {
1658             /* XXX gzdio/bzdio use fp for private data */
1659             if (fdGetFp(fd) == NULL)
1660                 fdSetFp(fd, fp);
1661             fdPush(fd, fpio, fp, fileno(fp));   /* Push fpio onto stack */
1662             fd = fdLink(fd);
1663         }
1664     }
1665
1666 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
1667     return fd;
1668 }
1669
1670 FD_t Fopen(const char *path, const char *fmode)
1671 {
1672     char stdio[20], other[20];
1673     const char *end = NULL;
1674     mode_t perms = 0666;
1675     int flags = 0;
1676     FD_t fd;
1677
1678     if (path == NULL || fmode == NULL)
1679         return NULL;
1680
1681     stdio[0] = '\0';
1682     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
1683     if (stdio[0] == '\0')
1684         return NULL;
1685
1686     if (end == NULL || rstreq(end, "fdio")) {
1687 if (_rpmio_debug)
1688 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
1689         fd = fdOpen(path, flags, perms);
1690         if (fdFileno(fd) < 0) {
1691             if (fd) (void) fdClose(fd);
1692             return NULL;
1693         }
1694     } else {
1695         /* XXX gzdio and bzdio here too */
1696
1697         switch (urlIsURL(path)) {
1698         case URL_IS_HTTPS:
1699         case URL_IS_HTTP:
1700         case URL_IS_HKP:
1701         case URL_IS_PATH:
1702         case URL_IS_DASH:
1703         case URL_IS_FTP:
1704         case URL_IS_UNKNOWN:
1705 if (_rpmio_debug)
1706 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
1707             fd = ufdOpen(path, flags, perms);
1708             if (fd == NULL || !(fdFileno(fd) >= 0))
1709                 return fd;
1710             break;
1711         default:
1712 if (_rpmio_debug)
1713 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
1714             return NULL;
1715             break;
1716         }
1717
1718     }
1719
1720     if (fd)
1721         fd = Fdopen(fd, fmode);
1722     return fd;
1723 }
1724
1725 int Fflush(FD_t fd)
1726 {
1727     void * vh;
1728     if (fd == NULL) return -1;
1729     if (fdGetIo(fd) == fpio)
1730         return fflush(fdGetFILE(fd));
1731
1732     vh = fdGetFp(fd);
1733 #if HAVE_ZLIB_H
1734     if (vh && fdGetIo(fd) == gzdio)
1735         return gzdFlush(vh);
1736 #endif
1737 #if HAVE_BZLIB_H
1738     if (vh && fdGetIo(fd) == bzdio)
1739         return bzdFlush(vh);
1740 #endif
1741 #if HAVE_LZMA_H
1742     if (vh && (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio))
1743         return lzdFlush(vh);
1744 #endif
1745 /* FIXME: If we get here, something went wrong above */
1746     return 0;
1747 }
1748
1749 off_t Ftell(FD_t fd)
1750 {
1751     FDIO_t iot;
1752     off_t pos = -2; /* assume not implemented */
1753
1754     if (fd == NULL) return -1;
1755     iot = fdGetIo(fd);
1756     /* this wont work correctly for compressed types */
1757     if (iot == fpio || iot == fdio || iot == ufdio) {
1758         pos = lseek(Fileno(fd), 0, SEEK_CUR);
1759     }
1760
1761     return pos;
1762 }
1763
1764 int Ferror(FD_t fd)
1765 {
1766     int i, rc = 0;
1767
1768     if (fd == NULL) return -1;
1769     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
1770         FDSTACK_t * fps = &fd->fps[i];
1771         int ec;
1772         
1773         if (fps->io == fpio) {
1774             ec = ferror(fdGetFILE(fd));
1775 #if HAVE_ZLIB_H
1776         } else if (fps->io == gzdio) {
1777             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
1778             i--;        /* XXX fdio under gzdio always has fdno == -1 */
1779 #endif
1780 #if HAVE_BZLIB_H
1781         } else if (fps->io == bzdio) {
1782             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
1783             i--;        /* XXX fdio under bzdio always has fdno == -1 */
1784 #endif
1785 #if HAVE_LZMA_H
1786         } else if (fps->io == xzdio || fps->io == lzdio) {
1787             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
1788             i--;        /* XXX fdio under xzdio/lzdio always has fdno == -1 */
1789 #endif
1790         } else {
1791         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
1792             ec = (fdFileno(fd) < 0 ? -1 : 0);
1793         }
1794
1795         if (rc == 0 && ec)
1796             rc = ec;
1797     }
1798 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
1799     return rc;
1800 }
1801
1802 int Fileno(FD_t fd)
1803 {
1804     int i, rc = -1;
1805
1806     if (fd == NULL) return -1;
1807     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
1808         rc = fd->fps[i].fdno;
1809     }
1810     
1811 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
1812     return rc;
1813 }
1814
1815 /* XXX this is naive */
1816 int Fcntl(FD_t fd, int op, void *lip)
1817 {
1818     return fcntl(Fileno(fd), op, lip);
1819 }
1820
1821 rpmop fdOp(FD_t fd, fdOpX opx)
1822 {
1823     rpmop op = NULL;
1824
1825     if (fd != NULL && fd->stats != NULL && opx >= 0 && opx < FDSTAT_MAX)
1826         op = fd->stats->ops + opx;
1827     return op;
1828 }
1829
1830 int rpmioSlurp(const char * fn, uint8_t ** bp, ssize_t * blenp)
1831 {
1832     static const ssize_t blenmax = (32 * BUFSIZ);
1833     ssize_t blen = 0;
1834     uint8_t * b = NULL;
1835     ssize_t size;
1836     FD_t fd;
1837     int rc = 0;
1838
1839     fd = Fopen(fn, "r.ufdio");
1840     if (fd == NULL || Ferror(fd)) {
1841         rc = 2;
1842         goto exit;
1843     }
1844
1845     size = fdSize(fd);
1846     blen = (size >= 0 ? size : blenmax);
1847     if (blen) {
1848         int nb;
1849         b = xmalloc(blen+1);
1850         b[0] = '\0';
1851         nb = Fread(b, sizeof(*b), blen, fd);
1852         if (Ferror(fd) || (size > 0 && nb != blen)) {
1853             rc = 1;
1854             goto exit;
1855         }
1856         if (blen == blenmax && nb < blen) {
1857             blen = nb;
1858             b = xrealloc(b, blen+1);
1859         }
1860         b[blen] = '\0';
1861     }
1862
1863 exit:
1864     if (fd) (void) Fclose(fd);
1865         
1866     if (rc) {
1867         if (b) free(b);
1868         b = NULL;
1869         blen = 0;
1870     }
1871
1872     if (bp) *bp = b;
1873     else if (b) free(b);
1874
1875     if (blenp) *blenp = blen;
1876
1877     return rc;
1878 }
1879
1880 static const struct FDIO_s fpio_s = {
1881   fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
1882   ufdOpen, NULL, fdGetFp, NULL
1883 };
1884 static const FDIO_t fpio = &fpio_s ;
1885
1886 void fdInitDigest(FD_t fd, pgpHashAlgo hashalgo, int flags)
1887 {
1888     if (fd->digests == NULL) {
1889         fd->digests = rpmDigestBundleNew();
1890     }
1891     fdstat_enter(fd, FDSTAT_DIGEST);
1892     rpmDigestBundleAdd(fd->digests, hashalgo, flags);
1893     fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
1894 }
1895
1896 static void fdUpdateDigests(FD_t fd, const unsigned char * buf, size_t buflen)
1897 {
1898     if (fd && fd->digests) {
1899         fdstat_enter(fd, FDSTAT_DIGEST);
1900         rpmDigestBundleUpdate(fd->digests, buf, buflen);
1901         fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) buflen);
1902     }
1903 }
1904
1905 void fdFiniDigest(FD_t fd, pgpHashAlgo hashalgo,
1906                 void ** datap,
1907                 size_t * lenp,
1908                 int asAscii)
1909 {
1910     if (fd && fd->digests) {
1911         fdstat_enter(fd, FDSTAT_DIGEST);
1912         rpmDigestBundleFinal(fd->digests, hashalgo, datap, lenp, asAscii);
1913         fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
1914     }
1915 }
1916
1917