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