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