fix: make a copy of retrieved header before loading.
[platform/upstream/rpm.git] / lib / rpmdb.c
1 /** \ingroup rpmdb dbi
2  * \file lib/rpmdb.c
3  */
4
5 #include "system.h"
6
7 static int _debug = 0;
8 #define INLINE
9
10 #include <sys/file.h>
11 #include <signal.h>
12 #include <sys/signal.h>
13
14 #include <rpmlib.h>
15 #include <rpmmacro.h>   /* XXX for rpmGetPath/rpmGenPath */
16
17 #include "rpmdb.h"
18 #include "fprint.h"
19 #include "misc.h"
20 #include "debug.h"
21
22 /*@access dbiIndexSet@*/
23 /*@access dbiIndexItem@*/
24 /*@access Header@*/             /* XXX compared with NULL */
25 /*@access rpmdbMatchIterator@*/
26
27 extern int _noDirTokens;
28 static int _rebuildinprogress = 0;
29 static int _db_filter_dups = 0;
30
31 int _filterDbDups = 0;  /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
32
33 #define _DBI_FLAGS      0
34 #define _DBI_PERMS      0644
35 #define _DBI_MAJOR      -1
36
37 static int dbiTagsMax = 0;
38 /*@only@*/ static int *dbiTags = NULL;
39
40 /**
41  * Return dbi index used for rpm tag.
42  * @param rpmtag        rpm header tag
43  * @return dbi index, -1 on error
44  */
45 static int dbiTagToDbix(int rpmtag)
46 {
47     int dbix;
48
49     if (!(dbiTagsMax > 0 && dbiTags))
50         return -1;
51     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
52         if (rpmtag == dbiTags[dbix])
53             return dbix;
54     }
55     return -1;
56 }
57
58 /**
59  * Initialize database (index, tag) tuple from configuration.
60  */
61 static void dbiTagsInit(void)
62 {
63 /*@observer@*/ static const char * const _dbiTagStr_default =
64         "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername";
65     char * dbiTagStr = NULL;
66     char * o, * oe;
67     int rpmtag;
68
69     dbiTagStr = rpmExpand("%{_dbi_tags}", NULL);
70     if (!(dbiTagStr && *dbiTagStr && *dbiTagStr != '%')) {
71         free((void *)dbiTagStr);
72         dbiTagStr = xstrdup(_dbiTagStr_default);
73     }
74
75     if (dbiTagsMax || dbiTags) {
76         free(dbiTags);
77         dbiTags = NULL;
78         dbiTagsMax = 0;
79     }
80
81     /* Always allocate package index */
82     dbiTagsMax = 1;
83     dbiTags = xcalloc(1, dbiTagsMax * sizeof(*dbiTags));
84
85     for (o = dbiTagStr; o && *o; o = oe) {
86         while (*o && isspace(*o))
87             o++;
88         if (*o == '\0')
89             break;
90         for (oe = o; oe && *oe; oe++) {
91             if (isspace(*oe))
92                 break;
93             if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
94                 break;
95         }
96         if (oe && *oe)
97             *oe++ = '\0';
98         rpmtag = tagValue(o);
99         if (rpmtag < 0) {
100
101             fprintf(stderr, _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
102             continue;
103         }
104         if (dbiTagToDbix(rpmtag) >= 0)
105             continue;
106
107         dbiTags = xrealloc(dbiTags, (dbiTagsMax + 1) * sizeof(*dbiTags)); /* XXX memory leak */
108         dbiTags[dbiTagsMax++] = rpmtag;
109     }
110
111     if (dbiTagStr)
112         free(dbiTagStr);
113 }
114
115 #if USE_DB1
116 extern struct _dbiVec db1vec;
117 #define DB1vec          &db1vec
118 #else
119 #define DB1vec          NULL
120 #endif
121
122 #if USE_DB2
123 extern struct _dbiVec db2vec;
124 #define DB2vec          &db2vec
125 #else
126 #define DB2vec          NULL
127 #endif
128
129 #if USE_DB3
130 extern struct _dbiVec db3vec;
131 #define DB3vec          &db3vec
132 #else
133 #define DB3vec          NULL
134 #endif
135
136 static struct _dbiVec *mydbvecs[] = {
137     DB1vec, DB1vec, DB2vec, DB3vec, NULL
138 };
139
140 INLINE int dbiSync(dbiIndex dbi, unsigned int flags) {
141 if (_debug < 0 || dbi->dbi_debug)
142 fprintf(stderr, "    Sync %s\n", tagName(dbi->dbi_rpmtag));
143     return (*dbi->dbi_vec->sync) (dbi, flags);
144 }
145
146 INLINE int dbiByteSwapped(dbiIndex dbi) {
147     return (*dbi->dbi_vec->byteswapped) (dbi);
148 }
149
150 INLINE int XdbiCopen(dbiIndex dbi, /*@out@*/ DBC ** dbcp, unsigned int flags,
151         const char * f, unsigned int l)
152 {
153 if (_debug < 0 || dbi->dbi_debug)
154 fprintf(stderr, "+++ RMW %s (%s:%u)\n", tagName(dbi->dbi_rpmtag), f, l);
155     return (*dbi->dbi_vec->copen) (dbi, dbcp, flags);
156 }
157
158 INLINE int XdbiCclose(dbiIndex dbi, /*@only@*/ DBC * dbcursor, unsigned int flags,
159         const char * f, unsigned int l)
160 {
161 if (_debug < 0 || dbi->dbi_debug)
162 fprintf(stderr, "--- RMW %s (%s:%u)\n", tagName(dbi->dbi_rpmtag), f, l);
163     return (*dbi->dbi_vec->cclose) (dbi, dbcursor, flags);
164 }
165
166 INLINE int dbiDel(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen, unsigned int flags)
167 {
168     int NULkey;
169     int rc;
170
171     /* XXX make sure that keylen is correct for "" lookup */
172     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
173     if (NULkey) keylen++;
174     rc = (*dbi->dbi_vec->cdel) (dbi, dbcursor, keyp, keylen, flags);
175     if (NULkey) keylen--;
176
177 if (_debug < 0 || dbi->dbi_debug)
178 fprintf(stderr, "    Del %s key (%p,%ld) %s rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)keyp : ""), rc);
179
180     return rc;
181 }
182
183 INLINE int dbiGet(dbiIndex dbi, DBC * dbcursor, void ** keypp, size_t * keylenp,
184         void ** datapp, size_t * datalenp, unsigned int flags)
185 {
186     int NULkey;
187     int rc;
188
189     /* XXX make sure that keylen is correct for "" lookup */
190     NULkey = (keypp && *keypp && *((char *)(*keypp)) == '\0' && keylenp && *keylenp == 0);
191     if (NULkey) (*keylenp)++;
192     rc = (*dbi->dbi_vec->cget) (dbi, dbcursor, keypp, keylenp, datapp, datalenp, flags);
193     if (NULkey) (*keylenp)--;
194
195 if (_debug < 0 || dbi->dbi_debug) {
196 char keyval[32];
197 int dataval = 0xdeadbeef;
198 if (dbi->dbi_rpmtag == RPMDBI_PACKAGES && keypp && *keypp && keylenp && *keylenp >= sizeof(keyval)) {
199     int keyint;
200     memcpy(&keyint, *keypp, sizeof(keyint));
201     sprintf(keyval, "%d", keyint);
202 } else keyval[0] = '\0';
203 if (rc == 0 && datapp && *datapp && datalenp && *datalenp >= sizeof(dataval))
204     memcpy(&dataval, *datapp, sizeof(dataval));
205 fprintf(stderr, "    Get %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n",
206     tagName(dbi->dbi_rpmtag), *keypp, (long)*keylenp, *datapp, (long)*datalenp,
207     (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)*keypp : keyval), (unsigned)dataval, rc);
208 }
209     return rc;
210 }
211
212 INLINE int dbiPut(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen,
213         const void * datap, size_t datalen, unsigned int flags)
214 {
215     int NULkey;
216     int rc;
217
218     /* XXX make sure that keylen is correct for "" lookup */
219     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
220     if (NULkey) keylen++;
221     rc = (*dbi->dbi_vec->cput) (dbi, dbcursor, keyp, keylen, datap, datalen, flags);
222     if (NULkey) keylen--;
223
224 if (_debug < 0 || dbi->dbi_debug) {
225 int dataval = 0xdeadbeef;
226 if (datap) memcpy(&dataval, datap, sizeof(dataval));
227 fprintf(stderr, "    Put %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, datap, (long)datalen, (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)keyp : ""), (unsigned)dataval, rc);
228 }
229
230     return rc;
231 }
232
233 INLINE int dbiClose(dbiIndex dbi, unsigned int flags) {
234 if (_debug < 0 || dbi->dbi_debug)
235 fprintf(stderr, "    %s Close\n", tagName(dbi->dbi_rpmtag));
236     return (*dbi->dbi_vec->close) (dbi, flags);
237 }
238
239 dbiIndex dbiOpen(rpmdb rpmdb, int rpmtag, /*@unused@*/ unsigned int flags)
240 {
241     int dbix;
242     dbiIndex dbi = NULL;
243     int _dbapi, _dbapi_rebuild, _dbapi_wanted;
244     int rc = 0;
245
246     dbix = dbiTagToDbix(rpmtag);
247     if (dbix < 0 || dbix >= dbiTagsMax)
248         return NULL;
249
250     /* Is this index already open ? */
251     if ((dbi = rpmdb->_dbi[dbix]) != NULL)
252         return dbi;
253
254     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
255     if (_dbapi_rebuild < 1 || _dbapi_rebuild > 3)
256         _dbapi_rebuild = 3;
257     _dbapi_wanted = (_rebuildinprogress ? -1 : rpmdb->db_api);
258
259     switch (_dbapi_wanted) {
260     default:
261         _dbapi = _dbapi_wanted;
262         if (_dbapi < 0 || _dbapi >= 4 || mydbvecs[_dbapi] == NULL) {
263             return NULL;
264         }
265         errno = 0;
266         dbi = NULL;
267         rc = (*mydbvecs[_dbapi]->open) (rpmdb, rpmtag, &dbi);
268         if (rc) {
269             static int _printed[32];
270             if (!_printed[dbix & 0x1f]++)
271                 rpmError(RPMERR_DBOPEN,
272                         _("cannot open %s index using db%d - %s (%d)\n"),
273                         tagName(rpmtag), _dbapi,
274                         (rc > 0 ? strerror(rc) : ""), rc);
275             _dbapi = -1;
276         }
277         break;
278     case -1:
279         _dbapi = 4;
280         while (_dbapi-- > 1) {
281             if (mydbvecs[_dbapi] == NULL)
282                 continue;
283             errno = 0;
284             dbi = NULL;
285             rc = (*mydbvecs[_dbapi]->open) (rpmdb, rpmtag, &dbi);
286             if (rc == 0 && dbi)
287                 break;
288         }
289         if (_dbapi <= 0) {
290             static int _printed[32];
291             if (!_printed[dbix & 0x1f]++)
292                 rpmError(RPMERR_DBOPEN, _("cannot open %s index\n"),
293                         tagName(rpmtag));
294             rc = 1;
295             goto exit;
296         }
297         if (rpmdb->db_api == -1 && _dbapi > 0)
298             rpmdb->db_api = _dbapi;
299         break;
300     }
301
302     /* Require conversion. */
303     if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
304         rc = (_rebuildinprogress ? 0 : 1);
305         goto exit;
306     }
307
308     /* Suggest possible configuration */
309     if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
310         rc = 1;
311         goto exit;
312     }
313
314     /* Suggest possible configuration */
315     if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
316         rc = (_rebuildinprogress ? 0 : 1);
317         goto exit;
318     }
319
320 exit:
321     if (rc == 0 && dbi) {
322         rpmdb->_dbi[dbix] = dbi;
323     } else if (dbi) {
324         db3Free(dbi);
325         dbi = NULL;
326     }
327
328     return dbi;
329 }
330
331 /**
332  * Create and initialize item for index database set.
333  * @param hdrNum        header instance in db
334  * @param tagNum        tag index in header
335  * @return              new item
336  */
337 static INLINE dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum) {
338     dbiIndexItem rec = xcalloc(1, sizeof(*rec));
339     rec->hdrNum = hdrNum;
340     rec->tagNum = tagNum;
341     return rec;
342 }
343
344 union _dbswap {
345     unsigned int ui;
346     unsigned char uc[4];
347 };
348
349 #define _DBSWAP(_a) \
350   { unsigned char _b, *_c = (_a).uc; \
351     _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
352     _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
353   }
354
355 /**
356  * Return items that match criteria.
357  * @param dbi           index database handle
358  * @param dbcursor      index database cursor
359  * @param keyp          search key
360  * @param keylen        search key length (0 will use strlen(key))
361  * @param setp          address of items retrieved from index database
362  * @return              -1 error, 0 success, 1 not found
363  */
364 static int dbiSearch(dbiIndex dbi, DBC * dbcursor,
365         const char * keyp, size_t keylen, dbiIndexSet * setp)
366 {
367     void * datap = NULL;
368     size_t datalen = 0;
369     int rc;
370
371     if (setp) *setp = NULL;
372     if (keylen == 0) keylen = strlen(keyp);
373
374     rc = dbiGet(dbi, dbcursor, (void **)&keyp, &keylen, &datap, &datalen, 0);
375
376     if (rc > 0) {
377         rpmError(RPMERR_DBGETINDEX,
378                 _("error(%d) getting \"%s\" records from %s index\n"),
379                 rc, keyp, tagName(dbi->dbi_rpmtag));
380     } else
381     if (rc == 0 && setp) {
382         int _dbbyteswapped = dbiByteSwapped(dbi);
383         const char * sdbir = datap;
384         dbiIndexSet set;
385         int i;
386
387         set = xmalloc(sizeof(*set));
388
389         /* Convert to database internal format */
390         switch (dbi->dbi_jlen) {
391         default:
392         case 2*sizeof(int_32):
393             set->count = datalen / (2*sizeof(int_32));
394             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
395             for (i = 0; i < set->count; i++) {
396                 union _dbswap hdrNum, tagNum;
397
398                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
399                 sdbir += sizeof(hdrNum.ui);
400                 memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
401                 sdbir += sizeof(tagNum.ui);
402                 if (_dbbyteswapped) {
403                     _DBSWAP(hdrNum);
404                     _DBSWAP(tagNum);
405                 }
406                 set->recs[i].hdrNum = hdrNum.ui;
407                 set->recs[i].tagNum = tagNum.ui;
408                 set->recs[i].fpNum = 0;
409                 set->recs[i].dbNum = 0;
410             }
411             break;
412         case 1*sizeof(int_32):
413             set->count = datalen / (1*sizeof(int_32));
414             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
415             for (i = 0; i < set->count; i++) {
416                 union _dbswap hdrNum;
417
418                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
419                 sdbir += sizeof(hdrNum.ui);
420                 if (_dbbyteswapped) {
421                     _DBSWAP(hdrNum);
422                 }
423                 set->recs[i].hdrNum = hdrNum.ui;
424                 set->recs[i].tagNum = 0;
425                 set->recs[i].fpNum = 0;
426                 set->recs[i].dbNum = 0;
427             }
428             break;
429         }
430         *setp = set;
431     }
432     return rc;
433 }
434
435 /**
436  * Change/delete items that match criteria.
437  * @param dbi           index database handle
438  * @param dbcursor      index database cursor
439  * @param keyp          update key
440  * @param keylen        update key length
441  * @param set           items to update in index database
442  * @return              0 success, 1 not found
443  */
444 /*@-compmempass@*/
445 static int dbiUpdateIndex(dbiIndex dbi, DBC * dbcursor,
446         const void * keyp, size_t keylen, dbiIndexSet set)
447 {
448     void * datap;
449     size_t datalen;
450     int rc;
451
452     if (set->count) {
453         char * tdbir;
454         int i;
455         int _dbbyteswapped = dbiByteSwapped(dbi);
456
457         /* Convert to database internal format */
458
459         switch (dbi->dbi_jlen) {
460         default:
461         case 2*sizeof(int_32):
462             datalen = set->count * (2 * sizeof(int_32));
463             datap = tdbir = alloca(datalen);
464             for (i = 0; i < set->count; i++) {
465                 union _dbswap hdrNum, tagNum;
466
467                 hdrNum.ui = set->recs[i].hdrNum;
468                 tagNum.ui = set->recs[i].tagNum;
469                 if (_dbbyteswapped) {
470                     _DBSWAP(hdrNum);
471                     _DBSWAP(tagNum);
472                 }
473                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
474                 tdbir += sizeof(hdrNum.ui);
475                 memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
476                 tdbir += sizeof(tagNum.ui);
477             }
478             break;
479         case 1*sizeof(int_32):
480             datalen = set->count * (1 * sizeof(int_32));
481             datap = tdbir = alloca(datalen);
482             for (i = 0; i < set->count; i++) {
483                 union _dbswap hdrNum;
484
485                 hdrNum.ui = set->recs[i].hdrNum;
486                 if (_dbbyteswapped) {
487                     _DBSWAP(hdrNum);
488                 }
489                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
490                 tdbir += sizeof(hdrNum.ui);
491             }
492             break;
493         }
494
495         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, 0);
496
497         if (rc) {
498             rpmError(RPMERR_DBPUTINDEX,
499                 _("error(%d) storing record %s into %s\n"),
500                 rc, keyp, tagName(dbi->dbi_rpmtag));
501         }
502
503     } else {
504
505         rc = dbiDel(dbi, dbcursor, keyp, keylen, 0);
506
507         if (rc) {
508             rpmError(RPMERR_DBPUTINDEX,
509                 _("error(%d) removing record %s from %s\n"),
510                 rc, keyp, tagName(dbi->dbi_rpmtag));
511         }
512
513     }
514
515     return rc;
516 }
517 /*@=compmempass@*/
518
519 /* XXX assumes hdrNum is first int in dbiIndexItem */
520 static int hdrNumCmp(const void * one, const void * two) {
521     const int * a = one, * b = two;
522     return (*a - *b);
523 }
524
525 /**
526  * Append element(s) to set of index database items.
527  * @param set           set of index database items
528  * @param recs          array of items to append to set
529  * @param nrecs         number of items
530  * @param recsize       size of an array item
531  * @param sortset       should resulting set be sorted?
532  * @return              0 success, 1 failure (bad args)
533  */
534 static INLINE int dbiAppendSet(dbiIndexSet set, const void * recs,
535         int nrecs, size_t recsize, int sortset)
536 {
537     const char * rptr = recs;
538     size_t rlen = (recsize < sizeof(*(set->recs)))
539                 ? recsize : sizeof(*(set->recs));
540
541     if (set == NULL || recs == NULL || nrecs <= 0 || recsize <= 0)
542         return 1;
543
544     set->recs = (set->count == 0)
545         ? xmalloc(nrecs * sizeof(*(set->recs)))
546         : xrealloc(set->recs, (set->count + nrecs) * sizeof(*(set->recs)));
547
548     memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
549
550     while (nrecs-- > 0) {
551         memcpy(set->recs + set->count, rptr, rlen);
552         rptr += recsize;
553         set->count++;
554     }
555
556     if (set->count > 1 && sortset)
557         qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
558
559     return 0;
560 }
561
562 /**
563  * Remove element(s) from set of index database items.
564  * @param set           set of index database items
565  * @param recs          array of items to remove from set
566  * @param nrecs         number of items
567  * @param recsize       size of an array item
568  * @param sorted        array is already sorted?
569  * @return              0 success, 1 failure (no items found)
570  */
571 static INLINE int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
572         size_t recsize, int sorted)
573 {
574     int from;
575     int to = 0;
576     int num = set->count;
577     int numCopied = 0;
578
579     if (nrecs > 1 && !sorted)
580         qsort(recs, nrecs, recsize, hdrNumCmp);
581
582     for (from = 0; from < num; from++) {
583         if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
584             set->count--;
585             continue;
586         }
587         if (from != to)
588             set->recs[to] = set->recs[from]; /* structure assignment */
589         to++;
590         numCopied++;
591     }
592
593     return (numCopied == num);
594 }
595
596 /* XXX transaction.c */
597 unsigned int dbiIndexSetCount(dbiIndexSet set) {
598     return set->count;
599 }
600
601 /* XXX transaction.c */
602 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
603     return set->recs[recno].hdrNum;
604 }
605
606 /* XXX transaction.c */
607 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
608     return set->recs[recno].tagNum;
609 }
610
611 /* XXX transaction.c */
612 void dbiFreeIndexSet(dbiIndexSet set) {
613     if (set) {
614         if (set->recs) free(set->recs);
615         free(set);
616     }
617 }
618
619 /**
620  * Disable all signals, returning previous signal mask.
621  */
622 static void blockSignals(rpmdb rpmdb, /*@out@*/ sigset_t * oldMask)
623 {
624     sigset_t newMask;
625
626     /* XXX HACK (disabled) permit ^C aborts for now ... */
627     if (!(rpmdb && rpmdb->db_api == 4)) {
628         sigfillset(&newMask);           /* block all signals */
629         sigprocmask(SIG_BLOCK, &newMask, oldMask);
630     }
631 }
632
633 /**
634  * Restore signal mask.
635  */
636 static void unblockSignals(rpmdb rpmdb, sigset_t * oldMask)
637 {
638     /* XXX HACK (disabled) permit ^C aborts for now ... */
639     if (!(rpmdb && rpmdb->db_api == 4)) {
640         sigprocmask(SIG_SETMASK, oldMask, NULL);
641     }
642 }
643
644 #define _DB_ROOT        "/"
645 #define _DB_HOME        "%{_dbpath}"
646 #define _DB_FLAGS       0
647 #define _DB_MODE        0
648 #define _DB_PERMS       0644
649
650 #define _DB_MAJOR       -1
651 #define _DB_REMOVE_ENV  0
652 #define _DB_FILTER_DUPS 0
653 #define _DB_ERRPFX      "rpmdb"
654
655 /*@-fullinitblock@*/
656 /*@observer@*/ static struct rpmdb_s dbTemplate = {
657     _DB_ROOT,   _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
658     _DB_MAJOR,  _DB_REMOVE_ENV, _DB_FILTER_DUPS, _DB_ERRPFX
659 };
660 /*@=fullinitblock@*/
661
662 int rpmdbOpenAll (rpmdb rpmdb)
663 {
664     int dbix;
665
666     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
667         if (rpmdb->_dbi[dbix] != NULL)
668             continue;
669         (void) dbiOpen(rpmdb, dbiTags[dbix], rpmdb->db_flags);
670     }
671     return 0;
672 }
673
674 /* XXX query.c, rpminstall.c, verify.c */
675 int rpmdbClose (rpmdb rpmdb)
676 {
677     int dbix;
678
679     for (dbix = rpmdb->db_ndbi; --dbix >= 0; ) {
680         if (rpmdb->_dbi[dbix] == NULL)
681             continue;
682         dbiClose(rpmdb->_dbi[dbix], 0);
683         rpmdb->_dbi[dbix] = NULL;
684     }
685     if (rpmdb->db_errpfx) {
686         free((void *)rpmdb->db_errpfx);
687         rpmdb->db_errpfx = NULL;
688     }
689     if (rpmdb->db_root) {
690         free((void *)rpmdb->db_root);
691         rpmdb->db_root = NULL;
692     }
693     if (rpmdb->db_home) {
694         free((void *)rpmdb->db_home);
695         rpmdb->db_home = NULL;
696     }
697     if (rpmdb->_dbi) {
698         free((void *)rpmdb->_dbi);
699         rpmdb->_dbi = NULL;
700     }
701     free(rpmdb);
702     return 0;
703 }
704
705 int rpmdbSync(rpmdb rpmdb)
706 {
707     int dbix;
708
709     for (dbix = 0; dbix < rpmdb->db_ndbi; dbix++) {
710         int xx;
711         if (rpmdb->_dbi[dbix] == NULL)
712             continue;
713         xx = dbiSync(rpmdb->_dbi[dbix], 0);
714     }
715     return 0;
716 }
717
718 static /*@only@*/ rpmdb newRpmdb(/*@kept@*/ const char * root,
719                 /*@kept@*/ const char * home,
720                 int mode, int perms, int flags)
721 {
722     rpmdb rpmdb = xcalloc(sizeof(*rpmdb), 1);
723     const char * epfx = _DB_ERRPFX;
724     static int _initialized = 0;
725
726     if (!_initialized) {
727         _filterDbDups = rpmExpandNumeric("%{_filterdbdups}");
728         _initialized = 1;
729     }
730
731     *rpmdb = dbTemplate;        /* structure assignment */
732
733     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
734
735     if (mode >= 0)      rpmdb->db_mode = mode;
736     if (perms >= 0)     rpmdb->db_perms = perms;
737     if (flags >= 0)     rpmdb->db_flags = flags;
738
739     rpmdb->db_root = rpmGetPath( (root && *root ? root : _DB_ROOT), NULL);
740     rpmdb->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL);
741     if (!(rpmdb->db_home && rpmdb->db_home[0] != '%')) {
742         rpmError(RPMERR_DBOPEN, _("no dbpath has been set\n"));
743         rpmdbClose(rpmdb);
744         return NULL;
745     }
746     rpmdb->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
747     rpmdb->db_remove_env = 0;
748     rpmdb->db_filter_dups = _filterDbDups;
749     rpmdb->db_ndbi = dbiTagsMax;
750     rpmdb->_dbi = xcalloc(rpmdb->db_ndbi, sizeof(*rpmdb->_dbi));
751     return rpmdb;
752 }
753
754 static int openDatabase(const char * prefix, const char * dbpath, int _dbapi,
755         /*@out@*/ rpmdb *dbp, int mode, int perms, int flags)
756 {
757     rpmdb rpmdb;
758     int rc;
759     static int _initialized = 0;
760     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
761     int minimal = flags & RPMDB_FLAG_MINIMAL;
762
763     if (!_initialized || dbiTagsMax == 0) {
764         dbiTagsInit();
765         _initialized++;
766     }
767
768     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
769     if (_dbapi < -1 || _dbapi > 3)
770         _dbapi = -1;
771     if (_dbapi == 0)
772         _dbapi = 1;
773
774     if (dbp)
775         *dbp = NULL;
776     if (mode & O_WRONLY) 
777         return 1;
778
779     rpmdb = newRpmdb(prefix, dbpath, mode, perms, flags);
780     rpmdb->db_api = _dbapi;
781
782     {   int dbix;
783
784         rc = 0;
785         for (dbix = 0; rc == 0 && dbix < dbiTagsMax; dbix++) {
786             dbiIndex dbi;
787             int rpmtag;
788
789             /* Filter out temporary databases */
790             switch ((rpmtag = dbiTags[dbix])) {
791             case RPMDBI_AVAILABLE:
792             case RPMDBI_ADDED:
793             case RPMDBI_REMOVED:
794             case RPMDBI_DEPENDS:
795                 continue;
796                 /*@notreached@*/ break;
797             default:
798                 break;
799             }
800
801             dbi = dbiOpen(rpmdb, rpmtag, 0);
802
803             switch (rpmtag) {
804             case RPMDBI_PACKAGES:
805                 if (dbi == NULL) rc |= 1;
806                 /* XXX open only Packages, indices created on the fly. */
807 #if 0
808                 if (rpmdb->db_api == 3)
809 #endif
810                     goto exit;
811                 /*@notreached@*/ break;
812             case RPMTAG_NAME:
813                 if (dbi == NULL) rc |= 1;
814                 if (minimal)
815                     goto exit;
816                 break;
817             case RPMTAG_BASENAMES:
818             {   void * keyp = NULL;
819                 DBC * dbcursor;
820                 int xx;
821
822     /* We used to store the fileindexes as complete paths, rather then
823        plain basenames. Let's see which version we are... */
824     /*
825      * XXX FIXME: db->fileindex can be NULL under pathological (e.g. mixed
826      * XXX db1/db2 linkage) conditions.
827      */
828                 if (justCheck)
829                     break;
830                 dbcursor = NULL;
831                 xx = dbiCopen(dbi, &dbcursor, 0);
832                 xx = dbiGet(dbi, dbcursor, &keyp, NULL, NULL, NULL, 0);
833                 if (xx == 0) {
834                     const char * akey = keyp;
835                     if (strchr(akey, '/')) {
836                         rpmError(RPMERR_OLDDB, _("old format database is present; "
837                                 "use --rebuilddb to generate a new format database\n"));
838                         rc |= 1;
839                     }
840                 }
841                 xx = dbiCclose(dbi, dbcursor, 0);
842                 dbcursor = NULL;
843             }   break;
844             default:
845                 break;
846             }
847         }
848     }
849
850 exit:
851     if (rc || justCheck || dbp == NULL)
852         rpmdbClose(rpmdb);
853     else
854         *dbp = rpmdb;
855
856     return rc;
857 }
858
859 /* XXX python/rpmmodule.c */
860 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
861 {
862     int _dbapi = rpmExpandNumeric("%{_dbapi}");
863     return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
864 }
865
866 int rpmdbInit (const char * prefix, int perms)
867 {
868     rpmdb rpmdb = NULL;
869     int _dbapi = rpmExpandNumeric("%{_dbapi}");
870     int rc;
871
872     rc = openDatabase(prefix, NULL, _dbapi, &rpmdb, (O_CREAT | O_RDWR),
873                 perms, RPMDB_FLAG_JUSTCHECK);
874     if (rpmdb) {
875         rpmdbOpenAll(rpmdb);
876         rpmdbClose(rpmdb);
877         rpmdb = NULL;
878     }
879     return rc;
880 }
881
882 static int rpmdbFindByFile(rpmdb rpmdb, const char * filespec,
883                         /*@out@*/ dbiIndexSet * matches)
884 {
885     const char * dirName;
886     const char * baseName;
887     fingerPrintCache fpc;
888     fingerPrint fp1;
889     dbiIndex dbi = NULL;
890     DBC * dbcursor;
891     dbiIndexSet allMatches = NULL;
892     dbiIndexItem rec = NULL;
893     int i;
894     int rc;
895     int xx;
896
897     *matches = NULL;
898     if ((baseName = strrchr(filespec, '/')) != NULL) {
899         char * t;
900         size_t len;
901
902         len = baseName - filespec + 1;
903         t = strncpy(alloca(len + 1), filespec, len);
904         t[len] = '\0';
905         dirName = t;
906         baseName++;
907     } else {
908         dirName = "";
909         baseName = filespec;
910     }
911
912     fpc = fpCacheCreate(20);
913     fp1 = fpLookup(fpc, dirName, baseName, 1);
914
915     dbi = dbiOpen(rpmdb, RPMTAG_BASENAMES, 0);
916     dbcursor = NULL;
917     xx = dbiCopen(dbi, &dbcursor, 0);
918     rc = dbiSearch(dbi, dbcursor, baseName, strlen(baseName), &allMatches);
919     xx = dbiCclose(dbi, dbcursor, 0);
920     dbcursor = NULL;
921     if (rc) {
922         dbiFreeIndexSet(allMatches);
923         allMatches = NULL;
924         fpCacheFree(fpc);
925         return rc;
926     }
927
928     *matches = xcalloc(1, sizeof(**matches));
929     rec = dbiIndexNewItem(0, 0);
930     i = 0;
931     while (i < allMatches->count) {
932         const char ** baseNames, ** dirNames;
933         int_32 * dirIndexes;
934         unsigned int offset = dbiIndexRecordOffset(allMatches, i);
935         unsigned int prevoff;
936         Header h;
937
938         {   rpmdbMatchIterator mi;
939             mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &offset, sizeof(offset));
940             h = rpmdbNextIterator(mi);
941             if (h)
942                 h = headerLink(h);
943             rpmdbFreeIterator(mi);
944         }
945
946         if (h == NULL) {
947             i++;
948             continue;
949         }
950
951         headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
952                                 (const void **) &baseNames, NULL);
953         headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, 
954                                 (const void **) &dirNames, NULL);
955         headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL, 
956                                 (const void **) &dirIndexes, NULL);
957
958         do {
959             fingerPrint fp2;
960             int num = dbiIndexRecordFileNumber(allMatches, i);
961
962             fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
963             if (FP_EQUAL(fp1, fp2)) {
964                 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
965                 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
966                 dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
967             }
968
969             prevoff = offset;
970             i++;
971             offset = dbiIndexRecordOffset(allMatches, i);
972         } while (i < allMatches->count && 
973                 (i == 0 || offset == prevoff));
974
975         free(baseNames);
976         free(dirNames);
977         headerFree(h);
978     }
979
980     if (rec) {
981         free(rec);
982         rec = NULL;
983     }
984     if (allMatches) {
985         dbiFreeIndexSet(allMatches);
986         allMatches = NULL;
987     }
988
989     fpCacheFree(fpc);
990
991     if ((*matches)->count == 0) {
992         dbiFreeIndexSet(*matches);
993         *matches = NULL; 
994         return 1;
995     }
996
997     return 0;
998 }
999
1000 /* XXX python/upgrade.c, install.c, uninstall.c */
1001 int rpmdbCountPackages(rpmdb rpmdb, const char * name)
1002 {
1003     dbiIndex dbi;
1004     dbiIndexSet matches = NULL;
1005     int rc = -1;
1006     int xx;
1007
1008     dbi = dbiOpen(rpmdb, RPMTAG_NAME, 0);
1009     if (dbi) {
1010         DBC * dbcursor = NULL;
1011         xx = dbiCopen(dbi, &dbcursor, 0);
1012         rc = dbiSearch(dbi, dbcursor, name, strlen(name), &matches);
1013         xx = dbiCclose(dbi, dbcursor, 0);
1014         dbcursor = NULL;
1015     }
1016
1017     if (rc == 0)        /* success */
1018         rc = dbiIndexSetCount(matches);
1019     else if (rc > 0)    /* error */
1020         rpmError(RPMERR_DBCORRUPT, _("error(%d) counting packages\n"), rc);
1021     else                /* not found */
1022         rc = 0;
1023
1024     if (matches)
1025         dbiFreeIndexSet(matches);
1026
1027     return rc;
1028 }
1029
1030 /* XXX transaction.c */
1031 /* 0 found matches */
1032 /* 1 no matches */
1033 /* 2 error */
1034 static int dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
1035         const char * name, const char * version, const char * release,
1036         /*@out@*/ dbiIndexSet * matches)
1037 {
1038     int gotMatches;
1039     int rc;
1040     int i;
1041
1042     rc = dbiSearch(dbi, dbcursor, name, strlen(name), matches);
1043
1044     if (rc != 0) {
1045         rc = ((rc == -1) ? 2 : 1);
1046         goto exit;
1047     }
1048
1049     if (!version && !release) {
1050         rc = 0;
1051         goto exit;
1052     }
1053
1054     gotMatches = 0;
1055
1056     /* make sure the version and releases match */
1057     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
1058         unsigned int recoff = dbiIndexRecordOffset(*matches, i);
1059         int goodRelease, goodVersion;
1060         const char * pkgVersion;
1061         const char * pkgRelease;
1062         Header h;
1063
1064         if (recoff == 0)
1065             continue;
1066
1067     {   rpmdbMatchIterator mi;
1068         mi = rpmdbInitIterator(dbi->dbi_rpmdb, RPMDBI_PACKAGES, &recoff, sizeof(recoff));
1069         h = rpmdbNextIterator(mi);
1070         if (h)
1071             h = headerLink(h);
1072         rpmdbFreeIterator(mi);
1073     }
1074
1075         if (h == NULL) {
1076             rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
1077                 "findMatches", recoff);
1078             rc = 2;
1079             goto exit;
1080         }
1081
1082         headerNVR(h, NULL, &pkgVersion, &pkgRelease);
1083             
1084         goodRelease = goodVersion = 1;
1085
1086         if (release && strcmp(release, pkgRelease)) goodRelease = 0;
1087         if (version && strcmp(version, pkgVersion)) goodVersion = 0;
1088
1089         if (goodRelease && goodVersion) {
1090             /* structure assignment */
1091             (*matches)->recs[gotMatches++] = (*matches)->recs[i];
1092         } else 
1093             (*matches)->recs[i].hdrNum = 0;
1094
1095         headerFree(h);
1096     }
1097
1098     if (gotMatches) {
1099         (*matches)->count = gotMatches;
1100         rc = 0;
1101     } else {
1102         rc = 1;
1103     }
1104
1105 exit:
1106     if (rc && matches && *matches) {
1107         dbiFreeIndexSet(*matches);
1108         *matches = NULL;
1109     }
1110     return rc;
1111 }
1112
1113 /**
1114  * Lookup by name, name-version, and finally by name-version-release.
1115  * @param dbi           index database handle (always RPMDBI_PACKAGES)
1116  * @param dbcursor      index database cursor
1117  * @param arg
1118  * @param matches
1119  * @return              0 on success, 1 on no mtches, 2 on error
1120  */
1121 static int dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, const char * arg, dbiIndexSet * matches)
1122 {
1123     char * localarg, * chptr;
1124     char * release;
1125     int rc;
1126  
1127     if (!strlen(arg)) return 1;
1128
1129     /* did they give us just a name? */
1130     rc = dbiFindMatches(dbi, dbcursor, arg, NULL, NULL, matches);
1131     if (rc != 1) return rc;
1132     if (*matches) {
1133         dbiFreeIndexSet(*matches);
1134         *matches = NULL;
1135     }
1136
1137     /* maybe a name and a release */
1138     localarg = alloca(strlen(arg) + 1);
1139     strcpy(localarg, arg);
1140
1141     chptr = (localarg + strlen(localarg)) - 1;
1142     while (chptr > localarg && *chptr != '-') chptr--;
1143     if (chptr == localarg) return 1;
1144
1145     *chptr = '\0';
1146     rc = dbiFindMatches(dbi, dbcursor, localarg, chptr + 1, NULL, matches);
1147     if (rc != 1) return rc;
1148     if (*matches) dbiFreeIndexSet(*matches);
1149     
1150     /* how about name-version-release? */
1151
1152     release = chptr + 1;
1153     while (chptr > localarg && *chptr != '-') chptr--;
1154     if (chptr == localarg) return 1;
1155
1156     *chptr = '\0';
1157     return dbiFindMatches(dbi, dbcursor, localarg, chptr + 1, release, matches);
1158 }
1159
1160 /**
1161  * Rewrite a header in the database.
1162  *   Note: this is called from a markReplacedFiles iteration, and *must*
1163  *   preserve the "join key" (i.e. offset) for the header.
1164  * @param dbi           index database handle (always RPMDBI_PACKAGES)
1165  * @param dbcursor      index database cursor
1166  * @param offset        join key
1167  * @param h             rpm header
1168  * @return              0 on success
1169  */
1170 static int dbiUpdateRecord(dbiIndex dbi, DBC * dbcursor, int offset, Header h)
1171 {
1172     sigset_t signalMask;
1173     void * uh;
1174     size_t uhlen;
1175     int rc;
1176     int xx;
1177
1178     if (_noDirTokens)
1179         expandFilelist(h);
1180
1181     uhlen = headerSizeof(h, HEADER_MAGIC_NO);
1182     uh = headerUnload(h);
1183     blockSignals(dbi->dbi_rpmdb, &signalMask);
1184     rc = dbiPut(dbi, dbcursor, &offset, sizeof(offset), uh, uhlen, 0);
1185     xx = dbiSync(dbi, 0);
1186     unblockSignals(dbi->dbi_rpmdb, &signalMask);
1187     free(uh);
1188
1189     return rc;
1190 }
1191
1192 struct _rpmdbMatchIterator {
1193 /*@only@*/ const void * mi_keyp;
1194     size_t              mi_keylen;
1195 /*@kept@*/ rpmdb        mi_rpmdb;
1196     int                 mi_rpmtag;
1197     dbiIndexSet         mi_set;
1198     DBC *               mi_dbc;
1199     int                 mi_setx;
1200     Header              mi_h;
1201     int                 mi_sorted;
1202     int                 mi_modified;
1203     unsigned int        mi_prevoffset;
1204     unsigned int        mi_offset;
1205     unsigned int        mi_filenum;
1206     unsigned int        mi_fpnum;
1207     unsigned int        mi_dbnum;
1208 /*@only@*/ const char *mi_version;
1209 /*@only@*/ const char *mi_release;
1210 };
1211
1212 void rpmdbFreeIterator(rpmdbMatchIterator mi)
1213 {
1214     dbiIndex dbi = NULL;
1215     int xx;
1216
1217     if (mi == NULL)
1218         return;
1219
1220     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
1221     if (mi->mi_h) {
1222         if (mi->mi_modified && mi->mi_prevoffset) {
1223             DBC * dbcursor = NULL;
1224             xx = dbiCopen(dbi, &dbcursor, 0);
1225             dbiUpdateRecord(dbi, dbcursor, mi->mi_prevoffset, mi->mi_h);
1226             xx = dbiCclose(dbi, dbcursor, 0);
1227             dbcursor = NULL;
1228         }
1229         headerFree(mi->mi_h);
1230         mi->mi_h = NULL;
1231     }
1232     if (dbi->dbi_rmw) {
1233         xx = dbiCclose(dbi, dbi->dbi_rmw, 0);
1234         dbi->dbi_rmw = NULL;
1235     }
1236
1237     if (mi->mi_release) {
1238         free((void *)mi->mi_release);
1239         mi->mi_release = NULL;
1240     }
1241     if (mi->mi_version) {
1242         free((void *)mi->mi_version);
1243         mi->mi_version = NULL;
1244     }
1245     if (mi->mi_dbc) {
1246         xx = dbiCclose(dbi, mi->mi_dbc, 1);
1247         mi->mi_dbc = NULL;
1248     }
1249     if (mi->mi_set) {
1250         dbiFreeIndexSet(mi->mi_set);
1251         mi->mi_set = NULL;
1252     }
1253     if (mi->mi_keyp) {
1254         free((void *)mi->mi_keyp);
1255         mi->mi_keyp = NULL;
1256     }
1257     free(mi);
1258 }
1259
1260 rpmdb rpmdbGetIteratorRpmDB(rpmdbMatchIterator mi) {
1261     if (mi == NULL)
1262         return 0;
1263     return mi->mi_rpmdb;
1264 }
1265
1266 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
1267     if (mi == NULL)
1268         return 0;
1269     return mi->mi_offset;
1270 }
1271
1272 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
1273     if (mi == NULL)
1274         return 0;
1275     return mi->mi_filenum;
1276 }
1277
1278 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
1279     if (!(mi && mi->mi_set))
1280         return 0;       /* XXX W2DO? */
1281     return mi->mi_set->count;
1282 }
1283
1284 void rpmdbSetIteratorRelease(rpmdbMatchIterator mi, const char * release) {
1285     if (mi == NULL)
1286         return;
1287     if (mi->mi_release) {
1288         free((void *)mi->mi_release);
1289         mi->mi_release = NULL;
1290     }
1291     mi->mi_release = (release ? xstrdup(release) : NULL);
1292 }
1293
1294 void rpmdbSetIteratorVersion(rpmdbMatchIterator mi, const char * version) {
1295     if (mi == NULL)
1296         return;
1297     if (mi->mi_version) {
1298         free((void *)mi->mi_version);
1299         mi->mi_version = NULL;
1300     }
1301     mi->mi_version = (version ? xstrdup(version) : NULL);
1302 }
1303
1304 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified) {
1305     int rc;
1306     if (mi == NULL)
1307         return 0;
1308     rc = mi->mi_modified;
1309     mi->mi_modified = modified;
1310     return rc;
1311 }
1312
1313 Header XrpmdbNextIterator(rpmdbMatchIterator mi, const char * f, unsigned l)
1314 {
1315     dbiIndex dbi;
1316     void * uh = NULL;
1317     size_t uhlen = 0;
1318     void * keyp;
1319     size_t keylen;
1320     int rc;
1321     int xx;
1322
1323     if (mi == NULL)
1324         return NULL;
1325
1326     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
1327     if (dbi == NULL)
1328         return NULL;
1329     /* XXX cursors need to be per-iterator, not per-dbi. Get a cursor now. */
1330     if (mi->mi_dbc == NULL) {
1331         xx = XdbiCopen(dbi, &mi->mi_dbc, 1, f, l);
1332     }
1333     dbi->dbi_lastoffset = mi->mi_prevoffset;
1334
1335 top:
1336     /* XXX skip over instances with 0 join key */
1337     do {
1338         if (mi->mi_set) {
1339             if (!(mi->mi_setx < mi->mi_set->count))
1340                 return NULL;
1341             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
1342             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
1343             keyp = &mi->mi_offset;
1344             keylen = sizeof(mi->mi_offset);
1345         } else {
1346             keyp = (void *)mi->mi_keyp;         /* XXX FIXME const */
1347             keylen = mi->mi_keylen;
1348
1349             rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, 0);
1350
1351             /*
1352              * If we got the next key, save the header instance number.
1353              * For db1 Packages (db1->dbi_lastoffset != 0), always copy.
1354              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
1355              * largest header instance in the database, and should be
1356              * skipped.
1357              */
1358             if (rc == 0 && keyp && (dbi->dbi_lastoffset || mi->mi_setx))
1359                 memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
1360
1361             /* Terminate on error or end of keys */
1362             if (rc || (mi->mi_setx && mi->mi_offset == 0))
1363                 return NULL;
1364         }
1365         mi->mi_setx++;
1366     } while (mi->mi_offset == 0);
1367
1368     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
1369         goto exit;
1370
1371     /* Retrieve next header */
1372     if (uh == NULL) {
1373         rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, 0);
1374         if (rc)
1375             return NULL;
1376     }
1377
1378     /* Free current header */
1379     if (mi->mi_h) {
1380         if (mi->mi_modified && mi->mi_prevoffset)
1381             dbiUpdateRecord(dbi, mi->mi_dbc, mi->mi_prevoffset, mi->mi_h);
1382         headerFree(mi->mi_h);
1383         mi->mi_h = NULL;
1384     }
1385
1386     mi->mi_h = headerCopyLoad(uh);
1387
1388     if (mi->mi_release) {
1389         const char *release;
1390         headerNVR(mi->mi_h, NULL, NULL, &release);
1391         if (strcmp(mi->mi_release, release))
1392             goto top;
1393     }
1394
1395     if (mi->mi_version) {
1396         const char *version;
1397         headerNVR(mi->mi_h, NULL, &version, NULL);
1398         if (strcmp(mi->mi_version, version))
1399             goto top;
1400     }
1401
1402     mi->mi_prevoffset = mi->mi_offset;
1403     mi->mi_modified = 0;
1404
1405 exit:
1406 #ifdef  NOTNOW
1407     if (mi->mi_h) {
1408         const char *n, *v, *r;
1409         headerNVR(mi->mi_h, &n, &v, &r);
1410         rpmMessage(RPMMESS_DEBUG, "%s-%s-%s at 0x%x, h %p\n", n, v, r,
1411                 mi->mi_offset, mi->mi_h);
1412     }
1413 #endif
1414     return mi->mi_h;
1415 }
1416
1417 static void rpmdbSortIterator(rpmdbMatchIterator mi) {
1418     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
1419         qsort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs),
1420                 hdrNumCmp);
1421         mi->mi_sorted = 1;
1422     }
1423 }
1424
1425 static int rpmdbGrowIterator(rpmdbMatchIterator mi,
1426         const void * keyp, size_t keylen, int fpNum)
1427 {
1428     dbiIndex dbi = NULL;
1429     DBC * dbcursor = NULL;
1430     dbiIndexSet set = NULL;
1431     int rc;
1432     int xx;
1433
1434     if (!(mi && keyp))
1435         return 1;
1436
1437     dbi = dbiOpen(mi->mi_rpmdb, mi->mi_rpmtag, 0);
1438     if (dbi == NULL)
1439         return 1;
1440
1441     if (keylen == 0)
1442         keylen = strlen(keyp);
1443
1444     xx = dbiCopen(dbi, &dbcursor, 0);
1445     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
1446     xx = dbiCclose(dbi, dbcursor, 0);
1447     dbcursor = NULL;
1448
1449     if (rc == 0) {      /* success */
1450         int i;
1451         for (i = 0; i < set->count; i++)
1452             set->recs[i].fpNum = fpNum;
1453
1454         if (mi->mi_set == NULL) {
1455             mi->mi_set = set;
1456             set = NULL;
1457         } else {
1458             mi->mi_set->recs = xrealloc(mi->mi_set->recs,
1459                 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
1460             memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
1461                 set->count * sizeof(*(mi->mi_set->recs)));
1462             mi->mi_set->count += set->count;
1463         }
1464     }
1465
1466     if (set)
1467         dbiFreeIndexSet(set);
1468     return rc;
1469 }
1470
1471 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
1472         int nHdrNums, int sorted)
1473 {
1474     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
1475         return 1;
1476
1477     if (mi->mi_set)
1478         dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
1479     return 0;
1480 }
1481
1482 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
1483 {
1484     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
1485         return 1;
1486
1487     if (mi->mi_set == NULL)
1488         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
1489     dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
1490     return 0;
1491 }
1492
1493 rpmdbMatchIterator rpmdbInitIterator(rpmdb rpmdb, int rpmtag,
1494         const void * keyp, size_t keylen)
1495 {
1496     rpmdbMatchIterator mi = NULL;
1497     dbiIndexSet set = NULL;
1498     dbiIndex dbi;
1499     const void * mi_keyp = NULL;
1500     int isLabel = 0;
1501
1502     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
1503     switch (rpmtag) {
1504     case RPMDBI_LABEL:
1505         rpmtag = RPMTAG_NAME;
1506         isLabel = 1;
1507         break;
1508     }
1509
1510     dbi = dbiOpen(rpmdb, rpmtag, 0);
1511     if (dbi == NULL)
1512         return NULL;
1513
1514 #if 0
1515     assert(dbi->dbi_rmw == NULL);       /* db3: avoid "lost" cursors */
1516     assert(dbi->dbi_lastoffset == 0);   /* db0: avoid "lost" cursors */
1517 #else
1518 if (dbi->dbi_rmw)
1519 fprintf(stderr, "*** RMW %s %p\n", tagName(rpmtag), dbi->dbi_rmw);
1520 #endif
1521
1522     dbi->dbi_lastoffset = 0;            /* db0: rewind to beginning */
1523
1524     if (rpmtag != RPMDBI_PACKAGES && keyp) {
1525         DBC * dbcursor = NULL;
1526         int rc;
1527         int xx;
1528
1529         if (isLabel) {
1530             /* XXX HACK to get rpmdbFindByLabel out of the API */
1531             xx = dbiCopen(dbi, &dbcursor, 0);
1532             rc = dbiFindByLabel(dbi, dbcursor, keyp, &set);
1533             xx = dbiCclose(dbi, dbcursor, 0);
1534             dbcursor = NULL;
1535         } else if (rpmtag == RPMTAG_BASENAMES) {
1536             rc = rpmdbFindByFile(rpmdb, keyp, &set);
1537         } else {
1538             xx = dbiCopen(dbi, &dbcursor, 0);
1539             rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
1540             xx = dbiCclose(dbi, dbcursor, 0);
1541             dbcursor = NULL;
1542         }
1543         if (rc) {       /* error/not found */
1544             if (set)
1545                 dbiFreeIndexSet(set);
1546             return NULL;
1547         }
1548     }
1549
1550     if (keyp) {
1551         char * k;
1552
1553         if (rpmtag != RPMDBI_PACKAGES && keylen == 0)
1554             keylen = strlen(keyp);
1555         k = xmalloc(keylen + 1);
1556         memcpy(k, keyp, keylen);
1557         k[keylen] = '\0';       /* XXX for strings */
1558         mi_keyp = k;
1559     }
1560
1561     mi = xcalloc(1, sizeof(*mi));
1562     mi->mi_keyp = mi_keyp;
1563     mi->mi_keylen = keylen;
1564
1565     mi->mi_rpmdb = rpmdb;
1566     mi->mi_rpmtag = rpmtag;
1567
1568     mi->mi_dbc = NULL;
1569     mi->mi_set = set;
1570     mi->mi_setx = 0;
1571     mi->mi_h = NULL;
1572     mi->mi_sorted = 0;
1573     mi->mi_modified = 0;
1574     mi->mi_prevoffset = 0;
1575     mi->mi_offset = 0;
1576     mi->mi_filenum = 0;
1577     mi->mi_fpnum = 0;
1578     mi->mi_dbnum = 0;
1579     mi->mi_version = NULL;
1580     mi->mi_release = NULL;
1581     return mi;
1582 }
1583
1584 /**
1585  * Remove entry from database index.
1586  * @param dbi           index database handle
1587  * @param dbcursor      index database cursor
1588  * @param keyp          search key
1589  * @param keylen        search key length
1590  * @param rec           record to remove
1591  * @return              0 on success
1592  */
1593 static INLINE int removeIndexEntry(dbiIndex dbi, DBC * dbcursor,
1594         const void * keyp, size_t keylen, dbiIndexItem rec)
1595 {
1596     dbiIndexSet set = NULL;
1597     int rc;
1598     
1599     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
1600
1601     if (rc < 0)                 /* not found */
1602         rc = 0;
1603     else if (rc > 0)            /* error */
1604         rc = 1;         /* error message already generated from dbindex.c */
1605     else {                      /* success */
1606         if (!dbiPruneSet(set, rec, 1, sizeof(*rec), 1) &&
1607             dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set))
1608             rc = 1;
1609     }
1610
1611     if (set) {
1612         dbiFreeIndexSet(set);
1613         set = NULL;
1614     }
1615
1616     return rc;
1617 }
1618
1619 /* XXX install.c uninstall.c */
1620 int rpmdbRemove(rpmdb rpmdb, int rid, unsigned int hdrNum)
1621 {
1622     Header h;
1623     sigset_t signalMask;
1624
1625     {   rpmdbMatchIterator mi;
1626         mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
1627         h = rpmdbNextIterator(mi);
1628         if (h)
1629             h = headerLink(h);
1630         rpmdbFreeIterator(mi);
1631     }
1632
1633     if (h == NULL) {
1634         rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
1635               "rpmdbRemove", hdrNum);
1636         return 1;
1637     }
1638
1639     /* Add remove transaction id to header. */
1640     if (rid > 0) {
1641         int_32 tid = rid;
1642         headerAddEntry(h, RPMTAG_REMOVETID, RPM_INT32_TYPE, &tid, 1);
1643     }
1644
1645     {   const char *n, *v, *r;
1646         headerNVR(h, &n, &v, &r);
1647         rpmMessage(RPMMESS_DEBUG, "  --- %10d %s-%s-%s\n", hdrNum, n, v, r);
1648     }
1649
1650     blockSignals(rpmdb, &signalMask);
1651
1652     {   int dbix;
1653         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
1654
1655         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
1656             dbiIndex dbi;
1657             DBC * dbcursor = NULL;
1658             const char *av[1];
1659             const char ** rpmvals = NULL;
1660             int rpmtype = 0;
1661             int rpmcnt = 0;
1662             int rpmtag;
1663             int xx;
1664             int i;
1665
1666             dbi = NULL;
1667             rpmtag = dbiTags[dbix];
1668
1669             switch (rpmtag) {
1670             /* Filter out temporary databases */
1671             case RPMDBI_AVAILABLE:
1672             case RPMDBI_ADDED:
1673             case RPMDBI_REMOVED:
1674             case RPMDBI_DEPENDS:
1675                 continue;
1676                 /*@notreached@*/ break;
1677             case RPMDBI_PACKAGES:
1678                 dbi = dbiOpen(rpmdb, rpmtag, 0);
1679                 xx = dbiCopen(dbi, &dbcursor, 0);
1680                 xx = dbiDel(dbi, dbcursor, &hdrNum, sizeof(hdrNum), 0);
1681                 xx = dbiCclose(dbi, dbcursor, 0);
1682                 dbcursor = NULL;
1683                 /* XXX HACK sync is on the bt with multiple db access */
1684                 if (!dbi->dbi_no_dbsync)
1685                     xx = dbiSync(dbi, 0);
1686                 continue;
1687                 /*@notreached@*/ break;
1688             }
1689         
1690             if (!headerGetEntry(h, rpmtag, &rpmtype,
1691                         (void **) &rpmvals, &rpmcnt))
1692                 continue;
1693
1694             dbi = dbiOpen(rpmdb, rpmtag, 0);
1695             xx = dbiCopen(dbi, &dbcursor, 0);
1696
1697             if (rpmtype == RPM_STRING_TYPE) {
1698
1699                 rpmMessage(RPMMESS_DEBUG, _("removing \"%s\" from %s index.\n"), 
1700                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
1701
1702                 /* XXX force uniform headerGetEntry return */
1703                 av[0] = (const char *) rpmvals;
1704                 rpmvals = av;
1705                 rpmcnt = 1;
1706             } else {
1707
1708                 rpmMessage(RPMMESS_DEBUG, _("removing %d entries from %s index.\n"), 
1709                         rpmcnt, tagName(dbi->dbi_rpmtag));
1710
1711             }
1712
1713             for (i = 0; i < rpmcnt; i++) {
1714                 const void * valp;
1715                 size_t vallen;
1716
1717                 /*
1718                  * This is almost right, but, if there are duplicate tag
1719                  * values, there will be duplicate attempts to remove
1720                  * the header instance. It's easier to just ignore errors
1721                  * than to do things correctly.
1722                  */
1723                 valp = rpmvals[i];
1724                 vallen = strlen(rpmvals[i]);
1725                 xx = removeIndexEntry(dbi, dbcursor, valp, vallen, rec);
1726             }
1727
1728             xx = dbiCclose(dbi, dbcursor, 0);
1729             dbcursor = NULL;
1730
1731             if (!dbi->dbi_no_dbsync)
1732                 xx = dbiSync(dbi, 0);
1733
1734             rpmvals = headerFreeData(rpmvals, rpmtype);
1735             rpmtype = 0;
1736             rpmcnt = 0;
1737         }
1738
1739         if (rec) {
1740             free(rec);
1741             rec = NULL;
1742         }
1743     }
1744
1745     unblockSignals(rpmdb, &signalMask);
1746
1747     headerFree(h);
1748
1749     return 0;
1750 }
1751
1752 /**
1753  * Add entry to database index.
1754  * @param dbi           index database handle
1755  * @param dbcursor      index database cursor
1756  * @param keyp          search key
1757  * @param keylen        search key length
1758  * @param rec           record to add
1759  * @return              0 on success
1760  */
1761 static INLINE int addIndexEntry(dbiIndex dbi, DBC * dbcursor,
1762         const char * keyp, size_t keylen, dbiIndexItem rec)
1763 {
1764     dbiIndexSet set = NULL;
1765     int rc;
1766
1767     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
1768
1769     if (rc > 0) {
1770         rc = 1;                 /* error */
1771     } else {
1772         if (rc < 0) {           /* not found */
1773             rc = 0;
1774             set = xcalloc(1, sizeof(*set));
1775         }
1776         dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
1777         if (dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set))
1778             rc = 1;
1779     }
1780
1781     if (set) {
1782         dbiFreeIndexSet(set);
1783         set = NULL;
1784     }
1785
1786     return 0;
1787 }
1788
1789 /* XXX install.c */
1790 int rpmdbAdd(rpmdb rpmdb, int iid, Header h)
1791 {
1792     sigset_t signalMask;
1793     const char ** baseNames;
1794     int count = 0;
1795     int type;
1796     dbiIndex dbi;
1797     int dbix;
1798     unsigned int hdrNum;
1799     int rc = 0;
1800     int xx;
1801
1802     if (iid > 0) {
1803         int_32 tid = iid;
1804         headerRemoveEntry(h, RPMTAG_REMOVETID);
1805         headerAddEntry(h, RPMTAG_INSTALLTID, RPM_INT32_TYPE, &tid, 1);
1806     }
1807
1808     /*
1809      * If old style filename tags is requested, the basenames need to be
1810      * retrieved early, and the header needs to be converted before
1811      * being written to the package header database.
1812      */
1813
1814     headerGetEntry(h, RPMTAG_BASENAMES, &type, (void **) &baseNames, &count);
1815
1816     if (_noDirTokens)
1817         expandFilelist(h);
1818
1819     blockSignals(rpmdb, &signalMask);
1820
1821     {
1822         unsigned int firstkey = 0;
1823         DBC * dbcursor = NULL;
1824         void * keyp = &firstkey;
1825         size_t keylen = sizeof(firstkey);
1826         void * datap = NULL;
1827         size_t datalen = 0;
1828
1829         dbi = dbiOpen(rpmdb, RPMDBI_PACKAGES, 0);
1830
1831         /* XXX db0: hack to pass sizeof header to fadAlloc */
1832         datap = h;
1833         datalen = headerSizeof(h, HEADER_MAGIC_NO);
1834
1835         xx = dbiCopen(dbi, &dbcursor, 0);
1836
1837         /* Retrieve join key for next header instance. */
1838
1839         rc = dbiGet(dbi, dbcursor, &keyp, &keylen, &datap, &datalen, 0);
1840
1841         hdrNum = 0;
1842         if (rc == 0 && datap)
1843             memcpy(&hdrNum, datap, sizeof(hdrNum));
1844         ++hdrNum;
1845         if (rc == 0 && datap) {
1846             memcpy(datap, &hdrNum, sizeof(hdrNum));
1847         } else {
1848             datap = &hdrNum;
1849             datalen = sizeof(hdrNum);
1850         }
1851
1852         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, 0);
1853         xx = dbiSync(dbi, 0);
1854
1855         xx = dbiCclose(dbi, dbcursor, 0);
1856         dbcursor = NULL;
1857
1858     }
1859
1860     if (rc) {
1861         rpmError(RPMERR_DBCORRUPT,
1862                 _("error(%d) allocating new package instance\n"), rc);
1863         goto exit;
1864     }
1865
1866     /* Now update the indexes */
1867
1868     {   dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
1869
1870         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
1871             DBC * dbcursor = NULL;
1872             const char *av[1];
1873             const char **rpmvals = NULL;
1874             int rpmtype = 0;
1875             int rpmcnt = 0;
1876             int rpmtag;
1877             int_32 * requireFlags;
1878             int i, j;
1879
1880             dbi = NULL;
1881             requireFlags = NULL;
1882             rpmtag = dbiTags[dbix];
1883
1884             switch (rpmtag) {
1885             /* Filter out temporary databases */
1886             case RPMDBI_AVAILABLE:
1887             case RPMDBI_ADDED:
1888             case RPMDBI_REMOVED:
1889             case RPMDBI_DEPENDS:
1890                 continue;
1891                 /*@notreached@*/ break;
1892             case RPMDBI_PACKAGES:
1893                 dbi = dbiOpen(rpmdb, rpmtag, 0);
1894                 xx = dbiCopen(dbi, &dbcursor, 0);
1895                 xx = dbiUpdateRecord(dbi, dbcursor, hdrNum, h);
1896                 xx = dbiCclose(dbi, dbcursor, 0);
1897                 dbcursor = NULL;
1898                 if (!dbi->dbi_no_dbsync)
1899                     xx = dbiSync(dbi, 0);
1900                 {   const char *n, *v, *r;
1901                     headerNVR(h, &n, &v, &r);
1902                     rpmMessage(RPMMESS_DEBUG, "  +++ %10d %s-%s-%s\n", hdrNum, n, v, r);
1903                 }
1904                 continue;
1905                 /*@notreached@*/ break;
1906             /* XXX preserve legacy behavior */
1907             case RPMTAG_BASENAMES:
1908                 rpmtype = type;
1909                 rpmvals = baseNames;
1910                 rpmcnt = count;
1911                 break;
1912             case RPMTAG_REQUIRENAME:
1913                 headerGetEntry(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
1914                 headerGetEntry(h, RPMTAG_REQUIREFLAGS, NULL,
1915                         (void **)&requireFlags, NULL);
1916                 break;
1917             default:
1918                 headerGetEntry(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
1919                 break;
1920             }
1921
1922             if (rpmcnt <= 0) {
1923                 if (rpmtag != RPMTAG_GROUP)
1924                     continue;
1925
1926                 /* XXX preserve legacy behavior */
1927                 rpmtype = RPM_STRING_TYPE;
1928                 rpmvals = (const char **) "Unknown";
1929                 rpmcnt = 1;
1930             }
1931
1932             dbi = dbiOpen(rpmdb, rpmtag, 0);
1933
1934             xx = dbiCopen(dbi, &dbcursor, 0);
1935             if (rpmtype == RPM_STRING_TYPE) {
1936                 rpmMessage(RPMMESS_DEBUG, _("adding \"%s\" to %s index.\n"), 
1937                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
1938
1939                 /* XXX force uniform headerGetEntry return */
1940                 av[0] = (const char *) rpmvals;
1941                 rpmvals = av;
1942                 rpmcnt = 1;
1943             } else {
1944
1945                 rpmMessage(RPMMESS_DEBUG, _("adding %d entries to %s index.\n"), 
1946                         rpmcnt, tagName(dbi->dbi_rpmtag));
1947
1948             }
1949
1950             for (i = 0; i < rpmcnt; i++) {
1951                 const void * valp;
1952                 size_t vallen;
1953
1954                 /*
1955                  * Include the tagNum in all indices. rpm-3.0.4 and earlier
1956                  * included the tagNum only for files.
1957                  */
1958                 switch (dbi->dbi_rpmtag) {
1959                 case RPMTAG_REQUIRENAME:
1960                     /* Filter out install prerequisites. */
1961                     if (requireFlags && isInstallPreReq(requireFlags[i]))
1962                         continue;
1963                     rec->tagNum = i;
1964                     break;
1965                 case RPMTAG_TRIGGERNAME:
1966                     if (i) {    /* don't add duplicates */
1967                         for (j = 0; j < i; j++) {
1968                             if (!strcmp(rpmvals[i], rpmvals[j]))
1969                                 break;
1970                         }
1971                         if (j < i)
1972                             continue;
1973                     }
1974                     rec->tagNum = i;
1975                     break;
1976                 default:
1977                     rec->tagNum = i;
1978                     break;
1979                 }
1980
1981                 valp = rpmvals[i];
1982                 vallen = strlen(rpmvals[i]);
1983                 rc += addIndexEntry(dbi, dbcursor, valp, vallen, rec);
1984             }
1985             xx = dbiCclose(dbi, dbcursor, 0);
1986             dbcursor = NULL;
1987
1988             /* XXX HACK sync is on the bt with multiple db access */
1989             if (!dbi->dbi_no_dbsync)
1990                 xx = dbiSync(dbi, 0);
1991
1992         /*@-observertrans@*/
1993             rpmvals = headerFreeData(rpmvals, rpmtype);
1994         /*@=observertrans@*/
1995             rpmtype = 0;
1996             rpmcnt = 0;
1997         }
1998
1999         if (rec) {
2000             free(rec);
2001             rec = NULL;
2002         }
2003     }
2004
2005 exit:
2006     unblockSignals(rpmdb, &signalMask);
2007
2008     return rc;
2009 }
2010
2011 /* XXX transaction.c */
2012 int rpmdbFindFpList(rpmdb rpmdb, fingerPrint * fpList, dbiIndexSet * matchList, 
2013                     int numItems)
2014 {
2015     rpmdbMatchIterator mi;
2016     fingerPrintCache fpc;
2017     Header h;
2018     int i;
2019
2020     mi = rpmdbInitIterator(rpmdb, RPMTAG_BASENAMES, NULL, 0);
2021
2022     /* Gather all matches from the database */
2023     for (i = 0; i < numItems; i++) {
2024         rpmdbGrowIterator(mi, fpList[i].baseName, 0, i);
2025         matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
2026     }
2027
2028     if ((i = rpmdbGetIteratorCount(mi)) == 0) {
2029         rpmdbFreeIterator(mi);
2030         return 0;
2031     }
2032     fpc = fpCacheCreate(i);
2033
2034     rpmdbSortIterator(mi);
2035     /* iterator is now sorted by (recnum, filenum) */
2036
2037     /* For each set of files matched in a package ... */
2038     while ((h = rpmdbNextIterator(mi)) != NULL) {
2039         const char ** dirNames;
2040         const char ** baseNames;
2041         const char ** fullBaseNames;
2042         int_32 * dirIndexes;
2043         int_32 * fullDirIndexes;
2044         fingerPrint * fps;
2045         dbiIndexItem im;
2046         int start;
2047         int num;
2048         int end;
2049
2050         start = mi->mi_setx - 1;
2051         im = mi->mi_set->recs + start;
2052
2053         /* Find the end of the set of matched files in this package. */
2054         for (end = start + 1; end < mi->mi_set->count; end++) {
2055             if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
2056                 break;
2057         }
2058         num = end - start;
2059
2060         /* Compute fingerprints for this header's matches */
2061         headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
2062                             (const void **) &fullBaseNames, NULL);
2063         headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, 
2064                             (const void **) &dirNames, NULL);
2065         headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL, 
2066                             (const void **) &fullDirIndexes, NULL);
2067
2068         baseNames = xcalloc(num, sizeof(*baseNames));
2069         dirIndexes = xcalloc(num, sizeof(*dirIndexes));
2070         for (i = 0; i < num; i++) {
2071             baseNames[i] = fullBaseNames[im[i].tagNum];
2072             dirIndexes[i] = fullDirIndexes[im[i].tagNum];
2073         }
2074
2075         fps = xcalloc(num, sizeof(*fps));
2076         fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
2077
2078         /* Add db (recnum,filenum) to list for fingerprint matches. */
2079         for (i = 0; i < num; i++, im++) {
2080             if (FP_EQUAL(fps[i], fpList[im->fpNum]))
2081                 dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
2082         }
2083
2084         free(fps);
2085         free(dirNames);
2086         free(fullBaseNames);
2087         free(baseNames);
2088         free(dirIndexes);
2089
2090         mi->mi_setx = end;
2091     }
2092
2093     rpmdbFreeIterator(mi);
2094
2095     fpCacheFree(fpc);
2096
2097     return 0;
2098
2099 }
2100
2101 char * db1basename (int rpmtag) {
2102     char * base = NULL;
2103     switch (rpmtag) {
2104     case RPMDBI_PACKAGES:       base = "packages.rpm";          break;
2105     case RPMTAG_NAME:           base = "nameindex.rpm";         break;
2106     case RPMTAG_BASENAMES:      base = "fileindex.rpm";         break;
2107     case RPMTAG_GROUP:          base = "groupindex.rpm";        break;
2108     case RPMTAG_REQUIRENAME:    base = "requiredby.rpm";        break;
2109     case RPMTAG_PROVIDENAME:    base = "providesindex.rpm";     break;
2110     case RPMTAG_CONFLICTNAME:   base = "conflictsindex.rpm";    break;
2111     case RPMTAG_TRIGGERNAME:    base = "triggerindex.rpm";      break;
2112     default:
2113       { const char * tn = tagName(rpmtag);
2114         base = alloca( strlen(tn) + sizeof(".idx") + 1 );
2115         (void) stpcpy( stpcpy(base, tn), ".idx");
2116       } break;
2117     }
2118     return xstrdup(base);
2119 }
2120
2121 static int rpmdbRemoveDatabase(const char * rootdir,
2122         const char * dbpath, int _dbapi)
2123
2124     int i;
2125     char * filename;
2126     int xx;
2127
2128     i = strlen(dbpath);
2129     if (dbpath[i - 1] != '/') {
2130         filename = alloca(i);
2131         strcpy(filename, dbpath);
2132         filename[i] = '/';
2133         filename[i + 1] = '\0';
2134         dbpath = filename;
2135     }
2136     
2137     filename = alloca(strlen(rootdir) + strlen(dbpath) + 40);
2138
2139     switch (_dbapi) {
2140     case 3:
2141         for (i = 0; i < dbiTagsMax; i++) {
2142             const char * base = tagName(dbiTags[i]);
2143             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
2144             (void)rpmCleanPath(filename);
2145             xx = unlink(filename);
2146         }
2147         for (i = 0; i < 16; i++) {
2148             sprintf(filename, "%s/%s/__db.%03d", rootdir, dbpath, i);
2149             (void)rpmCleanPath(filename);
2150             xx = unlink(filename);
2151         }
2152         break;
2153     case 2:
2154     case 1:
2155     case 0:
2156         for (i = 0; i < dbiTagsMax; i++) {
2157             const char * base = db1basename(dbiTags[i]);
2158             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
2159             (void)rpmCleanPath(filename);
2160             xx = unlink(filename);
2161             free((void *)base);
2162         }
2163         break;
2164     }
2165
2166     sprintf(filename, "%s/%s", rootdir, dbpath);
2167     (void)rpmCleanPath(filename);
2168     xx = rmdir(filename);
2169
2170     return 0;
2171 }
2172
2173 static int rpmdbMoveDatabase(const char * rootdir,
2174         const char * olddbpath, int _olddbapi,
2175         const char * newdbpath, int _newdbapi)
2176 {
2177     int i;
2178     char * ofilename, * nfilename;
2179     int rc = 0;
2180     int xx;
2181  
2182     i = strlen(olddbpath);
2183     if (olddbpath[i - 1] != '/') {
2184         ofilename = alloca(i + 2);
2185         strcpy(ofilename, olddbpath);
2186         ofilename[i] = '/';
2187         ofilename[i + 1] = '\0';
2188         olddbpath = ofilename;
2189     }
2190     
2191     i = strlen(newdbpath);
2192     if (newdbpath[i - 1] != '/') {
2193         nfilename = alloca(i + 2);
2194         strcpy(nfilename, newdbpath);
2195         nfilename[i] = '/';
2196         nfilename[i + 1] = '\0';
2197         newdbpath = nfilename;
2198     }
2199     
2200     ofilename = alloca(strlen(rootdir) + strlen(olddbpath) + 40);
2201     nfilename = alloca(strlen(rootdir) + strlen(newdbpath) + 40);
2202
2203     switch (_olddbapi) {
2204     case 3:
2205         for (i = 0; i < dbiTagsMax; i++) {
2206             const char * base;
2207             int rpmtag;
2208
2209             /* Filter out temporary databases */
2210             switch ((rpmtag = dbiTags[i])) {
2211             case RPMDBI_AVAILABLE:
2212             case RPMDBI_ADDED:
2213             case RPMDBI_REMOVED:
2214             case RPMDBI_DEPENDS:
2215                 continue;
2216                 /*@notreached@*/ break;
2217             default:
2218                 break;
2219             }
2220
2221             base = tagName(rpmtag);
2222             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
2223             (void)rpmCleanPath(ofilename);
2224             if (!rpmfileexists(ofilename))
2225                 continue;
2226             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
2227             (void)rpmCleanPath(nfilename);
2228             if ((xx = Rename(ofilename, nfilename)) != 0)
2229                 rc = 1;
2230         }
2231         for (i = 0; i < 16; i++) {
2232             sprintf(ofilename, "%s/%s/__db.%03d", rootdir, olddbpath, i);
2233             (void)rpmCleanPath(ofilename);
2234             if (!rpmfileexists(ofilename))
2235                 continue;
2236             sprintf(nfilename, "%s/%s/__db.%03d", rootdir, newdbpath, i);
2237             (void)rpmCleanPath(nfilename);
2238             if ((xx = Rename(ofilename, nfilename)) != 0)
2239                 rc = 1;
2240         }
2241         break;
2242     case 2:
2243     case 1:
2244     case 0:
2245         for (i = 0; i < dbiTagsMax; i++) {
2246             const char * base;
2247             int rpmtag;
2248
2249             /* Filter out temporary databases */
2250             switch ((rpmtag = dbiTags[i])) {
2251             case RPMDBI_AVAILABLE:
2252             case RPMDBI_ADDED:
2253             case RPMDBI_REMOVED:
2254             case RPMDBI_DEPENDS:
2255                 continue;
2256                 /*@notreached@*/ break;
2257             default:
2258                 break;
2259             }
2260
2261             base = db1basename(rpmtag);
2262             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
2263             (void)rpmCleanPath(ofilename);
2264             if (!rpmfileexists(ofilename))
2265                 continue;
2266             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
2267             (void)rpmCleanPath(nfilename);
2268             if ((xx = Rename(ofilename, nfilename)) != 0)
2269                 rc = 1;
2270             free((void *)base);
2271         }
2272         break;
2273     }
2274     if (rc || _olddbapi == _newdbapi)
2275         return rc;
2276
2277     rc = rpmdbRemoveDatabase(rootdir, newdbpath, _newdbapi);
2278
2279
2280     /* Remove /etc/rpm/macros.db1 configuration file if db3 rebuilt. */
2281     if (rc == 0 && _newdbapi == 1 && _olddbapi == 3) {
2282         const char * mdb1 = "/etc/rpm/macros.db1";
2283         struct stat st;
2284         if (!stat(mdb1, &st) && S_ISREG(st.st_mode) && !unlink(mdb1))
2285             rpmMessage(RPMMESS_DEBUG,
2286                 _("removing %s after successful db3 rebuild.\n"), mdb1);
2287     }
2288     return rc;
2289 }
2290
2291 int rpmdbRebuild(const char * rootdir)
2292 {
2293     rpmdb olddb;
2294     const char * dbpath = NULL;
2295     const char * rootdbpath = NULL;
2296     rpmdb newdb;
2297     const char * newdbpath = NULL;
2298     const char * newrootdbpath = NULL;
2299     const char * tfn;
2300     int nocleanup = 1;
2301     int failed = 0;
2302     int removedir = 0;
2303     int rc = 0;
2304     int _dbapi;
2305     int _dbapi_rebuild;
2306
2307     _dbapi = rpmExpandNumeric("%{_dbapi}");
2308     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
2309
2310     tfn = rpmGetPath("%{_dbpath}", NULL);
2311     if (!(tfn && tfn[0] != '%')) {
2312         rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
2313         rc = 1;
2314         goto exit;
2315     }
2316     dbpath = rootdbpath = rpmGetPath(rootdir, tfn, NULL);
2317     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
2318         dbpath += strlen(rootdir);
2319     free((void *)tfn);
2320
2321     tfn = rpmGetPath("%{_dbpath_rebuild}", NULL);
2322     if (!(tfn && tfn[0] != '%' && strcmp(tfn, dbpath))) {
2323         char pidbuf[20];
2324         char *t;
2325         sprintf(pidbuf, "rebuilddb.%d", (int) getpid());
2326         t = xmalloc(strlen(dbpath) + strlen(pidbuf) + 1);
2327         (void)stpcpy(stpcpy(t, dbpath), pidbuf);
2328         if (tfn) free((void *)tfn);
2329         tfn = t;
2330         nocleanup = 0;
2331     }
2332     newdbpath = newrootdbpath = rpmGetPath(rootdir, tfn, NULL);
2333     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
2334         newdbpath += strlen(rootdir);
2335     free((void *)tfn);
2336
2337     rpmMessage(RPMMESS_DEBUG, _("rebuilding database %s into %s\n"),
2338         rootdbpath, newrootdbpath);
2339
2340     if (!access(newrootdbpath, F_OK)) {
2341         rpmError(RPMERR_MKDIR, _("temporary database %s already exists\n"),
2342               newrootdbpath);
2343         rc = 1;
2344         goto exit;
2345     }
2346
2347     rpmMessage(RPMMESS_DEBUG, _("creating directory %s\n"), newrootdbpath);
2348     if (Mkdir(newrootdbpath, 0755)) {
2349         rpmError(RPMERR_MKDIR, _("creating directory %s: %s\n"),
2350               newrootdbpath, strerror(errno));
2351         rc = 1;
2352         goto exit;
2353     }
2354     removedir = 1;
2355
2356     rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"),
2357                 _dbapi);
2358     _rebuildinprogress = 1;
2359     if (openDatabase(rootdir, dbpath, _dbapi, &olddb, O_RDONLY, 0644, 
2360                      RPMDB_FLAG_MINIMAL)) {
2361         rc = 1;
2362         goto exit;
2363     }
2364     _dbapi = olddb->db_api;
2365     _rebuildinprogress = 0;
2366
2367     rpmMessage(RPMMESS_DEBUG, _("opening new database with dbapi %d\n"),
2368                 _dbapi_rebuild);
2369     if (openDatabase(rootdir, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
2370         rc = 1;
2371         goto exit;
2372     }
2373     _dbapi_rebuild = newdb->db_api;
2374     
2375     {   Header h = NULL;
2376         rpmdbMatchIterator mi;
2377 #define _RECNUM rpmdbGetIteratorOffset(mi)
2378
2379         /* RPMDBI_PACKAGES */
2380         mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
2381         while ((h = rpmdbNextIterator(mi)) != NULL) {
2382
2383             /* let's sanity check this record a bit, otherwise just skip it */
2384             if (!(headerIsEntry(h, RPMTAG_NAME) &&
2385                 headerIsEntry(h, RPMTAG_VERSION) &&
2386                 headerIsEntry(h, RPMTAG_RELEASE) &&
2387                 headerIsEntry(h, RPMTAG_BUILDTIME)))
2388             {
2389                 rpmError(RPMERR_INTERNAL,
2390                         _("record number %d in database is bad -- skipping.\n"),
2391                         _RECNUM);
2392                 continue;
2393             }
2394
2395             /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
2396             if (_db_filter_dups || newdb->db_filter_dups) {
2397                 const char * name, * version, * release;
2398                 int skip = 0;
2399
2400                 headerNVR(h, &name, &version, &release);
2401
2402                 /*@-shadow@*/
2403                 {   rpmdbMatchIterator mi;
2404                     mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
2405                     rpmdbSetIteratorVersion(mi, version);
2406                     rpmdbSetIteratorRelease(mi, release);
2407                     while (rpmdbNextIterator(mi)) {
2408                         skip = 1;
2409                         break;
2410                     }
2411                     rpmdbFreeIterator(mi);
2412                 }
2413                 /*@=shadow@*/
2414
2415                 if (skip)
2416                     continue;
2417             }
2418
2419             /* Deleted entries are eliminated in legacy headers by copy. */
2420             {   Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
2421                                 ? headerCopy(h) : NULL);
2422                 rc = rpmdbAdd(newdb, -1, (nh ? nh : h));
2423                 if (nh)
2424                     headerFree(nh);
2425             }
2426
2427             if (rc) {
2428                 rpmError(RPMERR_INTERNAL,
2429                         _("cannot add record originally at %d\n"), _RECNUM);
2430                 failed = 1;
2431                 break;
2432             }
2433         }
2434
2435         rpmdbFreeIterator(mi);
2436
2437     }
2438
2439     if (!nocleanup) {
2440         olddb->db_remove_env = 1;
2441         newdb->db_remove_env = 1;
2442     }
2443     rpmdbClose(olddb);
2444     rpmdbClose(newdb);
2445
2446     if (failed) {
2447         rpmMessage(RPMMESS_NORMAL, _("failed to rebuild database: original database "
2448                 "remains in place\n"));
2449
2450         rpmdbRemoveDatabase(rootdir, newdbpath, _dbapi_rebuild);
2451         rc = 1;
2452         goto exit;
2453     } else if (!nocleanup) {
2454         if (rpmdbMoveDatabase(rootdir, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
2455             rpmMessage(RPMMESS_ERROR, _("failed to replace old database with new "
2456                         "database!\n"));
2457             rpmMessage(RPMMESS_ERROR, _("replace files in %s with files from %s "
2458                         "to recover"), dbpath, newdbpath);
2459             rc = 1;
2460             goto exit;
2461         }
2462     }
2463     rc = 0;
2464
2465 exit:
2466     if (removedir && !(rc == 0 && nocleanup)) {
2467         rpmMessage(RPMMESS_DEBUG, _("removing directory %s\n"), newrootdbpath);
2468         if (Rmdir(newrootdbpath))
2469             rpmMessage(RPMMESS_ERROR, _("failed to remove directory %s: %s\n"),
2470                         newrootdbpath, strerror(errno));
2471     }
2472     if (newrootdbpath)  free((void *)newrootdbpath);
2473     if (rootdbpath)     free((void *)rootdbpath);
2474
2475     return rc;
2476 }