Sanitize python object -> tag number exception handling
[platform/upstream/rpm.git] / lib / transaction.c
1 /** \ingroup rpmts
2  * \file lib/transaction.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/rpmlib.h>         /* rpmMachineScore, rpmReadPackageFile */
8 #include <rpm/rpmmacro.h>       /* XXX for rpmExpand */
9 #include <rpm/rpmlog.h>
10 #include <rpm/rpmdb.h>
11 #include <rpm/rpmds.h>
12 #include <rpm/rpmfileutil.h>
13 #include <rpm/rpmstring.h>
14 #include <rpm/argv.h>
15
16 #include "lib/rpmdb_internal.h" /* XXX for dbiIndexSetCount */
17 #include "lib/fprint.h"
18 #include "lib/psm.h"
19 #include "lib/rpmlock.h"
20 #include "lib/rpmfi_internal.h" /* only internal apis */
21 #include "lib/rpmte_internal.h" /* only internal apis */
22 #include "lib/rpmts_internal.h"
23 #include "lib/cpio.h"
24 #include "rpmio/rpmhook.h"
25
26 #include "debug.h"
27
28
29 /**
30  */
31 static int archOkay(const char * pkgArch)
32 {
33     if (pkgArch == NULL) return 0;
34     return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
35 }
36
37 /**
38  */
39 static int osOkay(const char * pkgOs)
40 {
41     if (pkgOs == NULL) return 0;
42     return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
43 }
44
45 /* Calculate total number of files involved in transaction */
46 static uint64_t countFiles(rpmts ts)
47 {
48     uint64_t fc = 0;
49     rpmtsi pi = rpmtsiInit(ts);
50     rpmte p;
51     rpmfi fi;
52     while ((p = rpmtsiNext(pi, 0)) != NULL) {
53         if ((fi = rpmteFI(p)) == NULL)
54             continue;   /* XXX can't happen */
55         fc += rpmfiFC(fi);
56     }
57     pi = rpmtsiFree(pi);
58     return fc;
59 }
60
61 /**
62  * handleInstInstalledFiles.
63  * @param ts            transaction set
64  * @param p             current transaction element
65  * @param fi            file info set
66  * @param shared        shared file info
67  * @param sharedCount   no. of shared elements
68  * @param reportConflicts
69  */
70 /* XXX only ts->{probs,rpmdb} modified */
71 static int handleInstInstalledFile(const rpmts ts, rpmte p, rpmfi fi,
72                                    Header otherHeader, rpmfi otherFi,
73                                    int beingRemoved)
74 {
75     unsigned int fx = rpmfiFX(fi);
76     rpmfs fs = rpmteGetFileStates(p);
77     int isCfgFile = ((rpmfiFFlags(otherFi) | rpmfiFFlags(fi)) & RPMFILE_CONFIG);
78
79     if (XFA_SKIPPING(rpmfsGetAction(fs, fx)))
80         return 0;
81
82     if (rpmfiCompare(otherFi, fi)) {
83         rpm_color_t tscolor = rpmtsColor(ts);
84         rpm_color_t prefcolor = rpmtsPrefColor(ts);
85         rpm_color_t FColor = rpmfiFColor(fi) & tscolor;
86         rpm_color_t oFColor = rpmfiFColor(otherFi) & tscolor;
87         int rConflicts;
88
89         rConflicts = !(beingRemoved || (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES));
90         /* Resolve file conflicts to prefer Elf64 (if not forced). */
91         if (tscolor != 0 && FColor != 0 && FColor != oFColor) {
92             if (oFColor & prefcolor) {
93                 rpmfsSetAction(fs, fx, FA_SKIPCOLOR);
94                 rConflicts = 0;
95             } else if (FColor & prefcolor) {
96                 rpmfsSetAction(fs, fx, FA_CREATE);
97                 rConflicts = 0;
98             }
99         }
100
101         if (rConflicts) {
102             char *altNEVR = headerGetAsString(otherHeader, RPMTAG_NEVRA);
103             rpmps ps = rpmtsProblems(ts);
104             rpmpsAppend(ps, RPMPROB_FILE_CONFLICT,
105                         rpmteNEVRA(p), rpmteKey(p),
106                         rpmfiDN(fi), rpmfiBN(fi),
107                         altNEVR,
108                         0);
109             free(altNEVR);
110             rpmpsFree(ps);
111         }
112
113         /* Save file identifier to mark as state REPLACED. */
114         if ( !(isCfgFile || XFA_SKIPPING(rpmfsGetAction(fs, fx))) ) {
115             if (!beingRemoved)
116                 rpmfsAddReplaced(rpmteGetFileStates(p), rpmfiFX(fi),
117                                  headerGetInstance(otherHeader),
118                                  rpmfiFX(otherFi));
119         }
120     }
121
122     /* Determine config file dispostion, skipping missing files (if any). */
123     if (isCfgFile) {
124         int skipMissing = ((rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES) ? 0 : 1);
125         rpmFileAction action = rpmfiDecideFate(otherFi, fi, skipMissing);
126         rpmfsSetAction(fs, fx, action);
127     }
128     rpmfiSetFReplacedSize(fi, rpmfiFSize(otherFi));
129
130     return 0;
131 }
132
133 /**
134  * Update disk space needs on each partition for this package's files.
135  */
136 /* XXX only ts->{probs,di} modified */
137 static void handleOverlappedFiles(rpmts ts, rpmFpHash ht, rpmte p, rpmfi fi)
138 {
139     rpm_loff_t fixupSize = 0;
140     rpmps ps;
141     const char * fn;
142     int i, j;
143     rpm_color_t tscolor = rpmtsColor(ts);
144     rpm_color_t prefcolor = rpmtsPrefColor(ts);
145     rpmfs fs = rpmteGetFileStates(p);
146     rpmfs otherFs;
147
148     ps = rpmtsProblems(ts);
149     fi = rpmfiInit(fi, 0);
150     if (fi != NULL)
151     while ((i = rpmfiNext(fi)) >= 0) {
152         rpm_color_t oFColor, FColor;
153         struct fingerPrint_s * fiFps;
154         int otherPkgNum, otherFileNum;
155         rpmfi otherFi;
156         rpmte otherTe;
157         rpmfileAttrs FFlags;
158         rpm_mode_t FMode;
159         struct rpmffi_s * recs;
160         int numRecs;
161
162         if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
163             continue;
164
165         fn = rpmfiFN(fi);
166         fiFps = rpmfiFpsIndex(fi, i);
167         FFlags = rpmfiFFlags(fi);
168         FMode = rpmfiFMode(fi);
169         FColor = rpmfiFColor(fi);
170         FColor &= tscolor;
171
172         fixupSize = 0;
173
174         /*
175          * Retrieve all records that apply to this file. Note that the
176          * file info records were built in the same order as the packages
177          * will be installed and removed so the records for an overlapped
178          * files will be sorted in exactly the same order.
179          */
180         (void) rpmFpHashGetEntry(ht, fiFps, &recs, &numRecs, NULL);
181
182         /*
183          * If this package is being added, look only at other packages
184          * being added -- removed packages dance to a different tune.
185          *
186          * If both this and the other package are being added, overlapped
187          * files must be identical (or marked as a conflict). The
188          * disposition of already installed config files leads to
189          * a small amount of extra complexity.
190          *
191          * If this package is being removed, then there are two cases that
192          * need to be worried about:
193          * If the other package is being added, then skip any overlapped files
194          * so that this package removal doesn't nuke the overlapped files
195          * that were just installed.
196          * If both this and the other package are being removed, then each
197          * file removal from preceding packages needs to be skipped so that
198          * the file removal occurs only on the last occurence of an overlapped
199          * file in the transaction set.
200          *
201          */
202
203         /* Locate this overlapped file in the set of added/removed packages. */
204         for (j = 0; j < numRecs && recs[j].p != p; j++)
205             {};
206
207         /* Find what the previous disposition of this file was. */
208         otherFileNum = -1;                      /* keep gcc quiet */
209         otherFi = NULL;
210         otherTe = NULL;
211         otherFs = NULL;
212
213         for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
214             otherTe = recs[otherPkgNum].p;
215             otherFi = rpmteFI(otherTe);
216             otherFileNum = recs[otherPkgNum].fileno;
217             otherFs = rpmteGetFileStates(otherTe);
218
219             /* Added packages need only look at other added packages. */
220             if (rpmteType(p) == TR_ADDED && rpmteType(otherTe) != TR_ADDED)
221                 continue;
222
223             (void) rpmfiSetFX(otherFi, otherFileNum);
224
225             /* XXX Happens iff fingerprint for incomplete package install. */
226             if (rpmfsGetAction(otherFs, otherFileNum) != FA_UNKNOWN)
227                 break;
228         }
229
230         oFColor = rpmfiFColor(otherFi);
231         oFColor &= tscolor;
232
233         switch (rpmteType(p)) {
234         case TR_ADDED:
235           {
236             int reportConflicts =
237                 !(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES);
238             int done = 0;
239
240             if (otherPkgNum < 0) {
241                 /* XXX is this test still necessary? */
242                 rpmFileAction action;
243                 if (rpmfsGetAction(fs, i) != FA_UNKNOWN)
244                     break;
245                 if (rpmfiConfigConflict(fi)) {
246                     /* Here is a non-overlapped pre-existing config file. */
247                     action = (FFlags & RPMFILE_NOREPLACE) ?
248                               FA_ALTNAME : FA_BACKUP;
249                 } else {
250                     action = FA_CREATE;
251                 }
252                 rpmfsSetAction(fs, i, action);
253                 break;
254             }
255
256 assert(otherFi != NULL);
257             /* Mark added overlapped non-identical files as a conflict. */
258             if (rpmfiCompare(otherFi, fi)) {
259                 int rConflicts;
260
261                 rConflicts = reportConflicts;
262                 /* Resolve file conflicts to prefer Elf64 (if not forced) ... */
263                 if (tscolor != 0) {
264                     if (FColor & prefcolor) {
265                         /* ... last file of preferred colour is installed ... */
266                         if (!XFA_SKIPPING(rpmfsGetAction(fs, i)))
267                             rpmfsSetAction(otherFs, otherFileNum, FA_SKIPCOLOR);
268                         rpmfsSetAction(fs, i, FA_CREATE);
269                         rConflicts = 0;
270                     } else
271                     if (oFColor & prefcolor) {
272                         /* ... first file of preferred colour is installed ... */
273                         if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
274                             rpmfsSetAction(otherFs, otherFileNum, FA_CREATE);
275                         rpmfsSetAction(fs, i, FA_SKIPCOLOR);
276                         rConflicts = 0;
277                     }
278                     done = 1;
279                 }
280                 if (rConflicts) {
281                     rpmpsAppend(ps, RPMPROB_NEW_FILE_CONFLICT,
282                         rpmteNEVRA(p), rpmteKey(p),
283                         fn, NULL,
284                         rpmteNEVRA(otherTe),
285                         0);
286                 }
287             }
288
289             /* Try to get the disk accounting correct even if a conflict. */
290             fixupSize = rpmfiFSize(otherFi);
291
292             if (rpmfiConfigConflict(fi)) {
293                 /* Here is an overlapped  pre-existing config file. */
294                 rpmFileAction action;
295                 action = (FFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SKIP;
296                 rpmfsSetAction(fs, i, action);
297             } else {
298                 if (!done)
299                     rpmfsSetAction(fs, i, FA_CREATE);
300             }
301           } break;
302
303         case TR_REMOVED:
304             if (otherPkgNum >= 0) {
305                 assert(otherFi != NULL);
306                 /* Here is an overlapped added file we don't want to nuke. */
307                 if (rpmfsGetAction(otherFs, otherFileNum) != FA_ERASE) {
308                     /* On updates, don't remove files. */
309                     rpmfsSetAction(fs, i, FA_SKIP);
310                     break;
311                 }
312                 /* Here is an overlapped removed file: skip in previous. */
313                 rpmfsSetAction(otherFs, otherFileNum, FA_SKIP);
314             }
315             if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
316                 break;
317             if (rpmfiFState(fi) != RPMFILE_STATE_NORMAL)
318                 break;
319             if (!(S_ISREG(FMode) && (FFlags & RPMFILE_CONFIG))) {
320                 rpmfsSetAction(fs, i, FA_ERASE);
321                 break;
322             }
323                 
324             /* Here is a pre-existing modified config file that needs saving. */
325             {   pgpHashAlgo algo = 0;
326                 size_t diglen = 0;
327                 const unsigned char *digest;
328                 if ((digest = rpmfiFDigest(fi, &algo, &diglen))) {
329                     unsigned char fdigest[diglen];
330                     if (!rpmDoDigest(algo, fn, 0, fdigest, NULL) &&
331                         memcmp(digest, fdigest, diglen)) {
332                         rpmfsSetAction(fs, i, FA_BACKUP);
333                         break;
334                     }
335                 }
336             }
337             rpmfsSetAction(fs, i, FA_ERASE);
338             break;
339         }
340
341         /* Update disk space info for a file. */
342         rpmtsUpdateDSI(ts, fiFps->entry->dev, rpmfiFSize(fi),
343                        rpmfiFReplacedSize(fi), fixupSize, rpmfsGetAction(fs, i));
344
345     }
346     ps = rpmpsFree(ps);
347 }
348
349 /**
350  * Ensure that current package is newer than installed package.
351  * @param p             current transaction element
352  * @param h             installed header
353  * @param ps            problem set
354  * @return              0 if not newer, 1 if okay
355  */
356 static int ensureOlder(const rpmte p, const Header h, rpmps ps)
357 {
358     rpmsenseFlags reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
359     rpmds req;
360     int rc;
361
362     if (p == NULL || h == NULL)
363         return 1;
364
365     req = rpmdsSingle(RPMTAG_REQUIRENAME, rpmteN(p), rpmteEVR(p), reqFlags);
366     rc = rpmdsNVRMatchesDep(h, req, _rpmds_nopromote);
367     req = rpmdsFree(req);
368
369     if (rc == 0) {
370         char * altNEVR = headerGetAsString(h, RPMTAG_NEVRA);
371         rpmpsAppend(ps, RPMPROB_OLDPACKAGE,
372                 rpmteNEVRA(p), rpmteKey(p),
373                 NULL, NULL,
374                 altNEVR,
375                 0);
376         altNEVR = _free(altNEVR);
377         rc = 1;
378     } else
379         rc = 0;
380
381     return rc;
382 }
383
384 /**
385  * Check if the curent file in the file iterator is in the
386  * netshardpath and though should be excluded.
387  * @param ts            transaction set
388  * @param fi            file info set
389  * @returns pointer to matching path or NULL
390  */
391 static char ** matchNetsharedpath(const rpmts ts, rpmfi fi)
392 {
393     char ** nsp;
394     const char * dn, * bn;
395     size_t dnlen, bnlen;
396     char * s;
397     bn = rpmfiBN(fi);
398     bnlen = strlen(bn);
399     dn = rpmfiDN(fi);
400     dnlen = strlen(dn);
401     for (nsp = ts->netsharedPaths; nsp && *nsp; nsp++) {
402         size_t len;
403
404         len = strlen(*nsp);
405         if (dnlen >= len) {
406             if (!rstreqn(dn, *nsp, len))
407                 continue;
408             /* Only directories or complete file paths can be net shared */
409             if (!(dn[len] == '/' || dn[len] == '\0'))
410                 continue;
411         } else {
412             if (len < (dnlen + bnlen))
413                 continue;
414             if (!rstreqn(dn, *nsp, dnlen))
415                 continue;
416             /* Insure that only the netsharedpath basename is compared. */
417             if ((s = strchr((*nsp) + dnlen, '/')) != NULL && s[1] != '\0')
418                 continue;
419             if (!rstreqn(bn, (*nsp) + dnlen, bnlen))
420                 continue;
421             len = dnlen + bnlen;
422             /* Only directories or complete file paths can be net shared */
423             if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
424                 continue;
425         }
426
427         break;
428     }
429     return nsp;
430 }
431
432 static void skipEraseFiles(const rpmts ts, rpmte p)
433 {
434     rpmfi fi = rpmteFI(p);
435     rpmfs fs = rpmteGetFileStates(p);
436     int i;
437     char ** nsp;
438     /*
439      * Skip net shared paths.
440      * Net shared paths are not relative to the current root (though
441      * they do need to take package relocations into account).
442      */
443     fi = rpmfiInit(fi, 0);
444     while ((i = rpmfiNext(fi)) >= 0)
445     {
446         nsp = matchNetsharedpath(ts, fi);
447         if (nsp && *nsp) {
448             rpmfsSetAction(fs, i, FA_SKIPNETSHARED);
449         }
450     }
451 }
452
453
454 /**
455  * Skip any files that do not match install policies.
456  * @param ts            transaction set
457  * @param fi            file info set
458  */
459 static void skipInstallFiles(const rpmts ts, rpmte p)
460 {
461     rpm_color_t tscolor = rpmtsColor(ts);
462     rpm_color_t FColor;
463     int noConfigs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONFIGS);
464     int noDocs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NODOCS);
465     const char * dn, * bn;
466     size_t dnlen, bnlen;
467     int * drc;
468     char * dff;
469     int dc;
470     int i, j, ix;
471     rpmfi fi = rpmteFI(p);
472     rpmfs fs = rpmteGetFileStates(p);
473
474     if (!noDocs)
475         noDocs = rpmExpandNumeric("%{_excludedocs}");
476
477     /* Compute directory refcount, skip directory if now empty. */
478     dc = rpmfiDC(fi);
479     drc = xcalloc(dc, sizeof(*drc));
480     dff = xcalloc(dc, sizeof(*dff));
481
482     fi = rpmfiInit(fi, 0);
483     while ((i = rpmfiNext(fi)) >= 0)
484     {
485         char ** nsp;
486         const char *flangs;
487
488         bn = rpmfiBN(fi);
489         bnlen = strlen(bn);
490         ix = rpmfiDX(fi);
491         dn = rpmfiDN(fi);
492         dnlen = strlen(dn);
493         if (dn == NULL)
494             continue;   /* XXX can't happen */
495
496         drc[ix]++;
497
498         /* Don't bother with skipped files */
499         if (XFA_SKIPPING(rpmfsGetAction(fs, i))) {
500             drc[ix]--; dff[ix] = 1;
501             continue;
502         }
503
504         /* Ignore colored files not in our rainbow. */
505         FColor = rpmfiFColor(fi);
506         if (tscolor && FColor && !(tscolor & FColor)) {
507             drc[ix]--;  dff[ix] = 1;
508             rpmfsSetAction(fs, i, FA_SKIPCOLOR);
509             continue;
510         }
511
512         /*
513          * Skip net shared paths.
514          * Net shared paths are not relative to the current root (though
515          * they do need to take package relocations into account).
516          */
517         nsp = matchNetsharedpath(ts, fi);
518         if (nsp && *nsp) {
519             drc[ix]--;  dff[ix] = 1;
520             rpmfsSetAction(fs, i, FA_SKIPNETSHARED);
521             continue;
522         }
523
524         /*
525          * Skip i18n language specific files.
526          */
527         flangs = (ts->installLangs != NULL) ? rpmfiFLangs(fi) : NULL;
528         if (flangs != NULL && *flangs != '\0') {
529             const char *l, *le;
530             char **lang;
531             for (lang = ts->installLangs; *lang != NULL; lang++) {
532                 for (l = flangs; *l != '\0'; l = le) {
533                     for (le = l; *le != '\0' && *le != '|'; le++)
534                         {};
535                     if ((le-l) > 0 && rstreqn(*lang, l, (le-l)))
536                         break;
537                     if (*le == '|') le++;       /* skip over | */
538                 }
539                 if (*l != '\0')
540                     break;
541             }
542             if (*lang == NULL) {
543                 drc[ix]--;      dff[ix] = 1;
544                 rpmfsSetAction(fs, i, FA_SKIPNSTATE);
545                 continue;
546             }
547         }
548
549         /*
550          * Skip config files if requested.
551          */
552         if (noConfigs && (rpmfiFFlags(fi) & RPMFILE_CONFIG)) {
553             drc[ix]--;  dff[ix] = 1;
554             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
555             continue;
556         }
557
558         /*
559          * Skip documentation if requested.
560          */
561         if (noDocs && (rpmfiFFlags(fi) & RPMFILE_DOC)) {
562             drc[ix]--;  dff[ix] = 1;
563             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
564             continue;
565         }
566     }
567
568     /* Skip (now empty) directories that had skipped files. */
569 #ifndef NOTYET
570     if (fi != NULL)     /* XXX can't happen */
571     for (j = 0; j < dc; j++)
572 #else
573     if ((fi = rpmfiInitD(fi)) != NULL)
574     while (j = rpmfiNextD(fi) >= 0)
575 #endif
576     {
577
578         if (drc[j]) continue;   /* dir still has files. */
579         if (!dff[j]) continue;  /* dir was not emptied here. */
580         
581         /* Find parent directory and basename. */
582         dn = rpmfiDNIndex(fi, j); dnlen = strlen(dn) - 1;
583         bn = dn + dnlen;        bnlen = 0;
584         while (bn > dn && bn[-1] != '/') {
585                 bnlen++;
586                 dnlen--;
587                 bn--;
588         }
589
590         /* If explicitly included in the package, skip the directory. */
591         fi = rpmfiInit(fi, 0);
592         while ((i = rpmfiNext(fi)) >= 0) {
593             const char * fdn, * fbn;
594             rpm_mode_t fFMode;
595
596             if (XFA_SKIPPING(rpmfsGetAction(fs, i)))
597                 continue;
598
599             fFMode = rpmfiFMode(fi);
600
601             if (rpmfiWhatis(fFMode) != XDIR)
602                 continue;
603             fdn = rpmfiDN(fi);
604             if (strlen(fdn) != dnlen)
605                 continue;
606             if (!rstreqn(fdn, dn, dnlen))
607                 continue;
608             fbn = rpmfiBN(fi);
609             if (strlen(fbn) != bnlen)
610                 continue;
611             if (!rstreqn(fbn, bn, bnlen))
612                 continue;
613             rpmlog(RPMLOG_DEBUG, "excluding directory %s\n", dn);
614             rpmfsSetAction(fs, i, FA_SKIPNSTATE);
615             break;
616         }
617     }
618
619     free(drc);
620     free(dff);
621 }
622
623 #undef HASHTYPE
624 #undef HTKEYTYPE
625 #undef HTDATATYPE
626
627 #define HASHTYPE rpmStringSet
628 #define HTKEYTYPE const char *
629 #include "lib/rpmhash.H"
630 #include "lib/rpmhash.C"
631
632 /* Get a rpmdbMatchIterator containing all files in
633  * the rpmdb that share the basename with one from
634  * the transaction.
635  * @param ts            transaction set
636  * @return              rpmdbMatchIterator sorted
637                         by (package, fileNum)
638  */
639 static
640 rpmdbMatchIterator rpmFindBaseNamesInDB(rpmts ts, uint64_t fileCount)
641 {
642     rpmtsi pi;  rpmte p;
643     rpmfi fi;
644     rpmdbMatchIterator mi;
645     int xx;
646     const char * baseName;
647
648     rpmStringSet baseNames = rpmStringSetCreate(fileCount, 
649                                         hashFunctionString, strcmp, NULL);
650
651     mi = rpmdbInitIterator(rpmtsGetRdb(ts), RPMTAG_BASENAMES, NULL, 0);
652
653     pi = rpmtsiInit(ts);
654     while ((p = rpmtsiNext(pi, 0)) != NULL) {
655         (void) rpmdbCheckSignals();
656
657         if ((fi = rpmteFI(p)) == NULL)
658             continue;   /* XXX can't happen */
659         rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_PROGRESS, rpmtsiOc(pi),
660                     ts->orderCount);
661
662         /* Gather all installed headers with matching basename's. */
663         fi = rpmfiInit(fi, 0);
664         while (rpmfiNext(fi) >= 0) {
665             size_t keylen;
666             baseName = rpmfiBN(fi);
667             if (rpmStringSetHasEntry(baseNames, baseName))
668                 continue;
669
670             keylen = strlen(baseName);
671             if (keylen == 0)
672                 keylen++;       /* XXX "/" fixup. */
673             xx = rpmdbExtendIterator(mi, baseName, keylen);
674             rpmStringSetAddEntry(baseNames, baseName);
675          }
676     }
677     pi = rpmtsiFree(pi);
678     rpmStringSetFree(baseNames);
679
680     rpmdbSortIterator(mi);
681     /* iterator is now sorted by (recnum, filenum) */
682     return mi;
683 }
684
685 /* Check files in the transactions against the rpmdb
686  * Lookup all files with the same basename in the rpmdb
687  * and then check for matching finger prints
688  * @param ts            transaction set
689  * @param fpc           global finger print cache
690  */
691 static
692 void checkInstalledFiles(rpmts ts, uint64_t fileCount, rpmFpHash ht, fingerPrintCache fpc)
693 {
694     rpmte p;
695     rpmfi fi;
696     rpmfs fs;
697     rpmfi otherFi=NULL;
698     int j;
699     int xx;
700     unsigned int fileNum;
701     const char * oldDir;
702
703     rpmdbMatchIterator mi;
704     Header h, newheader;
705
706     int beingRemoved;
707
708     rpmlog(RPMLOG_DEBUG, "computing file dispositions\n");
709
710     mi = rpmFindBaseNamesInDB(ts, fileCount);
711
712     /* For all installed headers with matching basename's ... */
713     if (mi == NULL)
714          return;
715
716     if (rpmdbGetIteratorCount(mi) == 0) {
717         mi = rpmdbFreeIterator(mi);
718         return;
719     }
720
721     /* Loop over all packages from the rpmdb */
722     h = newheader = rpmdbNextIterator(mi);
723     while (h != NULL) {
724         headerGetFlags hgflags = HEADERGET_MINMEM;
725         struct rpmtd_s bnames, dnames, dindexes, ostates;
726         fingerPrint fp;
727         unsigned int installedPkg;
728
729         /* Is this package being removed? */
730         installedPkg = rpmdbGetIteratorOffset(mi);
731         beingRemoved = 0;
732         if (ts->removedPackages != NULL)
733         for (j = 0; j < ts->numRemovedPackages; j++) {
734             if (ts->removedPackages[j] != installedPkg)
735                 continue;
736             beingRemoved = 1;
737             break;
738         }
739
740         h = headerLink(h);
741         headerGet(h, RPMTAG_BASENAMES, &bnames, hgflags);
742         headerGet(h, RPMTAG_DIRNAMES, &dnames, hgflags);
743         headerGet(h, RPMTAG_DIRINDEXES, &dindexes, hgflags);
744         headerGet(h, RPMTAG_FILESTATES, &ostates, hgflags);
745
746         oldDir = NULL;
747         /* loop over all interesting files in that package */
748         do {
749             int gotRecs;
750             struct rpmffi_s * recs;
751             int numRecs;
752             const char * dirName;
753             const char * baseName;
754
755             fileNum = rpmdbGetIteratorFileNum(mi);
756             rpmtdSetIndex(&bnames, fileNum);
757             rpmtdSetIndex(&dindexes, fileNum);
758             rpmtdSetIndex(&dnames, *rpmtdGetUint32(&dindexes));
759             rpmtdSetIndex(&ostates, fileNum);
760
761             dirName = rpmtdGetString(&dnames);
762             baseName = rpmtdGetString(&bnames);
763
764             /* lookup finger print for this file */
765             if ( dirName == oldDir) {
766                 /* directory is the same as last round */
767                 fp.baseName = baseName;
768             } else {
769                 fp = fpLookup(fpc, dirName, baseName, 1);
770                 oldDir = dirName;
771             }
772             /* search for files in the transaction with same finger print */
773             gotRecs = rpmFpHashGetEntry(ht, &fp, &recs, &numRecs, NULL);
774
775             for (j=0; (j<numRecs)&&gotRecs; j++) {
776                 p = recs[j].p;
777                 fi = rpmteFI(p);
778                 fs = rpmteGetFileStates(p);
779
780                 /* Determine the fate of each file. */
781                 switch (rpmteType(p)) {
782                 case TR_ADDED:
783                     if (!otherFi) {
784                         otherFi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_KEEPHEADER);
785                     }
786                     rpmfiSetFX(fi, recs[j].fileno);
787                     rpmfiSetFX(otherFi, fileNum);
788                     xx = handleInstInstalledFile(ts, p, fi, h, otherFi, beingRemoved);
789                     break;
790                 case TR_REMOVED:
791                     if (!beingRemoved) {
792                         rpmfiSetFX(fi, recs[j].fileno);
793                         if (*rpmtdGetChar(&ostates) == RPMFILE_STATE_NORMAL)
794                             rpmfsSetAction(fs, recs[j].fileno, FA_SKIP);
795                     }
796                     break;
797                 }
798             }
799
800             newheader = rpmdbNextIterator(mi);
801
802         } while (newheader==h);
803
804         otherFi = rpmfiFree(otherFi);
805         rpmtdFreeData(&ostates);
806         rpmtdFreeData(&bnames);
807         rpmtdFreeData(&dnames);
808         rpmtdFreeData(&dindexes);
809         headerFree(h);
810         h = newheader;
811     }
812
813     mi = rpmdbFreeIterator(mi);
814 }
815
816 /*
817  * For packages being installed:
818  * - verify package arch/os.
819  * - verify package epoch:version-release is newer.
820  */
821 static rpmps checkProblems(rpmts ts)
822 {
823     rpm_color_t tscolor = rpmtsColor(ts);
824     rpmps ps = rpmpsCreate();
825     rpmtsi pi = rpmtsiInit(ts);
826     rpmte p;
827
828     /* The ordering doesn't matter here */
829     /* XXX Only added packages need be checked. */
830     rpmlog(RPMLOG_DEBUG, "sanity checking %d elements\n", rpmtsNElements(ts));
831     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
832         rpmdbMatchIterator mi;
833         rpmpsi psi;
834
835         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_IGNOREARCH))
836             if (!archOkay(rpmteA(p)))
837                 rpmpsAppend(ps, RPMPROB_BADARCH,
838                         rpmteNEVRA(p), rpmteKey(p),
839                         rpmteA(p), NULL,
840                         NULL, 0);
841
842         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_IGNOREOS))
843             if (!osOkay(rpmteO(p)))
844                 rpmpsAppend(ps, RPMPROB_BADOS,
845                         rpmteNEVRA(p), rpmteKey(p),
846                         rpmteO(p), NULL,
847                         NULL, 0);
848
849         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_OLDPACKAGE)) {
850             Header h;
851             mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
852             while ((h = rpmdbNextIterator(mi)) != NULL)
853                 ensureOlder(p, h, ps);
854             mi = rpmdbFreeIterator(mi);
855         }
856
857         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEPKG)) {
858             mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
859             rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_STRCMP, rpmteE(p));
860             rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_STRCMP, rpmteV(p));
861             rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_STRCMP, rpmteR(p));
862             if (tscolor) {
863                 rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_STRCMP, rpmteA(p));
864                 rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_STRCMP, rpmteO(p));
865             }
866
867             while (rpmdbNextIterator(mi) != NULL) {
868                 rpmpsAppend(ps, RPMPROB_PKG_INSTALLED,
869                         rpmteNEVRA(p), rpmteKey(p),
870                         NULL, NULL,
871                         NULL, 0);
872                 break;
873             }
874             mi = rpmdbFreeIterator(mi);
875         }
876
877         /* XXX rpmte problems can only be relocation problems atm */
878         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_FORCERELOCATE)) {
879             psi = rpmpsInitIterator(rpmteProblems(p));
880             while (rpmpsNextIterator(psi) >= 0) {
881                 rpmpsAppendProblem(ps, rpmpsGetProblem(psi));
882             }
883             rpmpsFreeIterator(psi);
884         }
885     }
886     pi = rpmtsiFree(pi);
887     return ps;
888 }
889
890 /*
891  * Run pre/post transaction scripts for transaction set
892  * param ts     Transaction set
893  * param stag   RPMTAG_PRETRANS or RPMTAG_POSTTRANS
894  * return       0 on success, -1 on error (invalid script tag)
895  */
896 static int runTransScripts(rpmts ts, rpmTag stag) 
897 {
898     rpmtsi pi; 
899     rpmte p;
900     rpmpsm psm;
901     rpmTag progtag = RPMTAG_NOT_FOUND;
902     int xx;
903
904     if (stag == RPMTAG_PRETRANS) {
905         progtag = RPMTAG_PRETRANSPROG;
906     } else if (stag == RPMTAG_POSTTRANS) {
907         progtag = RPMTAG_POSTTRANSPROG;
908     } else {
909         return -1;
910     }
911
912     pi = rpmtsiInit(ts);
913     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
914         /* If no pre/post-transaction script, then don't bother. */
915         if (!rpmteHaveTransScript(p, stag))
916             continue;
917
918         if (rpmteOpen(p, ts, 0)) {
919             psm = rpmpsmNew(ts, p);
920             xx = rpmpsmScriptStage(psm, stag, progtag);
921             psm = rpmpsmFree(psm);
922             rpmteClose(p, ts, 0);
923         }
924     }
925     pi = rpmtsiFree(pi);
926     return 0;
927 }
928
929 /* Add fingerprint for each file not skipped. */
930 static void addFingerprints(rpmts ts, uint64_t fileCount, rpmFpHash ht, fingerPrintCache fpc)
931 {
932     rpmtsi pi;
933     rpmte p;
934     rpmfi fi;
935     int i;
936
937     rpmFpHash symlinks = rpmFpHashCreate(fileCount/16+16, fpHashFunction, fpEqual, NULL, NULL);
938
939     pi = rpmtsiInit(ts);
940     while ((p = rpmtsiNext(pi, 0)) != NULL) {
941         (void) rpmdbCheckSignals();
942
943         if ((fi = rpmteFI(p)) == NULL)
944             continue;   /* XXX can't happen */
945
946         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
947         rpmfiFpLookup(fi, fpc);
948         /* collect symbolic links */
949         fi = rpmfiInit(fi, 0);
950         while ((i = rpmfiNext(fi)) >= 0) {
951             struct rpmffi_s ffi;
952             char const *linktarget;
953             linktarget = rpmfiFLink(fi);
954             if (!(linktarget && *linktarget != '\0'))
955                 continue;
956             if (XFA_SKIPPING(rpmfsGetAction(rpmteGetFileStates(p), i)))
957                 continue;
958             ffi.p = p;
959             ffi.fileno = i;
960             rpmFpHashAddEntry(symlinks, rpmfiFpsIndex(fi, i), ffi);
961         }
962         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), rpmfiFC(fi));
963
964     }
965     pi = rpmtsiFree(pi);
966
967     /* ===============================================
968      * Check fingerprints if they contain symlinks
969      * and add them to the hash table
970      */
971
972     pi = rpmtsiInit(ts);
973     while ((p = rpmtsiNext(pi, 0)) != NULL) {
974         (void) rpmdbCheckSignals();
975
976         if ((fi = rpmteFI(p)) == NULL)
977             continue;   /* XXX can't happen */
978         fi = rpmfiInit(fi, 0);
979         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
980         while ((i = rpmfiNext(fi)) >= 0) {
981             if (XFA_SKIPPING(rpmfsGetAction(rpmteGetFileStates(p), i)))
982                 continue;
983             fpLookupSubdir(symlinks, ht, fpc, p, i);
984         }
985         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
986     }
987     pi = rpmtsiFree(pi);
988
989     rpmFpHashFree(symlinks);
990 }
991
992 static int rpmtsSetup(rpmts ts, rpmprobFilterFlags ignoreSet)
993 {
994     rpm_tid_t tid = (rpm_tid_t) time(NULL);
995     int dbmode = (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST) ?  O_RDONLY : (O_RDWR|O_CREAT);
996
997     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOSCRIPTS)
998         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
999     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERS)
1000         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransTriggers));
1001
1002     if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)
1003         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
1004
1005     /* if SELinux isn't enabled or init fails, don't bother... */
1006     if (!rpmtsSELinuxEnabled(ts)) {
1007         rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS));
1008     }
1009
1010     if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
1011         char *fn = rpmGetPath("%{?_install_file_context_path}", NULL);
1012         if (matchpathcon_init(fn) == -1) {
1013             rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_NOCONTEXTS));
1014         }
1015         free(fn);
1016     }
1017
1018     /* XXX Make sure the database is open RDWR for package install/erase. */
1019     if (rpmtsOpenDB(ts, dbmode)) {
1020         return -1;      /* XXX W2DO? */
1021     }
1022
1023     ts->ignoreSet = ignoreSet;
1024     ts->probs = rpmpsFree(ts->probs);
1025
1026     {   char * currDir = rpmGetCwd();
1027         rpmtsSetCurrDir(ts, currDir);
1028         currDir = _free(currDir);
1029     }
1030
1031     (void) rpmtsSetChrootDone(ts, 0);
1032     (void) rpmtsSetTid(ts, tid);
1033
1034     /* Get available space on mounted file systems. */
1035     (void) rpmtsInitDSI(ts);
1036
1037     return 0;
1038 }
1039
1040 static int rpmtsFinish(rpmts ts)
1041 {
1042     if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONTEXTS)) {
1043         matchpathcon_fini();
1044     }
1045     return 0;
1046 }
1047
1048 static int rpmtsPrepare(rpmts ts)
1049 {
1050     rpmtsi pi;
1051     rpmte p;
1052     rpmfi fi;
1053     int xx, rc = 0;
1054     uint64_t fileCount = countFiles(ts);
1055     const char * rootDir = rpmtsRootDir(ts);
1056     int dochroot = (rootDir != NULL && !rstreq(rootDir, "/") && *rootDir == '/');
1057
1058     fingerPrintCache fpc = fpCacheCreate(fileCount/2 + 10001);
1059     rpmFpHash ht = rpmFpHashCreate(fileCount/2+1, fpHashFunction, fpEqual,
1060                              NULL, NULL);
1061
1062     rpmlog(RPMLOG_DEBUG, "computing %" PRIu64 " file fingerprints\n", fileCount);
1063
1064     /* Skip netshared paths, not our i18n files, and excluded docs */
1065     pi = rpmtsiInit(ts);
1066     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1067         if ((fi = rpmteFI(p)) == NULL)
1068             continue;   /* XXX can't happen */
1069
1070         if (rpmfiFC(fi) == 0)
1071             continue;
1072         if (rpmteType(p) == TR_ADDED) {
1073             skipInstallFiles(ts, p);
1074         } else {
1075             skipEraseFiles(ts, p);
1076         }
1077     }
1078     pi = rpmtsiFree(pi);
1079
1080     /* Enter chroot for fingerprinting if necessary */
1081     if (!rpmtsChrootDone(ts)) {
1082         xx = chdir("/");
1083         if (dochroot) {
1084             /* opening db before chroot not optimal, see rhbz#103852 c#3 */
1085             xx = rpmdbOpenAll(ts->rdb);
1086             if (chroot(rootDir) == -1) {
1087                 rpmlog(RPMLOG_ERR, _("Unable to change root directory: %m\n"));
1088                 rc = -1;
1089                 goto exit;
1090             }
1091         }
1092         (void) rpmtsSetChrootDone(ts, 1);
1093     }
1094     
1095     rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_START, 6, ts->orderCount);
1096     addFingerprints(ts, fileCount, ht, fpc);
1097     /* check against files in the rpmdb */
1098     checkInstalledFiles(ts, fileCount, ht, fpc);
1099
1100     pi = rpmtsiInit(ts);
1101     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1102         if ((fi = rpmteFI(p)) == NULL)
1103             continue;   /* XXX can't happen */
1104
1105         (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1106         /* check files in ts against each other and update disk space
1107            needs on each partition for this package. */
1108         handleOverlappedFiles(ts, ht, p, fi);
1109
1110         /* Check added package has sufficient space on each partition used. */
1111         if (rpmteType(p) == TR_ADDED) {
1112             rpmtsCheckDSIProblems(ts, p);
1113         }
1114         (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
1115     }
1116     pi = rpmtsiFree(pi);
1117     rpmtsNotify(ts, NULL, RPMCALLBACK_TRANS_STOP, 6, ts->orderCount);
1118
1119     /* return from chroot if done earlier */
1120     if (rpmtsChrootDone(ts)) {
1121         const char * currDir = rpmtsCurrDir(ts);
1122         if (dochroot)
1123             xx = chroot(".");
1124         (void) rpmtsSetChrootDone(ts, 0);
1125         if (currDir != NULL)
1126             xx = chdir(currDir);
1127     }
1128
1129     /* File info sets, fp caches etc not needed beyond here, free 'em up. */
1130     pi = rpmtsiInit(ts);
1131     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1132         rpmteSetFI(p, NULL);
1133     }
1134     pi = rpmtsiFree(pi);
1135
1136 exit:
1137     ht = rpmFpHashFree(ht);
1138     fpc = fpCacheFree(fpc);
1139     return rc;
1140 }
1141
1142 /*
1143  * Transaction main loop: install and remove packages
1144  */
1145 static int rpmtsProcess(rpmts ts)
1146 {
1147     rpmtsi pi;  rpmte p;
1148     int rc = 0;
1149
1150     pi = rpmtsiInit(ts);
1151     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1152         int failed = 1;
1153         rpmElementType tetype = rpmteType(p);
1154         rpmtsOpX op = (tetype == TR_ADDED) ? RPMTS_OP_INSTALL : RPMTS_OP_ERASE;
1155
1156         rpmlog(RPMLOG_DEBUG, "========== +++ %s %s-%s 0x%x\n",
1157                 rpmteNEVR(p), rpmteA(p), rpmteO(p), rpmteColor(p));
1158
1159         if (rpmteFailed(p)) {
1160             /* XXX this should be a warning, need a better message though */
1161             rpmlog(RPMLOG_DEBUG, "element %s marked as failed, skipping\n",
1162                                         rpmteNEVRA(p));
1163             rc++;
1164             continue;
1165         }
1166         
1167         if (rpmteOpen(p, ts, 1)) {
1168             rpmpsm psm = NULL;
1169             pkgStage stage = PSM_UNKNOWN;
1170
1171             switch (tetype) {
1172             case TR_ADDED:
1173                 stage = PSM_PKGINSTALL;
1174                 break;
1175             case TR_REMOVED:
1176                 stage = PSM_PKGERASE;
1177                 break;
1178             }
1179             psm = rpmpsmNew(ts, p);
1180             (void) rpmswEnter(rpmtsOp(ts, op), 0);
1181             failed = rpmpsmStage(psm, stage);
1182             (void) rpmswExit(rpmtsOp(ts, op), 0);
1183             psm = rpmpsmFree(psm);
1184             rpmteClose(p, ts, 1);
1185         }
1186         if (failed) {
1187             rpmteMarkFailed(p, ts);
1188             rc++;
1189         }
1190         (void) rpmdbSync(rpmtsGetRdb(ts));
1191     }
1192     pi = rpmtsiFree(pi);
1193     return rc;
1194 }
1195
1196 int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
1197 {
1198     int rc = -1; /* assume failure */
1199     void * lock = NULL;
1200
1201     /* XXX programmer error segfault avoidance. */
1202     if (rpmtsNElements(ts) <= 0) {
1203         goto exit;
1204     }
1205
1206     /* If we are in test mode, then there's no need for transaction lock. */
1207     if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
1208         if (!(lock = rpmtsAcquireLock(ts))) {
1209             goto exit;
1210         }
1211     }
1212
1213     /* Setup flags and such, open the DB */
1214     if (rpmtsSetup(ts, ignoreSet)) {
1215         goto exit;
1216     }
1217
1218     /* Check package set for problems */
1219     ts->probs = checkProblems(ts);
1220
1221     /* Run pre-transaction scripts, but only if there are no known
1222      * problems up to this point and not disabled otherwise. */
1223     if (!((rpmtsFlags(ts) & (RPMTRANS_FLAG_BUILD_PROBS|RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_NOPRE))
1224           || (rpmpsNumProblems(ts->probs) &&
1225                 (okProbs == NULL || rpmpsTrim(ts->probs, okProbs))))) {
1226         rpmlog(RPMLOG_DEBUG, "running pre-transaction scripts\n");
1227         runTransScripts(ts, RPMTAG_PRETRANS);
1228     }
1229
1230     /* Compute file disposition for each package in transaction set. */
1231     if (rpmtsPrepare(ts)) {
1232         goto exit;
1233     }
1234
1235      /* If unfiltered problems exist, free memory and return. */
1236     if ((rpmtsFlags(ts) & RPMTRANS_FLAG_BUILD_PROBS) ||
1237                 (rpmpsNumProblems(ts->probs) &&
1238                 (okProbs == NULL || rpmpsTrim(ts->probs, okProbs)))) {
1239         rc = ts->orderCount;
1240         goto exit;
1241     }
1242
1243     /* Actually install and remove packages, get final exit code */
1244     rc = rpmtsProcess(ts) ? -1 : 0;
1245
1246     /* Run post-transaction scripts unless disabled */
1247     if (!(rpmtsFlags(ts) & (RPMTRANS_FLAG_TEST|RPMTRANS_FLAG_NOPOST))) {
1248         rpmlog(RPMLOG_DEBUG, "running post-transaction scripts\n");
1249         runTransScripts(ts, RPMTAG_POSTTRANS);
1250     }
1251
1252     /* Finish up... */
1253     (void) rpmtsFinish(ts);
1254
1255 exit:
1256     rpmtsFreeLock(lock);
1257     return rc;
1258 }