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