Imported Upstream version 4.14.1
[platform/upstream/rpm.git] / lib / fsm.c
index 1ee7e67..b88bf15 100644 (file)
--- a/lib/fsm.c
+++ b/lib/fsm.c
 
 #include <rpm/rpmte.h>
 #include <rpm/rpmts.h>
-#include <rpm/rpmsq.h>
 #include <rpm/rpmlog.h>
+#include <rpm/rpmmacro.h>
 
 #include "rpmio/rpmio_internal.h"      /* fdInit/FiniDigest */
-#include "lib/cpio.h"
 #include "lib/fsm.h"
-#define        fsmUNSAFE       fsmStage
-#include "lib/rpmfi_internal.h"        /* XXX fi->apath, ... */
 #include "lib/rpmte_internal.h"        /* XXX rpmfs */
-#include "lib/rpmts_internal.h"        /* rpmtsSELabelFoo() only */
+#include "lib/rpmplugins.h"    /* rpm plugins hooks */
 #include "lib/rpmug.h"
-#include "lib/cpio.h"
 
 #include "debug.h"
 
 #define        _FSM_DEBUG      0
 int _fsm_debug = _FSM_DEBUG;
 
-extern int _fsm_debug;
-
-/** \ingroup payload
- */
-enum cpioMapFlags_e {
-    CPIO_MAP_PATH      = (1 << 0),
-    CPIO_MAP_MODE      = (1 << 1),
-    CPIO_MAP_UID       = (1 << 2),
-    CPIO_MAP_GID       = (1 << 3),
-    CPIO_FOLLOW_SYMLINKS= (1 << 4), /*!< only for building. */
-    CPIO_MAP_ABSOLUTE  = (1 << 5),
-    CPIO_MAP_ADDDOT    = (1 << 6),
-    CPIO_MAP_TYPE      = (1 << 8),  /*!< only for building. */
-    CPIO_SBIT_CHECK    = (1 << 9)
-};
-typedef rpmFlags cpioMapFlags;
-
-typedef struct fsmIterator_s * FSMI_t;
-typedef struct fsm_s * FSM_t;
-
-typedef struct hardLink_s * hardLink_t;
-
-typedef enum fileStage_e {
-    FSM_PKGINSTALL,
-    FSM_PKGERASE,
-    FSM_PKGBUILD,
-} fileStage;
-
 /* XXX Failure to remove is not (yet) cause for failure. */
 static int strict_erasures = 0;
 
-/** \ingroup payload
- * Keeps track of the set of all hard links to a file in an archive.
- */
-struct hardLink_s {
-    hardLink_t next;
-    const char ** nsuffix;
-    int * filex;
-    struct stat sb;
-    nlink_t nlink;
-    nlink_t linksLeft;
-    int linkIndex;
-    int createdPath;
-};
-
-/** \ingroup payload
- * Iterator across package file info, forward on install, backward on erase.
- */
-struct fsmIterator_s {
-    rpmfs fs;                  /*!< file state info. */
-    rpmfi fi;                  /*!< transaction element file info. */
-    int reverse;               /*!< reversed traversal? */
-    int isave;                 /*!< last returned iterator index. */
-    int i;                     /*!< iterator index. */
-};
-
-/** \ingroup payload
- * File name and stat information.
- */
-struct fsm_s {
-    char * path;               /*!< Current file name. */
-    char * buf;                        /*!<  read: Buffer. */
-    size_t bufsize;            /*!<  read: Buffer allocated size. */
-    FSMI_t iter;               /*!< File iterator. */
-    int ix;                    /*!< Current file iterator index. */
-    hardLink_t links;          /*!< Pending hard linked file(s). */
-    char ** failedFile;                /*!< First file name that failed. */
-    const char * osuffix;      /*!< Old, preserved, file suffix. */
-    const char * nsuffix;      /*!< New, created, file suffix. */
-    char * suffix;             /*!< Current file suffix. */
-    int postpone;              /*!< Skip remaining stages? */
-    int diskchecked;           /*!< Has stat(2) been performed? */
-    int exists;                        /*!< Does current file exist on disk? */
-    cpioMapFlags mapFlags;     /*!< Bit(s) to control mapping. */
-    const char * dirName;      /*!< File directory name. */
-    const char * baseName;     /*!< File base name. */
-    struct selabel_handle *sehandle;   /*!< SELinux label handle (if any). */
-
-    unsigned fflags;           /*!< File flags. */
-    rpmFileAction action;      /*!< File disposition. */
-    fileStage goal;            /*!< Package state machine goal. */
-    struct stat sb;            /*!< Current file stat(2) info. */
-    struct stat osb;           /*!< Original file stat(2) info. */
-};
-
-
-/**
- * Retrieve transaction element file info from file state machine iterator.
- * @param fsm          file state machine
- * @return             transaction element file info
- */
-static rpmfi fsmGetFi(const FSM_t fsm)
-{
-    const FSMI_t iter = fsm->iter;
-    return (iter ? iter->fi : NULL);
-}
-
-static rpmfs fsmGetFs(const FSM_t fsm)
-{
-    const FSMI_t iter = fsm->iter;
-    return (iter ? iter->fs : NULL);
-}
-
 #define        SUFFIX_RPMORIG  ".rpmorig"
 #define        SUFFIX_RPMSAVE  ".rpmsave"
 #define        SUFFIX_RPMNEW   ".rpmnew"
@@ -150,126 +46,20 @@ static const char * fileActionString(rpmFileAction a);
 
 /** \ingroup payload
  * Build path to file from file info, optionally ornamented with suffix.
- * @param fsm          file state machine data
- * @param isDir                directory or regular path?
+ * @param fi           file info iterator
  * @param suffix       suffix to use (NULL disables)
  * @retval             path to file (malloced)
  */
-static char * fsmFsPath(const FSM_t fsm, int isDir,
-                       const char * suffix)
+static char * fsmFsPath(rpmfi fi, const char * suffix)
 {
-    return rstrscat(NULL,
-                   fsm->dirName, fsm->baseName,
-                   (!isDir && suffix) ? suffix : "",
-                   NULL);
-}
-
-/** \ingroup payload
- * Destroy file info iterator.
- * @param p            file info iterator
- * @retval             NULL always
- */
-static FSMI_t mapFreeIterator(FSMI_t iter)
-{
-    if (iter) {
-       iter->fs = NULL; /* rpmfs is not refcounted */
-       iter->fi = rpmfiFree(iter->fi);
-       free(iter);
-    }
-    return NULL;
-}
-
-/** \ingroup payload
- * Create file info iterator.
- * @param fi           transaction element file info
- * @return             file info iterator
- */
-static FSMI_t 
-mapInitIterator(rpmfs fs, rpmfi fi, int reverse)
-{
-    FSMI_t iter = NULL;
-
-    iter = xcalloc(1, sizeof(*iter));
-    iter->fs = fs; /* rpmfs is not refcounted */
-    iter->fi = rpmfiLink(fi);
-    iter->reverse = reverse;
-    iter->i = (iter->reverse ? (rpmfiFC(fi) - 1) : 0);
-    iter->isave = iter->i;
-    return iter;
-}
-
-/** \ingroup payload
- * Return next index into file info.
- * @param a            file info iterator
- * @return             next index, -1 on termination
- */
-static int mapNextIterator(FSMI_t iter)
-{
-    int i = -1;
-
-    if (iter) {
-       const rpmfi fi = iter->fi;
-       if (iter->reverse) {
-           if (iter->i >= 0)   i = iter->i--;
-       } else {
-           if (iter->i < rpmfiFC(fi))  i = iter->i++;
-       }
-       iter->isave = i;
-    }
-    return i;
-}
-
-/** \ingroup payload
- */
-static int cpioStrCmp(const void * a, const void * b)
-{
-    const char * afn = *(const char **)a;
-    const char * bfn = *(const char **)b;
-
-    /* Match rpm-4.0 payloads with ./ prefixes. */
-    if (afn[0] == '.' && afn[1] == '/')        afn += 2;
-    if (bfn[0] == '.' && bfn[1] == '/')        bfn += 2;
-
-    /* If either path is absolute, make it relative. */
-    if (afn[0] == '/') afn += 1;
-    if (bfn[0] == '/') bfn += 1;
-
-    return strcmp(afn, bfn);
-}
-
-/** \ingroup payload
- * Locate archive path in file info.
- * @param iter         file info iterator
- * @param fsmPath      archive path
- * @return             index into file info, -1 if archive path was not found
- */
-static int mapFind(FSMI_t iter, const char * fsmPath)
-{
-    int ix = -1;
-
-    if (iter) {
-       const rpmfi fi = iter->fi;
-       int fc = rpmfiFC(fi);
-       if (fi && fc > 0 && fi->apath && fsmPath && *fsmPath) {
-           char ** p = NULL;
-
-           if (fi->apath != NULL)
-               p = bsearch(&fsmPath, fi->apath, fc, sizeof(fsmPath),
-                       cpioStrCmp);
-           if (p) {
-               iter->i = p - fi->apath;
-               ix = mapNextIterator(iter);
-           }
-       }
-    }
-    return ix;
+    return rstrscat(NULL, rpmfiDN(fi), rpmfiBN(fi), suffix ? suffix : "", NULL);
 }
 
 /** \ingroup payload
  * Directory name iterator.
  */
 typedef struct dnli_s {
-    rpmfi fi;
+    rpmfiles fi;
     char * active;
     int reverse;
     int isave;
@@ -278,7 +68,7 @@ typedef struct dnli_s {
 
 /** \ingroup payload
  * Destroy directory name iterator.
- * @param a            directory name iterator
+ * @param dnli         directory name iterator
  * @retval             NULL always
  */
 static DNLI_t dnlFreeIterator(DNLI_t dnli)
@@ -297,7 +87,7 @@ static DNLI_t dnlFreeIterator(DNLI_t dnli)
  * @param reverse      traverse directory names in reverse order?
  * @return             directory name iterator
  */
-static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse)
+static DNLI_t dnlInitIterator(rpmfiles fi, rpmfs fs, int reverse)
 {
     DNLI_t dnli;
     int i, j;
@@ -305,7 +95,7 @@ static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse)
 
     if (fi == NULL)
        return NULL;
-    dc = rpmfiDC(fi);
+    dc = rpmfilesDC(fi);
     dnli = xcalloc(1, sizeof(*dnli));
     dnli->fi = fi;
     dnli->reverse = reverse;
@@ -313,24 +103,24 @@ static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse)
 
     if (dc) {
        dnli->active = xcalloc(dc, sizeof(*dnli->active));
-       int fc = rpmfiFC(fi);
+       int fc = rpmfilesFC(fi);
 
        /* Identify parent directories not skipped. */
        for (i = 0; i < fc; i++)
             if (!XFA_SKIPPING(rpmfsGetAction(fs, i)))
-               dnli->active[rpmfiDIIndex(fi, i)] = 1;
+               dnli->active[rpmfilesDI(fi, i)] = 1;
 
        /* Exclude parent directories that are explicitly included. */
        for (i = 0; i < fc; i++) {
            int dil;
            size_t dnlen, bnlen;
 
-           if (!S_ISDIR(rpmfiFModeIndex(fi, i)))
+           if (!S_ISDIR(rpmfilesFMode(fi, i)))
                continue;
 
-           dil = rpmfiDIIndex(fi, i);
-           dnlen = strlen(rpmfiDNIndex(fi, dil));
-           bnlen = strlen(rpmfiBNIndex(fi, i));
+           dil = rpmfilesDI(fi, i);
+           dnlen = strlen(rpmfilesDN(fi, dil));
+           bnlen = strlen(rpmfilesBN(fi, i));
 
            for (j = 0; j < dc; j++) {
                const char * dnl;
@@ -338,13 +128,13 @@ static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse)
 
                if (!dnli->active[j] || j == dil)
                    continue;
-               dnl = rpmfiDNIndex(fi, j);
+               dnl = rpmfilesDN(fi, j);
                jlen = strlen(dnl);
                if (jlen != (dnlen+bnlen+1))
                    continue;
-               if (!rstreqn(dnl, rpmfiDNIndex(fi, dil), dnlen))
+               if (!rstreqn(dnl, rpmfilesDN(fi, dil), dnlen))
                    continue;
-               if (!rstreqn(dnl+dnlen, rpmfiBNIndex(fi, i), bnlen))
+               if (!rstreqn(dnl+dnlen, rpmfilesBN(fi, i), bnlen))
                    continue;
                if (dnl[dnlen+bnlen] != '/' || dnl[dnlen+bnlen+1] != '\0')
                    continue;
@@ -364,7 +154,7 @@ static DNLI_t dnlInitIterator(rpmfi fi, rpmfs fs, int reverse)
                    rpmlog(RPMLOG_DEBUG,
        "========== Directories not explicitly included in package:\n");
                }
-               rpmlog(RPMLOG_DEBUG, "%10d %s\n", i, rpmfiDNIndex(fi, i));
+               rpmlog(RPMLOG_DEBUG, "%10d %s\n", i, rpmfilesDN(fi, i));
            }
            if (j)
                rpmlog(RPMLOG_DEBUG, "==========\n");
@@ -384,8 +174,8 @@ const char * dnlNextIterator(DNLI_t dnli)
     const char * dn = NULL;
 
     if (dnli) {
-       rpmfi fi = dnli->fi;
-       int dc = rpmfiDC(fi);
+       rpmfiles fi = dnli->fi;
+       int dc = rpmfilesDC(fi);
        int i = -1;
 
        if (dnli->active)
@@ -394,7 +184,7 @@ const char * dnlNextIterator(DNLI_t dnli)
        } while (i >= 0 && i < dc && !dnli->active[i]);
 
        if (i >= 0 && i < dc)
-           dn = rpmfiDNIndex(fi, i);
+           dn = rpmfilesDN(fi, i);
        else
            i = -1;
        dnli->isave = i;
@@ -402,251 +192,6 @@ const char * dnlNextIterator(DNLI_t dnli)
     return dn;
 }
 
-/**
- * Map next file path and action.
- * @param fsm          file state machine
- * @param i            file index
- */
-static int fsmMapPath(FSM_t fsm, int i)
-{
-    rpmfi fi = fsmGetFi(fsm);  /* XXX const except for fstates */
-    int rc = 0;
-
-    fsm->osuffix = NULL;
-    fsm->nsuffix = NULL;
-    fsm->action = FA_UNKNOWN;
-
-    if (fi && i >= 0 && i < rpmfiFC(fi)) {
-       rpmfs fs = fsmGetFs(fsm);
-       /* XXX these should use rpmfiFFlags() etc */
-       fsm->action = rpmfsGetAction(fs, i);
-       fsm->fflags = rpmfiFFlagsIndex(fi, i);
-
-       /* src rpms have simple base name in payload. */
-       fsm->dirName = rpmfiDNIndex(fi, rpmfiDIIndex(fi, i));
-       fsm->baseName = rpmfiBNIndex(fi, i);
-
-       /* Never create backup for %ghost files. */
-       if (fsm->goal != FSM_PKGBUILD && !(fsm->fflags & RPMFILE_GHOST)) {
-           switch (fsm->action) {
-           case FA_ALTNAME:
-               fsm->nsuffix = SUFFIX_RPMNEW;
-               break;
-           case FA_SAVE:
-               fsm->osuffix = SUFFIX_RPMSAVE;
-               break;
-           case FA_BACKUP:
-               fsm->osuffix = (fsm->goal == FSM_PKGINSTALL) ?
-                               SUFFIX_RPMORIG : SUFFIX_RPMSAVE;
-               break;
-           default:
-               break;
-           }
-       }
-
-       if ((fsm->mapFlags & CPIO_MAP_PATH) || fsm->nsuffix) {
-           fsm->path = _free(fsm->path);
-           fsm->path = fsmFsPath(fsm, S_ISDIR(fsm->sb.st_mode),
-               (fsm->suffix ? fsm->suffix : fsm->nsuffix));
-       }
-    }
-    return rc;
-}
-
-/** \ingroup payload
- * Save hard link in chain.
- * @param fsm          file state machine data
- * @retval linkSet     hard link set when complete
- * @return             Is chain only partially filled?
- */
-static int saveHardLink(FSM_t fsm, hardLink_t * linkSet)
-{
-    struct stat * st = &fsm->sb;
-    int rc = 0;
-    int ix = -1;
-    int j;
-    hardLink_t *tailp, li;
-
-    /* Find hard link set. */
-    for (tailp = &fsm->links; (li = *tailp) != NULL; tailp = &li->next) {
-       if (li->sb.st_ino == st->st_ino && li->sb.st_dev == st->st_dev)
-           break;
-    }
-
-    /* New hard link encountered, add new link to set. */
-    if (li == NULL) {
-       li = xcalloc(1, sizeof(*li));
-       li->next = NULL;
-       li->sb = *st;   /* structure assignment */
-       li->nlink = st->st_nlink;
-       li->linkIndex = fsm->ix;
-       li->createdPath = -1;
-
-       li->filex = xcalloc(st->st_nlink, sizeof(li->filex[0]));
-       memset(li->filex, -1, (st->st_nlink * sizeof(li->filex[0])));
-       li->nsuffix = xcalloc(st->st_nlink, sizeof(*li->nsuffix));
-
-       if (fsm->goal == FSM_PKGBUILD)
-           li->linksLeft = st->st_nlink;
-       if (fsm->goal == FSM_PKGINSTALL)
-           li->linksLeft = 0;
-
-       *tailp = li;    /* append to tail of linked list */
-    }
-
-    if (fsm->goal == FSM_PKGBUILD) --li->linksLeft;
-    li->filex[li->linksLeft] = fsm->ix;
-    li->nsuffix[li->linksLeft] = fsm->nsuffix;
-    if (fsm->goal == FSM_PKGINSTALL) li->linksLeft++;
-
-    if (fsm->goal == FSM_PKGBUILD)
-       return (li->linksLeft > 0);
-
-    if (fsm->goal != FSM_PKGINSTALL)
-       return 0;
-
-    if (!(st->st_size || li->linksLeft == st->st_nlink))
-       return 1;
-
-    /* Here come the bits, time to choose a non-skipped file name. */
-    {  rpmfs fs = fsmGetFs(fsm);
-
-       for (j = li->linksLeft - 1; j >= 0; j--) {
-           ix = li->filex[j];
-           if (ix < 0 || XFA_SKIPPING(rpmfsGetAction(fs, ix)))
-               continue;
-           break;
-       }
-    }
-
-    /* Are all links skipped or not encountered yet? */
-    if (ix < 0 || j < 0)
-       return 1;       /* XXX W2DO? */
-
-    /* Save the non-skipped file name and map index. */
-    li->linkIndex = j;
-    if (linkSet)
-       *linkSet = li;
-    fsm->path = _free(fsm->path);
-    fsm->ix = ix;
-    rc = fsmMapPath(fsm, fsm->ix);
-    return rc;
-}
-
-/** \ingroup payload
- * Destroy set of hard links.
- * @param li           set of hard links
- * @return             NULL always
- */
-static hardLink_t freeHardLink(hardLink_t li)
-{
-    if (li) {
-       li->nsuffix = _free(li->nsuffix);       /* XXX elements are shared */
-       li->filex = _free(li->filex);
-       _free(li);
-    }
-    return NULL;
-}
-
-/* Check for hard links missing from payload */
-static int checkHardLinks(FSM_t fsm)
-{
-    int rc = 0;
-    rpmfs fs = fsmGetFs(fsm);
-
-    for (hardLink_t li = fsm->links; li != NULL; li = li->next) {
-       if (li->linksLeft) {
-           for (nlink_t i = 0 ; i < li->linksLeft; i++) {
-               int ix = li->filex[i];
-               if (ix < 0 || XFA_SKIPPING(rpmfsGetAction(fs, ix)))
-                   continue;
-               rc = CPIOERR_MISSING_HARDLINK;
-               if (fsm->failedFile && *fsm->failedFile == NULL) {
-                   if (!fsmMapPath(fsm, ix)) {
-                       /* Out-of-sync hardlinks handled as sub-state */
-                       *fsm->failedFile = fsm->path;
-                       fsm->path = NULL;
-                   }
-               }
-               break;
-           }
-       }
-    }
-    return rc;
-}
-
-static FSM_t fsmNew(fileStage goal, rpmfs fs, rpmfi fi, char ** failedFile)
-{
-    FSM_t fsm = xcalloc(1, sizeof(*fsm));
-
-    fsm->ix = -1;
-    fsm->goal = goal;
-    fsm->iter = mapInitIterator(fs, fi, (goal == FSM_PKGERASE));
-
-    /* common flags for all modes */
-    fsm->mapFlags = CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
-
-    if (fsm->goal == FSM_PKGINSTALL || fsm->goal == FSM_PKGBUILD) {
-        fsm->bufsize = 8 * BUFSIZ;
-        fsm->buf = xmalloc(fsm->bufsize);
-    }
-
-    fsm->failedFile = failedFile;
-    if (fsm->failedFile)
-       *fsm->failedFile = NULL;
-
-    return fsm;
-}
-
-static FSM_t fsmFree(FSM_t fsm)
-{
-    hardLink_t li;
-    fsm->buf = _free(fsm->buf);
-    fsm->bufsize = 0;
-
-    fsm->iter = mapFreeIterator(fsm->iter);
-    fsm->failedFile = NULL;
-
-    fsm->path = _free(fsm->path);
-    fsm->suffix = _free(fsm->suffix);
-
-    while ((li = fsm->links) != NULL) {
-       fsm->links = li->next;
-       li->next = NULL;
-       freeHardLink(li);
-    }
-    free(fsm);
-    return NULL;
-}
-
-/* Find and set file security context */
-static int fsmSetSELabel(struct selabel_handle *sehandle,
-                        const char *path, mode_t mode)
-{
-    int rc = 0;
-#if WITH_SELINUX
-    if (sehandle) {
-       security_context_t scon = NULL;
-
-       if (selabel_lookup_raw(sehandle, &scon, path, mode) == 0) {
-           rc = lsetfilecon(path, scon);
-
-           if (_fsm_debug) {
-               rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n",
-                       __func__, path, scon,
-                       (rc < 0 ? strerror(errno) : ""));
-           }
-
-           if (rc < 0 && errno == EOPNOTSUPP)
-               rc = 0;
-       }
-
-       freecon(scon);
-    }
-#endif
-    return rc ? CPIOERR_LSETFCON_FAILED : 0;
-}
-
 static int fsmSetFCaps(const char *path, const char *captxt)
 {
     int rc = 0;
@@ -654,7 +199,7 @@ static int fsmSetFCaps(const char *path, const char *captxt)
     if (captxt && *captxt != '\0') {
        cap_t fcaps = cap_from_text(captxt);
        if (fcaps == NULL || cap_set_file(path, fcaps) != 0) {
-           rc = CPIOERR_SETCAP_FAILED;
+           rc = RPMERR_SETCAP_FAILED;
        }
        cap_free(fcaps);
     } 
@@ -662,132 +207,102 @@ static int fsmSetFCaps(const char *path, const char *captxt)
     return rc;
 }
 
-/**
- * Map file stat(2) info.
- * @param fsm          file state machine
- */
-static int fsmMapAttrs(FSM_t fsm)
+static void wfd_close(FD_t *wfdp)
 {
-    struct stat * st = &fsm->sb;
-    rpmfi fi = fsmGetFi(fsm);
-    int i = fsm->ix;
-
-    /* this check is pretty moot,  rpmfi accessors check array bounds etc */
-    if (fi && i >= 0 && i < rpmfiFC(fi)) {
-       ino_t finalInode = rpmfiFInodeIndex(fi, i);
-       mode_t finalMode = rpmfiFModeIndex(fi, i);
-       dev_t finalRdev = rpmfiFRdevIndex(fi, i);
-       time_t finalMtime = rpmfiFMtimeIndex(fi, i);
-       const char *user = rpmfiFUserIndex(fi, i);
-       const char *group = rpmfiFGroupIndex(fi, i);
-       uid_t uid = 0;
-       gid_t gid = 0;
-
-       if (user && rpmugUid(user, &uid)) {
-           if (fsm->goal == FSM_PKGINSTALL)
-               rpmlog(RPMLOG_WARNING,
-                   _("user %s does not exist - using root\n"), user);
-           finalMode &= ~S_ISUID;      /* turn off suid bit */
+    if (wfdp && *wfdp) {
+       int myerrno = errno;
+       static int oneshot = 0;
+       static int flush_io = 0;
+       if (!oneshot) {
+           flush_io = rpmExpandNumeric("%{?_flush_io}");
+           oneshot = 1;
        }
-
-       if (group && rpmugGid(group, &gid)) {
-           if (fsm->goal == FSM_PKGINSTALL)
-               rpmlog(RPMLOG_WARNING,
-                   _("group %s does not exist - using root\n"), group);
-           finalMode &= ~S_ISGID;      /* turn off sgid bit */
+       if (flush_io) {
+           int fdno = Fileno(*wfdp);
+           fsync(fdno);
        }
+       Fclose(*wfdp);
+       *wfdp = NULL;
+       errno = myerrno;
+    }
+}
 
-       if (fsm->mapFlags & CPIO_MAP_MODE)
-           st->st_mode = (st->st_mode & S_IFMT) | (finalMode & ~S_IFMT);
-       if (fsm->mapFlags & CPIO_MAP_TYPE) {
-           st->st_mode = (st->st_mode & ~S_IFMT) | (finalMode & S_IFMT);
-           if ((S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
-           && st->st_nlink == 0)
-               st->st_nlink = 1;
-           st->st_ino = finalInode;
-           st->st_rdev = finalRdev;
-           st->st_mtime = finalMtime;
-       }
-       if (fsm->mapFlags & CPIO_MAP_UID)
-           st->st_uid = uid;
-       if (fsm->mapFlags & CPIO_MAP_GID)
-           st->st_gid = gid;
+static int wfd_open(FD_t *wfdp, const char *dest)
+{
+    int rc = 0;
+    /* Create the file with 0200 permissions (write by owner). */
+    {
+       mode_t old_umask = umask(0577);
+       *wfdp = Fopen(dest, "wx.ufdio");
+       umask(old_umask);
+    }
+    if (Ferror(*wfdp)) {
+       rc = RPMERR_OPEN_FAILED;
+       goto exit;
     }
+
     return 0;
+
+exit:
+    wfd_close(wfdp);
+    return rc;
 }
 
 /** \ingroup payload
  * Create file from payload stream.
- * @param fsm          file state machine data
- * @param archive      payload archive
  * @return             0 on success
  */
-static int expandRegular(FSM_t fsm, rpmpsm psm, rpmcpio_t archive, int nodigest)
+static int expandRegular(rpmfi fi, const char *dest, rpmpsm psm, int nodigest)
 {
     FD_t wfd = NULL;
-    const struct stat * st = &fsm->sb;
-    rpm_loff_t left = st->st_size;
-    const unsigned char * fidigest = NULL;
-    pgpHashAlgo digestalgo = 0;
-    int rc = 0;
-
-    wfd = Fopen(fsm->path, "w.ufdio");
-    if (Ferror(wfd)) {
-       rc = CPIOERR_OPEN_FAILED;
-       goto exit;
-    }
-
-    if (!nodigest) {
-       rpmfi fi = fsmGetFi(fsm);
-       digestalgo = rpmfiDigestAlgo(fi);
-       fidigest = rpmfiFDigestIndex(fi, fsm->ix, NULL, NULL);
-    }
-
-    if (st->st_size > 0 && fidigest)
-       fdInitDigest(wfd, digestalgo, 0);
-
-    while (left) {
-        size_t len;
-       len = (left > fsm->bufsize ? fsm->bufsize : left);
-        if (rpmcpioRead(archive, fsm->buf, len) != len) {
-            rc = CPIOERR_READ_FAILED;
-           goto exit;
-        }
-       if ((Fwrite(fsm->buf, sizeof(*fsm->buf), len, wfd) != len) || Ferror(wfd)) {
-           rc = CPIOERR_WRITE_FAILED;
-           goto exit;
-       }
-
-       left -= len;
+    int rc;
 
-       /* don't call this with fileSize == fileComplete */
-       if (!rc && left)
-           rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmcpioTell(archive));
-    }
+    rc = wfd_open(&wfd, dest);
+    if (rc != 0)
+        goto exit;
 
-    if (st->st_size > 0 && fidigest) {
-       void * digest = NULL;
+    rc = rpmfiArchiveReadToFilePsm(fi, wfd, nodigest, psm);
+    wfd_close(&wfd);
+exit:
+    return rc;
+}
 
-       (void) Fflush(wfd);
-       fdFiniDigest(wfd, digestalgo, &digest, NULL, 0);
+static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files,
+                    rpmpsm psm, int nodigest, int *setmeta,
+                    int * firsthardlink, FD_t *firstlinkfile)
+{
+    int rc = 0;
+    int numHardlinks = rpmfiFNlink(fi);
 
-       if (digest != NULL && fidigest != NULL) {
-           size_t diglen = rpmDigestLength(digestalgo);
-           if (memcmp(digest, fidigest, diglen)) {
-               rc = CPIOERR_DIGEST_MISMATCH;
-            }
+    if (numHardlinks > 1) {
+       /* Create first hardlinked file empty */
+       if (*firsthardlink < 0) {
+           *firsthardlink = rpmfiFX(fi);
+           rc = wfd_open(firstlinkfile, dest);
        } else {
-           rc = CPIOERR_DIGEST_MISMATCH;
+           /* Create hard links for others */
+           char *fn = rpmfilesFN(files, *firsthardlink);
+           rc = link(fn, dest);
+           if (rc < 0) {
+               rc = RPMERR_LINK_FAILED;
+           }
+           free(fn);
        }
-       free(digest);
     }
-
-exit:
-    if (wfd) {
-       int myerrno = errno;
-       Fclose(wfd);
-       errno = myerrno;
+    /* Write normal files or fill the last hardlinked (already
+       existing) file with content */
+    if (numHardlinks<=1) {
+       if (!rc)
+           rc = expandRegular(fi, dest, psm, nodigest);
+    } else if (rpmfiArchiveHasContent(fi)) {
+       if (!rc)
+           rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm);
+       wfd_close(firstlinkfile);
+       *firsthardlink = -1;
+    } else {
+       *setmeta = 0;
     }
+
     return rc;
 }
 
@@ -795,7 +310,7 @@ static int fsmReadLink(const char *path,
                       char *buf, size_t bufsize, size_t *linklen)
 {
     ssize_t llen = readlink(path, buf, bufsize - 1);
-    int rc = CPIOERR_READLINK_FAILED;
+    int rc = RPMERR_READLINK_FAILED;
 
     if (_fsm_debug) {
         rpmlog(RPMLOG_DEBUG, " %8s (%s, buf, %d) %s\n",
@@ -811,168 +326,6 @@ static int fsmReadLink(const char *path,
     return rc;
 }
 
-/** \ingroup payload
- * Write next item to payload stream.
- * @param fsm          file state machine data
- * @param writeData    should data be written?
- * @param archive      payload archive
- * @param ix           file index
- * @return             0 on success
- */
-static int writeFile(FSM_t fsm, int writeData, rpmcpio_t archive, int ix)
-{
-    FD_t rfd = NULL;
-    char * path = fsm->path;
-    struct stat * st = &fsm->sb;
-    struct stat * ost = &fsm->osb;
-    char * symbuf = NULL;
-    rpm_loff_t left;
-    int rc = 0;
-
-    st->st_size = (writeData ? ost->st_size : 0);
-
-    if (S_ISDIR(st->st_mode)) {
-       st->st_size = 0;
-    } else if (S_ISLNK(st->st_mode)) {
-       /*
-        * While linux puts the size of a symlink in the st_size field,
-        * I don't think that's a specified standard.
-        */
-        size_t linklen;
-       rc = fsmReadLink(fsm->path, fsm->buf, fsm->bufsize, &linklen);
-       if (rc) goto exit;
-       st->st_size = linklen;
-       rstrcat(&symbuf, fsm->buf);     /* XXX save readlink return. */
-    }
-
-    if (fsm->mapFlags & CPIO_MAP_ABSOLUTE) {
-       fsm->path = rstrscat(NULL, (fsm->mapFlags & CPIO_MAP_ADDDOT) ? "." : "",
-                                  fsm->dirName, fsm->baseName, NULL);
-    } else if (fsm->mapFlags & CPIO_MAP_PATH) {
-       rpmfi fi = fsmGetFi(fsm);
-       fsm->path = xstrdup((fi->apath ? fi->apath[ix] : 
-                                        rpmfiBNIndex(fi, ix)));
-    }
-
-    rc = rpmcpioHeaderWrite(archive, fsm->path, st);
-    _free(fsm->path);
-    fsm->path = path;
-
-    if (rc) goto exit;
-
-
-    if (writeData && S_ISREG(st->st_mode)) {
-       size_t len;
-
-       rfd = Fopen(fsm->path, "r.ufdio");
-       if (Ferror(rfd)) {
-           rc = CPIOERR_OPEN_FAILED;
-           goto exit;
-       }
-       
-       left = st->st_size;
-
-       while (left) {
-           len = (left > fsm->bufsize ? fsm->bufsize : left);
-           if (Fread(fsm->buf, sizeof(*fsm->buf), len, rfd) != len || Ferror(rfd)) {
-               rc = CPIOERR_READ_FAILED;
-               goto exit;
-           }
-
-           if (rpmcpioWrite(archive, fsm->buf, len) != len) {
-               rc = CPIOERR_WRITE_FAILED;
-               goto exit;
-           }
-           left -= len;
-       }
-    } else if (writeData && S_ISLNK(st->st_mode)) {
-        size_t len = strlen(symbuf);
-        if (rpmcpioWrite(archive, symbuf, len) != len) {
-            rc = CPIOERR_WRITE_FAILED;
-            goto exit;
-        }
-    }
-
-exit:
-    if (rfd) {
-       /* preserve any prior errno across close */
-       int myerrno = errno;
-       Fclose(rfd);
-       errno = myerrno;
-    }
-    fsm->path = path;
-    free(symbuf);
-    return rc;
-}
-
-/** \ingroup payload
- * Write set of linked files to payload stream.
- * @param fsm          file state machine data
- * @param archive      payload archive
- * @param li           link to write
- * @return             0 on success
- */
-static int writeLinkedFile(FSM_t fsm, rpmcpio_t archive, hardLink_t li)
-{
-    char * path = fsm->path;
-    const char * nsuffix = fsm->nsuffix;
-    int ec = 0;
-    int rc;
-    int i;
-
-    fsm->path = NULL;
-    fsm->nsuffix = NULL;
-
-    for (i = li->nlink - 1; i >= 0; i--) {
-
-       if (li->filex[i] < 0) continue;
-
-       rc = fsmMapPath(fsm, li->filex[i]);
-
-       /* Write data after last link. */
-       rc = writeFile(fsm, (i == 0), archive, li->filex[i]);
-       if (fsm->failedFile && rc != 0 && *fsm->failedFile == NULL) {
-           ec = rc;
-           *fsm->failedFile = xstrdup(fsm->path);
-       }
-
-       fsm->path = _free(fsm->path);
-       li->filex[i] = -1;
-    }
-
-    fsm->nsuffix = nsuffix;
-    fsm->path = path;
-    return ec;
-}
-
-static int writeLinks(FSM_t fsm, rpmcpio_t archive)
-{
-    int j, rc = 0;
-    nlink_t i, nlink;
-
-    for (hardLink_t li = fsm->links; li; li = li->next) {
-       /* Re-calculate link count for archive header. */
-       for (j = -1, nlink = 0, i = 0; i < li->nlink; i++) {
-           if (li->filex[i] < 0)
-               continue;
-           nlink++;
-           if (j == -1) j = i;
-       }
-       /* XXX force the contents out as well. */
-       if (j != 0) {
-           li->filex[0] = li->filex[j];
-           li->filex[j] = -1;
-       }
-       li->sb.st_nlink = nlink;
-
-       fsm->sb = li->sb;       /* structure assignment */
-       fsm->osb = fsm->sb;     /* structure assignment */
-
-       if (!rc) rc = writeLinkedFile(fsm, archive, li);
-    }
-    return rc;
-}
-
 static int fsmStat(const char *path, int dolstat, struct stat *sb)
 {
     int rc;
@@ -986,108 +339,13 @@ static int fsmStat(const char *path, int dolstat, struct stat *sb)
                __func__,
                path, (rc < 0 ? strerror(errno) : ""));
     if (rc < 0) {
-        rc = (errno == ENOENT ? CPIOERR_ENOENT : CPIOERR_LSTAT_FAILED);
-       /* WTH is this, and is it really needed, still? */
-        memset(sb, 0, sizeof(*sb));    /* XXX s390x hackery */
+        rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_LSTAT_FAILED);
+       /* Ensure consistent struct content on failure */
+        memset(sb, 0, sizeof(*sb));
     }
     return rc;
 }
 
-static int fsmVerify(FSM_t fsm);
-
-/** \ingroup payload
- * Create pending hard links to existing file.
- * @param fsm          file state machine data
- * @param li           hard link
- * @return             0 on success
- */
-static int fsmMakeLinks(FSM_t fsm, hardLink_t li)
-{
-    char * path = fsm->path;
-    char * opath = NULL;
-    const char * nsuffix = fsm->nsuffix;
-    int ec = 0;
-    int rc;
-    int i;
-
-    fsm->path = NULL;
-    fsm->nsuffix = NULL;
-
-    rc = fsmMapPath(fsm, li->filex[li->createdPath]);
-    opath = fsm->path;
-    fsm->path = NULL;
-    for (i = 0; i < li->nlink; i++) {
-       if (li->filex[i] < 0) continue;
-       if (li->createdPath == i) continue;
-
-       fsm->path = _free(fsm->path);
-       rc = fsmMapPath(fsm, li->filex[i]);
-       if (XFA_SKIPPING(fsm->action)) continue;
-
-       rc = fsmVerify(fsm);
-       if (!rc) continue;
-       if (!(rc == CPIOERR_ENOENT)) break;
-
-       /* XXX link(opath, fsm->path) */
-       rc = link(opath, fsm->path);
-       if (_fsm_debug)
-           rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__,
-               opath, fsm->path, (rc < 0 ? strerror(errno) : ""));
-       if (rc < 0)     rc = CPIOERR_LINK_FAILED;
-
-       if (fsm->failedFile && rc != 0 && *fsm->failedFile == NULL) {
-           ec = rc;
-           *fsm->failedFile = xstrdup(fsm->path);
-       }
-
-       li->linksLeft--;
-    }
-    fsm->path = _free(fsm->path);
-    free(opath);
-
-    fsm->nsuffix = nsuffix;
-    fsm->path = path;
-    return ec;
-}
-
-static int fsmCommit(FSM_t fsm, int ix);
-
-/** \ingroup payload
- * Commit hard linked file set atomically.
- * @param fsm          file state machine data
- * @return             0 on success
- */
-static int fsmCommitLinks(FSM_t fsm)
-{
-    char * path = fsm->path;
-    const char * nsuffix = fsm->nsuffix;
-    struct stat * st = &fsm->sb;
-    int rc = 0;
-    nlink_t i;
-    hardLink_t li;
-
-    fsm->path = NULL;
-    fsm->nsuffix = NULL;
-
-    for (li = fsm->links; li != NULL; li = li->next) {
-       if (li->sb.st_ino == st->st_ino && li->sb.st_dev == st->st_dev)
-           break;
-    }
-
-    for (i = 0; i < li->nlink; i++) {
-       if (li->filex[i] < 0) continue;
-       rc = fsmMapPath(fsm, li->filex[i]);
-       if (!XFA_SKIPPING(fsm->action))
-           rc = fsmCommit(fsm, li->filex[i]);
-       fsm->path = _free(fsm->path);
-       li->filex[i] = -1;
-    }
-
-    fsm->nsuffix = nsuffix;
-    fsm->path = path;
-    return rc;
-}
-
 static int fsmRmdir(const char *path)
 {
     int rc = rmdir(path);
@@ -1096,9 +354,9 @@ static int fsmRmdir(const char *path)
               path, (rc < 0 ? strerror(errno) : ""));
     if (rc < 0)
        switch (errno) {
-       case ENOENT:        rc = CPIOERR_ENOENT;    break;
-       case ENOTEMPTY:     rc = CPIOERR_ENOTEMPTY; break;
-       default:            rc = CPIOERR_RMDIR_FAILED; break;
+       case ENOENT:        rc = RPMERR_ENOENT;    break;
+       case ENOTEMPTY:     rc = RPMERR_ENOTEMPTY; break;
+       default:            rc = RPMERR_RMDIR_FAILED; break;
        }
     return rc;
 }
@@ -1110,7 +368,7 @@ static int fsmMkdir(const char *path, mode_t mode)
        rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__,
               path, (unsigned)(mode & 07777),
               (rc < 0 ? strerror(errno) : ""));
-    if (rc < 0)        rc = CPIOERR_MKDIR_FAILED;
+    if (rc < 0)        rc = RPMERR_MKDIR_FAILED;
     return rc;
 }
 
@@ -1125,7 +383,7 @@ static int fsmMkfifo(const char *path, mode_t mode)
     }
 
     if (rc < 0)
-       rc = CPIOERR_MKFIFO_FAILED;
+       rc = RPMERR_MKFIFO_FAILED;
 
     return rc;
 }
@@ -1142,23 +400,24 @@ static int fsmMknod(const char *path, mode_t mode, dev_t dev)
     }
 
     if (rc < 0)
-       rc = CPIOERR_MKNOD_FAILED;
+       rc = RPMERR_MKNOD_FAILED;
 
     return rc;
 }
 
 /**
  * Create (if necessary) directories not explicitly included in package.
- * @param dnli         file state machine data
- * @param sehandle     selinux label handle (bah)
+ * @param files                file data
+ * @param fs           file states
+ * @param plugins      rpm plugins handle
  * @return             0 on success
  */
-static int fsmMkdirs(rpmfi fi, rpmfs fs, struct selabel_handle *sehandle)
+static int fsmMkdirs(rpmfiles files, rpmfs fs, rpmPlugins plugins)
 {
-    DNLI_t dnli = dnlInitIterator(fi, fs, 0);
+    DNLI_t dnli = dnlInitIterator(files, fs, 0);
     struct stat sb;
     const char *dpath;
-    int dc = rpmfiDC(fi);
+    int dc = rpmfilesDC(files);
     int rc = 0;
     int i;
     int ldnlen = 0;
@@ -1210,13 +469,26 @@ static int fsmMkdirs(rpmfi fi, rpmfs fs, struct selabel_handle *sehandle)
            if (rc == 0 && S_ISDIR(sb.st_mode)) {
                /* Move pre-existing path marker forward. */
                dnlx[dc] = (te - dn);
-           } else if (rc == CPIOERR_ENOENT) {
+           } else if (rc == RPMERR_ENOENT) {
                *te = '\0';
                mode_t mode = S_IFDIR | (_dirPerms & 07777);
-               rc = fsmMkdir(dn, mode);
+               rpmFsmOp op = (FA_CREATE|FAF_UNOWNED);
+
+               /* Run fsm file pre hook for all plugins */
+               rc = rpmpluginsCallFsmFilePre(plugins, NULL, dn, mode, op);
+
+               if (!rc)
+                   rc = fsmMkdir(dn, mode);
+
                if (!rc) {
-                   rc = fsmSetSELabel(sehandle, dn, mode);
+                   rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, dn, dn,
+                                                     mode, op);
+               }
 
+               /* Run fsm file post hook for all plugins */
+               rpmpluginsCallFsmFilePost(plugins, NULL, dn, mode, op, rc);
+
+               if (!rc) {
                    rpmlog(RPMLOG_DEBUG,
                            "%s directory created with perms %04o\n",
                            dn, (unsigned)(mode & 07777));
@@ -1260,72 +532,14 @@ static void removeSBITS(const char *path)
     }
 }
 
-/********************************************************************/
-
-static void fsmReset(FSM_t fsm)
-{
-    fsm->path = _free(fsm->path);
-    fsm->postpone = 0;
-    fsm->diskchecked = fsm->exists = 0;
-    fsm->action = FA_UNKNOWN;
-    fsm->osuffix = NULL;
-    fsm->nsuffix = NULL;
-    memset(&(fsm->sb), 0, sizeof(fsm->sb));
-    memset(&(fsm->osb), 0, sizeof(fsm->sb));
-}
-
-static int fsmInit(FSM_t fsm)
+static void fsmDebug(const char *fpath, rpmFileAction action,
+                    const struct stat *st)
 {
-    int rc = 0;
-
-    /* On non-install, mode must be known so that dirs don't get suffix. */
-    if (fsm->goal != FSM_PKGINSTALL) {
-       rpmfi fi = fsmGetFi(fsm);
-       fsm->sb.st_mode = rpmfiFModeIndex(fi, fsm->ix);
-    }
-
-    /* Generate file path. */
-    rc = fsmMapPath(fsm, fsm->ix);
-    if (rc) return rc;
-
-    /* Perform lstat/stat for disk file. */
-    if (fsm->path != NULL &&
-       !(fsm->goal == FSM_PKGINSTALL && S_ISREG(fsm->sb.st_mode)))
-    {
-       int dolstat = !(fsm->mapFlags & CPIO_FOLLOW_SYMLINKS);
-       rc = fsmStat(fsm->path, dolstat, &fsm->osb);
-       if (rc == CPIOERR_ENOENT) {
-           // errno = saveerrno; XXX temporary commented out
-           rc = 0;
-           fsm->exists = 0;
-       } else if (rc == 0) {
-           fsm->exists = 1;
-       }
-    } else {
-       /* Skip %ghost files on build. */
-       fsm->exists = 0;
-    }
-    fsm->diskchecked = 1;
-    if (rc) return rc;
-
-    /* On non-install, the disk file stat is what's remapped. */
-    if (fsm->goal != FSM_PKGINSTALL)
-       fsm->sb = fsm->osb;                     /* structure assignment */
-
-    /* Remap file perms, owner, and group. */
-    rc = fsmMapAttrs(fsm);
-    if (rc) return rc;
-
-    fsm->postpone = XFA_SKIPPING(fsm->action);
-
     rpmlog(RPMLOG_DEBUG, "%-10s %06o%3d (%4d,%4d)%6d %s\n",
-          fileActionString(fsm->action), (int)fsm->sb.st_mode,
-          (int)fsm->sb.st_nlink, (int)fsm->sb.st_uid,
-          (int)fsm->sb.st_gid, (int)fsm->sb.st_size,
-           (fsm->path ? fsm->path : ""));
-
-    return rc;
-
+          fileActionString(action), (int)st->st_mode,
+          (int)st->st_nlink, (int)st->st_uid,
+          (int)st->st_gid, (int)st->st_size,
+           (fpath ? fpath : ""));
 }
 
 static int fsmSymlink(const char *opath, const char *path)
@@ -1338,29 +552,26 @@ static int fsmSymlink(const char *opath, const char *path)
     }
 
     if (rc < 0)
-       rc = CPIOERR_SYMLINK_FAILED;
+       rc = RPMERR_SYMLINK_FAILED;
     return rc;
 }
 
-static int fsmUnlink(const char *path, cpioMapFlags mapFlags)
+static int fsmUnlink(const char *path)
 {
     int rc = 0;
-    if (mapFlags & CPIO_SBIT_CHECK)
-        removeSBITS(path);
+    removeSBITS(path);
     rc = unlink(path);
     if (_fsm_debug)
        rpmlog(RPMLOG_DEBUG, " %8s (%s) %s\n", __func__,
               path, (rc < 0 ? strerror(errno) : ""));
     if (rc < 0)
-       rc = (errno == ENOENT ? CPIOERR_ENOENT : CPIOERR_UNLINK_FAILED);
+       rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_UNLINK_FAILED);
     return rc;
 }
 
-static int fsmRename(const char *opath, const char *path,
-                    cpioMapFlags mapFlags)
+static int fsmRename(const char *opath, const char *path)
 {
-    if (mapFlags & CPIO_SBIT_CHECK)
-        removeSBITS(path);
+    removeSBITS(path);
     int rc = rename(opath, path);
 #if defined(ETXTBSY) && defined(__HPUX__)
     /* XXX HP-UX (and other os'es) don't permit rename to busy files. */
@@ -1375,30 +586,19 @@ static int fsmRename(const char *opath, const char *path,
     if (_fsm_debug)
        rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__,
               opath, path, (rc < 0 ? strerror(errno) : ""));
-    if (rc < 0)        rc = CPIOERR_RENAME_FAILED;
+    if (rc < 0)
+       rc = (errno == EISDIR ? RPMERR_EXIST_AS_DIR : RPMERR_RENAME_FAILED);
     return rc;
 }
 
-
-static int fsmChown(const char *path, uid_t uid, gid_t gid)
+static int fsmRemove(const char *path, mode_t mode)
 {
-    int rc = chown(path, uid, gid);
-    if (rc < 0) {
-       struct stat st;
-       if (lstat(path, &st) == 0 && st.st_uid == uid && st.st_gid == gid)
-           rc = 0;
-    }
-    if (_fsm_debug)
-       rpmlog(RPMLOG_DEBUG, " %8s (%s, %d, %d) %s\n", __func__,
-              path, (int)uid, (int)gid,
-              (rc < 0 ? strerror(errno) : ""));
-    if (rc < 0)        rc = CPIOERR_CHOWN_FAILED;
-    return rc;
+    return S_ISDIR(mode) ? fsmRmdir(path) : fsmUnlink(path);
 }
 
-static int fsmLChown(const char *path, uid_t uid, gid_t gid)
+static int fsmChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
 {
-    int rc = lchown(path, uid, gid);
+    int rc = S_ISLNK(mode) ? lchown(path, uid, gid) : chown(path, uid, gid);
     if (rc < 0) {
        struct stat st;
        if (lstat(path, &st) == 0 && st.st_uid == uid && st.st_gid == gid)
@@ -1408,7 +608,7 @@ static int fsmLChown(const char *path, uid_t uid, gid_t gid)
        rpmlog(RPMLOG_DEBUG, " %8s (%s, %d, %d) %s\n", __func__,
               path, (int)uid, (int)gid,
               (rc < 0 ? strerror(errno) : ""));
-    if (rc < 0)        rc = CPIOERR_CHOWN_FAILED;
+    if (rc < 0)        rc = RPMERR_CHOWN_FAILED;
     return rc;
 }
 
@@ -1424,76 +624,90 @@ static int fsmChmod(const char *path, mode_t mode)
        rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__,
               path, (unsigned)(mode & 07777),
               (rc < 0 ? strerror(errno) : ""));
-    if (rc < 0)        rc = CPIOERR_CHMOD_FAILED;
+    if (rc < 0)        rc = RPMERR_CHMOD_FAILED;
     return rc;
 }
 
-static int fsmUtime(const char *path, time_t mtime)
+static int fsmUtime(const char *path, mode_t mode, time_t mtime)
 {
     int rc = 0;
-    struct utimbuf stamp;
-    stamp.actime = mtime;
-    stamp.modtime = mtime;
-    rc = utime(path, &stamp);
+    struct timeval stamps[2] = {
+       { .tv_sec = mtime, .tv_usec = 0 },
+       { .tv_sec = mtime, .tv_usec = 0 },
+    };
+
+#if HAVE_LUTIMES
+    rc = lutimes(path, stamps);
+#else
+    if (!S_ISLNK(mode))
+       rc = utimes(path, stamps);
+#endif
+    
     if (_fsm_debug)
        rpmlog(RPMLOG_DEBUG, " %8s (%s, 0x%x) %s\n", __func__,
               path, (unsigned)mtime, (rc < 0 ? strerror(errno) : ""));
-    if (rc < 0)        rc = CPIOERR_UTIME_FAILED;
+    if (rc < 0)        rc = RPMERR_UTIME_FAILED;
+    /* ...but utime error is not critical for directories */
+    if (rc && S_ISDIR(mode))
+       rc = 0;
     return rc;
 }
 
-static int fsmVerify(FSM_t fsm)
+static int fsmVerify(const char *path, rpmfi fi, const struct stat *fsb)
 {
     int rc;
-    struct stat * st = &fsm->sb;
-    struct stat * ost = &fsm->osb;
     int saveerrno = errno;
+    struct stat dsb;
+    mode_t mode = rpmfiFMode(fi);
 
-    if (fsm->diskchecked && !fsm->exists) {
-        return CPIOERR_ENOENT;
-    }
-    if (S_ISREG(st->st_mode)) {
+    rc = fsmStat(path, 1, &dsb);
+    if (rc)
+       return rc;
+
+    if (S_ISREG(mode)) {
        /* HP-UX (and other os'es) don't permit unlink on busy files. */
-       char *rmpath = rstrscat(NULL, fsm->path, "-RPMDELETE", NULL);
-       rc = fsmRename(fsm->path, rmpath, fsm->mapFlags);
+       char *rmpath = rstrscat(NULL, path, "-RPMDELETE", NULL);
+       rc = fsmRename(path, rmpath);
        /* XXX shouldn't we take unlink return code here? */
        if (!rc)
-           (void) fsmUnlink(rmpath, fsm->mapFlags);
+           (void) fsmUnlink(rmpath);
        else
-           rc = CPIOERR_UNLINK_FAILED;
+           rc = RPMERR_UNLINK_FAILED;
        free(rmpath);
-        return (rc ? rc : CPIOERR_ENOENT);     /* XXX HACK */
-    } else if (S_ISDIR(st->st_mode)) {
-        if (S_ISDIR(ost->st_mode)) return 0;
-        if (S_ISLNK(ost->st_mode)) {
-            rc = fsmStat(fsm->path, 0, &fsm->osb);
-            if (rc == CPIOERR_ENOENT) rc = 0;
+        return (rc ? rc : RPMERR_ENOENT);      /* XXX HACK */
+    } else if (S_ISDIR(mode)) {
+        if (S_ISDIR(dsb.st_mode)) return 0;
+        if (S_ISLNK(dsb.st_mode)) {
+           uid_t luid = dsb.st_uid;
+            rc = fsmStat(path, 0, &dsb);
+            if (rc == RPMERR_ENOENT) rc = 0;
             if (rc) return rc;
             errno = saveerrno;
-            if (S_ISDIR(ost->st_mode)) return 0;
+           /* Only permit directory symlinks by target owner and root */
+            if (S_ISDIR(dsb.st_mode) && (luid == 0 || luid == fsb->st_uid))
+                   return 0;
         }
-    } else if (S_ISLNK(st->st_mode)) {
-        if (S_ISLNK(ost->st_mode)) {
+    } else if (S_ISLNK(mode)) {
+        if (S_ISLNK(dsb.st_mode)) {
             char buf[8 * BUFSIZ];
             size_t len;
-            rc = fsmReadLink(fsm->path, buf, 8 * BUFSIZ, &len);
+            rc = fsmReadLink(path, buf, 8 * BUFSIZ, &len);
             errno = saveerrno;
             if (rc) return rc;
-           /* FSM_PROCESS puts link target to fsm->buf. */
-            if (rstreq(fsm->buf, buf)) return 0;
+            if (rstreq(rpmfiFLink(fi), buf)) return 0;
         }
-    } else if (S_ISFIFO(st->st_mode)) {
-        if (S_ISFIFO(ost->st_mode)) return 0;
-    } else if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) {
-        if ((S_ISCHR(ost->st_mode) || S_ISBLK(ost->st_mode)) &&
-            (ost->st_rdev == st->st_rdev)) return 0;
-    } else if (S_ISSOCK(st->st_mode)) {
-        if (S_ISSOCK(ost->st_mode)) return 0;
+    } else if (S_ISFIFO(mode)) {
+        if (S_ISFIFO(dsb.st_mode)) return 0;
+    } else if (S_ISCHR(mode) || S_ISBLK(mode)) {
+        if ((S_ISCHR(dsb.st_mode) || S_ISBLK(dsb.st_mode)) &&
+            (dsb.st_rdev == rpmfiFRdev(fi))) return 0;
+    } else if (S_ISSOCK(mode)) {
+        if (S_ISSOCK(dsb.st_mode)) return 0;
     }
     /* XXX shouldn't do this with commit/undo. */
-    rc = fsmUnlink(fsm->path, fsm->mapFlags);
-    if (rc == 0)       rc = CPIOERR_ENOENT;
-    return (rc ? rc : CPIOERR_ENOENT); /* XXX HACK */
+    rc = fsmUnlink(path);
+    if (rc == 0)       rc = RPMERR_ENOENT;
+    return (rc ? rc : RPMERR_ENOENT);  /* XXX HACK */
 }
 
 #define        IS_DEV_LOG(_x)  \
@@ -1505,78 +719,91 @@ static int fsmVerify(FSM_t fsm)
 
 
 /* Rename pre-existing modified or unmanaged file. */
-static int fsmBackup(FSM_t fsm)
+static int fsmBackup(rpmfi fi, rpmFileAction action)
 {
     int rc = 0;
+    const char *suffix = NULL;
 
-    /* FIXME: %ghost can have backup action but no suffix */
-    if ((fsm->action == FA_SAVE || fsm->action == FA_BACKUP) && fsm->osuffix) {
-        char * opath = fsmFsPath(fsm, S_ISDIR(fsm->sb.st_mode), NULL);
-        char * path = fsmFsPath(fsm, 0, fsm->osuffix);
-        rc = fsmRename(opath, path, fsm->mapFlags);
-        if (!rc) {
-            rpmlog(RPMLOG_WARNING, _("%s saved as %s\n"), opath, path);
-            fsm->exists = 0; /* it doesn't exist anymore... */
-        }
-        free(path);
-        free(opath);
+    if (!(rpmfiFFlags(fi) & RPMFILE_GHOST)) {
+       switch (action) {
+       case FA_SAVE:
+           suffix = SUFFIX_RPMSAVE;
+           break;
+       case FA_BACKUP:
+           suffix = SUFFIX_RPMORIG;
+           break;
+       default:
+           break;
+       }
+    }
+
+    if (suffix) {
+       char * opath = fsmFsPath(fi, NULL);
+       char * path = fsmFsPath(fi, suffix);
+       rc = fsmRename(opath, path);
+       if (!rc) {
+           rpmlog(RPMLOG_WARNING, _("%s saved as %s\n"), opath, path);
+       }
+       free(path);
+       free(opath);
     }
     return rc;
 }
 
-static int fsmCommit(FSM_t fsm, int ix)
+static int fsmSetmeta(const char *path, rpmfi fi, rpmPlugins plugins,
+                     rpmFileAction action, const struct stat * st,
+                     int nofcaps)
 {
     int rc = 0;
-    struct stat * st = &fsm->sb;
+    const char *dest = rpmfiFN(fi);
 
-    /* XXX Special case /dev/log, which shouldn't be packaged anyways */
-    if (!S_ISSOCK(st->st_mode) && !IS_DEV_LOG(fsm->path)) {
-       /* Backup on-disk file if needed. Directories are handled earlier */
-       if (!S_ISDIR(st->st_mode))
-           rc = fsmBackup(fsm);
-        /* Rename temporary to final file name. */
-        if (!S_ISDIR(st->st_mode) && (fsm->suffix || fsm->nsuffix)) {
-            char *npath = fsmFsPath(fsm, 0, fsm->nsuffix);
-            rc = fsmRename(fsm->path, npath, fsm->mapFlags);
-            if (!rc && fsm->nsuffix) {
-                char * opath = fsmFsPath(fsm, 0, NULL);
-                rpmlog(RPMLOG_WARNING, _("%s created as %s\n"),
-                       opath, npath);
-                free(opath);
-            }
-            free(fsm->path);
-            fsm->path = npath;
-        }
-        /* Set file security context (if enabled) */
-        if (!rc && !getuid()) {
-            rc = fsmSetSELabel(fsm->sehandle, fsm->path, fsm->sb.st_mode);
-        }
-        if (S_ISLNK(st->st_mode)) {
-            if (!rc && !getuid())
-                rc = fsmLChown(fsm->path, fsm->sb.st_uid, fsm->sb.st_gid);
-        } else {
-            rpmfi fi = fsmGetFi(fsm);
-            if (!rc && !getuid())
-                rc = fsmChown(fsm->path, fsm->sb.st_uid, fsm->sb.st_gid);
-            if (!rc)
-                rc = fsmChmod(fsm->path, fsm->sb.st_mode);
-            if (!rc) {
-                rc = fsmUtime(fsm->path, rpmfiFMtimeIndex(fi, ix));
-                /* utime error is not critical for directories */
-                if (rc && S_ISDIR(st->st_mode))
-                    rc = 0;
-            }
-            /* Set file capabilities (if enabled) */
-            if (!rc && !S_ISDIR(st->st_mode) && !getuid()) {
-                rc = fsmSetFCaps(fsm->path, rpmfiFCapsIndex(fi, ix));
-            }
-        }
+    if (!rc && !getuid()) {
+       rc = fsmChown(path, st->st_mode, st->st_uid, st->st_gid);
+    }
+    if (!rc && !S_ISLNK(st->st_mode)) {
+       rc = fsmChmod(path, st->st_mode);
     }
+    /* Set file capabilities (if enabled) */
+    if (!rc && !nofcaps && S_ISREG(st->st_mode) && !getuid()) {
+       rc = fsmSetFCaps(path, rpmfiFCaps(fi));
+    }
+    if (!rc) {
+       rc = fsmUtime(path, st->st_mode, rpmfiFMtime(fi));
+    }
+    if (!rc) {
+       rc = rpmpluginsCallFsmFilePrepare(plugins, fi,
+                                         path, dest, st->st_mode, action);
+    }
+
+    return rc;
+}
+
+static int fsmCommit(char **path, rpmfi fi, rpmFileAction action, const char *suffix)
+{
+    int rc = 0;
 
-    if (rc && fsm->failedFile && *fsm->failedFile == NULL) {
-        *fsm->failedFile = fsm->path;
-        fsm->path = NULL;
+    /* XXX Special case /dev/log, which shouldn't be packaged anyways */
+    if (!(S_ISSOCK(rpmfiFMode(fi)) && IS_DEV_LOG(*path))) {
+       const char *nsuffix = (action == FA_ALTNAME) ? SUFFIX_RPMNEW : NULL;
+       char *dest = *path;
+       /* Construct final destination path (nsuffix is usually NULL) */
+       if (suffix)
+           dest = fsmFsPath(fi, nsuffix);
+
+       /* Rename temporary to final file name if needed. */
+       if (dest != *path) {
+           rc = fsmRename(*path, dest);
+           if (!rc && nsuffix) {
+               char * opath = fsmFsPath(fi, NULL);
+               rpmlog(RPMLOG_WARNING, _("%s created as %s\n"),
+                      opath, dest);
+               free(opath);
+           }
+           free(*path);
+           *path = dest;
+       }
     }
+
     return rc;
 }
 
@@ -1590,8 +817,6 @@ static const char * fileActionString(rpmFileAction a)
     switch (a) {
     case FA_UNKNOWN:   return "unknown";
     case FA_CREATE:    return "create";
-    case FA_COPYOUT:   return "copyout";
-    case FA_COPYIN:    return "copyin";
     case FA_BACKUP:    return "backup";
     case FA_SAVE:      return "save";
     case FA_SKIP:      return "skip";
@@ -1600,14 +825,15 @@ static const char * fileActionString(rpmFileAction a)
     case FA_SKIPNSTATE: return "skipnstate";
     case FA_SKIPNETSHARED: return "skipnetshared";
     case FA_SKIPCOLOR: return "skipcolor";
+    case FA_TOUCH:     return "touch";
     default:           return "???";
     }
 }
 
 /* Remember any non-regular file state for recording in the rpmdb */
-static void setFileState(rpmfs fs, int i, rpmFileAction action)
+static void setFileState(rpmfs fs, int i)
 {
-    switch (action) {
+    switch (rpmfsGetAction(fs, i)) {
     case FA_SKIPNSTATE:
        rpmfsSetState(fs, i, RPMFILE_STATE_NOTINSTALLED);
        break;
@@ -1617,208 +843,219 @@ static void setFileState(rpmfs fs, int i, rpmFileAction action)
     case FA_SKIPCOLOR:
        rpmfsSetState(fs, i, RPMFILE_STATE_WRONGCOLOR);
        break;
+    case FA_TOUCH:
+       rpmfsSetState(fs, i, RPMFILE_STATE_NORMAL);
+       break;
     default:
        break;
     }
 }
 
-int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfi fi, FD_t cfd,
+int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
               rpmpsm psm, char ** failedFile)
 {
+    FD_t payload = rpmtePayload(te);
+    rpmfi fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
     rpmfs fs = rpmteGetFileStates(te);
-    FSM_t fsm = fsmNew(FSM_PKGINSTALL, fs, fi, failedFile);
-    rpmcpio_t archive = rpmcpioOpen(cfd, O_RDONLY);
-    struct stat * st = &fsm->sb;
+    rpmPlugins plugins = rpmtsPlugins(ts);
+    struct stat sb;
     int saveerrno = errno;
     int rc = 0;
-    int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST);
-
-    if (!rpmteIsSource(te))
-       fsm->mapFlags |= CPIO_SBIT_CHECK;
-
-    if (archive == NULL)
-       rc = CPIOERR_INTERNAL;
+    int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0;
+    int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0;
+    int firsthardlink = -1;
+    FD_t firstlinkfile = NULL;
+    int skip;
+    rpmFileAction action;
+    char *tid = NULL;
+    const char *suffix;
+    char *fpath = NULL;
+
+    if (fi == NULL) {
+       rc = RPMERR_BAD_MAGIC;
+       goto exit;
+    }
 
-    fsm->sehandle = rpmtsSELabelHandle(ts);
     /* transaction id used for temporary path suffix while installing */
-    rasprintf(&fsm->suffix, ";%08x", (unsigned)rpmtsGetTid(ts));
+    rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));
 
     /* Detect and create directories not explicitly in package. */
-    if (!rc) {
-       rc = fsmMkdirs(fi, rpmteGetFileStates(te), fsm->sehandle);
-    }
+    rc = fsmMkdirs(files, fs, plugins);
 
     while (!rc) {
-       hardLink_t li = NULL;
-
-        /* Clean fsm, free'ing memory. */
-       fsmReset(fsm);
-
        /* Read next payload header. */
-        rc = rpmcpioHeaderRead(archive, &(fsm->path), &(fsm->sb));
+       rc = rpmfiNext(fi);
 
-       /* Detect and exit on end-of-payload. */
-       if (rc == CPIOERR_HDR_TRAILER) {
-           rc = 0;
+       if (rc < 0) {
+           if (rc == RPMERR_ITER_END)
+               rc = 0;
            break;
        }
 
-       if (rc) break;
-
-       /* Identify mapping index. */
-       fsm->ix = mapFind(fsm->iter, fsm->path);
-
-       /* Mapping error */
-       if (fsm->ix < 0) {
-           if (fsm->failedFile && *fsm->failedFile == NULL)
-               *fsm->failedFile = xstrdup(fsm->path);
-           rc = CPIOERR_UNMAPPED_FILE;
-           break;
+       action = rpmfsGetAction(fs, rpmfiFX(fi));
+       skip = XFA_SKIPPING(action);
+       suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid;
+       if (action != FA_TOUCH) {
+           fpath = fsmFsPath(fi, suffix);
+       } else {
+           fpath = fsmFsPath(fi, "");
        }
 
-        rc = fsmInit(fsm);
+       /* Remap file perms, owner, and group. */
+       rc = rpmfiStat(fi, 1, &sb);
+
+       fsmDebug(fpath, action, &sb);
 
         /* Exit on error. */
-        if (rc) {
-            fsm->postpone = 1;
+        if (rc)
             break;
-        }
 
-       if (S_ISREG(fsm->sb.st_mode) && fsm->sb.st_nlink > 1)
-           fsm->postpone = saveHardLink(fsm, &li);
-
-       setFileState(rpmteGetFileStates(te), fsm->ix, fsm->action);
-
-        if (!fsm->postpone) {
-            if (S_ISREG(st->st_mode)) {
-                rc = fsmVerify(fsm);
-                if (!(rc == CPIOERR_ENOENT)) return rc;
-                rc = expandRegular(fsm, psm, archive, nodigest);
-            } else if (S_ISDIR(st->st_mode)) {
-               /* Directories replacing something need early backup */
-                rc = fsmBackup(fsm);
-                rc = fsmVerify(fsm);
-                if (rc == CPIOERR_ENOENT) {
-                    mode_t mode = st->st_mode;
+       /* Run fsm file pre hook for all plugins */
+       rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
+                                     sb.st_mode, action);
+       if (rc) {
+           skip = 1;
+       } else {
+           setFileState(fs, rpmfiFX(fi));
+       }
+
+        if (!skip) {
+           int setmeta = 1;
+
+           /* Directories replacing something need early backup */
+           if (!suffix) {
+               rc = fsmBackup(fi, action);
+           }
+           /* Assume file does't exist when tmp suffix is in use */
+           if (!suffix) {
+               rc = fsmVerify(fpath, fi, &sb);
+           } else {
+               rc = (action == FA_TOUCH) ? 0 : RPMERR_ENOENT;
+           }
+
+            if (S_ISREG(sb.st_mode)) {
+               if (rc == RPMERR_ENOENT) {
+                   rc = fsmMkfile(fi, fpath, files, psm, nodigest,
+                                  &setmeta, &firsthardlink, &firstlinkfile);
+               }
+            } else if (S_ISDIR(sb.st_mode)) {
+                if (rc == RPMERR_ENOENT) {
+                    mode_t mode = sb.st_mode;
                     mode &= ~07777;
                     mode |=  00700;
-                    rc = fsmMkdir(fsm->path, mode);
+                    rc = fsmMkdir(fpath, mode);
                 }
-            } else if (S_ISLNK(st->st_mode)) {
-                if ((st->st_size + 1) > fsm->bufsize) {
-                    rc = CPIOERR_HDR_SIZE;
-                } else if (rpmcpioRead(archive, fsm->buf, st->st_size) != st->st_size) {
-                    rc = CPIOERR_READ_FAILED;
-                } else {
-
-                    fsm->buf[st->st_size] = '\0';
-                    /* fsmVerify() assumes link target in fsm->buf */
-                    rc = fsmVerify(fsm);
-                    if (rc == CPIOERR_ENOENT) {
-                        rc = fsmSymlink(fsm->buf, fsm->path);
-                    }
-                }
-            } else if (S_ISFIFO(st->st_mode)) {
+            } else if (S_ISLNK(sb.st_mode)) {
+               if (rc == RPMERR_ENOENT) {
+                   rc = fsmSymlink(rpmfiFLink(fi), fpath);
+               }
+            } else if (S_ISFIFO(sb.st_mode)) {
                 /* This mimics cpio S_ISSOCK() behavior but probably isn't right */
-                rc = fsmVerify(fsm);
-                if (rc == CPIOERR_ENOENT) {
-                    rc = fsmMkfifo(fsm->path, 0000);
+                if (rc == RPMERR_ENOENT) {
+                    rc = fsmMkfifo(fpath, 0000);
                 }
-            } else if (S_ISCHR(st->st_mode) ||
-                       S_ISBLK(st->st_mode) ||
-                       S_ISSOCK(st->st_mode))
+            } else if (S_ISCHR(sb.st_mode) ||
+                       S_ISBLK(sb.st_mode) ||
+                       S_ISSOCK(sb.st_mode))
             {
-                rc = fsmVerify(fsm);
-                if (rc == CPIOERR_ENOENT) {
-                    rc = fsmMknod(fsm->path, fsm->sb.st_mode, fsm->sb.st_rdev);
+                if (rc == RPMERR_ENOENT) {
+                    rc = fsmMknod(fpath, sb.st_mode, sb.st_rdev);
                 }
             } else {
                 /* XXX Special case /dev/log, which shouldn't be packaged anyways */
-                if (!IS_DEV_LOG(fsm->path))
-                    rc = CPIOERR_UNKNOWN_FILETYPE;
-            }
-            if (li != NULL) {
-                li->createdPath = li->linkIndex;
-                rc = fsmMakeLinks(fsm, li);
+                if (!IS_DEV_LOG(fpath))
+                    rc = RPMERR_UNKNOWN_FILETYPE;
             }
-        }
+           /* Set permissions, timestamps etc for non-hardlink entries */
+           if (!rc && setmeta) {
+               rc = fsmSetmeta(fpath, fi, plugins, action, &sb, nofcaps);
+           }
+        } else if (firsthardlink >= 0 && rpmfiArchiveHasContent(fi)) {
+           /* we skip the hard linked file containing the content */
+           /* write the content to the first used instead */
+           char *fn = rpmfilesFN(files, firsthardlink);
+           rc = rpmfiArchiveReadToFilePsm(fi, firstlinkfile, nodigest, psm);
+           wfd_close(&firstlinkfile);
+           firsthardlink = -1;
+           free(fn);
+       }
 
         if (rc) {
-            if (!fsm->postpone) {
+            if (!skip) {
                 /* XXX only erase if temp fn w suffix is in use */
-                if (fsm->suffix) {
-                    if (S_ISDIR(st->st_mode)) {
-                        (void) fsmRmdir(fsm->path);
-                    } else {
-                        (void) fsmUnlink(fsm->path, fsm->mapFlags);
-                    }
+                if (suffix && (action != FA_TOUCH)) {
+                   (void) fsmRemove(fpath, sb.st_mode);
                 }
                 errno = saveerrno;
-                if (fsm->failedFile && *fsm->failedFile == NULL)
-                    *fsm->failedFile = xstrdup(fsm->path);
             }
+        } else {
+           /* Notify on success. */
+           rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi));
 
-            break;
-        }
+           if (!skip) {
+               /* Backup file if needed. Directories are handled earlier */
+               if (suffix)
+                   rc = fsmBackup(fi, action);
 
-        /* Notify on success. */
-        rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmcpioTell(archive));
+               if (!rc)
+                   rc = fsmCommit(&fpath, fi, action, suffix);
+           }
+       }
 
-        if (!fsm->postpone) {
-            rc = ((S_ISREG(st->st_mode) && st->st_nlink > 1)
-                  ? fsmCommitLinks(fsm) : fsmCommit(fsm, fsm->ix));
-        }
-        if (rc) {
-            break;
-        }
+       if (rc)
+           *failedFile = xstrdup(fpath);
+
+       /* Run fsm file post hook for all plugins */
+       rpmpluginsCallFsmFilePost(plugins, fi, fpath,
+                                 sb.st_mode, action, rc);
+       fpath = _free(fpath);
     }
 
-    if (!rc)
-       rc = checkHardLinks(fsm);
+    rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), fdOp(payload, FDSTAT_READ));
+    rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));
+
+exit:
 
     /* No need to bother with close errors on read */
-    rpmcpioFree(archive);
-    fsmFree(fsm);
+    rpmfiArchiveClose(fi);
+    rpmfiFree(fi);
+    Fclose(payload);
+    free(tid);
+    free(fpath);
 
     return rc;
 }
 
 
-int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfi fi,
+int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files,
               rpmpsm psm, char ** failedFile)
 {
+    rpmfi fi = rpmfilesIter(files, RPMFI_ITER_BACK);
     rpmfs fs = rpmteGetFileStates(te);
-    FSM_t fsm = fsmNew(FSM_PKGERASE, fs, fi, failedFile);
+    rpmPlugins plugins = rpmtsPlugins(ts);
+    struct stat sb;
     int rc = 0;
+    char *fpath = NULL;
 
-    if (!rpmteIsSource(te))
-       fsm->mapFlags |= CPIO_SBIT_CHECK;
+    while (!rc && rpmfiNext(fi) >= 0) {
+       rpmFileAction action = rpmfsGetAction(fs, rpmfiFX(fi));
+       fpath = fsmFsPath(fi, NULL);
+       rc = fsmStat(fpath, 1, &sb);
 
-    while (!rc) {
-        /* Clean fsm, free'ing memory. */
-       fsmReset(fsm);
+       fsmDebug(fpath, action, &sb);
 
-       /* Identify mapping index. */
-       fsm->ix = mapNextIterator(fsm->iter);
+       /* Run fsm file pre hook for all plugins */
+       rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
+                                     sb.st_mode, action);
 
-        /* Exit on end-of-payload. */
-        if (fsm->ix < 0)
-            break;
-
-        rc = fsmInit(fsm);
-
-       if (!fsm->postpone)
-           rc = fsmBackup(fsm);
+       if (!XFA_SKIPPING(action))
+           rc = fsmBackup(fi, action);
 
         /* Remove erased files. */
-        if (!fsm->postpone && fsm->action == FA_ERASE) {
-           int missingok = (fsm->fflags & (RPMFILE_MISSINGOK | RPMFILE_GHOST));
+        if (action == FA_ERASE) {
+           int missingok = (rpmfiFFlags(fi) & (RPMFILE_MISSINGOK | RPMFILE_GHOST));
 
-            if (S_ISDIR(fsm->sb.st_mode)) {
-                rc = fsmRmdir(fsm->path);
-            } else {
-                rc = fsmUnlink(fsm->path, fsm->mapFlags);
-           }
+           rc = fsmRemove(fpath, sb.st_mode);
 
            /*
             * Missing %ghost or %missingok entries are not errors.
@@ -1827,7 +1064,7 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfi fi,
             * and complaining about job already done seems like kinderkarten
             * level "But it was MY turn!" whining...
             */
-           if (rc == CPIOERR_ENOENT && missingok) {
+           if (rc == RPMERR_ENOENT && missingok) {
                rc = 0;
            }
 
@@ -1836,115 +1073,41 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfi fi,
             * to track at least some of the expected failures though,
             * such as when we knowingly left config file backups etc behind.
             */
-           if (rc == CPIOERR_ENOTEMPTY) {
+           if (rc == RPMERR_ENOTEMPTY) {
                rc = 0;
            }
 
            if (rc) {
                int lvl = strict_erasures ? RPMLOG_ERR : RPMLOG_WARNING;
                rpmlog(lvl, _("%s %s: remove failed: %s\n"),
-                       S_ISDIR(fsm->sb.st_mode) ? _("directory") : _("file"),
-                       fsm->path, strerror(errno));
+                       S_ISDIR(sb.st_mode) ? _("directory") : _("file"),
+                       fpath, strerror(errno));
             }
         }
+
+       /* Run fsm file post hook for all plugins */
+       rpmpluginsCallFsmFilePost(plugins, fi, fpath,
+                                 sb.st_mode, action, rc);
+
         /* XXX Failure to remove is not (yet) cause for failure. */
         if (!strict_erasures) rc = 0;
 
-        if (rc) break;
+       if (rc)
+           *failedFile = xstrdup(fpath);
 
-        /* Notify on success. */
-        /* On erase we're iterating backwards, fixup for progress */
-        rpm_loff_t amount = (fsm->ix >= 0) ?
-            rpmfiFC(fsmGetFi(fsm)) - fsm->ix : 0;
-        rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, amount);
+       if (rc == 0) {
+           /* Notify on success. */
+           /* On erase we're iterating backwards, fixup for progress */
+           rpm_loff_t amount = rpmfiFC(fi) - rpmfiFX(fi);
+           rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, amount);
+       }
+       fpath = _free(fpath);
     }
 
-    fsmFree(fsm);
+    free(fpath);
+    rpmfiFree(fi);
 
     return rc;
 }
 
 
-int rpmPackageFilesArchive(rpmfi fi, int isSrc, FD_t cfd,
-              rpm_loff_t * archiveSize, char ** failedFile)
-{
-    rpmfs fs = rpmfsNew(rpmfiFC(fi), 0);;
-    FSM_t fsm = fsmNew(FSM_PKGBUILD, fs, fi, failedFile);;
-    rpmcpio_t archive = rpmcpioOpen(cfd, O_WRONLY);
-    int rc = 0;
-
-    fsm->mapFlags |= CPIO_MAP_TYPE;
-    if (isSrc) 
-       fsm->mapFlags |= CPIO_FOLLOW_SYMLINKS;
-
-    if (archive == NULL) {
-       rc = CPIOERR_INTERNAL;
-    } else {
-       int ghost, i, fc = rpmfiFC(fi);
-
-       /* XXX Is this actually still needed? */
-       for (i = 0; i < fc; i++) {
-           ghost = (rpmfiFFlagsIndex(fi, i) & RPMFILE_GHOST);
-           rpmfsSetAction(fs, i, ghost ? FA_SKIP : FA_COPYOUT);
-       }
-    }
-           
-    while (!rc) {
-       fsmReset(fsm);
-
-       /* Identify mapping index. */
-       fsm->ix = mapNextIterator(fsm->iter);
-
-        /* Exit on end-of-payload. */
-        if (fsm->ix < 0)
-            break;
-
-        rc = fsmInit(fsm);
-
-        /* Exit on error. */
-        if (rc) {
-            fsm->postpone = 1;
-            break;
-        }
-
-       if (fsm->fflags & RPMFILE_GHOST) /* XXX Don't if %ghost file. */
-           continue;
-
-       if (S_ISREG(fsm->sb.st_mode) && fsm->sb.st_nlink > 1)
-           fsm->postpone = saveHardLink(fsm, NULL);
-
-        if (fsm->postpone)
-            continue;
-        /* Hardlinks are handled later */
-        if (!(S_ISREG(fsm->sb.st_mode) && fsm->sb.st_nlink > 1)) {
-            /* Copy file into archive. */
-            rc = writeFile(fsm, 1, archive, fsm->ix);
-        }
-
-        if (rc) {
-            if (!fsm->postpone) {
-                if (fsm->failedFile && *fsm->failedFile == NULL)
-                    *fsm->failedFile = xstrdup(fsm->path);
-            }
-
-            break;
-        }
-    }
-
-    /* Flush partial sets of hard linked files. */
-    if (!rc)
-        rc = writeLinks(fsm, archive);
-
-    /* Finish the payload stream */
-    if (!rc)
-       rc = rpmcpioClose(archive);
-
-    if (archiveSize)
-       *archiveSize = (rc == 0) ? rpmcpioTell(archive) : 0;
-
-    rpmcpioFree(archive);
-    rpmfsFree(fs);
-    fsmFree(fsm);
-
-    return rc;
-}