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