diff --git a/SOURCES/0001-RPM-with-Copy-on-Write.patch b/SOURCES/0001-RPM-with-Copy-on-Write.patch
new file mode 100644
index 0000000..4f4e8ba
--- /dev/null
+++ b/SOURCES/0001-RPM-with-Copy-on-Write.patch
@@ -0,0 +1,1344 @@
+From 7bd31ce85b2ed377f495c31fcea2422a07739e24 Mon Sep 17 00:00:00 2001
+From: Matthew Almond <malmond@fb.com>
+Date: Fri, 8 Nov 2019 09:29:43 -0800
+Subject: [PATCH 01/30] 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           |  45 +++-
+ 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, 1004 insertions(+), 12 deletions(-)
+ create mode 100644 plugins/reflink.c
+ create mode 100644 rpm2extents.c
+
+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..90193c749 100644
+--- a/lib/fsm.c
++++ b/lib/fsm.c
+@@ -8,6 +8,7 @@
+
+ #include <utime.h>
+ #include <errno.h>
++#include <stdbool.h>
+ #if WITH_CAP
+ #include <sys/capability.h>
+ #endif
+@@ -19,6 +20,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 +54,7 @@ struct filedata_s {
+     int stage;
+     int setmeta;
+     int skip;
++    int plugin_contents;
+     rpmFileAction action;
+     const char *suffix;
+     char *fpath;
+@@ -891,6 +894,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);
++    bool cpio = true;
++
++    if (payloadfmt && rstreq(payloadfmt, "clon")) {
++	cpio = false;
++    }
++
+     /* transaction id used for temporary path suffix while installing */
+     rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts));
+ 
+@@ -911,12 +922,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 = false;
++	switch (rc) {
++	case RPMRC_OK:
++	    setFileState(fs, fx);
++	    break;
++	case RPMRC_PLUGIN_CONTENTS:
++	    fp->plugin_contents = true;
++	    // 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 +946,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 +995,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 +1108,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 @@ exit:
+     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..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 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..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 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
+-- 
+2.35.1
+
diff --git a/SOURCES/0002-Remove-use-of-bool-type-for-consistency.patch b/SOURCES/0002-Remove-use-of-bool-type-for-consistency.patch
new file mode 100644
index 0000000..332b002
--- /dev/null
+++ b/SOURCES/0002-Remove-use-of-bool-type-for-consistency.patch
@@ -0,0 +1,53 @@
+From 845b5c3882b1eecb31d712b61a4e91fe0eb70712 Mon Sep 17 00:00:00 2001
+From: Matthew Almond <malmond@fb.com>
+Date: Sun, 31 Jan 2021 12:30:33 -0800
+Subject: [PATCH 02/30] 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 90193c749..feda3750c 100644
+--- a/lib/fsm.c
++++ b/lib/fsm.c
+@@ -8,7 +8,6 @@
+
+ #include <utime.h>
+ #include <errno.h>
+-#include <stdbool.h>
+ #if WITH_CAP
+ #include <sys/capability.h>
+ #endif
+@@ -896,10 +895,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+ 
+     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;
+     }
+ 
+     /* transaction id used for temporary path suffix while installing */
+@@ -927,13 +926,13 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+ 	/* Run fsm file pre hook for all plugins */
+ 	rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath,
+ 				      fp->sb.st_mode, fp->action);
+-	fp->plugin_contents = false;
++	fp->plugin_contents = 0;
+ 	switch (rc) {
+ 	case RPMRC_OK:
+ 	    setFileState(fs, fx);
+ 	    break;
+ 	case RPMRC_PLUGIN_CONTENTS:
+-	    fp->plugin_contents = true;
++	    fp->plugin_contents = 1;
+ 	    // reduce reads on cpio to this value. Could be zero if
+ 	    // this is from a hard link.
+ 	    rc = RPMRC_OK;
+-- 
+2.35.1
+
diff --git a/SOURCES/0003-Match-formatting-style-of-existing-code.patch b/SOURCES/0003-Match-formatting-style-of-existing-code.patch
new file mode 100644
index 0000000..aae1035
--- /dev/null
+++ b/SOURCES/0003-Match-formatting-style-of-existing-code.patch
@@ -0,0 +1,1249 @@
+From aa31421bfe835dadd29da3aa46ee446038f80d02 Mon Sep 17 00:00:00 2001
+From: Matthew Almond <malmond@fb.com>
+Date: Sun, 31 Jan 2021 13:51:16 -0800
+Subject: [PATCH 03/30] 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 @@ exit:
+     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;
+ }
+-- 
+2.35.1
+
diff --git a/SOURCES/0004-Fix-printf-formatting-in-reflink.c.patch b/SOURCES/0004-Fix-printf-formatting-in-reflink.c.patch
new file mode 100644
index 0000000..c33f3d5
--- /dev/null
+++ b/SOURCES/0004-Fix-printf-formatting-in-reflink.c.patch
@@ -0,0 +1,27 @@
+From 15127592f8cc3221129f61b79319d88c7727bec3 Mon Sep 17 00:00:00 2001
+From: Matthew Almond <malmond@fb.com>
+Date: Sun, 31 Jan 2021 15:24:25 -0800
+Subject: [PATCH 04/30] 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) {
+-- 
+2.35.1
+
diff --git a/SOURCES/0005-tests-rpm2extents-Add-basic-tests-for-rpm2extents.patch b/SOURCES/0005-tests-rpm2extents-Add-basic-tests-for-rpm2extents.patch
new file mode 100644
index 0000000..282dff0
--- /dev/null
+++ b/SOURCES/0005-tests-rpm2extents-Add-basic-tests-for-rpm2extents.patch
@@ -0,0 +1,73 @@
+From fd4060dcfbe4127fb0d19f1878d0d8b9f34c7b9a Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Fri, 28 Jan 2022 08:33:16 -0800
+Subject: [PATCH 05/30] [tests][rpm2extents] Add basic tests for rpm2extents
+
+---
+ tests/Makefile.am    |  1 +
+ tests/rpm2extents.at | 31 +++++++++++++++++++++++++++++++
+ tests/rpmtests.at    |  1 +
+ 3 files changed, 33 insertions(+)
+ create mode 100644 tests/rpm2extents.at
+
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index f78e17c3e..fc8a24a5e 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -36,6 +36,7 @@ TESTSUITE_AT += rpmio.at
+ TESTSUITE_AT += rpmio.at
+ TESTSUITE_AT += rpmorder.at
+ TESTSUITE_AT += rpmvfylevel.at
++TESTSUITE_AT += rpm2extents.at
+ EXTRA_DIST += $(TESTSUITE_AT)
+ 
+ ## testsuite data
+diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
+new file mode 100644
+index 000000000..f943b9af4
+--- /dev/null
++++ b/tests/rpm2extents.at
+@@ -0,0 +1,31 @@
++#    rpm2extents.at: Some very basic checks
++#
++#    Copyright (C) 2022  Manu Bretelle <chantr4@gmail.com>
++#
++#    This program is free software; you can redistribute it and/or modify
++#    it under the terms of the GNU General Public License as published by
++#    the Free Software Foundation; either version 2 of the License, or
++#    (at your option) any later version.
++#
++#    This program is distributed in the hope that it will be useful,
++#    but WITHOUT ANY WARRANTY; without even the implied warranty of
++#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++#    GNU General Public License for more details.
++#
++#    You should have received a copy of the GNU General Public License
++#    along with this program; if not, write to the Free Software
++#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
++
++AT_BANNER([rpm2extents tests])
++
++# ------------------------------
++
++# check that transcoder write magic at the end
++AT_SETUP([rpm2extents magic])
++AT_KEYWORDS([rpm2extents])
++AT_CHECK([runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 | tail -c8],
++[0],
++[KWTSH100],
++[ignore])
++AT_CLEANUP
++
+diff --git a/tests/rpmtests.at b/tests/rpmtests.at
+index a1adab8e0..205fed6a3 100644
+--- a/tests/rpmtests.at
++++ b/tests/rpmtests.at
+@@ -21,3 +21,4 @@ m4_include([rpmreplace.at])
+ m4_include([rpmmacro.at])
+ m4_include([rpmpython.at])
+ m4_include([rpmdepmatch.at])
++m4_include([rpm2extents.at])
+-- 
+2.35.1
+
diff --git a/SOURCES/0006-rpm2extents-verify-package-signature-during-transcod.patch b/SOURCES/0006-rpm2extents-verify-package-signature-during-transcod.patch
new file mode 100644
index 0000000..c2eb42c
--- /dev/null
+++ b/SOURCES/0006-rpm2extents-verify-package-signature-during-transcod.patch
@@ -0,0 +1,431 @@
+From ea1177fcef609519f0c2377ebee236001d2a8fae Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Fri, 28 Jan 2022 08:31:39 -0800
+Subject: [PATCH 06/30] [rpm2extents] verify package signature during
+ transcoding
+
+---
+ lib/rpmchecksig.c |  30 ++++++
+ lib/rpmcli.h      |   9 ++
+ rpm2extents.c     | 233 +++++++++++++++++++++++++++++++++++++++++-----
+ 3 files changed, 248 insertions(+), 24 deletions(-)
+
+diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c
+index 40a3ab83f..1a6b95323 100644
+--- a/lib/rpmchecksig.c
++++ b/lib/rpmchecksig.c
+@@ -304,3 +304,33 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv)
+     rpmKeyringFree(keyring);
+     return res;
+ }
++
++int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi)
++{
++    int res = 0;
++    rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
++    rpmVSFlags vsflags = rpmtsVfyFlags(ts);
++    int vfylevel = rpmtsVfyLevel(ts);
++
++    vsflags |= rpmcliVSFlags;
++    if (rpmcliVfyLevelMask) {
++	vfylevel &= ~rpmcliVfyLevelMask;
++	rpmtsSetVfyLevel(ts, vfylevel);
++    }
++
++    FD_t fd = fdDup(Fileno(fdi));
++    if (fd == NULL || Ferror(fd)) {
++	rpmlog(RPMLOG_ERR, _("fdDup failed: %s\n"), Fstrerror(fd));
++	res++;
++    } else if (rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, "stdin")) {
++	res++;
++    }
++
++    lseek(Fileno(fd), SEEK_SET, 0);
++    Fclose(fd);
++    rpmsqPoll();
++
++    rpmKeyringFree(keyring);
++    return res;
++}
++
+diff --git a/lib/rpmcli.h b/lib/rpmcli.h
+index 906fe9951..52443e459 100644
+--- a/lib/rpmcli.h
++++ b/lib/rpmcli.h
+@@ -411,6 +411,15 @@ int rpmcliImportPubkeys(rpmts ts, ARGV_const_t argv);
+  */
+ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv);
+ 
++
++/** \ingroup rpmcli
++ * Verify package signatures
++ * @param ts		transaction set
++ * @param fd		a file descriptor to verify
++ * @return		0 on success
++ */
++int rpmcliVerifySignaturesFD(rpmts ts, FD_t fd);
++
+ #ifdef __cplusplus
+ }
+ #endif
+diff --git a/rpm2extents.c b/rpm2extents.c
+index c111be0a2..d8e582676 100644
+--- a/rpm2extents.c
++++ b/rpm2extents.c
+@@ -2,7 +2,9 @@
+ 
+ #include "system.h"
+ 
++#include <rpm/rpmcli.h>
+ #include <rpm/rpmlib.h>		/* rpmReadPackageFile .. */
++#include <rpm/rpmlog.h>
+ #include <rpm/rpmfi.h>
+ #include <rpm/rpmtag.h>
+ #include <rpm/rpmio.h>
+@@ -10,6 +12,7 @@
+ 
+ #include <rpm/rpmts.h>
+ #include "lib/rpmlead.h"
++#include "lib/rpmts.h"
+ #include "lib/signature.h"
+ #include "lib/header_internal.h"
+ #include "rpmio/rpmio_internal.h"
+@@ -51,6 +54,16 @@ rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit)
+     return (unit - (pos % unit)) % unit;
+ }
+ 
++static struct poptOption optionsTable[] = {
++    { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
++    N_("Common options for all rpm modes and executables:"), NULL },
++
++    POPT_AUTOALIAS
++    POPT_AUTOHELP
++    POPT_TABLEEND
++};
++
++
+ static int digestor(
+     FD_t fdi,
+     FD_t fdo,
+@@ -120,7 +133,19 @@ exit:
+     return rc;
+ }
+ 
+-static rpmRC process_package(FD_t fdi, FD_t validationi)
++static rpmRC validator(FD_t fdi){
++    rpmts ts = rpmtsCreate();
++    rpmtsSetRootDir(ts, rpmcliRootDir);
++    /* rpmlog prints NOTICE to stdout */
++    // rpmlogSetFile(stderr);
++    if(rpmcliVerifySignaturesFD(ts, fdi)){
++	fprintf(stderr, _("Error validating package\n"));
++	return RPMRC_FAIL;
++    }
++    return RPMRC_OK;
++}
++
++static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ {
+     uint32_t diglen;
+     /* GNU C extension: can use diglen from outer context */
+@@ -148,7 +173,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
+     rpm_mode_t mode;
+     char *rpmio_flags = NULL, *zeros;
+     const unsigned char *digest;
+-    rpm_loff_t pos, size, pad, validation_pos;
++    rpm_loff_t pos, size, pad, digest_pos, validation_pos;
+     uint32_t offset_ix = 0;
+     size_t len;
+     int next = 0;
+@@ -278,17 +303,26 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
+ 	    goto exit;
+ 	}
+     }
+-    validation_pos = (
++    digest_pos = (
+ 	pos + sizeof(offset_ix) + sizeof(diglen) +
+ 	offset_ix * (diglen + sizeof(rpm_loff_t))
+     );
+ 
++    ssize_t digest_len = ufdCopy(digestori, fdo);
++    if (digest_len == -1) {
++	fprintf(stderr, _("digest table ufdCopy failed\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++
++    validation_pos = digest_pos + digest_len;
+     ssize_t validation_len = ufdCopy(validationi, fdo);
+     if (validation_len == -1) {
+-	fprintf(stderr, _("digest table ufdCopy failed\n"));
++	fprintf(stderr, _("validation output 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.
+@@ -307,11 +341,16 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+-    if (Fwrite(&validation_pos, len, 1, fdo) != len) {
++    if (Fwrite(&digest_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 output\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
+     uint64_t magic = MAGIC;
+     len = sizeof(magic);
+     if (Fwrite(&magic, len, 1, fdo) != len) {
+@@ -327,10 +366,156 @@ exit:
+     return rc;
+ }
+ 
++static off_t ufdTee(FD_t sfd, FD_t *fds, int len)
++{
++    char buf[BUFSIZ];
++    ssize_t rdbytes, wrbytes;
++    off_t total = 0;
++
++    while (1) {
++	rdbytes = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
++
++	if (rdbytes > 0) {
++	    for(int i=0; i < len; i++) {
++		wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, fds[i]);
++		if (wrbytes != rdbytes) {
++		    fprintf(stderr, "Error wriing to FD %d: %s\n", i, Fstrerror(fds[i]));
++		    total = -1;
++		    break;
++		}
++	    }
++	    if(total == -1){
++		break;
++	    }
++	    total += wrbytes;
++	} else {
++	    if (rdbytes < 0)
++		total = -1;
++	    break;
++	}
++    }
++
++    return total;
++}
++
++static int teeRpm(FD_t fdi, FD_t digestori) {
++    rpmRC rc;
++    off_t offt = -1;
++    int processorpipefd[2];
++    int validatorpipefd[2];
++    int rpmsignpipefd[2];
++    pid_t cpids[2], w;
++    int wstatus;
++    FD_t fds[2];
++
++     if (pipe(processorpipefd) == -1) {
++	fprintf(stderr, _("Processor pipe failure\n"));
++	return RPMRC_FAIL;
++    }
++
++    if (pipe(validatorpipefd) == -1) {
++	fprintf(stderr, _("Validator pipe failure\n"));
++	return RPMRC_FAIL;
++    }
++
++    if (pipe(rpmsignpipefd) == -1) {
++	fprintf(stderr, _("Validator pipe failure\n"));
++	return RPMRC_FAIL;
++    }
++
++    cpids[0] = fork();
++    if (cpids[0] == 0) {
++	/* child: validator */
++	close(processorpipefd[0]);
++	close(processorpipefd[1]);
++	close(validatorpipefd[1]);
++	close(rpmsignpipefd[0]);
++	FD_t fdi = fdDup(validatorpipefd[0]);
++	// redirect STDOUT to the pipe
++	close(STDOUT_FILENO);
++	FD_t fdo = fdDup(rpmsignpipefd[1]);
++	close(rpmsignpipefd[1]);
++	rc = validator(fdi);
++	if(rc != RPMRC_OK) {
++	    fprintf(stderr, _("Validator failed\n"));
++	}
++	Fclose(fdi);
++	Fclose(fdo);
++	if (rc != RPMRC_OK) {
++	    exit(EXIT_FAILURE);
++	}
++	exit(EXIT_SUCCESS);
++    } else {
++	/* parent: main program */
++	cpids[1] = fork();
++	if (cpids[1] == 0) {
++	    /* child: process_package */
++	    close(validatorpipefd[0]);
++	    close(validatorpipefd[1]);
++	    close(processorpipefd[1]);
++	    close(rpmsignpipefd[1]);
++	    FD_t fdi = fdDup(processorpipefd[0]);
++	    close(processorpipefd[0]);
++	    FD_t validatori = fdDup(rpmsignpipefd[0]);
++	    close(rpmsignpipefd[0]);
++
++	    rc = process_package(fdi, digestori, validatori);
++	    if(rc != RPMRC_OK) {
++		fprintf(stderr, _("Validator failed\n"));
++	    }
++	    Fclose(digestori);
++	    Fclose(validatori);
++	    /* fdi is normally closed through the stacked file gzdi in the
++	     * function
++	     */
++
++	    if (rc != RPMRC_OK) {
++		exit(EXIT_FAILURE);
++	    }
++	    exit(EXIT_SUCCESS);
++
++
++	} else {
++	    /* Actual parent. Read from fdi and write to both processes */
++	    close(processorpipefd[0]);
++	    close(validatorpipefd[0]);
++	    fds[0] = fdDup(processorpipefd[1]);
++	    fds[1] = fdDup(validatorpipefd[1]);
++	    close(validatorpipefd[1]);
++	    close(processorpipefd[1]);
++	    close(rpmsignpipefd[0]);
++	    close(rpmsignpipefd[1]);
++
++	    offt = ufdTee(fdi, fds, 2);
++	    if(offt == -1){
++		fprintf(stderr, _("Failed to tee RPM\n"));
++		rc = RPMRC_FAIL;
++	    }
++	    Fclose(fds[0]);
++	    Fclose(fds[1]);
++	    w = waitpid(cpids[0], &wstatus, 0);
++	    if (w == -1) {
++		fprintf(stderr, _("waitpid cpids[0] failed\n"));
++		rc = RPMRC_FAIL;
++	    }
++	    w = waitpid(cpids[1], &wstatus, 0);
++	    if (w == -1) {
++		fprintf(stderr, _("waitpid cpids[1] failed\n"));
++		rc = RPMRC_FAIL;
++	    }
++	}
++    }
++
++    return rc;
++}
++
+ int main(int argc, char *argv[]) {
+     rpmRC rc;
+     int cprc = 0;
+-    uint8_t algos[argc - 1];
++    poptContext optCon = NULL;
++    const char **args = NULL;
++    int nb_algos = 0;
++
+     int mainpipefd[2];
+     int metapipefd[2];
+     pid_t cpid, w;
+@@ -338,29 +523,30 @@ int main(int argc, char *argv[]) {
+ 
+     xsetprogname(argv[0]);	/* Portability call -- see system.h */
+     rpmReadConfigFiles(NULL, NULL);
++    optCon = rpmcliInit(argc, argv, optionsTable);
++    poptSetOtherOptionHelp(optCon, "[OPTIONS]* <DIGESTALGO>");
+ 
+-    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) {
++    if (poptPeekArg(optCon) == NULL) {
+ 	fprintf(stderr,
+ 		_("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));
++	poptPrintUsage(optCon, stderr, 0);
+ 	exit(EXIT_FAILURE);
+     }
+ 
+-    for (int x = 0; x < (argc - 1); x++) {
+-	if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0)
++    args = poptGetArgs(optCon);
++
++    for (nb_algos=0; args[nb_algos]; nb_algos++);
++    uint8_t algos[nb_algos];
++    for (int x = 0; x < nb_algos; x++) {
++	if (pgpStringVal(PGPVAL_HASHALGO, args[x], &algos[x]) != 0)
+ 	{
+ 	    fprintf(stderr,
+ 		    _("Unable to resolve '%s' as a digest algorithm, exiting\n"),
+-		    argv[x + 1]);
++		    args[x]);
+ 	    exit(EXIT_FAILURE);
+ 	}
+     }
+ 
+-
+     if (pipe(mainpipefd) == -1) {
+ 	fprintf(stderr, _("Main pipe failure\n"));
+ 	exit(EXIT_FAILURE);
+@@ -369,6 +555,7 @@ int main(int argc, char *argv[]) {
+ 	fprintf(stderr, _("Meta pipe failure\n"));
+ 	exit(EXIT_FAILURE);
+     }
++
+     cpid = fork();
+     if (cpid == 0) {
+ 	/* child: digestor */
+@@ -377,7 +564,7 @@ int main(int argc, char *argv[]) {
+ 	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);
++	rc = digestor(fdi, fdo, validationo, algos, nb_algos);
+ 	Fclose(validationo);
+ 	Fclose(fdo);
+ 	Fclose(fdi);
+@@ -386,12 +573,10 @@ int main(int argc, char *argv[]) {
+ 	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.
++	FD_t digestori = fdDup(metapipefd[0]);
++	rc = teeRpm(fdi, digestori);
++	Fclose(digestori);
++	/* Wait for child process (digestor for stdin) to complete.
+ 	 */
+ 	if (rc != RPMRC_OK) {
+ 	    if (kill(cpid, SIGTERM) != 0) {
+@@ -402,7 +587,7 @@ int main(int argc, char *argv[]) {
+ 	}
+ 	w = waitpid(cpid, &wstatus, 0);
+ 	if (w == -1) {
+-	    fprintf(stderr, _("waitpid failed\n"));
++	    fprintf(stderr, _("waitpid %d failed %s\n"), cpid, strerror(errno));
+ 	    cprc = EXIT_FAILURE;
+ 	} else if (WIFEXITED(wstatus)) {
+ 	    cprc = WEXITSTATUS(wstatus);
+-- 
+2.35.1
+
diff --git a/SOURCES/0007-rpm2extents-write-RC-and-output-to-transcodedfile-me.patch b/SOURCES/0007-rpm2extents-write-RC-and-output-to-transcodedfile-me.patch
new file mode 100644
index 0000000..07c84fd
--- /dev/null
+++ b/SOURCES/0007-rpm2extents-write-RC-and-output-to-transcodedfile-me.patch
@@ -0,0 +1,282 @@
+From a4755a5ed793ca439bb23b804ba7a8ab080ff110 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Fri, 28 Jan 2022 19:19:45 -0800
+Subject: [PATCH 07/30] [rpm2extents] write RC and output to transcodedfile
+ metadata
+
+create a new rpmcliVerifySignaturesFD function which takes a custom callback
+that allows collecting a textual output of the validation, similar to what
+rpmsign command would give.
+---
+ lib/rpmchecksig.c | 67 +++++++++++++++++++++++++++++++++++--------
+ lib/rpmcli.h      |  5 ++--
+ rpm2extents.c     | 73 +++++++++++++++++++++++++++++++----------------
+ 3 files changed, 106 insertions(+), 39 deletions(-)
+
+diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c
+index 1a6b95323..fcdbb424f 100644
+--- a/lib/rpmchecksig.c
++++ b/lib/rpmchecksig.c
+@@ -260,6 +260,29 @@ exit:
+     return rc;
+ }
+ 
++static int rpmpkgVerifySigsFD(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
++			   FD_t fd, rpmsinfoCb cb, void *cbdata)
++{
++    char *msg = NULL;
++    int rc;
++    struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);
++
++    rc = rpmpkgRead(vs, fd, NULL, NULL, &msg);
++
++    if (rc)
++	goto exit;
++
++    rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, cb, cbdata);
++
++exit:
++    if (rc && msg)
++	rpmlog(RPMLOG_ERR, "%s\n", msg);
++    rpmvsFree(vs);
++    free(msg);
++    return rc;
++}
++
++
+ /* Wrapper around rpmkVerifySigs to preserve API */
+ int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn)
+ {
+@@ -305,12 +328,38 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv)
+     return res;
+ }
+ 
+-int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi)
++struct vfydatafd_s {
++    size_t len;
++    char msg[BUFSIZ];
++};
++
++
++static int vfyFDCb(struct rpmsinfo_s *sinfo, void *cbdata)
+ {
+-    int res = 0;
++    struct vfydatafd_s *vd = cbdata;
++    char *vmsg, *msg;
++    size_t n;
++    size_t remainder = BUFSIZ - vd->len;
++
++    vmsg = rpmsinfoMsg(sinfo);
++    rasprintf(&msg, "    %s\n", vmsg);
++    n = rstrlcpy(vd->msg + vd->len, msg, remainder);
++    free(vmsg);
++    free(msg);
++    if(n <= remainder){
++	vd->len += n;
++    }
++    return 1;
++}
++
++
++int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi, char **msg)
++{
++    rpmRC rc = RPMRC_FAIL;
+     rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
+     rpmVSFlags vsflags = rpmtsVfyFlags(ts);
+     int vfylevel = rpmtsVfyLevel(ts);
++    struct vfydatafd_s vd = {.len = 0};
+ 
+     vsflags |= rpmcliVSFlags;
+     if (rpmcliVfyLevelMask) {
+@@ -318,19 +367,13 @@ int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi)
+ 	rpmtsSetVfyLevel(ts, vfylevel);
+     }
+ 
+-    FD_t fd = fdDup(Fileno(fdi));
+-    if (fd == NULL || Ferror(fd)) {
+-	rpmlog(RPMLOG_ERR, _("fdDup failed: %s\n"), Fstrerror(fd));
+-	res++;
+-    } else if (rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, "stdin")) {
+-	res++;
++    if (!rpmpkgVerifySigsFD(keyring, vfylevel, vsflags, fdi, vfyFDCb, &vd)) {
++	rc = RPMRC_OK;
+     }
+-
+-    lseek(Fileno(fd), SEEK_SET, 0);
+-    Fclose(fd);
++    *msg = strdup(vd.msg);
+     rpmsqPoll();
+ 
+     rpmKeyringFree(keyring);
+-    return res;
++    return rc;
+ }
+ 
+diff --git a/lib/rpmcli.h b/lib/rpmcli.h
+index 52443e459..7ff48b37a 100644
+--- a/lib/rpmcli.h
++++ b/lib/rpmcli.h
+@@ -413,12 +413,13 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv);
+ 
+ 
+ /** \ingroup rpmcli
+- * Verify package signatures
++ * Verify package signatures.
+  * @param ts		transaction set
+  * @param fd		a file descriptor to verify
++ * @param msg		a string containing textual information about the verification, similar to rpmcliVerifySignatures output.
+  * @return		0 on success
+  */
+-int rpmcliVerifySignaturesFD(rpmts ts, FD_t fd);
++int rpmcliVerifySignaturesFD(rpmts ts, FD_t fd, char **msg);
+ 
+ #ifdef __cplusplus
+ }
+diff --git a/rpm2extents.c b/rpm2extents.c
+index d8e582676..065a00306 100644
+--- a/rpm2extents.c
++++ b/rpm2extents.c
+@@ -133,16 +133,38 @@ exit:
+     return rc;
+ }
+ 
+-static rpmRC validator(FD_t fdi){
++static rpmRC validator(FD_t fdi, FD_t fdo){
++    int rc;
++    char *msg = NULL;
+     rpmts ts = rpmtsCreate();
++    size_t len;
++
+     rpmtsSetRootDir(ts, rpmcliRootDir);
+-    /* rpmlog prints NOTICE to stdout */
+-    // rpmlogSetFile(stderr);
+-    if(rpmcliVerifySignaturesFD(ts, fdi)){
++    rc = rpmcliVerifySignaturesFD(ts, fdi, &msg);
++    if(rc){
+ 	fprintf(stderr, _("Error validating package\n"));
+-	return RPMRC_FAIL;
+     }
+-    return RPMRC_OK;
++    len = sizeof(rc);
++    if (Fwrite(&rc, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write validator RC code %d\n"), rc);
++	goto exit;
++    }
++    size_t validator_len = msg ? strlen(msg) : 0;
++    len = sizeof(validator_len);
++    if (Fwrite(&validator_len, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write validator output length code %zd\n"), validator_len);
++	goto exit;
++    }
++    if (Fwrite(msg, validator_len, 1, fdo) != validator_len) {
++	fprintf(stderr, _("Unable to write validator output %s\n"), msg);
++	goto exit;
++    }
++
++exit:
++    if(msg) {
++	free(msg);
++    }
++    return rc ? RPMRC_FAIL : RPMRC_OK;
+ }
+ 
+ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+@@ -173,7 +195,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+     rpm_mode_t mode;
+     char *rpmio_flags = NULL, *zeros;
+     const unsigned char *digest;
+-    rpm_loff_t pos, size, pad, digest_pos, validation_pos;
++    rpm_loff_t pos, size, pad, digest_pos, validation_pos, digest_table_pos;
+     uint32_t offset_ix = 0;
+     size_t len;
+     int next = 0;
+@@ -278,6 +300,16 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+     qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset),
+ 	  digestoffsetCmp);
+ 
++    validation_pos = pos;
++    ssize_t validation_len = ufdCopy(validationi, fdo);
++    if (validation_len == -1) {
++	fprintf(stderr, _("validation output ufdCopy failed\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++
++    digest_table_pos = validation_pos + validation_len;
++
+     len = sizeof(offset_ix);
+     if (Fwrite(&offset_ix, len, 1, fdo) != len) {
+ 	fprintf(stderr, _("Unable to write length of table\n"));
+@@ -304,7 +336,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ 	}
+     }
+     digest_pos = (
+-	pos + sizeof(offset_ix) + sizeof(diglen) +
++	digest_table_pos + sizeof(offset_ix) + sizeof(diglen) +
+ 	offset_ix * (diglen + sizeof(rpm_loff_t))
+     );
+ 
+@@ -315,14 +347,6 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ 	goto exit;
+     }
+ 
+-    validation_pos = digest_pos + digest_len;
+-    ssize_t validation_len = ufdCopy(validationi, fdo);
+-    if (validation_len == -1) {
+-	fprintf(stderr, _("validation output 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.
+@@ -336,18 +360,18 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ 	goto exit;
+     }
+     zeros = _free(zeros);
+-    if (Fwrite(&pos, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write offset of digest table\n"));
++    if (Fwrite(&validation_pos, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write offset of validation output\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+-    if (Fwrite(&digest_pos, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write offset of validation table\n"));
++    if (Fwrite(&digest_table_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 output\n"));
++    if (Fwrite(&digest_pos, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write offset of validation table\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+@@ -431,11 +455,9 @@ static int teeRpm(FD_t fdi, FD_t digestori) {
+ 	close(validatorpipefd[1]);
+ 	close(rpmsignpipefd[0]);
+ 	FD_t fdi = fdDup(validatorpipefd[0]);
+-	// redirect STDOUT to the pipe
+-	close(STDOUT_FILENO);
+ 	FD_t fdo = fdDup(rpmsignpipefd[1]);
+ 	close(rpmsignpipefd[1]);
+-	rc = validator(fdi);
++	rc = validator(fdi, fdo);
+ 	if(rc != RPMRC_OK) {
+ 	    fprintf(stderr, _("Validator failed\n"));
+ 	}
+@@ -486,6 +508,7 @@ static int teeRpm(FD_t fdi, FD_t digestori) {
+ 	    close(rpmsignpipefd[0]);
+ 	    close(rpmsignpipefd[1]);
+ 
++	    rc = RPMRC_OK;
+ 	    offt = ufdTee(fdi, fds, 2);
+ 	    if(offt == -1){
+ 		fprintf(stderr, _("Failed to tee RPM\n"));
+-- 
+2.35.1
+
diff --git a/SOURCES/0008-rpm2extents-Add-script-to-troubleshoot-transcoded-fi.patch b/SOURCES/0008-rpm2extents-Add-script-to-troubleshoot-transcoded-fi.patch
new file mode 100644
index 0000000..ddbf31d
--- /dev/null
+++ b/SOURCES/0008-rpm2extents-Add-script-to-troubleshoot-transcoded-fi.patch
@@ -0,0 +1,116 @@
+From c705a6287f8c7fb5e37dad0ac87257731a41fa69 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Sat, 29 Jan 2022 07:00:27 +0000
+Subject: [PATCH 08/30] [rpm2extents] Add script to troubleshoot transcoded
+ file content This script is essentially dumping the metadata written at the
+ end of the transcoded files, it will also be used as part of the end-to-end
+ tests.
+
+---
+ scripts/rpm2extents_dump | 94 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 94 insertions(+)
+ create mode 100755 scripts/rpm2extents_dump
+
+diff --git a/scripts/rpm2extents_dump b/scripts/rpm2extents_dump
+new file mode 100755
+index 000000000..596a59a49
+--- /dev/null
++++ b/scripts/rpm2extents_dump
+@@ -0,0 +1,94 @@
++#!/usr/bin/env python3
++
++import argparse
++import binascii
++import os
++import struct
++import sys
++
++MAGIC_SIZE = 8
++MAGIC_STR = b'KWTSH100'
++
++POS_SIZE = 8
++
++def keep_position(func):
++    def wrapper(*args, **kwargs):
++        curr = args[0].tell()
++        res = func(*args, **kwargs)
++        f.seek(curr, os.SEEK_SET)
++        return res
++    return wrapper
++
++def read_validation_digest(f, validation_offset):
++	digests = []
++    # validation
++	f.seek(validation_offset, os.SEEK_SET)
++	val_content_len, val_digests_num = struct.unpack('=QI', f.read(8+4))
++	for i in range(val_digests_num):
++		algo_name_len, digest_len = struct.unpack('=II', f.read(8))
++		algo_name, digest = struct.unpack(f'{algo_name_len}s{digest_len}s', f.read(algo_name_len+digest_len))
++		digests.append((algo_name, binascii.hexlify(digest)))
++	return digests
++
++
++def read_digests_table(f, digest_offset):
++	digests = []
++    # validation
++	f.seek(digest_offset, os.SEEK_SET)
++	table_len, digest_len = struct.unpack('=II', f.read(8))
++
++	for i in range(table_len):
++		digest, pos = struct.unpack(f'{digest_len}sQ', f.read(digest_len + 8))
++		digests.append((pos, binascii.hexlify(digest)))
++	return digests
++
++def read_signature_output(f, signature_offset):
++    f.seek(signature_offset, os.SEEK_SET)
++    signature_rc, signature_output_len = struct.unpack('=IQ', f.read(12))
++    return signature_rc, f.read(signature_output_len)
++
++@keep_position
++def parse_file(f):
++	digests = []
++	pos_table_offset = f.seek(-8 - 3*POS_SIZE, os.SEEK_END)
++	signature_offset, digest_offset, validation_offset = struct.unpack('=QQQ', f.read(3*POS_SIZE))
++
++	validation_digests = read_validation_digest(f, validation_offset)
++	digests_table = read_digests_table(f, digest_offset)
++	signature_ouput = read_signature_output(f, signature_offset)
++
++	return validation_digests, digests_table, signature_ouput
++
++@keep_position
++def is_transcoded(f):
++    f.seek(-MAGIC_SIZE, os.SEEK_END)
++    magic = f.read(MAGIC_SIZE)
++    return magic == MAGIC_STR
++
++def arg_parse():
++    parser = argparse.ArgumentParser()
++    parser.add_argument('--dump-signature', action='store_true')
++    parser.add_argument('--dump-file-digest-table', action='store_true')
++    parser.add_argument('--dump-digests', action='store_true')
++    parser.add_argument('file')
++
++    return parser.parse_args()
++
++if __name__ == '__main__':
++    args = arg_parse()
++    f = open(args.file, 'rb')
++    if not is_transcoded(f):
++        sys.exit(1)
++
++    validation_digests, digests_table, signature_output = parse_file(f)
++    if(args.dump_file_digest_table):
++        for digest in digests_table:
++            print(f"FileDigest {hex(digest[0])}: {digest[1]}")
++
++    if(args.dump_digests):
++        for validation_digest in validation_digests:
++            print(f"HeaderDigest {validation_digest[0]} {validation_digest[1]}")
++
++    if(args.dump_signature):
++        print(f"RPMSignOutput RC {signature_output[0]}\nRPMSignOutput Content {signature_output[1].decode()}")
++
+-- 
+2.35.1
+
diff --git a/SOURCES/0009-rpm2extents-Add-test-verifying-RC-code-and-signature.patch b/SOURCES/0009-rpm2extents-Add-test-verifying-RC-code-and-signature.patch
new file mode 100644
index 0000000..a0e9757
--- /dev/null
+++ b/SOURCES/0009-rpm2extents-Add-test-verifying-RC-code-and-signature.patch
@@ -0,0 +1,70 @@
+From 44b86112136e4804eb606636cbcb4ae847cad773 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Sat, 29 Jan 2022 07:05:18 +0000
+Subject: [PATCH 09/30] [rpm2extents] Add test verifying RC code and signature
+ validation text
+
+When transcoding an RPM, the RPM signatures are being validated on the fly and the result from the signature validation is being written at the tail of the transcoded file.
+This test check that we get the expected results and is based on the rpmsigdig.at test.
+---
+ Makefile.am          |  2 +-
+ tests/rpm2extents.at | 33 +++++++++++++++++++++++++++++++++
+ 2 files changed, 34 insertions(+), 1 deletion(-)
+
+diff --git a/Makefile.am b/Makefile.am
+index 288668819..96542c8c8 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -185,7 +185,7 @@ bin_PROGRAMS +=		rpmgraph
+ rpmgraph_SOURCES =	tools/rpmgraph.c
+ rpmgraph_LDADD =	lib/librpm.la rpmio/librpmio.la @WITH_POPT_LIB@
+ 
+-dist_bin_SCRIPTS =	scripts/gendiff
++dist_bin_SCRIPTS =	scripts/gendiff scripts/rpm2extents_dump
+ 
+ rpmconfig_DATA = rpmrc
+ rpmrc: $(top_srcdir)/rpmrc.in
+diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
+index f943b9af4..baea987e4 100644
+--- a/tests/rpm2extents.at
++++ b/tests/rpm2extents.at
+@@ -29,3 +29,36 @@ AT_CHECK([runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rp
+ [ignore])
+ AT_CLEANUP
+ 
++# Check that tailer writes checksig return code and content.
++#
++AT_SETUP([rpm2extents signature])
++AT_KEYWORDS([rpm2extents digest signature])
++AT_CHECK([
++RPMDB_INIT
++
++runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null
++rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm
++runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub
++runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm
++rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm
++],
++[0],
++[RPMSignOutput RC 2
++RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY
++    Header SHA256 digest: OK
++    Header SHA1 digest: OK
++    Payload SHA256 digest: OK
++    V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY
++    MD5 digest: OK
++
++RPMSignOutput RC 0
++RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
++    Header SHA256 digest: OK
++    Header SHA1 digest: OK
++    Payload SHA256 digest: OK
++    V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
++    MD5 digest: OK
++
++],
++[])
++AT_CLEANUP
+-- 
+2.35.1
+
diff --git a/SOURCES/0010-rpm2extents-Make-rpmkeys-support-reading-metadata-fr.patch b/SOURCES/0010-rpm2extents-Make-rpmkeys-support-reading-metadata-fr.patch
new file mode 100644
index 0000000..98987d6
--- /dev/null
+++ b/SOURCES/0010-rpm2extents-Make-rpmkeys-support-reading-metadata-fr.patch
@@ -0,0 +1,204 @@
+From 7da1e826ccb08fdd404524146736b3f12a473e31 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Mon, 31 Jan 2022 14:42:25 -0800
+Subject: [PATCH 10/30] [rpm2extents] Make rpmkeys support reading metadata
+ from transcoded footer
+
+If the file is seekable and is a transcoded file, read signature validation from transcoded file tail metadata
+---
+ lib/rpmchecksig.c    | 112 ++++++++++++++++++++++++++++++++++++++++++-
+ tests/rpm2extents.at |  34 ++++++++++++-
+ 2 files changed, 144 insertions(+), 2 deletions(-)
+
+diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c
+index fcdbb424f..6164d012c 100644
+--- a/lib/rpmchecksig.c
++++ b/lib/rpmchecksig.c
+@@ -24,6 +24,11 @@
+ 
+ #include "debug.h"
+ 
++/* magic value at end of file (64 bits) that indicates this is a transcoded
++ * rpm.
++ */
++#define MAGIC 3472329499408095051
++
+ int _print_pkts = 0;
+ 
+ static int doImport(rpmts ts, const char *fn, char *buf, ssize_t blen)
+@@ -220,6 +225,107 @@ exit:
+     return rc;
+ }
+ 
++static rpmRC isTranscodedRpm(FD_t fd) {
++    rpmRC rc = RPMRC_NOTFOUND;
++    rpm_loff_t current;
++    uint64_t magic;
++    size_t len;
++
++    // If the file is not seekable, we cannot detect whether or not it is transcoded.
++    if(Fseek(fd, 0, SEEK_CUR) < 0) {
++        return RPMRC_FAIL;
++    }
++    current = Ftell(fd);
++
++    if(Fseek(fd, -(sizeof(magic)), SEEK_END) < 0) {
++	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for magic\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    len = sizeof(magic);
++    if (Fread(&magic, len, 1, fd) != len) {
++	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read magic\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    if (magic != MAGIC) {
++	rpmlog(RPMLOG_DEBUG, _("isTranscodedRpm: not transcoded\n"));
++	rc = RPMRC_NOTFOUND;
++	goto exit;
++    }
++    rc = RPMRC_OK;
++exit:
++    if (Fseek(fd, current, SEEK_SET) < 0) {
++	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n"));
++    }
++    return rc;
++}
++
++static int rpmpkgVerifySigsTranscoded(FD_t fd){
++    rpm_loff_t current;
++    uint64_t magic;
++    rpm_loff_t offset;
++    int32_t rc;
++    size_t len;
++    uint64_t content_len;
++    char *content = NULL;
++
++    current = Ftell(fd);
++
++    if(Fseek(fd, -(sizeof(magic) + 3 * sizeof(offset) ), SEEK_END) < 0) {
++	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: failed to seek for offset\n"));
++	rc = -1;
++	goto exit;
++    }
++
++    len = sizeof(offset);
++    if (Fread(&offset, len, 1, fd) != len) {
++	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read Signature Verification offset\n"));
++	rc = -1;
++	goto exit;
++    }
++
++    if(Fseek(fd,  offset, SEEK_SET) < 0) {
++	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to seek signature verification offset\n"));
++	rc = -1;
++	goto exit;
++    }
++    len = sizeof(rc);
++    if (Fread(&rc, len, 1, fd) != len) {
++	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read Signature Verification RC\n"));
++	rc = -1;
++	goto exit;
++    }
++
++    len = sizeof(content_len);
++    if (Fread(&content_len, len, 1, fd) != len) {
++	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read signature content length\n"));
++	goto exit;
++    }
++
++    content = malloc(content_len + 1);
++    if(content == NULL) {
++	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to allocate memory to read signature content\n"));
++	goto exit;
++    }
++    content[content_len] = 0;
++    if (Fread(content, content_len, 1, fd) != content_len) {
++	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read signature content\n"));
++	goto exit;
++    }
++
++    rpmlog(RPMLOG_NOTICE, "%s", content);
++exit:
++    if(content){
++	free(content);
++    }
++    if (Fseek(fd, current, SEEK_SET) < 0) {
++	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: unable to seek back to original location\n"));
++    }
++    return rc;
++
++}
++
+ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
+ 			   FD_t fd, const char *fn)
+ {
+@@ -229,10 +335,14 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
+ 			    .verbose = rpmIsVerbose(),
+     };
+     int rc;
+-    struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);
+ 
+     rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd.verbose ? "\n" : "");
+ 
++    if(isTranscodedRpm(fd) == RPMRC_OK){
++	return rpmpkgVerifySigsTranscoded(fd);
++    }
++    struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);
++
+     rc = rpmpkgRead(vs, fd, NULL, NULL, &msg);
+ 
+     if (rc)
+diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
+index baea987e4..18accfc75 100644
+--- a/tests/rpm2extents.at
++++ b/tests/rpm2extents.at
+@@ -29,7 +29,7 @@ AT_CHECK([runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rp
+ [ignore])
+ AT_CLEANUP
+ 
+-# Check that tailer writes checksig return code and content.
++# Check that transcoder writes checksig return code and content.
+ #
+ AT_SETUP([rpm2extents signature])
+ AT_KEYWORDS([rpm2extents digest signature])
+@@ -62,3 +62,35 @@ RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
+ ],
+ [])
+ AT_CLEANUP
++
++AT_SETUP([rpm2extents signature verification])
++AT_KEYWORDS([rpm2extents digest signature])
++AT_CHECK([
++RPMDB_INIT
++
++runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null
++runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?
++runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub
++runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm
++runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?
++],
++[0],
++[/tmp/hello-2.0-1.x86_64-signed.rpm:
++    Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY
++    Header SHA256 digest: OK
++    Header SHA1 digest: OK
++    Payload SHA256 digest: OK
++    V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY
++    MD5 digest: OK
++1
++/tmp/hello-2.0-1.x86_64-signed.rpm:
++    Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
++    Header SHA256 digest: OK
++    Header SHA1 digest: OK
++    Payload SHA256 digest: OK
++    V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
++    MD5 digest: OK
++0
++],
++[])
++AT_CLEANUP
+-- 
+2.35.1
+
diff --git a/SOURCES/0011-rpm2extents-Perform-digest-computation-within-the-va.patch b/SOURCES/0011-rpm2extents-Perform-digest-computation-within-the-va.patch
new file mode 100644
index 0000000..ea98bed
--- /dev/null
+++ b/SOURCES/0011-rpm2extents-Perform-digest-computation-within-the-va.patch
@@ -0,0 +1,389 @@
+From 86776bf17f1644c76fdf8b87042645cf77bd3873 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Wed, 2 Feb 2022 13:34:28 -0800
+Subject: [PATCH 11/30] [rpm2extents] Perform digest computation within the
+ validator
+
+The validator calls `rpmcliVerifySignaturesFD` which under the hood
+performs `Fread`. Digests are computed/updated for each `Fread`.
+
+This diffs takes advantage of that by initializing the digest before
+calling `rpmcliVerifySignaturesFD`. Once `rpmcliVerifySignaturesFD` as
+returned and the file has been read, the digests are available.
+
+This saves us from spawning a `digestor` process, as well as performing
+an extra file read within it.
+---
+ rpm2extents.c | 234 +++++++++++++++++++++++---------------------------
+ 1 file changed, 106 insertions(+), 128 deletions(-)
+
+diff --git a/rpm2extents.c b/rpm2extents.c
+index 065a00306..e316a2834 100644
+--- a/rpm2extents.c
++++ b/rpm2extents.c
+@@ -64,38 +64,37 @@ static struct poptOption optionsTable[] = {
+ };
+ 
+ 
+-static int digestor(
++static void FDDigestInit(FD_t fdi, uint8_t algos[], uint32_t algos_len){
++    int algo;
++
++    for (algo = 0; algo < algos_len; algo++) {
++	fdInitDigest(fdi, algos[algo], 0);
++    }
++}
++
++static int FDWriteDigests(
+     FD_t fdi,
+     FD_t fdo,
+-    FD_t validationo,
+     uint8_t algos[],
+-    uint32_t algos_len
+-)
++    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;
+-    }
++    ssize_t fdilength = fdOp(fdi, FDSTAT_READ)->bytes;
+ 
+     len = sizeof(fdilength);
+-    if (Fwrite(&fdilength, len, 1, validationo) != len) {
++    if (Fwrite(&fdilength, len, 1, fdo) != 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"));
++    if (Fwrite(&algos_len, len, 1, fdo) != len) {
++	algo_digest_len = (uint32_t)filedigest_len;
++	fprintf(stderr, _("Unable to write number of digests\n"));
+ 	goto exit;
+     }
+     for (algo = 0; algo < algos_len; algo++) {
+@@ -106,24 +105,24 @@ static int digestor(
+ 	algo_digest_len = (uint32_t)filedigest_len;
+ 
+ 	len = sizeof(algo_name_len);
+-	if (Fwrite(&algo_name_len, len, 1, validationo) != len) {
++	if (Fwrite(&algo_name_len, len, 1, fdo) != len) {
+ 	    fprintf(stderr,
+-		    _("Unable to write validation algo name length\n"));
++		    _("Unable to write digest algo name length\n"));
+ 	    goto exit;
+ 	}
+ 	len = sizeof(algo_digest_len);
+-	if (Fwrite(&algo_digest_len, len, 1, validationo) != len) {
++	if (Fwrite(&algo_digest_len, len, 1, fdo) != len) {
+ 	    fprintf(stderr,
+-		    _("Unable to write number of bytes for validation digest\n"));
++		    _("Unable to write number of bytes for 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"));
++	if (Fwrite(algo_name, algo_name_len, 1, fdo) != algo_name_len) {
++	    fprintf(stderr, _("Unable to write digest algo name\n"));
+ 	    goto exit;
+ 	}
+-	if (Fwrite(filedigest, algo_digest_len, 1, validationo ) != algo_digest_len) {
++	if (Fwrite(filedigest, algo_digest_len, 1, fdo ) != algo_digest_len) {
+ 	    fprintf(stderr,
+-		    _("Unable to write validation digest value %u, %zu\n"),
++		    _("Unable to write digest value %u, %zu\n"),
+ 		    algo_digest_len, filedigest_len);
+ 	    goto exit;
+ 	}
+@@ -133,38 +132,66 @@ exit:
+     return rc;
+ }
+ 
+-static rpmRC validator(FD_t fdi, FD_t fdo){
+-    int rc;
+-    char *msg = NULL;
+-    rpmts ts = rpmtsCreate();
++static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) {
+     size_t len;
++    rpmRC rc = RPMRC_FAIL;
+ 
+-    rpmtsSetRootDir(ts, rpmcliRootDir);
+-    rc = rpmcliVerifySignaturesFD(ts, fdi, &msg);
+-    if(rc){
+-	fprintf(stderr, _("Error validating package\n"));
++    if(rpmvsrc){
++	fprintf(stderr, _("Error verifying package signatures\n"));
+     }
+-    len = sizeof(rc);
+-    if (Fwrite(&rc, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write validator RC code %d\n"), rc);
++
++    len = sizeof(rpmvsrc);
++    if (Fwrite(&rpmvsrc, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write signature verification RC code %d\n"), rpmvsrc);
++	goto exit;
++    }
++    size_t content_len = msg ? strlen(msg) : 0;
++    len = sizeof(content_len);
++    if (Fwrite(&content_len, len, 1, fdo) != len) {
++	fprintf(stderr, _("Unable to write signature verification output length %zd\n"), content_len);
+ 	goto exit;
+     }
+-    size_t validator_len = msg ? strlen(msg) : 0;
+-    len = sizeof(validator_len);
+-    if (Fwrite(&validator_len, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write validator output length code %zd\n"), validator_len);
++    if (Fwrite(msg, content_len, 1, fdo) != content_len) {
++	fprintf(stderr, _("Unable to write signature verification output %s\n"), msg);
+ 	goto exit;
+     }
+-    if (Fwrite(msg, validator_len, 1, fdo) != validator_len) {
+-	fprintf(stderr, _("Unable to write validator output %s\n"), msg);
++
++    rc = RPMRC_OK;
++exit:
++
++    return rc;
++}
++
++static rpmRC validator(FD_t fdi, FD_t digesto, FD_t sigo,
++	uint8_t algos[],
++	uint32_t algos_len){
++    int rpmvsrc;
++    rpmRC rc = RPMRC_FAIL;
++    char *msg = NULL;
++    rpmts ts = rpmtsCreate();
++
++    rpmtsSetRootDir(ts, rpmcliRootDir);
++
++    FDDigestInit(fdi, algos, algos_len);
++
++    rpmvsrc = rpmcliVerifySignaturesFD(ts, fdi, &msg);
++
++    // Write result of digest computation
++    if(FDWriteDigests(fdi, digesto, algos, algos_len) != RPMRC_OK) {
++	fprintf(stderr, _("Failed to write digests"));
+ 	goto exit;
+     }
+ 
++    // Write result of signature validation.
++    if(FDWriteSignaturesValidation(sigo, rpmvsrc, msg)) {
++	goto exit;
++    }
++    rc = RPMRC_OK;
+ exit:
+     if(msg) {
+ 	free(msg);
+     }
+-    return rc ? RPMRC_FAIL : RPMRC_OK;
++    return rc;
+ }
+ 
+ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+@@ -422,12 +449,16 @@ static off_t ufdTee(FD_t sfd, FD_t *fds, int len)
+     return total;
+ }
+ 
+-static int teeRpm(FD_t fdi, FD_t digestori) {
+-    rpmRC rc;
++static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) {
++    rpmRC rc = RPMRC_FAIL;
+     off_t offt = -1;
++    // tee-ed stdin
+     int processorpipefd[2];
+     int validatorpipefd[2];
+-    int rpmsignpipefd[2];
++    // metadata
++    int meta_digestpipefd[2];
++    int meta_rpmsignpipefd[2];
++
+     pid_t cpids[2], w;
+     int wstatus;
+     FD_t fds[2];
+@@ -442,8 +473,13 @@ static int teeRpm(FD_t fdi, FD_t digestori) {
+ 	return RPMRC_FAIL;
+     }
+ 
+-    if (pipe(rpmsignpipefd) == -1) {
+-	fprintf(stderr, _("Validator pipe failure\n"));
++    if (pipe(meta_digestpipefd) == -1) {
++	fprintf(stderr, _("Meta digest pipe failure\n"));
++	return RPMRC_FAIL;
++    }
++
++    if (pipe(meta_rpmsignpipefd) == -1) {
++	fprintf(stderr, _("Meta rpm signature pipe failure\n"));
+ 	return RPMRC_FAIL;
+     }
+ 
+@@ -453,16 +489,20 @@ static int teeRpm(FD_t fdi, FD_t digestori) {
+ 	close(processorpipefd[0]);
+ 	close(processorpipefd[1]);
+ 	close(validatorpipefd[1]);
+-	close(rpmsignpipefd[0]);
++	close(meta_digestpipefd[0]);
++	close(meta_rpmsignpipefd[0]);
+ 	FD_t fdi = fdDup(validatorpipefd[0]);
+-	FD_t fdo = fdDup(rpmsignpipefd[1]);
+-	close(rpmsignpipefd[1]);
+-	rc = validator(fdi, fdo);
++	FD_t digesto = fdDup(meta_digestpipefd[1]);
++	FD_t sigo = fdDup(meta_rpmsignpipefd[1]);
++	close(meta_digestpipefd[1]);
++	close(meta_rpmsignpipefd[1]);
++	rc = validator(fdi, digesto, sigo, algos, algos_len);
+ 	if(rc != RPMRC_OK) {
+ 	    fprintf(stderr, _("Validator failed\n"));
+ 	}
+ 	Fclose(fdi);
+-	Fclose(fdo);
++	Fclose(digesto);
++	Fclose(sigo);
+ 	if (rc != RPMRC_OK) {
+ 	    exit(EXIT_FAILURE);
+ 	}
+@@ -475,18 +515,21 @@ static int teeRpm(FD_t fdi, FD_t digestori) {
+ 	    close(validatorpipefd[0]);
+ 	    close(validatorpipefd[1]);
+ 	    close(processorpipefd[1]);
+-	    close(rpmsignpipefd[1]);
++	    close(meta_digestpipefd[1]);
++	    close(meta_rpmsignpipefd[1]);
+ 	    FD_t fdi = fdDup(processorpipefd[0]);
+ 	    close(processorpipefd[0]);
+-	    FD_t validatori = fdDup(rpmsignpipefd[0]);
+-	    close(rpmsignpipefd[0]);
++	    FD_t sigi = fdDup(meta_rpmsignpipefd[0]);
++	    close(meta_rpmsignpipefd[0]);
++	    FD_t digestori = fdDup(meta_digestpipefd[0]);
++	    close(meta_digestpipefd[0]);
+ 
+-	    rc = process_package(fdi, digestori, validatori);
++	    rc = process_package(fdi, digestori, sigi);
+ 	    if(rc != RPMRC_OK) {
+ 		fprintf(stderr, _("Validator failed\n"));
+ 	    }
+ 	    Fclose(digestori);
+-	    Fclose(validatori);
++	    Fclose(sigi);
+ 	    /* fdi is normally closed through the stacked file gzdi in the
+ 	     * function
+ 	     */
+@@ -505,8 +548,10 @@ static int teeRpm(FD_t fdi, FD_t digestori) {
+ 	    fds[1] = fdDup(validatorpipefd[1]);
+ 	    close(validatorpipefd[1]);
+ 	    close(processorpipefd[1]);
+-	    close(rpmsignpipefd[0]);
+-	    close(rpmsignpipefd[1]);
++	    close(meta_digestpipefd[0]);
++	    close(meta_digestpipefd[1]);
++	    close(meta_rpmsignpipefd[0]);
++	    close(meta_rpmsignpipefd[1]);
+ 
+ 	    rc = RPMRC_OK;
+ 	    offt = ufdTee(fdi, fds, 2);
+@@ -534,16 +579,10 @@ static int teeRpm(FD_t fdi, FD_t digestori) {
+ 
+ int main(int argc, char *argv[]) {
+     rpmRC rc;
+-    int cprc = 0;
+     poptContext optCon = NULL;
+     const char **args = NULL;
+     int nb_algos = 0;
+ 
+-    int mainpipefd[2];
+-    int metapipefd[2];
+-    pid_t cpid, w;
+-    int wstatus;
+-
+     xsetprogname(argv[0]);	/* Portability call -- see system.h */
+     rpmReadConfigFiles(NULL, NULL);
+     optCon = rpmcliInit(argc, argv, optionsTable);
+@@ -570,69 +609,8 @@ int main(int argc, char *argv[]) {
+ 	}
+     }
+ 
+-    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, nb_algos);
+-	Fclose(validationo);
+-	Fclose(fdo);
+-	Fclose(fdi);
+-    } else {
+-	/* parent: main program */
+-	close(mainpipefd[1]);
+-	close(metapipefd[1]);
+-	FD_t fdi = fdDup(mainpipefd[0]);
+-	FD_t digestori = fdDup(metapipefd[0]);
+-	rc = teeRpm(fdi, digestori);
+-	Fclose(digestori);
+-	/* 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 %d failed %s\n"), cpid, strerror(errno));
+-	    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;
+-	}
+-    }
++    FD_t fdi = fdDup(STDIN_FILENO);
++    rc = teeRpm(fdi, algos, nb_algos);
+     if (rc != RPMRC_OK) {
+ 	/* translate rpmRC into generic failure return code. */
+ 	return EXIT_FAILURE;
+-- 
+2.35.1
+
diff --git a/SOURCES/0012-rpmextents-Create-an-internal-library-to-make-rpmext.patch b/SOURCES/0012-rpmextents-Create-an-internal-library-to-make-rpmext.patch
new file mode 100644
index 0000000..6a48dc8
--- /dev/null
+++ b/SOURCES/0012-rpmextents-Create-an-internal-library-to-make-rpmext.patch
@@ -0,0 +1,299 @@
+From ecab80b80e3917d3acf0f909c9cc84691a207fc0 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Thu, 3 Feb 2022 21:09:05 -0800
+Subject: [PATCH 12/30] [rpmextents] Create an internal library to make
+ rpmextents file manipulation easier and less duplicated
+
+---
+ lib/Makefile.am           |  3 ++-
+ lib/rpmchecksig.c         | 42 +---------------------------------
+ lib/rpmextents.c          | 46 +++++++++++++++++++++++++++++++++++++
+ lib/rpmextents_internal.h | 22 ++++++++++++++++++
+ plugins/reflink.c         | 48 +++++++++++++--------------------------
+ rpm2extents.c             |  8 ++-----
+ 6 files changed, 89 insertions(+), 80 deletions(-)
+ create mode 100644 lib/rpmextents.c
+ create mode 100644 lib/rpmextents_internal.h
+
+diff --git a/lib/Makefile.am b/lib/Makefile.am
+index 5a1b6ca9b..2f1b3597f 100644
+--- a/lib/Makefile.am
++++ b/lib/Makefile.am
+@@ -40,7 +40,8 @@ librpm_la_SOURCES = \
+ 	rpmscript.h rpmscript.c \
+ 	rpmchroot.c rpmchroot.h \
+ 	rpmplugins.c rpmplugins.h rpmplugin.h rpmug.c rpmug.h \
+-	rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h
++	rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h \
++	rpmextents.c rpmextents_internal.h
+ 
+ librpm_la_LDFLAGS = -version-info $(rpm_version_info)
+ 
+diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c
+index 6164d012c..dc1726a18 100644
+--- a/lib/rpmchecksig.c
++++ b/lib/rpmchecksig.c
+@@ -20,15 +20,11 @@
+ #include "rpmio/rpmio_internal.h" 	/* fdSetBundle() */
+ #include "lib/rpmlead.h"
+ #include "lib/header_internal.h"
++#include "lib/rpmextents_internal.h"
+ #include "lib/rpmvs.h"
+ 
+ #include "debug.h"
+ 
+-/* magic value at end of file (64 bits) that indicates this is a transcoded
+- * rpm.
+- */
+-#define MAGIC 3472329499408095051
+-
+ int _print_pkts = 0;
+ 
+ static int doImport(rpmts ts, const char *fn, char *buf, ssize_t blen)
+@@ -225,42 +221,6 @@ exit:
+     return rc;
+ }
+ 
+-static rpmRC isTranscodedRpm(FD_t fd) {
+-    rpmRC rc = RPMRC_NOTFOUND;
+-    rpm_loff_t current;
+-    uint64_t magic;
+-    size_t len;
+-
+-    // If the file is not seekable, we cannot detect whether or not it is transcoded.
+-    if(Fseek(fd, 0, SEEK_CUR) < 0) {
+-        return RPMRC_FAIL;
+-    }
+-    current = Ftell(fd);
+-
+-    if(Fseek(fd, -(sizeof(magic)), SEEK_END) < 0) {
+-	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for magic\n"));
+-	rc = RPMRC_FAIL;
+-	goto exit;
+-    }
+-    len = sizeof(magic);
+-    if (Fread(&magic, len, 1, fd) != len) {
+-	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read magic\n"));
+-	rc = RPMRC_FAIL;
+-	goto exit;
+-    }
+-    if (magic != MAGIC) {
+-	rpmlog(RPMLOG_DEBUG, _("isTranscodedRpm: not transcoded\n"));
+-	rc = RPMRC_NOTFOUND;
+-	goto exit;
+-    }
+-    rc = RPMRC_OK;
+-exit:
+-    if (Fseek(fd, current, SEEK_SET) < 0) {
+-	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n"));
+-    }
+-    return rc;
+-}
+-
+ static int rpmpkgVerifySigsTranscoded(FD_t fd){
+     rpm_loff_t current;
+     uint64_t magic;
+diff --git a/lib/rpmextents.c b/lib/rpmextents.c
+new file mode 100644
+index 000000000..015277751
+--- /dev/null
++++ b/lib/rpmextents.c
+@@ -0,0 +1,46 @@
++
++#include "system.h"
++
++#include <rpm/rpmlog.h>
++#include <rpm/rpmio.h>
++
++#include "lib/rpmextents_internal.h"
++
++rpmRC isTranscodedRpm(FD_t fd) {
++    rpmRC rc = RPMRC_NOTFOUND;
++    rpm_loff_t current;
++    extents_magic_t magic;
++    size_t len;
++
++    // If the file is not seekable, we cannot detect whether or not it is transcoded.
++    if(Fseek(fd, 0, SEEK_CUR) < 0) {
++        return RPMRC_FAIL;
++    }
++    current = Ftell(fd);
++
++    if(Fseek(fd, -(sizeof(magic)), SEEK_END) < 0) {
++	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for magic\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    len = sizeof(magic);
++    if (Fread(&magic, len, 1, fd) != len) {
++	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read magic\n"));
++	rc = RPMRC_FAIL;
++	goto exit;
++    }
++    if (magic != EXTENTS_MAGIC) {
++	rpmlog(RPMLOG_DEBUG, _("isTranscodedRpm: not transcoded\n"));
++	rc = RPMRC_NOTFOUND;
++	goto exit;
++    }
++    rc = RPMRC_OK;
++exit:
++    if (Fseek(fd, current, SEEK_SET) < 0) {
++	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n"));
++	rc = RPMRC_FAIL;
++    }
++    return rc;
++}
++
++
+diff --git a/lib/rpmextents_internal.h b/lib/rpmextents_internal.h
+new file mode 100644
+index 000000000..57cecfc31
+--- /dev/null
++++ b/lib/rpmextents_internal.h
+@@ -0,0 +1,22 @@
++#ifndef _RPMEXTENTS_INTERNAL_H
++#define _RPMEXTENTS_INTERNAL_H
++
++#ifdef __cplusplus
++extern "C" {
++#endif
++
++#include <stdint.h>
++
++/* magic value at end of file (64 bits) that indicates this is a transcoded
++ * rpm.
++ */
++#define EXTENTS_MAGIC 3472329499408095051
++
++typedef uint64_t extents_magic_t;
++
++rpmRC isTranscodedRpm(FD_t fd);
++
++#ifdef __cplusplus
++}
++#endif
++#endif
+diff --git a/plugins/reflink.c b/plugins/reflink.c
+index 513887604..ec575f55e 100644
+--- a/plugins/reflink.c
++++ b/plugins/reflink.c
+@@ -13,6 +13,7 @@
+ #include <rpm/rpmlog.h>
+ #include "lib/rpmlib.h"
+ #include "lib/rpmplugin.h"
++#include "lib/rpmextents_internal.h"
+ #include "lib/rpmte_internal.h"
+ #include <rpm/rpmfileutil.h>
+ #include "rpmio/rpmio_internal.h"
+@@ -40,11 +41,6 @@
+ 
+ #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;
+@@ -96,40 +92,28 @@ static void reflink_cleanup(rpmPlugin plugin) {
+ }
+ 
+ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
++    rpmRC rc;
++    size_t len;
++
+     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"));
++    rc = isTranscodedRpm(state->fd);
++
++    switch(rc){
++	// Fail to parse the file, fail the plugin.
++	case RPMRC_FAIL:
+ 	    return RPMRC_FAIL;
+-	}
+-	return RPMRC_OK;
++	// This is not a transcoded file, do nothing.
++	case RPMRC_NOTFOUND:
++	    return RPMRC_OK;
++	default:
++	    break;
+     }
+     rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));
+     Header h = rpmteHeader(te);
+@@ -140,7 +124,7 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
+     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) {
++    if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(extents_magic_t)), SEEK_END) < 0) {
+ 	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"),
+ 	       state->fd);
+ 	return RPMRC_FAIL;
+diff --git a/rpm2extents.c b/rpm2extents.c
+index e316a2834..a326e3857 100644
+--- a/rpm2extents.c
++++ b/rpm2extents.c
+@@ -15,6 +15,7 @@
+ #include "lib/rpmts.h"
+ #include "lib/signature.h"
+ #include "lib/header_internal.h"
++#include "lib/rpmextents_internal.h"
+ #include "rpmio/rpmio_internal.h"
+ 
+ #include <unistd.h>
+@@ -37,11 +38,6 @@
+ #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;
+@@ -402,7 +398,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+-    uint64_t magic = MAGIC;
++    extents_magic_t magic = EXTENTS_MAGIC;
+     len = sizeof(magic);
+     if (Fwrite(&magic, len, 1, fdo) != len) {
+ 	fprintf(stderr, _("Unable to write magic\n"));
+-- 
+2.35.1
+
diff --git a/SOURCES/0013-plugin-add-plugin_fsm_file_install_func-plugin-hook.patch b/SOURCES/0013-plugin-add-plugin_fsm_file_install_func-plugin-hook.patch
new file mode 100644
index 0000000..56bb554
--- /dev/null
+++ b/SOURCES/0013-plugin-add-plugin_fsm_file_install_func-plugin-hook.patch
@@ -0,0 +1,109 @@
+From 5c97d7f83f56015d6a37934cee4e55ed8d747890 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Tue, 8 Feb 2022 16:57:25 -0800
+Subject: [PATCH 13/30] [plugin] add `plugin_fsm_file_install_func` plugin hook
+
+This hook is to be called when installing individual files from the RPM.
+---
+ lib/rpmplugin.h  |  5 +++++
+ lib/rpmplugins.c | 37 +++++++++++++++++++++++++++++++++++++
+ lib/rpmplugins.h | 15 +++++++++++++++
+ 3 files changed, 57 insertions(+)
+
+diff --git a/lib/rpmplugin.h b/lib/rpmplugin.h
+index fd81aec8d..877db81f3 100644
+--- a/lib/rpmplugin.h
++++ b/lib/rpmplugin.h
+@@ -60,6 +60,10 @@ typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi,
+ 					      const char* path,
+ 					      const char *dest,
+ 					      mode_t file_mode, rpmFsmOp op);
++typedef rpmRC (*plugin_fsm_file_install_func)(rpmPlugin plugin, rpmfi fi,
++					      const char* path,
++					      mode_t file_mode, rpmFsmOp op);
++
+ 
+ typedef struct rpmPluginHooks_s * rpmPluginHooks;
+ struct rpmPluginHooks_s {
+@@ -80,6 +84,7 @@ struct rpmPluginHooks_s {
+     plugin_fsm_file_pre_func		fsm_file_pre;
+     plugin_fsm_file_post_func		fsm_file_post;
+     plugin_fsm_file_prepare_func	fsm_file_prepare;
++    plugin_fsm_file_install_func	fsm_file_install;
+ };
+ 
+ #ifdef __cplusplus
+diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c
+index 3da3097af..850a025a0 100644
+--- a/lib/rpmplugins.c
++++ b/lib/rpmplugins.c
+@@ -421,3 +421,40 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi,
+ 
+     return rc;
+ }
++
++rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,
++				   const char *path, mode_t file_mode,
++				   rpmFsmOp op)
++{
++    plugin_fsm_file_install_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_install);
++	if (hookFunc) {
++	    hook_rc = hookFunc(plugin, fi, path, file_mode, op);
++	    if (hook_rc == RPMRC_FAIL) {
++		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_install 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;
++		}
++	    }
++	}
++    }
++
++    return rc;
++}
++
++
+diff --git a/lib/rpmplugins.h b/lib/rpmplugins.h
+index 39762c376..5365cf698 100644
+--- a/lib/rpmplugins.h
++++ b/lib/rpmplugins.h
+@@ -167,6 +167,21 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi,
+                                    const char *path, const char *dest,
+                                    mode_t mode, rpmFsmOp op);
+ 
++/** \ingroup rpmplugins
++ * Call the fsm file install plugin hook
++ * @param plugins	plugins structure
++ * @param fi		file info iterator (or NULL)
++ * @param path		file object path
++ * @param file_mode	file object mode
++ * @param op		file operation + associated flags
++ * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
++ */
++RPM_GNUC_INTERNAL
++rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,
++				   const char* path, mode_t file_mode,
++				   rpmFsmOp op);
++
++
+ #ifdef __cplusplus
+ }
+ #endif
+-- 
+2.35.1
+
diff --git a/SOURCES/0014-fsm-Call-new-rpmpluginsCallFsmFileInstall-in-rpmPack.patch b/SOURCES/0014-fsm-Call-new-rpmpluginsCallFsmFileInstall-in-rpmPack.patch
new file mode 100644
index 0000000..737368d
--- /dev/null
+++ b/SOURCES/0014-fsm-Call-new-rpmpluginsCallFsmFileInstall-in-rpmPack.patch
@@ -0,0 +1,79 @@
+From ad46eb4132cbd2c4ee23686a1c52f2fc57afffc5 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Tue, 8 Feb 2022 17:06:55 -0800
+Subject: [PATCH 14/30] [fsm] Call new `rpmpluginsCallFsmFileInstall` in
+ `rpmPackageFilesInstall`
+
+Call `rpmpluginsCallFsmFileInstall` for every files to be installed by
+`rpmPackageFilesInstall`, this allows for plugin such as reflink to
+write (reflink) a file and make sure the parent directories have already
+been created.
+If this was done in `rpmpluginsCallFsmFilePre`, the directories may be
+missing, which makes file installation fail.
+---
+ lib/fsm.c | 29 +++++++++--------------------
+ 1 file changed, 9 insertions(+), 20 deletions(-)
+
+diff --git a/lib/fsm.c b/lib/fsm.c
+index feda3750c..711f553d3 100644
+--- a/lib/fsm.c
++++ b/lib/fsm.c
+@@ -53,7 +53,6 @@ struct filedata_s {
+     int stage;
+     int setmeta;
+     int skip;
+-    int plugin_contents;
+     rpmFileAction action;
+     const char *suffix;
+     char *fpath;
+@@ -921,23 +920,12 @@ 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);
+@@ -992,14 +980,15 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+ 	    if (fp->action == FA_TOUCH)
+ 		continue;
+ 
+-            if (S_ISREG(fp->sb.st_mode)) {
++	    rpmRC plugin_rc = rpmpluginsCallFsmFileInstall(plugins, fi, fp->fpath, fp->sb.st_mode, fp->action);
++	    if(!(plugin_rc == RPMRC_PLUGIN_CONTENTS || plugin_rc == RPMRC_OK)){
++		rc = plugin_rc;
++	    } else if(plugin_rc == RPMRC_PLUGIN_CONTENTS){
++		rc = RPMRC_OK;
++	    } else if (S_ISREG(fp->sb.st_mode)) {
+ 		if (rc == RPMERR_ENOENT) {
+-		    if(fp->plugin_contents) {
+-			rc = RPMRC_OK;
+-		    }else {
+-			rc = fsmMkfile(fi, fp, files, psm, nodigest,
+-			    &firstlink, &firstlinkfile);
+-		    }
++		    rc = fsmMkfile(fi, fp, files, psm, nodigest,
++			&firstlink, &firstlinkfile);
+ 		}
+             } else if (S_ISDIR(fp->sb.st_mode)) {
+                 if (rc == RPMERR_ENOENT) {
+-- 
+2.35.1
+
diff --git a/SOURCES/0015-reflink-use-reflink_fsm_file_install-hook-instead-of.patch b/SOURCES/0015-reflink-use-reflink_fsm_file_install-hook-instead-of.patch
new file mode 100644
index 0000000..8183898
--- /dev/null
+++ b/SOURCES/0015-reflink-use-reflink_fsm_file_install-hook-instead-of.patch
@@ -0,0 +1,33 @@
+From b2fc576828af873a1993bdaa2fcb7c860b94df3e Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Tue, 8 Feb 2022 17:10:23 -0800
+Subject: [PATCH 15/30] [reflink] use reflink_fsm_file_install hook instead of
+ reflink_fsm_file_pre
+
+---
+ plugins/reflink.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/plugins/reflink.c b/plugins/reflink.c
+index ec575f55e..7dda06d8e 100644
+--- a/plugins/reflink.c
++++ b/plugins/reflink.c
+@@ -223,7 +223,7 @@ rpm_loff_t find(const unsigned char *digest, reflink_state state) {
+     return offset;
+ }
+ 
+-static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,
++static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* path,
+                                   mode_t file_mode, rpmFsmOp op)
+ {
+     struct file_clone_range fcr;
+@@ -355,5 +355,5 @@ struct rpmPluginHooks_s reflink_hooks = {
+     .cleanup = reflink_cleanup,
+     .psm_pre = reflink_psm_pre,
+     .psm_post = reflink_psm_post,
+-    .fsm_file_pre = reflink_fsm_file_pre,
++    .fsm_file_install = reflink_fsm_file_install,
+ };
+-- 
+2.35.1
+
diff --git a/SOURCES/0016-test-new-runroot_plugins-function-to-run-command-in-.patch b/SOURCES/0016-test-new-runroot_plugins-function-to-run-command-in-.patch
new file mode 100644
index 0000000..1bd2253
--- /dev/null
+++ b/SOURCES/0016-test-new-runroot_plugins-function-to-run-command-in-.patch
@@ -0,0 +1,32 @@
+From e04b5d20a6d8c64dba7416edba8e435145a5d7d3 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Tue, 8 Feb 2022 17:12:09 -0800
+Subject: [PATCH 16/30] [test] new runroot_plugins function to run command in
+ fakeroot without having the plugins disabled
+
+---
+ tests/atlocal.in | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/tests/atlocal.in b/tests/atlocal.in
+index c3189d327..c18637362 100644
+--- a/tests/atlocal.in
++++ b/tests/atlocal.in
+@@ -82,6 +82,14 @@ function runroot()
+     )
+ }
+ 
++function runroot_plugins()
++{
++    (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \
++     MAGIC="/magic/magic" FAKECHROOT_BASE="${RPMTEST}" fakechroot "$@" --define "_buildhost testhost" --define "_topdir /build" --nouserns
++    )
++}
++
++
+ function runroot_other()
+ {
+     (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \
+-- 
+2.35.1
+
diff --git a/SOURCES/0017-test-Add-test-installing-an-RPM-with-reflink-plugin.patch b/SOURCES/0017-test-Add-test-installing-an-RPM-with-reflink-plugin.patch
new file mode 100644
index 0000000..20e572f
--- /dev/null
+++ b/SOURCES/0017-test-Add-test-installing-an-RPM-with-reflink-plugin.patch
@@ -0,0 +1,63 @@
+From e86207d3395e0963f19363b047551100569900df Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Tue, 8 Feb 2022 17:12:55 -0800
+Subject: [PATCH 17/30] [test] Add test installing an RPM with reflink plugin
+
+The test only runs for reflinkable FS, like XFS and BTRFS.
+dbus_announce is being disabled as it generates warning to stderr when
+running within the fakeroot.
+---
+ tests/atlocal.in     | 13 +++++++++++++
+ tests/rpm2extents.at | 15 +++++++++++++++
+ 2 files changed, 28 insertions(+)
+
+diff --git a/tests/atlocal.in b/tests/atlocal.in
+index c18637362..a110564e2 100644
+--- a/tests/atlocal.in
++++ b/tests/atlocal.in
+@@ -50,6 +50,19 @@ else
+ 
+ RPM_XFAIL=${RPM_XFAIL-1}
+ 
++FSTYPE=$(stat -f -c %T /)
++REFLINKABLE_FS=("xfs" "brtfs")
++
++REFLINK_DISABLED=true;
++for item in "${REFLINKABLE_FS[@]}"
++do
++    if test "${FSTYPE}" = "${item}"
++    then
++	REFLINK_DISABLED=false;
++	break
++    fi
++done
++
+ function run()
+ {
+     "$@" --define "_tmppath ${RPMTEST}/tmp" --define "_topdir ${TOPDIR}" --dbpath="${RPMTEST}/var/lib/rpm/"
+diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
+index 18accfc75..44e46a68e 100644
+--- a/tests/rpm2extents.at
++++ b/tests/rpm2extents.at
+@@ -94,3 +94,18 @@ runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?
+ ],
+ [])
+ AT_CLEANUP
++
++AT_SETUP([rpm2extents install package])
++AT_KEYWORDS([rpm2extents install package])
++AT_SKIP_IF([$REFLINK_DISABLED])
++AT_CHECK([
++RPMDB_INIT
++
++runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64.rpm 2> /dev/null
++runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm
++test -f ${RPMTEST}/usr/bin/hello
++],
++[0],
++[],
++[])
++AT_CLEANUP
+-- 
+2.35.1
+
diff --git a/SOURCES/0018-plugin-add-rpmpluginsCallFsmFileArchiveReader.patch b/SOURCES/0018-plugin-add-rpmpluginsCallFsmFileArchiveReader.patch
new file mode 100644
index 0000000..7a5c5fd
--- /dev/null
+++ b/SOURCES/0018-plugin-add-rpmpluginsCallFsmFileArchiveReader.patch
@@ -0,0 +1,97 @@
+From 048db395b6de8544dc88231f0afebee8570daee6 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Tue, 8 Feb 2022 18:21:33 -0800
+Subject: [PATCH 18/30] [plugin] add `rpmpluginsCallFsmFileArchiveReader`
+
+This allows plugins to provide a custom `rpmfi` to
+`rpmPackageFilesInstall` function in fsm.c. It enables supporting
+transcoded files such as with reflink plugin.
+---
+ lib/rpmplugin.h  |  4 ++++
+ lib/rpmplugins.c | 34 ++++++++++++++++++++++++++++++++++
+ lib/rpmplugins.h |  4 +++-
+ 3 files changed, 41 insertions(+), 1 deletion(-)
+
+diff --git a/lib/rpmplugin.h b/lib/rpmplugin.h
+index 877db81f3..6dbbcff35 100644
+--- a/lib/rpmplugin.h
++++ b/lib/rpmplugin.h
+@@ -63,6 +63,9 @@ typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi,
+ typedef rpmRC (*plugin_fsm_file_install_func)(rpmPlugin plugin, rpmfi fi,
+ 					      const char* path,
+ 					      mode_t file_mode, rpmFsmOp op);
++typedef rpmRC (*plugin_fsm_file_archive_reader_func)(rpmPlugin plugin,
++						     FD_t payload,
++						     rpmfiles files, rpmfi *fi);
+ 
+ 
+ typedef struct rpmPluginHooks_s * rpmPluginHooks;
+@@ -85,6 +88,7 @@ struct rpmPluginHooks_s {
+     plugin_fsm_file_post_func		fsm_file_post;
+     plugin_fsm_file_prepare_func	fsm_file_prepare;
+     plugin_fsm_file_install_func	fsm_file_install;
++    plugin_fsm_file_archive_reader_func	fsm_file_archive_reader;
+ };
+ 
+ #ifdef __cplusplus
+diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c
+index 850a025a0..901af1ac5 100644
+--- a/lib/rpmplugins.c
++++ b/lib/rpmplugins.c
+@@ -457,4 +457,38 @@ rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,
+     return rc;
+ }
+ 
++rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload,
++				   rpmfiles files, rpmfi *fi)
++{
++    plugin_fsm_file_archive_reader_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_archive_reader);
++	if (hookFunc) {
++	    hook_rc = hookFunc(plugin, payload, files, fi);
++	    if (hook_rc == RPMRC_FAIL) {
++		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_archive_reader 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;
++		}
++	    }
++	}
++    }
++
++    return rc;
++}
++
+ 
+diff --git a/lib/rpmplugins.h b/lib/rpmplugins.h
+index 5365cf698..88807c53c 100644
+--- a/lib/rpmplugins.h
++++ b/lib/rpmplugins.h
+@@ -181,7 +181,9 @@ rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,
+ 				   const char* path, mode_t file_mode,
+ 				   rpmFsmOp op);
+ 
+-
++RPM_GNUC_INTERNAL
++rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload,
++					 rpmfiles files, rpmfi *fi);
+ #ifdef __cplusplus
+ }
+ #endif
+-- 
+2.35.1
+
diff --git a/SOURCES/0019-reflink-use-rpmpluginsCallFsmFileArchiveReader-to-pr.patch b/SOURCES/0019-reflink-use-rpmpluginsCallFsmFileArchiveReader-to-pr.patch
new file mode 100644
index 0000000..1e98fd1
--- /dev/null
+++ b/SOURCES/0019-reflink-use-rpmpluginsCallFsmFileArchiveReader-to-pr.patch
@@ -0,0 +1,156 @@
+From 5f762af17c6e72e86e4710975dcdfd71fc5d1b07 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Tue, 8 Feb 2022 18:24:12 -0800
+Subject: [PATCH 19/30] [reflink] use `rpmpluginsCallFsmFileArchiveReader` to
+ provide custom rpmfi
+
+As the plugin can now be responsible to provide an Archive Reader to
+`rpmPackageFilesInstall`, we also don't need to mutate the
+`RPMTAG_PAYLOADFORMAT` header in memory, removing some special-casing.
+---
+ lib/depends.c     |  2 --
+ lib/fsm.c         | 38 +++++++++++++++++++-------------------
+ plugins/reflink.c | 18 +++++++++++++-----
+ 3 files changed, 32 insertions(+), 26 deletions(-)
+
+diff --git a/lib/depends.c b/lib/depends.c
+index 8998afcd3..30234df3d 100644
+--- a/lib/depends.c
++++ b/lib/depends.c
+@@ -81,8 +81,6 @@ 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 711f553d3..6972602e0 100644
+--- a/lib/fsm.c
++++ b/lib/fsm.c
+@@ -19,7 +19,6 @@
+ 
+ #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"
+@@ -892,14 +891,6 @@ 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));
+ 
+@@ -933,14 +924,24 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+     if (rc)
+ 	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);
++    rpmRC plugin_rc = rpmpluginsCallFsmFileArchiveReader(plugins, payload, files, &fi);
++    switch(plugin_rc) {
++	case RPMRC_PLUGIN_CONTENTS:
++	    if(fi == NULL) {
++		rc = RPMERR_BAD_MAGIC;
++		goto exit;
++	    }
++	    rc = RPMRC_OK;
++	    break;
++	case RPMRC_OK:
++	    fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
++	    if (fi == NULL) {
++		rc = RPMERR_BAD_MAGIC;
++		goto exit;
++	    }
++	    break;
++	default:
++	    rc = RPMRC_FAIL;
+     }
+ 
+     /* Detect and create directories not explicitly in package. */
+@@ -988,7 +989,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
+ 	    } else if (S_ISREG(fp->sb.st_mode)) {
+ 		if (rc == RPMERR_ENOENT) {
+ 		    rc = fsmMkfile(fi, fp, files, psm, nodigest,
+-			&firstlink, &firstlinkfile);
++				   &firstlink, &firstlinkfile);
+ 		}
+             } else if (S_ISDIR(fp->sb.st_mode)) {
+                 if (rc == RPMERR_ENOENT) {
+@@ -1096,7 +1097,6 @@ 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/plugins/reflink.c b/plugins/reflink.c
+index 7dda06d8e..d5e6db27a 100644
+--- a/plugins/reflink.c
++++ b/plugins/reflink.c
+@@ -54,6 +54,7 @@ struct reflink_state_s {
+     FD_t fd;
+     rpmfiles files;
+     inodeIndexHash inodeIndexes;
++    int transcoded;
+ };
+ 
+ typedef struct reflink_state_s * reflink_state;
+@@ -116,12 +117,8 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
+ 	    break;
+     }
+     rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));
+-    Header h = rpmteHeader(te);
++    state->transcoded = 1;
+ 
+-    /* 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(extents_magic_t)), SEEK_END) < 0) {
+@@ -350,10 +347,21 @@ static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* pa
+     return RPMRC_OK;
+ }
+ 
++static rpmRC reflink_fsm_file_archive_reader(rpmPlugin plugin, FD_t payload,
++					     rpmfiles files, rpmfi *fi) {
++    reflink_state state = rpmPluginGetData(plugin);
++    if(state->transcoded) {
++	*fi = rpmfilesIter(files, RPMFI_ITER_FWD);
++	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_install = reflink_fsm_file_install,
++    .fsm_file_archive_reader = reflink_fsm_file_archive_reader,
+ };
+-- 
+2.35.1
+
diff --git a/SOURCES/0020-reflink-tests-Can-install-standard-RPM-with-reflink.patch b/SOURCES/0020-reflink-tests-Can-install-standard-RPM-with-reflink.patch
new file mode 100644
index 0000000..24f0711
--- /dev/null
+++ b/SOURCES/0020-reflink-tests-Can-install-standard-RPM-with-reflink.patch
@@ -0,0 +1,35 @@
+From 13aea986ada3ed7d26182d81d8878bcc807a6ab5 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Thu, 10 Feb 2022 08:49:17 -0800
+Subject: [PATCH 20/30] [reflink][tests] Can install standard RPM with reflink
+
+Add a test to validate that if a file is a non-transcoded RPM, the reflink plugin correctly ignores the file and let core RPM do the job.
+---
+ tests/rpm2extents.at | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
+index 44e46a68e..648304287 100644
+--- a/tests/rpm2extents.at
++++ b/tests/rpm2extents.at
+@@ -109,3 +109,17 @@ test -f ${RPMTEST}/usr/bin/hello
+ [],
+ [])
+ AT_CLEANUP
++
++AT_SETUP([reflink ignores non-transcoded package])
++AT_KEYWORDS([reflink])
++AT_CHECK([
++RPMDB_INIT
++
++runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /data/RPMS/hello-2.0-1.x86_64.rpm && exit $?
++# Check that the file is properly installed in chroot
++test -f ${RPMTEST}/usr/bin/hello
++],
++[0],
++[],
++[])
++AT_CLEANUP
+-- 
+2.35.1
+
diff --git a/SOURCES/0021-tests-Fix-tests-AT_KEYWORDS-usage.patch b/SOURCES/0021-tests-Fix-tests-AT_KEYWORDS-usage.patch
new file mode 100644
index 0000000..250aa6b
--- /dev/null
+++ b/SOURCES/0021-tests-Fix-tests-AT_KEYWORDS-usage.patch
@@ -0,0 +1,43 @@
+From 3e363f853a4379e0199db81f777f4b610e158eae Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Thu, 10 Feb 2022 08:49:58 -0800
+Subject: [PATCH 21/30] [tests] Fix tests AT_KEYWORDS usage
+
+---
+ tests/rpm2extents.at | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
+index 648304287..fa124ac03 100644
+--- a/tests/rpm2extents.at
++++ b/tests/rpm2extents.at
+@@ -32,7 +32,7 @@ AT_CLEANUP
+ # Check that transcoder writes checksig return code and content.
+ #
+ AT_SETUP([rpm2extents signature])
+-AT_KEYWORDS([rpm2extents digest signature])
++AT_KEYWORDS([rpm2extents])
+ AT_CHECK([
+ RPMDB_INIT
+ 
+@@ -64,7 +64,7 @@ RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
+ AT_CLEANUP
+ 
+ AT_SETUP([rpm2extents signature verification])
+-AT_KEYWORDS([rpm2extents digest signature])
++AT_KEYWORDS([rpm2extents])
+ AT_CHECK([
+ RPMDB_INIT
+ 
+@@ -96,7 +96,7 @@ runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?
+ AT_CLEANUP
+ 
+ AT_SETUP([rpm2extents install package])
+-AT_KEYWORDS([rpm2extents install package])
++AT_KEYWORDS([rpm2extents reflink])
+ AT_SKIP_IF([$REFLINK_DISABLED])
+ AT_CHECK([
+ RPMDB_INIT
+-- 
+2.35.1
+
diff --git a/SOURCES/0022-reflink-fix-support-for-hardlinks.patch b/SOURCES/0022-reflink-fix-support-for-hardlinks.patch
new file mode 100644
index 0000000..e4d6558
--- /dev/null
+++ b/SOURCES/0022-reflink-fix-support-for-hardlinks.patch
@@ -0,0 +1,132 @@
+From fd8ffffced543e248847d63e9375fb95584f998d Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Fri, 11 Feb 2022 18:09:47 -0800
+Subject: [PATCH 22/30] [reflink] fix support for hardlinks
+
+a `suffix` made of ";${tid}" is now used until the changes are commited. As such, when
+linking the file, we should link to the suffixed file.
+
+The `suffix` is currently internal only to rpmPackageFilesInstall and there is
+no obvious way to use data from struct `filedata_s` without exposing it to
+a bunch of other places such as rpmplugin*.
+We already keep track of the first file index. We used to use this to fetch the
+file name, but due to suffix, the file is not named correctly, e.g it
+has the name we will want at the end, once the transaction is commited,
+not the temporary name.
+Instead of re-implementing a local suffix that may change over time as
+the implementation evolves, we can store the name of the file we should link to
+in the hash instead of the index, which we were then using to fetch the
+file name.
+When hardlinking, we can then retrieve the name of the target file
+instead of the index, like we previously did, and hardlink to that
+without any issues.
+Add a test to validate that reflink can handle hardlinking.
+
+Originally, the test added would fail with:
+
+```
+error: reflink: Unable to hard link /foo/hello -> /foo/hello-bar;6207219c due to No such file or directory
+error: Plugin reflink: hook fsm_file_install failed
+error: unpacking of archive failed on file /foo/hello-bar;6207219c:
+cpio: (error 0x2)
+error: hlinktest-1.0-1.noarch: install failed
+./rpm2extents.at:114: exit code was 1, expected 0
+499. rpm2extents.at:112: 499. reflink hardlink package
+(rpm2extents.at:112): FAILED (rpm2extents.at:114)
+```
+
+After this change, the test passes.
+---
+ plugins/reflink.c    | 19 ++++++++-----------
+ tests/rpm2extents.at | 15 +++++++++++++++
+ 2 files changed, 23 insertions(+), 11 deletions(-)
+
+diff --git a/plugins/reflink.c b/plugins/reflink.c
+index d5e6db27a..4fc1d74d1 100644
+--- a/plugins/reflink.c
++++ b/plugins/reflink.c
+@@ -29,7 +29,7 @@
+ #undef HTDATATYPE
+ #define HASHTYPE inodeIndexHash
+ #define HTKEYTYPE rpm_ino_t
+-#define HTDATATYPE int
++#define HTDATATYPE const char *
+ #include "lib/rpmhash.H"
+ #include "lib/rpmhash.C"
+ 
+@@ -163,7 +163,7 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
+ 	    return RPMRC_FAIL;
+ 	}
+ 	state->inodeIndexes = inodeIndexHashCreate(
+-	    state->keys, inodeId, inodeCmp, NULL, NULL
++	    state->keys, inodeId, inodeCmp, NULL, (inodeIndexHashFreeData)rfree
+ 	);
+     }
+ 
+@@ -226,7 +226,7 @@ static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* pa
+     struct file_clone_range fcr;
+     rpm_loff_t size;
+     int dst, rc;
+-    int *hlix;
++    const char **hl_target = NULL;
+ 
+     reflink_state state = rpmPluginGetData(plugin);
+     if (state->table == NULL) {
+@@ -243,18 +243,15 @@ static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* pa
+ 	/* 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)) {
++	if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hl_target,
++				   NULL, NULL)) {
+ 	    /* entry is in table, use hard link */
+-	    char *fn = rpmfilesFN(state->files, hlix[0]);
+-	    if (link(fn, path) != 0) {
++	    if (link(hl_target[0], path) != 0) {
+ 		rpmlog(RPMLOG_ERR,
+ 		       _("reflink: Unable to hard link %s -> %s due to %s\n"),
+-		       fn, path, strerror(errno));
+-		free(fn);
++		       hl_target[0], path, strerror(errno));
+ 		return RPMRC_FAIL;
+ 	    }
+-	    free(fn);
+ 	    return RPMRC_PLUGIN_CONTENTS;
+ 	}
+ 	/* if we didn't hard link, then we'll track this inode as being
+@@ -262,7 +259,7 @@ static rpmRC reflink_fsm_file_install(rpmPlugin plugin, rpmfi fi, const char* pa
+ 	 */
+ 	if (rpmfiFNlink(fi) > 1) {
+ 	    /* minor optimization: only store files with more than one link */
+-	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi));
++	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rstrdup(path));
+ 	}
+ 	/* derived from wfd_open in fsm.c */
+ 	mode_t old_umask = umask(0577);
+diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
+index fa124ac03..5c66de7f6 100644
+--- a/tests/rpm2extents.at
++++ b/tests/rpm2extents.at
+@@ -123,3 +123,18 @@ test -f ${RPMTEST}/usr/bin/hello
+ [],
+ [])
+ AT_CLEANUP
++
++AT_SETUP([reflink hardlink package])
++AT_KEYWORDS([reflink hardlink])
++AT_SKIP_IF([$REFLINK_DISABLED])
++AT_CHECK([
++RPMDB_INIT
++
++PKG=hlinktest-1.0-1.noarch.rpm
++runroot_other cat /data/RPMS/${PKG} | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/${PKG} 2> /dev/null
++runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/${PKG}
++],
++[0],
++[],
++[])
++AT_CLEANUP
+-- 
+2.35.1
+
diff --git a/SOURCES/0023-rpm2extents-Improve-logging.patch b/SOURCES/0023-rpm2extents-Improve-logging.patch
new file mode 100644
index 0000000..8ac52f7
--- /dev/null
+++ b/SOURCES/0023-rpm2extents-Improve-logging.patch
@@ -0,0 +1,393 @@
+From a2c959085250d186c54130b78af076095c3d3cd3 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Tue, 15 Feb 2022 09:11:33 -0800
+Subject: [PATCH 23/30] [rpm2extents] Improve logging
+
+```
+$ ls -l tests/data/RPMS/hello-2.0-1.x86_64.rpm
+-rw-r--r--. 1 chantra chantra 9915 Jan 19 09:10 tests/data/RPMS/hello-2.0-1.x86_64.rpm
+$ cp tests/data/RPMS/hello-2.0-1.x86_64.rpm ~/trunc-hello-2.0-1.x86_64.rpm
+$ truncate -s 9000 ~/trunc-hello-2.0-1.x86_64.rpm
+$ ls -l ~/trunc-hello-2.0-1.x86_64.rpm
+-rw-r--r--. 1 chantra chantra 9000 Feb 15 09:13 /home/chantra/trunc-hello-2.0-1.x86_64.rpm
+$ cat ~/trunc-hello-2.0-1.x86_64.rpm| ./rpm2extents SHA256 > /dev/null
+error: rpmfiArchiveReadToFile failed while extracting "/usr/bin/hello" with RC -32784: cpio: read failed - Illegal seek
+error: Package processor failed: -32784
+error: Unable to write input length 9000: 32, Broken pipe
+error: Failed to write digests: 32, Broken pipe
+error: Validator failed with RC 2
+```
+Before:
+```
+$ cat ~/trunc-hello-2.0-1.x86_64.rpm| ./rpm2extents SHA256 > /dev/null
+rpmfiArchiveReadToFile failed with -32784
+Validator failed
+Unable to write input length 9000
+Failed to write digestsValidator failed
+```
+---
+ rpm2extents.c | 120 ++++++++++++++++++++++++++++++--------------------
+ 1 file changed, 72 insertions(+), 48 deletions(-)
+
+diff --git a/rpm2extents.c b/rpm2extents.c
+index a326e3857..e1a19fedb 100644
+--- a/rpm2extents.c
++++ b/rpm2extents.c
+@@ -84,13 +84,15 @@ static int FDWriteDigests(
+ 
+     len = sizeof(fdilength);
+     if (Fwrite(&fdilength, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write input length %zd\n"), fdilength);
++	rpmlog(RPMLOG_ERR, _("Unable to write input length %zd: %d, %s\n"),
++	       fdilength, errno, strerror(errno));
+ 	goto exit;
+     }
+     len = sizeof(algos_len);
+     if (Fwrite(&algos_len, len, 1, fdo) != len) {
+ 	algo_digest_len = (uint32_t)filedigest_len;
+-	fprintf(stderr, _("Unable to write number of digests\n"));
++	rpmlog(RPMLOG_ERR, _("Unable to write number of digests: %d, %s\n"),
++	       errno, strerror(errno));
+ 	goto exit;
+     }
+     for (algo = 0; algo < algos_len; algo++) {
+@@ -102,24 +104,28 @@ static int FDWriteDigests(
+ 
+ 	len = sizeof(algo_name_len);
+ 	if (Fwrite(&algo_name_len, len, 1, fdo) != len) {
+-	    fprintf(stderr,
+-		    _("Unable to write digest algo name length\n"));
++	    rpmlog(RPMLOG_ERR,
++		   _("Unable to write digest algo name length: %d, %s\n"),
++		   errno, strerror(errno));
+ 	    goto exit;
+ 	}
+ 	len = sizeof(algo_digest_len);
+ 	if (Fwrite(&algo_digest_len, len, 1, fdo) != len) {
+-	    fprintf(stderr,
+-		    _("Unable to write number of bytes for digest\n"));
++	    rpmlog(RPMLOG_ERR,
++		   _("Unable to write number of bytes for digest: %d, %s\n"),
++		   errno, strerror(errno));
+ 	     goto exit;
+ 	}
+ 	if (Fwrite(algo_name, algo_name_len, 1, fdo) != algo_name_len) {
+-	    fprintf(stderr, _("Unable to write digest algo name\n"));
++	    rpmlog(RPMLOG_ERR, _("Unable to write digest algo name: %d, %s\n"),
++		   errno, strerror(errno));
+ 	    goto exit;
+ 	}
+ 	if (Fwrite(filedigest, algo_digest_len, 1, fdo ) != algo_digest_len) {
+-	    fprintf(stderr,
+-		    _("Unable to write digest value %u, %zu\n"),
+-		    algo_digest_len, filedigest_len);
++	    rpmlog(RPMLOG_ERR,
++		   _("Unable to write digest value %u, %zu: %d, %s\n"),
++		   algo_digest_len, filedigest_len,
++		   errno, strerror(errno));
+ 	    goto exit;
+ 	}
+     }
+@@ -133,22 +139,29 @@ static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) {
+     rpmRC rc = RPMRC_FAIL;
+ 
+     if(rpmvsrc){
+-	fprintf(stderr, _("Error verifying package signatures\n"));
++	rpmlog(RPMLOG_WARNING,
++	       _("Error verifying package signatures:\n%s\n"), msg);
+     }
+ 
+     len = sizeof(rpmvsrc);
+     if (Fwrite(&rpmvsrc, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write signature verification RC code %d\n"), rpmvsrc);
++	rpmlog(RPMLOG_ERR,
++	       _("Unable to write signature verification RC code %d: %d, %s\n"),
++	       rpmvsrc, errno, strerror(errno));
+ 	goto exit;
+     }
+     size_t content_len = msg ? strlen(msg) : 0;
+     len = sizeof(content_len);
+     if (Fwrite(&content_len, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write signature verification output length %zd\n"), content_len);
++	rpmlog(RPMLOG_ERR,
++	       _("Unable to write signature verification output length %zd: %d, %s\n"),
++	       content_len, errno, strerror(errno));
+ 	goto exit;
+     }
+     if (Fwrite(msg, content_len, 1, fdo) != content_len) {
+-	fprintf(stderr, _("Unable to write signature verification output %s\n"), msg);
++	rpmlog(RPMLOG_ERR,
++	       _("Unable to write signature verification output %s: %d, %s\n"),
++	       msg, errno, strerror(errno));
+ 	goto exit;
+     }
+ 
+@@ -174,12 +187,16 @@ static rpmRC validator(FD_t fdi, FD_t digesto, FD_t sigo,
+ 
+     // Write result of digest computation
+     if(FDWriteDigests(fdi, digesto, algos, algos_len) != RPMRC_OK) {
+-	fprintf(stderr, _("Failed to write digests"));
++	rpmlog(RPMLOG_ERR, _("Failed to write digests: %d, %s\n"),
++	       errno, strerror(errno));
+ 	goto exit;
+     }
+ 
+     // Write result of signature validation.
+     if(FDWriteSignaturesValidation(sigo, rpmvsrc, msg)) {
++	rpmlog(RPMLOG_ERR,
++	       _("Failed to write signature verification result: %d, %s\n"),
++	       errno, strerror(errno));
+ 	goto exit;
+     }
+     rc = RPMRC_OK;
+@@ -226,24 +243,24 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+     fdo = fdDup(STDOUT_FILENO);
+ 
+     if (rpmReadPackageRaw(fdi, &sigh, &h)) {
+-	fprintf(stderr, _("Error reading package\n"));
++	rpmlog(RPMLOG_ERR, _("Error reading package\n"));
+ 	exit(EXIT_FAILURE);
+     }
+ 
+     if (rpmLeadWrite(fdo, h))
+     {
+-	fprintf(stderr, _("Unable to write package lead: %s\n"),
++	rpmlog(RPMLOG_ERR, _("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));
++	rpmlog(RPMLOG_ERR, _("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));
++	rpmlog(RPMLOG_ERR, _("Unable to write headers: %s\n"), Fstrerror(fdo));
+ 	exit(EXIT_FAILURE);
+     }
+ 
+@@ -257,7 +274,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+     free(rpmio_flags);
+ 
+     if (gzdi == NULL) {
+-	fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));
++	rpmlog(RPMLOG_ERR, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));
+ 	exit(EXIT_FAILURE);
+     }
+ 
+@@ -300,7 +317,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ 	}
+ 	pad = pad_to(pos, fundamental_block_size);
+ 	if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) {
+-	    fprintf(stderr, _("Unable to write padding\n"));
++	    rpmlog(RPMLOG_ERR, _("Unable to write padding\n"));
+ 	    rc = RPMRC_FAIL;
+ 	    goto exit;
+ 	}
+@@ -313,7 +330,12 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ 	size = rpmfiFSize(fi);
+ 	rc = rpmfiArchiveReadToFile(fi, fdo, 0);
+ 	if (rc != RPMRC_OK) {
+-	    fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc);
++	    char *errstr = rpmfileStrerror(rc);
++	    rpmlog(RPMLOG_ERR,
++		   _("rpmfiArchiveReadToFile failed while extracting "\
++		   "\"%s\" with RC %d: %s\n"),
++		   rpmfiFN(fi), rc, errstr);
++	    free(errstr);
+ 	    goto exit;
+ 	}
+ 	pos += size;
+@@ -326,7 +348,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+     validation_pos = pos;
+     ssize_t validation_len = ufdCopy(validationi, fdo);
+     if (validation_len == -1) {
+-	fprintf(stderr, _("validation output ufdCopy failed\n"));
++	rpmlog(RPMLOG_ERR, _("validation output ufdCopy failed\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+@@ -335,25 +357,25 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ 
+     len = sizeof(offset_ix);
+     if (Fwrite(&offset_ix, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write length of table\n"));
++	rpmlog(RPMLOG_ERR, _("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"));
++	rpmlog(RPMLOG_ERR, _("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"));
++	    rpmlog(RPMLOG_ERR, _("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"));
++	    rpmlog(RPMLOG_ERR, _("Unable to write offset\n"));
+ 	    rc = RPMRC_FAIL;
+ 	    goto exit;
+ 	}
+@@ -365,7 +387,7 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ 
+     ssize_t digest_len = ufdCopy(digestori, fdo);
+     if (digest_len == -1) {
+-	fprintf(stderr, _("digest table ufdCopy failed\n"));
++	rpmlog(RPMLOG_ERR, _("digest table ufdCopy failed\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+@@ -378,30 +400,30 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+     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"));
++	rpmlog(RPMLOG_ERR, _("Unable to write final padding\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+     zeros = _free(zeros);
+     if (Fwrite(&validation_pos, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write offset of validation output\n"));
++	rpmlog(RPMLOG_ERR, _("Unable to write offset of validation output\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+     if (Fwrite(&digest_table_pos, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write offset of digest table\n"));
++	rpmlog(RPMLOG_ERR, _("Unable to write offset of digest table\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+     if (Fwrite(&digest_pos, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write offset of validation table\n"));
++	rpmlog(RPMLOG_ERR, _("Unable to write offset of validation table\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+     extents_magic_t magic = EXTENTS_MAGIC;
+     len = sizeof(magic);
+     if (Fwrite(&magic, len, 1, fdo) != len) {
+-	fprintf(stderr, _("Unable to write magic\n"));
++	rpmlog(RPMLOG_ERR, _("Unable to write magic\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+@@ -426,7 +448,9 @@ static off_t ufdTee(FD_t sfd, FD_t *fds, int len)
+ 	    for(int i=0; i < len; i++) {
+ 		wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, fds[i]);
+ 		if (wrbytes != rdbytes) {
+-		    fprintf(stderr, "Error wriing to FD %d: %s\n", i, Fstrerror(fds[i]));
++		    rpmlog(RPMLOG_ERR,
++			   _("Error wriing to FD %d: %s\n"),
++			   i, Fstrerror(fds[i]));
+ 		    total = -1;
+ 		    break;
+ 		}
+@@ -460,22 +484,22 @@ static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) {
+     FD_t fds[2];
+ 
+      if (pipe(processorpipefd) == -1) {
+-	fprintf(stderr, _("Processor pipe failure\n"));
++	rpmlog(RPMLOG_ERR, _("Processor pipe failure\n"));
+ 	return RPMRC_FAIL;
+     }
+ 
+     if (pipe(validatorpipefd) == -1) {
+-	fprintf(stderr, _("Validator pipe failure\n"));
++	rpmlog(RPMLOG_ERR, _("Validator pipe failure\n"));
+ 	return RPMRC_FAIL;
+     }
+ 
+     if (pipe(meta_digestpipefd) == -1) {
+-	fprintf(stderr, _("Meta digest pipe failure\n"));
++	rpmlog(RPMLOG_ERR, _("Meta digest pipe failure\n"));
+ 	return RPMRC_FAIL;
+     }
+ 
+     if (pipe(meta_rpmsignpipefd) == -1) {
+-	fprintf(stderr, _("Meta rpm signature pipe failure\n"));
++	rpmlog(RPMLOG_ERR, _("Meta rpm signature pipe failure\n"));
+ 	return RPMRC_FAIL;
+     }
+ 
+@@ -494,7 +518,7 @@ static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) {
+ 	close(meta_rpmsignpipefd[1]);
+ 	rc = validator(fdi, digesto, sigo, algos, algos_len);
+ 	if(rc != RPMRC_OK) {
+-	    fprintf(stderr, _("Validator failed\n"));
++	    rpmlog(RPMLOG_ERR, _("Validator failed with RC %d\n"), rc);
+ 	}
+ 	Fclose(fdi);
+ 	Fclose(digesto);
+@@ -522,7 +546,7 @@ static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) {
+ 
+ 	    rc = process_package(fdi, digestori, sigi);
+ 	    if(rc != RPMRC_OK) {
+-		fprintf(stderr, _("Validator failed\n"));
++		rpmlog(RPMLOG_ERR, _("Package processor failed: %d\n"), rc);
+ 	    }
+ 	    Fclose(digestori);
+ 	    Fclose(sigi);
+@@ -552,19 +576,19 @@ static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) {
+ 	    rc = RPMRC_OK;
+ 	    offt = ufdTee(fdi, fds, 2);
+ 	    if(offt == -1){
+-		fprintf(stderr, _("Failed to tee RPM\n"));
++		rpmlog(RPMLOG_ERR, _("Failed to tee RPM\n"));
+ 		rc = RPMRC_FAIL;
+ 	    }
+ 	    Fclose(fds[0]);
+ 	    Fclose(fds[1]);
+ 	    w = waitpid(cpids[0], &wstatus, 0);
+ 	    if (w == -1) {
+-		fprintf(stderr, _("waitpid cpids[0] failed\n"));
++		rpmlog(RPMLOG_ERR, _("waitpid cpids[0] failed\n"));
+ 		rc = RPMRC_FAIL;
+ 	    }
+ 	    w = waitpid(cpids[1], &wstatus, 0);
+ 	    if (w == -1) {
+-		fprintf(stderr, _("waitpid cpids[1] failed\n"));
++		rpmlog(RPMLOG_ERR, _("waitpid cpids[1] failed\n"));
+ 		rc = RPMRC_FAIL;
+ 	    }
+ 	}
+@@ -585,8 +609,8 @@ int main(int argc, char *argv[]) {
+     poptSetOtherOptionHelp(optCon, "[OPTIONS]* <DIGESTALGO>");
+ 
+     if (poptPeekArg(optCon) == NULL) {
+-	fprintf(stderr,
+-		_("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));
++	rpmlog(RPMLOG_ERR,
++	       _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));
+ 	poptPrintUsage(optCon, stderr, 0);
+ 	exit(EXIT_FAILURE);
+     }
+@@ -598,9 +622,9 @@ int main(int argc, char *argv[]) {
+     for (int x = 0; x < nb_algos; x++) {
+ 	if (pgpStringVal(PGPVAL_HASHALGO, args[x], &algos[x]) != 0)
+ 	{
+-	    fprintf(stderr,
+-		    _("Unable to resolve '%s' as a digest algorithm, exiting\n"),
+-		    args[x]);
++	    rpmlog(RPMLOG_ERR,
++		   _("Unable to resolve '%s' as a digest algorithm, exiting\n"),
++		   args[x]);
+ 	    exit(EXIT_FAILURE);
+ 	}
+     }
+-- 
+2.35.1
+
diff --git a/SOURCES/0024-rpm2extents-create-footer-struct-and-helpers.patch b/SOURCES/0024-rpm2extents-create-footer-struct-and-helpers.patch
new file mode 100644
index 0000000..baabce0
--- /dev/null
+++ b/SOURCES/0024-rpm2extents-create-footer-struct-and-helpers.patch
@@ -0,0 +1,200 @@
+From aabaa6c6587c37b84a1b9cfd98bff31f1b69345e Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Wed, 16 Feb 2022 17:00:09 -0800
+Subject: [PATCH 24/30] [rpm2extents] create footer struct and helpers
+
+new extents_footer and extents_footer_offset struct with a function to
+load offsets from an FD.
+Change existing code to use new struct/functions
+---
+ lib/rpmchecksig.c         | 16 +++-------------
+ lib/rpmextents.c          | 26 +++++++++++++++++---------
+ lib/rpmextents_internal.h | 31 ++++++++++++++++++++++++++++++-
+ rpm2extents.c             | 23 ++++-------------------
+ 4 files changed, 54 insertions(+), 42 deletions(-)
+
+diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c
+index dc1726a18..729f79f9f 100644
+--- a/lib/rpmchecksig.c
++++ b/lib/rpmchecksig.c
+@@ -223,29 +223,19 @@ exit:
+ 
+ static int rpmpkgVerifySigsTranscoded(FD_t fd){
+     rpm_loff_t current;
+-    uint64_t magic;
+-    rpm_loff_t offset;
+     int32_t rc;
+     size_t len;
+     uint64_t content_len;
+     char *content = NULL;
++    struct extents_footer_t footer;
+ 
+     current = Ftell(fd);
+ 
+-    if(Fseek(fd, -(sizeof(magic) + 3 * sizeof(offset) ), SEEK_END) < 0) {
+-	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: failed to seek for offset\n"));
++    if(extentsFooterFromFD(fd, &footer) != RPMRC_OK) {
+ 	rc = -1;
+ 	goto exit;
+     }
+-
+-    len = sizeof(offset);
+-    if (Fread(&offset, len, 1, fd) != len) {
+-	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read Signature Verification offset\n"));
+-	rc = -1;
+-	goto exit;
+-    }
+-
+-    if(Fseek(fd,  offset, SEEK_SET) < 0) {
++    if(Fseek(fd, footer.offsets.checksig_offset, SEEK_SET) < 0) {
+ 	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to seek signature verification offset\n"));
+ 	rc = -1;
+ 	goto exit;
+diff --git a/lib/rpmextents.c b/lib/rpmextents.c
+index 015277751..46b7aadff 100644
+--- a/lib/rpmextents.c
++++ b/lib/rpmextents.c
+@@ -3,13 +3,16 @@
+ 
+ #include <rpm/rpmlog.h>
+ #include <rpm/rpmio.h>
++#include <string.h>
++#include <errno.h>
++
+ 
+ #include "lib/rpmextents_internal.h"
+ 
+-rpmRC isTranscodedRpm(FD_t fd) {
++rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer) {
++
+     rpmRC rc = RPMRC_NOTFOUND;
+     rpm_loff_t current;
+-    extents_magic_t magic;
+     size_t len;
+ 
+     // If the file is not seekable, we cannot detect whether or not it is transcoded.
+@@ -18,19 +21,19 @@ rpmRC isTranscodedRpm(FD_t fd) {
+     }
+     current = Ftell(fd);
+ 
+-    if(Fseek(fd, -(sizeof(magic)), SEEK_END) < 0) {
+-	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for magic\n"));
++    len = sizeof(struct extents_footer_t);
++    if(Fseek(fd, -len, SEEK_END) < 0) {
++	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for footer: %s\n"), strerror(errno));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+-    len = sizeof(magic);
+-    if (Fread(&magic, len, 1, fd) != len) {
+-	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read magic\n"));
++    if (Fread(footer, len, 1, fd) != len) {
++	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read footer\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+-    if (magic != EXTENTS_MAGIC) {
+-	rpmlog(RPMLOG_DEBUG, _("isTranscodedRpm: not transcoded\n"));
++    if (footer->magic != EXTENTS_MAGIC) {
++	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: not transcoded\n"));
+ 	rc = RPMRC_NOTFOUND;
+ 	goto exit;
+     }
+@@ -43,4 +46,9 @@ exit:
+     return rc;
+ }
+ 
++rpmRC isTranscodedRpm(FD_t fd) {
++    struct extents_footer_t footer;
++    return extentsFooterFromFD(fd, &footer);
++}
++
+ 
+diff --git a/lib/rpmextents_internal.h b/lib/rpmextents_internal.h
+index 57cecfc31..f0c29c807 100644
+--- a/lib/rpmextents_internal.h
++++ b/lib/rpmextents_internal.h
+@@ -7,6 +7,10 @@ extern "C" {
+ 
+ #include <stdint.h>
+ 
++/** \ingroup rpmextents
++ * RPM extents library
++ */
++
+ /* magic value at end of file (64 bits) that indicates this is a transcoded
+  * rpm.
+  */
+@@ -14,9 +18,34 @@ extern "C" {
+ 
+ typedef uint64_t extents_magic_t;
+ 
++struct __attribute__ ((__packed__)) extents_footer_offsets_t {
++    off64_t checksig_offset;
++    off64_t table_offset;
++    off64_t csum_offset;
++};
++
++struct __attribute__ ((__packed__)) extents_footer_t {
++    struct extents_footer_offsets_t offsets;
++    extents_magic_t magic;
++};
++
++
++/** \ingroup rpmextents
++ * Read the RPM Extents footer from a file descriptor.
++ * @param fd		The FD_t of the transcoded RPM
++ * @param[out] footer	A pointer to an allocated extents_footer_t with a copy of the footer.
++ * @return		RPMRC_OK on success, RPMRC_NOTFOUND if not a transcoded file, RPMRC_FAIL on any failure.
++ */
++rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer);
++
++/** \ingroup rpmextents
++ * Check if a RPM is a transcoded RPM
++ * @param fd	The FD_t of the transcoded RPM
++ * return	RPMRC_OK on success, RPMRC_NOTFOUND if not a transcoded file, RPMRC_FAIL on any failure.
++ */
+ rpmRC isTranscodedRpm(FD_t fd);
+ 
+ #ifdef __cplusplus
+ }
+ #endif
+-#endif
++#endif /* _RPMEXTENTS_INTERNAL_H */
+diff --git a/rpm2extents.c b/rpm2extents.c
+index e1a19fedb..7dd5128de 100644
+--- a/rpm2extents.c
++++ b/rpm2extents.c
+@@ -405,25 +405,10 @@ static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
+ 	goto exit;
+     }
+     zeros = _free(zeros);
+-    if (Fwrite(&validation_pos, len, 1, fdo) != len) {
+-	rpmlog(RPMLOG_ERR, _("Unable to write offset of validation output\n"));
+-	rc = RPMRC_FAIL;
+-	goto exit;
+-    }
+-    if (Fwrite(&digest_table_pos, len, 1, fdo) != len) {
+-	rpmlog(RPMLOG_ERR, _("Unable to write offset of digest table\n"));
+-	rc = RPMRC_FAIL;
+-	goto exit;
+-    }
+-    if (Fwrite(&digest_pos, len, 1, fdo) != len) {
+-	rpmlog(RPMLOG_ERR, _("Unable to write offset of validation table\n"));
+-	rc = RPMRC_FAIL;
+-	goto exit;
+-    }
+-    extents_magic_t magic = EXTENTS_MAGIC;
+-    len = sizeof(magic);
+-    if (Fwrite(&magic, len, 1, fdo) != len) {
+-	rpmlog(RPMLOG_ERR, _("Unable to write magic\n"));
++    struct extents_footer_t footer = {.offsets = {validation_pos, digest_table_pos, digest_pos}, .magic = EXTENTS_MAGIC};
++    len = sizeof(footer);
++    if (Fwrite(&footer, len, 1, fdo) != len) {
++	rpmlog(RPMLOG_ERR, _("Unable to write footer\n"));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+-- 
+2.35.1
+
diff --git a/SOURCES/0025-extents-move-more-functions-helpers-behind-rpmextent.patch b/SOURCES/0025-extents-move-more-functions-helpers-behind-rpmextent.patch
new file mode 100644
index 0000000..71a54e6
--- /dev/null
+++ b/SOURCES/0025-extents-move-more-functions-helpers-behind-rpmextent.patch
@@ -0,0 +1,176 @@
+From 8235711d92d8783abe63d6e4f29afd495fc4b22e Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Wed, 16 Feb 2022 23:21:14 -0800
+Subject: [PATCH 25/30] [extents] move more functions/helpers behind
+ rpmextents_internal.h
+
+---
+ lib/rpmchecksig.c         | 58 ++-------------------------------------
+ lib/rpmextents.c          | 56 +++++++++++++++++++++++++++++++++++++
+ lib/rpmextents_internal.h |  6 ++++
+ 3 files changed, 64 insertions(+), 56 deletions(-)
+
+diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c
+index 729f79f9f..5e8794e2d 100644
+--- a/lib/rpmchecksig.c
++++ b/lib/rpmchecksig.c
+@@ -221,61 +221,6 @@ exit:
+     return rc;
+ }
+ 
+-static int rpmpkgVerifySigsTranscoded(FD_t fd){
+-    rpm_loff_t current;
+-    int32_t rc;
+-    size_t len;
+-    uint64_t content_len;
+-    char *content = NULL;
+-    struct extents_footer_t footer;
+-
+-    current = Ftell(fd);
+-
+-    if(extentsFooterFromFD(fd, &footer) != RPMRC_OK) {
+-	rc = -1;
+-	goto exit;
+-    }
+-    if(Fseek(fd, footer.offsets.checksig_offset, SEEK_SET) < 0) {
+-	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to seek signature verification offset\n"));
+-	rc = -1;
+-	goto exit;
+-    }
+-    len = sizeof(rc);
+-    if (Fread(&rc, len, 1, fd) != len) {
+-	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read Signature Verification RC\n"));
+-	rc = -1;
+-	goto exit;
+-    }
+-
+-    len = sizeof(content_len);
+-    if (Fread(&content_len, len, 1, fd) != len) {
+-	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read signature content length\n"));
+-	goto exit;
+-    }
+-
+-    content = malloc(content_len + 1);
+-    if(content == NULL) {
+-	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to allocate memory to read signature content\n"));
+-	goto exit;
+-    }
+-    content[content_len] = 0;
+-    if (Fread(content, content_len, 1, fd) != content_len) {
+-	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: Failed to read signature content\n"));
+-	goto exit;
+-    }
+-
+-    rpmlog(RPMLOG_NOTICE, "%s", content);
+-exit:
+-    if(content){
+-	free(content);
+-    }
+-    if (Fseek(fd, current, SEEK_SET) < 0) {
+-	rpmlog(RPMLOG_ERR, _("rpmpkgVerifySigsTranscoded: unable to seek back to original location\n"));
+-    }
+-    return rc;
+-
+-}
+-
+ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
+ 			   FD_t fd, const char *fn)
+ {
+@@ -289,8 +234,9 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
+     rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd.verbose ? "\n" : "");
+ 
+     if(isTranscodedRpm(fd) == RPMRC_OK){
+-	return rpmpkgVerifySigsTranscoded(fd);
++	return extentsVerifySigs(fd);
+     }
++
+     struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);
+ 
+     rc = rpmpkgRead(vs, fd, NULL, NULL, &msg);
+diff --git a/lib/rpmextents.c b/lib/rpmextents.c
+index 46b7aadff..f28596f0b 100644
+--- a/lib/rpmextents.c
++++ b/lib/rpmextents.c
+@@ -9,6 +9,62 @@
+ 
+ #include "lib/rpmextents_internal.h"
+ 
++
++int extentsVerifySigs(FD_t fd){
++    rpm_loff_t current;
++    int32_t rc;
++    size_t len;
++    uint64_t content_len;
++    char *content = NULL;
++    struct extents_footer_t footer;
++
++    current = Ftell(fd);
++
++    if(extentsFooterFromFD(fd, &footer) != RPMRC_OK) {
++	rc = -1;
++	goto exit;
++    }
++    if(Fseek(fd, footer.offsets.checksig_offset, SEEK_SET) < 0) {
++	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to seek signature verification offset\n"));
++	rc = -1;
++	goto exit;
++    }
++    len = sizeof(rc);
++    if (Fread(&rc, len, 1, fd) != len) {
++	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read Signature Verification RC\n"));
++	rc = -1;
++	goto exit;
++    }
++
++    len = sizeof(content_len);
++    if (Fread(&content_len, len, 1, fd) != len) {
++	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content length\n"));
++	goto exit;
++    }
++
++    content = rmalloc(content_len + 1);
++    if(content == NULL) {
++	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to allocate memory to read signature content\n"));
++	goto exit;
++    }
++    content[content_len] = 0;
++    if (Fread(content, content_len, 1, fd) != content_len) {
++	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content\n"));
++	goto exit;
++    }
++
++    rpmlog(RPMLOG_NOTICE, "%s", content);
++exit:
++    if(content){
++	rfree(content);
++    }
++    if (Fseek(fd, current, SEEK_SET) < 0) {
++	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: unable to seek back to original location\n"));
++    }
++    return rc;
++
++}
++
+ rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer) {
+ 
+     rpmRC rc = RPMRC_NOTFOUND;
+diff --git a/lib/rpmextents_internal.h b/lib/rpmextents_internal.h
+index f0c29c807..380c08425 100644
+--- a/lib/rpmextents_internal.h
++++ b/lib/rpmextents_internal.h
+@@ -29,6 +29,12 @@ struct __attribute__ ((__packed__)) extents_footer_t {
+     extents_magic_t magic;
+ };
+ 
++/** \ingroup rpmextents
++ * Checks the results of the signature verification ran during transcoding.
++ * @param fd	The FD_t of the transcoded RPM
++ * @return	The number of checks that `rpmvsVerify` failed during transcoding.
++ */
++int extentsVerifySigs(FD_t fd);
+ 
+ /** \ingroup rpmextents
+  * Read the RPM Extents footer from a file descriptor.
+-- 
+2.35.1
+
diff --git a/SOURCES/0026-fix-integer-underflow-in-vfyFDCb.patch b/SOURCES/0026-fix-integer-underflow-in-vfyFDCb.patch
new file mode 100644
index 0000000..5fe1900
--- /dev/null
+++ b/SOURCES/0026-fix-integer-underflow-in-vfyFDCb.patch
@@ -0,0 +1,25 @@
+From 3372e6c917e54b3a84c04ca4274000da04a98e86 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Thu, 17 Feb 2022 08:54:47 -0800
+Subject: [PATCH 26/30] fix integer underflow in vfyFDCb
+
+---
+ lib/rpmchecksig.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c
+index 5e8794e2d..7ad4e7034 100644
+--- a/lib/rpmchecksig.c
++++ b/lib/rpmchecksig.c
+@@ -345,7 +345,7 @@ static int vfyFDCb(struct rpmsinfo_s *sinfo, void *cbdata)
+     struct vfydatafd_s *vd = cbdata;
+     char *vmsg, *msg;
+     size_t n;
+-    size_t remainder = BUFSIZ - vd->len;
++    size_t remainder = BUFSIZ - vd->len >= 0 ? BUFSIZ - vd->len : 0;
+ 
+     vmsg = rpmsinfoMsg(sinfo);
+     rasprintf(&msg, "    %s\n", vmsg);
+-- 
+2.35.1
+
diff --git a/SOURCES/0027-rpmchecksig-Refactor-rpmpkgVerifySigs-with-custom-ve.patch b/SOURCES/0027-rpmchecksig-Refactor-rpmpkgVerifySigs-with-custom-ve.patch
new file mode 100644
index 0000000..7bb79b2
--- /dev/null
+++ b/SOURCES/0027-rpmchecksig-Refactor-rpmpkgVerifySigs-with-custom-ve.patch
@@ -0,0 +1,169 @@
+From 1e0850cf7649578e1d7da815751efaa8101773e7 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Fri, 18 Feb 2022 11:29:06 -0800
+Subject: [PATCH 27/30] [rpmchecksig] Refactor rpmpkgVerifySigs with custom
+ verify callback
+
+The current `rpmpkgVerifySigs` was conflating logging and the actual
+package verification.
+
+This change makes it possible to pass the verify callback and its data to
+`rpmpkgVerifySigs` so callers can customize how they handle the outcome
+of signature verifications.
+---
+ lib/rpmchecksig.c | 78 ++++++++++++++++++++++-------------------------
+ lib/rpmextents.c  |  1 -
+ 2 files changed, 36 insertions(+), 43 deletions(-)
+
+diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c
+index 7ad4e7034..c9fc3bbc9 100644
+--- a/lib/rpmchecksig.c
++++ b/lib/rpmchecksig.c
+@@ -222,16 +222,11 @@ exit:
+ }
+ 
+ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
+-			   FD_t fd, const char *fn)
++			   FD_t fd, rpmsinfoCb cb, void *cbdata)
+ {
+     char *msg = NULL;
+-    struct vfydata_s vd = { .seen = 0,
+-			    .bad = 0,
+-			    .verbose = rpmIsVerbose(),
+-    };
+     int rc;
+ 
+-    rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd.verbose ? "\n" : "");
+ 
+     if(isTranscodedRpm(fd) == RPMRC_OK){
+ 	return extentsVerifySigs(fd);
+@@ -244,19 +239,7 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
+     if (rc)
+ 	goto exit;
+ 
+-    rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd);
+-
+-    if (!vd.verbose) {
+-	if (vd.seen & RPMSIG_DIGEST_TYPE) {
+-	    rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_DIGEST_TYPE) ?
+-					_("DIGESTS") : _("digests"));
+-	}
+-	if (vd.seen & RPMSIG_SIGNATURE_TYPE) {
+-	    rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_SIGNATURE_TYPE) ?
+-					_("SIGNATURES") : _("signatures"));
+-	}
+-	rpmlog(RPMLOG_NOTICE, " %s\n", rc ? _("NOT OK") : _("OK"));
+-    }
++    rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, cb, cbdata);
+ 
+ exit:
+     if (rc && msg)
+@@ -266,38 +249,39 @@ exit:
+     return rc;
+ }
+ 
+-static int rpmpkgVerifySigsFD(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
+-			   FD_t fd, rpmsinfoCb cb, void *cbdata)
+-{
+-    char *msg = NULL;
+-    int rc;
+-    struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);
+-
+-    rc = rpmpkgRead(vs, fd, NULL, NULL, &msg);
+-
+-    if (rc)
+-	goto exit;
+-
+-    rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, cb, cbdata);
+-
+-exit:
+-    if (rc && msg)
+-	rpmlog(RPMLOG_ERR, "%s\n", msg);
+-    rpmvsFree(vs);
+-    free(msg);
+-    return rc;
++static void rpmkgVerifySigsPreLogging(struct vfydata_s *vd, const char *fn){
++    rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd->verbose ? "\n" : "");
+ }
+ 
++static void rpmkgVerifySigsPostLogging(struct vfydata_s *vd, int rc){
++    if (!vd->verbose) {
++	if (vd->seen & RPMSIG_DIGEST_TYPE) {
++	    rpmlog(RPMLOG_NOTICE, " %s", (vd->bad & RPMSIG_DIGEST_TYPE) ?
++					_("DIGESTS") : _("digests"));
++	}
++	if (vd->seen & RPMSIG_SIGNATURE_TYPE) {
++	    rpmlog(RPMLOG_NOTICE, " %s", (vd->bad & RPMSIG_SIGNATURE_TYPE) ?
++					_("SIGNATURES") : _("signatures"));
++	}
++	rpmlog(RPMLOG_NOTICE, " %s\n", rc ? _("NOT OK") : _("OK"));
++    }
++}
+ 
+ /* Wrapper around rpmkVerifySigs to preserve API */
+ int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn)
+ {
+     int rc = 1; /* assume failure */
++    struct vfydata_s vd = { .seen = 0,
++			    .bad = 0,
++			    .verbose = rpmIsVerbose(),
++    };
+     if (ts && qva && fd && fn) {
+ 	rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
+ 	rpmVSFlags vsflags = rpmtsVfyFlags(ts);
+ 	int vfylevel = rpmtsVfyLevel(ts);
+-	rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, fn);
++	rpmkgVerifySigsPreLogging(&vd, fn);
++	rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, vfyCb, &vd);
++	rpmkgVerifySigsPostLogging(&vd, rc);
+     	rpmKeyringFree(keyring);
+     }
+     return rc;
+@@ -319,12 +303,22 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv)
+ 
+     while ((arg = *argv++) != NULL) {
+ 	FD_t fd = Fopen(arg, "r.ufdio");
++	struct vfydata_s vd = { .seen = 0,
++				.bad = 0,
++				.verbose = rpmIsVerbose(),
++	};
+ 	if (fd == NULL || Ferror(fd)) {
+ 	    rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), 
+ 		     arg, Fstrerror(fd));
+ 	    res++;
+-	} else if (rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, arg)) {
++	} else {
++	    rpmkgVerifySigsPreLogging(&vd, arg);
++	    int rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd,
++				      vfyCb, &vd);
++	    rpmkgVerifySigsPostLogging(&vd, rc);
++	    if (rc) {
+ 	    res++;
++	    }
+ 	}
+ 
+ 	Fclose(fd);
+@@ -373,7 +367,7 @@ int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi, char **msg)
+ 	rpmtsSetVfyLevel(ts, vfylevel);
+     }
+ 
+-    if (!rpmpkgVerifySigsFD(keyring, vfylevel, vsflags, fdi, vfyFDCb, &vd)) {
++    if (!rpmpkgVerifySigs(keyring, vfylevel, vsflags, fdi, vfyFDCb, &vd)) {
+ 	rc = RPMRC_OK;
+     }
+     *msg = strdup(vd.msg);
+diff --git a/lib/rpmextents.c b/lib/rpmextents.c
+index f28596f0b..59ba427a4 100644
+--- a/lib/rpmextents.c
++++ b/lib/rpmextents.c
+@@ -89,7 +89,6 @@ rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer) {
+ 	goto exit;
+     }
+     if (footer->magic != EXTENTS_MAGIC) {
+-	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: not transcoded\n"));
+ 	rc = RPMRC_NOTFOUND;
+ 	goto exit;
+     }
+-- 
+2.35.1
+
diff --git a/SOURCES/0028-reflink-remove-requirement-for-executable-stack-flag.patch b/SOURCES/0028-reflink-remove-requirement-for-executable-stack-flag.patch
new file mode 100644
index 0000000..a3158f6
--- /dev/null
+++ b/SOURCES/0028-reflink-remove-requirement-for-executable-stack-flag.patch
@@ -0,0 +1,117 @@
+From 57aa660de4d1b8375cd56f7b8b5fcaf8ad9a5af7 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Fri, 25 Mar 2022 08:13:08 -0700
+Subject: [PATCH 28/30] [reflink] remove requirement for executable stack flag
+
+reflink was calling `bsearch` with a nested function comparator which
+make GCC require the executable stack flag (see `man execstack`).
+selinux prevents the use of this flag:
+```
+error: Failed to dlopen /usr/lib64/rpm-plugins/reflink.so
+/usr/lib64/rpm-plugins/reflink.so: cannot enable executable stack as
+shared object requires: Permission denied
+```
+
+To fix this, either rpm could be granted the correct selinux permission,
+but this would open up execstack for more the whole rpm process.
+Fundamentally, this is happening because there is no re-entrant version
+of `bsearch`. We could probably use a global variable and be done with
+it given that each rpm is processed sequencially, but that contract may
+not hold true for ever.
+Here we are copying stdlib's `bsearch` and making it re-entrant by
+allowing to pass an void * data parameter where we can pass the key
+size.
+
+After applying this patch, when reflink.o is installed, it has the executable
+flag cleared:
+```
+- /usr/lib64/rpm-plugins/reflink.so
+```
+---
+ plugins/reflink.c | 60 +++++++++++++++++++++++++++++++++++++----------
+ 1 file changed, 48 insertions(+), 12 deletions(-)
+
+diff --git a/plugins/reflink.c b/plugins/reflink.c
+index 4fc1d74d1..69e6b51e6 100644
+--- a/plugins/reflink.c
++++ b/plugins/reflink.c
+@@ -59,6 +59,50 @@ struct reflink_state_s {
+ 
+ typedef struct reflink_state_s * reflink_state;
+ 
++/*
++ * bsearch_r: implements a re-entrant version of stdlib's bsearch.
++ * code taken and adapted from /usr/include/bits/stdlib-bsearch.h
++ */
++inline void *
++bsearch_r (const void *__key, const void *__base, size_t __nmemb, size_t __size,
++	 __compar_d_fn_t __compar, void *__arg)
++{
++  size_t __l, __u, __idx;
++  const void *__p;
++  int __comparison;
++
++  __l = 0;
++  __u = __nmemb;
++  while (__l < __u)
++    {
++      __idx = (__l + __u) / 2;
++      __p = (const void *) (((const char *) __base) + (__idx * __size));
++      __comparison = (*__compar) (__key, __p, __arg);
++      if (__comparison < 0)
++	__u = __idx;
++      else if (__comparison > 0)
++	__l = __idx + 1;
++      else
++	{
++#if __GNUC_PREREQ(4, 6)
++# pragma GCC diagnostic push
++# pragma GCC diagnostic ignored "-Wcast-qual"
++#endif
++	  return (void *) __p;
++#if __GNUC_PREREQ(4, 6)
++# pragma GCC diagnostic pop
++#endif
++	}
++    }
++
++  return NULL;
++}
++
++static int cmpdigest(const void *k1, const void *k2, void *data) {
++    rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2);
++    return memcmp(k1, k2, *(int *)data);
++}
++
+ static int inodeCmp(rpm_ino_t a, rpm_ino_t b)
+ {
+     return (a != b);
+@@ -198,21 +242,13 @@ static rpmRC reflink_psm_post(rpmPlugin plugin, rpmte te, int res)
+ 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"),
++	   _("reflink: bsearch_r(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);
++    char *entry = bsearch_r(digest, state->table, state->keys,
++			    state->keysize + sizeof(rpm_loff_t), cmpdigest,
++			    &state->keysize);
+     if (entry == NULL) {
+ 	return NOT_FOUND;
+     }
+-- 
+2.35.1
+
diff --git a/SOURCES/0029-extentsVerifySigs-Make-it-optional-to-print-the-sign.patch b/SOURCES/0029-extentsVerifySigs-Make-it-optional-to-print-the-sign.patch
new file mode 100644
index 0000000..f4bf8e3
--- /dev/null
+++ b/SOURCES/0029-extentsVerifySigs-Make-it-optional-to-print-the-sign.patch
@@ -0,0 +1,109 @@
+From 5753b178a08043316e6f3556754741cdd9cd19c5 Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Mon, 28 Mar 2022 14:00:13 -0700
+Subject: [PATCH 29/30] [extentsVerifySigs] Make it optional to print the
+ signature verification output
+
+---
+ lib/rpmchecksig.c         |  2 +-
+ lib/rpmextents.c          | 39 ++++++++++++++++++++-------------------
+ lib/rpmextents_internal.h |  3 ++-
+ 3 files changed, 23 insertions(+), 21 deletions(-)
+
+diff --git a/lib/rpmchecksig.c b/lib/rpmchecksig.c
+index c9fc3bbc9..7f856154e 100644
+--- a/lib/rpmchecksig.c
++++ b/lib/rpmchecksig.c
+@@ -229,7 +229,7 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
+ 
+ 
+     if(isTranscodedRpm(fd) == RPMRC_OK){
+-	return extentsVerifySigs(fd);
++	return extentsVerifySigs(fd, 1);
+     }
+ 
+     struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);
+diff --git a/lib/rpmextents.c b/lib/rpmextents.c
+index 59ba427a4..ac43264af 100644
+--- a/lib/rpmextents.c
++++ b/lib/rpmextents.c
+@@ -10,7 +10,7 @@
+ #include "lib/rpmextents_internal.h"
+ 
+ 
+-int extentsVerifySigs(FD_t fd){
++int extentsVerifySigs(FD_t fd, int print_content){
+     rpm_loff_t current;
+     int32_t rc;
+     size_t len;
+@@ -36,24 +36,26 @@ int extentsVerifySigs(FD_t fd){
+ 	goto exit;
+     }
+ 
+-    len = sizeof(content_len);
+-    if (Fread(&content_len, len, 1, fd) != len) {
+-	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content length\n"));
+-	goto exit;
+-    }
+-
+-    content = rmalloc(content_len + 1);
+-    if(content == NULL) {
+-	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to allocate memory to read signature content\n"));
+-	goto exit;
++    if(print_content) {
++	len = sizeof(content_len);
++	if (Fread(&content_len, len, 1, fd) != len) {
++	    rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content length\n"));
++	    goto exit;
++	}
++
++	content = rmalloc(content_len + 1);
++	if(content == NULL) {
++	    rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to allocate memory to read signature content\n"));
++	    goto exit;
++	}
++	content[content_len] = 0;
++	if (Fread(content, content_len, 1, fd) != content_len) {
++	    rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content\n"));
++	    goto exit;
++	}
++
++	rpmlog(RPMLOG_NOTICE, "%s", content);
+     }
+-    content[content_len] = 0;
+-    if (Fread(content, content_len, 1, fd) != content_len) {
+-	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content\n"));
+-	goto exit;
+-    }
+-
+-    rpmlog(RPMLOG_NOTICE, "%s", content);
+ exit:
+     if(content){
+ 	rfree(content);
+@@ -79,7 +81,6 @@ rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer) {
+ 
+     len = sizeof(struct extents_footer_t);
+     if(Fseek(fd, -len, SEEK_END) < 0) {
+-	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for footer: %s\n"), strerror(errno));
+ 	rc = RPMRC_FAIL;
+ 	goto exit;
+     }
+diff --git a/lib/rpmextents_internal.h b/lib/rpmextents_internal.h
+index 380c08425..0a3318c8e 100644
+--- a/lib/rpmextents_internal.h
++++ b/lib/rpmextents_internal.h
+@@ -32,9 +32,10 @@ struct __attribute__ ((__packed__)) extents_footer_t {
+ /** \ingroup rpmextents
+  * Checks the results of the signature verification ran during transcoding.
+  * @param fd	The FD_t of the transcoded RPM
++ * @param print_content Whether or not to print the result from rpmsig
+  * @return	The number of checks that `rpmvsVerify` failed during transcoding.
+  */
+-int extentsVerifySigs(FD_t fd);
++int extentsVerifySigs(FD_t fd, int print_content);
+ 
+ /** \ingroup rpmextents
+  * Read the RPM Extents footer from a file descriptor.
+-- 
+2.35.1
+
diff --git a/SOURCES/0030-rpmcow-Make-rpm-i-install-package-without-the-need-o.patch b/SOURCES/0030-rpmcow-Make-rpm-i-install-package-without-the-need-o.patch
new file mode 100644
index 0000000..3e64a30
--- /dev/null
+++ b/SOURCES/0030-rpmcow-Make-rpm-i-install-package-without-the-need-o.patch
@@ -0,0 +1,91 @@
+From dc53b002bd3d03a21e9af406a9aff5e588710b5b Mon Sep 17 00:00:00 2001
+From: chantra <chantr4@gmail.com>
+Date: Mon, 28 Mar 2022 19:42:39 -0700
+Subject: [PATCH 30/30] [rpmcow] Make rpm -i install package without the need
+ of --nodigest
+
+When using transcoded files, the logic to check signature is different
+and was done while the file was transcoded. This change the code path
+used by `rpm -{i,U}` to check if the file is transcoded, and in such
+cases, assume it was already verified.
+---
+ lib/transaction.c    | 29 ++++++++++++++++++-----------
+ tests/rpm2extents.at |  6 +++---
+ 2 files changed, 21 insertions(+), 14 deletions(-)
+
+diff --git a/lib/transaction.c b/lib/transaction.c
+index 36c2a7a64..703e4140c 100644
+--- a/lib/transaction.c
++++ b/lib/transaction.c
+@@ -37,6 +37,7 @@
+ #include "lib/rpmfi_internal.h"	/* only internal apis */
+ #include "lib/rpmte_internal.h"	/* only internal apis */
+ #include "lib/rpmts_internal.h"
++#include "lib/rpmextents_internal.h"
+ #include "lib/rpmvs.h"
+ #include "rpmio/rpmhook.h"
+ #include "lib/rpmtriggers.h"
+@@ -1255,10 +1256,16 @@ static int verifyPackageFiles(rpmts ts, rpm_loff_t total)
+ 	    .signature = RPMRC_NOTFOUND,
+ 	    .vfylevel = vfylevel,
+ 	};
++	int verified = 0;
+ 	rpmRC prc = RPMRC_FAIL;
+ 
+ 	rpmtsNotify(ts, p, RPMCALLBACK_VERIFY_PROGRESS, oc++, total);
+ 	FD_t fd = rpmtsNotify(ts, p, RPMCALLBACK_INST_OPEN_FILE, 0, 0);
++	if (fd != NULL && isTranscodedRpm(fd) == RPMRC_OK) {
++	    /* Transcoded RPMs are validated at transcoding time */
++	    prc = RPMRC_OK;
++	    verified = 1;
++	} else {
+ 	if (fd != NULL) {
+ 	    prc = rpmpkgRead(vs, fd, NULL, NULL, &vd.msg);
+ 	    rpmtsNotify(ts, p, RPMCALLBACK_INST_CLOSE_FILE, 0, 0);
+@@ -1267,8 +1274,11 @@ static int verifyPackageFiles(rpmts ts, rpm_loff_t total)
+ 	if (prc == RPMRC_OK)
+ 	    prc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd);
+ 
++	    verified = vd.signature == RPMRC_OK;
++	}
++
+ 	/* Record verify result, signatures only for now */
+-	rpmteSetVerified(p, vd.signature == RPMRC_OK);
++	rpmteSetVerified(p, verified);
+ 
+ 	if (prc)
+ 	    rpmteAddProblem(p, RPMPROB_VERIFY, NULL, vd.msg, 0);
+diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
+index 5c66de7f6..5135c9cf8 100644
+--- a/tests/rpm2extents.at
++++ b/tests/rpm2extents.at
+@@ -102,7 +102,7 @@ AT_CHECK([
+ RPMDB_INIT
+ 
+ runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64.rpm 2> /dev/null
+-runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm
++runroot_plugins rpm -i --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm
+ test -f ${RPMTEST}/usr/bin/hello
+ ],
+ [0],
+@@ -115,7 +115,7 @@ AT_KEYWORDS([reflink])
+ AT_CHECK([
+ RPMDB_INIT
+ 
+-runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /data/RPMS/hello-2.0-1.x86_64.rpm && exit $?
++runroot_plugins rpm -i --nodeps --undefine=%__transaction_dbus_announce /data/RPMS/hello-2.0-1.x86_64.rpm && exit $?
+ # Check that the file is properly installed in chroot
+ test -f ${RPMTEST}/usr/bin/hello
+ ],
+@@ -132,7 +132,7 @@ RPMDB_INIT
+ 
+ PKG=hlinktest-1.0-1.noarch.rpm
+ runroot_other cat /data/RPMS/${PKG} | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/${PKG} 2> /dev/null
+-runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/${PKG}
++runroot_plugins rpm -i --nodeps --undefine=%__transaction_dbus_announce /tmp/${PKG}
+ ],
+ [0],
+ [],
+-- 
+2.35.1
+
diff --git a/SOURCES/cow...cow_signvalidation.diff b/SOURCES/cow...cow_signvalidation.diff
deleted file mode 100644
index 7408370..0000000
--- a/SOURCES/cow...cow_signvalidation.diff
+++ /dev/null
@@ -1,1961 +0,0 @@
-diff --git a/Makefile.am b/Makefile.am
-index 288668819..96542c8c8 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -185,7 +185,7 @@ bin_PROGRAMS +=		rpmgraph
- rpmgraph_SOURCES =	tools/rpmgraph.c
- rpmgraph_LDADD =	lib/librpm.la rpmio/librpmio.la @WITH_POPT_LIB@
- 
--dist_bin_SCRIPTS =	scripts/gendiff
-+dist_bin_SCRIPTS =	scripts/gendiff scripts/rpm2extents_dump
- 
- rpmconfig_DATA = rpmrc
- rpmrc: $(top_srcdir)/rpmrc.in
-diff --git a/lib/Makefile.am b/lib/Makefile.am
-index 5a1b6ca9b..2f1b3597f 100644
---- a/lib/Makefile.am
-+++ b/lib/Makefile.am
-@@ -40,7 +40,8 @@ librpm_la_SOURCES = \
- 	rpmscript.h rpmscript.c \
- 	rpmchroot.c rpmchroot.h \
- 	rpmplugins.c rpmplugins.h rpmplugin.h rpmug.c rpmug.h \
--	rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h
-+	rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h \
-+	rpmextents.c rpmextents_internal.h
- 
- librpm_la_LDFLAGS = -version-info $(rpm_version_info)
- 
-diff --git a/lib/depends.c b/lib/depends.c
-index 8998afcd3..30234df3d 100644
---- a/lib/depends.c
-+++ b/lib/depends.c
-@@ -81,8 +81,6 @@ 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 feda3750c..6972602e0 100644
---- a/lib/fsm.c
-+++ b/lib/fsm.c
-@@ -19,7 +19,6 @@
- 
- #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"
-@@ -53,7 +52,6 @@ struct filedata_s {
-     int stage;
-     int setmeta;
-     int skip;
--    int plugin_contents;
-     rpmFileAction action;
-     const char *suffix;
-     char *fpath;
-@@ -893,14 +891,6 @@ 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));
- 
-@@ -921,23 +911,12 @@ 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);
-@@ -945,14 +924,24 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
-     if (rc)
- 	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);
-+    rpmRC plugin_rc = rpmpluginsCallFsmFileArchiveReader(plugins, payload, files, &fi);
-+    switch(plugin_rc) {
-+	case RPMRC_PLUGIN_CONTENTS:
-+	    if(fi == NULL) {
-+		rc = RPMERR_BAD_MAGIC;
-+		goto exit;
-+	    }
-+	    rc = RPMRC_OK;
-+	    break;
-+	case RPMRC_OK:
-+	    fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE);
-+	    if (fi == NULL) {
-+		rc = RPMERR_BAD_MAGIC;
-+		goto exit;
-+	    }
-+	    break;
-+	default:
-+	    rc = RPMRC_FAIL;
-     }
- 
-     /* Detect and create directories not explicitly in package. */
-@@ -992,14 +981,15 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files,
- 	    if (fp->action == FA_TOUCH)
- 		continue;
- 
--            if (S_ISREG(fp->sb.st_mode)) {
-+	    rpmRC plugin_rc = rpmpluginsCallFsmFileInstall(plugins, fi, fp->fpath, fp->sb.st_mode, fp->action);
-+	    if(!(plugin_rc == RPMRC_PLUGIN_CONTENTS || plugin_rc == RPMRC_OK)){
-+		rc = plugin_rc;
-+	    } else if(plugin_rc == RPMRC_PLUGIN_CONTENTS){
-+		rc = RPMRC_OK;
-+	    } else if (S_ISREG(fp->sb.st_mode)) {
- 		if (rc == RPMERR_ENOENT) {
--		    if(fp->plugin_contents) {
--			rc = RPMRC_OK;
--		    }else {
--			rc = fsmMkfile(fi, fp, files, psm, nodigest,
--			    &firstlink, &firstlinkfile);
--		    }
-+		    rc = fsmMkfile(fi, fp, files, psm, nodigest,
-+				   &firstlink, &firstlinkfile);
- 		}
-             } else if (S_ISDIR(fp->sb.st_mode)) {
-                 if (rc == RPMERR_ENOENT) {
-@@ -1107,7 +1097,6 @@ 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/rpmchecksig.c b/lib/rpmchecksig.c
-index 40a3ab83f..7f856154e 100644
---- a/lib/rpmchecksig.c
-+++ b/lib/rpmchecksig.c
-@@ -20,6 +20,7 @@
- #include "rpmio/rpmio_internal.h" 	/* fdSetBundle() */
- #include "lib/rpmlead.h"
- #include "lib/header_internal.h"
-+#include "lib/rpmextents_internal.h"
- #include "lib/rpmvs.h"
- 
- #include "debug.h"
-@@ -221,36 +222,24 @@ rpmRC rpmpkgRead(struct rpmvs_s *vs, FD_t fd,
- }
- 
- static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
--			   FD_t fd, const char *fn)
-+			   FD_t fd, rpmsinfoCb cb, void *cbdata)
- {
-     char *msg = NULL;
--    struct vfydata_s vd = { .seen = 0,
--			    .bad = 0,
--			    .verbose = rpmIsVerbose(),
--    };
-     int rc;
--    struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);
- 
--    rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd.verbose ? "\n" : "");
-+
-+    if(isTranscodedRpm(fd) == RPMRC_OK){
-+	return extentsVerifySigs(fd, 1);
-+    }
-+
-+    struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring);
- 
-     rc = rpmpkgRead(vs, fd, NULL, NULL, &msg);
- 
-     if (rc)
- 	goto exit;
- 
--    rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd);
--
--    if (!vd.verbose) {
--	if (vd.seen & RPMSIG_DIGEST_TYPE) {
--	    rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_DIGEST_TYPE) ?
--					_("DIGESTS") : _("digests"));
--	}
--	if (vd.seen & RPMSIG_SIGNATURE_TYPE) {
--	    rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_SIGNATURE_TYPE) ?
--					_("SIGNATURES") : _("signatures"));
--	}
--	rpmlog(RPMLOG_NOTICE, " %s\n", rc ? _("NOT OK") : _("OK"));
--    }
-+    rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, cb, cbdata);
- 
- exit:
-     if (rc && msg)
-@@ -260,15 +249,39 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags,
-     return rc;
- }
- 
-+static void rpmkgVerifySigsPreLogging(struct vfydata_s *vd, const char *fn){
-+    rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd->verbose ? "\n" : "");
-+}
-+
-+static void rpmkgVerifySigsPostLogging(struct vfydata_s *vd, int rc){
-+    if (!vd->verbose) {
-+	if (vd->seen & RPMSIG_DIGEST_TYPE) {
-+	    rpmlog(RPMLOG_NOTICE, " %s", (vd->bad & RPMSIG_DIGEST_TYPE) ?
-+					_("DIGESTS") : _("digests"));
-+	}
-+	if (vd->seen & RPMSIG_SIGNATURE_TYPE) {
-+	    rpmlog(RPMLOG_NOTICE, " %s", (vd->bad & RPMSIG_SIGNATURE_TYPE) ?
-+					_("SIGNATURES") : _("signatures"));
-+	}
-+	rpmlog(RPMLOG_NOTICE, " %s\n", rc ? _("NOT OK") : _("OK"));
-+    }
-+}
-+
- /* Wrapper around rpmkVerifySigs to preserve API */
- int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn)
- {
-     int rc = 1; /* assume failure */
-+    struct vfydata_s vd = { .seen = 0,
-+			    .bad = 0,
-+			    .verbose = rpmIsVerbose(),
-+    };
-     if (ts && qva && fd && fn) {
- 	rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
- 	rpmVSFlags vsflags = rpmtsVfyFlags(ts);
- 	int vfylevel = rpmtsVfyLevel(ts);
--	rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, fn);
-+	rpmkgVerifySigsPreLogging(&vd, fn);
-+	rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, vfyCb, &vd);
-+	rpmkgVerifySigsPostLogging(&vd, rc);
-     	rpmKeyringFree(keyring);
-     }
-     return rc;
-@@ -290,12 +303,22 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv)
- 
-     while ((arg = *argv++) != NULL) {
- 	FD_t fd = Fopen(arg, "r.ufdio");
-+	struct vfydata_s vd = { .seen = 0,
-+				.bad = 0,
-+				.verbose = rpmIsVerbose(),
-+	};
- 	if (fd == NULL || Ferror(fd)) {
- 	    rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), 
- 		     arg, Fstrerror(fd));
- 	    res++;
--	} else if (rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, arg)) {
-+	} else {
-+	    rpmkgVerifySigsPreLogging(&vd, arg);
-+	    int rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd,
-+				      vfyCb, &vd);
-+	    rpmkgVerifySigsPostLogging(&vd, rc);
-+	    if (rc) {
- 	    res++;
-+	    }
- 	}
- 
- 	Fclose(fd);
-@@ -304,3 +327,53 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv)
-     rpmKeyringFree(keyring);
-     return res;
- }
-+
-+struct vfydatafd_s {
-+    size_t len;
-+    char msg[BUFSIZ];
-+};
-+
-+
-+static int vfyFDCb(struct rpmsinfo_s *sinfo, void *cbdata)
-+{
-+    struct vfydatafd_s *vd = cbdata;
-+    char *vmsg, *msg;
-+    size_t n;
-+    size_t remainder = BUFSIZ - vd->len >= 0 ? BUFSIZ - vd->len : 0;
-+
-+    vmsg = rpmsinfoMsg(sinfo);
-+    rasprintf(&msg, "    %s\n", vmsg);
-+    n = rstrlcpy(vd->msg + vd->len, msg, remainder);
-+    free(vmsg);
-+    free(msg);
-+    if(n <= remainder){
-+	vd->len += n;
-+    }
-+    return 1;
-+}
-+
-+
-+int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi, char **msg)
-+{
-+    rpmRC rc = RPMRC_FAIL;
-+    rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
-+    rpmVSFlags vsflags = rpmtsVfyFlags(ts);
-+    int vfylevel = rpmtsVfyLevel(ts);
-+    struct vfydatafd_s vd = {.len = 0};
-+
-+    vsflags |= rpmcliVSFlags;
-+    if (rpmcliVfyLevelMask) {
-+	vfylevel &= ~rpmcliVfyLevelMask;
-+	rpmtsSetVfyLevel(ts, vfylevel);
-+    }
-+
-+    if (!rpmpkgVerifySigs(keyring, vfylevel, vsflags, fdi, vfyFDCb, &vd)) {
-+	rc = RPMRC_OK;
-+    }
-+    *msg = strdup(vd.msg);
-+    rpmsqPoll();
-+
-+    rpmKeyringFree(keyring);
-+    return rc;
-+}
-+
-diff --git a/lib/rpmcli.h b/lib/rpmcli.h
-index 906fe9951..7ff48b37a 100644
---- a/lib/rpmcli.h
-+++ b/lib/rpmcli.h
-@@ -411,6 +411,16 @@ int rpmcliImportPubkeys(rpmts ts, ARGV_const_t argv);
-  */
- int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv);
- 
-+
-+/** \ingroup rpmcli
-+ * Verify package signatures.
-+ * @param ts		transaction set
-+ * @param fd		a file descriptor to verify
-+ * @param msg		a string containing textual information about the verification, similar to rpmcliVerifySignatures output.
-+ * @return		0 on success
-+ */
-+int rpmcliVerifySignaturesFD(rpmts ts, FD_t fd, char **msg);
-+
- #ifdef __cplusplus
- }
- #endif
-diff --git a/lib/rpmextents.c b/lib/rpmextents.c
-new file mode 100644
-index 000000000..ac43264af
---- /dev/null
-+++ b/lib/rpmextents.c
-@@ -0,0 +1,110 @@
-+
-+#include "system.h"
-+
-+#include <rpm/rpmlog.h>
-+#include <rpm/rpmio.h>
-+#include <string.h>
-+#include <errno.h>
-+
-+
-+#include "lib/rpmextents_internal.h"
-+
-+
-+int extentsVerifySigs(FD_t fd, int print_content){
-+    rpm_loff_t current;
-+    int32_t rc;
-+    size_t len;
-+    uint64_t content_len;
-+    char *content = NULL;
-+    struct extents_footer_t footer;
-+
-+    current = Ftell(fd);
-+
-+    if(extentsFooterFromFD(fd, &footer) != RPMRC_OK) {
-+	rc = -1;
-+	goto exit;
-+    }
-+    if(Fseek(fd, footer.offsets.checksig_offset, SEEK_SET) < 0) {
-+	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to seek signature verification offset\n"));
-+	rc = -1;
-+	goto exit;
-+    }
-+    len = sizeof(rc);
-+    if (Fread(&rc, len, 1, fd) != len) {
-+	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read Signature Verification RC\n"));
-+	rc = -1;
-+	goto exit;
-+    }
-+
-+    if(print_content) {
-+	len = sizeof(content_len);
-+	if (Fread(&content_len, len, 1, fd) != len) {
-+	    rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content length\n"));
-+	    goto exit;
-+	}
-+
-+	content = rmalloc(content_len + 1);
-+	if(content == NULL) {
-+	    rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to allocate memory to read signature content\n"));
-+	    goto exit;
-+	}
-+	content[content_len] = 0;
-+	if (Fread(content, content_len, 1, fd) != content_len) {
-+	    rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content\n"));
-+	    goto exit;
-+	}
-+
-+	rpmlog(RPMLOG_NOTICE, "%s", content);
-+    }
-+exit:
-+    if(content){
-+	rfree(content);
-+    }
-+    if (Fseek(fd, current, SEEK_SET) < 0) {
-+	rpmlog(RPMLOG_ERR, _("extentsVerifySigs: unable to seek back to original location\n"));
-+    }
-+    return rc;
-+
-+}
-+
-+rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer) {
-+
-+    rpmRC rc = RPMRC_NOTFOUND;
-+    rpm_loff_t current;
-+    size_t len;
-+
-+    // If the file is not seekable, we cannot detect whether or not it is transcoded.
-+    if(Fseek(fd, 0, SEEK_CUR) < 0) {
-+        return RPMRC_FAIL;
-+    }
-+    current = Ftell(fd);
-+
-+    len = sizeof(struct extents_footer_t);
-+    if(Fseek(fd, -len, SEEK_END) < 0) {
-+	rc = RPMRC_FAIL;
-+	goto exit;
-+    }
-+    if (Fread(footer, len, 1, fd) != len) {
-+	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read footer\n"));
-+	rc = RPMRC_FAIL;
-+	goto exit;
-+    }
-+    if (footer->magic != EXTENTS_MAGIC) {
-+	rc = RPMRC_NOTFOUND;
-+	goto exit;
-+    }
-+    rc = RPMRC_OK;
-+exit:
-+    if (Fseek(fd, current, SEEK_SET) < 0) {
-+	rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n"));
-+	rc = RPMRC_FAIL;
-+    }
-+    return rc;
-+}
-+
-+rpmRC isTranscodedRpm(FD_t fd) {
-+    struct extents_footer_t footer;
-+    return extentsFooterFromFD(fd, &footer);
-+}
-+
-+
-diff --git a/lib/rpmextents_internal.h b/lib/rpmextents_internal.h
-new file mode 100644
-index 000000000..0a3318c8e
---- /dev/null
-+++ b/lib/rpmextents_internal.h
-@@ -0,0 +1,58 @@
-+#ifndef _RPMEXTENTS_INTERNAL_H
-+#define _RPMEXTENTS_INTERNAL_H
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
-+#include <stdint.h>
-+
-+/** \ingroup rpmextents
-+ * RPM extents library
-+ */
-+
-+/* magic value at end of file (64 bits) that indicates this is a transcoded
-+ * rpm.
-+ */
-+#define EXTENTS_MAGIC 3472329499408095051
-+
-+typedef uint64_t extents_magic_t;
-+
-+struct __attribute__ ((__packed__)) extents_footer_offsets_t {
-+    off64_t checksig_offset;
-+    off64_t table_offset;
-+    off64_t csum_offset;
-+};
-+
-+struct __attribute__ ((__packed__)) extents_footer_t {
-+    struct extents_footer_offsets_t offsets;
-+    extents_magic_t magic;
-+};
-+
-+/** \ingroup rpmextents
-+ * Checks the results of the signature verification ran during transcoding.
-+ * @param fd	The FD_t of the transcoded RPM
-+ * @param print_content Whether or not to print the result from rpmsig
-+ * @return	The number of checks that `rpmvsVerify` failed during transcoding.
-+ */
-+int extentsVerifySigs(FD_t fd, int print_content);
-+
-+/** \ingroup rpmextents
-+ * Read the RPM Extents footer from a file descriptor.
-+ * @param fd		The FD_t of the transcoded RPM
-+ * @param[out] footer	A pointer to an allocated extents_footer_t with a copy of the footer.
-+ * @return		RPMRC_OK on success, RPMRC_NOTFOUND if not a transcoded file, RPMRC_FAIL on any failure.
-+ */
-+rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer);
-+
-+/** \ingroup rpmextents
-+ * Check if a RPM is a transcoded RPM
-+ * @param fd	The FD_t of the transcoded RPM
-+ * return	RPMRC_OK on success, RPMRC_NOTFOUND if not a transcoded file, RPMRC_FAIL on any failure.
-+ */
-+rpmRC isTranscodedRpm(FD_t fd);
-+
-+#ifdef __cplusplus
-+}
-+#endif
-+#endif /* _RPMEXTENTS_INTERNAL_H */
-diff --git a/lib/rpmplugin.h b/lib/rpmplugin.h
-index fd81aec8d..6dbbcff35 100644
---- a/lib/rpmplugin.h
-+++ b/lib/rpmplugin.h
-@@ -60,6 +60,13 @@ typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi,
- 					      const char* path,
- 					      const char *dest,
- 					      mode_t file_mode, rpmFsmOp op);
-+typedef rpmRC (*plugin_fsm_file_install_func)(rpmPlugin plugin, rpmfi fi,
-+					      const char* path,
-+					      mode_t file_mode, rpmFsmOp op);
-+typedef rpmRC (*plugin_fsm_file_archive_reader_func)(rpmPlugin plugin,
-+						     FD_t payload,
-+						     rpmfiles files, rpmfi *fi);
-+
- 
- typedef struct rpmPluginHooks_s * rpmPluginHooks;
- struct rpmPluginHooks_s {
-@@ -80,6 +87,8 @@ struct rpmPluginHooks_s {
-     plugin_fsm_file_pre_func		fsm_file_pre;
-     plugin_fsm_file_post_func		fsm_file_post;
-     plugin_fsm_file_prepare_func	fsm_file_prepare;
-+    plugin_fsm_file_install_func	fsm_file_install;
-+    plugin_fsm_file_archive_reader_func	fsm_file_archive_reader;
- };
- 
- #ifdef __cplusplus
-diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c
-index 3da3097af..901af1ac5 100644
---- a/lib/rpmplugins.c
-+++ b/lib/rpmplugins.c
-@@ -421,3 +421,74 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi,
- 
-     return rc;
- }
-+
-+rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,
-+				   const char *path, mode_t file_mode,
-+				   rpmFsmOp op)
-+{
-+    plugin_fsm_file_install_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_install);
-+	if (hookFunc) {
-+	    hook_rc = hookFunc(plugin, fi, path, file_mode, op);
-+	    if (hook_rc == RPMRC_FAIL) {
-+		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_install 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;
-+		}
-+	    }
-+	}
-+    }
-+
-+    return rc;
-+}
-+
-+rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload,
-+				   rpmfiles files, rpmfi *fi)
-+{
-+    plugin_fsm_file_archive_reader_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_archive_reader);
-+	if (hookFunc) {
-+	    hook_rc = hookFunc(plugin, payload, files, fi);
-+	    if (hook_rc == RPMRC_FAIL) {
-+		rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_archive_reader 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;
-+		}
-+	    }
-+	}
-+    }
-+
-+    return rc;
-+}
-+
-+
-diff --git a/lib/rpmplugins.h b/lib/rpmplugins.h
-index 39762c376..88807c53c 100644
---- a/lib/rpmplugins.h
-+++ b/lib/rpmplugins.h
-@@ -167,6 +167,23 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi,
-                                    const char *path, const char *dest,
-                                    mode_t mode, rpmFsmOp op);
- 
-+/** \ingroup rpmplugins
-+ * Call the fsm file install plugin hook
-+ * @param plugins	plugins structure
-+ * @param fi		file info iterator (or NULL)
-+ * @param path		file object path
-+ * @param file_mode	file object mode
-+ * @param op		file operation + associated flags
-+ * @return		RPMRC_OK on success, RPMRC_FAIL otherwise
-+ */
-+RPM_GNUC_INTERNAL
-+rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi,
-+				   const char* path, mode_t file_mode,
-+				   rpmFsmOp op);
-+
-+RPM_GNUC_INTERNAL
-+rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload,
-+					 rpmfiles files, rpmfi *fi);
- #ifdef __cplusplus
- }
- #endif
-diff --git a/lib/transaction.c b/lib/transaction.c
-index 36c2a7a64..a476a5365 100644
---- a/lib/transaction.c
-+++ b/lib/transaction.c
-@@ -37,6 +37,7 @@
- #include "lib/rpmfi_internal.h"	/* only internal apis */
- #include "lib/rpmte_internal.h"	/* only internal apis */
- #include "lib/rpmts_internal.h"
-+#include "lib/rpmextents_internal.h"
- #include "lib/rpmvs.h"
- #include "rpmio/rpmhook.h"
- #include "lib/rpmtriggers.h"
-@@ -1255,10 +1256,16 @@
- 	    .signature = RPMRC_NOTFOUND,
- 	    .vfylevel = vfylevel,
- 	};
-+	int verified = 0;
- 	rpmRC prc = RPMRC_FAIL;
- 
- 	rpmtsNotify(ts, p, RPMCALLBACK_VERIFY_PROGRESS, oc++, total);
- 	FD_t fd = rpmtsNotify(ts, p, RPMCALLBACK_INST_OPEN_FILE, 0, 0);
-+	if(fd != NULL && isTranscodedRpm(fd) == RPMRC_OK) {
-+	    /* Transcoded RPMs are validated at transcoding time */
-+	    prc = RPMRC_OK;
-+	    verified = 1;
-+	} else {
- 	if (fd != NULL) {
- 	    prc = rpmpkgRead(vs, fd, NULL, NULL, &vd.msg);
- 	    rpmtsNotify(ts, p, RPMCALLBACK_INST_CLOSE_FILE, 0, 0);
-@@ -1267,8 +1274,11 @@
- 	if (prc == RPMRC_OK)
- 	    prc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd);
- 
-+	    verified = vd.signature == RPMRC_OK;
-+	}
-+
- 	/* Record verify result, signatures only for now */
--	rpmteSetVerified(p, vd.signature == RPMRC_OK);
-+	rpmteSetVerified(p, verified);
- 
- 	if (prc)
- 	    rpmteAddProblem(p, RPMPROB_VERIFY, NULL, vd.msg, 0);
-diff --git a/plugins/reflink.c b/plugins/reflink.c
-index 513887604..69e6b51e6 100644
---- a/plugins/reflink.c
-+++ b/plugins/reflink.c
-@@ -13,6 +13,7 @@
- #include <rpm/rpmlog.h>
- #include "lib/rpmlib.h"
- #include "lib/rpmplugin.h"
-+#include "lib/rpmextents_internal.h"
- #include "lib/rpmte_internal.h"
- #include <rpm/rpmfileutil.h>
- #include "rpmio/rpmio_internal.h"
-@@ -28,7 +29,7 @@
- #undef HTDATATYPE
- #define HASHTYPE inodeIndexHash
- #define HTKEYTYPE rpm_ino_t
--#define HTDATATYPE int
-+#define HTDATATYPE const char *
- #include "lib/rpmhash.H"
- #include "lib/rpmhash.C"
- 
-@@ -40,11 +41,6 @@
- 
- #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;
-@@ -58,10 +54,55 @@ struct reflink_state_s {
-     FD_t fd;
-     rpmfiles files;
-     inodeIndexHash inodeIndexes;
-+    int transcoded;
- };
- 
- typedef struct reflink_state_s * reflink_state;
- 
-+/*
-+ * bsearch_r: implements a re-entrant version of stdlib's bsearch.
-+ * code taken and adapted from /usr/include/bits/stdlib-bsearch.h
-+ */
-+inline void *
-+bsearch_r (const void *__key, const void *__base, size_t __nmemb, size_t __size,
-+	 __compar_d_fn_t __compar, void *__arg)
-+{
-+  size_t __l, __u, __idx;
-+  const void *__p;
-+  int __comparison;
-+
-+  __l = 0;
-+  __u = __nmemb;
-+  while (__l < __u)
-+    {
-+      __idx = (__l + __u) / 2;
-+      __p = (const void *) (((const char *) __base) + (__idx * __size));
-+      __comparison = (*__compar) (__key, __p, __arg);
-+      if (__comparison < 0)
-+	__u = __idx;
-+      else if (__comparison > 0)
-+	__l = __idx + 1;
-+      else
-+	{
-+#if __GNUC_PREREQ(4, 6)
-+# pragma GCC diagnostic push
-+# pragma GCC diagnostic ignored "-Wcast-qual"
-+#endif
-+	  return (void *) __p;
-+#if __GNUC_PREREQ(4, 6)
-+# pragma GCC diagnostic pop
-+#endif
-+	}
-+    }
-+
-+  return NULL;
-+}
-+
-+static int cmpdigest(const void *k1, const void *k2, void *data) {
-+    rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2);
-+    return memcmp(k1, k2, *(int *)data);
-+}
-+
- static int inodeCmp(rpm_ino_t a, rpm_ino_t b)
- {
-     return (a != b);
-@@ -96,51 +137,35 @@ static void reflink_cleanup(rpmPlugin plugin) {
- }
- 
- static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
-+    rpmRC rc;
-+    size_t len;
-+
-     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"));
-+    rc = isTranscodedRpm(state->fd);
-+
-+    switch(rc){
-+	// Fail to parse the file, fail the plugin.
-+	case RPMRC_FAIL:
- 	    return RPMRC_FAIL;
--	}
--	return RPMRC_OK;
-+	// This is not a transcoded file, do nothing.
-+	case RPMRC_NOTFOUND:
-+	    return RPMRC_OK;
-+	default:
-+	    break;
-     }
-     rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n"));
--    Header h = rpmteHeader(te);
-+    state->transcoded = 1;
- 
--    /* 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) {
-+    if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(extents_magic_t)), SEEK_END) < 0) {
- 	rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"),
- 	       state->fd);
- 	return RPMRC_FAIL;
-@@ -182,7 +207,7 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) {
- 	    return RPMRC_FAIL;
- 	}
- 	state->inodeIndexes = inodeIndexHashCreate(
--	    state->keys, inodeId, inodeCmp, NULL, NULL
-+	    state->keys, inodeId, inodeCmp, NULL, (inodeIndexHashFreeData)rfree
- 	);
-     }
- 
-@@ -217,21 +242,13 @@ static rpmRC reflink_psm_post(rpmPlugin plugin, rpmte te, int res)
- 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"),
-+	   _("reflink: bsearch_r(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);
-+    char *entry = bsearch_r(digest, state->table, state->keys,
-+			    state->keysize + sizeof(rpm_loff_t), cmpdigest,
-+			    &state->keysize);
-     if (entry == NULL) {
- 	return NOT_FOUND;
-     }
-@@ -239,13 +256,13 @@ rpm_loff_t find(const unsigned char *digest, reflink_state state) {
-     return offset;
- }
- 
--static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,
-+static rpmRC reflink_fsm_file_install(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;
-+    const char **hl_target = NULL;
- 
-     reflink_state state = rpmPluginGetData(plugin);
-     if (state->table == NULL) {
-@@ -262,18 +279,15 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,
- 	/* 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)) {
-+	if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hl_target,
-+				   NULL, NULL)) {
- 	    /* entry is in table, use hard link */
--	    char *fn = rpmfilesFN(state->files, hlix[0]);
--	    if (link(fn, path) != 0) {
-+	    if (link(hl_target[0], path) != 0) {
- 		rpmlog(RPMLOG_ERR,
- 		       _("reflink: Unable to hard link %s -> %s due to %s\n"),
--		       fn, path, strerror(errno));
--		free(fn);
-+		       hl_target[0], path, strerror(errno));
- 		return RPMRC_FAIL;
- 	    }
--	    free(fn);
- 	    return RPMRC_PLUGIN_CONTENTS;
- 	}
- 	/* if we didn't hard link, then we'll track this inode as being
-@@ -281,7 +295,7 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,
- 	 */
- 	if (rpmfiFNlink(fi) > 1) {
- 	    /* minor optimization: only store files with more than one link */
--	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi));
-+	    inodeIndexHashAddEntry(state->inodeIndexes, inode, rstrdup(path));
- 	}
- 	/* derived from wfd_open in fsm.c */
- 	mode_t old_umask = umask(0577);
-@@ -366,10 +380,21 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path,
-     return RPMRC_OK;
- }
- 
-+static rpmRC reflink_fsm_file_archive_reader(rpmPlugin plugin, FD_t payload,
-+					     rpmfiles files, rpmfi *fi) {
-+    reflink_state state = rpmPluginGetData(plugin);
-+    if(state->transcoded) {
-+	*fi = rpmfilesIter(files, RPMFI_ITER_FWD);
-+	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,
-+    .fsm_file_install = reflink_fsm_file_install,
-+    .fsm_file_archive_reader = reflink_fsm_file_archive_reader,
- };
-diff --git a/rpm2extents.c b/rpm2extents.c
-index c111be0a2..7dd5128de 100644
---- a/rpm2extents.c
-+++ b/rpm2extents.c
-@@ -2,7 +2,9 @@
- 
- #include "system.h"
- 
-+#include <rpm/rpmcli.h>
- #include <rpm/rpmlib.h>		/* rpmReadPackageFile .. */
-+#include <rpm/rpmlog.h>
- #include <rpm/rpmfi.h>
- #include <rpm/rpmtag.h>
- #include <rpm/rpmio.h>
-@@ -10,8 +12,10 @@
- 
- #include <rpm/rpmts.h>
- #include "lib/rpmlead.h"
-+#include "lib/rpmts.h"
- #include "lib/signature.h"
- #include "lib/header_internal.h"
-+#include "lib/rpmextents_internal.h"
- #include "rpmio/rpmio_internal.h"
- 
- #include <unistd.h>
-@@ -34,11 +38,6 @@
- #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;
-@@ -51,38 +50,49 @@ rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit)
-     return (unit - (pos % unit)) % unit;
- }
- 
--static int digestor(
-+static struct poptOption optionsTable[] = {
-+    { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
-+    N_("Common options for all rpm modes and executables:"), NULL },
-+
-+    POPT_AUTOALIAS
-+    POPT_AUTOHELP
-+    POPT_TABLEEND
-+};
-+
-+
-+static void FDDigestInit(FD_t fdi, uint8_t algos[], uint32_t algos_len){
-+    int algo;
-+
-+    for (algo = 0; algo < algos_len; algo++) {
-+	fdInitDigest(fdi, algos[algo], 0);
-+    }
-+}
-+
-+static int FDWriteDigests(
-     FD_t fdi,
-     FD_t fdo,
--    FD_t validationo,
-     uint8_t algos[],
--    uint32_t algos_len
--)
-+    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;
--    }
-+    ssize_t fdilength = fdOp(fdi, FDSTAT_READ)->bytes;
- 
-     len = sizeof(fdilength);
--    if (Fwrite(&fdilength, len, 1, validationo) != len) {
--	fprintf(stderr, _("Unable to write input length %zd\n"), fdilength);
-+    if (Fwrite(&fdilength, len, 1, fdo) != len) {
-+	rpmlog(RPMLOG_ERR, _("Unable to write input length %zd: %d, %s\n"),
-+	       fdilength, errno, strerror(errno));
- 	goto exit;
-     }
-     len = sizeof(algos_len);
--    if (Fwrite(&algos_len, len, 1, validationo) != len) {
--	fprintf(stderr, _("Unable to write number of validation digests\n"));
-+    if (Fwrite(&algos_len, len, 1, fdo) != len) {
-+	algo_digest_len = (uint32_t)filedigest_len;
-+	rpmlog(RPMLOG_ERR, _("Unable to write number of digests: %d, %s\n"),
-+	       errno, strerror(errno));
- 	goto exit;
-     }
-     for (algo = 0; algo < algos_len; algo++) {
-@@ -93,25 +103,29 @@ static int digestor(
- 	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"));
-+	if (Fwrite(&algo_name_len, len, 1, fdo) != len) {
-+	    rpmlog(RPMLOG_ERR,
-+		   _("Unable to write digest algo name length: %d, %s\n"),
-+		   errno, strerror(errno));
- 	    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"));
-+	if (Fwrite(&algo_digest_len, len, 1, fdo) != len) {
-+	    rpmlog(RPMLOG_ERR,
-+		   _("Unable to write number of bytes for digest: %d, %s\n"),
-+		   errno, strerror(errno));
- 	     goto exit;
- 	}
--	if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) {
--	    fprintf(stderr, _("Unable to write validation algo name\n"));
-+	if (Fwrite(algo_name, algo_name_len, 1, fdo) != algo_name_len) {
-+	    rpmlog(RPMLOG_ERR, _("Unable to write digest algo name: %d, %s\n"),
-+		   errno, strerror(errno));
- 	    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);
-+	if (Fwrite(filedigest, algo_digest_len, 1, fdo ) != algo_digest_len) {
-+	    rpmlog(RPMLOG_ERR,
-+		   _("Unable to write digest value %u, %zu: %d, %s\n"),
-+		   algo_digest_len, filedigest_len,
-+		   errno, strerror(errno));
- 	    goto exit;
- 	}
-     }
-@@ -120,7 +134,80 @@ static int digestor(
-     return rc;
- }
- 
--static rpmRC process_package(FD_t fdi, FD_t validationi)
-+static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) {
-+    size_t len;
-+    rpmRC rc = RPMRC_FAIL;
-+
-+    if(rpmvsrc){
-+	rpmlog(RPMLOG_WARNING,
-+	       _("Error verifying package signatures:\n%s\n"), msg);
-+    }
-+
-+    len = sizeof(rpmvsrc);
-+    if (Fwrite(&rpmvsrc, len, 1, fdo) != len) {
-+	rpmlog(RPMLOG_ERR,
-+	       _("Unable to write signature verification RC code %d: %d, %s\n"),
-+	       rpmvsrc, errno, strerror(errno));
-+	goto exit;
-+    }
-+    size_t content_len = msg ? strlen(msg) : 0;
-+    len = sizeof(content_len);
-+    if (Fwrite(&content_len, len, 1, fdo) != len) {
-+	rpmlog(RPMLOG_ERR,
-+	       _("Unable to write signature verification output length %zd: %d, %s\n"),
-+	       content_len, errno, strerror(errno));
-+	goto exit;
-+    }
-+    if (Fwrite(msg, content_len, 1, fdo) != content_len) {
-+	rpmlog(RPMLOG_ERR,
-+	       _("Unable to write signature verification output %s: %d, %s\n"),
-+	       msg, errno, strerror(errno));
-+	goto exit;
-+    }
-+
-+    rc = RPMRC_OK;
-+exit:
-+
-+    return rc;
-+}
-+
-+static rpmRC validator(FD_t fdi, FD_t digesto, FD_t sigo,
-+	uint8_t algos[],
-+	uint32_t algos_len){
-+    int rpmvsrc;
-+    rpmRC rc = RPMRC_FAIL;
-+    char *msg = NULL;
-+    rpmts ts = rpmtsCreate();
-+
-+    rpmtsSetRootDir(ts, rpmcliRootDir);
-+
-+    FDDigestInit(fdi, algos, algos_len);
-+
-+    rpmvsrc = rpmcliVerifySignaturesFD(ts, fdi, &msg);
-+
-+    // Write result of digest computation
-+    if(FDWriteDigests(fdi, digesto, algos, algos_len) != RPMRC_OK) {
-+	rpmlog(RPMLOG_ERR, _("Failed to write digests: %d, %s\n"),
-+	       errno, strerror(errno));
-+	goto exit;
-+    }
-+
-+    // Write result of signature validation.
-+    if(FDWriteSignaturesValidation(sigo, rpmvsrc, msg)) {
-+	rpmlog(RPMLOG_ERR,
-+	       _("Failed to write signature verification result: %d, %s\n"),
-+	       errno, strerror(errno));
-+	goto exit;
-+    }
-+    rc = RPMRC_OK;
-+exit:
-+    if(msg) {
-+	free(msg);
-+    }
-+    return rc;
-+}
-+
-+static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi)
- {
-     uint32_t diglen;
-     /* GNU C extension: can use diglen from outer context */
-@@ -148,7 +235,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
-     rpm_mode_t mode;
-     char *rpmio_flags = NULL, *zeros;
-     const unsigned char *digest;
--    rpm_loff_t pos, size, pad, validation_pos;
-+    rpm_loff_t pos, size, pad, digest_pos, validation_pos, digest_table_pos;
-     uint32_t offset_ix = 0;
-     size_t len;
-     int next = 0;
-@@ -156,24 +243,24 @@ 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"));
-+	rpmlog(RPMLOG_ERR, _("Error reading package\n"));
- 	exit(EXIT_FAILURE);
-     }
- 
-     if (rpmLeadWrite(fdo, h))
-     {
--	fprintf(stderr, _("Unable to write package lead: %s\n"),
-+	rpmlog(RPMLOG_ERR, _("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));
-+	rpmlog(RPMLOG_ERR, _("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));
-+	rpmlog(RPMLOG_ERR, _("Unable to write headers: %s\n"), Fstrerror(fdo));
- 	exit(EXIT_FAILURE);
-     }
- 
-@@ -187,7 +274,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
-     free(rpmio_flags);
- 
-     if (gzdi == NULL) {
--	fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));
-+	rpmlog(RPMLOG_ERR, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));
- 	exit(EXIT_FAILURE);
-     }
- 
-@@ -230,7 +317,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
- 	}
- 	pad = pad_to(pos, fundamental_block_size);
- 	if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) {
--	    fprintf(stderr, _("Unable to write padding\n"));
-+	    rpmlog(RPMLOG_ERR, _("Unable to write padding\n"));
- 	    rc = RPMRC_FAIL;
- 	    goto exit;
- 	}
-@@ -243,7 +330,12 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
- 	size = rpmfiFSize(fi);
- 	rc = rpmfiArchiveReadToFile(fi, fdo, 0);
- 	if (rc != RPMRC_OK) {
--	    fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc);
-+	    char *errstr = rpmfileStrerror(rc);
-+	    rpmlog(RPMLOG_ERR,
-+		   _("rpmfiArchiveReadToFile failed while extracting "\
-+		   "\"%s\" with RC %d: %s\n"),
-+		   rpmfiFN(fi), rc, errstr);
-+	    free(errstr);
- 	    goto exit;
- 	}
- 	pos += size;
-@@ -253,42 +345,53 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
-     qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset),
- 	  digestoffsetCmp);
- 
-+    validation_pos = pos;
-+    ssize_t validation_len = ufdCopy(validationi, fdo);
-+    if (validation_len == -1) {
-+	rpmlog(RPMLOG_ERR, _("validation output ufdCopy failed\n"));
-+	rc = RPMRC_FAIL;
-+	goto exit;
-+    }
-+
-+    digest_table_pos = validation_pos + validation_len;
-+
-     len = sizeof(offset_ix);
-     if (Fwrite(&offset_ix, len, 1, fdo) != len) {
--	fprintf(stderr, _("Unable to write length of table\n"));
-+	rpmlog(RPMLOG_ERR, _("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"));
-+	rpmlog(RPMLOG_ERR, _("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"));
-+	    rpmlog(RPMLOG_ERR, _("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"));
-+	    rpmlog(RPMLOG_ERR, _("Unable to write offset\n"));
- 	    rc = RPMRC_FAIL;
- 	    goto exit;
- 	}
-     }
--    validation_pos = (
--	pos + sizeof(offset_ix) + sizeof(diglen) +
-+    digest_pos = (
-+	digest_table_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"));
-+    ssize_t digest_len = ufdCopy(digestori, fdo);
-+    if (digest_len == -1) {
-+	rpmlog(RPMLOG_ERR, _("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.
-@@ -297,25 +400,15 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
-     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"));
-+	rpmlog(RPMLOG_ERR, _("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"));
-+    struct extents_footer_t footer = {.offsets = {validation_pos, digest_table_pos, digest_pos}, .magic = EXTENTS_MAGIC};
-+    len = sizeof(footer);
-+    if (Fwrite(&footer, len, 1, fdo) != len) {
-+	rpmlog(RPMLOG_ERR, _("Unable to write footer\n"));
- 	rc = RPMRC_FAIL;
- 	goto exit;
-     }
-@@ -327,104 +420,202 @@ static rpmRC process_package(FD_t fdi, FD_t validationi)
-     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;
-+static off_t ufdTee(FD_t sfd, FD_t *fds, int len)
-+{
-+    char buf[BUFSIZ];
-+    ssize_t rdbytes, wrbytes;
-+    off_t total = 0;
-+
-+    while (1) {
-+	rdbytes = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd);
-+
-+	if (rdbytes > 0) {
-+	    for(int i=0; i < len; i++) {
-+		wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, fds[i]);
-+		if (wrbytes != rdbytes) {
-+		    rpmlog(RPMLOG_ERR,
-+			   _("Error wriing to FD %d: %s\n"),
-+			   i, Fstrerror(fds[i]));
-+		    total = -1;
-+		    break;
-+		}
-+	    }
-+	    if(total == -1){
-+		break;
-+	    }
-+	    total += wrbytes;
-+	} else {
-+	    if (rdbytes < 0)
-+		total = -1;
-+	    break;
-+	}
-+    }
- 
--    xsetprogname(argv[0]);	/* Portability call -- see system.h */
--    rpmReadConfigFiles(NULL, NULL);
-+    return total;
-+}
- 
--    if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) {
--	fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]);
--	exit(EXIT_FAILURE);
--    }
-+static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) {
-+    rpmRC rc = RPMRC_FAIL;
-+    off_t offt = -1;
-+    // tee-ed stdin
-+    int processorpipefd[2];
-+    int validatorpipefd[2];
-+    // metadata
-+    int meta_digestpipefd[2];
-+    int meta_rpmsignpipefd[2];
-+
-+    pid_t cpids[2], w;
-+    int wstatus;
-+    FD_t fds[2];
- 
--    if (argc == 1) {
--	fprintf(stderr,
--		_("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));
--	exit(EXIT_FAILURE);
-+     if (pipe(processorpipefd) == -1) {
-+	rpmlog(RPMLOG_ERR, _("Processor pipe failure\n"));
-+	return RPMRC_FAIL;
-     }
- 
--    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(validatorpipefd) == -1) {
-+	rpmlog(RPMLOG_ERR, _("Validator pipe failure\n"));
-+	return RPMRC_FAIL;
-     }
- 
--
--    if (pipe(mainpipefd) == -1) {
--	fprintf(stderr, _("Main pipe failure\n"));
--	exit(EXIT_FAILURE);
-+    if (pipe(meta_digestpipefd) == -1) {
-+	rpmlog(RPMLOG_ERR, _("Meta digest pipe failure\n"));
-+	return RPMRC_FAIL;
-     }
--    if (pipe(metapipefd) == -1) {
--	fprintf(stderr, _("Meta pipe failure\n"));
--	exit(EXIT_FAILURE);
-+
-+    if (pipe(meta_rpmsignpipefd) == -1) {
-+	rpmlog(RPMLOG_ERR, _("Meta rpm signature pipe failure\n"));
-+	return RPMRC_FAIL;
-     }
--    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);
-+
-+    cpids[0] = fork();
-+    if (cpids[0] == 0) {
-+	/* child: validator */
-+	close(processorpipefd[0]);
-+	close(processorpipefd[1]);
-+	close(validatorpipefd[1]);
-+	close(meta_digestpipefd[0]);
-+	close(meta_rpmsignpipefd[0]);
-+	FD_t fdi = fdDup(validatorpipefd[0]);
-+	FD_t digesto = fdDup(meta_digestpipefd[1]);
-+	FD_t sigo = fdDup(meta_rpmsignpipefd[1]);
-+	close(meta_digestpipefd[1]);
-+	close(meta_rpmsignpipefd[1]);
-+	rc = validator(fdi, digesto, sigo, algos, algos_len);
-+	if(rc != RPMRC_OK) {
-+	    rpmlog(RPMLOG_ERR, _("Validator failed with RC %d\n"), rc);
-+	}
- 	Fclose(fdi);
-+	Fclose(digesto);
-+	Fclose(sigo);
-+	if (rc != RPMRC_OK) {
-+	    exit(EXIT_FAILURE);
-+	}
-+	exit(EXIT_SUCCESS);
-     } 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));
-+	cpids[1] = fork();
-+	if (cpids[1] == 0) {
-+	    /* child: process_package */
-+	    close(validatorpipefd[0]);
-+	    close(validatorpipefd[1]);
-+	    close(processorpipefd[1]);
-+	    close(meta_digestpipefd[1]);
-+	    close(meta_rpmsignpipefd[1]);
-+	    FD_t fdi = fdDup(processorpipefd[0]);
-+	    close(processorpipefd[0]);
-+	    FD_t sigi = fdDup(meta_rpmsignpipefd[0]);
-+	    close(meta_rpmsignpipefd[0]);
-+	    FD_t digestori = fdDup(meta_digestpipefd[0]);
-+	    close(meta_digestpipefd[0]);
-+
-+	    rc = process_package(fdi, digestori, sigi);
-+	    if(rc != RPMRC_OK) {
-+		rpmlog(RPMLOG_ERR, _("Package processor failed: %d\n"), rc);
- 	    }
--	}
--	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);
-+	    Fclose(digestori);
-+	    Fclose(sigi);
-+	    /* fdi is normally closed through the stacked file gzdi in the
-+	     * function
-+	     */
-+
-+	    if (rc != RPMRC_OK) {
-+		exit(EXIT_FAILURE);
- 	    }
--	} else if (WIFSIGNALED(wstatus)) {
--	    fprintf(stderr,
--		    _("Digest process was terminated with a signal: %d\n"),
--		    WTERMSIG(wstatus));
--	    cprc = EXIT_FAILURE;
-+	    exit(EXIT_SUCCESS);
-+
-+
- 	} else {
--	    /* Don't think this can happen, but covering all bases */
--	    fprintf(stderr, _("Unhandled circumstance in waitpid\n"));
--	    cprc = EXIT_FAILURE;
-+	    /* Actual parent. Read from fdi and write to both processes */
-+	    close(processorpipefd[0]);
-+	    close(validatorpipefd[0]);
-+	    fds[0] = fdDup(processorpipefd[1]);
-+	    fds[1] = fdDup(validatorpipefd[1]);
-+	    close(validatorpipefd[1]);
-+	    close(processorpipefd[1]);
-+	    close(meta_digestpipefd[0]);
-+	    close(meta_digestpipefd[1]);
-+	    close(meta_rpmsignpipefd[0]);
-+	    close(meta_rpmsignpipefd[1]);
-+
-+	    rc = RPMRC_OK;
-+	    offt = ufdTee(fdi, fds, 2);
-+	    if(offt == -1){
-+		rpmlog(RPMLOG_ERR, _("Failed to tee RPM\n"));
-+		rc = RPMRC_FAIL;
-+	    }
-+	    Fclose(fds[0]);
-+	    Fclose(fds[1]);
-+	    w = waitpid(cpids[0], &wstatus, 0);
-+	    if (w == -1) {
-+		rpmlog(RPMLOG_ERR, _("waitpid cpids[0] failed\n"));
-+		rc = RPMRC_FAIL;
-+	    }
-+	    w = waitpid(cpids[1], &wstatus, 0);
-+	    if (w == -1) {
-+		rpmlog(RPMLOG_ERR, _("waitpid cpids[1] failed\n"));
-+		rc = RPMRC_FAIL;
-+	    }
- 	}
--	if (cprc != EXIT_SUCCESS) {
--	    rc = RPMRC_FAIL;
-+    }
-+
-+    return rc;
-+}
-+
-+int main(int argc, char *argv[]) {
-+    rpmRC rc;
-+    poptContext optCon = NULL;
-+    const char **args = NULL;
-+    int nb_algos = 0;
-+
-+    xsetprogname(argv[0]);	/* Portability call -- see system.h */
-+    rpmReadConfigFiles(NULL, NULL);
-+    optCon = rpmcliInit(argc, argv, optionsTable);
-+    poptSetOtherOptionHelp(optCon, "[OPTIONS]* <DIGESTALGO>");
-+
-+    if (poptPeekArg(optCon) == NULL) {
-+	rpmlog(RPMLOG_ERR,
-+	       _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n"));
-+	poptPrintUsage(optCon, stderr, 0);
-+	exit(EXIT_FAILURE);
-+    }
-+
-+    args = poptGetArgs(optCon);
-+
-+    for (nb_algos=0; args[nb_algos]; nb_algos++);
-+    uint8_t algos[nb_algos];
-+    for (int x = 0; x < nb_algos; x++) {
-+	if (pgpStringVal(PGPVAL_HASHALGO, args[x], &algos[x]) != 0)
-+	{
-+	    rpmlog(RPMLOG_ERR,
-+		   _("Unable to resolve '%s' as a digest algorithm, exiting\n"),
-+		   args[x]);
-+	    exit(EXIT_FAILURE);
- 	}
-     }
-+
-+    FD_t fdi = fdDup(STDIN_FILENO);
-+    rc = teeRpm(fdi, algos, nb_algos);
-     if (rc != RPMRC_OK) {
- 	/* translate rpmRC into generic failure return code. */
- 	return EXIT_FAILURE;
-diff --git a/scripts/rpm2extents_dump b/scripts/rpm2extents_dump
-new file mode 100755
-index 000000000..596a59a49
---- /dev/null
-+++ b/scripts/rpm2extents_dump
-@@ -0,0 +1,94 @@
-+#!/usr/bin/env python3
-+
-+import argparse
-+import binascii
-+import os
-+import struct
-+import sys
-+
-+MAGIC_SIZE = 8
-+MAGIC_STR = b'KWTSH100'
-+
-+POS_SIZE = 8
-+
-+def keep_position(func):
-+    def wrapper(*args, **kwargs):
-+        curr = args[0].tell()
-+        res = func(*args, **kwargs)
-+        f.seek(curr, os.SEEK_SET)
-+        return res
-+    return wrapper
-+
-+def read_validation_digest(f, validation_offset):
-+	digests = []
-+    # validation
-+	f.seek(validation_offset, os.SEEK_SET)
-+	val_content_len, val_digests_num = struct.unpack('=QI', f.read(8+4))
-+	for i in range(val_digests_num):
-+		algo_name_len, digest_len = struct.unpack('=II', f.read(8))
-+		algo_name, digest = struct.unpack(f'{algo_name_len}s{digest_len}s', f.read(algo_name_len+digest_len))
-+		digests.append((algo_name, binascii.hexlify(digest)))
-+	return digests
-+
-+
-+def read_digests_table(f, digest_offset):
-+	digests = []
-+    # validation
-+	f.seek(digest_offset, os.SEEK_SET)
-+	table_len, digest_len = struct.unpack('=II', f.read(8))
-+
-+	for i in range(table_len):
-+		digest, pos = struct.unpack(f'{digest_len}sQ', f.read(digest_len + 8))
-+		digests.append((pos, binascii.hexlify(digest)))
-+	return digests
-+
-+def read_signature_output(f, signature_offset):
-+    f.seek(signature_offset, os.SEEK_SET)
-+    signature_rc, signature_output_len = struct.unpack('=IQ', f.read(12))
-+    return signature_rc, f.read(signature_output_len)
-+
-+@keep_position
-+def parse_file(f):
-+	digests = []
-+	pos_table_offset = f.seek(-8 - 3*POS_SIZE, os.SEEK_END)
-+	signature_offset, digest_offset, validation_offset = struct.unpack('=QQQ', f.read(3*POS_SIZE))
-+
-+	validation_digests = read_validation_digest(f, validation_offset)
-+	digests_table = read_digests_table(f, digest_offset)
-+	signature_ouput = read_signature_output(f, signature_offset)
-+
-+	return validation_digests, digests_table, signature_ouput
-+
-+@keep_position
-+def is_transcoded(f):
-+    f.seek(-MAGIC_SIZE, os.SEEK_END)
-+    magic = f.read(MAGIC_SIZE)
-+    return magic == MAGIC_STR
-+
-+def arg_parse():
-+    parser = argparse.ArgumentParser()
-+    parser.add_argument('--dump-signature', action='store_true')
-+    parser.add_argument('--dump-file-digest-table', action='store_true')
-+    parser.add_argument('--dump-digests', action='store_true')
-+    parser.add_argument('file')
-+
-+    return parser.parse_args()
-+
-+if __name__ == '__main__':
-+    args = arg_parse()
-+    f = open(args.file, 'rb')
-+    if not is_transcoded(f):
-+        sys.exit(1)
-+
-+    validation_digests, digests_table, signature_output = parse_file(f)
-+    if(args.dump_file_digest_table):
-+        for digest in digests_table:
-+            print(f"FileDigest {hex(digest[0])}: {digest[1]}")
-+
-+    if(args.dump_digests):
-+        for validation_digest in validation_digests:
-+            print(f"HeaderDigest {validation_digest[0]} {validation_digest[1]}")
-+
-+    if(args.dump_signature):
-+        print(f"RPMSignOutput RC {signature_output[0]}\nRPMSignOutput Content {signature_output[1].decode()}")
-+
-diff --git a/tests/Makefile.am b/tests/Makefile.am
-index f78e17c3e..fc8a24a5e 100644
---- a/tests/Makefile.am
-+++ b/tests/Makefile.am
-@@ -36,6 +36,7 @@ TESTSUITE_AT += rpmio.at
- TESTSUITE_AT += rpmio.at
- TESTSUITE_AT += rpmorder.at
- TESTSUITE_AT += rpmvfylevel.at
-+TESTSUITE_AT += rpm2extents.at
- EXTRA_DIST += $(TESTSUITE_AT)
- 
- ## testsuite data
-diff --git a/tests/atlocal.in b/tests/atlocal.in
-index c3189d327..a110564e2 100644
---- a/tests/atlocal.in
-+++ b/tests/atlocal.in
-@@ -29,6 +29,19 @@ else
- 
- RPM_XFAIL=${RPM_XFAIL-1}
- 
-+FSTYPE=$(stat -f -c %T /)
-+REFLINKABLE_FS=("xfs" "brtfs")
-+
-+REFLINK_DISABLED=true;
-+for item in "${REFLINKABLE_FS[@]}"
-+do
-+    if test "${FSTYPE}" = "${item}"
-+    then
-+	REFLINK_DISABLED=false;
-+	break
-+    fi
-+done
-+
- function run()
- {
-     "$@" --define "_tmppath ${RPMTEST}/tmp" --define "_topdir ${TOPDIR}" --dbpath="${RPMTEST}/var/lib/rpm/"
-@@ -40,6 +53,14 @@ function runroot()
-     )
- }
- 
-+function runroot_plugins()
-+{
-+    (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \
-+     MAGIC="/magic/magic" FAKECHROOT_BASE="${RPMTEST}" fakechroot "$@" --define "_buildhost testhost" --define "_topdir /build" --nouserns
-+    )
-+}
-+
-+
- function runroot_other()
- {
-     (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \
-diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at
-new file mode 100644
-index 000000000..5135c9cf8
---- /dev/null
-+++ b/tests/rpm2extents.at
-@@ -0,0 +1,140 @@
-+#    rpm2extents.at: Some very basic checks
-+#
-+#    Copyright (C) 2022  Manu Bretelle <chantr4@gmail.com>
-+#
-+#    This program is free software; you can redistribute it and/or modify
-+#    it under the terms of the GNU General Public License as published by
-+#    the Free Software Foundation; either version 2 of the License, or
-+#    (at your option) any later version.
-+#
-+#    This program is distributed in the hope that it will be useful,
-+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-+#    GNU General Public License for more details.
-+#
-+#    You should have received a copy of the GNU General Public License
-+#    along with this program; if not, write to the Free Software
-+#    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-+
-+AT_BANNER([rpm2extents tests])
-+
-+# ------------------------------
-+
-+# check that transcoder write magic at the end
-+AT_SETUP([rpm2extents magic])
-+AT_KEYWORDS([rpm2extents])
-+AT_CHECK([runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 | tail -c8],
-+[0],
-+[KWTSH100],
-+[ignore])
-+AT_CLEANUP
-+
-+# Check that transcoder writes checksig return code and content.
-+#
-+AT_SETUP([rpm2extents signature])
-+AT_KEYWORDS([rpm2extents])
-+AT_CHECK([
-+RPMDB_INIT
-+
-+runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null
-+rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm
-+runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub
-+runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm
-+rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm
-+],
-+[0],
-+[RPMSignOutput RC 2
-+RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY
-+    Header SHA256 digest: OK
-+    Header SHA1 digest: OK
-+    Payload SHA256 digest: OK
-+    V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY
-+    MD5 digest: OK
-+
-+RPMSignOutput RC 0
-+RPMSignOutput Content     Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
-+    Header SHA256 digest: OK
-+    Header SHA1 digest: OK
-+    Payload SHA256 digest: OK
-+    V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
-+    MD5 digest: OK
-+
-+],
-+[])
-+AT_CLEANUP
-+
-+AT_SETUP([rpm2extents signature verification])
-+AT_KEYWORDS([rpm2extents])
-+AT_CHECK([
-+RPMDB_INIT
-+
-+runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null
-+runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?
-+runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub
-+runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm
-+runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $?
-+],
-+[0],
-+[/tmp/hello-2.0-1.x86_64-signed.rpm:
-+    Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY
-+    Header SHA256 digest: OK
-+    Header SHA1 digest: OK
-+    Payload SHA256 digest: OK
-+    V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY
-+    MD5 digest: OK
-+1
-+/tmp/hello-2.0-1.x86_64-signed.rpm:
-+    Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
-+    Header SHA256 digest: OK
-+    Header SHA1 digest: OK
-+    Payload SHA256 digest: OK
-+    V4 RSA/SHA256 Signature, key ID 1964c5fc: OK
-+    MD5 digest: OK
-+0
-+],
-+[])
-+AT_CLEANUP
-+
-+AT_SETUP([rpm2extents install package])
-+AT_KEYWORDS([rpm2extents reflink])
-+AT_SKIP_IF([$REFLINK_DISABLED])
-+AT_CHECK([
-+RPMDB_INIT
-+
-+runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64.rpm 2> /dev/null
-+runroot_plugins rpm -i --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm
-+test -f ${RPMTEST}/usr/bin/hello
-+],
-+[0],
-+[],
-+[])
-+AT_CLEANUP
-+
-+AT_SETUP([reflink ignores non-transcoded package])
-+AT_KEYWORDS([reflink])
-+AT_CHECK([
-+RPMDB_INIT
-+
-+runroot_plugins rpm -i --nodeps --undefine=%__transaction_dbus_announce /data/RPMS/hello-2.0-1.x86_64.rpm && exit $?
-+# Check that the file is properly installed in chroot
-+test -f ${RPMTEST}/usr/bin/hello
-+],
-+[0],
-+[],
-+[])
-+AT_CLEANUP
-+
-+AT_SETUP([reflink hardlink package])
-+AT_KEYWORDS([reflink hardlink])
-+AT_SKIP_IF([$REFLINK_DISABLED])
-+AT_CHECK([
-+RPMDB_INIT
-+
-+PKG=hlinktest-1.0-1.noarch.rpm
-+runroot_other cat /data/RPMS/${PKG} | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/${PKG} 2> /dev/null
-+runroot_plugins rpm -i --nodeps --undefine=%__transaction_dbus_announce /tmp/${PKG}
-+],
-+[0],
-+[],
-+[])
-+AT_CLEANUP
-diff --git a/tests/rpmtests.at b/tests/rpmtests.at
-index a1adab8e0..205fed6a3 100644
---- a/tests/rpmtests.at
-+++ b/tests/rpmtests.at
-@@ -21,3 +21,4 @@ m4_include([rpmreplace.at])
- m4_include([rpmmacro.at])
- m4_include([rpmpython.at])
- m4_include([rpmdepmatch.at])
-+m4_include([rpm2extents.at])
diff --git a/SOURCES/master...cow.diff b/SOURCES/master...cow.diff
deleted file mode 100644
index 0e4160c..0000000
--- a/SOURCES/master...cow.diff
+++ /dev/null
@@ -1,1176 +0,0 @@
-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/SPECS/rpm.spec b/SPECS/rpm.spec
index 9cb5f43..a13c2fa 100644
--- a/SPECS/rpm.spec
+++ b/SPECS/rpm.spec
@@ -198,13 +198,44 @@ Patch9000: ndb_default.patch
 Patch9001: ndb_query_ro_media.patch
 %endif
 
-Patch9989: 1534.patch
-Patch9990: https://github.com/rpm-software-management/rpm/pull/1381.patch
+Patch9800: 1534.patch
+Patch9801: https://github.com/rpm-software-management/rpm/pull/1381.patch
 Provides: rpm(pr1381)
-Patch9991: https://github.com/chantra/rpm/compare/master...cow.diff
+
+# Copy-on-Write
+Patch9901: 0001-RPM-with-Copy-on-Write.patch
+Patch9902: 0002-Remove-use-of-bool-type-for-consistency.patch
+Patch9903: 0003-Match-formatting-style-of-existing-code.patch
+Patch9904: 0004-Fix-printf-formatting-in-reflink.c.patch
+Patch9905: 0005-tests-rpm2extents-Add-basic-tests-for-rpm2extents.patch
+Patch9906: 0006-rpm2extents-verify-package-signature-during-transcod.patch
+Patch9907: 0007-rpm2extents-write-RC-and-output-to-transcodedfile-me.patch
+Patch9908: 0008-rpm2extents-Add-script-to-troubleshoot-transcoded-fi.patch
+Patch9909: 0009-rpm2extents-Add-test-verifying-RC-code-and-signature.patch
+Patch9910: 0010-rpm2extents-Make-rpmkeys-support-reading-metadata-fr.patch
+Patch9911: 0011-rpm2extents-Perform-digest-computation-within-the-va.patch
+Patch9912: 0012-rpmextents-Create-an-internal-library-to-make-rpmext.patch
+Patch9913: 0013-plugin-add-plugin_fsm_file_install_func-plugin-hook.patch
+Patch9914: 0014-fsm-Call-new-rpmpluginsCallFsmFileInstall-in-rpmPack.patch
+Patch9915: 0015-reflink-use-reflink_fsm_file_install-hook-instead-of.patch
+Patch9916: 0016-test-new-runroot_plugins-function-to-run-command-in-.patch
+Patch9917: 0017-test-Add-test-installing-an-RPM-with-reflink-plugin.patch
+Patch9918: 0018-plugin-add-rpmpluginsCallFsmFileArchiveReader.patch
+Patch9919: 0019-reflink-use-rpmpluginsCallFsmFileArchiveReader-to-pr.patch
+Patch9920: 0020-reflink-tests-Can-install-standard-RPM-with-reflink.patch
+Patch9921: 0021-tests-Fix-tests-AT_KEYWORDS-usage.patch
+Patch9922: 0022-reflink-fix-support-for-hardlinks.patch
+Patch9923: 0023-rpm2extents-Improve-logging.patch
+Patch9924: 0024-rpm2extents-create-footer-struct-and-helpers.patch
+Patch9925: 0025-extents-move-more-functions-helpers-behind-rpmextent.patch
+Patch9926: 0026-fix-integer-underflow-in-vfyFDCb.patch
+Patch9927: 0027-rpmchecksig-Refactor-rpmpkgVerifySigs-with-custom-ve.patch
+Patch9928: 0028-reflink-remove-requirement-for-executable-stack-flag.patch
+Patch9929: 0029-extentsVerifySigs-Make-it-optional-to-print-the-sign.patch
+Patch9930: 0030-rpmcow-Make-rpm-i-install-package-without-the-need-o.patch
 Provides: rpm(pr1470)
-Patch9992: https://github.com/chantra/rpm/compare/cow...cow_signvalidation.diff
 Provides: rpm(pr1470_1)
+
 Patch9999: measure.patch
 
 # Partially GPL/LGPL dual-licensed and some bits with BSD
@@ -802,6 +833,7 @@ make check || cat tests/rpmtests.log
 * Fri Mar 25 2022 Manu Bretelle <chantra@fb.com> - 4.14.3-21.6
 - Make `rpm -i` work without `--nodigest`
 - Remove need of executable stack for reflink plugin
+- Split RPM CoW diffs in individual patches
 
 * Fri Mar 04 2022 Manu Bretelle <chantra@fb.com> - 4.14.3-21.5
 - Backport GH#1040 to allow ndb DB query on read-only FS