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