1222 lines
28 KiB
C
1222 lines
28 KiB
C
#include "system.h"
|
|
|
|
#include <sys/file.h>
|
|
#include <signal.h>
|
|
#include <sys/signal.h>
|
|
|
|
#include <rpmlib.h>
|
|
#include <rpmurl.h>
|
|
#include <rpmmacro.h> /* XXX for rpmGetPath */
|
|
|
|
#include "dbindex.h"
|
|
/*@access dbiIndexSet@*/
|
|
/*@access dbiIndexRecord@*/
|
|
/*@access rpmdbMatchIterator@*/
|
|
|
|
#include "falloc.h"
|
|
#include "fprint.h"
|
|
#include "misc.h"
|
|
#include "rpmdb.h"
|
|
|
|
extern int _noDirTokens;
|
|
extern int _useDbiMajor;
|
|
|
|
#define _DBI_FLAGS 0
|
|
#define _DBI_PERMS 0644
|
|
#define _DBI_MAJOR -1
|
|
|
|
struct _dbiIndex rpmdbi[] = {
|
|
{ "packages.rpm", 0,
|
|
DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ "nameindex.rpm", RPMTAG_NAME,
|
|
DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ "fileindex.rpm", RPMTAG_BASENAMES,
|
|
DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ "groupindex.rpm", RPMTAG_GROUP,
|
|
DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ "requiredby.rpm", RPMTAG_REQUIRENAME,
|
|
DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ "providesindex.rpm", RPMTAG_PROVIDENAME,
|
|
DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ "conflictsindex.rpm", RPMTAG_CONFLICTNAME,
|
|
DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ "triggerindex.rpm", RPMTAG_TRIGGERNAME,
|
|
DBI_HASH, _DBI_FLAGS, _DBI_PERMS, _DBI_MAJOR, 0,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL },
|
|
{ NULL }
|
|
#define RPMDBI_MIN 0
|
|
#define RPMDBI_MAX 8
|
|
};
|
|
|
|
/* XXX the signal handling in here is not thread safe */
|
|
|
|
/* the requiredbyIndex isn't stricly necessary. In a perfect world, we could
|
|
have each header keep a list of packages that need it. However, we
|
|
can't reserve space in the header for extra information so all of the
|
|
required packages would move in the database every time a package was
|
|
added or removed. Instead, each package (or virtual package) name
|
|
keeps a list of package offsets of packages that might depend on this
|
|
one. Version numbers still need verification, but it gets us in the
|
|
right area w/o a linear search through the database. */
|
|
|
|
struct rpmdb_s {
|
|
dbiIndex _dbi[RPMDBI_MAX];
|
|
#ifdef DYING
|
|
#define nameIndex _dbi[RPMDBI_NAME]
|
|
#define fileIndex _dbi[RPMDBI_FILE]
|
|
#define groupIndex _dbi[RPMDBI_GROUP]
|
|
#define requiredbyIndex _dbi[RPMDBI_REQUIREDBY]
|
|
#define providesIndex _dbi[RPMDBI_PROVIDES]
|
|
#define conflictsIndex _dbi[RPMDBI_CONFLICTS]
|
|
#define triggerIndex _dbi[RPMDBI_TRIGGER]
|
|
#endif
|
|
};
|
|
|
|
static sigset_t signalMask;
|
|
|
|
static void blockSignals(void)
|
|
{
|
|
sigset_t newMask;
|
|
|
|
sigfillset(&newMask); /* block all signals */
|
|
sigprocmask(SIG_BLOCK, &newMask, &signalMask);
|
|
}
|
|
|
|
static void unblockSignals(void)
|
|
{
|
|
sigprocmask(SIG_SETMASK, &signalMask, NULL);
|
|
}
|
|
|
|
static int openDbFile(const char * prefix, const char * dbpath,
|
|
const dbiIndex dbi, int justCheck, int mode, dbiIndex * dbip)
|
|
{
|
|
char * filename, * fn;
|
|
int len;
|
|
|
|
if (dbi == NULL || dbip == NULL)
|
|
return 1;
|
|
*dbip = NULL;
|
|
|
|
len = (prefix ? strlen(prefix) : 0) +
|
|
strlen(dbpath) + strlen(dbi->dbi_basename) + 1;
|
|
fn = filename = alloca(len);
|
|
*fn = '\0';
|
|
switch (urlIsURL(dbpath)) {
|
|
case URL_IS_UNKNOWN:
|
|
if (prefix && *prefix &&
|
|
!(prefix[0] == '/' && prefix[1] == '\0' && dbpath[0] == '/'))
|
|
fn = stpcpy(fn, prefix);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
fn = stpcpy(fn, dbpath);
|
|
if (fn > filename && !(fn[-1] == '/' || dbi->dbi_basename[0] == '/'))
|
|
fn = stpcpy(fn, "/");
|
|
fn = stpcpy(fn, dbi->dbi_basename);
|
|
|
|
if (!justCheck || !rpmfileexists(filename)) {
|
|
if ((*dbip = dbiOpenIndex(filename, mode, dbi)) == NULL)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static /*@only@*/ rpmdb newRpmdb(void)
|
|
{
|
|
rpmdb db = xcalloc(sizeof(*db), 1);
|
|
return db;
|
|
}
|
|
|
|
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;
|
|
int minimal = flags & RPMDB_FLAG_MINIMAL;
|
|
|
|
if (dbp)
|
|
*dbp = NULL;
|
|
if (mode & O_WRONLY)
|
|
return 1;
|
|
|
|
if (!(perms & 0600)) /* XXX sanity */
|
|
perms = 0644;
|
|
|
|
/* we should accept NULL as a valid prefix */
|
|
if (!prefix) prefix="";
|
|
|
|
db = newRpmdb();
|
|
|
|
{ int dbix;
|
|
|
|
rc = 0;
|
|
for (dbix = RPMDBI_MIN; rc == 0 && dbix < RPMDBI_MAX; dbix++) {
|
|
dbiIndex dbiTemplate;
|
|
|
|
dbiTemplate = rpmdbi + dbix;
|
|
|
|
rc = openDbFile(prefix, dbpath, dbiTemplate, justcheck, mode,
|
|
&db->_dbi[dbix]);
|
|
if (rc)
|
|
continue;
|
|
|
|
switch (dbix) {
|
|
case 1:
|
|
if (minimal)
|
|
goto exit;
|
|
break;
|
|
case 2:
|
|
{ void * keyp = NULL;
|
|
|
|
/* We used to store the fileindexes as complete paths, rather then
|
|
plain basenames. Let's see which version we are... */
|
|
/*
|
|
* XXX FIXME: db->fileindex can be NULL under pathological (e.g. mixed
|
|
* XXX db1/db2 linkage) conditions.
|
|
*/
|
|
if (!justcheck && !dbiGetNextKey(db->_dbi[RPMDBI_FILE], &keyp, NULL)) {
|
|
const char * akey;
|
|
akey = keyp;
|
|
if (strchr(akey, '/')) {
|
|
rpmError(RPMERR_OLDDB, _("old format database is present; "
|
|
"use --rebuilddb to generate a new format database"));
|
|
rc |= 1;
|
|
}
|
|
dbiFreeCursor(db->_dbi[RPMDBI_FILE]);
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
if (!(rc || justcheck || dbp == NULL))
|
|
*dbp = db;
|
|
else
|
|
rpmdbClose(db);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int doRpmdbOpen (const char * prefix, /*@out@*/ rpmdb * dbp,
|
|
int mode, int perms, int flags)
|
|
{
|
|
const char * dbpath = rpmGetPath("%{_dbpath}", NULL);
|
|
int rc;
|
|
|
|
if (!(dbpath && dbpath[0] != '%')) {
|
|
rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
|
|
rc = 1;
|
|
} else
|
|
rc = openDatabase(prefix, dbpath, dbp, mode, perms, flags);
|
|
xfree(dbpath);
|
|
return rc;
|
|
}
|
|
|
|
/* XXX called from python/upgrade.c */
|
|
int rpmdbOpenForTraversal(const char * prefix, rpmdb * dbp)
|
|
{
|
|
return doRpmdbOpen(prefix, dbp, O_RDONLY, 0644, RPMDB_FLAG_MINIMAL);
|
|
}
|
|
|
|
/* XXX called from python/rpmmodule.c */
|
|
int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
|
|
{
|
|
return doRpmdbOpen(prefix, dbp, mode, perms, 0);
|
|
}
|
|
|
|
int rpmdbInit (const char * prefix, int perms)
|
|
{
|
|
rpmdb db;
|
|
return doRpmdbOpen(prefix, &db, (O_CREAT | O_RDWR), perms, RPMDB_FLAG_JUSTCHECK);
|
|
}
|
|
|
|
void rpmdbClose (rpmdb db)
|
|
{
|
|
int dbix;
|
|
|
|
for (dbix = RPMDBI_MAX; --dbix >= RPMDBI_MIN; ) {
|
|
if (db->_dbi[dbix] == NULL)
|
|
continue;
|
|
dbiCloseIndex(db->_dbi[dbix]);
|
|
db->_dbi[dbix] = NULL;
|
|
}
|
|
free(db);
|
|
}
|
|
|
|
Header rpmdbGetRecord(rpmdb db, unsigned int offset)
|
|
{
|
|
dbiIndex dbi = db->_dbi[RPMDBI_PACKAGES];
|
|
void * uh;
|
|
size_t uhlen;
|
|
void * keyp = &offset;
|
|
size_t keylen = sizeof(offset);
|
|
int rc;
|
|
|
|
rc = (*dbi->dbi_vec->get) (dbi, keyp, keylen, &uh, &uhlen);
|
|
if (rc)
|
|
return NULL;
|
|
return headerLoad(uh);
|
|
}
|
|
|
|
static int rpmdbFindByFile(rpmdb db, const char * filespec,
|
|
/*@out@*/ dbiIndexSet * matches)
|
|
{
|
|
const char * dirName;
|
|
const char * baseName;
|
|
fingerPrintCache fpc;
|
|
fingerPrint fp1;
|
|
dbiIndexSet allMatches = NULL;
|
|
dbiIndexRecord rec = NULL;
|
|
int i;
|
|
int rc;
|
|
|
|
*matches = NULL;
|
|
if ((baseName = strrchr(filespec, '/')) != NULL) {
|
|
char * t;
|
|
size_t len;
|
|
|
|
len = baseName - filespec + 1;
|
|
t = strncpy(alloca(len + 1), filespec, len);
|
|
t[len] = '\0';
|
|
dirName = t;
|
|
baseName++;
|
|
} else {
|
|
dirName = "";
|
|
baseName = filespec;
|
|
}
|
|
|
|
fpc = fpCacheCreate(20);
|
|
fp1 = fpLookup(fpc, dirName, baseName, 1);
|
|
|
|
rc = dbiSearchIndex(db->_dbi[RPMDBI_FILE], baseName, 0, &allMatches);
|
|
if (rc) {
|
|
dbiFreeIndexSet(allMatches);
|
|
allMatches = NULL;
|
|
fpCacheFree(fpc);
|
|
return rc;
|
|
}
|
|
|
|
*matches = dbiCreateIndexSet();
|
|
rec = dbiReturnIndexRecordInstance(0, 0);
|
|
i = 0;
|
|
while (i < allMatches->count) {
|
|
const char ** baseNames, ** dirNames;
|
|
int_32 * dirIndexes;
|
|
unsigned int offset = dbiIndexRecordOffset(allMatches, i);
|
|
unsigned int prevoff;
|
|
Header h;
|
|
|
|
if ((h = rpmdbGetRecord(db, offset)) == NULL) {
|
|
i++;
|
|
continue;
|
|
}
|
|
|
|
headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL,
|
|
(void **) &baseNames, NULL);
|
|
headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL,
|
|
(void **) &dirIndexes, NULL);
|
|
headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL,
|
|
(void **) &dirNames, NULL);
|
|
|
|
do {
|
|
fingerPrint fp2;
|
|
int num = dbiIndexRecordFileNumber(allMatches, i);
|
|
|
|
fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
|
|
if (FP_EQUAL(fp1, fp2)) {
|
|
rec->recOffset = dbiIndexRecordOffset(allMatches, i);
|
|
rec->fileNumber = dbiIndexRecordFileNumber(allMatches, i);
|
|
dbiAppendIndexRecord(*matches, rec);
|
|
}
|
|
|
|
prevoff = offset;
|
|
i++;
|
|
offset = dbiIndexRecordOffset(allMatches, i);
|
|
} while (i < allMatches->count &&
|
|
(i == 0 || offset == prevoff));
|
|
|
|
free(baseNames);
|
|
free(dirNames);
|
|
headerFree(h);
|
|
}
|
|
|
|
if (rec) {
|
|
dbiFreeIndexRecordInstance(rec);
|
|
rec = NULL;
|
|
}
|
|
if (allMatches) {
|
|
dbiFreeIndexSet(allMatches);
|
|
allMatches = NULL;
|
|
}
|
|
|
|
fpCacheFree(fpc);
|
|
|
|
if ((*matches)->count == 0) {
|
|
dbiFreeIndexSet(*matches);
|
|
*matches = NULL;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rpmdbFindPackage(rpmdb db, const char * name, dbiIndexSet * matches) {
|
|
return dbiSearchIndex(db->_dbi[RPMDBI_NAME], name, 0, matches);
|
|
}
|
|
|
|
int rpmdbCountPackages(rpmdb db, const char * name)
|
|
{
|
|
dbiIndexSet matches = NULL;
|
|
int rc;
|
|
|
|
rc = dbiSearchIndex(db->_dbi[RPMDBI_NAME], name, 0, &matches);
|
|
|
|
switch (rc) {
|
|
default:
|
|
case -1: /* error */
|
|
rpmError(RPMERR_DBCORRUPT, _("cannot retrieve package \"%s\" from db"),
|
|
name);
|
|
rc = -1;
|
|
break;
|
|
case 1: /* not found */
|
|
rc = 0;
|
|
break;
|
|
case 0: /* success */
|
|
rc = dbiIndexSetCount(matches);
|
|
break;
|
|
}
|
|
|
|
if (matches)
|
|
dbiFreeIndexSet(matches);
|
|
|
|
return rc;
|
|
}
|
|
|
|
struct _rpmdbMatchIterator {
|
|
const void * mi_key;
|
|
size_t mi_keylen;
|
|
rpmdb mi_db;
|
|
dbiIndex mi_dbi;
|
|
int mi_dbix;
|
|
dbiIndexSet mi_set;
|
|
int mi_setx;
|
|
Header mi_h;
|
|
unsigned int mi_offset;
|
|
const char * mi_version;
|
|
const char * mi_release;
|
|
};
|
|
|
|
void rpmdbFreeIterator(rpmdbMatchIterator mi)
|
|
{
|
|
if (mi == NULL)
|
|
return;
|
|
|
|
if (mi->mi_release) {
|
|
xfree(mi->mi_release);
|
|
mi->mi_release = NULL;
|
|
}
|
|
if (mi->mi_version) {
|
|
xfree(mi->mi_version);
|
|
mi->mi_version = NULL;
|
|
}
|
|
if (mi->mi_h) {
|
|
headerFree(mi->mi_h);
|
|
mi->mi_h = NULL;
|
|
}
|
|
if (mi->mi_set) {
|
|
dbiFreeIndexSet(mi->mi_set);
|
|
mi->mi_set = NULL;
|
|
} else {
|
|
dbiIndex dbi = mi->mi_db->_dbi[RPMDBI_PACKAGES];
|
|
(void) (*dbi->dbi_vec->cclose) (dbi);
|
|
}
|
|
if (mi->mi_key) {
|
|
xfree(mi->mi_key);
|
|
mi->mi_key = NULL;
|
|
}
|
|
free(mi);
|
|
}
|
|
|
|
unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
|
|
if (mi == NULL)
|
|
return 0;
|
|
return mi->mi_offset;
|
|
}
|
|
|
|
int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
|
|
if (!(mi && mi->mi_set))
|
|
return 0; /* XXX W2DO? */
|
|
return mi->mi_set->count;
|
|
}
|
|
|
|
void rpmdbSetIteratorRelease(rpmdbMatchIterator mi, const char * release) {
|
|
if (mi == NULL)
|
|
return;
|
|
if (mi->mi_release) {
|
|
xfree(mi->mi_release);
|
|
mi->mi_release = NULL;
|
|
}
|
|
mi->mi_release = (release ? xstrdup(release) : NULL);
|
|
}
|
|
|
|
void rpmdbSetIteratorVersion(rpmdbMatchIterator mi, const char * version) {
|
|
if (mi == NULL)
|
|
return;
|
|
if (mi->mi_version) {
|
|
xfree(mi->mi_version);
|
|
mi->mi_version = NULL;
|
|
}
|
|
mi->mi_version = (version ? xstrdup(version) : NULL);
|
|
}
|
|
|
|
Header rpmdbNextIterator(rpmdbMatchIterator mi)
|
|
{
|
|
dbiIndex dbi;
|
|
void * uh;
|
|
size_t uhlen;
|
|
void * keyp;
|
|
size_t keylen;
|
|
int rc;
|
|
|
|
if (mi == NULL)
|
|
return NULL;
|
|
|
|
dbi = mi->mi_db->_dbi[RPMDBI_PACKAGES];
|
|
keyp = &mi->mi_offset;
|
|
keylen = sizeof(mi->mi_offset);
|
|
|
|
top:
|
|
/* XXX skip over instances with 0 join key */
|
|
do {
|
|
if (mi->mi_set) {
|
|
if (!(mi->mi_setx < mi->mi_set->count))
|
|
return NULL;
|
|
mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
|
|
mi->mi_setx++;
|
|
} else {
|
|
rc = (*dbi->dbi_vec->cget) (dbi, &keyp, &keylen, NULL, NULL);
|
|
if (rc)
|
|
return NULL;
|
|
memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
|
|
}
|
|
} while (mi->mi_offset == 0);
|
|
|
|
/* Retrieve header */
|
|
rc = (*dbi->dbi_vec->get) (dbi, keyp, keylen, &uh, &uhlen);
|
|
if (rc)
|
|
return NULL;
|
|
|
|
if (mi->mi_h) {
|
|
headerFree(mi->mi_h);
|
|
mi->mi_h = NULL;
|
|
}
|
|
mi->mi_h = headerLoad(uh);
|
|
|
|
if (mi->mi_release) {
|
|
const char *release;
|
|
headerNVR(mi->mi_h, NULL, NULL, &release);
|
|
if (strcmp(mi->mi_release, release))
|
|
goto top;
|
|
}
|
|
|
|
if (mi->mi_version) {
|
|
const char *version;
|
|
headerNVR(mi->mi_h, NULL, &version, NULL);
|
|
if (strcmp(mi->mi_version, version))
|
|
goto top;
|
|
}
|
|
|
|
return mi->mi_h;
|
|
}
|
|
|
|
rpmdbMatchIterator rpmdbInitIterator(rpmdb db, int dbix, const void * key, size_t keylen)
|
|
{
|
|
rpmdbMatchIterator mi;
|
|
dbiIndex dbi = NULL;
|
|
dbiIndexSet set = NULL;
|
|
|
|
dbi = db->_dbi[dbix];
|
|
|
|
if (key) {
|
|
int rc;
|
|
if (dbix == RPMDBI_FILE) {
|
|
rc = rpmdbFindByFile(db, key, &set);
|
|
} else {
|
|
rc = dbiSearchIndex(dbi, key, keylen, &set);
|
|
}
|
|
switch (rc) {
|
|
default:
|
|
case -1: /* error */
|
|
case 1: /* not found */
|
|
if (set)
|
|
dbiFreeIndexSet(set);
|
|
return NULL;
|
|
/*@notreached@*/ break;
|
|
case 0: /* success */
|
|
break;
|
|
}
|
|
}
|
|
|
|
mi = xcalloc(sizeof(*mi), 1);
|
|
if (key) {
|
|
if (keylen == 0)
|
|
keylen = strlen(key);
|
|
|
|
{ char * k = xmalloc(keylen + 1);
|
|
memcpy(k, key, keylen);
|
|
k[keylen] = '\0'; /* XXX for strings */
|
|
mi->mi_key = k;
|
|
}
|
|
|
|
mi->mi_keylen = keylen;
|
|
} else {
|
|
mi->mi_key = NULL;
|
|
mi->mi_keylen = 0;
|
|
}
|
|
mi->mi_db = db;
|
|
mi->mi_dbi = dbi;
|
|
assert(dbi->dbi_dbcursor == NULL);
|
|
mi->mi_dbix = dbix;
|
|
mi->mi_set = set;
|
|
mi->mi_setx = 0;
|
|
mi->mi_h = NULL;
|
|
mi->mi_offset = 0;
|
|
mi->mi_version = NULL;
|
|
mi->mi_release = NULL;
|
|
return mi;
|
|
}
|
|
|
|
static void removeIndexEntry(dbiIndex dbi, const char * key, dbiIndexRecord rec,
|
|
int tolerant, const char * idxName)
|
|
{
|
|
dbiIndexSet matches = NULL;
|
|
int rc;
|
|
|
|
rc = dbiSearchIndex(dbi, key, 0, &matches);
|
|
switch (rc) {
|
|
case 0:
|
|
if (dbiRemoveIndexRecord(matches, rec)) {
|
|
if (!tolerant)
|
|
rpmError(RPMERR_DBCORRUPT, _("package not found with key \"%s\" in %s"),
|
|
key, idxName);
|
|
} else {
|
|
dbiUpdateIndex(dbi, key, matches);
|
|
/* errors from above will be reported from dbindex.c */
|
|
}
|
|
break;
|
|
case 1:
|
|
if (!tolerant)
|
|
rpmError(RPMERR_DBCORRUPT, _("key \"%s\" not found in %s"),
|
|
key, idxName);
|
|
break;
|
|
case 2:
|
|
break; /* error message already generated from dbindex.c */
|
|
}
|
|
if (matches) {
|
|
dbiFreeIndexSet(matches);
|
|
matches = NULL;
|
|
}
|
|
}
|
|
|
|
int rpmdbRemove(rpmdb db, unsigned int offset, int tolerant)
|
|
{
|
|
Header h;
|
|
|
|
h = rpmdbGetRecord(db, offset);
|
|
if (h == NULL) {
|
|
rpmError(RPMERR_DBCORRUPT, _("rpmdbRemove: cannot read header at 0x%x"),
|
|
offset);
|
|
return 1;
|
|
}
|
|
|
|
blockSignals();
|
|
|
|
{ int dbix;
|
|
dbiIndexRecord rec = dbiReturnIndexRecordInstance(offset, 0);
|
|
|
|
for (dbix = RPMDBI_MIN; dbix < RPMDBI_MAX; dbix++) {
|
|
dbiIndex dbi;
|
|
const char **rpmvals = NULL;
|
|
int rpmtype = 0;
|
|
int rpmcnt = 0;
|
|
|
|
dbi = db->_dbi[dbix];
|
|
|
|
if (dbi->dbi_rpmtag == 0) {
|
|
(void) (*dbi->dbi_vec->del) (dbi, &offset, sizeof(offset));
|
|
continue;
|
|
}
|
|
|
|
if (!headerGetEntry(h, dbi->dbi_rpmtag, &rpmtype,
|
|
(void **) &rpmvals, &rpmcnt)) {
|
|
rpmMessage(RPMMESS_DEBUG, _("removing 0 %s entries.\n"),
|
|
tagName(dbi->dbi_rpmtag));
|
|
continue;
|
|
}
|
|
|
|
if (rpmtype == RPM_STRING_TYPE) {
|
|
rpmMessage(RPMMESS_DEBUG, _("removing \"%s\" from %s index.\n"),
|
|
(const char *)rpmvals, tagName(dbi->dbi_rpmtag));
|
|
|
|
removeIndexEntry(dbi, (const char *)rpmvals,
|
|
rec, tolerant, dbi->dbi_basename);
|
|
} else {
|
|
int i, mytolerant;
|
|
|
|
rpmMessage(RPMMESS_DEBUG, _("removing %d entries in %s index:\n"),
|
|
rpmcnt, tagName(dbi->dbi_rpmtag));
|
|
|
|
for (i = 0; i < rpmcnt; i++) {
|
|
rpmMessage(RPMMESS_DEBUG, _("\t%6d %s\n"),
|
|
i, rpmvals[i]);
|
|
|
|
mytolerant = tolerant;
|
|
rec->fileNumber = 0;
|
|
|
|
switch (dbi->dbi_rpmtag) {
|
|
case RPMTAG_BASENAMES:
|
|
rec->fileNumber = i;
|
|
break;
|
|
/*
|
|
* There could be dups in the sorted list. Rather then
|
|
* sort the list, be tolerant of missing entries as they
|
|
* should just indicate duplicated entries.
|
|
*/
|
|
case RPMTAG_REQUIRENAME:
|
|
case RPMTAG_TRIGGERNAME:
|
|
mytolerant = 1;
|
|
break;
|
|
}
|
|
|
|
removeIndexEntry(dbi, rpmvals[i],
|
|
rec, mytolerant, dbi->dbi_basename);
|
|
}
|
|
}
|
|
|
|
dbiSyncIndex(dbi);
|
|
|
|
switch (rpmtype) {
|
|
case RPM_STRING_ARRAY_TYPE:
|
|
case RPM_I18NSTRING_TYPE:
|
|
xfree(rpmvals);
|
|
rpmvals = NULL;
|
|
break;
|
|
}
|
|
rpmtype = 0;
|
|
rpmcnt = 0;
|
|
}
|
|
dbiFreeIndexRecordInstance(rec);
|
|
}
|
|
|
|
unblockSignals();
|
|
|
|
headerFree(h);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int addIndexEntry(dbiIndex dbi, const char *index, dbiIndexRecord rec)
|
|
{
|
|
dbiIndexSet set = NULL;
|
|
int rc;
|
|
|
|
rc = dbiSearchIndex(dbi, index, 0, &set);
|
|
|
|
switch (rc) {
|
|
case -1: /* error */
|
|
if (set) {
|
|
dbiFreeIndexSet(set);
|
|
set = NULL;
|
|
}
|
|
return 1;
|
|
/*@notreached@*/ break;
|
|
case 1: /* new item */
|
|
set = dbiCreateIndexSet();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
dbiAppendIndexRecord(set, rec);
|
|
if (dbiUpdateIndex(dbi, index, set))
|
|
exit(EXIT_FAILURE); /* XXX W2DO? return 1; */
|
|
|
|
if (set) {
|
|
dbiFreeIndexSet(set);
|
|
set = NULL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rpmdbAdd(rpmdb db, Header h)
|
|
{
|
|
const char ** baseNames;
|
|
int count = 0;
|
|
int type;
|
|
dbiIndex dbi;
|
|
unsigned int offset;
|
|
int rc = 0;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
headerGetEntry(h, RPMTAG_BASENAMES, &type, (void **) &baseNames, &count);
|
|
|
|
if (_noDirTokens)
|
|
expandFilelist(h);
|
|
|
|
blockSignals();
|
|
|
|
{
|
|
unsigned int firstkey = 0;
|
|
void * keyp = &firstkey;
|
|
size_t keylen = sizeof(firstkey);
|
|
void * datap = NULL;
|
|
size_t datalen = 0;
|
|
int rc;
|
|
|
|
dbi = db->_dbi[RPMDBI_PACKAGES];
|
|
|
|
/* XXX hack to pass sizeof header to fadAlloc */
|
|
datap = h;
|
|
datalen = headerSizeof(h, HEADER_MAGIC_NO);
|
|
|
|
/* Retrieve join key for next header instance. */
|
|
|
|
rc = (*dbi->dbi_vec->get) (dbi, keyp, keylen, (void *)&datap, &datalen);
|
|
offset = 0;
|
|
if (rc == 0 && datap)
|
|
memcpy(&offset, datap, sizeof(offset));
|
|
++offset;
|
|
if (datap) {
|
|
memcpy(datap, &offset, sizeof(offset));
|
|
} else {
|
|
datap = &offset;
|
|
datalen = sizeof(offset);
|
|
}
|
|
rc = (*dbi->dbi_vec->put) (dbi, keyp, keylen, datap, datalen);
|
|
}
|
|
|
|
if (rc) {
|
|
rpmError(RPMERR_DBCORRUPT, _("cannot allocate new instance in database"));
|
|
goto exit;
|
|
}
|
|
|
|
/* Now update the indexes */
|
|
|
|
{ int dbix;
|
|
dbiIndexRecord rec = dbiReturnIndexRecordInstance(offset, 0);
|
|
|
|
for (dbix = RPMDBI_MIN; dbix < RPMDBI_MAX; dbix++) {
|
|
const char **rpmvals = NULL;
|
|
int rpmtype = 0;
|
|
int rpmcnt = 0;
|
|
|
|
dbi = db->_dbi[dbix];
|
|
|
|
if (dbi->dbi_rpmtag == 0) {
|
|
size_t uhlen = headerSizeof(h, HEADER_MAGIC_NO);
|
|
void * uh = headerUnload(h);
|
|
(void) (*dbi->dbi_vec->put) (dbi, &offset, sizeof(offset), uh, uhlen);
|
|
free(uh);
|
|
continue;
|
|
}
|
|
|
|
/* XXX preserve legacy behavior */
|
|
switch (dbi->dbi_rpmtag) {
|
|
case RPMTAG_BASENAMES:
|
|
rpmtype = type;
|
|
rpmvals = baseNames;
|
|
rpmcnt = count;
|
|
break;
|
|
default:
|
|
headerGetEntry(h, dbi->dbi_rpmtag, &rpmtype,
|
|
(void **) &rpmvals, &rpmcnt);
|
|
break;
|
|
}
|
|
|
|
if (rpmcnt <= 0) {
|
|
if (dbi->dbi_rpmtag != RPMTAG_GROUP) {
|
|
rpmMessage(RPMMESS_DEBUG, _("adding 0 %s entries.\n"),
|
|
tagName(dbi->dbi_rpmtag));
|
|
continue;
|
|
}
|
|
|
|
/* XXX preserve legacy behavior */
|
|
rpmtype = RPM_STRING_TYPE;
|
|
rpmvals = (const char **) "Unknown";
|
|
rpmcnt = 1;
|
|
}
|
|
|
|
if (rpmtype == RPM_STRING_TYPE) {
|
|
rpmMessage(RPMMESS_DEBUG, _("adding \"%s\" to %s index.\n"),
|
|
(const char *)rpmvals, tagName(dbi->dbi_rpmtag));
|
|
|
|
rc += addIndexEntry(dbi, (const char *)rpmvals, rec);
|
|
} else {
|
|
int i, j;
|
|
|
|
rpmMessage(RPMMESS_DEBUG, _("adding %d entries to %s index:\n"),
|
|
rpmcnt, tagName(dbi->dbi_rpmtag));
|
|
|
|
for (i = 0; i < rpmcnt; i++) {
|
|
rpmMessage(RPMMESS_DEBUG, _("\t%6d %s\n"),
|
|
i, rpmvals[i]);
|
|
|
|
rec->fileNumber = 0;
|
|
|
|
switch (dbi->dbi_rpmtag) {
|
|
case RPMTAG_BASENAMES:
|
|
rec->fileNumber = i;
|
|
break;
|
|
case RPMTAG_TRIGGERNAME: /* don't add duplicates */
|
|
if (i == 0)
|
|
break;
|
|
for (j = 0; j < i; j++) {
|
|
if (!strcmp(rpmvals[i], rpmvals[j]))
|
|
break;
|
|
}
|
|
if (j < i)
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
rc += addIndexEntry(dbi, rpmvals[i], rec);
|
|
}
|
|
}
|
|
|
|
dbiSyncIndex(dbi);
|
|
|
|
switch (rpmtype) {
|
|
case RPM_STRING_ARRAY_TYPE:
|
|
case RPM_I18NSTRING_TYPE:
|
|
xfree(rpmvals);
|
|
rpmvals = NULL;
|
|
break;
|
|
}
|
|
rpmtype = 0;
|
|
rpmcnt = 0;
|
|
}
|
|
dbiFreeIndexRecordInstance(rec);
|
|
}
|
|
|
|
exit:
|
|
unblockSignals();
|
|
|
|
return rc;
|
|
}
|
|
|
|
int rpmdbUpdateRecord(rpmdb db, int offset, Header newHeader)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (rpmdbRemove(db, offset, 1))
|
|
return 1;
|
|
|
|
if (rpmdbAdd(db, newHeader))
|
|
return 1;
|
|
|
|
return rc;
|
|
}
|
|
|
|
void rpmdbRemoveDatabase(const char * rootdir, const char * dbpath)
|
|
{
|
|
int i;
|
|
char * filename;
|
|
|
|
i = strlen(dbpath);
|
|
if (dbpath[i - 1] != '/') {
|
|
filename = alloca(i);
|
|
strcpy(filename, dbpath);
|
|
filename[i] = '/';
|
|
filename[i + 1] = '\0';
|
|
dbpath = filename;
|
|
}
|
|
|
|
filename = alloca(strlen(rootdir) + strlen(dbpath) + 40);
|
|
|
|
{ dbiIndex dbi;
|
|
int i;
|
|
|
|
for (dbi = rpmdbi; dbi->dbi_basename != NULL; dbi++) {
|
|
sprintf(filename, "%s/%s/%s", rootdir, dbpath, dbi->dbi_basename);
|
|
unlink(filename);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
sprintf(filename, "%s/%s/__db.%03d", rootdir, dbpath, i);
|
|
unlink(filename);
|
|
}
|
|
for (i = 0; i < 4; i++) {
|
|
sprintf(filename, "%s/%s/packages.db%d", rootdir, dbpath, i);
|
|
unlink(filename);
|
|
}
|
|
}
|
|
|
|
sprintf(filename, "%s/%s", rootdir, dbpath);
|
|
rmdir(filename);
|
|
|
|
}
|
|
|
|
int rpmdbMoveDatabase(const char * rootdir, const char * olddbpath, const char * newdbpath)
|
|
{
|
|
int i;
|
|
char * ofilename, * nfilename;
|
|
int rc = 0;
|
|
|
|
i = strlen(olddbpath);
|
|
if (olddbpath[i - 1] != '/') {
|
|
ofilename = alloca(i + 2);
|
|
strcpy(ofilename, olddbpath);
|
|
ofilename[i] = '/';
|
|
ofilename[i + 1] = '\0';
|
|
olddbpath = ofilename;
|
|
}
|
|
|
|
i = strlen(newdbpath);
|
|
if (newdbpath[i - 1] != '/') {
|
|
nfilename = alloca(i + 2);
|
|
strcpy(nfilename, newdbpath);
|
|
nfilename[i] = '/';
|
|
nfilename[i + 1] = '\0';
|
|
newdbpath = nfilename;
|
|
}
|
|
|
|
ofilename = alloca(strlen(rootdir) + strlen(olddbpath) + 40);
|
|
nfilename = alloca(strlen(rootdir) + strlen(newdbpath) + 40);
|
|
|
|
switch(_useDbiMajor) {
|
|
case 3:
|
|
{ int i;
|
|
sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, "packages.db3");
|
|
sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, "packages.db3");
|
|
(void)rpmCleanPath(ofilename);
|
|
(void)rpmCleanPath(nfilename);
|
|
if (Rename(ofilename, nfilename)) rc = 1;
|
|
for (i = 0; i < 16; i++) {
|
|
sprintf(ofilename, "%s/%s/__db.%03d", rootdir, olddbpath, i);
|
|
(void)rpmCleanPath(ofilename);
|
|
if (!rpmfileexists(ofilename))
|
|
continue;
|
|
sprintf(nfilename, "%s/%s/__db.%03d", rootdir, newdbpath, i);
|
|
(void)rpmCleanPath(nfilename);
|
|
if (Rename(ofilename, nfilename)) rc = 1;
|
|
}
|
|
for (i = 0; i < 4; i++) {
|
|
sprintf(ofilename, "%s/%s/packages.db%d", rootdir, olddbpath, i);
|
|
(void)rpmCleanPath(ofilename);
|
|
if (!rpmfileexists(ofilename))
|
|
continue;
|
|
sprintf(nfilename, "%s/%s/packages.db%d", rootdir, newdbpath, i);
|
|
(void)rpmCleanPath(nfilename);
|
|
if (Rename(ofilename, nfilename)) rc = 1;
|
|
}
|
|
} break;
|
|
case 2:
|
|
case 1:
|
|
case 0:
|
|
{ dbiIndex dbi;
|
|
int i;
|
|
for (dbi = rpmdbi; dbi->dbi_basename != NULL; dbi++) {
|
|
sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, dbi->dbi_basename);
|
|
sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, dbi->dbi_basename);
|
|
(void)rpmCleanPath(ofilename);
|
|
(void)rpmCleanPath(nfilename);
|
|
if (Rename(ofilename, nfilename))
|
|
rc = 1;
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
sprintf(ofilename, "%s/%s/__db.%03d", rootdir, olddbpath, i);
|
|
(void)rpmCleanPath(ofilename);
|
|
if (rpmfileexists(ofilename))
|
|
unlink(ofilename);
|
|
sprintf(nfilename, "%s/%s/__db.%03d", rootdir, newdbpath, i);
|
|
(void)rpmCleanPath(nfilename);
|
|
if (rpmfileexists(nfilename))
|
|
unlink(nfilename);
|
|
}
|
|
} break;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
#ifdef DYING
|
|
typedef struct intMatch {
|
|
unsigned int recOffset;
|
|
unsigned int fileNumber;
|
|
int fpNum;
|
|
} IM_t;
|
|
#else
|
|
typedef struct _dbiIndexRecord IM_t;
|
|
#endif
|
|
|
|
static int intMatchCmp(const void * one, const void * two)
|
|
{
|
|
const IM_t * a = one;
|
|
const IM_t * b = two;
|
|
|
|
if (a->recOffset < b->recOffset)
|
|
return -1;
|
|
else if (a->recOffset > b->recOffset)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rpmdbFindFpList(rpmdb db, fingerPrint * fpList, dbiIndexSet * matchList,
|
|
int numItems)
|
|
{
|
|
int numIntMatches = 0;
|
|
int intMatchesAlloced = numItems;
|
|
IM_t * intMatches;
|
|
int i, j;
|
|
int start, end;
|
|
int num;
|
|
int_32 fc;
|
|
const char ** dirNames, ** baseNames;
|
|
const char ** fullBaseNames;
|
|
int_32 * dirIndexes, * fullDirIndexes;
|
|
fingerPrintCache fpc;
|
|
dbiIndexRecord rec = NULL;
|
|
|
|
/* this may be worth batching by baseName, but probably not as
|
|
baseNames are quite unique as it is */
|
|
|
|
intMatches = xcalloc(intMatchesAlloced, sizeof(*intMatches));
|
|
|
|
/* Gather all matches from the database */
|
|
for (i = 0; i < numItems; i++) {
|
|
dbiIndexSet matches = NULL;
|
|
int rc;
|
|
rc = dbiSearchIndex(db->_dbi[RPMDBI_FILE], fpList[i].baseName, 0, &matches);
|
|
switch (rc) {
|
|
default:
|
|
break;
|
|
case 2:
|
|
if (matches) {
|
|
dbiFreeIndexSet(matches);
|
|
matches = NULL;
|
|
}
|
|
free(intMatches);
|
|
return 1;
|
|
/*@notreached@*/ break;
|
|
case 0:
|
|
if ((numIntMatches + matches->count) >= intMatchesAlloced) {
|
|
intMatchesAlloced += matches->count;
|
|
intMatchesAlloced += intMatchesAlloced / 5;
|
|
intMatches = xrealloc(intMatches,
|
|
sizeof(*intMatches) * intMatchesAlloced);
|
|
}
|
|
|
|
for (j = 0; j < matches->count; j++) {
|
|
IM_t * im;
|
|
|
|
im = intMatches + numIntMatches;
|
|
im->recOffset = dbiIndexRecordOffset(matches, j);
|
|
im->fileNumber = dbiIndexRecordFileNumber(matches, j);
|
|
im->fpNum = i;
|
|
numIntMatches++;
|
|
}
|
|
|
|
break;
|
|
}
|
|
if (matches) {
|
|
dbiFreeIndexSet(matches);
|
|
matches = NULL;
|
|
}
|
|
}
|
|
|
|
qsort(intMatches, numIntMatches, sizeof(*intMatches), intMatchCmp);
|
|
/* intMatches is now sorted by (recnum, filenum) */
|
|
|
|
for (i = 0; i < numItems; i++)
|
|
matchList[i] = dbiCreateIndexSet();
|
|
|
|
fpc = fpCacheCreate(numIntMatches);
|
|
|
|
rec = dbiReturnIndexRecordInstance(0, 0);
|
|
|
|
/* For each set of files matched in a package ... */
|
|
for (start = 0; start < numIntMatches; start = end) {
|
|
IM_t * im;
|
|
Header h;
|
|
fingerPrint * fps;
|
|
|
|
im = intMatches + start;
|
|
|
|
/* Find the end of the set of matched files in this package. */
|
|
for (end = start + 1; end < numIntMatches; end++) {
|
|
if (im->recOffset != intMatches[end].recOffset)
|
|
break;
|
|
}
|
|
num = end - start;
|
|
|
|
/* Compute fingerprints for each file match in this package. */
|
|
h = rpmdbGetRecord(db, im->recOffset);
|
|
if (h == NULL) {
|
|
free(intMatches);
|
|
return 1;
|
|
}
|
|
|
|
headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL,
|
|
(void **) &dirNames, NULL);
|
|
headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL,
|
|
(void **) &fullBaseNames, &fc);
|
|
headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL,
|
|
(void **) &fullDirIndexes, NULL);
|
|
|
|
baseNames = xcalloc(num, sizeof(*baseNames));
|
|
dirIndexes = xcalloc(num, sizeof(*dirIndexes));
|
|
for (i = 0; i < num; i++) {
|
|
baseNames[i] = fullBaseNames[im[i].fileNumber];
|
|
dirIndexes[i] = fullDirIndexes[im[i].fileNumber];
|
|
}
|
|
|
|
fps = xcalloc(num, sizeof(*fps));
|
|
fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
|
|
|
|
free(dirNames);
|
|
free(fullBaseNames);
|
|
free(baseNames);
|
|
free(dirIndexes);
|
|
|
|
/* Add db (recnum,filenum) to list for fingerprint matches. */
|
|
|
|
for (i = 0; i < num; i++) {
|
|
j = im[i].fpNum;
|
|
if (FP_EQUAL_DIFFERENT_CACHE(fps[i], fpList[j])) {
|
|
rec->recOffset = im[i].recOffset;
|
|
rec->fileNumber = im[i].fileNumber;
|
|
dbiAppendIndexRecord(matchList[j], rec);
|
|
}
|
|
}
|
|
|
|
headerFree(h);
|
|
|
|
free(fps);
|
|
}
|
|
|
|
dbiFreeIndexRecordInstance(rec);
|
|
fpCacheFree(fpc);
|
|
free(intMatches);
|
|
|
|
return 0;
|
|
}
|