#include "config.h" #if HAVE_ALLOCA_H # include #endif #include #include #include #include #include #include #include #include "dbindex.h" #include "falloc.h" #include "header.h" #include "intl.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_s { 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_s 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_s)); **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) { char * fs; char * src, * dst; /* we try and canonicalize the filespec a bit before doing the search */ fs = alloca(strlen(filespec) + 1); for (src = filespec, dst = fs; *src; ) { switch (*src) { case '/': if ((dst == fs) || (*(dst - 1) != '/')) *dst++ = *src; src++; break; default: *dst++ = *src++; } } *dst-- = '\0'; if (*dst == '/') *dst = '\0'; return dbiSearchIndex(db->fileIndex, fs, 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 */ } dbiFreeIndexRecord(matches); 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 */ } } 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); sprintf(filename, "%s/%s/conflictsindex.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; sprintf(ofilename, "%s/%s/conflictsindex.rpm", rootdir, olddbpath); sprintf(nfilename, "%s/%s/conflictsindex.rpm", rootdir, newdbpath); if (rename(ofilename, nfilename)) rc = 1; return rc; }