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