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