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