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