7 #define _USE_COPY_LOAD /* XXX don't use DB_DBT_MALLOC (yet) */
11 #ifndef DYING /* XXX already in "system.h" */
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>
30 #include "lib/rpmdb_internal.h"
31 #include "lib/fprint.h"
32 #include "lib/header_internal.h" /* XXX for headerSetInstance() */
37 static int _rebuildinprogress = 0;
40 #define _DBI_PERMS 0644
49 /* XXX should dbitags be per-db instead? */
50 static struct dbiTags_s dbiTags = { NULL, 0, 0 };
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))
60 #define __PBM_BITS(set) ((set)->bits)
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)
67 #define PBM_ALLOC(d) xcalloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
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
75 static inline pbm_set * PBM_REALLOC(pbm_set ** sp, int * odp, int nd)
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;
91 * Return dbi index used for rpm tag.
92 * @param rpmtag rpm header tag
93 * @return dbi index, -1 on error
95 static int dbiTagToDbix(rpmTag rpmtag)
99 if (dbiTags.tags != NULL)
100 for (dbix = 0; dbix < dbiTags.max; dbix++) {
101 if (rpmtag == dbiTags.tags[dbix])
108 * Initialize database (index, tag) tuple from configuration.
110 static void dbiTagsInit(void)
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;
119 if (dbiTags.tags != NULL && dbiTags.max > 0) {
123 dbiTagStr = rpmExpand("%{?_dbi_tags}", NULL);
124 if (!(dbiTagStr && *dbiTagStr)) {
125 dbiTagStr = _free(dbiTagStr);
126 dbiTagStr = xstrdup(_dbiTagStr_default);
129 /* Discard previous values. */
130 dbiTags.tags = _free(dbiTags.tags);
133 /* Always allocate package index */
134 dbiTags.tags = xcalloc(1, sizeof(*dbiTags.tags));
135 dbiTags.tags[dbiTags.max++] = RPMDBI_PACKAGES;
137 for (o = dbiTagStr; o && *o; o = oe) {
138 while (*o && risspace(*o))
142 for (oe = o; oe && *oe; oe++) {
145 if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
150 rpmtag = rpmTagGetValue(o);
151 if (rpmtag == RPMTAG_NOT_FOUND) {
152 rpmlog(RPMLOG_WARNING,
153 _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
156 if (dbiTagToDbix(rpmtag) >= 0)
159 dbiTags.tags = xrealloc(dbiTags.tags, (dbiTags.max + 1) * sizeof(*dbiTags.tags)); /* XXX memory leak */
160 dbiTags.tags[dbiTags.max++] = rpmtag;
163 dbiTagStr = _free(dbiTagStr);
166 static void dbiTagsFree(void)
168 if (--dbiTags.nlink > 0) {
171 dbiTags.tags = _free(dbiTags.tags);
179 extern struct _dbiVec db3vec;
180 #define DB3vec &db3vec
185 #ifdef HAVE_SQLITE3_H
186 extern struct _dbiVec sqlitevec;
187 #define SQLITEvec &sqlitevec
189 #define SQLITEvec NULL
192 static struct _dbiVec * const mydbvecs[] = {
193 DB1vec, DB1vec, DB2vec, DB3vec, SQLITEvec, NULL
196 dbiIndex dbiOpen(rpmdb db, rpmTag rpmtag, unsigned int flags)
200 int _dbapi, _dbapi_rebuild, _dbapi_wanted;
206 dbix = dbiTagToDbix(rpmtag);
207 if (dbix < 0 || dbix >= dbiTags.max)
210 /* Is this index already open ? */
211 /* FIX: db->_dbi may be NULL */
212 if ((dbi = db->_dbi[dbix]) != NULL)
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;
222 _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
223 if (_dbapi_rebuild < 1 || _dbapi_rebuild > 4)
225 /* _dbapi_wanted = (_rebuildinprogress ? -1 : db->db_api); */
226 _dbapi_wanted = (_rebuildinprogress ? _dbapi_rebuild : db->db_api);
228 switch (_dbapi_wanted) {
230 _dbapi = _dbapi_wanted;
231 if (_dbapi < 0 || _dbapi >= 5 || mydbvecs[_dbapi] == NULL) {
232 rpmlog(RPMLOG_ERR, _("dbiOpen: dbapi %d not available\n"), _dbapi);
237 rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
239 static int _printed[32];
240 if (!_printed[dbix & 0x1f]++)
242 _("cannot open %s index using db%d - %s (%d)\n"),
243 rpmTagGetName(rpmtag), _dbapi,
244 (rc > 0 ? strerror(rc) : ""), rc);
250 while (_dbapi-- > 1) {
251 if (mydbvecs[_dbapi] == NULL)
255 rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
260 static int _printed[32];
261 if (!_printed[dbix & 0x1f]++)
262 rpmlog(RPMLOG_ERR, _("cannot open %s index\n"),
263 rpmTagGetName(rpmtag));
267 if (db->db_api == -1 && _dbapi > 0)
272 /* We don't ever _REQUIRE_ conversion... */
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);
281 /* Suggest possible configuration */
282 if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
287 /* Suggest possible configuration */
288 if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
289 rc = (_rebuildinprogress ? 0 : 1);
295 if (dbi != NULL && rc == 0) {
296 db->_dbi[dbix] = dbi;
297 if (rpmtag == RPMDBI_PACKAGES && db->db_bits == NULL) {
299 if (!dbiStat(dbi, DB_FAST_STAT)) {
300 DB_HASH_STAT * hash = (DB_HASH_STAT *)dbi->dbi_stats;
302 db->db_nbits += hash->hash_nkeys;
304 db->db_bits = PBM_ALLOC(db->db_nbits);
312 /* FIX: db->_dbi may be NULL */
316 /* Retrieve (key,data) pair from index database. */
317 int dbiGet(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
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);
328 /* Store (key,data) pair in index database. */
329 int dbiPut(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
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);
341 * Create and initialize item for index database set.
342 * @param hdrNum header instance in db
343 * @param tagNum tag index in header
346 static dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum)
348 dbiIndexItem rec = xcalloc(1, sizeof(*rec));
349 rec->hdrNum = hdrNum;
350 rec->tagNum = tagNum;
359 #define _DBSWAP(_a) \
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; \
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.
372 static inline void dbiGrowSet(dbiIndexSet set, unsigned int nrecs)
374 size_t need = (set->count + nrecs) * sizeof(*(set->recs));
375 size_t alloced = set->alloced ? set->alloced : 1 << 4;
377 while (alloced < need)
380 if (alloced != set->alloced) {
381 set->recs = xrealloc(set->recs, alloced);
382 set->alloced = alloced;
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
393 static int dbt2set(dbiIndex dbi, DBT * data, dbiIndexSet * setp)
395 int _dbbyteswapped = dbiByteSwapped(dbi);
400 if (dbi == NULL || data == NULL || setp == NULL)
403 if ((sdbir = data->data) == NULL) {
408 set = xcalloc(1, sizeof(*set));
409 dbiGrowSet(set, data->size / dbi->dbi_jlen);
410 set->count = data->size / dbi->dbi_jlen;
412 switch (dbi->dbi_jlen) {
414 case 2*sizeof(int32_t):
415 for (i = 0; i < set->count; i++) {
416 union _dbswap hdrNum, tagNum;
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) {
426 set->recs[i].hdrNum = hdrNum.ui;
427 set->recs[i].tagNum = tagNum.ui;
430 case 1*sizeof(int32_t):
431 for (i = 0; i < set->count; i++) {
432 union _dbswap hdrNum;
434 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
435 sdbir += sizeof(hdrNum.ui);
436 if (_dbbyteswapped) {
439 set->recs[i].hdrNum = hdrNum.ui;
440 set->recs[i].tagNum = 0;
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
455 static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set)
457 int _dbbyteswapped = dbiByteSwapped(dbi);
461 if (dbi == NULL || data == NULL || set == NULL)
464 data->size = set->count * (dbi->dbi_jlen);
465 if (data->size == 0) {
469 tdbir = data->data = xmalloc(data->size);
471 switch (dbi->dbi_jlen) {
473 case 2*sizeof(int32_t):
474 for (i = 0; i < set->count; i++) {
475 union _dbswap hdrNum, tagNum;
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) {
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);
491 case 1*sizeof(int32_t):
492 for (i = 0; i < set->count; i++) {
493 union _dbswap hdrNum;
495 memset(&hdrNum, 0, sizeof(hdrNum));
496 hdrNum.ui = set->recs[i].hdrNum;
497 if (_dbbyteswapped) {
500 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
501 tdbir += sizeof(hdrNum.ui);
509 /* XXX assumes hdrNum is first int in dbiIndexItem */
510 static int hdrNumCmp(const void * one, const void * two)
512 const unsigned int * a = one, * b = two;
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)
525 static int dbiAppendSet(dbiIndexSet set, const void * recs,
526 int nrecs, size_t recsize, int sortset)
528 const char * rptr = recs;
529 size_t rlen = (recsize < sizeof(*(set->recs)))
530 ? recsize : sizeof(*(set->recs));
532 if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0)
535 dbiGrowSet(set, nrecs);
536 memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
538 while (nrecs-- > 0) {
539 memcpy(set->recs + set->count, rptr, rlen);
544 if (sortset && set->count > 1)
545 qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
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)
559 static int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
560 size_t recsize, int sorted)
564 unsigned int num = set->count;
565 unsigned int numCopied = 0;
567 assert(set->count > 0);
568 if (nrecs > 1 && !sorted)
569 qsort(recs, nrecs, recsize, hdrNumCmp);
571 for (from = 0; from < num; from++) {
572 if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
577 set->recs[to] = set->recs[from]; /* structure assignment */
581 return (numCopied == num);
584 /* XXX transaction.c */
585 unsigned int dbiIndexSetCount(dbiIndexSet set) {
589 /* XXX transaction.c */
590 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
591 return set->recs[recno].hdrNum;
594 /* XXX transaction.c */
595 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
596 return set->recs[recno].tagNum;
599 /* XXX transaction.c */
600 dbiIndexSet dbiFreeIndexSet(dbiIndexSet set) {
602 set->recs = _free(set->recs);
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 */
619 struct rpmdbMatchIterator_s {
620 rpmdbMatchIterator mi_next;
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) */
640 rpmRC (*mi_hdrchk) (rpmts ts, const void * uh, size_t uc, char ** msg);
644 static rpmdb rpmdbRock;
646 static rpmdbMatchIterator rpmmiRock;
648 int rpmdbCheckTerminate(int terminate)
650 sigset_t newMask, oldMask;
651 static int terminating = 0;
653 if (terminating) return 0;
655 (void) sigfillset(&newMask); /* block all signals */
656 (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
658 if (rpmsqIsCaught(SIGINT) > 0
659 || rpmsqIsCaught(SIGQUIT) > 0
660 || rpmsqIsCaught(SIGHUP) > 0
661 || rpmsqIsCaught(SIGTERM) > 0
662 || rpmsqIsCaught(SIGPIPE) > 0
668 rpmdbMatchIterator mi;
670 while ((mi = rpmmiRock) != NULL) {
671 rpmmiRock = mi->mi_next;
673 mi = rpmdbFreeIterator(mi);
676 while ((db = rpmdbRock) != NULL) {
677 rpmdbRock = db->db_next;
679 (void) rpmdbClose(db);
682 sigprocmask(SIG_SETMASK, &oldMask, NULL);
686 int rpmdbCheckSignals(void)
688 if (rpmdbCheckTerminate(0)) {
689 rpmlog(RPMLOG_DEBUG, "Exiting on signal...\n");
696 * Block all signals, returning previous signal mask.
698 static int blockSignals(sigset_t * oldMask)
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);
713 * Restore signal mask.
715 static int unblockSignals(sigset_t * oldMask)
717 (void) rpmdbCheckSignals();
718 return sigprocmask(SIG_SETMASK, oldMask, NULL);
722 #define _DB_HOME "%{_dbpath}"
723 #define _DB_FULLPATH NULL
726 #define _DB_PERMS 0644
729 #define _DB_ERRPFX "rpmdb"
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
736 static int isTemporaryDB(rpmTag rpmtag)
740 case RPMDBI_AVAILABLE:
752 rpmop rpmdbOp(rpmdb rpmdb, rpmdbOpX opx)
757 op = &rpmdb->db_getops;
760 op = &rpmdb->db_putops;
763 op = &rpmdb->db_delops;
771 const char *rpmdbHome(rpmdb db)
773 const char *dbdir = NULL;
775 dbdir = db->db_chrootDone ? db->db_home : db->db_fullpath;
780 int rpmdbSetChrootDone(rpmdb db, int chrootDone)
784 ochrootDone = db->db_chrootDone;
785 db->db_chrootDone = chrootDone;
790 int rpmdbOpenAll(rpmdb db)
795 if (db == NULL) return -2;
797 if (dbiTags.tags != NULL)
798 for (dbix = 0; dbix < dbiTags.max; dbix++) {
799 if (db->_dbi[dbix] != NULL)
801 /* Filter out temporary databases */
802 if (isTemporaryDB(dbiTags.tags[dbix]))
804 (void) dbiOpen(db, dbiTags.tags[dbix], db->db_flags);
809 int rpmdbCloseDBI(rpmdb db, rpmTag rpmtag)
814 if (db == NULL || db->_dbi == NULL || dbiTags.tags == NULL)
817 for (dbix = 0; dbix < dbiTags.max; dbix++) {
818 if (dbiTags.tags[dbix] != rpmtag)
820 if (db->_dbi[dbix] != NULL) {
822 /* FIX: double indirection. */
823 xx = dbiClose(db->_dbi[dbix], 0);
824 if (xx && rc == 0) rc = xx;
825 db->_dbi[dbix] = NULL;
832 /* XXX query.c, rpminstall.c, verify.c */
833 int rpmdbClose(rpmdb db)
842 (void) rpmdbUnlink(db, RPMDBG_M("rpmdbClose"));
848 for (dbix = db->db_ndbi; --dbix >= 0; ) {
850 if (db->_dbi[dbix] == NULL)
852 xx = dbiClose(db->_dbi[dbix], 0);
853 if (xx && rc == 0) rc = xx;
854 db->_dbi[dbix] = NULL;
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);
864 while ((next = *prev) != NULL && next != db)
865 prev = &next->db_next;
867 *prev = next->db_next;
868 next->db_next = NULL;
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);
884 int rpmdbSync(rpmdb db)
889 if (db == NULL) return 0;
890 for (dbix = 0; dbix < db->db_ndbi; dbix++) {
892 if (db->_dbi[dbix] == NULL)
894 if (db->_dbi[dbix]->dbi_no_dbsync)
896 xx = dbiSync(db->_dbi[dbix], 0);
897 if (xx && rc == 0) rc = xx;
902 /* FIX: dbTemplate structure assignment */
904 rpmdb newRpmdb(const char * root,
906 int mode, int perms, int flags)
908 rpmdb db = xcalloc(sizeof(*db), 1);
909 const char * epfx = _DB_ERRPFX;
910 static int _initialized = 0;
916 *db = dbTemplate; /* structure assignment */
920 if (!(perms & 0600)) perms = 0644; /* XXX sanity */
922 if (mode >= 0) db->db_mode = mode;
923 if (perms >= 0) db->db_perms = perms;
924 if (flags >= 0) db->db_flags = flags;
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);
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));
944 return rpmdbLink(db, RPMDBG_M("rpmdbCreate"));
947 static int openDatabase(const char * prefix,
949 int _dbapi, rpmdb *dbp,
950 int mode, int perms, int flags)
954 int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
955 int minimal = flags & RPMDB_FLAG_MINIMAL;
959 /* Insure that _dbapi has one of -1, 1, 2, or 3 */
960 if (_dbapi < -1 || _dbapi > 4)
967 if ((mode & O_ACCMODE) == O_WRONLY)
970 db = newRpmdb(prefix, dbpath, mode, perms, flags);
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);
985 if (dbiTags.tags != NULL)
986 for (dbix = 0; rc == 0 && dbix < dbiTags.max; dbix++) {
990 /* Filter out temporary databases */
991 if (isTemporaryDB((rpmtag = dbiTags.tags[dbix])))
994 dbi = dbiOpen(db, rpmtag, 0);
1001 case RPMDBI_PACKAGES:
1002 if (dbi == NULL) rc |= 1;
1004 /* XXX open only Packages, indices created on the fly. */
1005 if (db->db_api == 3)
1010 if (dbi == NULL) rc |= 1;
1021 if (rc || justCheck || dbp == NULL)
1022 xx = rpmdbClose(db);
1024 db->db_next = rpmdbRock;
1032 rpmdb rpmdbUnlink(rpmdb db, const char * msg)
1035 fprintf(stderr, "--> db %p -- %d %s\n", db, db->nrefs, msg);
1040 rpmdb rpmdbLink(rpmdb db, const char * msg)
1044 fprintf(stderr, "--> db %p ++ %d %s\n", db, db->nrefs, msg);
1048 /* XXX python/rpmmodule.c */
1049 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
1051 int _dbapi = rpmExpandNumeric("%{_dbapi}");
1052 return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
1055 int rpmdbInit (const char * prefix, int perms)
1058 int _dbapi = rpmExpandNumeric("%{_dbapi}");
1061 rc = openDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR),
1062 perms, RPMDB_FLAG_JUSTCHECK);
1065 xx = rpmdbOpenAll(db);
1066 if (xx && rc == 0) rc = xx;
1067 xx = rpmdbClose(db);
1068 if (xx && rc == 0) rc = xx;
1074 int rpmdbVerify(const char * prefix)
1077 int _dbapi = rpmExpandNumeric("%{_dbapi}");
1080 rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0);
1085 rc = rpmdbOpenAll(db);
1087 for (dbix = db->db_ndbi; --dbix >= 0; ) {
1088 if (db->_dbi[dbix] == NULL)
1090 /* FIX: double indirection. */
1091 xx = dbiVerify(db->_dbi[dbix], 0);
1092 if (xx && rc == 0) rc = xx;
1093 db->_dbi[dbix] = NULL;
1096 /* FIX: db->_dbi[] may be NULL. */
1097 xx = rpmdbClose(db);
1098 if (xx && rc == 0) rc = xx;
1105 * Find file matches in database.
1106 * @param db rpm database
1111 * @return 0 on success, 1 on not found, -2 on error
1113 static int rpmdbFindByFile(rpmdb db, const char * filespec,
1114 DBT * key, DBT * data, dbiIndexSet * matches)
1116 char * dirName = NULL;
1117 const char * baseName;
1118 fingerPrintCache fpc = NULL;
1120 dbiIndex dbi = NULL;
1122 dbiIndexSet allMatches = NULL;
1123 dbiIndexItem rec = NULL;
1125 int rc = -2; /* assume error */
1129 if (filespec == NULL) return rc; /* nothing alloced yet */
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';
1137 dirName = xstrdup("");
1138 baseName = filespec;
1140 if (baseName == NULL)
1143 dbi = dbiOpen(db, RPMTAG_BASENAMES, 0);
1146 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1148 key->data = (void *) baseName;
1149 key->size = strlen(baseName);
1151 key->size++; /* XXX "/" fixup. */
1153 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1156 _("error(%d) getting \"%s\" records from %s index\n"),
1157 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
1161 (void) dbt2set(dbi, data, &allMatches);
1163 xx = dbiCclose(dbi, dbcursor, 0);
1168 if (rc || allMatches == NULL) goto exit;
1170 *matches = xcalloc(1, sizeof(**matches));
1171 rec = dbiIndexNewItem(0, 0);
1172 fpc = fpCacheCreate(allMatches->count);
1173 fp1 = fpLookup(fpc, dirName, baseName, 1);
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;
1184 { rpmdbMatchIterator mi;
1185 mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset));
1186 h = rpmdbNextIterator(mi);
1189 mi = rpmdbFreeIterator(mi);
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;
1202 dirIndexes = di.data;
1206 int num = dbiIndexRecordFileNumber(allMatches, i);
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);
1217 if (i < allMatches->count)
1218 offset = dbiIndexRecordOffset(allMatches, i);
1219 } while (i < allMatches->count && offset == prevoff);
1230 if ((*matches)->count == 0) {
1231 *matches = dbiFreeIndexSet(*matches);
1238 dbiFreeIndexSet(allMatches);
1243 /* XXX python/upgrade.c, install.c, uninstall.c */
1244 int rpmdbCountPackages(rpmdb db, const char * name)
1246 DBC * dbcursor = NULL;
1256 memset(&key, 0, sizeof(key));
1257 memset(&data, 0, sizeof(data));
1259 dbi = dbiOpen(db, RPMTAG_NAME, 0);
1263 key.data = (void *) name;
1264 key.size = strlen(name);
1266 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1267 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
1269 xx = dbiCclose(dbi, dbcursor, 0);
1273 if (rc == 0) { /* success */
1274 dbiIndexSet matches;
1275 /* FIX: matches might be NULL */
1277 (void) dbt2set(dbi, &data, &matches);
1279 rc = dbiIndexSetCount(matches);
1280 matches = dbiFreeIndexSet(matches);
1283 if (rc == DB_NOTFOUND) { /* not found */
1285 } else { /* error */
1287 _("error(%d) getting \"%s\" records from %s index\n"),
1288 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
1293 xx = dbiCclose(dbi, dbcursor, 0);
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
1312 static rpmRC dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
1313 DBT * key, DBT * data,
1315 const char * version,
1316 const char * release,
1317 dbiIndexSet * matches)
1319 unsigned int gotMatches = 0;
1323 key->data = (void *) name;
1324 key->size = strlen(name);
1326 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1328 if (rc == 0) { /* success */
1329 (void) dbt2set(dbi, data, matches);
1330 if (version == NULL && release == NULL)
1333 if (rc == DB_NOTFOUND) { /* not found */
1334 return RPMRC_NOTFOUND;
1335 } else { /* error */
1337 _("error(%d) getting \"%s\" records from %s index\n"),
1338 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
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;
1351 mi = rpmdbInitIterator(dbi->dbi_rpmdb,
1352 RPMDBI_PACKAGES, &recoff, sizeof(recoff));
1354 /* Set iterator selectors for version/release if available. */
1356 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version))
1362 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release))
1368 h = rpmdbNextIterator(mi);
1370 (*matches)->recs[gotMatches++] = (*matches)->recs[i];
1372 (*matches)->recs[i].hdrNum = 0;
1373 mi = rpmdbFreeIterator(mi);
1377 (*matches)->count = gotMatches;
1380 rc = RPMRC_NOTFOUND;
1383 /* FIX: double indirection */
1384 if (rc && matches && *matches)
1385 *matches = dbiFreeIndexSet(*matches);
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
1401 static rpmRC dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
1402 const char * arg, dbiIndexSet * matches)
1404 const char * release;
1411 if (arg == NULL || strlen(arg) == 0) return RPMRC_NOTFOUND;
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;
1417 /* FIX: double indirection */
1418 *matches = dbiFreeIndexSet(*matches);
1420 /* maybe a name and a release */
1421 localarg = xmalloc(strlen(arg) + 1);
1422 s = stpcpy(localarg, arg);
1426 for (s -= 1; s > localarg; s--) {
1432 if (c != '[') brackets = 0;
1436 if (!brackets && *s == '-')
1440 /* FIX: *matches may be NULL. */
1441 if (s == localarg) {
1442 rc = RPMRC_NOTFOUND;
1447 rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, NULL, matches);
1448 if (rc != RPMRC_NOTFOUND) goto exit;
1450 /* FIX: double indirection */
1451 *matches = dbiFreeIndexSet(*matches);
1453 /* how about name-version-release? */
1459 for (; s > localarg; s--) {
1465 if (c != '[') brackets = 0;
1469 if (!brackets && *s == '-')
1473 if (s == localarg) {
1474 rc = RPMRC_NOTFOUND;
1479 /* FIX: *matches may be NULL. */
1480 rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, release, matches);
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
1494 static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi)
1498 if (mi == NULL || mi->mi_h == NULL)
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;
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);
1513 /* Check header digest/signature on blob export (if requested). */
1514 if (mi->mi_hdrchk && mi->mi_ts) {
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"));
1526 if (data->data != NULL && rpmrc != RPMRC_FAIL) {
1527 (void) blockSignals(&signalMask);
1528 rc = dbiPut(dbi, mi->mi_dbc, key, data, DB_KEYLAST);
1531 _("error(%d) storing record #%d into %s\n"),
1532 rc, mi->mi_prevoffset, rpmTagGetName(dbi->dbi_rpmtag));
1534 xx = dbiSync(dbi, 0);
1535 (void) unblockSignals(&signalMask);
1537 data->data = _free(data->data);
1541 mi->mi_h = headerFree(mi->mi_h);
1546 rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
1548 rpmdbMatchIterator * prev, next;
1557 while ((next = *prev) != NULL && next != mi)
1558 prev = &next->mi_next;
1560 *prev = next->mi_next;
1561 next->mi_next = NULL;
1564 dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
1565 if (dbi == NULL) /* XXX can't happen */
1568 xx = miFreeHeader(mi, dbi);
1571 xx = dbiCclose(dbi, mi->mi_dbc, 0);
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);
1583 mi->mi_re = _free(mi->mi_re);
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"));
1591 (void) rpmdbCheckSignals();
1596 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
1597 return (mi ? mi->mi_offset : 0);
1600 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
1601 return (mi ? mi->mi_filenum : 0);
1604 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
1605 return (mi && mi->mi_set ? mi->mi_set->count : 0);
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
1614 static int miregexec(miRE mire, const char * val)
1618 switch (mire->mode) {
1619 case RPMMIRE_STRCMP:
1620 rc = (!rstreq(mire->pattern, val));
1622 case RPMMIRE_DEFAULT:
1624 rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
1625 if (rc && rc != REG_NOMATCH) {
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);
1635 rc = fnmatch(mire->pattern, val, mire->fnflags);
1636 if (rc && rc != FNM_NOMATCH)
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
1653 static int mireCmp(const void * a, const void * b)
1655 const miRE mireA = (const miRE) a;
1656 const miRE mireB = (const miRE) b;
1657 return (mireA->tag - mireB->tag);
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
1667 static char * mireDup(rpmTag tag, rpmMireMode *modep,
1668 const char * pattern)
1679 case RPMMIRE_DEFAULT:
1680 if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
1681 *modep = RPMMIRE_GLOB;
1682 pat = xstrdup(pattern);
1686 nb = strlen(pattern) + sizeof("^$");
1688 /* Find no. of bytes needed for pattern. */
1689 /* periods and plusses are escaped, splats become '.*' */
1692 for (s = pattern; *s != '\0'; s++) {
1697 if (!brackets) nb++;
1706 if (c != '[') brackets = 0;
1712 pat = t = xmalloc(nb);
1714 if (pattern[0] != '^') *t++ = '^';
1716 /* Copy pattern, escaping periods, prefixing splats with period. */
1719 for (s = pattern; *s != '\0'; s++, t++) {
1723 if (!brackets) *t++ = '\\';
1726 if (!brackets) *t++ = '.';
1735 if (c != '[') brackets = 0;
1741 if (s > pattern && s[-1] != '$') *t++ = '$';
1743 *modep = RPMMIRE_REGEX;
1745 case RPMMIRE_STRCMP:
1748 pat = xstrdup(pattern);
1755 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
1756 rpmMireMode mode, const char * pattern)
1758 static rpmMireMode defmode = (rpmMireMode)-1;
1760 char * allpat = NULL;
1762 regex_t * preg = NULL;
1768 if (defmode == (rpmMireMode)-1) {
1769 char *t = rpmExpand("%{?_query_selector_match}", NULL);
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;
1780 defmode = RPMMIRE_DEFAULT;
1784 if (mi == NULL || pattern == NULL)
1787 /* Leading '!' inverts pattern match sense, like "grep -v". */
1788 if (*pattern == '!') {
1793 allpat = mireDup(tag, &mode, pattern);
1795 if (mode == RPMMIRE_DEFAULT)
1799 case RPMMIRE_DEFAULT:
1800 case RPMMIRE_STRCMP:
1803 preg = xcalloc(1, sizeof(*preg));
1804 cflags = (REG_EXTENDED | REG_NOSUB);
1805 rc = regcomp(preg, allpat, cflags);
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);
1814 fnflags = FNM_PATHNAME | FNM_PERIOD;
1822 /* FIX: mire has kept values */
1823 allpat = _free(allpat);
1831 mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
1832 mire = mi->mi_re + mi->mi_nre;
1837 mire->pattern = allpat;
1838 mire->notmatch = notmatch;
1840 mire->cflags = cflags;
1841 mire->eflags = eflags;
1842 mire->fnflags = fnflags;
1845 qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
1851 * Return iterator selector match.
1852 * @param mi rpm database iterator
1853 * @return 1 if header should be skipped
1855 static int mireSkip (const rpmdbMatchIterator mi)
1863 if (mi->mi_h == NULL) /* XXX can't happen */
1867 * Apply tag tests, implicitly "||" for multiple patterns/values of a
1868 * single tag, implicitly "&&" between multiple tag patterns.
1870 if ((mire = mi->mi_re) != NULL)
1871 for (int i = 0; i < mi->mi_nre; i++, mire++) {
1875 if (!headerGet(mi->mi_h, mire->tag, &td, HEADERGET_MINMEM)) {
1876 if (mire->tag != RPMTAG_EPOCH) {
1880 /* "is package already installed" checks rely on this behavior */
1882 td.type = RPM_INT32_TYPE;
1886 anymatch = 0; /* no matches yet */
1889 while (rpmtdNext(&td) >= 0) {
1890 char *str = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
1892 rc = miregexec(mire, str);
1893 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
1898 if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
1912 return (ntags == nmatches ? 0 : 1);
1915 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite)
1920 rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0;
1922 mi->mi_cflags |= DB_WRITECURSOR;
1924 mi->mi_cflags &= ~DB_WRITECURSOR;
1928 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified)
1933 rc = mi->mi_modified;
1934 mi->mi_modified = modified;
1938 int rpmdbSetHdrChk(rpmdbMatchIterator mi, rpmts ts,
1939 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
1944 /* XXX forward linkage prevents rpmtsLink */
1946 mi->mi_hdrchk = hdrchk;
1951 /* FIX: mi->mi_key.data may be NULL */
1952 Header rpmdbNextIterator(rpmdbMatchIterator mi)
1967 dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
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.
1977 if (mi->mi_dbc == NULL)
1978 xx = dbiCopen(dbi, dbi->dbi_txnid, &mi->mi_dbc, mi->mi_cflags);
1981 memset(key, 0, sizeof(*key));
1982 data = &mi->mi_data;
1983 memset(data, 0, sizeof(*data));
1990 union _dbswap mi_offset;
1993 if (!(mi->mi_setx < mi->mi_set->count))
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)
2001 keylen = sizeof(mi_offset.ui);
2004 key->data = keyp = (void *)mi->mi_keyp;
2005 key->size = keylen = mi->mi_keylen;
2008 #if !defined(_USE_COPY_LOAD)
2009 data->flags |= DB_DBT_MALLOC;
2011 rc = dbiGet(dbi, mi->mi_dbc, key, data,
2012 (key->data == NULL ? DB_NEXT : DB_SET));
2020 * If we got the next key, save the header instance number.
2022 * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
2023 * largest header instance in the database, and should be
2026 if (keyp && mi->mi_setx && rc == 0) {
2027 memcpy(&mi_offset, keyp, sizeof(mi_offset.ui));
2028 if (dbiByteSwapped(dbi) == 1)
2030 mi->mi_offset = mi_offset.ui;
2033 /* Terminate on error or end of keys */
2034 if (rc || (mi->mi_setx && mi->mi_offset == 0))
2038 } while (mi->mi_offset == 0);
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;
2046 /* Retrieve next header blob for index iterator. */
2050 #if !defined(_USE_COPY_LOAD)
2051 data->flags |= DB_DBT_MALLOC;
2053 rc = dbiGet(dbi, mi->mi_dbc, key, data, DB_SET);
2063 /* Rewrite current header (if necessary) and unlink. */
2064 xx = miFreeHeader(mi, dbi);
2066 /* Is this the end of the iteration? */
2070 /* Check header digest/signature once (if requested). */
2071 if (mi->mi_hdrchk && mi->mi_ts) {
2072 rpmRC rpmrc = RPMRC_NOTFOUND;
2074 /* Don't bother re-checking a previously read header. */
2075 if (mi->mi_db->db_bits) {
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))
2084 /* If blob is unchecked, check blob import consistency now. */
2085 if (rpmrc != RPMRC_OK) {
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"));
2096 /* Mark header checked. */
2097 if (mi->mi_db && mi->mi_db->db_bits && rpmrc == RPMRC_OK) {
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);
2105 /* Skip damaged and inconsistent headers. */
2106 if (rpmrc == RPMRC_FAIL)
2111 /* Did the header blob load correctly? */
2112 #if !defined(_USE_COPY_LOAD)
2113 mi->mi_h = headerLoad(uh);
2115 mi->mi_h = headerCopyLoad(uh);
2117 if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
2119 _("rpmdb: damaged header #%u retrieved -- skipping.\n"),
2125 * Skip this header if iterator selector (if any) doesn't match.
2128 /* XXX hack, can't restart with Packages locked on single instance. */
2129 if (mi->mi_set || mi->mi_keyp == NULL)
2133 headerSetInstance(mi->mi_h, mi->mi_offset);
2135 mi->mi_prevoffset = mi->mi_offset;
2136 mi->mi_modified = 0;
2142 * sort the iterator by (recnum, filenum)
2143 * Return database iterator.
2144 * @param mi rpm database iterator
2146 void rpmdbSortIterator(rpmdbMatchIterator mi)
2148 if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
2150 * mergesort is much (~10x with lots of identical basenames) faster
2151 * than pure quicksort, but glibc uses msort_with_tmp() on stack.
2153 #if defined(__GLIBC__)
2154 qsort(mi->mi_set->recs, mi->mi_set->count,
2155 sizeof(*mi->mi_set->recs), hdrNumCmp);
2157 mergesort(mi->mi_set->recs, mi->mi_set->count,
2158 sizeof(*mi->mi_set->recs), hdrNumCmp);
2164 static int rpmdbGrowIterator(rpmdbMatchIterator mi)
2169 dbiIndex dbi = NULL;
2177 dbcursor = mi->mi_dbc;
2179 data = &mi->mi_data;
2180 if (key->data == NULL)
2183 dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0);
2187 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2188 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2190 xx = dbiCclose(dbi, dbcursor, 0);
2194 if (rc) { /* error/not found */
2195 if (rc != DB_NOTFOUND)
2197 _("error(%d) getting \"%s\" records from %s index\n"),
2198 rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
2200 xx = dbiCclose(dbi, dbcursor, 0);
2207 (void) dbt2set(dbi, data, &set);
2210 xx = dbiCclose(dbi, dbcursor, 0);
2214 if (mi->mi_set == NULL) {
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);
2227 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
2228 int nHdrNums, int sorted)
2230 if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2234 (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
2238 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
2240 if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
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);
2249 rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmTag rpmtag,
2250 const void * keyp, size_t keylen)
2252 rpmdbMatchIterator mi;
2255 dbiIndexSet set = NULL;
2257 void * mi_keyp = NULL;
2263 (void) rpmdbCheckSignals();
2265 /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
2266 if (rpmtag == RPMDBI_LABEL) {
2267 rpmtag = RPMTAG_NAME;
2271 dbi = dbiOpen(db, rpmtag, 0);
2275 /* Chain cursors for teardown on abnormal exit. */
2276 mi = xcalloc(1, sizeof(*mi));
2277 mi->mi_next = rpmmiRock;
2281 data = &mi->mi_data;
2284 * Handle label and file name special cases.
2285 * Otherwise, retrieve join keys for secondary lookup.
2287 if (rpmtag != RPMDBI_PACKAGES && keyp) {
2288 DBC * dbcursor = NULL;
2293 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2294 rc = dbiFindByLabel(dbi, dbcursor, key, data, keyp, &set);
2295 xx = dbiCclose(dbi, dbcursor, 0);
2297 } else if (rpmtag == RPMTAG_BASENAMES) {
2298 rc = rpmdbFindByFile(db, keyp, key, data, &set);
2300 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2302 key->data = (void *) keyp;
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. */
2309 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2312 _("error(%d) getting \"%s\" records from %s index\n"),
2313 rc, (key->data ? (char *)key->data : "???"),
2314 rpmTagGetName(dbi->dbi_rpmtag));
2317 /* Join keys need to be native endian internally. */
2319 (void) dbt2set(dbi, data, &set);
2321 xx = dbiCclose(dbi, dbcursor, 0);
2324 if (rc) { /* error/not found */
2325 set = dbiFreeIndexSet(set);
2326 rpmmiRock = mi->mi_next;
2333 /* Copy the retrieval key, byte swapping header instance if necessary. */
2336 case RPMDBI_PACKAGES:
2339 assert(keylen == sizeof(k->ui)); /* xxx programmer error */
2340 k = xmalloc(sizeof(*k));
2341 memcpy(k, keyp, keylen);
2342 if (dbiByteSwapped(dbi) == 1)
2349 keylen = strlen(keyp);
2350 k = xmalloc(keylen + 1);
2351 memcpy(k, keyp, keylen);
2352 k[keylen] = '\0'; /* XXX assumes strings */
2358 mi->mi_keyp = mi_keyp;
2359 mi->mi_keylen = keylen;
2361 mi->mi_db = rpmdbLink(db, RPMDBG_M("matchIterator"));
2362 mi->mi_rpmtag = rpmtag;
2370 mi->mi_modified = 0;
2371 mi->mi_prevoffset = 0;
2378 mi->mi_hdrchk = NULL;
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
2390 int rpmdbExtendIterator(rpmdbMatchIterator mi,
2391 const void * keyp, size_t keylen)
2393 mi->mi_key.data = (void *) keyp;
2394 mi->mi_key.size = keylen ? keylen : strlen(keyp);
2395 return rpmdbGrowIterator(mi);
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)
2405 static int td2key(rpmtd tagdata, DBT *key, int *freedata)
2407 const char *str = NULL;
2408 uint8_t *bin = NULL;
2411 switch (rpmtdType(tagdata)) {
2414 key->size = sizeof(uint8_t);
2415 key->data = rpmtdGetChar(tagdata);
2417 case RPM_INT16_TYPE:
2418 key->size = sizeof(uint16_t);
2419 key->data = rpmtdGetUint16(tagdata);
2421 case RPM_INT32_TYPE:
2422 key->size = sizeof(uint32_t);
2423 key->data = rpmtdGetUint32(tagdata);
2425 case RPM_INT64_TYPE:
2426 key->size = sizeof(uint64_t);
2427 key->data = rpmtdGetUint64(tagdata);
2430 key->size = tagdata->count;
2431 key->data = tagdata->data;
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) {
2441 /* Filter out empty MD5 strings. */
2442 if (!(str && *str != '\0'))
2445 binlen = strlen(str) / 2;
2446 bin = xmalloc(binlen);
2447 /* Convert from hex to binary. */
2449 for (int j = 0; j < binlen; j++, t++, str += 2)
2450 *t = (rnibble(str[0]) << 4) | rnibble(str[1]);
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);
2471 str = rpmtdGetString(tagdata);
2472 key->data = (char *) str; /* XXX discards const */
2473 key->size = strlen(str);
2478 key->size = strlen((char *)key->data);
2480 key->size++; /* XXX "/" fixup. */
2485 static void logAddRemove(int removing, rpmtd tagdata)
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)));
2494 rpmlog(RPMLOG_DEBUG, "%s %d entries %s %s index.\n",
2495 removing ? "removing" : "adding", c,
2496 removing ? "from" : "to",
2497 rpmTagGetName(rpmtdTag(tagdata)));
2502 int rpmdbRemove(rpmdb db, int rid, unsigned int hdrNum,
2504 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
2506 DBC * dbcursor = NULL;
2509 union _dbswap mi_offset;
2511 sigset_t signalMask;
2518 memset(&key, 0, sizeof(key));
2519 memset(&data, 0, sizeof(data));
2521 { rpmdbMatchIterator mi;
2522 mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
2523 h = rpmdbNextIterator(mi);
2526 mi = rpmdbFreeIterator(mi);
2530 rpmlog(RPMLOG_ERR, _("%s: cannot read header at 0x%x\n"),
2531 "rpmdbRemove", hdrNum);
2536 char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
2537 rpmlog(RPMLOG_DEBUG, " --- h#%8u %s\n", hdrNum, nevra);
2541 (void) blockSignals(&signalMask);
2543 /* FIX: rpmvals heartburn */
2545 dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2547 if (dbiTags.tags != NULL)
2548 for (dbix = 0; dbix < dbiTags.max; dbix++) {
2552 struct rpmtd_s tagdata;
2555 rpmtag = dbiTags.tags[dbix];
2557 /* Filter out temporary databases */
2558 if (isTemporaryDB(rpmtag))
2561 if (rpmtag == RPMDBI_PACKAGES) {
2562 dbi = dbiOpen(db, rpmtag, 0);
2563 if (dbi == NULL) /* XXX shouldn't happen */
2566 mi_offset.ui = hdrNum;
2567 if (dbiByteSwapped(dbi) == 1)
2569 key.data = &mi_offset;
2570 key.size = sizeof(mi_offset.ui);
2572 rc = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2573 rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2576 _("error(%d) setting header #%d record for %s removal\n"),
2577 rc, hdrNum, rpmTagGetName(dbi->dbi_rpmtag));
2579 rc = dbiDel(dbi, dbcursor, &key, &data, 0);
2580 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2582 if (!dbi->dbi_no_dbsync)
2583 xx = dbiSync(dbi, 0);
2587 if (!headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM))
2590 if (!(dbi = dbiOpen(db, rpmtag, 0))) {
2591 rpmtdFreeData(&tagdata);
2594 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2596 logAddRemove(1, &tagdata);
2597 while (rpmtdNext(&tagdata) >= 0) {
2601 if (!td2key(&tagdata, &key, &freedata)) {
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.
2613 * XXX with duplicates, an accurate data value and
2614 * DB_GET_BOTH is needed.
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 */
2623 } else { /* error */
2625 _("error(%d) setting \"%s\" records from %s index\n"),
2626 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2631 rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
2633 /* If nothing was pruned, then don't bother updating. */
2635 set = dbiFreeIndexSet(set);
2639 if (set->count > 0) {
2640 (void) set2dbt(dbi, &data, set);
2641 rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2644 _("error(%d) storing record \"%s\" into %s\n"),
2645 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2648 data.data = _free(data.data);
2651 rc = dbiDel(dbi, dbcursor, &key, &data, 0);
2654 _("error(%d) removing record \"%s\" from %s\n"),
2655 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2659 set = dbiFreeIndexSet(set);
2666 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2669 if (!dbi->dbi_no_dbsync)
2670 xx = dbiSync(dbi, 0);
2672 rpmtdFreeData(&tagdata);
2678 (void) unblockSignals(&signalMask);
2682 /* XXX return ret; */
2687 int rpmdbAdd(rpmdb db, int iid, Header h,
2689 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
2691 DBC * dbcursor = NULL;
2694 sigset_t signalMask;
2697 union _dbswap mi_offset;
2698 unsigned int hdrNum = 0;
2706 memset(&key, 0, sizeof(key));
2707 memset(&data, 0, sizeof(data));
2709 #ifdef NOTYET /* XXX headerDel() broken on dribbles. */
2710 xx = headerDel(h, RPMTAG_REMOVETID);
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);
2718 (void) blockSignals(&signalMask);
2721 unsigned int firstkey = 0;
2722 void * keyp = &firstkey;
2723 size_t keylen = sizeof(firstkey);
2724 void * datap = NULL;
2727 dbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
2730 /* XXX db0: hack to pass sizeof header to fadAlloc */
2732 datalen = headerSizeof(h, HEADER_MAGIC_NO);
2734 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2736 /* Retrieve join key for next header instance. */
2741 data.size = datalen;
2742 ret = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
2746 datalen = data.size;
2749 if (ret == 0 && datap) {
2750 memcpy(&mi_offset, datap, sizeof(mi_offset.ui));
2751 if (dbiByteSwapped(dbi) == 1)
2753 hdrNum = mi_offset.ui;
2756 mi_offset.ui = hdrNum;
2757 if (dbiByteSwapped(dbi) == 1)
2759 if (ret == 0 && datap) {
2760 memcpy(datap, &mi_offset, sizeof(mi_offset.ui));
2763 datalen = sizeof(mi_offset.ui);
2769 data.size = datalen;
2771 ret = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2772 xx = dbiSync(dbi, 0);
2774 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2782 _("error(%d) allocating new package instance\n"), ret);
2786 /* Now update the indexes */
2790 dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2792 if (dbiTags.tags != NULL)
2793 for (dbix = 0; dbix < dbiTags.max; dbix++) {
2797 struct rpmtd_s tagdata, reqflags;
2799 rpmrc = RPMRC_NOTFOUND;
2801 rpmtag = dbiTags.tags[dbix];
2803 /* Filter out temporary databases */
2804 if (isTemporaryDB(rpmtag))
2808 case RPMDBI_PACKAGES:
2809 dbi = dbiOpen(db, rpmtag, 0);
2810 if (dbi == NULL) /* XXX shouldn't happen */
2812 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2814 mi_offset.ui = hdrNum;
2815 if (dbiByteSwapped(dbi) == 1)
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);
2822 /* Check header digest/signature on blob export. */
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"));
2835 if (data.data != NULL && rpmrc != RPMRC_FAIL) {
2836 xx = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2837 xx = dbiSync(dbi, 0);
2839 data.data = _free(data.data);
2841 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2843 if (!dbi->dbi_no_dbsync)
2844 xx = dbiSync(dbi, 0);
2847 case RPMTAG_REQUIRENAME:
2848 headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM);
2849 headerGet(h, RPMTAG_REQUIREFLAGS, &reqflags, HEADERGET_MINMEM);
2852 headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM);
2856 if (rpmtdCount(&tagdata) == 0) {
2857 if (rpmtag != RPMTAG_GROUP)
2860 /* XXX preserve legacy behavior */
2861 tagdata.type = RPM_STRING_TYPE;
2862 tagdata.data = (const char **) "Unknown";
2866 if (!(dbi = dbiOpen(db, rpmtag, 0))) {
2867 rpmtdFreeData(&tagdata);
2870 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2872 logAddRemove(0, &tagdata);
2873 while (rpmtdNext(&tagdata) >= 0) {
2875 int i, freedata = 0;
2878 * Include the tagNum in all indices. rpm-3.0.4 and earlier
2879 * included the tagNum only for files.
2881 i = rec->tagNum = rpmtdGetIndex(&tagdata);
2883 case RPMTAG_REQUIRENAME: {
2884 /* Filter out install prerequisites. */
2885 rpm_flag_t *rflag = rpmtdNextUint32(&reqflags);
2886 if (rflag && isInstallPreReq(*rflag) &&
2887 !isErasePreReq(*rflag))
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]))
2907 if (!td2key(&tagdata, &key, &freedata)) {
2912 * XXX with duplicates, an accurate data value and
2913 * DB_GET_BOTH is needed.
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 */
2925 _("error(%d) getting \"%s\" records from %s index\n"),
2926 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2931 if (set == NULL) /* not found or duplicate */
2932 set = xcalloc(1, sizeof(*set));
2934 (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
2936 (void) set2dbt(dbi, &data, set);
2937 rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
2941 _("error(%d) storing record %s into %s\n"),
2942 rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
2945 data.data = _free(data.data);
2947 set = dbiFreeIndexSet(set);
2954 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2957 if (!dbi->dbi_no_dbsync)
2958 xx = dbiSync(dbi, 0);
2960 rpmtdFreeData(&tagdata);
2964 /* If everthing ok, mark header as installed now */
2966 headerSetInstance(h, hdrNum);
2971 (void) unblockSignals(&signalMask);
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
2984 static int cleanDbenv(const char *prefix, const char *dbpath)
2986 ARGV_t paths = NULL, p;
2988 char *pattern = rpmGetPath(prefix, "/", dbpath, "/__db.???", NULL);
2990 if (rpmGlob(pattern, NULL, &paths) == 0) {
2991 for (p = paths; *p; p++) {
3000 static int rpmdbRemoveDatabase(const char * prefix,
3001 const char * dbpath, int _dbapi)
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)
3019 cleanDbenv(prefix, dbpath);
3028 path = rpmGetPath(prefix, "/", dbpath, NULL);
3035 static int rpmdbMoveDatabase(const char * prefix,
3036 const char * olddbpath, int _olddbapi,
3037 const char * newdbpath, int _newdbapi)
3043 int selinux = is_selinux_enabled() && (matchpathcon_init(NULL) != -1);
3047 blockSignals(&sigMask);
3048 switch (_olddbapi) {
3052 if (dbiTags.tags != NULL)
3053 for (i = 0; i < dbiTags.max; i++) {
3058 /* Filter out temporary databases */
3059 if (isTemporaryDB((rpmtag = dbiTags.tags[i])))
3062 base = rpmTagGetName(rpmtag);
3063 src = rpmGetPath(prefix, "/", olddbpath, "/", base, NULL);
3064 dest = rpmGetPath(prefix, "/", newdbpath, "/", base, NULL);
3066 if (access(src, F_OK) != 0)
3070 * Restore uid/gid/mode/mtime/security context if possible.
3072 if (stat(dest, &st) < 0)
3073 if (stat(src, &st) < 0)
3076 if ((xx = rename(src, dest)) != 0) {
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);
3089 security_context_t scon = NULL;
3090 if (matchpathcon(dest, st.st_mode, &scon) != -1) {
3091 (void) setfilecon(dest, scon);
3101 cleanDbenv(prefix, olddbpath);
3102 cleanDbenv(prefix, newdbpath);
3109 unblockSignals(&sigMask);
3111 #ifdef SQLITE_HACK_XXX
3112 if (rc || _olddbapi == _newdbapi)
3115 rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi);
3119 (void) matchpathcon_fini();
3125 int rpmdbRebuild(const char * prefix, rpmts ts,
3126 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
3129 char * dbpath = NULL;
3130 char * rootdbpath = NULL;
3132 char * newdbpath = NULL;
3133 char * newrootdbpath = NULL;
3142 if (prefix == NULL) prefix = "/";
3144 _dbapi = rpmExpandNumeric("%{_dbapi}");
3145 _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
3147 tfn = rpmGetPath("%{?_dbpath}", NULL);
3148 if (!(tfn && tfn[0] != '\0'))
3150 rpmlog(RPMLOG_ERR, _("no dbpath has been set"));
3154 dbpath = rootdbpath = rpmGetPath(prefix, tfn, NULL);
3155 if (!(prefix[0] == '/' && prefix[1] == '\0'))
3156 dbpath += strlen(prefix) - 1;
3159 tfn = rpmGetPath("%{?_dbpath_rebuild}", NULL);
3160 if (!(tfn && tfn[0] != '\0' && !rstreq(tfn, dbpath)))
3163 rasprintf(&tfn, "%srebuilddb.%d", dbpath, (int) getpid());
3166 newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL);
3167 if (!(prefix[0] == '/' && prefix[1] == '\0'))
3168 newdbpath += strlen(prefix) - 1;
3171 rpmlog(RPMLOG_DEBUG, "rebuilding database %s into %s\n",
3172 rootdbpath, newrootdbpath);
3174 if (!access(newrootdbpath, F_OK)) {
3175 rpmlog(RPMLOG_ERR, _("temporary database %s already exists\n"),
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));
3190 _rebuildinprogress = 0;
3192 rpmlog(RPMLOG_DEBUG, "opening old database with dbapi %d\n",
3194 if (openDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644,
3195 RPMDB_FLAG_MINIMAL)) {
3199 _dbapi = olddb->db_api;
3200 _rebuildinprogress = 1;
3201 rpmlog(RPMLOG_DEBUG, "opening new database with dbapi %d\n",
3203 (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1);
3204 if (openDatabase(prefix, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
3209 _rebuildinprogress = 0;
3211 _dbapi_rebuild = newdb->db_api;
3214 rpmdbMatchIterator mi;
3215 #define _RECNUM rpmdbGetIteratorOffset(mi)
3217 mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
3219 (void) rpmdbSetHdrChk(mi, ts, hdrchk);
3221 while ((h = rpmdbNextIterator(mi)) != NULL) {
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)))
3230 _("header #%u in the database is bad -- skipping.\n"),
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);
3244 _("cannot add record originally at %u\n"), _RECNUM);
3250 mi = rpmdbFreeIterator(mi);
3254 xx = rpmdbClose(olddb);
3255 xx = rpmdbClose(newdb);
3258 rpmlog(RPMLOG_WARNING,
3259 _("failed to rebuild database: original database "
3260 "remains in place\n"));
3262 xx = rpmdbRemoveDatabase(prefix, newdbpath, _dbapi_rebuild);
3265 } else if (!nocleanup) {
3266 if (rpmdbMoveDatabase(prefix, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
3267 rpmlog(RPMLOG_ERR, _("failed to replace old database with new "
3269 rpmlog(RPMLOG_ERR, _("replace files in %s with files from %s "
3270 "to recover"), dbpath, newdbpath);
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));
3284 newrootdbpath = _free(newrootdbpath);
3285 rootdbpath = _free(rootdbpath);