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