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