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