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