- transaction.c: use wrappers/iterators to access TFI_t.
[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         TFI_t otherFi;
585         const TFI_t * recs;
586         int numRecs;
587
588         if (XFA_SKIPPING(fi->actions[i]))
589             continue;
590
591         fn = tfiGetFN(fi);
592         fiFps = fi->fps + i;
593
594         if (ts->di) {
595             ds = ts->di;
596             while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
597             if (!ds->bsize) ds = NULL;
598             fixupSize = 0;
599         }
600
601         /*
602          * Retrieve all records that apply to this file. Note that the
603          * file info records were built in the same order as the packages
604          * will be installed and removed so the records for an overlapped
605          * files will be sorted in exactly the same order.
606          */
607         (void) htGetEntry(ts->ht, fiFps,
608                         (const void ***) &recs, &numRecs, NULL);
609
610         /*
611          * If this package is being added, look only at other packages
612          * being added -- removed packages dance to a different tune.
613          * If both this and the other package are being added, overlapped
614          * files must be identical (or marked as a conflict). The
615          * disposition of already installed config files leads to
616          * a small amount of extra complexity.
617          *
618          * If this package is being removed, then there are two cases that
619          * need to be worried about:
620          * If the other package is being added, then skip any overlapped files
621          * so that this package removal doesn't nuke the overlapped files
622          * that were just installed.
623          * If both this and the other package are being removed, then each
624          * file removal from preceding packages needs to be skipped so that
625          * the file removal occurs only on the last occurence of an overlapped
626          * file in the transaction set.
627          *
628          */
629
630         /* Locate this overlapped file in the set of added/removed packages. */
631         for (j = 0; j < numRecs && recs[j] != fi; j++)
632             {};
633
634         /* Find what the previous disposition of this file was. */
635         otherFileNum = -1;                      /* keep gcc quiet */
636         for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
637             struct fingerPrint_s * otherFps;
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 = tfiGetFC(otherFi);
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 assert(otherFi != NULL);
675             /* Mark added overlapped non-identical files as a conflict. */
676             if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
677              && filecmp(otherFi->fmodes[otherFileNum],
678                         otherFi->fmd5s[otherFileNum],
679                         otherFi->flinks[otherFileNum],
680                         fi->fmodes[i],
681                         fi->fmd5s[i],
682                         fi->flinks[i]))
683             {
684                 const char * altNEVR = otherFi->te->NEVR;
685                 rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT,
686                         p->NEVR, p->key,
687                         fn, NULL,
688                         altNEVR,
689                         0);
690             }
691
692             /* Try to get the disk accounting correct even if a conflict. */
693             fixupSize = otherFi->fsizes[otherFileNum];
694
695             if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(fn, &sb)) {
696                 /* Here is an overlapped  pre-existing config file. */
697                 fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
698                         ? FA_ALTNAME : FA_SKIP;
699             } else {
700                 fi->actions[i] = FA_CREATE;
701             }
702           } /*@switchbreak@*/ break;
703
704         case TR_REMOVED:
705             if (otherPkgNum >= 0) {
706 assert(otherFi != NULL);
707                 /* Here is an overlapped added file we don't want to nuke. */
708                 if (otherFi->actions[otherFileNum] != FA_ERASE) {
709                     /* On updates, don't remove files. */
710                     fi->actions[i] = FA_SKIP;
711                     /*@switchbreak@*/ break;
712                 }
713                 /* Here is an overlapped removed file: skip in previous. */
714                 otherFi->actions[otherFileNum] = FA_SKIP;
715             }
716             if (XFA_SKIPPING(fi->actions[i]))
717                 /*@switchbreak@*/ break;
718             if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
719                 /*@switchbreak@*/ break;
720             if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
721                 fi->actions[i] = FA_ERASE;
722                 /*@switchbreak@*/ break;
723             }
724                 
725             /* Here is a pre-existing modified config file that needs saving. */
726             {   char mdsum[50];
727                 if (!mdfile(fn, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
728                     fi->actions[i] = FA_BACKUP;
729                     /*@switchbreak@*/ break;
730                 }
731             }
732             fi->actions[i] = FA_ERASE;
733             /*@switchbreak@*/ break;
734         }
735
736         if (ds) {
737             uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
738
739             switch (fi->actions[i]) {
740               case FA_BACKUP:
741               case FA_SAVE:
742               case FA_ALTNAME:
743                 ds->ineeded++;
744                 ds->bneeded += s;
745                 /*@switchbreak@*/ break;
746
747             /*
748              * FIXME: If two packages share a file (same md5sum), and
749              * that file is being replaced on disk, will ds->bneeded get
750              * decremented twice? Quite probably!
751              */
752               case FA_CREATE:
753                 ds->bneeded += s;
754                 ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
755                 /*@switchbreak@*/ break;
756
757               case FA_ERASE:
758                 ds->ineeded--;
759                 ds->bneeded -= s;
760                 /*@switchbreak@*/ break;
761
762               default:
763                 /*@switchbreak@*/ break;
764             }
765
766             ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
767         }
768     }
769 }
770
771 /**
772  * Ensure that current package is newer than installed package.
773  * @param ts            transaction set
774  * @param p             current transaction element
775  * @param h             installed header
776  * @return              0 if not newer, 1 if okay
777  */
778 static int ensureOlder(rpmTransactionSet ts,
779                 const transactionElement p, const Header h)
780         /*@modifies ts @*/
781 {
782     int_32 reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
783     const char * reqEVR;
784     rpmDepSet req;
785     char * t;
786     int rc;
787
788     if (p == NULL || h == NULL)
789         return 1;
790
791     t = alloca(strlen(p->NEVR) + (p->epoch != NULL ? strlen(p->epoch) : 0) + 1);
792     *t = '\0';
793     reqEVR = t;
794     if (p->epoch != NULL)       t = stpcpy( stpcpy(t, p->epoch), ":");
795     if (p->version != NULL)     t = stpcpy(t, p->version);
796     *t++ = '-';
797     if (p->release != NULL)     t = stpcpy(t, p->release);
798     
799     req = dsSingle(RPMTAG_REQUIRENAME, p->name, reqEVR, reqFlags);
800     rc = headerMatchesDepFlags(h, req);
801     req = dsFree(req);
802
803     if (rc == 0) {
804         const char * altNEVR = hGetNEVR(h, NULL);
805         rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE,
806                 p->NEVR, p->key,
807                 NULL, NULL,
808                 altNEVR,
809                 0);
810         altNEVR = _free(altNEVR);
811         rc = 1;
812     } else
813         rc = 0;
814
815     return rc;
816 }
817
818 /**
819  */
820 /*@-mustmod@*/ /* FIX: fi->actions is modified. */
821 static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
822         /*@globals rpmGlobalMacroContext @*/
823         /*@modifies fi, rpmGlobalMacroContext @*/
824 {
825     int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
826     char ** netsharedPaths = NULL;
827     const char ** languages;
828     const char * dn, * bn;
829     int dnlen, bnlen, ix;
830     const char * s;
831     int * drc;
832     char * dff;
833     int dc;
834     int i, j;
835
836     if (!noDocs)
837         noDocs = rpmExpandNumeric("%{_excludedocs}");
838
839     {   const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
840         /*@-branchstate@*/
841         if (tmpPath && *tmpPath != '%')
842             netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
843         /*@=branchstate@*/
844         tmpPath = _free(tmpPath);
845     }
846
847     s = rpmExpand("%{_install_langs}", NULL);
848     /*@-branchstate@*/
849     if (!(s && *s != '%'))
850         s = _free(s);
851     if (s) {
852         languages = (const char **) splitString(s, strlen(s), ':');
853         s = _free(s);
854     } else
855         languages = NULL;
856     /*@=branchstate@*/
857
858     /* Compute directory refcount, skip directory if now empty. */
859     dc = tfiGetDC(fi);
860     drc = alloca(dc * sizeof(*drc));
861     memset(drc, 0, dc * sizeof(*drc));
862     dff = alloca(dc * sizeof(*dff));
863     memset(dff, 0, dc * sizeof(*dff));
864
865 #ifdef  DYING
866     for (i = 0; i < fi->fc; i++)
867 #else
868     if ((fi = tfiInit(fi, 0)) != NULL)
869     while ((i = tfiNext(fi)) >= 0)
870 #endif
871     {
872         char **nsp;
873
874 #ifdef  DYING
875         bn = fi->bnl[i];
876         bnlen = strlen(bn);
877         ix = fi->dil[i];
878         dn = fi->dnl[ix];
879         dnlen = strlen(dn);
880 #else
881         bn = tfiGetBN(fi);
882         bnlen = strlen(bn);
883         ix = tfiGetDX(fi);
884         dn = tfiGetDN(fi);
885         dnlen = strlen(dn);
886 #endif
887         if (dn == NULL)
888             continue;   /* XXX can't happen */
889
890         drc[ix]++;
891
892         /* Don't bother with skipped files */
893         if (XFA_SKIPPING(fi->actions[i])) {
894             drc[ix]--;
895             continue;
896         }
897
898         /*
899          * Skip net shared paths.
900          * Net shared paths are not relative to the current root (though
901          * they do need to take package relocations into account).
902          */
903         for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
904             int len;
905
906             len = strlen(*nsp);
907             if (dnlen >= len) {
908                 if (strncmp(dn, *nsp, len))
909                     /*@innercontinue@*/ continue;
910                 /* Only directories or complete file paths can be net shared */
911                 if (!(dn[len] == '/' || dn[len] == '\0'))
912                     /*@innercontinue@*/ continue;
913             } else {
914                 if (len < (dnlen + bnlen))
915                     /*@innercontinue@*/ continue;
916                 if (strncmp(dn, *nsp, dnlen))
917                     /*@innercontinue@*/ continue;
918                 if (strncmp(bn, (*nsp) + dnlen, bnlen))
919                     /*@innercontinue@*/ continue;
920                 len = dnlen + bnlen;
921                 /* Only directories or complete file paths can be net shared */
922                 if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0'))
923                     /*@innercontinue@*/ continue;
924             }
925
926             /*@innerbreak@*/ break;
927         }
928
929         if (nsp && *nsp) {
930             drc[ix]--;  dff[ix] = 1;
931             fi->actions[i] = FA_SKIPNETSHARED;
932             continue;
933         }
934
935         /*
936          * Skip i18n language specific files.
937          */
938         if (fi->flangs && languages && *fi->flangs[i]) {
939             const char **lang, *l, *le;
940             for (lang = languages; *lang != NULL; lang++) {
941                 if (!strcmp(*lang, "all"))
942                     /*@innerbreak@*/ break;
943                 for (l = fi->flangs[i]; *l != '\0'; l = le) {
944                     for (le = l; *le != '\0' && *le != '|'; le++)
945                         {};
946                     if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
947                         /*@innerbreak@*/ break;
948                     if (*le == '|') le++;       /* skip over | */
949                 }
950                 if (*l != '\0')
951                     /*@innerbreak@*/ break;
952             }
953             if (*lang == NULL) {
954                 drc[ix]--;      dff[ix] = 1;
955                 fi->actions[i] = FA_SKIPNSTATE;
956                 continue;
957             }
958         }
959
960         /*
961          * Skip documentation if requested.
962          */
963         if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
964             drc[ix]--;  dff[ix] = 1;
965             fi->actions[i] = FA_SKIPNSTATE;
966             continue;
967         }
968     }
969
970     /* Skip (now empty) directories that had skipped files. */
971 #ifndef NOTYET
972     if (fi != NULL)     /* XXX can't happen */
973     for (j = 0; j < dc; j++)
974 #else
975     if ((fi = tdiInit(fi)) != NULL)
976     while (j = tdiNext(fi) >= 0)
977 #endif
978     {
979
980         if (drc[j]) continue;   /* dir still has files. */
981         if (!dff[j]) continue;  /* dir was not emptied here. */
982         
983         /* Find parent directory and basename. */
984         dn = fi->dnl[j];        dnlen = strlen(dn) - 1;
985         bn = dn + dnlen;        bnlen = 0;
986         while (bn > dn && bn[-1] != '/') {
987                 bnlen++;
988                 dnlen--;
989                 bn--;
990         }
991
992         /* If explicitly included in the package, skip the directory. */
993 #ifdef  DYING
994         for (i = 0; i < fi->fc; i++)
995 #else
996         if ((fi = tfiInit(fi, 0)) != NULL)
997         while ((i = tfiNext(fi)) >= 0)
998 #endif
999         {
1000             const char * dir;
1001
1002             if (XFA_SKIPPING(fi->actions[i]))
1003                 /*@innercontinue@*/ continue;
1004             if (whatis(fi->fmodes[i]) != XDIR)
1005                 /*@innercontinue@*/ continue;
1006             dir = fi->dnl[fi->dil[i]];
1007             if (strlen(dir) != dnlen)
1008                 /*@innercontinue@*/ continue;
1009             if (strncmp(dir, dn, dnlen))
1010                 /*@innercontinue@*/ continue;
1011             if (strlen(fi->bnl[i]) != bnlen)
1012                 /*@innercontinue@*/ continue;
1013             if (strncmp(fi->bnl[i], bn, bnlen))
1014                 /*@innercontinue@*/ continue;
1015             rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
1016             fi->actions[i] = FA_SKIPNSTATE;
1017             /*@innerbreak@*/ break;
1018         }
1019     }
1020
1021     if (netsharedPaths) freeSplitString(netsharedPaths);
1022 #ifdef  DYING   /* XXX freeFi will deal with this later. */
1023     fi->flangs = _free(fi->flangs);
1024 #endif
1025     if (languages) freeSplitString((char **)languages);
1026 }
1027 /*@=mustmod@*/
1028
1029 /**
1030  * Return transaction element's file info.
1031  * @todo Take a TFI_t refcount here.
1032  * @param tei           transaction element iterator
1033  * @return              transaction element file info
1034  */
1035 static /*@null@*/
1036 TFI_t teGetFi(const teIterator tei)
1037         /*@modifies tei @*/
1038 {
1039     TFI_t fi = NULL;
1040
1041     if (tei != NULL && tei->ocsave != -1) {
1042         transactionElement te = tei->ts->order + tei->ocsave;
1043         if ((fi = te->fi) != NULL)
1044             fi->te = te;
1045     }
1046     /*@-compdef -refcounttrans -usereleased @*/
1047     return fi;
1048     /*@=compdef =refcounttrans =usereleased @*/
1049 }
1050
1051 #define NOTIFY(_ts, _al)        if ((_ts)->notify) (void) (_ts)->notify _al
1052
1053 int rpmRunTransactions( rpmTransactionSet ts,
1054                         rpmCallbackFunction notify, rpmCallbackData notifyData,
1055                         rpmProblemSet okProbs, rpmProblemSet * newProbs,
1056                         rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
1057 {
1058     int i, j;
1059     int ourrc = 0;
1060     int totalFileCount = 0;
1061     TFI_t fi;
1062     struct diskspaceInfo * dip;
1063     sharedFileInfo shared, sharedList;
1064     int numShared;
1065     int nexti;
1066     alKey lastKey;
1067     fingerPrintCache fpc;
1068     PSM_t psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
1069     teIterator pi;      transactionElement p;
1070     teIterator qi;      transactionElement q;
1071     int xx;
1072
1073     /* FIXME: what if the same package is included in ts twice? */
1074
1075     ts->transFlags = transFlags;
1076     if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
1077         ts->transFlags |= (_noTransScripts | _noTransTriggers);
1078     if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
1079         ts->transFlags |= _noTransTriggers;
1080
1081     /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
1082     if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
1083         ts->transFlags |= (_noTransScripts | _noTransTriggers);
1084
1085     ts->notify = notify;
1086     ts->notifyData = notifyData;
1087     ts->probs = rpmProblemSetFree(ts->probs);
1088     ts->probs = rpmProblemSetCreate();
1089     *newProbs = rpmpsLink(ts->probs, "RunTransactions");
1090     ts->ignoreSet = ignoreSet;
1091     ts->currDir = _free(ts->currDir);
1092     ts->currDir = currentDirectory();
1093     ts->chrootDone = 0;
1094     if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1095     ts->id = (int_32) time(NULL);
1096
1097     memset(psm, 0, sizeof(*psm));
1098     psm->ts = rpmtsLink(ts, "tsRun");
1099
1100     /* Get available space on mounted file systems. */
1101     if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
1102                 !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount))
1103     {
1104         struct stat sb;
1105
1106         rpmMessage(RPMMESS_DEBUG, _("getting list of mounted filesystems\n"));
1107
1108         ts->di = _free(ts->di);
1109         dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
1110
1111         for (i = 0; (i < ts->filesystemCount) && dip; i++) {
1112 #if STATFS_IN_SYS_STATVFS
1113             struct statvfs sfb;
1114             memset(&sfb, 0, sizeof(sfb));
1115             if (statvfs(ts->filesystems[i], &sfb))
1116 #else
1117             struct statfs sfb;
1118 #  if STAT_STATFS4
1119 /* This platform has the 4-argument version of the statfs call.  The last two
1120  * should be the size of struct statfs and 0, respectively.  The 0 is the
1121  * filesystem type, and is always 0 when statfs is called on a mounted
1122  * filesystem, as we're doing.
1123  */
1124             memset(&sfb, 0, sizeof(sfb));
1125             if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
1126 #  else
1127             memset(&sfb, 0, sizeof(sfb));
1128             if (statfs(ts->filesystems[i], &sfb))
1129 #  endif
1130 #endif
1131             {
1132                 dip = NULL;
1133             } else {
1134                 ts->di[i].bsize = sfb.f_bsize;
1135                 ts->di[i].bneeded = 0;
1136                 ts->di[i].ineeded = 0;
1137 #ifdef STATFS_HAS_F_BAVAIL
1138                 ts->di[i].bavail = sfb.f_bavail;
1139 #else
1140 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1141  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
1142  * it's about all we can do.
1143  */
1144                 ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
1145 #endif
1146                 /* XXX Avoid FAT and other file systems that have not inodes. */
1147                 ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1148                                 ? sfb.f_ffree : -1;
1149
1150                 xx = stat(ts->filesystems[i], &sb);
1151                 ts->di[i].dev = sb.st_dev;
1152             }
1153         }
1154
1155         if (dip) ts->di[i].bsize = 0;
1156     }
1157
1158     /* ===============================================
1159      * For packages being installed:
1160      * - verify package arch/os.
1161      * - verify package epoch:version-release is newer.
1162      * - count files.
1163      * For packages being removed:
1164      * - count files.
1165      */
1166     /* The ordering doesn't matter here */
1167     pi = teInitIterator(ts);
1168     while ((p = teNext(pi, TR_ADDED)) != NULL) {
1169         rpmdbMatchIterator mi;
1170         int fc;
1171
1172         if ((fi = teGetFi(pi)) == NULL)
1173             continue;   /* XXX can't happen */
1174         fc = tfiGetFC(fi);
1175
1176         if (!(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
1177             if (!archOkay(p->arch))
1178                 rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH,
1179                         p->NEVR, p->key,
1180                         p->arch, NULL,
1181                         NULL, 0);
1182
1183         if (!(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
1184             if (!osOkay(p->os))
1185                 rpmProblemSetAppend(ts->probs, RPMPROB_BADOS,
1186                         p->NEVR, p->key,
1187                         p->os, NULL,
1188                         NULL, 0);
1189
1190         if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
1191             Header h;
1192             mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1193             while ((h = rpmdbNextIterator(mi)) != NULL)
1194                 xx = ensureOlder(ts, p, h);
1195             mi = rpmdbFreeIterator(mi);
1196         }
1197
1198         /* XXX multilib should not display "already installed" problems */
1199         if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !p->multiLib) {
1200             mi = rpmtsInitIterator(ts, RPMTAG_NAME, p->name, 0);
1201             xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT,
1202                                 p->version);
1203             xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
1204                                 p->release);
1205
1206             while (rpmdbNextIterator(mi) != NULL) {
1207                 rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED,
1208                         p->NEVR, p->key,
1209                         NULL, NULL,
1210                         NULL, 0);
1211                 /*@innerbreak@*/ break;
1212             }
1213             mi = rpmdbFreeIterator(mi);
1214         }
1215
1216         /* Count no. of files (if any). */
1217         totalFileCount += fc;
1218
1219     }
1220     pi = teFreeIterator(pi);
1221
1222     /* The ordering doesn't matter here */
1223     pi = teInitIterator(ts);
1224     while ((p = teNext(pi, TR_REMOVED)) != NULL) {
1225         int fc;
1226
1227         if ((fi = teGetFi(pi)) == NULL)
1228             continue;   /* XXX can't happen */
1229         fc = tfiGetFC(fi);
1230
1231         totalFileCount += fc;
1232     }
1233     pi = teFreeIterator(pi);
1234
1235     /* ===============================================
1236      * Initialize transaction element file info for package:
1237      */
1238
1239     /*
1240      * FIXME?: we'd be better off assembling one very large file list and
1241      * calling fpLookupList only once. I'm not sure that the speedup is
1242      * worth the trouble though.
1243      */
1244     pi = teInitIterator(ts);
1245     while ((p = teNextIterator(pi)) != NULL) {
1246         int fc;
1247
1248         if ((fi = teGetFi(pi)) == NULL)
1249             continue;   /* XXX can't happen */
1250         fc = tfiGetFC(fi);
1251
1252 #ifdef  DYING   /* XXX W2DO? this is now done teGetFi, okay ??? */
1253         fi->magic = TFIMAGIC;
1254         fi->te = p;
1255 #endif
1256
1257         /*@-branchstate@*/
1258         switch (p->type) {
1259         case TR_ADDED:
1260             fi->record = 0;
1261             /* Skip netshared paths, not our i18n files, and excluded docs */
1262             if (fc > 0)
1263                 skipFiles(ts, fi);
1264             /*@switchbreak@*/ break;
1265         case TR_REMOVED:
1266             fi->record = p->u.removed.dboffset;
1267             /*@switchbreak@*/ break;
1268         }
1269         /*@=branchstate@*/
1270
1271         fi->fps = (fc > 0 ? xmalloc(fc * sizeof(*fi->fps)) : NULL);
1272     }
1273     pi = teFreeIterator(pi);
1274
1275     if (!ts->chrootDone) {
1276         xx = chdir("/");
1277         /*@-superuser -noeffect @*/
1278         xx = chroot(ts->rootDir);
1279         /*@=superuser =noeffect @*/
1280         ts->chrootDone = 1;
1281         if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
1282         /*@-onlytrans@*/
1283         /*@-mods@*/
1284         chroot_prefix = ts->rootDir;
1285         /*@=mods@*/
1286         /*@=onlytrans@*/
1287     }
1288
1289     ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
1290     fpc = fpCacheCreate(totalFileCount);
1291
1292     /* ===============================================
1293      * Add fingerprint for each file not skipped.
1294      */
1295     pi = teInitIterator(ts);
1296     while ((p = teNextIterator(pi)) != NULL) {
1297         int fc;
1298
1299         if ((fi = teGetFi(pi)) == NULL)
1300             continue;   /* XXX can't happen */
1301         fc = tfiGetFC(fi);
1302
1303         fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fc, fi->fps);
1304         /*@-branchstate@*/
1305 #ifdef  DYING
1306         for (i = 0; i < fi->fc; i++)
1307 #else
1308         if ((fi = tfiInit(fi, 0)) != NULL)
1309         while ((i = tfiNext(fi)) >= 0)
1310 #endif
1311         {
1312             if (XFA_SKIPPING(fi->actions[i]))
1313                 /*@innercontinue@*/ continue;
1314             /*@-dependenttrans@*/
1315             htAddEntry(ts->ht, fi->fps + i, (void *) fi);
1316             /*@=dependenttrans@*/
1317         }
1318         /*@=branchstate@*/
1319     }
1320     pi = teFreeIterator(pi);
1321
1322     /*@-noeffectuncon @*/ /* FIX: check rc */
1323     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->orderCount,
1324         NULL, ts->notifyData));
1325     /*@=noeffectuncon@*/
1326
1327     /* ===============================================
1328      * Compute file disposition for each package in transaction set.
1329      */
1330     pi = teInitIterator(ts);
1331     while ((p = teNextIterator(pi)) != NULL) {
1332         dbiIndexSet * matches;
1333         int knownBad;
1334         int fc;
1335
1336         if ((fi = teGetFi(pi)) == NULL)
1337             continue;   /* XXX can't happen */
1338         fc = tfiGetFC(fi);
1339
1340         /*@-noeffectuncon @*/ /* FIX: check rc */
1341         NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, teGetOc(pi),
1342                         ts->orderCount, NULL, ts->notifyData));
1343         /*@=noeffectuncon@*/
1344
1345         if (fc == 0) continue;
1346
1347         /* Extract file info for all files in this package from the database. */
1348         matches = xcalloc(fc, sizeof(*matches));
1349         if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fc)) {
1350             psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
1351             return 1;   /* XXX WTFO? */
1352         }
1353
1354         numShared = 0;
1355 #ifdef  DYING
1356         for (i = 0; i < fi->fc; i++)
1357 #else
1358         if ((fi = tfiInit(fi, 0)) != NULL)
1359         while ((i = tfiNext(fi)) >= 0)
1360 #endif
1361             numShared += dbiIndexSetCount(matches[i]);
1362
1363         /* Build sorted file info list for this package. */
1364         shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
1365
1366 #ifdef DYING
1367         for (i = 0; i < fi->fc; i++)
1368 #else
1369         if ((fi = tfiInit(fi, 0)) != NULL)
1370         while ((i = tfiNext(fi)) >= 0)
1371 #endif
1372         {
1373             /*
1374              * Take care not to mark files as replaced in packages that will
1375              * have been removed before we will get here.
1376              */
1377             for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
1378                 int ro;
1379                 ro = dbiIndexRecordOffset(matches[i], j);
1380                 knownBad = 0;
1381                 qi = teInitIterator(ts);
1382                 while ((q = teNext(qi, TR_REMOVED)) != NULL) {
1383                     if (ro == knownBad)
1384                         /*@innerbreak@*/ break;
1385                     if (q->u.removed.dboffset == ro)
1386                         knownBad = ro;
1387                 }
1388                 qi = teFreeIterator(qi);
1389
1390                 shared->pkgFileNum = i;
1391                 shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
1392                 shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
1393                 shared->isRemoved = (knownBad == ro);
1394                 shared++;
1395             }
1396             matches[i] = dbiFreeIndexSet(matches[i]);
1397         }
1398         numShared = shared - sharedList;
1399         shared->otherPkg = -1;
1400         matches = _free(matches);
1401
1402         /* Sort file info by other package index (otherPkg) */
1403         qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
1404
1405         /* For all files from this package that are in the database ... */
1406         for (i = 0; i < numShared; i = nexti) {
1407             int beingRemoved;
1408
1409             shared = sharedList + i;
1410
1411             /* Find the end of the files in the other package. */
1412             for (nexti = i + 1; nexti < numShared; nexti++) {
1413                 if (sharedList[nexti].otherPkg != shared->otherPkg)
1414                     /*@innerbreak@*/ break;
1415             }
1416
1417             /* Is this file from a package being removed? */
1418             beingRemoved = 0;
1419             if (ts->removedPackages != NULL)
1420             for (j = 0; j < ts->numRemovedPackages; j++) {
1421                 if (ts->removedPackages[j] != shared->otherPkg)
1422                     /*@innercontinue@*/ continue;
1423                 beingRemoved = 1;
1424                 /*@innerbreak@*/ break;
1425             }
1426
1427             /* Determine the fate of each file. */
1428             switch (p->type) {
1429             case TR_ADDED:
1430                 xx = handleInstInstalledFiles(ts, p, fi, shared, nexti - i,
1431         !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
1432                 /*@switchbreak@*/ break;
1433             case TR_REMOVED:
1434                 if (!beingRemoved)
1435                     xx = handleRmvdInstalledFiles(ts, fi, shared, nexti - i);
1436                 /*@switchbreak@*/ break;
1437             }
1438         }
1439
1440         free(sharedList);
1441
1442         /* Update disk space needs on each partition for this package. */
1443         handleOverlappedFiles(ts, p, fi);
1444
1445         /* Check added package has sufficient space on each partition used. */
1446         switch (p->type) {
1447         case TR_ADDED:
1448             if (!(ts->di && tfiGetFC(fi) > 0))
1449                 /*@switchbreak@*/ break;
1450             for (i = 0; i < ts->filesystemCount; i++) {
1451
1452                 dip = ts->di + i;
1453
1454                 /* XXX Avoid FAT and other file systems that have not inodes. */
1455                 if (dip->iavail <= 0)
1456                     /*@innercontinue@*/ continue;
1457
1458                 if (adj_fs_blocks(dip->bneeded) > dip->bavail) {
1459                     rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE,
1460                                 p->NEVR, p->key,
1461                                 ts->filesystems[i], NULL, NULL,
1462                    (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
1463                 }
1464
1465                 if (adj_fs_blocks(dip->ineeded) > dip->iavail) {
1466                     rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES,
1467                                 p->NEVR, p->key,
1468                                 ts->filesystems[i], NULL, NULL,
1469                     (adj_fs_blocks(dip->ineeded) - dip->iavail));
1470                 }
1471             }
1472             /*@switchbreak@*/ break;
1473         case TR_REMOVED:
1474             /*@switchbreak@*/ break;
1475         }
1476     }
1477     pi = teFreeIterator(pi);
1478
1479     if (ts->chrootDone) {
1480         /*@-superuser -noeffect @*/
1481         xx = chroot(".");
1482         /*@=superuser =noeffect @*/
1483         ts->chrootDone = 0;
1484         if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
1485         /*@-mods@*/
1486         chroot_prefix = NULL;
1487         /*@=mods@*/
1488         xx = chdir(ts->currDir);
1489     }
1490
1491     /*@-noeffectuncon @*/ /* FIX: check rc */
1492     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->orderCount,
1493         NULL, ts->notifyData));
1494     /*@=noeffectuncon @*/
1495
1496     /* ===============================================
1497      * Free unused memory as soon as possible.
1498      */
1499     pi = teInitIterator(ts);
1500     while ((p = teNextIterator(pi)) != NULL) {
1501         if ((fi = teGetFi(pi)) == NULL)
1502             continue;   /* XXX can't happen */
1503         if (tfiGetFC(fi) == 0)
1504             continue;
1505         fi->fps = _free(fi->fps);
1506     }
1507     pi = teFreeIterator(pi);
1508
1509     fpCacheFree(fpc);
1510     htFree(ts->ht);
1511     ts->ht = NULL;
1512
1513     /* ===============================================
1514      * If unfiltered problems exist, free memory and return.
1515      */
1516     if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
1517      || (ts->probs->numProblems &&
1518                 (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
1519        )
1520     {
1521         if (psm->ts != NULL)
1522             psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
1523         return ts->orderCount;
1524     }
1525
1526     /* ===============================================
1527      * Save removed files before erasing.
1528      */
1529     if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
1530         pi = teInitIterator(ts);
1531         while ((p = teNextIterator(pi)) != NULL) {
1532             fi = teGetFi(pi);
1533             switch (p->type) {
1534             case TR_ADDED:
1535                 /*@switchbreak@*/ break;
1536             case TR_REMOVED:
1537                 if (!(ts->transFlags & RPMTRANS_FLAG_REPACKAGE))
1538                     /*@switchbreak@*/ break;
1539                 psm->te = p;
1540                 psm->fi = rpmfiLink(fi, "tsRepackage");
1541                 xx = psmStage(psm, PSM_PKGSAVE);
1542                 (void) rpmfiUnlink(fi, "tsRepackage");
1543                 psm->fi = NULL;
1544                 psm->te = NULL;
1545                 /*@switchbreak@*/ break;
1546             }
1547         }
1548         pi = teFreeIterator(pi);
1549     }
1550
1551     /* ===============================================
1552      * Install and remove packages.
1553      */
1554     lastKey = (alKey)-2;        /* erased packages have -1 */
1555     pi = teInitIterator(ts);
1556     /*@-branchstate@*/ /* FIX: fi reload needs work */
1557     while ((p = teNextIterator(pi)) != NULL) {
1558         alKey pkgKey;
1559         Header h;
1560         int gotfd;
1561
1562         gotfd = 0;
1563         if ((fi = teGetFi(pi)) == NULL)
1564             continue;   /* XXX can't happen */
1565         
1566         psm->te = p;
1567         psm->fi = rpmfiLink(fi, "tsInstall");
1568         switch (p->type) {
1569         case TR_ADDED:
1570
1571             pkgKey = p->u.addedKey;
1572
1573             rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", p->NEVR);
1574             h = NULL;
1575             {
1576                 p->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
1577                                 p->key, ts->notifyData);
1578                 if (p->fd != NULL) {
1579                     rpmRC rpmrc;
1580
1581                     /*@=mustmod@*/      /* LCL: segfault */
1582                     rpmrc = rpmReadPackageFile(ts, p->fd,
1583                                 "rpmRunTransactions", &h);
1584                     /*@=mustmod@*/
1585
1586                     if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
1587                         p->fd = ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
1588                                         0, 0,
1589                                         p->key, ts->notifyData);
1590                         p->fd = NULL;
1591                         ourrc++;
1592                     }
1593                     if (p->fd != NULL) gotfd = 1;
1594                 }
1595             }
1596
1597             if (p->fd != NULL) {
1598                 {
1599 char * fstates = fi->fstates;
1600 fileAction * actions = fi->actions;
1601
1602 fi->fstates = NULL;
1603 fi->actions = NULL;
1604                     (void) fiFree(fi, 0);
1605 /*@-usereleased@*/
1606 fi->magic = TFIMAGIC;
1607 fi->te = p;
1608 fi->record = 0;
1609                     (void) fiNew(ts, fi, h, RPMTAG_BASENAMES, 1);
1610 fi->fstates = _free(fi->fstates);
1611 fi->fstates = fstates;
1612 fi->actions = _free(fi->actions);
1613 fi->actions = actions;
1614 /*@=usereleased@*/
1615
1616                 }
1617                 if (p->multiLib)
1618                     ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
1619
1620                 if (psmStage(psm, PSM_PKGINSTALL)) {
1621                     ourrc++;
1622                     lastKey = pkgKey;
1623                 }
1624                 fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
1625             } else {
1626                 ourrc++;
1627                 lastKey = pkgKey;
1628             }
1629
1630             h = headerFree(h, "TR_ADDED h free");
1631
1632             if (gotfd) {
1633                 /*@-noeffectuncon @*/ /* FIX: check rc */
1634                 (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
1635                         p->key, ts->notifyData);
1636                 /*@=noeffectuncon @*/
1637                 p->fd = NULL;
1638             }
1639             (void) fiFree(fi, 0);
1640             /*@switchbreak@*/ break;
1641         case TR_REMOVED:
1642             rpmMessage(RPMMESS_DEBUG, "========== --- %s\n", p->NEVR);
1643             /* If install failed, then we shouldn't erase. */
1644             if (p->u.removed.dependsOnKey != lastKey) {
1645                 if (psmStage(psm, PSM_PKGERASE))
1646                     ourrc++;
1647             }
1648             (void) fiFree(fi, 0);
1649             /*@switchbreak@*/ break;
1650         }
1651         xx = rpmdbSync(ts->rpmdb);
1652         (void) rpmfiUnlink(psm->fi, "tsInstall");
1653         psm->fi = NULL;
1654         psm->te = NULL;
1655     }
1656     /*@=branchstate@*/
1657     pi = teFreeIterator(pi);
1658
1659     psm->ts = rpmtsUnlink(psm->ts, "tsRun");
1660
1661     /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
1662     if (ourrc)
1663         return -1;
1664     else
1665         return 0;
1666     /*@=nullstate@*/
1667 }