- start hiding availablePackage data/methods in rpmal.c/rpmal.h.
[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 /*@access rpmTransactionSet@*/
56 /*@access TFI_t@*/
57 /*@access PSM_t@*/
58
59 /*@access availablePackage@*/
60 /*@access availableList@*/
61 /*@access transactionElement@*/
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                 if (ts->addedPackages->list) {
123                     availablePackage alp;
124                     alp = ts->addedPackages->list + ts->order[oc].u.addedIndex;
125                     *e = alp->key;
126                     /*@switchbreak@*/ break;
127                 }
128                 /*@fallthrough@*/
129             default:
130             case TR_REMOVED:
131                 /*@-mods@*/     /* FIX: double indirection. */
132                 *e = NULL;
133                 /*@=mods@*/
134                 /*@switchbreak@*/ break;
135             }
136         }
137     }
138     return rc;
139 }
140
141 /**
142  */
143 static int archOkay(Header h)
144         /*@*/
145 {
146     void * pkgArch;
147     int type, count;
148
149     /* make sure we're trying to install this on the proper architecture */
150     (void) headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
151 #ifndef DYING
152     if (type == RPM_INT8_TYPE) {
153         int_8 * pkgArchNum;
154         int archNum;
155
156         /* old arch handling */
157         rpmGetArchInfo(NULL, &archNum);
158         pkgArchNum = pkgArch;
159         if (archNum != *pkgArchNum) {
160             return 0;
161         }
162     } else
163 #endif
164     {
165         /* new arch handling */
166         if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
167             return 0;
168         }
169     }
170
171     return 1;
172 }
173
174 /**
175  */
176 static int osOkay(Header h)
177         /*@*/
178 {
179     void * pkgOs;
180     int type, count;
181
182     /* make sure we're trying to install this on the proper os */
183     (void) headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
184 #ifndef DYING
185     if (type == RPM_INT8_TYPE) {
186         /* v1 packages and v2 packages both used improper OS numbers, so just
187            deal with it hope things work */
188         return 1;
189     } else
190 #endif
191     {
192         /* new os handling */
193         if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
194             return 0;
195         }
196     }
197
198     return 1;
199 }
200
201 /**
202  */
203 static int sharedCmp(const void * one, const void * two)
204         /*@*/
205 {
206     const struct sharedFileInfo * a = one;
207     const struct sharedFileInfo * b = two;
208
209     if (a->otherPkg < b->otherPkg)
210         return -1;
211     else if (a->otherPkg > b->otherPkg)
212         return 1;
213
214     return 0;
215 }
216
217 /**
218  */
219 static fileAction decideFileFate(const char * dirName,
220                         const char * baseName, short dbMode,
221                         const char * dbMd5, const char * dbLink, short newMode,
222                         const char * newMd5, const char * newLink, int newFlags,
223                         rpmtransFlags transFlags)
224         /*@globals fileSystem @*/
225         /*@modifies fileSystem @*/
226 {
227     char buffer[1024];
228     const char * dbAttr, * newAttr;
229     fileTypes dbWhat, newWhat, diskWhat;
230     struct stat sb;
231     int i, rc;
232     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
233     char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
234
235     (void) stpcpy( stpcpy(filespec, dirName), baseName);
236
237     if (lstat(filespec, &sb)) {
238         /*
239          * The file doesn't exist on the disk. Create it unless the new
240          * package has marked it as missingok, or allfiles is requested.
241          */
242         if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
243            (newFlags & RPMFILE_MISSINGOK)) {
244             rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
245                         filespec);
246             return FA_SKIP;
247         } else {
248             return FA_CREATE;
249         }
250     }
251
252     diskWhat = whatis(sb.st_mode);
253     dbWhat = whatis(dbMode);
254     newWhat = whatis(newMode);
255
256     /* RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
257        them in older packages as well */
258     if (newWhat == XDIR) {
259         return FA_CREATE;
260     }
261
262     if (diskWhat != newWhat) {
263         return save;
264     } else if (newWhat != dbWhat && diskWhat != dbWhat) {
265         return save;
266     } else if (dbWhat != newWhat) {
267         return FA_CREATE;
268     } else if (dbWhat != LINK && dbWhat != REG) {
269         return FA_CREATE;
270     }
271
272     if (dbWhat == REG) {
273         rc = mdfile(filespec, buffer);
274
275         if (rc) {
276             /* assume the file has been removed, don't freak */
277             return FA_CREATE;
278         }
279         dbAttr = dbMd5;
280         newAttr = newMd5;
281     } else /* dbWhat == LINK */ {
282         memset(buffer, 0, sizeof(buffer));
283         i = readlink(filespec, buffer, sizeof(buffer) - 1);
284         if (i == -1) {
285             /* assume the file has been removed, don't freak */
286             return FA_CREATE;
287         }
288         dbAttr = dbLink;
289         newAttr = newLink;
290      }
291
292     /* this order matters - we'd prefer to CREATE the file if at all
293        possible in case something else (like the timestamp) has changed */
294
295     if (!strcmp(dbAttr, buffer)) {
296         /* this config file has never been modified, so just replace it */
297         return FA_CREATE;
298     }
299
300     if (!strcmp(dbAttr, newAttr)) {
301         /* this file is the same in all versions of this package */
302         return FA_SKIP;
303     }
304
305     /*
306      * The config file on the disk has been modified, but
307      * the ones in the two packages are different. It would
308      * be nice if RPM was smart enough to at least try and
309      * merge the difference ala CVS, but...
310      */
311     return save;
312 }
313
314 /**
315  */
316 static int filecmp(short mode1, const char * md51, const char * link1,
317                    short mode2, const char * md52, const char * link2)
318         /*@*/
319 {
320     fileTypes what1 = whatis(mode1);
321     fileTypes what2 = whatis(mode2);
322
323     if (what1 != what2) return 1;
324
325     if (what1 == LINK)
326         return strcmp(link1, link2);
327     else if (what1 == REG)
328         return strcmp(md51, md52);
329
330     return 0;
331 }
332
333 /**
334  */
335 /* XXX only ts->{probs,rpmdb} modified */
336 static int handleInstInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
337                 struct sharedFileInfo * shared,
338                 int sharedCount, int reportConflicts)
339         /*@globals fileSystem @*/
340         /*@modifies ts, fi, fileSystem @*/
341 {
342     HGE_t hge = fi->hge;
343     HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
344     rpmtransFlags transFlags = ts->transFlags;
345     rpmTagType oltype, omtype;
346     Header h;
347     int i;
348     const char ** otherMd5s;
349     const char ** otherLinks;
350     const char * otherStates;
351     uint_32 * otherFlags;
352     uint_32 * otherSizes;
353     uint_16 * otherModes;
354     int numReplaced = 0;
355     int xx;
356
357     rpmdbMatchIterator mi;
358
359     mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
360     h = rpmdbNextIterator(mi);
361     if (h == NULL) {
362         mi = rpmdbFreeIterator(mi);
363         return 1;
364     }
365
366     xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
367     xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
368     xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
369     xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
370     xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
371     xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
372
373     fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
374
375     for (i = 0; i < sharedCount; i++, shared++) {
376         int otherFileNum, fileNum;
377         otherFileNum = shared->otherFileNum;
378         fileNum = shared->pkgFileNum;
379
380         /* XXX another tedious segfault, assume file state normal. */
381         if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
382             continue;
383
384         if (XFA_SKIPPING(fi->actions[fileNum]))
385             continue;
386
387         if (filecmp(otherModes[otherFileNum],
388                         otherMd5s[otherFileNum],
389                         otherLinks[otherFileNum],
390                         fi->fmodes[fileNum],
391                         fi->fmd5s[fileNum],
392                         fi->flinks[fileNum])) {
393             if (reportConflicts)
394                 rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT, fi->ap,
395                         fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum], h, 0);
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                 rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap,
600                                 filespec, NULL, recs[otherPkgNum]->ap->h, 0);
601             }
602
603             /* Try to get the disk accounting correct even if a conflict. */
604             fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
605
606             if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
607                 /* Here is an overlapped  pre-existing config file. */
608                 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
609                         ? FA_ALTNAME : FA_SKIP;
610             } else {
611                 fi->actions[i] = FA_CREATE;
612             }
613           } /*@switchbreak@*/ break;
614         case TR_REMOVED:
615             if (otherPkgNum >= 0) {
616                 /* Here is an overlapped added file we don't want to nuke. */
617                 if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
618                     /* On updates, don't remove files. */
619                     fi->actions[i] = FA_SKIP;
620                     /*@switchbreak@*/ break;
621                 }
622                 /* Here is an overlapped removed file: skip in previous. */
623                 recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
624             }
625             if (XFA_SKIPPING(fi->actions[i]))
626                 /*@switchbreak@*/ break;
627             if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
628                 /*@switchbreak@*/ break;
629             if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
630                 fi->actions[i] = FA_ERASE;
631                 /*@switchbreak@*/ break;
632             }
633                 
634             /* Here is a pre-existing modified config file that needs saving. */
635             {   char mdsum[50];
636                 if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
637                     fi->actions[i] = FA_BACKUP;
638                     /*@switchbreak@*/ break;
639                 }
640             }
641             fi->actions[i] = FA_ERASE;
642             /*@switchbreak@*/ break;
643         }
644
645         if (ds) {
646             uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
647
648             switch (fi->actions[i]) {
649               case FA_BACKUP:
650               case FA_SAVE:
651               case FA_ALTNAME:
652                 ds->ineeded++;
653                 ds->bneeded += s;
654                 /*@switchbreak@*/ break;
655
656             /*
657              * FIXME: If two packages share a file (same md5sum), and
658              * that file is being replaced on disk, will ds->bneeded get
659              * decremented twice? Quite probably!
660              */
661               case FA_CREATE:
662                 ds->bneeded += s;
663                 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
664                 /*@switchbreak@*/ break;
665
666               case FA_ERASE:
667                 ds->ineeded--;
668                 ds->bneeded -= s;
669                 /*@switchbreak@*/ break;
670
671               default:
672                 /*@switchbreak@*/ break;
673             }
674
675             ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
676         }
677     }
678     if (filespec) free(filespec);
679 }
680
681 /**
682  */
683 static int ensureOlder(rpmTransactionSet ts, availablePackage alp, Header old)
684         /*@modifies ts, alp @*/
685 {
686     int result, rc = 0;
687
688     if (old == NULL) return 1;
689
690     result = rpmVersionCompare(old, alp->h);
691     if (result <= 0)
692         rc = 0;
693     else if (result > 0) {
694         rc = 1;
695         rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE, alp,
696                         NULL, NULL, old, 0);
697     }
698
699     return rc;
700 }
701
702 /**
703  */
704 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
705         /*@globals rpmGlobalMacroContext @*/
706         /*@modifies fi, rpmGlobalMacroContext @*/
707 {
708     int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
709     char ** netsharedPaths = NULL;
710     const char ** languages;
711     const char * dn, * bn;
712     int dnlen, bnlen, ix;
713     const char * s;
714     int * drc;
715     char * dff;
716     int i, j;
717
718     if (!noDocs)
719         noDocs = rpmExpandNumeric("%{_excludedocs}");
720
721     {   const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
722         /*@-branchstate@*/
723         if (tmpPath && *tmpPath != '%')
724             netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
725         /*@=branchstate@*/
726         tmpPath = _free(tmpPath);
727     }
728
729     s = rpmExpand("%{_install_langs}", NULL);
730     /*@-branchstate@*/
731     if (!(s && *s != '%'))
732         s = _free(s);
733     if (s) {
734         languages = (const char **) splitString(s, strlen(s), ':');
735         s = _free(s);
736     } else
737         languages = NULL;
738     /*@=branchstate@*/
739
740     /* Compute directory refcount, skip directory if now empty. */
741     drc = alloca(fi->dc * sizeof(*drc));
742     memset(drc, 0, fi->dc * sizeof(*drc));
743     dff = alloca(fi->dc * sizeof(*dff));
744     memset(dff, 0, fi->dc * sizeof(*dff));
745
746     for (i = 0; i < fi->fc; i++) {
747         char **nsp;
748
749         bn = fi->bnl[i];
750         bnlen = strlen(bn);
751         ix = fi->dil[i];
752         dn = fi->dnl[ix];
753         dnlen = strlen(dn);
754
755         drc[ix]++;
756
757         /* Don't bother with skipped files */
758         if (XFA_SKIPPING(fi->actions[i])) {
759             drc[ix]--;
760             continue;
761         }
762
763         /*
764          * Skip net shared paths.
765          * Net shared paths are not relative to the current root (though
766          * they do need to take package relocations into account).
767          */
768         for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
769             int len;
770
771             len = strlen(*nsp);
772             if (dnlen >= len) {
773                 if (strncmp(dn, *nsp, len))
774                     /*@innercontinue@*/ continue;
775                 /* Only directories or complete file paths can be net shared */
776                 if (!(dn[len] == '/' || dn[len] == '\0'))
777                     /*@innercontinue@*/ continue;
778             } else {
779                 if (len < (dnlen + bnlen))
780                     /*@innercontinue@*/ continue;
781                 if (strncmp(dn, *nsp, dnlen))
782                     /*@innercontinue@*/ continue;
783                 if (strncmp(bn, (*nsp) + dnlen, bnlen))
784                     /*@innercontinue@*/ continue;
785                 len = dnlen + bnlen;
786                 /* Only directories or complete file paths can be net shared */
787                 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
788                     /*@innercontinue@*/ continue;
789             }
790
791             /*@innerbreak@*/ break;
792         }
793
794         if (nsp && *nsp) {
795             drc[ix]--;  dff[ix] = 1;
796             fi->actions[i] = FA_SKIPNETSHARED;
797             continue;
798         }
799
800         /*
801          * Skip i18n language specific files.
802          */
803         if (fi->flangs && languages && *fi->flangs[i]) {
804             const char **lang, *l, *le;
805             for (lang = languages; *lang != NULL; lang++) {
806                 if (!strcmp(*lang, "all"))
807                     /*@innerbreak@*/ break;
808                 for (l = fi->flangs[i]; *l != '\0'; l = le) {
809                     for (le = l; *le != '\0' && *le != '|'; le++)
810                         {};
811                     if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
812                         /*@innerbreak@*/ break;
813                     if (*le == '|') le++;       /* skip over | */
814                 }
815                 if (*l != '\0')
816                     /*@innerbreak@*/ break;
817             }
818             if (*lang == NULL) {
819                 drc[ix]--;      dff[ix] = 1;
820                 fi->actions[i] = FA_SKIPNSTATE;
821                 continue;
822             }
823         }
824
825         /*
826          * Skip documentation if requested.
827          */
828         if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
829             drc[ix]--;  dff[ix] = 1;
830             fi->actions[i] = FA_SKIPNSTATE;
831             continue;
832         }
833     }
834
835     /* Skip (now empty) directories that had skipped files. */
836     for (j = 0; j < fi->dc; j++) {
837
838         if (drc[j]) continue;   /* dir still has files. */
839         if (!dff[j]) continue;  /* dir was not emptied here. */
840         
841         /* Find parent directory and basename. */
842         dn = fi->dnl[j];        dnlen = strlen(dn) - 1;
843         bn = dn + dnlen;        bnlen = 0;
844         while (bn > dn && bn[-1] != '/') {
845                 bnlen++;
846                 dnlen--;
847                 bn--;
848         }
849
850         /* If explicitly included in the package, skip the directory. */
851         for (i = 0; i < fi->fc; i++) {
852             const char * dir;
853
854             if (XFA_SKIPPING(fi->actions[i]))
855                 /*@innercontinue@*/ continue;
856             if (whatis(fi->fmodes[i]) != XDIR)
857                 /*@innercontinue@*/ continue;
858             dir = fi->dnl[fi->dil[i]];
859             if (strlen(dir) != dnlen)
860                 /*@innercontinue@*/ continue;
861             if (strncmp(dir, dn, dnlen))
862                 /*@innercontinue@*/ continue;
863             if (strlen(fi->bnl[i]) != bnlen)
864                 /*@innercontinue@*/ continue;
865             if (strncmp(fi->bnl[i], bn, bnlen))
866                 /*@innercontinue@*/ continue;
867             rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
868             fi->actions[i] = FA_SKIPNSTATE;
869             /*@innerbreak@*/ break;
870         }
871     }
872
873     if (netsharedPaths) freeSplitString(netsharedPaths);
874 #ifdef  DYING   /* XXX freeFi will deal with this later. */
875     fi->flangs = _free(fi->flangs);
876 #endif
877     if (languages) freeSplitString((char **)languages);
878 }
879
880 /**
881  * Iterator across transaction elements, forward on install, backward on erase.
882  */
883 struct tsIterator_s {
884 /*@refcounted@*/ rpmTransactionSet ts;  /*!< transaction set. */
885     int reverse;                        /*!< reversed traversal? */
886     int ocsave;                         /*!< last returned iterator index. */
887     int oc;                             /*!< iterator index. */
888 };
889
890 /**
891  * Return transaction element order count.
892  * @param a             transaction element iterator
893  * @return              element order count
894  */
895 static int tsGetOc(void * a)
896         /*@*/
897 {
898     struct tsIterator_s * iter = a;
899     int oc = iter->ocsave;
900     return oc;
901 }
902
903 /**
904  * Return transaction element available package pointer.
905  * @param a             transaction element iterator
906  * @return              available package pointer
907  */
908 static /*@dependent@*/ availablePackage tsGetAlp(void * a)
909         /*@*/
910 {
911     struct tsIterator_s * iter = a;
912     availablePackage alp = NULL;
913     int oc = iter->ocsave;
914
915     /*@-branchstate@*/
916     if (oc != -1) {
917         rpmTransactionSet ts = iter->ts;
918         TFI_t fi = ts->flList + oc;
919         if (ts->addedPackages->list && fi->type == TR_ADDED)
920             alp = ts->addedPackages->list + ts->order[oc].u.addedIndex;
921     }
922     /*@=branchstate@*/
923     return alp;
924 }
925
926 /**
927  * Destroy transaction element iterator.
928  * @param a             transaction element iterator
929  * @return              NULL always
930  */
931 static /*@null@*/ void * tsFreeIterator(/*@only@*//*@null@*/ void * a)
932         /*@*/
933 {
934     struct tsIterator_s * iter = a;
935     if (iter)
936         iter->ts = rpmtsUnlink(iter->ts, "tsIterator");
937     return _free(a);
938 }
939
940 /**
941  * Create transaction element iterator.
942  * @param ts            transaction set
943  * @return              transaction element iterator
944  */
945 static void * tsInitIterator(rpmTransactionSet ts)
946         /*@modifies ts @*/
947 {
948     struct tsIterator_s * iter = NULL;
949
950     iter = xcalloc(1, sizeof(*iter));
951     iter->ts = rpmtsLink(ts, "tsIterator");
952     iter->reverse = ((ts->transFlags & RPMTRANS_FLAG_REVERSE) ? 1 : 0);
953     iter->oc = (iter->reverse ? (ts->orderCount - 1) : 0);
954     iter->ocsave = iter->oc;
955     return iter;
956 }
957
958 /**
959  * Return next transaction element's file info.
960  * @param a             file info iterator
961  * @return              next index, -1 on termination
962  */
963 static TFI_t tsNextIterator(void * a)
964         /*@*/
965 {
966     struct tsIterator_s * iter = a;
967     TFI_t fi = NULL;
968     int oc = -1;
969
970     if (iter->reverse) {
971         if (iter->oc >= 0)                      oc = iter->oc--;
972     } else {
973         if (iter->oc < iter->ts->orderCount)    oc = iter->oc++;
974     }
975     iter->ocsave = oc;
976     if (oc != -1)
977         fi = iter->ts->flList + oc;
978     return fi;
979 }
980
981 #define NOTIFY(_ts, _al)        if ((_ts)->notify) (void) (_ts)->notify _al
982
983 int rpmRunTransactions( rpmTransactionSet ts,
984                         rpmCallbackFunction notify, rpmCallbackData notifyData,
985                         rpmProblemSet okProbs, rpmProblemSet * newProbs,
986                         rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
987 {
988     int i, j;
989     int ourrc = 0;
990     availablePackage alp;
991     int totalFileCount = 0;
992     TFI_t fi;
993     struct diskspaceInfo * dip;
994     struct sharedFileInfo * shared, * sharedList;
995     int numShared;
996     int nexti;
997     int lastFailed;
998     int oc;
999     fingerPrintCache fpc;
1000     struct psm_s psmbuf;
1001     PSM_t psm = &psmbuf;
1002     void * tsi;
1003     int xx;
1004 int keep_header = 1;    /* XXX rpmProblemSetAppend prevents dumping headers. */
1005
1006     /* FIXME: what if the same package is included in ts twice? */
1007
1008     ts->transFlags = transFlags;
1009     if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
1010         ts->transFlags |= (_noTransScripts | _noTransTriggers);
1011     if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
1012         ts->transFlags |= _noTransTriggers;
1013
1014     /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
1015     if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
1016         ts->transFlags |= (_noTransScripts | _noTransTriggers);
1017
1018     ts->notify = notify;
1019     ts->notifyData = notifyData;
1020     /*@-assignexpose@*/
1021     ts->probs = *newProbs = rpmProblemSetCreate();
1022     /*@=assignexpose@*/
1023     ts->ignoreSet = ignoreSet;
1024     ts->currDir = _free(ts->currDir);
1025     ts->currDir = currentDirectory();
1026     ts->chrootDone = 0;
1027     if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1028     ts->id = (int_32) time(NULL);
1029
1030     memset(psm, 0, sizeof(*psm));
1031     /*@-assignexpose@*/
1032     psm->ts = rpmtsLink(ts, "tsRun");
1033     /*@=assignexpose@*/
1034
1035     /* Get available space on mounted file systems. */
1036     if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
1037                 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
1038         struct stat sb;
1039
1040         ts->di = _free(ts->di);
1041         dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
1042
1043         for (i = 0; (i < ts->filesystemCount) && dip; i++) {
1044 #if STATFS_IN_SYS_STATVFS
1045             struct statvfs sfb;
1046             memset(&sfb, 0, sizeof(sfb));
1047             if (statvfs(ts->filesystems[i], &sfb))
1048 #else
1049             struct statfs sfb;
1050 #  if STAT_STATFS4
1051 /* This platform has the 4-argument version of the statfs call.  The last two
1052  * should be the size of struct statfs and 0, respectively.  The 0 is the
1053  * filesystem type, and is always 0 when statfs is called on a mounted
1054  * filesystem, as we're doing.
1055  */
1056             memset(&sfb, 0, sizeof(sfb));
1057             if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
1058 #  else
1059             memset(&sfb, 0, sizeof(sfb));
1060             if (statfs(ts->filesystems[i], &sfb))
1061 #  endif
1062 #endif
1063             {
1064                 dip = NULL;
1065             } else {
1066                 ts->di[i].bsize = sfb.f_bsize;
1067                 ts->di[i].bneeded = 0;
1068                 ts->di[i].ineeded = 0;
1069 #ifdef STATFS_HAS_F_BAVAIL
1070                 ts->di[i].bavail = sfb.f_bavail;
1071 #else
1072 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1073  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
1074  * it's about all we can do.
1075  */
1076                 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
1077 #endif
1078                 /* XXX Avoid FAT and other file systems that have not inodes. */
1079                 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1080                                 ? sfb.f_ffree : -1;
1081
1082                 xx = stat(ts->filesystems[i], &sb);
1083                 ts->di[i].dev = sb.st_dev;
1084             }
1085         }
1086
1087         if (dip) ts->di[i].bsize = 0;
1088     }
1089
1090     /* ===============================================
1091      * For packages being installed:
1092      * - verify package arch/os.
1093      * - verify package epoch:version-release is newer.
1094      * - count files.
1095      * For packages being removed:
1096      * - count files.
1097      */
1098     /* The ordering doesn't matter here */
1099     if (ts->addedPackages->list != NULL)
1100     for (alp = ts->addedPackages->list;
1101         (alp - ts->addedPackages->list) < ts->addedPackages->size;
1102         alp++)
1103     {
1104         if (!archOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1105             rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH, alp,
1106                         NULL, NULL, NULL, 0);
1107
1108         if (!osOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1109             rpmProblemSetAppend(ts->probs, RPMPROB_BADOS, alp,
1110                         NULL, NULL, NULL, 0);
1111
1112         if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1113             rpmdbMatchIterator mi;
1114             Header oldH;
1115             mi = rpmtsInitIterator(ts, RPMTAG_NAME, alp->name, 0);
1116             while ((oldH = rpmdbNextIterator(mi)) != NULL)
1117                 xx = ensureOlder(ts, alp, oldH);
1118             mi = rpmdbFreeIterator(mi);
1119         }
1120
1121         /* XXX multilib should not display "already installed" problems */
1122         if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !alp->multiLib) {
1123             rpmdbMatchIterator mi;
1124             mi = rpmtsInitIterator(ts, RPMTAG_NAME, alp->name, 0);
1125             xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
1126                         RPMMIRE_DEFAULT, alp->version);
1127             xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
1128                         RPMMIRE_DEFAULT, alp->release);
1129
1130             while (rpmdbNextIterator(mi) != NULL) {
1131                 rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED, alp,
1132                         NULL, NULL, NULL, 0);
1133                 /*@innerbreak@*/ break;
1134             }
1135             mi = rpmdbFreeIterator(mi);
1136         }
1137
1138         totalFileCount += alp->filesCount;
1139
1140     }
1141
1142     /* FIXME: it seems a bit silly to read in all of these headers twice */
1143     /* The ordering doesn't matter here */
1144     if (ts->numRemovedPackages > 0) {
1145         rpmdbMatchIterator mi;
1146         Header h;
1147         int fileCount;
1148
1149         mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
1150         xx = rpmdbAppendIterator(mi, ts->removedPackages, ts->numRemovedPackages);
1151         while ((h = rpmdbNextIterator(mi)) != NULL) {
1152             if (headerGetEntry(h, RPMTAG_BASENAMES, NULL, NULL, &fileCount))
1153                 totalFileCount += fileCount;
1154         }
1155         mi = rpmdbFreeIterator(mi);
1156     }
1157
1158     /* ===============================================
1159      * Initialize file list:
1160      */
1161     ts->flEntries = ts->addedPackages->size + ts->numRemovedPackages;
1162     ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
1163
1164     /*
1165      * FIXME?: we'd be better off assembling one very large file list and
1166      * calling fpLookupList only once. I'm not sure that the speedup is
1167      * worth the trouble though.
1168      */
1169     tsi = tsInitIterator(ts);
1170     while ((fi = tsNextIterator(tsi)) != NULL) {
1171         oc = tsGetOc(tsi);
1172         fi->magic = TFIMAGIC;
1173
1174         /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1175         fi->type = ts->order[oc].type;
1176         /*@-branchstate@*/
1177         switch (fi->type) {
1178         case TR_ADDED:
1179             /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
1180             fi->ap = tsGetAlp(tsi);
1181             fi->record = 0;
1182             loadFi(ts, fi, fi->ap->h, keep_header);
1183 /* XXX free fi->ap->h here if/when possible */
1184             if (fi->fc == 0)
1185                 continue;
1186
1187             /* Skip netshared paths, not our i18n files, and excluded docs */
1188             skipFiles(ts, fi);
1189             /*@switchbreak@*/ break;
1190         case TR_REMOVED:
1191             fi->ap = NULL;
1192             fi->record = ts->order[oc].u.removed.dboffset;
1193             /* Retrieve erased package header from the database. */
1194             {   rpmdbMatchIterator mi;
1195
1196                 mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
1197                                 &fi->record, sizeof(fi->record));
1198                 if ((fi->h = rpmdbNextIterator(mi)) != NULL)
1199                     fi->h = headerLink(fi->h);
1200                 mi = rpmdbFreeIterator(mi);
1201             }
1202             if (fi->h == NULL) {
1203                 /* ACK! */
1204                 continue;
1205             }
1206             /* XXX header arg unused. */
1207             loadFi(ts, fi, fi->h, 0);
1208             /*@switchbreak@*/ break;
1209         }
1210         /*@=branchstate@*/
1211
1212         if (fi->fc)
1213             fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
1214     }
1215     tsi = tsFreeIterator(tsi);
1216
1217     if (!ts->chrootDone) {
1218         xx = chdir("/");
1219         /*@-superuser -noeffect @*/
1220         xx = chroot(ts->rootDir);
1221         /*@=superuser =noeffect @*/
1222         ts->chrootDone = 1;
1223         if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1224         /*@-onlytrans@*/
1225         /*@-mods@*/
1226         chroot_prefix = ts->rootDir;
1227         /*@=mods@*/
1228         /*@=onlytrans@*/
1229     }
1230
1231     ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1232     fpc = fpCacheCreate(totalFileCount);
1233
1234     /* ===============================================
1235      * Add fingerprint for each file not skipped.
1236      */
1237     tsi = tsInitIterator(ts);
1238     while ((fi = tsNextIterator(tsi)) != NULL) {
1239         fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
1240         for (i = 0; i < fi->fc; i++) {
1241             if (XFA_SKIPPING(fi->actions[i]))
1242                 /*@innercontinue@*/ continue;
1243             /*@-dependenttrans@*/
1244             htAddEntry(ts->ht, fi->fps + i, fi);
1245             /*@=dependenttrans@*/
1246         }
1247     }
1248     tsi = tsFreeIterator(tsi);
1249
1250     /*@-noeffectuncon @*/ /* FIX: check rc */
1251     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
1252         NULL, ts->notifyData));
1253     /*@=noeffectuncon@*/
1254
1255     /* ===============================================
1256      * Compute file disposition for each package in transaction set.
1257      */
1258     tsi = tsInitIterator(ts);
1259     while ((fi = tsNextIterator(tsi)) != NULL) {
1260         dbiIndexSet * matches;
1261         int knownBad;
1262
1263         /*@-noeffectuncon @*/ /* FIX: check rc */
1264         NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
1265                         ts->flEntries, NULL, ts->notifyData));
1266         /*@=noeffectuncon@*/
1267
1268         if (fi->fc == 0) continue;
1269
1270         /* Extract file info for all files in this package from the database. */
1271         matches = xcalloc(fi->fc, sizeof(*matches));
1272         if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc)) {
1273             psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1274             return 1;   /* XXX WTFO? */
1275         }
1276
1277         numShared = 0;
1278         for (i = 0; i < fi->fc; i++)
1279             numShared += dbiIndexSetCount(matches[i]);
1280
1281         /* Build sorted file info list for this package. */
1282         shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1283         for (i = 0; i < fi->fc; i++) {
1284             /*
1285              * Take care not to mark files as replaced in packages that will
1286              * have been removed before we will get here.
1287              */
1288             for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1289                 int k, ro;
1290                 ro = dbiIndexRecordOffset(matches[i], j);
1291                 knownBad = 0;
1292                 for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
1293                     switch (ts->order[k].type) {
1294                     case TR_REMOVED:
1295                         if (ts->order[k].u.removed.dboffset == ro)
1296                             knownBad = ro;
1297                         /*@switchbreak@*/ break;
1298                     case TR_ADDED:
1299                         /*@switchbreak@*/ break;
1300                     }
1301                 }
1302
1303                 shared->pkgFileNum = i;
1304                 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1305                 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1306                 shared->isRemoved = (knownBad == ro);
1307                 shared++;
1308             }
1309             matches[i] = dbiFreeIndexSet(matches[i]);
1310         }
1311         numShared = shared - sharedList;
1312         shared->otherPkg = -1;
1313         matches = _free(matches);
1314
1315         /* Sort file info by other package index (otherPkg) */
1316         qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1317
1318         /* For all files from this package that are in the database ... */
1319         for (i = 0; i < numShared; i = nexti) {
1320             int beingRemoved;
1321
1322             shared = sharedList + i;
1323
1324             /* Find the end of the files in the other package. */
1325             for (nexti = i + 1; nexti < numShared; nexti++) {
1326                 if (sharedList[nexti].otherPkg != shared->otherPkg)
1327                     /*@innerbreak@*/ break;
1328             }
1329
1330             /* Is this file from a package being removed? */
1331             beingRemoved = 0;
1332             for (j = 0; j < ts->numRemovedPackages; j++) {
1333                 if (ts->removedPackages[j] != shared->otherPkg)
1334                     /*@innercontinue@*/ continue;
1335                 beingRemoved = 1;
1336                 /*@innerbreak@*/ break;
1337             }
1338
1339             /* Determine the fate of each file. */
1340             switch (fi->type) {
1341             case TR_ADDED:
1342                 xx = handleInstInstalledFiles(ts, fi, shared, nexti - i,
1343         !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1344                 /*@switchbreak@*/ break;
1345             case TR_REMOVED:
1346                 if (!beingRemoved)
1347                     xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1348                 /*@switchbreak@*/ break;
1349             }
1350         }
1351
1352         free(sharedList);
1353
1354         /* Update disk space needs on each partition for this package. */
1355         handleOverlappedFiles(ts, fi);
1356
1357         /* Check added package has sufficient space on each partition used. */
1358         switch (fi->type) {
1359         case TR_ADDED:
1360             if (!(ts->di && fi->fc))
1361                 /*@switchbreak@*/ break;
1362             for (i = 0; i < ts->filesystemCount; i++) {
1363
1364                 dip = ts->di + i;
1365
1366                 /* XXX Avoid FAT and other file systems that have not inodes. */
1367                 if (dip->iavail <= 0)
1368                     /*@innercontinue@*/ continue;
1369
1370                 if (adj_fs_blocks(dip->bneeded) > dip->bavail)
1371                     rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE, fi->ap,
1372                                 ts->filesystems[i], NULL, NULL,
1373                    (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1374
1375                 if (adj_fs_blocks(dip->ineeded) > dip->iavail)
1376                     rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES, fi->ap,
1377                                 ts->filesystems[i], NULL, NULL,
1378                     (adj_fs_blocks(dip->ineeded) - dip->iavail));
1379             }
1380             /*@switchbreak@*/ break;
1381         case TR_REMOVED:
1382             /*@switchbreak@*/ break;
1383         }
1384     }
1385     tsi = tsFreeIterator(tsi);
1386
1387     if (ts->chrootDone) {
1388         /*@-superuser -noeffect @*/
1389         xx = chroot(".");
1390         /*@=superuser =noeffect @*/
1391         ts->chrootDone = 0;
1392         if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1393         /*@-mods@*/
1394         chroot_prefix = NULL;
1395         /*@=mods@*/
1396         xx = chdir(ts->currDir);
1397     }
1398
1399     /*@-noeffectuncon @*/ /* FIX: check rc */
1400     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->flEntries,
1401         NULL, ts->notifyData));
1402     /*@=noeffectuncon @*/
1403
1404     /* ===============================================
1405      * Free unused memory as soon as possible.
1406      */
1407
1408     tsi = tsInitIterator(ts);
1409     while ((fi = tsNextIterator(tsi)) != NULL) {
1410         if (fi->fc == 0)
1411             continue;
1412         fi->fps = _free(fi->fps);
1413     }
1414     tsi = tsFreeIterator(tsi);
1415
1416     fpCacheFree(fpc);
1417     htFree(ts->ht);
1418     ts->ht = NULL;
1419
1420     /* ===============================================
1421      * If unfiltered problems exist, free memory and return.
1422      */
1423     if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
1424      || (ts->probs->numProblems &&
1425                 (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
1426        )
1427     {
1428         *newProbs = ts->probs;
1429
1430         ts->flList = freeFl(ts, ts->flList);
1431         ts->flEntries = 0;
1432         /*@-nullstate@*/
1433         psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1434         return ts->orderCount;
1435         /*@=nullstate@*/
1436     }
1437
1438     /* ===============================================
1439      * Save removed files before erasing.
1440      */
1441     if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1442         tsi = tsInitIterator(ts);
1443         while ((fi = tsNextIterator(tsi)) != NULL) {
1444             switch (fi->type) {
1445             case TR_ADDED:
1446                 /*@switchbreak@*/ break;
1447             case TR_REMOVED:
1448                 if (ts->transFlags & RPMTRANS_FLAG_REPACKAGE) {
1449                     psm->fi = rpmfiLink(fi, "tsRepackage");
1450                     xx = psmStage(psm, PSM_PKGSAVE);
1451                     (void) rpmfiUnlink(fi, "tsRepackage");
1452                     psm->fi = NULL;
1453                 }
1454                 /*@switchbreak@*/ break;
1455             }
1456         }
1457         tsi = tsFreeIterator(tsi);
1458     }
1459
1460     /* ===============================================
1461      * Install and remove packages.
1462      */
1463
1464     lastFailed = -2;    /* erased packages have -1 */
1465     tsi = tsInitIterator(ts);
1466     /*@-branchstate@*/ /* FIX: fi reload needs work */
1467     while ((fi = tsNextIterator(tsi)) != NULL) {
1468         Header h;
1469         int gotfd;
1470
1471         gotfd = 0;
1472         psm->fi = rpmfiLink(fi, "tsInstall");
1473         switch (fi->type) {
1474         case TR_ADDED:
1475             alp = tsGetAlp(tsi);
1476 assert(alp == fi->ap);
1477             i = alp - ts->addedPackages->list;
1478
1479             rpmMessage(RPMMESS_DEBUG, "========== +++ %s-%s-%s\n",
1480                         fi->name, fi->version, fi->release);
1481             h = (fi->h ? headerLink(fi->h) : NULL);
1482             /*@-branchstate@*/
1483             if (alp->fd == NULL) {
1484                 alp->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1485                             alp->key, ts->notifyData);
1486                 if (alp->fd) {
1487                     rpmRC rpmrc;
1488
1489                     h = headerFree(h);
1490
1491                     /*@-mustmod@*/      /* LCL: segfault */
1492                     rpmrc = rpmReadPackageFile(ts, alp->fd,
1493                                 "rpmRunTransactions", &h);
1494                     /*@=mustmod@*/
1495
1496                     if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1497                         /*@-noeffectuncon @*/ /* FIX: check rc */
1498                         (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1499                                         0, 0, alp->key, ts->notifyData);
1500                         /*@=noeffectuncon @*/
1501                         alp->fd = NULL;
1502                         ourrc++;
1503                     } else if (fi->h != NULL) {
1504                         Header foo = relocateFileList(ts, fi, alp, h, NULL);
1505                         h = headerFree(h);
1506                         h = headerLink(foo);
1507                         foo = headerFree(foo);
1508                     }
1509                     if (alp->fd) gotfd = 1;
1510                 }
1511             }
1512             /*@=branchstate@*/
1513
1514             if (alp->fd) {
1515                 Header hsave = NULL;
1516
1517                 if (fi->h) {
1518                     hsave = headerLink(fi->h);
1519                     fi->h = headerFree(fi->h);
1520                     fi->h = headerLink(h);
1521                 } else {
1522 char * fstates = fi->fstates;
1523 fileAction * actions = fi->actions;
1524 fi->fstates = NULL;
1525 fi->actions = NULL;
1526                     freeFi(fi);
1527 oc = tsGetOc(tsi);
1528 fi->magic = TFIMAGIC;
1529 fi->type = ts->order[oc].type;
1530 fi->ap = tsGetAlp(tsi);
1531 fi->record = 0;
1532                     loadFi(ts, fi, h, 1);
1533 fi->fstates = _free(fi->fstates);
1534 fi->fstates = fstates;
1535 fi->actions = _free(fi->actions);
1536 fi->actions = actions;
1537                 }
1538                 if (alp->multiLib)
1539                     ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1540
1541 assert(alp == fi->ap);
1542                 if (psmStage(psm, PSM_PKGINSTALL)) {
1543                     ourrc++;
1544                     lastFailed = i;
1545                 }
1546                 fi->h = headerFree(fi->h);
1547                 if (hsave) {
1548                     fi->h = headerLink(hsave);
1549                     hsave = headerFree(hsave);
1550                 }
1551             } else {
1552                 ourrc++;
1553                 lastFailed = i;
1554             }
1555
1556             h = headerFree(h);
1557
1558             if (gotfd) {
1559                 /*@-noeffectuncon @*/ /* FIX: check rc */
1560                 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1561                         alp->key, ts->notifyData);
1562                 /*@=noeffectuncon @*/
1563                 alp->fd = NULL;
1564             }
1565             freeFi(fi);
1566             /*@switchbreak@*/ break;
1567         case TR_REMOVED:
1568             rpmMessage(RPMMESS_DEBUG, "========== --- %s-%s-%s\n",
1569                         fi->name, fi->version, fi->release);
1570             oc = tsGetOc(tsi);
1571             /* If install failed, then we shouldn't erase. */
1572             if (ts->order[oc].u.removed.dependsOnIndex != lastFailed) {
1573                 if (psmStage(psm, PSM_PKGERASE))
1574                     ourrc++;
1575             }
1576             freeFi(fi);
1577             /*@switchbreak@*/ break;
1578         }
1579         xx = rpmdbSync(ts->rpmdb);
1580         (void) rpmfiUnlink(fi, "tsInstall");
1581         psm->fi = NULL;
1582     }
1583     /*@=branchstate@*/
1584     tsi = tsFreeIterator(tsi);
1585
1586     ts->flList = freeFl(ts, ts->flList);
1587     ts->flEntries = 0;
1588
1589     psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1590
1591     /*@-nullstate@*/
1592     if (ourrc)
1593         return -1;
1594     else
1595         return 0;
1596     /*@=nullstate@*/
1597 }