c405fa5371bfab1d92fef0a147ad212067142c49
[platform/upstream/rpm.git] / lib / rpmdb.c
1 /** \ingroup rpmdb dbi
2  * \file lib/rpmdb.c
3  */
4
5 #include "system.h"
6
7 #define _USE_COPY_LOAD  /* XXX don't use DB_DBT_MALLOC (yet) */
8
9 #include <sys/file.h>
10
11 #ifndef DYING   /* XXX already in "system.h" */
12 #include <fnmatch.h>
13 #endif
14
15 #include <regex.h>
16
17 #include <rpm/rpmtypes.h>
18 #include <rpm/rpmurl.h>
19 #include <rpm/rpmpgp.h>
20 #include <rpm/rpmpgp.h>
21 #include <rpm/rpmmacro.h>
22 #include <rpm/rpmsq.h>
23 #include <rpm/rpmstring.h>
24 #include <rpm/rpmfileutil.h>
25 #include <rpm/rpmds.h>                  /* XXX isInstallPreReq macro only */
26 #include <rpm/rpmlog.h>
27 #include <rpm/rpmdb.h>
28 #include <rpm/argv.h>
29
30 #include "lib/rpmdb_internal.h"
31 #include "lib/fprint.h"
32 #include "lib/header_internal.h"        /* XXX for headerSetInstance() */
33 #include "debug.h"
34
35 int _rpmdb_debug = 0;
36
37 static int _rebuildinprogress = 0;
38 static int _db_filter_dups = 0;
39
40 #define _DBI_FLAGS      0
41 #define _DBI_PERMS      0644
42 #define _DBI_MAJOR      -1
43
44 struct dbiTags_s {
45     rpmTag * tags;
46     rpmTag max;
47     int nlink;
48 };
49
50 /* XXX should dbitags be per-db instead? */
51 static struct dbiTags_s dbiTags = { NULL, 0, 0 };
52
53 /* Bit mask macros. */
54 typedef unsigned int __pbm_bits;
55 #define __PBM_NBITS             (8 * sizeof (__pbm_bits))
56 #define __PBM_IX(d)             ((d) / __PBM_NBITS)
57 #define __PBM_MASK(d)           ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
58 typedef struct {
59     __pbm_bits bits[1];
60 } pbm_set;
61 #define __PBM_BITS(set) ((set)->bits)
62
63 #define PBM_FREE(s)     _free(s);
64 #define PBM_SET(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
65 #define PBM_CLR(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
66 #define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
67
68 #define PBM_ALLOC(d)    xcalloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
69
70 /**
71  * Reallocate a bit map.
72  * @retval sp           address of bit map pointer
73  * @retval odp          no. of bits in map
74  * @param nd            desired no. of bits
75  */
76 static inline pbm_set * PBM_REALLOC(pbm_set ** sp, int * odp, int nd)
77 {
78     int i, nb;
79
80     if (nd > (*odp)) {
81         nd *= 2;
82         nb = __PBM_IX(nd) + 1;
83         *sp = xrealloc(*sp, nb * sizeof(__pbm_bits));
84         for (i = __PBM_IX(*odp) + 1; i < nb; i++)
85             __PBM_BITS(*sp)[i] = 0;
86         *odp = nd;
87     }
88     return *sp;
89 }
90
91 /**
92  * Return dbi index used for rpm tag.
93  * @param rpmtag        rpm header tag
94  * @return              dbi index, -1 on error
95  */
96 static int dbiTagToDbix(rpmTag rpmtag)
97 {
98     int dbix;
99
100     if (dbiTags.tags != NULL)
101     for (dbix = 0; dbix < dbiTags.max; dbix++) {
102         if (rpmtag == dbiTags.tags[dbix])
103             return dbix;
104     }
105     return -1;
106 }
107
108 /**
109  * Initialize database (index, tag) tuple from configuration.
110  */
111 static void dbiTagsInit(void)
112 {
113     static const char * const _dbiTagStr_default =
114         "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:ObsoleteName:Triggername:Dirnames:Requireversion:Provideversion:Installtid:Sigmd5:Sha1header:Filedigests:Depends:Pubkeys";
115     char * dbiTagStr = NULL;
116     char * o, * oe;
117     rpmTag rpmtag;
118
119     dbiTags.nlink++;
120     if (dbiTags.tags != NULL && dbiTags.max > 0) {
121         return;
122     }
123
124     dbiTagStr = rpmExpand("%{?_dbi_tags}", NULL);
125     if (!(dbiTagStr && *dbiTagStr)) {
126         dbiTagStr = _free(dbiTagStr);
127         dbiTagStr = xstrdup(_dbiTagStr_default);
128     }
129
130     /* Discard previous values. */
131     dbiTags.tags = _free(dbiTags.tags);
132     dbiTags.max = 0;
133
134     /* Always allocate package index */
135     dbiTags.tags = xcalloc(1, sizeof(*dbiTags.tags));
136     dbiTags.tags[dbiTags.max++] = RPMDBI_PACKAGES;
137
138     for (o = dbiTagStr; o && *o; o = oe) {
139         while (*o && risspace(*o))
140             o++;
141         if (*o == '\0')
142             break;
143         for (oe = o; oe && *oe; oe++) {
144             if (risspace(*oe))
145                 break;
146             if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
147                 break;
148         }
149         if (oe && *oe)
150             *oe++ = '\0';
151         rpmtag = rpmTagGetValue(o);
152         if (rpmtag == RPMTAG_NOT_FOUND) {
153             rpmlog(RPMLOG_WARNING,
154                 _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
155             continue;
156         }
157         if (dbiTagToDbix(rpmtag) >= 0)
158             continue;
159
160         dbiTags.tags = xrealloc(dbiTags.tags, (dbiTags.max + 1) * sizeof(*dbiTags.tags)); /* XXX memory leak */
161         dbiTags.tags[dbiTags.max++] = rpmtag;
162     }
163
164     dbiTagStr = _free(dbiTagStr);
165 }
166
167 static void dbiTagsFree(void)
168 {
169     if (--dbiTags.nlink > 0) {
170         return;
171     }
172     dbiTags.tags = _free(dbiTags.tags);
173     dbiTags.max = 0;
174 }
175
176 #define DB1vec          NULL
177 #define DB2vec          NULL
178
179 #ifdef HAVE_DB_H
180 extern struct _dbiVec db3vec;
181 #define DB3vec          &db3vec
182 #else
183 #define DB3vec          NULL
184 #endif
185
186 #ifdef HAVE_SQLITE3_H
187 extern struct _dbiVec sqlitevec;
188 #define SQLITEvec       &sqlitevec
189 #else
190 #define SQLITEvec       NULL
191 #endif
192
193 static struct _dbiVec * const mydbvecs[] = {
194     DB1vec, DB1vec, DB2vec, DB3vec, SQLITEvec, NULL
195 };
196
197 dbiIndex dbiOpen(rpmdb db, rpmTag rpmtag, unsigned int flags)
198 {
199     int dbix;
200     dbiIndex dbi = NULL;
201     int _dbapi, _dbapi_rebuild, _dbapi_wanted;
202     int rc = 0;
203
204     if (db == NULL)
205         return NULL;
206
207     dbix = dbiTagToDbix(rpmtag);
208     if (dbix < 0 || dbix >= dbiTags.max)
209         return NULL;
210
211     /* Is this index already open ? */
212     /* FIX: db->_dbi may be NULL */
213     if ((dbi = db->_dbi[dbix]) != NULL)
214         return dbi;
215
216     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
217     if (_dbapi_rebuild < 1 || _dbapi_rebuild > 4)
218         _dbapi_rebuild = 4;
219 /*    _dbapi_wanted = (_rebuildinprogress ? -1 : db->db_api); */
220     _dbapi_wanted = (_rebuildinprogress ? _dbapi_rebuild : db->db_api);
221
222     switch (_dbapi_wanted) {
223     default:
224         _dbapi = _dbapi_wanted;
225         if (_dbapi < 0 || _dbapi >= 5 || mydbvecs[_dbapi] == NULL) {
226             rpmlog(RPMLOG_ERR, _("dbiOpen: dbapi %d not available\n"), _dbapi);
227             return NULL;
228         }
229         errno = 0;
230         dbi = NULL;
231         rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
232         if (rc) {
233             static int _printed[32];
234             if (!_printed[dbix & 0x1f]++)
235                 rpmlog(RPMLOG_ERR,
236                         _("cannot open %s index using db%d - %s (%d)\n"),
237                         rpmTagGetName(rpmtag), _dbapi,
238                         (rc > 0 ? strerror(rc) : ""), rc);
239             _dbapi = -1;
240         }
241         break;
242     case -1:
243         _dbapi = 5;
244         while (_dbapi-- > 1) {
245             if (mydbvecs[_dbapi] == NULL)
246                 continue;
247             errno = 0;
248             dbi = NULL;
249             rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
250             if (rc == 0 && dbi)
251                 break;
252         }
253         if (_dbapi <= 0) {
254             static int _printed[32];
255             if (!_printed[dbix & 0x1f]++)
256                 rpmlog(RPMLOG_ERR, _("cannot open %s index\n"),
257                         rpmTagGetName(rpmtag));
258             rc = 1;
259             goto exit;
260         }
261         if (db->db_api == -1 && _dbapi > 0)
262             db->db_api = _dbapi;
263         break;
264     }
265
266 /* We don't ever _REQUIRE_ conversion... */
267 #define SQLITE_HACK
268 #ifdef  SQLITE_HACK_XXX
269     /* Require conversion. */
270     if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
271         rc = (_rebuildinprogress ? 0 : 1);
272         goto exit;
273     }
274
275     /* Suggest possible configuration */
276     if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
277         rc = 1;
278         goto exit;
279     }
280
281     /* Suggest possible configuration */
282     if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
283         rc = (_rebuildinprogress ? 0 : 1);
284         goto exit;
285     }
286 #endif
287
288 exit:
289     if (dbi != NULL && rc == 0) {
290         db->_dbi[dbix] = dbi;
291         if (rpmtag == RPMDBI_PACKAGES && db->db_bits == NULL) {
292             db->db_nbits = 1024;
293             if (!dbiStat(dbi, DB_FAST_STAT)) {
294                 DB_HASH_STAT * hash = (DB_HASH_STAT *)dbi->dbi_stats;
295                 if (hash)
296                     db->db_nbits += hash->hash_nkeys;
297             }
298             db->db_bits = PBM_ALLOC(db->db_nbits);
299         }
300     }
301 #ifdef HAVE_DB_H
302       else
303         dbi = db3Free(dbi);
304 #endif
305
306 /* FIX: db->_dbi may be NULL */
307     return dbi;
308 }
309
310 /* Retrieve (key,data) pair from index database. */
311 int dbiGet(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
312                 unsigned int flags)
313 {
314     int rc;
315     assert((flags == DB_NEXT) || (key->data != NULL && key->size > 0));
316     (void) rpmswEnter(&dbi->dbi_rpmdb->db_getops, 0);
317     rc = (dbi->dbi_vec->cget) (dbi, dbcursor, key, data, flags);
318     (void) rpmswExit(&dbi->dbi_rpmdb->db_getops, data->size);
319     return rc;
320 }
321
322 /* Store (key,data) pair in index database. */
323 int dbiPut(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
324                 unsigned int flags)
325 {
326     int rc;
327     assert(key->data != NULL && key->size > 0 && data->data != NULL && data->size > 0);
328     (void) rpmswEnter(&dbi->dbi_rpmdb->db_putops, (ssize_t) 0);
329     rc = (dbi->dbi_vec->cput) (dbi, dbcursor, key, data, flags);
330     (void) rpmswExit(&dbi->dbi_rpmdb->db_putops, (ssize_t) data->size);
331     return rc;
332 }
333
334 /**
335  * Create and initialize item for index database set.
336  * @param hdrNum        header instance in db
337  * @param tagNum        tag index in header
338  * @return              new item
339  */
340 static dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum)
341 {
342     dbiIndexItem rec = xcalloc(1, sizeof(*rec));
343     rec->hdrNum = hdrNum;
344     rec->tagNum = tagNum;
345     return rec;
346 }
347
348 union _dbswap {
349     unsigned int ui;
350     unsigned char uc[4];
351 };
352
353 #define _DBSWAP(_a) \
354 \
355   { unsigned char _b, *_c = (_a).uc; \
356     _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
357     _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
358 \
359   }
360
361 /* 
362  * Ensure sufficient memory for nrecs of new records in dbiIndexSet.
363  * Allocate in power of two sizes to avoid memory fragmentation, so
364  * realloc is not always needed.
365  */
366 static inline void dbiGrowSet(dbiIndexSet set, unsigned int nrecs)
367 {
368     size_t need = (set->count + nrecs) * sizeof(*(set->recs));
369     size_t alloced = set->alloced ? set->alloced : 1 << 4;
370
371     while (alloced < need)
372         alloced <<= 1;
373
374     if (alloced != set->alloced) {
375         set->recs = xrealloc(set->recs, alloced);
376         set->alloced = alloced;
377     }
378 }
379
380 /**
381  * Convert retrieved data to index set.
382  * @param dbi           index database handle
383  * @param data          retrieved data
384  * @retval setp         (malloc'ed) index set
385  * @return              0 on success
386  */
387 static int dbt2set(dbiIndex dbi, DBT * data, dbiIndexSet * setp)
388 {
389     int _dbbyteswapped = dbiByteSwapped(dbi);
390     const char * sdbir;
391     dbiIndexSet set;
392     unsigned int i;
393
394     if (dbi == NULL || data == NULL || setp == NULL)
395         return -1;
396
397     if ((sdbir = data->data) == NULL) {
398         *setp = NULL;
399         return 0;
400     }
401
402     set = xcalloc(1, sizeof(*set));
403     dbiGrowSet(set, data->size / dbi->dbi_jlen);
404     set->count = data->size / dbi->dbi_jlen;
405
406     switch (dbi->dbi_jlen) {
407     default:
408     case 2*sizeof(int32_t):
409         for (i = 0; i < set->count; i++) {
410             union _dbswap hdrNum, tagNum;
411
412             memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
413             sdbir += sizeof(hdrNum.ui);
414             memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
415             sdbir += sizeof(tagNum.ui);
416             if (_dbbyteswapped) {
417                 _DBSWAP(hdrNum);
418                 _DBSWAP(tagNum);
419             }
420             set->recs[i].hdrNum = hdrNum.ui;
421             set->recs[i].tagNum = tagNum.ui;
422         }
423         break;
424     case 1*sizeof(int32_t):
425         for (i = 0; i < set->count; i++) {
426             union _dbswap hdrNum;
427
428             memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
429             sdbir += sizeof(hdrNum.ui);
430             if (_dbbyteswapped) {
431                 _DBSWAP(hdrNum);
432             }
433             set->recs[i].hdrNum = hdrNum.ui;
434             set->recs[i].tagNum = 0;
435         }
436         break;
437     }
438     *setp = set;
439     return 0;
440 }
441
442 /**
443  * Convert index set to database representation.
444  * @param dbi           index database handle
445  * @param data          retrieved data
446  * @param set           index set
447  * @return              0 on success
448  */
449 static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set)
450 {
451     int _dbbyteswapped = dbiByteSwapped(dbi);
452     char * tdbir;
453     unsigned int i;
454
455     if (dbi == NULL || data == NULL || set == NULL)
456         return -1;
457
458     data->size = set->count * (dbi->dbi_jlen);
459     if (data->size == 0) {
460         data->data = NULL;
461         return 0;
462     }
463     tdbir = data->data = xmalloc(data->size);
464
465     switch (dbi->dbi_jlen) {
466     default:
467     case 2*sizeof(int32_t):
468         for (i = 0; i < set->count; i++) {
469             union _dbswap hdrNum, tagNum;
470
471             memset(&hdrNum, 0, sizeof(hdrNum));
472             memset(&tagNum, 0, sizeof(tagNum));
473             hdrNum.ui = set->recs[i].hdrNum;
474             tagNum.ui = set->recs[i].tagNum;
475             if (_dbbyteswapped) {
476                 _DBSWAP(hdrNum);
477                 _DBSWAP(tagNum);
478             }
479             memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
480             tdbir += sizeof(hdrNum.ui);
481             memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
482             tdbir += sizeof(tagNum.ui);
483         }
484         break;
485     case 1*sizeof(int32_t):
486         for (i = 0; i < set->count; i++) {
487             union _dbswap hdrNum;
488
489             memset(&hdrNum, 0, sizeof(hdrNum));
490             hdrNum.ui = set->recs[i].hdrNum;
491             if (_dbbyteswapped) {
492                 _DBSWAP(hdrNum);
493             }
494             memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
495             tdbir += sizeof(hdrNum.ui);
496         }
497         break;
498     }
499
500     return 0;
501 }
502
503 /* XXX assumes hdrNum is first int in dbiIndexItem */
504 static int hdrNumCmp(const void * one, const void * two)
505 {
506     const unsigned int * a = one, * b = two;
507     return (*a - *b);
508 }
509
510 /**
511  * Append element(s) to set of index database items.
512  * @param set           set of index database items
513  * @param recs          array of items to append to set
514  * @param nrecs         number of items
515  * @param recsize       size of an array item
516  * @param sortset       should resulting set be sorted?
517  * @return              0 success, 1 failure (bad args)
518  */
519 static int dbiAppendSet(dbiIndexSet set, const void * recs,
520         int nrecs, size_t recsize, int sortset)
521 {
522     const char * rptr = recs;
523     size_t rlen = (recsize < sizeof(*(set->recs)))
524                 ? recsize : sizeof(*(set->recs));
525
526     if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0)
527         return 1;
528
529     dbiGrowSet(set, nrecs);
530     memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
531
532     while (nrecs-- > 0) {
533         memcpy(set->recs + set->count, rptr, rlen);
534         rptr += recsize;
535         set->count++;
536     }
537
538     if (sortset && set->count > 1)
539         qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
540
541     return 0;
542 }
543
544 /**
545  * Remove element(s) from set of index database items.
546  * @param set           set of index database items
547  * @param recs          array of items to remove from set
548  * @param nrecs         number of items
549  * @param recsize       size of an array item
550  * @param sorted        array is already sorted?
551  * @return              0 success, 1 failure (no items found)
552  */
553 static int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
554                 size_t recsize, int sorted)
555 {
556     unsigned int from;
557     unsigned int to = 0;
558     unsigned int num = set->count;
559     unsigned int numCopied = 0;
560
561     assert(set->count > 0);
562     if (nrecs > 1 && !sorted)
563         qsort(recs, nrecs, recsize, hdrNumCmp);
564
565     for (from = 0; from < num; from++) {
566         if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
567             set->count--;
568             continue;
569         }
570         if (from != to)
571             set->recs[to] = set->recs[from]; /* structure assignment */
572         to++;
573         numCopied++;
574     }
575     return (numCopied == num);
576 }
577
578 /* XXX transaction.c */
579 unsigned int dbiIndexSetCount(dbiIndexSet set) {
580     return set->count;
581 }
582
583 /* XXX transaction.c */
584 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
585     return set->recs[recno].hdrNum;
586 }
587
588 /* XXX transaction.c */
589 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
590     return set->recs[recno].tagNum;
591 }
592
593 /* XXX transaction.c */
594 dbiIndexSet dbiFreeIndexSet(dbiIndexSet set) {
595     if (set) {
596         set->recs = _free(set->recs);
597         set = _free(set);
598     }
599     return set;
600 }
601
602 typedef struct miRE_s {
603     rpmTag              tag;            /*!< header tag */
604     rpmMireMode         mode;           /*!< pattern match mode */
605     char *              pattern;        /*!< pattern string */
606     int                 notmatch;       /*!< like "grep -v" */
607     regex_t *           preg;           /*!< regex compiled pattern buffer */
608     int                 cflags;         /*!< regcomp(3) flags */
609     int                 eflags;         /*!< regexec(3) flags */
610     int                 fnflags;        /*!< fnmatch(3) flags */
611 } * miRE;
612
613 struct rpmdbMatchIterator_s {
614     rpmdbMatchIterator  mi_next;
615     void *              mi_keyp;
616     size_t              mi_keylen;
617     rpmdb               mi_db;
618     rpmTag              mi_rpmtag;
619     dbiIndexSet         mi_set;
620     DBC *               mi_dbc;
621     DBT                 mi_key;
622     DBT                 mi_data;
623     int                 mi_setx;
624     Header              mi_h;
625     int                 mi_sorted;
626     int                 mi_cflags;
627     int                 mi_modified;
628     unsigned int        mi_prevoffset;  /* header instance (native endian) */
629     unsigned int        mi_offset;      /* header instance (native endian) */
630     unsigned int        mi_filenum;     /* tag element (native endian) */
631     int                 mi_nre;
632     miRE                mi_re;
633     rpmts               mi_ts;
634     rpmRC (*mi_hdrchk) (rpmts ts, const void * uh, size_t uc, char ** msg);
635
636 };
637
638 static rpmdb rpmdbRock;
639
640 static rpmdbMatchIterator rpmmiRock;
641
642 int rpmdbCheckTerminate(int terminate)
643 {
644     sigset_t newMask, oldMask;
645     static int terminating = 0;
646
647     if (terminating) return 0;
648
649     (void) sigfillset(&newMask);                /* block all signals */
650     (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
651
652     if (rpmsqIsCaught(SIGINT) > 0
653      || rpmsqIsCaught(SIGQUIT) > 0
654      || rpmsqIsCaught(SIGHUP) > 0
655      || rpmsqIsCaught(SIGTERM) > 0
656      || rpmsqIsCaught(SIGPIPE) > 0
657      || terminate)
658         terminating = 1;
659
660     if (terminating) {
661         rpmdb db;
662         rpmdbMatchIterator mi;
663
664         while ((mi = rpmmiRock) != NULL) {
665             rpmmiRock = mi->mi_next;
666             mi->mi_next = NULL;
667             mi = rpmdbFreeIterator(mi);
668         }
669
670         while ((db = rpmdbRock) != NULL) {
671             rpmdbRock = db->db_next;
672             db->db_next = NULL;
673             (void) rpmdbClose(db);
674         }
675     }
676     sigprocmask(SIG_SETMASK, &oldMask, NULL);
677     return terminating;
678 }
679
680 int rpmdbCheckSignals(void)
681 {
682     if (rpmdbCheckTerminate(0)) {
683         rpmlog(RPMLOG_DEBUG, "Exiting on signal...\n");
684         exit(EXIT_FAILURE);
685     }
686     return 0;
687 }
688
689 /**
690  * Block all signals, returning previous signal mask.
691  */
692 static int blockSignals(sigset_t * oldMask)
693 {
694     sigset_t newMask;
695
696     (void) sigfillset(&newMask);                /* block all signals */
697     (void) sigprocmask(SIG_BLOCK, &newMask, oldMask);
698     (void) sigdelset(&newMask, SIGINT);
699     (void) sigdelset(&newMask, SIGQUIT);
700     (void) sigdelset(&newMask, SIGHUP);
701     (void) sigdelset(&newMask, SIGTERM);
702     (void) sigdelset(&newMask, SIGPIPE);
703     return sigprocmask(SIG_BLOCK, &newMask, NULL);
704 }
705
706 /**
707  * Restore signal mask.
708  */
709 static int unblockSignals(sigset_t * oldMask)
710 {
711     (void) rpmdbCheckSignals();
712     return sigprocmask(SIG_SETMASK, oldMask, NULL);
713 }
714
715 #define _DB_ROOT        "/"
716 #define _DB_HOME        "%{_dbpath}"
717 #define _DB_FLAGS       0
718 #define _DB_MODE        0
719 #define _DB_PERMS       0644
720
721 #define _DB_MAJOR       -1
722 #define _DB_ERRPFX      "rpmdb"
723
724 static struct rpmdb_s const dbTemplate = {
725     _DB_ROOT,   _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
726     _DB_MAJOR,  _DB_ERRPFX
727 };
728
729 static int isTemporaryDB(rpmTag rpmtag) 
730 {
731     int rc = 0;
732     switch (rpmtag) {
733     case RPMDBI_AVAILABLE:
734     case RPMDBI_ADDED:
735     case RPMDBI_REMOVED:
736     case RPMDBI_DEPENDS:
737         rc = 1;
738         break;
739     default:
740         break;
741     }
742     return rc;
743 }
744
745 rpmop rpmdbOp(rpmdb rpmdb, rpmdbOpX opx)
746 {
747     rpmop op = NULL;
748     switch (opx) {
749     case RPMDB_OP_DBGET:
750         op = &rpmdb->db_getops;
751         break;
752     case RPMDB_OP_DBPUT:
753         op = &rpmdb->db_putops;
754         break;
755     case RPMDB_OP_DBDEL:
756         op = &rpmdb->db_delops;
757         break;
758     default:
759         break;
760     }
761     return op;
762 }
763
764 int rpmdbSetChrootDone(rpmdb db, int chrootDone)
765 {
766     int ochrootDone = 0;
767     if (db != NULL) {
768         ochrootDone = db->db_chrootDone;
769         db->db_chrootDone = chrootDone;
770     }
771     return ochrootDone;
772 }
773
774 int rpmdbOpenAll(rpmdb db)
775 {
776     int dbix;
777     int rc = 0;
778
779     if (db == NULL) return -2;
780
781     if (dbiTags.tags != NULL)
782     for (dbix = 0; dbix < dbiTags.max; dbix++) {
783         if (db->_dbi[dbix] != NULL)
784             continue;
785         /* Filter out temporary databases */
786         if (isTemporaryDB(dbiTags.tags[dbix])) 
787             continue;
788         (void) dbiOpen(db, dbiTags.tags[dbix], db->db_flags);
789     }
790     return rc;
791 }
792
793 int rpmdbCloseDBI(rpmdb db, rpmTag rpmtag)
794 {
795     int dbix;
796     int rc = 0;
797
798     if (db == NULL || db->_dbi == NULL || dbiTags.tags == NULL)
799         return 0;
800
801     for (dbix = 0; dbix < dbiTags.max; dbix++) {
802         if (dbiTags.tags[dbix] != rpmtag)
803             continue;
804         if (db->_dbi[dbix] != NULL) {
805             int xx;
806                         /* FIX: double indirection. */
807             xx = dbiClose(db->_dbi[dbix], 0);
808             if (xx && rc == 0) rc = xx;
809             db->_dbi[dbix] = NULL;
810         }
811         break;
812     }
813     return rc;
814 }
815
816 /* XXX query.c, rpminstall.c, verify.c */
817 int rpmdbClose(rpmdb db)
818 {
819     rpmdb * prev, next;
820     int dbix;
821     int rc = 0;
822
823     if (db == NULL)
824         goto exit;
825
826     (void) rpmdbUnlink(db, RPMDBG_M("rpmdbClose"));
827
828     if (db->nrefs > 0)
829         goto exit;
830
831     if (db->_dbi)
832     for (dbix = db->db_ndbi; --dbix >= 0; ) {
833         int xx;
834         if (db->_dbi[dbix] == NULL)
835             continue;
836         xx = dbiClose(db->_dbi[dbix], 0);
837         if (xx && rc == 0) rc = xx;
838         db->_dbi[dbix] = NULL;
839     }
840     db->db_errpfx = _free(db->db_errpfx);
841     db->db_root = _free(db->db_root);
842     db->db_home = _free(db->db_home);
843     db->db_bits = PBM_FREE(db->db_bits);
844     db->_dbi = _free(db->_dbi);
845
846     prev = &rpmdbRock;
847     while ((next = *prev) != NULL && next != db)
848         prev = &next->db_next;
849     if (next) {
850         *prev = next->db_next;
851         next->db_next = NULL;
852     }
853
854     db = _free(db);
855
856     dbiTagsFree();
857
858 exit:
859     (void) rpmsqEnable(-SIGHUP, NULL);
860     (void) rpmsqEnable(-SIGINT, NULL);
861     (void) rpmsqEnable(-SIGTERM,NULL);
862     (void) rpmsqEnable(-SIGQUIT,NULL);
863     (void) rpmsqEnable(-SIGPIPE,NULL);
864     return rc;
865 }
866
867 int rpmdbSync(rpmdb db)
868 {
869     int dbix;
870     int rc = 0;
871
872     if (db == NULL) return 0;
873     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
874         int xx;
875         if (db->_dbi[dbix] == NULL)
876             continue;
877         if (db->_dbi[dbix]->dbi_no_dbsync)
878             continue;
879         xx = dbiSync(db->_dbi[dbix], 0);
880         if (xx && rc == 0) rc = xx;
881     }
882     return rc;
883 }
884
885 /* FIX: dbTemplate structure assignment */
886 static
887 rpmdb newRpmdb(const char * root,
888                 const char * home,
889                 int mode, int perms, int flags)
890 {
891     rpmdb db = xcalloc(sizeof(*db), 1);
892     const char * epfx = _DB_ERRPFX;
893     static int _initialized = 0;
894
895     if (!_initialized) {
896         _db_filter_dups = rpmExpandNumeric("%{_filterdbdups}");
897         _initialized = 1;
898     }
899
900     *db = dbTemplate;   /* structure assignment */
901
902     db->_dbi = NULL;
903
904     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
905
906     if (mode >= 0)      db->db_mode = mode;
907     if (perms >= 0)     db->db_perms = perms;
908     if (flags >= 0)     db->db_flags = flags;
909
910     if (root && *root) {
911         db->db_root = rpmGetPath(root, NULL);
912     } else
913         db->db_root = rpmGetPath(_DB_ROOT, NULL);
914
915     db->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL);
916     if (!(db->db_home && db->db_home[0] != '%')) {
917         rpmlog(RPMLOG_ERR, _("no dbpath has been set\n"));
918         db->db_root = _free(db->db_root);
919         db->db_home = _free(db->db_home);
920         db = _free(db);
921         return NULL;
922     }
923     db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
924     db->db_remove_env = 0;
925     db->db_filter_dups = _db_filter_dups;
926     db->db_ndbi = dbiTags.max;
927     db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
928     db->nrefs = 0;
929     return rpmdbLink(db, RPMDBG_M("rpmdbCreate"));
930 }
931
932 static int openDatabase(const char * prefix,
933                 const char * dbpath,
934                 int _dbapi, rpmdb *dbp,
935                 int mode, int perms, int flags)
936 {
937     rpmdb db;
938     int rc, xx;
939     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
940     int minimal = flags & RPMDB_FLAG_MINIMAL;
941
942     dbiTagsInit();
943
944     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
945     if (_dbapi < -1 || _dbapi > 4)
946         _dbapi = -1;
947     if (_dbapi == 0)
948         _dbapi = 1;
949
950     if (dbp)
951         *dbp = NULL;
952     if ((mode & O_ACCMODE) == O_WRONLY) 
953         return 1;
954
955     db = newRpmdb(prefix, dbpath, mode, perms, flags);
956     if (db == NULL)
957         return 1;
958
959     (void) rpmsqEnable(SIGHUP,  NULL);
960     (void) rpmsqEnable(SIGINT,  NULL);
961     (void) rpmsqEnable(SIGTERM,NULL);
962     (void) rpmsqEnable(SIGQUIT,NULL);
963     (void) rpmsqEnable(SIGPIPE,NULL);
964
965     db->db_api = _dbapi;
966
967     {   int dbix;
968
969         rc = 0;
970         if (dbiTags.tags != NULL)
971         for (dbix = 0; rc == 0 && dbix < dbiTags.max; dbix++) {
972             dbiIndex dbi;
973             rpmTag rpmtag;
974
975             /* Filter out temporary databases */
976             if (isTemporaryDB((rpmtag = dbiTags.tags[dbix])))
977                 continue;
978
979             dbi = dbiOpen(db, rpmtag, 0);
980             if (dbi == NULL) {
981                 rc = -2;
982                 break;
983             }
984
985             switch (rpmtag) {
986             case RPMDBI_PACKAGES:
987                 if (dbi == NULL) rc |= 1;
988 #if 0
989                 /* XXX open only Packages, indices created on the fly. */
990                 if (db->db_api == 3)
991 #endif
992                     goto exit;
993                 break;
994             case RPMTAG_NAME:
995                 if (dbi == NULL) rc |= 1;
996                 if (minimal)
997                     goto exit;
998                 break;
999             default:
1000                 break;
1001             }
1002         }
1003     }
1004
1005 exit:
1006     if (rc || justCheck || dbp == NULL)
1007         xx = rpmdbClose(db);
1008     else {
1009         db->db_next = rpmdbRock;
1010         rpmdbRock = db;
1011         *dbp = db;
1012     }
1013
1014     return rc;
1015 }
1016
1017 rpmdb rpmdbUnlink(rpmdb db, const char * msg)
1018 {
1019 if (_rpmdb_debug)
1020 fprintf(stderr, "--> db %p -- %d %s\n", db, db->nrefs, msg);
1021     db->nrefs--;
1022     return NULL;
1023 }
1024
1025 rpmdb rpmdbLink(rpmdb db, const char * msg)
1026 {
1027     db->nrefs++;
1028 if (_rpmdb_debug)
1029 fprintf(stderr, "--> db %p ++ %d %s\n", db, db->nrefs, msg);
1030     return db;
1031 }
1032
1033 /* XXX python/rpmmodule.c */
1034 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
1035 {
1036     int _dbapi = rpmExpandNumeric("%{_dbapi}");
1037     return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
1038 }
1039
1040 int rpmdbInit (const char * prefix, int perms)
1041 {
1042     rpmdb db = NULL;
1043     int _dbapi = rpmExpandNumeric("%{_dbapi}");
1044     int rc;
1045
1046     rc = openDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR),
1047                 perms, RPMDB_FLAG_JUSTCHECK);
1048     if (db != NULL) {
1049         int xx;
1050         xx = rpmdbOpenAll(db);
1051         if (xx && rc == 0) rc = xx;
1052         xx = rpmdbClose(db);
1053         if (xx && rc == 0) rc = xx;
1054         db = NULL;
1055     }
1056     return rc;
1057 }
1058
1059 int rpmdbVerify(const char * prefix)
1060 {
1061     rpmdb db = NULL;
1062     int _dbapi = rpmExpandNumeric("%{_dbapi}");
1063     int rc = 0;
1064
1065     rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0);
1066
1067     if (db != NULL) {
1068         int dbix;
1069         int xx;
1070         rc = rpmdbOpenAll(db);
1071
1072         for (dbix = db->db_ndbi; --dbix >= 0; ) {
1073             if (db->_dbi[dbix] == NULL)
1074                 continue;
1075                         /* FIX: double indirection. */
1076             xx = dbiVerify(db->_dbi[dbix], 0);
1077             if (xx && rc == 0) rc = xx;
1078             db->_dbi[dbix] = NULL;
1079         }
1080
1081         /* FIX: db->_dbi[] may be NULL. */
1082         xx = rpmdbClose(db);
1083         if (xx && rc == 0) rc = xx;
1084         db = NULL;
1085     }
1086     return rc;
1087 }
1088
1089 /**
1090  * Find file matches in database.
1091  * @param db            rpm database
1092  * @param filespec
1093  * @param key
1094  * @param data
1095  * @param matches
1096  * @return              0 on success, 1 on not found, -2 on error
1097  */
1098 static int rpmdbFindByFile(rpmdb db, const char * filespec,
1099                 DBT * key, DBT * data, dbiIndexSet * matches)
1100 {
1101     char * dirName = NULL;
1102     const char * baseName;
1103     fingerPrintCache fpc = NULL;
1104     fingerPrint fp1;
1105     dbiIndex dbi = NULL;
1106     DBC * dbcursor;
1107     dbiIndexSet allMatches = NULL;
1108     dbiIndexItem rec = NULL;
1109     unsigned int i;
1110     int rc = -2; /* assume error */
1111     int xx;
1112
1113     *matches = NULL;
1114     if (filespec == NULL) return rc; /* nothing alloced yet */
1115
1116     if ((baseName = strrchr(filespec, '/')) != NULL) {
1117         size_t len = baseName - filespec + 1;
1118         dirName = strncpy(xmalloc(len + 1), filespec, len);
1119         dirName[len] = '\0';
1120         baseName++;
1121     } else {
1122         dirName = xstrdup("");
1123         baseName = filespec;
1124     }
1125     if (baseName == NULL)
1126         goto exit;
1127
1128     dbi = dbiOpen(db, RPMTAG_BASENAMES, 0);
1129     if (dbi != NULL) {
1130         dbcursor = NULL;
1131         xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1132
1133         key->data = (void *) baseName;
1134         key->size = strlen(baseName);
1135         if (key->size == 0) 
1136             key->size++;        /* XXX "/" fixup. */
1137
1138         rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1139         if (rc > 0) {
1140             rpmlog(RPMLOG_ERR,
1141                 _("error(%d) getting \"%s\" records from %s index\n"),
1142                 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
1143         }
1144
1145         if (rc == 0)
1146             (void) dbt2set(dbi, data, &allMatches);
1147
1148         xx = dbiCclose(dbi, dbcursor, 0);
1149         dbcursor = NULL;
1150     } else
1151         rc = -2;
1152
1153     if (rc || allMatches == NULL) goto exit;
1154
1155     *matches = xcalloc(1, sizeof(**matches));
1156     rec = dbiIndexNewItem(0, 0);
1157     fpc = fpCacheCreate(allMatches->count);
1158     fp1 = fpLookup(fpc, dirName, baseName, 1);
1159
1160     i = 0;
1161     while (i < allMatches->count) {
1162         struct rpmtd_s bn, dn, di;
1163         const char ** baseNames, ** dirNames;
1164         uint32_t * dirIndexes;
1165         unsigned int offset = dbiIndexRecordOffset(allMatches, i);
1166         unsigned int prevoff;
1167         Header h;
1168
1169         {   rpmdbMatchIterator mi;
1170             mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset));
1171             h = rpmdbNextIterator(mi);
1172             if (h)
1173                 h = headerLink(h);
1174             mi = rpmdbFreeIterator(mi);
1175         }
1176
1177         if (h == NULL) {
1178             i++;
1179             continue;
1180         }
1181
1182         headerGet(h, RPMTAG_BASENAMES, &bn, HEADERGET_MINMEM);
1183         headerGet(h, RPMTAG_DIRNAMES, &dn, HEADERGET_MINMEM);
1184         headerGet(h, RPMTAG_DIRINDEXES, &di, HEADERGET_MINMEM);
1185         baseNames = bn.data;
1186         dirNames = dn.data;
1187         dirIndexes = di.data;
1188
1189         do {
1190             fingerPrint fp2;
1191             int num = dbiIndexRecordFileNumber(allMatches, i);
1192
1193             fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
1194             if (FP_EQUAL(fp1, fp2)) {
1195                 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
1196                 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
1197                 xx = dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
1198             }
1199
1200             prevoff = offset;
1201             i++;
1202             if (i < allMatches->count)
1203                 offset = dbiIndexRecordOffset(allMatches, i);
1204         } while (i < allMatches->count && offset == prevoff);
1205
1206         rpmtdFreeData(&bn);
1207         rpmtdFreeData(&dn);
1208         rpmtdFreeData(&di);
1209         h = headerFree(h);
1210     }
1211
1212     rec = _free(rec);
1213     fpCacheFree(fpc);
1214
1215     if ((*matches)->count == 0) {
1216         *matches = dbiFreeIndexSet(*matches);
1217         rc = 1;
1218     } else {
1219         rc = 0;
1220     }
1221
1222 exit:
1223     dbiFreeIndexSet(allMatches);
1224     free(dirName);
1225     return rc;
1226 }
1227
1228 /* XXX python/upgrade.c, install.c, uninstall.c */
1229 int rpmdbCountPackages(rpmdb db, const char * name)
1230 {
1231     DBC * dbcursor = NULL;
1232     DBT key; 
1233     DBT data; 
1234     dbiIndex dbi;
1235     int rc;
1236     int xx;
1237
1238     if (db == NULL)
1239         return 0;
1240
1241     memset(&key, 0, sizeof(key));
1242     memset(&data, 0, sizeof(data));
1243
1244     dbi = dbiOpen(db, RPMTAG_NAME, 0);
1245     if (dbi == NULL)
1246         return 0;
1247
1248     key.data = (void *) name;
1249     key.size = strlen(name);
1250
1251     xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1252     rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
1253 #ifndef SQLITE_HACK
1254     xx = dbiCclose(dbi, dbcursor, 0);
1255     dbcursor = NULL;
1256 #endif
1257
1258     if (rc == 0) {              /* success */
1259         dbiIndexSet matches;
1260         /* FIX: matches might be NULL */
1261         matches = NULL;
1262         (void) dbt2set(dbi, &data, &matches);
1263         if (matches) {
1264             rc = dbiIndexSetCount(matches);
1265             matches = dbiFreeIndexSet(matches);
1266         }
1267     } else
1268     if (rc == DB_NOTFOUND) {    /* not found */
1269         rc = 0;
1270     } else {                    /* error */
1271         rpmlog(RPMLOG_ERR,
1272                 _("error(%d) getting \"%s\" records from %s index\n"),
1273                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
1274         rc = -1;
1275     }
1276
1277 #ifdef  SQLITE_HACK
1278     xx = dbiCclose(dbi, dbcursor, 0);
1279     dbcursor = NULL;
1280 #endif
1281
1282     return rc;
1283 }
1284
1285 /**
1286  * Attempt partial matches on name[-version[-release]] strings.
1287  * @param dbi           index database handle (always RPMTAG_NAME)
1288  * @param dbcursor      index database cursor
1289  * @param key           search key/length/flags
1290  * @param data          search data/length/flags
1291  * @param name          package name
1292  * @param version       package version (can be a pattern)
1293  * @param release       package release (can be a pattern)
1294  * @retval matches      set of header instances that match
1295  * @return              RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1296  */
1297 static rpmRC dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
1298                 DBT * key, DBT * data,
1299                 const char * name,
1300                 const char * version,
1301                 const char * release,
1302                 dbiIndexSet * matches)
1303 {
1304     unsigned int gotMatches = 0;
1305     int rc;
1306     unsigned int i;
1307
1308     key->data = (void *) name;
1309     key->size = strlen(name);
1310
1311     rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1312
1313     if (rc == 0) {              /* success */
1314         (void) dbt2set(dbi, data, matches);
1315         if (version == NULL && release == NULL)
1316             return RPMRC_OK;
1317     } else
1318     if (rc == DB_NOTFOUND) {    /* not found */
1319         return RPMRC_NOTFOUND;
1320     } else {                    /* error */
1321         rpmlog(RPMLOG_ERR,
1322                 _("error(%d) getting \"%s\" records from %s index\n"),
1323                 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
1324         return RPMRC_FAIL;
1325     }
1326
1327     /* Make sure the version and release match. */
1328     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
1329         unsigned int recoff = dbiIndexRecordOffset(*matches, i);
1330         rpmdbMatchIterator mi;
1331         Header h;
1332
1333         if (recoff == 0)
1334             continue;
1335
1336         mi = rpmdbInitIterator(dbi->dbi_rpmdb,
1337                         RPMDBI_PACKAGES, &recoff, sizeof(recoff));
1338
1339         /* Set iterator selectors for version/release if available. */
1340         if (version &&
1341             rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version))
1342         {
1343             rc = RPMRC_FAIL;
1344             goto exit;
1345         }
1346         if (release &&
1347             rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release))
1348         {
1349             rc = RPMRC_FAIL;
1350             goto exit;
1351         }
1352
1353         h = rpmdbNextIterator(mi);
1354         if (h)
1355             (*matches)->recs[gotMatches++] = (*matches)->recs[i];
1356         else
1357             (*matches)->recs[i].hdrNum = 0;
1358         mi = rpmdbFreeIterator(mi);
1359     }
1360
1361     if (gotMatches) {
1362         (*matches)->count = gotMatches;
1363         rc = RPMRC_OK;
1364     } else
1365         rc = RPMRC_NOTFOUND;
1366
1367 exit:
1368 /* FIX: double indirection */
1369     if (rc && matches && *matches)
1370         *matches = dbiFreeIndexSet(*matches);
1371     return rc;
1372 }
1373
1374 /**
1375  * Lookup by name, name-version, and finally by name-version-release.
1376  * Both version and release can be patterns.
1377  * @todo Name must be an exact match, as name is a db key.
1378  * @param dbi           index database handle (always RPMTAG_NAME)
1379  * @param dbcursor      index database cursor
1380  * @param key           search key/length/flags
1381  * @param data          search data/length/flags
1382  * @param arg           name[-version[-release]] string
1383  * @retval matches      set of header instances that match
1384  * @return              RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1385  */
1386 static rpmRC dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
1387                 const char * arg, dbiIndexSet * matches)
1388 {
1389     const char * release;
1390     char * localarg;
1391     char * s;
1392     char c;
1393     int brackets;
1394     rpmRC rc;
1395  
1396     if (arg == NULL || strlen(arg) == 0) return RPMRC_NOTFOUND;
1397
1398     /* did they give us just a name? */
1399     rc = dbiFindMatches(dbi, dbcursor, key, data, arg, NULL, NULL, matches);
1400     if (rc != RPMRC_NOTFOUND) return rc;
1401
1402     /* FIX: double indirection */
1403     *matches = dbiFreeIndexSet(*matches);
1404
1405     /* maybe a name and a release */
1406     localarg = xmalloc(strlen(arg) + 1);
1407     s = stpcpy(localarg, arg);
1408
1409     c = '\0';
1410     brackets = 0;
1411     for (s -= 1; s > localarg; s--) {
1412         switch (*s) {
1413         case '[':
1414             brackets = 1;
1415             break;
1416         case ']':
1417             if (c != '[') brackets = 0;
1418             break;
1419         }
1420         c = *s;
1421         if (!brackets && *s == '-')
1422             break;
1423     }
1424
1425         /* FIX: *matches may be NULL. */
1426     if (s == localarg) {
1427         rc = RPMRC_NOTFOUND;
1428         goto exit;
1429     }
1430
1431     *s = '\0';
1432     rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, NULL, matches);
1433     if (rc != RPMRC_NOTFOUND) goto exit;
1434
1435     /* FIX: double indirection */
1436     *matches = dbiFreeIndexSet(*matches);
1437     
1438     /* how about name-version-release? */
1439
1440     release = s + 1;
1441
1442     c = '\0';
1443     brackets = 0;
1444     for (; s > localarg; s--) {
1445         switch (*s) {
1446         case '[':
1447             brackets = 1;
1448             break;
1449         case ']':
1450             if (c != '[') brackets = 0;
1451             break;
1452         }
1453         c = *s;
1454         if (!brackets && *s == '-')
1455             break;
1456     }
1457
1458     if (s == localarg) {
1459         rc = RPMRC_NOTFOUND;
1460         goto exit;
1461     }
1462
1463     *s = '\0';
1464         /* FIX: *matches may be NULL. */
1465     rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, release, matches);
1466 exit:
1467     free(localarg);
1468     return rc;
1469 }
1470
1471 /**
1472  * Rewrite a header into packages (if necessary) and free the header.
1473  *   Note: this is called from a markReplacedFiles iteration, and *must*
1474  *   preserve the "join key" (i.e. offset) for the header.
1475  * @param mi            database iterator
1476  * @param dbi           index database handle
1477  * @return              0 on success
1478  */
1479 static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi)
1480 {
1481     int rc = 0;
1482
1483     if (mi == NULL || mi->mi_h == NULL)
1484         return 0;
1485
1486     if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
1487         DBT * key = &mi->mi_key;
1488         DBT * data = &mi->mi_data;
1489         sigset_t signalMask;
1490         rpmRC rpmrc = RPMRC_NOTFOUND;
1491         int xx;
1492
1493         key->data = (void *) &mi->mi_prevoffset;
1494         key->size = sizeof(mi->mi_prevoffset);
1495         data->data = headerUnload(mi->mi_h);
1496         data->size = headerSizeof(mi->mi_h, HEADER_MAGIC_NO);
1497
1498         /* Check header digest/signature on blob export (if requested). */
1499         if (mi->mi_hdrchk && mi->mi_ts) {
1500             char * msg = NULL;
1501             int lvl;
1502
1503             rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, data->data, data->size, &msg);
1504             lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
1505             rpmlog(lvl, "%s h#%8u %s",
1506                 (rpmrc == RPMRC_FAIL ? _("miFreeHeader: skipping") : "write"),
1507                         mi->mi_prevoffset, (msg ? msg : "\n"));
1508             msg = _free(msg);
1509         }
1510
1511         if (data->data != NULL && rpmrc != RPMRC_FAIL) {
1512             (void) blockSignals(&signalMask);
1513             rc = dbiPut(dbi, mi->mi_dbc, key, data, DB_KEYLAST);
1514             if (rc) {
1515                 rpmlog(RPMLOG_ERR,
1516                         _("error(%d) storing record #%d into %s\n"),
1517                         rc, mi->mi_prevoffset, rpmTagGetName(dbi->dbi_rpmtag));
1518             }
1519             xx = dbiSync(dbi, 0);
1520             (void) unblockSignals(&signalMask);
1521         }
1522         data->data = _free(data->data);
1523         data->size = 0;
1524     }
1525
1526     mi->mi_h = headerFree(mi->mi_h);
1527
1528     return rc;
1529 }
1530
1531 rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
1532 {
1533     rpmdbMatchIterator * prev, next;
1534     dbiIndex dbi;
1535     int xx;
1536     int i;
1537
1538     if (mi == NULL)
1539         return NULL;
1540
1541     prev = &rpmmiRock;
1542     while ((next = *prev) != NULL && next != mi)
1543         prev = &next->mi_next;
1544     if (next) {
1545         *prev = next->mi_next;
1546         next->mi_next = NULL;
1547     }
1548
1549     dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
1550     if (dbi == NULL)    /* XXX can't happen */
1551         return NULL;
1552
1553     xx = miFreeHeader(mi, dbi);
1554
1555     if (mi->mi_dbc)
1556         xx = dbiCclose(dbi, mi->mi_dbc, 0);
1557     mi->mi_dbc = NULL;
1558
1559     if (mi->mi_re != NULL)
1560     for (i = 0; i < mi->mi_nre; i++) {
1561         miRE mire = mi->mi_re + i;
1562         mire->pattern = _free(mire->pattern);
1563         if (mire->preg != NULL) {
1564             regfree(mire->preg);
1565             mire->preg = _free(mire->preg);
1566         }
1567     }
1568     mi->mi_re = _free(mi->mi_re);
1569
1570     mi->mi_set = dbiFreeIndexSet(mi->mi_set);
1571     mi->mi_keyp = _free(mi->mi_keyp);
1572     mi->mi_db = rpmdbUnlink(mi->mi_db, RPMDBG_M("matchIterator"));
1573
1574     mi = _free(mi);
1575
1576     (void) rpmdbCheckSignals();
1577
1578     return mi;
1579 }
1580
1581 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
1582     return (mi ? mi->mi_offset : 0);
1583 }
1584
1585 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
1586     return (mi ? mi->mi_filenum : 0);
1587 }
1588
1589 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
1590     return (mi && mi->mi_set ?  mi->mi_set->count : 0);
1591 }
1592
1593 /**
1594  * Return pattern match.
1595  * @param mire          match iterator regex
1596  * @param val           value to match
1597  * @return              0 if pattern matches, >0 on nomatch, <0 on error
1598  */
1599 static int miregexec(miRE mire, const char * val)
1600 {
1601     int rc = 0;
1602
1603     switch (mire->mode) {
1604     case RPMMIRE_STRCMP:
1605         rc = (!rstreq(mire->pattern, val));
1606         break;
1607     case RPMMIRE_DEFAULT:
1608     case RPMMIRE_REGEX:
1609         rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
1610         if (rc && rc != REG_NOMATCH) {
1611             char msg[256];
1612             (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
1613             msg[sizeof(msg)-1] = '\0';
1614             rpmlog(RPMLOG_ERR, _("%s: regexec failed: %s\n"),
1615                         mire->pattern, msg);
1616             rc = -1;
1617         }
1618         break;
1619     case RPMMIRE_GLOB:
1620         rc = fnmatch(mire->pattern, val, mire->fnflags);
1621         if (rc && rc != FNM_NOMATCH)
1622             rc = -1;
1623         break;
1624     default:
1625         rc = -1;
1626         break;
1627     }
1628
1629     return rc;
1630 }
1631
1632 /**
1633  * Compare iterator selectors by rpm tag (qsort/bsearch).
1634  * @param a             1st iterator selector
1635  * @param b             2nd iterator selector
1636  * @return              result of comparison
1637  */
1638 static int mireCmp(const void * a, const void * b)
1639 {
1640     const miRE mireA = (const miRE) a;
1641     const miRE mireB = (const miRE) b;
1642     return (mireA->tag - mireB->tag);
1643 }
1644
1645 /**
1646  * Copy pattern, escaping for appropriate mode.
1647  * @param tag           rpm tag
1648  * @retval modep        type of pattern match
1649  * @param pattern       pattern to duplicate
1650  * @return              duplicated pattern
1651  */
1652 static char * mireDup(rpmTag tag, rpmMireMode *modep,
1653                         const char * pattern)
1654 {
1655     const char * s;
1656     char * pat;
1657     char * t;
1658     int brackets;
1659     size_t nb;
1660     int c;
1661
1662     switch (*modep) {
1663     default:
1664     case RPMMIRE_DEFAULT:
1665         if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
1666             *modep = RPMMIRE_GLOB;
1667             pat = xstrdup(pattern);
1668             break;
1669         }
1670
1671         nb = strlen(pattern) + sizeof("^$");
1672
1673         /* Find no. of bytes needed for pattern. */
1674         /* periods and plusses are escaped, splats become '.*' */
1675         c = '\0';
1676         brackets = 0;
1677         for (s = pattern; *s != '\0'; s++) {
1678             switch (*s) {
1679             case '.':
1680             case '+':
1681             case '*':
1682                 if (!brackets) nb++;
1683                 break;
1684             case '\\':
1685                 s++;
1686                 break;
1687             case '[':
1688                 brackets = 1;
1689                 break;
1690             case ']':
1691                 if (c != '[') brackets = 0;
1692                 break;
1693             }
1694             c = *s;
1695         }
1696
1697         pat = t = xmalloc(nb);
1698
1699         if (pattern[0] != '^') *t++ = '^';
1700
1701         /* Copy pattern, escaping periods, prefixing splats with period. */
1702         c = '\0';
1703         brackets = 0;
1704         for (s = pattern; *s != '\0'; s++, t++) {
1705             switch (*s) {
1706             case '.':
1707             case '+':
1708                 if (!brackets) *t++ = '\\';
1709                 break;
1710             case '*':
1711                 if (!brackets) *t++ = '.';
1712                 break;
1713             case '\\':
1714                 *t++ = *s++;
1715                 break;
1716             case '[':
1717                 brackets = 1;
1718                 break;
1719             case ']':
1720                 if (c != '[') brackets = 0;
1721                 break;
1722             }
1723             c = *t = *s;
1724         }
1725
1726         if (s > pattern && s[-1] != '$') *t++ = '$';
1727         *t = '\0';
1728         *modep = RPMMIRE_REGEX;
1729         break;
1730     case RPMMIRE_STRCMP:
1731     case RPMMIRE_REGEX:
1732     case RPMMIRE_GLOB:
1733         pat = xstrdup(pattern);
1734         break;
1735     }
1736
1737     return pat;
1738 }
1739
1740 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
1741                 rpmMireMode mode, const char * pattern)
1742 {
1743     static rpmMireMode defmode = (rpmMireMode)-1;
1744     miRE mire = NULL;
1745     char * allpat = NULL;
1746     int notmatch = 0;
1747     regex_t * preg = NULL;
1748     int cflags = 0;
1749     int eflags = 0;
1750     int fnflags = 0;
1751     int rc = 0;
1752
1753     if (defmode == (rpmMireMode)-1) {
1754         char *t = rpmExpand("%{?_query_selector_match}", NULL);
1755
1756         if (*t == '\0' || rstreq(t, "default"))
1757             defmode = RPMMIRE_DEFAULT;
1758         else if (rstreq(t, "strcmp"))
1759             defmode = RPMMIRE_STRCMP;
1760         else if (rstreq(t, "regex"))
1761             defmode = RPMMIRE_REGEX;
1762         else if (rstreq(t, "glob"))
1763             defmode = RPMMIRE_GLOB;
1764         else
1765             defmode = RPMMIRE_DEFAULT;
1766         t = _free(t);
1767      }
1768
1769     if (mi == NULL || pattern == NULL)
1770         return rc;
1771
1772     /* Leading '!' inverts pattern match sense, like "grep -v". */
1773     if (*pattern == '!') {
1774         notmatch = 1;
1775         pattern++;
1776     }
1777
1778     allpat = mireDup(tag, &mode, pattern);
1779
1780     if (mode == RPMMIRE_DEFAULT)
1781         mode = defmode;
1782
1783     switch (mode) {
1784     case RPMMIRE_DEFAULT:
1785     case RPMMIRE_STRCMP:
1786         break;
1787     case RPMMIRE_REGEX:
1788         preg = xcalloc(1, sizeof(*preg));
1789         cflags = (REG_EXTENDED | REG_NOSUB);
1790         rc = regcomp(preg, allpat, cflags);
1791         if (rc) {
1792             char msg[256];
1793             (void) regerror(rc, preg, msg, sizeof(msg)-1);
1794             msg[sizeof(msg)-1] = '\0';
1795             rpmlog(RPMLOG_ERR, _("%s: regcomp failed: %s\n"), allpat, msg);
1796         }
1797         break;
1798     case RPMMIRE_GLOB:
1799         fnflags = FNM_PATHNAME | FNM_PERIOD;
1800         break;
1801     default:
1802         rc = -1;
1803         break;
1804     }
1805
1806     if (rc) {
1807         /* FIX: mire has kept values */
1808         allpat = _free(allpat);
1809         if (preg) {
1810             regfree(preg);
1811             preg = _free(preg);
1812         }
1813         return rc;
1814     }
1815
1816     mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
1817     mire = mi->mi_re + mi->mi_nre;
1818     mi->mi_nre++;
1819     
1820     mire->tag = tag;
1821     mire->mode = mode;
1822     mire->pattern = allpat;
1823     mire->notmatch = notmatch;
1824     mire->preg = preg;
1825     mire->cflags = cflags;
1826     mire->eflags = eflags;
1827     mire->fnflags = fnflags;
1828
1829     if (mi->mi_nre > 1)
1830         qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
1831
1832     return rc;
1833 }
1834
1835 /**
1836  * Return iterator selector match.
1837  * @param mi            rpm database iterator
1838  * @return              1 if header should be skipped
1839  */
1840 static int mireSkip (const rpmdbMatchIterator mi)
1841 {
1842     miRE mire;
1843     uint32_t zero = 0;
1844     int ntags = 0;
1845     int nmatches = 0;
1846     int rc;
1847
1848     if (mi->mi_h == NULL)       /* XXX can't happen */
1849         return 0;
1850
1851     /*
1852      * Apply tag tests, implicitly "||" for multiple patterns/values of a
1853      * single tag, implicitly "&&" between multiple tag patterns.
1854      */
1855     if ((mire = mi->mi_re) != NULL)
1856     for (int i = 0; i < mi->mi_nre; i++, mire++) {
1857         int anymatch;
1858         struct rpmtd_s td;
1859
1860         if (!headerGet(mi->mi_h, mire->tag, &td, HEADERGET_MINMEM)) {
1861             if (mire->tag != RPMTAG_EPOCH) {
1862                 ntags++;
1863                 continue;
1864             }
1865             /* "is package already installed" checks rely on this behavior */
1866             td.count = 1;
1867             td.type = RPM_INT32_TYPE;
1868             td.data = &zero;
1869         }
1870
1871         anymatch = 0;           /* no matches yet */
1872         while (1) {
1873             rpmtdInit(&td);
1874             while (rpmtdNext(&td) >= 0) {
1875                 char *str = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
1876                 if (str) {
1877                     rc = miregexec(mire, str);
1878                     if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
1879                         anymatch++;
1880                     free(str);
1881                 }
1882             }
1883             if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
1884                 i++;
1885                 mire++;
1886                 continue;
1887             }
1888             break;
1889         }
1890         rpmtdFreeData(&td);
1891
1892         ntags++;
1893         if (anymatch)
1894             nmatches++;
1895     }
1896
1897     return (ntags == nmatches ? 0 : 1);
1898 }
1899
1900 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite)
1901 {
1902     int rc;
1903     if (mi == NULL)
1904         return 0;
1905     rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0;
1906     if (rewrite)
1907         mi->mi_cflags |= DB_WRITECURSOR;
1908     else
1909         mi->mi_cflags &= ~DB_WRITECURSOR;
1910     return rc;
1911 }
1912
1913 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified)
1914 {
1915     int rc;
1916     if (mi == NULL)
1917         return 0;
1918     rc = mi->mi_modified;
1919     mi->mi_modified = modified;
1920     return rc;
1921 }
1922
1923 int rpmdbSetHdrChk(rpmdbMatchIterator mi, rpmts ts,
1924         rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
1925 {
1926     int rc = 0;
1927     if (mi == NULL)
1928         return 0;
1929     /* XXX forward linkage prevents rpmtsLink */
1930     mi->mi_ts = ts;
1931     mi->mi_hdrchk = hdrchk;
1932     return rc;
1933 }
1934
1935
1936 /* FIX: mi->mi_key.data may be NULL */
1937 Header rpmdbNextIterator(rpmdbMatchIterator mi)
1938 {
1939     dbiIndex dbi;
1940     void * uh;
1941     size_t uhlen;
1942     DBT * key;
1943     DBT * data;
1944     void * keyp;
1945     size_t keylen;
1946     int rc;
1947     int xx;
1948
1949     if (mi == NULL)
1950         return NULL;
1951
1952     dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
1953     if (dbi == NULL)
1954         return NULL;
1955
1956     /*
1957      * Cursors are per-iterator, not per-dbi, so get a cursor for the
1958      * iterator on 1st call. If the iteration is to rewrite headers, and the
1959      * CDB model is used for the database, then the cursor needs to
1960      * marked with DB_WRITECURSOR as well.
1961      */
1962     if (mi->mi_dbc == NULL)
1963         xx = dbiCopen(dbi, dbi->dbi_txnid, &mi->mi_dbc, mi->mi_cflags);
1964
1965     key = &mi->mi_key;
1966     memset(key, 0, sizeof(*key));
1967     data = &mi->mi_data;
1968     memset(data, 0, sizeof(*data));
1969
1970 top:
1971     uh = NULL;
1972     uhlen = 0;
1973
1974     do {
1975         union _dbswap mi_offset;
1976
1977         if (mi->mi_set) {
1978             if (!(mi->mi_setx < mi->mi_set->count))
1979                 return NULL;
1980             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
1981             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
1982             mi_offset.ui = mi->mi_offset;
1983             if (dbiByteSwapped(dbi) == 1)
1984                 _DBSWAP(mi_offset);
1985             keyp = &mi_offset;
1986             keylen = sizeof(mi_offset.ui);
1987         } else {
1988
1989             key->data = keyp = (void *)mi->mi_keyp;
1990             key->size = keylen = mi->mi_keylen;
1991             data->data = uh;
1992             data->size = uhlen;
1993 #if !defined(_USE_COPY_LOAD)
1994             data->flags |= DB_DBT_MALLOC;
1995 #endif
1996             rc = dbiGet(dbi, mi->mi_dbc, key, data,
1997                         (key->data == NULL ? DB_NEXT : DB_SET));
1998             data->flags = 0;
1999             keyp = key->data;
2000             keylen = key->size;
2001             uh = data->data;
2002             uhlen = data->size;
2003
2004             /*
2005              * If we got the next key, save the header instance number.
2006              *
2007              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
2008              * largest header instance in the database, and should be
2009              * skipped.
2010              */
2011             if (keyp && mi->mi_setx && rc == 0) {
2012                 memcpy(&mi_offset, keyp, sizeof(mi_offset.ui));
2013             if (dbiByteSwapped(dbi) == 1)
2014                 _DBSWAP(mi_offset);
2015                 mi->mi_offset = mi_offset.ui;
2016             }
2017
2018             /* Terminate on error or end of keys */
2019             if (rc || (mi->mi_setx && mi->mi_offset == 0))
2020                 return NULL;
2021         }
2022         mi->mi_setx++;
2023     } while (mi->mi_offset == 0);
2024
2025     /* If next header is identical, return it now. */
2026     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
2027         return mi->mi_h;
2028
2029     /* Retrieve next header blob for index iterator. */
2030     if (uh == NULL) {
2031         key->data = keyp;
2032         key->size = keylen;
2033 #if !defined(_USE_COPY_LOAD)
2034         data->flags |= DB_DBT_MALLOC;
2035 #endif
2036         rc = dbiGet(dbi, mi->mi_dbc, key, data, DB_SET);
2037         data->flags = 0;
2038         keyp = key->data;
2039         keylen = key->size;
2040         uh = data->data;
2041         uhlen = data->size;
2042         if (rc)
2043             return NULL;
2044     }
2045
2046     /* Rewrite current header (if necessary) and unlink. */
2047     xx = miFreeHeader(mi, dbi);
2048
2049     /* Is this the end of the iteration? */
2050     if (uh == NULL)
2051         return NULL;
2052
2053     /* Check header digest/signature once (if requested). */
2054     if (mi->mi_hdrchk && mi->mi_ts) {
2055         rpmRC rpmrc = RPMRC_NOTFOUND;
2056
2057         /* Don't bother re-checking a previously read header. */
2058         if (mi->mi_db->db_bits) {
2059             pbm_set * set;
2060
2061             set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
2062                         &mi->mi_db->db_nbits, mi->mi_offset);
2063             if (PBM_ISSET(mi->mi_offset, set))
2064                 rpmrc = RPMRC_OK;
2065         }
2066
2067         /* If blob is unchecked, check blob import consistency now. */
2068         if (rpmrc != RPMRC_OK) {
2069             char * msg = NULL;
2070             int lvl;
2071
2072             rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg);
2073             lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
2074             rpmlog(lvl, "%s h#%8u %s",
2075                 (rpmrc == RPMRC_FAIL ? _("rpmdbNextIterator: skipping") : " read"),
2076                         mi->mi_offset, (msg ? msg : "\n"));
2077             msg = _free(msg);
2078
2079             /* Mark header checked. */
2080             if (mi->mi_db && mi->mi_db->db_bits && rpmrc == RPMRC_OK) {
2081                 pbm_set * set;
2082
2083                 set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
2084                         &mi->mi_db->db_nbits, mi->mi_offset);
2085                 PBM_SET(mi->mi_offset, set);
2086             }
2087
2088             /* Skip damaged and inconsistent headers. */
2089             if (rpmrc == RPMRC_FAIL)
2090                 goto top;
2091         }
2092     }
2093
2094     /* Did the header blob load correctly? */
2095 #if !defined(_USE_COPY_LOAD)
2096     mi->mi_h = headerLoad(uh);
2097 #else
2098     mi->mi_h = headerCopyLoad(uh);
2099 #endif
2100     if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
2101         rpmlog(RPMLOG_ERR,
2102                 _("rpmdb: damaged header #%u retrieved -- skipping.\n"),
2103                 mi->mi_offset);
2104         goto top;
2105     }
2106
2107     /*
2108      * Skip this header if iterator selector (if any) doesn't match.
2109      */
2110     if (mireSkip(mi)) {
2111         /* XXX hack, can't restart with Packages locked on single instance. */
2112         if (mi->mi_set || mi->mi_keyp == NULL)
2113             goto top;
2114         return NULL;
2115     }
2116     headerSetInstance(mi->mi_h, mi->mi_offset);
2117
2118     mi->mi_prevoffset = mi->mi_offset;
2119     mi->mi_modified = 0;
2120
2121     return mi->mi_h;
2122 }
2123
2124 /** \ingroup rpmdb
2125  * sort the iterator by (recnum, filenum)
2126  * Return database iterator.
2127  * @param mi            rpm database iterator
2128  */
2129 void rpmdbSortIterator(rpmdbMatchIterator mi)
2130 {
2131     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
2132     /*
2133      * mergesort is much (~10x with lots of identical basenames) faster
2134      * than pure quicksort, but glibc uses msort_with_tmp() on stack.
2135      */
2136 #if defined(__GLIBC__)
2137         qsort(mi->mi_set->recs, mi->mi_set->count,
2138                 sizeof(*mi->mi_set->recs), hdrNumCmp);
2139 #else
2140         mergesort(mi->mi_set->recs, mi->mi_set->count,
2141                 sizeof(*mi->mi_set->recs), hdrNumCmp);
2142 #endif
2143         mi->mi_sorted = 1;
2144     }
2145 }
2146
2147 static int rpmdbGrowIterator(rpmdbMatchIterator mi)
2148 {
2149     DBC * dbcursor;
2150     DBT * key;
2151     DBT * data;
2152     dbiIndex dbi = NULL;
2153     dbiIndexSet set;
2154     int rc;
2155     int xx;
2156
2157     if (mi == NULL)
2158         return 1;
2159
2160     dbcursor = mi->mi_dbc;
2161     key = &mi->mi_key;
2162     data = &mi->mi_data;
2163     if (key->data == NULL)
2164         return 1;
2165
2166     dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0);
2167     if (dbi == NULL)
2168         return 1;
2169
2170     xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2171     rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2172 #ifndef SQLITE_HACK
2173     xx = dbiCclose(dbi, dbcursor, 0);
2174     dbcursor = NULL;
2175 #endif
2176
2177     if (rc) {                   /* error/not found */
2178         if (rc != DB_NOTFOUND)
2179             rpmlog(RPMLOG_ERR,
2180                 _("error(%d) getting \"%s\" records from %s index\n"),
2181                 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
2182 #ifdef  SQLITE_HACK
2183         xx = dbiCclose(dbi, dbcursor, 0);
2184         dbcursor = NULL;
2185 #endif
2186         return rc;
2187     }
2188
2189     set = NULL;
2190     (void) dbt2set(dbi, data, &set);
2191
2192 #ifdef  SQLITE_HACK
2193     xx = dbiCclose(dbi, dbcursor, 0);
2194     dbcursor = NULL;
2195 #endif
2196
2197     if (mi->mi_set == NULL) {
2198         mi->mi_set = set;
2199     } else {
2200         dbiGrowSet(mi->mi_set, set->count);
2201         memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
2202                 set->count * sizeof(*(mi->mi_set->recs)));
2203         mi->mi_set->count += set->count;
2204         set = dbiFreeIndexSet(set);
2205     }
2206
2207     return rc;
2208 }
2209
2210 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
2211         int nHdrNums, int sorted)
2212 {
2213     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2214         return 1;
2215
2216     if (mi->mi_set)
2217         (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
2218     return 0;
2219 }
2220
2221 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
2222 {
2223     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2224         return 1;
2225
2226     if (mi->mi_set == NULL)
2227         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
2228     (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
2229     return 0;
2230 }
2231
2232 rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmTag rpmtag,
2233                 const void * keyp, size_t keylen)
2234 {
2235     rpmdbMatchIterator mi;
2236     DBT * key;
2237     DBT * data;
2238     dbiIndexSet set = NULL;
2239     dbiIndex dbi;
2240     void * mi_keyp = NULL;
2241     int isLabel = 0;
2242
2243     if (db == NULL)
2244         return NULL;
2245
2246     (void) rpmdbCheckSignals();
2247
2248     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
2249     if (rpmtag == RPMDBI_LABEL) {
2250         rpmtag = RPMTAG_NAME;
2251         isLabel = 1;
2252     }
2253
2254     dbi = dbiOpen(db, rpmtag, 0);
2255     if (dbi == NULL)
2256         return NULL;
2257
2258     /* Chain cursors for teardown on abnormal exit. */
2259     mi = xcalloc(1, sizeof(*mi));
2260     mi->mi_next = rpmmiRock;
2261     rpmmiRock = mi;
2262
2263     key = &mi->mi_key;
2264     data = &mi->mi_data;
2265
2266     /*
2267      * Handle label and file name special cases.
2268      * Otherwise, retrieve join keys for secondary lookup.
2269      */
2270     if (rpmtag != RPMDBI_PACKAGES && keyp) {
2271         DBC * dbcursor = NULL;
2272         int rc;
2273         int xx;
2274
2275         if (isLabel) {
2276             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2277             rc = dbiFindByLabel(dbi, dbcursor, key, data, keyp, &set);
2278             xx = dbiCclose(dbi, dbcursor, 0);
2279             dbcursor = NULL;
2280         } else if (rpmtag == RPMTAG_BASENAMES) {
2281             rc = rpmdbFindByFile(db, keyp, key, data, &set);
2282         } else {
2283             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2284
2285             key->data = (void *) keyp;
2286             key->size = keylen;
2287             if (key->data && key->size == 0)
2288                 key->size = strlen((char *)key->data);
2289             if (key->data && key->size == 0)
2290                 key->size++;    /* XXX "/" fixup. */
2291
2292             rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2293             if (rc > 0) {
2294                 rpmlog(RPMLOG_ERR,
2295                         _("error(%d) getting \"%s\" records from %s index\n"),
2296                         rc, (key->data ? (char *)key->data : "???"), 
2297                         rpmTagGetName(dbi->dbi_rpmtag));
2298             }
2299
2300             /* Join keys need to be native endian internally. */
2301             if (rc == 0)
2302                 (void) dbt2set(dbi, data, &set);
2303
2304             xx = dbiCclose(dbi, dbcursor, 0);
2305             dbcursor = NULL;
2306         }
2307         if (rc) {       /* error/not found */
2308             set = dbiFreeIndexSet(set);
2309             rpmmiRock = mi->mi_next;
2310             mi->mi_next = NULL;
2311             mi = _free(mi);
2312             return NULL;
2313         }
2314     }
2315
2316     /* Copy the retrieval key, byte swapping header instance if necessary. */
2317     if (keyp) {
2318         switch (rpmtag) {
2319         case RPMDBI_PACKAGES:
2320           { union _dbswap *k;
2321
2322             assert(keylen == sizeof(k->ui));    /* xxx programmer error */
2323             k = xmalloc(sizeof(*k));
2324             memcpy(k, keyp, keylen);
2325             if (dbiByteSwapped(dbi) == 1)
2326                 _DBSWAP(*k);
2327             mi_keyp = k;
2328           } break;
2329         default:
2330           { char * k;
2331             if (keylen == 0)
2332                 keylen = strlen(keyp);
2333             k = xmalloc(keylen + 1);
2334             memcpy(k, keyp, keylen);
2335             k[keylen] = '\0';   /* XXX assumes strings */
2336             mi_keyp = k;
2337           } break;
2338         }
2339     }
2340
2341     mi->mi_keyp = mi_keyp;
2342     mi->mi_keylen = keylen;
2343
2344     mi->mi_db = rpmdbLink(db, RPMDBG_M("matchIterator"));
2345     mi->mi_rpmtag = rpmtag;
2346
2347     mi->mi_dbc = NULL;
2348     mi->mi_set = set;
2349     mi->mi_setx = 0;
2350     mi->mi_h = NULL;
2351     mi->mi_sorted = 0;
2352     mi->mi_cflags = 0;
2353     mi->mi_modified = 0;
2354     mi->mi_prevoffset = 0;
2355     mi->mi_offset = 0;
2356     mi->mi_filenum = 0;
2357     mi->mi_nre = 0;
2358     mi->mi_re = NULL;
2359
2360     mi->mi_ts = NULL;
2361     mi->mi_hdrchk = NULL;
2362
2363     return mi;
2364 }
2365
2366 /** \ingroup rpmdb
2367  * Return database iterator.
2368  * @param mi            rpm database iterator
2369  * @param keyp          key data (NULL for sequential access)
2370  * @param keylen        key data length (0 will use strlen(keyp))
2371  * @return              0 on success
2372  */
2373 int rpmdbExtendIterator(rpmdbMatchIterator mi,
2374                         const void * keyp, size_t keylen)
2375 {
2376     mi->mi_key.data = (void *) keyp;
2377     mi->mi_key.size = keylen ? keylen : strlen(keyp);
2378     return rpmdbGrowIterator(mi);
2379 }
2380
2381 /*
2382  * Convert current tag data to db key
2383  * @param tagdata       Tag data container
2384  * @retval key          DB key struct
2385  * @retval freedata     Should key.data be freed afterwards
2386  * Return 0 to signal this item should be discarded (ie continue)
2387  */
2388 static int td2key(rpmtd tagdata, DBT *key, int *freedata) 
2389 {
2390     const char *str = NULL;
2391     uint8_t *bin = NULL;
2392
2393     *freedata = 0; 
2394     switch (rpmtdType(tagdata)) {
2395     case RPM_CHAR_TYPE:
2396     case RPM_INT8_TYPE:
2397         key->size = sizeof(uint8_t);
2398         key->data = rpmtdGetChar(tagdata);
2399         break;
2400     case RPM_INT16_TYPE:
2401         key->size = sizeof(uint16_t);
2402         key->data = rpmtdGetUint16(tagdata);
2403         break;
2404     case RPM_INT32_TYPE:
2405         key->size = sizeof(uint32_t);
2406         key->data = rpmtdGetUint32(tagdata);
2407         break;
2408     case RPM_INT64_TYPE:
2409         key->size = sizeof(uint64_t);
2410         key->data = rpmtdGetUint64(tagdata);
2411         break;
2412     case RPM_BIN_TYPE:
2413         key->size = tagdata->count;
2414         key->data = tagdata->data;
2415         break;
2416     case RPM_STRING_TYPE:
2417     case RPM_I18NSTRING_TYPE:
2418     case RPM_STRING_ARRAY_TYPE:
2419         str = rpmtdGetString(tagdata);
2420         if (rpmtdTag(tagdata) == RPMTAG_FILEDIGESTS) {
2421             size_t binlen;
2422             uint8_t *t;
2423         
2424             /* Filter out empty MD5 strings. */
2425             if (!(str && *str != '\0'))
2426                 return 0;
2427
2428             binlen = strlen(str) / 2;
2429             bin = xmalloc(binlen);
2430             /* Convert from hex to binary. */
2431             t = bin;
2432             for (int j = 0; j < binlen; j++, t++, str += 2) 
2433                 *t = (rnibble(str[0]) << 4) | rnibble(str[1]);
2434             key->data = bin;
2435             key->size = binlen;
2436             *freedata = 1;
2437             break;
2438         } else if (rpmtdTag(tagdata) == RPMTAG_PUBKEYS) {
2439             /* Extract the pubkey id from the base64 blob. */
2440             bin = xmalloc(sizeof(pgpKeyID_t));
2441             int nbin = pgpExtractPubkeyFingerprint(str, bin);
2442             if (nbin <= 0) {
2443                 free(bin);
2444                 return 0;
2445             }
2446             key->data = bin;
2447             key->size = nbin;
2448             *freedata = 1;
2449             break;
2450         } else {
2451             /* fallthrough */;
2452         }
2453     default:
2454         str = rpmtdGetString(tagdata);
2455         key->data = (char *) str; /* XXX discards const */
2456         key->size = strlen(str);
2457         break;
2458     }
2459
2460     if (key->size == 0) 
2461         key->size = strlen((char *)key->data);
2462     if (key->size == 0) 
2463         key->size++;    /* XXX "/" fixup. */
2464
2465     return 1;
2466 }
2467
2468 static void logAddRemove(int removing, rpmtd tagdata)
2469 {
2470     rpm_count_t c = rpmtdCount(tagdata);
2471     if (c == 1 && rpmtdType(tagdata) == RPM_STRING_TYPE) {
2472         rpmlog(RPMLOG_DEBUG, "%s \"%s\" %s %s index.\n",
2473                 removing ? "removing" : "adding", rpmtdGetString(tagdata), 
2474                 removing ? "from" : "to", 
2475                 rpmTagGetName(rpmtdTag(tagdata)));
2476     } else if (c > 0) {
2477         rpmlog(RPMLOG_DEBUG, "%s %d entries %s %s index.\n",
2478                 removing ? "removing" : "adding", c, 
2479                 removing ? "from" : "to", 
2480                 rpmTagGetName(rpmtdTag(tagdata)));
2481     }
2482 }
2483
2484 /* XXX psm.c */
2485 int rpmdbRemove(rpmdb db, int rid, unsigned int hdrNum,
2486                 rpmts ts,
2487                 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
2488 {
2489     DBC * dbcursor = NULL;
2490     DBT key;
2491     DBT data;
2492     union _dbswap mi_offset;
2493     Header h;
2494     sigset_t signalMask;
2495     int ret = 0;
2496     int rc = 0;
2497
2498     if (db == NULL)
2499         return 0;
2500
2501     memset(&key, 0, sizeof(key));
2502     memset(&data, 0, sizeof(data));
2503
2504     {   rpmdbMatchIterator mi;
2505         mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
2506         h = rpmdbNextIterator(mi);
2507         if (h)
2508             h = headerLink(h);
2509         mi = rpmdbFreeIterator(mi);
2510     }
2511
2512     if (h == NULL) {
2513         rpmlog(RPMLOG_ERR, _("%s: cannot read header at 0x%x\n"),
2514               "rpmdbRemove", hdrNum);
2515         return 1;
2516     }
2517
2518     {   
2519         char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
2520         rpmlog(RPMLOG_DEBUG, "  --- h#%8u %s\n", hdrNum, nevra);
2521         free(nevra);
2522     }
2523
2524     (void) blockSignals(&signalMask);
2525
2526         /* FIX: rpmvals heartburn */
2527     {   int dbix;
2528         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2529
2530         if (dbiTags.tags != NULL)
2531         for (dbix = 0; dbix < dbiTags.max; dbix++) {
2532             dbiIndex dbi;
2533             rpmTag rpmtag;
2534             int xx = 0;
2535             struct rpmtd_s tagdata;
2536
2537             dbi = NULL;
2538             rpmtag = dbiTags.tags[dbix];
2539
2540             /* Filter out temporary databases */
2541             if (isTemporaryDB(rpmtag)) 
2542                 continue;
2543
2544             if (rpmtag == RPMDBI_PACKAGES) {
2545                 dbi = dbiOpen(db, rpmtag, 0);
2546                 if (dbi == NULL)        /* XXX shouldn't happen */
2547                     continue;
2548               
2549                 mi_offset.ui = hdrNum;
2550                 if (dbiByteSwapped(dbi) == 1)
2551                     _DBSWAP(mi_offset);
2552                 key.data = &mi_offset;
2553                 key.size = sizeof(mi_offset.ui);
2554
2555                 rc = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2556                 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2557                 if (rc) {
2558                     rpmlog(RPMLOG_ERR,
2559                         _("error(%d) setting header #%d record for %s removal\n"),
2560                         rc, hdrNum, rpmTagGetName(dbi->dbi_rpmtag));
2561                 } else
2562                     rc = dbiDel(dbi, dbcursor, &key, &data, 0);
2563                 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2564                 dbcursor = NULL;
2565                 if (!dbi->dbi_no_dbsync)
2566                     xx = dbiSync(dbi, 0);
2567                 continue;
2568             }
2569         
2570             if (!headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM))
2571                 continue;
2572
2573             if (!(dbi = dbiOpen(db, rpmtag, 0))) {
2574                 rpmtdFreeData(&tagdata);
2575                 continue;
2576             }
2577             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2578
2579             logAddRemove(1, &tagdata);
2580             while (rpmtdNext(&tagdata) >= 0) {
2581                 dbiIndexSet set;
2582                 int freedata = 0;
2583
2584                 if (!td2key(&tagdata, &key, &freedata)) {
2585                     continue;
2586                 }
2587
2588                 /* XXX
2589                  * This is almost right, but, if there are duplicate tag
2590                  * values, there will be duplicate attempts to remove
2591                  * the header instance. It's faster to just ignore errors
2592                  * than to do things correctly.
2593                  */
2594
2595                 /* 
2596                  * XXX with duplicates, an accurate data value and 
2597                  * DB_GET_BOTH is needed. 
2598                  * */
2599                 set = NULL;
2600
2601                 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2602                 if (rc == 0) {                  /* success */
2603                     (void) dbt2set(dbi, &data, &set);
2604                 } else if (rc == DB_NOTFOUND) { /* not found */
2605                     goto cont;
2606                 } else {                        /* error */
2607                     rpmlog(RPMLOG_ERR,
2608                         _("error(%d) setting \"%s\" records from %s index\n"),
2609                         rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2610                     ret += 1;
2611                     goto cont;
2612                 }
2613
2614                 rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
2615
2616                 /* If nothing was pruned, then don't bother updating. */
2617                 if (rc) {
2618                     set = dbiFreeIndexSet(set);
2619                     goto cont;
2620                 }
2621
2622                 if (set->count > 0) {
2623                     (void) set2dbt(dbi, &data, set);
2624                     rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2625                     if (rc) {
2626                         rpmlog(RPMLOG_ERR,
2627                                 _("error(%d) storing record \"%s\" into %s\n"),
2628                                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2629                         ret += 1;
2630                     }
2631                     data.data = _free(data.data);
2632                     data.size = 0;
2633                 } else {
2634                     rc = dbiDel(dbi, dbcursor, &key, &data, 0);
2635                     if (rc) {
2636                         rpmlog(RPMLOG_ERR,
2637                                 _("error(%d) removing record \"%s\" from %s\n"),
2638                                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2639                         ret += 1;
2640                     }
2641                 }
2642                 set = dbiFreeIndexSet(set);
2643 cont:
2644                 if (freedata) {
2645                    free(key.data); 
2646                 }
2647             }
2648
2649             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2650             dbcursor = NULL;
2651
2652             if (!dbi->dbi_no_dbsync)
2653                 xx = dbiSync(dbi, 0);
2654
2655             rpmtdFreeData(&tagdata);
2656         }
2657
2658         rec = _free(rec);
2659     }
2660
2661     (void) unblockSignals(&signalMask);
2662
2663     h = headerFree(h);
2664
2665     /* XXX return ret; */
2666     return 0;
2667 }
2668
2669 /* XXX install.c */
2670 int rpmdbAdd(rpmdb db, int iid, Header h,
2671              rpmts ts,
2672              rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
2673 {
2674     DBC * dbcursor = NULL;
2675     DBT key;
2676     DBT data;
2677     sigset_t signalMask;
2678     dbiIndex dbi;
2679     int dbix;
2680     union _dbswap mi_offset;
2681     unsigned int hdrNum = 0;
2682     int ret = 0;
2683     int rc;
2684     int xx;
2685
2686     if (db == NULL)
2687         return 0;
2688
2689     memset(&key, 0, sizeof(key));
2690     memset(&data, 0, sizeof(data));
2691
2692 #ifdef  NOTYET  /* XXX headerDel() broken on dribbles. */
2693     xx = headerDel(h, RPMTAG_REMOVETID);
2694 #endif
2695     if (iid != 0 && iid != -1) {
2696         rpm_tid_t tid = iid;
2697         if (!headerIsEntry(h, RPMTAG_INSTALLTID)) 
2698             headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1);
2699     }
2700
2701     (void) blockSignals(&signalMask);
2702
2703     {
2704         unsigned int firstkey = 0;
2705         void * keyp = &firstkey;
2706         size_t keylen = sizeof(firstkey);
2707         void * datap = NULL;
2708         size_t datalen = 0;
2709
2710       dbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
2711       if (dbi != NULL) {
2712
2713         /* XXX db0: hack to pass sizeof header to fadAlloc */
2714         datap = h;
2715         datalen = headerSizeof(h, HEADER_MAGIC_NO);
2716
2717         xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2718
2719         /* Retrieve join key for next header instance. */
2720
2721         key.data = keyp;
2722         key.size = keylen;
2723         data.data = datap;
2724         data.size = datalen;
2725         ret = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2726         keyp = key.data;
2727         keylen = key.size;
2728         datap = data.data;
2729         datalen = data.size;
2730
2731         hdrNum = 0;
2732         if (ret == 0 && datap) {
2733             memcpy(&mi_offset, datap, sizeof(mi_offset.ui));
2734             if (dbiByteSwapped(dbi) == 1)
2735                 _DBSWAP(mi_offset);
2736             hdrNum = mi_offset.ui;
2737         }
2738         ++hdrNum;
2739         mi_offset.ui = hdrNum;
2740         if (dbiByteSwapped(dbi) == 1)
2741             _DBSWAP(mi_offset);
2742         if (ret == 0 && datap) {
2743             memcpy(datap, &mi_offset, sizeof(mi_offset.ui));
2744         } else {
2745             datap = &mi_offset;
2746             datalen = sizeof(mi_offset.ui);
2747         }
2748
2749         key.data = keyp;
2750         key.size = keylen;
2751         data.data = datap;
2752         data.size = datalen;
2753
2754         ret = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2755         xx = dbiSync(dbi, 0);
2756
2757         xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2758         dbcursor = NULL;
2759       }
2760
2761     }
2762
2763     if (ret) {
2764         rpmlog(RPMLOG_ERR,
2765                 _("error(%d) allocating new package instance\n"), ret);
2766         goto exit;
2767     }
2768
2769     /* Now update the indexes */
2770
2771     if (hdrNum)
2772     {   
2773         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2774
2775         if (dbiTags.tags != NULL)
2776         for (dbix = 0; dbix < dbiTags.max; dbix++) {
2777             rpmTag rpmtag;
2778             rpmRC rpmrc;
2779             int j;
2780             struct rpmtd_s tagdata, reqflags;
2781
2782             rpmrc = RPMRC_NOTFOUND;
2783             dbi = NULL;
2784             rpmtag = dbiTags.tags[dbix];
2785
2786             /* Filter out temporary databases */
2787             if (isTemporaryDB(rpmtag)) 
2788                 continue;
2789
2790             switch (rpmtag) {
2791             case RPMDBI_PACKAGES:
2792                 dbi = dbiOpen(db, rpmtag, 0);
2793                 if (dbi == NULL)        /* XXX shouldn't happen */
2794                     continue;
2795                 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2796
2797                 mi_offset.ui = hdrNum;
2798                 if (dbiByteSwapped(dbi) == 1)
2799                     _DBSWAP(mi_offset);
2800                 key.data = (void *) &mi_offset;
2801                 key.size = sizeof(mi_offset.ui);
2802                 data.data = headerUnload(h);
2803                 data.size = headerSizeof(h, HEADER_MAGIC_NO);
2804
2805                 /* Check header digest/signature on blob export. */
2806                 if (hdrchk && ts) {
2807                     char * msg = NULL;
2808                     int lvl;
2809
2810                     rpmrc = (*hdrchk) (ts, data.data, data.size, &msg);
2811                     lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
2812                     rpmlog(lvl, "%s h#%8u %s",
2813                         (rpmrc == RPMRC_FAIL ? _("rpmdbAdd: skipping") : "  +++"),
2814                                 hdrNum, (msg ? msg : "\n"));
2815                     msg = _free(msg);
2816                 }
2817
2818                 if (data.data != NULL && rpmrc != RPMRC_FAIL) {
2819                     xx = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2820                     xx = dbiSync(dbi, 0);
2821                 }
2822                 data.data = _free(data.data);
2823                 data.size = 0;
2824                 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2825                 dbcursor = NULL;
2826                 if (!dbi->dbi_no_dbsync)
2827                     xx = dbiSync(dbi, 0);
2828                 continue;
2829                 break;
2830             case RPMTAG_REQUIRENAME:
2831                 headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM);
2832                 headerGet(h, RPMTAG_REQUIREFLAGS, &reqflags, HEADERGET_MINMEM);
2833                 break;
2834             default:
2835                 headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM);
2836                 break;
2837             }
2838
2839             if (rpmtdCount(&tagdata) == 0) {
2840                 if (rpmtag != RPMTAG_GROUP)
2841                     continue;
2842
2843                 /* XXX preserve legacy behavior */
2844                 tagdata.type = RPM_STRING_TYPE;
2845                 tagdata.data = (const char **) "Unknown";
2846                 tagdata.count = 1;
2847             }
2848
2849             if (!(dbi = dbiOpen(db, rpmtag, 0))) {
2850                 rpmtdFreeData(&tagdata);
2851                 continue;
2852             }
2853             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2854
2855             logAddRemove(0, &tagdata);
2856             while (rpmtdNext(&tagdata) >= 0) {
2857                 dbiIndexSet set;
2858                 int i, freedata = 0;
2859
2860                 /*
2861                  * Include the tagNum in all indices. rpm-3.0.4 and earlier
2862                  * included the tagNum only for files.
2863                  */
2864                 i = rec->tagNum = rpmtdGetIndex(&tagdata);
2865                 switch (rpmtag) {
2866                 case RPMTAG_REQUIRENAME: {
2867                     /* Filter out install prerequisites. */
2868                     rpm_flag_t *rflag = rpmtdNextUint32(&reqflags);
2869                     if (rflag && isInstallPreReq(*rflag) &&
2870                                  !isErasePreReq(*rflag))
2871                         continue;
2872                     break;
2873                     }
2874                 case RPMTAG_TRIGGERNAME:
2875                     if (i > 0) {        /* don't add duplicates */
2876                         const char **tnames = tagdata.data;
2877                         const char *str = rpmtdGetString(&tagdata);
2878                         for (j = 0; j < i; j++) {
2879                             if (rstreq(str, tnames[j]))
2880                                 break;
2881                         }
2882                         if (j < i)
2883                             continue;
2884                     }
2885                     break;
2886                 default:
2887                     break;
2888                 }
2889
2890                 if (!td2key(&tagdata, &key, &freedata)) {
2891                     continue;
2892                 }
2893
2894                 /* 
2895                  * XXX with duplicates, an accurate data value and 
2896                  * DB_GET_BOTH is needed. 
2897                  */
2898
2899                 set = NULL;
2900
2901                 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2902                 if (rc == 0) {                  /* success */
2903                 /* With duplicates, cursor is positioned, discard the record. */
2904                     if (!dbi->dbi_permit_dups)
2905                         (void) dbt2set(dbi, &data, &set);
2906                 } else if (rc != DB_NOTFOUND) { /* error */
2907                     rpmlog(RPMLOG_ERR,
2908                         _("error(%d) getting \"%s\" records from %s index\n"),
2909                         rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2910                     ret += 1;
2911                     goto cont;
2912                 }
2913
2914                 if (set == NULL)                /* not found or duplicate */
2915                     set = xcalloc(1, sizeof(*set));
2916
2917                 (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
2918
2919                 (void) set2dbt(dbi, &data, set);
2920                 rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2921
2922                 if (rc) {
2923                     rpmlog(RPMLOG_ERR,
2924                                 _("error(%d) storing record %s into %s\n"),
2925                                 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2926                     ret += 1;
2927                 }
2928                 data.data = _free(data.data);
2929                 data.size = 0;
2930                 set = dbiFreeIndexSet(set);
2931 cont:
2932                 if (freedata) {
2933                     free(key.data);
2934                 }
2935             }
2936
2937             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2938             dbcursor = NULL;
2939
2940             if (!dbi->dbi_no_dbsync)
2941                 xx = dbiSync(dbi, 0);
2942
2943             rpmtdFreeData(&tagdata);
2944         }
2945
2946         rec = _free(rec);
2947         /* If everthing ok, mark header as installed now */
2948         if (ret == 0) {
2949             headerSetInstance(h, hdrNum);
2950         }
2951     }
2952
2953 exit:
2954     (void) unblockSignals(&signalMask);
2955
2956     return ret;
2957 }
2958
2959 /*
2960  * Remove DB4 environment (and lock), ie the equivalent of 
2961  * rm -f <prefix>/<dbpath>/__db.???
2962  * Environment files not existing is not an error, failure to unlink is,
2963  * return zero on success.
2964  * Only useful for BDB, dbapi 3 and 4.  
2965  * TODO/FIX: push this down to db3.c where it belongs
2966  */
2967 static int cleanDbenv(const char *prefix, const char *dbpath)
2968 {
2969     ARGV_t paths = NULL, p;
2970     int rc = 0; 
2971     char *pattern = rpmGetPath(prefix, "/", dbpath, "/__db.???", NULL);
2972
2973     if (rpmGlob(pattern, NULL, &paths) == 0) {
2974         for (p = paths; *p; p++) {
2975             rc += unlink(*p);
2976         }
2977         argvFree(paths);
2978     }
2979     free(pattern);
2980     return rc;
2981 }
2982
2983 static int rpmdbRemoveDatabase(const char * prefix,
2984                 const char * dbpath, int _dbapi)
2985
2986     int i;
2987     char *path;
2988     int xx;
2989
2990     dbiTagsInit();
2991     switch (_dbapi) {
2992     case 4:
2993     case 3:
2994         if (dbiTags.tags != NULL)
2995         for (i = 0; i < dbiTags.max; i++) {
2996             const char * base = rpmTagGetName(dbiTags.tags[i]);
2997             path = rpmGetPath(prefix, "/", dbpath, "/", base, NULL);
2998             if (access(path, F_OK) == 0)
2999                 xx = unlink(path);
3000             free(path);
3001         }
3002         cleanDbenv(prefix, dbpath);
3003         break;
3004     case 2:
3005     case 1:
3006     case 0:
3007         break;
3008     }
3009     dbiTagsFree();
3010
3011     path = rpmGetPath(prefix, "/", dbpath, NULL);
3012     xx = rmdir(path);
3013     free(path);
3014
3015     return 0;
3016 }
3017
3018 static int rpmdbMoveDatabase(const char * prefix,
3019                 const char * olddbpath, int _olddbapi,
3020                 const char * newdbpath, int _newdbapi)
3021 {
3022     int i;
3023     struct stat st;
3024     int rc = 0;
3025     int xx;
3026     int selinux = is_selinux_enabled() && (matchpathcon_init(NULL) != -1);
3027     sigset_t sigMask;
3028
3029     dbiTagsInit();
3030     blockSignals(&sigMask);
3031     switch (_olddbapi) {
3032     case 4:
3033         /* Fall through */
3034     case 3:
3035         if (dbiTags.tags != NULL)
3036         for (i = 0; i < dbiTags.max; i++) {
3037             const char * base;
3038             char *src, *dest;
3039             rpmTag rpmtag;
3040
3041             /* Filter out temporary databases */
3042             if (isTemporaryDB((rpmtag = dbiTags.tags[i])))
3043                 continue;
3044
3045             base = rpmTagGetName(rpmtag);
3046             src = rpmGetPath(prefix, "/", olddbpath, "/", base, NULL);
3047             dest = rpmGetPath(prefix, "/", newdbpath, "/", base, NULL);
3048
3049             if (access(src, F_OK) != 0)
3050                 goto cont;
3051
3052             /*
3053              * Restore uid/gid/mode/mtime/security context if possible.
3054              */
3055             if (stat(dest, &st) < 0)
3056                 if (stat(src, &st) < 0)
3057                     goto cont;
3058
3059             if ((xx = rename(src, dest)) != 0) {
3060                 rc = 1;
3061                 goto cont;
3062             }
3063             xx = chown(dest, st.st_uid, st.st_gid);
3064             xx = chmod(dest, (st.st_mode & 07777));
3065             {   struct utimbuf stamp;
3066                 stamp.actime = st.st_atime;
3067                 stamp.modtime = st.st_mtime;
3068                 xx = utime(dest, &stamp);
3069             }
3070
3071             if (selinux) {
3072                 security_context_t scon = NULL;
3073                 if (matchpathcon(dest, st.st_mode, &scon) != -1) {
3074                     (void) setfilecon(dest, scon);
3075                     freecon(scon);
3076                 }
3077             }
3078                 
3079 cont:
3080             free(src);
3081             free(dest);
3082         }
3083
3084         cleanDbenv(prefix, olddbpath);
3085         cleanDbenv(prefix, newdbpath);
3086         break;
3087     case 2:
3088     case 1:
3089     case 0:
3090         break;
3091     }
3092     unblockSignals(&sigMask);
3093
3094 #ifdef  SQLITE_HACK_XXX
3095     if (rc || _olddbapi == _newdbapi)
3096         return rc;
3097
3098     rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi);
3099
3100 #endif
3101     if (selinux) {
3102         (void) matchpathcon_fini();
3103     }
3104     dbiTagsFree();
3105     return rc;
3106 }
3107
3108 int rpmdbRebuild(const char * prefix, rpmts ts,
3109                 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
3110 {
3111     rpmdb olddb;
3112     char * dbpath = NULL;
3113     char * rootdbpath = NULL;
3114     rpmdb newdb;
3115     char * newdbpath = NULL;
3116     char * newrootdbpath = NULL;
3117     char * tfn;
3118     int nocleanup = 1;
3119     int failed = 0;
3120     int removedir = 0;
3121     int rc = 0, xx;
3122     int _dbapi;
3123     int _dbapi_rebuild;
3124
3125     if (prefix == NULL) prefix = "/";
3126
3127     _dbapi = rpmExpandNumeric("%{_dbapi}");
3128     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
3129
3130     tfn = rpmGetPath("%{?_dbpath}", NULL);
3131     if (!(tfn && tfn[0] != '\0'))
3132     {
3133         rpmlog(RPMLOG_ERR, _("no dbpath has been set"));
3134         rc = 1;
3135         goto exit;
3136     }
3137     dbpath = rootdbpath = rpmGetPath(prefix, tfn, NULL);
3138     if (!(prefix[0] == '/' && prefix[1] == '\0'))
3139         dbpath += strlen(prefix) - 1;
3140     tfn = _free(tfn);
3141
3142     tfn = rpmGetPath("%{?_dbpath_rebuild}", NULL);
3143     if (!(tfn && tfn[0] != '\0' && !rstreq(tfn, dbpath)))
3144     {
3145         tfn = _free(tfn);
3146         rasprintf(&tfn, "%srebuilddb.%d", dbpath, (int) getpid());
3147         nocleanup = 0;
3148     }
3149     newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL);
3150     if (!(prefix[0] == '/' && prefix[1] == '\0'))
3151         newdbpath += strlen(prefix) - 1;
3152     tfn = _free(tfn);
3153
3154     rpmlog(RPMLOG_DEBUG, "rebuilding database %s into %s\n",
3155         rootdbpath, newrootdbpath);
3156
3157     if (!access(newrootdbpath, F_OK)) {
3158         rpmlog(RPMLOG_ERR, _("temporary database %s already exists\n"),
3159               newrootdbpath);
3160         rc = 1;
3161         goto exit;
3162     }
3163
3164     rpmlog(RPMLOG_DEBUG, "creating directory %s\n", newrootdbpath);
3165     if (mkdir(newrootdbpath, 0755)) {
3166         rpmlog(RPMLOG_ERR, _("failed to create directory %s: %s\n"),
3167               newrootdbpath, strerror(errno));
3168         rc = 1;
3169         goto exit;
3170     }
3171     removedir = 1;
3172
3173     _rebuildinprogress = 0;
3174
3175     rpmlog(RPMLOG_DEBUG, "opening old database with dbapi %d\n",
3176                 _dbapi);
3177     if (openDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644, 
3178                      RPMDB_FLAG_MINIMAL)) {
3179         rc = 1;
3180         goto exit;
3181     }
3182     _dbapi = olddb->db_api;
3183     _rebuildinprogress = 1;
3184     rpmlog(RPMLOG_DEBUG, "opening new database with dbapi %d\n",
3185                 _dbapi_rebuild);
3186     (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1);
3187     if (openDatabase(prefix, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
3188         rc = 1;
3189         goto exit;
3190     }
3191
3192     _rebuildinprogress = 0;
3193
3194     _dbapi_rebuild = newdb->db_api;
3195     
3196     {   Header h = NULL;
3197         rpmdbMatchIterator mi;
3198 #define _RECNUM rpmdbGetIteratorOffset(mi)
3199
3200         mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
3201         if (ts && hdrchk)
3202             (void) rpmdbSetHdrChk(mi, ts, hdrchk);
3203
3204         while ((h = rpmdbNextIterator(mi)) != NULL) {
3205
3206             /* let's sanity check this record a bit, otherwise just skip it */
3207             if (!(headerIsEntry(h, RPMTAG_NAME) &&
3208                 headerIsEntry(h, RPMTAG_VERSION) &&
3209                 headerIsEntry(h, RPMTAG_RELEASE) &&
3210                 headerIsEntry(h, RPMTAG_BUILDTIME)))
3211             {
3212                 rpmlog(RPMLOG_ERR,
3213                         _("header #%u in the database is bad -- skipping.\n"),
3214                         _RECNUM);
3215                 continue;
3216             }
3217
3218             /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
3219             if (_db_filter_dups || newdb->db_filter_dups) {
3220                 const char *name = headerGetString(h, RPMTAG_NAME);
3221                 const char *version = headerGetString(h, RPMTAG_VERSION);
3222                 const char *release = headerGetString(h, RPMTAG_RELEASE);
3223                 int skip = 0;
3224
3225                 {   rpmdbMatchIterator mi;
3226                     mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
3227                     (void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
3228                                 RPMMIRE_DEFAULT, version);
3229                     (void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
3230                                 RPMMIRE_DEFAULT, release);
3231                     while (rpmdbNextIterator(mi)) {
3232                         skip = 1;
3233                         break;
3234                     }
3235                     mi = rpmdbFreeIterator(mi);
3236                 }
3237
3238                 if (skip)
3239                     continue;
3240             }
3241
3242             /* Deleted entries are eliminated in legacy headers by copy. */
3243             {   Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
3244                                 ? headerCopy(h) : NULL);
3245                 rc = rpmdbAdd(newdb, -1, (nh ? nh : h), ts, hdrchk);
3246                 nh = headerFree(nh);
3247             }
3248
3249             if (rc) {
3250                 rpmlog(RPMLOG_ERR,
3251                         _("cannot add record originally at %u\n"), _RECNUM);
3252                 failed = 1;
3253                 break;
3254             }
3255         }
3256
3257         mi = rpmdbFreeIterator(mi);
3258
3259     }
3260
3261     xx = rpmdbClose(olddb);
3262     xx = rpmdbClose(newdb);
3263
3264     if (failed) {
3265         rpmlog(RPMLOG_WARNING, 
3266                 _("failed to rebuild database: original database "
3267                 "remains in place\n"));
3268
3269         xx = rpmdbRemoveDatabase(prefix, newdbpath, _dbapi_rebuild);
3270         rc = 1;
3271         goto exit;
3272     } else if (!nocleanup) {
3273         if (rpmdbMoveDatabase(prefix, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
3274             rpmlog(RPMLOG_ERR, _("failed to replace old database with new "
3275                         "database!\n"));
3276             rpmlog(RPMLOG_ERR, _("replace files in %s with files from %s "
3277                         "to recover"), dbpath, newdbpath);
3278             rc = 1;
3279             goto exit;
3280         }
3281     }
3282     rc = 0;
3283
3284 exit:
3285     if (removedir && !(rc == 0 && nocleanup)) {
3286         rpmlog(RPMLOG_DEBUG, "removing directory %s\n", newrootdbpath);
3287         if (rmdir(newrootdbpath))
3288             rpmlog(RPMLOG_ERR, _("failed to remove directory %s: %s\n"),
3289                         newrootdbpath, strerror(errno));
3290     }
3291     newrootdbpath = _free(newrootdbpath);
3292     rootdbpath = _free(rootdbpath);
3293
3294     return rc;
3295 }