7 #include <rpm/rpmlib.h> /* rpmVersionCompare, rpmlib provides */
8 #include <rpm/rpmtag.h>
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmdb.h>
11 #include <rpm/rpmds.h>
12 #include <rpm/rpmfi.h>
14 #include "lib/rpmts_internal.h"
15 #include "lib/rpmte_internal.h"
16 #include "lib/rpmds_internal.h"
21 const char * const RPMVERSION = VERSION;
23 const char * const rpmNAME = PACKAGE;
25 const char * const rpmEVR = VERSION;
27 const int rpmFLAGS = RPMSENSE_EQUAL;
33 #define HASHTYPE depCache
34 #define HTKEYTYPE const char *
35 #define HTDATATYPE int
36 #include "lib/rpmhash.H"
37 #include "lib/rpmhash.C"
42 #define HASHTYPE removedHash
43 #define HTKEYTYPE unsigned int
44 #define HTDATATYPE struct rpmte_s *
51 * Check for supported payload format in header.
52 * @param h header to check
53 * @return RPMRC_OK if supported, RPMRC_FAIL otherwise
55 static rpmRC headerCheckPayloadFormat(Header h) {
57 const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);
59 * XXX Ugh, rpm 3.x packages don't have payload format tag. Instead
60 * of blindly allowing, should check somehow (HDRID existence or... ?)
62 if (!payloadfmt) return rc;
64 if (!rstreq(payloadfmt, "cpio")) {
65 char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
66 if (payloadfmt && rstreq(payloadfmt, "drpm")) {
68 _("%s is a Delta RPM and cannot be directly installed\n"),
72 _("Unsupported payload (%s) in package %s\n"),
73 payloadfmt ? payloadfmt : "none", nevra);
82 * Add removed package instance to ordered transaction set.
83 * @param ts transaction set
85 * @param depends installed package of pair (or RPMAL_NOMATCH on erase)
86 * @return 0 on success
88 static int removePackage(rpmts ts, Header h, rpmte depends)
90 tsMembers tsmem = rpmtsMembers(ts);
92 unsigned int dboffset = headerGetInstance(h);
94 /* Can't remove what's not installed */
95 if (dboffset == 0) return 1;
97 /* Filter out duplicate erasures. */
98 if (removedHashHasEntry(tsmem->removedPackages, dboffset)) {
102 p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);
106 removedHashAddEntry(tsmem->removedPackages, dboffset, p);
108 if (tsmem->orderCount >= tsmem->orderAlloced) {
109 tsmem->orderAlloced += (tsmem->orderCount - tsmem->orderAlloced) + tsmem->delta;
110 tsmem->order = xrealloc(tsmem->order, sizeof(*tsmem->order) * tsmem->orderAlloced);
113 rpmteSetDependsOn(p, depends);
115 tsmem->order[tsmem->orderCount] = p;
121 /* Return rpmdb iterator with removals optionally pruned out */
122 static rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmDbiTagVal tag,
123 const char * key, int prune)
125 rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0);
127 tsMembers tsmem = rpmtsMembers(ts);
128 rpmdbPruneIterator(mi, tsmem->removedPackages);
134 * Decides whether to skip a package upgrade/obsoletion on TE color.
136 * @param tscolor color of this transaction
137 * @param color color of this transaction element
138 * @param ocolor header color of the upgraded/obsoleted package
140 * @return non-zero if the package should be skipped
142 static int skipColor(rpm_color_t tscolor, rpm_color_t color, rpm_color_t ocolor)
144 return tscolor && color && ocolor && !(color & ocolor);
147 static int rpmNameVersionCompare(Header first, Header second)
149 const char * one, * two;
152 one = headerGetString(first, RPMTAG_NAME);
153 two = headerGetString(second, RPMTAG_NAME);
154 rc = strcmp(one, two);
157 one = headerGetString(first, RPMTAG_ARCH);
158 two = headerGetString(second, RPMTAG_ARCH);
159 rc = strcmp(one, two);
162 return rpmVersionCompare(first, second);
165 /* Add erase elements for older packages of same color (if any). */
166 static int addUpgradeErasures(rpmts ts, rpm_color_t tscolor,
167 rpmte p, rpm_color_t hcolor, Header h)
170 rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
173 while((oh = rpmdbNextIterator(mi)) != NULL) {
174 /* Ignore colored packages not in our rainbow. */
175 if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
178 /* Skip packages that contain identical NEVRA. */
179 if (rpmNameVersionCompare(h, oh) == 0)
182 if (removePackage(ts, oh, p)) {
187 rpmdbFreeIterator(mi);
191 /* Add erase elements for obsoleted packages of same color (if any). */
192 static int addObsoleteErasures(rpmts ts, rpm_color_t tscolor, rpmte p)
194 rpmstrPool tspool = rpmtsPool(ts);
195 rpmds obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
199 while (rpmdsNext(obsoletes) >= 0 && rc == 0) {
201 rpmdbMatchIterator mi = NULL;
203 if ((Name = rpmdsN(obsoletes)) == NULL)
204 continue; /* XXX can't happen */
206 mi = rpmtsPrunedIterator(ts, RPMDBI_NAME, Name, 1);
208 while((oh = rpmdbNextIterator(mi)) != NULL) {
209 const char *oarch = headerGetString(oh, RPMTAG_ARCH);
212 /* avoid self-obsoleting packages */
213 if (rstreq(rpmteN(p), Name) && rstreq(rpmteA(p), oarch)) {
214 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
215 rpmlog(RPMLOG_DEBUG, " Not obsoleting: %s\n", ohNEVRA);
221 * Rpm prior to 3.0.3 does not have versioned obsoletes.
222 * If no obsoletes version info is available, match all names.
224 match = (rpmdsEVR(obsoletes) == NULL);
226 match = rpmdsMatches(tspool, oh, -1, obsoletes, 1,
230 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
231 rpmlog(RPMLOG_DEBUG, " Obsoletes: %s\t\terases %s\n",
232 rpmdsDNEVR(obsoletes)+2, ohNEVRA);
235 if (removePackage(ts, oh, p)) {
241 rpmdbFreeIterator(mi);
247 * Lookup obsoletions in the added set. In theory there could
248 * be more than one obsoleting package, but we only care whether this
249 * has been obsoleted by *something* or not.
251 static rpmte checkObsoleted(rpmal addedPackages, rpmds thisds)
254 rpmte *matches = NULL;
256 matches = rpmalAllObsoletes(addedPackages, thisds);
265 * Filtered rpmal lookup: on colored transactions there can be more
266 * than one identical NEVR but different arch, this must be allowed.
267 * Only a single element needs to be considred as there can only ever
268 * be one previous element to be replaced.
270 static rpmte checkAdded(rpmal addedPackages, rpm_color_t tscolor,
274 rpmte *matches = NULL;
276 matches = rpmalAllSatisfiesDepend(addedPackages, ds);
278 const char * arch = rpmteA(te);
279 const char * os = rpmteO(te);
281 for (rpmte *m = matches; m && *m; m++) {
283 const char * parch = rpmteA(*m);
284 const char * pos = rpmteO(*m);
286 if (arch == NULL || parch == NULL || os == NULL || pos == NULL)
288 if (!rstreq(arch, parch) || !rstreq(os, pos))
300 * Check for previously added versions and obsoletions.
301 * Return index where to place this element, or -1 to skip.
302 * XXX OBSOLETENAME is a bit of a hack, but gives us what
303 * we want from rpmal: we're only interested in added package
304 * names here, not their provides.
306 static int findPos(rpmts ts, rpm_color_t tscolor, rpmte te, int upgrade)
308 tsMembers tsmem = rpmtsMembers(ts);
309 int oc = tsmem->orderCount;
311 const char * name = rpmteN(te);
312 const char * evr = rpmteEVR(te);
314 rpmstrPool tspool = rpmtsPool(ts);
315 rpmds oldChk = rpmdsSinglePool(tspool, RPMTAG_OBSOLETENAME,
316 name, evr, (RPMSENSE_LESS));
317 rpmds newChk = rpmdsSinglePool(tspool, RPMTAG_OBSOLETENAME,
318 name, evr, (RPMSENSE_GREATER));
319 rpmds sameChk = rpmdsSinglePool(tspool, RPMTAG_OBSOLETENAME,
320 name, evr, (RPMSENSE_EQUAL));
321 rpmds obsChk = rpmteDS(te, RPMTAG_OBSOLETENAME);
323 /* If obsoleting package has already been added, skip this. */
324 if ((p = checkObsoleted(tsmem->addedPackages, rpmteDS(te, RPMTAG_NAME)))) {
329 /* If obsoleted package has already been added, replace with this. */
331 while (rpmdsNext(obsChk) >= 0) {
332 /* XXX Obsoletes are not colored */
333 if ((p = checkAdded(tsmem->addedPackages, 0, te, obsChk))) {
338 /* If same NEVR has already been added, skip this. */
339 if ((p = checkAdded(tsmem->addedPackages, tscolor, te, sameChk))) {
346 /* ...if newer NEVR has already been added, skip this. */
347 if ((p = checkAdded(tsmem->addedPackages, tscolor, te, newChk))) {
352 /* ...if older NEVR has already been added, replace with this. */
353 if ((p = checkAdded(tsmem->addedPackages, tscolor, te, oldChk))) {
359 /* If we found a previous element we've something to say */
360 if (p != NULL && rpmIsVerbose()) {
361 const char *msg = skip ?
362 _("package %s was already added, skipping %s\n") :
363 _("package %s was already added, replacing with %s\n");
364 rpmlog(RPMLOG_WARNING, msg, rpmteNEVRA(p), rpmteNEVRA(te));
367 /* If replacing a previous element, find out where it is. Pooh. */
368 if (!skip && p != NULL) {
369 for (oc = 0; oc < tsmem->orderCount; oc++) {
370 if (p == tsmem->order[oc])
378 return (skip) ? -1 : oc;
381 rpmal rpmtsCreateAl(rpmts ts, rpmElementTypes types)
387 rpmstrPool tspool = rpmtsPool(ts);
389 al = rpmalCreate(tspool, (rpmtsNElements(ts) / 4) + 1, rpmtsFlags(ts),
390 rpmtsColor(ts), rpmtsPrefColor(ts));
392 while ((p = rpmtsiNext(pi, types)))
399 int rpmtsAddInstallElement(rpmts ts, Header h,
400 fnpyKey key, int upgrade, rpmRelocation * relocs)
402 tsMembers tsmem = rpmtsMembers(ts);
403 rpm_color_t tscolor = rpmtsColor(ts);
405 int isSource = headerIsSource(h);
407 int oc = tsmem->orderCount;
409 /* Check for supported payload format if it's a package */
410 if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
415 /* Source packages are never "upgraded" */
419 /* Do lazy (readonly?) open of rpm database for upgrades. */
420 if (upgrade && rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
421 if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
425 p = rpmteNew(ts, h, TR_ADDED, key, relocs);
431 /* Check binary packages for redundancies in the set */
433 oc = findPos(ts, tscolor, p, upgrade);
434 /* If we're replacing a previously added element, free the old one */
435 if (oc >= 0 && oc < tsmem->orderCount) {
436 rpmalDel(tsmem->addedPackages, tsmem->order[oc]);
437 tsmem->order[oc] = rpmteFree(tsmem->order[oc]);
438 /* If newer NEVR was already added, we're done */
445 if (oc >= tsmem->orderAlloced) {
446 tsmem->orderAlloced += (oc - tsmem->orderAlloced) + tsmem->delta;
447 tsmem->order = xrealloc(tsmem->order,
448 tsmem->orderAlloced * sizeof(*tsmem->order));
452 tsmem->order[oc] = p;
453 if (oc == tsmem->orderCount) {
457 if (tsmem->addedPackages == NULL) {
458 tsmem->addedPackages = rpmalCreate(rpmtsPool(ts), 5, rpmtsFlags(ts),
459 tscolor, rpmtsPrefColor(ts));
461 rpmalAdd(tsmem->addedPackages, p);
463 /* Add erasure elements for old versions and obsoletions on upgrades */
464 /* XXX TODO: If either of these fails, we'd need to undo all additions */
466 addUpgradeErasures(ts, tscolor, p, rpmteColor(p), h);
467 addObsoleteErasures(ts, tscolor, p);
474 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
476 return removePackage(ts, h, NULL);
479 /* Cached rpmdb provide lookup, returns 0 if satisfied, 1 otherwise */
480 static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep)
482 const char * Name = rpmdsN(dep);
483 const char * DNEVR = rpmdsDNEVR(dep);
484 rpmTagVal deptag = rpmdsTagN(dep);
485 int *cachedrc = NULL;
486 rpmdbMatchIterator mi = NULL;
489 /* pretrans deps are provided by current packages, don't prune erasures */
490 int prune = (rpmdsFlags(dep) & RPMSENSE_PRETRANS) ? 0 : 1;
491 unsigned int keyhash = 0;
493 /* See if we already looked this up */
495 keyhash = depCacheKeyHash(dcache, DNEVR);
496 if (depCacheGetHEntry(dcache, DNEVR, keyhash, &cachedrc, NULL, NULL)) {
498 rpmdsNotify(dep, "(cached)", rc);
504 * See if a filename dependency is a real file in some package,
505 * taking file state into account: replaced, wrong colored and
506 * not installed files can not satisfy a dependency.
508 if (deptag != RPMTAG_OBSOLETENAME && Name[0] == '/') {
509 mi = rpmtsPrunedIterator(ts, RPMDBI_INSTFILENAMES, Name, prune);
510 while ((h = rpmdbNextIterator(mi)) != NULL) {
511 rpmdsNotify(dep, "(db files)", rc);
514 rpmdbFreeIterator(mi);
517 /* Otherwise look in provides no matter what the dependency looks like */
519 rpmstrPool tspool = rpmtsPool(ts);
520 /* Obsoletes use just name alone, everything else uses provides */
521 rpmTagVal dbtag = RPMDBI_PROVIDENAME;
523 if (deptag == RPMTAG_OBSOLETENAME) {
528 mi = rpmtsPrunedIterator(ts, dbtag, Name, prune);
529 while ((h = rpmdbNextIterator(mi)) != NULL) {
530 /* Provide-indexes can't be used with nevr-only matching */
531 int prix = (selfevr) ? -1 : rpmdbGetIteratorFileNum(mi);
532 int match = rpmdsMatches(tspool, h, prix, dep, selfevr,
535 rpmdsNotify(dep, "(db provides)", rc);
539 rpmdbFreeIterator(mi);
541 rc = (h != NULL) ? 0 : 1;
543 /* Cache the relatively expensive rpmdb lookup results */
544 /* Caching the oddball non-pruned case would mess up other results */
546 depCacheAddHEntry(dcache, xstrdup(DNEVR), keyhash, rc);
551 * Check dep for an unsatisfied dependency.
552 * @param ts transaction set
553 * @param dep dependency
554 * @return 0 if satisfied, 1 if not satisfied
556 static int unsatisfiedDepend(rpmts ts, depCache dcache, rpmds dep)
558 tsMembers tsmem = rpmtsMembers(ts);
561 int adding = (rpmdsInstance(dep) == 0);
562 rpmsenseFlags dsflags = rpmdsFlags(dep);
565 rc = 0; /* assume dependency is satisfied */
568 * New features in rpm packaging implicitly add versioned dependencies
569 * on rpmlib provides. The dependencies look like "rpmlib(YaddaYadda)".
570 * Check those dependencies now.
572 if (dsflags & RPMSENSE_RPMLIB) {
573 if (tsmem->rpmlib == NULL)
574 rpmdsRpmlibPool(rpmtsPool(ts), &(tsmem->rpmlib), NULL);
576 if (tsmem->rpmlib != NULL && rpmdsSearch(tsmem->rpmlib, dep) >= 0) {
577 rpmdsNotify(dep, "(rpmlib provides)", rc);
583 /* Dont look at pre-requisites of already installed packages */
584 if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags))
587 /* Pretrans dependencies can't be satisfied by added packages. */
588 if (!(dsflags & RPMSENSE_PRETRANS)) {
589 rpmte match = rpmalSatisfiesDepend(tsmem->addedPackages, dep);
592 * Handle definitive matches within the added package set.
593 * Self-obsoletes and -conflicts fall through here as we need to
594 * check for possible other matches in the rpmdb.
597 rpmTagVal dtag = rpmdsTagN(dep);
598 /* Requires match, look no further */
599 if (dtag == RPMTAG_REQUIRENAME)
602 /* Conflicts/obsoletes match on another package, look no further */
603 if (rpmteDS(match, dtag) != dep)
608 /* See if the rpmdb provides it */
609 if (rpmdbProvides(ts, dcache, dep) == 0)
612 /* Search for an unsatisfied dependency. */
613 if (adding && !retrying && !(dsflags & RPMSENSE_PRETRANS)) {
614 int xx = rpmtsSolve(ts, dep);
624 if (dsflags & RPMSENSE_MISSINGOK) {
625 /* note the result, but missingok deps are never unsatisfied */
626 rpmdsNotify(dep, "(missingok)", 1);
628 /* dependency is unsatisfied */
630 rpmdsNotify(dep, NULL, rc);
637 /* Check a dependency set for problems */
638 static void checkDS(rpmts ts, depCache dcache, rpmte te,
639 const char * pkgNEVRA, rpmds ds,
640 const char * depName, rpm_color_t tscolor)
643 /* require-problems are unsatisfied, others appear "satisfied" */
644 int is_problem = (rpmdsTagN(ds) == RPMTAG_REQUIRENAME);
647 while (rpmdsNext(ds) >= 0) {
648 /* Filter out dependencies that came along for the ride. */
649 if (depName != NULL && !rstreq(depName, rpmdsN(ds)))
652 /* Ignore colored dependencies not in our rainbow. */
653 dscolor = rpmdsColor(ds);
654 if (tscolor && dscolor && !(tscolor & dscolor))
657 if (unsatisfiedDepend(ts, dcache, ds) == is_problem)
658 rpmteAddDepProblem(te, pkgNEVRA, ds, NULL);
662 /* Check a given dependency type against installed packages */
663 static void checkInstDeps(rpmts ts, depCache dcache, rpmte te,
664 rpmTag depTag, const char *dep)
667 rpmdbMatchIterator mi = rpmtsPrunedIterator(ts, depTag, dep, 1);
668 rpmstrPool pool = rpmtsPool(ts);
670 while ((h = rpmdbNextIterator(mi)) != NULL) {
671 char * pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
672 rpmds ds = rpmdsNewPool(pool, h, depTag, 0);
674 checkDS(ts, dcache, te, pkgNEVRA, ds, dep, 0);
679 rpmdbFreeIterator(mi);
682 int rpmtsCheck(rpmts ts)
684 rpm_color_t tscolor = rpmtsColor(ts);
685 rpmtsi pi = NULL; rpmte p;
688 depCache dcache = NULL;
690 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
692 /* Do lazy, readonly, open of rpm database. */
693 if (rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
694 if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
699 /* XXX FIXME: figure some kind of heuristic for the cache size */
700 dcache = depCacheCreate(5001, rstrhash, strcmp,
701 (depCacheFreeKey)rfree, NULL);
704 * Look at all of the added packages and make sure their dependencies
708 while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
709 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
711 rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
712 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
714 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME),
716 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME),
718 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_OBSOLETENAME),
721 /* Check provides against conflicts in installed packages. */
722 while (rpmdsNext(provides) >= 0) {
723 checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides));
726 /* Skip obsoletion checks for source packages (ie build) */
727 if (rpmteIsSource(p))
730 /* Check package name (not provides!) against installed obsoletes */
731 checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p));
736 * Look at the removed packages and make sure they aren't critical.
739 while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
740 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
741 rpmfi fi = rpmfiInit(rpmteFI(p), 0);
743 rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
744 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
746 /* Check provides and filenames against installed dependencies. */
747 while (rpmdsNext(provides) >= 0) {
748 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmdsN(provides));
751 while (rpmfiNext(fi) >= 0) {
752 if (RPMFILE_IS_INSTALLED(rpmfiFState(fi)))
753 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, rpmfiFN(fi));
759 depCacheFree(dcache);
761 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
764 (void) rpmtsCloseDB(ts);