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