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