2412 lines
55 KiB
C
2412 lines
55 KiB
C
/** \ingroup rpmdb dbi
|
|
* \file lib/rpmdb.c
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include <sys/file.h>
|
|
#include <utime.h>
|
|
#include <errno.h>
|
|
|
|
#ifndef DYING /* XXX already in "system.h" */
|
|
#include <fnmatch.h>
|
|
#endif
|
|
|
|
#include <regex.h>
|
|
|
|
#include <rpm/rpmtypes.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/rpmts.h>
|
|
#include <rpm/argv.h>
|
|
|
|
#include "lib/rpmchroot.h"
|
|
#include "lib/rpmdb_internal.h"
|
|
#include "lib/fprint.h"
|
|
#include "lib/header_internal.h" /* XXX for headerSetInstance() */
|
|
#include "lib/backend/dbiset.h"
|
|
#include "debug.h"
|
|
|
|
#undef HASHTYPE
|
|
#undef HTKEYTYPE
|
|
#undef HTDATATYPE
|
|
#define HASHTYPE dbChk
|
|
#define HTKEYTYPE unsigned int
|
|
#define HTDATATYPE rpmRC
|
|
#include "lib/rpmhash.H"
|
|
#include "lib/rpmhash.C"
|
|
#undef HASHTYPE
|
|
#undef HTKEYTYPE
|
|
#undef HTDATATYPE
|
|
|
|
typedef rpmRC (*idxfunc)(dbiCursor dbc, const char *keyp, size_t keylen,
|
|
dbiIndexItem rec);
|
|
|
|
static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag,
|
|
unsigned int hdrNum, Header h,
|
|
idxfunc idxupdate);
|
|
|
|
static rpmRC indexPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h);
|
|
static rpmdb rpmdbUnlink(rpmdb db);
|
|
|
|
static int buildIndexes(rpmdb db)
|
|
{
|
|
int rc = 0;
|
|
Header h;
|
|
rpmdbMatchIterator mi;
|
|
|
|
rc += rpmdbOpenAll(db);
|
|
|
|
/* If the main db was just created, this is expected - dont whine */
|
|
if (!(dbiFlags(db->db_pkgs) & DBI_CREATED)) {
|
|
rpmlog(RPMLOG_WARNING,
|
|
_("Generating %d missing index(es), please wait...\n"),
|
|
db->db_buildindex);
|
|
}
|
|
|
|
/* Don't call us again */
|
|
db->db_buildindex = 0;
|
|
|
|
dbSetFSync(db->db_dbenv, 0);
|
|
|
|
mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, NULL, 0);
|
|
while ((h = rpmdbNextIterator(mi))) {
|
|
unsigned int hdrNum = headerGetInstance(h);
|
|
/* Build all secondary indexes which were created on open */
|
|
for (int dbix = 0; dbix < db->db_ndbi; dbix++) {
|
|
dbiIndex dbi = db->db_indexes[dbix];
|
|
if (dbi && (dbiFlags(dbi) & DBI_CREATED)) {
|
|
rc += indexPut(dbi, db->db_tags[dbix], hdrNum, h);
|
|
}
|
|
}
|
|
}
|
|
rpmdbFreeIterator(mi);
|
|
dbSetFSync(db->db_dbenv, !db->cfg.db_no_fsync);
|
|
return rc;
|
|
}
|
|
|
|
static int uintCmp(unsigned int a, unsigned int b)
|
|
{
|
|
return (a != b);
|
|
}
|
|
|
|
static unsigned int uintId(unsigned int a)
|
|
{
|
|
return a;
|
|
}
|
|
|
|
/** \ingroup dbi
|
|
* Return (newly allocated) integer of the epoch.
|
|
* @param s version string optionally containing epoch number
|
|
* @retval version only the version part of s
|
|
* @return epoch integer within the [0; UINT32_MAX] interval,
|
|
* or -1 for no epoch
|
|
*/
|
|
static int64_t splitEpoch(const char *s, const char **version)
|
|
{
|
|
int64_t e;
|
|
char *end;
|
|
int saveerrno = errno;
|
|
|
|
*version = s;
|
|
e = strtol(s, &end, 10);
|
|
if (*end == ':' && e >= 0 && e <= UINT32_MAX) {
|
|
*version = end + 1;
|
|
} else {
|
|
e = -1;
|
|
}
|
|
|
|
errno = saveerrno;
|
|
return e;
|
|
}
|
|
|
|
static int pkgdbOpen(rpmdb db, int flags, dbiIndex *dbip)
|
|
{
|
|
int rc = 0;
|
|
dbiIndex dbi = NULL;
|
|
|
|
if (db == NULL)
|
|
return -1;
|
|
|
|
/* Is this it already open ? */
|
|
if ((dbi = db->db_pkgs) != NULL)
|
|
goto exit;
|
|
|
|
rc = dbiOpen(db, RPMDBI_PACKAGES, &dbi, flags);
|
|
|
|
if (rc == 0) {
|
|
int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY);
|
|
|
|
db->db_pkgs = dbi;
|
|
/* Allocate header checking cache .. based on some random number */
|
|
if (!verifyonly && (db->db_checked == NULL)) {
|
|
db->db_checked = dbChkCreate(567, uintId, uintCmp, NULL, NULL);
|
|
}
|
|
/* If primary got created, we can safely run without fsync */
|
|
if ((!verifyonly && (dbiFlags(dbi) & DBI_CREATED)) || db->cfg.db_no_fsync) {
|
|
rpmlog(RPMLOG_DEBUG, "disabling fsync on database\n");
|
|
db->cfg.db_no_fsync = 1;
|
|
dbSetFSync(db->db_dbenv, 0);
|
|
}
|
|
} else {
|
|
rpmlog(RPMLOG_ERR, _("cannot open %s index using %s - %s (%d)\n"),
|
|
rpmTagGetName(RPMDBI_PACKAGES), db->db_descr,
|
|
(rc > 0 ? strerror(rc) : ""), rc);
|
|
}
|
|
|
|
exit:
|
|
if (rc == 0 && dbip)
|
|
*dbip = dbi;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int indexOpen(rpmdb db, rpmDbiTagVal rpmtag, int flags, dbiIndex *dbip)
|
|
{
|
|
int dbix, rc = 0;
|
|
dbiIndex dbi = NULL;
|
|
|
|
if (db == NULL)
|
|
return -1;
|
|
|
|
for (dbix = 0; dbix < db->db_ndbi; dbix++) {
|
|
if (rpmtag == db->db_tags[dbix])
|
|
break;
|
|
}
|
|
if (dbix >= db->db_ndbi)
|
|
return -1;
|
|
|
|
/* Is this index already open ? */
|
|
if ((dbi = db->db_indexes[dbix]) != NULL)
|
|
goto exit;
|
|
|
|
rc = dbiOpen(db, rpmtag, &dbi, flags);
|
|
|
|
if (rc == 0) {
|
|
int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY);
|
|
int rebuild = (db->db_flags & RPMDB_FLAG_REBUILD);
|
|
|
|
db->db_indexes[dbix] = dbi;
|
|
if (!rebuild && !verifyonly && (dbiFlags(dbi) & DBI_CREATED)) {
|
|
rpmlog(RPMLOG_DEBUG, "index %s needs creating\n", dbiName(dbi));
|
|
db->db_buildindex++;
|
|
if (db->db_buildindex == 1) {
|
|
buildIndexes(db);
|
|
}
|
|
}
|
|
} else {
|
|
rpmlog(RPMLOG_ERR, _("cannot open %s index using %s - %s (%d)\n"),
|
|
rpmTagGetName(rpmtag), db->db_descr,
|
|
(rc > 0 ? strerror(rc) : ""), rc);
|
|
}
|
|
|
|
exit:
|
|
if (rc == 0 && dbip)
|
|
*dbip = dbi;
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC indexGet(dbiIndex dbi, const char *keyp, size_t keylen,
|
|
dbiIndexSet *set)
|
|
{
|
|
rpmRC rc = RPMRC_FAIL; /* assume failure */
|
|
if (dbi != NULL) {
|
|
dbiCursor dbc = dbiCursorInit(dbi, DBC_READ);
|
|
|
|
if (keyp) {
|
|
if (keylen == 0) {
|
|
keylen = strlen(keyp);
|
|
if (keylen == 0)
|
|
keylen++; /* XXX "/" fixup */
|
|
}
|
|
rc = dbcCursorGet(dbc, keyp, keylen, set);
|
|
} else {
|
|
do {
|
|
rc = dbcCursorGet(dbc, NULL, 0, set);
|
|
} while (rc == RPMRC_OK);
|
|
|
|
/* If we got some results, not found is not an error */
|
|
if (rc == RPMRC_NOTFOUND && set != NULL)
|
|
rc = RPMRC_OK;
|
|
}
|
|
|
|
dbiCursorFree(dbc);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
typedef struct miRE_s {
|
|
rpmTagVal 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;
|
|
rpmdb mi_db;
|
|
rpmDbiTagVal mi_rpmtag;
|
|
dbiIndexSet mi_set;
|
|
dbiCursor mi_dbc;
|
|
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);
|
|
|
|
};
|
|
|
|
struct rpmdbIndexIterator_s {
|
|
rpmdbIndexIterator ii_next;
|
|
rpmdb ii_db;
|
|
dbiIndex ii_dbi;
|
|
rpmDbiTag ii_rpmtag;
|
|
dbiCursor ii_dbc;
|
|
dbiIndexSet ii_set;
|
|
};
|
|
|
|
static rpmdb rpmdbRock;
|
|
static rpmdbMatchIterator rpmmiRock;
|
|
static rpmdbIndexIterator rpmiiRock;
|
|
|
|
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 (rpmsqIsCaught(SIGINT) > 0
|
|
|| rpmsqIsCaught(SIGQUIT) > 0
|
|
|| rpmsqIsCaught(SIGHUP) > 0
|
|
|| rpmsqIsCaught(SIGTERM) > 0
|
|
|| rpmsqIsCaught(SIGPIPE) > 0
|
|
|| terminate)
|
|
terminating = 1;
|
|
|
|
if (terminating) {
|
|
rpmdb db;
|
|
rpmdbMatchIterator mi;
|
|
rpmdbIndexIterator ii;
|
|
|
|
while ((mi = rpmmiRock) != NULL) {
|
|
rpmmiRock = mi->mi_next;
|
|
mi->mi_next = NULL;
|
|
rpmdbFreeIterator(mi);
|
|
}
|
|
|
|
while ((ii = rpmiiRock) != NULL) {
|
|
rpmiiRock = ii->ii_next;
|
|
ii->ii_next = NULL;
|
|
rpmdbIndexIteratorFree(ii);
|
|
}
|
|
|
|
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)) {
|
|
rpmlog(RPMLOG_DEBUG, "Exiting on signal...\n");
|
|
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);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
const char *rpmdbHome(rpmdb db)
|
|
{
|
|
const char *dbdir = NULL;
|
|
if (db) {
|
|
dbdir = rpmChrootDone() ? db->db_home : db->db_fullpath;
|
|
}
|
|
return dbdir;
|
|
}
|
|
|
|
int rpmdbOpenAll(rpmdb db)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (db == NULL) return -2;
|
|
|
|
rc = pkgdbOpen(db, db->db_flags, NULL);
|
|
for (int dbix = 0; dbix < db->db_ndbi; dbix++) {
|
|
rc += indexOpen(db, db->db_tags[dbix], db->db_flags, NULL);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int dbiForeach(dbiIndex *dbis, int ndbi,
|
|
int (*func) (dbiIndex, unsigned int), int del)
|
|
{
|
|
int xx, rc = 0;
|
|
for (int dbix = ndbi; --dbix >= 0; ) {
|
|
if (dbis[dbix] == NULL)
|
|
continue;
|
|
xx = func(dbis[dbix], 0);
|
|
if (xx && rc == 0) rc = xx;
|
|
if (del)
|
|
dbis[dbix] = NULL;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int rpmdbClose(rpmdb db)
|
|
{
|
|
rpmdb * prev, next;
|
|
int rc = 0;
|
|
|
|
if (db == NULL)
|
|
goto exit;
|
|
|
|
(void) rpmdbUnlink(db);
|
|
|
|
if (db->nrefs > 0)
|
|
goto exit;
|
|
|
|
/* Always re-enable fsync on close of rw-database */
|
|
if ((db->db_mode & O_ACCMODE) != O_RDONLY)
|
|
dbSetFSync(db->db_dbenv, 1);
|
|
|
|
if (db->db_pkgs)
|
|
rc = dbiClose(db->db_pkgs, 0);
|
|
rc += dbiForeach(db->db_indexes, db->db_ndbi, dbiClose, 1);
|
|
|
|
db->db_root = _free(db->db_root);
|
|
db->db_home = _free(db->db_home);
|
|
db->db_fullpath = _free(db->db_fullpath);
|
|
db->db_checked = dbChkFree(db->db_checked);
|
|
db->db_indexes = _free(db->db_indexes);
|
|
db->db_descr = _free(db->db_descr);
|
|
|
|
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);
|
|
|
|
if (rpmdbRock == NULL) {
|
|
(void) rpmsqEnable(-SIGHUP, NULL);
|
|
(void) rpmsqEnable(-SIGINT, NULL);
|
|
(void) rpmsqEnable(-SIGTERM, NULL);
|
|
(void) rpmsqEnable(-SIGQUIT, NULL);
|
|
(void) rpmsqEnable(-SIGPIPE, NULL);
|
|
}
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static rpmdb newRpmdb(const char * root, const char * home,
|
|
int mode, int perms, int flags)
|
|
{
|
|
rpmdb db = NULL;
|
|
char * db_home = rpmGetPath((home && *home) ? home : "%{_dbpath}", NULL);
|
|
|
|
static rpmDbiTag const dbiTags[] = {
|
|
RPMDBI_PACKAGES,
|
|
RPMDBI_NAME,
|
|
RPMDBI_BASENAMES,
|
|
RPMDBI_GROUP,
|
|
RPMDBI_REQUIRENAME,
|
|
RPMDBI_PROVIDENAME,
|
|
RPMDBI_CONFLICTNAME,
|
|
RPMDBI_OBSOLETENAME,
|
|
RPMDBI_TRIGGERNAME,
|
|
RPMDBI_DIRNAMES,
|
|
RPMDBI_INSTALLTID,
|
|
RPMDBI_SIGMD5,
|
|
RPMDBI_SHA1HEADER,
|
|
};
|
|
|
|
if (!(db_home && db_home[0] != '%')) {
|
|
rpmlog(RPMLOG_ERR, _("no dbpath has been set\n"));
|
|
free(db_home);
|
|
return NULL;
|
|
}
|
|
|
|
db = xcalloc(sizeof(*db), 1);
|
|
|
|
if (!(perms & 0600)) perms = 0644; /* XXX sanity */
|
|
|
|
db->db_mode = (mode >= 0) ? mode : 0;
|
|
db->db_perms = (perms >= 0) ? perms : 0644;
|
|
db->db_flags = (flags >= 0) ? flags : 0;
|
|
|
|
db->db_home = db_home;
|
|
db->db_root = rpmGetPath((root && *root) ? root : "/", NULL);
|
|
db->db_fullpath = rpmGenPath(db->db_root, db->db_home, NULL);
|
|
/* XXX remove environment after chrooted operations, for now... */
|
|
db->db_remove_env = (!rstreq(db->db_root, "/") ? 1 : 0);
|
|
db->db_tags = dbiTags;
|
|
db->db_ndbi = sizeof(dbiTags) / sizeof(rpmDbiTag);
|
|
db->db_indexes = xcalloc(db->db_ndbi, sizeof(*db->db_indexes));
|
|
db->db_descr = xstrdup("unknown db");
|
|
db->nrefs = 0;
|
|
return rpmdbLink(db);
|
|
}
|
|
|
|
static int openDatabase(const char * prefix,
|
|
const char * dbpath, rpmdb *dbp,
|
|
int mode, int perms, int flags)
|
|
{
|
|
rpmdb db;
|
|
int rc;
|
|
int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
|
|
|
|
if (dbp)
|
|
*dbp = NULL;
|
|
if ((mode & O_ACCMODE) == O_WRONLY)
|
|
return 1;
|
|
|
|
db = newRpmdb(prefix, dbpath, mode, perms, flags);
|
|
if (db == NULL)
|
|
return 1;
|
|
|
|
/* Try to ensure db home exists, error out if we can't even create */
|
|
rc = rpmioMkpath(rpmdbHome(db), 0755, getuid(), getgid());
|
|
if (rc == 0) {
|
|
if (rpmdbRock == NULL) {
|
|
(void) rpmsqEnable(SIGHUP, NULL);
|
|
(void) rpmsqEnable(SIGINT, NULL);
|
|
(void) rpmsqEnable(SIGTERM, NULL);
|
|
(void) rpmsqEnable(SIGQUIT, NULL);
|
|
(void) rpmsqEnable(SIGPIPE, NULL);
|
|
}
|
|
|
|
/* Just the primary Packages database opened here */
|
|
rc = pkgdbOpen(db, db->db_flags, NULL);
|
|
}
|
|
|
|
if (rc || justCheck || dbp == NULL)
|
|
rpmdbClose(db);
|
|
else {
|
|
db->db_next = rpmdbRock;
|
|
rpmdbRock = db;
|
|
*dbp = db;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static rpmdb rpmdbUnlink(rpmdb db)
|
|
{
|
|
if (db)
|
|
db->nrefs--;
|
|
return NULL;
|
|
}
|
|
|
|
rpmdb rpmdbLink(rpmdb db)
|
|
{
|
|
if (db)
|
|
db->nrefs++;
|
|
return db;
|
|
}
|
|
|
|
int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
|
|
{
|
|
return openDatabase(prefix, NULL, dbp, mode, perms, 0);
|
|
}
|
|
|
|
int rpmdbInit (const char * prefix, int perms)
|
|
{
|
|
rpmdb db = NULL;
|
|
int rc;
|
|
|
|
rc = openDatabase(prefix, NULL, &db, (O_CREAT | O_RDWR), perms, 0);
|
|
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 rc = 0;
|
|
|
|
rc = openDatabase(prefix, NULL, &db, O_RDONLY, 0644, RPMDB_FLAG_VERIFYONLY);
|
|
|
|
if (db != NULL) {
|
|
int xx;
|
|
rc = rpmdbOpenAll(db);
|
|
|
|
rc = dbiForeach(db->db_indexes, db->db_ndbi, dbiVerify, 0);
|
|
|
|
xx = rpmdbClose(db);
|
|
if (xx && rc == 0) rc = xx;
|
|
db = NULL;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static Header rpmdbGetHeaderAt(rpmdb db, unsigned int offset)
|
|
{
|
|
rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMDBI_PACKAGES,
|
|
&offset, sizeof(offset));
|
|
Header h = headerLink(rpmdbNextIterator(mi));
|
|
rpmdbFreeIterator(mi);
|
|
return h;
|
|
}
|
|
|
|
/**
|
|
* Find file matches in database.
|
|
* @param db rpm database
|
|
* @param dbi index database handle (always RPMDBI_BASENAMES)
|
|
* @param filespec
|
|
* @param usestate take file state into account?
|
|
* @retval matches
|
|
* @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
|
|
*/
|
|
static rpmRC rpmdbFindByFile(rpmdb db, dbiIndex dbi, const char *filespec,
|
|
int usestate, dbiIndexSet * matches)
|
|
{
|
|
char * dirName = NULL;
|
|
const char * baseName;
|
|
fingerPrintCache fpc = NULL;
|
|
fingerPrint * fp1 = NULL;
|
|
dbiIndexSet allMatches = NULL;
|
|
unsigned int i;
|
|
rpmRC rc = RPMRC_FAIL; /* assume error */
|
|
|
|
*matches = NULL;
|
|
if (filespec == NULL) return rc; /* nothing alloced yet */
|
|
|
|
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)
|
|
goto exit;
|
|
|
|
rc = indexGet(dbi, baseName, 0, &allMatches);
|
|
|
|
if (rc || allMatches == NULL) goto exit;
|
|
|
|
*matches = dbiIndexSetNew(0);
|
|
fpc = fpCacheCreate(allMatches->count, NULL);
|
|
fpLookup(fpc, dirName, baseName, &fp1);
|
|
|
|
i = 0;
|
|
while (i < allMatches->count) {
|
|
struct rpmtd_s bn, dn, di, fs;
|
|
const char ** baseNames, ** dirNames;
|
|
uint32_t * dirIndexes;
|
|
unsigned int offset = dbiIndexRecordOffset(allMatches, i);
|
|
unsigned int prevoff;
|
|
Header h = rpmdbGetHeaderAt(db, offset);
|
|
|
|
if (h == NULL) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
headerGet(h, RPMTAG_BASENAMES, &bn, HEADERGET_MINMEM);
|
|
headerGet(h, RPMTAG_DIRNAMES, &dn, HEADERGET_MINMEM);
|
|
headerGet(h, RPMTAG_DIRINDEXES, &di, HEADERGET_MINMEM);
|
|
baseNames = bn.data;
|
|
dirNames = dn.data;
|
|
dirIndexes = di.data;
|
|
if (usestate)
|
|
headerGet(h, RPMTAG_FILESTATES, &fs, HEADERGET_MINMEM);
|
|
|
|
do {
|
|
unsigned int num = dbiIndexRecordFileNumber(allMatches, i);
|
|
int skip = 0;
|
|
|
|
if (usestate) {
|
|
rpmtdSetIndex(&fs, num);
|
|
if (!RPMFILE_IS_INSTALLED(rpmtdGetNumber(&fs))) {
|
|
skip = 1;
|
|
}
|
|
}
|
|
|
|
if (!skip) {
|
|
const char *dirName = dirNames[dirIndexes[num]];
|
|
if (fpLookupEquals(fpc, fp1, dirName, baseNames[num])) {
|
|
struct dbiIndexItem_s rec = {
|
|
.hdrNum = dbiIndexRecordOffset(allMatches, i),
|
|
.tagNum = dbiIndexRecordFileNumber(allMatches, i),
|
|
};
|
|
dbiIndexSetAppend(*matches, &rec, 1, 0);
|
|
}
|
|
}
|
|
|
|
prevoff = offset;
|
|
i++;
|
|
if (i < allMatches->count)
|
|
offset = dbiIndexRecordOffset(allMatches, i);
|
|
} while (i < allMatches->count && offset == prevoff);
|
|
|
|
rpmtdFreeData(&bn);
|
|
rpmtdFreeData(&dn);
|
|
rpmtdFreeData(&di);
|
|
if (usestate)
|
|
rpmtdFreeData(&fs);
|
|
headerFree(h);
|
|
}
|
|
|
|
free(fp1);
|
|
fpCacheFree(fpc);
|
|
|
|
if ((*matches)->count == 0) {
|
|
*matches = dbiIndexSetFree(*matches);
|
|
rc = RPMRC_NOTFOUND;
|
|
} else {
|
|
rc = RPMRC_OK;
|
|
}
|
|
|
|
exit:
|
|
dbiIndexSetFree(allMatches);
|
|
free(dirName);
|
|
return rc;
|
|
}
|
|
|
|
int rpmdbCountPackages(rpmdb db, const char * name)
|
|
{
|
|
int count = -1;
|
|
dbiIndex dbi = NULL;
|
|
|
|
if (name != NULL && indexOpen(db, RPMDBI_NAME, 0, &dbi) == 0) {
|
|
dbiIndexSet matches = NULL;
|
|
|
|
rpmRC rc = indexGet(dbi, name, strlen(name), &matches);
|
|
|
|
if (rc == RPMRC_OK) {
|
|
count = dbiIndexSetCount(matches);
|
|
} else {
|
|
count = (rc == RPMRC_NOTFOUND) ? 0 : -1;
|
|
}
|
|
dbiIndexSetFree(matches);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Attempt partial matches on name[-version[-release]][.arch] strings.
|
|
* @param db rpmdb handle
|
|
* @param dbi index database
|
|
* @param name package name
|
|
* @param epoch package epoch (-1 for any epoch)
|
|
* @param version package version (can be a pattern)
|
|
* @param release package release (can be a pattern)
|
|
* @param arch package arch (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(rpmdb db, dbiIndex dbi,
|
|
const char * name,
|
|
int64_t epoch,
|
|
const char * version,
|
|
const char * release,
|
|
const char * arch,
|
|
dbiIndexSet * matches)
|
|
{
|
|
unsigned int gotMatches = 0;
|
|
rpmRC rc;
|
|
unsigned int i;
|
|
|
|
rc = indexGet(dbi, name, strlen(name), matches);
|
|
|
|
/* No matches on the name, anything else wont match either */
|
|
if (rc != RPMRC_OK)
|
|
goto exit;
|
|
|
|
/* If we got matches on name and nothing else was specified, we're done */
|
|
if (epoch < 0 && version == NULL && release == NULL && arch == NULL)
|
|
goto exit;
|
|
|
|
/* 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(db, 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;
|
|
}
|
|
if (arch &&
|
|
rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_DEFAULT, arch))
|
|
{
|
|
rc = RPMRC_FAIL;
|
|
goto exit;
|
|
}
|
|
|
|
h = rpmdbNextIterator(mi);
|
|
|
|
if (epoch >= 0 && h) {
|
|
struct rpmtd_s td;
|
|
headerGet(h, RPMTAG_EPOCH, &td, HEADERGET_MINMEM);
|
|
if (epoch != rpmtdGetNumber(&td))
|
|
h = NULL;
|
|
rpmtdFreeData(&td);
|
|
}
|
|
if (h)
|
|
(*matches)->recs[gotMatches++] = (*matches)->recs[i];
|
|
else
|
|
(*matches)->recs[i].hdrNum = 0;
|
|
rpmdbFreeIterator(mi);
|
|
}
|
|
|
|
if (gotMatches) {
|
|
(*matches)->count = gotMatches;
|
|
rc = RPMRC_OK;
|
|
} else
|
|
rc = RPMRC_NOTFOUND;
|
|
|
|
exit:
|
|
/* FIX: double indirection */
|
|
if (rc && matches && *matches)
|
|
*matches = dbiIndexSetFree(*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 db rpmdb handle
|
|
* @param dbi index database handle (always RPMDBI_NAME)
|
|
* @param arg name[-[epoch:]version[-release]] string
|
|
* @param arglen length of arg
|
|
* @param arch possible arch string (or NULL)
|
|
* @retval matches set of header instances that match
|
|
* @return RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
|
|
*/
|
|
static rpmRC dbiFindByLabelArch(rpmdb db, dbiIndex dbi,
|
|
const char * arg, size_t arglen, const char *arch,
|
|
dbiIndexSet * matches)
|
|
{
|
|
char localarg[arglen+1];
|
|
int64_t epoch;
|
|
const char * version;
|
|
const char * release;
|
|
char * s;
|
|
char c;
|
|
int brackets;
|
|
rpmRC rc;
|
|
|
|
if (arglen == 0) return RPMRC_NOTFOUND;
|
|
|
|
strncpy(localarg, arg, arglen);
|
|
localarg[arglen] = '\0';
|
|
|
|
/* did they give us just a name? */
|
|
rc = dbiFindMatches(db, dbi, localarg, -1, NULL, NULL, arch, matches);
|
|
if (rc != RPMRC_NOTFOUND)
|
|
goto exit;
|
|
|
|
/* FIX: double indirection */
|
|
*matches = dbiIndexSetFree(*matches);
|
|
|
|
/* maybe a name-[epoch:]version ? */
|
|
s = localarg + arglen;
|
|
|
|
c = '\0';
|
|
brackets = 0;
|
|
for (s -= 1; s > localarg; s--) {
|
|
switch (*s) {
|
|
case '[':
|
|
brackets = 1;
|
|
break;
|
|
case ']':
|
|
if (c != '[') brackets = 0;
|
|
break;
|
|
}
|
|
if (!brackets && c && *s == '-')
|
|
break;
|
|
c = *s;
|
|
}
|
|
|
|
/* FIX: *matches may be NULL. */
|
|
if (s == localarg) {
|
|
rc = RPMRC_NOTFOUND;
|
|
goto exit;
|
|
}
|
|
|
|
*s = '\0';
|
|
|
|
epoch = splitEpoch(s + 1, &version);
|
|
rc = dbiFindMatches(db, dbi, localarg, epoch, version, NULL, arch, matches);
|
|
if (rc != RPMRC_NOTFOUND) goto exit;
|
|
|
|
/* FIX: double indirection */
|
|
*matches = dbiIndexSetFree(*matches);
|
|
|
|
/* how about name-[epoch:]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;
|
|
}
|
|
if (!brackets && c && *s == '-')
|
|
break;
|
|
c = *s;
|
|
}
|
|
|
|
if (s == localarg) {
|
|
rc = RPMRC_NOTFOUND;
|
|
goto exit;
|
|
}
|
|
|
|
*s = '\0';
|
|
/* FIX: *matches may be NULL. */
|
|
epoch = splitEpoch(s + 1, &version);
|
|
rc = dbiFindMatches(db, dbi, localarg, epoch, version, release, arch, matches);
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC dbiFindByLabel(rpmdb db, dbiIndex dbi, const char * label,
|
|
dbiIndexSet * matches)
|
|
{
|
|
const char *arch = NULL;
|
|
/* First, try with label as it is */
|
|
rpmRC rc = dbiFindByLabelArch(db, dbi, label, strlen(label), NULL, matches);
|
|
|
|
/* If not found, retry with possible .arch specifier if there is one */
|
|
if (rc == RPMRC_NOTFOUND && (arch = strrchr(label, '.')))
|
|
rc = dbiFindByLabelArch(db, dbi, label, arch-label, arch+1, matches);
|
|
|
|
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) {
|
|
rpmRC rpmrc = RPMRC_NOTFOUND;
|
|
unsigned int hdrLen = 0;
|
|
unsigned char *hdrBlob = headerExport(mi->mi_h, &hdrLen);
|
|
|
|
/* 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, hdrBlob, hdrLen, &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 (hdrBlob != NULL && rpmrc != RPMRC_FAIL) {
|
|
sigset_t signalMask;
|
|
|
|
blockSignals(&signalMask);
|
|
rc = pkgdbPut(dbi, mi->mi_dbc, mi->mi_prevoffset,
|
|
hdrBlob, hdrLen, NULL);
|
|
unblockSignals(&signalMask);
|
|
|
|
if (rc) {
|
|
rpmlog(RPMLOG_ERR,
|
|
_("error(%d) storing record #%d into %s\n"),
|
|
rc, mi->mi_prevoffset, dbiName(dbi));
|
|
}
|
|
}
|
|
free(hdrBlob);
|
|
}
|
|
|
|
mi->mi_h = headerFree(mi->mi_h);
|
|
|
|
return rc;
|
|
}
|
|
|
|
rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
|
|
{
|
|
rpmdbMatchIterator * prev, next;
|
|
dbiIndex dbi = NULL;
|
|
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;
|
|
}
|
|
|
|
pkgdbOpen(mi->mi_db, 0, &dbi);
|
|
|
|
miFreeHeader(mi, dbi);
|
|
|
|
mi->mi_dbc = dbiCursorFree(mi->mi_dbc);
|
|
|
|
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);
|
|
mire->preg = _free(mire->preg);
|
|
}
|
|
}
|
|
mi->mi_re = _free(mi->mi_re);
|
|
|
|
mi->mi_set = dbiIndexSetFree(mi->mi_set);
|
|
rpmdbClose(mi->mi_db);
|
|
mi->mi_ts = rpmtsFree(mi->mi_ts);
|
|
|
|
mi = _free(mi);
|
|
|
|
(void) rpmdbCheckSignals();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
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 = (!rstreq(mire->pattern, val));
|
|
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(rpmTagVal 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, rpmTagVal 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' || rstreq(t, "default"))
|
|
defmode = RPMMIRE_DEFAULT;
|
|
else if (rstreq(t, "strcmp"))
|
|
defmode = RPMMIRE_STRCMP;
|
|
else if (rstreq(t, "regex"))
|
|
defmode = RPMMIRE_REGEX;
|
|
else if (rstreq(t, "glob"))
|
|
defmode = RPMMIRE_GLOB;
|
|
else
|
|
defmode = RPMMIRE_DEFAULT;
|
|
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);
|
|
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)
|
|
{
|
|
miRE mire;
|
|
uint32_t zero = 0;
|
|
int ntags = 0;
|
|
int nmatches = 0;
|
|
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 (int i = 0; i < mi->mi_nre; i++, mire++) {
|
|
int anymatch;
|
|
struct rpmtd_s td;
|
|
|
|
if (!headerGet(mi->mi_h, mire->tag, &td, HEADERGET_MINMEM)) {
|
|
if (mire->tag != RPMTAG_EPOCH) {
|
|
ntags++;
|
|
continue;
|
|
}
|
|
/* "is package already installed" checks rely on this behavior */
|
|
td.count = 1;
|
|
td.type = RPM_INT32_TYPE;
|
|
td.data = &zero;
|
|
}
|
|
|
|
anymatch = 0; /* no matches yet */
|
|
while (1) {
|
|
rpmtdInit(&td);
|
|
while (rpmtdNext(&td) >= 0) {
|
|
char *str = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
|
|
if (str) {
|
|
rc = miregexec(mire, str);
|
|
if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
|
|
anymatch++;
|
|
free(str);
|
|
}
|
|
}
|
|
if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
|
|
i++;
|
|
mire++;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
rpmtdFreeData(&td);
|
|
|
|
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 & DBC_WRITE) ? 1 : 0;
|
|
if (rewrite)
|
|
mi->mi_cflags |= DBC_WRITE;
|
|
else
|
|
mi->mi_cflags &= ~DBC_WRITE;
|
|
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;
|
|
mi->mi_ts = rpmtsLink(ts);
|
|
mi->mi_hdrchk = hdrchk;
|
|
return rc;
|
|
}
|
|
|
|
static rpmRC miVerifyHeader(rpmdbMatchIterator mi, const void *uh, size_t uhlen)
|
|
{
|
|
rpmRC rpmrc = RPMRC_NOTFOUND;
|
|
|
|
if (!(mi->mi_hdrchk && mi->mi_ts))
|
|
return rpmrc;
|
|
|
|
/* Don't bother re-checking a previously read header. */
|
|
if (mi->mi_db->db_checked) {
|
|
rpmRC *res;
|
|
if (dbChkGetEntry(mi->mi_db->db_checked, mi->mi_offset,
|
|
&res, NULL, NULL)) {
|
|
rpmrc = res[0];
|
|
}
|
|
}
|
|
|
|
/* 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_checked) {
|
|
dbChkAddEntry(mi->mi_db->db_checked, mi->mi_offset, rpmrc);
|
|
}
|
|
}
|
|
return rpmrc;
|
|
}
|
|
|
|
/* FIX: mi->mi_key.data may be NULL */
|
|
Header rpmdbNextIterator(rpmdbMatchIterator mi)
|
|
{
|
|
dbiIndex dbi = NULL;
|
|
unsigned char * uh;
|
|
unsigned int uhlen;
|
|
int rc;
|
|
headerImportFlags importFlags = HEADERIMPORT_FAST;
|
|
|
|
if (mi == NULL)
|
|
return NULL;
|
|
|
|
if (pkgdbOpen(mi->mi_db, 0, &dbi))
|
|
return NULL;
|
|
|
|
#if defined(_USE_COPY_LOAD)
|
|
importFlags |= HEADERIMPORT_COPY;
|
|
#endif
|
|
/*
|
|
* Cursors are per-iterator, not per-dbi, so get a cursor for the
|
|
* iterator on 1st call. If the iteration is to rewrite headers,
|
|
* then the cursor needs to marked with DBC_WRITE as well.
|
|
*/
|
|
if (mi->mi_dbc == NULL)
|
|
mi->mi_dbc = dbiCursorInit(dbi, mi->mi_cflags);
|
|
|
|
top:
|
|
uh = NULL;
|
|
uhlen = 0;
|
|
|
|
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);
|
|
} else {
|
|
rc = pkgdbGet(dbi, mi->mi_dbc, 0, &uh, &uhlen, &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 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) {
|
|
rc = pkgdbGet(dbi, mi->mi_dbc, mi->mi_offset, &uh, &uhlen, NULL);
|
|
if (rc)
|
|
return NULL;
|
|
}
|
|
|
|
/* Rewrite current header (if necessary) and unlink. */
|
|
miFreeHeader(mi, dbi);
|
|
|
|
/* Is this the end of the iteration? */
|
|
if (uh == NULL)
|
|
return NULL;
|
|
|
|
/* Verify header if enabled, skip damaged and inconsistent headers */
|
|
if (miVerifyHeader(mi, uh, uhlen) == RPMRC_FAIL) {
|
|
goto top;
|
|
}
|
|
|
|
/* Did the header blob load correctly? */
|
|
mi->mi_h = headerImport(uh, uhlen, importFlags);
|
|
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)) {
|
|
if (mi->mi_set)
|
|
goto top;
|
|
return NULL;
|
|
}
|
|
headerSetInstance(mi->mi_h, mi->mi_offset);
|
|
|
|
mi->mi_prevoffset = mi->mi_offset;
|
|
mi->mi_modified = 0;
|
|
|
|
return mi->mi_h;
|
|
}
|
|
|
|
/** \ingroup rpmdb
|
|
* sort the iterator by (recnum, filenum)
|
|
* Return database iterator.
|
|
* @param mi rpm database iterator
|
|
*/
|
|
void rpmdbSortIterator(rpmdbMatchIterator mi)
|
|
{
|
|
if (mi && mi->mi_set) {
|
|
dbiIndexSetSort(mi->mi_set);
|
|
mi->mi_sorted = 1;
|
|
}
|
|
}
|
|
|
|
int rpmdbExtendIterator(rpmdbMatchIterator mi,
|
|
const void * keyp, size_t keylen)
|
|
{
|
|
dbiIndex dbi = NULL;
|
|
dbiIndexSet set = NULL;
|
|
int rc = 1; /* assume failure */
|
|
|
|
if (mi == NULL || keyp == NULL)
|
|
return rc;
|
|
|
|
rc = indexOpen(mi->mi_db, mi->mi_rpmtag, 0, &dbi);
|
|
|
|
if (rc == 0 && indexGet(dbi, keyp, keylen, &set) == RPMRC_OK) {
|
|
if (mi->mi_set == NULL) {
|
|
mi->mi_set = set;
|
|
} else {
|
|
dbiIndexSetAppendSet(mi->mi_set, set, 0);
|
|
dbiIndexSetFree(set);
|
|
}
|
|
rc = 0;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int rpmdbPruneIterator(rpmdbMatchIterator mi, removedHash hdrNums)
|
|
{
|
|
if (mi == NULL || hdrNums == NULL || removedHashNumKeys(hdrNums) <= 0)
|
|
return 1;
|
|
|
|
if (!mi->mi_set)
|
|
return 0;
|
|
|
|
unsigned int from;
|
|
unsigned int to = 0;
|
|
unsigned int num = mi->mi_set->count;
|
|
|
|
assert(mi->mi_set->count > 0);
|
|
|
|
for (from = 0; from < num; from++) {
|
|
if (removedHashHasEntry(hdrNums, mi->mi_set->recs[from].hdrNum)) {
|
|
mi->mi_set->count--;
|
|
continue;
|
|
}
|
|
if (from != to)
|
|
mi->mi_set->recs[to] = mi->mi_set->recs[from]; /* structure assignment */
|
|
to++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int rpmdbAppendIterator(rpmdbMatchIterator mi,
|
|
const unsigned int * hdrNums, unsigned int nHdrNums)
|
|
{
|
|
if (mi == NULL || hdrNums == NULL || nHdrNums == 0)
|
|
return 1;
|
|
|
|
if (mi->mi_set == NULL)
|
|
mi->mi_set = dbiIndexSetNew(nHdrNums);
|
|
|
|
for (unsigned int i = 0; i < nHdrNums; i++) {
|
|
struct dbiIndexItem_s rec = { .hdrNum = hdrNums[i], .tagNum = 0 };
|
|
dbiIndexSetAppend(mi->mi_set, &rec, 1, 0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
rpmdbMatchIterator rpmdbNewIterator(rpmdb db, rpmDbiTagVal dbitag)
|
|
{
|
|
rpmdbMatchIterator mi = NULL;
|
|
|
|
if (indexOpen(db, dbitag, 0, NULL))
|
|
return NULL;
|
|
|
|
mi = xcalloc(1, sizeof(*mi));
|
|
mi->mi_set = NULL;
|
|
mi->mi_db = rpmdbLink(db);
|
|
mi->mi_rpmtag = dbitag;
|
|
|
|
mi->mi_dbc = NULL;
|
|
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;
|
|
|
|
/* Chain cursors for teardown on abnormal exit. */
|
|
mi->mi_next = rpmmiRock;
|
|
rpmmiRock = mi;
|
|
|
|
return mi;
|
|
};
|
|
|
|
static rpmdbMatchIterator pkgdbIterInit(rpmdb db,
|
|
const unsigned int * keyp, size_t keylen)
|
|
{
|
|
rpmdbMatchIterator mi = NULL;
|
|
rpmDbiTagVal dbtag = RPMDBI_PACKAGES;
|
|
dbiIndex pkgs = NULL;
|
|
|
|
/* Require a sane keylen if one is specified */
|
|
if (keyp && keylen != sizeof(*keyp))
|
|
return NULL;
|
|
|
|
if (pkgdbOpen(db, 0, &pkgs) == 0) {
|
|
mi = rpmdbNewIterator(db, dbtag);
|
|
if (keyp)
|
|
rpmdbAppendIterator(mi, keyp, 1);
|
|
}
|
|
return mi;
|
|
}
|
|
|
|
static rpmdbMatchIterator indexIterInit(rpmdb db, rpmDbiTagVal rpmtag,
|
|
const void * keyp, size_t keylen)
|
|
{
|
|
rpmdbMatchIterator mi = NULL;
|
|
rpmDbiTagVal dbtag = rpmtag;
|
|
dbiIndex dbi = NULL;
|
|
dbiIndexSet set = NULL;
|
|
|
|
/* Fixup the physical index for our pseudo indexes */
|
|
if (rpmtag == RPMDBI_LABEL) {
|
|
dbtag = RPMDBI_NAME;
|
|
} else if (rpmtag == RPMDBI_INSTFILENAMES) {
|
|
dbtag = RPMDBI_BASENAMES;
|
|
}
|
|
|
|
if (indexOpen(db, dbtag, 0, &dbi) == 0) {
|
|
int rc = 0;
|
|
|
|
if (keyp) {
|
|
if (rpmtag == RPMDBI_LABEL) {
|
|
rc = dbiFindByLabel(db, dbi, keyp, &set);
|
|
} else if (rpmtag == RPMDBI_BASENAMES) {
|
|
rc = rpmdbFindByFile(db, dbi, keyp, 0, &set);
|
|
} else if (rpmtag == RPMDBI_INSTFILENAMES) {
|
|
rc = rpmdbFindByFile(db, dbi, keyp, 1, &set);
|
|
} else {
|
|
rc = indexGet(dbi, keyp, keylen, &set);
|
|
}
|
|
} else {
|
|
/* get all entries from index */
|
|
rc = indexGet(dbi, NULL, 0, &set);
|
|
}
|
|
|
|
if (rc) { /* error/not found */
|
|
set = dbiIndexSetFree(set);
|
|
} else {
|
|
mi = rpmdbNewIterator(db, dbtag);
|
|
mi->mi_set = set;
|
|
|
|
if (keyp) {
|
|
rpmdbSortIterator(mi);
|
|
}
|
|
}
|
|
}
|
|
|
|
return mi;
|
|
}
|
|
|
|
rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmDbiTagVal rpmtag,
|
|
const void * keyp, size_t keylen)
|
|
{
|
|
rpmdbMatchIterator mi = NULL;
|
|
|
|
if (db != NULL) {
|
|
(void) rpmdbCheckSignals();
|
|
|
|
if (rpmtag == RPMDBI_PACKAGES)
|
|
mi = pkgdbIterInit(db, keyp, keylen);
|
|
else
|
|
mi = indexIterInit(db, rpmtag, keyp, keylen);
|
|
}
|
|
|
|
return mi;
|
|
}
|
|
|
|
/*
|
|
* Convert current tag data to db key
|
|
* @param tagdata Tag data container
|
|
* @retval keylen Length of key
|
|
* @return Pointer to key value or NULL to signal skip
|
|
*/
|
|
static const void * td2key(rpmtd tagdata, unsigned int *keylen)
|
|
{
|
|
const void * data = NULL;
|
|
unsigned int size = 0;
|
|
const char *str = NULL;
|
|
|
|
switch (rpmtdType(tagdata)) {
|
|
case RPM_CHAR_TYPE:
|
|
case RPM_INT8_TYPE:
|
|
size = sizeof(uint8_t);
|
|
data = rpmtdGetChar(tagdata);
|
|
break;
|
|
case RPM_INT16_TYPE:
|
|
size = sizeof(uint16_t);
|
|
data = rpmtdGetUint16(tagdata);
|
|
break;
|
|
case RPM_INT32_TYPE:
|
|
size = sizeof(uint32_t);
|
|
data = rpmtdGetUint32(tagdata);
|
|
break;
|
|
case RPM_INT64_TYPE:
|
|
size = sizeof(uint64_t);
|
|
data = rpmtdGetUint64(tagdata);
|
|
break;
|
|
case RPM_BIN_TYPE:
|
|
size = tagdata->count;
|
|
data = tagdata->data;
|
|
break;
|
|
case RPM_STRING_TYPE:
|
|
case RPM_I18NSTRING_TYPE:
|
|
case RPM_STRING_ARRAY_TYPE:
|
|
str = rpmtdGetString(tagdata);
|
|
if (str) {
|
|
size = strlen(str);
|
|
if (size == 0)
|
|
size++; /* fixup for empty strings */
|
|
data = str;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (data && keylen)
|
|
*keylen = size;
|
|
|
|
return data;
|
|
}
|
|
/*
|
|
* rpmdbIndexIterator
|
|
*/
|
|
|
|
rpmdbIndexIterator rpmdbIndexIteratorInit(rpmdb db, rpmDbiTag rpmtag)
|
|
{
|
|
rpmdbIndexIterator ii;
|
|
dbiIndex dbi = NULL;
|
|
|
|
if (db == NULL)
|
|
return NULL;
|
|
|
|
(void) rpmdbCheckSignals();
|
|
|
|
if (indexOpen(db, rpmtag, 0, &dbi))
|
|
return NULL;
|
|
|
|
/* Chain cursors for teardown on abnormal exit. */
|
|
ii = xcalloc(1, sizeof(*ii));
|
|
ii->ii_next = rpmiiRock;
|
|
rpmiiRock = ii;
|
|
|
|
ii->ii_db = rpmdbLink(db);
|
|
ii->ii_rpmtag = rpmtag;
|
|
ii->ii_dbi = dbi;
|
|
ii->ii_set = NULL;
|
|
|
|
return ii;
|
|
}
|
|
|
|
int rpmdbIndexIteratorNext(rpmdbIndexIterator ii, const void ** key, size_t * keylen)
|
|
{
|
|
int rc;
|
|
unsigned int iikeylen = 0; /* argh, size_t vs uint pointer... */
|
|
|
|
if (ii == NULL)
|
|
return -1;
|
|
|
|
if (ii->ii_dbc == NULL)
|
|
ii->ii_dbc = dbiCursorInit(ii->ii_dbi, DBC_READ);
|
|
|
|
/* free old data */
|
|
ii->ii_set = dbiIndexSetFree(ii->ii_set);
|
|
|
|
rc = dbcCursorGet(ii->ii_dbc, NULL, 0, &ii->ii_set);
|
|
|
|
*key = dbiCursorKey(ii->ii_dbc, &iikeylen);
|
|
*keylen = iikeylen;
|
|
|
|
return (rc == RPMRC_OK) ? 0 : -1;
|
|
}
|
|
|
|
int rpmdbIndexIteratorNextTd(rpmdbIndexIterator ii, rpmtd keytd)
|
|
{
|
|
size_t keylen = 0;
|
|
const void * keyp = NULL;
|
|
|
|
int rc = rpmdbIndexIteratorNext(ii, &keyp, &keylen);
|
|
|
|
if (rc == 0) {
|
|
rpmTagVal tag = ii->ii_rpmtag;
|
|
rpmTagClass tagclass = rpmTagGetClass(tag);
|
|
|
|
/* Set the common values, overridden below as necessary */
|
|
keytd->type = rpmTagGetTagType(tag);
|
|
keytd->tag = tag;
|
|
keytd->flags = RPMTD_ALLOCED;
|
|
keytd->count = 1;
|
|
|
|
switch (tagclass) {
|
|
case RPM_STRING_CLASS: {
|
|
/*
|
|
* XXX: We never return arrays here, so everything is a
|
|
* "simple" string. However this can disagree with the
|
|
* type of the index tag, eg requires are string arrays.
|
|
*/
|
|
char *key = memcpy(xmalloc(keylen + 1), keyp, keylen);
|
|
key[keylen] = '\0';
|
|
keytd->data = key;
|
|
keytd->type = RPM_STRING_TYPE;
|
|
} break;
|
|
case RPM_BINARY_CLASS:
|
|
/* Binary types abuse count for data length */
|
|
keytd->count = keylen;
|
|
/* fallthrough */
|
|
case RPM_NUMERIC_CLASS:
|
|
keytd->data = memcpy(xmalloc(keylen), keyp, keylen);
|
|
break;
|
|
default:
|
|
rpmtdReset(keytd);
|
|
rc = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
unsigned int rpmdbIndexIteratorNumPkgs(rpmdbIndexIterator ii)
|
|
{
|
|
return (ii && ii->ii_set) ? dbiIndexSetCount(ii->ii_set) : 0;
|
|
}
|
|
|
|
unsigned int rpmdbIndexIteratorPkgOffset(rpmdbIndexIterator ii, unsigned int nr)
|
|
{
|
|
if (!ii || !ii->ii_set)
|
|
return 0;
|
|
if (dbiIndexSetCount(ii->ii_set) <= nr)
|
|
return 0;
|
|
return dbiIndexRecordOffset(ii->ii_set, nr);
|
|
}
|
|
|
|
unsigned int rpmdbIndexIteratorTagNum(rpmdbIndexIterator ii, unsigned int nr)
|
|
{
|
|
if (!ii || !ii->ii_set)
|
|
return 0;
|
|
if (dbiIndexSetCount(ii->ii_set) <= nr)
|
|
return 0;
|
|
return dbiIndexRecordFileNumber(ii->ii_set, nr);
|
|
}
|
|
|
|
rpmdbIndexIterator rpmdbIndexIteratorFree(rpmdbIndexIterator ii)
|
|
{
|
|
rpmdbIndexIterator * prev, next;
|
|
|
|
if (ii == NULL)
|
|
return NULL;
|
|
|
|
prev = &rpmiiRock;
|
|
while ((next = *prev) != NULL && next != ii)
|
|
prev = &next->ii_next;
|
|
if (next) {
|
|
*prev = next->ii_next;
|
|
next->ii_next = NULL;
|
|
}
|
|
|
|
ii->ii_dbc = dbiCursorFree(ii->ii_dbc);
|
|
ii->ii_dbi = NULL;
|
|
rpmdbClose(ii->ii_db);
|
|
ii->ii_set = dbiIndexSetFree(ii->ii_set);
|
|
|
|
ii = _free(ii);
|
|
return NULL;
|
|
}
|
|
|
|
static void logAddRemove(const char *dbiname, int removing, rpmtd tagdata)
|
|
{
|
|
rpm_count_t c = rpmtdCount(tagdata);
|
|
if (c == 1 && rpmtdType(tagdata) == RPM_STRING_TYPE) {
|
|
rpmlog(RPMLOG_DEBUG, "%s \"%s\" %s %s index.\n",
|
|
removing ? "removing" : "adding", rpmtdGetString(tagdata),
|
|
removing ? "from" : "to", dbiname);
|
|
} else if (c > 0) {
|
|
rpmlog(RPMLOG_DEBUG, "%s %d entries %s %s index.\n",
|
|
removing ? "removing" : "adding", c,
|
|
removing ? "from" : "to", dbiname);
|
|
}
|
|
}
|
|
|
|
static rpmRC indexDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h)
|
|
{
|
|
return tag2index(dbi, rpmtag, hdrNum, h, dbcCursorDel);
|
|
}
|
|
|
|
int rpmdbRemove(rpmdb db, unsigned int hdrNum)
|
|
{
|
|
dbiIndex dbi = NULL;
|
|
Header h;
|
|
sigset_t signalMask;
|
|
int ret = 0;
|
|
|
|
if (db == NULL)
|
|
return 0;
|
|
|
|
h = rpmdbGetHeaderAt(db, hdrNum);
|
|
|
|
if (h == NULL) {
|
|
rpmlog(RPMLOG_ERR, _("%s: cannot read header at 0x%x\n"),
|
|
"rpmdbRemove", hdrNum);
|
|
return 1;
|
|
} else {
|
|
char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
|
|
rpmlog(RPMLOG_DEBUG, " --- h#%8u %s\n", hdrNum, nevra);
|
|
free(nevra);
|
|
}
|
|
|
|
if (pkgdbOpen(db, 0, &dbi))
|
|
return 1;
|
|
|
|
(void) blockSignals(&signalMask);
|
|
|
|
/* Remove header from primary index */
|
|
ret = pkgdbDel(dbi, NULL, hdrNum);
|
|
|
|
/* Remove associated data from secondary indexes */
|
|
if (ret == 0) {
|
|
for (int dbix = 0; dbix < db->db_ndbi; dbix++) {
|
|
rpmDbiTag rpmtag = db->db_tags[dbix];
|
|
|
|
if (indexOpen(db, rpmtag, 0, &dbi))
|
|
continue;
|
|
|
|
ret += indexDel(dbi, rpmtag, hdrNum, h);
|
|
}
|
|
}
|
|
|
|
(void) unblockSignals(&signalMask);
|
|
|
|
headerFree(h);
|
|
|
|
/* XXX return ret; */
|
|
return 0;
|
|
}
|
|
|
|
static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag,
|
|
unsigned int hdrNum, Header h,
|
|
idxfunc idxupdate)
|
|
{
|
|
int i, rc = 0;
|
|
struct rpmtd_s tagdata, reqflags;
|
|
dbiCursor dbc = NULL;
|
|
|
|
switch (rpmtag) {
|
|
case RPMTAG_REQUIRENAME:
|
|
headerGet(h, RPMTAG_REQUIREFLAGS, &reqflags, HEADERGET_MINMEM);
|
|
/* fallthrough */
|
|
default:
|
|
headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM);
|
|
break;
|
|
}
|
|
|
|
if (rpmtdCount(&tagdata) == 0) {
|
|
if (rpmtag != RPMTAG_GROUP)
|
|
goto exit;
|
|
|
|
/* XXX preserve legacy behavior */
|
|
tagdata.type = RPM_STRING_TYPE;
|
|
tagdata.data = (const char **) "Unknown";
|
|
tagdata.count = 1;
|
|
}
|
|
|
|
dbc = dbiCursorInit(dbi, DBC_WRITE);
|
|
|
|
logAddRemove(dbiName(dbi), 0, &tagdata);
|
|
while ((i = rpmtdNext(&tagdata)) >= 0) {
|
|
const void * key = NULL;
|
|
unsigned int keylen = 0;
|
|
int j;
|
|
/* Include the tagNum in all indices (only files use though) */
|
|
struct dbiIndexItem_s rec = { .hdrNum = hdrNum, .tagNum = i };
|
|
|
|
switch (rpmtag) {
|
|
case RPMTAG_REQUIRENAME: {
|
|
/* Filter out install prerequisites. */
|
|
rpm_flag_t *rflag = rpmtdNextUint32(&reqflags);
|
|
if (rflag && isInstallPreReq(*rflag) &&
|
|
!isErasePreReq(*rflag))
|
|
continue;
|
|
break;
|
|
}
|
|
case RPMTAG_TRIGGERNAME:
|
|
if (i > 0) { /* don't add duplicates */
|
|
const char **tnames = tagdata.data;
|
|
const char *str = rpmtdGetString(&tagdata);
|
|
for (j = 0; j < i; j++) {
|
|
if (rstreq(str, tnames[j]))
|
|
break;
|
|
}
|
|
if (j < i)
|
|
continue;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if ((key = td2key(&tagdata, &keylen)) == NULL)
|
|
continue;
|
|
|
|
rc += idxupdate(dbc, key, keylen, &rec);
|
|
}
|
|
|
|
dbiCursorFree(dbc);
|
|
|
|
exit:
|
|
rpmtdFreeData(&tagdata);
|
|
return (rc == 0) ? RPMRC_OK : RPMRC_FAIL;
|
|
}
|
|
static rpmRC indexPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h)
|
|
{
|
|
return tag2index(dbi, rpmtag, hdrNum, h, dbcCursorPut);
|
|
}
|
|
|
|
int rpmdbAdd(rpmdb db, Header h)
|
|
{
|
|
sigset_t signalMask;
|
|
dbiIndex dbi = NULL;
|
|
unsigned int hdrNum = 0;
|
|
unsigned int hdrLen = 0;
|
|
unsigned char *hdrBlob = NULL;
|
|
int ret = 0;
|
|
|
|
if (db == NULL)
|
|
return 0;
|
|
|
|
hdrBlob = headerExport(h, &hdrLen);
|
|
if (hdrBlob == NULL || hdrLen == 0) {
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
|
|
ret = pkgdbOpen(db, 0, &dbi);
|
|
if (ret)
|
|
goto exit;
|
|
|
|
(void) blockSignals(&signalMask);
|
|
|
|
/* Add header to primary index */
|
|
ret = pkgdbPut(dbi, NULL, 0, hdrBlob, hdrLen, &hdrNum);
|
|
|
|
/* Add associated data to secondary indexes */
|
|
if (ret == 0) {
|
|
for (int dbix = 0; dbix < db->db_ndbi; dbix++) {
|
|
rpmDbiTag rpmtag = db->db_tags[dbix];
|
|
|
|
if (indexOpen(db, rpmtag, 0, &dbi))
|
|
continue;
|
|
|
|
ret += indexPut(dbi, rpmtag, hdrNum, h);
|
|
}
|
|
}
|
|
|
|
(void) unblockSignals(&signalMask);
|
|
|
|
/* If everything ok, mark header as installed now */
|
|
if (ret == 0) {
|
|
headerSetInstance(h, hdrNum);
|
|
/* Purge our verification cache on added public keys */
|
|
if (db->db_checked && headerIsEntry(h, RPMTAG_PUBKEYS)) {
|
|
dbChkEmpty(db->db_checked);
|
|
}
|
|
}
|
|
|
|
exit:
|
|
free(hdrBlob);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
* 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 unlinkTag(const char * prefix, const char *dbpath, rpmTagVal dbtag)
|
|
{
|
|
int rc = 0;
|
|
const char * base = rpmTagGetName(dbtag);
|
|
char * path = rpmGetPath(prefix, "/", dbpath, "/", base, NULL);
|
|
if (access(path, F_OK) == 0)
|
|
rc = unlink(path);
|
|
free(path);
|
|
return rc;
|
|
}
|
|
|
|
static int rpmdbRemoveDatabase(const char * prefix, const char * dbpath)
|
|
{
|
|
char *path;
|
|
int xx = 0;
|
|
/* create a handle but dont actually open */
|
|
rpmdb db = newRpmdb(prefix, dbpath, O_RDONLY, 0644, RPMDB_FLAG_REBUILD);
|
|
|
|
xx = unlinkTag(prefix, dbpath, RPMDBI_PACKAGES);
|
|
for (int i = 0; i < db->db_ndbi; i++) {
|
|
xx += unlinkTag(prefix, dbpath, db->db_tags[i]);
|
|
}
|
|
cleanDbenv(prefix, dbpath);
|
|
|
|
path = rpmGetPath(prefix, "/", dbpath, NULL);
|
|
xx += rmdir(path);
|
|
free(path);
|
|
rpmdbClose(db);
|
|
|
|
return (xx != 0);
|
|
}
|
|
|
|
static int renameTag(const char * prefix,
|
|
const char * olddbpath, const char *newdbpath,
|
|
rpmTagVal dbtag)
|
|
{
|
|
int xx, rc = 0;
|
|
const char *base = rpmTagGetName(dbtag);
|
|
char *src = rpmGetPath(prefix, "/", olddbpath, "/", base, NULL);
|
|
char *dest = rpmGetPath(prefix, "/", newdbpath, "/", base, NULL);
|
|
struct stat st;
|
|
|
|
if (access(src, F_OK) == 0) {
|
|
/*
|
|
* Restore uid/gid/mode/security context if possible.
|
|
*/
|
|
if (stat(dest, &st) < 0)
|
|
if (stat(src, &st) < 0)
|
|
goto exit;
|
|
|
|
if ((xx = rename(src, dest)) != 0) {
|
|
rc = 1;
|
|
goto exit;
|
|
}
|
|
xx = chown(dest, st.st_uid, st.st_gid);
|
|
xx = chmod(dest, (st.st_mode & 07777));
|
|
|
|
/* XXX: we should call file prepare plugins here for selinux etc! */
|
|
}
|
|
|
|
exit:
|
|
free(src);
|
|
free(dest);
|
|
return rc;
|
|
}
|
|
|
|
static int rpmdbMoveDatabase(const char * prefix,
|
|
const char * olddbpath, const char * newdbpath)
|
|
{
|
|
int rc = 0;
|
|
sigset_t sigMask;
|
|
/* create a handle but dont actually open */
|
|
rpmdb db = newRpmdb(prefix, newdbpath, O_RDONLY, 0644, RPMDB_FLAG_REBUILD);
|
|
|
|
blockSignals(&sigMask);
|
|
rc = renameTag(prefix, olddbpath, newdbpath, RPMDBI_PACKAGES);
|
|
for (int i = 0; i < db->db_ndbi; i++) {
|
|
rc += renameTag(prefix, olddbpath, newdbpath, db->db_tags[i]);
|
|
}
|
|
|
|
cleanDbenv(prefix, olddbpath);
|
|
cleanDbenv(prefix, newdbpath);
|
|
rpmdbClose(db);
|
|
|
|
unblockSignals(&sigMask);
|
|
|
|
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;
|
|
int nocleanup = 1;
|
|
int failed = 0;
|
|
int removedir = 0;
|
|
int rc = 0;
|
|
|
|
dbpath = rpmGetPath("%{?_dbpath}", NULL);
|
|
if (rstreq(dbpath, "")) {
|
|
rpmlog(RPMLOG_ERR, _("no dbpath has been set"));
|
|
rc = 1;
|
|
goto exit;
|
|
}
|
|
rootdbpath = rpmGetPath(prefix, dbpath, NULL);
|
|
|
|
newdbpath = rpmGetPath("%{?_dbpath_rebuild}", NULL);
|
|
if (rstreq(newdbpath, "") || rstreq(newdbpath, dbpath)) {
|
|
newdbpath = _free(newdbpath);
|
|
rasprintf(&newdbpath, "%srebuilddb.%d", dbpath, (int) getpid());
|
|
nocleanup = 0;
|
|
}
|
|
newrootdbpath = rpmGetPath(prefix, newdbpath, NULL);
|
|
|
|
rpmlog(RPMLOG_DEBUG, "rebuilding database %s into %s\n",
|
|
rootdbpath, newrootdbpath);
|
|
|
|
if (mkdir(newrootdbpath, 0755)) {
|
|
rpmlog(RPMLOG_ERR, _("failed to create directory %s: %s\n"),
|
|
newrootdbpath, strerror(errno));
|
|
rc = 1;
|
|
goto exit;
|
|
}
|
|
removedir = 1;
|
|
|
|
if (openDatabase(prefix, dbpath, &olddb,
|
|
O_RDONLY, 0644, RPMDB_FLAG_REBUILD)) {
|
|
rc = 1;
|
|
goto exit;
|
|
}
|
|
if (openDatabase(prefix, newdbpath, &newdb,
|
|
(O_RDWR | O_CREAT), 0644, RPMDB_FLAG_REBUILD)) {
|
|
rc = 1;
|
|
goto exit;
|
|
}
|
|
|
|
{ Header h = NULL;
|
|
rpmdbMatchIterator 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"),
|
|
rpmdbGetIteratorOffset(mi));
|
|
continue;
|
|
}
|
|
|
|
/* Deleted entries are eliminated in legacy headers by copy. */
|
|
{ Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
|
|
? headerCopy(h) : NULL);
|
|
rc = rpmdbAdd(newdb, (nh ? nh : h));
|
|
headerFree(nh);
|
|
}
|
|
|
|
if (rc) {
|
|
rpmlog(RPMLOG_ERR, _("cannot add record originally at %u\n"),
|
|
rpmdbGetIteratorOffset(mi));
|
|
failed = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
rpmdbFreeIterator(mi);
|
|
|
|
}
|
|
|
|
rpmdbClose(olddb);
|
|
rpmdbClose(newdb);
|
|
|
|
if (failed) {
|
|
rpmlog(RPMLOG_WARNING,
|
|
_("failed to rebuild database: original database "
|
|
"remains in place\n"));
|
|
|
|
rpmdbRemoveDatabase(prefix, newdbpath);
|
|
rc = 1;
|
|
goto exit;
|
|
} else if (!nocleanup) {
|
|
if (rpmdbMoveDatabase(prefix, newdbpath, dbpath)) {
|
|
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)) {
|
|
if (rmdir(newrootdbpath))
|
|
rpmlog(RPMLOG_ERR, _("failed to remove directory %s: %s\n"),
|
|
newrootdbpath, strerror(errno));
|
|
}
|
|
free(newdbpath);
|
|
free(dbpath);
|
|
free(newrootdbpath);
|
|
free(rootdbpath);
|
|
|
|
return rc;
|
|
}
|