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