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