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