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