/** \ingroup rpmts * \file lib/depends.c */ #include "system.h" #include /* rpmVersionCompare, rpmlib provides */ #include #include #include #include #include #include "lib/rpmts_internal.h" #include "lib/rpmte_internal.h" #include "lib/misc.h" #include "debug.h" const char * const RPMVERSION = VERSION; const char * const rpmNAME = PACKAGE; const char * const rpmEVR = VERSION; const int rpmFLAGS = RPMSENSE_EQUAL; /* rpmlib provides */ static rpmds rpmlibP = NULL; #undef HASHTYPE #undef HTKEYTYPE #undef HTDATATYPE #define HASHTYPE depCache #define HTKEYTYPE const char * #define HTDATATYPE int #include "lib/rpmhash.H" #include "lib/rpmhash.C" #undef HASHTYPE #undef HTKEYTYPE #undef HTDATATYPE #define HASHTYPE intHash #define HTKEYTYPE unsigned int #include "rpmhash.C" #undef HASHTYPE #undef HASHKEYTYPE /** * Check for supported payload format in header. * @param h header to check * @return RPMRC_OK if supported, RPMRC_FAIL otherwise */ static rpmRC headerCheckPayloadFormat(Header h) { rpmRC rc = RPMRC_OK; const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT); /* * XXX Ugh, rpm 3.x packages don't have payload format tag. Instead * of blindly allowing, should check somehow (HDRID existence or... ?) */ if (!payloadfmt) return rc; if (!rstreq(payloadfmt, "cpio")) { char *nevra = headerGetAsString(h, RPMTAG_NEVRA); if (payloadfmt && rstreq(payloadfmt, "drpm")) { rpmlog(RPMLOG_ERR, _("%s is a Delta RPM and cannot be directly installed\n"), nevra); } else { rpmlog(RPMLOG_ERR, _("Unsupported payload (%s) in package %s\n"), payloadfmt ? payloadfmt : "none", nevra); } free(nevra); rc = RPMRC_FAIL; } return rc; } /** * Add removed package instance to ordered transaction set. * @param ts transaction set * @param h header * @param depends installed package of pair (or RPMAL_NOMATCH on erase) * @return 0 on success */ static int removePackage(rpmts ts, Header h, rpmte depends) { tsMembers tsmem = rpmtsMembers(ts); rpmte p; unsigned int dboffset = headerGetInstance(h); /* Can't remove what's not installed */ if (dboffset == 0) return 1; /* Filter out duplicate erasures. */ if (intHashHasEntry(tsmem->removedPackages, dboffset)) { return 0; } p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL); if (p == NULL) return 1; intHashAddEntry(tsmem->removedPackages, dboffset); if (tsmem->orderCount >= tsmem->orderAlloced) { tsmem->orderAlloced += (tsmem->orderCount - tsmem->orderAlloced) + tsmem->delta; tsmem->order = xrealloc(tsmem->order, sizeof(*tsmem->order) * tsmem->orderAlloced); } rpmteSetDependsOn(p, depends); tsmem->order[tsmem->orderCount] = p; tsmem->orderCount++; return 0; } /* Return rpmdb iterator with removals optionally pruned out */ static rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmDbiTagVal tag, const char * key, int prune) { rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0); if (prune) { tsMembers tsmem = rpmtsMembers(ts); rpmdbPruneIterator(mi, tsmem->removedPackages); } return mi; } /** * Decides whether to skip a package upgrade/obsoletion on TE color. * * @param tscolor color of this transaction * @param color color of this transaction element * @param ocolor header color of the upgraded/obsoleted package * * @return non-zero if the package should be skipped */ static int skipColor(rpm_color_t tscolor, rpm_color_t color, rpm_color_t ocolor) { return tscolor && color && ocolor && !(color & ocolor); } /* Add erase elements for older packages of same color (if any). */ static int addUpgradeErasures(rpmts ts, rpm_color_t tscolor, rpmte p, rpm_color_t hcolor, Header h) { Header oh; rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0); int rc = 0; while((oh = rpmdbNextIterator(mi)) != NULL) { /* Ignore colored packages not in our rainbow. */ if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR))) continue; /* Skip packages that contain identical NEVR. */ if (rpmVersionCompare(h, oh) == 0) continue; if (removePackage(ts, oh, p)) { rc = 1; break; } } rpmdbFreeIterator(mi); return rc; } /* Add erase elements for obsoleted packages of same color (if any). */ static int addObsoleteErasures(rpmts ts, rpm_color_t tscolor, rpmte p) { rpmds obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME)); Header oh; int rc = 0; while (rpmdsNext(obsoletes) >= 0 && rc == 0) { const char * Name; rpmdbMatchIterator mi = NULL; if ((Name = rpmdsN(obsoletes)) == NULL) continue; /* XXX can't happen */ mi = rpmtsPrunedIterator(ts, RPMDBI_NAME, Name, 1); while((oh = rpmdbNextIterator(mi)) != NULL) { const char *oarch = headerGetString(oh, RPMTAG_ARCH); /* avoid self-obsoleting packages */ if (rstreq(rpmteN(p), Name) && rstreq(rpmteA(p), oarch)) { char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA); rpmlog(RPMLOG_DEBUG, " Not obsoleting: %s\n", ohNEVRA); free(ohNEVRA); continue; } /* * Rpm prior to 3.0.3 does not have versioned obsoletes. * If no obsoletes version info is available, match all names. */ if (rpmdsEVR(obsoletes) == NULL || rpmdsNVRMatchesDep(oh, obsoletes, _rpmds_nopromote)) { char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA); rpmlog(RPMLOG_DEBUG, " Obsoletes: %s\t\terases %s\n", rpmdsDNEVR(obsoletes)+2, ohNEVRA); free(ohNEVRA); if (removePackage(ts, oh, p)) { rc = 1; break; } } } rpmdbFreeIterator(mi); } return rc; } /* * Check for previously added versions and obsoletions. * Return index where to place this element, or -1 to skip. */ static int findPos(rpmts ts, rpm_color_t tscolor, Header h, int upgrade) { int oc; int obsolete = 0; const char * arch = headerGetString(h, RPMTAG_ARCH); const char * os = headerGetString(h, RPMTAG_OS); rpmte p; rpmds oldChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_LESS)); rpmds newChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_GREATER)); rpmds sameChk = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_EQUAL)); rpmds obsChk = rpmdsNew(h, RPMTAG_OBSOLETENAME, 0); rpmtsi pi = rpmtsiInit(ts); /* XXX can't use rpmtsiNext() filter or oc will have wrong value. */ for (oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) { rpmds thisds, obsoletes; /* Only added binary packages need checking */ if (rpmteType(p) == TR_REMOVED || rpmteIsSource(p)) continue; /* Skip packages obsoleted by already added packages */ obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME)); while (rpmdsNext(obsoletes) >= 0) { if (rpmdsCompare(obsoletes, sameChk)) { obsolete = 1; oc = -1; break; } } /* Replace already added obsoleted packages by obsoleting package */ thisds = rpmteDS(p, RPMTAG_NAME); rpmdsInit(obsChk); while (rpmdsNext(obsChk) >= 0) { if (rpmdsCompare(obsChk, thisds)) { obsolete = 1; break; } } if (obsolete) break; if (tscolor) { const char * parch = rpmteA(p); const char * pos = rpmteO(p); if (arch == NULL || parch == NULL || os == NULL || pos == NULL) continue; if (!rstreq(arch, parch) || !rstreq(os, pos)) continue; } /* * Always skip identical NEVR. * On upgrade, if newer NEVR was previously added, skip adding older. */ if (rpmdsCompare(sameChk, thisds) || (upgrade && rpmdsCompare(newChk, thisds))) { oc = -1; break;; } /* On upgrade, if older NEVR was previously added, replace with new */ if (upgrade && rpmdsCompare(oldChk, thisds) != 0) { break; } } /* If we broke out of the loop early we've something to say */ if (p != NULL && rpmIsVerbose()) { char *nevra = headerGetAsString(h, RPMTAG_NEVRA); const char *msg = (oc < 0) ? _("package %s was already added, skipping %s\n") : _("package %s was already added, replacing with %s\n"); rpmlog(RPMLOG_WARNING, msg, rpmteNEVRA(p), nevra); free(nevra); } rpmtsiFree(pi); rpmdsFree(oldChk); rpmdsFree(newChk); rpmdsFree(sameChk); rpmdsFree(obsChk); return oc; } int rpmtsAddInstallElement(rpmts ts, Header h, fnpyKey key, int upgrade, rpmRelocation * relocs) { tsMembers tsmem = rpmtsMembers(ts); rpm_color_t tscolor = rpmtsColor(ts); rpmte p = NULL; int isSource = headerIsSource(h); int ec = 0; int oc = tsmem->orderCount; /* Check for supported payload format if it's a package */ if (key && headerCheckPayloadFormat(h) != RPMRC_OK) { ec = 1; goto exit; } /* Source packages are never "upgraded" */ if (isSource) upgrade = 0; /* Do lazy (readonly?) open of rpm database for upgrades. */ if (upgrade && rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) { if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0) goto exit; } p = rpmteNew(ts, h, TR_ADDED, key, relocs); if (p == NULL) { ec = 1; goto exit; } /* Check binary packages for redundancies in the set */ if (!isSource) { oc = findPos(ts, tscolor, h, upgrade); /* If we're replacing a previously added element, free the old one */ if (oc >= 0 && oc < tsmem->orderCount) { rpmalDel(tsmem->addedPackages, tsmem->order[oc]); tsmem->order[oc] = rpmteFree(tsmem->order[oc]); /* If newer NEVR was already added, we're done */ } else if (oc < 0) { p = rpmteFree(p); goto exit; } } if (tsmem->addedPackages == NULL) { tsmem->addedPackages = rpmalCreate(5, rpmtsFlags(ts), tscolor, rpmtsPrefColor(ts)); } if (oc >= tsmem->orderAlloced) { tsmem->orderAlloced += (oc - tsmem->orderAlloced) + tsmem->delta; tsmem->order = xrealloc(tsmem->order, tsmem->orderAlloced * sizeof(*tsmem->order)); } tsmem->order[oc] = p; if (oc == tsmem->orderCount) { tsmem->orderCount++; } rpmalAdd(tsmem->addedPackages, p); /* Add erasure elements for old versions and obsoletions on upgrades */ /* XXX TODO: If either of these fails, we'd need to undo all additions */ if (upgrade) { addUpgradeErasures(ts, tscolor, p, rpmteColor(p), h); addObsoleteErasures(ts, tscolor, p); } exit: return ec; } int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset) { return removePackage(ts, h, NULL); } /* Cached rpmdb provide lookup, returns 0 if satisfied, 1 otherwise */ static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep) { const char * Name = rpmdsN(dep); const char * DNEVR = rpmdsDNEVR(dep); rpmTagVal deptag = rpmdsTagN(dep); int *cachedrc = NULL; rpmdbMatchIterator mi = NULL; Header h = NULL; int rc = 0; /* pretrans deps are provided by current packages, don't prune erasures */ int prune = (rpmdsFlags(dep) & RPMSENSE_PRETRANS) ? 0 : 1; /* See if we already looked this up */ if (prune && depCacheGetEntry(dcache, DNEVR, &cachedrc, NULL, NULL)) { rc = *cachedrc; rpmdsNotify(dep, "(cached)", rc); return rc; } /* * See if a filename dependency is a real file in some package, * taking file state into account: replaced, wrong colored and * not installed files can not satisfy a dependency. */ if (deptag != RPMTAG_OBSOLETENAME && Name[0] == '/') { mi = rpmtsPrunedIterator(ts, RPMDBI_INSTFILENAMES, Name, prune); while ((h = rpmdbNextIterator(mi)) != NULL) { rpmdsNotify(dep, "(db files)", rc); break; } rpmdbFreeIterator(mi); } /* Otherwise look in provides no matter what the dependency looks like */ if (h == NULL) { /* Obsoletes use just name alone, everything else uses provides */ rpmTagVal dbtag = RPMDBI_PROVIDENAME; if (deptag == RPMDBI_OBSOLETENAME) dbtag = RPMDBI_NAME; mi = rpmtsPrunedIterator(ts, dbtag, Name, prune); while ((h = rpmdbNextIterator(mi)) != NULL) { int match; if (dbtag == RPMDBI_OBSOLETENAME) { match = rpmdsNVRMatchesDep(h, dep, _rpmds_nopromote); } else { match = rpmdsMatchesDep(h, rpmdbGetIteratorFileNum(mi), dep, _rpmds_nopromote); } if (match) { rpmdsNotify(dep, "(db provides)", rc); break; } } rpmdbFreeIterator(mi); } rc = (h != NULL) ? 0 : 1; /* Cache the relatively expensive rpmdb lookup results */ /* Caching the oddball non-pruned case would mess up other results */ if (prune) depCacheAddEntry(dcache, xstrdup(DNEVR), rc); return rc; } /** * Check dep for an unsatisfied dependency. * @param ts transaction set * @param dep dependency * @return 0 if satisfied, 1 if not satisfied */ static int unsatisfiedDepend(rpmts ts, depCache dcache, rpmds dep) { tsMembers tsmem = rpmtsMembers(ts); int rc; int retrying = 0; int adding = (rpmdsInstance(dep) == 0); rpmsenseFlags dsflags = rpmdsFlags(dep); retry: rc = 0; /* assume dependency is satisfied */ /* * New features in rpm packaging implicitly add versioned dependencies * on rpmlib provides. The dependencies look like "rpmlib(YaddaYadda)". * Check those dependencies now. */ if (dsflags & RPMSENSE_RPMLIB) { static int oneshot = -1; if (oneshot) oneshot = rpmdsRpmlib(&rpmlibP, NULL); if (rpmlibP != NULL && rpmdsSearch(rpmlibP, dep) >= 0) { rpmdsNotify(dep, "(rpmlib provides)", rc); goto exit; } goto unsatisfied; } /* Dont look at pre-requisites of already installed packages */ if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags)) goto exit; /* Pretrans dependencies can't be satisfied by added packages. */ if (!(dsflags & RPMSENSE_PRETRANS)) { rpmte match = rpmalSatisfiesDepend(tsmem->addedPackages, dep); /* * Handle definitive matches within the added package set. * Self-obsoletes and -conflicts fall through here as we need to * check for possible other matches in the rpmdb. */ if (match) { rpmTagVal dtag = rpmdsTagN(dep); /* Requires match, look no further */ if (dtag == RPMTAG_REQUIRENAME) goto exit; /* Conflicts/obsoletes match on another package, look no further */ if (rpmteDS(match, dtag) != dep) goto exit; } } /* See if the rpmdb provides it */ if (rpmdbProvides(ts, dcache, dep) == 0) goto exit; /* Search for an unsatisfied dependency. */ if (adding && !retrying && !(dsflags & RPMSENSE_PRETRANS)) { int xx = rpmtsSolve(ts, dep); if (xx == 0) goto exit; if (xx == -1) { retrying = 1; goto retry; } } unsatisfied: rc = 1; /* dependency is unsatisfied */ rpmdsNotify(dep, NULL, rc); exit: return rc; } /* Check a dependency set for problems */ static void checkDS(rpmts ts, depCache dcache, rpmte te, const char * pkgNEVRA, rpmds ds, const char * depName, rpm_color_t tscolor) { rpm_color_t dscolor; /* require-problems are unsatisfied, others appear "satisfied" */ int is_problem = (rpmdsTagN(ds) == RPMTAG_REQUIRENAME); ds = rpmdsInit(ds); while (rpmdsNext(ds) >= 0) { /* Filter out dependencies that came along for the ride. */ if (depName != NULL && !rstreq(depName, rpmdsN(ds))) continue; /* Ignore colored dependencies not in our rainbow. */ dscolor = rpmdsColor(ds); if (tscolor && dscolor && !(tscolor & dscolor)) continue; if (unsatisfiedDepend(ts, dcache, ds) == is_problem) rpmteAddDepProblem(te, pkgNEVRA, ds, NULL); } } /* Check a given dependency type against installed packages */ static void checkInstDeps(rpmts ts, depCache dcache, rpmte te, rpmTag depTag, const char *dep) { Header h; rpmdbMatchIterator mi = rpmtsPrunedIterator(ts, depTag, dep, 1); while ((h = rpmdbNextIterator(mi)) != NULL) { char * pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA); rpmds ds = rpmdsNew(h, depTag, 0); checkDS(ts, dcache, te, pkgNEVRA, ds, dep, 0); rpmdsFree(ds); free(pkgNEVRA); } rpmdbFreeIterator(mi); } int rpmtsCheck(rpmts ts) { rpm_color_t tscolor = rpmtsColor(ts); rpmtsi pi = NULL; rpmte p; int closeatexit = 0; int rc = 0; depCache dcache = NULL; (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0); /* Do lazy, readonly, open of rpm database. */ if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) { if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0) goto exit; closeatexit = 1; } /* XXX FIXME: figure some kind of heuristic for the cache size */ dcache = depCacheCreate(5001, rstrhash, strcmp, (depCacheFreeKey)rfree, NULL); /* * Look at all of the added packages and make sure their dependencies * are satisfied. */ pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) { rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME)); rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n", rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p)); checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME), NULL, tscolor); checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME), NULL, tscolor); checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_OBSOLETENAME), NULL, tscolor); /* Check provides against conflicts in installed packages. */ while (rpmdsNext(provides) >= 0) { checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides)); } /* Skip obsoletion checks for source packages (ie build) */ if (rpmteIsSource(p)) continue; /* Check package name (not provides!) against installed obsoletes */ checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p)); } rpmtsiFree(pi); /* * Look at the removed packages and make sure they aren't critical. */ pi = rpmtsiInit(ts); while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) { rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME)); rpmfi fi = rpmfiInit(rpmteFI(p), 0); rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n", rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p)); /* Check provides and filenames against installed dependencies. */ while (rpmdsNext(provides) >= 0) { checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmdsN(provides)); } while (rpmfiNext(fi) >= 0) { if (RPMFILE_IS_INSTALLED(rpmfiFState(fi))) checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmfiFN(fi)); } } rpmtsiFree(pi); exit: depCacheFree(dcache); (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0); if (closeatexit) (void) rpmtsCloseDB(ts); return rc; }