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