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