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