fix: prevent segfault if malicious server sends 1 GB of data through ftpNLST.
[platform/upstream/rpm.git] / lib / transaction.c
index 69c6f05..d019de4 100644 (file)
@@ -1,4 +1,4 @@
-/** \ingroup rpmtrans
+/** \ingroup rpmts
  * \file lib/transaction.c
  */
 
 
 #include <rpmmacro.h>  /* XXX for rpmExpand */
 
+#include "fsm.h"
 #include "psm.h"
 
+#include "rpmdb.h"
+
 #include "rpmds.h"
+
+#define        _RPMFI_INTERNAL
 #include "rpmfi.h"
+
+#define        _RPMTE_INTERNAL
 #include "rpmte.h"
+
+#define        _RPMTS_INTERNAL
 #include "rpmts.h"
 
-#include "rpmdb.h"
+#include "cpio.h"
 #include "fprint.h"
 #include "legacy.h"    /* XXX domd5 */
 #include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
 
-/* XXX FIXME: merge with existing (broken?) tests in system.h */
-/* portability fiddles */
-#if STATFS_IN_SYS_STATVFS
-/*@-incondefs@*/
-# include <sys/statvfs.h>
-#if defined(__LCLINT__)
-/*@-declundef -exportheader -protoparammatch @*/ /* LCL: missing annotation */
-extern int statvfs (const char * file, /*@out@*/ struct statvfs * buf)
-       /*@globals fileSystem @*/
-       /*@modifies *buf, fileSystem @*/;
-/*@=declundef =exportheader =protoparammatch @*/
-/*@=incondefs@*/
-#endif
-#else
-# if STATFS_IN_SYS_VFS
-#  include <sys/vfs.h>
-# else
-#  if STATFS_IN_SYS_MOUNT
-#   include <sys/mount.h>
-#  else
-#   if STATFS_IN_SYS_STATFS
-#    include <sys/statfs.h>
-#   endif
-#  endif
-# endif
-#endif
-
 #include "debug.h"
 
-/*@access FD_t @*/             /* XXX compared with NULL */
-/*@access Header @*/           /* XXX compared with NULL */
-/*@access rpmProblemSet @*/    /* XXX need rpmProblemSetOK() */
+/*@access Header @*/           /* XXX ts->notify arg1 is void ptr */
+/*@access rpmps @*/    /* XXX need rpmProblemSetOK() */
 /*@access dbiIndexSet @*/
-/*@access rpmdb @*/
 
-/*@access PSM_t @*/
+/*@access rpmpsm @*/
 
 /*@access alKey @*/
 /*@access fnpyKey @*/
 
-/*@access TFI_t @*/
+/*@access rpmfi @*/
 
-/*@access teIterator @*/
-/*@access rpmTransactionSet @*/
-
-/**
- */
-struct diskspaceInfo {
-    dev_t dev;                 /*!< File system device number. */
-    signed long bneeded;       /*!< No. of blocks needed. */
-    signed long ineeded;       /*!< No. of inodes needed. */
-    int bsize;                 /*!< File system block size. */
-    signed long bavail;                /*!< No. of blocks available. */
-    signed long iavail;                /*!< No. of inodes available. */
-};
-
-/**
- * Adjust for root only reserved space. On linux e2fs, this is 5%.
- */
-#define        adj_fs_blocks(_nb)      (((_nb) * 21) / 20)
-
-/* argon thought a shift optimization here was a waste of time...  he's
-   probably right :-( */
-#define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
-
-void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
-{
-    ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
-}
-
-int rpmtransGetKeys(const rpmTransactionSet ts, fnpyKey ** ep, int * nep)
-{
-    int rc = 0;
-
-    if (nep) *nep = ts->orderCount;
-    if (ep) {
-       teIterator pi;  transactionElement p;
-       fnpyKey * e;
-
-       *ep = e = xmalloc(ts->orderCount * sizeof(*e));
-       pi = teInitIterator(ts);
-       while ((p = teNextIterator(pi)) != NULL) {
-           switch (teGetType(p)) {
-           case TR_ADDED:
-               /*@-dependenttrans@*/
-               *e = teGetKey(p);
-               /*@=dependenttrans@*/
-               /*@switchbreak@*/ break;
-           case TR_REMOVED:
-           default:
-               *e = NULL;
-               /*@switchbreak@*/ break;
-           }
-           e++;
-       }
-       pi = teFreeIterator(pi);
-    }
-    return rc;
-}
+/*@access rpmte @*/
+/*@access rpmtsi @*/
+/*@access rpmts @*/
 
 /**
  */
@@ -155,13 +81,14 @@ static int sharedCmp(const void * one, const void * two)
 
 /**
  */
-static fileAction decideFileFate(const rpmTransactionSet ts,
-               const TFI_t ofi, TFI_t nfi)
-       /*@globals fileSystem @*/
-       /*@modifies nfi, fileSystem @*/
+/*@-boundsread@*/
+static fileAction decideFileFate(const rpmts ts,
+               const rpmfi ofi, rpmfi nfi)
+       /*@globals fileSystem, internalState @*/
+       /*@modifies nfi, fileSystem, internalState @*/
 {
-    const char * fn = tfiGetFN(nfi);
-    int newFlags = tfiGetFFlags(nfi);
+    const char * fn = rpmfiFN(nfi);
+    int newFlags = rpmfiFFlags(nfi);
     char buffer[1024];
     fileTypes dbWhat, newWhat, diskWhat;
     struct stat sb;
@@ -172,7 +99,7 @@ static fileAction decideFileFate(const rpmTransactionSet ts,
         * The file doesn't exist on the disk. Create it unless the new
         * package has marked it as missingok, or allfiles is requested.
         */
-       if (!(ts->transFlags & RPMTRANS_FLAG_ALLFILES)
+       if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES)
         && (newFlags & RPMFILE_MISSINGOK))
        {
            rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
@@ -183,9 +110,9 @@ static fileAction decideFileFate(const rpmTransactionSet ts,
        }
     }
 
-    diskWhat = whatis(sb.st_mode);
-    dbWhat = whatis(ofi->fmodes[ofi->i]);
-    newWhat = whatis(nfi->fmodes[nfi->i]);
+    diskWhat = whatis((int_16)sb.st_mode);
+    dbWhat = whatis(rpmfiFMode(ofi));
+    newWhat = whatis(rpmfiFMode(nfi));
 
     /*
      * RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
@@ -208,38 +135,31 @@ static fileAction decideFileFate(const rpmTransactionSet ts,
      * possible in case something else (like the timestamp) has changed.
      */
     if (dbWhat == REG) {
-#ifdef DYING
-       if (ofi->md5s != NULL && nfi->md5s != NULL) {
-#endif
-           const unsigned char * omd5 = ofi->md5s + (16 * ofi->i);
-           const unsigned char * nmd5 = nfi->md5s + (16 * nfi->i);
-           if (domd5(fn, buffer, 0))
-               return FA_CREATE;       /* assume file has been removed */
-           if (!memcmp(omd5, buffer, 16))
-               return FA_CREATE;       /* unmodified config file, replace. */
-           if (!memcmp(omd5, nmd5, 16))
-               return FA_SKIP;         /* identical file, don't bother. */
-#ifdef DYING
-       } else {
-           const char * omd5 = ofi->fmd5s[ofi->i];
-           const char * nmd5 = nfi->fmd5s[nfi->i];
-           if (domd5(fn, buffer, 1))
-               return FA_CREATE;       /* assume file has been removed */
-           if (!strcmp(omd5, buffer))
-               return FA_CREATE;       /* unmodified config file, replace. */
-           if (!strcmp(omd5, nmd5))
-               return FA_SKIP;         /* identical file, don't bother. */
-       }
-#endif
+       const unsigned char * omd5, * nmd5;
+       if (domd5(fn, buffer, 0, NULL))
+           return FA_CREATE;   /* assume file has been removed */
+       omd5 = rpmfiMD5(ofi);
+       if (omd5 && !memcmp(omd5, buffer, 16))
+           return FA_CREATE;   /* unmodified config file, replace. */
+       nmd5 = rpmfiMD5(nfi);
+/*@-nullpass@*/
+       if (omd5 && nmd5 && !memcmp(omd5, nmd5, 16))
+           return FA_SKIP;     /* identical file, don't bother. */
+/*@=nullpass@*/
     } else /* dbWhat == LINK */ {
+       const char * oFLink, * nFLink;
        memset(buffer, 0, sizeof(buffer));
        if (readlink(fn, buffer, sizeof(buffer) - 1) == -1)
            return FA_CREATE;   /* assume file has been removed */
-       if (!strcmp(ofi->flinks[ofi->i], buffer))
+       oFLink = rpmfiFLink(ofi);
+       if (oFLink && !strcmp(oFLink, buffer))
            return FA_CREATE;   /* unmodified config file, replace. */
-       if (!strcmp(ofi->flinks[ofi->i], nfi->flinks[nfi->i]))
+       nFLink = rpmfiFLink(nfi);
+/*@-nullpass@*/
+       if (oFLink && nFLink && !strcmp(oFLink, nFLink))
            return FA_SKIP;     /* identical file, don't bother. */
-     }
+/*@=nullpass@*/
+    }
 
     /*
      * The config file on the disk has been modified, but
@@ -249,53 +169,57 @@ static fileAction decideFileFate(const rpmTransactionSet ts,
      */
     return save;
 }
+/*@=boundsread@*/
 
 /**
  */
-static int filecmp(TFI_t afi, TFI_t bfi)
+/*@-boundsread@*/
+static int filecmp(rpmfi afi, rpmfi bfi)
        /*@*/
 {
-    fileTypes awhat = whatis(afi->fmodes[afi->i]);
-    fileTypes bwhat = whatis(bfi->fmodes[bfi->i]);
+    fileTypes awhat = whatis(rpmfiFMode(afi));
+    fileTypes bwhat = whatis(rpmfiFMode(bfi));
 
     if (awhat != bwhat) return 1;
 
     if (awhat == LINK) {
-       const char * alink = afi->flinks[afi->i];
-       const char * blink = bfi->flinks[bfi->i];
+       const char * alink = rpmfiFLink(afi);
+       const char * blink = rpmfiFLink(bfi);
+       if (alink == blink) return 0;
+       if (alink == NULL) return 1;
+       if (blink == NULL) return -1;
        return strcmp(alink, blink);
     } else if (awhat == REG) {
-#ifdef DYING
-       if (afi->md5s != NULL && bfi->md5s != NULL) {
-#endif
-           const unsigned char * amd5 = afi->md5s + (16 * afi->i);
-           const unsigned char * bmd5 = bfi->md5s + (16 * bfi->i);
-           return memcmp(amd5, bmd5, 16);
-#ifdef DYING
-       } else {
-           const char * amd5 = afi->fmd5s[afi->i];
-           const char * bmd5 = bfi->fmd5s[bfi->i];
-           return strcmp(amd5, bmd5);
-       }
-#endif
+       const unsigned char * amd5 = rpmfiMD5(afi);
+       const unsigned char * bmd5 = rpmfiMD5(bfi);
+       if (amd5 == bmd5) return 0;
+       if (amd5 == NULL) return 1;
+       if (bmd5 == NULL) return -1;
+       return memcmp(amd5, bmd5, 16);
     }
 
     return 0;
 }
+/*@=boundsread@*/
 
 /**
  */
 /* XXX only ts->{probs,rpmdb} modified */
-static int handleInstInstalledFiles(const rpmTransactionSet ts,
-               transactionElement p, TFI_t fi,
+/*@-bounds@*/
+static int handleInstInstalledFiles(const rpmts ts,
+               rpmte p, rpmfi fi,
                sharedFileInfo shared,
                int sharedCount, int reportConflicts)
-       /*@globals fileSystem @*/
-       /*@modifies ts, fi, fileSystem @*/
+       /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
+       /*@modifies ts, fi, rpmGlobalMacroContext, fileSystem, internalState @*/
 {
+    uint_32 tscolor = rpmtsColor(ts);
+    uint_32 otecolor, tecolor;
+    uint_32 oficolor, ficolor;
     const char * altNEVR = NULL;
-    TFI_t otherFi = NULL;
+    rpmfi otherFi = NULL;
     int numReplaced = 0;
+    rpmps ps;
     int i;
 
     {  rpmdbMatchIterator mi;
@@ -306,25 +230,45 @@ static int handleInstInstalledFiles(const rpmTransactionSet ts,
                        &shared->otherPkg, sizeof(shared->otherPkg));
        while ((h = rpmdbNextIterator(mi)) != NULL) {
            altNEVR = hGetNEVR(h, NULL);
-           otherFi = fiNew(ts, NULL, h, RPMTAG_BASENAMES, scareMem);
+           otherFi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
            break;
        }
        mi = rpmdbFreeIterator(mi);
     }
 
+    /* Compute package color. */
+    tecolor = rpmteColor(p);
+    tecolor &= tscolor;
+
+    /* Compute other pkg color. */
+    otecolor = 0;
+    otherFi = rpmfiInit(otherFi, 0);
+    if (otherFi != NULL)
+    while (rpmfiNext(otherFi) >= 0)
+       otecolor |= rpmfiFColor(otherFi);
+    otecolor &= tscolor;
+
     if (otherFi == NULL)
        return 1;
 
     fi->replaced = xcalloc(sharedCount, sizeof(*fi->replaced));
 
+    ps = rpmtsProblems(ts);
     for (i = 0; i < sharedCount; i++, shared++) {
        int otherFileNum, fileNum;
+       int isCfgFile;
 
        otherFileNum = shared->otherFileNum;
-       (void) tfiSetFX(otherFi, otherFileNum);
+       (void) rpmfiSetFX(otherFi, otherFileNum);
+       oficolor = rpmfiFColor(otherFi);
+       oficolor &= tscolor;
 
        fileNum = shared->pkgFileNum;
-       (void) tfiSetFX(fi, fileNum);
+       (void) rpmfiSetFX(fi, fileNum);
+       ficolor = rpmfiFColor(fi);
+       ficolor &= tscolor;
+
+       isCfgFile = ((rpmfiFFlags(otherFi) | rpmfiFFlags(fi)) & RPMFILE_CONFIG);
 
 #ifdef DYING
        /* XXX another tedious segfault, assume file state normal. */
@@ -336,14 +280,16 @@ static int handleInstInstalledFiles(const rpmTransactionSet ts,
            continue;
 
        if (filecmp(otherFi, fi)) {
+           /* Report conflicts only for packages/files of same color. */
+           if (tscolor == 0 || (tecolor == otecolor && ficolor == oficolor))
            if (reportConflicts) {
-               rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT,
-                       teGetNEVR(p), teGetKey(p),
-                       tfiGetDN(fi), tfiGetBN(fi),
+               rpmpsAppend(ps, RPMPROB_FILE_CONFLICT,
+                       rpmteNEVR(p), rpmteKey(p),
+                       rpmfiDN(fi), rpmfiBN(fi),
                        altNEVR,
                        0);
            }
-           if (!(tfiGetFFlags(otherFi) | tfiGetFFlags(fi)) & RPMFILE_CONFIG) {
+           if (!isCfgFile) {
                /*@-assignexpose@*/ /* FIX: p->replaced, not fi */
                if (!shared->isRemoved)
                    fi->replaced[numReplaced++] = *shared;
@@ -351,17 +297,17 @@ static int handleInstInstalledFiles(const rpmTransactionSet ts,
            }
        }
 
-       if ((tfiGetFFlags(otherFi) | tfiGetFFlags(fi)) & RPMFILE_CONFIG) {
+       if (isCfgFile) {
            fileAction action;
            action = decideFileFate(ts, otherFi, fi);
            fi->actions[fileNum] = action;
        }
-       fi->replacedSizes[fileNum] = otherFi->fsizes[otherFi->i];
-
+       fi->replacedSizes[fileNum] = rpmfiFSize(otherFi);
     }
+    ps = rpmpsFree(ps);
 
     altNEVR = _free(altNEVR);
-    otherFi = fiFree(otherFi, 1);
+    otherFi = rpmfiFree(otherFi);
 
     fi->replaced = xrealloc(fi->replaced,      /* XXX memory leak */
                           sizeof(*fi->replaced) * (numReplaced + 1));
@@ -369,14 +315,15 @@ static int handleInstInstalledFiles(const rpmTransactionSet ts,
 
     return 0;
 }
+/*@=bounds@*/
 
 /**
  */
 /* XXX only ts->rpmdb modified */
-static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
+static int handleRmvdInstalledFiles(const rpmts ts, rpmfi fi,
                sharedFileInfo shared, int sharedCount)
-       /*@globals fileSystem @*/
-       /*@modifies ts, fi, fileSystem @*/
+       /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
+       /*@modifies ts, fi, rpmGlobalMacroContext, fileSystem, internalState @*/
 {
     HGE_t hge = fi->hge;
     Header h;
@@ -395,6 +342,7 @@ static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
 
     xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
 
+/*@-boundswrite@*/
     for (i = 0; i < sharedCount; i++, shared++) {
        int otherFileNum, fileNum;
        otherFileNum = shared->otherFileNum;
@@ -405,6 +353,7 @@ static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
 
        fi->actions[fileNum] = FA_SKIP;
     }
+/*@=boundswrite@*/
 
     mi = rpmdbFreeIterator(mi);
 
@@ -414,9 +363,10 @@ static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
 #define        ISROOT(_d)      (((_d)[0] == '/' && (_d)[1] == '\0') ? "" : (_d))
 
 /*@unchecked@*/
-static int _fps_debug = 0;
+int _fps_debug = 0;
 
 static int fpsCompare (const void * one, const void * two)
+       /*@*/
 {
     const struct fingerPrint_s * a = (const struct fingerPrint_s *)one;
     const struct fingerPrint_s * b = (const struct fingerPrint_s *)two;
@@ -432,6 +382,7 @@ static int fpsCompare (const void * one, const void * two)
     if (adnlen == 1 && asnlen != 0) adnlen = 0;
     if (bdnlen == 1 && bsnlen != 0) bdnlen = 0;
 
+/*@-boundswrite@*/
     afn = t = alloca(adnlen+asnlen+abnlen+2);
     if (adnlen) t = stpcpy(t, a->entry->dirName);
     *t++ = '/';
@@ -445,6 +396,7 @@ static int fpsCompare (const void * one, const void * two)
     if (b->subDir && bsnlen) t = stpcpy(t, b->subDir);
     if (bbnlen) t = stpcpy(t, b->baseName);
     if (bfn[0] == '/' && bfn[1] == '/') bfn++;
+/*@=boundswrite@*/
 
     rc = strcmp(afn, bfn);
 /*@-modfilesys@*/
@@ -510,7 +462,7 @@ otherFps->baseName);
 if (otherFileNum == otherFc) {
 /*@-modfilesys@*/
 if (_fps_debug)
-fprintf(stderr, "*** NULL %s/%s%s\n",
+fprintf(stderr, "*** FP_EQUAL NULL %s/%s%s\n",
 ISROOT(fiFps->entry->dirName),
 (fiFps->subDir ? fiFps->subDir : ""),
 fiFps->baseName);
@@ -523,10 +475,13 @@ fiFps->baseName);
 
     const struct fingerPrint_s * bingoFps;
 
+/*@-boundswrite@*/
     bingoFps = bsearch(fiFps, otherFps, otherFc, sizeof(*otherFps), fpsCompare);
+/*@=boundswrite@*/
     if (bingoFps == NULL) {
 /*@-modfilesys@*/
-fprintf(stderr, "*** NULL %s/%s%s\n",
+if (_fps_debug)
+fprintf(stderr, "*** bingoFps NULL %s/%s%s\n",
 ISROOT(fiFps->entry->dirName),
 (fiFps->subDir ? fiFps->subDir : ""),
 fiFps->baseName);
@@ -538,6 +493,7 @@ fiFps->baseName);
     /*@-nullpass@*/    /* LCL: looks good to me */
     if (!(fiFps == bingoFps || FP_EQUAL((*fiFps), (*bingoFps)))) {
 /*@-modfilesys@*/
+if (_fps_debug)
 fprintf(stderr, "***  BAD %s/%s%s\n",
 ISROOT(bingoFps->entry->dirName),
 (bingoFps->subDir ? bingoFps->subDir : ""),
@@ -554,40 +510,40 @@ bingoFps->baseName);
 }
 
 /**
- * Update disk space needs on each partition for this package.
+ * Update disk space needs on each partition for this package's files.
  */
 /* XXX only ts->{probs,di} modified */
-static void handleOverlappedFiles(const rpmTransactionSet ts,
-               const transactionElement p, TFI_t fi)
-       /*@globals fileSystem @*/
-       /*@modifies ts, fi, fileSystem @*/
+static void handleOverlappedFiles(const rpmts ts,
+               const rpmte p, rpmfi fi)
+       /*@globals fileSystem, internalState @*/
+       /*@modifies ts, fi, fileSystem, internalState @*/
 {
-    struct diskspaceInfo * ds = NULL;
     uint_32 fixupSize = 0;
+    rpmps ps;
     const char * fn;
     int i, j;
   
-    fi = tfiInit(fi, 0);
-    if (fi != NULL)    /* XXX lclint */
-    while ((i = tfiNext(fi)) >= 0) {
+    ps = rpmtsProblems(ts);
+    fi = rpmfiInit(fi, 0);
+    if (fi != NULL)
+    while ((i = rpmfiNext(fi)) >= 0) {
        struct fingerPrint_s * fiFps;
        int otherPkgNum, otherFileNum;
-       TFI_t otherFi;
-       const TFI_t * recs;
+       rpmfi otherFi;
+       int_32 FFlags;
+       int_16 FMode;
+       const rpmfi * recs;
        int numRecs;
 
        if (XFA_SKIPPING(fi->actions[i]))
            continue;
 
-       fn = tfiGetFN(fi);
+       fn = rpmfiFN(fi);
        fiFps = fi->fps + i;
+       FFlags = rpmfiFFlags(fi);
+       FMode = rpmfiFMode(fi);
 
-       if (ts->di) {
-           ds = ts->di;
-           while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
-           if (!ds->bsize) ds = NULL;
-           fixupSize = 0;
-       }
+       fixupSize = 0;
 
        /*
         * Retrieve all records that apply to this file. Note that the
@@ -601,6 +557,7 @@ static void handleOverlappedFiles(const rpmTransactionSet ts,
        /*
         * If this package is being added, look only at other packages
         * being added -- removed packages dance to a different tune.
+        *
         * If both this and the other package are being added, overlapped
         * files must be identical (or marked as a conflict). The
         * disposition of already installed config files leads to
@@ -632,31 +589,31 @@ static void handleOverlappedFiles(const rpmTransactionSet ts,
            otherFi = recs[otherPkgNum];
 
            /* Added packages need only look at other added packages. */
-           if (teGetType(p) == TR_ADDED && teGetType(otherFi->te) != TR_ADDED)
+           if (rpmteType(p) == TR_ADDED && rpmteType(otherFi->te) != TR_ADDED)
                /*@innercontinue@*/ continue;
 
            otherFps = otherFi->fps;
-           otherFc = tfiGetFC(otherFi);
+           otherFc = rpmfiFC(otherFi);
 
            otherFileNum = findFps(fiFps, otherFps, otherFc);
-           (void) tfiSetFX(otherFi, otherFileNum);
+           (void) rpmfiSetFX(otherFi, otherFileNum);
 
-           /* XXX is this test still necessary? */
+           /* XXX Happens iff fingerprint for incomplete package install. */
            if (otherFi->actions[otherFileNum] != FA_UNKNOWN)
                /*@innerbreak@*/ break;
        }
 
-       switch (teGetType(p)) {
+/*@-boundswrite@*/
+       switch (rpmteType(p)) {
        case TR_ADDED:
          { struct stat sb;
            if (otherPkgNum < 0) {
                /* XXX is this test still necessary? */
                if (fi->actions[i] != FA_UNKNOWN)
                    /*@switchbreak@*/ break;
-               if ((tfiGetFFlags(fi) & RPMFILE_CONFIG) && 
-                       !lstat(fn, &sb)) {
+               if ((FFlags & RPMFILE_CONFIG) && !lstat(fn, &sb)) {
                    /* Here is a non-overlapped pre-existing config file. */
-                   fi->actions[i] = (tfiGetFFlags(fi) & RPMFILE_NOREPLACE)
+                   fi->actions[i] = (FFlags & RPMFILE_NOREPLACE)
                        ? FA_ALTNAME : FA_BACKUP;
                } else {
                    fi->actions[i] = FA_CREATE;
@@ -666,22 +623,22 @@ static void handleOverlappedFiles(const rpmTransactionSet ts,
 
 assert(otherFi != NULL);
            /* Mark added overlapped non-identical files as a conflict. */
-           if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
+           if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES)
             && filecmp(otherFi, fi))
            {
-               rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT,
-                       teGetNEVR(p), teGetKey(p),
+               rpmpsAppend(ps, RPMPROB_NEW_FILE_CONFLICT,
+                       rpmteNEVR(p), rpmteKey(p),
                        fn, NULL,
-                       teGetNEVR(otherFi->te),
+                       rpmteNEVR(otherFi->te),
                        0);
            }
 
            /* Try to get the disk accounting correct even if a conflict. */
-           fixupSize = otherFi->fsizes[otherFileNum];
+           fixupSize = rpmfiFSize(otherFi);
 
-           if ((tfiGetFFlags(fi) & RPMFILE_CONFIG) && !lstat(fn, &sb)) {
+           if ((FFlags & RPMFILE_CONFIG) && !lstat(fn, &sb)) {
                /* Here is an overlapped  pre-existing config file. */
-               fi->actions[i] = (tfiGetFFlags(fi) & RPMFILE_NOREPLACE)
+               fi->actions[i] = (FFlags & RPMFILE_NOREPLACE)
                        ? FA_ALTNAME : FA_SKIP;
            } else {
                fi->actions[i] = FA_CREATE;
@@ -702,70 +659,32 @@ assert(otherFi != NULL);
            }
            if (XFA_SKIPPING(fi->actions[i]))
                /*@switchbreak@*/ break;
-           if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
+           if (rpmfiFState(fi) != RPMFILE_STATE_NORMAL)
                /*@switchbreak@*/ break;
-           if (!(S_ISREG(fi->fmodes[i]) && (tfiGetFFlags(fi) & RPMFILE_CONFIG))) {
+           if (!(S_ISREG(FMode) && (FFlags & RPMFILE_CONFIG))) {
                fi->actions[i] = FA_ERASE;
                /*@switchbreak@*/ break;
            }
                
            /* Here is a pre-existing modified config file that needs saving. */
            {   char md5sum[50];
-#ifdef DYING
-               if (fi->md5s != NULL) {
-#endif
-                   const unsigned char * md5 = fi->md5s + (16 * i);
-                   if (!domd5(fn, md5sum, 0) && memcmp(md5, md5sum, 16)) {
-                       fi->actions[i] = FA_BACKUP;
-                       /*@switchbreak@*/ break;
-                   }
-#ifdef DYING
-               } else {
-                   const char * fmd5 = fi->fmd5s[i];
-                   if (!domd5(fn, md5sum, 1) && strcmp(fmd5, md5sum)) {
-                       fi->actions[i] = FA_BACKUP;
-                       /*@switchbreak@*/ break;
-                   }
+               const unsigned char * MD5 = rpmfiMD5(fi);
+               if (!domd5(fn, md5sum, 0, NULL) && memcmp(MD5, md5sum, 16)) {
+                   fi->actions[i] = FA_BACKUP;
+                   /*@switchbreak@*/ break;
                }
-#endif
            }
            fi->actions[i] = FA_ERASE;
            /*@switchbreak@*/ break;
        }
+/*@=boundswrite@*/
 
-       if (ds) {
-           uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
-
-           switch (fi->actions[i]) {
-             case FA_BACKUP:
-             case FA_SAVE:
-             case FA_ALTNAME:
-               ds->ineeded++;
-               ds->bneeded += s;
-               /*@switchbreak@*/ break;
-
-           /*
-            * FIXME: If two packages share a file (same md5sum), and
-            * that file is being replaced on disk, will ds->bneeded get
-            * decremented twice? Quite probably!
-            */
-             case FA_CREATE:
-               ds->bneeded += s;
-               ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
-               /*@switchbreak@*/ break;
-
-             case FA_ERASE:
-               ds->ineeded--;
-               ds->bneeded -= s;
-               /*@switchbreak@*/ break;
+       /* Update disk space info for a file. */
+       rpmtsUpdateDSI(ts, fiFps->entry->dev, rpmfiFSize(fi),
+               fi->replacedSizes[i], fixupSize, fi->actions[i]);
 
-             default:
-               /*@switchbreak@*/ break;
-           }
-
-           ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
-       }
     }
+    ps = rpmpsFree(ps);
 }
 
 /**
@@ -775,13 +694,13 @@ assert(otherFi != NULL);
  * @param h            installed header
  * @return             0 if not newer, 1 if okay
  */
-static int ensureOlder(rpmTransactionSet ts,
-               const transactionElement p, const Header h)
+static int ensureOlder(rpmts ts,
+               const rpmte p, const Header h)
        /*@modifies ts @*/
 {
     int_32 reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
     const char * reqEVR;
-    rpmDepSet req;
+    rpmds req;
     char * t;
     int nb;
     int rc;
@@ -789,27 +708,31 @@ static int ensureOlder(rpmTransactionSet ts,
     if (p == NULL || h == NULL)
        return 1;
 
-    nb = strlen(teGetNEVR(p)) + (teGetE(p) != NULL ? strlen(teGetE(p)) : 0) + 1;
+/*@-boundswrite@*/
+    nb = strlen(rpmteNEVR(p)) + (rpmteE(p) != NULL ? strlen(rpmteE(p)) : 0) + 1;
     t = alloca(nb);
     *t = '\0';
     reqEVR = t;
-    if (teGetE(p) != NULL)     t = stpcpy( stpcpy(t, teGetE(p)), ":");
-    if (teGetV(p) != NULL)     t = stpcpy(t, teGetV(p));
+    if (rpmteE(p) != NULL)     t = stpcpy( stpcpy(t, rpmteE(p)), ":");
+    if (rpmteV(p) != NULL)     t = stpcpy(t, rpmteV(p));
     *t++ = '-';
-    if (teGetR(p) != NULL)     t = stpcpy(t, teGetR(p));
+    if (rpmteR(p) != NULL)     t = stpcpy(t, rpmteR(p));
+/*@=boundswrite@*/
     
-    req = dsSingle(RPMTAG_REQUIRENAME, teGetN(p), reqEVR, reqFlags);
-    rc = headerMatchesDepFlags(h, req);
-    req = dsFree(req);
+    req = rpmdsSingle(RPMTAG_REQUIRENAME, rpmteN(p), reqEVR, reqFlags);
+    rc = rpmdsNVRMatchesDep(h, req, _rpmds_nopromote);
+    req = rpmdsFree(req);
 
     if (rc == 0) {
+       rpmps ps = rpmtsProblems(ts);
        const char * altNEVR = hGetNEVR(h, NULL);
-       rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE,
-               teGetNEVR(p), teGetKey(p),
+       rpmpsAppend(ps, RPMPROB_OLDPACKAGE,
+               rpmteNEVR(p), rpmteKey(p),
                NULL, NULL,
                altNEVR,
                0);
        altNEVR = _free(altNEVR);
+       ps = rpmpsFree(ps);
        rc = 1;
     } else
        rc = 0;
@@ -818,13 +741,20 @@ static int ensureOlder(rpmTransactionSet ts,
 }
 
 /**
+ * Skip any files that do not match install policies.
+ * @param ts           transaction set
+ * @param fi           file info set
  */
 /*@-mustmod@*/ /* FIX: fi->actions is modified. */
-static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
+/*@-bounds@*/
+static void skipFiles(const rpmts ts, rpmfi fi)
        /*@globals rpmGlobalMacroContext @*/
        /*@modifies fi, rpmGlobalMacroContext @*/
 {
-    int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
+    uint_32 tscolor = rpmtsColor(ts);
+    uint_32 ficolor;
+    int noConfigs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCONFIGS);
+    int noDocs = (rpmtsFlags(ts) & RPMTRANS_FLAG_NODOCS);
     char ** netsharedPaths = NULL;
     const char ** languages;
     const char * dn, * bn;
@@ -858,22 +788,22 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
     /*@=branchstate@*/
 
     /* Compute directory refcount, skip directory if now empty. */
-    dc = tfiGetDC(fi);
+    dc = rpmfiDC(fi);
     drc = alloca(dc * sizeof(*drc));
     memset(drc, 0, dc * sizeof(*drc));
     dff = alloca(dc * sizeof(*dff));
     memset(dff, 0, dc * sizeof(*dff));
 
-    fi = tfiInit(fi, 0);
+    fi = rpmfiInit(fi, 0);
     if (fi != NULL)    /* XXX lclint */
-    while ((i = tfiNext(fi)) >= 0)
+    while ((i = rpmfiNext(fi)) >= 0)
     {
-       char **nsp;
+       char ** nsp;
 
-       bn = tfiGetBN(fi);
+       bn = rpmfiBN(fi);
        bnlen = strlen(bn);
-       ix = tfiGetDX(fi);
-       dn = tfiGetDN(fi);
+       ix = rpmfiDX(fi);
+       dn = rpmfiDN(fi);
        dnlen = strlen(dn);
        if (dn == NULL)
            continue;   /* XXX can't happen */
@@ -882,7 +812,15 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
 
        /* Don't bother with skipped files */
        if (XFA_SKIPPING(fi->actions[i])) {
-           drc[ix]--;
+           drc[ix]--; dff[ix] = 1;
+           continue;
+       }
+
+       /* Ignore colored files not in our rainbow. */
+       ficolor = rpmfiFColor(fi);
+       if (tscolor && ficolor && !(tscolor & ficolor)) {
+           drc[ix]--;  dff[ix] = 1;
+           fi->actions[i] = FA_SKIPCOLOR;
            continue;
        }
 
@@ -926,7 +864,7 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
        /*
         * Skip i18n language specific files.
         */
-       if (fi->flangs && languages && *fi->flangs[i]) {
+       if (languages != NULL && fi->flangs != NULL && *fi->flangs[i]) {
            const char **lang, *l, *le;
            for (lang = languages; *lang != NULL; lang++) {
                if (!strcmp(*lang, "all"))
@@ -949,9 +887,18 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
        }
 
        /*
+        * Skip config files if requested.
+        */
+       if (noConfigs && (rpmfiFFlags(fi) & RPMFILE_CONFIG)) {
+           drc[ix]--;  dff[ix] = 1;
+           fi->actions[i] = FA_SKIPNSTATE;
+           continue;
+       }
+
+       /*
         * Skip documentation if requested.
         */
-       if (noDocs && (tfiGetFFlags(fi) & RPMFILE_DOC)) {
+       if (noDocs && (rpmfiFFlags(fi) & RPMFILE_DOC)) {
            drc[ix]--;  dff[ix] = 1;
            fi->actions[i] = FA_SKIPNSTATE;
            continue;
@@ -963,8 +910,8 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
     if (fi != NULL)    /* XXX can't happen */
     for (j = 0; j < dc; j++)
 #else
-    if ((fi = tdiInit(fi)) != NULL)
-    while (j = tdiNext(fi) >= 0)
+    if ((fi = rpmfiInitD(fi)) != NULL)
+    while (j = rpmfiNextD(fi) >= 0)
 #endif
     {
 
@@ -981,23 +928,28 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
        }
 
        /* If explicitly included in the package, skip the directory. */
-       fi = tfiInit(fi, 0);
+       fi = rpmfiInit(fi, 0);
        if (fi != NULL)         /* XXX lclint */
-       while ((i = tfiNext(fi)) >= 0) {
-           const char * dir;
+       while ((i = rpmfiNext(fi)) >= 0) {
+           const char * fdn, * fbn;
+           int_16 fFMode;
 
            if (XFA_SKIPPING(fi->actions[i]))
                /*@innercontinue@*/ continue;
-           if (whatis(fi->fmodes[i]) != XDIR)
+
+           fFMode = rpmfiFMode(fi);
+
+           if (whatis(fFMode) != XDIR)
                /*@innercontinue@*/ continue;
-           dir = fi->dnl[fi->dil[i]];
-           if (strlen(dir) != dnlen)
+           fdn = rpmfiDN(fi);
+           if (strlen(fdn) != dnlen)
                /*@innercontinue@*/ continue;
-           if (strncmp(dir, dn, dnlen))
+           if (strncmp(fdn, dn, dnlen))
                /*@innercontinue@*/ continue;
-           if (strlen(fi->bnl[i]) != bnlen)
+           fbn = rpmfiBN(fi);
+           if (strlen(fbn) != bnlen)
                /*@innercontinue@*/ continue;
-           if (strncmp(fi->bnl[i], bn, bnlen))
+           if (strncmp(fbn, bn, bnlen))
                /*@innercontinue@*/ continue;
            rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
            fi->actions[i] = FA_SKIPNSTATE;
@@ -1005,31 +957,34 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
        }
     }
 
+/*@-dependenttrans@*/
     if (netsharedPaths) freeSplitString(netsharedPaths);
 #ifdef DYING   /* XXX freeFi will deal with this later. */
     fi->flangs = _free(fi->flangs);
 #endif
     if (languages) freeSplitString((char **)languages);
+/*@=dependenttrans@*/
 }
+/*@=bounds@*/
 /*@=mustmod@*/
 
 /**
  * Return transaction element's file info.
- * @todo Take a TFI_t refcount here.
- * @param tei          transaction element iterator
+ * @todo Take a rpmfi refcount here.
+ * @param tsi          transaction element iterator
  * @return             transaction element file info
  */
 static /*@null@*/
-TFI_t teiGetFi(const teIterator tei)
+rpmfi rpmtsiFi(const rpmtsi tsi)
        /*@*/
 {
-    TFI_t fi = NULL;
+    rpmfi fi = NULL;
 
-    if (tei != NULL && tei->ocsave != -1) {
-       /*@-type -abstract@*/ /* FIX: transactionElement not opaque */
-       transactionElement te = tei->ts->order[tei->ocsave];
+    if (tsi != NULL && tsi->ocsave != -1) {
+       /*@-type -abstract@*/ /* FIX: rpmte not opaque */
+       rpmte te = rpmtsElement(tsi->ts, tsi->ocsave);
        /*@-assignexpose@*/
-       if ((fi = te->fi) != NULL)
+       if (te != NULL && (fi = te->fi) != NULL)
            fi->te = te;
        /*@=assignexpose@*/
        /*@=type =abstract@*/
@@ -1039,108 +994,67 @@ TFI_t teiGetFi(const teIterator tei)
     /*@=compdef =refcounttrans =usereleased @*/
 }
 
-#define        NOTIFY(_ts, _al)        if ((_ts)->notify) (void) (_ts)->notify _al
+#define        NOTIFY(_ts, _al) /*@i@*/ if ((_ts)->notify) (void) (_ts)->notify _al
 
-int rpmRunTransactions(        rpmTransactionSet ts,
-                       rpmProblemSet okProbs,
-                       rpmprobFilterFlags ignoreSet)
+int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
 {
+    uint_32 tscolor = rpmtsColor(ts);
     int i, j;
     int ourrc = 0;
     int totalFileCount = 0;
-    TFI_t fi;
-    struct diskspaceInfo * dip;
+    rpmfi fi;
     sharedFileInfo shared, sharedList;
     int numShared;
     int nexti;
-    alKey lastKey;
+    alKey lastFailKey;
     fingerPrintCache fpc;
-    PSM_t psm = memset(alloca(sizeof(*psm)), 0, sizeof(*psm));
-    teIterator pi;     transactionElement p;
-    teIterator qi;     transactionElement q;
+    rpmps ps;
+    rpmpsm psm;
+    rpmtsi pi; rpmte p;
+    rpmtsi qi; rpmte q;
+    int numAdded;
+    int numRemoved;
     int xx;
 
-    /* FIXME: what if the same package is included in ts twice? */
-
-    if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
-       ts->transFlags |= (_noTransScripts | _noTransTriggers);
-    if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
-       ts->transFlags |= _noTransTriggers;
-
-    /* XXX MULTILIB is broken, as packages can and do execute /sbin/ldconfig. */
-    if (ts->transFlags & (RPMTRANS_FLAG_JUSTDB | RPMTRANS_FLAG_MULTILIB))
-       ts->transFlags |= (_noTransScripts | _noTransTriggers);
+    /* XXX programmer error segfault avoidance. */
+    if (rpmtsNElements(ts) <= 0)
+       return -1;
 
-    ts->probs = rpmProblemSetFree(ts->probs);
-    ts->probs = rpmProblemSetCreate();
-    ts->ignoreSet = ignoreSet;
-    ts->currDir = _free(ts->currDir);
-    ts->currDir = currentDirectory();
-    ts->chrootDone = 0;
-    if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
-    ts->id = (int_32) time(NULL);
+    if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOSCRIPTS)
+       (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
+    if (rpmtsFlags(ts) & RPMTRANS_FLAG_NOTRIGGERS)
+       (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransTriggers));
 
-    memset(psm, 0, sizeof(*psm));
-    psm->ts = rpmtsLink(ts, "tsRun");
+    if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)
+       (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
 
-    /* Get available space on mounted file systems. */
-    if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
-               !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount))
-    {
-       struct stat sb;
+    ts->probs = rpmpsFree(ts->probs);
+    ts->probs = rpmpsCreate();
 
-       rpmMessage(RPMMESS_DEBUG, _("getting list of mounted filesystems\n"));
+    /* XXX Make sure the database is open RDWR for package install/erase. */
+    {  int dbmode = (rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)
+               ? O_RDONLY : (O_RDWR|O_CREAT);
 
-       ts->di = _free(ts->di);
-       dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
+       /* Open database RDWR for installing packages. */
+       if (rpmtsOpenDB(ts, dbmode))
+           return -1;  /* XXX W2DO? */
+    }
 
-       for (i = 0; (i < ts->filesystemCount) && dip; i++) {
-#if STATFS_IN_SYS_STATVFS
-           struct statvfs sfb;
-           memset(&sfb, 0, sizeof(sfb));
-           if (statvfs(ts->filesystems[i], &sfb))
-#else
-           struct statfs sfb;
-#  if STAT_STATFS4
-/* This platform has the 4-argument version of the statfs call.  The last two
- * should be the size of struct statfs and 0, respectively.  The 0 is the
- * filesystem type, and is always 0 when statfs is called on a mounted
- * filesystem, as we're doing.
- */
-           memset(&sfb, 0, sizeof(sfb));
-           if (statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
-#  else
-           memset(&sfb, 0, sizeof(sfb));
-           if (statfs(ts->filesystems[i], &sfb))
-#  endif
-#endif
-           {
-               dip = NULL;
-           } else {
-               ts->di[i].bsize = sfb.f_bsize;
-               ts->di[i].bneeded = 0;
-               ts->di[i].ineeded = 0;
-#ifdef STATFS_HAS_F_BAVAIL
-               ts->di[i].bavail = sfb.f_bavail;
-#else
-/* FIXME: the statfs struct doesn't have a member to tell how many blocks are
- * available for non-superusers.  f_blocks - f_bfree is probably too big, but
- * it's about all we can do.
- */
-               ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
-#endif
-               /* XXX Avoid FAT and other file systems that have not inodes. */
-               ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
-                               ? sfb.f_ffree : -1;
+    ts->ignoreSet = ignoreSet;
+    {  const char * currDir = currentDirectory();
+       rpmtsSetCurrDir(ts, currDir);
+       currDir = _free(currDir);
+    }
 
-               xx = stat(ts->filesystems[i], &sb);
-               ts->di[i].dev = sb.st_dev;
-           }
-       }
+    (void) rpmtsSetChrootDone(ts, 0);
 
-       if (dip) ts->di[i].bsize = 0;
+    {  int_32 tid = (int_32) time(NULL);
+       (void) rpmtsSetTid(ts, tid);
     }
 
+    /* Get available space on mounted file systems. */
+    xx = rpmtsInitDSI(ts);
+
     /* ===============================================
      * For packages being installed:
      * - verify package arch/os.
@@ -1149,49 +1063,59 @@ int rpmRunTransactions( rpmTransactionSet ts,
      * For packages being removed:
      * - count files.
      */
+
+rpmMessage(RPMMESS_DEBUG, _("sanity checking %d elements\n"), rpmtsNElements(ts));
+    ps = rpmtsProblems(ts);
     /* The ordering doesn't matter here */
-    pi = teInitIterator(ts);
-    while ((p = teNext(pi, TR_ADDED)) != NULL) {
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
        rpmdbMatchIterator mi;
        int fc;
 
-       if ((fi = teiGetFi(pi)) == NULL)
+       if ((fi = rpmtsiFi(pi)) == NULL)
            continue;   /* XXX can't happen */
-       fc = tfiGetFC(fi);
+       fc = rpmfiFC(fi);
 
-       if (!(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
-           if (!archOkay(teGetA(p)))
-               rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH,
-                       teGetNEVR(p), teGetKey(p),
-                       teGetA(p), NULL,
+       if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_IGNOREARCH) && !tscolor)
+           if (!archOkay(rpmteA(p)))
+               rpmpsAppend(ps, RPMPROB_BADARCH,
+                       rpmteNEVR(p), rpmteKey(p),
+                       rpmteA(p), NULL,
                        NULL, 0);
 
-       if (!(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
-           if (!osOkay(teGetO(p)))
-               rpmProblemSetAppend(ts->probs, RPMPROB_BADOS,
-                       teGetNEVR(p), teGetKey(p),
-                       teGetO(p), NULL,
+       if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_IGNOREOS))
+           if (!osOkay(rpmteO(p)))
+               rpmpsAppend(ps, RPMPROB_BADOS,
+                       rpmteNEVR(p), rpmteKey(p),
+                       rpmteO(p), NULL,
                        NULL, 0);
 
-       if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
+       if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_OLDPACKAGE)) {
            Header h;
-           mi = rpmtsInitIterator(ts, RPMTAG_NAME, teGetN(p), 0);
+           mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
            while ((h = rpmdbNextIterator(mi)) != NULL)
                xx = ensureOlder(ts, p, h);
            mi = rpmdbFreeIterator(mi);
        }
 
-       /* XXX multilib should not display "already installed" problems */
-       if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG) && !teGetMultiLib(p)) {
-           mi = rpmtsInitIterator(ts, RPMTAG_NAME, teGetN(p), 0);
+       if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEPKG)) {
+           mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmteN(p), 0);
+           xx = rpmdbSetIteratorRE(mi, RPMTAG_EPOCH, RPMMIRE_DEFAULT,
+                               rpmteE(p));
            xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT,
-                               teGetV(p));
+                               rpmteV(p));
            xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
-                               teGetR(p));
+                               rpmteR(p));
+           if (tscolor) {
+               xx = rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_DEFAULT,
+                               rpmteA(p));
+               xx = rpmdbSetIteratorRE(mi, RPMTAG_OS, RPMMIRE_DEFAULT,
+                               rpmteO(p));
+           }
 
            while (rpmdbNextIterator(mi) != NULL) {
-               rpmProblemSetAppend(ts->probs, RPMPROB_PKG_INSTALLED,
-                       teGetNEVR(p), teGetKey(p),
+               rpmpsAppend(ps, RPMPROB_PKG_INSTALLED,
+                       rpmteNEVR(p), rpmteKey(p),
                        NULL, NULL,
                        NULL, 0);
                /*@innerbreak@*/ break;
@@ -1203,20 +1127,21 @@ int rpmRunTransactions( rpmTransactionSet ts,
        totalFileCount += fc;
 
     }
-    pi = teFreeIterator(pi);
+    pi = rpmtsiFree(pi);
+    ps = rpmpsFree(ps);
 
     /* The ordering doesn't matter here */
-    pi = teInitIterator(ts);
-    while ((p = teNext(pi, TR_REMOVED)) != NULL) {
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
        int fc;
 
-       if ((fi = teiGetFi(pi)) == NULL)
+       if ((fi = rpmtsiFi(pi)) == NULL)
            continue;   /* XXX can't happen */
-       fc = tfiGetFC(fi);
+       fc = rpmfiFC(fi);
 
        totalFileCount += fc;
     }
-    pi = teFreeIterator(pi);
+    pi = rpmtsiFree(pi);
 
     /* ===============================================
      * Initialize transaction element file info for package:
@@ -1227,44 +1152,45 @@ int rpmRunTransactions( rpmTransactionSet ts,
      * calling fpLookupList only once. I'm not sure that the speedup is
      * worth the trouble though.
      */
-    pi = teInitIterator(ts);
-    while ((p = teNextIterator(pi)) != NULL) {
+rpmMessage(RPMMESS_DEBUG, _("computing %d file fingerprints\n"), totalFileCount);
+
+    numAdded = numRemoved = 0;
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
        int fc;
 
-       if ((fi = teiGetFi(pi)) == NULL)
+       if ((fi = rpmtsiFi(pi)) == NULL)
            continue;   /* XXX can't happen */
-       fc = tfiGetFC(fi);
-
-#ifdef DYING   /* XXX W2DO? this is now done in teiGetFi, okay ??? */
-       fi->magic = TFIMAGIC;
-       fi->te = p;
-#endif
+       fc = rpmfiFC(fi);
 
        /*@-branchstate@*/
-       switch (teGetType(p)) {
+       switch (rpmteType(p)) {
        case TR_ADDED:
+           numAdded++;
            fi->record = 0;
            /* Skip netshared paths, not our i18n files, and excluded docs */
            if (fc > 0)
                skipFiles(ts, fi);
            /*@switchbreak@*/ break;
        case TR_REMOVED:
-           fi->record = teGetDBOffset(p);
+           numRemoved++;
+           fi->record = rpmteDBOffset(p);
            /*@switchbreak@*/ break;
        }
        /*@=branchstate@*/
 
        fi->fps = (fc > 0 ? xmalloc(fc * sizeof(*fi->fps)) : NULL);
     }
-    pi = teFreeIterator(pi);
+    pi = rpmtsiFree(pi);
 
-    if (!ts->chrootDone) {
+    if (!rpmtsChrootDone(ts)) {
+       const char * rootDir = rpmtsRootDir(ts);
        xx = chdir("/");
        /*@-superuser -noeffect @*/
-       xx = chroot(ts->rootDir);
+       if (rootDir != NULL)
+           xx = chroot(rootDir);
        /*@=superuser =noeffect @*/
-       ts->chrootDone = 1;
-       if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
+       (void) rpmtsSetChrootDone(ts, 1);
     }
 
     ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
@@ -1273,19 +1199,22 @@ int rpmRunTransactions( rpmTransactionSet ts,
     /* ===============================================
      * Add fingerprint for each file not skipped.
      */
-    pi = teInitIterator(ts);
-    while ((p = teNextIterator(pi)) != NULL) {
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
        int fc;
 
-       if ((fi = teiGetFi(pi)) == NULL)
+       (void) rpmdbCheckSignals();
+
+       if ((fi = rpmtsiFi(pi)) == NULL)
            continue;   /* XXX can't happen */
-       fc = tfiGetFC(fi);
+       fc = rpmfiFC(fi);
 
+       (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
        fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fc, fi->fps);
        /*@-branchstate@*/
-       fi = tfiInit(fi, 0);
+       fi = rpmfiInit(fi, 0);
        if (fi != NULL)         /* XXX lclint */
-       while ((i = tfiNext(fi)) >= 0) {
+       while ((i = rpmfiNext(fi)) >= 0) {
            if (XFA_SKIPPING(fi->actions[i]))
                /*@innercontinue@*/ continue;
            /*@-dependenttrans@*/
@@ -1293,51 +1222,54 @@ int rpmRunTransactions( rpmTransactionSet ts,
            /*@=dependenttrans@*/
        }
        /*@=branchstate@*/
+       (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), fc);
+
     }
-    pi = teFreeIterator(pi);
+    pi = rpmtsiFree(pi);
 
-    /*@-noeffectuncon @*/ /* FIX: check rc */
     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->orderCount,
        NULL, ts->notifyData));
-    /*@=noeffectuncon@*/
 
     /* ===============================================
      * Compute file disposition for each package in transaction set.
      */
-    pi = teInitIterator(ts);
-    while ((p = teNextIterator(pi)) != NULL) {
+rpmMessage(RPMMESS_DEBUG, _("computing file dispositions\n"));
+    ps = rpmtsProblems(ts);
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
        dbiIndexSet * matches;
        int knownBad;
        int fc;
 
-       if ((fi = teiGetFi(pi)) == NULL)
+       (void) rpmdbCheckSignals();
+
+       if ((fi = rpmtsiFi(pi)) == NULL)
            continue;   /* XXX can't happen */
-       fc = tfiGetFC(fi);
+       fc = rpmfiFC(fi);
 
-       /*@-noeffectuncon @*/ /* FIX: check rc */
-       NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, teiGetOc(pi),
+       NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, rpmtsiOc(pi),
                        ts->orderCount, NULL, ts->notifyData));
-       /*@=noeffectuncon@*/
 
        if (fc == 0) continue;
 
+       (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
        /* Extract file info for all files in this package from the database. */
        matches = xcalloc(fc, sizeof(*matches));
-       if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fc)) {
-           psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
+       if (rpmdbFindFpList(rpmtsGetRdb(ts), fi->fps, matches, fc)) {
+           ps = rpmpsFree(ps);
            return 1;   /* XXX WTFO? */
        }
 
        numShared = 0;
-       fi = tfiInit(fi, 0);
-       while ((i = tfiNext(fi)) >= 0)
+       fi = rpmfiInit(fi, 0);
+       while ((i = rpmfiNext(fi)) >= 0)
            numShared += dbiIndexSetCount(matches[i]);
 
        /* Build sorted file info list for this package. */
        shared = sharedList = xcalloc((numShared + 1), sizeof(*sharedList));
 
-       fi = tfiInit(fi, 0);
-       while ((i = tfiNext(fi)) >= 0) {
+       fi = rpmfiInit(fi, 0);
+       while ((i = rpmfiNext(fi)) >= 0) {
            /*
             * Take care not to mark files as replaced in packages that will
             * have been removed before we will get here.
@@ -1346,14 +1278,14 @@ int rpmRunTransactions( rpmTransactionSet ts,
                int ro;
                ro = dbiIndexRecordOffset(matches[i], j);
                knownBad = 0;
-               qi = teInitIterator(ts);
-               while ((q = teNext(qi, TR_REMOVED)) != NULL) {
+               qi = rpmtsiInit(ts);
+               while ((q = rpmtsiNext(qi, TR_REMOVED)) != NULL) {
                    if (ro == knownBad)
                        /*@innerbreak@*/ break;
-                   if (teGetDBOffset(q) == ro)
+                   if (rpmteDBOffset(q) == ro)
                        knownBad = ro;
                }
-               qi = teFreeIterator(qi);
+               qi = rpmtsiFree(qi);
 
                shared->pkgFileNum = i;
                shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
@@ -1394,10 +1326,10 @@ int rpmRunTransactions( rpmTransactionSet ts,
            }
 
            /* Determine the fate of each file. */
-           switch (teGetType(p)) {
+           switch (rpmteType(p)) {
            case TR_ADDED:
                xx = handleInstInstalledFiles(ts, p, fi, shared, nexti - i,
-       !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
+       !(beingRemoved || (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES)));
                /*@switchbreak@*/ break;
            case TR_REMOVED:
                if (!beingRemoved)
@@ -1413,225 +1345,265 @@ int rpmRunTransactions(       rpmTransactionSet ts,
        handleOverlappedFiles(ts, p, fi);
 
        /* Check added package has sufficient space on each partition used. */
-       switch (teGetType(p)) {
+       switch (rpmteType(p)) {
        case TR_ADDED:
-           if (!(ts->di && tfiGetFC(fi) > 0))
-               /*@switchbreak@*/ break;
-           for (i = 0; i < ts->filesystemCount; i++) {
-
-               dip = ts->di + i;
-
-               /* XXX Avoid FAT and other file systems that have not inodes. */
-               if (dip->iavail <= 0)
-                   /*@innercontinue@*/ continue;
-
-               if (adj_fs_blocks(dip->bneeded) > dip->bavail) {
-                   rpmProblemSetAppend(ts->probs, RPMPROB_DISKSPACE,
-                               teGetNEVR(p), teGetKey(p),
-                               ts->filesystems[i], NULL, NULL,
-                  (adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
-               }
-
-               if (adj_fs_blocks(dip->ineeded) > dip->iavail) {
-                   rpmProblemSetAppend(ts->probs, RPMPROB_DISKNODES,
-                               teGetNEVR(p), teGetKey(p),
-                               ts->filesystems[i], NULL, NULL,
-                   (adj_fs_blocks(dip->ineeded) - dip->iavail));
-               }
-           }
+           rpmtsCheckDSIProblems(ts, p);
            /*@switchbreak@*/ break;
        case TR_REMOVED:
            /*@switchbreak@*/ break;
        }
+       (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), fc);
     }
-    pi = teFreeIterator(pi);
+    pi = rpmtsiFree(pi);
+    ps = rpmpsFree(ps);
 
-    if (ts->chrootDone) {
+    if (rpmtsChrootDone(ts)) {
+       const char * currDir = rpmtsCurrDir(ts);
        /*@-superuser -noeffect @*/
        xx = chroot(".");
        /*@=superuser =noeffect @*/
-       ts->chrootDone = 0;
-       if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
-       xx = chdir(ts->currDir);
+       (void) rpmtsSetChrootDone(ts, 0);
+       if (currDir != NULL)
+           xx = chdir(currDir);
     }
 
-    /*@-noeffectuncon @*/ /* FIX: check rc */
     NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->orderCount,
        NULL, ts->notifyData));
-    /*@=noeffectuncon @*/
 
     /* ===============================================
      * Free unused memory as soon as possible.
      */
-    pi = teInitIterator(ts);
-    while ((p = teNextIterator(pi)) != NULL) {
-       if ((fi = teiGetFi(pi)) == NULL)
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
+       if ((fi = rpmtsiFi(pi)) == NULL)
            continue;   /* XXX can't happen */
-       if (tfiGetFC(fi) == 0)
+       if (rpmfiFC(fi) == 0)
            continue;
        fi->fps = _free(fi->fps);
     }
-    pi = teFreeIterator(pi);
+    pi = rpmtsiFree(pi);
 
-    fpCacheFree(fpc);
-    htFree(ts->ht);
-    ts->ht = NULL;
+    fpc = fpCacheFree(fpc);
+    ts->ht = htFree(ts->ht);
 
     /* ===============================================
      * If unfiltered problems exist, free memory and return.
      */
-    if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS)
+    if ((rpmtsFlags(ts) & RPMTRANS_FLAG_BUILD_PROBS)
      || (ts->probs->numProblems &&
-               (okProbs != NULL || rpmProblemSetTrim(ts->probs, okProbs)))
+               (okProbs != NULL || rpmpsTrim(ts->probs, okProbs)))
        )
     {
-       if (psm->ts != NULL)
-           psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
        return ts->orderCount;
     }
 
     /* ===============================================
      * Save removed files before erasing.
      */
-    if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
-       pi = teInitIterator(ts);
-       while ((p = teNextIterator(pi)) != NULL) {
-           fi = teiGetFi(pi);
-           switch (teGetType(p)) {
+    if (rpmtsFlags(ts) & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
+       int progress;
+
+       progress = 0;
+       pi = rpmtsiInit(ts);
+       while ((p = rpmtsiNext(pi, 0)) != NULL) {
+
+           (void) rpmdbCheckSignals();
+
+           if ((fi = rpmtsiFi(pi)) == NULL)
+               continue;       /* XXX can't happen */
+           switch (rpmteType(p)) {
            case TR_ADDED:
                /*@switchbreak@*/ break;
            case TR_REMOVED:
-               if (!(ts->transFlags & RPMTRANS_FLAG_REPACKAGE))
+               if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_REPACKAGE))
                    /*@switchbreak@*/ break;
-               psm->te = p;
-               psm->fi = rpmfiLink(fi, "tsRepackage");
-               xx = psmStage(psm, PSM_PKGSAVE);
-               (void) rpmfiUnlink(fi, "tsRepackage");
-               psm->fi = NULL;
-               psm->te = NULL;
+               if (!progress)
+                   NOTIFY(ts, (NULL, RPMCALLBACK_REPACKAGE_START,
+                               7, numRemoved, NULL, ts->notifyData));
+
+               NOTIFY(ts, (NULL, RPMCALLBACK_REPACKAGE_PROGRESS, progress,
+                       numRemoved, NULL, ts->notifyData));
+               progress++;
+
+               (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_REPACKAGE), 0);
+
+       /* XXX TR_REMOVED needs CPIO_MAP_{ABSOLUTE,ADDDOT} CPIO_ALL_HARDLINKS */
+               fi->mapflags |= CPIO_MAP_ABSOLUTE;
+               fi->mapflags |= CPIO_MAP_ADDDOT;
+               fi->mapflags |= CPIO_ALL_HARDLINKS;
+               psm = rpmpsmNew(ts, p, fi);
+               xx = rpmpsmStage(psm, PSM_PKGSAVE);
+               psm = rpmpsmFree(psm);
+               fi->mapflags &= ~CPIO_MAP_ABSOLUTE;
+               fi->mapflags &= ~CPIO_MAP_ADDDOT;
+               fi->mapflags &= ~CPIO_ALL_HARDLINKS;
+
+               (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_REPACKAGE), 0);
+
                /*@switchbreak@*/ break;
            }
        }
-       pi = teFreeIterator(pi);
+       pi = rpmtsiFree(pi);
+       if (progress) {
+           NOTIFY(ts, (NULL, RPMCALLBACK_REPACKAGE_STOP, 7, numRemoved,
+                       NULL, ts->notifyData));
+       }
     }
 
     /* ===============================================
      * Install and remove packages.
      */
-    lastKey = (alKey)-2;       /* erased packages have -1 */
-    pi = teInitIterator(ts);
+    lastFailKey = (alKey)-2;   /* erased packages have -1 */
+    pi = rpmtsiInit(ts);
     /*@-branchstate@*/ /* FIX: fi reload needs work */
-    while ((p = teNextIterator(pi)) != NULL) {
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
        alKey pkgKey;
-       Header h;
        int gotfd;
 
+       (void) rpmdbCheckSignals();
+
        gotfd = 0;
-       if ((fi = teiGetFi(pi)) == NULL)
+       if ((fi = rpmtsiFi(pi)) == NULL)
            continue;   /* XXX can't happen */
        
-       psm->te = p;
-       psm->fi = rpmfiLink(fi, "tsInstall");
-       switch (teGetType(p)) {
+       psm = rpmpsmNew(ts, p, fi);
+assert(psm != NULL);
+       psm->unorderedSuccessor =
+               (rpmtsiOc(pi) >= rpmtsUnorderedSuccessors(ts, -1) ? 1 : 0);
+
+       switch (rpmteType(p)) {
        case TR_ADDED:
+           (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_INSTALL), 0);
 
-           pkgKey = teGetAddedKey(p);
+           pkgKey = rpmteAddedKey(p);
 
-           rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", teGetNEVR(p));
-           h = NULL;
-           /*@-type@*/ /* FIX: transactionElement not opaque */
+           rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", rpmteNEVR(p));
+           p->h = NULL;
+           /*@-type@*/ /* FIX: rpmte not opaque */
            {
                /*@-noeffectuncon@*/ /* FIX: notify annotations */
-               p->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
-                               teGetKey(p), ts->notifyData);
+               p->fd = ts->notify(p->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
+                               rpmteKey(p), ts->notifyData);
                /*@=noeffectuncon@*/
-               if (teGetFd(p) != NULL) {
+               if (rpmteFd(p) != NULL) {
+                   rpmVSFlags ovsflags = rpmtsVSFlags(ts);
+                   rpmVSFlags vsflags = ovsflags | RPMVSF_NEEDPAYLOAD;
                    rpmRC rpmrc;
 
-                   rpmrc = rpmReadPackageFile(ts, teGetFd(p),
-                               "rpmRunTransactions", &h);
+                   ovsflags = rpmtsSetVSFlags(ts, vsflags);
+                   rpmrc = rpmReadPackageFile(ts, rpmteFd(p),
+                               rpmteNEVR(p), &p->h);
+                   vsflags = rpmtsSetVSFlags(ts, ovsflags);
 
-                   if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
+                   switch (rpmrc) {
+                   default:
                        /*@-noeffectuncon@*/ /* FIX: notify annotations */
-                       p->fd = ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
+                       p->fd = ts->notify(p->h, RPMCALLBACK_INST_CLOSE_FILE,
                                        0, 0,
-                                       teGetKey(p), ts->notifyData);
+                                       rpmteKey(p), ts->notifyData);
                        /*@=noeffectuncon@*/
                        p->fd = NULL;
                        ourrc++;
+                       /*@innerbreak@*/ break;
+                   case RPMRC_NOTTRUSTED:
+                   case RPMRC_NOKEY:
+                   case RPMRC_OK:
+                       /*@innerbreak@*/ break;
                    }
-                   if (teGetFd(p) != NULL) gotfd = 1;
+                   if (rpmteFd(p) != NULL) gotfd = 1;
                }
            }
            /*@=type@*/
 
-           if (teGetFd(p) != NULL) {
+           if (rpmteFd(p) != NULL) {
+               /*
+                * XXX Sludge necessary to tranfer existing fstates/actions
+                * XXX around a recreated file info set.
+                */
+               psm->fi = rpmfiFree(psm->fi);
                {
-char * fstates = fi->fstates;
-fileAction * actions = fi->actions;
-
-fi->fstates = NULL;
-fi->actions = NULL;
-                   (void) fiFree(fi, 0);
-/*@-usereleased@*/
-fi->magic = TFIMAGIC;
-fi->te = p;
-fi->record = 0;
-                   (void) fiNew(ts, fi, h, RPMTAG_BASENAMES, 1);
-fi->fstates = _free(fi->fstates);
-fi->fstates = fstates;
-fi->actions = _free(fi->actions);
-fi->actions = actions;
-/*@=usereleased@*/
-
+                   char * fstates = fi->fstates;
+                   fileAction * actions = fi->actions;
+                   rpmte savep;
+
+                   fi->fstates = NULL;
+                   fi->actions = NULL;
+/*@-nullstate@*/ /* FIX: fi->actions is NULL */
+                   fi = rpmfiFree(fi);
+/*@=nullstate@*/
+
+                   savep = rpmtsSetRelocateElement(ts, p);
+                   fi = rpmfiNew(ts, p->h, RPMTAG_BASENAMES, 1);
+                   (void) rpmtsSetRelocateElement(ts, savep);
+
+                   if (fi != NULL) {   /* XXX can't happen */
+                       fi->te = p;
+                       fi->fstates = _free(fi->fstates);
+                       fi->fstates = fstates;
+                       fi->actions = _free(fi->actions);
+                       fi->actions = actions;
+                       p->fi = fi;
+                   }
                }
-               if (teGetMultiLib(p))
-                   ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
-               else
-                   ts->transFlags &= ~RPMTRANS_FLAG_MULTILIB;
+               psm->fi = rpmfiLink(p->fi, NULL);
 
-               if (psmStage(psm, PSM_PKGINSTALL)) {
+/*@-nullstate@*/ /* FIX: psm->fi may be NULL */
+               if (rpmpsmStage(psm, PSM_PKGINSTALL)) {
                    ourrc++;
-                   lastKey = pkgKey;
+                   lastFailKey = pkgKey;
                }
-               fi->h = headerFree(fi->h, "TR_ADDED fi->h free");
+/*@=nullstate@*/
            } else {
                ourrc++;
-               lastKey = pkgKey;
+               lastFailKey = pkgKey;
            }
 
-           h = headerFree(h, "TR_ADDED h free");
-
            if (gotfd) {
                /*@-noeffectuncon @*/ /* FIX: check rc */
-               (void) ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
-                       teGetKey(p), ts->notifyData);
+               (void) ts->notify(p->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
+                       rpmteKey(p), ts->notifyData);
                /*@=noeffectuncon @*/
                /*@-type@*/
                p->fd = NULL;
                /*@=type@*/
            }
-           (void) fiFree(fi, 0);
+
+           p->h = headerFree(p->h);
+
+           (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_INSTALL), 0);
+
            /*@switchbreak@*/ break;
+
        case TR_REMOVED:
-           rpmMessage(RPMMESS_DEBUG, "========== --- %s\n", teGetNEVR(p));
-           /* If install failed, then we shouldn't erase. */
-           if (teGetDependsOnKey(p) != lastKey) {
-               if (psmStage(psm, PSM_PKGERASE))
+           (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_ERASE), 0);
+
+           rpmMessage(RPMMESS_DEBUG, "========== --- %s\n", rpmteNEVR(p));
+           /*
+            * XXX This has always been a hack, now mostly broken.
+            * If install failed, then we shouldn't erase.
+            */
+           if (rpmteDependsOnKey(p) != lastFailKey) {
+               if (rpmpsmStage(psm, PSM_PKGERASE))
                    ourrc++;
            }
-           (void) fiFree(fi, 0);
+
+           (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_ERASE), 0);
+
            /*@switchbreak@*/ break;
        }
-       xx = rpmdbSync(ts->rpmdb);
-       (void) rpmfiUnlink(psm->fi, "tsInstall");
-       psm->fi = NULL;
-       psm->te = NULL;
+       xx = rpmdbSync(rpmtsGetRdb(ts));
+
+/*@-nullstate@*/ /* FIX: psm->fi may be NULL */
+       psm = rpmpsmFree(psm);
+/*@=nullstate@*/
+
+/*@-type@*/ /* FIX: p is almost opaque */
+       p->fi = rpmfiFree(p->fi);
+/*@=type@*/
+
     }
     /*@=branchstate@*/
-    pi = teFreeIterator(pi);
-
-    psm->ts = rpmtsUnlink(psm->ts, "tsRun");
+    pi = rpmtsiFree(pi);
 
     /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
     if (ourrc)