Lclint fiddles.
[platform/upstream/rpm.git] / lib / transaction.c
1 /** \ingroup rpmts
2  * \file lib/transaction.c
3  */
4
5 #include "system.h"
6 #include <rpmlib.h>
7
8 #include <rpmmacro.h>   /* XXX for rpmExpand */
9
10 #include "psm.h"
11
12 #include "rpmps.h"
13
14 #include "rpmds.h"
15
16 #define _RPMFI_INTERNAL
17 #include "rpmfi.h"
18
19 #define _RPMTE_INTERNAL
20 #include "rpmte.h"
21
22 #define _RPMTS_INTERNAL
23 #include "rpmts.h"
24
25 #include "rpmdb.h"
26 #include "fprint.h"
27 #include "legacy.h"     /* XXX domd5 */
28 #include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
29
30 #include "debug.h"
31
32 /*@access FD_t @*/              /* XXX compared with NULL */
33 /*@access Header @*/            /* XXX compared with NULL */
34 /*@access rpmps @*/     /* XXX need rpmProblemSetOK() */
35 /*@access dbiIndexSet @*/
36 /*@access rpmdb @*/
37
38 /*@access PSM_t @*/
39
40 /*@access alKey @*/
41 /*@access fnpyKey @*/
42
43 /*@access rpmfi @*/
44
45 /*@access rpmte @*/
46 /*@access rpmtsi @*/
47 /*@access rpmts @*/
48
49 /**
50  */
51 static int archOkay(/*@null@*/ const char * pkgArch)
52         /*@*/
53 {
54     if (pkgArch == NULL) return 0;
55     return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
56 }
57
58 /**
59  */
60 static int osOkay(/*@null@*/ const char * pkgOs)
61         /*@*/
62 {
63     if (pkgOs == NULL) return 0;
64     return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
65 }
66
67 /**
68  */
69 static int sharedCmp(const void * one, const void * two)
70         /*@*/
71 {
72     sharedFileInfo a = (sharedFileInfo) one;
73     sharedFileInfo b = (sharedFileInfo) two;
74
75     if (a->otherPkg < b->otherPkg)
76         return -1;
77     else if (a->otherPkg > b->otherPkg)
78         return 1;
79
80     return 0;
81 }
82
83 /**
84  */
85 static fileAction decideFileFate(const rpmts ts,
86                 const rpmfi ofi, rpmfi nfi)
87         /*@globals fileSystem @*/
88         /*@modifies nfi, fileSystem @*/
89 {
90     const char * fn = rpmfiFN(nfi);
91     int newFlags = rpmfiFFlags(nfi);
92     char buffer[1024];
93     fileTypes dbWhat, newWhat, diskWhat;
94     struct stat sb;
95     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
96
97     if (lstat(fn, &sb)) {
98         /*
99          * The file doesn't exist on the disk. Create it unless the new
100          * package has marked it as missingok, or allfiles is requested.
101          */
102         if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES)
103          && (newFlags & RPMFILE_MISSINGOK))
104         {
105             rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
106                         fn);
107             return FA_SKIP;
108         } else {
109             return FA_CREATE;
110         }
111     }
112
113     diskWhat = whatis(sb.st_mode);
114     dbWhat = whatis(ofi->fmodes[ofi->i]);
115     newWhat = whatis(nfi->fmodes[nfi->i]);
116
117     /*
118      * RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
119      * them in older packages as well.
120      */
121     if (newWhat == XDIR)
122         return FA_CREATE;
123
124     if (diskWhat != newWhat)
125         return save;
126     else if (newWhat != dbWhat && diskWhat != dbWhat)
127         return save;
128     else if (dbWhat != newWhat)
129         return FA_CREATE;
130     else if (dbWhat != LINK && dbWhat != REG)
131         return FA_CREATE;
132
133     /*
134      * This order matters - we'd prefer to CREATE the file if at all
135      * possible in case something else (like the timestamp) has changed.
136      */
137     if (dbWhat == REG) {
138 #ifdef  DYING
139         if (ofi->md5s != NULL && nfi->md5s != NULL) {
140 #endif
141             const unsigned char * omd5 = ofi->md5s + (16 * ofi->i);
142             const unsigned char * nmd5 = nfi->md5s + (16 * nfi->i);
143             if (domd5(fn, buffer, 0))
144                 return FA_CREATE;       /* assume file has been removed */
145             if (!memcmp(omd5, buffer, 16))
146                 return FA_CREATE;       /* unmodified config file, replace. */
147             if (!memcmp(omd5, nmd5, 16))
148                 return FA_SKIP;         /* identical file, don't bother. */
149 #ifdef  DYING
150         } else {
151             const char * omd5 = ofi->fmd5s[ofi->i];
152             const char * nmd5 = nfi->fmd5s[nfi->i];
153             if (domd5(fn, buffer, 1))
154                 return FA_CREATE;       /* assume file has been removed */
155             if (!strcmp(omd5, buffer))
156                 return FA_CREATE;       /* unmodified config file, replace. */
157             if (!strcmp(omd5, nmd5))
158                 return FA_SKIP;         /* identical file, don't bother. */
159         }
160 #endif
161     } else /* dbWhat == LINK */ {
162         memset(buffer, 0, sizeof(buffer));
163         if (readlink(fn, buffer, sizeof(buffer) - 1) == -1)
164             return FA_CREATE;   /* assume file has been removed */
165         if (!strcmp(ofi->flinks[ofi->i], buffer))
166             return FA_CREATE;   /* unmodified config file, replace. */
167         if (!strcmp(ofi->flinks[ofi->i], nfi->flinks[nfi->i]))
168             return FA_SKIP;     /* identical file, don't bother. */
169      }
170
171     /*
172      * The config file on the disk has been modified, but
173      * the ones in the two packages are different. It would
174      * be nice if RPM was smart enough to at least try and
175      * merge the difference ala CVS, but...
176      */
177     return save;
178 }
179
180 /**
181  */
182 static int filecmp(rpmfi afi, rpmfi bfi)
183         /*@*/
184 {
185     fileTypes awhat = whatis(afi->fmodes[afi->i]);
186     fileTypes bwhat = whatis(bfi->fmodes[bfi->i]);
187
188     if (awhat != bwhat) return 1;
189
190     if (awhat == LINK) {
191         const char * alink = afi->flinks[afi->i];
192         const char * blink = bfi->flinks[bfi->i];
193         return strcmp(alink, blink);
194     } else if (awhat == REG) {
195 #ifdef  DYING
196         if (afi->md5s != NULL && bfi->md5s != NULL) {
197 #endif
198             const unsigned char * amd5 = afi->md5s + (16 * afi->i);
199             const unsigned char * bmd5 = bfi->md5s + (16 * bfi->i);
200             return memcmp(amd5, bmd5, 16);
201 #ifdef  DYING
202         } else {
203             const char * amd5 = afi->fmd5s[afi->i];
204             const char * bmd5 = bfi->fmd5s[bfi->i];
205             return strcmp(amd5, bmd5);
206         }
207 #endif
208     }
209
210     return 0;
211 }
212
213 /**
214  */
215 /* XXX only ts->{probs,rpmdb} modified */
216 static int handleInstInstalledFiles(const rpmts ts,
217                 rpmte p, rpmfi fi,
218                 sharedFileInfo shared,
219                 int sharedCount, int reportConflicts)
220         /*@globals fileSystem @*/
221         /*@modifies ts, fi, fileSystem @*/
222 {
223     const char * altNEVR = NULL;
224     rpmfi otherFi = NULL;
225     int numReplaced = 0;
226     rpmps ps;
227     int i;
228
229     {   rpmdbMatchIterator mi;
230         Header h;
231         int scareMem = 0;
232
233         mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
234                         &shared->otherPkg, sizeof(shared->otherPkg));
235         while ((h = rpmdbNextIterator(mi)) != NULL) {
236             altNEVR = hGetNEVR(h, NULL);
237             otherFi = rpmfiNew(ts, NULL, h, RPMTAG_BASENAMES, scareMem);
238             break;
239         }
240         mi = rpmdbFreeIterator(mi);
241     }
242
243     if (otherFi == NULL)
244         return 1;
245
246     fi->replaced = xcalloc(sharedCount, sizeof(*fi->replaced));
247
248     ps = rpmtsProblems(ts);
249     for (i = 0; i < sharedCount; i++, shared++) {
250         int otherFileNum, fileNum;
251
252         otherFileNum = shared->otherFileNum;
253         (void) rpmfiSetFX(otherFi, otherFileNum);
254
255         fileNum = shared->pkgFileNum;
256         (void) rpmfiSetFX(fi, fileNum);
257
258 #ifdef  DYING
259         /* XXX another tedious segfault, assume file state normal. */
260         if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
261             continue;
262 #endif
263
264         if (XFA_SKIPPING(fi->actions[fileNum]))
265             continue;
266
267         if (filecmp(otherFi, fi)) {
268             if (reportConflicts) {
269                 rpmpsAppend(ps, RPMPROB_FILE_CONFLICT,
270                         rpmteNEVR(p), rpmteKey(p),
271                         rpmfiDN(fi), rpmfiBN(fi),
272                         altNEVR,
273                         0);
274             }
275             if (!(rpmfiFFlags(otherFi) | rpmfiFFlags(fi)) & RPMFILE_CONFIG) {
276                 /*@-assignexpose@*/ /* FIX: p->replaced, not fi */
277                 if (!shared->isRemoved)
278                     fi->replaced[numReplaced++] = *shared;
279                 /*@=assignexpose@*/
280             }
281         }
282
283         if ((rpmfiFFlags(otherFi) | rpmfiFFlags(fi)) & RPMFILE_CONFIG) {
284             fileAction action;
285             action = decideFileFate(ts, otherFi, fi);
286             fi->actions[fileNum] = action;
287         }
288         fi->replacedSizes[fileNum] = otherFi->fsizes[otherFi->i];
289
290     }
291     ps = rpmpsFree(ps);
292
293     altNEVR = _free(altNEVR);
294     otherFi = rpmfiFree(otherFi, 1);
295
296     fi->replaced = xrealloc(fi->replaced,       /* XXX memory leak */
297                            sizeof(*fi->replaced) * (numReplaced + 1));
298     fi->replaced[numReplaced].otherPkg = 0;
299
300     return 0;
301 }
302
303 /**
304  */
305 /* XXX only ts->rpmdb modified */
306 static int handleRmvdInstalledFiles(const rpmts ts, rpmfi fi,
307                 sharedFileInfo shared, int sharedCount)
308         /*@globals fileSystem @*/
309         /*@modifies ts, fi, fileSystem @*/
310 {
311     HGE_t hge = fi->hge;
312     Header h;
313     const char * otherStates;
314     int i, xx;
315    
316     rpmdbMatchIterator mi;
317
318     mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
319                         &shared->otherPkg, sizeof(shared->otherPkg));
320     h = rpmdbNextIterator(mi);
321     if (h == NULL) {
322         mi = rpmdbFreeIterator(mi);
323         return 1;
324     }
325
326     xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
327
328     for (i = 0; i < sharedCount; i++, shared++) {
329         int otherFileNum, fileNum;
330         otherFileNum = shared->otherFileNum;
331         fileNum = shared->pkgFileNum;
332
333         if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
334             continue;
335
336         fi->actions[fileNum] = FA_SKIP;
337     }
338
339     mi = rpmdbFreeIterator(mi);
340
341     return 0;
342 }
343
344 #define ISROOT(_d)      (((_d)[0] == '/' && (_d)[1] == '\0') ? "" : (_d))
345
346 /*@unchecked@*/
347 static int _fps_debug = 0;
348
349 static int fpsCompare (const void * one, const void * two)
350         /*@*/
351 {
352     const struct fingerPrint_s * a = (const struct fingerPrint_s *)one;
353     const struct fingerPrint_s * b = (const struct fingerPrint_s *)two;
354     int adnlen = strlen(a->entry->dirName);
355     int asnlen = (a->subDir ? strlen(a->subDir) : 0);
356     int abnlen = strlen(a->baseName);
357     int bdnlen = strlen(b->entry->dirName);
358     int bsnlen = (b->subDir ? strlen(b->subDir) : 0);
359     int bbnlen = strlen(b->baseName);
360     char * afn, * bfn, * t;
361     int rc = 0;
362
363     if (adnlen == 1 && asnlen != 0) adnlen = 0;
364     if (bdnlen == 1 && bsnlen != 0) bdnlen = 0;
365
366     afn = t = alloca(adnlen+asnlen+abnlen+2);
367     if (adnlen) t = stpcpy(t, a->entry->dirName);
368     *t++ = '/';
369     if (a->subDir && asnlen) t = stpcpy(t, a->subDir);
370     if (abnlen) t = stpcpy(t, a->baseName);
371     if (afn[0] == '/' && afn[1] == '/') afn++;
372
373     bfn = t = alloca(bdnlen+bsnlen+bbnlen+2);
374     if (bdnlen) t = stpcpy(t, b->entry->dirName);
375     *t++ = '/';
376     if (b->subDir && bsnlen) t = stpcpy(t, b->subDir);
377     if (bbnlen) t = stpcpy(t, b->baseName);
378     if (bfn[0] == '/' && bfn[1] == '/') bfn++;
379
380     rc = strcmp(afn, bfn);
381 /*@-modfilesys@*/
382 if (_fps_debug)
383 fprintf(stderr, "\trc(%d) = strcmp(\"%s\", \"%s\")\n", rc, afn, bfn);
384 /*@=modfilesys@*/
385
386 /*@-modfilesys@*/
387 if (_fps_debug)
388 fprintf(stderr, "\t%s/%s%s\trc %d\n",
389 ISROOT(b->entry->dirName),
390 (b->subDir ? b->subDir : ""),
391 b->baseName,
392 rc
393 );
394 /*@=modfilesys@*/
395
396     return rc;
397 }
398
399 /*@unchecked@*/
400 static int _linear_fps_search = 0;
401
402 static int findFps(const struct fingerPrint_s * fiFps,
403                 const struct fingerPrint_s * otherFps,
404                 int otherFc)
405         /*@*/
406 {
407     int otherFileNum;
408
409 /*@-modfilesys@*/
410 if (_fps_debug)
411 fprintf(stderr, "==> %s/%s%s\n",
412 ISROOT(fiFps->entry->dirName),
413 (fiFps->subDir ? fiFps->subDir : ""),
414 fiFps->baseName);
415 /*@=modfilesys@*/
416
417   if (_linear_fps_search) {
418
419 linear:
420     for (otherFileNum = 0; otherFileNum < otherFc; otherFileNum++, otherFps++) {
421
422 /*@-modfilesys@*/
423 if (_fps_debug)
424 fprintf(stderr, "\t%4d %s/%s%s\n", otherFileNum,
425 ISROOT(otherFps->entry->dirName),
426 (otherFps->subDir ? otherFps->subDir : ""),
427 otherFps->baseName);
428 /*@=modfilesys@*/
429
430         /* If the addresses are the same, so are the values. */
431         if (fiFps == otherFps)
432             break;
433
434         /* Otherwise, compare fingerprints by value. */
435         /*@-nullpass@*/ /* LCL: looks good to me */
436         if (FP_EQUAL((*fiFps), (*otherFps)))
437             break;
438         /*@=nullpass@*/
439     }
440
441 if (otherFileNum == otherFc) {
442 /*@-modfilesys@*/
443 if (_fps_debug)
444 fprintf(stderr, "*** NULL %s/%s%s\n",
445 ISROOT(fiFps->entry->dirName),
446 (fiFps->subDir ? fiFps->subDir : ""),
447 fiFps->baseName);
448 /*@=modfilesys@*/
449 }
450
451     return otherFileNum;
452
453   } else {
454
455     const struct fingerPrint_s * bingoFps;
456
457     bingoFps = bsearch(fiFps, otherFps, otherFc, sizeof(*otherFps), fpsCompare);
458     if (bingoFps == NULL) {
459 /*@-modfilesys@*/
460 fprintf(stderr, "*** NULL %s/%s%s\n",
461 ISROOT(fiFps->entry->dirName),
462 (fiFps->subDir ? fiFps->subDir : ""),
463 fiFps->baseName);
464 /*@=modfilesys@*/
465         goto linear;
466     }
467
468     /* If the addresses are the same, so are the values. */
469     /*@-nullpass@*/     /* LCL: looks good to me */
470     if (!(fiFps == bingoFps || FP_EQUAL((*fiFps), (*bingoFps)))) {
471 /*@-modfilesys@*/
472 fprintf(stderr, "***  BAD %s/%s%s\n",
473 ISROOT(bingoFps->entry->dirName),
474 (bingoFps->subDir ? bingoFps->subDir : ""),
475 bingoFps->baseName);
476 /*@=modfilesys@*/
477         goto linear;
478     }
479
480     otherFileNum = (bingoFps != NULL ? (bingoFps - otherFps) : 0);
481
482   }
483
484     return otherFileNum;
485 }
486
487 /**
488  * Update disk space needs on each partition for this package's files.
489  */
490 /* XXX only ts->{probs,di} modified */
491 static void handleOverlappedFiles(const rpmts ts,
492                 const rpmte p, rpmfi fi)
493         /*@globals fileSystem @*/
494         /*@modifies ts, fi, fileSystem @*/
495 {
496     uint_32 fixupSize = 0;
497     rpmps ps;
498     const char * fn;
499     int i, j;
500   
501     ps = rpmtsProblems(ts);
502     fi = rpmfiInit(fi, 0);
503     if (fi != NULL)
504     while ((i = rpmfiNext(fi)) >= 0) {
505         struct fingerPrint_s * fiFps;
506         int otherPkgNum, otherFileNum;
507         rpmfi otherFi;
508         const rpmfi * recs;
509         int numRecs;
510
511         if (XFA_SKIPPING(fi->actions[i]))
512             continue;
513
514         fn = rpmfiFN(fi);
515         fiFps = fi->fps + i;
516
517         fixupSize = 0;
518
519         /*
520          * Retrieve all records that apply to this file. Note that the
521          * file info records were built in the same order as the packages
522          * will be installed and removed so the records for an overlapped
523          * files will be sorted in exactly the same order.
524          */
525         (void) htGetEntry(ts->ht, fiFps,
526                         (const void ***) &recs, &numRecs, NULL);
527
528         /*
529          * If this package is being added, look only at other packages
530          * being added -- removed packages dance to a different tune.
531          *
532          * If both this and the other package are being added, overlapped
533          * files must be identical (or marked as a conflict). The
534          * disposition of already installed config files leads to
535          * a small amount of extra complexity.
536          *
537          * If this package is being removed, then there are two cases that
538          * need to be worried about:
539          * If the other package is being added, then skip any overlapped files
540          * so that this package removal doesn't nuke the overlapped files
541          * that were just installed.
542          * If both this and the other package are being removed, then each
543          * file removal from preceding packages needs to be skipped so that
544          * the file removal occurs only on the last occurence of an overlapped
545          * file in the transaction set.
546          *
547          */
548
549         /* Locate this overlapped file in the set of added/removed packages. */
550         for (j = 0; j < numRecs && recs[j] != fi; j++)
551             {};
552
553         /* Find what the previous disposition of this file was. */
554         otherFileNum = -1;                      /* keep gcc quiet */
555         otherFi = NULL;
556         for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
557             struct fingerPrint_s * otherFps;
558             int otherFc;
559
560             otherFi = recs[otherPkgNum];
561
562             /* Added packages need only look at other added packages. */
563             if (rpmteType(p) == TR_ADDED && rpmteType(otherFi->te) != TR_ADDED)
564                 /*@innercontinue@*/ continue;
565
566             otherFps = otherFi->fps;
567             otherFc = rpmfiFC(otherFi);
568
569             otherFileNum = findFps(fiFps, otherFps, otherFc);
570             (void) rpmfiSetFX(otherFi, otherFileNum);
571
572             /* XXX is this test still necessary? */
573 assert(otherFi->actions[otherFileNum] != FA_UNKNOWN);
574             if (otherFi->actions[otherFileNum] != FA_UNKNOWN)
575                 /*@innerbreak@*/ break;
576         }
577
578         switch (rpmteType(p)) {
579         case TR_ADDED:
580           { struct stat sb;
581             if (otherPkgNum < 0) {
582                 /* XXX is this test still necessary? */
583                 if (fi->actions[i] != FA_UNKNOWN)
584                     /*@switchbreak@*/ break;
585                 if ((rpmfiFFlags(fi) & RPMFILE_CONFIG) && 
586                         !lstat(fn, &sb)) {
587                     /* Here is a non-overlapped pre-existing config file. */
588                     fi->actions[i] = (rpmfiFFlags(fi) & RPMFILE_NOREPLACE)
589                         ? FA_ALTNAME : FA_BACKUP;
590                 } else {
591                     fi->actions[i] = FA_CREATE;
592                 }
593                 /*@switchbreak@*/ break;
594             }
595
596 assert(otherFi != NULL);
597             /* Mark added overlapped non-identical files as a conflict. */
598             if ((rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES)
599              && filecmp(otherFi, fi))
600             {
601                 rpmpsAppend(ps, RPMPROB_NEW_FILE_CONFLICT,
602                         rpmteNEVR(p), rpmteKey(p),
603                         fn, NULL,
604                         rpmteNEVR(otherFi->te),
605                         0);
606             }
607
608             /* Try to get the disk accounting correct even if a conflict. */
609             fixupSize = otherFi->fsizes[otherFileNum];
610
611             if ((rpmfiFFlags(fi) & RPMFILE_CONFIG) && !lstat(fn, &sb)) {
612                 /* Here is an overlapped  pre-existing config file. */
613                 fi->actions[i] = (rpmfiFFlags(fi) & RPMFILE_NOREPLACE)
614                         ? FA_ALTNAME : FA_SKIP;
615             } else {
616                 fi->actions[i] = FA_CREATE;
617             }
618           } /*@switchbreak@*/ break;
619
620         case TR_REMOVED:
621             if (otherPkgNum >= 0) {
622 assert(otherFi != NULL);
623                 /* Here is an overlapped added file we don't want to nuke. */
624                 if (otherFi->actions[otherFileNum] != FA_ERASE) {
625                     /* On updates, don't remove files. */
626                     fi->actions[i] = FA_SKIP;
627                     /*@switchbreak@*/ break;
628                 }
629                 /* Here is an overlapped removed file: skip in previous. */
630                 otherFi->actions[otherFileNum] = FA_SKIP;
631             }
632             if (XFA_SKIPPING(fi->actions[i]))
633                 /*@switchbreak@*/ break;
634             if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
635                 /*@switchbreak@*/ break;
636             if (!(S_ISREG(fi->fmodes[i]) && (rpmfiFFlags(fi) & RPMFILE_CONFIG))) {
637                 fi->actions[i] = FA_ERASE;
638                 /*@switchbreak@*/ break;
639             }
640                 
641             /* Here is a pre-existing modified config file that needs saving. */
642             {   char md5sum[50];
643                 const unsigned char * md5 = fi->md5s + (16 * i);
644                 if (!domd5(fn, md5sum, 0) && memcmp(md5, md5sum, 16)) {
645                     fi->actions[i] = FA_BACKUP;
646                     /*@switchbreak@*/ break;
647                 }
648             }
649             fi->actions[i] = FA_ERASE;
650             /*@switchbreak@*/ break;
651         }
652
653         /* Update disk space info for a file. */
654         rpmtsUpdateDSI(ts, fi->fps[i].entry->dev,
655                 fi->fsizes[i], fi->replacedSizes[i], fixupSize, fi->actions[i]);
656
657     }
658     ps = rpmpsFree(ps);
659 }
660
661 /**
662  * Ensure that current package is newer than installed package.
663  * @param ts            transaction set
664  * @param p             current transaction element
665  * @param h             installed header
666  * @return              0 if not newer, 1 if okay
667  */
668 static int ensureOlder(rpmts ts,
669                 const rpmte p, const Header h)
670         /*@modifies ts @*/
671 {
672     int_32 reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
673     const char * reqEVR;
674     rpmds req;
675     char * t;
676     int nb;
677     int rc;
678
679     if (p == NULL || h == NULL)
680         return 1;
681
682     nb = strlen(rpmteNEVR(p)) + (rpmteE(p) != NULL ? strlen(rpmteE(p)) : 0) + 1;
683     t = alloca(nb);
684     *t = '\0';
685     reqEVR = t;
686     if (rpmteE(p) != NULL)      t = stpcpy( stpcpy(t, rpmteE(p)), ":");
687     if (rpmteV(p) != NULL)      t = stpcpy(t, rpmteV(p));
688     *t++ = '-';
689     if (rpmteR(p) != NULL)      t = stpcpy(t, rpmteR(p));
690     
691     req = rpmdsSingle(RPMTAG_REQUIRENAME, rpmteN(p), reqEVR, reqFlags);
692     rc = headerMatchesDepFlags(h, req);
693     req = rpmdsFree(req);
694
695     if (rc == 0) {
696         rpmps ps = rpmtsProblems(ts);
697         const char * altNEVR = hGetNEVR(h, NULL);
698         rpmpsAppend(ps, RPMPROB_OLDPACKAGE,
699                 rpmteNEVR(p), rpmteKey(p),
700                 NULL, NULL,
701                 altNEVR,
702                 0);
703         altNEVR = _free(altNEVR);
704         ps = rpmpsFree(ps);
705         rc = 1;
706     } else
707         rc = 0;
708
709     return rc;
710 }
711
712 /**
713  */
714 /*@-mustmod@*/ /* FIX: fi->actions is modified. */
715 static void skipFiles(const rpmts ts, rpmfi fi)
716         /*@globals rpmGlobalMacroContext @*/
717         /*@modifies fi, rpmGlobalMacroContext @*/
718 {
719     int noDocs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NODOCS);
720     char ** netsharedPaths = NULL;
721     const char ** languages;
722     const char * dn, * bn;
723     int dnlen, bnlen, ix;
724     const char * s;
725     int * drc;
726     char * dff;
727     int dc;
728     int i, j;
729
730     if (!noDocs)
731         noDocs = rpmExpandNumeric("%{_excludedocs}");
732
733     {   const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
734         /*@-branchstate@*/
735         if (tmpPath && *tmpPath != '%')
736             netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
737         /*@=branchstate@*/
738         tmpPath = _free(tmpPath);
739     }
740
741     s = rpmExpand("%{_install_langs}", NULL);
742     /*@-branchstate@*/
743     if (!(s && *s != '%'))
744         s = _free(s);
745     if (s) {
746         languages = (const char **) splitString(s, strlen(s), ':');
747         s = _free(s);
748     } else
749         languages = NULL;
750     /*@=branchstate@*/
751
752     /* Compute directory refcount, skip directory if now empty. */
753     dc = rpmfiDC(fi);
754     drc = alloca(dc * sizeof(*drc));
755     memset(drc, 0, dc * sizeof(*drc));
756     dff = alloca(dc * sizeof(*dff));
757     memset(dff, 0, dc * sizeof(*dff));
758
759     fi = rpmfiInit(fi, 0);
760     if (fi != NULL)     /* XXX lclint */
761     while ((i = rpmfiNext(fi)) >= 0)
762     {
763         char **nsp;
764
765         bn = rpmfiBN(fi);
766         bnlen = strlen(bn);
767         ix = rpmfiDX(fi);
768         dn = rpmfiDN(fi);
769         dnlen = strlen(dn);
770         if (dn == NULL)
771             continue;   /* XXX can't happen */
772
773         drc[ix]++;
774
775         /* Don't bother with skipped files */
776         if (XFA_SKIPPING(fi->actions[i])) {
777             drc[ix]--;
778             continue;
779         }
780
781         /*
782          * Skip net shared paths.
783          * Net shared paths are not relative to the current root (though
784          * they do need to take package relocations into account).
785          */
786         for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
787             int len;
788
789             len = strlen(*nsp);
790             if (dnlen >= len) {
791                 if (strncmp(dn, *nsp, len))
792                     /*@innercontinue@*/ continue;
793                 /* Only directories or complete file paths can be net shared */
794                 if (!(dn[len] == '/' || dn[len] == '\0'))
795                     /*@innercontinue@*/ continue;
796             } else {
797                 if (len < (dnlen + bnlen))
798                     /*@innercontinue@*/ continue;
799                 if (strncmp(dn, *nsp, dnlen))
800                     /*@innercontinue@*/ continue;
801                 if (strncmp(bn, (*nsp) + dnlen, bnlen))
802                     /*@innercontinue@*/ continue;
803                 len = dnlen + bnlen;
804                 /* Only directories or complete file paths can be net shared */
805                 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
806                     /*@innercontinue@*/ continue;
807             }
808
809             /*@innerbreak@*/ break;
810         }
811
812         if (nsp && *nsp) {
813             drc[ix]--;  dff[ix] = 1;
814             fi->actions[i] = FA_SKIPNETSHARED;
815             continue;
816         }
817
818         /*
819          * Skip i18n language specific files.
820          */
821         if (fi->flangs && languages && *fi->flangs[i]) {
822             const char **lang, *l, *le;
823             for (lang = languages; *lang != NULL; lang++) {
824                 if (!strcmp(*lang, "all"))
825                     /*@innerbreak@*/ break;
826                 for (l = fi->flangs[i]; *l != '\0'; l = le) {
827                     for (le = l; *le != '\0' && *le != '|'; le++)
828                         {};
829                     if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
830                         /*@innerbreak@*/ break;
831                     if (*le == '|') le++;       /* skip over | */
832                 }
833                 if (*l != '\0')
834                     /*@innerbreak@*/ break;
835             }
836             if (*lang == NULL) {
837                 drc[ix]--;      dff[ix] = 1;
838                 fi->actions[i] = FA_SKIPNSTATE;
839                 continue;
840             }
841         }
842
843         /*
844          * Skip documentation if requested.
845          */
846         if (noDocs && (rpmfiFFlags(fi) & RPMFILE_DOC)) {
847             drc[ix]--;  dff[ix] = 1;
848             fi->actions[i] = FA_SKIPNSTATE;
849             continue;
850         }
851     }
852
853     /* Skip (now empty) directories that had skipped files. */
854 #ifndef NOTYET
855     if (fi != NULL)     /* XXX can't happen */
856     for (j = 0; j < dc; j++)
857 #else
858     if ((fi = rpmfiInitD(fi)) != NULL)
859     while (j = rpmfiNextD(fi) >= 0)
860 #endif
861     {
862
863         if (drc[j]) continue;   /* dir still has files. */
864         if (!dff[j]) continue;  /* dir was not emptied here. */
865         
866         /* Find parent directory and basename. */
867         dn = fi->dnl[j];        dnlen = strlen(dn) - 1;
868         bn = dn + dnlen;        bnlen = 0;
869         while (bn > dn && bn[-1] != '/') {
870                 bnlen++;
871                 dnlen--;
872                 bn--;
873         }
874
875         /* If explicitly included in the package, skip the directory. */
876         fi = rpmfiInit(fi, 0);
877         if (fi != NULL)         /* XXX lclint */
878         while ((i = rpmfiNext(fi)) >= 0) {
879             const char * dir;
880
881             if (XFA_SKIPPING(fi->actions[i]))
882                 /*@innercontinue@*/ continue;
883             if (whatis(fi->fmodes[i]) != XDIR)
884                 /*@innercontinue@*/ continue;
885             dir = fi->dnl[fi->dil[i]];
886             if (strlen(dir) != dnlen)
887                 /*@innercontinue@*/ continue;
888             if (strncmp(dir, dn, dnlen))
889                 /*@innercontinue@*/ continue;
890             if (strlen(fi->bnl[i]) != bnlen)
891                 /*@innercontinue@*/ continue;
892             if (strncmp(fi->bnl[i], bn, bnlen))
893                 /*@innercontinue@*/ continue;
894             rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
895             fi->actions[i] = FA_SKIPNSTATE;
896             /*@innerbreak@*/ break;
897         }
898     }
899
900     if (netsharedPaths) freeSplitString(netsharedPaths);
901 #ifdef  DYING   /* XXX freeFi will deal with this later. */
902     fi->flangs = _free(fi->flangs);
903 #endif
904     if (languages) freeSplitString((char **)languages);
905 }
906 /*@=mustmod@*/
907
908 /**
909  * Return transaction element's file info.
910  * @todo Take a rpmfi refcount here.
911  * @param tsi           transaction element iterator
912  * @return              transaction element file info
913  */
914 static /*@null@*/
915 rpmfi rpmtsiFi(const rpmtsi tsi)
916         /*@*/
917 {
918     rpmfi fi = NULL;
919
920     if (tsi != NULL && tsi->ocsave != -1) {
921         /*@-type -abstract@*/ /* FIX: rpmte not opaque */
922         rpmte te = rpmtsElement(tsi->ts, tsi->ocsave);
923         /*@-assignexpose@*/
924         if (te != NULL && (fi = te->fi) != NULL)
925             fi->te = te;
926         /*@=assignexpose@*/
927         /*@=type =abstract@*/
928     }
929     /*@-compdef -refcounttrans -usereleased @*/
930     return fi;
931     /*@=compdef =refcounttrans =usereleased @*/
932 }
933
934 #define NOTIFY(_ts, _al) /*@i@*/ if ((_ts)->notify) (void) (_ts)->notify _al
935
936 int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
937 {
938     int i, j;
939     int ourrc = 0;
940     int totalFileCount = 0;
941     rpmfi fi;
942     sharedFileInfo shared, sharedList;
943     int numShared;
944     int nexti;
945     alKey lastKey;
946     fingerPrintCache fpc;
947     rpmps ps;
948     PSM_t psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
949     rpmtsi pi;  rpmte p;
950     rpmtsi qi;  rpmte q;
951     int xx;
952
953     /* FIXME: what if the same package is included in ts twice? */
954
955     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOSCRIPTS)
956         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
957     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERS)
958         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransTriggers));
959
960     /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
961     if (rpmtsFlags(ts) & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
962         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
963
964     ts->probs = rpmpsFree(ts->probs);
965     ts->probs = rpmpsCreate();
966
967     ts->ignoreSet = ignoreSet;
968     {   const char * currDir = currentDirectory();
969         rpmtsSetCurrDir(ts, currDir);
970         currDir = _free(currDir);
971     }
972
973     (void) rpmtsSetChrootDone(ts, 0);
974
975     {   int_32 tid = (int_32) time(NULL);
976         (void) rpmtsSetTid(ts, tid);
977     }
978
979     memset(psm, 0, sizeof(*psm));
980     psm->ts = rpmtsLink(ts, "tsRun");
981
982     /* Get available space on mounted file systems. */
983     xx = rpmtsInitDSI(ts);
984
985     /* ===============================================
986      * For packages being installed:
987      * - verify package arch/os.
988      * - verify package epoch:version-release is newer.
989      * - count files.
990      * For packages being removed:
991      * - count files.
992      */
993     ps = rpmtsProblems(ts);
994     /* The ordering doesn't matter here */
995     pi = rpmtsiInit(ts);
996     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
997         rpmdbMatchIterator mi;
998         int fc;
999
1000         if ((fi = rpmtsiFi(pi)) == NULL)
1001             continue;   /* XXX can't happen */
1002         fc = rpmfiFC(fi);
1003
1004         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_IGNOREARCH))
1005             if (!archOkay(rpmteA(p)))
1006                 rpmpsAppend(ps, RPMPROB_BADARCH,
1007                         rpmteNEVR(p), rpmteKey(p),
1008                         rpmteA(p), NULL,
1009                         NULL, 0);
1010
1011         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_IGNOREOS))
1012             if (!osOkay(rpmteO(p)))
1013                 rpmpsAppend(ps, RPMPROB_BADOS,
1014                         rpmteNEVR(p), rpmteKey(p),
1015                         rpmteO(p), NULL,
1016                         NULL, 0);
1017
1018         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_OLDPACKAGE)) {
1019             Header h;
1020             mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
1021             while ((h = rpmdbNextIterator(mi)) != NULL)
1022                 xx = ensureOlder(ts, p, h);
1023             mi = rpmdbFreeIterator(mi);
1024         }
1025
1026         /* XXX multilib should not display "already installed" problems */
1027         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEPKG) && !rpmteMultiLib(p)) {
1028             mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
1029             xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT,
1030                                 rpmteV(p));
1031             xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
1032                                 rpmteR(p));
1033
1034             while (rpmdbNextIterator(mi) != NULL) {
1035                 rpmpsAppend(ps, RPMPROB_PKG_INSTALLED,
1036                         rpmteNEVR(p), rpmteKey(p),
1037                         NULL, NULL,
1038                         NULL, 0);
1039                 /*@innerbreak@*/ break;
1040             }
1041             mi = rpmdbFreeIterator(mi);
1042         }
1043
1044         /* Count no. of files (if any). */
1045         totalFileCount += fc;
1046
1047     }
1048     pi = rpmtsiFree(pi);
1049     ps = rpmpsFree(ps);
1050
1051     /* The ordering doesn't matter here */
1052     pi = rpmtsiInit(ts);
1053     while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
1054         int fc;
1055
1056         if ((fi = rpmtsiFi(pi)) == NULL)
1057             continue;   /* XXX can't happen */
1058         fc = rpmfiFC(fi);
1059
1060         totalFileCount += fc;
1061     }
1062     pi = rpmtsiFree(pi);
1063
1064     /* ===============================================
1065      * Initialize transaction element file info for package:
1066      */
1067
1068     /*
1069      * FIXME?: we'd be better off assembling one very large file list and
1070      * calling fpLookupList only once. I'm not sure that the speedup is
1071      * worth the trouble though.
1072      */
1073     pi = rpmtsiInit(ts);
1074     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1075         int fc;
1076
1077         if ((fi = rpmtsiFi(pi)) == NULL)
1078             continue;   /* XXX can't happen */
1079         fc = rpmfiFC(fi);
1080
1081 #ifdef  DYING   /* XXX W2DO? this is now done in rpmtsiFi, okay ??? */
1082         fi->magic = RPMFIMAGIC;
1083         fi->te = p;
1084 #endif
1085
1086         /*@-branchstate@*/
1087         switch (rpmteType(p)) {
1088         case TR_ADDED:
1089             fi->record = 0;
1090             /* Skip netshared paths, not our i18n files, and excluded docs */
1091             if (fc > 0)
1092                 skipFiles(ts, fi);
1093             /*@switchbreak@*/ break;
1094         case TR_REMOVED:
1095             fi->record = rpmteDBOffset(p);
1096             /*@switchbreak@*/ break;
1097         }
1098         /*@=branchstate@*/
1099
1100         fi->fps = (fc > 0 ? xmalloc(fc * sizeof(*fi->fps)) : NULL);
1101     }
1102     pi = rpmtsiFree(pi);
1103
1104     if (!rpmtsChrootDone(ts)) {
1105         const char * rootDir = rpmtsRootDir(ts);
1106         xx = chdir("/");
1107         /*@-superuser -noeffect @*/
1108         if (rootDir != NULL)
1109             xx = chroot(rootDir);
1110         /*@=superuser =noeffect @*/
1111         (void) rpmtsSetChrootDone(ts, 1);
1112     }
1113
1114     ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1115     fpc = fpCacheCreate(totalFileCount);
1116
1117     /* ===============================================
1118      * Add fingerprint for each file not skipped.
1119      */
1120     pi = rpmtsiInit(ts);
1121     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1122         int fc;
1123
1124         if ((fi = rpmtsiFi(pi)) == NULL)
1125             continue;   /* XXX can't happen */
1126         fc = rpmfiFC(fi);
1127
1128         fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fc, fi->fps);
1129         /*@-branchstate@*/
1130         fi = rpmfiInit(fi, 0);
1131         if (fi != NULL)         /* XXX lclint */
1132         while ((i = rpmfiNext(fi)) >= 0) {
1133             if (XFA_SKIPPING(fi->actions[i]))
1134                 /*@innercontinue@*/ continue;
1135             /*@-dependenttrans@*/
1136             htAddEntry(ts->ht, fi->fps + i, (void *) fi);
1137             /*@=dependenttrans@*/
1138         }
1139         /*@=branchstate@*/
1140     }
1141     pi = rpmtsiFree(pi);
1142
1143     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->orderCount,
1144         NULL, ts->notifyData));
1145
1146     /* ===============================================
1147      * Compute file disposition for each package in transaction set.
1148      */
1149     ps = rpmtsProblems(ts);
1150     pi = rpmtsiInit(ts);
1151     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1152         dbiIndexSet * matches;
1153         int knownBad;
1154         int fc;
1155
1156         if ((fi = rpmtsiFi(pi)) == NULL)
1157             continue;   /* XXX can't happen */
1158         fc = rpmfiFC(fi);
1159
1160         NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, rpmtsiOc(pi),
1161                         ts->orderCount, NULL, ts->notifyData));
1162
1163         if (fc == 0) continue;
1164
1165         /* Extract file info for all files in this package from the database. */
1166         matches = xcalloc(fc, sizeof(*matches));
1167         if (rpmdbFindFpList(rpmtsGetRdb(ts), fi->fps, matches, fc)) {
1168             psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1169             ps = rpmpsFree(ps);
1170             return 1;   /* XXX WTFO? */
1171         }
1172
1173         numShared = 0;
1174         fi = rpmfiInit(fi, 0);
1175         while ((i = rpmfiNext(fi)) >= 0)
1176             numShared += dbiIndexSetCount(matches[i]);
1177
1178         /* Build sorted file info list for this package. */
1179         shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1180
1181         fi = rpmfiInit(fi, 0);
1182         while ((i = rpmfiNext(fi)) >= 0) {
1183             /*
1184              * Take care not to mark files as replaced in packages that will
1185              * have been removed before we will get here.
1186              */
1187             for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1188                 int ro;
1189                 ro = dbiIndexRecordOffset(matches[i], j);
1190                 knownBad = 0;
1191                 qi = rpmtsiInit(ts);
1192                 while ((q = rpmtsiNext(qi, TR_REMOVED)) != NULL) {
1193                     if (ro == knownBad)
1194                         /*@innerbreak@*/ break;
1195                     if (rpmteDBOffset(q) == ro)
1196                         knownBad = ro;
1197                 }
1198                 qi = rpmtsiFree(qi);
1199
1200                 shared->pkgFileNum = i;
1201                 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1202                 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1203                 shared->isRemoved = (knownBad == ro);
1204                 shared++;
1205             }
1206             matches[i] = dbiFreeIndexSet(matches[i]);
1207         }
1208         numShared = shared - sharedList;
1209         shared->otherPkg = -1;
1210         matches = _free(matches);
1211
1212         /* Sort file info by other package index (otherPkg) */
1213         qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1214
1215         /* For all files from this package that are in the database ... */
1216         /*@-branchstate@*/
1217         for (i = 0; i < numShared; i = nexti) {
1218             int beingRemoved;
1219
1220             shared = sharedList + i;
1221
1222             /* Find the end of the files in the other package. */
1223             for (nexti = i + 1; nexti < numShared; nexti++) {
1224                 if (sharedList[nexti].otherPkg != shared->otherPkg)
1225                     /*@innerbreak@*/ break;
1226             }
1227
1228             /* Is this file from a package being removed? */
1229             beingRemoved = 0;
1230             if (ts->removedPackages != NULL)
1231             for (j = 0; j < ts->numRemovedPackages; j++) {
1232                 if (ts->removedPackages[j] != shared->otherPkg)
1233                     /*@innercontinue@*/ continue;
1234                 beingRemoved = 1;
1235                 /*@innerbreak@*/ break;
1236             }
1237
1238             /* Determine the fate of each file. */
1239             switch (rpmteType(p)) {
1240             case TR_ADDED:
1241                 xx = handleInstInstalledFiles(ts, p, fi, shared, nexti - i,
1242         !(beingRemoved || (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES)));
1243                 /*@switchbreak@*/ break;
1244             case TR_REMOVED:
1245                 if (!beingRemoved)
1246                     xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1247                 /*@switchbreak@*/ break;
1248             }
1249         }
1250         /*@=branchstate@*/
1251
1252         free(sharedList);
1253
1254         /* Update disk space needs on each partition for this package. */
1255         handleOverlappedFiles(ts, p, fi);
1256
1257         /* Check added package has sufficient space on each partition used. */
1258         switch (rpmteType(p)) {
1259         case TR_ADDED:
1260             rpmtsCheckDSIProblems(ts, p);
1261             /*@switchbreak@*/ break;
1262         case TR_REMOVED:
1263             /*@switchbreak@*/ break;
1264         }
1265     }
1266     pi = rpmtsiFree(pi);
1267     ps = rpmpsFree(ps);
1268
1269     if (rpmtsChrootDone(ts)) {
1270         const char * currDir = rpmtsCurrDir(ts);
1271         /*@-superuser -noeffect @*/
1272         xx = chroot(".");
1273         /*@=superuser =noeffect @*/
1274         (void) rpmtsSetChrootDone(ts, 0);
1275         if (currDir != NULL)
1276             xx = chdir(currDir);
1277     }
1278
1279     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->orderCount,
1280         NULL, ts->notifyData));
1281
1282     /* ===============================================
1283      * Free unused memory as soon as possible.
1284      */
1285     pi = rpmtsiInit(ts);
1286     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1287         if ((fi = rpmtsiFi(pi)) == NULL)
1288             continue;   /* XXX can't happen */
1289         if (rpmfiFC(fi) == 0)
1290             continue;
1291         fi->fps = _free(fi->fps);
1292     }
1293     pi = rpmtsiFree(pi);
1294
1295     fpc = fpCacheFree(fpc);
1296     ts->ht = htFree(ts->ht);
1297
1298     /* ===============================================
1299      * If unfiltered problems exist, free memory and return.
1300      */
1301     if ((rpmtsFlags(ts) & RPMTRANS_FLAG_BUILD_PROBS)
1302      || (ts->probs->numProblems &&
1303                 (okProbs != NULL || rpmpsTrim(ts->probs, okProbs)))
1304        )
1305     {
1306         if (psm->ts != NULL)
1307             psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1308         return ts->orderCount;
1309     }
1310
1311     /* ===============================================
1312      * Save removed files before erasing.
1313      */
1314     if (rpmtsFlags(ts) & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1315         pi = rpmtsiInit(ts);
1316         while ((p = rpmtsiNext(pi, 0)) != NULL) {
1317             fi = rpmtsiFi(pi);
1318             switch (rpmteType(p)) {
1319             case TR_ADDED:
1320                 /*@switchbreak@*/ break;
1321             case TR_REMOVED:
1322                 if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_REPACKAGE))
1323                     /*@switchbreak@*/ break;
1324                 psm->te = p;
1325                 psm->fi = rpmfiLink(fi, "tsRepackage");
1326                 xx = psmStage(psm, PSM_PKGSAVE);
1327                 (void) rpmfiUnlink(fi, "tsRepackage");
1328                 psm->fi = NULL;
1329                 psm->te = NULL;
1330                 /*@switchbreak@*/ break;
1331             }
1332         }
1333         pi = rpmtsiFree(pi);
1334     }
1335
1336     /* ===============================================
1337      * Install and remove packages.
1338      */
1339     lastKey = (alKey)-2;        /* erased packages have -1 */
1340     pi = rpmtsiInit(ts);
1341     /*@-branchstate@*/ /* FIX: fi reload needs work */
1342     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1343         alKey pkgKey;
1344         int gotfd;
1345
1346         gotfd = 0;
1347         if ((fi = rpmtsiFi(pi)) == NULL)
1348             continue;   /* XXX can't happen */
1349         
1350         psm->te = p;
1351         psm->fi = rpmfiLink(fi, "tsInstall");
1352         switch (rpmteType(p)) {
1353         case TR_ADDED:
1354
1355             pkgKey = rpmteAddedKey(p);
1356
1357             rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", rpmteNEVR(p));
1358             p->h = NULL;
1359             /*@-type@*/ /* FIX: rpmte not opaque */
1360             {
1361                 /*@-noeffectuncon@*/ /* FIX: notify annotations */
1362                 p->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1363                                 rpmteKey(p), ts->notifyData);
1364                 /*@=noeffectuncon@*/
1365                 if (rpmteFd(p) != NULL) {
1366                     rpmRC rpmrc;
1367
1368                     rpmrc = rpmReadPackageFile(ts, rpmteFd(p),
1369                                 "rpmtsRun", &p->h);
1370
1371                     if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1372                         /*@-noeffectuncon@*/ /* FIX: notify annotations */
1373                         p->fd = ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1374                                         0, 0,
1375                                         rpmteKey(p), ts->notifyData);
1376                         /*@=noeffectuncon@*/
1377                         p->fd = NULL;
1378                         ourrc++;
1379                     }
1380                     if (rpmteFd(p) != NULL) gotfd = 1;
1381                 }
1382             }
1383             /*@=type@*/
1384
1385             if (rpmteFd(p) != NULL) {
1386                 {
1387 char * fstates = fi->fstates;
1388 fileAction * actions = fi->actions;
1389
1390 fi->fstates = NULL;
1391 fi->actions = NULL;
1392                     psm->fi = rpmfiFree(psm->fi, 1);
1393                     (void) rpmfiFree(fi, 0);
1394 /*@-usereleased@*/
1395 fi->magic = RPMFIMAGIC;
1396 fi->te = p;
1397 fi->record = 0;
1398                     (void) rpmfiNew(ts, fi, p->h, RPMTAG_BASENAMES, 1);
1399                     psm->fi = rpmfiLink(fi, "tsInstall");
1400 fi->fstates = _free(fi->fstates);
1401 fi->fstates = fstates;
1402 fi->actions = _free(fi->actions);
1403 fi->actions = actions;
1404 /*@=usereleased@*/
1405
1406                 }
1407                 if (rpmteMultiLib(p))
1408                     (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_MULTILIB));
1409                 else
1410                     (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) & ~RPMTRANS_FLAG_MULTILIB));
1411
1412                 if (psmStage(psm, PSM_PKGINSTALL)) {
1413                     ourrc++;
1414                     lastKey = pkgKey;
1415                 }
1416                 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1417             } else {
1418                 ourrc++;
1419                 lastKey = pkgKey;
1420             }
1421
1422             p->h = headerFree(p->h, "TR_ADDED h free");
1423
1424             if (gotfd) {
1425                 /*@-noeffectuncon @*/ /* FIX: check rc */
1426                 (void) ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1427                         rpmteKey(p), ts->notifyData);
1428                 /*@=noeffectuncon @*/
1429                 /*@-type@*/
1430                 p->fd = NULL;
1431                 /*@=type@*/
1432             }
1433             /*@switchbreak@*/ break;
1434         case TR_REMOVED:
1435             rpmMessage(RPMMESS_DEBUG, "========== --- %s\n", rpmteNEVR(p));
1436             /* If install failed, then we shouldn't erase. */
1437             if (rpmteDependsOnKey(p) != lastKey) {
1438                 if (psmStage(psm, PSM_PKGERASE))
1439                     ourrc++;
1440             }
1441             /*@switchbreak@*/ break;
1442         }
1443         xx = rpmdbSync(rpmtsGetRdb(ts));
1444         (void) rpmfiUnlink(psm->fi, "tsInstall");
1445         psm->fi = NULL;
1446         psm->te = NULL;
1447 /*@-type@*/ /* FIX: p is almost opaque */
1448         p->fi = rpmfiFree(fi, 1);
1449 /*@=type@*/
1450     }
1451     /*@=branchstate@*/
1452     pi = rpmtsiFree(pi);
1453
1454     psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1455
1456     /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
1457     if (ourrc)
1458         return -1;
1459     else
1460         return 0;
1461     /*@=nullstate@*/
1462 }