add support for setting xz memlimit
[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 off_t ufdCopy(FD_t sfd, FD_t tfd)
456 {
457     char buf[BUFSIZ];
458     ssize_t rdbytes, wrbytes;
459     off_t total = 0;
460
461     while (1) {
462         rdbytes = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
463
464         if (rdbytes > 0) {
465             wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, tfd);
466             if (wrbytes != rdbytes) {
467                 total = -1;
468                 break;
469             }
470             total += wrbytes;
471         } else {
472             if (rdbytes < 0)
473                 total = -1;
474             break;
475         }
476     }
477
478     return total;
479 }
480
481 /*
482  * Deal with remote url's by fetching them with a helper application
483  * and treat as local file afterwards.
484  * TODO:
485  * - better error checking + reporting 
486  * - curl & friends don't know about hkp://, transform to http?
487  */
488
489 static FD_t urlOpen(const char * url, int flags, mode_t mode)
490 {
491     FD_t fd;
492     char *dest = NULL;
493     int rc = 1; /* assume failure */
494
495     fd = rpmMkTempFile(NULL, &dest);
496     if (fd == NULL) {
497         return NULL;
498     }
499     Fclose(fd);
500
501     rc = urlGetFile(url, dest);
502     if (rc == 0) {
503         fd = fdOpen(dest, flags, mode);
504         unlink(dest);
505     } else {
506         fd = NULL;
507     }
508     dest = _free(dest);
509
510     return fd;
511
512 }
513 static FD_t ufdOpen(const char * url, int flags, mode_t mode)
514 {
515     FD_t fd = NULL;
516     const char * path;
517     urltype urlType = urlPath(url, &path);
518
519 if (_rpmio_debug)
520 fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mode);
521
522     switch (urlType) {
523     case URL_IS_FTP:
524     case URL_IS_HTTPS:
525     case URL_IS_HTTP:
526     case URL_IS_HKP:
527         fd = urlOpen(url, flags, mode);
528         /* we're dealing with local file when urlOpen() returns */
529         urlType = URL_IS_UNKNOWN;
530         break;
531     case URL_IS_DASH:
532         if ((flags & O_ACCMODE) == O_RDWR) {
533             fd = NULL;
534         } else {
535             fd = fdDup((flags & O_ACCMODE) == O_WRONLY ?
536                         STDOUT_FILENO : STDIN_FILENO);
537         }
538         break;
539     case URL_IS_PATH:
540     case URL_IS_UNKNOWN:
541     default:
542         fd = fdOpen(path, flags, mode);
543         break;
544     }
545
546     if (fd == NULL) return NULL;
547
548     fdSetIo(fd, ufdio);
549     fd->urlType = urlType;
550
551     if (Fileno(fd) < 0) {
552         (void) fdClose(fd);
553         return NULL;
554     }
555     return fd;
556 }
557
558 static const struct FDIO_s ufdio_s = {
559   fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno,
560   ufdOpen, NULL, fdGetFp, fdFlush, fdTell
561 };
562 static const FDIO_t ufdio = &ufdio_s ;
563
564 /* =============================================================== */
565 /* Support for GZIP library.  */
566 #include <zlib.h>
567
568 static void * gzdFileno(FD_t fd)
569 {
570     return iotFileno(fd, gzdio);
571 }
572
573 static
574 FD_t gzdOpen(const char * path, const char * fmode)
575 {
576     FD_t fd;
577     gzFile gzfile;
578     if ((gzfile = gzopen(path, fmode)) == NULL)
579         return NULL;
580     fd = fdNew(path);
581     fdPop(fd); fdPush(fd, gzdio, gzfile, -1);
582     
583     return fdLink(fd);
584 }
585
586 static FD_t gzdFdopen(FD_t fd, const char *fmode)
587 {
588     int fdno;
589     gzFile gzfile;
590
591     if (fd == NULL || fmode == NULL) return NULL;
592     fdno = fdFileno(fd);
593     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
594     if (fdno < 0) return NULL;
595     gzfile = gzdopen(fdno, fmode);
596     if (gzfile == NULL) return NULL;
597
598     fdPush(fd, gzdio, gzfile, fdno);            /* Push gzdio onto stack */
599
600     return fdLink(fd);
601 }
602
603 static int gzdFlush(FD_t fd)
604 {
605     gzFile gzfile;
606     gzfile = gzdFileno(fd);
607     if (gzfile == NULL) return -2;
608     return gzflush(gzfile, Z_SYNC_FLUSH);       /* XXX W2DO? */
609 }
610
611 static ssize_t gzdRead(FD_t fd, void * buf, size_t count)
612 {
613     gzFile gzfile;
614     ssize_t rc;
615
616     gzfile = gzdFileno(fd);
617     if (gzfile == NULL) return -2;      /* XXX can't happen */
618
619     rc = gzread(gzfile, buf, count);
620     if (rc < 0) {
621         int zerror = 0;
622         fd->errcookie = gzerror(gzfile, &zerror);
623         if (zerror == Z_ERRNO) {
624             fd->syserrno = errno;
625             fd->errcookie = strerror(fd->syserrno);
626         }
627     }
628     return rc;
629 }
630
631 static ssize_t gzdWrite(FD_t fd, const void * buf, size_t count)
632 {
633     gzFile gzfile;
634     ssize_t rc;
635
636     gzfile = gzdFileno(fd);
637     if (gzfile == NULL) return -2;      /* XXX can't happen */
638
639     rc = gzwrite(gzfile, (void *)buf, count);
640     if (rc < 0) {
641         int zerror = 0;
642         fd->errcookie = gzerror(gzfile, &zerror);
643         if (zerror == Z_ERRNO) {
644             fd->syserrno = errno;
645             fd->errcookie = strerror(fd->syserrno);
646         }
647     }
648     return rc;
649 }
650
651 /* XXX zlib-1.0.4 has not */
652 static int gzdSeek(FD_t fd, off_t pos, int whence)
653 {
654     off_t p = pos;
655     int rc;
656 #if HAVE_GZSEEK
657     gzFile gzfile;
658
659     if (fd == NULL) return -2;
660
661     gzfile = gzdFileno(fd);
662     if (gzfile == NULL) return -2;      /* XXX can't happen */
663
664     rc = gzseek(gzfile, p, whence);
665     if (rc < 0) {
666         int zerror = 0;
667         fd->errcookie = gzerror(gzfile, &zerror);
668         if (zerror == Z_ERRNO) {
669             fd->syserrno = errno;
670             fd->errcookie = strerror(fd->syserrno);
671         }
672     }
673 #else
674     rc = -2;
675 #endif
676     return rc;
677 }
678
679 static int gzdClose(FD_t fd)
680 {
681     gzFile gzfile;
682     int rc;
683
684     gzfile = gzdFileno(fd);
685     if (gzfile == NULL) return -2;      /* XXX can't happen */
686
687     rc = gzclose(gzfile);
688
689     /* XXX TODO: preserve fd if errors */
690
691     if (fd) {
692         if (rc < 0) {
693             fd->errcookie = "gzclose error";
694             if (rc == Z_ERRNO) {
695                 fd->syserrno = errno;
696                 fd->errcookie = strerror(fd->syserrno);
697             }
698         }
699     }
700
701     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr);
702     if (rc == 0)
703         fdFree(fd);
704     return rc;
705 }
706
707 static long gzdTell(FD_t fd)
708 {
709     off_t pos = -1;
710     gzFile gzfile = gzdFileno(fd);
711
712     if (gzfile != NULL) {
713 #if HAVE_GZSEEK
714         pos = gztell(gzfile);
715         if (pos < 0) {
716             int zerror = 0;
717             fd->errcookie = gzerror(gzfile, &zerror);
718             if (zerror == Z_ERRNO) {
719                 fd->syserrno = errno;
720                 fd->errcookie = strerror(fd->syserrno);
721             }
722         }
723 #else
724         pos = -2;
725 #endif    
726     }
727     return pos;
728 }
729 static const struct FDIO_s gzdio_s = {
730   gzdRead, gzdWrite, gzdSeek, gzdClose, fdLink, fdFree, fdNew, fdFileno,
731   NULL, gzdOpen, gzdFileno, gzdFlush, gzdTell
732 };
733 static const FDIO_t gzdio = &gzdio_s ;
734
735 /* =============================================================== */
736 /* Support for BZIP2 library.  */
737 #if HAVE_BZLIB_H
738
739 #include <bzlib.h>
740
741 static void * bzdFileno(FD_t fd)
742 {
743     return iotFileno(fd, bzdio);
744 }
745
746 static FD_t bzdOpen(const char * path, const char * mode)
747 {
748     FD_t fd;
749     BZFILE *bzfile;;
750     if ((bzfile = BZ2_bzopen(path, mode)) == NULL)
751         return NULL;
752     fd = fdNew(path);
753     fdPop(fd); fdPush(fd, bzdio, bzfile, -1);
754     return fdLink(fd);
755 }
756
757 static FD_t bzdFdopen(FD_t fd, const char * fmode)
758 {
759     int fdno;
760     BZFILE *bzfile;
761
762     if (fd == NULL || fmode == NULL) return NULL;
763     fdno = fdFileno(fd);
764     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
765     if (fdno < 0) return NULL;
766     bzfile = BZ2_bzdopen(fdno, fmode);
767     if (bzfile == NULL) return NULL;
768
769     fdPush(fd, bzdio, bzfile, fdno);            /* Push bzdio onto stack */
770
771     return fdLink(fd);
772 }
773
774 static int bzdFlush(FD_t fd)
775 {
776     return BZ2_bzflush(bzdFileno(fd));
777 }
778
779 static ssize_t bzdRead(FD_t fd, void * buf, size_t count)
780 {
781     BZFILE *bzfile;
782     ssize_t rc = 0;
783
784     bzfile = bzdFileno(fd);
785     if (bzfile)
786         rc = BZ2_bzread(bzfile, buf, count);
787     if (rc == -1) {
788         int zerror = 0;
789         if (bzfile)
790             fd->errcookie = BZ2_bzerror(bzfile, &zerror);
791     }
792     return rc;
793 }
794
795 static ssize_t bzdWrite(FD_t fd, const void * buf, size_t count)
796 {
797     BZFILE *bzfile;
798     ssize_t rc;
799
800     bzfile = bzdFileno(fd);
801     rc = BZ2_bzwrite(bzfile, (void *)buf, count);
802     if (rc == -1) {
803         int zerror = 0;
804         fd->errcookie = BZ2_bzerror(bzfile, &zerror);
805     }
806     return rc;
807 }
808
809 static int bzdClose(FD_t fd)
810 {
811     BZFILE *bzfile;
812     int rc;
813
814     bzfile = bzdFileno(fd);
815
816     if (bzfile == NULL) return -2;
817     /* FIX: check rc */
818     BZ2_bzclose(bzfile);
819     rc = 0;     /* XXX FIXME */
820
821     /* XXX TODO: preserve fd if errors */
822
823     if (fd) {
824         if (rc == -1) {
825             int zerror = 0;
826             fd->errcookie = BZ2_bzerror(bzfile, &zerror);
827         }
828     }
829
830     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr);
831     if (rc == 0)
832         fdFree(fd);
833     return rc;
834 }
835
836 static const struct FDIO_s bzdio_s = {
837   bzdRead, bzdWrite, NULL, bzdClose, fdLink, fdFree, fdNew, fdFileno,
838   NULL, bzdOpen, bzdFileno, bzdFlush, NULL
839 };
840 static const FDIO_t bzdio = &bzdio_s ;
841
842 #endif  /* HAVE_BZLIB_H */
843
844 static const char * getFdErrstr (FD_t fd)
845 {
846     const char *errstr = NULL;
847
848     if (fdGetIo(fd) == gzdio) {
849         errstr = fd->errcookie;
850     } else
851 #ifdef  HAVE_BZLIB_H
852     if (fdGetIo(fd) == bzdio) {
853         errstr = fd->errcookie;
854     } else
855 #endif  /* HAVE_BZLIB_H */
856 #ifdef  HAVE_LZMA_H
857     if (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio) {
858         errstr = fd->errcookie;
859     } else
860 #endif  /* HAVE_LZMA_H */
861     {
862         errstr = (fd->syserrno ? strerror(fd->syserrno) : "");
863     }
864
865     return errstr;
866 }
867
868 /* =============================================================== */
869 /* Support for LZMA library.  */
870
871 #ifdef HAVE_LZMA_H
872
873 #include <sys/types.h>
874 #include <inttypes.h>
875 #include <lzma.h>
876
877 #define kBufferSize (1 << 15)
878
879 typedef struct lzfile {
880   /* IO buffer */
881     uint8_t buf[kBufferSize];
882
883     lzma_stream strm;
884
885     FILE *file;
886
887     int encoding;
888     int eof;
889
890 } LZFILE;
891
892 static LZFILE *lzopen_internal(const char *path, const char *mode, int fd, int xz)
893 {
894     int level = LZMA_PRESET_DEFAULT;
895     int encoding = 0;
896     FILE *fp;
897     LZFILE *lzfile;
898     lzma_ret ret;
899     lzma_stream init_strm = LZMA_STREAM_INIT;
900     uint64_t mem_limit = rpmExpandNumeric("%{_xz_memlimit}");
901
902     for (; *mode; mode++) {
903         if (*mode == 'w')
904             encoding = 1;
905         else if (*mode == 'r')
906             encoding = 0;
907         else if (*mode >= '0' && *mode <= '9')
908             level = *mode - '0';
909     }
910     if (fd != -1)
911         fp = fdopen(fd, encoding ? "w" : "r");
912     else
913         fp = fopen(path, encoding ? "w" : "r");
914     if (!fp)
915         return 0;
916     lzfile = calloc(1, sizeof(*lzfile));
917     if (!lzfile) {
918         fclose(fp);
919         return 0;
920     }
921     
922     lzfile->file = fp;
923     lzfile->encoding = encoding;
924     lzfile->eof = 0;
925     lzfile->strm = init_strm;
926     if (encoding) {
927         if (xz) {
928             ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256);
929         } else {
930             lzma_options_lzma options;
931             lzma_lzma_preset(&options, level);
932             ret = lzma_alone_encoder(&lzfile->strm, &options);
933         }
934     } else {    /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */
935         ret = lzma_auto_decoder(&lzfile->strm, mem_limit ? mem_limit : 100<<20, 0);
936     }
937     if (ret != LZMA_OK) {
938         switch (ret) {
939             case LZMA_MEM_ERROR:
940                 rpmlog(RPMLOG_ERR, "liblzma: Memory allocation failed");
941                 break;
942
943             case LZMA_DATA_ERROR:
944                 rpmlog(RPMLOG_ERR, "liblzma: File size limits exceeded");
945                 break;
946
947             default:
948                 rpmlog(RPMLOG_ERR, "liblzma: <Unknown error (%d), possibly a bug", ret);
949                 break;
950         }
951         fclose(fp);
952         free(lzfile);
953         return 0;
954     }
955     return lzfile;
956 }
957
958 static LZFILE *xzopen(const char *path, const char *mode)
959 {
960     return lzopen_internal(path, mode, -1, 1);
961 }
962
963 static LZFILE *xzdopen(int fd, const char *mode)
964 {
965     if (fd < 0)
966         return 0;
967     return lzopen_internal(0, mode, fd, 1);
968 }
969
970 static LZFILE *lzopen(const char *path, const char *mode)
971 {
972     return lzopen_internal(path, mode, -1, 0);
973 }
974
975 static LZFILE *lzdopen(int fd, const char *mode)
976 {
977     if (fd < 0)
978         return 0;
979     return lzopen_internal(0, mode, fd, 0);
980 }
981
982 static int lzflush(LZFILE *lzfile)
983 {
984     return fflush(lzfile->file);
985 }
986
987 static int lzclose(LZFILE *lzfile)
988 {
989     lzma_ret ret;
990     size_t n;
991     int rc;
992
993     if (!lzfile)
994         return -1;
995     if (lzfile->encoding) {
996         for (;;) {
997             lzfile->strm.avail_out = kBufferSize;
998             lzfile->strm.next_out = lzfile->buf;
999             ret = lzma_code(&lzfile->strm, LZMA_FINISH);
1000             if (ret != LZMA_OK && ret != LZMA_STREAM_END)
1001                 return -1;
1002             n = kBufferSize - lzfile->strm.avail_out;
1003             if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
1004                 return -1;
1005             if (ret == LZMA_STREAM_END)
1006                 break;
1007         }
1008     }
1009     lzma_end(&lzfile->strm);
1010     rc = fclose(lzfile->file);
1011     free(lzfile);
1012     return rc;
1013 }
1014
1015 static ssize_t lzread(LZFILE *lzfile, void *buf, size_t len)
1016 {
1017     lzma_ret ret;
1018     int eof = 0;
1019
1020     if (!lzfile || lzfile->encoding)
1021       return -1;
1022     if (lzfile->eof)
1023       return 0;
1024     lzfile->strm.next_out = buf;
1025     lzfile->strm.avail_out = len;
1026     for (;;) {
1027         if (!lzfile->strm.avail_in) {
1028             lzfile->strm.next_in = lzfile->buf;
1029             lzfile->strm.avail_in = fread(lzfile->buf, 1, kBufferSize, lzfile->file);
1030             if (!lzfile->strm.avail_in)
1031                 eof = 1;
1032         }
1033         ret = lzma_code(&lzfile->strm, LZMA_RUN);
1034         if (ret == LZMA_STREAM_END) {
1035             lzfile->eof = 1;
1036             return len - lzfile->strm.avail_out;
1037         }
1038         if (ret != LZMA_OK)
1039             return -1;
1040         if (!lzfile->strm.avail_out)
1041             return len;
1042         if (eof)
1043             return -1;
1044       }
1045 }
1046
1047 static ssize_t lzwrite(LZFILE *lzfile, void *buf, size_t len)
1048 {
1049     lzma_ret ret;
1050     size_t n;
1051     if (!lzfile || !lzfile->encoding)
1052         return -1;
1053     if (!len)
1054         return 0;
1055     lzfile->strm.next_in = buf;
1056     lzfile->strm.avail_in = len;
1057     for (;;) {
1058         lzfile->strm.next_out = lzfile->buf;
1059         lzfile->strm.avail_out = kBufferSize;
1060         ret = lzma_code(&lzfile->strm, LZMA_RUN);
1061         if (ret != LZMA_OK)
1062             return -1;
1063         n = kBufferSize - lzfile->strm.avail_out;
1064         if (n && fwrite(lzfile->buf, 1, n, lzfile->file) != n)
1065             return -1;
1066         if (!lzfile->strm.avail_in)
1067             return len;
1068     }
1069 }
1070
1071 static void * lzdFileno(FD_t fd)
1072 {
1073     void * rc = NULL;
1074     
1075     if (fd == NULL)
1076         return NULL;
1077
1078     for (int i = fd->nfps; i >= 0; i--) {
1079         FDSTACK_t * fps = &fd->fps[i];
1080         if (fps->io != xzdio && fps->io != lzdio)
1081             continue;
1082         rc = fps->fp;
1083         break;
1084     }
1085     return rc;
1086 }
1087
1088 static FD_t xzdOpen(const char * path, const char * mode)
1089 {
1090     FD_t fd;
1091     LZFILE *lzfile;
1092     if ((lzfile = xzopen(path, mode)) == NULL)
1093         return NULL;
1094     fd = fdNew(path);
1095     fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
1096     return fdLink(fd);
1097 }
1098
1099 static FD_t xzdFdopen(FD_t fd, const char * fmode)
1100 {
1101     int fdno;
1102     LZFILE *lzfile;
1103
1104     if (fd == NULL || fmode == NULL) return NULL;
1105     fdno = fdFileno(fd);
1106     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
1107     if (fdno < 0) return NULL;
1108     lzfile = xzdopen(fdno, fmode);
1109     if (lzfile == NULL) return NULL;
1110     fdPush(fd, xzdio, lzfile, fdno);
1111     return fdLink(fd);
1112 }
1113
1114 static FD_t lzdOpen(const char * path, const char * mode)
1115 {
1116     FD_t fd;
1117     LZFILE *lzfile;
1118     if ((lzfile = lzopen(path, mode)) == NULL)
1119         return NULL;
1120     fd = fdNew(path);
1121     fdPop(fd); fdPush(fd, xzdio, lzfile, -1);
1122     return fdLink(fd);
1123 }
1124
1125 static FD_t lzdFdopen(FD_t fd, const char * fmode)
1126 {
1127     int fdno;
1128     LZFILE *lzfile;
1129
1130     if (fd == NULL || fmode == NULL) return NULL;
1131     fdno = fdFileno(fd);
1132     fdSetFdno(fd, -1);          /* XXX skip the fdio close */
1133     if (fdno < 0) return NULL;
1134     lzfile = lzdopen(fdno, fmode);
1135     if (lzfile == NULL) return NULL;
1136     fdPush(fd, xzdio, lzfile, fdno);
1137     return fdLink(fd);
1138 }
1139
1140 static int lzdFlush(FD_t fd)
1141 {
1142     return lzflush(lzdFileno(fd));
1143 }
1144
1145 static ssize_t lzdRead(FD_t fd, void * buf, size_t count)
1146 {
1147     LZFILE *lzfile;
1148     ssize_t rc = 0;
1149
1150     lzfile = lzdFileno(fd);
1151     if (lzfile)
1152         rc = lzread(lzfile, buf, count);
1153     if (rc == -1) {
1154         fd->errcookie = "Lzma: decoding error";
1155     }
1156     return rc;
1157 }
1158
1159 static ssize_t lzdWrite(FD_t fd, const void * buf, size_t count)
1160 {
1161     LZFILE *lzfile;
1162     ssize_t rc = 0;
1163
1164     lzfile = lzdFileno(fd);
1165
1166     rc = lzwrite(lzfile, (void *)buf, count);
1167     if (rc < 0) {
1168         fd->errcookie = "Lzma: encoding error";
1169     }
1170     return rc;
1171 }
1172
1173 static int lzdClose(FD_t fd)
1174 {
1175     LZFILE *lzfile;
1176     int rc;
1177
1178     lzfile = lzdFileno(fd);
1179
1180     if (lzfile == NULL) return -2;
1181     rc = lzclose(lzfile);
1182
1183     /* XXX TODO: preserve fd if errors */
1184
1185     if (fd) {
1186         if (rc == -1) {
1187             fd->errcookie = "lzclose error";
1188             fd->syserrno = errno;
1189             fd->errcookie = strerror(fd->syserrno);
1190         }
1191     }
1192
1193     if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "XZDIO", stderr);
1194     if (rc == 0)
1195         fdFree(fd);
1196     return rc;
1197 }
1198
1199 static struct FDIO_s xzdio_s = {
1200   lzdRead, lzdWrite, NULL, lzdClose, NULL, NULL, NULL, fdFileno,
1201   NULL, xzdOpen, lzdFileno, lzdFlush, NULL
1202 };
1203 static const FDIO_t xzdio = &xzdio_s;
1204
1205 static struct FDIO_s lzdio_s = {
1206   lzdRead, lzdWrite, NULL, lzdClose, NULL, NULL, NULL, fdFileno,
1207   NULL, lzdOpen, lzdFileno, lzdFlush, NULL
1208 };
1209 static const FDIO_t lzdio = &lzdio_s;
1210
1211 #endif  /* HAVE_LZMA_H */
1212
1213 /* =============================================================== */
1214
1215 const char *Fstrerror(FD_t fd)
1216 {
1217     if (fd == NULL)
1218         return (errno ? strerror(errno) : "");
1219     return getFdErrstr(fd);
1220 }
1221
1222 #define FDIOVEC(_fd, _vec)      \
1223   ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL)
1224
1225 ssize_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd)
1226 {
1227     ssize_t rc = -1;
1228
1229     if (fd != NULL) {
1230         fdio_read_function_t _read = FDIOVEC(fd, read);
1231
1232         fdstat_enter(fd, FDSTAT_READ);
1233         do {
1234             rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2);
1235         } while (rc == -1 && errno == EINTR);
1236         fdstat_exit(fd, FDSTAT_READ, rc);
1237
1238         if (fd->digests && rc > 0)
1239             fdUpdateDigests(fd, buf, rc);
1240     }
1241
1242     DBGIO(fd, (stderr, "==>\tFread(%p,%p,%ld) rc %ld %s\n",
1243           fd, buf, (long)size * nmemb, (long)rc, fdbg(fd)));
1244
1245     return rc;
1246 }
1247
1248 ssize_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd)
1249 {
1250     ssize_t rc = -1;
1251
1252     if (fd != NULL) {
1253         fdio_write_function_t _write = FDIOVEC(fd, write);
1254         
1255         fdstat_enter(fd, FDSTAT_WRITE);
1256         do {
1257             rc = (_write ? _write(fd, buf, size * nmemb) : -2);
1258         } while (rc == -1 && errno == EINTR);
1259         fdstat_exit(fd, FDSTAT_WRITE, rc);
1260
1261         if (fd->digests && rc > 0)
1262             fdUpdateDigests(fd, buf, rc);
1263     }
1264
1265     DBGIO(fd, (stderr, "==>\tFwrite(%p,%p,%ld) rc %ld %s\n",
1266           fd, buf, (long)size * nmemb, (long)rc, fdbg(fd)));
1267
1268     return rc;
1269 }
1270
1271 int Fseek(FD_t fd, off_t offset, int whence)
1272 {
1273     int rc = -1;
1274
1275     if (fd != NULL) {
1276         fdio_seek_function_t _seek = FDIOVEC(fd, seek);
1277
1278         fdstat_enter(fd, FDSTAT_SEEK);
1279         rc = (_seek ? _seek(fd, offset, whence) : -2);
1280         fdstat_exit(fd, FDSTAT_SEEK, rc);
1281     }
1282
1283     DBGIO(fd, (stderr, "==>\tFseek(%p,%ld,%d) rc %lx %s\n",
1284           fd, (long)offset, whence, (unsigned long)rc, fdbg(fd)));
1285
1286     return rc;
1287 }
1288
1289 int Fclose(FD_t fd)
1290 {
1291     int rc = 0, ec = 0;
1292
1293     if (fd == NULL)
1294         return -1;
1295
1296     fd = fdLink(fd);
1297     fdstat_enter(fd, FDSTAT_CLOSE);
1298     while (fd->nfps >= 0) {
1299         fdio_close_function_t _close = FDIOVEC(fd, close);
1300         rc = _close ? _close(fd) : -2;
1301
1302         if (fd->nfps == 0)
1303             break;
1304         if (ec == 0 && rc)
1305             ec = rc;
1306         fdPop(fd);
1307     }
1308     fdstat_exit(fd, FDSTAT_CLOSE, rc);
1309     DBGIO(fd, (stderr, "==>\tFclose(%p) rc %lx %s\n",
1310           (fd ? fd : NULL), (unsigned long)rc, fdbg(fd)));
1311
1312     fdFree(fd);
1313     return ec;
1314 }
1315
1316 /**
1317  * Convert stdio fmode to open(2) mode, filtering out zlib/bzlib flags.
1318  *      returns stdio[0] = NUL on error.
1319  *
1320  * - gzopen:    [0-9] is compression level
1321  * - gzopen:    'f' is filtered (Z_FILTERED)
1322  * - gzopen:    'h' is Huffman encoding (Z_HUFFMAN_ONLY)
1323  * - bzopen:    [1-9] is block size (modulo 100K)
1324  * - bzopen:    's' is smallmode
1325  * - HACK:      '.' terminates, rest is type of I/O
1326  */
1327 static void cvtfmode (const char *m,
1328                                 char *stdio, size_t nstdio,
1329                                 char *other, size_t nother,
1330                                 const char **end, int * f)
1331 {
1332     int flags = 0;
1333     char c;
1334
1335     switch (*m) {
1336     case 'a':
1337         flags |= O_WRONLY | O_CREAT | O_APPEND;
1338         if (--nstdio > 0) *stdio++ = *m;
1339         break;
1340     case 'w':
1341         flags |= O_WRONLY | O_CREAT | O_TRUNC;
1342         if (--nstdio > 0) *stdio++ = *m;
1343         break;
1344     case 'r':
1345         flags |= O_RDONLY;
1346         if (--nstdio > 0) *stdio++ = *m;
1347         break;
1348     default:
1349         *stdio = '\0';
1350         return;
1351         break;
1352     }
1353     m++;
1354
1355     while ((c = *m++) != '\0') {
1356         switch (c) {
1357         case '.':
1358             break;
1359         case '+':
1360             flags &= ~(O_RDONLY|O_WRONLY);
1361             flags |= O_RDWR;
1362             if (--nstdio > 0) *stdio++ = c;
1363             continue;
1364             break;
1365         case 'b':
1366             if (--nstdio > 0) *stdio++ = c;
1367             continue;
1368             break;
1369         case 'x':
1370             flags |= O_EXCL;
1371             if (--nstdio > 0) *stdio++ = c;
1372             continue;
1373             break;
1374         default:
1375             if (--nother > 0) *other++ = c;
1376             continue;
1377             break;
1378         }
1379         break;
1380     }
1381
1382     *stdio = *other = '\0';
1383     if (end != NULL)
1384         *end = (*m != '\0' ? m : NULL);
1385     if (f != NULL)
1386         *f = flags;
1387 }
1388
1389 FD_t Fdopen(FD_t ofd, const char *fmode)
1390 {
1391     char stdio[20], other[20], zstdio[40];
1392     const char *end = NULL;
1393     FDIO_t iof = NULL;
1394     FD_t fd = ofd;
1395
1396 if (_rpmio_debug)
1397 fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd));
1398
1399     if (fd == NULL || fmode == NULL)
1400         return NULL;
1401
1402     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL);
1403     if (stdio[0] == '\0')
1404         return NULL;
1405     zstdio[0] = '\0';
1406     strncat(zstdio, stdio, sizeof(zstdio) - strlen(zstdio) - 1);
1407     strncat(zstdio, other, sizeof(zstdio) - strlen(zstdio) - 1);
1408
1409     if (end == NULL && other[0] == '\0')
1410         return fd;
1411
1412     if (end && *end) {
1413         if (rstreq(end, "fdio")) {
1414             iof = fdio;
1415         } else if (rstreq(end, "gzdio") || rstreq(end, "gzip")) {
1416             iof = gzdio;
1417             fd = gzdFdopen(fd, zstdio);
1418 #if HAVE_BZLIB_H
1419         } else if (rstreq(end, "bzdio") || rstreq(end, "bzip2")) {
1420             iof = bzdio;
1421             fd = bzdFdopen(fd, zstdio);
1422 #endif
1423 #if HAVE_LZMA_H
1424         } else if (rstreq(end, "xzdio") || rstreq(end, "xz")) {
1425             iof = xzdio;
1426             fd = xzdFdopen(fd, zstdio);
1427         } else if (rstreq(end, "lzdio") || rstreq(end, "lzma")) {
1428             iof = lzdio;
1429             fd = lzdFdopen(fd, zstdio);
1430 #endif
1431         } else if (rstreq(end, "ufdio")) {
1432             iof = ufdio;
1433         }
1434     } else if (other[0] != '\0') {
1435         for (end = other; *end && strchr("0123456789fh", *end); end++)
1436             {};
1437         if (*end == '\0') {
1438             iof = gzdio;
1439             fd = gzdFdopen(fd, zstdio);
1440         }
1441     }
1442     if (iof == NULL)
1443         return fd;
1444
1445 DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd)));
1446     return fd;
1447 }
1448
1449 FD_t Fopen(const char *path, const char *fmode)
1450 {
1451     char stdio[20], other[20];
1452     const char *end = NULL;
1453     mode_t perms = 0666;
1454     int flags = 0;
1455     FD_t fd;
1456
1457     if (path == NULL || fmode == NULL)
1458         return NULL;
1459
1460     stdio[0] = '\0';
1461     cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, &flags);
1462     if (stdio[0] == '\0')
1463         return NULL;
1464
1465     if (end == NULL || rstreq(end, "fdio")) {
1466 if (_rpmio_debug)
1467 fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode);
1468         fd = fdOpen(path, flags, perms);
1469         if (fdFileno(fd) < 0) {
1470             if (fd) (void) fdClose(fd);
1471             return NULL;
1472         }
1473     } else {
1474         /* XXX gzdio and bzdio here too */
1475
1476         switch (urlIsURL(path)) {
1477         case URL_IS_HTTPS:
1478         case URL_IS_HTTP:
1479         case URL_IS_HKP:
1480         case URL_IS_PATH:
1481         case URL_IS_DASH:
1482         case URL_IS_FTP:
1483         case URL_IS_UNKNOWN:
1484 if (_rpmio_debug)
1485 fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode);
1486             fd = ufdOpen(path, flags, perms);
1487             if (fd == NULL || !(fdFileno(fd) >= 0))
1488                 return fd;
1489             break;
1490         default:
1491 if (_rpmio_debug)
1492 fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode);
1493             return NULL;
1494             break;
1495         }
1496
1497     }
1498
1499     if (fd)
1500         fd = Fdopen(fd, fmode);
1501
1502     DBGIO(fd, (stderr, "==>\tFopen(\"%s\",%x,0%o) %s\n",
1503           path, (unsigned)flags, (unsigned)perms, fdbg(fd)));
1504
1505     return fd;
1506 }
1507
1508 int Fflush(FD_t fd)
1509 {
1510     int rc = -1;
1511     if (fd != NULL) {
1512         fdio_fflush_function_t _fflush = FDIOVEC(fd, _fflush);
1513
1514         rc = (_fflush ? _fflush(fd) : -2);
1515     }
1516     return rc;
1517 }
1518
1519 off_t Ftell(FD_t fd)
1520 {
1521     off_t pos = -1;
1522     if (fd != NULL) {
1523         fdio_ftell_function_t _ftell = FDIOVEC(fd, _ftell);
1524
1525         pos = (_ftell ? _ftell(fd) : -2);
1526     }
1527     return pos;
1528 }
1529
1530 int Ferror(FD_t fd)
1531 {
1532     int i, rc = 0;
1533
1534     if (fd == NULL) return -1;
1535     for (i = fd->nfps; rc == 0 && i >= 0; i--) {
1536         FDSTACK_t * fps = &fd->fps[i];
1537         int ec;
1538         
1539         if (fps->io == gzdio) {
1540             ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0;
1541             i--;        /* XXX fdio under gzdio always has fdno == -1 */
1542 #if HAVE_BZLIB_H
1543         } else if (fps->io == bzdio) {
1544             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
1545             i--;        /* XXX fdio under bzdio always has fdno == -1 */
1546 #endif
1547 #if HAVE_LZMA_H
1548         } else if (fps->io == xzdio || fps->io == lzdio) {
1549             ec = (fd->syserrno  || fd->errcookie != NULL) ? -1 : 0;
1550             i--;        /* XXX fdio under xzdio/lzdio always has fdno == -1 */
1551 #endif
1552         } else {
1553         /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */
1554             ec = (fdFileno(fd) < 0 ? -1 : 0);
1555         }
1556
1557         if (rc == 0 && ec)
1558             rc = ec;
1559     }
1560 DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd)));
1561     return rc;
1562 }
1563
1564 int Fileno(FD_t fd)
1565 {
1566     int i, rc = -1;
1567
1568     if (fd == NULL) return -1;
1569     for (i = fd->nfps ; rc == -1 && i >= 0; i--) {
1570         rc = fd->fps[i].fdno;
1571     }
1572     
1573 DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd)));
1574     return rc;
1575 }
1576
1577 /* XXX this is naive */
1578 int Fcntl(FD_t fd, int op, void *lip)
1579 {
1580     return fcntl(Fileno(fd), op, lip);
1581 }
1582
1583 rpmop fdOp(FD_t fd, fdOpX opx)
1584 {
1585     rpmop op = NULL;
1586
1587     if (fd != NULL && fd->stats != NULL && opx >= 0 && opx < FDSTAT_MAX)
1588         op = fd->stats->ops + opx;
1589     return op;
1590 }
1591
1592 int rpmioSlurp(const char * fn, uint8_t ** bp, ssize_t * blenp)
1593 {
1594     static const ssize_t blenmax = (32 * BUFSIZ);
1595     ssize_t blen = 0;
1596     uint8_t * b = NULL;
1597     ssize_t size;
1598     FD_t fd;
1599     int rc = 0;
1600
1601     fd = Fopen(fn, "r.ufdio");
1602     if (fd == NULL || Ferror(fd)) {
1603         rc = 2;
1604         goto exit;
1605     }
1606
1607     size = fdSize(fd);
1608     blen = (size >= 0 ? size : blenmax);
1609     if (blen) {
1610         int nb;
1611         b = xmalloc(blen+1);
1612         b[0] = '\0';
1613         nb = Fread(b, sizeof(*b), blen, fd);
1614         if (Ferror(fd) || (size > 0 && nb != blen)) {
1615             rc = 1;
1616             goto exit;
1617         }
1618         if (blen == blenmax && nb < blen) {
1619             blen = nb;
1620             b = xrealloc(b, blen+1);
1621         }
1622         b[blen] = '\0';
1623     }
1624
1625 exit:
1626     if (fd) (void) Fclose(fd);
1627         
1628     if (rc) {
1629         if (b) free(b);
1630         b = NULL;
1631         blen = 0;
1632     }
1633
1634     if (bp) *bp = b;
1635     else if (b) free(b);
1636
1637     if (blenp) *blenp = blen;
1638
1639     return rc;
1640 }
1641
1642 void fdInitDigest(FD_t fd, int hashalgo, rpmDigestFlags flags)
1643 {
1644     if (fd->digests == NULL) {
1645         fd->digests = rpmDigestBundleNew();
1646     }
1647     fdstat_enter(fd, FDSTAT_DIGEST);
1648     rpmDigestBundleAdd(fd->digests, hashalgo, flags);
1649     fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
1650 }
1651
1652 static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen)
1653 {
1654     if (fd && fd->digests) {
1655         fdstat_enter(fd, FDSTAT_DIGEST);
1656         rpmDigestBundleUpdate(fd->digests, buf, buflen);
1657         fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) buflen);
1658     }
1659 }
1660
1661 void fdFiniDigest(FD_t fd, int hashalgo,
1662                 void ** datap, size_t * lenp, int asAscii)
1663 {
1664     if (fd && fd->digests) {
1665         fdstat_enter(fd, FDSTAT_DIGEST);
1666         rpmDigestBundleFinal(fd->digests, hashalgo, datap, lenp, asAscii);
1667         fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0);
1668     }
1669 }
1670
1671