teknoraver / rpms / rpm

Forked from rpms/rpm 4 months ago
Clone

Blame 1534.patch

23dfb9
From 22d18785b2c41f1a8937b712920b4342f7596a7e Mon Sep 17 00:00:00 2001
23dfb9
From: Panu Matilainen <pmatilai@redhat.com>
23dfb9
Date: Mon, 8 Feb 2021 10:45:59 +0200
23dfb9
Subject: [PATCH 1/9] Clean up file unpack iteration logic a bit
23dfb9
23dfb9
Handle rpmfiNext() in the while-condition directly to make it more like
23dfb9
similar other constructs elsewhere, adjust for the end of iteration
23dfb9
code after the loop. Also take the file index from rpmfiNext() so
23dfb9
we don't need multiple calls to rpmfiFX() later.
23dfb9
---
23dfb9
 lib/fsm.c | 19 +++++++------------
23dfb9
 1 file changed, 7 insertions(+), 12 deletions(-)
23dfb9
23dfb9
diff --git a/lib/fsm.c b/lib/fsm.c
23dfb9
index 35dcda081c..7c291adb02 100644
23dfb9
--- a/lib/fsm.c
23dfb9
+++ b/lib/fsm.c
23dfb9
@@ -841,6 +841,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
     struct stat sb;
23dfb9
     int saveerrno = errno;
23dfb9
     int rc = 0;
23dfb9
+    int fx = -1;
23dfb9
     int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0;
23dfb9
     int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0;
23dfb9
     int firsthardlink = -1;
23dfb9
@@ -862,17 +863,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
     /* Detect and create directories not explicitly in package. */
23dfb9
     rc = fsmMkdirs(files, fs, plugins);
23dfb9
 
23dfb9
-    while (!rc) {
23dfb9
-	/* Read next payload header. */
23dfb9
-	rc = rpmfiNext(fi);
23dfb9
-
23dfb9
-	if (rc < 0) {
23dfb9
-	    if (rc == RPMERR_ITER_END)
23dfb9
-		rc = 0;
23dfb9
-	    break;
23dfb9
-	}
23dfb9
-
23dfb9
-	action = rpmfsGetAction(fs, rpmfiFX(fi));
23dfb9
+    while (!rc && (fx = rpmfiNext(fi)) >= 0) {
23dfb9
+	action = rpmfsGetAction(fs, fx);
23dfb9
 	skip = XFA_SKIPPING(action);
23dfb9
 	if (action != FA_TOUCH) {
23dfb9
 	    suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid;
23dfb9
@@ -896,7 +888,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	if (rc) {
23dfb9
 	    skip = 1;
23dfb9
 	} else {
23dfb9
-	    setFileState(fs, rpmfiFX(fi));
23dfb9
+	    setFileState(fs, fx);
23dfb9
 	}
23dfb9
 
23dfb9
         if (!skip) {
23dfb9
@@ -1005,6 +997,9 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	fpath = _free(fpath);
23dfb9
     }
23dfb9
 
23dfb9
+    if (!rc && fx != RPMERR_ITER_END)
23dfb9
+	rc = fx;
23dfb9
+
23dfb9
     rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), fdOp(payload, FDSTAT_READ));
23dfb9
     rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));
23dfb9
 
23dfb9
23dfb9
From c0dc57b820791dd76ce8baafac59b9a58ab0f0d3 Mon Sep 17 00:00:00 2001
23dfb9
From: Panu Matilainen <pmatilai@redhat.com>
23dfb9
Date: Wed, 10 Feb 2021 08:25:28 +0200
23dfb9
Subject: [PATCH 2/9] Drop unused filename variable
23dfb9
23dfb9
---
23dfb9
 lib/fsm.c | 2 --
23dfb9
 1 file changed, 2 deletions(-)
23dfb9
23dfb9
diff --git a/lib/fsm.c b/lib/fsm.c
23dfb9
index 7c291adb02..41b6267ddc 100644
23dfb9
--- a/lib/fsm.c
23dfb9
+++ b/lib/fsm.c
23dfb9
@@ -959,11 +959,9 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	    /* On FA_TOUCH no hardlinks are created thus this is skipped. */
23dfb9
 	    /* we skip the hard linked file containing the content */
23dfb9
 	    /* write the content to the first used instead */
23dfb9
-	    char *fn = rpmfilesFN(files, firsthardlink);
23dfb9
 	    rc = rpmfiArchiveReadToFilePsm(fi, firstlinkfile, nodigest, psm);
23dfb9
 	    wfd_close(&firstlinkfile);
23dfb9
 	    firsthardlink = -1;
23dfb9
-	    free(fn);
23dfb9
 	}
23dfb9
 
23dfb9
         if (rc) {
23dfb9
23dfb9
From dcb5791066afd5caa1003a5d35903a7f5dc79cc2 Mon Sep 17 00:00:00 2001
23dfb9
From: Panu Matilainen <pmatilai@redhat.com>
23dfb9
Date: Wed, 10 Feb 2021 09:57:17 +0200
23dfb9
Subject: [PATCH 3/9] Don't update path info if rename failed on file commit
23dfb9
23dfb9
---
23dfb9
 lib/fsm.c | 16 +++++++++-------
23dfb9
 1 file changed, 9 insertions(+), 7 deletions(-)
23dfb9
23dfb9
diff --git a/lib/fsm.c b/lib/fsm.c
23dfb9
index 41b6267ddc..c581a918a5 100644
23dfb9
--- a/lib/fsm.c
23dfb9
+++ b/lib/fsm.c
23dfb9
@@ -773,14 +773,16 @@ static int fsmCommit(char **path, rpmfi fi, rpmFileAction action, const char *su
23dfb9
 	/* Rename temporary to final file name if needed. */
23dfb9
 	if (dest != *path) {
23dfb9
 	    rc = fsmRename(*path, dest);
23dfb9
-	    if (!rc && nsuffix) {
23dfb9
-		char * opath = fsmFsPath(fi, NULL);
23dfb9
-		rpmlog(RPMLOG_WARNING, _("%s created as %s\n"),
23dfb9
-		       opath, dest);
23dfb9
-		free(opath);
23dfb9
+	    if (!rc) {
23dfb9
+		if (nsuffix) {
23dfb9
+		    char * opath = fsmFsPath(fi, NULL);
23dfb9
+		    rpmlog(RPMLOG_WARNING, _("%s created as %s\n"),
23dfb9
+			   opath, dest);
23dfb9
+		    free(opath);
23dfb9
+		}
23dfb9
+		free(*path);
23dfb9
+		*path = dest;
23dfb9
 	    }
23dfb9
-	    free(*path);
23dfb9
-	    *path = dest;
23dfb9
 	}
23dfb9
     }
23dfb9
 
23dfb9
23dfb9
From 7fdf248e7d29a244b97c46b19be0df64992fdfae Mon Sep 17 00:00:00 2001
23dfb9
From: Panu Matilainen <pmatilai@redhat.com>
23dfb9
Date: Wed, 10 Feb 2021 09:47:19 +0200
23dfb9
Subject: [PATCH 4/9] Refactor file install and remove around a common struct
23dfb9
23dfb9
Collect the common state info into a struct shared by both file install
23dfb9
and remove, update code accordingly. The change looks much more drastic
23dfb9
than it is - it's just adding fp-> prefix to a lot of places.
23dfb9
While we're at it, remember the state data throughout the operation.
23dfb9
23dfb9
No functional changes here, just paving way for the next steps which
23dfb9
will look clearer with these pre-requisites in place.
23dfb9
---
23dfb9
 lib/fsm.c | 158 +++++++++++++++++++++++++++++-------------------------
23dfb9
 1 file changed, 85 insertions(+), 73 deletions(-)
23dfb9
23dfb9
diff --git a/lib/fsm.c b/lib/fsm.c
23dfb9
index c581a918a5..9dba30560f 100644
23dfb9
--- a/lib/fsm.c
23dfb9
+++ b/lib/fsm.c
23dfb9
@@ -38,6 +38,14 @@ static int strict_erasures = 0;
23dfb9
 #define _dirPerms 0755
23dfb9
 #define _filePerms 0644
23dfb9
 
23dfb9
+struct filedata_s {
23dfb9
+    int skip;
23dfb9
+    rpmFileAction action;
23dfb9
+    const char *suffix;
23dfb9
+    char *fpath;
23dfb9
+    struct stat sb;
23dfb9
+};
23dfb9
+
23dfb9
 /* 
23dfb9
  * XXX Forward declarations for previously exported functions to avoid moving 
23dfb9
  * things around needlessly 
23dfb9
@@ -840,19 +848,16 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
     rpmfi fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
23dfb9
     rpmfs fs = rpmteGetFileStates(te);
23dfb9
     rpmPlugins plugins = rpmtsPlugins(ts);
23dfb9
-    struct stat sb;
23dfb9
     int saveerrno = errno;
23dfb9
     int rc = 0;
23dfb9
     int fx = -1;
23dfb9
+    int fc = rpmfilesFC(files);
23dfb9
     int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0;
23dfb9
     int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0;
23dfb9
     int firsthardlink = -1;
23dfb9
     FD_t firstlinkfile = NULL;
23dfb9
-    int skip;
23dfb9
-    rpmFileAction action;
23dfb9
     char *tid = NULL;
23dfb9
-    const char *suffix;
23dfb9
-    char *fpath = NULL;
23dfb9
+    struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata));
23dfb9
 
23dfb9
     if (fi == NULL) {
23dfb9
 	rc = RPMERR_BAD_MAGIC;
23dfb9
@@ -866,96 +871,99 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
     rc = fsmMkdirs(files, fs, plugins);
23dfb9
 
23dfb9
     while (!rc && (fx = rpmfiNext(fi)) >= 0) {
23dfb9
-	action = rpmfsGetAction(fs, fx);
23dfb9
-	skip = XFA_SKIPPING(action);
23dfb9
-	if (action != FA_TOUCH) {
23dfb9
-	    suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid;
23dfb9
+	struct filedata_s *fp = &fdata[fx];
23dfb9
+	fp->action = rpmfsGetAction(fs, fx);
23dfb9
+	fp->skip = XFA_SKIPPING(fp->action);
23dfb9
+	if (fp->action != FA_TOUCH) {
23dfb9
+	    fp->suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid;
23dfb9
 	} else {
23dfb9
-	    suffix = NULL;
23dfb9
+	    fp->suffix = NULL;
23dfb9
 	}
23dfb9
-	fpath = fsmFsPath(fi, suffix);
23dfb9
+	fp->fpath = fsmFsPath(fi, fp->suffix);
23dfb9
 
23dfb9
 	/* Remap file perms, owner, and group. */
23dfb9
-	rc = rpmfiStat(fi, 1, &sb);
23dfb9
+	rc = rpmfiStat(fi, 1, &fp->sb);
23dfb9
 
23dfb9
-	fsmDebug(fpath, action, &sb);
23dfb9
+	fsmDebug(fp->fpath, fp->action, &fp->sb);
23dfb9
 
23dfb9
         /* Exit on error. */
23dfb9
         if (rc)
23dfb9
             break;
23dfb9
 
23dfb9
 	/* Run fsm file pre hook for all plugins */
23dfb9
-	rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
23dfb9
-				      sb.st_mode, action);
23dfb9
+	rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath,
23dfb9
+				      fp->sb.st_mode, fp->action);
23dfb9
 	if (rc) {
23dfb9
-	    skip = 1;
23dfb9
+	    fp->skip = 1;
23dfb9
 	} else {
23dfb9
 	    setFileState(fs, fx);
23dfb9
 	}
23dfb9
 
23dfb9
-        if (!skip) {
23dfb9
+        if (!fp->skip) {
23dfb9
 	    int setmeta = 1;
23dfb9
 
23dfb9
 	    /* Directories replacing something need early backup */
23dfb9
-	    if (!suffix) {
23dfb9
-		rc = fsmBackup(fi, action);
23dfb9
+	    if (!fp->suffix) {
23dfb9
+		rc = fsmBackup(fi, fp->action);
23dfb9
 	    }
23dfb9
 	    /* Assume file does't exist when tmp suffix is in use */
23dfb9
-	    if (!suffix) {
23dfb9
-		rc = fsmVerify(fpath, fi);
23dfb9
+	    if (!fp->suffix) {
23dfb9
+		rc = fsmVerify(fp->fpath, fi);
23dfb9
 	    } else {
23dfb9
 		rc = RPMERR_ENOENT;
23dfb9
 	    }
23dfb9
 
23dfb9
 	    /* See if the file was removed while our attention was elsewhere */
23dfb9
-	    if (rc == RPMERR_ENOENT && action == FA_TOUCH) {
23dfb9
-		rpmlog(RPMLOG_DEBUG, "file %s vanished unexpectedly\n", fpath);
23dfb9
-		action = FA_CREATE;
23dfb9
-		fsmDebug(fpath, action, &sb);
23dfb9
+	    if (rc == RPMERR_ENOENT && fp->action == FA_TOUCH) {
23dfb9
+		rpmlog(RPMLOG_DEBUG, "file %s vanished unexpectedly\n",
23dfb9
+			fp->fpath);
23dfb9
+		fp->action = FA_CREATE;
23dfb9
+		fsmDebug(fp->fpath, fp->action, &fp->sb);
23dfb9
 	    }
23dfb9
 
23dfb9
 	    /* When touching we don't need any of this... */
23dfb9
-	    if (action == FA_TOUCH)
23dfb9
+	    if (fp->action == FA_TOUCH)
23dfb9
 		goto touch;
23dfb9
 
23dfb9
-            if (S_ISREG(sb.st_mode)) {
23dfb9
+            if (S_ISREG(fp->sb.st_mode)) {
23dfb9
 		if (rc == RPMERR_ENOENT) {
23dfb9
-		    rc = fsmMkfile(fi, fpath, files, psm, nodigest,
23dfb9
+		    rc = fsmMkfile(fi, fp->fpath, files, psm, nodigest,
23dfb9
 				   &setmeta, &firsthardlink, &firstlinkfile);
23dfb9
 		}
23dfb9
-            } else if (S_ISDIR(sb.st_mode)) {
23dfb9
+            } else if (S_ISDIR(fp->sb.st_mode)) {
23dfb9
                 if (rc == RPMERR_ENOENT) {
23dfb9
-                    mode_t mode = sb.st_mode;
23dfb9
+                    mode_t mode = fp->sb.st_mode;
23dfb9
                     mode &= ~07777;
23dfb9
                     mode |=  00700;
23dfb9
-                    rc = fsmMkdir(fpath, mode);
23dfb9
+                    rc = fsmMkdir(fp->fpath, mode);
23dfb9
                 }
23dfb9
-            } else if (S_ISLNK(sb.st_mode)) {
23dfb9
+            } else if (S_ISLNK(fp->sb.st_mode)) {
23dfb9
 		if (rc == RPMERR_ENOENT) {
23dfb9
-		    rc = fsmSymlink(rpmfiFLink(fi), fpath);
23dfb9
+		    rc = fsmSymlink(rpmfiFLink(fi), fp->fpath);
23dfb9
 		}
23dfb9
-            } else if (S_ISFIFO(sb.st_mode)) {
23dfb9
+            } else if (S_ISFIFO(fp->sb.st_mode)) {
23dfb9
                 /* This mimics cpio S_ISSOCK() behavior but probably isn't right */
23dfb9
                 if (rc == RPMERR_ENOENT) {
23dfb9
-                    rc = fsmMkfifo(fpath, 0000);
23dfb9
+                    rc = fsmMkfifo(fp->fpath, 0000);
23dfb9
                 }
23dfb9
-            } else if (S_ISCHR(sb.st_mode) ||
23dfb9
-                       S_ISBLK(sb.st_mode) ||
23dfb9
-                       S_ISSOCK(sb.st_mode))
23dfb9
+            } else if (S_ISCHR(fp->sb.st_mode) ||
23dfb9
+                       S_ISBLK(fp->sb.st_mode) ||
23dfb9
+                       S_ISSOCK(fp->sb.st_mode))
23dfb9
             {
23dfb9
                 if (rc == RPMERR_ENOENT) {
23dfb9
-                    rc = fsmMknod(fpath, sb.st_mode, sb.st_rdev);
23dfb9
+                    rc = fsmMknod(fp->fpath, fp->sb.st_mode, fp->sb.st_rdev);
23dfb9
                 }
23dfb9
             } else {
23dfb9
                 /* XXX Special case /dev/log, which shouldn't be packaged anyways */
23dfb9
-                if (!IS_DEV_LOG(fpath))
23dfb9
+                if (!IS_DEV_LOG(fp->fpath))
23dfb9
                     rc = RPMERR_UNKNOWN_FILETYPE;
23dfb9
             }
23dfb9
 
23dfb9
 touch:
23dfb9
 	    /* Set permissions, timestamps etc for non-hardlink entries */
23dfb9
 	    if (!rc && setmeta) {
23dfb9
-		rc = fsmSetmeta(fpath, fi, plugins, action, &sb, nofcaps);
23dfb9
+		rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action,
23dfb9
+				&fp->sb, nofcaps);
23dfb9
 	    }
23dfb9
         } else if (firsthardlink >= 0 && rpmfiArchiveHasContent(fi)) {
23dfb9
 	    /* On FA_TOUCH no hardlinks are created thus this is skipped. */
23dfb9
@@ -967,10 +975,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	}
23dfb9
 
23dfb9
         if (rc) {
23dfb9
-            if (!skip) {
23dfb9
+            if (!fp->skip) {
23dfb9
                 /* XXX only erase if temp fn w suffix is in use */
23dfb9
-                if (suffix) {
23dfb9
-		    (void) fsmRemove(fpath, sb.st_mode);
23dfb9
+                if (fp->suffix) {
23dfb9
+		    (void) fsmRemove(fp->fpath, fp->sb.st_mode);
23dfb9
                 }
23dfb9
                 errno = saveerrno;
23dfb9
             }
23dfb9
@@ -978,23 +986,22 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	    /* Notify on success. */
23dfb9
 	    rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi));
23dfb9
 
23dfb9
-	    if (!skip) {
23dfb9
+	    if (!fp->skip) {
23dfb9
 		/* Backup file if needed. Directories are handled earlier */
23dfb9
-		if (suffix)
23dfb9
-		    rc = fsmBackup(fi, action);
23dfb9
+		if (fp->suffix)
23dfb9
+		    rc = fsmBackup(fi, fp->action);
23dfb9
 
23dfb9
 		if (!rc)
23dfb9
-		    rc = fsmCommit(&fpath, fi, action, suffix);
23dfb9
+		    rc = fsmCommit(&fp->fpath, fi, fp->action, fp->suffix);
23dfb9
 	    }
23dfb9
 	}
23dfb9
 
23dfb9
 	if (rc)
23dfb9
-	    *failedFile = xstrdup(fpath);
23dfb9
+	    *failedFile = xstrdup(fp->fpath);
23dfb9
 
23dfb9
 	/* Run fsm file post hook for all plugins */
23dfb9
-	rpmpluginsCallFsmFilePost(plugins, fi, fpath,
23dfb9
-				  sb.st_mode, action, rc);
23dfb9
-	fpath = _free(fpath);
23dfb9
+	rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath,
23dfb9
+				  fp->sb.st_mode, fp->action, rc);
23dfb9
     }
23dfb9
 
23dfb9
     if (!rc && fx != RPMERR_ITER_END)
23dfb9
@@ -1010,7 +1017,9 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
     rpmfiFree(fi);
23dfb9
     Fclose(payload);
23dfb9
     free(tid);
23dfb9
-    free(fpath);
23dfb9
+    for (int i = 0; i < fc; i++)
23dfb9
+	free(fdata[i].fpath);
23dfb9
+    free(fdata);
23dfb9
 
23dfb9
     return rc;
23dfb9
 }
23dfb9
@@ -1022,29 +1031,31 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files,
23dfb9
     rpmfi fi = rpmfilesIter(files, RPMFI_ITER_BACK);
23dfb9
     rpmfs fs = rpmteGetFileStates(te);
23dfb9
     rpmPlugins plugins = rpmtsPlugins(ts);
23dfb9
-    struct stat sb;
23dfb9
+    int fc = rpmfilesFC(files);
23dfb9
+    int fx = -1;
23dfb9
+    struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata));
23dfb9
     int rc = 0;
23dfb9
-    char *fpath = NULL;
23dfb9
 
23dfb9
-    while (!rc && rpmfiNext(fi) >= 0) {
23dfb9
-	rpmFileAction action = rpmfsGetAction(fs, rpmfiFX(fi));
23dfb9
-	fpath = fsmFsPath(fi, NULL);
23dfb9
-	rc = fsmStat(fpath, 1, &sb);
23dfb9
+    while (!rc && (fx = rpmfiNext(fi)) >= 0) {
23dfb9
+	struct filedata_s *fp = &fdata[fx];
23dfb9
+	fp->action = rpmfsGetAction(fs, rpmfiFX(fi));
23dfb9
+	fp->fpath = fsmFsPath(fi, NULL);
23dfb9
+	rc = fsmStat(fp->fpath, 1, &fp->sb);
23dfb9
 
23dfb9
-	fsmDebug(fpath, action, &sb);
23dfb9
+	fsmDebug(fp->fpath, fp->action, &fp->sb);
23dfb9
 
23dfb9
 	/* Run fsm file pre hook for all plugins */
23dfb9
-	rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
23dfb9
-				      sb.st_mode, action);
23dfb9
+	rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath,
23dfb9
+				      fp->sb.st_mode, fp->action);
23dfb9
 
23dfb9
-	if (!XFA_SKIPPING(action))
23dfb9
-	    rc = fsmBackup(fi, action);
23dfb9
+	if (!XFA_SKIPPING(fp->action))
23dfb9
+	    rc = fsmBackup(fi, fp->action);
23dfb9
 
23dfb9
         /* Remove erased files. */
23dfb9
-        if (action == FA_ERASE) {
23dfb9
+        if (fp->action == FA_ERASE) {
23dfb9
 	    int missingok = (rpmfiFFlags(fi) & (RPMFILE_MISSINGOK | RPMFILE_GHOST));
23dfb9
 
23dfb9
-	    rc = fsmRemove(fpath, sb.st_mode);
23dfb9
+	    rc = fsmRemove(fp->fpath, fp->sb.st_mode);
23dfb9
 
23dfb9
 	    /*
23dfb9
 	     * Missing %ghost or %missingok entries are not errors.
23dfb9
@@ -1069,20 +1080,20 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	    if (rc) {
23dfb9
 		int lvl = strict_erasures ? RPMLOG_ERR : RPMLOG_WARNING;
23dfb9
 		rpmlog(lvl, _("%s %s: remove failed: %s\n"),
23dfb9
-			S_ISDIR(sb.st_mode) ? _("directory") : _("file"),
23dfb9
-			fpath, strerror(errno));
23dfb9
+			S_ISDIR(fp->sb.st_mode) ? _("directory") : _("file"),
23dfb9
+			fp->fpath, strerror(errno));
23dfb9
             }
23dfb9
         }
23dfb9
 
23dfb9
 	/* Run fsm file post hook for all plugins */
23dfb9
-	rpmpluginsCallFsmFilePost(plugins, fi, fpath,
23dfb9
-				  sb.st_mode, action, rc);
23dfb9
+	rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath,
23dfb9
+				  fp->sb.st_mode, fp->action, rc);
23dfb9
 
23dfb9
         /* XXX Failure to remove is not (yet) cause for failure. */
23dfb9
         if (!strict_erasures) rc = 0;
23dfb9
 
23dfb9
 	if (rc)
23dfb9
-	    *failedFile = xstrdup(fpath);
23dfb9
+	    *failedFile = xstrdup(fp->fpath);
23dfb9
 
23dfb9
 	if (rc == 0) {
23dfb9
 	    /* Notify on success. */
23dfb9
@@ -1090,10 +1101,11 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	    rpm_loff_t amount = rpmfiFC(fi) - rpmfiFX(fi);
23dfb9
 	    rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, amount);
23dfb9
 	}
23dfb9
-	fpath = _free(fpath);
23dfb9
     }
23dfb9
 
23dfb9
-    free(fpath);
23dfb9
+    for (int i = 0; i < fc; i++)
23dfb9
+	free(fdata[i].fpath);
23dfb9
+    free(fdata);
23dfb9
     rpmfiFree(fi);
23dfb9
 
23dfb9
     return rc;
23dfb9
23dfb9
From 202c9e9cd2e199b7e7c9655704a98ab2d4fad69c Mon Sep 17 00:00:00 2001
23dfb9
From: Panu Matilainen <pmatilai@redhat.com>
23dfb9
Date: Wed, 10 Feb 2021 10:08:27 +0200
23dfb9
Subject: [PATCH 5/9] Refactor fsmMkfile() to take advantage of the new state
23dfb9
 struct
23dfb9
23dfb9
Move setmeta into the struct too (we'll want this later anyhow),
23dfb9
and now we only need to pass the struct to fsmMkfile(). One less
23dfb9
argument to pass around, it has way too many still.
23dfb9
23dfb9
No functional changes.
23dfb9
---
23dfb9
 lib/fsm.c | 22 +++++++++++-----------
23dfb9
 1 file changed, 11 insertions(+), 11 deletions(-)
23dfb9
23dfb9
diff --git a/lib/fsm.c b/lib/fsm.c
23dfb9
index 9dba30560f..80ca234b1e 100644
23dfb9
--- a/lib/fsm.c
23dfb9
+++ b/lib/fsm.c
23dfb9
@@ -39,6 +39,7 @@ static int strict_erasures = 0;
23dfb9
 #define _filePerms 0644
23dfb9
 
23dfb9
 struct filedata_s {
23dfb9
+    int setmeta;
23dfb9
     int skip;
23dfb9
     rpmFileAction action;
23dfb9
     const char *suffix;
23dfb9
@@ -279,8 +280,8 @@ static int expandRegular(rpmfi fi, const char *dest, rpmpsm psm, int nodigest)
23dfb9
     return rc;
23dfb9
 }
23dfb9
 
23dfb9
-static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files,
23dfb9
-		     rpmpsm psm, int nodigest, int *setmeta,
23dfb9
+static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files,
23dfb9
+		     rpmpsm psm, int nodigest,
23dfb9
 		     int * firsthardlink, FD_t *firstlinkfile)
23dfb9
 {
23dfb9
     int rc = 0;
23dfb9
@@ -290,11 +291,11 @@ static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files,
23dfb9
 	/* Create first hardlinked file empty */
23dfb9
 	if (*firsthardlink < 0) {
23dfb9
 	    *firsthardlink = rpmfiFX(fi);
23dfb9
-	    rc = wfd_open(firstlinkfile, dest);
23dfb9
+	    rc = wfd_open(firstlinkfile, fp->fpath);
23dfb9
 	} else {
23dfb9
 	    /* Create hard links for others */
23dfb9
 	    char *fn = rpmfilesFN(files, *firsthardlink);
23dfb9
-	    rc = link(fn, dest);
23dfb9
+	    rc = link(fn, fp->fpath);
23dfb9
 	    if (rc < 0) {
23dfb9
 		rc = RPMERR_LINK_FAILED;
23dfb9
 	    }
23dfb9
@@ -305,14 +306,14 @@ static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files,
23dfb9
        existing) file with content */
23dfb9
     if (numHardlinks<=1) {
23dfb9
 	if (!rc)
23dfb9
-	    rc = expandRegular(fi, dest, psm, nodigest);
23dfb9
+	    rc = expandRegular(fi, fp->fpath, psm, nodigest);
23dfb9
     } else if (rpmfiArchiveHasContent(fi)) {
23dfb9
 	if (!rc)
23dfb9
 	    rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm);
23dfb9
 	wfd_close(firstlinkfile);
23dfb9
 	*firsthardlink = -1;
23dfb9
     } else {
23dfb9
-	*setmeta = 0;
23dfb9
+	fp->setmeta = 0;
23dfb9
     }
23dfb9
 
23dfb9
     return rc;
23dfb9
@@ -874,6 +875,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	struct filedata_s *fp = &fdata[fx];
23dfb9
 	fp->action = rpmfsGetAction(fs, fx);
23dfb9
 	fp->skip = XFA_SKIPPING(fp->action);
23dfb9
+	fp->setmeta = 1;
23dfb9
 	if (fp->action != FA_TOUCH) {
23dfb9
 	    fp->suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid;
23dfb9
 	} else {
23dfb9
@@ -900,8 +902,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	}
23dfb9
 
23dfb9
         if (!fp->skip) {
23dfb9
-	    int setmeta = 1;
23dfb9
-
23dfb9
 	    /* Directories replacing something need early backup */
23dfb9
 	    if (!fp->suffix) {
23dfb9
 		rc = fsmBackup(fi, fp->action);
23dfb9
@@ -927,8 +927,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 
23dfb9
             if (S_ISREG(fp->sb.st_mode)) {
23dfb9
 		if (rc == RPMERR_ENOENT) {
23dfb9
-		    rc = fsmMkfile(fi, fp->fpath, files, psm, nodigest,
23dfb9
-				   &setmeta, &firsthardlink, &firstlinkfile);
23dfb9
+		    rc = fsmMkfile(fi, fp, files, psm, nodigest,
23dfb9
+				   &firsthardlink, &firstlinkfile);
23dfb9
 		}
23dfb9
             } else if (S_ISDIR(fp->sb.st_mode)) {
23dfb9
                 if (rc == RPMERR_ENOENT) {
23dfb9
@@ -961,7 +961,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 
23dfb9
 touch:
23dfb9
 	    /* Set permissions, timestamps etc for non-hardlink entries */
23dfb9
-	    if (!rc && setmeta) {
23dfb9
+	    if (!rc && fp->setmeta) {
23dfb9
 		rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action,
23dfb9
 				&fp->sb, nofcaps);
23dfb9
 	    }
23dfb9
23dfb9
From f014fc46325efe92b79841145f8dc0cb40896c64 Mon Sep 17 00:00:00 2001
23dfb9
From: Panu Matilainen <pmatilai@redhat.com>
23dfb9
Date: Wed, 10 Feb 2021 10:24:22 +0200
23dfb9
Subject: [PATCH 6/9] Clarify file installation temporary suffix rule
23dfb9
23dfb9
We only use a temporary suffix for regular files that we are actually
23dfb9
creating, skipped and touched files should not have it. Add XFA_CREATING()
23dfb9
macro to accomppany XFA_SKIPPING() to easily check whether the file
23dfb9
is being created or something else.
23dfb9
23dfb9
No functional changes but makes the logic clearer.
23dfb9
---
23dfb9
 lib/fsm.c      | 5 +----
23dfb9
 lib/rpmfiles.h | 3 +++
23dfb9
 2 files changed, 4 insertions(+), 4 deletions(-)
23dfb9
23dfb9
diff --git a/lib/fsm.c b/lib/fsm.c
23dfb9
index 80ca234b1e..554ea712f5 100644
23dfb9
--- a/lib/fsm.c
23dfb9
+++ b/lib/fsm.c
23dfb9
@@ -876,11 +876,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	fp->action = rpmfsGetAction(fs, fx);
23dfb9
 	fp->skip = XFA_SKIPPING(fp->action);
23dfb9
 	fp->setmeta = 1;
23dfb9
-	if (fp->action != FA_TOUCH) {
23dfb9
+	if (XFA_CREATING(fp->action))
23dfb9
 	    fp->suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid;
23dfb9
-	} else {
23dfb9
-	    fp->suffix = NULL;
23dfb9
-	}
23dfb9
 	fp->fpath = fsmFsPath(fi, fp->suffix);
23dfb9
 
23dfb9
 	/* Remap file perms, owner, and group. */
23dfb9
diff --git a/lib/rpmfiles.h b/lib/rpmfiles.h
23dfb9
index 7ce1712323..e0adbd8aff 100644
23dfb9
--- a/lib/rpmfiles.h
23dfb9
+++ b/lib/rpmfiles.h
23dfb9
@@ -121,6 +121,9 @@ typedef enum rpmFileAction_e {
23dfb9
 #define XFA_SKIPPING(_a)	\
23dfb9
     ((_a) == FA_SKIP || (_a) == FA_SKIPNSTATE || (_a) == FA_SKIPNETSHARED || (_a) == FA_SKIPCOLOR)
23dfb9
 
23dfb9
+#define XFA_CREATING(_a)	\
23dfb9
+    ((_a) == FA_CREATE || (_a) == FA_BACKUP || (_a) == FA_SAVE || (_a) == FA_ALTNAME)
23dfb9
+
23dfb9
 /**
23dfb9
  * We pass these around as an array with a sentinel.
23dfb9
  */
23dfb9
23dfb9
From fc54439f8a10901cd72028cdbdb924e3e7501624 Mon Sep 17 00:00:00 2001
23dfb9
From: Panu Matilainen <pmatilai@redhat.com>
23dfb9
Date: Wed, 10 Feb 2021 10:24:22 +0200
23dfb9
Subject: [PATCH 7/9] Clarify file installation temporary suffix rule
23dfb9
23dfb9
We only use a temporary suffix for regular files that we are actually
23dfb9
creating, skipped and touched files should not have it. Add XFA_CREATING()
23dfb9
macro to accomppany XFA_SKIPPING() to easily check whether the file
23dfb9
is being created or something else.
23dfb9
23dfb9
No functional changes but makes the logic clearer.
23dfb9
---
23dfb9
 lib/fsm.c | 4 ++--
23dfb9
 1 file changed, 2 insertions(+), 2 deletions(-)
23dfb9
23dfb9
diff --git a/lib/fsm.c b/lib/fsm.c
23dfb9
index 554ea712f5..094f5e2bb6 100644
23dfb9
--- a/lib/fsm.c
23dfb9
+++ b/lib/fsm.c
23dfb9
@@ -876,8 +876,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	fp->action = rpmfsGetAction(fs, fx);
23dfb9
 	fp->skip = XFA_SKIPPING(fp->action);
23dfb9
 	fp->setmeta = 1;
23dfb9
-	if (XFA_CREATING(fp->action))
23dfb9
-	    fp->suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid;
23dfb9
+	if (XFA_CREATING(fp->action) && !S_ISDIR(rpmfiFMode(fi)))
23dfb9
+	    fp->suffix = tid;
23dfb9
 	fp->fpath = fsmFsPath(fi, fp->suffix);
23dfb9
 
23dfb9
 	/* Remap file perms, owner, and group. */
23dfb9
23dfb9
From 9294ffa898bc7f1f77ff80c0a3f4f0f70c4dfef2 Mon Sep 17 00:00:00 2001
23dfb9
From: Panu Matilainen <pmatilai@redhat.com>
23dfb9
Date: Wed, 10 Feb 2021 11:25:10 +0200
23dfb9
Subject: [PATCH 8/9] Handle hardlink tracking with a file state pointer
23dfb9
23dfb9
No functional changes, just makes it a little cleaner as firstlink now
23dfb9
points to the actual file data instead of a index number somewhere.
23dfb9
---
23dfb9
 lib/fsm.c | 20 +++++++++-----------
23dfb9
 1 file changed, 9 insertions(+), 11 deletions(-)
23dfb9
23dfb9
diff --git a/lib/fsm.c b/lib/fsm.c
23dfb9
index 094f5e2bb6..f86383a986 100644
23dfb9
--- a/lib/fsm.c
23dfb9
+++ b/lib/fsm.c
23dfb9
@@ -282,24 +282,22 @@ static int expandRegular(rpmfi fi, const char *dest, rpmpsm psm, int nodigest)
23dfb9
 
23dfb9
 static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files,
23dfb9
 		     rpmpsm psm, int nodigest,
23dfb9
-		     int * firsthardlink, FD_t *firstlinkfile)
23dfb9
+		     struct filedata_s ** firstlink, FD_t *firstlinkfile)
23dfb9
 {
23dfb9
     int rc = 0;
23dfb9
     int numHardlinks = rpmfiFNlink(fi);
23dfb9
 
23dfb9
     if (numHardlinks > 1) {
23dfb9
 	/* Create first hardlinked file empty */
23dfb9
-	if (*firsthardlink < 0) {
23dfb9
-	    *firsthardlink = rpmfiFX(fi);
23dfb9
+	if (*firstlink == NULL) {
23dfb9
+	    *firstlink = fp;
23dfb9
 	    rc = wfd_open(firstlinkfile, fp->fpath);
23dfb9
 	} else {
23dfb9
 	    /* Create hard links for others */
23dfb9
-	    char *fn = rpmfilesFN(files, *firsthardlink);
23dfb9
-	    rc = link(fn, fp->fpath);
23dfb9
+	    rc = link((*firstlink)->fpath, fp->fpath);
23dfb9
 	    if (rc < 0) {
23dfb9
 		rc = RPMERR_LINK_FAILED;
23dfb9
 	    }
23dfb9
-	    free(fn);
23dfb9
 	}
23dfb9
     }
23dfb9
     /* Write normal files or fill the last hardlinked (already
23dfb9
@@ -311,7 +309,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files,
23dfb9
 	if (!rc)
23dfb9
 	    rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm);
23dfb9
 	wfd_close(firstlinkfile);
23dfb9
-	*firsthardlink = -1;
23dfb9
+	*firstlink = NULL;
23dfb9
     } else {
23dfb9
 	fp->setmeta = 0;
23dfb9
     }
23dfb9
@@ -855,10 +853,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
     int fc = rpmfilesFC(files);
23dfb9
     int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0;
23dfb9
     int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0;
23dfb9
-    int firsthardlink = -1;
23dfb9
     FD_t firstlinkfile = NULL;
23dfb9
     char *tid = NULL;
23dfb9
     struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata));
23dfb9
+    struct filedata_s *firstlink = NULL;
23dfb9
 
23dfb9
     if (fi == NULL) {
23dfb9
 	rc = RPMERR_BAD_MAGIC;
23dfb9
@@ -925,7 +923,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
             if (S_ISREG(fp->sb.st_mode)) {
23dfb9
 		if (rc == RPMERR_ENOENT) {
23dfb9
 		    rc = fsmMkfile(fi, fp, files, psm, nodigest,
23dfb9
-				   &firsthardlink, &firstlinkfile);
23dfb9
+				   &firstlink, &firstlinkfile);
23dfb9
 		}
23dfb9
             } else if (S_ISDIR(fp->sb.st_mode)) {
23dfb9
                 if (rc == RPMERR_ENOENT) {
23dfb9
@@ -962,13 +960,13 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 		rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action,
23dfb9
 				&fp->sb, nofcaps);
23dfb9
 	    }
23dfb9
-        } else if (firsthardlink >= 0 && rpmfiArchiveHasContent(fi)) {
23dfb9
+        } else if (firstlink && rpmfiArchiveHasContent(fi)) {
23dfb9
 	    /* On FA_TOUCH no hardlinks are created thus this is skipped. */
23dfb9
 	    /* we skip the hard linked file containing the content */
23dfb9
 	    /* write the content to the first used instead */
23dfb9
 	    rc = rpmfiArchiveReadToFilePsm(fi, firstlinkfile, nodigest, psm);
23dfb9
 	    wfd_close(&firstlinkfile);
23dfb9
-	    firsthardlink = -1;
23dfb9
+	    firstlink = NULL;
23dfb9
 	}
23dfb9
 
23dfb9
         if (rc) {
23dfb9
23dfb9
From d6a9a00396a89d14858c6b6e2548eca2065c1d64 Mon Sep 17 00:00:00 2001
23dfb9
From: Panu Matilainen <pmatilai@redhat.com>
23dfb9
Date: Wed, 10 Feb 2021 14:15:33 +0200
23dfb9
Subject: [PATCH 9/9] Handle file install failures more gracefully
23dfb9
23dfb9
Run the file installation in multiple stages:
23dfb9
1) gather intel
23dfb9
2) unpack the archive to temporary files
23dfb9
3) set file metadatas
23dfb9
4) commit files to final location
23dfb9
5) mop up leftovers on failure
23dfb9
23dfb9
This means we no longer leave behind a trail of untracked, potentially
23dfb9
harmful junk on installation failure.
23dfb9
23dfb9
If commit step fails the package can still be left in an inconsistent stage,
23dfb9
this could be further improved by first backing up old files to temporary
23dfb9
location to allow undo on failure, but leaving that for some other day.
23dfb9
Also unowned directories will still be left behind.
23dfb9
23dfb9
And yes, this is a somewhat scary change as it's the biggest ever change
23dfb9
to how rpm lays down files on install. Adopt the hardlink test spec
23dfb9
over to install tests and add some more tests for the new behavior.
23dfb9
23dfb9
Fixes: #967 (+ multiple reports over the years)
23dfb9
---
23dfb9
 lib/fsm.c                       | 147 ++++++++++++++++++++------------
23dfb9
 tests/data/SPECS/hlinktest.spec |   4 +
23dfb9
 tests/rpmbuild.at               |  32 -------
23dfb9
 tests/rpmi.at                   |  92 ++++++++++++++++++++
23dfb9
 4 files changed, 189 insertions(+), 86 deletions(-)
23dfb9
23dfb9
diff --git a/lib/fsm.c b/lib/fsm.c
23dfb9
index f86383a986..6efd25bddd 100644
23dfb9
--- a/lib/fsm.c
23dfb9
+++ b/lib/fsm.c
23dfb9
@@ -38,7 +38,17 @@ static int strict_erasures = 0;
23dfb9
 #define _dirPerms 0755
23dfb9
 #define _filePerms 0644
23dfb9
 
23dfb9
+enum filestage_e {
23dfb9
+    FILE_COMMIT = -1,
23dfb9
+    FILE_NONE   = 0,
23dfb9
+    FILE_PRE    = 1,
23dfb9
+    FILE_UNPACK = 2,
23dfb9
+    FILE_PREP   = 3,
23dfb9
+    FILE_POST   = 4,
23dfb9
+};
23dfb9
+
23dfb9
 struct filedata_s {
23dfb9
+    int stage;
23dfb9
     int setmeta;
23dfb9
     int skip;
23dfb9
     rpmFileAction action;
23dfb9
@@ -844,10 +854,9 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
               rpmpsm psm, char ** failedFile)
23dfb9
 {
23dfb9
     FD_t payload = rpmtePayload(te);
23dfb9
-    rpmfi fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
23dfb9
+    rpmfi fi = NULL;
23dfb9
     rpmfs fs = rpmteGetFileStates(te);
23dfb9
     rpmPlugins plugins = rpmtsPlugins(ts);
23dfb9
-    int saveerrno = errno;
23dfb9
     int rc = 0;
23dfb9
     int fx = -1;
23dfb9
     int fc = rpmfilesFC(files);
23dfb9
@@ -858,20 +867,17 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
     struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata));
23dfb9
     struct filedata_s *firstlink = NULL;
23dfb9
 
23dfb9
-    if (fi == NULL) {
23dfb9
-	rc = RPMERR_BAD_MAGIC;
23dfb9
-	goto exit;
23dfb9
-    }
23dfb9
-
23dfb9
     /* transaction id used for temporary path suffix while installing */
23dfb9
     rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));
23dfb9
 
23dfb9
-    /* Detect and create directories not explicitly in package. */
23dfb9
-    rc = fsmMkdirs(files, fs, plugins);
23dfb9
-
23dfb9
+    /* Collect state data for the whole operation */
23dfb9
+    fi = rpmfilesIter(files, RPMFI_ITER_FWD);
23dfb9
     while (!rc && (fx = rpmfiNext(fi)) >= 0) {
23dfb9
 	struct filedata_s *fp = &fdata[fx];
23dfb9
-	fp->action = rpmfsGetAction(fs, fx);
23dfb9
+	if (rpmfiFFlags(fi) & RPMFILE_GHOST)
23dfb9
+            fp->action = FA_SKIP;
23dfb9
+	else
23dfb9
+	    fp->action = rpmfsGetAction(fs, fx);
23dfb9
 	fp->skip = XFA_SKIPPING(fp->action);
23dfb9
 	fp->setmeta = 1;
23dfb9
 	if (XFA_CREATING(fp->action) && !S_ISDIR(rpmfiFMode(fi)))
23dfb9
@@ -881,20 +887,32 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	/* Remap file perms, owner, and group. */
23dfb9
 	rc = rpmfiStat(fi, 1, &fp->sb);
23dfb9
 
23dfb9
+	setFileState(fs, fx);
23dfb9
 	fsmDebug(fp->fpath, fp->action, &fp->sb);
23dfb9
 
23dfb9
-        /* Exit on error. */
23dfb9
-        if (rc)
23dfb9
-            break;
23dfb9
-
23dfb9
 	/* Run fsm file pre hook for all plugins */
23dfb9
 	rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath,
23dfb9
 				      fp->sb.st_mode, fp->action);
23dfb9
-	if (rc) {
23dfb9
-	    fp->skip = 1;
23dfb9
-	} else {
23dfb9
-	    setFileState(fs, fx);
23dfb9
-	}
23dfb9
+	fp->stage = FILE_PRE;
23dfb9
+    }
23dfb9
+    fi = rpmfiFree(fi);
23dfb9
+
23dfb9
+    if (rc)
23dfb9
+	goto exit;
23dfb9
+
23dfb9
+    fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
23dfb9
+    if (fi == NULL) {
23dfb9
+        rc = RPMERR_BAD_MAGIC;
23dfb9
+        goto exit;
23dfb9
+    }
23dfb9
+
23dfb9
+    /* Detect and create directories not explicitly in package. */
23dfb9
+    if (!rc)
23dfb9
+	rc = fsmMkdirs(files, fs, plugins);
23dfb9
+
23dfb9
+    /* Process the payload */
23dfb9
+    while (!rc && (fx = rpmfiNext(fi)) >= 0) {
23dfb9
+	struct filedata_s *fp = &fdata[fx];
23dfb9
 
23dfb9
         if (!fp->skip) {
23dfb9
 	    /* Directories replacing something need early backup */
23dfb9
@@ -918,7 +936,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 
23dfb9
 	    /* When touching we don't need any of this... */
23dfb9
 	    if (fp->action == FA_TOUCH)
23dfb9
-		goto touch;
23dfb9
+		continue;
23dfb9
 
23dfb9
             if (S_ISREG(fp->sb.st_mode)) {
23dfb9
 		if (rc == RPMERR_ENOENT) {
23dfb9
@@ -954,12 +972,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
                     rc = RPMERR_UNKNOWN_FILETYPE;
23dfb9
             }
23dfb9
 
23dfb9
-touch:
23dfb9
-	    /* Set permissions, timestamps etc for non-hardlink entries */
23dfb9
-	    if (!rc && fp->setmeta) {
23dfb9
-		rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action,
23dfb9
-				&fp->sb, nofcaps);
23dfb9
-	    }
23dfb9
         } else if (firstlink && rpmfiArchiveHasContent(fi)) {
23dfb9
 	    /* On FA_TOUCH no hardlinks are created thus this is skipped. */
23dfb9
 	    /* we skip the hard linked file containing the content */
23dfb9
@@ -969,47 +981,74 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
23dfb9
 	    firstlink = NULL;
23dfb9
 	}
23dfb9
 
23dfb9
-        if (rc) {
23dfb9
-            if (!fp->skip) {
23dfb9
-                /* XXX only erase if temp fn w suffix is in use */
23dfb9
-                if (fp->suffix) {
23dfb9
-		    (void) fsmRemove(fp->fpath, fp->sb.st_mode);
23dfb9
-                }
23dfb9
-                errno = saveerrno;
23dfb9
-            }
23dfb9
-        } else {
23dfb9
-	    /* Notify on success. */
23dfb9
+	/* Notify on success. */
23dfb9
+	if (rc)
23dfb9
+	    *failedFile = xstrdup(fp->fpath);
23dfb9
+	else
23dfb9
 	    rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi));
23dfb9
+	fp->stage = FILE_UNPACK;
23dfb9
+    }
23dfb9
+    fi = rpmfiFree(fi);
23dfb9
 
23dfb9
-	    if (!fp->skip) {
23dfb9
-		/* Backup file if needed. Directories are handled earlier */
23dfb9
-		if (fp->suffix)
23dfb9
-		    rc = fsmBackup(fi, fp->action);
23dfb9
+    if (!rc && fx < 0 && fx != RPMERR_ITER_END)
23dfb9
+	rc = fx;
23dfb9
 
23dfb9
-		if (!rc)
23dfb9
-		    rc = fsmCommit(&fp->fpath, fi, fp->action, fp->suffix);
23dfb9
-	    }
23dfb9
+    /* Set permissions, timestamps etc for non-hardlink entries */
23dfb9
+    fi = rpmfilesIter(files, RPMFI_ITER_FWD);
23dfb9
+    while (!rc && (fx = rpmfiNext(fi)) >= 0) {
23dfb9
+	struct filedata_s *fp = &fdata[fx];
23dfb9
+	if (!fp->skip && fp->setmeta) {
23dfb9
+	    rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action,
23dfb9
+			    &fp->sb, nofcaps);
23dfb9
 	}
23dfb9
-
23dfb9
 	if (rc)
23dfb9
 	    *failedFile = xstrdup(fp->fpath);
23dfb9
+	fp->stage = FILE_PREP;
23dfb9
+    }
23dfb9
+    fi = rpmfiFree(fi);
23dfb9
 
23dfb9
-	/* Run fsm file post hook for all plugins */
23dfb9
-	rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath,
23dfb9
-				  fp->sb.st_mode, fp->action, rc);
23dfb9
+    /* If all went well, commit files to final destination */
23dfb9
+    fi = rpmfilesIter(files, RPMFI_ITER_FWD);
23dfb9
+    while (!rc && (fx = rpmfiNext(fi)) >= 0) {
23dfb9
+	struct filedata_s *fp = &fdata[fx];
23dfb9
+
23dfb9
+	if (!fp->skip) {
23dfb9
+	    /* Backup file if needed. Directories are handled earlier */
23dfb9
+	    if (!rc && fp->suffix)
23dfb9
+		rc = fsmBackup(fi, fp->action);
23dfb9
+
23dfb9
+	    if (!rc)
23dfb9
+		rc = fsmCommit(&fp->fpath, fi, fp->action, fp->suffix);
23dfb9
+
23dfb9
+	    if (!rc)
23dfb9
+		fp->stage = FILE_COMMIT;
23dfb9
+	    else
23dfb9
+		*failedFile = xstrdup(fp->fpath);
23dfb9
+	}
23dfb9
     }
23dfb9
+    fi = rpmfiFree(fi);
23dfb9
 
23dfb9
-    if (!rc && fx != RPMERR_ITER_END)
23dfb9
-	rc = fx;
23dfb9
+    /* Walk backwards in case we need to erase */
23dfb9
+    fi = rpmfilesIter(files, RPMFI_ITER_BACK);
23dfb9
+    while ((fx = rpmfiNext(fi)) >= 0) {
23dfb9
+	struct filedata_s *fp = &fdata[fx];
23dfb9
+	/* Run fsm file post hook for all plugins for all processed files */
23dfb9
+	if (fp->stage) {
23dfb9
+	    rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath,
23dfb9
+				      fp->sb.st_mode, fp->action, rc);
23dfb9
+	}
23dfb9
+
23dfb9
+	/* On failure, erase non-committed files */
23dfb9
+	if (rc && fp->stage > FILE_NONE && !fp->skip) {
23dfb9
+	    (void) fsmRemove(fp->fpath, fp->sb.st_mode);
23dfb9
+	}
23dfb9
+    }
23dfb9
 
23dfb9
     rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), fdOp(payload, FDSTAT_READ));
23dfb9
     rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));
23dfb9
 
23dfb9
 exit:
23dfb9
-
23dfb9
-    /* No need to bother with close errors on read */
23dfb9
-    rpmfiArchiveClose(fi);
23dfb9
-    rpmfiFree(fi);
23dfb9
+    fi = rpmfiFree(fi);
23dfb9
     Fclose(payload);
23dfb9
     free(tid);
23dfb9
     for (int i = 0; i < fc; i++)
23dfb9
diff --git a/tests/data/SPECS/hlinktest.spec b/tests/data/SPECS/hlinktest.spec
23dfb9
index 753c174cd8..3f1437d89c 100644
23dfb9
--- a/tests/data/SPECS/hlinktest.spec
23dfb9
+++ b/tests/data/SPECS/hlinktest.spec
23dfb9
@@ -1,5 +1,6 @@
23dfb9
 %bcond_with unpackaged_dirs
23dfb9
 %bcond_with unpackaged_files
23dfb9
+%bcond_with owned_dir
23dfb9
 
23dfb9
 Summary:          Testing hard link behavior
23dfb9
 Name:             hlinktest
23dfb9
@@ -43,4 +44,7 @@ touch $RPM_BUILD_ROOT/teet
23dfb9
 
23dfb9
 %files
23dfb9
 %defattr(-,root,root)
23dfb9
+%if %{with owned_dir}
23dfb9
+%dir /foo
23dfb9
+%endif
23dfb9
 /foo/*
23dfb9
diff --git a/tests/rpmbuild.at b/tests/rpmbuild.at
23dfb9
index 1f0e679c44..f0eef4eff0 100644
23dfb9
--- a/tests/rpmbuild.at
23dfb9
+++ b/tests/rpmbuild.at
23dfb9
@@ -253,38 +253,6 @@ drwxrwxrwx zoot     zoot     /j/dir
23dfb9
 [])
23dfb9
 AT_CLEANUP
23dfb9
 
23dfb9
-# ------------------------------
23dfb9
-# hardlink tests
23dfb9
-AT_SETUP([rpmbuild hardlink])
23dfb9
-AT_KEYWORDS([build])
23dfb9
-RPMDB_INIT
23dfb9
-AT_CHECK([
23dfb9
-RPMDB_INIT
23dfb9
-
23dfb9
-runroot rpmbuild \
23dfb9
-  -bb --quiet /data/SPECS/hlinktest.spec
23dfb9
-
23dfb9
-runroot rpm -i /build/RPMS/noarch/hlinktest-1.0-1.noarch.rpm
23dfb9
-
23dfb9
-runroot rpm -q --qf "[[%{filenlinks} %{filenames}\n]]%{longsize}\n" hlinktest
23dfb9
-runroot rpm -V --nouser --nogroup hlinktest
23dfb9
-ls -i "${RPMTEST}"/foo/hello* | awk {'print $1'} | sort -u | wc -l
23dfb9
-
23dfb9
-],
23dfb9
-[0],
23dfb9
-[2 /foo/aaaa
23dfb9
-1 /foo/copyllo
23dfb9
-4 /foo/hello
23dfb9
-4 /foo/hello-bar
23dfb9
-4 /foo/hello-foo
23dfb9
-4 /foo/hello-world
23dfb9
-2 /foo/zzzz
23dfb9
-87
23dfb9
-1
23dfb9
-],
23dfb9
-[])
23dfb9
-AT_CLEANUP
23dfb9
-
23dfb9
 AT_SETUP([rpmbuild unpackaged files])
23dfb9
 AT_KEYWORDS([build])
23dfb9
 RPMDB_INIT
23dfb9
diff --git a/tests/rpmi.at b/tests/rpmi.at
23dfb9
index 42dc52ba35..295fbc230f 100644
23dfb9
--- a/tests/rpmi.at
23dfb9
+++ b/tests/rpmi.at
23dfb9
@@ -722,3 +722,95 @@ runroot rpm -V --nouser --nogroup suicidal
23dfb9
 [],
23dfb9
 [])
23dfb9
 AT_CLEANUP
23dfb9
+
23dfb9
+# ------------------------------
23dfb9
+# hardlink tests
23dfb9
+AT_SETUP([rpm -i hardlinks])
23dfb9
+AT_KEYWORDS([build install])
23dfb9
+RPMDB_INIT
23dfb9
+
23dfb9
+# Need a reproducable test package
23dfb9
+runroot rpmbuild \
23dfb9
+  --define "%optflags -O2 -g" \
23dfb9
+  --define "%_target_platform noarch-linux" \
23dfb9
+  --define "%_binary_payload w.ufdio" \
23dfb9
+  --define "%_buildhost localhost" \
23dfb9
+  --define "%use_source_date_epoch_as_buildtime 1" \
23dfb9
+  --define "%source_date_epoch_from_changelog 1" \
23dfb9
+  --define "%clamp_mtime_to_source_date_epoch 1" \
23dfb9
+  --with owned_dir \
23dfb9
+  -bb --quiet /data/SPECS/hlinktest.spec
23dfb9
+
23dfb9
+pkg="/build/RPMS/noarch/hlinktest-1.0-1.noarch.rpm"
23dfb9
+
23dfb9
+cp "${RPMTEST}/${pkg}" "${RPMTEST}/tmp/1.rpm"
23dfb9
+dd if=/dev/zero of="${RPMTEST}/tmp/1.rpm" \
23dfb9
+   conv=notrunc bs=1 seek=8180 count=6 2> /dev/null
23dfb9
+
23dfb9
+cp "${RPMTEST}/${pkg}" "${RPMTEST}/tmp/2.rpm"
23dfb9
+dd if=/dev/zero of="${RPMTEST}/tmp/2.rpm" \
23dfb9
+   conv=notrunc bs=1 seek=8150 count=6 2> /dev/null
23dfb9
+
23dfb9
+cp "${RPMTEST}/${pkg}" "${RPMTEST}/tmp/3.rpm"
23dfb9
+dd if=/dev/zero of="${RPMTEST}/tmp/3.rpm" \
23dfb9
+   conv=notrunc bs=1 seek=8050 count=6 2> /dev/null
23dfb9
+
23dfb9
+AT_CHECK([
23dfb9
+RPMDB_INIT
23dfb9
+runroot rpm -i --noverify /tmp/1.rpm
23dfb9
+# test that nothing of the contents remains after failure
23dfb9
+test -d "${RPMTEST}/foo"
23dfb9
+],
23dfb9
+[1],
23dfb9
+[],
23dfb9
+[error: unpacking of archive failed: cpio: Archive file not in header
23dfb9
+error: hlinktest-1.0-1.noarch: install failed
23dfb9
+])
23dfb9
+
23dfb9
+AT_CHECK([
23dfb9
+RPMDB_INIT
23dfb9
+runroot rpm -i --noverify /tmp/2.rpm
23dfb9
+# test that nothing of the contents remains after failure
23dfb9
+test -d "${RPMTEST}/foo"
23dfb9
+],
23dfb9
+[1],
23dfb9
+[],
23dfb9
+[error: unpacking of archive failed: cpio: Bad/unreadable  header
23dfb9
+error: hlinktest-1.0-1.noarch: install failed
23dfb9
+])
23dfb9
+
23dfb9
+AT_CHECK([
23dfb9
+RPMDB_INIT
23dfb9
+runroot rpm -i --noverify /tmp/3.rpm 2>&1| sed 's/;.*:/:/g'
23dfb9
+# test that nothing of the contents remains after failure
23dfb9
+test -d "${RPMTEST}/foo"
23dfb9
+],
23dfb9
+[1],
23dfb9
+[error: unpacking of archive failed on file /foo/hello-world: Digest mismatch
23dfb9
+error: hlinktest-1.0-1.noarch: install failed
23dfb9
+],
23dfb9
+[])
23dfb9
+
23dfb9
+AT_CHECK([
23dfb9
+RPMDB_INIT
23dfb9
+runroot rpm -i /build/RPMS/noarch/hlinktest-1.0-1.noarch.rpm
23dfb9
+runroot rpm -q --qf "[[%{filenlinks} %{filenames}\n]]%{longsize}\n" hlinktest
23dfb9
+ls -i "${RPMTEST}"/foo/hello* | awk {'print $1'} | sort -u | wc -l
23dfb9
+runroot rpm -e hlinktest
23dfb9
+
23dfb9
+],
23dfb9
+[0],
23dfb9
+[1 /foo
23dfb9
+2 /foo/aaaa
23dfb9
+1 /foo/copyllo
23dfb9
+4 /foo/hello
23dfb9
+4 /foo/hello-bar
23dfb9
+4 /foo/hello-foo
23dfb9
+4 /foo/hello-world
23dfb9
+2 /foo/zzzz
23dfb9
+87
23dfb9
+1
23dfb9
+],
23dfb9
+[])
23dfb9
+AT_CLEANUP
23dfb9
+