rpm/lib/rpmdb.c

648 lines
17 KiB
C

#include "config.h"
#if HAVE_ALLOCA_H
# include <alloca.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <signal.h>
#include <sys/signal.h>
#include <unistd.h>
#include "dbindex.h"
#include "falloc.h"
#include "header.h"
#include "misc.h"
#include "rpmdb.h"
#include "rpmlib.h"
#include "messages.h"
/* 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 {
faFile pkgs;
dbiIndex * nameIndex, * fileIndex, * groupIndex, * providesIndex;
dbiIndex * requiredbyIndex, * conflictsIndex;
};
static void removeIndexEntry(dbiIndex * dbi, char * name, dbiIndexRecord rec,
int tolerant, char * idxName);
static int addIndexEntry(dbiIndex * idx, char * index, unsigned int offset,
unsigned int fileNumber);
static void blockSignals(void);
static void unblockSignals(void);
static sigset_t signalMask;
int rpmdbOpen (char * prefix, rpmdb *rpmdbp, int mode, int perms) {
char * dbpath;
dbpath = rpmGetVar(RPMVAR_DBPATH);
if (!dbpath) {
rpmMessage(RPMMESS_DEBUG, "no dbpath has been set");
return 1;
}
return openDatabase(prefix, dbpath, rpmdbp, mode, perms, 0);
}
int rpmdbInit (char * prefix, int perms) {
rpmdb db;
char * dbpath;
dbpath = rpmGetVar(RPMVAR_DBPATH);
if (!dbpath) {
rpmMessage(RPMMESS_DEBUG, "no dbpath has been set");
return 1;
}
return openDatabase(prefix, dbpath, &db, O_CREAT | O_RDWR, perms, 1);
}
int openDatabase(char * prefix, char * dbpath, rpmdb *rpmdbp, int mode,
int perms, int justcheck) {
char * filename;
struct rpmdb db;
int i;
struct flock lockinfo;
i = strlen(dbpath);
if (dbpath[i - 1] != '/') {
filename = alloca(i + 2);
strcpy(filename, dbpath);
filename[i] = '/';
filename[i + 1] = '\0';
dbpath = filename;
}
filename = alloca(strlen(prefix) + strlen(dbpath) + 40);
if (mode & O_WRONLY)
return 1;
strcpy(filename, prefix);
strcat(filename, dbpath);
rpmMessage(RPMMESS_DEBUG, "opening database in %s\n", filename);
strcat(filename, "packages.rpm");
memset(&db, 0, sizeof(db));
if (!justcheck || !exists(filename)) {
db.pkgs = faOpen(filename, mode, 0644);
if (!db.pkgs) {
rpmError(RPMERR_DBOPEN, "failed to open %s\n", filename);
return 1;
}
/* try and get a lock - this is released by the kernel when we close
the file */
lockinfo.l_whence = 0;
lockinfo.l_start = 0;
lockinfo.l_len = 0;
if (mode & O_RDWR) {
lockinfo.l_type = F_WRLCK;
if (fcntl(db.pkgs->fd, F_SETLK, (void *) &lockinfo)) {
rpmError(RPMERR_FLOCK, "cannot get %s lock on database",
"exclusive");
return 1;
}
} else {
lockinfo.l_type = F_RDLCK;
if (fcntl(db.pkgs->fd, F_SETLK, (void *) &lockinfo)) {
rpmError(RPMERR_FLOCK, "cannot get %s lock on database", "shared");
return 1;
}
}
}
strcpy(filename, prefix);
strcat(filename, dbpath);
strcat(filename, "nameindex.rpm");
if (!justcheck || !exists(filename)) {
db.nameIndex = dbiOpenIndex(filename, mode, 0644);
if (!db.nameIndex) {
faClose(db.pkgs);
return 1;
}
}
strcpy(filename, prefix);
strcat(filename, dbpath);
strcat(filename, "fileindex.rpm");
if (!justcheck || !exists(filename)) {
db.fileIndex = dbiOpenIndex(filename, mode, 0644);
if (!db.fileIndex) {
faClose(db.pkgs);
dbiCloseIndex(db.nameIndex);
return 1;
}
}
strcpy(filename, prefix);
strcat(filename, dbpath);
strcat(filename, "groupindex.rpm");
if (!justcheck || !exists(filename)) {
db.groupIndex = dbiOpenIndex(filename, mode, 0644);
if (!db.groupIndex) {
faClose(db.pkgs);
dbiCloseIndex(db.nameIndex);
dbiCloseIndex(db.fileIndex);
return 1;
}
}
strcpy(filename, prefix);
strcat(filename, dbpath);
strcat(filename, "providesindex.rpm");
if (!justcheck || !exists(filename)) {
db.providesIndex = dbiOpenIndex(filename, mode, 0644);
if (!db.providesIndex) {
faClose(db.pkgs);
dbiCloseIndex(db.fileIndex);
dbiCloseIndex(db.nameIndex);
dbiCloseIndex(db.groupIndex);
return 1;
}
}
strcpy(filename, prefix);
strcat(filename, dbpath);
strcat(filename, "requiredby.rpm");
if (!justcheck || !exists(filename)) {
db.requiredbyIndex = dbiOpenIndex(filename, mode, 0644);
if (!db.requiredbyIndex) {
faClose(db.pkgs);
dbiCloseIndex(db.fileIndex);
dbiCloseIndex(db.nameIndex);
dbiCloseIndex(db.groupIndex);
dbiCloseIndex(db.providesIndex);
return 1;
}
}
strcpy(filename, prefix);
strcat(filename, dbpath);
strcat(filename, "conflictsindex.rpm");
if (!justcheck || !exists(filename)) {
db.conflictsIndex = dbiOpenIndex(filename, mode, 0644);
if (!db.conflictsIndex) {
faClose(db.pkgs);
dbiCloseIndex(db.fileIndex);
dbiCloseIndex(db.nameIndex);
dbiCloseIndex(db.groupIndex);
dbiCloseIndex(db.providesIndex);
dbiCloseIndex(db.requiredbyIndex);
return 1;
}
}
*rpmdbp = malloc(sizeof(struct rpmdb));
**rpmdbp = db;
if (justcheck) {
rpmdbClose(*rpmdbp);
}
return 0;
}
void rpmdbClose (rpmdb db) {
if (db->pkgs) faClose(db->pkgs);
if (db->fileIndex) dbiCloseIndex(db->fileIndex);
if (db->groupIndex) dbiCloseIndex(db->groupIndex);
if (db->nameIndex) dbiCloseIndex(db->nameIndex);
if (db->providesIndex) dbiCloseIndex(db->providesIndex);
if (db->requiredbyIndex) dbiCloseIndex(db->requiredbyIndex);
if (db->conflictsIndex) dbiCloseIndex(db->conflictsIndex);
free(db);
}
int rpmdbFirstRecNum(rpmdb db) {
return faFirstOffset(db->pkgs);
}
int rpmdbNextRecNum(rpmdb db, unsigned int lastOffset) {
/* 0 at end */
return faNextOffset(db->pkgs, lastOffset);
}
Header rpmdbGetRecord(rpmdb db, unsigned int offset) {
lseek(db->pkgs->fd, offset, SEEK_SET);
return headerRead(db->pkgs->fd, HEADER_MAGIC_NO);
}
int rpmdbFindByFile(rpmdb db, char * filespec, dbiIndexSet * matches) {
return dbiSearchIndex(db->fileIndex, filespec, matches);
}
int rpmdbFindByProvides(rpmdb db, char * filespec, dbiIndexSet * matches) {
return dbiSearchIndex(db->providesIndex, filespec, matches);
}
int rpmdbFindByRequiredBy(rpmdb db, char * filespec, dbiIndexSet * matches) {
return dbiSearchIndex(db->requiredbyIndex, filespec, matches);
}
int rpmdbFindByConflicts(rpmdb db, char * filespec, dbiIndexSet * matches) {
return dbiSearchIndex(db->conflictsIndex, filespec, matches);
}
int rpmdbFindByGroup(rpmdb db, char * group, dbiIndexSet * matches) {
return dbiSearchIndex(db->groupIndex, group, matches);
}
int rpmdbFindPackage(rpmdb db, char * name, dbiIndexSet * matches) {
return dbiSearchIndex(db->nameIndex, name, matches);
}
static void removeIndexEntry(dbiIndex * dbi, char * key, dbiIndexRecord rec,
int tolerant, char * idxName) {
int rc;
dbiIndexSet matches;
rc = dbiSearchIndex(dbi, key, &matches);
switch (rc) {
case 0:
if (dbiRemoveIndexRecord(&matches, rec) && !tolerant) {
rpmError(RPMERR_DBCORRUPT, "package %s not listed 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, "package %s not found in %s", key, idxName);
break;
case 2:
break; /* error message already generated from dbindex.c */
}
dbiFreeIndexRecord(matches);
}
int rpmdbRemove(rpmdb db, unsigned int offset, int tolerant) {
Header h;
char * name, * group;
int type;
unsigned int count;
dbiIndexRecord rec;
char ** fileList, ** providesList, ** requiredbyList;
char ** conflictList;
int i;
rec.recOffset = offset;
rec.fileNumber = 0;
h = rpmdbGetRecord(db, offset);
if (!h) {
rpmError(RPMERR_DBCORRUPT, "cannot read header at %d for uninstall",
offset);
return 1;
}
blockSignals();
if (!headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count)) {
rpmError(RPMERR_DBCORRUPT, "package has no name");
} else {
rpmMessage(RPMMESS_DEBUG, "removing name index\n");
removeIndexEntry(db->nameIndex, name, rec, tolerant, "name index");
}
if (!headerGetEntry(h, RPMTAG_GROUP, &type, (void **) &group, &count)) {
rpmMessage(RPMMESS_DEBUG, "package has no group\n");
} else {
rpmMessage(RPMMESS_DEBUG, "removing group index\n");
removeIndexEntry(db->groupIndex, group, rec, tolerant, "group index");
}
if (headerGetEntry(h, RPMTAG_PROVIDES, &type, (void **) &providesList,
&count)) {
for (i = 0; i < count; i++) {
rpmMessage(RPMMESS_DEBUG, "removing provides index for %s\n",
providesList[i]);
removeIndexEntry(db->providesIndex, providesList[i], rec, tolerant,
"providesfile index");
}
free(providesList);
}
if (headerGetEntry(h, RPMTAG_REQUIRENAME, &type, (void **) &requiredbyList,
&count)) {
for (i = 0; i < count; i++) {
rpmMessage(RPMMESS_DEBUG, "removing requiredby index for %s\n",
requiredbyList[i]);
removeIndexEntry(db->requiredbyIndex, requiredbyList[i], rec,
tolerant, "requiredby index");
}
free(requiredbyList);
}
if (headerGetEntry(h, RPMTAG_CONFLICTNAME, &type, (void **) &conflictList,
&count)) {
for (i = 0; i < count; i++) {
rpmMessage(RPMMESS_DEBUG, "removing conflict index for %s\n",
conflictList[i]);
removeIndexEntry(db->conflictsIndex, conflictList[i], rec,
tolerant, "conflict index");
}
free(conflictList);
}
if (headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList,
&count)) {
for (i = 0; i < count; i++) {
rpmMessage(RPMMESS_DEBUG, "removing file index for %s\n", fileList[i]);
rec.fileNumber = i;
removeIndexEntry(db->fileIndex, fileList[i], rec, tolerant,
"file index");
}
free(fileList);
} else {
rpmMessage(RPMMESS_DEBUG, "package has no files\n");
}
faFree(db->pkgs, offset);
dbiSyncIndex(db->nameIndex);
dbiSyncIndex(db->groupIndex);
dbiSyncIndex(db->fileIndex);
unblockSignals();
headerFree(h);
return 0;
}
static int addIndexEntry(dbiIndex * idx, char * index, unsigned int offset,
unsigned int fileNumber) {
dbiIndexSet set;
dbiIndexRecord irec;
int rc;
irec.recOffset = offset;
irec.fileNumber = fileNumber;
rc = dbiSearchIndex(idx, index, &set);
if (rc == -1) /* error */
return 1;
if (rc == 1) /* new item */
set = dbiCreateIndexRecord();
dbiAppendIndexRecord(&set, irec);
if (dbiUpdateIndex(idx, index, &set))
exit(1);
dbiFreeIndexRecord(set);
return 0;
}
int rpmdbAdd(rpmdb db, Header dbentry) {
unsigned int dboffset;
unsigned int i;
char ** fileList;
char ** providesList;
char ** requiredbyList;
char ** conflictList;
char * name, * group;
int count, providesCount, requiredbyCount, conflictCount;
int type;
int rc = 0;
headerGetEntry(dbentry, RPMTAG_NAME, &type, (void **) &name, &count);
headerGetEntry(dbentry, RPMTAG_GROUP, &type, (void **) &group, &count);
if (!group) group = "Unknown";
if (!headerGetEntry(dbentry, RPMTAG_FILENAMES, &type, (void **) &fileList,
&count)) {
count = 0;
}
if (!headerGetEntry(dbentry, RPMTAG_PROVIDES, &type, (void **) &providesList,
&providesCount)) {
providesCount = 0;
}
if (!headerGetEntry(dbentry, RPMTAG_REQUIRENAME, &type,
(void **) &requiredbyList, &requiredbyCount)) {
requiredbyCount = 0;
}
if (!headerGetEntry(dbentry, RPMTAG_CONFLICTNAME, &type,
(void **) &conflictList, &conflictCount)) {
conflictCount = 0;
}
blockSignals();
dboffset = faAlloc(db->pkgs, headerSizeof(dbentry, HEADER_MAGIC_NO));
if (!dboffset) {
rpmError(RPMERR_DBCORRUPT, "cannot allocate space for database");
unblockSignals();
if (providesCount) free(providesList);
if (requiredbyCount) free(requiredbyList);
if (count) free(fileList);
return 1;
}
lseek(db->pkgs->fd, dboffset, SEEK_SET);
headerWrite(db->pkgs->fd, dbentry, HEADER_MAGIC_NO);
/* Now update the appropriate indexes */
if (addIndexEntry(db->nameIndex, name, dboffset, 0))
rc = 1;
if (addIndexEntry(db->groupIndex, group, dboffset, 0))
rc = 1;
for (i = 0; i < conflictCount; i++) {
if (addIndexEntry(db->conflictsIndex, conflictList[i], dboffset, 0))
rc = 1;
}
for (i = 0; i < requiredbyCount; i++) {
if (addIndexEntry(db->requiredbyIndex, requiredbyList[i], dboffset, 0))
rc = 1;
}
for (i = 0; i < providesCount; i++) {
if (addIndexEntry(db->providesIndex, providesList[i], dboffset, 0))
rc = 1;
}
for (i = 0; i < count; i++) {
if (addIndexEntry(db->fileIndex, fileList[i], dboffset, i))
rc = 1;
}
dbiSyncIndex(db->nameIndex);
dbiSyncIndex(db->groupIndex);
dbiSyncIndex(db->fileIndex);
dbiSyncIndex(db->providesIndex);
dbiSyncIndex(db->requiredbyIndex);
unblockSignals();
if (requiredbyCount) free(requiredbyList);
if (providesCount) free(providesList);
if (count) free(fileList);
return rc;
}
int rpmdbUpdateRecord(rpmdb db, int offset, Header newHeader) {
Header oldHeader;
int oldSize;
oldHeader = rpmdbGetRecord(db, offset);
if (!oldHeader) {
rpmError(RPMERR_DBCORRUPT, "cannot read header at %d for update",
offset);
return 1;
}
oldSize = headerSizeof(oldHeader, HEADER_MAGIC_NO);
headerFree(oldHeader);
if (oldSize != headerSizeof(newHeader, HEADER_MAGIC_NO)) {
rpmMessage(RPMMESS_DEBUG, "header changed size!");
if (rpmdbRemove(db, offset, 1))
return 1;
if (rpmdbAdd(db, newHeader))
return 1;
} else {
blockSignals();
lseek(db->pkgs->fd, offset, SEEK_SET);
headerWrite(db->pkgs->fd, newHeader, HEADER_MAGIC_NO);
unblockSignals();
}
return 0;
}
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);
}
void rpmdbRemoveDatabase(char * rootdir, 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);
sprintf(filename, "%s/%s/packages.rpm", rootdir, dbpath);
unlink(filename);
sprintf(filename, "%s/%s/nameindex.rpm", rootdir, dbpath);
unlink(filename);
sprintf(filename, "%s/%s/fileindex.rpm", rootdir, dbpath);
unlink(filename);
sprintf(filename, "%s/%s/groupindex.rpm", rootdir, dbpath);
unlink(filename);
sprintf(filename, "%s/%s/requiredby.rpm", rootdir, dbpath);
unlink(filename);
sprintf(filename, "%s/%s/providesindex.rpm", rootdir, dbpath);
unlink(filename);
}
int rpmdbMoveDatabase(char * rootdir, char * olddbpath, 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);
sprintf(ofilename, "%s/%s/packages.rpm", rootdir, olddbpath);
sprintf(nfilename, "%s/%s/packages.rpm", rootdir, newdbpath);
if (rename(ofilename, nfilename)) rc = 1;
sprintf(ofilename, "%s/%s/nameindex.rpm", rootdir, olddbpath);
sprintf(nfilename, "%s/%s/nameindex.rpm", rootdir, newdbpath);
if (rename(ofilename, nfilename)) rc = 1;
sprintf(ofilename, "%s/%s/fileindex.rpm", rootdir, olddbpath);
sprintf(nfilename, "%s/%s/fileindex.rpm", rootdir, newdbpath);
if (rename(ofilename, nfilename)) rc = 1;
sprintf(ofilename, "%s/%s/groupindex.rpm", rootdir, olddbpath);
sprintf(nfilename, "%s/%s/groupindex.rpm", rootdir, newdbpath);
if (rename(ofilename, nfilename)) rc = 1;
sprintf(ofilename, "%s/%s/requiredby.rpm", rootdir, olddbpath);
sprintf(nfilename, "%s/%s/requiredby.rpm", rootdir, newdbpath);
if (rename(ofilename, nfilename)) rc = 1;
sprintf(ofilename, "%s/%s/providesindex.rpm", rootdir, olddbpath);
sprintf(nfilename, "%s/%s/providesindex.rpm", rootdir, newdbpath);
if (rename(ofilename, nfilename)) rc = 1;
return rc;
}