7 #define _USE_COPY_LOAD /* XXX don't use DB_DBT_MALLOC (yet) */
11 #ifndef DYING /* XXX already in "system.h" */
15 #if defined(__LCLINT__)
16 /*@-declundef -exportheader -redecl @*/ /* LCL: missing annotation */
17 extern int fnmatch (const char *pattern, const char *string, int flags)
19 /*@=declundef =exportheader =redecl @*/
24 #if defined(__LCLINT__)
25 /*@-declundef -exportheader @*/ /* LCL: missing modifies (only is bogus) */
26 extern void regfree (/*@only@*/ regex_t *preg)
27 /*@modifies *preg @*/;
28 /*@=declundef =exportheader @*/
31 #include <rpmio_internal.h>
38 #include "header_internal.h" /* XXX for HEADERFLAG_ALLOCATED */
41 /*@access dbiIndexSet@*/
42 /*@access dbiIndexItem@*/
43 /*@access rpmts@*/ /* XXX compared with NULL */
44 /*@access Header@*/ /* XXX compared with NULL */
45 /*@access rpmdbMatchIterator@*/
52 static int _rebuildinprogress = 0;
54 static int _db_filter_dups = 0;
57 #define _DBI_PERMS 0644
61 /*@globstate@*/ /*@null@*/ int * dbiTags = NULL;
65 /* Bit mask macros. */
67 typedef unsigned int __pbm_bits;
69 #define __PBM_NBITS (8 * sizeof (__pbm_bits))
70 #define __PBM_IX(d) ((d) / __PBM_NBITS)
71 #define __PBM_MASK(d) ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
77 #define __PBM_BITS(set) ((set)->bits)
79 #define PBM_FREE(s) _free(s);
80 #define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
81 #define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
82 #define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
84 #define PBM_ALLOC(d) xcalloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
87 * Reallocate a bit map.
88 * @retval sp address of bit map pointer
89 * @retval odp no. of bits in map
90 * @param nd desired no. of bits
93 static inline pbm_set * PBM_REALLOC(pbm_set ** sp, int * odp, int nd)
94 /*@modifies *sp, *odp @*/
98 /*@-bounds -sizeoftype@*/
101 nb = __PBM_IX(nd) + 1;
102 /*@-unqualifiedtrans@*/
103 *sp = xrealloc(*sp, nb * sizeof(__pbm_bits));
104 /*@=unqualifiedtrans@*/
105 for (i = __PBM_IX(*odp) + 1; i < nb; i++)
106 __PBM_BITS(*sp)[i] = 0;
109 /*@=bounds =sizeoftype@*/
110 /*@-compdef -retalias -usereleased@*/
112 /*@=compdef =retalias =usereleased@*/
116 * Convert hex to binary nibble.
117 * @param c hex character
118 * @return binary nibble
120 static inline unsigned char nibble(char c)
123 if (c >= '0' && c <= '9')
125 if (c >= 'A' && c <= 'F')
126 return (c - 'A') + 10;
127 if (c >= 'a' && c <= 'f')
128 return (c - 'a') + 10;
134 * Check key for printable characters.
135 * @param ptr key value pointer
136 * @param len key value length
137 * @return 1 if only ASCII, 0 otherwise.
139 static int printable(const void * ptr, size_t len) /*@*/
141 const char * s = ptr;
143 for (i = 0; i < len; i++, s++)
144 if (!(*s >= ' ' && *s <= '~')) return 0;
150 * Return dbi index used for rpm tag.
151 * @param rpmtag rpm header tag
152 * @return dbi index, -1 on error
154 static int dbiTagToDbix(int rpmtag)
160 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
162 if (rpmtag == dbiTags[dbix])
170 * Initialize database (index, tag) tuple from configuration.
172 static void dbiTagsInit(void)
173 /*@globals rpmGlobalMacroContext, dbiTags, dbiTagsMax @*/
174 /*@modifies rpmGlobalMacroContext, dbiTags, dbiTagsMax @*/
176 /*@observer@*/ static const char * const _dbiTagStr_default =
177 "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername:Dirnames:Requireversion:Provideversion:Installtid:Sigmd5:Sha1header:Filemd5s:Depends:Pubkeys";
178 char * dbiTagStr = NULL;
182 dbiTagStr = rpmExpand("%{?_dbi_tags}", NULL);
183 if (!(dbiTagStr && *dbiTagStr)) {
184 dbiTagStr = _free(dbiTagStr);
185 dbiTagStr = xstrdup(_dbiTagStr_default);
188 /* Discard previous values. */
189 dbiTags = _free(dbiTags);
192 /* Always allocate package index */
193 dbiTags = xcalloc(1, sizeof(*dbiTags));
194 dbiTags[dbiTagsMax++] = RPMDBI_PACKAGES;
196 for (o = dbiTagStr; o && *o; o = oe) {
197 while (*o && xisspace(*o))
201 for (oe = o; oe && *oe; oe++) {
203 /*@innerbreak@*/ break;
204 if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
205 /*@innerbreak@*/ break;
209 rpmtag = tagValue(o);
211 rpmMessage(RPMMESS_WARNING,
212 _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
215 if (dbiTagToDbix(rpmtag) >= 0)
218 dbiTags = xrealloc(dbiTags, (dbiTagsMax + 1) * sizeof(*dbiTags)); /* XXX memory leak */
219 dbiTags[dbiTagsMax++] = rpmtag;
222 dbiTagStr = _free(dbiTagStr);
229 /*@-exportheadervar -declundef @*/
231 extern struct _dbiVec db3vec;
232 /*@=exportheadervar =declundef @*/
233 #define DB3vec &db3vec
237 /*@observer@*/ /*@unchecked@*/
238 static struct _dbiVec *mydbvecs[] = {
239 DB1vec, DB1vec, DB2vec, DB3vec, NULL
243 dbiIndex dbiOpen(rpmdb db, rpmTag rpmtag, /*@unused@*/ unsigned int flags)
247 int _dbapi, _dbapi_rebuild, _dbapi_wanted;
253 dbix = dbiTagToDbix(rpmtag);
254 if (dbix < 0 || dbix >= dbiTagsMax)
257 /* Is this index already open ? */
258 /*@-compdef@*/ /* FIX: db->_dbi may be NULL */
259 if ((dbi = db->_dbi[dbix]) != NULL)
263 _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
264 if (_dbapi_rebuild < 1 || _dbapi_rebuild > 3)
266 _dbapi_wanted = (_rebuildinprogress ? -1 : db->db_api);
268 switch (_dbapi_wanted) {
270 _dbapi = _dbapi_wanted;
271 if (_dbapi < 0 || _dbapi >= 4 || mydbvecs[_dbapi] == NULL) {
276 rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
278 static int _printed[32];
279 if (!_printed[dbix & 0x1f]++)
280 rpmError(RPMERR_DBOPEN,
281 _("cannot open %s index using db%d - %s (%d)\n"),
282 tagName(rpmtag), _dbapi,
283 (rc > 0 ? strerror(rc) : ""), rc);
289 while (_dbapi-- > 1) {
290 if (mydbvecs[_dbapi] == NULL)
294 rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
296 /*@loopbreak@*/ break;
299 static int _printed[32];
300 if (!_printed[dbix & 0x1f]++)
301 rpmError(RPMERR_DBOPEN, _("cannot open %s index\n"),
306 if (db->db_api == -1 && _dbapi > 0)
311 /* Require conversion. */
312 if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
313 rc = (_rebuildinprogress ? 0 : 1);
317 /* Suggest possible configuration */
318 if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
323 /* Suggest possible configuration */
324 if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
325 rc = (_rebuildinprogress ? 0 : 1);
330 if (dbi != NULL && rc == 0) {
331 db->_dbi[dbix] = dbi;
333 if (rpmtag == RPMDBI_PACKAGES && db->db_bits == NULL) {
335 if (!dbiStat(dbi, DB_FAST_STAT)) {
336 DB_HASH_STAT * hash = (DB_HASH_STAT *)dbi->dbi_stats;
338 db->db_nbits += hash->hash_nkeys;
340 db->db_bits = PBM_ALLOC(db->db_nbits);
346 /*@-compdef -nullstate@*/ /* FIX: db->_dbi may be NULL */
348 /*@=compdef =nullstate@*/
352 * Create and initialize item for index database set.
353 * @param hdrNum header instance in db
354 * @param tagNum tag index in header
357 static dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum)
360 dbiIndexItem rec = xcalloc(1, sizeof(*rec));
361 rec->hdrNum = hdrNum;
362 rec->tagNum = tagNum;
371 #define _DBSWAP(_a) \
372 { unsigned char _b, *_c = (_a).uc; \
373 _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
374 _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
378 * Convert retrieved data to index set.
379 * @param dbi index database handle
380 * @param data retrieved data
381 * @retval setp (malloc'ed) index set
382 * @return 0 on success
384 static int dbt2set(dbiIndex dbi, DBT * data, /*@out@*/ dbiIndexSet * setp)
385 /*@modifies dbi, *setp @*/
387 int _dbbyteswapped = dbiByteSwapped(dbi);
392 if (dbi == NULL || data == NULL || setp == NULL)
395 if ((sdbir = data->data) == NULL) {
400 set = xmalloc(sizeof(*set));
401 set->count = data->size / dbi->dbi_jlen;
402 set->recs = xmalloc(set->count * sizeof(*(set->recs)));
404 /*@-bounds -sizeoftype @*/
405 switch (dbi->dbi_jlen) {
407 case 2*sizeof(int_32):
408 for (i = 0; i < set->count; i++) {
409 union _dbswap hdrNum, tagNum;
411 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
412 sdbir += sizeof(hdrNum.ui);
413 memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
414 sdbir += sizeof(tagNum.ui);
415 if (_dbbyteswapped) {
419 set->recs[i].hdrNum = hdrNum.ui;
420 set->recs[i].tagNum = tagNum.ui;
421 set->recs[i].fpNum = 0;
424 case 1*sizeof(int_32):
425 for (i = 0; i < set->count; i++) {
426 union _dbswap hdrNum;
428 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
429 sdbir += sizeof(hdrNum.ui);
430 if (_dbbyteswapped) {
433 set->recs[i].hdrNum = hdrNum.ui;
434 set->recs[i].tagNum = 0;
435 set->recs[i].fpNum = 0;
440 /*@=bounds =sizeoftype @*/
447 * Convert index set to database representation.
448 * @param dbi index database handle
449 * @param data retrieved data
450 * @param set index set
451 * @return 0 on success
453 static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set)
454 /*@modifies dbi, *data @*/
456 int _dbbyteswapped = dbiByteSwapped(dbi);
460 if (dbi == NULL || data == NULL || set == NULL)
463 data->size = set->count * (dbi->dbi_jlen);
464 if (data->size == 0) {
468 tdbir = data->data = xmalloc(data->size);
470 /*@-bounds -sizeoftype@*/
471 switch (dbi->dbi_jlen) {
473 case 2*sizeof(int_32):
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(int_32):
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);
505 /*@=bounds =sizeoftype@*/
512 /* XXX assumes hdrNum is first int in dbiIndexItem */
513 static int hdrNumCmp(const void * one, const void * two)
516 const int * a = one, * b = two;
521 * Append element(s) to set of index database items.
522 * @param set set of index database items
523 * @param recs array of items to append to set
524 * @param nrecs number of items
525 * @param recsize size of an array item
526 * @param sortset should resulting set be sorted?
527 * @return 0 success, 1 failure (bad args)
529 static int dbiAppendSet(dbiIndexSet set, const void * recs,
530 int nrecs, size_t recsize, int sortset)
533 const char * rptr = recs;
534 size_t rlen = (recsize < sizeof(*(set->recs)))
535 ? recsize : sizeof(*(set->recs));
537 if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0)
540 set->recs = xrealloc(set->recs,
541 (set->count + nrecs) * sizeof(*(set->recs)));
543 memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
545 while (nrecs-- > 0) {
546 /*@-mayaliasunique@*/
547 memcpy(set->recs + set->count, rptr, rlen);
548 /*@=mayaliasunique@*/
553 if (sortset && set->count > 1)
554 qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
560 * Remove element(s) from set of index database items.
561 * @param set set of index database items
562 * @param recs array of items to remove from set
563 * @param nrecs number of items
564 * @param recsize size of an array item
565 * @param sorted array is already sorted?
566 * @return 0 success, 1 failure (no items found)
568 static int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
569 size_t recsize, int sorted)
570 /*@modifies set, recs @*/
574 int num = set->count;
577 assert(set->count > 0);
578 if (nrecs > 1 && !sorted)
579 qsort(recs, nrecs, recsize, hdrNumCmp);
581 for (from = 0; from < num; from++) {
582 if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
587 set->recs[to] = set->recs[from]; /* structure assignment */
591 return (numCopied == num);
594 /* XXX transaction.c */
595 unsigned int dbiIndexSetCount(dbiIndexSet set) {
599 /* XXX transaction.c */
600 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
601 return set->recs[recno].hdrNum;
604 /* XXX transaction.c */
605 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
606 return set->recs[recno].tagNum;
609 /* XXX transaction.c */
610 dbiIndexSet dbiFreeIndexSet(dbiIndexSet set) {
612 set->recs = _free(set->recs);
618 typedef struct miRE_s {
619 rpmTag tag; /*!< header tag */
620 rpmMireMode mode; /*!< pattern match mode */
621 /*@only@*/ const char * pattern; /*!< pattern string */
622 int notmatch; /*!< like "grep -v" */
623 /*@only@*/ regex_t * preg; /*!< regex compiled pattern buffer */
624 int cflags; /*!< regcomp(3) flags */
625 int eflags; /*!< regexec(3) flags */
626 int fnflags; /*!< fnmatch(3) flags */
629 struct _rpmdbMatchIterator {
630 /*@dependent@*/ /*@null@*/
631 rpmdbMatchIterator mi_next;
633 const void * mi_keyp;
643 /*@refcounted@*/ /*@null@*/
648 unsigned int mi_prevoffset;
649 unsigned int mi_offset;
650 unsigned int mi_filenum;
652 /*@only@*/ /*@null@*/
657 rpmRC (*mi_hdrchk) (rpmts ts, const void * uh, size_t uc, const char ** msg)
658 /*@modifies ts, *msg @*/;
663 static rpmdb rpmdbRock;
665 /*@unchecked@*/ /*@exposed@*/ /*@null@*/
666 static rpmdbMatchIterator rpmmiRock;
668 int rpmdbCheckSignals(void)
669 /*@globals rpmdbRock, rpmmiRock @*/
670 /*@modifies rpmdbRock, rpmmiRock @*/
672 sigset_t newMask, oldMask;
673 static int terminate = 0;
675 if (terminate) return 0;
677 (void) sigfillset(&newMask); /* block all signals */
678 (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
680 if (sigismember(&rpmsqCaught, SIGINT)
681 || sigismember(&rpmsqCaught, SIGQUIT)
682 || sigismember(&rpmsqCaught, SIGHUP)
683 || sigismember(&rpmsqCaught, SIGTERM)
684 || sigismember(&rpmsqCaught, SIGPIPE))
689 rpmdbMatchIterator mi;
691 rpmMessage(RPMMESS_DEBUG, "Exiting on signal ...\n");
694 while ((mi = rpmmiRock) != NULL) {
695 /*@i@*/ rpmmiRock = mi->mi_next;
697 /*@i@*/ mi = rpmdbFreeIterator(mi);
702 while ((db = rpmdbRock) != NULL) {
703 /*@i@*/ rpmdbRock = db->db_next;
705 (void) rpmdbClose(db);
710 return sigprocmask(SIG_SETMASK, &oldMask, NULL);
714 * Block all signals, returning previous signal mask.
716 static int blockSignals(/*@unused@*/ rpmdb db, /*@out@*/ sigset_t * oldMask)
717 /*@globals fileSystem @*/
718 /*@modifies *oldMask, fileSystem @*/
722 (void) sigfillset(&newMask); /* block all signals */
723 (void) sigprocmask(SIG_BLOCK, &newMask, oldMask);
724 (void) sigdelset(&newMask, SIGINT);
725 (void) sigdelset(&newMask, SIGQUIT);
726 (void) sigdelset(&newMask, SIGHUP);
727 (void) sigdelset(&newMask, SIGTERM);
728 (void) sigdelset(&newMask, SIGPIPE);
729 return sigprocmask(SIG_BLOCK, &newMask, NULL);
733 * Restore signal mask.
736 static int unblockSignals(/*@unused@*/ rpmdb db, sigset_t * oldMask)
737 /*@globals rpmdbRock, fileSystem, internalState @*/
738 /*@modifies rpmdbRock, fileSystem, internalState @*/
740 (void) rpmdbCheckSignals();
741 return sigprocmask(SIG_SETMASK, oldMask, NULL);
745 #define _DB_HOME "%{_dbpath}"
748 #define _DB_PERMS 0644
751 #define _DB_ERRPFX "rpmdb"
754 /*@observer@*/ /*@unchecked@*/
755 static struct rpmdb_s dbTemplate = {
756 _DB_ROOT, _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
757 _DB_MAJOR, _DB_ERRPFX
761 int rpmdbOpenAll(rpmdb db)
766 if (db == NULL) return -2;
769 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
770 if (db->_dbi[dbix] != NULL)
772 (void) dbiOpen(db, dbiTags[dbix], db->db_flags);
777 int rpmdbCloseDBI(rpmdb db, int rpmtag)
782 if (db == NULL || db->_dbi == NULL || dbiTags == NULL)
785 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
786 if (dbiTags[dbix] != rpmtag)
789 if (db->_dbi[dbix] != NULL) {
791 /*@-unqualifiedtrans@*/ /* FIX: double indirection. */
792 xx = dbiClose(db->_dbi[dbix], 0);
793 if (xx && rc == 0) rc = xx;
794 db->_dbi[dbix] = NULL;
795 /*@=unqualifiedtrans@*/
803 /* XXX query.c, rpminstall.c, verify.c */
805 int rpmdbClose(rpmdb db)
806 /*@globals rpmdbRock @*/
807 /*@modifies rpmdbRock @*/
816 (void) rpmdbUnlink(db, "rpmdbClose");
823 for (dbix = db->db_ndbi; --dbix >= 0; ) {
825 if (db->_dbi[dbix] == NULL)
827 /*@-unqualifiedtrans@*/ /* FIX: double indirection. */
828 xx = dbiClose(db->_dbi[dbix], 0);
829 if (xx && rc == 0) rc = xx;
830 db->_dbi[dbix] = NULL;
831 /*@=unqualifiedtrans@*/
833 db->db_errpfx = _free(db->db_errpfx);
834 db->db_root = _free(db->db_root);
835 db->db_home = _free(db->db_home);
836 db->db_bits = PBM_FREE(db->db_bits);
837 db->_dbi = _free(db->_dbi);
841 while ((next = *prev) != NULL && next != db)
842 prev = &next->db_next;
844 /*@i@*/ *prev = next->db_next;
845 next->db_next = NULL;
849 /*@-refcounttrans@*/ db = _free(db); /*@=refcounttrans@*/
853 (void) rpmsqEnable(-SIGHUP, NULL);
854 (void) rpmsqEnable(-SIGINT, NULL);
855 (void) rpmsqEnable(-SIGTERM,NULL);
856 (void) rpmsqEnable(-SIGQUIT,NULL);
857 (void) rpmsqEnable(-SIGPIPE,NULL);
862 int rpmdbSync(rpmdb db)
867 if (db == NULL) return 0;
868 for (dbix = 0; dbix < db->db_ndbi; dbix++) {
870 if (db->_dbi[dbix] == NULL)
872 xx = dbiSync(db->_dbi[dbix], 0);
873 if (xx && rc == 0) rc = xx;
878 /*@-mods@*/ /* FIX: dbTemplate structure assignment */
879 static /*@only@*/ /*@null@*/
880 rpmdb newRpmdb(/*@kept@*/ /*@null@*/ const char * root,
881 /*@kept@*/ /*@null@*/ const char * home,
882 int mode, int perms, int flags)
883 /*@globals _db_filter_dups, rpmGlobalMacroContext @*/
884 /*@modifies _db_filter_dups, rpmGlobalMacroContext @*/
886 rpmdb db = xcalloc(sizeof(*db), 1);
887 const char * epfx = _DB_ERRPFX;
888 static int _initialized = 0;
891 _db_filter_dups = rpmExpandNumeric("%{_filterdbdups}");
897 *db = dbTemplate; /* structure assignment */
903 if (!(perms & 0600)) perms = 0644; /* XXX sanity */
905 if (mode >= 0) db->db_mode = mode;
906 if (perms >= 0) db->db_perms = perms;
907 if (flags >= 0) db->db_flags = flags;
910 db->db_root = rpmGetPath( (root && *root ? root : _DB_ROOT), NULL);
911 db->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL);
913 if (!(db->db_home && db->db_home[0] != '%')) {
914 rpmError(RPMERR_DBOPEN, _("no dbpath has been set\n"));
915 db->db_root = _free(db->db_root);
916 db->db_home = _free(db->db_home);
918 /*@-globstate@*/ return NULL; /*@=globstate@*/
920 db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
921 db->db_remove_env = 0;
922 db->db_filter_dups = _db_filter_dups;
923 db->db_ndbi = dbiTagsMax;
924 db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
927 return rpmdbLink(db, "rpmdbCreate");
932 static int openDatabase(/*@null@*/ const char * prefix,
933 /*@null@*/ const char * dbpath,
934 int _dbapi, /*@null@*/ /*@out@*/ rpmdb *dbp,
935 int mode, int perms, int flags)
936 /*@globals rpmdbRock, rpmGlobalMacroContext,
937 fileSystem, internalState @*/
938 /*@modifies rpmdbRock, *dbp, rpmGlobalMacroContext,
939 fileSystem, internalState @*/
940 /*@requires maxSet(dbp) >= 0 @*/
944 static int _tags_initialized = 0;
945 int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
946 int minimal = flags & RPMDB_FLAG_MINIMAL;
948 if (!_tags_initialized || dbiTagsMax == 0) {
953 /* Insure that _dbapi has one of -1, 1, 2, or 3 */
954 if (_dbapi < -1 || _dbapi > 3)
964 db = newRpmdb(prefix, dbpath, mode, perms, flags);
968 (void) rpmsqEnable(SIGHUP, NULL);
969 (void) rpmsqEnable(SIGINT, NULL);
970 (void) rpmsqEnable(SIGTERM,NULL);
971 (void) rpmsqEnable(SIGQUIT,NULL);
972 (void) rpmsqEnable(SIGPIPE,NULL);
980 for (dbix = 0; rc == 0 && dbix < dbiTagsMax; dbix++) {
984 /* Filter out temporary databases */
985 switch ((rpmtag = dbiTags[dbix])) {
986 case RPMDBI_AVAILABLE:
991 /*@notreached@*/ /*@switchbreak@*/ break;
993 /*@switchbreak@*/ break;
996 dbi = dbiOpen(db, rpmtag, 0);
1003 case RPMDBI_PACKAGES:
1004 if (dbi == NULL) rc |= 1;
1006 /* XXX open only Packages, indices created on the fly. */
1007 if (db->db_api == 3)
1010 /*@notreached@*/ /*@switchbreak@*/ break;
1012 if (dbi == NULL) rc |= 1;
1015 /*@switchbreak@*/ break;
1017 /*@switchbreak@*/ break;
1023 if (rc || justCheck || dbp == NULL)
1024 xx = rpmdbClose(db);
1026 /*@-assignexpose -newreftrans@*/
1027 /*@i@*/ db->db_next = rpmdbRock;
1030 /*@=assignexpose =newreftrans@*/
1036 rpmdb XrpmdbUnlink(rpmdb db, const char * msg, const char * fn, unsigned ln)
1040 fprintf(stderr, "--> db %p -- %d %s at %s:%u\n", db, db->nrefs, msg, fn, ln);
1046 rpmdb XrpmdbLink(rpmdb db, const char * msg, const char * fn, unsigned ln)
1051 fprintf(stderr, "--> db %p ++ %d %s at %s:%u\n", db, db->nrefs, msg, fn, ln);
1053 /*@-refcounttrans@*/ return db; /*@=refcounttrans@*/
1056 /* XXX python/rpmmodule.c */
1057 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
1059 int _dbapi = rpmExpandNumeric("%{_dbapi}");
1061 return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
1065 int rpmdbInit (const char * prefix, int perms)
1068 int _dbapi = rpmExpandNumeric("%{_dbapi}");
1072 rc = openDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR),
1073 perms, RPMDB_FLAG_JUSTCHECK);
1077 xx = rpmdbOpenAll(db);
1078 if (xx && rc == 0) rc = xx;
1079 xx = rpmdbClose(db);
1080 if (xx && rc == 0) rc = xx;
1086 int rpmdbVerify(const char * prefix)
1089 int _dbapi = rpmExpandNumeric("%{_dbapi}");
1093 rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0);
1099 rc = rpmdbOpenAll(db);
1101 for (dbix = db->db_ndbi; --dbix >= 0; ) {
1102 if (db->_dbi[dbix] == NULL)
1104 /*@-unqualifiedtrans@*/ /* FIX: double indirection. */
1105 xx = dbiVerify(db->_dbi[dbix], 0);
1106 if (xx && rc == 0) rc = xx;
1107 db->_dbi[dbix] = NULL;
1108 /*@=unqualifiedtrans@*/
1111 /*@-nullstate@*/ /* FIX: db->_dbi[] may be NULL. */
1112 xx = rpmdbClose(db);
1114 if (xx && rc == 0) rc = xx;
1121 * Find file matches in database.
1122 * @param db rpm database
1127 * @return 0 on success, 1 on not found, -2 on error
1129 static int rpmdbFindByFile(rpmdb db, /*@null@*/ const char * filespec,
1130 DBT * key, DBT * data, /*@out@*/ dbiIndexSet * matches)
1131 /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
1132 /*@modifies db, *key, *data, *matches, rpmGlobalMacroContext,
1133 fileSystem, internalState @*/
1134 /*@requires maxSet(matches) >= 0 @*/
1136 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
1137 HFD_t hfd = headerFreeData;
1138 const char * dirName;
1139 const char * baseName;
1140 rpmTagType bnt, dnt;
1141 fingerPrintCache fpc;
1143 dbiIndex dbi = NULL;
1145 dbiIndexSet allMatches = NULL;
1146 dbiIndexItem rec = NULL;
1152 if (filespec == NULL) return -2;
1155 if ((baseName = strrchr(filespec, '/')) != NULL) {
1159 len = baseName - filespec + 1;
1161 t = strncpy(alloca(len + 1), filespec, len);
1168 baseName = filespec;
1171 if (baseName == NULL)
1174 fpc = fpCacheCreate(20);
1175 fp1 = fpLookup(fpc, dirName, baseName, 1);
1177 dbi = dbiOpen(db, RPMTAG_BASENAMES, 0);
1181 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1184 key->data = (void *) baseName;
1186 key->size = strlen(baseName);
1187 if (key->size == 0) key->size++; /* XXX "/" fixup. */
1189 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1191 rpmError(RPMERR_DBGETINDEX,
1192 _("error(%d) getting \"%s\" records from %s index\n"),
1193 rc, key->data, tagName(dbi->dbi_rpmtag));
1197 (void) dbt2set(dbi, data, &allMatches);
1199 xx = dbiCclose(dbi, dbcursor, 0);
1206 allMatches = dbiFreeIndexSet(allMatches);
1207 fpc = fpCacheFree(fpc);
1211 *matches = xcalloc(1, sizeof(**matches));
1212 rec = dbiIndexNewItem(0, 0);
1214 if (allMatches != NULL)
1215 while (i < allMatches->count) {
1216 const char ** baseNames, ** dirNames;
1217 int_32 * dirIndexes;
1218 unsigned int offset = dbiIndexRecordOffset(allMatches, i);
1219 unsigned int prevoff;
1222 { rpmdbMatchIterator mi;
1223 mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset));
1224 h = rpmdbNextIterator(mi);
1227 mi = rpmdbFreeIterator(mi);
1235 xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, NULL);
1236 xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
1237 xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
1241 int num = dbiIndexRecordFileNumber(allMatches, i);
1243 fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
1245 if (FP_EQUAL(fp1, fp2)) {
1247 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
1248 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
1249 xx = dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
1254 if (i < allMatches->count)
1255 offset = dbiIndexRecordOffset(allMatches, i);
1256 } while (i < allMatches->count && offset == prevoff);
1258 baseNames = hfd(baseNames, bnt);
1259 dirNames = hfd(dirNames, dnt);
1264 allMatches = dbiFreeIndexSet(allMatches);
1266 fpc = fpCacheFree(fpc);
1268 if ((*matches)->count == 0) {
1269 *matches = dbiFreeIndexSet(*matches);
1276 /* XXX python/upgrade.c, install.c, uninstall.c */
1277 int rpmdbCountPackages(rpmdb db, const char * name)
1279 DBC * dbcursor = NULL;
1280 DBT * key = alloca(sizeof(*key));
1281 DBT * data = alloca(sizeof(*data));
1289 memset(key, 0, sizeof(*key));
1290 memset(data, 0, sizeof(*data));
1292 dbi = dbiOpen(db, RPMTAG_NAME, 0);
1297 key->data = (void *) name;
1299 key->size = strlen(name);
1301 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1302 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1303 xx = dbiCclose(dbi, dbcursor, 0);
1306 if (rc == 0) { /* success */
1307 dbiIndexSet matches;
1308 /*@-nullpass@*/ /* FIX: matches might be NULL */
1310 (void) dbt2set(dbi, data, &matches);
1312 rc = dbiIndexSetCount(matches);
1313 matches = dbiFreeIndexSet(matches);
1317 if (rc == DB_NOTFOUND) { /* not found */
1319 } else { /* error */
1320 rpmError(RPMERR_DBGETINDEX,
1321 _("error(%d) getting \"%s\" records from %s index\n"),
1322 rc, key->data, tagName(dbi->dbi_rpmtag));
1330 * Attempt partial matches on name[-version[-release]] strings.
1331 * @param dbi index database handle (always RPMTAG_NAME)
1332 * @param dbcursor index database cursor
1333 * @param key search key/length/flags
1334 * @param data search data/length/flags
1335 * @param name package name
1336 * @param version package version (can be a pattern)
1337 * @param release package release (can be a pattern)
1338 * @retval matches set of header instances that match
1339 * @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1341 static rpmRC dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
1342 DBT * key, DBT * data,
1344 /*@null@*/ const char * version,
1345 /*@null@*/ const char * release,
1346 /*@out@*/ dbiIndexSet * matches)
1347 /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
1348 /*@modifies dbi, *dbcursor, *key, *data, *matches,
1349 rpmGlobalMacroContext, fileSystem, internalState @*/
1350 /*@requires maxSet(matches) >= 0 @*/
1357 key->data = (void *) name;
1359 key->size = strlen(name);
1361 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1363 if (rc == 0) { /* success */
1364 (void) dbt2set(dbi, data, matches);
1365 if (version == NULL && release == NULL)
1368 if (rc == DB_NOTFOUND) { /* not found */
1369 return RPMRC_NOTFOUND;
1370 } else { /* error */
1371 rpmError(RPMERR_DBGETINDEX,
1372 _("error(%d) getting \"%s\" records from %s index\n"),
1373 rc, key->data, tagName(dbi->dbi_rpmtag));
1377 /* Make sure the version and release match. */
1379 for (i = 0; i < dbiIndexSetCount(*matches); i++) {
1380 unsigned int recoff = dbiIndexRecordOffset(*matches, i);
1381 rpmdbMatchIterator mi;
1387 mi = rpmdbInitIterator(dbi->dbi_rpmdb,
1388 RPMDBI_PACKAGES, &recoff, sizeof(recoff));
1390 /* Set iterator selectors for version/release if available. */
1392 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version))
1398 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release))
1404 h = rpmdbNextIterator(mi);
1407 (*matches)->recs[gotMatches++] = (*matches)->recs[i];
1409 (*matches)->recs[i].hdrNum = 0;
1411 mi = rpmdbFreeIterator(mi);
1416 (*matches)->count = gotMatches;
1419 rc = RPMRC_NOTFOUND;
1422 /*@-unqualifiedtrans@*/ /* FIX: double indirection */
1423 if (rc && matches && *matches)
1424 *matches = dbiFreeIndexSet(*matches);
1425 /*@=unqualifiedtrans@*/
1430 * Lookup by name, name-version, and finally by name-version-release.
1431 * Both version and release can be patterns.
1432 * @todo Name must be an exact match, as name is a db key.
1433 * @param dbi index database handle (always RPMTAG_NAME)
1434 * @param dbcursor index database cursor
1435 * @param key search key/length/flags
1436 * @param data search data/length/flags
1437 * @param arg name[-version[-release]] string
1438 * @retval matches set of header instances that match
1439 * @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1441 static rpmRC dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
1442 /*@null@*/ const char * arg, /*@out@*/ dbiIndexSet * matches)
1443 /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
1444 /*@modifies dbi, *dbcursor, *key, *data, *matches,
1445 rpmGlobalMacroContext, fileSystem, internalState @*/
1446 /*@requires maxSet(matches) >= 0 @*/
1448 const char * release;
1455 if (arg == NULL || strlen(arg) == 0) return RPMRC_NOTFOUND;
1457 /* did they give us just a name? */
1458 rc = dbiFindMatches(dbi, dbcursor, key, data, arg, NULL, NULL, matches);
1459 if (rc != RPMRC_NOTFOUND) return rc;
1461 /*@-unqualifiedtrans@*/ /* FIX: double indirection */
1462 *matches = dbiFreeIndexSet(*matches);
1463 /*@=unqualifiedtrans@*/
1465 /* maybe a name and a release */
1466 localarg = alloca(strlen(arg) + 1);
1467 s = stpcpy(localarg, arg);
1471 for (s -= 1; s > localarg; s--) {
1475 /*@switchbreak@*/ break;
1477 if (c != '[') brackets = 0;
1478 /*@switchbreak@*/ break;
1481 if (!brackets && *s == '-')
1485 /*@-nullstate@*/ /* FIX: *matches may be NULL. */
1486 if (s == localarg) return RPMRC_NOTFOUND;
1491 rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, NULL, matches);
1493 if (rc != RPMRC_NOTFOUND) return rc;
1495 /*@-unqualifiedtrans@*/ /* FIX: double indirection */
1496 *matches = dbiFreeIndexSet(*matches);
1497 /*@=unqualifiedtrans@*/
1499 /* how about name-version-release? */
1505 for (; s > localarg; s--) {
1509 /*@switchbreak@*/ break;
1511 if (c != '[') brackets = 0;
1512 /*@switchbreak@*/ break;
1515 if (!brackets && *s == '-')
1519 if (s == localarg) return RPMRC_NOTFOUND;
1524 /*@-nullstate@*/ /* FIX: *matches may be NULL. */
1525 return dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, release, matches);
1530 * Rewrite a header into packages (if necessary) and free the header.
1531 * Note: this is called from a markReplacedFiles iteration, and *must*
1532 * preserve the "join key" (i.e. offset) for the header.
1533 * @param mi database iterator
1534 * @param dbi index database handle
1535 * @return 0 on success
1537 static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi)
1538 /*@globals fileSystem, internalState @*/
1539 /*@modifies mi, dbi, fileSystem, internalState @*/
1543 if (mi == NULL || mi->mi_h == NULL)
1546 if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
1547 DBT * key = &mi->mi_key;
1548 DBT * data = &mi->mi_data;
1549 sigset_t signalMask;
1550 rpmRC rpmrc = RPMRC_NOTFOUND;
1553 /*@i@*/ key->data = (void *) &mi->mi_prevoffset;
1554 key->size = sizeof(mi->mi_prevoffset);
1555 data->data = headerUnload(mi->mi_h);
1556 data->size = headerSizeof(mi->mi_h, HEADER_MAGIC_NO);
1558 /* Check header digest/signature on blob export (if requested). */
1559 if (mi->mi_hdrchk && mi->mi_ts) {
1560 const char * msg = NULL;
1563 rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, data->data, data->size, &msg);
1564 lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG);
1565 rpmMessage(lvl, "%s h#%8u %s",
1566 (rpmrc == RPMRC_FAIL ? _("miFreeHeader: skipping") : "write"),
1567 mi->mi_prevoffset, (msg ? msg : "\n"));
1571 if (data->data != NULL && rpmrc != RPMRC_FAIL) {
1572 (void) blockSignals(dbi->dbi_rpmdb, &signalMask);
1573 rc = dbiPut(dbi, mi->mi_dbc, key, data, DB_KEYLAST);
1575 rpmError(RPMERR_DBPUTINDEX,
1576 _("error(%d) storing record #%d into %s\n"),
1577 rc, mi->mi_prevoffset, tagName(dbi->dbi_rpmtag));
1579 xx = dbiSync(dbi, 0);
1580 (void) unblockSignals(dbi->dbi_rpmdb, &signalMask);
1582 data->data = _free(data->data);
1586 mi->mi_h = headerFree(mi->mi_h);
1593 rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
1594 /*@globals rpmmiRock @*/
1595 /*@modifies rpmmiRock @*/
1597 rpmdbMatchIterator * prev, next;
1606 while ((next = *prev) != NULL && next != mi)
1607 prev = &next->mi_next;
1609 /*@i@*/ *prev = next->mi_next;
1610 next->mi_next = NULL;
1613 dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
1614 if (dbi == NULL) /* XXX can't happen */
1617 xx = miFreeHeader(mi, dbi);
1620 xx = dbiCclose(dbi, mi->mi_dbc, 0);
1623 if (mi->mi_re != NULL)
1624 for (i = 0; i < mi->mi_nre; i++) {
1625 miRE mire = mi->mi_re + i;
1626 mire->pattern = _free(mire->pattern);
1627 if (mire->preg != NULL) {
1628 regfree(mire->preg);
1629 /*@+voidabstract -usereleased @*/ /* LCL: regfree has bogus only */
1630 mire->preg = _free(mire->preg);
1631 /*@=voidabstract =usereleased @*/
1634 mi->mi_re = _free(mi->mi_re);
1636 mi->mi_set = dbiFreeIndexSet(mi->mi_set);
1637 mi->mi_keyp = _free(mi->mi_keyp);
1638 mi->mi_db = rpmdbUnlink(mi->mi_db, "matchIterator");
1642 (void) rpmdbCheckSignals();
1647 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
1648 return (mi ? mi->mi_offset : 0);
1651 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
1652 return (mi ? mi->mi_filenum : 0);
1655 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
1656 return (mi && mi->mi_set ? mi->mi_set->count : 0);
1660 * Return pattern match.
1661 * @param mire match iterator regex
1662 * @param val value to match
1663 * @return 0 if pattern matches, >0 on nomatch, <0 on error
1665 static int miregexec(miRE mire, const char * val)
1670 switch (mire->mode) {
1671 case RPMMIRE_STRCMP:
1672 rc = strcmp(mire->pattern, val);
1675 case RPMMIRE_DEFAULT:
1678 rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
1680 if (rc && rc != REG_NOMATCH) {
1682 (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
1683 msg[sizeof(msg)-1] = '\0';
1684 rpmError(RPMERR_REGEXEC, "%s: regexec failed: %s\n",
1685 mire->pattern, msg);
1690 rc = fnmatch(mire->pattern, val, mire->fnflags);
1691 if (rc && rc != FNM_NOMATCH)
1703 * Compare iterator selectors by rpm tag (qsort/bsearch).
1704 * @param a 1st iterator selector
1705 * @param b 2nd iterator selector
1706 * @return result of comparison
1708 static int mireCmp(const void * a, const void * b)
1710 const miRE mireA = (const miRE) a;
1711 const miRE mireB = (const miRE) b;
1712 return (mireA->tag - mireB->tag);
1716 * Copy pattern, escaping for appropriate mode.
1717 * @param tag rpm tag
1718 * @retval modep type of pattern match
1719 * @param pattern pattern to duplicate
1720 * @return duplicated pattern
1722 static /*@only@*/ char * mireDup(rpmTag tag, rpmMireMode *modep,
1723 const char * pattern)
1724 /*@modifies *modep @*/
1725 /*@requires maxSet(modep) >= 0 @*/
1737 case RPMMIRE_DEFAULT:
1738 if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
1739 *modep = RPMMIRE_GLOB;
1740 pat = xstrdup(pattern);
1744 nb = strlen(pattern) + sizeof("^$");
1746 /* Find no. of bytes needed for pattern. */
1747 /* periods are escaped, splats become '.*' */
1750 for (s = pattern; *s != '\0'; s++) {
1754 if (!brackets) nb++;
1755 /*@switchbreak@*/ break;
1758 /*@switchbreak@*/ break;
1761 /*@switchbreak@*/ break;
1763 if (c != '[') brackets = 0;
1764 /*@switchbreak@*/ break;
1769 pat = t = xmalloc(nb);
1771 if (pattern[0] != '^') *t++ = '^';
1773 /* Copy pattern, escaping periods, prefixing splats with period. */
1776 for (s = pattern; *s != '\0'; s++, t++) {
1779 if (!brackets) *t++ = '\\';
1780 /*@switchbreak@*/ break;
1782 if (!brackets) *t++ = '.';
1783 /*@switchbreak@*/ break;
1786 /*@switchbreak@*/ break;
1789 /*@switchbreak@*/ break;
1791 if (c != '[') brackets = 0;
1792 /*@switchbreak@*/ break;
1797 if (s > pattern && s[-1] != '$') *t++ = '$';
1799 *modep = RPMMIRE_REGEX;
1801 case RPMMIRE_STRCMP:
1804 pat = xstrdup(pattern);
1812 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
1813 rpmMireMode mode, const char * pattern)
1815 static rpmMireMode defmode = (rpmMireMode)-1;
1817 const char * allpat = NULL;
1819 regex_t * preg = NULL;
1826 if (defmode == (rpmMireMode)-1) {
1827 const char *t = rpmExpand("%{?_query_selector_match}", NULL);
1829 if (*t == '\0' || !strcmp(t, "default"))
1830 defmode = RPMMIRE_DEFAULT;
1831 else if (!strcmp(t, "strcmp"))
1832 defmode = RPMMIRE_STRCMP;
1833 else if (!strcmp(t, "regex"))
1834 defmode = RPMMIRE_REGEX;
1835 else if (!strcmp(t, "glob"))
1836 defmode = RPMMIRE_GLOB;
1838 defmode = RPMMIRE_DEFAULT;
1842 if (mi == NULL || pattern == NULL)
1845 /* Leading '!' inverts pattern match sense, like "grep -v". */
1846 if (*pattern == '!') {
1853 allpat = mireDup(tag, &mode, pattern);
1856 if (mode == RPMMIRE_DEFAULT)
1861 case RPMMIRE_DEFAULT:
1862 case RPMMIRE_STRCMP:
1866 preg = xcalloc(1, sizeof(*preg));
1868 cflags = (REG_EXTENDED | REG_NOSUB);
1869 rc = regcomp(preg, allpat, cflags);
1872 (void) regerror(rc, preg, msg, sizeof(msg)-1);
1873 msg[sizeof(msg)-1] = '\0';
1874 rpmError(RPMERR_REGCOMP, "%s: regcomp failed: %s\n", allpat, msg);
1878 fnflags = FNM_PATHNAME | FNM_PERIOD;
1887 /*@=kepttrans@*/ /* FIX: mire has kept values */
1888 allpat = _free(allpat);
1891 /*@+voidabstract -usereleased @*/ /* LCL: regfree has bogus only */
1893 /*@=voidabstract =usereleased @*/
1899 mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
1900 mire = mi->mi_re + mi->mi_nre;
1905 mire->pattern = allpat;
1906 mire->notmatch = notmatch;
1908 mire->cflags = cflags;
1909 mire->eflags = eflags;
1910 mire->fnflags = fnflags;
1914 qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
1921 * Return iterator selector match.
1922 * @param mi rpm database iterator
1923 * @return 1 if header should be skipped
1925 static int mireSkip (const rpmdbMatchIterator mi)
1928 HGE_t hge = (HGE_t) headerGetEntryMinMemory;
1929 HFD_t hfd = (HFD_t) headerFreeData;
1942 static int_32 zero = 0;
1948 if (mi->mi_h == NULL) /* XXX can't happen */
1952 * Apply tag tests, implicitly "||" for multiple patterns/values of a
1953 * single tag, implicitly "&&" between multiple tag patterns.
1956 if ((mire = mi->mi_re) != NULL)
1957 for (i = 0; i < mi->mi_nre; i++, mire++) {
1960 if (!hge(mi->mi_h, mire->tag, &t, (void **)&u, &c)) {
1961 if (mire->tag != RPMTAG_EPOCH)
1964 /*@-immediatetrans@*/
1966 /*@=immediatetrans@*/
1970 anymatch = 0; /* no matches yet */
1975 sprintf(numbuf, "%d", (int) *u.i8p);
1976 rc = miregexec(mire, numbuf);
1977 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
1979 /*@switchbreak@*/ break;
1980 case RPM_INT16_TYPE:
1981 sprintf(numbuf, "%d", (int) *u.i16p);
1982 rc = miregexec(mire, numbuf);
1983 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
1985 /*@switchbreak@*/ break;
1986 case RPM_INT32_TYPE:
1987 sprintf(numbuf, "%d", (int) *u.i32p);
1988 rc = miregexec(mire, numbuf);
1989 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
1991 /*@switchbreak@*/ break;
1992 case RPM_STRING_TYPE:
1993 rc = miregexec(mire, u.str);
1994 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
1996 /*@switchbreak@*/ break;
1997 case RPM_I18NSTRING_TYPE:
1998 case RPM_STRING_ARRAY_TYPE:
1999 for (j = 0; j < c; j++) {
2000 rc = miregexec(mire, u.argv[j]);
2001 if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) {
2003 /*@innerbreak@*/ break;
2006 /*@switchbreak@*/ break;
2010 /*@switchbreak@*/ break;
2012 if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
2015 /*@innercontinue@*/ continue;
2017 /*@innerbreak@*/ break;
2021 u.ptr = hfd(u.ptr, t);
2028 return (ntags == nmatches ? 0 : 1);
2031 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite)
2036 rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0;
2038 mi->mi_cflags |= DB_WRITECURSOR;
2040 mi->mi_cflags &= ~DB_WRITECURSOR;
2044 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified)
2049 rc = mi->mi_modified;
2050 mi->mi_modified = modified;
2054 int rpmdbSetHdrChk(rpmdbMatchIterator mi, rpmts ts,
2055 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
2060 /*@-assignexpose -newreftrans @*/
2061 /*@i@*/ mi->mi_ts = ts;
2062 mi->mi_hdrchk = hdrchk;
2063 /*@=assignexpose =newreftrans @*/
2068 /*@-nullstate@*/ /* FIX: mi->mi_key.data may be NULL */
2069 Header rpmdbNextIterator(rpmdbMatchIterator mi)
2084 dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
2089 * Cursors are per-iterator, not per-dbi, so get a cursor for the
2090 * iterator on 1st call. If the iteration is to rewrite headers, and the
2091 * CDB model is used for the database, then the cursor needs to
2092 * marked with DB_WRITECURSOR as well.
2094 if (mi->mi_dbc == NULL)
2095 xx = dbiCopen(dbi, dbi->dbi_txnid, &mi->mi_dbc, mi->mi_cflags);
2099 memset(key, 0, sizeof(*key));
2100 data = &mi->mi_data;
2101 memset(data, 0, sizeof(*data));
2109 /*@-branchstate -compmempass @*/
2111 if (!(mi->mi_setx < mi->mi_set->count))
2113 mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
2114 mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
2115 keyp = &mi->mi_offset;
2116 keylen = sizeof(mi->mi_offset);
2119 key->data = keyp = (void *)mi->mi_keyp;
2120 key->size = keylen = mi->mi_keylen;
2123 #if !defined(_USE_COPY_LOAD)
2124 data->flags |= DB_DBT_MALLOC;
2126 rc = dbiGet(dbi, mi->mi_dbc, key, data,
2127 (key->data == NULL ? DB_NEXT : DB_SET));
2135 * If we got the next key, save the header instance number.
2137 * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
2138 * largest header instance in the database, and should be
2142 if (keyp && mi->mi_setx && rc == 0)
2143 memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
2146 /* Terminate on error or end of keys */
2147 if (rc || (mi->mi_setx && mi->mi_offset == 0))
2150 /*@=branchstate =compmempass @*/
2152 } while (mi->mi_offset == 0);
2154 /* If next header is identical, return it now. */
2155 /*@-compdef -refcounttrans -retalias -retexpose -usereleased @*/
2156 if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
2158 /*@=compdef =refcounttrans =retalias =retexpose =usereleased @*/
2160 /* Retrieve next header blob for index iterator. */
2161 /*@-branchstate -compmempass -immediatetrans @*/
2165 #if !defined(_USE_COPY_LOAD)
2166 data->flags |= DB_DBT_MALLOC;
2168 rc = dbiGet(dbi, mi->mi_dbc, key, data, DB_SET);
2177 /*@=branchstate =compmempass =immediatetrans @*/
2179 /* Rewrite current header (if necessary) and unlink. */
2180 xx = miFreeHeader(mi, dbi);
2182 /* Is this the end of the iteration? */
2186 /* Check header digest/signature once (if requested). */
2187 /*@-boundsread -branchstate -sizeoftype @*/
2188 if (mi->mi_hdrchk && mi->mi_ts) {
2189 rpmRC rpmrc = RPMRC_NOTFOUND;
2190 pbm_set * set = NULL;
2192 /* Don't bother re-checking a previously read header. */
2193 if (mi->mi_db->db_bits) {
2194 set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
2195 &mi->mi_db->db_nbits, mi->mi_offset);
2196 if (PBM_ISSET(mi->mi_offset, set))
2200 /* If blob is unchecked, check blob import consistency now. */
2201 if (rpmrc != RPMRC_OK) {
2202 const char * msg = NULL;
2205 rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg);
2206 lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG);
2207 rpmMessage(lvl, "%s h#%8u %s",
2208 (rpmrc == RPMRC_FAIL ? _("rpmdbNextIterator: skipping") : " read"),
2209 mi->mi_offset, (msg ? msg : "\n"));
2212 /* Mark header checked. */
2213 if (set && rpmrc == RPMRC_OK)
2214 PBM_SET(mi->mi_offset, set);
2216 /* Skip damaged and inconsistent headers. */
2217 if (rpmrc == RPMRC_FAIL)
2221 /*@=boundsread =branchstate =sizeoftype @*/
2223 /* Did the header blob load correctly? */
2224 #if !defined(_USE_COPY_LOAD)
2226 mi->mi_h = headerLoad(uh);
2229 mi->mi_h->flags |= HEADERFLAG_ALLOCATED;
2231 mi->mi_h = headerCopyLoad(uh);
2233 if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
2234 rpmError(RPMERR_BADHEADER,
2235 _("rpmdb: damaged header #%u retrieved -- skipping.\n"),
2241 * Skip this header if iterator selector (if any) doesn't match.
2244 /* XXX hack, can't restart with Packages locked on single instance. */
2245 if (mi->mi_set || mi->mi_keyp == NULL)
2250 mi->mi_prevoffset = mi->mi_offset;
2251 mi->mi_modified = 0;
2253 /*@-compdef -retalias -retexpose -usereleased @*/
2255 /*@=compdef =retalias =retexpose =usereleased @*/
2259 static void rpmdbSortIterator(/*@null@*/ rpmdbMatchIterator mi)
2262 if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
2264 * mergesort is much (~10x with lots of identical basenames) faster
2265 * than pure quicksort, but glibc uses msort_with_tmp() on stack.
2267 #if defined(__GLIBC__)
2269 qsort(mi->mi_set->recs, mi->mi_set->count,
2270 sizeof(*mi->mi_set->recs), hdrNumCmp);
2273 mergesort(mi->mi_set->recs, mi->mi_set->count,
2274 sizeof(*mi->mi_set->recs), hdrNumCmp);
2280 /*@-bounds@*/ /* LCL: segfault */
2281 static int rpmdbGrowIterator(/*@null@*/ rpmdbMatchIterator mi, int fpNum)
2282 /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
2283 /*@modifies mi, rpmGlobalMacroContext, fileSystem, internalState @*/
2288 dbiIndex dbi = NULL;
2297 dbcursor = mi->mi_dbc;
2299 data = &mi->mi_data;
2300 if (key->data == NULL)
2303 dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0);
2307 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2308 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2309 xx = dbiCclose(dbi, dbcursor, 0);
2312 if (rc) { /* error/not found */
2313 if (rc != DB_NOTFOUND)
2314 rpmError(RPMERR_DBGETINDEX,
2315 _("error(%d) getting \"%s\" records from %s index\n"),
2316 rc, key->data, tagName(dbi->dbi_rpmtag));
2321 (void) dbt2set(dbi, data, &set);
2322 for (i = 0; i < set->count; i++)
2323 set->recs[i].fpNum = fpNum;
2326 if (mi->mi_set == NULL) {
2330 fprintf(stderr, "+++ %d = %d + %d\t\"%s\"\n", (mi->mi_set->count + set->count), mi->mi_set->count, set->count, ((char *)key->data));
2332 mi->mi_set->recs = xrealloc(mi->mi_set->recs,
2333 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
2334 memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
2335 set->count * sizeof(*(mi->mi_set->recs)));
2336 mi->mi_set->count += set->count;
2337 set = dbiFreeIndexSet(set);
2345 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
2346 int nHdrNums, int sorted)
2348 if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2352 (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
2356 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
2358 if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2361 if (mi->mi_set == NULL)
2362 mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
2363 (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
2367 rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmTag rpmtag,
2368 const void * keyp, size_t keylen)
2369 /*@globals rpmmiRock @*/
2370 /*@modifies rpmmiRock @*/
2372 rpmdbMatchIterator mi;
2375 dbiIndexSet set = NULL;
2377 const void * mi_keyp = NULL;
2383 (void) rpmdbCheckSignals();
2385 /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
2386 if (rpmtag == RPMDBI_LABEL) {
2387 rpmtag = RPMTAG_NAME;
2391 dbi = dbiOpen(db, rpmtag, 0);
2395 mi = xcalloc(1, sizeof(*mi));
2396 mi->mi_next = rpmmiRock;
2400 data = &mi->mi_data;
2403 if (rpmtag != RPMDBI_PACKAGES && keyp) {
2404 DBC * dbcursor = NULL;
2409 /* XXX HACK to get rpmdbFindByLabel out of the API */
2410 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2411 rc = dbiFindByLabel(dbi, dbcursor, key, data, keyp, &set);
2412 xx = dbiCclose(dbi, dbcursor, 0);
2414 } else if (rpmtag == RPMTAG_BASENAMES) {
2415 rc = rpmdbFindByFile(db, keyp, key, data, &set);
2417 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2420 key->data = (void *) keyp;
2423 if (key->data && key->size == 0) key->size = strlen((char *)key->data);
2424 if (key->data && key->size == 0) key->size++; /* XXX "/" fixup. */
2427 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2430 rpmError(RPMERR_DBGETINDEX,
2431 _("error(%d) getting \"%s\" records from %s index\n"),
2432 rc, (key->data ? key->data : "???"), tagName(dbi->dbi_rpmtag));
2436 (void) dbt2set(dbi, data, &set);
2438 xx = dbiCclose(dbi, dbcursor, 0);
2441 if (rc) { /* error/not found */
2442 set = dbiFreeIndexSet(set);
2443 rpmmiRock = mi->mi_next;
2454 if (rpmtag != RPMDBI_PACKAGES && keylen == 0)
2455 keylen = strlen(keyp);
2456 k = xmalloc(keylen + 1);
2458 memcpy(k, keyp, keylen);
2460 k[keylen] = '\0'; /* XXX for strings */
2464 mi->mi_keyp = mi_keyp;
2465 mi->mi_keylen = keylen;
2467 mi->mi_db = rpmdbLink(db, "matchIterator");
2468 mi->mi_rpmtag = rpmtag;
2476 mi->mi_modified = 0;
2477 mi->mi_prevoffset = 0;
2484 mi->mi_hdrchk = NULL;
2490 int rpmdbRemove(rpmdb db, /*@unused@*/ int rid, unsigned int hdrNum,
2491 /*@unused@*/ rpmts ts,
2492 /*@unused@*/ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
2494 DBC * dbcursor = NULL;
2495 DBT * key = alloca(sizeof(*key));
2496 DBT * data = alloca(sizeof(*data));
2497 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
2498 HFD_t hfd = headerFreeData;
2500 sigset_t signalMask;
2507 memset(key, 0, sizeof(*key));
2508 memset(data, 0, sizeof(*data));
2510 { rpmdbMatchIterator mi;
2511 mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
2512 h = rpmdbNextIterator(mi);
2515 mi = rpmdbFreeIterator(mi);
2519 rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
2520 "rpmdbRemove", hdrNum);
2525 /* Add remove transaction id to header. */
2526 if (rid != 0 && rid != -1) {
2528 (void) headerAddEntry(h, RPMTAG_REMOVETID, RPM_INT32_TYPE, &tid, 1);
2532 { const char *n, *v, *r;
2533 (void) headerNVR(h, &n, &v, &r);
2534 rpmMessage(RPMMESS_DEBUG, " --- h#%8u %s-%s-%s\n", hdrNum, n, v, r);
2537 (void) blockSignals(db, &signalMask);
2539 /*@-nullpass -nullptrarith -nullderef @*/ /* FIX: rpmvals heartburn */
2541 dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2543 if (dbiTags != NULL)
2544 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
2547 const char ** rpmvals = NULL;
2548 rpmTagType rpmtype = 0;
2556 rpmtag = dbiTags[dbix];
2561 /* Filter out temporary databases */
2562 case RPMDBI_AVAILABLE:
2564 case RPMDBI_REMOVED:
2565 case RPMDBI_DEPENDS:
2567 /*@notreached@*/ /*@switchbreak@*/ break;
2568 case RPMDBI_PACKAGES:
2569 dbi = dbiOpen(db, rpmtag, 0);
2570 if (dbi == NULL) /* XXX shouldn't happen */
2573 /*@-immediatetrans@*/
2574 key->data = &hdrNum;
2575 /*@=immediatetrans@*/
2576 key->size = sizeof(hdrNum);
2578 rc = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2579 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2581 rpmError(RPMERR_DBGETINDEX,
2582 _("error(%d) setting header #%d record for %s removal\n"),
2583 rc, hdrNum, tagName(dbi->dbi_rpmtag));
2585 rc = dbiDel(dbi, dbcursor, key, data, 0);
2586 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2588 if (!dbi->dbi_no_dbsync)
2589 xx = dbiSync(dbi, 0);
2591 /*@notreached@*/ /*@switchbreak@*/ break;
2595 if (!hge(h, rpmtag, &rpmtype, (void **) &rpmvals, &rpmcnt))
2598 dbi = dbiOpen(db, rpmtag, 0);
2602 if (rpmtype == RPM_STRING_TYPE) {
2603 /* XXX force uniform headerGetEntry return */
2604 av[0] = (const char *) rpmvals;
2610 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2612 for (i = 0; i < rpmcnt; i++) {
2617 switch (dbi->dbi_rpmtag) {
2618 case RPMTAG_FILEMD5S:
2619 /* Filter out empty MD5 strings. */
2620 if (!(rpmvals[i] && *rpmvals[i] != '\0'))
2621 /*@innercontinue@*/ continue;
2622 /*@switchbreak@*/ break;
2624 /*@switchbreak@*/ break;
2627 /* Identify value pointer and length. */
2633 key->size = sizeof(RPM_CHAR_TYPE);
2634 key->data = rpmvals + i;
2635 /*@switchbreak@*/ break;
2636 case RPM_INT16_TYPE:
2637 key->size = sizeof(int_16);
2638 key->data = rpmvals + i;
2639 /*@switchbreak@*/ break;
2640 case RPM_INT32_TYPE:
2641 key->size = sizeof(int_32);
2642 key->data = rpmvals + i;
2643 /*@switchbreak@*/ break;
2647 key->data = rpmvals;
2648 rpmcnt = 1; /* XXX break out of loop. */
2649 /*@switchbreak@*/ break;
2650 case RPM_STRING_TYPE:
2651 case RPM_I18NSTRING_TYPE:
2652 rpmcnt = 1; /* XXX break out of loop. */
2654 case RPM_STRING_ARRAY_TYPE:
2655 /* Convert from hex to binary. */
2657 if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) {
2663 for (j = 0; j < 16; j++, t++, s += 2)
2664 *t = (nibble(s[0]) << 4) | nibble(s[1]);
2667 /*@switchbreak@*/ break;
2669 /* Extract the pubkey id from the base64 blob. */
2670 if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
2671 pgpDig dig = pgpNewDig();
2675 if (b64decode(rpmvals[i], (void **)&pkt, &pktlen))
2676 /*@innercontinue@*/ continue;
2677 (void) pgpPrtPkts(pkt, pktlen, dig, 0);
2678 memcpy(bin, dig->pubkey.signid, 8);
2683 /*@switchbreak@*/ break;
2688 /*@i@*/ key->data = (void *) rpmvals[i];
2689 key->size = strlen(rpmvals[i]);
2691 /*@switchbreak@*/ break;
2695 if (rpmcnt == 1 && stringvalued) {
2696 rpmMessage(RPMMESS_DEBUG,
2697 _("removing \"%s\" from %s index.\n"),
2698 (char *)key->data, tagName(dbi->dbi_rpmtag));
2700 rpmMessage(RPMMESS_DEBUG,
2701 _("removing %d entries from %s index.\n"),
2702 rpmcnt, tagName(dbi->dbi_rpmtag));
2708 * This is almost right, but, if there are duplicate tag
2709 * values, there will be duplicate attempts to remove
2710 * the header instance. It's faster to just ignore errors
2711 * than to do things correctly.
2714 /* XXX with duplicates, an accurate data value and DB_GET_BOTH is needed. */
2718 if (key->size == 0) key->size = strlen((char *)key->data);
2719 if (key->size == 0) key->size++; /* XXX "/" fixup. */
2722 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2723 if (rc == 0) { /* success */
2724 (void) dbt2set(dbi, data, &set);
2725 } else if (rc == DB_NOTFOUND) { /* not found */
2726 /*@innercontinue@*/ continue;
2727 } else { /* error */
2728 rpmError(RPMERR_DBGETINDEX,
2729 _("error(%d) setting \"%s\" records from %s index\n"),
2730 rc, key->data, tagName(dbi->dbi_rpmtag));
2732 /*@innercontinue@*/ continue;
2736 rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
2738 /* If nothing was pruned, then don't bother updating. */
2740 set = dbiFreeIndexSet(set);
2741 /*@innercontinue@*/ continue;
2745 if (set->count > 0) {
2746 (void) set2dbt(dbi, data, set);
2747 rc = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
2749 rpmError(RPMERR_DBPUTINDEX,
2750 _("error(%d) storing record \"%s\" into %s\n"),
2751 rc, key->data, tagName(dbi->dbi_rpmtag));
2754 data->data = _free(data->data);
2757 rc = dbiDel(dbi, dbcursor, key, data, 0);
2759 rpmError(RPMERR_DBPUTINDEX,
2760 _("error(%d) removing record \"%s\" from %s\n"),
2761 rc, key->data, tagName(dbi->dbi_rpmtag));
2766 set = dbiFreeIndexSet(set);
2770 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2773 if (!dbi->dbi_no_dbsync)
2774 xx = dbiSync(dbi, 0);
2777 if (rpmtype != RPM_BIN_TYPE) /* XXX WTFO? HACK ALERT */
2778 rpmvals = hfd(rpmvals, rpmtype);
2785 /*@=nullpass =nullptrarith =nullderef @*/
2787 (void) unblockSignals(db, &signalMask);
2791 /* XXX return ret; */
2796 int rpmdbAdd(rpmdb db, int iid, Header h,
2797 /*@unused@*/ rpmts ts,
2798 /*@unused@*/ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
2800 DBC * dbcursor = NULL;
2801 DBT * key = alloca(sizeof(*key));
2802 DBT * data = alloca(sizeof(*data));
2803 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
2804 HFD_t hfd = headerFreeData;
2805 sigset_t signalMask;
2806 const char ** baseNames;
2811 unsigned int hdrNum = 0;
2819 memset(key, 0, sizeof(*key));
2820 memset(data, 0, sizeof(*data));
2822 #ifdef NOTYET /* XXX headerRemoveEntry() broken on dribbles. */
2823 xx = headerRemoveEntry(h, RPMTAG_REMOVETID);
2825 if (iid != 0 && iid != -1) {
2827 if (!headerIsEntry(h, RPMTAG_INSTALLTID))
2828 xx = headerAddEntry(h, RPMTAG_INSTALLTID, RPM_INT32_TYPE, &tid, 1);
2832 * If old style filename tags is requested, the basenames need to be
2833 * retrieved early, and the header needs to be converted before
2834 * being written to the package header database.
2837 xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, &count);
2842 (void) blockSignals(db, &signalMask);
2845 unsigned int firstkey = 0;
2846 void * keyp = &firstkey;
2847 size_t keylen = sizeof(firstkey);
2848 void * datap = NULL;
2851 dbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
2855 /* XXX db0: hack to pass sizeof header to fadAlloc */
2857 datalen = headerSizeof(h, HEADER_MAGIC_NO);
2859 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2861 /* Retrieve join key for next header instance. */
2866 /*@i@*/ data->data = datap;
2867 data->size = datalen;
2868 ret = dbiGet(dbi, dbcursor, key, data, DB_SET);
2872 datalen = data->size;
2877 if (ret == 0 && datap)
2878 memcpy(&hdrNum, datap, sizeof(hdrNum));
2880 if (ret == 0 && datap) {
2881 memcpy(datap, &hdrNum, sizeof(hdrNum));
2884 datalen = sizeof(hdrNum);
2893 data->size = datalen;
2896 ret = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
2898 xx = dbiSync(dbi, 0);
2900 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2908 rpmError(RPMERR_DBCORRUPT,
2909 _("error(%d) allocating new package instance\n"), ret);
2913 /* Now update the indexes */
2916 { dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2918 if (dbiTags != NULL)
2919 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
2921 const char **rpmvals = NULL;
2922 rpmTagType rpmtype = 0;
2925 int_32 * requireFlags;
2929 rpmrc = RPMRC_NOTFOUND;
2931 requireFlags = NULL;
2933 rpmtag = dbiTags[dbix];
2937 /* Filter out temporary databases */
2938 case RPMDBI_AVAILABLE:
2940 case RPMDBI_REMOVED:
2941 case RPMDBI_DEPENDS:
2943 /*@notreached@*/ /*@switchbreak@*/ break;
2944 case RPMDBI_PACKAGES:
2945 dbi = dbiOpen(db, rpmtag, 0);
2946 if (dbi == NULL) /* XXX shouldn't happen */
2948 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2950 key->data = (void *) &hdrNum;
2951 key->size = sizeof(hdrNum);
2952 data->data = headerUnload(h);
2953 data->size = headerSizeof(h, HEADER_MAGIC_NO);
2955 /* Check header digest/signature on blob export. */
2957 const char * msg = NULL;
2960 rpmrc = (*hdrchk) (ts, data->data, data->size, &msg);
2961 lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG);
2962 rpmMessage(lvl, "%s h#%8u %s",
2963 (rpmrc == RPMRC_FAIL ? _("rpmdbAdd: skipping") : " +++"),
2964 hdrNum, (msg ? msg : "\n"));
2968 if (data->data != NULL && rpmrc != RPMRC_FAIL) {
2970 xx = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
2972 xx = dbiSync(dbi, 0);
2974 data->data = _free(data->data);
2976 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2978 if (!dbi->dbi_no_dbsync)
2979 xx = dbiSync(dbi, 0);
2981 /*@notreached@*/ /*@switchbreak@*/ break;
2982 case RPMTAG_BASENAMES: /* XXX preserve legacy behavior */
2984 rpmvals = baseNames;
2986 /*@switchbreak@*/ break;
2987 case RPMTAG_REQUIRENAME:
2988 xx = hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
2989 xx = hge(h, RPMTAG_REQUIREFLAGS, NULL, (void **)&requireFlags, NULL);
2990 /*@switchbreak@*/ break;
2992 xx = hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
2993 /*@switchbreak@*/ break;
2998 if (rpmtag != RPMTAG_GROUP)
3001 /* XXX preserve legacy behavior */
3002 rpmtype = RPM_STRING_TYPE;
3003 rpmvals = (const char **) "Unknown";
3008 dbi = dbiOpen(db, rpmtag, 0);
3012 if (rpmtype == RPM_STRING_TYPE) {
3013 /* XXX force uniform headerGetEntry return */
3014 /*@-observertrans@*/
3015 av[0] = (const char *) rpmvals;
3016 /*@=observertrans@*/
3022 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
3024 for (i = 0; i < rpmcnt; i++) {
3031 * Include the tagNum in all indices. rpm-3.0.4 and earlier
3032 * included the tagNum only for files.
3035 switch (dbi->dbi_rpmtag) {
3036 case RPMTAG_PUBKEYS:
3037 /*@switchbreak@*/ break;
3038 case RPMTAG_FILEMD5S:
3039 /* Filter out empty MD5 strings. */
3040 if (!(rpmvals[i] && *rpmvals[i] != '\0'))
3041 /*@innercontinue@*/ continue;
3042 /*@switchbreak@*/ break;
3043 case RPMTAG_REQUIRENAME:
3044 /* Filter out install prerequisites. */
3045 if (requireFlags && isInstallPreReq(requireFlags[i]))
3046 /*@innercontinue@*/ continue;
3047 /*@switchbreak@*/ break;
3048 case RPMTAG_TRIGGERNAME:
3049 if (i) { /* don't add duplicates */
3051 for (j = 0; j < i; j++) {
3052 if (!strcmp(rpmvals[i], rpmvals[j]))
3053 /*@innerbreak@*/ break;
3057 /*@innercontinue@*/ continue;
3059 /*@switchbreak@*/ break;
3061 /*@switchbreak@*/ break;
3064 /* Identify value pointer and length. */
3071 key->size = sizeof(int_8);
3072 /*@i@*/ key->data = rpmvals + i;
3073 /*@switchbreak@*/ break;
3074 case RPM_INT16_TYPE:
3075 key->size = sizeof(int_16);
3076 /*@i@*/ key->data = rpmvals + i;
3077 /*@switchbreak@*/ break;
3078 case RPM_INT32_TYPE:
3079 key->size = sizeof(int_32);
3080 /*@i@*/ key->data = rpmvals + i;
3081 /*@switchbreak@*/ break;
3085 /*@i@*/ key->data = rpmvals;
3086 rpmcnt = 1; /* XXX break out of loop. */
3087 /*@switchbreak@*/ break;
3088 case RPM_STRING_TYPE:
3089 case RPM_I18NSTRING_TYPE:
3090 rpmcnt = 1; /* XXX break out of loop. */
3092 case RPM_STRING_ARRAY_TYPE:
3093 /* Convert from hex to binary. */
3095 if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) {
3100 for (j = 0; j < 16; j++, t++, s += 2)
3101 *t = (nibble(s[0]) << 4) | nibble(s[1]);
3104 /*@switchbreak@*/ break;
3106 /* Extract the pubkey id from the base64 blob. */
3107 if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
3108 pgpDig dig = pgpNewDig();
3112 if (b64decode(rpmvals[i], (void **)&pkt, &pktlen))
3113 /*@innercontinue@*/ continue;
3114 (void) pgpPrtPkts(pkt, pktlen, dig, 0);
3115 memcpy(bin, dig->pubkey.signid, 8);
3120 /*@switchbreak@*/ break;
3125 /*@i@*/ key->data = (void *) rpmvals[i];
3126 key->size = strlen(rpmvals[i]);
3128 /*@switchbreak@*/ break;
3133 if (rpmcnt == 1 && stringvalued) {
3134 rpmMessage(RPMMESS_DEBUG,
3135 _("adding \"%s\" to %s index.\n"),
3136 (char *)key->data, tagName(dbi->dbi_rpmtag));
3138 rpmMessage(RPMMESS_DEBUG,
3139 _("adding %d entries to %s index.\n"),
3140 rpmcnt, tagName(dbi->dbi_rpmtag));
3145 /* XXX with duplicates, an accurate data value and DB_GET_BOTH is needed. */
3149 if (key->size == 0) key->size = strlen((char *)key->data);
3150 if (key->size == 0) key->size++; /* XXX "/" fixup. */
3153 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
3154 if (rc == 0) { /* success */
3155 /* With duplicates, cursor is positioned, discard the record. */
3156 if (!dbi->dbi_permit_dups)
3157 (void) dbt2set(dbi, data, &set);
3158 } else if (rc != DB_NOTFOUND) { /* error */
3159 rpmError(RPMERR_DBGETINDEX,
3160 _("error(%d) getting \"%s\" records from %s index\n"),
3161 rc, key->data, tagName(dbi->dbi_rpmtag));
3163 /*@innercontinue@*/ continue;
3167 if (set == NULL) /* not found or duplicate */
3168 set = xcalloc(1, sizeof(*set));
3170 (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
3173 (void) set2dbt(dbi, data, set);
3174 rc = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
3178 rpmError(RPMERR_DBPUTINDEX,
3179 _("error(%d) storing record %s into %s\n"),
3180 rc, key->data, tagName(dbi->dbi_rpmtag));
3183 /*@-unqualifiedtrans@*/
3184 data->data = _free(data->data);
3185 /*@=unqualifiedtrans@*/
3187 set = dbiFreeIndexSet(set);
3190 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
3193 if (!dbi->dbi_no_dbsync)
3194 xx = dbiSync(dbi, 0);
3197 /*@-observertrans@*/
3198 if (rpmtype != RPM_BIN_TYPE) /* XXX WTFO? HACK ALERT */
3199 rpmvals = hfd(rpmvals, rpmtype);
3200 /*@=observertrans@*/
3204 /*@=nullpass =nullptrarith =nullderef @*/
3210 (void) unblockSignals(db, &signalMask);
3215 #define _skip(_dn) { sizeof(_dn)-1, (_dn) }
3217 /*@unchecked@*/ /*@observer@*/
3218 static struct skipDir_s {
3220 /*@observer@*/ /*@null@*/
3223 _skip("/usr/share/zoneinfo"),
3224 _skip("/usr/share/locale"),
3225 _skip("/usr/share/i18n"),
3226 _skip("/usr/share/doc"),
3227 _skip("/usr/lib/locale"),
3228 _skip("/usr/src/debug"),
3232 static int skipDir(const char * dn)
3235 struct skipDir_s * sd = skipDirs;
3239 for (sd = skipDirs; sd->dn != NULL; sd++) {
3240 if (dnlen < sd->dnlen)
3242 if (strncmp(dn, sd->dn, sd->dnlen))
3249 /* XXX transaction.c */
3251 int rpmdbFindFpList(rpmdb db, fingerPrint * fpList, dbiIndexSet * matchList,
3256 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
3257 HFD_t hfd = headerFreeData;
3258 rpmdbMatchIterator mi;
3259 fingerPrintCache fpc;
3263 if (db == NULL) return 0;
3265 mi = rpmdbInitIterator(db, RPMTAG_BASENAMES, NULL, 0);
3266 if (mi == NULL) /* XXX should never happen */
3270 data = &mi->mi_data;
3272 /* Gather all installed headers with matching basename's. */
3273 for (i = 0; i < numItems; i++) {
3276 matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
3279 /*@-boundsread -dependenttrans@*/
3280 key->data = (void *) fpList[i].baseName;
3281 /*@=boundsread =dependenttrans@*/
3282 key->size = strlen((char *)key->data);
3283 if (key->size == 0) key->size++; /* XXX "/" fixup. */
3285 if (skipDir(fpList[i].entry->dirName))
3288 xx = rpmdbGrowIterator(mi, i);
3292 if ((i = rpmdbGetIteratorCount(mi)) == 0) {
3293 mi = rpmdbFreeIterator(mi);
3296 fpc = fpCacheCreate(i);
3298 rpmdbSortIterator(mi);
3299 /* iterator is now sorted by (recnum, filenum) */
3301 /* For all installed headers with matching basename's ... */
3303 while ((h = rpmdbNextIterator(mi)) != NULL) {
3304 const char ** dirNames;
3305 const char ** baseNames;
3306 const char ** fullBaseNames;
3307 rpmTagType bnt, dnt;
3308 int_32 * dirIndexes;
3309 int_32 * fullDirIndexes;
3316 start = mi->mi_setx - 1;
3317 im = mi->mi_set->recs + start;
3319 /* Find the end of the set of matched basename's in this package. */
3321 for (end = start + 1; end < mi->mi_set->count; end++) {
3322 if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
3323 /*@innerbreak@*/ break;
3328 /* Compute fingerprints for this installed header's matches */
3329 xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &fullBaseNames, NULL);
3330 xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
3331 xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fullDirIndexes, NULL);
3333 baseNames = xcalloc(num, sizeof(*baseNames));
3334 dirIndexes = xcalloc(num, sizeof(*dirIndexes));
3336 for (i = 0; i < num; i++) {
3337 baseNames[i] = fullBaseNames[im[i].tagNum];
3338 dirIndexes[i] = fullDirIndexes[im[i].tagNum];
3342 fps = xcalloc(num, sizeof(*fps));
3343 fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
3345 /* Add db (recnum,filenum) to list for fingerprint matches. */
3347 for (i = 0; i < num; i++, im++) {
3348 /*@-nullpass@*/ /* FIX: fpList[].subDir may be NULL */
3349 if (!FP_EQUAL(fps[i], fpList[im->fpNum]))
3350 /*@innercontinue@*/ continue;
3352 xx = dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
3357 dirNames = hfd(dirNames, dnt);
3358 fullBaseNames = hfd(fullBaseNames, bnt);
3359 baseNames = _free(baseNames);
3360 dirIndexes = _free(dirIndexes);
3365 mi = rpmdbFreeIterator(mi);
3367 fpc = fpCacheFree(fpc);
3375 * Check if file esists using stat(2).
3376 * @param urlfn file name (may be URL)
3377 * @return 1 if file exists, 0 if not
3379 static int rpmioFileExists(const char * urlfn)
3380 /*@globals fileSystem, internalState @*/
3381 /*@modifies fileSystem, internalState @*/
3384 int urltype = urlPath(urlfn, &fn);
3388 if (*fn == '\0') fn = "/";
3391 case URL_IS_FTP: /* XXX WRONG WRONG WRONG */
3392 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
3394 case URL_IS_UNKNOWN:
3395 if (Stat(fn, &buf)) {
3406 /*@notreached@*/ break;
3412 static int rpmdbRemoveDatabase(const char * prefix,
3413 const char * dbpath, int _dbapi)
3414 /*@globals fileSystem, internalState @*/
3415 /*@modifies fileSystem, internalState @*/
3422 /*@-bounds -branchstate@*/
3423 if (dbpath[i - 1] != '/') {
3424 filename = alloca(i);
3425 strcpy(filename, dbpath);
3427 filename[i + 1] = '\0';
3430 /*@=bounds =branchstate@*/
3432 filename = alloca(strlen(prefix) + strlen(dbpath) + 40);
3436 if (dbiTags != NULL)
3437 for (i = 0; i < dbiTagsMax; i++) {
3439 const char * base = tagName(dbiTags[i]);
3441 sprintf(filename, "%s/%s/%s", prefix, dbpath, base);
3442 (void)rpmCleanPath(filename);
3443 if (!rpmioFileExists(filename))
3445 xx = unlink(filename);
3447 for (i = 0; i < 16; i++) {
3448 sprintf(filename, "%s/%s/__db.%03d", prefix, dbpath, i);
3449 (void)rpmCleanPath(filename);
3450 if (!rpmioFileExists(filename))
3452 xx = unlink(filename);
3461 sprintf(filename, "%s/%s", prefix, dbpath);
3462 (void)rpmCleanPath(filename);
3463 xx = rmdir(filename);
3468 static int rpmdbMoveDatabase(const char * prefix,
3469 const char * olddbpath, int _olddbapi,
3470 const char * newdbpath, int _newdbapi)
3471 /*@globals fileSystem, internalState @*/
3472 /*@modifies fileSystem, internalState @*/
3475 char * ofilename, * nfilename;
3476 struct stat * nst = alloca(sizeof(*nst));
3480 i = strlen(olddbpath);
3482 if (olddbpath[i - 1] != '/') {
3483 ofilename = alloca(i + 2);
3484 strcpy(ofilename, olddbpath);
3486 ofilename[i + 1] = '\0';
3487 olddbpath = ofilename;
3491 i = strlen(newdbpath);
3493 if (newdbpath[i - 1] != '/') {
3494 nfilename = alloca(i + 2);
3495 strcpy(nfilename, newdbpath);
3497 nfilename[i + 1] = '\0';
3498 newdbpath = nfilename;
3502 ofilename = alloca(strlen(prefix) + strlen(olddbpath) + 40);
3503 nfilename = alloca(strlen(prefix) + strlen(newdbpath) + 40);
3505 switch (_olddbapi) {
3507 if (dbiTags != NULL)
3508 for (i = 0; i < dbiTagsMax; i++) {
3512 /* Filter out temporary databases */
3513 switch ((rpmtag = dbiTags[i])) {
3514 case RPMDBI_AVAILABLE:
3516 case RPMDBI_REMOVED:
3517 case RPMDBI_DEPENDS:
3519 /*@notreached@*/ /*@switchbreak@*/ break;
3521 /*@switchbreak@*/ break;
3524 base = tagName(rpmtag);
3525 sprintf(ofilename, "%s/%s/%s", prefix, olddbpath, base);
3526 (void)rpmCleanPath(ofilename);
3527 if (!rpmioFileExists(ofilename))
3529 sprintf(nfilename, "%s/%s/%s", prefix, newdbpath, base);
3530 (void)rpmCleanPath(nfilename);
3533 * Get uid/gid/mode/mtime. If old doesn't exist, use new.
3534 * XXX Yes, the variable names are backwards.
3536 if (stat(nfilename, nst) < 0)
3537 if (stat(ofilename, nst) < 0)
3540 if ((xx = rename(ofilename, nfilename)) != 0) {
3544 xx = chown(nfilename, nst->st_uid, nst->st_gid);
3545 xx = chmod(nfilename, (nst->st_mode & 07777));
3546 { struct utimbuf stamp;
3547 stamp.actime = nst->st_atime;
3548 stamp.modtime = nst->st_mtime;
3549 xx = utime(nfilename, &stamp);
3552 for (i = 0; i < 16; i++) {
3553 sprintf(ofilename, "%s/%s/__db.%03d", prefix, olddbpath, i);
3554 (void)rpmCleanPath(ofilename);
3555 if (!rpmioFileExists(ofilename))
3557 xx = unlink(ofilename);
3558 sprintf(nfilename, "%s/%s/__db.%03d", prefix, newdbpath, i);
3559 (void)rpmCleanPath(nfilename);
3560 xx = unlink(nfilename);
3568 if (rc || _olddbapi == _newdbapi)
3571 rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi);
3574 /* Remove /etc/rpm/macros.db1 configuration file if db3 rebuilt. */
3575 if (rc == 0 && _newdbapi == 1 && _olddbapi == 3) {
3576 const char * mdb1 = "/etc/rpm/macros.db1";
3578 if (!stat(mdb1, &st) && S_ISREG(st.st_mode) && !unlink(mdb1))
3579 rpmMessage(RPMMESS_DEBUG,
3580 _("removing %s after successful db3 rebuild.\n"), mdb1);
3585 int rpmdbRebuild(const char * prefix, rpmts ts,
3586 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
3587 /*@globals _rebuildinprogress @*/
3588 /*@modifies _rebuildinprogress @*/
3591 const char * dbpath = NULL;
3592 const char * rootdbpath = NULL;
3594 const char * newdbpath = NULL;
3595 const char * newrootdbpath = NULL;
3605 if (prefix == NULL) prefix = "/";
3608 _dbapi = rpmExpandNumeric("%{_dbapi}");
3609 _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
3612 tfn = rpmGetPath("%{?_dbpath}", NULL);
3615 if (!(tfn && tfn[0] != '\0'))
3618 rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
3622 dbpath = rootdbpath = rpmGetPath(prefix, tfn, NULL);
3623 if (!(prefix[0] == '/' && prefix[1] == '\0'))
3624 dbpath += strlen(prefix);
3628 tfn = rpmGetPath("%{?_dbpath_rebuild}", NULL);
3631 if (!(tfn && tfn[0] != '\0' && strcmp(tfn, dbpath)))
3636 sprintf(pidbuf, "rebuilddb.%d", (int) getpid());
3637 t = xmalloc(strlen(dbpath) + strlen(pidbuf) + 1);
3639 (void)stpcpy(stpcpy(t, dbpath), pidbuf);
3645 newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL);
3646 if (!(prefix[0] == '/' && prefix[1] == '\0'))
3647 newdbpath += strlen(prefix);
3650 rpmMessage(RPMMESS_DEBUG, _("rebuilding database %s into %s\n"),
3651 rootdbpath, newrootdbpath);
3653 if (!access(newrootdbpath, F_OK)) {
3654 rpmError(RPMERR_MKDIR, _("temporary database %s already exists\n"),
3660 rpmMessage(RPMMESS_DEBUG, _("creating directory %s\n"), newrootdbpath);
3661 if (Mkdir(newrootdbpath, 0755)) {
3662 rpmError(RPMERR_MKDIR, _("creating directory %s: %s\n"),
3663 newrootdbpath, strerror(errno));
3669 rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"),
3671 _rebuildinprogress = 1;
3673 if (openDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644,
3674 RPMDB_FLAG_MINIMAL)) {
3679 _dbapi = olddb->db_api;
3680 _rebuildinprogress = 0;
3682 rpmMessage(RPMMESS_DEBUG, _("opening new database with dbapi %d\n"),
3684 (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1);
3686 if (openDatabase(prefix, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
3691 _dbapi_rebuild = newdb->db_api;
3694 rpmdbMatchIterator mi;
3695 #define _RECNUM rpmdbGetIteratorOffset(mi)
3697 /* RPMDBI_PACKAGES */
3698 mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
3700 (void) rpmdbSetHdrChk(mi, ts, hdrchk);
3702 while ((h = rpmdbNextIterator(mi)) != NULL) {
3704 /* let's sanity check this record a bit, otherwise just skip it */
3705 if (!(headerIsEntry(h, RPMTAG_NAME) &&
3706 headerIsEntry(h, RPMTAG_VERSION) &&
3707 headerIsEntry(h, RPMTAG_RELEASE) &&
3708 headerIsEntry(h, RPMTAG_BUILDTIME)))
3710 rpmError(RPMERR_INTERNAL,
3711 _("header #%u in the database is bad -- skipping.\n"),
3716 /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
3717 if (_db_filter_dups || newdb->db_filter_dups) {
3718 const char * name, * version, * release;
3721 (void) headerNVR(h, &name, &version, &release);
3724 { rpmdbMatchIterator mi;
3725 mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
3726 (void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
3727 RPMMIRE_DEFAULT, version);
3728 (void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
3729 RPMMIRE_DEFAULT, release);
3730 while (rpmdbNextIterator(mi)) {
3732 /*@innerbreak@*/ break;
3734 mi = rpmdbFreeIterator(mi);
3742 /* Deleted entries are eliminated in legacy headers by copy. */
3743 { Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
3744 ? headerCopy(h) : NULL);
3745 rc = rpmdbAdd(newdb, -1, (nh ? nh : h), ts, hdrchk);
3746 nh = headerFree(nh);
3750 rpmError(RPMERR_INTERNAL,
3751 _("cannot add record originally at %u\n"), _RECNUM);
3757 mi = rpmdbFreeIterator(mi);
3762 olddb->db_remove_env = 1;
3763 newdb->db_remove_env = 1;
3765 xx = rpmdbClose(olddb);
3766 xx = rpmdbClose(newdb);
3769 rpmMessage(RPMMESS_NORMAL, _("failed to rebuild database: original database "
3770 "remains in place\n"));
3772 xx = rpmdbRemoveDatabase(prefix, newdbpath, _dbapi_rebuild);
3775 } else if (!nocleanup) {
3776 if (rpmdbMoveDatabase(prefix, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
3777 rpmMessage(RPMMESS_ERROR, _("failed to replace old database with new "
3779 rpmMessage(RPMMESS_ERROR, _("replace files in %s with files from %s "
3780 "to recover"), dbpath, newdbpath);
3788 if (removedir && !(rc == 0 && nocleanup)) {
3789 rpmMessage(RPMMESS_DEBUG, _("removing directory %s\n"), newrootdbpath);
3790 if (Rmdir(newrootdbpath))
3791 rpmMessage(RPMMESS_ERROR, _("failed to remove directory %s: %s\n"),
3792 newrootdbpath, strerror(errno));
3794 newrootdbpath = _free(newrootdbpath);
3795 rootdbpath = _free(rootdbpath);