- fix: reading macro files can corrupt memory if BUFSIZ is teensy.
[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, NULL))
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, NULL))
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 Happens iff fingerprint for incomplete package install. */
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, NULL) && 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 = rpmtsProblems(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 /*@-bounds@*/
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 /*@=bounds@*/
907 /*@=mustmod@*/
908
909 /**
910  * Return transaction element's file info.
911  * @todo Take a rpmfi refcount here.
912  * @param tsi           transaction element iterator
913  * @return              transaction element file info
914  */
915 static /*@null@*/
916 rpmfi rpmtsiFi(const rpmtsi tsi)
917         /*@*/
918 {
919     rpmfi fi = NULL;
920
921     if (tsi != NULL && tsi->ocsave != -1) {
922         /*@-type -abstract@*/ /* FIX: rpmte not opaque */
923         rpmte te = rpmtsElement(tsi->ts, tsi->ocsave);
924         /*@-assignexpose@*/
925         if (te != NULL && (fi = te->fi) != NULL)
926             fi->te = te;
927         /*@=assignexpose@*/
928         /*@=type =abstract@*/
929     }
930     /*@-compdef -refcounttrans -usereleased @*/
931     return fi;
932     /*@=compdef =refcounttrans =usereleased @*/
933 }
934
935 #define NOTIFY(_ts, _al) /*@i@*/ if ((_ts)->notify) (void) (_ts)->notify _al
936
937 int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
938 {
939     int i, j;
940     int ourrc = 0;
941     int totalFileCount = 0;
942     rpmfi fi;
943     sharedFileInfo shared, sharedList;
944     int numShared;
945     int nexti;
946     alKey lastKey;
947     fingerPrintCache fpc;
948     rpmps ps;
949     PSM_t psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
950     rpmtsi pi;  rpmte p;
951     rpmtsi qi;  rpmte q;
952     int xx;
953
954     /* FIXME: what if the same package is included in ts twice? */
955
956     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOSCRIPTS)
957         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
958     if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERS)
959         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransTriggers));
960
961     /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
962     if (rpmtsFlags(ts) & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
963         (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
964
965     ts->probs = rpmpsFree(ts->probs);
966     ts->probs = rpmpsCreate();
967
968     ts->ignoreSet = ignoreSet;
969     {   const char * currDir = currentDirectory();
970         rpmtsSetCurrDir(ts, currDir);
971         currDir = _free(currDir);
972     }
973
974     (void) rpmtsSetChrootDone(ts, 0);
975
976     {   int_32 tid = (int_32) time(NULL);
977         (void) rpmtsSetTid(ts, tid);
978     }
979
980     memset(psm, 0, sizeof(*psm));
981     psm->ts = rpmtsLink(ts, "tsRun");
982
983     /* Get available space on mounted file systems. */
984     xx = rpmtsInitDSI(ts);
985
986     /* ===============================================
987      * For packages being installed:
988      * - verify package arch/os.
989      * - verify package epoch:version-release is newer.
990      * - count files.
991      * For packages being removed:
992      * - count files.
993      */
994     ps = rpmtsProblems(ts);
995     /* The ordering doesn't matter here */
996     pi = rpmtsiInit(ts);
997     while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
998         rpmdbMatchIterator mi;
999         int fc;
1000
1001         if ((fi = rpmtsiFi(pi)) == NULL)
1002             continue;   /* XXX can't happen */
1003         fc = rpmfiFC(fi);
1004
1005         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_IGNOREARCH))
1006             if (!archOkay(rpmteA(p)))
1007                 rpmpsAppend(ps, RPMPROB_BADARCH,
1008                         rpmteNEVR(p), rpmteKey(p),
1009                         rpmteA(p), NULL,
1010                         NULL, 0);
1011
1012         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_IGNOREOS))
1013             if (!osOkay(rpmteO(p)))
1014                 rpmpsAppend(ps, RPMPROB_BADOS,
1015                         rpmteNEVR(p), rpmteKey(p),
1016                         rpmteO(p), NULL,
1017                         NULL, 0);
1018
1019         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_OLDPACKAGE)) {
1020             Header h;
1021             mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
1022             while ((h = rpmdbNextIterator(mi)) != NULL)
1023                 xx = ensureOlder(ts, p, h);
1024             mi = rpmdbFreeIterator(mi);
1025         }
1026
1027         /* XXX multilib should not display "already installed" problems */
1028         if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEPKG) && !rpmteMultiLib(p)) {
1029             mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
1030             xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT,
1031                                 rpmteV(p));
1032             xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
1033                                 rpmteR(p));
1034
1035             while (rpmdbNextIterator(mi) != NULL) {
1036                 rpmpsAppend(ps, RPMPROB_PKG_INSTALLED,
1037                         rpmteNEVR(p), rpmteKey(p),
1038                         NULL, NULL,
1039                         NULL, 0);
1040                 /*@innerbreak@*/ break;
1041             }
1042             mi = rpmdbFreeIterator(mi);
1043         }
1044
1045         /* Count no. of files (if any). */
1046         totalFileCount += fc;
1047
1048     }
1049     pi = rpmtsiFree(pi);
1050     ps = rpmpsFree(ps);
1051
1052     /* The ordering doesn't matter here */
1053     pi = rpmtsiInit(ts);
1054     while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
1055         int fc;
1056
1057         if ((fi = rpmtsiFi(pi)) == NULL)
1058             continue;   /* XXX can't happen */
1059         fc = rpmfiFC(fi);
1060
1061         totalFileCount += fc;
1062     }
1063     pi = rpmtsiFree(pi);
1064
1065     /* ===============================================
1066      * Initialize transaction element file info for package:
1067      */
1068
1069     /*
1070      * FIXME?: we'd be better off assembling one very large file list and
1071      * calling fpLookupList only once. I'm not sure that the speedup is
1072      * worth the trouble though.
1073      */
1074     pi = rpmtsiInit(ts);
1075     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1076         int fc;
1077
1078         if ((fi = rpmtsiFi(pi)) == NULL)
1079             continue;   /* XXX can't happen */
1080         fc = rpmfiFC(fi);
1081
1082 #ifdef  DYING   /* XXX W2DO? this is now done in rpmtsiFi, okay ??? */
1083         fi->magic = RPMFIMAGIC;
1084         fi->te = p;
1085 #endif
1086
1087         /*@-branchstate@*/
1088         switch (rpmteType(p)) {
1089         case TR_ADDED:
1090             fi->record = 0;
1091             /* Skip netshared paths, not our i18n files, and excluded docs */
1092             if (fc > 0)
1093                 skipFiles(ts, fi);
1094             /*@switchbreak@*/ break;
1095         case TR_REMOVED:
1096             fi->record = rpmteDBOffset(p);
1097             /*@switchbreak@*/ break;
1098         }
1099         /*@=branchstate@*/
1100
1101         fi->fps = (fc > 0 ? xmalloc(fc * sizeof(*fi->fps)) : NULL);
1102     }
1103     pi = rpmtsiFree(pi);
1104
1105     if (!rpmtsChrootDone(ts)) {
1106         const char * rootDir = rpmtsRootDir(ts);
1107         xx = chdir("/");
1108         /*@-superuser -noeffect @*/
1109         if (rootDir != NULL)
1110             xx = chroot(rootDir);
1111         /*@=superuser =noeffect @*/
1112         (void) rpmtsSetChrootDone(ts, 1);
1113     }
1114
1115     ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1116     fpc = fpCacheCreate(totalFileCount);
1117
1118     /* ===============================================
1119      * Add fingerprint for each file not skipped.
1120      */
1121     pi = rpmtsiInit(ts);
1122     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1123         int fc;
1124
1125         if ((fi = rpmtsiFi(pi)) == NULL)
1126             continue;   /* XXX can't happen */
1127         fc = rpmfiFC(fi);
1128
1129         fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fc, fi->fps);
1130         /*@-branchstate@*/
1131         fi = rpmfiInit(fi, 0);
1132         if (fi != NULL)         /* XXX lclint */
1133         while ((i = rpmfiNext(fi)) >= 0) {
1134             if (XFA_SKIPPING(fi->actions[i]))
1135                 /*@innercontinue@*/ continue;
1136             /*@-dependenttrans@*/
1137             htAddEntry(ts->ht, fi->fps + i, (void *) fi);
1138             /*@=dependenttrans@*/
1139         }
1140         /*@=branchstate@*/
1141     }
1142     pi = rpmtsiFree(pi);
1143
1144     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->orderCount,
1145         NULL, ts->notifyData));
1146
1147     /* ===============================================
1148      * Compute file disposition for each package in transaction set.
1149      */
1150     ps = rpmtsProblems(ts);
1151     pi = rpmtsiInit(ts);
1152     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1153         dbiIndexSet * matches;
1154         int knownBad;
1155         int fc;
1156
1157         if ((fi = rpmtsiFi(pi)) == NULL)
1158             continue;   /* XXX can't happen */
1159         fc = rpmfiFC(fi);
1160
1161         NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, rpmtsiOc(pi),
1162                         ts->orderCount, NULL, ts->notifyData));
1163
1164         if (fc == 0) continue;
1165
1166         /* Extract file info for all files in this package from the database. */
1167         matches = xcalloc(fc, sizeof(*matches));
1168         if (rpmdbFindFpList(rpmtsGetRdb(ts), fi->fps, matches, fc)) {
1169             psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1170             ps = rpmpsFree(ps);
1171             return 1;   /* XXX WTFO? */
1172         }
1173
1174         numShared = 0;
1175         fi = rpmfiInit(fi, 0);
1176         while ((i = rpmfiNext(fi)) >= 0)
1177             numShared += dbiIndexSetCount(matches[i]);
1178
1179         /* Build sorted file info list for this package. */
1180         shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1181
1182         fi = rpmfiInit(fi, 0);
1183         while ((i = rpmfiNext(fi)) >= 0) {
1184             /*
1185              * Take care not to mark files as replaced in packages that will
1186              * have been removed before we will get here.
1187              */
1188             for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1189                 int ro;
1190                 ro = dbiIndexRecordOffset(matches[i], j);
1191                 knownBad = 0;
1192                 qi = rpmtsiInit(ts);
1193                 while ((q = rpmtsiNext(qi, TR_REMOVED)) != NULL) {
1194                     if (ro == knownBad)
1195                         /*@innerbreak@*/ break;
1196                     if (rpmteDBOffset(q) == ro)
1197                         knownBad = ro;
1198                 }
1199                 qi = rpmtsiFree(qi);
1200
1201                 shared->pkgFileNum = i;
1202                 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1203                 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1204                 shared->isRemoved = (knownBad == ro);
1205                 shared++;
1206             }
1207             matches[i] = dbiFreeIndexSet(matches[i]);
1208         }
1209         numShared = shared - sharedList;
1210         shared->otherPkg = -1;
1211         matches = _free(matches);
1212
1213         /* Sort file info by other package index (otherPkg) */
1214         qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1215
1216         /* For all files from this package that are in the database ... */
1217         /*@-branchstate@*/
1218         for (i = 0; i < numShared; i = nexti) {
1219             int beingRemoved;
1220
1221             shared = sharedList + i;
1222
1223             /* Find the end of the files in the other package. */
1224             for (nexti = i + 1; nexti < numShared; nexti++) {
1225                 if (sharedList[nexti].otherPkg != shared->otherPkg)
1226                     /*@innerbreak@*/ break;
1227             }
1228
1229             /* Is this file from a package being removed? */
1230             beingRemoved = 0;
1231             if (ts->removedPackages != NULL)
1232             for (j = 0; j < ts->numRemovedPackages; j++) {
1233                 if (ts->removedPackages[j] != shared->otherPkg)
1234                     /*@innercontinue@*/ continue;
1235                 beingRemoved = 1;
1236                 /*@innerbreak@*/ break;
1237             }
1238
1239             /* Determine the fate of each file. */
1240             switch (rpmteType(p)) {
1241             case TR_ADDED:
1242                 xx = handleInstInstalledFiles(ts, p, fi, shared, nexti - i,
1243         !(beingRemoved || (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES)));
1244                 /*@switchbreak@*/ break;
1245             case TR_REMOVED:
1246                 if (!beingRemoved)
1247                     xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1248                 /*@switchbreak@*/ break;
1249             }
1250         }
1251         /*@=branchstate@*/
1252
1253         free(sharedList);
1254
1255         /* Update disk space needs on each partition for this package. */
1256         handleOverlappedFiles(ts, p, fi);
1257
1258         /* Check added package has sufficient space on each partition used. */
1259         switch (rpmteType(p)) {
1260         case TR_ADDED:
1261             rpmtsCheckDSIProblems(ts, p);
1262             /*@switchbreak@*/ break;
1263         case TR_REMOVED:
1264             /*@switchbreak@*/ break;
1265         }
1266     }
1267     pi = rpmtsiFree(pi);
1268     ps = rpmpsFree(ps);
1269
1270     if (rpmtsChrootDone(ts)) {
1271         const char * currDir = rpmtsCurrDir(ts);
1272         /*@-superuser -noeffect @*/
1273         xx = chroot(".");
1274         /*@=superuser =noeffect @*/
1275         (void) rpmtsSetChrootDone(ts, 0);
1276         if (currDir != NULL)
1277             xx = chdir(currDir);
1278     }
1279
1280     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->orderCount,
1281         NULL, ts->notifyData));
1282
1283     /* ===============================================
1284      * Free unused memory as soon as possible.
1285      */
1286     pi = rpmtsiInit(ts);
1287     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1288         if ((fi = rpmtsiFi(pi)) == NULL)
1289             continue;   /* XXX can't happen */
1290         if (rpmfiFC(fi) == 0)
1291             continue;
1292         fi->fps = _free(fi->fps);
1293     }
1294     pi = rpmtsiFree(pi);
1295
1296     fpc = fpCacheFree(fpc);
1297     ts->ht = htFree(ts->ht);
1298
1299     /* ===============================================
1300      * If unfiltered problems exist, free memory and return.
1301      */
1302     if ((rpmtsFlags(ts) & RPMTRANS_FLAG_BUILD_PROBS)
1303      || (ts->probs->numProblems &&
1304                 (okProbs != NULL || rpmpsTrim(ts->probs, okProbs)))
1305        )
1306     {
1307         if (psm->ts != NULL)
1308             psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1309         return ts->orderCount;
1310     }
1311
1312     /* ===============================================
1313      * Save removed files before erasing.
1314      */
1315     if (rpmtsFlags(ts) & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1316         pi = rpmtsiInit(ts);
1317         while ((p = rpmtsiNext(pi, 0)) != NULL) {
1318             fi = rpmtsiFi(pi);
1319             switch (rpmteType(p)) {
1320             case TR_ADDED:
1321                 /*@switchbreak@*/ break;
1322             case TR_REMOVED:
1323                 if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_REPACKAGE))
1324                     /*@switchbreak@*/ break;
1325                 psm->te = p;
1326                 psm->fi = rpmfiLink(fi, "tsRepackage");
1327                 xx = psmStage(psm, PSM_PKGSAVE);
1328                 (void) rpmfiUnlink(fi, "tsRepackage");
1329                 psm->fi = NULL;
1330                 psm->te = NULL;
1331                 /*@switchbreak@*/ break;
1332             }
1333         }
1334         pi = rpmtsiFree(pi);
1335     }
1336
1337     /* ===============================================
1338      * Install and remove packages.
1339      */
1340     lastKey = (alKey)-2;        /* erased packages have -1 */
1341     pi = rpmtsiInit(ts);
1342     /*@-branchstate@*/ /* FIX: fi reload needs work */
1343     while ((p = rpmtsiNext(pi, 0)) != NULL) {
1344         alKey pkgKey;
1345         int gotfd;
1346
1347         gotfd = 0;
1348         if ((fi = rpmtsiFi(pi)) == NULL)
1349             continue;   /* XXX can't happen */
1350         
1351         psm->te = p;
1352         psm->fi = rpmfiLink(fi, "tsInstall");
1353         switch (rpmteType(p)) {
1354         case TR_ADDED:
1355
1356             pkgKey = rpmteAddedKey(p);
1357
1358             rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", rpmteNEVR(p));
1359             p->h = NULL;
1360             /*@-type@*/ /* FIX: rpmte not opaque */
1361             {
1362                 /*@-noeffectuncon@*/ /* FIX: notify annotations */
1363                 p->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1364                                 rpmteKey(p), ts->notifyData);
1365                 /*@=noeffectuncon@*/
1366                 if (rpmteFd(p) != NULL) {
1367                     rpmRC rpmrc;
1368
1369                     rpmrc = rpmReadPackageFile(ts, rpmteFd(p),
1370                                 "rpmtsRun", &p->h);
1371
1372                     if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1373                         /*@-noeffectuncon@*/ /* FIX: notify annotations */
1374                         p->fd = ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1375                                         0, 0,
1376                                         rpmteKey(p), ts->notifyData);
1377                         /*@=noeffectuncon@*/
1378                         p->fd = NULL;
1379                         ourrc++;
1380                     }
1381                     if (rpmteFd(p) != NULL) gotfd = 1;
1382                 }
1383             }
1384             /*@=type@*/
1385
1386             if (rpmteFd(p) != NULL) {
1387                 {
1388 char * fstates = fi->fstates;
1389 fileAction * actions = fi->actions;
1390
1391 fi->fstates = NULL;
1392 fi->actions = NULL;
1393                     psm->fi = rpmfiFree(psm->fi, 1);
1394                     (void) rpmfiFree(fi, 0);
1395 /*@-usereleased@*/
1396 fi->magic = RPMFIMAGIC;
1397 fi->te = p;
1398 fi->record = 0;
1399                     (void) rpmfiNew(ts, fi, p->h, RPMTAG_BASENAMES, 1);
1400                     psm->fi = rpmfiLink(fi, "tsInstall");
1401 fi->fstates = _free(fi->fstates);
1402 fi->fstates = fstates;
1403 fi->actions = _free(fi->actions);
1404 fi->actions = actions;
1405 /*@=usereleased@*/
1406
1407                 }
1408                 if (rpmteMultiLib(p))
1409                     (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | RPMTRANS_FLAG_MULTILIB));
1410                 else
1411                     (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) & ~RPMTRANS_FLAG_MULTILIB));
1412
1413                 if (psmStage(psm, PSM_PKGINSTALL)) {
1414                     ourrc++;
1415                     lastKey = pkgKey;
1416                 }
1417                 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1418             } else {
1419                 ourrc++;
1420                 lastKey = pkgKey;
1421             }
1422
1423             p->h = headerFree(p->h, "TR_ADDED h free");
1424
1425             if (gotfd) {
1426                 /*@-noeffectuncon @*/ /* FIX: check rc */
1427                 (void) ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1428                         rpmteKey(p), ts->notifyData);
1429                 /*@=noeffectuncon @*/
1430                 /*@-type@*/
1431                 p->fd = NULL;
1432                 /*@=type@*/
1433             }
1434             /*@switchbreak@*/ break;
1435         case TR_REMOVED:
1436             rpmMessage(RPMMESS_DEBUG, "========== --- %s\n", rpmteNEVR(p));
1437             /* If install failed, then we shouldn't erase. */
1438             if (rpmteDependsOnKey(p) != lastKey) {
1439                 if (psmStage(psm, PSM_PKGERASE))
1440                     ourrc++;
1441             }
1442             /*@switchbreak@*/ break;
1443         }
1444         xx = rpmdbSync(rpmtsGetRdb(ts));
1445         (void) rpmfiUnlink(psm->fi, "tsInstall");
1446         psm->fi = NULL;
1447         psm->te = NULL;
1448 /*@-type@*/ /* FIX: p is almost opaque */
1449         p->fi = rpmfiFree(fi, 1);
1450 /*@=type@*/
1451     }
1452     /*@=branchstate@*/
1453     pi = rpmtsiFree(pi);
1454
1455     psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1456
1457     /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
1458     if (ourrc)
1459         return -1;
1460     else
1461         return 0;
1462     /*@=nullstate@*/
1463 }