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