1 /** \ingroup rpmts
2 * \file lib/depends.c
3 */
4
5 #include "system.h"
6
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>
13
14 #include "lib/rpmts_internal.h"
15 #include "lib/rpmte_internal.h"
16 #include "lib/rpmds_internal.h"
17 #include "lib/rpmfi_internal.h" /* rpmfiles stuff for now */
18 #include "lib/misc.h"
19
20 #include "lib/backend/dbiset.h"
21
22 #include "debug.h"
23
24 const char * const RPMVERSION = VERSION;
25
26 const char * const rpmNAME = PACKAGE;
27
28 const char * const rpmEVR = VERSION;
29
30 const int rpmFLAGS = RPMSENSE_EQUAL;
31
32 #undef HASHTYPE
33 #undef HTKEYTYPE
34 #undef HTDATATYPE
35
36 #define HASHTYPE depCache
37 #define HTKEYTYPE const char *
38 #define HTDATATYPE int
39 #include "lib/rpmhash.H"
40 #include "lib/rpmhash.C"
41 #undef HASHTYPE
42 #undef HTKEYTYPE
43 #undef HTDATATYPE
44
45 #define HASHTYPE packageHash
46 #define HTKEYTYPE unsigned int
47 #define HTDATATYPE struct rpmte_s *
48 #include "rpmhash.C"
49 #undef HASHTYPE
50 #undef HTKEYTYPE
51 #undef HTDATATYPE
52
53 #define HASHTYPE filedepHash
54 #define HTKEYTYPE rpmsid
55 #define HTDATATYPE rpmsid
56 #include "rpmhash.H"
57 #include "rpmhash.C"
58 #undef HASHTYPE
59 #undef HTKEYTYPE
60 #undef HTDATATYPE
61
62 #define HASHTYPE depexistsHash
63 #define HTKEYTYPE rpmsid
64 #include "lib/rpmhash.H"
65 #include "lib/rpmhash.C"
66 #undef HASHTYPE
67 #undef HTKEYTYPE
68
69 /**
70 * Check for supported payload format in header.
71 * @param h header to check
72 * @return RPMRC_OK if supported, RPMRC_FAIL otherwise
73 */
headerCheckPayloadFormat(Header h)74 static rpmRC headerCheckPayloadFormat(Header h) {
75 rpmRC rc = RPMRC_OK;
76 const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);
77 /*
78 * XXX Ugh, rpm 3.x packages don't have payload format tag. Instead
79 * of blindly allowing, should check somehow (HDRID existence or... ?)
80 */
81 if (!payloadfmt) return rc;
82
83 if (!rstreq(payloadfmt, "cpio")) {
84 char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
85 if (payloadfmt && rstreq(payloadfmt, "drpm")) {
86 rpmlog(RPMLOG_ERR,
87 _("%s is a Delta RPM and cannot be directly installed\n"),
88 nevra);
89 } else {
90 rpmlog(RPMLOG_ERR,
91 _("Unsupported payload (%s) in package %s\n"),
92 payloadfmt ? payloadfmt : "none", nevra);
93 }
94 free(nevra);
95 rc = RPMRC_FAIL;
96 }
97 return rc;
98 }
99
100 /**
101 * Add removed package instance to ordered transaction set.
102 * @param ts transaction set
103 * @param h header
104 * @param depends installed package of pair (or RPMAL_NOMATCH on erase)
105 * @return 0 on success
106 */
removePackage(rpmts ts,Header h,rpmte depends)107 static int removePackage(rpmts ts, Header h, rpmte depends)
108 {
109 tsMembers tsmem = rpmtsMembers(ts);
110 rpmte p, *pp;
111 unsigned int dboffset = headerGetInstance(h);
112
113 /* Can't remove what's not installed */
114 if (dboffset == 0) return 1;
115
116 /* Filter out duplicate erasures. */
117 if (packageHashGetEntry(tsmem->removedPackages, dboffset, &pp, NULL, NULL)) {
118 if (depends)
119 rpmteSetDependsOn(pp[0], depends);
120 return 0;
121 }
122
123 p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL, 0);
124 if (p == NULL)
125 return 1;
126
127 packageHashAddEntry(tsmem->removedPackages, dboffset, p);
128
129 if (tsmem->orderCount >= tsmem->orderAlloced) {
130 tsmem->orderAlloced += (tsmem->orderCount - tsmem->orderAlloced) + tsmem->delta;
131 tsmem->order = xrealloc(tsmem->order, sizeof(*tsmem->order) * tsmem->orderAlloced);
132 }
133
134 rpmteSetDependsOn(p, depends);
135
136 tsmem->order[tsmem->orderCount] = p;
137 tsmem->orderCount++;
138
139 return 0;
140 }
141
142 /* Return rpmdb iterator with removals optionally pruned out */
rpmtsPrunedIterator(rpmts ts,rpmDbiTagVal tag,const char * key,int prune)143 rpmdbMatchIterator rpmtsPrunedIterator(rpmts ts, rpmDbiTagVal tag,
144 const char * key, int prune)
145 {
146 rpmdbMatchIterator mi = rpmtsInitIterator(ts, tag, key, 0);
147 if (prune) {
148 tsMembers tsmem = rpmtsMembers(ts);
149 rpmdbPruneIterator(mi, tsmem->removedPackages);
150 }
151 return mi;
152 }
153
154 /**
155 * Decides whether to skip a package upgrade/obsoletion on TE color.
156 *
157 * @param tscolor color of this transaction
158 * @param color color of this transaction element
159 * @param ocolor header color of the upgraded/obsoleted package
160 *
161 * @return non-zero if the package should be skipped
162 */
skipColor(rpm_color_t tscolor,rpm_color_t color,rpm_color_t ocolor)163 static int skipColor(rpm_color_t tscolor, rpm_color_t color, rpm_color_t ocolor)
164 {
165 return tscolor && color && ocolor && !(color & ocolor);
166 }
167
168 /* Add erase elements for older packages of same color (if any). */
addSelfErasures(rpmts ts,rpm_color_t tscolor,int op,rpmte p,rpm_color_t hcolor,Header h)169 static int addSelfErasures(rpmts ts, rpm_color_t tscolor, int op,
170 rpmte p, rpm_color_t hcolor, Header h)
171 {
172 Header oh;
173 rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
174 int rc = 0;
175
176 while ((oh = rpmdbNextIterator(mi)) != NULL) {
177 /* Ignore colored packages not in our rainbow. */
178 if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
179 continue;
180
181 /* On reinstall, skip packages with differing NEVRA. */
182 if (op != RPMTE_UPGRADE) {
183 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
184 if (!rstreq(rpmteNEVRA(p), ohNEVRA)) {
185 free(ohNEVRA);
186 continue;
187 }
188 free(ohNEVRA);
189 }
190
191 if (removePackage(ts, oh, p)) {
192 rc = 1;
193 break;
194 }
195 }
196 rpmdbFreeIterator(mi);
197 return rc;
198 }
199
200 /* Add erase elements for obsoleted packages of same color (if any). */
addObsoleteErasures(rpmts ts,rpm_color_t tscolor,rpmte p)201 static int addObsoleteErasures(rpmts ts, rpm_color_t tscolor, rpmte p)
202 {
203 rpmstrPool tspool = rpmtsPool(ts);
204 rpmds obsoletes = rpmdsInit(rpmteDS(p, RPMTAG_OBSOLETENAME));
205 Header oh;
206 int rc = 0;
207
208 while (rpmdsNext(obsoletes) >= 0 && rc == 0) {
209 const char * Name;
210 rpmdbMatchIterator mi = NULL;
211
212 if ((Name = rpmdsN(obsoletes)) == NULL)
213 continue; /* XXX can't happen */
214
215 mi = rpmtsPrunedIterator(ts, RPMDBI_NAME, Name, 1);
216
217 while ((oh = rpmdbNextIterator(mi)) != NULL) {
218 int match;
219
220 /*
221 * Rpm prior to 3.0.3 does not have versioned obsoletes.
222 * If no obsoletes version info is available, match all names.
223 */
224 match = (rpmdsEVR(obsoletes) == NULL);
225 if (!match)
226 match = rpmdsMatches(tspool, oh, -1, obsoletes, 1);
227
228 if (match) {
229 char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
230 rpmlog(RPMLOG_DEBUG, " Obsoletes: %s\t\terases %s\n",
231 rpmdsDNEVR(obsoletes)+2, ohNEVRA);
232 free(ohNEVRA);
233
234 if (removePackage(ts, oh, p)) {
235 rc = 1;
236 break;
237 }
238 }
239 }
240 rpmdbFreeIterator(mi);
241 }
242 return rc;
243 }
244
245 /*
246 * Lookup obsoletions in the added set. In theory there could
247 * be more than one obsoleting package, but we only care whether this
248 * has been obsoleted by *something* or not.
249 */
checkObsoleted(rpmal addedPackages,rpmds thisds)250 static rpmte checkObsoleted(rpmal addedPackages, rpmds thisds)
251 {
252 rpmte p = NULL;
253 rpmte *matches = NULL;
254
255 matches = rpmalAllObsoletes(addedPackages, thisds);
256 if (matches) {
257 p = matches[0];
258 free(matches);
259 }
260 return p;
261 }
262
263 /*
264 * Filtered rpmal lookup: on colored transactions there can be more
265 * than one identical NEVR but different arch, this must be allowed.
266 * Only a single element needs to be considred as there can only ever
267 * be one previous element to be replaced.
268 */
checkAdded(rpmal addedPackages,rpm_color_t tscolor,rpmte te,rpmds ds)269 static rpmte checkAdded(rpmal addedPackages, rpm_color_t tscolor,
270 rpmte te, rpmds ds)
271 {
272 rpmte p = NULL;
273 rpmte *matches = NULL;
274
275 matches = rpmalAllSatisfiesDepend(addedPackages, ds);
276 if (matches) {
277 const char * arch = rpmteA(te);
278 const char * os = rpmteO(te);
279
280 for (rpmte *m = matches; m && *m; m++) {
281 if (tscolor) {
282 const char * parch = rpmteA(*m);
283 const char * pos = rpmteO(*m);
284
285 if (arch == NULL || parch == NULL || os == NULL || pos == NULL)
286 continue;
287 if (!rstreq(arch, parch) || !rstreq(os, pos))
288 continue;
289 }
290 p = *m;
291 break;
292 }
293 free(matches);
294 }
295 return p;
296 }
297
298 /*
299 * Check for previously added versions and obsoletions.
300 * Return index where to place this element, or -1 to skip.
301 * XXX OBSOLETENAME is a bit of a hack, but gives us what
302 * we want from rpmal: we're only interested in added package
303 * names here, not their provides.
304 */
findPos(rpmts ts,rpm_color_t tscolor,rpmte te,int upgrade)305 static int findPos(rpmts ts, rpm_color_t tscolor, rpmte te, int upgrade)
306 {
307 tsMembers tsmem = rpmtsMembers(ts);
308 int oc = tsmem->orderCount;
309 int skip = 0;
310 const char * name = rpmteN(te);
311 const char * evr = rpmteEVR(te);
312 rpmte p;
313 rpmstrPool tspool = rpmtsPool(ts);
314 rpmds oldChk = rpmdsSinglePool(tspool, RPMTAG_OBSOLETENAME,
315 name, evr, (RPMSENSE_LESS));
316 rpmds newChk = rpmdsSinglePool(tspool, RPMTAG_OBSOLETENAME,
317 name, evr, (RPMSENSE_GREATER));
318 rpmds sameChk = rpmdsSinglePool(tspool, RPMTAG_OBSOLETENAME,
319 name, evr, (RPMSENSE_EQUAL));
320 rpmds obsChk = rpmteDS(te, RPMTAG_OBSOLETENAME);
321
322 /* If obsoleting package has already been added, skip this. */
323 if ((p = checkObsoleted(tsmem->addedPackages, rpmteDS(te, RPMTAG_NAME)))) {
324 skip = 1;
325 goto exit;
326 }
327
328 /* If obsoleted package has already been added, replace with this. */
329 rpmdsInit(obsChk);
330 while (rpmdsNext(obsChk) >= 0) {
331 /* XXX Obsoletes are not colored */
332 if ((p = checkAdded(tsmem->addedPackages, 0, te, obsChk))) {
333 goto exit;
334 }
335 }
336
337 /* If same NEVR has already been added, skip this. */
338 if ((p = checkAdded(tsmem->addedPackages, tscolor, te, sameChk))) {
339 skip = 1;
340 goto exit;
341 }
342
343 /* On upgrades... */
344 if (upgrade) {
345 /* ...if newer NEVR has already been added, skip this. */
346 if ((p = checkAdded(tsmem->addedPackages, tscolor, te, newChk))) {
347 skip = 1;
348 goto exit;
349 }
350
351 /* ...if older NEVR has already been added, replace with this. */
352 if ((p = checkAdded(tsmem->addedPackages, tscolor, te, oldChk))) {
353 goto exit;
354 }
355 }
356
357 exit:
358 /* If we found a previous element we've something to say */
359 if (p != NULL && rpmIsVerbose()) {
360 const char *msg = skip ?
361 _("package %s was already added, skipping %s\n") :
362 _("package %s was already added, replacing with %s\n");
363 rpmlog(RPMLOG_WARNING, msg, rpmteNEVRA(p), rpmteNEVRA(te));
364 }
365
366 /* If replacing a previous element, find out where it is. Pooh. */
367 if (!skip && p != NULL) {
368 for (oc = 0; oc < tsmem->orderCount; oc++) {
369 if (p == tsmem->order[oc])
370 break;
371 }
372 }
373
374 rpmdsFree(oldChk);
375 rpmdsFree(newChk);
376 rpmdsFree(sameChk);
377 return (skip) ? -1 : oc;
378 }
379
rpmtsCreateAl(rpmts ts,rpmElementTypes types)380 rpmal rpmtsCreateAl(rpmts ts, rpmElementTypes types)
381 {
382 rpmal al = NULL;
383 if (ts) {
384 rpmte p;
385 rpmtsi pi;
386
387 al = rpmalCreate(ts, (rpmtsNElements(ts) / 4) + 1);
388 pi = rpmtsiInit(ts);
389 while ((p = rpmtsiNext(pi, types)))
390 rpmalAdd(al, p);
391 rpmtsiFree(pi);
392 }
393 return al;
394 }
395
addPackage(rpmts ts,Header h,fnpyKey key,int op,rpmRelocation * relocs)396 static int addPackage(rpmts ts, Header h,
397 fnpyKey key, int op, rpmRelocation * relocs)
398 {
399 tsMembers tsmem = rpmtsMembers(ts);
400 rpm_color_t tscolor = rpmtsColor(ts);
401 rpmte p = NULL;
402 int isSource = headerIsSource(h);
403 int ec = 0;
404 int oc = tsmem->orderCount;
405
406 /* Check for supported payload format if it's a package */
407 if (key && headerCheckPayloadFormat(h) != RPMRC_OK) {
408 ec = 1;
409 goto exit;
410 }
411
412 /* Source packages are never "upgraded" */
413 if (isSource)
414 op = RPMTE_INSTALL;
415
416 /* Do lazy (readonly?) open of rpm database for upgrades. */
417 if (op != RPMTE_INSTALL && rpmtsGetRdb(ts) == NULL && rpmtsGetDBMode(ts) != -1) {
418 if ((ec = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
419 goto exit;
420 }
421
422 p = rpmteNew(ts, h, TR_ADDED, key, relocs, op);
423 if (p == NULL) {
424 ec = 1;
425 goto exit;
426 }
427
428 /* Check binary packages for redundancies in the set */
429 if (!isSource) {
430 oc = findPos(ts, tscolor, p, (op == RPMTE_UPGRADE));
431 /* If we're replacing a previously added element, free the old one */
432 if (oc >= 0 && oc < tsmem->orderCount) {
433 rpmalDel(tsmem->addedPackages, tsmem->order[oc]);
434 tsmem->order[oc] = rpmteFree(tsmem->order[oc]);
435 /* If newer NEVR was already added, we're done */
436 } else if (oc < 0) {
437 p = rpmteFree(p);
438 goto exit;
439 }
440 }
441
442 if (oc >= tsmem->orderAlloced) {
443 tsmem->orderAlloced += (oc - tsmem->orderAlloced) + tsmem->delta;
444 tsmem->order = xrealloc(tsmem->order,
445 tsmem->orderAlloced * sizeof(*tsmem->order));
446 }
447
448
449 tsmem->order[oc] = p;
450 if (oc == tsmem->orderCount) {
451 tsmem->orderCount++;
452 }
453
454 if (tsmem->addedPackages == NULL) {
455 tsmem->addedPackages = rpmalCreate(ts, 5);
456 }
457 rpmalAdd(tsmem->addedPackages, p);
458
459 /* Add erasure elements for old versions and obsoletions on upgrades */
460 /* XXX TODO: If either of these fails, we'd need to undo all additions */
461 if (op != RPMTE_INSTALL)
462 addSelfErasures(ts, tscolor, op, p, rpmteColor(p), h);
463 if (op == RPMTE_UPGRADE)
464 addObsoleteErasures(ts, tscolor, p);
465
466 exit:
467 return ec;
468 }
469
rpmtsAddInstallElement(rpmts ts,Header h,fnpyKey key,int upgrade,rpmRelocation * relocs)470 int rpmtsAddInstallElement(rpmts ts, Header h,
471 fnpyKey key, int upgrade, rpmRelocation * relocs)
472 {
473 int op = (upgrade == 0) ? RPMTE_INSTALL : RPMTE_UPGRADE;
474 if (rpmtsSetupTransactionPlugins(ts) == RPMRC_FAIL)
475 return 1;
476 return addPackage(ts, h, key, op, relocs);
477 }
478
rpmtsAddReinstallElement(rpmts ts,Header h,fnpyKey key)479 int rpmtsAddReinstallElement(rpmts ts, Header h, fnpyKey key)
480 {
481 if (rpmtsSetupTransactionPlugins(ts) == RPMRC_FAIL)
482 return 1;
483 /* TODO: pull relocations from installed package */
484 /* TODO: should reinstall of non-installed package fail? */
485 return addPackage(ts, h, key, RPMTE_REINSTALL, NULL);
486 }
487
rpmtsAddEraseElement(rpmts ts,Header h,int dboffset)488 int rpmtsAddEraseElement(rpmts ts, Header h, int dboffset)
489 {
490 if (rpmtsSetupTransactionPlugins(ts) == RPMRC_FAIL)
491 return 1;
492 return removePackage(ts, h, NULL);
493 }
494
495 /* Cached rpmdb provide lookup, returns 0 if satisfied, 1 otherwise */
rpmdbProvides(rpmts ts,depCache dcache,rpmds dep,dbiIndexSet * matches)496 static int rpmdbProvides(rpmts ts, depCache dcache, rpmds dep, dbiIndexSet *matches)
497 {
498 const char * Name = rpmdsN(dep);
499 const char * DNEVR = rpmdsDNEVR(dep);
500 rpmTagVal deptag = rpmdsTagN(dep);
501 int *cachedrc = NULL;
502 rpmdbMatchIterator mi = NULL;
503 Header h = NULL;
504 int rc = 0;
505 /* pretrans deps are provided by current packages, don't prune erasures */
506 int prune = (rpmdsFlags(dep) & RPMSENSE_PRETRANS) ? 0 : 1;
507 unsigned int keyhash = 0;
508
509 /* See if we already looked this up */
510 if (prune && !matches) {
511 keyhash = depCacheKeyHash(dcache, DNEVR);
512 if (depCacheGetHEntry(dcache, DNEVR, keyhash, &cachedrc, NULL, NULL)) {
513 rc = *cachedrc;
514 rpmdsNotify(dep, "(cached)", rc);
515 return rc;
516 }
517 }
518
519 if (matches)
520 *matches = dbiIndexSetNew(0);
521 /*
522 * See if a filename dependency is a real file in some package,
523 * taking file state into account: replaced, wrong colored and
524 * not installed files can not satisfy a dependency.
525 */
526 if (deptag != RPMTAG_OBSOLETENAME && Name[0] == '/') {
527 mi = rpmtsPrunedIterator(ts, RPMDBI_INSTFILENAMES, Name, prune);
528 while ((h = rpmdbNextIterator(mi)) != NULL) {
529 /* Ignore self-conflicts */
530 if (deptag == RPMTAG_CONFLICTNAME) {
531 unsigned int instance = headerGetInstance(h);
532 if (instance && instance == rpmdsInstance(dep))
533 continue;
534 }
535 if (matches) {
536 dbiIndexSetAppendOne(*matches, headerGetInstance(h), 0, 0);
537 continue;
538 }
539 rpmdsNotify(dep, "(db files)", rc);
540 break;
541 }
542 rpmdbFreeIterator(mi);
543 }
544
545 /* Otherwise look in provides no matter what the dependency looks like */
546 if (h == NULL) {
547 rpmstrPool tspool = rpmtsPool(ts);
548 /* Obsoletes use just name alone, everything else uses provides */
549 rpmTagVal dbtag = RPMDBI_PROVIDENAME;
550 int selfevr = 0;
551 if (deptag == RPMTAG_OBSOLETENAME) {
552 dbtag = RPMDBI_NAME;
553 selfevr = 1;
554 }
555
556 mi = rpmtsPrunedIterator(ts, dbtag, Name, prune);
557 while ((h = rpmdbNextIterator(mi)) != NULL) {
558 /* Provide-indexes can't be used with nevr-only matching */
559 int prix = (selfevr) ? -1 : rpmdbGetIteratorFileNum(mi);
560 int match = rpmdsMatches(tspool, h, prix, dep, selfevr);
561 /* Ignore self-obsoletes and self-conflicts */
562 if (match && (deptag == RPMTAG_OBSOLETENAME || deptag == RPMTAG_CONFLICTNAME)) {
563 unsigned int instance = headerGetInstance(h);
564 if (instance && instance == rpmdsInstance(dep))
565 match = 0;
566 }
567 if (match) {
568 if (matches) {
569 dbiIndexSetAppendOne(*matches, headerGetInstance(h), 0, 0);
570 continue;
571 }
572 rpmdsNotify(dep, "(db provides)", rc);
573 break;
574 }
575 }
576 rpmdbFreeIterator(mi);
577 }
578 rc = (h != NULL) ? 0 : 1;
579
580 if (matches) {
581 dbiIndexSetUniq(*matches, 0);
582 rc = dbiIndexSetCount(*matches) ? 0 : 1;
583 }
584
585 /* Cache the relatively expensive rpmdb lookup results */
586 /* Caching the oddball non-pruned case would mess up other results */
587 if (prune && !matches)
588 depCacheAddHEntry(dcache, xstrdup(DNEVR), keyhash, rc);
589 return rc;
590 }
591
unsatisfiedDependSet(rpmts ts,rpmds dep)592 static dbiIndexSet unsatisfiedDependSet(rpmts ts, rpmds dep)
593 {
594 dbiIndexSet set1 = NULL, set2 = NULL;
595 tsMembers tsmem = rpmtsMembers(ts);
596 rpmsenseFlags dsflags = rpmdsFlags(dep);
597
598 if (dsflags & RPMSENSE_RPMLIB)
599 goto exit;
600
601 if (rpmdsIsRich(dep)) {
602 rpmds ds1, ds2;
603 rpmrichOp op;
604 char *emsg = 0;
605
606 if (rpmdsParseRichDep(dep, &ds1, &ds2, &op, &emsg) != RPMRC_OK) {
607 rpmdsNotify(dep, emsg ? emsg : "(parse error)", 1);
608 _free(emsg);
609 goto exit;
610 }
611 /* only a subset of ops is supported in set mode */
612 if (op != RPMRICHOP_WITH && op != RPMRICHOP_WITHOUT
613 && op != RPMRICHOP_OR && op != RPMRICHOP_SINGLE) {
614 rpmdsNotify(dep, "(unsupported op in set mode)", 1);
615 goto exit_rich;
616 }
617
618 set1 = unsatisfiedDependSet(ts, ds1);
619 if (op == RPMRICHOP_SINGLE)
620 goto exit_rich;
621 if (op != RPMRICHOP_OR && dbiIndexSetCount(set1) == 0)
622 goto exit_rich;
623 set2 = unsatisfiedDependSet(ts, ds2);
624 if (op == RPMRICHOP_WITH) {
625 dbiIndexSetFilterSet(set1, set2, 0);
626 } else if (op == RPMRICHOP_WITHOUT) {
627 dbiIndexSetPruneSet(set1, set2, 0);
628 } else if (op == RPMRICHOP_OR) {
629 dbiIndexSetAppendSet(set1, set2, 0);
630 }
631 exit_rich:
632 ds1 = rpmdsFree(ds1);
633 ds2 = rpmdsFree(ds2);
634 goto exit;
635 }
636
637 /* match database entries */
638 rpmdbProvides(ts, NULL, dep, &set1);
639
640 /* Pretrans dependencies can't be satisfied by added packages. */
641 if (!(dsflags & RPMSENSE_PRETRANS)) {
642 rpmte *matches = rpmalAllSatisfiesDepend(tsmem->addedPackages, dep);
643 if (matches) {
644 for (rpmte *p = matches; *p; p++)
645 dbiIndexSetAppendOne(set1, rpmalLookupTE(tsmem->addedPackages, *p), 1, 0);
646 }
647 _free(matches);
648 }
649
650 exit:
651 set2 = dbiIndexSetFree(set2);
652 return set1 ? set1 : dbiIndexSetNew(0);
653 }
654
655 /**
656 * Check dep for an unsatisfied dependency.
657 * @param ts transaction set
658 * @param dcache dependency cache
659 * @param dep dependency
660 * @return 0 if satisfied, 1 if not satisfied
661 */
unsatisfiedDepend(rpmts ts,depCache dcache,rpmds dep)662 static int unsatisfiedDepend(rpmts ts, depCache dcache, rpmds dep)
663 {
664 tsMembers tsmem = rpmtsMembers(ts);
665 int rc;
666 int retrying = 0;
667 int adding = (rpmdsInstance(dep) == 0);
668 rpmsenseFlags dsflags = rpmdsFlags(dep);
669
670 retry:
671 rc = 0; /* assume dependency is satisfied */
672
673 /*
674 * New features in rpm packaging implicitly add versioned dependencies
675 * on rpmlib provides. The dependencies look like "rpmlib(YaddaYadda)".
676 * Check those dependencies now.
677 */
678 if (dsflags & RPMSENSE_RPMLIB) {
679 if (tsmem->rpmlib == NULL)
680 rpmdsRpmlibPool(rpmtsPool(ts), &(tsmem->rpmlib), NULL);
681
682 if (tsmem->rpmlib != NULL && rpmdsSearch(tsmem->rpmlib, dep) >= 0) {
683 rpmdsNotify(dep, "(rpmlib provides)", rc);
684 goto exit;
685 }
686 goto unsatisfied;
687 }
688
689 /* Dont look at pre-requisites of already installed packages */
690 if (!adding && isTransientReq(dsflags))
691 goto exit;
692
693 /* Handle rich dependencies */
694 if (rpmdsIsRich(dep)) {
695 rpmds ds1, ds2;
696 rpmrichOp op;
697 char *emsg = 0;
698 if (rpmdsParseRichDep(dep, &ds1, &ds2, &op, &emsg) != RPMRC_OK) {
699 rc = rpmdsTagN(dep) == RPMTAG_CONFLICTNAME ? 0 : 1;
700 if (rpmdsInstance(dep) != 0)
701 rc = !rc; /* ignore errors for installed packages */
702 rpmdsNotify(dep, emsg ? emsg : "(parse error)", rc);
703 _free(emsg);
704 goto exit;
705 }
706 if (op == RPMRICHOP_WITH || op == RPMRICHOP_WITHOUT) {
707 /* switch to set mode processing */
708 dbiIndexSet set = unsatisfiedDependSet(ts, dep);
709 rc = dbiIndexSetCount(set) ? 0 : 1;
710 dbiIndexSetFree(set);
711 ds1 = rpmdsFree(ds1);
712 ds2 = rpmdsFree(ds2);
713 rpmdsNotify(dep, "(rich)", rc);
714 goto exit;
715 }
716 if (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS) {
717 /* A IF B -> A OR NOT(B) */
718 /* A UNLESS B -> A AND NOT(B) */
719 if (rpmdsIsRich(ds2)) {
720 /* check if this has an ELSE clause */
721 rpmds ds21 = NULL, ds22 = NULL;
722 rpmrichOp op2;
723 if (rpmdsParseRichDep(ds2, &ds21, &ds22, &op2, NULL) == RPMRC_OK && op2 == RPMRICHOP_ELSE) {
724 /* A IF B ELSE C -> (A OR NOT(B)) AND (C OR B) */
725 /* A UNLESS B ELSE C -> (A AND NOT(B)) OR (C AND B) */
726 rc = !unsatisfiedDepend(ts, dcache, ds21); /* NOT(B) */
727 if ((rc && op == RPMRICHOP_IF) || (!rc && op == RPMRICHOP_UNLESS)) {
728 rc = unsatisfiedDepend(ts, dcache, ds1); /* A */
729 } else {
730 rc = unsatisfiedDepend(ts, dcache, ds22); /* C */
731 }
732 rpmdsFree(ds21);
733 rpmdsFree(ds22);
734 goto exitrich;
735 }
736 rpmdsFree(ds21);
737 rpmdsFree(ds22);
738 }
739 rc = !unsatisfiedDepend(ts, dcache, ds2); /* NOT(B) */
740 if ((rc && op == RPMRICHOP_IF) || (!rc && op == RPMRICHOP_UNLESS))
741 rc = unsatisfiedDepend(ts, dcache, ds1);
742 } else {
743 rc = unsatisfiedDepend(ts, dcache, ds1);
744 if ((rc && op == RPMRICHOP_OR) || (!rc && op == RPMRICHOP_AND))
745 rc = unsatisfiedDepend(ts, dcache, ds2);
746 }
747 exitrich:
748 ds1 = rpmdsFree(ds1);
749 ds2 = rpmdsFree(ds2);
750 rpmdsNotify(dep, "(rich)", rc);
751 goto exit;
752 }
753
754 /* Pretrans dependencies can't be satisfied by added packages. */
755 if (!(dsflags & RPMSENSE_PRETRANS)) {
756 rpmte *matches = rpmalAllSatisfiesDepend(tsmem->addedPackages, dep);
757 int match = matches && *matches;
758 _free(matches);
759 if (match)
760 goto exit;
761 }
762
763 /* See if the rpmdb provides it */
764 if (rpmdbProvides(ts, dcache, dep, NULL) == 0)
765 goto exit;
766
767 /* Search for an unsatisfied dependency. */
768 if (adding && !retrying && !(dsflags & RPMSENSE_PRETRANS)) {
769 int xx = rpmtsSolve(ts, dep);
770 if (xx == 0)
771 goto exit;
772 if (xx == -1) {
773 retrying = 1;
774 goto retry;
775 }
776 }
777
778 unsatisfied:
779 if (dsflags & RPMSENSE_MISSINGOK) {
780 /* note the result, but missingok deps are never unsatisfied */
781 rpmdsNotify(dep, "(missingok)", 1);
782 } else {
783 /* dependency is unsatisfied */
784 rc = 1;
785 rpmdsNotify(dep, NULL, rc);
786 }
787
788 exit:
789 return rc;
790 }
791
792 /* Check a dependency set for problems */
checkDS(rpmts ts,depCache dcache,rpmte te,const char * pkgNEVRA,rpmds ds,rpm_color_t tscolor)793 static void checkDS(rpmts ts, depCache dcache, rpmte te,
794 const char * pkgNEVRA, rpmds ds,
795 rpm_color_t tscolor)
796 {
797 rpm_color_t dscolor;
798 /* require-problems are unsatisfied, others appear "satisfied" */
799 int is_problem = (rpmdsTagN(ds) == RPMTAG_REQUIRENAME);
800
801 ds = rpmdsInit(ds);
802 while (rpmdsNext(ds) >= 0) {
803 /* Ignore colored dependencies not in our rainbow. */
804 dscolor = rpmdsColor(ds);
805 if (tscolor && dscolor && !(tscolor & dscolor))
806 continue;
807
808 if (unsatisfiedDepend(ts, dcache, ds) == is_problem)
809 rpmteAddDepProblem(te, pkgNEVRA, ds, NULL);
810 }
811 }
812
813 /* Check a given dependency against installed packages */
checkInstDeps(rpmts ts,depCache dcache,rpmte te,rpmTag depTag,const char * dep,rpmds depds,int neg)814 static void checkInstDeps(rpmts ts, depCache dcache, rpmte te,
815 rpmTag depTag, const char *dep, rpmds depds, int neg)
816 {
817 Header h;
818 rpmdbMatchIterator mi;
819 rpmstrPool pool = rpmtsPool(ts);
820 char *ndep = NULL;
821 /* require-problems are unsatisfied, others appear "satisfied" */
822 int is_problem = (depTag == RPMTAG_REQUIRENAME);
823
824 if (depds)
825 dep = rpmdsN(depds);
826 if (neg) {
827 ndep = rmalloc(strlen(dep) + 2);
828 ndep[0] = '!';
829 strcpy(ndep + 1, dep);
830 dep = ndep;
831 }
832
833 mi = rpmtsPrunedIterator(ts, depTag, dep, 1);
834 while ((h = rpmdbNextIterator(mi)) != NULL) {
835 int match = 1;
836 rpmds ds;
837
838 /* Ignore self-obsoletes and self-conflicts */
839 if (depTag == RPMTAG_OBSOLETENAME || depTag == RPMTAG_CONFLICTNAME) {
840 unsigned int instance = headerGetInstance(h);
841 if (instance && instance == rpmteDBInstance(te))
842 continue;
843 }
844
845 ds = rpmdsNewPool(pool, h, depTag, 0);
846 rpmdsSetIx(ds, rpmdbGetIteratorFileNum(mi));
847
848 /* Is it in our range at all? (but file deps have no range) */
849 if (depds)
850 match = rpmdsCompare(ds, depds);
851
852 if (match && unsatisfiedDepend(ts, dcache, ds) == is_problem) {
853 char *pkgNEVRA = headerGetAsString(h, RPMTAG_NEVRA);
854 rpmteAddDepProblem(te, pkgNEVRA, ds, NULL);
855 free(pkgNEVRA);
856 }
857
858 rpmdsFree(ds);
859 }
860 rpmdbFreeIterator(mi);
861 free(ndep);
862 }
863
checkInstFileDeps(rpmts ts,depCache dcache,rpmte te,rpmTag depTag,rpmfi fi,int is_not,filedepHash cache,fingerPrintCache * fpcp)864 static void checkInstFileDeps(rpmts ts, depCache dcache, rpmte te,
865 rpmTag depTag, rpmfi fi, int is_not,
866 filedepHash cache, fingerPrintCache *fpcp)
867 {
868 rpmstrPool pool = rpmtsPool(ts);
869 fingerPrintCache fpc = *fpcp;
870 fingerPrint * fp = NULL;
871 rpmsid basename = rpmfiBNId(fi);
872 rpmsid dirname;
873 rpmsid *dirnames = 0;
874 int ndirnames = 0;
875 int i;
876
877 filedepHashGetEntry(cache, basename, &dirnames, &ndirnames, NULL);
878 if (!ndirnames)
879 return;
880 dirname = rpmfiDNId(fi);
881 for (i = 0; i < ndirnames; i++) {
882 char *fpdep = 0;
883 const char *dep;
884 if (dirnames[i] == dirname) {
885 dep = rpmfiFN(fi);
886 } else {
887 if (!fpc)
888 *fpcp = fpc = fpCacheCreate(1001, pool);
889 if (!fp)
890 fpLookupId(fpc, dirname, basename, &fp);
891 if (!fpLookupEqualsId(fpc, fp, dirnames[i], basename))
892 continue;
893 rstrscat(&fpdep, rpmstrPoolStr(pool, dirnames[i]),
894 rpmstrPoolStr(pool, basename), NULL);
895 dep = fpdep;
896 }
897 checkInstDeps(ts, dcache, te, depTag, dep, NULL, is_not);
898 _free(fpdep);
899 }
900 _free(fp);
901 }
902
addFileDepToHash(rpmstrPool pool,filedepHash hash,char * key,size_t keylen)903 static void addFileDepToHash(rpmstrPool pool, filedepHash hash, char *key, size_t keylen)
904 {
905 int i;
906 rpmsid basename, dirname;
907 if (!keylen || key[0] != '/')
908 return;
909 for (i = keylen - 1; key[i] != '/'; i--)
910 ;
911 i++; /* include '/' in dirname */
912 dirname = rpmstrPoolIdn(pool, key, i, 1);
913 basename = rpmstrPoolIdn(pool, key + i, keylen - i, 1);
914 filedepHashAddEntry(hash, basename, dirname);
915 }
916
addDepToHash(rpmstrPool pool,depexistsHash hash,char * key,size_t keylen)917 static void addDepToHash(rpmstrPool pool, depexistsHash hash, char *key, size_t keylen)
918 {
919 if (keylen)
920 depexistsHashAddEntry(hash, rpmstrPoolIdn(pool, key, keylen, 1));
921 }
922
addIndexToDepHashes(rpmts ts,rpmDbiTag tag,depexistsHash dephash,filedepHash filehash,depexistsHash depnothash,filedepHash filenothash)923 static void addIndexToDepHashes(rpmts ts, rpmDbiTag tag,
924 depexistsHash dephash, filedepHash filehash,
925 depexistsHash depnothash, filedepHash filenothash)
926 {
927 rpmstrPool pool = rpmtsPool(ts);
928 char *key;
929 size_t keylen;
930 rpmdbIndexIterator ii = rpmdbIndexKeyIteratorInit(rpmtsGetRdb(ts), tag);
931
932 if (!ii)
933 return;
934 while ((rpmdbIndexIteratorNext(ii, (const void**)&key, &keylen)) == 0) {
935 if (!key || !keylen)
936 continue;
937 if (*key == '!' && keylen > 1) {
938 key++;
939 keylen--;
940 if (*key == '/' && filenothash)
941 addFileDepToHash(pool, filenothash, key, keylen);
942 if (depnothash)
943 addDepToHash(pool, depnothash, key, keylen);
944 } else {
945 if (*key == '/' && filehash)
946 addFileDepToHash(pool, filehash, key, keylen);
947 if (dephash)
948 addDepToHash(pool, dephash, key, keylen);
949 }
950 }
951 rpmdbIndexIteratorFree(ii);
952 }
953
sidHash(rpmsid sid)954 static unsigned int sidHash(rpmsid sid)
955 {
956 return sid;
957 }
958
sidCmp(rpmsid a,rpmsid b)959 static int sidCmp(rpmsid a, rpmsid b)
960 {
961 return (a != b);
962 }
963
964
rpmtsCheck(rpmts ts)965 int rpmtsCheck(rpmts ts)
966 {
967 rpm_color_t tscolor = rpmtsColor(ts);
968 rpmtsi pi = NULL; rpmte p;
969 int closeatexit = 0;
970 int rc = 0;
971 depCache dcache = NULL;
972 filedepHash confilehash = NULL; /* file conflicts of installed packages */
973 filedepHash connotfilehash = NULL; /* file conflicts of installed packages */
974 depexistsHash connothash = NULL;
975 filedepHash reqfilehash = NULL; /* file requires of installed packages */
976 filedepHash reqnotfilehash = NULL; /* file requires of installed packages */
977 depexistsHash reqnothash = NULL;
978 fingerPrintCache fpc = NULL;
979 rpmdb rdb = NULL;
980
981 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
982
983 /* Do lazy, readonly, open of rpm database. */
984 rdb = rpmtsGetRdb(ts);
985 if (rdb == NULL && rpmtsGetDBMode(ts) != -1) {
986 if ((rc = rpmtsOpenDB(ts, rpmtsGetDBMode(ts))) != 0)
987 goto exit;
988 rdb = rpmtsGetRdb(ts);
989 closeatexit = 1;
990 }
991
992 if (rdb)
993 rpmdbCtrl(rdb, RPMDB_CTRL_LOCK_RO);
994
995 /* XXX FIXME: figure some kind of heuristic for the cache size */
996 dcache = depCacheCreate(5001, rstrhash, strcmp,
997 (depCacheFreeKey)rfree, NULL);
998
999 /* build hashes of all confilict sdependencies */
1000 confilehash = filedepHashCreate(257, sidHash, sidCmp, NULL, NULL);
1001 connothash = depexistsHashCreate(257, sidHash, sidCmp, NULL);
1002 connotfilehash = filedepHashCreate(257, sidHash, sidCmp, NULL, NULL);
1003 addIndexToDepHashes(ts, RPMTAG_CONFLICTNAME, NULL, confilehash, connothash, connotfilehash);
1004 if (!filedepHashNumKeys(confilehash))
1005 confilehash = filedepHashFree(confilehash);
1006 if (!depexistsHashNumKeys(connothash))
1007 connothash= depexistsHashFree(connothash);
1008 if (!filedepHashNumKeys(connotfilehash))
1009 connotfilehash = filedepHashFree(connotfilehash);
1010
1011 /* build hashes of all requires dependencies */
1012 reqfilehash = filedepHashCreate(8191, sidHash, sidCmp, NULL, NULL);
1013 reqnothash = depexistsHashCreate(257, sidHash, sidCmp, NULL);
1014 reqnotfilehash = filedepHashCreate(257, sidHash, sidCmp, NULL, NULL);
1015 addIndexToDepHashes(ts, RPMTAG_REQUIRENAME, NULL, reqfilehash, reqnothash, reqnotfilehash);
1016 if (!filedepHashNumKeys(reqfilehash))
1017 reqfilehash = filedepHashFree(reqfilehash);
1018 if (!depexistsHashNumKeys(reqnothash))
1019 reqnothash= depexistsHashFree(reqnothash);
1020 if (!filedepHashNumKeys(reqnotfilehash))
1021 reqnotfilehash = filedepHashFree(reqnotfilehash);
1022
1023 /*
1024 * Look at all of the added packages and make sure their dependencies
1025 * are satisfied.
1026 */
1027 pi = rpmtsiInit(ts);
1028 while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
1029 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
1030
1031 rpmlog(RPMLOG_DEBUG, "========== +++ %s %s/%s 0x%x\n",
1032 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
1033
1034 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_REQUIRENAME),
1035 tscolor);
1036 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_CONFLICTNAME),
1037 tscolor);
1038 checkDS(ts, dcache, p, rpmteNEVRA(p), rpmteDS(p, RPMTAG_OBSOLETENAME),
1039 tscolor);
1040
1041 /* Skip obsoletion and provides checks for source packages (ie build) */
1042 if (rpmteIsSource(p))
1043 continue;
1044
1045 /* Check provides against conflicts in installed packages. */
1046 while (rpmdsNext(provides) >= 0) {
1047 checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, NULL, provides, 0);
1048 if (reqnothash && depexistsHashHasEntry(reqnothash, rpmdsNId(provides)))
1049 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, NULL, provides, 1);
1050 }
1051
1052 /* Check package name (not provides!) against installed obsoletes */
1053 checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, NULL, rpmteDS(p, RPMTAG_NAME), 0);
1054
1055 /* Check filenames against installed conflicts */
1056 if (confilehash || reqnotfilehash) {
1057 rpmfiles files = rpmteFiles(p);
1058 rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD);
1059 while (rpmfiNext(fi) >= 0) {
1060 if (confilehash)
1061 checkInstFileDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, fi, 0, confilehash, &fpc);
1062 if (reqnotfilehash)
1063 checkInstFileDeps(ts, dcache, p, RPMTAG_REQUIRENAME, fi, 1, reqnotfilehash, &fpc);
1064 }
1065 rpmfiFree(fi);
1066 rpmfilesFree(files);
1067 }
1068 }
1069 rpmtsiFree(pi);
1070
1071 /*
1072 * Look at the removed packages and make sure they aren't critical.
1073 */
1074 pi = rpmtsiInit(ts);
1075 while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
1076 rpmds provides = rpmdsInit(rpmteDS(p, RPMTAG_PROVIDENAME));
1077
1078 rpmlog(RPMLOG_DEBUG, "========== --- %s %s/%s 0x%x\n",
1079 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
1080
1081 /* Check provides and filenames against installed dependencies. */
1082 while (rpmdsNext(provides) >= 0) {
1083 checkInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, NULL, provides, 0);
1084 if (connothash && depexistsHashHasEntry(connothash, rpmdsNId(provides)))
1085 checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, NULL, provides, 1);
1086 }
1087
1088 if (reqfilehash || connotfilehash) {
1089 rpmfiles files = rpmteFiles(p);
1090 rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD);
1091 while (rpmfiNext(fi) >= 0) {
1092 if (RPMFILE_IS_INSTALLED(rpmfiFState(fi))) {
1093 if (reqfilehash)
1094 checkInstFileDeps(ts, dcache, p, RPMTAG_REQUIRENAME, fi, 0, reqfilehash, &fpc);
1095 if (connotfilehash)
1096 checkInstFileDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, fi, 1, connotfilehash, &fpc);
1097 }
1098 }
1099 rpmfiFree(fi);
1100 rpmfilesFree(files);
1101 }
1102 }
1103 rpmtsiFree(pi);
1104
1105 if (rdb)
1106 rpmdbCtrl(rdb, RPMDB_CTRL_UNLOCK_RO);
1107
1108 exit:
1109 depCacheFree(dcache);
1110 filedepHashFree(confilehash);
1111 filedepHashFree(connotfilehash);
1112 depexistsHashFree(connothash);
1113 filedepHashFree(reqfilehash);
1114 filedepHashFree(reqnotfilehash);
1115 depexistsHashFree(reqnothash);
1116 fpCacheFree(fpc);
1117
1118 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
1119
1120 if (closeatexit)
1121 (void) rpmtsCloseDB(ts);
1122 return rc;
1123 }
1124