Florian Festi 94360a
From 36ee14a07630668629a0d461fba8b5b2248d7d71 Mon Sep 17 00:00:00 2001
Florian Festi 94360a
From: Florian Festi <ffesti@redhat.com>
Florian Festi 94360a
Date: Tue, 10 Oct 2023 16:46:17 +0200
Florian Festi 94360a
Subject: [PATCH] Use file state machine from rpm-4.19
Florian Festi 94360a
Florian Festi 94360a
This new implementation fixes several race conditions when placing down
Florian Festi 94360a
files on disc
Florian Festi 94360a
---
Florian Festi 94360a
 lib/fsm.c        | 1164 +++++++++++++++++++++++++---------------------
Florian Festi 94360a
 lib/rpmarchive.h |    3 +
Florian Festi 94360a
 lib/rpmfiles.h   |    3 +
Florian Festi 94360a
Florian Festi 94360a
diff --git a/lib/rpmarchive.h b/lib/rpmarchive.h
Florian Festi 94360a
index c864e5b56..e5cda4f97 100644
Florian Festi 94360a
--- a/lib/rpmarchive.h
Florian Festi 94360a
+++ b/lib/rpmarchive.h
Florian Festi 94360a
@@ -26,6 +26,8 @@ enum rpmfilesErrorCodes {
Florian Festi 94360a
 	RPMERR_FILE_SIZE	= -12,
Florian Festi 94360a
 	RPMERR_ITER_SKIP	= -13,
Florian Festi 94360a
 	RPMERR_EXIST_AS_DIR	= -14,
Florian Festi 94360a
+	RPMERR_INVALID_SYMLINK  = -15,
Florian Festi 94360a
+	RPMERR_ENOTDIR          = -16,
Florian Festi 94360a
 
Florian Festi 94360a
 	RPMERR_OPEN_FAILED	= -32768,
Florian Festi 94360a
 	RPMERR_CHMOD_FAILED	= -32769,
Florian Festi 94360a
@@ -47,6 +49,7 @@ enum rpmfilesErrorCodes {
Florian Festi 94360a
 	RPMERR_COPY_FAILED	= -32785,
Florian Festi 94360a
 	RPMERR_LSETFCON_FAILED	= -32786,
Florian Festi 94360a
 	RPMERR_SETCAP_FAILED	= -32787,
Florian Festi 94360a
+	RPMERR_CLOSE_FAILED     = -32788,
Florian Festi 94360a
 };
Florian Festi 94360a
 
Florian Festi 94360a
 #ifdef __cplusplus
Florian Festi 94360a
diff --git a/lib/rpmfiles.h b/lib/rpmfiles.h
Florian Festi 94360a
index daf572cf4..e74bb2201 100644
Florian Festi 94360a
--- a/lib/rpmfiles.h
Florian Festi 94360a
+++ b/lib/rpmfiles.h
Florian Festi 94360a
@@ -90,6 +90,9 @@ typedef enum rpmFileAction_e {
Florian Festi 94360a
 #define XFA_SKIPPING(_a)	\
Florian Festi 94360a
     ((_a) == FA_SKIP || (_a) == FA_SKIPNSTATE || (_a) == FA_SKIPNETSHARED || (_a) == FA_SKIPCOLOR)
Florian Festi 94360a
 
Florian Festi 94360a
+#define XFA_CREATING(_a)	\
Florian Festi 94360a
+    ((_a) == FA_CREATE || (_a) == FA_BACKUP || (_a) == FA_SAVE || (_a) == FA_ALTNAME)
Florian Festi 94360a
+
Florian Festi 94360a
 /**
Florian Festi 94360a
  * We pass these around as an array with a sentinel.
Florian Festi 94360a
  */
Florian Festi 94360a
--- rpm-4.16.1.3/lib/fsm.c.orig	2023-11-11 10:05:19.208206675 +0100
Florian Festi 94360a
+++ rpm-4.16.1.3/lib/fsm.c	2023-11-11 10:05:43.559432708 +0100
Florian Festi 94360a
@@ -5,9 +5,11 @@
Florian Festi 94360a
 
Florian Festi 94360a
 #include "system.h"
Florian Festi 94360a
 
Florian Festi 94360a
+#include <inttypes.h>
Florian Festi 94360a
 #include <utime.h>
Florian Festi 94360a
 #include <errno.h>
Florian Festi 94360a
-#if WITH_CAP
Florian Festi 94360a
+#include <fcntl.h>
Florian Festi 94360a
+#ifdef WITH_CAP
Florian Festi 94360a
 #include <sys/capability.h>
Florian Festi 94360a
 #endif
Florian Festi 94360a
 
Florian Festi 94360a
@@ -17,10 +19,11 @@
Florian Festi 94360a
 #include <rpm/rpmmacro.h>
Florian Festi 94360a
 
Florian Festi 94360a
 #include "rpmio/rpmio_internal.h"	/* fdInit/FiniDigest */
Florian Festi 94360a
-#include "lib/fsm.h"
Florian Festi 94360a
-#include "lib/rpmte_internal.h"	/* XXX rpmfs */
Florian Festi 94360a
-#include "lib/rpmplugins.h"	/* rpm plugins hooks */
Florian Festi 94360a
-#include "lib/rpmug.h"
Florian Festi 94360a
+#include "fsm.h"
Florian Festi 94360a
+#include "rpmte_internal.h"	/* XXX rpmfs */
Florian Festi 94360a
+#include "rpmfi_internal.h" /* rpmfiSetOnChdir */
Florian Festi 94360a
+#include "rpmplugins.h"	/* rpm plugins hooks */
Florian Festi 94360a
+#include "rpmug.h"
Florian Festi 94360a
 
Florian Festi 94360a
 #include "debug.h"
Florian Festi 94360a
 
Florian Festi 94360a
@@ -38,172 +41,92 @@
Florian Festi 94360a
 #define _dirPerms 0755
Florian Festi 94360a
 #define _filePerms 0644
Florian Festi 94360a
 
Florian Festi 94360a
+enum filestage_e {
Florian Festi 94360a
+    FILE_COMMIT = -1,
Florian Festi 94360a
+    FILE_NONE   = 0,
Florian Festi 94360a
+    FILE_PRE    = 1,
Florian Festi 94360a
+    FILE_UNPACK = 2,
Florian Festi 94360a
+    FILE_PREP   = 3,
Florian Festi 94360a
+    FILE_POST   = 4,
Florian Festi 94360a
+};
Florian Festi 94360a
+
Florian Festi 94360a
+struct filedata_s {
Florian Festi 94360a
+    int stage;
Florian Festi 94360a
+    int setmeta;
Florian Festi 94360a
+    int skip;
Florian Festi 94360a
+    rpmFileAction action;
Florian Festi 94360a
+    const char *suffix;
Florian Festi 94360a
+    char *fpath;
Florian Festi 94360a
+    struct stat sb;
Florian Festi 94360a
+};
Florian Festi 94360a
+
Florian Festi 94360a
 /* 
Florian Festi 94360a
  * XXX Forward declarations for previously exported functions to avoid moving 
Florian Festi 94360a
  * things around needlessly 
Florian Festi 94360a
  */ 
Florian Festi 94360a
 static const char * fileActionString(rpmFileAction a);
Florian Festi 94360a
+static int fsmOpenat(int dirfd, const char *path, int flags, int dir);
Florian Festi 94360a
+static int fsmClose(int *wfdp);
Florian Festi 94360a
 
Florian Festi 94360a
 /** \ingroup payload
Florian Festi 94360a
  * Build path to file from file info, optionally ornamented with suffix.
Florian Festi 94360a
+ * "/" needs special handling to avoid appearing as empty path.
Florian Festi 94360a
  * @param fi		file info iterator
Florian Festi 94360a
  * @param suffix	suffix to use (NULL disables)
Florian Festi 94360a
- * @retval		path to file (malloced)
Florian Festi 94360a
+ * @param[out]		path to file (malloced)
Florian Festi 94360a
  */
Florian Festi 94360a
 static char * fsmFsPath(rpmfi fi, const char * suffix)
Florian Festi 94360a
 {
Florian Festi 94360a
-    return rstrscat(NULL, rpmfiDN(fi), rpmfiBN(fi), suffix ? suffix : "", NULL);
Florian Festi 94360a
-}
Florian Festi 94360a
-
Florian Festi 94360a
-/** \ingroup payload
Florian Festi 94360a
- * Directory name iterator.
Florian Festi 94360a
- */
Florian Festi 94360a
-typedef struct dnli_s {
Florian Festi 94360a
-    rpmfiles fi;
Florian Festi 94360a
-    char * active;
Florian Festi 94360a
-    int reverse;
Florian Festi 94360a
-    int isave;
Florian Festi 94360a
-    int i;
Florian Festi 94360a
-} * DNLI_t;
Florian Festi 94360a
-
Florian Festi 94360a
-/** \ingroup payload
Florian Festi 94360a
- * Destroy directory name iterator.
Florian Festi 94360a
- * @param dnli		directory name iterator
Florian Festi 94360a
- * @retval		NULL always
Florian Festi 94360a
- */
Florian Festi 94360a
-static DNLI_t dnlFreeIterator(DNLI_t dnli)
Florian Festi 94360a
-{
Florian Festi 94360a
-    if (dnli) {
Florian Festi 94360a
-	if (dnli->active) free(dnli->active);
Florian Festi 94360a
-	free(dnli);
Florian Festi 94360a
-    }
Florian Festi 94360a
-    return NULL;
Florian Festi 94360a
+    const char *bn = rpmfiBN(fi);
Florian Festi 94360a
+    return rstrscat(NULL, *bn ? bn : "/", suffix ? suffix : "", NULL);
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-/** \ingroup payload
Florian Festi 94360a
- * Create directory name iterator.
Florian Festi 94360a
- * @param fi		file info set
Florian Festi 94360a
- * @param fs		file state set
Florian Festi 94360a
- * @param reverse	traverse directory names in reverse order?
Florian Festi 94360a
- * @return		directory name iterator
Florian Festi 94360a
- */
Florian Festi 94360a
-static DNLI_t dnlInitIterator(rpmfiles fi, rpmfs fs, int reverse)
Florian Festi 94360a
+static int fsmLink(int odirfd, const char *opath, int dirfd, const char *path)
Florian Festi 94360a
 {
Florian Festi 94360a
-    DNLI_t dnli;
Florian Festi 94360a
-    int i, j;
Florian Festi 94360a
-    int dc;
Florian Festi 94360a
-
Florian Festi 94360a
-    if (fi == NULL)
Florian Festi 94360a
-	return NULL;
Florian Festi 94360a
-    dc = rpmfilesDC(fi);
Florian Festi 94360a
-    dnli = xcalloc(1, sizeof(*dnli));
Florian Festi 94360a
-    dnli->fi = fi;
Florian Festi 94360a
-    dnli->reverse = reverse;
Florian Festi 94360a
-    dnli->i = (reverse ? dc : 0);
Florian Festi 94360a
-
Florian Festi 94360a
-    if (dc) {
Florian Festi 94360a
-	dnli->active = xcalloc(dc, sizeof(*dnli->active));
Florian Festi 94360a
-	int fc = rpmfilesFC(fi);
Florian Festi 94360a
-
Florian Festi 94360a
-	/* Identify parent directories not skipped. */
Florian Festi 94360a
-	for (i = 0; i < fc; i++)
Florian Festi 94360a
-            if (!XFA_SKIPPING(rpmfsGetAction(fs, i)))
Florian Festi 94360a
-		dnli->active[rpmfilesDI(fi, i)] = 1;
Florian Festi 94360a
-
Florian Festi 94360a
-	/* Exclude parent directories that are explicitly included. */
Florian Festi 94360a
-	for (i = 0; i < fc; i++) {
Florian Festi 94360a
-	    int dil;
Florian Festi 94360a
-	    size_t dnlen, bnlen;
Florian Festi 94360a
+    int rc = linkat(odirfd, opath, dirfd, path, 0);
Florian Festi 94360a
 
Florian Festi 94360a
-	    if (!S_ISDIR(rpmfilesFMode(fi, i)))
Florian Festi 94360a
-		continue;
Florian Festi 94360a
-
Florian Festi 94360a
-	    dil = rpmfilesDI(fi, i);
Florian Festi 94360a
-	    dnlen = strlen(rpmfilesDN(fi, dil));
Florian Festi 94360a
-	    bnlen = strlen(rpmfilesBN(fi, i));
Florian Festi 94360a
-
Florian Festi 94360a
-	    for (j = 0; j < dc; j++) {
Florian Festi 94360a
-		const char * dnl;
Florian Festi 94360a
-		size_t jlen;
Florian Festi 94360a
-
Florian Festi 94360a
-		if (!dnli->active[j] || j == dil)
Florian Festi 94360a
-		    continue;
Florian Festi 94360a
-		dnl = rpmfilesDN(fi, j);
Florian Festi 94360a
-		jlen = strlen(dnl);
Florian Festi 94360a
-		if (jlen != (dnlen+bnlen+1))
Florian Festi 94360a
-		    continue;
Florian Festi 94360a
-		if (!rstreqn(dnl, rpmfilesDN(fi, dil), dnlen))
Florian Festi 94360a
-		    continue;
Florian Festi 94360a
-		if (!rstreqn(dnl+dnlen, rpmfilesBN(fi, i), bnlen))
Florian Festi 94360a
-		    continue;
Florian Festi 94360a
-		if (dnl[dnlen+bnlen] != '/' || dnl[dnlen+bnlen+1] != '\0')
Florian Festi 94360a
-		    continue;
Florian Festi 94360a
-		/* This directory is included in the package. */
Florian Festi 94360a
-		dnli->active[j] = 0;
Florian Festi 94360a
-		break;
Florian Festi 94360a
-	    }
Florian Festi 94360a
-	}
Florian Festi 94360a
-
Florian Festi 94360a
-	/* Print only once per package. */
Florian Festi 94360a
-	if (!reverse) {
Florian Festi 94360a
-	    j = 0;
Florian Festi 94360a
-	    for (i = 0; i < dc; i++) {
Florian Festi 94360a
-		if (!dnli->active[i]) continue;
Florian Festi 94360a
-		if (j == 0) {
Florian Festi 94360a
-		    j = 1;
Florian Festi 94360a
-		    rpmlog(RPMLOG_DEBUG,
Florian Festi 94360a
-	"========== Directories not explicitly included in package:\n");
Florian Festi 94360a
-		}
Florian Festi 94360a
-		rpmlog(RPMLOG_DEBUG, "%10d %s\n", i, rpmfilesDN(fi, i));
Florian Festi 94360a
-	    }
Florian Festi 94360a
-	    if (j)
Florian Festi 94360a
-		rpmlog(RPMLOG_DEBUG, "==========\n");
Florian Festi 94360a
-	}
Florian Festi 94360a
+    if (_fsm_debug) {
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d %s, %d %s) %s\n", __func__,
Florian Festi 94360a
+	       odirfd, opath, dirfd, path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     }
Florian Festi 94360a
-    return dnli;
Florian Festi 94360a
+
Florian Festi 94360a
+    if (rc < 0)
Florian Festi 94360a
+	rc = RPMERR_LINK_FAILED;
Florian Festi 94360a
+    return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-/** \ingroup payload
Florian Festi 94360a
- * Return next directory name (from file info).
Florian Festi 94360a
- * @param dnli		directory name iterator
Florian Festi 94360a
- * @return		next directory name
Florian Festi 94360a
- */
Florian Festi 94360a
-static
Florian Festi 94360a
-const char * dnlNextIterator(DNLI_t dnli)
Florian Festi 94360a
+#ifdef WITH_CAP
Florian Festi 94360a
+static int cap_set_fileat(int dirfd, const char *path, cap_t fcaps)
Florian Festi 94360a
 {
Florian Festi 94360a
-    const char * dn = NULL;
Florian Festi 94360a
-
Florian Festi 94360a
-    if (dnli) {
Florian Festi 94360a
-	rpmfiles fi = dnli->fi;
Florian Festi 94360a
-	int dc = rpmfilesDC(fi);
Florian Festi 94360a
-	int i = -1;
Florian Festi 94360a
-
Florian Festi 94360a
-	if (dnli->active)
Florian Festi 94360a
-	do {
Florian Festi 94360a
-	    i = (!dnli->reverse ? dnli->i++ : --dnli->i);
Florian Festi 94360a
-	} while (i >= 0 && i < dc && !dnli->active[i]);
Florian Festi 94360a
-
Florian Festi 94360a
-	if (i >= 0 && i < dc)
Florian Festi 94360a
-	    dn = rpmfilesDN(fi, i);
Florian Festi 94360a
-	else
Florian Festi 94360a
-	    i = -1;
Florian Festi 94360a
-	dnli->isave = i;
Florian Festi 94360a
+    int rc = -1;
Florian Festi 94360a
+    int fd = fsmOpenat(dirfd, path, O_RDONLY|O_NOFOLLOW, 0);
Florian Festi 94360a
+    if (fd >= 0) {
Florian Festi 94360a
+	rc = cap_set_fd(fd, fcaps);
Florian Festi 94360a
+	fsmClose(&fd;;
Florian Festi 94360a
     }
Florian Festi 94360a
-    return dn;
Florian Festi 94360a
+    return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
+#endif
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmSetFCaps(const char *path, const char *captxt)
Florian Festi 94360a
+static int fsmSetFCaps(int fd, int dirfd, const char *path, const char *captxt)
Florian Festi 94360a
 {
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
-#if WITH_CAP
Florian Festi 94360a
+
Florian Festi 94360a
+#ifdef WITH_CAP
Florian Festi 94360a
     if (captxt && *captxt != '\0') {
Florian Festi 94360a
 	cap_t fcaps = cap_from_text(captxt);
Florian Festi 94360a
-	if (fcaps == NULL || cap_set_file(path, fcaps) != 0) {
Florian Festi 94360a
-	    rc = RPMERR_SETCAP_FAILED;
Florian Festi 94360a
+
Florian Festi 94360a
+	if (fd >= 0) {
Florian Festi 94360a
+	    if (fcaps == NULL || cap_set_fd(fd, fcaps))
Florian Festi 94360a
+		rc = RPMERR_SETCAP_FAILED;
Florian Festi 94360a
+	} else {
Florian Festi 94360a
+	    if (fcaps == NULL || cap_set_fileat(dirfd, path, fcaps))
Florian Festi 94360a
+		rc = RPMERR_SETCAP_FAILED;
Florian Festi 94360a
 	}
Florian Festi 94360a
+
Florian Festi 94360a
 	if (_fsm_debug) {
Florian Festi 94360a
-	    rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__,
Florian Festi 94360a
-		   path, captxt, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+	    rpmlog(RPMLOG_DEBUG, " %8s (%d - %d %s, %s) %s\n", __func__,
Florian Festi 94360a
+		   fd, dirfd, path, captxt, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
 	}
Florian Festi 94360a
 	cap_free(fcaps);
Florian Festi 94360a
     } 
Florian Festi 94360a
@@ -211,101 +134,104 @@
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static void wfd_close(FD_t *wfdp)
Florian Festi 94360a
+static int fsmClose(int *wfdp)
Florian Festi 94360a
 {
Florian Festi 94360a
-    if (wfdp && *wfdp) {
Florian Festi 94360a
+    int rc = 0;
Florian Festi 94360a
+    if (wfdp && *wfdp >= 0) {
Florian Festi 94360a
 	int myerrno = errno;
Florian Festi 94360a
 	static int oneshot = 0;
Florian Festi 94360a
 	static int flush_io = 0;
Florian Festi 94360a
+	int fdno = *wfdp;
Florian Festi 94360a
+
Florian Festi 94360a
 	if (!oneshot) {
Florian Festi 94360a
 	    flush_io = (rpmExpandNumeric("%{?_flush_io}") > 0);
Florian Festi 94360a
 	    oneshot = 1;
Florian Festi 94360a
 	}
Florian Festi 94360a
 	if (flush_io) {
Florian Festi 94360a
-	    int fdno = Fileno(*wfdp);
Florian Festi 94360a
 	    fsync(fdno);
Florian Festi 94360a
 	}
Florian Festi 94360a
-	Fclose(*wfdp);
Florian Festi 94360a
-	*wfdp = NULL;
Florian Festi 94360a
+	if (close(fdno))
Florian Festi 94360a
+	    rc = RPMERR_CLOSE_FAILED;
Florian Festi 94360a
+
Florian Festi 94360a
+	if (_fsm_debug) {
Florian Festi 94360a
+	    rpmlog(RPMLOG_DEBUG, " %8s ([%d]) %s\n", __func__,
Florian Festi 94360a
+		   fdno, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+	}
Florian Festi 94360a
+	*wfdp = -1;
Florian Festi 94360a
 	errno = myerrno;
Florian Festi 94360a
     }
Florian Festi 94360a
+    return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int wfd_open(FD_t *wfdp, const char *dest)
Florian Festi 94360a
+static int fsmOpen(int *wfdp, int dirfd, const char *dest)
Florian Festi 94360a
 {
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
     /* Create the file with 0200 permissions (write by owner). */
Florian Festi 94360a
-    {
Florian Festi 94360a
-	mode_t old_umask = umask(0577);
Florian Festi 94360a
-	*wfdp = Fopen(dest, "wx.ufdio");
Florian Festi 94360a
-	umask(old_umask);
Florian Festi 94360a
-    }
Florian Festi 94360a
-    if (Ferror(*wfdp)) {
Florian Festi 94360a
+    int fd = openat(dirfd, dest, O_WRONLY|O_EXCL|O_CREAT, 0200);
Florian Festi 94360a
+
Florian Festi 94360a
+    if (fd < 0)
Florian Festi 94360a
 	rc = RPMERR_OPEN_FAILED;
Florian Festi 94360a
-	goto exit;
Florian Festi 94360a
-    }
Florian Festi 94360a
 
Florian Festi 94360a
-    return 0;
Florian Festi 94360a
+    if (_fsm_debug) {
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%s [%d]) %s\n", __func__,
Florian Festi 94360a
+	       dest, fd, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+    }
Florian Festi 94360a
+    *wfdp = fd;
Florian Festi 94360a
 
Florian Festi 94360a
-exit:
Florian Festi 94360a
-    wfd_close(wfdp);
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-/** \ingroup payload
Florian Festi 94360a
- * Create file from payload stream.
Florian Festi 94360a
- * @return		0 on success
Florian Festi 94360a
- */
Florian Festi 94360a
-static int expandRegular(rpmfi fi, const char *dest, rpmpsm psm, int nodigest)
Florian Festi 94360a
+static int fsmUnpack(rpmfi fi, int fdno, rpmpsm psm, int nodigest)
Florian Festi 94360a
 {
Florian Festi 94360a
-    FD_t wfd = NULL;
Florian Festi 94360a
-    int rc;
Florian Festi 94360a
-
Florian Festi 94360a
-    rc = wfd_open(&wfd, dest);
Florian Festi 94360a
-    if (rc != 0)
Florian Festi 94360a
-        goto exit;
Florian Festi 94360a
-
Florian Festi 94360a
-    rc = rpmfiArchiveReadToFilePsm(fi, wfd, nodigest, psm);
Florian Festi 94360a
-    wfd_close(&wfd;;
Florian Festi 94360a
-exit:
Florian Festi 94360a
+    FD_t fd = fdDup(fdno);
Florian Festi 94360a
+    int rc = rpmfiArchiveReadToFilePsm(fi, fd, nodigest, psm);
Florian Festi 94360a
+    if (_fsm_debug) {
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%s %" PRIu64 " bytes [%d]) %s\n", __func__,
Florian Festi 94360a
+	       rpmfiFN(fi), rpmfiFSize(fi), Fileno(fd),
Florian Festi 94360a
+	       (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+    }
Florian Festi 94360a
+    Fclose(fd);
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files,
Florian Festi 94360a
-		     rpmpsm psm, int nodigest, int *setmeta,
Florian Festi 94360a
-		     int * firsthardlink, FD_t *firstlinkfile)
Florian Festi 94360a
+static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files,
Florian Festi 94360a
+		     rpmpsm psm, int nodigest,
Florian Festi 94360a
+		     struct filedata_s ** firstlink, int *firstlinkfile,
Florian Festi 94360a
+		     int *firstdir, int *fdp)
Florian Festi 94360a
 {
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
-    int numHardlinks = rpmfiFNlink(fi);
Florian Festi 94360a
+    int fd = -1;
Florian Festi 94360a
 
Florian Festi 94360a
-    if (numHardlinks > 1) {
Florian Festi 94360a
-	/* Create first hardlinked file empty */
Florian Festi 94360a
-	if (*firsthardlink < 0) {
Florian Festi 94360a
-	    *firsthardlink = rpmfiFX(fi);
Florian Festi 94360a
-	    rc = wfd_open(firstlinkfile, dest);
Florian Festi 94360a
-	} else {
Florian Festi 94360a
-	    /* Create hard links for others */
Florian Festi 94360a
-	    char *fn = rpmfilesFN(files, *firsthardlink);
Florian Festi 94360a
-	    rc = link(fn, dest);
Florian Festi 94360a
-	    if (rc < 0) {
Florian Festi 94360a
-		rc = RPMERR_LINK_FAILED;
Florian Festi 94360a
-	    }
Florian Festi 94360a
-	    free(fn);
Florian Festi 94360a
+    if (*firstlink == NULL) {
Florian Festi 94360a
+	/* First encounter, open file for writing */
Florian Festi 94360a
+	rc = fsmOpen(&fd, dirfd, fp->fpath);
Florian Festi 94360a
+	/* If it's a part of a hardlinked set, the content may come later */
Florian Festi 94360a
+	if (fp->sb.st_nlink > 1) {
Florian Festi 94360a
+	    *firstlink = fp;
Florian Festi 94360a
+	    *firstlinkfile = fd;
Florian Festi 94360a
+	    *firstdir = dup(dirfd);
Florian Festi 94360a
+	}
Florian Festi 94360a
+    } else {
Florian Festi 94360a
+	/* Create hard links for others and avoid redundant metadata setting */
Florian Festi 94360a
+	if (*firstlink != fp) {
Florian Festi 94360a
+	    rc = fsmLink(*firstdir, (*firstlink)->fpath, dirfd, fp->fpath);
Florian Festi 94360a
 	}
Florian Festi 94360a
+	fd = *firstlinkfile;
Florian Festi 94360a
     }
Florian Festi 94360a
-    /* Write normal files or fill the last hardlinked (already
Florian Festi 94360a
-       existing) file with content */
Florian Festi 94360a
-    if (numHardlinks<=1) {
Florian Festi 94360a
-	if (!rc)
Florian Festi 94360a
-	    rc = expandRegular(fi, dest, psm, nodigest);
Florian Festi 94360a
-    } else if (rpmfiArchiveHasContent(fi)) {
Florian Festi 94360a
+
Florian Festi 94360a
+    /* If the file has content, unpack it */
Florian Festi 94360a
+    if (rpmfiArchiveHasContent(fi)) {
Florian Festi 94360a
 	if (!rc)
Florian Festi 94360a
-	    rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm);
Florian Festi 94360a
-	wfd_close(firstlinkfile);
Florian Festi 94360a
-	*firsthardlink = -1;
Florian Festi 94360a
-    } else {
Florian Festi 94360a
-	*setmeta = 0;
Florian Festi 94360a
+	    rc = fsmUnpack(fi, fd, psm, nodigest);
Florian Festi 94360a
+	/* Last file of hardlink set, ensure metadata gets set */
Florian Festi 94360a
+	if (*firstlink) {
Florian Festi 94360a
+	    fp->setmeta = 1;
Florian Festi 94360a
+	    *firstlink = NULL;
Florian Festi 94360a
+	    *firstlinkfile = -1;
Florian Festi 94360a
+	    fsmClose(firstdir);
Florian Festi 94360a
+	}
Florian Festi 94360a
     }
Florian Festi 94360a
+    *fdp = fd;
Florian Festi 94360a
 
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
@@ -330,18 +256,15 @@
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmStat(const char *path, int dolstat, struct stat *sb)
Florian Festi 94360a
+static int fsmStat(int dirfd, const char *path, int dolstat, struct stat *sb)
Florian Festi 94360a
 {
Florian Festi 94360a
-    int rc;
Florian Festi 94360a
-    if (dolstat){
Florian Festi 94360a
-	rc = lstat(path, sb);
Florian Festi 94360a
-    } else {
Florian Festi 94360a
-        rc = stat(path, sb);
Florian Festi 94360a
-    }
Florian Festi 94360a
+    int flags = dolstat ? AT_SYMLINK_NOFOLLOW : 0;
Florian Festi 94360a
+    int rc = fstatat(dirfd, path, sb, flags);
Florian Festi 94360a
+
Florian Festi 94360a
     if (_fsm_debug && rc && errno != ENOENT)
Florian Festi 94360a
-        rpmlog(RPMLOG_DEBUG, " %8s (%s, ost) %s\n",
Florian Festi 94360a
+        rpmlog(RPMLOG_DEBUG, " %8s (%d %s, ost) %s\n",
Florian Festi 94360a
                __func__,
Florian Festi 94360a
-               path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+               dirfd, path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     if (rc < 0) {
Florian Festi 94360a
         rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_LSTAT_FAILED);
Florian Festi 94360a
 	/* Ensure consistent struct content on failure */
Florian Festi 94360a
@@ -350,12 +273,12 @@
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmRmdir(const char *path)
Florian Festi 94360a
+static int fsmRmdir(int dirfd, const char *path)
Florian Festi 94360a
 {
Florian Festi 94360a
-    int rc = rmdir(path);
Florian Festi 94360a
+    int rc = unlinkat(dirfd, path, AT_REMOVEDIR);
Florian Festi 94360a
     if (_fsm_debug)
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s) %s\n", __func__,
Florian Festi 94360a
-	       path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d %s) %s\n", __func__,
Florian Festi 94360a
+	       dirfd, path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     if (rc < 0)
Florian Festi 94360a
 	switch (errno) {
Florian Festi 94360a
 	case ENOENT:        rc = RPMERR_ENOENT;    break;
Florian Festi 94360a
@@ -365,148 +288,194 @@
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmMkdir(const char *path, mode_t mode)
Florian Festi 94360a
+static int fsmMkdir(int dirfd, const char *path, mode_t mode)
Florian Festi 94360a
 {
Florian Festi 94360a
-    int rc = mkdir(path, (mode & 07777));
Florian Festi 94360a
+    int rc = mkdirat(dirfd, path, (mode & 07777));
Florian Festi 94360a
     if (_fsm_debug)
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__,
Florian Festi 94360a
-	       path, (unsigned)(mode & 07777),
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d %s, 0%04o) %s\n", __func__,
Florian Festi 94360a
+	       dirfd, path, (unsigned)(mode & 07777),
Florian Festi 94360a
 	       (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     if (rc < 0)	rc = RPMERR_MKDIR_FAILED;
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmMkfifo(const char *path, mode_t mode)
Florian Festi 94360a
+static int fsmOpenat(int dirfd, const char *path, int flags, int dir)
Florian Festi 94360a
 {
Florian Festi 94360a
-    int rc = mkfifo(path, (mode & 07777));
Florian Festi 94360a
-
Florian Festi 94360a
-    if (_fsm_debug) {
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n",
Florian Festi 94360a
-	       __func__, path, (unsigned)(mode & 07777),
Florian Festi 94360a
-	       (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+    struct stat lsb, sb;
Florian Festi 94360a
+    int sflags = flags | O_NOFOLLOW;
Florian Festi 94360a
+    int fd = openat(dirfd, path, sflags);
Florian Festi 94360a
+
Florian Festi 94360a
+    /*
Florian Festi 94360a
+     * Only ever follow symlinks by root or target owner. Since we can't
Florian Festi 94360a
+     * open the symlink itself, the order matters: we stat the link *after*
Florian Festi 94360a
+     * opening the target, and if the link ownership changed between the calls
Florian Festi 94360a
+     * it could've only been the link owner or root.
Florian Festi 94360a
+     */
Florian Festi 94360a
+    if (fd < 0 && errno == ELOOP && flags != sflags) {
Florian Festi 94360a
+	int ffd = openat(dirfd, path, flags);
Florian Festi 94360a
+	if (ffd >= 0) {
Florian Festi 94360a
+	    if (fstatat(dirfd, path, &lsb, AT_SYMLINK_NOFOLLOW) == 0) {
Florian Festi 94360a
+		if (fstat(ffd, &sb) == 0) {
Florian Festi 94360a
+		    if (lsb.st_uid == 0 || lsb.st_uid == sb.st_uid) {
Florian Festi 94360a
+			fd = ffd;
Florian Festi 94360a
+		    }
Florian Festi 94360a
+		}
Florian Festi 94360a
+	    }
Florian Festi 94360a
+	    if (ffd != fd)
Florian Festi 94360a
+		close(ffd);
Florian Festi 94360a
+	}
Florian Festi 94360a
     }
Florian Festi 94360a
 
Florian Festi 94360a
-    if (rc < 0)
Florian Festi 94360a
-	rc = RPMERR_MKFIFO_FAILED;
Florian Festi 94360a
-
Florian Festi 94360a
-    return rc;
Florian Festi 94360a
+    /* O_DIRECTORY equivalent */
Florian Festi 94360a
+    if (dir && fd >= 0 && fstat(fd, &sb) == 0 && !S_ISDIR(sb.st_mode)) {
Florian Festi 94360a
+	errno = ENOTDIR;
Florian Festi 94360a
+	fsmClose(&fd;;
Florian Festi 94360a
+    }
Florian Festi 94360a
+    return fd;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmMknod(const char *path, mode_t mode, dev_t dev)
Florian Festi 94360a
+static int fsmDoMkDir(rpmPlugins plugins, int dirfd, const char *dn,
Florian Festi 94360a
+			const char *apath,
Florian Festi 94360a
+			int owned, mode_t mode, int *fdp)
Florian Festi 94360a
 {
Florian Festi 94360a
-    /* FIX: check S_IFIFO or dev != 0 */
Florian Festi 94360a
-    int rc = mknod(path, (mode & ~07777), dev);
Florian Festi 94360a
+    int rc;
Florian Festi 94360a
+    rpmFsmOp op = (FA_CREATE);
Florian Festi 94360a
+    if (!owned)
Florian Festi 94360a
+	op |= FAF_UNOWNED;
Florian Festi 94360a
 
Florian Festi 94360a
-    if (_fsm_debug) {
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%o, 0x%x) %s\n",
Florian Festi 94360a
-	       __func__, path, (unsigned)(mode & ~07777),
Florian Festi 94360a
-	       (unsigned)dev, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+    /* Run fsm file pre hook for all plugins */
Florian Festi 94360a
+    rc = rpmpluginsCallFsmFilePre(plugins, NULL, apath, mode, op);
Florian Festi 94360a
+
Florian Festi 94360a
+    if (!rc)
Florian Festi 94360a
+	rc = fsmMkdir(dirfd, dn, mode);
Florian Festi 94360a
+
Florian Festi 94360a
+    if (!rc) {
Florian Festi 94360a
+	*fdp = fsmOpenat(dirfd, dn, O_RDONLY|O_NOFOLLOW, 1);
Florian Festi 94360a
+	if (*fdp == -1)
Florian Festi 94360a
+	    rc = RPMERR_ENOTDIR;
Florian Festi 94360a
     }
Florian Festi 94360a
 
Florian Festi 94360a
-    if (rc < 0)
Florian Festi 94360a
-	rc = RPMERR_MKNOD_FAILED;
Florian Festi 94360a
+    if (!rc) {
Florian Festi 94360a
+	rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, *fdp, apath, apath, mode, op);
Florian Festi 94360a
+    }
Florian Festi 94360a
+
Florian Festi 94360a
+    /* Run fsm file post hook for all plugins */
Florian Festi 94360a
+    rpmpluginsCallFsmFilePost(plugins, NULL, apath, mode, op, rc);
Florian Festi 94360a
+
Florian Festi 94360a
+    if (!rc) {
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG,
Florian Festi 94360a
+		"%s directory created with perms %04o\n",
Florian Festi 94360a
+		apath, (unsigned)(mode & 07777));
Florian Festi 94360a
+    }
Florian Festi 94360a
 
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-/**
Florian Festi 94360a
- * Create (if necessary) directories not explicitly included in package.
Florian Festi 94360a
- * @param files		file data
Florian Festi 94360a
- * @param fs		file states
Florian Festi 94360a
- * @param plugins	rpm plugins handle
Florian Festi 94360a
- * @return		0 on success
Florian Festi 94360a
- */
Florian Festi 94360a
-static int fsmMkdirs(rpmfiles files, rpmfs fs, rpmPlugins plugins)
Florian Festi 94360a
+static int ensureDir(rpmPlugins plugins, const char *p, int owned, int create,
Florian Festi 94360a
+		    int quiet, int *dirfdp)
Florian Festi 94360a
 {
Florian Festi 94360a
-    DNLI_t dnli = dnlInitIterator(files, fs, 0);
Florian Festi 94360a
-    struct stat sb;
Florian Festi 94360a
-    const char *dpath;
Florian Festi 94360a
+    char *sp = NULL, *bn;
Florian Festi 94360a
+    char *apath = NULL;
Florian Festi 94360a
+    int oflags = O_RDONLY;
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
-    int i;
Florian Festi 94360a
-    size_t ldnlen = 0;
Florian Festi 94360a
-    const char * ldn = NULL;
Florian Festi 94360a
-
Florian Festi 94360a
-    while ((dpath = dnlNextIterator(dnli)) != NULL) {
Florian Festi 94360a
-	size_t dnlen = strlen(dpath);
Florian Festi 94360a
-	char * te, dn[dnlen+1];
Florian Festi 94360a
 
Florian Festi 94360a
-	if (dnlen <= 1)
Florian Festi 94360a
-	    continue;
Florian Festi 94360a
+    if (*dirfdp >= 0)
Florian Festi 94360a
+	return rc;
Florian Festi 94360a
 
Florian Festi 94360a
-	if (dnlen == ldnlen && rstreq(dpath, ldn))
Florian Festi 94360a
-	    continue;
Florian Festi 94360a
+    int dirfd = fsmOpenat(-1, "/", oflags, 1);
Florian Festi 94360a
+    int fd = dirfd; /* special case of "/" */
Florian Festi 94360a
 
Florian Festi 94360a
-	/* Copy as we need to modify the string */
Florian Festi 94360a
-	(void) stpcpy(dn, dpath);
Florian Festi 94360a
+    char *path = xstrdup(p);
Florian Festi 94360a
+    char *dp = path;
Florian Festi 94360a
 
Florian Festi 94360a
-	/* Assume '/' directory exists, "mkdir -p" for others if non-existent */
Florian Festi 94360a
-	for (i = 1, te = dn + 1; *te != '\0'; te++, i++) {
Florian Festi 94360a
-	    if (*te != '/')
Florian Festi 94360a
-		continue;
Florian Festi 94360a
+    while ((bn = strtok_r(dp, "/", &sp)) != NULL) {
Florian Festi 94360a
+	fd = fsmOpenat(dirfd, bn, oflags, 1);
Florian Festi 94360a
+	/* assemble absolute path for plugins benefit, sigh */
Florian Festi 94360a
+	apath = rstrscat(&apath, "/", bn, NULL);
Florian Festi 94360a
+
Florian Festi 94360a
+	if (fd < 0 && errno == ENOENT && create) {
Florian Festi 94360a
+	    mode_t mode = S_IFDIR | (_dirPerms & 07777);
Florian Festi 94360a
+	    rc = fsmDoMkDir(plugins, dirfd, bn, apath, owned, mode, &fd;;
Florian Festi 94360a
+	}
Florian Festi 94360a
 
Florian Festi 94360a
-	    /* Already validated? */
Florian Festi 94360a
-	    if (i < ldnlen &&
Florian Festi 94360a
-		(ldn[i] == '/' || ldn[i] == '\0') && rstreqn(dn, ldn, i))
Florian Festi 94360a
-		continue;
Florian Festi 94360a
+	fsmClose(&dirfd);
Florian Festi 94360a
+	if (fd >= 0) {
Florian Festi 94360a
+	    dirfd = fd;
Florian Festi 94360a
+	} else {
Florian Festi 94360a
+	    if (!quiet) {
Florian Festi 94360a
+		rpmlog(RPMLOG_ERR, _("failed to open dir %s of %s: %s\n"),
Florian Festi 94360a
+			bn, p, strerror(errno));
Florian Festi 94360a
+	    }
Florian Festi 94360a
+	    rc = RPMERR_OPEN_FAILED;
Florian Festi 94360a
+	    break;
Florian Festi 94360a
+	}
Florian Festi 94360a
 
Florian Festi 94360a
-	    /* Validate next component of path. */
Florian Festi 94360a
-	    *te = '\0';
Florian Festi 94360a
-	    rc = fsmStat(dn, 1, &sb); /* lstat */
Florian Festi 94360a
-	    *te = '/';
Florian Festi 94360a
+	dp = NULL;
Florian Festi 94360a
+    }
Florian Festi 94360a
 
Florian Festi 94360a
-	    /* Directory already exists? */
Florian Festi 94360a
-	    if (rc == 0 && S_ISDIR(sb.st_mode)) {
Florian Festi 94360a
-		continue;
Florian Festi 94360a
-	    } else if (rc == RPMERR_ENOENT) {
Florian Festi 94360a
-		*te = '\0';
Florian Festi 94360a
-		mode_t mode = S_IFDIR | (_dirPerms & 07777);
Florian Festi 94360a
-		rpmFsmOp op = (FA_CREATE|FAF_UNOWNED);
Florian Festi 94360a
-
Florian Festi 94360a
-		/* Run fsm file pre hook for all plugins */
Florian Festi 94360a
-		rc = rpmpluginsCallFsmFilePre(plugins, NULL, dn, mode, op);
Florian Festi 94360a
-
Florian Festi 94360a
-		if (!rc)
Florian Festi 94360a
-		    rc = fsmMkdir(dn, mode);
Florian Festi 94360a
-
Florian Festi 94360a
-		if (!rc) {
Florian Festi 94360a
-		    rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, dn, dn,
Florian Festi 94360a
-						      mode, op);
Florian Festi 94360a
-		}
Florian Festi 94360a
+    if (rc) {
Florian Festi 94360a
+	fsmClose(&fd;;
Florian Festi 94360a
+	fsmClose(&dirfd);
Florian Festi 94360a
+    } else {
Florian Festi 94360a
+	rc = 0;
Florian Festi 94360a
+    }
Florian Festi 94360a
+    *dirfdp = dirfd;
Florian Festi 94360a
 
Florian Festi 94360a
-		/* Run fsm file post hook for all plugins */
Florian Festi 94360a
-		rpmpluginsCallFsmFilePost(plugins, NULL, dn, mode, op, rc);
Florian Festi 94360a
+    if (_fsm_debug) {
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%s: %d) %s\n", __func__,
Florian Festi 94360a
+		p, dirfd, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+    }
Florian Festi 94360a
 
Florian Festi 94360a
-		if (!rc) {
Florian Festi 94360a
-		    rpmlog(RPMLOG_DEBUG,
Florian Festi 94360a
-			    "%s directory created with perms %04o\n",
Florian Festi 94360a
-			    dn, (unsigned)(mode & 07777));
Florian Festi 94360a
-		}
Florian Festi 94360a
-		*te = '/';
Florian Festi 94360a
-	    }
Florian Festi 94360a
-	    if (rc)
Florian Festi 94360a
-		break;
Florian Festi 94360a
-	}
Florian Festi 94360a
-	if (rc) break;
Florian Festi 94360a
+    free(path);
Florian Festi 94360a
+    free(apath);
Florian Festi 94360a
+    return rc;
Florian Festi 94360a
+}
Florian Festi 94360a
 
Florian Festi 94360a
-	/* Save last validated path. */
Florian Festi 94360a
-	ldn = dpath;
Florian Festi 94360a
-	ldnlen = dnlen;
Florian Festi 94360a
+static int fsmMkfifo(int dirfd, const char *path, mode_t mode)
Florian Festi 94360a
+{
Florian Festi 94360a
+    int rc = mkfifoat(dirfd, path, (mode & 07777));
Florian Festi 94360a
+
Florian Festi 94360a
+    if (_fsm_debug) {
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d %s, 0%04o) %s\n",
Florian Festi 94360a
+	       __func__, dirfd, path, (unsigned)(mode & 07777),
Florian Festi 94360a
+	       (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     }
Florian Festi 94360a
-    dnlFreeIterator(dnli);
Florian Festi 94360a
+
Florian Festi 94360a
+    if (rc < 0)
Florian Festi 94360a
+	rc = RPMERR_MKFIFO_FAILED;
Florian Festi 94360a
 
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static void removeSBITS(const char *path)
Florian Festi 94360a
+static int fsmMknod(int dirfd, const char *path, mode_t mode, dev_t dev)
Florian Festi 94360a
+{
Florian Festi 94360a
+    /* FIX: check S_IFIFO or dev != 0 */
Florian Festi 94360a
+    int rc = mknodat(dirfd, path, (mode & ~07777), dev);
Florian Festi 94360a
+
Florian Festi 94360a
+    if (_fsm_debug) {
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d %s, 0%o, 0x%x) %s\n",
Florian Festi 94360a
+	       __func__, dirfd, path, (unsigned)(mode & ~07777),
Florian Festi 94360a
+	       (unsigned)dev, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+    }
Florian Festi 94360a
+
Florian Festi 94360a
+    if (rc < 0)
Florian Festi 94360a
+	rc = RPMERR_MKNOD_FAILED;
Florian Festi 94360a
+
Florian Festi 94360a
+    return rc;
Florian Festi 94360a
+}
Florian Festi 94360a
+
Florian Festi 94360a
+static void removeSBITS(int dirfd, const char *path)
Florian Festi 94360a
 {
Florian Festi 94360a
     struct stat stb;
Florian Festi 94360a
-    if (lstat(path, &stb) == 0 && S_ISREG(stb.st_mode)) {
Florian Festi 94360a
+    int flags = AT_SYMLINK_NOFOLLOW;
Florian Festi 94360a
+    if (fstatat(dirfd, path, &stb, flags) == 0 && S_ISREG(stb.st_mode)) {
Florian Festi 94360a
+    	/* We now know it's not a link so no need to worry about following */
Florian Festi 94360a
 	if ((stb.st_mode & 06000) != 0) {
Florian Festi 94360a
-	    (void) chmod(path, stb.st_mode & 0777);
Florian Festi 94360a
+	    (void) fchmodat(dirfd, path, stb.st_mode & 0777, 0);
Florian Festi 94360a
 	}
Florian Festi 94360a
-#if WITH_CAP
Florian Festi 94360a
+#ifdef WITH_CAP
Florian Festi 94360a
 	if (stb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) {
Florian Festi 94360a
-	    (void) cap_set_file(path, NULL);
Florian Festi 94360a
+	    (void) cap_set_fileat(dirfd, path, NULL);
Florian Festi 94360a
 	}
Florian Festi 94360a
 #endif
Florian Festi 94360a
     }
Florian Festi 94360a
@@ -522,13 +491,13 @@
Florian Festi 94360a
 	    (fpath ? fpath : ""));
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmSymlink(const char *opath, const char *path)
Florian Festi 94360a
+static int fsmSymlink(const char *opath, int dirfd, const char *path)
Florian Festi 94360a
 {
Florian Festi 94360a
-    int rc = symlink(opath, path);
Florian Festi 94360a
+    int rc = symlinkat(opath, dirfd, path);
Florian Festi 94360a
 
Florian Festi 94360a
     if (_fsm_debug) {
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__,
Florian Festi 94360a
-	       opath, path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%s, %d %s) %s\n", __func__,
Florian Festi 94360a
+	       opath, dirfd, path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     }
Florian Festi 94360a
 
Florian Festi 94360a
     if (rc < 0)
Florian Festi 94360a
@@ -536,96 +505,125 @@
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmUnlink(const char *path)
Florian Festi 94360a
+static int fsmUnlink(int dirfd, const char *path)
Florian Festi 94360a
 {
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
-    removeSBITS(path);
Florian Festi 94360a
-    rc = unlink(path);
Florian Festi 94360a
+    removeSBITS(dirfd, path);
Florian Festi 94360a
+    rc = unlinkat(dirfd, path, 0);
Florian Festi 94360a
     if (_fsm_debug)
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s) %s\n", __func__,
Florian Festi 94360a
-	       path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d %s) %s\n", __func__,
Florian Festi 94360a
+	       dirfd, path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     if (rc < 0)
Florian Festi 94360a
 	rc = (errno == ENOENT ? RPMERR_ENOENT : RPMERR_UNLINK_FAILED);
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmRename(const char *opath, const char *path)
Florian Festi 94360a
+static int fsmRename(int odirfd, const char *opath, int dirfd, const char *path)
Florian Festi 94360a
 {
Florian Festi 94360a
-    removeSBITS(path);
Florian Festi 94360a
-    int rc = rename(opath, path);
Florian Festi 94360a
+    removeSBITS(dirfd, path);
Florian Festi 94360a
+    int rc = renameat(odirfd, opath, dirfd, path);
Florian Festi 94360a
 #if defined(ETXTBSY) && defined(__HPUX__)
Florian Festi 94360a
     /* XXX HP-UX (and other os'es) don't permit rename to busy files. */
Florian Festi 94360a
     if (rc && errno == ETXTBSY) {
Florian Festi 94360a
 	char *rmpath = NULL;
Florian Festi 94360a
 	rstrscat(&rmpath, path, "-RPMDELETE", NULL);
Florian Festi 94360a
-	rc = rename(path, rmpath);
Florian Festi 94360a
-	if (!rc) rc = rename(opath, path);
Florian Festi 94360a
+	/* Rename within the original directory */
Florian Festi 94360a
+	rc = renameat(odirfd, path, odirfd, rmpath);
Florian Festi 94360a
+	if (!rc) rc = renameat(odirfd, opath, dirfd, path);
Florian Festi 94360a
 	free(rmpath);
Florian Festi 94360a
     }
Florian Festi 94360a
 #endif
Florian Festi 94360a
     if (_fsm_debug)
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__,
Florian Festi 94360a
-	       opath, path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d %s, %d %s) %s\n", __func__,
Florian Festi 94360a
+	       odirfd, opath, dirfd, path, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     if (rc < 0)
Florian Festi 94360a
 	rc = (errno == EISDIR ? RPMERR_EXIST_AS_DIR : RPMERR_RENAME_FAILED);
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmRemove(const char *path, mode_t mode)
Florian Festi 94360a
+static int fsmRemove(int dirfd, const char *path, mode_t mode)
Florian Festi 94360a
 {
Florian Festi 94360a
-    return S_ISDIR(mode) ? fsmRmdir(path) : fsmUnlink(path);
Florian Festi 94360a
+    return S_ISDIR(mode) ? fsmRmdir(dirfd, path) : fsmUnlink(dirfd, path);
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
Florian Festi 94360a
+static int fsmChown(int fd, int dirfd, const char *path, mode_t mode, uid_t uid, gid_t gid)
Florian Festi 94360a
 {
Florian Festi 94360a
-    int rc = S_ISLNK(mode) ? lchown(path, uid, gid) : chown(path, uid, gid);
Florian Festi 94360a
-    if (rc < 0) {
Florian Festi 94360a
-	struct stat st;
Florian Festi 94360a
-	if (lstat(path, &st) == 0 && st.st_uid == uid && st.st_gid == gid)
Florian Festi 94360a
-	    rc = 0;
Florian Festi 94360a
+    int rc;
Florian Festi 94360a
+    struct stat st;
Florian Festi 94360a
+
Florian Festi 94360a
+    if (fd >= 0) {
Florian Festi 94360a
+	rc = fchown(fd, uid, gid);
Florian Festi 94360a
+	if (rc < 0) {
Florian Festi 94360a
+	    if (fstat(fd, &st) == 0 && (st.st_uid == uid && st.st_gid == gid)) {
Florian Festi 94360a
+		rc = 0;
Florian Festi 94360a
+	    }
Florian Festi 94360a
+	}
Florian Festi 94360a
+    } else {
Florian Festi 94360a
+	int flags = AT_SYMLINK_NOFOLLOW;
Florian Festi 94360a
+	rc = fchownat(dirfd, path, uid, gid, flags);
Florian Festi 94360a
+	if (rc < 0) {
Florian Festi 94360a
+	    struct stat st;
Florian Festi 94360a
+	    if (fstatat(dirfd, path, &st, flags) == 0 &&
Florian Festi 94360a
+		    (st.st_uid == uid && st.st_gid == gid)) {
Florian Festi 94360a
+		rc = 0;
Florian Festi 94360a
+	    }
Florian Festi 94360a
+	}
Florian Festi 94360a
     }
Florian Festi 94360a
-    if (_fsm_debug)
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s, %d, %d) %s\n", __func__,
Florian Festi 94360a
-	       path, (int)uid, (int)gid,
Florian Festi 94360a
+    if (_fsm_debug) {
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d - %d %s, %d, %d) %s\n", __func__,
Florian Festi 94360a
+	       fd, dirfd, path, (int)uid, (int)gid,
Florian Festi 94360a
 	       (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+    }
Florian Festi 94360a
     if (rc < 0)	rc = RPMERR_CHOWN_FAILED;
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmChmod(const char *path, mode_t mode)
Florian Festi 94360a
+static int fsmChmod(int fd, int dirfd, const char *path, mode_t mode)
Florian Festi 94360a
 {
Florian Festi 94360a
-    int rc = chmod(path, (mode & 07777));
Florian Festi 94360a
-    if (rc < 0) {
Florian Festi 94360a
-	struct stat st;
Florian Festi 94360a
-	if (lstat(path, &st) == 0 && (st.st_mode & 07777) == (mode & 07777))
Florian Festi 94360a
-	    rc = 0;
Florian Festi 94360a
+    mode_t fmode = (mode & 07777);
Florian Festi 94360a
+    int rc;
Florian Festi 94360a
+    if (fd >= 0) {
Florian Festi 94360a
+	rc = fchmod(fd, fmode);
Florian Festi 94360a
+	if (rc < 0) {
Florian Festi 94360a
+	    struct stat st;
Florian Festi 94360a
+	    if (fstat(fd, &st) == 0 && (st.st_mode & 07777) == fmode) {
Florian Festi 94360a
+		rc = 0;
Florian Festi 94360a
+	    }
Florian Festi 94360a
+	}
Florian Festi 94360a
+    } else {
Florian Festi 94360a
+	rc = fchmodat(dirfd, path, fmode, 0);
Florian Festi 94360a
+	if (rc < 0) {
Florian Festi 94360a
+	    struct stat st;
Florian Festi 94360a
+	    if (fstatat(dirfd, path, &st, AT_SYMLINK_NOFOLLOW) == 0 &&
Florian Festi 94360a
+		    (st.st_mode & 07777) == fmode) {
Florian Festi 94360a
+		rc = 0;
Florian Festi 94360a
+	    }
Florian Festi 94360a
+	}
Florian Festi 94360a
     }
Florian Festi 94360a
     if (_fsm_debug)
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__,
Florian Festi 94360a
-	       path, (unsigned)(mode & 07777),
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d - %d %s, 0%04o) %s\n", __func__,
Florian Festi 94360a
+	       fd, dirfd, path, (unsigned)(mode & 07777),
Florian Festi 94360a
 	       (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     if (rc < 0)	rc = RPMERR_CHMOD_FAILED;
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmUtime(const char *path, mode_t mode, time_t mtime)
Florian Festi 94360a
+static int fsmUtime(int fd, int dirfd, const char *path, mode_t mode, time_t mtime)
Florian Festi 94360a
 {
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
-    struct timeval stamps[2] = {
Florian Festi 94360a
-	{ .tv_sec = mtime, .tv_usec = 0 },
Florian Festi 94360a
-	{ .tv_sec = mtime, .tv_usec = 0 },
Florian Festi 94360a
+    struct timespec stamps[2] = {
Florian Festi 94360a
+	{ .tv_sec = mtime, .tv_nsec = 0 },
Florian Festi 94360a
+	{ .tv_sec = mtime, .tv_nsec = 0 },
Florian Festi 94360a
     };
Florian Festi 94360a
 
Florian Festi 94360a
-#if HAVE_LUTIMES
Florian Festi 94360a
-    rc = lutimes(path, stamps);
Florian Festi 94360a
-#else
Florian Festi 94360a
-    if (!S_ISLNK(mode))
Florian Festi 94360a
-	rc = utimes(path, stamps);
Florian Festi 94360a
-#endif
Florian Festi 94360a
+    if (fd >= 0)
Florian Festi 94360a
+	rc = futimens(fd, stamps);
Florian Festi 94360a
+    else
Florian Festi 94360a
+	rc = utimensat(dirfd, path, stamps, AT_SYMLINK_NOFOLLOW);
Florian Festi 94360a
     
Florian Festi 94360a
     if (_fsm_debug)
Florian Festi 94360a
-	rpmlog(RPMLOG_DEBUG, " %8s (%s, 0x%x) %s\n", __func__,
Florian Festi 94360a
-	       path, (unsigned)mtime, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
+	rpmlog(RPMLOG_DEBUG, " %8s (%d - %d %s, 0x%x) %s\n", __func__,
Florian Festi 94360a
+	       fd, dirfd, path, (unsigned)mtime, (rc < 0 ? strerror(errno) : ""));
Florian Festi 94360a
     if (rc < 0)	rc = RPMERR_UTIME_FAILED;
Florian Festi 94360a
     /* ...but utime error is not critical for directories */
Florian Festi 94360a
     if (rc && S_ISDIR(mode))
Florian Festi 94360a
@@ -633,24 +631,24 @@
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmVerify(const char *path, rpmfi fi)
Florian Festi 94360a
+static int fsmVerify(int dirfd, const char *path, rpmfi fi)
Florian Festi 94360a
 {
Florian Festi 94360a
     int rc;
Florian Festi 94360a
     int saveerrno = errno;
Florian Festi 94360a
     struct stat dsb;
Florian Festi 94360a
     mode_t mode = rpmfiFMode(fi);
Florian Festi 94360a
 
Florian Festi 94360a
-    rc = fsmStat(path, 1, &dsb;;
Florian Festi 94360a
+    rc = fsmStat(dirfd, path, 1, &dsb;;
Florian Festi 94360a
     if (rc)
Florian Festi 94360a
 	return rc;
Florian Festi 94360a
 
Florian Festi 94360a
     if (S_ISREG(mode)) {
Florian Festi 94360a
 	/* HP-UX (and other os'es) don't permit unlink on busy files. */
Florian Festi 94360a
 	char *rmpath = rstrscat(NULL, path, "-RPMDELETE", NULL);
Florian Festi 94360a
-	rc = fsmRename(path, rmpath);
Florian Festi 94360a
+	rc = fsmRename(dirfd, path, dirfd, rmpath);
Florian Festi 94360a
 	/* XXX shouldn't we take unlink return code here? */
Florian Festi 94360a
 	if (!rc)
Florian Festi 94360a
-	    (void) fsmUnlink(rmpath);
Florian Festi 94360a
+	    (void) fsmUnlink(dirfd, rmpath);
Florian Festi 94360a
 	else
Florian Festi 94360a
 	    rc = RPMERR_UNLINK_FAILED;
Florian Festi 94360a
 	free(rmpath);
Florian Festi 94360a
@@ -659,7 +657,7 @@
Florian Festi 94360a
         if (S_ISDIR(dsb.st_mode)) return 0;
Florian Festi 94360a
         if (S_ISLNK(dsb.st_mode)) {
Florian Festi 94360a
 	    uid_t luid = dsb.st_uid;
Florian Festi 94360a
-            rc = fsmStat(path, 0, &dsb;;
Florian Festi 94360a
+            rc = fsmStat(dirfd, path, 0, &dsb;;
Florian Festi 94360a
             if (rc == RPMERR_ENOENT) rc = 0;
Florian Festi 94360a
             if (rc) return rc;
Florian Festi 94360a
             errno = saveerrno;
Florian Festi 94360a
@@ -685,7 +683,7 @@
Florian Festi 94360a
         if (S_ISSOCK(dsb.st_mode)) return 0;
Florian Festi 94360a
     }
Florian Festi 94360a
     /* XXX shouldn't do this with commit/undo. */
Florian Festi 94360a
-    rc = fsmUnlink(path);
Florian Festi 94360a
+    rc = fsmUnlink(dirfd, path);
Florian Festi 94360a
     if (rc == 0)	rc = RPMERR_ENOENT;
Florian Festi 94360a
     return (rc ? rc : RPMERR_ENOENT);	/* XXX HACK */
Florian Festi 94360a
 }
Florian Festi 94360a
@@ -699,7 +697,7 @@
Florian Festi 94360a
 
Florian Festi 94360a
 
Florian Festi 94360a
 /* Rename pre-existing modified or unmanaged file. */
Florian Festi 94360a
-static int fsmBackup(rpmfi fi, rpmFileAction action)
Florian Festi 94360a
+static int fsmBackup(int dirfd, rpmfi fi, rpmFileAction action)
Florian Festi 94360a
 {
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
     const char *suffix = NULL;
Florian Festi 94360a
@@ -720,9 +718,10 @@
Florian Festi 94360a
     if (suffix) {
Florian Festi 94360a
 	char * opath = fsmFsPath(fi, NULL);
Florian Festi 94360a
 	char * path = fsmFsPath(fi, suffix);
Florian Festi 94360a
-	rc = fsmRename(opath, path);
Florian Festi 94360a
+	rc = fsmRename(dirfd, opath, dirfd, path);
Florian Festi 94360a
 	if (!rc) {
Florian Festi 94360a
-	    rpmlog(RPMLOG_WARNING, _("%s saved as %s\n"), opath, path);
Florian Festi 94360a
+	    rpmlog(RPMLOG_WARNING, _("%s%s saved as %s%s\n"),
Florian Festi 94360a
+		   rpmfiDN(fi), opath, rpmfiDN(fi), path);
Florian Festi 94360a
 	}
Florian Festi 94360a
 	free(path);
Florian Festi 94360a
 	free(opath);
Florian Festi 94360a
@@ -730,7 +729,8 @@
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmSetmeta(const char *path, rpmfi fi, rpmPlugins plugins,
Florian Festi 94360a
+static int fsmSetmeta(int fd, int dirfd, const char *path,
Florian Festi 94360a
+		      rpmfi fi, rpmPlugins plugins,
Florian Festi 94360a
 		      rpmFileAction action, const struct stat * st,
Florian Festi 94360a
 		      int nofcaps)
Florian Festi 94360a
 {
Florian Festi 94360a
@@ -738,27 +738,28 @@
Florian Festi 94360a
     const char *dest = rpmfiFN(fi);
Florian Festi 94360a
 
Florian Festi 94360a
     if (!rc && !getuid()) {
Florian Festi 94360a
-	rc = fsmChown(path, st->st_mode, st->st_uid, st->st_gid);
Florian Festi 94360a
+	rc = fsmChown(fd, dirfd, path, st->st_mode, st->st_uid, st->st_gid);
Florian Festi 94360a
     }
Florian Festi 94360a
     if (!rc && !S_ISLNK(st->st_mode)) {
Florian Festi 94360a
-	rc = fsmChmod(path, st->st_mode);
Florian Festi 94360a
+	rc = fsmChmod(fd, dirfd, path, st->st_mode);
Florian Festi 94360a
     }
Florian Festi 94360a
     /* Set file capabilities (if enabled) */
Florian Festi 94360a
     if (!rc && !nofcaps && S_ISREG(st->st_mode) && !getuid()) {
Florian Festi 94360a
-	rc = fsmSetFCaps(path, rpmfiFCaps(fi));
Florian Festi 94360a
+	rc = fsmSetFCaps(fd, dirfd, path, rpmfiFCaps(fi));
Florian Festi 94360a
     }
Florian Festi 94360a
     if (!rc) {
Florian Festi 94360a
-	rc = fsmUtime(path, st->st_mode, rpmfiFMtime(fi));
Florian Festi 94360a
+	rc = fsmUtime(fd, dirfd, path, st->st_mode, rpmfiFMtime(fi));
Florian Festi 94360a
     }
Florian Festi 94360a
     if (!rc) {
Florian Festi 94360a
 	rc = rpmpluginsCallFsmFilePrepare(plugins, fi,
Florian Festi 94360a
-					  path, dest, st->st_mode, action);
Florian Festi 94360a
+					  fd, path, dest,
Florian Festi 94360a
+					  st->st_mode, action);
Florian Festi 94360a
     }
Florian Festi 94360a
 
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
-static int fsmCommit(char **path, rpmfi fi, rpmFileAction action, const char *suffix)
Florian Festi 94360a
+static int fsmCommit(int dirfd, char **path, rpmfi fi, rpmFileAction action, const char *suffix)
Florian Festi 94360a
 {
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
 
Florian Festi 94360a
@@ -772,15 +773,18 @@
Florian Festi 94360a
 
Florian Festi 94360a
 	/* Rename temporary to final file name if needed. */
Florian Festi 94360a
 	if (dest != *path) {
Florian Festi 94360a
-	    rc = fsmRename(*path, dest);
Florian Festi 94360a
-	    if (!rc && nsuffix) {
Florian Festi 94360a
-		char * opath = fsmFsPath(fi, NULL);
Florian Festi 94360a
-		rpmlog(RPMLOG_WARNING, _("%s created as %s\n"),
Florian Festi 94360a
-		       opath, dest);
Florian Festi 94360a
-		free(opath);
Florian Festi 94360a
-	    }
Florian Festi 94360a
-	    free(*path);
Florian Festi 94360a
-	    *path = dest;
Florian Festi 94360a
+	    rc = fsmRename(dirfd, *path, dirfd, dest);
Florian Festi 94360a
+	    if (!rc) {
Florian Festi 94360a
+		if (nsuffix) {
Florian Festi 94360a
+		    char * opath = fsmFsPath(fi, NULL);
Florian Festi 94360a
+		    rpmlog(RPMLOG_WARNING, _("%s%s created as %s%s\n"),
Florian Festi 94360a
+			   rpmfiDN(fi), opath, rpmfiDN(fi), dest);
Florian Festi 94360a
+		    free(opath);
Florian Festi 94360a
+		}
Florian Festi 94360a
+		free(*path);
Florian Festi 94360a
+		*path = dest;
Florian Festi 94360a
+	    } else
Florian Festi 94360a
+		free(dest);
Florian Festi 94360a
 	}
Florian Festi 94360a
     }
Florian Festi 94360a
 
Florian Festi 94360a
@@ -831,191 +835,277 @@
Florian Festi 94360a
     }
Florian Festi 94360a
 }
Florian Festi 94360a
 
Florian Festi 94360a
+struct diriter_s {
Florian Festi 94360a
+    int dirfd;
Florian Festi 94360a
+    int firstdir;
Florian Festi 94360a
+};
Florian Festi 94360a
+
Florian Festi 94360a
+static int onChdir(rpmfi fi, void *data)
Florian Festi 94360a
+{
Florian Festi 94360a
+    struct diriter_s *di = data;
Florian Festi 94360a
+
Florian Festi 94360a
+    fsmClose(&(di->dirfd));
Florian Festi 94360a
+    return 0;
Florian Festi 94360a
+}
Florian Festi 94360a
+
Florian Festi 94360a
+static rpmfi fsmIter(FD_t payload, rpmfiles files, rpmFileIter iter, void *data)
Florian Festi 94360a
+{
Florian Festi 94360a
+    rpmfi fi;
Florian Festi 94360a
+    if (payload)
Florian Festi 94360a
+	fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
Florian Festi 94360a
+    else
Florian Festi 94360a
+	fi = rpmfilesIter(files, iter);
Florian Festi 94360a
+    if (fi && data)
Florian Festi 94360a
+	rpmfiSetOnChdir(fi, onChdir, data);
Florian Festi 94360a
+    return fi;
Florian Festi 94360a
+}
Florian Festi 94360a
+
Florian Festi 94360a
+static rpmfi fsmIterFini(rpmfi fi, struct diriter_s *di)
Florian Festi 94360a
+{
Florian Festi 94360a
+    fsmClose(&(di->dirfd));
Florian Festi 94360a
+    fsmClose(&(di->firstdir));
Florian Festi 94360a
+    return rpmfiFree(fi);
Florian Festi 94360a
+}
Florian Festi 94360a
+
Florian Festi 94360a
 int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
Florian Festi 94360a
               rpmpsm psm, char ** failedFile)
Florian Festi 94360a
 {
Florian Festi 94360a
     FD_t payload = rpmtePayload(te);
Florian Festi 94360a
-    rpmfi fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
Florian Festi 94360a
+    rpmfi fi = NULL;
Florian Festi 94360a
     rpmfs fs = rpmteGetFileStates(te);
Florian Festi 94360a
     rpmPlugins plugins = rpmtsPlugins(ts);
Florian Festi 94360a
-    struct stat sb;
Florian Festi 94360a
-    int saveerrno = errno;
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
+    int fx = -1;
Florian Festi 94360a
+    int fc = rpmfilesFC(files);
Florian Festi 94360a
     int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0;
Florian Festi 94360a
     int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0;
Florian Festi 94360a
-    int firsthardlink = -1;
Florian Festi 94360a
-    FD_t firstlinkfile = NULL;
Florian Festi 94360a
-    int skip;
Florian Festi 94360a
-    rpmFileAction action;
Florian Festi 94360a
+    int firstlinkfile = -1;
Florian Festi 94360a
     char *tid = NULL;
Florian Festi 94360a
-    const char *suffix;
Florian Festi 94360a
-    char *fpath = NULL;
Florian Festi 94360a
-
Florian Festi 94360a
-    if (fi == NULL) {
Florian Festi 94360a
-	rc = RPMERR_BAD_MAGIC;
Florian Festi 94360a
-	goto exit;
Florian Festi 94360a
-    }
Florian Festi 94360a
+    struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata));
Florian Festi 94360a
+    struct filedata_s *firstlink = NULL;
Florian Festi 94360a
+    struct diriter_s di = { -1, -1 };
Florian Festi 94360a
 
Florian Festi 94360a
     /* transaction id used for temporary path suffix while installing */
Florian Festi 94360a
     rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));
Florian Festi 94360a
 
Florian Festi 94360a
-    /* Detect and create directories not explicitly in package. */
Florian Festi 94360a
-    rc = fsmMkdirs(files, fs, plugins);
Florian Festi 94360a
+    /* Collect state data for the whole operation */
Florian Festi 94360a
+    fi = rpmfilesIter(files, RPMFI_ITER_FWD);
Florian Festi 94360a
+    while (!rc && (fx = rpmfiNext(fi)) >= 0) {
Florian Festi 94360a
+	struct filedata_s *fp = &fdata[fx];
Florian Festi 94360a
+	if (rpmfiFFlags(fi) & RPMFILE_GHOST)
Florian Festi 94360a
+            fp->action = FA_SKIP;
Florian Festi 94360a
+	else
Florian Festi 94360a
+	    fp->action = rpmfsGetAction(fs, fx);
Florian Festi 94360a
+	fp->skip = XFA_SKIPPING(fp->action);
Florian Festi 94360a
+	if (XFA_CREATING(fp->action) && !S_ISDIR(rpmfiFMode(fi)))
Florian Festi 94360a
+	    fp->suffix = tid;
Florian Festi 94360a
+	fp->fpath = fsmFsPath(fi, fp->suffix);
Florian Festi 94360a
 
Florian Festi 94360a
-    while (!rc) {
Florian Festi 94360a
-	/* Read next payload header. */
Florian Festi 94360a
-	rc = rpmfiNext(fi);
Florian Festi 94360a
+	/* Remap file perms, owner, and group. */
Florian Festi 94360a
+	rc = rpmfiStat(fi, 1, &fp->sb);
Florian Festi 94360a
 
Florian Festi 94360a
-	if (rc < 0) {
Florian Festi 94360a
-	    if (rc == RPMERR_ITER_END)
Florian Festi 94360a
-		rc = 0;
Florian Festi 94360a
-	    break;
Florian Festi 94360a
-	}
Florian Festi 94360a
+	/* Hardlinks are tricky and handled elsewhere for install */
Florian Festi 94360a
+	fp->setmeta = (fp->skip == 0) &&
Florian Festi 94360a
+		      (fp->sb.st_nlink == 1 || fp->action == FA_TOUCH);
Florian Festi 94360a
 
Florian Festi 94360a
-	action = rpmfsGetAction(fs, rpmfiFX(fi));
Florian Festi 94360a
-	skip = XFA_SKIPPING(action);
Florian Festi 94360a
-	if (action != FA_TOUCH) {
Florian Festi 94360a
-	    suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid;
Florian Festi 94360a
-	} else {
Florian Festi 94360a
-	    suffix = NULL;
Florian Festi 94360a
-	}
Florian Festi 94360a
-	fpath = fsmFsPath(fi, suffix);
Florian Festi 94360a
+	setFileState(fs, fx);
Florian Festi 94360a
+	fsmDebug(fp->fpath, fp->action, &fp->sb);
Florian Festi 94360a
 
Florian Festi 94360a
-	/* Remap file perms, owner, and group. */
Florian Festi 94360a
-	rc = rpmfiStat(fi, 1, &sb);
Florian Festi 94360a
+	fp->stage = FILE_PRE;
Florian Festi 94360a
+    }
Florian Festi 94360a
+    fi = rpmfiFree(fi);
Florian Festi 94360a
 
Florian Festi 94360a
-	fsmDebug(fpath, action, &sb);
Florian Festi 94360a
+    if (rc)
Florian Festi 94360a
+	goto exit;
Florian Festi 94360a
 
Florian Festi 94360a
-        /* Exit on error. */
Florian Festi 94360a
-        if (rc)
Florian Festi 94360a
-            break;
Florian Festi 94360a
+    fi = fsmIter(payload, files,
Florian Festi 94360a
+		 payload ? RPMFI_ITER_READ_ARCHIVE : RPMFI_ITER_FWD, &di);
Florian Festi 94360a
 
Florian Festi 94360a
-	/* Run fsm file pre hook for all plugins */
Florian Festi 94360a
-	rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
Florian Festi 94360a
-				      sb.st_mode, action);
Florian Festi 94360a
-	if (rc) {
Florian Festi 94360a
-	    skip = 1;
Florian Festi 94360a
-	} else {
Florian Festi 94360a
-	    setFileState(fs, rpmfiFX(fi));
Florian Festi 94360a
-	}
Florian Festi 94360a
+    if (fi == NULL) {
Florian Festi 94360a
+        rc = RPMERR_BAD_MAGIC;
Florian Festi 94360a
+        goto exit;
Florian Festi 94360a
+    }
Florian Festi 94360a
 
Florian Festi 94360a
-        if (!skip) {
Florian Festi 94360a
-	    int setmeta = 1;
Florian Festi 94360a
+    /* Process the payload */
Florian Festi 94360a
+    while (!rc && (fx = rpmfiNext(fi)) >= 0) {
Florian Festi 94360a
+	struct filedata_s *fp = &fdata[fx];
Florian Festi 94360a
+
Florian Festi 94360a
+	/*
Florian Festi 94360a
+	 * Tricksy case: this file is a being skipped, but it's part of
Florian Festi 94360a
+	 * a hardlinked set and has the actual content linked with it.
Florian Festi 94360a
+	 * Write the content to the first non-skipped file of the set
Florian Festi 94360a
+	 * instead.
Florian Festi 94360a
+	 */
Florian Festi 94360a
+	if (fp->skip && firstlink && rpmfiArchiveHasContent(fi))
Florian Festi 94360a
+	    fp = firstlink;
Florian Festi 94360a
+
Florian Festi 94360a
+        if (!fp->skip) {
Florian Festi 94360a
+	    int mayopen = 0;
Florian Festi 94360a
+	    int fd = -1;
Florian Festi 94360a
+	    rc = ensureDir(plugins, rpmfiDN(fi), 0,
Florian Festi 94360a
+			    (fp->action == FA_CREATE), 0, &di.dirfd);
Florian Festi 94360a
 
Florian Festi 94360a
 	    /* Directories replacing something need early backup */
Florian Festi 94360a
-	    if (!suffix) {
Florian Festi 94360a
-		rc = fsmBackup(fi, action);
Florian Festi 94360a
+	    if (!rc && !fp->suffix && fp != firstlink) {
Florian Festi 94360a
+		rc = fsmBackup(di.dirfd, fi, fp->action);
Florian Festi 94360a
 	    }
Florian Festi 94360a
+
Florian Festi 94360a
+	    /* Run fsm file pre hook for all plugins */
Florian Festi 94360a
+	    if (!rc)
Florian Festi 94360a
+		rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath,
Florian Festi 94360a
+					      fp->sb.st_mode, fp->action);
Florian Festi 94360a
+	    if (rc)
Florian Festi 94360a
+		goto setmeta; /* for error notification */
Florian Festi 94360a
+
Florian Festi 94360a
 	    /* Assume file does't exist when tmp suffix is in use */
Florian Festi 94360a
-	    if (!suffix) {
Florian Festi 94360a
-		rc = fsmVerify(fpath, fi);
Florian Festi 94360a
+	    if (!fp->suffix) {
Florian Festi 94360a
+		if (fp->action == FA_TOUCH) {
Florian Festi 94360a
+		    struct stat sb;
Florian Festi 94360a
+		    rc = fsmStat(di.dirfd, fp->fpath, 1, &sb);
Florian Festi 94360a
+		} else {
Florian Festi 94360a
+		    rc = fsmVerify(di.dirfd, fp->fpath, fi);
Florian Festi 94360a
+		}
Florian Festi 94360a
 	    } else {
Florian Festi 94360a
 		rc = RPMERR_ENOENT;
Florian Festi 94360a
 	    }
Florian Festi 94360a
 
Florian Festi 94360a
 	    /* See if the file was removed while our attention was elsewhere */
Florian Festi 94360a
-	    if (rc == RPMERR_ENOENT && action == FA_TOUCH) {
Florian Festi 94360a
-		rpmlog(RPMLOG_DEBUG, "file %s vanished unexpectedly\n", fpath);
Florian Festi 94360a
-		action = FA_CREATE;
Florian Festi 94360a
-		fsmDebug(fpath, action, &sb);
Florian Festi 94360a
+	    if (rc == RPMERR_ENOENT && fp->action == FA_TOUCH) {
Florian Festi 94360a
+		rpmlog(RPMLOG_DEBUG, "file %s vanished unexpectedly\n",
Florian Festi 94360a
+			fp->fpath);
Florian Festi 94360a
+		fp->action = FA_CREATE;
Florian Festi 94360a
+		fsmDebug(fp->fpath, fp->action, &fp->sb);
Florian Festi 94360a
 	    }
Florian Festi 94360a
 
Florian Festi 94360a
 	    /* When touching we don't need any of this... */
Florian Festi 94360a
-	    if (action == FA_TOUCH)
Florian Festi 94360a
-		goto touch;
Florian Festi 94360a
+	    if (fp->action == FA_TOUCH)
Florian Festi 94360a
+		goto setmeta;
Florian Festi 94360a
 
Florian Festi 94360a
-            if (S_ISREG(sb.st_mode)) {
Florian Festi 94360a
+            if (S_ISREG(fp->sb.st_mode)) {
Florian Festi 94360a
 		if (rc == RPMERR_ENOENT) {
Florian Festi 94360a
-		    rc = fsmMkfile(fi, fpath, files, psm, nodigest,
Florian Festi 94360a
-				   &setmeta, &firsthardlink, &firstlinkfile);
Florian Festi 94360a
+		    rc = fsmMkfile(di.dirfd, fi, fp, files, psm, nodigest,
Florian Festi 94360a
+				   &firstlink, &firstlinkfile, &di.firstdir,
Florian Festi 94360a
+				   &fd;;
Florian Festi 94360a
 		}
Florian Festi 94360a
-            } else if (S_ISDIR(sb.st_mode)) {
Florian Festi 94360a
+            } else if (S_ISDIR(fp->sb.st_mode)) {
Florian Festi 94360a
                 if (rc == RPMERR_ENOENT) {
Florian Festi 94360a
-                    mode_t mode = sb.st_mode;
Florian Festi 94360a
+                    mode_t mode = fp->sb.st_mode;
Florian Festi 94360a
                     mode &= ~07777;
Florian Festi 94360a
                     mode |=  00700;
Florian Festi 94360a
-                    rc = fsmMkdir(fpath, mode);
Florian Festi 94360a
+                    rc = fsmMkdir(di.dirfd, fp->fpath, mode);
Florian Festi 94360a
                 }
Florian Festi 94360a
-            } else if (S_ISLNK(sb.st_mode)) {
Florian Festi 94360a
+            } else if (S_ISLNK(fp->sb.st_mode)) {
Florian Festi 94360a
 		if (rc == RPMERR_ENOENT) {
Florian Festi 94360a
-		    rc = fsmSymlink(rpmfiFLink(fi), fpath);
Florian Festi 94360a
+		    rc = fsmSymlink(rpmfiFLink(fi), di.dirfd, fp->fpath);
Florian Festi 94360a
 		}
Florian Festi 94360a
-            } else if (S_ISFIFO(sb.st_mode)) {
Florian Festi 94360a
+            } else if (S_ISFIFO(fp->sb.st_mode)) {
Florian Festi 94360a
                 /* This mimics cpio S_ISSOCK() behavior but probably isn't right */
Florian Festi 94360a
                 if (rc == RPMERR_ENOENT) {
Florian Festi 94360a
-                    rc = fsmMkfifo(fpath, 0000);
Florian Festi 94360a
+                    rc = fsmMkfifo(di.dirfd, fp->fpath, 0000);
Florian Festi 94360a
                 }
Florian Festi 94360a
-            } else if (S_ISCHR(sb.st_mode) ||
Florian Festi 94360a
-                       S_ISBLK(sb.st_mode) ||
Florian Festi 94360a
-                       S_ISSOCK(sb.st_mode))
Florian Festi 94360a
+            } else if (S_ISCHR(fp->sb.st_mode) ||
Florian Festi 94360a
+                       S_ISBLK(fp->sb.st_mode) ||
Florian Festi 94360a
+                       S_ISSOCK(fp->sb.st_mode))
Florian Festi 94360a
             {
Florian Festi 94360a
                 if (rc == RPMERR_ENOENT) {
Florian Festi 94360a
-                    rc = fsmMknod(fpath, sb.st_mode, sb.st_rdev);
Florian Festi 94360a
+                    rc = fsmMknod(di.dirfd, fp->fpath, fp->sb.st_mode, fp->sb.st_rdev);
Florian Festi 94360a
                 }
Florian Festi 94360a
             } else {
Florian Festi 94360a
                 /* XXX Special case /dev/log, which shouldn't be packaged anyways */
Florian Festi 94360a
-                if (!IS_DEV_LOG(fpath))
Florian Festi 94360a
+                if (!IS_DEV_LOG(fp->fpath))
Florian Festi 94360a
                     rc = RPMERR_UNKNOWN_FILETYPE;
Florian Festi 94360a
             }
Florian Festi 94360a
 
Florian Festi 94360a
-touch:
Florian Festi 94360a
-	    /* Set permissions, timestamps etc for non-hardlink entries */
Florian Festi 94360a
-	    if (!rc && setmeta) {
Florian Festi 94360a
-		rc = fsmSetmeta(fpath, fi, plugins, action, &sb, nofcaps);
Florian Festi 94360a
+setmeta:
Florian Festi 94360a
+	    /* Special files require path-based ops */
Florian Festi 94360a
+	    mayopen = S_ISREG(fp->sb.st_mode) || S_ISDIR(fp->sb.st_mode);
Florian Festi 94360a
+	    if (!rc && fd == -1 && mayopen) {
Florian Festi 94360a
+		int flags = O_RDONLY;
Florian Festi 94360a
+		/* Only follow safe symlinks, and never on temporary files */
Florian Festi 94360a
+		if (fp->suffix)
Florian Festi 94360a
+		    flags |= AT_SYMLINK_NOFOLLOW;
Florian Festi 94360a
+		fd = fsmOpenat(di.dirfd, fp->fpath, flags,
Florian Festi 94360a
+				S_ISDIR(fp->sb.st_mode));
Florian Festi 94360a
+		if (fd < 0)
Florian Festi 94360a
+		    rc = RPMERR_OPEN_FAILED;
Florian Festi 94360a
 	    }
Florian Festi 94360a
-        } else if (firsthardlink >= 0 && rpmfiArchiveHasContent(fi)) {
Florian Festi 94360a
-	    /* On FA_TOUCH no hardlinks are created thus this is skipped. */
Florian Festi 94360a
-	    /* we skip the hard linked file containing the content */
Florian Festi 94360a
-	    /* write the content to the first used instead */
Florian Festi 94360a
-	    char *fn = rpmfilesFN(files, firsthardlink);
Florian Festi 94360a
-	    rc = rpmfiArchiveReadToFilePsm(fi, firstlinkfile, nodigest, psm);
Florian Festi 94360a
-	    wfd_close(&firstlinkfile);
Florian Festi 94360a
-	    firsthardlink = -1;
Florian Festi 94360a
-	    free(fn);
Florian Festi 94360a
-	}
Florian Festi 94360a
-
Florian Festi 94360a
-        if (rc) {
Florian Festi 94360a
-            if (!skip) {
Florian Festi 94360a
-                /* XXX only erase if temp fn w suffix is in use */
Florian Festi 94360a
-                if (suffix) {
Florian Festi 94360a
-		    (void) fsmRemove(fpath, sb.st_mode);
Florian Festi 94360a
-                }
Florian Festi 94360a
-                errno = saveerrno;
Florian Festi 94360a
-            }
Florian Festi 94360a
-        } else {
Florian Festi 94360a
-	    /* Notify on success. */
Florian Festi 94360a
-	    rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi));
Florian Festi 94360a
 
Florian Festi 94360a
-	    if (!skip) {
Florian Festi 94360a
-		/* Backup file if needed. Directories are handled earlier */
Florian Festi 94360a
-		if (suffix)
Florian Festi 94360a
-		    rc = fsmBackup(fi, action);
Florian Festi 94360a
-
Florian Festi 94360a
-		if (!rc)
Florian Festi 94360a
-		    rc = fsmCommit(&fpath, fi, action, suffix);
Florian Festi 94360a
+	    if (!rc && fp->setmeta) {
Florian Festi 94360a
+		rc = fsmSetmeta(fd, di.dirfd, fp->fpath,
Florian Festi 94360a
+				fi, plugins, fp->action,
Florian Festi 94360a
+				&fp->sb, nofcaps);
Florian Festi 94360a
 	    }
Florian Festi 94360a
+
Florian Festi 94360a
+	    if (fd != firstlinkfile)
Florian Festi 94360a
+		fsmClose(&fd;;
Florian Festi 94360a
 	}
Florian Festi 94360a
 
Florian Festi 94360a
+	/* Notify on success. */
Florian Festi 94360a
 	if (rc)
Florian Festi 94360a
-	    *failedFile = xstrdup(fpath);
Florian Festi 94360a
+	    *failedFile = rstrscat(NULL, rpmfiDN(fi), fp->fpath, NULL);
Florian Festi 94360a
+	else
Florian Festi 94360a
+	    rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi));
Florian Festi 94360a
+	fp->stage = FILE_UNPACK;
Florian Festi 94360a
+    }
Florian Festi 94360a
+    fi = fsmIterFini(fi, &di);
Florian Festi 94360a
 
Florian Festi 94360a
-	/* Run fsm file post hook for all plugins */
Florian Festi 94360a
-	rpmpluginsCallFsmFilePost(plugins, fi, fpath,
Florian Festi 94360a
-				  sb.st_mode, action, rc);
Florian Festi 94360a
-	fpath = _free(fpath);
Florian Festi 94360a
+    if (!rc && fx < 0 && fx != RPMERR_ITER_END)
Florian Festi 94360a
+	rc = fx;
Florian Festi 94360a
+
Florian Festi 94360a
+    /* If all went well, commit files to final destination */
Florian Festi 94360a
+    fi = fsmIter(NULL, files, RPMFI_ITER_FWD, &di);
Florian Festi 94360a
+    while (!rc && (fx = rpmfiNext(fi)) >= 0) {
Florian Festi 94360a
+	struct filedata_s *fp = &fdata[fx];
Florian Festi 94360a
+
Florian Festi 94360a
+	if (!fp->skip) {
Florian Festi 94360a
+	    if (!rc)
Florian Festi 94360a
+		rc = ensureDir(NULL, rpmfiDN(fi), 0, 0, 0, &di.dirfd);
Florian Festi 94360a
+
Florian Festi 94360a
+	    /* Backup file if needed. Directories are handled earlier */
Florian Festi 94360a
+	    if (!rc && fp->suffix)
Florian Festi 94360a
+		rc = fsmBackup(di.dirfd, fi, fp->action);
Florian Festi 94360a
+
Florian Festi 94360a
+	    if (!rc)
Florian Festi 94360a
+		rc = fsmCommit(di.dirfd, &fp->fpath, fi, fp->action, fp->suffix);
Florian Festi 94360a
+
Florian Festi 94360a
+	    if (!rc)
Florian Festi 94360a
+		fp->stage = FILE_COMMIT;
Florian Festi 94360a
+	    else
Florian Festi 94360a
+		*failedFile = rstrscat(NULL, rpmfiDN(fi), fp->fpath, NULL);
Florian Festi 94360a
+
Florian Festi 94360a
+	    /* Run fsm file post hook for all plugins for all processed files */
Florian Festi 94360a
+	    rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath,
Florian Festi 94360a
+				      fp->sb.st_mode, fp->action, rc);
Florian Festi 94360a
+	}
Florian Festi 94360a
+    }
Florian Festi 94360a
+    fi = fsmIterFini(fi, &di);
Florian Festi 94360a
+
Florian Festi 94360a
+    /* On failure, walk backwards and erase non-committed files */
Florian Festi 94360a
+    if (rc) {
Florian Festi 94360a
+	fi = fsmIter(NULL, files, RPMFI_ITER_BACK, &di);
Florian Festi 94360a
+	while ((fx = rpmfiNext(fi)) >= 0) {
Florian Festi 94360a
+	    struct filedata_s *fp = &fdata[fx];
Florian Festi 94360a
+
Florian Festi 94360a
+	    /* If the directory doesn't exist there's nothing to clean up */
Florian Festi 94360a
+	    if (ensureDir(NULL, rpmfiDN(fi), 0, 0, 1, &di.dirfd))
Florian Festi 94360a
+		continue;
Florian Festi 94360a
+
Florian Festi 94360a
+	    if (fp->stage > FILE_NONE && !fp->skip) {
Florian Festi 94360a
+		(void) fsmRemove(di.dirfd, fp->fpath, fp->sb.st_mode);
Florian Festi 94360a
+	    }
Florian Festi 94360a
+	}
Florian Festi 94360a
     }
Florian Festi 94360a
 
Florian Festi 94360a
     rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), fdOp(payload, FDSTAT_READ));
Florian Festi 94360a
     rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));
Florian Festi 94360a
 
Florian Festi 94360a
 exit:
Florian Festi 94360a
-
Florian Festi 94360a
-    /* No need to bother with close errors on read */
Florian Festi 94360a
-    rpmfiArchiveClose(fi);
Florian Festi 94360a
-    rpmfiFree(fi);
Florian Festi 94360a
+    fi = fsmIterFini(fi, &di);
Florian Festi 94360a
     Fclose(payload);
Florian Festi 94360a
     free(tid);
Florian Festi 94360a
-    free(fpath);
Florian Festi 94360a
+    for (int i = 0; i < fc; i++)
Florian Festi 94360a
+	free(fdata[i].fpath);
Florian Festi 94360a
+    free(fdata);
Florian Festi 94360a
 
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }
Florian Festi 94360a
@@ -1024,32 +1114,42 @@
Florian Festi 94360a
 int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files,
Florian Festi 94360a
               rpmpsm psm, char ** failedFile)
Florian Festi 94360a
 {
Florian Festi 94360a
-    rpmfi fi = rpmfilesIter(files, RPMFI_ITER_BACK);
Florian Festi 94360a
+    struct diriter_s di = { -1, -1 };
Florian Festi 94360a
+    rpmfi fi = fsmIter(NULL, files, RPMFI_ITER_BACK, &di);
Florian Festi 94360a
     rpmfs fs = rpmteGetFileStates(te);
Florian Festi 94360a
     rpmPlugins plugins = rpmtsPlugins(ts);
Florian Festi 94360a
-    struct stat sb;
Florian Festi 94360a
+    int fc = rpmfilesFC(files);
Florian Festi 94360a
+    int fx = -1;
Florian Festi 94360a
+    struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata));
Florian Festi 94360a
     int rc = 0;
Florian Festi 94360a
-    char *fpath = NULL;
Florian Festi 94360a
 
Florian Festi 94360a
-    while (!rc && rpmfiNext(fi) >= 0) {
Florian Festi 94360a
-	rpmFileAction action = rpmfsGetAction(fs, rpmfiFX(fi));
Florian Festi 94360a
-	fpath = fsmFsPath(fi, NULL);
Florian Festi 94360a
-	rc = fsmStat(fpath, 1, &sb);
Florian Festi 94360a
+    while (!rc && (fx = rpmfiNext(fi)) >= 0) {
Florian Festi 94360a
+	struct filedata_s *fp = &fdata[fx];
Florian Festi 94360a
+	fp->action = rpmfsGetAction(fs, rpmfiFX(fi));
Florian Festi 94360a
+
Florian Festi 94360a
+	if (XFA_SKIPPING(fp->action))
Florian Festi 94360a
+	    continue;
Florian Festi 94360a
+
Florian Festi 94360a
+	fp->fpath = fsmFsPath(fi, NULL);
Florian Festi 94360a
+	/* If the directory doesn't exist there's nothing to clean up */
Florian Festi 94360a
+	if (ensureDir(NULL, rpmfiDN(fi), 0, 0, 1, &di.dirfd))
Florian Festi 94360a
+	    continue;
Florian Festi 94360a
+
Florian Festi 94360a
+	rc = fsmStat(di.dirfd, fp->fpath, 1, &fp->sb);
Florian Festi 94360a
 
Florian Festi 94360a
-	fsmDebug(fpath, action, &sb);
Florian Festi 94360a
+	fsmDebug(fp->fpath, fp->action, &fp->sb);
Florian Festi 94360a
 
Florian Festi 94360a
 	/* Run fsm file pre hook for all plugins */
Florian Festi 94360a
-	rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
Florian Festi 94360a
-				      sb.st_mode, action);
Florian Festi 94360a
+	rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath,
Florian Festi 94360a
+				      fp->sb.st_mode, fp->action);
Florian Festi 94360a
 
Florian Festi 94360a
-	if (!XFA_SKIPPING(action))
Florian Festi 94360a
-	    rc = fsmBackup(fi, action);
Florian Festi 94360a
+	rc = fsmBackup(di.dirfd, fi, fp->action);
Florian Festi 94360a
 
Florian Festi 94360a
         /* Remove erased files. */
Florian Festi 94360a
-        if (action == FA_ERASE) {
Florian Festi 94360a
+        if (fp->action == FA_ERASE) {
Florian Festi 94360a
 	    int missingok = (rpmfiFFlags(fi) & (RPMFILE_MISSINGOK | RPMFILE_GHOST));
Florian Festi 94360a
 
Florian Festi 94360a
-	    rc = fsmRemove(fpath, sb.st_mode);
Florian Festi 94360a
+	    rc = fsmRemove(di.dirfd, fp->fpath, fp->sb.st_mode);
Florian Festi 94360a
 
Florian Festi 94360a
 	    /*
Florian Festi 94360a
 	     * Missing %ghost or %missingok entries are not errors.
Florian Festi 94360a
@@ -1074,20 +1174,20 @@
Florian Festi 94360a
 	    if (rc) {
Florian Festi 94360a
 		int lvl = strict_erasures ? RPMLOG_ERR : RPMLOG_WARNING;
Florian Festi 94360a
 		rpmlog(lvl, _("%s %s: remove failed: %s\n"),
Florian Festi 94360a
-			S_ISDIR(sb.st_mode) ? _("directory") : _("file"),
Florian Festi 94360a
-			fpath, strerror(errno));
Florian Festi 94360a
+			S_ISDIR(fp->sb.st_mode) ? _("directory") : _("file"),
Florian Festi 94360a
+			fp->fpath, strerror(errno));
Florian Festi 94360a
             }
Florian Festi 94360a
         }
Florian Festi 94360a
 
Florian Festi 94360a
 	/* Run fsm file post hook for all plugins */
Florian Festi 94360a
-	rpmpluginsCallFsmFilePost(plugins, fi, fpath,
Florian Festi 94360a
-				  sb.st_mode, action, rc);
Florian Festi 94360a
+	rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath,
Florian Festi 94360a
+				  fp->sb.st_mode, fp->action, rc);
Florian Festi 94360a
 
Florian Festi 94360a
         /* XXX Failure to remove is not (yet) cause for failure. */
Florian Festi 94360a
         if (!strict_erasures) rc = 0;
Florian Festi 94360a
 
Florian Festi 94360a
 	if (rc)
Florian Festi 94360a
-	    *failedFile = xstrdup(fpath);
Florian Festi 94360a
+	    *failedFile = rstrscat(NULL, rpmfiDN(fi), fp->fpath, NULL);
Florian Festi 94360a
 
Florian Festi 94360a
 	if (rc == 0) {
Florian Festi 94360a
 	    /* Notify on success. */
Florian Festi 94360a
@@ -1095,11 +1195,12 @@
Florian Festi 94360a
 	    rpm_loff_t amount = rpmfiFC(fi) - rpmfiFX(fi);
Florian Festi 94360a
 	    rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, amount);
Florian Festi 94360a
 	}
Florian Festi 94360a
-	fpath = _free(fpath);
Florian Festi 94360a
     }
Florian Festi 94360a
 
Florian Festi 94360a
-    free(fpath);
Florian Festi 94360a
-    rpmfiFree(fi);
Florian Festi 94360a
+    for (int i = 0; i < fc; i++)
Florian Festi 94360a
+	free(fdata[i].fpath);
Florian Festi 94360a
+    free(fdata);
Florian Festi 94360a
+    fsmIterFini(fi, &di);
Florian Festi 94360a
 
Florian Festi 94360a
     return rc;
Florian Festi 94360a
 }