/*@-sizeoftype @*/ /** \ingroup rpmdb dbi * \file rpmdb/rpmdb.c */ #include "system.h" #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 "rpmcli.h" #include "rpmdb.h" #include "fprint.h" #include "misc.h" #include "debug.h" /*@access dbiIndexSet@*/ /*@access dbiIndexItem@*/ /*@access Header@*/ /* XXX compared with NULL */ /*@access rpmdbMatchIterator@*/ /*@unchecked@*/ static int _debug = 0; #define INLINE /*@unchecked@*/ static int _rebuildinprogress = 0; /*@unchecked@*/ const int dbiTags[] = { RPMDBI_PACKAGES, RPMTAG_NAME, RPMTAG_BASENAMES, RPMTAG_GROUP, RPMTAG_REQUIRENAME, RPMTAG_PROVIDENAME, RPMTAG_CONFLICTNAME, RPMTAG_OBSOLETENAME, RPMTAG_TRIGGERNAME, RPMTAG_DIRNAMES, RPMTAG_INSTALLTID, RPMTAG_SIGMD5, RPMTAG_SHA1HEADER, }; const int dbiTagsMax = sizeof(dbiTags) / sizeof(*dbiTags); /** * 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; } /** * 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; } /*@-redecl@*/ #define DB1vec NULL #define DB2vec NULL /*@unchecked@*/ extern struct _dbiVec db3vec; #define DB3vec &db3vec /*@=redecl@*/ /*@-nullassign@*/ /*@observer@*/ /*@unchecked@*/ static struct _dbiVec *mydbvecs[] = { DB1vec, DB1vec, DB2vec, DB3vec, NULL }; /*@=nullassign@*/ INLINE int dbiSync(dbiIndex dbi, unsigned int flags) { if (_debug < 0 || dbi->dbi_debug) fprintf(stderr, " Sync %s\n", tagName(dbi->dbi_rpmtag)); return (*dbi->dbi_vec->sync) (dbi, flags); } INLINE int dbiByteSwapped(dbiIndex dbi) { return (*dbi->dbi_vec->byteswapped) (dbi); } INLINE int dbiCopen(dbiIndex dbi, /*@out@*/ DBC ** dbcp, unsigned int flags) { if (_debug < 0 || dbi->dbi_debug) fprintf(stderr, "+++ RMW %s %s\n", tagName(dbi->dbi_rpmtag), ((flags & DBI_WRITECURSOR) ? "WRITECURSOR" : "")); return (*dbi->dbi_vec->copen) (dbi, dbcp, flags); } INLINE int dbiCclose(dbiIndex dbi, /*@only@*/DBC * dbcursor, unsigned int flags) { if (_debug < 0 || dbi->dbi_debug) fprintf(stderr, "--- RMW %s\n", tagName(dbi->dbi_rpmtag)); return (*dbi->dbi_vec->cclose) (dbi, dbcursor, flags); } INLINE int dbiDel(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen, unsigned int flags) { int NULkey; int rc; /* Make sure that keylen is correct for "" lookup. */ NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0); if (NULkey) keylen++; rc = (*dbi->dbi_vec->cdel) (dbi, dbcursor, keyp, keylen, flags); if (NULkey) keylen--; if (_debug < 0 || dbi->dbi_debug) fprintf(stderr, " Del %s key (%p,%ld) %s rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)keyp : ""), rc); return rc; } INLINE int dbiGet(dbiIndex dbi, DBC * dbcursor, void ** keypp, size_t * keylenp, void ** datapp, size_t * datalenp, unsigned int flags) { int NULkey; int rc; /* Make sure that keylen is correct for "" lookup. */ NULkey = (keypp && *keypp && *((char *)(*keypp)) == '\0'); NULkey = (keylenp && *keylenp == 0 && NULkey); if (keylenp && NULkey) (*keylenp)++; rc = (*dbi->dbi_vec->cget) (dbi, dbcursor, keypp, keylenp, datapp, datalenp, flags); if (keylenp && NULkey) (*keylenp)--; /*@-nullderef -nullpass@*/ if (_debug < 0 || dbi->dbi_debug) { int dataval = 0xdeadbeef; const char * kvp; char keyval[64]; keyval[0] = '\0'; if (keypp && *keypp && keylenp) { if (printable(*keypp, *keylenp)) { kvp = *keypp; } else if (*keylenp <= sizeof(int)) { int keyint = 0; memcpy(&keyint, *keypp, sizeof(keyint)); sprintf(keyval, "#%d", keyint); kvp = keyval; } else { static const char hex[] = "0123456789abcdef"; const unsigned char * s = *keypp; char * t = keyval; int i; for (i = 0; i < *keylenp && t < (keyval+sizeof(keyval)-2); i++) { *t++ = hex[ (unsigned)((*s >> 4) & 0x0f) ]; *t++ = hex[ (unsigned)((*s++ ) & 0x0f) ]; } *t = '\0'; kvp = keyval; } } else kvp = keyval; if (rc == 0 && datapp && *datapp && datalenp && *datalenp >= sizeof(dataval)) { memcpy(&dataval, *datapp, sizeof(dataval)); } fprintf(stderr, " Get %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n", tagName(dbi->dbi_rpmtag), *keypp, (long)*keylenp, *datapp, (long)*datalenp, kvp, (unsigned)dataval, rc); } /*@=nullderef =nullpass@*/ return rc; } INLINE int dbiPut(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen, const void * datap, size_t datalen, unsigned int flags) { int NULkey; int rc; /* XXX make sure that keylen is correct for "" lookup */ NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0); if (NULkey) keylen++; rc = (*dbi->dbi_vec->cput) (dbi, dbcursor, keyp, keylen, datap, datalen, flags); if (NULkey) keylen--; /*@-nullderef -nullpass@*/ if (_debug < 0 || dbi->dbi_debug) { int dataval = 0xdeadbeef; const char * kvp; char keyval[64]; keyval[0] = '\0'; if (keyp) { if (printable(keyp, keylen)) { kvp = keyp; } else if (keylen <= sizeof(int)) { int keyint = 0; memcpy(&keyint, keyp, sizeof(keyint)); sprintf(keyval, "#%d", keyint); kvp = keyval; } else { static const char hex[] = "0123456789abcdef"; const unsigned char * s = keyp; char * t = keyval; int i; for (i = 0; i < keylen && t < (keyval+sizeof(keyval)-2); i++) { *t++ = hex[ (unsigned)((*s >> 4) & 0x0f) ]; *t++ = hex[ (unsigned)((*s++ ) & 0x0f) ]; } *t = '\0'; kvp = keyval; } } else kvp = keyval; if (rc == 0 && datap && datalen >= sizeof(dataval)) { memcpy(&dataval, datap, sizeof(dataval)); } fprintf(stderr, " Put %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, (datap ? datap : NULL), (long)datalen, kvp, (unsigned)dataval, rc); } /*@=nullderef =nullpass@*/ return rc; } INLINE int dbiCount(dbiIndex dbi, DBC * dbcursor, unsigned int * countp, unsigned int flags) { int rc = (*dbi->dbi_vec->ccount) (dbi, dbcursor, countp, flags); if (rc == 0 && countp && *countp > 1) fprintf(stderr, " Count %s: %u rc %d\n", tagName(dbi->dbi_rpmtag), *countp, rc); return rc; } INLINE int dbiVerify(dbiIndex dbi, unsigned int flags) { int dbi_debug = dbi->dbi_debug; int dbi_rpmtag = dbi->dbi_rpmtag; int rc; dbi->dbi_verify_on_close = 1; rc = (*dbi->dbi_vec->close) (dbi, flags); if (_debug < 0 || dbi_debug) fprintf(stderr, " Verify %s rc %d\n", tagName(dbi_rpmtag), rc); return rc; } INLINE int dbiClose(dbiIndex dbi, unsigned int flags) { if (_debug < 0 || dbi->dbi_debug) fprintf(stderr, " Close %s\n", tagName(dbi->dbi_rpmtag)); return (*dbi->dbi_vec->close) (dbi, flags); } dbiIndex dbiOpen(rpmdb db, int rpmtag, /*@unused@*/ unsigned int flags) { dbiIndex dbi = NULL; int _dbapi, _dbapi_rebuild, _dbapi_wanted; int rc = 0; if (db == NULL) return NULL; int dbix; for (dbix = 0; dbix < dbiTagsMax; dbix++) if (rpmtag == dbiTags[dbix]) break; if (dbix >= dbiTagsMax) return NULL; /* Is this index already open ? */ if ((dbi = db->_dbi[dbix]) != NULL) return dbi; /*@-globs -mods @*/ /* FIX: rpmGlobalMacroContext not in */ _dbapi_rebuild = rpmExpandNumeric("%{?_dbapi_rebuild}"); /*@=globs =mods @*/ 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; } /*@-mods@*/ errno = 0; /*@=mods@*/ 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; /*@-mods@*/ errno = 0; /*@=mods@*/ 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 (rc == 0 && dbi) db->_dbi[dbix] = dbi; else dbi = db3Free(dbi); return dbi; } /** * Create and initialize item for index database set. * @param hdrNum header instance in db * @param tagNum tag index in header * @return new item */ static INLINE 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; \ } /** * Return items that match criteria. * @param dbi index database handle * @param dbcursor index database cursor * @param keyp search key * @param keylen search key length (0 will use strlen(key)) * @retval setp address of items retrieved from index database * @return -1 error, 0 success, 1 not found */ static int dbiSearch(dbiIndex dbi, DBC * dbcursor, const char * keyp, size_t keylen, /*@out@*/ dbiIndexSet * setp) /*@globals fileSystem @*/ /*@modifies *dbcursor, *setp, fileSystem @*/ { unsigned int gflags = 0; /* dbiGet() flags */ void * datap = NULL; size_t datalen = 0; int rc; if (setp) *setp = NULL; if (keylen == 0) keylen = strlen(keyp); /*@-mods@*/ /* FIX: indirection @*/ rc = dbiGet(dbi, dbcursor, (void **)&keyp, &keylen, &datap, &datalen, gflags); /*@=mods@*/ if (rc > 0) { rpmError(RPMERR_DBGETINDEX, _("error(%d) getting \"%s\" records from %s index\n"), rc, keyp, tagName(dbi->dbi_rpmtag)); } else if (rc == 0 && setp) { int _dbbyteswapped = dbiByteSwapped(dbi); const char * sdbir = datap; dbiIndexSet set; int i; set = xmalloc(sizeof(*set)); /* Convert to database internal format */ if (sdbir) switch (dbi->dbi_jlen) { default: case 2*sizeof(int_32): set->count = datalen / (2*sizeof(int_32)); set->recs = xmalloc(set->count * sizeof(*(set->recs))); 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; set->recs[i].dbNum = 0; } break; case 1*sizeof(int_32): set->count = datalen / (1*sizeof(int_32)); set->recs = xmalloc(set->count * sizeof(*(set->recs))); 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; set->recs[i].dbNum = 0; } break; } /*@-branchstate@*/ if (setp) *setp = set; /*@=branchstate@*/ } return rc; } /** * Change/delete items that match criteria. * @param dbi index database handle * @param dbcursor index database cursor * @param keyp update key * @param keylen update key length * @param set items to update in index database * @return 0 success, 1 not found */ /*@-compmempass -mustmod@*/ static int dbiUpdateIndex(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen, dbiIndexSet set) /*@globals fileSystem @*/ /*@modifies *dbcursor, set, fileSystem @*/ { unsigned int pflags = 0; /* dbiPut() flags */ unsigned int dflags = 0; /* dbiDel() flags */ void * datap; size_t datalen; int rc; if (set->count) { char * tdbir; int i; int _dbbyteswapped = dbiByteSwapped(dbi); /* Convert to database internal format */ switch (dbi->dbi_jlen) { default: case 2*sizeof(int_32): datalen = set->count * (2 * sizeof(int_32)); datap = tdbir = alloca(datalen); 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): datalen = set->count * (1 * sizeof(int_32)); datap = tdbir = alloca(datalen); 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; } rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, pflags); if (rc) { rpmError(RPMERR_DBPUTINDEX, _("error(%d) storing record %s into %s\n"), rc, keyp, tagName(dbi->dbi_rpmtag)); } } else { rc = dbiDel(dbi, dbcursor, keyp, keylen, dflags); if (rc) { rpmError(RPMERR_DBPUTINDEX, _("error(%d) removing record %s from %s\n"), rc, keyp, tagName(dbi->dbi_rpmtag)); } } return rc; } /*@=compmempass =mustmod@*/ /* 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 INLINE 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 (set->count > 1 && sortset) qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp); /*@-compmempass@*/ /* FIX: set->recs.{hdrNum,tagNum,fpNum,dbNum} undef */ return 0; /*@=compmempass@*/ } /** * 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 INLINE 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; 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; } /** * Disable all signals, returning previous signal mask. */ static int blockSignals(/*@unused@*/ rpmdb db, /*@out@*/ sigset_t * oldMask) /*@globals fileSystem @*/ /*@modifies *oldMask, fileSystem @*/ { sigset_t newMask; (void) sigfillset(&newMask); /* block all signals */ return sigprocmask(SIG_BLOCK, &newMask, oldMask); } /** * Restore signal mask. */ static int unblockSignals(/*@unused@*/ rpmdb db, sigset_t * oldMask) /*@globals fileSystem @*/ /*@modifies fileSystem @*/ { 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; for (dbix = 0; dbix < dbiTagsMax; dbix++) { if (db->_dbi[dbix] != NULL) continue; (void) dbiOpen(db, dbiTags[dbix], db->db_flags); } return rc; } /* XXX query.c, rpminstall.c, verify.c */ int rpmdbClose(rpmdb db) { int dbix; int rc = 0; if (db == NULL) return 0; 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->_dbi = _free(db->_dbi); db = _free(db); return rc; } 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@*/ static /*@only@*/ /*@null@*/ rpmdb newRpmdb(/*@kept@*/ /*@null@*/ const char * root, /*@kept@*/ /*@null@*/ const char * home, int mode, int perms, int flags) /*@globals rpmGlobalMacroContext @*/ /*@modifies rpmGlobalMacroContext @*/ { rpmdb db = xcalloc(sizeof(*db), 1); const char * epfx = _DB_ERRPFX; static int _initialized = 0; if (!_initialized) _initialized = 1; /*@-assignexpose@*/ *db = dbTemplate; /* structure assignment */ /*@=assignexpose@*/ 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@*/ } /*@-nullpass@*/ db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL); /*@=nullpass@*/ db->db_remove_env = 0; db->db_ndbi = dbiTagsMax; db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi)); /*@-globstate@*/ return db; /*@=globstate@*/ } static int openDatabase(/*@null@*/ const char * prefix, /*@null@*/ const char * dbpath, int _dbapi, /*@null@*/ /*@out@*/ rpmdb *dbp, int mode, int perms, int flags) /*@globals rpmGlobalMacroContext, fileSystem @*/ /*@modifies *dbp, fileSystem @*/ { rpmdb db; int rc, xx; static int _dbenv_removed = 0; int justCheck = flags & RPMDB_FLAG_JUSTCHECK; int minimal = flags & RPMDB_FLAG_MINIMAL; /* 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; /*@-mods@*/ db = newRpmdb(prefix, dbpath, mode, perms, flags); /*@=mods@*/ if (db == NULL) return 1; if (!_dbenv_removed) { static int _enable_cdb = -1; /* XXX hack in suoport for CDB, otherwise nuke the state. */ /*@-mods@*/ if (_enable_cdb < 0) _enable_cdb = rpmExpandNumeric("%{?__dbi_cdb:1}"); /*@=mods@*/ if (!_enable_cdb) { char * fn; int i; i = sizeof("//__db.000"); if (db->db_root) i += strlen(db->db_root); if (db->db_home) i += strlen(db->db_home); fn = alloca(i); for (i = 0; i < 16; i++) { sprintf(fn, "%s/%s/__db.%03d", (db->db_root ? db->db_root : ""), (db->db_home ? db->db_home : ""), i); (void) rpmCleanPath(fn); (void) unlink(fn); } } _dbenv_removed++; } db->db_api = _dbapi; { int dbix; rc = 0; 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: 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; /* XXX open only Packages, indices created on the fly. */ #if 0 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 *dbp = db; return rc; } /* XXX python/rpmmodule.c */ /*@-globs@*/ /* FIX: rpmGlobalMacroContext not in */ int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms) { /*@-mods@*/ int _dbapi = rpmExpandNumeric("%{?_dbapi}"); /*@=mods@*/ return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0); } int rpmdbInit (const char * prefix, int perms) { rpmdb db = NULL; /*@-mods@*/ int _dbapi = rpmExpandNumeric("%{?_dbapi}"); /*@=mods@*/ int rc; rc = openDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR), perms, RPMDB_FLAG_JUSTCHECK); 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; /*@-mods@*/ int _dbapi = rpmExpandNumeric("%{?_dbapi}"); /*@=mods@*/ int rc = 0; rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0); if (rc) return rc; 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; } /*@=globs@*/ static int rpmdbFindByFile(rpmdb db, /*@null@*/ const char * filespec, /*@out@*/ dbiIndexSet * matches) /*@globals fileSystem @*/ /*@modifies db, *matches, fileSystem @*/ { HGE_t hge = (HGE_t)headerGetEntryMinMemory; HFD_t hfd = headerFreeData; const char * dirName; const char * baseName; rpmTagType bnt, dnt; 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; t = strncpy(alloca(len + 1), filespec, len); t[len] = '\0'; dirName = t; baseName++; } else { dirName = ""; baseName = filespec; } /*@=branchstate@*/ if (baseName == NULL) return -2; dbi = dbiOpen(db, RPMTAG_BASENAMES, 0); if (dbi != NULL) { dbcursor = NULL; xx = dbiCopen(dbi, &dbcursor, 0); rc = dbiSearch(dbi, dbcursor, baseName, strlen(baseName), &allMatches); xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; } else rc = -2; if (rc) { allMatches = dbiFreeIndexSet(allMatches); return rc; } assert(allMatches && allMatches->count > 0); fingerPrintCache fpc = fpCacheCreate(allMatches->count); fingerPrint fp1 = fpLookup(fpc, dirName, baseName, 1); *matches = xcalloc(1, sizeof(**matches)); rec = dbiIndexNewItem(0, 0); i = 0; 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) { dbiIndex dbi; dbiIndexSet matches = NULL; int rc = -1; int xx; if (db == NULL) return 0; /* XXX * There's a segfault here with CDB access, let's treat the symptom * while diagnosing the disease. */ if (name == NULL || *name == '\0') return 0; dbi = dbiOpen(db, RPMTAG_NAME, 0); if (dbi) { DBC * dbcursor = NULL; xx = dbiCopen(dbi, &dbcursor, 0); rc = dbiSearch(dbi, dbcursor, name, strlen(name), &matches); xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; } /*@-nullpass@*/ /* FIX: matches might be NULL */ if (rc == 0) /* success */ rc = dbiIndexSetCount(matches); else if (rc > 0) /* error */ rpmError(RPMERR_DBCORRUPT, _("error(%d) counting packages\n"), rc); else /* not found */ rc = 0; /*@=nullpass@*/ matches = dbiFreeIndexSet(matches); return rc; } /* XXX transaction.c */ /* 0 found matches */ /* 1 no matches */ /* 2 error */ /** * Attempt partial matches on name[-version[-release]] strings. * @param dbi index database handle (always RPMTAG_NAME) * @param dbcursor index database cursor * @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 0 on match, 1 on no match, 2 on error */ static int dbiFindMatches(dbiIndex dbi, DBC * dbcursor, const char * name, /*@null@*/ const char * version, /*@null@*/ const char * release, /*@out@*/ dbiIndexSet * matches) /*@globals fileSystem @*/ /*@modifies dbi, *dbcursor, *matches, fileSystem @*/ { int gotMatches; int rc; int i; rc = dbiSearch(dbi, dbcursor, name, strlen(name), matches); if (rc != 0) { rc = ((rc == -1) ? 2 : 1); goto exit; } if (version == NULL && release == NULL) { rc = 0; goto exit; } gotMatches = 0; /* Make sure the version and release match. */ /*@-branchstate@*/ for (i = 0; i < dbiIndexSetCount(*matches); i++) { unsigned int recoff = dbiIndexRecordOffset(*matches, i); Header h; if (recoff == 0) continue; { rpmdbMatchIterator mi; 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 = 2; goto exit; } if (release && rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release)) { rc = 2; goto exit; } h = rpmdbNextIterator(mi); if (h) h = headerLink(h); mi = rpmdbFreeIterator(mi); } if (h) /* structure assignment */ (*matches)->recs[gotMatches++] = (*matches)->recs[i]; else (*matches)->recs[i].hdrNum = 0; h = headerFree(h); } /*@=branchstate@*/ if (gotMatches) { (*matches)->count = gotMatches; rc = 0; } else rc = 1; exit: if (rc && matches && *matches) { /*@-unqualifiedtrans@*/ /* FIX: double indirection */ *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 arg name[-version[-release]] string * @retval matches set of header instances that match * @return 0 on match, 1 on no match, 2 on error */ static int dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, /*@null@*/ const char * arg, /*@out@*/ dbiIndexSet * matches) /*@globals fileSystem @*/ /*@modifies dbi, *dbcursor, *matches, fileSystem @*/ { const char * release; char * localarg; char * s; char c; int brackets; int rc; if (arg == NULL || strlen(arg) == 0) return 1; /* did they give us just a name? */ rc = dbiFindMatches(dbi, dbcursor, arg, NULL, NULL, matches); if (rc != 1) return rc; /*@-unqualifiedtrans@*/ *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 1; *s = '\0'; rc = dbiFindMatches(dbi, dbcursor, localarg, s + 1, NULL, matches); if (rc != 1) return rc; /*@-unqualifiedtrans@*/ *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 1; *s = '\0'; return dbiFindMatches(dbi, dbcursor, localarg, s + 1, release, matches); /*@=nullstate@*/ } /** * Rewrite a header in the database. * Note: this is called from a markReplacedFiles iteration, and *must* * preserve the "join key" (i.e. offset) for the header. * @param dbi index database handle (always RPMDBI_PACKAGES) * @param dbcursor index database cursor * @param offset join key * @param h rpm header * @return 0 on success */ static int dbiUpdateRecord(dbiIndex dbi, DBC * dbcursor, int offset, Header h) /*@globals fileSystem @*/ /*@modifies *dbcursor, h, fileSystem @*/ { sigset_t signalMask; void * uh; size_t uhlen; int rc = EINVAL; /* XXX W2DO? */ unsigned int pflags = 0; /* dbiPut() flags */ int xx; uhlen = headerSizeof(h, HEADER_MAGIC_NO); uh = headerUnload(h); if (uh) { (void) blockSignals(dbi->dbi_rpmdb, &signalMask); rc = dbiPut(dbi, dbcursor, &offset, sizeof(offset), uh, uhlen, pflags); xx = dbiSync(dbi, 0); (void) unblockSignals(dbi->dbi_rpmdb, &signalMask); uh = _free(uh); } else fprintf(stderr, "*** dbiUpdateRecord: uh is NULL\n"); return rc; } 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; /*@kept@*/ rpmdb mi_db; int mi_rpmtag; dbiIndexSet mi_set; DBC * mi_dbc; unsigned int mi_ndups; int mi_setx; /*@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; unsigned int mi_fpnum; unsigned int mi_dbnum; int mi_nre; /*@only@*//*@null@*/ miRE mi_re; /*@only@*//*@null@*/ const char * mi_version; /*@only@*//*@null@*/ const char * mi_release; }; rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi) { dbiIndex dbi = NULL; int xx; int i; if (mi == NULL) return mi; dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0); /*@-branchstate@*/ if (mi->mi_h) { if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) { xx = dbiUpdateRecord(dbi, mi->mi_dbc, mi->mi_prevoffset, mi->mi_h); } mi->mi_h = headerFree(mi->mi_h); } /*@=branchstate@*/ if (dbi) { if (dbi->dbi_rmw) xx = dbiCclose(dbi, dbi->dbi_rmw, 0); dbi->dbi_rmw = 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_release = _free(mi->mi_release); mi->mi_version = _free(mi->mi_version); /*@-branchstate@*/ if (dbi && mi->mi_dbc) xx = dbiCclose(dbi, mi->mi_dbc, DBI_ITERATOR); /*@=branchstate@*/ mi->mi_dbc = NULL; mi->mi_set = dbiFreeIndexSet(mi->mi_set); mi->mi_keyp = _free(mi->mi_keyp); mi = _free(mi); return mi; } rpmdb rpmdbGetIteratorRpmDB(rpmdbMatchIterator mi) { if (mi == NULL) return NULL; /*@-retexpose -retalias@*/ return mi->mi_db; /*@=retexpose =retalias@*/ } unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) { if (mi == NULL) return 0; return mi->mi_offset; } unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) { if (mi == NULL) return 0; return mi->mi_filenum; } int rpmdbGetIteratorCount(rpmdbMatchIterator mi) { if (!(mi && mi->mi_set)) return 0; /* XXX W2DO? */ return mi->mi_set->count; } /** * Return pattern match. * @param mire match iterator regex * @param val value to match * @return 0 if pattern matches */ static int miregexec(miRE mire, const char * val) /*@*/ { int rc = 0; switch (mire->mode) { case RPMMIRE_STRCMP: rc = strcmp(mire->pattern, val); break; case RPMMIRE_DEFAULT: case RPMMIRE_REGEX: /*@-nullpass@*/ /* LCL: annotation needs fix */ rc = regexec(mire->preg, val, 0, NULL, mire->eflags); /*@=nullpass@*/ 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: /*@-moduncon@*/ rc = fnmatch(mire->pattern, val, mire->fnflags); /*@=moduncon@*/ if (rc && rc != FNM_NOMATCH) rc = -1; break; default: rc = -1; break; } return rc; } /** * Compare iterator selctors 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 @*/ { const char * s; char * pat; char * t; int brackets; size_t nb; int c; switch (*modep) { default: case RPMMIRE_DEFAULT: if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) { *modep = RPMMIRE_GLOB; pat = xstrdup(pattern); break; } nb = strlen(pattern) + sizeof("^$"); /* 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++ = '^'; /* periods are escaped, splats become '.*' */ 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 (pattern[nb-1] != '$') *t++ = '$'; *t = '\0'; *modep = RPMMIRE_REGEX; break; case RPMMIRE_STRCMP: case RPMMIRE_REGEX: case RPMMIRE_GLOB: pat = xstrdup(pattern); break; } return pat; } /*@-globs@*/ /* FIX: rpmGlobalMacroContext not in */ 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; if (defmode == (rpmMireMode)-1) { /*@-mods -nullpass @*/ const char *t = rpmExpand("%{?_query_selector_match}", NULL); /*@=mods =nullpass @*/ 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++; } /*@-mods@*/ /* FIX: WTFO? */ allpat = mireDup(tag, &mode, pattern); /*@=mods@*/ 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; (void) qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp); /*@-nullstate@*/ /* FIX: mi->mi_re->preg may be NULL */ return rc; /*@=nullstate@*/ } /*@=globs@*/ /** * 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; 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, implictly "||" for multiple patterns/values of a * single tag, implictly "&&" between multiple tag patterns. */ 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)) continue; 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; } u.ptr = hfd(u.ptr, t); ntags++; if (anymatch) nmatches++; } return (ntags == nmatches ? 0 : 1); } int rpmdbSetIteratorRelease(rpmdbMatchIterator mi, const char * release) { return rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release); } int rpmdbSetIteratorVersion(rpmdbMatchIterator mi, const char * version) { return rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version); } int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite) { int rc; if (mi == NULL) return 0; rc = (mi->mi_cflags & DBI_WRITECURSOR) ? 1 : 0; if (rewrite) mi->mi_cflags |= DBI_WRITECURSOR; else mi->mi_cflags &= ~DBI_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; } Header XrpmdbNextIterator(rpmdbMatchIterator mi, /*@unused@*/ const char * f, /*@unused@*/ unsigned int l) { return rpmdbNextIterator(mi); } Header rpmdbNextIterator(rpmdbMatchIterator mi) { dbiIndex dbi; void * uh = NULL; size_t uhlen = 0; unsigned int gflags = 0; /* dbiGet() flags */ void * keyp; size_t keylen; int rc; int xx; if (mi == NULL) return NULL; 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, &mi->mi_dbc, (mi->mi_cflags | DBI_ITERATOR)); dbi->dbi_lastoffset = mi->mi_prevoffset; top: /* XXX skip over instances with 0 join key */ do { 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 { keyp = (void *)mi->mi_keyp; /* XXX FIXME const */ keylen = mi->mi_keylen; rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, gflags); /* * 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. */ if (rc == 0 && keyp && (dbi->dbi_lastoffset || mi->mi_setx)) memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset)); /* Terminate on error or end of keys */ if (rc || (mi->mi_setx && mi->mi_offset == 0)) return NULL; } mi->mi_setx++; } while (mi->mi_offset == 0); if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset) goto exit; /* Retrieve next header */ if (uh == NULL) { rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, gflags); if (rc) return NULL; } /* Free current header */ if (mi->mi_h) { if (mi->mi_modified && mi->mi_prevoffset) (void)dbiUpdateRecord(dbi, mi->mi_dbc, mi->mi_prevoffset, mi->mi_h); mi->mi_h = headerFree(mi->mi_h); } /* Is this the end of the iteration? */ if (uh == NULL) goto exit; mi->mi_h = headerCopyLoad(uh); /* XXX db1 with hybrid, simulated db interface on falloc.c needs free. */ /*@-branchstate@*/ if (dbi->dbi_api == 1) uh = _free(uh); /*@=branchstate@*/ /* Did the header load correctly? */ if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) { rpmError(RPMERR_BADHEADER, _("rpmdb: damaged header instance #%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; } headerSetInstance(mi->mi_h, mi->mi_offset); mi->mi_prevoffset = mi->mi_offset; mi->mi_modified = 0; exit: #ifdef NOTNOW if (mi->mi_h) { const char *n, *v, *r; (void) headerNVRD(mi->mi_h, &n, &v, &r, NULL); // we don't print the disttag rpmMessage(RPMMESS_DEBUG, "%s-%s-%s at 0x%x, h %p\n", n, v, r, mi->mi_offset, mi->mi_h); } #endif /*@-retexpose -retalias@*/ /*@-compdef -usereleased@*/ return mi->mi_h; /*@=compdef =usereleased@*/ /*@=retexpose =retalias@*/ } 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__) qsort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs), hdrNumCmp); #else mergesort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs), hdrNumCmp); #endif mi->mi_sorted = 1; } } static int rpmdbGrowIterator(/*@null@*/ rpmdbMatchIterator mi, const void * keyp, size_t keylen, int fpNum) /*@globals fileSystem @*/ /*@modifies mi, fileSystem @*/ { dbiIndex dbi = NULL; DBC * dbcursor = NULL; dbiIndexSet set = NULL; int rc; int xx; if (!(mi && keyp)) return 1; dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0); if (dbi == NULL) return 1; if (keylen == 0) keylen = strlen(keyp); xx = dbiCopen(dbi, &dbcursor, 0); rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set); xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; if (rc == 0) { /* success */ int i; for (i = 0; i < set->count; i++) set->recs[i].fpNum = fpNum; if (mi->mi_set == NULL) { mi->mi_set = set; set = NULL; } else { 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); return rc; } 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, int rpmtag, const void * keyp, size_t keylen) { rpmdbMatchIterator mi = NULL; dbiIndexSet set = NULL; dbiIndex dbi; const void * mi_keyp = NULL; int isLabel = 0; if (db == NULL) return NULL; /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */ switch (rpmtag) { case RPMDBI_LABEL: rpmtag = RPMTAG_NAME; isLabel = 1; break; } dbi = dbiOpen(db, rpmtag, 0); if (dbi == NULL) return NULL; #if 0 assert(dbi->dbi_rmw == NULL); /* db3: avoid "lost" cursors */ assert(dbi->dbi_lastoffset == 0); /* db0: avoid "lost" cursors */ #else if (dbi->dbi_rmw) fprintf(stderr, "*** RMW %s %p\n", tagName(rpmtag), dbi->dbi_rmw); #endif dbi->dbi_lastoffset = 0; /* db0: rewind to beginning */ 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, &dbcursor, 0); rc = dbiFindByLabel(dbi, dbcursor, keyp, &set); xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; } else if (rpmtag == RPMTAG_BASENAMES) { rc = rpmdbFindByFile(db, keyp, &set); } else { xx = dbiCopen(dbi, &dbcursor, 0); /*@-nullpass@*/ /* LCL: keyp != NULL here. */ rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set); /*@=nullpass@*/ xx = dbiCclose(dbi, dbcursor, 0); dbcursor = NULL; } if (rc) { /* error/not found */ set = dbiFreeIndexSet(set); return NULL; } } if (keyp) { char * k; if (rpmtag != RPMDBI_PACKAGES && keylen == 0) keylen = strlen(keyp); k = xmalloc(keylen + 1); memcpy(k, keyp, keylen); k[keylen] = '\0'; /* XXX for strings */ mi_keyp = k; } mi = xcalloc(1, sizeof(*mi)); mi->mi_keyp = mi_keyp; mi->mi_keylen = keylen; /*@-assignexpose@*/ mi->mi_db = db; /*@=assignexpose@*/ mi->mi_rpmtag = rpmtag; mi->mi_dbc = NULL; mi->mi_set = set; mi->mi_setx = 0; mi->mi_ndups = 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_fpnum = 0; mi->mi_dbnum = 0; mi->mi_nre = 0; mi->mi_re = NULL; mi->mi_version = NULL; mi->mi_release = NULL; /*@-nullret@*/ /* FIX: mi->mi_{keyp,dbc,set,re->preg} are NULL */ return mi; /*@=nullret@*/ } /** * Remove entry from database index. * @param dbi index database handle * @param dbcursor index database cursor * @param keyp search key * @param keylen search key length * @param rec record to remove * @return 0 on success */ static INLINE int removeIndexEntry(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen, dbiIndexItem rec) /*@globals fileSystem @*/ /*@modifies *dbcursor, fileSystem @*/ { dbiIndexSet set = NULL; int rc; rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set); if (rc < 0) /* not found */ rc = 0; else if (rc > 0) /* error */ rc = 1; /* error message already generated from dbindex.c */ else { /* success */ /*@-mods@*/ /* a single rec is not modified */ rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1); /*@=mods@*/ if (rc == 0 && dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set)) rc = 1; } set = dbiFreeIndexSet(set); return rc; } /*@-mods@*/ /* XXX install.c uninstall.c */ int rpmdbRemove(rpmdb db, /*@unused@*/ int rid, unsigned int hdrNum) { HGE_t hge = (HGE_t)headerGetEntryMinMemory; HFD_t hfd = headerFreeData; Header h; sigset_t signalMask; if (db == NULL) return 0; { 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) headerNVRD(h, &n, &v, &r, NULL); // we don't print the disttag rpmMessage(RPMMESS_DEBUG, " --- %10u %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); for (dbix = 0; dbix < dbiTagsMax; dbix++) { dbiIndex dbi; DBC * dbcursor = NULL; const char *av[1]; const char ** rpmvals = NULL; rpmTagType rpmtype = 0; int rpmcnt = 0; int rpmtag; int xx; int i, j; dbi = NULL; rpmtag = dbiTags[dbix]; /*@-branchstate@*/ switch (rpmtag) { /* Filter out temporary databases */ case RPMDBI_AVAILABLE: case RPMDBI_ADDED: case RPMDBI_REMOVED: continue; /*@notreached@*/ /*@switchbreak@*/ break; case RPMDBI_PACKAGES: dbi = dbiOpen(db, rpmtag, 0); if (dbi != NULL) { xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR); xx = dbiDel(dbi, dbcursor, &hdrNum, sizeof(hdrNum), 0); xx = dbiCclose(dbi, dbcursor, DBI_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, &dbcursor, DBI_WRITECURSOR); for (i = 0; i < rpmcnt; i++) { const void * valp; size_t vallen; int stringvalued; unsigned char bin[32]; /* Identify value pointer and length. */ stringvalued = 0; switch (rpmtype) { case RPM_CHAR_TYPE: case RPM_INT8_TYPE: vallen = sizeof(RPM_CHAR_TYPE); valp = rpmvals + i; /*@switchbreak@*/ break; case RPM_INT16_TYPE: vallen = sizeof(int_16); valp = rpmvals + i; /*@switchbreak@*/ break; case RPM_INT32_TYPE: vallen = sizeof(int_32); valp = rpmvals + i; /*@switchbreak@*/ break; case RPM_BIN_TYPE: vallen = rpmcnt; valp = 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: #ifdef NOTYET /* 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); valp = bin; vallen = 8; /*@switchbreak@*/ break; } #endif /*@fallthrough@*/ default: valp = rpmvals[i]; vallen = strlen(rpmvals[i]); stringvalued = 1; /*@switchbreak@*/ break; } if (!printed) { if (rpmcnt == 1 && stringvalued) { rpmMessage(RPMMESS_DEBUG, _("removing \"%s\" from %s index.\n"), valp, tagName(dbi->dbi_rpmtag)); } else { rpmMessage(RPMMESS_DEBUG, _("removing %d entries from %s index.\n"), rpmcnt, tagName(dbi->dbi_rpmtag)); } printed++; } /* * This is almost right, but, if there are duplicate tag * values, there will be duplicate attempts to remove * the header instance. It's easier to just ignore errors * than to do things correctly. */ xx = removeIndexEntry(dbi, dbcursor, valp, vallen, rec); } xx = dbiCclose(dbi, dbcursor, DBI_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); return 0; } /** * Add entry to database index. * @param dbi index database handle * @param dbcursor index database cursor * @param keyp search key * @param keylen search key length * @param rec record to add * @return 0 on success */ static INLINE int addIndexEntry(dbiIndex dbi, DBC * dbcursor, const char * keyp, size_t keylen, dbiIndexItem rec) /*@globals fileSystem @*/ /*@modifies *dbcursor, fileSystem @*/ { dbiIndexSet set = NULL; int rc; rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set); if (rc > 0) { /* error */ rc = 1; } else { /* With duplicates, cursor is positioned, discard the record. */ /*@-branchstate@*/ if (rc == 0 && dbi->dbi_permit_dups) set = dbiFreeIndexSet(set); /*@=branchstate@*/ if (set == NULL || rc < 0) { /* not found */ rc = 0; set = xcalloc(1, sizeof(*set)); } (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0); if (dbiUpdateIndex(dbi, dbcursor, keyp, keylen, set)) rc = 1; } set = dbiFreeIndexSet(set); return 0; } /* XXX install.c */ int rpmdbAdd(rpmdb db, int iid, Header h) { 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 gflags = 0; /* dbiGet() flags */ unsigned int pflags = 0; /* dbiPut() flags */ unsigned int hdrNum = 0; int rc = 0; int xx; if (db == NULL) return 0; #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); (void) blockSignals(db, &signalMask); { unsigned int firstkey = 0; DBC * dbcursor = NULL; 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, &dbcursor, DBI_WRITECURSOR); /* Retrieve join key for next header instance. */ rc = dbiGet(dbi, dbcursor, &keyp, &keylen, &datap, &datalen, gflags); hdrNum = 0; if (rc == 0 && datap) memcpy(&hdrNum, datap, sizeof(hdrNum)); ++hdrNum; if (rc == 0 && datap) { /*@-refcounttrans@*/ /* FIX: datap aliases h */ memcpy(datap, &hdrNum, sizeof(hdrNum)); /*@=refcounttrans@*/ } else { datap = &hdrNum; datalen = sizeof(hdrNum); } rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, pflags); xx = dbiSync(dbi, 0); xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR); dbcursor = NULL; } /*@=branchstate@*/ } if (rc) { rpmError(RPMERR_DBCORRUPT, _("error(%d) allocating new package instance\n"), rc); goto exit; } /* Now update the indexes */ if (hdrNum) { dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0); /*@-nullpass -nullptrarith -nullderef @*/ /* FIX: rpmvals heartburn */ for (dbix = 0; dbix < dbiTagsMax; dbix++) { DBC * dbcursor = NULL; const char *av[1]; const char **rpmvals = NULL; rpmTagType rpmtype = 0; int rpmcnt = 0; int rpmtag; int i, j; dbi = NULL; rpmtag = dbiTags[dbix]; switch (rpmtag) { /* Filter out temporary databases */ case RPMDBI_AVAILABLE: case RPMDBI_ADDED: case RPMDBI_REMOVED: continue; /*@notreached@*/ /*@switchbreak@*/ break; case RPMDBI_PACKAGES: dbi = dbiOpen(db, rpmtag, 0); if (dbi != NULL) { xx = dbiCopen(dbi, &dbcursor, DBI_WRITECURSOR); xx = dbiUpdateRecord(dbi, dbcursor, hdrNum, h); xx = dbiCclose(dbi, dbcursor, DBI_WRITECURSOR); dbcursor = NULL; if (!dbi->dbi_no_dbsync) xx = dbiSync(dbi, 0); { const char *n, *v, *r; xx = headerNVRD(h, &n, &v, &r, NULL); // we don't print the disttag rpmMessage(RPMMESS_DEBUG, " +++ %10u %s-%s-%s\n", hdrNum, n, v, r); } } continue; /*@notreached@*/ /*@switchbreak@*/ break; /* XXX preserve legacy behavior */ case RPMTAG_BASENAMES: rpmtype = bnt; rpmvals = baseNames; rpmcnt = count; /*@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, &dbcursor, DBI_WRITECURSOR); for (i = 0; i < rpmcnt; i++) { const void * valp; size_t vallen; int stringvalued; unsigned char bin[32]; /* * 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) { #ifdef NOTYET case RPMTAG_PUBKEYS: /*@switchbreak@*/ break; #endif case RPMTAG_REQUIRENAME: /* Filter out rpmlib dependencies */ if (strncmp(rpmvals[i], "rpmlib(", 7) == 0) /*@innercontinue@*/ continue; /*@switchbreak@*/ break; case RPMTAG_TRIGGERNAME: if (i) { /* don't add duplicates */ for (j = 0; j < i; j++) { if (!strcmp(rpmvals[i], rpmvals[j])) /*@innerbreak@*/ break; } if (j < i) /*@innercontinue@*/ continue; } /*@switchbreak@*/ break; default: /*@switchbreak@*/ break; } /* Identify value pointer and length. */ stringvalued = 0; switch (rpmtype) { case RPM_CHAR_TYPE: case RPM_INT8_TYPE: vallen = sizeof(int_8); valp = rpmvals + i; /*@switchbreak@*/ break; case RPM_INT16_TYPE: vallen = sizeof(int_16); valp = rpmvals + i; /*@switchbreak@*/ break; case RPM_INT32_TYPE: vallen = sizeof(int_32); valp = rpmvals + i; /*@switchbreak@*/ break; case RPM_BIN_TYPE: vallen = rpmcnt; valp = 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: #ifdef NOTYET /* 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); valp = bin; vallen = 8; /*@switchbreak@*/ break; } #endif /*@fallthrough@*/ default: valp = rpmvals[i]; vallen = strlen(rpmvals[i]); stringvalued = 1; /*@switchbreak@*/ break; } if (!printed) { if (rpmcnt == 1 && stringvalued) { rpmMessage(RPMMESS_DEBUG, _("adding \"%s\" to %s index.\n"), valp, tagName(dbi->dbi_rpmtag)); } else { rpmMessage(RPMMESS_DEBUG, _("adding %d entries to %s index.\n"), rpmcnt, tagName(dbi->dbi_rpmtag)); } printed++; } rc += addIndexEntry(dbi, dbcursor, valp, vallen, rec); } xx = dbiCclose(dbi, dbcursor, DBI_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); /* If everthing ok, mark header as installed now */ if (rc == 0) headerSetInstance(h, hdrNum); } exit: (void) unblockSignals(db, &signalMask); return rc; } /* XXX transaction.c */ int rpmdbFindFpList(rpmdb db, fingerPrint * fpList, dbiIndexSet * matchList, int numItems, fingerPrintCache fpc) { HGE_t hge = (HGE_t)headerGetEntryMinMemory; HFD_t hfd = headerFreeData; rpmdbMatchIterator mi; Header h; int i, xx; if (db == NULL) return 0; mi = rpmdbInitIterator(db, RPMTAG_BASENAMES, NULL, 0); /* Gather all matches from the database */ for (i = 0; i < numItems; i++) { xx = rpmdbGrowIterator(mi, fpList[i].baseName, 0, i); matchList[i] = xcalloc(1, sizeof(*(matchList[i]))); } if ((i = rpmdbGetIteratorCount(mi)) == 0) { mi = rpmdbFreeIterator(mi); return 0; } rpmdbSortIterator(mi); /* iterator is now sorted by (recnum, filenum) */ /* For each set of files matched in a package ... */ 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 files in this package. */ for (end = start + 1; end < mi->mi_set->count; end++) { if (im->hdrNum != mi->mi_set->recs[end].hdrNum) /*@innerbreak@*/ break; } num = end - start; /* Compute fingerprints for this 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)); for (i = 0; i < num; i++) { baseNames[i] = fullBaseNames[im[i].tagNum]; dirIndexes[i] = fullDirIndexes[im[i].tagNum]; } fps = xcalloc(num, sizeof(*fps)); fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps); /* Add db (recnum,filenum) to list for fingerprint matches. */ for (i = 0; i < num; i++, im++) { /*@-nullpass@*/ if (FP_EQUAL(fps[i], fpList[im->fpNum])) { /*@=nullpass@*/ /*@-usedef@*/ xx = dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0); /*@=usedef@*/ } } fps = _free(fps); dirNames = hfd(dirNames, dnt); fullBaseNames = hfd(fullBaseNames, bnt); baseNames = _free(baseNames); dirIndexes = _free(dirIndexes); mi->mi_setx = end; } mi = rpmdbFreeIterator(mi); return 0; } /** * 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 @*/ /*@modifies fileSystem @*/ { 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 @*/ /*@modifies fileSystem @*/ { int i; char * filename; int xx; i = strlen(dbpath); /*@-branchstate@*/ if (dbpath[i - 1] != '/') { filename = alloca(i); strcpy(filename, dbpath); filename[i] = '/'; filename[i + 1] = '\0'; dbpath = filename; } /*@=branchstate@*/ filename = alloca(strlen(prefix) + strlen(dbpath) + 40); switch (_dbapi) { case 3: for (i = 0; i < dbiTagsMax; i++) { const char * base = tagName(dbiTags[i]); 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 @*/ /*@modifies fileSystem @*/ { int i; struct stat st; char * ofilename, * nfilename; int rc = 0; int xx; int selinux = (is_selinux_enabled() > 0) && (matchpathcon_init(NULL) != -1); 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: 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: 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); if (Rename(ofilename, nfilename) != 0) rc = 1; else if (selinux && stat(nfilename, &st) == 0) { security_context_t scon = NULL; if (matchpathcon(nfilename, st.st_mode, &scon) != -1) { (void) setfilecon(nfilename, scon); freecon(scon); } } } 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); #ifdef DYING if ((xx = Rename(ofilename, nfilename)) != 0) rc = 1; #else xx = unlink(nfilename); #endif } break; case 2: case 1: case 0: break; } if (selinux) (void) matchpathcon_fini(); if (rc || _olddbapi == _newdbapi) return rc; rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi); return rc; } /*@-globs@*/ /* FIX: rpmGlobalMacroContext not in */ int rpmdbRebuild(const char * prefix) { 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@*/ if (!(tfn && tfn[0])) { 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@*/ if (!(tfn && tfn[0] && strcmp(tfn, dbpath))) { tfn = _free(tfn); tfn = xasprintf("%srebuilddb.%u", dbpath, getpid()); nocleanup = 0; } newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL); if (!(prefix[0] == '/' && prefix[1] == '\0')) newdbpath += strlen(prefix); tfn = _free(tfn); rpmMessage(RPMMESS_VERBOSE, _("rebuilding database %s started\n"), rootdbpath); 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; { struct stat st; if ( !lstat( rootdbpath, &st ) ) { lchown( newrootdbpath, st.st_uid, st.st_gid ); chmod( newrootdbpath, st.st_mode ); } } rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"), _dbapi); _rebuildinprogress = 1; if (openDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644, RPMDB_FLAG_MINIMAL)) { rc = 1; goto exit; } _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); if (openDatabase(prefix, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) { rc = 1; goto exit; } _dbapi_rebuild = newdb->db_api; { Header h = NULL; rpmdbMatchIterator mi; #define _RECNUM rpmdbGetIteratorOffset(mi) /* RPMDBI_PACKAGES */ mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0); 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, _("record number %u in database is bad -- skipping.\n"), _RECNUM); 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)); 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; rpmMessage(RPMMESS_VERBOSE, _("rebuilding database %s finished\n"), rootdbpath); 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; } /*@=globs@*/ /*@=mods@*/ /*@=sizeoftype @*/