/** \ingroup rpmdb dbi * \file rpmdb/rpmdb.c */ #include "system.h" #define _USE_COPY_LOAD /* XXX don't use DB_DBT_MALLOC (yet) */ /*@unchecked@*/ int _rpmdb_debug = 0; #include #include #include #ifndef DYING /* XXX already in "system.h" */ /*@-noparams@*/ #include /*@=noparams@*/ #if defined(__LCLINT__) /*@-declundef -exportheader -redecl @*/ /* LCL: missing annotation */ extern int fnmatch (const char *pattern, const char *string, int flags) /*@*/; /*@=declundef =exportheader =redecl @*/ #endif #endif #include #if defined(__LCLINT__) /*@-declundef -exportheader @*/ /* LCL: missing modifies (only is bogus) */ extern void regfree (/*@only@*/ regex_t *preg) /*@modifies *preg @*/; /*@=declundef =exportheader @*/ #endif #include #include #include "rpmdb.h" #include "fprint.h" #include "legacy.h" #include "header_internal.h" /* XXX for HEADERFLAG_ALLOCATED */ #include "debug.h" /*@access dbiIndexSet@*/ /*@access dbiIndexItem@*/ /*@access rpmts@*/ /* XXX compared with NULL */ /*@access Header@*/ /* XXX compared with NULL */ /*@access rpmdbMatchIterator@*/ /*@access pgpDig@*/ /*@unchecked@*/ static int _rebuildinprogress = 0; /*@unchecked@*/ static int _db_filter_dups = 0; #define _DBI_FLAGS 0 #define _DBI_PERMS 0644 #define _DBI_MAJOR -1 /*@unchecked@*/ /*@globstate@*/ /*@null@*/ int * dbiTags = NULL; /*@unchecked@*/ int dbiTagsMax = 0; /* Bit mask macros. */ /*@-exporttype@*/ typedef unsigned int __pbm_bits; /*@=exporttype@*/ #define __PBM_NBITS (8 * sizeof (__pbm_bits)) #define __PBM_IX(d) ((d) / __PBM_NBITS) #define __PBM_MASK(d) ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS)) /*@-exporttype@*/ typedef struct { __pbm_bits bits[1]; } pbm_set; /*@=exporttype@*/ #define __PBM_BITS(set) ((set)->bits) #define PBM_FREE(s) _free(s); #define PBM_SET(d, s) (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d)) #define PBM_CLR(d, s) (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d)) #define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0) #define PBM_ALLOC(d) xcalloc(__PBM_IX (d) + 1, sizeof(__pbm_bits)) /** * Reallocate a bit map. * @retval sp address of bit map pointer * @retval odp no. of bits in map * @param nd desired no. of bits */ /*@unused@*/ static inline pbm_set * PBM_REALLOC(pbm_set ** sp, int * odp, int nd) /*@modifies *sp, *odp @*/ { int i, nb; /*@-bounds -sizeoftype@*/ if (nd > (*odp)) { nd *= 2; nb = __PBM_IX(nd) + 1; /*@-unqualifiedtrans@*/ *sp = xrealloc(*sp, nb * sizeof(__pbm_bits)); /*@=unqualifiedtrans@*/ for (i = __PBM_IX(*odp) + 1; i < nb; i++) __PBM_BITS(*sp)[i] = 0; *odp = nd; } /*@=bounds =sizeoftype@*/ /*@-compdef -retalias -usereleased@*/ return *sp; /*@=compdef =retalias =usereleased@*/ } /** * Convert hex to binary nibble. * @param c hex character * @return binary nibble */ static inline unsigned char nibble(char c) /*@*/ { if (c >= '0' && c <= '9') return (c - '0'); if (c >= 'A' && c <= 'F') return (c - 'A') + 10; if (c >= 'a' && c <= 'f') return (c - 'a') + 10; return 0; } #ifdef DYING /** * Check key for printable characters. * @param ptr key value pointer * @param len key value length * @return 1 if only ASCII, 0 otherwise. */ static int printable(const void * ptr, size_t len) /*@*/ { const char * s = ptr; int i; for (i = 0; i < len; i++, s++) if (!(*s >= ' ' && *s <= '~')) return 0; return 1; } #endif /** * Return dbi index used for rpm tag. * @param rpmtag rpm header tag * @return dbi index, -1 on error */ static int dbiTagToDbix(int rpmtag) /*@*/ { int dbix; if (dbiTags != NULL) for (dbix = 0; dbix < dbiTagsMax; dbix++) { /*@-boundsread@*/ if (rpmtag == dbiTags[dbix]) return dbix; /*@=boundsread@*/ } return -1; } /** * Initialize database (index, tag) tuple from configuration. */ static void dbiTagsInit(void) /*@globals rpmGlobalMacroContext, dbiTags, dbiTagsMax @*/ /*@modifies rpmGlobalMacroContext, dbiTags, dbiTagsMax @*/ { /*@observer@*/ static const char * const _dbiTagStr_default = "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername:Dirnames:Requireversion:Provideversion:Installtid:Sigmd5:Sha1header:Filemd5s:Depends:Pubkeys"; char * dbiTagStr = NULL; char * o, * oe; int rpmtag; dbiTagStr = rpmExpand("%{?_dbi_tags}", NULL); if (!(dbiTagStr && *dbiTagStr)) { dbiTagStr = _free(dbiTagStr); dbiTagStr = xstrdup(_dbiTagStr_default); } /* Discard previous values. */ dbiTags = _free(dbiTags); dbiTagsMax = 0; /* Always allocate package index */ dbiTags = xcalloc(1, sizeof(*dbiTags)); dbiTags[dbiTagsMax++] = RPMDBI_PACKAGES; for (o = dbiTagStr; o && *o; o = oe) { while (*o && xisspace(*o)) o++; if (*o == '\0') break; for (oe = o; oe && *oe; oe++) { if (xisspace(*oe)) /*@innerbreak@*/ break; if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/')) /*@innerbreak@*/ break; } if (oe && *oe) *oe++ = '\0'; rpmtag = tagValue(o); if (rpmtag < 0) { rpmMessage(RPMMESS_WARNING, _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o); continue; } if (dbiTagToDbix(rpmtag) >= 0) continue; dbiTags = xrealloc(dbiTags, (dbiTagsMax + 1) * sizeof(*dbiTags)); /* XXX memory leak */ dbiTags[dbiTagsMax++] = rpmtag; } dbiTagStr = _free(dbiTagStr); } /*@-redecl@*/ #define DB1vec NULL #define DB2vec NULL /*@-exportheadervar -declundef @*/ /*@unchecked@*/ extern struct _dbiVec db3vec; /*@=exportheadervar =declundef @*/ #define DB3vec &db3vec /*@=redecl@*/ /*@-nullassign@*/ /*@observer@*/ /*@unchecked@*/ static struct _dbiVec *mydbvecs[] = { DB1vec, DB1vec, DB2vec, DB3vec, NULL }; /*@=nullassign@*/ dbiIndex dbiOpen(rpmdb db, rpmTag rpmtag, /*@unused@*/ unsigned int flags) { int dbix; dbiIndex dbi = NULL; int _dbapi, _dbapi_rebuild, _dbapi_wanted; int rc = 0; if (db == NULL) return NULL; dbix = dbiTagToDbix(rpmtag); if (dbix < 0 || dbix >= dbiTagsMax) return NULL; /* Is this index already open ? */ /*@-compdef@*/ /* FIX: db->_dbi may be NULL */ if ((dbi = db->_dbi[dbix]) != NULL) return dbi; /*@=compdef@*/ _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}"); if (_dbapi_rebuild < 1 || _dbapi_rebuild > 3) _dbapi_rebuild = 3; _dbapi_wanted = (_rebuildinprogress ? -1 : db->db_api); switch (_dbapi_wanted) { default: _dbapi = _dbapi_wanted; if (_dbapi < 0 || _dbapi >= 4 || mydbvecs[_dbapi] == NULL) { return NULL; } errno = 0; dbi = NULL; rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi); if (rc) { static int _printed[32]; if (!_printed[dbix & 0x1f]++) rpmError(RPMERR_DBOPEN, _("cannot open %s index using db%d - %s (%d)\n"), tagName(rpmtag), _dbapi, (rc > 0 ? strerror(rc) : ""), rc); _dbapi = -1; } break; case -1: _dbapi = 4; while (_dbapi-- > 1) { if (mydbvecs[_dbapi] == NULL) continue; errno = 0; dbi = NULL; rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi); if (rc == 0 && dbi) /*@loopbreak@*/ break; } if (_dbapi <= 0) { static int _printed[32]; if (!_printed[dbix & 0x1f]++) rpmError(RPMERR_DBOPEN, _("cannot open %s index\n"), tagName(rpmtag)); rc = 1; goto exit; } if (db->db_api == -1 && _dbapi > 0) db->db_api = _dbapi; break; } /* Require conversion. */ if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) { rc = (_rebuildinprogress ? 0 : 1); goto exit; } /* Suggest possible configuration */ if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) { rc = 1; goto exit; } /* Suggest possible configuration */ if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) { rc = (_rebuildinprogress ? 0 : 1); goto exit; } exit: if (dbi != NULL && rc == 0) { db->_dbi[dbix] = dbi; /*@-sizeoftype@*/ if (rpmtag == RPMDBI_PACKAGES && db->db_bits == NULL) { db->db_nbits = 1024; if (!dbiStat(dbi, DB_FAST_STAT)) { DB_HASH_STAT * hash = (DB_HASH_STAT *)dbi->dbi_stats; if (hash) db->db_nbits += hash->hash_nkeys; } db->db_bits = PBM_ALLOC(db->db_nbits); } /*@=sizeoftype@*/ } else dbi = db3Free(dbi); /*@-compdef -nullstate@*/ /* FIX: db->_dbi may be NULL */ return dbi; /*@=compdef =nullstate@*/ } /** * Create and initialize item for index database set. * @param hdrNum header instance in db * @param tagNum tag index in header * @return new item */ static dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum) /*@*/ { dbiIndexItem rec = xcalloc(1, sizeof(*rec)); rec->hdrNum = hdrNum; rec->tagNum = tagNum; return rec; } union _dbswap { unsigned int ui; unsigned char uc[4]; }; #define _DBSWAP(_a) \ { unsigned char _b, *_c = (_a).uc; \ _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \ _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \ } /** * Convert retrieved data to index set. * @param dbi index database handle * @param data retrieved data * @retval setp (malloc'ed) index set * @return 0 on success */ static int dbt2set(dbiIndex dbi, DBT * data, /*@out@*/ dbiIndexSet * setp) /*@modifies dbi, *setp @*/ { int _dbbyteswapped = dbiByteSwapped(dbi); const char * sdbir; dbiIndexSet set; int i; if (dbi == NULL || data == NULL || setp == NULL) return -1; if ((sdbir = data->data) == NULL) { *setp = NULL; return 0; } set = xmalloc(sizeof(*set)); set->count = data->size / dbi->dbi_jlen; set->recs = xmalloc(set->count * sizeof(*(set->recs))); /*@-bounds -sizeoftype @*/ switch (dbi->dbi_jlen) { default: case 2*sizeof(int_32): for (i = 0; i < set->count; i++) { union _dbswap hdrNum, tagNum; memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui)); sdbir += sizeof(hdrNum.ui); memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui)); sdbir += sizeof(tagNum.ui); if (_dbbyteswapped) { _DBSWAP(hdrNum); _DBSWAP(tagNum); } set->recs[i].hdrNum = hdrNum.ui; set->recs[i].tagNum = tagNum.ui; set->recs[i].fpNum = 0; } break; case 1*sizeof(int_32): for (i = 0; i < set->count; i++) { union _dbswap hdrNum; memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui)); sdbir += sizeof(hdrNum.ui); if (_dbbyteswapped) { _DBSWAP(hdrNum); } set->recs[i].hdrNum = hdrNum.ui; set->recs[i].tagNum = 0; set->recs[i].fpNum = 0; } break; } *setp = set; /*@=bounds =sizeoftype @*/ /*@-compdef@*/ return 0; /*@=compdef@*/ } /** * Convert index set to database representation. * @param dbi index database handle * @param data retrieved data * @param set index set * @return 0 on success */ static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set) /*@modifies dbi, *data @*/ { int _dbbyteswapped = dbiByteSwapped(dbi); char * tdbir; int i; if (dbi == NULL || data == NULL || set == NULL) return -1; data->size = set->count * (dbi->dbi_jlen); if (data->size == 0) { data->data = NULL; return 0; } tdbir = data->data = xmalloc(data->size); /*@-bounds -sizeoftype@*/ switch (dbi->dbi_jlen) { default: case 2*sizeof(int_32): for (i = 0; i < set->count; i++) { union _dbswap hdrNum, tagNum; memset(&hdrNum, 0, sizeof(hdrNum)); memset(&tagNum, 0, sizeof(tagNum)); hdrNum.ui = set->recs[i].hdrNum; tagNum.ui = set->recs[i].tagNum; if (_dbbyteswapped) { _DBSWAP(hdrNum); _DBSWAP(tagNum); } memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui)); tdbir += sizeof(hdrNum.ui); memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui)); tdbir += sizeof(tagNum.ui); } break; case 1*sizeof(int_32): for (i = 0; i < set->count; i++) { union _dbswap hdrNum; memset(&hdrNum, 0, sizeof(hdrNum)); hdrNum.ui = set->recs[i].hdrNum; if (_dbbyteswapped) { _DBSWAP(hdrNum); } memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui)); tdbir += sizeof(hdrNum.ui); } break; } /*@=bounds =sizeoftype@*/ /*@-compdef@*/ return 0; /*@=compdef@*/ } /* XXX assumes hdrNum is first int in dbiIndexItem */ static int hdrNumCmp(const void * one, const void * two) /*@*/ { const int * a = one, * b = two; return (*a - *b); } /** * Append element(s) to set of index database items. * @param set set of index database items * @param recs array of items to append to set * @param nrecs number of items * @param recsize size of an array item * @param sortset should resulting set be sorted? * @return 0 success, 1 failure (bad args) */ static int dbiAppendSet(dbiIndexSet set, const void * recs, int nrecs, size_t recsize, int sortset) /*@modifies *set @*/ { const char * rptr = recs; size_t rlen = (recsize < sizeof(*(set->recs))) ? recsize : sizeof(*(set->recs)); if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0) return 1; set->recs = xrealloc(set->recs, (set->count + nrecs) * sizeof(*(set->recs))); memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs))); while (nrecs-- > 0) { /*@-mayaliasunique@*/ memcpy(set->recs + set->count, rptr, rlen); /*@=mayaliasunique@*/ rptr += recsize; set->count++; } if (sortset && set->count > 1) qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp); return 0; } /** * Remove element(s) from set of index database items. * @param set set of index database items * @param recs array of items to remove from set * @param nrecs number of items * @param recsize size of an array item * @param sorted array is already sorted? * @return 0 success, 1 failure (no items found) */ static int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs, size_t recsize, int sorted) /*@modifies set, recs @*/ { int from; int to = 0; int num = set->count; int numCopied = 0; assert(set->count > 0); if (nrecs > 1 && !sorted) qsort(recs, nrecs, recsize, hdrNumCmp); for (from = 0; from < num; from++) { if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) { set->count--; continue; } if (from != to) set->recs[to] = set->recs[from]; /* structure assignment */ to++; numCopied++; } return (numCopied == num); } /* XXX transaction.c */ unsigned int dbiIndexSetCount(dbiIndexSet set) { return set->count; } /* XXX transaction.c */ unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) { return set->recs[recno].hdrNum; } /* XXX transaction.c */ unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) { return set->recs[recno].tagNum; } /* XXX transaction.c */ dbiIndexSet dbiFreeIndexSet(dbiIndexSet set) { if (set) { set->recs = _free(set->recs); set = _free(set); } return set; } /** */ /*@unchecked@*/ static sigset_t caught; /* forward ref */ static void handler(int signum); /** */ /*@unchecked@*/ /*@-fullinitblock@*/ static struct sigtbl_s { int signum; int active; struct sigaction act; struct sigaction oact; } satbl[] = { { SIGHUP, 0, { {handler} } }, { SIGINT, 0, { {handler} } }, { SIGTERM, 0, { {handler} } }, { SIGQUIT, 0, { {handler} } }, { -1, 0, { {NULL} } }, }; /*@=fullinitblock@*/ /** */ /*@-incondefs@*/ static void handler(int signum) /*@globals caught, satbl @*/ /*@modifies caught @*/ { struct sigtbl_s * tbl; for(tbl = satbl; tbl->signum >= 0; tbl++) { if (tbl->signum != signum) continue; if (!tbl->active) continue; (void) sigaddset(&caught, signum); break; } } /*@=incondefs@*/ /** * Enable all signal handlers. */ static int enableSignals(void) /*@globals caught, satbl, fileSystem @*/ /*@modifies caught, satbl, fileSystem @*/ { struct sigtbl_s * tbl; sigset_t newMask, oldMask; int rc; (void) sigfillset(&newMask); /* block all signals */ (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask); rc = 0; for(tbl = satbl; tbl->signum >= 0; tbl++) { if (tbl->active++ > 0) continue; (void) sigdelset(&caught, tbl->signum); rc = sigaction(tbl->signum, &tbl->act, &tbl->oact); if (rc) break; } return sigprocmask(SIG_SETMASK, &oldMask, NULL); } /*@unchecked@*/ static rpmdb dbrock; /** * Check for signals. */ /*@mayexit@*/ static int checkSignals(void) /*@globals dbrock, satbl, fileSystem @*/ /*@modifies dbrock, fileSystem @*/ { struct sigtbl_s * tbl; sigset_t newMask, oldMask; int terminate = 0; (void) sigfillset(&newMask); /* block all signals */ (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask); for(tbl = satbl; tbl->signum >= 0; tbl++) { if (tbl->active == 0) continue; if (sigismember(&caught, tbl->signum)) terminate = 1; } if (terminate) { rpmdb db; rpmMessage(RPMMESS_WARNING, "Exiting on signal ...\n"); /*@-newreftrans@*/ while ((db = dbrock) != NULL) { /*@i@*/ dbrock = db->db_next; db->db_next = NULL; (void) rpmdbClose(db); } /*@=newreftrans@*/ exit(EXIT_FAILURE); } return sigprocmask(SIG_SETMASK, &oldMask, NULL); } /** * Disable all signal handlers. */ static int disableSignals(void) /*@globals satbl, fileSystem @*/ /*@modifies satbl, fileSystem @*/ { struct sigtbl_s * tbl; sigset_t newMask, oldMask; int rc; (void) sigfillset(&newMask); /* block all signals */ (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask); rc = 0; for(tbl = satbl; tbl->signum >= 0; tbl++) { if (--tbl->active > 0) continue; rc = sigaction(tbl->signum, &tbl->oact, NULL); if (rc) break; } return sigprocmask(SIG_SETMASK, &oldMask, NULL); } /** * Block all signals, returning previous signal mask. */ static int blockSignals(/*@unused@*/ rpmdb db, /*@out@*/ sigset_t * oldMask) /*@globals satbl, fileSystem @*/ /*@modifies *oldMask, satbl, fileSystem @*/ { struct sigtbl_s * tbl; sigset_t newMask; (void) sigfillset(&newMask); /* block all signals */ (void) sigprocmask(SIG_BLOCK, &newMask, oldMask); for(tbl = satbl; tbl->signum >= 0; tbl++) { if (tbl->active == 0) continue; (void) sigdelset(&newMask, tbl->signum); } return sigprocmask(SIG_BLOCK, &newMask, NULL); } /** * Restore signal mask. */ /*@mayexit@*/ static int unblockSignals(/*@unused@*/ rpmdb db, sigset_t * oldMask) /*@globals dbrock, fileSystem @*/ /*@modifies dbrock, fileSystem @*/ { (void) checkSignals(); return sigprocmask(SIG_SETMASK, oldMask, NULL); } #define _DB_ROOT "/" #define _DB_HOME "%{_dbpath}" #define _DB_FLAGS 0 #define _DB_MODE 0 #define _DB_PERMS 0644 #define _DB_MAJOR -1 #define _DB_ERRPFX "rpmdb" /*@-fullinitblock@*/ /*@observer@*/ /*@unchecked@*/ static struct rpmdb_s dbTemplate = { _DB_ROOT, _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS, _DB_MAJOR, _DB_ERRPFX }; /*@=fullinitblock@*/ int rpmdbOpenAll(rpmdb db) { int dbix; int rc = 0; if (db == NULL) return -2; if (dbiTags != NULL) for (dbix = 0; dbix < dbiTagsMax; dbix++) { if (db->_dbi[dbix] != NULL) continue; (void) dbiOpen(db, dbiTags[dbix], db->db_flags); } return rc; } int rpmdbCloseDBI(rpmdb db, int rpmtag) { int dbix; int rc = 0; if (db == NULL || db->_dbi == NULL || dbiTags == NULL) return 0; for (dbix = 0; dbix < dbiTagsMax; dbix++) { if (dbiTags[dbix] != rpmtag) continue; /*@-boundswrite@*/ if (db->_dbi[dbix] != NULL) { int xx; /*@-unqualifiedtrans@*/ /* FIX: double indirection. */ xx = dbiClose(db->_dbi[dbix], 0); if (xx && rc == 0) rc = xx; db->_dbi[dbix] = NULL; /*@=unqualifiedtrans@*/ } /*@=boundswrite@*/ break; } return rc; } /* XXX query.c, rpminstall.c, verify.c */ /*@-incondefs@*/ int rpmdbClose(rpmdb db) /*@globals dbrock @*/ /*@modifies dbrock @*/ { rpmdb * prev, next; int dbix; int rc = 0; if (db == NULL) goto exit; (void) rpmdbUnlink(db, "rpmdbClose"); /*@-usereleased@*/ if (db->nrefs > 0) goto exit; if (db->_dbi) for (dbix = db->db_ndbi; --dbix >= 0; ) { int xx; if (db->_dbi[dbix] == NULL) continue; /*@-unqualifiedtrans@*/ /* FIX: double indirection. */ xx = dbiClose(db->_dbi[dbix], 0); if (xx && rc == 0) rc = xx; db->_dbi[dbix] = NULL; /*@=unqualifiedtrans@*/ } db->db_errpfx = _free(db->db_errpfx); db->db_root = _free(db->db_root); db->db_home = _free(db->db_home); db->db_bits = PBM_FREE(db->db_bits); db->_dbi = _free(db->_dbi); /*@-newreftrans@*/ prev = &dbrock; while ((next = *prev) != NULL && next != db) prev = &next->db_next; if (next) { /*@i@*/ *prev = next->db_next; next->db_next = NULL; } /*@=newreftrans@*/ /*@-refcounttrans@*/ db = _free(db); /*@=refcounttrans@*/ /*@=usereleased@*/ exit: (void) disableSignals(); return rc; } /*@=incondefs@*/ int rpmdbSync(rpmdb db) { int dbix; int rc = 0; if (db == NULL) return 0; for (dbix = 0; dbix < db->db_ndbi; dbix++) { int xx; if (db->_dbi[dbix] == NULL) continue; xx = dbiSync(db->_dbi[dbix], 0); if (xx && rc == 0) rc = xx; } return rc; } /*@-mods@*/ /* FIX: dbTemplate structure assignment */ static /*@only@*/ /*@null@*/ rpmdb newRpmdb(/*@kept@*/ /*@null@*/ const char * root, /*@kept@*/ /*@null@*/ const char * home, int mode, int perms, int flags) /*@globals _db_filter_dups, rpmGlobalMacroContext @*/ /*@modifies _db_filter_dups, rpmGlobalMacroContext @*/ { rpmdb db = xcalloc(sizeof(*db), 1); const char * epfx = _DB_ERRPFX; static int _initialized = 0; if (!_initialized) { _db_filter_dups = rpmExpandNumeric("%{_filterdbdups}"); _initialized = 1; } /*@-boundswrite@*/ /*@-assignexpose@*/ *db = dbTemplate; /* structure assignment */ /*@=assignexpose@*/ /*@=boundswrite@*/ db->_dbi = NULL; if (!(perms & 0600)) perms = 0644; /* XXX sanity */ if (mode >= 0) db->db_mode = mode; if (perms >= 0) db->db_perms = perms; if (flags >= 0) db->db_flags = flags; /*@-nullpass@*/ db->db_root = rpmGetPath( (root && *root ? root : _DB_ROOT), NULL); db->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL); /*@=nullpass@*/ if (!(db->db_home && db->db_home[0] != '%')) { rpmError(RPMERR_DBOPEN, _("no dbpath has been set\n")); db->db_root = _free(db->db_root); db->db_home = _free(db->db_home); db = _free(db); /*@-globstate@*/ return NULL; /*@=globstate@*/ } db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL); db->db_remove_env = 0; db->db_filter_dups = _db_filter_dups; db->db_ndbi = dbiTagsMax; db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi)); db->nrefs = 0; /*@-globstate@*/ return rpmdbLink(db, "rpmdbCreate"); /*@=globstate@*/ } /*@=mods@*/ static int openDatabase(/*@null@*/ const char * prefix, /*@null@*/ const char * dbpath, int _dbapi, /*@null@*/ /*@out@*/ rpmdb *dbp, int mode, int perms, int flags) /*@globals dbrock, rpmGlobalMacroContext, fileSystem, internalState @*/ /*@modifies dbrock, *dbp, rpmGlobalMacroContext, fileSystem, internalState @*/ /*@requires maxSet(dbp) >= 0 @*/ { rpmdb db; int rc, xx; static int _tags_initialized = 0; int justCheck = flags & RPMDB_FLAG_JUSTCHECK; int minimal = flags & RPMDB_FLAG_MINIMAL; if (!_tags_initialized || dbiTagsMax == 0) { dbiTagsInit(); _tags_initialized++; } /* Insure that _dbapi has one of -1, 1, 2, or 3 */ if (_dbapi < -1 || _dbapi > 3) _dbapi = -1; if (_dbapi == 0) _dbapi = 1; if (dbp) *dbp = NULL; if (mode & O_WRONLY) return 1; db = newRpmdb(prefix, dbpath, mode, perms, flags); if (db == NULL) return 1; (void) enableSignals(); db->db_api = _dbapi; { int dbix; rc = 0; if (dbiTags != NULL) for (dbix = 0; rc == 0 && dbix < dbiTagsMax; dbix++) { dbiIndex dbi; int rpmtag; /* Filter out temporary databases */ switch ((rpmtag = dbiTags[dbix])) { case RPMDBI_AVAILABLE: case RPMDBI_ADDED: case RPMDBI_REMOVED: case RPMDBI_DEPENDS: continue; /*@notreached@*/ /*@switchbreak@*/ break; default: /*@switchbreak@*/ break; } dbi = dbiOpen(db, rpmtag, 0); if (dbi == NULL) { rc = -2; break; } switch (rpmtag) { case RPMDBI_PACKAGES: if (dbi == NULL) rc |= 1; #if 0 /* XXX open only Packages, indices created on the fly. */ if (db->db_api == 3) #endif goto exit; /*@notreached@*/ /*@switchbreak@*/ break; case RPMTAG_NAME: if (dbi == NULL) rc |= 1; if (minimal) goto exit; /*@switchbreak@*/ break; default: /*@switchbreak@*/ break; } } } exit: if (rc || justCheck || dbp == NULL) xx = rpmdbClose(db); else { /*@-assignexpose -newreftrans@*/ /*@i@*/ db->db_next = dbrock; dbrock = db; /*@i@*/ *dbp = db; /*@=assignexpose =newreftrans@*/ } return rc; } rpmdb XrpmdbUnlink(rpmdb db, const char * msg, const char * fn, unsigned ln) { /*@-modfilesys@*/ if (_rpmdb_debug) fprintf(stderr, "--> db %p -- %d %s at %s:%u\n", db, db->nrefs, msg, fn, ln); /*@=modfilesys@*/ db->nrefs--; return NULL; } rpmdb XrpmdbLink(rpmdb db, const char * msg, const char * fn, unsigned ln) { db->nrefs++; /*@-modfilesys@*/ if (_rpmdb_debug) fprintf(stderr, "--> db %p ++ %d %s at %s:%u\n", db, db->nrefs, msg, fn, ln); /*@=modfilesys@*/ /*@-refcounttrans@*/ return db; /*@=refcounttrans@*/ } /* XXX python/rpmmodule.c */ int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms) { int _dbapi = rpmExpandNumeric("%{_dbapi}"); /*@-boundswrite@*/ return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0); /*@=boundswrite@*/ } int rpmdbInit (const char * prefix, int perms) { rpmdb db = NULL; int _dbapi = rpmExpandNumeric("%{_dbapi}"); int rc; /*@-boundswrite@*/ rc = openDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR), perms, RPMDB_FLAG_JUSTCHECK); /*@=boundswrite@*/ if (db != NULL) { int xx; xx = rpmdbOpenAll(db); if (xx && rc == 0) rc = xx; xx = rpmdbClose(db); if (xx && rc == 0) rc = xx; db = NULL; } return rc; } int rpmdbVerify(const char * prefix) { rpmdb db = NULL; int _dbapi = rpmExpandNumeric("%{_dbapi}"); int rc = 0; /*@-boundswrite@*/ rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0); /*@=boundswrite@*/ if (db != NULL) { int dbix; int xx; rc = rpmdbOpenAll(db); for (dbix = db->db_ndbi; --dbix >= 0; ) { if (db->_dbi[dbix] == NULL) continue; /*@-unqualifiedtrans@*/ /* FIX: double indirection. */ xx = dbiVerify(db->_dbi[dbix], 0); if (xx && rc == 0) rc = xx; db->_dbi[dbix] = NULL; /*@=unqualifiedtrans@*/ } /*@-nullstate@*/ /* FIX: db->_dbi[] may be NULL. */ xx = rpmdbClose(db); /*@=nullstate@*/ if (xx && rc == 0) rc = xx; db = NULL; } return rc; } /** * Find file matches in database. * @param db rpm database * @param filespec * @param key * @param data * @param matches * @return 0 on success, 1 on not found, -2 on error */ static int rpmdbFindByFile(rpmdb db, /*@null@*/ const char * filespec, DBT * key, DBT * data, /*@out@*/ dbiIndexSet * matches) /*@globals rpmGlobalMacroContext, fileSystem @*/ /*@modifies db, *key, *data, *matches, rpmGlobalMacroContext, fileSystem @*/ /*@requires maxSet(matches) >= 0 @*/ { HGE_t hge = (HGE_t)headerGetEntryMinMemory; HFD_t hfd = headerFreeData; const char * dirName; const char * baseName; rpmTagType bnt, dnt; fingerPrintCache fpc; fingerPrint fp1; dbiIndex dbi = NULL; DBC * dbcursor; dbiIndexSet allMatches = NULL; dbiIndexItem rec = NULL; int i; int rc; int xx; *matches = NULL; if (filespec == NULL) return -2; /*@-branchstate@*/ if ((baseName = strrchr(filespec, '/')) != NULL) { char * t; size_t len; len = baseName - filespec + 1; /*@-boundswrite@*/ t = strncpy(alloca(len + 1), filespec, len); t[len] = '\0'; /*@=boundswrite@*/ dirName = t; baseName++; } else { dirName = ""; baseName = filespec; } /*@=branchstate@*/ if (baseName == NULL) return -2; fpc = fpCacheCreate(20); fp1 = fpLookup(fpc, dirName, baseName, 1); dbi = dbiOpen(db, RPMTAG_BASENAMES, 0); /*@-branchstate@*/ if (dbi != NULL) { dbcursor = NULL; xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0); /*@-temptrans@*/ key->data = (void *) baseName; /*@=temptrans@*/ key->size = strlen(baseName); if (key->size == 0) key->size++; /* XXX "/" fixup. */ rc = dbiGet(dbi, dbcursor, key, data, DB_SET); if (rc > 0) { rpmError(RPMERR_DBGETINDEX, _("error(%d) getting \"%s\" records from %s index\n"), rc, key->data, tagName(dbi->dbi_rpmtag)); } if (rc == 0) (void) dbt2set(dbi, data, &allMatches); xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; } else rc = -2; /*@=branchstate@*/ if (rc) { allMatches = dbiFreeIndexSet(allMatches); fpc = fpCacheFree(fpc); return rc; } *matches = xcalloc(1, sizeof(**matches)); rec = dbiIndexNewItem(0, 0); i = 0; if (allMatches != NULL) while (i < allMatches->count) { const char ** baseNames, ** dirNames; int_32 * dirIndexes; unsigned int offset = dbiIndexRecordOffset(allMatches, i); unsigned int prevoff; Header h; { rpmdbMatchIterator mi; mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset)); h = rpmdbNextIterator(mi); if (h) h = headerLink(h); mi = rpmdbFreeIterator(mi); } if (h == NULL) { i++; continue; } xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, NULL); xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL); xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL); do { fingerPrint fp2; int num = dbiIndexRecordFileNumber(allMatches, i); fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1); /*@-nullpass@*/ if (FP_EQUAL(fp1, fp2)) { /*@=nullpass@*/ rec->hdrNum = dbiIndexRecordOffset(allMatches, i); rec->tagNum = dbiIndexRecordFileNumber(allMatches, i); xx = dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0); } prevoff = offset; i++; offset = dbiIndexRecordOffset(allMatches, i); } while (i < allMatches->count && (i == 0 || offset == prevoff)); baseNames = hfd(baseNames, bnt); dirNames = hfd(dirNames, dnt); h = headerFree(h); } rec = _free(rec); allMatches = dbiFreeIndexSet(allMatches); fpc = fpCacheFree(fpc); if ((*matches)->count == 0) { *matches = dbiFreeIndexSet(*matches); return 1; } return 0; } /* XXX python/upgrade.c, install.c, uninstall.c */ int rpmdbCountPackages(rpmdb db, const char * name) { DBC * dbcursor = NULL; DBT * key = alloca(sizeof(*key)); DBT * data = alloca(sizeof(*data)); dbiIndex dbi; int rc; int xx; if (db == NULL) return 0; memset(key, 0, sizeof(*key)); memset(data, 0, sizeof(*data)); dbi = dbiOpen(db, RPMTAG_NAME, 0); if (dbi == NULL) return 0; /*@-temptrans@*/ key->data = (void *) name; /*@=temptrans@*/ key->size = strlen(name); xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0); rc = dbiGet(dbi, dbcursor, key, data, DB_SET); xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; if (rc == 0) { /* success */ dbiIndexSet matches; /*@-nullpass@*/ /* FIX: matches might be NULL */ matches = NULL; (void) dbt2set(dbi, data, &matches); if (matches) { rc = dbiIndexSetCount(matches); matches = dbiFreeIndexSet(matches); } /*@=nullpass@*/ } else if (rc == DB_NOTFOUND) { /* not found */ rc = 0; } else { /* error */ rpmError(RPMERR_DBGETINDEX, _("error(%d) getting \"%s\" records from %s index\n"), rc, key->data, tagName(dbi->dbi_rpmtag)); rc = -1; } return rc; } /** * Attempt partial matches on name[-version[-release]] strings. * @param dbi index database handle (always RPMTAG_NAME) * @param dbcursor index database cursor * @param key search key/length/flags * @param data search data/length/flags * @param name package name * @param version package version (can be a pattern) * @param release package release (can be a pattern) * @retval matches set of header instances that match * @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL */ static rpmRC dbiFindMatches(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data, const char * name, /*@null@*/ const char * version, /*@null@*/ const char * release, /*@out@*/ dbiIndexSet * matches) /*@globals rpmGlobalMacroContext, fileSystem @*/ /*@modifies dbi, *dbcursor, *key, *data, *matches, rpmGlobalMacroContext, fileSystem @*/ /*@requires maxSet(matches) >= 0 @*/ { int gotMatches = 0; int rc; int i; /*@-temptrans@*/ key->data = (void *) name; /*@=temptrans@*/ key->size = strlen(name); rc = dbiGet(dbi, dbcursor, key, data, DB_SET); if (rc == 0) { /* success */ (void) dbt2set(dbi, data, matches); if (version == NULL && release == NULL) return RPMRC_OK; } else if (rc == DB_NOTFOUND) { /* not found */ return RPMRC_NOTFOUND; } else { /* error */ rpmError(RPMERR_DBGETINDEX, _("error(%d) getting \"%s\" records from %s index\n"), rc, key->data, tagName(dbi->dbi_rpmtag)); return RPMRC_FAIL; } /* Make sure the version and release match. */ /*@-branchstate@*/ for (i = 0; i < dbiIndexSetCount(*matches); i++) { unsigned int recoff = dbiIndexRecordOffset(*matches, i); rpmdbMatchIterator mi; Header h; if (recoff == 0) continue; mi = rpmdbInitIterator(dbi->dbi_rpmdb, RPMDBI_PACKAGES, &recoff, sizeof(recoff)); /* Set iterator selectors for version/release if available. */ if (version && rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version)) { rc = RPMRC_FAIL; goto exit; } if (release && rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release)) { rc = RPMRC_FAIL; goto exit; } h = rpmdbNextIterator(mi); /*@-boundswrite@*/ if (h) (*matches)->recs[gotMatches++] = (*matches)->recs[i]; else (*matches)->recs[i].hdrNum = 0; /*@=boundswrite@*/ mi = rpmdbFreeIterator(mi); } /*@=branchstate@*/ if (gotMatches) { (*matches)->count = gotMatches; rc = RPMRC_OK; } else rc = RPMRC_NOTFOUND; exit: /*@-unqualifiedtrans@*/ /* FIX: double indirection */ if (rc && matches && *matches) *matches = dbiFreeIndexSet(*matches); /*@=unqualifiedtrans@*/ return rc; } /** * Lookup by name, name-version, and finally by name-version-release. * Both version and release can be patterns. * @todo Name must be an exact match, as name is a db key. * @param dbi index database handle (always RPMTAG_NAME) * @param dbcursor index database cursor * @param key search key/length/flags * @param data search data/length/flags * @param arg name[-version[-release]] string * @retval matches set of header instances that match * @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL */ static rpmRC dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data, /*@null@*/ const char * arg, /*@out@*/ dbiIndexSet * matches) /*@globals rpmGlobalMacroContext, fileSystem @*/ /*@modifies dbi, *dbcursor, *key, *data, *matches, rpmGlobalMacroContext, fileSystem @*/ /*@requires maxSet(matches) >= 0 @*/ { const char * release; char * localarg; char * s; char c; int brackets; rpmRC rc; if (arg == NULL || strlen(arg) == 0) return RPMRC_NOTFOUND; /* did they give us just a name? */ rc = dbiFindMatches(dbi, dbcursor, key, data, arg, NULL, NULL, matches); if (rc != RPMRC_NOTFOUND) return rc; /*@-unqualifiedtrans@*/ /* FIX: double indirection */ *matches = dbiFreeIndexSet(*matches); /*@=unqualifiedtrans@*/ /* maybe a name and a release */ localarg = alloca(strlen(arg) + 1); s = stpcpy(localarg, arg); c = '\0'; brackets = 0; for (s -= 1; s > localarg; s--) { switch (*s) { case '[': brackets = 1; /*@switchbreak@*/ break; case ']': if (c != '[') brackets = 0; /*@switchbreak@*/ break; } c = *s; if (!brackets && *s == '-') break; } /*@-nullstate@*/ /* FIX: *matches may be NULL. */ if (s == localarg) return RPMRC_NOTFOUND; /*@-boundswrite@*/ *s = '\0'; /*@=boundswrite@*/ rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, NULL, matches); /*@=nullstate@*/ if (rc != RPMRC_NOTFOUND) return rc; /*@-unqualifiedtrans@*/ /* FIX: double indirection */ *matches = dbiFreeIndexSet(*matches); /*@=unqualifiedtrans@*/ /* how about name-version-release? */ release = s + 1; c = '\0'; brackets = 0; for (; s > localarg; s--) { switch (*s) { case '[': brackets = 1; /*@switchbreak@*/ break; case ']': if (c != '[') brackets = 0; /*@switchbreak@*/ break; } c = *s; if (!brackets && *s == '-') break; } if (s == localarg) return RPMRC_NOTFOUND; /*@-boundswrite@*/ *s = '\0'; /*@=boundswrite@*/ /*@-nullstate@*/ /* FIX: *matches may be NULL. */ return dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, release, matches); /*@=nullstate@*/ } typedef struct miRE_s { rpmTag tag; /*!< header tag */ rpmMireMode mode; /*!< pattern match mode */ /*@only@*/ const char * pattern; /*!< pattern string */ int notmatch; /*!< like "grep -v" */ /*@only@*/ regex_t * preg; /*!< regex compiled pattern buffer */ int cflags; /*!< regcomp(3) flags */ int eflags; /*!< regexec(3) flags */ int fnflags; /*!< fnmatch(3) flags */ } * miRE; struct _rpmdbMatchIterator { /*@only@*/ const void * mi_keyp; size_t mi_keylen; /*@refcounted@*/ rpmdb mi_db; rpmTag mi_rpmtag; dbiIndexSet mi_set; DBC * mi_dbc; DBT mi_key; DBT mi_data; int mi_setx; /*@refcounted@*/ /*@null@*/ Header mi_h; int mi_sorted; int mi_cflags; int mi_modified; unsigned int mi_prevoffset; unsigned int mi_offset; unsigned int mi_filenum; int mi_nre; /*@only@*/ /*@null@*/ miRE mi_re; /*@null@*/ rpmts mi_ts; /*@null@*/ rpmRC (*mi_hdrchk) (rpmts ts, const void * uh, size_t uc, const char ** msg) /*@modifies ts, *msg @*/; }; /** * Rewrite a header into packages (if necessary) and free the header. * Note: this is called from a markReplacedFiles iteration, and *must* * preserve the "join key" (i.e. offset) for the header. * @param mi database iterator * @param dbi index database handle * @return 0 on success */ static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi) /*@globals fileSystem @*/ /*@modifies mi, fileSystem @*/ { int rc = 0; if (mi == NULL || mi->mi_h == NULL) return 0; if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) { DBT * key = &mi->mi_key; DBT * data = &mi->mi_data; sigset_t signalMask; rpmRC rpmrc = RPMRC_NOTFOUND; int xx; /*@i@*/ key->data = (void *) &mi->mi_prevoffset; key->size = sizeof(mi->mi_prevoffset); data->data = headerUnload(mi->mi_h); data->size = headerSizeof(mi->mi_h, HEADER_MAGIC_NO); /* Check header digest/signature on blob export (if requested). */ if (mi->mi_hdrchk && mi->mi_ts) { const char * msg = NULL; int lvl; rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, data->data, data->size, &msg); lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG); rpmMessage(lvl, "%s h#%8u %s", (rpmrc == RPMRC_FAIL ? _("rpmdb: skipping") : "write"), mi->mi_prevoffset, (msg ? msg : "\n")); msg = _free(msg); } if (data->data != NULL && rpmrc != RPMRC_FAIL) { (void) blockSignals(dbi->dbi_rpmdb, &signalMask); rc = dbiPut(dbi, mi->mi_dbc, key, data, DB_KEYLAST); if (rc) { rpmError(RPMERR_DBPUTINDEX, _("error(%d) storing record #%d into %s\n"), rc, mi->mi_prevoffset, tagName(dbi->dbi_rpmtag)); } xx = dbiSync(dbi, 0); (void) unblockSignals(dbi->dbi_rpmdb, &signalMask); } data->data = _free(data->data); data->size = 0; } mi->mi_h = headerFree(mi->mi_h); /*@-nullstate@*/ return rc; /*@=nullstate@*/ } rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi) { dbiIndex dbi; int xx; int i; if (mi == NULL) return NULL; dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0); if (dbi == NULL) /* XXX can't happen */ return NULL; xx = miFreeHeader(mi, dbi); if (mi->mi_dbc) xx = dbiCclose(dbi, mi->mi_dbc, 0); mi->mi_dbc = NULL; if (mi->mi_re != NULL) for (i = 0; i < mi->mi_nre; i++) { miRE mire = mi->mi_re + i; mire->pattern = _free(mire->pattern); if (mire->preg != NULL) { regfree(mire->preg); /*@+voidabstract -usereleased @*/ /* LCL: regfree has bogus only */ mire->preg = _free(mire->preg); /*@=voidabstract =usereleased @*/ } } mi->mi_re = _free(mi->mi_re); mi->mi_set = dbiFreeIndexSet(mi->mi_set); mi->mi_keyp = _free(mi->mi_keyp); mi->mi_db = rpmdbUnlink(mi->mi_db, "matchIterator"); mi = _free(mi); (void) checkSignals(); return mi; } unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) { return (mi ? mi->mi_offset : 0); } unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) { return (mi ? mi->mi_filenum : 0); } int rpmdbGetIteratorCount(rpmdbMatchIterator mi) { return (mi && mi->mi_set ? mi->mi_set->count : 0); } /** * Return pattern match. * @param mire match iterator regex * @param val value to match * @return 0 if pattern matches, >0 on nomatch, <0 on error */ static int miregexec(miRE mire, const char * val) /*@*/ { int rc = 0; switch (mire->mode) { case RPMMIRE_STRCMP: rc = strcmp(mire->pattern, val); if (rc) rc = 1; break; case RPMMIRE_DEFAULT: case RPMMIRE_REGEX: /*@-boundswrite@*/ rc = regexec(mire->preg, val, 0, NULL, mire->eflags); /*@=boundswrite@*/ if (rc && rc != REG_NOMATCH) { char msg[256]; (void) regerror(rc, mire->preg, msg, sizeof(msg)-1); msg[sizeof(msg)-1] = '\0'; rpmError(RPMERR_REGEXEC, "%s: regexec failed: %s\n", mire->pattern, msg); rc = -1; } break; case RPMMIRE_GLOB: rc = fnmatch(mire->pattern, val, mire->fnflags); if (rc && rc != FNM_NOMATCH) rc = -1; break; default: rc = -1; break; } return rc; } /** * Compare iterator selectors by rpm tag (qsort/bsearch). * @param a 1st iterator selector * @param b 2nd iterator selector * @return result of comparison */ static int mireCmp(const void * a, const void * b) { const miRE mireA = (const miRE) a; const miRE mireB = (const miRE) b; return (mireA->tag - mireB->tag); } /** * Copy pattern, escaping for appropriate mode. * @param tag rpm tag * @retval modep type of pattern match * @param pattern pattern to duplicate * @return duplicated pattern */ static /*@only@*/ char * mireDup(rpmTag tag, rpmMireMode *modep, const char * pattern) /*@modifies *modep @*/ /*@requires maxSet(modep) >= 0 @*/ { const char * s; char * pat; char * t; int brackets; size_t nb; int c; /*@-boundswrite@*/ switch (*modep) { default: case RPMMIRE_DEFAULT: if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) { *modep = RPMMIRE_GLOB; pat = xstrdup(pattern); break; } nb = strlen(pattern) + sizeof("^$"); /* Find no. of bytes needed for pattern. */ /* periods are escaped, splats become '.*' */ c = '\0'; brackets = 0; for (s = pattern; *s != '\0'; s++) { switch (*s) { case '.': case '*': if (!brackets) nb++; /*@switchbreak@*/ break; case '\\': s++; /*@switchbreak@*/ break; case '[': brackets = 1; /*@switchbreak@*/ break; case ']': if (c != '[') brackets = 0; /*@switchbreak@*/ break; } c = *s; } pat = t = xmalloc(nb); if (pattern[0] != '^') *t++ = '^'; /* Copy pattern, escaping periods, prefixing splats with period. */ c = '\0'; brackets = 0; for (s = pattern; *s != '\0'; s++, t++) { switch (*s) { case '.': if (!brackets) *t++ = '\\'; /*@switchbreak@*/ break; case '*': if (!brackets) *t++ = '.'; /*@switchbreak@*/ break; case '\\': *t++ = *s++; /*@switchbreak@*/ break; case '[': brackets = 1; /*@switchbreak@*/ break; case ']': if (c != '[') brackets = 0; /*@switchbreak@*/ break; } c = *t = *s; } if (s > pattern && s[-1] != '$') *t++ = '$'; *t = '\0'; *modep = RPMMIRE_REGEX; break; case RPMMIRE_STRCMP: case RPMMIRE_REGEX: case RPMMIRE_GLOB: pat = xstrdup(pattern); break; } /*@-boundswrite@*/ return pat; } int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag, rpmMireMode mode, const char * pattern) { static rpmMireMode defmode = (rpmMireMode)-1; miRE mire = NULL; const char * allpat = NULL; int notmatch = 0; regex_t * preg = NULL; int cflags = 0; int eflags = 0; int fnflags = 0; int rc = 0; /*@-boundsread@*/ if (defmode == (rpmMireMode)-1) { const char *t = rpmExpand("%{?_query_selector_match}", NULL); if (*t == '\0' || !strcmp(t, "default")) defmode = RPMMIRE_DEFAULT; else if (!strcmp(t, "strcmp")) defmode = RPMMIRE_STRCMP; else if (!strcmp(t, "regex")) defmode = RPMMIRE_REGEX; else if (!strcmp(t, "glob")) defmode = RPMMIRE_GLOB; else defmode = RPMMIRE_DEFAULT; t = _free(t); } if (mi == NULL || pattern == NULL) return rc; /* Leading '!' inverts pattern match sense, like "grep -v". */ if (*pattern == '!') { notmatch = 1; pattern++; } /*@=boundsread@*/ /*@-boundswrite@*/ allpat = mireDup(tag, &mode, pattern); /*@=boundswrite@*/ if (mode == RPMMIRE_DEFAULT) mode = defmode; /*@-branchstate@*/ switch (mode) { case RPMMIRE_DEFAULT: case RPMMIRE_STRCMP: break; case RPMMIRE_REGEX: /*@-type@*/ preg = xcalloc(1, sizeof(*preg)); /*@=type@*/ cflags = (REG_EXTENDED | REG_NOSUB); rc = regcomp(preg, allpat, cflags); if (rc) { char msg[256]; (void) regerror(rc, preg, msg, sizeof(msg)-1); msg[sizeof(msg)-1] = '\0'; rpmError(RPMERR_REGCOMP, "%s: regcomp failed: %s\n", allpat, msg); } break; case RPMMIRE_GLOB: fnflags = FNM_PATHNAME | FNM_PERIOD; break; default: rc = -1; break; } /*@=branchstate@*/ if (rc) { /*@=kepttrans@*/ /* FIX: mire has kept values */ allpat = _free(allpat); if (preg) { regfree(preg); /*@+voidabstract -usereleased @*/ /* LCL: regfree has bogus only */ preg = _free(preg); /*@=voidabstract =usereleased @*/ } /*@=kepttrans@*/ return rc; } mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re)); mire = mi->mi_re + mi->mi_nre; mi->mi_nre++; mire->tag = tag; mire->mode = mode; mire->pattern = allpat; mire->notmatch = notmatch; mire->preg = preg; mire->cflags = cflags; mire->eflags = eflags; mire->fnflags = fnflags; /*@-boundsread@*/ if (mi->mi_nre > 1) qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp); /*@=boundsread@*/ return rc; } /** * Return iterator selector match. * @param mi rpm database iterator * @return 1 if header should be skipped */ static int mireSkip (const rpmdbMatchIterator mi) /*@*/ { HGE_t hge = (HGE_t) headerGetEntryMinMemory; HFD_t hfd = (HFD_t) headerFreeData; union { void * ptr; const char ** argv; const char * str; int_32 * i32p; int_16 * i16p; int_8 * i8p; } u; char numbuf[32]; rpmTagType t; int_32 c; miRE mire; static int_32 zero = 0; int ntags = 0; int nmatches = 0; int i, j; int rc; if (mi->mi_h == NULL) /* XXX can't happen */ return 0; /* * Apply tag tests, implicitly "||" for multiple patterns/values of a * single tag, implicitly "&&" between multiple tag patterns. */ /*@-boundsread@*/ if ((mire = mi->mi_re) != NULL) for (i = 0; i < mi->mi_nre; i++, mire++) { int anymatch; if (!hge(mi->mi_h, mire->tag, &t, (void **)&u, &c)) { if (mire->tag != RPMTAG_EPOCH) continue; t = RPM_INT32_TYPE; /*@-immediatetrans@*/ u.i32p = &zero; /*@=immediatetrans@*/ c = 1; } anymatch = 0; /* no matches yet */ while (1) { switch (t) { case RPM_CHAR_TYPE: case RPM_INT8_TYPE: sprintf(numbuf, "%d", (int) *u.i8p); rc = miregexec(mire, numbuf); if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) anymatch++; /*@switchbreak@*/ break; case RPM_INT16_TYPE: sprintf(numbuf, "%d", (int) *u.i16p); rc = miregexec(mire, numbuf); if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) anymatch++; /*@switchbreak@*/ break; case RPM_INT32_TYPE: sprintf(numbuf, "%d", (int) *u.i32p); rc = miregexec(mire, numbuf); if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) anymatch++; /*@switchbreak@*/ break; case RPM_STRING_TYPE: rc = miregexec(mire, u.str); if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) anymatch++; /*@switchbreak@*/ break; case RPM_I18NSTRING_TYPE: case RPM_STRING_ARRAY_TYPE: for (j = 0; j < c; j++) { rc = miregexec(mire, u.argv[j]); if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) { anymatch++; /*@innerbreak@*/ break; } } /*@switchbreak@*/ break; case RPM_NULL_TYPE: case RPM_BIN_TYPE: default: /*@switchbreak@*/ break; } if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) { i++; mire++; /*@innercontinue@*/ continue; } /*@innerbreak@*/ break; } /*@=boundsread@*/ u.ptr = hfd(u.ptr, t); ntags++; if (anymatch) nmatches++; } return (ntags == nmatches ? 0 : 1); } int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite) { int rc; if (mi == NULL) return 0; rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0; if (rewrite) mi->mi_cflags |= DB_WRITECURSOR; else mi->mi_cflags &= ~DB_WRITECURSOR; return rc; } int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified) { int rc; if (mi == NULL) return 0; rc = mi->mi_modified; mi->mi_modified = modified; return rc; } int rpmdbSetHdrChk(rpmdbMatchIterator mi, rpmts ts, rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg)) { int rc = 0; if (mi == NULL) return 0; /*@-assignexpose -newreftrans @*/ /*@i@*/ mi->mi_ts = ts; mi->mi_hdrchk = hdrchk; /*@=assignexpose =newreftrans @*/ return rc; } /*@-nullstate@*/ /* FIX: mi->mi_key.data may be NULL */ Header rpmdbNextIterator(rpmdbMatchIterator mi) { dbiIndex dbi; void * uh; size_t uhlen; DBT * key; DBT * data; void * keyp; size_t keylen; int rc; int xx; if (mi == NULL) return NULL; (void) checkSignals(); dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0); if (dbi == NULL) return NULL; /* * Cursors are per-iterator, not per-dbi, so get a cursor for the * iterator on 1st call. If the iteration is to rewrite headers, and the * CDB model is used for the database, then the cursor needs to * marked with DB_WRITECURSOR as well. */ if (mi->mi_dbc == NULL) xx = dbiCopen(dbi, dbi->dbi_txnid, &mi->mi_dbc, mi->mi_cflags); /*@-boundswrite@*/ key = &mi->mi_key; memset(key, 0, sizeof(*key)); data = &mi->mi_data; memset(data, 0, sizeof(*data)); /*@=boundswrite@*/ top: uh = NULL; uhlen = 0; do { /*@-branchstate -compmempass @*/ if (mi->mi_set) { if (!(mi->mi_setx < mi->mi_set->count)) return NULL; mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx); mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx); keyp = &mi->mi_offset; keylen = sizeof(mi->mi_offset); } else { key->data = keyp = (void *)mi->mi_keyp; key->size = keylen = mi->mi_keylen; data->data = uh; data->size = uhlen; #if !defined(_USE_COPY_LOAD) data->flags |= DB_DBT_MALLOC; #endif rc = dbiGet(dbi, mi->mi_dbc, key, data, (key->data == NULL ? DB_NEXT : DB_SET)); data->flags = 0; keyp = key->data; keylen = key->size; uh = data->data; uhlen = data->size; /* * If we got the next key, save the header instance number. * * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the * largest header instance in the database, and should be * skipped. */ /*@-boundswrite@*/ if (keyp && mi->mi_setx && rc == 0) memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset)); /*@=boundswrite@*/ /* Terminate on error or end of keys */ if (rc || (mi->mi_setx && mi->mi_offset == 0)) return NULL; } /*@=branchstate =compmempass @*/ mi->mi_setx++; } while (mi->mi_offset == 0); /* If next header is identical, return it now. */ /*@-compdef -refcounttrans -retalias -retexpose -usereleased @*/ if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset) return mi->mi_h; /*@=compdef =refcounttrans =retalias =retexpose =usereleased @*/ /* Retrieve next header blob for index iterator. */ /*@-branchstate -compmempass -immediatetrans @*/ if (uh == NULL) { key->data = keyp; key->size = keylen; #if !defined(_USE_COPY_LOAD) data->flags |= DB_DBT_MALLOC; #endif rc = dbiGet(dbi, mi->mi_dbc, key, data, DB_SET); data->flags = 0; keyp = key->data; keylen = key->size; uh = data->data; uhlen = data->size; if (rc) return NULL; } /*@=branchstate =compmempass =immediatetrans @*/ /* Rewrite current header (if necessary) and unlink. */ xx = miFreeHeader(mi, dbi); /* Is this the end of the iteration? */ if (uh == NULL) return NULL; /* Check header digest/signature once (if requested). */ /*@-boundsread -branchstate -sizeoftype @*/ if (mi->mi_hdrchk && mi->mi_ts) { rpmRC rpmrc = RPMRC_NOTFOUND; pbm_set * set = NULL; /* Don't bother re-checking a previously read header. */ if (mi->mi_db->db_bits) { set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits, &mi->mi_db->db_nbits, mi->mi_offset); if (PBM_ISSET(mi->mi_offset, set)) rpmrc = RPMRC_OK; } /* If blob is unchecked, check blob import consistency now. */ if (rpmrc != RPMRC_OK) { const char * msg = NULL; int lvl; rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg); lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG); rpmMessage(lvl, "%s h#%8u %s", (rpmrc == RPMRC_FAIL ? _("rpmdb: skipping") : " read"), mi->mi_offset, (msg ? msg : "\n")); msg = _free(msg); /* Mark header checked. */ if (set && rpmrc == RPMRC_OK) PBM_SET(mi->mi_offset, set); /* Skip damaged and inconsistent headers. */ if (rpmrc == RPMRC_FAIL) goto top; } } /*@=boundsread =branchstate =sizeoftype @*/ /* Did the header blob load correctly? */ #if !defined(_USE_COPY_LOAD) /*@-onlytrans@*/ mi->mi_h = headerLoad(uh); /*@=onlytrans@*/ if (mi->mi_h) mi->mi_h->flags |= HEADERFLAG_ALLOCATED; #else mi->mi_h = headerCopyLoad(uh); #endif if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) { rpmError(RPMERR_BADHEADER, _("rpmdb: damaged header #%u retrieved -- skipping.\n"), mi->mi_offset); goto top; } /* * Skip this header if iterator selector (if any) doesn't match. */ if (mireSkip(mi)) { /* XXX hack, can't restart with Packages locked on single instance. */ if (mi->mi_set || mi->mi_keyp == NULL) goto top; return NULL; } mi->mi_prevoffset = mi->mi_offset; mi->mi_modified = 0; /*@-compdef -retalias -retexpose -usereleased @*/ return mi->mi_h; /*@=compdef =retalias =retexpose =usereleased @*/ } /*@=nullstate@*/ static void rpmdbSortIterator(/*@null@*/ rpmdbMatchIterator mi) /*@modifies mi @*/ { if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) { /* * mergesort is much (~10x with lots of identical basenames) faster * than pure quicksort, but glibc uses msort_with_tmp() on stack. */ #if defined(__GLIBC__) /*@-boundsread@*/ qsort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs), hdrNumCmp); /*@=boundsread@*/ #else mergesort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs), hdrNumCmp); #endif mi->mi_sorted = 1; } } /*@-bounds@*/ /* LCL: segfault */ static int rpmdbGrowIterator(/*@null@*/ rpmdbMatchIterator mi, int fpNum) /*@globals rpmGlobalMacroContext, fileSystem @*/ /*@modifies mi, rpmGlobalMacroContext, fileSystem @*/ { DBC * dbcursor; DBT * key; DBT * data; dbiIndex dbi = NULL; dbiIndexSet set; int rc; int xx; int i; if (mi == NULL) return 1; dbcursor = mi->mi_dbc; key = &mi->mi_key; data = &mi->mi_data; if (key->data == NULL) return 1; dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0); if (dbi == NULL) return 1; xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0); rc = dbiGet(dbi, dbcursor, key, data, DB_SET); xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; if (rc) { /* error/not found */ if (rc != DB_NOTFOUND) rpmError(RPMERR_DBGETINDEX, _("error(%d) getting \"%s\" records from %s index\n"), rc, key->data, tagName(dbi->dbi_rpmtag)); return rc; } set = NULL; (void) dbt2set(dbi, data, &set); for (i = 0; i < set->count; i++) set->recs[i].fpNum = fpNum; /*@-branchstate@*/ if (mi->mi_set == NULL) { mi->mi_set = set; } else { #if 0 fprintf(stderr, "+++ %d = %d + %d\t\"%s\"\n", (mi->mi_set->count + set->count), mi->mi_set->count, set->count, ((char *)key->data)); #endif mi->mi_set->recs = xrealloc(mi->mi_set->recs, (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs))); memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs, set->count * sizeof(*(mi->mi_set->recs))); mi->mi_set->count += set->count; set = dbiFreeIndexSet(set); } /*@=branchstate@*/ return rc; } /*@=bounds@*/ int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums, int nHdrNums, int sorted) { if (mi == NULL || hdrNums == NULL || nHdrNums <= 0) return 1; if (mi->mi_set) (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted); return 0; } int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums) { if (mi == NULL || hdrNums == NULL || nHdrNums <= 0) return 1; if (mi->mi_set == NULL) mi->mi_set = xcalloc(1, sizeof(*mi->mi_set)); (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0); return 0; } rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmTag rpmtag, const void * keyp, size_t keylen) { rpmdbMatchIterator mi; DBT * key; DBT * data; dbiIndexSet set = NULL; dbiIndex dbi; const void * mi_keyp = NULL; int isLabel = 0; if (db == NULL) return NULL; (void) checkSignals(); /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */ if (rpmtag == RPMDBI_LABEL) { rpmtag = RPMTAG_NAME; isLabel = 1; } dbi = dbiOpen(db, rpmtag, 0); if (dbi == NULL) return NULL; mi = xcalloc(1, sizeof(*mi)); key = &mi->mi_key; data = &mi->mi_data; /*@-branchstate@*/ if (rpmtag != RPMDBI_PACKAGES && keyp) { DBC * dbcursor = NULL; int rc; int xx; if (isLabel) { /* XXX HACK to get rpmdbFindByLabel out of the API */ xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0); rc = dbiFindByLabel(dbi, dbcursor, key, data, keyp, &set); xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; } else if (rpmtag == RPMTAG_BASENAMES) { rc = rpmdbFindByFile(db, keyp, key, data, &set); } else { xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0); /*@-temptrans@*/ key->data = (void *) keyp; /*@=temptrans@*/ key->size = keylen; if (key->data && key->size == 0) key->size = strlen((char *)key->data); if (key->data && key->size == 0) key->size++; /* XXX "/" fixup. */ /*@-nullstate@*/ rc = dbiGet(dbi, dbcursor, key, data, DB_SET); /*@=nullstate@*/ if (rc > 0) { rpmError(RPMERR_DBGETINDEX, _("error(%d) getting \"%s\" records from %s index\n"), rc, (key->data ? key->data : "???"), tagName(dbi->dbi_rpmtag)); } if (rc == 0) (void) dbt2set(dbi, data, &set); xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; } if (rc) { /* error/not found */ set = dbiFreeIndexSet(set); mi = _free(mi); return NULL; } } /*@=branchstate@*/ if (keyp) { char * k; if (rpmtag != RPMDBI_PACKAGES && keylen == 0) keylen = strlen(keyp); k = xmalloc(keylen + 1); /*@-boundsread@*/ memcpy(k, keyp, keylen); /*@=boundsread@*/ k[keylen] = '\0'; /* XXX for strings */ mi_keyp = k; } mi->mi_keyp = mi_keyp; mi->mi_keylen = keylen; mi->mi_db = rpmdbLink(db, "matchIterator"); mi->mi_rpmtag = rpmtag; mi->mi_dbc = NULL; mi->mi_set = set; mi->mi_setx = 0; mi->mi_h = NULL; mi->mi_sorted = 0; mi->mi_cflags = 0; mi->mi_modified = 0; mi->mi_prevoffset = 0; mi->mi_offset = 0; mi->mi_filenum = 0; mi->mi_nre = 0; mi->mi_re = NULL; mi->mi_ts = NULL; mi->mi_hdrchk = NULL; /*@-nullret@*/ /* FIX: mi->mi_key.data may be NULL */ return mi; /*@=nullret@*/ } /* XXX psm.c */ int rpmdbRemove(rpmdb db, /*@unused@*/ int rid, unsigned int hdrNum, /*@unused@*/ rpmts ts, /*@unused@*/ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg)) { DBC * dbcursor = NULL; DBT * key = alloca(sizeof(*key)); DBT * data = alloca(sizeof(*data)); HGE_t hge = (HGE_t)headerGetEntryMinMemory; HFD_t hfd = headerFreeData; Header h; sigset_t signalMask; int ret = 0; int rc = 0; if (db == NULL) return 0; memset(key, 0, sizeof(*key)); memset(data, 0, sizeof(*data)); { rpmdbMatchIterator mi; mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum)); h = rpmdbNextIterator(mi); if (h) h = headerLink(h); mi = rpmdbFreeIterator(mi); } if (h == NULL) { rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"), "rpmdbRemove", hdrNum); return 1; } #ifdef DYING /* Add remove transaction id to header. */ if (rid != 0 && rid != -1) { int_32 tid = rid; (void) headerAddEntry(h, RPMTAG_REMOVETID, RPM_INT32_TYPE, &tid, 1); } #endif { const char *n, *v, *r; (void) headerNVR(h, &n, &v, &r); rpmMessage(RPMMESS_DEBUG, " --- h#%8u %s-%s-%s\n", hdrNum, n, v, r); } (void) blockSignals(db, &signalMask); /*@-nullpass -nullptrarith -nullderef @*/ /* FIX: rpmvals heartburn */ { int dbix; dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0); if (dbiTags != NULL) for (dbix = 0; dbix < dbiTagsMax; dbix++) { dbiIndex dbi; const char *av[1]; const char ** rpmvals = NULL; rpmTagType rpmtype = 0; int rpmcnt = 0; int rpmtag; int xx; int i, j; dbi = NULL; /*@-boundsread@*/ rpmtag = dbiTags[dbix]; /*@=boundsread@*/ /*@-branchstate@*/ switch (rpmtag) { /* Filter out temporary databases */ case RPMDBI_AVAILABLE: case RPMDBI_ADDED: case RPMDBI_REMOVED: case RPMDBI_DEPENDS: continue; /*@notreached@*/ /*@switchbreak@*/ break; case RPMDBI_PACKAGES: dbi = dbiOpen(db, rpmtag, 0); if (dbi == NULL) /* XXX shouldn't happen */ continue; /*@-immediatetrans@*/ key->data = &hdrNum; /*@=immediatetrans@*/ key->size = sizeof(hdrNum); rc = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR); rc = dbiGet(dbi, dbcursor, key, data, DB_SET); if (rc) { rpmError(RPMERR_DBGETINDEX, _("error(%d) setting header #%d record for %s removal\n"), rc, hdrNum, tagName(dbi->dbi_rpmtag)); } else rc = dbiDel(dbi, dbcursor, key, data, 0); xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR); dbcursor = NULL; if (!dbi->dbi_no_dbsync) xx = dbiSync(dbi, 0); continue; /*@notreached@*/ /*@switchbreak@*/ break; } /*@=branchstate@*/ if (!hge(h, rpmtag, &rpmtype, (void **) &rpmvals, &rpmcnt)) continue; dbi = dbiOpen(db, rpmtag, 0); if (dbi != NULL) { int printed; if (rpmtype == RPM_STRING_TYPE) { /* XXX force uniform headerGetEntry return */ av[0] = (const char *) rpmvals; rpmvals = av; rpmcnt = 1; } printed = 0; xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR); /*@-branchstate@*/ for (i = 0; i < rpmcnt; i++) { dbiIndexSet set; int stringvalued; byte bin[32]; switch (dbi->dbi_rpmtag) { case RPMTAG_FILEMD5S: /* Filter out empty MD5 strings. */ if (!(rpmvals[i] && *rpmvals[i] != '\0')) /*@innercontinue@*/ continue; /*@switchbreak@*/ break; default: /*@switchbreak@*/ break; } /* Identify value pointer and length. */ stringvalued = 0; switch (rpmtype) { /*@-sizeoftype@*/ case RPM_CHAR_TYPE: case RPM_INT8_TYPE: key->size = sizeof(RPM_CHAR_TYPE); key->data = rpmvals + i; /*@switchbreak@*/ break; case RPM_INT16_TYPE: key->size = sizeof(int_16); key->data = rpmvals + i; /*@switchbreak@*/ break; case RPM_INT32_TYPE: key->size = sizeof(int_32); key->data = rpmvals + i; /*@switchbreak@*/ break; /*@=sizeoftype@*/ case RPM_BIN_TYPE: key->size = rpmcnt; key->data = rpmvals; rpmcnt = 1; /* XXX break out of loop. */ /*@switchbreak@*/ break; case RPM_STRING_TYPE: case RPM_I18NSTRING_TYPE: rpmcnt = 1; /* XXX break out of loop. */ /*@fallthrough@*/ case RPM_STRING_ARRAY_TYPE: /* Convert from hex to binary. */ /*@-boundsread@*/ if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) { const char * s; byte * t; s = rpmvals[i]; t = bin; for (j = 0; j < 16; j++, t++, s += 2) *t = (nibble(s[0]) << 4) | nibble(s[1]); key->data = bin; key->size = 16; /*@switchbreak@*/ break; } /* Extract the pubkey id from the base64 blob. */ if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) { pgpDig dig = pgpNewDig(); const byte * pkt; ssize_t pktlen; if (b64decode(rpmvals[i], (void **)&pkt, &pktlen)) /*@innercontinue@*/ continue; (void) pgpPrtPkts(pkt, pktlen, dig, 0); memcpy(bin, dig->pubkey.signid, 8); pkt = _free(pkt); dig = _free(dig); key->data = bin; key->size = 8; /*@switchbreak@*/ break; } /*@=boundsread@*/ /*@fallthrough@*/ default: /*@i@*/ key->data = (void *) rpmvals[i]; key->size = strlen(rpmvals[i]); stringvalued = 1; /*@switchbreak@*/ break; } if (!printed) { if (rpmcnt == 1 && stringvalued) { rpmMessage(RPMMESS_DEBUG, _("removing \"%s\" from %s index.\n"), (char *)key->data, tagName(dbi->dbi_rpmtag)); } else { rpmMessage(RPMMESS_DEBUG, _("removing %d entries from %s index.\n"), rpmcnt, tagName(dbi->dbi_rpmtag)); } printed++; } /* XXX * This is almost right, but, if there are duplicate tag * values, there will be duplicate attempts to remove * the header instance. It's faster to just ignore errors * than to do things correctly. */ /* XXX with duplicates, an accurate data value and DB_GET_BOTH is needed. */ set = NULL; if (key->size == 0) key->size = strlen((char *)key->data); if (key->size == 0) key->size++; /* XXX "/" fixup. */ /*@-compmempass@*/ rc = dbiGet(dbi, dbcursor, key, data, DB_SET); if (rc == 0) { /* success */ (void) dbt2set(dbi, data, &set); } else if (rc == DB_NOTFOUND) { /* not found */ /*@innercontinue@*/ continue; } else { /* error */ rpmError(RPMERR_DBGETINDEX, _("error(%d) setting \"%s\" records from %s index\n"), rc, key->data, tagName(dbi->dbi_rpmtag)); ret += 1; /*@innercontinue@*/ continue; } /*@=compmempass@*/ rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1); /* If nothing was pruned, then don't bother updating. */ if (rc) { set = dbiFreeIndexSet(set); /*@innercontinue@*/ continue; } /*@-compmempass@*/ if (set->count > 0) { (void) set2dbt(dbi, data, set); rc = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST); if (rc) { rpmError(RPMERR_DBPUTINDEX, _("error(%d) storing record \"%s\" into %s\n"), rc, key->data, tagName(dbi->dbi_rpmtag)); ret += 1; } data->data = _free(data->data); data->size = 0; } else { rc = dbiDel(dbi, dbcursor, key, data, 0); if (rc) { rpmError(RPMERR_DBPUTINDEX, _("error(%d) removing record \"%s\" from %s\n"), rc, key->data, tagName(dbi->dbi_rpmtag)); ret += 1; } } /*@=compmempass@*/ set = dbiFreeIndexSet(set); } /*@=branchstate@*/ xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR); dbcursor = NULL; if (!dbi->dbi_no_dbsync) xx = dbiSync(dbi, 0); } if (rpmtype != RPM_BIN_TYPE) /* XXX WTFO? HACK ALERT */ rpmvals = hfd(rpmvals, rpmtype); rpmtype = 0; rpmcnt = 0; } rec = _free(rec); } /*@=nullpass =nullptrarith =nullderef @*/ (void) unblockSignals(db, &signalMask); h = headerFree(h); /* XXX return ret; */ return 0; } /* XXX install.c */ int rpmdbAdd(rpmdb db, int iid, Header h, /*@unused@*/ rpmts ts, /*@unused@*/ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg)) { DBC * dbcursor = NULL; DBT * key = alloca(sizeof(*key)); DBT * data = alloca(sizeof(*data)); HGE_t hge = (HGE_t)headerGetEntryMinMemory; HFD_t hfd = headerFreeData; sigset_t signalMask; const char ** baseNames; rpmTagType bnt; int count = 0; dbiIndex dbi; int dbix; unsigned int hdrNum = 0; int ret = 0; int rc; int xx; if (db == NULL) return 0; memset(key, 0, sizeof(*key)); memset(data, 0, sizeof(*data)); #ifdef NOTYET /* XXX headerRemoveEntry() broken on dribbles. */ xx = headerRemoveEntry(h, RPMTAG_REMOVETID); #endif if (iid != 0 && iid != -1) { int_32 tid = iid; if (!headerIsEntry(h, RPMTAG_INSTALLTID)) xx = headerAddEntry(h, RPMTAG_INSTALLTID, RPM_INT32_TYPE, &tid, 1); } /* * If old style filename tags is requested, the basenames need to be * retrieved early, and the header needs to be converted before * being written to the package header database. */ xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, &count); if (_noDirTokens) expandFilelist(h); (void) blockSignals(db, &signalMask); { unsigned int firstkey = 0; void * keyp = &firstkey; size_t keylen = sizeof(firstkey); void * datap = NULL; size_t datalen = 0; dbi = dbiOpen(db, RPMDBI_PACKAGES, 0); /*@-branchstate@*/ if (dbi != NULL) { /* XXX db0: hack to pass sizeof header to fadAlloc */ datap = h; datalen = headerSizeof(h, HEADER_MAGIC_NO); xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR); /* Retrieve join key for next header instance. */ /*@-compmempass@*/ key->data = keyp; key->size = keylen; /*@i@*/ data->data = datap; data->size = datalen; ret = dbiGet(dbi, dbcursor, key, data, DB_SET); keyp = key->data; keylen = key->size; datap = data->data; datalen = data->size; /*@=compmempass@*/ /*@-bounds@*/ hdrNum = 0; if (ret == 0 && datap) memcpy(&hdrNum, datap, sizeof(hdrNum)); ++hdrNum; if (ret == 0 && datap) { memcpy(datap, &hdrNum, sizeof(hdrNum)); } else { datap = &hdrNum; datalen = sizeof(hdrNum); } /*@=bounds@*/ key->data = keyp; key->size = keylen; /*@-kepttrans@*/ data->data = datap; /*@=kepttrans@*/ data->size = datalen; /*@-compmempass@*/ ret = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST); /*@=compmempass@*/ xx = dbiSync(dbi, 0); xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR); dbcursor = NULL; } /*@=branchstate@*/ } if (ret) { rpmError(RPMERR_DBCORRUPT, _("error(%d) allocating new package instance\n"), ret); goto exit; } /* Now update the indexes */ if (hdrNum) { dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0); if (dbiTags != NULL) for (dbix = 0; dbix < dbiTagsMax; dbix++) { const char *av[1]; const char **rpmvals = NULL; rpmTagType rpmtype = 0; int rpmcnt = 0; int rpmtag; int_32 * requireFlags; rpmRC rpmrc; int i, j; rpmrc = RPMRC_NOTFOUND; dbi = NULL; requireFlags = NULL; /*@-boundsread@*/ rpmtag = dbiTags[dbix]; /*@=boundsread@*/ switch (rpmtag) { /* Filter out temporary databases */ case RPMDBI_AVAILABLE: case RPMDBI_ADDED: case RPMDBI_REMOVED: case RPMDBI_DEPENDS: continue; /*@notreached@*/ /*@switchbreak@*/ break; case RPMDBI_PACKAGES: dbi = dbiOpen(db, rpmtag, 0); if (dbi == NULL) /* XXX shouldn't happen */ continue; xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR); key->data = (void *) &hdrNum; key->size = sizeof(hdrNum); data->data = headerUnload(h); data->size = headerSizeof(h, HEADER_MAGIC_NO); /* Check header digest/signature on blob export. */ if (hdrchk && ts) { const char * msg = NULL; int lvl; rpmrc = (*hdrchk) (ts, data->data, data->size, &msg); lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG); rpmMessage(lvl, "%s h#%8u %s", (rpmrc == RPMRC_FAIL ? _("rpmdb: skipping") : " +++"), hdrNum, (msg ? msg : "\n")); msg = _free(msg); } if (data->data != NULL && rpmrc != RPMRC_FAIL) { /*@-compmempass@*/ xx = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST); /*@=compmempass@*/ xx = dbiSync(dbi, 0); } data->data = _free(data->data); data->size = 0; xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR); dbcursor = NULL; if (!dbi->dbi_no_dbsync) xx = dbiSync(dbi, 0); continue; /*@notreached@*/ /*@switchbreak@*/ break; case RPMTAG_BASENAMES: /* XXX preserve legacy behavior */ rpmtype = bnt; rpmvals = baseNames; rpmcnt = count; /*@switchbreak@*/ break; case RPMTAG_REQUIRENAME: xx = hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt); xx = hge(h, RPMTAG_REQUIREFLAGS, NULL, (void **)&requireFlags, NULL); /*@switchbreak@*/ break; default: xx = hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt); /*@switchbreak@*/ break; } /*@-branchstate@*/ if (rpmcnt <= 0) { if (rpmtag != RPMTAG_GROUP) continue; /* XXX preserve legacy behavior */ rpmtype = RPM_STRING_TYPE; rpmvals = (const char **) "Unknown"; rpmcnt = 1; } /*@=branchstate@*/ dbi = dbiOpen(db, rpmtag, 0); if (dbi != NULL) { int printed; if (rpmtype == RPM_STRING_TYPE) { /* XXX force uniform headerGetEntry return */ /*@-observertrans@*/ av[0] = (const char *) rpmvals; /*@=observertrans@*/ rpmvals = av; rpmcnt = 1; } printed = 0; xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR); for (i = 0; i < rpmcnt; i++) { dbiIndexSet set; int stringvalued; byte bin[32]; byte * t; /* * Include the tagNum in all indices. rpm-3.0.4 and earlier * included the tagNum only for files. */ rec->tagNum = i; switch (dbi->dbi_rpmtag) { case RPMTAG_PUBKEYS: /*@switchbreak@*/ break; case RPMTAG_FILEMD5S: /* Filter out empty MD5 strings. */ if (!(rpmvals[i] && *rpmvals[i] != '\0')) /*@innercontinue@*/ continue; /*@switchbreak@*/ break; case RPMTAG_REQUIRENAME: /* Filter out install prerequisites. */ if (requireFlags && isInstallPreReq(requireFlags[i])) /*@innercontinue@*/ continue; /*@switchbreak@*/ break; case RPMTAG_TRIGGERNAME: if (i) { /* don't add duplicates */ /*@-boundsread@*/ for (j = 0; j < i; j++) { if (!strcmp(rpmvals[i], rpmvals[j])) /*@innerbreak@*/ break; } /*@=boundsread@*/ if (j < i) /*@innercontinue@*/ continue; } /*@switchbreak@*/ break; default: /*@switchbreak@*/ break; } /* Identify value pointer and length. */ stringvalued = 0; /*@-branchstate@*/ switch (rpmtype) { /*@-sizeoftype@*/ case RPM_CHAR_TYPE: case RPM_INT8_TYPE: key->size = sizeof(int_8); /*@i@*/ key->data = rpmvals + i; /*@switchbreak@*/ break; case RPM_INT16_TYPE: key->size = sizeof(int_16); /*@i@*/ key->data = rpmvals + i; /*@switchbreak@*/ break; case RPM_INT32_TYPE: key->size = sizeof(int_32); /*@i@*/ key->data = rpmvals + i; /*@switchbreak@*/ break; /*@=sizeoftype@*/ case RPM_BIN_TYPE: key->size = rpmcnt; /*@i@*/ key->data = rpmvals; rpmcnt = 1; /* XXX break out of loop. */ /*@switchbreak@*/ break; case RPM_STRING_TYPE: case RPM_I18NSTRING_TYPE: rpmcnt = 1; /* XXX break out of loop. */ /*@fallthrough@*/ case RPM_STRING_ARRAY_TYPE: /* Convert from hex to binary. */ /*@-boundsread@*/ if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) { const char * s; s = rpmvals[i]; t = bin; for (j = 0; j < 16; j++, t++, s += 2) *t = (nibble(s[0]) << 4) | nibble(s[1]); key->data = bin; key->size = 16; /*@switchbreak@*/ break; } /* Extract the pubkey id from the base64 blob. */ if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) { pgpDig dig = pgpNewDig(); const byte * pkt; ssize_t pktlen; if (b64decode(rpmvals[i], (void **)&pkt, &pktlen)) /*@innercontinue@*/ continue; (void) pgpPrtPkts(pkt, pktlen, dig, 0); memcpy(bin, dig->pubkey.signid, 8); pkt = _free(pkt); dig = _free(dig); key->data = bin; key->size = 8; /*@switchbreak@*/ break; } /*@=boundsread@*/ /*@fallthrough@*/ default: /*@i@*/ key->data = (void *) rpmvals[i]; key->size = strlen(rpmvals[i]); stringvalued = 1; /*@switchbreak@*/ break; } /*@=branchstate@*/ if (!printed) { if (rpmcnt == 1 && stringvalued) { rpmMessage(RPMMESS_DEBUG, _("adding \"%s\" to %s index.\n"), (char *)key->data, tagName(dbi->dbi_rpmtag)); } else { rpmMessage(RPMMESS_DEBUG, _("adding %d entries to %s index.\n"), rpmcnt, tagName(dbi->dbi_rpmtag)); } printed++; } /* XXX with duplicates, an accurate data value and DB_GET_BOTH is needed. */ set = NULL; if (key->size == 0) key->size = strlen((char *)key->data); if (key->size == 0) key->size++; /* XXX "/" fixup. */ /*@-compmempass@*/ rc = dbiGet(dbi, dbcursor, key, data, DB_SET); if (rc == 0) { /* success */ /* With duplicates, cursor is positioned, discard the record. */ if (!dbi->dbi_permit_dups) (void) dbt2set(dbi, data, &set); } else if (rc != DB_NOTFOUND) { /* error */ rpmError(RPMERR_DBGETINDEX, _("error(%d) getting \"%s\" records from %s index\n"), rc, key->data, tagName(dbi->dbi_rpmtag)); ret += 1; /*@innercontinue@*/ continue; } /*@=compmempass@*/ if (set == NULL) /* not found or duplicate */ set = xcalloc(1, sizeof(*set)); (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0); /*@-compmempass@*/ (void) set2dbt(dbi, data, set); rc = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST); /*@=compmempass@*/ if (rc) { rpmError(RPMERR_DBPUTINDEX, _("error(%d) storing record %s into %s\n"), rc, key->data, tagName(dbi->dbi_rpmtag)); ret += 1; } /*@-unqualifiedtrans@*/ data->data = _free(data->data); /*@=unqualifiedtrans@*/ data->size = 0; set = dbiFreeIndexSet(set); } xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR); dbcursor = NULL; if (!dbi->dbi_no_dbsync) xx = dbiSync(dbi, 0); } /*@-observertrans@*/ if (rpmtype != RPM_BIN_TYPE) /* XXX WTFO? HACK ALERT */ rpmvals = hfd(rpmvals, rpmtype); /*@=observertrans@*/ rpmtype = 0; rpmcnt = 0; } /*@=nullpass =nullptrarith =nullderef @*/ rec = _free(rec); } exit: (void) unblockSignals(db, &signalMask); return ret; } #define _skip(_dn) { sizeof(_dn)-1, (_dn) } /*@unchecked@*/ /*@observer@*/ static struct skipDir_s { int dnlen; /*@observer@*/ /*@null@*/ const char * dn; } skipDirs[] = { _skip("/usr/share/zoneinfo"), _skip("/usr/share/locale"), _skip("/usr/share/i18n"), _skip("/usr/lib/locale"), { 0, NULL } }; static int skipDir(const char * dn) /*@*/ { struct skipDir_s * sd = skipDirs; int dnlen; dnlen = strlen(dn); for (sd = skipDirs; sd->dn != NULL; sd++) { if (dnlen < sd->dnlen) continue; if (strncmp(dn, sd->dn, sd->dnlen)) continue; return 1; } return 0; } /* XXX transaction.c */ /*@-compmempass@*/ int rpmdbFindFpList(rpmdb db, fingerPrint * fpList, dbiIndexSet * matchList, int numItems) { DBT * key; DBT * data; HGE_t hge = (HGE_t)headerGetEntryMinMemory; HFD_t hfd = headerFreeData; rpmdbMatchIterator mi; fingerPrintCache fpc; Header h; int i, xx; if (db == NULL) return 0; mi = rpmdbInitIterator(db, RPMTAG_BASENAMES, NULL, 0); if (mi == NULL) /* XXX should never happen */ return 0; key = &mi->mi_key; data = &mi->mi_data; /* Gather all installed headers with matching basename's. */ for (i = 0; i < numItems; i++) { /*@-boundswrite@*/ matchList[i] = xcalloc(1, sizeof(*(matchList[i]))); /*@=boundswrite@*/ /*@-boundsread -dependenttrans@*/ key->data = (void *) fpList[i].baseName; /*@=boundsread =dependenttrans@*/ key->size = strlen((char *)key->data); if (key->size == 0) key->size++; /* XXX "/" fixup. */ if (skipDir(fpList[i].entry->dirName)) continue; xx = rpmdbGrowIterator(mi, i); } if ((i = rpmdbGetIteratorCount(mi)) == 0) { mi = rpmdbFreeIterator(mi); return 0; } fpc = fpCacheCreate(i); rpmdbSortIterator(mi); /* iterator is now sorted by (recnum, filenum) */ /* For all installed headers with matching basename's ... */ if (mi != NULL) while ((h = rpmdbNextIterator(mi)) != NULL) { const char ** dirNames; const char ** baseNames; const char ** fullBaseNames; rpmTagType bnt, dnt; int_32 * dirIndexes; int_32 * fullDirIndexes; fingerPrint * fps; dbiIndexItem im; int start; int num; int end; start = mi->mi_setx - 1; im = mi->mi_set->recs + start; /* Find the end of the set of matched basename's in this package. */ /*@-boundsread@*/ for (end = start + 1; end < mi->mi_set->count; end++) { if (im->hdrNum != mi->mi_set->recs[end].hdrNum) /*@innerbreak@*/ break; } /*@=boundsread@*/ num = end - start; /* Compute fingerprints for this installed header's matches */ xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &fullBaseNames, NULL); xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL); xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fullDirIndexes, NULL); baseNames = xcalloc(num, sizeof(*baseNames)); dirIndexes = xcalloc(num, sizeof(*dirIndexes)); /*@-bounds@*/ for (i = 0; i < num; i++) { baseNames[i] = fullBaseNames[im[i].tagNum]; dirIndexes[i] = fullDirIndexes[im[i].tagNum]; } /*@=bounds@*/ fps = xcalloc(num, sizeof(*fps)); fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps); /* Add db (recnum,filenum) to list for fingerprint matches. */ /*@-boundsread@*/ for (i = 0; i < num; i++, im++) { /*@-nullpass@*/ /* FIX: fpList[].subDir may be NULL */ if (!FP_EQUAL(fps[i], fpList[im->fpNum])) /*@innercontinue@*/ continue; /*@=nullpass@*/ xx = dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0); } /*@=boundsread@*/ fps = _free(fps); dirNames = hfd(dirNames, dnt); fullBaseNames = hfd(fullBaseNames, bnt); baseNames = _free(baseNames); dirIndexes = _free(dirIndexes); mi->mi_setx = end; } mi = rpmdbFreeIterator(mi); fpc = fpCacheFree(fpc); return 0; } /*@=compmempass@*/ /** * Check if file esists using stat(2). * @param urlfn file name (may be URL) * @return 1 if file exists, 0 if not */ static int rpmioFileExists(const char * urlfn) /*@globals fileSystem, internalState @*/ /*@modifies fileSystem, internalState @*/ { const char *fn; int urltype = urlPath(urlfn, &fn); struct stat buf; /*@-branchstate@*/ if (*fn == '\0') fn = "/"; /*@=branchstate@*/ switch (urltype) { case URL_IS_FTP: /* XXX WRONG WRONG WRONG */ case URL_IS_HTTP: /* XXX WRONG WRONG WRONG */ case URL_IS_PATH: case URL_IS_UNKNOWN: if (Stat(fn, &buf)) { switch(errno) { case ENOENT: case EINVAL: return 0; } } break; case URL_IS_DASH: default: return 0; /*@notreached@*/ break; } return 1; } static int rpmdbRemoveDatabase(const char * prefix, const char * dbpath, int _dbapi) /*@globals fileSystem, internalState @*/ /*@modifies fileSystem, internalState @*/ { int i; char * filename; int xx; i = strlen(dbpath); /*@-bounds -branchstate@*/ if (dbpath[i - 1] != '/') { filename = alloca(i); strcpy(filename, dbpath); filename[i] = '/'; filename[i + 1] = '\0'; dbpath = filename; } /*@=bounds =branchstate@*/ filename = alloca(strlen(prefix) + strlen(dbpath) + 40); switch (_dbapi) { case 3: if (dbiTags != NULL) for (i = 0; i < dbiTagsMax; i++) { /*@-boundsread@*/ const char * base = tagName(dbiTags[i]); /*@=boundsread@*/ sprintf(filename, "%s/%s/%s", prefix, dbpath, base); (void)rpmCleanPath(filename); if (!rpmioFileExists(filename)) continue; xx = unlink(filename); } for (i = 0; i < 16; i++) { sprintf(filename, "%s/%s/__db.%03d", prefix, dbpath, i); (void)rpmCleanPath(filename); if (!rpmioFileExists(filename)) continue; xx = unlink(filename); } break; case 2: case 1: case 0: break; } sprintf(filename, "%s/%s", prefix, dbpath); (void)rpmCleanPath(filename); xx = rmdir(filename); return 0; } static int rpmdbMoveDatabase(const char * prefix, const char * olddbpath, int _olddbapi, const char * newdbpath, int _newdbapi) /*@globals fileSystem, internalState @*/ /*@modifies fileSystem, internalState @*/ { int i; char * ofilename, * nfilename; struct stat * nst = alloca(sizeof(*nst)); int rc = 0; int xx; i = strlen(olddbpath); /*@-branchstate@*/ if (olddbpath[i - 1] != '/') { ofilename = alloca(i + 2); strcpy(ofilename, olddbpath); ofilename[i] = '/'; ofilename[i + 1] = '\0'; olddbpath = ofilename; } /*@=branchstate@*/ i = strlen(newdbpath); /*@-branchstate@*/ if (newdbpath[i - 1] != '/') { nfilename = alloca(i + 2); strcpy(nfilename, newdbpath); nfilename[i] = '/'; nfilename[i + 1] = '\0'; newdbpath = nfilename; } /*@=branchstate@*/ ofilename = alloca(strlen(prefix) + strlen(olddbpath) + 40); nfilename = alloca(strlen(prefix) + strlen(newdbpath) + 40); switch (_olddbapi) { case 3: if (dbiTags != NULL) for (i = 0; i < dbiTagsMax; i++) { const char * base; int rpmtag; /* Filter out temporary databases */ switch ((rpmtag = dbiTags[i])) { case RPMDBI_AVAILABLE: case RPMDBI_ADDED: case RPMDBI_REMOVED: case RPMDBI_DEPENDS: continue; /*@notreached@*/ /*@switchbreak@*/ break; default: /*@switchbreak@*/ break; } base = tagName(rpmtag); sprintf(ofilename, "%s/%s/%s", prefix, olddbpath, base); (void)rpmCleanPath(ofilename); if (!rpmioFileExists(ofilename)) continue; sprintf(nfilename, "%s/%s/%s", prefix, newdbpath, base); (void)rpmCleanPath(nfilename); /* * Get uid/gid/mode/mtime. If old doesn't exist, use new. * XXX Yes, the variable names are backwards. */ if (stat(nfilename, nst) < 0) if (stat(ofilename, nst) < 0) continue; if ((xx = rename(ofilename, nfilename)) != 0) { rc = 1; continue; } xx = chown(nfilename, nst->st_uid, nst->st_gid); xx = chmod(nfilename, (nst->st_mode & 07777)); { struct utimbuf stamp; stamp.actime = nst->st_atime; stamp.modtime = nst->st_mtime; xx = utime(nfilename, &stamp); } } for (i = 0; i < 16; i++) { sprintf(ofilename, "%s/%s/__db.%03d", prefix, olddbpath, i); (void)rpmCleanPath(ofilename); if (!rpmioFileExists(ofilename)) continue; xx = unlink(ofilename); sprintf(nfilename, "%s/%s/__db.%03d", prefix, newdbpath, i); (void)rpmCleanPath(nfilename); xx = unlink(nfilename); } break; case 2: case 1: case 0: break; } if (rc || _olddbapi == _newdbapi) return rc; rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi); /* Remove /etc/rpm/macros.db1 configuration file if db3 rebuilt. */ if (rc == 0 && _newdbapi == 1 && _olddbapi == 3) { const char * mdb1 = "/etc/rpm/macros.db1"; struct stat st; if (!stat(mdb1, &st) && S_ISREG(st.st_mode) && !unlink(mdb1)) rpmMessage(RPMMESS_DEBUG, _("removing %s after successful db3 rebuild.\n"), mdb1); } return rc; } int rpmdbRebuild(const char * prefix, rpmts ts, rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg)) /*@globals _rebuildinprogress @*/ /*@modifies _rebuildinprogress @*/ { rpmdb olddb; const char * dbpath = NULL; const char * rootdbpath = NULL; rpmdb newdb; const char * newdbpath = NULL; const char * newrootdbpath = NULL; const char * tfn; int nocleanup = 1; int failed = 0; int removedir = 0; int rc = 0, xx; int _dbapi; int _dbapi_rebuild; /*@-branchstate@*/ if (prefix == NULL) prefix = "/"; /*@=branchstate@*/ _dbapi = rpmExpandNumeric("%{_dbapi}"); _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}"); /*@-nullpass@*/ tfn = rpmGetPath("%{?_dbpath}", NULL); /*@=nullpass@*/ /*@-boundsread@*/ if (!(tfn && tfn[0] != '\0')) /*@=boundsread@*/ { rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set")); rc = 1; goto exit; } dbpath = rootdbpath = rpmGetPath(prefix, tfn, NULL); if (!(prefix[0] == '/' && prefix[1] == '\0')) dbpath += strlen(prefix); tfn = _free(tfn); /*@-nullpass@*/ tfn = rpmGetPath("%{?_dbpath_rebuild}", NULL); /*@=nullpass@*/ /*@-boundsread@*/ if (!(tfn && tfn[0] != '\0' && strcmp(tfn, dbpath))) /*@=boundsread@*/ { char pidbuf[20]; char *t; sprintf(pidbuf, "rebuilddb.%d", (int) getpid()); t = xmalloc(strlen(dbpath) + strlen(pidbuf) + 1); /*@-boundswrite@*/ (void)stpcpy(stpcpy(t, dbpath), pidbuf); /*@=boundswrite@*/ tfn = _free(tfn); tfn = t; nocleanup = 0; } newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL); if (!(prefix[0] == '/' && prefix[1] == '\0')) newdbpath += strlen(prefix); tfn = _free(tfn); rpmMessage(RPMMESS_DEBUG, _("rebuilding database %s into %s\n"), rootdbpath, newrootdbpath); if (!access(newrootdbpath, F_OK)) { rpmError(RPMERR_MKDIR, _("temporary database %s already exists\n"), newrootdbpath); rc = 1; goto exit; } rpmMessage(RPMMESS_DEBUG, _("creating directory %s\n"), newrootdbpath); if (Mkdir(newrootdbpath, 0755)) { rpmError(RPMERR_MKDIR, _("creating directory %s: %s\n"), newrootdbpath, strerror(errno)); rc = 1; goto exit; } removedir = 1; rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"), _dbapi); _rebuildinprogress = 1; /*@-boundswrite@*/ if (openDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644, RPMDB_FLAG_MINIMAL)) { rc = 1; goto exit; } /*@=boundswrite@*/ _dbapi = olddb->db_api; _rebuildinprogress = 0; rpmMessage(RPMMESS_DEBUG, _("opening new database with dbapi %d\n"), _dbapi_rebuild); (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1); /*@-boundswrite@*/ if (openDatabase(prefix, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) { rc = 1; goto exit; } /*@=boundswrite@*/ _dbapi_rebuild = newdb->db_api; { Header h = NULL; rpmdbMatchIterator mi; #define _RECNUM rpmdbGetIteratorOffset(mi) /* RPMDBI_PACKAGES */ mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0); if (ts && hdrchk) (void) rpmdbSetHdrChk(mi, ts, hdrchk); while ((h = rpmdbNextIterator(mi)) != NULL) { /* let's sanity check this record a bit, otherwise just skip it */ if (!(headerIsEntry(h, RPMTAG_NAME) && headerIsEntry(h, RPMTAG_VERSION) && headerIsEntry(h, RPMTAG_RELEASE) && headerIsEntry(h, RPMTAG_BUILDTIME))) { rpmError(RPMERR_INTERNAL, _("header #%u in the database is bad -- skipping.\n"), _RECNUM); continue; } /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */ if (_db_filter_dups || newdb->db_filter_dups) { const char * name, * version, * release; int skip = 0; (void) headerNVR(h, &name, &version, &release); /*@-shadow@*/ { rpmdbMatchIterator mi; mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0); (void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version); (void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release); while (rpmdbNextIterator(mi)) { skip = 1; /*@innerbreak@*/ break; } mi = rpmdbFreeIterator(mi); } /*@=shadow@*/ if (skip) continue; } /* Deleted entries are eliminated in legacy headers by copy. */ { Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE) ? headerCopy(h) : NULL); rc = rpmdbAdd(newdb, -1, (nh ? nh : h), ts, hdrchk); nh = headerFree(nh); } if (rc) { rpmError(RPMERR_INTERNAL, _("cannot add record originally at %u\n"), _RECNUM); failed = 1; break; } } mi = rpmdbFreeIterator(mi); } if (!nocleanup) { olddb->db_remove_env = 1; newdb->db_remove_env = 1; } xx = rpmdbClose(olddb); xx = rpmdbClose(newdb); if (failed) { rpmMessage(RPMMESS_NORMAL, _("failed to rebuild database: original database " "remains in place\n")); xx = rpmdbRemoveDatabase(prefix, newdbpath, _dbapi_rebuild); rc = 1; goto exit; } else if (!nocleanup) { if (rpmdbMoveDatabase(prefix, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) { rpmMessage(RPMMESS_ERROR, _("failed to replace old database with new " "database!\n")); rpmMessage(RPMMESS_ERROR, _("replace files in %s with files from %s " "to recover"), dbpath, newdbpath); rc = 1; goto exit; } } rc = 0; exit: if (removedir && !(rc == 0 && nocleanup)) { rpmMessage(RPMMESS_DEBUG, _("removing directory %s\n"), newrootdbpath); if (Rmdir(newrootdbpath)) rpmMessage(RPMMESS_ERROR, _("failed to remove directory %s: %s\n"), newrootdbpath, strerror(errno)); } newrootdbpath = _free(newrootdbpath); rootdbpath = _free(rootdbpath); return rc; }