7 #define _USE_COPY_LOAD /* XXX don't use DB_DBT_MALLOC (yet) */
14 #include <sys/signal.h>
16 #ifndef DYING /* XXX already in "system.h" */
20 #if defined(__LCLINT__)
21 /*@-declundef -exportheader -redecl @*/ /* LCL: missing annotation */
22 extern int fnmatch (const char *pattern, const char *string, int flags)
24 /*@=declundef =exportheader =redecl @*/
29 #if defined(__LCLINT__)
30 /*@-declundef -exportheader @*/ /* LCL: missing modifies (only is bogus) */
31 extern void regfree (/*@only@*/ regex_t *preg)
32 /*@modifies *preg @*/;
33 /*@=declundef =exportheader @*/
36 #include <rpmio_internal.h>
42 #include "header_internal.h" /* XXX for HEADERFLAG_ALLOCATED */
45 /*@access dbiIndexSet@*/
46 /*@access dbiIndexItem@*/
47 /*@access rpmts@*/ /* XXX compared with NULL */
48 /*@access Header@*/ /* XXX compared with NULL */
49 /*@access rpmdbMatchIterator@*/
53 static int _rebuildinprogress = 0;
55 static int _db_filter_dups = 0;
58 #define _DBI_PERMS 0644
62 /*@globstate@*/ /*@null@*/ int * dbiTags = NULL;
66 /* Bit mask macros. */
68 typedef unsigned int __pbm_bits;
70 #define __PBM_NBITS (8 * sizeof (__pbm_bits))
71 #define __PBM_IX(d) ((d) / __PBM_NBITS)
72 #define __PBM_MASK(d) ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
78 #define __PBM_BITS(set) ((set)->bits)
80 #define PBM_FREE(s) _free(s);
81 #define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
82 #define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
83 #define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
85 #define PBM_ALLOC(d) xcalloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
88 * Reallocate a bit map.
89 * @retval sp address of bit map pointer
90 * @retval odp no. of bits in map
91 * @param nd desired no. of bits
94 static inline pbm_set * PBM_REALLOC(pbm_set ** sp, int * odp, int nd)
95 /*@modifies *sp, *odp @*/
99 /*@-bounds -sizeoftype@*/
102 nb = __PBM_IX(nd) + 1;
103 /*@-unqualifiedtrans@*/
104 *sp = xrealloc(*sp, nb * sizeof(__pbm_bits));
105 /*@=unqualifiedtrans@*/
106 for (i = __PBM_IX(*odp) + 1; i < nb; i++)
107 __PBM_BITS(*sp)[i] = 0;
110 /*@=bounds =sizeoftype@*/
111 /*@-compdef -retalias -usereleased@*/
113 /*@=compdef =retalias =usereleased@*/
117 * Convert hex to binary nibble.
118 * @param c hex character
119 * @return binary nibble
121 static inline unsigned char nibble(char c)
124 if (c >= '0' && c <= '9')
126 if (c >= 'A' && c <= 'F')
127 return (c - 'A') + 10;
128 if (c >= 'a' && c <= 'f')
129 return (c - 'a') + 10;
135 * Check key for printable characters.
136 * @param ptr key value pointer
137 * @param len key value length
138 * @return 1 if only ASCII, 0 otherwise.
140 static int printable(const void * ptr, size_t len) /*@*/
142 const char * s = ptr;
144 for (i = 0; i < len; i++, s++)
145 if (!(*s >= ' ' && *s <= '~')) return 0;
151 * Return dbi index used for rpm tag.
152 * @param rpmtag rpm header tag
153 * @return dbi index, -1 on error
155 static int dbiTagToDbix(int rpmtag)
161 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
163 if (rpmtag == dbiTags[dbix])
171 * Initialize database (index, tag) tuple from configuration.
173 static void dbiTagsInit(void)
174 /*@globals rpmGlobalMacroContext, dbiTags, dbiTagsMax @*/
175 /*@modifies rpmGlobalMacroContext, dbiTags, dbiTagsMax @*/
177 /*@observer@*/ static const char * const _dbiTagStr_default =
178 "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername:Dirnames:Requireversion:Provideversion:Installtid:Sigmd5:Sha1header:Filemd5s:Depends:Pubkeys";
179 char * dbiTagStr = NULL;
183 dbiTagStr = rpmExpand("%{?_dbi_tags}", NULL);
184 if (!(dbiTagStr && *dbiTagStr)) {
185 dbiTagStr = _free(dbiTagStr);
186 dbiTagStr = xstrdup(_dbiTagStr_default);
189 /* Discard previous values. */
190 dbiTags = _free(dbiTags);
193 /* Always allocate package index */
194 dbiTags = xcalloc(1, sizeof(*dbiTags));
195 dbiTags[dbiTagsMax++] = RPMDBI_PACKAGES;
197 for (o = dbiTagStr; o && *o; o = oe) {
198 while (*o && xisspace(*o))
202 for (oe = o; oe && *oe; oe++) {
204 /*@innerbreak@*/ break;
205 if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
206 /*@innerbreak@*/ break;
210 rpmtag = tagValue(o);
212 rpmMessage(RPMMESS_WARNING,
213 _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
216 if (dbiTagToDbix(rpmtag) >= 0)
219 dbiTags = xrealloc(dbiTags, (dbiTagsMax + 1) * sizeof(*dbiTags)); /* XXX memory leak */
220 dbiTags[dbiTagsMax++] = rpmtag;
223 dbiTagStr = _free(dbiTagStr);
230 /*@-exportheadervar -declundef @*/
232 extern struct _dbiVec db3vec;
233 /*@=exportheadervar =declundef @*/
234 #define DB3vec &db3vec
238 /*@observer@*/ /*@unchecked@*/
239 static struct _dbiVec *mydbvecs[] = {
240 DB1vec, DB1vec, DB2vec, DB3vec, NULL
244 dbiIndex dbiOpen(rpmdb db, rpmTag rpmtag, /*@unused@*/ unsigned int flags)
248 int _dbapi, _dbapi_rebuild, _dbapi_wanted;
254 dbix = dbiTagToDbix(rpmtag);
255 if (dbix < 0 || dbix >= dbiTagsMax)
258 /* Is this index already open ? */
259 /*@-compdef@*/ /* FIX: db->_dbi may be NULL */
260 if ((dbi = db->_dbi[dbix]) != NULL)
264 _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
265 if (_dbapi_rebuild < 1 || _dbapi_rebuild > 3)
267 _dbapi_wanted = (_rebuildinprogress ? -1 : db->db_api);
269 switch (_dbapi_wanted) {
271 _dbapi = _dbapi_wanted;
272 if (_dbapi < 0 || _dbapi >= 4 || mydbvecs[_dbapi] == NULL) {
277 rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
279 static int _printed[32];
280 if (!_printed[dbix & 0x1f]++)
281 rpmError(RPMERR_DBOPEN,
282 _("cannot open %s index using db%d - %s (%d)\n"),
283 tagName(rpmtag), _dbapi,
284 (rc > 0 ? strerror(rc) : ""), rc);
290 while (_dbapi-- > 1) {
291 if (mydbvecs[_dbapi] == NULL)
295 rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
297 /*@loopbreak@*/ break;
300 static int _printed[32];
301 if (!_printed[dbix & 0x1f]++)
302 rpmError(RPMERR_DBOPEN, _("cannot open %s index\n"),
307 if (db->db_api == -1 && _dbapi > 0)
312 /* Require conversion. */
313 if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
314 rc = (_rebuildinprogress ? 0 : 1);
318 /* Suggest possible configuration */
319 if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
324 /* Suggest possible configuration */
325 if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
326 rc = (_rebuildinprogress ? 0 : 1);
331 if (dbi != NULL && rc == 0) {
332 db->_dbi[dbix] = dbi;
334 if (rpmtag == RPMDBI_PACKAGES && db->db_bits == NULL) {
336 if (!dbiStat(dbi, DB_FAST_STAT)) {
337 DB_HASH_STAT * hash = (DB_HASH_STAT *)dbi->dbi_stats;
339 db->db_nbits += hash->hash_nkeys;
341 db->db_bits = PBM_ALLOC(db->db_nbits);
347 /*@-compdef -nullstate@*/ /* FIX: db->_dbi may be NULL */
349 /*@=compdef =nullstate@*/
353 * Create and initialize item for index database set.
354 * @param hdrNum header instance in db
355 * @param tagNum tag index in header
358 static dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum)
361 dbiIndexItem rec = xcalloc(1, sizeof(*rec));
362 rec->hdrNum = hdrNum;
363 rec->tagNum = tagNum;
372 #define _DBSWAP(_a) \
373 { unsigned char _b, *_c = (_a).uc; \
374 _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
375 _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
379 * Convert retrieved data to index set.
380 * @param dbi index database handle
381 * @param data retrieved data
382 * @retval setp (malloc'ed) index set
383 * @return 0 on success
385 static int dbt2set(dbiIndex dbi, DBT * data, /*@out@*/ dbiIndexSet * setp)
386 /*@modifies dbi, *setp @*/
388 int _dbbyteswapped = dbiByteSwapped(dbi);
393 if (dbi == NULL || data == NULL || setp == NULL)
396 if ((sdbir = data->data) == NULL) {
401 set = xmalloc(sizeof(*set));
402 set->count = data->size / dbi->dbi_jlen;
403 set->recs = xmalloc(set->count * sizeof(*(set->recs)));
405 /*@-bounds -sizeoftype @*/
406 switch (dbi->dbi_jlen) {
408 case 2*sizeof(int_32):
409 for (i = 0; i < set->count; i++) {
410 union _dbswap hdrNum, tagNum;
412 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
413 sdbir += sizeof(hdrNum.ui);
414 memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
415 sdbir += sizeof(tagNum.ui);
416 if (_dbbyteswapped) {
420 set->recs[i].hdrNum = hdrNum.ui;
421 set->recs[i].tagNum = tagNum.ui;
422 set->recs[i].fpNum = 0;
425 case 1*sizeof(int_32):
426 for (i = 0; i < set->count; i++) {
427 union _dbswap hdrNum;
429 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
430 sdbir += sizeof(hdrNum.ui);
431 if (_dbbyteswapped) {
434 set->recs[i].hdrNum = hdrNum.ui;
435 set->recs[i].tagNum = 0;
436 set->recs[i].fpNum = 0;
441 /*@=bounds =sizeoftype @*/
448 * Convert index set to database representation.
449 * @param dbi index database handle
450 * @param data retrieved data
451 * @param set index set
452 * @return 0 on success
454 static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set)
455 /*@modifies dbi, *data @*/
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 /*@-bounds -sizeoftype@*/
472 switch (dbi->dbi_jlen) {
474 case 2*sizeof(int_32):
475 for (i = 0; i < set->count; i++) {
476 union _dbswap hdrNum, tagNum;
478 memset(&hdrNum, 0, sizeof(hdrNum));
479 memset(&tagNum, 0, sizeof(tagNum));
480 hdrNum.ui = set->recs[i].hdrNum;
481 tagNum.ui = set->recs[i].tagNum;
482 if (_dbbyteswapped) {
486 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
487 tdbir += sizeof(hdrNum.ui);
488 memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
489 tdbir += sizeof(tagNum.ui);
492 case 1*sizeof(int_32):
493 for (i = 0; i < set->count; i++) {
494 union _dbswap hdrNum;
496 memset(&hdrNum, 0, sizeof(hdrNum));
497 hdrNum.ui = set->recs[i].hdrNum;
498 if (_dbbyteswapped) {
501 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
502 tdbir += sizeof(hdrNum.ui);
506 /*@=bounds =sizeoftype@*/
513 /* XXX assumes hdrNum is first int in dbiIndexItem */
514 static int hdrNumCmp(const void * one, const void * two)
517 const int * a = one, * b = two;
522 * Append element(s) to set of index database items.
523 * @param set set of index database items
524 * @param recs array of items to append to set
525 * @param nrecs number of items
526 * @param recsize size of an array item
527 * @param sortset should resulting set be sorted?
528 * @return 0 success, 1 failure (bad args)
530 static int dbiAppendSet(dbiIndexSet set, const void * recs,
531 int nrecs, size_t recsize, int sortset)
534 const char * rptr = recs;
535 size_t rlen = (recsize < sizeof(*(set->recs)))
536 ? recsize : sizeof(*(set->recs));
538 if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0)
541 set->recs = xrealloc(set->recs,
542 (set->count + nrecs) * sizeof(*(set->recs)));
544 memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
546 while (nrecs-- > 0) {
547 /*@-mayaliasunique@*/
548 memcpy(set->recs + set->count, rptr, rlen);
549 /*@=mayaliasunique@*/
554 if (sortset && set->count > 1)
555 qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
561 * Remove element(s) from set of index database items.
562 * @param set set of index database items
563 * @param recs array of items to remove from set
564 * @param nrecs number of items
565 * @param recsize size of an array item
566 * @param sorted array is already sorted?
567 * @return 0 success, 1 failure (no items found)
569 static int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
570 size_t recsize, int sorted)
571 /*@modifies set, recs @*/
575 int num = set->count;
578 assert(set->count > 0);
579 if (nrecs > 1 && !sorted)
580 qsort(recs, nrecs, recsize, hdrNumCmp);
582 for (from = 0; from < num; from++) {
583 if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
588 set->recs[to] = set->recs[from]; /* structure assignment */
592 return (numCopied == num);
595 /* XXX transaction.c */
596 unsigned int dbiIndexSetCount(dbiIndexSet set) {
600 /* XXX transaction.c */
601 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
602 return set->recs[recno].hdrNum;
605 /* XXX transaction.c */
606 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
607 return set->recs[recno].tagNum;
610 /* XXX transaction.c */
611 dbiIndexSet dbiFreeIndexSet(dbiIndexSet set) {
613 set->recs = _free(set->recs);
622 static sigset_t caught;
625 static void handler(int signum);
631 static struct sigtbl_s {
634 struct sigaction act;
635 struct sigaction oact;
637 { SIGHUP, 0, { {handler} } },
638 { SIGINT, 0, { {handler} } },
639 { SIGTERM, 0, { {handler} } },
640 { SIGQUIT, 0, { {handler} } },
641 { -1, 0, { {NULL} } },
648 static void handler(int signum)
649 /*@globals caught, satbl @*/
650 /*@modifies caught @*/
652 struct sigtbl_s * tbl;
654 for(tbl = satbl; tbl->signum >= 0; tbl++) {
655 if (tbl->signum != signum)
659 (void) sigaddset(&caught, signum);
666 * Enable all signal handlers.
668 static int enableSignals(void)
669 /*@globals caught, satbl, fileSystem @*/
670 /*@modifies caught, satbl, fileSystem @*/
672 struct sigtbl_s * tbl;
673 sigset_t newMask, oldMask;
676 (void) sigfillset(&newMask); /* block all signals */
677 (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
679 for(tbl = satbl; tbl->signum >= 0; tbl++) {
680 if (tbl->active++ > 0)
682 (void) sigdelset(&caught, tbl->signum);
683 rc = sigaction(tbl->signum, &tbl->act, &tbl->oact);
686 return sigprocmask(SIG_SETMASK, &oldMask, NULL);
690 static rpmdb rpmdbRock;
692 int rpmdbCheckSignals(void)
693 /*@globals rpmdbRock, satbl @*/
694 /*@modifies rpmdbRock @*/
696 struct sigtbl_s * tbl;
697 sigset_t newMask, oldMask;
700 (void) sigfillset(&newMask); /* block all signals */
701 (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
702 for(tbl = satbl; tbl->signum >= 0; tbl++) {
703 if (tbl->active == 0)
705 if (sigismember(&caught, tbl->signum))
711 rpmMessage(RPMMESS_WARNING, "Exiting on signal ...\n");
713 while ((db = rpmdbRock) != NULL) {
714 /*@i@*/ rpmdbRock = db->db_next;
716 (void) rpmdbClose(db);
721 return sigprocmask(SIG_SETMASK, &oldMask, NULL);
725 * Disable all signal handlers.
727 static int disableSignals(void)
728 /*@globals satbl, fileSystem @*/
729 /*@modifies satbl, fileSystem @*/
731 struct sigtbl_s * tbl;
732 sigset_t newMask, oldMask;
735 (void) sigfillset(&newMask); /* block all signals */
736 (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
738 for(tbl = satbl; tbl->signum >= 0; tbl++) {
739 if (--tbl->active > 0)
741 rc = sigaction(tbl->signum, &tbl->oact, NULL);
744 return sigprocmask(SIG_SETMASK, &oldMask, NULL);
748 * Block all signals, returning previous signal mask.
750 static int blockSignals(/*@unused@*/ rpmdb db, /*@out@*/ sigset_t * oldMask)
751 /*@globals satbl, fileSystem @*/
752 /*@modifies *oldMask, satbl, fileSystem @*/
754 struct sigtbl_s * tbl;
757 (void) sigfillset(&newMask); /* block all signals */
758 (void) sigprocmask(SIG_BLOCK, &newMask, oldMask);
759 for(tbl = satbl; tbl->signum >= 0; tbl++) {
760 if (tbl->active == 0)
762 (void) sigdelset(&newMask, tbl->signum);
764 return sigprocmask(SIG_BLOCK, &newMask, NULL);
768 * Restore signal mask.
771 static int unblockSignals(/*@unused@*/ rpmdb db, sigset_t * oldMask)
772 /*@globals rpmdbRock, fileSystem @*/
773 /*@modifies rpmdbRock, fileSystem @*/
775 (void) rpmdbCheckSignals();
776 return sigprocmask(SIG_SETMASK, oldMask, NULL);
780 #define _DB_HOME "%{_dbpath}"
783 #define _DB_PERMS 0644
786 #define _DB_ERRPFX "rpmdb"
789 /*@observer@*/ /*@unchecked@*/
790 static struct rpmdb_s dbTemplate = {
791 _DB_ROOT, _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
792 _DB_MAJOR, _DB_ERRPFX
796 int rpmdbOpenAll(rpmdb db)
801 if (db == NULL) return -2;
804 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
805 if (db->_dbi[dbix] != NULL)
807 (void) dbiOpen(db, dbiTags[dbix], db->db_flags);
812 int rpmdbCloseDBI(rpmdb db, int rpmtag)
817 if (db == NULL || db->_dbi == NULL || dbiTags == NULL)
820 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
821 if (dbiTags[dbix] != rpmtag)
824 if (db->_dbi[dbix] != NULL) {
826 /*@-unqualifiedtrans@*/ /* FIX: double indirection. */
827 xx = dbiClose(db->_dbi[dbix], 0);
828 if (xx && rc == 0) rc = xx;
829 db->_dbi[dbix] = NULL;
830 /*@=unqualifiedtrans@*/
838 /* XXX query.c, rpminstall.c, verify.c */
840 int rpmdbClose(rpmdb db)
841 /*@globals rpmdbRock @*/
842 /*@modifies rpmdbRock @*/
851 (void) rpmdbUnlink(db, "rpmdbClose");
858 for (dbix = db->db_ndbi; --dbix >= 0; ) {
860 if (db->_dbi[dbix] == NULL)
862 /*@-unqualifiedtrans@*/ /* FIX: double indirection. */
863 xx = dbiClose(db->_dbi[dbix], 0);
864 if (xx && rc == 0) rc = xx;
865 db->_dbi[dbix] = NULL;
866 /*@=unqualifiedtrans@*/
868 db->db_errpfx = _free(db->db_errpfx);
869 db->db_root = _free(db->db_root);
870 db->db_home = _free(db->db_home);
871 db->db_bits = PBM_FREE(db->db_bits);
872 db->_dbi = _free(db->_dbi);
876 while ((next = *prev) != NULL && next != db)
877 prev = &next->db_next;
879 /*@i@*/ *prev = next->db_next;
880 next->db_next = NULL;
884 /*@-refcounttrans@*/ db = _free(db); /*@=refcounttrans@*/
888 (void) disableSignals();
893 int rpmdbSync(rpmdb db)
898 if (db == NULL) return 0;
899 for (dbix = 0; dbix < db->db_ndbi; dbix++) {
901 if (db->_dbi[dbix] == NULL)
903 xx = dbiSync(db->_dbi[dbix], 0);
904 if (xx && rc == 0) rc = xx;
909 /*@-mods@*/ /* FIX: dbTemplate structure assignment */
910 static /*@only@*/ /*@null@*/
911 rpmdb newRpmdb(/*@kept@*/ /*@null@*/ const char * root,
912 /*@kept@*/ /*@null@*/ const char * home,
913 int mode, int perms, int flags)
914 /*@globals _db_filter_dups, rpmGlobalMacroContext @*/
915 /*@modifies _db_filter_dups, rpmGlobalMacroContext @*/
917 rpmdb db = xcalloc(sizeof(*db), 1);
918 const char * epfx = _DB_ERRPFX;
919 static int _initialized = 0;
922 _db_filter_dups = rpmExpandNumeric("%{_filterdbdups}");
928 *db = dbTemplate; /* structure assignment */
934 if (!(perms & 0600)) perms = 0644; /* XXX sanity */
936 if (mode >= 0) db->db_mode = mode;
937 if (perms >= 0) db->db_perms = perms;
938 if (flags >= 0) db->db_flags = flags;
941 db->db_root = rpmGetPath( (root && *root ? root : _DB_ROOT), NULL);
942 db->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL);
944 if (!(db->db_home && db->db_home[0] != '%')) {
945 rpmError(RPMERR_DBOPEN, _("no dbpath has been set\n"));
946 db->db_root = _free(db->db_root);
947 db->db_home = _free(db->db_home);
949 /*@-globstate@*/ return NULL; /*@=globstate@*/
951 db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
952 db->db_remove_env = 0;
953 db->db_filter_dups = _db_filter_dups;
954 db->db_ndbi = dbiTagsMax;
955 db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
958 return rpmdbLink(db, "rpmdbCreate");
963 static int openDatabase(/*@null@*/ const char * prefix,
964 /*@null@*/ const char * dbpath,
965 int _dbapi, /*@null@*/ /*@out@*/ rpmdb *dbp,
966 int mode, int perms, int flags)
967 /*@globals rpmdbRock, rpmGlobalMacroContext,
968 fileSystem, internalState @*/
969 /*@modifies rpmdbRock, *dbp, rpmGlobalMacroContext,
970 fileSystem, internalState @*/
971 /*@requires maxSet(dbp) >= 0 @*/
975 static int _tags_initialized = 0;
976 int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
977 int minimal = flags & RPMDB_FLAG_MINIMAL;
979 if (!_tags_initialized || dbiTagsMax == 0) {
984 /* Insure that _dbapi has one of -1, 1, 2, or 3 */
985 if (_dbapi < -1 || _dbapi > 3)
995 db = newRpmdb(prefix, dbpath, mode, perms, flags);
999 (void) enableSignals();
1001 db->db_api = _dbapi;
1006 if (dbiTags != NULL)
1007 for (dbix = 0; rc == 0 && dbix < dbiTagsMax; dbix++) {
1011 /* Filter out temporary databases */
1012 switch ((rpmtag = dbiTags[dbix])) {
1013 case RPMDBI_AVAILABLE:
1015 case RPMDBI_REMOVED:
1016 case RPMDBI_DEPENDS:
1018 /*@notreached@*/ /*@switchbreak@*/ break;
1020 /*@switchbreak@*/ break;
1023 dbi = dbiOpen(db, rpmtag, 0);
1030 case RPMDBI_PACKAGES:
1031 if (dbi == NULL) rc |= 1;
1033 /* XXX open only Packages, indices created on the fly. */
1034 if (db->db_api == 3)
1037 /*@notreached@*/ /*@switchbreak@*/ break;
1039 if (dbi == NULL) rc |= 1;
1042 /*@switchbreak@*/ break;
1044 /*@switchbreak@*/ break;
1050 if (rc || justCheck || dbp == NULL)
1051 xx = rpmdbClose(db);
1053 /*@-assignexpose -newreftrans@*/
1054 /*@i@*/ db->db_next = rpmdbRock;
1057 /*@=assignexpose =newreftrans@*/
1063 rpmdb XrpmdbUnlink(rpmdb db, const char * msg, const char * fn, unsigned ln)
1067 fprintf(stderr, "--> db %p -- %d %s at %s:%u\n", db, db->nrefs, msg, fn, ln);
1073 rpmdb XrpmdbLink(rpmdb db, const char * msg, const char * fn, unsigned ln)
1078 fprintf(stderr, "--> db %p ++ %d %s at %s:%u\n", db, db->nrefs, msg, fn, ln);
1080 /*@-refcounttrans@*/ return db; /*@=refcounttrans@*/
1083 /* XXX python/rpmmodule.c */
1084 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
1086 int _dbapi = rpmExpandNumeric("%{_dbapi}");
1088 return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
1092 int rpmdbInit (const char * prefix, int perms)
1095 int _dbapi = rpmExpandNumeric("%{_dbapi}");
1099 rc = openDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR),
1100 perms, RPMDB_FLAG_JUSTCHECK);
1104 xx = rpmdbOpenAll(db);
1105 if (xx && rc == 0) rc = xx;
1106 xx = rpmdbClose(db);
1107 if (xx && rc == 0) rc = xx;
1113 int rpmdbVerify(const char * prefix)
1116 int _dbapi = rpmExpandNumeric("%{_dbapi}");
1120 rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0);
1126 rc = rpmdbOpenAll(db);
1128 for (dbix = db->db_ndbi; --dbix >= 0; ) {
1129 if (db->_dbi[dbix] == NULL)
1131 /*@-unqualifiedtrans@*/ /* FIX: double indirection. */
1132 xx = dbiVerify(db->_dbi[dbix], 0);
1133 if (xx && rc == 0) rc = xx;
1134 db->_dbi[dbix] = NULL;
1135 /*@=unqualifiedtrans@*/
1138 /*@-nullstate@*/ /* FIX: db->_dbi[] may be NULL. */
1139 xx = rpmdbClose(db);
1141 if (xx && rc == 0) rc = xx;
1148 * Find file matches in database.
1149 * @param db rpm database
1154 * @return 0 on success, 1 on not found, -2 on error
1156 static int rpmdbFindByFile(rpmdb db, /*@null@*/ const char * filespec,
1157 DBT * key, DBT * data, /*@out@*/ dbiIndexSet * matches)
1158 /*@globals rpmGlobalMacroContext, fileSystem @*/
1159 /*@modifies db, *key, *data, *matches, rpmGlobalMacroContext,
1161 /*@requires maxSet(matches) >= 0 @*/
1163 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
1164 HFD_t hfd = headerFreeData;
1165 const char * dirName;
1166 const char * baseName;
1167 rpmTagType bnt, dnt;
1168 fingerPrintCache fpc;
1170 dbiIndex dbi = NULL;
1172 dbiIndexSet allMatches = NULL;
1173 dbiIndexItem rec = NULL;
1179 if (filespec == NULL) return -2;
1182 if ((baseName = strrchr(filespec, '/')) != NULL) {
1186 len = baseName - filespec + 1;
1188 t = strncpy(alloca(len + 1), filespec, len);
1195 baseName = filespec;
1198 if (baseName == NULL)
1201 fpc = fpCacheCreate(20);
1202 fp1 = fpLookup(fpc, dirName, baseName, 1);
1204 dbi = dbiOpen(db, RPMTAG_BASENAMES, 0);
1208 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1211 key->data = (void *) baseName;
1213 key->size = strlen(baseName);
1214 if (key->size == 0) key->size++; /* XXX "/" fixup. */
1216 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1218 rpmError(RPMERR_DBGETINDEX,
1219 _("error(%d) getting \"%s\" records from %s index\n"),
1220 rc, key->data, tagName(dbi->dbi_rpmtag));
1224 (void) dbt2set(dbi, data, &allMatches);
1226 xx = dbiCclose(dbi, dbcursor, 0);
1233 allMatches = dbiFreeIndexSet(allMatches);
1234 fpc = fpCacheFree(fpc);
1238 *matches = xcalloc(1, sizeof(**matches));
1239 rec = dbiIndexNewItem(0, 0);
1241 if (allMatches != NULL)
1242 while (i < allMatches->count) {
1243 const char ** baseNames, ** dirNames;
1244 int_32 * dirIndexes;
1245 unsigned int offset = dbiIndexRecordOffset(allMatches, i);
1246 unsigned int prevoff;
1249 { rpmdbMatchIterator mi;
1250 mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset));
1251 h = rpmdbNextIterator(mi);
1254 mi = rpmdbFreeIterator(mi);
1262 xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, NULL);
1263 xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
1264 xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
1268 int num = dbiIndexRecordFileNumber(allMatches, i);
1270 fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
1272 if (FP_EQUAL(fp1, fp2)) {
1274 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
1275 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
1276 xx = dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
1281 offset = dbiIndexRecordOffset(allMatches, i);
1282 } while (i < allMatches->count &&
1283 (i == 0 || offset == prevoff));
1285 baseNames = hfd(baseNames, bnt);
1286 dirNames = hfd(dirNames, dnt);
1291 allMatches = dbiFreeIndexSet(allMatches);
1293 fpc = fpCacheFree(fpc);
1295 if ((*matches)->count == 0) {
1296 *matches = dbiFreeIndexSet(*matches);
1303 /* XXX python/upgrade.c, install.c, uninstall.c */
1304 int rpmdbCountPackages(rpmdb db, const char * name)
1306 DBC * dbcursor = NULL;
1307 DBT * key = alloca(sizeof(*key));
1308 DBT * data = alloca(sizeof(*data));
1316 memset(key, 0, sizeof(*key));
1317 memset(data, 0, sizeof(*data));
1319 dbi = dbiOpen(db, RPMTAG_NAME, 0);
1324 key->data = (void *) name;
1326 key->size = strlen(name);
1328 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
1329 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1330 xx = dbiCclose(dbi, dbcursor, 0);
1333 if (rc == 0) { /* success */
1334 dbiIndexSet matches;
1335 /*@-nullpass@*/ /* FIX: matches might be NULL */
1337 (void) dbt2set(dbi, data, &matches);
1339 rc = dbiIndexSetCount(matches);
1340 matches = dbiFreeIndexSet(matches);
1344 if (rc == DB_NOTFOUND) { /* not found */
1346 } else { /* error */
1347 rpmError(RPMERR_DBGETINDEX,
1348 _("error(%d) getting \"%s\" records from %s index\n"),
1349 rc, key->data, tagName(dbi->dbi_rpmtag));
1357 * Attempt partial matches on name[-version[-release]] strings.
1358 * @param dbi index database handle (always RPMTAG_NAME)
1359 * @param dbcursor index database cursor
1360 * @param key search key/length/flags
1361 * @param data search data/length/flags
1362 * @param name package name
1363 * @param version package version (can be a pattern)
1364 * @param release package release (can be a pattern)
1365 * @retval matches set of header instances that match
1366 * @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1368 static rpmRC dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
1369 DBT * key, DBT * data,
1371 /*@null@*/ const char * version,
1372 /*@null@*/ const char * release,
1373 /*@out@*/ dbiIndexSet * matches)
1374 /*@globals rpmGlobalMacroContext, fileSystem @*/
1375 /*@modifies dbi, *dbcursor, *key, *data, *matches,
1376 rpmGlobalMacroContext, fileSystem @*/
1377 /*@requires maxSet(matches) >= 0 @*/
1384 key->data = (void *) name;
1386 key->size = strlen(name);
1388 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
1390 if (rc == 0) { /* success */
1391 (void) dbt2set(dbi, data, matches);
1392 if (version == NULL && release == NULL)
1395 if (rc == DB_NOTFOUND) { /* not found */
1396 return RPMRC_NOTFOUND;
1397 } else { /* error */
1398 rpmError(RPMERR_DBGETINDEX,
1399 _("error(%d) getting \"%s\" records from %s index\n"),
1400 rc, key->data, tagName(dbi->dbi_rpmtag));
1404 /* Make sure the version and release match. */
1406 for (i = 0; i < dbiIndexSetCount(*matches); i++) {
1407 unsigned int recoff = dbiIndexRecordOffset(*matches, i);
1408 rpmdbMatchIterator mi;
1414 mi = rpmdbInitIterator(dbi->dbi_rpmdb,
1415 RPMDBI_PACKAGES, &recoff, sizeof(recoff));
1417 /* Set iterator selectors for version/release if available. */
1419 rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version))
1425 rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release))
1431 h = rpmdbNextIterator(mi);
1434 (*matches)->recs[gotMatches++] = (*matches)->recs[i];
1436 (*matches)->recs[i].hdrNum = 0;
1438 mi = rpmdbFreeIterator(mi);
1443 (*matches)->count = gotMatches;
1446 rc = RPMRC_NOTFOUND;
1449 /*@-unqualifiedtrans@*/ /* FIX: double indirection */
1450 if (rc && matches && *matches)
1451 *matches = dbiFreeIndexSet(*matches);
1452 /*@=unqualifiedtrans@*/
1457 * Lookup by name, name-version, and finally by name-version-release.
1458 * Both version and release can be patterns.
1459 * @todo Name must be an exact match, as name is a db key.
1460 * @param dbi index database handle (always RPMTAG_NAME)
1461 * @param dbcursor index database cursor
1462 * @param key search key/length/flags
1463 * @param data search data/length/flags
1464 * @param arg name[-version[-release]] string
1465 * @retval matches set of header instances that match
1466 * @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
1468 static rpmRC dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
1469 /*@null@*/ const char * arg, /*@out@*/ dbiIndexSet * matches)
1470 /*@globals rpmGlobalMacroContext, fileSystem @*/
1471 /*@modifies dbi, *dbcursor, *key, *data, *matches,
1472 rpmGlobalMacroContext, fileSystem @*/
1473 /*@requires maxSet(matches) >= 0 @*/
1475 const char * release;
1482 if (arg == NULL || strlen(arg) == 0) return RPMRC_NOTFOUND;
1484 /* did they give us just a name? */
1485 rc = dbiFindMatches(dbi, dbcursor, key, data, arg, NULL, NULL, matches);
1486 if (rc != RPMRC_NOTFOUND) return rc;
1488 /*@-unqualifiedtrans@*/ /* FIX: double indirection */
1489 *matches = dbiFreeIndexSet(*matches);
1490 /*@=unqualifiedtrans@*/
1492 /* maybe a name and a release */
1493 localarg = alloca(strlen(arg) + 1);
1494 s = stpcpy(localarg, arg);
1498 for (s -= 1; s > localarg; s--) {
1502 /*@switchbreak@*/ break;
1504 if (c != '[') brackets = 0;
1505 /*@switchbreak@*/ break;
1508 if (!brackets && *s == '-')
1512 /*@-nullstate@*/ /* FIX: *matches may be NULL. */
1513 if (s == localarg) return RPMRC_NOTFOUND;
1518 rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, NULL, matches);
1520 if (rc != RPMRC_NOTFOUND) return rc;
1522 /*@-unqualifiedtrans@*/ /* FIX: double indirection */
1523 *matches = dbiFreeIndexSet(*matches);
1524 /*@=unqualifiedtrans@*/
1526 /* how about name-version-release? */
1532 for (; s > localarg; s--) {
1536 /*@switchbreak@*/ break;
1538 if (c != '[') brackets = 0;
1539 /*@switchbreak@*/ break;
1542 if (!brackets && *s == '-')
1546 if (s == localarg) return RPMRC_NOTFOUND;
1551 /*@-nullstate@*/ /* FIX: *matches may be NULL. */
1552 return dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, release, matches);
1556 typedef struct miRE_s {
1557 rpmTag tag; /*!< header tag */
1558 rpmMireMode mode; /*!< pattern match mode */
1559 /*@only@*/ const char * pattern; /*!< pattern string */
1560 int notmatch; /*!< like "grep -v" */
1561 /*@only@*/ regex_t * preg; /*!< regex compiled pattern buffer */
1562 int cflags; /*!< regcomp(3) flags */
1563 int eflags; /*!< regexec(3) flags */
1564 int fnflags; /*!< fnmatch(3) flags */
1567 struct _rpmdbMatchIterator {
1569 const void * mi_keyp;
1579 /*@refcounted@*/ /*@null@*/
1584 unsigned int mi_prevoffset;
1585 unsigned int mi_offset;
1586 unsigned int mi_filenum;
1588 /*@only@*/ /*@null@*/
1593 rpmRC (*mi_hdrchk) (rpmts ts, const void * uh, size_t uc, const char ** msg)
1594 /*@modifies ts, *msg @*/;
1599 * Rewrite a header into packages (if necessary) and free the header.
1600 * Note: this is called from a markReplacedFiles iteration, and *must*
1601 * preserve the "join key" (i.e. offset) for the header.
1602 * @param mi database iterator
1603 * @param dbi index database handle
1604 * @return 0 on success
1606 static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi)
1607 /*@globals fileSystem @*/
1608 /*@modifies mi, fileSystem @*/
1612 if (mi == NULL || mi->mi_h == NULL)
1615 if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
1616 DBT * key = &mi->mi_key;
1617 DBT * data = &mi->mi_data;
1618 sigset_t signalMask;
1619 rpmRC rpmrc = RPMRC_NOTFOUND;
1622 /*@i@*/ key->data = (void *) &mi->mi_prevoffset;
1623 key->size = sizeof(mi->mi_prevoffset);
1624 data->data = headerUnload(mi->mi_h);
1625 data->size = headerSizeof(mi->mi_h, HEADER_MAGIC_NO);
1627 /* Check header digest/signature on blob export (if requested). */
1628 if (mi->mi_hdrchk && mi->mi_ts) {
1629 const char * msg = NULL;
1632 rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, data->data, data->size, &msg);
1633 lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG);
1634 rpmMessage(lvl, "%s h#%8u %s",
1635 (rpmrc == RPMRC_FAIL ? _("miFreeHeader: skipping") : "write"),
1636 mi->mi_prevoffset, (msg ? msg : "\n"));
1640 if (data->data != NULL && rpmrc != RPMRC_FAIL) {
1641 (void) blockSignals(dbi->dbi_rpmdb, &signalMask);
1642 rc = dbiPut(dbi, mi->mi_dbc, key, data, DB_KEYLAST);
1644 rpmError(RPMERR_DBPUTINDEX,
1645 _("error(%d) storing record #%d into %s\n"),
1646 rc, mi->mi_prevoffset, tagName(dbi->dbi_rpmtag));
1648 xx = dbiSync(dbi, 0);
1649 (void) unblockSignals(dbi->dbi_rpmdb, &signalMask);
1651 data->data = _free(data->data);
1655 mi->mi_h = headerFree(mi->mi_h);
1662 rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
1671 dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
1672 if (dbi == NULL) /* XXX can't happen */
1675 xx = miFreeHeader(mi, dbi);
1678 xx = dbiCclose(dbi, mi->mi_dbc, 0);
1681 if (mi->mi_re != NULL)
1682 for (i = 0; i < mi->mi_nre; i++) {
1683 miRE mire = mi->mi_re + i;
1684 mire->pattern = _free(mire->pattern);
1685 if (mire->preg != NULL) {
1686 regfree(mire->preg);
1687 /*@+voidabstract -usereleased @*/ /* LCL: regfree has bogus only */
1688 mire->preg = _free(mire->preg);
1689 /*@=voidabstract =usereleased @*/
1692 mi->mi_re = _free(mi->mi_re);
1694 mi->mi_set = dbiFreeIndexSet(mi->mi_set);
1695 mi->mi_keyp = _free(mi->mi_keyp);
1696 mi->mi_db = rpmdbUnlink(mi->mi_db, "matchIterator");
1699 (void) rpmdbCheckSignals();
1704 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
1705 return (mi ? mi->mi_offset : 0);
1708 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
1709 return (mi ? mi->mi_filenum : 0);
1712 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
1713 return (mi && mi->mi_set ? mi->mi_set->count : 0);
1717 * Return pattern match.
1718 * @param mire match iterator regex
1719 * @param val value to match
1720 * @return 0 if pattern matches, >0 on nomatch, <0 on error
1722 static int miregexec(miRE mire, const char * val)
1727 switch (mire->mode) {
1728 case RPMMIRE_STRCMP:
1729 rc = strcmp(mire->pattern, val);
1732 case RPMMIRE_DEFAULT:
1735 rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
1737 if (rc && rc != REG_NOMATCH) {
1739 (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
1740 msg[sizeof(msg)-1] = '\0';
1741 rpmError(RPMERR_REGEXEC, "%s: regexec failed: %s\n",
1742 mire->pattern, msg);
1747 rc = fnmatch(mire->pattern, val, mire->fnflags);
1748 if (rc && rc != FNM_NOMATCH)
1760 * Compare iterator selectors by rpm tag (qsort/bsearch).
1761 * @param a 1st iterator selector
1762 * @param b 2nd iterator selector
1763 * @return result of comparison
1765 static int mireCmp(const void * a, const void * b)
1767 const miRE mireA = (const miRE) a;
1768 const miRE mireB = (const miRE) b;
1769 return (mireA->tag - mireB->tag);
1773 * Copy pattern, escaping for appropriate mode.
1774 * @param tag rpm tag
1775 * @retval modep type of pattern match
1776 * @param pattern pattern to duplicate
1777 * @return duplicated pattern
1779 static /*@only@*/ char * mireDup(rpmTag tag, rpmMireMode *modep,
1780 const char * pattern)
1781 /*@modifies *modep @*/
1782 /*@requires maxSet(modep) >= 0 @*/
1794 case RPMMIRE_DEFAULT:
1795 if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
1796 *modep = RPMMIRE_GLOB;
1797 pat = xstrdup(pattern);
1801 nb = strlen(pattern) + sizeof("^$");
1803 /* Find no. of bytes needed for pattern. */
1804 /* periods are escaped, splats become '.*' */
1807 for (s = pattern; *s != '\0'; s++) {
1811 if (!brackets) nb++;
1812 /*@switchbreak@*/ break;
1815 /*@switchbreak@*/ break;
1818 /*@switchbreak@*/ break;
1820 if (c != '[') brackets = 0;
1821 /*@switchbreak@*/ break;
1826 pat = t = xmalloc(nb);
1828 if (pattern[0] != '^') *t++ = '^';
1830 /* Copy pattern, escaping periods, prefixing splats with period. */
1833 for (s = pattern; *s != '\0'; s++, t++) {
1836 if (!brackets) *t++ = '\\';
1837 /*@switchbreak@*/ break;
1839 if (!brackets) *t++ = '.';
1840 /*@switchbreak@*/ break;
1843 /*@switchbreak@*/ break;
1846 /*@switchbreak@*/ break;
1848 if (c != '[') brackets = 0;
1849 /*@switchbreak@*/ break;
1854 if (s > pattern && s[-1] != '$') *t++ = '$';
1856 *modep = RPMMIRE_REGEX;
1858 case RPMMIRE_STRCMP:
1861 pat = xstrdup(pattern);
1869 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
1870 rpmMireMode mode, const char * pattern)
1872 static rpmMireMode defmode = (rpmMireMode)-1;
1874 const char * allpat = NULL;
1876 regex_t * preg = NULL;
1883 if (defmode == (rpmMireMode)-1) {
1884 const char *t = rpmExpand("%{?_query_selector_match}", NULL);
1886 if (*t == '\0' || !strcmp(t, "default"))
1887 defmode = RPMMIRE_DEFAULT;
1888 else if (!strcmp(t, "strcmp"))
1889 defmode = RPMMIRE_STRCMP;
1890 else if (!strcmp(t, "regex"))
1891 defmode = RPMMIRE_REGEX;
1892 else if (!strcmp(t, "glob"))
1893 defmode = RPMMIRE_GLOB;
1895 defmode = RPMMIRE_DEFAULT;
1899 if (mi == NULL || pattern == NULL)
1902 /* Leading '!' inverts pattern match sense, like "grep -v". */
1903 if (*pattern == '!') {
1910 allpat = mireDup(tag, &mode, pattern);
1913 if (mode == RPMMIRE_DEFAULT)
1918 case RPMMIRE_DEFAULT:
1919 case RPMMIRE_STRCMP:
1923 preg = xcalloc(1, sizeof(*preg));
1925 cflags = (REG_EXTENDED | REG_NOSUB);
1926 rc = regcomp(preg, allpat, cflags);
1929 (void) regerror(rc, preg, msg, sizeof(msg)-1);
1930 msg[sizeof(msg)-1] = '\0';
1931 rpmError(RPMERR_REGCOMP, "%s: regcomp failed: %s\n", allpat, msg);
1935 fnflags = FNM_PATHNAME | FNM_PERIOD;
1944 /*@=kepttrans@*/ /* FIX: mire has kept values */
1945 allpat = _free(allpat);
1948 /*@+voidabstract -usereleased @*/ /* LCL: regfree has bogus only */
1950 /*@=voidabstract =usereleased @*/
1956 mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
1957 mire = mi->mi_re + mi->mi_nre;
1962 mire->pattern = allpat;
1963 mire->notmatch = notmatch;
1965 mire->cflags = cflags;
1966 mire->eflags = eflags;
1967 mire->fnflags = fnflags;
1971 qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
1978 * Return iterator selector match.
1979 * @param mi rpm database iterator
1980 * @return 1 if header should be skipped
1982 static int mireSkip (const rpmdbMatchIterator mi)
1985 HGE_t hge = (HGE_t) headerGetEntryMinMemory;
1986 HFD_t hfd = (HFD_t) headerFreeData;
1999 static int_32 zero = 0;
2005 if (mi->mi_h == NULL) /* XXX can't happen */
2009 * Apply tag tests, implicitly "||" for multiple patterns/values of a
2010 * single tag, implicitly "&&" between multiple tag patterns.
2013 if ((mire = mi->mi_re) != NULL)
2014 for (i = 0; i < mi->mi_nre; i++, mire++) {
2017 if (!hge(mi->mi_h, mire->tag, &t, (void **)&u, &c)) {
2018 if (mire->tag != RPMTAG_EPOCH)
2021 /*@-immediatetrans@*/
2023 /*@=immediatetrans@*/
2027 anymatch = 0; /* no matches yet */
2032 sprintf(numbuf, "%d", (int) *u.i8p);
2033 rc = miregexec(mire, numbuf);
2034 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
2036 /*@switchbreak@*/ break;
2037 case RPM_INT16_TYPE:
2038 sprintf(numbuf, "%d", (int) *u.i16p);
2039 rc = miregexec(mire, numbuf);
2040 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
2042 /*@switchbreak@*/ break;
2043 case RPM_INT32_TYPE:
2044 sprintf(numbuf, "%d", (int) *u.i32p);
2045 rc = miregexec(mire, numbuf);
2046 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
2048 /*@switchbreak@*/ break;
2049 case RPM_STRING_TYPE:
2050 rc = miregexec(mire, u.str);
2051 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
2053 /*@switchbreak@*/ break;
2054 case RPM_I18NSTRING_TYPE:
2055 case RPM_STRING_ARRAY_TYPE:
2056 for (j = 0; j < c; j++) {
2057 rc = miregexec(mire, u.argv[j]);
2058 if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) {
2060 /*@innerbreak@*/ break;
2063 /*@switchbreak@*/ break;
2067 /*@switchbreak@*/ break;
2069 if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
2072 /*@innercontinue@*/ continue;
2074 /*@innerbreak@*/ break;
2078 u.ptr = hfd(u.ptr, t);
2085 return (ntags == nmatches ? 0 : 1);
2088 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite)
2093 rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0;
2095 mi->mi_cflags |= DB_WRITECURSOR;
2097 mi->mi_cflags &= ~DB_WRITECURSOR;
2101 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified)
2106 rc = mi->mi_modified;
2107 mi->mi_modified = modified;
2111 int rpmdbSetHdrChk(rpmdbMatchIterator mi, rpmts ts,
2112 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
2117 /*@-assignexpose -newreftrans @*/
2118 /*@i@*/ mi->mi_ts = ts;
2119 mi->mi_hdrchk = hdrchk;
2120 /*@=assignexpose =newreftrans @*/
2125 /*@-nullstate@*/ /* FIX: mi->mi_key.data may be NULL */
2126 Header rpmdbNextIterator(rpmdbMatchIterator mi)
2141 dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
2146 * Cursors are per-iterator, not per-dbi, so get a cursor for the
2147 * iterator on 1st call. If the iteration is to rewrite headers, and the
2148 * CDB model is used for the database, then the cursor needs to
2149 * marked with DB_WRITECURSOR as well.
2151 if (mi->mi_dbc == NULL)
2152 xx = dbiCopen(dbi, dbi->dbi_txnid, &mi->mi_dbc, mi->mi_cflags);
2156 memset(key, 0, sizeof(*key));
2157 data = &mi->mi_data;
2158 memset(data, 0, sizeof(*data));
2166 /*@-branchstate -compmempass @*/
2168 if (!(mi->mi_setx < mi->mi_set->count))
2170 mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
2171 mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
2172 keyp = &mi->mi_offset;
2173 keylen = sizeof(mi->mi_offset);
2176 key->data = keyp = (void *)mi->mi_keyp;
2177 key->size = keylen = mi->mi_keylen;
2180 #if !defined(_USE_COPY_LOAD)
2181 data->flags |= DB_DBT_MALLOC;
2183 rc = dbiGet(dbi, mi->mi_dbc, key, data,
2184 (key->data == NULL ? DB_NEXT : DB_SET));
2192 * If we got the next key, save the header instance number.
2194 * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
2195 * largest header instance in the database, and should be
2199 if (keyp && mi->mi_setx && rc == 0)
2200 memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
2203 /* Terminate on error or end of keys */
2204 if (rc || (mi->mi_setx && mi->mi_offset == 0))
2207 /*@=branchstate =compmempass @*/
2209 } while (mi->mi_offset == 0);
2211 /* If next header is identical, return it now. */
2212 /*@-compdef -refcounttrans -retalias -retexpose -usereleased @*/
2213 if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
2215 /*@=compdef =refcounttrans =retalias =retexpose =usereleased @*/
2217 /* Retrieve next header blob for index iterator. */
2218 /*@-branchstate -compmempass -immediatetrans @*/
2222 #if !defined(_USE_COPY_LOAD)
2223 data->flags |= DB_DBT_MALLOC;
2225 rc = dbiGet(dbi, mi->mi_dbc, key, data, DB_SET);
2234 /*@=branchstate =compmempass =immediatetrans @*/
2236 /* Rewrite current header (if necessary) and unlink. */
2237 xx = miFreeHeader(mi, dbi);
2239 /* Is this the end of the iteration? */
2243 /* Check header digest/signature once (if requested). */
2244 /*@-boundsread -branchstate -sizeoftype @*/
2245 if (mi->mi_hdrchk && mi->mi_ts) {
2246 rpmRC rpmrc = RPMRC_NOTFOUND;
2247 pbm_set * set = NULL;
2249 /* Don't bother re-checking a previously read header. */
2250 if (mi->mi_db->db_bits) {
2251 set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
2252 &mi->mi_db->db_nbits, mi->mi_offset);
2253 if (PBM_ISSET(mi->mi_offset, set))
2257 /* If blob is unchecked, check blob import consistency now. */
2258 if (rpmrc != RPMRC_OK) {
2259 const char * msg = NULL;
2262 rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg);
2263 lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG);
2264 rpmMessage(lvl, "%s h#%8u %s",
2265 (rpmrc == RPMRC_FAIL ? _("rpmdbNextIterator: skipping") : " read"),
2266 mi->mi_offset, (msg ? msg : "\n"));
2269 /* Mark header checked. */
2270 if (set && rpmrc == RPMRC_OK)
2271 PBM_SET(mi->mi_offset, set);
2273 /* Skip damaged and inconsistent headers. */
2274 if (rpmrc == RPMRC_FAIL)
2278 /*@=boundsread =branchstate =sizeoftype @*/
2280 /* Did the header blob load correctly? */
2281 #if !defined(_USE_COPY_LOAD)
2283 mi->mi_h = headerLoad(uh);
2286 mi->mi_h->flags |= HEADERFLAG_ALLOCATED;
2288 mi->mi_h = headerCopyLoad(uh);
2290 if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
2291 rpmError(RPMERR_BADHEADER,
2292 _("rpmdb: damaged header #%u retrieved -- skipping.\n"),
2298 * Skip this header if iterator selector (if any) doesn't match.
2301 /* XXX hack, can't restart with Packages locked on single instance. */
2302 if (mi->mi_set || mi->mi_keyp == NULL)
2307 mi->mi_prevoffset = mi->mi_offset;
2308 mi->mi_modified = 0;
2310 /*@-compdef -retalias -retexpose -usereleased @*/
2312 /*@=compdef =retalias =retexpose =usereleased @*/
2316 static void rpmdbSortIterator(/*@null@*/ rpmdbMatchIterator mi)
2319 if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
2321 * mergesort is much (~10x with lots of identical basenames) faster
2322 * than pure quicksort, but glibc uses msort_with_tmp() on stack.
2324 #if defined(__GLIBC__)
2326 qsort(mi->mi_set->recs, mi->mi_set->count,
2327 sizeof(*mi->mi_set->recs), hdrNumCmp);
2330 mergesort(mi->mi_set->recs, mi->mi_set->count,
2331 sizeof(*mi->mi_set->recs), hdrNumCmp);
2337 /*@-bounds@*/ /* LCL: segfault */
2338 static int rpmdbGrowIterator(/*@null@*/ rpmdbMatchIterator mi, int fpNum)
2339 /*@globals rpmGlobalMacroContext, fileSystem @*/
2340 /*@modifies mi, rpmGlobalMacroContext, fileSystem @*/
2345 dbiIndex dbi = NULL;
2354 dbcursor = mi->mi_dbc;
2356 data = &mi->mi_data;
2357 if (key->data == NULL)
2360 dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0);
2364 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2365 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2366 xx = dbiCclose(dbi, dbcursor, 0);
2369 if (rc) { /* error/not found */
2370 if (rc != DB_NOTFOUND)
2371 rpmError(RPMERR_DBGETINDEX,
2372 _("error(%d) getting \"%s\" records from %s index\n"),
2373 rc, key->data, tagName(dbi->dbi_rpmtag));
2378 (void) dbt2set(dbi, data, &set);
2379 for (i = 0; i < set->count; i++)
2380 set->recs[i].fpNum = fpNum;
2383 if (mi->mi_set == NULL) {
2387 fprintf(stderr, "+++ %d = %d + %d\t\"%s\"\n", (mi->mi_set->count + set->count), mi->mi_set->count, set->count, ((char *)key->data));
2389 mi->mi_set->recs = xrealloc(mi->mi_set->recs,
2390 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
2391 memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
2392 set->count * sizeof(*(mi->mi_set->recs)));
2393 mi->mi_set->count += set->count;
2394 set = dbiFreeIndexSet(set);
2402 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
2403 int nHdrNums, int sorted)
2405 if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2409 (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
2413 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
2415 if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
2418 if (mi->mi_set == NULL)
2419 mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
2420 (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
2424 rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmTag rpmtag,
2425 const void * keyp, size_t keylen)
2427 rpmdbMatchIterator mi;
2430 dbiIndexSet set = NULL;
2432 const void * mi_keyp = NULL;
2438 (void) rpmdbCheckSignals();
2440 /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
2441 if (rpmtag == RPMDBI_LABEL) {
2442 rpmtag = RPMTAG_NAME;
2446 dbi = dbiOpen(db, rpmtag, 0);
2450 mi = xcalloc(1, sizeof(*mi));
2452 data = &mi->mi_data;
2455 if (rpmtag != RPMDBI_PACKAGES && keyp) {
2456 DBC * dbcursor = NULL;
2461 /* XXX HACK to get rpmdbFindByLabel out of the API */
2462 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2463 rc = dbiFindByLabel(dbi, dbcursor, key, data, keyp, &set);
2464 xx = dbiCclose(dbi, dbcursor, 0);
2466 } else if (rpmtag == RPMTAG_BASENAMES) {
2467 rc = rpmdbFindByFile(db, keyp, key, data, &set);
2469 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
2472 key->data = (void *) keyp;
2475 if (key->data && key->size == 0) key->size = strlen((char *)key->data);
2476 if (key->data && key->size == 0) key->size++; /* XXX "/" fixup. */
2479 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2482 rpmError(RPMERR_DBGETINDEX,
2483 _("error(%d) getting \"%s\" records from %s index\n"),
2484 rc, (key->data ? key->data : "???"), tagName(dbi->dbi_rpmtag));
2488 (void) dbt2set(dbi, data, &set);
2490 xx = dbiCclose(dbi, dbcursor, 0);
2493 if (rc) { /* error/not found */
2494 set = dbiFreeIndexSet(set);
2504 if (rpmtag != RPMDBI_PACKAGES && keylen == 0)
2505 keylen = strlen(keyp);
2506 k = xmalloc(keylen + 1);
2508 memcpy(k, keyp, keylen);
2510 k[keylen] = '\0'; /* XXX for strings */
2514 mi->mi_keyp = mi_keyp;
2515 mi->mi_keylen = keylen;
2517 mi->mi_db = rpmdbLink(db, "matchIterator");
2518 mi->mi_rpmtag = rpmtag;
2526 mi->mi_modified = 0;
2527 mi->mi_prevoffset = 0;
2534 mi->mi_hdrchk = NULL;
2536 /*@-nullret@*/ /* FIX: mi->mi_key.data may be NULL */
2542 int rpmdbRemove(rpmdb db, /*@unused@*/ int rid, unsigned int hdrNum,
2543 /*@unused@*/ rpmts ts,
2544 /*@unused@*/ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
2546 DBC * dbcursor = NULL;
2547 DBT * key = alloca(sizeof(*key));
2548 DBT * data = alloca(sizeof(*data));
2549 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
2550 HFD_t hfd = headerFreeData;
2552 sigset_t signalMask;
2559 memset(key, 0, sizeof(*key));
2560 memset(data, 0, sizeof(*data));
2562 { rpmdbMatchIterator mi;
2563 mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
2564 h = rpmdbNextIterator(mi);
2567 mi = rpmdbFreeIterator(mi);
2571 rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
2572 "rpmdbRemove", hdrNum);
2577 /* Add remove transaction id to header. */
2578 if (rid != 0 && rid != -1) {
2580 (void) headerAddEntry(h, RPMTAG_REMOVETID, RPM_INT32_TYPE, &tid, 1);
2584 { const char *n, *v, *r;
2585 (void) headerNVR(h, &n, &v, &r);
2586 rpmMessage(RPMMESS_DEBUG, " --- h#%8u %s-%s-%s\n", hdrNum, n, v, r);
2589 (void) blockSignals(db, &signalMask);
2591 /*@-nullpass -nullptrarith -nullderef @*/ /* FIX: rpmvals heartburn */
2593 dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2595 if (dbiTags != NULL)
2596 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
2599 const char ** rpmvals = NULL;
2600 rpmTagType rpmtype = 0;
2608 rpmtag = dbiTags[dbix];
2613 /* Filter out temporary databases */
2614 case RPMDBI_AVAILABLE:
2616 case RPMDBI_REMOVED:
2617 case RPMDBI_DEPENDS:
2619 /*@notreached@*/ /*@switchbreak@*/ break;
2620 case RPMDBI_PACKAGES:
2621 dbi = dbiOpen(db, rpmtag, 0);
2622 if (dbi == NULL) /* XXX shouldn't happen */
2625 /*@-immediatetrans@*/
2626 key->data = &hdrNum;
2627 /*@=immediatetrans@*/
2628 key->size = sizeof(hdrNum);
2630 rc = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2631 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2633 rpmError(RPMERR_DBGETINDEX,
2634 _("error(%d) setting header #%d record for %s removal\n"),
2635 rc, hdrNum, tagName(dbi->dbi_rpmtag));
2637 rc = dbiDel(dbi, dbcursor, key, data, 0);
2638 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2640 if (!dbi->dbi_no_dbsync)
2641 xx = dbiSync(dbi, 0);
2643 /*@notreached@*/ /*@switchbreak@*/ break;
2647 if (!hge(h, rpmtag, &rpmtype, (void **) &rpmvals, &rpmcnt))
2650 dbi = dbiOpen(db, rpmtag, 0);
2654 if (rpmtype == RPM_STRING_TYPE) {
2655 /* XXX force uniform headerGetEntry return */
2656 av[0] = (const char *) rpmvals;
2662 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2664 for (i = 0; i < rpmcnt; i++) {
2669 switch (dbi->dbi_rpmtag) {
2670 case RPMTAG_FILEMD5S:
2671 /* Filter out empty MD5 strings. */
2672 if (!(rpmvals[i] && *rpmvals[i] != '\0'))
2673 /*@innercontinue@*/ continue;
2674 /*@switchbreak@*/ break;
2676 /*@switchbreak@*/ break;
2679 /* Identify value pointer and length. */
2685 key->size = sizeof(RPM_CHAR_TYPE);
2686 key->data = rpmvals + i;
2687 /*@switchbreak@*/ break;
2688 case RPM_INT16_TYPE:
2689 key->size = sizeof(int_16);
2690 key->data = rpmvals + i;
2691 /*@switchbreak@*/ break;
2692 case RPM_INT32_TYPE:
2693 key->size = sizeof(int_32);
2694 key->data = rpmvals + i;
2695 /*@switchbreak@*/ break;
2699 key->data = rpmvals;
2700 rpmcnt = 1; /* XXX break out of loop. */
2701 /*@switchbreak@*/ break;
2702 case RPM_STRING_TYPE:
2703 case RPM_I18NSTRING_TYPE:
2704 rpmcnt = 1; /* XXX break out of loop. */
2706 case RPM_STRING_ARRAY_TYPE:
2707 /* Convert from hex to binary. */
2709 if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) {
2715 for (j = 0; j < 16; j++, t++, s += 2)
2716 *t = (nibble(s[0]) << 4) | nibble(s[1]);
2719 /*@switchbreak@*/ break;
2721 /* Extract the pubkey id from the base64 blob. */
2722 if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
2723 pgpDig dig = pgpNewDig();
2727 if (b64decode(rpmvals[i], (void **)&pkt, &pktlen))
2728 /*@innercontinue@*/ continue;
2729 (void) pgpPrtPkts(pkt, pktlen, dig, 0);
2730 memcpy(bin, dig->pubkey.signid, 8);
2735 /*@switchbreak@*/ break;
2740 /*@i@*/ key->data = (void *) rpmvals[i];
2741 key->size = strlen(rpmvals[i]);
2743 /*@switchbreak@*/ break;
2747 if (rpmcnt == 1 && stringvalued) {
2748 rpmMessage(RPMMESS_DEBUG,
2749 _("removing \"%s\" from %s index.\n"),
2750 (char *)key->data, tagName(dbi->dbi_rpmtag));
2752 rpmMessage(RPMMESS_DEBUG,
2753 _("removing %d entries from %s index.\n"),
2754 rpmcnt, tagName(dbi->dbi_rpmtag));
2760 * This is almost right, but, if there are duplicate tag
2761 * values, there will be duplicate attempts to remove
2762 * the header instance. It's faster to just ignore errors
2763 * than to do things correctly.
2766 /* XXX with duplicates, an accurate data value and DB_GET_BOTH is needed. */
2770 if (key->size == 0) key->size = strlen((char *)key->data);
2771 if (key->size == 0) key->size++; /* XXX "/" fixup. */
2774 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
2775 if (rc == 0) { /* success */
2776 (void) dbt2set(dbi, data, &set);
2777 } else if (rc == DB_NOTFOUND) { /* not found */
2778 /*@innercontinue@*/ continue;
2779 } else { /* error */
2780 rpmError(RPMERR_DBGETINDEX,
2781 _("error(%d) setting \"%s\" records from %s index\n"),
2782 rc, key->data, tagName(dbi->dbi_rpmtag));
2784 /*@innercontinue@*/ continue;
2788 rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
2790 /* If nothing was pruned, then don't bother updating. */
2792 set = dbiFreeIndexSet(set);
2793 /*@innercontinue@*/ continue;
2797 if (set->count > 0) {
2798 (void) set2dbt(dbi, data, set);
2799 rc = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
2801 rpmError(RPMERR_DBPUTINDEX,
2802 _("error(%d) storing record \"%s\" into %s\n"),
2803 rc, key->data, tagName(dbi->dbi_rpmtag));
2806 data->data = _free(data->data);
2809 rc = dbiDel(dbi, dbcursor, key, data, 0);
2811 rpmError(RPMERR_DBPUTINDEX,
2812 _("error(%d) removing record \"%s\" from %s\n"),
2813 rc, key->data, tagName(dbi->dbi_rpmtag));
2818 set = dbiFreeIndexSet(set);
2822 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2825 if (!dbi->dbi_no_dbsync)
2826 xx = dbiSync(dbi, 0);
2829 if (rpmtype != RPM_BIN_TYPE) /* XXX WTFO? HACK ALERT */
2830 rpmvals = hfd(rpmvals, rpmtype);
2837 /*@=nullpass =nullptrarith =nullderef @*/
2839 (void) unblockSignals(db, &signalMask);
2843 /* XXX return ret; */
2848 int rpmdbAdd(rpmdb db, int iid, Header h,
2849 /*@unused@*/ rpmts ts,
2850 /*@unused@*/ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
2852 DBC * dbcursor = NULL;
2853 DBT * key = alloca(sizeof(*key));
2854 DBT * data = alloca(sizeof(*data));
2855 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
2856 HFD_t hfd = headerFreeData;
2857 sigset_t signalMask;
2858 const char ** baseNames;
2863 unsigned int hdrNum = 0;
2871 memset(key, 0, sizeof(*key));
2872 memset(data, 0, sizeof(*data));
2874 #ifdef NOTYET /* XXX headerRemoveEntry() broken on dribbles. */
2875 xx = headerRemoveEntry(h, RPMTAG_REMOVETID);
2877 if (iid != 0 && iid != -1) {
2879 if (!headerIsEntry(h, RPMTAG_INSTALLTID))
2880 xx = headerAddEntry(h, RPMTAG_INSTALLTID, RPM_INT32_TYPE, &tid, 1);
2884 * If old style filename tags is requested, the basenames need to be
2885 * retrieved early, and the header needs to be converted before
2886 * being written to the package header database.
2889 xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, &count);
2894 (void) blockSignals(db, &signalMask);
2897 unsigned int firstkey = 0;
2898 void * keyp = &firstkey;
2899 size_t keylen = sizeof(firstkey);
2900 void * datap = NULL;
2903 dbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
2907 /* XXX db0: hack to pass sizeof header to fadAlloc */
2909 datalen = headerSizeof(h, HEADER_MAGIC_NO);
2911 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
2913 /* Retrieve join key for next header instance. */
2918 /*@i@*/ data->data = datap;
2919 data->size = datalen;
2920 ret = dbiGet(dbi, dbcursor, key, data, DB_SET);
2924 datalen = data->size;
2929 if (ret == 0 && datap)
2930 memcpy(&hdrNum, datap, sizeof(hdrNum));
2932 if (ret == 0 && datap) {
2933 memcpy(datap, &hdrNum, sizeof(hdrNum));
2936 datalen = sizeof(hdrNum);
2945 data->size = datalen;
2948 ret = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
2950 xx = dbiSync(dbi, 0);
2952 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
2960 rpmError(RPMERR_DBCORRUPT,
2961 _("error(%d) allocating new package instance\n"), ret);
2965 /* Now update the indexes */
2968 { dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
2970 if (dbiTags != NULL)
2971 for (dbix = 0; dbix < dbiTagsMax; dbix++) {
2973 const char **rpmvals = NULL;
2974 rpmTagType rpmtype = 0;
2977 int_32 * requireFlags;
2981 rpmrc = RPMRC_NOTFOUND;
2983 requireFlags = NULL;
2985 rpmtag = dbiTags[dbix];
2989 /* Filter out temporary databases */
2990 case RPMDBI_AVAILABLE:
2992 case RPMDBI_REMOVED:
2993 case RPMDBI_DEPENDS:
2995 /*@notreached@*/ /*@switchbreak@*/ break;
2996 case RPMDBI_PACKAGES:
2997 dbi = dbiOpen(db, rpmtag, 0);
2998 if (dbi == NULL) /* XXX shouldn't happen */
3000 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
3002 key->data = (void *) &hdrNum;
3003 key->size = sizeof(hdrNum);
3004 data->data = headerUnload(h);
3005 data->size = headerSizeof(h, HEADER_MAGIC_NO);
3007 /* Check header digest/signature on blob export. */
3009 const char * msg = NULL;
3012 rpmrc = (*hdrchk) (ts, data->data, data->size, &msg);
3013 lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG);
3014 rpmMessage(lvl, "%s h#%8u %s",
3015 (rpmrc == RPMRC_FAIL ? _("rpmdbAdd: skipping") : " +++"),
3016 hdrNum, (msg ? msg : "\n"));
3020 if (data->data != NULL && rpmrc != RPMRC_FAIL) {
3022 xx = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
3024 xx = dbiSync(dbi, 0);
3026 data->data = _free(data->data);
3028 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
3030 if (!dbi->dbi_no_dbsync)
3031 xx = dbiSync(dbi, 0);
3033 /*@notreached@*/ /*@switchbreak@*/ break;
3034 case RPMTAG_BASENAMES: /* XXX preserve legacy behavior */
3036 rpmvals = baseNames;
3038 /*@switchbreak@*/ break;
3039 case RPMTAG_REQUIRENAME:
3040 xx = hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
3041 xx = hge(h, RPMTAG_REQUIREFLAGS, NULL, (void **)&requireFlags, NULL);
3042 /*@switchbreak@*/ break;
3044 xx = hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
3045 /*@switchbreak@*/ break;
3050 if (rpmtag != RPMTAG_GROUP)
3053 /* XXX preserve legacy behavior */
3054 rpmtype = RPM_STRING_TYPE;
3055 rpmvals = (const char **) "Unknown";
3060 dbi = dbiOpen(db, rpmtag, 0);
3064 if (rpmtype == RPM_STRING_TYPE) {
3065 /* XXX force uniform headerGetEntry return */
3066 /*@-observertrans@*/
3067 av[0] = (const char *) rpmvals;
3068 /*@=observertrans@*/
3074 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
3076 for (i = 0; i < rpmcnt; i++) {
3083 * Include the tagNum in all indices. rpm-3.0.4 and earlier
3084 * included the tagNum only for files.
3087 switch (dbi->dbi_rpmtag) {
3088 case RPMTAG_PUBKEYS:
3089 /*@switchbreak@*/ break;
3090 case RPMTAG_FILEMD5S:
3091 /* Filter out empty MD5 strings. */
3092 if (!(rpmvals[i] && *rpmvals[i] != '\0'))
3093 /*@innercontinue@*/ continue;
3094 /*@switchbreak@*/ break;
3095 case RPMTAG_REQUIRENAME:
3096 /* Filter out install prerequisites. */
3097 if (requireFlags && isInstallPreReq(requireFlags[i]))
3098 /*@innercontinue@*/ continue;
3099 /*@switchbreak@*/ break;
3100 case RPMTAG_TRIGGERNAME:
3101 if (i) { /* don't add duplicates */
3103 for (j = 0; j < i; j++) {
3104 if (!strcmp(rpmvals[i], rpmvals[j]))
3105 /*@innerbreak@*/ break;
3109 /*@innercontinue@*/ continue;
3111 /*@switchbreak@*/ break;
3113 /*@switchbreak@*/ break;
3116 /* Identify value pointer and length. */
3123 key->size = sizeof(int_8);
3124 /*@i@*/ key->data = rpmvals + i;
3125 /*@switchbreak@*/ break;
3126 case RPM_INT16_TYPE:
3127 key->size = sizeof(int_16);
3128 /*@i@*/ key->data = rpmvals + i;
3129 /*@switchbreak@*/ break;
3130 case RPM_INT32_TYPE:
3131 key->size = sizeof(int_32);
3132 /*@i@*/ key->data = rpmvals + i;
3133 /*@switchbreak@*/ break;
3137 /*@i@*/ key->data = rpmvals;
3138 rpmcnt = 1; /* XXX break out of loop. */
3139 /*@switchbreak@*/ break;
3140 case RPM_STRING_TYPE:
3141 case RPM_I18NSTRING_TYPE:
3142 rpmcnt = 1; /* XXX break out of loop. */
3144 case RPM_STRING_ARRAY_TYPE:
3145 /* Convert from hex to binary. */
3147 if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) {
3152 for (j = 0; j < 16; j++, t++, s += 2)
3153 *t = (nibble(s[0]) << 4) | nibble(s[1]);
3156 /*@switchbreak@*/ break;
3158 /* Extract the pubkey id from the base64 blob. */
3159 if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
3160 pgpDig dig = pgpNewDig();
3164 if (b64decode(rpmvals[i], (void **)&pkt, &pktlen))
3165 /*@innercontinue@*/ continue;
3166 (void) pgpPrtPkts(pkt, pktlen, dig, 0);
3167 memcpy(bin, dig->pubkey.signid, 8);
3172 /*@switchbreak@*/ break;
3177 /*@i@*/ key->data = (void *) rpmvals[i];
3178 key->size = strlen(rpmvals[i]);
3180 /*@switchbreak@*/ break;
3185 if (rpmcnt == 1 && stringvalued) {
3186 rpmMessage(RPMMESS_DEBUG,
3187 _("adding \"%s\" to %s index.\n"),
3188 (char *)key->data, tagName(dbi->dbi_rpmtag));
3190 rpmMessage(RPMMESS_DEBUG,
3191 _("adding %d entries to %s index.\n"),
3192 rpmcnt, tagName(dbi->dbi_rpmtag));
3197 /* XXX with duplicates, an accurate data value and DB_GET_BOTH is needed. */
3201 if (key->size == 0) key->size = strlen((char *)key->data);
3202 if (key->size == 0) key->size++; /* XXX "/" fixup. */
3205 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
3206 if (rc == 0) { /* success */
3207 /* With duplicates, cursor is positioned, discard the record. */
3208 if (!dbi->dbi_permit_dups)
3209 (void) dbt2set(dbi, data, &set);
3210 } else if (rc != DB_NOTFOUND) { /* error */
3211 rpmError(RPMERR_DBGETINDEX,
3212 _("error(%d) getting \"%s\" records from %s index\n"),
3213 rc, key->data, tagName(dbi->dbi_rpmtag));
3215 /*@innercontinue@*/ continue;
3219 if (set == NULL) /* not found or duplicate */
3220 set = xcalloc(1, sizeof(*set));
3222 (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
3225 (void) set2dbt(dbi, data, set);
3226 rc = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
3230 rpmError(RPMERR_DBPUTINDEX,
3231 _("error(%d) storing record %s into %s\n"),
3232 rc, key->data, tagName(dbi->dbi_rpmtag));
3235 /*@-unqualifiedtrans@*/
3236 data->data = _free(data->data);
3237 /*@=unqualifiedtrans@*/
3239 set = dbiFreeIndexSet(set);
3242 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
3245 if (!dbi->dbi_no_dbsync)
3246 xx = dbiSync(dbi, 0);
3249 /*@-observertrans@*/
3250 if (rpmtype != RPM_BIN_TYPE) /* XXX WTFO? HACK ALERT */
3251 rpmvals = hfd(rpmvals, rpmtype);
3252 /*@=observertrans@*/
3256 /*@=nullpass =nullptrarith =nullderef @*/
3262 (void) unblockSignals(db, &signalMask);
3267 #define _skip(_dn) { sizeof(_dn)-1, (_dn) }
3269 /*@unchecked@*/ /*@observer@*/
3270 static struct skipDir_s {
3272 /*@observer@*/ /*@null@*/
3275 _skip("/usr/share/zoneinfo"),
3276 _skip("/usr/share/locale"),
3277 _skip("/usr/share/i18n"),
3278 _skip("/usr/lib/locale"),
3282 static int skipDir(const char * dn)
3285 struct skipDir_s * sd = skipDirs;
3289 for (sd = skipDirs; sd->dn != NULL; sd++) {
3290 if (dnlen < sd->dnlen)
3292 if (strncmp(dn, sd->dn, sd->dnlen))
3299 /* XXX transaction.c */
3301 int rpmdbFindFpList(rpmdb db, fingerPrint * fpList, dbiIndexSet * matchList,
3306 HGE_t hge = (HGE_t)headerGetEntryMinMemory;
3307 HFD_t hfd = headerFreeData;
3308 rpmdbMatchIterator mi;
3309 fingerPrintCache fpc;
3313 if (db == NULL) return 0;
3315 mi = rpmdbInitIterator(db, RPMTAG_BASENAMES, NULL, 0);
3316 if (mi == NULL) /* XXX should never happen */
3320 data = &mi->mi_data;
3322 /* Gather all installed headers with matching basename's. */
3323 for (i = 0; i < numItems; i++) {
3326 matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
3329 /*@-boundsread -dependenttrans@*/
3330 key->data = (void *) fpList[i].baseName;
3331 /*@=boundsread =dependenttrans@*/
3332 key->size = strlen((char *)key->data);
3333 if (key->size == 0) key->size++; /* XXX "/" fixup. */
3335 if (skipDir(fpList[i].entry->dirName))
3338 xx = rpmdbGrowIterator(mi, i);
3342 if ((i = rpmdbGetIteratorCount(mi)) == 0) {
3343 mi = rpmdbFreeIterator(mi);
3346 fpc = fpCacheCreate(i);
3348 rpmdbSortIterator(mi);
3349 /* iterator is now sorted by (recnum, filenum) */
3351 /* For all installed headers with matching basename's ... */
3353 while ((h = rpmdbNextIterator(mi)) != NULL) {
3354 const char ** dirNames;
3355 const char ** baseNames;
3356 const char ** fullBaseNames;
3357 rpmTagType bnt, dnt;
3358 int_32 * dirIndexes;
3359 int_32 * fullDirIndexes;
3366 start = mi->mi_setx - 1;
3367 im = mi->mi_set->recs + start;
3369 /* Find the end of the set of matched basename's in this package. */
3371 for (end = start + 1; end < mi->mi_set->count; end++) {
3372 if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
3373 /*@innerbreak@*/ break;
3378 /* Compute fingerprints for this installed header's matches */
3379 xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &fullBaseNames, NULL);
3380 xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
3381 xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fullDirIndexes, NULL);
3383 baseNames = xcalloc(num, sizeof(*baseNames));
3384 dirIndexes = xcalloc(num, sizeof(*dirIndexes));
3386 for (i = 0; i < num; i++) {
3387 baseNames[i] = fullBaseNames[im[i].tagNum];
3388 dirIndexes[i] = fullDirIndexes[im[i].tagNum];
3392 fps = xcalloc(num, sizeof(*fps));
3393 fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
3395 /* Add db (recnum,filenum) to list for fingerprint matches. */
3397 for (i = 0; i < num; i++, im++) {
3398 /*@-nullpass@*/ /* FIX: fpList[].subDir may be NULL */
3399 if (!FP_EQUAL(fps[i], fpList[im->fpNum]))
3400 /*@innercontinue@*/ continue;
3402 xx = dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
3407 dirNames = hfd(dirNames, dnt);
3408 fullBaseNames = hfd(fullBaseNames, bnt);
3409 baseNames = _free(baseNames);
3410 dirIndexes = _free(dirIndexes);
3415 mi = rpmdbFreeIterator(mi);
3417 fpc = fpCacheFree(fpc);
3425 * Check if file esists using stat(2).
3426 * @param urlfn file name (may be URL)
3427 * @return 1 if file exists, 0 if not
3429 static int rpmioFileExists(const char * urlfn)
3430 /*@globals fileSystem, internalState @*/
3431 /*@modifies fileSystem, internalState @*/
3434 int urltype = urlPath(urlfn, &fn);
3438 if (*fn == '\0') fn = "/";
3441 case URL_IS_FTP: /* XXX WRONG WRONG WRONG */
3442 case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */
3444 case URL_IS_UNKNOWN:
3445 if (Stat(fn, &buf)) {
3456 /*@notreached@*/ break;
3462 static int rpmdbRemoveDatabase(const char * prefix,
3463 const char * dbpath, int _dbapi)
3464 /*@globals fileSystem, internalState @*/
3465 /*@modifies fileSystem, internalState @*/
3472 /*@-bounds -branchstate@*/
3473 if (dbpath[i - 1] != '/') {
3474 filename = alloca(i);
3475 strcpy(filename, dbpath);
3477 filename[i + 1] = '\0';
3480 /*@=bounds =branchstate@*/
3482 filename = alloca(strlen(prefix) + strlen(dbpath) + 40);
3486 if (dbiTags != NULL)
3487 for (i = 0; i < dbiTagsMax; i++) {
3489 const char * base = tagName(dbiTags[i]);
3491 sprintf(filename, "%s/%s/%s", prefix, dbpath, base);
3492 (void)rpmCleanPath(filename);
3493 if (!rpmioFileExists(filename))
3495 xx = unlink(filename);
3497 for (i = 0; i < 16; i++) {
3498 sprintf(filename, "%s/%s/__db.%03d", prefix, dbpath, i);
3499 (void)rpmCleanPath(filename);
3500 if (!rpmioFileExists(filename))
3502 xx = unlink(filename);
3511 sprintf(filename, "%s/%s", prefix, dbpath);
3512 (void)rpmCleanPath(filename);
3513 xx = rmdir(filename);
3518 static int rpmdbMoveDatabase(const char * prefix,
3519 const char * olddbpath, int _olddbapi,
3520 const char * newdbpath, int _newdbapi)
3521 /*@globals fileSystem, internalState @*/
3522 /*@modifies fileSystem, internalState @*/
3525 char * ofilename, * nfilename;
3526 struct stat * nst = alloca(sizeof(*nst));
3530 i = strlen(olddbpath);
3532 if (olddbpath[i - 1] != '/') {
3533 ofilename = alloca(i + 2);
3534 strcpy(ofilename, olddbpath);
3536 ofilename[i + 1] = '\0';
3537 olddbpath = ofilename;
3541 i = strlen(newdbpath);
3543 if (newdbpath[i - 1] != '/') {
3544 nfilename = alloca(i + 2);
3545 strcpy(nfilename, newdbpath);
3547 nfilename[i + 1] = '\0';
3548 newdbpath = nfilename;
3552 ofilename = alloca(strlen(prefix) + strlen(olddbpath) + 40);
3553 nfilename = alloca(strlen(prefix) + strlen(newdbpath) + 40);
3555 switch (_olddbapi) {
3557 if (dbiTags != NULL)
3558 for (i = 0; i < dbiTagsMax; i++) {
3562 /* Filter out temporary databases */
3563 switch ((rpmtag = dbiTags[i])) {
3564 case RPMDBI_AVAILABLE:
3566 case RPMDBI_REMOVED:
3567 case RPMDBI_DEPENDS:
3569 /*@notreached@*/ /*@switchbreak@*/ break;
3571 /*@switchbreak@*/ break;
3574 base = tagName(rpmtag);
3575 sprintf(ofilename, "%s/%s/%s", prefix, olddbpath, base);
3576 (void)rpmCleanPath(ofilename);
3577 if (!rpmioFileExists(ofilename))
3579 sprintf(nfilename, "%s/%s/%s", prefix, newdbpath, base);
3580 (void)rpmCleanPath(nfilename);
3583 * Get uid/gid/mode/mtime. If old doesn't exist, use new.
3584 * XXX Yes, the variable names are backwards.
3586 if (stat(nfilename, nst) < 0)
3587 if (stat(ofilename, nst) < 0)
3590 if ((xx = rename(ofilename, nfilename)) != 0) {
3594 xx = chown(nfilename, nst->st_uid, nst->st_gid);
3595 xx = chmod(nfilename, (nst->st_mode & 07777));
3596 { struct utimbuf stamp;
3597 stamp.actime = nst->st_atime;
3598 stamp.modtime = nst->st_mtime;
3599 xx = utime(nfilename, &stamp);
3602 for (i = 0; i < 16; i++) {
3603 sprintf(ofilename, "%s/%s/__db.%03d", prefix, olddbpath, i);
3604 (void)rpmCleanPath(ofilename);
3605 if (!rpmioFileExists(ofilename))
3607 xx = unlink(ofilename);
3608 sprintf(nfilename, "%s/%s/__db.%03d", prefix, newdbpath, i);
3609 (void)rpmCleanPath(nfilename);
3610 xx = unlink(nfilename);
3618 if (rc || _olddbapi == _newdbapi)
3621 rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi);
3624 /* Remove /etc/rpm/macros.db1 configuration file if db3 rebuilt. */
3625 if (rc == 0 && _newdbapi == 1 && _olddbapi == 3) {
3626 const char * mdb1 = "/etc/rpm/macros.db1";
3628 if (!stat(mdb1, &st) && S_ISREG(st.st_mode) && !unlink(mdb1))
3629 rpmMessage(RPMMESS_DEBUG,
3630 _("removing %s after successful db3 rebuild.\n"), mdb1);
3635 int rpmdbRebuild(const char * prefix, rpmts ts,
3636 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
3637 /*@globals _rebuildinprogress @*/
3638 /*@modifies _rebuildinprogress @*/
3641 const char * dbpath = NULL;
3642 const char * rootdbpath = NULL;
3644 const char * newdbpath = NULL;
3645 const char * newrootdbpath = NULL;
3655 if (prefix == NULL) prefix = "/";
3658 _dbapi = rpmExpandNumeric("%{_dbapi}");
3659 _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
3662 tfn = rpmGetPath("%{?_dbpath}", NULL);
3665 if (!(tfn && tfn[0] != '\0'))
3668 rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
3672 dbpath = rootdbpath = rpmGetPath(prefix, tfn, NULL);
3673 if (!(prefix[0] == '/' && prefix[1] == '\0'))
3674 dbpath += strlen(prefix);
3678 tfn = rpmGetPath("%{?_dbpath_rebuild}", NULL);
3681 if (!(tfn && tfn[0] != '\0' && strcmp(tfn, dbpath)))
3686 sprintf(pidbuf, "rebuilddb.%d", (int) getpid());
3687 t = xmalloc(strlen(dbpath) + strlen(pidbuf) + 1);
3689 (void)stpcpy(stpcpy(t, dbpath), pidbuf);
3695 newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL);
3696 if (!(prefix[0] == '/' && prefix[1] == '\0'))
3697 newdbpath += strlen(prefix);
3700 rpmMessage(RPMMESS_DEBUG, _("rebuilding database %s into %s\n"),
3701 rootdbpath, newrootdbpath);
3703 if (!access(newrootdbpath, F_OK)) {
3704 rpmError(RPMERR_MKDIR, _("temporary database %s already exists\n"),
3710 rpmMessage(RPMMESS_DEBUG, _("creating directory %s\n"), newrootdbpath);
3711 if (Mkdir(newrootdbpath, 0755)) {
3712 rpmError(RPMERR_MKDIR, _("creating directory %s: %s\n"),
3713 newrootdbpath, strerror(errno));
3719 rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"),
3721 _rebuildinprogress = 1;
3723 if (openDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644,
3724 RPMDB_FLAG_MINIMAL)) {
3729 _dbapi = olddb->db_api;
3730 _rebuildinprogress = 0;
3732 rpmMessage(RPMMESS_DEBUG, _("opening new database with dbapi %d\n"),
3734 (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1);
3736 if (openDatabase(prefix, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
3741 _dbapi_rebuild = newdb->db_api;
3744 rpmdbMatchIterator mi;
3745 #define _RECNUM rpmdbGetIteratorOffset(mi)
3747 /* RPMDBI_PACKAGES */
3748 mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
3750 (void) rpmdbSetHdrChk(mi, ts, hdrchk);
3752 while ((h = rpmdbNextIterator(mi)) != NULL) {
3754 /* let's sanity check this record a bit, otherwise just skip it */
3755 if (!(headerIsEntry(h, RPMTAG_NAME) &&
3756 headerIsEntry(h, RPMTAG_VERSION) &&
3757 headerIsEntry(h, RPMTAG_RELEASE) &&
3758 headerIsEntry(h, RPMTAG_BUILDTIME)))
3760 rpmError(RPMERR_INTERNAL,
3761 _("header #%u in the database is bad -- skipping.\n"),
3766 /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
3767 if (_db_filter_dups || newdb->db_filter_dups) {
3768 const char * name, * version, * release;
3771 (void) headerNVR(h, &name, &version, &release);
3774 { rpmdbMatchIterator mi;
3775 mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
3776 (void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
3777 RPMMIRE_DEFAULT, version);
3778 (void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
3779 RPMMIRE_DEFAULT, release);
3780 while (rpmdbNextIterator(mi)) {
3782 /*@innerbreak@*/ break;
3784 mi = rpmdbFreeIterator(mi);
3792 /* Deleted entries are eliminated in legacy headers by copy. */
3793 { Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
3794 ? headerCopy(h) : NULL);
3795 rc = rpmdbAdd(newdb, -1, (nh ? nh : h), ts, hdrchk);
3796 nh = headerFree(nh);
3800 rpmError(RPMERR_INTERNAL,
3801 _("cannot add record originally at %u\n"), _RECNUM);
3807 mi = rpmdbFreeIterator(mi);
3812 olddb->db_remove_env = 1;
3813 newdb->db_remove_env = 1;
3815 xx = rpmdbClose(olddb);
3816 xx = rpmdbClose(newdb);
3819 rpmMessage(RPMMESS_NORMAL, _("failed to rebuild database: original database "
3820 "remains in place\n"));
3822 xx = rpmdbRemoveDatabase(prefix, newdbpath, _dbapi_rebuild);
3825 } else if (!nocleanup) {
3826 if (rpmdbMoveDatabase(prefix, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
3827 rpmMessage(RPMMESS_ERROR, _("failed to replace old database with new "
3829 rpmMessage(RPMMESS_ERROR, _("replace files in %s with files from %s "
3830 "to recover"), dbpath, newdbpath);
3838 if (removedir && !(rc == 0 && nocleanup)) {
3839 rpmMessage(RPMMESS_DEBUG, _("removing directory %s\n"), newrootdbpath);
3840 if (Rmdir(newrootdbpath))
3841 rpmMessage(RPMMESS_ERROR, _("failed to remove directory %s: %s\n"),
3842 newrootdbpath, strerror(errno));
3844 newrootdbpath = _free(newrootdbpath);
3845 rootdbpath = _free(rootdbpath);