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