summaryrefslogtreecommitdiff
path: root/lib/rpmdb.c
diff options
context:
space:
mode:
authorPanu Matilainen <pmatilai@redhat.com>2008-05-12 12:17:01 +0300
committerPanu Matilainen <pmatilai@redhat.com>2008-05-12 12:17:01 +0300
commita68a5a049e98bc8406c0e7015ad5dadbed39972d (patch)
treee08c94133348a696e542bc120061845fa94a22b9 /lib/rpmdb.c
parent962c7b49d2c290678afad9a9893f76450e655108 (diff)
downloadrpm-a68a5a049e98bc8406c0e7015ad5dadbed39972d.tar.gz
rpm-a68a5a049e98bc8406c0e7015ad5dadbed39972d.tar.bz2
rpm-a68a5a049e98bc8406c0e7015ad5dadbed39972d.zip
Start (re-)joining librpmdb and librpm
- the split is largely arbitrary and mostly just causes, well, arbitrary issues and limitations - just move content from rpmdb/ to lib/ for now
Diffstat (limited to 'lib/rpmdb.c')
-rw-r--r--lib/rpmdb.c3525
1 files changed, 3525 insertions, 0 deletions
diff --git a/lib/rpmdb.c b/lib/rpmdb.c
new file mode 100644
index 000000000..72d86adee
--- /dev/null
+++ b/lib/rpmdb.c
@@ -0,0 +1,3525 @@
+/** \ingroup rpmdb dbi
+ * \file rpmdb/rpmdb.c
+ */
+
+#include "system.h"
+
+#define _USE_COPY_LOAD /* XXX don't use DB_DBT_MALLOC (yet) */
+
+#include <sys/file.h>
+
+#ifndef DYING /* XXX already in "system.h" */
+#include <fnmatch.h>
+#endif
+
+#include <regex.h>
+
+#include <rpm/rpmtag.h>
+#include <rpm/rpmurl.h>
+#include <rpm/rpmpgp.h>
+#include <rpm/rpmpgp.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmsq.h>
+#include <rpm/rpmstring.h>
+#include <rpm/rpmfileutil.h>
+#include <rpm/rpmds.h> /* XXX isInstallPreReq macro only */
+#include <rpm/rpmlog.h>
+#include <rpm/rpmdb.h>
+#include <rpm/argv.h>
+
+#include "rpmdb/rpmdb_internal.h"
+#include "rpmdb/fprint.h"
+#include "rpmdb/header_internal.h" /* XXX for HEADERFLAG_ALLOCATED */
+#include "debug.h"
+
+int _rpmdb_debug = 0;
+
+static int _rebuildinprogress = 0;
+static int _db_filter_dups = 0;
+
+#define _DBI_FLAGS 0
+#define _DBI_PERMS 0644
+#define _DBI_MAJOR -1
+
+struct dbiTags_s {
+ rpmTag * tags;
+ rpmTag max;
+ int nlink;
+};
+
+/* XXX should dbitags be per-db instead? */
+static struct dbiTags_s dbiTags = { NULL, 0, 0 };
+
+/* Bit mask macros. */
+typedef unsigned int __pbm_bits;
+#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))
+typedef struct {
+ __pbm_bits bits[1];
+} pbm_set;
+#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
+ */
+static inline pbm_set * PBM_REALLOC(pbm_set ** sp, int * odp, int nd)
+{
+ int i, nb;
+
+ if (nd > (*odp)) {
+ nd *= 2;
+ nb = __PBM_IX(nd) + 1;
+ *sp = xrealloc(*sp, nb * sizeof(__pbm_bits));
+ for (i = __PBM_IX(*odp) + 1; i < nb; i++)
+ __PBM_BITS(*sp)[i] = 0;
+ *odp = nd;
+ }
+ return *sp;
+}
+
+/**
+ * Return dbi index used for rpm tag.
+ * @param rpmtag rpm header tag
+ * @return dbi index, -1 on error
+ */
+static int dbiTagToDbix(rpmTag rpmtag)
+{
+ int dbix;
+
+ if (dbiTags.tags != NULL)
+ for (dbix = 0; dbix < dbiTags.max; dbix++) {
+ if (rpmtag == dbiTags.tags[dbix])
+ return dbix;
+ }
+ return -1;
+}
+
+/**
+ * Initialize database (index, tag) tuple from configuration.
+ */
+static void dbiTagsInit(void)
+{
+ 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;
+ rpmTag rpmtag;
+
+ dbiTags.nlink++;
+ if (dbiTags.tags != NULL && dbiTags.max > 0) {
+ return;
+ }
+
+ dbiTagStr = rpmExpand("%{?_dbi_tags}", NULL);
+ if (!(dbiTagStr && *dbiTagStr)) {
+ dbiTagStr = _free(dbiTagStr);
+ dbiTagStr = xstrdup(_dbiTagStr_default);
+ }
+
+ /* Discard previous values. */
+ dbiTags.tags = _free(dbiTags.tags);
+ dbiTags.max = 0;
+
+ /* Always allocate package index */
+ dbiTags.tags = xcalloc(1, sizeof(*dbiTags.tags));
+ dbiTags.tags[dbiTags.max++] = RPMDBI_PACKAGES;
+
+ for (o = dbiTagStr; o && *o; o = oe) {
+ while (*o && risspace(*o))
+ o++;
+ if (*o == '\0')
+ break;
+ for (oe = o; oe && *oe; oe++) {
+ if (risspace(*oe))
+ break;
+ if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
+ break;
+ }
+ if (oe && *oe)
+ *oe++ = '\0';
+ rpmtag = rpmTagGetValue(o);
+ if (rpmtag == RPMTAG_NOT_FOUND) {
+ rpmlog(RPMLOG_WARNING,
+ _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
+ continue;
+ }
+ if (dbiTagToDbix(rpmtag) >= 0)
+ continue;
+
+ dbiTags.tags = xrealloc(dbiTags.tags, (dbiTags.max + 1) * sizeof(*dbiTags.tags)); /* XXX memory leak */
+ dbiTags.tags[dbiTags.max++] = rpmtag;
+ }
+
+ dbiTagStr = _free(dbiTagStr);
+}
+
+static void dbiTagsFree(void)
+{
+ if (--dbiTags.nlink > 0) {
+ return;
+ }
+ dbiTags.tags = _free(dbiTags.tags);
+ dbiTags.max = 0;
+}
+
+#define DB1vec NULL
+#define DB2vec NULL
+
+#ifdef HAVE_DB_H
+extern struct _dbiVec db3vec;
+#define DB3vec &db3vec
+#else
+#define DB3vec NULL
+#endif
+
+#ifdef HAVE_SQLITE3_H
+extern struct _dbiVec sqlitevec;
+#define SQLITEvec &sqlitevec
+#else
+#define SQLITEvec NULL
+#endif
+
+static struct _dbiVec * const mydbvecs[] = {
+ DB1vec, DB1vec, DB2vec, DB3vec, SQLITEvec, NULL
+};
+
+dbiIndex dbiOpen(rpmdb db, rpmTag rpmtag, 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 >= dbiTags.max)
+ return NULL;
+
+ /* Is this index already open ? */
+ /* FIX: db->_dbi may be NULL */
+ if ((dbi = db->_dbi[dbix]) != NULL)
+ return dbi;
+
+ _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
+ if (_dbapi_rebuild < 1 || _dbapi_rebuild > 4)
+ _dbapi_rebuild = 4;
+/* _dbapi_wanted = (_rebuildinprogress ? -1 : db->db_api); */
+ _dbapi_wanted = (_rebuildinprogress ? _dbapi_rebuild : db->db_api);
+
+ switch (_dbapi_wanted) {
+ default:
+ _dbapi = _dbapi_wanted;
+ if (_dbapi < 0 || _dbapi >= 5 || mydbvecs[_dbapi] == NULL) {
+ rpmlog(RPMLOG_ERR, _("dbiOpen: dbapi %d not available\n"), _dbapi);
+ return NULL;
+ }
+ errno = 0;
+ dbi = NULL;
+ rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
+ if (rc) {
+ static int _printed[32];
+ if (!_printed[dbix & 0x1f]++)
+ rpmlog(RPMLOG_ERR,
+ _("cannot open %s index using db%d - %s (%d)\n"),
+ rpmTagGetName(rpmtag), _dbapi,
+ (rc > 0 ? strerror(rc) : ""), rc);
+ _dbapi = -1;
+ }
+ break;
+ case -1:
+ _dbapi = 5;
+ while (_dbapi-- > 1) {
+ if (mydbvecs[_dbapi] == NULL)
+ continue;
+ errno = 0;
+ dbi = NULL;
+ rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
+ if (rc == 0 && dbi)
+ break;
+ }
+ if (_dbapi <= 0) {
+ static int _printed[32];
+ if (!_printed[dbix & 0x1f]++)
+ rpmlog(RPMLOG_ERR, _("cannot open %s index\n"),
+ rpmTagGetName(rpmtag));
+ rc = 1;
+ goto exit;
+ }
+ if (db->db_api == -1 && _dbapi > 0)
+ db->db_api = _dbapi;
+ break;
+ }
+
+/* We don't ever _REQUIRE_ conversion... */
+#define SQLITE_HACK
+#ifdef SQLITE_HACK_XXX
+ /* 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;
+ }
+#endif
+
+exit:
+ if (dbi != NULL && rc == 0) {
+ db->_dbi[dbix] = dbi;
+ 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);
+ }
+ }
+#ifdef HAVE_DB_H
+ else
+ dbi = db3Free(dbi);
+#endif
+
+/* FIX: db->_dbi may be NULL */
+ 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 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, dbiIndexSet * 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)));
+
+ switch (dbi->dbi_jlen) {
+ default:
+ case 2*sizeof(int32_t):
+ 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(int32_t):
+ 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;
+ return 0;
+}
+
+/**
+ * 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)
+{
+ 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);
+
+ switch (dbi->dbi_jlen) {
+ default:
+ case 2*sizeof(int32_t):
+ 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(int32_t):
+ 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;
+ }
+
+ return 0;
+}
+
+/* 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)
+{
+ 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) {
+ memcpy(set->recs + set->count, rptr, rlen);
+ 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)
+{
+ 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;
+}
+
+typedef struct miRE_s {
+ rpmTag tag; /*!< header tag */
+ rpmMireMode mode; /*!< pattern match mode */
+ char * pattern; /*!< pattern string */
+ int notmatch; /*!< like "grep -v" */
+ 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_s {
+ rpmdbMatchIterator mi_next;
+ void * mi_keyp;
+ size_t mi_keylen;
+ rpmdb mi_db;
+ rpmTag mi_rpmtag;
+ dbiIndexSet mi_set;
+ DBC * mi_dbc;
+ DBT mi_key;
+ DBT mi_data;
+ int mi_setx;
+ Header mi_h;
+ int mi_sorted;
+ int mi_cflags;
+ int mi_modified;
+ unsigned int mi_prevoffset; /* header instance (native endian) */
+ unsigned int mi_offset; /* header instance (native endian) */
+ unsigned int mi_filenum; /* tag element (native endian) */
+ int mi_nre;
+ miRE mi_re;
+ rpmts mi_ts;
+ rpmRC (*mi_hdrchk) (rpmts ts, const void * uh, size_t uc, char ** msg);
+
+};
+
+static rpmdb rpmdbRock;
+
+static rpmdbMatchIterator rpmmiRock;
+
+int rpmdbCheckTerminate(int terminate)
+{
+ sigset_t newMask, oldMask;
+ static int terminating = 0;
+
+ if (terminating) return 0;
+
+ (void) sigfillset(&newMask); /* block all signals */
+ (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
+
+ if (sigismember(&rpmsqCaught, SIGINT)
+ || sigismember(&rpmsqCaught, SIGQUIT)
+ || sigismember(&rpmsqCaught, SIGHUP)
+ || sigismember(&rpmsqCaught, SIGTERM)
+ || sigismember(&rpmsqCaught, SIGPIPE)
+ || terminate)
+ terminating = 1;
+
+ if (terminating) {
+ rpmdb db;
+ rpmdbMatchIterator mi;
+
+ while ((mi = rpmmiRock) != NULL) {
+ rpmmiRock = mi->mi_next;
+ mi->mi_next = NULL;
+ mi = rpmdbFreeIterator(mi);
+ }
+
+ while ((db = rpmdbRock) != NULL) {
+ rpmdbRock = db->db_next;
+ db->db_next = NULL;
+ (void) rpmdbClose(db);
+ }
+ }
+ sigprocmask(SIG_SETMASK, &oldMask, NULL);
+ return terminating;
+}
+
+int rpmdbCheckSignals(void)
+{
+ if (rpmdbCheckTerminate(0)) {
+ /* sigset_t is abstract type */
+ rpmlog(RPMLOG_DEBUG, "Exiting on signal(0x%lx) ...\n",
+ *((unsigned long *)&rpmsqCaught));
+ exit(EXIT_FAILURE);
+ }
+ return 0;
+}
+
+/**
+ * Block all signals, returning previous signal mask.
+ */
+static int blockSignals(sigset_t * oldMask)
+{
+ sigset_t newMask;
+
+ (void) sigfillset(&newMask); /* block all signals */
+ (void) sigprocmask(SIG_BLOCK, &newMask, oldMask);
+ (void) sigdelset(&newMask, SIGINT);
+ (void) sigdelset(&newMask, SIGQUIT);
+ (void) sigdelset(&newMask, SIGHUP);
+ (void) sigdelset(&newMask, SIGTERM);
+ (void) sigdelset(&newMask, SIGPIPE);
+ return sigprocmask(SIG_BLOCK, &newMask, NULL);
+}
+
+/**
+ * Restore signal mask.
+ */
+static int unblockSignals(sigset_t * oldMask)
+{
+ (void) rpmdbCheckSignals();
+ 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"
+
+static struct rpmdb_s const dbTemplate = {
+ _DB_ROOT, _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
+ _DB_MAJOR, _DB_ERRPFX
+};
+
+static int isTemporaryDB(rpmTag rpmtag)
+{
+ int rc = 0;
+ switch (rpmtag) {
+ case RPMDBI_AVAILABLE:
+ case RPMDBI_ADDED:
+ case RPMDBI_REMOVED:
+ case RPMDBI_DEPENDS:
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+ return rc;
+}
+
+rpmop rpmdbOp(rpmdb rpmdb, rpmdbOpX opx)
+{
+ rpmop op = NULL;
+ switch (opx) {
+ case RPMDB_OP_DBGET:
+ op = &rpmdb->db_getops;
+ break;
+ case RPMDB_OP_DBPUT:
+ op = &rpmdb->db_putops;
+ break;
+ case RPMDB_OP_DBDEL:
+ op = &rpmdb->db_delops;
+ break;
+ default:
+ break;
+ }
+ return op;
+}
+
+int rpmdbSetChrootDone(rpmdb db, int chrootDone)
+{
+ int ochrootDone = 0;
+ if (db != NULL) {
+ ochrootDone = db->db_chrootDone;
+ db->db_chrootDone = chrootDone;
+ }
+ return ochrootDone;
+}
+
+int rpmdbOpenAll(rpmdb db)
+{
+ int dbix;
+ int rc = 0;
+
+ if (db == NULL) return -2;
+
+ if (dbiTags.tags != NULL)
+ for (dbix = 0; dbix < dbiTags.max; dbix++) {
+ if (db->_dbi[dbix] != NULL)
+ continue;
+ /* Filter out temporary databases */
+ if (isTemporaryDB(dbiTags.tags[dbix]))
+ continue;
+ (void) dbiOpen(db, dbiTags.tags[dbix], db->db_flags);
+ }
+ return rc;
+}
+
+int rpmdbCloseDBI(rpmdb db, rpmTag rpmtag)
+{
+ int dbix;
+ int rc = 0;
+
+ if (db == NULL || db->_dbi == NULL || dbiTags.tags == NULL)
+ return 0;
+
+ for (dbix = 0; dbix < dbiTags.max; dbix++) {
+ if (dbiTags.tags[dbix] != rpmtag)
+ continue;
+ if (db->_dbi[dbix] != NULL) {
+ int xx;
+ /* FIX: double indirection. */
+ xx = dbiClose(db->_dbi[dbix], 0);
+ if (xx && rc == 0) rc = xx;
+ db->_dbi[dbix] = NULL;
+ }
+ break;
+ }
+ return rc;
+}
+
+/* XXX query.c, rpminstall.c, verify.c */
+int rpmdbClose(rpmdb db)
+{
+ rpmdb * prev, next;
+ int dbix;
+ int rc = 0;
+
+ if (db == NULL)
+ goto exit;
+
+ (void) rpmdbUnlink(db, RPMDBG_M("rpmdbClose"));
+
+ if (db->nrefs > 0)
+ goto exit;
+
+ if (db->_dbi)
+ for (dbix = db->db_ndbi; --dbix >= 0; ) {
+ int xx;
+ if (db->_dbi[dbix] == NULL)
+ continue;
+ xx = dbiClose(db->_dbi[dbix], 0);
+ if (xx && rc == 0) rc = xx;
+ db->_dbi[dbix] = NULL;
+ }
+ 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);
+
+ prev = &rpmdbRock;
+ while ((next = *prev) != NULL && next != db)
+ prev = &next->db_next;
+ if (next) {
+ *prev = next->db_next;
+ next->db_next = NULL;
+ }
+
+ db = _free(db);
+
+ dbiTagsFree();
+
+exit:
+ (void) rpmsqEnable(-SIGHUP, NULL);
+ (void) rpmsqEnable(-SIGINT, NULL);
+ (void) rpmsqEnable(-SIGTERM,NULL);
+ (void) rpmsqEnable(-SIGQUIT,NULL);
+ (void) rpmsqEnable(-SIGPIPE,NULL);
+ 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;
+ if (db->_dbi[dbix]->dbi_no_dbsync)
+ continue;
+ xx = dbiSync(db->_dbi[dbix], 0);
+ if (xx && rc == 0) rc = xx;
+ }
+ return rc;
+}
+
+/* FIX: dbTemplate structure assignment */
+static
+rpmdb newRpmdb(const char * root,
+ const char * home,
+ int mode, int perms, int flags)
+{
+ rpmdb db = xcalloc(sizeof(*db), 1);
+ const char * epfx = _DB_ERRPFX;
+ static int _initialized = 0;
+
+ if (!_initialized) {
+ _db_filter_dups = rpmExpandNumeric("%{_filterdbdups}");
+ _initialized = 1;
+ }
+
+ *db = dbTemplate; /* structure assignment */
+
+ 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;
+
+ if (root && *root) {
+ db->db_root = rpmGetPath(root, NULL);
+ } else
+ db->db_root = rpmGetPath(_DB_ROOT, NULL);
+
+ db->db_home = rpmGetPath( (home && *home ? home : _DB_HOME), NULL);
+ if (!(db->db_home && db->db_home[0] != '%')) {
+ rpmlog(RPMLOG_ERR, _("no dbpath has been set\n"));
+ db->db_root = _free(db->db_root);
+ db->db_home = _free(db->db_home);
+ db = _free(db);
+ return NULL;
+ }
+ 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 = dbiTags.max;
+ db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
+ db->nrefs = 0;
+ return rpmdbLink(db, RPMDBG_M("rpmdbCreate"));
+}
+
+static int openDatabase(const char * prefix,
+ const char * dbpath,
+ int _dbapi, rpmdb *dbp,
+ int mode, int perms, int flags)
+{
+ rpmdb db;
+ int rc, xx;
+ int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
+ int minimal = flags & RPMDB_FLAG_MINIMAL;
+
+ dbiTagsInit();
+
+ /* Insure that _dbapi has one of -1, 1, 2, or 3 */
+ if (_dbapi < -1 || _dbapi > 4)
+ _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) rpmsqEnable(SIGHUP, NULL);
+ (void) rpmsqEnable(SIGINT, NULL);
+ (void) rpmsqEnable(SIGTERM,NULL);
+ (void) rpmsqEnable(SIGQUIT,NULL);
+ (void) rpmsqEnable(SIGPIPE,NULL);
+
+ db->db_api = _dbapi;
+
+ { int dbix;
+
+ rc = 0;
+ if (dbiTags.tags != NULL)
+ for (dbix = 0; rc == 0 && dbix < dbiTags.max; dbix++) {
+ dbiIndex dbi;
+ rpmTag rpmtag;
+
+ /* Filter out temporary databases */
+ if (isTemporaryDB((rpmtag = dbiTags.tags[dbix])))
+ continue;
+
+ 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;
+ break;
+ case RPMTAG_NAME:
+ if (dbi == NULL) rc |= 1;
+ if (minimal)
+ goto exit;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+exit:
+ if (rc || justCheck || dbp == NULL)
+ xx = rpmdbClose(db);
+ else {
+ db->db_next = rpmdbRock;
+ rpmdbRock = db;
+ *dbp = db;
+ }
+
+ return rc;
+}
+
+rpmdb rpmdbUnlink(rpmdb db, const char * msg)
+{
+if (_rpmdb_debug)
+fprintf(stderr, "--> db %p -- %d %s\n", db, db->nrefs, msg);
+ db->nrefs--;
+ return NULL;
+}
+
+rpmdb rpmdbLink(rpmdb db, const char * msg)
+{
+ db->nrefs++;
+if (_rpmdb_debug)
+fprintf(stderr, "--> db %p ++ %d %s\n", db, db->nrefs, msg);
+ return db;
+}
+
+/* XXX python/rpmmodule.c */
+int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
+{
+ int _dbapi = rpmExpandNumeric("%{_dbapi}");
+ return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
+}
+
+int rpmdbInit (const char * prefix, int perms)
+{
+ rpmdb db = NULL;
+ int _dbapi = rpmExpandNumeric("%{_dbapi}");
+ 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;
+ int _dbapi = rpmExpandNumeric("%{_dbapi}");
+ int rc = 0;
+
+ rc = openDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0);
+
+ if (db != NULL) {
+ int dbix;
+ int xx;
+ rc = rpmdbOpenAll(db);
+
+ for (dbix = db->db_ndbi; --dbix >= 0; ) {
+ if (db->_dbi[dbix] == NULL)
+ continue;
+ /* FIX: double indirection. */
+ xx = dbiVerify(db->_dbi[dbix], 0);
+ if (xx && rc == 0) rc = xx;
+ db->_dbi[dbix] = NULL;
+ }
+
+ /* FIX: db->_dbi[] may be NULL. */
+ xx = rpmdbClose(db);
+ 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, const char * filespec,
+ DBT * key, DBT * data, dbiIndexSet * matches)
+{
+ HGE_t hge = (HGE_t)headerGetEntryMinMemory;
+ HFD_t hfd = headerFreeData;
+ 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;
+
+ if ((baseName = strrchr(filespec, '/')) != NULL) {
+ size_t len = baseName - filespec + 1;
+ dirName = strncpy(xmalloc(len + 1), filespec, len);
+ dirName[len] = '\0';
+ baseName++;
+ } else {
+ dirName = xstrdup("");
+ baseName = filespec;
+ }
+ if (baseName == NULL)
+ return -2;
+
+ fpc = fpCacheCreate(20);
+ fp1 = fpLookup(fpc, dirName, baseName, 1);
+ free(dirName);
+
+ dbi = dbiOpen(db, RPMTAG_BASENAMES, 0);
+ if (dbi != NULL) {
+ dbcursor = NULL;
+ xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
+
+ key->data = (void *) baseName;
+ key->size = strlen(baseName);
+ if (key->size == 0)
+ key->size++; /* XXX "/" fixup. */
+
+ rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
+ if (rc > 0) {
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) getting \"%s\" records from %s index\n"),
+ rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
+ }
+
+ if (rc == 0)
+ (void) dbt2set(dbi, data, &allMatches);
+
+ xx = dbiCclose(dbi, dbcursor, 0);
+ dbcursor = NULL;
+ } else
+ rc = -2;
+
+ 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;
+ uint32_t * 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, (rpm_data_t *) &baseNames, NULL);
+ xx = hge(h, RPMTAG_DIRNAMES, &dnt, (rpm_data_t *) &dirNames, NULL);
+ xx = hge(h, RPMTAG_DIRINDEXES, NULL, (rpm_data_t *) &dirIndexes, NULL);
+
+ do {
+ fingerPrint fp2;
+ int num = dbiIndexRecordFileNumber(allMatches, i);
+
+ fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
+ if (FP_EQUAL(fp1, fp2)) {
+ rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
+ rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
+ xx = dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
+ }
+
+ prevoff = offset;
+ i++;
+ if (i < allMatches->count)
+ offset = dbiIndexRecordOffset(allMatches, i);
+ } while (i < allMatches->count && 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;
+ DBT 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;
+
+ key.data = (void *) name;
+ key.size = strlen(name);
+
+ xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
+ rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
+#ifndef SQLITE_HACK
+ xx = dbiCclose(dbi, dbcursor, 0);
+ dbcursor = NULL;
+#endif
+
+ if (rc == 0) { /* success */
+ dbiIndexSet matches;
+ /* FIX: matches might be NULL */
+ matches = NULL;
+ (void) dbt2set(dbi, &data, &matches);
+ if (matches) {
+ rc = dbiIndexSetCount(matches);
+ matches = dbiFreeIndexSet(matches);
+ }
+ } else
+ if (rc == DB_NOTFOUND) { /* not found */
+ rc = 0;
+ } else { /* error */
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) getting \"%s\" records from %s index\n"),
+ rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
+ rc = -1;
+ }
+
+#ifdef SQLITE_HACK
+ xx = dbiCclose(dbi, dbcursor, 0);
+ dbcursor = NULL;
+#endif
+
+ 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,
+ const char * version,
+ const char * release,
+ dbiIndexSet * matches)
+{
+ int gotMatches = 0;
+ int rc;
+ int i;
+
+ key->data = (void *) name;
+ 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 */
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) getting \"%s\" records from %s index\n"),
+ rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
+ return RPMRC_FAIL;
+ }
+
+ /* Make sure the version and release match. */
+ 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);
+ if (h)
+ (*matches)->recs[gotMatches++] = (*matches)->recs[i];
+ else
+ (*matches)->recs[i].hdrNum = 0;
+ mi = rpmdbFreeIterator(mi);
+ }
+
+ if (gotMatches) {
+ (*matches)->count = gotMatches;
+ rc = RPMRC_OK;
+ } else
+ rc = RPMRC_NOTFOUND;
+
+exit:
+/* FIX: double indirection */
+ if (rc && matches && *matches)
+ *matches = dbiFreeIndexSet(*matches);
+ 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,
+ const char * arg, dbiIndexSet * matches)
+{
+ 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;
+
+ /* FIX: double indirection */
+ *matches = dbiFreeIndexSet(*matches);
+
+ /* maybe a name and a release */
+ localarg = xmalloc(strlen(arg) + 1);
+ s = stpcpy(localarg, arg);
+
+ c = '\0';
+ brackets = 0;
+ for (s -= 1; s > localarg; s--) {
+ switch (*s) {
+ case '[':
+ brackets = 1;
+ break;
+ case ']':
+ if (c != '[') brackets = 0;
+ break;
+ }
+ c = *s;
+ if (!brackets && *s == '-')
+ break;
+ }
+
+ /* FIX: *matches may be NULL. */
+ if (s == localarg) {
+ rc = RPMRC_NOTFOUND;
+ goto exit;
+ }
+
+ *s = '\0';
+ rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, NULL, matches);
+ if (rc != RPMRC_NOTFOUND) goto exit;
+
+ /* FIX: double indirection */
+ *matches = dbiFreeIndexSet(*matches);
+
+ /* how about name-version-release? */
+
+ release = s + 1;
+
+ c = '\0';
+ brackets = 0;
+ for (; s > localarg; s--) {
+ switch (*s) {
+ case '[':
+ brackets = 1;
+ break;
+ case ']':
+ if (c != '[') brackets = 0;
+ break;
+ }
+ c = *s;
+ if (!brackets && *s == '-')
+ break;
+ }
+
+ if (s == localarg) {
+ rc = RPMRC_NOTFOUND;
+ goto exit;
+ }
+
+ *s = '\0';
+ /* FIX: *matches may be NULL. */
+ rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, release, matches);
+exit:
+ free(localarg);
+ return rc;
+}
+
+/**
+ * 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)
+{
+ 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;
+
+ 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) {
+ char * msg = NULL;
+ int lvl;
+
+ rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, data->data, data->size, &msg);
+ lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
+ rpmlog(lvl, "%s h#%8u %s",
+ (rpmrc == RPMRC_FAIL ? _("miFreeHeader: skipping") : "write"),
+ mi->mi_prevoffset, (msg ? msg : "\n"));
+ msg = _free(msg);
+ }
+
+ if (data->data != NULL && rpmrc != RPMRC_FAIL) {
+ (void) blockSignals(&signalMask);
+ rc = dbiPut(dbi, mi->mi_dbc, key, data, DB_KEYLAST);
+ if (rc) {
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) storing record #%d into %s\n"),
+ rc, mi->mi_prevoffset, rpmTagGetName(dbi->dbi_rpmtag));
+ }
+ xx = dbiSync(dbi, 0);
+ (void) unblockSignals(&signalMask);
+ }
+ data->data = _free(data->data);
+ data->size = 0;
+ }
+
+ mi->mi_h = headerFree(mi->mi_h);
+
+ return rc;
+}
+
+rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
+{
+ rpmdbMatchIterator * prev, next;
+ dbiIndex dbi;
+ int xx;
+ int i;
+
+ if (mi == NULL)
+ return NULL;
+
+ prev = &rpmmiRock;
+ while ((next = *prev) != NULL && next != mi)
+ prev = &next->mi_next;
+ if (next) {
+ *prev = next->mi_next;
+ next->mi_next = 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);
+ /* LCL: regfree has bogus only */
+ mire->preg = _free(mire->preg);
+ }
+ }
+ 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, RPMDBG_M("matchIterator"));
+
+ mi = _free(mi);
+
+ (void) rpmdbCheckSignals();
+
+ 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:
+ rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
+ if (rc && rc != REG_NOMATCH) {
+ char msg[256];
+ (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
+ msg[sizeof(msg)-1] = '\0';
+ rpmlog(RPMLOG_ERR, _("%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 char * mireDup(rpmTag tag, rpmMireMode *modep,
+ const char * pattern)
+{
+ 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("^$");
+
+ /* Find no. of bytes needed for pattern. */
+ /* periods and plusses are escaped, splats become '.*' */
+ c = '\0';
+ brackets = 0;
+ for (s = pattern; *s != '\0'; s++) {
+ switch (*s) {
+ case '.':
+ case '+':
+ case '*':
+ if (!brackets) nb++;
+ break;
+ case '\\':
+ s++;
+ break;
+ case '[':
+ brackets = 1;
+ break;
+ case ']':
+ if (c != '[') brackets = 0;
+ 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 '.':
+ case '+':
+ if (!brackets) *t++ = '\\';
+ break;
+ case '*':
+ if (!brackets) *t++ = '.';
+ break;
+ case '\\':
+ *t++ = *s++;
+ break;
+ case '[':
+ brackets = 1;
+ break;
+ case ']':
+ if (c != '[') brackets = 0;
+ 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;
+ }
+
+ return pat;
+}
+
+int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
+ rpmMireMode mode, const char * pattern)
+{
+ static rpmMireMode defmode = (rpmMireMode)-1;
+ miRE mire = NULL;
+ 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) {
+ 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++;
+ }
+
+ allpat = mireDup(tag, &mode, pattern);
+
+ if (mode == RPMMIRE_DEFAULT)
+ mode = defmode;
+
+ switch (mode) {
+ case RPMMIRE_DEFAULT:
+ case RPMMIRE_STRCMP:
+ break;
+ case RPMMIRE_REGEX:
+ preg = xcalloc(1, sizeof(*preg));
+ 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';
+ rpmlog(RPMLOG_ERR, _("%s: regcomp failed: %s\n"), allpat, msg);
+ }
+ break;
+ case RPMMIRE_GLOB:
+ fnflags = FNM_PATHNAME | FNM_PERIOD;
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+
+ if (rc) {
+ /* FIX: mire has kept values */
+ allpat = _free(allpat);
+ if (preg) {
+ regfree(preg);
+ /* LCL: regfree has bogus only */
+ preg = _free(preg);
+ }
+ 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;
+
+ if (mi->mi_nre > 1)
+ qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
+
+ 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;
+ int32_t * i32p;
+ int16_t * i16p;
+ int8_t * i8p;
+ } u;
+ char numbuf[32];
+ rpmTagType t;
+ rpm_count_t c;
+ miRE mire;
+ static int32_t 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.
+ */
+ 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, (rpm_data_t *)&u, &c)) {
+ if (mire->tag != RPMTAG_EPOCH) {
+ ntags++;
+ continue;
+ }
+ t = RPM_INT32_TYPE;
+ u.i32p = &zero;
+ 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++;
+ break;
+ case RPM_INT16_TYPE:
+ sprintf(numbuf, "%d", (int) *u.i16p);
+ rc = miregexec(mire, numbuf);
+ if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
+ anymatch++;
+ break;
+ case RPM_INT32_TYPE:
+ sprintf(numbuf, "%d", (int) *u.i32p);
+ rc = miregexec(mire, numbuf);
+ if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
+ anymatch++;
+ break;
+ case RPM_STRING_TYPE:
+ rc = miregexec(mire, u.str);
+ if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
+ anymatch++;
+ 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++;
+ break;
+ }
+ }
+ break;
+ case RPM_BIN_TYPE:
+ {
+ char * str = pgpHexStr((const unsigned char*) u.ptr, c);
+ rc = miregexec(mire, str);
+ if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
+ anymatch++;
+ _free(str);
+ }
+ break;
+ case RPM_NULL_TYPE:
+ default:
+ break;
+ }
+ if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
+ i++;
+ mire++;
+ continue;
+ }
+ break;
+ }
+
+ if ((rpmTagGetType(mire->tag) & RPM_MASK_RETURN_TYPE) ==
+ RPM_ARRAY_RETURN_TYPE) {
+ 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, char ** msg))
+{
+ int rc = 0;
+ if (mi == NULL)
+ return 0;
+ /* XXX forward linkage prevents rpmtsLink */
+ mi->mi_ts = ts;
+ mi->mi_hdrchk = hdrchk;
+ return rc;
+}
+
+
+/* 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;
+
+ 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);
+
+ key = &mi->mi_key;
+ memset(key, 0, sizeof(*key));
+ data = &mi->mi_data;
+ memset(data, 0, sizeof(*data));
+
+top:
+ uh = NULL;
+ uhlen = 0;
+
+ do {
+ union _dbswap mi_offset;
+
+ 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);
+ mi_offset.ui = mi->mi_offset;
+ if (dbiByteSwapped(dbi) == 1)
+ _DBSWAP(mi_offset);
+ keyp = &mi_offset;
+ keylen = sizeof(mi_offset.ui);
+ } 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.
+ */
+ if (keyp && mi->mi_setx && rc == 0) {
+ memcpy(&mi_offset, keyp, sizeof(mi_offset.ui));
+ if (dbiByteSwapped(dbi) == 1)
+ _DBSWAP(mi_offset);
+ mi->mi_offset = mi_offset.ui;
+ }
+
+ /* 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 next header is identical, return it now. */
+ if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
+ return mi->mi_h;
+
+ /* Retrieve next header blob for index iterator. */
+ 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;
+ }
+
+ /* 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). */
+ if (mi->mi_hdrchk && mi->mi_ts) {
+ rpmRC rpmrc = RPMRC_NOTFOUND;
+
+ /* Don't bother re-checking a previously read header. */
+ if (mi->mi_db->db_bits) {
+ pbm_set * set;
+
+ 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) {
+ char * msg = NULL;
+ int lvl;
+
+ rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg);
+ lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
+ rpmlog(lvl, "%s h#%8u %s",
+ (rpmrc == RPMRC_FAIL ? _("rpmdbNextIterator: skipping") : " read"),
+ mi->mi_offset, (msg ? msg : "\n"));
+ msg = _free(msg);
+
+ /* Mark header checked. */
+ if (mi->mi_db && mi->mi_db->db_bits && rpmrc == RPMRC_OK) {
+ pbm_set * set;
+
+ set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
+ &mi->mi_db->db_nbits, mi->mi_offset);
+ PBM_SET(mi->mi_offset, set);
+ }
+
+ /* Skip damaged and inconsistent headers. */
+ if (rpmrc == RPMRC_FAIL)
+ goto top;
+ }
+ }
+
+ /* Did the header blob load correctly? */
+#if !defined(_USE_COPY_LOAD)
+ mi->mi_h = headerLoad(uh);
+ 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)) {
+ rpmlog(RPMLOG_ERR,
+ _("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;
+
+ return mi->mi_h;
+}
+
+static void rpmdbSortIterator(rpmdbMatchIterator 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;
+ }
+}
+
+/* LCL: segfault */
+static int rpmdbGrowIterator(rpmdbMatchIterator mi, int fpNum)
+{
+ 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);
+#ifndef SQLITE_HACK
+ xx = dbiCclose(dbi, dbcursor, 0);
+ dbcursor = NULL;
+#endif
+
+ if (rc) { /* error/not found */
+ if (rc != DB_NOTFOUND)
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) getting \"%s\" records from %s index\n"),
+ rc, (char*)key->data, rpmTagGetName(dbi->dbi_rpmtag));
+#ifdef SQLITE_HACK
+ xx = dbiCclose(dbi, dbcursor, 0);
+ dbcursor = NULL;
+#endif
+ return rc;
+ }
+
+ set = NULL;
+ (void) dbt2set(dbi, data, &set);
+ for (i = 0; i < set->count; i++)
+ set->recs[i].fpNum = fpNum;
+
+#ifdef SQLITE_HACK
+ xx = dbiCclose(dbi, dbcursor, 0);
+ dbcursor = NULL;
+#endif
+
+ 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);
+ }
+
+ 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, rpmTag rpmtag,
+ const void * keyp, size_t keylen)
+{
+ rpmdbMatchIterator mi;
+ DBT * key;
+ DBT * data;
+ dbiIndexSet set = NULL;
+ dbiIndex dbi;
+ void * mi_keyp = NULL;
+ int isLabel = 0;
+
+ if (db == NULL)
+ return NULL;
+
+ (void) rpmdbCheckSignals();
+
+ /* 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;
+
+ /* Chain cursors for teardown on abnormal exit. */
+ mi = xcalloc(1, sizeof(*mi));
+ mi->mi_next = rpmmiRock;
+ rpmmiRock = mi;
+
+ key = &mi->mi_key;
+ data = &mi->mi_data;
+
+ /*
+ * Handle label and file name special cases.
+ * Otherwise, retrieve join keys for secondary lookup.
+ */
+ if (rpmtag != RPMDBI_PACKAGES && keyp) {
+ DBC * dbcursor = NULL;
+ int rc;
+ int xx;
+
+ if (isLabel) {
+ 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);
+
+ key->data = (void *) keyp;
+ 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. */
+
+ rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
+ if (rc > 0) {
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) getting \"%s\" records from %s index\n"),
+ rc, (key->data ? (char *)key->data : "???"),
+ rpmTagGetName(dbi->dbi_rpmtag));
+ }
+
+ /* Join keys need to be native endian internally. */
+ if (rc == 0)
+ (void) dbt2set(dbi, data, &set);
+
+ xx = dbiCclose(dbi, dbcursor, 0);
+ dbcursor = NULL;
+ }
+ if (rc) { /* error/not found */
+ set = dbiFreeIndexSet(set);
+ rpmmiRock = mi->mi_next;
+ mi->mi_next = NULL;
+ mi = _free(mi);
+ return NULL;
+ }
+ }
+
+ /* Copy the retrieval key, byte swapping header instance if necessary. */
+ if (keyp) {
+ switch (rpmtag) {
+ case RPMDBI_PACKAGES:
+ { union _dbswap *k;
+
+ assert(keylen == sizeof(k->ui)); /* xxx programmer error */
+ k = xmalloc(sizeof(*k));
+ memcpy(k, keyp, keylen);
+ if (dbiByteSwapped(dbi) == 1)
+ _DBSWAP(*k);
+ mi_keyp = k;
+ } break;
+ default:
+ { char * k;
+ if (keylen == 0)
+ keylen = strlen(keyp);
+ k = xmalloc(keylen + 1);
+ memcpy(k, keyp, keylen);
+ k[keylen] = '\0'; /* XXX assumes strings */
+ mi_keyp = k;
+ } break;
+ }
+ }
+
+ mi->mi_keyp = mi_keyp;
+ mi->mi_keylen = keylen;
+
+ mi->mi_db = rpmdbLink(db, RPMDBG_M("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;
+
+ return mi;
+}
+
+/* XXX psm.c */
+int rpmdbRemove(rpmdb db, int rid, unsigned int hdrNum,
+ rpmts ts,
+ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
+{
+ DBC * dbcursor = NULL;
+ DBT key;
+ DBT data;
+ union _dbswap mi_offset;
+ 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) {
+ rpmlog(RPMLOG_ERR, _("%s: cannot read header at 0x%x\n"),
+ "rpmdbRemove", hdrNum);
+ return 1;
+ }
+
+ {
+ char *nevra = headerGetNEVRA(h, NULL);
+ rpmlog(RPMLOG_DEBUG, " --- h#%8u %s\n", hdrNum, nevra);
+ free(nevra);
+ }
+
+ (void) blockSignals(&signalMask);
+
+ /* FIX: rpmvals heartburn */
+ { int dbix;
+ dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
+
+ if (dbiTags.tags != NULL)
+ for (dbix = 0; dbix < dbiTags.max; dbix++) {
+ dbiIndex dbi;
+ const char *av[1];
+ const char ** rpmvals = NULL;
+ rpmTagType rpmtype = 0;
+ rpm_count_t rpmcnt = 0;
+ rpmTag rpmtag;
+ int xx;
+ rpm_count_t i, j;
+
+ dbi = NULL;
+ rpmtag = dbiTags.tags[dbix];
+
+ /* Filter out temporary databases */
+ if (isTemporaryDB(rpmtag))
+ continue;
+
+ switch ((rpm_tag_t) rpmtag) {
+ case RPMDBI_PACKAGES:
+ dbi = dbiOpen(db, rpmtag, 0);
+ if (dbi == NULL) /* XXX shouldn't happen */
+ continue;
+
+ mi_offset.ui = hdrNum;
+ if (dbiByteSwapped(dbi) == 1)
+ _DBSWAP(mi_offset);
+ key.data = &mi_offset;
+ key.size = sizeof(mi_offset.ui);
+
+ rc = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
+ rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
+ if (rc) {
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) setting header #%d record for %s removal\n"),
+ rc, hdrNum, rpmTagGetName(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;
+ break;
+ }
+
+ if (!hge(h, rpmtag, &rpmtype, (rpm_data_t *) &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);
+ for (i = 0; i < rpmcnt; i++) {
+ dbiIndexSet set;
+ int stringvalued;
+ uint8_t bin[32];
+
+ switch (dbi->dbi_rpmtag) {
+ case RPMTAG_FILEMD5S:
+ /* Filter out empty MD5 strings. */
+ if (!(rpmvals[i] && *rpmvals[i] != '\0'))
+ continue;
+ break;
+ default:
+ break;
+ }
+
+ /* Identify value pointer and length. */
+ stringvalued = 0;
+ switch (rpmtype) {
+ case RPM_CHAR_TYPE:
+ case RPM_INT8_TYPE:
+ key.size = sizeof(RPM_CHAR_TYPE);
+ key.data = rpmvals + i;
+ break;
+ case RPM_INT16_TYPE:
+ key.size = sizeof(int16_t);
+ key.data = rpmvals + i;
+ break;
+ case RPM_INT32_TYPE:
+ key.size = sizeof(int32_t);
+ key.data = rpmvals + i;
+ break;
+ case RPM_BIN_TYPE:
+ key.size = rpmcnt;
+ key.data = rpmvals;
+ rpmcnt = 1; /* XXX break out of loop. */
+ break;
+ case RPM_STRING_TYPE:
+ case RPM_I18NSTRING_TYPE:
+ rpmcnt = 1; /* XXX break out of loop. */
+ case RPM_STRING_ARRAY_TYPE:
+ /* Convert from hex to binary. */
+ if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) {
+ const char * s;
+ uint8_t * t;
+
+ s = rpmvals[i];
+ t = bin;
+ for (j = 0; j < 16; j++, t++, s += 2)
+ *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
+ key.data = bin;
+ key.size = 16;
+ break;
+ }
+ /* Extract the pubkey id from the base64 blob. */
+ if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
+ int nbin = pgpExtractPubkeyFingerprint(rpmvals[i], bin);
+ if (nbin <= 0)
+ continue;
+ key.data = bin;
+ key.size = nbin;
+ break;
+ }
+ default:
+ key.data = (void *) rpmvals[i];
+ key.size = strlen(rpmvals[i]);
+ stringvalued = 1;
+ break;
+ }
+
+ if (!printed) {
+ if (rpmcnt == 1 && stringvalued) {
+ rpmlog(RPMLOG_DEBUG,
+ "removing \"%s\" from %s index.\n",
+ (char *)key.data, rpmTagGetName(dbi->dbi_rpmtag));
+ } else {
+ rpmlog(RPMLOG_DEBUG,
+ "removing %d entries from %s index.\n",
+ rpmcnt, rpmTagGetName(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. */
+
+ rc = dbiGet(dbi, dbcursor, &key, &data, DB_SET);
+ if (rc == 0) { /* success */
+ (void) dbt2set(dbi, &data, &set);
+ } else if (rc == DB_NOTFOUND) { /* not found */
+ continue;
+ } else { /* error */
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) setting \"%s\" records from %s index\n"),
+ rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
+ ret += 1;
+ continue;
+ }
+
+ rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
+
+ /* If nothing was pruned, then don't bother updating. */
+ if (rc) {
+ set = dbiFreeIndexSet(set);
+ continue;
+ }
+
+ if (set->count > 0) {
+ (void) set2dbt(dbi, &data, set);
+ rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
+ if (rc) {
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) storing record \"%s\" into %s\n"),
+ rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
+ ret += 1;
+ }
+ data.data = _free(data.data);
+ data.size = 0;
+ } else {
+ rc = dbiDel(dbi, dbcursor, &key, &data, 0);
+ if (rc) {
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) removing record \"%s\" from %s\n"),
+ rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
+ ret += 1;
+ }
+ }
+ set = dbiFreeIndexSet(set);
+ }
+
+ 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);
+ }
+
+ (void) unblockSignals(&signalMask);
+
+ h = headerFree(h);
+
+ /* XXX return ret; */
+ return 0;
+}
+
+/* XXX install.c */
+int rpmdbAdd(rpmdb db, int iid, Header h,
+ rpmts ts,
+ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
+{
+ DBC * dbcursor = NULL;
+ DBT key;
+ DBT data;
+ HGE_t hge = (HGE_t)headerGetEntryMinMemory;
+ HFD_t hfd = headerFreeData;
+ sigset_t signalMask;
+ const char ** baseNames;
+ rpmTagType bnt;
+ rpm_count_t count = 0;
+ dbiIndex dbi;
+ int dbix;
+ union _dbswap mi_offset;
+ 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) {
+ rpm_tid_t 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, (rpm_data_t *) &baseNames, &count);
+
+ (void) blockSignals(&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);
+ 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. */
+
+ key.data = keyp;
+ key.size = keylen;
+ 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;
+
+ hdrNum = 0;
+ if (ret == 0 && datap) {
+ memcpy(&mi_offset, datap, sizeof(mi_offset.ui));
+ if (dbiByteSwapped(dbi) == 1)
+ _DBSWAP(mi_offset);
+ hdrNum = mi_offset.ui;
+ }
+ ++hdrNum;
+ mi_offset.ui = hdrNum;
+ if (dbiByteSwapped(dbi) == 1)
+ _DBSWAP(mi_offset);
+ if (ret == 0 && datap) {
+ memcpy(datap, &mi_offset, sizeof(mi_offset.ui));
+ } else {
+ datap = &mi_offset;
+ datalen = sizeof(mi_offset.ui);
+ }
+
+ key.data = keyp;
+ key.size = keylen;
+ data.data = datap;
+ data.size = datalen;
+
+ ret = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
+ xx = dbiSync(dbi, 0);
+
+ xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
+ dbcursor = NULL;
+ }
+
+ }
+
+ if (ret) {
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) allocating new package instance\n"), ret);
+ goto exit;
+ }
+
+ /* Now update the indexes */
+
+ if (hdrNum)
+ {
+ dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
+
+ if (dbiTags.tags != NULL)
+ for (dbix = 0; dbix < dbiTags.max; dbix++) {
+ const char *av[1];
+ const char **rpmvals = NULL;
+ rpmTagType rpmtype = 0;
+ rpm_count_t rpmcnt = 0;
+ rpmTag rpmtag;
+ rpm_flag_t * requireFlags;
+ rpmRC rpmrc;
+ int i, j;
+
+ rpmrc = RPMRC_NOTFOUND;
+ dbi = NULL;
+ requireFlags = NULL;
+ rpmtag = dbiTags.tags[dbix];
+
+ /* Filter out temporary databases */
+ if (isTemporaryDB(rpmtag))
+ continue;
+
+ switch (rpmtag) {
+ 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);
+
+ mi_offset.ui = hdrNum;
+ if (dbiByteSwapped(dbi) == 1)
+ _DBSWAP(mi_offset);
+ key.data = (void *) &mi_offset;
+ key.size = sizeof(mi_offset.ui);
+ data.data = headerUnload(h);
+ data.size = headerSizeof(h, HEADER_MAGIC_NO);
+
+ /* Check header digest/signature on blob export. */
+ if (hdrchk && ts) {
+ char * msg = NULL;
+ int lvl;
+
+ rpmrc = (*hdrchk) (ts, data.data, data.size, &msg);
+ lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
+ rpmlog(lvl, "%s h#%8u %s",
+ (rpmrc == RPMRC_FAIL ? _("rpmdbAdd: skipping") : " +++"),
+ hdrNum, (msg ? msg : "\n"));
+ msg = _free(msg);
+ }
+
+ if (data.data != NULL && rpmrc != RPMRC_FAIL) {
+ xx = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
+ 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;
+ break;
+ case RPMTAG_BASENAMES: /* XXX preserve legacy behavior */
+ rpmtype = bnt;
+ rpmvals = baseNames;
+ rpmcnt = count;
+ break;
+ case RPMTAG_REQUIRENAME:
+ xx = hge(h, rpmtag, &rpmtype, (rpm_data_t *)&rpmvals, &rpmcnt);
+ xx = hge(h, RPMTAG_REQUIREFLAGS, NULL, (rpm_data_t *)&requireFlags, NULL);
+ break;
+ default:
+ xx = hge(h, rpmtag, &rpmtype, (rpm_data_t *)&rpmvals, &rpmcnt);
+ break;
+ }
+
+ if (rpmcnt == 0) {
+ if (rpmtag != RPMTAG_GROUP)
+ continue;
+
+ /* XXX preserve legacy behavior */
+ rpmtype = RPM_STRING_TYPE;
+ rpmvals = (const char **) "Unknown";
+ rpmcnt = 1;
+ }
+
+ 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);
+
+ for (i = 0; i < rpmcnt; i++) {
+ dbiIndexSet set;
+ int stringvalued;
+ uint8_t bin[32];
+ uint8_t * 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:
+ break;
+ case RPMTAG_FILEMD5S:
+ /* Filter out empty MD5 strings. */
+ if (!(rpmvals[i] && *rpmvals[i] != '\0'))
+ continue;
+ break;
+ case RPMTAG_REQUIRENAME:
+ /* Filter out install prerequisites. */
+ if (requireFlags && isInstallPreReq(requireFlags[i]))
+ continue;
+ break;
+ case RPMTAG_TRIGGERNAME:
+ if (i) { /* don't add duplicates */
+ for (j = 0; j < i; j++) {
+ if (!strcmp(rpmvals[i], rpmvals[j]))
+ break;
+ }
+ if (j < i)
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /* Identify value pointer and length. */
+ stringvalued = 0;
+ switch (rpmtype) {
+ case RPM_CHAR_TYPE:
+ case RPM_INT8_TYPE:
+ key.size = sizeof(int8_t);
+ key.data = rpmvals + i;
+ break;
+ case RPM_INT16_TYPE:
+ key.size = sizeof(int16_t);
+ key.data = rpmvals + i;
+ break;
+ case RPM_INT32_TYPE:
+ key.size = sizeof(int32_t);
+ key.data = rpmvals + i;
+ break;
+ case RPM_BIN_TYPE:
+ key.size = rpmcnt;
+ key.data = rpmvals;
+ rpmcnt = 1; /* XXX break out of loop. */
+ break;
+ case RPM_STRING_TYPE:
+ case RPM_I18NSTRING_TYPE:
+ rpmcnt = 1; /* XXX break out of loop. */
+ case RPM_STRING_ARRAY_TYPE:
+ /* Convert from hex to binary. */
+ if (dbi->dbi_rpmtag == RPMTAG_FILEMD5S) {
+ const char * s;
+
+ s = rpmvals[i];
+ t = bin;
+ for (j = 0; j < 16; j++, t++, s += 2)
+ *t = (rnibble(s[0]) << 4) | rnibble(s[1]);
+ key.data = bin;
+ key.size = 16;
+ break;
+ }
+ /* Extract the pubkey id from the base64 blob. */
+ if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
+ int nbin = pgpExtractPubkeyFingerprint(rpmvals[i], bin);
+ if (nbin <= 0)
+ continue;
+ key.data = bin;
+ key.size = nbin;
+ break;
+ }
+ default:
+ key.data = (void *) rpmvals[i];
+ key.size = strlen(rpmvals[i]);
+ stringvalued = 1;
+ break;
+ }
+
+ if (!printed) {
+ if (rpmcnt == 1 && stringvalued) {
+ rpmlog(RPMLOG_DEBUG,
+ "adding \"%s\" to %s index.\n",
+ (char *)key.data, rpmTagGetName(dbi->dbi_rpmtag));
+ } else {
+ rpmlog(RPMLOG_DEBUG,
+ "adding %d entries to %s index.\n",
+ rpmcnt, rpmTagGetName(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. */
+
+ 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 */
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) getting \"%s\" records from %s index\n"),
+ rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
+ ret += 1;
+ continue;
+ }
+
+ if (set == NULL) /* not found or duplicate */
+ set = xcalloc(1, sizeof(*set));
+
+ (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
+
+ (void) set2dbt(dbi, &data, set);
+ rc = dbiPut(dbi, dbcursor, &key, &data, DB_KEYLAST);
+
+ if (rc) {
+ rpmlog(RPMLOG_ERR,
+ _("error(%d) storing record %s into %s\n"),
+ rc, (char*)key.data, rpmTagGetName(dbi->dbi_rpmtag));
+ ret += 1;
+ }
+ data.data = _free(data.data);
+ data.size = 0;
+ set = dbiFreeIndexSet(set);
+ }
+
+ 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);
+ }
+
+exit:
+ (void) unblockSignals(&signalMask);
+
+ return ret;
+}
+
+#define _skip(_dn) { sizeof(_dn)-1, (_dn) }
+
+static struct skipDir_s {
+ int dnlen;
+ const char * dn;
+} skipDirs[] = {
+ { 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 */
+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 1;
+
+ mi = rpmdbInitIterator(db, RPMTAG_BASENAMES, NULL, 0);
+ if (mi == NULL) /* XXX should never happen */
+ return 1;
+
+ key = &mi->mi_key;
+ data = &mi->mi_data;
+
+ /* Gather all installed headers with matching basename's. */
+ for (i = 0; i < numItems; i++) {
+
+ matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
+
+ key->data = (void *) fpList[i].baseName;
+ 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;
+ uint32_t * dirIndexes;
+ uint32_t * 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. */
+ for (end = start + 1; end < mi->mi_set->count; end++) {
+ if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
+ break;
+ }
+ num = end - start;
+
+ /* Compute fingerprints for this installed header's matches */
+ xx = hge(h, RPMTAG_BASENAMES, &bnt, (rpm_data_t *) &fullBaseNames, NULL);
+ xx = hge(h, RPMTAG_DIRNAMES, &dnt, (rpm_data_t *) &dirNames, NULL);
+ xx = hge(h, RPMTAG_DIRINDEXES, NULL, (rpm_data_t *) &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++) {
+ /* FIX: fpList[].subDir may be NULL */
+ if (!FP_EQUAL(fps[i], fpList[im->fpNum]))
+ continue;
+ xx = dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
+ }
+
+ 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;
+
+}
+
+/*
+ * Remove DB4 environment (and lock), ie the equivalent of
+ * rm -f <prefix>/<dbpath>/__db.???
+ * Environment files not existing is not an error, failure to unlink is,
+ * return zero on success.
+ * Only useful for BDB, dbapi 3 and 4.
+ * TODO/FIX: push this down to db3.c where it belongs
+ */
+static int cleanDbenv(const char *prefix, const char *dbpath)
+{
+ ARGV_t paths = NULL, p;
+ int rc = 0;
+ char *pattern = rpmGetPath(prefix, "/", dbpath, "/__db.???", NULL);
+
+ if (rpmGlob(pattern, NULL, &paths) == 0) {
+ for (p = paths; *p; p++) {
+ rc += unlink(*p);
+ }
+ argvFree(paths);
+ }
+ free(pattern);
+ return rc;
+}
+
+static int rpmdbRemoveDatabase(const char * prefix,
+ const char * dbpath, int _dbapi)
+{
+ int i;
+ char *path;
+ int xx;
+
+ switch (_dbapi) {
+ case 4:
+ case 3:
+ if (dbiTags.tags != NULL)
+ for (i = 0; i < dbiTags.max; i++) {
+ const char * base = rpmTagGetName(dbiTags.tags[i]);
+ path = rpmGetPath(prefix, "/", dbpath, "/", base, NULL);
+ if (access(path, F_OK) == 0)
+ xx = unlink(path);
+ free(path);
+ }
+ cleanDbenv(prefix, dbpath);
+ break;
+ case 2:
+ case 1:
+ case 0:
+ break;
+ }
+
+ path = rpmGetPath(prefix, "/", dbpath, NULL);
+ xx = rmdir(path);
+ free(path);
+
+ return 0;
+}
+
+static int rpmdbMoveDatabase(const char * prefix,
+ const char * olddbpath, int _olddbapi,
+ const char * newdbpath, int _newdbapi)
+{
+ int i;
+ struct stat st;
+ int rc = 0;
+ int xx;
+ int selinux = is_selinux_enabled() && (matchpathcon_init(NULL) != -1);
+ sigset_t sigMask;
+
+ blockSignals(&sigMask);
+ switch (_olddbapi) {
+ case 4:
+ /* Fall through */
+ case 3:
+ if (dbiTags.tags != NULL)
+ for (i = 0; i < dbiTags.max; i++) {
+ const char * base;
+ char *src, *dest;
+ rpmTag rpmtag;
+
+ /* Filter out temporary databases */
+ if (isTemporaryDB((rpmtag = dbiTags.tags[i])))
+ continue;
+
+ base = rpmTagGetName(rpmtag);
+ src = rpmGetPath(prefix, "/", olddbpath, "/", base, NULL);
+ dest = rpmGetPath(prefix, "/", newdbpath, "/", base, NULL);
+
+ if (access(src, F_OK) != 0)
+ goto cont;
+
+ /*
+ * Restore uid/gid/mode/mtime/security context if possible.
+ */
+ if (stat(dest, &st) < 0)
+ if (stat(src, &st) < 0)
+ goto cont;
+
+ if ((xx = rename(src, dest)) != 0) {
+ rc = 1;
+ goto cont;
+ }
+ xx = chown(dest, st.st_uid, st.st_gid);
+ xx = chmod(dest, (st.st_mode & 07777));
+ { struct utimbuf stamp;
+ stamp.actime = st.st_atime;
+ stamp.modtime = st.st_mtime;
+ xx = utime(dest, &stamp);
+ }
+
+ if (selinux) {
+ security_context_t scon = NULL;
+ if (matchpathcon(dest, st.st_mode, &scon) != -1) {
+ (void) setfilecon(dest, scon);
+ freecon(scon);
+ }
+ }
+
+cont:
+ free(src);
+ free(dest);
+ }
+
+ cleanDbenv(prefix, olddbpath);
+ cleanDbenv(prefix, newdbpath);
+ break;
+ case 2:
+ case 1:
+ case 0:
+ break;
+ }
+ unblockSignals(&sigMask);
+
+#ifdef SQLITE_HACK_XXX
+ if (rc || _olddbapi == _newdbapi)
+ return rc;
+
+ rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi);
+
+#endif
+ if (selinux) {
+ (void) matchpathcon_fini();
+ }
+ return rc;
+}
+
+int rpmdbRebuild(const char * prefix, rpmts ts,
+ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
+{
+ rpmdb olddb;
+ char * dbpath = NULL;
+ char * rootdbpath = NULL;
+ rpmdb newdb;
+ char * newdbpath = NULL;
+ char * newrootdbpath = NULL;
+ char * tfn;
+ int nocleanup = 1;
+ int failed = 0;
+ int removedir = 0;
+ int rc = 0, xx;
+ int _dbapi;
+ int _dbapi_rebuild;
+
+ if (prefix == NULL) prefix = "/";
+
+ _dbapi = rpmExpandNumeric("%{_dbapi}");
+ _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
+
+ tfn = rpmGetPath("%{?_dbpath}", NULL);
+ if (!(tfn && tfn[0] != '\0'))
+ {
+ rpmlog(RPMLOG_ERR, _("no dbpath has been set"));
+ rc = 1;
+ goto exit;
+ }
+ dbpath = rootdbpath = rpmGetPath(prefix, tfn, NULL);
+ if (!(prefix[0] == '/' && prefix[1] == '\0'))
+ dbpath += strlen(prefix) - 1;
+ tfn = _free(tfn);
+
+ tfn = rpmGetPath("%{?_dbpath_rebuild}", NULL);
+ if (!(tfn && tfn[0] != '\0' && strcmp(tfn, dbpath)))
+ {
+ tfn = _free(tfn);
+ rasprintf(&tfn, "%srebuilddb.%d", dbpath, (int) getpid());
+ nocleanup = 0;
+ }
+ newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL);
+ if (!(prefix[0] == '/' && prefix[1] == '\0'))
+ newdbpath += strlen(prefix) - 1;
+ tfn = _free(tfn);
+
+ rpmlog(RPMLOG_DEBUG, "rebuilding database %s into %s\n",
+ rootdbpath, newrootdbpath);
+
+ if (!access(newrootdbpath, F_OK)) {
+ rpmlog(RPMLOG_ERR, _("temporary database %s already exists\n"),
+ newrootdbpath);
+ rc = 1;
+ goto exit;
+ }
+
+ rpmlog(RPMLOG_DEBUG, "creating directory %s\n", newrootdbpath);
+ if (mkdir(newrootdbpath, 0755)) {
+ rpmlog(RPMLOG_ERR, _("failed to create directory %s: %s\n"),
+ newrootdbpath, strerror(errno));
+ rc = 1;
+ goto exit;
+ }
+ removedir = 1;
+
+ _rebuildinprogress = 0;
+
+ rpmlog(RPMLOG_DEBUG, "opening old database with dbapi %d\n",
+ _dbapi);
+ if (openDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644,
+ RPMDB_FLAG_MINIMAL)) {
+ rc = 1;
+ goto exit;
+ }
+ _dbapi = olddb->db_api;
+ _rebuildinprogress = 1;
+ rpmlog(RPMLOG_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;
+ }
+
+ _rebuildinprogress = 0;
+
+ _dbapi_rebuild = newdb->db_api;
+
+ { Header h = NULL;
+ rpmdbMatchIterator mi;
+#define _RECNUM rpmdbGetIteratorOffset(mi)
+
+ 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)))
+ {
+ rpmlog(RPMLOG_ERR,
+ _("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);
+
+ { 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;
+ break;
+ }
+ mi = rpmdbFreeIterator(mi);
+ }
+
+ 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) {
+ rpmlog(RPMLOG_ERR,
+ _("cannot add record originally at %u\n"), _RECNUM);
+ failed = 1;
+ break;
+ }
+ }
+
+ mi = rpmdbFreeIterator(mi);
+
+ }
+
+ xx = rpmdbClose(olddb);
+ xx = rpmdbClose(newdb);
+
+ if (failed) {
+ rpmlog(RPMLOG_NOTICE, _("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)) {
+ rpmlog(RPMLOG_ERR, _("failed to replace old database with new "
+ "database!\n"));
+ rpmlog(RPMLOG_ERR, _("replace files in %s with files from %s "
+ "to recover"), dbpath, newdbpath);
+ rc = 1;
+ goto exit;
+ }
+ }
+ rc = 0;
+
+exit:
+ if (removedir && !(rc == 0 && nocleanup)) {
+ rpmlog(RPMLOG_DEBUG, "removing directory %s\n", newrootdbpath);
+ if (rmdir(newrootdbpath))
+ rpmlog(RPMLOG_ERR, _("failed to remove directory %s: %s\n"),
+ newrootdbpath, strerror(errno));
+ }
+ newrootdbpath = _free(newrootdbpath);
+ rootdbpath = _free(rootdbpath);
+
+ return rc;
+}