fix: prevent segfault if malicious server sends 1 GB of data through ftpNLST.
[platform/upstream/rpm.git] / lib / transaction.c
index 647ad5b..d019de4 100644 (file)
-/** \ingroup rpmtrans
+/** \ingroup rpmts
  * \file lib/transaction.c
  */
 
 #include "system.h"
+#include <rpmlib.h>
 
-#include "psm.h"
-#include "rpmal.h"
 #include <rpmmacro.h>  /* XXX for rpmExpand */
 
-#include "fprint.h"
-#include "legacy.h"    /* XXX mdfile */
-#include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
+#include "fsm.h"
+#include "psm.h"
+
 #include "rpmdb.h"
 
-/*@-redecl -exportheadervar@*/
-/*@unchecked@*/
-extern const char * chroot_prefix;
-/*@=redecl =exportheadervar@*/
-
-/* 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 "rpmds.h"
 
-#include "debug.h"
+#define        _RPMFI_INTERNAL
+#include "rpmfi.h"
 
-/*@access FD_t@*/              /* XXX compared with NULL */
-/*@access Header@*/            /* XXX compared with NULL */
-/*@access rpmProblemSet@*/     /* XXX need rpmProblemSetOK() */
-/*@access dbiIndexSet@*/
-/*@access rpmdb@*/
-/*@access rpmTransactionSet@*/
-/*@access TFI_t@*/
-/*@access PSM_t@*/
+#define        _RPMTE_INTERNAL
+#include "rpmte.h"
 
-/*@access availablePackage@*/
-/*@access availableList@*/
-/*@access transactionElement@*/
+#define        _RPMTS_INTERNAL
+#include "rpmts.h"
 
-/**
- */
-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. */
-};
+#include "cpio.h"
+#include "fprint.h"
+#include "legacy.h"    /* XXX domd5 */
+#include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
 
-/**
- * Adjust for root only reserved space. On linux e2fs, this is 5%.
- */
-#define        adj_fs_blocks(_nb)      (((_nb) * 21) / 20)
+#include "debug.h"
 
-/* argon thought a shift optimization here was a waste of time...  he's
-   probably right :-( */
-#define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
+/*@access Header @*/           /* XXX ts->notify arg1 is void ptr */
+/*@access rpmps @*/    /* XXX need rpmProblemSetOK() */
+/*@access dbiIndexSet @*/
 
-/**
- */
-static /*@null@*/ void * freeFl(rpmTransactionSet ts,
-               /*@only@*/ /*@null@*/ TFI_t flList)
-       /*@*/
-{
-    if (flList) {
-       TFI_t fi;
-       int oc;
-
-       /*@-usereleased -onlytrans @*/ /* FIX: fi needs to be only */
-       for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++)
-           freeFi(fi);
-       flList = _free(flList);
-       /*@=usereleased =onlytrans @*/
-    }
-    return NULL;
-}
+/*@access rpmpsm @*/
 
-void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
-{
-    /*@-type@*/ /* FIX: cast? */
-    ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
-    /*@=type@*/
-}
+/*@access alKey @*/
+/*@access fnpyKey @*/
 
-int rpmtransGetKeys(const rpmTransactionSet ts, const void *** ep, int * nep)
-{
-    int rc = 0;
+/*@access rpmfi @*/
 
-    if (nep) *nep = ts->orderCount;
-    if (ep) {
-       const void ** e;
-       int oc;
-
-       *ep = e = xmalloc(ts->orderCount * sizeof(*e));
-       for (oc = 0; oc < ts->orderCount; oc++, e++) {
-           switch (ts->order[oc].type) {
-           case TR_ADDED:
-               if (ts->addedPackages->list) {
-                   availablePackage alp;
-                   alp = ts->addedPackages->list + ts->order[oc].u.addedIndex;
-                   *e = alp->key;
-                   /*@switchbreak@*/ break;
-               }
-               /*@fallthrough@*/
-           default:
-           case TR_REMOVED:
-               /*@-mods@*/     /* FIX: double indirection. */
-               *e = NULL;
-               /*@=mods@*/
-               /*@switchbreak@*/ break;
-           }
-       }
-    }
-    return rc;
-}
+/*@access rpmte @*/
+/*@access rpmtsi @*/
+/*@access rpmts @*/
 
 /**
  */
-static int archOkay(Header h)
+static int archOkay(/*@null@*/ const char * pkgArch)
        /*@*/
 {
-    void * pkgArch;
-    int type, count;
-
-    /* make sure we're trying to install this on the proper architecture */
-    (void) headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
-#ifndef        DYING
-    if (type == RPM_INT8_TYPE) {
-       int_8 * pkgArchNum;
-       int archNum;
-
-       /* old arch handling */
-       rpmGetArchInfo(NULL, &archNum);
-       pkgArchNum = pkgArch;
-       if (archNum != *pkgArchNum) {
-           return 0;
-       }
-    } else
-#endif
-    {
-       /* new arch handling */
-       if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
-           return 0;
-       }
-    }
-
-    return 1;
+    if (pkgArch == NULL) return 0;
+    return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
 }
 
 /**
  */
-static int osOkay(Header h)
+static int osOkay(/*@null@*/ const char * pkgOs)
        /*@*/
 {
-    void * pkgOs;
-    int type, count;
-
-    /* make sure we're trying to install this on the proper os */
-    (void) headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
-#ifndef        DYING
-    if (type == RPM_INT8_TYPE) {
-       /* v1 packages and v2 packages both used improper OS numbers, so just
-          deal with it hope things work */
-       return 1;
-    } else
-#endif
-    {
-       /* new os handling */
-       if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
-           return 0;
-       }
-    }
-
-    return 1;
+    if (pkgOs == NULL) return 0;
+    return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
 }
 
 /**
@@ -203,8 +68,8 @@ static int osOkay(Header h)
 static int sharedCmp(const void * one, const void * two)
        /*@*/
 {
-    const struct sharedFileInfo * a = one;
-    const struct sharedFileInfo * b = two;
+    sharedFileInfo a = (sharedFileInfo) one;
+    sharedFileInfo b = (sharedFileInfo) two;
 
     if (a->otherPkg < b->otherPkg)
        return -1;
@@ -216,90 +81,84 @@ static int sharedCmp(const void * one, const void * two)
 
 /**
  */
-static fileAction decideFileFate(const char * dirName,
-                       const char * baseName, short dbMode,
-                       const char * dbMd5, const char * dbLink, short newMode,
-                       const char * newMd5, const char * newLink, int newFlags,
-                       rpmtransFlags transFlags)
-       /*@globals fileSystem @*/
-       /*@modifies fileSystem @*/
+/*@-boundsread@*/
+static fileAction decideFileFate(const rpmts ts,
+               const rpmfi ofi, rpmfi nfi)
+       /*@globals fileSystem, internalState @*/
+       /*@modifies nfi, fileSystem, internalState @*/
 {
+    const char * fn = rpmfiFN(nfi);
+    int newFlags = rpmfiFFlags(nfi);
     char buffer[1024];
-    const char * dbAttr, * newAttr;
     fileTypes dbWhat, newWhat, diskWhat;
     struct stat sb;
-    int i, rc;
     int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
-    char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
 
-    (void) stpcpy( stpcpy(filespec, dirName), baseName);
-
-    if (lstat(filespec, &sb)) {
+    if (lstat(fn, &sb)) {
        /*
         * The file doesn't exist on the disk. Create it unless the new
         * package has marked it as missingok, or allfiles is requested.
         */
-       if (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
-          (newFlags & RPMFILE_MISSINGOK)) {
+       if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_ALLFILES)
+        && (newFlags & RPMFILE_MISSINGOK))
+       {
            rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
-                       filespec);
+                       fn);
            return FA_SKIP;
        } else {
            return FA_CREATE;
        }
     }
 
-    diskWhat = whatis(sb.st_mode);
-    dbWhat = whatis(dbMode);
-    newWhat = whatis(newMode);
+    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
-       them in older packages as well */
-    if (newWhat == XDIR) {
+    /*
+     * RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
+     * them in older packages as well.
+     */
+    if (newWhat == XDIR)
        return FA_CREATE;
-    }
 
-    if (diskWhat != newWhat) {
+    if (diskWhat != newWhat)
        return save;
-    } else if (newWhat != dbWhat && diskWhat != dbWhat) {
+    else if (newWhat != dbWhat && diskWhat != dbWhat)
        return save;
-    } else if (dbWhat != newWhat) {
+    else if (dbWhat != newWhat)
        return FA_CREATE;
-    } else if (dbWhat != LINK && dbWhat != REG) {
+    else if (dbWhat != LINK && dbWhat != REG)
        return FA_CREATE;
-    }
 
+    /*
+     * This order matters - we'd prefer to CREATE the file if at all
+     * possible in case something else (like the timestamp) has changed.
+     */
     if (dbWhat == REG) {
-       rc = mdfile(filespec, buffer);
-
-       if (rc) {
-           /* assume the file has been removed, don't freak */
-           return FA_CREATE;
-       }
-       dbAttr = dbMd5;
-       newAttr = newMd5;
+       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));
-       i = readlink(filespec, buffer, sizeof(buffer) - 1);
-       if (i == -1) {
-           /* assume the file has been removed, don't freak */
-           return FA_CREATE;
-       }
-       dbAttr = dbLink;
-       newAttr = newLink;
-     }
-
-    /* this order matters - we'd prefer to CREATE the file if at all
-       possible in case something else (like the timestamp) has changed */
-
-    if (!strcmp(dbAttr, buffer)) {
-       /* this config file has never been modified, so just replace it */
-       return FA_CREATE;
-    }
-
-    if (!strcmp(dbAttr, newAttr)) {
-       /* this file is the same in all versions of this package */
-       return FA_SKIP;
+       if (readlink(fn, buffer, sizeof(buffer) - 1) == -1)
+           return FA_CREATE;   /* assume file has been removed */
+       oFLink = rpmfiFLink(ofi);
+       if (oFLink && !strcmp(oFLink, buffer))
+           return FA_CREATE;   /* unmodified config file, replace. */
+       nFLink = rpmfiFLink(nfi);
+/*@-nullpass@*/
+       if (oFLink && nFLink && !strcmp(oFLink, nFLink))
+           return FA_SKIP;     /* identical file, don't bother. */
+/*@=nullpass@*/
     }
 
     /*
@@ -310,118 +169,145 @@ static fileAction decideFileFate(const char * dirName,
      */
     return save;
 }
+/*@=boundsread@*/
 
 /**
  */
-static int filecmp(short mode1, const char * md51, const char * link1,
-                  short mode2, const char * md52, const char * link2)
+/*@-boundsread@*/
+static int filecmp(rpmfi afi, rpmfi bfi)
        /*@*/
 {
-    fileTypes what1 = whatis(mode1);
-    fileTypes what2 = whatis(mode2);
-
-    if (what1 != what2) return 1;
-
-    if (what1 == LINK)
-       return strcmp(link1, link2);
-    else if (what1 == REG)
-       return strcmp(md51, md52);
+    fileTypes awhat = whatis(rpmfiFMode(afi));
+    fileTypes bwhat = whatis(rpmfiFMode(bfi));
+
+    if (awhat != bwhat) return 1;
+
+    if (awhat == LINK) {
+       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) {
+       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, TFI_t fi,
-               struct sharedFileInfo * shared,
+/*@-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 @*/
 {
-    HGE_t hge = fi->hge;
-    HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
-    rpmtransFlags transFlags = ts->transFlags;
-    rpmTagType oltype, omtype;
-    Header h;
-    int i;
-    const char ** otherMd5s;
-    const char ** otherLinks;
-    const char * otherStates;
-    uint_32 * otherFlags;
-    uint_32 * otherSizes;
-    uint_16 * otherModes;
+    uint_32 tscolor = rpmtsColor(ts);
+    uint_32 otecolor, tecolor;
+    uint_32 oficolor, ficolor;
+    const char * altNEVR = NULL;
+    rpmfi otherFi = NULL;
     int numReplaced = 0;
-    int xx;
+    rpmps ps;
+    int i;
 
-    rpmdbMatchIterator mi;
+    {  rpmdbMatchIterator mi;
+       Header h;
+       int scareMem = 0;
 
-    mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
-    h = rpmdbNextIterator(mi);
-    if (h == NULL) {
+       mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
+                       &shared->otherPkg, sizeof(shared->otherPkg));
+       while ((h = rpmdbNextIterator(mi)) != NULL) {
+           altNEVR = hGetNEVR(h, NULL);
+           otherFi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
+           break;
+       }
        mi = rpmdbFreeIterator(mi);
-       return 1;
     }
 
-    xx = hge(h, RPMTAG_FILEMD5S, &omtype, (void **) &otherMd5s, NULL);
-    xx = hge(h, RPMTAG_FILELINKTOS, &oltype, (void **) &otherLinks, NULL);
-    xx = hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
-    xx = hge(h, RPMTAG_FILEMODES, NULL, (void **) &otherModes, NULL);
-    xx = hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &otherFlags, NULL);
-    xx = hge(h, RPMTAG_FILESIZES, NULL, (void **) &otherSizes, NULL);
+    /* 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;
 
-    fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
+    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) rpmfiSetFX(otherFi, otherFileNum);
+       oficolor = rpmfiFColor(otherFi);
+       oficolor &= tscolor;
+
        fileNum = shared->pkgFileNum;
+       (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. */
        if (otherStates && otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
            continue;
+#endif
 
        if (XFA_SKIPPING(fi->actions[fileNum]))
            continue;
 
-       if (filecmp(otherModes[otherFileNum],
-                       otherMd5s[otherFileNum],
-                       otherLinks[otherFileNum],
-                       fi->fmodes[fileNum],
-                       fi->fmd5s[fileNum],
-                       fi->flinks[fileNum])) {
-           if (reportConflicts)
-               rpmProblemSetAppend(ts->probs, RPMPROB_FILE_CONFLICT, fi->ap,
-                       fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum], h, 0);
-           if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
-                       & RPMFILE_CONFIG) {
-               /*@-assignexpose@*/
+       if (filecmp(otherFi, fi)) {
+           /* Report conflicts only for packages/files of same color. */
+           if (tscolor == 0 || (tecolor == otecolor && ficolor == oficolor))
+           if (reportConflicts) {
+               rpmpsAppend(ps, RPMPROB_FILE_CONFLICT,
+                       rpmteNEVR(p), rpmteKey(p),
+                       rpmfiDN(fi), rpmfiBN(fi),
+                       altNEVR,
+                       0);
+           }
+           if (!isCfgFile) {
+               /*@-assignexpose@*/ /* FIX: p->replaced, not fi */
                if (!shared->isRemoved)
                    fi->replaced[numReplaced++] = *shared;
                /*@=assignexpose@*/
            }
        }
 
-       if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
-           fi->actions[fileNum] = decideFileFate(
-                       fi->dnl[fi->dil[fileNum]],
-                       fi->bnl[fileNum],
-                       otherModes[otherFileNum],
-                       otherMd5s[otherFileNum],
-                       otherLinks[otherFileNum],
-                       fi->fmodes[fileNum],
-                       fi->fmd5s[fileNum],
-                       fi->flinks[fileNum],
-                       fi->fflags[fileNum],
-                       transFlags);
+       if (isCfgFile) {
+           fileAction action;
+           action = decideFileFate(ts, otherFi, fi);
+           fi->actions[fileNum] = action;
        }
-
-       fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
+       fi->replacedSizes[fileNum] = rpmfiFSize(otherFi);
     }
+    ps = rpmpsFree(ps);
 
-    otherMd5s = hfd(otherMd5s, omtype);
-    otherLinks = hfd(otherLinks, oltype);
-    mi = rpmdbFreeIterator(mi);
+    altNEVR = _free(altNEVR);
+    otherFi = rpmfiFree(otherFi);
 
     fi->replaced = xrealloc(fi->replaced,      /* XXX memory leak */
                           sizeof(*fi->replaced) * (numReplaced + 1));
@@ -429,14 +315,15 @@ static int handleInstInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
 
     return 0;
 }
+/*@=bounds@*/
 
 /**
  */
 /* XXX only ts->rpmdb modified */
-static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
-               struct sharedFileInfo * shared, int sharedCount)
-       /*@globals fileSystem @*/
-       /*@modifies fi, fileSystem @*/
+static int handleRmvdInstalledFiles(const rpmts ts, rpmfi fi,
+               sharedFileInfo shared, int sharedCount)
+       /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
+       /*@modifies ts, fi, rpmGlobalMacroContext, fileSystem, internalState @*/
 {
     HGE_t hge = fi->hge;
     Header h;
@@ -455,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;
@@ -465,50 +353,197 @@ static int handleRmvdInstalledFiles(const rpmTransactionSet ts, TFI_t fi,
 
        fi->actions[fileNum] = FA_SKIP;
     }
+/*@=boundswrite@*/
 
     mi = rpmdbFreeIterator(mi);
 
     return 0;
 }
 
+#define        ISROOT(_d)      (((_d)[0] == '/' && (_d)[1] == '\0') ? "" : (_d))
+
+/*@unchecked@*/
+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;
+    int adnlen = strlen(a->entry->dirName);
+    int asnlen = (a->subDir ? strlen(a->subDir) : 0);
+    int abnlen = strlen(a->baseName);
+    int bdnlen = strlen(b->entry->dirName);
+    int bsnlen = (b->subDir ? strlen(b->subDir) : 0);
+    int bbnlen = strlen(b->baseName);
+    char * afn, * bfn, * t;
+    int rc = 0;
+
+    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++ = '/';
+    if (a->subDir && asnlen) t = stpcpy(t, a->subDir);
+    if (abnlen) t = stpcpy(t, a->baseName);
+    if (afn[0] == '/' && afn[1] == '/') afn++;
+
+    bfn = t = alloca(bdnlen+bsnlen+bbnlen+2);
+    if (bdnlen) t = stpcpy(t, b->entry->dirName);
+    *t++ = '/';
+    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@*/
+if (_fps_debug)
+fprintf(stderr, "\trc(%d) = strcmp(\"%s\", \"%s\")\n", rc, afn, bfn);
+/*@=modfilesys@*/
+
+/*@-modfilesys@*/
+if (_fps_debug)
+fprintf(stderr, "\t%s/%s%s\trc %d\n",
+ISROOT(b->entry->dirName),
+(b->subDir ? b->subDir : ""),
+b->baseName,
+rc
+);
+/*@=modfilesys@*/
+
+    return rc;
+}
+
+/*@unchecked@*/
+static int _linear_fps_search = 0;
+
+static int findFps(const struct fingerPrint_s * fiFps,
+               const struct fingerPrint_s * otherFps,
+               int otherFc)
+       /*@*/
+{
+    int otherFileNum;
+
+/*@-modfilesys@*/
+if (_fps_debug)
+fprintf(stderr, "==> %s/%s%s\n",
+ISROOT(fiFps->entry->dirName),
+(fiFps->subDir ? fiFps->subDir : ""),
+fiFps->baseName);
+/*@=modfilesys@*/
+
+  if (_linear_fps_search) {
+
+linear:
+    for (otherFileNum = 0; otherFileNum < otherFc; otherFileNum++, otherFps++) {
+
+/*@-modfilesys@*/
+if (_fps_debug)
+fprintf(stderr, "\t%4d %s/%s%s\n", otherFileNum,
+ISROOT(otherFps->entry->dirName),
+(otherFps->subDir ? otherFps->subDir : ""),
+otherFps->baseName);
+/*@=modfilesys@*/
+
+       /* If the addresses are the same, so are the values. */
+       if (fiFps == otherFps)
+           break;
+
+       /* Otherwise, compare fingerprints by value. */
+       /*@-nullpass@*/ /* LCL: looks good to me */
+       if (FP_EQUAL((*fiFps), (*otherFps)))
+           break;
+       /*@=nullpass@*/
+    }
+
+if (otherFileNum == otherFc) {
+/*@-modfilesys@*/
+if (_fps_debug)
+fprintf(stderr, "*** FP_EQUAL NULL %s/%s%s\n",
+ISROOT(fiFps->entry->dirName),
+(fiFps->subDir ? fiFps->subDir : ""),
+fiFps->baseName);
+/*@=modfilesys@*/
+}
+
+    return otherFileNum;
+
+  } else {
+
+    const struct fingerPrint_s * bingoFps;
+
+/*@-boundswrite@*/
+    bingoFps = bsearch(fiFps, otherFps, otherFc, sizeof(*otherFps), fpsCompare);
+/*@=boundswrite@*/
+    if (bingoFps == NULL) {
+/*@-modfilesys@*/
+if (_fps_debug)
+fprintf(stderr, "*** bingoFps NULL %s/%s%s\n",
+ISROOT(fiFps->entry->dirName),
+(fiFps->subDir ? fiFps->subDir : ""),
+fiFps->baseName);
+/*@=modfilesys@*/
+       goto linear;
+    }
+
+    /* If the addresses are the same, so are the values. */
+    /*@-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 : ""),
+bingoFps->baseName);
+/*@=modfilesys@*/
+       goto linear;
+    }
+
+    otherFileNum = (bingoFps != NULL ? (bingoFps - otherFps) : 0);
+
+  }
+
+    return otherFileNum;
+}
+
 /**
- * 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, 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;
-    char * filespec = NULL;
-    int fileSpecAlloced = 0;
+    rpmps ps;
+    const char * fn;
     int i, j;
   
-    for (i = 0; i < fi->fc; i++) {
+    ps = rpmtsProblems(ts);
+    fi = rpmfiInit(fi, 0);
+    if (fi != NULL)
+    while ((i = rpmfiNext(fi)) >= 0) {
+       struct fingerPrint_s * fiFps;
        int otherPkgNum, otherFileNum;
-       const TFI_t * recs;
+       rpmfi otherFi;
+       int_32 FFlags;
+       int_16 FMode;
+       const rpmfi * recs;
        int numRecs;
 
        if (XFA_SKIPPING(fi->actions[i]))
            continue;
 
-       j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
-       /*@-branchstate@*/
-       if (j > fileSpecAlloced) {
-           fileSpecAlloced = j * 2;
-           filespec = xrealloc(filespec, fileSpecAlloced);
-       }
-       /*@=branchstate@*/
+       fn = rpmfiFN(fi);
+       fiFps = fi->fps + i;
+       FFlags = rpmfiFFlags(fi);
+       FMode = rpmfiFMode(fi);
 
-       (void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
-
-       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
@@ -516,12 +551,13 @@ static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
         * will be installed and removed so the records for an overlapped
         * files will be sorted in exactly the same order.
         */
-       (void) htGetEntry(ts->ht, &fi->fps[i],
+       (void) htGetEntry(ts->ht, fiFps,
                        (const void ***) &recs, &numRecs, NULL);
 
        /*
         * 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
@@ -545,42 +581,39 @@ static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
 
        /* Find what the previous disposition of this file was. */
        otherFileNum = -1;                      /* keep gcc quiet */
+       otherFi = NULL;
        for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
+           struct fingerPrint_s * otherFps;
+           int otherFc;
+
+           otherFi = recs[otherPkgNum];
+
            /* Added packages need only look at other added packages. */
-           if (fi->type == TR_ADDED && recs[otherPkgNum]->type != TR_ADDED)
+           if (rpmteType(p) == TR_ADDED && rpmteType(otherFi->te) != TR_ADDED)
                /*@innercontinue@*/ continue;
 
-           /* TESTME: there are more efficient searches in the world... */
-           for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc;
-                otherFileNum++) {
+           otherFps = otherFi->fps;
+           otherFc = rpmfiFC(otherFi);
 
-               /* If the addresses are the same, so are the values. */
-               if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
-                   /*@innerbreak@*/ break;
+           otherFileNum = findFps(fiFps, otherFps, otherFc);
+           (void) rpmfiSetFX(otherFi, otherFileNum);
 
-               /* Otherwise, compare fingerprints by value. */
-               /*@-nullpass@*/ /* LCL: looks good to me */
-               if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
-                   /*@innerbreak@*/ break;
-               /*@=nullpass@*/
-
-           }
-           /* XXX is this test still necessary? */
-           if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
+           /* XXX Happens iff fingerprint for incomplete package install. */
+           if (otherFi->actions[otherFileNum] != FA_UNKNOWN)
                /*@innerbreak@*/ break;
        }
 
-       switch (fi->type) {
+/*@-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 ((fi->fflags[i] & RPMFILE_CONFIG) && 
-                       !lstat(filespec, &sb)) {
+               if ((FFlags & RPMFILE_CONFIG) && !lstat(fn, &sb)) {
                    /* Here is a non-overlapped pre-existing config file. */
-                   fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
+                   fi->actions[i] = (FFlags & RPMFILE_NOREPLACE)
                        ? FA_ALTNAME : FA_BACKUP;
                } else {
                    fi->actions[i] = FA_CREATE;
@@ -588,52 +621,55 @@ static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
                /*@switchbreak@*/ break;
            }
 
+assert(otherFi != NULL);
            /* Mark added overlapped non-identical files as a conflict. */
-           if ((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
-            && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
-                       recs[otherPkgNum]->fmd5s[otherFileNum],
-                       recs[otherPkgNum]->flinks[otherFileNum],
-                       fi->fmodes[i],
-                       fi->fmd5s[i],
-                       fi->flinks[i])) {
-               rpmProblemSetAppend(ts->probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap,
-                               filespec, NULL, recs[otherPkgNum]->ap->h, 0);
+           if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACENEWFILES)
+            && filecmp(otherFi, fi))
+           {
+               rpmpsAppend(ps, RPMPROB_NEW_FILE_CONFLICT,
+                       rpmteNEVR(p), rpmteKey(p),
+                       fn, NULL,
+                       rpmteNEVR(otherFi->te),
+                       0);
            }
 
            /* Try to get the disk accounting correct even if a conflict. */
-           fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
+           fixupSize = rpmfiFSize(otherFi);
 
-           if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
+           if ((FFlags & RPMFILE_CONFIG) && !lstat(fn, &sb)) {
                /* Here is an overlapped  pre-existing config file. */
-               fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
+               fi->actions[i] = (FFlags & RPMFILE_NOREPLACE)
                        ? FA_ALTNAME : FA_SKIP;
            } else {
                fi->actions[i] = FA_CREATE;
            }
          } /*@switchbreak@*/ break;
+
        case TR_REMOVED:
            if (otherPkgNum >= 0) {
+assert(otherFi != NULL);
                /* Here is an overlapped added file we don't want to nuke. */
-               if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
+               if (otherFi->actions[otherFileNum] != FA_ERASE) {
                    /* On updates, don't remove files. */
                    fi->actions[i] = FA_SKIP;
                    /*@switchbreak@*/ break;
                }
                /* Here is an overlapped removed file: skip in previous. */
-               recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
+               otherFi->actions[otherFileNum] = FA_SKIP;
            }
            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]) && (fi->fflags[i] & 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 mdsum[50];
-               if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
+           {   char md5sum[50];
+               const unsigned char * MD5 = rpmfiMD5(fi);
+               if (!domd5(fn, md5sum, 0, NULL) && memcmp(MD5, md5sum, 16)) {
                    fi->actions[i] = FA_BACKUP;
                    /*@switchbreak@*/ break;
                }
@@ -641,71 +677,84 @@ static void handleOverlappedFiles(const rpmTransactionSet ts, TFI_t fi)
            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;
+       /* Update disk space info for a file. */
+       rpmtsUpdateDSI(ts, fiFps->entry->dev, rpmfiFSize(fi),
+               fi->replacedSizes[i], fixupSize, fi->actions[i]);
 
-             case FA_ERASE:
-               ds->ineeded--;
-               ds->bneeded -= s;
-               /*@switchbreak@*/ break;
-
-             default:
-               /*@switchbreak@*/ break;
-           }
-
-           ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
-       }
     }
-    if (filespec) free(filespec);
+    ps = rpmpsFree(ps);
 }
 
 /**
+ * Ensure that current package is newer than installed package.
+ * @param ts           transaction set
+ * @param p            current transaction element
+ * @param h            installed header
+ * @return             0 if not newer, 1 if okay
  */
-static int ensureOlder(rpmTransactionSet ts, availablePackage alp, Header old)
-       /*@modifies ts, alp @*/
+static int ensureOlder(rpmts ts,
+               const rpmte p, const Header h)
+       /*@modifies ts @*/
 {
-    int result, rc = 0;
-
-    if (old == NULL) return 1;
+    int_32 reqFlags = (RPMSENSE_LESS | RPMSENSE_EQUAL);
+    const char * reqEVR;
+    rpmds req;
+    char * t;
+    int nb;
+    int rc;
+
+    if (p == NULL || h == NULL)
+       return 1;
 
-    result = rpmVersionCompare(old, alp->h);
-    if (result <= 0)
-       rc = 0;
-    else if (result > 0) {
+/*@-boundswrite@*/
+    nb = strlen(rpmteNEVR(p)) + (rpmteE(p) != NULL ? strlen(rpmteE(p)) : 0) + 1;
+    t = alloca(nb);
+    *t = '\0';
+    reqEVR = t;
+    if (rpmteE(p) != NULL)     t = stpcpy( stpcpy(t, rpmteE(p)), ":");
+    if (rpmteV(p) != NULL)     t = stpcpy(t, rpmteV(p));
+    *t++ = '-';
+    if (rpmteR(p) != NULL)     t = stpcpy(t, rpmteR(p));
+/*@=boundswrite@*/
+    
+    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);
+       rpmpsAppend(ps, RPMPROB_OLDPACKAGE,
+               rpmteNEVR(p), rpmteKey(p),
+               NULL, NULL,
+               altNEVR,
+               0);
+       altNEVR = _free(altNEVR);
+       ps = rpmpsFree(ps);
        rc = 1;
-       rpmProblemSetAppend(ts->probs, RPMPROB_OLDPACKAGE, alp,
-                       NULL, NULL, old, 0);
-    }
+    } else
+       rc = 0;
 
     return rc;
 }
 
 /**
+ * Skip any files that do not match install policies.
+ * @param ts           transaction set
+ * @param fi           file info set
  */
-static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
+/*@-mustmod@*/ /* FIX: fi->actions is modified. */
+/*@-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;
@@ -713,6 +762,7 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
     const char * s;
     int * drc;
     char * dff;
+    int dc;
     int i, j;
 
     if (!noDocs)
@@ -738,25 +788,39 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
     /*@=branchstate@*/
 
     /* Compute directory refcount, skip directory if now empty. */
-    drc = alloca(fi->dc * sizeof(*drc));
-    memset(drc, 0, fi->dc * sizeof(*drc));
-    dff = alloca(fi->dc * sizeof(*dff));
-    memset(dff, 0, fi->dc * sizeof(*dff));
-
-    for (i = 0; i < fi->fc; i++) {
-       char **nsp;
+    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 = rpmfiInit(fi, 0);
+    if (fi != NULL)    /* XXX lclint */
+    while ((i = rpmfiNext(fi)) >= 0)
+    {
+       char ** nsp;
 
-       bn = fi->bnl[i];
+       bn = rpmfiBN(fi);
        bnlen = strlen(bn);
-       ix = fi->dil[i];
-       dn = fi->dnl[ix];
+       ix = rpmfiDX(fi);
+       dn = rpmfiDN(fi);
        dnlen = strlen(dn);
+       if (dn == NULL)
+           continue;   /* XXX can't happen */
 
        drc[ix]++;
 
        /* 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;
        }
 
@@ -800,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"))
@@ -823,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 && (fi->fflags[i] & RPMFILE_DOC)) {
+       if (noDocs && (rpmfiFFlags(fi) & RPMFILE_DOC)) {
            drc[ix]--;  dff[ix] = 1;
            fi->actions[i] = FA_SKIPNSTATE;
            continue;
@@ -833,7 +906,14 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
     }
 
     /* Skip (now empty) directories that had skipped files. */
-    for (j = 0; j < fi->dc; j++) {
+#ifndef        NOTYET
+    if (fi != NULL)    /* XXX can't happen */
+    for (j = 0; j < dc; j++)
+#else
+    if ((fi = rpmfiInitD(fi)) != NULL)
+    while (j = rpmfiNextD(fi) >= 0)
+#endif
+    {
 
        if (drc[j]) continue;   /* dir still has files. */
        if (!dff[j]) continue;  /* dir was not emptied here. */
@@ -848,21 +928,28 @@ static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
        }
 
        /* If explicitly included in the package, skip the directory. */
-       for (i = 0; i < fi->fc; i++) {
-           const char * dir;
+       fi = rpmfiInit(fi, 0);
+       if (fi != NULL)         /* XXX lclint */
+       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;
@@ -870,223 +957,104 @@ 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@*/
 
 /**
- * Iterator across transaction elements, forward on install, backward on erase.
- */
-struct tsIterator_s {
-/*@refcounted@*/ rpmTransactionSet ts; /*!< transaction set. */
-    int reverse;                       /*!< reversed traversal? */
-    int ocsave;                                /*!< last returned iterator index. */
-    int oc;                            /*!< iterator index. */
-};
-
-/**
- * Return transaction element order count.
- * @param a            transaction element iterator
- * @return             element order count
- */
-static int tsGetOc(void * a)
-       /*@*/
-{
-    struct tsIterator_s * iter = a;
-    int oc = iter->ocsave;
-    return oc;
-}
-
-/**
- * Return transaction element available package pointer.
- * @param a            transaction element iterator
- * @return             available package pointer
- */
-static /*@dependent@*/ availablePackage tsGetAlp(void * a)
-       /*@*/
-{
-    struct tsIterator_s * iter = a;
-    availablePackage alp = NULL;
-    int oc = iter->ocsave;
-
-    /*@-branchstate@*/
-    if (oc != -1) {
-       rpmTransactionSet ts = iter->ts;
-       TFI_t fi = ts->flList + oc;
-       if (ts->addedPackages->list && fi->type == TR_ADDED)
-           alp = ts->addedPackages->list + ts->order[oc].u.addedIndex;
-    }
-    /*@=branchstate@*/
-    return alp;
-}
-
-/**
- * Destroy transaction element iterator.
- * @param a            transaction element iterator
- * @return             NULL always
- */
-static /*@null@*/ void * tsFreeIterator(/*@only@*//*@null@*/ void * a)
-       /*@*/
-{
-    struct tsIterator_s * iter = a;
-    if (iter)
-       iter->ts = rpmtsUnlink(iter->ts, "tsIterator");
-    return _free(a);
-}
-
-/**
- * Create transaction element iterator.
- * @param ts           transaction set
- * @return             transaction element iterator
- */
-static void * tsInitIterator(rpmTransactionSet ts)
-       /*@modifies ts @*/
-{
-    struct tsIterator_s * iter = NULL;
-
-    iter = xcalloc(1, sizeof(*iter));
-    iter->ts = rpmtsLink(ts, "tsIterator");
-    iter->reverse = ((ts->transFlags & RPMTRANS_FLAG_REVERSE) ? 1 : 0);
-    iter->oc = (iter->reverse ? (ts->orderCount - 1) : 0);
-    iter->ocsave = iter->oc;
-    return iter;
-}
-
-/**
- * Return next transaction element's file info.
- * @param a            file info iterator
- * @return             next index, -1 on termination
+ * Return transaction element's file info.
+ * @todo Take a rpmfi refcount here.
+ * @param tsi          transaction element iterator
+ * @return             transaction element file info
  */
-static TFI_t tsNextIterator(void * a)
+static /*@null@*/
+rpmfi rpmtsiFi(const rpmtsi tsi)
        /*@*/
 {
-    struct tsIterator_s * iter = a;
-    TFI_t fi = NULL;
-    int oc = -1;
-
-    if (iter->reverse) {
-       if (iter->oc >= 0)                      oc = iter->oc--;
-    } else {
-       if (iter->oc < iter->ts->orderCount)    oc = iter->oc++;
+    rpmfi fi = NULL;
+
+    if (tsi != NULL && tsi->ocsave != -1) {
+       /*@-type -abstract@*/ /* FIX: rpmte not opaque */
+       rpmte te = rpmtsElement(tsi->ts, tsi->ocsave);
+       /*@-assignexpose@*/
+       if (te != NULL && (fi = te->fi) != NULL)
+           fi->te = te;
+       /*@=assignexpose@*/
+       /*@=type =abstract@*/
     }
-    iter->ocsave = oc;
-    if (oc != -1)
-       fi = iter->ts->flList + oc;
+    /*@-compdef -refcounttrans -usereleased @*/
     return fi;
+    /*@=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,
-                       rpmCallbackFunction notify, rpmCallbackData notifyData,
-                       rpmProblemSet okProbs, rpmProblemSet * newProbs,
-                       rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
+int rpmtsRun(rpmts ts, rpmps okProbs, rpmprobFilterFlags ignoreSet)
 {
+    uint_32 tscolor = rpmtsColor(ts);
     int i, j;
     int ourrc = 0;
-    availablePackage alp;
     int totalFileCount = 0;
-    TFI_t fi;
-    struct diskspaceInfo * dip;
-    struct sharedFileInfo * shared, * sharedList;
+    rpmfi fi;
+    sharedFileInfo shared, sharedList;
     int numShared;
     int nexti;
-    int lastFailed;
-    int oc;
+    alKey lastFailKey;
     fingerPrintCache fpc;
-    struct psm_s psmbuf;
-    PSM_t psm = &psmbuf;
-    void * tsi;
+    rpmps ps;
+    rpmpsm psm;
+    rpmtsi pi; rpmte p;
+    rpmtsi qi; rpmte q;
+    int numAdded;
+    int numRemoved;
     int xx;
-int keep_header = 1;   /* XXX rpmProblemSetAppend prevents dumping headers. */
 
-    /* FIXME: what if the same package is included in ts twice? */
+    /* XXX programmer error segfault avoidance. */
+    if (rpmtsNElements(ts) <= 0)
+       return -1;
 
-    ts->transFlags = transFlags;
-    if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
-       ts->transFlags |= (_noTransScripts | _noTransTriggers);
-    if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
-       ts->transFlags |= _noTransTriggers;
+    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));
 
-    /* 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);
+    if (rpmtsFlags(ts) & RPMTRANS_FLAG_JUSTDB)
+       (void) rpmtsSetFlags(ts, (rpmtsFlags(ts) | _noTransScripts | _noTransTriggers));
 
-    ts->notify = notify;
-    ts->notifyData = notifyData;
-    /*@-assignexpose@*/
-    ts->probs = *newProbs = rpmProblemSetCreate();
-    /*@=assignexpose@*/
-    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);
+    ts->probs = rpmpsFree(ts->probs);
+    ts->probs = rpmpsCreate();
 
-    memset(psm, 0, sizeof(*psm));
-    /*@-assignexpose@*/
-    psm->ts = rpmtsLink(ts, "tsRun");
-    /*@=assignexpose@*/
+    /* 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);
 
-    /* Get available space on mounted file systems. */
-    if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
-               !rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
-       struct stat sb;
-
-       ts->di = _free(ts->di);
-       dip = ts->di = xcalloc((ts->filesystemCount + 1), sizeof(*ts->di));
-
-       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;
+       /* Open database RDWR for installing packages. */
+       if (rpmtsOpenDB(ts, dbmode))
+           return -1;  /* XXX W2DO? */
+    }
 
-               xx = stat(ts->filesystems[i], &sb);
-               ts->di[i].dev = sb.st_dev;
-           }
-       }
+    ts->ignoreSet = ignoreSet;
+    {  const char * currDir = currentDirectory();
+       rpmtsSetCurrDir(ts, currDir);
+       currDir = _free(currDir);
+    }
 
-       if (dip) ts->di[i].bsize = 0;
+    (void) rpmtsSetChrootDone(ts, 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.
@@ -1095,137 +1063,134 @@ int keep_header = 1;  /* XXX rpmProblemSetAppend prevents dumping headers. */
      * 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 */
-    if (ts->addedPackages->list != NULL)
-    for (alp = ts->addedPackages->list;
-       (alp - ts->addedPackages->list) < ts->addedPackages->size;
-       alp++)
-    {
-       if (!archOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
-           rpmProblemSetAppend(ts->probs, RPMPROB_BADARCH, alp,
-                       NULL, NULL, NULL, 0);
-
-       if (!osOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
-           rpmProblemSetAppend(ts->probs, RPMPROB_BADOS, alp,
-                       NULL, NULL, NULL, 0);
-
-       if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
-           rpmdbMatchIterator mi;
-           Header oldH;
-           mi = rpmtsInitIterator(ts, RPMTAG_NAME, alp->name, 0);
-           while ((oldH = rpmdbNextIterator(mi)) != NULL)
-               xx = ensureOlder(ts, alp, oldH);
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) {
+       rpmdbMatchIterator mi;
+       int fc;
+
+       if ((fi = rpmtsiFi(pi)) == NULL)
+           continue;   /* XXX can't happen */
+       fc = rpmfiFC(fi);
+
+       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 (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_IGNOREOS))
+           if (!osOkay(rpmteO(p)))
+               rpmpsAppend(ps, RPMPROB_BADOS,
+                       rpmteNEVR(p), rpmteKey(p),
+                       rpmteO(p), NULL,
+                       NULL, 0);
+
+       if (!(rpmtsFilterFlags(ts) & RPMPROB_FILTER_OLDPACKAGE)) {
+           Header h;
+           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) && !alp->multiLib) {
-           rpmdbMatchIterator mi;
-           mi = rpmtsInitIterator(ts, RPMTAG_NAME, alp->name, 0);
-           xx = rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
-                       RPMMIRE_DEFAULT, alp->version);
-           xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
-                       RPMMIRE_DEFAULT, alp->release);
+       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,
+                               rpmteV(p));
+           xx = rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT,
+                               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, alp,
-                       NULL, NULL, NULL, 0);
+               rpmpsAppend(ps, RPMPROB_PKG_INSTALLED,
+                       rpmteNEVR(p), rpmteKey(p),
+                       NULL, NULL,
+                       NULL, 0);
                /*@innerbreak@*/ break;
            }
            mi = rpmdbFreeIterator(mi);
        }
 
-       totalFileCount += alp->filesCount;
+       /* Count no. of files (if any). */
+       totalFileCount += fc;
 
     }
+    pi = rpmtsiFree(pi);
+    ps = rpmpsFree(ps);
 
-    /* FIXME: it seems a bit silly to read in all of these headers twice */
     /* The ordering doesn't matter here */
-    if (ts->numRemovedPackages > 0) {
-       rpmdbMatchIterator mi;
-       Header h;
-       int fileCount;
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
+       int fc;
 
-       mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
-       xx = rpmdbAppendIterator(mi, ts->removedPackages, ts->numRemovedPackages);
-       while ((h = rpmdbNextIterator(mi)) != NULL) {
-           if (headerGetEntry(h, RPMTAG_BASENAMES, NULL, NULL, &fileCount))
-               totalFileCount += fileCount;
-       }
-       mi = rpmdbFreeIterator(mi);
+       if ((fi = rpmtsiFi(pi)) == NULL)
+           continue;   /* XXX can't happen */
+       fc = rpmfiFC(fi);
+
+       totalFileCount += fc;
     }
+    pi = rpmtsiFree(pi);
 
     /* ===============================================
-     * Initialize file list:
+     * Initialize transaction element file info for package:
      */
-    ts->flEntries = ts->addedPackages->size + ts->numRemovedPackages;
-    ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
 
     /*
      * FIXME?: we'd be better off assembling one very large file list and
      * calling fpLookupList only once. I'm not sure that the speedup is
      * worth the trouble though.
      */
-    tsi = tsInitIterator(ts);
-    while ((fi = tsNextIterator(tsi)) != NULL) {
-       oc = tsGetOc(tsi);
-       fi->magic = TFIMAGIC;
+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 = rpmtsiFi(pi)) == NULL)
+           continue;   /* XXX can't happen */
+       fc = rpmfiFC(fi);
 
-       /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
-       fi->type = ts->order[oc].type;
        /*@-branchstate@*/
-       switch (fi->type) {
+       switch (rpmteType(p)) {
        case TR_ADDED:
-           /* XXX watchout: fi->type must be set for tsGetAlp() to "work" */
-           fi->ap = tsGetAlp(tsi);
+           numAdded++;
            fi->record = 0;
-           loadFi(ts, fi, fi->ap->h, keep_header);
-/* XXX free fi->ap->h here if/when possible */
-           if (fi->fc == 0)
-               continue;
-
            /* Skip netshared paths, not our i18n files, and excluded docs */
-           skipFiles(ts, fi);
+           if (fc > 0)
+               skipFiles(ts, fi);
            /*@switchbreak@*/ break;
        case TR_REMOVED:
-           fi->ap = NULL;
-           fi->record = ts->order[oc].u.removed.dboffset;
-           /* Retrieve erased package header from the database. */
-           {   rpmdbMatchIterator mi;
-
-               mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES,
-                               &fi->record, sizeof(fi->record));
-               if ((fi->h = rpmdbNextIterator(mi)) != NULL)
-                   fi->h = headerLink(fi->h);
-               mi = rpmdbFreeIterator(mi);
-           }
-           if (fi->h == NULL) {
-               /* ACK! */
-               continue;
-           }
-           /* XXX header arg unused. */
-           loadFi(ts, fi, fi->h, 0);
+           numRemoved++;
+           fi->record = rpmteDBOffset(p);
            /*@switchbreak@*/ break;
        }
        /*@=branchstate@*/
 
-       if (fi->fc)
-           fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
+       fi->fps = (fc > 0 ? xmalloc(fc * sizeof(*fi->fps)) : NULL);
     }
-    tsi = tsFreeIterator(tsi);
+    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;
-       /*@-onlytrans@*/
-       /*@-mods@*/
-       chroot_prefix = ts->rootDir;
-       /*@=mods@*/
-       /*@=onlytrans@*/
+       (void) rpmtsSetChrootDone(ts, 1);
     }
 
     ts->ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
@@ -1234,71 +1199,93 @@ int keep_header = 1;    /* XXX rpmProblemSetAppend prevents dumping headers. */
     /* ===============================================
      * Add fingerprint for each file not skipped.
      */
-    tsi = tsInitIterator(ts);
-    while ((fi = tsNextIterator(tsi)) != NULL) {
-       fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
-       for (i = 0; i < fi->fc; i++) {
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
+       int fc;
+
+       (void) rpmdbCheckSignals();
+
+       if ((fi = rpmtsiFi(pi)) == NULL)
+           continue;   /* XXX can't happen */
+       fc = rpmfiFC(fi);
+
+       (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), 0);
+       fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fc, fi->fps);
+       /*@-branchstate@*/
+       fi = rpmfiInit(fi, 0);
+       if (fi != NULL)         /* XXX lclint */
+       while ((i = rpmfiNext(fi)) >= 0) {
            if (XFA_SKIPPING(fi->actions[i]))
                /*@innercontinue@*/ continue;
            /*@-dependenttrans@*/
-           htAddEntry(ts->ht, fi->fps + i, fi);
+           htAddEntry(ts->ht, fi->fps + i, (void *) fi);
            /*@=dependenttrans@*/
        }
+       /*@=branchstate@*/
+       (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_FINGERPRINT), fc);
+
     }
-    tsi = tsFreeIterator(tsi);
+    pi = rpmtsiFree(pi);
 
-    /*@-noeffectuncon @*/ /* FIX: check rc */
-    NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
+    NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->orderCount,
        NULL, ts->notifyData));
-    /*@=noeffectuncon@*/
 
     /* ===============================================
      * Compute file disposition for each package in transaction set.
      */
-    tsi = tsInitIterator(ts);
-    while ((fi = tsNextIterator(tsi)) != 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;
 
-       /*@-noeffectuncon @*/ /* FIX: check rc */
-       NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
-                       ts->flEntries, NULL, ts->notifyData));
-       /*@=noeffectuncon@*/
+       (void) rpmdbCheckSignals();
 
-       if (fi->fc == 0) continue;
+       if ((fi = rpmtsiFi(pi)) == NULL)
+           continue;   /* XXX can't happen */
+       fc = rpmfiFC(fi);
 
+       NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, rpmtsiOc(pi),
+                       ts->orderCount, NULL, ts->notifyData));
+
+       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(fi->fc, sizeof(*matches));
-       if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc)) {
-           psm->ts = rpmtsUnlink(ts, "tsRun (rpmFindFpList fail)");
+       matches = xcalloc(fc, sizeof(*matches));
+       if (rpmdbFindFpList(rpmtsGetRdb(ts), fi->fps, matches, fc)) {
+           ps = rpmpsFree(ps);
            return 1;   /* XXX WTFO? */
        }
 
        numShared = 0;
-       for (i = 0; i < fi->fc; i++)
+       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));
-       for (i = 0; i < fi->fc; i++) {
+
+       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.
             */
            for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
-               int k, ro;
+               int ro;
                ro = dbiIndexRecordOffset(matches[i], j);
                knownBad = 0;
-               for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
-                   switch (ts->order[k].type) {
-                   case TR_REMOVED:
-                       if (ts->order[k].u.removed.dboffset == ro)
-                           knownBad = ro;
-                       /*@switchbreak@*/ break;
-                   case TR_ADDED:
-                       /*@switchbreak@*/ break;
-                   }
+               qi = rpmtsiInit(ts);
+               while ((q = rpmtsiNext(qi, TR_REMOVED)) != NULL) {
+                   if (ro == knownBad)
+                       /*@innerbreak@*/ break;
+                   if (rpmteDBOffset(q) == ro)
+                       knownBad = ro;
                }
+               qi = rpmtsiFree(qi);
 
                shared->pkgFileNum = i;
                shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
@@ -1316,6 +1303,7 @@ int keep_header = 1;      /* XXX rpmProblemSetAppend prevents dumping headers. */
        qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
 
        /* For all files from this package that are in the database ... */
+       /*@-branchstate@*/
        for (i = 0; i < numShared; i = nexti) {
            int beingRemoved;
 
@@ -1329,6 +1317,7 @@ int keep_header = 1;      /* XXX rpmProblemSetAppend prevents dumping headers. */
 
            /* Is this file from a package being removed? */
            beingRemoved = 0;
+           if (ts->removedPackages != NULL)
            for (j = 0; j < ts->numRemovedPackages; j++) {
                if (ts->removedPackages[j] != shared->otherPkg)
                    /*@innercontinue@*/ continue;
@@ -1337,10 +1326,10 @@ int keep_header = 1;    /* XXX rpmProblemSetAppend prevents dumping headers. */
            }
 
            /* Determine the fate of each file. */
-           switch (fi->type) {
+           switch (rpmteType(p)) {
            case TR_ADDED:
-               xx = handleInstInstalledFiles(ts, fi, shared, nexti - i,
-       !(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)));
+               xx = handleInstInstalledFiles(ts, p, fi, shared, nexti - i,
+       !(beingRemoved || (rpmtsFilterFlags(ts) & RPMPROB_FILTER_REPLACEOLDFILES)));
                /*@switchbreak@*/ break;
            case TR_REMOVED:
                if (!beingRemoved)
@@ -1348,247 +1337,275 @@ int keep_header = 1;  /* XXX rpmProblemSetAppend prevents dumping headers. */
                /*@switchbreak@*/ break;
            }
        }
+       /*@=branchstate@*/
 
        free(sharedList);
 
        /* Update disk space needs on each partition for this package. */
-       handleOverlappedFiles(ts, fi);
+       handleOverlappedFiles(ts, p, fi);
 
        /* Check added package has sufficient space on each partition used. */
-       switch (fi->type) {
+       switch (rpmteType(p)) {
        case TR_ADDED:
-           if (!(ts->di && fi->fc))
-               /*@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, fi->ap,
-                               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, fi->ap,
-                               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);
     }
-    tsi = tsFreeIterator(tsi);
+    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;
-       /*@-mods@*/
-       chroot_prefix = NULL;
-       /*@=mods@*/
-       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->flEntries,
+    NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->orderCount,
        NULL, ts->notifyData));
-    /*@=noeffectuncon @*/
 
     /* ===============================================
      * Free unused memory as soon as possible.
      */
-
-    tsi = tsInitIterator(ts);
-    while ((fi = tsNextIterator(tsi)) != NULL) {
-       if (fi->fc == 0)
+    pi = rpmtsiInit(ts);
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
+       if ((fi = rpmtsiFi(pi)) == NULL)
+           continue;   /* XXX can't happen */
+       if (rpmfiFC(fi) == 0)
            continue;
        fi->fps = _free(fi->fps);
     }
-    tsi = tsFreeIterator(tsi);
+    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)))
        )
     {
-       *newProbs = ts->probs;
-
-       ts->flList = freeFl(ts, ts->flList);
-       ts->flEntries = 0;
-       /*@-nullstate@*/
-       psm->ts = rpmtsUnlink(psm->ts, "tsRun (problems)");
        return ts->orderCount;
-       /*@=nullstate@*/
     }
 
     /* ===============================================
      * Save removed files before erasing.
      */
-    if (ts->transFlags & (RPMTRANS_FLAG_DIRSTASH | RPMTRANS_FLAG_REPACKAGE)) {
-       tsi = tsInitIterator(ts);
-       while ((fi = tsNextIterator(tsi)) != NULL) {
-           switch (fi->type) {
+    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) {
-                   psm->fi = rpmfiLink(fi, "tsRepackage");
-                   xx = psmStage(psm, PSM_PKGSAVE);
-                   (void) rpmfiUnlink(fi, "tsRepackage");
-                   psm->fi = NULL;
-               }
+               if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_REPACKAGE))
+                   /*@switchbreak@*/ break;
+               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;
            }
        }
-       tsi = tsFreeIterator(tsi);
+       pi = rpmtsiFree(pi);
+       if (progress) {
+           NOTIFY(ts, (NULL, RPMCALLBACK_REPACKAGE_STOP, 7, numRemoved,
+                       NULL, ts->notifyData));
+       }
     }
 
     /* ===============================================
      * Install and remove packages.
      */
-
-    lastFailed = -2;   /* erased packages have -1 */
-    tsi = tsInitIterator(ts);
+    lastFailKey = (alKey)-2;   /* erased packages have -1 */
+    pi = rpmtsiInit(ts);
     /*@-branchstate@*/ /* FIX: fi reload needs work */
-    while ((fi = tsNextIterator(tsi)) != NULL) {
-       Header h;
+    while ((p = rpmtsiNext(pi, 0)) != NULL) {
+       alKey pkgKey;
        int gotfd;
 
+       (void) rpmdbCheckSignals();
+
        gotfd = 0;
-       psm->fi = rpmfiLink(fi, "tsInstall");
-       switch (fi->type) {
+       if ((fi = rpmtsiFi(pi)) == NULL)
+           continue;   /* XXX can't happen */
+       
+       psm = rpmpsmNew(ts, p, fi);
+assert(psm != NULL);
+       psm->unorderedSuccessor =
+               (rpmtsiOc(pi) >= rpmtsUnorderedSuccessors(ts, -1) ? 1 : 0);
+
+       switch (rpmteType(p)) {
        case TR_ADDED:
-           alp = tsGetAlp(tsi);
-assert(alp == fi->ap);
-           i = alp - ts->addedPackages->list;
-
-           rpmMessage(RPMMESS_DEBUG, "========== +++ %s-%s-%s\n",
-                       fi->name, fi->version, fi->release);
-           h = (fi->h ? headerLink(fi->h) : NULL);
-           /*@-branchstate@*/
-           if (alp->fd == NULL) {
-               alp->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
-                           alp->key, ts->notifyData);
-               if (alp->fd) {
-                   rpmRC rpmrc;
+           (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_INSTALL), 0);
 
-                   h = headerFree(h);
+           pkgKey = rpmteAddedKey(p);
 
-                   /*@-mustmod@*/      /* LCL: segfault */
-                   rpmrc = rpmReadPackageFile(ts, alp->fd,
-                               "rpmRunTransactions", &h);
-                   /*@=mustmod@*/
+           rpmMessage(RPMMESS_DEBUG, "========== +++ %s\n", rpmteNEVR(p));
+           p->h = NULL;
+           /*@-type@*/ /* FIX: rpmte not opaque */
+           {
+               /*@-noeffectuncon@*/ /* FIX: notify annotations */
+               p->fd = ts->notify(p->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
+                               rpmteKey(p), ts->notifyData);
+               /*@=noeffectuncon@*/
+               if (rpmteFd(p) != NULL) {
+                   rpmVSFlags ovsflags = rpmtsVSFlags(ts);
+                   rpmVSFlags vsflags = ovsflags | RPMVSF_NEEDPAYLOAD;
+                   rpmRC rpmrc;
 
-                   if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
-                       /*@-noeffectuncon @*/ /* FIX: check rc */
-                       (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
-                                       0, 0, alp->key, ts->notifyData);
-                       /*@=noeffectuncon @*/
-                       alp->fd = NULL;
+                   ovsflags = rpmtsSetVSFlags(ts, vsflags);
+                   rpmrc = rpmReadPackageFile(ts, rpmteFd(p),
+                               rpmteNEVR(p), &p->h);
+                   vsflags = rpmtsSetVSFlags(ts, ovsflags);
+
+                   switch (rpmrc) {
+                   default:
+                       /*@-noeffectuncon@*/ /* FIX: notify annotations */
+                       p->fd = ts->notify(p->h, RPMCALLBACK_INST_CLOSE_FILE,
+                                       0, 0,
+                                       rpmteKey(p), ts->notifyData);
+                       /*@=noeffectuncon@*/
+                       p->fd = NULL;
                        ourrc++;
-                   } else if (fi->h != NULL) {
-                       Header foo = relocateFileList(ts, fi, alp, h, NULL);
-                       h = headerFree(h);
-                       h = headerLink(foo);
-                       foo = headerFree(foo);
+                       /*@innerbreak@*/ break;
+                   case RPMRC_NOTTRUSTED:
+                   case RPMRC_NOKEY:
+                   case RPMRC_OK:
+                       /*@innerbreak@*/ break;
                    }
-                   if (alp->fd) gotfd = 1;
+                   if (rpmteFd(p) != NULL) gotfd = 1;
                }
            }
-           /*@=branchstate@*/
-
-           if (alp->fd) {
-               Header hsave = NULL;
-
-               if (fi->h) {
-                   hsave = headerLink(fi->h);
-                   fi->h = headerFree(fi->h);
-                   fi->h = headerLink(h);
-               } else {
-char * fstates = fi->fstates;
-fileAction * actions = fi->actions;
-fi->fstates = NULL;
-fi->actions = NULL;
-                   freeFi(fi);
-oc = tsGetOc(tsi);
-fi->magic = TFIMAGIC;
-fi->type = ts->order[oc].type;
-fi->ap = tsGetAlp(tsi);
-fi->record = 0;
-                   loadFi(ts, fi, h, 1);
-fi->fstates = _free(fi->fstates);
-fi->fstates = fstates;
-fi->actions = _free(fi->actions);
-fi->actions = actions;
+           /*@=type@*/
+
+           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;
+                   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 (alp->multiLib)
-                   ts->transFlags |= RPMTRANS_FLAG_MULTILIB;
+               psm->fi = rpmfiLink(p->fi, NULL);
 
-assert(alp == fi->ap);
-               if (psmStage(psm, PSM_PKGINSTALL)) {
+/*@-nullstate@*/ /* FIX: psm->fi may be NULL */
+               if (rpmpsmStage(psm, PSM_PKGINSTALL)) {
                    ourrc++;
-                   lastFailed = i;
-               }
-               fi->h = headerFree(fi->h);
-               if (hsave) {
-                   fi->h = headerLink(hsave);
-                   hsave = headerFree(hsave);
+                   lastFailKey = pkgKey;
                }
+/*@=nullstate@*/
            } else {
                ourrc++;
-               lastFailed = i;
+               lastFailKey = pkgKey;
            }
 
-           h = headerFree(h);
-
            if (gotfd) {
                /*@-noeffectuncon @*/ /* FIX: check rc */
-               (void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
-                       alp->key, ts->notifyData);
+               (void) ts->notify(p->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
+                       rpmteKey(p), ts->notifyData);
                /*@=noeffectuncon @*/
-               alp->fd = NULL;
+               /*@-type@*/
+               p->fd = NULL;
+               /*@=type@*/
            }
-           freeFi(fi);
+
+           p->h = headerFree(p->h);
+
+           (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_INSTALL), 0);
+
            /*@switchbreak@*/ break;
+
        case TR_REMOVED:
-           rpmMessage(RPMMESS_DEBUG, "========== --- %s-%s-%s\n",
-                       fi->name, fi->version, fi->release);
-           oc = tsGetOc(tsi);
-           /* If install failed, then we shouldn't erase. */
-           if (ts->order[oc].u.removed.dependsOnIndex != lastFailed) {
-               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++;
            }
-           freeFi(fi);
+
+           (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_ERASE), 0);
+
            /*@switchbreak@*/ break;
        }
-       xx = rpmdbSync(ts->rpmdb);
-       (void) rpmfiUnlink(fi, "tsInstall");
-       psm->fi = NULL;
-    }
-    /*@=branchstate@*/
-    tsi = tsFreeIterator(tsi);
+       xx = rpmdbSync(rpmtsGetRdb(ts));
+
+/*@-nullstate@*/ /* FIX: psm->fi may be NULL */
+       psm = rpmpsmFree(psm);
+/*@=nullstate@*/
 
-    ts->flList = freeFl(ts, ts->flList);
-    ts->flEntries = 0;
+/*@-type@*/ /* FIX: p is almost opaque */
+       p->fi = rpmfiFree(p->fi);
+/*@=type@*/
 
-    psm->ts = rpmtsUnlink(psm->ts, "tsRun");
+    }
+    /*@=branchstate@*/
+    pi = rpmtsiFree(pi);
 
-    /*@-nullstate@*/
+    /*@-nullstate@*/ /* FIX: ts->flList may be NULL */
     if (ourrc)
        return -1;
     else