diff --git a/SOURCES/1470.patch b/SOURCES/1470.patch
deleted file mode 100644
index 569aabc..0000000
--- a/SOURCES/1470.patch
+++ /dev/null
@@ -1,2659 +0,0 @@
-From 82b454d2ff43b39e1a3b38fded0c2e5caffcd336 Mon Sep 17 00:00:00 2001
-From: Matthew Almond <malmond@fb.com>
-Date: Fri, 8 Nov 2019 09:29:43 -0800
-Subject: [PATCH 1/4] RPM with Copy on Write
-
-This is part of https://fedoraproject.org/wiki/Changes/RPMCoW
-
-The majority of changes are in two new programs:
-
-= rpm2extents
-
-Modeled as a 'stream processor'. It reads a regular .rpm file on stdin,
-and produces a modified .rpm file on stdout. The lead, signature and
-headers are preserved 1:1 to allow all the normal metadata inspection,
-signature verification to work as expected. Only the 'payload' is
-modified.
-
-The primary motivation for this tool is to re-organize the payload as a
-sequence of raw file extents (hence the name). The files are organized
-by their digest identity instead of path/filename. If any digest is
-repeated, then the file is skipped/de-duped. Only regular files are
-represented. All other entries like directories, symlinks, devices are
-fully described in the headers and are omitted.
-
-The files are padded so they start on `sysconf(_SC_PAGESIZE)` boundries
-to permit 'reflink' syscalls to work in the `reflink` plugin.
-
-At the end of the file is a footer with 3 sections:
-
-1. List of calculated digests of the input stream. This is used in
-   `librepo` because the file *written* is a derivative, and not the
-   same as the repo metadata describes. `rpm2extents` takes one or more
-   positional arguments that described which digest algorithms are
-   desired. This is often just `SHA256`. This program is only measuring
-   and recording the digest - it does not express an opinion on whether
-   the file is correct. Due to the API on most compression libraries
-   directly reading the source file, the whole file digest is measured
-   using a subprocess and pipes. I don't love it, but it works.
-2. Sorted List of file content digests + offset pairs. This is used in
-   the plugin with a trivial binary search to locate the start of file
-   content. The size is not needed because it's part of normal headers.
-3. (offset of 1., offset of 2., 8 byte MAGIC value) triple
-
-= reflink plugin
-
-Looks for the 8 byte magic value at the end of the rpm file. If present
-it alters the `RPMTAG_PAYLOADFORMAT` in memory to `clon`, and reads in
-the digest-> offset table.
-
-`rpmPackageFilesInstall()` in `fsm.c` is
-modified to alter the enumeration strategy from
-`rpmfiNewArchiveReader()` to `rpmfilesIter()` if not `cpio`. This is
-needed because there is no cpio to enumerate. In the same function, if
-`rpmpluginsCallFsmFilePre()` returns `RPMRC_PLUGIN_CONTENTS` then
-`fsmMkfile()` is skipped as it is assumed the plugin did the work.
-
-The majority of the work is in `reflink_fsm_file_pre()` - the per file
-hook for RPM plugins. If the file enumerated in
-`rpmPackageFilesInstall()` is a regular file, this function will look up
-the offset in the digest->offset table and will try to reflink it, then
-fall back to a regular copy. If reflinking does work: we will have
-reflinked a whole number of pages, so we truncate the file to the
-expected size. Therefore installing most files does involve two writes:
-the reflink of the full size, then a fork/copy on write for the last
-page worth.
-
-If the file passed to `reflink_fsm_file_pre()` is anything other than a
-regular file, it return `RPMRC_OK` so the normal mechanics of
-`rpmPackageFilesInstall()` are used. That handles directories, symlinks
-and other non file types.
-
-= New API for internal use
-
-1. `rpmReadPackageRaw()` is used within `rpm2extents` to read all the
-   headers without trying to validate signatures. This eliminates the
-   runtime dependency on rpmdb.
-2. `rpmteFd()` exposes the Fd behind the rpmte, so plugins can interact
-   with the rpm itself.
-3. `RPMRC_PLUGIN_CONTENTS` in `rpmRC_e` for use in
-   `rpmpluginsCallFsmFilePre()` specifically.
-4. `pgpStringVal()` is used to help parse the command line in
-   `rpm2extents` - the positional arguments are strings, and this
-   converts the values back to the values in the table.
-
-Nothing has been removed, and none of the changes are intended to be
-used externally, so I don't think a soname bump is warranted here.
----
- Makefile.am         |   6 +-
- lib/depends.c       |   2 +
- lib/fsm.c           |  50 ++++-
- lib/package.c       |  40 ++++
- lib/rpmlib.h        |   9 +
- lib/rpmplugins.c    |  21 +-
- lib/rpmte.c         |   5 +
- lib/rpmte.h         |   2 +
- lib/rpmtypes.h      |   3 +-
- macros.in           |   1 +
- plugins/Makefile.am |   4 +
- plugins/reflink.c   | 340 +++++++++++++++++++++++++++++
- rpm2extents.c       | 519 ++++++++++++++++++++++++++++++++++++++++++++
- rpmio/rpmpgp.c      |  10 +
- rpmio/rpmpgp.h      |   9 +
- 15 files changed, 1006 insertions(+), 15 deletions(-)
- create mode 100644 plugins/reflink.c
- create mode 100644 rpm2extents.c
-
-diff --git a/Makefile.am b/Makefile.am
-index aca2f8996..17c1e0e8a 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -105,7 +105,7 @@ pkginclude_HEADERS += build/rpmfc.h
- pkginclude_HEADERS += build/rpmspec.h
- 
- 
--bin_PROGRAMS =		rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec
-+bin_PROGRAMS =		rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec rpm2extents
- if WITH_ARCHIVE
- bin_PROGRAMS += 	rpm2archive 
- endif
-@@ -159,6 +159,10 @@ rpm2cpio_SOURCES =	rpm2cpio.c debug.h system.h
- rpm2cpio_LDADD =	lib/librpm.la rpmio/librpmio.la
- rpm2cpio_LDADD +=	@WITH_POPT_LIB@
- 
-+rpm2extents_SOURCES =	rpm2extents.c debug.h system.h
-+rpm2extents_LDADD =	lib/librpm.la rpmio/librpmio.la
-+rpm2extents_LDADD +=	@WITH_POPT_LIB@
-+
- rpm2archive_SOURCES =	rpm2archive.c debug.h system.h
- rpm2archive_LDADD =	lib/librpm.la rpmio/librpmio.la
- rpm2archive_LDADD +=	@WITH_POPT_LIB@ @WITH_ARCHIVE_LIB@
-diff --git a/lib/depends.c b/lib/depends.c
-index 28a4a784d..9e0489bcc 100644
---- a/lib/depends.c
-+++ b/lib/depends.c
-@@ -80,6 +80,8 @@ static rpmRC headerCheckPayloadFormat(Header h) {
-      */
-     if (!payloadfmt) return rc;
- 
-+    if (rstreq(payloadfmt, "clon")) return rc;
-+
-     if (!rstreq(payloadfmt, "cpio")) {
-         char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
-         if (payloadfmt && rstreq(payloadfmt, "drpm")) {
-diff --git a/lib/fsm.c b/lib/fsm.c
-index 35dcda081..03a716474 100644
---- a/lib/fsm.c
-+++ b/lib/fsm.c
-@@ -7,6 +7,7 @@
- 
- #include <utime.h>
- #include <errno.h>
-+#include <stdbool.h>
- #if WITH_CAP
- #include <sys/capability.h>
- #endif
-@@ -18,6 +19,7 @@
- 
- #include "rpmio/rpmio_internal.h"	/* fdInit/FiniDigest */
- #include "lib/fsm.h"
-+#include "lib/rpmlib.h"
- #include "lib/rpmte_internal.h"	/* XXX rpmfs */
- #include "lib/rpmplugins.h"	/* rpm plugins hooks */
- #include "lib/rpmug.h"
-@@ -835,7 +837,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
-               rpmpsm psm, char ** failedFile)
- {
-     FD_t payload = rpmtePayload(te);
--    rpmfi fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
-+    rpmfi fi;
-     rpmfs fs = rpmteGetFileStates(te);
-     rpmPlugins plugins = rpmtsPlugins(ts);
-     struct stat sb;
-@@ -850,10 +852,21 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
-     char *tid = NULL;
-     const char *suffix;
-     char *fpath = NULL;
-+    Header h = rpmteHeader(te);
-+    const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);
-+    bool cpio = true;
- 
--    if (fi == NULL) {
--	rc = RPMERR_BAD_MAGIC;
--	goto exit;
-+    if (payloadfmt && rstreq(payloadfmt, "clon")) {
-+	cpio = false;
-+    }
-+    if (cpio) {
-+	fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
-+	if (fi == NULL) {
-+	    rc = RPMERR_BAD_MAGIC;
-+	    goto exit;
-+	}
-+    } else {
-+	fi = rpmfilesIter(files, RPMFI_ITER_FWD);
-     }
- 
-     /* transaction id used for temporary path suffix while installing */
-@@ -893,10 +906,20 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
- 	/* Run fsm file pre hook for all plugins */
- 	rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
- 				      sb.st_mode, action);
--	if (rc) {
--	    skip = 1;
--	} else {
-+	skip = skip || rpmfiFFlags(fi) & RPMFILE_GHOST;
-+	bool plugin_contents = false;
-+	switch (rc) {
-+	case RPMRC_OK:
- 	    setFileState(fs, rpmfiFX(fi));
-+	    break;
-+	case RPMRC_PLUGIN_CONTENTS:
-+	    plugin_contents = true;
-+	    // reduce reads on cpio to this value. Could be zero if
-+	    // this is from a hard link.
-+	    rc = RPMRC_OK;
-+	    break;
-+	default:
-+	    skip = 1;
- 	}
- 
-         if (!skip) {
-@@ -926,8 +949,12 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
- 
-             if (S_ISREG(sb.st_mode)) {
- 		if (rc == RPMERR_ENOENT) {
--		    rc = fsmMkfile(fi, fpath, files, psm, nodigest,
--				   &setmeta, &firsthardlink, &firstlinkfile);
-+		    if (plugin_contents) {
-+			rc = RPMRC_OK;
-+		    } else {
-+			rc = fsmMkfile(fi, fpath, files, psm, nodigest,
-+				&setmeta, &firsthardlink, &firstlinkfile);
-+		    }
- 		}
-             } else if (S_ISDIR(sb.st_mode)) {
-                 if (rc == RPMERR_ENOENT) {
-@@ -1011,7 +1038,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
- exit:
- 
-     /* No need to bother with close errors on read */
--    rpmfiArchiveClose(fi);
-+    if (cpio) {
-+	rpmfiArchiveClose(fi);
-+    }
-+	h = headerFree(h);
-     rpmfiFree(fi);
-     Fclose(payload);
-     free(tid);
-diff --git a/lib/package.c b/lib/package.c
-index 93a06ebfe..b0c1c2857 100644
---- a/lib/package.c
-+++ b/lib/package.c
-@@ -407,5 +407,45 @@ rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp)
-     return rc;
- }
- 
-+rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp)
-+{
-+    char *msg = NULL;
-+    hdrblob sigblob = hdrblobCreate();
-+    hdrblob blob = hdrblobCreate();
-+    Header h = NULL;
-+    Header sigh = NULL;
-+
-+    rpmRC rc = rpmLeadRead(fd, &msg);
-+    if (rc != RPMRC_OK)
-+	goto exit;
-+
-+    rc = hdrblobRead(fd, 1, 0, RPMTAG_HEADERSIGNATURES, sigblob, &msg);
-+    if (rc != RPMRC_OK)
-+	goto exit;
-+
-+    rc = hdrblobRead(fd, 1, 1, RPMTAG_HEADERIMMUTABLE, blob, &msg);
-+    if (rc != RPMRC_OK)
-+	goto exit;
-+
-+    rc = hdrblobImport(sigblob, 0, &sigh, &msg);
-+    if (rc)
-+	goto exit;
- 
-+    rc = hdrblobImport(blob, 0, &h, &msg);
-+    if (rc)
-+	goto exit;
- 
-+    *sigp = headerLink(sigh);
-+    *hdrp = headerLink(h);
-+
-+exit:
-+    if (rc != RPMRC_OK && msg)
-+	rpmlog(RPMLOG_ERR, "%s: %s\n", Fdescr(fd), msg);
-+    hdrblobFree(sigblob);
-+    hdrblobFree(blob);
-+    headerFree(sigh);
-+    headerFree(h);
-+    free(msg);
-+
-+    return rc;
-+}
-diff --git a/lib/rpmlib.h b/lib/rpmlib.h
-index 72ee724e8..4f1a24d14 100644
---- a/lib/rpmlib.h
-+++ b/lib/rpmlib.h
-@@ -156,6 +156,15 @@ rpmRC rpmReadHeader(rpmts ts, FD_t fd, Header *hdrp, char ** msg);
- rpmRC rpmReadPackageFile(rpmts ts, FD_t fd,
- 		const char * fn, Header * hdrp);
- 
-+/** \ingroup header
-+ * Return package signature, header from file handle, no verification.
-+ * @param fd		file handle
-+ * @param[out] sigp		address of header (or NULL)
-+ * @param[out] hdrp		address of header (or NULL)
-+ * @return		RPMRC_OK on success
-+ */
-+rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp);
-+
- /** \ingroup rpmtrans
-  * Install source package.
-  * @param ts		transaction set
-diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c
-index 62d75c4cf..c5084d398 100644
---- a/lib/rpmplugins.c
-+++ b/lib/rpmplugins.c
-@@ -356,13 +356,28 @@ rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char *path,
-     plugin_fsm_file_pre_func hookFunc;
-     int i;
-     rpmRC rc = RPMRC_OK;
-+    rpmRC hook_rc;
- 
-     for (i = 0; i < plugins->count; i++) {
- 	rpmPlugin plugin = plugins->plugins[i];
- 	RPMPLUGINS_SET_HOOK_FUNC(fsm_file_pre);
--	if (hookFunc && hookFunc(plugin, fi, path, file_mode, op) == RPMRC_FAIL) {
--	    rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name);
--	    rc = RPMRC_FAIL;
-+	if (hookFunc) {
-+	    hook_rc = hookFunc(plugin, fi, path, file_mode, op);
-+	    if (hook_rc == RPMRC_FAIL) {
-+		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name);
-+		rc = RPMRC_FAIL;
-+	    } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) {
-+		if (rc == RPMRC_PLUGIN_CONTENTS) {
-+		    /*
-+		    Another plugin already said it'd handle contents. It's undefined how
-+		    these would combine, so treat this as a failure condition.
-+		    */
-+		    rc = RPMRC_FAIL;
-+		} else {
-+		    /* Plugin will handle content */
-+		    rc = RPMRC_PLUGIN_CONTENTS;
-+		}
-+	    }
- 	}
-     }
- 
-diff --git a/lib/rpmte.c b/lib/rpmte.c
-index fe9782953..ba06c2985 100644
---- a/lib/rpmte.c
-+++ b/lib/rpmte.c
-@@ -422,6 +422,11 @@ FD_t rpmteSetFd(rpmte te, FD_t fd)
-     return NULL;
- }
- 
-+FD_t rpmteFd(rpmte te)
-+{
-+    return (te != NULL ? te->fd : NULL);
-+}
-+
- fnpyKey rpmteKey(rpmte te)
- {
-     return (te != NULL ? te->key : NULL);
-diff --git a/lib/rpmte.h b/lib/rpmte.h
-index 81acf7a19..6fc0a9f91 100644
---- a/lib/rpmte.h
-+++ b/lib/rpmte.h
-@@ -209,6 +209,8 @@ const char * rpmteNEVR(rpmte te);
-  */
- const char * rpmteNEVRA(rpmte te);
- 
-+FD_t rpmteFd(rpmte te);
-+
- /** \ingroup rpmte
-  * Retrieve key from transaction element.
-  * @param te		transaction element
-diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h
-index e8e69b506..af2611e9e 100644
---- a/lib/rpmtypes.h
-+++ b/lib/rpmtypes.h
-@@ -106,7 +106,8 @@ typedef	enum rpmRC_e {
-     RPMRC_NOTFOUND	= 1,	/*!< Generic not found code. */
-     RPMRC_FAIL		= 2,	/*!< Generic failure code. */
-     RPMRC_NOTTRUSTED	= 3,	/*!< Signature is OK, but key is not trusted. */
--    RPMRC_NOKEY		= 4	/*!< Public key is unavailable. */
-+    RPMRC_NOKEY		= 4,	/*!< Public key is unavailable. */
-+    RPMRC_PLUGIN_CONTENTS = 5     /*!< fsm_file_pre plugin is handling content */
- } rpmRC;
- 
- #ifdef __cplusplus
-diff --git a/macros.in b/macros.in
-index 5778a1f58..15a28c2d2 100644
---- a/macros.in
-+++ b/macros.in
-@@ -1175,6 +1175,7 @@ package or when debugging this package.\
- 
- # Transaction plugin macros
- %__plugindir		%{_libdir}/rpm-plugins
-+%__transaction_reflink		%{__plugindir}/reflink.so
- %__transaction_systemd_inhibit	%{__plugindir}/systemd_inhibit.so
- %__transaction_selinux		%{__plugindir}/selinux.so
- %__transaction_syslog		%{__plugindir}/syslog.so
-diff --git a/plugins/Makefile.am b/plugins/Makefile.am
-index 963d53db4..a21401966 100644
---- a/plugins/Makefile.am
-+++ b/plugins/Makefile.am
-@@ -33,6 +33,10 @@ prioreset_la_SOURCES = prioreset.c
- prioreset_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
- plugins_LTLIBRARIES += prioreset.la
- 
-+reflink_la_SOURCES = reflink.c
-+reflink_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
-+plugins_LTLIBRARIES += reflink.la
-+
- syslog_la_SOURCES = syslog.c
- syslog_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
- plugins_LTLIBRARIES += syslog.la
-diff --git a/plugins/reflink.c b/plugins/reflink.c
-new file mode 100644
-index 000000000..d7f19acd9
---- /dev/null
-+++ b/plugins/reflink.c
-@@ -0,0 +1,340 @@
-+#include "system.h"
-+
-+#include <errno.h>
-+#include <sys/resource.h>
-+#include <unistd.h>
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+#if defined(__linux__)
-+#include <linux/fs.h>        /* For FICLONE */
-+#endif
-+
-+#include <rpm/rpmlog.h>
-+#include "lib/rpmlib.h"
-+#include "lib/rpmplugin.h"
-+#include "lib/rpmte_internal.h"
-+#include <rpm/rpmfileutil.h>
-+#include "rpmio/rpmio_internal.h"
-+
-+
-+#include "debug.h"
-+
-+#include <sys/ioctl.h>
-+
-+/* use hash table to remember inode -> ix (for rpmfilesFN(ix)) lookups */
-+#undef HASHTYPE
-+#undef HTKEYTYPE
-+#undef HTDATATYPE
-+#define HASHTYPE inodeIndexHash
-+#define HTKEYTYPE rpm_ino_t
-+#define HTDATATYPE int
-+#include "lib/rpmhash.H"
-+#include "lib/rpmhash.C"
-+
-+/*
-+We use this in find to indicate a key wasn't found. This is an unrecoverable
-+error, but we can at least show a decent error. 0 is never a valid offset
-+because it's the offset of the start of the file.
-+*/
-+#define NOT_FOUND 0
-+
-+#define BUFFER_SIZE (1024 * 128)
-+
-+/* magic value at end of file (64 bits) that indicates this is a transcoded rpm */
-+#define MAGIC 3472329499408095051
-+
-+struct reflink_state_s {
-+  /* Stuff that's used across rpms */
-+  long fundamental_block_size;
-+  char *buffer;
-+
-+  /* stuff that's used/updated per psm */
-+  uint32_t keys, keysize;
-+
-+  // table for current rpm, keys * (keysize + sizeof(rpm_loff_t))
-+  unsigned char *table;
-+  FD_t fd;
-+  rpmfiles files;
-+  inodeIndexHash inodeIndexes;
-+};
-+
-+typedef struct reflink_state_s * reflink_state;
-+
-+static int inodeCmp(rpm_ino_t a, rpm_ino_t b)
-+{
-+    return (a != b);
-+}
-+
-+static unsigned int inodeId(rpm_ino_t a)
-+{
-+    /* rpm_ino_t is uint32_t so maps safely to unsigned int */
-+    return (unsigned int)a;
-+}
-+
-+static rpmRC reflink_init(rpmPlugin plugin, rpmts ts) {
-+  reflink_state state = rcalloc(1, sizeof(struct reflink_state_s));
-+
-+  /*
-+  IOCTL-FICLONERANGE(2): ...Disk filesystems generally require the offset and
-+  length arguments to be aligned to the fundamental block size.
-+
-+  The value of "fundamental block size" is directly related to the system's
-+  page size, so we should use that.
-+  */
-+  state->fundamental_block_size = sysconf(_SC_PAGESIZE);
-+  state->buffer = rcalloc(1, BUFFER_SIZE);
-+  rpmPluginSetData(plugin, state);
-+
-+  return RPMRC_OK;
-+}
-+
-+static void reflink_cleanup(rpmPlugin plugin) {
-+  reflink_state state = rpmPluginGetData(plugin);
-+  free(state->buffer);
-+  free(state);
-+}
-+
-+static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
-+    reflink_state state = rpmPluginGetData(plugin);
-+    state->fd = rpmteFd(te);
-+    if (state->fd == 0) {
-+      rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n"));
-+      return RPMRC_OK;
-+    }
-+    rpm_loff_t current = Ftell(state->fd);
-+    uint64_t magic;
-+    if (Fseek(state->fd, -(sizeof(magic)), SEEK_END) < 0) {
-+      rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n"));
-+      if (Fseek(state->fd, current, SEEK_SET) < 0) {
-+        /* yes this gets a bit repetitive */
-+        rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n"));
-+      }
-+      return RPMRC_FAIL;
-+    }
-+    size_t len = sizeof(magic);
-+    if (Fread(&magic, len, 1, state->fd) != len) {
-+      rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n"));
-+      if (Fseek(state->fd, current, SEEK_SET) < 0) {
-+        rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n"));
-+      }
-+      return RPMRC_FAIL;
-+    }
-+    if (magic != MAGIC) {
-+      rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n"));
-+      if (Fseek(state->fd, current, SEEK_SET) < 0) {
-+        rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n"));
-+        return RPMRC_FAIL;
-+      }
-+      return RPMRC_OK;
-+    }
-+    rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));
-+    Header h = rpmteHeader(te);
-+
-+    /* replace/add header that main fsm.c can read */
-+    headerDel(h, RPMTAG_PAYLOADFORMAT);
-+    headerPutString(h, RPMTAG_PAYLOADFORMAT, "clon");
-+    headerFree(h);
-+    state->files = rpmteFiles(te);
-+    /* tail of file contains offset_table, offset_checksums
-+       then magic
-+    */
-+    if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(magic)), SEEK_END) < 0) {
-+      rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"), state->fd);
-+      return RPMRC_FAIL;
-+    }
-+    rpm_loff_t table_start;
-+    len = sizeof(table_start);
-+    if (Fread(&table_start, len, 1, state->fd) != len) {
-+      rpmlog(RPMLOG_ERR, _("reflink: unable to read table_start\n"));
-+      return RPMRC_FAIL;
-+    }
-+    if (Fseek(state->fd, table_start, SEEK_SET) < 0) {
-+      rpmlog(RPMLOG_ERR, _("reflink: unable to seek to table_start\n"));
-+      return RPMRC_FAIL;
-+    }
-+    len = sizeof(state->keys);
-+    if (Fread(&state->keys, len, 1, state->fd) != len) {
-+      rpmlog(RPMLOG_ERR, _("reflink: unable to read number of keys\n"));
-+      return RPMRC_FAIL;
-+    }
-+    len = sizeof(state->keysize);
-+    if (Fread(&state->keysize, len, 1, state->fd) != len) {
-+      rpmlog(RPMLOG_ERR, _("reflink: unable to read keysize\n"));
-+      return RPMRC_FAIL;
-+    }
-+    rpmlog(RPMLOG_DEBUG, _("reflink: table_start=0x%lx, keys=%d, keysize=%d\n"), table_start, state->keys, state->keysize);
-+    // now get digest table if there is a reason to have one.
-+    if (state->keys == 0 || state->keysize == 0) {
-+      // no files (or no digests(!))
-+      state->table = NULL;
-+    } else {
-+      int table_size = state->keys * (state->keysize + sizeof(rpm_loff_t));
-+      state->table = rcalloc(1, table_size);
-+      if (Fread(state->table, table_size, 1, state->fd) != table_size) {
-+        rpmlog(RPMLOG_ERR, _("reflink: unable to read table\n"));
-+        return RPMRC_FAIL;
-+      }
-+      state->inodeIndexes = inodeIndexHashCreate(state->keys, inodeId, inodeCmp, NULL, NULL);
-+    }
-+
-+    // seek back to original location
-+    // might not be needed if we seek to offset immediately
-+    if (Fseek(state->fd, current, SEEK_SET) < 0) {
-+      rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n"));
-+      return RPMRC_FAIL;
-+    }
-+    return RPMRC_OK;
-+}
-+
-+static rpmRC reflink_psm_post(rpmPlugin plugin, rpmte te, int res)
-+{
-+    reflink_state state = rpmPluginGetData(plugin);
-+    state->files = rpmfilesFree(state->files);
-+    if (state->table) {
-+      free(state->table);
-+      state->table = NULL;
-+    }
-+    if (state->inodeIndexes) {
-+      inodeIndexHashFree(state->inodeIndexes);
-+      state->inodeIndexes = NULL;
-+    }
-+    return RPMRC_OK;
-+}
-+
-+
-+// have a prototype, warnings system
-+rpm_loff_t find(const unsigned char *digest, reflink_state state);
-+
-+rpm_loff_t find(const unsigned char *digest, reflink_state state) {
-+# if defined(__GNUC__)
-+  /* GCC nested function because bsearch's comparison function can't access
-+     state-keysize otherwise
-+  */
-+  int cmpdigest(const void *k1, const void *k2) {
-+    rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2);
-+    return memcmp(k1, k2, state->keysize);
-+  }
-+# endif
-+  rpmlog(RPMLOG_DEBUG, _("reflink: bsearch(key=%p, base=%p, nmemb=%d, size=%lu)\n"), digest, state->table, state->keys, state->keysize + sizeof(rpm_loff_t));
-+  char *entry = bsearch(digest, state->table, state->keys, state->keysize + sizeof(rpm_loff_t), cmpdigest);
-+  if (entry == NULL) {
-+    return NOT_FOUND;
-+  }
-+  rpm_loff_t offset = *(rpm_loff_t *)(entry + state->keysize);
-+  return offset;
-+}
-+
-+static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, mode_t file_mode, rpmFsmOp op)
-+{
-+    struct file_clone_range fcr;
-+    rpm_loff_t size;
-+    int dst, rc;
-+    int *hlix;
-+
-+    reflink_state state = rpmPluginGetData(plugin);
-+    if (state->table == NULL) {
-+        // no table means rpm is not in reflink format, so leave. Now.
-+        return RPMRC_OK;
-+    }
-+    if (op == FA_TOUCH) {
-+        // we're not overwriting an existing file
-+        return RPMRC_OK;
-+    }
-+    fcr.dest_offset = 0;
-+    if (S_ISREG(file_mode) && !(rpmfiFFlags(fi) & RPMFILE_GHOST)) {
-+      rpm_ino_t inode = rpmfiFInode(fi);
-+      /* check for hard link entry in table. GetEntry overwrites hlix with the address of the first match */
-+      if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL, NULL)) {
-+        // entry is in table, use hard link
-+        char *fn = rpmfilesFN(state->files, hlix[0]);
-+        if (link(fn, path) != 0) {
-+          rpmlog(RPMLOG_ERR, _("reflink: Unable to hard link %s -> %s due to %s\n"), fn, path, strerror(errno));
-+          free(fn);
-+          return RPMRC_FAIL;
-+        }
-+        free(fn);
-+        return RPMRC_PLUGIN_CONTENTS;
-+      }
-+      /* if we didn't hard link, then we'll track this inode as being created soon */
-+      if (rpmfiFNlink(fi) > 1) {
-+        /* minor optimization: only store files with more than one link */
-+        inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi));
-+      }
-+      /* derived from wfd_open in fsm.c */
-+      mode_t old_umask = umask(0577);
-+      dst = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
-+      umask(old_umask);
-+      if (dst == -1) {
-+          rpmlog(RPMLOG_ERR, _("reflink: Unable to open %s for writing due to %s, flags = %x\n"), path, strerror(errno), rpmfiFFlags(fi));
-+          return RPMRC_FAIL;
-+      }
-+      size = rpmfiFSize(fi);
-+      if (size > 0) {
-+          /* round src_length down to fundamental_block_size multiple */
-+          fcr.src_length = size / state->fundamental_block_size * state->fundamental_block_size;
-+          if ((size % state->fundamental_block_size) > 0) {
-+              /* round up to next fundamental_block_size. We expect the data in the rpm to be similarly padded */
-+              fcr.src_length += state->fundamental_block_size;
-+          }
-+          fcr.src_fd = Fileno(state->fd);
-+          if (fcr.src_fd == -1) {
-+            close(dst);
-+            rpmlog(RPMLOG_ERR, _("reflink: src fd lookup failed\n"));
-+            return RPMRC_FAIL;
-+          }
-+          fcr.src_offset = find(rpmfiFDigest(fi, NULL, NULL), state);
-+          if (fcr.src_offset == NOT_FOUND) {
-+            close(dst);
-+            rpmlog(RPMLOG_ERR, _("reflink: digest not found\n"));
-+            return RPMRC_FAIL;
-+          }
-+          rpmlog(RPMLOG_DEBUG, _("reflink: Reflinking %lu bytes at %lu to %s orig size=%lu, file=%ld\n"), fcr.src_length, fcr.src_offset, path, size, fcr.src_fd);
-+          rc = ioctl(dst, FICLONERANGE, &fcr);
-+          if (rc) {
-+            rpmlog(RPMLOG_WARNING, _("reflink: falling back to copying bits for %s due to %d, %d = %s\n"), path, rc, errno, strerror(errno));
-+            if (Fseek(state->fd, fcr.src_offset, SEEK_SET) < 0) {
-+                close(dst);
-+                rpmlog(RPMLOG_ERR, _("reflink: unable to seek on copying bits\n"));
-+                return RPMRC_FAIL;
-+            }
-+            rpm_loff_t left = size;
-+            size_t len, read, written;
-+            while (left) {
-+              len = (left > BUFFER_SIZE ? BUFFER_SIZE : left);
-+              read = Fread(state->buffer, len, 1, state->fd);
-+              if (read != len) {
-+                close(dst);
-+                rpmlog(RPMLOG_ERR, _("reflink: short read on copying bits\n"));
-+                return RPMRC_FAIL;
-+              }
-+              written = write(dst, state->buffer, len);
-+              if (read != written) {
-+                close(dst);
-+                rpmlog(RPMLOG_ERR, _("reflink: short write on copying bits\n"));
-+                return RPMRC_FAIL;
-+              }
-+              left -= len;
-+            }
-+          } else {
-+            /* reflink worked, so truncate */
-+            rc = ftruncate(dst, size);
-+            if (rc) {
-+                rpmlog(RPMLOG_ERR, _("reflink: Unable to truncate %s to %ld due to %s\n"), path, size, strerror(errno));
-+                return RPMRC_FAIL;
-+            }
-+          }
-+      }
-+      close(dst);
-+      return RPMRC_PLUGIN_CONTENTS;
-+    }
-+    return RPMRC_OK;
-+}
-+
-+struct rpmPluginHooks_s reflink_hooks = {
-+    .init = reflink_init,
-+    .cleanup = reflink_cleanup,
-+    .psm_pre = reflink_psm_pre,
-+    .psm_post = reflink_psm_post,
-+    .fsm_file_pre = reflink_fsm_file_pre,
-+};
-diff --git a/rpm2extents.c b/rpm2extents.c
-new file mode 100644
-index 000000000..5662b86a6
---- /dev/null
-+++ b/rpm2extents.c
-@@ -0,0 +1,519 @@
-+/* rpm2extents: convert payload to inline extents */
-+
-+#include "system.h"
-+
-+#include <rpm/rpmlib.h>		/* rpmReadPackageFile .. */
-+#include <rpm/rpmfi.h>
-+#include <rpm/rpmtag.h>
-+#include <rpm/rpmio.h>
-+#include <rpm/rpmpgp.h>
-+
-+#include <rpm/rpmts.h>
-+#include "lib/rpmlead.h"
-+#include "lib/signature.h"
-+#include "lib/header_internal.h"
-+#include "rpmio/rpmio_internal.h"
-+
-+#include <unistd.h>
-+#include <sys/types.h>
-+#include <sys/wait.h>
-+#include <signal.h>
-+#include <errno.h>
-+#include <string.h>
-+
-+#include "debug.h"
-+
-+/* hash of void * (pointers) to file digests to offsets within output.
-+   The length of the key depends on what the FILEDIGESTALGO is.
-+ */
-+#undef HASHTYPE
-+#undef HTKEYTYPE
-+#undef HTDATATYPE
-+#define HASHTYPE digestSet
-+#define HTKEYTYPE const unsigned char *
-+#include "lib/rpmhash.H"
-+#include "lib/rpmhash.C"
-+
-+/* magic value at end of file (64 bits) that indicates this is a transcoded rpm */
-+#define MAGIC 3472329499408095051
-+
-+struct digestoffset {
-+    const unsigned char * digest;
-+    rpm_loff_t pos;
-+};
-+
-+rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit);
-+
-+rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit)
-+{
-+    return (unit - (pos % unit)) % unit;
-+}
-+
-+static int digestor(
-+    FD_t fdi,
-+    FD_t fdo,
-+    FD_t validationo,
-+    uint8_t algos[],
-+    uint32_t algos_len
-+)
-+{
-+    ssize_t fdilength;
-+    const char *filedigest, *algo_name;
-+    size_t filedigest_len, len;
-+    uint32_t algo_name_len, algo_digest_len;
-+    int algo;
-+    rpmRC rc = RPMRC_FAIL;
-+
-+    for (algo = 0; algo < algos_len; algo++)
-+    {
-+        fdInitDigest(fdi, algos[algo], 0);
-+    }
-+    fdilength = ufdCopy(fdi, fdo);
-+    if (fdilength == -1)
-+    {
-+        fprintf(stderr, _("digest cat failed\n"));
-+        goto exit;
-+    }
-+
-+    len = sizeof(fdilength);
-+    if (Fwrite(&fdilength, len, 1, validationo) != len)
-+    {
-+        fprintf(stderr, _("Unable to write input length %zd\n"), fdilength);
-+        goto exit;
-+    }
-+    len = sizeof(algos_len);
-+    if (Fwrite(&algos_len, len, 1, validationo) != len)
-+    {
-+        fprintf(stderr, _("Unable to write number of validation digests\n"));
-+        goto exit;
-+    }
-+    for (algo = 0; algo < algos_len; algo++)
-+    {
-+        fdFiniDigest(fdi, algos[algo], (void **)&filedigest, &filedigest_len, 0);
-+
-+        algo_name = pgpValString(PGPVAL_HASHALGO, algos[algo]);
-+        algo_name_len = (uint32_t)strlen(algo_name);
-+        algo_digest_len = (uint32_t)filedigest_len;
-+
-+        len = sizeof(algo_name_len);
-+        if (Fwrite(&algo_name_len, len, 1, validationo) != len)
-+        {
-+            fprintf(
-+                stderr,
-+                _("Unable to write validation algo name length\n")
-+            );
-+            goto exit;
-+        }
-+        len = sizeof(algo_digest_len);
-+        if (Fwrite(&algo_digest_len, len, 1, validationo) != len)
-+        {
-+            fprintf(
-+                stderr,
-+                _("Unable to write number of bytes for validation digest\n")
-+            );
-+            goto exit;
-+        }
-+        if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len)
-+        {
-+            fprintf(stderr, _("Unable to write validation algo name\n"));
-+            goto exit;
-+        }
-+        if (
-+            Fwrite(
-+                filedigest,
-+                algo_digest_len,
-+                1,
-+                validationo
-+            ) != algo_digest_len
-+        )
-+        {
-+            fprintf(
-+                stderr,
-+                _("Unable to write validation digest value %u, %zu\n"),
-+                algo_digest_len,
-+                filedigest_len
-+            );
-+            goto exit;
-+        }
-+    }
-+    rc = RPMRC_OK;
-+exit:
-+    return rc;
-+}
-+
-+static rpmRC process_package(FD_t fdi, FD_t validationi)
-+{
-+    uint32_t diglen;
-+    /* GNU C extension: can use diglen from outer context */
-+    int digestSetCmp(const unsigned char * a, const unsigned char * b)
-+    {
-+        return memcmp(a, b, diglen);
-+    }
-+
-+    unsigned int digestSetHash(const unsigned char * digest)
-+    {
-+        /* assumes sizeof(unsigned int) < diglen */
-+        return *(unsigned int *)digest;
-+    }
-+
-+    int digestoffsetCmp(const void * a, const void * b)
-+    {
-+        return digestSetCmp(
-+            ((struct digestoffset *)a)->digest,
-+            ((struct digestoffset *)b)->digest
-+        );
-+    }
-+
-+    FD_t fdo;
-+    FD_t gzdi;
-+    Header h, sigh;
-+    long fundamental_block_size = sysconf(_SC_PAGESIZE);
-+    rpmRC rc = RPMRC_OK;
-+    rpm_mode_t mode;
-+    char *rpmio_flags = NULL, *zeros;
-+    const unsigned char *digest;
-+    rpm_loff_t pos, size, pad, validation_pos;
-+    uint32_t offset_ix = 0;
-+    size_t len;
-+    int next = 0;
-+
-+    fdo = fdDup(STDOUT_FILENO);
-+
-+    if (rpmReadPackageRaw(fdi, &sigh, &h))
-+    {
-+        fprintf(stderr, _("Error reading package\n"));
-+        exit(EXIT_FAILURE);
-+    }
-+
-+    if (rpmLeadWrite(fdo, h))
-+    {
-+        fprintf(
-+            stderr,
-+            _("Unable to write package lead: %s\n"),
-+            Fstrerror(fdo)
-+        );
-+        exit(EXIT_FAILURE);
-+    }
-+
-+    if (rpmWriteSignature(fdo, sigh))
-+    {
-+        fprintf(stderr, _("Unable to write signature: %s\n"), Fstrerror(fdo));
-+        exit(EXIT_FAILURE);
-+    }
-+
-+    if (headerWrite(fdo, h, HEADER_MAGIC_YES))
-+    {
-+        fprintf(stderr, _("Unable to write headers: %s\n"), Fstrerror(fdo));
-+        exit(EXIT_FAILURE);
-+    }
-+
-+    /* Retrieve payload size and compression type. */
-+    {	const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
-+        rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL);
-+    }
-+
-+    gzdi = Fdopen(fdi, rpmio_flags);	/* XXX gzdi == fdi */
-+    free(rpmio_flags);
-+
-+    if (gzdi == NULL)
-+    {
-+        fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));
-+        exit(EXIT_FAILURE);
-+    }
-+
-+    rpmfiles files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER);
-+    rpmfi fi = rpmfiNewArchiveReader(
-+        gzdi,
-+        files,
-+        RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST
-+    );
-+
-+    /* this is encoded in the file format, so needs to be fixed size (for
-+        now?)
-+    */
-+    diglen = (uint32_t)rpmDigestLength(rpmfiDigestAlgo(fi));
-+    digestSet ds = digestSetCreate(
-+        rpmfiFC(fi),
-+        digestSetHash,
-+        digestSetCmp,
-+        NULL
-+    );
-+    struct digestoffset offsets[rpmfiFC(fi)];
-+    pos = RPMLEAD_SIZE + headerSizeof(sigh, HEADER_MAGIC_YES);
-+
-+    /* main headers are aligned to 8 byte boundry */
-+    pos += pad_to(pos, 8);
-+    pos += headerSizeof(h, HEADER_MAGIC_YES);
-+
-+    zeros = xcalloc(fundamental_block_size, 1);
-+
-+    while (next >= 0)
-+    {
-+        next = rpmfiNext(fi);
-+        if (next == RPMERR_ITER_END)
-+        {
-+            rc = RPMRC_OK;
-+            break;
-+        }
-+        mode = rpmfiFMode(fi);
-+        if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi))
-+        {
-+            /* not a regular file, or the archive doesn't contain any content for
-+               this entry
-+            */
-+            continue;
-+        }
-+        digest = rpmfiFDigest(fi, NULL, NULL);
-+        if (digestSetGetEntry(ds, digest, NULL))
-+        {
-+            /* This specific digest has already been included, so skip it */
-+            continue;
-+        }
-+        pad = pad_to(pos, fundamental_block_size);
-+        if (Fwrite(zeros, sizeof(char), pad, fdo) != pad)
-+        {
-+            fprintf(stderr, _("Unable to write padding\n"));
-+            rc = RPMRC_FAIL;
-+            goto exit;
-+        }
-+        /* round up to next fundamental_block_size */
-+        pos += pad;
-+        digestSetAddEntry(ds, digest);
-+        offsets[offset_ix].digest = digest;
-+        offsets[offset_ix].pos = pos;
-+        offset_ix++;
-+        size = rpmfiFSize(fi);
-+        rc = rpmfiArchiveReadToFile(fi, fdo, 0);
-+        if (rc != RPMRC_OK)
-+        {
-+            fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc);
-+            goto exit;
-+        }
-+        pos += size;
-+    }
-+    Fclose(gzdi);	/* XXX gzdi == fdi */
-+
-+    qsort(
-+        offsets,
-+        (size_t)offset_ix,
-+        sizeof(struct digestoffset),
-+        digestoffsetCmp
-+    );
-+
-+    len = sizeof(offset_ix);
-+    if (Fwrite(&offset_ix, len, 1, fdo) != len)
-+    {
-+        fprintf(stderr, _("Unable to write length of table\n"));
-+        rc = RPMRC_FAIL;
-+        goto exit;
-+    }
-+    len = sizeof(diglen);
-+    if (Fwrite(&diglen, len, 1, fdo) != len)
-+    {
-+        fprintf(stderr, _("Unable to write length of digest\n"));
-+        rc = RPMRC_FAIL;
-+        goto exit;
-+    }
-+    len = sizeof(rpm_loff_t);
-+    for (int x = 0; x < offset_ix; x++)
-+    {
-+        if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen)
-+        {
-+            fprintf(stderr, _("Unable to write digest\n"));
-+            rc = RPMRC_FAIL;
-+            goto exit;
-+        }
-+        if (Fwrite(&offsets[x].pos, len, 1, fdo) != len)
-+        {
-+            fprintf(stderr, _("Unable to write offset\n"));
-+            rc = RPMRC_FAIL;
-+            goto exit;
-+        }
-+    }
-+    validation_pos = (
-+        pos + sizeof(offset_ix) + sizeof(diglen) +
-+        offset_ix * (diglen + sizeof(rpm_loff_t))
-+    );
-+
-+    ssize_t validation_len = ufdCopy(validationi, fdo);
-+    if (validation_len == -1)
-+    {
-+        fprintf(stderr, _("digest table ufdCopy failed\n"));
-+        rc = RPMRC_FAIL;
-+        goto exit;
-+    }
-+    /* add more padding so the last file can be cloned. It doesn't matter that
-+       the table and validation etc are in this space. In fact, it's pretty
-+       efficient if it is
-+    */
-+
-+    pad = pad_to(
-+        (
-+            validation_pos + validation_len + 2 * sizeof(rpm_loff_t) +
-+            sizeof(uint64_t)
-+        ),
-+        fundamental_block_size
-+    );
-+    if (Fwrite(zeros, sizeof(char), pad, fdo) != pad)
-+    {
-+        fprintf(stderr, _("Unable to write final padding\n"));
-+        rc = RPMRC_FAIL;
-+        goto exit;
-+    }
-+    zeros = _free(zeros);
-+    if (Fwrite(&pos, len, 1, fdo) != len)
-+    {
-+        fprintf(stderr, _("Unable to write offset of digest table\n"));
-+        rc = RPMRC_FAIL;
-+        goto exit;
-+    }
-+    if (Fwrite(&validation_pos, len, 1, fdo) != len)
-+    {
-+        fprintf(stderr, _("Unable to write offset of validation table\n"));
-+        rc = RPMRC_FAIL;
-+        goto exit;
-+    }
-+    uint64_t magic = MAGIC;
-+    len = sizeof(magic);
-+    if (Fwrite(&magic, len, 1, fdo) != len)
-+    {
-+        fprintf(stderr, _("Unable to write magic\n"));
-+        rc = RPMRC_FAIL;
-+        goto exit;
-+    }
-+
-+exit:
-+    rpmfilesFree(files);
-+    rpmfiFree(fi);
-+    headerFree(h);
-+    return rc;
-+}
-+
-+int main(int argc, char *argv[])
-+{
-+    rpmRC rc;
-+    int cprc = 0;
-+    uint8_t algos[argc - 1];
-+    int mainpipefd[2];
-+    int metapipefd[2];
-+    pid_t cpid, w;
-+    int wstatus;
-+
-+    xsetprogname(argv[0]);	/* Portability call -- see system.h */
-+    rpmReadConfigFiles(NULL, NULL);
-+
-+    if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help")))
-+    {
-+        fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]);
-+        exit(EXIT_FAILURE);
-+    }
-+
-+    if (argc == 1)
-+    {
-+        fprintf(
-+            stderr,
-+            _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")
-+        );
-+        exit(EXIT_FAILURE);
-+    }
-+
-+    for (int x = 0; x < (argc - 1); x++)
-+    {
-+        if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0)
-+        {
-+            fprintf(
-+                stderr,
-+                _("Unable to resolve '%s' as a digest algorithm, exiting\n"),
-+                argv[x + 1]
-+            );
-+            exit(EXIT_FAILURE);
-+        }
-+    }
-+
-+
-+    if (pipe(mainpipefd) == -1)
-+    {
-+        fprintf(stderr, _("Main pipe failure\n"));
-+        exit(EXIT_FAILURE);
-+    }
-+    if (pipe(metapipefd) == -1)
-+    {
-+        fprintf(stderr, _("Meta pipe failure\n"));
-+        exit(EXIT_FAILURE);
-+    }
-+    cpid = fork();
-+    if (cpid == 0)
-+    {
-+        /* child: digestor */
-+        close(mainpipefd[0]);
-+        close(metapipefd[0]);
-+        FD_t fdi = fdDup(STDIN_FILENO);
-+        FD_t fdo = fdDup(mainpipefd[1]);
-+        FD_t validationo = fdDup(metapipefd[1]);
-+        rc = digestor(fdi, fdo, validationo, algos, argc - 1);
-+        Fclose(validationo);
-+        Fclose(fdo);
-+        Fclose(fdi);
-+    } else {
-+        /* parent: main program */
-+        close(mainpipefd[1]);
-+        close(metapipefd[1]);
-+        FD_t fdi = fdDup(mainpipefd[0]);
-+        FD_t validationi = fdDup(metapipefd[0]);
-+        rc = process_package(fdi, validationi);
-+        Fclose(validationi);
-+        /* fdi is normally closed through the stacked file gzdi in the function. */
-+        /* wait for child process (digestor for stdin) to complete. */
-+        if (rc != RPMRC_OK)
-+        {
-+            if (kill(cpid, SIGTERM) != 0)
-+            {
-+                fprintf(
-+                    stderr,
-+                    _("Failed to kill digest process when main process failed: %s\n"),
-+                    strerror(errno)
-+                );
-+            }
-+        }
-+        w = waitpid(cpid, &wstatus, 0);
-+        if (w == -1)
-+        {
-+            fprintf(stderr, _("waitpid failed\n"));
-+            cprc = EXIT_FAILURE;
-+        } else if (WIFEXITED(wstatus))
-+        {
-+            cprc = WEXITSTATUS(wstatus);
-+            if (cprc != 0)
-+            {
-+                fprintf(
-+                    stderr,
-+                    _("Digest process non-zero exit code %d\n"),
-+                    cprc
-+                );
-+            }
-+        } else if (WIFSIGNALED(wstatus))
-+        {
-+            fprintf(
-+                stderr,
-+                _("Digest process was terminated with a signal: %d\n"),
-+                WTERMSIG(wstatus)
-+            );
-+            cprc = EXIT_FAILURE;
-+        } else
-+        {
-+            /* don't think this can happen, but covering all bases */
-+            fprintf(stderr, _("Unhandled circumstance in waitpid\n"));
-+            cprc = EXIT_FAILURE;
-+        }
-+        if (cprc != EXIT_SUCCESS)
-+        {
-+            rc = RPMRC_FAIL;
-+        }
-+    }
-+    if (rc != RPMRC_OK)
-+    {
-+        /* translate rpmRC into generic failure return code. */
-+        return EXIT_FAILURE;
-+    }
-+    return EXIT_SUCCESS;
-+}
-diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c
-index d1d2e7de3..2b58de167 100644
---- a/rpmio/rpmpgp.c
-+++ b/rpmio/rpmpgp.c
-@@ -298,6 +298,16 @@ int pgpValTok(pgpValTbl vs, const char * s, const char * se)
-     return vs->val;
- }
- 
-+int pgpStringVal(pgpValType type, const char *str, uint8_t *val)
-+{
-+    pgpValTbl tbl = pgpValTable(type);
-+    if (tbl == NULL) return -1;
-+    int v = pgpValTok(tbl, str, str + strlen(str));
-+    if (v == -1) return -1;
-+    *val = (uint8_t)v;
-+    return 0;
-+}
-+
- /** \ingroup rpmpgp
-  * Decode length from 1, 2, or 5 octet body length encoding, used in
-  * new format packet headers and V4 signature subpackets.
-diff --git a/rpmio/rpmpgp.h b/rpmio/rpmpgp.h
-index 1614750d6..4626a2efc 100644
---- a/rpmio/rpmpgp.h
-+++ b/rpmio/rpmpgp.h
-@@ -973,6 +973,15 @@ typedef rpmFlags rpmDigestFlags;
-  */
- const char * pgpValString(pgpValType type, uint8_t val);
- 
-+/** \ingroup rpmpgp
-+ * Return  OpenPGP value for a string.
-+ * @param type		type of value
-+ * @param str		string to lookup
-+ * @param[out] val  byte value associated with string
-+ * @return		0 on success else -1
-+ */
-+int pgpStringVal(pgpValType type, const char *str, uint8_t *val);
-+
- /** \ingroup rpmpgp
-  * Return (native-endian) integer from big-endian representation.
-  * @param s		pointer to big-endian integer
-
-From 9de362d128634768543e1999763fb1371313c40d Mon Sep 17 00:00:00 2001
-From: Matthew Almond <malmond@fb.com>
-Date: Sun, 31 Jan 2021 12:30:33 -0800
-Subject: [PATCH 2/4] Remove use of bool type for consistency
-
----
- lib/fsm.c | 9 ++++-----
- 1 file changed, 4 insertions(+), 5 deletions(-)
-
-diff --git a/lib/fsm.c b/lib/fsm.c
-index 03a716474..7966ac2f1 100644
---- a/lib/fsm.c
-+++ b/lib/fsm.c
-@@ -7,7 +7,6 @@
- 
- #include <utime.h>
- #include <errno.h>
--#include <stdbool.h>
- #if WITH_CAP
- #include <sys/capability.h>
- #endif
-@@ -854,10 +853,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
-     char *fpath = NULL;
-     Header h = rpmteHeader(te);
-     const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);
--    bool cpio = true;
-+    int cpio = 1;
- 
-     if (payloadfmt && rstreq(payloadfmt, "clon")) {
--	cpio = false;
-+	cpio = 0;
-     }
-     if (cpio) {
- 	fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
-@@ -907,13 +906,13 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
- 	rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath,
- 				      sb.st_mode, action);
- 	skip = skip || rpmfiFFlags(fi) & RPMFILE_GHOST;
--	bool plugin_contents = false;
-+	int plugin_contents = 0;
- 	switch (rc) {
- 	case RPMRC_OK:
- 	    setFileState(fs, rpmfiFX(fi));
- 	    break;
- 	case RPMRC_PLUGIN_CONTENTS:
--	    plugin_contents = true;
-+	    plugin_contents = 1;
- 	    // reduce reads on cpio to this value. Could be zero if
- 	    // this is from a hard link.
- 	    rc = RPMRC_OK;
-
-From 91f7284e961cdbecdfec5beedbce03ee2f0fbd85 Mon Sep 17 00:00:00 2001
-From: Matthew Almond <malmond@fb.com>
-Date: Sun, 31 Jan 2021 13:51:16 -0800
-Subject: [PATCH 3/4] Match formatting/style of existing code
-
-The existing code contains some variability in formatting. I'm not sure
-if { is meant to be on the end of the line, or on a new line, but I've
-standardized on the former.
-
-The indentation is intended to match the existing convention: 4 column
-indent, but 8 column wide tab characters. This is easy to follow/use in
-vim, but is surprisingly difficult to get right in vscode. I am doing
-this reformat here and now, and future changes will be after this.
-
-I'm keen to fold the patches together, but for now, I'm trying to keep
-the history of #1470 linear so everyone can follow along.
----
- lib/rpmplugins.c  |   6 +-
- plugins/reflink.c | 407 ++++++++++++++++++---------------
- rpm2extents.c     | 562 ++++++++++++++++++++--------------------------
- 3 files changed, 462 insertions(+), 513 deletions(-)
-
-diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c
-index c5084d398..3da3097af 100644
---- a/lib/rpmplugins.c
-+++ b/lib/rpmplugins.c
-@@ -368,9 +368,9 @@ rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char *path,
- 		rc = RPMRC_FAIL;
- 	    } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) {
- 		if (rc == RPMRC_PLUGIN_CONTENTS) {
--		    /*
--		    Another plugin already said it'd handle contents. It's undefined how
--		    these would combine, so treat this as a failure condition.
-+		    /* Another plugin already said it'd handle contents. It's
-+		     * undefined how these would combine, so treat this as a
-+		     * failure condition.
- 		    */
- 		    rc = RPMRC_FAIL;
- 		} else {
-diff --git a/plugins/reflink.c b/plugins/reflink.c
-index d7f19acd9..9eaa87094 100644
---- a/plugins/reflink.c
-+++ b/plugins/reflink.c
-@@ -32,31 +32,32 @@
- #include "lib/rpmhash.H"
- #include "lib/rpmhash.C"
- 
--/*
--We use this in find to indicate a key wasn't found. This is an unrecoverable
--error, but we can at least show a decent error. 0 is never a valid offset
--because it's the offset of the start of the file.
--*/
-+/* We use this in find to indicate a key wasn't found. This is an
-+ * unrecoverable error, but we can at least show a decent error. 0 is never a
-+ * valid offset because it's the offset of the start of the file.
-+ */
- #define NOT_FOUND 0
- 
- #define BUFFER_SIZE (1024 * 128)
- 
--/* magic value at end of file (64 bits) that indicates this is a transcoded rpm */
-+/* magic value at end of file (64 bits) that indicates this is a transcoded
-+ * rpm.
-+ */
- #define MAGIC 3472329499408095051
- 
- struct reflink_state_s {
--  /* Stuff that's used across rpms */
--  long fundamental_block_size;
--  char *buffer;
-+    /* Stuff that's used across rpms */
-+    long fundamental_block_size;
-+    char *buffer;
- 
--  /* stuff that's used/updated per psm */
--  uint32_t keys, keysize;
-+    /* stuff that's used/updated per psm */
-+    uint32_t keys, keysize;
- 
--  // table for current rpm, keys * (keysize + sizeof(rpm_loff_t))
--  unsigned char *table;
--  FD_t fd;
--  rpmfiles files;
--  inodeIndexHash inodeIndexes;
-+    /* table for current rpm, keys * (keysize + sizeof(rpm_loff_t)) */
-+    unsigned char *table;
-+    FD_t fd;
-+    rpmfiles files;
-+    inodeIndexHash inodeIndexes;
- };
- 
- typedef struct reflink_state_s * reflink_state;
-@@ -73,60 +74,62 @@ static unsigned int inodeId(rpm_ino_t a)
- }
- 
- static rpmRC reflink_init(rpmPlugin plugin, rpmts ts) {
--  reflink_state state = rcalloc(1, sizeof(struct reflink_state_s));
-+    reflink_state state = rcalloc(1, sizeof(struct reflink_state_s));
- 
--  /*
--  IOCTL-FICLONERANGE(2): ...Disk filesystems generally require the offset and
--  length arguments to be aligned to the fundamental block size.
-+    /* IOCTL-FICLONERANGE(2): ...Disk filesystems generally require the offset
-+     * and length arguments to be aligned to the fundamental block size.
-+     *
-+     * The value of "fundamental block size" is directly related to the
-+     * system's page size, so we should use that.
-+     */
-+    state->fundamental_block_size = sysconf(_SC_PAGESIZE);
-+    state->buffer = rcalloc(1, BUFFER_SIZE);
-+    rpmPluginSetData(plugin, state);
- 
--  The value of "fundamental block size" is directly related to the system's
--  page size, so we should use that.
--  */
--  state->fundamental_block_size = sysconf(_SC_PAGESIZE);
--  state->buffer = rcalloc(1, BUFFER_SIZE);
--  rpmPluginSetData(plugin, state);
--
--  return RPMRC_OK;
-+    return RPMRC_OK;
- }
- 
- static void reflink_cleanup(rpmPlugin plugin) {
--  reflink_state state = rpmPluginGetData(plugin);
--  free(state->buffer);
--  free(state);
-+    reflink_state state = rpmPluginGetData(plugin);
-+    free(state->buffer);
-+    free(state);
- }
- 
- static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
-     reflink_state state = rpmPluginGetData(plugin);
-     state->fd = rpmteFd(te);
-     if (state->fd == 0) {
--      rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n"));
--      return RPMRC_OK;
-+	rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n"));
-+	return RPMRC_OK;
-     }
-     rpm_loff_t current = Ftell(state->fd);
-     uint64_t magic;
-     if (Fseek(state->fd, -(sizeof(magic)), SEEK_END) < 0) {
--      rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n"));
--      if (Fseek(state->fd, current, SEEK_SET) < 0) {
--        /* yes this gets a bit repetitive */
--        rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n"));
--      }
--      return RPMRC_FAIL;
-+	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n"));
-+	if (Fseek(state->fd, current, SEEK_SET) < 0) {
-+	    /* yes this gets a bit repetitive */
-+	    rpmlog(RPMLOG_ERR,
-+		 _("reflink: unable to seek back to original location\n"));
-+	}
-+	return RPMRC_FAIL;
-     }
-     size_t len = sizeof(magic);
-     if (Fread(&magic, len, 1, state->fd) != len) {
--      rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n"));
--      if (Fseek(state->fd, current, SEEK_SET) < 0) {
--        rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n"));
--      }
--      return RPMRC_FAIL;
-+	rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n"));
-+	if (Fseek(state->fd, current, SEEK_SET) < 0) {
-+	    rpmlog(RPMLOG_ERR,
-+		   _("reflink: unable to seek back to original location\n"));
-+	}
-+	return RPMRC_FAIL;
-     }
-     if (magic != MAGIC) {
--      rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n"));
--      if (Fseek(state->fd, current, SEEK_SET) < 0) {
--        rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n"));
--        return RPMRC_FAIL;
--      }
--      return RPMRC_OK;
-+	rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n"));
-+	if (Fseek(state->fd, current, SEEK_SET) < 0) {
-+	    rpmlog(RPMLOG_ERR,
-+		   _("reflink: unable to seek back to original location\n"));
-+	    return RPMRC_FAIL;
-+	}
-+	return RPMRC_OK;
-     }
-     rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));
-     Header h = rpmteHeader(te);
-@@ -136,53 +139,60 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
-     headerPutString(h, RPMTAG_PAYLOADFORMAT, "clon");
-     headerFree(h);
-     state->files = rpmteFiles(te);
--    /* tail of file contains offset_table, offset_checksums
--       then magic
--    */
-+    /* tail of file contains offset_table, offset_checksums then magic */
-     if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(magic)), SEEK_END) < 0) {
--      rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"), state->fd);
--      return RPMRC_FAIL;
-+	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"),
-+	       state->fd);
-+	return RPMRC_FAIL;
-     }
-     rpm_loff_t table_start;
-     len = sizeof(table_start);
-     if (Fread(&table_start, len, 1, state->fd) != len) {
--      rpmlog(RPMLOG_ERR, _("reflink: unable to read table_start\n"));
--      return RPMRC_FAIL;
-+	rpmlog(RPMLOG_ERR, _("reflink: unable to read table_start\n"));
-+	return RPMRC_FAIL;
-     }
-     if (Fseek(state->fd, table_start, SEEK_SET) < 0) {
--      rpmlog(RPMLOG_ERR, _("reflink: unable to seek to table_start\n"));
--      return RPMRC_FAIL;
-+	rpmlog(RPMLOG_ERR, _("reflink: unable to seek to table_start\n"));
-+	return RPMRC_FAIL;
-     }
-     len = sizeof(state->keys);
-     if (Fread(&state->keys, len, 1, state->fd) != len) {
--      rpmlog(RPMLOG_ERR, _("reflink: unable to read number of keys\n"));
--      return RPMRC_FAIL;
-+	rpmlog(RPMLOG_ERR, _("reflink: unable to read number of keys\n"));
-+	return RPMRC_FAIL;
-     }
-     len = sizeof(state->keysize);
-     if (Fread(&state->keysize, len, 1, state->fd) != len) {
--      rpmlog(RPMLOG_ERR, _("reflink: unable to read keysize\n"));
--      return RPMRC_FAIL;
-+	rpmlog(RPMLOG_ERR, _("reflink: unable to read keysize\n"));
-+	return RPMRC_FAIL;
-     }
--    rpmlog(RPMLOG_DEBUG, _("reflink: table_start=0x%lx, keys=%d, keysize=%d\n"), table_start, state->keys, state->keysize);
--    // now get digest table if there is a reason to have one.
-+    rpmlog(
-+	RPMLOG_DEBUG,
-+	_("reflink: table_start=0x%lx, keys=%d, keysize=%d\n"),
-+	table_start, state->keys, state->keysize
-+    );
-+    /* now get digest table if there is a reason to have one. */
-     if (state->keys == 0 || state->keysize == 0) {
--      // no files (or no digests(!))
--      state->table = NULL;
-+	/* no files (or no digests(!)) */
-+	state->table = NULL;
-     } else {
--      int table_size = state->keys * (state->keysize + sizeof(rpm_loff_t));
--      state->table = rcalloc(1, table_size);
--      if (Fread(state->table, table_size, 1, state->fd) != table_size) {
--        rpmlog(RPMLOG_ERR, _("reflink: unable to read table\n"));
--        return RPMRC_FAIL;
--      }
--      state->inodeIndexes = inodeIndexHashCreate(state->keys, inodeId, inodeCmp, NULL, NULL);
-+	int table_size = state->keys * (state->keysize + sizeof(rpm_loff_t));
-+	state->table = rcalloc(1, table_size);
-+	if (Fread(state->table, table_size, 1, state->fd) != table_size) {
-+	    rpmlog(RPMLOG_ERR, _("reflink: unable to read table\n"));
-+	    return RPMRC_FAIL;
-+	}
-+	state->inodeIndexes = inodeIndexHashCreate(
-+	    state->keys, inodeId, inodeCmp, NULL, NULL
-+	);
-     }
- 
--    // seek back to original location
--    // might not be needed if we seek to offset immediately
-+    /* Seek back to original location.
-+     * Might not be needed if we seek to offset immediately
-+     */
-     if (Fseek(state->fd, current, SEEK_SET) < 0) {
--      rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n"));
--      return RPMRC_FAIL;
-+	rpmlog(RPMLOG_ERR,
-+	       _("reflink: unable to seek back to original location\n"));
-+	return RPMRC_FAIL;
-     }
-     return RPMRC_OK;
- }
-@@ -192,40 +202,45 @@ static rpmRC reflink_psm_post(rpmPlugin plugin, rpmte te, int res)
-     reflink_state state = rpmPluginGetData(plugin);
-     state->files = rpmfilesFree(state->files);
-     if (state->table) {
--      free(state->table);
--      state->table = NULL;
-+	free(state->table);
-+	state->table = NULL;
-     }
-     if (state->inodeIndexes) {
--      inodeIndexHashFree(state->inodeIndexes);
--      state->inodeIndexes = NULL;
-+	inodeIndexHashFree(state->inodeIndexes);
-+	state->inodeIndexes = NULL;
-     }
-     return RPMRC_OK;
- }
- 
- 
--// have a prototype, warnings system
-+/* have a prototype, warnings system */
- rpm_loff_t find(const unsigned char *digest, reflink_state state);
- 
- rpm_loff_t find(const unsigned char *digest, reflink_state state) {
- # if defined(__GNUC__)
--  /* GCC nested function because bsearch's comparison function can't access
--     state-keysize otherwise
--  */
--  int cmpdigest(const void *k1, const void *k2) {
--    rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2);
--    return memcmp(k1, k2, state->keysize);
--  }
-+    /* GCC nested function because bsearch's comparison function can't access
-+     * state-keysize otherwise
-+     */
-+    int cmpdigest(const void *k1, const void *k2) {
-+	rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2);
-+	return memcmp(k1, k2, state->keysize);
-+    }
- # endif
--  rpmlog(RPMLOG_DEBUG, _("reflink: bsearch(key=%p, base=%p, nmemb=%d, size=%lu)\n"), digest, state->table, state->keys, state->keysize + sizeof(rpm_loff_t));
--  char *entry = bsearch(digest, state->table, state->keys, state->keysize + sizeof(rpm_loff_t), cmpdigest);
--  if (entry == NULL) {
--    return NOT_FOUND;
--  }
--  rpm_loff_t offset = *(rpm_loff_t *)(entry + state->keysize);
--  return offset;
-+    rpmlog(RPMLOG_DEBUG,
-+	   _("reflink: bsearch(key=%p, base=%p, nmemb=%d, size=%lu)\n"),
-+	   digest, state->table, state->keys,
-+	   state->keysize + sizeof(rpm_loff_t));
-+    char *entry = bsearch(digest, state->table, state->keys,
-+			  state->keysize + sizeof(rpm_loff_t), cmpdigest);
-+    if (entry == NULL) {
-+	return NOT_FOUND;
-+    }
-+    rpm_loff_t offset = *(rpm_loff_t *)(entry + state->keysize);
-+    return offset;
- }
- 
--static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, mode_t file_mode, rpmFsmOp op)
-+static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,
-+                                  mode_t file_mode, rpmFsmOp op)
- {
-     struct file_clone_range fcr;
-     rpm_loff_t size;
-@@ -234,99 +249,119 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,
- 
-     reflink_state state = rpmPluginGetData(plugin);
-     if (state->table == NULL) {
--        // no table means rpm is not in reflink format, so leave. Now.
--        return RPMRC_OK;
-+	/* no table means rpm is not in reflink format, so leave. Now. */
-+	return RPMRC_OK;
-     }
-     if (op == FA_TOUCH) {
--        // we're not overwriting an existing file
--        return RPMRC_OK;
-+	/* we're not overwriting an existing file. */
-+	return RPMRC_OK;
-     }
-     fcr.dest_offset = 0;
-     if (S_ISREG(file_mode) && !(rpmfiFFlags(fi) & RPMFILE_GHOST)) {
--      rpm_ino_t inode = rpmfiFInode(fi);
--      /* check for hard link entry in table. GetEntry overwrites hlix with the address of the first match */
--      if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL, NULL)) {
--        // entry is in table, use hard link
--        char *fn = rpmfilesFN(state->files, hlix[0]);
--        if (link(fn, path) != 0) {
--          rpmlog(RPMLOG_ERR, _("reflink: Unable to hard link %s -> %s due to %s\n"), fn, path, strerror(errno));
--          free(fn);
--          return RPMRC_FAIL;
--        }
--        free(fn);
--        return RPMRC_PLUGIN_CONTENTS;
--      }
--      /* if we didn't hard link, then we'll track this inode as being created soon */
--      if (rpmfiFNlink(fi) > 1) {
--        /* minor optimization: only store files with more than one link */
--        inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi));
--      }
--      /* derived from wfd_open in fsm.c */
--      mode_t old_umask = umask(0577);
--      dst = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
--      umask(old_umask);
--      if (dst == -1) {
--          rpmlog(RPMLOG_ERR, _("reflink: Unable to open %s for writing due to %s, flags = %x\n"), path, strerror(errno), rpmfiFFlags(fi));
--          return RPMRC_FAIL;
--      }
--      size = rpmfiFSize(fi);
--      if (size > 0) {
--          /* round src_length down to fundamental_block_size multiple */
--          fcr.src_length = size / state->fundamental_block_size * state->fundamental_block_size;
--          if ((size % state->fundamental_block_size) > 0) {
--              /* round up to next fundamental_block_size. We expect the data in the rpm to be similarly padded */
--              fcr.src_length += state->fundamental_block_size;
--          }
--          fcr.src_fd = Fileno(state->fd);
--          if (fcr.src_fd == -1) {
--            close(dst);
--            rpmlog(RPMLOG_ERR, _("reflink: src fd lookup failed\n"));
--            return RPMRC_FAIL;
--          }
--          fcr.src_offset = find(rpmfiFDigest(fi, NULL, NULL), state);
--          if (fcr.src_offset == NOT_FOUND) {
--            close(dst);
--            rpmlog(RPMLOG_ERR, _("reflink: digest not found\n"));
--            return RPMRC_FAIL;
--          }
--          rpmlog(RPMLOG_DEBUG, _("reflink: Reflinking %lu bytes at %lu to %s orig size=%lu, file=%ld\n"), fcr.src_length, fcr.src_offset, path, size, fcr.src_fd);
--          rc = ioctl(dst, FICLONERANGE, &fcr);
--          if (rc) {
--            rpmlog(RPMLOG_WARNING, _("reflink: falling back to copying bits for %s due to %d, %d = %s\n"), path, rc, errno, strerror(errno));
--            if (Fseek(state->fd, fcr.src_offset, SEEK_SET) < 0) {
--                close(dst);
--                rpmlog(RPMLOG_ERR, _("reflink: unable to seek on copying bits\n"));
--                return RPMRC_FAIL;
--            }
--            rpm_loff_t left = size;
--            size_t len, read, written;
--            while (left) {
--              len = (left > BUFFER_SIZE ? BUFFER_SIZE : left);
--              read = Fread(state->buffer, len, 1, state->fd);
--              if (read != len) {
--                close(dst);
--                rpmlog(RPMLOG_ERR, _("reflink: short read on copying bits\n"));
--                return RPMRC_FAIL;
--              }
--              written = write(dst, state->buffer, len);
--              if (read != written) {
--                close(dst);
--                rpmlog(RPMLOG_ERR, _("reflink: short write on copying bits\n"));
--                return RPMRC_FAIL;
--              }
--              left -= len;
--            }
--          } else {
--            /* reflink worked, so truncate */
--            rc = ftruncate(dst, size);
--            if (rc) {
--                rpmlog(RPMLOG_ERR, _("reflink: Unable to truncate %s to %ld due to %s\n"), path, size, strerror(errno));
--                return RPMRC_FAIL;
--            }
--          }
--      }
--      close(dst);
--      return RPMRC_PLUGIN_CONTENTS;
-+	rpm_ino_t inode = rpmfiFInode(fi);
-+	/* check for hard link entry in table. GetEntry overwrites hlix with
-+	 * the address of the first match.
-+	 */
-+	if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL,
-+	                           NULL)) {
-+	    /* entry is in table, use hard link */
-+	    char *fn = rpmfilesFN(state->files, hlix[0]);
-+	    if (link(fn, path) != 0) {
-+		rpmlog(RPMLOG_ERR,
-+		       _("reflink: Unable to hard link %s -> %s due to %s\n"),
-+		       fn, path, strerror(errno));
-+		free(fn);
-+		return RPMRC_FAIL;
-+	    }
-+	    free(fn);
-+	    return RPMRC_PLUGIN_CONTENTS;
-+	}
-+	/* if we didn't hard link, then we'll track this inode as being
-+	 * created soon
-+	 */
-+	if (rpmfiFNlink(fi) > 1) {
-+	    /* minor optimization: only store files with more than one link */
-+	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi));
-+	}
-+	/* derived from wfd_open in fsm.c */
-+	mode_t old_umask = umask(0577);
-+	dst = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
-+	umask(old_umask);
-+	if (dst == -1) {
-+	    rpmlog(RPMLOG_ERR,
-+		   _("reflink: Unable to open %s for writing due to %s, flags = %x\n"),
-+		   path, strerror(errno), rpmfiFFlags(fi));
-+	    return RPMRC_FAIL;
-+	}
-+	size = rpmfiFSize(fi);
-+	if (size > 0) {
-+	    /* round src_length down to fundamental_block_size multiple */
-+	    fcr.src_length = size / state->fundamental_block_size * state->fundamental_block_size;
-+	    if ((size % state->fundamental_block_size) > 0) {
-+		/* round up to next fundamental_block_size. We expect the data
-+		 * in the rpm to be similarly padded.
-+		 */
-+		fcr.src_length += state->fundamental_block_size;
-+	    }
-+	    fcr.src_fd = Fileno(state->fd);
-+	    if (fcr.src_fd == -1) {
-+		close(dst);
-+		rpmlog(RPMLOG_ERR, _("reflink: src fd lookup failed\n"));
-+		return RPMRC_FAIL;
-+	    }
-+	    fcr.src_offset = find(rpmfiFDigest(fi, NULL, NULL), state);
-+	    if (fcr.src_offset == NOT_FOUND) {
-+		close(dst);
-+		rpmlog(RPMLOG_ERR, _("reflink: digest not found\n"));
-+		return RPMRC_FAIL;
-+	    }
-+	    rpmlog(RPMLOG_DEBUG,
-+	           _("reflink: Reflinking %lu bytes at %lu to %s orig size=%lu, file=%ld\n"),
-+		   fcr.src_length, fcr.src_offset, path, size, fcr.src_fd);
-+	    rc = ioctl(dst, FICLONERANGE, &fcr);
-+	    if (rc) {
-+		rpmlog(RPMLOG_WARNING,
-+		       _("reflink: falling back to copying bits for %s due to %d, %d = %s\n"),
-+		       path, rc, errno, strerror(errno));
-+		if (Fseek(state->fd, fcr.src_offset, SEEK_SET) < 0) {
-+		    close(dst);
-+		    rpmlog(RPMLOG_ERR,
-+			   _("reflink: unable to seek on copying bits\n"));
-+		    return RPMRC_FAIL;
-+		}
-+		rpm_loff_t left = size;
-+		size_t len, read, written;
-+		while (left) {
-+		    len = (left > BUFFER_SIZE ? BUFFER_SIZE : left);
-+		    read = Fread(state->buffer, len, 1, state->fd);
-+		    if (read != len) {
-+			close(dst);
-+			rpmlog(RPMLOG_ERR,
-+			       _("reflink: short read on copying bits\n"));
-+			return RPMRC_FAIL;
-+		    }
-+		    written = write(dst, state->buffer, len);
-+		    if (read != written) {
-+			close(dst);
-+			rpmlog(RPMLOG_ERR,
-+			       _("reflink: short write on copying bits\n"));
-+			return RPMRC_FAIL;
-+		    }
-+		    left -= len;
-+		}
-+	    } else {
-+		/* reflink worked, so truncate */
-+		rc = ftruncate(dst, size);
-+		if (rc) {
-+		    rpmlog(RPMLOG_ERR,
-+			   _("reflink: Unable to truncate %s to %ld due to %s\n"),
-+			   path, size, strerror(errno));
-+		     return RPMRC_FAIL;
-+		}
-+	    }
-+	}
-+	close(dst);
-+	return RPMRC_PLUGIN_CONTENTS;
-     }
-     return RPMRC_OK;
- }
-diff --git a/rpm2extents.c b/rpm2extents.c
-index 5662b86a6..c111be0a2 100644
---- a/rpm2extents.c
-+++ b/rpm2extents.c
-@@ -24,7 +24,7 @@
- #include "debug.h"
- 
- /* hash of void * (pointers) to file digests to offsets within output.
--   The length of the key depends on what the FILEDIGESTALGO is.
-+ * The length of the key depends on what the FILEDIGESTALGO is.
-  */
- #undef HASHTYPE
- #undef HTKEYTYPE
-@@ -34,7 +34,9 @@
- #include "lib/rpmhash.H"
- #include "lib/rpmhash.C"
- 
--/* magic value at end of file (64 bits) that indicates this is a transcoded rpm */
-+/* magic value at end of file (64 bits) that indicates this is a transcoded
-+ * rpm.
-+ */
- #define MAGIC 3472329499408095051
- 
- struct digestoffset {
-@@ -64,77 +66,54 @@ static int digestor(
-     int algo;
-     rpmRC rc = RPMRC_FAIL;
- 
--    for (algo = 0; algo < algos_len; algo++)
--    {
--        fdInitDigest(fdi, algos[algo], 0);
-+    for (algo = 0; algo < algos_len; algo++) {
-+	fdInitDigest(fdi, algos[algo], 0);
-     }
-     fdilength = ufdCopy(fdi, fdo);
--    if (fdilength == -1)
--    {
--        fprintf(stderr, _("digest cat failed\n"));
--        goto exit;
-+    if (fdilength == -1) {
-+	fprintf(stderr, _("digest cat failed\n"));
-+	goto exit;
-     }
- 
-     len = sizeof(fdilength);
--    if (Fwrite(&fdilength, len, 1, validationo) != len)
--    {
--        fprintf(stderr, _("Unable to write input length %zd\n"), fdilength);
--        goto exit;
-+    if (Fwrite(&fdilength, len, 1, validationo) != len) {
-+	fprintf(stderr, _("Unable to write input length %zd\n"), fdilength);
-+	goto exit;
-     }
-     len = sizeof(algos_len);
--    if (Fwrite(&algos_len, len, 1, validationo) != len)
--    {
--        fprintf(stderr, _("Unable to write number of validation digests\n"));
--        goto exit;
-+    if (Fwrite(&algos_len, len, 1, validationo) != len) {
-+	fprintf(stderr, _("Unable to write number of validation digests\n"));
-+	goto exit;
-     }
--    for (algo = 0; algo < algos_len; algo++)
--    {
--        fdFiniDigest(fdi, algos[algo], (void **)&filedigest, &filedigest_len, 0);
--
--        algo_name = pgpValString(PGPVAL_HASHALGO, algos[algo]);
--        algo_name_len = (uint32_t)strlen(algo_name);
--        algo_digest_len = (uint32_t)filedigest_len;
--
--        len = sizeof(algo_name_len);
--        if (Fwrite(&algo_name_len, len, 1, validationo) != len)
--        {
--            fprintf(
--                stderr,
--                _("Unable to write validation algo name length\n")
--            );
--            goto exit;
--        }
--        len = sizeof(algo_digest_len);
--        if (Fwrite(&algo_digest_len, len, 1, validationo) != len)
--        {
--            fprintf(
--                stderr,
--                _("Unable to write number of bytes for validation digest\n")
--            );
--            goto exit;
--        }
--        if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len)
--        {
--            fprintf(stderr, _("Unable to write validation algo name\n"));
--            goto exit;
--        }
--        if (
--            Fwrite(
--                filedigest,
--                algo_digest_len,
--                1,
--                validationo
--            ) != algo_digest_len
--        )
--        {
--            fprintf(
--                stderr,
--                _("Unable to write validation digest value %u, %zu\n"),
--                algo_digest_len,
--                filedigest_len
--            );
--            goto exit;
--        }
-+    for (algo = 0; algo < algos_len; algo++) {
-+	fdFiniDigest(fdi, algos[algo], (void **)&filedigest, &filedigest_len, 0);
-+
-+	algo_name = pgpValString(PGPVAL_HASHALGO, algos[algo]);
-+	algo_name_len = (uint32_t)strlen(algo_name);
-+	algo_digest_len = (uint32_t)filedigest_len;
-+
-+	len = sizeof(algo_name_len);
-+	if (Fwrite(&algo_name_len, len, 1, validationo) != len) {
-+	    fprintf(stderr,
-+		    _("Unable to write validation algo name length\n"));
-+	    goto exit;
-+	}
-+	len = sizeof(algo_digest_len);
-+	if (Fwrite(&algo_digest_len, len, 1, validationo) != len) {
-+	    fprintf(stderr,
-+		    _("Unable to write number of bytes for validation digest\n"));
-+	     goto exit;
-+	}
-+	if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) {
-+	    fprintf(stderr, _("Unable to write validation algo name\n"));
-+	    goto exit;
-+	}
-+	if (Fwrite(filedigest, algo_digest_len, 1, validationo ) != algo_digest_len) {
-+	    fprintf(stderr,
-+		    _("Unable to write validation digest value %u, %zu\n"),
-+		    algo_digest_len, filedigest_len);
-+	    goto exit;
-+	}
-     }
-     rc = RPMRC_OK;
- exit:
-@@ -145,23 +124,20 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
- {
-     uint32_t diglen;
-     /* GNU C extension: can use diglen from outer context */
--    int digestSetCmp(const unsigned char * a, const unsigned char * b)
--    {
--        return memcmp(a, b, diglen);
-+    int digestSetCmp(const unsigned char * a, const unsigned char * b) {
-+	return memcmp(a, b, diglen);
-     }
- 
--    unsigned int digestSetHash(const unsigned char * digest)
--    {
-+    unsigned int digestSetHash(const unsigned char * digest) {
-         /* assumes sizeof(unsigned int) < diglen */
-         return *(unsigned int *)digest;
-     }
- 
--    int digestoffsetCmp(const void * a, const void * b)
--    {
--        return digestSetCmp(
--            ((struct digestoffset *)a)->digest,
--            ((struct digestoffset *)b)->digest
--        );
-+    int digestoffsetCmp(const void * a, const void * b) {
-+	return digestSetCmp(
-+	    ((struct digestoffset *)a)->digest,
-+	    ((struct digestoffset *)b)->digest
-+	);
-     }
- 
-     FD_t fdo;
-@@ -179,65 +155,52 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
- 
-     fdo = fdDup(STDOUT_FILENO);
- 
--    if (rpmReadPackageRaw(fdi, &sigh, &h))
--    {
--        fprintf(stderr, _("Error reading package\n"));
--        exit(EXIT_FAILURE);
-+    if (rpmReadPackageRaw(fdi, &sigh, &h)) {
-+	fprintf(stderr, _("Error reading package\n"));
-+	exit(EXIT_FAILURE);
-     }
- 
-     if (rpmLeadWrite(fdo, h))
-     {
--        fprintf(
--            stderr,
--            _("Unable to write package lead: %s\n"),
--            Fstrerror(fdo)
--        );
--        exit(EXIT_FAILURE);
-+	fprintf(stderr, _("Unable to write package lead: %s\n"),
-+		Fstrerror(fdo));
-+	exit(EXIT_FAILURE);
-     }
- 
--    if (rpmWriteSignature(fdo, sigh))
--    {
--        fprintf(stderr, _("Unable to write signature: %s\n"), Fstrerror(fdo));
--        exit(EXIT_FAILURE);
-+    if (rpmWriteSignature(fdo, sigh)) {
-+	fprintf(stderr, _("Unable to write signature: %s\n"), Fstrerror(fdo));
-+	exit(EXIT_FAILURE);
-     }
- 
--    if (headerWrite(fdo, h, HEADER_MAGIC_YES))
--    {
--        fprintf(stderr, _("Unable to write headers: %s\n"), Fstrerror(fdo));
--        exit(EXIT_FAILURE);
-+    if (headerWrite(fdo, h, HEADER_MAGIC_YES)) {
-+	fprintf(stderr, _("Unable to write headers: %s\n"), Fstrerror(fdo));
-+	exit(EXIT_FAILURE);
-     }
- 
-     /* Retrieve payload size and compression type. */
--    {	const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
--        rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL);
-+    {
-+	const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
-+	rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL);
-     }
- 
-     gzdi = Fdopen(fdi, rpmio_flags);	/* XXX gzdi == fdi */
-     free(rpmio_flags);
- 
--    if (gzdi == NULL)
--    {
--        fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));
--        exit(EXIT_FAILURE);
-+    if (gzdi == NULL) {
-+	fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));
-+	exit(EXIT_FAILURE);
-     }
- 
-     rpmfiles files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER);
--    rpmfi fi = rpmfiNewArchiveReader(
--        gzdi,
--        files,
--        RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST
--    );
-+    rpmfi fi = rpmfiNewArchiveReader(gzdi, files,
-+				     RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST);
- 
-     /* this is encoded in the file format, so needs to be fixed size (for
--        now?)
--    */
-+     * now?)
-+     */
-     diglen = (uint32_t)rpmDigestLength(rpmfiDigestAlgo(fi));
--    digestSet ds = digestSetCreate(
--        rpmfiFC(fi),
--        digestSetHash,
--        digestSetCmp,
--        NULL
--    );
-+    digestSet ds = digestSetCreate(rpmfiFC(fi), digestSetHash, digestSetCmp,
-+				   NULL);
-     struct digestoffset offsets[rpmfiFC(fi)];
-     pos = RPMLEAD_SIZE + headerSizeof(sigh, HEADER_MAGIC_YES);
- 
-@@ -247,139 +210,114 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
- 
-     zeros = xcalloc(fundamental_block_size, 1);
- 
--    while (next >= 0)
--    {
--        next = rpmfiNext(fi);
--        if (next == RPMERR_ITER_END)
--        {
--            rc = RPMRC_OK;
--            break;
--        }
--        mode = rpmfiFMode(fi);
--        if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi))
--        {
--            /* not a regular file, or the archive doesn't contain any content for
--               this entry
--            */
--            continue;
--        }
--        digest = rpmfiFDigest(fi, NULL, NULL);
--        if (digestSetGetEntry(ds, digest, NULL))
--        {
--            /* This specific digest has already been included, so skip it */
--            continue;
--        }
--        pad = pad_to(pos, fundamental_block_size);
--        if (Fwrite(zeros, sizeof(char), pad, fdo) != pad)
--        {
--            fprintf(stderr, _("Unable to write padding\n"));
--            rc = RPMRC_FAIL;
--            goto exit;
--        }
--        /* round up to next fundamental_block_size */
--        pos += pad;
--        digestSetAddEntry(ds, digest);
--        offsets[offset_ix].digest = digest;
--        offsets[offset_ix].pos = pos;
--        offset_ix++;
--        size = rpmfiFSize(fi);
--        rc = rpmfiArchiveReadToFile(fi, fdo, 0);
--        if (rc != RPMRC_OK)
--        {
--            fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc);
--            goto exit;
--        }
--        pos += size;
-+    while (next >= 0) {
-+	next = rpmfiNext(fi);
-+	if (next == RPMERR_ITER_END) {
-+	    rc = RPMRC_OK;
-+	    break;
-+	}
-+	mode = rpmfiFMode(fi);
-+	if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi)) {
-+	    /* not a regular file, or the archive doesn't contain any content
-+	     * for this entry.
-+	    */
-+	    continue;
-+	}
-+	digest = rpmfiFDigest(fi, NULL, NULL);
-+	if (digestSetGetEntry(ds, digest, NULL)) {
-+	    /* This specific digest has already been included, so skip it. */
-+	    continue;
-+	}
-+	pad = pad_to(pos, fundamental_block_size);
-+	if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) {
-+	    fprintf(stderr, _("Unable to write padding\n"));
-+	    rc = RPMRC_FAIL;
-+	    goto exit;
-+	}
-+	/* round up to next fundamental_block_size */
-+	pos += pad;
-+	digestSetAddEntry(ds, digest);
-+	offsets[offset_ix].digest = digest;
-+	offsets[offset_ix].pos = pos;
-+	offset_ix++;
-+	size = rpmfiFSize(fi);
-+	rc = rpmfiArchiveReadToFile(fi, fdo, 0);
-+	if (rc != RPMRC_OK) {
-+	    fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc);
-+	    goto exit;
-+	}
-+	pos += size;
-     }
-     Fclose(gzdi);	/* XXX gzdi == fdi */
- 
--    qsort(
--        offsets,
--        (size_t)offset_ix,
--        sizeof(struct digestoffset),
--        digestoffsetCmp
--    );
-+    qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset),
-+	  digestoffsetCmp);
- 
-     len = sizeof(offset_ix);
--    if (Fwrite(&offset_ix, len, 1, fdo) != len)
--    {
--        fprintf(stderr, _("Unable to write length of table\n"));
--        rc = RPMRC_FAIL;
--        goto exit;
-+    if (Fwrite(&offset_ix, len, 1, fdo) != len) {
-+	fprintf(stderr, _("Unable to write length of table\n"));
-+	rc = RPMRC_FAIL;
-+	goto exit;
-     }
-     len = sizeof(diglen);
--    if (Fwrite(&diglen, len, 1, fdo) != len)
--    {
--        fprintf(stderr, _("Unable to write length of digest\n"));
--        rc = RPMRC_FAIL;
--        goto exit;
-+    if (Fwrite(&diglen, len, 1, fdo) != len) {
-+	fprintf(stderr, _("Unable to write length of digest\n"));
-+	rc = RPMRC_FAIL;
-+	goto exit;
-     }
-     len = sizeof(rpm_loff_t);
--    for (int x = 0; x < offset_ix; x++)
--    {
--        if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen)
--        {
--            fprintf(stderr, _("Unable to write digest\n"));
--            rc = RPMRC_FAIL;
--            goto exit;
--        }
--        if (Fwrite(&offsets[x].pos, len, 1, fdo) != len)
--        {
--            fprintf(stderr, _("Unable to write offset\n"));
--            rc = RPMRC_FAIL;
--            goto exit;
--        }
-+    for (int x = 0; x < offset_ix; x++) {
-+	if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) {
-+	    fprintf(stderr, _("Unable to write digest\n"));
-+	    rc = RPMRC_FAIL;
-+	    goto exit;
-+	}
-+	if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) {
-+	    fprintf(stderr, _("Unable to write offset\n"));
-+	    rc = RPMRC_FAIL;
-+	    goto exit;
-+	}
-     }
-     validation_pos = (
--        pos + sizeof(offset_ix) + sizeof(diglen) +
--        offset_ix * (diglen + sizeof(rpm_loff_t))
-+	pos + sizeof(offset_ix) + sizeof(diglen) +
-+	offset_ix * (diglen + sizeof(rpm_loff_t))
-     );
- 
-     ssize_t validation_len = ufdCopy(validationi, fdo);
--    if (validation_len == -1)
--    {
--        fprintf(stderr, _("digest table ufdCopy failed\n"));
--        rc = RPMRC_FAIL;
--        goto exit;
-+    if (validation_len == -1) {
-+	fprintf(stderr, _("digest table ufdCopy failed\n"));
-+	rc = RPMRC_FAIL;
-+	goto exit;
-     }
-     /* add more padding so the last file can be cloned. It doesn't matter that
--       the table and validation etc are in this space. In fact, it's pretty
--       efficient if it is
-+     * the table and validation etc are in this space. In fact, it's pretty
-+     * efficient if it is.
-     */
- 
--    pad = pad_to(
--        (
--            validation_pos + validation_len + 2 * sizeof(rpm_loff_t) +
--            sizeof(uint64_t)
--        ),
--        fundamental_block_size
--    );
--    if (Fwrite(zeros, sizeof(char), pad, fdo) != pad)
--    {
--        fprintf(stderr, _("Unable to write final padding\n"));
--        rc = RPMRC_FAIL;
--        goto exit;
-+    pad = pad_to((validation_pos + validation_len + 2 * sizeof(rpm_loff_t) +
-+		 sizeof(uint64_t)), fundamental_block_size);
-+    if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) {
-+	fprintf(stderr, _("Unable to write final padding\n"));
-+	rc = RPMRC_FAIL;
-+	goto exit;
-     }
-     zeros = _free(zeros);
--    if (Fwrite(&pos, len, 1, fdo) != len)
--    {
--        fprintf(stderr, _("Unable to write offset of digest table\n"));
--        rc = RPMRC_FAIL;
--        goto exit;
-+    if (Fwrite(&pos, len, 1, fdo) != len) {
-+	fprintf(stderr, _("Unable to write offset of digest table\n"));
-+	rc = RPMRC_FAIL;
-+	goto exit;
-     }
--    if (Fwrite(&validation_pos, len, 1, fdo) != len)
--    {
--        fprintf(stderr, _("Unable to write offset of validation table\n"));
--        rc = RPMRC_FAIL;
--        goto exit;
-+    if (Fwrite(&validation_pos, len, 1, fdo) != len) {
-+	fprintf(stderr, _("Unable to write offset of validation table\n"));
-+	rc = RPMRC_FAIL;
-+	goto exit;
-     }
-     uint64_t magic = MAGIC;
-     len = sizeof(magic);
--    if (Fwrite(&magic, len, 1, fdo) != len)
--    {
--        fprintf(stderr, _("Unable to write magic\n"));
--        rc = RPMRC_FAIL;
--        goto exit;
-+    if (Fwrite(&magic, len, 1, fdo) != len) {
-+	fprintf(stderr, _("Unable to write magic\n"));
-+	rc = RPMRC_FAIL;
-+	goto exit;
-     }
- 
- exit:
-@@ -389,8 +327,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
-     return rc;
- }
- 
--int main(int argc, char *argv[])
--{
-+int main(int argc, char *argv[]) {
-     rpmRC rc;
-     int cprc = 0;
-     uint8_t algos[argc - 1];
-@@ -402,118 +339,95 @@ int main(int argc, char *argv[])
-     xsetprogname(argv[0]);	/* Portability call -- see system.h */
-     rpmReadConfigFiles(NULL, NULL);
- 
--    if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help")))
--    {
--        fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]);
--        exit(EXIT_FAILURE);
-+    if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) {
-+	fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]);
-+	exit(EXIT_FAILURE);
-     }
- 
--    if (argc == 1)
--    {
--        fprintf(
--            stderr,
--            _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")
--        );
--        exit(EXIT_FAILURE);
-+    if (argc == 1) {
-+	fprintf(stderr,
-+		_("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));
-+	exit(EXIT_FAILURE);
-     }
- 
--    for (int x = 0; x < (argc - 1); x++)
--    {
--        if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0)
--        {
--            fprintf(
--                stderr,
--                _("Unable to resolve '%s' as a digest algorithm, exiting\n"),
--                argv[x + 1]
--            );
--            exit(EXIT_FAILURE);
--        }
-+    for (int x = 0; x < (argc - 1); x++) {
-+	if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0)
-+	{
-+	    fprintf(stderr,
-+		    _("Unable to resolve '%s' as a digest algorithm, exiting\n"),
-+		    argv[x + 1]);
-+	    exit(EXIT_FAILURE);
-+	}
-     }
- 
- 
--    if (pipe(mainpipefd) == -1)
--    {
--        fprintf(stderr, _("Main pipe failure\n"));
--        exit(EXIT_FAILURE);
-+    if (pipe(mainpipefd) == -1) {
-+	fprintf(stderr, _("Main pipe failure\n"));
-+	exit(EXIT_FAILURE);
-     }
--    if (pipe(metapipefd) == -1)
--    {
--        fprintf(stderr, _("Meta pipe failure\n"));
--        exit(EXIT_FAILURE);
-+    if (pipe(metapipefd) == -1) {
-+	fprintf(stderr, _("Meta pipe failure\n"));
-+	exit(EXIT_FAILURE);
-     }
-     cpid = fork();
--    if (cpid == 0)
--    {
--        /* child: digestor */
--        close(mainpipefd[0]);
--        close(metapipefd[0]);
--        FD_t fdi = fdDup(STDIN_FILENO);
--        FD_t fdo = fdDup(mainpipefd[1]);
--        FD_t validationo = fdDup(metapipefd[1]);
--        rc = digestor(fdi, fdo, validationo, algos, argc - 1);
--        Fclose(validationo);
--        Fclose(fdo);
--        Fclose(fdi);
-+    if (cpid == 0) {
-+	/* child: digestor */
-+	close(mainpipefd[0]);
-+	close(metapipefd[0]);
-+	FD_t fdi = fdDup(STDIN_FILENO);
-+	FD_t fdo = fdDup(mainpipefd[1]);
-+	FD_t validationo = fdDup(metapipefd[1]);
-+	rc = digestor(fdi, fdo, validationo, algos, argc - 1);
-+	Fclose(validationo);
-+	Fclose(fdo);
-+	Fclose(fdi);
-     } else {
--        /* parent: main program */
--        close(mainpipefd[1]);
--        close(metapipefd[1]);
--        FD_t fdi = fdDup(mainpipefd[0]);
--        FD_t validationi = fdDup(metapipefd[0]);
--        rc = process_package(fdi, validationi);
--        Fclose(validationi);
--        /* fdi is normally closed through the stacked file gzdi in the function. */
--        /* wait for child process (digestor for stdin) to complete. */
--        if (rc != RPMRC_OK)
--        {
--            if (kill(cpid, SIGTERM) != 0)
--            {
--                fprintf(
--                    stderr,
--                    _("Failed to kill digest process when main process failed: %s\n"),
--                    strerror(errno)
--                );
--            }
--        }
--        w = waitpid(cpid, &wstatus, 0);
--        if (w == -1)
--        {
--            fprintf(stderr, _("waitpid failed\n"));
--            cprc = EXIT_FAILURE;
--        } else if (WIFEXITED(wstatus))
--        {
--            cprc = WEXITSTATUS(wstatus);
--            if (cprc != 0)
--            {
--                fprintf(
--                    stderr,
--                    _("Digest process non-zero exit code %d\n"),
--                    cprc
--                );
--            }
--        } else if (WIFSIGNALED(wstatus))
--        {
--            fprintf(
--                stderr,
--                _("Digest process was terminated with a signal: %d\n"),
--                WTERMSIG(wstatus)
--            );
--            cprc = EXIT_FAILURE;
--        } else
--        {
--            /* don't think this can happen, but covering all bases */
--            fprintf(stderr, _("Unhandled circumstance in waitpid\n"));
--            cprc = EXIT_FAILURE;
--        }
--        if (cprc != EXIT_SUCCESS)
--        {
--            rc = RPMRC_FAIL;
--        }
-+	/* parent: main program */
-+	close(mainpipefd[1]);
-+	close(metapipefd[1]);
-+	FD_t fdi = fdDup(mainpipefd[0]);
-+	FD_t validationi = fdDup(metapipefd[0]);
-+	rc = process_package(fdi, validationi);
-+	Fclose(validationi);
-+	/* fdi is normally closed through the stacked file gzdi in the
-+	 * function.
-+	 * Wait for child process (digestor for stdin) to complete.
-+	 */
-+	if (rc != RPMRC_OK) {
-+	    if (kill(cpid, SIGTERM) != 0) {
-+		fprintf(stderr,
-+		        _("Failed to kill digest process when main process failed: %s\n"),
-+			strerror(errno));
-+	    }
-+	}
-+	w = waitpid(cpid, &wstatus, 0);
-+	if (w == -1) {
-+	    fprintf(stderr, _("waitpid failed\n"));
-+	    cprc = EXIT_FAILURE;
-+	} else if (WIFEXITED(wstatus)) {
-+	    cprc = WEXITSTATUS(wstatus);
-+	    if (cprc != 0) {
-+		fprintf(stderr,
-+			_("Digest process non-zero exit code %d\n"),
-+			cprc);
-+	    }
-+	} else if (WIFSIGNALED(wstatus)) {
-+	    fprintf(stderr,
-+		    _("Digest process was terminated with a signal: %d\n"),
-+		    WTERMSIG(wstatus));
-+	    cprc = EXIT_FAILURE;
-+	} else {
-+	    /* Don't think this can happen, but covering all bases */
-+	    fprintf(stderr, _("Unhandled circumstance in waitpid\n"));
-+	    cprc = EXIT_FAILURE;
-+	}
-+	if (cprc != EXIT_SUCCESS) {
-+	    rc = RPMRC_FAIL;
-+	}
-     }
--    if (rc != RPMRC_OK)
--    {
--        /* translate rpmRC into generic failure return code. */
--        return EXIT_FAILURE;
-+    if (rc != RPMRC_OK) {
-+	/* translate rpmRC into generic failure return code. */
-+	return EXIT_FAILURE;
-     }
-     return EXIT_SUCCESS;
- }
-
-From 19694b76508d83bc83201441ff2c2721d45c4d1d Mon Sep 17 00:00:00 2001
-From: Matthew Almond <malmond@fb.com>
-Date: Sun, 31 Jan 2021 15:24:25 -0800
-Subject: [PATCH 4/4] Fix printf formatting in reflink.c
-
-There were some mismatches on field "sizes". This should eliminate the
-error messages.
----
- plugins/reflink.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/plugins/reflink.c b/plugins/reflink.c
-index 9eaa87094..513887604 100644
---- a/plugins/reflink.c
-+++ b/plugins/reflink.c
-@@ -316,7 +316,7 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,
- 		return RPMRC_FAIL;
- 	    }
- 	    rpmlog(RPMLOG_DEBUG,
--	           _("reflink: Reflinking %lu bytes at %lu to %s orig size=%lu, file=%ld\n"),
-+	           _("reflink: Reflinking %llu bytes at %llu to %s orig size=%ld, file=%lld\n"),
- 		   fcr.src_length, fcr.src_offset, path, size, fcr.src_fd);
- 	    rc = ioctl(dst, FICLONERANGE, &fcr);
- 	    if (rc) {
diff --git a/SOURCES/master...cow.diff b/SOURCES/master...cow.diff
new file mode 100644
index 0000000..0e4160c
--- /dev/null
+++ b/SOURCES/master...cow.diff
@@ -0,0 +1,1176 @@
+diff --git a/Makefile.am b/Makefile.am
+index e5c75d7b4..288668819 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -99,7 +99,7 @@ pkginclude_HEADERS += build/rpmfc.h
+ pkginclude_HEADERS += build/rpmspec.h
+ 
+ 
+-bin_PROGRAMS =		rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec
++bin_PROGRAMS =		rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec rpm2extents
+ if WITH_ARCHIVE
+ bin_PROGRAMS += 	rpm2archive 
+ endif
+@@ -154,6 +154,10 @@ rpm2cpio_SOURCES =	rpm2cpio.c debug.h system.h
+ rpm2cpio_LDADD =	lib/librpm.la rpmio/librpmio.la
+ rpm2cpio_LDADD +=	@WITH_POPT_LIB@
+ 
++rpm2extents_SOURCES =	rpm2extents.c debug.h system.h
++rpm2extents_LDADD =	lib/librpm.la rpmio/librpmio.la
++rpm2extents_LDADD +=	@WITH_POPT_LIB@
++
+ rpm2archive_SOURCES =	rpm2archive.c debug.h system.h
+ rpm2archive_LDADD =	lib/librpm.la rpmio/librpmio.la
+ rpm2archive_LDADD +=	@WITH_POPT_LIB@ @WITH_ARCHIVE_LIB@
+diff --git a/lib/depends.c b/lib/depends.c
+index 30234df3d..8998afcd3 100644
+--- a/lib/depends.c
++++ b/lib/depends.c
+@@ -81,6 +81,8 @@ static rpmRC headerCheckPayloadFormat(Header h) {
+      */
+     if (!payloadfmt) return rc;
+ 
++    if (rstreq(payloadfmt, "clon")) return rc;
++
+     if (!rstreq(payloadfmt, "cpio")) {
+         char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
+         if (payloadfmt && rstreq(payloadfmt, "drpm")) {
+diff --git a/lib/fsm.c b/lib/fsm.c
+index 935a0a5c6..feda3750c 100644
+--- a/lib/fsm.c
++++ b/lib/fsm.c
+@@ -19,6 +19,7 @@
+ 
+ #include "rpmio/rpmio_internal.h"	/* fdInit/FiniDigest */
+ #include "lib/fsm.h"
++#include "lib/rpmlib.h"
+ #include "lib/rpmte_internal.h"	/* XXX rpmfs */
+ #include "lib/rpmplugins.h"	/* rpm plugins hooks */
+ #include "lib/rpmug.h"
+@@ -52,6 +53,7 @@ struct filedata_s {
+     int stage;
+     int setmeta;
+     int skip;
++    int plugin_contents;
+     rpmFileAction action;
+     const char *suffix;
+     char *fpath;
+@@ -891,6 +893,14 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+     struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata));
+     struct filedata_s *firstlink = NULL;
+ 
++    Header h = rpmteHeader(te);
++    const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT);
++    int cpio = 1;
++
++    if (payloadfmt && rstreq(payloadfmt, "clon")) {
++	cpio = 0;
++    }
++
+     /* transaction id used for temporary path suffix while installing */
+     rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));
+ 
+@@ -911,12 +921,23 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+ 	/* Remap file perms, owner, and group. */
+ 	rc = rpmfiStat(fi, 1, &fp->sb);
+ 
+-	setFileState(fs, fx);
+ 	fsmDebug(fp->fpath, fp->action, &fp->sb);
+ 
+ 	/* Run fsm file pre hook for all plugins */
+ 	rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath,
+ 				      fp->sb.st_mode, fp->action);
++	fp->plugin_contents = 0;
++	switch (rc) {
++	case RPMRC_OK:
++	    setFileState(fs, fx);
++	    break;
++	case RPMRC_PLUGIN_CONTENTS:
++	    fp->plugin_contents = 1;
++	    // reduce reads on cpio to this value. Could be zero if
++	    // this is from a hard link.
++	    rc = RPMRC_OK;
++	    break;
++	}
+ 	fp->stage = FILE_PRE;
+     }
+     fi = rpmfiFree(fi);
+@@ -924,10 +945,14 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+     if (rc)
+ 	goto exit;
+ 
+-    fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
+-    if (fi == NULL) {
+-        rc = RPMERR_BAD_MAGIC;
+-        goto exit;
++    if (cpio) {
++	fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
++	if (fi == NULL) {
++	    rc = RPMERR_BAD_MAGIC;
++	    goto exit;
++	}
++    } else {
++	fi = rpmfilesIter(files, RPMFI_ITER_FWD);
+     }
+ 
+     /* Detect and create directories not explicitly in package. */
+@@ -969,8 +994,12 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+ 
+             if (S_ISREG(fp->sb.st_mode)) {
+ 		if (rc == RPMERR_ENOENT) {
+-		    rc = fsmMkfile(fi, fp, files, psm, nodigest,
+-				   &firstlink, &firstlinkfile);
++		    if(fp->plugin_contents) {
++			rc = RPMRC_OK;
++		    }else {
++			rc = fsmMkfile(fi, fp, files, psm, nodigest,
++			    &firstlink, &firstlinkfile);
++		    }
+ 		}
+             } else if (S_ISDIR(fp->sb.st_mode)) {
+                 if (rc == RPMERR_ENOENT) {
+@@ -1078,6 +1107,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+     rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST));
+ 
+ exit:
++    h = headerFree(h);
+     fi = rpmfiFree(fi);
+     Fclose(payload);
+     free(tid);
+diff --git a/lib/package.c b/lib/package.c
+index 281275029..90bd0d8a7 100644
+--- a/lib/package.c
++++ b/lib/package.c
+@@ -404,5 +404,45 @@ rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp)
+     return rc;
+ }
+ 
++rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp)
++{
++    char *msg = NULL;
++    hdrblob sigblob = hdrblobCreate();
++    hdrblob blob = hdrblobCreate();
++    Header h = NULL;
++    Header sigh = NULL;
++
++    rpmRC rc = rpmLeadRead(fd, &msg);
++    if (rc != RPMRC_OK)
++	goto exit;
++
++    rc = hdrblobRead(fd, 1, 0, RPMTAG_HEADERSIGNATURES, sigblob, &msg);
++    if (rc != RPMRC_OK)
++	goto exit;
++
++    rc = hdrblobRead(fd, 1, 1, RPMTAG_HEADERIMMUTABLE, blob, &msg);
++    if (rc != RPMRC_OK)
++	goto exit;
++
++    rc = hdrblobImport(sigblob, 0, &sigh, &msg);
++    if (rc)
++	goto exit;
+ 
++    rc = hdrblobImport(blob, 0, &h, &msg);
++    if (rc)
++	goto exit;
+ 
++    *sigp = headerLink(sigh);
++    *hdrp = headerLink(h);
++
++exit:
++    if (rc != RPMRC_OK && msg)
++	rpmlog(RPMLOG_ERR, "%s: %s\n", Fdescr(fd), msg);
++    hdrblobFree(sigblob);
++    hdrblobFree(blob);
++    headerFree(sigh);
++    headerFree(h);
++    free(msg);
++
++    return rc;
++}
+diff --git a/lib/rpmlib.h b/lib/rpmlib.h
+index 0879d04e5..a09ba0daf 100644
+--- a/lib/rpmlib.h
++++ b/lib/rpmlib.h
+@@ -155,6 +155,15 @@ rpmRC rpmReadHeader(rpmts ts, FD_t fd, Header *hdrp, char ** msg);
+ rpmRC rpmReadPackageFile(rpmts ts, FD_t fd,
+ 		const char * fn, Header * hdrp);
+ 
++/** \ingroup header
++ * Return package signature, header from file handle, no verification.
++ * @param fd		file handle
++ * @param[out] sigp		address of header (or NULL)
++ * @param[out] hdrp		address of header (or NULL)
++ * @return		RPMRC_OK on success
++ */
++rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp);
++
+ /** \ingroup rpmtrans
+  * Install source package.
+  * @param ts		transaction set
+diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c
+index 62d75c4cf..3da3097af 100644
+--- a/lib/rpmplugins.c
++++ b/lib/rpmplugins.c
+@@ -356,13 +356,28 @@ rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char *path,
+     plugin_fsm_file_pre_func hookFunc;
+     int i;
+     rpmRC rc = RPMRC_OK;
++    rpmRC hook_rc;
+ 
+     for (i = 0; i < plugins->count; i++) {
+ 	rpmPlugin plugin = plugins->plugins[i];
+ 	RPMPLUGINS_SET_HOOK_FUNC(fsm_file_pre);
+-	if (hookFunc && hookFunc(plugin, fi, path, file_mode, op) == RPMRC_FAIL) {
+-	    rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name);
+-	    rc = RPMRC_FAIL;
++	if (hookFunc) {
++	    hook_rc = hookFunc(plugin, fi, path, file_mode, op);
++	    if (hook_rc == RPMRC_FAIL) {
++		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name);
++		rc = RPMRC_FAIL;
++	    } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) {
++		if (rc == RPMRC_PLUGIN_CONTENTS) {
++		    /* Another plugin already said it'd handle contents. It's
++		     * undefined how these would combine, so treat this as a
++		     * failure condition.
++		    */
++		    rc = RPMRC_FAIL;
++		} else {
++		    /* Plugin will handle content */
++		    rc = RPMRC_PLUGIN_CONTENTS;
++		}
++	    }
+ 	}
+     }
+ 
+diff --git a/lib/rpmte.c b/lib/rpmte.c
+index 3663604e7..d43dc41ad 100644
+--- a/lib/rpmte.c
++++ b/lib/rpmte.c
+@@ -423,6 +423,11 @@ FD_t rpmteSetFd(rpmte te, FD_t fd)
+     return NULL;
+ }
+ 
++FD_t rpmteFd(rpmte te)
++{
++    return (te != NULL ? te->fd : NULL);
++}
++
+ fnpyKey rpmteKey(rpmte te)
+ {
+     return (te != NULL ? te->key : NULL);
+diff --git a/lib/rpmte.h b/lib/rpmte.h
+index 81acf7a19..6fc0a9f91 100644
+--- a/lib/rpmte.h
++++ b/lib/rpmte.h
+@@ -209,6 +209,8 @@ const char * rpmteNEVR(rpmte te);
+  */
+ const char * rpmteNEVRA(rpmte te);
+ 
++FD_t rpmteFd(rpmte te);
++
+ /** \ingroup rpmte
+  * Retrieve key from transaction element.
+  * @param te		transaction element
+diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h
+index e8e69b506..af2611e9e 100644
+--- a/lib/rpmtypes.h
++++ b/lib/rpmtypes.h
+@@ -106,7 +106,8 @@ typedef	enum rpmRC_e {
+     RPMRC_NOTFOUND	= 1,	/*!< Generic not found code. */
+     RPMRC_FAIL		= 2,	/*!< Generic failure code. */
+     RPMRC_NOTTRUSTED	= 3,	/*!< Signature is OK, but key is not trusted. */
+-    RPMRC_NOKEY		= 4	/*!< Public key is unavailable. */
++    RPMRC_NOKEY		= 4,	/*!< Public key is unavailable. */
++    RPMRC_PLUGIN_CONTENTS = 5     /*!< fsm_file_pre plugin is handling content */
+ } rpmRC;
+ 
+ #ifdef __cplusplus
+diff --git a/macros.in b/macros.in
+index e90cefa9a..363252b0f 100644
+--- a/macros.in
++++ b/macros.in
+@@ -1143,6 +1143,7 @@ package or when debugging this package.\
+ 
+ # Transaction plugin macros
+ %__plugindir		%{_libdir}/rpm-plugins
++%__transaction_reflink		%{__plugindir}/reflink.so
+ %__transaction_systemd_inhibit	%{__plugindir}/systemd_inhibit.so
+ %__transaction_selinux		%{__plugindir}/selinux.so
+ %__transaction_syslog		%{__plugindir}/syslog.so
+diff --git a/plugins/Makefile.am b/plugins/Makefile.am
+index 3a929d0ce..ad0d3bce7 100644
+--- a/plugins/Makefile.am
++++ b/plugins/Makefile.am
+@@ -42,6 +42,10 @@ prioreset_la_SOURCES = prioreset.c
+ prioreset_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
+ plugins_LTLIBRARIES += prioreset.la
+ 
++reflink_la_SOURCES = reflink.c
++reflink_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
++plugins_LTLIBRARIES += reflink.la
++
+ syslog_la_SOURCES = syslog.c
+ syslog_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la
+ plugins_LTLIBRARIES += syslog.la
+diff --git a/plugins/reflink.c b/plugins/reflink.c
+new file mode 100644
+index 000000000..513887604
+--- /dev/null
++++ b/plugins/reflink.c
+@@ -0,0 +1,375 @@
++#include "system.h"
++
++#include <errno.h>
++#include <sys/resource.h>
++#include <unistd.h>
++#include <sys/types.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#if defined(__linux__)
++#include <linux/fs.h>        /* For FICLONE */
++#endif
++
++#include <rpm/rpmlog.h>
++#include "lib/rpmlib.h"
++#include "lib/rpmplugin.h"
++#include "lib/rpmte_internal.h"
++#include <rpm/rpmfileutil.h>
++#include "rpmio/rpmio_internal.h"
++
++
++#include "debug.h"
++
++#include <sys/ioctl.h>
++
++/* use hash table to remember inode -> ix (for rpmfilesFN(ix)) lookups */
++#undef HASHTYPE
++#undef HTKEYTYPE
++#undef HTDATATYPE
++#define HASHTYPE inodeIndexHash
++#define HTKEYTYPE rpm_ino_t
++#define HTDATATYPE int
++#include "lib/rpmhash.H"
++#include "lib/rpmhash.C"
++
++/* We use this in find to indicate a key wasn't found. This is an
++ * unrecoverable error, but we can at least show a decent error. 0 is never a
++ * valid offset because it's the offset of the start of the file.
++ */
++#define NOT_FOUND 0
++
++#define BUFFER_SIZE (1024 * 128)
++
++/* magic value at end of file (64 bits) that indicates this is a transcoded
++ * rpm.
++ */
++#define MAGIC 3472329499408095051
++
++struct reflink_state_s {
++    /* Stuff that's used across rpms */
++    long fundamental_block_size;
++    char *buffer;
++
++    /* stuff that's used/updated per psm */
++    uint32_t keys, keysize;
++
++    /* table for current rpm, keys * (keysize + sizeof(rpm_loff_t)) */
++    unsigned char *table;
++    FD_t fd;
++    rpmfiles files;
++    inodeIndexHash inodeIndexes;
++};
++
++typedef struct reflink_state_s * reflink_state;
++
++static int inodeCmp(rpm_ino_t a, rpm_ino_t b)
++{
++    return (a != b);
++}
++
++static unsigned int inodeId(rpm_ino_t a)
++{
++    /* rpm_ino_t is uint32_t so maps safely to unsigned int */
++    return (unsigned int)a;
++}
++
++static rpmRC reflink_init(rpmPlugin plugin, rpmts ts) {
++    reflink_state state = rcalloc(1, sizeof(struct reflink_state_s));
++
++    /* IOCTL-FICLONERANGE(2): ...Disk filesystems generally require the offset
++     * and length arguments to be aligned to the fundamental block size.
++     *
++     * The value of "fundamental block size" is directly related to the
++     * system's page size, so we should use that.
++     */
++    state->fundamental_block_size = sysconf(_SC_PAGESIZE);
++    state->buffer = rcalloc(1, BUFFER_SIZE);
++    rpmPluginSetData(plugin, state);
++
++    return RPMRC_OK;
++}
++
++static void reflink_cleanup(rpmPlugin plugin) {
++    reflink_state state = rpmPluginGetData(plugin);
++    free(state->buffer);
++    free(state);
++}
++
++static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
++    reflink_state state = rpmPluginGetData(plugin);
++    state->fd = rpmteFd(te);
++    if (state->fd == 0) {
++	rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n"));
++	return RPMRC_OK;
++    }
++    rpm_loff_t current = Ftell(state->fd);
++    uint64_t magic;
++    if (Fseek(state->fd, -(sizeof(magic)), SEEK_END) < 0) {
++	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n"));
++	if (Fseek(state->fd, current, SEEK_SET) < 0) {
++	    /* yes this gets a bit repetitive */
++	    rpmlog(RPMLOG_ERR,
++		 _("reflink: unable to seek back to original location\n"));
++	}
++	return RPMRC_FAIL;
++    }
++    size_t len = sizeof(magic);
++    if (Fread(&magic, len, 1, state->fd) != len) {
++	rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n"));
++	if (Fseek(state->fd, current, SEEK_SET) < 0) {
++	    rpmlog(RPMLOG_ERR,
++		   _("reflink: unable to seek back to original location\n"));
++	}
++	return RPMRC_FAIL;
++    }
++    if (magic != MAGIC) {
++	rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n"));
++	if (Fseek(state->fd, current, SEEK_SET) < 0) {
++	    rpmlog(RPMLOG_ERR,
++		   _("reflink: unable to seek back to original location\n"));
++	    return RPMRC_FAIL;
++	}
++	return RPMRC_OK;
++    }
++    rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));
++    Header h = rpmteHeader(te);
++
++    /* replace/add header that main fsm.c can read */
++    headerDel(h, RPMTAG_PAYLOADFORMAT);
++    headerPutString(h, RPMTAG_PAYLOADFORMAT, "clon");
++    headerFree(h);
++    state->files = rpmteFiles(te);
++    /* tail of file contains offset_table, offset_checksums then magic */
++    if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(magic)), SEEK_END) < 0) {
++	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"),
++	       state->fd);
++	return RPMRC_FAIL;
++    }
++    rpm_loff_t table_start;
++    len = sizeof(table_start);
++    if (Fread(&table_start, len, 1, state->fd) != len) {
++	rpmlog(RPMLOG_ERR, _("reflink: unable to read table_start\n"));
++	return RPMRC_FAIL;
++    }
++    if (Fseek(state->fd, table_start, SEEK_SET) < 0) {
++	rpmlog(RPMLOG_ERR, _("reflink: unable to seek to table_start\n"));
++	return RPMRC_FAIL;
++    }
++    len = sizeof(state->keys);
++    if (Fread(&state->keys, len, 1, state->fd) != len) {
++	rpmlog(RPMLOG_ERR, _("reflink: unable to read number of keys\n"));
++	return RPMRC_FAIL;
++    }
++    len = sizeof(state->keysize);
++    if (Fread(&state->keysize, len, 1, state->fd) != len) {
++	rpmlog(RPMLOG_ERR, _("reflink: unable to read keysize\n"));
++	return RPMRC_FAIL;
++    }
++    rpmlog(
++	RPMLOG_DEBUG,
++	_("reflink: table_start=0x%lx, keys=%d, keysize=%d\n"),
++	table_start, state->keys, state->keysize
++    );
++    /* now get digest table if there is a reason to have one. */
++    if (state->keys == 0 || state->keysize == 0) {
++	/* no files (or no digests(!)) */
++	state->table = NULL;
++    } else {
++	int table_size = state->keys * (state->keysize + sizeof(rpm_loff_t));
++	state->table = rcalloc(1, table_size);
++	if (Fread(state->table, table_size, 1, state->fd) != table_size) {
++	    rpmlog(RPMLOG_ERR, _("reflink: unable to read table\n"));
++	    return RPMRC_FAIL;
++	}
++	state->inodeIndexes = inodeIndexHashCreate(
++	    state->keys, inodeId, inodeCmp, NULL, NULL
++	);
++    }
++
++    /* Seek back to original location.
++     * Might not be needed if we seek to offset immediately
++     */
++    if (Fseek(state->fd, current, SEEK_SET) < 0) {
++	rpmlog(RPMLOG_ERR,
++	       _("reflink: unable to seek back to original location\n"));
++	return RPMRC_FAIL;
++    }
++    return RPMRC_OK;
++}
++
++static rpmRC reflink_psm_post(rpmPlugin plugin, rpmte te, int res)
++{
++    reflink_state state = rpmPluginGetData(plugin);
++    state->files = rpmfilesFree(state->files);
++    if (state->table) {
++	free(state->table);
++	state->table = NULL;
++    }
++    if (state->inodeIndexes) {
++	inodeIndexHashFree(state->inodeIndexes);
++	state->inodeIndexes = NULL;
++    }
++    return RPMRC_OK;
++}
++
++
++/* have a prototype, warnings system */
++rpm_loff_t find(const unsigned char *digest, reflink_state state);
++
++rpm_loff_t find(const unsigned char *digest, reflink_state state) {
++# if defined(__GNUC__)
++    /* GCC nested function because bsearch's comparison function can't access
++     * state-keysize otherwise
++     */
++    int cmpdigest(const void *k1, const void *k2) {
++	rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2);
++	return memcmp(k1, k2, state->keysize);
++    }
++# endif
++    rpmlog(RPMLOG_DEBUG,
++	   _("reflink: bsearch(key=%p, base=%p, nmemb=%d, size=%lu)\n"),
++	   digest, state->table, state->keys,
++	   state->keysize + sizeof(rpm_loff_t));
++    char *entry = bsearch(digest, state->table, state->keys,
++			  state->keysize + sizeof(rpm_loff_t), cmpdigest);
++    if (entry == NULL) {
++	return NOT_FOUND;
++    }
++    rpm_loff_t offset = *(rpm_loff_t *)(entry + state->keysize);
++    return offset;
++}
++
++static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,
++                                  mode_t file_mode, rpmFsmOp op)
++{
++    struct file_clone_range fcr;
++    rpm_loff_t size;
++    int dst, rc;
++    int *hlix;
++
++    reflink_state state = rpmPluginGetData(plugin);
++    if (state->table == NULL) {
++	/* no table means rpm is not in reflink format, so leave. Now. */
++	return RPMRC_OK;
++    }
++    if (op == FA_TOUCH) {
++	/* we're not overwriting an existing file. */
++	return RPMRC_OK;
++    }
++    fcr.dest_offset = 0;
++    if (S_ISREG(file_mode) && !(rpmfiFFlags(fi) & RPMFILE_GHOST)) {
++	rpm_ino_t inode = rpmfiFInode(fi);
++	/* check for hard link entry in table. GetEntry overwrites hlix with
++	 * the address of the first match.
++	 */
++	if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL,
++	                           NULL)) {
++	    /* entry is in table, use hard link */
++	    char *fn = rpmfilesFN(state->files, hlix[0]);
++	    if (link(fn, path) != 0) {
++		rpmlog(RPMLOG_ERR,
++		       _("reflink: Unable to hard link %s -> %s due to %s\n"),
++		       fn, path, strerror(errno));
++		free(fn);
++		return RPMRC_FAIL;
++	    }
++	    free(fn);
++	    return RPMRC_PLUGIN_CONTENTS;
++	}
++	/* if we didn't hard link, then we'll track this inode as being
++	 * created soon
++	 */
++	if (rpmfiFNlink(fi) > 1) {
++	    /* minor optimization: only store files with more than one link */
++	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi));
++	}
++	/* derived from wfd_open in fsm.c */
++	mode_t old_umask = umask(0577);
++	dst = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR);
++	umask(old_umask);
++	if (dst == -1) {
++	    rpmlog(RPMLOG_ERR,
++		   _("reflink: Unable to open %s for writing due to %s, flags = %x\n"),
++		   path, strerror(errno), rpmfiFFlags(fi));
++	    return RPMRC_FAIL;
++	}
++	size = rpmfiFSize(fi);
++	if (size > 0) {
++	    /* round src_length down to fundamental_block_size multiple */
++	    fcr.src_length = size / state->fundamental_block_size * state->fundamental_block_size;
++	    if ((size % state->fundamental_block_size) > 0) {
++		/* round up to next fundamental_block_size. We expect the data
++		 * in the rpm to be similarly padded.
++		 */
++		fcr.src_length += state->fundamental_block_size;
++	    }
++	    fcr.src_fd = Fileno(state->fd);
++	    if (fcr.src_fd == -1) {
++		close(dst);
++		rpmlog(RPMLOG_ERR, _("reflink: src fd lookup failed\n"));
++		return RPMRC_FAIL;
++	    }
++	    fcr.src_offset = find(rpmfiFDigest(fi, NULL, NULL), state);
++	    if (fcr.src_offset == NOT_FOUND) {
++		close(dst);
++		rpmlog(RPMLOG_ERR, _("reflink: digest not found\n"));
++		return RPMRC_FAIL;
++	    }
++	    rpmlog(RPMLOG_DEBUG,
++	           _("reflink: Reflinking %llu bytes at %llu to %s orig size=%ld, file=%lld\n"),
++		   fcr.src_length, fcr.src_offset, path, size, fcr.src_fd);
++	    rc = ioctl(dst, FICLONERANGE, &fcr);
++	    if (rc) {
++		rpmlog(RPMLOG_WARNING,
++		       _("reflink: falling back to copying bits for %s due to %d, %d = %s\n"),
++		       path, rc, errno, strerror(errno));
++		if (Fseek(state->fd, fcr.src_offset, SEEK_SET) < 0) {
++		    close(dst);
++		    rpmlog(RPMLOG_ERR,
++			   _("reflink: unable to seek on copying bits\n"));
++		    return RPMRC_FAIL;
++		}
++		rpm_loff_t left = size;
++		size_t len, read, written;
++		while (left) {
++		    len = (left > BUFFER_SIZE ? BUFFER_SIZE : left);
++		    read = Fread(state->buffer, len, 1, state->fd);
++		    if (read != len) {
++			close(dst);
++			rpmlog(RPMLOG_ERR,
++			       _("reflink: short read on copying bits\n"));
++			return RPMRC_FAIL;
++		    }
++		    written = write(dst, state->buffer, len);
++		    if (read != written) {
++			close(dst);
++			rpmlog(RPMLOG_ERR,
++			       _("reflink: short write on copying bits\n"));
++			return RPMRC_FAIL;
++		    }
++		    left -= len;
++		}
++	    } else {
++		/* reflink worked, so truncate */
++		rc = ftruncate(dst, size);
++		if (rc) {
++		    rpmlog(RPMLOG_ERR,
++			   _("reflink: Unable to truncate %s to %ld due to %s\n"),
++			   path, size, strerror(errno));
++		     return RPMRC_FAIL;
++		}
++	    }
++	}
++	close(dst);
++	return RPMRC_PLUGIN_CONTENTS;
++    }
++    return RPMRC_OK;
++}
++
++struct rpmPluginHooks_s reflink_hooks = {
++    .init = reflink_init,
++    .cleanup = reflink_cleanup,
++    .psm_pre = reflink_psm_pre,
++    .psm_post = reflink_psm_post,
++    .fsm_file_pre = reflink_fsm_file_pre,
++};
+diff --git a/rpm2extents.c b/rpm2extents.c
+new file mode 100644
+index 000000000..c111be0a2
+--- /dev/null
++++ b/rpm2extents.c
+@@ -0,0 +1,433 @@
++/* rpm2extents: convert payload to inline extents */
++
++#include "system.h"
++
++#include <rpm/rpmlib.h>		/* rpmReadPackageFile .. */
++#include <rpm/rpmfi.h>
++#include <rpm/rpmtag.h>
++#include <rpm/rpmio.h>
++#include <rpm/rpmpgp.h>
++
++#include <rpm/rpmts.h>
++#include "lib/rpmlead.h"
++#include "lib/signature.h"
++#include "lib/header_internal.h"
++#include "rpmio/rpmio_internal.h"
++
++#include <unistd.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <signal.h>
++#include <errno.h>
++#include <string.h>
++
++#include "debug.h"
++
++/* hash of void * (pointers) to file digests to offsets within output.
++ * The length of the key depends on what the FILEDIGESTALGO is.
++ */
++#undef HASHTYPE
++#undef HTKEYTYPE
++#undef HTDATATYPE
++#define HASHTYPE digestSet
++#define HTKEYTYPE const unsigned char *
++#include "lib/rpmhash.H"
++#include "lib/rpmhash.C"
++
++/* magic value at end of file (64 bits) that indicates this is a transcoded
++ * rpm.
++ */
++#define MAGIC 3472329499408095051
++
++struct digestoffset {
++    const unsigned char * digest;
++    rpm_loff_t pos;
++};
++
++rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit);
++
++rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit)
++{
++    return (unit - (pos % unit)) % unit;
++}
++
++static int digestor(
++    FD_t fdi,
++    FD_t fdo,
++    FD_t validationo,
++    uint8_t algos[],
++    uint32_t algos_len
++)
++{
++    ssize_t fdilength;
++    const char *filedigest, *algo_name;
++    size_t filedigest_len, len;
++    uint32_t algo_name_len, algo_digest_len;
++    int algo;
++    rpmRC rc = RPMRC_FAIL;
++
++    for (algo = 0; algo < algos_len; algo++) {
++	fdInitDigest(fdi, algos[algo], 0);
++    }
++    fdilength = ufdCopy(fdi, fdo);
++    if (fdilength == -1) {
++	fprintf(stderr, _("digest cat failed\n"));
++	goto exit;
++    }
++
++    len = sizeof(fdilength);
++    if (Fwrite(&fdilength, len, 1, validationo) != len) {
++	fprintf(stderr, _("Unable to write input length %zd\n"), fdilength);
++	goto exit;
++    }
++    len = sizeof(algos_len);
++    if (Fwrite(&algos_len, len, 1, validationo) != len) {
++	fprintf(stderr, _("Unable to write number of validation digests\n"));
++	goto exit;
++    }
++    for (algo = 0; algo < algos_len; algo++) {
++	fdFiniDigest(fdi, algos[algo], (void **)&filedigest, &filedigest_len, 0);
++
++	algo_name = pgpValString(PGPVAL_HASHALGO, algos[algo]);
++	algo_name_len = (uint32_t)strlen(algo_name);
++	algo_digest_len = (uint32_t)filedigest_len;
++
++	len = sizeof(algo_name_len);
++	if (Fwrite(&algo_name_len, len, 1, validationo) != len) {
++	    fprintf(stderr,
++		    _("Unable to write validation algo name length\n"));
++	    goto exit;
++	}
++	len = sizeof(algo_digest_len);
++	if (Fwrite(&algo_digest_len, len, 1, validationo) != len) {
++	    fprintf(stderr,
++		    _("Unable to write number of bytes for validation digest\n"));
++	     goto exit;
++	}
++	if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) {
++	    fprintf(stderr, _("Unable to write validation algo name\n"));
++	    goto exit;
++	}
++	if (Fwrite(filedigest, algo_digest_len, 1, validationo ) != algo_digest_len) {
++	    fprintf(stderr,
++		    _("Unable to write validation digest value %u, %zu\n"),
++		    algo_digest_len, filedigest_len);
++	    goto exit;
++	}
++    }
++    rc = RPMRC_OK;
++exit:
++    return rc;
++}
++
++static rpmRC process_package(FD_t fdi, FD_t validationi)
++{
++    uint32_t diglen;
++    /* GNU C extension: can use diglen from outer context */
++    int digestSetCmp(const unsigned char * a, const unsigned char * b) {
++	return memcmp(a, b, diglen);
++    }
++
++    unsigned int digestSetHash(const unsigned char * digest) {
++        /* assumes sizeof(unsigned int) < diglen */
++        return *(unsigned int *)digest;
++    }
++
++    int digestoffsetCmp(const void * a, const void * b) {
++	return digestSetCmp(
++	    ((struct digestoffset *)a)->digest,
++	    ((struct digestoffset *)b)->digest
++	);
++    }
++
++    FD_t fdo;
++    FD_t gzdi;
++    Header h, sigh;
++    long fundamental_block_size = sysconf(_SC_PAGESIZE);
++    rpmRC rc = RPMRC_OK;
++    rpm_mode_t mode;
++    char *rpmio_flags = NULL, *zeros;
++    const unsigned char *digest;
++    rpm_loff_t pos, size, pad, validation_pos;
++    uint32_t offset_ix = 0;
++    size_t len;
++    int next = 0;
++
++    fdo = fdDup(STDOUT_FILENO);
++
++    if (rpmReadPackageRaw(fdi, &sigh, &h)) {
++	fprintf(stderr, _("Error reading package\n"));
++	exit(EXIT_FAILURE);
++    }
++
++    if (rpmLeadWrite(fdo, h))
++    {
++	fprintf(stderr, _("Unable to write package lead: %s\n"),
++		Fstrerror(fdo));
++	exit(EXIT_FAILURE);
++    }
++
++    if (rpmWriteSignature(fdo, sigh)) {
++	fprintf(stderr, _("Unable to write signature: %s\n"), Fstrerror(fdo));
++	exit(EXIT_FAILURE);
++    }
++
++    if (headerWrite(fdo, h, HEADER_MAGIC_YES)) {
++	fprintf(stderr, _("Unable to write headers: %s\n"), Fstrerror(fdo));
++	exit(EXIT_FAILURE);
++    }
++
++    /* Retrieve payload size and compression type. */
++    {
++	const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
++	rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL);
++    }
++
++    gzdi = Fdopen(fdi, rpmio_flags);	/* XXX gzdi == fdi */
++    free(rpmio_flags);
++
++    if (gzdi == NULL) {
++	fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));
++	exit(EXIT_FAILURE);
++    }
++
++    rpmfiles files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER);
++    rpmfi fi = rpmfiNewArchiveReader(gzdi, files,
++				     RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST);
++
++    /* this is encoded in the file format, so needs to be fixed size (for
++     * now?)
++     */
++    diglen = (uint32_t)rpmDigestLength(rpmfiDigestAlgo(fi));
++    digestSet ds = digestSetCreate(rpmfiFC(fi), digestSetHash, digestSetCmp,
++				   NULL);
++    struct digestoffset offsets[rpmfiFC(fi)];
++    pos = RPMLEAD_SIZE + headerSizeof(sigh, HEADER_MAGIC_YES);
++
++    /* main headers are aligned to 8 byte boundry */
++    pos += pad_to(pos, 8);
++    pos += headerSizeof(h, HEADER_MAGIC_YES);
++
++    zeros = xcalloc(fundamental_block_size, 1);
++
++    while (next >= 0) {
++	next = rpmfiNext(fi);
++	if (next == RPMERR_ITER_END) {
++	    rc = RPMRC_OK;
++	    break;
++	}
++	mode = rpmfiFMode(fi);
++	if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi)) {
++	    /* not a regular file, or the archive doesn't contain any content
++	     * for this entry.
++	    */
++	    continue;
++	}
++	digest = rpmfiFDigest(fi, NULL, NULL);
++	if (digestSetGetEntry(ds, digest, NULL)) {
++	    /* This specific digest has already been included, so skip it. */
++	    continue;
++	}
++	pad = pad_to(pos, fundamental_block_size);
++	if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) {
++	    fprintf(stderr, _("Unable to write padding\n"));
++	    rc = RPMRC_FAIL;
++	    goto exit;
++	}
++	/* round up to next fundamental_block_size */
++	pos += pad;
++	digestSetAddEntry(ds, digest);
++	offsets[offset_ix].digest = digest;
++	offsets[offset_ix].pos = pos;
++	offset_ix++;
++	size = rpmfiFSize(fi);
++	rc = rpmfiArchiveReadToFile(fi, fdo, 0);
++	if (rc != RPMRC_OK) {
++	    fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc);
++	    goto exit;
++	}
++	pos += size;
++    }
++    Fclose(gzdi);	/* XXX gzdi == fdi */
++
++    qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset),
++	  digestoffsetCmp);
++
++    len = sizeof(offset_ix);
++    if (Fwrite(&offset_ix, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write length of table\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    len = sizeof(diglen);
++    if (Fwrite(&diglen, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write length of digest\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    len = sizeof(rpm_loff_t);
++    for (int x = 0; x < offset_ix; x++) {
++	if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) {
++	    fprintf(stderr, _("Unable to write digest\n"));
++	    rc = RPMRC_FAIL;
++	    goto exit;
++	}
++	if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) {
++	    fprintf(stderr, _("Unable to write offset\n"));
++	    rc = RPMRC_FAIL;
++	    goto exit;
++	}
++    }
++    validation_pos = (
++	pos + sizeof(offset_ix) + sizeof(diglen) +
++	offset_ix * (diglen + sizeof(rpm_loff_t))
++    );
++
++    ssize_t validation_len = ufdCopy(validationi, fdo);
++    if (validation_len == -1) {
++	fprintf(stderr, _("digest table ufdCopy failed\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    /* add more padding so the last file can be cloned. It doesn't matter that
++     * the table and validation etc are in this space. In fact, it's pretty
++     * efficient if it is.
++    */
++
++    pad = pad_to((validation_pos + validation_len + 2 * sizeof(rpm_loff_t) +
++		 sizeof(uint64_t)), fundamental_block_size);
++    if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) {
++	fprintf(stderr, _("Unable to write final padding\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    zeros = _free(zeros);
++    if (Fwrite(&pos, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write offset of digest table\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    if (Fwrite(&validation_pos, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write offset of validation table\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    uint64_t magic = MAGIC;
++    len = sizeof(magic);
++    if (Fwrite(&magic, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write magic\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++
++exit:
++    rpmfilesFree(files);
++    rpmfiFree(fi);
++    headerFree(h);
++    return rc;
++}
++
++int main(int argc, char *argv[]) {
++    rpmRC rc;
++    int cprc = 0;
++    uint8_t algos[argc - 1];
++    int mainpipefd[2];
++    int metapipefd[2];
++    pid_t cpid, w;
++    int wstatus;
++
++    xsetprogname(argv[0]);	/* Portability call -- see system.h */
++    rpmReadConfigFiles(NULL, NULL);
++
++    if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) {
++	fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]);
++	exit(EXIT_FAILURE);
++    }
++
++    if (argc == 1) {
++	fprintf(stderr,
++		_("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));
++	exit(EXIT_FAILURE);
++    }
++
++    for (int x = 0; x < (argc - 1); x++) {
++	if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0)
++	{
++	    fprintf(stderr,
++		    _("Unable to resolve '%s' as a digest algorithm, exiting\n"),
++		    argv[x + 1]);
++	    exit(EXIT_FAILURE);
++	}
++    }
++
++
++    if (pipe(mainpipefd) == -1) {
++	fprintf(stderr, _("Main pipe failure\n"));
++	exit(EXIT_FAILURE);
++    }
++    if (pipe(metapipefd) == -1) {
++	fprintf(stderr, _("Meta pipe failure\n"));
++	exit(EXIT_FAILURE);
++    }
++    cpid = fork();
++    if (cpid == 0) {
++	/* child: digestor */
++	close(mainpipefd[0]);
++	close(metapipefd[0]);
++	FD_t fdi = fdDup(STDIN_FILENO);
++	FD_t fdo = fdDup(mainpipefd[1]);
++	FD_t validationo = fdDup(metapipefd[1]);
++	rc = digestor(fdi, fdo, validationo, algos, argc - 1);
++	Fclose(validationo);
++	Fclose(fdo);
++	Fclose(fdi);
++    } else {
++	/* parent: main program */
++	close(mainpipefd[1]);
++	close(metapipefd[1]);
++	FD_t fdi = fdDup(mainpipefd[0]);
++	FD_t validationi = fdDup(metapipefd[0]);
++	rc = process_package(fdi, validationi);
++	Fclose(validationi);
++	/* fdi is normally closed through the stacked file gzdi in the
++	 * function.
++	 * Wait for child process (digestor for stdin) to complete.
++	 */
++	if (rc != RPMRC_OK) {
++	    if (kill(cpid, SIGTERM) != 0) {
++		fprintf(stderr,
++		        _("Failed to kill digest process when main process failed: %s\n"),
++			strerror(errno));
++	    }
++	}
++	w = waitpid(cpid, &wstatus, 0);
++	if (w == -1) {
++	    fprintf(stderr, _("waitpid failed\n"));
++	    cprc = EXIT_FAILURE;
++	} else if (WIFEXITED(wstatus)) {
++	    cprc = WEXITSTATUS(wstatus);
++	    if (cprc != 0) {
++		fprintf(stderr,
++			_("Digest process non-zero exit code %d\n"),
++			cprc);
++	    }
++	} else if (WIFSIGNALED(wstatus)) {
++	    fprintf(stderr,
++		    _("Digest process was terminated with a signal: %d\n"),
++		    WTERMSIG(wstatus));
++	    cprc = EXIT_FAILURE;
++	} else {
++	    /* Don't think this can happen, but covering all bases */
++	    fprintf(stderr, _("Unhandled circumstance in waitpid\n"));
++	    cprc = EXIT_FAILURE;
++	}
++	if (cprc != EXIT_SUCCESS) {
++	    rc = RPMRC_FAIL;
++	}
++    }
++    if (rc != RPMRC_OK) {
++	/* translate rpmRC into generic failure return code. */
++	return EXIT_FAILURE;
++    }
++    return EXIT_SUCCESS;
++}
+diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c
+index 015c15a5c..7b972b4a6 100644
+--- a/rpmio/rpmpgp.c
++++ b/rpmio/rpmpgp.c
+@@ -283,6 +283,16 @@ int pgpValTok(pgpValTbl vs, const char * s, const char * se)
+     return vs->val;
+ }
+ 
++int pgpStringVal(pgpValType type, const char *str, uint8_t *val)
++{
++    pgpValTbl tbl = pgpValTable(type);
++    if (tbl == NULL) return -1;
++    int v = pgpValTok(tbl, str, str + strlen(str));
++    if (v == -1) return -1;
++    *val = (uint8_t)v;
++    return 0;
++}
++
+ /** \ingroup rpmpgp
+  * Decode length from 1, 2, or 5 octet body length encoding, used in
+  * new format packet headers and V4 signature subpackets.
+diff --git a/rpmio/rpmpgp.h b/rpmio/rpmpgp.h
+index c53e29b01..2b57318ba 100644
+--- a/rpmio/rpmpgp.h
++++ b/rpmio/rpmpgp.h
+@@ -973,6 +973,15 @@ typedef rpmFlags rpmDigestFlags;
+  */
+ const char * pgpValString(pgpValType type, uint8_t val);
+ 
++/** \ingroup rpmpgp
++ * Return  OpenPGP value for a string.
++ * @param type		type of value
++ * @param str		string to lookup
++ * @param[out] val  byte value associated with string
++ * @return		0 on success else -1
++ */
++int pgpStringVal(pgpValType type, const char *str, uint8_t *val);
++
+ /** \ingroup rpmpgp
+  * Return (native-endian) integer from big-endian representation.
+  * @param s		pointer to big-endian integer
diff --git a/SOURCES/measure.patch b/SOURCES/measure.patch
index dab1596..b0c580f 100644
--- a/SOURCES/measure.patch
+++ b/SOURCES/measure.patch
@@ -15,9 +15,9 @@ index 3cc8a3555..c8a087959 100644
 --- a/macros.in
 +++ b/macros.in
 @@ -1173,6 +1173,7 @@ package or when debugging this package.\
-
  # Transaction plugin macros
  %__plugindir		%{_libdir}/rpm-plugins
+ %__transaction_reflink		%{__plugindir}/reflink.so
 +%__transaction_measure		%{__plugindir}/measure.so
  %__transaction_systemd_inhibit	%{__plugindir}/systemd_inhibit.so
  %__transaction_selinux		%{__plugindir}/selinux.so
diff --git a/SPECS/rpm.spec b/SPECS/rpm.spec
index db24eb0..7685441 100644
--- a/SPECS/rpm.spec
+++ b/SPECS/rpm.spec
@@ -193,10 +193,10 @@ Patch1983: 0034-rpmsign-Adopting-PKCS11-opaque-keys-support-in-libfsverity-for-f
 %endif
 
 Patch9989: 1534.patch
-#Patch9990: https://github.com/rpm-software-management/rpm/pull/1381.patch
-#Provides: rpm(pr1381)
-#Patch9991: https://github.com/rpm-software-management/rpm/pull/1470.patch
-#Provides: rpm(pr1470)
+Patch9990: https://github.com/rpm-software-management/rpm/pull/1381.patch
+Provides: rpm(pr1381)
+Patch9991: https://github.com/chantra/rpm/compare/master...cow.diff
+Provides: rpm(pr1470)
 Patch9999: measure.patch
 
 # Partially GPL/LGPL dual-licensed and some bits with BSD
@@ -490,6 +490,13 @@ Obsoletes: fapolicyd-dnf-plugin
 %description plugin-fapolicyd
 %{summary}.
 
+%package plugin-reflink
+Summary: Rpm plugin for reflink functionality
+Requires: rpm-libs%{_isa} = %{version}-%{release}
+
+%description plugin-reflink
+%{summary}.
+
 %package plugin-measure
 Summary: Rpm plugin for measure
 Group: System Environment/Base
@@ -715,6 +722,10 @@ make check || cat tests/rpmtests.log
 %{_libdir}/rpm-plugins/fapolicyd.so
 %{_mandir}/man8/rpm-plugin-fapolicyd.8*
 
+%files plugin-reflink
+%{_bindir}/rpm2extents
+%{_libdir}/rpm-plugins/reflink.so
+
 %files plugin-measure
 %{_libdir}/rpm-plugins/measure.so
 %endif # with plugins